first commit
This commit is contained in:
391
src/components/faculty/TeamMemberDetail.tsx
Normal file
391
src/components/faculty/TeamMemberDetail.tsx
Normal file
@ -0,0 +1,391 @@
|
||||
'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);
|
||||
|
||||
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>
|
||||
);
|
||||
}
|
||||
|
||||
// Create a comprehensive description from the member's details
|
||||
const getFullDescription = () => {
|
||||
return member.description || `${member.name} is a dedicated member of our faculty with expertise in ${member.specialty?.toLowerCase() || 'medical practice'}. They bring valuable experience in 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;
|
||||
};
|
||||
|
||||
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>
|
||||
</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 */}
|
||||
<div className="aspect-square relative overflow-hidden">
|
||||
<img
|
||||
src={member.image}
|
||||
alt={member.name}
|
||||
className="w-full h-full object-cover transform hover:scale-105 transition-transform duration-300"
|
||||
onError={(e) => {
|
||||
const target = e.target as HTMLImageElement;
|
||||
target.src = '/images/default-avatar.jpg';
|
||||
}}
|
||||
/>
|
||||
</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.description || `${member.name} is a dedicated 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 && (
|
||||
<span className="text-xs" style={{ color: '#666' }}>
|
||||
{skill.level}% proficiency
|
||||
</span>
|
||||
)}
|
||||
</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;
|
||||
Reference in New Issue
Block a user