Show assigned guards and vehicle information in the planning view
Fixes an issue where the guard assignment form was not displaying correctly in the general planning view by ensuring all necessary data is fetched and rendered. Replit-Commit-Author: Agent Replit-Commit-Session-Id: e5565357-90e1-419f-b9a8-6ee8394636df Replit-Commit-Checkpoint-Type: intermediate_checkpoint Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/6d543d2c-20b9-4ea6-93fe-70fe9b1d9f80/e5565357-90e1-419f-b9a8-6ee8394636df/NB1Ej1f
This commit is contained in:
parent
03049f4090
commit
03531f0d5c
4
.replit
4
.replit
@ -15,6 +15,10 @@ run = ["npm", "run", "start"]
|
||||
localPort = 5000
|
||||
externalPort = 80
|
||||
|
||||
[[ports]]
|
||||
localPort = 32811
|
||||
externalPort = 5173
|
||||
|
||||
[[ports]]
|
||||
localPort = 33035
|
||||
externalPort = 3001
|
||||
|
||||
@ -558,103 +558,8 @@ export default function GeneralPlanning() {
|
||||
|
||||
{selectedCell && (
|
||||
<div className="space-y-4 overflow-y-auto pr-2">
|
||||
{/* Info turni */}
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<p className="text-sm text-muted-foreground">Turni Pianificati</p>
|
||||
<p className="text-2xl font-bold">{selectedCell.data.shiftsCount}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-muted-foreground">Ore Totali</p>
|
||||
<p className="text-2xl font-bold">{selectedCell.data.totalShiftHours}h</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Veicoli */}
|
||||
{selectedCell.data.vehicles.length > 0 && (
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center gap-2 text-sm font-semibold">
|
||||
<Car className="h-4 w-4" />
|
||||
Veicoli Assegnati ({selectedCell.data.vehicles.length})
|
||||
</div>
|
||||
<div className="grid gap-2">
|
||||
{selectedCell.data.vehicles.map((vehicle, idx) => (
|
||||
<div key={idx} className="flex items-center gap-2 p-2 bg-accent/10 rounded-md">
|
||||
<p className="font-medium">{vehicle.licensePlate}</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{vehicle.brand} {vehicle.model}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Guardie mancanti */}
|
||||
{selectedCell.data.missingGuards > 0 && (
|
||||
<div className="p-4 bg-destructive/10 border border-destructive/20 rounded-md">
|
||||
<div className="flex items-center gap-2 text-destructive font-semibold mb-2">
|
||||
<AlertTriangle className="h-5 w-5" />
|
||||
Attenzione: Guardie Mancanti
|
||||
</div>
|
||||
<p className="text-sm">
|
||||
Servono ancora <span className="font-bold">{selectedCell.data.missingGuards}</span>{" "}
|
||||
{selectedCell.data.missingGuards === 1 ? "guardia" : "guardie"} per coprire completamente il servizio
|
||||
(calcolato su {selectedCell.data.totalShiftHours}h con max 9h per guardia e {selectedCell.data.minGuards} {selectedCell.data.minGuards === 1 ? "guardia minima" : "guardie minime"} contemporanee)
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* No turni */}
|
||||
{selectedCell.data.shiftsCount === 0 && (
|
||||
<div className="text-center py-8 text-muted-foreground">
|
||||
<Calendar className="h-12 w-12 mx-auto mb-4 opacity-50" />
|
||||
<p>Nessun turno pianificato per questa data</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Form assegnazione guardia */}
|
||||
<div className="border-t pt-4 space-y-4">
|
||||
{/* Mostra guardie già assegnate per questo giorno */}
|
||||
{selectedCell.data.guards.length > 0 && (
|
||||
<div className="bg-muted/30 p-3 rounded-md space-y-2">
|
||||
<p className="text-xs font-medium text-muted-foreground">
|
||||
Guardie già assegnate per questa data:
|
||||
</p>
|
||||
<div className="grid gap-2">
|
||||
{selectedCell.data.guards.map((guard, idx) => (
|
||||
<div key={idx} className="flex items-start justify-between gap-2 bg-background p-2.5 rounded border">
|
||||
<div className="flex-1 space-y-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="font-medium text-sm">{guard.guardName}</span>
|
||||
<Badge variant="outline" className="text-xs">#{guard.badgeNumber}</Badge>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-xs text-muted-foreground">
|
||||
<Clock className="h-3 w-3" />
|
||||
<span>{formatTime(guard.plannedStartTime)} - {formatTime(guard.plannedEndTime)}</span>
|
||||
<span className="font-medium">({guard.hours}h)</span>
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-7 w-7 text-destructive hover:text-destructive hover:bg-destructive/10"
|
||||
onClick={() => {
|
||||
if (confirm(`Confermi di voler rimuovere ${guard.guardName} da questo turno?`)) {
|
||||
deleteAssignmentMutation.mutate(guard.assignmentId);
|
||||
}
|
||||
}}
|
||||
disabled={deleteAssignmentMutation.isPending}
|
||||
data-testid={`button-delete-assignment-${guard.guardId}`}
|
||||
>
|
||||
<Trash2 className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Form assegnazione guardia - SEMPRE IN ALTO E VISIBILE */}
|
||||
<div className="bg-accent/5 border border-accent/20 rounded-lg p-4 space-y-4">
|
||||
<div className="flex items-center gap-2 text-sm font-semibold">
|
||||
<Plus className="h-4 w-4" />
|
||||
Assegna Nuova Guardia
|
||||
@ -868,6 +773,110 @@ export default function GeneralPlanning() {
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Guardie già assegnate - fuori dal form box per evitare di nascondere il form */}
|
||||
{selectedCell.data.guards.length > 0 && (
|
||||
<div className="space-y-2">
|
||||
<h3 className="text-sm font-semibold flex items-center gap-2">
|
||||
<Users className="h-4 w-4" />
|
||||
Guardie Già Assegnate ({selectedCell.data.guards.length})
|
||||
</h3>
|
||||
<div className="grid gap-2">
|
||||
{selectedCell.data.guards.map((guard, idx) => (
|
||||
<div key={idx} className="flex items-start justify-between gap-2 bg-muted/30 p-2.5 rounded border">
|
||||
<div className="flex-1 space-y-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="font-medium text-sm">{guard.guardName}</span>
|
||||
<Badge variant="outline" className="text-xs">#{guard.badgeNumber}</Badge>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-xs text-muted-foreground">
|
||||
<Clock className="h-3 w-3" />
|
||||
<span>{formatTime(guard.plannedStartTime)} - {formatTime(guard.plannedEndTime)}</span>
|
||||
<span className="font-medium">({guard.hours}h)</span>
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-7 w-7 text-destructive hover:text-destructive hover:bg-destructive/10"
|
||||
onClick={() => {
|
||||
if (confirm(`Confermi di voler rimuovere ${guard.guardName} da questo turno?`)) {
|
||||
deleteAssignmentMutation.mutate(guard.assignmentId);
|
||||
}
|
||||
}}
|
||||
disabled={deleteAssignmentMutation.isPending}
|
||||
data-testid={`button-delete-assignment-${guard.guardId}`}
|
||||
>
|
||||
<Trash2 className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Separator */}
|
||||
<div className="border-t" />
|
||||
|
||||
{/* Info turni esistenti */}
|
||||
<div className="space-y-4">
|
||||
<h3 className="font-semibold text-sm">Situazione Attuale</h3>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<p className="text-sm text-muted-foreground">Turni Pianificati</p>
|
||||
<p className="text-2xl font-bold">{selectedCell.data.shiftsCount}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-muted-foreground">Ore Totali</p>
|
||||
<p className="text-2xl font-bold">{selectedCell.data.totalShiftHours}h</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Guardie mancanti */}
|
||||
{selectedCell.data.missingGuards > 0 && (
|
||||
<div className="p-4 bg-destructive/10 border border-destructive/20 rounded-md">
|
||||
<div className="flex items-center gap-2 text-destructive font-semibold mb-2">
|
||||
<AlertTriangle className="h-5 w-5" />
|
||||
Attenzione: Guardie Mancanti
|
||||
</div>
|
||||
<p className="text-sm">
|
||||
Servono ancora <span className="font-bold">{selectedCell.data.missingGuards}</span>{" "}
|
||||
{selectedCell.data.missingGuards === 1 ? "guardia" : "guardie"} per coprire completamente il servizio
|
||||
(calcolato su {selectedCell.data.totalShiftHours}h con max 9h per guardia e {selectedCell.data.minGuards} {selectedCell.data.minGuards === 1 ? "guardia minima" : "guardie minime"} contemporanee)
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Veicoli */}
|
||||
{selectedCell.data.vehicles.length > 0 && (
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center gap-2 text-sm font-semibold">
|
||||
<Car className="h-4 w-4" />
|
||||
Veicoli Assegnati ({selectedCell.data.vehicles.length})
|
||||
</div>
|
||||
<div className="grid gap-2">
|
||||
{selectedCell.data.vehicles.map((vehicle, idx) => (
|
||||
<div key={idx} className="flex items-center gap-2 p-2 bg-accent/10 rounded-md">
|
||||
<p className="font-medium">{vehicle.licensePlate}</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{vehicle.brand} {vehicle.model}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* No turni */}
|
||||
{selectedCell.data.shiftsCount === 0 && (
|
||||
<div className="text-center py-6 text-muted-foreground">
|
||||
<Calendar className="h-10 w-10 mx-auto mb-3 opacity-50" />
|
||||
<p className="text-sm">Nessun turno pianificato per questa data</p>
|
||||
<p className="text-xs mt-1">Usa il modulo sopra per assegnare la prima guardia</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user