|
- import { useSession } from "next-auth/react";
- import Box from "@mui/material/Box";
- import React from "react";
- import List from "@mui/material/List";
- import ListItemButton from "@mui/material/ListItemButton";
- import ListItemText from "@mui/material/ListItemText";
- import ListItemIcon from "@mui/material/ListItemIcon";
- import Dashboard from "@mui/icons-material/Dashboard";
- import Storefront from "@mui/icons-material/Storefront";
- import LocalShipping from "@mui/icons-material/LocalShipping";
- import Assignment from "@mui/icons-material/Assignment";
- import Inventory from "@mui/icons-material/Inventory";
- import AssignmentTurnedIn from "@mui/icons-material/AssignmentTurnedIn";
- import ReportProblem from "@mui/icons-material/ReportProblem";
- import QrCodeIcon from "@mui/icons-material/QrCode";
- import ViewModule from "@mui/icons-material/ViewModule";
- import Description from "@mui/icons-material/Description";
- import CalendarMonth from "@mui/icons-material/CalendarMonth";
- import Factory from "@mui/icons-material/Factory";
- import PostAdd from "@mui/icons-material/PostAdd";
- import Kitchen from "@mui/icons-material/Kitchen";
- import Inventory2 from "@mui/icons-material/Inventory2";
- import Print from "@mui/icons-material/Print";
- import Assessment from "@mui/icons-material/Assessment";
- import Settings from "@mui/icons-material/Settings";
- import Person from "@mui/icons-material/Person";
- import Group from "@mui/icons-material/Group";
- import Category from "@mui/icons-material/Category";
- import TrendingUp from "@mui/icons-material/TrendingUp";
- import Build from "@mui/icons-material/Build";
- import Warehouse from "@mui/icons-material/Warehouse";
- import VerifiedUser from "@mui/icons-material/VerifiedUser";
- import Label from "@mui/icons-material/Label";
- import Checklist from "@mui/icons-material/Checklist";
- import Science from "@mui/icons-material/Science";
- import UploadFile from "@mui/icons-material/UploadFile";
- import { useTranslation } from "react-i18next";
- import { usePathname } from "next/navigation";
- import Link from "next/link";
- import { NAVIGATION_CONTENT_WIDTH } from "@/config/uiConfig";
- import Logo from "../Logo";
- import { AUTH } from "../../authorities";
-
- interface NavigationItem {
- icon: React.ReactNode;
- label: string;
- path: string;
- children?: NavigationItem[];
- isHidden?: boolean | undefined;
- requiredAbility?: string | string[];
- }
-
- const NavigationContent: React.FC = () => {
- const { data: session, status } = useSession();
- const abilities = session?.user?.abilities ?? [];
-
- // Helper: check if user has required permission
- const hasAbility = (required?: string | string[]): boolean => {
- if (!required) return true; // no requirement → always show
- if (Array.isArray(required)) {
- return required.some(ability => abilities.includes(ability));
- }
- return abilities.includes(required);
- };
-
- const navigationItems: NavigationItem[] = [
- {
- icon: <Dashboard />,
- label: "Dashboard",
- path: "/dashboard",
- },
- {
- icon: <Storefront />,
- label: "Store Management",
- path: "",
- requiredAbility: [AUTH.PURCHASE, AUTH.STOCK, AUTH.STOCK_TAKE, AUTH.STOCK_FG, AUTH.STOCK_IN_BIND, AUTH.ADMIN],
- children: [
- {
- icon: <LocalShipping />,
- label: "Purchase Order",
- requiredAbility: [AUTH.PURCHASE, AUTH.ADMIN],
- path: "/po",
- },
- {
- icon: <Assignment />,
- label: "Pick Order",
- requiredAbility: [AUTH.STOCK, AUTH.ADMIN],
- path: "/pickOrder",
- },
- {
- icon: <Inventory />,
- label: "View item In-out And inventory Ledger",
- requiredAbility: [AUTH.STOCK, AUTH.ADMIN],
- path: "/inventory",
- },
- {
- icon: <AssignmentTurnedIn />,
- label: "Stock Take Management",
- requiredAbility: [AUTH.STOCK, AUTH.STOCK_TAKE, AUTH.ADMIN],
- path: "/stocktakemanagement",
- },
- {
- icon: <ReportProblem />,
- label: "Stock Issue",
- requiredAbility: [AUTH.STOCK, AUTH.STOCK_TAKE, AUTH.ADMIN],
- path: "/stockIssue",
- },
- {
- icon: <QrCodeIcon />,
- label: "Put Away Scan",
- requiredAbility: [AUTH.STOCK, AUTH.STOCK_TAKE, AUTH.STOCK_IN_BIND, AUTH.ADMIN],
- path: "/putAway",
- },
- {
- icon: <ViewModule />,
- label: "Finished Good Order",
- requiredAbility: [AUTH.STOCK_FG, AUTH.ADMIN],
- path: "/finishedGood",
- },
- {
- icon: <Description />,
- label: "Stock Record",
- requiredAbility: [AUTH.STOCK, AUTH.STOCK_TAKE, AUTH.STOCK_IN_BIND, AUTH.STOCK_FG, AUTH.ADMIN],
- path: "/stockRecord",
- },
- ],
- },
- {
- icon: <LocalShipping />,
- label: "Delivery Order",
- path: "/do",
- requiredAbility: [AUTH.STOCK_FG, AUTH.ADMIN],
- },
- {
- icon: <CalendarMonth />,
- label: "Scheduling",
- path: "/ps",
- requiredAbility: [AUTH.FORECAST, AUTH.ADMIN],
- isHidden: false,
- },
- {
- icon: <Factory />,
- label: "Management Job Order",
- path: "",
- requiredAbility: [AUTH.JOB_CREATE, AUTH.JOB_PICK, AUTH.JOB_PROD, AUTH.ADMIN],
- children: [
- {
- icon: <PostAdd />,
- label: "Search Job Order/ Create Job Order",
- requiredAbility: [AUTH.JOB_CREATE, AUTH.ADMIN],
- path: "/jo",
- },
- {
- icon: <Inventory />,
- label: "Job Order Pickexcution",
- requiredAbility: [AUTH.JOB_PICK, AUTH.JOB_MAT, AUTH.ADMIN],
- path: "/jodetail",
- },
- {
- icon: <Kitchen />,
- label: "Job Order Production Process",
- requiredAbility: [AUTH.JOB_PROD, AUTH.ADMIN],
- path: "/productionProcess",
- },
- {
- icon: <Inventory2 />,
- label: "Bag Usage",
- requiredAbility: [AUTH.JOB_PROD, AUTH.ADMIN],
- path: "/bag",
- },
- ],
- },
- {
- icon: <Print />,
- label: "打袋機列印",
- path: "/testing",
- requiredAbility: [AUTH.TESTING, AUTH.ADMIN],
- isHidden: false,
- },
- {
- icon: <Assessment />,
- label: "報告管理",
- path: "/report",
- requiredAbility: [AUTH.TESTING, AUTH.ADMIN],
- isHidden: false,
- },
- {
- icon: <Settings />,
- label: "Settings",
- path: "",
- requiredAbility: [AUTH.VIEW_USER, AUTH.ADMIN],
- children: [
- {
- icon: <Person />,
- label: "User",
- path: "/settings/user",
- requiredAbility: [AUTH.VIEW_USER, AUTH.ADMIN],
- },
- //{
- // icon: <Group />,
- // label: "User Group",
- // path: "/settings/user",
- // requiredAbility: [AUTH.VIEW_GROUP, AUTH.ADMIN],
- //},
- {
- icon: <Category />,
- label: "Items",
- path: "/settings/items",
- },
- {
- icon: <ViewModule />,
- label: "BOM Weighting Score List",
- path: "/settings/bomWeighting",
- },
- {
- icon: <Storefront />,
- label: "ShopAndTruck",
- path: "/settings/shop",
- },
- {
- icon: <TrendingUp />,
- label: "Demand Forecast Setting",
- path: "/settings/rss",
- },
- {
- icon: <Build />,
- label: "Equipment",
- path: "/settings/equipment",
- },
- {
- icon: <Warehouse />,
- label: "Warehouse",
- path: "/settings/warehouse",
- },
- {
- icon: <Print />,
- label: "Printer",
- path: "/settings/printer",
- },
- //{
- // icon: <Person />,
- // label: "Customer",
- // path: "/settings/user",
- //},
- {
- icon: <VerifiedUser />,
- label: "QC Check Item",
- path: "/settings/qcItem",
- },
- {
- icon: <Label />,
- label: "QC Category",
- path: "/settings/qcCategory",
- },
- {
- icon: <Checklist />,
- label: "QC Item All",
- path: "/settings/qcItemAll",
- },
- {
- icon: <QrCodeIcon />,
- label: "QR Code Handle",
- path: "/settings/qrCodeHandle",
- },
- {
- icon: <Science />,
- label: "Import Testing",
- path: "/settings/m18ImportTesting",
- },
- {
- icon: <UploadFile />,
- label: "Import Excel",
- path: "/settings/importExcel",
- },
- {
- icon: <UploadFile />,
- label: "Import BOM",
- path: "/settings/importBom",
- },
- ],
- },
- ];
- const { t } = useTranslation("common");
- const pathname = usePathname();
-
- const [openItems, setOpenItems] = React.useState<string[]>([]);
- const toggleItem = (label: string) => {
- setOpenItems((prevOpenItems) =>
- prevOpenItems.includes(label)
- ? prevOpenItems.filter((item) => item !== label)
- : [...prevOpenItems, label],
- );
- };
-
- const renderNavigationItem = (item: NavigationItem) => {
- if (!hasAbility(item.requiredAbility)) {
- return null;
- }
-
- const isOpen = openItems.includes(item.label);
- const hasVisibleChildren = item.children?.some(child => hasAbility(child.requiredAbility));
- const isLeaf = Boolean(item.path);
- const isSelected = isLeaf && item.path
- ? pathname === item.path || pathname.startsWith(item.path + "/")
- : hasVisibleChildren && item.children?.some(
- (c) => c.path && (pathname === c.path || pathname.startsWith(c.path + "/"))
- );
-
- const content = (
- <ListItemButton
- selected={isSelected}
- onClick={isLeaf ? undefined : () => toggleItem(item.label)}
- sx={{
- mx: 1,
- "&.Mui-selected .MuiListItemIcon-root": { color: "primary.main" },
- }}
- >
- <ListItemIcon sx={{ minWidth: 40 }}>{item.icon}</ListItemIcon>
- <ListItemText
- primary={t(item.label)}
- primaryTypographyProps={{ fontWeight: isSelected ? 600 : 500 }}
- />
- </ListItemButton>
- );
-
- return (
- <Box key={`${item.label}-${item.path}`}>
- {isLeaf ? (
- <Link href={item.path!} style={{ textDecoration: "none", color: "inherit" }}>
- {content}
- </Link>
- ) : (
- content
- )}
- {item.children && isOpen && hasVisibleChildren && (
- <List sx={{ pl: 2, py: 0 }}>
- {item.children.map(
- (child) => !child.isHidden && hasAbility(child.requiredAbility) && (
- <Box
- key={`${child.label}-${child.path}`}
- component={Link}
- href={child.path}
- sx={{ textDecoration: "none", color: "inherit" }}
- >
- <ListItemButton
- selected={pathname === child.path || (child.path && pathname.startsWith(child.path + "/"))}
- sx={{
- mx: 1,
- py: 1,
- "&.Mui-selected .MuiListItemIcon-root": { color: "primary.main" },
- }}
- >
- <ListItemIcon sx={{ minWidth: 40 }}>{child.icon}</ListItemIcon>
- <ListItemText
- primary={t(child.label)}
- primaryTypographyProps={{
- fontWeight: pathname === child.path || (child.path && pathname.startsWith(child.path + "/")) ? 600 : 500,
- fontSize: "0.875rem",
- }}
- />
- </ListItemButton>
- </Box>
- ),
- )}
- </List>
- )}
- </Box>
- );
- };
-
- if (status === "loading") {
- return <Box sx={{ width: NAVIGATION_CONTENT_WIDTH, p: 3 }}>Loading...</Box>;
- }
-
- return (
- <Box sx={{ width: NAVIGATION_CONTENT_WIDTH, height: "100%", display: "flex", flexDirection: "column" }}>
- <Box
- className="bg-gradient-to-br from-blue-500/15 via-slate-100 to-slate-50 dark:from-blue-500/20 dark:via-slate-800 dark:to-slate-900"
- sx={{
- mx: 1,
- mt: 1,
- mb: 1,
- px: 1.5,
- py: 2,
- flexShrink: 0,
- display: "flex",
- alignItems: "center",
- justifyContent: "flex-start",
- minHeight: 56,
- }}
- >
- <Logo height={42} />
- </Box>
- <Box sx={{ borderTop: 1, borderColor: "divider" }} />
- <List component="nav" sx={{ flex: 1, overflow: "auto", py: 1, px: 0 }}>
- {navigationItems
- .filter(item => !item.isHidden)
- .map(renderNavigationItem)
- .filter(Boolean)}
- </List>
- </Box>
- );
- };
-
- export default NavigationContent;
|