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:
parent
3e3b9851a8
commit
7961971ad0
4
.replit
4
.replit
@ -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
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user