diff --git a/.replit b/.replit index c50bc15..f28aea5 100644 --- a/.replit +++ b/.replit @@ -31,6 +31,10 @@ externalPort = 3002 localPort = 43267 externalPort = 3003 +[[ports]] +localPort = 43839 +externalPort = 4200 + [env] PORT = "5000" diff --git a/client/src/pages/sites.tsx b/client/src/pages/sites.tsx index 49bb16d..a72f34d 100644 --- a/client/src/pages/sites.tsx +++ b/client/src/pages/sites.tsx @@ -142,6 +142,32 @@ export default function Sites() { }); }; + // Funzione per determinare lo stato del contratto + const getContractStatus = (site: Site): "active" | "expiring" | "expired" | "none" => { + if (!site.contractStartDate || !site.contractEndDate) return "none"; + + const today = new Date(); + const startDate = new Date(site.contractStartDate); + const endDate = new Date(site.contractEndDate); + + if (today < startDate) return "none"; // Contratto non ancora iniziato + if (today > endDate) return "expired"; + + // Calcola i giorni rimanenti + const daysLeft = Math.ceil((endDate.getTime() - today.getTime()) / (1000 * 60 * 60 * 24)); + + if (daysLeft <= 30) return "expiring"; // In scadenza se mancano 30 giorni o meno + + return "active"; + }; + + const contractStatusLabels = { + active: { label: "Contratto Attivo", variant: "default" as const }, + expiring: { label: "In Scadenza", variant: "outline" as const }, + expired: { label: "Scaduto", variant: "destructive" as const }, + none: { label: "Nessun Contratto", variant: "secondary" as const }, + }; + return (
@@ -679,12 +705,28 @@ export default function Sites() {
-
+
{shiftTypeLabels[site.shiftType]} + {(() => { + const status = getContractStatus(site); + const statusInfo = contractStatusLabels[status]; + return ( + + {statusInfo.label} + + ); + })()}
+ {site.contractReference && ( +
+ Contratto: {site.contractReference} + {site.contractEndDate && ` • Scade: ${new Date(site.contractEndDate).toLocaleDateString('it-IT')}`} +
+ )} +
diff --git a/server/routes.ts b/server/routes.ts index 7e45a80..062576d 100644 --- a/server/routes.ts +++ b/server/routes.ts @@ -944,7 +944,20 @@ export async function registerRoutes(app: Express): Promise { return res.status(400).json({ message: "Missing required fields" }); } - // Convert and validate dates + // Verifica stato contratto del sito + const site = await storage.getSite(req.body.siteId); + if (!site) { + return res.status(404).json({ message: "Sito non trovato" }); + } + + // Controllo validità contratto - richiesto per creare turni + if (!site.contractStartDate || !site.contractEndDate) { + return res.status(400).json({ + message: `Impossibile creare turno: il sito "${site.name}" non ha un contratto attivo` + }); + } + + // Convert and validate shift dates first const startTime = new Date(req.body.startTime); const endTime = new Date(req.body.endTime); @@ -952,6 +965,30 @@ export async function registerRoutes(app: Express): Promise { return res.status(400).json({ message: "Invalid date format" }); } + // Normalizza date contratto a giorno intero (00:00 - 23:59) + const contractStart = new Date(site.contractStartDate); + contractStart.setHours(0, 0, 0, 0); + + const contractEnd = new Date(site.contractEndDate); + contractEnd.setHours(23, 59, 59, 999); + + // Normalizza data turno a giorno (per confronto) + const shiftDate = new Date(startTime); + shiftDate.setHours(0, 0, 0, 0); + + // Verifica che il turno sia dentro il periodo contrattuale + if (shiftDate > contractEnd) { + return res.status(400).json({ + message: `Impossibile creare turno: il contratto per il sito "${site.name}" scade il ${new Date(site.contractEndDate).toLocaleDateString('it-IT')}` + }); + } + + if (shiftDate < contractStart) { + return res.status(400).json({ + message: `Impossibile creare turno: il contratto per il sito "${site.name}" inizia il ${new Date(site.contractStartDate).toLocaleDateString('it-IT')}` + }); + } + // Validate and transform the request body const validatedData = insertShiftSchema.parse({ siteId: req.body.siteId,