![]() 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/domains/lavocat.ca/private_html/src/pages/court-clerk/ |
import React, { useState, useEffect } from 'react';
import { useRouter } from 'next/router';
import { useSession } from 'next-auth/react';
import { toast } from 'react-hot-toast';
import {
User,
Mail,
Phone,
MapPin,
Calendar,
Briefcase,
Award,
Globe,
Lock,
Unlock,
Upload,
Save,
X,
Eye,
EyeOff
} from 'lucide-react';
interface CourtClerkProfile {
id: string;
userId: string;
firstName: string;
lastName: string;
email: string;
phone: string;
address: string;
city: string;
state: string;
zipCode: string;
country: string;
dateOfBirth: string;
gender: string;
bio: string;
experience: string;
education: string;
certifications: string;
languages: string;
specializations: string;
courtLevel: string;
jurisdiction: string;
availability: string;
hourlyRate: number;
isPublic: boolean;
profilePicture: string;
linkedinUrl: string;
websiteUrl: string;
createdAt: string;
updatedAt: string;
}
export default function CourtClerkProfilePage() {
const { data: session, status } = useSession();
const router = useRouter();
const [profile, setProfile] = useState<CourtClerkProfile | null>(null);
const [loading, setLoading] = useState(true);
const [saving, setSaving] = useState(false);
const [showPassword, setShowPassword] = useState(false);
const [password, setPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');
useEffect(() => {
if (status === 'loading') return;
if (!session) {
router.push('/auth/login');
return;
}
if (session.user?.role !== 'COURT_CLERK') {
router.push('/dashboard');
return;
}
fetchProfile();
}, [session, status, router]);
const fetchProfile = async () => {
try {
const response = await fetch('/api/court-clerk/profile');
if (response.ok) {
const data = await response.json();
setProfile(data);
} else {
toast.error('Failed to load profile');
}
} catch (error) {
toast.error('Error loading profile');
} finally {
setLoading(false);
}
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!profile) return;
if (password && password !== confirmPassword) {
toast.error('Passwords do not match');
return;
}
setSaving(true);
try {
const formData = new FormData();
// Add profile data
Object.entries(profile).forEach(([key, value]) => {
if (key !== 'id' && key !== 'userId' && key !== 'createdAt' && key !== 'updatedAt') {
formData.append(key, String(value));
}
});
// Add password if provided
if (password) {
formData.append('password', password);
}
const response = await fetch('/api/court-clerk/profile', {
method: 'PUT',
body: formData,
});
if (response.ok) {
toast.success('Profile updated successfully');
setPassword('');
setConfirmPassword('');
} else {
const error = await response.json();
toast.error(error.message || 'Failed to update profile');
}
} catch (error) {
toast.error('Error updating profile');
} finally {
setSaving(false);
}
};
const handleProfilePictureUpload = async (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (!file || !profile) return;
const formData = new FormData();
formData.append('profilePicture', file);
try {
const response = await fetch('/api/court-clerk/profile/picture', {
method: 'POST',
body: formData,
});
if (response.ok) {
const { profilePicture } = await response.json();
setProfile(prev => prev ? { ...prev, profilePicture } : null);
toast.success('Profile picture updated');
} else {
toast.error('Failed to upload profile picture');
}
} catch (error) {
toast.error('Error uploading profile picture');
}
};
const handleInputChange = (field: keyof CourtClerkProfile, value: string | number | boolean) => {
if (!profile) return;
setProfile(prev => prev ? { ...prev, [field]: value } : null);
};
if (loading) {
return (
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
<div className="animate-spin rounded-full h-32 w-32 border-b-2 border-blue-600"></div>
</div>
);
}
if (!profile) {
return (
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
<div className="text-center">
<h2 className="text-2xl font-bold text-gray-900 mb-4">Profile Not Found</h2>
<p className="text-gray-600">Unable to load your profile.</p>
</div>
</div>
);
}
return (
<div className="min-h-screen bg-gray-50 py-8">
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="bg-white rounded-lg shadow-lg overflow-hidden">
{/* Header */}
<div className="bg-gradient-to-r from-purple-600 to-pink-600 px-6 py-8">
<div className="flex items-center justify-between">
<div className="flex items-center space-x-4">
<div className="relative">
<div className="w-20 h-20 rounded-full bg-white/20 flex items-center justify-center">
{profile.profilePicture ? (
<img
src={profile.profilePicture}
alt="Profile"
className="w-20 h-20 rounded-full object-cover"
/>
) : (
<User className="w-10 h-10 text-white" />
)}
</div>
<label className="absolute bottom-0 right-0 bg-white rounded-full p-1 cursor-pointer hover:bg-gray-100">
<Upload className="w-4 h-4 text-gray-600" />
<input
type="file"
accept="image/*"
onChange={handleProfilePictureUpload}
className="hidden"
/>
</label>
</div>
<div>
<h1 className="text-2xl font-bold text-white">
{profile.firstName} {profile.lastName}
</h1>
<p className="text-purple-100">Court Clerk Profile</p>
</div>
</div>
<div className="flex items-center space-x-2">
<button
onClick={() => handleInputChange('isPublic', !profile.isPublic)}
className={`flex items-center space-x-2 px-4 py-2 rounded-lg ${
profile.isPublic
? 'bg-green-500 hover:bg-green-600 text-white'
: 'bg-gray-500 hover:bg-gray-600 text-white'
}`}
>
{profile.isPublic ? <Globe className="w-4 h-4" /> : <Lock className="w-4 h-4" />}
<span>{profile.isPublic ? 'Public' : 'Private'}</span>
</button>
</div>
</div>
</div>
{/* Form */}
<form onSubmit={handleSubmit} className="p-6 space-y-8">
{/* Personal Information */}
<div className="space-y-6">
<h2 className="text-xl font-semibold text-gray-900 flex items-center">
<User className="w-5 h-5 mr-2" />
Personal Information
</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
First Name *
</label>
<input
type="text"
value={profile.firstName}
onChange={(e) => handleInputChange('firstName', e.target.value)}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent"
required
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Last Name *
</label>
<input
type="text"
value={profile.lastName}
onChange={(e) => handleInputChange('lastName', e.target.value)}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent"
required
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Email *
</label>
<div className="relative">
<Mail className="absolute left-3 top-2.5 w-4 h-4 text-gray-400" />
<input
type="email"
value={profile.email}
onChange={(e) => handleInputChange('email', e.target.value)}
className="w-full pl-10 pr-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent"
required
/>
</div>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Phone *
</label>
<div className="relative">
<Phone className="absolute left-3 top-2.5 w-4 h-4 text-gray-400" />
<input
type="tel"
value={profile.phone}
onChange={(e) => handleInputChange('phone', e.target.value)}
className="w-full pl-10 pr-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent"
required
/>
</div>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Date of Birth
</label>
<input
type="date"
value={profile.dateOfBirth}
onChange={(e) => handleInputChange('dateOfBirth', e.target.value)}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Gender
</label>
<select
value={profile.gender}
onChange={(e) => handleInputChange('gender', e.target.value)}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent"
>
<option value="">Select Gender</option>
<option value="male">Male</option>
<option value="female">Female</option>
<option value="other">Other</option>
<option value="prefer-not-to-say">Prefer not to say</option>
</select>
</div>
</div>
</div>
{/* Address Information */}
<div className="space-y-6">
<h2 className="text-xl font-semibold text-gray-900 flex items-center">
<MapPin className="w-5 h-5 mr-2" />
Address Information
</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div className="md:col-span-2">
<label className="block text-sm font-medium text-gray-700 mb-2">
Address
</label>
<input
type="text"
value={profile.address}
onChange={(e) => handleInputChange('address', e.target.value)}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
City
</label>
<input
type="text"
value={profile.city}
onChange={(e) => handleInputChange('city', e.target.value)}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
State/Province
</label>
<input
type="text"
value={profile.state}
onChange={(e) => handleInputChange('state', e.target.value)}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
ZIP/Postal Code
</label>
<input
type="text"
value={profile.zipCode}
onChange={(e) => handleInputChange('zipCode', e.target.value)}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Country
</label>
<input
type="text"
value={profile.country}
onChange={(e) => handleInputChange('country', e.target.value)}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent"
/>
</div>
</div>
</div>
{/* Professional Information */}
<div className="space-y-6">
<h2 className="text-xl font-semibold text-gray-900 flex items-center">
<Briefcase className="w-5 h-5 mr-2" />
Professional Information
</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Years of Experience
</label>
<input
type="text"
value={profile.experience}
onChange={(e) => handleInputChange('experience', e.target.value)}
placeholder="e.g., 4 years"
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Education
</label>
<input
type="text"
value={profile.education}
onChange={(e) => handleInputChange('education', e.target.value)}
placeholder="e.g., Bachelor's in Criminal Justice"
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Certifications
</label>
<input
type="text"
value={profile.certifications}
onChange={(e) => handleInputChange('certifications', e.target.value)}
placeholder="e.g., Court Clerk Certification, Notary Public"
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Languages
</label>
<input
type="text"
value={profile.languages}
onChange={(e) => handleInputChange('languages', e.target.value)}
placeholder="e.g., English, Spanish, French"
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Specializations
</label>
<input
type="text"
value={profile.specializations}
onChange={(e) => handleInputChange('specializations', e.target.value)}
placeholder="e.g., Criminal Court, Civil Court, Family Court"
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Court Level
</label>
<input
type="text"
value={profile.courtLevel}
onChange={(e) => handleInputChange('courtLevel', e.target.value)}
placeholder="e.g., Municipal, State, Federal"
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Jurisdiction
</label>
<input
type="text"
value={profile.jurisdiction}
onChange={(e) => handleInputChange('jurisdiction', e.target.value)}
placeholder="e.g., County, District, Circuit"
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Availability
</label>
<input
type="text"
value={profile.availability}
onChange={(e) => handleInputChange('availability', e.target.value)}
placeholder="e.g., Full-time, Part-time, Flexible"
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Hourly Rate ($)
</label>
<input
type="number"
value={profile.hourlyRate}
onChange={(e) => handleInputChange('hourlyRate', parseFloat(e.target.value) || 0)}
placeholder="0.00"
min="0"
step="0.01"
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent"
/>
</div>
</div>
</div>
{/* Bio */}
<div className="space-y-6">
<h2 className="text-xl font-semibold text-gray-900">Professional Bio</h2>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
About Me
</label>
<textarea
value={profile.bio}
onChange={(e) => handleInputChange('bio', e.target.value)}
rows={6}
placeholder="Tell clients about your experience, skills, and what makes you a great court clerk..."
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent"
/>
</div>
</div>
{/* Social Links */}
<div className="space-y-6">
<h2 className="text-xl font-semibold text-gray-900 flex items-center">
<Globe className="w-5 h-5 mr-2" />
Social Links
</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
LinkedIn URL
</label>
<input
type="url"
value={profile.linkedinUrl}
onChange={(e) => handleInputChange('linkedinUrl', e.target.value)}
placeholder="https://linkedin.com/in/yourprofile"
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Website URL
</label>
<input
type="url"
value={profile.websiteUrl}
onChange={(e) => handleInputChange('websiteUrl', e.target.value)}
placeholder="https://yourwebsite.com"
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent"
/>
</div>
</div>
</div>
{/* Password Change */}
<div className="space-y-6">
<h2 className="text-xl font-semibold text-gray-900">Change Password</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
New Password
</label>
<div className="relative">
<input
type={showPassword ? 'text' : 'password'}
value={password}
onChange={(e) => setPassword(e.target.value)}
className="w-full pr-10 pl-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent"
/>
<button
type="button"
onClick={() => setShowPassword(!showPassword)}
className="absolute right-3 top-2.5 text-gray-400 hover:text-gray-600"
>
{showPassword ? <EyeOff className="w-4 h-4" /> : <Eye className="w-4 h-4" />}
</button>
</div>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Confirm New Password
</label>
<input
type="password"
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent"
/>
</div>
</div>
</div>
{/* Submit Button */}
<div className="flex justify-end space-x-4 pt-6 border-t border-gray-200">
<button
type="button"
onClick={() => router.back()}
className="px-6 py-2 border border-gray-300 rounded-lg text-gray-700 hover:bg-gray-50 focus:ring-2 focus:ring-purple-500 focus:border-transparent"
>
Cancel
</button>
<button
type="submit"
disabled={saving}
className="px-6 py-2 bg-purple-600 text-white rounded-lg hover:bg-purple-700 focus:ring-2 focus:ring-purple-500 focus:border-transparent disabled:opacity-50 disabled:cursor-not-allowed flex items-center"
>
{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 Changes
</>
)}
</button>
</div>
</form>
</div>
</div>
</div>
);
}