From 500da807cfdc801561822a702f87013ea1b826db Mon Sep 17 00:00:00 2001 From: marco370 <48531002-marco370@users.noreply.replit.com> Date: Fri, 17 Oct 2025 07:55:17 +0000 Subject: [PATCH] Add a new page for managing different types of security services Implement the Services page with routing, navigation, and data fetching for service statistics. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 42d8028a-fa71-4ec2-938c-e43eedf7df01 Replit-Commit-Checkpoint-Type: intermediate_checkpoint Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/6d543d2c-20b9-4ea6-93fe-70fe9b1d9f80/42d8028a-fa71-4ec2-938c-e43eedf7df01/kxc8yZp --- .replit | 4 + client/src/App.tsx | 2 + client/src/components/app-sidebar.tsx | 7 + client/src/pages/services.tsx | 189 ++++++++++++++++++++++++++ 4 files changed, 202 insertions(+) create mode 100644 client/src/pages/services.tsx diff --git a/.replit b/.replit index 048618b..239b456 100644 --- a/.replit +++ b/.replit @@ -19,6 +19,10 @@ externalPort = 80 localPort = 33035 externalPort = 3001 +[[ports]] +localPort = 36589 +externalPort = 3003 + [[ports]] localPort = 41343 externalPort = 3000 diff --git a/client/src/App.tsx b/client/src/App.tsx index bf62c07..9ce1ec6 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -20,6 +20,7 @@ import Users from "@/pages/users"; import Planning from "@/pages/planning"; import Vehicles from "@/pages/vehicles"; import Parameters from "@/pages/parameters"; +import Services from "@/pages/services"; function Router() { const { isAuthenticated, isLoading } = useAuth(); @@ -34,6 +35,7 @@ function Router() { + diff --git a/client/src/components/app-sidebar.tsx b/client/src/components/app-sidebar.tsx index a258eaa..6556728 100644 --- a/client/src/components/app-sidebar.tsx +++ b/client/src/components/app-sidebar.tsx @@ -10,6 +10,7 @@ import { UserCog, ClipboardList, Car, + Briefcase, } from "lucide-react"; import { Link, useLocation } from "wouter"; import { @@ -60,6 +61,12 @@ const menuItems = [ icon: MapPin, roles: ["admin", "coordinator", "client"], }, + { + title: "Servizi", + url: "/services", + icon: Briefcase, + roles: ["admin", "coordinator"], + }, { title: "Parco Automezzi", url: "/vehicles", diff --git a/client/src/pages/services.tsx b/client/src/pages/services.tsx new file mode 100644 index 0000000..687da72 --- /dev/null +++ b/client/src/pages/services.tsx @@ -0,0 +1,189 @@ +import { useQuery } from "@tanstack/react-query"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { Badge } from "@/components/ui/badge"; +import { Site } from "@shared/schema"; +import { Building2, Shield, Eye, MapPin, Zap } from "lucide-react"; + +const serviceTypeInfo = { + fixed_post: { + label: "Presidio Fisso", + description: "Guardia fissa presso una struttura", + icon: Building2, + color: "bg-blue-500/10 text-blue-500 border-blue-500/20" + }, + patrol: { + label: "Pattugliamento", + description: "Ronde e controlli su area", + icon: Eye, + color: "bg-green-500/10 text-green-500 border-green-500/20" + }, + night_inspection: { + label: "Ispettorato Notturno", + description: "Controlli notturni programmati", + icon: Shield, + color: "bg-purple-500/10 text-purple-500 border-purple-500/20" + }, + quick_response: { + label: "Pronto Intervento", + description: "Intervento rapido su chiamata", + icon: Zap, + color: "bg-orange-500/10 text-orange-500 border-orange-500/20" + } +} as const; + +export default function Services() { + const { data: sites = [], isLoading } = useQuery({ + queryKey: ["/api/sites"], + }); + + // Calculate statistics per service type + const stats = Object.keys(serviceTypeInfo).reduce((acc, type) => { + const sitesForType = sites.filter(s => s.shiftType === type); + acc[type] = { + total: sitesForType.length, + active: sitesForType.filter(s => s.isActive).length, + requiresArmed: sitesForType.filter(s => s.requiresArmed).length, + requiresDriver: sitesForType.filter(s => s.requiresDriverLicense).length, + }; + return acc; + }, {} as Record); + + return ( +
+
+

Gestione Servizi

+

+ Panoramica tipologie di servizio e relative configurazioni +

+
+ + {isLoading ? ( +
Caricamento servizi...
+ ) : ( +
+ {Object.entries(serviceTypeInfo).map(([type, info]) => { + const Icon = info.icon; + const stat = stats[type] || { total: 0, active: 0, requiresArmed: 0, requiresDriver: 0 }; + + return ( + + +
+
+
+ +
+
+ {info.label} + {info.description} +
+
+
+
+ +
+
+

Siti Totali

+

+ {stat.total} +

+
+
+

Attivi

+

+ {stat.active} +

+
+
+

Richiedono Armati

+

+ {stat.requiresArmed} +

+
+
+

Richiedono Patente

+

+ {stat.requiresDriver} +

+
+
+ +
+
+ + + {sites.filter(s => s.shiftType === type && s.location === 'roccapiemonte').length} Roccapiemonte + + + + {sites.filter(s => s.shiftType === type && s.location === 'milano').length} Milano + + + + {sites.filter(s => s.shiftType === type && s.location === 'roma').length} Roma + +
+
+
+
+ ); + })} +
+ )} + + + + Informazioni Tipologie Servizio + Caratteristiche e utilizzo delle diverse tipologie + + +
+
+ +
+

Presidio Fisso

+

+ Utilizzato per siti che richiedono sorveglianza continua con presenza fissa delle guardie. + Ideale per banche, musei, uffici pubblici. +

+
+
+ +
+ +
+

Pattugliamento

+

+ Servizio di ronde mobili su area estesa. Le guardie effettuano controlli periodici + seguendo percorsi predefiniti. Richiede spesso patente di guida. +

+
+
+ +
+ +
+

Ispettorato Notturno

+

+ Controlli specifici durante le ore notturne. Prevede verifiche programmate + di sicurezza e aperture/chiusure di strutture. +

+
+
+ +
+ +
+

Pronto Intervento

+

+ Servizio di intervento rapido su chiamata. Le guardie devono essere disponibili + per interventi urgenti, spesso armati e con veicolo dedicato. +

+
+
+
+
+
+
+ ); +}