FPSMS-frontend
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 

436 строки
12 KiB

  1. import { useSession } from "next-auth/react";
  2. import Divider from "@mui/material/Divider";
  3. import Box from "@mui/material/Box";
  4. import React, { useEffect } from "react";
  5. import List from "@mui/material/List";
  6. import ListItemButton from "@mui/material/ListItemButton";
  7. import ListItemText from "@mui/material/ListItemText";
  8. import ListItemIcon from "@mui/material/ListItemIcon";
  9. import WorkHistory from "@mui/icons-material/WorkHistory";
  10. import Dashboard from "@mui/icons-material/Dashboard";
  11. import SummarizeIcon from "@mui/icons-material/Summarize";
  12. import PaymentsIcon from "@mui/icons-material/Payments";
  13. import AccountTreeIcon from "@mui/icons-material/AccountTree";
  14. import RequestQuote from "@mui/icons-material/RequestQuote";
  15. import PeopleIcon from "@mui/icons-material/People";
  16. import Task from "@mui/icons-material/Task";
  17. import Assignment from "@mui/icons-material/Assignment";
  18. import Settings from "@mui/icons-material/Settings";
  19. import Analytics from "@mui/icons-material/Analytics";
  20. import Payments from "@mui/icons-material/Payments";
  21. import QrCodeIcon from "@mui/icons-material/QrCode";
  22. import { useTranslation } from "react-i18next";
  23. import Typography from "@mui/material/Typography";
  24. import { usePathname } from "next/navigation";
  25. import Link from "next/link";
  26. import { NAVIGATION_CONTENT_WIDTH } from "@/config/uiConfig";
  27. import Logo from "../Logo";
  28. import BugReportIcon from "@mui/icons-material/BugReport";
  29. import {
  30. VIEW_USER,
  31. MAINTAIN_USER,
  32. VIEW_GROUP,
  33. MAINTAIN_GROUP,
  34. // Add more authorities as needed, e.g.:
  35. TESTING, PROD, PACK, ADMIN, STOCK, Driver
  36. } from "../../authorities";
  37. interface NavigationItem {
  38. icon: React.ReactNode;
  39. label: string;
  40. path: string;
  41. children?: NavigationItem[];
  42. isHidden?: boolean | undefined;
  43. requiredAbility?: string | string[];
  44. }
  45. const NavigationContent: React.FC = () => {
  46. const { data: session, status } = useSession();
  47. const abilities = session?.user?.abilities ?? [];
  48. // Helper: check if user has required permission
  49. const hasAbility = (required?: string | string[]): boolean => {
  50. if (!required) return true; // no requirement → always show
  51. if (Array.isArray(required)) {
  52. return required.some(ability => abilities.includes(ability));
  53. }
  54. return abilities.includes(required);
  55. };
  56. const navigationItems: NavigationItem[] = [
  57. {
  58. icon: <Dashboard />,
  59. label: "Dashboard",
  60. path: "/dashboard",
  61. },
  62. {
  63. icon: <RequestQuote />,
  64. label: "Store Management",
  65. path: "",
  66. children: [
  67. {
  68. icon: <RequestQuote />,
  69. label: "Purchase Order",
  70. path: "/po",
  71. },
  72. {
  73. icon: <RequestQuote />,
  74. label: "Pick Order",
  75. path: "/pickOrder",
  76. },
  77. // {
  78. // icon: <RequestQuote />,
  79. // label: "Cons. Pick Order",
  80. // path: "",
  81. // },
  82. // {
  83. // icon: <RequestQuote />,
  84. // label: "Delivery Pick Order",
  85. // path: "",
  86. // },
  87. // {
  88. // icon: <RequestQuote />,
  89. // label: "Warehouse",
  90. // path: "",
  91. // },
  92. // {
  93. // icon: <RequestQuote />,
  94. // label: "Location Transfer Order",
  95. // path: "",
  96. // },
  97. {
  98. icon: <RequestQuote />,
  99. label: "View item In-out And inventory Ledger",
  100. path: "/inventory",
  101. },
  102. {
  103. icon: <RequestQuote />,
  104. label: "Stock Take Management",
  105. path: "/stocktakemanagement",
  106. },
  107. {
  108. icon: <RequestQuote />,
  109. label: "Stock Issue",
  110. path: "/stockIssue",
  111. },
  112. //TODO: anna
  113. // {
  114. // icon: <RequestQuote />,
  115. // label: "Stock Issue",
  116. // path: "/stockIssue",
  117. // },
  118. {
  119. icon: <RequestQuote />,
  120. label: "Put Away Scan",
  121. path: "/putAway",
  122. },
  123. {
  124. icon: <RequestQuote />,
  125. label: "Finished Good Order",
  126. path: "/finishedGood",
  127. },
  128. ],
  129. },
  130. {
  131. icon: <RequestQuote />,
  132. label: "Delivery",
  133. path: "",
  134. //requiredAbility: VIEW_DO,
  135. children: [
  136. {
  137. icon: <RequestQuote />,
  138. label: "Delivery Order",
  139. path: "/do",
  140. },
  141. ],
  142. },
  143. // {
  144. // icon: <RequestQuote />,
  145. // label: "Report",
  146. // path: "",
  147. // children: [
  148. // {
  149. // icon: <RequestQuote />,
  150. // label: "report",
  151. // path: "",
  152. // },
  153. // ],
  154. // },
  155. // {
  156. // icon: <RequestQuote />,
  157. // label: "Recipe",
  158. // path: "",
  159. // children: [
  160. // {
  161. // icon: <RequestQuote />,
  162. // label: "FG Recipe",
  163. // path: "",
  164. // },
  165. // {
  166. // icon: <RequestQuote />,
  167. // label: "SFG Recipe",
  168. // path: "",
  169. // },
  170. // {
  171. // icon: <RequestQuote />,
  172. // label: "Recipe",
  173. // path: "",
  174. // },
  175. // ],
  176. // },
  177. {
  178. icon: <RequestQuote />,
  179. label: "Scheduling",
  180. path: "",
  181. children: [
  182. {
  183. icon: <RequestQuote />,
  184. label: "Demand Forecast",
  185. path: "/scheduling/rough",
  186. },
  187. {
  188. icon: <RequestQuote />,
  189. label: "Detail Scheduling",
  190. path: "/scheduling/detailed",
  191. },
  192. /*
  193. {
  194. icon: <RequestQuote />,
  195. label: "Production",
  196. path: "/production",
  197. },
  198. */
  199. ],
  200. },
  201. {
  202. icon: <RequestQuote />,
  203. label: "Management Job Order",
  204. path: "",
  205. children: [
  206. {
  207. icon: <RequestQuote />,
  208. label: "Search Job Order/ Create Job Order",
  209. path: "/jo",
  210. },
  211. {
  212. icon: <RequestQuote />,
  213. label: "Job Order Pickexcution",
  214. path: "/jodetail",
  215. },
  216. {
  217. icon: <RequestQuote />,
  218. label: "Job Order Production Process",
  219. path: "/productionProcess",
  220. },
  221. {
  222. icon: <RequestQuote />,
  223. label: "Bag Usage",
  224. path: "/bag",
  225. },
  226. ],
  227. },
  228. {
  229. icon: <BugReportIcon />,
  230. label: "PS",
  231. path: "/ps",
  232. requiredAbility: TESTING,
  233. isHidden: false,
  234. },
  235. {
  236. icon: <BugReportIcon />,
  237. label: "Printer Testing",
  238. path: "/testing",
  239. requiredAbility: TESTING,
  240. isHidden: false,
  241. },
  242. {
  243. icon: <RequestQuote />,
  244. label: "Settings",
  245. path: "",
  246. requiredAbility: [VIEW_USER, VIEW_GROUP],
  247. children: [
  248. {
  249. icon: <RequestQuote />,
  250. label: "User",
  251. path: "/settings/user",
  252. requiredAbility: VIEW_USER,
  253. },
  254. {
  255. icon: <RequestQuote />,
  256. label: "User Group",
  257. path: "/settings/user",
  258. requiredAbility: VIEW_GROUP,
  259. },
  260. // {
  261. // icon: <RequestQuote />,
  262. // label: "Material",
  263. // path: "/settings/material",
  264. // },
  265. // {
  266. // icon: <RequestQuote />,
  267. // label: "By-product",
  268. // path: "/settings/byProduct",
  269. // },
  270. {
  271. icon: <RequestQuote />,
  272. label: "Items",
  273. path: "/settings/items",
  274. },
  275. {
  276. icon: <RequestQuote />,
  277. label: "ShopAndTruck",
  278. path: "/settings/shop",
  279. },
  280. {
  281. icon: <RequestQuote />,
  282. label: "Demand Forecast Setting",
  283. path: "/settings/rss",
  284. },
  285. //{
  286. // icon: <RequestQuote />,
  287. // label: "Equipment Type",
  288. // path: "/settings/equipmentType",
  289. //},
  290. {
  291. icon: <RequestQuote />,
  292. label: "Equipment",
  293. path: "/settings/equipment",
  294. },
  295. {
  296. icon: <RequestQuote />,
  297. label: "Warehouse",
  298. path: "/settings/warehouse",
  299. },
  300. {
  301. icon: <RequestQuote />,
  302. label: "Supplier",
  303. path: "/settings/user",
  304. },
  305. {
  306. icon: <RequestQuote />,
  307. label: "Customer",
  308. path: "/settings/user",
  309. },
  310. {
  311. icon: <RequestQuote />,
  312. label: "QC Check Item",
  313. path: "/settings/qcItem",
  314. },
  315. {
  316. icon: <RequestQuote />,
  317. label: "QC Category",
  318. path: "/settings/qcCategory",
  319. },
  320. {
  321. icon: <RequestQuote />,
  322. label: "QC Check Template",
  323. path: "/settings/user",
  324. },
  325. {
  326. icon: <QrCodeIcon />,
  327. label: "QR Code Handle",
  328. path: "/settings/qrCodeHandle",
  329. },
  330. // {
  331. // icon: <RequestQuote />,
  332. // label: "Mail",
  333. // path: "/settings/mail",
  334. // },
  335. {
  336. icon: <RequestQuote />,
  337. label: "Import Testing",
  338. path: "/settings/m18ImportTesting",
  339. },
  340. {
  341. icon: <RequestQuote />,
  342. label: "Import Excel",
  343. path: "/settings/importExcel",
  344. },
  345. ],
  346. },
  347. ];
  348. const { t } = useTranslation("common");
  349. const pathname = usePathname();
  350. const [openItems, setOpenItems] = React.useState<string[]>([]);
  351. const toggleItem = (label: string) => {
  352. setOpenItems((prevOpenItems) =>
  353. prevOpenItems.includes(label)
  354. ? prevOpenItems.filter((item) => item !== label)
  355. : [...prevOpenItems, label],
  356. );
  357. };
  358. const renderNavigationItem = (item: NavigationItem) => {
  359. if (!hasAbility(item.requiredAbility)) {
  360. return null;
  361. }
  362. const isOpen = openItems.includes(item.label);
  363. const hasVisibleChildren = item.children?.some(child => hasAbility(child.requiredAbility));
  364. return (
  365. <Box
  366. key={`${item.label}-${item.path}`}
  367. component={Link}
  368. href={item.path}
  369. sx={{ textDecoration: "none", color: "inherit" }}
  370. >
  371. <ListItemButton
  372. selected={pathname.includes(item.label)}
  373. onClick={() => item.children && toggleItem(item.label)}
  374. >
  375. <ListItemIcon>{item.icon}</ListItemIcon>
  376. <ListItemText primary={t(item.label)} />
  377. </ListItemButton>
  378. {item.children && isOpen && hasVisibleChildren && (
  379. <List sx={{ pl: 2 }}>
  380. {item.children.map(
  381. (child) => !child.isHidden && renderNavigationItem(child),
  382. )}
  383. </List>
  384. )}
  385. </Box>
  386. );
  387. };
  388. if (status === "loading") {
  389. return <Box sx={{ width: NAVIGATION_CONTENT_WIDTH, p: 3 }}>Loading...</Box>;
  390. }
  391. return (
  392. <Box sx={{ width: NAVIGATION_CONTENT_WIDTH }}>
  393. <Box sx={{ p: 3, display: "flex" }}>
  394. <Logo height={60} />
  395. {/* <button className="float-right bg-transparent border-transparent" >
  396. <ArrowCircleLeftRoundedIcon className="text-slate-400 hover:text-blue-400 hover:cursor-pointer " style={{ fontSize: '35px' }} />
  397. </button> */}
  398. </Box>
  399. <Divider />
  400. <List component="nav">
  401. {navigationItems
  402. .filter(item => !item.isHidden)
  403. .map(renderNavigationItem)
  404. .filter(Boolean)}
  405. {/* {navigationItems.map(({ icon, label, path }, index) => {
  406. return (
  407. <Box
  408. key={`${label}-${index}`}
  409. component={Link}
  410. href={path}
  411. sx={{ textDecoration: "none", color: "inherit" }}
  412. >
  413. <ListItemButton selected={pathname.includes(path)}>
  414. <ListItemIcon>{icon}</ListItemIcon>
  415. <ListItemText primary={t(label)} />
  416. </ListItemButton>
  417. </Box>
  418. );
  419. })} */}
  420. </List>
  421. </Box>
  422. );
  423. };
  424. export default NavigationContent;