Team and service update
This commit is contained in:
@ -3,9 +3,11 @@
|
||||
import React, { useState } from "react";
|
||||
import Link from "next/link";
|
||||
import Image from "next/image";
|
||||
import { usePathname } from "next/navigation";
|
||||
|
||||
const Header = () => {
|
||||
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
||||
const pathname = usePathname();
|
||||
|
||||
const closeAllMenus = () => {
|
||||
setIsMenuOpen(false);
|
||||
@ -25,78 +27,98 @@ const Header = () => {
|
||||
|
||||
return (
|
||||
<header className="sticky bg-white top-0 z-50 shadow-sm">
|
||||
<div className="container max-w-none mx-auto px-4 sm:px-6">
|
||||
<div className="flex justify-between items-center py-3 sm:py-4">
|
||||
{/* Left Logo */}
|
||||
<div className="flex-shrink-0">
|
||||
<Link href="/" onClick={closeAllMenus} className="flex items-center">
|
||||
<div className="relative w-60 sm:w-80 h-14 sm:h-18 mr-3 rounded overflow-hidden">
|
||||
<Image
|
||||
src="/images/finalcmclogo.svg"
|
||||
alt="CMC Logo"
|
||||
fill
|
||||
className="object-fill"
|
||||
priority
|
||||
/>
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* Desktop Navigation */}
|
||||
<nav className="hidden lg:flex items-center space-x-3 xl:space-x-5">
|
||||
{menuItems.map((item, idx) => (
|
||||
<Link
|
||||
key={idx}
|
||||
href={item.href}
|
||||
className="text-blue-900 hover:text-red-600 transition-colors font-medium text-sm xl:text-base whitespace-nowrap"
|
||||
onClick={closeAllMenus}
|
||||
>
|
||||
{item.label}
|
||||
{/* Top section with logos */}
|
||||
<div className="border-b border-gray-200">
|
||||
<div className="container max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="flex justify-between items-center py-1.5 sm:py-2">
|
||||
{/* Left Logo */}
|
||||
<div className="flex-1 flex justify-start">
|
||||
<Link href="/" onClick={closeAllMenus} className="flex items-center">
|
||||
<div className="relative w-64 sm:w-80 md:w-96 lg:w-[420px] h-16 sm:h-18 md:h-20 lg:h-22">
|
||||
<Image
|
||||
src="/images/finalcmclogo.svg"
|
||||
alt="CMC Logo"
|
||||
fill
|
||||
className="object-contain object-left"
|
||||
priority
|
||||
/>
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</nav>
|
||||
|
||||
{/* Right side - Logo with Batch ID and Mobile Menu Button */}
|
||||
<div className="flex items-center gap-4">
|
||||
{/* Right Logo with Batch ID */}
|
||||
<div className="flex flex-col items-center">
|
||||
<div className="relative w-16 h-16 sm:w-20 sm:h-20 rounded overflow-hidden">
|
||||
<Image
|
||||
src="/images/rightlogo.png"
|
||||
alt="Batch Logo"
|
||||
fill
|
||||
className="object-contain"
|
||||
priority
|
||||
/>
|
||||
</div>
|
||||
<span className="text-xs sm:text-sm font-semibold text-blue-900 mt-1 whitespace-nowrap">
|
||||
H-2024-1431
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Mobile menu button */}
|
||||
{/* Right Logo with Batch ID */}
|
||||
<div className="flex-1 flex justify-end">
|
||||
<div className="flex flex-col items-center justify-center">
|
||||
<div className="relative w-16 h-16 sm:w-18 sm:h-18 md:w-20 md:h-20 lg:w-[88px] lg:h-[88px]">
|
||||
<Image
|
||||
src="/images/rightlogo.png"
|
||||
alt="Batch Logo"
|
||||
fill
|
||||
className="object-contain"
|
||||
priority
|
||||
/>
|
||||
</div>
|
||||
<span className="text-[10px] sm:text-xs lg:text-sm font-bold text-blue-900 -mt-1 whitespace-nowrap">
|
||||
H-2024-1431
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Bottom section with menu */}
|
||||
<div className="bg-blue-900">
|
||||
<div className="container max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="flex justify-between items-center">
|
||||
{/* Desktop Navigation */}
|
||||
<nav className="hidden lg:flex items-center w-full">
|
||||
<div className="flex items-center w-full">
|
||||
{menuItems.map((item, idx) => {
|
||||
const isActive = pathname === item.href ||
|
||||
(item.href !== "/" && pathname.startsWith(item.href));
|
||||
return (
|
||||
<Link
|
||||
key={idx}
|
||||
href={item.href}
|
||||
className={`font-medium text-sm xl:text-[15px] whitespace-nowrap px-4 xl:px-5 py-3.5 transition-colors flex-1 text-center ${
|
||||
isActive
|
||||
? "text-white bg-red-600"
|
||||
: "text-white hover:bg-red-600"
|
||||
}`}
|
||||
onClick={closeAllMenus}
|
||||
>
|
||||
{item.label}
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
{/* Mobile menu button - More visible */}
|
||||
<button
|
||||
className="lg:hidden p-2 text-blue-900 hover:text-red-600 transition-colors"
|
||||
className="lg:hidden p-3 text-white hover:bg-red-600 transition-colors w-full flex items-center justify-between"
|
||||
onClick={() => setIsMenuOpen(!isMenuOpen)}
|
||||
aria-label="Toggle menu"
|
||||
>
|
||||
<span className="font-medium text-sm sm:text-base">Menu</span>
|
||||
<svg
|
||||
className="w-6 h-6"
|
||||
className="w-6 h-6 sm:w-7 sm:h-7"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth={2.5}
|
||||
>
|
||||
{isMenuOpen ? (
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M6 18L18 6M6 6l12 12"
|
||||
/>
|
||||
) : (
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M4 6h16M4 12h16M4 18h16"
|
||||
/>
|
||||
)}
|
||||
@ -106,22 +128,30 @@ const Header = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Mobile Menu */}
|
||||
{/* Mobile Menu Dropdown */}
|
||||
{isMenuOpen && (
|
||||
<div className="lg:hidden bg-gray-100 border-t border-blue-900">
|
||||
<nav className="px-4 py-4">
|
||||
<ul className="space-y-4">
|
||||
{menuItems.map((item, idx) => (
|
||||
<li key={idx}>
|
||||
<Link
|
||||
href={item.href}
|
||||
className="block font-medium py-2 text-blue-900 hover:text-red-600 transition-colors"
|
||||
onClick={closeAllMenus}
|
||||
>
|
||||
{item.label}
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
<div className="lg:hidden bg-white border-t border-gray-200 shadow-lg">
|
||||
<nav className="container max-w-7xl mx-auto px-4 sm:px-6 py-2">
|
||||
<ul className="space-y-1">
|
||||
{menuItems.map((item, idx) => {
|
||||
const isActive = pathname === item.href ||
|
||||
(item.href !== "/" && pathname.startsWith(item.href));
|
||||
return (
|
||||
<li key={idx}>
|
||||
<Link
|
||||
href={item.href}
|
||||
className={`block font-medium py-3 px-4 transition-colors rounded text-sm sm:text-base ${
|
||||
isActive
|
||||
? "bg-red-600 text-white"
|
||||
: "text-blue-900 hover:bg-gray-100 hover:text-red-600"
|
||||
}`}
|
||||
onClick={closeAllMenus}
|
||||
>
|
||||
{item.label}
|
||||
</Link>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
@ -17,26 +17,6 @@ const StatisticsTiles = () => {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState(false);
|
||||
|
||||
// Default fallback data
|
||||
const defaultTiles = [
|
||||
{
|
||||
title: "Primary Trauma Care",
|
||||
description: "Specialized care for Priority One trauma patients in close coordination with the Emergency Department team."
|
||||
},
|
||||
{
|
||||
title: "24x7 Trauma Surgeon",
|
||||
description: "Round-the-clock availability of trauma surgeons ensures immediate surgical intervention when needed."
|
||||
},
|
||||
{
|
||||
title: "Trauma Intensive & Ward Care",
|
||||
description: "Comprehensive trauma intensive care and dedicated trauma ward services for critical and recovering patients."
|
||||
},
|
||||
{
|
||||
title: "Trauma Education",
|
||||
description: "Focused education and counseling for patients and families to enhance recovery and awareness."
|
||||
},
|
||||
];
|
||||
|
||||
useEffect(() => {
|
||||
const fetchServiceTiles = async () => {
|
||||
try {
|
||||
@ -54,11 +34,11 @@ const StatisticsTiles = () => {
|
||||
setTiles(activeTiles);
|
||||
setError(false);
|
||||
} else {
|
||||
// No active tiles, use defaults
|
||||
// No active tiles
|
||||
setError(true);
|
||||
}
|
||||
} else {
|
||||
// API error, use defaults
|
||||
// API error
|
||||
setError(true);
|
||||
}
|
||||
} catch (err) {
|
||||
@ -72,8 +52,16 @@ const StatisticsTiles = () => {
|
||||
fetchServiceTiles();
|
||||
}, []);
|
||||
|
||||
// Use fetched tiles or fall back to default
|
||||
const displayTiles = error || tiles.length === 0 ? defaultTiles : tiles;
|
||||
// Function to parse description and convert to bullet points (comma-separated)
|
||||
const parseDescription = (description: string) => {
|
||||
// Split by commas and filter out empty strings
|
||||
const points = description
|
||||
.split(',')
|
||||
.map(point => point.trim())
|
||||
.filter(point => point.length > 0);
|
||||
|
||||
return points;
|
||||
};
|
||||
|
||||
return (
|
||||
<section className="py-8 sm:py-12" style={{ backgroundColor: '#f4f4f4' }}>
|
||||
@ -90,30 +78,40 @@ const StatisticsTiles = () => {
|
||||
<div className="flex justify-center items-center py-12">
|
||||
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600"></div>
|
||||
</div>
|
||||
) : error || tiles.length === 0 ? (
|
||||
// No services available
|
||||
<div className="text-center py-8">
|
||||
<p className="text-gray-600 text-base sm:text-lg">
|
||||
No services are currently available. Please check back later.
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
// Display tiles
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 lg:gap-6">
|
||||
{displayTiles.map((tile, index) => (
|
||||
{tiles.map((tile, index) => (
|
||||
<div
|
||||
key={index}
|
||||
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 className="flex items-center mb-3">
|
||||
<h3
|
||||
className="text-base sm:text-lg font-medium"
|
||||
style={{ color: '#012068' }}
|
||||
>
|
||||
{tile.title}
|
||||
</h3>
|
||||
</div>
|
||||
<p
|
||||
className="text-sm sm:text-base leading-relaxed flex-grow"
|
||||
style={{ color: '#333' }}
|
||||
<h3
|
||||
className="text-base sm:text-lg font-semibold mb-3 text-left"
|
||||
style={{ color: '#012068' }}
|
||||
>
|
||||
{tile.description}
|
||||
</p>
|
||||
{tile.title}
|
||||
</h3>
|
||||
<ul className="list-disc pl-5 space-y-2 flex-grow text-left">
|
||||
{parseDescription(tile.description).map((point, idx) => (
|
||||
<li
|
||||
key={idx}
|
||||
className="text-sm sm:text-base leading-relaxed text-left"
|
||||
style={{ color: '#333' }}
|
||||
>
|
||||
{point}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
@ -35,10 +35,19 @@ const TeamListing: React.FC<TeamListingProps> = ({
|
||||
loadFacultyData();
|
||||
}, []);
|
||||
|
||||
// Filter members by category
|
||||
const facultyMembers = teamMembers.filter(member => member.category === 'FACULTY');
|
||||
const supportTeam = teamMembers.filter(member => member.category === 'SUPPORT_TEAM');
|
||||
const traineesAndFellows = teamMembers.filter(member => member.category === 'TRAINEE_FELLOW');
|
||||
// Filter members by category and status
|
||||
const facultyMembers = teamMembers.filter(member =>
|
||||
member.category === 'FACULTY' && member.status !== 'RETIRED'
|
||||
);
|
||||
const supportTeam = teamMembers.filter(member =>
|
||||
member.category === 'SUPPORT_TEAM' && member.status !== 'RETIRED'
|
||||
);
|
||||
const traineesAndFellows = teamMembers.filter(member =>
|
||||
member.category === 'TRAINEE_FELLOW' && member.status !== 'RETIRED'
|
||||
);
|
||||
const retiredStaff = teamMembers.filter(member =>
|
||||
member.status === 'RETIRED'
|
||||
);
|
||||
|
||||
const handleMemberClick = (member: TeamMember) => {
|
||||
if (onMemberClick) {
|
||||
@ -79,6 +88,9 @@ const TeamListing: React.FC<TeamListingProps> = ({
|
||||
const [imageError, setImageError] = useState(false);
|
||||
const [imageLoading, setImageLoading] = useState(true);
|
||||
const isTrainee = member.category === 'TRAINEE_FELLOW';
|
||||
const isSupportTeam = member.category === 'SUPPORT_TEAM';
|
||||
const isRetired = member.status === 'RETIRED';
|
||||
const isNonClickable = isTrainee || isSupportTeam;
|
||||
|
||||
// Check if image URL is valid and not a default placeholder
|
||||
const hasValidImage = member.image &&
|
||||
@ -91,7 +103,8 @@ const TeamListing: React.FC<TeamListingProps> = ({
|
||||
member.image.includes('/profile-image?') ||
|
||||
member.image === 'https://via.placeholder.com/400');
|
||||
|
||||
const shouldShowImage = !isTrainee && hasValidImage;
|
||||
// Don't show images for trainees or support team
|
||||
const shouldShowImage = !isNonClickable && hasValidImage;
|
||||
|
||||
const handleImageError = (e: React.SyntheticEvent<HTMLImageElement>) => {
|
||||
console.error('Image failed to load:', member.image);
|
||||
@ -108,12 +121,12 @@ const TeamListing: React.FC<TeamListingProps> = ({
|
||||
return (
|
||||
<div
|
||||
className={`bg-white rounded-lg border border-gray-300 overflow-hidden transition-all duration-300 ${
|
||||
!isTrainee ? 'group cursor-pointer hover:shadow-lg' : 'cursor-default'
|
||||
!isNonClickable ? 'group cursor-pointer hover:shadow-lg' : 'cursor-default'
|
||||
}`}
|
||||
onClick={() => !isTrainee && handleMemberClick(member)}
|
||||
onClick={() => !isNonClickable && handleMemberClick(member)}
|
||||
>
|
||||
{/* Only show image section for non-trainees */}
|
||||
{!isTrainee && (
|
||||
{/* Only show image section for faculty */}
|
||||
{!isNonClickable && (
|
||||
<div className="relative aspect-square overflow-hidden bg-gray-50">
|
||||
{shouldShowImage && !imageError ? (
|
||||
<>
|
||||
@ -158,7 +171,7 @@ const TeamListing: React.FC<TeamListingProps> = ({
|
||||
<p className="text-sm leading-relaxed" style={{ color: '#e64838' }}>
|
||||
{member.position}
|
||||
</p>
|
||||
{!isTrainee && (
|
||||
{!isNonClickable && (
|
||||
<>
|
||||
{member.department && (
|
||||
<p className="text-xs mt-1" style={{ color: '#666' }}>
|
||||
@ -230,9 +243,30 @@ const TeamListing: React.FC<TeamListingProps> = ({
|
||||
</section>
|
||||
)}
|
||||
|
||||
{/* Retired Staff Section */}
|
||||
{retiredStaff.length > 0 && (
|
||||
<section className="py-8" style={{ backgroundColor: '#f9f9f9' }}>
|
||||
<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
|
||||
</h2>
|
||||
<p className="text-sm mb-2" style={{ color: '#666' }}>
|
||||
Honoring our retired faculty and staff members for their dedicated service
|
||||
</p>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
|
||||
{retiredStaff.map((member) => (
|
||||
<TeamMemberCard key={member.id} member={member} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{/* Support Team Section */}
|
||||
{supportTeam.length > 0 && (
|
||||
<section className="py-8" style={{ backgroundColor: '#f9f9f9' }}>
|
||||
<section className="py-8" style={{ backgroundColor: '#fff' }}>
|
||||
<div className="max-w-7xl mx-auto px-4">
|
||||
<div className="mb-8">
|
||||
<h2 className="text-2xl font-bold mb-4" style={{ color: '#012068' }}>
|
||||
@ -241,7 +275,7 @@ const TeamListing: React.FC<TeamListingProps> = ({
|
||||
<p className="text-base leading-relaxed mb-2" style={{ color: '#012068' }}>
|
||||
<strong>Clinical Support Staff</strong> - Essential team members providing specialized support services
|
||||
</p>
|
||||
<p className="text-sm" style={{ color: '#012068' }}>
|
||||
<p className="text-sm mb-1" style={{ color: '#012068' }}>
|
||||
<strong>Administrative & Technical Support</strong> - Dedicated professionals ensuring smooth operations
|
||||
</p>
|
||||
</div>
|
||||
@ -256,7 +290,7 @@ const TeamListing: React.FC<TeamListingProps> = ({
|
||||
|
||||
{/* Trainees & Fellows Section */}
|
||||
{traineesAndFellows.length > 0 && (
|
||||
<section className="py-8" style={{ backgroundColor: '#fff' }}>
|
||||
<section className="py-8" style={{ backgroundColor: '#f9f9f9' }}>
|
||||
<div className="max-w-7xl mx-auto px-4">
|
||||
<div className="mb-8">
|
||||
<h2 className="text-2xl font-bold mb-4" style={{ color: '#012068' }}>
|
||||
@ -296,7 +330,7 @@ const TeamListing: React.FC<TeamListingProps> = ({
|
||||
)}
|
||||
|
||||
{/* Show message if all categories are empty but we have data */}
|
||||
{teamMembers.length > 0 && facultyMembers.length === 0 && supportTeam.length === 0 && traineesAndFellows.length === 0 && (
|
||||
{teamMembers.length > 0 && facultyMembers.length === 0 && supportTeam.length === 0 && traineesAndFellows.length === 0 && retiredStaff.length === 0 && (
|
||||
<section className="py-16 text-center">
|
||||
<div className="max-w-2xl mx-auto px-4">
|
||||
<h2 className="text-2xl font-bold mb-4" style={{ color: '#012068' }}>
|
||||
|
||||
@ -74,6 +74,7 @@ const TeamMemberDetail: React.FC<TeamMemberDetailProps> = ({ memberId, memberDat
|
||||
}
|
||||
|
||||
const isTrainee = member.category === 'TRAINEE_FELLOW';
|
||||
const isRetired = member.status === 'RETIRED';
|
||||
|
||||
// Check if image URL is valid and not a default placeholder
|
||||
const hasValidImage = member.image &&
|
||||
@ -146,6 +147,11 @@ const TeamMemberDetail: React.FC<TeamMemberDetailProps> = ({ memberId, memberDat
|
||||
Trainee/Fellow
|
||||
</span>
|
||||
)}
|
||||
{isRetired && (
|
||||
<span className="ml-4 px-3 py-1 text-xs font-medium rounded-full bg-red-600 text-white">
|
||||
RETIRED
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<p className="text-base max-w-2xl leading-relaxed" style={{ color: '#333' }}>
|
||||
{member.designation || member.position} {member.experience && `with ${member.experience} of experience`} at CMC Vellore
|
||||
@ -292,7 +298,7 @@ const TeamMemberDetail: React.FC<TeamMemberDetailProps> = ({ memberId, memberDat
|
||||
</div>
|
||||
|
||||
<div className="prose prose-lg max-w-none mb-8 leading-relaxed">
|
||||
<p className="first-letter:text-4xl first-letter:font-semibold first-letter:float-left first-letter:mr-2 first-letter:leading-none first-letter:mt-1 text-base first-letter:text-blue-900 text-gray-700">
|
||||
<p className="text-base text-gray-700">
|
||||
{getFullDescription()}
|
||||
</p>
|
||||
|
||||
@ -314,10 +320,13 @@ const TeamMemberDetail: React.FC<TeamMemberDetailProps> = ({ memberId, memberDat
|
||||
</div>
|
||||
|
||||
<div className="space-y-6">
|
||||
<div className="rounded-lg p-4 border border-gray-300" style={{ backgroundColor: '#f4f4f4' }}>
|
||||
<div className="font-medium mb-2 text-sm" style={{ color: '#012068' }}>Training & Professional Development</div>
|
||||
<div className="text-sm leading-relaxed" style={{ color: '#333' }}>{member.training || 'Professional training details available upon request'}</div>
|
||||
</div>
|
||||
{/* Only show Training section if NOT retired */}
|
||||
{!isRetired && (
|
||||
<div className="rounded-lg p-4 border border-gray-300" style={{ backgroundColor: '#f4f4f4' }}>
|
||||
<div className="font-medium mb-2 text-sm" style={{ color: '#012068' }}>Training & Professional Development</div>
|
||||
<div className="text-sm leading-relaxed" style={{ color: '#333' }}>{member.training || 'Professional training details available upon request'}</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{member.workDays && member.workDays.length > 0 && (
|
||||
<div className="rounded-lg p-4 border border-gray-300" style={{ backgroundColor: '#f4f4f4' }}>
|
||||
|
||||
Reference in New Issue
Block a user