![]() 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/judge/ |
import React, { useState } from 'react';
import { useRouter } from 'next/router';
import { useRequireRole } from '../../lib/auth-utils';
import { USER_ROLES } from '../../lib/auth-utils';
interface Case {
id: string;
caseNumber: string;
title: string;
parties: string;
status: 'pending' | 'active' | 'under_review' | 'decided' | 'closed';
priority: 'low' | 'medium' | 'high' | 'urgent';
assignedJudge: string;
nextHearing: string;
lastUpdated: string;
caseType: string;
complexity: 'simple' | 'moderate' | 'complex';
}
const JudgeCasesPage: React.FC = () => {
const router = useRouter();
const { session, status, isAuthorized } = useRequireRole([USER_ROLES.JUDGE, USER_ROLES.ADMIN, USER_ROLES.SUPERADMIN, USER_ROLES.SUPERADMIN]);
const [selectedStatus, setSelectedStatus] = useState<string>('all');
const [selectedPriority, setSelectedPriority] = useState<string>('all');
const [searchTerm, setSearchTerm] = useState('');
// Mock data for cases
const [cases] = useState<Case[]>([
{
id: '1',
caseNumber: 'CR-2025-001',
title: 'State v. Johnson - Criminal Case',
parties: 'State of Quebec v. Michael Johnson',
status: 'active',
priority: 'high',
assignedJudge: 'Hon. Marie Dubois',
nextHearing: '2025-07-15 09:00',
lastUpdated: '2025-06-30 14:30',
caseType: 'Criminal',
complexity: 'complex'
},
{
id: '2',
caseNumber: 'CV-2025-045',
title: 'Smith v. Corporation Ltd - Civil Dispute',
parties: 'Jane Smith v. Corporation Ltd',
status: 'under_review',
priority: 'medium',
assignedJudge: 'Hon. Marie Dubois',
nextHearing: '2025-07-10 14:00',
lastUpdated: '2025-06-29 16:45',
caseType: 'Civil',
complexity: 'moderate'
},
{
id: '3',
caseNumber: 'FA-2025-012',
title: 'In re: Estate of Williams',
parties: 'Estate of Robert Williams',
status: 'pending',
priority: 'low',
assignedJudge: 'Hon. Marie Dubois',
nextHearing: '2025-07-20 10:30',
lastUpdated: '2025-06-28 11:20',
caseType: 'Family',
complexity: 'simple'
},
{
id: '4',
caseNumber: 'AD-2025-078',
title: 'Administrative Appeal - License Revocation',
parties: 'Department of Transportation v. Sarah Chen',
status: 'active',
priority: 'urgent',
assignedJudge: 'Hon. Marie Dubois',
nextHearing: '2025-07-05 13:00',
lastUpdated: '2025-06-30 09:15',
caseType: 'Administrative',
complexity: 'complex'
}
]);
const getStatusColor = (status: string) => {
switch (status) {
case 'pending': return 'bg-yellow-100 text-yellow-800';
case 'active': return 'bg-blue-100 text-blue-800';
case 'under_review': return 'bg-purple-100 text-purple-800';
case 'decided': return 'bg-green-100 text-green-800';
case 'closed': return 'bg-gray-100 text-gray-800';
default: return 'bg-gray-100 text-gray-800';
}
};
const getPriorityColor = (priority: string) => {
switch (priority) {
case 'low': return 'bg-green-100 text-green-800';
case 'medium': return 'bg-yellow-100 text-yellow-800';
case 'high': return 'bg-orange-100 text-orange-800';
case 'urgent': return 'bg-red-100 text-red-800';
default: return 'bg-gray-100 text-gray-800';
}
};
const getComplexityColor = (complexity: string) => {
switch (complexity) {
case 'simple': return 'bg-green-100 text-green-800';
case 'moderate': return 'bg-yellow-100 text-yellow-800';
case 'complex': return 'bg-red-100 text-red-800';
default: return 'bg-gray-100 text-gray-800';
}
};
const filteredCases = cases.filter(caseItem => {
const matchesStatus = selectedStatus === 'all' || caseItem.status === selectedStatus;
const matchesPriority = selectedPriority === 'all' || caseItem.priority === selectedPriority;
const matchesSearch = caseItem.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
caseItem.caseNumber.toLowerCase().includes(searchTerm.toLowerCase()) ||
caseItem.parties.toLowerCase().includes(searchTerm.toLowerCase());
return matchesStatus && matchesPriority && matchesSearch;
});
if (status === 'loading') {
return (
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
<div className="text-center">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto"></div>
<p className="mt-4 text-gray-600">Loading case oversight...</p>
</div>
</div>
);
}
return (
<div className="min-h-screen bg-gray-50">
{/* Header */}
<div className="bg-white shadow-sm border-b">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex justify-between items-center py-6">
<div>
<h1 className="text-3xl font-bold text-gray-900">Case Oversight</h1>
<p className="mt-1 text-sm text-gray-500">
Manage and monitor all assigned cases
</p>
</div>
<div className="flex space-x-3">
<button
onClick={() => router.push('/judge/dashboard')}
className="inline-flex items-center px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
>
← Back to Dashboard
</button>
<button className="inline-flex items-center px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
+ New Case Assignment
</button>
</div>
</div>
</div>
</div>
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
{/* Stats Overview */}
<div className="grid grid-cols-1 md:grid-cols-4 gap-6 mb-8">
<div className="bg-white overflow-hidden shadow rounded-lg">
<div className="p-5">
<div className="flex items-center">
<div className="flex-shrink-0">
<div className="w-8 h-8 bg-blue-500 rounded-md flex items-center justify-center">
<svg className="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
</svg>
</div>
</div>
<div className="ml-5 w-0 flex-1">
<dl>
<dt className="text-sm font-medium text-gray-500 truncate">Total Cases</dt>
<dd className="text-lg font-medium text-gray-900">{cases.length}</dd>
</dl>
</div>
</div>
</div>
</div>
<div className="bg-white overflow-hidden shadow rounded-lg">
<div className="p-5">
<div className="flex items-center">
<div className="flex-shrink-0">
<div className="w-8 h-8 bg-green-500 rounded-md flex items-center justify-center">
<svg className="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
</div>
</div>
<div className="ml-5 w-0 flex-1">
<dl>
<dt className="text-sm font-medium text-gray-500 truncate">Active Cases</dt>
<dd className="text-lg font-medium text-gray-900">
{cases.filter(c => c.status === 'active').length}
</dd>
</dl>
</div>
</div>
</div>
</div>
<div className="bg-white overflow-hidden shadow rounded-lg">
<div className="p-5">
<div className="flex items-center">
<div className="flex-shrink-0">
<div className="w-8 h-8 bg-orange-500 rounded-md flex items-center justify-center">
<svg className="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
</div>
</div>
<div className="ml-5 w-0 flex-1">
<dl>
<dt className="text-sm font-medium text-gray-500 truncate">This Week</dt>
<dd className="text-lg font-medium text-gray-900">
{cases.filter(c => new Date(c.nextHearing) <= new Date(Date.now() + 7 * 24 * 60 * 60 * 1000)).length}
</dd>
</dl>
</div>
</div>
</div>
</div>
<div className="bg-white overflow-hidden shadow rounded-lg">
<div className="p-5">
<div className="flex items-center">
<div className="flex-shrink-0">
<div className="w-8 h-8 bg-red-500 rounded-md flex items-center justify-center">
<svg className="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z" />
</svg>
</div>
</div>
<div className="ml-5 w-0 flex-1">
<dl>
<dt className="text-sm font-medium text-gray-500 truncate">Urgent Cases</dt>
<dd className="text-lg font-medium text-gray-900">
{cases.filter(c => c.priority === 'urgent').length}
</dd>
</dl>
</div>
</div>
</div>
</div>
</div>
{/* Filters and Search */}
<div className="bg-white shadow rounded-lg mb-6">
<div className="px-6 py-4 border-b border-gray-200">
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between space-y-4 sm:space-y-0">
<div className="flex-1 max-w-lg">
<label htmlFor="search" className="sr-only">Search cases</label>
<div className="relative">
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<svg className="h-5 w-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
</svg>
</div>
<input
id="search"
name="search"
className="block w-full pl-10 pr-3 py-2 border border-gray-300 rounded-md leading-5 bg-white placeholder-gray-500 focus:outline-none focus:placeholder-gray-400 focus:ring-1 focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
placeholder="Search cases by title, number, or parties..."
type="search"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
</div>
</div>
<div className="flex space-x-4">
<select
value={selectedStatus}
onChange={(e) => setSelectedStatus(e.target.value)}
className="block w-full pl-3 pr-10 py-2 text-base border-gray-300 focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm rounded-md"
>
<option value="all">All Statuses</option>
<option value="pending">Pending</option>
<option value="active">Active</option>
<option value="under_review">Under Review</option>
<option value="decided">Decided</option>
<option value="closed">Closed</option>
</select>
<select
value={selectedPriority}
onChange={(e) => setSelectedPriority(e.target.value)}
className="block w-full pl-3 pr-10 py-2 text-base border-gray-300 focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm rounded-md"
>
<option value="all">All Priorities</option>
<option value="low">Low</option>
<option value="medium">Medium</option>
<option value="high">High</option>
<option value="urgent">Urgent</option>
</select>
</div>
</div>
</div>
</div>
{/* Cases Table */}
<div className="bg-white shadow overflow-hidden sm:rounded-md">
<ul className="divide-y divide-gray-200">
{filteredCases.map((caseItem) => (
<li key={caseItem.id}>
<div className="px-6 py-4 hover:bg-gray-50">
<div className="flex items-center justify-between">
<div className="flex-1 min-w-0">
<div className="flex items-center space-x-3">
<div className="flex-shrink-0">
<div className="w-10 h-10 bg-blue-100 rounded-lg flex items-center justify-center">
<svg className="w-6 h-6 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
</svg>
</div>
</div>
<div className="flex-1 min-w-0">
<div className="flex items-center space-x-2">
<p className="text-sm font-medium text-gray-900 truncate">
{caseItem.title}
</p>
<span className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${getStatusColor(caseItem.status)}`}>
{caseItem.status.replace('_', ' ')}
</span>
<span className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${getPriorityColor(caseItem.priority)}`}>
{caseItem.priority}
</span>
<span className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${getComplexityColor(caseItem.complexity)}`}>
{caseItem.complexity}
</span>
</div>
<p className="text-sm text-gray-500 truncate">
{caseItem.caseNumber} • {caseItem.parties}
</p>
<p className="text-xs text-gray-400 mt-1">
Next hearing: {new Date(caseItem.nextHearing).toLocaleDateString()} at {new Date(caseItem.nextHearing).toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'})}
</p>
</div>
</div>
</div>
<div className="flex items-center space-x-4">
<div className="text-right">
<p className="text-sm text-gray-500">Last updated</p>
<p className="text-sm text-gray-900">{new Date(caseItem.lastUpdated).toLocaleDateString()}</p>
</div>
<div className="flex space-x-2">
<button
onClick={() => router.push(`/judge/cases/${caseItem.id}`)}
className="inline-flex items-center px-3 py-1 border border-transparent text-sm leading-4 font-medium rounded-md text-blue-700 bg-blue-100 hover:bg-blue-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
>
View Details
</button>
<button className="inline-flex items-center px-3 py-1 border border-gray-300 text-sm leading-4 font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
Add Notes
</button>
</div>
</div>
</div>
</div>
</li>
))}
</ul>
</div>
{filteredCases.length === 0 && (
<div className="text-center py-12">
<svg className="mx-auto h-12 w-12 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
</svg>
<h3 className="mt-2 text-sm font-medium text-gray-900">No cases found</h3>
<p className="mt-1 text-sm text-gray-500">
Try adjusting your search or filter criteria.
</p>
</div>
)}
</div>
</div>
);
};
export default JudgeCasesPage;