Professor category

This commit is contained in:
2026-05-05 11:55:20 +05:30
parent 7b88426f96
commit 4fc0be9071
2 changed files with 346 additions and 188 deletions
+118 -76
View File
@@ -9,7 +9,10 @@ interface TeamListingProps {
onMemberClick?: (member: TeamMember) => void;
}
const TeamListing: React.FC<TeamListingProps> = ({
const GUIDES_FRIENDS_PATRONS_CATEGORIES = ['GUIDES', 'FRIENDS', 'PATRONS'];
const NON_CLICKABLE_CATEGORIES = ['TRAINEE_FELLOW', 'SUPPORT_TEAM', 'GUIDES', 'FRIENDS', 'PATRONS'];
const TeamListing: React.FC<TeamListingProps> = ({
title = "Our Team",
onMemberClick
}) => {
@@ -35,25 +38,36 @@ const TeamListing: React.FC<TeamListingProps> = ({
loadFacultyData();
}, []);
// Filter members by category and status
const facultyMembers = teamMembers.filter(member =>
// Active faculty members only
const facultyMembers = teamMembers.filter(member =>
member.category === 'FACULTY' && member.status !== 'RETIRED'
);
const supportTeam = teamMembers.filter(member =>
// Active support team only
const supportTeam = teamMembers.filter(member =>
member.category === 'SUPPORT_TEAM' && member.status !== 'RETIRED'
);
const traineesAndFellows = teamMembers.filter(member =>
// Active trainees and fellows only
const traineesAndFellows = teamMembers.filter(member =>
member.category === 'TRAINEE_FELLOW' && member.status !== 'RETIRED'
);
const retiredStaff = teamMembers.filter(member =>
member.status === 'RETIRED'
// Guides, Friends, Patrons — all three categories in one section
const guidesFriendsPatrons = teamMembers.filter(member =>
GUIDES_FRIENDS_PATRONS_CATEGORIES.includes(member.category) && member.status !== 'RETIRED'
);
// Alumni = RETIRED (any category) + RESIGNED (any status)
const alumniStaff = teamMembers.filter(member =>
member.status === 'RETIRED' || member.category === 'RESIGNED'
);
const handleMemberClick = (member: TeamMember) => {
if (onMemberClick) {
onMemberClick(member);
} else {
window.location.href = `/faculty/${member.id}`;
window.location.href = `/faculty/${member.professorId}`;
}
};
@@ -73,8 +87,8 @@ const TeamListing: React.FC<TeamListingProps> = ({
<div className="min-h-screen flex items-center justify-center">
<div className="text-center">
<p className="text-red-600 mb-4">{error}</p>
<button
onClick={() => window.location.reload()}
<button
onClick={() => window.location.reload()}
className="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700"
>
Retry
@@ -87,45 +101,44 @@ const TeamListing: React.FC<TeamListingProps> = ({
const TeamMemberCard: React.FC<{ member: TeamMember }> = ({ member }) => {
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 &&
member.image.trim() !== '' &&
!member.image.includes('default-avatar') &&
!member.image.includes('placeholder.') &&
!member.image.includes('robot') &&
// Only filter if URL ENDS with /profile-image (backend default endpoint)
!(member.image.endsWith('/profile-image') ||
member.image.includes('/profile-image?') ||
member.image === 'https://via.placeholder.com/400');
// Don't show images for trainees or support team
const isNonClickable = NON_CLICKABLE_CATEGORIES.includes(member.category);
const isRetired = member.status === 'RETIRED';
const isResigned = member.category === 'RESIGNED';
// Check if image URL is valid and not a default placeholder
const hasValidImage =
member.image &&
member.image.trim() !== '' &&
!member.image.includes('default-avatar') &&
!member.image.includes('placeholder.') &&
!member.image.includes('robot') &&
!(
member.image.endsWith('/profile-image') ||
member.image.includes('/profile-image?') ||
member.image === 'https://via.placeholder.com/400'
);
// Don't show images for non-clickable categories
const shouldShowImage = !isNonClickable && hasValidImage;
const handleImageError = (e: React.SyntheticEvent<HTMLImageElement>) => {
console.error('Image failed to load:', member.image);
console.error('Member:', member.name, 'Professor ID:', member.professorId);
const handleImageError = () => {
setImageError(true);
setImageLoading(false);
};
const handleImageLoad = () => {
console.log('Image loaded successfully:', member.image);
setImageLoading(false);
};
return (
<div
<div
className={`bg-white rounded-lg border border-gray-300 overflow-hidden transition-all duration-300 ${
!isNonClickable ? 'group cursor-pointer hover:shadow-lg' : 'cursor-default'
}`}
onClick={() => !isNonClickable && handleMemberClick(member)}
>
{/* Only show image section for faculty */}
{/* Only show image section for clickable categories (Faculty + Alumni) */}
{!isNonClickable && (
<div className="relative aspect-square overflow-hidden bg-gray-50">
{shouldShowImage && !imageError ? (
@@ -135,9 +148,8 @@ const TeamListing: React.FC<TeamListingProps> = ({
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
</div>
)}
<img
src={member.image}
<img
src={member.image}
alt={member.name}
className={`w-full h-full object-cover transition-transform duration-300 group-hover:scale-105 ${
imageLoading ? 'opacity-0' : 'opacity-100'
@@ -148,29 +160,41 @@ const TeamListing: React.FC<TeamListingProps> = ({
/>
</>
) : (
<div
<div
className="w-full h-full flex items-center justify-center"
style={{ backgroundColor: '#e0e5eb' }}
>
<svg
className="w-32 h-32 text-gray-400"
fill="currentColor"
viewBox="0 0 24 24"
>
<path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/>
<svg className="w-32 h-32 text-gray-400" fill="currentColor" viewBox="0 0 24 24">
<path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z" />
</svg>
</div>
)}
</div>
)}
<div className="p-4 sm:p-6">
<h3 className="text-lg font-medium mb-2 group-hover:opacity-70 transition-opacity" style={{ color: '#012068' }}>
{/* Status badge for alumni */}
{(isRetired || isResigned) && (
<div className="mb-2">
<span
className="inline-block px-2 py-0.5 text-xs font-medium rounded-full text-white"
style={{ backgroundColor: isResigned ? '#6b7280' : '#dc2626' }}
>
{isResigned ? 'Resigned' : 'Retired'}
</span>
</div>
)}
<h3
className="text-lg font-medium mb-2 group-hover:opacity-70 transition-opacity"
style={{ color: '#012068' }}
>
{member.name}
</h3>
<p className="text-sm leading-relaxed" style={{ color: '#e64838' }}>
{member.position}
</p>
{!isNonClickable && (
<>
{member.department && (
@@ -196,8 +220,8 @@ const TeamListing: React.FC<TeamListingProps> = ({
<section className="py-4" style={{ backgroundColor: '#f4f4f4' }}>
<div className="max-w-7xl mx-auto px-4">
<nav className="flex items-center space-x-2 text-sm">
<Link
href="/"
<Link
href="/"
className="hover:opacity-70 transition-opacity duration-200"
style={{ color: '#012068' }}
>
@@ -208,7 +232,7 @@ const TeamListing: React.FC<TeamListingProps> = ({
Faculty
</span>
</nav>
<div className="mt-6">
<div className="flex items-center mb-4">
<h1 className="text-3xl font-bold" style={{ color: '#012068' }}>
@@ -216,7 +240,9 @@ const TeamListing: React.FC<TeamListingProps> = ({
</h1>
</div>
<p className="text-base max-w-3xl leading-relaxed" style={{ color: '#333' }}>
Meet our dedicated team of medical professionals, researchers, and support staff committed to advancing healthcare, education, and patient outcomes at Christian Medical College, Vellore
Meet our dedicated team of medical professionals, researchers, and support staff
committed to advancing healthcare, education, and patient outcomes at Christian
Medical College, Vellore
</p>
</div>
</div>
@@ -243,8 +269,8 @@ const TeamListing: React.FC<TeamListingProps> = ({
</section>
)}
{/* Retired Staff Section */}
{retiredStaff.length > 0 && (
{/* Faculty Alumni Section — RETIRED + RESIGNED */}
{alumniStaff.length > 0 && (
<section className="py-8" style={{ backgroundColor: '#f9f9f9' }}>
<div className="max-w-7xl mx-auto px-4">
<div className="mb-8">
@@ -252,11 +278,11 @@ const TeamListing: React.FC<TeamListingProps> = ({
Faculty Alumni
</h2>
<p className="text-sm mb-2" style={{ color: '#666' }}>
Honoring our retired faculty and staff members for their dedicated service
Honoring our retired and former faculty 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) => (
{alumniStaff.map((member) => (
<TeamMemberCard key={member.id} member={member} />
))}
</div>
@@ -273,10 +299,12 @@ const TeamListing: React.FC<TeamListingProps> = ({
Support Team
</h2>
<p className="text-base leading-relaxed mb-2" style={{ color: '#012068' }}>
<strong>Clinical Support Staff</strong> - Essential team members providing specialized support services
<strong>Clinical Support Staff</strong> — Essential team members providing
specialised support services
</p>
<p className="text-sm mb-1" style={{ color: '#012068' }}>
<strong>Administrative & Technical Support</strong> - Dedicated professionals ensuring smooth operations
<strong>Administrative &amp; Technical Support</strong> — Dedicated professionals
ensuring smooth operations
</p>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
@@ -288,16 +316,43 @@ const TeamListing: React.FC<TeamListingProps> = ({
</section>
)}
{/* Trainees & Fellows Section */}
{traineesAndFellows.length > 0 && (
{/* Guides / Friends / Patrons Section */}
{guidesFriendsPatrons.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' }}>
Trainees & Fellows
Guides / Friends / Patrons
</h2>
<p className="text-base leading-relaxed mb-2" style={{ color: '#012068' }}>
<strong>Guides</strong> — Mentors and senior professionals who have shaped our
department
</p>
<p className="text-sm mb-1" style={{ color: '#012068' }}>
<strong>Friends &amp; Patrons</strong> — Valued supporters and well-wishers of our
institution
</p>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
{guidesFriendsPatrons.map((member) => (
<TeamMemberCard key={member.id} member={member} />
))}
</div>
</div>
</section>
)}
{/* Trainees & Fellows Section */}
{traineesAndFellows.length > 0 && (
<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' }}>
Trainees &amp; Fellows
</h2>
<p className="text-sm mb-2" style={{ color: '#666' }}>
Medical trainees, residents, and fellows advancing their skills and contributing to patient care
Medical trainees, residents, and fellows advancing their skills and contributing
to patient care
</p>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
@@ -309,7 +364,7 @@ const TeamListing: React.FC<TeamListingProps> = ({
</section>
)}
{/* Show message if no faculty data */}
{/* No data message */}
{teamMembers.length === 0 && !loading && (
<section className="py-16 text-center">
<div className="max-w-2xl mx-auto px-4">
@@ -317,10 +372,11 @@ const TeamListing: React.FC<TeamListingProps> = ({
No Faculty Data Available
</h2>
<p className="text-gray-600 mb-6">
Faculty information is currently being updated. Please check back later or contact administration.
Faculty information is currently being updated. Please check back later or contact
administration.
</p>
<button
onClick={() => window.location.reload()}
<button
onClick={() => window.location.reload()}
className="px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
>
Refresh Page
@@ -328,20 +384,6 @@ const TeamListing: React.FC<TeamListingProps> = ({
</div>
</section>
)}
{/* Show message if all categories are empty but we have data */}
{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' }}>
No Categorized Faculty Available
</h2>
<p className="text-gray-600 mb-6">
Faculty members need to be assigned to categories. Please contact administration.
</p>
</div>
</section>
)}
</div>
);
};