Changes on 19-11-2025

This commit is contained in:
2025-11-19 15:01:00 +05:30
parent f55a506ea5
commit 4db6f5d132
8 changed files with 579 additions and 277 deletions

View File

@ -7,18 +7,24 @@ import { usePathname } from "next/navigation";
const Header = () => {
const [isMenuOpen, setIsMenuOpen] = useState(false);
const [isServicesOpen, setIsServicesOpen] = useState(false);
const [isMobileServicesOpen, setIsMobileServicesOpen] = useState(false);
const pathname = usePathname();
const closeAllMenus = () => {
setIsMenuOpen(false);
setIsServicesOpen(false);
setIsMobileServicesOpen(false);
};
const menuItems = [
{ href: "/", label: "Home" },
{ href: "/about", label: "About" },
{ href: "/teamMember", label: "Our Team" },
{ href: "/education-training", label: "Academics & Research" },
{ href: "/services", label: "Services" },
{ href: "/services", label: "Services", hasDropdown: true },
{ href: "/events", label: "Events" },
{ href: "/blogs", label: "Blogs" },
{ href: "/career", label: "Career" },
@ -77,6 +83,50 @@ const Header = () => {
{menuItems.map((item, idx) => {
const isActive = pathname === item.href ||
(item.href !== "/" && pathname.startsWith(item.href));
if (item.hasDropdown) {
return (
<div
key={idx}
className="relative flex-1"
onMouseEnter={() => setIsServicesOpen(true)}
onMouseLeave={() => setIsServicesOpen(false)}
>
<Link
href="/services"
className={`font-medium text-sm xl:text-[15px] whitespace-nowrap px-4 xl:px-5 py-3.5 transition-colors w-full ${
isActive
? "text-white bg-red-600"
: "text-white hover:bg-red-600"
} flex items-center justify-center gap-1`}
>
{item.label}
<svg
className={`w-4 h-4 transition-transform ${isServicesOpen ? "rotate-180" : ""}`}
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
</svg>
</Link>
{/* Services Dropdown */}
{isServicesOpen && (
<div className="absolute top-full left-0 mt-0 w-64 bg-white shadow-lg border border-gray-200 rounded-b-md z-50">
<Link
href="/services/acute-care-surgery"
className="block px-4 py-3 text-sm font-medium text-blue-900 hover:bg-gray-100 hover:text-red-600 transition-colors"
onClick={closeAllMenus}
>
Acute Care Surgery Services
</Link>
</div>
)}
</div>
);
}
return (
<Link
key={idx}
@ -95,7 +145,7 @@ const Header = () => {
</div>
</nav>
{/* Mobile menu button - More visible */}
{/* Mobile menu button */}
<button
className="lg:hidden p-3 text-white hover:bg-red-600 transition-colors w-full flex items-center justify-between"
onClick={() => setIsMenuOpen(!isMenuOpen)}
@ -136,6 +186,47 @@ const Header = () => {
{menuItems.map((item, idx) => {
const isActive = pathname === item.href ||
(item.href !== "/" && pathname.startsWith(item.href));
if (item.hasDropdown) {
return (
<li key={idx}>
<button
className={`w-full text-left font-medium py-3 px-4 transition-colors rounded text-sm sm:text-base flex items-center justify-between ${
isActive
? "bg-red-600 text-white"
: "text-blue-900 hover:bg-gray-100 hover:text-red-600"
}`}
onClick={() => setIsMobileServicesOpen(!isMobileServicesOpen)}
>
{item.label}
<svg
className={`w-4 h-4 transition-transform ${isMobileServicesOpen ? "rotate-180" : ""}`}
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
</svg>
</button>
{/* Mobile Services Submenu */}
{isMobileServicesOpen && (
<ul className="ml-4 mt-1 space-y-1">
<li>
<Link
href="/services/acute-care-surgery"
className="block py-2.5 px-4 text-sm font-medium text-gray-700 hover:bg-gray-100 hover:text-red-600 rounded transition-colors"
onClick={closeAllMenus}
>
Acute Care Surgery Services
</Link>
</li>
</ul>
)}
</li>
);
}
return (
<li key={idx}>
<Link

View File

@ -64,10 +64,10 @@ const StatisticsTiles = () => {
};
return (
<section className="py-8 sm:py-12" style={{ backgroundColor: '#f4f4f4' }}>
<div className="max-w-7xl mx-auto px-4">
<section className="py-12 sm:py-16" style={{ backgroundColor: '#f4f4f4' }}>
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<h2
className="text-2xl sm:text-3xl font-semibold text-center mb-8 sm:mb-12"
className="text-3xl sm:text-4xl font-bold text-center mb-12 sm:mb-16"
style={{ color: '#012068' }}
>
Our Trauma Care Services
@ -86,34 +86,32 @@ const StatisticsTiles = () => {
</p>
</div>
) : (
// Display tiles
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 lg:gap-6">
// Display services one after another
<div className="space-y-8">
{tiles.map((tile, index) => (
<div
key={tile.id}
className="border border-gray-300 rounded-lg p-5 h-full hover:shadow-lg transition-shadow duration-300"
style={{ backgroundColor: '#ffffff' }}
>
<div className="flex flex-col h-full">
<div key={tile.id} className="text-left">
<h3
className="text-base sm:text-lg font-semibold mb-3 text-left"
style={{ color: '#012068' }}
className="text-xl sm:text-2xl font-bold mb-4 pb-2 border-b-2 inline-block"
style={{ color: '#012068', borderColor: '#012068' }}
>
{tile.title}
</h3>
<ul className="list-disc pl-5 space-y-2 flex-grow text-left">
<ul className="space-y-2.5 mt-4">
{parseDescription(tile.description).map((point, idx) => (
<li
key={idx}
className="text-sm sm:text-base leading-relaxed text-left"
className="text-sm sm:text-base leading-relaxed flex items-start"
style={{ color: '#333' }}
>
{point}
<span
className="inline-block w-1.5 h-1.5 rounded-full mt-2 mr-3 flex-shrink-0"
style={{ backgroundColor: '#012068' }}
></span>
<span>{point}</span>
</li>
))}
</ul>
</div>
</div>
))}
</div>
)}

View File

@ -116,20 +116,6 @@ const BlogDetail: React.FC = () => {
target.src = '/images/default-blog-image.jpg';
};
const getAuthorName = (blog: Blog) => {
if (blog.professors && blog.professors.length > 0) {
return blog.professors.map(prof => prof.firstName || prof.name).join(', ');
}
return 'Medical Team';
};
const getAuthorBio = (blog: Blog) => {
if (blog.professors && blog.professors.length > 0) {
return `Medical professional${blog.professors.length > 1 ? 's' : ''} specializing in trauma care and mental health support.`;
}
return 'Our medical team consists of experienced professionals dedicated to trauma care and mental health support.';
};
if (!mounted) {
return null;
}
@ -286,23 +272,6 @@ const BlogDetail: React.FC = () => {
<span>{blogData.readTime}</span>
</div>
</div>
{/* Author Info */}
<div className="flex items-start space-x-4 mt-6 p-4 rounded-lg" style={{ backgroundColor: '#f4f4f4' }}>
<div className="w-15 h-15 bg-gray-300 rounded-full flex items-center justify-center flex-shrink-0">
<span className="text-2xl font-medium" style={{ color: '#012068' }}>
{getAuthorName(blogData).charAt(0)}
</span>
</div>
<div>
<h3 className="font-medium" style={{ color: '#012068' }}>
{getAuthorName(blogData)}
</h3>
<p className="text-sm mt-1" style={{ color: '#666' }}>
{getAuthorBio(blogData)}
</p>
</div>
</div>
</header>
{/* Share Buttons */}

View File

@ -1,6 +1,6 @@
'use client'
import React, { useState, useEffect } from 'react';
import { Calendar, MapPin, Clock, Users, Star, Share2, ArrowLeft, ChevronRight } from 'lucide-react';
import { Calendar, MapPin, Clock, Users, Star, Share2, ArrowLeft, ChevronRight, ExternalLink } from 'lucide-react';
import { useRouter, useParams } from 'next/navigation';
import Link from "next/link";
import { eventAPI, Event } from '../../lib/api'; // Adjust path as needed
@ -75,7 +75,7 @@ const EventDetail = () => {
}
};
// NEW FUNCTION: Handle book seat click - redirects to admin-provided link
// Handle book seat click - redirects to admin-provided link
const handleBookSeat = () => {
if (!eventData) return;
@ -100,6 +100,28 @@ const EventDetail = () => {
}
};
// Handle learn more click - redirects to admin-provided learn more link
const handleLearnMore = () => {
if (!eventData) return;
// Check if admin has provided a custom learn more link
if (eventData.learnMoreLink && eventData.learnMoreLink.trim() !== '') {
// Validate if it's a proper URL
try {
// If the URL doesn't start with http:// or https://, add https://
let url = eventData.learnMoreLink;
if (!url.startsWith('http://') && !url.startsWith('https://')) {
url = 'https://' + url;
}
// Open in new tab
window.open(url, '_blank', 'noopener,noreferrer');
} catch (error) {
console.error('Invalid learn more link:', error);
alert('Invalid information link. Please contact the organizers.');
}
}
};
const handleShare = async () => {
if (!eventData || typeof window === 'undefined') return;
@ -348,9 +370,20 @@ const EventDetail = () => {
<div className="text-xs font-medium mb-2" style={{ color: '#e64838' }}>
{eventData.date}
</div>
<h1 className="text-2xl md:text-3xl font-medium leading-tight" style={{ color: '#012068' }}>
<h1 className="text-2xl md:text-3xl font-medium leading-tight mb-3" style={{ color: '#012068' }}>
{eventData.title}
</h1>
{/* Learn More Button - THIS IS THE NEW ADDITION */}
{eventData.learnMoreLink && eventData.learnMoreLink.trim() !== '' && (
<button
onClick={handleLearnMore}
className="inline-flex items-center text-xs hover:underline transition-all duration-200"
style={{ color: '#012068', opacity: 0.7 }}
>
<ExternalLink className="w-3 h-3 mr-1" />
Learn More
</button>
)}
</div>
<div className="text-left sm:text-right w-full sm:w-auto">
<div className="text-xs mb-1" style={{ color: '#012068', opacity: 0.8 }}>From</div>

View File

@ -3,68 +3,130 @@ import React, { useState, useEffect } from 'react';
import { useRouter } from 'next/navigation';
import { ChevronRight } from 'lucide-react';
import Link from "next/link";
import { eventAPI, Event } from '../../lib/api'; // Adjust path as needed
import { eventAPI, Event } from '../../lib/api';
import { educationService, Course } from '../../services/educationService';
// Combined type for events and training programs
type DisplayItem =
| (Event & { type: 'event'; itemDate: Date })
| (Course & {
type: 'training';
itemDate: Date;
mainImage: string;
date: string; // Add this field
detail: string;
galleryImages?: string[];
venue?: any[]
});
const MedicalEventsComponent = () => {
const [selectedPeriod, setSelectedPeriod] = useState('Upcoming Events');
const [activeTab, setActiveTab] = useState('Upcoming Events');
const [upcomingEvents, setUpcomingEvents] = useState<Event[]>([]);
const [pastEvents, setPastEvents] = useState<Event[]>([]);
const [nextEvent, setNextEvent] = useState<Event | null>(null);
const [upcomingItems, setUpcomingItems] = useState<DisplayItem[]>([]);
const [pastItems, setPastItems] = useState<DisplayItem[]>([]);
const [nextItem, setNextItem] = useState<DisplayItem | null>(null);
const [loading, setLoading] = useState(true);
const [searchTerm, setSearchTerm] = useState('');
const router = useRouter();
useEffect(() => {
loadEvents();
loadData();
}, []);
const loadEvents = async () => {
const loadData = async () => {
try {
setLoading(true);
const [upcoming, past] = await Promise.all([
eventAPI.getUpcomingEvents(),
eventAPI.getPastEvents()
const [events, trainingPrograms] = await Promise.all([
eventAPI.getAllEvents(), // Get all events (both upcoming and past)
educationService.getActiveCourses() // Get all training programs
]);
setUpcomingEvents(upcoming);
setPastEvents(past);
const currentDate = new Date();
currentDate.setHours(0, 0, 0, 0); // Set to start of day for accurate comparison
// Set the next event (first upcoming event)
// Transform events with type
const eventsWithType: DisplayItem[] = events.map(event => {
const eventDate = new Date(event.date);
return {
...event,
type: 'event' as const,
itemDate: eventDate
};
});
// Transform training programs (courses) with type
const trainingWithType: DisplayItem[] = trainingPrograms.map(course => {
const courseDate = course.startDate ? new Date(course.startDate) : new Date();
return {
...course,
type: 'training' as const,
itemDate: courseDate,
mainImage: course.image,
detail: `${course.duration} | ${course.seats} seats available | Led by ${course.instructor}`,
galleryImages: [], // Training programs don't have gallery images
date: course.startDate || 'Date TBD'
};
});
// Combine all items (events + training programs)
const allItems = [...eventsWithType, ...trainingWithType];
// Separate into upcoming and past based on date
const upcoming = allItems
.filter(item => item.itemDate >= currentDate)
.sort((a, b) => a.itemDate.getTime() - b.itemDate.getTime());
const past = allItems
.filter(item => item.itemDate < currentDate)
.sort((a, b) => b.itemDate.getTime() - a.itemDate.getTime()); // Most recent first
setUpcomingItems(upcoming);
setPastItems(past);
// Set the next item (first upcoming item)
if (upcoming.length > 0) {
setNextEvent(upcoming[0]);
setNextItem(upcoming[0]);
}
} catch (error) {
console.error('Error loading events:', error);
console.error('Error loading data:', error);
} finally {
setLoading(false);
}
};
// Filter events based on search term
const filteredEvents = () => {
const events = activeTab === 'Upcoming Events' ? upcomingEvents : pastEvents;
if (!searchTerm) return events;
// Filter items based on search term and active tab
const filteredItems = () => {
const items = activeTab === 'Upcoming Events' ? upcomingItems : pastItems;
if (!searchTerm) return items;
return events.filter(event =>
event.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
event.description.toLowerCase().includes(searchTerm.toLowerCase()) ||
event.subject.toLowerCase().includes(searchTerm.toLowerCase())
return items.filter(item =>
item.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
item.description.toLowerCase().includes(searchTerm.toLowerCase())
);
};
// Navigation function for App Router
const navigateToEventDetail = (eventId: string | number) => {
router.push(`/event-detail/${eventId}`);
// Navigation function
const navigateToDetail = (item: DisplayItem) => {
if (item.type === 'event') {
router.push(`/event-detail/${item.id}`);
} else {
// For training programs, navigate to course detail
router.push(`/education-training/course-detail?id=${item.id}`);
}
};
// Format price display from fees
const formatPrice = (event: Event) => {
if (event.fee && event.fee.length > 0) {
return `${event.fee[0].cost} per seat`;
// Format price display
const formatPrice = (item: DisplayItem) => {
if (item.type === 'event') {
if (item.fee && item.fee.length > 0) {
return `${item.fee[0].cost} per seat`;
}
return '₹1,800 per seat';
} else {
// Training program
return item.price !== 'N/A' ? item.price : 'Contact for pricing';
}
return '₹1,800 per seat'; // fallback price
};
// Get safe image URL with fallback
@ -72,8 +134,8 @@ const MedicalEventsComponent = () => {
return imageUrl && imageUrl.trim() !== '' ? imageUrl : fallback;
};
// Get gallery images with fallbacks
const getGalleryImages = (galleryImages: string[] | undefined) => {
// Get gallery images with fallbacks - only for events, not training programs
const getGalleryImages = (item: DisplayItem) => {
const fallbackImages = [
'https://images.unsplash.com/photo-1551601651-2a8555f1a136?w=200&h=100&fit=crop',
'https://images.unsplash.com/photo-1582750433449-648ed127bb54?w=200&h=100&fit=crop',
@ -81,6 +143,13 @@ const MedicalEventsComponent = () => {
'https://images.unsplash.com/photo-1551601651-2a8555f1a136?w=200&h=100&fit=crop'
];
if (item.type === 'training') {
// Training programs don't have gallery images
return [];
}
const galleryImages = item.galleryImages;
if (!galleryImages || galleryImages.length === 0) {
return fallbackImages;
}
@ -97,7 +166,7 @@ const MedicalEventsComponent = () => {
if (loading) {
return (
<div className="min-h-screen flex items-center justify-center">
<div className="text-lg" style={{ color: '#012068' }}>Loading events...</div>
<div className="text-lg" style={{ color: '#012068' }}>Loading events and training programs...</div>
</div>
);
}
@ -129,7 +198,7 @@ const MedicalEventsComponent = () => {
</h1>
</div>
<p className="text-sm md:text-base max-w-2xl leading-relaxed" style={{ color: '#333' }}>
Discover upcoming medical conferences, workshops, and professional development events
Discover upcoming medical conferences, workshops, training programs and professional development events
</p>
</div>
</div>
@ -164,7 +233,7 @@ const MedicalEventsComponent = () => {
<div className="relative w-full sm:w-64">
<input
type="text"
placeholder="Search an event..."
placeholder="Search events or training programs..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="border rounded-lg px-4 py-2 pl-4 pr-10 text-sm focus:outline-none w-full"
@ -184,20 +253,34 @@ const MedicalEventsComponent = () => {
</div>
</div>
{/* Next Event Hero Section */}
{nextEvent && (
{/* Next Item Hero Section */}
{nextItem && (
<div className="mb-8">
<h2 className="text-lg font-medium mb-4" style={{ color: '#012068' }}>Next Event</h2>
<h2 className="text-lg font-medium mb-4" style={{ color: '#012068' }}>
Next {nextItem.type === 'training' ? 'Training Program' : 'Event'}
</h2>
<div
className="bg-white border border-gray-100 rounded-lg overflow-hidden cursor-pointer hover:shadow-lg transition-shadow"
onClick={() => navigateToEventDetail(nextEvent.id)}
onClick={() => navigateToDetail(nextItem)}
>
<div className="relative h-48 md:h-64">
<img
src={getSafeImageUrl(nextEvent.mainImage, "https://images.unsplash.com/photo-1576091160550-2173dba999ef?w=800&h=300&fit=crop")}
alt={nextEvent.title}
src={getSafeImageUrl(nextItem.mainImage, "https://images.unsplash.com/photo-1576091160550-2173dba999ef?w=800&h=300&fit=crop")}
alt={nextItem.title}
className="w-full h-full object-cover"
/>
{/* Type Badge */}
<div className="absolute top-4 right-4">
<span
className="px-3 py-1 rounded text-xs font-medium"
style={{
backgroundColor: nextItem.type === 'event' ? '#e64838' : '#012068',
color: 'white'
}}
>
{nextItem.type === 'event' ? 'Event' : 'Training Program'}
</span>
</div>
<div className="absolute bottom-4 left-1/2 transform -translate-x-1/2 flex space-x-2">
<div className="w-2 h-2 bg-white rounded-full shadow"></div>
<div className="w-2 h-2 bg-gray-400 rounded-full"></div>
@ -207,26 +290,30 @@ const MedicalEventsComponent = () => {
<div className="p-4 md:p-6">
<div className="flex flex-col lg:flex-row lg:justify-between gap-4">
<div className="flex-1">
<div className="text-xs font-medium mb-3" style={{ color: '#e64838' }}>{nextEvent.date}</div>
<div className="text-xs font-medium mb-3" style={{ color: '#e64838' }}>
{nextItem.type === 'event' ? nextItem.date : (nextItem.date || 'Date TBD')}
</div>
<div className="text-lg md:text-xl font-medium mb-2" style={{ color: '#012068' }}>
{nextEvent.title}
{nextItem.title}
</div>
<div className="text-xs leading-relaxed mb-1" style={{ color: '#333' }}>
{nextEvent.description}
{nextItem.description}
</div>
<div className="text-xs leading-relaxed mb-4" style={{ color: '#333' }}>
{nextEvent.detail}
{nextItem.detail || (nextItem.type === 'event' ? nextItem.detail : '')}
</div>
<div className="flex flex-col sm:flex-row sm:items-center gap-2 sm:gap-4">
<div
className="text-xs cursor-pointer hover:underline"
style={{ color: '#012068' }}
>
Share Event
Share {nextItem.type === 'training' ? 'Training Program' : 'Event'}
</div>
{nextItem.type === 'event' && (
<div className="text-xs" style={{ color: '#012068' }}>
📍 {nextEvent.venue?.[0]?.address || 'Convention Center, Medical District'}
📍 {nextItem.venue?.[0]?.address || 'Convention Center, Medical District'}
</div>
)}
</div>
</div>
<div className="text-left lg:text-right">
@ -238,15 +325,17 @@ const MedicalEventsComponent = () => {
}}
onClick={(e) => {
e.stopPropagation();
console.log('Book seat clicked');
navigateToDetail(nextItem);
}}
>
Book Your Seat
{nextItem.type === 'training' ? 'Register Now' : 'Book Your Seat'}
</button>
<div className="text-sm font-medium" style={{ color: '#e64838' }}>{formatPrice(nextEvent)}</div>
<div className="text-sm font-medium" style={{ color: '#e64838' }}>{formatPrice(nextItem)}</div>
{nextItem.type === 'event' && (
<div className="text-xs mt-1" style={{ color: '#333' }}>
Early bird discount available
</div>
)}
</div>
</div>
</div>
@ -272,7 +361,7 @@ const MedicalEventsComponent = () => {
color: activeTab === 'Upcoming Events' ? '#012068' : '#012068'
}}
>
Upcoming Events ({upcomingEvents.length})
Upcoming Events ({upcomingItems.length})
</button>
<button
onClick={() => setActiveTab('Past Events')}
@ -286,63 +375,81 @@ const MedicalEventsComponent = () => {
color: activeTab === 'Past Events' ? '#012068' : '#012068'
}}
>
Past Events ({pastEvents.length})
Past Events ({pastItems.length})
</button>
</div>
</div>
{/* Event Rows */}
{/* Item Rows */}
<div className="space-y-6">
{filteredEvents().length === 0 ? (
{filteredItems().length === 0 ? (
<div className="text-center py-8">
<p className="text-gray-500">No events found.</p>
<p className="text-gray-500">No items found.</p>
</div>
) : (
filteredEvents().map((event) => (
filteredItems().map((item) => {
const galleryImages = getGalleryImages(item);
const showGallery = item.type === 'event' && galleryImages.length > 0;
return (
<div
key={event.id}
key={`${item.type}-${item.id}`}
className="bg-white border border-gray-100 rounded-lg p-4 md:p-6 cursor-pointer hover:shadow-lg transition-shadow"
onClick={() => navigateToEventDetail(event.id)}
onClick={() => navigateToDetail(item)}
>
<div className="flex flex-col md:flex-row gap-4">
{/* Images Section */}
<div className="flex flex-col sm:flex-row gap-1 md:w-auto">
{/* Main image */}
<div className="w-full sm:w-48 h-32 md:h-30 flex-shrink-0 rounded-xs overflow-hidden">
<div className="w-full sm:w-48 h-32 md:h-30 flex-shrink-0 rounded-xs overflow-hidden relative">
<img
src={getSafeImageUrl(event.mainImage, "https://images.unsplash.com/photo-1559757148-5c350d0d3c56?w=400&h=200&fit=crop")}
alt={event.title}
src={getSafeImageUrl(item.mainImage, "https://images.unsplash.com/photo-1559757148-5c350d0d3c56?w=400&h=200&fit=crop")}
alt={item.title}
className="w-full h-full object-cover"
/>
{/* Type Badge */}
<div className="absolute top-2 right-2">
<span
className="px-2 py-1 rounded text-xs font-medium"
style={{
backgroundColor: item.type === 'event' ? '#e64838' : '#012068',
color: 'white'
}}
>
{item.type === 'event' ? 'Event' : 'Training'}
</span>
</div>
</div>
{/* Gallery grid */}
{/* Gallery grid - only for events */}
{showGallery && (
<div className="grid grid-cols-2 gap-1 w-full sm:w-60 h-32 md:h-30">
{getGalleryImages(event.galleryImages).map((img, index) => (
{galleryImages.map((img, index) => (
<div key={index} className="rounded-xs overflow-hidden">
<img
src={img}
alt={`${event.title} gallery ${index + 1}`}
alt={`${item.title} gallery ${index + 1}`}
className="w-full h-full object-cover"
/>
</div>
))}
</div>
)}
</div>
{/* Event details */}
{/* Item details */}
<div className="flex-1 text-sm">
<div className="mb-1 font-medium text-xs" style={{ color: '#e64838' }}>
{event.date}
{item.type === 'event' ? item.date : item.date}
</div>
<div className="font-medium mb-2 text-lg md:text-xl" style={{ color: '#012068' }}>
{event.title}
{item.title}
</div>
<div className="text-xs leading-relaxed mb-1" style={{ color: '#333' }}>
{event.description}
{item.description}
</div>
<div className="text-xs leading-relaxed mb-4" style={{ color: '#333' }}>
{event.detail}
{item.type === 'event' ? item.detail : item.detail}
</div>
<div className="flex flex-col sm:flex-row sm:items-center gap-2 sm:gap-4">
<button
@ -350,20 +457,21 @@ const MedicalEventsComponent = () => {
style={{ color: '#012068' }}
onClick={(e) => {
e.stopPropagation();
navigateToEventDetail(event.id);
navigateToDetail(item);
}}
>
View Details
</button>
<span className="text-gray-400 hidden sm:inline">|</span>
<span className="text-xs font-medium" style={{ color: '#e64838' }}>
{formatPrice(event)}
{formatPrice(item)}
</span>
</div>
</div>
</div>
</div>
))
);
})
)}
</div>
</div>
@ -374,11 +482,13 @@ const MedicalEventsComponent = () => {
Recent Past Events
</h3>
<div className="grid sm:grid-cols-2 lg:grid-cols-1 gap-6">
{pastEvents.slice(0, 2).map((event) => (
{pastItems.filter(item => item.type === 'event').slice(0, 2).map((item) => {
const event = item as Event & { type: 'event'; itemDate: Date };
return (
<div
key={event.id}
className="cursor-pointer hover:bg-gray-50 p-2 rounded-lg transition-colors"
onClick={() => navigateToEventDetail(event.id)}
onClick={() => router.push(`/event-detail/${event.id}`)}
>
<div className="w-full h-32 lg:h-28 mb-3 overflow-hidden rounded-xs">
<img
@ -402,7 +512,8 @@ const MedicalEventsComponent = () => {
</div>
</div>
</div>
))}
);
})}
</div>
</div>
</div>

View File

@ -249,7 +249,7 @@ const TeamListing: React.FC<TeamListingProps> = ({
<div className="max-w-7xl mx-auto px-4">
<div className="mb-8">
<h2 className="text-2xl font-bold mb-4" style={{ color: '#012068' }}>
Retired Staff
FACULTY ALUMNI
</h2>
<p className="text-sm mb-2" style={{ color: '#666' }}>
Honoring our retired faculty and staff members for their dedicated service

View File

@ -2,60 +2,113 @@
import React, { useState, useEffect } from 'react';
import { useRouter } from 'next/navigation';
import { eventAPI, Event } from '../../lib/api';
import { educationService, Course } from '../../services/educationService';
// Combined type for events and courses
type EventOrCourse = (Event & { type: 'event' }) | (Course & { type: 'course', date: string, mainImage: string });
const EventsSection = () => {
const router = useRouter();
const [upcomingEvents, setUpcomingEvents] = useState<Event[]>([]);
const [pastEvents, setPastEvents] = useState<Event[]>([]);
const [courses, setCourses] = useState<Course[]>([]);
const [loading, setLoading] = useState(true);
const [combinedUpcoming, setCombinedUpcoming] = useState<EventOrCourse[]>([]);
useEffect(() => {
loadEvents();
loadData();
}, []);
const loadEvents = async () => {
const loadData = async () => {
try {
setLoading(true);
const [upcoming, past] = await Promise.all([
const [upcoming, past, activeCourses] = await Promise.all([
eventAPI.getUpcomingEvents(),
eventAPI.getPastEvents()
eventAPI.getPastEvents(),
educationService.getActiveCourses()
]);
setUpcomingEvents(upcoming);
setPastEvents(past);
setCourses(activeCourses);
// Combine events and courses for display
const eventsWithType: EventOrCourse[] = upcoming.map(event => ({
...event,
type: 'event' as const
}));
const coursesWithType: EventOrCourse[] = activeCourses.map(course => ({
...course,
type: 'course' as const,
date: course.startDate || 'Date TBD',
mainImage: course.image
}));
// Combine and sort by date
const combined = [...eventsWithType, ...coursesWithType].sort((a, b) => {
const dateA = new Date(a.date);
const dateB = new Date(b.date);
return dateA.getTime() - dateB.getTime();
});
setCombinedUpcoming(combined);
} catch (error) {
console.error('Error loading events:', error);
console.error('Error loading data:', error);
} finally {
setLoading(false);
}
};
// Navigation function for App Router
const navigateToEventDetail = (eventId: string | number) => {
router.push(`/event-detail/${eventId}`);
// Navigation functions
const navigateToDetail = (item: EventOrCourse) => {
if (item.type === 'event') {
router.push(`/event-detail/${item.id}`);
} else {
router.push(`/education-training/course-detail?id=${item.id}`);
}
};
const navigateToAllEvents = () => {
router.push('/events');
};
// Format price display from fees
const formatPrice = (event: Event) => {
if (event.fee && event.fee.length > 0) {
return `${event.fee[0].cost} per ticket`;
// Format price display
const formatPrice = (item: EventOrCourse) => {
if (item.type === 'event') {
if (item.fee && item.fee.length > 0) {
return `${item.fee[0].cost} per ticket`;
}
return '₹1,800 per ticket';
} else {
return item.price !== 'N/A' ? item.price : 'Contact for pricing';
}
};
// Top section events (first 4 events for the grid)
const topEvents = upcomingEvents.slice(0, 4);
// Get title for the item
const getTitle = (item: EventOrCourse) => {
return item.title;
};
// Featured event (first event)
const featuredEvent = upcomingEvents.length > 0 ? upcomingEvents[0] : null;
// Get description for the item
const getDescription = (item: EventOrCourse) => {
if (item.type === 'event') {
return item.description;
} else {
return item.description;
}
};
// Top section items (first 4 for the grid)
const topItems = combinedUpcoming.slice(0, 4);
// Featured item (first item)
const featuredItem = combinedUpcoming.length > 0 ? combinedUpcoming[0] : null;
if (loading) {
return (
<div className="py-12 px-6 sm:px-8 md:px-6 lg:px-6 xl:px-6 bg-white max-w-7xl mx-auto">
<div className="text-center" style={{ color: '#012068' }}>Loading events...</div>
<div className="text-center" style={{ color: '#012068' }}>Loading events and courses...</div>
</div>
);
}
@ -65,7 +118,7 @@ const EventsSection = () => {
{/* Section Header */}
<div className="flex justify-between items-center mb-8">
<h2 className="text-2xl md:text-3xl font-bold" style={{ color: '#012068' }}>
Our Events
Our Events & Courses
</h2>
<button
className="text-sm font-medium hover:opacity-70 transition-opacity duration-200"
@ -76,39 +129,52 @@ const EventsSection = () => {
</button>
</div>
{/* Events Grid - Top Section with Event Details */}
{topEvents.length === 0 ? (
{/* Events/Courses Grid - Top Section with Details */}
{topItems.length === 0 ? (
<div className="text-center py-8 mb-12">
<p className="text-gray-500">No upcoming events.</p>
<p className="text-gray-500">No upcoming events or courses.</p>
</div>
) : (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-12">
{topEvents.map((event, index) => (
{topItems.map((item, index) => (
<div
key={event.id}
key={`${item.type}-${item.id}`}
className={`relative rounded-lg h-48 overflow-hidden cursor-pointer hover:shadow-lg transition-shadow ${
index === 2 ? 'lg:hidden xl:block' : ''
} ${index === 3 ? 'md:col-span-2 lg:col-span-1' : ''}`}
onClick={() => navigateToEventDetail(event.id)}
onClick={() => navigateToDetail(item)}
>
<img
src={event.mainImage}
alt={event.title}
src={item.mainImage}
alt={getTitle(item)}
className="w-full h-full object-cover"
/>
{/* Gradient overlay for better text readability */}
<div className="absolute inset-0 bg-gradient-to-t from-black/70 via-black/20 to-transparent"></div>
{/* Event details overlay */}
{/* Item type badge */}
<div className="absolute top-3 right-3">
<span
className="text-xs font-medium px-2 py-1 rounded"
style={{
backgroundColor: item.type === 'event' ? '#e64838' : '#012068',
color: 'white'
}}
>
{item.type === 'event' ? 'Event' : 'Course'}
</span>
</div>
{/* Item details overlay */}
<div className="absolute bottom-0 left-0 right-0 p-4 text-white">
<div className="text-xs font-medium mb-1" style={{ color: '#e64838' }}>
{event.date}
{item.date}
</div>
<h4 className="font-medium mb-2 text-sm leading-tight">
{event.title}
<h4 className="font-medium mb-2 text-sm leading-tight line-clamp-2">
{getTitle(item)}
</h4>
<div className="text-xs font-medium">
{formatPrice(event)}
{formatPrice(item)}
</div>
</div>
</div>
@ -116,26 +182,40 @@ const EventsSection = () => {
</div>
)}
{/* Featured Events Section */}
{/* Featured Section */}
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
{/* Left - Large Featured Event */}
{/* Left - Large Featured Item */}
<div className="lg:col-span-2 px-4 py-4 rounded-lg" style={{ backgroundColor: '#f4f4f4' }}>
<h3 className="text-xl font-semibold mb-4" style={{ color: '#012068' }}>Upcoming Events</h3>
{!featuredEvent ? (
<h3 className="text-xl font-semibold mb-4" style={{ color: '#012068' }}>
Featured {featuredItem?.type === 'course' ? 'Course' : 'Event'}
</h3>
{!featuredItem ? (
<div className="text-center py-8">
<p className="text-gray-500">No upcoming events.</p>
<p className="text-gray-500">No upcoming events or courses.</p>
</div>
) : (
<div
className="bg-white border border-gray-100 rounded-lg overflow-hidden cursor-pointer hover:shadow-lg transition-shadow"
onClick={() => navigateToEventDetail(featuredEvent.id)}
onClick={() => navigateToDetail(featuredItem)}
>
<div className="relative h-48 md:h-64">
<img
src={featuredEvent.mainImage}
alt={featuredEvent.title}
src={featuredItem.mainImage}
alt={getTitle(featuredItem)}
className="w-full h-full object-cover"
/>
{/* Item type badge */}
<div className="absolute top-4 right-4">
<span
className="text-xs font-medium px-3 py-1 rounded"
style={{
backgroundColor: featuredItem.type === 'event' ? '#e64838' : '#012068',
color: 'white'
}}
>
{featuredItem.type === 'event' ? 'Event' : 'Course'}
</span>
</div>
{/* Pagination dots */}
<div className="absolute bottom-4 left-1/2 transform -translate-x-1/2 flex space-x-2">
<div className="w-2 h-2 bg-white rounded-full shadow"></div>
@ -147,27 +227,47 @@ const EventsSection = () => {
<div className="flex flex-col lg:flex-row lg:justify-between gap-4">
<div className="flex-1">
<div className="text-xs font-medium mb-3" style={{ color: '#e64838' }}>
{featuredEvent.date}
{featuredItem.date}
</div>
<h4 className="text-lg md:text-xl font-medium mb-2" style={{ color: '#012068' }}>
{featuredEvent.title}
{getTitle(featuredItem)}
</h4>
<div className="text-xs leading-relaxed mb-1" style={{ color: '#333' }}>
{featuredEvent.description}
</div>
<div className="text-xs leading-relaxed mb-4" style={{ color: '#333' }}>
{featuredEvent.detail}
{getDescription(featuredItem)}
</div>
{featuredItem.type === 'event' && (
<div className="text-xs leading-relaxed mb-4" style={{ color: '#333' }}>
{featuredItem.detail}
</div>
)}
{featuredItem.type === 'course' && (
<div className="flex items-center gap-4 mb-4 text-xs" style={{ color: '#012068' }}>
<div className="flex items-center">
<span className="mr-1"></span>
<span>{featuredItem.duration}</span>
</div>
<div className="flex items-center">
<span className="mr-1">👥</span>
<span>{featuredItem.seats} seats</span>
</div>
<div className="flex items-center">
<span className="mr-1">👨🏫</span>
<span>{featuredItem.instructor}</span>
</div>
</div>
)}
<div className="flex flex-col sm:flex-row sm:items-center gap-2 sm:gap-4">
<div
className="text-xs cursor-pointer hover:underline"
style={{ color: '#012068' }}
>
Share Event
Share {featuredItem.type === 'course' ? 'Course' : 'Event'}
</div>
{featuredItem.type === 'event' && (
<div className="text-xs" style={{ color: '#012068' }}>
📍 {featuredEvent.venue?.[0]?.address || 'Convention Center, Medical District'}
📍 {featuredItem.venue?.[0]?.address || 'Convention Center, Medical District'}
</div>
)}
</div>
</div>
<div className="text-left lg:text-right">
@ -179,17 +279,19 @@ const EventsSection = () => {
}}
onClick={(e) => {
e.stopPropagation();
console.log('Book seat clicked');
navigateToDetail(featuredItem);
}}
>
Book Your Seat
{featuredItem.type === 'course' ? 'Apply Now' : 'Book Your Seat'}
</button>
<div className="text-sm font-medium" style={{ color: '#e64838' }}>
{formatPrice(featuredEvent)}
{formatPrice(featuredItem)}
</div>
{featuredItem.type === 'event' && (
<div className="text-xs mt-1" style={{ color: '#012068', opacity: 0.8 }}>
Early bird discount available
</div>
)}
</div>
</div>
</div>
@ -197,7 +299,7 @@ const EventsSection = () => {
)}
</div>
{/* Right - Event List with Scroller */}
{/* Right - Past Events List with Scroller */}
<div className="lg:col-span-1">
<h3 className="text-xl font-semibold mb-4" style={{ color: '#012068' }}>Past Events</h3>
{pastEvents.length === 0 ? (
@ -210,7 +312,7 @@ const EventsSection = () => {
<div
key={event.id}
className="bg-white border border-gray-100 rounded-lg overflow-hidden cursor-pointer hover:shadow-lg transition-shadow flex-shrink-0"
onClick={() => navigateToEventDetail(event.id)}
onClick={() => router.push(`/event-detail/${event.id}`)}
>
<div className="h-24">
<img
@ -226,7 +328,7 @@ const EventsSection = () => {
<h4 className="font-medium mb-1 text-sm" style={{ color: '#012068' }}>
{event.title}
</h4>
<p className="text-xs leading-relaxed" style={{ color: '#333' }}>
<p className="text-xs leading-relaxed line-clamp-2" style={{ color: '#333' }}>
{event.description}
</p>
<div className="mt-2 flex justify-between items-center">
@ -235,14 +337,11 @@ const EventsSection = () => {
style={{ color: '#012068' }}
onClick={(e) => {
e.stopPropagation();
navigateToEventDetail(event.id);
router.push(`/event-detail/${event.id}`);
}}
>
View Details
</button>
{/* <span className="text-xs font-medium" style={{ color: '#e64838' }}>
{formatPrice(event)}
</span> */}
</div>
</div>
</div>

View File

@ -18,7 +18,8 @@ export interface Event {
phone: string;
email: string;
isActive: boolean;
bookSeatLink?: string; // NEW FIELD
bookSeatLink?: string; // Registration/booking link
learnMoreLink?: string; // Additional information link
professors?: Professor[];
}