import { useState } from "react"; import { useQuery } from "@tanstack/react-query"; import { format, addWeeks, addDays, startOfWeek } from "date-fns"; import { it } from "date-fns/locale"; import { ChevronLeft, ChevronRight, Users, Building2, Navigation, Shield, Car as CarIcon, MapPin } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Skeleton } from "@/components/ui/skeleton"; import { Badge } from "@/components/ui/badge"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; type Location = "roccapiemonte" | "milano" | "roma"; interface FixedShiftDetail { shiftId: string; date: string; from: string; to: string; siteName: string; siteAddress: string; siteId: string; isArmed: boolean; vehicle?: { licensePlate: string; brand: string; model: string; }; hours: number; } interface FixedGuardSchedule { guardId: string; guardName: string; badgeNumber: string; shifts: FixedShiftDetail[]; totalHours: number; } interface PatrolRoute { routeId: string; guardId: string; shiftDate: string; startTime: string; endTime: string; isArmedRoute: boolean; vehicle?: { licensePlate: string; brand: string; model: string; }; stops: { siteId: string; siteName: string; siteAddress: string; sequenceOrder: number; }[]; } interface MobileGuardSchedule { guardId: string; guardName: string; badgeNumber: string; routes: PatrolRoute[]; totalRoutes: number; } interface SiteSchedule { siteId: string; siteName: string; location: string; shifts: { shiftId: string; date: string; from: string; to: string; guards: { guardName: string; badgeNumber: string; hours: number; isArmed: boolean; }[]; vehicle?: { licensePlate: string; brand: string; model: string; }; totalGuards: number; totalHours: number; }[]; totalShifts: number; totalHours: number; } export default function ServicePlanning() { const [selectedLocation, setSelectedLocation] = useState("roccapiemonte"); const [weekStart, setWeekStart] = useState(startOfWeek(new Date(), { weekStartsOn: 1 })); const [viewMode, setViewMode] = useState<"guard-fixed" | "guard-mobile" | "site">("guard-fixed"); const weekStartStr = format(weekStart, "yyyy-MM-dd"); // Query per vista Agenti Fissi const { data: fixedGuardSchedules, isLoading: isLoadingFixedGuards } = useQuery({ queryKey: ["/api/service-planning/guards-fixed", weekStartStr, selectedLocation], queryFn: async () => { const response = await fetch(`/api/service-planning/guards-fixed?weekStart=${weekStartStr}&location=${selectedLocation}`); if (!response.ok) throw new Error("Failed to fetch fixed guard schedules"); return response.json(); }, enabled: viewMode === "guard-fixed", }); // Query per vista Agenti Mobili const { data: mobileGuardSchedules, isLoading: isLoadingMobileGuards } = useQuery({ queryKey: ["/api/service-planning/guards-mobile", weekStartStr, selectedLocation], queryFn: async () => { const response = await fetch(`/api/service-planning/guards-mobile?weekStart=${weekStartStr}&location=${selectedLocation}`); if (!response.ok) throw new Error("Failed to fetch mobile guard schedules"); return response.json(); }, enabled: viewMode === "guard-mobile", }); // Query per vista Siti const { data: siteSchedules, isLoading: isLoadingSites } = useQuery({ queryKey: ["/api/service-planning/by-site", weekStartStr, selectedLocation], queryFn: async () => { const response = await fetch(`/api/service-planning/by-site?weekStart=${weekStartStr}&location=${selectedLocation}`); if (!response.ok) throw new Error("Failed to fetch site schedules"); return response.json(); }, enabled: viewMode === "site", }); const goToPreviousWeek = () => setWeekStart(addWeeks(weekStart, -1)); const goToNextWeek = () => setWeekStart(addWeeks(weekStart, 1)); return (
{/* Header */}

Visione Servizi

Visualizza orari e dotazioni per agente fisso, agente mobile o per sito

{/* Controlli */}
{/* Selezione sede */}
{/* Navigazione settimana */}
{format(weekStart, "d MMM", { locale: it })} - {format(addDays(weekStart, 6), "d MMM yyyy", { locale: it })}
{/* Tabs per vista */} setViewMode(v as "guard-fixed" | "guard-mobile" | "site")}> Agenti Fissi Agenti Mobili Vista Sito {/* Vista Agenti Fissi */} {isLoadingFixedGuards ? (
{[1, 2, 3].map((i) => ( ))}
) : fixedGuardSchedules && fixedGuardSchedules.length > 0 ? (
{fixedGuardSchedules.map((guard) => (
{guard.guardName} {guard.badgeNumber} {guard.totalHours}h totali
{guard.shifts.length === 0 ? (

Nessun turno fisso assegnato

) : (
{guard.shifts.map((shift) => (
{shift.siteName}
{shift.siteAddress}
{format(new Date(shift.date), "EEEE d MMM", { locale: it })} • {shift.from} - {shift.to} ({shift.hours}h)
{shift.isArmed && ( Armato )} {shift.vehicle && ( {shift.vehicle.licensePlate} )}
))}
)}
))}
) : (

Nessun agente con turni fissi assegnati

)}
{/* Vista Agenti Mobili */} {isLoadingMobileGuards ? (
{[1, 2, 3].map((i) => ( ))}
) : mobileGuardSchedules && mobileGuardSchedules.length > 0 ? (
{mobileGuardSchedules.map((guard) => (
{guard.guardName} {guard.badgeNumber} {guard.totalRoutes} {guard.totalRoutes === 1 ? 'percorso' : 'percorsi'}
{guard.routes.length === 0 ? (

Nessun percorso pattuglia assegnato

) : (
{guard.routes.map((route) => (
{format(new Date(route.shiftDate), "EEEE d MMM yyyy", { locale: it })}
{route.startTime} - {route.endTime}
{route.isArmedRoute && ( Armato )} {route.vehicle && ( {route.vehicle.licensePlate} )}
Percorso ({route.stops.length} {route.stops.length === 1 ? 'tappa' : 'tappe'}):
{route.stops.map((stop) => (
{stop.sequenceOrder}
{stop.siteName}
{stop.siteAddress}
))}
))}
)}
))}
) : (

Nessun agente con percorsi pattuglia assegnati

)}
{/* Vista Sito */} {isLoadingSites ? (
{[1, 2, 3].map((i) => ( ))}
) : siteSchedules && siteSchedules.length > 0 ? (
{siteSchedules.map((site) => (
{site.siteName}
{site.totalShifts} turni {site.totalHours}h totali
{site.shifts.length === 0 ? (

Nessun turno programmato

) : (
{site.shifts.map((shift) => (
{format(new Date(shift.date), "EEEE d MMM", { locale: it })} • {shift.from} - {shift.to}
{shift.totalGuards} {shift.totalGuards === 1 ? "guardia" : "guardie"} {shift.vehicle && ( {shift.vehicle.licensePlate} )}
{shift.guards.map((guard, idx) => (
{guard.guardName} ({guard.badgeNumber}) - {guard.hours}h {guard.isArmed && ( Armato )}
))}
))}
)}
))}
) : (

Nessun sito con turni programmati

)}
); }