![]() 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/hire/ |
import React, { useState, useEffect } from 'react';
import { useRouter } from 'next/router';
import { useSession } from 'next-auth/react';
import Head from 'next/head';
import LayoutWithSidebar from '../../components/LayoutWithSidebar';
import { motion } from 'framer-motion';
import {
Users, FileText, DollarSign, ArrowLeft, ArrowRight,
User, CheckCircle, Handshake, Target
} from 'lucide-react';
import toast from 'react-hot-toast';
interface Lawyer {
id: string;
name: string;
hourlyRate?: number;
specialization?: string;
isVerified: boolean;
}
interface Case {
id: string;
title: string;
caseType: string;
status: string;
priority: string;
budget?: number;
description?: string;
}
const CaseOfferPage: React.FC = () => {
const router = useRouter();
const { data: session } = useSession();
const [lawyer, setLawyer] = useState<Lawyer | null>(null);
const [cases, setCases] = useState<Case[]>([]);
const [selectedCase, setSelectedCase] = useState<string>('');
const [loading, setLoading] = useState(true);
const [submitting, setSubmitting] = useState(false);
const [formData, setFormData] = useState({
collaborationType: 'JOINT_REPRESENTATION',
feeSplit: '50-50',
proposedRate: '',
estimatedHours: '',
message: ''
});
const { lawyerId } = router.query;
useEffect(() => {
if (session?.user?.id) {
fetchLawyerDetails();
fetchUserCases();
}
}, [session, lawyerId]);
const fetchLawyerDetails = async () => {
try {
const response = await fetch(`/api/user/${lawyerId}`);
if (response.ok) {
const data = await response.json();
setLawyer(data.user);
}
} catch (error) {
console.error('Error fetching lawyer details:', error);
toast.error('Failed to load lawyer details');
}
};
const fetchUserCases = async () => {
try {
const response = await fetch('/api/user/cases');
if (response.ok) {
const data = await response.json();
setCases(data.cases || []);
}
} catch (error) {
console.error('Error fetching cases:', error);
toast.error('Failed to load your cases');
} finally {
setLoading(false);
}
};
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>) => {
const { name, value } = e.target;
setFormData(prev => ({
...prev,
[name]: value
}));
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!selectedCase) {
toast.error('Please select a case');
return;
}
setSubmitting(true);
try {
const response = await fetch('/api/hire/case-offer', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
lawyerId,
caseId: selectedCase,
...formData
}),
});
if (response.ok) {
toast.success('Case collaboration request sent successfully!');
router.push('/user/dashboard');
} else {
const error = await response.json();
toast.error(error.error || 'Failed to send request');
}
} catch (error) {
console.error('Error sending case offer:', error);
toast.error('Failed to send request');
} finally {
setSubmitting(false);
}
};
const collaborationTypes = [
{ value: 'JOINT_REPRESENTATION', label: 'Joint Representation' },
{ value: 'CO_COUNSEL', label: 'Co-Counsel' },
{ value: 'SPECIALIST_CONSULTATION', label: 'Specialist Consultation' },
{ value: 'LEAD_COUNSEL', label: 'Lead Counsel (You Lead)' },
{ value: 'SUPPORTING_COUNSEL', label: 'Supporting Counsel' }
];
const feeSplits = [
{ value: '50-50', label: '50% - 50% (Equal Split)' },
{ value: '60-40', label: '60% - 40% (You Lead)' },
{ value: '40-60', label: '40% - 60% (Lawyer Leads)' },
{ value: '70-30', label: '70% - 30% (You Lead)' },
{ value: '30-70', label: '30% - 70% (Lawyer Leads)' },
{ value: 'NEGOTIABLE', label: 'Negotiable' }
];
if (loading) {
return (
<LayoutWithSidebar>
<div className="flex items-center justify-center min-h-screen">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
</div>
</LayoutWithSidebar>
);
}
if (!lawyer) {
return (
<LayoutWithSidebar>
<div className="text-center py-12">
<h1 className="text-2xl font-bold text-gray-900 mb-4">Lawyer not found</h1>
<button
onClick={() => router.back()}
className="text-blue-600 hover:text-blue-800"
>
Go back
</button>
</div>
</LayoutWithSidebar>
);
}
return (
<LayoutWithSidebar>
<Head>
<title>Case Collaboration - {lawyer.name}</title>
</Head>
<div className="max-w-4xl mx-auto px-4 py-8">
{/* Header */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
className="mb-8"
>
<div className="flex items-center gap-4 mb-4">
<button
onClick={() => router.back()}
className="p-2 text-gray-600 hover:text-gray-800 transition-colors"
>
<ArrowLeft className="h-5 w-5" />
</button>
<div>
<h1 className="text-3xl font-bold text-gray-900">Case Collaboration</h1>
<p className="text-gray-600">Propose collaboration with {lawyer.name}</p>
</div>
</div>
</motion.div>
{/* Lawyer Info */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.1 }}
className="bg-white rounded-lg shadow-sm border border-gray-200 p-6 mb-6"
>
<div className="flex items-center gap-4">
<div className="w-16 h-16 bg-gradient-to-br from-blue-400 to-purple-500 rounded-full flex items-center justify-center">
<User className="h-8 w-8 text-white" />
</div>
<div>
<h2 className="text-xl font-semibold text-gray-900 flex items-center">
{lawyer.name}
{lawyer.isVerified && (
<span className="ml-2 text-blue-600">✓ Verified</span>
)}
</h2>
{lawyer.specialization && (
<p className="text-gray-600">{lawyer.specialization}</p>
)}
{lawyer.hourlyRate && (
<p className="text-green-600 font-medium">${lawyer.hourlyRate}/hour</p>
)}
</div>
</div>
</motion.div>
{/* Case Selection */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.2 }}
className="bg-white rounded-lg shadow-sm border border-gray-200 p-6 mb-6"
>
<h2 className="text-xl font-semibold text-gray-900 mb-4 flex items-center">
<FileText className="h-5 w-5 mr-2 text-blue-600" />
Select Case for Collaboration
</h2>
{cases.length === 0 ? (
<div className="text-center py-8">
<FileText className="h-16 w-16 text-gray-400 mx-auto mb-4" />
<h3 className="text-lg font-medium text-gray-900 mb-2">No cases found</h3>
<p className="text-gray-600 mb-4">You need to have a case to propose collaboration.</p>
<button
onClick={() => router.push('/user/cases/new')}
className="inline-flex items-center px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
>
<FileText className="h-4 w-4 mr-2" />
Create New Case
</button>
</div>
) : (
<div className="space-y-4">
{cases.map((case_) => (
<div
key={case_.id}
className={`border-2 rounded-lg p-4 cursor-pointer transition-all duration-200 ${
selectedCase === case_.id
? 'border-blue-500 bg-blue-50'
: 'border-gray-200 hover:border-blue-300'
}`}
onClick={() => setSelectedCase(case_.id)}
>
<div className="flex items-start justify-between">
<div className="flex-1">
<div className="flex items-center gap-2 mb-2">
<h3 className="font-semibold text-gray-900">{case_.title}</h3>
{selectedCase === case_.id && (
<CheckCircle className="h-5 w-5 text-blue-600" />
)}
</div>
<div className="flex items-center gap-4 text-sm text-gray-600 mb-2">
<span>{case_.caseType}</span>
<span>{case_.status}</span>
{case_.budget && (
<span className="text-green-600">${case_.budget.toLocaleString()}</span>
)}
</div>
{case_.description && (
<p className="text-sm text-gray-600 line-clamp-2">
{case_.description}
</p>
)}
<div className="flex items-center gap-2 mt-2">
<span className={`px-2 py-1 text-xs rounded-full ${
case_.priority === 'HIGH'
? 'bg-red-100 text-red-800'
: case_.priority === 'MEDIUM'
? 'bg-yellow-100 text-yellow-800'
: 'bg-green-100 text-green-800'
}`}>
{case_.priority} Priority
</span>
</div>
</div>
</div>
</div>
))}
</div>
)}
</motion.div>
{/* Collaboration Form */}
<motion.form
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.3 }}
onSubmit={handleSubmit}
className="bg-white rounded-lg shadow-sm border border-gray-200 p-6"
>
<h2 className="text-xl font-semibold text-gray-900 mb-6 flex items-center">
<Handshake className="h-5 w-5 mr-2 text-blue-600" />
Collaboration Details
</h2>
<div className="space-y-6">
{/* Collaboration Type */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Collaboration Type
</label>
<select
name="collaborationType"
value={formData.collaborationType}
onChange={handleInputChange}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
>
{collaborationTypes.map(type => (
<option key={type.value} value={type.value}>
{type.label}
</option>
))}
</select>
</div>
{/* Fee Split */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Fee Split
</label>
<select
name="feeSplit"
value={formData.feeSplit}
onChange={handleInputChange}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
>
{feeSplits.map(split => (
<option key={split.value} value={split.value}>
{split.label}
</option>
))}
</select>
</div>
{/* Proposed Rate and Hours */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Proposed Rate (CAD/hour)
</label>
<div className="relative">
<DollarSign className="absolute left-3 top-1/2 transform -translate-y-1/2 h-5 w-5 text-gray-400" />
<input
type="number"
name="proposedRate"
value={formData.proposedRate}
onChange={handleInputChange}
placeholder="Enter rate"
className="w-full pl-10 pr-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
</div>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Estimated Hours
</label>
<input
type="number"
name="estimatedHours"
value={formData.estimatedHours}
onChange={handleInputChange}
placeholder="Enter hours"
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
</div>
</div>
{/* Message */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Collaboration Proposal
</label>
<textarea
name="message"
value={formData.message}
onChange={handleInputChange}
rows={4}
placeholder="Describe your collaboration proposal, roles, and expectations..."
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
</div>
{/* Benefits */}
<div className="bg-green-50 border border-green-200 rounded-md p-4">
<h3 className="font-medium text-green-900 mb-3 flex items-center">
<Target className="h-5 w-5 mr-2" />
Collaboration Benefits
</h3>
<div className="space-y-2 text-sm text-green-800">
<div className="flex items-center">
<CheckCircle className="h-4 w-4 mr-2" />
Shared expertise and resources
</div>
<div className="flex items-center">
<CheckCircle className="h-4 w-4 mr-2" />
Reduced workload and risk
</div>
<div className="flex items-center">
<CheckCircle className="h-4 w-4 mr-2" />
Enhanced case strategy
</div>
<div className="flex items-center">
<CheckCircle className="h-4 w-4 mr-2" />
Better client service
</div>
</div>
</div>
</div>
{/* Submit Button */}
<div className="mt-8 flex justify-end">
<button
type="submit"
disabled={!selectedCase || submitting}
className={`px-6 py-3 rounded-lg transition-colors ${
!selectedCase || submitting
? 'bg-gray-400 cursor-not-allowed'
: 'bg-blue-600 text-white hover:bg-blue-700'
}`}
>
{submitting ? (
<div className="flex items-center">
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white mr-2"></div>
Sending Request...
</div>
) : (
<div className="flex items-center">
Send Collaboration Request
<ArrowRight className="h-4 w-4 ml-2" />
</div>
)}
</button>
</div>
</motion.form>
</div>
</LayoutWithSidebar>
);
};
export default CaseOfferPage;