![]() 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/consultant/ |
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
} from 'lucide-react';
interface ConsultantProfile {
id?: string;
userId: string;
// Basic Info
name: string;
email: string;
title: string;
bio: string;
profilePicture: string;
// Consulting Specific
consultingSpecialties: string;
industryExpertise: string;
advisoryServiceTypes: string;
clientSizePreferences: string;
projectTypes: string;
// Professional Details
yearsOfExperience: number;
totalConsultations: number;
clientSatisfactionRate: number;
hourlyRate: number;
projectRate: number;
retainerAvailable: boolean;
// Contact & Location
phone: string;
workPhone: string;
officeLocation: string;
timezone: string;
websiteUrl: string;
linkedinUrl: string;
// Settings
isProfilePublic: boolean;
availability: string;
pronouns: string;
}
export default function ConsultantProfile() {
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<ConsultantProfile>({
userId: '',
name: '',
email: '',
title: '',
bio: '',
profilePicture: '',
consultingSpecialties: '',
industryExpertise: '',
advisoryServiceTypes: '',
clientSizePreferences: '',
projectTypes: '',
yearsOfExperience: 0,
totalConsultations: 0,
clientSatisfactionRate: 0,
hourlyRate: 0,
projectRate: 0,
retainerAvailable: false,
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('/auth/login');
return;
}
if (session.user?.role !== 'LEGAL_CONSULTANT') {
router.push('/');
return;
}
loadProfile();
}, [status, router]);
const loadProfile = async () => {
try {
const response = await fetch('/api/consultant/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 || 'Legal Consultant',
}));
}
} 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/consultant/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 ConsultantProfile, 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>Legal Consultant Profile - Legal Platform</title>
<meta name="description" content="Manage your legal consultant 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">Legal Consultant Profile</h1>
<p className="mt-2 text-gray-600">Manage your professional profile and consulting expertise</p>
</div>
<button
onClick={() => router.push('/consultant/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., Legal Consultant, Compliance Advisor"
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 consulting approach, expertise, and what makes you unique..."
className="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500"
/>
</div>
{/* Consulting Expertise */}
<div className="border-t pt-6">
<h3 className="text-lg font-medium text-gray-900 mb-4">Consulting 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">Consulting Specialties</label>
<textarea
value={profile.consultingSpecialties}
onChange={(e) => handleInputChange('consultingSpecialties', e.target.value)}
rows={3}
placeholder="e.g., Compliance, Risk Management, Contract Review, Regulatory Affairs"
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">Industry Expertise</label>
<textarea
value={profile.industryExpertise}
onChange={(e) => handleInputChange('industryExpertise', e.target.value)}
rows={3}
placeholder="e.g., Technology, Healthcare, Finance, Manufacturing"
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">Advisory Service Types</label>
<textarea
value={profile.advisoryServiceTypes}
onChange={(e) => handleInputChange('advisoryServiceTypes', e.target.value)}
rows={3}
placeholder="e.g., Strategic Planning, Policy Development, Training, Audits"
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">Client Size Preferences</label>
<select
value={profile.clientSizePreferences}
onChange={(e) => handleInputChange('clientSizePreferences', e.target.value)}
className="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500"
>
<option value="">Select preference</option>
<option value="startup">Startups & Small Business</option>
<option value="sme">SMEs (10-500 employees)</option>
<option value="enterprise">Enterprise (500+ employees)</option>
<option value="all">All sizes welcome</option>
</select>
</div>
<div>
<label className="block text-sm font-medium text-gray-700">Project Types</label>
<textarea
value={profile.projectTypes}
onChange={(e) => handleInputChange('projectTypes', e.target.value)}
rows={3}
placeholder="e.g., Project-based consulting, Ongoing advisory, Training sessions"
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, Flexible scheduling available"
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 Consultations</label>
<input
type="number"
value={profile.totalConsultations}
onChange={(e) => handleInputChange('totalConsultations', 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">Client Satisfaction Rate (%)</label>
<input
type="number"
value={profile.clientSatisfactionRate}
onChange={(e) => handleInputChange('clientSatisfactionRate', 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>
</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">Project Rate ($)</label>
<input
type="number"
value={profile.projectRate}
onChange={(e) => handleInputChange('projectRate', 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 className="flex items-center">
<input
type="checkbox"
checked={profile.retainerAvailable}
onChange={(e) => handleInputChange('retainerAvailable', 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">
Retainer Agreements Available
</label>
</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 potential clients and other professionals
</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>
</>
);
}