Files
cmc_frontend/src/components/faculty/TeamMemberDetail.tsx
2025-11-05 15:54:33 +05:30

425 lines
20 KiB
TypeScript

'use client'
import React, { useState, useEffect } from 'react';
import Link from 'next/link';
import {
Phone,
Mail,
Share,
ChevronRight,
Check,
} from 'lucide-react';
import { TeamMember, FacultyService } from '../../lib/facultyData';
interface TeamMemberDetailProps {
memberId: number;
memberData?: TeamMember;
}
const TeamMemberDetail: React.FC<TeamMemberDetailProps> = ({ memberId, memberData }) => {
const [member, setMember] = useState<TeamMember | null>(memberData || null);
const [loading, setLoading] = useState(!memberData);
const [error, setError] = useState<string | null>(null);
const [showSocialShare, setShowSocialShare] = useState(false);
const [imageError, setImageError] = useState(false);
useEffect(() => {
const loadMemberData = async () => {
if (memberData) return; // Already have data
try {
setLoading(true);
const fetchedMember = await FacultyService.getFacultyById(memberId);
if (fetchedMember) {
setMember(fetchedMember);
} else {
setError('Faculty member not found');
}
} catch (err) {
console.error('Failed to load faculty member:', err);
setError('Failed to load faculty member data');
} finally {
setLoading(false);
}
};
loadMemberData();
}, [memberId, memberData]);
if (loading) {
return (
<div className="min-h-screen flex items-center justify-center">
<div className="text-center">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto mb-4"></div>
<p>Loading faculty member details...</p>
</div>
</div>
);
}
if (error || !member) {
return (
<div className="min-h-screen flex items-center justify-center">
<div className="text-center">
<p className="text-red-600 mb-4">{error || 'Faculty member not found'}</p>
<Link
href="/teamMember"
className="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 transition-colors"
>
Back to Faculty
</Link>
</div>
</div>
);
}
const isTrainee = member.category === 'TRAINEE_FELLOW';
// 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');
const shouldShowImage = !isTrainee && hasValidImage && !imageError;
// Create a comprehensive description from the member's details
const getFullDescription = () => {
return member.description || `${member.name} is a dedicated member of our ${isTrainee ? 'trainee program' : 'faculty'} with expertise in ${member.specialty?.toLowerCase() || 'medical practice'}. They bring valuable experience in ${isTrainee ? 'clinical training' : 'surgical education'}, patient care, and clinical research to Christian Medical College, Vellore.`;
};
// Parse phone numbers if multiple are provided
const getFormattedPhone = () => {
if (!member.phone || member.phone === 'Not available') {
return 'Contact through main office';
}
return member.phone.includes(',')
? member.phone.split(',').map(p => p.trim()).join(' / ')
: member.phone;
};
const handleImageError = (e: React.SyntheticEvent<HTMLImageElement>) => {
console.error('Image failed to load:', member.image);
setImageError(true);
};
return (
<div className="min-h-screen bg-white">
{/* Breadcrumb Section */}
<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="/"
className="hover:opacity-70 transition-opacity duration-200"
style={{ color: '#012068' }}
>
Home
</Link>
<ChevronRight className="w-4 h-4" style={{ color: '#012068' }} />
<Link
href="/teamMember"
className="hover:opacity-70 transition-opacity duration-200"
style={{ color: '#012068' }}
>
Faculty
</Link>
<ChevronRight className="w-4 h-4" style={{ color: '#012068' }} />
<span className="font-medium" style={{ color: '#e64838' }}>
{member.name}
</span>
</nav>
{/* Page Header */}
<div className="mt-6">
<div className="flex items-center mb-4">
<h1 className="text-3xl font-bold" style={{ color: '#012068' }}>
{member.name}
</h1>
{isTrainee && (
<span className="ml-4 px-3 py-1 text-xs font-medium rounded-full bg-gray-200 text-gray-700">
Trainee/Fellow
</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
</p>
</div>
</div>
</section>
{/* Main Content */}
<div className="max-w-7xl mx-auto px-4 py-8 bg-white">
<div className="grid grid-cols-1 xl:grid-cols-4 gap-6 lg:gap-8">
{/* Sidebar */}
<div className="xl:col-span-1">
<div className="bg-white rounded-md border border-gray-300 overflow-hidden sticky top-8">
{/* Profile Image or Avatar */}
<div className="aspect-square relative overflow-hidden bg-gray-50">
{shouldShowImage ? (
<img
src={member.image}
alt={member.name}
className="w-full h-full object-cover transform hover:scale-105 transition-transform duration-300"
onError={handleImageError}
/>
) : (
<div
className="w-full h-full flex items-center justify-center"
style={{ backgroundColor: '#e0e5eb' }}
>
<svg
className="w-48 h-48 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>
{/* Profile Info */}
<div className="p-4 sm:p-6">
<div className="mb-6">
<div
className="inline-flex items-center py-1 text-sm font-medium mb-3"
style={{ color: '#012068' }}
>
{member.designation || member.position}
</div>
<h2 className="text-xl font-bold mb-2" style={{ color: '#012068' }}>{member.name}</h2>
</div>
<p className="text-sm mb-6 leading-relaxed" style={{ color: '#333' }}>
{member.specialty || `${member.name} is a dedicated ${isTrainee ? 'trainee' : 'faculty member'} at CMC Vellore.`}
</p>
<div className="space-y-4 mb-6">
<div className="flex items-start">
<Phone className="w-4 h-4 mr-3 mt-0.5 flex-shrink-0" style={{ color: '#e64838' }} />
<div>
<div className="text-sm font-medium mb-1" style={{ color: '#012068' }}>Phone</div>
<div className="text-sm" style={{ color: '#333' }}>{getFormattedPhone()}</div>
</div>
</div>
<div className="flex items-start">
<Mail className="w-4 h-4 mr-3 mt-0.5 flex-shrink-0" style={{ color: '#e64838' }} />
<div>
<div className="text-sm font-medium mb-1" style={{ color: '#012068' }}>Email</div>
<a href={`mailto:${member.email}`} className="text-sm hover:underline transition-colors" style={{ color: '#e64838' }}>
{member.email}
</a>
</div>
</div>
{member.experience && (
<div className="flex items-start">
<div
className="w-4 h-4 rounded-full mr-3 mt-0.5 flex-shrink-0 flex items-center justify-center"
style={{ backgroundColor: '#e64838' }}
>
<div className="w-2 h-2 bg-white rounded-full"></div>
</div>
<div>
<div className="text-sm font-medium mb-1" style={{ color: '#012068' }}>Experience</div>
<div className="text-sm" style={{ color: '#333' }}>{member.experience}</div>
</div>
</div>
)}
{member.department && (
<div className="flex items-start">
<div
className="w-4 h-4 rounded-full mr-3 mt-0.5 flex-shrink-0 flex items-center justify-center"
style={{ backgroundColor: '#012068' }}
>
<div className="w-2 h-2 bg-white rounded-full"></div>
</div>
<div>
<div className="text-sm font-medium mb-1" style={{ color: '#012068' }}>Department</div>
<div className="text-sm" style={{ color: '#333' }}>{member.department}</div>
</div>
</div>
)}
{member.officeLocation && (
<div className="flex items-start">
<div
className="w-4 h-4 rounded-full mr-3 mt-0.5 flex-shrink-0 flex items-center justify-center"
style={{ backgroundColor: '#012068' }}
>
<div className="w-2 h-2 bg-white rounded-full"></div>
</div>
<div>
<div className="text-sm font-medium mb-1" style={{ color: '#012068' }}>Office Location</div>
<div className="text-sm" style={{ color: '#333' }}>{member.officeLocation}</div>
</div>
</div>
)}
</div>
{/* Social Share */}
<div className="relative">
<button
onClick={() => setShowSocialShare(!showSocialShare)}
className="flex items-center justify-center w-full px-4 py-3 rounded-lg transition-opacity font-medium text-sm hover:opacity-90"
style={{ backgroundColor: '#f4f4f4', color: '#012068' }}
>
<Share className="w-4 h-4 mr-2" />
Share Profile
</button>
</div>
</div>
</div>
</div>
{/* Main Content */}
<div className="xl:col-span-3">
<div className="bg-white rounded-md border border-gray-300 overflow-hidden">
{/* Personal Info */}
<div className="p-6 sm:p-8">
<div className="mb-8">
<div className="flex items-center mb-6">
<h3 className="text-xl font-semibold" style={{ color: '#012068' }}>About</h3>
</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">
{getFullDescription()}
</p>
<p className="mt-6 text-base text-gray-700">
As a dedicated member of the Department of {member.department || 'Medicine'} at Christian Medical College, Vellore,
{member.name.split(' ')[1] || member.name} contributes to advancing medical education, patient care, and
clinical research. Their commitment to excellence in healthcare and medical education makes
them an invaluable asset to the medical community and the patients they serve.
</p>
</div>
{/* Details Grid */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<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' }}>Clinical Focus & Specialty</div>
<div className="text-sm leading-relaxed" style={{ color: '#333' }}>{member.specialty || 'General Medicine'}</div>
</div>
<div className="rounded-lg p-4 border border-gray-300" style={{ backgroundColor: '#f4f4f4' }}>
<div className="font-medium mb-2 text-sm" style={{ color: '#012068' }}>Education & Certification</div>
<div className="text-sm leading-relaxed" style={{ color: '#333' }}>{member.certification || 'Medical certification details available upon request'}</div>
</div>
</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>
{member.workDays && member.workDays.length > 0 && (
<div className="rounded-lg p-4 border border-gray-300" style={{ backgroundColor: '#f4f4f4' }}>
<div className="font-medium mb-3 text-sm" style={{ color: '#012068' }}>Clinical Days</div>
<div className="space-y-2">
{member.workDays.map((day, index) => (
<div key={index} className="flex items-center">
<div
className="w-4 h-4 rounded flex items-center justify-center mr-3"
style={{ backgroundColor: '#e64838' }}
>
<Check className="w-3 h-3 text-white" />
</div>
<span className="text-sm font-medium" style={{ color: '#012068' }}>{day}</span>
</div>
))}
</div>
</div>
)}
</div>
</div>
</div>
{/* Skills and Awards */}
<div className="grid grid-cols-1 xl:grid-cols-2 gap-8 lg:gap-12">
{/* Research Interests & Skills */}
{member.skills && member.skills.length > 0 && (
<div>
<div className="flex items-center mb-6">
<h3 className="text-xl font-semibold" style={{ color: '#012068' }}>Research Interests & Expertise</h3>
</div>
<p className="text-sm mb-6 leading-relaxed" style={{ color: '#333' }}>
Areas of clinical expertise, research focus, and professional competencies that contribute to advancing medical practice and education at CMC Vellore.
</p>
<div className="space-y-4">
{member.skills.map((skill, index) => (
<div key={index} className="rounded-lg p-4 border border-gray-300" style={{ backgroundColor: '#f4f4f4' }}>
<div className="flex justify-between items-center">
<span className="font-medium text-sm" style={{ color: '#012068' }}>{skill.name}</span>
{skill.level}
</div>
</div>
))}
</div>
</div>
)}
{/* Awards */}
{member.awards && member.awards.length > 0 && (
<div>
<div className="flex items-center mb-6">
<h3 className="text-xl font-semibold" style={{ color: '#012068' }}>Awards and Recognition</h3>
</div>
<p className="text-sm mb-6 leading-relaxed" style={{ color: '#333' }}>
Professional achievements, awards, and recognition received for excellence in medical practice, research, and education.
</p>
<div className="space-y-6">
{member.awards.map((award, index) => (
<div key={index} className="rounded-lg p-4 border border-gray-300" style={{ backgroundColor: '#f4f4f4' }}>
<div className="flex items-start space-x-3">
<img
src={award.image}
alt={award.title}
className="w-12 h-12 rounded object-cover border border-gray-300 flex-shrink-0"
onError={(e) => {
const target = e.target as HTMLImageElement;
target.src = '/images/award-icon.png';
}}
/>
<div className="flex-1">
<div
className="inline-flex items-center px-2 py-1 rounded text-xs font-medium mb-2"
style={{ backgroundColor: '#e64838', color: 'white' }}
>
{award.year}
</div>
<h4 className="text-base font-medium mb-2" style={{ color: '#012068' }}>{award.title}</h4>
<p className="text-sm leading-relaxed" style={{ color: '#333' }}>{award.description}</p>
</div>
</div>
</div>
))}
</div>
</div>
)}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
);
};
export default TeamMemberDetail;