Team and service update

This commit is contained in:
2025-11-12 09:20:24 +05:30
parent d38ee9ea42
commit f55a506ea5
4 changed files with 195 additions and 124 deletions

View File

@ -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>

View File

@ -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>
))}

View File

@ -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' }}>

View File

@ -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' }}>