'use client'; import { useState } from 'react'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { Download, Plus, Edit, Trash2, Train as TrainIcon } from 'lucide-react'; import DataTable from '@/components/ui/DataTable'; import Badge from '@/components/ui/Badge'; import ActionButton from '@/components/ui/ActionButton'; import Modal from '@/components/ui/Modal'; import { schedulesApi, fleetApi } from '@/lib/api'; import { routesApi } from '@/lib/api/routes'; import { formatDateTime, formatCurrency } from '@/lib/utils'; export default function SchedulesPage() { const [filters, setFilters] = useState({ search: '', status: '' }); const [showModal, setShowModal] = useState(false); const [showCoachModal, setShowCoachModal] = useState(false); const [editingSchedule, setEditingSchedule] = useState(null); const [selectedSchedule, setSelectedSchedule] = useState(null); const [selectedCoaches, setSelectedCoaches] = useState>([]); const queryClient = useQueryClient(); const { data, isLoading } = useQuery({ queryKey: ['schedules', filters], queryFn: () => schedulesApi.getAll(filters), }); const { data: trainsData } = useQuery({ queryKey: ['trains'], queryFn: () => fleetApi.getTrains(), }); const { data: routesData } = useQuery({ queryKey: ['routes'], queryFn: () => routesApi.getAll(), }); const { data: coachesData } = useQuery({ queryKey: ['coaches'], queryFn: () => fleetApi.getCoaches(), }); const createMutation = useMutation({ mutationFn: schedulesApi.create, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['schedules'] }); setShowModal(false); setEditingSchedule(null); }, }); const updateMutation = useMutation({ mutationFn: ({ id, data }: { id: string; data: any }) => schedulesApi.update(id, data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['schedules'] }); setShowModal(false); setEditingSchedule(null); }, }); const deleteMutation = useMutation({ mutationFn: schedulesApi.delete, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['schedules'] }); }, }); const assignCoachesMutation = useMutation({ mutationFn: ({ scheduleId, coaches }: { scheduleId: string; coaches: Array<{ coachId: string; positionNumber: number }> }) => schedulesApi.assignCoaches(scheduleId, coaches), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['schedules'] }); setShowCoachModal(false); setSelectedSchedule(null); setSelectedCoaches([]); alert('Coaches assigned successfully'); }, }); const removeCoachMutation = useMutation({ mutationFn: ({ scheduleId, coachId }: { scheduleId: string; coachId: string }) => schedulesApi.removeCoachAssignment(scheduleId, coachId), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['schedules'] }); }, }); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); const formData = new FormData(e.currentTarget); const departureAt = formData.get('departureAt') as string; const arrivalAt = formData.get('arrivalAt') as string; // Convert datetime-local to ISO 8601 const departureISO = new Date(departureAt).toISOString(); const arrivalISO = new Date(arrivalAt).toISOString(); const scheduleData = { trainId: formData.get('trainId') as string, routeId: formData.get('routeId') as string, departureAt: departureISO, arrivalAt: arrivalISO, plannedTimes: [], // Will be auto-generated by backend based on route stops }; if (editingSchedule) { await updateMutation.mutateAsync({ id: editingSchedule.id, data: scheduleData }); } else { await createMutation.mutateAsync(scheduleData); } }; const trains = trainsData?.items || trainsData?.data || []; const routes = routesData?.items || routesData?.data || []; const coaches = coachesData?.items || coachesData?.data || []; const handleDelete = async (schedule: any) => { if (confirm('Are you sure you want to delete this schedule?')) { await deleteMutation.mutateAsync(schedule.id); } }; const handleAssignCoaches = (schedule: any) => { setSelectedSchedule(schedule); setSelectedCoaches([]); setShowCoachModal(true); }; const handleRemoveCoach = async (schedule: any, coachId: string) => { if (confirm('Remove this coach from the schedule?')) { await removeCoachMutation.mutateAsync({ scheduleId: schedule.id, coachId }); } }; const handleToggleCoach = (coachId: string) => { setSelectedCoaches(prev => { const exists = prev.find(c => c.coachId === coachId); if (exists) { return prev.filter(c => c.coachId !== coachId); } else { const maxPosition = prev.length > 0 ? Math.max(...prev.map(c => c.positionNumber)) : 0; return [...prev, { coachId, positionNumber: maxPosition + 1 }]; } }); }; const handleSubmitCoaches = async () => { if (selectedCoaches.length === 0) { alert('Please select at least one coach'); return; } await assignCoachesMutation.mutateAsync({ scheduleId: selectedSchedule.id, coaches: selectedCoaches, }); }; const columns = [ { key: 'train', label: 'Train', render: (schedule: any) => schedule.train?.name || 'N/A' }, { key: 'route', label: 'Route', render: (schedule: any) => schedule.route?.name || `${schedule.originStation?.name || 'N/A'} → ${schedule.destinationStation?.name || 'N/A'}` }, { key: 'departureAt', label: 'Departure', render: (schedule: any) => formatDateTime(schedule.departureAt) }, { key: 'coaches', label: 'Coaches', render: (schedule: any) => { const coachCount = schedule._count?.coachAssignments || 0; if (coachCount === 0) { return No coaches assigned; } return (
{schedule.coachAssignments?.slice(0, 3).map((assignment: any) => (
{assignment.coach?.coachNumber || 'N/A'}
))} {coachCount > 3 && ( +{coachCount - 3} )}
); } }, { key: 'status', label: 'Status', render: (schedule: any) => {schedule.status} }, ]; const actions = [ { label: 'Assign Coaches', onClick: handleAssignCoaches, variant: 'primary' as const, icon: TrainIcon, }, { label: 'Edit', onClick: (schedule: any) => { setEditingSchedule(schedule); setShowModal(true); }, variant: 'secondary' as const, icon: Edit, }, { label: 'Delete', onClick: handleDelete, variant: 'danger' as const, icon: Trash2, }, ]; return (

Schedules

Manage train schedules and trips

Export { setEditingSchedule(null); setShowModal(true); }} > Add Schedule
setFilters({ ...filters, search: e.target.value })} />
{/* Add/Edit Modal */} { setShowModal(false); setEditingSchedule(null); }} title={`${editingSchedule ? 'Edit' : 'Add'} Schedule`} size="lg" >
{ setShowModal(false); setEditingSchedule(null); }} > Cancel {editingSchedule ? 'Update' : 'Create'} Schedule
{/* Coach Assignment Modal */} { setShowCoachModal(false); setSelectedSchedule(null); setSelectedCoaches([]); }} title="Assign Coaches to Schedule" size="lg" >

Select coaches to assign to this schedule. Coaches will be ordered by selection.

{coaches.map((coach: any) => { const isSelected = selectedCoaches.some(c => c.coachId === coach.id); const position = selectedCoaches.find(c => c.coachId === coach.id)?.positionNumber; return (
handleToggleCoach(coach.id)} className={`p-4 border rounded-lg cursor-pointer transition-colors ${ isSelected ? 'border-edr-green-600 bg-edr-green-50 dark:bg-edr-green-900/20' : 'border-gray-200 dark:border-gray-700 hover:border-gray-300 dark:hover:border-gray-600' }`} >
{coach.label}
{coach.coachNumber} • {coach.seatClass?.name || 'N/A'} • {coach.totalUnits} seats
{isSelected && (
Position {position}
)}
); })}
{selectedCoaches.length > 0 && (
Selected Coaches ({selectedCoaches.length}):
{selectedCoaches .sort((a, b) => a.positionNumber - b.positionNumber) .map(sc => { const coach = coaches.find((c: any) => c.id === sc.coachId); return ( {sc.positionNumber}. {coach?.label || 'Unknown'} ); })}
)}
{ setShowCoachModal(false); setSelectedSchedule(null); setSelectedCoaches([]); }} > Cancel Assign {selectedCoaches.length} Coach{selectedCoaches.length !== 1 ? 'es' : ''}
); }