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,