![]() 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/lawyer/ |
import React, { useEffect, useState } from 'react';
import { useSession } from 'next-auth/react';
import { useRouter } from 'next/router';
import LayoutWithSidebar from '@/components/LayoutWithSidebar';
import { useRequireRole, USER_ROLES } from '@/lib/auth-utils';
import { toast } from 'react-hot-toast';
import CaseWidget from '@/components/CaseWidget';
import PaymentWidget from '@/components/payments/PaymentWidget';
import DashboardModal from '@/components/DashboardModal';
import { useDashboardModal } from '@/hooks/useDashboardModal';
import {
Briefcase,
Users,
Calendar,
BarChart2,
MessageSquare,
User,
FileText,
DollarSign,
Clock,
CheckCircle,
AlertTriangle,
TrendingUp,
Plus,
Phone,
Video,
ArrowRight
} from 'lucide-react';
const navCards = [
{
title: 'My Cases',
description: 'View and manage all your assigned cases.',
icon: <Briefcase className="h-8 w-8 text-blue-600" />,
route: '/lawyer/cases',
color: 'from-blue-500 to-blue-600',
},
{
title: 'Consultations',
description: 'Manage your legal consultations and bookings.',
icon: <MessageSquare className="h-8 w-8 text-green-600" />,
route: '/lawyer/consultations',
color: 'from-green-500 to-green-600',
},
{
title: 'My Team',
description: 'Collaborate with your legal team and assign tasks.',
icon: <Users className="h-8 w-8 text-purple-600" />,
route: '/lawyer/team',
color: 'from-purple-500 to-purple-600',
},
{
title: 'Calendar',
description: 'See all your hearings, meetings, and deadlines.',
icon: <Calendar className="h-8 w-8 text-orange-600" />,
route: '/lawyer/calendar',
color: 'from-orange-500 to-orange-600',
},
{
title: 'Analytics',
description: 'Track your performance and case outcomes.',
icon: <BarChart2 className="h-8 w-8 text-pink-600" />,
route: '/lawyer/analytics',
color: 'from-pink-500 to-pink-600',
},
{
title: 'Clients',
description: 'Manage your clients and communications.',
icon: <User className="h-8 w-8 text-teal-600" />,
route: '/lawyer/clients',
color: 'from-teal-500 to-teal-600',
},
{
title: 'Profile',
description: 'Manage your professional profile and credentials.',
icon: <User className="h-8 w-8 text-indigo-600" />,
route: '/lawyer/profile',
color: 'from-indigo-500 to-indigo-600',
},
{
title: 'My Applications',
description: 'View and manage your submitted applications.',
icon: <FileText className="h-8 w-8 text-cyan-600" />,
route: '/lawyer/applications',
color: 'from-cyan-500 to-cyan-600',
},
];
const LawyerDashboard: React.FC = () => {
const { data: session, status } = useSession();
const router = useRouter();
const [loading, setLoading] = useState(true);
const [stats, setStats] = useState<{
cases: number;
consultations: number;
team: number;
upcoming: number;
clients: number;
billing: number;
caseOutcomes: number;
clientSatisfaction: number;
pendingDocuments: number;
totalCases: number;
totalConsultations: number;
totalClients: number;
caseSuccessRate: number;
consultationCompletionRate: number;
taskCompletionRate: number;
recentActivity: number;
recentCases: number;
recentConsultations: number;
recentTasks: number;
upcomingEvents: any[];
} | null>(null);
const { isModalOpen, currentRoute, modalTitle, openModal, closeModal } = useDashboardModal();
// Role-based access control - allow LAWYER, ADMIN, SUPERADMIN to access lawyer dashboard
const { isAuthorized } = useRequireRole([
USER_ROLES.LAWYER,
USER_ROLES.ADMIN,
USER_ROLES.SUPERADMIN, USER_ROLES.SUPERADMIN
], '/');
// Determine verification status
const isVerified = session?.user?.isVerifiedLawyer || session?.user?.verificationStatus === 'VERIFIED_BARREAU';
useEffect(() => {
if (status === 'loading') return;
if (!session || !isAuthorized) {
router.push('/');
return;
}
fetchStats();
}, [session, status, router, isAuthorized]);
const fetchStats = async () => {
try {
const response = await fetch('/api/lawyer/dashboard-stats');
if (response.ok) {
const data = await response.json();
setStats({
cases: data.stats.cases || 0,
consultations: data.stats.consultations || 0,
team: data.stats.team || 0,
upcoming: data.stats.upcoming || 0,
clients: data.stats.clients || 0,
billing: data.stats.billing || 0,
caseOutcomes: data.stats.completedCases || 0,
clientSatisfaction: data.stats.clientSatisfaction || 0,
pendingDocuments: data.stats.pendingTasks || 0,
totalCases: data.stats.totalCases || 0,
totalConsultations: data.stats.totalConsultations || 0,
totalClients: data.stats.totalClients || 0,
caseSuccessRate: data.stats.caseSuccessRate || 0,
consultationCompletionRate: data.stats.consultationCompletionRate || 0,
taskCompletionRate: data.stats.taskCompletionRate || 0,
recentActivity: data.stats.recentActivity || 0,
recentCases: data.stats.recentCases || 0,
recentConsultations: data.stats.recentConsultations || 0,
recentTasks: data.stats.recentTasks || 0,
upcomingEvents: data.stats.upcomingEvents || []
});
} else {
toast.error('Failed to load stats');
}
} catch (e) {
console.error('Error fetching stats:', e);
toast.error('Failed to load stats');
} finally {
setLoading(false);
}
};
const handleCardClick = (route: string, title: string) => {
openModal(route, title);
};
if (status === 'loading' || loading) {
return (
<LayoutWithSidebar>
<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>
</div>
</LayoutWithSidebar>
);
}
return (
<LayoutWithSidebar>
<div className="max-w-7xl mx-auto px-4 py-8">
{/* Verification Banner */}
{!isVerified && (
<div className="mb-6 p-4 bg-yellow-100 border-l-4 border-yellow-500 text-yellow-800 rounded">
<div className="flex items-center mb-2">
<AlertTriangle className="h-6 w-6 mr-2 text-yellow-600" />
<span className="font-semibold">Account Not Verified</span>
</div>
<div>
You must complete Barreau verification to access all features. Sensitive actions (accepting cases, messaging, uploading files, etc.) are disabled until you are verified.<br />
<a href="/register-verified" className="text-blue-700 underline">Click here to verify now or upload proof for manual review.</a>
</div>
</div>
)}
{/* Welcome Section */}
<div className="mb-8">
<h1 className="text-3xl font-bold text-gray-900 mb-2">
Welcome, {session?.user?.name || 'Lawyer'}
</h1>
<p className="text-gray-600">
This is your professional command center. Manage your cases, consultations, team, and moreāall in one place.
</p>
</div>
{/* Enhanced Stats */}
<div className="grid grid-cols-2 md:grid-cols-5 lg:grid-cols-9 gap-4 mb-10">
<div className="bg-gradient-to-r from-blue-500 to-blue-600 text-white rounded-lg p-4 shadow">
<div className="text-xs">Active Cases</div>
<div className="text-2xl font-bold">{stats?.cases ?? '-'}</div>
</div>
<div className="bg-gradient-to-r from-green-500 to-green-600 text-white rounded-lg p-4 shadow">
<div className="text-xs">Consultations</div>
<div className="text-2xl font-bold">{stats?.consultations ?? '-'}</div>
</div>
<div className="bg-gradient-to-r from-purple-500 to-purple-600 text-white rounded-lg p-4 shadow">
<div className="text-xs">Team Members</div>
<div className="text-2xl font-bold">{stats?.team ?? '-'}</div>
</div>
<div className="bg-gradient-to-r from-orange-500 to-orange-600 text-white rounded-lg p-4 shadow">
<div className="text-xs">Upcoming</div>
<div className="text-2xl font-bold">{stats?.upcoming ?? '-'}</div>
</div>
<div className="bg-gradient-to-r from-teal-500 to-teal-600 text-white rounded-lg p-4 shadow">
<div className="text-xs">Clients</div>
<div className="text-2xl font-bold">{stats?.clients ?? '-'}</div>
</div>
<div className="bg-gradient-to-r from-emerald-500 to-emerald-600 text-white rounded-lg p-4 shadow">
<div className="text-xs">Billing ($)</div>
<div className="text-2xl font-bold">${((stats?.billing || 0) / 1000).toFixed(0)}k</div>
</div>
<div className="bg-gradient-to-r from-indigo-500 to-indigo-600 text-white rounded-lg p-4 shadow">
<div className="text-xs">Case Outcomes</div>
<div className="text-2xl font-bold">{stats?.caseOutcomes ?? '-'}</div>
</div>
<div className="bg-gradient-to-r from-yellow-500 to-yellow-600 text-white rounded-lg p-4 shadow">
<div className="text-xs">Satisfaction</div>
<div className="text-2xl font-bold">{stats?.clientSatisfaction ?? '-'}/5</div>
</div>
<div className="bg-gradient-to-r from-red-500 to-red-600 text-white rounded-lg p-4 shadow">
<div className="text-xs">Pending Docs</div>
<div className="text-2xl font-bold">{stats?.pendingDocuments ?? '-'}</div>
</div>
</div>
{/* Quick Actions */}
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-6 mb-8">
<h2 className="text-xl font-semibold text-gray-900 mb-4">Quick Actions</h2>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<button
onClick={() => handleCardClick('/lawyer/cases', 'My Cases')}
className={`flex items-center p-4 border border-gray-200 rounded-lg transition-colors ${!isVerified ? 'opacity-50 cursor-not-allowed' : 'hover:bg-gray-50'}`}
disabled={!isVerified}
title={!isVerified ? 'You must be verified to access this feature.' : ''}
>
<FileText className="h-6 w-6 text-blue-600 mr-3" />
<div className="text-left">
<div className="font-medium text-gray-900">My Cases</div>
<div className="text-sm text-gray-500">View and manage your cases</div>
</div>
</button>
<button
onClick={() => router.push('/hire/new-case')}
className={`flex items-center p-4 border border-gray-200 rounded-lg transition-colors ${!isVerified ? 'opacity-50 cursor-not-allowed' : 'hover:bg-gray-50'}`}
disabled={!isVerified}
title={!isVerified ? 'You must be verified to access this feature.' : ''}
>
<Plus className="h-6 w-6 text-green-600 mr-3" />
<div className="text-left">
<div className="font-medium text-gray-900">New Case</div>
<div className="text-sm text-gray-500">Create a new case</div>
</div>
</button>
<button
onClick={() => handleCardClick('/lawyer/consultations', 'Consultations')}
className={`flex items-center p-4 border border-gray-200 rounded-lg transition-colors ${!isVerified ? 'opacity-50 cursor-not-allowed' : 'hover:bg-gray-50'}`}
disabled={!isVerified}
title={!isVerified ? 'You must be verified to access this feature.' : ''}
>
<MessageSquare className="h-6 w-6 text-purple-600 mr-3" />
<div className="text-left">
<div className="font-medium text-gray-900">Consultations</div>
<div className="text-sm text-gray-500">Manage consultations</div>
</div>
</button>
</div>
</div>
{/* Payment Widget */}
<div className="mb-8">
<PaymentWidget
userId={session?.user?.id || ''}
userRole={session?.user?.role || 'LAWYER'}
compact={false}
/>
</div>
{/* Upcoming Events and Recent Activity */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8 mb-8">
{/* Upcoming Events */}
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
<h2 className="text-xl font-semibold text-gray-900 mb-4 flex items-center">
<Calendar className="h-5 w-5 mr-2 text-orange-600" />
Upcoming Events
</h2>
{stats?.upcomingEvents && stats.upcomingEvents.length > 0 ? (
<div className="space-y-3">
{stats.upcomingEvents.slice(0, 5).map((event: any, index: number) => (
<div key={index} className="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
<div>
<p className="font-medium text-gray-900">{event.clientName}</p>
<p className="text-sm text-gray-600">
{new Date(event.date).toLocaleDateString()} at {event.time}
</p>
</div>
<span className="text-xs bg-orange-100 text-orange-800 px-2 py-1 rounded-full">
{event.type}
</span>
</div>
))}
</div>
) : (
<p className="text-gray-600">No upcoming events</p>
)}
</div>
{/* Recent Activity */}
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
<h2 className="text-xl font-semibold text-gray-900 mb-4 flex items-center">
<TrendingUp className="h-5 w-5 mr-2 text-green-600" />
Recent Activity
</h2>
<div className="space-y-4">
<div className="flex items-center justify-between">
<span className="text-gray-600">Recent Cases</span>
<span className="font-semibold text-gray-900">{stats?.recentCases || 0}</span>
</div>
<div className="flex items-center justify-between">
<span className="text-gray-600">Recent Consultations</span>
<span className="font-semibold text-gray-900">{stats?.recentConsultations || 0}</span>
</div>
<div className="flex items-center justify-between">
<span className="text-gray-600">Recent Tasks</span>
<span className="font-semibold text-gray-900">{stats?.recentTasks || 0}</span>
</div>
<div className="pt-2 border-t border-gray-200">
<div className="flex items-center justify-between">
<span className="text-gray-600">Total Activity (30 days)</span>
<span className="font-semibold text-green-600">{stats?.recentActivity || 0}</span>
</div>
</div>
</div>
</div>
</div>
{/* Performance Metrics */}
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-6 mb-8">
<h2 className="text-xl font-semibold text-gray-900 mb-4">Performance Metrics</h2>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
<div className="text-center">
<div className="text-3xl font-bold text-blue-600 mb-2">{stats?.caseSuccessRate || 0}%</div>
<div className="text-sm text-gray-600">Case Success Rate</div>
</div>
<div className="text-center">
<div className="text-3xl font-bold text-green-600 mb-2">{stats?.consultationCompletionRate || 0}%</div>
<div className="text-sm text-gray-600">Consultation Completion</div>
</div>
<div className="text-center">
<div className="text-3xl font-bold text-purple-600 mb-2">{stats?.taskCompletionRate || 0}%</div>
<div className="text-sm text-gray-600">Task Completion</div>
</div>
</div>
</div>
{/* Navigation Cards */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
{navCards.map((card, index) => (
<button
key={index}
onClick={() => handleCardClick(card.route, card.title)}
className="group relative bg-white rounded-xl shadow-sm border border-gray-200 p-6 hover:shadow-lg transition-all duration-200 hover:scale-105"
>
<div className="flex items-center justify-between mb-4">
<div className={`p-3 rounded-lg bg-gradient-to-r ${card.color}`}>
{card.icon}
</div>
<div className="opacity-0 group-hover:opacity-100 transition-opacity">
<ArrowRight className="h-5 w-5 text-gray-400" />
</div>
</div>
<h3 className="text-lg font-semibold text-gray-900 mb-2">{card.title}</h3>
<p className="text-sm text-gray-600">{card.description}</p>
</button>
))}
</div>
{/* Dashboard Modal */}
<DashboardModal
isOpen={isModalOpen}
onClose={closeModal}
route={currentRoute}
title={modalTitle}
/>
</div>
</LayoutWithSidebar>
);
};
export default LawyerDashboard;