![]() 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.quebec/private_html/src/components/ |
import React, { useState, useEffect } from 'react';
import { useSession } from 'next-auth/react';
import Link from 'next/link';
import {
Eye,
Edit,
FileText,
Calendar,
User,
MapPin,
Clock,
Search,
Filter,
Plus
} from 'lucide-react';
interface CaseListProps {
title?: string;
subtitle?: string;
showCreateButton?: boolean;
createButtonText?: string;
createButtonHref?: string;
role?: string;
userId?: string;
className?: string;
}
interface LegalCase {
id: string;
title: string;
description?: string;
status: string;
jurisdiction?: string;
court?: string;
priority?: string;
createdAt: string;
updatedAt: string;
leadLawyerId?: string;
createdBy?: string;
clientId?: string;
_count?: {
registrations?: number;
offers?: number;
comments?: number;
};
leadLawyer?: {
name?: string;
email?: string;
};
client?: {
name?: string;
email?: string;
};
}
const CaseList: React.FC<CaseListProps> = ({
title = "Cases",
subtitle = "Manage your cases",
showCreateButton = false,
createButtonText = "Create Case",
createButtonHref = "/admin/cases/new",
role,
userId,
className = ""
}) => {
const { data: session } = useSession();
const [cases, setCases] = useState<LegalCase[]>([]);
const [loading, setLoading] = useState(true);
const [searchQuery, setSearchQuery] = useState('');
const [statusFilter, setStatusFilter] = useState('all');
const isLawyer = session?.user?.role === 'LAWYER';
const isVerified = session?.user?.isVerifiedLawyer || session?.user?.verificationStatus === 'VERIFIED_BARREAU';
useEffect(() => {
fetchCases();
}, [role, userId]);
const fetchCases = async () => {
try {
setLoading(true);
let url = '/api/cases';
// Add role-specific filtering
if (role === 'LAWYER' && userId) {
url = `/api/cases?role=lawyer&userId=${userId}
} else if (role === 'CLIENT' && userId) {
url = `/api/cases?role=client&userId=${userId}
} else if (role === 'ADMIN' || role === 'SUPERADMIN') {
url = '/api/cases?role=admin';
}
const response = await fetch(url);
if (response.ok) {
const data = await response.json();
setCases(data.cases || data || []);
}
} catch (error) {
} finally {
setLoading(false);
}
};
const filteredCases = cases.filter(case_ => {
const matchesSearch = case_.title.toLowerCase().includes(searchQuery.toLowerCase()) ||
case_.description?.toLowerCase().includes(searchQuery.toLowerCase()) ||
case_.jurisdiction?.toLowerCase().includes(searchQuery.toLowerCase());
const matchesStatus = statusFilter === 'all' || case_.status === statusFilter;
return matchesSearch && matchesStatus;
});
const getStatusColor = (status: string) => {
switch (status.toLowerCase()) {
case 'active': return 'bg-green-100 text-green-800';
case 'pending': return 'bg-yellow-100 text-yellow-800';
case 'closed': return 'bg-gray-100 text-gray-800';
case 'draft': return 'bg-blue-100 text-blue-800';
default: return 'bg-gray-100 text-gray-800';
}
};
const getPriorityColor = (priority: string) => {
switch (priority?.toLowerCase()) {
case 'high': return 'bg-red-100 text-red-800';
case 'medium': return 'bg-yellow-100 text-yellow-800';
case 'low': return 'bg-green-100 text-green-800';
default: return 'bg-gray-100 text-gray-800';
}
};
if (loading) {
return (
<div className={`bg-white rounded-lg shadow-sm border ${className}
<div className="p-6">
<div className="animate-pulse">
<div className="h-4 bg-gray-200 rounded w-1/4 mb-4"></div>
<div className="space-y-3">
{[1, 2, 3].map((i) => (
<div key={i} className="h-16 bg-gray-200 rounded"></div>
))}
</div>
</div>
</div>
</div>
);
}
return (
<div className={`bg-white rounded-lg shadow-sm border ${className}
{/* Verification Banner for Unverified Lawyers */}
{isLawyer && !isVerified && (
<div className="p-4 bg-yellow-100 border-l-4 border-yellow-500 text-yellow-800 rounded-t">
<strong>Account Not Verified:</strong> You must complete Barreau verification to create or manage cases. Sensitive actions are disabled until you are verified.
</div>
)}
{/* Header */}
<div className="p-6 border-b border-gray-200">
<div className="flex items-center justify-between">
<div>
<h1 className="text-2xl font-bold text-gray-900">{title}</h1>
<p className="text-gray-600 mt-1">{subtitle}</p>
</div>
{showCreateButton && (!isLawyer || isVerified) && (
<Link
href={createButtonHref}
className="inline-flex items-center px-4 py-2 bg-blue-600 text-white text-sm font-medium rounded-lg hover:bg-blue-700 transition-colors"
>
<Plus className="h-4 w-4 mr-2" />
{createButtonText}
</Link>
)}
{showCreateButton && isLawyer && !isVerified && (
<button
className="inline-flex items-center px-4 py-2 bg-gray-300 text-gray-500 text-sm font-medium rounded-lg cursor-not-allowed"
disabled
title="You must be a verified lawyer to create a new case."
>
<Plus className="h-4 w-4 mr-2" />
{createButtonText}
</button>
)}
</div>
</div>
{/* Filters */}
<div className="p-6 border-b border-gray-200">
<div className="flex flex-col sm:flex-row gap-4">
<div className="flex-1">
<div className="relative">
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-gray-400" />
<input
type="text"
placeholder="Search cases..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
/>
</div>
</div>
<div className="flex gap-2">
<select
value={statusFilter}
onChange={(e) => setStatusFilter(e.target.value)}
className="px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
>
<option value="all">All Status</option>
<option value="active">Active</option>
<option value="pending">Pending</option>
<option value="closed">Closed</option>
<option value="draft">Draft</option>
</select>
</div>
</div>
</div>
{/* Cases List */}
<div className="divide-y divide-gray-200">
{filteredCases.length === 0 ? (
<div className="p-6 text-center">
<FileText className="h-12 w-12 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">
{searchQuery || statusFilter !== 'all'
? 'Try adjusting your search or filters'
: 'Get started by creating your first case'
}
</p>
</div>
) : (
filteredCases.map((case_) => (
<div key={case_.id} className="p-6 hover:bg-gray-50 transition-colors">
<div className="flex items-start justify-between">
<div className="flex-1">
<div className="flex items-center gap-3 mb-2">
<h3 className="text-lg font-semibold text-gray-900">
{case_.title}
</h3>
<span className={`px-2 py-1 text-xs font-medium rounded-full ${getStatusColor(case_.status)}
{case_.status}
</span>
{case_.priority && (
<span className={`px-2 py-1 text-xs font-medium rounded-full ${getPriorityColor(case_.priority)}
{case_.priority}
</span>
)}
</div>
{case_.description && (
<p className="text-gray-600 mb-3 line-clamp-2">
{case_.description}
</p>
)}
<div className="flex flex-wrap items-center gap-4 text-sm text-gray-500">
{case_.jurisdiction && (
<div className="flex items-center gap-1">
<MapPin className="h-4 w-4" />
<span>{case_.jurisdiction}</span>
</div>
)}
{case_.court && (
<div className="flex items-center gap-1">
<FileText className="h-4 w-4" />
<span>{case_.court}</span>
</div>
)}
<div className="flex items-center gap-1">
<Calendar className="h-4 w-4" />
<span>Created {new Date(case_.createdAt).toLocaleDateString()}</span>
</div>
{case_.leadLawyer?.name && (
<div className="flex items-center gap-1">
<User className="h-4 w-4" />
<span>Lead: {case_.leadLawyer.name}</span>
</div>
)}
{case_._count?.registrations && (
<div className="flex items-center gap-1">
<span>📝 {case_._count.registrations} applications</span>
</div>
)}
</div>
</div>
<div className="flex items-center gap-2 ml-4">
<Link
href={`/public/cases/${case_.id}
className="inline-flex items-center px-3 py-1 text-sm text-blue-600 hover:text-blue-800 hover:bg-blue-50 rounded-md transition-colors"
>
<Eye className="h-4 w-4 mr-1" />
View
</Link>
{(role === 'ADMIN' || role === 'SUPERADMIN' ||
(role === 'LAWYER' && case_.leadLawyerId === userId) ||
(role === 'CLIENT' && case_.createdBy === userId)) && (
<Link
href={`/admin/cases/${case_.id}/edit
className="inline-flex items-center px-3 py-1 text-sm text-gray-600 hover:text-gray-800 hover:bg-gray-50 rounded-md transition-colors"
>
<Edit className="h-4 w-4 mr-1" />
Edit
</Link>
)}
</div>
</div>
</div>
))
)}
</div>
</div>
);
};
export default CaseList;