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

1001 строка
38 KiB

  1. import PropTypes from "prop-types";
  2. import React, { useState, useContext, useEffect, useRef } from "react";
  3. import { useDispatch, useSelector } from "react-redux";
  4. import { useNavigate, Link } from "react-router-dom";
  5. import { SysContext } from "components/SysSettingProvider";
  6. import { checkIsOnlyOnlinePayment } from "../../../utils/Utils";
  7. // material-ui
  8. import {
  9. AppBar,
  10. Typography,
  11. Box,
  12. Stack,
  13. Toolbar,
  14. Divider,
  15. IconButton,
  16. Drawer,
  17. Grid,
  18. } from "@mui/material";
  19. import MenuIcon from "@mui/icons-material/Menu";
  20. import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown";
  21. // project import
  22. import Logo from "components/Logo";
  23. import AdminLogo from "components/AdminLogo";
  24. import MobileLogo from "components/MobileLogo";
  25. import "assets/style/navbarStyles.css";
  26. import {
  27. isUserLoggedIn,
  28. isGLDLoggedIn,
  29. isPrimaryLoggedIn,
  30. isCreditorLoggedIn,
  31. isINDLoggedIn,
  32. isPasswordExpiry,
  33. haveOrgPaymentRecord,
  34. haveOrgDnRecord,
  35. isORGLoggedIn,
  36. checkSysEnv,
  37. } from "utils/Utils";
  38. import { handleLogoutFunction } from "auth/index";
  39. import { isGranted, isGrantedAny } from "auth/utils";
  40. import LocaleSelector from "./HeaderContent/LocaleSelector";
  41. import { FormattedMessage, useIntl } from "react-intl";
  42. const drawerWidth = 300;
  43. const isAfterAboutSwitchDate = checkIsOnlyOnlinePayment;
  44. const getAboutExternalUrlByLocale = () => {
  45. const locale = (localStorage.getItem("locale") || "").toLowerCase();
  46. if (locale === "zh-hk") return "https://www.gld.gov.hk/zh-hk/our-services/printing/advertising-gov-gazette/";
  47. if (locale === "zh-cn") return "https://www.gld.gov.hk/zh-cn/our-services/printing/advertising-gov-gazette/";
  48. return "https://www.gld.gov.hk/en/our-services/printing/advertising-gov-gazette/";
  49. };
  50. /**
  51. * Accessible dropdown trigger:
  52. * - button is always focusable
  53. * - opens on focus (keyboard tab) via CSS :focus-within + optional state
  54. * - supports Enter/Space/ArrowDown to jump to first submenu item
  55. * - supports Escape to close + return focus
  56. */
  57. function Header(props) {
  58. const { sysSetting } = useContext(SysContext);
  59. const { window } = props;
  60. /** Subscribe to auth changes (LOGIN/LOGOUT/cross-tab storage); Header reads localStorage and must re-render. */
  61. useSelector((state) => state.authRevision);
  62. const [mobileOpen, setMobileOpen] = useState(false);
  63. const dispatch = useDispatch();
  64. const navigate = useNavigate();
  65. const intl = useIntl();
  66. // which dropdown is open (optional but useful for aria-expanded + closing)
  67. const [openMenu, setOpenMenu] = useState({
  68. payment: false,
  69. client: false,
  70. report: false,
  71. settings: false,
  72. paymentHistory: false,
  73. userSetting: false,
  74. });
  75. const rootNavRef = useRef(null);
  76. const handleDrawerToggle = () => setMobileOpen((prev) => !prev);
  77. const handleLogout = async () => {
  78. await dispatch(handleLogoutFunction());
  79. await navigate("/login");
  80. };
  81. const openKey = (key) => setOpenMenu((p) => ({ ...p, [key]: true }));
  82. const closeKey = (key) => setOpenMenu((p) => ({ ...p, [key]: false }));
  83. const closeAll = () =>
  84. setOpenMenu({
  85. payment: false,
  86. client: false,
  87. report: false,
  88. settings: false,
  89. paymentHistory: false,
  90. userSetting: false,
  91. });
  92. // close dropdowns on outside click / focus
  93. useEffect(() => {
  94. const onDocMouseDown = (e) => {
  95. if (!rootNavRef.current) return;
  96. if (!rootNavRef.current.contains(e.target)) closeAll();
  97. };
  98. document.addEventListener("mousedown", onDocMouseDown, true);
  99. return () => document.removeEventListener("mousedown", onDocMouseDown, true);
  100. }, []);
  101. const onMenuKeyDown = (key) => (e) => {
  102. if (e.key === "Enter" || e.key === " " || e.key === "ArrowDown") {
  103. e.preventDefault();
  104. openKey(key);
  105. requestAnimationFrame(() => {
  106. const first = document.querySelector(
  107. `[data-submenu="${key}"] a, [data-submenu="${key}"] button`
  108. );
  109. first?.focus?.();
  110. });
  111. } else if (e.key === "Escape") {
  112. e.preventDefault();
  113. closeKey(key);
  114. e.currentTarget?.focus?.();
  115. } else if (e.key === "ArrowUp") {
  116. // optional: close on ArrowUp when on trigger
  117. closeKey(key);
  118. }
  119. };
  120. const onTriggerBlur = (key) => (e) => {
  121. // close only when focus leaves the whole LI (trigger + submenu)
  122. const li = e.currentTarget.closest("li");
  123. if (li && li.contains(e.relatedTarget)) return;
  124. closeKey(key);
  125. };
  126. // =============================
  127. // Desktop top nav content (login)
  128. // =============================
  129. const loginContent = isGLDLoggedIn() ? (
  130. <div id="adminContent">
  131. {isPasswordExpiry() ? (
  132. <div id="passwordExpiryedContent">
  133. <li>
  134. <Link className="manageUser" to={"/user/changePassword"}>
  135. <Typography style={{ opacity: 0.9 }} variant={"pnspsHeaderTitle"} sx={{ ml: 2 }}>
  136. <FormattedMessage id="userChangePassword" />
  137. </Typography>
  138. </Link>
  139. </li>
  140. </div>
  141. ) : (
  142. <div id="adminContentList" ref={rootNavRef}>
  143. <li>
  144. <Link className="dashboard" to="/dashboard">
  145. <Typography style={{ opacity: 0.9 }} variant={"pnspsHeaderTitle"} sx={{ ml: 1 }}>
  146. Dashboard
  147. </Typography>
  148. </Link>
  149. </li>
  150. {isGrantedAny(["VIEW_APPLICATION", "MAINTAIN_APPLICATION"]) ? (
  151. <li>
  152. <Link className="application" to="/application/search">
  153. <Typography style={{ opacity: 0.9 }} variant={"pnspsHeaderTitle"} sx={{ ml: 1 }}>
  154. Application
  155. </Typography>
  156. </Link>
  157. </li>
  158. ) : null}
  159. {isGrantedAny(["VIEW_PROOF", "MAINTAIN_PROOF"]) ? (
  160. <li>
  161. <Link className="proof" to="/proof/search">
  162. <Typography style={{ opacity: 0.9 }} variant={"pnspsHeaderTitle"} sx={{ ml: 1 }}>
  163. Proof
  164. </Typography>
  165. </Link>
  166. </li>
  167. ) : null}
  168. {/* ===== Payment dropdown (admin) ===== */}
  169. {isGrantedAny([
  170. "MAINTAIN_PROOF",
  171. "MAINTAIN_PAYMENT",
  172. "MAINTAIN_RECON",
  173. "VIEW_DEMANDNOTE",
  174. "MAINTAIN_DEMANDNOTE",
  175. ]) ? (
  176. <li>
  177. <button
  178. type="button"
  179. className="navTrigger paymentTop"
  180. aria-haspopup="true"
  181. aria-expanded={openMenu.payment}
  182. onFocus={() => openKey("payment")}
  183. onBlur={onTriggerBlur("payment")}
  184. onKeyDown={onMenuKeyDown("payment")}
  185. >
  186. <Typography style={{ opacity: 0.9 }} variant={"pnspsHeaderTitle"} sx={{ ml: 1 }}>
  187. Payment
  188. </Typography>
  189. <KeyboardArrowDownIcon sx={{ fontSize: "1vw" }} />
  190. </button>
  191. <ul className="dropdown" data-submenu="payment">
  192. {isGranted("MAINTAIN_PROOF") ? (
  193. <li>
  194. <Link className="payment" to="/paymentPage/exportGDN">
  195. <Typography style={{ opacity: 0.9 }} variant={"pnspsHeaderTitle"} sx={{ ml: 1 }}>
  196. Export for GDN
  197. </Typography>
  198. </Link>
  199. </li>
  200. ) : null}
  201. {isGranted("MAINTAIN_PAYMENT") ? (
  202. <li>
  203. <Link className="payment" to="/application/markAsPaid/search">
  204. <Typography style={{ opacity: 0.9 }} variant={"pnspsHeaderTitle"} sx={{ ml: 1 }}>
  205. Mark Payment
  206. </Typography>
  207. </Link>
  208. </li>
  209. ) : null}
  210. {isGranted("MAINTAIN_PAYMENT") ? (
  211. <li>
  212. <Link className="payment" to="/paymentPage/search">
  213. <Typography style={{ opacity: 0.9 }} variant={"pnspsHeaderTitle"} sx={{ ml: 1 }}>
  214. Online Payment Record
  215. </Typography>
  216. </Link>
  217. </li>
  218. ) : null}
  219. {isGranted("MAINTAIN_RECON") ? (
  220. <li>
  221. <Link className="payment" to="/gfmis/search">
  222. <Typography style={{ opacity: 0.9 }} variant={"pnspsHeaderTitle"} sx={{ ml: 1 }}>
  223. GFMIS Generate XML
  224. </Typography>
  225. </Link>
  226. </li>
  227. ) : null}
  228. {isGranted("MAINTAIN_DEMANDNOTE") ? (
  229. <li>
  230. <Link className="payment" to="/paymentPage/createDemandNote">
  231. <Typography style={{ opacity: 0.9 }} variant={"pnspsHeaderTitle"} sx={{ ml: 1 }}>
  232. Create Demand Note
  233. </Typography>
  234. </Link>
  235. </li>
  236. ) : null}
  237. {isGrantedAny(["VIEW_DEMANDNOTE", "MAINTAIN_DEMANDNOTE"]) ? (
  238. <li>
  239. <Link className="payment" to="/paymentPage/demandNote">
  240. <Typography style={{ opacity: 0.9 }} variant={"pnspsHeaderTitle"} sx={{ ml: 1 }}>
  241. Demand Note
  242. </Typography>
  243. </Link>
  244. </li>
  245. ) : null}
  246. {isGranted("MAINTAIN_RECON") ? (
  247. <li>
  248. <Link className="payment" to="/paymentPage/reconReport">
  249. <Typography style={{ opacity: 0.9 }} variant={"pnspsHeaderTitle"} sx={{ ml: 1 }}>
  250. Recon Report
  251. </Typography>
  252. </Link>
  253. </li>
  254. ) : null}
  255. </ul>
  256. </li>
  257. ) : null}
  258. {/* ===== Client dropdown (admin) ===== */}
  259. {isGrantedAny([
  260. "VIEW_USER",
  261. "MAINTAIN_USER",
  262. "VIEW_ORG",
  263. "MAINTAIN_ORG",
  264. "VIEW_GROUP",
  265. "MAINTAIN_GROUP",
  266. "VIEW_GLD_USER",
  267. "VIEW_IND_USER",
  268. "VIEW_ORG_USER",
  269. "MAINTAIN_GLD_USER",
  270. "MAINTAIN_IND_USER",
  271. "MAINTAIN_ORG_USER",
  272. ]) ? (
  273. <li>
  274. <button
  275. type="button"
  276. className="navTrigger client"
  277. aria-haspopup="true"
  278. aria-expanded={openMenu.client}
  279. onFocus={() => openKey("client")}
  280. onBlur={onTriggerBlur("client")}
  281. onKeyDown={onMenuKeyDown("client")}
  282. >
  283. <Typography style={{ opacity: 0.9 }} variant={"pnspsHeaderTitle"} sx={{ ml: 1 }}>
  284. Client
  285. </Typography>
  286. <KeyboardArrowDownIcon sx={{ fontSize: "1vw" }} />
  287. </button>
  288. <ul className="dropdown" data-submenu="client">
  289. {isGrantedAny(["VIEW_USER", "MAINTAIN_USER"]) ? (
  290. <>
  291. <li>
  292. <Link className="user" to="/userSearchview">
  293. <Typography style={{ opacity: 0.9 }} variant={"pnspsHeaderTitle"} sx={{ ml: 2, mt: 1, mb: 1 }}>
  294. Users (GLD)
  295. </Typography>
  296. </Link>
  297. </li>
  298. <li>
  299. <Link className="user" to="/indUser">
  300. <Typography style={{ opacity: 0.9 }} variant={"pnspsHeaderTitle"} sx={{ ml: 2, mt: 1, mb: 1 }}>
  301. Users (Individual)
  302. </Typography>
  303. </Link>
  304. </li>
  305. <li>
  306. <Link className="user" to="/orgUser">
  307. <Typography style={{ opacity: 0.9 }} variant={"pnspsHeaderTitle"} sx={{ ml: 2, mt: 1, mb: 1 }}>
  308. Users (Organisation)
  309. </Typography>
  310. </Link>
  311. </li>
  312. </>
  313. ) : (
  314. <>
  315. {isGrantedAny(["VIEW_GLD_USER", "MAINTAIN_GLD_USER"]) ? (
  316. <li>
  317. <Link className="user" to="/userSearchview">
  318. <Typography style={{ opacity: 0.9 }} variant={"pnspsHeaderTitle"} sx={{ ml: 2, mt: 1, mb: 1 }}>
  319. Users (GLD)
  320. </Typography>
  321. </Link>
  322. </li>
  323. ) : null}
  324. {isGrantedAny(["VIEW_IND_USER", "MAINTAIN_IND_USER"]) ? (
  325. <li>
  326. <Link className="user" to="/indUser">
  327. <Typography style={{ opacity: 0.9 }} variant={"pnspsHeaderTitle"} sx={{ ml: 2, mt: 1, mb: 1 }}>
  328. Users (Individual)
  329. </Typography>
  330. </Link>
  331. </li>
  332. ) : null}
  333. {isGrantedAny(["VIEW_ORG_USER", "MAINTAIN_ORG_USER"]) ? (
  334. <li>
  335. <Link className="user" to="/orgUser">
  336. <Typography style={{ opacity: 0.9 }} variant={"pnspsHeaderTitle"} sx={{ ml: 2, mt: 1, mb: 1 }}>
  337. Users (Organisation)
  338. </Typography>
  339. </Link>
  340. </li>
  341. ) : null}
  342. </>
  343. )}
  344. {isGrantedAny(["VIEW_ORG", "MAINTAIN_ORG"]) ? (
  345. <li>
  346. <Link className="user" to="/org">
  347. <Typography style={{ opacity: 0.9 }} variant={"pnspsHeaderTitle"} sx={{ ml: 2, mt: 1, mb: 1 }}>
  348. Organisation
  349. </Typography>
  350. </Link>
  351. </li>
  352. ) : null}
  353. {isGrantedAny(["VIEW_GROUP", "MAINTAIN_GROUP"]) ? (
  354. <li>
  355. <Link className="user" to="/usergroupSearchview">
  356. <Typography style={{ opacity: 0.9 }} variant={"pnspsHeaderTitle"} sx={{ ml: 2, mt: 1, mb: 1 }}>
  357. User Group
  358. </Typography>
  359. </Link>
  360. </li>
  361. ) : null}
  362. </ul>
  363. </li>
  364. ) : null}
  365. {/* ===== Report dropdown (admin) ===== */}
  366. <li>
  367. <button
  368. type="button"
  369. className="navTrigger setting"
  370. aria-haspopup="true"
  371. aria-expanded={openMenu.report}
  372. onFocus={() => openKey("report")}
  373. onBlur={onTriggerBlur("report")}
  374. onKeyDown={onMenuKeyDown("report")}
  375. >
  376. <Typography style={{ opacity: 0.9 }} variant={"pnspsHeaderTitle"} sx={{ ml: 1 }}>
  377. Report
  378. </Typography>
  379. <KeyboardArrowDownIcon sx={{ fontSize: "1vw" }} />
  380. </button>
  381. <ul className="dropdown" data-submenu="report">
  382. <li>
  383. <Link className="report" to="/setting/report/summary">
  384. <Typography style={{ opacity: 0.9 }} variant={"pnspsHeaderTitle"} sx={{ ml: 1 }}>
  385. Summary of Gazette Notice
  386. </Typography>
  387. </Link>
  388. </li>
  389. <li>
  390. <Link className="report" to="/setting/report/fullList">
  391. <Typography style={{ opacity: 0.9 }} variant={"pnspsHeaderTitle"} sx={{ ml: 1 }}>
  392. Gazette Notice Full List
  393. </Typography>
  394. </Link>
  395. </li>
  396. </ul>
  397. </li>
  398. {/* ===== Settings dropdown (admin) ===== */}
  399. <li>
  400. <button
  401. type="button"
  402. className="navTrigger setting"
  403. aria-haspopup="true"
  404. aria-expanded={openMenu.settings}
  405. onFocus={() => openKey("settings")}
  406. onBlur={onTriggerBlur("settings")}
  407. onKeyDown={onMenuKeyDown("settings")}
  408. >
  409. <Typography style={{ opacity: 0.9 }} variant={"pnspsHeaderTitle"} sx={{ ml: 1 }}>
  410. Settings
  411. </Typography>
  412. <KeyboardArrowDownIcon sx={{ fontSize: "1vw" }} />
  413. </button>
  414. <ul className="dropdown" data-submenu="settings">
  415. <li>
  416. <Link className="systemSetting" to="/user/profile">
  417. <Typography style={{ opacity: 0.9 }} variant={"pnspsHeaderTitle"} sx={{ ml: 2, mt: 1, mb: 1 }}>
  418. My Profile
  419. </Typography>
  420. </Link>
  421. </li>
  422. <li>
  423. <Link className="manageUser" to={"/user/changePassword"}>
  424. <Typography style={{ opacity: 0.9 }} variant={"pnspsHeaderTitle"} sx={{ ml: 2 }}>
  425. <FormattedMessage id="userChangePassword" />
  426. </Typography>
  427. </Link>
  428. </li>
  429. {isGranted("VIEW_GAZETTE_ISSUE", "MAINTAIN_GAZETTE_ISSUE") ? (
  430. <>
  431. <li>
  432. <Link className="systemSetting" to="/setting/holiday">
  433. <Typography style={{ opacity: 0.9 }} variant={"pnspsHeaderTitle"} sx={{ ml: 2, mt: 1, mb: 1 }}>
  434. Holiday Settings
  435. </Typography>
  436. </Link>
  437. </li>
  438. <li>
  439. <Link className="systemSetting" to="/setting/gazetteissuepage">
  440. <Typography style={{ opacity: 0.9 }} variant={"pnspsHeaderTitle"} sx={{ ml: 2, mt: 1, mb: 1 }}>
  441. Gazette Issues
  442. </Typography>
  443. </Link>
  444. </li>
  445. </>
  446. ) : null}
  447. {isGranted("MAINTAIN_ANNOUNCEMENT") ? (
  448. <li>
  449. <Link className="systemSetting" to="/setting/announcement">
  450. <Typography style={{ opacity: 0.9 }} variant={"pnspsHeaderTitle"} sx={{ ml: 2, mt: 1, mb: 1 }}>
  451. Announcement
  452. </Typography>
  453. </Link>
  454. </li>
  455. ) : null}
  456. {isGranted("MAINTAIN_EMAIL") ? (
  457. <li>
  458. <Link className="systemSetting" to="/setting/emailTemplate">
  459. <Typography style={{ opacity: 0.9 }} variant={"pnspsHeaderTitle"} sx={{ ml: 2, mt: 1, mb: 1 }}>
  460. Email Template
  461. </Typography>
  462. </Link>
  463. </li>
  464. ) : null}
  465. {isGranted("MAINTAIN_DR") ? (
  466. <li>
  467. <Link className="systemSetting" to="/setting/drImport">
  468. <Typography style={{ opacity: 0.9 }} variant={"pnspsHeaderTitle"} sx={{ ml: 2, mt: 1, mb: 1 }}>
  469. DR Import
  470. </Typography>
  471. </Link>
  472. </li>
  473. ) : null}
  474. {isGranted("MAINTAIN_SETTING") ? (
  475. <>
  476. <li>
  477. <Link className="systemSetting" to="/setting/sys">
  478. <Typography style={{ opacity: 0.9 }} variant={"pnspsHeaderTitle"} sx={{ ml: 2, mt: 1, mb: 1 }}>
  479. System Settings
  480. </Typography>
  481. </Link>
  482. </li>
  483. <li>
  484. <Link className="systemSetting" to="/setting/auditLog">
  485. <Typography style={{ opacity: 0.9 }} variant={"pnspsHeaderTitle"} sx={{ ml: 2, mt: 1, mb: 1 }}>
  486. Audit Log
  487. </Typography>
  488. </Link>
  489. </li>
  490. </>
  491. ) : null}
  492. </ul>
  493. </li>
  494. <Box sx={{ display: { xs: "none", sm: "none", md: "block" } }}>
  495. <li>
  496. <Link className="logout" onClick={handleLogout}>
  497. <Typography variant={"pnspsHeaderTitle"} sx={{ ml: 2 }}>
  498. Logout
  499. </Typography>
  500. </Link>
  501. </li>
  502. </Box>
  503. </div>
  504. )}
  505. </div>
  506. ) : (
  507. // ===== Non-GLD login content (your original logic, only dropdown triggers changed to <button>) =====
  508. <div id="individualUserContent" ref={rootNavRef}>
  509. {isPasswordExpiry() ? (
  510. <div id="passwordExpiryedContent">
  511. <li>
  512. <Link className="manageUser" to={"/user/changePassword"}>
  513. <Typography style={{ opacity: 0.9 }} variant={"pnspsHeaderTitle"} sx={{ ml: 2 }}>
  514. <FormattedMessage id="userChangePassword" />
  515. </Typography>
  516. </Link>
  517. </li>
  518. </div>
  519. ) : (
  520. <div id="individualUserContentList">
  521. <li>
  522. <Link className="dashboard" to="/dashboard">
  523. <Typography style={{ opacity: 0.9 }} variant={"pnspsHeaderTitle"} sx={{ ml: 1 }}>
  524. <FormattedMessage id="mainPage" />
  525. </Typography>
  526. </Link>
  527. </li>
  528. <li>
  529. <Link className="myDocumet" to="/publicNotice">
  530. <Typography style={{ opacity: 0.9 }} variant={"pnspsHeaderTitle"} sx={{ ml: 1 }}>
  531. <FormattedMessage id="myPublicNotice" />
  532. </Typography>
  533. </Link>
  534. </li>
  535. <li>
  536. <Link className="documentRecord" to="/proof/search">
  537. <Typography style={{ opacity: 0.9 }} variant={"pnspsHeaderTitle"} sx={{ ml: 1 }}>
  538. <FormattedMessage id="proofRecord" />
  539. </Typography>
  540. </Link>
  541. </li>
  542. {/* ===== Payment History dropdown (non-admin) ===== */}
  543. <li>
  544. {(isCreditorLoggedIn() && haveOrgPaymentRecord()) ||
  545. (isORGLoggedIn() && haveOrgPaymentRecord()) ||
  546. (!isCreditorLoggedIn() && !isORGLoggedIn()) ? (
  547. <>
  548. <button
  549. type="button"
  550. className="navTrigger paymentRecord"
  551. aria-haspopup="true"
  552. aria-expanded={openMenu.paymentHistory}
  553. onFocus={() => openKey("paymentHistory")}
  554. onBlur={onTriggerBlur("paymentHistory")}
  555. onKeyDown={onMenuKeyDown("paymentHistory")}
  556. >
  557. <Typography style={{ opacity: 0.9 }} variant={"pnspsHeaderTitle"} sx={{ ml: 1 }}>
  558. <FormattedMessage id="paymentHistory" />
  559. </Typography>
  560. <KeyboardArrowDownIcon sx={{ fontSize: "1.0rem" }} />
  561. </button>
  562. <ul className="dropdown" data-submenu="paymentHistory">
  563. <li>
  564. <Link className="manageOrgUser" to="/paymentPage/search">
  565. <Typography style={{ opacity: 0.9 }} variant={"pnspsHeaderTitle"} sx={{ ml: 1 }}>
  566. <FormattedMessage id="onlinePaymentHistory" />
  567. </Typography>
  568. </Link>
  569. </li>
  570. {(isCreditorLoggedIn() && haveOrgPaymentRecord()) ||
  571. (isORGLoggedIn() && haveOrgPaymentRecord() && haveOrgDnRecord()) ||
  572. (!isCreditorLoggedIn() && !isORGLoggedIn() && haveOrgDnRecord()) ? (
  573. <li>
  574. <Link className="manageOrgUser" to="/paymentPage/demandNote">
  575. <Typography style={{ opacity: 0.9 }} variant={"pnspsHeaderTitle"} sx={{ ml: 1 }}>
  576. <FormattedMessage id="paymentInfoRecord" />
  577. </Typography>
  578. </Link>
  579. </li>
  580. ) : null}
  581. </ul>
  582. </>
  583. ) : (
  584. <Link className="manageOrgUser" to="/paymentPage/demandNote">
  585. <Typography style={{ opacity: 0.9 }} variant={"pnspsHeaderTitle"} sx={{ ml: 1 }}>
  586. <FormattedMessage id="paymentInfoRecord" />
  587. </Typography>
  588. </Link>
  589. )}
  590. </li>
  591. {/* ===== User Setting dropdown (non-admin) ===== */}
  592. <li>
  593. <button
  594. type="button"
  595. className="navTrigger userSetting"
  596. aria-haspopup="true"
  597. aria-expanded={openMenu.userSetting}
  598. onFocus={() => openKey("userSetting")}
  599. onBlur={onTriggerBlur("userSetting")}
  600. onKeyDown={onMenuKeyDown("userSetting")}
  601. >
  602. <Typography style={{ opacity: 0.9 }} variant={"pnspsHeaderTitle"} sx={{ ml: 1 }}>
  603. <FormattedMessage id="setting" />
  604. </Typography>
  605. <KeyboardArrowDownIcon sx={{ fontSize: "1.0rem" }} />
  606. </button>
  607. <ul className="dropdown" style={{ width: "max-content" }} data-submenu="userSetting">
  608. {isPrimaryLoggedIn() ? (
  609. <>
  610. <li>
  611. <Link className="manageOrgUser" to="setting/manageUser">
  612. <Typography style={{ opacity: 0.9 }} variant={"pnspsHeaderTitle"} sx={{ ml: 2 }}>
  613. <FormattedMessage id="companyOrUserRecord" />
  614. </Typography>
  615. </Link>
  616. </li>
  617. <li>
  618. <Link className="manageUser" to={"/org"}>
  619. <Typography style={{ opacity: 0.9 }} variant={"pnspsHeaderTitle"} sx={{ ml: 2 }}>
  620. <FormattedMessage id="organizationProfile" />
  621. </Typography>
  622. </Link>
  623. </li>
  624. <li>
  625. <Link className="manageUser" to={"/orgUser"}>
  626. <Typography style={{ opacity: 0.9 }} variant={"pnspsHeaderTitle"} sx={{ ml: 2 }}>
  627. <FormattedMessage id="userProfile" />
  628. </Typography>
  629. </Link>
  630. </li>
  631. </>
  632. ) : isINDLoggedIn() ? (
  633. <li>
  634. <Link className="manageUser" to={"/indUser"}>
  635. <Typography style={{ opacity: 0.9 }} variant={"pnspsHeaderTitle"} sx={{ ml: 2 }}>
  636. <FormattedMessage id="userProfile" />
  637. </Typography>
  638. </Link>
  639. </li>
  640. ) : (
  641. <li>
  642. <Link className="manageUser" to={"/orgUser"}>
  643. <Typography style={{ opacity: 0.9 }} variant={"pnspsHeaderTitle"} sx={{ ml: 2 }}>
  644. <FormattedMessage id="userProfile" />
  645. </Typography>
  646. </Link>
  647. </li>
  648. )}
  649. <li>
  650. <Link className="manageUser" to={"/user/changePassword"}>
  651. <Typography style={{ opacity: 0.9 }} variant={"pnspsHeaderTitle"} sx={{ ml: 2 }}>
  652. <FormattedMessage id="userChangePassword" />
  653. </Typography>
  654. </Link>
  655. </li>
  656. </ul>
  657. </li>
  658. </div>
  659. )}
  660. <Box sx={{ display: { xs: "none", sm: "none", md: "block" } }}>
  661. <li>
  662. <Link className="logout" onClick={handleLogout}>
  663. <Typography variant={"pnspsHeaderTitle"} sx={{ ml: 2 }}>
  664. <FormattedMessage id="logout" />
  665. </Typography>
  666. </Link>
  667. </li>
  668. </Box>
  669. </div>
  670. );
  671. // =============================
  672. // Logged-out top nav content
  673. // =============================
  674. const logoutContent = (
  675. <div>
  676. <li>
  677. {!isAfterAboutSwitchDate() ? (
  678. <Link className="login" to="/aboutUs">
  679. <Typography style={{ opacity: 0.9 }} variant={"pnspsHeaderTitle"} sx={{ ml: 2 }}>
  680. <FormattedMessage id="aboutUs" />
  681. </Typography>
  682. </Link>
  683. ) : (
  684. <a className="login" href={getAboutExternalUrlByLocale()} target="_blank" rel="noopener noreferrer">
  685. <Typography style={{ opacity: 0.9 }} variant={"pnspsHeaderTitle"} sx={{ ml: 2 }}>
  686. <FormattedMessage id="aboutUs" />
  687. </Typography>
  688. </a>
  689. )}
  690. </li>
  691. <li>
  692. <Link className="login" to={"/userGuidePub"}>
  693. <Typography style={{ opacity: 0.9 }} variant={"pnspsHeaderTitle"} sx={{ ml: 2 }}>
  694. <FormattedMessage id="userGuide" />
  695. </Typography>
  696. </Link>
  697. </li>
  698. <li>
  699. <Link className="login" to="/login">
  700. <Typography style={{ opacity: 0.9 }} variant={"pnspsHeaderTitle"} sx={{ ml: 2 }}>
  701. <FormattedMessage id="login" />
  702. </Typography>
  703. </Link>
  704. </li>
  705. {sysSetting?.allowRegistration ? (
  706. <li>
  707. <Link className="register" to="/register">
  708. <Typography style={{ opacity: 0.9 }} variant={"pnspsHeaderTitle"} sx={{ ml: 2 }}>
  709. <FormattedMessage id="register" />
  710. </Typography>
  711. </Link>
  712. </li>
  713. ) : null}
  714. </div>
  715. );
  716. const drawer = isUserLoggedIn() ? (
  717. <Stack id="sidebar" direction="column" justifyContent="center" alignItems="center" sx={{ textAlign: "center", width: "300px" }}>
  718. <Box sx={{ mr: 2, mt: 1, display: { md: "none" } }}>
  719. <MobileLogo />
  720. <span style={{ color: checkSysEnv() !== "" ? "red" : "#1976d2" }} id="mobileTitle">
  721. PNSPS
  722. </span>
  723. </Box>
  724. <Divider />
  725. <ul id="sidebartop">{loginContent}</ul>
  726. <Divider />
  727. <ul id="sidebarbottom">
  728. <li>
  729. <Link className="logout" onClick={handleLogout}>
  730. <FormattedMessage id="logout" />
  731. </Link>
  732. </li>
  733. </ul>
  734. </Stack>
  735. ) : (
  736. <Stack id="sidebar" direction="column" justifyContent="center" alignItems="center" onClick={handleDrawerToggle} sx={{ textAlign: "center" }}>
  737. <Box sx={{ mr: 2, mt: 1, display: { md: "none" } }}>
  738. <MobileLogo />
  739. <span style={{ color: checkSysEnv() !== "" ? "red" : "#1976d2" }} id="mobileTitle">
  740. PNSPS
  741. </span>
  742. </Box>
  743. <Divider />
  744. <ul id="logoutContent">{logoutContent}</ul>
  745. <Divider />
  746. </Stack>
  747. );
  748. const container = window !== undefined ? () => window().document.body : undefined;
  749. // =============================
  750. // Render (your layout kept)
  751. // =============================
  752. return isUserLoggedIn() ? (
  753. <Box>
  754. <AppBar component="nav" elevation={0}>
  755. <Toolbar id="nav" width="100%">
  756. {isGLDLoggedIn() ? (
  757. <Stack direction="row" justifyContent="flex-start" alignItems="center" spacing={0} sx={{ width: { xs: "100%", md: "5%" } }}>
  758. <Box mt={0.5} sx={{ flexGrow: 1, display: { xs: "none", sm: "none", md: "block" } }}>
  759. <AdminLogo />
  760. </Box>
  761. <IconButton
  762. color="inherit"
  763. aria-label={intl.formatMessage({ id: "openMenu" })}
  764. aria-expanded={mobileOpen}
  765. edge="start"
  766. onClick={handleDrawerToggle}
  767. sx={{ mr: 2, display: { md: "none" } }}
  768. >
  769. <MenuIcon style={{ color: "#1976d2" }} />
  770. </IconButton>
  771. <Box sx={{ flexGrow: 1, mr: 2, display: { sm: "block", md: "none" } }}>
  772. <Stack direction="row" justifyContent="space-between" alignItems="center" width="100%">
  773. <MobileLogo />
  774. <Stack justifyContent="flex-start" alignItems="flex-start" width="100%" ml={2}>
  775. <span style={{ color: checkSysEnv() !== "" ? "red" : "#1976d2" }} id="mobileTitle">
  776. PNSPS
  777. </span>
  778. </Stack>
  779. <Stack justifyContent="flex-end" alignItems="center">
  780. <span style={{ color: "#B11B1B", fontWeight: "bold", fontSize: "15px" }}>RESTRICTED</span>
  781. </Stack>
  782. </Stack>
  783. </Box>
  784. </Stack>
  785. ) : (
  786. <Stack direction="row" justifyContent="flex-start" alignItems="center" spacing={0} sx={{ width: { xs: "100%", md: "25%" } }}>
  787. <Box sx={{ width: "450px", flexGrow: 1, display: { xs: "none", sm: "none", md: "block" } }}>
  788. <Stack direction="row" justifyContent="flex-start" alignItems="center">
  789. <Logo />
  790. <Stack justifyContent="flex-start" alignItems="center">
  791. <span style={{ color: checkSysEnv() !== "" ? "#B00020" : "#1976d2" }} id="systemTitle">
  792. <FormattedMessage id="PNSPS" />
  793. </span>
  794. </Stack>
  795. </Stack>
  796. </Box>
  797. <IconButton
  798. color="inherit"
  799. aria-label={intl.formatMessage({ id: "openMenu" })}
  800. aria-expanded={mobileOpen}
  801. edge="start"
  802. onClick={handleDrawerToggle}
  803. sx={{ mr: 2, display: { md: "none" } }}
  804. >
  805. <MenuIcon style={{ color: "#1976d2" }} />
  806. </IconButton>
  807. <Box sx={{ flexGrow: 1, mr: 2, display: { sm: "block", md: "none" } }}>
  808. <Stack direction="row" justifyContent="space-between" alignItems="center" width="100%">
  809. <MobileLogo />
  810. <Stack justifyContent="flex-start" alignItems="flex-start" width="100%" ml={2}>
  811. <span style={{ color: checkSysEnv() !== "" ? "red" : "#1976d2" }} id="mobileTitle">
  812. <FormattedMessage id="PNSPS" />
  813. </span>
  814. </Stack>
  815. <Stack justifyContent="flex-end" alignItems="center">
  816. <LocaleSelector />
  817. </Stack>
  818. </Stack>
  819. </Box>
  820. </Stack>
  821. )}
  822. <Box sx={{ display: { xs: "none", sm: "none", md: "block" }, width: "100%" }}>
  823. <Stack direction="row" justifyContent="space-between" alignItems="center" spacing={1}>
  824. <ul id="navbar" width="100%">
  825. {loginContent}
  826. </ul>
  827. <Grid item>
  828. <Grid container direction="column" justifyContent="flex-end" alignItems="center">
  829. {isGLDLoggedIn() ? (
  830. <Grid item>
  831. <span style={{ color: "#B11B1B", fontWeight: "bold", fontSize: "15px" }}>RESTRICTED</span>
  832. </Grid>
  833. ) : (
  834. <Grid item>
  835. <LocaleSelector />
  836. </Grid>
  837. )}
  838. </Grid>
  839. </Grid>
  840. </Stack>
  841. </Box>
  842. </Toolbar>
  843. </AppBar>
  844. <Box component="nav">
  845. <Drawer
  846. container={container}
  847. variant="temporary"
  848. open={mobileOpen}
  849. onClose={handleDrawerToggle}
  850. ModalProps={{ keepMounted: true }}
  851. sx={{
  852. display: { sm: "block", md: "none" },
  853. "& .MuiDrawer-paper": { boxSizing: "border-box", width: drawerWidth },
  854. }}
  855. >
  856. {drawer}
  857. </Drawer>
  858. </Box>
  859. </Box>
  860. ) : (
  861. <Box>
  862. <AppBar component="nav" elevation={0}>
  863. <Toolbar id="nav" width="100%">
  864. <Stack direction="row" justifyContent="flex-start" alignItems="center" spacing={0} sx={{ width: { xs: "100%", md: "25%" } }}>
  865. <Box sx={{ flexGrow: 1, display: { xs: "none", sm: "none", md: "block" } }}>
  866. <Stack direction="row" justifyContent="flex-start" alignItems="center">
  867. <Logo />
  868. <Stack justifyContent="flex-start" alignItems="center">
  869. <span id="systemTitle">
  870. <FormattedMessage id="PNSPS" />
  871. </span>
  872. </Stack>
  873. </Stack>
  874. </Box>
  875. <IconButton
  876. color="inherit"
  877. aria-label={intl.formatMessage({ id: "openMenu" })}
  878. aria-expanded={mobileOpen}
  879. edge="start"
  880. onClick={handleDrawerToggle}
  881. sx={{ mr: 2, display: { md: "none" } }}
  882. >
  883. <MenuIcon style={{ color: "#1976d2" }} />
  884. </IconButton>
  885. <Box sx={{ flexGrow: 1, mr: 2, display: { sm: "block", md: "none" } }}>
  886. <Stack direction="row" justifyContent="space-between" alignItems="center" width="100%">
  887. <MobileLogo />
  888. <Stack justifyContent="flex-start" alignItems="flex-start" width="100%" ml={2}>
  889. <span style={{ color: checkSysEnv() !== "" ? "red" : "#1976d2" }} id="mobileTitle">
  890. <FormattedMessage id="PNSPS" />
  891. </span>
  892. </Stack>
  893. <Stack justifyContent="flex-end" alignItems="center">
  894. <LocaleSelector />
  895. </Stack>
  896. </Stack>
  897. </Box>
  898. </Stack>
  899. <Box sx={{ display: { xs: "none", sm: "none", md: "block" }, width: "75%" }}>
  900. <Stack direction="row" justifyContent="space-between" alignItems="center" spacing={1}>
  901. <ul id="navbar" width="100%">
  902. {logoutContent}
  903. </ul>
  904. <LocaleSelector />
  905. </Stack>
  906. </Box>
  907. </Toolbar>
  908. </AppBar>
  909. <Box component="nav">
  910. <Drawer
  911. container={container}
  912. variant="temporary"
  913. open={mobileOpen}
  914. onClose={handleDrawerToggle}
  915. ModalProps={{ keepMounted: true }}
  916. sx={{
  917. display: { sm: "block", md: "none" },
  918. "& .MuiDrawer-paper": { boxSizing: "border-box", width: drawerWidth },
  919. }}
  920. >
  921. {drawer}
  922. </Drawer>
  923. </Box>
  924. </Box>
  925. );
  926. }
  927. Header.propTypes = {
  928. window: PropTypes.func,
  929. };
  930. export default Header;