+ {/* Header */}
+
+
+
+ Planning per Sito
+
+
+ Visualizza tutti gli agenti assegnati a un sito con dotazioni
+
+
+
+
+ {/* Selettore sito */}
+
+
+ Seleziona Sito
+
+
+
+
+
+ {selectedSite && (
+
+
{selectedSite.name}
+
{selectedSite.address}
+
+ )}
+
+
+
+
+ {/* Navigazione settimana */}
+ {selectedSiteId && (
+
+
+
+
+
+ {format(currentWeekStart, "dd MMMM", { locale: it })} -{" "}
+ {format(addDays(currentWeekStart, 6), "dd MMMM yyyy", { locale: it })}
+
+
+
+
+
+ )}
+
+ {/* Loading state */}
+ {isLoading && (
+
+
+ Caricamento planning sito...
+
+
+ )}
+
+ {/* Griglia giorni settimana */}
+ {selectedSiteId && !isLoading && (
+
+ {weekDays.map((day) => {
+ const dateStr = format(day, "yyyy-MM-dd");
+ const dayGuards = planningByDay[dateStr] || [];
+
+ return (
+
+
+
+ {format(day, "EEEE dd/MM", { locale: it })}
+
+
+ {dayGuards.length === 0
+ ? "Nessun agente"
+ : `${dayGuards.length} agente${dayGuards.length > 1 ? "i" : ""}`}
+
+
+
+ {dayGuards.length === 0 ? (
+
+ Nessuna copertura
+
+ ) : (
+ dayGuards.map((guard, index) => {
+ // Parsing sicuro orari
+ let startTime = "N/A";
+ let endTime = "N/A";
+
+ if (guard.plannedStartTime) {
+ const parsedStart = parseISO(guard.plannedStartTime);
+ if (isValid(parsedStart)) {
+ startTime = format(parsedStart, "HH:mm");
+ }
+ }
+
+ if (guard.plannedEndTime) {
+ const parsedEnd = parseISO(guard.plannedEndTime);
+ if (isValid(parsedEnd)) {
+ endTime = format(parsedEnd, "HH:mm");
+ }
+ }
+
+ return (
+
+
+
+
+
+ {guard.guardName}
+
+
+ Matricola: {guard.badgeNumber}
+
+
+
+
+
+
+
+ {startTime} - {endTime}
+
+
+
+ {/* Dotazioni */}
+
+ {guard.armed && (
+
+
+ Armato
+
+ )}
+ {guard.vehicleId && (
+
+
+ {guard.vehiclePlate || "Automezzo"}
+
+ )}
+
+
+ );
+ })
+ )}
+
+
+ );
+ })}
+
+ )}
+
+ );
+}
diff --git a/replit.md b/replit.md
index cf41ac5..467b9da 100644
--- a/replit.md
+++ b/replit.md
@@ -120,6 +120,28 @@ To prevent timezone-related bugs, especially when assigning shifts, dates should
- Card display now shows service type label from database instead of hardcoded labels
- **Impact**: Sites now correctly reference service types configured in "Tipologie Servizi" page, ensuring consistency across the system
+### Advanced Planning System Complete (October 23, 2025)
+- **Implementation**: Full planning system with guard views, exclusivity constraints, and database persistence
+- **Features**:
+ - **Patrol Route Database Persistence**:
+ - Backend endpoints: GET/POST/PUT/DELETE `/api/patrol-routes`
+ - Database schema: `patrol_routes` table with `patrol_route_stops` for sequence
+ - Planning Mobile loads existing routes when guard selected, saves to DB
+ - Green markers on map for sites in current patrol route
+ - **Exclusivity Constraint (fisso/mobile)**:
+ - Validation in 3 backend endpoints: POST `/api/patrol-routes`, POST `/api/shift-assignments`, POST `/api/shifts/:shiftId/assignments`
+ - Guards cannot be assigned to both fixed posts and mobile patrols on same date
+ - Clear error messages when constraint violated
+ - **Guard Planning Views**:
+ - `/my-shifts-fixed`: Guards view their fixed post shifts with orari, dotazioni (armato, automezzo), location, sito
+ - `/my-shifts-mobile`: Guards view patrol routes with sequenced site list, addresses, vehicle assignment
+ - Backend endpoints: GET `/api/my-shifts/fixed`, GET `/api/my-shifts/mobile` with date range filters
+ - **Site Planning View**:
+ - `/site-planning-view`: Coordinators view all guards assigned to a site across a week
+ - Shows guard name, badge, orari, dotazioni for each assignment
+ - Backend endpoint: GET `/api/site-planning/:siteId` with date range filters
+- **Impact**: Complete end-to-end planning system supporting both coordinator and guard roles with database-backed route planning and operational equipment tracking
+
## External Dependencies
- **Replit Auth**: For OpenID Connect (OIDC) based authentication.
- **Neon**: Managed PostgreSQL database service.
diff --git a/server/routes.ts b/server/routes.ts
index 84ad193..1e0b999 100644
--- a/server/routes.ts
+++ b/server/routes.ts
@@ -2756,6 +2756,250 @@ export async function registerRoutes(app: Express): Promise