FPSMS-frontend
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

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