![]() 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/ |
'use client';
import React, { useState, useEffect } from 'react';
import { useRouter } from 'next/router';
import { useSession } from 'next-auth/react';
import {
Calendar,
Clock,
Users,
Plus,
Edit,
Trash2,
Bell,
ExternalLink,
Filter,
Search,
Download,
Upload,
Settings,
User,
MapPin,
Phone,
Mail,
Video,
FileText,
AlertCircle,
CheckCircle,
XCircle,
MoreVertical,
ChevronLeft,
ChevronRight
} from 'lucide-react';
import { format, startOfWeek, endOfWeek, eachDayOfInterval, isSameDay, parseISO, addDays, subDays, startOfMonth, endOfMonth, eachDayOfInterval as eachDay, addMonths, subMonths, isSameMonth, getDay } from 'date-fns';
import { fr, enUS } from 'date-fns/locale';
import { toast } from 'react-hot-toast';
interface CalendarEvent {
id: string;
title: string;
description?: string;
startTime: string;
endTime: string;
type: 'consultation' | 'court_hearing' | 'client_meeting' | 'team_meeting' | 'deadline' | 'reminder';
status: 'scheduled' | 'confirmed' | 'completed' | 'cancelled';
participants: string[];
location?: string;
meetingLink?: string;
caseId?: string;
clientId?: string;
clientName?: string;
clientEmail?: string;
reminderSent: boolean;
createdAt: string;
updatedAt: string;
}
interface CalendarView {
id: string;
name: string;
type: 'day' | 'week' | 'month' | 'agenda';
color: string;
}
// Helper functions moved outside component for reuse
const getEventTypeColor = (type: string) => {
const colors = {
consultation: 'bg-blue-100 text-blue-800 border-blue-200',
court_hearing: 'bg-red-100 text-red-800 border-red-200',
client_meeting: 'bg-green-100 text-green-800 border-green-200',
team_meeting: 'bg-purple-100 text-purple-800 border-purple-200',
deadline: 'bg-orange-100 text-orange-800 border-orange-200',
reminder: 'bg-gray-100 text-gray-800 border-gray-200'
};
return colors[type as keyof typeof colors] || colors.reminder;
};
const getStatusIcon = (status: string) => {
switch (status) {
case 'confirmed': return <CheckCircle className="w-4 h-4 text-green-500" />;
case 'completed': return <CheckCircle className="w-4 h-4 text-blue-500" />;
case 'cancelled': return <XCircle className="w-4 h-4 text-red-500" />;
default: return <Clock className="w-4 h-4 text-yellow-500" />;
}
};
const CalendarPage: React.FC = () => {
const { data: session, status } = useSession();
const router = useRouter();
const { locale } = router;
const t = locale === 'fr' ? require('../../dictionaries/fr.json') : require('../../dictionaries/en.json');
const [events, setEvents] = useState<CalendarEvent[]>([]);
const [loading, setLoading] = useState(true);
const [currentDate, setCurrentDate] = useState(new Date());
const [selectedView, setSelectedView] = useState<'day' | 'week' | 'month' | 'agenda'>('month'); // Default to month view
const [showEventModal, setShowEventModal] = useState(false);
const [selectedEvent, setSelectedEvent] = useState<CalendarEvent | null>(null);
const [selectedDate, setSelectedDate] = useState<Date | null>(null);
const [showDayModal, setShowDayModal] = useState(false);
const [filters, setFilters] = useState({
type: 'all',
status: 'all',
participant: 'all'
});
const [searchTerm, setSearchTerm] = useState('');
// Calendar views
const calendarViews: CalendarView[] = [
{ id: 'month', name: t.calendar.month, type: 'month', color: '#F59E0B' },
{ id: 'week', name: t.calendar.week, type: 'week', color: '#10B981' },
{ id: 'day', name: t.calendar.day, type: 'day', color: '#3B82F6' },
{ id: 'agenda', name: t.calendar.agenda, type: 'agenda', color: '#EF4444' }
];
useEffect(() => {
if (status === 'loading') return;
if (!session?.user) {
router.push('/auth/login');
return;
}
fetchEvents();
}, [session, status, currentDate, selectedView, filters]);
const fetchEvents = async () => {
try {
setLoading(true);
// Enhanced API call with better error handling and real data integration
const params = new URLSearchParams({
date: currentDate.toISOString(),
view: selectedView,
filters: JSON.stringify(filters),
lawyerId: session?.user?.id || ''
});
const response = await fetch(`/api/lawyer/calendar?${params}`);
if (response.ok) {
const data = await response.json();
setEvents(data.events || []);
} else {
const errorData = await response.json();
console.error('Failed to fetch calendar events:', errorData.error);
// Show user-friendly error message
toast.error('Failed to load calendar events. Please try again.');
}
} catch (error) {
console.error('Error fetching calendar events:', error);
toast.error('Network error. Please check your connection.');
} finally {
setLoading(false);
}
};
const handleCreateEvent = () => {
setSelectedEvent(null);
setShowEventModal(true);
};
const handleEditEvent = (event: CalendarEvent) => {
setSelectedEvent(event);
setShowEventModal(true);
};
const handleDeleteEvent = async (eventId: string) => {
if (!confirm(t.calendar.confirmDelete)) return;
try {
const response = await fetch(`/api/lawyer/calendar/${eventId}`, {
method: 'DELETE'
});
if (response.ok) {
setEvents(events.filter(e => e.id !== eventId));
toast.success('Event deleted successfully');
} else {
console.error('Failed to delete event');
toast.error('Failed to delete event');
}
} catch (error) {
console.error('Error deleting event:', error);
toast.error('Failed to delete event');
}
};
const handleSaveEvent = async (eventData: Partial<CalendarEvent>) => {
try {
const url = selectedEvent
? `/api/lawyer/calendar/${selectedEvent.id}`
: '/api/lawyer/calendar';
const method = selectedEvent ? 'PUT' : 'POST';
const response = await fetch(url, {
method,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(eventData)
});
if (response.ok) {
setShowEventModal(false);
fetchEvents();
toast.success(selectedEvent ? 'Event updated successfully' : 'Event created successfully');
} else {
console.error('Failed to save event');
toast.error('Failed to save event');
}
} catch (error) {
console.error('Error saving event:', error);
toast.error('Failed to save event');
}
};
// Quick actions for calendar
const quickActions = [
{ label: 'Today', action: () => setCurrentDate(new Date()) },
{ label: 'Previous Week', action: () => setCurrentDate(subDays(currentDate, 7)) },
{ label: 'Next Week', action: () => setCurrentDate(addDays(currentDate, 7)) },
{ label: 'Refresh', action: fetchEvents }
];
// MONTH VIEW - This is what lawyers actually need!
const renderMonthView = () => {
const start = startOfMonth(currentDate);
const end = endOfMonth(currentDate);
const startDate = startOfWeek(start, { weekStartsOn: 1 });
const endDate = endOfWeek(end, { weekStartsOn: 1 });
const days = eachDay({ start: startDate, end: endDate });
const weekDays = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
return (
<div className="bg-white rounded-lg shadow-sm border border-gray-200 overflow-hidden">
{/* Month header */}
<div className="bg-gray-50 px-6 py-4 border-b border-gray-200">
<div className="flex items-center justify-between">
<h2 className="text-xl font-semibold text-gray-900">
{format(currentDate, 'MMMM yyyy', { locale: locale === 'fr' ? fr : enUS })}
</h2>
<div className="flex items-center gap-2">
<button
onClick={() => setCurrentDate(subMonths(currentDate, 1))}
className="p-2 text-gray-600 hover:bg-gray-100 rounded-full"
>
<ChevronLeft className="w-5 h-5" />
</button>
<button
onClick={() => setCurrentDate(new Date())}
className="px-3 py-1 text-sm bg-blue-600 text-white rounded-md hover:bg-blue-700"
>
Today
</button>
<button
onClick={() => setCurrentDate(addMonths(currentDate, 1))}
className="p-2 text-gray-600 hover:bg-gray-100 rounded-full"
>
<ChevronRight className="w-5 h-5" />
</button>
</div>
</div>
</div>
{/* Week days header */}
<div className="grid grid-cols-7 bg-gray-50 border-b border-gray-200">
{weekDays.map(day => (
<div key={day} className="px-3 py-3 text-center text-sm font-medium text-gray-700">
{day}
</div>
))}
</div>
{/* Calendar grid */}
<div className="grid grid-cols-7">
{days.map((day, index) => {
const dayEvents = events.filter(event =>
isSameDay(parseISO(event.startTime), day)
);
const isCurrentMonth = isSameMonth(day, currentDate);
const isToday = isSameDay(day, new Date());
return (
<div
key={index}
className={`min-h-[120px] border-r border-b border-gray-200 p-2 cursor-pointer hover:bg-gray-50 transition-colors ${
!isCurrentMonth ? 'bg-gray-50' : ''
}`}
onClick={() => {
setSelectedDate(day);
setShowDayModal(true);
}}
>
<div className={`text-sm font-medium mb-1 ${
isToday
? 'bg-blue-600 text-white rounded-full w-6 h-6 flex items-center justify-center'
: isCurrentMonth
? 'text-gray-900'
: 'text-gray-400'
}`}>
{format(day, 'd')}
</div>
{/* Events for this day */}
<div className="space-y-1">
{dayEvents.slice(0, 3).map(event => (
<div
key={event.id}
className={`text-xs p-1 rounded truncate cursor-pointer ${getEventTypeColor(event.type)}`}
onClick={(e) => {
e.stopPropagation();
handleEditEvent(event);
}}
>
{event.title}
</div>
))}
{dayEvents.length > 3 && (
<div className="text-xs text-gray-500 text-center">
+{dayEvents.length - 3} more
</div>
)}
</div>
</div>
);
})}
</div>
</div>
);
};
const renderWeekView = () => {
const start = startOfWeek(currentDate, { weekStartsOn: 1 });
const end = endOfWeek(currentDate, { weekStartsOn: 1 });
const days = eachDayOfInterval({ start, end });
return (
<div className="grid grid-cols-8 gap-1">
{/* Time column */}
<div className="col-span-1">
<div className="h-12"></div>
{Array.from({ length: 24 }, (_, i) => (
<div key={i} className="h-16 border-r border-gray-200 text-xs text-gray-500 pr-2 text-right">
{i === 0 ? '00:00' : `${i.toString().padStart(2, '0')}:00`}
</div>
))}
</div>
{/* Days columns */}
{days.map((day, dayIndex) => (
<div key={dayIndex} className="col-span-1">
<div className="h-12 border-b border-gray-200 text-center text-sm font-medium">
<div>{format(day, 'EEE', { locale: locale === 'fr' ? fr : enUS })}</div>
<div className={`text-lg ${isSameDay(day, new Date()) ? 'bg-blue-500 text-white rounded-full w-8 h-8 mx-auto flex items-center justify-center' : ''}`}>
{format(day, 'd')}
</div>
</div>
{Array.from({ length: 24 }, (_, hour) => (
<div key={hour} className="h-16 border-b border-gray-100 relative">
{events
.filter(event => {
const eventDate = parseISO(event.startTime);
return isSameDay(eventDate, day) && eventDate.getHours() === hour;
})
.map(event => (
<div
key={event.id}
className={`absolute left-0 right-0 mx-1 p-1 text-xs rounded cursor-pointer ${getEventTypeColor(event.type)}`}
onClick={() => handleEditEvent(event)}
>
<div className="font-medium truncate">{event.title}</div>
<div className="text-xs opacity-75">
{format(parseISO(event.startTime), 'HH:mm')} - {format(parseISO(event.endTime), 'HH:mm')}
</div>
</div>
))}
</div>
))}
</div>
))}
</div>
);
};
const renderAgendaView = () => {
const sortedEvents = events.sort((a, b) =>
new Date(a.startTime).getTime() - new Date(b.startTime).getTime()
);
return (
<div className="space-y-4">
{sortedEvents.map(event => (
<div key={event.id} className="bg-white rounded-lg border border-gray-200 p-4 hover:shadow-md transition-shadow">
<div className="flex items-start justify-between">
<div className="flex-1">
<div className="flex items-center gap-2 mb-2">
<span className={`px-2 py-1 rounded-full text-xs font-medium ${getEventTypeColor(event.type)}`}>
{t.calendar.eventTypes[event.type]}
</span>
{getStatusIcon(event.status)}
</div>
<h3 className="font-semibold text-lg mb-1">{event.title}</h3>
{event.description && (
<p className="text-gray-600 text-sm mb-2">{event.description}</p>
)}
<div className="flex items-center gap-4 text-sm text-gray-500">
<div className="flex items-center gap-1">
<Clock className="w-4 h-4" />
{format(parseISO(event.startTime), 'PPp', { locale: locale === 'fr' ? fr : enUS })}
</div>
{event.location && (
<div className="flex items-center gap-1">
<MapPin className="w-4 h-4" />
{event.location}
</div>
)}
{event.participants.length > 0 && (
<div className="flex items-center gap-1">
<Users className="w-4 h-4" />
{event.participants.length} {t.calendar.participants}
</div>
)}
</div>
</div>
<div className="flex items-center gap-2">
{event.meetingLink && (
<a
href={event.meetingLink}
target="_blank"
rel="noopener noreferrer"
className="p-2 text-blue-600 hover:bg-blue-50 rounded-full"
>
<Video className="w-4 h-4" />
</a>
)}
<button
onClick={() => handleEditEvent(event)}
className="p-2 text-gray-600 hover:bg-gray-50 rounded-full"
>
<Edit className="w-4 h-4" />
</button>
<button
onClick={() => handleDeleteEvent(event.id)}
className="p-2 text-red-600 hover:bg-red-50 rounded-full"
>
<Trash2 className="w-4 h-4" />
</button>
</div>
</div>
</div>
))}
</div>
);
};
if (status === 'loading' || loading) {
return (
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
<div className="animate-spin rounded-full h-32 w-32 border-b-2 border-blue-600"></div>
</div>
);
}
return (
<div className="min-h-screen bg-gray-50">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
{/* Header */}
<div className="mb-8">
<div className="flex items-center justify-between">
<div>
<h1 className="text-3xl font-bold text-gray-900">{t.calendar.title}</h1>
<p className="text-gray-600 mt-2">{t.calendar.description}</p>
</div>
<div className="flex items-center gap-4">
<button
onClick={handleCreateEvent}
className="inline-flex items-center px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
>
<Plus className="w-4 h-4 mr-2" />
New Event
</button>
</div>
</div>
</div>
{/* Controls */}
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-6 mb-6">
<div className="flex items-center justify-between mb-6">
<div className="flex items-center gap-4">
{/* View Selector */}
<div className="flex bg-gray-100 rounded-lg p-1">
{calendarViews.map(view => (
<button
key={view.id}
onClick={() => setSelectedView(view.type)}
className={`px-4 py-2 rounded-md text-sm font-medium transition-colors ${
selectedView === view.type
? 'bg-white text-gray-900 shadow-sm'
: 'text-gray-600 hover:text-gray-900'
}`}
>
{view.name}
</button>
))}
</div>
</div>
{/* Search and Filters */}
<div className="flex items-center gap-4">
<div className="relative">
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" />
<input
type="text"
placeholder={t.calendar.searchEvents}
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
/>
</div>
<button className="p-2 text-gray-600 hover:bg-gray-100 rounded-lg">
<Filter className="w-4 h-4" />
</button>
</div>
</div>
</div>
{/* Calendar Content */}
<div className="mb-6">
{selectedView === 'month' && renderMonthView()}
{selectedView === 'week' && renderWeekView()}
{selectedView === 'agenda' && renderAgendaView()}
</div>
{/* Event Modal */}
{showEventModal && (
<EventModal
event={selectedEvent}
onSave={handleSaveEvent}
onClose={() => setShowEventModal(false)}
t={t}
locale={locale || 'en'}
/>
)}
{/* Day Modal - Shows all events for a selected day */}
{showDayModal && selectedDate && (
<DayModal
date={selectedDate}
events={events.filter(event => isSameDay(parseISO(event.startTime), selectedDate))}
onClose={() => setShowDayModal(false)}
onEditEvent={handleEditEvent}
onDeleteEvent={handleDeleteEvent}
onCreateEvent={() => {
setShowDayModal(false);
setSelectedEvent(null);
setShowEventModal(true);
}}
t={t}
locale={locale || 'en'}
/>
)}
</div>
</div>
);
};
// Event Modal Component
interface EventModalProps {
event: CalendarEvent | null;
onSave: (eventData: Partial<CalendarEvent>) => void;
onClose: () => void;
t: any;
locale: string;
}
const EventModal: React.FC<EventModalProps> = ({ event, onSave, onClose, t, locale }) => {
const [formData, setFormData] = useState({
title: event?.title || '',
description: event?.description || '',
startTime: event?.startTime ? format(parseISO(event.startTime), "yyyy-MM-dd'T'HH:mm") : '',
endTime: event?.endTime ? format(parseISO(event.endTime), "yyyy-MM-dd'T'HH:mm") : '',
type: event?.type || 'consultation',
status: event?.status || 'scheduled',
location: event?.location || '',
meetingLink: event?.meetingLink || '',
participants: event?.participants || []
});
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
onSave(formData);
};
return (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
<div className="bg-white rounded-lg shadow-xl max-w-2xl w-full mx-4 max-h-[90vh] overflow-y-auto">
<div className="p-6">
<div className="flex items-center justify-between mb-6">
<h2 className="text-2xl font-bold text-gray-900">
{event ? t.calendar.editEvent : t.calendar.createEvent}
</h2>
<button
onClick={onClose}
className="p-2 text-gray-400 hover:text-gray-600 rounded-full"
>
×
</button>
</div>
<form onSubmit={handleSubmit} className="space-y-6">
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
{t.calendar.title}
</label>
<input
type="text"
value={formData.title}
onChange={(e) => setFormData({ ...formData, title: e.target.value })}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
required
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
{t.calendar.type}
</label>
<select
value={formData.type}
onChange={(e) => setFormData({ ...formData, type: e.target.value as any })}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
>
<option value="consultation">{t.calendar.eventTypes.consultation}</option>
<option value="court_hearing">{t.calendar.eventTypes.court_hearing}</option>
<option value="client_meeting">{t.calendar.eventTypes.client_meeting}</option>
<option value="team_meeting">{t.calendar.eventTypes.team_meeting}</option>
<option value="deadline">{t.calendar.eventTypes.deadline}</option>
<option value="reminder">{t.calendar.eventTypes.reminder}</option>
</select>
</div>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
{t.calendar.description}
</label>
<textarea
value={formData.description}
onChange={(e) => setFormData({ ...formData, description: e.target.value })}
rows={3}
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 className="grid grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
{t.calendar.startTime}
</label>
<input
type="datetime-local"
value={formData.startTime}
onChange={(e) => setFormData({ ...formData, startTime: e.target.value })}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
required
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
{t.calendar.endTime}
</label>
<input
type="datetime-local"
value={formData.endTime}
onChange={(e) => setFormData({ ...formData, endTime: e.target.value })}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
required
/>
</div>
</div>
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
{t.calendar.location}
</label>
<input
type="text"
value={formData.location}
onChange={(e) => setFormData({ ...formData, location: e.target.value })}
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>
<label className="block text-sm font-medium text-gray-700 mb-2">
{t.calendar.meetingLink}
</label>
<input
type="url"
value={formData.meetingLink}
onChange={(e) => setFormData({ ...formData, meetingLink: e.target.value })}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
placeholder="https://meet.google.com/..."
/>
</div>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
{t.calendar.status}
</label>
<select
value={formData.status}
onChange={(e) => setFormData({ ...formData, status: e.target.value as any })}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
>
<option value="scheduled">{t.calendar.statuses.scheduled}</option>
<option value="confirmed">{t.calendar.statuses.confirmed}</option>
<option value="completed">{t.calendar.statuses.completed}</option>
<option value="cancelled">{t.calendar.statuses.cancelled}</option>
</select>
</div>
<div className="flex items-center justify-end gap-4 pt-6 border-t border-gray-200">
<button
type="button"
onClick={onClose}
className="px-4 py-2 text-gray-600 hover:text-gray-800"
>
{t.common.cancel}
</button>
<button
type="submit"
className="px-6 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700"
>
{event ? t.common.update : t.common.create}
</button>
</div>
</form>
</div>
</div>
</div>
);
};
// Day Modal Component - Shows all events for a specific day
interface DayModalProps {
date: Date;
events: CalendarEvent[];
onClose: () => void;
onEditEvent: (event: CalendarEvent) => void;
onDeleteEvent: (eventId: string) => void;
onCreateEvent: () => void;
t: any;
locale: string;
}
const DayModal: React.FC<DayModalProps> = ({
date,
events,
onClose,
onEditEvent,
onDeleteEvent,
onCreateEvent,
t,
locale
}) => {
const [loading, setLoading] = React.useState(false);
React.useEffect(() => {
const handleEsc = (e: KeyboardEvent) => {
if (e.key === 'Escape') onClose();
};
window.addEventListener('keydown', handleEsc);
return () => window.removeEventListener('keydown', handleEsc);
}, [onClose]);
return (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50" onClick={onClose}>
<div className="bg-white rounded-lg shadow-xl max-w-2xl w-full mx-4 max-h-[80vh] overflow-hidden" onClick={e => e.stopPropagation()}>
<div className="px-6 py-4 border-b border-gray-200 flex items-center justify-between">
<h2 className="text-xl font-semibold text-gray-900">
{format(date, 'EEEE, MMMM d, yyyy', { locale: locale === 'fr' ? fr : enUS })}
</h2>
<button
onClick={onClose}
className="text-gray-400 hover:text-gray-600"
title="Close (ESC)"
>
<XCircle className="w-6 h-6" />
</button>
</div>
<div className="p-6 overflow-y-auto max-h-[60vh]">
{loading ? (
<div className="flex items-center justify-center py-8">
<div className="animate-spin rounded-full h-10 w-10 border-b-2 border-blue-600"></div>
</div>
) : events.length === 0 ? (
<div className="text-center py-8">
<Calendar className="w-12 h-12 text-gray-400 mx-auto mb-4" />
<h3 className="text-lg font-medium text-gray-900 mb-2">No events scheduled</h3>
<p className="text-gray-500 mb-4">This day is free of scheduled events.</p>
<button
onClick={onCreateEvent}
className="inline-flex items-center px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700"
>
<Plus className="w-4 h-4 mr-2" />
Schedule Event
</button>
</div>
) : (
<div className="space-y-4">
{events.map(event => (
<div key={event.id} className="border border-gray-200 rounded-lg p-4 hover:shadow-md transition-shadow">
<div className="flex items-start justify-between mb-3">
<div className="flex-1">
<div className="flex items-center gap-2 mb-2">
<span className={`px-2 py-1 rounded-full text-xs font-medium ${getEventTypeColor(event.type)}`}>
{t.calendar.eventTypes?.[event.type] || event.type}
</span>
{getStatusIcon(event.status)}
</div>
<h3 className="font-semibold text-lg mb-1">{event.title}</h3>
{event.description && (
<p className="text-gray-600 text-sm mb-2">{event.description}</p>
)}
<div className="flex items-center gap-4 text-sm text-gray-500 mb-3">
<div className="flex items-center gap-1">
<Clock className="w-4 h-4" />
{format(parseISO(event.startTime), 'HH:mm')} - {format(parseISO(event.endTime), 'HH:mm')}
</div>
{event.location && (
<div className="flex items-center gap-1">
<MapPin className="w-4 h-4" />
{event.location}
</div>
)}
</div>
{/* Client Information - Clickable to profile */}
{event.clientId && event.clientName && (
<div className="mb-3 p-3 bg-blue-50 rounded-lg">
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-gray-900">Client</p>
<button
onClick={() => window.open(`/lawyer/clients/${event.clientId}`, '_blank')}
className="text-blue-700 underline hover:text-blue-900 text-sm font-semibold"
title="View client profile"
>
{event.clientName}
</button>
{event.clientEmail && (
<p className="text-xs text-gray-500">{event.clientEmail}</p>
)}
</div>
</div>
</div>
)}
</div>
<div className="flex items-center gap-2">
{event.meetingLink && (
<a
href={event.meetingLink}
target="_blank"
rel="noopener noreferrer"
className="p-2 text-blue-600 hover:bg-blue-50 rounded-full"
title="Join meeting"
>
<Video className="w-4 h-4" />
</a>
)}
<button
onClick={() => onEditEvent(event)}
className="p-2 text-gray-600 hover:bg-gray-50 rounded-full"
title="Edit event"
>
<Edit className="w-4 h-4" />
</button>
<button
onClick={() => onDeleteEvent(event.id)}
className="p-2 text-red-600 hover:bg-red-50 rounded-full"
title="Delete event"
>
<Trash2 className="w-4 h-4" />
</button>
</div>
</div>
</div>
))}
<div className="pt-4 border-t border-gray-200">
<button
onClick={onCreateEvent}
className="w-full inline-flex items-center justify-center px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700"
>
<Plus className="w-4 h-4 mr-2" />
Add Event to This Day
</button>
</div>
</div>
)}
</div>
</div>
</div>
);
};
export default CalendarPage;