65 コミット

作成者 SHA1 メッセージ 日付
  Jason Chuang 467e9731fc Merge branch 'web_access_fix' into CR013B2 2週間前
  Jason Chuang de4d310839 iAM Smart login fix 2週間前
  Jason Chuang f270b5c83a forgot password, double click control 2週間前
  Jason Chuang a6155bf36a iAM Smart handling 2週間前
  Jason Chuang 40948a26f8 fix regsiterForm havent check email 2週間前
  Jason Chuang 5a1d0f077a show messsage if iamsmart user already existing 2週間前
  Jason Chuang 27c54c1bd7 user group validation 2週間前
  Jason Chuang b33a9275b5 fix create user group 2週間前
  Jason Chuang 3bd8bcc000 fix proof error 2週間前
  Jason Chuang 70ea25cce2 fix MUI 2週間前
  Jason Chuang 3da4bb5ae0 loading for export excel 2週間前
  Jason Chuang 22232a6dab other double click button 2週間前
  Jason Chuang 2b3fd67494 registration preview submit button control 3週間前
  Jason Chuang 0904ddba63 fix district and email validation 3週間前
  Jason Chuang b40345170d various changes 3週間前
  Jason Chuang f86a80bfb2 fixed UI out of sync and multiple token expiry alert 3週間前
  Jason Chuang 29f71088b2 update to new CSP with embed font 3週間前
  Jason Chuang a1c8ef0b66 handle double verify link 3週間前
  Jason Chuang fdb8163219 remove summary report 1000 days limit 3週間前
  Jason Chuang 0db8ee2694 fix username and email validation check, used more than 0, then show error 3週間前
  Jason Chuang 35259e9cdd fix audit log search and export criteria bug 3週間前
  Jason Chuang 6c69e10164 fix gld user blank page 3週間前
  Jason Chuang a783b7a895 fix double click on login button 3週間前
  Jason Chuang 15d9736fcf avoid double click to org page 3週間前
  Jason Chuang b6415f1be9 avoid double click issue for public update user record 3週間前
  Jason Chuang 2e4c44629f improve forgot password page 3週間前
  Jason Chuang 40dd3d56ec avoid double click from changing password form 3週間前
  Jason Chuang f009bb8138 handle double verify email 3週間前
  Jason Chuang 0ff322d87a avoid double click to forgot page 1ヶ月前
  Jason Chuang d6ec014a26 add back toastify message box with 30 sec 1ヶ月前
  Jason Chuang cbe3357883 incident update 1ヶ月前
  Jason Chuang 71339bd540 update to avoid displaying too much alert "Login verification has expired, please log in again." 1ヶ月前
  Jason Chuang 7673697565 apply html title and description to match the page contents 1ヶ月前
  Jason Chuang c9b48778ed button add keyboard control 1ヶ月前
  Jason Chuang 3e4f5227b6 button add keyboard control 1ヶ月前
  Jason Chuang ea5529d24b remove toastify and change to dialog box 1ヶ月前
  Jason Chuang 42a8501fd2 fix for concurrent create proof an check create 1ヶ月前
  Jason Chuang 1f16b40fe9 aria-label for careOf 2ヶ月前
  Jason Chuang b648db4023 added back missing i18n 2ヶ月前
  Jason Chuang 20accadbe1 add back aria-label with i18n 2ヶ月前
  Jason Chuang 23918f7cf1 update all aria-label 2ヶ月前
  Jason Chuang 572762b31c add i18n 2ヶ月前
  Jason Chuang 1a85f8f146 update navbar style 2ヶ月前
  Jason Chuang 5aa7d51974 minor adjust login page layout 2ヶ月前
  Jason Chuang d5dc361b87 avoid displaying too much alert "Login verification has expired, please log in again." 2ヶ月前
  Jason Chuang dbb1462474 avoid double click to submit in application form 2ヶ月前
  Jason Chuang 6de845a965 CR020 - remove importantNotice and landingMessage and privacyPolicy to settings 2ヶ月前
  Jason Chuang f6c68b0327 CR020 - remove importantNotice and landingMessage and privacyPolicy to settings 2ヶ月前
  Jason Chuang 1e8e9402d1 fix multiple reply proof and reset username and password 2ヶ月前
  Jason Chuang 933bfe2752 update reset button in FormWizard 2ヶ月前
  Jason Chuang 9a0e515d64 update style. 2ヶ月前
  Jason Chuang 4978621d6b double the message display time 2ヶ月前
  Jason Chuang a865e4e3f3 WCAG 2.0 A 1.3.1 Info and Relationships 2ヶ月前
  Jason Chuang 8bc293c817 Merge branch 'web_access_fix' of https://git.2fi-solutions.com/alex/PNSPS-frontend-MaterialUI into web_access_fix 2ヶ月前
  Jason Chuang bc87f5df36 Async Queue for Fallback Excel with Email 2ヶ月前
  Alex Cheung e7d1c3402e update WCAG to 4.1.2 2ヶ月前
  Alex Cheung 617904b649 WCAG 1.3.1 and add back 1.1.1 2ヶ月前
  Jason Chuang 1a74f172d9 WCAG 2.0, A 4.1.2 Name, Role, Value 2ヶ月前
  Jason Chuang a412582bbc remove border 2ヶ月前
  Jason Chuang 686ca8aeeb WCAG 2.0 AA 1.4.3 Contrast (Minimum) 2ヶ月前
  Jason Chuang 3e7280485f add back footer info 2ヶ月前
  Jason Chuang 27b5128b23 WCAG 2.0, AA 2.4.7 Focus Visible 2ヶ月前
  Jason Chuang bfecc333d6 WCAG 2.1, AA 1.4.11 Non-Text Contrast 2ヶ月前
  Alex Cheung f3f75f97c6 add audio for captcha 2ヶ月前
  Alex Cheung e8690f0baf update 1.1.1 and test audio in window 2ヶ月前
100個のファイルの変更15616行の追加7873行の削除
分割表示
  1. +51
    -0
      docs/csp-apache.conf.md
  2. +12770
    -6394
      package-lock.json
  3. +3
    -0
      package.json
  4. +3
    -8
      public/index.html
  5. +11
    -12
      src/App.js
  6. +19
    -0
      src/assets/fonts.css
  7. +330
    -114
      src/assets/style/navbarStyles.css
  8. +88
    -20
      src/assets/style/styles.css
  9. +41
    -9
      src/auth/index.js
  10. +3
    -0
      src/auth/utils.js
  11. +1
    -5
      src/components/@extended/AnimateButton.js
  12. +23
    -11
      src/components/AdminLogo/index.js
  13. +11
    -3
      src/components/AutoLogoutProvider.js
  14. +62
    -12
      src/components/FiDataGrid.js
  15. +4
    -0
      src/components/Logo/index.js
  16. +27
    -1
      src/components/MainCard.js
  17. +21
    -8
      src/components/MobileLogo/index.js
  18. +33
    -16
      src/components/cards/AuthFooter.js
  19. +3
    -2
      src/components/iAmSmartButton.js
  20. +42
    -0
      src/components/usePageTitle.js
  21. +2
    -1
      src/index.js
  22. +3
    -1
      src/layout/MainLayout/Drawer/index.js
  23. +22
    -10
      src/layout/MainLayout/Header/HeaderContent/LocaleSelector.js
  24. +20
    -13
      src/layout/MainLayout/Header/HeaderContent/MobileSection.js
  25. +16
    -6
      src/layout/MainLayout/Header/HeaderContent/Notification.js
  26. +4
    -2
      src/layout/MainLayout/Header/HeaderContent/Profile/index.js
  27. +824
    -745
      src/layout/MainLayout/Header/index.js
  28. +1
    -1
      src/layout/MainLayout/index.js
  29. +3
    -3
      src/pages/Announcement/Details/AnnouncementForm.js
  30. +4
    -2
      src/pages/Announcement/Search_Public/SearchForm.js
  31. +3
    -2
      src/pages/Announcement/Search_Public/index.js
  32. +16
    -11
      src/pages/AuditLog/AuditLogSearchForm.js
  33. +11
    -4
      src/pages/DemandNote/Create/SearchForm.js
  34. +7
    -3
      src/pages/DemandNote/Details/ApplicationDetailCard.js
  35. +6
    -4
      src/pages/DemandNote/Details/DnDetailCard.js
  36. +5
    -1
      src/pages/DemandNote/Export/SearchForm.js
  37. +11
    -8
      src/pages/DemandNote/Search/DataGrid.js
  38. +13
    -1
      src/pages/DemandNote/Search/SearchForm.js
  39. +1
    -2
      src/pages/DemandNote/Search_Public/DataGrid.js
  40. +11
    -5
      src/pages/DemandNote/Search_Public/SearchForm.js
  41. +3
    -2
      src/pages/DemandNote/Search_Public/index.js
  42. +2
    -4
      src/pages/GFMIS/SearchForm.js
  43. +53
    -30
      src/pages/GFMIS/index.js
  44. +5
    -1
      src/pages/GazetteIssue/ExportForm.js
  45. +5
    -1
      src/pages/GazetteIssue/SearchForm.js
  46. +22
    -5
      src/pages/GazetteIssue/index.js
  47. +8
    -3
      src/pages/Holiday/DataGrid.js
  48. +9
    -5
      src/pages/Holiday/SearchForm.js
  49. +23
    -7
      src/pages/Holiday/index.js
  50. +82
    -16
      src/pages/JVM/index.js
  51. +7
    -3
      src/pages/Message/Details/index.js
  52. +2
    -2
      src/pages/Message/Search/SearchForm.js
  53. +3
    -2
      src/pages/Message/Search/index.js
  54. +57
    -51
      src/pages/Organization/DetailPage/OrganizationCard.js
  55. +42
    -34
      src/pages/Organization/DetailPage/OrganizationPubCard.js
  56. +6
    -2
      src/pages/Organization/DetailPage/index.js
  57. +1
    -1
      src/pages/Organization/DetailPage_FromUser/OrganizationCard_loadFromUser.js
  58. +4
    -0
      src/pages/Organization/DetailPage_FromUser/index.js
  59. +1
    -1
      src/pages/Organization/SearchPage/OrganizationSearchForm.js
  60. +3
    -0
      src/pages/Organization/SearchPage/index.js
  61. +5
    -2
      src/pages/Payment/Details_Public/index.js
  62. +4
    -8
      src/pages/Payment/Search_GLD/SearchForm.js
  63. +9
    -6
      src/pages/Payment/Search_Public/SearchForm.js
  64. +3
    -1
      src/pages/Payment/Search_Public/index.js
  65. +49
    -12
      src/pages/Proof/Create_FromApp/ProofForm.js
  66. +3
    -3
      src/pages/Proof/Create_FromApp/index.js
  67. +52
    -8
      src/pages/Proof/Reply_GLD/ApplicationDetails.js
  68. +36
    -18
      src/pages/Proof/Reply_Public/ProofForm.js
  69. +7
    -9
      src/pages/Proof/Reply_Public/index.js
  70. +5
    -1
      src/pages/Proof/Search_GLD/DataGrid.js
  71. +15
    -16
      src/pages/Proof/Search_GLD/SearchForm.js
  72. +12
    -1
      src/pages/Proof/Search_Public/DataGrid.js
  73. +19
    -9
      src/pages/Proof/Search_Public/SearchForm.js
  74. +4
    -2
      src/pages/Proof/Search_Public/index.js
  75. +6
    -5
      src/pages/PublicNotice/ApplyForm/PublicNoticeApplyForm.js
  76. +5
    -3
      src/pages/PublicNotice/ApplyForm/index.js
  77. +76
    -40
      src/pages/PublicNotice/Details_GLD/ApplicationDetailCard.js
  78. +14
    -5
      src/pages/PublicNotice/Details_GLD/GazetteDetailCard.js
  79. +61
    -21
      src/pages/PublicNotice/Details_GLD/StatusChangeDialog.js
  80. +115
    -21
      src/pages/PublicNotice/Details_GLD/index.js
  81. +8
    -0
      src/pages/PublicNotice/Details_GLD/tabTableDetail/PaymentTab.js
  82. +11
    -2
      src/pages/PublicNotice/Details_GLD/tabTableDetail/ProofTab.js
  83. +8
    -1
      src/pages/PublicNotice/Details_GLD/tabTableDetail/StatusHistoryTab.js
  84. +6
    -5
      src/pages/PublicNotice/Details_GLD/tabTableDetail/TabTable.js
  85. +14
    -4
      src/pages/PublicNotice/Details_Public/ApplicationDetailCard.js
  86. +33
    -10
      src/pages/PublicNotice/Details_Public/StatusChangeDialog.js
  87. +22
    -4
      src/pages/PublicNotice/Details_Public/index.js
  88. +8
    -0
      src/pages/PublicNotice/Details_Public/tabTableDetail/PaymentTab.js
  89. +10
    -3
      src/pages/PublicNotice/Details_Public/tabTableDetail/ProofTab.js
  90. +10
    -2
      src/pages/PublicNotice/Details_Public/tabTableDetail/StatusHistoryTab.js
  91. +1
    -1
      src/pages/PublicNotice/Details_Public/tabTableDetail/TabTable.js
  92. +10
    -2
      src/pages/PublicNotice/ListPanel/BaseGrid.js
  93. +40
    -17
      src/pages/PublicNotice/ListPanel/PendingPaymentTab.js
  94. +16
    -4
      src/pages/PublicNotice/ListPanel/SearchPublicNoticeForm.js
  95. +11
    -2
      src/pages/PublicNotice/ListPanel/SearchPublicNoticeTable.js
  96. +11
    -6
      src/pages/PublicNotice/ListPanel/index.js
  97. +14
    -3
      src/pages/PublicNotice/Search_GLD/DataGrid.js
  98. +4
    -4
      src/pages/PublicNotice/Search_GLD/SearchForm.js
  99. +10
    -0
      src/pages/PublicNotice/Search_Mark_As_Paid_GLD/DataGrid.js
  100. +2
    -2
      src/pages/PublicNotice/Search_Mark_As_Paid_GLD/SearchForm.js

+ 51
- 0
docs/csp-apache.conf.md ファイルの表示

@@ -0,0 +1,51 @@
# Content-Security-Policy (Apache)

Self-hosted fonts: no `fonts.googleapis.com` or `fonts.gstatic.com`. Static assets (including `woff2`) are served from `'self'`.

Adjust `report-uri` if your API base path or host differs (example below targets UAT).

## Enforcing

```apache
Header always set Content-Security-Policy "default-src 'self'; \
base-uri 'self'; \
object-src 'none'; \
frame-ancestors 'none'; \
form-action 'self'; \
script-src 'self'; \
style-src 'self' 'unsafe-inline'; \
style-src-elem 'self' 'unsafe-inline'; \
img-src 'self' data: https://www.w3.org https://w3.org; \
media-src 'self' blob:; \
font-src 'self' data:; \
connect-src 'self'; \
upgrade-insecure-requests"
```

## Report-Only

Same policy plus violation reporting:

```apache
Header always set Content-Security-Policy-Report-Only "default-src 'self'; \
base-uri 'self'; \
object-src 'none'; \
frame-ancestors 'none'; \
form-action 'self'; \
script-src 'self'; \
style-src 'self' 'unsafe-inline'; \
style-src-elem 'self' 'unsafe-inline'; \
img-src 'self' data: https://www.w3.org https://w3.org; \
media-src 'self' blob:; \
font-src 'self' data:; \
connect-src 'self'; \
upgrade-insecure-requests; \
report-uri https://pnspsuat.gld.gov.hk/api/csp-report"
```

## Notes

- **`style-src-elem`**: Explicit, alongside `style-src`, for `<link rel="stylesheet">` behaviour in modern browsers.
- **`img-src`**: Includes `https://www.w3.org` and `https://w3.org` so W3C URLs that redirect between hosts are allowed.
- **`font-src`**: `'self' data:` covers bundled fonts and `data:` URLs if used.
- Add origins to the relevant directive only if you introduce third-party scripts, styles, fonts, or APIs.

+ 12770
- 6394
package-lock.json
ファイル差分が大きすぎるため省略します
ファイルの表示


+ 3
- 0
package.json ファイルの表示

@@ -10,6 +10,9 @@
"@emotion/cache": "^11.10.3",
"@emotion/react": "^11.10.4",
"@emotion/styled": "^11.10.4",
"@fontsource/noto-sans-hk": "^5.2.9",
"@fontsource/noto-sans-sc": "^5.2.9",
"@fontsource/public-sans": "^5.2.7",
"@material-ui/pickers": "^3.3.10",
"@mui/icons-material": "^5.14.1",
"@mui/lab": "^5.0.0-alpha.139",


+ 3
- 8
public/index.html ファイルの表示

@@ -8,10 +8,10 @@
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#ffffff" />
<meta name="msapplication-TileColor" content="#da532c">
<meta name="title" content="PNSPS" />
<meta name="title" content="PNSPS - Public Notice Submission and Payment System" />
<meta
name="description"
content="The Government of the Hong Kong Special Administrative Region Gazette Public Notice Submission and Payment System."
content="Public Notice Submission and Payment System - Government Logistics Department"
/>
<meta
name="keywords"
@@ -19,12 +19,7 @@
/>
<meta name="author" content="Government Logistics Department" />
<link rel="apple-touch-icon" href="%PUBLIC_URL%/apple-touch-icon.png" />
<title>PNSPS</title>
<link rel="preconnect" href="https://fonts.gstatic.com" />
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Poppins:wght@400;500;600;700&family=Roboto:wght@400;500;700&display=swap&family=Public+Sans:wght@400;500;600;700"
rel="stylesheet"
/>
<title>PNSPS - Public Notice Submission and Payment System</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>


+ 11
- 12
src/App.js ファイルの表示

@@ -2,24 +2,23 @@
import Routes from 'routes';
import ThemeCustomization from 'themes';
import ScrollTop from 'components/ScrollTop';
import {ToastContainer} from "react-toastify";
import { PNSPS_THEME } from './themes/themeConst';
import { ThemeProvider } from '@emotion/react';
import { ToastContainer } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import {PNSPS_THEME} from "./themes/themeConst";
import {ThemeProvider} from "@emotion/react";
//import {isUserLoggedIn} from 'utils/Utils';
//import {DefaultRoute} from 'routes/index'
// ==============================|| APP - THEME, ROUTER, LOCAL ||============================== //

const App = () => (
<ThemeCustomization>
<ThemeProvider theme={PNSPS_THEME}>
<ScrollTop>
<Routes>
</Routes>
</ScrollTop>
</ThemeProvider>
<ToastContainer/>
</ThemeCustomization>
<ThemeCustomization>
<ThemeProvider theme={PNSPS_THEME}>
<ScrollTop>
<Routes />
</ScrollTop>
</ThemeProvider>
<ToastContainer />
</ThemeCustomization>
);

export default App;

+ 19
- 0
src/assets/fonts.css ファイルの表示

@@ -0,0 +1,19 @@
/* Self-hosted fonts (bundled via @fontsource; no Google Fonts CDN — CSP-friendly). */

/* Public Sans — MUI theme (`themeConst.js`), charts, config */
@import "@fontsource/public-sans/400.css";
@import "@fontsource/public-sans/500.css";
@import "@fontsource/public-sans/600.css";
@import "@fontsource/public-sans/700.css";

/* Noto Sans HK — `styles.css` / navbar (Latin + Hong Kong glyphs) */
@import "@fontsource/noto-sans-hk/latin-400.css";
@import "@fontsource/noto-sans-hk/chinese-hongkong-400.css";
@import "@fontsource/noto-sans-hk/latin-600.css";
@import "@fontsource/noto-sans-hk/chinese-hongkong-600.css";

/* Noto Sans SC — fallback for simplified Chinese */
@import "@fontsource/noto-sans-sc/latin-400.css";
@import "@fontsource/noto-sans-sc/chinese-simplified-400.css";
@import "@fontsource/noto-sans-sc/latin-600.css";
@import "@fontsource/noto-sans-sc/chinese-simplified-600.css";

+ 330
- 114
src/assets/style/navbarStyles.css ファイルの表示

@@ -1,162 +1,378 @@
/* assets/style/navbarStyles.css */

/* ===== FIX typo ===== */
#nav{
display: flex;
align-items: center;
justify-content: space-between;
background-color: white;
/* padding: 20px 80px; */
box-shadow: 0 5px 15px rgba(0,0 0,0,0.06);
position:fixed;
top: 0px;
width: 100%;
z-index: 9999;
border-bottom: 3px solid #0C489E;
display: flex;
align-items: center;
justify-content: space-between;
background-color: white;
box-shadow: 0 5px 15px rgba(0,0,0,0.06); /* <-- fixed */
position: fixed;
top: 0px;
width: 100%;
z-index: 9999;
border-bottom: 3px solid #0C489E;
min-height: 77px;
}

#navbar > div > li {
height: 77px; /* one single source of truth */
display: flex;
align-items: stretch;
padding: 0; /* IMPORTANT: stop li padding making different widths */
margin: 0;
}

#navbar div li{
padding: 0; /* override your earlier padding: 0 1vw */
}

/* Make dropdown button identical to anchor */
#navbar > div > li > button.navTrigger {
background: transparent;
border: 0;
padding: 0; /* remove default button padding */
margin: 0;
font: inherit; /* inherit font from li */
line-height: 1; /* normalize */
height: auto;
display: inline-flex;
align-items: center;
cursor: pointer;
}

/* Make both anchor and button same vertical box */
#navbar > div > li > a,
#navbar > div > li > button.navTrigger {
height: 100% !important; /* override the old 72px */
padding: 0 24px !important; /* bigger hit area */
display: flex;
align-items: center;
box-sizing: border-box;
}

/* Submenu triggers: same font as app (Noto Sans), bold */
#navbar > div > li > button.navTrigger,
#navbar > div > li > button.navTrigger .MuiTypography-root {
font-weight: 600 !important;
font-family: "Noto Sans HK", "Noto Sans SC", sans-serif !important;
font-size: 1.1rem !important;
}

/* Keep links bold */
#navbar > div > li > a,
#navbar > div > li > a .MuiTypography-root {
font-weight: 600;
}

#navbar div{
display: flex;
align-items: center;
justify-content: center;
display: flex;
align-items: center;
justify-content: center;
}

#navbar div li{
list-style: none;
padding: 0 1vw;
position: relative;
list-style: none;
padding: 0 1vw;
position: relative;
}

/* keep your <a> styling */
#navbar div li a{
text-decoration: none;
font-size: 1.2rem;
font-weight: 600;
/* font-family: 微軟正黑體; */
color: black;
transition: 0.3s ease-in-out;
text-decoration: none;
font-size: 1.2rem;
font-weight: 600;
color: black;
transition: 0.3s ease-in-out;
}

#navbar div li a span{
font-size: 1vw !important;
/* Dropdown trigger buttons: Noto Sans, bold */
#navbar div li button.navTrigger{
background: transparent;
border: 0;
padding: 0;
cursor: pointer;

text-decoration: none;
font-size: 1.1rem;
font-weight: 600;
font-family: "Noto Sans HK", "Noto Sans SC", sans-serif;
color: black;
transition: 0.3s ease-in-out;

display: inline-flex;
align-items: center;
gap: 0.25rem;
}

#navbar div li a span,
#navbar div li a svg{
font-size: 1vw !important;
font-size: 1vw !important;
}

#navbar div li a:hover{
color: #0C489E;
#navbar div li a:hover,
#navbar div li button.navTrigger:hover{
color: #0C489E;
}

#navbar div li a:hover::after,
#navbar div li a:focus::after{
content: "";
width: 80%;
height: 3px;
background:#0C489E;
position: absolute;
bottom: -5px;
left: 20px;
}
#navbar div li ul {
#navbar div li a:focus-visible::after,
#navbar div li button.navTrigger:hover::after,
#navbar div li button.navTrigger:focus-visible::after{
content: "";
width: 80%;
height: 3px;
background:#0C489E;
position: absolute;
bottom: -5px;
left: 20px;
}

/* submenu base */
#navbar div li ul{
background: white;
visibility: hidden;
opacity: 0;
min-width: 18rem;
position: absolute;
/* transition: all 0.5s ease; */
left: 0;
display: none;
padding-left: 0px;
padding-bottom: 7px;
/* border: 1px solid #0C489E; */
background-clip: padding-box;
border: 1px solid rgba(0,0,0,.15);
border-radius: 0.25rem;
}
#navbar div li:hover > ul{
visibility: visible;
opacity: 1;
display: block
}
/* #navbar div li:focus-within > ul,
#navbar div li ul:hover,
#navbar div li ul:focus {

/* hover open */
#navbar div li:hover > ul{
visibility: visible;
opacity: 1;
display: block;
}

/* keyboard open */
#navbar div li:focus-within > ul{
visibility: visible;
opacity: 1;
display: block
} */
display: block;
}

/* Navbar focus ring */
#navbar a:focus,
#navbar button.navTrigger:focus{
outline: none;
}

#navbar a:focus-visible,
#navbar button.navTrigger:focus-visible{
outline: 3px solid #0C489E;
outline-offset: 2px;
border-radius: 10px;
}

/* Top-level links (Dashboard, Application, Proof, Logout, etc.) */
#navbar a.dashboard,
#navbar a.application,
#navbar a.proof,
#navbar a.myDocumet,
#navbar a.documentRecord,
#navbar a.manageOrgUser,
#navbar a.manageUser,
#navbar a.systemSetting,
#navbar a.report,
#navbar a.payment,
#navbar a.user,
#navbar a.logout {
font-size: 1.1rem !important;
font-weight: 600 !important;
}

#navbar a.dashboard .MuiTypography-root,
#navbar a.application .MuiTypography-root,
#navbar a.proof .MuiTypography-root,
#navbar a.myDocumet .MuiTypography-root,
#navbar a.documentRecord .MuiTypography-root,
#navbar a.manageOrgUser .MuiTypography-root,
#navbar a.manageUser .MuiTypography-root,
#navbar a.systemSetting .MuiTypography-root,
#navbar a.report .MuiTypography-root,
#navbar a.payment .MuiTypography-root,
#navbar a.user .MuiTypography-root,
#navbar a.logout .MuiTypography-root {
font-size: 1.1rem !important;
font-weight: 600 !important;
}

/* Submenu triggers: Noto Sans, 1.1rem, bold */
#navbar button.navTrigger.paymentTop,
#navbar button.navTrigger.client,
#navbar button.navTrigger.setting,
#navbar button.navTrigger.paymentRecord,
#navbar button.navTrigger.userSetting,
#navbar button.navTrigger.paymentTop .MuiTypography-root,
#navbar button.navTrigger.client .MuiTypography-root,
#navbar button.navTrigger.setting .MuiTypography-root,
#navbar button.navTrigger.paymentRecord .MuiTypography-root,
#navbar button.navTrigger.userSetting .MuiTypography-root {
font-size: 1.1rem !important;
font-weight: 600 !important;
font-family: "Noto Sans HK", "Noto Sans SC", sans-serif !important;
}

/* Make them bigger + bold */
#navbar a.login,
#navbar a.register,
#navbar a.login .MuiTypography-root,
#navbar a.register .MuiTypography-root {
font-size: 1.1rem !important;
font-weight: 600 !important;
}

/* ===== your existing sidebar (unchanged) ===== */
#systemTitle{
text-decoration: none;
font-size: 1.3rem;
font-weight: 600;
color: #0C489E;
transition: 0.3s ease-in-out;
/* font-family: 微軟正黑體; */
text-align: center;
text-decoration: none;
font-size: 1.1rem;
font-weight: 600;
color: #0C489E;
transition: 0.3s ease-in-out;
text-align: center;
}
#mobileTitle{
text-decoration: none;
font-size: 1.2rem;
font-weight: 600;
color: #0C489E;
transition: 0.3s ease-in-out;
/* font-family: 微軟正黑體; */
text-align: center;
text-decoration: none;
font-size: 1.1rem;
font-weight: 600;
color: #0C489E;
transition: 0.3s ease-in-out;
text-align: center;
}
#sidebar{
font-size: 1.3rem;
font-weight: 600;
/* font-family: 微軟正黑體; */
font-size: 1.1rem;
font-weight: 600;
}
#sidebartop{
align-items: center;
justify-content: start;
padding: 0;
display: flex;
width: 100%;
align-items: center;
justify-content: start;
padding: 0;
display: flex;
width: 100%;
}
#logoutContent{
align-items: center;
justify-content: center;
padding: 0;
display: flex;
width: 100%;
align-items: center;
justify-content: center;
padding: 0;
display: flex;
width: 100%;
}
#sidebarbottom{
align-items: center;
justify-content: center;
padding: 0;
display: flex;
align-items: center;
justify-content: center;
padding: 0;
display: flex;
}
#sidebar li{
list-style: none;
padding: 0 20px;
position: relative;
text-align: left;
list-style: none;
padding: 0 20px;
position: relative;
text-align: left;
}
#sidebar li a{
text-decoration: none;
font-size: 1.3rem;
font-weight: 600;
/* font-family: 微軟正黑體; */
color: black;
transition: 0.3s ease-in-out;
text-decoration: none;
font-size: 1.1rem;
font-weight: 600;
color: black;
transition: 0.3s ease-in-out;
}
#sidebar li a:hover{
color: #0C489E;
}
#sidebar div li ul {
background: white;
visibility: hidden;
opacity: 0;
min-width: 16rem;
position: relative;
/* transition: all 0.5s ease; */
left: 0;
display: none;
padding-left: 0px;
/* border: 1px solid #0C489E; */
}
#sidebar div li:hover > ul,
#sidebar div li:focus-within > ul,
#sidebar div li ul:hover,
#sidebar div li ul:focus {
visibility: visible;
opacity: 1;
display: block
}
color: #0C489E;
}
#sidebar div li ul{
background: white;
visibility: hidden;
opacity: 0;
min-width: 16rem;
position: relative;
left: 0;
display: none;
padding-left: 0px;
}
#sidebar div li:hover > ul,
#sidebar div li:focus-within > ul,
#sidebar div li ul:hover,
#sidebar div li ul:focus{
visibility: visible;
opacity: 1;
display: block;
}

/* ===== Mobile Drawer / Sidebar menu styling ===== */
#sidebar ul {
width: 100%;
padding: 0;
margin: 0;
}

#sidebar li{
width: 100%;
padding: 0; /* remove the old 0 20px */
margin: 0;
}

/* Make BOTH links and dropdown buttons look the same */
#sidebar li > a,
#sidebar li > button.navTrigger{
width: 100%;
display: flex;
align-items: center;
justify-content: space-between; /* text left, arrow right */
gap: 12px;

background: transparent;
border: 0; /* kill grey border */
box-shadow: none;
outline: none;

padding: 14px 20px;
text-align: left;

font-size: 1.3rem;
font-weight: 600;
color: black;
cursor: pointer;
}

/* Hover / focus */
#sidebar li > a:hover,
#sidebar li > button.navTrigger:hover{
color: #0C489E;
}

#sidebar li > a:focus-visible,
#sidebar li > button.navTrigger:focus-visible{
outline: 3px solid #0C489E;
outline-offset: 2px;
border-radius: 10px;
}

/* Submenu (indent + full width) */
#sidebar li ul{
width: 100%;
padding: 6px 0 6px 0;
margin: 0;
display: none;
visibility: hidden;
opacity: 0;
}

/* Open on focus-within (tap/click focuses button) */
#sidebar li:focus-within > ul{
display: block;
visibility: visible;
opacity: 1;
}

#sidebar li ul li > a{
padding: 12px 20px 12px 40px; /* indent submenu */
font-size: 1.15rem;
font-weight: 600;
}

+ 88
- 20
src/assets/style/styles.css ファイルの表示

@@ -1,6 +1,3 @@
@import url('https://fonts.googleapis.com/css?family=Noto+Sans+HK|Noto+Sans+SC&display=swap');
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+SC&display=swap');

html,
body,
#root,
@@ -52,26 +49,97 @@ img

img:hover{-webkit-filter:none;}


a:link {
color: #1890ff;
a:link,
a:visited,
a:hover,
a:active {
color: #005FCC; /* WCAG AA compliant */
background-color: transparent;
text-decoration: none;
text-decoration: underline;
}
a:visited {
color: #1890ff;
background-color: transparent;
text-decoration: none;

/* iframe#webpack-dev-server-client-overlay{display:none!important} */

/* ===== WCAG 2.4.7 Focus Visible (Keyboard only) ===== */
:where(
a,
button,
input,
select,
textarea,
summary,
[role="button"],
[role="link"],
[tabindex]:not([tabindex="-1"])
):focus-visible {
outline: 3px solid #0C489E;
outline-offset: 2px;
border-radius: 4px;
}
a:hover {
color: #1890ff;
background-color: transparent;
text-decoration: none;

/* Suppress mouse focus */
:where(
a,
button,
input,
select,
textarea,
summary,
[role="button"],
[role="link"],
[tabindex]:not([tabindex="-1"])
):focus:not(:focus-visible) {
outline: none;
}
a:active {
color: #1890ff;
background-color: transparent;
text-decoration: none;

/* ===== MUI DataGrid keyboard focus (WCAG 2.4.7 / 2.4.11) ===== */
.MuiDataGrid-columnHeader:focus-visible,
.MuiDataGrid-cell:focus-visible {
outline: 3px solid #0C489E;
outline-offset: -2px;
box-shadow: 0 0 0 3px rgba(12, 72, 158, 0.25);
}

/* Contained buttons only */
.MuiButton-contained {
box-shadow: none;
}

.MuiButton-contained:hover {
}

/* iAM Smart button — keep green border */
.MuiButton-outlinedIAmSmart {
color: #2E7D32; /* green text */
border: 1px solid #2E7D32; /* keep your green */
}

.MuiButton-outlinedIAmSmart:hover {
color: #2E7D32; /* green text */
border: 1px solid #2E7D32;
background-color: rgba(46, 125, 50, 0.08); /* optional */
}

/* ===== Outlined button focus ===== */
.MuiButton-outlined:focus-visible {
outline: 3px solid #0C489E;
outline-offset: 2px;
}

/* Step icon number color (text inside the circle) */
.MuiStepIcon-text {
fill: #1A1A1A; /* dark text, high contrast */
}
/* Placeholder text contrast */
.MuiInputBase-input::placeholder {
color: #6B6B6B; /* passes 4.5:1 on white */
opacity: 1; /* IMPORTANT */
}

/* iframe#webpack-dev-server-client-overlay{display:none!important} */
/* WCAG-safe error text color */
.Mui-error,
.MuiFormHelperText-root.Mui-error,
.MuiTypography-errorMessage1 {
color: #B00020; /* dark red, AA compliant */
opacity: 1; /* remove faded appearance */
}

+ 41
- 9
src/auth/index.js ファイルの表示

@@ -13,6 +13,12 @@ export const windowCount = 'windowCount'
import {useNavigate} from "react-router-dom";
import {useDispatch} from "react-redux";
import { REFRESH_TOKEN } from 'utils/ApiPathConst';
import { getMessage } from 'utils/getI18nMessage';

// Guard so we only register interceptors once (ThemeRoutes re-renders add duplicate handlers otherwise)
let axiosInterceptorsSetup = false;
// In-memory guard so only one 401/logout flow shows the alert (avoids race when multiple 401s or interceptors run)
let expiredAlertShownInMemory = false;

// ** Handle User Login
export const handleLogin = data => {
@@ -39,6 +45,8 @@ export const handleLogin = data => {
localStorage.setItem(windowCount, '0')
localStorage.setItem(predictProductionQty, '0')
localStorage.setItem(predictUsageCount, '0')
localStorage.removeItem('expiredAlertShown')
expiredAlertShownInMemory = false
}
}

@@ -94,7 +102,9 @@ export const handleLogoutFunction = () => {
localStorage.removeItem('transactionid')
localStorage.removeItem('searchCriteria')
//localStorage.removeItem(config.storageUserRoleKeyName)
localStorage.removeItem('expiredAlertShown')
// Do not clear expiredAlertShown / expiredAlertShownInMemory here: logout runs right after
// the token-expired alert, and parallel 401s would each show another popup if we reset.
// Reset those only on successful login (handleLogin).
localStorage.removeItem(refreshIntervalName)
localStorage.removeItem(windowCount)
localStorage.removeItem(predictProductionQty)
@@ -109,7 +119,13 @@ export const SetupAxiosInterceptors = () => {
const dispatch = useDispatch();
//const updateLastRequestTime = useContext(TimerContext);
let isRefreshToken= false;

// Avoid stacking interceptors on every ThemeRoutes re-render (would cause multiple alerts on 401)
if (axiosInterceptorsSetup) {
return;
}
axiosInterceptorsSetup = true;

axios.interceptors.request.use(
config => {
// ** Get token from localStorage
@@ -134,7 +150,17 @@ export const SetupAxiosInterceptors = () => {
},
async (error) => {
// const { config, response: { status } } = error
if (error.response.status === 401 && error.config.url !== apiPath + REFRESH_TOKEN) {
const status = error.response?.status
const reqUrl = error.config?.url || ''
// iAM Smart registration: HKID already linked — caller must show /iamsmart/pleaseLogin (not refresh/logout)
if (
status === 401 &&
reqUrl.includes('/smart/getProfile') &&
error.response?.data?.exception === 'msg: account created'
) {
return Promise.reject(error)
}
if (status === 401 && error.config?.url !== apiPath + REFRESH_TOKEN) {
// Make a request to refresh the access token
const refreshToken = localStorage.getItem('refreshToken');
if (isRefreshToken) {
@@ -156,17 +182,23 @@ export const SetupAxiosInterceptors = () => {
}
})
.catch((refreshError) => {
isRefreshToken = false;
if (!expiredAlertShownInMemory && localStorage.getItem("expiredAlertShown") === null) {
expiredAlertShownInMemory = true;
localStorage.setItem("expiredAlertShown", "true");
alert(getMessage("autoLogout"));
}
dispatch(handleLogoutFunction());
navigate('/login');
isRefreshToken = false;
window.location.reload();
throw refreshError;
});
} else {
if (error.response.status === 401) {
if (localStorage.getItem("expiredAlertShown") === null) {
localStorage.setItem("expiredAlertShown", true)
alert("登入驗證已過期,請重新登入。")
if (error.response && error.response.status === 401) {
if (!expiredAlertShownInMemory && localStorage.getItem("expiredAlertShown") === null) {
expiredAlertShownInMemory = true;
localStorage.setItem("expiredAlertShown", "true");
alert(getMessage("autoLogout"));
}
}
@@ -191,7 +223,7 @@ export const SetupAxiosInterceptors = () => {
await window.location.reload();
}

if (error.response.status === 500){
if (error.response?.status === 500){
//setIsUploading(false);
}
// console.log(error)


+ 3
- 0
src/auth/utils.js ファイルの表示

@@ -171,3 +171,6 @@ export const getPaymentMethodByCode = (code) => {
return "other";
};

export function setPageTitle(title) {
document.title = `${title} - PNSPS | Government Logistics Department`;
}

+ 1
- 5
src/components/@extended/AnimateButton.js ファイルの表示

@@ -5,7 +5,7 @@ import { motion } from 'framer-motion';

// ==============================|| ANIMATION BUTTON ||============================== //

export default function AnimateButton({ children, type }) {
export default function AnimateButton({ children, type = 'scale' }) {
switch (type) {
case 'rotate': // only available in paid version
case 'slide': // only available in paid version
@@ -23,7 +23,3 @@ AnimateButton.propTypes = {
children: PropTypes.node,
type: PropTypes.oneOf(['slide', 'scale', 'rotate'])
};

AnimateButton.defaultProps = {
type: 'scale'
};

+ 23
- 11
src/components/AdminLogo/index.js ファイルの表示

@@ -15,24 +15,36 @@ import {
checkSysEnv
} from "utils/Utils";

import {useIntl} from "react-intl";
// ==============================|| MAIN LOGO ||============================== //

const LogoSection = ({ sx, to }) => {
const { defaultId } = useSelector((state) => state.menu);
const dispatch = useDispatch();
const intl = useIntl();
return (
<Stack direction="column" justifyContent="center" alignItems="center" >
<ButtonBase
disableRipple
component={Link}
onClick={() => dispatch(activeItem({ openItem: [defaultId] }))}
to={!to ? config.defaultPath : to}
sx={sx}
>
<ButtonBase
disableRipple
component={Link}
onClick={() => dispatch(activeItem({ openItem: [defaultId] }))}
to={!to ? config.defaultPath : to}
aria-label={intl.formatMessage({ id: "PNSPS", defaultMessage: "PNSPS" })}
sx={{
...sx,
/* ✅ WCAG 2.4.7 focus indicator */
'&:focus-visible': {
outline: '3px solid #0C489E',
outlineOffset: '2px',
borderRadius: '6px'
}
}}
>
<Stack direction="column" justifyContent="center" alignItems="center" >
<Logo />
</ButtonBase>
<span style={{ color: checkSysEnv()!=''?'red':'#0C489E'}} id="systemTitle">PNSPS</span>
</Stack>
<span style={{ color: checkSysEnv()!=''?'red':'#0C489E'}} id="systemTitle">PNSPS</span>
</Stack>
</ButtonBase>

);
};


+ 11
- 3
src/components/AutoLogoutProvider.js ファイルの表示

@@ -1,8 +1,9 @@
import React, { createContext, useState, useEffect } from 'react';
import React, { createContext, useState, useEffect, useRef } from 'react';
import {useNavigate} from "react-router-dom";
import {useIdleTimer} from "react-idle-timer";
import { handleLogoutFunction } from 'auth/index';
import { useDispatch } from "react-redux";
import { useIntl } from "react-intl";
import {
isUserLoggedIn,
isGLDLoggedIn,
@@ -12,9 +13,11 @@ import {
const TimerContext = createContext();

const AutoLogoutProvider = ({ children }) => {
const intl = useIntl();
const [lastRequestTime, setLastRequestTime] = useState(Date.now());
const navigate = useNavigate();
const [logoutInterval, setLogoutInterval] = useState(1);
const idleLogoutTriggeredRef = useRef(false);
// const [remainingInterval] = useState(5);
const [state, setState] = useState('Active');
const dispatch = useDispatch()
@@ -93,8 +96,13 @@ const AutoLogoutProvider = ({ children }) => {
// console.log(remainingInterval * 60);
// console.log(logoutInterval * 60 * 1000 - timeElapsed)
if (timeElapsed >= logoutInterval * 60 * 1000) {
if(isUserLoggedIn()){
alert("登入驗證已過期,請重新登入。")
if (isUserLoggedIn() && !idleLogoutTriggeredRef.current) {
idleLogoutTriggeredRef.current = true;
// Skip alert if token-expiry (axios 401) already showed one, to avoid duplicate alerts
if (localStorage.getItem("expiredAlertShown") === null) {
localStorage.setItem("expiredAlertShown", "true");
alert(intl.formatMessage({ id: "autoLogout" }));
}
dispatch(handleLogoutFunction());
navigate('/login');
window.location.reload();


+ 62
- 12
src/components/FiDataGrid.js ファイルの表示

@@ -1,5 +1,5 @@
// material-ui
import { useState, useEffect } from 'react';
import { useState, useEffect, useRef } from 'react';
import { Box } from "@mui/material";
import {
DataGrid, GridOverlay,
@@ -202,9 +202,49 @@ export function FiDataGrid({ rows, columns, sx, autoHeight = true,
});
}

const gridRootRef = useRef(null);

useEffect(() => {
const root = gridRootRef.current;
if (!root) return;

const sortText = intl.formatMessage({ id: "sort", defaultMessage: "Sort" });

const apply = () => {
// 1) Make ALL column headers tabbable (optional; DataGrid already manages focus well)
root
.querySelectorAll('.MuiDataGrid-columnHeaders [role="columnheader"]')
.forEach((el) => {
if (el.getAttribute("tabindex") !== "0") el.setAttribute("tabindex", "0");
});

// 2) Localize sort icon button label (handles "sort"/"Sort"/any old value)
const sortButtons = root.querySelectorAll(
'.MuiDataGrid-columnHeaders button.MuiIconButton-root'
);

sortButtons.forEach((btn) => {
const al = (btn.getAttribute("aria-label") || "").trim().toLowerCase();
const ti = (btn.getAttribute("title") || "").trim().toLowerCase();

// Only rewrite the ones that are the sort icon buttons
if (al === "sort" || ti === "sort") {
btn.setAttribute("aria-label", sortText);
btn.setAttribute("title", sortText);
}
});
};

apply();

const obs = new MutationObserver(apply);
obs.observe(root, { childList: true, subtree: true });

return () => obs.disconnect();
}, [intl]);

return (
<Box sx={containerSx}>
<Box sx={containerSx} ref={gridRootRef} role="table">
<DataGrid
{...props}
rows={_rows}
@@ -239,16 +279,26 @@ export function FiDataGrid({ rows, columns, sx, autoHeight = true,
? {
Pagination: () => (
<TablePagination
count={rowCount || 0}
page={page}
rowsPerPage={pageSize}
rowsPerPageOptions={_pageSizeOptions}
labelDisplayedRows={() =>
`${(_rows?.length ? page * pageSize + 1 : 0)}-${page * pageSize + (_rows?.length ?? 0)} ${intl.formatMessage({ id: "of" })} ${rowCount}`
}
labelRowsPerPage={intl.formatMessage({ id: "rowsPerPage" }) + ":"}
onPageChange={handleChangePage}
onRowsPerPageChange={handleChangePageSize}
component="div"
count={rowCount || 0}
page={page}
rowsPerPage={pageSize}
rowsPerPageOptions={_pageSizeOptions}
labelDisplayedRows={() =>
`${(_rows?.length ? page * pageSize + 1 : 0)}-${page * pageSize + (_rows?.length ?? 0)} ${intl.formatMessage({ id: "of" })} ${rowCount}`
}
labelRowsPerPage={intl.formatMessage({ id: "rowsPerPage" }) + ":"}
getItemAriaLabel={(type) => {
if (type === 'previous') {
return intl.formatMessage({ id: 'paginationPrev' });
}
if (type === 'next') {
return intl.formatMessage({ id: 'paginationNext' });
}
return '';
}}
onPageChange={handleChangePage}
onRowsPerPageChange={handleChangePageSize}
/>
),
}


+ 4
- 0
src/components/Logo/index.js ファイルの表示

@@ -9,18 +9,22 @@ import { useDispatch, useSelector } from 'react-redux';
import Logo from './Logo';
import config from 'config';
import { activeItem } from 'store/reducers/menu';
import {useIntl} from "react-intl";

// ==============================|| MAIN LOGO ||============================== //

const LogoSection = ({ sx, to }) => {
const { defaultId } = useSelector((state) => state.menu);
const dispatch = useDispatch();
const intl = useIntl();

return (
<ButtonBase
disableRipple
component={Link}
onClick={() => dispatch(activeItem({ openItem: [defaultId] }))}
to={!to ? config.defaultPath : to}
aria-label={intl.formatMessage({ id: "PNSPS", defaultMessage: "PNSPS" })}
sx={sx}
>
<Logo />


+ 27
- 1
src/components/MainCard.js ファイルの表示

@@ -1,5 +1,6 @@
import PropTypes from 'prop-types';
import { forwardRef } from 'react';
import omit from 'lodash/omit';

// material-ui
import { useTheme } from '@mui/material/styles';
@@ -8,6 +9,29 @@ import { Card, CardContent, CardHeader, Divider, Typography, Grid } from '@mui/m
// project import
import Highlighter from './third-party/Highlighter';

/** Props often mistaken for Card / Grid / sx — must not reach the underlying DOM node via `{...others}`. */
const PROPS_OMIT_FROM_CARD = [
'backgroundColor',
'bgcolor',
'xs',
'sm',
'md',
'lg',
'xl',
'item',
'container',
'spacing',
'direction',
'justify',
'alignItems',
'alignContent',
'flexWrap',
'wrap',
'zeroMinWidth',
'rowSpacing',
'columnSpacing'
];

// header style
const headerSX = {
p: 2.5,
@@ -38,6 +62,8 @@ const MainCard = forwardRef(
const theme = useTheme();
boxShadow = theme.palette.mode === 'dark' ? boxShadow || true : boxShadow;

const cardProps = omit(others, PROPS_OMIT_FROM_CARD);

return (
<Grid container direction="column"
// alignItems="center"
@@ -46,7 +72,7 @@ const MainCard = forwardRef(
<Card
elevation={elevation || 0}
ref={ref}
{...others}
{...cardProps}
sx={{
alignItems: "center",
border: border ? '1px solid' : 'none',


+ 21
- 8
src/components/MobileLogo/index.js ファイルの表示

@@ -9,22 +9,35 @@ import { useDispatch, useSelector } from 'react-redux';
import Logo from './MobileLogo';
import config from 'config';
import { activeItem } from 'store/reducers/menu';
import {useIntl} from "react-intl";

// ==============================|| MAIN LOGO ||============================== //

const LogoSection = ({ sx, to }) => {
const intl = useIntl();

const { defaultId } = useSelector((state) => state.menu);
const dispatch = useDispatch();
return (
<ButtonBase
disableRipple
component={Link}
onClick={() => dispatch(activeItem({ openItem: [defaultId] }))}
to={!to ? config.defaultPath : to}
sx={sx}
>
<Logo />
</ButtonBase>
disableRipple
component={Link}
onClick={() => dispatch(activeItem({ openItem: [defaultId] }))}
to={!to ? config.defaultPath : to}
aria-label={intl.formatMessage({ id: "PNSPS" })}
sx={{
...sx,

/* WCAG 2.4.7 – visible keyboard focus */
'&:focus-visible': {
outline: '3px solid #0C489E',
outlineOffset: '2px',
borderRadius: '6px'
}
}}
>
<Logo />
</ButtonBase>
);
};



+ 33
- 16
src/components/cards/AuthFooter.js ファイルの表示

@@ -2,6 +2,7 @@
import { useMediaQuery, Container, Link, Typography, Stack } from '@mui/material';
import bhkLogo from 'assets/images/BHK_logo_rgb_zh-hk.png';
import {FormattedMessage} from "react-intl";
import {useIntl} from "react-intl";
import {
isGLDLoggedIn,
} from "utils/Utils";
@@ -9,9 +10,14 @@ import {

const AuthFooter = () => {
const matchDownSM = useMediaQuery((theme) => theme.breakpoints.down('sm'));
const intl = useIntl();

const bhkAlt = intl.formatMessage({ id: "bhkLogoAlt" });
const wcagAlt = intl.formatMessage({ id: "wcagAaAlt" });

return (
<Container maxWidth= "xl" sx={{minHeight: '5vh'}}>
<Container maxWidth="xl" sx={{ minHeight: '5vh' }}>

<Stack
direction={matchDownSM ? 'column' : 'row'}
justifyContent={matchDownSM ? 'center' : 'flex-start'}
@@ -19,7 +25,11 @@ const AuthFooter = () => {
textAlign={matchDownSM ? 'center' : 'inherit'}
alignItems="center"
>
<Typography variant="subtitle2" color="secondary" component="span">
<Typography
variant="subtitle2"
component="span"
sx={{ color: '#4A4A4A' }}
>
2024 &copy; <FormattedMessage id="HKGLD" />
</Typography>
<Typography
@@ -44,22 +54,29 @@ const AuthFooter = () => {
</Typography>
</Stack>
<Stack direction={matchDownSM ? 'column' : 'row'} spacing={matchDownSM ? 1 : 3} textAlign={matchDownSM ? 'center' : 'inherit'} justifyContent={matchDownSM?"center":"flex-end"}>
{!isGLDLoggedIn()?
<a href="https://www.w3.org/WAI/WCAG2AA-Conformance"
title="Explanation of WCAG 2 Level AA conformance">
<img height="32" width="88"
src="https://www.w3.org/WAI/wcag2AA"
alt="Level AA conformance,
W3C WAI Web Content Accessibility Guidelines 2.0"/>
</a>:null
}

<a href="https://www.brandhk.gov.hk/zh-hk"
title="Brand Hong Kong">
<img src={bhkLogo} alt="logo" height="32" width="88"
{!isGLDLoggedIn()? (
<a
href="https://www.w3.org/WAI/WCAG2AA-Conformance"
title="Explanation of WCAG 2 Level AA conformance"
>
<img
height="32"
width="88"
src="https://www.w3.org/WAI/wcag2AA"
alt={wcagAlt}
/>
</a>
</Stack>
) : null}

<a href="https://www.brandhk.gov.hk/zh-hk" title="Brand Hong Kong">
<img
src={bhkLogo}
alt={bhkAlt}
height="32"
width="88"
/>
</a>
</Stack>
</Container>
);
};


+ 3
- 2
src/components/iAmSmartButton.js ファイルの表示

@@ -1,5 +1,6 @@
// material-ui
import {useState, useEffect} from 'react';
import { useIntl } from 'react-intl';
import iAmSmartICon from 'assets/images/icons/icon_iAmSmart.png';
import {
Button,
@@ -9,7 +10,7 @@ import {
// ==============================|| EVENT TABLE ||============================== //

export function IAmSmartButton({ label, onClickFun, fullWidth }) {
const intl = useIntl();
const [_label, set_label] = useState("");
useEffect(()=>{
@@ -23,7 +24,7 @@ export function IAmSmartButton({ label, onClickFun, fullWidth }) {
}

return (
<Button onClick={()=>doOnClick()} sx={{textTransform: 'none'}} color="iAmSmart" fullWidth={fullWidth} size="large" variant="outlined" startIcon={<img src={iAmSmartICon} alt="iAM Smart" width="30" />}>
<Button onClick={()=>doOnClick()} sx={{textTransform: 'none'}} color="iAmSmart" fullWidth={fullWidth} size="large" variant="outlined" startIcon={<img src={iAmSmartICon} alt={intl.formatMessage({ id: 'iAmSmartAlt' })} width="30" />}>
<Typography variant="h5">
{_label}
</Typography>


+ 42
- 0
src/components/usePageTitle.js ファイルの表示

@@ -0,0 +1,42 @@
import { useEffect } from "react";
import { useIntl } from "react-intl";

export default function usePageTitle(messageIdOrText) {
const intl = useIntl();

useEffect(() => {
let pageTitle;
let systemName;
let gldName;

// If string looks like an intl id, try translate
try {
pageTitle = intl.formatMessage({ id: messageIdOrText });
systemName = intl.formatMessage({ id: "PNSPS_fullname" });
gldName = intl.formatMessage({ id: "HKGLD" });
} catch {
pageTitle = messageIdOrText;
}

const fullTitle = `${pageTitle} - ${systemName} | ${gldName}`;

// Update document title (tab title)
document.title = fullTitle;

// Update <meta name="title"> and <meta name="description"> so they're language-dependent too
const metaTitle = document.querySelector('meta[name="title"]');
if (metaTitle) {
metaTitle.setAttribute("content", fullTitle);
}

const metaDescription = document.querySelector('meta[name="description"]');
if (metaDescription) {
metaDescription.setAttribute("content", fullTitle);
}

// Keep <html lang="..."> in sync with current locale
if (document.documentElement) {
document.documentElement.lang = intl.locale || "en";
}
}, [messageIdOrText, intl]);
}

+ 2
- 1
src/index.js ファイルの表示

@@ -1,7 +1,8 @@
import { StrictMode, useEffect, useContext } from 'react';
import { createRoot } from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import "./assets/style/styles.css"
import './assets/fonts.css';
import './assets/style/styles.css';

// scroll bar
import 'simplebar/src/simplebar.css';


+ 3
- 1
src/layout/MainLayout/Drawer/index.js ファイルの表示

@@ -4,6 +4,7 @@ import { useMemo } from 'react';
// material-ui
import { useTheme } from '@mui/material/styles';
import { Box, Drawer, useMediaQuery } from '@mui/material';
import { useIntl } from 'react-intl';

// project import
import DrawerHeader from './DrawerHeader';
@@ -15,6 +16,7 @@ import { drawerWidth } from 'config';

const MainDrawer = ({ open, handleDrawerToggle, window }) => {
const theme = useTheme();
const intl = useIntl();
const matchDownMD = useMediaQuery(theme.breakpoints.down('lg'));

// responsive drawer container
@@ -25,7 +27,7 @@ const MainDrawer = ({ open, handleDrawerToggle, window }) => {
const drawerHeader = useMemo(() => <DrawerHeader open={open} />, [open]);

return (
<Box component="nav" sx={{ flexShrink: { md: 0 }, zIndex: 1300 }} aria-label="mailbox folders">
<Box component="nav" sx={{ flexShrink: { md: 0 }, zIndex: 1300 }} aria-label={intl.formatMessage({ id: 'ariaMailboxFolders' })}>
{!matchDownMD ? (
<MiniDrawerStyled variant="permanent" open={open}>
{drawerHeader}


+ 22
- 10
src/layout/MainLayout/Header/HeaderContent/LocaleSelector.js ファイルの表示

@@ -16,7 +16,7 @@ import {

import Transitions from 'components/@extended/Transitions';
import LanguageIcon from '@mui/icons-material/Language';
import {FormattedMessage} from "react-intl";
import {FormattedMessage, useIntl} from "react-intl";
import * as React from "react";
import LocaleContext from "components/I18nProvider";

@@ -27,6 +27,8 @@ const LocaleSelector = () => {
const matchesXs = useMediaQuery(theme.breakpoints.down('md'));
const { setLocale } = useContext(LocaleContext);

const intl = useIntl();

const anchorRef = useRef(null);
const [open, setOpen] = useState(false);
const handleToggle = () => {
@@ -47,16 +49,26 @@ const LocaleSelector = () => {
return (
<Box sx={{ flexShrink: 0, ml: 0.75 }}>
<IconButton
disableRipple
color="secondary"
sx={{ color: 'text.primary', bgcolor: open ? iconBackColorOpen : iconBackColor }}
aria-label="open profile"
ref={anchorRef}
aria-controls={open ? 'profile-grow' : undefined}
aria-haspopup="true"
onClick={handleToggle}
disableRipple
color="secondary"
sx={{
color: 'text.primary',
bgcolor: open ? iconBackColorOpen : iconBackColor,

/* ✅ WCAG 2.4.7 focus indicator */
'&:focus-visible': {
outline: '3px solid #0C489E',
outlineOffset: '2px',
borderRadius: '6px'
}
}}
aria-label={intl.formatMessage({id: 'openLanguage'})}
ref={anchorRef}
aria-controls={open ? 'profile-grow' : undefined}
aria-haspopup="true"
onClick={handleToggle}
>
<LanguageIcon />
<LanguageIcon />
</IconButton>
<Popper
placement={matchesXs ? 'bottom' : 'bottom-end'}


+ 20
- 13
src/layout/MainLayout/Header/HeaderContent/MobileSection.js ファイルの表示

@@ -45,19 +45,26 @@ const MobileSection = () => {
<>
<Box sx={{ flexShrink: 0, ml: 0.75 }}>
<IconButton
component="span"
disableRipple
sx={{
bgcolor: open ? 'grey.300' : 'grey.100'
}}
ref={anchorRef}
aria-controls={open ? 'menu-list-grow' : undefined}
aria-haspopup="true"
onClick={handleToggle}
color="inherit"
>
<MoreOutlined />
</IconButton>
component="span"
disableRipple
sx={{
bgcolor: open ? 'grey.300' : 'grey.100',

/* WCAG 2.4.7 – visible keyboard focus */
'&:focus-visible': {
outline: '3px solid #0C489E',
outlineOffset: '2px',
borderRadius: '6px'
}
}}
ref={anchorRef}
aria-controls={open ? 'menu-list-grow' : undefined}
aria-haspopup="true"
onClick={handleToggle}
color="inherit"
>
<MoreOutlined />
</IconButton>
</Box>
<Popper
placement="bottom-end"


+ 16
- 6
src/layout/MainLayout/Header/HeaderContent/Notification.js ファイルの表示

@@ -71,17 +71,27 @@ const Notification = () => {
<IconButton
disableRipple
color="secondary"
sx={{ color: 'text.primary', bgcolor: open ? iconBackColorOpen : iconBackColor }}
aria-label="open profile"
sx={{
color: 'text.primary',
bgcolor: open ? iconBackColorOpen : iconBackColor,

/* ✅ WCAG 2.4.7 focus indicator */
'&:focus-visible': {
outline: '3px solid #0C489E',
outlineOffset: '2px',
borderRadius: '6px'
}
}}
aria-label={intl.formatMessage({id: 'openLanguage'})}
ref={anchorRef}
aria-controls={open ? 'profile-grow' : undefined}
aria-haspopup="true"
onClick={handleToggle}
>
<Badge badgeContent={4} color="primary">
<BellOutlined />
</Badge>
</IconButton>
<Badge badgeContent={4} color="primary">
<BellOutlined />
</Badge>
</IconButton>
<Popper
placement={matchesXs ? 'bottom' : 'bottom-end'}
open={open}


+ 4
- 2
src/layout/MainLayout/Header/HeaderContent/Profile/index.js ファイルの表示

@@ -33,6 +33,7 @@ import { LogoutOutlined,
import { handleLogoutFunction } from 'auth/index';
import {useNavigate} from "react-router-dom";
import {useDispatch} from "react-redux";
import { useIntl } from 'react-intl';
import AccountCircleIcon from '@mui/icons-material/AccountCircle';

// tab panel wrapper
@@ -61,6 +62,7 @@ TabPanel.propTypes = {

const Profile = () => {
const theme = useTheme();
const intl = useIntl();
const navigate = useNavigate()
const dispatch = useDispatch()

@@ -101,7 +103,7 @@ const Profile = () => {
borderRadius: 1,
'&:hover': { bgcolor: 'secondary.lighter' }
}}
aria-label="open profile"
aria-label={intl.formatMessage({id: 'openLanguage'})}
ref={anchorRef}
aria-controls={open ? 'profile-grow' : undefined}
aria-haspopup="true"
@@ -170,7 +172,7 @@ const Profile = () => {
{/* {open && (
<>
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
<Tabs variant="fullWidth" value={value} onChange={handleChange} aria-label="profile tabs">
<Tabs variant="fullWidth" value={value} onChange={handleChange} aria-label={intl.formatMessage({ id: 'ariaProfileTabs' })}>
<Tab
sx={{
display: 'flex',


+ 824
- 745
src/layout/MainLayout/Header/index.js
ファイル差分が大きすぎるため省略します
ファイルの表示


+ 1
- 1
src/layout/MainLayout/index.js ファイルの表示

@@ -62,7 +62,7 @@ const MainLayout = () => {
<Box sx={{backgroundColor:'#ffffff', display: 'flex', width: '100%', flexDirection: "column", paddingTop: { xs: "5px", sm: "25px", md: "43px" }}}>
<Header/>
{/* <Drawer open={open} handleDrawerToggle={handleDrawerToggle} /> */}
<Box style={{ width: '100%', flexGrow: 1 } } sx={{ paddingTop: "38px" }}>
<Box style={{ width: '100%', flexGrow: 1 }} sx={{ paddingTop: "36px" }}>
{/* <Toolbar /> */}
{/* <Breadcrumbs navigation={navigation} title /> */}
<Outlet />


+ 3
- 3
src/pages/Announcement/Details/AnnouncementForm.js ファイルの表示

@@ -143,7 +143,7 @@ const AnnouncementForm = ({ loadedData }) => {
</Grid>
</Grid>

<Grid item xs={12} md={12} sx={{ mt: 2 }}><Divider fullWidth></Divider></Grid>
<Grid item xs={12} md={12} sx={{ mt: 2 }}><Divider sx={{ width: '100%' }} /></Grid>
<Grid item xs={12} md={12}><Typography variant="h5">English</Typography></Grid>
<Grid item xs={12} md={12} >
<Grid container alignItems={"center"} xs={12} sm={12} md={12} lg={12} sx={{ mb: 2 }}>
@@ -202,7 +202,7 @@ const AnnouncementForm = ({ loadedData }) => {
</Grid>
</Grid>

<Grid item xs={12} md={12} sx={{ mt: 2 }}><Divider fullWidth></Divider></Grid>
<Grid item xs={12} md={12} sx={{ mt: 2 }}><Divider sx={{ width: '100%' }} /></Grid>
<Grid item xs={12} md={12}><Typography variant="h5">Traditional Chinese</Typography></Grid>
<Grid item xs={12} md={12}>
<Grid container alignItems={"center"} xs={12} sm={12} md={12} lg={12} sx={{ mb: 2 }}>
@@ -263,7 +263,7 @@ const AnnouncementForm = ({ loadedData }) => {



<Grid item xs={12} md={12} sx={{ mt: 2 }}><Divider fullWidth></Divider></Grid>
<Grid item xs={12} md={12} sx={{ mt: 2 }}><Divider sx={{ width: '100%' }} /></Grid>
<Grid item xs={12} md={12}><Typography variant="h5">Simplified Chinese</Typography></Grid>
<Grid item xs={12} md={12}>
<Grid container alignItems={"center"} xs={12} sm={12} md={12} lg={12} sx={{ mb: 2 }}>


+ 4
- 2
src/pages/Announcement/Search_Public/SearchForm.js ファイルの表示

@@ -116,7 +116,7 @@ const SearchPublicNoticeForm = ({ applySearch, searchCriteria, onGridReady }) =>
<Grid item xs={12} s={6} md={6} lg={4} sx={{ ml: 3, mr: 3, mb: 3 }}>
<Grid container>
<Grid item xs={5.25} s={5.25} md={5.25} lg={5.5}>
<LocalizationProvider dateAdapter={AdapterDayjs}>
<LocalizationProvider dateAdapter={AdapterDayjs} localeText={DateUtils.getPickerLocaleText(intl.locale)}>
<DemoItem components={['DatePicker']}>
<DatePicker
id="dateFrom"
@@ -148,7 +148,7 @@ const SearchPublicNoticeForm = ({ applySearch, searchCriteria, onGridReady }) =>
</Grid>

<Grid item xs={5.25} s={5.25} md={5.25} lg={5.5}>
<LocalizationProvider dateAdapter={AdapterDayjs}>
<LocalizationProvider dateAdapter={AdapterDayjs} localeText={DateUtils.getPickerLocaleText(intl.locale)}>
<DemoItem components={['DatePicker']}>
<DatePicker
id="dateTo"
@@ -195,7 +195,9 @@ const SearchPublicNoticeForm = ({ applySearch, searchCriteria, onGridReady }) =>
<Grid item sx={{ ml: 3 }}>
<Button
variant="contained"
color="cancel"
onClick={resetForm}
aria-label={intl.formatMessage({ id: 'reset' })}
>
<FormattedMessage id="reset"></FormattedMessage>
</Button>


+ 3
- 2
src/pages/Announcement/Search_Public/index.js ファイルの表示

@@ -15,6 +15,7 @@ const EventTable = Loadable(React.lazy(() => import('./DataGrid')));
import titleBackgroundImg from 'assets/images/dashboard/gazette-bar.png'
import { FormattedMessage } from "react-intl";
import { getSearchCriteria } from "auth/utils";
import usePageTitle from "components/usePageTitle";

const BackgroundHead = {
backgroundImage: `url(${titleBackgroundImg})`,
@@ -29,7 +30,7 @@ const BackgroundHead = {
// ==============================|| DASHBOARD - DEFAULT ||============================== //

const UserSearchPage_Individual = () => {
usePageTitle("announcement");
const [searchCriteria, setSearchCriteria] = React.useState({});
const [onReady, setOnReady] = React.useState(false);
const [onGridReady, setGridOnReady] = React.useState(false);
@@ -74,7 +75,7 @@ const UserSearchPage_Individual = () => {
<Grid item xs={12}>
<div style={BackgroundHead}>
<Stack direction="row" height='70px' justifyContent="flex-start" alignItems="center">
<Typography ml={15} color='#FFF' variant="h4" sx={{ "textShadow": "0px 0px 25px #0C489E" }}><FormattedMessage id="announcement" /></Typography>
<Typography component="h1" ml={15} color='#FFF' variant="h4" sx={{ "textShadow": "0px 0px 25px #0C489E" }}><FormattedMessage id="announcement" /></Typography>
</Stack>
</div>
</Grid>


+ 16
- 11
src/pages/AuditLog/AuditLogSearchForm.js ファイルの表示

@@ -43,7 +43,7 @@ const AuditLogSearchForm = ({ applySearch, searchCriteria, onGridReady}) => {
const marginBottom = 2.5;

const { reset, register, handleSubmit } = useForm()
const { reset, register, handleSubmit, getValues } = useForm()

React.useEffect(() => {
setFromDateValue(minDate);
@@ -53,21 +53,26 @@ const AuditLogSearchForm = ({ applySearch, searchCriteria, onGridReady}) => {
setToDateValue(maxDate);
}, [maxDate]);

const onSubmit = (data) => {
/** Same filter fields as the list API; must reflect current form/UI state (not parent `searchCriteria`). */
const getCurrentFilterParams = () => {
let sentDateFrom = "";
let sentDateTo = "";

if (fromDateValue != "dd / mm / yyyy" && toDateValue != "dd / mm / yyyy") {
sentDateFrom = DateUtils.dateValue(fromDateValue)
sentDateTo = DateUtils.dateValue(toDateValue)
sentDateFrom = DateUtils.dateValue(fromDateValue);
sentDateTo = DateUtils.dateValue(toDateValue);
}

const temp = {
username: data.userName,
return {
username: getValues("userName"),
modifiedTo: sentDateTo,
modifiedFrom: sentDateFrom,
start:0,
limit:10
};
};

const onSubmit = () => {
const temp = {
...getCurrentFilterParams(),
start: 0,
limit: 10,
};
applySearch(temp);
};
@@ -81,7 +86,7 @@ const AuditLogSearchForm = ({ applySearch, searchCriteria, onGridReady}) => {
setOnDownload(true)
HttpUtils.fileDownload({
url: UrlUtils.AUDIT_LOG_EXPORT,
params: searchCriteria,
params: getCurrentFilterParams(),
onResponse:()=>{
setOnDownload(false)
},


+ 11
- 4
src/pages/DemandNote/Create/SearchForm.js ファイルの表示

@@ -13,7 +13,7 @@ import * as DateUtils from "utils/DateUtils";
import * as UrlUtils from "utils/ApiPathConst";
import * as HttpUtils from "utils/HttpUtils";
import { useNavigate } from "react-router-dom";
import { notifyDownloadSuccess } from 'utils/CommonFunction';
import { notifyActionError } from 'utils/CommonFunction';
import { PNSPS_BUTTON_THEME } from "../../../themes/buttonConst";
import { ThemeProvider } from "@emotion/react";
import { useIntl } from "react-intl";
@@ -118,8 +118,11 @@ const SearchPublicNoticeForm = ({ applySearch, issueComboData, _paymentCount, _p
params: {
"dnIdList": dnIdList
},
onSuccess: function () {
notifyDownloadSuccess();
onResponse: function () {
// 200: browser handles save; no success toast
},
onError: function () {
notifyActionError(intl.formatMessage({ id: 'downloadFailed' }));
}
});
}
@@ -186,6 +189,10 @@ const SearchPublicNoticeForm = ({ applySearch, issueComboData, _paymentCount, _p
}}
/>
)}
clearText={intl.formatMessage({ id: "muiClear" })}
closeText={intl.formatMessage({ id: "muiClose" })}
openText={intl.formatMessage({ id: "muiOpen" })}
noOptionsText={intl.formatMessage({ id: "muiNoOptions" })}
/>
</Grid>
<Grid item >
@@ -194,7 +201,7 @@ const SearchPublicNoticeForm = ({ applySearch, issueComboData, _paymentCount, _p
variant="contained"
onClick={onSubmit}
color="success"
minWidth={150}
sx={{ minWidth: 150 }}
>
Create
</Button>


+ 7
- 3
src/pages/DemandNote/Details/ApplicationDetailCard.js ファイルの表示

@@ -15,11 +15,12 @@ import * as StatusUtils from "utils/statusUtils/PublicNoteStatusUtils";
import * as HttpUtils from "utils/HttpUtils";

import DownloadIcon from '@mui/icons-material/Download';
import { notifyDownloadSuccess } from 'utils/CommonFunction';
import { notifyActionError } from 'utils/CommonFunction';
import { useIntl } from 'react-intl';

// ==============================|| DASHBOARD - DEFAULT ||============================== //
const ApplicationDetailCard = ({ data }) => {
const intl = useIntl();
const [appDetail, setAppDetails] = React.useState({});

React.useEffect(() => {
@@ -33,8 +34,11 @@ const ApplicationDetailCard = ({ data }) => {
fileId: appDetail.appFileId,
skey: appDetail.appSkey,
filename: appDetail.appFilename,
onResponse: function () {},
onError: function () {
notifyActionError(intl.formatMessage({ id: 'downloadFailed' }));
}
});
notifyDownloadSuccess();
};

return (


+ 6
- 4
src/pages/DemandNote/Details/DnDetailCard.js ファイルの表示

@@ -14,12 +14,13 @@ import Loadable from 'components/Loadable';
const MainCard = Loadable(React.lazy(() => import('components/MainCard')));

import DownloadIcon from '@mui/icons-material/Download';
import { notifyDownloadSuccess } from 'utils/CommonFunction';
import { notifyActionError } from 'utils/CommonFunction';
import { useIntl } from 'react-intl';


// ==============================|| DASHBOARD - DEFAULT ||============================== //
const DnDetailCard = ({ data }) => {
const intl = useIntl();
const [dnData, setDnData] = React.useState({});

React.useEffect(() => {
@@ -33,8 +34,9 @@ const DnDetailCard = ({ data }) => {
fileId: dnData.fileId,
skey: dnData.skey,
filename: dnData.filename,
onResponse: function () {
notifyDownloadSuccess();
onResponse: function () {},
onError: function () {
notifyActionError(intl.formatMessage({ id: 'downloadFailed' }));
}
});
};


+ 5
- 1
src/pages/DemandNote/Export/SearchForm.js ファイルの表示

@@ -187,6 +187,10 @@ const SearchPublicNoticeForm = ({ applySearch, issueComboData }) => {
}}
/>
)}
clearText={intl.formatMessage({ id: "muiClear" })}
closeText={intl.formatMessage({ id: "muiClose" })}
openText={intl.formatMessage({ id: "muiOpen" })}
noOptionsText={intl.formatMessage({ id: "muiNoOptions" })}
/>
</Grid>
@@ -197,7 +201,7 @@ const SearchPublicNoticeForm = ({ applySearch, issueComboData }) => {
onClick={onSubmit}
color="success"
disabled={waitDownload}
minWidth={150}
sx={{ minWidth: 150 }}
>
Export
</Button>


+ 11
- 8
src/pages/DemandNote/Search/DataGrid.js ファイルの表示

@@ -13,7 +13,7 @@ import * as FormatUtils from "utils/FormatUtils";
import * as StatusUtils from "utils/statusUtils/DnStatus";
import { useNavigate } from "react-router-dom";
import { FiDataGrid } from "components/FiDataGrid";
import { notifyDownloadSuccess } from 'utils/CommonFunction';
import { notifyActionError } from 'utils/CommonFunction';
import {
DEMAND_NOTE_EXPORT,
DEMAND_NOTE_SEND,
@@ -26,10 +26,11 @@ import * as HttpUtils from "utils/HttpUtils";
import { PNSPS_BUTTON_THEME } from "themes/buttonConst";
import { ThemeProvider } from "@emotion/react";
import { isGrantedAny } from "auth/utils";
import { useIntl } from "react-intl";
// ==============================|| EVENT TABLE ||============================== //

export default function SearchDemandNote({ applySearch, searchCriteria, applyGridOnReady }) {
const intl = useIntl();
const [isConfirmPopUp, setConfirmPopUp] = useState(false);
const [isRevokePopUp, setRevokePopUp] = useState(false);
const [isSendPopUp, setSendPopUp] = useState(false);
@@ -80,8 +81,9 @@ export default function SearchDemandNote({ applySearch, searchCriteria, applyGri
params: {
dnIdList: idList
},
onSuccess: function () {
notifyDownloadSuccess();
onResponse: function () {},
onError: function () {
notifyActionError(intl.formatMessage({ id: 'downloadFailed' }));
}
});
}
@@ -92,11 +94,12 @@ export default function SearchDemandNote({ applySearch, searchCriteria, applyGri
fileId: params.row.fileId,
skey: params.row.skey,
filename: params.row.filename,
onResponse:()=>{
setOnDownload(false)
onResponse: () => {
setOnDownload(false);
},
onError:()=>{
setOnDownload(false)
onError: () => {
setOnDownload(false);
notifyActionError(intl.formatMessage({ id: 'downloadFailed' }));
}
});
};


+ 13
- 1
src/pages/DemandNote/Search/SearchForm.js ファイルの表示

@@ -224,6 +224,10 @@ const SearchDemandNoteForm = ({ applySearch, orgComboData, searchCriteria, issue
}}
/>
)}
clearText={intl.formatMessage({ id: "muiClear" })}
closeText={intl.formatMessage({ id: "muiClose" })}
openText={intl.formatMessage({ id: "muiOpen" })}
noOptionsText={intl.formatMessage({ id: "muiNoOptions" })}
/>
</Grid>

@@ -282,6 +286,10 @@ const SearchDemandNoteForm = ({ applySearch, orgComboData, searchCriteria, issue
{params.children}
</Grid>
)}
clearText={intl.formatMessage({ id: "muiClear" })}
closeText={intl.formatMessage({ id: "muiClose" })}
openText={intl.formatMessage({ id: "muiOpen" })}
noOptionsText={intl.formatMessage({ id: "muiNoOptions" })}
/>
</Grid>
: <></>
@@ -435,7 +443,7 @@ const SearchDemandNoteForm = ({ applySearch, orgComboData, searchCriteria, issue
'& .MuiAutocomplete-endAdornment': { top: '50%', transform: 'translateY(-50%)' },
'& .MuiOutlinedInput-root': { height: 40 }
}}
getOptionLabel={(option) => option.label}
getOptionLabel={(option) => (option?.label != null ? String(option.label) : "")}
renderInput={(params) => (
<TextField
{...params}
@@ -445,6 +453,10 @@ const SearchDemandNoteForm = ({ applySearch, orgComboData, searchCriteria, issue
}}
/>
)}
clearText={intl.formatMessage({ id: "muiClear" })}
closeText={intl.formatMessage({ id: "muiClose" })}
openText={intl.formatMessage({ id: "muiOpen" })}
noOptionsText={intl.formatMessage({ id: "muiNoOptions" })}
/>
</Grid>



+ 1
- 2
src/pages/DemandNote/Search_Public/DataGrid.js ファイルの表示

@@ -66,8 +66,7 @@ export default function SearchDemandNote({ searchCriteria, applyGridOnReady,appl
width: isMdOrLg ? 'auto' : 175,
flex: isMdOrLg ? 1 : undefined,
renderCell: (params) => {

return [StatusUtils.getStatus_i18n(params, locale) ]
return StatusUtils.getStatus_i18n(params, locale);
},
},
{


+ 11
- 5
src/pages/DemandNote/Search_Public/SearchForm.js ファイルの表示

@@ -180,6 +180,10 @@ const SearchDemandNoteForm = ({ applySearch, searchCriteria, issueComboData, onG
}}
/>
)}
clearText={intl.formatMessage({ id: "muiClear" })}
closeText={intl.formatMessage({ id: "muiClose" })}
openText={intl.formatMessage({ id: "muiOpen" })}
noOptionsText={intl.formatMessage({ id: "muiNoOptions" })}
/>
</Grid>

@@ -210,7 +214,7 @@ const SearchDemandNoteForm = ({ applySearch, searchCriteria, issueComboData, onG
</Grid>

<Grid item xs={9} s={6} md={5} lg={3} sx={{ ml: 3, mr: 3, mb: 3 }}>
<LocalizationProvider dateAdapter={AdapterDayjs}>
<LocalizationProvider dateAdapter={AdapterDayjs} localeText={DateUtils.getPickerLocaleText(intl.locale)}>
<DemoItem components={['DatePicker']}>
<DatePicker
id="dateFrom"
@@ -237,7 +241,7 @@ const SearchDemandNoteForm = ({ applySearch, searchCriteria, issueComboData, onG
</Grid>

<Grid item xs={9} s={6} md={5} lg={3} sx={{ ml: 3, mr: 3, mb: 3 }}>
<LocalizationProvider dateAdapter={AdapterDayjs}>
<LocalizationProvider dateAdapter={AdapterDayjs} localeText={DateUtils.getPickerLocaleText(intl.locale)}>
<DemoItem components={['DatePicker']}>
<DatePicker
id="dateTo"
@@ -288,11 +292,13 @@ const SearchDemandNoteForm = ({ applySearch, searchCriteria, issueComboData, onG
<TextField
{...params}
label={intl.formatMessage({ id: 'status' })}
InputLabelProps={{ shrink: true }}
/>
)}
InputLabelProps={{
shrink: true
}}
clearText={intl.formatMessage({ id: "muiClear" })}
closeText={intl.formatMessage({ id: "muiClose" })}
openText={intl.formatMessage({ id: "muiOpen" })}
noOptionsText={intl.formatMessage({ id: "muiNoOptions" })}
/>
</Grid>



+ 3
- 2
src/pages/DemandNote/Search_Public/index.js ファイルの表示

@@ -18,6 +18,7 @@ const SearchForm = Loadable(React.lazy(() => import('./SearchForm')));
const EventTable = Loadable(React.lazy(() => import('./DataGrid')));
import titleBackgroundImg from 'assets/images/dashboard/gazette-bar.png'
import {FormattedMessage} from "react-intl";
import usePageTitle from "components/usePageTitle";

const BackgroundHead = {
backgroundImage: `url(${titleBackgroundImg})`,
@@ -32,7 +33,7 @@ const BackgroundHead = {
// ==============================|| DASHBOARD - DEFAULT ||============================== //

const SearchPage_DemandNote_Pub = () => {
usePageTitle("paymentInfoRecord");
const [orgCombo, setOrgCombo] = React.useState([]);
const [issueCombo, setIssueCombo] = React.useState([]);
const [searchCriteria, setSearchCriteria] = React.useState({});
@@ -101,7 +102,7 @@ const SearchPage_DemandNote_Pub = () => {
<Grid item xs={12}>
<div style={BackgroundHead}>
<Stack direction="row" height='70px' justifyContent="flex-start" alignItems="center">
<Typography ml={15} color='#FFF' variant="h4" sx={{display: { xs: 'none', sm: 'none', md: 'block' }}}>
<Typography component="h1" ml={15} color='#FFF' variant="h4" sx={{display: { xs: 'none', sm: 'none', md: 'block' }}}>
<FormattedMessage id="paymentInfoRecord" />
</Typography>
</Stack>


+ 2
- 4
src/pages/GFMIS/SearchForm.js ファイルの表示

@@ -180,7 +180,7 @@ const SearchPublicNoticeForm = ({ applySearch, generateXML, searchCriteria, onGr
filterOptions={(options) => options}
options={ComboData.payMethod}
value={payMethod}
getOptionLabel={(option) => option.label}
getOptionLabel={(option) => (option?.label != null ? String(option.label) : "")}
inputValue={payMethod?.label ? payMethod?.label : ""}
onChange={(event, newValue) => {
if(newValue==null){
@@ -197,11 +197,9 @@ const SearchPublicNoticeForm = ({ applySearch, generateXML, searchCriteria, onGr
renderInput={(params) => (
<TextField {...params}
label="Payment Method"
InputLabelProps={{ shrink: true }}
/>
)}
InputLabelProps={{
shrink: true
}}
/>
</Grid>



+ 53
- 30
src/pages/GFMIS/index.js ファイルの表示

@@ -4,6 +4,7 @@ import {
Typography,
Stack,
Button,
CircularProgress,
Dialog, DialogTitle, DialogContent, DialogActions,
} from '@mui/material';
import MainCard from "components/MainCard";
@@ -55,6 +56,8 @@ const Index = () => {
const [isPreviewLoading, setIsPreviewLoading] = React.useState(false);
const [isPopUp, setIsPopUp] = React.useState(false);
const [isXmlDialogSubmitting, setIsXmlDialogSubmitting] = React.useState(false);
const xmlDownloadInFlightRef = React.useRef(false);
const [downloadInput, setDownloadInput] = React.useState();
const [selectedIds, setSelectedIds] = React.useState([]);

@@ -66,6 +69,13 @@ const Index = () => {
setInputDateValue(inputDate);
}, [inputDate]);

React.useEffect(() => {
if (!isPopUp) {
xmlDownloadInFlightRef.current = false;
setIsXmlDialogSubmitting(false);
}
}, [isPopUp]);

React.useEffect(() => {
setOnReady(true);
}, [searchCriteria]);
@@ -94,66 +104,61 @@ const Index = () => {


function downloadXML() {
console.log(selectedIds.join(','))
setIsPopUp(false)
if (xmlDownloadInFlightRef.current) return;
xmlDownloadInFlightRef.current = true;
console.log(selectedIds.join(','));
setIsXmlDialogSubmitting(true);
let sentDateFrom = "";
if (inputDateValue != "dd / mm / yyyy") {
sentDateFrom = DateUtils.dateValue(inputDateValue)
sentDateFrom = DateUtils.dateValue(inputDateValue);
}
HttpUtils.get({
url: GEN_GFMIS_XML + "/today",
params:{
params: {
dateTo: downloadInput.dateTo,
dateFrom: downloadInput.dateFrom,
inputDate: sentDateFrom,
paymentId: selectedIds.join(',')
},
onSuccess: (responseData) => {
// console.log(responseData)
const parser = new DOMParser();
const xmlDoc = parser.parseFromString(responseData, 'application/xml');
// Get the DCBHeader element
const dcbHeader = xmlDoc.querySelector("DCBHeader");
// Get the Receipt and Allocation elements

const receiptElement = dcbHeader.querySelector("Receipt");
const allocationElement = dcbHeader.querySelector("Allocation");
const paymentMethodElements = Array.from(dcbHeader.querySelectorAll("PaymentMethod"));

// Remove existing elements from DCBHeader
dcbHeader.innerHTML = "";
dcbHeader.appendChild(receiptElement);
dcbHeader.appendChild(allocationElement);
if (paymentMethodElements) {
paymentMethodElements.forEach((paymentMethodElement) => {
dcbHeader.appendChild(paymentMethodElement);
});
dcbHeader.appendChild(paymentMethodElement);
});
}
const updatedXmlString = new XMLSerializer().serializeToString(xmlDoc);
const filename = xmlDoc.querySelector('FileHeader').getAttribute('H_Filename');
// console.log(updatedXmlString)
const blob = new Blob([updatedXmlString], { type: 'application/xml' });
// Create a download link
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = filename+'.xml';
// Append the link to the document body
link.download = filename + '.xml';

document.body.appendChild(link);
// Programmatically click the link to trigger the download
link.click();
// Clean up the link
document.body.removeChild(link);
setIsPopUp(false);
},
onFinally: () => {
xmlDownloadInFlightRef.current = false;
setIsXmlDialogSubmitting(false);
}
});
// open(UrlUtils.GEN_GFMIS_XML + "/today?online=true")
}
});
}


function applySearch(input) {
@@ -263,7 +268,11 @@ const Index = () => {
</Grid>
<Dialog
open={isPopUp}
onClose={() => setIsPopUp(false)}
onClose={() => {
if (isXmlDialogSubmitting) return;
setIsPopUp(false);
}}
disableEscapeKeyDown={isXmlDialogSubmitting}
PaperProps={{
sx: {
minWidth: '40vw',
@@ -300,8 +309,22 @@ const Index = () => {
</LocalizationProvider>
</DialogContent>
<DialogActions>
<Button onClick={() => setIsPopUp(false)}><Typography variant="h5">Cancel</Typography></Button>
<Button onClick={() => downloadXML()}><Typography variant="h5">Confirm</Typography></Button>
<Button
onClick={() => {
if (isXmlDialogSubmitting) return;
setIsPopUp(false);
}}
disabled={isXmlDialogSubmitting}
>
<Typography variant="h5">Cancel</Typography>
</Button>
<Button
onClick={() => downloadXML()}
disabled={isXmlDialogSubmitting}
startIcon={isXmlDialogSubmitting ? <CircularProgress color="inherit" size={20} /> : null}
>
<Typography variant="h5">Confirm</Typography>
</Button>
</DialogActions>
</Dialog>
</Grid>


+ 5
- 1
src/pages/GazetteIssue/ExportForm.js ファイルの表示

@@ -76,7 +76,11 @@ const SearchGazetteIssueForm = ({ applyExport, comboData, waitDownload}) => {
// defaultValue={selectedYear}
options={comboList}
// disabled={checkCountry}
getOptionLabel={(option) => option.label ? option.label : ""}
getOptionLabel={(option) =>
option != null && typeof option === "object" && option.label != null
? String(option.label)
: ""
}
onChange={(event, newValue) => {
setSelectedYear(newValue);
}}


+ 5
- 1
src/pages/GazetteIssue/SearchForm.js ファイルの表示

@@ -74,7 +74,11 @@ const SearchGazetteIssueForm = ({ applySearch, comboData, onGridReady}) => {
// defaultValue={selectedYear}
options={comboList}
// disabled={checkCountry}
getOptionLabel={(option) => option.label ? option.label : ""}
getOptionLabel={(option) =>
option != null && typeof option === "object" && option.label != null
? String(option.label)
: ""
}
onChange={(event, newValue) => {
setSelectedYear(newValue);
}}


+ 22
- 5
src/pages/GazetteIssue/index.js ファイルの表示

@@ -31,10 +31,12 @@ import { ThemeProvider } from "@emotion/react";
import { dateStr_Year } from "utils/DateUtils";
import { notifySaveSuccess } from 'utils/CommonFunction';
import { isGrantedAny } from "auth/utils";
import { useIntl } from 'react-intl';

// ==============================|| DASHBOARD - DEFAULT ||============================== //

const Index = () => {
const intl = useIntl();
const [comboData, setComboData] = React.useState([]);
const [holidayComboData, setHolidayComboData] = React.useState([]);
const [onReady, setOnReady] = React.useState(false);
@@ -50,6 +52,7 @@ const Index = () => {
const [waitDownload, setWaitDownload] = React.useState(false);
const [isWarningPopUp, setIsWarningPopUp] = React.useState(false);
const [warningText, setWarningText] = React.useState("");
const fileInputRef = React.useRef(null);

React.useEffect(() => {
setOnSearchReady(false);
@@ -184,13 +187,27 @@ const Index = () => {
<Stack direction="row" justifyContent="flex-start" alignItems="center" spacing={2} sx={{ ml: 2, mt: 1 }}>
<ThemeProvider theme={PNSPS_LONG_BUTTON_THEME}>
<Button
component="label"
variant="contained"
size="large"
disabled={waitImport}
type="button"
onClick={() => {
if (fileInputRef.current) {
fileInputRef.current.click();
}
}}
onKeyDown={(event) => {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault();
if (fileInputRef.current) {
fileInputRef.current.click();
}
}
}}
>
<Typography variant="h5">Upload Files</Typography>
<input
</Button>
<input
id="uploadFileBtn"
name="file"
type="file"
@@ -198,9 +215,9 @@ const Index = () => {
hidden
disabled={waitImport}
onChange={readFile}
aria-label="Upload Excel file (.xlsx)"
/>
</Button>
aria-label={intl.formatMessage({ id: 'ariaUploadExcelFile' })}
ref={fileInputRef}
/>
</ThemeProvider>
</Stack>
</Grid>


+ 8
- 3
src/pages/Holiday/DataGrid.js ファイルの表示

@@ -9,14 +9,19 @@ import { dateStr } from "utils/DateUtils";

// ==============================|| EVENT TABLE ||============================== //

function holidayRowsFromResponse(recordList) {
if (Array.isArray(recordList)) return recordList;
if (recordList && Array.isArray(recordList.records)) return recordList.records;
return [];
}

export default function HolidayTable({ recordList, applyGridOnReady }) {
const [rows, setRows] = React.useState(recordList);
const [rows, setRows] = React.useState(() => holidayRowsFromResponse(recordList));

// const navigate = useNavigate()

useEffect(() => {
// console.log(recordList)
setRows(recordList.records);
setRows(holidayRowsFromResponse(recordList));
}, [recordList]);

const columns = [


+ 9
- 5
src/pages/Holiday/SearchForm.js ファイルの表示

@@ -17,7 +17,7 @@ import {ThemeProvider} from "@emotion/react";


const SearchHolidayForm = ({ applySearch, comboData, onGridReady}) => {
const [selectedYear, setSelectedYear] = React.useState([]);
const [selectedYear, setSelectedYear] = React.useState(null);
// const [defaultYear, setDefaultYear] = React.useState(searchCriteria.year);
const [comboList, setComboList] = React.useState([]);
// const [onReady, setOnReady] = React.useState(false);
@@ -27,7 +27,7 @@ const SearchHolidayForm = ({ applySearch, comboData, onGridReady}) => {
handleSubmit } = useForm()

const onSubmit = () => {
if (selectedYear !=null){
if (selectedYear != null) {
const temp = {
year: selectedYear.label,
};
@@ -40,8 +40,8 @@ const SearchHolidayForm = ({ applySearch, comboData, onGridReady}) => {
// console.log(comboData)
// const labelValue = comboData.find(obj => obj.label === searchCriteria.year);
// console.log(labelValue)
if(selectedYear.length == 0){
setSelectedYear(comboData[0])
if (!selectedYear) {
setSelectedYear(comboData[0]);
}
setComboList(comboData)
// setSelectedYear(searchCriteria.dateFrom)
@@ -74,7 +74,11 @@ const SearchHolidayForm = ({ applySearch, comboData, onGridReady}) => {
// defaultValue={selectedYear}
options={comboList}
// disabled={checkCountry}
getOptionLabel={(option) => option.label ? option.label : ""}
getOptionLabel={(option) =>
option != null && typeof option === "object" && option.label != null
? String(option.label)
: ""
}
onChange={(event, newValue) => {
setSelectedYear(newValue);
}}


+ 23
- 7
src/pages/Holiday/index.js ファイルの表示

@@ -31,11 +31,12 @@ import { ThemeProvider } from "@emotion/react";
import { dateStr_Year } from "utils/DateUtils";
import { notifySaveSuccess } from 'utils/CommonFunction';
import { isGrantedAny } from "auth/utils";
import { useIntl } from 'react-intl';

// ==============================|| DASHBOARD - DEFAULT ||============================== //

const Index = () => {
const intl = useIntl();
const [record, setRecord] = React.useState([]);
const [comboData, setComboData] = React.useState([]);
const [onReady, setOnReady] = React.useState(false);
@@ -50,6 +51,7 @@ const Index = () => {
const [waitDownload, setWaitDownload] = React.useState(false);
const [isWarningPopUp, setIsWarningPopUp] = React.useState(false);
const [warningText, setWarningText] = React.useState("");
const fileInputRef = React.useRef(null);

React.useEffect(() => {
setOnSearchReady(false);
@@ -172,7 +174,7 @@ const Index = () => {
size="large"
disabled={waitDownload}
onClick={doExport}
aria-label="Export holiday template"
aria-label={intl.formatMessage({ id: 'ariaExportHolidayTemplate' })}
>
<Typography variant="h5">Export</Typography>
</Button>
@@ -180,13 +182,27 @@ const Index = () => {
{isGrantedAny(["MAINTAIN_GAZETTE_ISSUE"]) ?
<ThemeProvider theme={PNSPS_LONG_BUTTON_THEME}>
<Button
component="label"
variant="contained"
size="large"
disabled={waitImport}
type="button"
onClick={() => {
if (fileInputRef.current) {
fileInputRef.current.click();
}
}}
onKeyDown={(event) => {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault();
if (fileInputRef.current) {
fileInputRef.current.click();
}
}
}}
>
<Typography variant="h5">Upload Files</Typography>
<input
</Button>
<input
id="uploadFileBtn"
name="file"
type="file"
@@ -194,9 +210,9 @@ const Index = () => {
hidden
disabled={waitImport}
onChange={readFile}
aria-label="Upload Excel file (.xlsx)"
/>
</Button>
aria-label={intl.formatMessage({ id: 'ariaUploadExcelFile' })}
ref={fileInputRef}
/>
</ThemeProvider>
: null
}


+ 82
- 16
src/pages/JVM/index.js ファイルの表示

@@ -11,7 +11,7 @@ import {
Button
} from '@mui/material';
import * as React from "react";
import { GET_JVM_INFO } from "utils/ApiPathConst";
import { GET_JVM_INFO, GET_NOTIFICATION_QUEUE_STATUS } from "utils/ApiPathConst";
import axios from "axios";

import titleBackgroundImg from 'assets/images/dashboard/gazette-bar.png'
@@ -20,7 +20,11 @@ const JVMDefault = () => {
const [jvmInfo, setJvmInfo] = React.useState(null);
const [loading, setLoading] = React.useState(true);
const [error, setError] = React.useState(null);

const [queueStatus, setQueueStatus] = React.useState(null);
const [queueLoading, setQueueLoading] = React.useState(false);
const [queueError, setQueueError] = React.useState(null);

const fetchJvmInfo = () => {
setLoading(true);
setError(null);
@@ -37,6 +41,24 @@ const JVMDefault = () => {
});
};

const fetchNotificationQueueStatus = () => {
setQueueLoading(true);
setQueueError(null);
setQueueStatus(null);
axios.get(`${GET_NOTIFICATION_QUEUE_STATUS}`)
.then((response) => {
if (response.status === 200) {
setQueueStatus(response.data);
}
})
.catch(err => {
setQueueError(err);
})
.finally(() => {
setQueueLoading(false);
});
};

React.useEffect(() => {
localStorage.setItem('searchCriteria', "");
setLoading(false);
@@ -66,25 +88,40 @@ const JVMDefault = () => {
<div style={BackgroundHead}>
<Stack direction="row" height='70px' justifyContent="space-between" alignItems="center">
<Typography ml={15} color='#FFF' variant="h4" sx={{ "textShadow": "0px 0px 25px #0C489E" }}>
JVM Information
System Background Status
</Typography>
</Stack>
</div>
</Grid>
<Grid item xs={12} ml={15} mb={2} mt={2}>
<Button
size="large"
variant="contained"
type="submit"
sx={{
textTransform: 'capitalize',
alignItems: 'end'
}}
onClick={fetchJvmInfo}
disabled={loading}
>
<Typography variant="h5">JVM Info</Typography>
</Button>
<Stack direction="row" spacing={2}>
<Button
size="large"
variant="contained"
type="submit"
sx={{
textTransform: 'capitalize',
alignItems: 'end'
}}
onClick={fetchJvmInfo}
disabled={loading}
>
<Typography variant="h5">JVM Info</Typography>
</Button>
<Button
size="large"
variant="contained"
type="button"
sx={{
textTransform: 'capitalize',
alignItems: 'end'
}}
onClick={fetchNotificationQueueStatus}
disabled={queueLoading}
>
<Typography variant="h5">Notification Queue Status</Typography>
</Button>
</Stack>
</Grid>
<Grid item xs={12} ml={15} mb={2} mt={2}>
<Paper elevation={3} sx={{ p: 2, bgcolor: 'background.paper' }}>
@@ -114,6 +151,35 @@ const JVMDefault = () => {
)}
</Paper>
</Grid>
<Grid item xs={12} ml={15} mb={2} mt={2}>
<Paper elevation={3} sx={{ p: 2, bgcolor: 'background.paper' }}>
<Typography variant="h6" gutterBottom>Notification Queue Status</Typography>
{queueLoading ? (
<Box display="flex" justifyContent="center" alignItems="center" minHeight={120}>
<CircularProgress />
</Box>
) : queueError ? (
<Typography color="error">Error: {queueError.message}</Typography>
) : queueStatus ? (
<Box
component="pre"
sx={{
p: 2,
borderRadius: 1,
bgcolor: 'grey.100',
overflow: 'auto',
maxHeight: 300,
fontSize: '0.875rem',
lineHeight: 1.6
}}
>
{JSON.stringify(queueStatus, null, 2)}
</Box>
) : (
<Typography color="text.secondary">Click &quot;Notification Queue Status&quot; to load data.</Typography>
)}
</Paper>
</Grid>
</Grid>
);
};


+ 7
- 3
src/pages/Message/Details/index.js ファイルの表示

@@ -17,6 +17,8 @@ const LoadingComponent = Loadable(React.lazy(() => import('pages/extra-pages/Loa

import titleBackgroundImg from 'assets/images/dashboard/gazette-bar.png'
import { FormattedMessage } from "react-intl";
import usePageTitle from 'components/usePageTitle';

const BackgroundHead = {
backgroundImage: `url(${titleBackgroundImg})`,
width: '100%',
@@ -30,6 +32,8 @@ const BackgroundHead = {
// ==============================|| DASHBOARD - DEFAULT ||============================== //

const Index = () => {
usePageTitle("msgDetails");
const params = useParams();
const navigate = useNavigate()

@@ -72,7 +76,7 @@ const Index = () => {
<Grid item xs={12} width="100%">
<div style={BackgroundHead} width="100%">
<Stack direction="row" height='70px'>
<Typography ml={15} color='#FFF' variant="h4" sx={{ pt: 2 }}>
<Typography component="h1" ml={15} color='#FFF' variant="h4" sx={{ pt: 2 }}>
<FormattedMessage id="msgDetails" />
</Typography>
</Stack>
@@ -83,7 +87,7 @@ const Index = () => {
<Grid container justifyContent="flex-start" alignItems="center" >
<center>
<Grid item xs={12} md={12} sx={{p:2}} >
<Typography variant="h3" sx={{ textAlign: "left", borderBottom: "1px solid black" }}>
<Typography component="h2" variant="h3" sx={{ textAlign: "left", borderBottom: "1px solid black" }}>
{record?.subject}
</Typography>
<Typography sx={{p:1}} align="justify">{DateUtils.datetimeStr(record?.sentDate)}</Typography>
@@ -91,7 +95,7 @@ const Index = () => {
<div dangerouslySetInnerHTML={{__html: record?.content}}></div>
</Typography>

<Typography variant="h4" sx={{ ml: 8, mt: 4, mr: 8, textAlign: "center" }}>
<Typography component="h3" variant="h4" sx={{ ml: 8, mt: 4, mr: 8, textAlign: "center" }}>
<Button
component="span"
variant="contained"


+ 2
- 2
src/pages/Message/Search/SearchForm.js ファイルの表示

@@ -119,7 +119,7 @@ const SearchForm = ({ applySearch, searchCriteria, onGridReady }) => {
<Grid item xs={12} s={6} md={6} lg={4} sx={{ ml: 3, mr: 3, mb: 3 }}>
<Grid container>
<Grid item xs={5.25} s={5.25} md={5.25} lg={5.5}>
<LocalizationProvider dateAdapter={AdapterDayjs}>
<LocalizationProvider dateAdapter={AdapterDayjs} localeText={DateUtils.getPickerLocaleText(intl.locale)}>
<DemoItem components={['DatePicker']}>
<DatePicker
id="dateFrom"
@@ -151,7 +151,7 @@ const SearchForm = ({ applySearch, searchCriteria, onGridReady }) => {
</Grid>

<Grid item xs={5.25} s={5.25} md={5.25} lg={5.5}>
<LocalizationProvider dateAdapter={AdapterDayjs}>
<LocalizationProvider dateAdapter={AdapterDayjs} localeText={DateUtils.getPickerLocaleText(intl.locale)}>
<DemoItem components={['DatePicker']}>
<DatePicker
id="dateTo"


+ 3
- 2
src/pages/Message/Search/index.js ファイルの表示

@@ -17,7 +17,7 @@ const EventTable = Loadable(React.lazy(() => import('./DataGrid')));
import titleBackgroundImg from 'assets/images/dashboard/gazette-bar.png'
import {FormattedMessage} from "react-intl";
import { getSearchCriteria } from "auth/utils";
import usePageTitle from "components/usePageTitle";
const BackgroundHead = {
backgroundImage: `url(${titleBackgroundImg})`,
width: '100%',
@@ -31,6 +31,7 @@ const BackgroundHead = {
// ==============================|| DASHBOARD - DEFAULT ||============================== //

const Index = () => {
usePageTitle("systemMessage");

const [searchCriteria, setSearchCriteria] = React.useState({});
const [onReady, setOnReady] = React.useState(false);
@@ -87,7 +88,7 @@ const Index = () => {
<Grid item xs={12}>
<div style={BackgroundHead}>
<Stack direction="row" height='70px' justifyContent="flex-start" alignItems="center">
<Typography ml={15} color='#FFF' variant="h4" sx={{display: { xs: 'none', sm: 'none', md: 'block' }}}>
<Typography component="h1" ml={15} color='#FFF' variant="h4" sx={{display: { xs: 'none', sm: 'none', md: 'block' }}}>
<FormattedMessage id="systemMessage"/>
</Typography>
</Stack>


+ 57
- 51
src/pages/Organization/DetailPage/OrganizationCard.js ファイルの表示

@@ -2,7 +2,7 @@
import {
Grid, Button, Checkbox, FormControlLabel, Typography,
Dialog, DialogTitle, DialogContent, DialogActions,
FormHelperText, TextField,
FormHelperText, TextField, CircularProgress,
} from '@mui/material';
// import { FormControlLabel } from '@material-ui/core';
import MainCard from "components/MainCard";
@@ -111,56 +111,58 @@ const OrganizationCard = ({ userData, loadDataFun, id, setEditModeFun }) => {
onSubmit: (values) => {
if (values.country == null) {
setErrorMsg(intl.formatMessage({ id: 'pleaseFillInCountry' }))
} else {
if (values.country.type == "hongKong" && values.district == null) {
setErrorMsg(intl.formatMessage({ id: 'pleaseFillInDistrict' }))
} else {
let sentDateFrom = "";
if (fromDateValue == null) {
setErrorMsg(intl.formatMessage({ id: 'pleaseFillInBusinessRegCertValidityDate' }))
} else {
sentDateFrom = DateUtils.dateValue(fromDateValue)
HttpUtils.post({
url: UrlUtils.POST_ORG_SAVE_PATH,
params: {
id: id > 0 ? id : null,
enCompanyName: values.enCompanyName,
chCompanyName: values.chCompanyName,
orgShortName: values.orgShortName === "N/A" ? "" : values.orgShortName,
brNo: values.brNo,
// brExpiryDate: values.brExpiryDate,
brExpiryDate: sentDateFrom,
enCompanyNameTemp: values.enCompanyNameTemp,
chCompanyNameTemp: values.chCompanyNameTemp,
brExpiryDateTemp: values.brExpiryDateTemp,
contactPerson: values.contactPerson,
contactTel: {
countryCode: values.tel_countryCode,
phoneNumber: values.phoneNumber
},
faxNo: {
countryCode: values.fax_countryCode,
faxNumber: values.faxNumber
},
addressTemp: {
country: values.country.type,
district: values.district?.type,
addressLine1: values.addressLine1,
addressLine2: values.addressLine2,
addressLine3: values.addressLine3,
},
creditor: values.creditor,
},
onSuccess: function () {
notifySaveSuccess()
loadDataFun();
setEditMode(false);
}
});
}

}
return;
}
if (values.country.type == "hongKong" && values.district == null) {
setErrorMsg(intl.formatMessage({ id: 'pleaseFillInDistrict' }))
return;
}
if (fromDateValue == null) {
setErrorMsg(intl.formatMessage({ id: 'pleaseFillInBusinessRegCertValidityDate' }))
return;
}
const sentDateFrom = DateUtils.dateValue(fromDateValue);
return new Promise((resolve, reject) => {
HttpUtils.post({
url: UrlUtils.POST_ORG_SAVE_PATH,
params: {
id: id > 0 ? id : null,
enCompanyName: values.enCompanyName,
chCompanyName: values.chCompanyName,
orgShortName: values.orgShortName === "N/A" ? "" : values.orgShortName,
brNo: values.brNo,
brExpiryDate: sentDateFrom,
enCompanyNameTemp: values.enCompanyNameTemp,
chCompanyNameTemp: values.chCompanyNameTemp,
brExpiryDateTemp: values.brExpiryDateTemp,
contactPerson: values.contactPerson,
contactTel: {
countryCode: values.tel_countryCode,
phoneNumber: values.phoneNumber
},
faxNo: {
countryCode: values.fax_countryCode,
faxNumber: values.faxNumber
},
addressTemp: {
country: values.country.type,
district: values.district?.type,
addressLine1: values.addressLine1,
addressLine2: values.addressLine2,
addressLine3: values.addressLine3,
},
creditor: values.creditor,
},
onSuccess: function () {
notifySaveSuccess()
loadDataFun();
setEditMode(false);
resolve();
},
onFail: () => reject(),
onError: () => reject(),
});
});
}
});

@@ -267,6 +269,8 @@ const OrganizationCard = ({ userData, loadDataFun, id, setEditModeFun }) => {
variant="contained"
type="submit"
color="success"
disabled={formik.isSubmitting}
startIcon={formik.isSubmitting ? <CircularProgress color="inherit" size={18} /> : null}
>
Create
</Button>
@@ -289,6 +293,8 @@ const OrganizationCard = ({ userData, loadDataFun, id, setEditModeFun }) => {
variant="contained"
type="submit"
color="success"
disabled={formik.isSubmitting}
startIcon={formik.isSubmitting ? <CircularProgress color="inherit" size={18} /> : null}
>
Save
</Button>
@@ -440,7 +446,7 @@ const OrganizationCard = ({ userData, loadDataFun, id, setEditModeFun }) => {
value={fromDate != null ? DateUtils.dateStr(fromDate) : DateUtils.dateStr(currentFromDate)}
disabled={true}
/> :
<LocalizationProvider dateAdapter={AdapterDayjs}>
<LocalizationProvider dateAdapter={AdapterDayjs} localeText={DateUtils.getPickerLocaleText(intl.locale)}>
<DemoItem components={['DatePicker']}>
<DatePicker
id="brExpiryDate"


+ 42
- 34
src/pages/Organization/DetailPage/OrganizationPubCard.js ファイルの表示

@@ -4,7 +4,7 @@ import {
// Checkbox, FormControlLabel,
Typography,
Dialog, DialogTitle, DialogContent, DialogActions,
FormHelperText
FormHelperText, CircularProgress,
} from '@mui/material';
// import { FormControlLabel } from '@material-ui/core';
import MainCard from "components/MainCard";
@@ -61,42 +61,46 @@ const OrganizationPubCard = ({ userData, loadDataFun, id, setEditModeFun }) => {
phoneNumber: yup.string().min(8, displayErrorMsg(intl.formatMessage({ id: 'requiredValidNumber' }))).required(displayErrorMsg(intl.formatMessage({ id: 'requireContactNumber' }))),
faxNumber: yup.string().min(8, displayErrorMsg(intl.formatMessage({ id: 'require8Number' }))).nullable(),
}),
onSubmit: values => {
onSubmit: (values) => {
if (values.country == null) {
setErrorMsg(intl.formatMessage({ id: 'pleaseFillInCountry' }))
} else {
if (values.country.type == "hongKong" && values.district == null) {
setErrorMsg(intl.formatMessage({ id: 'pleaseFillInDistrict' }))
} else {
HttpUtils.post({
url: UrlUtils.POST_PUB_ORG_SAVE_PATH,
params: {
contactPerson: values.contactPerson,
contactTel: {
countryCode: values.tel_countryCode,
phoneNumber: values.phoneNumber
},
faxNo: {
countryCode: values.fax_countryCode,
faxNumber: values.faxNumber
},
addressTemp: {
country: values.country.type,
district: values.district?.type,
addressLine1: values.addressLine1,
addressLine2: values.addressLine2,
addressLine3: values.addressLine3,
},
//creditor: values.creditor,
},
onSuccess: function () {
notifySaveSuccess()
loadDataFun();
setEditMode(false);
}
});
}
return;
}
if (values.country.type == "hongKong" && values.district == null) {
setErrorMsg(intl.formatMessage({ id: 'pleaseFillInDistrict' }))
return;
}
return new Promise((resolve, reject) => {
HttpUtils.post({
url: UrlUtils.POST_PUB_ORG_SAVE_PATH,
params: {
contactPerson: values.contactPerson,
contactTel: {
countryCode: values.tel_countryCode,
phoneNumber: values.phoneNumber
},
faxNo: {
countryCode: values.fax_countryCode,
faxNumber: values.faxNumber
},
addressTemp: {
country: values.country.type,
district: values.district?.type,
addressLine1: values.addressLine1,
addressLine2: values.addressLine2,
addressLine3: values.addressLine3,
},
},
onSuccess: function () {
notifySaveSuccess()
loadDataFun();
setEditMode(false);
resolve();
},
onFail: () => reject(),
onError: () => reject(),
});
});
}
});

@@ -147,6 +151,8 @@ const OrganizationPubCard = ({ userData, loadDataFun, id, setEditModeFun }) => {
variant="contained"
type="submit"
color="success"
disabled={formik.isSubmitting}
startIcon={formik.isSubmitting ? <CircularProgress color="inherit" size={18} /> : null}
>
<FormattedMessage id="create" />
</Button>
@@ -171,6 +177,8 @@ const OrganizationPubCard = ({ userData, loadDataFun, id, setEditModeFun }) => {
variant="contained"
type="submit"
color="success"
disabled={formik.isSubmitting}
startIcon={formik.isSubmitting ? <CircularProgress color="inherit" size={18} /> : null}
>
<FormattedMessage id="save" />
</Button>


+ 6
- 2
src/pages/Organization/DetailPage/index.js ファイルの表示

@@ -22,6 +22,7 @@ import {
isORGLoggedIn,
isPrimaryLoggedIn
} from "utils/Utils";
import usePageTitle from "components/usePageTitle";

const BackgroundHead = {
backgroundImage: `url(${titleBackgroundImg})`,
@@ -42,6 +43,9 @@ import {


const OrganizationDetailPage = () => {
// Localized document title/meta for organisation details (GLD)
usePageTitle("organizationProfile");

const params = useParams();
const [formData, setFormData] = React.useState({})
const [list, setList] = React.useState([])
@@ -136,11 +140,11 @@ const OrganizationDetailPage = () => {
<div style={BackgroundHead}>
<Stack direction="row" height='70px' justifyContent="flex-start" alignItems="center">
{isGLDLoggedIn()?
<Typography ml={15} color='#FFF' variant="h4" sx={{display: { xs: 'none', sm: 'none', md: 'block' }}}>
<Typography component="h1" ml={15} color='#FFF' variant="h4" sx={{display: { xs: 'none', sm: 'none', md: 'block' }}}>
Maintain Organisation
</Typography>
:
<Typography ml={15} color='#FFF' variant="h4" sx={{display: { xs: 'none', sm: 'none', md: 'block' }}}>
<Typography component="h1" ml={15} color='#FFF' variant="h4" sx={{display: { xs: 'none', sm: 'none', md: 'block' }}}>
<FormattedMessage id="organizationProfile" />
</Typography>
}


+ 1
- 1
src/pages/Organization/DetailPage_FromUser/OrganizationCard_loadFromUser.js ファイルの表示

@@ -233,7 +233,7 @@ const OrganizationCard_loadFromUser = ({ userData, userId }) => {
<Typography variant="pnspsFormParagraphBold">{FieldUtils.notNullFieldLabel("Expiry Date:")}</Typography>
</Grid>
<Grid item xs={12} md={6} lg={6}>
<LocalizationProvider dateAdapter={AdapterDayjs}>
<LocalizationProvider dateAdapter={AdapterDayjs} localeText={DateUtils.getPickerLocaleText(intl.locale)}>
<DemoItem components={['DatePicker']}>
<DatePicker
id="brExpiryDate"


+ 4
- 0
src/pages/Organization/DetailPage_FromUser/index.js ファイルの表示

@@ -25,10 +25,14 @@ const BackgroundHead = {
backgroundColor: '#0C489E',
backgroundPosition: 'right'
}
import usePageTitle from "components/usePageTitle";
// ==============================|| DASHBOARD - DEFAULT ||============================== //


const OrganizationDetailPage_FromUser = () => {
// Localized document title/meta for organisation details (from user)
usePageTitle("organizationProfile");

const params = useParams();
const [formData, setFormData] = useState({})
const [isLoading, setLoding] = useState(true);


+ 1
- 1
src/pages/Organization/SearchPage/OrganizationSearchForm.js ファイルの表示

@@ -154,7 +154,7 @@ const OrganizationSearchForm = ({ applySearch, onGridReady, searchCriteria }) =>
'& .MuiAutocomplete-endAdornment': { top: '50%', transform: 'translateY(-50%)' },
'& .MuiOutlinedInput-root': { height: 40 }
}}
getOptionLabel={(option) => option.label}
getOptionLabel={(option) => (option?.label != null ? String(option.label) : "")}
renderInput={(params) => (
<TextField
{...params}


+ 3
- 0
src/pages/Organization/SearchPage/index.js ファイルの表示

@@ -6,6 +6,7 @@ import MainCard from "components/MainCard";
import { useEffect, useState } from "react";
import * as React from "react";
import { getSearchCriteria } from "auth/utils";
import usePageTitle from "components/usePageTitle";

// import LoadingComponent from "../extra-pages/LoadingComponent";
// import SearchForm from "./OrganizationSearchForm";
@@ -29,6 +30,8 @@ const BackgroundHead = {
// ==============================|| DASHBOARD - DEFAULT ||============================== //

const OrganizationSearchPage = () => {
// Localized document title/meta for organisation search
usePageTitle("organizationProfile");

const [searchCriteria, setSearchCriteria] = useState({});
const [onReady, setOnReady] = useState(false);


+ 5
- 2
src/pages/Payment/Details_Public/index.js ファイルの表示

@@ -20,6 +20,7 @@ const DataGrid = Loadable(React.lazy(() => import('./DataGrid')));
import ForwardIcon from '@mui/icons-material/Forward';
import titleBackgroundImg from 'assets/images/dashboard/gazette-bar.png'
import {FormattedMessage,useIntl} from "react-intl";
import usePageTitle from "components/usePageTitle";
const BackgroundHead = {
backgroundImage: `url(${titleBackgroundImg})`,
width: '100%',
@@ -33,6 +34,8 @@ const BackgroundHead = {
// ==============================|| DASHBOARD - DEFAULT ||============================== //

const Index = () => {
usePageTitle("payDetail");
const params = useParams();
const navigate = useNavigate()
const intl = useIntl();
@@ -144,7 +147,7 @@ const Index = () => {
<Grid className="printHidden" item xs={12} width="100%">
<div style={BackgroundHead} width="100%">
<Stack direction="row" height='70px'>
<Typography ml={15} color='#FFF' variant="h4" sx={{ pt: 2 }}>
<Typography component="h1" ml={15} color='#FFF' variant="h4" sx={{ pt: 2 }}>
<FormattedMessage id="payDetail"/>
</Typography>
</Stack>
@@ -160,7 +163,7 @@ const Index = () => {
</Button>
</Grid>
{/*row 1*/}
<Grid item xs={12} md={12} spacing={2} sx={{ textAlign: "center" }}>
<Grid item xs={12} md={12} sx={{ textAlign: "center" }}>
<Grid container justifyContent="center" direction="column" spacing={2} sx={{ p: 2 }} alignitems="stretch" >
<Grid item className="printOrder" xs={12} md={12} sx={{ pt: 2 }} style={{ height: '100%', order: 1 }}>
<Box xs={12} md={12} sx={{ border: '3px solid #eee', borderRadius: '10px' }} >


+ 4
- 8
src/pages/Payment/Search_GLD/SearchForm.js ファイルの表示

@@ -222,7 +222,7 @@ const SearchPublicNoticeForm = ({ applySearch, searchCriteria, onGridReady }) =>
filterOptions={(options) => options}
options={ComboData.paymentStatus}
value={status}
getOptionLabel={(option) => option.label}
getOptionLabel={(option) => (option?.label != null ? String(option.label) : "")}
inputValue={status?.label ? status?.label : ""}
onChange={(event, newValue) => {
if(newValue==null){
@@ -239,11 +239,9 @@ const SearchPublicNoticeForm = ({ applySearch, searchCriteria, onGridReady }) =>
renderInput={(params) => (
<TextField {...params}
label="Status"
InputLabelProps={{ shrink: true }}
/>
)}
InputLabelProps={{
shrink: true
}}
/>
</Grid>
@@ -256,7 +254,7 @@ const SearchPublicNoticeForm = ({ applySearch, searchCriteria, onGridReady }) =>
filterOptions={(options) => options}
options={ComboData.payMethod}
value={payMethod}
getOptionLabel={(option) => option.label}
getOptionLabel={(option) => (option?.label != null ? String(option.label) : "")}
inputValue={payMethod?.label ? payMethod?.label : ""}
onChange={(event, newValue) => {
if(newValue==null){
@@ -273,11 +271,9 @@ const SearchPublicNoticeForm = ({ applySearch, searchCriteria, onGridReady }) =>
renderInput={(params) => (
<TextField {...params}
label="Payment Method"
InputLabelProps={{ shrink: true }}
/>
)}
InputLabelProps={{
shrink: true
}}
/>
</Grid>
</Grid>


+ 9
- 6
src/pages/Payment/Search_Public/SearchForm.js ファイルの表示

@@ -124,7 +124,7 @@ const SearchPublicNoticeForm = ({ applySearch, searchCriteria, onGridReady }) =>
<Grid item xs={12} s={6} md={5} lg={3} sx={{ ml: 3, mr: 3, mb: 3 }}>
<Grid container spacing={1}>
<Grid item xs={6}>
<LocalizationProvider dateAdapter={AdapterDayjs}>
<LocalizationProvider dateAdapter={AdapterDayjs} localeText={DateUtils.getPickerLocaleText(intl.locale)}>
<DemoItem components={['DatePicker']}>
<DatePicker
id="dateFrom"
@@ -152,7 +152,7 @@ const SearchPublicNoticeForm = ({ applySearch, searchCriteria, onGridReady }) =>
</Grid>

<Grid item xs={6}>
<LocalizationProvider dateAdapter={AdapterDayjs}>
<LocalizationProvider dateAdapter={AdapterDayjs} localeText={DateUtils.getPickerLocaleText(intl.locale)}>
<DemoItem components={['DatePicker']}>
<DatePicker
id="dateTo"
@@ -206,13 +206,16 @@ const SearchPublicNoticeForm = ({ applySearch, searchCriteria, onGridReady }) =>
'& .MuiOutlinedInput-root': { height: 40 }
}}
renderInput={(params) => (
<TextField {...params}
<TextField
{...params}
label={intl.formatMessage({id: 'status'})}
InputLabelProps={{ ...params.InputLabelProps, shrink: true }}
/>
)}
InputLabelProps={{
shrink: true
}}
clearText={intl.formatMessage({ id: "muiClear" })}
closeText={intl.formatMessage({ id: "muiClose" })}
openText={intl.formatMessage({ id: "muiOpen" })}
noOptionsText={intl.formatMessage({ id: "muiNoOptions" })}
/>
</Grid>



+ 3
- 1
src/pages/Payment/Search_Public/index.js ファイルの表示

@@ -15,6 +15,7 @@ const EventTable = Loadable(React.lazy(() => import('./DataGrid')));
import titleBackgroundImg from 'assets/images/dashboard/gazette-bar.png'
import {FormattedMessage} from "react-intl";
import { getSearchCriteria } from "auth/utils";
import usePageTitle from "components/usePageTitle";

const BackgroundHead = {
backgroundImage: `url(${titleBackgroundImg})`,
@@ -29,6 +30,7 @@ const BackgroundHead = {
// ==============================|| DASHBOARD - DEFAULT ||============================== //

const Index = () => {
usePageTitle("onlinePaymentHistory");
const [searchCriteria, setSearchCriteria] = React.useState({
dateTo: DateUtils.dateValue(new Date()),
dateFrom: DateUtils.dateValue(new Date().setDate(new Date().getDate()-14)),
@@ -76,7 +78,7 @@ const Index = () => {
<Grid item xs={12}>
<div style={BackgroundHead}>
<Stack direction="row" height='70px' justifyContent="flex-start" alignItems="center">
<Typography ml={15} color='#FFF' variant="h4" sx={{display: { xs: 'none', sm: 'none', md: 'block' }}}>
<Typography component="h1" ml={15} color='#FFF' variant="h4" sx={{display: { xs: 'none', sm: 'none', md: 'block' }}}>
<FormattedMessage id="onlinePaymentHistory"/>
</Typography>
</Stack>


+ 49
- 12
src/pages/Proof/Create_FromApp/ProofForm.js ファイルの表示

@@ -19,12 +19,23 @@ import * as ComboData from "utils/ComboData";
import * as React from "react";
import { useFormik } from 'formik';
import { useNavigate } from "react-router-dom";
import { useIntl } from 'react-intl';
import Loadable from 'components/Loadable';
import { notifySaveSuccess } from 'utils/CommonFunction';
const UploadFileTable = Loadable(React.lazy(() => import('./UploadFileTable')));

const FormPanel = ({ formData }) => {
/** Keeps Formik fields defined so inputs stay controlled before API data loads. */
const proofFormInitialValues = {
reviseDeadline: '',
proofPaymentDeadline: '',
length: 0,
noOfPages: 0,
fee: 0,
groupType: ''
};

const FormPanel = ({ formData }) => {
const intl = useIntl();
const [data, setData] = React.useState({});
const [columnPrice, setColumnPrice] = React.useState(ComboData.proofPrice[0]);
const [attachments, setAttachments] = React.useState([]);
@@ -41,16 +52,24 @@ const FormPanel = ({ formData }) => {

const [proofPaymentDeadlineMin, setProofPaymentDeadlineMin] = React.useState({});
const [reviseDeadlineMin, setReviseDeadlineMin] = React.useState({});
const fileInputRef = React.useRef(null);

const navigate = useNavigate()

React.useEffect(() => {
if (formData) {
setData(formData);
const normalizedFormData = {
...formData,
length: formData.length ?? 0,
noOfPages: formData.noOfPages ?? 0,
fee: formData.fee ?? 0
};

if (formData.groupType == "Private Bill") {
setColumnPrice(ComboData.proofPrice[1])
formData['length'] = 18;
normalizedFormData['length'] = 18;
}
setData(normalizedFormData);
setProofPaymentDeadlineMin(formData.proofPaymentDeadline);
setReviseDeadlineMin(formData.reviseDeadline);
setExpectedCode(formData.groupNo.substr(1,formData.groupNo.length)+"-"+formData.issueNo+"-"+formData.issueYear.toString().substr(2, formData.issueYear.toString().length));
@@ -121,7 +140,7 @@ const FormPanel = ({ formData }) => {

const formik = useFormik({
enableReinitialize: true,
initialValues: data,
initialValues: { ...proofFormInitialValues, ...data },
onSubmit: values => {
setSaving(true);
if (!attachments || attachments.length <= 0) {
@@ -167,7 +186,9 @@ const FormPanel = ({ formData }) => {
if (msg === "haveActiveProof") {
msg = "Action Failed: There is already a pending payment and proofreading record for client review."
} else if (msg === "haveProofed") {
msg = "Action Failed: Already proofed."
msg = "Action Failed: An active proof is already created for this application."
} else {
msg = intl.formatMessage({ id: msg });
}
setWarningText(msg);
setIsWarningPopUp(true);
@@ -203,7 +224,7 @@ const FormPanel = ({ formData }) => {
}

return (
<MainCard xs={12} md={12} lg={12}
<MainCard
border={false}
content={false}>

@@ -275,12 +296,26 @@ const FormPanel = ({ formData }) => {
</Grid>
<Grid item xs={12} md={12}>
<Button
component="label"
variant="contained"
size="large"
disabled={attachments.length >= (formik.values.groupType == "Private Bill" ? 2 : 1)}
type="button"
onClick={() => {
if (fileInputRef.current) {
fileInputRef.current.click();
}
}}
onKeyDown={(event) => {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault();
if (fileInputRef.current) {
fileInputRef.current.click();
}
}
}}
>
<Typography variant="h5">Upload Files</Typography>
<Typography variant="h5">Upload Files</Typography>
</Button>
<input
id="uploadFileBtn"
name="file"
@@ -289,9 +324,9 @@ const FormPanel = ({ formData }) => {
hidden
disabled={attachments.length >= (formik.values.groupType == "Private Bill" ? 2 : 1)}
onChange={readFile}
aria-label="Upload PDF file"
aria-label={intl.formatMessage({ id: 'ariaUploadPdfFile' })}
ref={fileInputRef}
/>
</Button>
</Grid>
<Grid item xs={12} md={12}>
<UploadFileTable
@@ -407,7 +442,9 @@ const FormPanel = ({ formData }) => {
options={ComboData.proofPrice}
value={columnPrice}
inputValue={(columnPrice?.label) ? columnPrice?.label : ""}
getOptionLabel={(option) => option.label ? option.label : ""}
getOptionLabel={(option) =>
option != null && option.label != null ? String(option.label) : ""
}
onChange={(event, newValue) => {
setColumnPrice(newValue)
formik.values["fee"] = newValue.value * formik.values.length;
@@ -494,7 +531,7 @@ const FormPanel = ({ formData }) => {
}
}}
>
<DialogTitle><Typography variant="h3">Warning</Typography></DialogTitle>
<DialogTitle><Typography component="span" variant="h3">Warning</Typography></DialogTitle>
<DialogContent style={{ display: 'flex', }}>
<Typography variant="h4" style={{ padding: '16px' }}>{warningText}</Typography>
</DialogContent>


+ 3
- 3
src/pages/Proof/Create_FromApp/index.js ファイルの表示

@@ -75,7 +75,7 @@ const Index = () => {
</Grid>
</Grid>
:
<Grid container sx={{ minHeight: '85vh', backgroundColor: "backgroundColor.default" }} direction="column" spacing={1} >
<Grid container sx={{ minHeight: '85vh', bgcolor: 'background.default' }} direction="column" spacing={1} >
<Grid item xs={12}>
<div style={BackgroundHead}>
<Stack direction="row" height='70px' justifyContent="flex-start" alignItems="center">
@@ -88,7 +88,7 @@ const Index = () => {
border={false}
content={false}
sx={{
backgroundColor: "backgroundColor.default"
bgcolor: 'background.default'
}}
>

@@ -126,7 +126,7 @@ const Index = () => {
<MainCard elevation={0}
border={false}
content={false}
backgroundColor={"backgroundColor.default"}
sx={{ bgcolor: 'background.default' }}
>
<Box xs={12} ml={4} mt={3} sx={{ p: 1, borderRadius: '10px', backgroundColor: "#fff" }}>
<ProofForm


+ 52
- 8
src/pages/Proof/Reply_GLD/ApplicationDetails.js ファイルの表示

@@ -8,11 +8,13 @@ import {
Button,
Stack,
Dialog, DialogTitle, DialogContent, DialogActions,
CircularProgress,
} from '@mui/material';

import { useFormik } from 'formik';
import { useIntl } from 'react-intl';
import {isGranted} from "auth/utils";
import {useState,useEffect,lazy} from "react";
import {useState, useEffect, useRef, lazy} from "react";
import * as HttpUtils from "utils/HttpUtils"
import * as UrlUtils from "utils/ApiPathConst"
import * as DateUtils from "utils/DateUtils"
@@ -31,10 +33,15 @@ const ApplicationDetailCard = ({
}) => {

const params = useParams();
const intl = useIntl();

const [data, setData] = useState({});
const [cancelPopUp, setCancelPopUp] = useState(false);
const [cancelLoading, setCancelLoading] = useState(false);
const cancellingRef = useRef(false);
const [onDownload, setOnDownload] = useState(false);
const [alertMsg, setAlertMsg] = useState('');
const [showAlert, setShowAlert] = useState(false);

useEffect(() => {
if (formData) {
@@ -94,11 +101,30 @@ const ApplicationDetailCard = ({
}

const confirmCancel = () => {
setCancelPopUp(false);
if (cancellingRef.current) return;
cancellingRef.current = true;
setCancelLoading(true);
HttpUtils.get({
url: UrlUtils.CANCEL_PROOF + "/" + params.id,
onSuccess: function () {
window.location.reload(false);
onSuccess: function (responseData) {
cancellingRef.current = false;
setCancelLoading(false);
if (responseData && responseData.success === false) {
setCancelPopUp(false);
const msg = responseData.msg ? intl.formatMessage({ id: responseData.msg }) : intl.formatMessage({ id: 'proofAlreadyCancelled' });
setAlertMsg(msg);
setShowAlert(true);
} else {
window.location.reload(false);
}
},
onFail: () => {
cancellingRef.current = false;
setCancelLoading(false);
},
onError: () => {
cancellingRef.current = false;
setCancelLoading(false);
}
});
}
@@ -189,7 +215,7 @@ const ApplicationDetailCard = ({

<Grid item xs={12} md={9} lg={9} sx={{ display: 'flex', alignItems: 'center' }}>
<FormControl variant="outlined">
{StatusUtils.getStatusByText(data.appStatus)}
{StatusUtils.getStatusByTextEng(data.appStatus, data.creditor)}
</FormControl>
</Grid>
</Grid>
@@ -338,15 +364,33 @@ const ApplicationDetailCard = ({
<div>
<Dialog
open={cancelPopUp}
onClose={() => setCancelPopUp(false)}
onClose={() => {
if (cancelLoading) return;
setCancelPopUp(false);
}}
>
<DialogTitle><Typography variant="h3">Confirm</Typography></DialogTitle>
<DialogContent style={{ display: 'flex', }}>
<Typography variant="h4" style={{ padding: '16px' }}>Are you sure you want to cancel this proof?</Typography>
</DialogContent>
<DialogActions>
<Button onClick={() => setCancelPopUp(false)}><Typography variant="h5">Cancel</Typography></Button>
<Button onClick={() => confirmCancel()}><Typography variant="h5">Confirm</Typography></Button>
<Button onClick={() => setCancelPopUp(false)} disabled={cancelLoading}><Typography variant="h5">Cancel</Typography></Button>
<Button
onClick={() => confirmCancel()}
disabled={cancelLoading}
startIcon={cancelLoading ? <CircularProgress color="inherit" size={20} /> : null}
>
<Typography variant="h5">Confirm</Typography>
</Button>
</DialogActions>
</Dialog>
<Dialog open={showAlert} onClose={() => setShowAlert(false)}>
<DialogTitle><Typography variant="h3">{intl.formatMessage({ id: 'attention' })}</Typography></DialogTitle>
<DialogContent>
<Typography variant="h4" style={{ padding: '16px' }}>{alertMsg}</Typography>
</DialogContent>
<DialogActions>
<Button onClick={() => setShowAlert(false)}><Typography variant="h5">OK</Typography></Button>
</DialogActions>
</Dialog>
</div>


+ 36
- 18
src/pages/Proof/Reply_Public/ProofForm.js ファイルの表示

@@ -55,6 +55,7 @@ const FormPanel = ({ formData }) => {
const [isSubmitting, setIsSubmitting] = React.useState(false);
const [isOnlyOnlinePayment, setOnlyOnlinePayment] = React.useState();
const [isNoPayment, setNoPayment] = React.useState();
const fileInputRef = React.useRef(null);
const navigate = useNavigate()
const params = useParams();
@@ -166,14 +167,16 @@ const FormPanel = ({ formData }) => {
onFail: function (response) {
setIsSubmitting(false);
setWarningTitle(intl.formatMessage({ id: "attention" }))
setWarningText(intl.formatMessage({ id: 'actionFail' }));
const msg = response?.data?.msg ? intl.formatMessage({ id: response.data.msg }) : intl.formatMessage({ id: 'actionFail' });
setWarningText(msg);
setIsWarningPopUp(true);
console.log(response);
},
onError: function (error) {
setIsSubmitting(false);
setWarningTitle(intl.formatMessage({ id: "attention" }))
setWarningText(intl.formatMessage({ id: 'actionFail' }));
const msg = error?.response?.data?.msg ? intl.formatMessage({ id: error.response.data.msg }) : intl.formatMessage({ id: 'actionFail' });
setWarningText(msg);
setIsWarningPopUp(true);
console.log(error);
}
@@ -628,6 +631,30 @@ const FormPanel = ({ formData }) => {
</Grid>

<Grid item xs={12} md={12} textAlign="left">
<ThemeProvider theme={PNSPS_BUTTON_THEME}>
<Button
color="save"
variant="contained"
type="button"
aria-label={intl.formatMessage({ id: 'upload' })}
disabled={attachments.length >= (formik.values.groupType === "Private Bill" ? 2 : 1)}
onClick={() => {
if (fileInputRef.current) {
fileInputRef.current.click();
}
}}
onKeyDown={(event) => {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault();
if (fileInputRef.current) {
fileInputRef.current.click();
}
}
}}
>
<FormattedMessage id="upload" />
</Button>
</ThemeProvider>
<input
id="uploadFileBtn"
name="file"
@@ -638,20 +665,8 @@ const FormPanel = ({ formData }) => {
onChange={(event) => {
readFile(event)
}}
ref={fileInputRef}
/>
<label htmlFor="uploadFileBtn">
<ThemeProvider theme={PNSPS_BUTTON_THEME}>
<Button
color="save"
component="span"
variant="contained"
aria-label={intl.formatMessage({ id: 'upload' })}
disabled={attachments.length >= (formik.values.groupType === "Private Bill" ? 2 : 1)}
>
<FormattedMessage id="upload" />
</Button>
</ThemeProvider>
</label>
</Grid>


@@ -700,13 +715,16 @@ const FormPanel = ({ formData }) => {
<ThemeProvider theme={PNSPS_BUTTON_THEME}>
<Button
variant="contained"
color="success"
type="submit"
disabled={(actionValue == false && isOverReviseDeadline()) || isSubmitting}
startIcon={isSubmitting ? <CircularProgress size={20} color="inherit" /> : null}
aria-label={intl.formatMessage({ id: 'submitReply' })}
sx={{
backgroundColor: '#0C489E',
color: '#FFFFFF',
'&:hover': { backgroundColor: '#093A7A' },
}}
>
<FormattedMessage id="submitReply" />
{isSubmitting ? <FormattedMessage id="loading" /> : <FormattedMessage id="submitReply" />}
</Button>
</ThemeProvider>
</Grid>


+ 7
- 9
src/pages/Proof/Reply_Public/index.js ファイルの表示

@@ -21,6 +21,7 @@ const ProofForm = Loadable(React.lazy(() => import('./ProofForm')));
import titleBackgroundImg from 'assets/images/dashboard/gazette-bar.png'
import MainCard from "../../../components/MainCard";
import {FormattedMessage} from "react-intl";
import usePageTitle from "components/usePageTitle";
const BackgroundHead = {
backgroundImage: `url(${titleBackgroundImg})`,
width: '100%',
@@ -35,6 +36,8 @@ import {useIntl} from "react-intl";
// ==============================|| DASHBOARD - DEFAULT ||============================== //

const Index = () => {
usePageTitle("proofRecord");
const params = useParams();
const navigate = useNavigate()
const intl = useIntl();
@@ -92,7 +95,7 @@ const Index = () => {
<Grid item xs={12} width="100%">
<div style={BackgroundHead} width="100%">
<Stack direction="row" height='70px'>
<Typography ml={15} color='#FFF' variant="h4" sx={{display: { xs: 'none', sm: 'none', md: 'block' }, pt:2}}>
<Typography component="h1" ml={15} color='#FFF' variant="h4" sx={{display: { xs: 'none', sm: 'none', md: 'block' }, pt:2}}>
<FormattedMessage id="proofRecord"/>
</Typography>
</Stack>
@@ -111,12 +114,10 @@ const Index = () => {
<Grid item xs={12} sm={12} md={12} lg={12} sx={{ width:'100%', mt:2, mb: -3}}>
<MainCard
sx={{
mr:2,
mr: 2,
boxShadow: 1,
border: 1,
borderColor: '#DDD',
border: '1px groove #DDD',
}}
border= '1px groove grey'
>
<ApplicationDetails
formData={record}
@@ -129,11 +130,8 @@ const Index = () => {
<MainCard
sx={{
boxShadow: 1,
border: 1,
borderColor: '#DDD',
border: '1px groove #DDD',
}}
border= '1px groove grey'
// sx={..._sx}
>
<ProofForm
formData={record}


+ 5
- 1
src/pages/Proof/Search_GLD/DataGrid.js ファイルの表示

@@ -46,10 +46,14 @@ export default function SearchPublicNoticeTable({searchCriteria, applyGridOnRead
},
},
{
id: 'actions',
id: 'proofStatus',
field: 'proofStatus',
headerName: 'Status',
flex: 1,
minWidth: 150,
sortable: false,
filterable: false,
valueGetter: () => '',
renderCell: (params) => {
return ProofStatus.getStatus_Eng(params);
},


+ 15
- 16
src/pages/Proof/Search_GLD/SearchForm.js ファイルの表示

@@ -26,11 +26,15 @@ const SearchPublicNoticeForm = ({ applySearch, orgComboData, searchCriteria, iss

const [type, setType] = React.useState([]);
const [status, setStatus] = React.useState(searchCriteria.statusKey!=undefined?ComboData.proofStatus_GLD[searchCriteria.statusKey]:ComboData.proofStatus_GLD[0]);
const [orgSelected, setOrgSelected] = React.useState({});
const [orgSelected, setOrgSelected] = React.useState(null);
const [orgCombo, setOrgCombo] = React.useState();
const [issueSelected, setIssueSelected] = React.useState({});
const [issueSelected, setIssueSelected] = React.useState(null);
const [issueCombo, setIssueCombo] = React.useState([]);
const [groupSelected, setGroupSelected] = React.useState(searchCriteria.gazettGroup!=undefined?ComboData.groupTitle.find(item => item.code === searchCriteria.gazettGroup):{});
const [groupSelected, setGroupSelected] = React.useState(
searchCriteria.gazettGroup != undefined
? ComboData.groupTitle.find(item => item.code === searchCriteria.gazettGroup) ?? null
: null
);

const [minDate, setMinDate] = React.useState(searchCriteria.dateFrom);
const [maxDate, setMaxDate] = React.useState(searchCriteria.dateTo);
@@ -136,9 +140,9 @@ const SearchPublicNoticeForm = ({ applySearch, orgComboData, searchCriteria, iss
function resetForm() {
setType([]);
setStatus(ComboData.proofStatus[0]);
setOrgSelected({});
setIssueSelected({});
setGroupSelected({});
setOrgSelected(null);
setIssueSelected(null);
setGroupSelected(null);
setMinDate(DateUtils.dateValue(new Date().setDate(new Date().getDate()-14)))
setMaxDate(DateUtils.dateValue(new Date()))
reset({
@@ -257,7 +261,7 @@ const SearchPublicNoticeForm = ({ applySearch, orgComboData, searchCriteria, iss
options={ComboData.groupTitle}
value={groupSelected}
inputValue={(groupSelected?.label) ? groupSelected?.label : ""}
getOptionLabel={(option) => option.label}
getOptionLabel={(option) => (option?.label != null ? String(option.label) : "")}
onChange={(event, newValue) => {
setGroupSelected(newValue);
}}
@@ -371,13 +375,12 @@ const SearchPublicNoticeForm = ({ applySearch, orgComboData, searchCriteria, iss
'& .MuiOutlinedInput-root': { height: 40 }
}}
renderInput={(params) => (
<TextField {...params}
<TextField
{...params}
label="Status"
InputLabelProps={{ shrink: true }}
/>
)}
InputLabelProps={{
shrink: true
}}
/>
</Grid>

@@ -396,11 +399,7 @@ const SearchPublicNoticeForm = ({ applySearch, orgComboData, searchCriteria, iss
inputValue={orgSelected ? orgSelected.name!=undefined?orgSelected.name:"" : ""}

onChange={(event, newValue) => {
if (newValue !== null) {
setOrgSelected(newValue);
}else{
setOrgSelected({});
}
setOrgSelected(newValue ?? null);
}}
sx={{
'& .MuiInputBase-root': { alignItems: 'center' },


+ 12
- 1
src/pages/Proof/Search_Public/DataGrid.js ファイルの表示

@@ -100,6 +100,10 @@ export default function SearchPublicNoticeTable({ searchCriteria, applyGridOnRea
}


const renderHeaderWithAria = (params) => (
<span aria-label={params.colDef.headerName}>{params.colDef.headerName}</span>
);

const columns = [
{
field: 'actions',
@@ -107,6 +111,7 @@ export default function SearchPublicNoticeTable({ searchCriteria, applyGridOnRea
width: isMdOrLg ? 'auto' : 200,
flex: isMdOrLg ? 1.5 : undefined,
cellClassName: 'actions',
renderHeader: renderHeaderWithAria,
renderCell: (params) => {
return clickableLink('/proof/reply/' + params.row.id, params.row.refNo);
},
@@ -117,6 +122,7 @@ export default function SearchPublicNoticeTable({ searchCriteria, applyGridOnRea
headerName: isORGLoggedIn() ? intl.formatMessage({ id: 'gazetteCount3' }) : intl.formatMessage({ id: 'gazetteCount2' }),
width: isMdOrLg ? 'auto' : 330,
flex: isMdOrLg ? 2 : undefined,
renderHeader: renderHeaderWithAria,
renderCell: (params) => {
// let appNo = params.row.appNo;
// let code = params.row.groupNo;
@@ -132,6 +138,7 @@ export default function SearchPublicNoticeTable({ searchCriteria, applyGridOnRea
headerName: intl.formatMessage({ id: 'proofDate' }),
width: isMdOrLg ? 'auto' : 200,
flex: isMdOrLg ? 1.5 : undefined,
renderHeader: renderHeaderWithAria,
valueGetter: (params) => {
return DateUtils.datetimeStr(params?.value);
}
@@ -142,6 +149,7 @@ export default function SearchPublicNoticeTable({ searchCriteria, applyGridOnRea
headerName: intl.formatMessage({ id: 'replyBefore' }),
width: isMdOrLg ? 'auto' : 200,
flex: isMdOrLg ? 1.5 : undefined,
renderHeader: renderHeaderWithAria,
valueGetter: (params) => {
const proofPaymentDeadline = DateUtils.convertToDate(params?.value);
return DateUtils.datetimeStr(
@@ -156,15 +164,17 @@ export default function SearchPublicNoticeTable({ searchCriteria, applyGridOnRea
headerName: intl.formatMessage({ id: 'replyDate' }),
width: isMdOrLg ? 'auto' : 200,
flex: isMdOrLg ? 1.5 : undefined,
renderHeader: renderHeaderWithAria,
valueGetter: (params) => {
return params?.value ? DateUtils.datetimeStr(params?.value) : "";
}
},
{
id: 'actions',
field: 'proofStatus',
headerName: intl.formatMessage({ id: 'status' }),
width: isMdOrLg ? 'auto' : 160,
flex: isMdOrLg ? 1 : undefined,
renderHeader: renderHeaderWithAria,
renderCell: (params) => {
return locale === 'en' ? ProofStatus.getStatus_Eng(params) : locale === 'zh-HK' ? ProofStatus.getStatus_Cht(params) : ProofStatus.getStatus_Cn(params);
},
@@ -175,6 +185,7 @@ export default function SearchPublicNoticeTable({ searchCriteria, applyGridOnRea
headerName: intl.formatMessage({ id: 'fee' }),
width: isMdOrLg ? 'auto' : 160,
flex: isMdOrLg ? 1 : undefined,
renderHeader: renderHeaderWithAria,
valueGetter: (params) => {
return (params?.value) ? "$ " + FormatUtils.currencyFormat(params?.value) : "";
}


+ 19
- 9
src/pages/Proof/Search_Public/SearchForm.js ファイルの表示

@@ -29,7 +29,7 @@ const SearchPublicNoticeForm = ({ applySearch, searchCriteria, issueComboData, o
const [type, setType] = React.useState([]);
const [status, setStatus] = React.useState(searchCriteria.statusKey!=undefined?ComboData.proofStatusFull[searchCriteria.statusKey]:ComboData.proofStatusFull[0]);
const [issueSelected, setIssueSelected] = React.useState({});
const [issueSelected, setIssueSelected] = React.useState(null);
const [issueCombo, setIssueCombo] = React.useState([]);
const [groupSelected, setGroupSelected] = React.useState(searchCriteria.gazettGroup!=undefined?ComboData.groupTitle.find(item => item.code === searchCriteria.gazettGroup):{});

@@ -110,7 +110,7 @@ const SearchPublicNoticeForm = ({ applySearch, searchCriteria, issueComboData, o
if (issueComboData && issueComboData.length > 0) {
setIssueCombo(issueComboData);
if(searchCriteria.issueId!=undefined){
setIssueSelected(issueComboData.find(item => item.id === searchCriteria.issueId))
setIssueSelected(issueComboData.find(item => item.id === searchCriteria.issueId) ?? null)
}
}
}, [issueComboData]);
@@ -118,7 +118,7 @@ const SearchPublicNoticeForm = ({ applySearch, searchCriteria, issueComboData, o
function resetForm() {
setType([]);
setStatus(ComboData.proofStatusFull[0]);
setIssueSelected({});
setIssueSelected(null);
setGroupSelected({});
setMinDate(DateUtils.dateValue(new Date().setDate(new Date().getDate()-14)))
setMaxDate(DateUtils.dateValue(new Date()))
@@ -205,8 +205,9 @@ const SearchPublicNoticeForm = ({ applySearch, searchCriteria, issueComboData, o
id="issueId"
options={issueCombo}
value={issueSelected}
isOptionEqualToValue={(option, value) => option?.id === value?.id}
inputValue={(issueSelected?.id) ? getIssueLabel(issueSelected) : ""}
getOptionLabel={(option) => getIssueLabel(option)}
getOptionLabel={(option) => (option?.id ? getIssueLabel(option) : "")}
onChange={(event, newValue) => {
setIssueSelected(newValue);
}}
@@ -223,6 +224,10 @@ const SearchPublicNoticeForm = ({ applySearch, searchCriteria, issueComboData, o
}}
/>
)}
clearText={intl.formatMessage({ id: "muiClear" })}
closeText={intl.formatMessage({ id: "muiClose" })}
openText={intl.formatMessage({ id: "muiOpen" })}
noOptionsText={intl.formatMessage({ id: "muiNoOptions" })}
/>
</Grid>

@@ -252,7 +257,7 @@ const SearchPublicNoticeForm = ({ applySearch, searchCriteria, issueComboData, o
<Grid item xs={12} s={6} md={5} lg={3} sx={{ ml: 3, mr: 3, mb: 3 }}>
<Grid container spacing={1}>
<Grid item xs={6}>
<LocalizationProvider dateAdapter={AdapterDayjs}>
<LocalizationProvider dateAdapter={AdapterDayjs} localeText={DateUtils.getPickerLocaleText(intl.locale)}>
<DemoItem components={['DatePicker']}>
<DatePicker
id="dateFrom"
@@ -279,7 +284,7 @@ const SearchPublicNoticeForm = ({ applySearch, searchCriteria, issueComboData, o
</Grid>

<Grid item xs={6}>
<LocalizationProvider dateAdapter={AdapterDayjs}>
<LocalizationProvider dateAdapter={AdapterDayjs} localeText={DateUtils.getPickerLocaleText(intl.locale)}>
<DemoItem components={['DatePicker']}>
<DatePicker
id="dateTo"
@@ -348,11 +353,16 @@ const SearchPublicNoticeForm = ({ applySearch, searchCriteria, issueComboData, o
renderInput={(params) => (
<TextField {...params}
label={intl.formatMessage({id: 'status'})}
InputLabelProps={{
...params.InputLabelProps,
shrink: true
}}
/>
)}
InputLabelProps={{
shrink: true
}}
clearText={intl.formatMessage({ id: "muiClear" })}
closeText={intl.formatMessage({ id: "muiClose" })}
openText={intl.formatMessage({ id: "muiOpen" })}
noOptionsText={intl.formatMessage({ id: "muiNoOptions" })}
/>
</Grid>



+ 4
- 2
src/pages/Proof/Search_Public/index.js ファイルの表示

@@ -17,6 +17,7 @@ const SearchForm = Loadable(React.lazy(() => import('./SearchForm')));
const EventTable = Loadable(React.lazy(() => import('./DataGrid')));
import titleBackgroundImg from 'assets/images/dashboard/gazette-bar.png'
import {FormattedMessage} from "react-intl";
import usePageTitle from "components/usePageTitle";

const BackgroundHead = {
backgroundImage: `url(${titleBackgroundImg})`,
@@ -31,7 +32,8 @@ const BackgroundHead = {
// ==============================|| DASHBOARD - DEFAULT ||============================== //

const UserSearchPage_Individual = () => {

usePageTitle("proofRecord");
const [issueCombo,setIssueCombo] = React.useState([]);
const [searchCriteria, setSearchCriteria] = React.useState({
dateTo: DateUtils.dateValue(new Date()),
@@ -87,7 +89,7 @@ const UserSearchPage_Individual = () => {
<Grid item xs={12}>
<div style={BackgroundHead} >
<Stack direction="row" height='70px' justifyContent="flex-start" alignItems="center" >
<Typography ml={15} color='#FFF' variant="h4" sx={{display: { xs: 'none', sm: 'none', md: 'block' }}}>
<Typography component="h1" ml={15} color='#FFF' variant="h4" sx={{display: { xs: 'none', sm: 'none', md: 'block' }}}>
<FormattedMessage id="proofRecord"/>
</Typography>
</Stack>


+ 6
- 5
src/pages/PublicNotice/ApplyForm/PublicNoticeApplyForm.js ファイルの表示

@@ -282,7 +282,7 @@ const PublicNoticeApplyForm = ({ loadedData, _selections, gazetteIssueList }) =>
<Grid item xs={12} md={12} width="100%" >
<div style={BackgroundHead}>
<Stack direction="row" height='70px' justifyContent="flex-start" alignItems="center">
<Typography ml={15} color='#FFF' variant="h4" sx={{ display: { xs: 'none', sm: 'none', md: 'block' } }}>
<Typography component="h1" ml={15} color='#FFF' variant="h4" sx={{ display: { xs: 'none', sm: 'none', md: 'block' } }}>
<FormattedMessage id="applyPublicNotice" />
</Typography>
</Stack>
@@ -566,6 +566,7 @@ const PublicNoticeApplyForm = ({ loadedData, _selections, gazetteIssueList }) =>
label: intl.formatMessage({ id: 'careOf' }) + ":",
valueName: "careOf",
form: formik,
inputProps: { "aria-label": intl.formatMessage({ id: 'careOf' }) }
// disabled: true
})}
</Grid>
@@ -593,7 +594,7 @@ const PublicNoticeApplyForm = ({ loadedData, _selections, gazetteIssueList }) =>
label: intl.formatMessage({ id: 'extraMark' }) + ":",
valueName: "remarks",
form: formik,
inputProps: { maxLength: 255 }
inputProps: { maxLength: 255, "aria-label": intl.formatMessage({ id: 'extraMark' }) }
})}
</Grid>
}
@@ -622,6 +623,9 @@ const PublicNoticeApplyForm = ({ loadedData, _selections, gazetteIssueList }) =>
name="tickAccept"
color="primary"
size="small"
inputProps={{
"aria-label": intl.formatMessage({ id: "applyTickStr" })
}}
/>
<Typography variant="h6" height="100%" >
<div style={{ padding: 12, textAlign: 'justify' }} dangerouslySetInnerHTML={{ __html: intl.formatMessage({ id: "applyTickStr" }) }} />
@@ -651,9 +655,6 @@ const PublicNoticeApplyForm = ({ loadedData, _selections, gazetteIssueList }) =>
</Typography>
</Grid>




</Grid>
</form>
) : null}


+ 5
- 3
src/pages/PublicNotice/ApplyForm/index.js ファイルの表示

@@ -16,7 +16,7 @@ import Loadable from 'components/Loadable';
import { lazy } from 'react';
const LoadingComponent = Loadable(lazy(() => import('../../extra-pages/LoadingComponent')));
const PublicNoticeApplyForm = Loadable(lazy(() => import('./PublicNoticeApplyForm')));
import usePageTitle from "components/usePageTitle";
import {
// isORGLoggedIn,
isDummyLoggedIn,
@@ -27,6 +27,8 @@ import {
// ==============================|| DASHBOARD - DEFAULT ||============================== //

const ApplyForm = () => {
usePageTitle("applyPublicNotice");
const [userData, setUserData] = React.useState(null);
const [gazetteIssueList, setGazetteIssueList] = React.useState([]);

@@ -62,7 +64,7 @@ const ApplyForm = () => {
for (var i = 0; i < response?.gazetteIssueList?.length; i++) {
let data = response.gazetteIssueList[i];
//let label = getIssueLabel(data);
selection.push(<FormControlLabel value={data.id} control={<Radio />} label={getIssueLabel(data)} />);
selection.push(<FormControlLabel key={data.id} value={data.id} control={<Radio />} label={getIssueLabel(data)} />);
}
setGazetteIssueList(response?.gazetteIssueList);
setSelection(selection);
@@ -78,7 +80,7 @@ const ApplyForm = () => {
for (var i = 0; i < gazetteIssueList?.length; i++) {
let data = gazetteIssueList[i];
let label = getIssueLabel(data);
selection.push(<FormControlLabel value={data.id} control={<Radio />} label={label} />);
selection.push(<FormControlLabel key={data.id} value={data.id} control={<Radio />} label={label} />);
}
setSelection(selection);
}


+ 76
- 40
src/pages/PublicNotice/Details_GLD/ApplicationDetailCard.js ファイルの表示

@@ -9,6 +9,7 @@ import {
Stack,
Dialog, DialogTitle, DialogContent, DialogActions, InputAdornment, Autocomplete
} from '@mui/material';
import LoadingButton from '@mui/lab/LoadingButton';
import { isGranted, delBugMode, getPaymentMethodGLD} from "auth/utils";
const MainCard = Loadable(lazy(() => import('components/MainCard')));
import { useForm } from "react-hook-form";
@@ -33,18 +34,46 @@ import CloseIcon from '@mui/icons-material/Close';
import EditNoteIcon from '@mui/icons-material/EditNote';
import DownloadIcon from '@mui/icons-material/Download';
import ReplayIcon from '@mui/icons-material/Replay';
import { notifyDownloadSuccess } from 'utils/CommonFunction';
import { notifyActionError } from 'utils/CommonFunction';
import { isGrantedAny } from "auth/utils";
// import { useIntl } from "react-intl";
import { useIntl } from "react-intl";

/** Contained buttons with custom bg must restyle disabled/loading or they stay green/orange. */
const publishWithdrawLoadingSx = (mainBg, hoverBg) => (theme) => ({
textTransform: 'capitalize',
alignItems: 'end',
backgroundColor: mainBg,
color: '#fff',
'&:hover:not(.Mui-disabled):not(.MuiLoadingButton-loading)': {
backgroundColor: hoverBg,
},
'&.Mui-disabled, &.MuiLoadingButton-loading': {
backgroundColor: theme.palette.action.disabledBackground,
color: theme.palette.action.disabled,
},
'&.Mui-disabled .MuiSvgIcon-root, &.MuiLoadingButton-loading .MuiSvgIcon-root': {
color: theme.palette.action.disabled,
},
'&.Mui-disabled .MuiTypography-root, &.MuiLoadingButton-loading .MuiTypography-root': {
color: `${theme.palette.action.disabled} !important`,
},
});

// ==============================|| DASHBOARD - DEFAULT ||============================== //
const ApplicationDetailCard = (
{ applicationDetailData,
setStatus,
setUploadStatus
setUploadStatus,
statusDialogOpen = false,
statusDialogKind = "",
statusActionLoading = false,
}
) => {

const publishWithdrawBusy =
statusActionLoading ||
(statusDialogOpen && (statusDialogKind === 'publish' || statusDialogKind === 'withdraw'));

const [currentApplicationDetailData, setCurrentApplicationDetailData] = useState({});
const [companyName, setCompanyName] = useState({});
const [orgDetail, setOrgDetail] = useState({});
@@ -59,13 +88,15 @@ const ApplicationDetailCard = (
const [mode, setMode] = useState("");

const { register, handleSubmit } = useForm()
// const intl = useIntl();
const intl = useIntl();

const [isWarningPopUp, setIsWarningPopUp] = useState(false);
const [warningText, setWarningText] = useState("");

const [remarksPopUp, setRemarksPopUp] = useState(false);
const [onDownload, setOnDownload] = useState(false);
// eslint-disable-next-line no-unused-vars -- isProofCheckLoading in onProofClick + Button disabled; setIsProofCheckLoading in onProofClick callbacks
const [isProofCheckLoading, setIsProofCheckLoading] = useState(false);

useEffect(() => {
//if user data from parent are not null
@@ -125,12 +156,12 @@ const ApplicationDetailCard = (
fileId: fileDetail?.id,
skey: fileDetail?.skey,
filename: fileDetail?.filename,
onResponse:()=>{
setOnDownload(false)
notifyDownloadSuccess()
onResponse: () => {
setOnDownload(false);
},
onError:()=>{
setOnDownload(false)
onError: () => {
setOnDownload(false);
notifyActionError(intl.formatMessage({ id: 'downloadFailed' }));
}
});
setUploadStatus(true)
@@ -166,10 +197,12 @@ const ApplicationDetailCard = (
};

const withdrawnClick = () => () => {
if (publishWithdrawBusy) return;
setStatus("withdraw")
};

const doPublish = () => () => {
if (publishWithdrawBusy) return;
setStatus("publish")
}

@@ -178,10 +211,13 @@ const ApplicationDetailCard = (
};

const onProofClick = () => {
if (isProofCheckLoading) return;
if (applicationDetailData.data.groupNo) {
setIsProofCheckLoading(true);
HttpUtils.get({
url: CHECK_CREATE_PROOF + "/" + currentApplicationDetailData.id,
onSuccess: function (responeData) {
setIsProofCheckLoading(false);
if (responeData.success == true) {
window.open("/proof/create/" + currentApplicationDetailData.id, "_blank", "noreferrer");
window.addEventListener("focus", onFocus)
@@ -190,12 +226,16 @@ const ApplicationDetailCard = (
if (msg === "haveActiveProof") {
msg = "Action Failed: There is already a pending payment and proofreading record for client review."
} else if (msg === "haveProofed") {
msg = "Action Failed: Already proofed."
msg = "Action Failed: An active proof is already created for this application."
} else {
msg = intl.formatMessage({ id: msg });
}
setWarningText(msg);
setIsWarningPopUp(true);
}
}
},
onFail: () => setIsProofCheckLoading(false),
onError: () => setIsProofCheckLoading(false)
});
} else {
setWarningText("Please generate Gazette Code before Create Proof.");
@@ -256,6 +296,7 @@ const ApplicationDetailCard = (
<Button
// size="large"
variant="contained"
disabled={isProofCheckLoading}
onClick={() => { onProofClick() }}
sx={{
textTransform: 'capitalize',
@@ -324,31 +365,26 @@ const ApplicationDetailCard = (
</> :
(currentApplicationDetailData.status == "confirmed" && currentApplicationDetailData.creditor == 1) ?
<>
<Button
<LoadingButton
// size="large"
variant="contained"
onClick={doPublish()}
disabled={setCompleteDisable()}
sx={{
textTransform: 'capitalize',
alignItems: 'end',
backgroundColor: '#52b202'
}}>
disabled={setCompleteDisable() || publishWithdrawBusy}
loading={statusActionLoading && statusDialogKind === 'publish'}
sx={publishWithdrawLoadingSx('#52b202', '#489f04')}>
<DoneIcon />
<Typography ml={1} variant="h5">Publish</Typography>
</Button>
<Button
</LoadingButton>
<LoadingButton
// size="large"
variant="contained"
onClick={withdrawnClick()}
sx={{
textTransform: 'capitalize',
alignItems: 'end',
backgroundColor: '#ffa733'
}}>
disabled={publishWithdrawBusy}
loading={statusActionLoading && statusDialogKind === 'withdraw'}
sx={publishWithdrawLoadingSx('#ffa733', '#e8982e')}>
<CloseIcon />
<Typography ml={1} variant="h5">Withdraw</Typography>
</Button>
</LoadingButton>
</>
:
(
@@ -382,18 +418,16 @@ const ApplicationDetailCard = (
<DoneIcon />
<Typography ml={1} variant="h5">Publish</Typography>
</Button>
<Button
<LoadingButton
// size="large"
variant="contained"
onClick={withdrawnClick()}
sx={{
textTransform: 'capitalize',
alignItems: 'end',
backgroundColor: '#ffa733'
}}>
disabled={publishWithdrawBusy}
loading={statusActionLoading && statusDialogKind === 'withdraw'}
sx={publishWithdrawLoadingSx('#ffa733', '#e8982e')}>
<CloseIcon />
<Typography ml={1} variant="h5">Withdraw</Typography>
</Button>
</LoadingButton>
</> : null
)
}
@@ -715,7 +749,11 @@ const ApplicationDetailCard = (
sx={{
textTransform: 'capitalize',
alignItems: 'end',
}}>
backgroundColor: '#0C489E',
color: '#FFFFFF',
'&:hover': { backgroundColor: '#093A7A' },
}}
>
<DownloadIcon />
</Button>
</Grid>
@@ -841,7 +879,7 @@ const ApplicationDetailCard = (
}
}}
>
<DialogTitle><Typography variant="h3">Warning</Typography></DialogTitle>
<DialogTitle component="div"><Typography variant="h3">Warning</Typography></DialogTitle>
<DialogContent style={{ display: 'flex', }}>
<Typography variant="h4" style={{ padding: '16px' }}>{warningText}</Typography>
</DialogContent>
@@ -863,7 +901,7 @@ const ApplicationDetailCard = (
}}
>
<form onSubmit={handleSubmit(onSubmit)}>
<DialogTitle><Typography variant="h3">Remarks</Typography></DialogTitle>
<DialogTitle component="div"><Typography variant="h3">Remarks</Typography></DialogTitle>
<DialogContent style={{ display: 'flex', }}>
<Grid container direction="column">
<Grid item sx={{ padding: '16px' }}>
@@ -914,7 +952,7 @@ const ApplicationDetailCard = (
filterOptions={(options) => options}
options={ComboData.paymentMeans}
value={paymentMeans}
getOptionLabel={(option) => option.label}
getOptionLabel={(option) => (option?.label != null ? String(option.label) : "")}
inputValue={paymentMeans?.label ? paymentMeans?.label : ""}
onChange={(event, newValue) => {
setPaymentMeans(newValue);
@@ -927,11 +965,9 @@ const ApplicationDetailCard = (
renderInput={(params) => (
<TextField {...params}
label=""
InputLabelProps={{ shrink: true }}
/>
)}
InputLabelProps={{
shrink: true,
}}
disableClearable={true}
/>
</Grid>


+ 14
- 5
src/pages/PublicNotice/Details_GLD/GazetteDetailCard.js ファイルの表示

@@ -20,12 +20,14 @@ const LoadingComponent = Loadable(lazy(() => import('../../extra-pages/LoadingCo
import * as DateUtils from "utils/DateUtils";
import EditNoteIcon from '@mui/icons-material/EditNote';
import { isGrantedAny } from "auth/utils";
import { useIntl } from "react-intl";
// ==============================|| DASHBOARD - DEFAULT ||============================== //
const GazetteDetailCard = (
{ applicationDetailData,
setStatus
}
) => {
const intl = useIntl();
const [onReady, setOnReady] = useState(false);
const [issueNum, setIssueNum] = useState("");
const [issueDate, setIssueDate] = useState("");
@@ -51,11 +53,11 @@ const GazetteDetailCard = (
setIssueNum(applicationDetailData.gazetteIssueDetail.volume + "/" + applicationDetailData.gazetteIssueDetail.issueYear
+ " No. " + applicationDetailData.gazetteIssueDetail.issueNo);
setIssueDate(DateUtils.dateFormat(applicationDetailData.gazetteIssueDetail.issueDate, "D MMM YYYY (ddd)"));
setGazetteCode(applicationDetailData.data.groupNo)
setGazetteCode(applicationDetailData.data.groupNo ?? '')
// console.log(applicationDetailData)
setSysType(applicationDetailData.userData.sysType)
setCareOf(applicationDetailData.data.careOf)
setGroupTitle(applicationDetailData.data.groupTitle)
setCareOf(applicationDetailData.data.careOf ?? '')
setGroupTitle(applicationDetailData.data.groupTitle ?? '')
if (applicationDetailData.data.mode != null){
setMode(applicationDetailData.data.mode);
}
@@ -71,7 +73,11 @@ const GazetteDetailCard = (
}, [issueNum]);

const groupDetailClick = () => () => {
if (gazetteCode == null) {
// groupNo is normalized to '' when absent, so check empty string — not only null
const hasGazetteCode =
gazetteCode != null &&
String(gazetteCode).trim() !== "";
if (!hasGazetteCode) {
setStatus("genGazetteCode");
return;
}
@@ -275,6 +281,7 @@ const GazetteDetailCard = (
})}
value={careOf}
id='careOf'
inputProps={{ 'aria-label': intl.formatMessage({ id: 'careOf' }) }}
sx={{
"& .MuiInputBase-input.Mui-disabled": {
WebkitTextFillColor: "#000000",
@@ -303,7 +310,9 @@ const GazetteDetailCard = (
maxHeight: { xs: '90vh', s: '70vh', m: '70vh', lg: '60vh' }
}
}}>
<DialogTitle><Typography variant="h3">Warning</Typography></DialogTitle>
<DialogTitle component="div">
<Typography variant="h3" component="h2">Warning</Typography>
</DialogTitle>
<DialogContent style={{ display: 'flex', }}>
<Typography variant="h4" style={{ padding: '16px' }}>{warningText}</Typography>
</DialogContent>


+ 61
- 21
src/pages/PublicNotice/Details_GLD/StatusChangeDialog.js ファイルの表示

@@ -1,6 +1,7 @@
import {
useEffect,
useState
useState,
useRef
} from "react";

// material-ui
@@ -16,8 +17,9 @@ import {
FormLabel,
Autocomplete,
TextField,
Grid
Grid,
} from '@mui/material';
import LoadingButton from '@mui/lab/LoadingButton';

import * as ComboData from "utils/ComboData";
import { useFormik, FormikProvider } from 'formik';
@@ -30,8 +32,15 @@ const StatusChangeDialog = (props) => {
const [remarks, setRemarks] = useState("");
const [helperText, setHelperText] = useState("");
const [comboInputValue, setComboInputValue] = useState({});
const [positiveSubmitting, setPositiveSubmitting] = useState(false);
const positiveOnceRef = useRef(false);
const groupTitleComboList = ComboData.groupTitle;

const confirmLoading = Boolean(props.confirmLoading) || positiveSubmitting;
const gazetteGroupMissing =
props.getStatus === "genGazetteCode" &&
Object.keys(props.selectedGazetteGroup ?? {}).length === 0;

useEffect(() => {
setComboInputValue({});
if (props.getStatus == "genGazetteCode") {
@@ -58,22 +67,40 @@ const StatusChangeDialog = (props) => {
}
}, [props.getStatus]);

useEffect(() => {
if (!props.open) {
positiveOnceRef.current = false;
setPositiveSubmitting(false);
}
}, [props.open]);

const wasConfirmLoadingRef = useRef(false);
useEffect(() => {
if (wasConfirmLoadingRef.current && !props.confirmLoading) {
positiveOnceRef.current = false;
setPositiveSubmitting(false);
}
wasConfirmLoadingRef.current = Boolean(props.confirmLoading);
}, [props.confirmLoading]);

const acceptedHandle = () => () => {
const getStatus = props.getStatus.status;
if (getStatus == "notAccepted") {
if (!remarks || remarks == "")
if (confirmLoading) return;
if (positiveOnceRef.current) return;

const statusKey = props.getStatus;

if (statusKey === "notAccepted" || statusKey === "resubmit") {
if (!remarks || remarks === "") {
setHelperText("Please enter reason");
}
if (!helperText) {
props.setReason({ "reason": remarks });
if (remarks != null && remarks != "") {
// console.log(remarks)
// props.setStatusWindowAccepted(true);
return;
}
setHelperText("");
props.setReason({ "reason": remarks });
}
if (getStatus != "notAccepted") {
props.setStatusWindowAccepted(true);
}

positiveOnceRef.current = true;
setPositiveSubmitting(true);
props.setStatusWindowAccepted(true);
};


@@ -147,6 +174,11 @@ const StatusChangeDialog = (props) => {
id="gazetteGroup"
options={groupTitleComboList}
filterOptions={(options) => options}
getOptionLabel={(option) => {
if (option == null) return "";
if (typeof option === "string") return option;
return option.label != null ? String(option.label) : "";
}}
inputValue={comboInputValue.label}
onChange={(event, newValue) => {
if (newValue != null && newValue != {}) {
@@ -156,7 +188,6 @@ const StatusChangeDialog = (props) => {
props.setSelectedGazetteGroup(newValue);
formik.setFieldValue("", "")
} else {
gazetteGroup
props.setSelectedGazetteGroupInputType("");
}
}}
@@ -177,7 +208,10 @@ const StatusChangeDialog = (props) => {
return (
<Dialog
open={props.open}
onClose={props.handleClose}
onClose={() => {
if (confirmLoading) return;
props.handleClose();
}}
fullWidth={true}
maxWidth={'md'}
>
@@ -195,7 +229,7 @@ const StatusChangeDialog = (props) => {
<FormikProvider value={formik}>
<form>
<DialogContent>
<DialogContentText>
<DialogContentText component="div">
{content}
</DialogContentText>
</DialogContent>
@@ -203,18 +237,24 @@ const StatusChangeDialog = (props) => {
</FormikProvider>
<Stack direction="row" justifyContent="space-around">
<DialogActions>
<Button variant="contained" onClick={props.handleClose} autoFocus >
<Button variant="contained" onClick={props.handleClose} autoFocus disabled={confirmLoading}>
<Typography variant="h5">
Cancel
</Typography>
</Button>
</DialogActions>
<DialogActions>
<Button variant="contained" color="success" onClick={acceptedHandle()} autoFocus disabled={Object.keys(props.selectedGazetteGroup).length === 0 && props.getStatus === "genGazetteCode"}>
<Typography variant="h5">
<LoadingButton
variant="contained"
color="success"
onClick={acceptedHandle()}
loading={confirmLoading}
disabled={gazetteGroupMissing}
>
<Typography variant="h5" component="span">
{prositiveBtnText}
</Typography>
</Button>
</LoadingButton>
</DialogActions>
</Stack>
</Dialog>


+ 115
- 21
src/pages/PublicNotice/Details_GLD/index.js ファイルの表示

@@ -70,6 +70,7 @@ const PublicNoticeDetail_GLD = () => {
const [open, setOpen] = useState(false);
const [getStatus, setStatus] = useState("");
const [statusWindowAccepted, setStatusWindowAccepted] = useState(false);
const [statusConfirmLoading, setStatusConfirmLoading] = useState(false);
const [selectedGazetteGroup, setSelectedGazetteGroup] = useState({});
const [selectedGazetteGroupInputType, setSelectedGazetteGroupInputType] = useState("");
const [getReason, setReason] = useState({});
@@ -182,24 +183,30 @@ const PublicNoticeDetail_GLD = () => {
};

useEffect(() => {
if (statusWindowAccepted) {
if (getStatus == "genGazetteCode") {
onAcceptedClick()
} else if (getStatus == "complete") {
onComplatedClick()
} else if (getStatus == "withdraw") {
onWithdrawnClick()
} else if (getStatus == "notAccepted") {
onNotAcceptClick(getReason);
} else if (getStatus == "resubmit") {
onReSubmitClick(getReason);
} else if (getStatus == "publish") {
onPublishClick();
} else if (getStatus == "revoke") {
onRevokeClick();
} else if(getStatus == "paid"){
onPaidClick();
}
if (!statusWindowAccepted) {
setStatusConfirmLoading(false);
return;
}
setStatusConfirmLoading(true);
if (getStatus == "genGazetteCode") {
onAcceptedClick()
} else if (getStatus == "complete") {
onComplatedClick()
} else if (getStatus == "withdraw") {
onWithdrawnClick()
} else if (getStatus == "notAccepted") {
onNotAcceptClick(getReason);
} else if (getStatus == "resubmit") {
onReSubmitClick(getReason);
} else if (getStatus == "publish") {
onPublishClick();
} else if (getStatus == "revoke") {
onRevokeClick();
} else if(getStatus == "paid"){
onPaidClick();
} else {
setStatusConfirmLoading(false);
setStatusWindowAccepted(false);
}
}, [statusWindowAccepted]);

@@ -223,12 +230,23 @@ const PublicNoticeDetail_GLD = () => {
.catch(error => {
console.log(error);
return false;
})
.finally(() => {
setStatusConfirmLoading(false);
setStatusWindowAccepted(false);
});
} else {
setStatusConfirmLoading(false);
setStatusWindowAccepted(false);
}
};

const onNotAcceptClick = (reason) => {
if (params.id <= 0) return;
if (params.id <= 0) {
setStatusConfirmLoading(false);
setStatusWindowAccepted(false);
return;
}
HttpUtils.post({
url: `${SET_PUBLIC_NOTICE_STATUS_NOT_ACCEPT}/${params.id}`,
params: reason,
@@ -238,12 +256,24 @@ const PublicNoticeDetail_GLD = () => {
// location.reload();
loadApplicationDetail()
notifySaveSuccess()
},
onFail: () => {
setStatusConfirmLoading(false);
setStatusWindowAccepted(false);
},
onError: () => {
setStatusConfirmLoading(false);
setStatusWindowAccepted(false);
}
});
}

const onPublishClick = () => {
if (params.id <= 0) return;
if (params.id <= 0) {
setStatusConfirmLoading(false);
setStatusWindowAccepted(false);
return;
}
HttpUtils.get({
url: `${SET_PUBLIC_NOTICE_STATUS_PUBLISH}/${params.id}`,
onSuccess: function () {
@@ -251,6 +281,18 @@ const PublicNoticeDetail_GLD = () => {
handleClose();
loadApplicationDetail()
notifySaveSuccess()
},
onFail: () => {
setStatusConfirmLoading(false);
setStatusWindowAccepted(false);
},
onError: () => {
setStatusConfirmLoading(false);
setStatusWindowAccepted(false);
},
onFinally: () => {
setStatusConfirmLoading(false);
setStatusWindowAccepted(false);
}
});
}
@@ -270,7 +312,14 @@ const PublicNoticeDetail_GLD = () => {
.catch(error => {
console.log(error);
return false;
})
.finally(() => {
setStatusConfirmLoading(false);
setStatusWindowAccepted(false);
});
} else {
setStatusConfirmLoading(false);
setStatusWindowAccepted(false);
}
};

@@ -289,7 +338,14 @@ const PublicNoticeDetail_GLD = () => {
.catch(error => {
console.log(error);
return false;
})
.finally(() => {
setStatusConfirmLoading(false);
setStatusWindowAccepted(false);
});
} else {
setStatusConfirmLoading(false);
setStatusWindowAccepted(false);
}
};

@@ -307,7 +363,14 @@ const PublicNoticeDetail_GLD = () => {
.catch(error => {
console.log(error);
return false;
})
.finally(() => {
setStatusConfirmLoading(false);
setStatusWindowAccepted(false);
});
} else {
setStatusConfirmLoading(false);
setStatusWindowAccepted(false);
}
};

@@ -322,6 +385,14 @@ const PublicNoticeDetail_GLD = () => {
// location.reload();
loadApplicationDetail()
notifySaveSuccess()
},
onFail: () => {
setStatusConfirmLoading(false);
setStatusWindowAccepted(false);
},
onError: () => {
setStatusConfirmLoading(false);
setStatusWindowAccepted(false);
}
});
// axios.post(`${SET_PUBLIC_NOTICE_STATUS_RESUBMIT}/${params.id}`)
@@ -338,11 +409,18 @@ const PublicNoticeDetail_GLD = () => {
// console.log(error);
// return false;
// });
} else {
setStatusConfirmLoading(false);
setStatusWindowAccepted(false);
}
};

const onRevokeClick = () => {
if (params.id <= 0) return;
if (params.id <= 0) {
setStatusConfirmLoading(false);
setStatusWindowAccepted(false);
return;
}
HttpUtils.get({
url: `${SET_PUBLIC_NOTICE_STATUS_REVOKE}/${params.id}`,
onSuccess: function () {
@@ -350,6 +428,18 @@ const PublicNoticeDetail_GLD = () => {
handleClose();
loadApplicationDetail()
notifySaveSuccess()
},
onFail: () => {
setStatusConfirmLoading(false);
setStatusWindowAccepted(false);
},
onError: () => {
setStatusConfirmLoading(false);
setStatusWindowAccepted(false);
},
onFinally: () => {
setStatusConfirmLoading(false);
setStatusWindowAccepted(false);
}
});
}
@@ -383,6 +473,7 @@ const PublicNoticeDetail_GLD = () => {
issueDate={issueDate}
issueNum={issueNum}
gazetteIssue={gazetteIssue}
confirmLoading={statusConfirmLoading}
//combo value
selectedGazetteGroup={selectedGazetteGroup}
setSelectedGazetteGroup={setSelectedGazetteGroup}
@@ -425,6 +516,9 @@ const PublicNoticeDetail_GLD = () => {
setUpdateApplicationObject={setUpdateApplicationObject}
isEditMode={isEditMode}
setiIsSave={setiIsSave}
statusDialogOpen={open}
statusDialogKind={getStatus}
statusActionLoading={statusConfirmLoading}
// isNewRecord={isNewRecord}
/>
}


+ 8
- 0
src/pages/PublicNotice/Details_GLD/tabTableDetail/PaymentTab.js ファイルの表示

@@ -17,6 +17,10 @@ export default function SubmittedTab({ appId, setCount }) {
const theme = useTheme();
const isMdOrLg = useMediaQuery(theme.breakpoints.up('md'));

const renderHeaderWithAria = (params) => (
<span aria-label={params.colDef.headerName}>{params.colDef.headerName}</span>
);

const columns = [
{
field: 'actions',
@@ -24,6 +28,7 @@ export default function SubmittedTab({ appId, setCount }) {
width: isMdOrLg ? 'auto' : 160,
flex: isMdOrLg ? 1 : undefined,
cellClassName: 'actions',
renderHeader: renderHeaderWithAria,
renderCell: (params) => {
return clickableLink('/paymentPage/details/' + params.row.id, params.row.transNo);
},
@@ -34,6 +39,7 @@ export default function SubmittedTab({ appId, setCount }) {
headerName: 'Trans. Date',
width: isMdOrLg ? 'auto' : 160,
flex: isMdOrLg ? 1 : undefined,
renderHeader: renderHeaderWithAria,
valueGetter: (params) => {
return DateUtils.datetimeStr(params.value);
}
@@ -44,6 +50,7 @@ export default function SubmittedTab({ appId, setCount }) {
headerName: 'Status',
width: isMdOrLg ? 'auto' : 160,
flex: isMdOrLg ? 1 : undefined,
renderHeader: renderHeaderWithAria,
renderCell: (params) => {
return PaymentStatus.getStatus_Eng(params);
}
@@ -53,6 +60,7 @@ export default function SubmittedTab({ appId, setCount }) {
field: 'payAmount',
headerName: 'Amount',
width: 150,
renderHeader: renderHeaderWithAria,
valueGetter: (params) => {
return (params?.value) ? "$ " + FormatUtils.currencyFormat(params?.value) : "";
}


+ 11
- 2
src/pages/PublicNotice/Details_GLD/tabTableDetail/ProofTab.js ファイルの表示

@@ -28,6 +28,10 @@ export default function ProofTab({appId, setCount}) {
});
};

const renderHeaderWithAria = (params) => (
<span aria-label={params.colDef.headerName}>{params.colDef.headerName}</span>
);

const columns = [
{
@@ -36,6 +40,7 @@ export default function ProofTab({appId, setCount}) {
width: isMdOrLg ? 'auto' : 200,
flex: isMdOrLg ? 1 : undefined,
cellClassName: 'actions',
renderHeader: renderHeaderWithAria,
renderCell: (params) => {
return clickableLink('/proof/reply/' + params.row.id, params.row.refNo);
},
@@ -45,6 +50,7 @@ export default function ProofTab({appId, setCount}) {
headerName: 'Status',
width: isMdOrLg ? 'auto' : 160,
flex: isMdOrLg ? 1 : undefined,
renderHeader: renderHeaderWithAria,
renderCell: (params) => {
return ProofStatus.getStatus_Eng(params);
},
@@ -54,7 +60,7 @@ export default function ProofTab({appId, setCount}) {
headerName: 'Proof Issue Date',
width: isMdOrLg ? 'auto' : 160,
flex: isMdOrLg ? 1 : undefined,
renderHeader: renderHeaderWithAria,
valueGetter: (params) => {
return DateUtils.datetimeStr(params?.value);
}
@@ -64,6 +70,7 @@ export default function ProofTab({appId, setCount}) {
headerName: 'Confirmed/Return Date',
width: isMdOrLg ? 'auto' : 160,
flex: isMdOrLg ? 1 : undefined,
renderHeader: renderHeaderWithAria,
valueGetter: (params) => {
return params?.value?DateUtils.datetimeStr(params?.value):"";
}
@@ -73,15 +80,17 @@ export default function ProofTab({appId, setCount}) {
headerName: 'Fee',
width: isMdOrLg ? 'auto' : 160,
flex: isMdOrLg ? 1 : undefined,
renderHeader: renderHeaderWithAria,
valueGetter: (params) => {
return (params?.value)?"$ "+FormatUtils.currencyFormat(params?.value):"";
}
},
{
field: 'actions',
type: 'actions',
headerName: 'Proof Slip',
width: 100,
renderHeader: renderHeaderWithAria,
cellClassName: 'actions',
getActions: (params) => {
if(params.row.action == null) return[];


+ 8
- 1
src/pages/PublicNotice/Details_GLD/tabTableDetail/StatusHistoryTab.js ファイルの表示

@@ -13,6 +13,10 @@ export default function StatusHistoryTab({appId, setCount}) {
const theme = useTheme();
const isMdOrLg = useMediaQuery(theme.breakpoints.up('md'));

const renderHeaderWithAria = (params) => (
<span aria-label={params.colDef.headerName}>{params.colDef.headerName}</span>
);

const columns = [
{
id: 'created',
@@ -20,6 +24,7 @@ export default function StatusHistoryTab({appId, setCount}) {
headerName: 'Date',
width: isMdOrLg ? 'auto' : 160,
flex: isMdOrLg ? 1 : undefined,
renderHeader: renderHeaderWithAria,
valueGetter: (params) => {
return DateUtils.datetimeStr(params?.value);
}
@@ -31,6 +36,7 @@ export default function StatusHistoryTab({appId, setCount}) {
headerName: 'Changed By',
width: isMdOrLg ? 'auto' : 160,
flex: isMdOrLg ? 1 : undefined,
renderHeader: renderHeaderWithAria,
},
{
id: 'status',
@@ -38,8 +44,9 @@ export default function StatusHistoryTab({appId, setCount}) {
headerName: 'Status',
width: isMdOrLg ? 'auto' : 160,
flex: isMdOrLg ? 1 : undefined,
renderHeader: renderHeaderWithAria,
renderCell: (params) => {
return [StatusUtils.getStatusEng(params)]
return StatusUtils.getStatusEng(params);
},
},
];


+ 6
- 5
src/pages/PublicNotice/Details_GLD/tabTableDetail/TabTable.js ファイルの表示

@@ -9,6 +9,7 @@ import {

import { TabPanel, TabContext, TabList } from '@mui/lab';
import {useState, useEffect, lazy} from "react";
import { useIntl } from 'react-intl';

import Loadable from 'components/Loadable';
const LoadingComponent = Loadable(lazy(() => import('../../../extra-pages/LoadingComponent')));
@@ -20,7 +21,7 @@ const StatusHistoryTab = Loadable(lazy(() => import('./StatusHistoryTab')));
// ==============================|| DASHBOARD - DEFAULT ||============================== //

const PublicNotice = ({ appId, proofCount, paymentCount, statusHistoryCount, setProofCount, setPaymentCount, setStatusHistoryCount }) => {
const intl = useIntl();
const [onReady, setOnReady] = useState(false);
const [selectedTab, setSelectedTab] = useState("1");

@@ -41,10 +42,10 @@ const PublicNotice = ({ appId, proofCount, paymentCount, statusHistoryCount, set
<Grid item xs={12}>
<TabContext value={selectedTab}>
<Box sx={{ borderBottom: 1, borderColor: 'divider', overflowX: 'auto' }}>
<TabList onChange={handleChange} aria-label="lab API tabs example">
<Tab renderActiveOnly={false} label={"Proof (" + proofCount + ") "} value="1" />
<Tab renderActiveOnly={false} label={"Online Payment (" + paymentCount + ") "} value="2" />
<Tab renderActiveOnly={false} label={"Status History (" + statusHistoryCount + ") "} value="3" />
<TabList onChange={handleChange} aria-label={intl.formatMessage({ id: 'ariaRelatedRecords' })}>
<Tab label={"Proof (" + proofCount + ") "} value="1" />
<Tab label={"Online Payment (" + paymentCount + ") "} value="2" />
<Tab label={"Status History (" + statusHistoryCount + ") "} value="3" />
</TabList>
</Box>
<TabPanel value="1" sx={{ p: 0 }}>


+ 14
- 4
src/pages/PublicNotice/Details_Public/ApplicationDetailCard.js ファイルの表示

@@ -222,7 +222,6 @@ const ApplicationDetailCard = (
<Button
variant="contained"
onClick={cancelledClick()}
color="edit"
disabled={currentApplicationDetailData.status == "rejected"
|| currentApplicationDetailData.status == "cancelled"
|| currentApplicationDetailData.status == "withdrawn"
@@ -239,6 +238,11 @@ const ApplicationDetailCard = (
title={intl.formatMessage({ id: 'cancelApp' })}
startIcon={<CloseIcon />}
aria-label={intl.formatMessage({ id: 'cancelApp' })}
sx={{
backgroundColor: '#0C489E',
color: '#FFFFFF',
'&:hover': { backgroundColor: '#093A7A' },
}}
>
<FormattedMessage id="cancelApp" />
</Button>
@@ -651,20 +655,24 @@ const ApplicationDetailCard = (
<Grid container direction="row" alignItems="center" justifyContent="flex-start">
<Grid item xs={12} sm={12} md={12} lg={12} sx={{ wordBreak: 'break-word', }}>
<Typography
fullWidth
id='fileName'
variant="pnspsFormParagraph"
sx={{ width: '100%' }}
>
{fileDetail?.filename}
</Typography>
<ThemeProvider theme={PNSPS_BUTTON_THEME}>
<Button
sx={{ ml: 3 }}
sx={{
ml: 3,
backgroundColor: '#0C489E',
color: '#FFFFFF',
'&:hover': { backgroundColor: '#093A7A' },
}}
variant="contained"
onClick={onDownloadClick()}
aria-label={intl.formatMessage({ id: 'download' })}
title={intl.formatMessage({ id: 'download' })}
color="save"
disabled={!fileDetail?.filename||onDownload}
startIcon={<DownloadIcon sx={{ alignItems: "center" }} />}
>
@@ -690,7 +698,9 @@ const ApplicationDetailCard = (
<FormControl variant="outlined" sx={{ width: '100%' }} disabled>
<OutlinedInput
size="small"
id="careOf"
value={currentApplicationDetailData.careOf}
inputProps={{ 'aria-label': intl.formatMessage({ id: 'careOf' }) }}
sx={{
"& .MuiInputBase-input.Mui-disabled": {
WebkitTextFillColor: "#000000",


+ 33
- 10
src/pages/PublicNotice/Details_Public/StatusChangeDialog.js ファイルの表示

@@ -1,10 +1,12 @@
import {
useEffect,
useState
useState,
useRef
} from "react";

// material-ui
import {
Box,
Button,
// Link,
Stack,
@@ -14,7 +16,7 @@ import {
DialogContent,
DialogContentText,
DialogTitle,
FormLabel,
CircularProgress,
} from '@mui/material';
import { useFormik,FormikProvider } from 'formik';
import * as yup from 'yup';
@@ -26,6 +28,7 @@ import {useIntl} from "react-intl";
const StatusChangeDialog = (props) => {
const [status, setStatus] = useState("");
const intl = useIntl();
const confirmOnceRef = useRef(false);

useEffect(() => {
// console.log(Object.keys(!props.selectedGazetteGroup).length)
@@ -33,9 +36,25 @@ const StatusChangeDialog = (props) => {
setStatus(intl.formatMessage({id: 'cancel'}))
}
}, [props.getStatus]);

useEffect(() => {
if (!props.open) {
confirmOnceRef.current = false;
}
}, [props.open]);

const wasConfirmLoadingRef = useRef(false);
useEffect(() => {
if (wasConfirmLoadingRef.current && !props.confirmLoading) {
confirmOnceRef.current = false;
}
wasConfirmLoadingRef.current = props.confirmLoading;
}, [props.confirmLoading]);
const acceptedHandle = () => () =>{
// console.log(selectedGazetteGroup)
if (props.confirmLoading) return;
if (confirmOnceRef.current) return;
confirmOnceRef.current = true;
props.setStatusWindowAccepted(true)
};

@@ -60,19 +79,20 @@ const StatusChangeDialog = (props) => {
fullWidth={true}
maxWidth={'xs'}
>
<DialogTitle >
<Typography variant="h4">
<DialogTitle component="div">
<Typography variant="h4" component="h2">
{status} {intl.formatMessage({id: 'publicNotice'})}
</Typography>
</DialogTitle>
<FormikProvider value={formik}>
<form>
<DialogContent>
<DialogContentText>
<FormLabel sx={{fontSize: "18px", color:"#000000",textAlign:"center"}}>
<Typography variant="h5">
{intl.formatMessage({id: 'confirmTo'})}{status} {intl.formatMessage({id: 'publicNoticeApp'})}?</Typography>
</FormLabel>
<DialogContentText component="div">
<Box sx={{ fontSize: '18px', color: '#000000', textAlign: 'center' }}>
<Typography variant="h5" component="p" sx={{ m: 0 }}>
{intl.formatMessage({id: 'confirmTo'})}{status} {intl.formatMessage({id: 'publicNoticeApp'})}?
</Typography>
</Box>
</DialogContentText>
</DialogContent>
</form>
@@ -85,6 +105,7 @@ const StatusChangeDialog = (props) => {
onClick={props.handleClose}
autoFocus
color="cancel"
disabled={props.confirmLoading}
>
<FormattedMessage id="cancel"/>
</Button>
@@ -95,6 +116,8 @@ const StatusChangeDialog = (props) => {
color="save"
onClick={acceptedHandle()}
autoFocus
disabled={props.confirmLoading}
startIcon={props.confirmLoading ? <CircularProgress color="inherit" size={20} /> : null}
>
<FormattedMessage id="confirm"/>
</Button>


+ 22
- 4
src/pages/PublicNotice/Details_Public/index.js ファイルの表示

@@ -1,6 +1,7 @@
import React, {
useEffect,
useState,
useRef,
lazy
} from "react";

@@ -32,10 +33,12 @@ import { useNavigate } from "react-router-dom";
import ForwardIcon from '@mui/icons-material/Forward';
import { notifyActionSuccess } from "utils/CommonFunction";
import { FormattedMessage, useIntl } from "react-intl";
import usePageTitle from "components/usePageTitle";
// ==============================|| Body - DEFAULT ||============================== //

const DashboardDefault = () => {
usePageTitle("myPublicNotice");
const params = useParams();
const [applicationDetailData, setApplicationDetailData] = useState({});
const [appNo, setAapNo] = useState("");
@@ -47,6 +50,8 @@ const DashboardDefault = () => {
const [open, setOpen] = useState(false);
const [getStatus, setStatus] = useState("");
const [statusWindowAccepted, setStatusWindowAccepted] = useState(false);
const [cancelLoading, setCancelLoading] = useState(false);
const cancellingRef = useRef(false);

const [proofCount, setProofCount] = useState(0);
const [paymentCount, setPaymentCount] = useState(0);
@@ -135,7 +140,10 @@ const DashboardDefault = () => {
}, [getStatus]);

const onCancelledClick = () => {
if (cancellingRef.current) return;
if (params.id > 0) {
cancellingRef.current = true;
setCancelLoading(true);
axios.get(`${SET_PUBLIC_NOTICE_STATUS_CANCELLED}/${params.id}`)
.then((response) => {
if (response.status === 204) {
@@ -149,17 +157,27 @@ const DashboardDefault = () => {
.catch(error => {
console.log(error);
return false;
})
.finally(() => {
cancellingRef.current = false;
setCancelLoading(false);
});
}
};

return (
<Grid container sx={{ backgroundColor: '#ffffff' }} direction="column">
<StatusChangeDialog open={open} handleClose={handleClose} setStatusWindowAccepted={setStatusWindowAccepted} getStatus={getStatus} />
<StatusChangeDialog
open={open}
handleClose={handleClose}
setStatusWindowAccepted={setStatusWindowAccepted}
getStatus={getStatus}
confirmLoading={cancelLoading}
/>
<Grid item xs={12}>
<div style={BackgroundHead}>
<Stack direction="row" height='70px' justifyContent="flex-start" alignItems="center">
<Typography ml={15} color='#FFF' variant="h4" sx={{ display: { xs: 'none', sm: 'none', md: 'block' } }}>
<Typography component="h1" ml={15} color='#FFF' variant="h4" sx={{ display: { xs: 'none', sm: 'none', md: 'block' } }}>
<FormattedMessage id="myPublicNotice" />
</Typography>
</Stack>
@@ -177,7 +195,7 @@ const DashboardDefault = () => {
>
<ForwardIcon style={{ height: 30, width: 50, transform: "rotate(180deg)" }} />
</Button>
<Typography ml={3} mt={3} variant="h4">{title}</Typography>
<Typography component="h2" ml={3} mt={3} variant="h4">{title}</Typography>
</Stack>
</Grid>
<Grid item width={{ md: "60%", xs: "90%" }}>


+ 8
- 0
src/pages/PublicNotice/Details_Public/tabTableDetail/PaymentTab.js ファイルの表示

@@ -21,6 +21,10 @@ export default function SubmittedTab({ appId, setCount }) {
const { locale } = intl;

const renderHeaderWithAria = (params) => (
<span aria-label={params.colDef.headerName}>{params.colDef.headerName}</span>
);

const columns = [
{
field: 'actions',
@@ -28,6 +32,7 @@ export default function SubmittedTab({ appId, setCount }) {
width: isMdOrLg ? 'auto' : 160,
flex: isMdOrLg ? 1 : undefined,
cellClassName: 'actions',
renderHeader: renderHeaderWithAria,
renderCell: (params) => {
return clickableLink('/paymentPage/details/' + params.row.id, params.row.transNo);
},
@@ -38,6 +43,7 @@ export default function SubmittedTab({ appId, setCount }) {
headerName: intl.formatMessage({id: 'payDate'}),
width: isMdOrLg ? 'auto' : 160,
flex: isMdOrLg ? 1 : undefined,
renderHeader: renderHeaderWithAria,
valueGetter: (params) => {
return DateUtils.datetimeStr(params.value);
}
@@ -48,6 +54,7 @@ export default function SubmittedTab({ appId, setCount }) {
headerName: intl.formatMessage({id: 'payStatus'}),
width: isMdOrLg ? 'auto' : 160,
flex: isMdOrLg ? 1 : undefined,
renderHeader: renderHeaderWithAria,
renderCell: (params) => {
return locale === 'en' ? PaymentStatus.getStatus_Eng(params):PaymentStatus.getStatus_Cht(params);
}
@@ -57,6 +64,7 @@ export default function SubmittedTab({ appId, setCount }) {
field: 'payAmount',
headerName: intl.formatMessage({id: 'fee'}),
width: 150,
renderHeader: renderHeaderWithAria,
valueGetter: (params) => {
return (params?.value) ? "$ " + FormatUtils.currencyFormat(params?.value) : "";
}


+ 10
- 3
src/pages/PublicNotice/Details_Public/tabTableDetail/ProofTab.js ファイルの表示

@@ -19,23 +19,27 @@ export default function ProofTab({appId, setCount}) {
const isMdOrLg = useMediaQuery(theme.breakpoints.up('md'));
const { locale } = intl;

const renderHeaderWithAria = (params) => (
<span aria-label={params.colDef.headerName}>{params.colDef.headerName}</span>
);

const columns = [
{
field: 'actions',
field: 'refNo',
headerName: intl.formatMessage({id: 'proofId'}),
width: 200,
cellClassName: 'actions',
renderHeader: renderHeaderWithAria,
renderCell: (params) => {
return clickableLink('/proof/reply/' + params.row.id, params.row.refNo);
},
},
{
id: 'actions',
field: 'status',
headerName: intl.formatMessage({id: 'status'}),
width: isMdOrLg ? 'auto' : 160,
flex: isMdOrLg ? 1 : undefined,
renderHeader: renderHeaderWithAria,
renderCell: (params) => {
return locale === 'en' ? ProofStatus.getStatus_Eng(params) : ProofStatus.getStatus_Cht(params);
},
@@ -46,6 +50,7 @@ export default function ProofTab({appId, setCount}) {
headerName: intl.formatMessage({id: 'proofDate'}),
width: isMdOrLg ? 'auto' : 160,
flex: isMdOrLg ? 1 : undefined,
renderHeader: renderHeaderWithAria,
valueGetter: (params) => {
return DateUtils.datetimeStr(params?.value);
}
@@ -56,6 +61,7 @@ export default function ProofTab({appId, setCount}) {
headerName: intl.formatMessage({id: 'replyDate'}),
width: isMdOrLg ? 'auto' : 160,
flex: isMdOrLg ? 1 : undefined,
renderHeader: renderHeaderWithAria,
valueGetter: (params) => {
return params?.value?DateUtils.datetimeStr(params?.value):"";
}
@@ -66,6 +72,7 @@ export default function ProofTab({appId, setCount}) {
headerName: intl.formatMessage({id: 'fee'}),
width: isMdOrLg ? 'auto' : 160,
flex: isMdOrLg ? 1 : undefined,
renderHeader: renderHeaderWithAria,
valueGetter: (params) => {
return (params?.value)?"$ "+FormatUtils.currencyFormat(params?.value):"";
}


+ 10
- 2
src/pages/PublicNotice/Details_Public/tabTableDetail/StatusHistoryTab.js ファイルの表示

@@ -1,5 +1,6 @@
// material-ui
import * as React from 'react';
import { useTheme, useMediaQuery } from '@mui/material';
import {FiDataGrid} from "components/FiDataGrid";
import * as DateUtils from "utils/DateUtils"
import * as StatusUtils from "utils/statusUtils/PublicNoteStatusUtils";
@@ -9,7 +10,7 @@ import {GET_PUBLIC_NOTICE_APPLY_DETAIL_STATUS_HISTORY } from "utils/ApiPathConst
// ==============================|| EVENT TABLE ||============================== //

export default function StatusHistoryTab({appId, setCount}) {
const { useState, useEffect } = React;
const theme = useTheme();
const isMdOrLg = useMediaQuery(theme.breakpoints.up('md'));

@@ -21,6 +22,10 @@ export default function StatusHistoryTab({appId, setCount}) {
set_appId(appId);
}, []);

const renderHeaderWithAria = (params) => (
<span aria-label={params.colDef.headerName}>{params.colDef.headerName}</span>
);

const columns = [
{
id: 'created',
@@ -28,6 +33,7 @@ export default function StatusHistoryTab({appId, setCount}) {
headerName: 'Date',
width: isMdOrLg ? 'auto' : 160,
flex: isMdOrLg ? 1 : undefined,
renderHeader: renderHeaderWithAria,
valueGetter: (params) => {
return DateUtils.datetimeStr(params?.value);
}
@@ -39,6 +45,7 @@ export default function StatusHistoryTab({appId, setCount}) {
headerName: 'Changed By',
width: isMdOrLg ? 'auto' : 160,
flex: isMdOrLg ? 1 : undefined,
renderHeader: renderHeaderWithAria,
},
{
id: 'status',
@@ -46,8 +53,9 @@ export default function StatusHistoryTab({appId, setCount}) {
headerName: 'Status',
width: isMdOrLg ? 'auto' : 160,
flex: isMdOrLg ? 1 : undefined,
renderHeader: renderHeaderWithAria,
renderCell: (params) => {
return [StatusUtils.getStatusEng(params)]
return StatusUtils.getStatusEng(params);
},
},
];


+ 1
- 1
src/pages/PublicNotice/Details_Public/tabTableDetail/TabTable.js ファイルの表示

@@ -43,7 +43,7 @@ const PublicNotice = ({ appId, proofCount, paymentCount, setProofCount, setPayme
<Grid item xs={12}>
<TabContext value={selectedTab}>
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
<TabList onChange={handleChange} aria-label="lab API tabs example">
<TabList onChange={handleChange} aria-label={intl.formatMessage({ id: 'ariaRelatedRecords' })}>
<Tab
aria-label={intl.formatMessage({ id: 'proofRecord' })}
label={


+ 10
- 2
src/pages/PublicNotice/ListPanel/BaseGrid.js ファイルの表示

@@ -28,6 +28,9 @@ export default function BaseGrid({setCount, url}) {
navigate('/publicNotice/'+ params.id);
};

const renderHeaderWithAria = (params) => (
<span aria-label={params.colDef.headerName}>{params.colDef.headerName}</span>
);

const columns = [
{
@@ -36,8 +39,9 @@ export default function BaseGrid({setCount, url}) {
headerName: intl.formatMessage({id: 'applicationId'}),
width: isMdOrLg ? 'auto' : 160,
flex: isMdOrLg ? 1 : undefined,
renderHeader: renderHeaderWithAria,
renderCell: (params) => {
return [params.row.appNo+getModeIntl(params,intl)]
return params.row.appNo + getModeIntl(params, intl);
},
},
{
@@ -46,6 +50,7 @@ export default function BaseGrid({setCount, url}) {
headerName: intl.formatMessage({id: 'submitDate'}),
width: isMdOrLg ? 'auto' : 300,
flex: isMdOrLg ? 1 : undefined,
renderHeader: renderHeaderWithAria,
valueGetter:(params)=>{
return DateUtils.datetimeStr(params?.value);
}
@@ -57,6 +62,7 @@ export default function BaseGrid({setCount, url}) {
headerName: isORGLoggedIn()? intl.formatMessage({id: 'gazetteCount2_1'}) : intl.formatMessage({id: 'myRemarks'}),
width: isMdOrLg ? 'auto' : 400,
flex: isMdOrLg ? 3 : undefined,
renderHeader: renderHeaderWithAria,
renderCell: (params) => (
isORGLoggedIn()?
isDummyLoggedIn()?
@@ -88,14 +94,16 @@ export default function BaseGrid({setCount, url}) {
headerName: intl.formatMessage({id: 'status'}),
width: isMdOrLg ? 'auto' : 160,
flex: isMdOrLg ? 1 : undefined,
renderHeader: renderHeaderWithAria,
renderCell: (params) => {
return [getStatusIntl(params,intl)]
return getStatusIntl(params, intl);
},
},
{
field: 'actions',
headerName: '',
width: 160,
renderHeader: renderHeaderWithAria,
cellClassName: 'actions',
renderCell: (params) => {
return <Button aria-label={intl.formatMessage({id: 'viewDetail'})} onClick={handleDetailClick(params)}>


+ 40
- 17
src/pages/PublicNotice/ListPanel/PendingPaymentTab.js ファイルの表示

@@ -81,6 +81,10 @@ export default function SubmittedTab({ setCount, url }) {
navigate('/publicNotice/' + params.id);
};

const renderHeaderWithAria = (params) => (
<span aria-label={params.colDef.headerName}>{params.colDef.headerName}</span>
);

const handlePaymentBtn = () => {
let appIdList = [];
let paymentCheckList = [];
@@ -175,6 +179,7 @@ export default function SubmittedTab({ setCount, url }) {
headerName: intl.formatMessage({ id: 'applicationId' }),
width: isMdOrLg ? 'auto' : 160,
flex: isMdOrLg ? 1 : undefined,
renderHeader: renderHeaderWithAria,
},
{
id: 'created',
@@ -182,6 +187,7 @@ export default function SubmittedTab({ setCount, url }) {
headerName: intl.formatMessage({ id: 'submitDate' }),
width: isMdOrLg ? 'auto' : 160,
flex: isMdOrLg ? 1 : undefined,
renderHeader: renderHeaderWithAria,
valueGetter: (params) => {
return DateUtils.datetimeStr(params.value);
}
@@ -192,6 +198,7 @@ export default function SubmittedTab({ setCount, url }) {
headerName: isORGLoggedIn() ? intl.formatMessage({ id: 'gazetteCount2_1' }) : intl.formatMessage({ id: 'myRemarks' }),
width: isMdOrLg ? 'auto' : 400,
flex: isMdOrLg ? 3 : undefined,
renderHeader: renderHeaderWithAria,
renderCell: (params) => (
isORGLoggedIn() ?
isDummyLoggedIn()?
@@ -222,6 +229,7 @@ export default function SubmittedTab({ setCount, url }) {
headerName: intl.formatMessage({ id: 'price' }),
width: isMdOrLg ? 'auto' : 160,
flex: isMdOrLg ? 1 : undefined,
renderHeader: renderHeaderWithAria,
renderCell: (params) => {
return FormatUtils.currencyFormat(params.row.fee)
},
@@ -230,6 +238,7 @@ export default function SubmittedTab({ setCount, url }) {
id: 'paymentMethodAndDeadLine',
field: 'paymentMethodAndDeadLine',
headerName: intl.formatMessage({ id: 'paymentMethodAndDeadLine' }),
renderHeader: renderHeaderWithAria,
width: isMdOrLg ? 'auto' : 250,
flex: isMdOrLg ? 2 : undefined,
renderCell: (params) => (
@@ -287,8 +296,9 @@ export default function SubmittedTab({ setCount, url }) {
headerName: intl.formatMessage({ id: 'status' }),
width: isMdOrLg ? 'auto' : 160,
flex: isMdOrLg ? 1 : undefined,
renderHeader: renderHeaderWithAria,
renderCell: (params) => {
return [StatusUtils.getStatusIntl(params, intl)]
return StatusUtils.getStatusIntl(params, intl);
},
},
{
@@ -296,6 +306,7 @@ export default function SubmittedTab({ setCount, url }) {
type: 'actions',
headerName: '',
width: 150,
renderHeader: renderHeaderWithAria,
cellClassName: 'actions',
renderCell: (params) => {
return <Button aria-label={intl.formatMessage({ id: 'viewDetail' })} onClick={handleDetailClick(params)}>
@@ -313,23 +324,27 @@ export default function SubmittedTab({ setCount, url }) {
selectedRowItems.includes(row.id)
);
for (var i = 0; i < datas?.length; i++) {
content.push(<>
<Stack direction="row" justifyContent="space-between">
<Typography variant="h5">
<FormattedMessage id="applicationId" />: {datas[i].appNo}
</Typography>
({DateUtils.datetimeStr(datas[i].created)})
</Stack>
<FormattedMessage id="extraMark" />: {datas[i].remarks}
<br /><br />
</>);
content.push(
<React.Fragment key={datas[i].id}>
<Stack direction="row" justifyContent="space-between">
<Typography variant="h5">
<FormattedMessage id="applicationId" />: {datas[i].appNo}
</Typography>
({DateUtils.datetimeStr(datas[i].created)})
</Stack>
<FormattedMessage id="extraMark" />: {datas[i].remarks}
<br /><br />
</React.Fragment>
);

totalAmount += datas[i].fee;
}
content.push(<Typography variant="h5">
<FormattedMessage id="totalAmount" /> ($): {FormatUtils.currencyFormat(totalAmount)}
<br /><br />
</Typography>);
content.push(
<Typography key="payment-total" variant="h5">
<FormattedMessage id="totalAmount" /> ($): {FormatUtils.currencyFormat(totalAmount)}
<br /><br />
</Typography>
);
return content;
}

@@ -391,6 +406,11 @@ export default function SubmittedTab({ setCount, url }) {
id="careOfCombo"
value={selectedCareOf === null ? null : selectedCareOf}
options={careOfComboList}
getOptionLabel={(option) => {
if (option == null) return "";
if (typeof option === "string") return option;
return option.label != null ? String(option.label) : "";
}}
onChange={(event, newValue) => {
// console.log(newValue)
setSelectedCareOf(newValue);
@@ -400,7 +420,11 @@ export default function SubmittedTab({ setCount, url }) {
'& .MuiAutocomplete-endAdornment': { top: '50%', transform: 'translateY(-50%)' },
'& .MuiOutlinedInput-root': { height: 40 }
}}
renderInput={(params) => <TextField {...params} />}
renderInput={(params) => <TextField {...params} inputProps={{ ...params.inputProps, 'aria-label': intl.formatMessage({ id: 'careOf' }) }} />}
clearText={intl.formatMessage({ id: "muiClear" })}
closeText={intl.formatMessage({ id: "muiClose" })}
openText={intl.formatMessage({ id: "muiOpen" })}
noOptionsText={intl.formatMessage({ id: "muiNoOptions" })}
/>
</Grid>
</Grid> : null
@@ -427,7 +451,6 @@ export default function SubmittedTab({ setCount, url }) {

<ThemeProvider theme={PNSPS_BUTTON_THEME}>
<Button
color="create"
variant="contained"
aria-label={intl.formatMessage({ id: 'payOnlineBtn' })}
onClick={() => { handlePaymentBtn() }}


+ 16
- 4
src/pages/PublicNotice/ListPanel/SearchPublicNoticeForm.js ファイルの表示

@@ -138,7 +138,7 @@ const SearchPublicNoticeForm = ({ applySearch, searchCriteria, onGridReady }) =>
</Grid>

<Grid item xs={9} s={6} md={5} lg={3} sx={{ ml: 3, mr: 3, mb: 3 }}>
<LocalizationProvider dateAdapter={AdapterDayjs}>
<LocalizationProvider dateAdapter={AdapterDayjs} localeText={DateUtils.getPickerLocaleText(intl.locale)}>
<DemoItem components={['DatePicker']}>
<DatePicker
id="dateFrom"
@@ -166,7 +166,7 @@ const SearchPublicNoticeForm = ({ applySearch, searchCriteria, onGridReady }) =>
</Grid>
<Grid item xs={9} s={6} md={5} lg={3} sx={{ ml: 3, mr: 3, mb: 3 }}>
<LocalizationProvider dateAdapter={AdapterDayjs}>
<LocalizationProvider dateAdapter={AdapterDayjs} localeText={DateUtils.getPickerLocaleText(intl.locale)}>
<DemoItem components={['DatePicker']}>
<DatePicker
id="dateTo"
@@ -234,7 +234,11 @@ const SearchPublicNoticeForm = ({ applySearch, searchCriteria, onGridReady }) =>
}
value={status}
// inputValue={status?.labelCht}
getOptionLabel={(option) => intl.formatMessage({id: option.label})}
getOptionLabel={(option) => {
if (option == null || option.label == null) return "";
const s = intl.formatMessage({ id: option.label });
return typeof s === "string" ? s : String(s ?? "");
}}
onChange={(event, newValue) => {
if(newValue ==null){
setStatus(localStorage.getItem('userData').creditor?ComboData.publicNoticeStatic_Creditor[0]:ComboData.publicNoticeStatic[0]);
@@ -256,6 +260,10 @@ const SearchPublicNoticeForm = ({ applySearch, searchCriteria, onGridReady }) =>
}}
/>
)}
clearText={intl.formatMessage({ id: "muiClear" })}
closeText={intl.formatMessage({ id: "muiClose" })}
openText={intl.formatMessage({ id: "muiOpen" })}
noOptionsText={intl.formatMessage({ id: "muiNoOptions" })}
// InputLabelProps={{
// shrink: true
// }}
@@ -276,7 +284,11 @@ const SearchPublicNoticeForm = ({ applySearch, searchCriteria, onGridReady }) =>
}
value={status}
// inputValue={status?.labelCht}
getOptionLabel={(option) => intl.formatMessage({id: option.label})}
getOptionLabel={(option) => {
if (option == null || option.label == null) return "";
const s = intl.formatMessage({ id: option.label });
return typeof s === "string" ? s : String(s ?? "");
}}
onChange={(event, newValue) => {
console.log(newValue)
const findAllIndex = newValue.findIndex((ele) => {


+ 11
- 2
src/pages/PublicNotice/ListPanel/SearchPublicNoticeTable.js ファイルの表示

@@ -35,6 +35,10 @@ export default function SearchPublicNoticeTable({ searchCriteria, applyGridOnRea
navigate('/publicNotice/' + params.id);
};

const renderHeaderWithAria = (params) => (
<span aria-label={params.colDef.headerName}>{params.colDef.headerName}</span>
);

const columns = [
{
id: 'appNo',
@@ -42,8 +46,9 @@ export default function SearchPublicNoticeTable({ searchCriteria, applyGridOnRea
headerName: intl.formatMessage({ id: 'applicationId' }),
width: isMdOrLg ? 'auto' : 160,
flex: isMdOrLg ? 1 : undefined,
renderHeader: renderHeaderWithAria,
renderCell: (params) => {
return [params.row.appNo+getModeIntl(params,intl)]
return params.row.appNo + getModeIntl(params, intl);
},
},
{
@@ -52,6 +57,7 @@ export default function SearchPublicNoticeTable({ searchCriteria, applyGridOnRea
headerName: intl.formatMessage({ id: 'submitDate' }),
width: isMdOrLg ? 'auto' : 160,
flex: isMdOrLg ? 1 : undefined,
renderHeader: renderHeaderWithAria,
valueGetter: (params) => {
return DateUtils.datetimeStr(params?.value);
}
@@ -88,6 +94,7 @@ export default function SearchPublicNoticeTable({ searchCriteria, applyGridOnRea
headerName: isORGLoggedIn() ? intl.formatMessage({ id: 'gazetteCount2_1' }) : intl.formatMessage({ id: 'myRemarks' }),
width: isMdOrLg ? 'auto' : 400,
flex: isMdOrLg ? 3 : undefined,
renderHeader: renderHeaderWithAria,
renderCell: (params) => (
isORGLoggedIn() ?
isDummyLoggedIn()?
@@ -117,8 +124,9 @@ export default function SearchPublicNoticeTable({ searchCriteria, applyGridOnRea
field: 'status',
headerName: intl.formatMessage({ id: 'status' }),
width: 200,
renderHeader: renderHeaderWithAria,
renderCell: (params) => {
return [StatusUtils.getStatusIntl(params, intl)]
return StatusUtils.getStatusIntl(params, intl);
},
},
{
@@ -126,6 +134,7 @@ export default function SearchPublicNoticeTable({ searchCriteria, applyGridOnRea
type: 'actions',
headerName: '',
width: 150,
renderHeader: renderHeaderWithAria,
cellClassName: 'actions',
renderCell: (params) => {
return <Button onClick={handleDetailClick(params)}


+ 11
- 6
src/pages/PublicNotice/ListPanel/index.js ファイルの表示

@@ -31,11 +31,12 @@ import titleBackgroundImg from 'assets/images/dashboard/gazette-bar.png'
import { PNSPS_LONG_BUTTON_THEME } from "../../../themes/buttonConst";
import { ThemeProvider } from "@emotion/react";
import { FormattedMessage, useIntl } from "react-intl";


import usePageTitle from 'components/usePageTitle';
// ==============================|| DASHBOARD - DEFAULT ||============================== //

const PublicNotice = () => {
usePageTitle("myPublicNotice");
const [submittedCount, setSubmittedCount] = useState(0);
const [pendingPaymentCount, setPendingPaymentCount] = useState(0);
const [pendingPublishCount, setPendingPublishCount] = useState(0);
@@ -113,7 +114,7 @@ const PublicNotice = () => {
<Grid item xs={12}>
<div style={BackgroundHead}>
<Stack direction="row" height='70px' justifyContent="flex-start" alignItems="center">
<Typography ml={15} color='#FFF' variant="h4" sx={{ display: { xs: 'none', sm: 'none', md: 'block' } }}>
<Typography component="h1" ml={15} color='#FFF' variant="h4" sx={{ display: { xs: 'none', sm: 'none', md: 'block' } }}>
<FormattedMessage id="myPublicNotice" />
</Typography>
</Stack>
@@ -123,7 +124,11 @@ const PublicNotice = () => {
<Stack direction="row" justifyContent="flex-end" alignItems="center">
<ThemeProvider theme={PNSPS_LONG_BUTTON_THEME}>
<Box sx={{ mr: { md: "47px" } }}>
<Button aria-label={intl.formatMessage({ id: 'applyPublicNotice' })} variant="contained" onClick={() => { onBtnClick() }}>
<Button
aria-label={intl.formatMessage({ id: 'applyPublicNotice' })}
variant="contained"
onClick={() => { onBtnClick() }}
>
<FormattedMessage id="applyPublicNotice" />
</Button>
</Box>
@@ -137,7 +142,7 @@ const PublicNotice = () => {
<Grid item xs={12} sm={12} md={12} lg={12} sx={{ height: '100%', maxWidth: '100%', width: "-webkit-fill-available", backgroundColor: "#fff", mt: 3, mr: { xs: 1, md: 3 }, ml: { xs: 1, md: 3 }, mb: 3, ..._sx }}>
<TabContext value={selectedTab}>
<Box sx={{ borderBottom: 1, borderColor: 'divider', overflowX: 'auto', overflowY: 'auto' }}>
<TabList variant="scrollable" onChange={handleChange} aria-label="lab API tabs example" sx={{ display: 'flex', flexDirection: 'row' }}>
<TabList variant="scrollable" onChange={handleChange} aria-label={intl.formatMessage({ id: 'ariaRelatedRecords' })} sx={{ display: 'flex', flexDirection: 'row' }}>
<Tab aria-label={intl.formatMessage({ id: 'processing' })} label={intl.formatMessage({ id: 'processing' }) + " (" + submittedCount + ")"} value="1" />
<Tab aria-label={intl.formatMessage({ id: 'pendingPublish' })} label={intl.formatMessage({ id: 'pendingPublish' }) + " (" + pendingPublishCount + ")"} value="3" />
<Tab aria-label={intl.formatMessage({ id: 'pendingPayment' })} label={intl.formatMessage({ id: 'pendingPayment' }) + " (" + pendingPaymentCount + ")"} value="4" />
@@ -171,7 +176,7 @@ const PublicNotice = () => {
<Grid item xs={12} sx={{ minHeight: '80vh', height: "100%", maxHeight: '300vh', maxWidth: '95%', width: "-webkit-fill-available", backgroundColor: "#fff", mt: 3, mr: { xs: 1, md: 3 }, ml: { xs: 1, md: 3 }, mb: 3, ..._sx }}>
<TabContext value={selectedTab}>
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
<TabList variant="scrollable" onChange={handleChange} aria-label="lab API tabs example">
<TabList variant="scrollable" onChange={handleChange} aria-label={intl.formatMessage({ id: 'ariaApplicationGroup' })}>
<Tab aria-label={intl.formatMessage({ id: 'processing' })} label={intl.formatMessage({ id: 'processing' }) + " (" + submittedCount + ")"} value="1" />
<Tab aria-label={intl.formatMessage({ id: 'pendingPayment' })} label={intl.formatMessage({ id: 'pendingPayment' }) + " (" + pendingPaymentCount + ")"} value="3" />
<Tab aria-label={intl.formatMessage({ id: 'pendingPublish' })} label={intl.formatMessage({ id: 'pendingPublish' }) + " (" + pendingPublishCount + ")"} value="4" />


+ 14
- 3
src/pages/PublicNotice/Search_GLD/DataGrid.js ファイルの表示

@@ -53,6 +53,10 @@ export default function SearchPublicNoticeTable({ searchCriteria, applyGridOnRea
return groupNo
}

const renderHeaderWithAria = (params) => (
<span aria-label={params.colDef.headerName}>{params.colDef.headerName}</span>
);

const columns = [
{
field: 'actions',
@@ -60,6 +64,7 @@ export default function SearchPublicNoticeTable({ searchCriteria, applyGridOnRea
sortable: false,
width: 150,
cellClassName: 'actions',
renderHeader: renderHeaderWithAria,
renderCell: (params) => {
return clickableLink('/application/' + params.id, params.row.appNo);
},
@@ -70,8 +75,9 @@ export default function SearchPublicNoticeTable({ searchCriteria, applyGridOnRea
headerName: 'Mode',
sortable: false,
width: 100,
renderHeader: renderHeaderWithAria,
renderCell: (params) => {
return [StatusUtils.getModeEng(params)]
return StatusUtils.getModeEng(params);
},
},
{
@@ -80,8 +86,9 @@ export default function SearchPublicNoticeTable({ searchCriteria, applyGridOnRea
headerName: 'Status',
sortable: false,
width: 240,
renderHeader: renderHeaderWithAria,
renderCell: (params) => {
return [StatusUtils.getStatusEng(params)]
return StatusUtils.getStatusEng(params);
},
},
{
@@ -90,8 +97,9 @@ export default function SearchPublicNoticeTable({ searchCriteria, applyGridOnRea
headerName: 'With Proof',
sortable: false,
width: 120,
renderHeader: renderHeaderWithAria,
renderCell: (params) => {
return [params.row.proofId != null ? "Yes" : ""]
return params.row.proofId != null ? "Yes" : "";
},
},
{
@@ -101,6 +109,7 @@ export default function SearchPublicNoticeTable({ searchCriteria, applyGridOnRea
sortable: false,
flex: 1,
minWidth: 200,
renderHeader: renderHeaderWithAria,
valueGetter: (params) => {
return DateUtils.datetimeStr(params?.value);
}
@@ -112,6 +121,7 @@ export default function SearchPublicNoticeTable({ searchCriteria, applyGridOnRea
sortable: false,
minWidth: 250,
flex: 2,
renderHeader: renderHeaderWithAria,
renderCell: (params) => {
let company = params.row.enCompanyName != null ? params.row.enCompanyName : params.row.chCompanyName;
company = company != null ? company : "";
@@ -131,6 +141,7 @@ export default function SearchPublicNoticeTable({ searchCriteria, applyGridOnRea
sortable: false,
flex: 1.5,
minWidth: 350,
renderHeader: renderHeaderWithAria,
renderCell: (params) => (
<div>
{genIssueNo(params)}


+ 4
- 4
src/pages/PublicNotice/Search_GLD/SearchForm.js ファイルの表示

@@ -279,7 +279,7 @@ const SearchPublicNoticeForm = ({ applySearch, orgComboData, searchCriteria, iss
setSelectedStatus(newValue);
}
}}
getOptionLabel={(option) => option.label}
getOptionLabel={(option) => (option?.label != null ? String(option.label) : "")}
sx={{
'& .MuiInputBase-root': { alignItems: 'center' },
'& .MuiAutocomplete-endAdornment': { top: '50%', transform: 'translateY(-50%)' },
@@ -317,7 +317,7 @@ const SearchPublicNoticeForm = ({ applySearch, orgComboData, searchCriteria, iss
setSelectedLabelsString(selectedLabelsString);
}
}}
getOptionLabel={(option) => option.label}
getOptionLabel={(option) => (option?.label != null ? String(option.label) : "")}
renderInput={(params) => (
<TextField
{...params}
@@ -419,7 +419,7 @@ const SearchPublicNoticeForm = ({ applySearch, orgComboData, searchCriteria, iss
options={ComboData.groupTitle}
value={groupSelected}
inputValue={(groupSelected?.label) ? groupSelected?.label : ""}
getOptionLabel={(option) => option.label}
getOptionLabel={(option) => (option?.label != null ? String(option.label) : "")}
onChange={(event, newValue) => {
setGroupSelected(newValue);
}}
@@ -470,7 +470,7 @@ const SearchPublicNoticeForm = ({ applySearch, orgComboData, searchCriteria, iss
'& .MuiAutocomplete-endAdornment': { top: '50%', transform: 'translateY(-50%)' },
'& .MuiOutlinedInput-root': { height: 40 }
}}
getOptionLabel={(option) => option.label}
getOptionLabel={(option) => (option?.label != null ? String(option.label) : "")}
renderInput={(params) => (
<TextField
{...params}


+ 10
- 0
src/pages/PublicNotice/Search_Mark_As_Paid_GLD/DataGrid.js ファイルの表示

@@ -115,6 +115,10 @@ export default function SearchPublicNoticeTable({ searchCriteria, applyGridOnRea

};

const renderHeaderWithAria = (params) => (
<span aria-label={params.colDef.headerName}>{params.colDef.headerName}</span>
);

const columns = [
{
field: 'actions',
@@ -122,6 +126,7 @@ export default function SearchPublicNoticeTable({ searchCriteria, applyGridOnRea
sortable: false,
width: 150,
cellClassName: 'actions',
renderHeader: renderHeaderWithAria,
renderCell: (params) => {
return clickableLink('/application/' + params.id, params.row.appNo);
},
@@ -132,6 +137,7 @@ export default function SearchPublicNoticeTable({ searchCriteria, applyGridOnRea
headerName: 'Customer Name',
flex: 1,
minWidth: 50,
renderHeader: renderHeaderWithAria,
renderCell: (params) => {
let company = params.row.enCompanyName != null ? params.row.enCompanyName : params.row.chCompanyName;
company = company != null ? company : "";
@@ -150,6 +156,7 @@ export default function SearchPublicNoticeTable({ searchCriteria, applyGridOnRea
sortable: false,
flex: 1.5,
minWidth: 350,
renderHeader: renderHeaderWithAria,
renderCell: (params) => (
<div>
{genIssueNo(params)}
@@ -165,6 +172,7 @@ export default function SearchPublicNoticeTable({ searchCriteria, applyGridOnRea
sortable: false,
minWidth: 250,
flex: 2,
renderHeader: renderHeaderWithAria,
renderCell: (params) => {
let paymentMethod = params.row.paymentMethod!=null?intl.formatMessage({ id: utils.getPaymentMethod(params.row.paymentMethod)}):""
return (<>
@@ -178,6 +186,7 @@ export default function SearchPublicNoticeTable({ searchCriteria, applyGridOnRea
headerName: 'Amount($)',
flex: 1,
minWidth: 100,
renderHeader: renderHeaderWithAria,
valueGetter: (params) => {
return FormatUtils.currencyFormat(params?.value);
}
@@ -188,6 +197,7 @@ export default function SearchPublicNoticeTable({ searchCriteria, applyGridOnRea
headerName: 'Remarks',
flex: 2,
minWidth: 200,
renderHeader: renderHeaderWithAria,
renderCell: (params) => {
const handleBlur = (event) => {
const newValue = event.target.value;


+ 2
- 2
src/pages/PublicNotice/Search_Mark_As_Paid_GLD/SearchForm.js ファイルの表示

@@ -266,7 +266,7 @@ const SearchPublicNoticeForm = ({ applySearch, orgComboData, searchCriteria, iss
setSelectedStatus(newValue);
}
}}
getOptionLabel={(option) => option.label}
getOptionLabel={(option) => (option?.label != null ? String(option.label) : "")}
sx={{
'& .MuiInputBase-root': { alignItems: 'center' },
'& .MuiAutocomplete-endAdornment': { top: '50%', transform: 'translateY(-50%)' },
@@ -304,7 +304,7 @@ const SearchPublicNoticeForm = ({ applySearch, orgComboData, searchCriteria, iss
setSelectedLabelsString(selectedLabelsString);
}
}}
getOptionLabel={(option) => option.label}
getOptionLabel={(option) => (option?.label != null ? String(option.label) : "")}
renderInput={(params) => (
<TextField
{...params}


変更されたファイルが多すぎるため、一部のファイルは表示されません

読み込み中…
キャンセル
保存