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
externalPort = 3001
[[ports]]
localPort = 40417
externalPort = 8000
[[ports]]
localPort = 41295
externalPort = 5173

View File

@ -12,6 +12,11 @@ import {
Car,
Briefcase,
Navigation,
ChevronDown,
FileText,
FolderKanban,
Building2,
Wrench,
} from "lucide-react";
import { Link, useLocation } from "wouter";
import {
@ -23,15 +28,31 @@ import {
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
SidebarMenuSub,
SidebarMenuSubItem,
SidebarMenuSubButton,
SidebarHeader,
SidebarFooter,
} from "@/components/ui/sidebar";
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "@/components/ui/collapsible";
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 = [
interface MenuItem {
title: string;
url?: string;
icon: any;
roles: string[];
items?: MenuItem[];
}
const menuItems: MenuItem[] = [
{
title: "Dashboard",
url: "/",
@ -39,100 +60,117 @@ const menuItems = [
roles: ["admin", "coordinator", "guard", "client"],
},
{
title: "Turni",
url: "/shifts",
icon: Calendar,
roles: ["admin", "coordinator", "guard"],
},
{
title: "Pianificazione",
url: "/planning",
icon: ClipboardList,
title: "Planning",
icon: FolderKanban,
roles: ["admin", "coordinator"],
items: [
{
title: "Fissi",
url: "/general-planning",
icon: Calendar,
roles: ["admin", "coordinator"],
},
{
title: "Mobili",
url: "/planning-mobile",
icon: Navigation,
roles: ["admin", "coordinator"],
},
{
title: "Vista",
url: "/service-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",
title: "Scadenziario",
url: "/advanced-planning",
icon: ClipboardList,
icon: Calendar,
roles: ["admin", "coordinator"],
},
{
title: "Guardie",
url: "/guards",
icon: Users,
title: "Anagrafiche",
icon: Building2,
roles: ["admin", "coordinator"],
items: [
{
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: "Automezzi",
url: "/vehicles",
icon: Car,
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,
title: "Tipologia",
icon: Wrench,
roles: ["admin", "coordinator"],
items: [
{
title: "Servizi",
url: "/services",
icon: Briefcase,
roles: ["admin", "coordinator"],
},
{
title: "Contratti",
url: "/parameters",
icon: Settings,
roles: ["admin", "coordinator"],
},
],
},
{
title: "Report",
url: "/reports",
icon: BarChart3,
roles: ["admin", "coordinator", "client"],
items: [
{
title: "Report Amministrativo",
url: "/reports",
icon: FileText,
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",
title: "Utilità",
icon: Settings,
roles: ["admin", "coordinator"],
roles: ["admin", "coordinator", "guard"],
items: [
{
title: "Utenti",
url: "/users",
icon: UserCog,
roles: ["admin"],
},
{
title: "Notifiche",
url: "/notifications",
icon: Bell,
roles: ["admin", "coordinator", "guard"],
},
],
},
];
@ -140,9 +178,78 @@ export function AppSidebar() {
const { user } = useAuth();
const [location] = useLocation();
const filteredItems = menuItems.filter(
(item) => user && item.roles.includes(user.role)
);
const filterMenuItems = (items: MenuItem[]): MenuItem[] => {
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 (
<Sidebar>
@ -161,20 +268,7 @@ export function AppSidebar() {
<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>
))}
{filteredItems.map(renderMenuItem)}
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>