![]() Server : Apache/2 System : Linux server-15-235-50-60 5.15.0-164-generic #174-Ubuntu SMP Fri Nov 14 20:25:16 UTC 2025 x86_64 User : gositeme ( 1004) PHP Version : 8.2.29 Disable Function : exec,system,passthru,shell_exec,proc_close,proc_open,dl,popen,show_source,posix_kill,posix_mkfifo,posix_getpwuid,posix_setpgid,posix_setsid,posix_setuid,posix_setgid,posix_seteuid,posix_setegid,posix_uname Directory : /home/gositeme/backups/lavocat.quebec/backup-20250730-021618/src/pages/expert/ |
import React, { useState, useEffect } from 'react';
import { useSession } from 'next-auth/react';
import { useRouter } from 'next/router';
import Head from 'next/head';
import { toast } from 'react-hot-toast';
import {
User,
Award,
Clock,
Globe,
DollarSign,
Shield,
FileText,
Star,
CheckCircle,
AlertCircle,
Save,
Upload,
Eye,
EyeOff,
Building,
Briefcase,
Target,
Users,
Gavel,
TestTube,
Calculator,
Stethoscope
} from 'lucide-react';
interface ExpertWitnessProfile {
id?: string;
userId: string;
// Basic Info
name: string;
email: string;
title: string;
bio: string;
profilePicture: string;
// Expert Witness Specific
expertTestimonyAreas: string;
credentials: string;
courtExperience: string;
jurisdictions: string;
testimonySuccessRate: number;
reportPreparationServices: string;
// Professional Details
yearsOfExperience: number;
totalTestimonies: number;
averageRating: number;
hourlyRate: number;
reportRate: number;
depositionRate: number;
// Contact & Location
phone: string;
workPhone: string;
officeLocation: string;
timezone: string;
websiteUrl: string;
linkedinUrl: string;
// Settings
isProfilePublic: boolean;
availability: string;
pronouns: string;
}
export default function ExpertWitnessProfile() {
const { data: session, status } = useSession();
const router = useRouter();
const [loading, setLoading] = useState(true);
const [saving, setSaving] = useState(false);
const [uploading, setUploading] = useState(false);
const [profile, setProfile] = useState<ExpertWitnessProfile>({
userId: '',
name: '',
email: '',
title: '',
bio: '',
profilePicture: '',
expertTestimonyAreas: '',
credentials: '',
courtExperience: '',
jurisdictions: '',
testimonySuccessRate: 0,
reportPreparationServices: '',
yearsOfExperience: 0,
totalTestimonies: 0,
averageRating: 0,
hourlyRate: 0,
reportRate: 0,
depositionRate: 0,
phone: '',
workPhone: '',
officeLocation: '',
timezone: '',
websiteUrl: '',
linkedinUrl: '',
isProfilePublic: false,
availability: '',
pronouns: ''
});
useEffect(() => {
if (status === 'unauthenticated') {
router.push('/auth/login');
return;
}
if (status === 'loading') return;
if (!session) {
router.push('/');
return;
}
if (session.user?.role !== 'EXPERT_WITNESS') {
router.push('/');
return;
}
loadProfile();
}, [session, status, router]);
const loadProfile = async () => {
try {
const response = await fetch('/api/expert/profile');
if (response.ok) {
const data = await response.json();
setProfile(data);
} else {
// Initialize with session data
setProfile(prev => ({
...prev,
userId: session?.user?.id || '',
name: session?.user?.name || '',
email: session?.user?.email || '',
title: (session?.user as any)?.title || 'Expert Witness',
}));
}
} catch (error) {
console.error('Error loading profile:', error);
toast.error('Failed to load profile');
} finally {
setLoading(false);
}
};
const handleSave = async () => {
setSaving(true);
try {
const response = await fetch('/api/expert/profile', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(profile),
});
if (response.ok) {
toast.success('Profile updated successfully!');
} else {
const error = await response.json();
toast.error(error.message || 'Failed to update profile');
}
} catch (error) {
console.error('Error saving profile:', error);
toast.error('Failed to save profile');
} finally {
setSaving(false);
}
};
const handleImageUpload = async (event: React.ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0];
if (!file) return;
setUploading(true);
const formData = new FormData();
formData.append('file', file);
try {
const response = await fetch('/api/upload/profile-picture', {
method: 'POST',
body: formData,
});
if (response.ok) {
const data = await response.json();
setProfile(prev => ({ ...prev, profilePicture: data.url }));
toast.success('Profile picture updated!');
} else {
toast.error('Failed to upload image');
}
} catch (error) {
console.error('Error uploading image:', error);
toast.error('Failed to upload image');
} finally {
setUploading(false);
}
};
const handleInputChange = (field: keyof ExpertWitnessProfile, value: any) => {
setProfile(prev => ({ ...prev, [field]: value }));
};
if (status === 'loading') {
return (
<div className="flex items-center justify-center min-h-screen">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600"></div>
<span className="ml-3 text-gray-600">Loading...</span>
</div>
);
}
return (
<>
<Head>
<title>Expert Witness Profile - Legal Platform</title>
<meta name="description" content="Manage your expert witness profile and professional information" />
</Head>
<div className="min-h-screen bg-gray-50">
<div className="max-w-4xl mx-auto py-8 px-4 sm:px-6 lg:px-8">
{/* Header */}
<div className="mb-8">
<div className="flex items-center justify-between">
<div>
<h1 className="text-3xl font-bold text-gray-900">Expert Witness Profile</h1>
<p className="mt-2 text-gray-600">Manage your professional profile and expert testimony expertise</p>
</div>
<button
onClick={() => router.push('/expert/dashboard')}
className="px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50"
>
Back to Dashboard
</button>
</div>
</div>
<div className="bg-white shadow rounded-lg">
<div className="px-6 py-4 border-b border-gray-200">
<h2 className="text-lg font-medium text-gray-900">Profile Information</h2>
</div>
<div className="p-6 space-y-8">
{/* Profile Picture Section */}
<div className="flex items-center space-x-6">
<div className="relative">
<div className="w-24 h-24 rounded-full overflow-hidden bg-gray-200">
{profile.profilePicture ? (
<img
src={profile.profilePicture}
alt="Profile"
className="w-full h-full object-cover"
/>
) : (
<div className="w-full h-full flex items-center justify-center">
<User className="w-12 h-12 text-gray-400" />
</div>
)}
</div>
<label className="absolute bottom-0 right-0 bg-blue-600 text-white p-1 rounded-full cursor-pointer hover:bg-blue-700">
<Upload className="w-4 h-4" />
<input
type="file"
accept="image/*"
onChange={handleImageUpload}
className="hidden"
/>
</label>
</div>
<div>
<h3 className="text-lg font-medium text-gray-900">Profile Picture</h3>
<p className="text-sm text-gray-500">
{uploading ? 'Uploading...' : 'Upload a professional photo'}
</p>
</div>
</div>
{/* Basic Information */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label className="block text-sm font-medium text-gray-700">Full Name</label>
<input
type="text"
value={profile.name}
onChange={(e) => handleInputChange('name', e.target.value)}
className="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700">Email</label>
<input
type="email"
value={profile.email}
disabled
className="mt-1 block w-full border-gray-300 rounded-md shadow-sm bg-gray-50"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700">Professional Title</label>
<input
type="text"
value={profile.title}
onChange={(e) => handleInputChange('title', e.target.value)}
placeholder="e.g., Forensic Expert, Medical Expert, Financial Expert"
className="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700">Pronouns</label>
<input
type="text"
value={profile.pronouns}
onChange={(e) => handleInputChange('pronouns', e.target.value)}
placeholder="e.g., he/him, she/her, they/them"
className="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500"
/>
</div>
</div>
{/* Bio */}
<div>
<label className="block text-sm font-medium text-gray-700">Professional Bio</label>
<textarea
value={profile.bio}
onChange={(e) => handleInputChange('bio', e.target.value)}
rows={4}
placeholder="Describe your expertise, qualifications, and what makes you a credible expert witness..."
className="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500"
/>
</div>
{/* Expert Witness Expertise */}
<div className="border-t pt-6">
<h3 className="text-lg font-medium text-gray-900 mb-4">Expert Witness Expertise</h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label className="block text-sm font-medium text-gray-700">Expert Testimony Areas</label>
<textarea
value={profile.expertTestimonyAreas}
onChange={(e) => handleInputChange('expertTestimonyAreas', e.target.value)}
rows={3}
placeholder="e.g., Forensic Science, Medical Malpractice, Financial Analysis, Engineering"
className="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700">Professional Credentials</label>
<textarea
value={profile.credentials}
onChange={(e) => handleInputChange('credentials', e.target.value)}
rows={3}
placeholder="e.g., PhD, MD, CPA, PE, Board Certifications"
className="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700">Court Experience</label>
<textarea
value={profile.courtExperience}
onChange={(e) => handleInputChange('courtExperience', e.target.value)}
rows={3}
placeholder="e.g., Federal Courts, State Courts, Administrative Hearings, Arbitration"
className="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700">Jurisdictions</label>
<input
type="text"
value={profile.jurisdictions}
onChange={(e) => handleInputChange('jurisdictions', e.target.value)}
placeholder="e.g., California, New York, Federal Courts"
className="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700">Report Preparation Services</label>
<textarea
value={profile.reportPreparationServices}
onChange={(e) => handleInputChange('reportPreparationServices', e.target.value)}
rows={3}
placeholder="e.g., Expert reports, Affidavits, Declaration preparation, Document review"
className="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500"
/>
</div>
</div>
</div>
{/* Availability */}
<div className="border-t pt-6">
<h3 className="text-lg font-medium text-gray-900 mb-4">Availability</h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label className="block text-sm font-medium text-gray-700">Working Hours</label>
<textarea
value={profile.availability}
onChange={(e) => handleInputChange('availability', e.target.value)}
rows={3}
placeholder="e.g., Monday-Friday 9AM-5PM, Weekends for urgent cases"
className="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700">Timezone</label>
<input
type="text"
value={profile.timezone}
onChange={(e) => handleInputChange('timezone', e.target.value)}
placeholder="e.g., EST, PST, GMT"
className="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500"
/>
</div>
</div>
</div>
{/* Professional Statistics */}
<div className="border-t pt-6">
<h3 className="text-lg font-medium text-gray-900 mb-4">Professional Statistics</h3>
<div className="grid grid-cols-1 md:grid-cols-4 gap-6">
<div>
<label className="block text-sm font-medium text-gray-700">Years of Experience</label>
<input
type="number"
value={profile.yearsOfExperience}
onChange={(e) => handleInputChange('yearsOfExperience', parseInt(e.target.value) || 0)}
className="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700">Total Testimonies</label>
<input
type="number"
value={profile.totalTestimonies}
onChange={(e) => handleInputChange('totalTestimonies', parseInt(e.target.value) || 0)}
className="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700">Testimony Success Rate (%)</label>
<input
type="number"
value={profile.testimonySuccessRate}
onChange={(e) => handleInputChange('testimonySuccessRate', parseFloat(e.target.value) || 0)}
min="0"
max="100"
className="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700">Average Rating</label>
<input
type="number"
value={profile.averageRating}
onChange={(e) => handleInputChange('averageRating', parseFloat(e.target.value) || 0)}
min="0"
max="5"
step="0.1"
className="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500"
/>
</div>
</div>
</div>
{/* Pricing */}
<div className="border-t pt-6">
<h3 className="text-lg font-medium text-gray-900 mb-4">Pricing & Services</h3>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
<div>
<label className="block text-sm font-medium text-gray-700">Hourly Rate ($)</label>
<input
type="number"
value={profile.hourlyRate}
onChange={(e) => handleInputChange('hourlyRate', parseFloat(e.target.value) || 0)}
className="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700">Report Rate ($)</label>
<input
type="number"
value={profile.reportRate}
onChange={(e) => handleInputChange('reportRate', parseFloat(e.target.value) || 0)}
className="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700">Deposition Rate ($)</label>
<input
type="number"
value={profile.depositionRate}
onChange={(e) => handleInputChange('depositionRate', parseFloat(e.target.value) || 0)}
className="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500"
/>
</div>
</div>
</div>
{/* Contact Information */}
<div className="border-t pt-6">
<h3 className="text-lg font-medium text-gray-900 mb-4">Contact Information</h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label className="block text-sm font-medium text-gray-700">Phone</label>
<input
type="tel"
value={profile.phone}
onChange={(e) => handleInputChange('phone', e.target.value)}
className="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700">Work Phone</label>
<input
type="tel"
value={profile.workPhone}
onChange={(e) => handleInputChange('workPhone', e.target.value)}
className="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700">Office Location</label>
<input
type="text"
value={profile.officeLocation}
onChange={(e) => handleInputChange('officeLocation', e.target.value)}
placeholder="City, Province/State"
className="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700">Website</label>
<input
type="url"
value={profile.websiteUrl}
onChange={(e) => handleInputChange('websiteUrl', e.target.value)}
placeholder="https://your-website.com"
className="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700">LinkedIn</label>
<input
type="url"
value={profile.linkedinUrl}
onChange={(e) => handleInputChange('linkedinUrl', e.target.value)}
placeholder="https://linkedin.com/in/your-profile"
className="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500"
/>
</div>
</div>
</div>
{/* Profile Visibility */}
<div className="border-t pt-6">
<div className="flex items-center justify-between">
<div>
<h3 className="text-lg font-medium text-gray-900">Profile Visibility</h3>
<p className="text-sm text-gray-500">
Make your profile visible to attorneys and legal professionals seeking expert witnesses
</p>
</div>
<div className="flex items-center">
<input
type="checkbox"
checked={profile.isProfilePublic}
onChange={(e) => handleInputChange('isProfilePublic', e.target.checked)}
className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded"
/>
<label className="ml-2 block text-sm text-gray-900">
Make Profile Public
</label>
</div>
</div>
</div>
{/* Save Button */}
<div className="border-t pt-6">
<div className="flex justify-end">
<button
onClick={handleSave}
disabled={saving}
className="inline-flex items-center px-6 py-3 border border-transparent text-base font-medium rounded-md shadow-sm text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50"
>
{saving ? (
<>
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white mr-2"></div>
Saving...
</>
) : (
<>
<Save className="w-4 h-4 mr-2" />
Save Profile
</>
)}
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</>
);
}