publications admin upload updated
This commit is contained in:
14
src/app/blogs-details/[id]/page.tsx
Normal file
14
src/app/blogs-details/[id]/page.tsx
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import Header from "../../../components/Layouts/Header"; // Adjust path based on your project structure
|
||||||
|
import { Footer } from "../../../components/Layouts/Footer"
|
||||||
|
import BlogDetail from "../../../components/blogs/BlogDetail";
|
||||||
|
|
||||||
|
|
||||||
|
export default function blogsdetails() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Header />
|
||||||
|
<BlogDetail/>
|
||||||
|
<Footer />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -1,9 +1,9 @@
|
|||||||
import Header from "../../components/Layouts/Header"; // Adjust path based on your project structure
|
import Header from "../../components/Layouts/Header"; // Adjust path based on your project structure
|
||||||
import { Footer } from "../../components/Layouts/Footer"
|
import { Footer } from "../../components/Layouts/Footer"
|
||||||
import BlogListing from '../../components/publications/BlogListing';
|
import BlogListing from '../../components/blogs/BlogListing';
|
||||||
|
|
||||||
|
|
||||||
export default function publications() {
|
export default function blogs() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Header />
|
<Header />
|
||||||
@ -1,6 +1,8 @@
|
|||||||
import Header from "../../components/Layouts/Header"; // Adjust path based on your project structure
|
import Header from "../../components/Layouts/Header"; // Adjust path based on your project structure
|
||||||
import { Footer } from "../../components/Layouts/Footer"
|
import { Footer } from "../../components/Layouts/Footer"
|
||||||
import AcademicResearch from "@/components/education/EducationTraining";
|
import AcademicResearch from "../../components/education/EducationTraining";
|
||||||
|
import CTASection from "../../components/education/CTASection";
|
||||||
|
import Publications from "../../components/Publication/Publication";
|
||||||
|
|
||||||
|
|
||||||
export default function contact() {
|
export default function contact() {
|
||||||
@ -8,6 +10,8 @@ export default function contact() {
|
|||||||
<>
|
<>
|
||||||
<Header />
|
<Header />
|
||||||
<AcademicResearch/>
|
<AcademicResearch/>
|
||||||
|
<Publications/>
|
||||||
|
<CTASection/>
|
||||||
<Footer />
|
<Footer />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
import Header from "../../../components/Layouts/Header"; // Adjust path based on your project structure
|
import Header from "../../../components/Layouts/Header"; // Adjust path based on your project structure
|
||||||
import { Footer } from "../../../components/Layouts/Footer"
|
import { Footer } from "../../../components/Layouts/Footer"
|
||||||
import BlogDetail from "../../../components/publications/BlogDetail";
|
import PublicationDetail from "../../../components/Publication/Publicationdetails";
|
||||||
|
|
||||||
|
|
||||||
export default function publicationsdetails() {
|
export default function blogsdetails() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Header />
|
<Header />
|
||||||
<BlogDetail/>
|
<PublicationDetail/>
|
||||||
<Footer />
|
<Footer />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -18,7 +18,7 @@ const Header = () => {
|
|||||||
{ href: "/education-training", label: "Academics & Research" },
|
{ href: "/education-training", label: "Academics & Research" },
|
||||||
{ href: "/services", label: "Services" },
|
{ href: "/services", label: "Services" },
|
||||||
{ href: "/events", label: "Events" },
|
{ href: "/events", label: "Events" },
|
||||||
{ href: "/publications", label: "Blogs" },
|
{ href: "/blogs", label: "Blogs" },
|
||||||
{ href: "/career", label: "Career" },
|
{ href: "/career", label: "Career" },
|
||||||
{ href: "/contact", label: "Contact Us" },
|
{ href: "/contact", label: "Contact Us" },
|
||||||
];
|
];
|
||||||
|
|||||||
319
src/components/Publication/Publication.tsx
Normal file
319
src/components/Publication/Publication.tsx
Normal file
@ -0,0 +1,319 @@
|
|||||||
|
'use client'
|
||||||
|
import React, { useState, useEffect, useMemo } from 'react';
|
||||||
|
import Link from 'next/link';
|
||||||
|
import { Search, BookOpen, ExternalLink, Users, Filter, X } from 'lucide-react';
|
||||||
|
|
||||||
|
interface Publication {
|
||||||
|
id: number;
|
||||||
|
authors: string;
|
||||||
|
year: number;
|
||||||
|
title: string;
|
||||||
|
journal: string;
|
||||||
|
doi: string;
|
||||||
|
category?: string;
|
||||||
|
abstractText?: string;
|
||||||
|
publicationDate?: string;
|
||||||
|
keywords?: string;
|
||||||
|
isActive: boolean;
|
||||||
|
displayOrder?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PublicationsSection: React.FC = () => {
|
||||||
|
const [publications, setPublications] = useState<Publication[]>([]);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
const [searchTerm, setSearchTerm] = useState('');
|
||||||
|
const [selectedYear, setSelectedYear] = useState<number | 'all'>('all');
|
||||||
|
const [selectedCategory, setSelectedCategory] = useState<string>('All');
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchPublications = async () => {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
setError(null);
|
||||||
|
|
||||||
|
const apiUrl = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8080';
|
||||||
|
const response = await fetch(`${apiUrl}/publications/active`);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Failed to fetch publications: ${response.statusText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data: Publication[] = await response.json();
|
||||||
|
setPublications(data);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error fetching publications:', err);
|
||||||
|
setError(err instanceof Error ? err.message : 'Failed to load publications');
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchPublications();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Get unique years and categories from fetched data
|
||||||
|
const years = useMemo(() => {
|
||||||
|
const yearSet = new Set(publications.map(pub => pub.year));
|
||||||
|
return Array.from(yearSet).sort((a, b) => b - a);
|
||||||
|
}, [publications]);
|
||||||
|
|
||||||
|
const categories = useMemo(() => {
|
||||||
|
const categorySet = new Set(publications.map(pub => pub.category || 'Uncategorized').filter(Boolean));
|
||||||
|
return ['All', ...Array.from(categorySet).sort()];
|
||||||
|
}, [publications]);
|
||||||
|
|
||||||
|
// Filter publications
|
||||||
|
const filteredPublications = useMemo(() => {
|
||||||
|
return publications.filter(pub => {
|
||||||
|
const matchesSearch =
|
||||||
|
pub.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||||
|
pub.authors.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||||
|
pub.journal.toLowerCase().includes(searchTerm.toLowerCase());
|
||||||
|
|
||||||
|
const matchesYear = selectedYear === 'all' || pub.year === selectedYear;
|
||||||
|
const matchesCategory = selectedCategory === 'All' || pub.category === selectedCategory;
|
||||||
|
|
||||||
|
return matchesSearch && matchesYear && matchesCategory;
|
||||||
|
});
|
||||||
|
}, [publications, searchTerm, selectedYear, selectedCategory]);
|
||||||
|
|
||||||
|
const clearFilters = () => {
|
||||||
|
setSearchTerm('');
|
||||||
|
setSelectedYear('all');
|
||||||
|
setSelectedCategory('All');
|
||||||
|
};
|
||||||
|
|
||||||
|
const hasActiveFilters = searchTerm || selectedYear !== 'all' || selectedCategory !== 'All';
|
||||||
|
|
||||||
|
// Parse authors from comma-separated string
|
||||||
|
const getAuthorsArray = (authors: string): string[] => {
|
||||||
|
return authors.split(',').map(a => a.trim()).filter(a => a.length > 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return (
|
||||||
|
<section className="py-12">
|
||||||
|
<div className="max-w-7xl mx-auto px-4">
|
||||||
|
<h2 className="text-3xl font-semibold mb-4 text-center" style={{ color: '#012068' }}>
|
||||||
|
Publications
|
||||||
|
</h2>
|
||||||
|
<div className="flex justify-center items-center py-12">
|
||||||
|
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return (
|
||||||
|
<section className="py-12">
|
||||||
|
<div className="max-w-7xl mx-auto px-4">
|
||||||
|
<h2 className="text-3xl font-semibold mb-4 text-center" style={{ color: '#012068' }}>
|
||||||
|
Publications
|
||||||
|
</h2>
|
||||||
|
<div className="text-center py-12">
|
||||||
|
<div className="text-red-600 mb-4">
|
||||||
|
<p className="text-lg font-semibold">Failed to load publications</p>
|
||||||
|
<p className="text-sm mt-2">{error}</p>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={() => window.location.reload()}
|
||||||
|
className="px-6 py-3 rounded-lg transition-opacity hover:opacity-90 text-sm font-medium"
|
||||||
|
style={{ backgroundColor: '#012068', color: 'white' }}
|
||||||
|
>
|
||||||
|
Try Again
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className="py-12">
|
||||||
|
<div className="max-w-7xl mx-auto px-4">
|
||||||
|
<h2 className="text-3xl font-semibold mb-4 text-center" style={{ color: '#012068' }}>
|
||||||
|
Publications
|
||||||
|
</h2>
|
||||||
|
<p className="text-center text-gray-600 mb-8">
|
||||||
|
Research and scholarly work from the Trauma Surgery Department
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{publications.length === 0 ? (
|
||||||
|
<div className="text-center py-12">
|
||||||
|
<BookOpen className="w-16 h-16 mx-auto mb-4 text-gray-400" />
|
||||||
|
<h3 className="text-xl font-semibold mb-2" style={{ color: '#012068' }}>
|
||||||
|
No publications available
|
||||||
|
</h3>
|
||||||
|
<p className="text-gray-600">
|
||||||
|
Publications will appear here once they are added by the admin.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
{/* Search and Filter */}
|
||||||
|
<div className="mb-8">
|
||||||
|
{/* Filter Options - Categories on left, Year and Search on right */}
|
||||||
|
<div className="flex flex-col md:flex-row justify-between items-start md:items-center gap-4 mb-4">
|
||||||
|
{/* Left side: Category Filter */}
|
||||||
|
<div className="flex flex-wrap gap-2">
|
||||||
|
{categories.map((category) => (
|
||||||
|
<button
|
||||||
|
key={category}
|
||||||
|
onClick={() => setSelectedCategory(category)}
|
||||||
|
className={`px-4 py-2 text-sm font-medium rounded-lg transition-colors duration-200 ${
|
||||||
|
selectedCategory === category ? 'text-white' : 'border-2'
|
||||||
|
}`}
|
||||||
|
style={{
|
||||||
|
backgroundColor: selectedCategory === category ? '#012068' : 'transparent',
|
||||||
|
borderColor: '#012068',
|
||||||
|
color: selectedCategory === category ? 'white' : '#012068'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{category}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Right side: Year Filter and Search */}
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
{/* Year Filter */}
|
||||||
|
<select
|
||||||
|
value={selectedYear}
|
||||||
|
onChange={(e) => setSelectedYear(e.target.value === 'all' ? 'all' : parseInt(e.target.value))}
|
||||||
|
className="px-4 py-2 border-2 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm"
|
||||||
|
style={{ borderColor: '#012068' }}
|
||||||
|
>
|
||||||
|
<option value="all">All Years</option>
|
||||||
|
{years.map(year => (
|
||||||
|
<option key={year} value={year}>{year}</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
{/* Search */}
|
||||||
|
<div className="relative">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Search publications..."
|
||||||
|
value={searchTerm}
|
||||||
|
onChange={(e) => setSearchTerm(e.target.value)}
|
||||||
|
className="border-2 rounded-lg px-4 py-2 pl-4 pr-10 text-sm focus:outline-none w-64"
|
||||||
|
style={{ borderColor: '#012068', color: '#333' }}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
className="absolute inset-y-0 right-0 flex items-center px-3 rounded-lg"
|
||||||
|
style={{ backgroundColor: '#012068' }}
|
||||||
|
>
|
||||||
|
<svg className="w-4 h-4 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Results Count and Clear Filters */}
|
||||||
|
<div className="mt-4 flex justify-between items-center">
|
||||||
|
<div className="text-sm text-gray-600">
|
||||||
|
Showing {filteredPublications.length} of {publications.length} publications
|
||||||
|
</div>
|
||||||
|
{hasActiveFilters && (
|
||||||
|
<button
|
||||||
|
onClick={clearFilters}
|
||||||
|
className="flex items-center px-4 py-2 rounded-lg transition-opacity hover:opacity-90 text-sm font-medium"
|
||||||
|
style={{ backgroundColor: '#e64838', color: 'white' }}
|
||||||
|
>
|
||||||
|
<X className="w-4 h-4 mr-2" />
|
||||||
|
Clear Filters
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Publications List */}
|
||||||
|
{filteredPublications.length === 0 ? (
|
||||||
|
<div className="text-center py-12">
|
||||||
|
<BookOpen className="w-16 h-16 mx-auto mb-4 text-gray-400" />
|
||||||
|
<h3 className="text-xl font-semibold mb-2" style={{ color: '#012068' }}>
|
||||||
|
No publications found
|
||||||
|
</h3>
|
||||||
|
<p className="text-gray-600 mb-4">
|
||||||
|
Try adjusting your search or filter criteria
|
||||||
|
</p>
|
||||||
|
{hasActiveFilters && (
|
||||||
|
<button
|
||||||
|
onClick={clearFilters}
|
||||||
|
className="px-4 py-2 rounded-lg transition-opacity hover:opacity-90 text-sm font-medium"
|
||||||
|
style={{ backgroundColor: '#012068', color: 'white' }}
|
||||||
|
>
|
||||||
|
Clear Filters
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||||
|
{filteredPublications.map((pub) => (
|
||||||
|
<Link
|
||||||
|
key={pub.id}
|
||||||
|
href={`/publications-detail/${pub.id}`}
|
||||||
|
className="block rounded-lg p-6 hover:shadow-lg transition-all duration-300 cursor-pointer group"
|
||||||
|
style={{ backgroundColor: '#f4f4f4' }}
|
||||||
|
>
|
||||||
|
{/* Publication Header */}
|
||||||
|
<div className="flex items-center space-x-3 mb-3">
|
||||||
|
<div
|
||||||
|
className="px-3 py-1 rounded-full text-xs font-semibold"
|
||||||
|
style={{ backgroundColor: '#e64838', color: 'white' }}
|
||||||
|
>
|
||||||
|
{pub.year}
|
||||||
|
</div>
|
||||||
|
{pub.category && (
|
||||||
|
<div
|
||||||
|
className="px-3 py-1 rounded-full text-xs font-medium"
|
||||||
|
style={{ backgroundColor: 'white', color: '#012068' }}
|
||||||
|
>
|
||||||
|
{pub.category}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Title */}
|
||||||
|
<h3 className="text-lg font-semibold mb-3 leading-tight group-hover:opacity-70 transition-opacity" style={{ color: '#012068' }}>
|
||||||
|
{pub.title}
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
{/* Authors */}
|
||||||
|
<div className="flex items-start mb-3">
|
||||||
|
<Users className="w-4 h-4 mr-2 mt-1 flex-shrink-0" style={{ color: '#e64838' }} />
|
||||||
|
<p className="text-sm text-gray-700 line-clamp-2">
|
||||||
|
{getAuthorsArray(pub.authors).join(', ')}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Journal */}
|
||||||
|
<div className="flex items-center mb-4">
|
||||||
|
<BookOpen className="w-4 h-4 mr-2 flex-shrink-0" style={{ color: '#012068' }} />
|
||||||
|
<p className="text-sm font-medium text-gray-700">
|
||||||
|
{pub.journal}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* View Details Button */}
|
||||||
|
<div className="flex items-center text-sm font-medium group-hover:opacity-70 transition-opacity" style={{ color: '#012068' }}>
|
||||||
|
View Details
|
||||||
|
<ExternalLink className="w-4 h-4 ml-2" />
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PublicationsSection;
|
||||||
326
src/components/Publication/Publicationdetails.tsx
Normal file
326
src/components/Publication/Publicationdetails.tsx
Normal file
@ -0,0 +1,326 @@
|
|||||||
|
'use client'
|
||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import Link from 'next/link';
|
||||||
|
import { useParams } from 'next/navigation';
|
||||||
|
import {
|
||||||
|
ChevronRight,
|
||||||
|
BookOpen,
|
||||||
|
Users,
|
||||||
|
Calendar,
|
||||||
|
ExternalLink,
|
||||||
|
FileText,
|
||||||
|
Tag
|
||||||
|
} from 'lucide-react';
|
||||||
|
|
||||||
|
interface Publication {
|
||||||
|
id: number;
|
||||||
|
authors: string;
|
||||||
|
year: number;
|
||||||
|
title: string;
|
||||||
|
journal: string;
|
||||||
|
doi?: string;
|
||||||
|
category?: string;
|
||||||
|
abstractText?: string;
|
||||||
|
publicationDate?: string;
|
||||||
|
keywords?: string;
|
||||||
|
isActive: boolean;
|
||||||
|
displayOrder?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PublicationDetail: React.FC = () => {
|
||||||
|
const params = useParams();
|
||||||
|
const publicationId = params?.id as string;
|
||||||
|
const [publication, setPublication] = useState<Publication | null>(null);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchPublication = async () => {
|
||||||
|
if (!publicationId) {
|
||||||
|
setError('No publication ID provided');
|
||||||
|
setLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
setError(null);
|
||||||
|
|
||||||
|
const apiUrl = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8080';
|
||||||
|
const response = await fetch(`${apiUrl}/publications/${publicationId}`);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
if (response.status === 404) {
|
||||||
|
throw new Error('Publication not found');
|
||||||
|
}
|
||||||
|
throw new Error(`Failed to fetch publication: ${response.statusText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data: Publication = await response.json();
|
||||||
|
setPublication(data);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error fetching publication:', err);
|
||||||
|
setError(err instanceof Error ? err.message : 'Failed to load publication');
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchPublication();
|
||||||
|
}, [publicationId]);
|
||||||
|
|
||||||
|
// Parse comma-separated strings
|
||||||
|
const getAuthorsArray = (authors: string): string[] => {
|
||||||
|
return authors.split(',').map(a => a.trim()).filter(a => a.length > 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getKeywordsArray = (keywords: string): string[] => {
|
||||||
|
return keywords.split(',').map(k => k.trim()).filter(k => k.length > 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen flex items-center justify-center">
|
||||||
|
<div className="text-center">
|
||||||
|
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto mb-4"></div>
|
||||||
|
<p className="text-gray-600">Loading publication details...</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error || !publication) {
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen flex items-center justify-center">
|
||||||
|
<div className="text-center">
|
||||||
|
<BookOpen className="w-16 h-16 mx-auto mb-4 text-gray-400" />
|
||||||
|
<h2 className="text-2xl font-bold mb-4" style={{ color: '#012068' }}>
|
||||||
|
{error === 'Publication not found' ? 'Publication Not Found' : 'Error Loading Publication'}
|
||||||
|
</h2>
|
||||||
|
<p className="text-gray-600 mb-6">
|
||||||
|
{error || 'The publication you\'re looking for doesn\'t exist.'}
|
||||||
|
</p>
|
||||||
|
<div className="flex gap-4 justify-center">
|
||||||
|
<Link
|
||||||
|
href="/education-training"
|
||||||
|
className="inline-block px-6 py-3 rounded transition-opacity hover:opacity-90 text-sm font-medium"
|
||||||
|
style={{ backgroundColor: '#012068', color: 'white' }}
|
||||||
|
>
|
||||||
|
Back to Publications
|
||||||
|
</Link>
|
||||||
|
<button
|
||||||
|
onClick={() => window.location.reload()}
|
||||||
|
className="px-6 py-3 rounded transition-opacity hover:opacity-90 text-sm font-medium border-2"
|
||||||
|
style={{ borderColor: '#012068', color: '#012068' }}
|
||||||
|
>
|
||||||
|
Try Again
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen bg-white">
|
||||||
|
{/* Breadcrumb Section */}
|
||||||
|
<section className="py-4" style={{ backgroundColor: '#f4f4f4' }}>
|
||||||
|
<div className="max-w-7xl mx-auto px-4">
|
||||||
|
<nav className="flex items-center space-x-2 text-sm">
|
||||||
|
<Link
|
||||||
|
href="/"
|
||||||
|
className="hover:opacity-70 transition-opacity duration-200"
|
||||||
|
style={{ color: '#012068' }}
|
||||||
|
>
|
||||||
|
Home
|
||||||
|
</Link>
|
||||||
|
<ChevronRight className="w-4 h-4" style={{ color: '#012068' }} />
|
||||||
|
<Link
|
||||||
|
href="/education-training"
|
||||||
|
className="hover:opacity-70 transition-opacity duration-200"
|
||||||
|
style={{ color: '#012068' }}
|
||||||
|
>
|
||||||
|
Academic & Research
|
||||||
|
</Link>
|
||||||
|
<ChevronRight className="w-4 h-4" style={{ color: '#012068' }} />
|
||||||
|
<span className="font-medium" style={{ color: '#e64838' }}>
|
||||||
|
Publication Details
|
||||||
|
</span>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
{/* Publication Header */}
|
||||||
|
<div className="mt-6">
|
||||||
|
<div className="flex items-center space-x-3 mb-4">
|
||||||
|
<div
|
||||||
|
className="px-3 py-1 rounded-full text-sm font-semibold"
|
||||||
|
style={{ backgroundColor: '#e64838', color: 'white' }}
|
||||||
|
>
|
||||||
|
{publication.year}
|
||||||
|
</div>
|
||||||
|
{publication.category && (
|
||||||
|
<div
|
||||||
|
className="px-3 py-1 rounded-full text-sm font-medium"
|
||||||
|
style={{ backgroundColor: 'white', color: '#012068', border: '2px solid #012068' }}
|
||||||
|
>
|
||||||
|
{publication.category}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<h1 className="text-3xl font-bold mb-4" style={{ color: '#012068' }}>
|
||||||
|
{publication.title}
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Main Content */}
|
||||||
|
<div className="max-w-7xl mx-auto px-4 py-12">
|
||||||
|
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
||||||
|
{/* Main Content Column */}
|
||||||
|
<div className="lg:col-span-2">
|
||||||
|
{/* Abstract Section */}
|
||||||
|
{publication.abstractText && (
|
||||||
|
<div className="mb-8">
|
||||||
|
<div className="flex items-center mb-4">
|
||||||
|
<FileText className="w-6 h-6 mr-3" style={{ color: '#e64838' }} />
|
||||||
|
<h2 className="text-2xl font-semibold" style={{ color: '#012068' }}>
|
||||||
|
Abstract
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<div className="rounded-lg p-6" style={{ backgroundColor: '#f9fafb' }}>
|
||||||
|
<p className="text-gray-700 leading-relaxed whitespace-pre-wrap">
|
||||||
|
{publication.abstractText}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Authors Section */}
|
||||||
|
<div className="mb-8">
|
||||||
|
<div className="flex items-center mb-4">
|
||||||
|
<Users className="w-6 h-6 mr-3" style={{ color: '#e64838' }} />
|
||||||
|
<h2 className="text-2xl font-semibold" style={{ color: '#012068' }}>
|
||||||
|
Authors
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<div className="rounded-lg p-6" style={{ backgroundColor: '#f9fafb' }}>
|
||||||
|
<div className="flex flex-wrap gap-3">
|
||||||
|
{getAuthorsArray(publication.authors).map((author, index) => (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
className="px-4 py-2 rounded-lg text-sm font-medium"
|
||||||
|
style={{ backgroundColor: 'white', color: '#012068', border: '1px solid #e5e7eb' }}
|
||||||
|
>
|
||||||
|
{author}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Keywords Section */}
|
||||||
|
{publication.keywords && (
|
||||||
|
<div className="mb-8">
|
||||||
|
<div className="flex items-center mb-4">
|
||||||
|
<Tag className="w-6 h-6 mr-3" style={{ color: '#e64838' }} />
|
||||||
|
<h2 className="text-2xl font-semibold" style={{ color: '#012068' }}>
|
||||||
|
Keywords
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<div className="rounded-lg p-6" style={{ backgroundColor: '#f9fafb' }}>
|
||||||
|
<div className="flex flex-wrap gap-2">
|
||||||
|
{getKeywordsArray(publication.keywords).map((keyword, index) => (
|
||||||
|
<span
|
||||||
|
key={index}
|
||||||
|
className="px-3 py-1 rounded-full text-sm"
|
||||||
|
style={{ backgroundColor: '#e64838', color: 'white' }}
|
||||||
|
>
|
||||||
|
{keyword}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Sidebar Column */}
|
||||||
|
<div className="lg:col-span-1">
|
||||||
|
<div className="rounded-lg p-6 sticky top-8" style={{ backgroundColor: '#f4f4f4' }}>
|
||||||
|
<h3 className="text-xl font-semibold mb-6" style={{ color: '#012068' }}>
|
||||||
|
Publication Information
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
{/* Journal */}
|
||||||
|
<div className="mb-6">
|
||||||
|
<div className="flex items-center mb-2">
|
||||||
|
<BookOpen className="w-5 h-5 mr-2" style={{ color: '#e64838' }} />
|
||||||
|
<span className="text-sm font-semibold" style={{ color: '#012068' }}>
|
||||||
|
Journal
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<p className="text-sm text-gray-700 ml-7">
|
||||||
|
{publication.journal}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Publication Date */}
|
||||||
|
{publication.publicationDate && (
|
||||||
|
<div className="mb-6">
|
||||||
|
<div className="flex items-center mb-2">
|
||||||
|
<Calendar className="w-5 h-5 mr-2" style={{ color: '#e64838' }} />
|
||||||
|
<span className="text-sm font-semibold" style={{ color: '#012068' }}>
|
||||||
|
Published
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<p className="text-sm text-gray-700 ml-7">
|
||||||
|
{publication.publicationDate}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* DOI */}
|
||||||
|
{publication.doi && (
|
||||||
|
<div className="mb-6">
|
||||||
|
<div className="flex items-center mb-2">
|
||||||
|
<ExternalLink className="w-5 h-5 mr-2" style={{ color: '#e64838' }} />
|
||||||
|
<span className="text-sm font-semibold" style={{ color: '#012068' }}>
|
||||||
|
DOI
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<a
|
||||||
|
href={publication.doi}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="text-sm hover:underline ml-7 break-all"
|
||||||
|
style={{ color: '#e64838' }}
|
||||||
|
>
|
||||||
|
{publication.doi.replace('https://doi.org/', '')}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Action Buttons */}
|
||||||
|
<div className="space-y-3 pt-6 border-t border-gray-300">
|
||||||
|
{publication.doi && (
|
||||||
|
<a
|
||||||
|
href={publication.doi}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="flex items-center justify-center w-full px-4 py-3 rounded transition-opacity hover:opacity-90 text-sm font-medium"
|
||||||
|
style={{ backgroundColor: '#012068', color: 'white' }}
|
||||||
|
>
|
||||||
|
<ExternalLink className="w-4 h-4 mr-2" />
|
||||||
|
View Full Publication
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PublicationDetail;
|
||||||
@ -360,7 +360,7 @@ const BlogDetail: React.FC = () => {
|
|||||||
key={post.id}
|
key={post.id}
|
||||||
className="group bg-white rounded-lg overflow-hidden border border-gray-300 hover:shadow-lg transition-all duration-300"
|
className="group bg-white rounded-lg overflow-hidden border border-gray-300 hover:shadow-lg transition-all duration-300"
|
||||||
>
|
>
|
||||||
<Link href={`/publications-detail/${post.id}`} className="block">
|
<Link href={`/blogs-details/${post.id}`} className="block">
|
||||||
<div className="relative h-40 overflow-hidden">
|
<div className="relative h-40 overflow-hidden">
|
||||||
<Image
|
<Image
|
||||||
src={post.image}
|
src={post.image}
|
||||||
@ -268,7 +268,7 @@ const BlogListing: React.FC = () => {
|
|||||||
>
|
>
|
||||||
{/* All cards redirect to blog detail page with ID */}
|
{/* All cards redirect to blog detail page with ID */}
|
||||||
<Link
|
<Link
|
||||||
href={`/publications-detail/${blog.id}`}
|
href={`/blogs-details/${blog.id}`}
|
||||||
className="absolute top-0 left-0 h-full w-full z-10"
|
className="absolute top-0 left-0 h-full w-full z-10"
|
||||||
aria-label={`Read article: ${blog.title}`}
|
aria-label={`Read article: ${blog.title}`}
|
||||||
/>
|
/>
|
||||||
53
src/components/education/CTASection.tsx
Normal file
53
src/components/education/CTASection.tsx
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
'use client';
|
||||||
|
import { useState, useEffect } from 'react';
|
||||||
|
import Image from 'next/image';
|
||||||
|
import Link from 'next/link';
|
||||||
|
import {
|
||||||
|
ChevronRight,
|
||||||
|
Clock,
|
||||||
|
Users,
|
||||||
|
Award,
|
||||||
|
Calendar,
|
||||||
|
BookOpen,
|
||||||
|
MapPin,
|
||||||
|
GraduationCap,
|
||||||
|
FlaskConical
|
||||||
|
} from 'lucide-react';
|
||||||
|
{/* CTA Section */ }
|
||||||
|
const CTASection = () => {
|
||||||
|
return (
|
||||||
|
<section className="py-16" style={{ backgroundColor: '#f4f4f4' }}>
|
||||||
|
<div className="max-w-4xl mx-auto px-4 text-center">
|
||||||
|
<Award className="w-12 h-12 mx-auto mb-6" style={{ color: '#e64838' }} />
|
||||||
|
<h2 className="text-3xl font-bold mb-4" style={{ color: '#012068' }}>
|
||||||
|
Ready to Advance Your Trauma Care Expertise?
|
||||||
|
</h2>
|
||||||
|
<p className="text-lg mb-8 max-w-2xl mx-auto" style={{ color: '#666' }}>
|
||||||
|
Join our structured training programs designed to empower healthcare professionals, nurses, and community responders with critical trauma care skills.
|
||||||
|
</p>
|
||||||
|
<div className="flex flex-col sm:flex-row gap-4 justify-center">
|
||||||
|
<Link
|
||||||
|
href="/contact"
|
||||||
|
className="inline-block px-6 py-3 text-sm font-medium rounded hover:opacity-90 transition-opacity duration-300"
|
||||||
|
style={{
|
||||||
|
backgroundColor: '#012068',
|
||||||
|
color: 'white'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Contact Admissions
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
href="/#"
|
||||||
|
className="inline-block px-6 py-3 text-sm font-medium rounded border-2 hover:opacity-70 transition-opacity duration-300"
|
||||||
|
style={{
|
||||||
|
borderColor: '#012068',
|
||||||
|
color: '#012068'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Download Brochure
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);};
|
||||||
|
export default CTASection;
|
||||||
@ -485,40 +485,7 @@ const AcademicResearch: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* CTA Section */}
|
|
||||||
<section className="py-16" style={{ backgroundColor: '#f4f4f4' }}>
|
|
||||||
<div className="max-w-4xl mx-auto px-4 text-center">
|
|
||||||
<Award className="w-12 h-12 mx-auto mb-6" style={{ color: '#e64838' }} />
|
|
||||||
<h2 className="text-3xl font-bold mb-4" style={{ color: '#012068' }}>
|
|
||||||
Ready to Advance Your Trauma Care Expertise?
|
|
||||||
</h2>
|
|
||||||
<p className="text-lg mb-8 max-w-2xl mx-auto" style={{ color: '#666' }}>
|
|
||||||
Join our structured training programs designed to empower healthcare professionals, nurses, and community responders with critical trauma care skills.
|
|
||||||
</p>
|
|
||||||
<div className="flex flex-col sm:flex-row gap-4 justify-center">
|
|
||||||
<Link
|
|
||||||
href="/contact"
|
|
||||||
className="inline-block px-6 py-3 text-sm font-medium rounded hover:opacity-90 transition-opacity duration-300"
|
|
||||||
style={{
|
|
||||||
backgroundColor: '#012068',
|
|
||||||
color: 'white'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Contact Admissions
|
|
||||||
</Link>
|
|
||||||
<Link
|
|
||||||
href="/#"
|
|
||||||
className="inline-block px-6 py-3 text-sm font-medium rounded border-2 hover:opacity-70 transition-opacity duration-300"
|
|
||||||
style={{
|
|
||||||
borderColor: '#012068',
|
|
||||||
color: '#012068'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Download Brochure
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@ -25,7 +25,7 @@ const HeroSection = () => {
|
|||||||
title: 'This year, we celebrate',
|
title: 'This year, we celebrate',
|
||||||
subtitle: '125 years of CMC Vellore',
|
subtitle: '125 years of CMC Vellore',
|
||||||
description: '1900 - 2025',
|
description: '1900 - 2025',
|
||||||
imageUrl: '/images/heronew.png',
|
imageUrl: '/images/hero.png',
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -121,16 +121,16 @@ const HeroSection = () => {
|
|||||||
<div className="relative z-10 flex items-center h-full px-4 sm:px-6 md:px-8 lg:px-12 xl:px-16 2xl:px-24">
|
<div className="relative z-10 flex items-center h-full px-4 sm:px-6 md:px-8 lg:px-12 xl:px-16 2xl:px-24">
|
||||||
<div className="max-w-2xl lg:max-w-3xl">
|
<div className="max-w-2xl lg:max-w-3xl">
|
||||||
{/* Glass Card */}
|
{/* Glass Card */}
|
||||||
<div className="bg-white/20 backdrop-blur-[2px] rounded-2xl p-8 sm:p-10 md:p-12 lg:p-4 shadow-2xl border border-white/30">
|
<div className="bg-black/10 backdrop-blur-[1px] rounded-2xl p-8 sm:p-10 md:p-12 lg:p-4 shadow-2xl border border-white/30">
|
||||||
{/* Main Heading */}
|
{/* Main Heading */}
|
||||||
<h1 className="mb-6 sm:mb-8 md:mb-10" style={{ color: '#012068' }}>
|
<h1 className="mb-6 sm:mb-8 md:mb-10" style={{ color: '#fff' }}>
|
||||||
<div className="text-lg sm:text-xl md:text-2xl lg:text-3xl xl:text-4xl 2xl:text-5xl font-semibold leading-tight mb-1 sm:mb-2 md:mb-3 lg:mb-4">
|
<div className="text-lg sm:text-xl md:text-2xl lg:text-3xl xl:text-4xl 2xl:text-5xl font-semibold leading-tight mb-1 sm:mb-2 md:mb-3 lg:mb-4">
|
||||||
This year, we celebrate <span className='text-5xl text-bold'>125 years</span>
|
This year, we celebrate <span className='text-5xl text-bold'>125 years</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-lg sm:text-xl md:text-2xl lg:text-3xl xl:text-4xl 2xl:text-5xl font-semibold leading-tight mb-1 sm:mb-2 md:mb-3 lg:mb-4">
|
<div className="text-lg sm:text-xl md:text-2xl lg:text-3xl xl:text-4xl 2xl:text-5xl font-semibold leading-tight mb-1 sm:mb-2 md:mb-3 lg:mb-4">
|
||||||
of CMC Vellore
|
of CMC Vellore
|
||||||
</div>
|
</div>
|
||||||
<div className="text-base sm:text-lg md:text-xl lg:text-2xl xl:text-3xl 2xl:text-4xl font-semibold" style={{ color: '#012068' }}>
|
<div className="text-base sm:text-lg md:text-xl lg:text-2xl xl:text-3xl 2xl:text-4xl font-semibold" style={{ color: '#fff' }}>
|
||||||
1900 - 2025
|
1900 - 2025
|
||||||
</div>
|
</div>
|
||||||
</h1>
|
</h1>
|
||||||
|
|||||||
Reference in New Issue
Block a user