Changes on 19-11-2025
This commit is contained in:
@ -7,18 +7,24 @@ import { usePathname } from "next/navigation";
|
|||||||
|
|
||||||
const Header = () => {
|
const Header = () => {
|
||||||
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
||||||
|
const [isServicesOpen, setIsServicesOpen] = useState(false);
|
||||||
|
const [isMobileServicesOpen, setIsMobileServicesOpen] = useState(false);
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
|
|
||||||
const closeAllMenus = () => {
|
const closeAllMenus = () => {
|
||||||
setIsMenuOpen(false);
|
setIsMenuOpen(false);
|
||||||
|
setIsServicesOpen(false);
|
||||||
|
setIsMobileServicesOpen(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const menuItems = [
|
const menuItems = [
|
||||||
{ href: "/", label: "Home" },
|
{ href: "/", label: "Home" },
|
||||||
{ href: "/about", label: "About" },
|
{ href: "/about", label: "About" },
|
||||||
{ href: "/teamMember", label: "Our Team" },
|
{ href: "/teamMember", label: "Our Team" },
|
||||||
{ href: "/education-training", label: "Academics & Research" },
|
{ href: "/education-training", label: "Academics & Research" },
|
||||||
{ href: "/services", label: "Services" },
|
{ href: "/services", label: "Services", hasDropdown: true },
|
||||||
{ href: "/events", label: "Events" },
|
{ href: "/events", label: "Events" },
|
||||||
{ href: "/blogs", label: "Blogs" },
|
{ href: "/blogs", label: "Blogs" },
|
||||||
{ href: "/career", label: "Career" },
|
{ href: "/career", label: "Career" },
|
||||||
@ -77,6 +83,50 @@ const Header = () => {
|
|||||||
{menuItems.map((item, idx) => {
|
{menuItems.map((item, idx) => {
|
||||||
const isActive = pathname === item.href ||
|
const isActive = pathname === item.href ||
|
||||||
(item.href !== "/" && pathname.startsWith(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 (
|
return (
|
||||||
<Link
|
<Link
|
||||||
key={idx}
|
key={idx}
|
||||||
@ -95,7 +145,7 @@ const Header = () => {
|
|||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
{/* Mobile menu button - More visible */}
|
{/* Mobile menu button */}
|
||||||
<button
|
<button
|
||||||
className="lg:hidden p-3 text-white hover:bg-red-600 transition-colors w-full flex items-center justify-between"
|
className="lg:hidden p-3 text-white hover:bg-red-600 transition-colors w-full flex items-center justify-between"
|
||||||
onClick={() => setIsMenuOpen(!isMenuOpen)}
|
onClick={() => setIsMenuOpen(!isMenuOpen)}
|
||||||
@ -136,6 +186,47 @@ const Header = () => {
|
|||||||
{menuItems.map((item, idx) => {
|
{menuItems.map((item, idx) => {
|
||||||
const isActive = pathname === item.href ||
|
const isActive = pathname === item.href ||
|
||||||
(item.href !== "/" && pathname.startsWith(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 (
|
return (
|
||||||
<li key={idx}>
|
<li key={idx}>
|
||||||
<Link
|
<Link
|
||||||
|
|||||||
@ -64,10 +64,10 @@ const StatisticsTiles = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="py-8 sm:py-12" style={{ backgroundColor: '#f4f4f4' }}>
|
<section className="py-12 sm:py-16" style={{ backgroundColor: '#f4f4f4' }}>
|
||||||
<div className="max-w-7xl mx-auto px-4">
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||||
<h2
|
<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' }}
|
style={{ color: '#012068' }}
|
||||||
>
|
>
|
||||||
Our Trauma Care Services
|
Our Trauma Care Services
|
||||||
@ -86,33 +86,31 @@ const StatisticsTiles = () => {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
// Display tiles
|
// Display services one after another
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 lg:gap-6">
|
<div className="space-y-8">
|
||||||
{tiles.map((tile, index) => (
|
{tiles.map((tile, index) => (
|
||||||
<div
|
<div key={tile.id} className="text-left">
|
||||||
key={tile.id}
|
<h3
|
||||||
className="border border-gray-300 rounded-lg p-5 h-full hover:shadow-lg transition-shadow duration-300"
|
className="text-xl sm:text-2xl font-bold mb-4 pb-2 border-b-2 inline-block"
|
||||||
style={{ backgroundColor: '#ffffff' }}
|
style={{ color: '#012068', borderColor: '#012068' }}
|
||||||
>
|
>
|
||||||
<div className="flex flex-col h-full">
|
{tile.title}
|
||||||
<h3
|
</h3>
|
||||||
className="text-base sm:text-lg font-semibold mb-3 text-left"
|
<ul className="space-y-2.5 mt-4">
|
||||||
style={{ color: '#012068' }}
|
{parseDescription(tile.description).map((point, idx) => (
|
||||||
>
|
<li
|
||||||
{tile.title}
|
key={idx}
|
||||||
</h3>
|
className="text-sm sm:text-base leading-relaxed flex items-start"
|
||||||
<ul className="list-disc pl-5 space-y-2 flex-grow text-left">
|
style={{ color: '#333' }}
|
||||||
{parseDescription(tile.description).map((point, idx) => (
|
>
|
||||||
<li
|
<span
|
||||||
key={idx}
|
className="inline-block w-1.5 h-1.5 rounded-full mt-2 mr-3 flex-shrink-0"
|
||||||
className="text-sm sm:text-base leading-relaxed text-left"
|
style={{ backgroundColor: '#012068' }}
|
||||||
style={{ color: '#333' }}
|
></span>
|
||||||
>
|
<span>{point}</span>
|
||||||
{point}
|
</li>
|
||||||
</li>
|
))}
|
||||||
))}
|
</ul>
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -116,20 +116,6 @@ const BlogDetail: React.FC = () => {
|
|||||||
target.src = '/images/default-blog-image.jpg';
|
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) {
|
if (!mounted) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -286,23 +272,6 @@ const BlogDetail: React.FC = () => {
|
|||||||
<span>{blogData.readTime}</span>
|
<span>{blogData.readTime}</span>
|
||||||
</div>
|
</div>
|
||||||
</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>
|
</header>
|
||||||
|
|
||||||
{/* Share Buttons */}
|
{/* Share Buttons */}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import React, { useState, useEffect } from 'react';
|
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 { useRouter, useParams } from 'next/navigation';
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { eventAPI, Event } from '../../lib/api'; // Adjust path as needed
|
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 = () => {
|
const handleBookSeat = () => {
|
||||||
if (!eventData) return;
|
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 () => {
|
const handleShare = async () => {
|
||||||
if (!eventData || typeof window === 'undefined') return;
|
if (!eventData || typeof window === 'undefined') return;
|
||||||
|
|
||||||
@ -348,9 +370,20 @@ const EventDetail = () => {
|
|||||||
<div className="text-xs font-medium mb-2" style={{ color: '#e64838' }}>
|
<div className="text-xs font-medium mb-2" style={{ color: '#e64838' }}>
|
||||||
{eventData.date}
|
{eventData.date}
|
||||||
</div>
|
</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}
|
{eventData.title}
|
||||||
</h1>
|
</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>
|
||||||
<div className="text-left sm:text-right w-full sm:w-auto">
|
<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>
|
<div className="text-xs mb-1" style={{ color: '#012068', opacity: 0.8 }}>From</div>
|
||||||
|
|||||||
@ -3,68 +3,130 @@ import React, { useState, useEffect } from 'react';
|
|||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { ChevronRight } from 'lucide-react';
|
import { ChevronRight } from 'lucide-react';
|
||||||
import Link from "next/link";
|
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 MedicalEventsComponent = () => {
|
||||||
const [selectedPeriod, setSelectedPeriod] = useState('Upcoming Events');
|
const [selectedPeriod, setSelectedPeriod] = useState('Upcoming Events');
|
||||||
const [activeTab, setActiveTab] = useState('Upcoming Events');
|
const [activeTab, setActiveTab] = useState('Upcoming Events');
|
||||||
const [upcomingEvents, setUpcomingEvents] = useState<Event[]>([]);
|
const [upcomingItems, setUpcomingItems] = useState<DisplayItem[]>([]);
|
||||||
const [pastEvents, setPastEvents] = useState<Event[]>([]);
|
const [pastItems, setPastItems] = useState<DisplayItem[]>([]);
|
||||||
const [nextEvent, setNextEvent] = useState<Event | null>(null);
|
const [nextItem, setNextItem] = useState<DisplayItem | null>(null);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [searchTerm, setSearchTerm] = useState('');
|
const [searchTerm, setSearchTerm] = useState('');
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadEvents();
|
loadData();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const loadEvents = async () => {
|
const loadData = async () => {
|
||||||
try {
|
try {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
const [upcoming, past] = await Promise.all([
|
const [events, trainingPrograms] = await Promise.all([
|
||||||
eventAPI.getUpcomingEvents(),
|
eventAPI.getAllEvents(), // Get all events (both upcoming and past)
|
||||||
eventAPI.getPastEvents()
|
educationService.getActiveCourses() // Get all training programs
|
||||||
]);
|
]);
|
||||||
|
|
||||||
setUpcomingEvents(upcoming);
|
const currentDate = new Date();
|
||||||
setPastEvents(past);
|
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) {
|
if (upcoming.length > 0) {
|
||||||
setNextEvent(upcoming[0]);
|
setNextItem(upcoming[0]);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error loading events:', error);
|
console.error('Error loading data:', error);
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Filter events based on search term
|
// Filter items based on search term and active tab
|
||||||
const filteredEvents = () => {
|
const filteredItems = () => {
|
||||||
const events = activeTab === 'Upcoming Events' ? upcomingEvents : pastEvents;
|
const items = activeTab === 'Upcoming Events' ? upcomingItems : pastItems;
|
||||||
if (!searchTerm) return events;
|
if (!searchTerm) return items;
|
||||||
|
|
||||||
return events.filter(event =>
|
return items.filter(item =>
|
||||||
event.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
item.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||||
event.description.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
item.description.toLowerCase().includes(searchTerm.toLowerCase())
|
||||||
event.subject.toLowerCase().includes(searchTerm.toLowerCase())
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Navigation function for App Router
|
// Navigation function
|
||||||
const navigateToEventDetail = (eventId: string | number) => {
|
const navigateToDetail = (item: DisplayItem) => {
|
||||||
router.push(`/event-detail/${eventId}`);
|
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
|
// Format price display
|
||||||
const formatPrice = (event: Event) => {
|
const formatPrice = (item: DisplayItem) => {
|
||||||
if (event.fee && event.fee.length > 0) {
|
if (item.type === 'event') {
|
||||||
return `₹${event.fee[0].cost} per seat`;
|
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
|
// Get safe image URL with fallback
|
||||||
@ -72,8 +134,8 @@ const MedicalEventsComponent = () => {
|
|||||||
return imageUrl && imageUrl.trim() !== '' ? imageUrl : fallback;
|
return imageUrl && imageUrl.trim() !== '' ? imageUrl : fallback;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get gallery images with fallbacks
|
// Get gallery images with fallbacks - only for events, not training programs
|
||||||
const getGalleryImages = (galleryImages: string[] | undefined) => {
|
const getGalleryImages = (item: DisplayItem) => {
|
||||||
const fallbackImages = [
|
const fallbackImages = [
|
||||||
'https://images.unsplash.com/photo-1551601651-2a8555f1a136?w=200&h=100&fit=crop',
|
'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',
|
'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'
|
'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) {
|
if (!galleryImages || galleryImages.length === 0) {
|
||||||
return fallbackImages;
|
return fallbackImages;
|
||||||
}
|
}
|
||||||
@ -97,7 +166,7 @@ const MedicalEventsComponent = () => {
|
|||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen flex items-center justify-center">
|
<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>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -129,7 +198,7 @@ const MedicalEventsComponent = () => {
|
|||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm md:text-base max-w-2xl leading-relaxed" style={{ color: '#333' }}>
|
<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>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -164,7 +233,7 @@ const MedicalEventsComponent = () => {
|
|||||||
<div className="relative w-full sm:w-64">
|
<div className="relative w-full sm:w-64">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Search an event..."
|
placeholder="Search events or training programs..."
|
||||||
value={searchTerm}
|
value={searchTerm}
|
||||||
onChange={(e) => setSearchTerm(e.target.value)}
|
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"
|
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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Next Event Hero Section */}
|
{/* Next Item Hero Section */}
|
||||||
{nextEvent && (
|
{nextItem && (
|
||||||
<div className="mb-8">
|
<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
|
<div
|
||||||
className="bg-white border border-gray-100 rounded-lg overflow-hidden cursor-pointer hover:shadow-lg transition-shadow"
|
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">
|
<div className="relative h-48 md:h-64">
|
||||||
<img
|
<img
|
||||||
src={getSafeImageUrl(nextEvent.mainImage, "https://images.unsplash.com/photo-1576091160550-2173dba999ef?w=800&h=300&fit=crop")}
|
src={getSafeImageUrl(nextItem.mainImage, "https://images.unsplash.com/photo-1576091160550-2173dba999ef?w=800&h=300&fit=crop")}
|
||||||
alt={nextEvent.title}
|
alt={nextItem.title}
|
||||||
className="w-full h-full object-cover"
|
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="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-white rounded-full shadow"></div>
|
||||||
<div className="w-2 h-2 bg-gray-400 rounded-full"></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="p-4 md:p-6">
|
||||||
<div className="flex flex-col lg:flex-row lg:justify-between gap-4">
|
<div className="flex flex-col lg:flex-row lg:justify-between gap-4">
|
||||||
<div className="flex-1">
|
<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' }}>
|
<div className="text-lg md:text-xl font-medium mb-2" style={{ color: '#012068' }}>
|
||||||
{nextEvent.title}
|
{nextItem.title}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xs leading-relaxed mb-1" style={{ color: '#333' }}>
|
<div className="text-xs leading-relaxed mb-1" style={{ color: '#333' }}>
|
||||||
{nextEvent.description}
|
{nextItem.description}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xs leading-relaxed mb-4" style={{ color: '#333' }}>
|
<div className="text-xs leading-relaxed mb-4" style={{ color: '#333' }}>
|
||||||
{nextEvent.detail}
|
{nextItem.detail || (nextItem.type === 'event' ? nextItem.detail : '')}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col sm:flex-row sm:items-center gap-2 sm:gap-4">
|
<div className="flex flex-col sm:flex-row sm:items-center gap-2 sm:gap-4">
|
||||||
<div
|
<div
|
||||||
className="text-xs cursor-pointer hover:underline"
|
className="text-xs cursor-pointer hover:underline"
|
||||||
style={{ color: '#012068' }}
|
style={{ color: '#012068' }}
|
||||||
>
|
>
|
||||||
Share Event
|
Share {nextItem.type === 'training' ? 'Training Program' : 'Event'}
|
||||||
</div>
|
|
||||||
<div className="text-xs" style={{ color: '#012068' }}>
|
|
||||||
📍 {nextEvent.venue?.[0]?.address || 'Convention Center, Medical District'}
|
|
||||||
</div>
|
</div>
|
||||||
|
{nextItem.type === 'event' && (
|
||||||
|
<div className="text-xs" style={{ color: '#012068' }}>
|
||||||
|
📍 {nextItem.venue?.[0]?.address || 'Convention Center, Medical District'}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-left lg:text-right">
|
<div className="text-left lg:text-right">
|
||||||
@ -238,15 +325,17 @@ const MedicalEventsComponent = () => {
|
|||||||
}}
|
}}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
console.log('Book seat clicked');
|
navigateToDetail(nextItem);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Book Your Seat
|
{nextItem.type === 'training' ? 'Register Now' : 'Book Your Seat'}
|
||||||
</button>
|
</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>
|
||||||
<div className="text-xs mt-1" style={{ color: '#333' }}>
|
{nextItem.type === 'event' && (
|
||||||
Early bird discount available
|
<div className="text-xs mt-1" style={{ color: '#333' }}>
|
||||||
</div>
|
Early bird discount available
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -272,7 +361,7 @@ const MedicalEventsComponent = () => {
|
|||||||
color: activeTab === 'Upcoming Events' ? '#012068' : '#012068'
|
color: activeTab === 'Upcoming Events' ? '#012068' : '#012068'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Upcoming Events ({upcomingEvents.length})
|
Upcoming Events ({upcomingItems.length})
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => setActiveTab('Past Events')}
|
onClick={() => setActiveTab('Past Events')}
|
||||||
@ -286,84 +375,103 @@ const MedicalEventsComponent = () => {
|
|||||||
color: activeTab === 'Past Events' ? '#012068' : '#012068'
|
color: activeTab === 'Past Events' ? '#012068' : '#012068'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Past Events ({pastEvents.length})
|
Past Events ({pastItems.length})
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Event Rows */}
|
{/* Item Rows */}
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
{filteredEvents().length === 0 ? (
|
{filteredItems().length === 0 ? (
|
||||||
<div className="text-center py-8">
|
<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>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
filteredEvents().map((event) => (
|
filteredItems().map((item) => {
|
||||||
<div
|
const galleryImages = getGalleryImages(item);
|
||||||
key={event.id}
|
const showGallery = item.type === 'event' && galleryImages.length > 0;
|
||||||
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)}
|
return (
|
||||||
>
|
<div
|
||||||
<div className="flex flex-col md:flex-row gap-4">
|
key={`${item.type}-${item.id}`}
|
||||||
{/* Images Section */}
|
className="bg-white border border-gray-100 rounded-lg p-4 md:p-6 cursor-pointer hover:shadow-lg transition-shadow"
|
||||||
<div className="flex flex-col sm:flex-row gap-1 md:w-auto">
|
onClick={() => navigateToDetail(item)}
|
||||||
{/* Main image */}
|
>
|
||||||
<div className="w-full sm:w-48 h-32 md:h-30 flex-shrink-0 rounded-xs overflow-hidden">
|
<div className="flex flex-col md:flex-row gap-4">
|
||||||
<img
|
{/* Images Section */}
|
||||||
src={getSafeImageUrl(event.mainImage, "https://images.unsplash.com/photo-1559757148-5c350d0d3c56?w=400&h=200&fit=crop")}
|
<div className="flex flex-col sm:flex-row gap-1 md:w-auto">
|
||||||
alt={event.title}
|
{/* Main image */}
|
||||||
className="w-full h-full object-cover"
|
<div className="w-full sm:w-48 h-32 md:h-30 flex-shrink-0 rounded-xs overflow-hidden relative">
|
||||||
/>
|
<img
|
||||||
|
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 - only for events */}
|
||||||
|
{showGallery && (
|
||||||
|
<div className="grid grid-cols-2 gap-1 w-full sm:w-60 h-32 md:h-30">
|
||||||
|
{galleryImages.map((img, index) => (
|
||||||
|
<div key={index} className="rounded-xs overflow-hidden">
|
||||||
|
<img
|
||||||
|
src={img}
|
||||||
|
alt={`${item.title} gallery ${index + 1}`}
|
||||||
|
className="w-full h-full object-cover"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Gallery grid */}
|
{/* Item details */}
|
||||||
<div className="grid grid-cols-2 gap-1 w-full sm:w-60 h-32 md:h-30">
|
<div className="flex-1 text-sm">
|
||||||
{getGalleryImages(event.galleryImages).map((img, index) => (
|
<div className="mb-1 font-medium text-xs" style={{ color: '#e64838' }}>
|
||||||
<div key={index} className="rounded-xs overflow-hidden">
|
{item.type === 'event' ? item.date : item.date}
|
||||||
<img
|
</div>
|
||||||
src={img}
|
<div className="font-medium mb-2 text-lg md:text-xl" style={{ color: '#012068' }}>
|
||||||
alt={`${event.title} gallery ${index + 1}`}
|
{item.title}
|
||||||
className="w-full h-full object-cover"
|
</div>
|
||||||
/>
|
<div className="text-xs leading-relaxed mb-1" style={{ color: '#333' }}>
|
||||||
</div>
|
{item.description}
|
||||||
))}
|
</div>
|
||||||
</div>
|
<div className="text-xs leading-relaxed mb-4" style={{ color: '#333' }}>
|
||||||
</div>
|
{item.type === 'event' ? item.detail : item.detail}
|
||||||
|
</div>
|
||||||
{/* Event details */}
|
<div className="flex flex-col sm:flex-row sm:items-center gap-2 sm:gap-4">
|
||||||
<div className="flex-1 text-sm">
|
<button
|
||||||
<div className="mb-1 font-medium text-xs" style={{ color: '#e64838' }}>
|
className="text-xs hover:underline text-left"
|
||||||
{event.date}
|
style={{ color: '#012068' }}
|
||||||
</div>
|
onClick={(e) => {
|
||||||
<div className="font-medium mb-2 text-lg md:text-xl" style={{ color: '#012068' }}>
|
e.stopPropagation();
|
||||||
{event.title}
|
navigateToDetail(item);
|
||||||
</div>
|
}}
|
||||||
<div className="text-xs leading-relaxed mb-1" style={{ color: '#333' }}>
|
>
|
||||||
{event.description}
|
View Details
|
||||||
</div>
|
</button>
|
||||||
<div className="text-xs leading-relaxed mb-4" style={{ color: '#333' }}>
|
<span className="text-gray-400 hidden sm:inline">|</span>
|
||||||
{event.detail}
|
<span className="text-xs font-medium" style={{ color: '#e64838' }}>
|
||||||
</div>
|
{formatPrice(item)}
|
||||||
<div className="flex flex-col sm:flex-row sm:items-center gap-2 sm:gap-4">
|
</span>
|
||||||
<button
|
</div>
|
||||||
className="text-xs hover:underline text-left"
|
|
||||||
style={{ color: '#012068' }}
|
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
navigateToEventDetail(event.id);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
View Details
|
|
||||||
</button>
|
|
||||||
<span className="text-gray-400 hidden sm:inline">|</span>
|
|
||||||
<span className="text-xs font-medium" style={{ color: '#e64838' }}>
|
|
||||||
{formatPrice(event)}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
);
|
||||||
))
|
})
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -374,35 +482,38 @@ const MedicalEventsComponent = () => {
|
|||||||
Recent Past Events
|
Recent Past Events
|
||||||
</h3>
|
</h3>
|
||||||
<div className="grid sm:grid-cols-2 lg:grid-cols-1 gap-6">
|
<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) => {
|
||||||
<div
|
const event = item as Event & { type: 'event'; itemDate: Date };
|
||||||
key={event.id}
|
return (
|
||||||
className="cursor-pointer hover:bg-gray-50 p-2 rounded-lg transition-colors"
|
<div
|
||||||
onClick={() => navigateToEventDetail(event.id)}
|
key={event.id}
|
||||||
>
|
className="cursor-pointer hover:bg-gray-50 p-2 rounded-lg transition-colors"
|
||||||
<div className="w-full h-32 lg:h-28 mb-3 overflow-hidden rounded-xs">
|
onClick={() => router.push(`/event-detail/${event.id}`)}
|
||||||
<img
|
>
|
||||||
src={getSafeImageUrl(event.mainImage, "https://images.unsplash.com/photo-1551601651-2a8555f1a136?w=300&h=200&fit=crop")}
|
<div className="w-full h-32 lg:h-28 mb-3 overflow-hidden rounded-xs">
|
||||||
alt={event.title}
|
<img
|
||||||
className="w-full h-full object-cover"
|
src={getSafeImageUrl(event.mainImage, "https://images.unsplash.com/photo-1551601651-2a8555f1a136?w=300&h=200&fit=crop")}
|
||||||
/>
|
alt={event.title}
|
||||||
</div>
|
className="w-full h-full object-cover"
|
||||||
<div className="text-sm">
|
/>
|
||||||
<div className="mb-1 font-medium text-xs" style={{ color: '#e64838' }}>
|
|
||||||
{event.date}
|
|
||||||
</div>
|
</div>
|
||||||
<div className="font-medium mb-1" style={{ color: '#012068' }}>
|
<div className="text-sm">
|
||||||
{event.title}
|
<div className="mb-1 font-medium text-xs" style={{ color: '#e64838' }}>
|
||||||
</div>
|
{event.date}
|
||||||
<div className="text-xs leading-relaxed mb-1" style={{ color: '#333' }}>
|
</div>
|
||||||
{event.description}
|
<div className="font-medium mb-1" style={{ color: '#012068' }}>
|
||||||
</div>
|
{event.title}
|
||||||
<div className="text-xs leading-relaxed" style={{ color: '#333' }}>
|
</div>
|
||||||
{event.detail}
|
<div className="text-xs leading-relaxed mb-1" style={{ color: '#333' }}>
|
||||||
|
{event.description}
|
||||||
|
</div>
|
||||||
|
<div className="text-xs leading-relaxed" style={{ color: '#333' }}>
|
||||||
|
{event.detail}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
);
|
||||||
))}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -249,7 +249,7 @@ const TeamListing: React.FC<TeamListingProps> = ({
|
|||||||
<div className="max-w-7xl mx-auto px-4">
|
<div className="max-w-7xl mx-auto px-4">
|
||||||
<div className="mb-8">
|
<div className="mb-8">
|
||||||
<h2 className="text-2xl font-bold mb-4" style={{ color: '#012068' }}>
|
<h2 className="text-2xl font-bold mb-4" style={{ color: '#012068' }}>
|
||||||
Retired Staff
|
FACULTY ALUMNI
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-sm mb-2" style={{ color: '#666' }}>
|
<p className="text-sm mb-2" style={{ color: '#666' }}>
|
||||||
Honoring our retired faculty and staff members for their dedicated service
|
Honoring our retired faculty and staff members for their dedicated service
|
||||||
|
|||||||
@ -2,60 +2,113 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { eventAPI, Event } from '../../lib/api';
|
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 EventsSection = () => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [upcomingEvents, setUpcomingEvents] = useState<Event[]>([]);
|
const [upcomingEvents, setUpcomingEvents] = useState<Event[]>([]);
|
||||||
const [pastEvents, setPastEvents] = useState<Event[]>([]);
|
const [pastEvents, setPastEvents] = useState<Event[]>([]);
|
||||||
|
const [courses, setCourses] = useState<Course[]>([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
|
const [combinedUpcoming, setCombinedUpcoming] = useState<EventOrCourse[]>([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadEvents();
|
loadData();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const loadEvents = async () => {
|
const loadData = async () => {
|
||||||
try {
|
try {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
const [upcoming, past] = await Promise.all([
|
const [upcoming, past, activeCourses] = await Promise.all([
|
||||||
eventAPI.getUpcomingEvents(),
|
eventAPI.getUpcomingEvents(),
|
||||||
eventAPI.getPastEvents()
|
eventAPI.getPastEvents(),
|
||||||
|
educationService.getActiveCourses()
|
||||||
]);
|
]);
|
||||||
|
|
||||||
setUpcomingEvents(upcoming);
|
setUpcomingEvents(upcoming);
|
||||||
setPastEvents(past);
|
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) {
|
} catch (error) {
|
||||||
console.error('Error loading events:', error);
|
console.error('Error loading data:', error);
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Navigation function for App Router
|
// Navigation functions
|
||||||
const navigateToEventDetail = (eventId: string | number) => {
|
const navigateToDetail = (item: EventOrCourse) => {
|
||||||
router.push(`/event-detail/${eventId}`);
|
if (item.type === 'event') {
|
||||||
|
router.push(`/event-detail/${item.id}`);
|
||||||
|
} else {
|
||||||
|
router.push(`/education-training/course-detail?id=${item.id}`);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const navigateToAllEvents = () => {
|
const navigateToAllEvents = () => {
|
||||||
router.push('/events');
|
router.push('/events');
|
||||||
};
|
};
|
||||||
|
|
||||||
// Format price display from fees
|
// Format price display
|
||||||
const formatPrice = (event: Event) => {
|
const formatPrice = (item: EventOrCourse) => {
|
||||||
if (event.fee && event.fee.length > 0) {
|
if (item.type === 'event') {
|
||||||
return `₹${event.fee[0].cost} per ticket`;
|
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';
|
||||||
}
|
}
|
||||||
return '₹1,800 per ticket';
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Top section events (first 4 events for the grid)
|
// Get title for the item
|
||||||
const topEvents = upcomingEvents.slice(0, 4);
|
const getTitle = (item: EventOrCourse) => {
|
||||||
|
return item.title;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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 event (first event)
|
// Featured item (first item)
|
||||||
const featuredEvent = upcomingEvents.length > 0 ? upcomingEvents[0] : null;
|
const featuredItem = combinedUpcoming.length > 0 ? combinedUpcoming[0] : null;
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
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="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>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -65,7 +118,7 @@ const EventsSection = () => {
|
|||||||
{/* Section Header */}
|
{/* Section Header */}
|
||||||
<div className="flex justify-between items-center mb-8">
|
<div className="flex justify-between items-center mb-8">
|
||||||
<h2 className="text-2xl md:text-3xl font-bold" style={{ color: '#012068' }}>
|
<h2 className="text-2xl md:text-3xl font-bold" style={{ color: '#012068' }}>
|
||||||
Our Events
|
Our Events & Courses
|
||||||
</h2>
|
</h2>
|
||||||
<button
|
<button
|
||||||
className="text-sm font-medium hover:opacity-70 transition-opacity duration-200"
|
className="text-sm font-medium hover:opacity-70 transition-opacity duration-200"
|
||||||
@ -76,39 +129,52 @@ const EventsSection = () => {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Events Grid - Top Section with Event Details */}
|
{/* Events/Courses Grid - Top Section with Details */}
|
||||||
{topEvents.length === 0 ? (
|
{topItems.length === 0 ? (
|
||||||
<div className="text-center py-8 mb-12">
|
<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>
|
||||||
) : (
|
) : (
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-12">
|
<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
|
<div
|
||||||
key={event.id}
|
key={`${item.type}-${item.id}`}
|
||||||
className={`relative rounded-lg h-48 overflow-hidden cursor-pointer hover:shadow-lg transition-shadow ${
|
className={`relative rounded-lg h-48 overflow-hidden cursor-pointer hover:shadow-lg transition-shadow ${
|
||||||
index === 2 ? 'lg:hidden xl:block' : ''
|
index === 2 ? 'lg:hidden xl:block' : ''
|
||||||
} ${index === 3 ? 'md:col-span-2 lg:col-span-1' : ''}`}
|
} ${index === 3 ? 'md:col-span-2 lg:col-span-1' : ''}`}
|
||||||
onClick={() => navigateToEventDetail(event.id)}
|
onClick={() => navigateToDetail(item)}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src={event.mainImage}
|
src={item.mainImage}
|
||||||
alt={event.title}
|
alt={getTitle(item)}
|
||||||
className="w-full h-full object-cover"
|
className="w-full h-full object-cover"
|
||||||
/>
|
/>
|
||||||
{/* Gradient overlay for better text readability */}
|
{/* Gradient overlay for better text readability */}
|
||||||
<div className="absolute inset-0 bg-gradient-to-t from-black/70 via-black/20 to-transparent"></div>
|
<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="absolute bottom-0 left-0 right-0 p-4 text-white">
|
||||||
<div className="text-xs font-medium mb-1" style={{ color: '#e64838' }}>
|
<div className="text-xs font-medium mb-1" style={{ color: '#e64838' }}>
|
||||||
{event.date}
|
{item.date}
|
||||||
</div>
|
</div>
|
||||||
<h4 className="font-medium mb-2 text-sm leading-tight">
|
<h4 className="font-medium mb-2 text-sm leading-tight line-clamp-2">
|
||||||
{event.title}
|
{getTitle(item)}
|
||||||
</h4>
|
</h4>
|
||||||
<div className="text-xs font-medium">
|
<div className="text-xs font-medium">
|
||||||
{formatPrice(event)}
|
{formatPrice(item)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -116,26 +182,40 @@ const EventsSection = () => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Featured Events Section */}
|
{/* Featured Section */}
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
<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' }}>
|
<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>
|
<h3 className="text-xl font-semibold mb-4" style={{ color: '#012068' }}>
|
||||||
{!featuredEvent ? (
|
Featured {featuredItem?.type === 'course' ? 'Course' : 'Event'}
|
||||||
|
</h3>
|
||||||
|
{!featuredItem ? (
|
||||||
<div className="text-center py-8">
|
<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>
|
||||||
) : (
|
) : (
|
||||||
<div
|
<div
|
||||||
className="bg-white border border-gray-100 rounded-lg overflow-hidden cursor-pointer hover:shadow-lg transition-shadow"
|
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">
|
<div className="relative h-48 md:h-64">
|
||||||
<img
|
<img
|
||||||
src={featuredEvent.mainImage}
|
src={featuredItem.mainImage}
|
||||||
alt={featuredEvent.title}
|
alt={getTitle(featuredItem)}
|
||||||
className="w-full h-full object-cover"
|
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 */}
|
{/* Pagination dots */}
|
||||||
<div className="absolute bottom-4 left-1/2 transform -translate-x-1/2 flex space-x-2">
|
<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-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 flex-col lg:flex-row lg:justify-between gap-4">
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<div className="text-xs font-medium mb-3" style={{ color: '#e64838' }}>
|
<div className="text-xs font-medium mb-3" style={{ color: '#e64838' }}>
|
||||||
{featuredEvent.date}
|
{featuredItem.date}
|
||||||
</div>
|
</div>
|
||||||
<h4 className="text-lg md:text-xl font-medium mb-2" style={{ color: '#012068' }}>
|
<h4 className="text-lg md:text-xl font-medium mb-2" style={{ color: '#012068' }}>
|
||||||
{featuredEvent.title}
|
{getTitle(featuredItem)}
|
||||||
</h4>
|
</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' }}>
|
<div className="text-xs leading-relaxed mb-4" style={{ color: '#333' }}>
|
||||||
{featuredEvent.detail}
|
{getDescription(featuredItem)}
|
||||||
</div>
|
</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="flex flex-col sm:flex-row sm:items-center gap-2 sm:gap-4">
|
||||||
<div
|
<div
|
||||||
className="text-xs cursor-pointer hover:underline"
|
className="text-xs cursor-pointer hover:underline"
|
||||||
style={{ color: '#012068' }}
|
style={{ color: '#012068' }}
|
||||||
>
|
>
|
||||||
Share Event
|
Share {featuredItem.type === 'course' ? 'Course' : 'Event'}
|
||||||
</div>
|
|
||||||
<div className="text-xs" style={{ color: '#012068' }}>
|
|
||||||
📍 {featuredEvent.venue?.[0]?.address || 'Convention Center, Medical District'}
|
|
||||||
</div>
|
</div>
|
||||||
|
{featuredItem.type === 'event' && (
|
||||||
|
<div className="text-xs" style={{ color: '#012068' }}>
|
||||||
|
📍 {featuredItem.venue?.[0]?.address || 'Convention Center, Medical District'}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-left lg:text-right">
|
<div className="text-left lg:text-right">
|
||||||
@ -179,17 +279,19 @@ const EventsSection = () => {
|
|||||||
}}
|
}}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
console.log('Book seat clicked');
|
navigateToDetail(featuredItem);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Book Your Seat
|
{featuredItem.type === 'course' ? 'Apply Now' : 'Book Your Seat'}
|
||||||
</button>
|
</button>
|
||||||
<div className="text-sm font-medium" style={{ color: '#e64838' }}>
|
<div className="text-sm font-medium" style={{ color: '#e64838' }}>
|
||||||
{formatPrice(featuredEvent)}
|
{formatPrice(featuredItem)}
|
||||||
</div>
|
|
||||||
<div className="text-xs mt-1" style={{ color: '#012068', opacity: 0.8 }}>
|
|
||||||
Early bird discount available
|
|
||||||
</div>
|
</div>
|
||||||
|
{featuredItem.type === 'event' && (
|
||||||
|
<div className="text-xs mt-1" style={{ color: '#012068', opacity: 0.8 }}>
|
||||||
|
Early bird discount available
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -197,7 +299,7 @@ const EventsSection = () => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Right - Event List with Scroller */}
|
{/* Right - Past Events List with Scroller */}
|
||||||
<div className="lg:col-span-1">
|
<div className="lg:col-span-1">
|
||||||
<h3 className="text-xl font-semibold mb-4" style={{ color: '#012068' }}>Past Events</h3>
|
<h3 className="text-xl font-semibold mb-4" style={{ color: '#012068' }}>Past Events</h3>
|
||||||
{pastEvents.length === 0 ? (
|
{pastEvents.length === 0 ? (
|
||||||
@ -210,7 +312,7 @@ const EventsSection = () => {
|
|||||||
<div
|
<div
|
||||||
key={event.id}
|
key={event.id}
|
||||||
className="bg-white border border-gray-100 rounded-lg overflow-hidden cursor-pointer hover:shadow-lg transition-shadow flex-shrink-0"
|
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">
|
<div className="h-24">
|
||||||
<img
|
<img
|
||||||
@ -226,7 +328,7 @@ const EventsSection = () => {
|
|||||||
<h4 className="font-medium mb-1 text-sm" style={{ color: '#012068' }}>
|
<h4 className="font-medium mb-1 text-sm" style={{ color: '#012068' }}>
|
||||||
{event.title}
|
{event.title}
|
||||||
</h4>
|
</h4>
|
||||||
<p className="text-xs leading-relaxed" style={{ color: '#333' }}>
|
<p className="text-xs leading-relaxed line-clamp-2" style={{ color: '#333' }}>
|
||||||
{event.description}
|
{event.description}
|
||||||
</p>
|
</p>
|
||||||
<div className="mt-2 flex justify-between items-center">
|
<div className="mt-2 flex justify-between items-center">
|
||||||
@ -235,14 +337,11 @@ const EventsSection = () => {
|
|||||||
style={{ color: '#012068' }}
|
style={{ color: '#012068' }}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
navigateToEventDetail(event.id);
|
router.push(`/event-detail/${event.id}`);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
View Details
|
View Details
|
||||||
</button>
|
</button>
|
||||||
{/* <span className="text-xs font-medium" style={{ color: '#e64838' }}>
|
|
||||||
{formatPrice(event)}
|
|
||||||
</span> */}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -18,7 +18,8 @@ export interface Event {
|
|||||||
phone: string;
|
phone: string;
|
||||||
email: string;
|
email: string;
|
||||||
isActive: boolean;
|
isActive: boolean;
|
||||||
bookSeatLink?: string; // NEW FIELD
|
bookSeatLink?: string; // Registration/booking link
|
||||||
|
learnMoreLink?: string; // Additional information link
|
||||||
professors?: Professor[];
|
professors?: Professor[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user