import { useState } from "react"; import { useQuery, useMutation } from "@tanstack/react-query"; import { ShiftWithDetails, InsertShift, Site, Guard } from "@shared/schema"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"; import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { insertShiftFormSchema } from "@shared/schema"; import { Plus, Calendar, MapPin, Users, Clock } from "lucide-react"; import { apiRequest, queryClient } from "@/lib/queryClient"; import { useToast } from "@/hooks/use-toast"; import { StatusBadge } from "@/components/status-badge"; import { Skeleton } from "@/components/ui/skeleton"; import { format } from "date-fns"; import { it } from "date-fns/locale"; export default function Shifts() { const { toast } = useToast(); const [isDialogOpen, setIsDialogOpen] = useState(false); const { data: shifts, isLoading: shiftsLoading } = useQuery({ queryKey: ["/api/shifts"], }); const { data: sites } = useQuery({ queryKey: ["/api/sites"], }); const form = useForm({ resolver: zodResolver(insertShiftFormSchema), defaultValues: { siteId: "", startTime: "", endTime: "", status: "planned" as const, }, }); const createMutation = useMutation({ mutationFn: async (data: InsertShift) => { return await apiRequest("POST", "/api/shifts", data); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["/api/shifts"] }); toast({ title: "Turno creato", description: "Il turno รจ stato pianificato con successo", }); setIsDialogOpen(false); form.reset(); }, onError: (error) => { toast({ title: "Errore", description: error.message, variant: "destructive", }); }, }); const onSubmit = (data: InsertShift) => { createMutation.mutate(data); }; const getStatusLabel = (status: string) => { const labels: Record = { planned: "Pianificato", active: "Attivo", completed: "Completato", cancelled: "Annullato", }; return labels[status] || status; }; const getStatusVariant = (status: string): "active" | "inactive" | "pending" | "completed" => { const variants: Record = { planned: "pending", active: "active", completed: "completed", cancelled: "inactive", }; return variants[status] || "inactive"; }; return (

Pianificazione Turni

Calendario 24/7 con assegnazione guardie

Nuovo Turno Pianifica un nuovo turno di servizio
( Sito )} />
( Inizio field.onChange(e.target.value)} data-testid="input-start-time" /> )} /> ( Fine field.onChange(e.target.value)} data-testid="input-end-time" /> )} />
{shiftsLoading ? (
) : shifts && shifts.length > 0 ? (
{shifts.map((shift) => (
{shift.site.name} {format(new Date(shift.startTime), "dd/MM/yyyy HH:mm", { locale: it })} -{" "} {format(new Date(shift.endTime), "HH:mm", { locale: it })}
{getStatusLabel(shift.status)}
{shift.assignments.length > 0 ? `${shift.assignments.length} guardie assegnate` : "Nessuna guardia assegnata"}
{shift.assignments.length > 0 && (
{shift.assignments.map((assignment) => (
{assignment.guard.user?.firstName} {assignment.guard.user?.lastName}
))}
)}
))}
) : (

Nessun turno pianificato

Inizia pianificando il primo turno di servizio

)}
); }