Improve guard selection by filtering based on availability and overtime
Refactors the guard selection UI to dynamically filter available guards, showing regular hours first and providing an option to display those requiring overtime. 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/qoWuIE4
This commit is contained in:
parent
b782f16797
commit
da547137b7
4
.replit
4
.replit
@ -39,6 +39,10 @@ externalPort = 5000
|
||||
localPort = 43267
|
||||
externalPort = 3003
|
||||
|
||||
[[ports]]
|
||||
localPort = 45469
|
||||
externalPort = 5173
|
||||
|
||||
[env]
|
||||
PORT = "5000"
|
||||
|
||||
|
||||
@ -685,97 +685,111 @@ export default function GeneralPlanning() {
|
||||
</div>
|
||||
|
||||
{/* Select guardia disponibile */}
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<Label htmlFor="guard-select">Guardia Disponibile</Label>
|
||||
{!isLoadingGuards && availableGuards && availableGuards.some(g => g.requiresOvertime && g.isAvailable) && (
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => setShowOvertimeGuards(!showOvertimeGuards)}
|
||||
className="h-7 text-xs"
|
||||
data-testid="button-toggle-overtime"
|
||||
>
|
||||
{showOvertimeGuards ? "Nascondi" : "Mostra"} Straordinario
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
{isLoadingGuards ? (
|
||||
<Skeleton className="h-10 w-full" />
|
||||
) : (
|
||||
<>
|
||||
{(() => {
|
||||
// Filtra guardie: mostra solo con ore ordinarie se toggle è off
|
||||
const filteredGuards = availableGuards?.filter(g =>
|
||||
g.isAvailable && (showOvertimeGuards || !g.requiresOvertime)
|
||||
) || [];
|
||||
{(() => {
|
||||
// Filtra guardie: mostra solo con ore ordinarie se toggle è off
|
||||
const filteredGuards = availableGuards?.filter(g =>
|
||||
g.isAvailable && (showOvertimeGuards || !g.requiresOvertime)
|
||||
) || [];
|
||||
|
||||
return (
|
||||
<>
|
||||
<Select
|
||||
value={selectedGuardId}
|
||||
onValueChange={setSelectedGuardId}
|
||||
disabled={assignGuardMutation.isPending}
|
||||
>
|
||||
<SelectTrigger id="guard-select" data-testid="select-guard">
|
||||
<SelectValue placeholder="Seleziona guardia..." />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{filteredGuards.length > 0 ? (
|
||||
filteredGuards.map((guard) => (
|
||||
<SelectItem key={guard.guardId} value={guard.guardId}>
|
||||
{guard.guardName} ({guard.badgeNumber}) - {guard.ordinaryHoursRemaining}h ord.
|
||||
{guard.requiresOvertime && ` + ${guard.overtimeHoursRemaining}h strao.`}
|
||||
{guard.requiresOvertime && " 🔸"}
|
||||
</SelectItem>
|
||||
))
|
||||
) : (
|
||||
<SelectItem value="no-guards" disabled>
|
||||
{showOvertimeGuards
|
||||
? "Nessuna guardia disponibile"
|
||||
: "Nessuna guardia con ore ordinarie (prova 'Mostra Straordinario')"}
|
||||
const hasOvertimeGuards = availableGuards?.some(g => g.requiresOvertime && g.isAvailable) || false;
|
||||
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<Label htmlFor="guard-select">Guardia Disponibile</Label>
|
||||
{!isLoadingGuards && hasOvertimeGuards && (
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => setShowOvertimeGuards(!showOvertimeGuards)}
|
||||
className="h-7 text-xs"
|
||||
data-testid="button-toggle-overtime"
|
||||
>
|
||||
{showOvertimeGuards ? "Nascondi" : "Mostra"} Straordinario
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
{isLoadingGuards ? (
|
||||
<Skeleton className="h-10 w-full" />
|
||||
) : (
|
||||
<>
|
||||
<Select
|
||||
value={selectedGuardId}
|
||||
onValueChange={setSelectedGuardId}
|
||||
disabled={assignGuardMutation.isPending}
|
||||
>
|
||||
<SelectTrigger id="guard-select" data-testid="select-guard">
|
||||
<SelectValue placeholder="Seleziona guardia..." />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{filteredGuards.length > 0 ? (
|
||||
filteredGuards.map((guard) => (
|
||||
<SelectItem key={guard.guardId} value={guard.guardId}>
|
||||
{guard.guardName} ({guard.badgeNumber}) - {guard.ordinaryHoursRemaining}h ord.
|
||||
{guard.requiresOvertime && ` + ${guard.overtimeHoursRemaining}h strao.`}
|
||||
{guard.requiresOvertime && " 🔸"}
|
||||
</SelectItem>
|
||||
)}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
{filteredGuards.length === 0 && !showOvertimeGuards && availableGuards && availableGuards.some(g => g.isAvailable && g.requiresOvertime) && (
|
||||
<p className="text-xs text-muted-foreground">
|
||||
ℹ️ Alcune guardie disponibili richiedono straordinario. Clicca "Mostra Straordinario" per vederle.
|
||||
</p>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
})()}
|
||||
</>
|
||||
)}
|
||||
{availableGuards && availableGuards.length > 0 && selectedGuardId && (
|
||||
<div className="text-xs space-y-1">
|
||||
{(() => {
|
||||
const guard = availableGuards.find(g => g.guardId === selectedGuardId);
|
||||
if (!guard) return null;
|
||||
return (
|
||||
<>
|
||||
<p className="text-muted-foreground">
|
||||
Ore assegnate: {guard.weeklyHoursAssigned}h / {guard.weeklyHoursMax}h (rimangono {guard.weeklyHoursRemaining}h)
|
||||
))
|
||||
) : (
|
||||
<SelectItem value="no-guards" disabled>
|
||||
{showOvertimeGuards
|
||||
? "Nessuna guardia disponibile"
|
||||
: "Nessuna guardia con ore ordinarie (prova 'Mostra Straordinario')"}
|
||||
</SelectItem>
|
||||
)}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
{filteredGuards.length === 0 && !showOvertimeGuards && hasOvertimeGuards && (
|
||||
<p className="text-xs text-muted-foreground">
|
||||
ℹ️ Alcune guardie disponibili richiedono straordinario. Clicca "Mostra Straordinario" per vederle.
|
||||
</p>
|
||||
{guard.conflicts && guard.conflicts.length > 0 && (
|
||||
<p className="text-destructive font-medium">
|
||||
⚠️ Conflitto: {guard.conflicts.map((c: any) =>
|
||||
`${c.siteName} (${new Date(c.from).toLocaleTimeString('it-IT', {hour: '2-digit', minute:'2-digit'})} - ${new Date(c.to).toLocaleTimeString('it-IT', {hour: '2-digit', minute:'2-digit'})})`
|
||||
).join(", ")}
|
||||
</p>
|
||||
)}
|
||||
{guard.unavailabilityReasons && guard.unavailabilityReasons.length > 0 && (
|
||||
<p className="text-yellow-600 dark:text-yellow-500">
|
||||
{guard.unavailabilityReasons.join(", ")}
|
||||
</p>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
})()}
|
||||
)}
|
||||
{filteredGuards.length > 0 && selectedGuardId && (
|
||||
<div className="text-xs space-y-1">
|
||||
{(() => {
|
||||
const guard = availableGuards?.find(g => g.guardId === selectedGuardId);
|
||||
if (!guard) return null;
|
||||
return (
|
||||
<>
|
||||
<p className="text-muted-foreground">
|
||||
Ore ordinarie: {guard.ordinaryHoursRemaining}h / 40h disponibili
|
||||
{guard.requiresOvertime && ` • Straordinario: ${guard.overtimeHoursRemaining}h / 8h`}
|
||||
</p>
|
||||
<p className="text-muted-foreground">
|
||||
Ore assegnate: {guard.weeklyHoursAssigned}h / {guard.weeklyHoursMax}h (rimangono {guard.weeklyHoursRemaining}h)
|
||||
</p>
|
||||
{guard.nightHoursAssigned > 0 && (
|
||||
<p className="text-muted-foreground">
|
||||
Ore notturne: {guard.nightHoursAssigned}h / 48h settimanali
|
||||
</p>
|
||||
)}
|
||||
{guard.hasRestViolation && (
|
||||
<p className="text-yellow-600 dark:text-yellow-500 font-medium">
|
||||
⚠️ Attenzione: riposo insufficiente dall'ultimo turno
|
||||
</p>
|
||||
)}
|
||||
{guard.conflicts && guard.conflicts.length > 0 && (
|
||||
<p className="text-destructive font-medium">
|
||||
⚠️ Conflitto: {guard.conflicts.map((c: any) =>
|
||||
`${c.siteName} (${new Date(c.from).toLocaleTimeString('it-IT', {hour: '2-digit', minute:'2-digit'})} - ${new Date(c.to).toLocaleTimeString('it-IT', {hour: '2-digit', minute:'2-digit'})})`
|
||||
).join(", ")}
|
||||
</p>
|
||||
)}
|
||||
{guard.unavailabilityReasons && guard.unavailabilityReasons.length > 0 && (
|
||||
<p className="text-yellow-600 dark:text-yellow-500">
|
||||
{guard.unavailabilityReasons.join(", ")}
|
||||
</p>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
})()}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})()}
|
||||
|
||||
{/* Bottone assegna */}
|
||||
<Button
|
||||
|
||||
Loading…
Reference in New Issue
Block a user