VigilanzaTurni/client/src/components/app-sidebar.tsx
marco370 bd4a55e001 Add mobile planning interface and backend endpoints
Introduce a new "Planning Mobile" section to the application, including a frontend page (client/src/pages/planning-mobile.tsx) for managing mobile services (patrols, inspections, interventions) and backend API routes (server/routes.ts) to fetch relevant sites and guard availability based on location and date. This also includes updates to the app sidebar (client/src/components/app-sidebar.tsx) and router (client/src/App.tsx) to integrate the new functionality.

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/1nTItRR
2025-10-23 08:52:16 +00:00

220 lines
5.2 KiB
TypeScript

import {
Calendar,
Shield,
MapPin,
Users,
BarChart3,
Bell,
Settings,
LogOut,
UserCog,
ClipboardList,
Car,
Briefcase,
Navigation,
} from "lucide-react";
import { Link, useLocation } from "wouter";
import {
Sidebar,
SidebarContent,
SidebarGroup,
SidebarGroupContent,
SidebarGroupLabel,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
SidebarHeader,
SidebarFooter,
} from "@/components/ui/sidebar";
import { useAuth } from "@/hooks/useAuth";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Button } from "@/components/ui/button";
import { ThemeToggle } from "@/components/theme-toggle";
const menuItems = [
{
title: "Dashboard",
url: "/",
icon: Shield,
roles: ["admin", "coordinator", "guard", "client"],
},
{
title: "Turni",
url: "/shifts",
icon: Calendar,
roles: ["admin", "coordinator", "guard"],
},
{
title: "Pianificazione",
url: "/planning",
icon: ClipboardList,
roles: ["admin", "coordinator"],
},
{
title: "Pianificazione Operativa",
url: "/operational-planning",
icon: Calendar,
roles: ["admin", "coordinator"],
},
{
title: "Planning Fissi",
url: "/general-planning",
icon: BarChart3,
roles: ["admin", "coordinator"],
},
{
title: "Planning Mobile",
url: "/planning-mobile",
icon: Navigation,
roles: ["admin", "coordinator"],
},
{
title: "Planning di Servizio",
url: "/service-planning",
icon: ClipboardList,
roles: ["admin", "coordinator"],
},
{
title: "Gestione Pianificazioni",
url: "/advanced-planning",
icon: ClipboardList,
roles: ["admin", "coordinator"],
},
{
title: "Guardie",
url: "/guards",
icon: Users,
roles: ["admin", "coordinator"],
},
{
title: "Siti",
url: "/sites",
icon: MapPin,
roles: ["admin", "coordinator", "client"],
},
{
title: "Clienti",
url: "/customers",
icon: Briefcase,
roles: ["admin", "coordinator"],
},
{
title: "Servizi",
url: "/services",
icon: Briefcase,
roles: ["admin", "coordinator"],
},
{
title: "Parco Automezzi",
url: "/vehicles",
icon: Car,
roles: ["admin", "coordinator"],
},
{
title: "Report",
url: "/reports",
icon: BarChart3,
roles: ["admin", "coordinator", "client"],
},
{
title: "Notifiche",
url: "/notifications",
icon: Bell,
roles: ["admin", "coordinator", "guard"],
},
{
title: "Utenti",
url: "/users",
icon: UserCog,
roles: ["admin"],
},
{
title: "Parametri",
url: "/parameters",
icon: Settings,
roles: ["admin", "coordinator"],
},
];
export function AppSidebar() {
const { user } = useAuth();
const [location] = useLocation();
const filteredItems = menuItems.filter(
(item) => user && item.roles.includes(user.role)
);
return (
<Sidebar>
<SidebarHeader className="p-4 border-b">
<div className="flex items-center gap-3">
<Shield className="h-8 w-8 text-primary" />
<div>
<h1 className="text-lg font-semibold">VigilanzaTurni</h1>
<p className="text-xs text-muted-foreground">Sistema Gestione</p>
</div>
</div>
</SidebarHeader>
<SidebarContent>
<SidebarGroup>
<SidebarGroupLabel>Menu Principale</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
{filteredItems.map((item) => (
<SidebarMenuItem key={item.title}>
<SidebarMenuButton
asChild
isActive={location === item.url}
data-testid={`link-${item.title.toLowerCase()}`}
>
<Link href={item.url}>
<item.icon className="h-4 w-4" />
<span>{item.title}</span>
</Link>
</SidebarMenuButton>
</SidebarMenuItem>
))}
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
</SidebarContent>
<SidebarFooter className="p-4 border-t space-y-4">
<div className="flex items-center justify-between gap-3">
<div className="flex items-center gap-3 min-w-0">
<Avatar className="h-8 w-8">
<AvatarImage src={user?.profileImageUrl || undefined} />
<AvatarFallback>
{user?.firstName?.[0]}{user?.lastName?.[0]}
</AvatarFallback>
</Avatar>
<div className="min-w-0">
<p className="text-sm font-medium truncate">
{user?.firstName} {user?.lastName}
</p>
<p className="text-xs text-muted-foreground capitalize">
{user?.role}
</p>
</div>
</div>
<ThemeToggle />
</div>
<div className="flex gap-2">
<Button
variant="outline"
size="sm"
className="flex-1"
onClick={() => window.location.href = '/api/logout'}
data-testid="button-logout"
>
<LogOut className="h-4 w-4 mr-2" />
Esci
</Button>
</div>
</SidebarFooter>
</Sidebar>
);
}