| @@ -0,0 +1,15 @@ | |||||
| { | |||||
| // Use IntelliSense to learn about possible attributes. | |||||
| // Hover to view descriptions of existing attributes. | |||||
| // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 | |||||
| "version": "0.2.0", | |||||
| "configurations": [ | |||||
| { | |||||
| "type": "chrome", | |||||
| "request": "launch", | |||||
| "name": "Launch Chrome against localhost", | |||||
| "url": "http://localhost:3000", | |||||
| "webRoot": "${workspaceFolder}/src" | |||||
| } | |||||
| ] | |||||
| } | |||||
| @@ -22,7 +22,7 @@ | |||||
| "@testing-library/user-event": "^14.4.3", | "@testing-library/user-event": "^14.4.3", | ||||
| "@types/react-input-mask": "^3.0.2", | "@types/react-input-mask": "^3.0.2", | ||||
| "apexcharts": "^3.35.5", | "apexcharts": "^3.35.5", | ||||
| "axios": "^1.4.0", | |||||
| "axios": "^1.12.2", | |||||
| "date-fns": "^3.0.6", | "date-fns": "^3.0.6", | ||||
| "dayjs": "^1.11.9", | "dayjs": "^1.11.9", | ||||
| "formik": "^2.2.9", | "formik": "^2.2.9", | ||||
| @@ -94,6 +94,8 @@ | |||||
| "@babel/eslint-parser": "^7.21.3", | "@babel/eslint-parser": "^7.21.3", | ||||
| "@babel/plugin-proposal-private-property-in-object": "^7.21.11", | "@babel/plugin-proposal-private-property-in-object": "^7.21.11", | ||||
| "@mui/x-date-pickers": "^6.18.0", | "@mui/x-date-pickers": "^6.18.0", | ||||
| "ajv": "^6.12.6", | |||||
| "ajv-keywords": "^3.5.2", | |||||
| "eslint": "^8.38.0", | "eslint": "^8.38.0", | ||||
| "eslint-config-prettier": "^8.8.0", | "eslint-config-prettier": "^8.8.0", | ||||
| "eslint-config-react-app": "^7.0.1", | "eslint-config-react-app": "^7.0.1", | ||||
| @@ -104,5 +106,14 @@ | |||||
| "eslint-plugin-react": "^7.32.2", | "eslint-plugin-react": "^7.32.2", | ||||
| "eslint-plugin-react-hooks": "^4.6.0", | "eslint-plugin-react-hooks": "^4.6.0", | ||||
| "prettier": "^2.8.7" | "prettier": "^2.8.7" | ||||
| }, | |||||
| "overrides": { | |||||
| "schema-utils@3": { | |||||
| "ajv": "6.12.6", | |||||
| "ajv-keywords": "3.5.2" | |||||
| }, | |||||
| "@apideck/better-ajv-errors": { | |||||
| "ajv": "^8.12.0" | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| @@ -2,53 +2,24 @@ | |||||
| <html lang="en"> | <html lang="en"> | ||||
| <head> | <head> | ||||
| <meta charset="utf-8" /> | <meta charset="utf-8" /> | ||||
| <link rel="icon" href="%PUBLIC_URL%/favicon.svg" /> | |||||
| <link rel="icon" type="image/png" sizes="32x32" href="%PUBLIC_URL%/favicon-32x32.png"> | |||||
| <link rel="icon" type="image/png" sizes="16x16" href="%PUBLIC_URL%/favicon-16x16.png"> | |||||
| <link rel="mask-icon" href="%PUBLIC_URL%/safari-pinned-tab.svg" color="#5bbad5"> | |||||
| <meta name="viewport" content="width=device-width, initial-scale=1" /> | <meta name="viewport" content="width=device-width, initial-scale=1" /> | ||||
| <meta name="theme-color" content="#1f1f1f" /> | |||||
| <meta name="title" content="PNSPS" /> | |||||
| <meta name="theme-color" content="#ffffff" /> | |||||
| <meta name="msapplication-TileColor" content="#da532c"> | |||||
| <meta name="title" content="PNSPS - Public Notice Submission and Payment System" /> | |||||
| <meta | <meta | ||||
| name="description" | name="description" | ||||
| content="Mantis is a free, super flexible and customizable react redux dashboard template built using MUI React components with open source MIT license." | |||||
| content="Public Notice Submission and Payment System - Government Logistics Department" | |||||
| /> | /> | ||||
| <meta | <meta | ||||
| name="keywords" | name="keywords" | ||||
| content="react dashboard, react admin, react redux dashboard, ant design template, saas admin, free react dashboard" | |||||
| content="PNSPS, GLD, react redux dashboard, Gazette, Public Notice" | |||||
| /> | /> | ||||
| <meta name="author" content="CodedThemes" /> | |||||
| <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" /> | |||||
| <!-- Open Graph / Facebook --> | |||||
| <meta property="og:locale" content="en_US" /> | |||||
| <meta property="og:type" content="website" /> | |||||
| <meta property="og:url" content="https://mantisdashboard.io/" /> | |||||
| <meta property="og:site_name" content="mantisdashboard.io" /> | |||||
| <meta property="article:publisher" content="https://www.facebook.com/codedthemes" /> | |||||
| <meta property="og:title" content="PNSPS" /> | |||||
| <meta | |||||
| property="og:description" | |||||
| content="Mantis is a free, super flexible and customizable react redux dashboard template built using MUI React components with open source MIT license." | |||||
| /> | |||||
| <meta property="og:image" content="https://mantisdashboard.io/adv-banner-images/og-social.png" /> | |||||
| <!-- Twitter --> | |||||
| <meta property="twitter:card" content="summary_large_image" /> | |||||
| <meta property="twitter:url" content="https://mantisdashboard.io" /> | |||||
| <meta property="twitter:title" content="PNSPS" /> | |||||
| <meta | |||||
| property="twitter:description" | |||||
| content="Mantis is a free, super flexible and customizable react redux dashboard template built using MUI React components with open source MIT license." | |||||
| /> | |||||
| <meta property="twitter:image" content="https://mantisdashboard.io/adv-banner-images/og-social.png" /> | |||||
| <meta name="twitter:creator" content="@codedthemes" /> | |||||
| <!-- | |||||
| Notice the use of %PUBLIC_URL% in the tags above. | |||||
| It will be replaced with the URL of the `public` folder during the build. | |||||
| Only files inside the `public` folder can be referenced from the HTML. | |||||
| Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will | |||||
| work correctly both with client-side routing and a non-root public URL. | |||||
| Learn how to configure a non-root public URL by running `npm run build`. | |||||
| --> | |||||
| <title>PNSPS</title> | |||||
| <meta name="author" content="Government Logistics Department" /> | |||||
| <link rel="apple-touch-icon" href="%PUBLIC_URL%/apple-touch-icon.png" /> | |||||
| <title>PNSPS - Public Notice Submission and Payment System</title> | |||||
| <link rel="preconnect" href="https://fonts.gstatic.com" /> | <link rel="preconnect" href="https://fonts.gstatic.com" /> | ||||
| <link | <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" | 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" | ||||
| @@ -0,0 +1,54 @@ | |||||
| <?xml version="1.0" standalone="no"?> | |||||
| <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" | |||||
| "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"> | |||||
| <svg version="1.0" xmlns="http://www.w3.org/2000/svg" | |||||
| width="680.000000pt" height="680.000000pt" viewBox="0 0 680.000000 680.000000" | |||||
| preserveAspectRatio="xMidYMid meet"> | |||||
| <metadata> | |||||
| Created by potrace 1.14, written by Peter Selinger 2001-2017 | |||||
| </metadata> | |||||
| <g transform="translate(0.000000,680.000000) scale(0.100000,-0.100000)" | |||||
| fill="#000000" stroke="none"> | |||||
| <path d="M2270 4555 l0 -1345 912 2 913 3 0 340 0 340 -195 2 c-107 1 -322 2 | |||||
| -476 3 l-282 0 -1 928 c0 510 -1 960 -1 1000 l0 72 -435 0 -435 0 0 -1345z"/> | |||||
| <path d="M4627 5044 c-1 -1 -45 -5 -97 -8 -85 -5 -126 -9 -210 -20 -56 -8 | |||||
| -159 -28 -215 -43 -33 -8 -68 -17 -78 -19 -47 -9 -204 -72 -307 -124 -170 -85 | |||||
| -180 -91 -181 -120 0 -14 -1 -151 -2 -306 l-2 -280 280 0 280 1 0 112 0 112 | |||||
| 49 22 c86 37 195 69 286 84 19 3 46 7 60 10 21 4 175 17 205 17 24 1 212 -13 | |||||
| 235 -16 14 -2 43 -7 65 -10 48 -7 203 -48 264 -71 76 -28 203 -95 291 -153 99 | |||||
| -66 288 -250 345 -337 22 -33 43 -62 46 -65 9 -7 98 -173 100 -185 0 -5 9 -26 | |||||
| 19 -45 44 -86 102 -303 117 -435 13 -114 5 -428 -12 -454 -2 -4 -6 -26 -9 -49 | |||||
| -9 -79 -78 -288 -132 -401 -113 -237 -256 -417 -449 -561 -64 -47 -103 -70 | |||||
| -255 -144 -14 -7 -45 -18 -70 -25 -25 -8 -52 -15 -60 -18 -8 -2 -16 -4 -17 -4 | |||||
| -2 0 -16 -4 -33 -9 -87 -25 -340 -34 -472 -17 -32 4 -84 10 -115 13 -63 6 | |||||
| -116 13 -148 19 -11 2 -41 6 -67 10 l-47 8 -1 -260 c0 -200 2 -262 12 -270 7 | |||||
| -6 36 -16 63 -22 28 -7 57 -14 65 -17 186 -45 213 -49 420 -48 221 0 307 11 | |||||
| 517 64 100 25 340 113 353 129 3 3 31 19 63 35 32 16 97 55 145 87 300 198 | |||||
| 551 508 697 859 35 86 102 291 110 336 3 20 7 39 9 43 2 3 7 22 10 41 10 60 | |||||
| 16 94 20 115 28 130 28 570 1 700 -3 11 -7 36 -10 55 -19 133 -79 336 -137 | |||||
| 470 -79 181 -210 395 -312 508 -185 207 -395 366 -631 478 -103 49 -316 126 | |||||
| -381 138 -16 2 -40 7 -54 11 -24 5 -89 18 -155 30 -46 8 -155 19 -197 19 -21 | |||||
| 1 -38 3 -38 6 0 4 -229 8 -233 4z"/> | |||||
| <path d="M1958 5035 c-2 -1 -30 -5 -63 -8 -114 -11 -231 -31 -320 -54 -33 -8 | |||||
| -67 -17 -75 -18 -8 -2 -15 -4 -15 -5 0 -2 -14 -5 -53 -14 -12 -3 -27 -8 -32 | |||||
| -11 -5 -3 -18 -8 -27 -10 -122 -25 -529 -238 -603 -315 -3 -3 -30 -25 -60 -50 | |||||
| -184 -148 -367 -375 -487 -605 -77 -148 -171 -436 -188 -575 -4 -30 -9 -58 | |||||
| -11 -61 -17 -29 -26 -431 -11 -564 9 -86 20 -157 31 -205 3 -14 8 -36 11 -50 | |||||
| 8 -44 42 -167 49 -179 4 -6 9 -21 11 -33 15 -78 144 -348 212 -445 13 -17 23 | |||||
| -36 23 -41 0 -5 9 -17 20 -27 11 -10 20 -22 20 -26 0 -10 0 -10 96 -124 178 | |||||
| -213 410 -391 664 -512 181 -86 425 -159 595 -179 22 -2 54 -7 70 -10 17 -3 | |||||
| 109 -7 205 -9 200 -5 254 3 420 56 3 1 19 6 35 12 17 6 30 10 30 8 0 -1 45 21 | |||||
| 100 49 185 96 342 213 510 384 104 105 185 194 185 202 0 3 9 15 21 27 31 33 | |||||
| 126 159 179 237 8 12 20 29 27 38 8 13 11 -62 10 -317 -1 -210 2 -339 8 -347 | |||||
| 8 -10 285 -111 355 -129 17 -5 97 -33 120 -42 23 -9 33 -12 55 -15 20 -3 20 5 | |||||
| 20 947 l0 950 -910 0 -910 -1 -3 -277 -2 -277 287 -1 c159 0 372 -1 475 -2 | |||||
| 204 -1 198 1 146 -58 -13 -15 -36 -44 -53 -65 -133 -169 -383 -441 -491 -534 | |||||
| -282 -242 -401 -288 -684 -266 -78 6 -97 8 -167 20 -24 4 -44 7 -45 6 -2 -1 | |||||
| -16 3 -31 9 -15 6 -42 13 -60 16 -76 13 -364 140 -397 175 -3 3 -30 23 -60 45 | |||||
| -30 22 -60 44 -66 50 -82 75 -126 120 -159 161 -21 27 -42 51 -45 54 -3 3 -27 | |||||
| 38 -53 78 -79 118 -176 337 -201 452 -8 35 -25 120 -31 150 -26 135 -26 428 0 | |||||
| 550 3 14 8 36 10 50 24 152 133 412 226 544 16 22 37 52 47 65 45 65 157 182 | |||||
| 237 250 167 141 481 282 685 307 19 2 46 7 59 10 14 3 57 7 95 10 39 3 76 6 | |||||
| 81 8 6 2 10 102 10 272 l-1 269 -62 1 c-34 1 -63 0 -64 -1z"/> | |||||
| </g> | |||||
| </svg> | |||||
| @@ -2,24 +2,23 @@ | |||||
| import Routes from 'routes'; | import Routes from 'routes'; | ||||
| import ThemeCustomization from 'themes'; | import ThemeCustomization from 'themes'; | ||||
| import ScrollTop from 'components/ScrollTop'; | 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 'react-toastify/dist/ReactToastify.css'; | ||||
| import {PNSPS_THEME} from "./themes/themeConst"; | |||||
| import {ThemeProvider} from "@emotion/react"; | |||||
| //import {isUserLoggedIn} from 'utils/Utils'; | //import {isUserLoggedIn} from 'utils/Utils'; | ||||
| //import {DefaultRoute} from 'routes/index' | //import {DefaultRoute} from 'routes/index' | ||||
| // ==============================|| APP - THEME, ROUTER, LOCAL ||============================== // | // ==============================|| APP - THEME, ROUTER, LOCAL ||============================== // | ||||
| const App = () => ( | 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; | export default App; | ||||
| @@ -1,162 +1,378 @@ | |||||
| /* assets/style/navbarStyles.css */ | |||||
| /* ===== FIX typo ===== */ | |||||
| #nav{ | #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{ | #navbar div{ | ||||
| display: flex; | |||||
| align-items: center; | |||||
| justify-content: center; | |||||
| display: flex; | |||||
| align-items: center; | |||||
| justify-content: center; | |||||
| } | } | ||||
| #navbar div li{ | #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{ | #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{ | #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: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; | background: white; | ||||
| visibility: hidden; | visibility: hidden; | ||||
| opacity: 0; | opacity: 0; | ||||
| min-width: 18rem; | min-width: 18rem; | ||||
| position: absolute; | position: absolute; | ||||
| /* transition: all 0.5s ease; */ | |||||
| left: 0; | left: 0; | ||||
| display: none; | display: none; | ||||
| padding-left: 0px; | padding-left: 0px; | ||||
| padding-bottom: 7px; | padding-bottom: 7px; | ||||
| /* border: 1px solid #0C489E; */ | |||||
| background-clip: padding-box; | background-clip: padding-box; | ||||
| border: 1px solid rgba(0,0,0,.15); | border: 1px solid rgba(0,0,0,.15); | ||||
| border-radius: 0.25rem; | 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; | visibility: visible; | ||||
| opacity: 1; | 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{ | #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{ | #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{ | #sidebar{ | ||||
| font-size: 1.3rem; | |||||
| font-weight: 600; | |||||
| /* font-family: 微軟正黑體; */ | |||||
| font-size: 1.1rem; | |||||
| font-weight: 600; | |||||
| } | } | ||||
| #sidebartop{ | #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{ | #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{ | #sidebarbottom{ | ||||
| align-items: center; | |||||
| justify-content: center; | |||||
| padding: 0; | |||||
| display: flex; | |||||
| align-items: center; | |||||
| justify-content: center; | |||||
| padding: 0; | |||||
| display: flex; | |||||
| } | } | ||||
| #sidebar li{ | #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{ | #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{ | #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; | |||||
| } | |||||
| @@ -11,6 +11,10 @@ body, | |||||
| font-family: "Noto Sans HK", "Noto Sans SC"; | font-family: "Noto Sans HK", "Noto Sans SC"; | ||||
| } | } | ||||
| .page-grey { | |||||
| filter: grayscale(100%); | |||||
| } | |||||
| /* Chrome, Safari, Edge, Opera */ | /* Chrome, Safari, Edge, Opera */ | ||||
| input::-webkit-outer-spin-button, | input::-webkit-outer-spin-button, | ||||
| input::-webkit-inner-spin-button { | input::-webkit-inner-spin-button { | ||||
| @@ -48,26 +52,97 @@ img | |||||
| img:hover{-webkit-filter:none;} | 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; | 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 */ | |||||
| } | |||||
| @@ -13,6 +13,12 @@ export const windowCount = 'windowCount' | |||||
| import {useNavigate} from "react-router-dom"; | import {useNavigate} from "react-router-dom"; | ||||
| import {useDispatch} from "react-redux"; | import {useDispatch} from "react-redux"; | ||||
| import { REFRESH_TOKEN } from 'utils/ApiPathConst'; | 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 | // ** Handle User Login | ||||
| export const handleLogin = data => { | export const handleLogin = data => { | ||||
| @@ -32,6 +38,7 @@ export const handleLogin = data => { | |||||
| localStorage.setItem('accessToken', data.accessToken) | localStorage.setItem('accessToken', data.accessToken) | ||||
| localStorage.setItem('refreshToken', data.refreshToken) | localStorage.setItem('refreshToken', data.refreshToken) | ||||
| localStorage.setItem('axiosToken', "Bearer " + data.accessToken) | localStorage.setItem('axiosToken', "Bearer " + data.accessToken) | ||||
| localStorage.setItem('searchCriteria',"") | |||||
| //localStorage.setItem(config.storageUserRoleKeyName, JSON.stringify(data.role).slice(1).slice(0, -1)) | //localStorage.setItem(config.storageUserRoleKeyName, JSON.stringify(data.role).slice(1).slice(0, -1)) | ||||
| localStorage.setItem(refreshIntervalName, "60") | localStorage.setItem(refreshIntervalName, "60") | ||||
| // for demo only | // for demo only | ||||
| @@ -91,8 +98,10 @@ export const handleLogoutFunction = () => { | |||||
| localStorage.removeItem('refreshToken') | localStorage.removeItem('refreshToken') | ||||
| localStorage.removeItem('webtoken') | localStorage.removeItem('webtoken') | ||||
| localStorage.removeItem('transactionid') | localStorage.removeItem('transactionid') | ||||
| localStorage.removeItem('searchCriteria') | |||||
| //localStorage.removeItem(config.storageUserRoleKeyName) | //localStorage.removeItem(config.storageUserRoleKeyName) | ||||
| localStorage.removeItem('expiredAlertShown') | |||||
| localStorage.removeItem('expiredAlertShown'); | |||||
| expiredAlertShownInMemory = false; | |||||
| localStorage.removeItem(refreshIntervalName) | localStorage.removeItem(refreshIntervalName) | ||||
| localStorage.removeItem(windowCount) | localStorage.removeItem(windowCount) | ||||
| localStorage.removeItem(predictProductionQty) | localStorage.removeItem(predictProductionQty) | ||||
| @@ -107,7 +116,13 @@ export const SetupAxiosInterceptors = () => { | |||||
| const dispatch = useDispatch(); | const dispatch = useDispatch(); | ||||
| //const updateLastRequestTime = useContext(TimerContext); | //const updateLastRequestTime = useContext(TimerContext); | ||||
| let isRefreshToken= false; | 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( | axios.interceptors.request.use( | ||||
| config => { | config => { | ||||
| // ** Get token from localStorage | // ** Get token from localStorage | ||||
| @@ -154,17 +169,23 @@ export const SetupAxiosInterceptors = () => { | |||||
| } | } | ||||
| }) | }) | ||||
| .catch((refreshError) => { | .catch((refreshError) => { | ||||
| isRefreshToken = false; | |||||
| if (!expiredAlertShownInMemory && localStorage.getItem("expiredAlertShown") === null) { | |||||
| expiredAlertShownInMemory = true; | |||||
| localStorage.setItem("expiredAlertShown", "true"); | |||||
| alert(getMessage("autoLogout")); | |||||
| } | |||||
| dispatch(handleLogoutFunction()); | dispatch(handleLogoutFunction()); | ||||
| navigate('/login'); | navigate('/login'); | ||||
| isRefreshToken = false; | |||||
| window.location.reload(); | window.location.reload(); | ||||
| throw refreshError; | throw refreshError; | ||||
| }); | }); | ||||
| } else { | } 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")); | |||||
| } | } | ||||
| } | } | ||||
| @@ -225,7 +246,9 @@ export const handleRefreshTokenFunction = () => { | |||||
| }) | }) | ||||
| .catch((refreshError) => { | .catch((refreshError) => { | ||||
| console.log('Failed to refresh token'); | console.log('Failed to refresh token'); | ||||
| console.log(refreshError) | |||||
| if (refreshError != undefined){ | |||||
| console.log(refreshError) | |||||
| } | |||||
| // token = null | // token = null | ||||
| isRefresh = false; | isRefresh = false; | ||||
| }); | }); | ||||
| @@ -14,25 +14,10 @@ export const paymentPath = window.location.href.match("localhost:3000") ? `${hos | |||||
| export const delBugMode = true; | export const delBugMode = true; | ||||
| /** | |||||
| * Testing: | |||||
| * Domain: apigw-isit.staging-eid.gov.hk | |||||
| * URL: hk.gov.iamsmart.testapp:// | |||||
| * | |||||
| * Production | |||||
| * Domain: apigw.iamsmart.gov.hk | |||||
| * URL: hk.gov.iamsmart:// | |||||
| */ | |||||
| export const iAmSmartPath = `https://apigw-isit.staging-eid.gov.hk`; | |||||
| export const iAmSmartAppPath = `hk.gov.iamsmart.testapp://`; | |||||
| export const clientId = "cf61fa7c121e4869966f69c8694b1cd2"; | |||||
| export const iAmSmartCallbackPath = () => { | export const iAmSmartCallbackPath = () => { | ||||
| let hostname = window.location.hostname; | let hostname = window.location.hostname; | ||||
| if (hostname.match("pnspsuat")) { | if (hostname.match("pnspsuat")) { | ||||
| hostname = "pnspsuat.gld.gov.hk"; | hostname = "pnspsuat.gld.gov.hk"; | ||||
| } else { | |||||
| hostname = "pnspsdev.gld.gov.hk"; | |||||
| } | } | ||||
| return hostname; | return hostname; | ||||
| }; | }; | ||||
| @@ -57,7 +42,10 @@ export const getBowserType = () => { | |||||
| if (navigator.userAgent.match(/Android/i)) return "Android_Chrome" | if (navigator.userAgent.match(/Android/i)) return "Android_Chrome" | ||||
| if (navigator.userAgent.match(/iPhone|iPad|iPod/i)) return "iOS_Chrome" | if (navigator.userAgent.match(/iPhone|iPad|iPod/i)) return "iOS_Chrome" | ||||
| return "PC_Browser" | return "PC_Browser" | ||||
| } else if (navigator.userAgent.indexOf("Safari") != -1) { | |||||
| } else if (navigator.userAgent.indexOf('CriOS') != -1) { | |||||
| if (navigator.userAgent.match(/iPhone|iPad|iPod/i)) return "iOS_Chrome" | |||||
| return "PC_Browser" | |||||
| } else if (navigator.userAgent.indexOf("Safari") != -1 && navigator.userAgent.indexOf("Chrome") == -1) { | |||||
| if (navigator.userAgent.match(/iPhone|iPad|iPod/i)) return "iOS_Safari" | if (navigator.userAgent.match(/iPhone|iPad|iPod/i)) return "iOS_Safari" | ||||
| return "PC_Browser" | return "PC_Browser" | ||||
| } else if (navigator.userAgent.indexOf("Firefox") != -1) { | } else if (navigator.userAgent.indexOf("Firefox") != -1) { | ||||
| @@ -89,7 +77,7 @@ export const isGranted = (auth) => { | |||||
| const abilities = getUserData() ? getUserData()["abilities"] : null; | const abilities = getUserData() ? getUserData()["abilities"] : null; | ||||
| if (abilities == null || abilities.length == 0) return false; | if (abilities == null || abilities.length == 0) return false; | ||||
| if (!Array.isArray(auth)) return _checkAuth(abilities, auth); | if (!Array.isArray(auth)) return _checkAuth(abilities, auth); | ||||
| if (auth.length > abilities.length) return false; | if (auth.length > abilities.length) return false; | ||||
| let haveAuth = true; | let haveAuth = true; | ||||
| for (let i = 0; i < auth.length; i++) { | for (let i = 0; i < auth.length; i++) { | ||||
| @@ -138,8 +126,51 @@ export const local = { en: "en-us", zh: "zh-hk", cn: "zh-cn" }; | |||||
| export const preferpaymentmethods = ['visa', 'mastercard', 'pps', 'creditcard', 'fps']; | export const preferpaymentmethods = ['visa', 'mastercard', 'pps', 'creditcard', 'fps']; | ||||
| export const getPaymentMethod = (paymentMethod) => { | export const getPaymentMethod = (paymentMethod) => { | ||||
| if(paymentMethod == "online") return 'payOnlineMethod'; | |||||
| if(paymentMethod == "demandNote") return 'payDnMethod'; | |||||
| if(paymentMethod == "office") return 'payNPGOMethod'; | |||||
| if (paymentMethod == "online") return 'payOnlineMethod'; | |||||
| if (paymentMethod == "demandNote") return 'payDnMethod'; | |||||
| if (paymentMethod == "office") return 'payNPGOMethod'; | |||||
| return "other"; | |||||
| } | |||||
| export const getPaymentMethodGLD = (paymentMethod) => { | |||||
| if (paymentMethod == "online") return 'Online'; | |||||
| if (paymentMethod == "demandNote") return 'Demand Note'; | |||||
| if (paymentMethod == "office") return 'NPGO Collection Office'; | |||||
| return "other"; | |||||
| } | |||||
| export const getSearchCriteria = (path) =>{ | |||||
| let searchCriteria = "" | |||||
| if (localStorage.getItem('searchCriteria')==""){ | |||||
| return searchCriteria | |||||
| } else if (Object.keys(localStorage.getItem('searchCriteria')).length>0){ | |||||
| searchCriteria = JSON.parse(localStorage.getItem("searchCriteria")) | |||||
| if (searchCriteria.path === path){ | |||||
| return searchCriteria.data | |||||
| } else { | |||||
| return "" | |||||
| } | |||||
| } | |||||
| } | |||||
| export const checkSearchCriteriaPath = (path) =>{ | |||||
| if(!path.startsWith("/application") | |||||
| || path === "/application/search"){ | |||||
| if(!path.startsWith("/user/")){ | |||||
| if(!path.startsWith("/publicNotice/")|| path === "/publicNotice"){ | |||||
| return true | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| export const getPaymentMethodByCode = (code) => { | |||||
| if (code === "01,PPSB,PPS") return "PPS"; | |||||
| if (code === "02,BCMP,CreditCard" || code === "03,BCMP,CreditCard") return "Credit Card"; | |||||
| if (code === "04,BCFP,FPS") return "FPS"; | |||||
| return "other"; | return "other"; | ||||
| }; | |||||
| export function setPageTitle(title) { | |||||
| document.title = `${title} - PNSPS | Government Logistics Department`; | |||||
| } | } | ||||
| @@ -11,25 +11,40 @@ import Logo from './AdminLogo'; | |||||
| import config from 'config'; | import config from 'config'; | ||||
| import { activeItem } from 'store/reducers/menu'; | import { activeItem } from 'store/reducers/menu'; | ||||
| import { Stack } from '@mui/material'; | import { Stack } from '@mui/material'; | ||||
| import { | |||||
| checkSysEnv | |||||
| } from "utils/Utils"; | |||||
| import {useIntl} from "react-intl"; | |||||
| // ==============================|| MAIN LOGO ||============================== // | // ==============================|| MAIN LOGO ||============================== // | ||||
| const LogoSection = ({ sx, to }) => { | const LogoSection = ({ sx, to }) => { | ||||
| const { defaultId } = useSelector((state) => state.menu); | const { defaultId } = useSelector((state) => state.menu); | ||||
| const dispatch = useDispatch(); | const dispatch = useDispatch(); | ||||
| const intl = useIntl(); | |||||
| return ( | 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 /> | <Logo /> | ||||
| </ButtonBase> | |||||
| <span id="systemTitle" >PNSPS</span> | |||||
| </Stack> | |||||
| <span style={{ color: checkSysEnv()!=''?'red':'#0C489E'}} id="systemTitle">PNSPS</span> | |||||
| </Stack> | |||||
| </ButtonBase> | |||||
| ); | ); | ||||
| }; | }; | ||||
| @@ -1,19 +1,23 @@ | |||||
| import React, { createContext, useState, useEffect } from 'react'; | |||||
| import React, { createContext, useState, useEffect, useRef } from 'react'; | |||||
| import {useNavigate} from "react-router-dom"; | import {useNavigate} from "react-router-dom"; | ||||
| import {useIdleTimer} from "react-idle-timer"; | import {useIdleTimer} from "react-idle-timer"; | ||||
| import { handleLogoutFunction } from 'auth/index'; | import { handleLogoutFunction } from 'auth/index'; | ||||
| import { useDispatch } from "react-redux"; | import { useDispatch } from "react-redux"; | ||||
| import { useIntl } from "react-intl"; | |||||
| import { | import { | ||||
| isUserLoggedIn, | isUserLoggedIn, | ||||
| isGLDLoggedIn, | |||||
| isGLDLoggedIn, | |||||
| isPasswordExpiry | |||||
| } from "utils/Utils"; | } from "utils/Utils"; | ||||
| const TimerContext = createContext(); | const TimerContext = createContext(); | ||||
| const AutoLogoutProvider = ({ children }) => { | const AutoLogoutProvider = ({ children }) => { | ||||
| const intl = useIntl(); | |||||
| const [lastRequestTime, setLastRequestTime] = useState(Date.now()); | const [lastRequestTime, setLastRequestTime] = useState(Date.now()); | ||||
| const navigate = useNavigate(); | const navigate = useNavigate(); | ||||
| const [logoutInterval, setLogoutInterval] = useState(1); | const [logoutInterval, setLogoutInterval] = useState(1); | ||||
| const idleLogoutTriggeredRef = useRef(false); | |||||
| // const [remainingInterval] = useState(5); | // const [remainingInterval] = useState(5); | ||||
| const [state, setState] = useState('Active'); | const [state, setState] = useState('Active'); | ||||
| const dispatch = useDispatch() | const dispatch = useDispatch() | ||||
| @@ -81,6 +85,9 @@ const AutoLogoutProvider = ({ children }) => { | |||||
| // console.log(logoutInterval) | // console.log(logoutInterval) | ||||
| const interval = setInterval(async () => { | const interval = setInterval(async () => { | ||||
| const currentTime = Date.now(); | const currentTime = Date.now(); | ||||
| if (isPasswordExpiry()){ | |||||
| navigate('/user/changePassword'); | |||||
| } | |||||
| // getRemainingTime(); | // getRemainingTime(); | ||||
| if(state !== "Active" && lastActiveTab){ | if(state !== "Active" && lastActiveTab){ | ||||
| const timeElapsed = currentTime - lastRequestTime; | const timeElapsed = currentTime - lastRequestTime; | ||||
| @@ -89,8 +96,13 @@ const AutoLogoutProvider = ({ children }) => { | |||||
| // console.log(remainingInterval * 60); | // console.log(remainingInterval * 60); | ||||
| // console.log(logoutInterval * 60 * 1000 - timeElapsed) | // console.log(logoutInterval * 60 * 1000 - timeElapsed) | ||||
| if (timeElapsed >= logoutInterval * 60 * 1000) { | 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()); | dispatch(handleLogoutFunction()); | ||||
| navigate('/login'); | navigate('/login'); | ||||
| window.location.reload(); | window.location.reload(); | ||||
| @@ -1,17 +1,20 @@ | |||||
| // material-ui | // material-ui | ||||
| import {useState, useEffect} from 'react'; | |||||
| import { useState, useEffect, useRef } from 'react'; | |||||
| import { Box } from "@mui/material"; | |||||
| import { | import { | ||||
| DataGrid, GridOverlay, | DataGrid, GridOverlay, | ||||
| } from "@mui/x-data-grid"; | } from "@mui/x-data-grid"; | ||||
| import * as HttpUtils from "utils/HttpUtils"; | import * as HttpUtils from "utils/HttpUtils"; | ||||
| import {FormattedMessage, useIntl} from "react-intl"; | |||||
| import {TablePagination, Typography} from '@mui/material'; | |||||
| import { FormattedMessage, useIntl } from "react-intl"; | |||||
| import { TablePagination, Typography } from '@mui/material'; | |||||
| import { getSearchCriteria, checkSearchCriteriaPath } from "auth/utils"; | |||||
| // ==============================|| EVENT TABLE ||============================== // | // ==============================|| EVENT TABLE ||============================== // | ||||
| export function FiDataGrid({ rows, columns, sx, autoHeight, | |||||
| hideFooterSelectedRowCount, rowModesModel, editMode, | |||||
| pageSizeOptions, filterItems, customPageSize, doLoad, ...props }) { | |||||
| export function FiDataGrid({ rows, columns, sx, autoHeight = true, | |||||
| hideFooterSelectedRowCount, rowModesModel, editMode, | |||||
| pageSizeOptions, filterItems, customPageSize, doLoad, applyGridOnReady, applySearch, | |||||
| tab, height, maxHeight, pagination = true, ...props }) { | |||||
| const intl = useIntl(); | const intl = useIntl(); | ||||
| const [_rows, set_rows] = useState([]); | const [_rows, set_rows] = useState([]); | ||||
| const [_doLoad, set_doLoad] = useState({}); | const [_doLoad, set_doLoad] = useState({}); | ||||
| @@ -20,13 +23,15 @@ export function FiDataGrid({ rows, columns, sx, autoHeight, | |||||
| const [_editMode, set_editMode] = useState("row"); | const [_editMode, set_editMode] = useState("row"); | ||||
| const [_pageSizeOptions, set_pageSizeOptions] = useState([10]); | const [_pageSizeOptions, set_pageSizeOptions] = useState([10]); | ||||
| const [_filterItems, set_filterItems] = useState([]); | const [_filterItems, set_filterItems] = useState([]); | ||||
| const [loading, setLoading] = useState(false); | |||||
| const [page, setPage] = useState(0); | const [page, setPage] = useState(0); | ||||
| const [pageSize, setPageSize] = useState(10); | const [pageSize, setPageSize] = useState(10); | ||||
| const [_autoHeight, set_autoHeight] = useState(true); | |||||
| // const [_autoHeight, set_autoHeight] = useState(true); | |||||
| const [myHideFooterSelectedRowCount, setMyHideFooterSelectedRowCount] = useState(true); | const [myHideFooterSelectedRowCount, setMyHideFooterSelectedRowCount] = useState(true); | ||||
| const [_sx, set_sx] = useState({ | const [_sx, set_sx] = useState({ | ||||
| padding: "4 2 4 2", | padding: "4 2 4 2", | ||||
| lineHeight: "normal", | |||||
| '& .MuiDataGrid-cell': { | '& .MuiDataGrid-cell': { | ||||
| borderTop: 1, | borderTop: 1, | ||||
| borderBottom: 1, | borderBottom: 1, | ||||
| @@ -36,18 +41,50 @@ export function FiDataGrid({ rows, columns, sx, autoHeight, | |||||
| border: 1, | border: 1, | ||||
| borderColor: "#EEE" | borderColor: "#EEE" | ||||
| }, | }, | ||||
| "& .MuiDataGrid-columnHeaderTitle": { | |||||
| whiteSpace: "normal", | |||||
| lineHeight: "normal" | |||||
| }, | |||||
| "& .MuiDataGrid-columnHeader": { | |||||
| // Forced to use important since overriding inline styles | |||||
| height: "unset !important" | |||||
| }, | |||||
| }); | }); | ||||
| const effectiveAutoHeight = autoHeight && !height && !maxHeight; | |||||
| const containerSx = { | |||||
| width: '100%', | |||||
| ...(height ? { height } : {}), | |||||
| ...(maxHeight ? { maxHeight, height: '100%' } : {}), | |||||
| overflow: 'hidden', | |||||
| }; | |||||
| const [rowCount, setRowCount] = useState(0); | const [rowCount, setRowCount] = useState(0); | ||||
| useEffect(() => { | |||||
| setPage(0); | |||||
| set_doLoad(doLoad); | |||||
| useEffect(() => { | |||||
| if (doLoad !== undefined && Object.keys(doLoad).length>0 ){ | |||||
| if(applySearch!=undefined){ | |||||
| if (Object.keys(getSearchCriteria(window.location.pathname)).length>0){ | |||||
| const localStorageSearchCriteria = getSearchCriteria(window.location.pathname) | |||||
| // console.log(localStorageSearchCriteria) | |||||
| if(localStorageSearchCriteria.start!=undefined){ | |||||
| // console.log(localStorageSearchCriteria) | |||||
| setPage(localStorageSearchCriteria.start/pageSize); | |||||
| } | |||||
| } | |||||
| }else{ | |||||
| setPage(0); | |||||
| setPageSize(parseInt(event.target.value, 10)); | |||||
| } | |||||
| set_doLoad(doLoad); | |||||
| setLoading(true) | |||||
| } | |||||
| }, [doLoad]); | }, [doLoad]); | ||||
| useEffect(()=>{ | |||||
| useEffect(() => { | |||||
| getDataList(); | getDataList(); | ||||
| },[_doLoad, page]); | |||||
| }, [_doLoad, page]); | |||||
| useEffect(() => { | useEffect(() => { | ||||
| @@ -70,18 +107,25 @@ export function FiDataGrid({ rows, columns, sx, autoHeight, | |||||
| if (pageSizeOptions) { | if (pageSizeOptions) { | ||||
| set_pageSizeOptions(pageSizeOptions) | set_pageSizeOptions(pageSizeOptions) | ||||
| } | } | ||||
| if(autoHeight !== undefined){ | |||||
| set_autoHeight(autoHeight) | |||||
| } | |||||
| if(editMode){ | |||||
| // if (autoHeight !== undefined) { | |||||
| // set_autoHeight(autoHeight) | |||||
| // } | |||||
| if (editMode) { | |||||
| set_editMode(editMode); | set_editMode(editMode); | ||||
| } | } | ||||
| if(filterItems){ | |||||
| if (filterItems) { | |||||
| set_filterItems(filterItems); | set_filterItems(filterItems); | ||||
| } | } | ||||
| if(customPageSize){ | |||||
| if (customPageSize) { | |||||
| setPageSize(customPageSize); | setPageSize(customPageSize); | ||||
| } | } | ||||
| // console.log(_doLoad) | |||||
| if (_doLoad !== undefined && Object.keys(_doLoad).length==0 ){ | |||||
| setLoading(false) | |||||
| if (applyGridOnReady !== undefined){ | |||||
| applyGridOnReady(false) | |||||
| } | |||||
| } | |||||
| }, [sx, hideFooterSelectedRowCount, rowModesModel, rows, columns, pageSizeOptions, autoHeight, editMode, filterItems, customPageSize]); | }, [sx, hideFooterSelectedRowCount, rowModesModel, rows, columns, pageSizeOptions, autoHeight, editMode, filterItems, customPageSize]); | ||||
| const handleChangePage = (event, newPage) => { | const handleChangePage = (event, newPage) => { | ||||
| @@ -95,65 +139,172 @@ export function FiDataGrid({ rows, columns, sx, autoHeight, | |||||
| function CustomNoRowsOverlay() { | function CustomNoRowsOverlay() { | ||||
| return ( | return ( | ||||
| <GridOverlay> | |||||
| <Typography variant="body1"> | |||||
| <GridOverlay | |||||
| sx={{ | |||||
| width: "100%", | |||||
| justifyContent: "flex-start", // align overlay to left | |||||
| pl: 2, // padding-left to match grid cells | |||||
| }} | |||||
| > | |||||
| <Typography variant="body1" sx={{ textAlign: "left", width: "100%" }}> | |||||
| <FormattedMessage id="noRecordFound" /> | <FormattedMessage id="noRecordFound" /> | ||||
| </Typography> | </Typography> | ||||
| </GridOverlay> | </GridOverlay> | ||||
| ); | ); | ||||
| } | } | ||||
| function getDataList() { | function getDataList() { | ||||
| if(_doLoad?.url == null) return; | |||||
| if(_doLoad.params == null) _doLoad.params = {}; | |||||
| _doLoad.params.start = page*pageSize; | |||||
| // console.log(Object.keys(_doLoad.params).length > 0) | |||||
| // console.log(Object.keys(_doLoad.params).length > 0) | |||||
| if (_doLoad?.url == null){ | |||||
| setLoading(false) | |||||
| return; | |||||
| } | |||||
| if (_doLoad.params == undefined) return; | |||||
| if (_doLoad.params.searchCriteria !== undefined) return; | |||||
| if (_doLoad.params == null) _doLoad.params = {}; | |||||
| _doLoad.params.start = page * pageSize; | |||||
| _doLoad.params.limit = pageSize; | _doLoad.params.limit = pageSize; | ||||
| if(checkSearchCriteriaPath(window.location.pathname)){ | |||||
| if(window.location.pathname === "/publicNotice"){ | |||||
| if (tab != undefined && tab ==="application"){ | |||||
| localStorage.setItem('searchCriteria', JSON.stringify({path:window.location.pathname,data:_doLoad.params})) | |||||
| } | |||||
| }else if (window.location.pathname != "/publicNotice"){ | |||||
| localStorage.setItem('searchCriteria', JSON.stringify({path:window.location.pathname,data:_doLoad.params})) | |||||
| } | |||||
| } | |||||
| HttpUtils.get({ | HttpUtils.get({ | ||||
| url: _doLoad.url, | url: _doLoad.url, | ||||
| params: _doLoad.params, | params: _doLoad.params, | ||||
| onSuccess: function (responseData) { | onSuccess: function (responseData) { | ||||
| set_rows(responseData?.records); | set_rows(responseData?.records); | ||||
| setRowCount(responseData?.count); | setRowCount(responseData?.count); | ||||
| if(_doLoad.callback != null){ | |||||
| if (_doLoad.callback != null) { | |||||
| _doLoad.callback(responseData); | _doLoad.callback(responseData); | ||||
| } | } | ||||
| setLoading(false) | |||||
| // console.log(applyGridOnReady) | |||||
| if (applyGridOnReady !== undefined){ | |||||
| applyGridOnReady(false) | |||||
| } | |||||
| }, | |||||
| onError: function (error){ | |||||
| console.log(error) | |||||
| setLoading(false) | |||||
| if (applyGridOnReady !== undefined){ | |||||
| applyGridOnReady(false) | |||||
| } | |||||
| } | } | ||||
| }); | }); | ||||
| } | } | ||||
| 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 ( | return ( | ||||
| <DataGrid | |||||
| {...props} | |||||
| rows={_rows} | |||||
| columns={_columns} | |||||
| paginationMode="server" | |||||
| disableColumnMenu | |||||
| rowModesModel={_rowModesModel} | |||||
| pageSizeOptions={_pageSizeOptions} | |||||
| editMode={_editMode} | |||||
| autoHeight={_autoHeight} | |||||
| hideFooterSelectedRowCount={myHideFooterSelectedRowCount} | |||||
| filterModel={{ items: _filterItems }} | |||||
| sx={_sx} | |||||
| components={{ | |||||
| noRowsOverlay: CustomNoRowsOverlay, | |||||
| Pagination: () => ( | |||||
| <TablePagination | |||||
| count={rowCount?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}` | |||||
| <Box sx={containerSx} ref={gridRootRef} role="table"> | |||||
| <DataGrid | |||||
| {...props} | |||||
| rows={_rows} | |||||
| rowCount={rowCount || 0} | |||||
| columns={_columns} | |||||
| disableColumnMenu | |||||
| shrinkWrap | |||||
| rowModesModel={_rowModesModel} | |||||
| pageSizeOptions={pagination ? _pageSizeOptions : []} | |||||
| editMode={_editMode} | |||||
| autoHeight={effectiveAutoHeight} | |||||
| hideFooterSelectedRowCount={myHideFooterSelectedRowCount} | |||||
| filterModel={{ items: _filterItems }} | |||||
| loading={loading} | |||||
| paginationMode={pagination ? "server" : undefined} | |||||
| sx={{ | |||||
| ..._sx, | |||||
| '& .MuiDataGrid-virtualScroller': { | |||||
| overflowY: height || maxHeight ? 'auto' : 'visible', | |||||
| overflowX: height || maxHeight ? 'auto' : 'visible', | |||||
| }, | |||||
| // 👇 completely hide the footer when pagination is off | |||||
| ...(pagination === false && { | |||||
| '& .MuiDataGrid-footerContainer': { | |||||
| display: 'none', | |||||
| }, | |||||
| }), | |||||
| }} | |||||
| components={{ | |||||
| NoRowsOverlay: CustomNoRowsOverlay, | |||||
| ...(pagination | |||||
| ? { | |||||
| 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" }) + ":"} | |||||
| getItemAriaLabel={(type) => { | |||||
| if (type === 'previous') { | |||||
| return intl.formatMessage({ id: 'paginationPrev' }); | |||||
| } | |||||
| if (type === 'next') { | |||||
| return intl.formatMessage({ id: 'paginationNext' }); | |||||
| } | |||||
| return ''; | |||||
| }} | |||||
| onPageChange={handleChangePage} | |||||
| onRowsPerPageChange={handleChangePageSize} | |||||
| /> | |||||
| ), | |||||
| } | } | ||||
| labelRowsPerPage={intl.formatMessage({ id: "rowsPerPage" }) + ":"} | |||||
| onPageChange={handleChangePage} | |||||
| onRowsPerPageChange={handleChangePageSize} | |||||
| /> | |||||
| ), | |||||
| }} | |||||
| /> | |||||
| : {}), | |||||
| }} | |||||
| /> | |||||
| </Box> | |||||
| ); | ); | ||||
| } | } | ||||
| @@ -19,6 +19,7 @@ export default function FileList({ refType, refId, allowDelete, sx, dateHideable | |||||
| const theme = useTheme(); | const theme = useTheme(); | ||||
| const isMdOrLg = useMediaQuery(theme.breakpoints.up('md')); | const isMdOrLg = useMediaQuery(theme.breakpoints.up('md')); | ||||
| const intl = useIntl(); | const intl = useIntl(); | ||||
| const [onDownload, setOnDownload] = React.useState(false); | |||||
| React.useEffect(() => { | React.useEffect(() => { | ||||
| loadData(); | loadData(); | ||||
| @@ -41,10 +42,17 @@ export default function FileList({ refType, refId, allowDelete, sx, dateHideable | |||||
| }; | }; | ||||
| const onDownloadClick = (fileId, skey, filename) => () => { | const onDownloadClick = (fileId, skey, filename) => () => { | ||||
| setOnDownload(true) | |||||
| HttpUtils.fileDownload({ | HttpUtils.fileDownload({ | ||||
| fileId: fileId, | fileId: fileId, | ||||
| skey: skey, | skey: skey, | ||||
| filename: filename, | filename: filename, | ||||
| onResponse:()=>{ | |||||
| setOnDownload(false) | |||||
| }, | |||||
| onError:()=>{ | |||||
| setOnDownload(false) | |||||
| } | |||||
| }); | }); | ||||
| }; | }; | ||||
| @@ -91,6 +99,7 @@ export default function FileList({ refType, refId, allowDelete, sx, dateHideable | |||||
| className="textPrimary" | className="textPrimary" | ||||
| onClick={onDownloadClick(params.id, params.row.skey, params.row.filename)} | onClick={onDownloadClick(params.id, params.row.skey, params.row.filename)} | ||||
| color="primary" | color="primary" | ||||
| disabled={onDownload} | |||||
| />] | />] | ||||
| }, | }, | ||||
| }, | }, | ||||
| @@ -139,6 +148,7 @@ export default function FileList({ refType, refId, allowDelete, sx, dateHideable | |||||
| className="textPrimary" | className="textPrimary" | ||||
| onClick={onDownloadClick(params.id, params.row.skey, params.row.filename)} | onClick={onDownloadClick(params.id, params.row.skey, params.row.filename)} | ||||
| color="primary" | color="primary" | ||||
| disabled={onDownload} | |||||
| />] | />] | ||||
| }, | }, | ||||
| }, | }, | ||||
| @@ -1,8 +1,8 @@ | |||||
| import { useState, useEffect, createContext } from 'react'; | |||||
| import { useState, useEffect, createContext, useMemo } from 'react'; | |||||
| import { IntlProvider } from 'react-intl'; | import { IntlProvider } from 'react-intl'; | ||||
| import enMessages from '../translations/en.json'; | |||||
| import cnMessages from '../translations/zh-CN.json'; | |||||
| import hkMessages from '../translations/zh-HK.json'; | |||||
| import enBase from '../translations/en.json'; | |||||
| import cnBase from '../translations/zh-CN.json'; | |||||
| import hkBase from '../translations/zh-HK.json'; | |||||
| import { GET_COMBO, GET_CONTENT } from "utils/ApiPathConst"; | import { GET_COMBO, GET_CONTENT } from "utils/ApiPathConst"; | ||||
| import { get } from "utils/HttpUtils"; | import { get } from "utils/HttpUtils"; | ||||
| @@ -10,72 +10,106 @@ import { get } from "utils/HttpUtils"; | |||||
| const LocaleContext = createContext(); | const LocaleContext = createContext(); | ||||
| export const I18nProvider = ({ children }) => { | export const I18nProvider = ({ children }) => { | ||||
| const systemMessages = { | |||||
| "en": enMessages, | |||||
| "zh": hkMessages, | |||||
| "zh-HK": hkMessages, | |||||
| "zh-CN": cnMessages | |||||
| }; | |||||
| const [locale, setLocale] = useState('en'); | |||||
| // keep base messages immutable | |||||
| const [systemMessages, setSystemMessages] = useState({ | |||||
| en: { ...enBase }, | |||||
| zh: { ...hkBase }, | |||||
| 'zh-HK': { ...hkBase }, | |||||
| 'zh-CN': { ...cnBase } | |||||
| }); | |||||
| const [loaded, setLoaded] = useState(false); | |||||
| useEffect(() => { | |||||
| const saved = localStorage.getItem('locale'); | |||||
| if (!saved) localStorage.setItem('locale', 'en'); | |||||
| else setLocale(saved); | |||||
| }, []); | |||||
| const [locale, setLocale] = useState('en'); // Default locale, you can change this as per your requirement | |||||
| const [messages, setMessages] = useState(systemMessages[locale]); | |||||
| useEffect(() => { | |||||
| let alive = true; | |||||
| const loadTermsAndConditions = () => { | const loadTermsAndConditions = () => { | ||||
| // load both endpoints then merge into state | |||||
| const p1 = new Promise((resolve) => { | |||||
| get({ | get({ | ||||
| url: GET_CONTENT, | |||||
| onSuccess: (responseData) => { | |||||
| for (const key in responseData) { | |||||
| const value = responseData[key]; | |||||
| enMessages[key] = value.en??""; | |||||
| cnMessages[key] = value.cn??""; | |||||
| hkMessages[key] = value.zh??""; | |||||
| } | |||||
| } | |||||
| url: GET_CONTENT, | |||||
| onSuccess: (resp) => resolve(resp || {}), | |||||
| onError: () => resolve({}) | |||||
| }); | }); | ||||
| }); | |||||
| const p2 = new Promise((resolve) => { | |||||
| get({ | get({ | ||||
| url: GET_COMBO, | |||||
| onSuccess: (responseData) => { | |||||
| for (let i = 0; i < responseData.length; i++) { | |||||
| let item = responseData[i]; | |||||
| enMessages[item.key] = item.en; | |||||
| cnMessages[item.key] = item.cn; | |||||
| hkMessages[item.key] = item.zh; | |||||
| } | |||||
| } | |||||
| url: GET_COMBO, | |||||
| onSuccess: (resp) => resolve(resp || []), | |||||
| onError: () => resolve([]) | |||||
| }); | |||||
| }); | |||||
| Promise.all([p1, p2]).then(([contentMap, comboList]) => { | |||||
| if (!alive) return; | |||||
| setSystemMessages((prev) => { | |||||
| // clone prev first (immutably) | |||||
| const next = { | |||||
| ...prev, | |||||
| en: { ...prev.en }, | |||||
| 'zh-CN': { ...prev['zh-CN'] }, | |||||
| 'zh-HK': { ...prev['zh-HK'] }, | |||||
| zh: { ...prev.zh } | |||||
| }; | |||||
| // merge GET_CONTENT (object) | |||||
| for (const key in contentMap) { | |||||
| const v = contentMap[key] || {}; | |||||
| next.en[key] = v.en ?? ""; | |||||
| next['zh-CN'][key] = v.cn ?? ""; | |||||
| next['zh-HK'][key] = v.zh ?? ""; | |||||
| next.zh[key] = v.zh ?? ""; | |||||
| } | |||||
| // merge GET_COMBO (array) | |||||
| for (const item of comboList) { | |||||
| if (!item?.key) continue; | |||||
| next.en[item.key] = item.en ?? ""; | |||||
| next['zh-CN'][item.key] = item.cn ?? ""; | |||||
| next['zh-HK'][item.key] = item.zh ?? ""; | |||||
| next.zh[item.key] = item.zh ?? ""; | |||||
| } | |||||
| return next; | |||||
| }); | }); | ||||
| } | |||||
| useEffect(() => { | |||||
| loadTermsAndConditions(); | |||||
| if (localStorage.getItem('locale') === null) { | |||||
| //no locale case | |||||
| localStorage.setItem('locale', 'en'); | |||||
| } | |||||
| else { | |||||
| setLocale(localStorage.getItem('locale')); | |||||
| } | |||||
| }, []); | |||||
| useEffect(() => { | |||||
| // Load the messages for the selected locale | |||||
| const fetchMessages = async () => { | |||||
| setMessages(systemMessages[locale]); | |||||
| }; | |||||
| fetchMessages(); | |||||
| }, [locale]); | |||||
| return ( | |||||
| <LocaleContext.Provider value={{ locale, setLocale }} > | |||||
| <IntlProvider locale={locale} messages={messages}> | |||||
| {children} | |||||
| </IntlProvider> | |||||
| </LocaleContext.Provider> | |||||
| ); | |||||
| } | |||||
| export default LocaleContext; | |||||
| setLoaded(true); | |||||
| }); | |||||
| }; | |||||
| loadTermsAndConditions(); | |||||
| return () => { | |||||
| alive = false; | |||||
| }; | |||||
| }, []); | |||||
| const messages = useMemo(() => { | |||||
| return systemMessages[locale] || systemMessages.en; | |||||
| }, [systemMessages, locale]); | |||||
| return ( | |||||
| <LocaleContext.Provider value={{ locale, setLocale }}> | |||||
| <IntlProvider | |||||
| key={locale} | |||||
| locale={locale} | |||||
| messages={messages} | |||||
| defaultLocale="en" | |||||
| > | |||||
| {loaded ? children : <div />} | |||||
| </IntlProvider> | |||||
| </LocaleContext.Provider> | |||||
| ); | |||||
| }; | |||||
| export default LocaleContext; | |||||
| @@ -9,18 +9,22 @@ import { useDispatch, useSelector } from 'react-redux'; | |||||
| import Logo from './Logo'; | import Logo from './Logo'; | ||||
| import config from 'config'; | import config from 'config'; | ||||
| import { activeItem } from 'store/reducers/menu'; | import { activeItem } from 'store/reducers/menu'; | ||||
| import {useIntl} from "react-intl"; | |||||
| // ==============================|| MAIN LOGO ||============================== // | // ==============================|| MAIN LOGO ||============================== // | ||||
| const LogoSection = ({ sx, to }) => { | const LogoSection = ({ sx, to }) => { | ||||
| const { defaultId } = useSelector((state) => state.menu); | const { defaultId } = useSelector((state) => state.menu); | ||||
| const dispatch = useDispatch(); | const dispatch = useDispatch(); | ||||
| const intl = useIntl(); | |||||
| return ( | return ( | ||||
| <ButtonBase | <ButtonBase | ||||
| disableRipple | disableRipple | ||||
| component={Link} | component={Link} | ||||
| onClick={() => dispatch(activeItem({ openItem: [defaultId] }))} | onClick={() => dispatch(activeItem({ openItem: [defaultId] }))} | ||||
| to={!to ? config.defaultPath : to} | to={!to ? config.defaultPath : to} | ||||
| aria-label={intl.formatMessage({ id: "PNSPS", defaultMessage: "PNSPS" })} | |||||
| sx={sx} | sx={sx} | ||||
| > | > | ||||
| <Logo /> | <Logo /> | ||||
| @@ -9,22 +9,35 @@ import { useDispatch, useSelector } from 'react-redux'; | |||||
| import Logo from './MobileLogo'; | import Logo from './MobileLogo'; | ||||
| import config from 'config'; | import config from 'config'; | ||||
| import { activeItem } from 'store/reducers/menu'; | import { activeItem } from 'store/reducers/menu'; | ||||
| import {useIntl} from "react-intl"; | |||||
| // ==============================|| MAIN LOGO ||============================== // | // ==============================|| MAIN LOGO ||============================== // | ||||
| const LogoSection = ({ sx, to }) => { | const LogoSection = ({ sx, to }) => { | ||||
| const intl = useIntl(); | |||||
| const { defaultId } = useSelector((state) => state.menu); | const { defaultId } = useSelector((state) => state.menu); | ||||
| const dispatch = useDispatch(); | const dispatch = useDispatch(); | ||||
| return ( | return ( | ||||
| <ButtonBase | <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> | |||||
| ); | ); | ||||
| }; | }; | ||||
| @@ -35,7 +35,9 @@ const RefreshTokenProvider = ({ children }) => { | |||||
| }) | }) | ||||
| .catch((refreshError) => { | .catch((refreshError) => { | ||||
| console.log('Failed to refresh token'); | console.log('Failed to refresh token'); | ||||
| console.log(refreshError) | |||||
| if (refreshError != undefined){ | |||||
| console.log(refreshError) | |||||
| } | |||||
| token.current = null | token.current = null | ||||
| isRefresh.current = false; | isRefresh.current = false; | ||||
| }); | }); | ||||
| @@ -0,0 +1,36 @@ | |||||
| import { useState, useEffect, createContext } from 'react'; | |||||
| import { get } from "utils/HttpUtils" | |||||
| import {GET_SYS_SETTING} from "utils/ApiPathConst" | |||||
| const SysContext = createContext(); | |||||
| const SysSettingProvider = ({ children }) => { | |||||
| const [sysSetting, setSysSetting] = useState({}); | |||||
| useEffect(() => { | |||||
| loadSysSetting(); | |||||
| }, []); | |||||
| const loadSysSetting = () => { | |||||
| get({ | |||||
| url: GET_SYS_SETTING, | |||||
| onSuccess: (responseData) => { | |||||
| // console.log(responseData) | |||||
| setSysSetting(responseData); | |||||
| localStorage.setItem('sysEnv', responseData.sysEnv) | |||||
| localStorage.setItem('paymentSuspension', responseData.suspensionMode) | |||||
| } | |||||
| }); | |||||
| } | |||||
| return ( | |||||
| <SysContext.Provider value={{ sysSetting, setSysSetting }} > | |||||
| {children} | |||||
| </SysContext.Provider> | |||||
| ); | |||||
| } | |||||
| export {SysContext, SysSettingProvider}; | |||||
| @@ -2,6 +2,7 @@ | |||||
| import { useMediaQuery, Container, Link, Typography, Stack } from '@mui/material'; | import { useMediaQuery, Container, Link, Typography, Stack } from '@mui/material'; | ||||
| import bhkLogo from 'assets/images/BHK_logo_rgb_zh-hk.png'; | import bhkLogo from 'assets/images/BHK_logo_rgb_zh-hk.png'; | ||||
| import {FormattedMessage} from "react-intl"; | import {FormattedMessage} from "react-intl"; | ||||
| import {useIntl} from "react-intl"; | |||||
| import { | import { | ||||
| isGLDLoggedIn, | isGLDLoggedIn, | ||||
| } from "utils/Utils"; | } from "utils/Utils"; | ||||
| @@ -9,9 +10,14 @@ import { | |||||
| const AuthFooter = () => { | const AuthFooter = () => { | ||||
| const matchDownSM = useMediaQuery((theme) => theme.breakpoints.down('sm')); | const matchDownSM = useMediaQuery((theme) => theme.breakpoints.down('sm')); | ||||
| const intl = useIntl(); | |||||
| const bhkAlt = intl.formatMessage({ id: "bhkLogoAlt" }); | |||||
| const wcagAlt = intl.formatMessage({ id: "wcagAaAlt" }); | |||||
| return ( | return ( | ||||
| <Container maxWidth= "xl" sx={{minHeight: '5vh'}}> | |||||
| <Container maxWidth="xl" sx={{ minHeight: '5vh' }}> | |||||
| <Stack | <Stack | ||||
| direction={matchDownSM ? 'column' : 'row'} | direction={matchDownSM ? 'column' : 'row'} | ||||
| justifyContent={matchDownSM ? 'center' : 'flex-start'} | justifyContent={matchDownSM ? 'center' : 'flex-start'} | ||||
| @@ -19,14 +25,18 @@ const AuthFooter = () => { | |||||
| textAlign={matchDownSM ? 'center' : 'inherit'} | textAlign={matchDownSM ? 'center' : 'inherit'} | ||||
| alignItems="center" | alignItems="center" | ||||
| > | > | ||||
| <Typography variant="subtitle2" color="secondary" component="span"> | |||||
| <Typography | |||||
| variant="subtitle2" | |||||
| component="span" | |||||
| sx={{ color: '#4A4A4A' }} | |||||
| > | |||||
| 2024 © <FormattedMessage id="HKGLD" /> | 2024 © <FormattedMessage id="HKGLD" /> | ||||
| </Typography> | </Typography> | ||||
| <Typography | <Typography | ||||
| variant="subtitle2" | variant="subtitle2" | ||||
| color="secondary" | color="secondary" | ||||
| component={Link} | component={Link} | ||||
| // href="https://material-ui.com/store/contributors/codedthemes/" | |||||
| href="/importantNotice" | |||||
| target="_blank" | target="_blank" | ||||
| underline="hover" | underline="hover" | ||||
| > | > | ||||
| @@ -36,8 +46,7 @@ const AuthFooter = () => { | |||||
| variant="subtitle2" | variant="subtitle2" | ||||
| color="secondary" | color="secondary" | ||||
| component={Link} | component={Link} | ||||
| href="https://www.gld.gov.hk/zh-hk/privacy-policy/" | |||||
| //href="/testMailPage" | |||||
| href="/privacyPolicy" | |||||
| target="_blank" | target="_blank" | ||||
| underline="hover" | underline="hover" | ||||
| > | > | ||||
| @@ -45,22 +54,29 @@ const AuthFooter = () => { | |||||
| </Typography> | </Typography> | ||||
| </Stack> | </Stack> | ||||
| <Stack direction={matchDownSM ? 'column' : 'row'} spacing={matchDownSM ? 1 : 3} textAlign={matchDownSM ? 'center' : 'inherit'} justifyContent={matchDownSM?"center":"flex-end"}> | <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> | </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> | </Container> | ||||
| ); | ); | ||||
| }; | }; | ||||
| @@ -1,5 +1,6 @@ | |||||
| // material-ui | // material-ui | ||||
| import {useState, useEffect} from 'react'; | import {useState, useEffect} from 'react'; | ||||
| import { useIntl } from 'react-intl'; | |||||
| import iAmSmartICon from 'assets/images/icons/icon_iAmSmart.png'; | import iAmSmartICon from 'assets/images/icons/icon_iAmSmart.png'; | ||||
| import { | import { | ||||
| Button, | Button, | ||||
| @@ -9,7 +10,7 @@ import { | |||||
| // ==============================|| EVENT TABLE ||============================== // | // ==============================|| EVENT TABLE ||============================== // | ||||
| export function IAmSmartButton({ label, onClickFun, fullWidth }) { | export function IAmSmartButton({ label, onClickFun, fullWidth }) { | ||||
| const intl = useIntl(); | |||||
| const [_label, set_label] = useState(""); | const [_label, set_label] = useState(""); | ||||
| useEffect(()=>{ | useEffect(()=>{ | ||||
| @@ -23,7 +24,7 @@ export function IAmSmartButton({ label, onClickFun, fullWidth }) { | |||||
| } | } | ||||
| return ( | 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"> | <Typography variant="h5"> | ||||
| {_label} | {_label} | ||||
| </Typography> | </Typography> | ||||
| @@ -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]); | |||||
| } | |||||
| @@ -1,4 +1,4 @@ | |||||
| import { StrictMode } from 'react'; | |||||
| import { StrictMode, useEffect, useContext } from 'react'; | |||||
| import { createRoot } from 'react-dom/client'; | import { createRoot } from 'react-dom/client'; | ||||
| import { BrowserRouter } from 'react-router-dom'; | import { BrowserRouter } from 'react-router-dom'; | ||||
| import "./assets/style/styles.css" | import "./assets/style/styles.css" | ||||
| @@ -16,9 +16,34 @@ import 'assets/third-party/apex-chart.css'; | |||||
| import App from './App'; | import App from './App'; | ||||
| import { store } from 'store'; | import { store } from 'store'; | ||||
| import reportWebVitals from './reportWebVitals'; | import reportWebVitals from './reportWebVitals'; | ||||
| import {I18nProvider} from "components/I18nProvider"; | |||||
| import {AutoLogoutProvider} from "components/AutoLogoutProvider"; | |||||
| import {RefreshTokenProvider} from "components/RefreshTokenProvider"; | |||||
| import { I18nProvider } from "components/I18nProvider"; | |||||
| import { AutoLogoutProvider } from "components/AutoLogoutProvider"; | |||||
| import { RefreshTokenProvider } from "components/RefreshTokenProvider"; | |||||
| import { SysSettingProvider, SysContext } from 'components/SysSettingProvider'; | |||||
| import { useLocation } from 'react-router-dom'; | |||||
| function GreyWrapper({ children }) { | |||||
| const location = useLocation(); | |||||
| const { sysSetting } = useContext(SysContext); | |||||
| useEffect(() => { | |||||
| const isLoginPage = location.pathname === '/login'; | |||||
| const enableGrey = sysSetting?.greyLogin === true; | |||||
| if (isLoginPage && enableGrey) { | |||||
| document.body.classList.add('page-grey'); | |||||
| } else { | |||||
| document.body.classList.remove('page-grey'); | |||||
| } | |||||
| return () => { | |||||
| document.body.classList.remove('page-grey'); | |||||
| }; | |||||
| }, [location.pathname, sysSetting?.greyLogin]); | |||||
| return children; | |||||
| } | |||||
| // ==============================|| MAIN - REACT DOM RENDER ||============================== // | // ==============================|| MAIN - REACT DOM RENDER ||============================== // | ||||
| @@ -26,18 +51,23 @@ const container = document.getElementById('root'); | |||||
| const root = createRoot(container); // createRoot(container!) if you use TypeScript | const root = createRoot(container); // createRoot(container!) if you use TypeScript | ||||
| //const NotAuthorized = lazy(() => import('../views/NotAuthorized')) | //const NotAuthorized = lazy(() => import('../views/NotAuthorized')) | ||||
| //const Error = lazy(() => import('../views/Error')) | //const Error = lazy(() => import('../views/Error')) | ||||
| root.render( | root.render( | ||||
| <StrictMode> | <StrictMode> | ||||
| <ReduxProvider store={store}> | <ReduxProvider store={store}> | ||||
| <SysSettingProvider> | |||||
| <I18nProvider> | <I18nProvider> | ||||
| <BrowserRouter basename="/"> | <BrowserRouter basename="/"> | ||||
| <RefreshTokenProvider> | <RefreshTokenProvider> | ||||
| <AutoLogoutProvider> | <AutoLogoutProvider> | ||||
| <GreyWrapper> | |||||
| <App /> | <App /> | ||||
| </GreyWrapper> | |||||
| </AutoLogoutProvider> | </AutoLogoutProvider> | ||||
| </RefreshTokenProvider> | </RefreshTokenProvider> | ||||
| </BrowserRouter> | </BrowserRouter> | ||||
| </I18nProvider> | </I18nProvider> | ||||
| </SysSettingProvider> | |||||
| </ReduxProvider> | </ReduxProvider> | ||||
| </StrictMode> | </StrictMode> | ||||
| ); | ); | ||||
| @@ -4,6 +4,7 @@ import { useMemo } from 'react'; | |||||
| // material-ui | // material-ui | ||||
| import { useTheme } from '@mui/material/styles'; | import { useTheme } from '@mui/material/styles'; | ||||
| import { Box, Drawer, useMediaQuery } from '@mui/material'; | import { Box, Drawer, useMediaQuery } from '@mui/material'; | ||||
| import { useIntl } from 'react-intl'; | |||||
| // project import | // project import | ||||
| import DrawerHeader from './DrawerHeader'; | import DrawerHeader from './DrawerHeader'; | ||||
| @@ -15,6 +16,7 @@ import { drawerWidth } from 'config'; | |||||
| const MainDrawer = ({ open, handleDrawerToggle, window }) => { | const MainDrawer = ({ open, handleDrawerToggle, window }) => { | ||||
| const theme = useTheme(); | const theme = useTheme(); | ||||
| const intl = useIntl(); | |||||
| const matchDownMD = useMediaQuery(theme.breakpoints.down('lg')); | const matchDownMD = useMediaQuery(theme.breakpoints.down('lg')); | ||||
| // responsive drawer container | // responsive drawer container | ||||
| @@ -25,7 +27,7 @@ const MainDrawer = ({ open, handleDrawerToggle, window }) => { | |||||
| const drawerHeader = useMemo(() => <DrawerHeader open={open} />, [open]); | const drawerHeader = useMemo(() => <DrawerHeader open={open} />, [open]); | ||||
| return ( | 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 ? ( | {!matchDownMD ? ( | ||||
| <MiniDrawerStyled variant="permanent" open={open}> | <MiniDrawerStyled variant="permanent" open={open}> | ||||
| {drawerHeader} | {drawerHeader} | ||||
| @@ -16,7 +16,7 @@ import { | |||||
| import Transitions from 'components/@extended/Transitions'; | import Transitions from 'components/@extended/Transitions'; | ||||
| import LanguageIcon from '@mui/icons-material/Language'; | import LanguageIcon from '@mui/icons-material/Language'; | ||||
| import {FormattedMessage} from "react-intl"; | |||||
| import {FormattedMessage, useIntl} from "react-intl"; | |||||
| import * as React from "react"; | import * as React from "react"; | ||||
| import LocaleContext from "components/I18nProvider"; | import LocaleContext from "components/I18nProvider"; | ||||
| @@ -27,6 +27,8 @@ const LocaleSelector = () => { | |||||
| const matchesXs = useMediaQuery(theme.breakpoints.down('md')); | const matchesXs = useMediaQuery(theme.breakpoints.down('md')); | ||||
| const { setLocale } = useContext(LocaleContext); | const { setLocale } = useContext(LocaleContext); | ||||
| const intl = useIntl(); | |||||
| const anchorRef = useRef(null); | const anchorRef = useRef(null); | ||||
| const [open, setOpen] = useState(false); | const [open, setOpen] = useState(false); | ||||
| const handleToggle = () => { | const handleToggle = () => { | ||||
| @@ -47,16 +49,26 @@ const LocaleSelector = () => { | |||||
| return ( | return ( | ||||
| <Box sx={{ flexShrink: 0, ml: 0.75 }}> | <Box sx={{ flexShrink: 0, ml: 0.75 }}> | ||||
| <IconButton | <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> | </IconButton> | ||||
| <Popper | <Popper | ||||
| placement={matchesXs ? 'bottom' : 'bottom-end'} | placement={matchesXs ? 'bottom' : 'bottom-end'} | ||||
| @@ -96,8 +108,9 @@ const LocaleSelector = () => { | |||||
| <ListItem disablePadding> | <ListItem disablePadding> | ||||
| <ListItemButton | <ListItemButton | ||||
| onClick={() => { | onClick={() => { | ||||
| setLocale("en") | |||||
| setLocale("en"); | |||||
| localStorage.setItem('locale','en'); | localStorage.setItem('locale','en'); | ||||
| setOpen(false); | |||||
| }} | }} | ||||
| > | > | ||||
| <ListItemText | <ListItemText | ||||
| @@ -108,8 +121,9 @@ const LocaleSelector = () => { | |||||
| <ListItem disablePadding> | <ListItem disablePadding> | ||||
| <ListItemButton | <ListItemButton | ||||
| onClick={() => { | onClick={() => { | ||||
| setLocale("zh-HK") | |||||
| setLocale("zh-HK"); | |||||
| localStorage.setItem('locale','zh-HK'); | localStorage.setItem('locale','zh-HK'); | ||||
| setOpen(false); | |||||
| }} | }} | ||||
| > | > | ||||
| <ListItemText | <ListItemText | ||||
| @@ -120,8 +134,9 @@ const LocaleSelector = () => { | |||||
| <ListItem disablePadding> | <ListItem disablePadding> | ||||
| <ListItemButton | <ListItemButton | ||||
| onClick={() => { | onClick={() => { | ||||
| setLocale("zh-CN") | |||||
| setLocale("zh-CN"); | |||||
| localStorage.setItem('locale','zh-CN'); | localStorage.setItem('locale','zh-CN'); | ||||
| setOpen(false); | |||||
| }} | }} | ||||
| > | > | ||||
| <ListItemText | <ListItemText | ||||
| @@ -45,19 +45,26 @@ const MobileSection = () => { | |||||
| <> | <> | ||||
| <Box sx={{ flexShrink: 0, ml: 0.75 }}> | <Box sx={{ flexShrink: 0, ml: 0.75 }}> | ||||
| <IconButton | <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> | </Box> | ||||
| <Popper | <Popper | ||||
| placement="bottom-end" | placement="bottom-end" | ||||
| @@ -71,17 +71,27 @@ const Notification = () => { | |||||
| <IconButton | <IconButton | ||||
| disableRipple | disableRipple | ||||
| color="secondary" | 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} | ref={anchorRef} | ||||
| aria-controls={open ? 'profile-grow' : undefined} | aria-controls={open ? 'profile-grow' : undefined} | ||||
| aria-haspopup="true" | aria-haspopup="true" | ||||
| onClick={handleToggle} | onClick={handleToggle} | ||||
| > | > | ||||
| <Badge badgeContent={4} color="primary"> | |||||
| <BellOutlined /> | |||||
| </Badge> | |||||
| </IconButton> | |||||
| <Badge badgeContent={4} color="primary"> | |||||
| <BellOutlined /> | |||||
| </Badge> | |||||
| </IconButton> | |||||
| <Popper | <Popper | ||||
| placement={matchesXs ? 'bottom' : 'bottom-end'} | placement={matchesXs ? 'bottom' : 'bottom-end'} | ||||
| open={open} | open={open} | ||||
| @@ -33,6 +33,7 @@ import { LogoutOutlined, | |||||
| import { handleLogoutFunction } from 'auth/index'; | import { handleLogoutFunction } from 'auth/index'; | ||||
| import {useNavigate} from "react-router-dom"; | import {useNavigate} from "react-router-dom"; | ||||
| import {useDispatch} from "react-redux"; | import {useDispatch} from "react-redux"; | ||||
| import { useIntl } from 'react-intl'; | |||||
| import AccountCircleIcon from '@mui/icons-material/AccountCircle'; | import AccountCircleIcon from '@mui/icons-material/AccountCircle'; | ||||
| // tab panel wrapper | // tab panel wrapper | ||||
| @@ -61,6 +62,7 @@ TabPanel.propTypes = { | |||||
| const Profile = () => { | const Profile = () => { | ||||
| const theme = useTheme(); | const theme = useTheme(); | ||||
| const intl = useIntl(); | |||||
| const navigate = useNavigate() | const navigate = useNavigate() | ||||
| const dispatch = useDispatch() | const dispatch = useDispatch() | ||||
| @@ -101,7 +103,7 @@ const Profile = () => { | |||||
| borderRadius: 1, | borderRadius: 1, | ||||
| '&:hover': { bgcolor: 'secondary.lighter' } | '&:hover': { bgcolor: 'secondary.lighter' } | ||||
| }} | }} | ||||
| aria-label="open profile" | |||||
| aria-label={intl.formatMessage({id: 'openLanguage'})} | |||||
| ref={anchorRef} | ref={anchorRef} | ||||
| aria-controls={open ? 'profile-grow' : undefined} | aria-controls={open ? 'profile-grow' : undefined} | ||||
| aria-haspopup="true" | aria-haspopup="true" | ||||
| @@ -170,7 +172,7 @@ const Profile = () => { | |||||
| {/* {open && ( | {/* {open && ( | ||||
| <> | <> | ||||
| <Box sx={{ borderBottom: 1, borderColor: 'divider' }}> | <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 | <Tab | ||||
| sx={{ | sx={{ | ||||
| display: 'flex', | display: 'flex', | ||||
| @@ -1,6 +1,7 @@ | |||||
| import { useEffect, useState } from 'react'; | import { useEffect, useState } from 'react'; | ||||
| import { Outlet } from 'react-router-dom'; | import { Outlet } from 'react-router-dom'; | ||||
| import { useDispatch, useSelector } from 'react-redux'; | import { useDispatch, useSelector } from 'react-redux'; | ||||
| import { useLocation } from 'react-router-dom'; | |||||
| // material-ui | // material-ui | ||||
| import { useTheme } from '@mui/material/styles'; | import { useTheme } from '@mui/material/styles'; | ||||
| @@ -31,7 +32,8 @@ const MainLayout = () => { | |||||
| const theme = useTheme(); | const theme = useTheme(); | ||||
| const matchDownLG = useMediaQuery(theme.breakpoints.down('lg')); | const matchDownLG = useMediaQuery(theme.breakpoints.down('lg')); | ||||
| const dispatch = useDispatch(); | const dispatch = useDispatch(); | ||||
| const location = useLocation(); | |||||
| const hideNavbarRoutes = ['/databaseHealthCheck'] | |||||
| const { drawerOpen } = useSelector((state) => state.menu); | const { drawerOpen } = useSelector((state) => state.menu); | ||||
| // drawer toggler | // drawer toggler | ||||
| @@ -55,18 +57,26 @@ const MainLayout = () => { | |||||
| }, [drawerOpen]); | }, [drawerOpen]); | ||||
| return ( | return ( | ||||
| <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" }}> | |||||
| {/* <Toolbar /> */} | |||||
| {/* <Breadcrumbs navigation={navigation} title /> */} | |||||
| <Outlet /> | |||||
| </Box> | |||||
| <Box sx={{borderTop: "3px solid #0C489E"}}> | |||||
| <Footer/> | |||||
| </Box> | |||||
| </Box> | |||||
| <> | |||||
| {!hideNavbarRoutes.includes(location.pathname) && ( | |||||
| <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: "36px" }}> | |||||
| {/* <Toolbar /> */} | |||||
| {/* <Breadcrumbs navigation={navigation} title /> */} | |||||
| <Outlet /> | |||||
| </Box> | |||||
| <Box sx={{borderTop: "3px solid #0C489E"}}> | |||||
| <Footer/> | |||||
| </Box> | |||||
| </Box> | |||||
| )} | |||||
| {hideNavbarRoutes.includes(location.pathname) && ( | |||||
| <Outlet /> | |||||
| )} | |||||
| </> | |||||
| ); | ); | ||||
| }; | }; | ||||
| @@ -10,7 +10,7 @@ import { clickableLink } from 'utils/CommonFunction'; | |||||
| import {GET_ANNOUNCE_LIST} from "utils/ApiPathConst"; | import {GET_ANNOUNCE_LIST} from "utils/ApiPathConst"; | ||||
| // ==============================|| EVENT TABLE ||============================== // | // ==============================|| EVENT TABLE ||============================== // | ||||
| export default function SearchPublicNoticeTable({ searchCriteria }) { | |||||
| export default function SearchPublicNoticeTable({ searchCriteria, applyGridOnReady, applySearch}) { | |||||
| const navigate = useNavigate() | const navigate = useNavigate() | ||||
| @@ -73,10 +73,16 @@ export default function SearchPublicNoticeTable({ searchCriteria }) { | |||||
| customPageSize={10} | customPageSize={10} | ||||
| getRowHeight={() => 'auto'} | getRowHeight={() => 'auto'} | ||||
| onRowDoubleClick={handleRowDoubleClick} | onRowDoubleClick={handleRowDoubleClick} | ||||
| doLoad={{ | |||||
| applyGridOnReady={applyGridOnReady} | |||||
| applySearch = {applySearch} | |||||
| // doLoad={{ | |||||
| // url: GET_ANNOUNCE_LIST, | |||||
| // params: _searchCriteria, | |||||
| // }} | |||||
| doLoad={React.useMemo(() => ({ | |||||
| url: GET_ANNOUNCE_LIST, | url: GET_ANNOUNCE_LIST, | ||||
| params: _searchCriteria, | params: _searchCriteria, | ||||
| }} | |||||
| }), [_searchCriteria])} | |||||
| /> | /> | ||||
| </div> | </div> | ||||
| ); | ); | ||||
| @@ -17,8 +17,9 @@ import dayjs from "dayjs"; | |||||
| import {DemoItem} from "@mui/x-date-pickers/internals/demo"; | import {DemoItem} from "@mui/x-date-pickers/internals/demo"; | ||||
| import {LocalizationProvider} from "@mui/x-date-pickers/LocalizationProvider"; | import {LocalizationProvider} from "@mui/x-date-pickers/LocalizationProvider"; | ||||
| import {AdapterDayjs} from "@mui/x-date-pickers/AdapterDayjs"; | import {AdapterDayjs} from "@mui/x-date-pickers/AdapterDayjs"; | ||||
| // ==============================|| DASHBOARD - DEFAULT ||============================== // | // ==============================|| DASHBOARD - DEFAULT ||============================== // | ||||
| const SearchPublicNoticeForm = ({ applySearch, searchCriteria}) => { | |||||
| const SearchPublicNoticeForm = ({ applySearch, searchCriteria, onGridReady}) => { | |||||
| const navigate = useNavigate() | const navigate = useNavigate() | ||||
| const [minDate, setMinDate] = React.useState(searchCriteria.dateFrom); | const [minDate, setMinDate] = React.useState(searchCriteria.dateFrom); | ||||
| @@ -50,6 +51,8 @@ const SearchPublicNoticeForm = ({ applySearch, searchCriteria}) => { | |||||
| key: data.key, | key: data.key, | ||||
| dateFrom: sentDateFrom, | dateFrom: sentDateFrom, | ||||
| dateTo: sentDateTo, | dateTo: sentDateTo, | ||||
| start:0, | |||||
| limit:10 | |||||
| }; | }; | ||||
| applySearch(temp); | applySearch(temp); | ||||
| }; | }; | ||||
| @@ -58,7 +61,8 @@ const SearchPublicNoticeForm = ({ applySearch, searchCriteria}) => { | |||||
| function resetForm() { | function resetForm() { | ||||
| setMinDate(DateUtils.dateValue(new Date().setDate(new Date().getDate()-14))) | setMinDate(DateUtils.dateValue(new Date().setDate(new Date().getDate()-14))) | ||||
| setMaxDate(DateUtils.dateValue(new Date())) | setMaxDate(DateUtils.dateValue(new Date())) | ||||
| reset(); | |||||
| reset({key:""}); | |||||
| localStorage.setItem('searchCriteria',"") | |||||
| } | } | ||||
| @@ -180,6 +184,7 @@ const SearchPublicNoticeForm = ({ applySearch, searchCriteria}) => { | |||||
| <Button | <Button | ||||
| variant="contained" | variant="contained" | ||||
| type="submit" | type="submit" | ||||
| disabled={onGridReady} | |||||
| > | > | ||||
| Submit | Submit | ||||
| </Button> | </Button> | ||||
| @@ -7,6 +7,7 @@ import { | |||||
| import MainCard from "components/MainCard"; | import MainCard from "components/MainCard"; | ||||
| import * as React from "react"; | import * as React from "react"; | ||||
| import * as DateUtils from "utils/DateUtils"; | import * as DateUtils from "utils/DateUtils"; | ||||
| import { getSearchCriteria } from "auth/utils"; | |||||
| import Loadable from 'components/Loadable'; | import Loadable from 'components/Loadable'; | ||||
| const LoadingComponent = Loadable(React.lazy(() => import('pages/extra-pages/LoadingComponent'))); | const LoadingComponent = Loadable(React.lazy(() => import('pages/extra-pages/LoadingComponent'))); | ||||
| @@ -28,20 +29,37 @@ const BackgroundHead = { | |||||
| const UserSearchPage_Individual = () => { | const UserSearchPage_Individual = () => { | ||||
| const [searchCriteria, setSearchCriteria] = React.useState({ | |||||
| dateTo: DateUtils.dateValue(new Date()), | |||||
| dateFrom: DateUtils.dateValue(new Date().setDate(new Date().getDate() - 90)), | |||||
| }); | |||||
| const [searchCriteria, setSearchCriteria] = React.useState({}); | |||||
| const [onReady, setOnReady] = React.useState(false); | const [onReady, setOnReady] = React.useState(false); | ||||
| const [onGridReady, setGridOnReady] = React.useState(false); | |||||
| React.useEffect(() => { | |||||
| if (Object.keys(getSearchCriteria(window.location.pathname)).length>0){ | |||||
| setSearchCriteria(getSearchCriteria(window.location.pathname)) | |||||
| }else{ | |||||
| localStorage.setItem('searchCriteria',"") | |||||
| setSearchCriteria({ | |||||
| dateTo: DateUtils.dateValue(new Date()), | |||||
| dateFrom: DateUtils.dateValue(new Date().setDate(new Date().getDate()-14)), | |||||
| }) | |||||
| } | |||||
| }, []); | |||||
| React.useEffect(() => { | React.useEffect(() => { | ||||
| setOnReady(true); | setOnReady(true); | ||||
| }, [searchCriteria]); | }, [searchCriteria]); | ||||
| function applySearch(input) { | function applySearch(input) { | ||||
| setGridOnReady(true) | |||||
| setSearchCriteria(input); | setSearchCriteria(input); | ||||
| localStorage.setItem('searchCriteria', JSON.stringify({path:window.location.pathname,data:input})) | |||||
| } | |||||
| function applyGridOnReady(input) { | |||||
| setGridOnReady(input); | |||||
| } | } | ||||
| return ( | return ( | ||||
| !onReady ? | !onReady ? | ||||
| <Grid container sx={{ minHeight: '95vh', mb: 3 }} direction="column" justifyContent="center" alignItems="center"> | <Grid container sx={{ minHeight: '95vh', mb: 3 }} direction="column" justifyContent="center" alignItems="center"> | ||||
| @@ -63,6 +81,7 @@ const UserSearchPage_Individual = () => { | |||||
| <SearchForm | <SearchForm | ||||
| applySearch={applySearch} | applySearch={applySearch} | ||||
| searchCriteria={searchCriteria} | searchCriteria={searchCriteria} | ||||
| onGridReady={onGridReady} | |||||
| /> | /> | ||||
| </Grid> | </Grid> | ||||
| {/*row 2*/} | {/*row 2*/} | ||||
| @@ -73,7 +92,9 @@ const UserSearchPage_Individual = () => { | |||||
| sx={{ backgroundColor: '#fff' }} | sx={{ backgroundColor: '#fff' }} | ||||
| > | > | ||||
| <EventTable | <EventTable | ||||
| searchCriteria={searchCriteria} | |||||
| searchCriteria={searchCriteria} | |||||
| applyGridOnReady={applyGridOnReady} | |||||
| applySearch={applySearch} | |||||
| /> | /> | ||||
| </MainCard> | </MainCard> | ||||
| </Grid> | </Grid> | ||||
| @@ -6,7 +6,7 @@ import { FormattedMessage, useIntl } from "react-intl"; | |||||
| import {GET_ANNOUNCE_LIST} from "utils/ApiPathConst"; | import {GET_ANNOUNCE_LIST} from "utils/ApiPathConst"; | ||||
| // ==============================|| EVENT TABLE ||============================== // | // ==============================|| EVENT TABLE ||============================== // | ||||
| export default function SearchPublicNoticeTable({ searchCriteria }) { | |||||
| export default function SearchPublicNoticeTable({ searchCriteria, applyGridOnReady,applySearch }) { | |||||
| const intl = useIntl(); | const intl = useIntl(); | ||||
| const { locale } = intl; | const { locale } = intl; | ||||
| @@ -57,10 +57,16 @@ export default function SearchPublicNoticeTable({ searchCriteria }) { | |||||
| columns={columns} | columns={columns} | ||||
| customPageSize={10} | customPageSize={10} | ||||
| getRowHeight={() => 'auto'} | getRowHeight={() => 'auto'} | ||||
| doLoad={{ | |||||
| applyGridOnReady={applyGridOnReady} | |||||
| applySearch={applySearch} | |||||
| // doLoad={{ | |||||
| // url: GET_ANNOUNCE_LIST, | |||||
| // params: _searchCriteria | |||||
| // }} | |||||
| doLoad={React.useMemo(() => ({ | |||||
| url: GET_ANNOUNCE_LIST, | url: GET_ANNOUNCE_LIST, | ||||
| params: _searchCriteria | |||||
| }} | |||||
| params: _searchCriteria, | |||||
| }), [_searchCriteria])} | |||||
| /> | /> | ||||
| </div> | </div> | ||||
| ); | ); | ||||
| @@ -19,7 +19,7 @@ import {DemoItem} from "@mui/x-date-pickers/internals/demo"; | |||||
| import {LocalizationProvider} from "@mui/x-date-pickers/LocalizationProvider"; | import {LocalizationProvider} from "@mui/x-date-pickers/LocalizationProvider"; | ||||
| import {AdapterDayjs} from "@mui/x-date-pickers/AdapterDayjs"; | import {AdapterDayjs} from "@mui/x-date-pickers/AdapterDayjs"; | ||||
| // ==============================|| DASHBOARD - DEFAULT ||============================== // | // ==============================|| DASHBOARD - DEFAULT ||============================== // | ||||
| const SearchPublicNoticeForm = ({ applySearch, searchCriteria }) => { | |||||
| const SearchPublicNoticeForm = ({ applySearch, searchCriteria, onGridReady }) => { | |||||
| // const navigate = useNavigate() | // const navigate = useNavigate() | ||||
| const [minDate, setMinDate] = React.useState(searchCriteria.dateFrom); | const [minDate, setMinDate] = React.useState(searchCriteria.dateFrom); | ||||
| @@ -39,6 +39,22 @@ const SearchPublicNoticeForm = ({ applySearch, searchCriteria }) => { | |||||
| setToDateValue(maxDate); | setToDateValue(maxDate); | ||||
| }, [maxDate]); | }, [maxDate]); | ||||
| const _sx = { | |||||
| padding: "4 2 4 2", | |||||
| boxShadow: 1, | |||||
| border: 1, | |||||
| borderColor: '#DDD', | |||||
| '& .MuiDataGrid-cell': { | |||||
| borderTop: 1, | |||||
| borderBottom: 1, | |||||
| borderColor: "#EEE" | |||||
| }, | |||||
| '& .MuiDataGrid-footerContainer': { | |||||
| border: 1, | |||||
| borderColor: "#EEE" | |||||
| } | |||||
| } | |||||
| const marginBottom = 2.5; | const marginBottom = 2.5; | ||||
| const { reset, register, handleSubmit } = useForm() | const { reset, register, handleSubmit } = useForm() | ||||
| const onSubmit = (data) => { | const onSubmit = (data) => { | ||||
| @@ -52,6 +68,8 @@ const SearchPublicNoticeForm = ({ applySearch, searchCriteria }) => { | |||||
| key: data.key, | key: data.key, | ||||
| dateFrom: sentDateFrom, | dateFrom: sentDateFrom, | ||||
| dateTo: sentDateTo, | dateTo: sentDateTo, | ||||
| start:0, | |||||
| limit:10 | |||||
| }; | }; | ||||
| applySearch(temp); | applySearch(temp); | ||||
| }; | }; | ||||
| @@ -60,7 +78,8 @@ const SearchPublicNoticeForm = ({ applySearch, searchCriteria }) => { | |||||
| function resetForm() { | function resetForm() { | ||||
| setMinDate(DateUtils.dateValue(new Date().setDate(new Date().getDate()-14))) | setMinDate(DateUtils.dateValue(new Date().setDate(new Date().getDate()-14))) | ||||
| setMaxDate(DateUtils.dateValue(new Date())) | setMaxDate(DateUtils.dateValue(new Date())) | ||||
| reset(); | |||||
| reset({key:""}); | |||||
| localStorage.setItem('searchCriteria',"") | |||||
| } | } | ||||
| @@ -68,7 +87,7 @@ const SearchPublicNoticeForm = ({ applySearch, searchCriteria }) => { | |||||
| <MainCard xs={12} md={12} lg={12} | <MainCard xs={12} md={12} lg={12} | ||||
| border={false} | border={false} | ||||
| content={false} | content={false} | ||||
| sx={{ backgroundColor: '#fff' }} | |||||
| sx={_sx} | |||||
| > | > | ||||
| <form onSubmit={handleSubmit(onSubmit)}> | <form onSubmit={handleSubmit(onSubmit)}> | ||||
| @@ -97,7 +116,7 @@ const SearchPublicNoticeForm = ({ applySearch, searchCriteria }) => { | |||||
| <Grid item xs={12} s={6} md={6} lg={4} sx={{ ml: 3, mr: 3, mb: 3 }}> | <Grid item xs={12} s={6} md={6} lg={4} sx={{ ml: 3, mr: 3, mb: 3 }}> | ||||
| <Grid container> | <Grid container> | ||||
| <Grid item xs={5.25} s={5.25} md={5.25} lg={5.5}> | <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']}> | <DemoItem components={['DatePicker']}> | ||||
| <DatePicker | <DatePicker | ||||
| id="dateFrom" | id="dateFrom" | ||||
| @@ -129,7 +148,7 @@ const SearchPublicNoticeForm = ({ applySearch, searchCriteria }) => { | |||||
| </Grid> | </Grid> | ||||
| <Grid item xs={5.25} s={5.25} md={5.25} lg={5.5}> | <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']}> | <DemoItem components={['DatePicker']}> | ||||
| <DatePicker | <DatePicker | ||||
| id="dateTo" | id="dateTo" | ||||
| @@ -176,7 +195,9 @@ const SearchPublicNoticeForm = ({ applySearch, searchCriteria }) => { | |||||
| <Grid item sx={{ ml: 3 }}> | <Grid item sx={{ ml: 3 }}> | ||||
| <Button | <Button | ||||
| variant="contained" | variant="contained" | ||||
| color="cancel" | |||||
| onClick={resetForm} | onClick={resetForm} | ||||
| aria-label={intl.formatMessage({ id: 'reset' })} | |||||
| > | > | ||||
| <FormattedMessage id="reset"></FormattedMessage> | <FormattedMessage id="reset"></FormattedMessage> | ||||
| </Button> | </Button> | ||||
| @@ -186,6 +207,8 @@ const SearchPublicNoticeForm = ({ applySearch, searchCriteria }) => { | |||||
| <Button | <Button | ||||
| variant="contained" | variant="contained" | ||||
| type="submit" | type="submit" | ||||
| disabled={onGridReady} | |||||
| aria-label={intl.formatMessage({id: 'submit'})} | |||||
| > | > | ||||
| <FormattedMessage id="submit"></FormattedMessage> | <FormattedMessage id="submit"></FormattedMessage> | ||||
| </Button> | </Button> | ||||
| @@ -14,6 +14,8 @@ const SearchForm = Loadable(React.lazy(() => import('./SearchForm'))); | |||||
| const EventTable = Loadable(React.lazy(() => import('./DataGrid'))); | const EventTable = Loadable(React.lazy(() => import('./DataGrid'))); | ||||
| import titleBackgroundImg from 'assets/images/dashboard/gazette-bar.png' | import titleBackgroundImg from 'assets/images/dashboard/gazette-bar.png' | ||||
| import { FormattedMessage } from "react-intl"; | import { FormattedMessage } from "react-intl"; | ||||
| import { getSearchCriteria } from "auth/utils"; | |||||
| import usePageTitle from "components/usePageTitle"; | |||||
| const BackgroundHead = { | const BackgroundHead = { | ||||
| backgroundImage: `url(${titleBackgroundImg})`, | backgroundImage: `url(${titleBackgroundImg})`, | ||||
| @@ -28,20 +30,37 @@ const BackgroundHead = { | |||||
| // ==============================|| DASHBOARD - DEFAULT ||============================== // | // ==============================|| DASHBOARD - DEFAULT ||============================== // | ||||
| const UserSearchPage_Individual = () => { | const UserSearchPage_Individual = () => { | ||||
| const [searchCriteria, setSearchCriteria] = React.useState({ | |||||
| dateTo: DateUtils.dateValue(new Date()), | |||||
| dateFrom: DateUtils.dateValue(new Date().setDate(new Date().getDate() - 90)), | |||||
| }); | |||||
| usePageTitle("announcement"); | |||||
| const [searchCriteria, setSearchCriteria] = React.useState({}); | |||||
| const [onReady, setOnReady] = React.useState(false); | const [onReady, setOnReady] = React.useState(false); | ||||
| const [onGridReady, setGridOnReady] = React.useState(false); | |||||
| React.useEffect(() => { | |||||
| if (Object.keys(getSearchCriteria(window.location.pathname)).length>0){ | |||||
| setSearchCriteria(getSearchCriteria(window.location.pathname)) | |||||
| }else{ | |||||
| localStorage.setItem('searchCriteria',"") | |||||
| setSearchCriteria({ | |||||
| dateTo: DateUtils.dateValue(new Date()), | |||||
| dateFrom: DateUtils.dateValue(new Date().setDate(new Date().getDate()-14)), | |||||
| }) | |||||
| } | |||||
| }, []); | |||||
| React.useEffect(() => { | React.useEffect(() => { | ||||
| setOnReady(true); | |||||
| if(Object.keys(searchCriteria).length>0){ | |||||
| setOnReady(true); | |||||
| } | |||||
| }, [searchCriteria]); | }, [searchCriteria]); | ||||
| function applySearch(input) { | function applySearch(input) { | ||||
| setSearchCriteria(input); | setSearchCriteria(input); | ||||
| localStorage.setItem('searchCriteria', JSON.stringify({path:window.location.pathname,data:input})) | |||||
| } | |||||
| function applyGridOnReady(input) { | |||||
| setGridOnReady(input); | |||||
| } | } | ||||
| return ( | return ( | ||||
| @@ -56,7 +75,7 @@ const UserSearchPage_Individual = () => { | |||||
| <Grid item xs={12}> | <Grid item xs={12}> | ||||
| <div style={BackgroundHead}> | <div style={BackgroundHead}> | ||||
| <Stack direction="row" height='70px' justifyContent="flex-start" alignItems="center"> | <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> | </Stack> | ||||
| </div> | </div> | ||||
| </Grid> | </Grid> | ||||
| @@ -65,6 +84,7 @@ const UserSearchPage_Individual = () => { | |||||
| <SearchForm | <SearchForm | ||||
| applySearch={applySearch} | applySearch={applySearch} | ||||
| searchCriteria={searchCriteria} | searchCriteria={searchCriteria} | ||||
| onGridReady={onGridReady} | |||||
| /> | /> | ||||
| </Grid> | </Grid> | ||||
| {/*row 2*/} | {/*row 2*/} | ||||
| @@ -76,6 +96,8 @@ const UserSearchPage_Individual = () => { | |||||
| > | > | ||||
| <EventTable | <EventTable | ||||
| searchCriteria={searchCriteria} | searchCriteria={searchCriteria} | ||||
| applyGridOnReady={applyGridOnReady} | |||||
| applySearch={applySearch} | |||||
| /> | /> | ||||
| </MainCard> | </MainCard> | ||||
| </Grid> | </Grid> | ||||
| @@ -21,16 +21,18 @@ import {ThemeProvider} from "@emotion/react"; | |||||
| import * as DateUtils from "utils/DateUtils"; | import * as DateUtils from "utils/DateUtils"; | ||||
| import * as UrlUtils from "utils/ApiPathConst"; | import * as UrlUtils from "utils/ApiPathConst"; | ||||
| import * as HttpUtils from "utils/HttpUtils"; | import * as HttpUtils from "utils/HttpUtils"; | ||||
| import Loadable from 'components/Loadable'; | |||||
| const LoadingComponent = Loadable(React.lazy(() => import('pages/extra-pages/LoadingComponent'))); | |||||
| // import Loadable from 'components/Loadable'; | |||||
| // const LoadingComponent = Loadable(React.lazy(() => import('pages/extra-pages/LoadingComponent'))); | |||||
| import {DatePicker} from "@mui/x-date-pickers/DatePicker"; | import {DatePicker} from "@mui/x-date-pickers/DatePicker"; | ||||
| import dayjs from "dayjs"; | import dayjs from "dayjs"; | ||||
| import {DemoItem} from "@mui/x-date-pickers/internals/demo"; | import {DemoItem} from "@mui/x-date-pickers/internals/demo"; | ||||
| import {LocalizationProvider} from "@mui/x-date-pickers/LocalizationProvider"; | import {LocalizationProvider} from "@mui/x-date-pickers/LocalizationProvider"; | ||||
| import {AdapterDayjs} from "@mui/x-date-pickers/AdapterDayjs"; | import {AdapterDayjs} from "@mui/x-date-pickers/AdapterDayjs"; | ||||
| import { isGranted } from "auth/utils"; | |||||
| // ==============================|| DASHBOARD - DEFAULT ||============================== // | // ==============================|| DASHBOARD - DEFAULT ||============================== // | ||||
| const AuditLogSearchForm = ({ applySearch, searchCriteria}) => { | |||||
| const AuditLogSearchForm = ({ applySearch, searchCriteria, onGridReady}) => { | |||||
| // const navigate = useNavigate(); | // const navigate = useNavigate(); | ||||
| const [minDate, setMinDate] = React.useState(searchCriteria.modifiedFrom); | const [minDate, setMinDate] = React.useState(searchCriteria.modifiedFrom); | ||||
| @@ -64,6 +66,8 @@ const AuditLogSearchForm = ({ applySearch, searchCriteria}) => { | |||||
| username: data.userName, | username: data.userName, | ||||
| modifiedTo: sentDateTo, | modifiedTo: sentDateTo, | ||||
| modifiedFrom: sentDateFrom, | modifiedFrom: sentDateFrom, | ||||
| start:0, | |||||
| limit:10 | |||||
| }; | }; | ||||
| applySearch(temp); | applySearch(temp); | ||||
| }; | }; | ||||
| @@ -77,6 +81,7 @@ const AuditLogSearchForm = ({ applySearch, searchCriteria}) => { | |||||
| setOnDownload(true) | setOnDownload(true) | ||||
| HttpUtils.fileDownload({ | HttpUtils.fileDownload({ | ||||
| url: UrlUtils.AUDIT_LOG_EXPORT, | url: UrlUtils.AUDIT_LOG_EXPORT, | ||||
| params: searchCriteria, | |||||
| onResponse:()=>{ | onResponse:()=>{ | ||||
| setOnDownload(false) | setOnDownload(false) | ||||
| }, | }, | ||||
| @@ -185,18 +190,17 @@ const AuditLogSearchForm = ({ applySearch, searchCriteria}) => { | |||||
| <ThemeProvider theme={PNSPS_BUTTON_THEME}> | <ThemeProvider theme={PNSPS_BUTTON_THEME}> | ||||
| <Grid item xs={12} md={12}> | <Grid item xs={12} md={12}> | ||||
| <Grid container maxWidth justifyContent="flex-end"> | <Grid container maxWidth justifyContent="flex-end"> | ||||
| <Grid item sx={{ ml: 3, mr: 3, mb: 3,}}> | |||||
| {onDownload? | |||||
| <LoadingComponent disableText={true} alignItems="flex-start"/> | |||||
| : | |||||
| {isGranted("MAINTAIN_SETTING") ? | |||||
| <Grid item sx={{ ml: 3, mr: 3, mb: 3,}}> | |||||
| <Button | <Button | ||||
| variant="contained" | variant="contained" | ||||
| onClick={exportExcel} | onClick={exportExcel} | ||||
| disabled={onDownload} | |||||
| > | > | ||||
| Export | Export | ||||
| </Button> | </Button> | ||||
| } | |||||
| </Grid> | |||||
| </Grid> : null | |||||
| } | |||||
| <Grid item sx={{ ml: 3, mr: 3, mb: 3,}}> | <Grid item sx={{ ml: 3, mr: 3, mb: 3,}}> | ||||
| <Button | <Button | ||||
| variant="contained" | variant="contained" | ||||
| @@ -210,6 +214,7 @@ const AuditLogSearchForm = ({ applySearch, searchCriteria}) => { | |||||
| <Button | <Button | ||||
| variant="contained" | variant="contained" | ||||
| type="submit" | type="submit" | ||||
| disabled={onGridReady} | |||||
| > | > | ||||
| Search | Search | ||||
| </Button> | </Button> | ||||
| @@ -12,7 +12,7 @@ import { | |||||
| } from '@mui/material'; | } from '@mui/material'; | ||||
| // ==============================|| EVENT TABLE ||============================== // | // ==============================|| EVENT TABLE ||============================== // | ||||
| export default function AuditLogTable({searchCriteria}) { | |||||
| export default function AuditLogTable({searchCriteria, applyGridOnReady,applySearch}) { | |||||
| const [_searchCriteria, set_searchCriteria] = React.useState(searchCriteria); | const [_searchCriteria, set_searchCriteria] = React.useState(searchCriteria); | ||||
| useEffect(() => { | useEffect(() => { | ||||
| @@ -87,10 +87,16 @@ export default function AuditLogTable({searchCriteria}) { | |||||
| columns={columns} | columns={columns} | ||||
| customPageSize={10} | customPageSize={10} | ||||
| getRowHeight={() => 'auto'} | getRowHeight={() => 'auto'} | ||||
| doLoad={{ | |||||
| applyGridOnReady={applyGridOnReady} | |||||
| applySearch={applySearch} | |||||
| // doLoad={{ | |||||
| // url: GET_AUDIT_LOG_LIST, | |||||
| // params: _searchCriteria | |||||
| // }} | |||||
| doLoad={React.useMemo(() => ({ | |||||
| url: GET_AUDIT_LOG_LIST, | url: GET_AUDIT_LOG_LIST, | ||||
| params: _searchCriteria | |||||
| }} | |||||
| params: _searchCriteria, | |||||
| }), [_searchCriteria])} | |||||
| /> | /> | ||||
| </div> | </div> | ||||
| ); | ); | ||||
| @@ -8,6 +8,7 @@ import { | |||||
| import MainCard from "components/MainCard"; | import MainCard from "components/MainCard"; | ||||
| import { useEffect, useState } from "react"; | import { useEffect, useState } from "react"; | ||||
| import * as DateUtils from "utils/DateUtils"; | import * as DateUtils from "utils/DateUtils"; | ||||
| import * as React from "react"; | |||||
| import Loadable from 'components/Loadable'; | import Loadable from 'components/Loadable'; | ||||
| import { lazy } from 'react'; | import { lazy } from 'react'; | ||||
| @@ -33,15 +34,21 @@ const AuditLogPage = () => { | |||||
| modifiedFrom: DateUtils.dateValue(new Date().setDate(new Date().getDate()-14)), | modifiedFrom: DateUtils.dateValue(new Date().setDate(new Date().getDate()-14)), | ||||
| }); | }); | ||||
| const [onReady, setOnReady] = useState(false); | const [onReady, setOnReady] = useState(false); | ||||
| const [onGridReady, setGridOnReady] = React.useState(false); | |||||
| useEffect(() => { | useEffect(() => { | ||||
| setOnReady(true); | setOnReady(true); | ||||
| }, [searchCriteria]); | }, [searchCriteria]); | ||||
| function applySearch(input) { | function applySearch(input) { | ||||
| setGridOnReady(true) | |||||
| setSearchCriteria(input); | setSearchCriteria(input); | ||||
| } | } | ||||
| function applyGridOnReady(input) { | |||||
| setGridOnReady(input); | |||||
| } | |||||
| return ( | return ( | ||||
| !onReady ? | !onReady ? | ||||
| <Grid container sx={{ minHeight: '87vh', mb: 3 }} direction="column" justifyContent="center" alignItems="center"> | <Grid container sx={{ minHeight: '87vh', mb: 3 }} direction="column" justifyContent="center" alignItems="center"> | ||||
| @@ -64,7 +71,7 @@ const AuditLogPage = () => { | |||||
| <SearchForm | <SearchForm | ||||
| applySearch={applySearch} | applySearch={applySearch} | ||||
| searchCriteria={searchCriteria} | searchCriteria={searchCriteria} | ||||
| onGridReady={onGridReady} | |||||
| /> | /> | ||||
| </Grid> | </Grid> | ||||
| {/*row 2*/} | {/*row 2*/} | ||||
| @@ -75,6 +82,8 @@ const AuditLogPage = () => { | |||||
| > | > | ||||
| <EventTable | <EventTable | ||||
| searchCriteria={searchCriteria} | searchCriteria={searchCriteria} | ||||
| applyGridOnReady={applyGridOnReady} | |||||
| applySearch={applySearch} | |||||
| /> | /> | ||||
| </MainCard> | </MainCard> | ||||
| </Grid> | </Grid> | ||||
| @@ -13,7 +13,7 @@ import * as DateUtils from "utils/DateUtils"; | |||||
| import * as UrlUtils from "utils/ApiPathConst"; | import * as UrlUtils from "utils/ApiPathConst"; | ||||
| import * as HttpUtils from "utils/HttpUtils"; | import * as HttpUtils from "utils/HttpUtils"; | ||||
| import { useNavigate } from "react-router-dom"; | import { useNavigate } from "react-router-dom"; | ||||
| import { notifyDownloadSuccess } from 'utils/CommonFunction'; | |||||
| import { notifyActionError } from 'utils/CommonFunction'; | |||||
| import { PNSPS_BUTTON_THEME } from "../../../themes/buttonConst"; | import { PNSPS_BUTTON_THEME } from "../../../themes/buttonConst"; | ||||
| import { ThemeProvider } from "@emotion/react"; | import { ThemeProvider } from "@emotion/react"; | ||||
| import { useIntl } from "react-intl"; | import { useIntl } from "react-intl"; | ||||
| @@ -118,8 +118,11 @@ const SearchPublicNoticeForm = ({ applySearch, issueComboData, _paymentCount, _p | |||||
| params: { | params: { | ||||
| "dnIdList": dnIdList | "dnIdList": dnIdList | ||||
| }, | }, | ||||
| onSuccess: function () { | |||||
| notifyDownloadSuccess(); | |||||
| onResponse: function () { | |||||
| // 200: browser handles save; no success toast | |||||
| }, | |||||
| onError: function () { | |||||
| notifyActionError(intl.formatMessage({ id: 'downloadFailed' })); | |||||
| } | } | ||||
| }); | }); | ||||
| } | } | ||||
| @@ -136,6 +139,8 @@ const SearchPublicNoticeForm = ({ applySearch, issueComboData, _paymentCount, _p | |||||
| } | } | ||||
| const temp = { | const temp = { | ||||
| issueId: issueSelected.id, | issueId: issueSelected.id, | ||||
| start:0, | |||||
| limit:10 | |||||
| }; | }; | ||||
| applySearch(temp); | applySearch(temp); | ||||
| }; | }; | ||||
| @@ -171,6 +176,11 @@ const SearchPublicNoticeForm = ({ applySearch, issueComboData, _paymentCount, _p | |||||
| setIssueSelected(newValue); | setIssueSelected(newValue); | ||||
| } | } | ||||
| }} | }} | ||||
| sx={{ | |||||
| '& .MuiInputBase-root': { alignItems: 'center' }, | |||||
| '& .MuiAutocomplete-endAdornment': { top: '50%', transform: 'translateY(-50%)' }, | |||||
| '& .MuiOutlinedInput-root': { height: 40 } | |||||
| }} | |||||
| renderInput={(params) => ( | renderInput={(params) => ( | ||||
| <TextField {...params} | <TextField {...params} | ||||
| label="Gazette Issue" | label="Gazette Issue" | ||||
| @@ -179,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> | ||||
| <Grid item > | <Grid item > | ||||
| @@ -15,11 +15,12 @@ import * as StatusUtils from "utils/statusUtils/PublicNoteStatusUtils"; | |||||
| import * as HttpUtils from "utils/HttpUtils"; | import * as HttpUtils from "utils/HttpUtils"; | ||||
| import DownloadIcon from '@mui/icons-material/Download'; | import DownloadIcon from '@mui/icons-material/Download'; | ||||
| import { notifyDownloadSuccess } from 'utils/CommonFunction'; | |||||
| import { notifyActionError } from 'utils/CommonFunction'; | |||||
| import { useIntl } from 'react-intl'; | |||||
| // ==============================|| DASHBOARD - DEFAULT ||============================== // | // ==============================|| DASHBOARD - DEFAULT ||============================== // | ||||
| const ApplicationDetailCard = ({ data }) => { | const ApplicationDetailCard = ({ data }) => { | ||||
| const intl = useIntl(); | |||||
| const [appDetail, setAppDetails] = React.useState({}); | const [appDetail, setAppDetails] = React.useState({}); | ||||
| React.useEffect(() => { | React.useEffect(() => { | ||||
| @@ -33,8 +34,11 @@ const ApplicationDetailCard = ({ data }) => { | |||||
| fileId: appDetail.appFileId, | fileId: appDetail.appFileId, | ||||
| skey: appDetail.appSkey, | skey: appDetail.appSkey, | ||||
| filename: appDetail.appFilename, | filename: appDetail.appFilename, | ||||
| onResponse: function () {}, | |||||
| onError: function () { | |||||
| notifyActionError(intl.formatMessage({ id: 'downloadFailed' })); | |||||
| } | |||||
| }); | }); | ||||
| notifyDownloadSuccess(); | |||||
| }; | }; | ||||
| return ( | return ( | ||||
| @@ -143,7 +147,7 @@ const ApplicationDetailCard = ({ data }) => { | |||||
| </Grid> | </Grid> | ||||
| <Grid container direction="row" justifyContent="space-between" | <Grid container direction="row" justifyContent="space-between" | ||||
| alignItems="center"> | alignItems="center"> | ||||
| <Grid item xs={12} md={6} lg={6} mt={1}> | |||||
| <Grid item xs={12} md={6} lg={6} mt={1} mb={2}> | |||||
| <Grid container alignItems={"center"}> | <Grid container alignItems={"center"}> | ||||
| <Grid item xs={12} md={12} lg={12}> | <Grid item xs={12} md={12} lg={12}> | ||||
| <Grid container direction="row"> | <Grid container direction="row"> | ||||
| @@ -14,12 +14,13 @@ import Loadable from 'components/Loadable'; | |||||
| const MainCard = Loadable(React.lazy(() => import('components/MainCard'))); | const MainCard = Loadable(React.lazy(() => import('components/MainCard'))); | ||||
| import DownloadIcon from '@mui/icons-material/Download'; | import DownloadIcon from '@mui/icons-material/Download'; | ||||
| import { notifyDownloadSuccess } from 'utils/CommonFunction'; | |||||
| import { notifyActionError } from 'utils/CommonFunction'; | |||||
| import { useIntl } from 'react-intl'; | |||||
| // ==============================|| DASHBOARD - DEFAULT ||============================== // | // ==============================|| DASHBOARD - DEFAULT ||============================== // | ||||
| const DnDetailCard = ({ data }) => { | const DnDetailCard = ({ data }) => { | ||||
| const intl = useIntl(); | |||||
| const [dnData, setDnData] = React.useState({}); | const [dnData, setDnData] = React.useState({}); | ||||
| React.useEffect(() => { | React.useEffect(() => { | ||||
| @@ -33,8 +34,9 @@ const DnDetailCard = ({ data }) => { | |||||
| fileId: dnData.fileId, | fileId: dnData.fileId, | ||||
| skey: dnData.skey, | skey: dnData.skey, | ||||
| filename: dnData.filename, | filename: dnData.filename, | ||||
| onResponse: function () { | |||||
| notifyDownloadSuccess(); | |||||
| onResponse: function () {}, | |||||
| onError: function () { | |||||
| notifyActionError(intl.formatMessage({ id: 'downloadFailed' })); | |||||
| } | } | ||||
| }); | }); | ||||
| }; | }; | ||||
| @@ -77,9 +77,9 @@ export default function SearchPublicNoticeTable({ searchCriteria,}) { | |||||
| // let user = params.row.enCompanyName != null ? params.row.enCompanyName : params.row.chCompanyName; | // let user = params.row.enCompanyName != null ? params.row.enCompanyName : params.row.chCompanyName; | ||||
| let user = params.row.contactPerson; | let user = params.row.contactPerson; | ||||
| // user = user != null ? user : ""; | // user = user != null ? user : ""; | ||||
| if (params.row.sysType != null && params.row.sysType == "dummy"){ | |||||
| user = "Dummy - PD" | |||||
| } | |||||
| // if (params.row.sysType != null && params.row.sysType == "dummy"){ | |||||
| // user = "Dummy - PD" | |||||
| // } | |||||
| return <div> | return <div> | ||||
| {user} | {user} | ||||
| </div>; | </div>; | ||||
| @@ -95,7 +95,7 @@ export default function SearchPublicNoticeTable({ searchCriteria,}) { | |||||
| let company = params.row.enCompanyName != null ? params.row.enCompanyName : params.row.chCompanyName; | let company = params.row.enCompanyName != null ? params.row.enCompanyName : params.row.chCompanyName; | ||||
| company = company != null ? company : ""; | company = company != null ? company : ""; | ||||
| if (params.row.sysType != null && params.row.sysType == "dummy"){ | if (params.row.sysType != null && params.row.sysType == "dummy"){ | ||||
| company = params.row.contactPerson | |||||
| company = params.row.custName | |||||
| } | } | ||||
| return <div> | return <div> | ||||
| {company} | {company} | ||||
| @@ -109,7 +109,13 @@ export default function SearchPublicNoticeTable({ searchCriteria,}) { | |||||
| flex: 2, | flex: 2, | ||||
| minWidth: 200, | minWidth: 200, | ||||
| renderCell: (params) => { | renderCell: (params) => { | ||||
| return <>{(params?.value)}</>; | |||||
| let careOf = params.row.careOf | |||||
| // if (params.row.sysType != null && params.row.sysType == "dummy"){ | |||||
| // careOf = '' | |||||
| // } | |||||
| return <div> | |||||
| {careOf} | |||||
| </div>; | |||||
| } | } | ||||
| }, | }, | ||||
| { | { | ||||
| @@ -120,13 +126,13 @@ export default function SearchPublicNoticeTable({ searchCriteria,}) { | |||||
| minWidth: 100, | minWidth: 100, | ||||
| valueGetter: (params) => { | valueGetter: (params) => { | ||||
| let length = params.row.length | let length = params.row.length | ||||
| let colCount = params.row.colCount | |||||
| // let colCount = params.row.colCount | |||||
| let noOfPages = params.row.noOfPages | let noOfPages = params.row.noOfPages | ||||
| let dimension = 0 | let dimension = 0 | ||||
| if (noOfPages != null){ | if (noOfPages != null){ | ||||
| dimension = length*colCount*noOfPages | |||||
| dimension = length*noOfPages | |||||
| }else{ | }else{ | ||||
| dimension = length*colCount | |||||
| dimension = length | |||||
| } | } | ||||
| return dimension; | return dimension; | ||||
| } | } | ||||
| @@ -134,7 +140,7 @@ export default function SearchPublicNoticeTable({ searchCriteria,}) { | |||||
| { | { | ||||
| id: 'fee', | id: 'fee', | ||||
| field: 'fee', | field: 'fee', | ||||
| headerName: 'Amount(HK$)', | |||||
| headerName: 'Amount($)', | |||||
| flex: 1, | flex: 1, | ||||
| minWidth: 100, | minWidth: 100, | ||||
| valueGetter: (params) => { | valueGetter: (params) => { | ||||
| @@ -137,6 +137,8 @@ const SearchPublicNoticeForm = ({ applySearch, issueComboData }) => { | |||||
| } | } | ||||
| const temp = { | const temp = { | ||||
| issueId: issueSelected.id, | issueId: issueSelected.id, | ||||
| start:0, | |||||
| limit:10 | |||||
| }; | }; | ||||
| applySearch(temp); | applySearch(temp); | ||||
| }; | }; | ||||
| @@ -172,6 +174,11 @@ const SearchPublicNoticeForm = ({ applySearch, issueComboData }) => { | |||||
| setIssueSelected(newValue); | setIssueSelected(newValue); | ||||
| } | } | ||||
| }} | }} | ||||
| sx={{ | |||||
| '& .MuiInputBase-root': { alignItems: 'center' }, | |||||
| '& .MuiAutocomplete-endAdornment': { top: '50%', transform: 'translateY(-50%)' }, | |||||
| '& .MuiOutlinedInput-root': { height: 40 } | |||||
| }} | |||||
| renderInput={(params) => ( | renderInput={(params) => ( | ||||
| <TextField {...params} | <TextField {...params} | ||||
| label="Gazette Issue" | label="Gazette Issue" | ||||
| @@ -180,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> | </Grid> | ||||
| @@ -13,37 +13,42 @@ import * as FormatUtils from "utils/FormatUtils"; | |||||
| import * as StatusUtils from "utils/statusUtils/DnStatus"; | import * as StatusUtils from "utils/statusUtils/DnStatus"; | ||||
| import { useNavigate } from "react-router-dom"; | import { useNavigate } from "react-router-dom"; | ||||
| import { FiDataGrid } from "components/FiDataGrid"; | import { FiDataGrid } from "components/FiDataGrid"; | ||||
| import { notifyDownloadSuccess } from 'utils/CommonFunction'; | |||||
| import { notifyActionError } from 'utils/CommonFunction'; | |||||
| import { | import { | ||||
| DEMAND_NOTE_EXPORT, | DEMAND_NOTE_EXPORT, | ||||
| DEMAND_NOTE_SEND, | DEMAND_NOTE_SEND, | ||||
| DEMAND_NOTE_ATTACH, | DEMAND_NOTE_ATTACH, | ||||
| DEMAND_NOTE_MARK_PAID, | DEMAND_NOTE_MARK_PAID, | ||||
| DEMAND_NOTE_LIST_ALL | |||||
| DEMAND_NOTE_LIST_ALL, | |||||
| DEMAND_NOTE_REVOKE_PAID | |||||
| } from "utils/ApiPathConst"; | } from "utils/ApiPathConst"; | ||||
| import * as HttpUtils from "utils/HttpUtils"; | import * as HttpUtils from "utils/HttpUtils"; | ||||
| import { PNSPS_BUTTON_THEME } from "themes/buttonConst"; | import { PNSPS_BUTTON_THEME } from "themes/buttonConst"; | ||||
| import { ThemeProvider } from "@emotion/react"; | import { ThemeProvider } from "@emotion/react"; | ||||
| import { isGrantedAny } from "auth/utils"; | import { isGrantedAny } from "auth/utils"; | ||||
| import { useIntl } from "react-intl"; | |||||
| // ==============================|| EVENT TABLE ||============================== // | // ==============================|| EVENT TABLE ||============================== // | ||||
| export default function SearchDemandNote({ searchCriteria, applySearch }) { | |||||
| export default function SearchDemandNote({ applySearch, searchCriteria, applyGridOnReady }) { | |||||
| const intl = useIntl(); | |||||
| const [isConfirmPopUp, setConfirmPopUp] = useState(false); | const [isConfirmPopUp, setConfirmPopUp] = useState(false); | ||||
| const [isRevokePopUp, setRevokePopUp] = useState(false); | |||||
| const [isSendPopUp, setSendPopUp] = useState(false); | const [isSendPopUp, setSendPopUp] = useState(false); | ||||
| const [isErrorPopUp, setIsErrorPopUp] = useState(false); | const [isErrorPopUp, setIsErrorPopUp] = useState(false); | ||||
| const [selectonWarning, setSelectonWarning] = useState(false); | const [selectonWarning, setSelectonWarning] = useState(false); | ||||
| const [wait, setWait] = useState(false); | const [wait, setWait] = useState(false); | ||||
| const [reload, setReload] = useState(new Date()); | |||||
| const [rows, setRows] = useState([]); | const [rows, setRows] = useState([]); | ||||
| const [_searchCriteria, set_searchCriteria] = useState(searchCriteria); | |||||
| const [_searchCriteria, set_searchCriteria] = useState({}); | |||||
| const [selectedRowItems, setSelectedRowItems] = useState([]); | const [selectedRowItems, setSelectedRowItems] = useState([]); | ||||
| const navigate = useNavigate() | const navigate = useNavigate() | ||||
| const [onDownload, setOnDownload] = useState(false); | |||||
| useEffect(() => { | useEffect(() => { | ||||
| set_searchCriteria(searchCriteria); | set_searchCriteria(searchCriteria); | ||||
| }, [searchCriteria]); | }, [searchCriteria]); | ||||
| const handleDnClick = (params) => () => { | const handleDnClick = (params) => () => { | ||||
| navigate('/paymentPage/demandNote/details/' + params.id); | navigate('/paymentPage/demandNote/details/' + params.id); | ||||
| }; | }; | ||||
| @@ -76,17 +81,26 @@ export default function SearchDemandNote({ searchCriteria, applySearch }) { | |||||
| params: { | params: { | ||||
| dnIdList: idList | dnIdList: idList | ||||
| }, | }, | ||||
| onSuccess: function () { | |||||
| notifyDownloadSuccess(); | |||||
| onResponse: function () {}, | |||||
| onError: function () { | |||||
| notifyActionError(intl.formatMessage({ id: 'downloadFailed' })); | |||||
| } | } | ||||
| }); | }); | ||||
| } | } | ||||
| const onDownloadClick = (params) => () => { | const onDownloadClick = (params) => () => { | ||||
| setOnDownload(true) | |||||
| HttpUtils.fileDownload({ | HttpUtils.fileDownload({ | ||||
| fileId: params.row.fileId, | fileId: params.row.fileId, | ||||
| skey: params.row.skey, | skey: params.row.skey, | ||||
| filename: params.row.filename, | filename: params.row.filename, | ||||
| onResponse: () => { | |||||
| setOnDownload(false); | |||||
| }, | |||||
| onError: () => { | |||||
| setOnDownload(false); | |||||
| notifyActionError(intl.formatMessage({ id: 'downloadFailed' })); | |||||
| } | |||||
| }); | }); | ||||
| }; | }; | ||||
| @@ -109,7 +123,7 @@ export default function SearchDemandNote({ searchCriteria, applySearch }) { | |||||
| dnIdList: idList | dnIdList: idList | ||||
| }, | }, | ||||
| onSuccess: () => { | onSuccess: () => { | ||||
| if (reloadFun) reloadFun(); | |||||
| setReload(new Date()); | |||||
| } | } | ||||
| }); | }); | ||||
| @@ -133,13 +147,36 @@ export default function SearchDemandNote({ searchCriteria, applySearch }) { | |||||
| }, | }, | ||||
| files: [file], | files: [file], | ||||
| onSuccess() { | onSuccess() { | ||||
| setWait(false); | |||||
| if (reloadFun) reloadFun(); | |||||
| setReload(new Date()); | |||||
| }, | }, | ||||
| }); | }); | ||||
| document.getElementById("uploadFileBtn").value = ""; | document.getElementById("uploadFileBtn").value = ""; | ||||
| } | } | ||||
| const revokePaid = () => { | |||||
| setRevokePopUp(false); | |||||
| let idList = []; | |||||
| const datas = rows?.filter((row) => | |||||
| selectedRowItems.includes(row.id) | |||||
| ); | |||||
| if (datas?.length < 1) { | |||||
| setSelectonWarning(true); | |||||
| return; | |||||
| } | |||||
| for (var i = 0; i < datas?.length; i++) { | |||||
| idList.push(datas[i].id); | |||||
| } | |||||
| HttpUtils.post({ | |||||
| url: DEMAND_NOTE_REVOKE_PAID, | |||||
| params: { | |||||
| dnIdList: idList | |||||
| }, | |||||
| onSuccess: () => { | |||||
| setReload(new Date()); | |||||
| } | |||||
| }); | |||||
| } | |||||
| const markPaid = () => { | const markPaid = () => { | ||||
| setConfirmPopUp(false); | setConfirmPopUp(false); | ||||
| let idList = []; | let idList = []; | ||||
| @@ -159,7 +196,7 @@ export default function SearchDemandNote({ searchCriteria, applySearch }) { | |||||
| dnIdList: idList | dnIdList: idList | ||||
| }, | }, | ||||
| onSuccess: () => { | onSuccess: () => { | ||||
| if (reloadFun) reloadFun(); | |||||
| setReload(new Date()); | |||||
| } | } | ||||
| }); | }); | ||||
| } | } | ||||
| @@ -240,9 +277,11 @@ export default function SearchDemandNote({ searchCriteria, applySearch }) { | |||||
| width: 300, | width: 300, | ||||
| renderCell: (params) => { | renderCell: (params) => { | ||||
| return (<table> | return (<table> | ||||
| <tr><td>Issue:</td><td>{DateUtils.dateStr(params?.row.issueDate)}</td></tr> | |||||
| <tr><td>Due:</td><td>{params?.value ? DateUtils.dateStr(params?.value) : "--"}</td></tr> | |||||
| <tr><td>Sent:</td><td>{params.row.sentDate ? <> {DateUtils.datetimeStr(params.row.sentDate)} - {params.row.sentBy} </> : <> To be sent</>}</td></tr> | |||||
| <tbody> | |||||
| <tr><td>Issue:</td><td>{DateUtils.dateStr(params?.row.issueDate)}</td></tr> | |||||
| <tr><td>Due:</td><td>{params?.value ? DateUtils.dateStr(params?.value) : "--"}</td></tr> | |||||
| <tr><td>Sent:</td><td>{params.row.sentDate ? DateUtils.datetimeStr(params.row.sentDate) +" - "+ params.row.sentBy : "To be sent"}</td></tr> | |||||
| </tbody> | |||||
| </table>); | </table>); | ||||
| } | } | ||||
| }, | }, | ||||
| @@ -254,7 +293,7 @@ export default function SearchDemandNote({ searchCriteria, applySearch }) { | |||||
| ), | ), | ||||
| width: 280, | width: 280, | ||||
| renderCell: (params) => { | renderCell: (params) => { | ||||
| return <Button onClick={onDownloadClick(params)}><u>{params.row.filename}</u></Button>; | |||||
| return <Button disabled={onDownload} onClick={onDownloadClick(params)}><u>{params.row.filename}</u></Button>; | |||||
| }, | }, | ||||
| }, | }, | ||||
| { | { | ||||
| @@ -262,7 +301,7 @@ export default function SearchDemandNote({ searchCriteria, applySearch }) { | |||||
| headerName: 'Status', | headerName: 'Status', | ||||
| width: 175, | width: 175, | ||||
| renderCell: (params) => { | renderCell: (params) => { | ||||
| return [StatusUtils.getStatus_Eng(params)] | |||||
| return StatusUtils.getStatus_Eng(params) | |||||
| }, | }, | ||||
| }, | }, | ||||
| ]; | ]; | ||||
| @@ -300,6 +339,7 @@ export default function SearchDemandNote({ searchCriteria, applySearch }) { | |||||
| </Button> | </Button> | ||||
| </label> | </label> | ||||
| </Grid> | </Grid> | ||||
| <Grid item sx={{ ml: 3, mr: 3, mb: 3, mt: 3 }}> | <Grid item sx={{ ml: 3, mr: 3, mb: 3, mt: 3 }}> | ||||
| <Button | <Button | ||||
| variant="contained" | variant="contained" | ||||
| @@ -334,13 +374,23 @@ export default function SearchDemandNote({ searchCriteria, applySearch }) { | |||||
| Mark as Paid | Mark as Paid | ||||
| </Button> | </Button> | ||||
| </Grid> | </Grid> | ||||
| <Grid item sx={{ ml: 3, mr: 3, mb: 3, mt: 3 }}> | |||||
| <Button | |||||
| variant="contained" | |||||
| onClick={() => setRevokePopUp(true)} | |||||
| > | |||||
| Revoke payment | |||||
| </Button> | |||||
| </Grid> | |||||
| </ThemeProvider> | </ThemeProvider> | ||||
| </Grid> | </Grid> | ||||
| : <></> | : <></> | ||||
| } | } | ||||
| <Box sx={{ backgroundColor: "#fff", ml: 2 }} width="98%"> | <Box sx={{ backgroundColor: "#fff", ml: 2 }} width="98%"> | ||||
| <FiDataGrid | <FiDataGrid | ||||
| checkboxSelection = {isGrantedAny(["MAINTAIN_DEMANDNOTE"])} | |||||
| checkboxSelection={isGrantedAny(["MAINTAIN_DEMANDNOTE"])} | |||||
| disableRowSelectionOnClick | disableRowSelectionOnClick | ||||
| onRowSelectionModelChange={(newSelection) => { | onRowSelectionModelChange={(newSelection) => { | ||||
| setSelectedRowItems(newSelection); | setSelectedRowItems(newSelection); | ||||
| @@ -349,13 +399,15 @@ export default function SearchDemandNote({ searchCriteria, applySearch }) { | |||||
| customPageSize={100} | customPageSize={100} | ||||
| getRowHeight={() => 'auto'} | getRowHeight={() => 'auto'} | ||||
| onRowDoubleClick={handleRowDoubleClick} | onRowDoubleClick={handleRowDoubleClick} | ||||
| applyGridOnReady={applyGridOnReady} | |||||
| applySearch={applySearch} | |||||
| doLoad={useMemo(() => ({ | doLoad={useMemo(() => ({ | ||||
| url: DEMAND_NOTE_LIST_ALL, | url: DEMAND_NOTE_LIST_ALL, | ||||
| params: _searchCriteria, | params: _searchCriteria, | ||||
| callback: function (responseData) { | callback: function (responseData) { | ||||
| setRows(responseData?.records); | setRows(responseData?.records); | ||||
| } | } | ||||
| }), [_searchCriteria])} | |||||
| }), [_searchCriteria, reload])} | |||||
| /> | /> | ||||
| </Box> | </Box> | ||||
| <div> | <div> | ||||
| @@ -422,6 +474,28 @@ export default function SearchDemandNote({ searchCriteria, applySearch }) { | |||||
| </DialogActions> | </DialogActions> | ||||
| </Dialog> | </Dialog> | ||||
| </div> | </div> | ||||
| <div> | |||||
| <Dialog | |||||
| open={isRevokePopUp} | |||||
| onClose={() => setRevokePopUp(false)} | |||||
| PaperProps={{ | |||||
| sx: { | |||||
| minWidth: '40vw', | |||||
| maxWidth: { xs: '90vw', s: '90vw', m: '70vw', lg: '70vw' }, | |||||
| maxHeight: { xs: '90vh', s: '70vh', m: '70vh', lg: '60vh' } | |||||
| } | |||||
| }} | |||||
| > | |||||
| <DialogTitle><Typography variant="h3">Confirm</Typography></DialogTitle> | |||||
| <DialogContent style={{ display: 'flex', }}> | |||||
| <Typography variant="h4" style={{ padding: '16px' }}>Are you sure to revoke DN as To Be Paid?</Typography> | |||||
| </DialogContent> | |||||
| <DialogActions> | |||||
| <Button onClick={() => setRevokePopUp(false)}><Typography variant="h5">Cancel</Typography></Button> | |||||
| <Button onClick={() => revokePaid()}><Typography variant="h5">Confirm</Typography></Button> | |||||
| </DialogActions> | |||||
| </Dialog> | |||||
| </div> | |||||
| <div> | <div> | ||||
| <Dialog | <Dialog | ||||
| open={isSendPopUp} | open={isSendPopUp} | ||||
| @@ -21,7 +21,7 @@ import {DemoItem} from "@mui/x-date-pickers/internals/demo"; | |||||
| import {LocalizationProvider} from "@mui/x-date-pickers/LocalizationProvider"; | import {LocalizationProvider} from "@mui/x-date-pickers/LocalizationProvider"; | ||||
| import {AdapterDayjs} from "@mui/x-date-pickers/AdapterDayjs"; | import {AdapterDayjs} from "@mui/x-date-pickers/AdapterDayjs"; | ||||
| // ==============================|| DASHBOARD - DEFAULT ||============================== // | // ==============================|| DASHBOARD - DEFAULT ||============================== // | ||||
| const SearchDemandNoteForm = ({ applySearch, orgComboData, searchCriteria, issueComboData | |||||
| const SearchDemandNoteForm = ({ applySearch, orgComboData, searchCriteria, issueComboData, onGridReady | |||||
| }) => { | }) => { | ||||
| const [type, setType] = React.useState([]); | const [type, setType] = React.useState([]); | ||||
| @@ -44,6 +44,28 @@ const SearchDemandNoteForm = ({ applySearch, orgComboData, searchCriteria, issue | |||||
| const intl = useIntl(); | const intl = useIntl(); | ||||
| const { locale } = intl; | const { locale } = intl; | ||||
| React.useEffect(() => { | |||||
| if(searchCriteria.status!=undefined){ | |||||
| if(searchCriteria.status === ""){ | |||||
| ComboData.denmandNoteStatus[0] | |||||
| }else{ | |||||
| setSelectedStatus(ComboData.denmandNoteStatus.find(item => item.type === searchCriteria.status)) | |||||
| } | |||||
| if(searchCriteria.dueDateFrom != ""){ | |||||
| setMinDueDate(DateUtils.dateValue(searchCriteria.dueDateFrom)) | |||||
| }else{ | |||||
| setMinDueDate(null) | |||||
| } | |||||
| if(searchCriteria.dueDateTo != ""){ | |||||
| setMaxDueDate(DateUtils.dateValue(searchCriteria.dueDateTo)) | |||||
| }else{ | |||||
| setMaxDueDate(null); | |||||
| } | |||||
| }else{ | |||||
| setSelectedStatus(ComboData.denmandNoteStatus[0]) | |||||
| } | |||||
| }, [searchCriteria]); | |||||
| React.useEffect(() => { | React.useEffect(() => { | ||||
| setFromDateValue(minDate); | setFromDateValue(minDate); | ||||
| }, [minDate]); | }, [minDate]); | ||||
| @@ -93,6 +115,8 @@ const SearchDemandNoteForm = ({ applySearch, orgComboData, searchCriteria, issue | |||||
| dueDateFrom: sentDueDateFrom, | dueDateFrom: sentDueDateFrom, | ||||
| dueDateTo: sentDueDateTo, | dueDateTo: sentDueDateTo, | ||||
| status: (data?.status === '' || data?.status?.includes("all")) ? "" : data.status, | status: (data?.status === '' || data?.status?.includes("all")) ? "" : data.status, | ||||
| start:0, | |||||
| limit:10 | |||||
| }; | }; | ||||
| applySearch(temp); | applySearch(temp); | ||||
| }; | }; | ||||
| @@ -101,12 +125,18 @@ const SearchDemandNoteForm = ({ applySearch, orgComboData, searchCriteria, issue | |||||
| React.useEffect(() => { | React.useEffect(() => { | ||||
| if (orgComboData && orgComboData.length > 0) { | if (orgComboData && orgComboData.length > 0) { | ||||
| setOrgCombo(orgComboData); | setOrgCombo(orgComboData); | ||||
| if(searchCriteria.orgId!=undefined){ | |||||
| setOrgSelected(orgComboData.find(item => item.key === searchCriteria.orgId)) | |||||
| } | |||||
| } | } | ||||
| }, [orgComboData]); | }, [orgComboData]); | ||||
| React.useEffect(() => { | React.useEffect(() => { | ||||
| if (issueComboData && issueComboData.length > 0) { | if (issueComboData && issueComboData.length > 0) { | ||||
| setIssueCombo(issueComboData); | setIssueCombo(issueComboData); | ||||
| if(searchCriteria.issueId!=undefined){ | |||||
| setIssueSelected(issueComboData.find(item => item.id === searchCriteria.issueId)) | |||||
| } | |||||
| } | } | ||||
| }, [issueComboData]); | }, [issueComboData]); | ||||
| @@ -117,9 +147,13 @@ const SearchDemandNoteForm = ({ applySearch, orgComboData, searchCriteria, issue | |||||
| setSelectedStatus(ComboData.denmandNoteStatus[0]); | setSelectedStatus(ComboData.denmandNoteStatus[0]); | ||||
| setMinDueDate(null); | setMinDueDate(null); | ||||
| setMaxDueDate(null); | setMaxDueDate(null); | ||||
| setMinDate(searchCriteria.dateFrom); | |||||
| setMaxDate(searchCriteria.dateTo); | |||||
| reset(); | |||||
| setMinDate(DateUtils.dateValue(new Date().setDate(new Date().getDate()-14))) | |||||
| setMaxDate(DateUtils.dateValue(new Date())) | |||||
| reset({ | |||||
| appNo:"", | |||||
| dnNo:"", | |||||
| }); | |||||
| localStorage.setItem('searchCriteria',"") | |||||
| } | } | ||||
| function getIssueLabel(data) { | function getIssueLabel(data) { | ||||
| @@ -177,6 +211,11 @@ const SearchDemandNoteForm = ({ applySearch, orgComboData, searchCriteria, issue | |||||
| onChange={(event, newValue) => { | onChange={(event, newValue) => { | ||||
| setIssueSelected(newValue); | setIssueSelected(newValue); | ||||
| }} | }} | ||||
| sx={{ | |||||
| '& .MuiInputBase-root': { alignItems: 'center' }, | |||||
| '& .MuiAutocomplete-endAdornment': { top: '50%', transform: 'translateY(-50%)' }, | |||||
| '& .MuiOutlinedInput-root': { height: 40 } | |||||
| }} | |||||
| renderInput={(params) => ( | renderInput={(params) => ( | ||||
| <TextField {...params} | <TextField {...params} | ||||
| label="Gazette Issue No." | label="Gazette Issue No." | ||||
| @@ -185,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> | </Grid> | ||||
| @@ -209,10 +252,12 @@ const SearchDemandNoteForm = ({ applySearch, orgComboData, searchCriteria, issue | |||||
| disablePortal | disablePortal | ||||
| id="orgId" | id="orgId" | ||||
| options={orgCombo} | options={orgCombo} | ||||
| groupBy={(option) => option.groupType} | |||||
| size="small" | size="small" | ||||
| value={orgSelected} | value={orgSelected} | ||||
| getOptionLabel={(option) => option.name? option.name : ""} | getOptionLabel={(option) => option.name? option.name : ""} | ||||
| inputValue={orgSelected ? orgSelected.name : ""} | |||||
| inputValue={orgSelected ? orgSelected.name!=undefined?orgSelected.name:"" : ""} | |||||
| onChange={(event, newValue) => { | onChange={(event, newValue) => { | ||||
| if (newValue !== null) { | if (newValue !== null) { | ||||
| setOrgSelected(newValue); | setOrgSelected(newValue); | ||||
| @@ -220,6 +265,11 @@ const SearchDemandNoteForm = ({ applySearch, orgComboData, searchCriteria, issue | |||||
| setOrgSelected({}); | setOrgSelected({}); | ||||
| } | } | ||||
| }} | }} | ||||
| sx={{ | |||||
| '& .MuiInputBase-root': { alignItems: 'center' }, | |||||
| '& .MuiAutocomplete-endAdornment': { top: '50%', transform: 'translateY(-50%)' }, | |||||
| '& .MuiOutlinedInput-root': { height: 40 } | |||||
| }} | |||||
| renderInput={(params) => ( | renderInput={(params) => ( | ||||
| <TextField {...params} | <TextField {...params} | ||||
| label="Organisation" | label="Organisation" | ||||
| @@ -228,12 +278,23 @@ const SearchDemandNoteForm = ({ applySearch, orgComboData, searchCriteria, issue | |||||
| }} | }} | ||||
| /> | /> | ||||
| )} | )} | ||||
| renderGroup={(params) => ( | |||||
| <Grid item key={params.key}> | |||||
| <Typography fontSize={20} fontStyle="italic" p={1}> | |||||
| {params.group} | |||||
| </Typography> | |||||
| {params.children} | |||||
| </Grid> | |||||
| )} | |||||
| clearText={intl.formatMessage({ id: "muiClear" })} | |||||
| closeText={intl.formatMessage({ id: "muiClose" })} | |||||
| openText={intl.formatMessage({ id: "muiOpen" })} | |||||
| noOptionsText={intl.formatMessage({ id: "muiNoOptions" })} | |||||
| /> | /> | ||||
| </Grid> | </Grid> | ||||
| : <></> | : <></> | ||||
| } | } | ||||
| <Grid item xs={9} s={6} md={5} lg={3} sx={{ ml: 3, mr: 3, mb: 3 }}> | <Grid item xs={9} s={6} md={5} lg={3} sx={{ ml: 3, mr: 3, mb: 3 }}> | ||||
| <TextField | <TextField | ||||
| fullWidth | fullWidth | ||||
| @@ -377,6 +438,11 @@ const SearchDemandNoteForm = ({ applySearch, orgComboData, searchCriteria, issue | |||||
| setSelectedStatus(newValue); | setSelectedStatus(newValue); | ||||
| } | } | ||||
| }} | }} | ||||
| sx={{ | |||||
| '& .MuiInputBase-root': { alignItems: 'center' }, | |||||
| '& .MuiAutocomplete-endAdornment': { top: '50%', transform: 'translateY(-50%)' }, | |||||
| '& .MuiOutlinedInput-root': { height: 40 } | |||||
| }} | |||||
| getOptionLabel={(option) => option.label} | getOptionLabel={(option) => option.label} | ||||
| renderInput={(params) => ( | renderInput={(params) => ( | ||||
| <TextField | <TextField | ||||
| @@ -387,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> | </Grid> | ||||
| @@ -411,6 +481,7 @@ const SearchDemandNoteForm = ({ applySearch, orgComboData, searchCriteria, issue | |||||
| <Button | <Button | ||||
| variant="contained" | variant="contained" | ||||
| type="submit" | type="submit" | ||||
| disabled={onGridReady} | |||||
| > | > | ||||
| Submit | Submit | ||||
| </Button> | </Button> | ||||
| @@ -10,6 +10,7 @@ import * as React from "react"; | |||||
| import * as UrlUtils from "utils/ApiPathConst"; | import * as UrlUtils from "utils/ApiPathConst"; | ||||
| import * as HttpUtils from "utils/HttpUtils"; | import * as HttpUtils from "utils/HttpUtils"; | ||||
| import * as DateUtils from "utils/DateUtils"; | import * as DateUtils from "utils/DateUtils"; | ||||
| import { getSearchCriteria } from "auth/utils"; | |||||
| import Loadable from 'components/Loadable'; | import Loadable from 'components/Loadable'; | ||||
| const LoadingComponent = Loadable(React.lazy(() => import('pages/extra-pages/LoadingComponent'))); | const LoadingComponent = Loadable(React.lazy(() => import('pages/extra-pages/LoadingComponent'))); | ||||
| @@ -35,16 +36,26 @@ const UserSearchPage_Individual = () => { | |||||
| const [orgCombo, setOrgCombo] = React.useState([]); | const [orgCombo, setOrgCombo] = React.useState([]); | ||||
| const [issueCombo, setIssueCombo] = React.useState([]); | const [issueCombo, setIssueCombo] = React.useState([]); | ||||
| const [searchCriteria, setSearchCriteria] = React.useState({ | const [searchCriteria, setSearchCriteria] = React.useState({ | ||||
| dateTo: DateUtils.dateValue(new Date()), | |||||
| dateFrom: DateUtils.dateValue(new Date().setDate(new Date().getDate() - 14)), | |||||
| // dateTo: DateUtils.dateValue(new Date()), | |||||
| // dateFrom: DateUtils.dateValue(new Date().setDate(new Date().getDate() - 14)), | |||||
| // dueDateTo: DateUtils.dateValue(new Date()), | // dueDateTo: DateUtils.dateValue(new Date()), | ||||
| // dueDateFrom: DateUtils.dateValue(new Date().setDate(new Date().getDate() - 14)), | // dueDateFrom: DateUtils.dateValue(new Date().setDate(new Date().getDate() - 14)), | ||||
| }); | }); | ||||
| const [onReady, setOnReady] = React.useState(false); | const [onReady, setOnReady] = React.useState(false); | ||||
| const [onGridReady, setGridOnReady] = React.useState(false); | |||||
| React.useEffect(() => { | React.useEffect(() => { | ||||
| getOrgCombo(); | getOrgCombo(); | ||||
| getIssueCombo(); | getIssueCombo(); | ||||
| if (Object.keys(getSearchCriteria(window.location.pathname)).length>0){ | |||||
| setSearchCriteria(getSearchCriteria(window.location.pathname)) | |||||
| }else{ | |||||
| localStorage.setItem('searchCriteria',"") | |||||
| setSearchCriteria({ | |||||
| dateTo: DateUtils.dateValue(new Date()), | |||||
| dateFrom: DateUtils.dateValue(new Date().setDate(new Date().getDate()-14)), | |||||
| }) | |||||
| } | |||||
| }, []); | }, []); | ||||
| React.useEffect(() => { | React.useEffect(() => { | ||||
| @@ -72,9 +83,14 @@ const UserSearchPage_Individual = () => { | |||||
| }); | }); | ||||
| } | } | ||||
| function applySearch(input) { | function applySearch(input) { | ||||
| setGridOnReady(true) | |||||
| setSearchCriteria(input); | setSearchCriteria(input); | ||||
| localStorage.setItem('searchCriteria', JSON.stringify({path:window.location.pathname,data:input})) | |||||
| } | |||||
| function applyGridOnReady(input) { | |||||
| setGridOnReady(input); | |||||
| } | } | ||||
| return ( | return ( | ||||
| @@ -102,7 +118,7 @@ const UserSearchPage_Individual = () => { | |||||
| orgComboData={orgCombo} | orgComboData={orgCombo} | ||||
| issueComboData={issueCombo} | issueComboData={issueCombo} | ||||
| searchCriteria={searchCriteria} | searchCriteria={searchCriteria} | ||||
| onGridReady={onGridReady} | |||||
| /> | /> | ||||
| </Grid> | </Grid> | ||||
| {/*row 2*/} | {/*row 2*/} | ||||
| @@ -115,6 +131,7 @@ const UserSearchPage_Individual = () => { | |||||
| <EventTable | <EventTable | ||||
| applySearch={applySearch} | applySearch={applySearch} | ||||
| searchCriteria={searchCriteria} | searchCriteria={searchCriteria} | ||||
| applyGridOnReady={applyGridOnReady} | |||||
| /> | /> | ||||
| </MainCard> | </MainCard> | ||||
| </Grid> | </Grid> | ||||
| @@ -15,11 +15,12 @@ import {useIntl} from "react-intl"; | |||||
| import {DEMAND_NOTE_LIST} from "utils/ApiPathConst"; | import {DEMAND_NOTE_LIST} from "utils/ApiPathConst"; | ||||
| // ==============================|| EVENT TABLE ||============================== // | // ==============================|| EVENT TABLE ||============================== // | ||||
| export default function SearchDemandNote({ searchCriteria }) { | |||||
| export default function SearchDemandNote({ searchCriteria, applyGridOnReady,applySearch }) { | |||||
| const intl = useIntl(); | const intl = useIntl(); | ||||
| const theme = useTheme(); | const theme = useTheme(); | ||||
| const isMdOrLg = useMediaQuery(theme.breakpoints.up('md')); | const isMdOrLg = useMediaQuery(theme.breakpoints.up('md')); | ||||
| const { locale } = intl; | |||||
| const [_searchCriteria, set_searchCriteria] = React.useState(searchCriteria); | const [_searchCriteria, set_searchCriteria] = React.useState(searchCriteria); | ||||
| @@ -52,7 +53,7 @@ export default function SearchDemandNote({ searchCriteria }) { | |||||
| { | { | ||||
| id: 'issueDate', | id: 'issueDate', | ||||
| field: 'issueDate', | field: 'issueDate', | ||||
| headerName: intl.formatMessage({id: 'receiptDate'}), | |||||
| headerName: intl.formatMessage({id: 'sendDate'}), | |||||
| width: isMdOrLg ? 'auto' : 175, | width: isMdOrLg ? 'auto' : 175, | ||||
| flex: isMdOrLg ? 1 : undefined, | flex: isMdOrLg ? 1 : undefined, | ||||
| valueGetter: (params) => { | valueGetter: (params) => { | ||||
| @@ -65,12 +66,13 @@ export default function SearchDemandNote({ searchCriteria }) { | |||||
| width: isMdOrLg ? 'auto' : 175, | width: isMdOrLg ? 'auto' : 175, | ||||
| flex: isMdOrLg ? 1 : undefined, | flex: isMdOrLg ? 1 : undefined, | ||||
| renderCell: (params) => { | renderCell: (params) => { | ||||
| return [StatusUtils.getStatus_Cht(params)] | |||||
| return [StatusUtils.getStatus_i18n(params, locale) ] | |||||
| }, | }, | ||||
| }, | }, | ||||
| { | { | ||||
| field: 'sentDate', | field: 'sentDate', | ||||
| headerName: intl.formatMessage({id: 'sendDate'}), | |||||
| headerName: intl.formatMessage({id: 'sendDateTime'}), | |||||
| width: isMdOrLg ? 'auto' : 200, | width: isMdOrLg ? 'auto' : 200, | ||||
| flex: isMdOrLg ? 1 : undefined, | flex: isMdOrLg ? 1 : undefined, | ||||
| valueGetter: (params) => { | valueGetter: (params) => { | ||||
| @@ -95,10 +97,16 @@ export default function SearchDemandNote({ searchCriteria }) { | |||||
| columns={columns} | columns={columns} | ||||
| customPageSize={10} | customPageSize={10} | ||||
| getRowHeight={() => 'auto'} | getRowHeight={() => 'auto'} | ||||
| doLoad={{ | |||||
| applyGridOnReady={applyGridOnReady} | |||||
| applySearch={applySearch} | |||||
| // doLoad={{ | |||||
| // url: DEMAND_NOTE_LIST, | |||||
| // params: _searchCriteria, | |||||
| // }} | |||||
| doLoad={React.useMemo(() => ({ | |||||
| url: DEMAND_NOTE_LIST, | url: DEMAND_NOTE_LIST, | ||||
| params: _searchCriteria, | params: _searchCriteria, | ||||
| }} | |||||
| }), [_searchCriteria])} | |||||
| /> | /> | ||||
| </Box> | </Box> | ||||
| </div> | </div> | ||||
| @@ -22,7 +22,7 @@ import {DemoItem} from "@mui/x-date-pickers/internals/demo"; | |||||
| import {LocalizationProvider} from "@mui/x-date-pickers/LocalizationProvider"; | import {LocalizationProvider} from "@mui/x-date-pickers/LocalizationProvider"; | ||||
| import {AdapterDayjs} from "@mui/x-date-pickers/AdapterDayjs"; | import {AdapterDayjs} from "@mui/x-date-pickers/AdapterDayjs"; | ||||
| // ==============================|| DASHBOARD - DEFAULT ||============================== // | // ==============================|| DASHBOARD - DEFAULT ||============================== // | ||||
| const SearchDemandNoteForm = ({ applySearch, searchCriteria, issueComboData | |||||
| const SearchDemandNoteForm = ({ applySearch, searchCriteria, issueComboData, onGridReady | |||||
| }) => { | }) => { | ||||
| const intl = useIntl(); | const intl = useIntl(); | ||||
| @@ -38,6 +38,18 @@ const SearchDemandNoteForm = ({ applySearch, searchCriteria, issueComboData | |||||
| const [fromDateValue, setFromDateValue] = React.useState("dd / mm / yyyy"); | const [fromDateValue, setFromDateValue] = React.useState("dd / mm / yyyy"); | ||||
| const [toDateValue, setToDateValue] = React.useState("dd / mm / yyyy"); | const [toDateValue, setToDateValue] = React.useState("dd / mm / yyyy"); | ||||
| React.useEffect(() => { | |||||
| if(searchCriteria.status!=undefined){ | |||||
| if(searchCriteria.status === ""){ | |||||
| ComboData.denmandNoteStatus_Public[0] | |||||
| }else{ | |||||
| setSelectedStatus(ComboData.denmandNoteStatus_Public.find(item => item.type === searchCriteria.status)) | |||||
| } | |||||
| }else{ | |||||
| setSelectedStatus(ComboData.denmandNoteStatus_Public[0]) | |||||
| } | |||||
| }, [searchCriteria]); | |||||
| React.useEffect(() => { | React.useEffect(() => { | ||||
| setFromDateValue(minDate); | setFromDateValue(minDate); | ||||
| }, [minDate]); | }, [minDate]); | ||||
| @@ -76,17 +88,23 @@ const SearchDemandNoteForm = ({ applySearch, searchCriteria, issueComboData | |||||
| React.useEffect(() => { | React.useEffect(() => { | ||||
| if (issueComboData && issueComboData.length > 0) { | if (issueComboData && issueComboData.length > 0) { | ||||
| setIssueCombo(issueComboData); | setIssueCombo(issueComboData); | ||||
| if(searchCriteria.issueId!=undefined){ | |||||
| setIssueSelected(issueComboData.find(item => item.id === searchCriteria.issueId)) | |||||
| } | |||||
| } | } | ||||
| }, [issueComboData]); | }, [issueComboData]); | ||||
| function resetForm() { | function resetForm() { | ||||
| setType([]); | setType([]); | ||||
| // setStatus({ key: 0, label: 'All', type: 'all' }); | |||||
| setSelectedStatus(ComboData.denmandNoteStatus_Public[0]); | |||||
| // setOrgSelected({}); | // setOrgSelected({}); | ||||
| setMinDate(DateUtils.dateValue(new Date().setDate(new Date().getDate()-14))) | setMinDate(DateUtils.dateValue(new Date().setDate(new Date().getDate()-14))) | ||||
| setMaxDate(DateUtils.dateValue(new Date())) | setMaxDate(DateUtils.dateValue(new Date())) | ||||
| setIssueSelected({}); | setIssueSelected({}); | ||||
| reset(); | |||||
| reset({ | |||||
| appNo:"", | |||||
| dnNo:"", | |||||
| }); | |||||
| } | } | ||||
| function getIssueLabel(data) { | function getIssueLabel(data) { | ||||
| @@ -149,6 +167,11 @@ const SearchDemandNoteForm = ({ applySearch, searchCriteria, issueComboData | |||||
| setIssueSelected(newValue); | setIssueSelected(newValue); | ||||
| } | } | ||||
| }} | }} | ||||
| sx={{ | |||||
| '& .MuiInputBase-root': { alignItems: 'center' }, | |||||
| '& .MuiAutocomplete-endAdornment': { top: '50%', transform: 'translateY(-50%)' }, | |||||
| '& .MuiOutlinedInput-root': { height: 40 } | |||||
| }} | |||||
| renderInput={(params) => ( | renderInput={(params) => ( | ||||
| <TextField {...params} | <TextField {...params} | ||||
| label={intl.formatMessage({ id: 'gazetteCount' })} | label={intl.formatMessage({ id: 'gazetteCount' })} | ||||
| @@ -157,6 +180,10 @@ const SearchDemandNoteForm = ({ applySearch, searchCriteria, issueComboData | |||||
| }} | }} | ||||
| /> | /> | ||||
| )} | )} | ||||
| clearText={intl.formatMessage({ id: "muiClear" })} | |||||
| closeText={intl.formatMessage({ id: "muiClose" })} | |||||
| openText={intl.formatMessage({ id: "muiOpen" })} | |||||
| noOptionsText={intl.formatMessage({ id: "muiNoOptions" })} | |||||
| /> | /> | ||||
| </Grid> | </Grid> | ||||
| @@ -187,7 +214,7 @@ const SearchDemandNoteForm = ({ applySearch, searchCriteria, issueComboData | |||||
| </Grid> | </Grid> | ||||
| <Grid item xs={9} s={6} md={5} lg={3} sx={{ ml: 3, mr: 3, mb: 3 }}> | <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']}> | <DemoItem components={['DatePicker']}> | ||||
| <DatePicker | <DatePicker | ||||
| id="dateFrom" | id="dateFrom" | ||||
| @@ -214,7 +241,7 @@ const SearchDemandNoteForm = ({ applySearch, searchCriteria, issueComboData | |||||
| </Grid> | </Grid> | ||||
| <Grid item xs={9} s={6} md={5} lg={3} sx={{ ml: 3, mr: 3, mb: 3 }}> | <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']}> | <DemoItem components={['DatePicker']}> | ||||
| <DatePicker | <DatePicker | ||||
| id="dateTo" | id="dateTo" | ||||
| @@ -246,6 +273,7 @@ const SearchDemandNoteForm = ({ applySearch, searchCriteria, issueComboData | |||||
| {...register("status")} | {...register("status")} | ||||
| id="status" | id="status" | ||||
| size="small" | size="small" | ||||
| disableClearable | |||||
| options={ComboData.denmandNoteStatus_Public} | options={ComboData.denmandNoteStatus_Public} | ||||
| getOptionLabel={(option) => option?.i18nLabel ? intl.formatMessage({ id: option.i18nLabel }) : ""} | getOptionLabel={(option) => option?.i18nLabel ? intl.formatMessage({ id: option.i18nLabel }) : ""} | ||||
| inputValue={selectedStatus?.i18nLabel ? intl.formatMessage({ id: selectedStatus.i18nLabel }) : ""} | inputValue={selectedStatus?.i18nLabel ? intl.formatMessage({ id: selectedStatus.i18nLabel }) : ""} | ||||
| @@ -255,6 +283,11 @@ const SearchDemandNoteForm = ({ applySearch, searchCriteria, issueComboData | |||||
| setSelectedStatus(newValue); | setSelectedStatus(newValue); | ||||
| } | } | ||||
| }} | }} | ||||
| sx={{ | |||||
| '& .MuiInputBase-root': { alignItems: 'center' }, | |||||
| '& .MuiAutocomplete-endAdornment': { top: '50%', transform: 'translateY(-50%)' }, | |||||
| '& .MuiOutlinedInput-root': { height: 40 } | |||||
| }} | |||||
| renderInput={(params) => ( | renderInput={(params) => ( | ||||
| <TextField | <TextField | ||||
| {...params} | {...params} | ||||
| @@ -264,6 +297,10 @@ const SearchDemandNoteForm = ({ applySearch, searchCriteria, issueComboData | |||||
| InputLabelProps={{ | InputLabelProps={{ | ||||
| shrink: true | shrink: true | ||||
| }} | }} | ||||
| clearText={intl.formatMessage({ id: "muiClear" })} | |||||
| closeText={intl.formatMessage({ id: "muiClose" })} | |||||
| openText={intl.formatMessage({ id: "muiOpen" })} | |||||
| noOptionsText={intl.formatMessage({ id: "muiNoOptions" })} | |||||
| /> | /> | ||||
| </Grid> | </Grid> | ||||
| @@ -288,6 +325,7 @@ const SearchDemandNoteForm = ({ applySearch, searchCriteria, issueComboData | |||||
| <Button | <Button | ||||
| variant="contained" | variant="contained" | ||||
| type="submit" | type="submit" | ||||
| disabled={onGridReady} | |||||
| > | > | ||||
| <FormattedMessage id="submit" /> | <FormattedMessage id="submit" /> | ||||
| </Button> | </Button> | ||||
| @@ -10,6 +10,7 @@ import * as React from "react"; | |||||
| import {GET_ORG_COMBO, GET_ISSUE_COMBO} from "utils/ApiPathConst"; | import {GET_ORG_COMBO, GET_ISSUE_COMBO} from "utils/ApiPathConst"; | ||||
| import * as HttpUtils from "utils/HttpUtils"; | import * as HttpUtils from "utils/HttpUtils"; | ||||
| import * as DateUtils from "utils/DateUtils"; | import * as DateUtils from "utils/DateUtils"; | ||||
| import { getSearchCriteria } from "auth/utils"; | |||||
| import Loadable from 'components/Loadable'; | import Loadable from 'components/Loadable'; | ||||
| const LoadingComponent = Loadable(React.lazy(() => import('pages/extra-pages/LoadingComponent'))); | const LoadingComponent = Loadable(React.lazy(() => import('pages/extra-pages/LoadingComponent'))); | ||||
| @@ -17,6 +18,7 @@ const SearchForm = Loadable(React.lazy(() => import('./SearchForm'))); | |||||
| const EventTable = Loadable(React.lazy(() => import('./DataGrid'))); | const EventTable = Loadable(React.lazy(() => import('./DataGrid'))); | ||||
| import titleBackgroundImg from 'assets/images/dashboard/gazette-bar.png' | import titleBackgroundImg from 'assets/images/dashboard/gazette-bar.png' | ||||
| import {FormattedMessage} from "react-intl"; | import {FormattedMessage} from "react-intl"; | ||||
| import usePageTitle from "components/usePageTitle"; | |||||
| const BackgroundHead = { | const BackgroundHead = { | ||||
| backgroundImage: `url(${titleBackgroundImg})`, | backgroundImage: `url(${titleBackgroundImg})`, | ||||
| @@ -31,18 +33,25 @@ const BackgroundHead = { | |||||
| // ==============================|| DASHBOARD - DEFAULT ||============================== // | // ==============================|| DASHBOARD - DEFAULT ||============================== // | ||||
| const SearchPage_DemandNote_Pub = () => { | const SearchPage_DemandNote_Pub = () => { | ||||
| usePageTitle("paymentInfoRecord"); | |||||
| const [orgCombo, setOrgCombo] = React.useState([]); | const [orgCombo, setOrgCombo] = React.useState([]); | ||||
| const [issueCombo, setIssueCombo] = React.useState([]); | const [issueCombo, setIssueCombo] = React.useState([]); | ||||
| const [searchCriteria, setSearchCriteria] = React.useState({ | |||||
| dateTo: DateUtils.dateValue(new Date()), | |||||
| dateFrom: DateUtils.dateValue(new Date().setDate(new Date().getDate() - 14)), | |||||
| }); | |||||
| const [searchCriteria, setSearchCriteria] = React.useState({}); | |||||
| const [onReady, setOnReady] = React.useState(false); | const [onReady, setOnReady] = React.useState(false); | ||||
| const [onGridReady, setGridOnReady] = React.useState(false); | |||||
| React.useEffect(() => { | React.useEffect(() => { | ||||
| getOrgCombo(); | getOrgCombo(); | ||||
| getIssueCombo(); | getIssueCombo(); | ||||
| if (Object.keys(getSearchCriteria(window.location.pathname)).length>0){ | |||||
| setSearchCriteria(getSearchCriteria(window.location.pathname)) | |||||
| }else{ | |||||
| localStorage.setItem('searchCriteria',"") | |||||
| setSearchCriteria({ | |||||
| dateTo: DateUtils.dateValue(new Date()), | |||||
| dateFrom: DateUtils.dateValue(new Date().setDate(new Date().getDate()-14)), | |||||
| }) | |||||
| } | |||||
| }, []); | }, []); | ||||
| React.useEffect(() => { | React.useEffect(() => { | ||||
| @@ -72,7 +81,13 @@ const SearchPage_DemandNote_Pub = () => { | |||||
| function applySearch(input) { | function applySearch(input) { | ||||
| setGridOnReady(true) | |||||
| setSearchCriteria(input); | setSearchCriteria(input); | ||||
| localStorage.setItem('searchCriteria', JSON.stringify({path:window.location.pathname,data:input})) | |||||
| } | |||||
| function applyGridOnReady(input) { | |||||
| setGridOnReady(input); | |||||
| } | } | ||||
| return ( | return ( | ||||
| @@ -87,7 +102,7 @@ const SearchPage_DemandNote_Pub = () => { | |||||
| <Grid item xs={12}> | <Grid item xs={12}> | ||||
| <div style={BackgroundHead}> | <div style={BackgroundHead}> | ||||
| <Stack direction="row" height='70px' justifyContent="flex-start" alignItems="center"> | <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" /> | <FormattedMessage id="paymentInfoRecord" /> | ||||
| </Typography> | </Typography> | ||||
| </Stack> | </Stack> | ||||
| @@ -100,6 +115,7 @@ const SearchPage_DemandNote_Pub = () => { | |||||
| orgComboData={orgCombo} | orgComboData={orgCombo} | ||||
| issueComboData={issueCombo} | issueComboData={issueCombo} | ||||
| searchCriteria={searchCriteria} | searchCriteria={searchCriteria} | ||||
| onGridReady={onGridReady} | |||||
| /> | /> | ||||
| </Grid> | </Grid> | ||||
| {/*row 2*/} | {/*row 2*/} | ||||
| @@ -110,7 +126,9 @@ const SearchPage_DemandNote_Pub = () => { | |||||
| sx={{ backgroundColor: '#fff' }} | sx={{ backgroundColor: '#fff' }} | ||||
| > | > | ||||
| <EventTable | <EventTable | ||||
| searchCriteria={searchCriteria} | |||||
| searchCriteria={searchCriteria} | |||||
| applyGridOnReady={applyGridOnReady} | |||||
| applySearch={applySearch} | |||||
| /> | /> | ||||
| </MainCard> | </MainCard> | ||||
| </Grid> | </Grid> | ||||
| @@ -50,7 +50,7 @@ const Index = () => { | |||||
| axios.get(`${UrlUtils.GET_EMAIL}/${params.id}`) | axios.get(`${UrlUtils.GET_EMAIL}/${params.id}`) | ||||
| .then((response) => { | .then((response) => { | ||||
| if (response.status === 200) { | if (response.status === 200) { | ||||
| console.log(response) | |||||
| // console.log(response) | |||||
| setRecord(response.data.data) | setRecord(response.data.data) | ||||
| } | } | ||||
| }) | }) | ||||
| @@ -95,14 +95,14 @@ const Index = () => { | |||||
| console.log(error); | console.log(error); | ||||
| return false; | return false; | ||||
| }); | }); | ||||
| console.log(data) | |||||
| // console.log(data) | |||||
| } | } | ||||
| const handleDelete = () => { | const handleDelete = () => { | ||||
| axios.delete(`${UrlUtils.DELETE_EMAIL}/${params.id}`, | axios.delete(`${UrlUtils.DELETE_EMAIL}/${params.id}`, | ||||
| ) | ) | ||||
| .then((response) => { | .then((response) => { | ||||
| console.log(response) | |||||
| // console.log(response) | |||||
| if (response.status === 204) { | if (response.status === 204) { | ||||
| // location.reload(); | // location.reload(); | ||||
| navigate('/setting/emailTemplate'); | navigate('/setting/emailTemplate'); | ||||
| @@ -97,10 +97,14 @@ export default function EmailTemplateTable({ responseData }) { | |||||
| customPageSize={10} | customPageSize={10} | ||||
| onRowDoubleClick={handleRowDoubleClick} | onRowDoubleClick={handleRowDoubleClick} | ||||
| getRowHeight={() => 'auto'} | getRowHeight={() => 'auto'} | ||||
| doLoad={{ | |||||
| url:GET_EMAIL_LIST, | |||||
| params: _responseData | |||||
| }} | |||||
| // doLoad={{ | |||||
| // url:GET_EMAIL_LIST, | |||||
| // params: _responseData | |||||
| // }} | |||||
| doLoad={React.useMemo(() => ({ | |||||
| url: GET_EMAIL_LIST, | |||||
| params: _responseData, | |||||
| }), [_responseData])} | |||||
| /> | /> | ||||
| </div> | </div> | ||||
| ); | ); | ||||
| @@ -4,10 +4,11 @@ import * as FormatUtils from "utils/FormatUtils" | |||||
| import {GFIMIS_LIST} from "utils/ApiPathConst"; | import {GFIMIS_LIST} from "utils/ApiPathConst"; | ||||
| import { useNavigate } from "react-router-dom"; | import { useNavigate } from "react-router-dom"; | ||||
| import { FiDataGrid } from "components/FiDataGrid"; | import { FiDataGrid } from "components/FiDataGrid"; | ||||
| // ==============================|| EVENT TABLE ||============================== // | // ==============================|| EVENT TABLE ||============================== // | ||||
| export default function SearchTable({ searchCriteria }) { | |||||
| const [_searchCriteria, set_searchCriteria] = React.useState(searchCriteria); | |||||
| export default function SearchTable({ previewSearchCriteria, onPreviewGridOnReady,selectedIds = [], previewToken }) { | |||||
| // const [_searchCriteria, set_searchCriteria] = React.useState(previewSearchCriteria); | |||||
| const navigate = useNavigate() | const navigate = useNavigate() | ||||
| // const [rows, setRows] = React.useState([]); | // const [rows, setRows] = React.useState([]); | ||||
| @@ -27,9 +28,21 @@ export default function SearchTable({ searchCriteria }) { | |||||
| } | } | ||||
| } | } | ||||
| React.useEffect(() => { | |||||
| set_searchCriteria(searchCriteria); | |||||
| }, [searchCriteria]); | |||||
| const doLoad = React.useMemo(() => { | |||||
| if (!selectedIds?.length) return undefined; | |||||
| return { | |||||
| url: GFIMIS_LIST, | |||||
| params: { | |||||
| ...previewSearchCriteria, | |||||
| paymentId: selectedIds.join(',') | |||||
| } | |||||
| }; | |||||
| }, [previewSearchCriteria, selectedIds, previewToken]); | |||||
| // React.useEffect(() => { | |||||
| // set_searchCriteria(previewSearchCriteria); | |||||
| // }, [previewSearchCriteria]); | |||||
| const handleEditClick = (params) => () => { | const handleEditClick = (params) => () => { | ||||
| navigate('/paymentPage/details/' + params.row.id); | navigate('/paymentPage/details/' + params.row.id); | ||||
| @@ -39,7 +52,7 @@ export default function SearchTable({ searchCriteria }) { | |||||
| { | { | ||||
| id: 'paymentMethod', | id: 'paymentMethod', | ||||
| field: 'paymentMethod', | field: 'paymentMethod', | ||||
| headerName: 'Payment Means', | |||||
| headerName: 'GFMIS Payment Method', | |||||
| flex: 4, | flex: 4, | ||||
| renderCell: (params) => { | renderCell: (params) => { | ||||
| let paymentMethod = params.row.paymentMethod; | let paymentMethod = params.row.paymentMethod; | ||||
| @@ -60,20 +73,15 @@ export default function SearchTable({ searchCriteria }) { | |||||
| return ( | return ( | ||||
| <div style={{ width: '100%' }}> | <div style={{ width: '100%' }}> | ||||
| <FiDataGrid | <FiDataGrid | ||||
| key={previewToken} | |||||
| sx={_sx} | sx={_sx} | ||||
| rowHeight={80} | rowHeight={80} | ||||
| columns={columns} | columns={columns} | ||||
| customPageSize={10} | customPageSize={10} | ||||
| onRowDoubleClick={handleEditClick} | onRowDoubleClick={handleEditClick} | ||||
| doLoad={React.useMemo(() => ({ | |||||
| url: GFIMIS_LIST, | |||||
| params: _searchCriteria, | |||||
| callback: function () { | |||||
| // setRows(responseData?.records); | |||||
| } | |||||
| }), [_searchCriteria])} | |||||
| applyGridOnReady={onPreviewGridOnReady} | |||||
| doLoad={doLoad} | |||||
| /> | /> | ||||
| </div> | </div> | ||||
| ); | ); | ||||
| @@ -2,8 +2,8 @@ | |||||
| import { | import { | ||||
| Button, | Button, | ||||
| Grid, | Grid, | ||||
| // TextField, | |||||
| // Autocomplete, | |||||
| TextField, | |||||
| Autocomplete, | |||||
| Typography | Typography | ||||
| } from '@mui/material'; | } from '@mui/material'; | ||||
| import MainCard from "components/MainCard"; | import MainCard from "components/MainCard"; | ||||
| @@ -14,6 +14,7 @@ import {PNSPS_BUTTON_THEME} from "../../themes/buttonConst"; | |||||
| import {ThemeProvider} from "@emotion/react"; | import {ThemeProvider} from "@emotion/react"; | ||||
| // import * as ComboData from "utils/ComboData"; | // import * as ComboData from "utils/ComboData"; | ||||
| import * as DateUtils from "utils/DateUtils"; | import * as DateUtils from "utils/DateUtils"; | ||||
| import * as ComboData from "utils/ComboData"; | |||||
| import {DatePicker} from "@mui/x-date-pickers/DatePicker"; | import {DatePicker} from "@mui/x-date-pickers/DatePicker"; | ||||
| import dayjs from "dayjs"; | import dayjs from "dayjs"; | ||||
| @@ -21,56 +22,82 @@ import {DemoItem} from "@mui/x-date-pickers/internals/demo"; | |||||
| import {LocalizationProvider} from "@mui/x-date-pickers/LocalizationProvider"; | import {LocalizationProvider} from "@mui/x-date-pickers/LocalizationProvider"; | ||||
| import {AdapterDayjs} from "@mui/x-date-pickers/AdapterDayjs"; | import {AdapterDayjs} from "@mui/x-date-pickers/AdapterDayjs"; | ||||
| // ==============================|| DASHBOARD - DEFAULT ||============================== // | // ==============================|| DASHBOARD - DEFAULT ||============================== // | ||||
| const SearchPublicNoticeForm = ({ applySearch, generateXML, searchCriteria }) => { | |||||
| const SearchPublicNoticeForm = ({ applySearch, generateXML, searchCriteria, onGridReady, selectedIds = [] }) => { | |||||
| // const [minDate, setMinDate] = React.useState(searchCriteria.dateFrom); | // const [minDate, setMinDate] = React.useState(searchCriteria.dateFrom); | ||||
| const [minDate, setMinDate] = React.useState(searchCriteria.dateFrom); | const [minDate, setMinDate] = React.useState(searchCriteria.dateFrom); | ||||
| const [maxDate] = React.useState(searchCriteria.dateFrom); | |||||
| // const [status, setStatus] = React.useState(ComboData.paymentStatus[0]); | |||||
| const [maxDate, setMaxDate] = React.useState(searchCriteria.dateTo); | |||||
| const [fromDateValue, setFromDateValue] = React.useState("dd / mm / yyyy"); | const [fromDateValue, setFromDateValue] = React.useState("dd / mm / yyyy"); | ||||
| const [toDateValue, setToDateValue] = React.useState("dd / mm / yyyy"); | |||||
| const [payMethod, setPayMethod] = React.useState(ComboData.payMethod[0]); | |||||
| // const [status, setStatus] = React.useState(ComboData.paymentStatus[0]); | |||||
| const marginBottom = 2.5; | |||||
| const { | |||||
| // register, | |||||
| const { | |||||
| register, | |||||
| handleSubmit, | handleSubmit, | ||||
| } = useForm() | } = useForm() | ||||
| React.useEffect(() => { | React.useEffect(() => { | ||||
| setFromDateValue(minDate); | setFromDateValue(minDate); | ||||
| }, [minDate]); | }, [minDate]); | ||||
| React.useEffect(() => { | |||||
| setToDateValue(maxDate); | |||||
| }, [maxDate]); | |||||
| const toPayMethodArray = (opt) => { | |||||
| if (!opt || opt.type === 'all') return []; | |||||
| return Array.isArray(opt.type) ? opt.type : [opt.type]; | |||||
| }; | |||||
| const onSubmit = () => { | const onSubmit = () => { | ||||
| let sentDateFrom = ""; | let sentDateFrom = ""; | ||||
| let sentDateTo = ""; | |||||
| if (fromDateValue != "dd / mm / yyyy") { | if (fromDateValue != "dd / mm / yyyy") { | ||||
| sentDateFrom = DateUtils.dateValue(fromDateValue) | sentDateFrom = DateUtils.dateValue(fromDateValue) | ||||
| } | } | ||||
| if (toDateValue != "dd / mm / yyyy") { | |||||
| sentDateTo = DateUtils.dateValue(toDateValue) | |||||
| } | |||||
| const temp = { | const temp = { | ||||
| // code: data.code, | // code: data.code, | ||||
| // transNo: data.transNo, | // transNo: data.transNo, | ||||
| dateFrom: sentDateFrom, | dateFrom: sentDateFrom, | ||||
| // dateTo: data.dateTo, | |||||
| dateTo: sentDateTo, | |||||
| // paymentId: selectedIds.join(','), | |||||
| payMethod : toPayMethodArray(payMethod), | |||||
| // status : (status?.type && status?.type != 'all') ? status?.type : "", | // status : (status?.type && status?.type != 'all') ? status?.type : "", | ||||
| }; | }; | ||||
| applySearch(temp); | applySearch(temp); | ||||
| }; | }; | ||||
| const generateHandler = () => { | const generateHandler = () => { | ||||
| if (!selectedIds || selectedIds.length === 0) { | |||||
| alert('No payment is selected.'); | |||||
| return; | |||||
| } | |||||
| let sentDateFrom = ""; | let sentDateFrom = ""; | ||||
| let sentDateTo = ""; | |||||
| if (fromDateValue != "dd / mm / yyyy") { | |||||
| sentDateFrom = DateUtils.dateValue(fromDateValue) | |||||
| if (fromDateValue !== "dd / mm / yyyy") { | |||||
| sentDateFrom = DateUtils.dateValue(fromDateValue); | |||||
| } | |||||
| if (toDateValue !== "dd / mm / yyyy") { | |||||
| sentDateTo = DateUtils.dateValue(toDateValue); | |||||
| } | } | ||||
| // const dateTo = getValues("dateTo") | |||||
| const temp = { | const temp = { | ||||
| // code: data.code, | |||||
| // transNo: data.transNo, | |||||
| dateFrom: sentDateFrom, | dateFrom: sentDateFrom, | ||||
| dateTo: "", | |||||
| // status : (status?.type && status?.type != 'all') ? status?.type : "", | |||||
| dateTo: sentDateTo, | |||||
| }; | }; | ||||
| generateXML(temp); | generateXML(temp); | ||||
| } | |||||
| }; | |||||
| return ( | return ( | ||||
| @@ -90,8 +117,7 @@ const SearchPublicNoticeForm = ({ applySearch, generateXML, searchCriteria }) => | |||||
| {/*row 2*/} | {/*row 2*/} | ||||
| <Grid container display="flex" alignItems={"center"}> | <Grid container display="flex" alignItems={"center"}> | ||||
| <Grid item xs={9} s={6} md={4} lg={4} sx={{ ml: 3, mr: 3, mb: 3 }}> | |||||
| <Grid item xs={9} s={6} md={3} lg={3} sx={{ ml: 3, mr: 3, mb: 3 }}> | |||||
| <LocalizationProvider dateAdapter={AdapterDayjs}> | <LocalizationProvider dateAdapter={AdapterDayjs}> | ||||
| <DemoItem components={['DatePicker']}> | <DemoItem components={['DatePicker']}> | ||||
| <DatePicker | <DatePicker | ||||
| @@ -104,7 +130,7 @@ const SearchPublicNoticeForm = ({ applySearch, generateXML, searchCriteria }) => | |||||
| // }, | // }, | ||||
| }} | }} | ||||
| format="DD/MM/YYYY" | format="DD/MM/YYYY" | ||||
| label="Credit Date" | |||||
| label="From Date" | |||||
| value={minDate === null ? null : dayjs(minDate)} | value={minDate === null ? null : dayjs(minDate)} | ||||
| maxDate={maxDate === null ? null : dayjs(maxDate)} | maxDate={maxDate === null ? null : dayjs(maxDate)} | ||||
| onChange={(newValue) => { | onChange={(newValue) => { | ||||
| @@ -118,22 +144,65 @@ const SearchPublicNoticeForm = ({ applySearch, generateXML, searchCriteria }) => | |||||
| </LocalizationProvider> | </LocalizationProvider> | ||||
| </Grid> | </Grid> | ||||
| <Grid item xs={9} s={6} md={4} lg={4} sx={{ ml: 3, mr: 3, mb: 3 }}> | |||||
| {/* <TextField | |||||
| fullWidth | |||||
| InputLabelProps={{ | |||||
| shrink: true | |||||
| }} | |||||
| {...register("dateTo")} | |||||
| InputProps={{ inputProps: { min: minDate } }} | |||||
| onChange={(newValue) => { | |||||
| setMaxDate(DateUtils.dateValue(newValue)); | |||||
| }} | |||||
| id="dateTo" | |||||
| type="date" | |||||
| label="To" | |||||
| defaultValue={searchCriteria.dateTo} | |||||
| /> */} | |||||
| <Grid item xs={9} s={6} md={3} lg={3} sx={{ ml: 3, mr: 3, mb: 3 }}> | |||||
| <LocalizationProvider dateAdapter={AdapterDayjs}> | |||||
| <DemoItem components={['DatePicker']}> | |||||
| <DatePicker | |||||
| id="dateTo" | |||||
| // onError={(newError) => setReceiptFromError(newError)} | |||||
| slotProps={{ | |||||
| field: { readOnly: true, }, | |||||
| // textField: { | |||||
| // helperText: receiptFromErrorMessage, | |||||
| // }, | |||||
| }} | |||||
| format="DD/MM/YYYY" | |||||
| label="To Date" | |||||
| value={maxDate === null ? null : dayjs(maxDate)} | |||||
| minDate={minDate === null ? null : dayjs(minDate)} | |||||
| onChange={(newValue) => { | |||||
| // console.log(newValue) | |||||
| if(newValue!=null){ | |||||
| setMaxDate(newValue); | |||||
| } | |||||
| }} | |||||
| /> | |||||
| </DemoItem > | |||||
| </LocalizationProvider> | |||||
| </Grid> | |||||
| <Grid item xs={9} s={6} md={3} lg={3} sx={{ ml: 3, mr: 3, mb: marginBottom }}> | |||||
| <Autocomplete | |||||
| {...register("payMethod")} | |||||
| disablePortal={false} | |||||
| size="small" | |||||
| id="payMethod" | |||||
| filterOptions={(options) => options} | |||||
| options={ComboData.payMethod} | |||||
| value={payMethod} | |||||
| getOptionLabel={(option) => option.label} | |||||
| inputValue={payMethod?.label ? payMethod?.label : ""} | |||||
| onChange={(event, newValue) => { | |||||
| if(newValue==null){ | |||||
| setPayMethod(ComboData.payMethod[0]); | |||||
| }else{ | |||||
| setPayMethod(newValue); | |||||
| } | |||||
| }} | |||||
| sx={{ | |||||
| '& .MuiInputBase-root': { alignItems: 'center' }, | |||||
| '& .MuiAutocomplete-endAdornment': { top: '50%', transform: 'translateY(-50%)' }, | |||||
| '& .MuiOutlinedInput-root': { height: 40 } | |||||
| }} | |||||
| renderInput={(params) => ( | |||||
| <TextField {...params} | |||||
| label="Payment Method" | |||||
| /> | |||||
| )} | |||||
| InputLabelProps={{ | |||||
| shrink: true | |||||
| }} | |||||
| /> | |||||
| </Grid> | </Grid> | ||||
| {/* <Grid item xs={9} s={6} md={4} lg={3}> | {/* <Grid item xs={9} s={6} md={4} lg={3}> | ||||
| @@ -146,8 +215,9 @@ const SearchPublicNoticeForm = ({ applySearch, generateXML, searchCriteria }) => | |||||
| <Button | <Button | ||||
| variant="contained" | variant="contained" | ||||
| type="submit" | type="submit" | ||||
| disabled={onGridReady} | |||||
| > | > | ||||
| Preview | |||||
| Search | |||||
| </Button> | </Button> | ||||
| </Grid> | </Grid> | ||||
| @@ -155,9 +225,11 @@ const SearchPublicNoticeForm = ({ applySearch, generateXML, searchCriteria }) => | |||||
| <Button | <Button | ||||
| variant="contained" | variant="contained" | ||||
| onClick={generateHandler} | onClick={generateHandler} | ||||
| > | |||||
| disabled={!selectedIds || selectedIds.length === 0} | |||||
| > | |||||
| Generate | Generate | ||||
| </Button> | </Button> | ||||
| </Grid> | </Grid> | ||||
| </ThemeProvider> | </ThemeProvider> | ||||
| </Grid> | </Grid> | ||||
| @@ -0,0 +1,156 @@ | |||||
| // material-ui | |||||
| import * as React from 'react'; | |||||
| import * as DateUtils from "utils/DateUtils"; | |||||
| import {PAYMENT_GFMIS_LIST} from "utils/ApiPathConst"; | |||||
| // import * as HttpUtils from "utils/HttpUtils"; | |||||
| import * as FormatUtils from "utils/FormatUtils" | |||||
| // import { useNavigate } from "react-router-dom"; | |||||
| import { FiDataGrid } from "components/FiDataGrid"; | |||||
| import { clickableLink } from 'utils/CommonFunction'; | |||||
| import { getPaymentMethodByCode} from "auth/utils"; | |||||
| import { | |||||
| // Checkbox, | |||||
| // Dialog, DialogTitle, DialogContent, DialogActions, | |||||
| // Button,Typography | |||||
| // MenuItem | |||||
| } from '@mui/material'; | |||||
| // ==============================|| EVENT TABLE ||============================== // | |||||
| export default function SearchPaymentTable({ searchCriteria, applyGridOnReady, applySearch, selectedIds = [], onSelectionChange,}) { | |||||
| const [_searchCriteria, set_searchCriteria] = React.useState(searchCriteria); | |||||
| // const navigate = useNavigate() | |||||
| const _sx = { | |||||
| padding: "4 2 4 2", | |||||
| boxShadow: 1, | |||||
| border: 1, | |||||
| borderColor: '#DDD', | |||||
| '& .MuiDataGrid-cell': { | |||||
| borderTop: 1, | |||||
| borderBottom: 1, | |||||
| borderColor: "#EEE" | |||||
| }, | |||||
| '& .MuiDataGrid-footerContainer': { | |||||
| border: 1, | |||||
| borderColor: "#EEE" | |||||
| } | |||||
| } | |||||
| React.useEffect(() => { | |||||
| set_searchCriteria(searchCriteria); | |||||
| }, [searchCriteria]); | |||||
| const selectedIdsRef = React.useRef(selectedIds); | |||||
| React.useEffect(() => { | |||||
| selectedIdsRef.current = selectedIds; | |||||
| }, [selectedIds]); | |||||
| const columns = [ | |||||
| { | |||||
| id: 'appNos', | |||||
| field: 'appNos', | |||||
| headerName: 'Application No.', | |||||
| flex: 1, | |||||
| minWidth: 150, | |||||
| renderCell: (params) => { | |||||
| let appNo = params.row.appNos; | |||||
| return <div style={{ marginTop: 2, marginBottom: 2 }}>{appNo}</div> | |||||
| }, | |||||
| }, | |||||
| { | |||||
| field: 'actions', | |||||
| headerName: 'Transaction No.', | |||||
| flex: 1, | |||||
| minWidth: 200, | |||||
| cellClassName: 'actions', | |||||
| renderCell: (params) => { | |||||
| return clickableLink('/paymentPage/details/' + params.row.id, params.row.transNo); | |||||
| }, | |||||
| }, | |||||
| { | |||||
| id: 'transDateTime', | |||||
| field: 'transDateTime', | |||||
| headerName: 'Transaction Date', | |||||
| flex: 1, | |||||
| minWidth: 150, | |||||
| // sorting/filtering uses this value | |||||
| valueGetter: (params) => DateUtils.toDate(params?.value), | |||||
| // display uses this (params.value is the *Date* returned above) | |||||
| valueFormatter: (params) => { | |||||
| const d = params.value; // Date or Invalid Date | |||||
| return d instanceof Date && !isNaN(d.getTime()) | |||||
| ? DateUtils.dateStr(d) | |||||
| : ""; | |||||
| }, | |||||
| // make sorting 100% deterministic | |||||
| sortComparator: (v1, v2) => { | |||||
| const t1 = v1 instanceof Date && !isNaN(v1.getTime()) ? v1.getTime() : -Infinity; | |||||
| const t2 = v2 instanceof Date && !isNaN(v2.getTime()) ? v2.getTime() : -Infinity; | |||||
| return t1 - t2; | |||||
| } | |||||
| }, | |||||
| { | |||||
| field: 'payMethod', | |||||
| headerName: 'Payment Method', | |||||
| flex: 1, | |||||
| width: 150, | |||||
| renderCell: (params) => { | |||||
| return getPaymentMethodByCode(params?.value); | |||||
| } | |||||
| }, | |||||
| { | |||||
| id: 'payAmount', | |||||
| field: 'payAmount', | |||||
| headerName: 'Amount ($)', | |||||
| width: 150, | |||||
| valueGetter: (params) => { | |||||
| return (params?.value) ? "$ " + FormatUtils.currencyFormat(params?.value) : ""; | |||||
| } | |||||
| }, | |||||
| ]; | |||||
| return ( | |||||
| <div style={{ width: '100%' }}> | |||||
| <FiDataGrid | |||||
| sx={_sx} | |||||
| rowHeight={60} | |||||
| columns={columns} | |||||
| height={500} | |||||
| autoHeight={false} | |||||
| pagination={false} | |||||
| applyGridOnReady={applyGridOnReady} | |||||
| applySearch={applySearch} | |||||
| checkboxSelection | |||||
| disableRowSelectionOnClick | |||||
| rowSelectionModel={selectedIds} | |||||
| onRowSelectionModelChange={(ids) => onSelectionChange?.(ids)} | |||||
| doLoad={React.useMemo(() => ({ | |||||
| url: PAYMENT_GFMIS_LIST, | |||||
| params: _searchCriteria, | |||||
| callback: (responseData) => { | |||||
| const newIds = (responseData?.records ?? []).map(r => r.id); | |||||
| const currentSelected = selectedIdsRef.current; | |||||
| if (newIds.length === 0) { | |||||
| onSelectionChange?.([]); | |||||
| return; | |||||
| } | |||||
| if (!currentSelected || currentSelected.length === 0) { | |||||
| onSelectionChange?.(newIds); | |||||
| } else { | |||||
| const stillValid = currentSelected.filter(id => newIds.includes(id)); | |||||
| onSelectionChange?.(stillValid); | |||||
| } | |||||
| } | |||||
| }), [_searchCriteria])} | |||||
| /> | |||||
| </div> | |||||
| ); | |||||
| } | |||||
| @@ -2,7 +2,9 @@ | |||||
| import { | import { | ||||
| Grid, | Grid, | ||||
| Typography, | Typography, | ||||
| Stack | |||||
| Stack, | |||||
| Button, | |||||
| Dialog, DialogTitle, DialogContent, DialogActions, | |||||
| } from '@mui/material'; | } from '@mui/material'; | ||||
| import MainCard from "components/MainCard"; | import MainCard from "components/MainCard"; | ||||
| import {GEN_GFMIS_XML} from "utils/ApiPathConst"; | import {GEN_GFMIS_XML} from "utils/ApiPathConst"; | ||||
| @@ -10,10 +12,17 @@ import * as React from "react"; | |||||
| import * as HttpUtils from "utils/HttpUtils"; | import * as HttpUtils from "utils/HttpUtils"; | ||||
| import * as DateUtils from "utils/DateUtils"; | import * as DateUtils from "utils/DateUtils"; | ||||
| import {DatePicker} from "@mui/x-date-pickers/DatePicker"; | |||||
| import dayjs from "dayjs"; | |||||
| import {DemoItem} from "@mui/x-date-pickers/internals/demo"; | |||||
| import {LocalizationProvider} from "@mui/x-date-pickers/LocalizationProvider"; | |||||
| import {AdapterDayjs} from "@mui/x-date-pickers/AdapterDayjs"; | |||||
| import Loadable from 'components/Loadable'; | import Loadable from 'components/Loadable'; | ||||
| const LoadingComponent = Loadable(React.lazy(() => import('pages/extra-pages/LoadingComponent'))); | const LoadingComponent = Loadable(React.lazy(() => import('pages/extra-pages/LoadingComponent'))); | ||||
| const SearchForm = Loadable(React.lazy(() => import('./SearchForm'))); | const SearchForm = Loadable(React.lazy(() => import('./SearchForm'))); | ||||
| const EventTable = Loadable(React.lazy(() => import('./DataGrid'))); | const EventTable = Loadable(React.lazy(() => import('./DataGrid'))); | ||||
| const TransactionTable = Loadable(React.lazy(() => import('./TransactionDataGrid'))); | |||||
| import titleBackgroundImg from 'assets/images/dashboard/gazette-bar.png' | import titleBackgroundImg from 'assets/images/dashboard/gazette-bar.png' | ||||
| const BackgroundHead = { | const BackgroundHead = { | ||||
| @@ -30,27 +39,78 @@ const BackgroundHead = { | |||||
| const Index = () => { | const Index = () => { | ||||
| const [isTxLoading, setIsTxLoading] = React.useState(false); | |||||
| const [autoPreviewPending, setAutoPreviewPending] = React.useState(false); | |||||
| const [searchCriteria, setSearchCriteria] = React.useState({ | const [searchCriteria, setSearchCriteria] = React.useState({ | ||||
| dateFrom: DateUtils.dateValue(new Date()), | |||||
| // dateFrom: DateUtils.dateValue(new Date().setDate(new Date().getDate()-14)), | |||||
| dateTo: DateUtils.dateValue(new Date()), | |||||
| dateFrom: DateUtils.dateValue(new Date().setDate(new Date().getDate()-14)), | |||||
| }); | |||||
| const [previewSearchCriteria, setPreviewSearchCriteria] = React.useState({ | |||||
| dateTo: DateUtils.dateValue(new Date()), | |||||
| dateFrom: DateUtils.dateValue(new Date().setDate(new Date().getDate()-14)), | |||||
| }); | }); | ||||
| const [onReady, setOnReady] = React.useState(false); | const [onReady, setOnReady] = React.useState(false); | ||||
| const [onGridReady, setGridOnReady] = React.useState(false); | |||||
| const [isPreviewLoading, setIsPreviewLoading] = React.useState(false); | |||||
| const [isPopUp, setIsPopUp] = React.useState(false); | |||||
| const [downloadInput, setDownloadInput] = React.useState(); | |||||
| const [selectedIds, setSelectedIds] = React.useState([]); | |||||
| const [inputDate, setInputDate] = React.useState(searchCriteria.dateTo); | |||||
| const [inputDateValue, setInputDateValue] = React.useState("dd / mm / yyyy"); | |||||
| const [previewToken, setPreviewToken] = React.useState(0); | |||||
| React.useEffect(() => { | |||||
| setInputDateValue(inputDate); | |||||
| }, [inputDate]); | |||||
| React.useEffect(() => { | React.useEffect(() => { | ||||
| setOnReady(true); | setOnReady(true); | ||||
| }, [searchCriteria]); | }, [searchCriteria]); | ||||
| React.useEffect(() => { | |||||
| if (!autoPreviewPending) return; | |||||
| // wait for tx grid load complete, and for auto-selection to happen | |||||
| if (isTxLoading) return; | |||||
| if (!selectedIds || selectedIds.length === 0) return; | |||||
| // trigger preview exactly once | |||||
| const withToken = { ...searchCriteria, __ts: Date.now() }; | |||||
| setPreviewSearchCriteria(withToken); | |||||
| setPreviewToken(t => t + 1); | |||||
| function downloadXML(input) { | |||||
| // console.log(input) | |||||
| setAutoPreviewPending(false); | |||||
| }, [autoPreviewPending, isTxLoading, selectedIds, searchCriteria]); | |||||
| React.useEffect(() => { | |||||
| if (selectedIds.length === 0) { | |||||
| setPreviewSearchCriteria({}); | |||||
| setPreviewToken(t => t + 1); // forces preview grid remount -> clears rows | |||||
| } | |||||
| }, [selectedIds]); | |||||
| function downloadXML() { | |||||
| console.log(selectedIds.join(',')) | |||||
| setIsPopUp(false) | |||||
| let sentDateFrom = ""; | |||||
| if (inputDateValue != "dd / mm / yyyy") { | |||||
| sentDateFrom = DateUtils.dateValue(inputDateValue) | |||||
| } | |||||
| HttpUtils.get({ | HttpUtils.get({ | ||||
| url: GEN_GFMIS_XML + "/today", | url: GEN_GFMIS_XML + "/today", | ||||
| params:{ | params:{ | ||||
| // dateTo: input.dateTo, | |||||
| dateFrom: input.dateFrom, | |||||
| dateTo: downloadInput.dateTo, | |||||
| dateFrom: downloadInput.dateFrom, | |||||
| inputDate: sentDateFrom, | |||||
| paymentId: selectedIds.join(',') | |||||
| }, | }, | ||||
| onSuccess: (responseData) => { | onSuccess: (responseData) => { | ||||
| console.log(responseData) | |||||
| // console.log(responseData) | |||||
| const parser = new DOMParser(); | const parser = new DOMParser(); | ||||
| const xmlDoc = parser.parseFromString(responseData, 'application/xml'); | const xmlDoc = parser.parseFromString(responseData, 'application/xml'); | ||||
| // Get the DCBHeader element | // Get the DCBHeader element | ||||
| @@ -75,7 +135,7 @@ const Index = () => { | |||||
| const updatedXmlString = new XMLSerializer().serializeToString(xmlDoc); | const updatedXmlString = new XMLSerializer().serializeToString(xmlDoc); | ||||
| const filename = xmlDoc.querySelector('FileHeader').getAttribute('H_Filename'); | const filename = xmlDoc.querySelector('FileHeader').getAttribute('H_Filename'); | ||||
| console.log(updatedXmlString) | |||||
| // console.log(updatedXmlString) | |||||
| const blob = new Blob([updatedXmlString], { type: 'application/xml' }); | const blob = new Blob([updatedXmlString], { type: 'application/xml' }); | ||||
| // Create a download link | // Create a download link | ||||
| const link = document.createElement('a'); | const link = document.createElement('a'); | ||||
| @@ -97,11 +157,42 @@ const Index = () => { | |||||
| function applySearch(input) { | function applySearch(input) { | ||||
| setAutoPreviewPending(true); // NEW: ask for auto-preview after grid loads | |||||
| setGridOnReady(true); | |||||
| setSelectedIds([]); | |||||
| setPreviewSearchCriteria({}); | |||||
| setSearchCriteria(input); | setSearchCriteria(input); | ||||
| setInputDate(input.dateFrom); | |||||
| } | |||||
| function previewSearch() { | |||||
| if (selectedIds.length === 0) return; | |||||
| setIsPopUp(false); | |||||
| setIsPreviewLoading(true); | |||||
| const withToken = { ...searchCriteria, __ts: Date.now() }; | |||||
| setPreviewSearchCriteria(withToken); | |||||
| setPreviewToken(t => t + 1); | |||||
| } | |||||
| function onPreviewGridOnReady(isLoading) { | |||||
| // FiDataGrid calls this with true/false | |||||
| setIsPreviewLoading(isLoading); | |||||
| } | |||||
| function applyGridOnReady(isLoading) { | |||||
| setGridOnReady(isLoading); // keep existing behavior for disabling Search | |||||
| setIsTxLoading(isLoading); // NEW: remember tx grid loading | |||||
| } | } | ||||
| function generateXML(input) { | function generateXML(input) { | ||||
| downloadXML(input); | |||||
| setDownloadInput(input); | |||||
| setIsPopUp(true) | |||||
| } | } | ||||
| return ( | return ( | ||||
| @@ -121,10 +212,13 @@ const Index = () => { | |||||
| {/*row 1*/} | {/*row 1*/} | ||||
| <Grid item xs={12} md={12} lg={12} sx={{mb:-1}}> | <Grid item xs={12} md={12} lg={12} sx={{mb:-1}}> | ||||
| <SearchForm | <SearchForm | ||||
| applySearch={applySearch} | |||||
| generateXML={generateXML} | |||||
| searchCriteria={searchCriteria} | |||||
| applySearch={applySearch} | |||||
| generateXML={generateXML} | |||||
| searchCriteria={searchCriteria} | |||||
| onGridReady={onGridReady} | |||||
| selectedIds={selectedIds} | |||||
| /> | /> | ||||
| </Grid> | </Grid> | ||||
| {/*row 2*/} | {/*row 2*/} | ||||
| <Grid item xs={12} md={12} lg={12}> | <Grid item xs={12} md={12} lg={12}> | ||||
| @@ -133,11 +227,83 @@ const Index = () => { | |||||
| content={false} | content={false} | ||||
| sx={{width: "-webkit-fill-available"}} | sx={{width: "-webkit-fill-available"}} | ||||
| > | > | ||||
| <EventTable | |||||
| <TransactionTable | |||||
| searchCriteria={searchCriteria} | searchCriteria={searchCriteria} | ||||
| applyGridOnReady={applyGridOnReady} | |||||
| applySearch={applySearch} | |||||
| selectedIds={selectedIds} | |||||
| onSelectionChange={setSelectedIds} | |||||
| /> | /> | ||||
| </MainCard> | </MainCard> | ||||
| </Grid> | </Grid> | ||||
| <Grid item xs={12} md={12} lg={12} sx={{ml:2}}> | |||||
| <Button | |||||
| variant="contained" | |||||
| onClick={previewSearch} | |||||
| disabled={isPreviewLoading || selectedIds.length === 0} | |||||
| > | |||||
| Preview | |||||
| </Button> | |||||
| </Grid> | |||||
| <Grid item xs={12} md={12} lg={12}> | |||||
| <MainCard elevation={0} | |||||
| border={false} | |||||
| content={false} | |||||
| sx={{width: "-webkit-fill-available"}} | |||||
| > | |||||
| <EventTable | |||||
| previewSearchCriteria={previewSearchCriteria} | |||||
| onPreviewGridOnReady={onPreviewGridOnReady} | |||||
| selectedIds={selectedIds} | |||||
| previewToken={previewToken} | |||||
| /> | |||||
| </MainCard> | |||||
| </Grid> | |||||
| <Dialog | |||||
| open={isPopUp} | |||||
| onClose={() => setIsPopUp(false)} | |||||
| PaperProps={{ | |||||
| sx: { | |||||
| minWidth: '40vw', | |||||
| maxWidth: { xs: '90vw', s: '90vw', m: '70vw', lg: '70vw' }, | |||||
| maxHeight: { xs: '90vh', s: '70vh', m: '70vh', lg: '60vh' } | |||||
| } | |||||
| }} | |||||
| > | |||||
| <DialogTitle> Bank Statement Collection Date </DialogTitle> | |||||
| <DialogContent style={{ display: 'flex', }}> | |||||
| <LocalizationProvider dateAdapter={AdapterDayjs}> | |||||
| <DemoItem components={['DatePicker']}> | |||||
| <DatePicker | |||||
| id="dateFrom" | |||||
| // onError={(newError) => setReceiptFromError(newError)} | |||||
| slotProps={{ | |||||
| field: { readOnly: true, }, | |||||
| // textField: { | |||||
| // helperText: receiptFromErrorMessage, | |||||
| // }, | |||||
| }} | |||||
| format="DD/MM/YYYY" | |||||
| // label="Credit Date" | |||||
| value={inputDate === null ? null : dayjs(inputDate)} | |||||
| minDate={searchCriteria.dateFrom === null ? null : dayjs(searchCriteria.dateFrom)} | |||||
| onChange={(newValue) => { | |||||
| // console.log(newValue) | |||||
| if(newValue!=null){ | |||||
| setInputDate(newValue); | |||||
| } | |||||
| }} | |||||
| /> | |||||
| </DemoItem > | |||||
| </LocalizationProvider> | |||||
| </DialogContent> | |||||
| <DialogActions> | |||||
| <Button onClick={() => setIsPopUp(false)}><Typography variant="h5">Cancel</Typography></Button> | |||||
| <Button onClick={() => downloadXML()}><Typography variant="h5">Confirm</Typography></Button> | |||||
| </DialogActions> | |||||
| </Dialog> | |||||
| </Grid> | </Grid> | ||||
| ); | ); | ||||
| }; | }; | ||||
| @@ -7,7 +7,7 @@ import {GET_ISSUE} from "utils/ApiPathConst"; | |||||
| // ==============================|| EVENT TABLE ||============================== // | // ==============================|| EVENT TABLE ||============================== // | ||||
| export default function GazetteIssueTable({ searchCriteria }) { | |||||
| export default function GazetteIssueTable({ searchCriteria, applyGridOnReady }) { | |||||
| const [_searchCriteria, set_searchCriteria] = React.useState(searchCriteria); | const [_searchCriteria, set_searchCriteria] = React.useState(searchCriteria); | ||||
| React.useEffect(() => { | React.useEffect(() => { | ||||
| @@ -100,10 +100,15 @@ export default function GazetteIssueTable({ searchCriteria }) { | |||||
| customPageSize={10} | customPageSize={10} | ||||
| // onRowDoubleClick={handleRowDoubleClick} | // onRowDoubleClick={handleRowDoubleClick} | ||||
| getRowHeight={() => 'auto'} | getRowHeight={() => 'auto'} | ||||
| doLoad={{ | |||||
| applyGridOnReady={applyGridOnReady} | |||||
| // doLoad={{ | |||||
| // url: GET_ISSUE, | |||||
| // params: _searchCriteria, | |||||
| // }} | |||||
| doLoad={React.useMemo(() => ({ | |||||
| url: GET_ISSUE, | url: GET_ISSUE, | ||||
| params: _searchCriteria, | params: _searchCriteria, | ||||
| }} | |||||
| }), [_searchCriteria])} | |||||
| /> | /> | ||||
| </div> | </div> | ||||
| ); | ); | ||||
| @@ -27,9 +27,9 @@ const SearchGazetteIssueForm = ({ applyExport, comboData, waitDownload}) => { | |||||
| handleSubmit } = useForm() | handleSubmit } = useForm() | ||||
| const onSubmit = () => { | const onSubmit = () => { | ||||
| console.log(selectedYear) | |||||
| // console.log(selectedYear) | |||||
| if (selectedYear !=null && Object.keys(selectedYear).length>0){ | if (selectedYear !=null && Object.keys(selectedYear).length>0){ | ||||
| console.log("okkkkkkkkkkkkkkkk") | |||||
| // console.log("okkkkkkkkkkkkkkkk") | |||||
| const temp = { | const temp = { | ||||
| year: selectedYear.label, | year: selectedYear.label, | ||||
| }; | }; | ||||
| @@ -81,9 +81,9 @@ const SearchGazetteIssueForm = ({ applyExport, comboData, waitDownload}) => { | |||||
| setSelectedYear(newValue); | setSelectedYear(newValue); | ||||
| }} | }} | ||||
| sx={{ | sx={{ | ||||
| "& .MuiInputBase-root": { height: "41px" }, | |||||
| "#year-combo": { padding: "0px 0px 0px 0px" }, | |||||
| "& .MuiAutocomplete-endAdornment": { top: "auto" }, | |||||
| '& .MuiInputBase-root': { alignItems: 'center' }, | |||||
| '& .MuiAutocomplete-endAdornment': { top: '50%', transform: 'translateY(-50%)' }, | |||||
| '& .MuiOutlinedInput-root': { height: 40 } | |||||
| }} | }} | ||||
| renderInput={(params) => <TextField {...params} placeholder={""}/>} | renderInput={(params) => <TextField {...params} placeholder={""}/>} | ||||
| /> | /> | ||||
| @@ -16,7 +16,7 @@ import {ThemeProvider} from "@emotion/react"; | |||||
| // ==============================|| DASHBOARD - DEFAULT ||============================== // | // ==============================|| DASHBOARD - DEFAULT ||============================== // | ||||
| const SearchGazetteIssueForm = ({ applySearch, comboData}) => { | |||||
| const SearchGazetteIssueForm = ({ applySearch, comboData, onGridReady}) => { | |||||
| const [selectedYear, setSelectedYear] = React.useState([]); | const [selectedYear, setSelectedYear] = React.useState([]); | ||||
| // const [defaultYear, setDefaultYear] = React.useState(searchCriteria.year); | // const [defaultYear, setDefaultYear] = React.useState(searchCriteria.year); | ||||
| const [comboList, setComboList] = React.useState([]); | const [comboList, setComboList] = React.useState([]); | ||||
| @@ -79,9 +79,9 @@ const SearchGazetteIssueForm = ({ applySearch, comboData}) => { | |||||
| setSelectedYear(newValue); | setSelectedYear(newValue); | ||||
| }} | }} | ||||
| sx={{ | sx={{ | ||||
| "& .MuiInputBase-root": { height: "41px" }, | |||||
| "#year-combo": { padding: "0px 0px 0px 0px" }, | |||||
| "& .MuiAutocomplete-endAdornment": { top: "auto" }, | |||||
| '& .MuiInputBase-root': { alignItems: 'center' }, | |||||
| '& .MuiAutocomplete-endAdornment': { top: '50%', transform: 'translateY(-50%)' }, | |||||
| '& .MuiOutlinedInput-root': { height: 40 } | |||||
| }} | }} | ||||
| renderInput={(params) => <TextField {...params} placeholder={""}/>} | renderInput={(params) => <TextField {...params} placeholder={""}/>} | ||||
| /> | /> | ||||
| @@ -114,6 +114,7 @@ const SearchGazetteIssueForm = ({ applySearch, comboData}) => { | |||||
| <Button | <Button | ||||
| variant="contained" | variant="contained" | ||||
| type="submit" | type="submit" | ||||
| disabled={onGridReady} | |||||
| > | > | ||||
| Search | Search | ||||
| </Button> | </Button> | ||||
| @@ -1,4 +1,3 @@ | |||||
| // material-ui | |||||
| import { | import { | ||||
| Grid, | Grid, | ||||
| Typography, | Typography, | ||||
| @@ -16,7 +15,7 @@ import MainCard from 'components/MainCard'; | |||||
| const ExportForm = Loadable(React.lazy(() => import('./ExportForm'))); | const ExportForm = Loadable(React.lazy(() => import('./ExportForm'))); | ||||
| const SearchForm = Loadable(React.lazy(() => import('./SearchForm'))); | const SearchForm = Loadable(React.lazy(() => import('./SearchForm'))); | ||||
| const GazetteIssueTable = Loadable(React.lazy(() => import('./DataGrid'))) | |||||
| const GazetteIssueTable = Loadable(React.lazy(() => import('./DataGrid'))); | |||||
| const BackgroundHead = { | const BackgroundHead = { | ||||
| backgroundImage: `url(${titleBackgroundImg})`, | backgroundImage: `url(${titleBackgroundImg})`, | ||||
| @@ -26,42 +25,39 @@ const BackgroundHead = { | |||||
| backgroundRepeat: 'no-repeat', | backgroundRepeat: 'no-repeat', | ||||
| backgroundColor: '#0C489E', | backgroundColor: '#0C489E', | ||||
| backgroundPosition: 'right' | backgroundPosition: 'right' | ||||
| } | |||||
| import {PNSPS_LONG_BUTTON_THEME} from "themes/buttonConst"; | |||||
| import {ThemeProvider} from "@emotion/react"; | |||||
| }; | |||||
| import { PNSPS_LONG_BUTTON_THEME } from "themes/buttonConst"; | |||||
| import { ThemeProvider } from "@emotion/react"; | |||||
| import { dateStr_Year } from "utils/DateUtils"; | import { dateStr_Year } from "utils/DateUtils"; | ||||
| import { notifySaveSuccess } from 'utils/CommonFunction'; | import { notifySaveSuccess } from 'utils/CommonFunction'; | ||||
| import { isGrantedAny } from "auth/utils"; | |||||
| import { useIntl } from 'react-intl'; | |||||
| // ==============================|| DASHBOARD - DEFAULT ||============================== // | // ==============================|| DASHBOARD - DEFAULT ||============================== // | ||||
| const Index = () => { | const Index = () => { | ||||
| const intl = useIntl(); | |||||
| const [comboData, setComboData] = React.useState([]); | const [comboData, setComboData] = React.useState([]); | ||||
| const [holidayComboData, setHolidayComboData] = React.useState([]); | const [holidayComboData, setHolidayComboData] = React.useState([]); | ||||
| const [onReady, setOnReady] = React.useState(false); | const [onReady, setOnReady] = React.useState(false); | ||||
| const [onGridReady, setGridOnReady] = React.useState(false); | |||||
| const [onSearchReady, setOnSearchReady] = React.useState(false); | const [onSearchReady, setOnSearchReady] = React.useState(false); | ||||
| const [onExportReady, setOnExportReady] = React.useState(false); | const [onExportReady, setOnExportReady] = React.useState(false); | ||||
| const [searchCriteria, setSearchCriteria] = React.useState({ | const [searchCriteria, setSearchCriteria] = React.useState({ | ||||
| year: dateStr_Year(new Date()), | year: dateStr_Year(new Date()), | ||||
| // dateFrom: DateUtils.dateValue(new Date().setDate(new Date().getDate()-14)), | |||||
| }); | |||||
| const [exportCriteria, setExportCriteria] = React.useState({ | |||||
| // year: dateStr_Year(new Date()), | |||||
| // dateFrom: DateUtils.dateValue(new Date().setDate(new Date().getDate()-14)), | |||||
| }); | }); | ||||
| const [exportCriteria, setExportCriteria] = React.useState({}); | |||||
| const [attachments, setAttachments] = React.useState([]); | const [attachments, setAttachments] = React.useState([]); | ||||
| const [waitImport, setWaitImport] = React.useState(false); | const [waitImport, setWaitImport] = React.useState(false); | ||||
| const [waitDownload, setWaitDownload] = React.useState(false); | const [waitDownload, setWaitDownload] = React.useState(false); | ||||
| const [isWarningPopUp, setIsWarningPopUp] = React.useState(false); | const [isWarningPopUp, setIsWarningPopUp] = React.useState(false); | ||||
| const [warningText, setWarningText] = React.useState(""); | const [warningText, setWarningText] = React.useState(""); | ||||
| const fileInputRef = React.useRef(null); | |||||
| React.useEffect(() => { | React.useEffect(() => { | ||||
| // console.log(searchCriteria) | |||||
| setOnSearchReady(false) | |||||
| setOnSearchReady(false); | |||||
| loadCombo(); | loadCombo(); | ||||
| }, [searchCriteria]); | }, [searchCriteria]); | ||||
| function loadCombo() { | function loadCombo() { | ||||
| HttpUtils.get({ | HttpUtils.get({ | ||||
| @@ -69,9 +65,8 @@ const Index = () => { | |||||
| onSuccess: (responseData) => { | onSuccess: (responseData) => { | ||||
| let combo = responseData; | let combo = responseData; | ||||
| setComboData(combo); | setComboData(combo); | ||||
| setOnSearchReady(true) | |||||
| loadHolidayCombo(true) | |||||
| // setOnReady(true); | |||||
| setOnSearchReady(true); | |||||
| loadHolidayCombo(true); | |||||
| } | } | ||||
| }); | }); | ||||
| } | } | ||||
| @@ -80,25 +75,29 @@ const Index = () => { | |||||
| HttpUtils.get({ | HttpUtils.get({ | ||||
| url: UrlUtils.GET_HOLIDAY_COMBO, | url: UrlUtils.GET_HOLIDAY_COMBO, | ||||
| onSuccess: (responseData) => { | onSuccess: (responseData) => { | ||||
| let combo = responseData | |||||
| setHolidayComboData(combo) | |||||
| setOnExportReady(true) | |||||
| setOnReady(true) | |||||
| let combo = responseData; | |||||
| setHolidayComboData(combo); | |||||
| setOnExportReady(true); | |||||
| setOnReady(true); | |||||
| } | } | ||||
| }); | }); | ||||
| } | } | ||||
| function applySearch(input) { | function applySearch(input) { | ||||
| setGridOnReady(true); | |||||
| setSearchCriteria(input); | setSearchCriteria(input); | ||||
| } | } | ||||
| function applyExport(input) { | function applyExport(input) { | ||||
| setExportCriteria(input); | setExportCriteria(input); | ||||
| } | } | ||||
| function applyGridOnReady(input) { | |||||
| setGridOnReady(input); | |||||
| } | |||||
| React.useEffect(() => { | React.useEffect(() => { | ||||
| if (Object.keys(exportCriteria).length > 0) { | |||||
| // console.log(exportCriteria) | |||||
| if (Object.keys(exportCriteria).length > 0) { | |||||
| doExport(); | doExport(); | ||||
| } | } | ||||
| }, [exportCriteria]); | }, [exportCriteria]); | ||||
| @@ -112,51 +111,47 @@ const Index = () => { | |||||
| const readFile = (event) => { | const readFile = (event) => { | ||||
| let file = event.target.files[0]; | let file = event.target.files[0]; | ||||
| if (file) { | if (file) { | ||||
| if (!file.name.toLowerCase().substr(file.name.length - 5).includes(".xlsx")) { | |||||
| if (!file.name.toLowerCase().endsWith(".xlsx")) { | |||||
| setWarningText("Please upload a valid file (File format: .xlsx)."); | setWarningText("Please upload a valid file (File format: .xlsx)."); | ||||
| setIsWarningPopUp(true); | setIsWarningPopUp(true); | ||||
| document.getElementById("uploadFileBtn").value = ""; | |||||
| event.target.value = ""; | |||||
| return; | return; | ||||
| } | } | ||||
| file['id'] = attachments.length; | file['id'] = attachments.length; | ||||
| setAttachments([ | |||||
| ...attachments, | |||||
| file | |||||
| ]); | |||||
| document.getElementById("uploadFileBtn").value = ""; | |||||
| setAttachments([...attachments, file]); | |||||
| event.target.value = ""; | |||||
| } | } | ||||
| } | |||||
| }; | |||||
| const doExport=()=>{ | |||||
| setWaitDownload(true) | |||||
| const doExport = () => { | |||||
| setWaitDownload(true); | |||||
| HttpUtils.fileDownload({ | HttpUtils.fileDownload({ | ||||
| url: UrlUtils.GET_ISSUE_LIST, | url: UrlUtils.GET_ISSUE_LIST, | ||||
| params: exportCriteria, | params: exportCriteria, | ||||
| onResponse: () => { | onResponse: () => { | ||||
| setTimeout(()=> setWaitDownload(false), 500) | |||||
| setTimeout(() => setWaitDownload(false), 500); | |||||
| } | } | ||||
| }); | }); | ||||
| } | |||||
| }; | |||||
| const importHoliday = () => { | const importHoliday = () => { | ||||
| setWaitImport(true); | setWaitImport(true); | ||||
| if (!attachments || attachments.length <= 0) { | if (!attachments || attachments.length <= 0) { | ||||
| setWarningText("Please upload file."); | setWarningText("Please upload file."); | ||||
| setSaving(false); | |||||
| setWaitImport(false); | |||||
| return; | return; | ||||
| } | } | ||||
| HttpUtils.postWithFiles({ | HttpUtils.postWithFiles({ | ||||
| url: UrlUtils.POST_ISSUE_FILE, | url: UrlUtils.POST_ISSUE_FILE, | ||||
| files: attachments, | files: attachments, | ||||
| onSuccess: () => { | onSuccess: () => { | ||||
| notifySaveSuccess() | |||||
| notifySaveSuccess(); | |||||
| setWaitImport(false); | setWaitImport(false); | ||||
| setAttachments([]); | setAttachments([]); | ||||
| loadCombo(); | loadCombo(); | ||||
| loadForm(); | |||||
| } | } | ||||
| }); | }); | ||||
| } | |||||
| }; | |||||
| return ( | return ( | ||||
| !onReady ? | !onReady ? | ||||
| @@ -167,7 +162,7 @@ const Index = () => { | |||||
| </Grid> | </Grid> | ||||
| : | : | ||||
| ( | ( | ||||
| <Grid container sx={{minHeight: '87vh', backgroundColor: 'backgroundColor.default'}} direction="column" justifyContent="flex-start" alignItems="center" > | |||||
| <Grid container sx={{ minHeight: '87vh', backgroundColor: 'backgroundColor.default' }} direction="column" justifyContent="flex-start" alignItems="center"> | |||||
| <Grid item xs={12} width="100%"> | <Grid item xs={12} width="100%"> | ||||
| <div style={BackgroundHead} width="100%"> | <div style={BackgroundHead} width="100%"> | ||||
| <Stack direction="row" height='70px'> | <Stack direction="row" height='70px'> | ||||
| @@ -175,90 +170,107 @@ const Index = () => { | |||||
| </Stack> | </Stack> | ||||
| </div> | </div> | ||||
| </Grid> | </Grid> | ||||
| {!onExportReady? | |||||
| <LoadingComponent />: | |||||
| {!onExportReady ? | |||||
| <LoadingComponent /> : | |||||
| <Grid item xs={12} md={12} lg={12} width="100%"> | <Grid item xs={12} md={12} lg={12} width="100%"> | ||||
| <ExportForm | |||||
| <ExportForm | |||||
| applyExport={applyExport} | applyExport={applyExport} | ||||
| comboData={holidayComboData} | comboData={holidayComboData} | ||||
| waitDownload={waitDownload} | waitDownload={waitDownload} | ||||
| /> | /> | ||||
| </Grid> | </Grid> | ||||
| } | } | ||||
| <Grid item xs={12} md={12} lg={6} width="100%"> | |||||
| <Stack direction="row" justifyContent="flex-start" alignItems="center" spacing={2} sx={{ml:2,mt:1}} > | |||||
| <ThemeProvider theme={PNSPS_LONG_BUTTON_THEME}> | |||||
| <input | |||||
| id="uploadFileBtn" | |||||
| name="file" | |||||
| type="file" | |||||
| accept=".xlsx" | |||||
| style={{ display: 'none' }} | |||||
| disabled={waitImport} | |||||
| onChange={(event) => { | |||||
| readFile(event) | |||||
| }} | |||||
| /> | |||||
| <label htmlFor="uploadFileBtn"> | |||||
| {isGrantedAny(["MAINTAIN_GAZETTE_ISSUE"]) && | |||||
| <Grid item xs={12} md={12} lg={6} width="100%"> | |||||
| <Stack direction="row" justifyContent="flex-start" alignItems="center" spacing={2} sx={{ ml: 2, mt: 1 }}> | |||||
| <ThemeProvider theme={PNSPS_LONG_BUTTON_THEME}> | |||||
| <Button | <Button | ||||
| component="span" | |||||
| variant="contained" | variant="contained" | ||||
| size="large" | size="large" | ||||
| disabled={waitImport} | 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> | <Typography variant="h5">Upload Files</Typography> | ||||
| </Button> | </Button> | ||||
| </label> | |||||
| </ThemeProvider> | |||||
| </Stack> | |||||
| </Grid> | |||||
| {/*row 1*/} | |||||
| <input | |||||
| id="uploadFileBtn" | |||||
| name="file" | |||||
| type="file" | |||||
| accept=".xlsx" | |||||
| hidden | |||||
| disabled={waitImport} | |||||
| onChange={readFile} | |||||
| aria-label={intl.formatMessage({ id: 'ariaUploadExcelFile' })} | |||||
| ref={fileInputRef} | |||||
| /> | |||||
| </ThemeProvider> | |||||
| </Stack> | |||||
| </Grid> | |||||
| } | |||||
| {/* Row 1 */} | |||||
| <Grid item xs={12} md={12} lg={12} width="100%"> | <Grid item xs={12} md={12} lg={12} width="100%"> | ||||
| <SearchForm | |||||
| <SearchForm | |||||
| applySearch={applySearch} | applySearch={applySearch} | ||||
| comboData={comboData} | comboData={comboData} | ||||
| onGridReady={onGridReady} | |||||
| /> | /> | ||||
| </Grid> | </Grid> | ||||
| {/*row 2*/} | |||||
| {!onSearchReady? | |||||
| <LoadingComponent/>: | |||||
| {/* Row 2 */} | |||||
| {!onSearchReady ? | |||||
| <LoadingComponent /> : | |||||
| <Grid item xs={12} md={12} lg={12} width="100%"> | <Grid item xs={12} md={12} lg={12} width="100%"> | ||||
| <MainCard elevation={0} | |||||
| border={false} | |||||
| content={false} | |||||
| > | |||||
| <MainCard elevation={0} border={false} content={false}> | |||||
| <GazetteIssueTable | <GazetteIssueTable | ||||
| searchCriteria={searchCriteria} | |||||
| searchCriteria={searchCriteria} | |||||
| applyGridOnReady={applyGridOnReady} | |||||
| /> | /> | ||||
| </MainCard> | </MainCard> | ||||
| </Grid> | </Grid> | ||||
| } | } | ||||
| <div> | |||||
| <Dialog | |||||
| open={isWarningPopUp} | |||||
| onClose={() => setIsWarningPopUp(false)} | |||||
| PaperProps={{ | |||||
| sx: { | |||||
| minWidth: '40vw', | |||||
| maxWidth: { xs: '90vw', s: '90vw', m: '70vw', lg: '70vw' }, | |||||
| maxHeight: { xs: '90vh', s: '70vh', m: '70vh', lg: '60vh' } | |||||
| } | |||||
| }} | |||||
| > | |||||
| <DialogTitle><Typography variant="h3">Warning</Typography></DialogTitle> | |||||
| <DialogContent style={{ display: 'flex', }}> | |||||
| <Typography variant="h4" style={{ padding: '16px' }}>{warningText}</Typography> | |||||
| </DialogContent> | |||||
| <DialogActions> | |||||
| <Button onClick={() => setIsWarningPopUp(false)}><Typography variant="h5">OK</Typography></Button> | |||||
| </DialogActions> | |||||
| </Dialog> | |||||
| </div> | |||||
| </Grid > | |||||
| <Dialog | |||||
| open={isWarningPopUp} | |||||
| onClose={() => setIsWarningPopUp(false)} | |||||
| PaperProps={{ | |||||
| sx: { | |||||
| minWidth: '40vw', | |||||
| maxWidth: { xs: '90vw', s: '90vw', m: '70vw', lg: '70vw' }, | |||||
| maxHeight: { xs: '90vh', s: '70vh', m: '70vh', lg: '60vh' } | |||||
| } | |||||
| }} | |||||
| > | |||||
| <DialogTitle> | |||||
| <Typography variant="h3">Warning</Typography> | |||||
| </DialogTitle> | |||||
| <DialogContent style={{ display: 'flex' }}> | |||||
| <Typography variant="h4" style={{ padding: '16px' }}>{warningText}</Typography> | |||||
| </DialogContent> | |||||
| <DialogActions> | |||||
| <Button onClick={() => setIsWarningPopUp(false)}> | |||||
| <Typography variant="h5">OK</Typography> | |||||
| </Button> | |||||
| </DialogActions> | |||||
| </Dialog> | |||||
| </Grid> | |||||
| ) | ) | ||||
| ); | ); | ||||
| }; | }; | ||||
| export default Index; | |||||
| export default Index; | |||||
| @@ -9,13 +9,13 @@ import { dateStr } from "utils/DateUtils"; | |||||
| // ==============================|| EVENT TABLE ||============================== // | // ==============================|| EVENT TABLE ||============================== // | ||||
| export default function HolidayTable({ recordList }) { | |||||
| export default function HolidayTable({ recordList, applyGridOnReady }) { | |||||
| const [rows, setRows] = React.useState(recordList); | const [rows, setRows] = React.useState(recordList); | ||||
| // const navigate = useNavigate() | // const navigate = useNavigate() | ||||
| useEffect(() => { | useEffect(() => { | ||||
| console.log(recordList) | |||||
| // console.log(recordList) | |||||
| setRows(recordList.records); | setRows(recordList.records); | ||||
| }, [recordList]); | }, [recordList]); | ||||
| @@ -48,8 +48,13 @@ export default function HolidayTable({ recordList }) { | |||||
| rows={rows} | rows={rows} | ||||
| columns={columns} | columns={columns} | ||||
| customPageSize={20} | customPageSize={20} | ||||
| applyGridOnReady={applyGridOnReady} | |||||
| // onRowDoubleClick={handleRowDoubleClick} | // onRowDoubleClick={handleRowDoubleClick} | ||||
| getRowHeight={() => 'auto'} | getRowHeight={() => 'auto'} | ||||
| // doLoad={React.useMemo(() => ({ | |||||
| // url: LIST_PROOF, | |||||
| // params: _searchCriteria, | |||||
| // }), [_searchCriteria])} | |||||
| /> | /> | ||||
| </div> | </div> | ||||
| ); | ); | ||||
| @@ -16,7 +16,7 @@ import {ThemeProvider} from "@emotion/react"; | |||||
| // ==============================|| DASHBOARD - DEFAULT ||============================== // | // ==============================|| DASHBOARD - DEFAULT ||============================== // | ||||
| const SearchHolidayForm = ({ applySearch, comboData}) => { | |||||
| const SearchHolidayForm = ({ applySearch, comboData, onGridReady}) => { | |||||
| const [selectedYear, setSelectedYear] = React.useState([]); | const [selectedYear, setSelectedYear] = React.useState([]); | ||||
| // const [defaultYear, setDefaultYear] = React.useState(searchCriteria.year); | // const [defaultYear, setDefaultYear] = React.useState(searchCriteria.year); | ||||
| const [comboList, setComboList] = React.useState([]); | const [comboList, setComboList] = React.useState([]); | ||||
| @@ -79,9 +79,9 @@ const SearchHolidayForm = ({ applySearch, comboData}) => { | |||||
| setSelectedYear(newValue); | setSelectedYear(newValue); | ||||
| }} | }} | ||||
| sx={{ | sx={{ | ||||
| "& .MuiInputBase-root": { height: "41px" }, | |||||
| "#year-combo": { padding: "0px 0px 0px 0px" }, | |||||
| "& .MuiAutocomplete-endAdornment": { top: "auto" }, | |||||
| '& .MuiInputBase-root': { alignItems: 'center' }, | |||||
| '& .MuiAutocomplete-endAdornment': { top: '50%', transform: 'translateY(-50%)' }, | |||||
| '& .MuiOutlinedInput-root': { height: 40 } | |||||
| }} | }} | ||||
| renderInput={(params) => <TextField {...params} placeholder={""}/>} | renderInput={(params) => <TextField {...params} placeholder={""}/>} | ||||
| /> | /> | ||||
| @@ -114,6 +114,7 @@ const SearchHolidayForm = ({ applySearch, comboData}) => { | |||||
| <Button | <Button | ||||
| variant="contained" | variant="contained" | ||||
| type="submit" | type="submit" | ||||
| disabled={onGridReady} | |||||
| > | > | ||||
| Search | Search | ||||
| </Button> | </Button> | ||||
| @@ -1,4 +1,3 @@ | |||||
| // material-ui | |||||
| import { | import { | ||||
| Grid, | Grid, | ||||
| Typography, | Typography, | ||||
| @@ -12,9 +11,8 @@ import * as HttpUtils from "utils/HttpUtils"; | |||||
| import Loadable from 'components/Loadable'; | import Loadable from 'components/Loadable'; | ||||
| const LoadingComponent = Loadable(React.lazy(() => import('pages/extra-pages/LoadingComponent'))); | const LoadingComponent = Loadable(React.lazy(() => import('pages/extra-pages/LoadingComponent'))); | ||||
| const HolidayTable = Loadable(React.lazy(() => import('pages/Holiday/DataGrid'))) | |||||
| import titleBackgroundImg from 'assets/images/dashboard/gazette-bar.png' | |||||
| // import AddCircleOutlineIcon from '@mui/icons-material/AddCircleOutline'; | |||||
| const HolidayTable = Loadable(React.lazy(() => import('pages/Holiday/DataGrid'))); | |||||
| import titleBackgroundImg from 'assets/images/dashboard/gazette-bar.png'; | |||||
| import MainCard from 'components/MainCard'; | import MainCard from 'components/MainCard'; | ||||
| const SearchForm = Loadable(React.lazy(() => import('./SearchForm'))); | const SearchForm = Loadable(React.lazy(() => import('./SearchForm'))); | ||||
| @@ -26,59 +24,50 @@ const BackgroundHead = { | |||||
| backgroundRepeat: 'no-repeat', | backgroundRepeat: 'no-repeat', | ||||
| backgroundColor: '#0C489E', | backgroundColor: '#0C489E', | ||||
| backgroundPosition: 'right' | backgroundPosition: 'right' | ||||
| } | |||||
| // import { useNavigate } from "react-router"; | |||||
| import {PNSPS_LONG_BUTTON_THEME} from "themes/buttonConst"; | |||||
| import {ThemeProvider} from "@emotion/react"; | |||||
| }; | |||||
| import { PNSPS_LONG_BUTTON_THEME } from "themes/buttonConst"; | |||||
| import { ThemeProvider } from "@emotion/react"; | |||||
| import { dateStr_Year } from "utils/DateUtils"; | import { dateStr_Year } from "utils/DateUtils"; | ||||
| import { notifySaveSuccess } from 'utils/CommonFunction'; | import { notifySaveSuccess } from 'utils/CommonFunction'; | ||||
| import { isGrantedAny } from "auth/utils"; | |||||
| import { useIntl } from 'react-intl'; | |||||
| // ==============================|| DASHBOARD - DEFAULT ||============================== // | // ==============================|| DASHBOARD - DEFAULT ||============================== // | ||||
| const Index = () => { | const Index = () => { | ||||
| const intl = useIntl(); | |||||
| const [record, setRecord] = React.useState([]); | const [record, setRecord] = React.useState([]); | ||||
| const [comboData, setComboData] = React.useState([]); | const [comboData, setComboData] = React.useState([]); | ||||
| const [onReady, setOnReady] = React.useState(false); | const [onReady, setOnReady] = React.useState(false); | ||||
| const [onSearchReady, setOnSearchReady] = React.useState(false); | const [onSearchReady, setOnSearchReady] = React.useState(false); | ||||
| // const navigate = useNavigate() | |||||
| const [onGridReady, setGridOnReady] = React.useState(false); | |||||
| const [searchCriteria, setSearchCriteria] = React.useState({ | const [searchCriteria, setSearchCriteria] = React.useState({ | ||||
| year: dateStr_Year(new Date()), | year: dateStr_Year(new Date()), | ||||
| // dateFrom: DateUtils.dateValue(new Date().setDate(new Date().getDate()-14)), | |||||
| }); | }); | ||||
| const [attachments, setAttachments] = React.useState([]); | const [attachments, setAttachments] = React.useState([]); | ||||
| const [waitImport, setWaitImport] = React.useState(false); | const [waitImport, setWaitImport] = React.useState(false); | ||||
| const [waitDownload, setWaitDownload] = React.useState(false); | const [waitDownload, setWaitDownload] = React.useState(false); | ||||
| const [isWarningPopUp, setIsWarningPopUp] = React.useState(false); | const [isWarningPopUp, setIsWarningPopUp] = React.useState(false); | ||||
| const [warningText, setWarningText] = React.useState(""); | const [warningText, setWarningText] = React.useState(""); | ||||
| // React.useLayoutEffect(() => { | |||||
| // loadForm(); | |||||
| // }, []); | |||||
| // React.useLayoutEffect(() => { | |||||
| // if (comboData) { | |||||
| // setOnReady(true); | |||||
| // } | |||||
| // }, [comboData]); | |||||
| const fileInputRef = React.useRef(null); | |||||
| React.useEffect(() => { | React.useEffect(() => { | ||||
| // console.log(searchCriteria) | |||||
| setOnSearchReady(false) | |||||
| setOnSearchReady(false); | |||||
| loadForm(); | loadForm(); | ||||
| }, [searchCriteria]); | }, [searchCriteria]); | ||||
| function loadForm() { | function loadForm() { | ||||
| HttpUtils.get({ | HttpUtils.get({ | ||||
| url: UrlUtils.GET_HOLIDAY, | url: UrlUtils.GET_HOLIDAY, | ||||
| params: searchCriteria, | params: searchCriteria, | ||||
| onSuccess: (responseData) => { | onSuccess: (responseData) => { | ||||
| // console.log(responseData) | |||||
| setRecord(responseData); | setRecord(responseData); | ||||
| if (comboData.length == 0) { | |||||
| if (comboData.length === 0) { | |||||
| loadCombo(); | loadCombo(); | ||||
| }else{ | |||||
| setOnSearchReady(true) | |||||
| } else { | |||||
| setOnSearchReady(true); | |||||
| } | } | ||||
| } | } | ||||
| }); | }); | ||||
| @@ -91,15 +80,20 @@ const Index = () => { | |||||
| let combo = responseData; | let combo = responseData; | ||||
| setComboData(combo); | setComboData(combo); | ||||
| setOnReady(true); | setOnReady(true); | ||||
| setOnSearchReady(true) | |||||
| setOnSearchReady(true); | |||||
| } | } | ||||
| }); | }); | ||||
| } | } | ||||
| function applySearch(input) { | function applySearch(input) { | ||||
| setGridOnReady(true); | |||||
| setSearchCriteria(input); | setSearchCriteria(input); | ||||
| } | } | ||||
| function applyGridOnReady(input) { | |||||
| setGridOnReady(input); | |||||
| } | |||||
| React.useEffect(() => { | React.useEffect(() => { | ||||
| if (attachments.length > 0) { | if (attachments.length > 0) { | ||||
| importHoliday(); | importHoliday(); | ||||
| @@ -107,52 +101,51 @@ const Index = () => { | |||||
| }, [attachments]); | }, [attachments]); | ||||
| const readFile = (event) => { | const readFile = (event) => { | ||||
| let file = event.target.files[0]; | |||||
| let file = event.target.files && event.target.files[0]; | |||||
| if (file) { | if (file) { | ||||
| if (!file.name.toLowerCase().substr(file.name.length - 5).includes(".xlsx")) { | |||||
| if (!file.name.toLowerCase().endsWith(".xlsx")) { | |||||
| setWarningText("Please upload a valid file (File format: .xlsx)."); | setWarningText("Please upload a valid file (File format: .xlsx)."); | ||||
| setIsWarningPopUp(true); | setIsWarningPopUp(true); | ||||
| document.getElementById("uploadFileBtn").value = ""; | |||||
| // clear the input value | |||||
| event.target.value = ""; | |||||
| return; | return; | ||||
| } | } | ||||
| file['id'] = attachments.length; | file['id'] = attachments.length; | ||||
| setAttachments([ | |||||
| ...attachments, | |||||
| file | |||||
| ]); | |||||
| document.getElementById("uploadFileBtn").value = ""; | |||||
| setAttachments(prev => [...prev, file]); | |||||
| // clear the input value | |||||
| event.target.value = ""; | |||||
| } | } | ||||
| } | |||||
| }; | |||||
| const doExport=()=>{ | |||||
| setWaitDownload(true) | |||||
| const doExport = () => { | |||||
| setWaitDownload(true); | |||||
| HttpUtils.fileDownload({ | HttpUtils.fileDownload({ | ||||
| url: UrlUtils.GET_HOLIDAY_TEMPLATE, | url: UrlUtils.GET_HOLIDAY_TEMPLATE, | ||||
| onResponse: () => { | onResponse: () => { | ||||
| setTimeout(()=> setWaitDownload(false), 500) | |||||
| setTimeout(() => setWaitDownload(false), 500); | |||||
| } | } | ||||
| }); | }); | ||||
| } | |||||
| }; | |||||
| const importHoliday = () => { | const importHoliday = () => { | ||||
| setWaitImport(true); | setWaitImport(true); | ||||
| setOnSearchReady(false); | setOnSearchReady(false); | ||||
| if (!attachments || attachments.length <= 0) { | if (!attachments || attachments.length <= 0) { | ||||
| setWarningText("Please upload file."); | setWarningText("Please upload file."); | ||||
| setSaving(false); | |||||
| setWaitImport(false); | |||||
| return; | return; | ||||
| } | } | ||||
| HttpUtils.postWithFiles({ | HttpUtils.postWithFiles({ | ||||
| url: UrlUtils.POST_HOLIDAY, | url: UrlUtils.POST_HOLIDAY, | ||||
| files: attachments, | files: attachments, | ||||
| onSuccess: () => { | onSuccess: () => { | ||||
| notifySaveSuccess() | |||||
| notifySaveSuccess(); | |||||
| setWaitImport(false); | setWaitImport(false); | ||||
| setAttachments([]); | setAttachments([]); | ||||
| loadForm(); | loadForm(); | ||||
| } | } | ||||
| }); | }); | ||||
| } | |||||
| }; | |||||
| return ( | return ( | ||||
| !onReady ? | !onReady ? | ||||
| @@ -163,7 +156,7 @@ const Index = () => { | |||||
| </Grid> | </Grid> | ||||
| : | : | ||||
| ( | ( | ||||
| <Grid container sx={{minHeight: '87vh', backgroundColor: 'backgroundColor.default'}} direction="column" justifyContent="flex-start" alignItems="center" > | |||||
| <Grid container sx={{ minHeight: '87vh', backgroundColor: 'backgroundColor.default' }} direction="column" justifyContent="flex-start" alignItems="center" > | |||||
| <Grid item xs={12} width="100%"> | <Grid item xs={12} width="100%"> | ||||
| <div style={BackgroundHead} width="100%"> | <div style={BackgroundHead} width="100%"> | ||||
| <Stack direction="row" height='70px'> | <Stack direction="row" height='70px'> | ||||
| @@ -171,97 +164,106 @@ const Index = () => { | |||||
| </Stack> | </Stack> | ||||
| </div> | </div> | ||||
| </Grid> | </Grid> | ||||
| <Grid item xs={12} md={12} lg={6} width="100%"> | <Grid item xs={12} md={12} lg={6} width="100%"> | ||||
| <Stack direction="row" justifyContent="flex-start" alignItems="center" spacing={2} sx={{ml:2,mt:1}} > | |||||
| <Stack direction="row" justifyContent="flex-start" alignItems="center" spacing={2} sx={{ ml: 2, mt: 1 }} > | |||||
| <ThemeProvider theme={PNSPS_LONG_BUTTON_THEME}> | <ThemeProvider theme={PNSPS_LONG_BUTTON_THEME}> | ||||
| <label htmlFor="downloadFileBtn"> | |||||
| <Button | |||||
| component="span" | |||||
| variant="contained" | |||||
| size="large" | |||||
| disabled={waitDownload} | |||||
| onClick={doExport} | |||||
| > | |||||
| <Typography variant="h5">Export</Typography> | |||||
| </Button> | |||||
| </label> | |||||
| <Button | |||||
| variant="contained" | |||||
| size="large" | |||||
| disabled={waitDownload} | |||||
| onClick={doExport} | |||||
| aria-label={intl.formatMessage({ id: 'ariaExportHolidayTemplate' })} | |||||
| > | |||||
| <Typography variant="h5">Export</Typography> | |||||
| </Button> | |||||
| </ThemeProvider> | </ThemeProvider> | ||||
| <ThemeProvider theme={PNSPS_LONG_BUTTON_THEME}> | |||||
| <input | |||||
| id="uploadFileBtn" | |||||
| name="file" | |||||
| type="file" | |||||
| accept=".xlsx" | |||||
| style={{ display: 'none' }} | |||||
| disabled={waitImport} | |||||
| onChange={(event) => { | |||||
| readFile(event) | |||||
| }} | |||||
| /> | |||||
| <label htmlFor="uploadFileBtn"> | |||||
| {isGrantedAny(["MAINTAIN_GAZETTE_ISSUE"]) ? | |||||
| <ThemeProvider theme={PNSPS_LONG_BUTTON_THEME}> | |||||
| <Button | <Button | ||||
| component="span" | |||||
| variant="contained" | variant="contained" | ||||
| size="large" | size="large" | ||||
| disabled={waitImport} | 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> | <Typography variant="h5">Upload Files</Typography> | ||||
| </Button> | </Button> | ||||
| </label> | |||||
| </ThemeProvider> | |||||
| <input | |||||
| id="uploadFileBtn" | |||||
| name="file" | |||||
| type="file" | |||||
| accept=".xlsx" | |||||
| hidden | |||||
| disabled={waitImport} | |||||
| onChange={readFile} | |||||
| aria-label={intl.formatMessage({ id: 'ariaUploadExcelFile' })} | |||||
| ref={fileInputRef} | |||||
| /> | |||||
| </ThemeProvider> | |||||
| : null | |||||
| } | |||||
| </Stack> | </Stack> | ||||
| </Grid> | </Grid> | ||||
| {/*row 1*/} | |||||
| {/* row 1 */} | |||||
| <Grid item xs={12} md={12} lg={12} width="100%"> | <Grid item xs={12} md={12} lg={12} width="100%"> | ||||
| <SearchForm | |||||
| <SearchForm | |||||
| applySearch={applySearch} | applySearch={applySearch} | ||||
| // generateXML={generateXML} | |||||
| searchCriteria={searchCriteria} | searchCriteria={searchCriteria} | ||||
| comboData={comboData} | comboData={comboData} | ||||
| onGridReady={onGridReady} | |||||
| /> | /> | ||||
| </Grid> | </Grid> | ||||
| {/*row 2*/} | |||||
| {!onSearchReady? | |||||
| <LoadingComponent/>: | |||||
| {/* row 2 */} | |||||
| {!onSearchReady ? | |||||
| <LoadingComponent /> : | |||||
| <Grid item xs={12} md={12} lg={12} width="100%"> | <Grid item xs={12} md={12} lg={12} width="100%"> | ||||
| <MainCard elevation={0} | |||||
| border={false} | |||||
| content={false} | |||||
| > | |||||
| <MainCard elevation={0} border={false} content={false}> | |||||
| <HolidayTable | <HolidayTable | ||||
| recordList={record} | recordList={record} | ||||
| applyGridOnReady={applyGridOnReady} | |||||
| /> | /> | ||||
| </MainCard> | </MainCard> | ||||
| </Grid> | </Grid> | ||||
| } | } | ||||
| <div> | |||||
| <Dialog | |||||
| open={isWarningPopUp} | |||||
| onClose={() => setIsWarningPopUp(false)} | |||||
| PaperProps={{ | |||||
| sx: { | |||||
| minWidth: '40vw', | |||||
| maxWidth: { xs: '90vw', s: '90vw', m: '70vw', lg: '70vw' }, | |||||
| maxHeight: { xs: '90vh', s: '70vh', m: '70vh', lg: '60vh' } | |||||
| } | |||||
| }} | |||||
| > | |||||
| <DialogTitle><Typography variant="h3">Warning</Typography></DialogTitle> | |||||
| <DialogContent style={{ display: 'flex', }}> | |||||
| <Typography variant="h4" style={{ padding: '16px' }}>{warningText}</Typography> | |||||
| </DialogContent> | |||||
| <DialogActions> | |||||
| <Button onClick={() => setIsWarningPopUp(false)}><Typography variant="h5">OK</Typography></Button> | |||||
| </DialogActions> | |||||
| </Dialog> | |||||
| </div> | |||||
| <Dialog | |||||
| open={isWarningPopUp} | |||||
| onClose={() => setIsWarningPopUp(false)} | |||||
| PaperProps={{ | |||||
| sx: { | |||||
| minWidth: '40vw', | |||||
| maxWidth: { xs: '90vw', s: '90vw', m: '70vw', lg: '70vw' }, | |||||
| maxHeight: { xs: '90vh', s: '70vh', m: '70vh', lg: '60vh' } | |||||
| } | |||||
| }} | |||||
| > | |||||
| <DialogTitle><Typography variant="h3">Warning</Typography></DialogTitle> | |||||
| <DialogContent style={{ display: 'flex' }}> | |||||
| <Typography variant="h4" style={{ padding: '16px' }}>{warningText}</Typography> | |||||
| </DialogContent> | |||||
| <DialogActions> | |||||
| <Button onClick={() => setIsWarningPopUp(false)}><Typography variant="h5">OK</Typography></Button> | |||||
| </DialogActions> | |||||
| </Dialog> | |||||
| </Grid > | </Grid > | ||||
| ) | ) | ||||
| ); | ); | ||||
| }; | }; | ||||
| export default Index; | |||||
| export default Index; | |||||
| @@ -0,0 +1,187 @@ | |||||
| // import { useState } from 'react'; | |||||
| // material-ui | |||||
| import { | |||||
| Grid, | |||||
| Typography, | |||||
| Stack, | |||||
| Paper, | |||||
| Box, | |||||
| CircularProgress, | |||||
| Button | |||||
| } from '@mui/material'; | |||||
| import * as React from "react"; | |||||
| import { GET_JVM_INFO, GET_NOTIFICATION_QUEUE_STATUS } from "utils/ApiPathConst"; | |||||
| import axios from "axios"; | |||||
| import titleBackgroundImg from 'assets/images/dashboard/gazette-bar.png' | |||||
| 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); | |||||
| axios.get(`${GET_JVM_INFO}`) | |||||
| .then((response) => { | |||||
| if (response.status === 200) { | |||||
| console.log(response) | |||||
| setJvmInfo(response.data); | |||||
| } | |||||
| }) | |||||
| .catch(error => { | |||||
| setError(error); | |||||
| setLoading(false); | |||||
| }); | |||||
| }; | |||||
| 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); | |||||
| }, []); | |||||
| React.useEffect(() => { | |||||
| if(jvmInfo != null) { | |||||
| if (Object.keys(jvmInfo).length > 0 && jvmInfo !== undefined) { | |||||
| setLoading(false); | |||||
| } | |||||
| } | |||||
| }, [jvmInfo]); | |||||
| const BackgroundHead = { | |||||
| backgroundImage: `url(${titleBackgroundImg})`, | |||||
| width: '100%', | |||||
| height: '100%', | |||||
| backgroundSize: 'contain', | |||||
| backgroundRepeat: 'no-repeat', | |||||
| backgroundColor: '#0C489E', | |||||
| backgroundPosition: 'right' | |||||
| }; | |||||
| return ( | |||||
| <Grid container sx={{ minHeight: '87vh', backgroundColor: "backgroundColor.default" }} direction="column"> | |||||
| <Grid item xs={12}> | |||||
| <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" }}> | |||||
| System Background Status | |||||
| </Typography> | |||||
| </Stack> | |||||
| </div> | |||||
| </Grid> | |||||
| <Grid item xs={12} ml={15} mb={2} mt={2}> | |||||
| <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' }}> | |||||
| {loading ? ( | |||||
| <Box display="flex" justifyContent="center" alignItems="center" minHeight={200}> | |||||
| <CircularProgress /> | |||||
| </Box> | |||||
| ) : error ? ( | |||||
| <Typography color="error">Error: {error.message}</Typography> | |||||
| ) : jvmInfo ? ( | |||||
| <Box | |||||
| component="pre" | |||||
| sx={{ | |||||
| p: 2, | |||||
| borderRadius: 1, | |||||
| bgcolor: 'grey.100', | |||||
| overflow: 'auto', | |||||
| maxHeight: 400, | |||||
| fontSize: '0.875rem', | |||||
| lineHeight: 1.6 | |||||
| }} | |||||
| > | |||||
| {JSON.stringify(jvmInfo, null, 2)} | |||||
| </Box> | |||||
| ) : ( | |||||
| <Typography>No data available</Typography> | |||||
| )} | |||||
| </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 "Notification Queue Status" to load data.</Typography> | |||||
| )} | |||||
| </Paper> | |||||
| </Grid> | |||||
| </Grid> | |||||
| ); | |||||
| }; | |||||
| export default JVMDefault; | |||||
| @@ -17,6 +17,8 @@ const LoadingComponent = Loadable(React.lazy(() => import('pages/extra-pages/Loa | |||||
| import titleBackgroundImg from 'assets/images/dashboard/gazette-bar.png' | import titleBackgroundImg from 'assets/images/dashboard/gazette-bar.png' | ||||
| import { FormattedMessage } from "react-intl"; | import { FormattedMessage } from "react-intl"; | ||||
| import usePageTitle from 'components/usePageTitle'; | |||||
| const BackgroundHead = { | const BackgroundHead = { | ||||
| backgroundImage: `url(${titleBackgroundImg})`, | backgroundImage: `url(${titleBackgroundImg})`, | ||||
| width: '100%', | width: '100%', | ||||
| @@ -30,6 +32,8 @@ const BackgroundHead = { | |||||
| // ==============================|| DASHBOARD - DEFAULT ||============================== // | // ==============================|| DASHBOARD - DEFAULT ||============================== // | ||||
| const Index = () => { | const Index = () => { | ||||
| usePageTitle("msgDetails"); | |||||
| const params = useParams(); | const params = useParams(); | ||||
| const navigate = useNavigate() | const navigate = useNavigate() | ||||
| @@ -72,7 +76,7 @@ const Index = () => { | |||||
| <Grid item xs={12} width="100%"> | <Grid item xs={12} width="100%"> | ||||
| <div style={BackgroundHead} width="100%"> | <div style={BackgroundHead} width="100%"> | ||||
| <Stack direction="row" height='70px'> | <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" /> | <FormattedMessage id="msgDetails" /> | ||||
| </Typography> | </Typography> | ||||
| </Stack> | </Stack> | ||||
| @@ -83,7 +87,7 @@ const Index = () => { | |||||
| <Grid container justifyContent="flex-start" alignItems="center" > | <Grid container justifyContent="flex-start" alignItems="center" > | ||||
| <center> | <center> | ||||
| <Grid item xs={12} md={12} sx={{p:2}} > | <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} | {record?.subject} | ||||
| </Typography> | </Typography> | ||||
| <Typography sx={{p:1}} align="justify">{DateUtils.datetimeStr(record?.sentDate)}</Typography> | <Typography sx={{p:1}} align="justify">{DateUtils.datetimeStr(record?.sentDate)}</Typography> | ||||
| @@ -91,7 +95,7 @@ const Index = () => { | |||||
| <div dangerouslySetInnerHTML={{__html: record?.content}}></div> | <div dangerouslySetInnerHTML={{__html: record?.content}}></div> | ||||
| </Typography> | </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 | <Button | ||||
| component="span" | component="span" | ||||
| variant="contained" | variant="contained" | ||||
| @@ -5,13 +5,21 @@ import { useNavigate } from "react-router-dom"; | |||||
| import { FiDataGrid } from "components/FiDataGrid"; | import { FiDataGrid } from "components/FiDataGrid"; | ||||
| import {useIntl} from "react-intl"; | import {useIntl} from "react-intl"; | ||||
| import { clickableLink } from 'utils/CommonFunction'; | import { clickableLink } from 'utils/CommonFunction'; | ||||
| import {GET_MSG_LIST} from "utils/ApiPathConst"; | |||||
| // ==============================|| EVENT TABLE ||============================== // | // ==============================|| EVENT TABLE ||============================== // | ||||
| export default function MsgTable({ recordList }) { | |||||
| const [rows, setRows] = React.useState(recordList); | |||||
| export default function MsgTable({ searchCriteria, applyGridOnReady, applySearch}) { | |||||
| const navigate = useNavigate() | const navigate = useNavigate() | ||||
| const intl = useIntl(); | const intl = useIntl(); | ||||
| const [_searchCriteria, set_searchCriteria] = React.useState(searchCriteria); | |||||
| React.useEffect(() => { | |||||
| set_searchCriteria(searchCriteria); | |||||
| }, [searchCriteria]); | |||||
| const _sx = { | const _sx = { | ||||
| padding: "4 2 4 2", | padding: "4 2 4 2", | ||||
| boxShadow: 1, | boxShadow: 1, | ||||
| @@ -25,48 +33,56 @@ export default function MsgTable({ recordList }) { | |||||
| '& .MuiDataGrid-footerContainer': { | '& .MuiDataGrid-footerContainer': { | ||||
| border: 1, | border: 1, | ||||
| borderColor: "#EEE" | borderColor: "#EEE" | ||||
| } | |||||
| }, | |||||
| "& .MuiDataGrid-columnHeaderTitle": { | |||||
| whiteSpace: "normal", | |||||
| lineHeight: "normal" | |||||
| }, | |||||
| "& .MuiDataGrid-columnHeader": { | |||||
| // Forced to use important since overriding inline styles | |||||
| height: "unset !important" | |||||
| }, | |||||
| } | } | ||||
| React.useEffect(() => { | |||||
| setRows(recordList); | |||||
| }, [recordList]); | |||||
| const handleEditClick = (params) => () => { | |||||
| navigate('/msg/details/' + params.row.id); | |||||
| }; | |||||
| const columns = [ | const columns = [ | ||||
| { | { | ||||
| id: 'sentDate', | id: 'sentDate', | ||||
| field: 'sentDate', | field: 'sentDate', | ||||
| headerName: intl.formatMessage({id: 'date'}), | headerName: intl.formatMessage({id: 'date'}), | ||||
| width: 160, | |||||
| renderCell: (params) => { | |||||
| return DateUtils.datetimeStr(params.row.sentDate); | |||||
| width: 170, | |||||
| valueGetter: (params) => { | |||||
| return DateUtils.datetimeStr(params?.value); | |||||
| }, | }, | ||||
| }, | }, | ||||
| { | { | ||||
| field: 'actions', | |||||
| headerName: intl.formatMessage({id: 'payId'}), | |||||
| field: 'subject', | |||||
| headerName: intl.formatMessage({id: 'subject'}), | |||||
| flex: 1 , | flex: 1 , | ||||
| cellClassName: 'actions', | |||||
| cellClassName: 'subject', | |||||
| renderCell: (params) => { | renderCell: (params) => { | ||||
| return clickableLink('/msg/details/' + params.row.id, params.row.subject); | return clickableLink('/msg/details/' + params.row.id, params.row.subject); | ||||
| }, | }, | ||||
| }, | }, | ||||
| ]; | ]; | ||||
| function handleEditClick(params) { | |||||
| navigate('/msg/details/' + params.row.id); | |||||
| } | |||||
| return ( | return ( | ||||
| <div style={{ minHeight: 400, width: '100%' }}> | |||||
| <div style={{ width: '100%', overflowX: 'auto'}}> | |||||
| <FiDataGrid | <FiDataGrid | ||||
| sx={_sx} | sx={_sx} | ||||
| rowHeight={80} | |||||
| rows={rows} | |||||
| columns={columns} | columns={columns} | ||||
| customPageSize={20} | |||||
| customPageSize={10} | |||||
| getRowHeight={() => 'auto'} | |||||
| onRowDoubleClick={handleEditClick} | onRowDoubleClick={handleEditClick} | ||||
| applyGridOnReady={applyGridOnReady} | |||||
| applySearch={applySearch} | |||||
| doLoad={React.useMemo(() => ({ | |||||
| url: GET_MSG_LIST, | |||||
| params: _searchCriteria, | |||||
| }), [_searchCriteria])} | |||||
| /> | /> | ||||
| </div> | </div> | ||||
| ); | ); | ||||
| @@ -21,7 +21,7 @@ import {AdapterDayjs} from "@mui/x-date-pickers/AdapterDayjs"; | |||||
| // ==============================|| DASHBOARD - DEFAULT ||============================== // | // ==============================|| DASHBOARD - DEFAULT ||============================== // | ||||
| const SearchForm = ({ applySearch, searchCriteria }) => { | |||||
| const SearchForm = ({ applySearch, searchCriteria, onGridReady }) => { | |||||
| const intl = useIntl(); | const intl = useIntl(); | ||||
| const [minDate, setMinDate] = React.useState(searchCriteria.dateFrom); | const [minDate, setMinDate] = React.useState(searchCriteria.dateFrom); | ||||
| const [maxDate, setMaxDate] = React.useState(searchCriteria.dateTo); | const [maxDate, setMaxDate] = React.useState(searchCriteria.dateTo); | ||||
| @@ -64,9 +64,11 @@ const SearchForm = ({ applySearch, searchCriteria }) => { | |||||
| sentDateTo = DateUtils.dateValue(toDateValue) | sentDateTo = DateUtils.dateValue(toDateValue) | ||||
| } | } | ||||
| const temp = { | const temp = { | ||||
| keywork: data.keywork, | |||||
| keyword: data.keyword, | |||||
| dateFrom: sentDateFrom, | dateFrom: sentDateFrom, | ||||
| dateTo: sentDateTo, | dateTo: sentDateTo, | ||||
| start:0, | |||||
| limit:10 | |||||
| }; | }; | ||||
| applySearch(temp); | applySearch(temp); | ||||
| }; | }; | ||||
| @@ -75,6 +77,8 @@ const SearchForm = ({ applySearch, searchCriteria }) => { | |||||
| setMinDate(DateUtils.dateValue(new Date().setDate(new Date().getDate()-14))) | setMinDate(DateUtils.dateValue(new Date().setDate(new Date().getDate()-14))) | ||||
| setMaxDate(DateUtils.dateValue(new Date())) | setMaxDate(DateUtils.dateValue(new Date())) | ||||
| reset(); | reset(); | ||||
| localStorage.setItem('searchCriteria',"") | |||||
| } | } | ||||
| @@ -115,7 +119,7 @@ const SearchForm = ({ applySearch, searchCriteria }) => { | |||||
| <Grid item xs={12} s={6} md={6} lg={4} sx={{ ml: 3, mr: 3, mb: 3 }}> | <Grid item xs={12} s={6} md={6} lg={4} sx={{ ml: 3, mr: 3, mb: 3 }}> | ||||
| <Grid container> | <Grid container> | ||||
| <Grid item xs={5.25} s={5.25} md={5.25} lg={5.5}> | <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']}> | <DemoItem components={['DatePicker']}> | ||||
| <DatePicker | <DatePicker | ||||
| id="dateFrom" | id="dateFrom" | ||||
| @@ -147,7 +151,7 @@ const SearchForm = ({ applySearch, searchCriteria }) => { | |||||
| </Grid> | </Grid> | ||||
| <Grid item xs={5.25} s={5.25} md={5.25} lg={5.5}> | <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']}> | <DemoItem components={['DatePicker']}> | ||||
| <DatePicker | <DatePicker | ||||
| id="dateTo" | id="dateTo" | ||||
| @@ -196,6 +200,7 @@ const SearchForm = ({ applySearch, searchCriteria }) => { | |||||
| <Button | <Button | ||||
| variant="contained" | variant="contained" | ||||
| type="submit" | type="submit" | ||||
| disabled={onGridReady} | |||||
| aria-label={intl.formatMessage({id: 'submit'})} | aria-label={intl.formatMessage({id: 'submit'})} | ||||
| > | > | ||||
| <FormattedMessage id="submit"/> | <FormattedMessage id="submit"/> | ||||
| @@ -5,9 +5,9 @@ import { | |||||
| Stack | Stack | ||||
| } from '@mui/material'; | } from '@mui/material'; | ||||
| import MainCard from "components/MainCard"; | import MainCard from "components/MainCard"; | ||||
| import * as UrlUtils from "utils/ApiPathConst"; | |||||
| // import * as UrlUtils from "utils/ApiPathConst"; | |||||
| import * as React from "react"; | import * as React from "react"; | ||||
| import * as HttpUtils from "utils/HttpUtils"; | |||||
| // import * as HttpUtils from "utils/HttpUtils"; | |||||
| import * as DateUtils from "utils/DateUtils"; | import * as DateUtils from "utils/DateUtils"; | ||||
| import Loadable from 'components/Loadable'; | import Loadable from 'components/Loadable'; | ||||
| @@ -16,7 +16,8 @@ const SearchForm = Loadable(React.lazy(() => import('./SearchForm'))); | |||||
| const EventTable = Loadable(React.lazy(() => import('./DataGrid'))); | const EventTable = Loadable(React.lazy(() => import('./DataGrid'))); | ||||
| import titleBackgroundImg from 'assets/images/dashboard/gazette-bar.png' | import titleBackgroundImg from 'assets/images/dashboard/gazette-bar.png' | ||||
| import {FormattedMessage} from "react-intl"; | import {FormattedMessage} from "react-intl"; | ||||
| import { getSearchCriteria } from "auth/utils"; | |||||
| import usePageTitle from "components/usePageTitle"; | |||||
| const BackgroundHead = { | const BackgroundHead = { | ||||
| backgroundImage: `url(${titleBackgroundImg})`, | backgroundImage: `url(${titleBackgroundImg})`, | ||||
| width: '100%', | width: '100%', | ||||
| @@ -30,46 +31,64 @@ const BackgroundHead = { | |||||
| // ==============================|| DASHBOARD - DEFAULT ||============================== // | // ==============================|| DASHBOARD - DEFAULT ||============================== // | ||||
| const Index = () => { | const Index = () => { | ||||
| usePageTitle("systemMessage"); | |||||
| const [record,setRecord] = React.useState([]); | |||||
| const [searchCriteria, setSearchCriteria] = React.useState({ | |||||
| dateTo: DateUtils.dateValue(new Date()), | |||||
| dateFrom: DateUtils.dateValue(new Date().setDate(new Date().getDate()-14)), | |||||
| }); | |||||
| const [searchCriteria, setSearchCriteria] = React.useState({}); | |||||
| const [onReady, setOnReady] = React.useState(false); | const [onReady, setOnReady] = React.useState(false); | ||||
| const [onGridReady, setGridOnReady] = React.useState(false); | |||||
| React.useEffect(() => { | React.useEffect(() => { | ||||
| setOnReady(true); | |||||
| }, [record]); | |||||
| if (Object.keys(getSearchCriteria(window.location.pathname)).length>0){ | |||||
| setSearchCriteria(getSearchCriteria(window.location.pathname)) | |||||
| }else{ | |||||
| localStorage.setItem('searchCriteria',"") | |||||
| setSearchCriteria({ | |||||
| dateTo: DateUtils.dateValue(new Date()), | |||||
| dateFrom: DateUtils.dateValue(new Date().setDate(new Date().getDate()-14)), | |||||
| }) | |||||
| } | |||||
| }, []); | |||||
| React.useEffect(() => { | React.useEffect(() => { | ||||
| loadGrid(); | |||||
| if(Object.keys(searchCriteria).length>0){ | |||||
| setOnReady(true); | |||||
| } | |||||
| }, [searchCriteria]); | }, [searchCriteria]); | ||||
| function loadGrid(){ | |||||
| HttpUtils.get({ | |||||
| url: UrlUtils.GET_MSG_LIST, | |||||
| params: searchCriteria, | |||||
| onSuccess: function(responseData){ | |||||
| setRecord(responseData); | |||||
| } | |||||
| }); | |||||
| } | |||||
| // function loadGrid(){ | |||||
| // HttpUtils.get({ | |||||
| // url: UrlUtils.GET_MSG_LIST, | |||||
| // params: searchCriteria, | |||||
| // onSuccess: function(responseData){ | |||||
| // setRecord(responseData); | |||||
| // } | |||||
| // }); | |||||
| // } | |||||
| function applySearch(input) { | function applySearch(input) { | ||||
| setGridOnReady(true) | |||||
| setSearchCriteria(input); | setSearchCriteria(input); | ||||
| localStorage.setItem('searchCriteria', JSON.stringify({path:window.location.pathname,data:input})) | |||||
| } | |||||
| function applyGridOnReady(input) { | |||||
| setGridOnReady(input); | |||||
| } | } | ||||
| return ( | return ( | ||||
| !onReady ? | !onReady ? | ||||
| <LoadingComponent/> | |||||
| <Grid container sx={{ minHeight: '95vh', mb: 3 }} direction="column" justifyContent="center" alignItems="center"> | |||||
| <Grid item> | |||||
| <LoadingComponent /> | |||||
| </Grid> | |||||
| </Grid> | |||||
| : | : | ||||
| <Grid container sx={{minHeight: '85vh',backgroundColor:'#ffffff'}} direction="column"> | |||||
| <Grid container sx={{ minHeight: '95vh',backgroundColor: 'backgroundColor.default' }} direction="column"> | |||||
| <Grid item xs={12}> | <Grid item xs={12}> | ||||
| <div style={BackgroundHead}> | <div style={BackgroundHead}> | ||||
| <Stack direction="row" height='70px' justifyContent="flex-start" alignItems="center"> | <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"/> | <FormattedMessage id="systemMessage"/> | ||||
| </Typography> | </Typography> | ||||
| </Stack> | </Stack> | ||||
| @@ -78,8 +97,9 @@ const Index = () => { | |||||
| {/*row 1*/} | {/*row 1*/} | ||||
| <Grid item xs={12} md={12} lg={12}> | <Grid item xs={12} md={12} lg={12}> | ||||
| <SearchForm | <SearchForm | ||||
| applySearch={applySearch} | |||||
| searchCriteria={searchCriteria} | |||||
| applySearch={applySearch} | |||||
| searchCriteria={searchCriteria} | |||||
| onGridReady={onGridReady} | |||||
| /> | /> | ||||
| </Grid> | </Grid> | ||||
| {/*row 2*/} | {/*row 2*/} | ||||
| @@ -90,7 +110,10 @@ const Index = () => { | |||||
| sx={{width: "-webkit-fill-available"}} | sx={{width: "-webkit-fill-available"}} | ||||
| > | > | ||||
| <EventTable | <EventTable | ||||
| recordList={record} | |||||
| // recordList={record} | |||||
| searchCriteria={searchCriteria} | |||||
| applyGridOnReady={applyGridOnReady} | |||||
| applySearch={applySearch} | |||||
| /> | /> | ||||
| </MainCard> | </MainCard> | ||||
| </Grid> | </Grid> | ||||
| @@ -14,6 +14,7 @@ import { useEffect, useState, lazy } from "react"; | |||||
| import * as DateUtils from 'utils/DateUtils'; | import * as DateUtils from 'utils/DateUtils'; | ||||
| import * as HttpUtils from 'utils/HttpUtils'; | import * as HttpUtils from 'utils/HttpUtils'; | ||||
| import * as UrlUtils from "utils/ApiPathConst"; | import * as UrlUtils from "utils/ApiPathConst"; | ||||
| import {checkMarkAsCreditClient} from 'utils/Utils'; | |||||
| import * as FieldUtils from "utils/FieldUtils"; | import * as FieldUtils from "utils/FieldUtils"; | ||||
| import * as ComboData from "utils/ComboData"; | import * as ComboData from "utils/ComboData"; | ||||
| const LoadingComponent = Loadable(lazy(() => import('../../extra-pages/LoadingComponent'))); | const LoadingComponent = Loadable(lazy(() => import('../../extra-pages/LoadingComponent'))); | ||||
| @@ -24,18 +25,20 @@ import { PNSPS_BUTTON_THEME } from "themes/buttonConst"; | |||||
| import { ThemeProvider } from "@emotion/react"; | import { ThemeProvider } from "@emotion/react"; | ||||
| import { isGrantedAny } from "auth/utils"; | import { isGrantedAny } from "auth/utils"; | ||||
| import {DatePicker} from "@mui/x-date-pickers/DatePicker"; | |||||
| import { DatePicker } from "@mui/x-date-pickers/DatePicker"; | |||||
| import dayjs from "dayjs"; | import dayjs from "dayjs"; | ||||
| import {DemoItem} from "@mui/x-date-pickers/internals/demo"; | |||||
| import {LocalizationProvider} from "@mui/x-date-pickers/LocalizationProvider"; | |||||
| import {AdapterDayjs} from "@mui/x-date-pickers/AdapterDayjs"; | |||||
| import { DemoItem } from "@mui/x-date-pickers/internals/demo"; | |||||
| import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider"; | |||||
| import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs"; | |||||
| // ==============================|| DASHBOARD - DEFAULT ||============================== // | // ==============================|| DASHBOARD - DEFAULT ||============================== // | ||||
| const OrganizationCard = ({ userData, loadDataFun, id, setEditModeFun }) => { | const OrganizationCard = ({ userData, loadDataFun, id, setEditModeFun }) => { | ||||
| const intl = useIntl(); | const intl = useIntl(); | ||||
| const [creditorConfirmPopUp, setCreditorConfirmPopUp] = React.useState(false); | const [creditorConfirmPopUp, setCreditorConfirmPopUp] = React.useState(false); | ||||
| const [nonCreditorConfirmPopUp, setNonCreditorConfirmPopUp] = React.useState(false); | const [nonCreditorConfirmPopUp, setNonCreditorConfirmPopUp] = React.useState(false); | ||||
| const [afterSendPopUp, setAfterSendPopUp] = React.useState(false); | |||||
| const [currentUserData, setCurrentUserData] = useState({}); | const [currentUserData, setCurrentUserData] = useState({}); | ||||
| const [overduePublicNotice, setOverduePublicNotice] = useState(0); | |||||
| const [editMode, setEditMode] = useState(false); | const [editMode, setEditMode] = useState(false); | ||||
| const [createMode, setCreateMode] = useState(false); | const [createMode, setCreateMode] = useState(false); | ||||
| const [onReady, setOnReady] = useState(false); | const [onReady, setOnReady] = useState(false); | ||||
| @@ -44,7 +47,7 @@ const OrganizationCard = ({ userData, loadDataFun, id, setEditModeFun }) => { | |||||
| const [fromDate, setFromDate] = React.useState(null); | const [fromDate, setFromDate] = React.useState(null); | ||||
| const [currentFromDate, setCurrentFromDate] = React.useState(null); | const [currentFromDate, setCurrentFromDate] = React.useState(null); | ||||
| const [fromDateValue, setFromDateValue] = React.useState(null); | const [fromDateValue, setFromDateValue] = React.useState(null); | ||||
| const {register, handleSubmit, reset} = useForm() | |||||
| const { register, handleSubmit, reset } = useForm() | |||||
| React.useEffect(() => { | React.useEffect(() => { | ||||
| setFromDateValue(fromDate); | setFromDateValue(fromDate); | ||||
| @@ -54,14 +57,14 @@ const OrganizationCard = ({ userData, loadDataFun, id, setEditModeFun }) => { | |||||
| //if state data are ready and assign to different field | //if state data are ready and assign to different field | ||||
| // console.log(currentApplicationDetailData) | // console.log(currentApplicationDetailData) | ||||
| if (Object.keys(currentUserData).length > 0) { | if (Object.keys(currentUserData).length > 0) { | ||||
| console.log(currentUserData) | |||||
| if(DateUtils.dateValue(currentUserData.brExpiryDate)>DateUtils.dateValue(minDate)){ | |||||
| // console.log(currentUserData) | |||||
| if (DateUtils.dateValue(currentUserData.brExpiryDate) > DateUtils.dateValue(minDate)) { | |||||
| setFromDate(currentUserData.brExpiryDate); | setFromDate(currentUserData.brExpiryDate); | ||||
| }else{ | |||||
| } else { | |||||
| setCurrentFromDate(currentUserData.brExpiryDate); | setCurrentFromDate(currentUserData.brExpiryDate); | ||||
| // setErrorMsg("Please select a date after today.") | // setErrorMsg("Please select a date after today.") | ||||
| } | } | ||||
| setOnReady(true); | setOnReady(true); | ||||
| } | } | ||||
| }, [currentUserData]); | }, [currentUserData]); | ||||
| @@ -115,7 +118,7 @@ const OrganizationCard = ({ userData, loadDataFun, id, setEditModeFun }) => { | |||||
| let sentDateFrom = ""; | let sentDateFrom = ""; | ||||
| if (fromDateValue == null) { | if (fromDateValue == null) { | ||||
| setErrorMsg(intl.formatMessage({ id: 'pleaseFillInBusinessRegCertValidityDate' })) | setErrorMsg(intl.formatMessage({ id: 'pleaseFillInBusinessRegCertValidityDate' })) | ||||
| }else{ | |||||
| } else { | |||||
| sentDateFrom = DateUtils.dateValue(fromDateValue) | sentDateFrom = DateUtils.dateValue(fromDateValue) | ||||
| HttpUtils.post({ | HttpUtils.post({ | ||||
| url: UrlUtils.POST_ORG_SAVE_PATH, | url: UrlUtils.POST_ORG_SAVE_PATH, | ||||
| @@ -123,7 +126,7 @@ const OrganizationCard = ({ userData, loadDataFun, id, setEditModeFun }) => { | |||||
| id: id > 0 ? id : null, | id: id > 0 ? id : null, | ||||
| enCompanyName: values.enCompanyName, | enCompanyName: values.enCompanyName, | ||||
| chCompanyName: values.chCompanyName, | chCompanyName: values.chCompanyName, | ||||
| orgShortName: values.orgShortName==="N/A"?"":values.orgShortName, | |||||
| orgShortName: values.orgShortName === "N/A" ? "" : values.orgShortName, | |||||
| brNo: values.brNo, | brNo: values.brNo, | ||||
| // brExpiryDate: values.brExpiryDate, | // brExpiryDate: values.brExpiryDate, | ||||
| brExpiryDate: sentDateFrom, | brExpiryDate: sentDateFrom, | ||||
| @@ -188,9 +191,9 @@ const OrganizationCard = ({ userData, loadDataFun, id, setEditModeFun }) => { | |||||
| const onSubmit = (data) => { | const onSubmit = (data) => { | ||||
| let sentOrgShortName = ""; | let sentOrgShortName = ""; | ||||
| if(data.orgShortName!=null && data.orgShortName!="" && data.orgShortName!="N/A"){ | |||||
| sentOrgShortName = data.orgShortName | |||||
| if (sentOrgShortName.length <=24){ | |||||
| if (data.orgShortName != null && data.orgShortName != "" && data.orgShortName != "N/A") { | |||||
| sentOrgShortName = data.orgShortName | |||||
| if (sentOrgShortName.length <= 24) { | |||||
| const temp = { | const temp = { | ||||
| orgShortName: sentOrgShortName, | orgShortName: sentOrgShortName, | ||||
| }; | }; | ||||
| @@ -230,6 +233,17 @@ const OrganizationCard = ({ userData, loadDataFun, id, setEditModeFun }) => { | |||||
| }); | }); | ||||
| } | } | ||||
| const sendDn_Overdue = () => { | |||||
| setNonCreditorConfirmPopUp(false); | |||||
| HttpUtils.get({ | |||||
| url: UrlUtils.GET_SEND_OVERDUE_CREDITOR_LIST + "/" + id, | |||||
| onSuccess: (responseData) => { | |||||
| setOverduePublicNotice(responseData.overduePublicNotice); | |||||
| setAfterSendPopUp(true); | |||||
| } | |||||
| }); | |||||
| } | |||||
| return ( | return ( | ||||
| <MainCard elevation={0} | <MainCard elevation={0} | ||||
| border={false} | border={false} | ||||
| @@ -299,29 +313,47 @@ const OrganizationCard = ({ userData, loadDataFun, id, setEditModeFun }) => { | |||||
| { | { | ||||
| currentUserData.creditor ? | currentUserData.creditor ? | ||||
| <Grid item sx={{ ml: 3, mr: 3 }}> | |||||
| <ThemeProvider theme={PNSPS_BUTTON_THEME}> | |||||
| <Button | |||||
| variant="contained" | |||||
| color="error" | |||||
| onClick={() => setNonCreditorConfirmPopUp(true)} | |||||
| > | |||||
| Mark as Non-Credit Client | |||||
| </Button> | |||||
| </ThemeProvider> | |||||
| </Grid> | |||||
| !checkMarkAsCreditClient()? | |||||
| <Grid item sx={{ ml: 3, mr: 3 }}> | |||||
| <ThemeProvider theme={PNSPS_BUTTON_THEME}> | |||||
| <Button | |||||
| variant="contained" | |||||
| color="error" | |||||
| onClick={() => setNonCreditorConfirmPopUp(true)} | |||||
| > | |||||
| Mark as Non-Credit Client | |||||
| </Button> | |||||
| </ThemeProvider> | |||||
| </Grid>:null | |||||
| : | : | ||||
| <Grid item sx={{ ml: 3, mr: 3 }}> | |||||
| <ThemeProvider theme={PNSPS_BUTTON_THEME}> | |||||
| <Button | |||||
| variant="contained" | |||||
| color="orange" | |||||
| onClick={() => setCreditorConfirmPopUp(true)} | |||||
| > | |||||
| Mark as Credit Client | |||||
| </Button> | |||||
| </ThemeProvider> | |||||
| </Grid> | |||||
| <> | |||||
| {!checkMarkAsCreditClient()? | |||||
| <Grid item sx={{ ml: 3, mr: 3 }}> | |||||
| <ThemeProvider theme={PNSPS_BUTTON_THEME}> | |||||
| <Button | |||||
| variant="contained" | |||||
| color="orange" | |||||
| onClick={() => setCreditorConfirmPopUp(true)} | |||||
| > | |||||
| Mark as Credit Client | |||||
| </Button> | |||||
| </ThemeProvider> | |||||
| </Grid>:null | |||||
| } | |||||
| { isGrantedAny("MAINTAIN_DEMANDNOTE")? | |||||
| <Grid item sx={{ ml: 3, mr: 3 }}> | |||||
| <ThemeProvider theme={PNSPS_BUTTON_THEME}> | |||||
| <Button | |||||
| variant="contained" | |||||
| color="primary" | |||||
| onClick={() => sendDn_Overdue(true)} | |||||
| > | |||||
| Generate O/S DN List | |||||
| </Button> | |||||
| </ThemeProvider> | |||||
| </Grid> : null | |||||
| } | |||||
| </> | |||||
| } | } | ||||
| </> | </> | ||||
| } | } | ||||
| @@ -357,18 +389,20 @@ const OrganizationCard = ({ userData, loadDataFun, id, setEditModeFun }) => { | |||||
| })} | })} | ||||
| </Grid> | </Grid> | ||||
| <Grid item xs={12} lg={4} > | |||||
| <FormControlLabel | |||||
| control={<Checkbox checked={formik.values.creditor} />} | |||||
| label="is Credit Client" | |||||
| name="creditor" | |||||
| onChange={() => { | |||||
| formik.setFieldValue("creditor", !formik.values.creditor); | |||||
| }} | |||||
| disabled={true} | |||||
| //disabled={!editMode && !createMode} | |||||
| /> | |||||
| </Grid> | |||||
| {!checkMarkAsCreditClient()? | |||||
| <Grid item xs={12} lg={4} > | |||||
| <FormControlLabel | |||||
| control={<Checkbox checked={formik.values.creditor} />} | |||||
| label="is Credit Client" | |||||
| name="creditor" | |||||
| onChange={() => { | |||||
| formik.setFieldValue("creditor", !formik.values.creditor); | |||||
| }} | |||||
| disabled={true} | |||||
| //disabled={!editMode && !createMode} | |||||
| /> | |||||
| </Grid>:null | |||||
| } | |||||
| <Grid item xs={12} lg={4} ></Grid> | <Grid item xs={12} lg={4} ></Grid> | ||||
| @@ -396,18 +430,18 @@ const OrganizationCard = ({ userData, loadDataFun, id, setEditModeFun }) => { | |||||
| <Typography variant="pnspsFormParagraphBold">{FieldUtils.notNullFieldLabel("Expiry Date:")}</Typography> | <Typography variant="pnspsFormParagraphBold">{FieldUtils.notNullFieldLabel("Expiry Date:")}</Typography> | ||||
| </Grid> | </Grid> | ||||
| <Grid item xs={12} md={6} lg={6}> | <Grid item xs={12} md={6} lg={6}> | ||||
| {(!editMode && !createMode)? | |||||
| {(!editMode && !createMode) ? | |||||
| <TextField | <TextField | ||||
| fullWidth | fullWidth | ||||
| id="currentExDate" | id="currentExDate" | ||||
| // error={(fromDate===null)} | // error={(fromDate===null)} | ||||
| // type="date" | // type="date" | ||||
| name="currentExDate" | name="currentExDate" | ||||
| value={fromDate!=null?DateUtils.dateStr(fromDate):DateUtils.dateStr(currentFromDate)} | |||||
| value={fromDate != null ? DateUtils.dateStr(fromDate) : DateUtils.dateStr(currentFromDate)} | |||||
| disabled={true} | disabled={true} | ||||
| />: | |||||
| <LocalizationProvider dateAdapter={AdapterDayjs}> | |||||
| <DemoItem components={['DatePicker']}> | |||||
| /> : | |||||
| <LocalizationProvider dateAdapter={AdapterDayjs} localeText={DateUtils.getPickerLocaleText(intl.locale)}> | |||||
| <DemoItem components={['DatePicker']}> | |||||
| <DatePicker | <DatePicker | ||||
| id="brExpiryDate" | id="brExpiryDate" | ||||
| name="brExpiryDate" | name="brExpiryDate" | ||||
| @@ -426,9 +460,9 @@ const OrganizationCard = ({ userData, loadDataFun, id, setEditModeFun }) => { | |||||
| onChange={(newValue) => { | onChange={(newValue) => { | ||||
| // console.log(newValue) | // console.log(newValue) | ||||
| // setErrorMsg("") | // setErrorMsg("") | ||||
| if(DateUtils.dateValue(newValue)>DateUtils.dateValue(new Date())){ | |||||
| if (DateUtils.dateValue(newValue) > DateUtils.dateValue(new Date())) { | |||||
| setFromDate(newValue); | setFromDate(newValue); | ||||
| }else{ | |||||
| } else { | |||||
| // setErrorMsg("Please select a date after today.") | // setErrorMsg("Please select a date after today.") | ||||
| } | } | ||||
| }} | }} | ||||
| @@ -438,19 +472,20 @@ const OrganizationCard = ({ userData, loadDataFun, id, setEditModeFun }) => { | |||||
| } | } | ||||
| </Grid> | </Grid> | ||||
| { | { | ||||
| fromDate==null? | |||||
| (!editMode && !createMode)? | |||||
| <FormHelperText error id="helper-text-date"> | |||||
| Please select a date after today. | |||||
| </FormHelperText> | |||||
| : | |||||
| fromDate == null ? | |||||
| (!editMode && !createMode) ? | |||||
| // <FormHelperText error id="helper-text-date"> | |||||
| // Please select a date after today. | |||||
| // </FormHelperText> | |||||
| null | |||||
| : | |||||
| <FormHelperText error id="helper-text-date"> | <FormHelperText error id="helper-text-date"> | ||||
| {intl.formatMessage({ id: 'pleaseFillInBusinessRegCertValidityDate' })} | {intl.formatMessage({ id: 'pleaseFillInBusinessRegCertValidityDate' })} | ||||
| </FormHelperText> | </FormHelperText> | ||||
| : | |||||
| : | |||||
| null | null | ||||
| } | |||||
| } | |||||
| </Grid> | </Grid> | ||||
| </Grid> | </Grid> | ||||
| @@ -488,27 +523,6 @@ const OrganizationCard = ({ userData, loadDataFun, id, setEditModeFun }) => { | |||||
| })} | })} | ||||
| </Grid> | </Grid> | ||||
| <Grid item xs={12} lg={4} > | |||||
| {FieldUtils.getComboField({ | |||||
| label: FieldUtils.notNullFieldLabel("Country:"), | |||||
| valueName: "country", | |||||
| disabled: (!editMode && !createMode), | |||||
| dataList: ComboData.country, | |||||
| getOptionLabel: (option) => option.type ? intl.formatMessage({ id: option.type }) : "", | |||||
| form: formik | |||||
| })} | |||||
| </Grid> | |||||
| <Grid item xs={12} lg={4} > | |||||
| {FieldUtils.getComboField({ | |||||
| label: FieldUtils.notNullFieldLabel("District:"), | |||||
| valueName: "district", | |||||
| disabled: (!editMode && !createMode), | |||||
| dataList: ComboData.district, | |||||
| getOptionLabel: (option) => option.type ? intl.formatMessage({ id: option.type }) : "", | |||||
| form: formik | |||||
| })} | |||||
| </Grid> | |||||
| { | { | ||||
| currentUserData.creditor ? | currentUserData.creditor ? | ||||
| <Grid item xs={12} lg={4} > | <Grid item xs={12} lg={4} > | ||||
| @@ -533,6 +547,28 @@ const OrganizationCard = ({ userData, loadDataFun, id, setEditModeFun }) => { | |||||
| })} | })} | ||||
| </Grid> | </Grid> | ||||
| <Grid item xs={12} lg={12} > | |||||
| {FieldUtils.getProfileComboField({ | |||||
| label: "", | |||||
| valueName: "district", | |||||
| disabled: (!editMode && !createMode), | |||||
| dataList: ComboData.district, | |||||
| getOptionLabel: (option) => option.type ? intl.formatMessage({ id: option.type }) : "", | |||||
| form: formik | |||||
| })} | |||||
| </Grid> | |||||
| <Grid item xs={12} lg={12} > | |||||
| {FieldUtils.getProfileComboField({ | |||||
| label: "", | |||||
| valueName: "country", | |||||
| disabled: true, | |||||
| dataList: ComboData.country, | |||||
| getOptionLabel: (option) => option.type ? intl.formatMessage({ id: option.type }) : "", | |||||
| form: formik | |||||
| })} | |||||
| </Grid> | |||||
| <Grid item lg={12} ></Grid> | <Grid item lg={12} ></Grid> | ||||
| </Grid> | </Grid> | ||||
| @@ -566,7 +602,7 @@ const OrganizationCard = ({ userData, loadDataFun, id, setEditModeFun }) => { | |||||
| {...register("orgShortName")} | {...register("orgShortName")} | ||||
| id='orgShortName' | id='orgShortName' | ||||
| label="Organisation Short Name" | label="Organisation Short Name" | ||||
| defaultValue={currentUserData.orgShortName!="N/A"?currentUserData.orgShortName:""} | |||||
| defaultValue={currentUserData.orgShortName != "N/A" ? currentUserData.orgShortName : ""} | |||||
| InputLabelProps={{ | InputLabelProps={{ | ||||
| shrink: true | shrink: true | ||||
| }} | }} | ||||
| @@ -605,6 +641,27 @@ const OrganizationCard = ({ userData, loadDataFun, id, setEditModeFun }) => { | |||||
| </DialogActions> | </DialogActions> | ||||
| </Dialog> | </Dialog> | ||||
| </div> | </div> | ||||
| <div> | |||||
| <Dialog | |||||
| open={afterSendPopUp} | |||||
| onClose={() => setAfterSendPopUp(false)} | |||||
| PaperProps={{ | |||||
| sx: { | |||||
| minWidth: '40vw', | |||||
| maxWidth: { xs: '90vw', s: '90vw', m: '70vw', lg: '70vw' }, | |||||
| maxHeight: { xs: '90vh', s: '70vh', m: '70vh', lg: '60vh' } | |||||
| } | |||||
| }} | |||||
| > | |||||
| <DialogTitle><Typography variant="h3">Info</Typography></DialogTitle> | |||||
| <DialogContent style={{ display: 'flex', }}> | |||||
| <Typography variant="h4" style={{ padding: '16px' }}>Overdue Public Notice count: {overduePublicNotice}</Typography> | |||||
| </DialogContent> | |||||
| <DialogActions> | |||||
| <Button onClick={() => setAfterSendPopUp(false)}><Typography variant="h5">OK</Typography></Button> | |||||
| </DialogActions> | |||||
| </Dialog> | |||||
| </div> | |||||
| </MainCard> | </MainCard> | ||||
| ); | ); | ||||
| }; | }; | ||||
| @@ -1,6 +1,6 @@ | |||||
| // material-ui | // material-ui | ||||
| import { | import { | ||||
| Grid, Button, | |||||
| Grid, Button, | |||||
| // Checkbox, FormControlLabel, | // Checkbox, FormControlLabel, | ||||
| Typography, | Typography, | ||||
| Dialog, DialogTitle, DialogContent, DialogActions, | Dialog, DialogTitle, DialogContent, DialogActions, | ||||
| @@ -20,9 +20,9 @@ const LoadingComponent = Loadable(lazy(() => import('../../extra-pages/LoadingCo | |||||
| import Loadable from 'components/Loadable'; | import Loadable from 'components/Loadable'; | ||||
| import { lazy } from 'react'; | import { lazy } from 'react'; | ||||
| import { notifySaveSuccess } from 'utils/CommonFunction'; | import { notifySaveSuccess } from 'utils/CommonFunction'; | ||||
| import {FormattedMessage, useIntl} from "react-intl"; | |||||
| import {PNSPS_BUTTON_THEME} from "themes/buttonConst"; | |||||
| import {ThemeProvider} from "@emotion/react"; | |||||
| import { FormattedMessage, useIntl } from "react-intl"; | |||||
| import { PNSPS_BUTTON_THEME } from "themes/buttonConst"; | |||||
| import { ThemeProvider } from "@emotion/react"; | |||||
| // ==============================|| DASHBOARD - DEFAULT ||============================== // | // ==============================|| DASHBOARD - DEFAULT ||============================== // | ||||
| @@ -54,19 +54,19 @@ const OrganizationPubCard = ({ userData, loadDataFun, id, setEditModeFun }) => { | |||||
| enableReinitialize: true, | enableReinitialize: true, | ||||
| initialValues: currentUserData, | initialValues: currentUserData, | ||||
| validationSchema: yup.object().shape({ | validationSchema: yup.object().shape({ | ||||
| addressLine1: yup.string().max(40).required(displayErrorMsg(intl.formatMessage({id: 'validateAddressLine1'}))), | |||||
| addressLine2: yup.string().max(40, displayErrorMsg(intl.formatMessage({id: 'noMoreThen40Words'}))), | |||||
| addressLine3: yup.string().max(40, displayErrorMsg(intl.formatMessage({id: 'noMoreThen40Words'}))), | |||||
| tel_countryCode: yup.string().min(3, displayErrorMsg(intl.formatMessage({id: 'requireDialingCode'}))), | |||||
| phoneNumber: yup.string().min(8, displayErrorMsg(intl.formatMessage({id: 'requiredValidNumber'}))).required(displayErrorMsg(intl.formatMessage({id: 'requireContactNumber'}))), | |||||
| addressLine1: yup.string().max(40).required(displayErrorMsg(intl.formatMessage({ id: 'validateAddressLine1' }))), | |||||
| addressLine2: yup.string().max(40, displayErrorMsg(intl.formatMessage({ id: 'noMoreThen40Words' }))), | |||||
| addressLine3: yup.string().max(40, displayErrorMsg(intl.formatMessage({ id: 'noMoreThen40Words' }))), | |||||
| tel_countryCode: yup.string().min(3, displayErrorMsg(intl.formatMessage({ id: 'requireDialingCode' }))), | |||||
| 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(), | faxNumber: yup.string().min(8, displayErrorMsg(intl.formatMessage({ id: 'require8Number' }))).nullable(), | ||||
| }), | }), | ||||
| onSubmit: values => { | onSubmit: values => { | ||||
| if (values.country==null){ | |||||
| setErrorMsg(intl.formatMessage({id: 'pleaseFillInCountry'})) | |||||
| if (values.country == null) { | |||||
| setErrorMsg(intl.formatMessage({ id: 'pleaseFillInCountry' })) | |||||
| } else { | } else { | ||||
| if (values.country.type =="hongKong" && values.district == null){ | |||||
| setErrorMsg(intl.formatMessage({id: 'pleaseFillInDistrict'})) | |||||
| if (values.country.type == "hongKong" && values.district == null) { | |||||
| setErrorMsg(intl.formatMessage({ id: 'pleaseFillInDistrict' })) | |||||
| } else { | } else { | ||||
| HttpUtils.post({ | HttpUtils.post({ | ||||
| url: UrlUtils.POST_PUB_ORG_SAVE_PATH, | url: UrlUtils.POST_PUB_ORG_SAVE_PATH, | ||||
| @@ -100,9 +100,9 @@ const OrganizationPubCard = ({ userData, loadDataFun, id, setEditModeFun }) => { | |||||
| } | } | ||||
| }); | }); | ||||
| useEffect(()=>{ | |||||
| useEffect(() => { | |||||
| setEditModeFun(editMode); | setEditModeFun(editMode); | ||||
| },[editMode]); | |||||
| }, [editMode]); | |||||
| useEffect(() => { | useEffect(() => { | ||||
| if (Object.keys(userData).length > 0) { | if (Object.keys(userData).length > 0) { | ||||
| @@ -188,7 +188,7 @@ const OrganizationPubCard = ({ userData, loadDataFun, id, setEditModeFun }) => { | |||||
| onClick={onEditClick} | onClick={onEditClick} | ||||
| color="success" | color="success" | ||||
| > | > | ||||
| < FormattedMessage id="edit" /> | |||||
| < FormattedMessage id="edit" /> | |||||
| </Button> | </Button> | ||||
| </ThemeProvider> | </ThemeProvider> | ||||
| </Grid> | </Grid> | ||||
| @@ -202,11 +202,11 @@ const OrganizationPubCard = ({ userData, loadDataFun, id, setEditModeFun }) => { | |||||
| <LoadingComponent /> | <LoadingComponent /> | ||||
| : | : | ||||
| <Grid container spacing={1}> | <Grid container spacing={1}> | ||||
| <Grid item xs={12}> | |||||
| {/* <Grid item xs={12}> | |||||
| <Typography variant="h4" sx={{ mb: 2, mr: 3, borderBottom: "1px solid black" }}> | <Typography variant="h4" sx={{ mb: 2, mr: 3, borderBottom: "1px solid black" }}> | ||||
| <FormattedMessage id="organizationDetails" /> | <FormattedMessage id="organizationDetails" /> | ||||
| </Typography> | </Typography> | ||||
| </Grid> | |||||
| </Grid> */} | |||||
| <Grid item xs={12}> | <Grid item xs={12}> | ||||
| <FormHelperText error id="helper-text-address1-signup"> | <FormHelperText error id="helper-text-address1-signup"> | ||||
| <Typography variant="errorMessage1"> | <Typography variant="errorMessage1"> | ||||
| @@ -216,27 +216,27 @@ const OrganizationPubCard = ({ userData, loadDataFun, id, setEditModeFun }) => { | |||||
| </Grid> | </Grid> | ||||
| <Grid item xs={12} lg={4} > | <Grid item xs={12} lg={4} > | ||||
| {FieldUtils.getTextField({ | {FieldUtils.getTextField({ | ||||
| label: intl.formatMessage({id: 'brNo'}) + ":", | |||||
| label: intl.formatMessage({ id: 'brNo' }) + ":", | |||||
| valueName: "brNo", | valueName: "brNo", | ||||
| disabled: true, | disabled: true, | ||||
| form: formik | form: formik | ||||
| })} | })} | ||||
| </Grid> | </Grid> | ||||
| <Grid item xs={12} lg={4} > | <Grid item xs={12} lg={4} > | ||||
| {/* {FieldUtils.getTextField({ | |||||
| label: intl.formatMessage({id: 'creditorAccount'}) + ":", | |||||
| valueName: "creditor", | |||||
| disabled: true, | |||||
| form: formik | |||||
| })} */} | |||||
| {FieldUtils.getTextField({ | |||||
| label: FieldUtils.notNullFieldLabel(intl.formatMessage({ id: 'expiryDate' }) + ":"), | |||||
| valueName: "brExpiryDate", | |||||
| disabled: true, | |||||
| form: formik | |||||
| })} | |||||
| </Grid> | </Grid> | ||||
| <Grid item xs={12} lg={4} ></Grid> | <Grid item xs={12} lg={4} ></Grid> | ||||
| <Grid item xs={12} lg={4} > | <Grid item xs={12} lg={4} > | ||||
| {FieldUtils.getTextField({ | {FieldUtils.getTextField({ | ||||
| label: FieldUtils.notNullFieldLabel(intl.formatMessage({id: 'nameEng'}) + ":"), | |||||
| label: FieldUtils.notNullFieldLabel(intl.formatMessage({ id: 'nameEng' }) + ":"), | |||||
| valueName: "enCompanyName", | valueName: "enCompanyName", | ||||
| disabled: true, | disabled: true, | ||||
| form: formik | form: formik | ||||
| @@ -245,7 +245,7 @@ const OrganizationPubCard = ({ userData, loadDataFun, id, setEditModeFun }) => { | |||||
| <Grid item xs={12} lg={4} > | <Grid item xs={12} lg={4} > | ||||
| {FieldUtils.getTextField({ | {FieldUtils.getTextField({ | ||||
| label: intl.formatMessage({id: 'nameChi'}) + ":", | |||||
| label: intl.formatMessage({ id: 'nameChi' }) + ":", | |||||
| valueName: "chCompanyName", | valueName: "chCompanyName", | ||||
| disabled: true, | disabled: true, | ||||
| form: formik | form: formik | ||||
| @@ -253,17 +253,12 @@ const OrganizationPubCard = ({ userData, loadDataFun, id, setEditModeFun }) => { | |||||
| </Grid> | </Grid> | ||||
| <Grid item xs={12} lg={4} > | <Grid item xs={12} lg={4} > | ||||
| {FieldUtils.getTextField({ | |||||
| label: FieldUtils.notNullFieldLabel(intl.formatMessage({id: 'expiryDate'}) + ":"), | |||||
| valueName: "brExpiryDate", | |||||
| disabled: true, | |||||
| form: formik | |||||
| })} | |||||
| </Grid> | </Grid> | ||||
| <Grid item xs={12} lg={4} > | <Grid item xs={12} lg={4} > | ||||
| {FieldUtils.getTextField({ | {FieldUtils.getTextField({ | ||||
| label: FieldUtils.notNullFieldLabel(intl.formatMessage({id: 'contactPerson'}) + ":"), | |||||
| label: FieldUtils.notNullFieldLabel(intl.formatMessage({ id: 'contactPerson' }) + ":"), | |||||
| valueName: "contactPerson", | valueName: "contactPerson", | ||||
| disabled: (!editMode && !createMode), | disabled: (!editMode && !createMode), | ||||
| form: formik | form: formik | ||||
| @@ -272,7 +267,7 @@ const OrganizationPubCard = ({ userData, loadDataFun, id, setEditModeFun }) => { | |||||
| <Grid item xs={12} lg={4} > | <Grid item xs={12} lg={4} > | ||||
| {FieldUtils.getPhoneField({ | {FieldUtils.getPhoneField({ | ||||
| label: FieldUtils.notNullFieldLabel(intl.formatMessage({id: 'userContactNumber'}) + ":"), | |||||
| label: FieldUtils.notNullFieldLabel(intl.formatMessage({ id: 'userContactNumber' }) + ":"), | |||||
| valueName: { | valueName: { | ||||
| code: "tel_countryCode", | code: "tel_countryCode", | ||||
| num: "phoneNumber" | num: "phoneNumber" | ||||
| @@ -284,7 +279,7 @@ const OrganizationPubCard = ({ userData, loadDataFun, id, setEditModeFun }) => { | |||||
| <Grid item xs={12} lg={4} > | <Grid item xs={12} lg={4} > | ||||
| {FieldUtils.getPhoneField({ | {FieldUtils.getPhoneField({ | ||||
| label: intl.formatMessage({id: 'contactFaxNumber'}) + ":", | |||||
| label: intl.formatMessage({ id: 'contactFaxNumber' }) + ":", | |||||
| valueName: { | valueName: { | ||||
| code: "fax_countryCode", | code: "fax_countryCode", | ||||
| num: "faxNumber" | num: "faxNumber" | ||||
| @@ -294,34 +289,35 @@ const OrganizationPubCard = ({ userData, loadDataFun, id, setEditModeFun }) => { | |||||
| })} | })} | ||||
| </Grid> | </Grid> | ||||
| <Grid item xs={12} lg={4} > | |||||
| {FieldUtils.getComboField({ | |||||
| label: FieldUtils.notNullFieldLabel(intl.formatMessage({id: 'country'}) + ":"), | |||||
| valueName: "country", | |||||
| <Grid item xs={12} lg={12} > | |||||
| {FieldUtils.getAddressField({ | |||||
| label: FieldUtils.notNullFieldLabel(intl.formatMessage({ id: 'formAddress' }) + ":"), | |||||
| valueName: ["addressLine1", "addressLine2", "addressLine3"], | |||||
| disabled: (!editMode && !createMode), | disabled: (!editMode && !createMode), | ||||
| dataList: ComboData.country, | |||||
| getOptionLabel: (option) => option.type? intl.formatMessage({ id: option.type }) : "", | |||||
| form: formik | form: formik | ||||
| })} | })} | ||||
| </Grid> | </Grid> | ||||
| <Grid item xs={12} lg={4} > | |||||
| {FieldUtils.getComboField({ | |||||
| label: FieldUtils.notNullFieldLabel(intl.formatMessage({id: 'district'}) + ":"), | |||||
| <Grid item xs={12} lg={12} > | |||||
| {FieldUtils.getProfileComboField({ | |||||
| // label: FieldUtils.notNullFieldLabel(""), | |||||
| label: "", | |||||
| valueName: "district", | valueName: "district", | ||||
| disabled: (!editMode && !createMode), | disabled: (!editMode && !createMode), | ||||
| dataList: ComboData.district, | dataList: ComboData.district, | ||||
| getOptionLabel: (option) => option.type? intl.formatMessage({ id: option.type }) : "", | |||||
| getOptionLabel: (option) => option.type ? intl.formatMessage({ id: option.type }) : "", | |||||
| form: formik | form: formik | ||||
| })} | })} | ||||
| </Grid> | </Grid> | ||||
| <Grid item xs={12} lg={12} > | <Grid item xs={12} lg={12} > | ||||
| {FieldUtils.getAddressField({ | |||||
| label: FieldUtils.notNullFieldLabel(intl.formatMessage({id: 'formAddress'}) + ":"), | |||||
| valueName: ["addressLine1", "addressLine2", "addressLine3"], | |||||
| disabled: (!editMode && !createMode), | |||||
| {FieldUtils.getProfileComboField({ | |||||
| // label: FieldUtils.notNullFieldLabel(""), | |||||
| label: "", | |||||
| valueName: "country", | |||||
| disabled: true, | |||||
| dataList: ComboData.country, | |||||
| getOptionLabel: (option) => option.type ? intl.formatMessage({ id: option.type }) : "", | |||||
| form: formik | form: formik | ||||
| })} | })} | ||||
| </Grid> | </Grid> | ||||
| @@ -348,7 +344,7 @@ const OrganizationPubCard = ({ userData, loadDataFun, id, setEditModeFun }) => { | |||||
| <Typography variant="h4" style={{ padding: '16px' }}>Are you sure mark as Credit Client?</Typography> | <Typography variant="h4" style={{ padding: '16px' }}>Are you sure mark as Credit Client?</Typography> | ||||
| </DialogContent> | </DialogContent> | ||||
| <DialogActions> | <DialogActions> | ||||
| <Button onClick={() => setCreditorConfirmPopUp(false)}><Typography variant="h5">Cancel</Typography></Button> | |||||
| <Button onClick={() => setCreditorConfirmPopUp(false)}><Typography variant="h5">Cancel</Typography></Button> | |||||
| <Button onClick={() => markAsCreditor()}><Typography variant="h5">Confirm</Typography></Button> | <Button onClick={() => markAsCreditor()}><Typography variant="h5">Confirm</Typography></Button> | ||||
| </DialogActions> | </DialogActions> | ||||
| </Dialog> | </Dialog> | ||||
| @@ -370,7 +366,7 @@ const OrganizationPubCard = ({ userData, loadDataFun, id, setEditModeFun }) => { | |||||
| <Typography variant="h4" style={{ padding: '16px' }}>Are you sure mark as Non-Credit Client?</Typography> | <Typography variant="h4" style={{ padding: '16px' }}>Are you sure mark as Non-Credit Client?</Typography> | ||||
| </DialogContent> | </DialogContent> | ||||
| <DialogActions> | <DialogActions> | ||||
| <Button onClick={() => setNonCreditorConfirmPopUp(false)}><Typography variant="h5">Cancel</Typography></Button> | |||||
| <Button onClick={() => setNonCreditorConfirmPopUp(false)}><Typography variant="h5">Cancel</Typography></Button> | |||||
| <Button onClick={() => markAsNonCreditor()}><Typography variant="h5">Confirm</Typography></Button> | <Button onClick={() => markAsNonCreditor()}><Typography variant="h5">Confirm</Typography></Button> | ||||
| </DialogActions> | </DialogActions> | ||||
| </Dialog> | </Dialog> | ||||
| @@ -22,6 +22,7 @@ import { | |||||
| isORGLoggedIn, | isORGLoggedIn, | ||||
| isPrimaryLoggedIn | isPrimaryLoggedIn | ||||
| } from "utils/Utils"; | } from "utils/Utils"; | ||||
| import usePageTitle from "components/usePageTitle"; | |||||
| const BackgroundHead = { | const BackgroundHead = { | ||||
| backgroundImage: `url(${titleBackgroundImg})`, | backgroundImage: `url(${titleBackgroundImg})`, | ||||
| @@ -42,6 +43,9 @@ import { | |||||
| const OrganizationDetailPage = () => { | const OrganizationDetailPage = () => { | ||||
| // Localized document title/meta for organisation details (GLD) | |||||
| usePageTitle("organizationProfile"); | |||||
| const params = useParams(); | const params = useParams(); | ||||
| const [formData, setFormData] = React.useState({}) | const [formData, setFormData] = React.useState({}) | ||||
| const [list, setList] = React.useState([]) | const [list, setList] = React.useState([]) | ||||
| @@ -136,11 +140,11 @@ const OrganizationDetailPage = () => { | |||||
| <div style={BackgroundHead}> | <div style={BackgroundHead}> | ||||
| <Stack direction="row" height='70px' justifyContent="flex-start" alignItems="center"> | <Stack direction="row" height='70px' justifyContent="flex-start" alignItems="center"> | ||||
| {isGLDLoggedIn()? | {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 | Maintain Organisation | ||||
| </Typography> | </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" /> | <FormattedMessage id="organizationProfile" /> | ||||
| </Typography> | </Typography> | ||||
| } | } | ||||
| @@ -233,7 +233,7 @@ const OrganizationCard_loadFromUser = ({ userData, userId }) => { | |||||
| <Typography variant="pnspsFormParagraphBold">{FieldUtils.notNullFieldLabel("Expiry Date:")}</Typography> | <Typography variant="pnspsFormParagraphBold">{FieldUtils.notNullFieldLabel("Expiry Date:")}</Typography> | ||||
| </Grid> | </Grid> | ||||
| <Grid item xs={12} md={6} lg={6}> | <Grid item xs={12} md={6} lg={6}> | ||||
| <LocalizationProvider dateAdapter={AdapterDayjs}> | |||||
| <LocalizationProvider dateAdapter={AdapterDayjs} localeText={DateUtils.getPickerLocaleText(intl.locale)}> | |||||
| <DemoItem components={['DatePicker']}> | <DemoItem components={['DatePicker']}> | ||||
| <DatePicker | <DatePicker | ||||
| id="brExpiryDate" | id="brExpiryDate" | ||||
| @@ -310,19 +310,17 @@ const OrganizationCard_loadFromUser = ({ userData, userId }) => { | |||||
| })} | })} | ||||
| </Grid> | </Grid> | ||||
| <Grid item xs={12} lg={4}> | |||||
| {FieldUtils.getComboField({ | |||||
| label: FieldUtils.notNullFieldLabel("Country:"), | |||||
| valueName: "country", | |||||
| dataList: ComboData.country, | |||||
| getOptionLabel: (option) => option.type ? intl.formatMessage({ id: option.type }) : "", | |||||
| <Grid item xs={12}> | |||||
| {FieldUtils.getAddressField({ | |||||
| label: FieldUtils.notNullFieldLabel("Address:"), | |||||
| valueName: ["addressLine1", "addressLine2", "addressLine3"], | |||||
| form: formik | form: formik | ||||
| })} | })} | ||||
| </Grid> | </Grid> | ||||
| <Grid item xs={12} lg={4}> | |||||
| <Grid item xs={12} lg={12}> | |||||
| {FieldUtils.getComboField({ | {FieldUtils.getComboField({ | ||||
| label: FieldUtils.notNullFieldLabel("District:"), | |||||
| label: "", | |||||
| valueName: "district", | valueName: "district", | ||||
| dataList: ComboData.district, | dataList: ComboData.district, | ||||
| getOptionLabel: (option) => option.type ? intl.formatMessage({ id: option.type }) : "", | getOptionLabel: (option) => option.type ? intl.formatMessage({ id: option.type }) : "", | ||||
| @@ -330,13 +328,17 @@ const OrganizationCard_loadFromUser = ({ userData, userId }) => { | |||||
| })} | })} | ||||
| </Grid> | </Grid> | ||||
| <Grid item xs={12}> | |||||
| {FieldUtils.getAddressField({ | |||||
| label: FieldUtils.notNullFieldLabel("Address:"), | |||||
| valueName: ["addressLine1", "addressLine2", "addressLine3"], | |||||
| <Grid item xs={12} lg={12}> | |||||
| {FieldUtils.getComboField({ | |||||
| label: "", | |||||
| valueName: "country", | |||||
| disabled: true, | |||||
| dataList: ComboData.country, | |||||
| getOptionLabel: (option) => option.type ? intl.formatMessage({ id: option.type }) : "", | |||||
| form: formik | form: formik | ||||
| })} | })} | ||||
| </Grid> | </Grid> | ||||
| </Grid> | </Grid> | ||||
| } | } | ||||
| </form> | </form> | ||||
| @@ -25,10 +25,14 @@ const BackgroundHead = { | |||||
| backgroundColor: '#0C489E', | backgroundColor: '#0C489E', | ||||
| backgroundPosition: 'right' | backgroundPosition: 'right' | ||||
| } | } | ||||
| import usePageTitle from "components/usePageTitle"; | |||||
| // ==============================|| DASHBOARD - DEFAULT ||============================== // | // ==============================|| DASHBOARD - DEFAULT ||============================== // | ||||
| const OrganizationDetailPage_FromUser = () => { | const OrganizationDetailPage_FromUser = () => { | ||||
| // Localized document title/meta for organisation details (from user) | |||||
| usePageTitle("organizationProfile"); | |||||
| const params = useParams(); | const params = useParams(); | ||||
| const [formData, setFormData] = useState({}) | const [formData, setFormData] = useState({}) | ||||
| const [isLoading, setLoding] = useState(true); | const [isLoading, setLoding] = useState(true); | ||||
| @@ -8,7 +8,7 @@ import { | |||||
| import MainCard from "components/MainCard"; | import MainCard from "components/MainCard"; | ||||
| import { useForm } from "react-hook-form"; | import { useForm } from "react-hook-form"; | ||||
| import { useState } from "react"; | |||||
| import { useState,useEffect } from "react"; | |||||
| import * as React from "react"; | import * as React from "react"; | ||||
| import * as UrlUtils from "utils/ApiPathConst"; | import * as UrlUtils from "utils/ApiPathConst"; | ||||
| @@ -19,11 +19,21 @@ import {ThemeProvider} from "@emotion/react"; | |||||
| // ==============================|| DASHBOARD - DEFAULT ||============================== // | // ==============================|| DASHBOARD - DEFAULT ||============================== // | ||||
| const OrganizationSearchForm = ({ applySearch }) => { | |||||
| const OrganizationSearchForm = ({ applySearch, onGridReady, searchCriteria }) => { | |||||
| const [type, setType] = useState([]); | const [type, setType] = useState([]); | ||||
| const [creditorSelected, setCreditorSelected] = React.useState(ComboData.CreditorStatus[0]); | const [creditorSelected, setCreditorSelected] = React.useState(ComboData.CreditorStatus[0]); | ||||
| const { reset, register, handleSubmit } = useForm() | const { reset, register, handleSubmit } = useForm() | ||||
| const [onDownload, setOnDownload] = React.useState(false); | |||||
| useEffect(() => { | |||||
| if(searchCriteria.creditor!=undefined){ | |||||
| setCreditorSelected(ComboData.CreditorStatus.find(item => item.type === searchCriteria.creditor.toString())) | |||||
| }else{ | |||||
| setCreditorSelected(ComboData.CreditorStatus[0]); | |||||
| } | |||||
| }, [searchCriteria]); | |||||
| const onSubmit = (data) => { | const onSubmit = (data) => { | ||||
| let typeArray = []; | let typeArray = []; | ||||
| @@ -48,12 +58,23 @@ const OrganizationSearchForm = ({ applySearch }) => { | |||||
| function resetForm() { | function resetForm() { | ||||
| setType([]); | setType([]); | ||||
| setCreditorSelected(ComboData.CreditorStatus[0]); | setCreditorSelected(ComboData.CreditorStatus[0]); | ||||
| reset(); | |||||
| reset({ | |||||
| brNo: "", | |||||
| enCompanyName: "", | |||||
| chCompanyName: "", | |||||
| }); | |||||
| } | } | ||||
| const doExport=()=>{ | const doExport=()=>{ | ||||
| setOnDownload(true) | |||||
| HttpUtils.fileDownload({ | HttpUtils.fileDownload({ | ||||
| url: UrlUtils.GET_ORG_EXPORT | |||||
| url: UrlUtils.GET_ORG_EXPORT, | |||||
| onResponse:()=>{ | |||||
| setOnDownload(false) | |||||
| }, | |||||
| onError:()=>{ | |||||
| setOnDownload(false) | |||||
| } | |||||
| }); | }); | ||||
| } | } | ||||
| @@ -80,6 +101,7 @@ const OrganizationSearchForm = ({ applySearch }) => { | |||||
| {...register("brNo")} | {...register("brNo")} | ||||
| id='brNo' | id='brNo' | ||||
| label="BR No." | label="BR No." | ||||
| defaultValue={searchCriteria.brNo} | |||||
| InputLabelProps={{ | InputLabelProps={{ | ||||
| shrink: true | shrink: true | ||||
| }} | }} | ||||
| @@ -92,6 +114,7 @@ const OrganizationSearchForm = ({ applySearch }) => { | |||||
| {...register("enCompanyName")} | {...register("enCompanyName")} | ||||
| id="enCompanyName" | id="enCompanyName" | ||||
| label="Name (English)" | label="Name (English)" | ||||
| defaultValue={searchCriteria.enCompanyName} | |||||
| InputLabelProps={{ | InputLabelProps={{ | ||||
| shrink: true | shrink: true | ||||
| }} | }} | ||||
| @@ -104,6 +127,7 @@ const OrganizationSearchForm = ({ applySearch }) => { | |||||
| {...register("chCompanyName")} | {...register("chCompanyName")} | ||||
| id="chCompanyName" | id="chCompanyName" | ||||
| label="Name (Chinese)" | label="Name (Chinese)" | ||||
| defaultValue={searchCriteria.chCompanyName} | |||||
| InputLabelProps={{ | InputLabelProps={{ | ||||
| shrink: true | shrink: true | ||||
| }} | }} | ||||
| @@ -125,6 +149,11 @@ const OrganizationSearchForm = ({ applySearch }) => { | |||||
| } | } | ||||
| }} | }} | ||||
| sx={{ | |||||
| '& .MuiInputBase-root': { alignItems: 'center' }, | |||||
| '& .MuiAutocomplete-endAdornment': { top: '50%', transform: 'translateY(-50%)' }, | |||||
| '& .MuiOutlinedInput-root': { height: 40 } | |||||
| }} | |||||
| getOptionLabel={(option) => option.label} | getOptionLabel={(option) => option.label} | ||||
| renderInput={(params) => ( | renderInput={(params) => ( | ||||
| <TextField | <TextField | ||||
| @@ -148,6 +177,7 @@ const OrganizationSearchForm = ({ applySearch }) => { | |||||
| <Button | <Button | ||||
| variant="contained" | variant="contained" | ||||
| onClick={doExport} | onClick={doExport} | ||||
| disabled={onDownload} | |||||
| > | > | ||||
| Export | Export | ||||
| </Button> | </Button> | ||||
| @@ -167,6 +197,7 @@ const OrganizationSearchForm = ({ applySearch }) => { | |||||
| <Button | <Button | ||||
| variant="contained" | variant="contained" | ||||
| type="submit" | type="submit" | ||||
| disabled={onGridReady} | |||||
| > | > | ||||
| Submit | Submit | ||||
| </Button> | </Button> | ||||
| @@ -11,7 +11,7 @@ import { clickableLink} from 'utils/CommonFunction'; | |||||
| import {GET_ORG_PATH} from "utils/ApiPathConst"; | import {GET_ORG_PATH} from "utils/ApiPathConst"; | ||||
| // ==============================|| EVENT TABLE ||============================== // | // ==============================|| EVENT TABLE ||============================== // | ||||
| export default function OrganizationTable({ searchCriteria }) { | |||||
| export default function OrganizationTable({ searchCriteria, applyGridOnReady, applySearch}) { | |||||
| const [_searchCriteria, set_searchCriteria] = React.useState(searchCriteria); | const [_searchCriteria, set_searchCriteria] = React.useState(searchCriteria); | ||||
| const navigate = useNavigate() | const navigate = useNavigate() | ||||
| @@ -111,12 +111,18 @@ export default function OrganizationTable({ searchCriteria }) { | |||||
| <div style={{ height: "fit-content", width: '100%' }}> | <div style={{ height: "fit-content", width: '100%' }}> | ||||
| <FiDataGrid | <FiDataGrid | ||||
| columns={columns} | columns={columns} | ||||
| customPageSize={5} | |||||
| customPageSize={10} | |||||
| onRowDoubleClick={handleRowDoubleClick} | onRowDoubleClick={handleRowDoubleClick} | ||||
| doLoad={{ | |||||
| applyGridOnReady={applyGridOnReady} | |||||
| applySearch={applySearch} | |||||
| // doLoad={{ | |||||
| // url: GET_ORG_PATH, | |||||
| // params: _searchCriteria, | |||||
| // }} | |||||
| doLoad={React.useMemo(() => ({ | |||||
| url: GET_ORG_PATH, | url: GET_ORG_PATH, | ||||
| params: _searchCriteria, | params: _searchCriteria, | ||||
| }} | |||||
| }), [_searchCriteria])} | |||||
| /> | /> | ||||
| </div> | </div> | ||||
| ); | ); | ||||
| @@ -5,7 +5,8 @@ import { | |||||
| import MainCard from "components/MainCard"; | import MainCard from "components/MainCard"; | ||||
| import { useEffect, useState } from "react"; | import { useEffect, useState } from "react"; | ||||
| import * as React from "react"; | import * as React from "react"; | ||||
| import { getSearchCriteria } from "auth/utils"; | |||||
| import usePageTitle from "components/usePageTitle"; | |||||
| // import LoadingComponent from "../extra-pages/LoadingComponent"; | // import LoadingComponent from "../extra-pages/LoadingComponent"; | ||||
| // import SearchForm from "./OrganizationSearchForm"; | // import SearchForm from "./OrganizationSearchForm"; | ||||
| @@ -29,16 +30,34 @@ const BackgroundHead = { | |||||
| // ==============================|| DASHBOARD - DEFAULT ||============================== // | // ==============================|| DASHBOARD - DEFAULT ||============================== // | ||||
| const OrganizationSearchPage = () => { | const OrganizationSearchPage = () => { | ||||
| // Localized document title/meta for organisation search | |||||
| usePageTitle("organizationProfile"); | |||||
| const [searchCriteria, setSearchCriteria] = useState({}); | const [searchCriteria, setSearchCriteria] = useState({}); | ||||
| const [onReady, setOnReady] = useState(false); | const [onReady, setOnReady] = useState(false); | ||||
| const [onGridReady, setGridOnReady] = React.useState(false); | |||||
| useEffect(() => { | |||||
| if (Object.keys(getSearchCriteria(window.location.pathname)).length>0){ | |||||
| setSearchCriteria(getSearchCriteria(window.location.pathname)) | |||||
| }else{ | |||||
| localStorage.setItem('searchCriteria',"") | |||||
| setSearchCriteria({}) | |||||
| } | |||||
| }, []); | |||||
| useEffect(() => { | useEffect(() => { | ||||
| setOnReady(true); | setOnReady(true); | ||||
| }, [searchCriteria]); | }, [searchCriteria]); | ||||
| function applySearch(input) { | function applySearch(input) { | ||||
| setGridOnReady(true) | |||||
| setSearchCriteria(input); | setSearchCriteria(input); | ||||
| localStorage.setItem('searchCriteria', JSON.stringify({path:window.location.pathname,data:input})) | |||||
| } | |||||
| function applyGridOnReady(input) { | |||||
| setGridOnReady(input); | |||||
| } | } | ||||
| return ( | return ( | ||||
| @@ -59,7 +78,11 @@ const OrganizationSearchPage = () => { | |||||
| </Grid> | </Grid> | ||||
| {/*row 1*/} | {/*row 1*/} | ||||
| <Grid item xs={12} md={12} lg={12} sx={{ mb: -1 }}> | <Grid item xs={12} md={12} lg={12} sx={{ mb: -1 }}> | ||||
| <SearchForm applySearch={applySearch} /> | |||||
| <SearchForm | |||||
| applySearch={applySearch} | |||||
| onGridReady={onGridReady} | |||||
| searchCriteria={searchCriteria} | |||||
| /> | |||||
| </Grid> | </Grid> | ||||
| {/*row 2*/} | {/*row 2*/} | ||||
| <Grid item xs={12} md={12} lg={12}> | <Grid item xs={12} md={12} lg={12}> | ||||
| @@ -69,6 +92,8 @@ const OrganizationSearchPage = () => { | |||||
| > | > | ||||
| <EventTable | <EventTable | ||||
| searchCriteria={searchCriteria} | searchCriteria={searchCriteria} | ||||
| applyGridOnReady={applyGridOnReady} | |||||
| applySearch={applySearch} | |||||
| /> | /> | ||||
| </MainCard> | </MainCard> | ||||
| </Grid> | </Grid> | ||||
| @@ -43,7 +43,7 @@ export default function SearchPublicNoticeTable({ recordList }) { | |||||
| flex: 1, | flex: 1, | ||||
| renderCell: (params) => { | renderCell: (params) => { | ||||
| let appNo = params.row.appNo; | let appNo = params.row.appNo; | ||||
| console.log(params.row) | |||||
| // console.log(params.row) | |||||
| return <div style={{ margin: 4, textAlign:"left"}}>Gazette Supplement No. 6 <br/> | return <div style={{ margin: 4, textAlign:"left"}}>Gazette Supplement No. 6 <br/> | ||||
| {isORGLoggedIn()&¶ms.row.careOf!=null&¶ms.row.careOf!=""?<>{params.row.careOf}<br /></>:null} | {isORGLoggedIn()&¶ms.row.careOf!=null&¶ms.row.careOf!=""?<>{params.row.careOf}<br /></>:null} | ||||
| App No: {appNo}<br/> | App No: {appNo}<br/> | ||||
| @@ -9,15 +9,19 @@ import { | |||||
| import * as React from "react"; | import * as React from "react"; | ||||
| import * as FormatUtils from "utils/FormatUtils"; | import * as FormatUtils from "utils/FormatUtils"; | ||||
| import * as PaymentStatus from "utils/statusUtils/PaymentStatus"; | import * as PaymentStatus from "utils/statusUtils/PaymentStatus"; | ||||
| import * as DateUtils from "utils/DateUtils"; | |||||
| import Loadable from 'components/Loadable'; | import Loadable from 'components/Loadable'; | ||||
| const MainCard = Loadable(React.lazy(() => import('components/MainCard'))); | const MainCard = Loadable(React.lazy(() => import('components/MainCard'))); | ||||
| const LoadingComponent = Loadable(React.lazy(() => import('pages/extra-pages/LoadingComponent'))); | const LoadingComponent = Loadable(React.lazy(() => import('pages/extra-pages/LoadingComponent'))); | ||||
| import DownloadIcon from '@mui/icons-material/Download'; | import DownloadIcon from '@mui/icons-material/Download'; | ||||
| import {useIntl} from "react-intl"; | |||||
| // ==============================|| DASHBOARD - DEFAULT ||============================== // | // ==============================|| DASHBOARD - DEFAULT ||============================== // | ||||
| const PaymentDetails = ({ formData,doPrint,onDownload }) => { | const PaymentDetails = ({ formData,doPrint,onDownload }) => { | ||||
| const intl = useIntl(); | |||||
| const [data, setData] = React.useState({}); | const [data, setData] = React.useState({}); | ||||
| const [onReady, setOnReady] = React.useState(false); | const [onReady, setOnReady] = React.useState(false); | ||||
| // const { locale } = intl; | |||||
| React.useEffect(() => { | React.useEffect(() => { | ||||
| if (formData != null && formData != undefined && Object.keys(formData).length > 0) { | if (formData != null && formData != undefined && Object.keys(formData).length > 0) { | ||||
| @@ -89,7 +93,7 @@ const PaymentDetails = ({ formData,doPrint,onDownload }) => { | |||||
| </Grid> | </Grid> | ||||
| <Grid item xs={6} md={5} sx={{ml:5, textAlign: "left" }}> | <Grid item xs={6} md={5} sx={{ml:5, textAlign: "left" }}> | ||||
| <FormLabel sx={{ color: "#000000" }}> | <FormLabel sx={{ color: "#000000" }}> | ||||
| {data.transDateStr + " (DD/MM/YYYY)"} | |||||
| {DateUtils.dateFormat(data.transDateStr, intl.formatMessage({id: "dateStrFormat"})) +" ("+intl.formatMessage({id: "dateStrFormat"})+")"} | |||||
| </FormLabel> | </FormLabel> | ||||
| </Grid> | </Grid> | ||||
| </Grid> | </Grid> | ||||
| @@ -131,7 +135,7 @@ const PaymentDetails = ({ formData,doPrint,onDownload }) => { | |||||
| </Grid> | </Grid> | ||||
| <Grid item xs={6} md={5} sx={{ml:5, textAlign: "left" }}> | <Grid item xs={6} md={5} sx={{ml:5, textAlign: "left" }}> | ||||
| <FormLabel sx={{ color: "#000000" }}> | <FormLabel sx={{ color: "#000000" }}> | ||||
| {"HK$ " + FormatUtils.currencyFormat(data.payload?.amount)} | |||||
| {"$ " + FormatUtils.currencyFormat(data.payload?.amount)} | |||||
| </FormLabel> | </FormLabel> | ||||
| </Grid> | </Grid> | ||||
| </Grid> | </Grid> | ||||
| @@ -161,14 +165,10 @@ const PaymentDetails = ({ formData,doPrint,onDownload }) => { | |||||
| </FormLabel> | </FormLabel> | ||||
| </Grid> | </Grid> | ||||
| <Grid xs={6} md={5} sx={{ml:5,textAlign: "left" }}> | <Grid xs={6} md={5} sx={{ml:5,textAlign: "left" }}> | ||||
| {onDownload? | |||||
| <LoadingComponent disableText={true} alignItems="flex-start"/> | |||||
| : | |||||
| <Button className="printHidden" variant="contained" sx={{ mt:2 }} onClick={doPrint}> | |||||
| <DownloadIcon/> | |||||
| <Typography sx={{fontSize: "16px"}}>Download</Typography> | |||||
| </Button> | |||||
| } | |||||
| <Button className="printHidden" variant="contained" disabled={onDownload} sx={{ mt:2 }} onClick={doPrint}> | |||||
| <DownloadIcon/> | |||||
| <Typography sx={{fontSize: "16px"}}>Download</Typography> | |||||
| </Button> | |||||
| </Grid> | </Grid> | ||||
| </Grid> | </Grid> | ||||
| </Grid> | </Grid> | ||||
| @@ -77,7 +77,7 @@ const Index = () => { | |||||
| if (!responseData.data?.id) { | if (!responseData.data?.id) { | ||||
| navigate("/paymentPage/search"); | navigate("/paymentPage/search"); | ||||
| } | } | ||||
| responseData.data["transDateStr"] = DateUtils.dateFormat(responseData.data.transDateTime, "DD/MM/YYYY"); | |||||
| responseData.data["transDateStr"] = responseData.data.transDateTime; | |||||
| responseData.data["transTimeStr"] = DateUtils.dateFormat(responseData.data.transDateTime, "HH:mm:ss"); | responseData.data["transTimeStr"] = DateUtils.dateFormat(responseData.data.transDateTime, "HH:mm:ss"); | ||||
| setItemList(responseData.paymentItemList) | setItemList(responseData.paymentItemList) | ||||
| setRecord(responseData.data); | setRecord(responseData.data); | ||||
| @@ -81,7 +81,7 @@ export default function SearchPublicNoticeTable({ recordList }) { | |||||
| { | { | ||||
| id: 'fee', | id: 'fee', | ||||
| field: 'fee', | field: 'fee', | ||||
| headerName: intl.formatMessage({id: 'currencyAmount'}) + ' ($)', | |||||
| headerName: intl.formatMessage({id: 'currencyAmount'}), | |||||
| width: 200, | width: 200, | ||||
| valueGetter: (params) => { | valueGetter: (params) => { | ||||
| return (params?.value) ? "$ " + FormatUtils.currencyFormat(params?.value) : ""; | return (params?.value) ? "$ " + FormatUtils.currencyFormat(params?.value) : ""; | ||||
| @@ -9,6 +9,7 @@ import { | |||||
| import * as React from "react"; | import * as React from "react"; | ||||
| import * as FormatUtils from "utils/FormatUtils"; | import * as FormatUtils from "utils/FormatUtils"; | ||||
| import * as PaymentStatus from "utils/statusUtils/PaymentStatus"; | import * as PaymentStatus from "utils/statusUtils/PaymentStatus"; | ||||
| import * as DateUtils from "utils/DateUtils"; | |||||
| import Loadable from 'components/Loadable'; | import Loadable from 'components/Loadable'; | ||||
| const MainCard = Loadable(React.lazy(() => import('components/MainCard'))); | const MainCard = Loadable(React.lazy(() => import('components/MainCard'))); | ||||
| const LoadingComponent = Loadable(React.lazy(() => import('pages/extra-pages/LoadingComponent'))); | const LoadingComponent = Loadable(React.lazy(() => import('pages/extra-pages/LoadingComponent'))); | ||||
| @@ -24,30 +25,56 @@ const PaymentDetails = ({ formData,doPrint,onDownload }) => { | |||||
| React.useEffect(() => { | React.useEffect(() => { | ||||
| if (formData != null && formData != undefined && Object.keys(formData).length > 0) { | if (formData != null && formData != undefined && Object.keys(formData).length > 0) { | ||||
| console.log(formData) | |||||
| // console.log(formData) | |||||
| setData(formData); | setData(formData); | ||||
| } | } | ||||
| }, [formData]); | }, [formData]); | ||||
| React.useEffect(() => { | React.useEffect(() => { | ||||
| if (data != null && data != undefined && Object.keys(data).length > 0) { | if (data != null && data != undefined && Object.keys(data).length > 0) { | ||||
| console.log(data) | |||||
| // console.log(data) | |||||
| setOnReady(data != {}); | setOnReady(data != {}); | ||||
| } | } | ||||
| }, [data]); | }, [data]); | ||||
| const getPaymentMethod=()=>{ | const getPaymentMethod=()=>{ | ||||
| let paymentmethod = "" | let paymentmethod = "" | ||||
| // console.log(locale) | |||||
| if (data?.payload!=null) { | if (data?.payload!=null) { | ||||
| paymentmethod = data.payload?.paymentdetail.paymentmethod; | paymentmethod = data.payload?.paymentdetail.paymentmethod; | ||||
| if("01" == paymentmethod) return "PPS"; | |||||
| if("02" == paymentmethod || "03" == paymentmethod) return "Credit Card"; | |||||
| if("04" == paymentmethod) return "FPS"; | |||||
| if (locale == "zh-HK"){ | |||||
| if("01" == paymentmethod) return "繳費靈"; | |||||
| if("02" == paymentmethod || "03" == paymentmethod) return "信用卡"; | |||||
| if("04" == paymentmethod) return "轉數快"; | |||||
| } | |||||
| else if (locale == "zh-CN"){ | |||||
| if("01" == paymentmethod) return "缴费灵"; | |||||
| if("02" == paymentmethod || "03" == paymentmethod) return "信用卡"; | |||||
| if("04" == paymentmethod) return "转数快"; | |||||
| } | |||||
| else { | |||||
| if("01" == paymentmethod) return "PPS"; | |||||
| if("02" == paymentmethod || "03" == paymentmethod) return "Credit Card"; | |||||
| if("04" == paymentmethod) return "FPS"; | |||||
| } | |||||
| } else { | } else { | ||||
| paymentmethod = data.payMethod; | paymentmethod = data.payMethod; | ||||
| if("01,PPSB,PPS" == paymentmethod) return "PPS"; | |||||
| if("02,BCMP,CreditCard" == paymentmethod || "03,BCMP,CreditCard" == paymentmethod) return "Credit Card"; | |||||
| if("04,BCFP,FPS" == paymentmethod) return "FPS"; | |||||
| if (locale == "zh-HK"){ | |||||
| if("01,PPSB,PPS" == paymentmethod) return "繳費靈"; | |||||
| if("02,BCMP,CreditCard" == paymentmethod || "03,BCMP,CreditCard" == paymentmethod) return "信用卡"; | |||||
| if("04,BCFP,FPS" == paymentmethod) return "轉數快"; | |||||
| } | |||||
| else if (locale == "zh-CN"){ | |||||
| if("01,PPSB,PPS" == paymentmethod) return "缴费灵"; | |||||
| if("02,BCMP,CreditCard" == paymentmethod || "03,BCMP,CreditCard" == paymentmethod) return "信用卡"; | |||||
| if("04,BCFP,FPS" == paymentmethod) return "转数快"; | |||||
| } | |||||
| else { | |||||
| if("01,PPSB,PPS" == paymentmethod) return "PPS"; | |||||
| if("02,BCMP,CreditCard" == paymentmethod || "03,BCMP,CreditCard" == paymentmethod) return "Credit Card"; | |||||
| if("04,BCFP,FPS" == paymentmethod) return "FPS"; | |||||
| } | |||||
| } | } | ||||
| return paymentmethod; | return paymentmethod; | ||||
| } | } | ||||
| @@ -109,7 +136,7 @@ const PaymentDetails = ({ formData,doPrint,onDownload }) => { | |||||
| </Grid> | </Grid> | ||||
| <Grid item xs={6} md={6} sx={{textAlign: "left" }}> | <Grid item xs={6} md={6} sx={{textAlign: "left" }}> | ||||
| <FormLabel sx={{ fontSize: "16px", color: "#000000" }}> | <FormLabel sx={{ fontSize: "16px", color: "#000000" }}> | ||||
| {data.transDateStr + " (DD/MM/YYYY)"} | |||||
| {DateUtils.dateFormat(data.transDateStr, intl.formatMessage({id: "dateStrFormat"})) +" ("+intl.formatMessage({id: "dateStrFormat"})+")"} | |||||
| </FormLabel> | </FormLabel> | ||||
| </Grid> | </Grid> | ||||
| </Grid> | </Grid> | ||||
| @@ -151,7 +178,7 @@ const PaymentDetails = ({ formData,doPrint,onDownload }) => { | |||||
| </Grid> | </Grid> | ||||
| <Grid item xs={6} md={6} sx={{textAlign: "left" }}> | <Grid item xs={6} md={6} sx={{textAlign: "left" }}> | ||||
| <FormLabel sx={{ fontSize: "16px", color: "#000000" }}> | <FormLabel sx={{ fontSize: "16px", color: "#000000" }}> | ||||
| {"HK$ " + FormatUtils.currencyFormat(data.payload?.amount?data.payload?.amount:data.payAmount)} | |||||
| {"$ " + FormatUtils.currencyFormat(data.payload?.amount?data.payload?.amount:data.payAmount)} | |||||
| </FormLabel> | </FormLabel> | ||||
| </Grid> | </Grid> | ||||
| </Grid> | </Grid> | ||||
| @@ -181,16 +208,12 @@ const PaymentDetails = ({ formData,doPrint,onDownload }) => { | |||||
| </FormLabel> | </FormLabel> | ||||
| </Grid> | </Grid> | ||||
| <Grid item xs={6} md={5} sx={{textAlign: "left" }}> | <Grid item xs={6} md={5} sx={{textAlign: "left" }}> | ||||
| {onDownload? | |||||
| <LoadingComponent disableText={true} alignItems="flex-start"/> | |||||
| : | |||||
| <Button className="printHidden" variant="contained" sx={{ mt:2 }} onClick={doPrint}> | |||||
| <DownloadIcon/> | |||||
| <Typography sx={{fontSize: "16px"}}> | |||||
| <FormattedMessage id="download"/> | |||||
| </Typography> | |||||
| </Button> | |||||
| } | |||||
| <Button className="printHidden" variant="contained" disabled={onDownload} sx={{ mt:2 }} onClick={doPrint}> | |||||
| <DownloadIcon/> | |||||
| <Typography sx={{fontSize: "16px"}}> | |||||
| <FormattedMessage id="download"/> | |||||
| </Typography> | |||||
| </Button> | |||||
| </Grid> | </Grid> | ||||
| </Grid> | </Grid> | ||||
| </Grid> | </Grid> | ||||
| @@ -20,6 +20,7 @@ const DataGrid = Loadable(React.lazy(() => import('./DataGrid'))); | |||||
| import ForwardIcon from '@mui/icons-material/Forward'; | import ForwardIcon from '@mui/icons-material/Forward'; | ||||
| import titleBackgroundImg from 'assets/images/dashboard/gazette-bar.png' | import titleBackgroundImg from 'assets/images/dashboard/gazette-bar.png' | ||||
| import {FormattedMessage,useIntl} from "react-intl"; | import {FormattedMessage,useIntl} from "react-intl"; | ||||
| import usePageTitle from "components/usePageTitle"; | |||||
| const BackgroundHead = { | const BackgroundHead = { | ||||
| backgroundImage: `url(${titleBackgroundImg})`, | backgroundImage: `url(${titleBackgroundImg})`, | ||||
| width: '100%', | width: '100%', | ||||
| @@ -33,6 +34,8 @@ const BackgroundHead = { | |||||
| // ==============================|| DASHBOARD - DEFAULT ||============================== // | // ==============================|| DASHBOARD - DEFAULT ||============================== // | ||||
| const Index = () => { | const Index = () => { | ||||
| usePageTitle("payDetail"); | |||||
| const params = useParams(); | const params = useParams(); | ||||
| const navigate = useNavigate() | const navigate = useNavigate() | ||||
| const intl = useIntl(); | const intl = useIntl(); | ||||
| @@ -59,9 +62,9 @@ const Index = () => { | |||||
| React.useEffect(() => { | React.useEffect(() => { | ||||
| if (Object.keys(transactionData).length > 0) { | if (Object.keys(transactionData).length > 0) { | ||||
| console.log(transactionData) | |||||
| console.log(itemList) | |||||
| console.log(record) | |||||
| // console.log(transactionData) | |||||
| // console.log(itemList) | |||||
| // console.log(record) | |||||
| setOnReady(true); | setOnReady(true); | ||||
| } | } | ||||
| }, [transactionData]); | }, [transactionData]); | ||||
| @@ -102,21 +105,21 @@ const Index = () => { | |||||
| "paymentId": params.id | "paymentId": params.id | ||||
| }, | }, | ||||
| onSuccess: function(responseData2){ | onSuccess: function(responseData2){ | ||||
| responseData2.data["transDateStr"] = DateUtils.dateFormat(responseData2.data.transDateTime, "DD/MM/YYYY"); | |||||
| responseData2.data["transDateStr"] = responseData2.data.transDateTime; | |||||
| responseData2.data["transTimeStr"] = DateUtils.dateFormat(responseData2.data.transDateTime, "HH:mm:ss"); | responseData2.data["transTimeStr"] = DateUtils.dateFormat(responseData2.data.transDateTime, "HH:mm:ss"); | ||||
| setResponeData(responseData2.transactionData) | setResponeData(responseData2.transactionData) | ||||
| setItemList(responseData2.paymentItemList) | setItemList(responseData2.paymentItemList) | ||||
| setRecord(responseData2.data); | setRecord(responseData2.data); | ||||
| }, | }, | ||||
| onError: function(){ | onError: function(){ | ||||
| responseData.data["transDateStr"] = DateUtils.dateFormat(responseData.data.transDateTime, "DD/MM/YYYY"); | |||||
| responseData.data["transDateStr"] = responseData.data.transDateTime; | |||||
| responseData.data["transTimeStr"] = DateUtils.dateFormat(responseData.data.transDateTime, "HH:mm:ss"); | responseData.data["transTimeStr"] = DateUtils.dateFormat(responseData.data.transDateTime, "HH:mm:ss"); | ||||
| setResponeData(responseData) | setResponeData(responseData) | ||||
| } | } | ||||
| }); | }); | ||||
| }else{ | }else{ | ||||
| responseData.data["transDateStr"] = DateUtils.dateFormat(responseData.data.transDateTime, "DD/MM/YYYY"); | |||||
| responseData.data["transDateStr"] = responseData.data.transDateTime; | |||||
| responseData.data["transTimeStr"] = DateUtils.dateFormat(responseData.data.transDateTime, "HH:mm:ss"); | responseData.data["transTimeStr"] = DateUtils.dateFormat(responseData.data.transDateTime, "HH:mm:ss"); | ||||
| setResponeData(responseData) | setResponeData(responseData) | ||||
| setItemList(responseData.paymentItemList) | setItemList(responseData.paymentItemList) | ||||
| @@ -144,7 +147,7 @@ const Index = () => { | |||||
| <Grid className="printHidden" item xs={12} width="100%"> | <Grid className="printHidden" item xs={12} width="100%"> | ||||
| <div style={BackgroundHead} width="100%"> | <div style={BackgroundHead} width="100%"> | ||||
| <Stack direction="row" height='70px'> | <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"/> | <FormattedMessage id="payDetail"/> | ||||
| </Typography> | </Typography> | ||||
| </Stack> | </Stack> | ||||
| @@ -104,7 +104,7 @@ const AckPage = () => { | |||||
| onSuccess: function(responseData){ | onSuccess: function(responseData){ | ||||
| localStorage.removeItem("webtoken"); | localStorage.removeItem("webtoken"); | ||||
| localStorage.removeItem("transactionid"); | localStorage.removeItem("transactionid"); | ||||
| responseData.data["transDateStr"] = DateUtils.dateFormat(responseData.data.transDateTime, "DD/MM/YYYY"); | |||||
| responseData.data["transDateStr"] = responseData.data.transDateTime; | |||||
| responseData.data["transTimeStr"] = DateUtils.dateFormat(responseData.data.transDateTime, "HH:mm:ss"); | responseData.data["transTimeStr"] = DateUtils.dateFormat(responseData.data.transDateTime, "HH:mm:ss"); | ||||
| setResponeDataData(responseData.transactionData) | setResponeDataData(responseData.transactionData) | ||||
| setItemList(responseData.paymentItemList) | setItemList(responseData.paymentItemList) | ||||
| @@ -202,7 +202,8 @@ const AckPage = () => { | |||||
| {/*row 1*/} | {/*row 1*/} | ||||
| <Grid item xs={12} md={12} spacing={2} sx={{ textAlign: "center" }}> | <Grid item xs={12} md={12} spacing={2} sx={{ textAlign: "center" }}> | ||||
| <Typography variant="h3" sx={{ ml: 8, mt: 4, mr: 8, textAlign: "center" }}> | <Typography variant="h3" sx={{ ml: 8, mt: 4, mr: 8, textAlign: "center" }}> | ||||
| 您的申請和付款已收到 | |||||
| {/* 您的申請和付款已收到 */} | |||||
| <FormattedMessage id="MSG.paymentMsg"/> | |||||
| </Typography> | </Typography> | ||||
| <Grid container justifyContent="center" direction="column" spacing={2} sx={{ p: 2 }} alignitems="stretch" > | <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 }}> | <Grid item className="printOrder" xs={12} md={12} sx={{ pt: 2 }} style={{ height: '100%', order: 1 }}> | ||||
| @@ -253,21 +254,22 @@ const AckPage = () => { | |||||
| <center> | <center> | ||||
| <Grid item xs={12} md={8} > | <Grid item xs={12} md={8} > | ||||
| <Typography variant="h5" sx={{ ml: 8, mt: 4, mr: 8, textAlign: "left" }}> | <Typography variant="h5" sx={{ ml: 8, mt: 4, mr: 8, textAlign: "left" }}> | ||||
| 付款取消訊息: | |||||
| {/* 付款取消訊息: */} | |||||
| <FormattedMessage id="MSG.paymentCancelMsg1"/> | |||||
| <br /><br /> | <br /><br /> | ||||
| 您的付款已被取消。我們收到了您的付款請求,但由於某些原因,付款無法完成。請注意以下事項: | |||||
| <FormattedMessage id="MSG.paymentCancelMsg2"/> | |||||
| <br /><br /> | <br /><br /> | ||||
| 如果您主動取消了支付,請確認並確保取消是您的意願。 | |||||
| <FormattedMessage id="MSG.paymentCancelMsg3"/> | |||||
| <br /> | <br /> | ||||
| 如果付款被取消是由於系統問題或其他原因,請您嘗試以下解決方法: | |||||
| <FormattedMessage id="MSG.paymentCancelMsg4"/> | |||||
| <br /><br /> | <br /><br /> | ||||
| 檢查您的支付帳戶是否有任何異常或限制。 | |||||
| <FormattedMessage id="MSG.paymentCancelMsg5"/> | |||||
| <br /> | <br /> | ||||
| 確保您的付款資訊準確無誤。 | |||||
| <FormattedMessage id="MSG.paymentCancelMsg6"/> | |||||
| <br /> | <br /> | ||||
| 檢查您的網路連線是否正常。 | |||||
| <FormattedMessage id="MSG.paymentCancelMsg7"/> | |||||
| <br /><br /> | <br /><br /> | ||||
| 如果您需要進一步的協助或有任何疑問,請隨時與我們聯繫,我們將盡快解決您的付款問題。謝謝! | |||||
| <FormattedMessage id="MSG.paymentCancelMsg8"/> | |||||
| </Typography> | </Typography> | ||||
| </Grid> | </Grid> | ||||
| </center> | </center> | ||||
| @@ -296,29 +298,39 @@ const AckPage = () => { | |||||
| <Grid container justifyContent="flex-start" alignItems="center" > | <Grid container justifyContent="flex-start" alignItems="center" > | ||||
| <center> | <center> | ||||
| <Grid item xs={12} md={8} > | <Grid item xs={12} md={8} > | ||||
| <Typography variant="h5" sx={{ ml: 8, mt: 4, mr: 8, textAlign: "left" }}> | <Typography variant="h5" sx={{ ml: 8, mt: 4, mr: 8, textAlign: "left" }}> | ||||
| 付款失敗訊息: | |||||
| <FormattedMessage id="MSG.paymentFailMsg1"/> | |||||
| <br /><br /> | <br /><br /> | ||||
| 親愛的用戶,很遺憾地告訴您,您的付款操作未成功。我們在處理您的付款時遇到了問題。請您仔細檢查以下事項: | |||||
| <FormattedMessage id="MSG.paymentFailMsg2"/> | |||||
| <br /><br /> | <br /><br /> | ||||
| 您的支付帳戶餘額是否足夠。 | |||||
| <br /> | |||||
| 您提供的付款資訊是否準確無誤。 | |||||
| <br /> | |||||
| 請檢查您的網路連線是否正常。 | |||||
| <ul> | |||||
| <li> | |||||
| <FormattedMessage id="MSG.paymentFailMsg3"/> | |||||
| </li> | |||||
| <li> | |||||
| <FormattedMessage id="MSG.paymentFailMsg4"/> | |||||
| </li> | |||||
| <li> | |||||
| <FormattedMessage id="MSG.paymentFailMsg5"/> | |||||
| </li> | |||||
| </ul> | |||||
| <br /><br /> | <br /><br /> | ||||
| 如果您已確認以上問題無誤,但付款失敗,請您嘗試以下解決方法: | |||||
| <FormattedMessage id="MSG.paymentFailMsg6"/> | |||||
| <br /><br /> | <br /><br /> | ||||
| 嘗試使用其他付款方式進行付款。 | |||||
| <br /> | |||||
| 檢查您的支付帳戶是否有異常或限制。 | |||||
| <br /> | |||||
| 聯絡我們的客服人員尋求協助。 | |||||
| <ul> | |||||
| <li> | |||||
| <FormattedMessage id="MSG.paymentFailMsg7"/> | |||||
| </li> | |||||
| <li> | |||||
| <FormattedMessage id="MSG.paymentFailMsg8"/> | |||||
| </li> | |||||
| <li> | |||||
| <FormattedMessage id="MSG.paymentFailMsg9"/> | |||||
| </li> | |||||
| </ul> | |||||
| <br /><br /> | <br /><br /> | ||||
| 如果您需要進一步的協助或有任何疑問,請隨時與我們聯繫。非常抱歉給您帶來不便,我們將盡快解決您的付款問題。謝謝! | |||||
| <FormattedMessage id="MSG.paymentFailMsg10"/> | |||||
| </Typography> | </Typography> | ||||
| </Grid> | </Grid> | ||||
| </center> | </center> | ||||
| </Grid> | </Grid> | ||||
| @@ -10,6 +10,9 @@ import * as HttpUtils from "utils/HttpUtils"; | |||||
| import * as UrlUtils from "utils/ApiPathConst"; | import * as UrlUtils from "utils/ApiPathConst"; | ||||
| import { useNavigate } from "react-router-dom"; | import { useNavigate } from "react-router-dom"; | ||||
| import FpsIcon from "assets/images/icons/fps.svg"; | import FpsIcon from "assets/images/icons/fps.svg"; | ||||
| import expiredQrcodeEN from "assets/images/icons/expiredQrcodeEN.png"; | |||||
| import expiredQrcodeZH from "assets/images/icons/expiredQrcodeZH.png"; | |||||
| import expiredQrcodeCN from "assets/images/icons/expiredQrcodeCN.png"; | |||||
| import { useLocation } from 'react-router-dom'; | import { useLocation } from 'react-router-dom'; | ||||
| // import {paymentPath} from "auth/utils"; | // import {paymentPath} from "auth/utils"; | ||||
| import {currencyFormat} from "utils/FormatUtils"; | import {currencyFormat} from "utils/FormatUtils"; | ||||
| @@ -19,7 +22,7 @@ import Loadable from 'components/Loadable'; | |||||
| const LoadingComponent = Loadable(React.lazy(() => import('pages/extra-pages/LoadingComponent'))); | const LoadingComponent = Loadable(React.lazy(() => import('pages/extra-pages/LoadingComponent'))); | ||||
| import titleBackgroundImg from 'assets/images/dashboard/gazette-bar.png' | import titleBackgroundImg from 'assets/images/dashboard/gazette-bar.png' | ||||
| import {FormattedMessage} from "react-intl"; | |||||
| import {FormattedMessage, useIntl} from "react-intl"; | |||||
| const BackgroundHead = { | const BackgroundHead = { | ||||
| backgroundImage: `url(${titleBackgroundImg})`, | backgroundImage: `url(${titleBackgroundImg})`, | ||||
| width: '100%', | width: '100%', | ||||
| @@ -30,15 +33,21 @@ const BackgroundHead = { | |||||
| backgroundPosition: 'right' | backgroundPosition: 'right' | ||||
| } | } | ||||
| // ==============================|| DASHBOARD - DEFAULT ||============================== // | // ==============================|| DASHBOARD - DEFAULT ||============================== // | ||||
| const Index = () => { | const Index = () => { | ||||
| const navigate = useNavigate() | const navigate = useNavigate() | ||||
| const location = useLocation(); | const location = useLocation(); | ||||
| const intl = useIntl(); | |||||
| const { locale } = intl; | |||||
| const [locationData, setLocationData] = React.useState({}); | const [locationData, setLocationData] = React.useState({}); | ||||
| const [paymentData, setPaymentData] = React.useState({}); | const [paymentData, setPaymentData] = React.useState({}); | ||||
| const [onReady, setOnReady] = React.useState(false); | const [onReady, setOnReady] = React.useState(false); | ||||
| const [qrCodeTimeout, setqrCodeTimeout] = React.useState(false); | |||||
| const [paymentStatusCode, setPaymentStatusCode] = React.useState(""); | |||||
| const [expiredQrcode, setExpiredQrcode] = React.useState(expiredQrcodeEN); | |||||
| const [responeData, setResponeDataData] = React.useState({}); | const [responeData, setResponeDataData] = React.useState({}); | ||||
| const [fpsTransctionData, setFpsTransctionData] = React.useState({}); | const [fpsTransctionData, setFpsTransctionData] = React.useState({}); | ||||
| @@ -49,6 +58,7 @@ const Index = () => { | |||||
| const [fpsqrcodeurlPrd, setFpsqrcodeurlPrd] = React.useState(""); | const [fpsqrcodeurlPrd, setFpsqrcodeurlPrd] = React.useState(""); | ||||
| const [fpsqrcodeurlFps, setFpsqrcodeurlFps] = React.useState(""); | const [fpsqrcodeurlFps, setFpsqrcodeurlFps] = React.useState(""); | ||||
| const [browserType, setBrowserType] = React.useState(""); | const [browserType, setBrowserType] = React.useState(""); | ||||
| const [sysEnv, setSysEnv] = React.useState(""); | |||||
| const mobileBrowser = "Mobile"; | const mobileBrowser = "Mobile"; | ||||
| const desktopBrowser = "Desktop"; | const desktopBrowser = "Desktop"; | ||||
| @@ -69,12 +79,14 @@ const Index = () => { | |||||
| if(Object.keys(location.state).length > 0){ | if(Object.keys(location.state).length > 0){ | ||||
| // console.log (location.state) | // console.log (location.state) | ||||
| setLocationData(location.state) | setLocationData(location.state) | ||||
| setBrowserType(desktopBrowser) | |||||
| if (/Android|webOS|iPhone|iPad|iPod|Opera Mini/i.test(navigator.userAgent)) { | if (/Android|webOS|iPhone|iPad|iPod|Opera Mini/i.test(navigator.userAgent)) { | ||||
| console.log('Mobile web browser'); | |||||
| // console.log('Mobile web browser'); | |||||
| setBrowserType(mobileBrowser) | setBrowserType(mobileBrowser) | ||||
| // setFpsqrcodeurl(openPASGUrl) | // setFpsqrcodeurl(openPASGUrl) | ||||
| } else { | } else { | ||||
| console.log('Desktop web browser'); | |||||
| // console.log('Desktop web browser'); | |||||
| setBrowserType(desktopBrowser) | setBrowserType(desktopBrowser) | ||||
| } | } | ||||
| } | } | ||||
| @@ -88,6 +100,17 @@ const Index = () => { | |||||
| } | } | ||||
| }, [locationData]); | }, [locationData]); | ||||
| React.useEffect(() => { | |||||
| // console.log (locationData) | |||||
| if (locale === 'zh-HK'){ | |||||
| setExpiredQrcode(expiredQrcodeZH) | |||||
| } else if (locale === 'en'){ | |||||
| setExpiredQrcode(expiredQrcodeEN) | |||||
| } else { | |||||
| setExpiredQrcode(expiredQrcodeCN) | |||||
| } | |||||
| }, [locale]); | |||||
| React.useEffect(() => { | React.useEffect(() => { | ||||
| // console.log (paymentData) | // console.log (paymentData) | ||||
| if (Object.keys(paymentData).length > 0){ | if (Object.keys(paymentData).length > 0){ | ||||
| @@ -159,6 +182,7 @@ const Index = () => { | |||||
| */ | */ | ||||
| setResponeDataData(responseData) | setResponeDataData(responseData) | ||||
| const timeoutdatetime = responseData.fpsmerchanttimeoutdatetime | const timeoutdatetime = responseData.fpsmerchanttimeoutdatetime | ||||
| setSysEnv(responseData.sysEnv) | |||||
| const searchString = "[UTC]"; | const searchString = "[UTC]"; | ||||
| let convertedDateString = ""; | let convertedDateString = ""; | ||||
| if ( timeoutdatetime.toString().includes(searchString) ){ | if ( timeoutdatetime.toString().includes(searchString) ){ | ||||
| @@ -175,12 +199,18 @@ const Index = () => { | |||||
| console.log(fpsqrcodeurl) | console.log(fpsqrcodeurl) | ||||
| console.log(fpsqrcodeurlwithFps) | console.log(fpsqrcodeurlwithFps) | ||||
| const openPASGUrl = pasgPath + '?pay_req_obj=' + encodeURIComponent(fpsqrcodeurl) + '&callback=' | |||||
| + encodeURIComponent("https://"+window.location.hostname+ '/paymentPage/fps/fpscallback?TRANSACTION_ID='+transactionid+"&WEB_TOKEN="+webtoken+"&PAYMENT_ID="+localStorage.getItem("paymentId")); | |||||
| const openPASGUrlPrd = pasgPathPrd + '?pay_req_obj=' + encodeURIComponent(fpsqrcodeurl) + '&callback=' | |||||
| + encodeURIComponent("https://"+window.location.hostname+ '/paymentPage/fps/fpscallback?TRANSACTION_ID='+transactionid+"&WEB_TOKEN="+webtoken+"&PAYMENT_ID="+localStorage.getItem("paymentId")); | |||||
| const openPASGUrl = pasgPath + '?pay_req_obj=' + encodeURIComponent(fpsqrcodeurlwithFps) + '&callback=' | |||||
| + encodeURIComponent("https://"+window.location.hostname+ '/paymentPage/fps/fpscallback?&PAYMENT_ID='+localStorage.getItem("paymentId")); | |||||
| const openPASGUrlPrd = pasgPathPrd + '?pay_req_obj=' + encodeURIComponent(fpsqrcodeurlwithFps) + '&callback=' | |||||
| + encodeURIComponent("https://"+window.location.hostname+ '/paymentPage/fps/fpscallback?&PAYMENT_ID='+localStorage.getItem("paymentId")); | |||||
| const openPASGUrlPrdFps = pasgPath + '?pay_req_obj=' + encodeURIComponent(fpsqrcodeurlwithFps) + '&callback=' | const openPASGUrlPrdFps = pasgPath + '?pay_req_obj=' + encodeURIComponent(fpsqrcodeurlwithFps) + '&callback=' | ||||
| + encodeURIComponent("https://"+window.location.hostname+ '/paymentPage/fps/fpscallback?TRANSACTION_ID='+transactionid+"&WEB_TOKEN="+webtoken+"&PAYMENT_ID="+localStorage.getItem("paymentId")); | |||||
| + encodeURIComponent("https://"+window.location.hostname+ '/paymentPage/fps/fpscallback?&PAYMENT_ID='+localStorage.getItem("paymentId")); | |||||
| // const openPASGUrl = pasgPath + '?pay_req_obj=' + encodeURIComponent(fpsqrcodeurlwithFps) + '&callback=' | |||||
| // + encodeURIComponent("https://"+window.location.hostname+ '/paymentPage/fps/fpscallback?TRANSACTION_ID='+transactionid+"&WEB_TOKEN="+webtoken+"&PAYMENT_ID="+localStorage.getItem("paymentId")); | |||||
| // const openPASGUrlPrd = pasgPathPrd + '?pay_req_obj=' + encodeURIComponent(fpsqrcodeurlwithFps) + '&callback=' | |||||
| // + encodeURIComponent("https://"+window.location.hostname+ '/paymentPage/fps/fpscallback?TRANSACTION_ID='+transactionid+"&WEB_TOKEN="+webtoken+"&PAYMENT_ID="+localStorage.getItem("paymentId")); | |||||
| // const openPASGUrlPrdFps = pasgPath + '?pay_req_obj=' + encodeURIComponent(fpsqrcodeurlwithFps) + '&callback=' | |||||
| // + encodeURIComponent("https://"+window.location.hostname+ '/paymentPage/fps/fpscallback?TRANSACTION_ID='+transactionid+"&WEB_TOKEN="+webtoken+"&PAYMENT_ID="+localStorage.getItem("paymentId")); | |||||
| setFpsqrcodeurl(openPASGUrl) | setFpsqrcodeurl(openPASGUrl) | ||||
| setFpsqrcodeurlPrd(openPASGUrlPrd) | setFpsqrcodeurlPrd(openPASGUrlPrd) | ||||
| setFpsqrcodeurlFps(openPASGUrlPrdFps) | setFpsqrcodeurlFps(openPASGUrlPrdFps) | ||||
| @@ -188,7 +218,7 @@ const Index = () => { | |||||
| }); | }); | ||||
| //testing | //testing | ||||
| // const timeoutdatetime = "2024-05-06T11:10:30Z[UTC]" | |||||
| // const timeoutdatetime = "2024-11-18T07:04:35Z[UTC]" | |||||
| // const convertedDateString = timeoutdatetime.replace("[UTC]", ""); | // const convertedDateString = timeoutdatetime.replace("[UTC]", ""); | ||||
| // setFpsmerchanttimeoutdatetime(convertedDateString) | // setFpsmerchanttimeoutdatetime(convertedDateString) | ||||
| // setPaymentId("C202310268000681") | // setPaymentId("C202310268000681") | ||||
| @@ -196,7 +226,7 @@ const Index = () => { | |||||
| // { | // { | ||||
| // "paymentid": "C202310268000681", | // "paymentid": "C202310268000681", | ||||
| // "paymentstatus": "INPR", | // "paymentstatus": "INPR", | ||||
| // "fpsmerchanttimeoutdatetime": "2024-05-06T11:10:30Z[UTC]", | |||||
| // "fpsmerchanttimeoutdatetime": "2024-11-18T07:04:35Z[UTC]", | |||||
| // "fpsqrcodeimgbase64": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASwAAAEsCAYAAAB5fY51AAAuyklEQVR4Xu3dfcy/d1nff2IAAAAASUVORK5CYII=", | // "fpsqrcodeimgbase64": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASwAAAEsCAYAAAB5fY51AAAuyklEQVR4Xu3dfcy/d1nff2IAAAAASUVORK5CYII=", | ||||
| // "fpsqrcodeurl": "http://127.0.0.1:8080/api/payment/wallet/fps/enquiryfpspayload/vm.JKDDlTOavR3ASviSwUnS1Lw4-" | // "fpsqrcodeurl": "http://127.0.0.1:8080/api/payment/wallet/fps/enquiryfpspayload/vm.JKDDlTOavR3ASviSwUnS1Lw4-" | ||||
| // } | // } | ||||
| @@ -231,6 +261,7 @@ const Index = () => { | |||||
| }, | }, | ||||
| onSuccess: function(responseData){ | onSuccess: function(responseData){ | ||||
| const paymentstatuscode = responseData.paymentdetail.result.paymentstatuscode; | const paymentstatuscode = responseData.paymentdetail.result.paymentstatuscode; | ||||
| setPaymentStatusCode(paymentstatuscode) | |||||
| if (paymentstatuscode != "" && paymentstatuscode != "INPR" ){ | if (paymentstatuscode != "" && paymentstatuscode != "INPR" ){ | ||||
| if (paymentstatuscode === 'APPR') { | if (paymentstatuscode === 'APPR') { | ||||
| // const timestamp = Date.now(); | // const timestamp = Date.now(); | ||||
| @@ -249,7 +280,8 @@ const Index = () => { | |||||
| } | } | ||||
| }, | }, | ||||
| onError: function(){ | onError: function(){ | ||||
| cancelPayment() | |||||
| alert("ERROR") | |||||
| // cancelPayment() | |||||
| // clearInterval(currentTimer.current); | // clearInterval(currentTimer.current); | ||||
| } | } | ||||
| }); | }); | ||||
| @@ -260,35 +292,43 @@ const Index = () => { | |||||
| const timeOutDate = new Date(fpsmerchanttimeoutdatetime); | const timeOutDate = new Date(fpsmerchanttimeoutdatetime); | ||||
| const currentTime = new Date; | const currentTime = new Date; | ||||
| const timedowncount = Math.round((timeOutDate.getTime() - currentTime.getTime()) / 1000); | const timedowncount = Math.round((timeOutDate.getTime() - currentTime.getTime()) / 1000); | ||||
| setTimeDownCount(timedowncount); | |||||
| // console.log(time) | // console.log(time) | ||||
| // console.log(timeOutDate) | // console.log(timeOutDate) | ||||
| // console.log(currentTime) | // console.log(currentTime) | ||||
| // console.log(timeOutDate.getTime()-currentTime.getTime()) | // console.log(timeOutDate.getTime()-currentTime.getTime()) | ||||
| getPaymentStatus(); | |||||
| if (timeOutDate.getTime()<currentTime.getTime()){ | |||||
| // console.log("stop"); | |||||
| clearInterval(currentTimer.current); | |||||
| cancelPayment() | |||||
| if (browserType === desktopBrowser){ | |||||
| getPaymentStatus(); | |||||
| if (timeOutDate.getTime()<currentTime.getTime()){ | |||||
| // console.log("stop"); | |||||
| clearInterval(currentTimer.current); | |||||
| setqrCodeTimeout(true) | |||||
| setTimeDownCount(0); | |||||
| // cancelPayment() | |||||
| }else{ | |||||
| setTimeDownCount(timedowncount); | |||||
| } | |||||
| } | } | ||||
| },[time]) | },[time]) | ||||
| const cancelPayment = ()=>{ | const cancelPayment = ()=>{ | ||||
| if (Object.keys(paymentData).length>0){ | if (Object.keys(paymentData).length>0){ | ||||
| HttpUtils.post({ | |||||
| url: UrlUtils.CANCEL_PAYMENT_URL, | |||||
| params:{ | |||||
| "transactionid": paymentData.transactionid, | |||||
| "webtoken": paymentData.webtoken, | |||||
| "paymentid": fpsTransctionData.paymentid | |||||
| }, | |||||
| onSuccess: function(){ | |||||
| // navigate('/paymentPage/fps/ackpage'); | |||||
| let page = '/paymentPage/fps/ackpage'; | |||||
| let stateParams = { state: { transactionid: paymentData.transactionid} } | |||||
| navigate(page, stateParams); | |||||
| } | |||||
| }); | |||||
| getPaymentStatus() | |||||
| if (paymentStatusCode === "INPR"){ | |||||
| HttpUtils.post({ | |||||
| url: UrlUtils.CANCEL_PAYMENT_URL, | |||||
| params:{ | |||||
| "transactionid": paymentData.transactionid, | |||||
| "webtoken": paymentData.webtoken, | |||||
| "paymentid": fpsTransctionData.paymentid | |||||
| }, | |||||
| onSuccess: function(){ | |||||
| navigate('/paymentPage/fps/ackpage'); | |||||
| let page = '/paymentPage/fps/ackpage'; | |||||
| let stateParams = { state: { transactionid: paymentData.transactionid} } | |||||
| navigate(page, stateParams); | |||||
| } | |||||
| }); | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| @@ -330,6 +370,8 @@ const Index = () => { | |||||
| <Grid item xs={12} md={12} > | <Grid item xs={12} md={12} > | ||||
| <Typography variant="h3" sx={{ ml: 8, mt: 4, mr: 8, textAlign: "center" }}> | <Typography variant="h3" sx={{ ml: 8, mt: 4, mr: 8, textAlign: "center" }}> | ||||
| <FormattedMessage id="payAlert"/> | |||||
| <br /><br /> | |||||
| <img src={FpsIcon} width="80" height="80" alt="FPS"></img> | <img src={FpsIcon} width="80" height="80" alt="FPS"></img> | ||||
| <br /> | <br /> | ||||
| <FormattedMessage id="payTotalDeatail"/> | <FormattedMessage id="payTotalDeatail"/> | ||||
| @@ -338,50 +380,69 @@ const Index = () => { | |||||
| </Typography> | </Typography> | ||||
| {browserType==mobileBrowser? | {browserType==mobileBrowser? | ||||
| <Typography variant="h3" sx={{ ml: 8, mt: 4, mr: 8, textAlign: "center" }}> | <Typography variant="h3" sx={{ ml: 8, mt: 4, mr: 8, textAlign: "center" }}> | ||||
| <Button | |||||
| component="span" | |||||
| variant="contained" | |||||
| size="large" | |||||
| color="primary" | |||||
| onClick={()=>{ | |||||
| mobliePayment(); | |||||
| }} | |||||
| sx={{ m: 4 }} | |||||
| >請選擇支付程式付款-Testing</Button> | |||||
| <Button | |||||
| component="span" | |||||
| variant="contained" | |||||
| size="large" | |||||
| color="primary" | |||||
| onClick={()=>{ | |||||
| mobliePaymentPrd(); | |||||
| }} | |||||
| sx={{ m: 4 }} | |||||
| >請選擇支付程式付款-PRD</Button> | |||||
| <Button | |||||
| component="span" | |||||
| variant="contained" | |||||
| size="large" | |||||
| color="primary" | |||||
| onClick={()=>{ | |||||
| mobliePaymentFps(); | |||||
| }} | |||||
| sx={{ m: 4 }} | |||||
| >請選擇支付程式付款-fps prefix</Button> | |||||
| { | |||||
| sysEnv=="prod"? | |||||
| <Button | |||||
| component="span" | |||||
| variant="contained" | |||||
| size="large" | |||||
| color="primary" | |||||
| onClick={()=>{ | |||||
| mobliePaymentPrd(); | |||||
| }} | |||||
| sx={{ m: 4 }} | |||||
| >請選擇支付程式付款</Button> | |||||
| : | |||||
| <> | |||||
| <Button | |||||
| component="span" | |||||
| variant="contained" | |||||
| size="large" | |||||
| color="primary" | |||||
| onClick={()=>{ | |||||
| mobliePayment(); | |||||
| }} | |||||
| sx={{ m: 4 }} | |||||
| >請選擇支付程式付款-Testing</Button> | |||||
| <Button | |||||
| component="span" | |||||
| variant="contained" | |||||
| size="large" | |||||
| color="primary" | |||||
| onClick={()=>{ | |||||
| mobliePaymentFps(); | |||||
| }} | |||||
| sx={{ m: 4 }} | |||||
| >請選擇支付程式付款-fps prefix</Button> | |||||
| </> | |||||
| } | |||||
| </Typography> | </Typography> | ||||
| : | : | ||||
| <Typography variant="h3" sx={{ ml: 8, mt: 4, mr: 8, textAlign: "center" }}> | <Typography variant="h3" sx={{ ml: 8, mt: 4, mr: 8, textAlign: "center" }}> | ||||
| 請掃描以下二維碼 | |||||
| <FormattedMessage id="fpsQrcodeTitle1"/> | |||||
| <br /> | <br /> | ||||
| <img src={fpsTransctionData.fpsqrcodeimgbase64} alt="QR Code"/> | |||||
| { | |||||
| !qrCodeTimeout? | |||||
| <img src={fpsTransctionData.fpsqrcodeimgbase64} alt="QR Code"/> | |||||
| :<img src={expiredQrcode} alt="Expired QR Code"/> | |||||
| } | |||||
| <br /> | <br /> | ||||
| {"["+paymentId+"]"} | {"["+paymentId+"]"} | ||||
| <br/> | <br/> | ||||
| 二維碼有效期限3分鐘 | |||||
| <br /> | |||||
| 請在規定時間內完成付款流程 | |||||
| <br /> | |||||
| {"剩餘時間: "+timeDownCount+ "秒"} | |||||
| { | |||||
| timeDownCount<=0? | |||||
| <FormattedMessage id="fpsQrcodeExpired"/>: | |||||
| <> | |||||
| <FormattedMessage id="fpsQrcodeTitle2"/> | |||||
| <br /> | |||||
| <FormattedMessage id="fpsQrcodeTitle3"/> | |||||
| <br /> | |||||
| <FormattedMessage id="fpsQrcodeTitle4"/> | |||||
| {timeDownCount} | |||||
| <FormattedMessage id="fpsQrcodeTitle5"/> | |||||
| </> | |||||
| } | |||||
| </Typography> | </Typography> | ||||
| } | } | ||||
| <Typography variant="h3" sx={{ ml: 8, mt: 4, mr: 8, textAlign: "center" }}> | <Typography variant="h3" sx={{ ml: 8, mt: 4, mr: 8, textAlign: "center" }}> | ||||
| @@ -312,7 +312,7 @@ const Index = () => { | |||||
| <br/> | <br/> | ||||
| 二維碼有效期限3分鐘 | 二維碼有效期限3分鐘 | ||||
| <br /> | <br /> | ||||
| 請在規定時間內完成付款流程 | |||||
| 請在限規定時間內完成付款流程 | |||||
| <br /> | <br /> | ||||
| {"剩餘時間:"+timeDownCount} | {"剩餘時間:"+timeDownCount} | ||||
| </Typography> | </Typography> | ||||
| @@ -76,22 +76,22 @@ const Fpscallback = () => { | |||||
| const loadForm = () => { | const loadForm = () => { | ||||
| const params = new URLSearchParams(window.location.search); | const params = new URLSearchParams(window.location.search); | ||||
| let transactionid = params.get("TRANSACTION_ID") | |||||
| let webtoken = params.get("WEB_TOKEN") | |||||
| // let transactionid = params.get("TRANSACTION_ID") | |||||
| // let webtoken = params.get("WEB_TOKEN") | |||||
| let paymentId = params.get("PAYMENT_ID") | let paymentId = params.get("PAYMENT_ID") | ||||
| paymentId = paymentId.split('?is_successful')[0]; | paymentId = paymentId.split('?is_successful')[0]; | ||||
| console.log(transactionid) | |||||
| console.log(webtoken) | |||||
| // console.log(transactionid) | |||||
| // console.log(webtoken) | |||||
| console.log(paymentId) | console.log(paymentId) | ||||
| HttpUtils.post({ | HttpUtils.post({ | ||||
| url: UrlUtils.PAYMENT_CALLBACK_STATUS_API, | url: UrlUtils.PAYMENT_CALLBACK_STATUS_API, | ||||
| params:{ | params:{ | ||||
| "apprefid": transactionid, | |||||
| "webtoken": webtoken, | |||||
| // "apprefid": transactionid, | |||||
| // "webtoken": webtoken, | |||||
| "paymentId": paymentId, | "paymentId": paymentId, | ||||
| "transactionid":Number(transactionid) | |||||
| // "transactionid":Number(transactionid) | |||||
| }, | }, | ||||
| onSuccess: function(responseData){ | onSuccess: function(responseData){ | ||||
| setResponeDataData(responseData) | setResponeDataData(responseData) | ||||
| @@ -99,7 +99,7 @@ const Fpscallback = () => { | |||||
| localStorage.removeItem("webtoken"); | localStorage.removeItem("webtoken"); | ||||
| localStorage.removeItem("transactionid"); | localStorage.removeItem("transactionid"); | ||||
| } | } | ||||
| responseData.data["transDateStr"] = DateUtils.dateFormat(responseData.data.transDateTime, "DD/MM/YYYY"); | |||||
| responseData.data["transDateStr"] = responseData.data.transDateTime; | |||||
| responseData.data["transTimeStr"] = DateUtils.dateFormat(responseData.data.transDateTime, "HH:mm:ss"); | responseData.data["transTimeStr"] = DateUtils.dateFormat(responseData.data.transDateTime, "HH:mm:ss"); | ||||
| setResponeDataData(responseData.transactionData) | setResponeDataData(responseData.transactionData) | ||||
| setItemList(responseData.paymentItemList) | setItemList(responseData.paymentItemList) | ||||
| @@ -191,7 +191,7 @@ const Fpscallback = () => { | |||||
| {/*row 1*/} | {/*row 1*/} | ||||
| <Grid item xs={12} md={12} spacing={2} sx={{ textAlign: "center" }}> | <Grid item xs={12} md={12} spacing={2} sx={{ textAlign: "center" }}> | ||||
| <Typography variant="h3" sx={{ ml: 8, mt: 4, mr: 8, textAlign: "center" }}> | <Typography variant="h3" sx={{ ml: 8, mt: 4, mr: 8, textAlign: "center" }}> | ||||
| 您的申請和付款已收到 | |||||
| <FormattedMessage id="MSG.paymentMsg"/> | |||||
| </Typography> | </Typography> | ||||
| <Grid container justifyContent="center" direction="column" spacing={2} sx={{ p: 2 }} alignitems="stretch" > | <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 }}> | <Grid item className="printOrder" xs={12} md={12} sx={{ pt: 2 }} style={{ height: '100%', order: 1 }}> | ||||
| @@ -242,21 +242,21 @@ const Fpscallback = () => { | |||||
| <center> | <center> | ||||
| <Grid item xs={12} md={8} > | <Grid item xs={12} md={8} > | ||||
| <Typography variant="h5" sx={{ ml: 8, mt: 4, mr: 8, textAlign: "left" }}> | <Typography variant="h5" sx={{ ml: 8, mt: 4, mr: 8, textAlign: "left" }}> | ||||
| 付款取消訊息: | |||||
| <br /><br /> | |||||
| 您的付款已被取消。我們收到了您的付款請求,但由於某些原因,付款無法完成。請注意以下事項: | |||||
| <br /><br /> | |||||
| 如果您主動取消了支付,請確認並確保取消是您的意願。 | |||||
| <br /> | |||||
| 如果付款被取消是由於系統問題或其他原因,請您嘗試以下解決方法: | |||||
| <br /><br /> | |||||
| 檢查您的支付帳戶是否有任何異常或限制。 | |||||
| <br /> | |||||
| 確保您的付款資訊準確無誤。 | |||||
| <br /> | |||||
| 檢查您的網路連線是否正常。 | |||||
| <br /><br /> | |||||
| 如果您需要進一步的協助或有任何疑問,請隨時與我們聯繫,我們將盡快解決您的付款問題。謝謝! | |||||
| <FormattedMessage id="MSG.paymentCancelMsg1"/> | |||||
| <br /><br /> | |||||
| <FormattedMessage id="MSG.paymentCancelMsg2"/> | |||||
| <br /><br /> | |||||
| <FormattedMessage id="MSG.paymentCancelMsg3"/> | |||||
| <br /> | |||||
| <FormattedMessage id="MSG.paymentCancelMsg4"/> | |||||
| <br /><br /> | |||||
| <FormattedMessage id="MSG.paymentCancelMsg5"/> | |||||
| <br /> | |||||
| <FormattedMessage id="MSG.paymentCancelMsg6"/> | |||||
| <br /> | |||||
| <FormattedMessage id="MSG.paymentCancelMsg7"/> | |||||
| <br /><br /> | |||||
| <FormattedMessage id="MSG.paymentCancelMsg8"/> | |||||
| </Typography> | </Typography> | ||||
| </Grid> | </Grid> | ||||
| </center> | </center> | ||||
| @@ -285,29 +285,39 @@ const Fpscallback = () => { | |||||
| <Grid container justifyContent="flex-start" alignItems="center" > | <Grid container justifyContent="flex-start" alignItems="center" > | ||||
| <center> | <center> | ||||
| <Grid item xs={12} md={8} > | <Grid item xs={12} md={8} > | ||||
| <Typography variant="h5" sx={{ ml: 8, mt: 4, mr: 8, textAlign: "left" }}> | <Typography variant="h5" sx={{ ml: 8, mt: 4, mr: 8, textAlign: "left" }}> | ||||
| 付款失敗訊息: | |||||
| <FormattedMessage id="MSG.paymentFailMsg1"/> | |||||
| <br /><br /> | <br /><br /> | ||||
| 親愛的用戶,很遺憾地告訴您,您的付款操作未成功。我們在處理您的付款時遇到了問題。請您仔細檢查以下事項: | |||||
| <FormattedMessage id="MSG.paymentFailMsg2"/> | |||||
| <br /><br /> | <br /><br /> | ||||
| 您的支付帳戶餘額是否足夠。 | |||||
| <br /> | |||||
| 您提供的付款資訊是否準確無誤。 | |||||
| <br /> | |||||
| 請檢查您的網路連線是否正常。 | |||||
| <ul> | |||||
| <li> | |||||
| <FormattedMessage id="MSG.paymentFailMsg3"/> | |||||
| </li> | |||||
| <li> | |||||
| <FormattedMessage id="MSG.paymentFailMsg4"/> | |||||
| </li> | |||||
| <li> | |||||
| <FormattedMessage id="MSG.paymentFailMsg5"/> | |||||
| </li> | |||||
| </ul> | |||||
| <br /><br /> | <br /><br /> | ||||
| 如果您已確認以上問題無誤,但付款失敗,請您嘗試以下解決方法: | |||||
| <FormattedMessage id="MSG.paymentFailMsg6"/> | |||||
| <br /><br /> | <br /><br /> | ||||
| 嘗試使用其他付款方式進行付款。 | |||||
| <br /> | |||||
| 檢查您的支付帳戶是否有異常或限制。 | |||||
| <br /> | |||||
| 聯絡我們的客服人員尋求協助。 | |||||
| <ul> | |||||
| <li> | |||||
| <FormattedMessage id="MSG.paymentFailMsg7"/> | |||||
| </li> | |||||
| <li> | |||||
| <FormattedMessage id="MSG.paymentFailMsg8"/> | |||||
| </li> | |||||
| <li> | |||||
| <FormattedMessage id="MSG.paymentFailMsg9"/> | |||||
| </li> | |||||
| </ul> | |||||
| <br /><br /> | <br /><br /> | ||||
| 如果您需要進一步的協助或有任何疑問,請隨時與我們聯繫。非常抱歉給您帶來不便,我們將盡快解決您的付款問題。謝謝! | |||||
| <FormattedMessage id="MSG.paymentFailMsg10"/> | |||||
| </Typography> | </Typography> | ||||
| </Grid> | </Grid> | ||||
| </center> | </center> | ||||
| </Grid> | </Grid> | ||||
| @@ -64,7 +64,7 @@ const MultiPaymentWindow = (props) => { | |||||
| // console.log(props.transactionData) | // console.log(props.transactionData) | ||||
| if(Object.keys(props.transactionData).length > 0){ | if(Object.keys(props.transactionData).length > 0){ | ||||
| setLoadtTransactionData(props.transactionData) | setLoadtTransactionData(props.transactionData) | ||||
| console.log(props.browserType) | |||||
| // console.log(props.browserType) | |||||
| } | } | ||||
| }, [props.transactionData]); | }, [props.transactionData]); | ||||
| @@ -292,7 +292,7 @@ const MultiPaymentWindow = (props) => { | |||||
| <DialogContent> | <DialogContent> | ||||
| <DialogContentText> | <DialogContentText> | ||||
| <FormLabel sx={{ fontSize: "20px", color: "#000000", textAlign: "left", ml:1}}> | <FormLabel sx={{ fontSize: "20px", color: "#000000", textAlign: "left", ml:1}}> | ||||
| <FormattedMessage id="paymentProcessLimited"/>。 | |||||
| <FormattedMessage id="paymentProcessLimited"/> | |||||
| </FormLabel> | </FormLabel> | ||||
| <Grid item xs={12} md={12} sx={{ pt: 2 }} style={{ height: '100%' }} width="100%"> | <Grid item xs={12} md={12} sx={{ pt: 2 }} style={{ height: '100%' }} width="100%"> | ||||
| <Box xs={12} md={12} sx={{ p: 4, border: '3px solid #eee', borderRadius: '10px' }} > | <Box xs={12} md={12} sx={{ p: 4, border: '3px solid #eee', borderRadius: '10px' }} > | ||||
| @@ -304,7 +304,7 @@ const MultiPaymentWindow = (props) => { | |||||
| </Typography> | </Typography> | ||||
| {/* <Typography variant="h5" sx={{ textAlign: "left" }}> | {/* <Typography variant="h5" sx={{ textAlign: "left" }}> | ||||
| 支付金額: HK$ {FormatUtils.currencyFormat(props.totalAmount)} | |||||
| 付款金額: HK$ {FormatUtils.currencyFormat(props.totalAmount)} | |||||
| </Typography> */} | </Typography> */} | ||||
| {!props.onReady ? | {!props.onReady ? | ||||
| <LoadingComponent /> | <LoadingComponent /> | ||||
| @@ -373,7 +373,7 @@ const MultiPaymentWindow = (props) => { | |||||
| </Grid>: | </Grid>: | ||||
| <Grid container direction="row" justifyContent="center" alignItems="center"> | <Grid container direction="row" justifyContent="center" alignItems="center"> | ||||
| <FormLabel sx={{ fontSize: "20px", color: "#000000", textAlign: "center"}}> | <FormLabel sx={{ fontSize: "20px", color: "#000000", textAlign: "center"}}> | ||||
| <FormattedMessage id="paymentMethodNotAvailable"/>。 | |||||
| <FormattedMessage id="paymentMethodNotAvailable"/> | |||||
| </FormLabel> | </FormLabel> | ||||
| </Grid> | </Grid> | ||||
| } | } | ||||
| @@ -381,12 +381,12 @@ const MultiPaymentWindow = (props) => { | |||||
| <Grid item xs={12} md={12}> | <Grid item xs={12} md={12}> | ||||
| <Grid container > | <Grid container > | ||||
| <Grid item> | <Grid item> | ||||
| <Typography variant="pnspsFormParagraphBold" sx={{ color: "#000000", textAlign: "left" }}> | |||||
| <FormattedMessage id="payTotal"/>(HK$): | |||||
| <Typography variant="h5" sx={{ color: "#000000", textAlign: "left" }}> | |||||
| <FormattedMessage id="payTotal"/> ($): | |||||
| </Typography> | </Typography> | ||||
| </Grid> | </Grid> | ||||
| <Grid item> | <Grid item> | ||||
| <Typography variant="pnspsFormParagraphBold" sx={{color: "#000000", textAlign: "left" }}> | |||||
| <Typography variant="h5" sx={{color: "#000000", textAlign: "left" }}> | |||||
| {" HK$ " + FormatUtils.currencyFormat(props.totalAmount)} | {" HK$ " + FormatUtils.currencyFormat(props.totalAmount)} | ||||
| </Typography> | </Typography> | ||||
| </Grid> | </Grid> | ||||
| @@ -123,7 +123,7 @@ const Index = () => { | |||||
| localStorage.removeItem("webtoken"); | localStorage.removeItem("webtoken"); | ||||
| localStorage.removeItem("transactionid"); | localStorage.removeItem("transactionid"); | ||||
| } | } | ||||
| responseData.data["transDateStr"] = DateUtils.dateFormat(responseData.data.transDateTime, "DD/MM/YYYY"); | |||||
| responseData.data["transDateStr"] = responseData.data.transDateTime; | |||||
| responseData.data["transTimeStr"] = DateUtils.dateFormat(responseData.data.transDateTime, "HH:mm:ss"); | responseData.data["transTimeStr"] = DateUtils.dateFormat(responseData.data.transDateTime, "HH:mm:ss"); | ||||
| setResponeDataData(responseData.transactionData) | setResponeDataData(responseData.transactionData) | ||||
| setItemList(responseData.paymentItemList) | setItemList(responseData.paymentItemList) | ||||
| @@ -294,29 +294,39 @@ const Index = () => { | |||||
| <Grid container justifyContent="flex-start" alignItems="center" > | <Grid container justifyContent="flex-start" alignItems="center" > | ||||
| <center> | <center> | ||||
| <Grid item xs={12} md={8} > | <Grid item xs={12} md={8} > | ||||
| <Typography variant="h5" sx={{ ml: 8, mt: 4, mr: 8, textAlign: "left" }}> | <Typography variant="h5" sx={{ ml: 8, mt: 4, mr: 8, textAlign: "left" }}> | ||||
| <FormattedMessage id="MSG.paymentFailMsg1"/> | <FormattedMessage id="MSG.paymentFailMsg1"/> | ||||
| <br /><br /> | <br /><br /> | ||||
| <FormattedMessage id="MSG.paymentFailMsg2"/> | <FormattedMessage id="MSG.paymentFailMsg2"/> | ||||
| <br /><br /> | <br /><br /> | ||||
| <FormattedMessage id="MSG.paymentFailMsg3"/> | |||||
| <br /> | |||||
| <FormattedMessage id="MSG.paymentFailMsg4"/> | |||||
| <br /> | |||||
| <FormattedMessage id="MSG.paymentFailMsg5"/> | |||||
| <ul> | |||||
| <li> | |||||
| <FormattedMessage id="MSG.paymentFailMsg3"/> | |||||
| </li> | |||||
| <li> | |||||
| <FormattedMessage id="MSG.paymentFailMsg4"/> | |||||
| </li> | |||||
| <li> | |||||
| <FormattedMessage id="MSG.paymentFailMsg5"/> | |||||
| </li> | |||||
| </ul> | |||||
| <br /><br /> | <br /><br /> | ||||
| <FormattedMessage id="MSG.paymentFailMsg6"/> | <FormattedMessage id="MSG.paymentFailMsg6"/> | ||||
| <br /><br /> | <br /><br /> | ||||
| <FormattedMessage id="MSG.paymentFailMsg7"/> | |||||
| <br /> | |||||
| <FormattedMessage id="MSG.paymentFailMsg8"/> | |||||
| <br /> | |||||
| <FormattedMessage id="MSG.paymentFailMsg9"/> | |||||
| <ul> | |||||
| <li> | |||||
| <FormattedMessage id="MSG.paymentFailMsg7"/> | |||||
| </li> | |||||
| <li> | |||||
| <FormattedMessage id="MSG.paymentFailMsg8"/> | |||||
| </li> | |||||
| <li> | |||||
| <FormattedMessage id="MSG.paymentFailMsg9"/> | |||||
| </li> | |||||
| </ul> | |||||
| <br /><br /> | <br /><br /> | ||||
| <FormattedMessage id="MSG.paymentFailMsg10"/> | <FormattedMessage id="MSG.paymentFailMsg10"/> | ||||
| </Typography> | |||||
| </Typography> | |||||
| </Grid> | </Grid> | ||||
| </center> | </center> | ||||
| </Grid> | </Grid> | ||||
| @@ -1,17 +1,35 @@ | |||||
| // material-ui | // material-ui | ||||
| import * as React from 'react'; | import * as React from 'react'; | ||||
| import * as DateUtils from "utils/DateUtils"; | import * as DateUtils from "utils/DateUtils"; | ||||
| import {PAYMENT_LIST} from "utils/ApiPathConst"; | |||||
| import {PAYMENT_LIST, PAYMENT_BIB} from "utils/ApiPathConst"; | |||||
| import * as HttpUtils from "utils/HttpUtils"; | |||||
| import * as FormatUtils from "utils/FormatUtils" | import * as FormatUtils from "utils/FormatUtils" | ||||
| import * as PaymentStatus from "utils/statusUtils/PaymentStatus" | import * as PaymentStatus from "utils/statusUtils/PaymentStatus" | ||||
| import { useNavigate } from "react-router-dom"; | import { useNavigate } from "react-router-dom"; | ||||
| import { FiDataGrid } from "components/FiDataGrid"; | import { FiDataGrid } from "components/FiDataGrid"; | ||||
| import { clickableLink } from 'utils/CommonFunction'; | import { clickableLink } from 'utils/CommonFunction'; | ||||
| import { getPaymentMethodByCode} from "auth/utils"; | |||||
| import { | |||||
| Checkbox, | |||||
| Dialog, DialogTitle, DialogContent, DialogActions, | |||||
| Button,Typography | |||||
| // MenuItem | |||||
| } from '@mui/material'; | |||||
| // ==============================|| EVENT TABLE ||============================== // | // ==============================|| EVENT TABLE ||============================== // | ||||
| export default function SearchPaymentTable({ searchCriteria }) { | |||||
| export default function SearchPaymentTable({ searchCriteria, applyGridOnReady, applySearch}) { | |||||
| const [_searchCriteria, set_searchCriteria] = React.useState(searchCriteria); | const [_searchCriteria, set_searchCriteria] = React.useState(searchCriteria); | ||||
| const navigate = useNavigate() | const navigate = useNavigate() | ||||
| const [isPopUp, setIsPopUp] = React.useState(false); | |||||
| const [bibId, setBibId] = React.useState(); | |||||
| const [bib, setBib] = React.useState(); | |||||
| const [appNo, setAppNo] = React.useState(); | |||||
| const [refreshTrigger, setRefreshTrigger] = React.useState(0); | |||||
| const forceRefresh = () => { | |||||
| setRefreshTrigger(prev => prev + 1); | |||||
| }; | |||||
| const _sx = { | const _sx = { | ||||
| padding: "4 2 4 2", | padding: "4 2 4 2", | ||||
| @@ -37,6 +55,24 @@ export default function SearchPaymentTable({ searchCriteria }) { | |||||
| navigate('/paymentPage/details/' + params.row.id); | navigate('/paymentPage/details/' + params.row.id); | ||||
| }; | }; | ||||
| const doBIB = () => { | |||||
| setIsPopUp(false); | |||||
| // console.log(refreshTrigger) | |||||
| HttpUtils.post({ | |||||
| url: PAYMENT_BIB + "/" + bibId, | |||||
| onSuccess: function () { | |||||
| forceRefresh() | |||||
| } | |||||
| }); | |||||
| } | |||||
| const popUPBib = (id, bibFlag, appNo) => { | |||||
| setBibId(id) | |||||
| setBib(bibFlag) | |||||
| setAppNo(appNo) | |||||
| setIsPopUp(true); | |||||
| } | |||||
| const columns = [ | const columns = [ | ||||
| { | { | ||||
| id: 'appNos', | id: 'appNos', | ||||
| @@ -59,14 +95,46 @@ export default function SearchPaymentTable({ searchCriteria }) { | |||||
| return clickableLink('/paymentPage/details/' + params.row.id, params.row.transNo); | return clickableLink('/paymentPage/details/' + params.row.id, params.row.transNo); | ||||
| }, | }, | ||||
| }, | }, | ||||
| { | |||||
| field: 'payMethod', | |||||
| headerName: 'Payment Method', | |||||
| flex: 1, | |||||
| width: 150, | |||||
| renderCell: (params) => { | |||||
| return getPaymentMethodByCode(params?.value) | |||||
| } | |||||
| }, | |||||
| { | { | ||||
| id: 'transDateTime', | id: 'transDateTime', | ||||
| field: 'transDateTime', | field: 'transDateTime', | ||||
| headerName: 'Transaction Date', | headerName: 'Transaction Date', | ||||
| flex: 1, | flex: 1, | ||||
| minWidth: 150, | minWidth: 150, | ||||
| valueGetter: (params) => { | |||||
| return DateUtils.dateStr(params?.value); | |||||
| // sorting/filtering uses this value | |||||
| valueGetter: (params) => DateUtils.toDate(params?.value), | |||||
| // display uses this (params.value is the *Date* returned above) | |||||
| valueFormatter: (params) => { | |||||
| const d = params.value; // Date or Invalid Date | |||||
| return d instanceof Date && !isNaN(d.getTime()) | |||||
| ? DateUtils.dateStr(d) | |||||
| : ""; | |||||
| }, | |||||
| // make sorting 100% deterministic | |||||
| sortComparator: (v1, v2) => { | |||||
| const t1 = v1 instanceof Date && !isNaN(v1.getTime()) ? v1.getTime() : -Infinity; | |||||
| const t2 = v2 instanceof Date && !isNaN(v2.getTime()) ? v2.getTime() : -Infinity; | |||||
| return t1 - t2; | |||||
| } | |||||
| }, | |||||
| { | |||||
| field: 'bib', | |||||
| headerName: 'BIB', | |||||
| width: 150, | |||||
| renderCell: (params) => { | |||||
| return <Checkbox checked={params.row.bib} onChange={() => {popUPBib(params.row.id, params.row.bib, params.row.appNos)}}/>; | |||||
| } | } | ||||
| }, | }, | ||||
| { | { | ||||
| @@ -96,11 +164,39 @@ export default function SearchPaymentTable({ searchCriteria }) { | |||||
| columns={columns} | columns={columns} | ||||
| customPageSize={10} | customPageSize={10} | ||||
| onRowDoubleClick={handleEditClick} | onRowDoubleClick={handleEditClick} | ||||
| doLoad={{ | |||||
| url:PAYMENT_LIST, | |||||
| params:_searchCriteria, | |||||
| }} | |||||
| applyGridOnReady={applyGridOnReady} | |||||
| applySearch={applySearch} | |||||
| // doLoad={{ | |||||
| // url:PAYMENT_LIST, | |||||
| // params:_searchCriteria, | |||||
| // }} | |||||
| doLoad={React.useMemo(() => ({ | |||||
| url: PAYMENT_LIST, | |||||
| params: _searchCriteria, | |||||
| }), [_searchCriteria, refreshTrigger])} | |||||
| /> | /> | ||||
| <div> | |||||
| <Dialog | |||||
| open={isPopUp} | |||||
| onClose={() => setIsPopUp(false)} | |||||
| PaperProps={{ | |||||
| sx: { | |||||
| minWidth: '40vw', | |||||
| maxWidth: { xs: '90vw', s: '90vw', m: '70vw', lg: '70vw' }, | |||||
| maxHeight: { xs: '90vh', s: '70vh', m: '70vh', lg: '60vh' } | |||||
| } | |||||
| }} | |||||
| > | |||||
| <DialogTitle>Bank-in-bank</DialogTitle> | |||||
| <DialogContent style={{ display: 'flex', }}> | |||||
| <Typography variant="h5" style={{ padding: '16px' }}>{bib?"Cancel Bank-in-bank?":"Set "+appNo+" as Bank-in-bank?"}</Typography> | |||||
| </DialogContent> | |||||
| <DialogActions> | |||||
| <Button onClick={() => setIsPopUp(false)}><Typography variant="h5">Cancel</Typography></Button> | |||||
| <Button onClick={() => doBIB()}><Typography variant="h5">Confirm</Typography></Button> | |||||
| </DialogActions> | |||||
| </Dialog> | |||||
| </div> | |||||
| </div> | </div> | ||||
| ); | ); | ||||
| } | } | ||||
| @@ -19,11 +19,12 @@ import {DemoItem} from "@mui/x-date-pickers/internals/demo"; | |||||
| import {LocalizationProvider} from "@mui/x-date-pickers/LocalizationProvider"; | import {LocalizationProvider} from "@mui/x-date-pickers/LocalizationProvider"; | ||||
| import {AdapterDayjs} from "@mui/x-date-pickers/AdapterDayjs"; | import {AdapterDayjs} from "@mui/x-date-pickers/AdapterDayjs"; | ||||
| // ==============================|| DASHBOARD - DEFAULT ||============================== // | // ==============================|| DASHBOARD - DEFAULT ||============================== // | ||||
| const SearchPublicNoticeForm = ({ applySearch, searchCriteria }) => { | |||||
| const SearchPublicNoticeForm = ({ applySearch, searchCriteria, onGridReady }) => { | |||||
| const [minDate, setMinDate] = React.useState(searchCriteria.dateFrom); | const [minDate, setMinDate] = React.useState(searchCriteria.dateFrom); | ||||
| const [maxDate, setMaxDate] = React.useState(searchCriteria.dateTo); | const [maxDate, setMaxDate] = React.useState(searchCriteria.dateTo); | ||||
| const [status, setStatus] = React.useState(ComboData.paymentStatus[0]); | const [status, setStatus] = React.useState(ComboData.paymentStatus[0]); | ||||
| const [payMethod, setPayMethod] = React.useState(ComboData.payMethod[0]); | |||||
| const { reset, register, handleSubmit } = useForm() | const { reset, register, handleSubmit } = useForm() | ||||
| const marginBottom = 2.5; | const marginBottom = 2.5; | ||||
| @@ -31,6 +32,38 @@ const SearchPublicNoticeForm = ({ applySearch, searchCriteria }) => { | |||||
| const [fromDateValue, setFromDateValue] = React.useState("dd / mm / yyyy"); | const [fromDateValue, setFromDateValue] = React.useState("dd / mm / yyyy"); | ||||
| const [toDateValue, setToDateValue] = React.useState("dd / mm / yyyy"); | const [toDateValue, setToDateValue] = React.useState("dd / mm / yyyy"); | ||||
| React.useEffect(() => { | |||||
| if(searchCriteria.status!=undefined){ | |||||
| if(searchCriteria.status === ""){ | |||||
| ComboData.paymentStatus[0] | |||||
| }else{ | |||||
| setStatus(ComboData.paymentStatus.find(item => item.type === searchCriteria.status)) | |||||
| } | |||||
| }else{ | |||||
| setStatus(ComboData.paymentStatus[0]) | |||||
| } | |||||
| }, [searchCriteria]); | |||||
| React.useEffect(() => { | |||||
| const defaultPayMethod = ComboData.payMethod[0]; | |||||
| const value = searchCriteria?.payMethod; // may be [], null, undefined, or array of strings | |||||
| if (!value || value.length === 0) { | |||||
| setPayMethod(defaultPayMethod); | |||||
| return; | |||||
| } | |||||
| // Find the matching entry whose type array matches value contents | |||||
| const found = ComboData.payMethod.find(item => | |||||
| Array.isArray(item.type) && | |||||
| item.type.length === value.length && | |||||
| item.type.every((v, i) => v === value[i]) // strict positional match | |||||
| ); | |||||
| setPayMethod(found ?? defaultPayMethod); | |||||
| }, [searchCriteria?.payMethod]); | |||||
| React.useEffect(() => { | React.useEffect(() => { | ||||
| setFromDateValue(minDate); | setFromDateValue(minDate); | ||||
| }, [minDate]); | }, [minDate]); | ||||
| @@ -39,6 +72,13 @@ const SearchPublicNoticeForm = ({ applySearch, searchCriteria }) => { | |||||
| setToDateValue(maxDate); | setToDateValue(maxDate); | ||||
| }, [maxDate]); | }, [maxDate]); | ||||
| // add near the top inside the component (after useState for payMethod) | |||||
| const toPayMethodArray = (opt) => { | |||||
| if (!opt || opt.type === 'all') return []; | |||||
| return Array.isArray(opt.type) ? opt.type : [opt.type]; | |||||
| }; | |||||
| const onSubmit = (data) => { | const onSubmit = (data) => { | ||||
| let sentDateFrom = ""; | let sentDateFrom = ""; | ||||
| let sentDateTo = ""; | let sentDateTo = ""; | ||||
| @@ -54,6 +94,9 @@ const SearchPublicNoticeForm = ({ applySearch, searchCriteria }) => { | |||||
| dateFrom: sentDateFrom, | dateFrom: sentDateFrom, | ||||
| dateTo: sentDateTo, | dateTo: sentDateTo, | ||||
| status : (status?.type && status?.type != 'all') ? status?.type : "", | status : (status?.type && status?.type != 'all') ? status?.type : "", | ||||
| payMethod : toPayMethodArray(payMethod), | |||||
| start:0, | |||||
| limit:10 | |||||
| }; | }; | ||||
| applySearch(temp); | applySearch(temp); | ||||
| }; | }; | ||||
| @@ -62,7 +105,11 @@ const SearchPublicNoticeForm = ({ applySearch, searchCriteria }) => { | |||||
| setStatus(ComboData.paymentStatus[0]); | setStatus(ComboData.paymentStatus[0]); | ||||
| setMinDate(DateUtils.dateValue(new Date().setDate(new Date().getDate()-14))) | setMinDate(DateUtils.dateValue(new Date().setDate(new Date().getDate()-14))) | ||||
| setMaxDate(DateUtils.dateValue(new Date())) | setMaxDate(DateUtils.dateValue(new Date())) | ||||
| reset(); | |||||
| reset({ | |||||
| code:"", | |||||
| transNo:"" | |||||
| }); | |||||
| localStorage.setItem('searchCriteria',"") | |||||
| } | } | ||||
| @@ -184,6 +231,11 @@ const SearchPublicNoticeForm = ({ applySearch, searchCriteria }) => { | |||||
| setStatus(newValue); | setStatus(newValue); | ||||
| } | } | ||||
| }} | }} | ||||
| sx={{ | |||||
| '& .MuiInputBase-root': { alignItems: 'center' }, | |||||
| '& .MuiAutocomplete-endAdornment': { top: '50%', transform: 'translateY(-50%)' }, | |||||
| '& .MuiOutlinedInput-root': { height: 40 } | |||||
| }} | |||||
| renderInput={(params) => ( | renderInput={(params) => ( | ||||
| <TextField {...params} | <TextField {...params} | ||||
| label="Status" | label="Status" | ||||
| @@ -194,6 +246,40 @@ const SearchPublicNoticeForm = ({ applySearch, searchCriteria }) => { | |||||
| }} | }} | ||||
| /> | /> | ||||
| </Grid> | </Grid> | ||||
| <Grid item xs={9} s={6} md={5} lg={3} sx={{ ml: 3, mr: 3, mb: marginBottom }}> | |||||
| <Autocomplete | |||||
| {...register("payMethod")} | |||||
| disablePortal={false} | |||||
| size="small" | |||||
| id="payMethod" | |||||
| filterOptions={(options) => options} | |||||
| options={ComboData.payMethod} | |||||
| value={payMethod} | |||||
| getOptionLabel={(option) => option.label} | |||||
| inputValue={payMethod?.label ? payMethod?.label : ""} | |||||
| onChange={(event, newValue) => { | |||||
| if(newValue==null){ | |||||
| setPayMethod(ComboData.payMethod[0]); | |||||
| }else{ | |||||
| setPayMethod(newValue); | |||||
| } | |||||
| }} | |||||
| sx={{ | |||||
| '& .MuiInputBase-root': { alignItems: 'center' }, | |||||
| '& .MuiAutocomplete-endAdornment': { top: '50%', transform: 'translateY(-50%)' }, | |||||
| '& .MuiOutlinedInput-root': { height: 40 } | |||||
| }} | |||||
| renderInput={(params) => ( | |||||
| <TextField {...params} | |||||
| label="Payment Method" | |||||
| /> | |||||
| )} | |||||
| InputLabelProps={{ | |||||
| shrink: true | |||||
| }} | |||||
| /> | |||||
| </Grid> | |||||
| </Grid> | </Grid> | ||||
| </Grid> | </Grid> | ||||
| @@ -215,6 +301,7 @@ const SearchPublicNoticeForm = ({ applySearch, searchCriteria }) => { | |||||
| <Button | <Button | ||||
| variant="contained" | variant="contained" | ||||
| type="submit" | type="submit" | ||||
| disabled={onGridReady} | |||||
| > | > | ||||
| Submit | Submit | ||||
| </Button> | </Button> | ||||
| @@ -7,6 +7,7 @@ import { | |||||
| import MainCard from "components/MainCard"; | import MainCard from "components/MainCard"; | ||||
| import * as React from "react"; | import * as React from "react"; | ||||
| import * as DateUtils from "utils/DateUtils"; | import * as DateUtils from "utils/DateUtils"; | ||||
| import { getSearchCriteria } from "auth/utils"; | |||||
| import Loadable from 'components/Loadable'; | import Loadable from 'components/Loadable'; | ||||
| const LoadingComponent = Loadable(React.lazy(() => import('pages/extra-pages/LoadingComponent'))); | const LoadingComponent = Loadable(React.lazy(() => import('pages/extra-pages/LoadingComponent'))); | ||||
| @@ -28,18 +29,34 @@ const BackgroundHead = { | |||||
| const Index = () => { | const Index = () => { | ||||
| const [searchCriteria, setSearchCriteria] = React.useState({ | |||||
| dateTo: DateUtils.dateValue(new Date()), | |||||
| dateFrom: DateUtils.dateValue(new Date().setDate(new Date().getDate()-14)), | |||||
| }); | |||||
| const [searchCriteria, setSearchCriteria] = React.useState({}); | |||||
| const [onReady, setOnReady] = React.useState(false); | const [onReady, setOnReady] = React.useState(false); | ||||
| const [onGridReady, setGridOnReady] = React.useState(false); | |||||
| React.useEffect(() => { | |||||
| if (Object.keys(getSearchCriteria(window.location.pathname)).length>0){ | |||||
| setSearchCriteria(getSearchCriteria(window.location.pathname)) | |||||
| }else{ | |||||
| localStorage.setItem('searchCriteria',"") | |||||
| setSearchCriteria({ | |||||
| dateTo: DateUtils.dateValue(new Date()), | |||||
| dateFrom: DateUtils.dateValue(new Date().setDate(new Date().getDate()-14)), | |||||
| }) | |||||
| } | |||||
| }, []); | |||||
| React.useEffect(() => { | React.useEffect(() => { | ||||
| setOnReady(true); | setOnReady(true); | ||||
| }, [searchCriteria]); | }, [searchCriteria]); | ||||
| function applySearch(input) { | function applySearch(input) { | ||||
| setGridOnReady(true) | |||||
| setSearchCriteria(input); | setSearchCriteria(input); | ||||
| localStorage.setItem('searchCriteria', JSON.stringify({path:window.location.pathname,data:input})) | |||||
| } | |||||
| function applyGridOnReady(input) { | |||||
| setGridOnReady(input); | |||||
| } | } | ||||
| return ( | return ( | ||||
| @@ -63,8 +80,9 @@ const Index = () => { | |||||
| {/*row 1*/} | {/*row 1*/} | ||||
| <Grid item xs={12} md={12} lg={12} sx={{mb:-1}}> | <Grid item xs={12} md={12} lg={12} sx={{mb:-1}}> | ||||
| <SearchForm | <SearchForm | ||||
| applySearch={applySearch} | |||||
| searchCriteria={searchCriteria} | |||||
| applySearch={applySearch} | |||||
| searchCriteria={searchCriteria} | |||||
| onGridReady={onGridReady} | |||||
| /> | /> | ||||
| </Grid> | </Grid> | ||||
| {/*row 2*/} | {/*row 2*/} | ||||
| @@ -76,6 +94,8 @@ const Index = () => { | |||||
| > | > | ||||
| <EventTable | <EventTable | ||||
| searchCriteria={searchCriteria} | searchCriteria={searchCriteria} | ||||
| applyGridOnReady={applyGridOnReady} | |||||
| applySearch={applySearch} | |||||
| /> | /> | ||||
| </MainCard> | </MainCard> | ||||
| </Grid> | </Grid> | ||||
| @@ -14,7 +14,7 @@ import { clickableLink } from 'utils/CommonFunction'; | |||||
| import {PAYMENT_LIST} from "utils/ApiPathConst"; | import {PAYMENT_LIST} from "utils/ApiPathConst"; | ||||
| // ==============================|| EVENT TABLE ||============================== // | // ==============================|| EVENT TABLE ||============================== // | ||||
| export default function SearchPublicNoticeTable({ searchCriteria }) { | |||||
| export default function SearchPublicNoticeTable({ searchCriteria, applyGridOnReady, applySearch }) { | |||||
| const navigate = useNavigate() | const navigate = useNavigate() | ||||
| const theme = useTheme(); | const theme = useTheme(); | ||||
| const isMdOrLg = useMediaQuery(theme.breakpoints.up('md')); | const isMdOrLg = useMediaQuery(theme.breakpoints.up('md')); | ||||
| @@ -94,7 +94,7 @@ export default function SearchPublicNoticeTable({ searchCriteria }) { | |||||
| { | { | ||||
| id: 'payAmount', | id: 'payAmount', | ||||
| field: 'payAmount', | field: 'payAmount', | ||||
| headerName: intl.formatMessage({id: 'currencyAmount'}) + ' ($)', | |||||
| headerName: intl.formatMessage({id: 'currencyAmount'}), | |||||
| width: 150, | width: 150, | ||||
| valueGetter: (params) => { | valueGetter: (params) => { | ||||
| return (params?.value) ? "$ " + FormatUtils.currencyFormat(params?.value) : ""; | return (params?.value) ? "$ " + FormatUtils.currencyFormat(params?.value) : ""; | ||||
| @@ -110,10 +110,16 @@ export default function SearchPublicNoticeTable({ searchCriteria }) { | |||||
| columns={columns} | columns={columns} | ||||
| customPageSize={10} | customPageSize={10} | ||||
| onRowDoubleClick={handleEditDoubleClick} | onRowDoubleClick={handleEditDoubleClick} | ||||
| doLoad={{ | |||||
| applyGridOnReady={applyGridOnReady} | |||||
| applySearch={applySearch} | |||||
| // doLoad={{ | |||||
| // url: PAYMENT_LIST, | |||||
| // params: _searchCriteria, | |||||
| // }} | |||||
| doLoad={React.useMemo(() => ({ | |||||
| url: PAYMENT_LIST, | url: PAYMENT_LIST, | ||||
| params: _searchCriteria, | params: _searchCriteria, | ||||
| }} | |||||
| }), [_searchCriteria])} | |||||
| /> | /> | ||||
| </div> | </div> | ||||
| ); | ); | ||||
| @@ -21,11 +21,11 @@ import {DemoItem} from "@mui/x-date-pickers/internals/demo"; | |||||
| import {LocalizationProvider} from "@mui/x-date-pickers/LocalizationProvider"; | import {LocalizationProvider} from "@mui/x-date-pickers/LocalizationProvider"; | ||||
| import {AdapterDayjs} from "@mui/x-date-pickers/AdapterDayjs"; | import {AdapterDayjs} from "@mui/x-date-pickers/AdapterDayjs"; | ||||
| // ==============================|| DASHBOARD - DEFAULT ||============================== // | // ==============================|| DASHBOARD - DEFAULT ||============================== // | ||||
| const SearchPublicNoticeForm = ({ applySearch, searchCriteria }) => { | |||||
| const SearchPublicNoticeForm = ({ applySearch, searchCriteria, onGridReady }) => { | |||||
| const intl = useIntl(); | const intl = useIntl(); | ||||
| const [minDate, setMinDate] = React.useState(searchCriteria.dateFrom); | const [minDate, setMinDate] = React.useState(searchCriteria.dateFrom); | ||||
| const [maxDate, setMaxDate] = React.useState(searchCriteria.dateTo); | const [maxDate, setMaxDate] = React.useState(searchCriteria.dateTo); | ||||
| const [status, setStatus] = React.useState(ComboData.paymentStatus[0]); | |||||
| const [status, setStatus] = React.useState(searchCriteria.status!=undefined?ComboData.paymentStatus.find(item => item.type === searchCriteria.status):ComboData.paymentStatus[0]); | |||||
| const [fromDateValue, setFromDateValue] = React.useState("dd / mm / yyyy"); | const [fromDateValue, setFromDateValue] = React.useState("dd / mm / yyyy"); | ||||
| const [toDateValue, setToDateValue] = React.useState("dd / mm / yyyy"); | const [toDateValue, setToDateValue] = React.useState("dd / mm / yyyy"); | ||||
| @@ -70,6 +70,8 @@ const SearchPublicNoticeForm = ({ applySearch, searchCriteria }) => { | |||||
| dateFrom: sentDateFrom, | dateFrom: sentDateFrom, | ||||
| dateTo: sentDateTo, | dateTo: sentDateTo, | ||||
| status : (status?.type && status?.type != 'all') ? status?.type : "", | status : (status?.type && status?.type != 'all') ? status?.type : "", | ||||
| start:0, | |||||
| limit:10 | |||||
| }; | }; | ||||
| applySearch(temp); | applySearch(temp); | ||||
| }; | }; | ||||
| @@ -78,7 +80,11 @@ const SearchPublicNoticeForm = ({ applySearch, searchCriteria }) => { | |||||
| setStatus(ComboData.paymentStatus[0]); | setStatus(ComboData.paymentStatus[0]); | ||||
| setMinDate(DateUtils.dateValue(new Date().setDate(new Date().getDate()-14))) | setMinDate(DateUtils.dateValue(new Date().setDate(new Date().getDate()-14))) | ||||
| setMaxDate(DateUtils.dateValue(new Date())) | setMaxDate(DateUtils.dateValue(new Date())) | ||||
| reset(); | |||||
| reset({ | |||||
| code:"", | |||||
| transNo:"" | |||||
| }); | |||||
| localStorage.setItem('searchCriteria',"") | |||||
| } | } | ||||
| @@ -118,7 +124,7 @@ const SearchPublicNoticeForm = ({ applySearch, searchCriteria }) => { | |||||
| <Grid item xs={12} s={6} md={5} lg={3} sx={{ ml: 3, mr: 3, mb: 3 }}> | <Grid item xs={12} s={6} md={5} lg={3} sx={{ ml: 3, mr: 3, mb: 3 }}> | ||||
| <Grid container spacing={1}> | <Grid container spacing={1}> | ||||
| <Grid item xs={6}> | <Grid item xs={6}> | ||||
| <LocalizationProvider dateAdapter={AdapterDayjs}> | |||||
| <LocalizationProvider dateAdapter={AdapterDayjs} localeText={DateUtils.getPickerLocaleText(intl.locale)}> | |||||
| <DemoItem components={['DatePicker']}> | <DemoItem components={['DatePicker']}> | ||||
| <DatePicker | <DatePicker | ||||
| id="dateFrom" | id="dateFrom" | ||||
| @@ -146,7 +152,7 @@ const SearchPublicNoticeForm = ({ applySearch, searchCriteria }) => { | |||||
| </Grid> | </Grid> | ||||
| <Grid item xs={6}> | <Grid item xs={6}> | ||||
| <LocalizationProvider dateAdapter={AdapterDayjs}> | |||||
| <LocalizationProvider dateAdapter={AdapterDayjs} localeText={DateUtils.getPickerLocaleText(intl.locale)}> | |||||
| <DemoItem components={['DatePicker']}> | <DemoItem components={['DatePicker']}> | ||||
| <DatePicker | <DatePicker | ||||
| id="dateTo" | id="dateTo" | ||||
| @@ -181,6 +187,7 @@ const SearchPublicNoticeForm = ({ applySearch, searchCriteria }) => { | |||||
| disablePortal={false} | disablePortal={false} | ||||
| id="status" | id="status" | ||||
| size="small" | size="small" | ||||
| disableClearable | |||||
| filterOptions={(options) => options} | filterOptions={(options) => options} | ||||
| options={ComboData.paymentStatus} | options={ComboData.paymentStatus} | ||||
| value={status} | value={status} | ||||
| @@ -193,6 +200,11 @@ const SearchPublicNoticeForm = ({ applySearch, searchCriteria }) => { | |||||
| setStatus(ComboData.paymentStatus[0]); | setStatus(ComboData.paymentStatus[0]); | ||||
| } | } | ||||
| }} | }} | ||||
| sx={{ | |||||
| '& .MuiInputBase-root': { alignItems: 'center' }, | |||||
| '& .MuiAutocomplete-endAdornment': { top: '50%', transform: 'translateY(-50%)' }, | |||||
| '& .MuiOutlinedInput-root': { height: 40 } | |||||
| }} | |||||
| renderInput={(params) => ( | renderInput={(params) => ( | ||||
| <TextField {...params} | <TextField {...params} | ||||
| label={intl.formatMessage({id: 'status'})} | label={intl.formatMessage({id: 'status'})} | ||||
| @@ -201,6 +213,10 @@ const SearchPublicNoticeForm = ({ applySearch, searchCriteria }) => { | |||||
| InputLabelProps={{ | InputLabelProps={{ | ||||
| shrink: true | shrink: true | ||||
| }} | }} | ||||
| clearText={intl.formatMessage({ id: "muiClear" })} | |||||
| closeText={intl.formatMessage({ id: "muiClose" })} | |||||
| openText={intl.formatMessage({ id: "muiOpen" })} | |||||
| noOptionsText={intl.formatMessage({ id: "muiNoOptions" })} | |||||
| /> | /> | ||||
| </Grid> | </Grid> | ||||
| @@ -239,6 +255,7 @@ const SearchPublicNoticeForm = ({ applySearch, searchCriteria }) => { | |||||
| variant="contained" | variant="contained" | ||||
| type="submit" | type="submit" | ||||
| aria-label={intl.formatMessage({id: 'submit'})} | aria-label={intl.formatMessage({id: 'submit'})} | ||||
| disabled={onGridReady} | |||||
| > | > | ||||
| <FormattedMessage id="submit"/> | <FormattedMessage id="submit"/> | ||||
| </Button> | </Button> | ||||