![]() 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 PerformanceMetric {
id: string;
category: string;
metric: string;
value: number;
target: number;
unit: string;
trend: 'up' | 'down' | 'stable';
period: string;
description: string;
}
interface CaseStatistic {
id: string;
caseType: string;
totalCases: number;
resolvedCases: number;
pendingCases: number;
averageResolutionTime: number;
successRate: number;
period: string;
}
const JudgeMetricsPage: React.FC = () => {
const router = useRouter();
const { session, status, isAuthorized } = useRequireRole([USER_ROLES.JUDGE, USER_ROLES.ADMIN, USER_ROLES.SUPERADMIN, USER_ROLES.SUPERADMIN]);
const [selectedPeriod, setSelectedPeriod] = useState('current_year');
const [selectedCategory, setSelectedCategory] = useState('all');
// Mock data for performance metrics
const [metrics] = useState<PerformanceMetric[]>([
{
id: '1',
category: 'efficiency',
metric: 'Case Resolution Rate',
value: 94.2,
target: 90.0,
unit: '%',
trend: 'up',
period: 'Current Year',
description: 'Percentage of cases resolved within target timeframe'
},
{
id: '2',
category: 'efficiency',
metric: 'Average Case Duration',
value: 45.3,
target: 60.0,
unit: 'days',
trend: 'down',
period: 'Current Year',
description: 'Average time from filing to resolution'
},
{
id: '3',
category: 'quality',
metric: 'Appeal Success Rate',
value: 87.5,
target: 85.0,
unit: '%',
trend: 'up',
period: 'Current Year',
description: 'Percentage of decisions upheld on appeal'
},
{
id: '4',
category: 'quality',
metric: 'Client Satisfaction',
value: 4.6,
target: 4.0,
unit: '/5',
trend: 'up',
period: 'Current Year',
description: 'Average satisfaction rating from court users'
},
{
id: '5',
category: 'productivity',
metric: 'Cases Per Month',
value: 12.8,
target: 10.0,
unit: 'cases',
trend: 'up',
period: 'Current Year',
description: 'Average number of cases handled per month'
},
{
id: '6',
category: 'productivity',
metric: 'Education Credits',
value: 24,
target: 20,
unit: 'credits',
trend: 'up',
period: 'Current Year',
description: 'Continuing education credits earned'
},
{
id: '7',
category: 'timeliness',
metric: 'Decision Turnaround',
value: 3.2,
target: 5.0,
unit: 'days',
trend: 'down',
period: 'Current Year',
description: 'Average days to issue written decisions'
},
{
id: '8',
category: 'timeliness',
metric: 'Hearing Punctuality',
value: 98.7,
target: 95.0,
unit: '%',
trend: 'up',
period: 'Current Year',
description: 'Percentage of hearings started on time'
}
]);
// Mock data for case statistics
const [caseStats] = useState<CaseStatistic[]>([
{
id: '1',
caseType: 'Criminal',
totalCases: 45,
resolvedCases: 42,
pendingCases: 3,
averageResolutionTime: 38.5,
successRate: 93.3,
period: 'Current Year'
},
{
id: '2',
caseType: 'Civil',
totalCases: 32,
resolvedCases: 30,
pendingCases: 2,
averageResolutionTime: 52.1,
successRate: 87.5,
period: 'Current Year'
},
{
id: '3',
caseType: 'Family',
totalCases: 28,
resolvedCases: 25,
pendingCases: 3,
averageResolutionTime: 41.8,
successRate: 89.3,
period: 'Current Year'
},
{
id: '4',
caseType: 'Administrative',
totalCases: 18,
resolvedCases: 17,
pendingCases: 1,
averageResolutionTime: 29.3,
successRate: 94.4,
period: 'Current Year'
}
]);
const getTrendColor = (trend: string) => {
switch (trend) {
case 'up': return 'text-green-600';
case 'down': return 'text-red-600';
case 'stable': return 'text-gray-600';
default: return 'text-gray-600';
}
};
const getTrendIcon = (trend: string) => {
switch (trend) {
case 'up': return '↗';
case 'down': return '↘';
case 'stable': return '→';
default: return '→';
}
};
const getCategoryColor = (category: string) => {
switch (category) {
case 'efficiency': return 'bg-blue-100 text-blue-800';
case 'quality': return 'bg-green-100 text-green-800';
case 'productivity': return 'bg-purple-100 text-purple-800';
case 'timeliness': return 'bg-orange-100 text-orange-800';
default: return 'bg-gray-100 text-gray-800';
}
};
const filteredMetrics = metrics.filter(metric => {
return selectedCategory === 'all' || metric.category === selectedCategory;
});
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 performance metrics...</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">Performance Metrics</h1>
<p className="mt-1 text-sm text-gray-500">
Track judicial performance, case statistics, and professional development
</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">
Export Report
</button>
</div>
</div>
</div>
</div>
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
{/* Overview Stats */}
<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-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">Overall Performance</dt>
<dd className="text-lg font-medium text-gray-900">94.2%</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-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">
{caseStats.reduce((sum, stat) => sum + stat.totalCases, 0)}
</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-purple-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="M13 10V3L4 14h7v7l9-11h-7z" />
</svg>
</div>
</div>
<div className="ml-5 w-0 flex-1">
<dl>
<dt className="text-sm font-medium text-gray-500 truncate">Avg Resolution</dt>
<dd className="text-lg font-medium text-gray-900">
{Math.round(caseStats.reduce((sum, stat) => sum + stat.averageResolutionTime, 0) / caseStats.length)} days
</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">Education Credits</dt>
<dd className="text-lg font-medium text-gray-900">24/20</dd>
</dl>
</div>
</div>
</div>
</div>
</div>
{/* Filters */}
<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">
<h3 className="text-lg font-medium text-gray-900">Performance Metrics</h3>
<div className="flex space-x-4">
<select
value={selectedPeriod}
onChange={(e) => setSelectedPeriod(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="current_year">Current Year</option>
<option value="last_year">Last Year</option>
<option value="last_quarter">Last Quarter</option>
<option value="last_month">Last Month</option>
</select>
<select
value={selectedCategory}
onChange={(e) => setSelectedCategory(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 Categories</option>
<option value="efficiency">Efficiency</option>
<option value="quality">Quality</option>
<option value="productivity">Productivity</option>
<option value="timeliness">Timeliness</option>
</select>
</div>
</div>
</div>
<div className="p-6">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
{filteredMetrics.map((metric) => (
<div key={metric.id} className="bg-white border border-gray-200 rounded-lg p-6">
<div className="flex items-center justify-between mb-4">
<span className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${getCategoryColor(metric.category)}`}>
{metric.category}
</span>
<span className={`text-sm font-medium ${getTrendColor(metric.trend)}`}>
{getTrendIcon(metric.trend)}
</span>
</div>
<h3 className="text-lg font-medium text-gray-900 mb-2">{metric.metric}</h3>
<div className="flex items-baseline">
<span className="text-2xl font-bold text-gray-900">
{metric.value}{metric.unit}
</span>
<span className="ml-2 text-sm text-gray-500">
/ {metric.target}{metric.unit}
</span>
</div>
<p className="text-sm text-gray-600 mt-2">{metric.description}</p>
<div className="mt-4">
<div className="flex justify-between text-sm text-gray-500 mb-1">
<span>Progress</span>
<span>{Math.round((metric.value / metric.target) * 100)}%</span>
</div>
<div className="w-full bg-gray-200 rounded-full h-2">
<div
className={`h-2 rounded-full ${
metric.value >= metric.target ? 'bg-green-500' : 'bg-blue-500'
}`}
style={{ width: `${Math.min((metric.value / metric.target) * 100, 100)}%` }}
></div>
</div>
</div>
</div>
))}
</div>
</div>
</div>
{/* Case Statistics */}
<div className="bg-white shadow rounded-lg">
<div className="px-6 py-4 border-b border-gray-200">
<h3 className="text-lg font-medium text-gray-900">Case Statistics by Type</h3>
</div>
<div className="p-6">
<div className="overflow-hidden shadow ring-1 ring-black ring-opacity-5 md:rounded-lg">
<table className="min-w-full divide-y divide-gray-300">
<thead className="bg-gray-50">
<tr>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Case Type</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Total Cases</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Resolved</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Pending</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Avg Resolution (days)</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Success Rate</th>
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{caseStats.map((stat) => (
<tr key={stat.id}>
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
{stat.caseType}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
{stat.totalCases}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
<span className="text-green-600 font-medium">{stat.resolvedCases}</span>
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
<span className="text-orange-600 font-medium">{stat.pendingCases}</span>
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
{stat.averageResolutionTime.toFixed(1)}
</td>
<td className="px-6 py-4 whitespace-nowrap">
<span className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${
stat.successRate >= 90 ? 'bg-green-100 text-green-800' :
stat.successRate >= 80 ? 'bg-yellow-100 text-yellow-800' :
'bg-red-100 text-red-800'
}`}>
{stat.successRate}%
</span>
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
</div>
{/* Performance Insights */}
<div className="mt-6 bg-white shadow rounded-lg">
<div className="px-6 py-4 border-b border-gray-200">
<h3 className="text-lg font-medium text-gray-900">Performance Insights</h3>
</div>
<div className="p-6">
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<h4 className="text-sm font-medium text-gray-900 mb-3">Strengths</h4>
<ul className="space-y-2">
<li className="flex items-start">
<span className="text-green-500 mr-2">✓</span>
<span className="text-sm text-gray-600">Excellent case resolution rate (94.2%)</span>
</li>
<li className="flex items-start">
<span className="text-green-500 mr-2">✓</span>
<span className="text-sm text-gray-600">High client satisfaction rating (4.6/5)</span>
</li>
<li className="flex items-start">
<span className="text-green-500 mr-2">✓</span>
<span className="text-sm text-gray-600">Strong appeal success rate (87.5%)</span>
</li>
<li className="flex items-start">
<span className="text-green-500 mr-2">✓</span>
<span className="text-sm text-gray-600">Exceeded education credit requirements</span>
</li>
</ul>
</div>
<div>
<h4 className="text-sm font-medium text-gray-900 mb-3">Areas for Improvement</h4>
<ul className="space-y-2">
<li className="flex items-start">
<span className="text-yellow-500 mr-2">⚠</span>
<span className="text-sm text-gray-600">Civil cases taking longer than target (52.1 days)</span>
</li>
<li className="flex items-start">
<span className="text-yellow-500 mr-2">⚠</span>
<span className="text-sm text-gray-600">Family law success rate below 90% (89.3%)</span>
</li>
<li className="flex items-start">
<span className="text-blue-500 mr-2">ℹ</span>
<span className="text-sm text-gray-600">Consider additional training in family law</span>
</li>
<li className="flex items-start">
<span className="text-blue-500 mr-2">ℹ</span>
<span className="text-sm text-gray-600">Monitor civil case backlog management</span>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
);
};
export default JudgeMetricsPage;