|
- import { useSession } from "next-auth/react";
- import Box from "@mui/material/Box";
- import Stack from "@mui/material/Stack";
- import Typography from "@mui/material/Typography";
- 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 ShowChart from "@mui/icons-material/ShowChart";
- 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 Sync from "@mui/icons-material/Sync";
- import Layers from "@mui/icons-material/Layers";
- import Devices from "@mui/icons-material/Devices";
- 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";
- import { isMonitoringEnabled } from "@/config/monitoring";
- import PurchaseStockInNavAlerts from "./PurchaseStockInNavAlerts";
- import JobOrderFgStockInNavAlerts from "./JobOrderFgStockInNavAlerts";
- import MasterDataIssuesNavBadge from "./MasterDataIssuesNavBadge";
-
- interface NavigationItem {
- id: string;
- icon: React.ReactNode;
- labelKey: 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[] = [
- {
- id: "nav.dashboard",
- icon: <Dashboard />,
- labelKey: "nav.dashboard",
- path: "/dashboard",
- },
- {
- id: "nav.storeManagement",
- icon: <Storefront />,
- labelKey: "nav.storeManagement",
- path: "",
- requiredAbility: [AUTH.PURCHASE, AUTH.STOCK, AUTH.STOCK_TAKE, AUTH.STOCK_FG, AUTH.STOCK_IN_BIND, AUTH.ITEM_TRACING, AUTH.ADMIN],
- children: [
- {
- id: "nav.store.purchaseOrder",
- icon: <LocalShipping />,
- labelKey: "nav.store.purchaseOrder",
- requiredAbility: [AUTH.PURCHASE, AUTH.ADMIN],
- path: "/po",
- },
- {
- id: "nav.store.pickOrder",
- icon: <Assignment />,
- labelKey: "nav.store.pickOrder",
- requiredAbility: [AUTH.STOCK, AUTH.ADMIN],
- path: "/pickOrder",
- },
- {
- id: "nav.store.inventoryLedger",
- icon: <Inventory />,
- labelKey: "nav.store.inventoryLedger",
- requiredAbility: [AUTH.STOCK, AUTH.ADMIN],
- path: "/inventory",
- },
- {
- id: "nav.store.itemTracing",
- icon: <QrCodeIcon />,
- labelKey: "nav.store.itemTracing",
- requiredAbility: [AUTH.ITEM_TRACING],
- path: "/itemTracing",
- },
- {
- id: "nav.store.stockTake",
- icon: <AssignmentTurnedIn />,
- labelKey: "nav.store.stockTake",
- requiredAbility: [AUTH.STOCK, AUTH.STOCK_TAKE, AUTH.ADMIN],
- path: "/stocktakemanagement",
- },
- {
- id: "nav.store.stockIssue",
- icon: <ReportProblem />,
- labelKey: "nav.store.stockIssue",
- requiredAbility: [AUTH.STOCK, AUTH.STOCK_TAKE, AUTH.ADMIN],
- path: "/stockIssue",
- },
- {
- id: "nav.store.putAwayScan",
- icon: <QrCodeIcon />,
- labelKey: "nav.store.putAwayScan",
- requiredAbility: [AUTH.STOCK, AUTH.STOCK_TAKE, AUTH.STOCK_IN_BIND, AUTH.ADMIN],
- path: "/putAway",
- },
- {
- id: "nav.store.finishedGoodManagement",
- icon: <ViewModule />,
- labelKey: "nav.store.finishedGoodManagement",
- requiredAbility: [AUTH.ADMIN],
- path: "/finishedGood/management",
- },
- {
- id: "nav.store.stockRecord",
- icon: <Description />,
- labelKey: "nav.store.stockRecord",
- requiredAbility: [AUTH.STOCK, AUTH.STOCK_TAKE, AUTH.STOCK_IN_BIND, AUTH.STOCK_FG, AUTH.ADMIN],
- path: "/stockRecord",
- },
- {
- id: "nav.store.doWorkbench",
- icon: <Description />,
- labelKey: "nav.store.doWorkbench",
- requiredAbility: [AUTH.STOCK, AUTH.STOCK_TAKE, AUTH.STOCK_IN_BIND, AUTH.STOCK_FG, AUTH.ADMIN],
- path: "/doworkbench",
- },
- ],
- },
- {
- id: "nav.deliveryOrder",
- icon: <LocalShipping />,
- labelKey: "nav.deliveryOrder",
- path: "/do",
- requiredAbility: [AUTH.STOCK_FG, AUTH.ADMIN],
- },
- {
- id: "nav.scheduling",
- icon: <CalendarMonth />,
- labelKey: "nav.scheduling",
- path: "/ps",
- requiredAbility: [AUTH.FORECAST, AUTH.ADMIN],
- isHidden: false,
- },
- {
- id: "nav.jobOrderManagement",
- icon: <Factory />,
- labelKey: "nav.jobOrderManagement",
- path: "",
- requiredAbility: [AUTH.JOB_CREATE, AUTH.JOB_PICK, AUTH.JOB_PROD, AUTH.ADMIN],
- children: [
- {
- id: "nav.jobOrder.searchCreate",
- icon: <PostAdd />,
- labelKey: "nav.jobOrder.searchCreate",
- requiredAbility: [AUTH.JOB_CREATE, AUTH.ADMIN],
- path: "/jo",
- },
- {
- id: "nav.jobOrder.pickExecution",
- icon: <Inventory />,
- labelKey: "nav.jobOrder.pickExecution",
- requiredAbility: [AUTH.JOB_PICK, AUTH.JOB_MAT, AUTH.ADMIN],
- path: "/jodetail",
- },
- {
- id: "nav.jobOrder.productionProcess",
- icon: <Kitchen />,
- labelKey: "nav.jobOrder.productionProcess",
- requiredAbility: [AUTH.JOB_PROD, AUTH.ADMIN],
- path: "/productionProcess",
- },
- {
- id: "nav.jobOrder.bagUsage",
- icon: <Inventory2 />,
- labelKey: "nav.jobOrder.bagUsage",
- requiredAbility: [AUTH.JOB_PROD, AUTH.ADMIN],
- path: "/bag",
- },
- ],
- },
- {
- id: "nav.bagPrint",
- icon: <Print />,
- labelKey: "nav.bagPrint",
- path: "/bagPrint",
- requiredAbility: [AUTH.JOB_PROD, AUTH.ADMIN],
- isHidden: false,
- },
- {
- id: "nav.laserPrint",
- icon: <Print />,
- labelKey: "nav.laserPrint",
- path: "/laserPrint",
- requiredAbility: [AUTH.JOB_PROD, AUTH.ADMIN],
- isHidden: false,
- },
- {
- id: "nav.report",
- icon: <Assessment />,
- labelKey: "nav.report",
- path: "/report",
- requiredAbility: [AUTH.REPORT_MGMT, AUTH.ADMIN],
- isHidden: false,
- },
- {
- id: "nav.m18Sync",
- icon: <Sync />,
- labelKey: "nav.m18Sync",
- path: "/m18Syn",
- requiredAbility: [AUTH.ADMIN],
- isHidden: false,
- },
- {
- id: "nav.chartReports",
- icon: <ShowChart />,
- labelKey: "nav.chartReports",
- path: "",
- requiredAbility: [AUTH.TESTING, AUTH.ADMIN],
- isHidden: false,
- children: [
- {
- id: "nav.chart.purchase",
- icon: <Storefront />,
- labelKey: "nav.chart.purchase",
- path: "/chart/purchase",
- requiredAbility: [AUTH.TESTING, AUTH.ADMIN],
- },
- {
- id: "nav.chart.jobOrder",
- icon: <Assignment />,
- labelKey: "nav.chart.jobOrder",
- path: "/chart/joborder",
- requiredAbility: [AUTH.TESTING, AUTH.ADMIN],
- },
- {
- id: "nav.chart.jobOrderBoard",
- icon: <ViewModule />,
- labelKey: "nav.chart.jobOrderBoard",
- path: "/chart/joborder/board",
- requiredAbility: [AUTH.TESTING, AUTH.ADMIN],
- },
- {
- id: "nav.chart.delivery",
- icon: <LocalShipping />,
- labelKey: "nav.chart.delivery",
- path: "/chart/delivery",
- requiredAbility: [AUTH.TESTING, AUTH.ADMIN],
- },
- {
- id: "nav.chart.warehouse",
- icon: <Warehouse />,
- labelKey: "nav.chart.warehouse",
- path: "/chart/warehouse",
- requiredAbility: [AUTH.TESTING, AUTH.ADMIN],
- },
- {
- id: "nav.chart.forecast",
- icon: <TrendingUp />,
- labelKey: "nav.chart.forecast",
- path: "/chart/forecast",
- requiredAbility: [AUTH.TESTING, AUTH.ADMIN],
- },
- ],
- },
- {
- id: "nav.settings",
- icon: <Settings />,
- labelKey: "nav.settings",
- path: "",
- requiredAbility: [AUTH.VIEW_USER, AUTH.ADMIN],
- children: [
- {
- id: "nav.settings.user",
- icon: <Person />,
- labelKey: "nav.settings.user",
- path: "/settings/user",
- requiredAbility: [AUTH.VIEW_USER, AUTH.ADMIN],
- },
- {
- id: "nav.settings.clientMonitor",
- icon: <Devices />,
- labelKey: "nav.settings.clientMonitor",
- path: "/settings/clientMonitor",
- requiredAbility: [AUTH.ADMIN, AUTH.TESTING],
- isHidden: !isMonitoringEnabled,
- },
- {
- id: "nav.settings.items",
- icon: <Category />,
- labelKey: "nav.settings.items",
- path: "/settings/items",
- },
- {
- id: "nav.settings.equipment",
- icon: <Build />,
- labelKey: "nav.settings.equipment",
- path: "/settings/equipment",
- },
- {
- id: "nav.settings.warehouse",
- icon: <Warehouse />,
- labelKey: "nav.settings.warehouse",
- path: "/settings/warehouse",
- },
- {
- id: "nav.settings.printer",
- icon: <Print />,
- labelKey: "nav.settings.printer",
- path: "/settings/printer",
- },
- {
- id: "nav.settings.priceInquiry",
- icon: <Assessment />,
- labelKey: "nav.settings.priceInquiry",
- path: "/settings/itemPrice",
- },
- {
- id: "nav.settings.qcItem",
- icon: <VerifiedUser />,
- labelKey: "nav.settings.qcItem",
- path: "/settings/qcItem",
- },
- {
- id: "nav.settings.qcCategory",
- icon: <Label />,
- labelKey: "nav.settings.qcCategory",
- path: "/settings/qcCategory",
- },
- {
- id: "nav.settings.qcItemAll",
- icon: <Checklist />,
- labelKey: "nav.settings.qcItemAll",
- path: "/settings/qcItemAll",
- },
- {
- id: "nav.settings.shopAndTruck",
- icon: <Storefront />,
- labelKey: "nav.settings.shopAndTruck",
- path: "/settings/shop/board",
- },
- {
- id: "nav.settings.deliveryOrderFloor",
- icon: <Layers />,
- labelKey: "nav.settings.deliveryOrderFloor",
- path: "/settings/deliveryOrderFloor",
- },
- {
- id: "nav.settings.demandForecast",
- icon: <TrendingUp />,
- labelKey: "nav.settings.demandForecast",
- path: "/settings/rss",
- },
- {
- id: "nav.settings.bomWeighting",
- icon: <ViewModule />,
- labelKey: "nav.settings.bomWeighting",
- path: "/settings/bomWeighting",
- },
- {
- id: "nav.settings.masterDataIssues",
- icon: <ReportProblem />,
- labelKey: "nav.settings.masterDataIssues",
- path: "/settings/masterDataIssues",
- requiredAbility: [AUTH.ADMIN],
- },
- {
- id: "nav.settings.qrCodeHandle",
- icon: <QrCodeIcon />,
- labelKey: "nav.settings.qrCodeHandle",
- path: "/settings/qrCodeHandle",
- },
- {
- id: "nav.settings.importTesting",
- icon: <Science />,
- labelKey: "nav.settings.importTesting",
- path: "/settings/m18ImportTesting",
- },
- {
- id: "nav.settings.importExcel",
- icon: <UploadFile />,
- labelKey: "nav.settings.importExcel",
- path: "/settings/importExcel",
- },
- {
- id: "nav.settings.importBom",
- icon: <UploadFile />,
- labelKey: "nav.settings.importBom",
- path: "/settings/importBom",
- },
- ],
- },
- ];
- const { t } = useTranslation("navigation");
- const pathname = usePathname();
- const abilitySet = new Set(abilities.map((a) => String(a).trim()));
- /** 採購入庫側欄紅點:TESTING / ADMIN / STOCK */
- const canSeePoAlerts =
- abilitySet.has(AUTH.TESTING) || abilitySet.has(AUTH.ADMIN) || abilitySet.has(AUTH.STOCK);
- /** 工單 QC/上架紅點:仍僅 TESTING */
- const canSeeJoFgAlerts = abilitySet.has(AUTH.TESTING);
- const canSeeMasterDataIssueBadge = abilitySet.has(AUTH.ADMIN);
-
- const [openItems, setOpenItems] = React.useState<string[]>([]);
- /** Keep parent sections expanded on deep links (e.g. /po/edit from nav red spot) so alerts stay visible. */
- React.useEffect(() => {
- const ensureOpen: string[] = [];
- if (pathname.startsWith("/chart")) {
- ensureOpen.push("nav.chartReports");
- }
- if (pathname === "/po" || pathname.startsWith("/po/")) {
- ensureOpen.push("nav.storeManagement");
- }
- if (pathname === "/productionProcess" || pathname.startsWith("/productionProcess/")) {
- ensureOpen.push("nav.jobOrderManagement");
- }
- if (
- pathname === "/jo/workbench" ||
- pathname.startsWith("/jo/workbench/") ||
- pathname === "/jodetail" ||
- pathname.startsWith("/jodetail/")
- ) {
- ensureOpen.push("nav.jobOrderManagement");
- }
- if (
- pathname === "/doworkbench" ||
- pathname.startsWith("/doworkbench/") ||
- pathname === "/doworkbenchsearch" ||
- pathname.startsWith("/doworkbenchsearch/")
- ) {
- ensureOpen.push("nav.storeManagement");
- }
- if (ensureOpen.length === 0) return;
- setOpenItems((prev) => {
- const set = new Set(prev);
- let changed = false;
- for (const id of ensureOpen) {
- if (!set.has(id)) {
- set.add(id);
- changed = true;
- }
- }
- return changed ? Array.from(set) : prev;
- });
- }, [pathname]);
- const toggleItem = (id: string) => {
- setOpenItems((prevOpenItems) =>
- prevOpenItems.includes(id)
- ? prevOpenItems.filter((item) => item !== id)
- : [...prevOpenItems, id],
- );
- };
-
- const selectedLeafPath = React.useMemo(() => {
- const leafPaths: string[] = [];
- const walk = (items: NavigationItem[]) => {
- for (const it of items) {
- if (it.isHidden) continue;
- if (!hasAbility(it.requiredAbility)) continue;
- if (it.path) leafPaths.push(it.path);
- if (it.children?.length) walk(it.children);
- }
- };
- walk(navigationItems);
-
- // Pick the most specific (longest) match to avoid double-highlighting
- const matches = leafPaths.filter((p) => {
- if (pathname === p) return true;
- if (!pathname.startsWith(p + "/")) return false;
- // `/doworkbench` must not claim `/doworkbenchsearch` (prefix without trailing slash)
- if (p === "/doworkbench" && pathname.startsWith("/doworkbenchsearch")) return false;
- // Shop sub-routes (detail, truckdetail, legacy tab page) share one nav entry → board
- if (
- p === "/settings/shop/board" &&
- (pathname === "/settings/shop" || pathname.startsWith("/settings/shop/"))
- ) {
- return true;
- }
- return true;
- });
- matches.sort((a, b) => b.length - a.length);
- return matches[0] ?? "";
- }, [hasAbility, navigationItems, pathname]);
-
- const renderNavigationItem = (item: NavigationItem) => {
- if (!hasAbility(item.requiredAbility)) {
- return null;
- }
-
- const isOpen = openItems.includes(item.id);
- const hasVisibleChildren = item.children?.some(child => hasAbility(child.requiredAbility));
- const isLeaf = Boolean(item.path);
- const isSelected = isLeaf && item.path
- ? item.path === selectedLeafPath
- : hasVisibleChildren && item.children?.some((c) => c.path && c.path === selectedLeafPath);
-
- const content = (
- <ListItemButton
- selected={isSelected}
- onClick={isLeaf ? undefined : () => toggleItem(item.id)}
- sx={{
- mx: 1,
- "&.Mui-selected .MuiListItemIcon-root": { color: "primary.main" },
- }}
- >
- <ListItemIcon sx={{ minWidth: 40 }}>{item.icon}</ListItemIcon>
- <ListItemText
- primary={t(item.labelKey)}
- primaryTypographyProps={{ fontWeight: isSelected ? 600 : 500 }}
- />
- </ListItemButton>
- );
-
- return (
- <Box key={item.id}>
- {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) && (
- child.path === "/po" ? (
- <Box
- key={child.id}
- sx={{
- display: "flex",
- alignItems: "stretch",
- mx: 1,
- borderRadius: 1,
- overflow: "hidden",
- "&:hover": { bgcolor: "action.hover" },
- }}
- >
- <Box
- component={Link}
- href={child.path}
- sx={{
- flex: 1,
- minWidth: 0,
- textDecoration: "none",
- color: "inherit",
- display: "flex",
- }}
- >
- <ListItemButton
- selected={child.path === selectedLeafPath}
- sx={{
- flex: 1,
- py: 1,
- pr: 0.5,
- "&.Mui-selected .MuiListItemIcon-root": { color: "primary.main" },
- }}
- >
- <ListItemIcon sx={{ minWidth: 40 }}>{child.icon}</ListItemIcon>
- <ListItemText
- primary={t(child.labelKey)}
- primaryTypographyProps={{
- fontWeight:
- child.path === selectedLeafPath ? 600 : 500,
- fontSize: "0.875rem",
- }}
- />
- </ListItemButton>
- </Box>
- <PurchaseStockInNavAlerts enabled={canSeePoAlerts} />
- </Box>
- ) : child.path === "/settings/masterDataIssues" ? (
- <Box
- key={child.id}
- component={Link}
- href={child.path}
- sx={{
- display: "block",
- mx: 1,
- borderRadius: 1,
- textDecoration: "none",
- color: "inherit",
- "&:hover": { bgcolor: "action.hover" },
- }}
- >
- <ListItemButton
- selected={child.path === selectedLeafPath}
- sx={{
- py: 1,
- pr: 1.5,
- display: "flex",
- alignItems: "center",
- gap: 0.5,
- "&.Mui-selected .MuiListItemIcon-root": { color: "primary.main" },
- }}
- >
- <ListItemIcon sx={{ minWidth: 40 }}>{child.icon}</ListItemIcon>
- <ListItemText
- primary={t(child.labelKey)}
- sx={{ flex: 1, minWidth: 0, my: 0 }}
- primaryTypographyProps={{
- fontWeight:
- child.path === selectedLeafPath ? 600 : 500,
- fontSize: "0.875rem",
- }}
- />
- <MasterDataIssuesNavBadge enabled={canSeeMasterDataIssueBadge} />
- </ListItemButton>
- </Box>
- ) : child.path === "/productionProcess" ? (
- <Box
- key={child.id}
- sx={{
- display: "flex",
- alignItems: "stretch",
- mx: 1,
- borderRadius: 1,
- overflow: "hidden",
- "&:hover": { bgcolor: "action.hover" },
- }}
- >
- <Box
- component={Link}
- href={child.path}
- sx={{
- flex: 1,
- minWidth: 0,
- textDecoration: "none",
- color: "inherit",
- display: "flex",
- }}
- >
- <ListItemButton
- selected={child.path === selectedLeafPath}
- sx={{
- flex: 1,
- py: 1,
- pr: 0.5,
- "&.Mui-selected .MuiListItemIcon-root": { color: "primary.main" },
- }}
- >
- <ListItemIcon sx={{ minWidth: 40 }}>{child.icon}</ListItemIcon>
- <ListItemText
- primary={t(child.labelKey)}
- primaryTypographyProps={{
- fontWeight:
- child.path === selectedLeafPath ? 600 : 500,
- fontSize: "0.875rem",
- }}
- />
- </ListItemButton>
- </Box>
- <JobOrderFgStockInNavAlerts enabled={canSeeJoFgAlerts} />
- </Box>
- ) : (
- <Box
- key={child.id}
- component={Link}
- href={child.path}
- sx={{ textDecoration: "none", color: "inherit" }}
- >
- <ListItemButton
- selected={child.path === selectedLeafPath}
- sx={{
- mx: 1,
- py: 1,
- "&.Mui-selected .MuiListItemIcon-root": { color: "primary.main" },
- }}
- >
- <ListItemIcon sx={{ minWidth: 40 }}>{child.icon}</ListItemIcon>
- <ListItemText
- primary={t(child.labelKey)}
- primaryTypographyProps={{
- fontWeight:
- child.path === selectedLeafPath
- ? 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,
- }}
- >
- <Stack direction="column" alignItems="flex-start" spacing={0.5}>
- <Logo height={42} />
- {process.env.NODE_ENV === "production" && (
- <Typography
- variant="body2"
- sx={{
- fontWeight: 700,
- color: "warning.dark",
- bgcolor: "warning.light",
- px: 1,
- py: 0.5,
- borderRadius: 1,
- border: "1px solid",
- borderColor: "warning.main",
- }}
- >
- {t("nav.productionServer")}
- </Typography>
- )}
- </Stack>
- </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;
|