Organize the main navigation menu with new sub-menus and updated labels

Refactor the `app-sidebar.tsx` component to restructure the navigation menu, introducing collapsible sub-menus for planning and master data, and renaming several menu items.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: e0b5b11c-5b75-4389-8ea9-5f3cd9332f88
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/6d543d2c-20b9-4ea6-93fe-70fe9b1d9f80/e0b5b11c-5b75-4389-8ea9-5f3cd9332f88/EPTvOHB
This commit is contained in:
marco370 2025-10-29 08:08:37 +00:00
parent 3e3b9851a8
commit 7961971ad0
2 changed files with 191 additions and 93 deletions

View File

@ -19,6 +19,10 @@ externalPort = 80
localPort = 33035 localPort = 33035
externalPort = 3001 externalPort = 3001
[[ports]]
localPort = 40417
externalPort = 8000
[[ports]] [[ports]]
localPort = 41295 localPort = 41295
externalPort = 5173 externalPort = 5173

View File

@ -12,6 +12,11 @@ import {
Car, Car,
Briefcase, Briefcase,
Navigation, Navigation,
ChevronDown,
FileText,
FolderKanban,
Building2,
Wrench,
} from "lucide-react"; } from "lucide-react";
import { Link, useLocation } from "wouter"; import { Link, useLocation } from "wouter";
import { import {
@ -23,15 +28,31 @@ import {
SidebarMenu, SidebarMenu,
SidebarMenuButton, SidebarMenuButton,
SidebarMenuItem, SidebarMenuItem,
SidebarMenuSub,
SidebarMenuSubItem,
SidebarMenuSubButton,
SidebarHeader, SidebarHeader,
SidebarFooter, SidebarFooter,
} from "@/components/ui/sidebar"; } from "@/components/ui/sidebar";
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "@/components/ui/collapsible";
import { useAuth } from "@/hooks/useAuth"; import { useAuth } from "@/hooks/useAuth";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { ThemeToggle } from "@/components/theme-toggle"; import { ThemeToggle } from "@/components/theme-toggle";
const menuItems = [ interface MenuItem {
title: string;
url?: string;
icon: any;
roles: string[];
items?: MenuItem[];
}
const menuItems: MenuItem[] = [
{ {
title: "Dashboard", title: "Dashboard",
url: "/", url: "/",
@ -39,47 +60,41 @@ const menuItems = [
roles: ["admin", "coordinator", "guard", "client"], roles: ["admin", "coordinator", "guard", "client"],
}, },
{ {
title: "Turni", title: "Planning",
url: "/shifts", icon: FolderKanban,
icon: Calendar,
roles: ["admin", "coordinator", "guard"],
},
{
title: "Pianificazione",
url: "/planning",
icon: ClipboardList,
roles: ["admin", "coordinator"], roles: ["admin", "coordinator"],
}, items: [
{ {
title: "Pianificazione Operativa", title: "Fissi",
url: "/operational-planning",
icon: Calendar,
roles: ["admin", "coordinator"],
},
{
title: "Planning Fissi",
url: "/general-planning", url: "/general-planning",
icon: BarChart3, icon: Calendar,
roles: ["admin", "coordinator"], roles: ["admin", "coordinator"],
}, },
{ {
title: "Planning Mobile", title: "Mobili",
url: "/planning-mobile", url: "/planning-mobile",
icon: Navigation, icon: Navigation,
roles: ["admin", "coordinator"], roles: ["admin", "coordinator"],
}, },
{ {
title: "Planning di Servizio", title: "Vista",
url: "/service-planning", url: "/service-planning",
icon: ClipboardList, icon: ClipboardList,
roles: ["admin", "coordinator"], roles: ["admin", "coordinator"],
}, },
],
},
{ {
title: "Gestione Pianificazioni", title: "Scadenziario",
url: "/advanced-planning", url: "/advanced-planning",
icon: ClipboardList, icon: Calendar,
roles: ["admin", "coordinator"], roles: ["admin", "coordinator"],
}, },
{
title: "Anagrafiche",
icon: Building2,
roles: ["admin", "coordinator"],
items: [
{ {
title: "Guardie", title: "Guardie",
url: "/guards", url: "/guards",
@ -98,6 +113,19 @@ const menuItems = [
icon: Briefcase, icon: Briefcase,
roles: ["admin", "coordinator"], roles: ["admin", "coordinator"],
}, },
{
title: "Automezzi",
url: "/vehicles",
icon: Car,
roles: ["admin", "coordinator"],
},
],
},
{
title: "Tipologia",
icon: Wrench,
roles: ["admin", "coordinator"],
items: [
{ {
title: "Servizi", title: "Servizi",
url: "/services", url: "/services",
@ -105,23 +133,31 @@ const menuItems = [
roles: ["admin", "coordinator"], roles: ["admin", "coordinator"],
}, },
{ {
title: "Parco Automezzi", title: "Contratti",
url: "/vehicles", url: "/parameters",
icon: Car, icon: Settings,
roles: ["admin", "coordinator"], roles: ["admin", "coordinator"],
}, },
],
},
{ {
title: "Report", title: "Report",
url: "/reports",
icon: BarChart3, icon: BarChart3,
roles: ["admin", "coordinator", "client"], roles: ["admin", "coordinator", "client"],
items: [
{
title: "Report Amministrativo",
url: "/reports",
icon: FileText,
roles: ["admin", "coordinator", "client"],
},
],
}, },
{ {
title: "Notifiche", title: "Utilità",
url: "/notifications", icon: Settings,
icon: Bell,
roles: ["admin", "coordinator", "guard"], roles: ["admin", "coordinator", "guard"],
}, items: [
{ {
title: "Utenti", title: "Utenti",
url: "/users", url: "/users",
@ -129,10 +165,12 @@ const menuItems = [
roles: ["admin"], roles: ["admin"],
}, },
{ {
title: "Parametri", title: "Notifiche",
url: "/parameters", url: "/notifications",
icon: Settings, icon: Bell,
roles: ["admin", "coordinator"], roles: ["admin", "coordinator", "guard"],
},
],
}, },
]; ];
@ -140,9 +178,78 @@ export function AppSidebar() {
const { user } = useAuth(); const { user } = useAuth();
const [location] = useLocation(); const [location] = useLocation();
const filteredItems = menuItems.filter( const filterMenuItems = (items: MenuItem[]): MenuItem[] => {
(item) => user && item.roles.includes(user.role) if (!user) return [];
return items.filter((item) => {
const hasRole = item.roles.includes(user.role);
if (!hasRole) return false;
if (item.items) {
item.items = filterMenuItems(item.items);
return item.items.length > 0;
}
return true;
});
};
const filteredItems = filterMenuItems(menuItems);
const renderMenuItem = (item: MenuItem) => {
// Menu item con sottomenu
if (item.items && item.items.length > 0) {
const isAnySubItemActive = item.items.some((subItem) => location === subItem.url);
return (
<Collapsible key={item.title} defaultOpen={isAnySubItemActive} className="group/collapsible">
<SidebarMenuItem>
<CollapsibleTrigger asChild>
<SidebarMenuButton data-testid={`menu-${item.title.toLowerCase()}`}>
<item.icon className="h-4 w-4" />
<span>{item.title}</span>
<ChevronDown className="ml-auto h-4 w-4 transition-transform group-data-[state=open]/collapsible:rotate-180" />
</SidebarMenuButton>
</CollapsibleTrigger>
<CollapsibleContent>
<SidebarMenuSub>
{item.items.map((subItem) => (
<SidebarMenuSubItem key={subItem.title}>
<SidebarMenuSubButton
asChild
isActive={location === subItem.url}
data-testid={`link-${subItem.title.toLowerCase()}`}
>
<Link href={subItem.url!}>
<subItem.icon className="h-4 w-4" />
<span>{subItem.title}</span>
</Link>
</SidebarMenuSubButton>
</SidebarMenuSubItem>
))}
</SidebarMenuSub>
</CollapsibleContent>
</SidebarMenuItem>
</Collapsible>
); );
}
// Menu item semplice
return (
<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>
);
};
return ( return (
<Sidebar> <Sidebar>
@ -161,20 +268,7 @@ export function AppSidebar() {
<SidebarGroupLabel>Menu Principale</SidebarGroupLabel> <SidebarGroupLabel>Menu Principale</SidebarGroupLabel>
<SidebarGroupContent> <SidebarGroupContent>
<SidebarMenu> <SidebarMenu>
{filteredItems.map((item) => ( {filteredItems.map(renderMenuItem)}
<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> </SidebarMenu>
</SidebarGroupContent> </SidebarGroupContent>
</SidebarGroup> </SidebarGroup>