| Author | SHA1 | Message | Date |
|---|---|---|---|
|
|
f3d166942c | fix login sometime no dialog issue | 2 weeks ago |
|
|
936c892c26 | update year | 2 weeks ago |
|
|
6c8c38597a | update year | 2 weeks ago |
|
|
f037144000 | web access fix update | 3 weeks ago |
|
|
d9e48deeeb | update web access fix | 3 weeks ago |
|
|
e99b83966e | web access fix update | 3 weeks ago |
|
|
b236c09b5d | focus color update | 3 weeks ago |
|
|
31a6c6d901 | bib confirm loading | 4 weeks ago |
|
|
f3f590534d | published loading | 4 weeks ago |
|
|
6877bb8f80 | copyright auto year | 4 weeks ago |
|
|
580b09fb21 | label | 1 month ago |
|
|
ebb7a17bff | fix FPS text size and add i18n | 1 month ago |
|
|
6273b4db68 | font update | 1 month ago |
| @@ -2,6 +2,47 @@ | |||||
| <html lang="en"> | <html lang="en"> | ||||
| <head> | <head> | ||||
| <meta charset="utf-8" /> | <meta charset="utf-8" /> | ||||
| <!-- Preload critical Public Sans webfont files and declare @font-face so the browser | |||||
| uses the custom font immediately on page render, eliminating the flash of the | |||||
| default/fallback font (FOIT/FOUT). --> | |||||
| <link rel="preload" as="font" href="%PUBLIC_URL%/public-sans-latin-400-normal.woff2" type="font/woff2" crossorigin="anonymous" /> | |||||
| <link rel="preload" as="font" href="%PUBLIC_URL%/public-sans-latin-500-normal.woff2" type="font/woff2" crossorigin="anonymous" /> | |||||
| <link rel="preload" as="font" href="%PUBLIC_URL%/public-sans-latin-600-normal.woff2" type="font/woff2" crossorigin="anonymous" /> | |||||
| <link rel="preload" as="font" href="%PUBLIC_URL%/public-sans-latin-700-normal.woff2" type="font/woff2" crossorigin="anonymous" /> | |||||
| <style> | |||||
| @font-face { | |||||
| font-family: 'Public Sans'; | |||||
| font-style: normal; | |||||
| font-display: swap; | |||||
| font-weight: 400; | |||||
| src: url(%PUBLIC_URL%/public-sans-latin-400-normal.woff2) format('woff2'); | |||||
| unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; | |||||
| } | |||||
| @font-face { | |||||
| font-family: 'Public Sans'; | |||||
| font-style: normal; | |||||
| font-display: swap; | |||||
| font-weight: 500; | |||||
| src: url(%PUBLIC_URL%/public-sans-latin-500-normal.woff2) format('woff2'); | |||||
| unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; | |||||
| } | |||||
| @font-face { | |||||
| font-family: 'Public Sans'; | |||||
| font-style: normal; | |||||
| font-display: swap; | |||||
| font-weight: 600; | |||||
| src: url(%PUBLIC_URL%/public-sans-latin-600-normal.woff2) format('woff2'); | |||||
| unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; | |||||
| } | |||||
| @font-face { | |||||
| font-family: 'Public Sans'; | |||||
| font-style: normal; | |||||
| font-display: swap; | |||||
| font-weight: 700; | |||||
| src: url(%PUBLIC_URL%/public-sans-latin-700-normal.woff2) format('woff2'); | |||||
| unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; | |||||
| } | |||||
| </style> | |||||
| <link rel="icon" type="image/png" sizes="32x32" href="%PUBLIC_URL%/favicon-32x32.png"> | <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="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"> | <link rel="mask-icon" href="%PUBLIC_URL%/safari-pinned-tab.svg" color="#5bbad5"> | ||||
| @@ -1,19 +1,20 @@ | |||||
| /* Self-hosted fonts (bundled via @fontsource; no Google Fonts CDN — CSP-friendly). */ | /* Self-hosted fonts (bundled via @fontsource; no Google Fonts CDN — CSP-friendly). */ | ||||
| /* All @fontsource v5 packages already use font-display: swap by default. */ | |||||
| /* Public Sans — MUI theme (`themeConst.js`), charts, config */ | /* Public Sans — MUI theme (`themeConst.js`), charts, config */ | ||||
| @import "@fontsource/public-sans/400.css"; | |||||
| @import "@fontsource/public-sans/500.css"; | |||||
| @import "@fontsource/public-sans/600.css"; | |||||
| @import "@fontsource/public-sans/700.css"; | |||||
| @import url("@fontsource/public-sans/400.css"); | |||||
| @import url("@fontsource/public-sans/500.css"); | |||||
| @import url("@fontsource/public-sans/600.css"); | |||||
| @import url("@fontsource/public-sans/700.css"); | |||||
| /* Noto Sans HK — `styles.css` / navbar (Latin + Hong Kong glyphs) */ | |||||
| @import "@fontsource/noto-sans-hk/latin-400.css"; | |||||
| @import "@fontsource/noto-sans-hk/chinese-hongkong-400.css"; | |||||
| @import "@fontsource/noto-sans-hk/latin-600.css"; | |||||
| @import "@fontsource/noto-sans-hk/chinese-hongkong-600.css"; | |||||
| /* Noto Sans HK — navbar (Latin + Hong Kong glyphs) */ | |||||
| @import url("@fontsource/noto-sans-hk/latin-400.css"); | |||||
| @import url("@fontsource/noto-sans-hk/chinese-hongkong-400.css"); | |||||
| @import url("@fontsource/noto-sans-hk/latin-600.css"); | |||||
| @import url("@fontsource/noto-sans-hk/chinese-hongkong-600.css"); | |||||
| /* Noto Sans SC — fallback for simplified Chinese */ | /* Noto Sans SC — fallback for simplified Chinese */ | ||||
| @import "@fontsource/noto-sans-sc/latin-400.css"; | |||||
| @import "@fontsource/noto-sans-sc/chinese-simplified-400.css"; | |||||
| @import "@fontsource/noto-sans-sc/latin-600.css"; | |||||
| @import "@fontsource/noto-sans-sc/chinese-simplified-600.css"; | |||||
| @import url("@fontsource/noto-sans-sc/latin-400.css"); | |||||
| @import url("@fontsource/noto-sans-sc/chinese-simplified-400.css"); | |||||
| @import url("@fontsource/noto-sans-sc/latin-600.css"); | |||||
| @import url("@fontsource/noto-sans-sc/chinese-simplified-600.css"); | |||||
| @@ -95,9 +95,8 @@ a:active { | |||||
| /* ===== MUI DataGrid keyboard focus (WCAG 2.4.7 / 2.4.11) ===== */ | /* ===== MUI DataGrid keyboard focus (WCAG 2.4.7 / 2.4.11) ===== */ | ||||
| .MuiDataGrid-columnHeader:focus-visible, | .MuiDataGrid-columnHeader:focus-visible, | ||||
| .MuiDataGrid-cell:focus-visible { | .MuiDataGrid-cell:focus-visible { | ||||
| outline: 3px solid #0c489e; | |||||
| outline-offset: -2px; | |||||
| box-shadow: 0 0 0 3px rgba(25, 118, 210, 0.25); | |||||
| outline: none; | |||||
| box-shadow: inset 0 0 0 3px #0c489e; | |||||
| } | } | ||||
| /* Contained buttons only */ | /* Contained buttons only */ | ||||
| @@ -20,6 +20,16 @@ let axiosInterceptorsSetup = false; | |||||
| // In-memory guard so only one 401/logout flow shows the alert (avoids race when multiple 401s or interceptors run) | // In-memory guard so only one 401/logout flow shows the alert (avoids race when multiple 401s or interceptors run) | ||||
| let expiredAlertShownInMemory = false; | let expiredAlertShownInMemory = false; | ||||
| /** Login / public auth endpoints must not trigger token refresh or session-expiry reload. */ | |||||
| const isPublicAuthRequest = (reqUrl) => | |||||
| reqUrl.includes('/login') || reqUrl.includes('/ldap-login'); | |||||
| /** Clear stale session-expiry flag so a new login attempt can show its own error dialog. */ | |||||
| export const clearExpiredSessionAlert = () => { | |||||
| localStorage.removeItem('expiredAlertShown'); | |||||
| expiredAlertShownInMemory = false; | |||||
| }; | |||||
| // ** Handle User Login | // ** Handle User Login | ||||
| export const handleLogin = data => { | export const handleLogin = data => { | ||||
| return dispatch => { | return dispatch => { | ||||
| @@ -152,6 +162,12 @@ export const SetupAxiosInterceptors = () => { | |||||
| // const { config, response: { status } } = error | // const { config, response: { status } } = error | ||||
| const status = error.response?.status | const status = error.response?.status | ||||
| const reqUrl = error.config?.url || '' | const reqUrl = error.config?.url || '' | ||||
| // Let login forms handle their own errors (wrong password, locked account, etc.) | |||||
| if (isPublicAuthRequest(reqUrl)) { | |||||
| return Promise.reject(error) | |||||
| } | |||||
| // iAM Smart registration: HKID already linked — caller must show /iamsmart/pleaseLogin (not refresh/logout) | // iAM Smart registration: HKID already linked — caller must show /iamsmart/pleaseLogin (not refresh/logout) | ||||
| if ( | if ( | ||||
| status === 401 && | status === 401 && | ||||
| @@ -160,11 +176,15 @@ export const SetupAxiosInterceptors = () => { | |||||
| ) { | ) { | ||||
| return Promise.reject(error) | return Promise.reject(error) | ||||
| } | } | ||||
| if (status === 401 && error.config?.url !== apiPath + REFRESH_TOKEN) { | |||||
| if ( | |||||
| status === 401 && | |||||
| !isPublicAuthRequest(reqUrl) && | |||||
| error.config?.url !== apiPath + REFRESH_TOKEN | |||||
| ) { | |||||
| // Make a request to refresh the access token | // Make a request to refresh the access token | ||||
| const refreshToken = localStorage.getItem('refreshToken'); | const refreshToken = localStorage.getItem('refreshToken'); | ||||
| if (isRefreshToken) { | if (isRefreshToken) { | ||||
| return; | |||||
| return Promise.reject(error); | |||||
| } | } | ||||
| isRefreshToken = true; | isRefreshToken = true; | ||||
| return axios | return axios | ||||
| @@ -217,7 +237,7 @@ export const SetupAxiosInterceptors = () => { | |||||
| // } | // } | ||||
| // } | // } | ||||
| if (localStorage.getItem("expiredAlertShown")) { | |||||
| if (localStorage.getItem("expiredAlertShown") && !isPublicAuthRequest(reqUrl)) { | |||||
| await dispatch(handleLogoutFunction()); | await dispatch(handleLogoutFunction()); | ||||
| await navigate('/login'); | await navigate('/login'); | ||||
| await window.location.reload(); | await window.location.reload(); | ||||
| @@ -40,29 +40,46 @@ export default class JwtService { | |||||
| // ** const { config, response: { status } } = error | // ** const { config, response: { status } } = error | ||||
| const {config, response} = error | const {config, response} = error | ||||
| const originalRequest = config | const originalRequest = config | ||||
| const reqUrl = config?.url || '' | |||||
| // ** if (status === 401) { | // ** if (status === 401) { | ||||
| if (response && response.status === 401) { | if (response && response.status === 401) { | ||||
| const isPublicAuthRequest = | |||||
| reqUrl.includes('/login') || | |||||
| reqUrl.includes('/ldap-login') || | |||||
| reqUrl.includes('/refresh-token') | |||||
| if (isPublicAuthRequest) { | |||||
| return Promise.reject(error) | |||||
| } | |||||
| if (!this.isAlreadyFetchingAccessToken) { | if (!this.isAlreadyFetchingAccessToken) { | ||||
| this.isAlreadyFetchingAccessToken = true | this.isAlreadyFetchingAccessToken = true | ||||
| this.refreshToken().then(r => { | |||||
| this.isAlreadyFetchingAccessToken = false | |||||
| // ** Update accessToken in localStorage | |||||
| this.setToken(r.data.accessToken) | |||||
| this.setRefreshToken(r.data.refreshToken) | |||||
| this.onAccessTokenFetched(r.data.accessToken) | |||||
| }) | |||||
| this.refreshToken() | |||||
| .then(r => { | |||||
| this.isAlreadyFetchingAccessToken = false | |||||
| // ** Update accessToken in localStorage | |||||
| this.setToken(r.data.accessToken) | |||||
| this.setRefreshToken(r.data.refreshToken) | |||||
| this.onAccessTokenFetched(r.data.accessToken) | |||||
| }) | |||||
| .catch(refreshError => { | |||||
| this.isAlreadyFetchingAccessToken = false | |||||
| this.onAccessTokenFetchFailed(refreshError) | |||||
| }) | |||||
| } | } | ||||
| const retryOriginalRequest = new Promise(resolve => { | |||||
| this.addSubscriber(accessToken => { | |||||
| // ** Make sure to assign accessToken according to your response. | |||||
| // ** Check: https://pixinvent.ticksy.com/ticket/2413870 | |||||
| // ** Change Authorization header | |||||
| originalRequest.headers.Authorization = `${this.jwtConfig.tokenType} ${accessToken}` | |||||
| resolve(this.axios(originalRequest)) | |||||
| }) | |||||
| const retryOriginalRequest = new Promise((resolve, reject) => { | |||||
| this.addSubscriber( | |||||
| accessToken => { | |||||
| // ** Make sure to assign accessToken according to your response. | |||||
| // ** Check: https://pixinvent.ticksy.com/ticket/2413870 | |||||
| // ** Change Authorization header | |||||
| originalRequest.headers.Authorization = `${this.jwtConfig.tokenType} ${accessToken}` | |||||
| resolve(axios(originalRequest)) | |||||
| }, | |||||
| reject | |||||
| ) | |||||
| }) | }) | ||||
| return retryOriginalRequest | return retryOriginalRequest | ||||
| } | } | ||||
| @@ -72,11 +89,17 @@ export default class JwtService { | |||||
| } | } | ||||
| onAccessTokenFetched(accessToken) { | onAccessTokenFetched(accessToken) { | ||||
| this.subscribers = this.subscribers.filter(callback => callback(accessToken)) | |||||
| this.subscribers.forEach(({ onSuccess }) => onSuccess(accessToken)) | |||||
| this.subscribers = [] | |||||
| } | |||||
| onAccessTokenFetchFailed(refreshError) { | |||||
| this.subscribers.forEach(({ onFailure }) => onFailure(refreshError)) | |||||
| this.subscribers = [] | |||||
| } | } | ||||
| addSubscriber(callback) { | |||||
| this.subscribers.push(callback) | |||||
| addSubscriber(onSuccess, onFailure) { | |||||
| this.subscribers.push({ onSuccess, onFailure }) | |||||
| } | } | ||||
| getToken() { | getToken() { | ||||
| @@ -30,7 +30,7 @@ const AuthFooter = () => { | |||||
| component="span" | component="span" | ||||
| sx={{ color: '#4A4A4A' }} | sx={{ color: '#4A4A4A' }} | ||||
| > | > | ||||
| 2024 © <FormattedMessage id="HKGLD" /> | |||||
| 2024-{new Date().getFullYear()} © <FormattedMessage id="HKGLD" /> | |||||
| </Typography> | </Typography> | ||||
| <Typography | <Typography | ||||
| variant="subtitle2" | variant="subtitle2" | ||||
| @@ -398,8 +398,8 @@ const Index = () => { | |||||
| <Grid container justifyContent="flex-start" alignItems="center" > | <Grid container justifyContent="flex-start" alignItems="center" > | ||||
| <center> | <center> | ||||
| <Grid item xs={12} md={12} > | <Grid item xs={12} md={12} > | ||||
| <Typography variant="h3" sx={{ ml: 8, mt: 4, mr: 8, textAlign: "center" }}> | |||||
| <br /><br /> | |||||
| <Typography component="span" variant="body1" sx={{ width: '80%', margin: 'auto', mt: 4, textAlign: "left", fontSize: '20px' }}> | |||||
| <FormattedMessage id="payAlert"/> | <FormattedMessage id="payAlert"/> | ||||
| <br /><br /> | <br /><br /> | ||||
| <img src={FpsIcon} width="80" height="80" alt="FPS"></img> | <img src={FpsIcon} width="80" height="80" alt="FPS"></img> | ||||
| @@ -408,8 +408,9 @@ const Index = () => { | |||||
| <br /> | <br /> | ||||
| {"HK$ " + currencyFormat(paymentData.amount)} | {"HK$ " + currencyFormat(paymentData.amount)} | ||||
| </Typography> | </Typography> | ||||
| <br /><br /> | |||||
| {isError ? | {isError ? | ||||
| <Typography variant="h3" color="error" sx={{ ml: 8, mt: 4, mr: 8, textAlign: "center" }}> | |||||
| <Typography component="span" variant="body1" color="error" sx={{ width: '80%', margin: 'auto', mt: 4, textAlign: "left", fontSize: '20px' }}> | |||||
| <FormattedMessage id="fpsPaymentErrorMsg"/> | <FormattedMessage id="fpsPaymentErrorMsg"/> | ||||
| </Typography> | </Typography> | ||||
| : | : | ||||
| @@ -426,7 +427,7 @@ const Index = () => { | |||||
| mobliePaymentPrd(); | mobliePaymentPrd(); | ||||
| }} | }} | ||||
| sx={{ m: 4 }} | sx={{ m: 4 }} | ||||
| >請選擇支付程式付款</Button> | |||||
| ><FormattedMessage id="fpsSelectPaymentApp"/></Button> | |||||
| : | : | ||||
| <> | <> | ||||
| <Button | <Button | ||||
| @@ -438,7 +439,7 @@ const Index = () => { | |||||
| mobliePayment(); | mobliePayment(); | ||||
| }} | }} | ||||
| sx={{ m: 4 }} | sx={{ m: 4 }} | ||||
| >請選擇支付程式付款-Testing</Button> | |||||
| ><FormattedMessage id="fpsSelectPaymentApp"/>-Testing</Button> | |||||
| <Button | <Button | ||||
| component="span" | component="span" | ||||
| variant="contained" | variant="contained" | ||||
| @@ -448,7 +449,7 @@ const Index = () => { | |||||
| mobliePaymentFps(); | mobliePaymentFps(); | ||||
| }} | }} | ||||
| sx={{ m: 4 }} | sx={{ m: 4 }} | ||||
| >請選擇支付程式付款-fps prefix</Button> | |||||
| ><FormattedMessage id="fpsSelectPaymentApp"/>-fps prefix</Button> | |||||
| </> | </> | ||||
| } | } | ||||
| </Typography> | </Typography> | ||||
| @@ -13,9 +13,10 @@ import { getPaymentMethodByCode} from "auth/utils"; | |||||
| import { | import { | ||||
| Checkbox, | Checkbox, | ||||
| Dialog, DialogTitle, DialogContent, DialogActions, | Dialog, DialogTitle, DialogContent, DialogActions, | ||||
| Button,Typography | |||||
| Button, Typography | |||||
| // MenuItem | // MenuItem | ||||
| } from '@mui/material'; | } from '@mui/material'; | ||||
| import LoadingButton from '@mui/lab/LoadingButton'; | |||||
| // ==============================|| EVENT TABLE ||============================== // | // ==============================|| EVENT TABLE ||============================== // | ||||
| export default function SearchPaymentTable({ searchCriteria, applyGridOnReady, applySearch}) { | export default function SearchPaymentTable({ searchCriteria, applyGridOnReady, applySearch}) { | ||||
| @@ -26,6 +27,7 @@ export default function SearchPaymentTable({ searchCriteria, applyGridOnReady, a | |||||
| const [bib, setBib] = React.useState(); | const [bib, setBib] = React.useState(); | ||||
| const [appNo, setAppNo] = React.useState(); | const [appNo, setAppNo] = React.useState(); | ||||
| const [refreshTrigger, setRefreshTrigger] = React.useState(0); | const [refreshTrigger, setRefreshTrigger] = React.useState(0); | ||||
| const [bibLoading, setBibLoading] = React.useState(false); | |||||
| const forceRefresh = () => { | const forceRefresh = () => { | ||||
| setRefreshTrigger(prev => prev + 1); | setRefreshTrigger(prev => prev + 1); | ||||
| @@ -55,23 +57,43 @@ export default function SearchPaymentTable({ searchCriteria, applyGridOnReady, a | |||||
| navigate('/paymentPage/details/' + params.row.id); | navigate('/paymentPage/details/' + params.row.id); | ||||
| }; | }; | ||||
| const finishBibRequest = () => { | |||||
| setBibLoading(false); | |||||
| }; | |||||
| const doBIB = () => { | const doBIB = () => { | ||||
| setIsPopUp(false); | |||||
| // console.log(refreshTrigger) | |||||
| if (bibLoading) { | |||||
| return; | |||||
| } | |||||
| setBibLoading(true); | |||||
| HttpUtils.post({ | HttpUtils.post({ | ||||
| url: PAYMENT_BIB + "/" + bibId, | url: PAYMENT_BIB + "/" + bibId, | ||||
| onSuccess: function () { | onSuccess: function () { | ||||
| forceRefresh() | |||||
| } | |||||
| setIsPopUp(false); | |||||
| forceRefresh(); | |||||
| finishBibRequest(); | |||||
| }, | |||||
| onFail: finishBibRequest, | |||||
| onError: finishBibRequest, | |||||
| }); | }); | ||||
| } | |||||
| }; | |||||
| const popUPBib = (id, bibFlag, appNo) => { | const popUPBib = (id, bibFlag, appNo) => { | ||||
| setBibId(id) | |||||
| setBib(bibFlag) | |||||
| setAppNo(appNo) | |||||
| if (bibLoading) { | |||||
| return; | |||||
| } | |||||
| setBibId(id); | |||||
| setBib(bibFlag); | |||||
| setAppNo(appNo); | |||||
| setIsPopUp(true); | setIsPopUp(true); | ||||
| } | |||||
| }; | |||||
| const closeBibDialog = () => { | |||||
| if (bibLoading) { | |||||
| return; | |||||
| } | |||||
| setIsPopUp(false); | |||||
| }; | |||||
| const columns = [ | const columns = [ | ||||
| { | { | ||||
| @@ -134,7 +156,13 @@ export default function SearchPaymentTable({ searchCriteria, applyGridOnReady, a | |||||
| headerName: 'BIB', | headerName: 'BIB', | ||||
| width: 150, | width: 150, | ||||
| renderCell: (params) => { | renderCell: (params) => { | ||||
| return <Checkbox checked={params.row.bib} onChange={() => {popUPBib(params.row.id, params.row.bib, params.row.appNos)}}/>; | |||||
| return ( | |||||
| <Checkbox | |||||
| checked={params.row.bib} | |||||
| disabled={bibLoading} | |||||
| onChange={() => popUPBib(params.row.id, params.row.bib, params.row.appNos)} | |||||
| /> | |||||
| ); | |||||
| } | } | ||||
| }, | }, | ||||
| { | { | ||||
| @@ -178,7 +206,7 @@ export default function SearchPaymentTable({ searchCriteria, applyGridOnReady, a | |||||
| <div> | <div> | ||||
| <Dialog | <Dialog | ||||
| open={isPopUp} | open={isPopUp} | ||||
| onClose={() => setIsPopUp(false)} | |||||
| onClose={closeBibDialog} | |||||
| PaperProps={{ | PaperProps={{ | ||||
| sx: { | sx: { | ||||
| minWidth: '40vw', | minWidth: '40vw', | ||||
| @@ -192,8 +220,12 @@ export default function SearchPaymentTable({ searchCriteria, applyGridOnReady, a | |||||
| <Typography variant="h5" style={{ padding: '16px' }}>{bib?"Cancel Bank-in-bank?":"Set "+appNo+" as Bank-in-bank?"}</Typography> | <Typography variant="h5" style={{ padding: '16px' }}>{bib?"Cancel Bank-in-bank?":"Set "+appNo+" as Bank-in-bank?"}</Typography> | ||||
| </DialogContent> | </DialogContent> | ||||
| <DialogActions> | <DialogActions> | ||||
| <Button onClick={() => setIsPopUp(false)}><Typography variant="h5">Cancel</Typography></Button> | |||||
| <Button onClick={() => doBIB()}><Typography variant="h5">Confirm</Typography></Button> | |||||
| <Button onClick={closeBibDialog} disabled={bibLoading}> | |||||
| <Typography variant="h5">Cancel</Typography> | |||||
| </Button> | |||||
| <LoadingButton onClick={doBIB} loading={bibLoading} disabled={bibLoading}> | |||||
| <Typography variant="h5">Confirm</Typography> | |||||
| </LoadingButton> | |||||
| </DialogActions> | </DialogActions> | ||||
| </Dialog> | </Dialog> | ||||
| </div> | </div> | ||||
| @@ -557,7 +557,7 @@ const PublicNoticeApplyForm = ({ loadedData, _selections, gazetteIssueList }) => | |||||
| </Grid> | </Grid> | ||||
| </Grid> | </Grid> | ||||
| <Grid item xs={12} md={12} lg={12} sx={{ mb: 3 }}> | <Grid item xs={12} md={12} lg={12} sx={{ mb: 3 }}> | ||||
| <Typography display="inline" variant="subtitle1" sx={{ color: 'primary.primary' }}> | |||||
| <Typography component="span" display="inline" variant="subtitle1" sx={{ color: 'primary.primary' }}> | |||||
| <FormattedMessage id="uploadApplicationFileRemark" /> | <FormattedMessage id="uploadApplicationFileRemark" /> | ||||
| </Typography> | </Typography> | ||||
| </Grid> | </Grid> | ||||
| @@ -573,7 +573,7 @@ const PublicNoticeApplyForm = ({ loadedData, _selections, gazetteIssueList }) => | |||||
| })} | })} | ||||
| </Grid> | </Grid> | ||||
| <Grid item xs={12} md={12} lg={12} sx={{ mb: 3 }}> | <Grid item xs={12} md={12} lg={12} sx={{ mb: 3 }}> | ||||
| <Typography display="inline" variant="subtitle1" sx={{ color: 'primary.primary' }}> | |||||
| <Typography component="span" sx={{ color: 'primary.primary' }}> | |||||
| <FormattedMessage id="noteOnClientRemark" /> | <FormattedMessage id="noteOnClientRemark" /> | ||||
| </Typography> | </Typography> | ||||
| </Grid> | </Grid> | ||||
| @@ -605,7 +605,7 @@ const PublicNoticeApplyForm = ({ loadedData, _selections, gazetteIssueList }) => | |||||
| <Typography variant="pnspsFormParagraphBold"> | <Typography variant="pnspsFormParagraphBold"> | ||||
| <span style={{ textAlign: 'justify', }} dangerouslySetInnerHTML={{ __html: intl.formatMessage({ id: "applyTickUnderStr0" }) }} /> | <span style={{ textAlign: 'justify', }} dangerouslySetInnerHTML={{ __html: intl.formatMessage({ id: "applyTickUnderStr0" }) }} /> | ||||
| </Typography> | </Typography> | ||||
| <Typography display="inline" variant="subtitle1" sx={{ color: 'primary.primary' }} > | |||||
| <Typography display="inline" variant="subtitle1" component="span" sx={{ color: 'primary.primary' }} > | |||||
| <ol style={{ textAlign: 'justify', }}> | <ol style={{ textAlign: 'justify', }}> | ||||
| <li dangerouslySetInnerHTML={{ __html: intl.formatMessage({ id: "applyTickUnderStr1" }) }} /> | <li dangerouslySetInnerHTML={{ __html: intl.formatMessage({ id: "applyTickUnderStr1" }) }} /> | ||||
| <li dangerouslySetInnerHTML={{ __html: intl.formatMessage({ id: "applyTickUnderStr2" }) }} /> | <li dangerouslySetInnerHTML={{ __html: intl.formatMessage({ id: "applyTickUnderStr2" }) }} /> | ||||
| @@ -8,6 +8,7 @@ import { | |||||
| Stack, | Stack, | ||||
| Dialog, DialogTitle, DialogContent, DialogActions, | Dialog, DialogTitle, DialogContent, DialogActions, | ||||
| } from '@mui/material'; | } from '@mui/material'; | ||||
| import LoadingButton from '@mui/lab/LoadingButton'; | |||||
| import { GET_PUBLIC_NOTICE_LIST, SET_PUBLIC_NOTICE_STATUS_PUBLISH_BULK } from "utils/ApiPathConst"; | import { GET_PUBLIC_NOTICE_LIST, SET_PUBLIC_NOTICE_STATUS_PUBLISH_BULK } 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"; | ||||
| @@ -29,6 +30,7 @@ export default function SearchPublicNoticeTable({ searchCriteria, applyGridOnRea | |||||
| const [selectedRowItems, setSelectedRowItems] = React.useState([]); | const [selectedRowItems, setSelectedRowItems] = React.useState([]); | ||||
| const [isConfirmPopUp, setIsConfirmPopUp] = React.useState(false); | const [isConfirmPopUp, setIsConfirmPopUp] = React.useState(false); | ||||
| const [isWarningPopUp, setIsWarningPopUp] = React.useState(false); | const [isWarningPopUp, setIsWarningPopUp] = React.useState(false); | ||||
| const [publishLoading, setPublishLoading] = React.useState(false); | |||||
| const [reload, setReload] = React.useState(new Date()); | const [reload, setReload] = React.useState(new Date()); | ||||
| @@ -39,6 +41,12 @@ export default function SearchPublicNoticeTable({ searchCriteria, applyGridOnRea | |||||
| set_searchCriteria(searchCriteria); | set_searchCriteria(searchCriteria); | ||||
| }, [searchCriteria]); | }, [searchCriteria]); | ||||
| React.useEffect(() => { | |||||
| if (!isConfirmPopUp) { | |||||
| setPublishLoading(false); | |||||
| } | |||||
| }, [isConfirmPopUp]); | |||||
| function genIssueNo(params) { | function genIssueNo(params) { | ||||
| const issueNo = params.row.issueYear + " Vol. " + FormatUtils.zeroPad(params.row.issueVolume, 3) + ", No. " + FormatUtils.zeroPad(params.row.issueNo, 2) + ", " + DateUtils.dateFormat(params.row.issueDate, "D MMM YYYY (ddd)") | const issueNo = params.row.issueYear + " Vol. " + FormatUtils.zeroPad(params.row.issueVolume, 3) + ", No. " + FormatUtils.zeroPad(params.row.issueNo, 2) + ", " + DateUtils.dateFormat(params.row.issueDate, "D MMM YYYY (ddd)") | ||||
| @@ -158,26 +166,36 @@ export default function SearchPublicNoticeTable({ searchCriteria, applyGridOnRea | |||||
| } | } | ||||
| const doPublish = () => { | const doPublish = () => { | ||||
| setIsConfirmPopUp(false); | |||||
| if (publishLoading) return; | |||||
| let appIdList = []; | let appIdList = []; | ||||
| const datas = rows?.filter((row) => | const datas = rows?.filter((row) => | ||||
| selectedRowItems.includes(row.id) | selectedRowItems.includes(row.id) | ||||
| ); | ); | ||||
| // console.log(datas) | |||||
| for (var i = 0; i < datas?.length; i++) { | for (var i = 0; i < datas?.length; i++) { | ||||
| appIdList.push(datas[i].id); | appIdList.push(datas[i].id); | ||||
| } | } | ||||
| if (appIdList.length < 1) { | if (appIdList.length < 1) { | ||||
| setIsConfirmPopUp(false); | |||||
| setIsWarningPopUp(true); | setIsWarningPopUp(true); | ||||
| } else { | } else { | ||||
| setPublishLoading(true); | |||||
| HttpUtils.post({ | HttpUtils.post({ | ||||
| url: SET_PUBLIC_NOTICE_STATUS_PUBLISH_BULK, | url: SET_PUBLIC_NOTICE_STATUS_PUBLISH_BULK, | ||||
| params: { | params: { | ||||
| ids: appIdList | ids: appIdList | ||||
| }, | }, | ||||
| onSuccess: () => { | onSuccess: () => { | ||||
| setPublishLoading(false); | |||||
| setIsConfirmPopUp(false); | |||||
| setReload(new Date()); | setReload(new Date()); | ||||
| notifyActionSuccess("Action Success!") | notifyActionSuccess("Action Success!") | ||||
| }, | |||||
| onFail: () => { | |||||
| setPublishLoading(false); | |||||
| }, | |||||
| onError: () => { | |||||
| setPublishLoading(false); | |||||
| } | } | ||||
| }); | }); | ||||
| } | } | ||||
| @@ -190,6 +208,7 @@ export default function SearchPublicNoticeTable({ searchCriteria, applyGridOnRea | |||||
| <Grid container direction="row" justifyContent="flex-start" alignItems="center" sx={{ p: 1 }} > | <Grid container direction="row" justifyContent="flex-start" alignItems="center" sx={{ p: 1 }} > | ||||
| <Button | <Button | ||||
| variant="contained" | variant="contained" | ||||
| disabled={publishLoading} | |||||
| onClick={() => setIsConfirmPopUp(true)} | onClick={() => setIsConfirmPopUp(true)} | ||||
| > | > | ||||
| Published | Published | ||||
| @@ -228,7 +247,10 @@ export default function SearchPublicNoticeTable({ searchCriteria, applyGridOnRea | |||||
| <div> | <div> | ||||
| <Dialog | <Dialog | ||||
| open={isConfirmPopUp} | open={isConfirmPopUp} | ||||
| onClose={() => setIsConfirmPopUp(false)} | |||||
| onClose={() => { | |||||
| if (publishLoading) return; | |||||
| setIsConfirmPopUp(false); | |||||
| }} | |||||
| PaperProps={{ | PaperProps={{ | ||||
| sx: { | sx: { | ||||
| minWidth: '40vw', | minWidth: '40vw', | ||||
| @@ -248,15 +270,24 @@ export default function SearchPublicNoticeTable({ searchCriteria, applyGridOnRea | |||||
| </Stack> | </Stack> | ||||
| </DialogContent> | </DialogContent> | ||||
| <DialogActions> | <DialogActions> | ||||
| <Button onClick={() => setIsConfirmPopUp(false)} aria-label={intl.formatMessage({ id: 'close' })}> | |||||
| <Button | |||||
| onClick={() => setIsConfirmPopUp(false)} | |||||
| disabled={publishLoading} | |||||
| aria-label={intl.formatMessage({ id: 'close' })} | |||||
| > | |||||
| <Typography variant="h5"> | <Typography variant="h5"> | ||||
| <FormattedMessage id="close" /> | <FormattedMessage id="close" /> | ||||
| </Typography></Button> | </Typography></Button> | ||||
| <Button onClick={() => doPublish()} aria-label={intl.formatMessage({ id: 'confirm' })}> | |||||
| <Typography variant="h5"> | |||||
| <LoadingButton | |||||
| onClick={() => doPublish()} | |||||
| loading={publishLoading} | |||||
| aria-label={intl.formatMessage({ id: 'confirm' })} | |||||
| > | |||||
| <Typography variant="h5" component="span"> | |||||
| <FormattedMessage id="confirm" /> | <FormattedMessage id="confirm" /> | ||||
| </Typography></Button> | |||||
| </Typography> | |||||
| </LoadingButton> | |||||
| </DialogActions> | </DialogActions> | ||||
| </Dialog> | </Dialog> | ||||
| </div> | </div> | ||||
| @@ -394,7 +394,7 @@ const Index = () => { | |||||
| </Grid> | </Grid> | ||||
| <Grid container spacing={2} alignItems="center"> | <Grid container spacing={2} alignItems="center"> | ||||
| <Grid item sx={{ mt: 1 }}> | <Grid item sx={{ mt: 1 }}> | ||||
| <Typography variant="subtitle1"> | |||||
| <Typography variant="subtitle1" component="p"> | |||||
| •<FormattedMessage id="pwRemark1" /><br /> | •<FormattedMessage id="pwRemark1" /><br /> | ||||
| •<FormattedMessage id="pwRemark2" /><br /> | •<FormattedMessage id="pwRemark2" /><br /> | ||||
| •<FormattedMessage id="pwRemark3" /><br /> | •<FormattedMessage id="pwRemark3" /><br /> | ||||
| @@ -154,7 +154,7 @@ const BusRegister = () => { | |||||
| </Stepper> | </Stepper> | ||||
| {allStepsCompleted() ? ( | {allStepsCompleted() ? ( | ||||
| <React.Fragment> | <React.Fragment> | ||||
| <Typography variant="h4" sx={{ mt: 2, mb: 1 }}> | |||||
| <Typography component="span" variant="h4" sx={{ mt: 2, mb: 1, display: 'block' }}> | |||||
| All steps completed - you're finished | All steps completed - you're finished | ||||
| </Typography> | </Typography> | ||||
| <Stack direction="row" sx={{ pb: 2 }}> | <Stack direction="row" sx={{ pb: 2 }}> | ||||
| @@ -185,7 +185,7 @@ const BusRegister = () => { | |||||
| sx={{ mr: 1 }} | sx={{ mr: 1 }} | ||||
| variant="h5" | variant="h5" | ||||
| > | > | ||||
| <Typography variant="h5"> | |||||
| <Typography component="span" variant="h5"> | |||||
| <FormattedMessage id="back"/> | <FormattedMessage id="back"/> | ||||
| </Typography> | </Typography> | ||||
| </Button> | </Button> | ||||
| @@ -197,7 +197,7 @@ const BusRegister = () => { | |||||
| sx={{ mr: 1 }} | sx={{ mr: 1 }} | ||||
| variant="h5" | variant="h5" | ||||
| > | > | ||||
| <Typography variant="h5"> | |||||
| <Typography component="span" variant="h5"> | |||||
| <FormattedMessage id="back"/> | <FormattedMessage id="back"/> | ||||
| </Typography> | </Typography> | ||||
| </Button> | </Button> | ||||
| @@ -216,7 +216,7 @@ const BusRegister = () => { | |||||
| {isNextBusy ? ( | {isNextBusy ? ( | ||||
| <CircularProgress size={22} color="inherit" aria-hidden /> | <CircularProgress size={22} color="inherit" aria-hidden /> | ||||
| ) : ( | ) : ( | ||||
| <Typography variant="h5" sx={{ color: 'inherit' }}> | |||||
| <Typography component="span" variant="h5" sx={{ color: 'inherit' }}> | |||||
| <FormattedMessage id="submit"/> | <FormattedMessage id="submit"/> | ||||
| </Typography> | </Typography> | ||||
| )} | )} | ||||
| @@ -225,7 +225,7 @@ const BusRegister = () => { | |||||
| ( | ( | ||||
| <Button variant="contained" color="inherit" | <Button variant="contained" color="inherit" | ||||
| disabled={true} sx={{ mr: 1, ...PRIMARY_CONTAINED_BUTTON_SX }}> | disabled={true} sx={{ mr: 1, ...PRIMARY_CONTAINED_BUTTON_SX }}> | ||||
| <Typography variant="h5" sx={{ color: 'inherit' }}> | |||||
| <Typography component="span" variant="h5" sx={{ color: 'inherit' }}> | |||||
| <FormattedMessage id="submit"/> | <FormattedMessage id="submit"/> | ||||
| </Typography> | </Typography> | ||||
| </Button> | </Button> | ||||
| @@ -233,7 +233,7 @@ const BusRegister = () => { | |||||
| ( | ( | ||||
| // <Button disabled={updateValid} variant="outlined" onClick={handleNext} sx={{ mr: 1 }}> | // <Button disabled={updateValid} variant="outlined" onClick={handleNext} sx={{ mr: 1 }}> | ||||
| <Button disabled={!updateValid || isNextBusy} variant="contained" onClick={handleNext} sx={{ mr: 1, ...PRIMARY_CONTAINED_BUTTON_SX }}> | <Button disabled={!updateValid || isNextBusy} variant="contained" onClick={handleNext} sx={{ mr: 1, ...PRIMARY_CONTAINED_BUTTON_SX }}> | ||||
| <Typography variant="h5" sx={{ color: 'inherit' }}> | |||||
| <Typography component="span" variant="h5" sx={{ color: 'inherit' }}> | |||||
| <FormattedMessage id="continue"/> | <FormattedMessage id="continue"/> | ||||
| </Typography> | </Typography> | ||||
| </Button> | </Button> | ||||
| @@ -161,12 +161,12 @@ const Register = () => { | |||||
| </Stepper> | </Stepper> | ||||
| {allStepsCompleted() ? ( | {allStepsCompleted() ? ( | ||||
| <React.Fragment> | <React.Fragment> | ||||
| <Typography variant="h4" sx={{ mt: 2, mb: 1 }}> | |||||
| <Typography component="span" variant="h4" sx={{ mt: 2, mb: 1, display: 'block' }}> | |||||
| All steps completed - you're finished | All steps completed - you're finished | ||||
| </Typography> | </Typography> | ||||
| <Stack direction="row" sx={{ pt: 2 }}> | <Stack direction="row" sx={{ pt: 2 }}> | ||||
| <Stack sx={{ flex: '1 1 auto' }} /> | <Stack sx={{ flex: '1 1 auto' }} /> | ||||
| <Button onClick={handleReset}><Typography variant="h5"> <FormattedMessage id="reset"/></Typography></Button> | |||||
| <Button onClick={handleReset}><Typography component="span" variant="h5"> <FormattedMessage id="reset"/></Typography></Button> | |||||
| </Stack> | </Stack> | ||||
| </React.Fragment> | </React.Fragment> | ||||
| ) : ( | ) : ( | ||||
| @@ -192,7 +192,7 @@ const Register = () => { | |||||
| onClick={handleBack} | onClick={handleBack} | ||||
| sx={{ mr: 1 }} | sx={{ mr: 1 }} | ||||
| > | > | ||||
| <Typography variant="h5"> | |||||
| <Typography component="span" variant="h5"> | |||||
| <FormattedMessage id="back"/> | <FormattedMessage id="back"/> | ||||
| </Typography> | </Typography> | ||||
| </Button> | </Button> | ||||
| @@ -203,7 +203,7 @@ const Register = () => { | |||||
| onClick={handleBack} | onClick={handleBack} | ||||
| sx={{ mr: 1 }} | sx={{ mr: 1 }} | ||||
| > | > | ||||
| <Typography variant="h5"> | |||||
| <Typography component="span" variant="h5"> | |||||
| <FormattedMessage id="back"/> | <FormattedMessage id="back"/> | ||||
| </Typography> | </Typography> | ||||
| </Button> | </Button> | ||||
| @@ -222,7 +222,7 @@ const Register = () => { | |||||
| {isNextBusy ? ( | {isNextBusy ? ( | ||||
| <CircularProgress size={22} color="inherit" aria-hidden /> | <CircularProgress size={22} color="inherit" aria-hidden /> | ||||
| ) : ( | ) : ( | ||||
| <Typography variant="h5" sx={{ color: 'inherit' }}> | |||||
| <Typography component="span" variant="h5" sx={{ color: 'inherit' }}> | |||||
| <FormattedMessage id="submit"/> | <FormattedMessage id="submit"/> | ||||
| </Typography> | </Typography> | ||||
| )} | )} | ||||
| @@ -231,7 +231,7 @@ const Register = () => { | |||||
| ( | ( | ||||
| <Button variant="contained" color="inherit" | <Button variant="contained" color="inherit" | ||||
| disabled={true} sx={{ mr: 1, ...PRIMARY_CONTAINED_BUTTON_SX }}> | disabled={true} sx={{ mr: 1, ...PRIMARY_CONTAINED_BUTTON_SX }}> | ||||
| <Typography variant="h5" sx={{ color: 'inherit' }}> | |||||
| <Typography component="span" variant="h5" sx={{ color: 'inherit' }}> | |||||
| <FormattedMessage id="submit"/> | <FormattedMessage id="submit"/> | ||||
| </Typography> | </Typography> | ||||
| </Button> | </Button> | ||||
| @@ -239,7 +239,7 @@ const Register = () => { | |||||
| ( | ( | ||||
| // <Button disabled={updateValid} variant="outlined" onClick={handleNext} sx={{ mr: 1 }}> | // <Button disabled={updateValid} variant="outlined" onClick={handleNext} sx={{ mr: 1 }}> | ||||
| <Button disabled={!updateValid || isNextBusy} variant="contained" onClick={handleNext} sx={{ mr: 1, ...PRIMARY_CONTAINED_BUTTON_SX }}> | <Button disabled={!updateValid || isNextBusy} variant="contained" onClick={handleNext} sx={{ mr: 1, ...PRIMARY_CONTAINED_BUTTON_SX }}> | ||||
| <Typography variant="h5" sx={{ color: 'inherit' }}> | |||||
| <Typography component="span" variant="h5" sx={{ color: 'inherit' }}> | |||||
| <FormattedMessage id="continue"/> | <FormattedMessage id="continue"/> | ||||
| </Typography> | </Typography> | ||||
| </Button> | </Button> | ||||
| @@ -160,12 +160,12 @@ const Register = () => { | |||||
| </Stepper> | </Stepper> | ||||
| {allStepsCompleted() ? ( | {allStepsCompleted() ? ( | ||||
| <React.Fragment> | <React.Fragment> | ||||
| <Typography variant="h4" sx={{ mt: 2, mb: 1 }}> | |||||
| <Typography component="span" variant="h4" sx={{ mt: 2, mb: 1, display: 'block' }}> | |||||
| All steps completed - you're finished | All steps completed - you're finished | ||||
| </Typography> | </Typography> | ||||
| <Stack direction="row" sx={{ pt: 2 }}> | <Stack direction="row" sx={{ pt: 2 }}> | ||||
| <Stack sx={{ flex: '1 1 auto' }} /> | <Stack sx={{ flex: '1 1 auto' }} /> | ||||
| <Button onClick={handleReset}><Typography variant="h5">Reset</Typography></Button> | |||||
| <Button onClick={handleReset}><Typography component="span" variant="h5">Reset</Typography></Button> | |||||
| </Stack> | </Stack> | ||||
| </React.Fragment> | </React.Fragment> | ||||
| ) : ( | ) : ( | ||||
| @@ -191,7 +191,7 @@ const Register = () => { | |||||
| sx={{ mr: 1 }} | sx={{ mr: 1 }} | ||||
| variant="h5" | variant="h5" | ||||
| > | > | ||||
| <Typography variant="h5"> | |||||
| <Typography component="span" variant="h5"> | |||||
| <FormattedMessage id="back"/> | <FormattedMessage id="back"/> | ||||
| </Typography> | </Typography> | ||||
| </Button> | </Button> | ||||
| @@ -203,7 +203,7 @@ const Register = () => { | |||||
| sx={{ mr: 1 }} | sx={{ mr: 1 }} | ||||
| variant="h5" | variant="h5" | ||||
| > | > | ||||
| <Typography variant="h5"> | |||||
| <Typography component="span" variant="h5"> | |||||
| <FormattedMessage id="back"/> | <FormattedMessage id="back"/> | ||||
| </Typography> | </Typography> | ||||
| </Button> | </Button> | ||||
| @@ -222,7 +222,7 @@ const Register = () => { | |||||
| {isNextBusy ? ( | {isNextBusy ? ( | ||||
| <CircularProgress size={22} color="inherit" aria-hidden /> | <CircularProgress size={22} color="inherit" aria-hidden /> | ||||
| ) : ( | ) : ( | ||||
| <Typography variant="h5" sx={{ color: 'inherit' }}> | |||||
| <Typography component="span" variant="h5" sx={{ color: 'inherit' }}> | |||||
| <FormattedMessage id="submit"/> | <FormattedMessage id="submit"/> | ||||
| </Typography> | </Typography> | ||||
| )} | )} | ||||
| @@ -231,7 +231,7 @@ const Register = () => { | |||||
| ( | ( | ||||
| <Button variant="contained" color="inherit" | <Button variant="contained" color="inherit" | ||||
| disabled={true} sx={{ mr: 1, ...PRIMARY_CONTAINED_BUTTON_SX }}> | disabled={true} sx={{ mr: 1, ...PRIMARY_CONTAINED_BUTTON_SX }}> | ||||
| <Typography variant="h5" sx={{ color: 'inherit' }}> | |||||
| <Typography component="span" variant="h5" sx={{ color: 'inherit' }}> | |||||
| <FormattedMessage id="submit"/> | <FormattedMessage id="submit"/> | ||||
| </Typography> | </Typography> | ||||
| </Button> | </Button> | ||||
| @@ -239,7 +239,7 @@ const Register = () => { | |||||
| ( | ( | ||||
| // <Button disabled={updateValid} variant="outlined" onClick={handleNext} sx={{ mr: 1 }}> | // <Button disabled={updateValid} variant="outlined" onClick={handleNext} sx={{ mr: 1 }}> | ||||
| <Button disabled={!updateValid || isNextBusy} variant="contained" onClick={handleNext} sx={{ mr: 1, ...PRIMARY_CONTAINED_BUTTON_SX }}> | <Button disabled={!updateValid || isNextBusy} variant="contained" onClick={handleNext} sx={{ mr: 1, ...PRIMARY_CONTAINED_BUTTON_SX }}> | ||||
| <Typography variant="h5" sx={{ color: 'inherit' }}> | |||||
| <Typography component="span" variant="h5" sx={{ color: 'inherit' }}> | |||||
| <FormattedMessage id="continue"/> | <FormattedMessage id="continue"/> | ||||
| </Typography> | </Typography> | ||||
| </Button> | </Button> | ||||
| @@ -40,7 +40,7 @@ import { EyeOutlined, EyeInvisibleOutlined } from '@ant-design/icons'; | |||||
| // import axios from "axios"; | // import axios from "axios"; | ||||
| import { useDispatch } from "react-redux"; | import { useDispatch } from "react-redux"; | ||||
| import { handleLogin } from "auth/index"; | |||||
| import { handleLogin, clearExpiredSessionAlert } from "auth/index"; | |||||
| import useJwt from "auth/jwt/useJwt"; | import useJwt from "auth/jwt/useJwt"; | ||||
| import { FormattedMessage, useIntl } from "react-intl"; | import { FormattedMessage, useIntl } from "react-intl"; | ||||
| import { SysContext } from "components/SysSettingProvider" | import { SysContext } from "components/SysSettingProvider" | ||||
| @@ -74,6 +74,10 @@ const AuthLoginCustom = () => { | |||||
| /** Blocks a second login before React re-renders (double-click / rapid taps). */ | /** Blocks a second login before React re-renders (double-click / rapid taps). */ | ||||
| const loginInFlightRef = useRef(false); | const loginInFlightRef = useRef(false); | ||||
| useEffect(() => { | |||||
| clearExpiredSessionAlert(); | |||||
| }, []); | |||||
| const handleMouseDownPassword = (event) => { | const handleMouseDownPassword = (event) => { | ||||
| event.preventDefault(); | event.preventDefault(); | ||||
| }; | }; | ||||
| @@ -84,6 +88,7 @@ const AuthLoginCustom = () => { | |||||
| return; | return; | ||||
| } | } | ||||
| loginInFlightRef.current = true; | loginInFlightRef.current = true; | ||||
| clearExpiredSessionAlert(); | |||||
| setOnLogin(true) | setOnLogin(true) | ||||
| useJwt | useJwt | ||||
| .login({ username: values.username, password: values.password }) | .login({ username: values.username, password: values.password }) | ||||
| @@ -739,7 +739,7 @@ const BusCustomFormWizard = (props) => { | |||||
| </Typography> | </Typography> | ||||
| </Button> | </Button> | ||||
| <div style={{ borderBottom: "3px solid #1A4399", width: "100%", margin_right: "15px" }}> | <div style={{ borderBottom: "3px solid #1A4399", width: "100%", margin_right: "15px" }}> | ||||
| <Typography display="inline" variant="h3"component="h1" sx={{ color: '#1A4399' }}> | |||||
| <Typography display="inline" variant="h3" component="h1" sx={{ color: '#1A4399' }}> | |||||
| <FormattedMessage id="becomeNewBusinessUser"/> | <FormattedMessage id="becomeNewBusinessUser"/> | ||||
| </Typography> | </Typography> | ||||
| </div> | </div> | ||||
| @@ -872,7 +872,7 @@ const BusCustomFormWizard = (props) => { | |||||
| <Box sx={{ bgcolor: level?.color, width: 85, height: 8, borderRadius: '7px' }} /> | <Box sx={{ bgcolor: level?.color, width: 85, height: 8, borderRadius: '7px' }} /> | ||||
| </Grid> | </Grid> | ||||
| <Grid item> | <Grid item> | ||||
| <Typography variant="subtitle1"> | |||||
| <Typography component="span" variant="subtitle1" sx={{ display: 'block' }}> | |||||
| <FormattedMessage id={level ? level?.label : "pwWeak" }/> | <FormattedMessage id={level ? level?.label : "pwWeak" }/> | ||||
| </Typography> | </Typography> | ||||
| </Grid> | </Grid> | ||||
| @@ -939,7 +939,7 @@ const BusCustomFormWizard = (props) => { | |||||
| </Stack> | </Stack> | ||||
| <Grid container spacing={2} alignItems="center"> | <Grid container spacing={2} alignItems="center"> | ||||
| <Grid item sx={{mt:1}}> | <Grid item sx={{mt:1}}> | ||||
| <Typography variant="subtitle1"> | |||||
| <Typography component="span" variant="subtitle1" sx={{ display: 'block' }}> | |||||
| •<FormattedMessage id="pwRemark1"/><br /> | •<FormattedMessage id="pwRemark1"/><br /> | ||||
| •<FormattedMessage id="pwRemark2"/><br /> | •<FormattedMessage id="pwRemark2"/><br /> | ||||
| •<FormattedMessage id="pwRemark3"/><br /> | •<FormattedMessage id="pwRemark3"/><br /> | ||||
| @@ -963,7 +963,7 @@ const BusCustomFormWizard = (props) => { | |||||
| </Grid> | </Grid> | ||||
| <Grid item xs={12} md={12}> | <Grid item xs={12} md={12}> | ||||
| <Typography variant="subtitle1"> | |||||
| <Typography component="span" variant="subtitle1" sx={{ display: 'block' }}> | |||||
| •<FormattedMessage id="pleaseEnterOrgOrCompName"/> | •<FormattedMessage id="pleaseEnterOrgOrCompName"/> | ||||
| </Typography> | </Typography> | ||||
| </Grid> | </Grid> | ||||
| @@ -1617,7 +1617,7 @@ const BusCustomFormWizard = (props) => { | |||||
| <Grid item xs={12} md={12}> | <Grid item xs={12} md={12}> | ||||
| <Grid container> | <Grid container> | ||||
| <Grid item xs={12} md={12}> | <Grid item xs={12} md={12}> | ||||
| <Typography variant="h6" height="100%" sx={{ textAlign: "left", /*overflow: "scroll",*/ borderRadius: "inherit", borderStyle: "solid", borderWidth: "1px", borderColor: "#0c489e" }}> | |||||
| <Typography component="span" variant="h6" height="100%" sx={{ textAlign: "left", /*overflow: "scroll",*/ borderRadius: "inherit", borderStyle: "solid", borderWidth: "1px", borderColor: "#0c489e", display: "block" }}> | |||||
| <div style={{padding: 12}} dangerouslySetInnerHTML={{__html: intl.formatMessage({id: "termsAndCon"})}} /> | <div style={{padding: 12}} dangerouslySetInnerHTML={{__html: intl.formatMessage({id: "termsAndCon"})}} /> | ||||
| </Typography> | </Typography> | ||||
| </Grid> | </Grid> | ||||
| @@ -1744,7 +1744,7 @@ const BusCustomFormWizard = (props) => { | |||||
| <Grid item xs={12}> | <Grid item xs={12}> | ||||
| <Stack direction="column" justifyContent="space-between" alignItems="baseline" sx={{ mb: { xs: -0.5, sm: 0.5 } }}> | <Stack direction="column" justifyContent="space-between" alignItems="baseline" sx={{ mb: { xs: -0.5, sm: 0.5 } }}> | ||||
| <div style={{ borderBottom: "3px solid #1A4399", width: "100%", margin_right: "15px" }}> | <div style={{ borderBottom: "3px solid #1A4399", width: "100%", margin_right: "15px" }}> | ||||
| <Typography display="inline" variant="h3"component="h1" sx={{ color: '#1A4399' }}> | |||||
| <Typography display="inline" variant="h3" component="h1" sx={{ color: '#1A4399' }}> | |||||
| <FormattedMessage id="becomeNewBusinessUser"/> | <FormattedMessage id="becomeNewBusinessUser"/> | ||||
| </Typography> | </Typography> | ||||
| </div> | </div> | ||||
| @@ -1090,7 +1090,7 @@ const CustomFormWizard = (props) => { | |||||
| <Box sx={{ bgcolor: level?.color, width: 85, height: 8, borderRadius: '7px' }} /> | <Box sx={{ bgcolor: level?.color, width: 85, height: 8, borderRadius: '7px' }} /> | ||||
| </Grid> | </Grid> | ||||
| <Grid item> | <Grid item> | ||||
| <Typography variant="subtitle1"> | |||||
| <Typography component="span" variant="subtitle1" sx={{ display: 'block' }}> | |||||
| <FormattedMessage id={level ? level?.label : "pwWeak"} /> | <FormattedMessage id={level ? level?.label : "pwWeak"} /> | ||||
| </Typography> | </Typography> | ||||
| </Grid> | </Grid> | ||||
| @@ -1157,7 +1157,7 @@ const CustomFormWizard = (props) => { | |||||
| </Stack> | </Stack> | ||||
| <Grid container spacing={2} alignItems="center"> | <Grid container spacing={2} alignItems="center"> | ||||
| <Grid item sx={{ mt: 1 }}> | <Grid item sx={{ mt: 1 }}> | ||||
| <Typography variant="subtitle1"> | |||||
| <Typography component="span" variant="subtitle1" sx={{ display: 'block' }}> | |||||
| •<FormattedMessage id="pwRemark1" /><br /> | •<FormattedMessage id="pwRemark1" /><br /> | ||||
| •<FormattedMessage id="pwRemark2" /><br /> | •<FormattedMessage id="pwRemark2" /><br /> | ||||
| •<FormattedMessage id="pwRemark3" /><br /> | •<FormattedMessage id="pwRemark3" /><br /> | ||||
| @@ -1392,7 +1392,7 @@ const CustomFormWizard = (props) => { | |||||
| </Grid> | </Grid> | ||||
| </Grid> | </Grid> | ||||
| <Grid item xs={12} md={12}> | <Grid item xs={12} md={12}> | ||||
| <Typography variant="subtitle1"> | |||||
| <Typography component="span" variant="subtitle1" sx={{ display: 'block' }}> | |||||
| <FormattedMessage id="registerNameLabel" /> | <FormattedMessage id="registerNameLabel" /> | ||||
| </Typography> | </Typography> | ||||
| </Grid> | </Grid> | ||||
| @@ -1881,10 +1881,10 @@ const CustomFormWizard = (props) => { | |||||
| <Typography display="inline" variant="h4" component="h2" sx={{ color: 'primary.primary' }}> | <Typography display="inline" variant="h4" component="h2" sx={{ color: 'primary.primary' }}> | ||||
| <FormattedMessage id="userIdDoc" /> | <FormattedMessage id="userIdDoc" /> | ||||
| <span style={{ color: '#B00020' }}>*</span></Typography> | <span style={{ color: '#B00020' }}>*</span></Typography> | ||||
| <Typography display="inline" variant="subtitle1" sx={{ color: 'primary.primary' }}> | |||||
| <Typography component="span" variant="subtitle1" sx={{ color: 'primary.primary', display: 'block' }}> | |||||
| <FormattedMessage id="pleaseUploadIdDoc" /> | <FormattedMessage id="pleaseUploadIdDoc" /> | ||||
| </Typography> | </Typography> | ||||
| <Typography display="inline" variant="subtitle1" sx={{ color: 'primary.primary' }}> | |||||
| <Typography component="span" variant="subtitle1" sx={{ color: 'primary.primary', display: 'block' }}> | |||||
| <FormattedMessage id="pleaseUploadIdDocSubTitle" /> | <FormattedMessage id="pleaseUploadIdDocSubTitle" /> | ||||
| </Typography> | </Typography> | ||||
| <Stack mt={1} direction="row" justifyContent="flex-start" alignItems="center" spacing={2}> | <Stack mt={1} direction="row" justifyContent="flex-start" alignItems="center" spacing={2}> | ||||
| @@ -1945,7 +1945,7 @@ const CustomFormWizard = (props) => { | |||||
| <Grid item xs={12} md={12}> | <Grid item xs={12} md={12}> | ||||
| <Grid container> | <Grid container> | ||||
| <Grid item xs={12} md={12}> | <Grid item xs={12} md={12}> | ||||
| <Typography height="100%" sx={{ textAlign: "left", /*overflow: "scroll",*/ borderRadius: "inherit", borderStyle: "solid", borderWidth: "1px", borderColor: "#0c489e" }}> | |||||
| <Typography height="100%" component="span" sx={{ textAlign: "left", display: "block", /*overflow: "scroll",*/ borderRadius: "inherit", borderStyle: "solid", borderWidth: "1px", borderColor: "#0c489e" }}> | |||||
| <div style={{padding: 12}} dangerouslySetInnerHTML={{__html: intl.formatMessage({id: "termsAndCon"})}} /> | <div style={{padding: 12}} dangerouslySetInnerHTML={{__html: intl.formatMessage({id: "termsAndCon"})}} /> | ||||
| </Typography> | </Typography> | ||||
| </Grid> | </Grid> | ||||
| @@ -1053,7 +1053,7 @@ const CustomFormWizard = (props) => { | |||||
| <Grid item xs={12} md={12}> | <Grid item xs={12} md={12}> | ||||
| <Grid container> | <Grid container> | ||||
| <Grid item xs={12} md={12}> | <Grid item xs={12} md={12}> | ||||
| <Typography sx={{ textAlign: "left", borderRadius: "inherit", borderStyle: "solid", borderWidth: "1px", borderColor: "#0c489e" }}> | |||||
| <Typography component="span" sx={{ textAlign: "left", display: "block", borderRadius: "inherit", borderStyle: "solid", borderWidth: "1px", borderColor: "#0c489e" }}> | |||||
| <div style={{ padding: 12 }} dangerouslySetInnerHTML={{ __html: intl.formatMessage({ id: "termsAndCon" }) }} /> | <div style={{ padding: 12 }} dangerouslySetInnerHTML={{ __html: intl.formatMessage({ id: "termsAndCon" }) }} /> | ||||
| </Typography> | </Typography> | ||||
| @@ -1192,7 +1192,7 @@ const CustomFormWizard = (props) => { | |||||
| </Grid> | </Grid> | ||||
| <Grid item xs={12} md={12} > | <Grid item xs={12} md={12} > | ||||
| <Stack direction="row"> | <Stack direction="row"> | ||||
| <Typography variant="h5" component="h2" color={theme.palette.grey[600]} sx={{ mr: 1 }}> | |||||
| <Typography variant="h5" component="span" color={theme.palette.grey[600]} sx={{ mr: 1 }}> | |||||
| <FormattedMessage id="userIdDoc" /> | <FormattedMessage id="userIdDoc" /> | ||||
| </Typography> | </Typography> | ||||
| <Typography variant="h5" component="span" name="preview-idDocType-1"> | <Typography variant="h5" component="span" name="preview-idDocType-1"> | ||||
| @@ -34,7 +34,7 @@ export default function PreviewUploadFileTable({ recordList, }) { | |||||
| function NoRowsOverlay() { | function NoRowsOverlay() { | ||||
| return ( | return ( | ||||
| <Stack height="100%" alignItems="center" justifyContent="center"> | <Stack height="100%" alignItems="center" justifyContent="center"> | ||||
| <Typography variant="h6"> | |||||
| <Typography component="span" variant="h6"> | |||||
| <FormattedMessage id="noFile"/> | <FormattedMessage id="noFile"/> | ||||
| </Typography> | </Typography> | ||||
| {/* <pre>(rows={[]})</pre> */} | {/* <pre>(rows={[]})</pre> */} | ||||
| @@ -48,7 +48,7 @@ export default function UploadFileTable({ recordList, setUpdateRows, }) { | |||||
| justifyContent: 'center', | justifyContent: 'center', | ||||
| }} | }} | ||||
| > | > | ||||
| <Typography variant="h6"> | |||||
| <Typography component="span" variant="h6"> | |||||
| <FormattedMessage id="noFile"/> | <FormattedMessage id="noFile"/> | ||||
| </Typography> | </Typography> | ||||
| </Box> | </Box> | ||||
| @@ -20,7 +20,7 @@ export const PNSPS_BUTTON_THEME = createTheme({ | |||||
| }, | }, | ||||
| save:{ | save:{ | ||||
| main: '#448DF2', | |||||
| main: '#0c489e', | |||||
| contrastText: '#FFFFFF', | contrastText: '#FFFFFF', | ||||
| }, | }, | ||||
| export:{ | export:{ | ||||
| @@ -44,7 +44,7 @@ export const PNSPS_BUTTON_THEME = createTheme({ | |||||
| contrastText: '#FFFFFF', | contrastText: '#FFFFFF', | ||||
| }, | }, | ||||
| success:{ | success:{ | ||||
| main: '#448DF2', | |||||
| main: '#0c489e', | |||||
| contrastText: '#FFFFFF', | contrastText: '#FFFFFF', | ||||
| }, | }, | ||||
| green:{ | green:{ | ||||
| @@ -194,7 +194,7 @@ export const PNSPS_LONG_BUTTON_THEME = createTheme({ | |||||
| }, | }, | ||||
| save:{ | save:{ | ||||
| main: '#448DF2', | |||||
| main: '#0c489e', | |||||
| contrastText: '#FFFFFF', | contrastText: '#FFFFFF', | ||||
| }, | }, | ||||
| export:{ | export:{ | ||||
| @@ -28,7 +28,7 @@ export const piechartColor2 = [ | |||||
| ]; | ]; | ||||
| export const cardBorderColor = [ | export const cardBorderColor = [ | ||||
| '#efb142', '#4bb641', '#448df2', '#e03c04' | |||||
| '#efb142', '#4bb641', '#0c489e', '#e03c04' | |||||
| ]; | ]; | ||||
| export const chartLineColor = [ | export const chartLineColor = [ | ||||
| @@ -39,7 +39,7 @@ export const GENERAL_RED_COLOR = '#e03c04'; | |||||
| export const TABLE_HEADER_TEXT_COLOR = "#1565C0"; | export const TABLE_HEADER_TEXT_COLOR = "#1565C0"; | ||||
| export const GENERAL_INFO_COLOR = '#448df2'; | |||||
| export const GENERAL_INFO_COLOR = '#0c489e'; | |||||
| export const GENERAL_SETTING_COLOR = '#666666'; | export const GENERAL_SETTING_COLOR = '#666666'; | ||||
| @@ -24,7 +24,7 @@ export default function Checkbox(theme) { | |||||
| color: CONTAINED_PRIMARY_BLUE | color: CONTAINED_PRIMARY_BLUE | ||||
| }, | }, | ||||
| '&.Mui-focusVisible': { | '&.Mui-focusVisible': { | ||||
| boxShadow: '0 0 0 3px rgba(25, 118, 210, 0.35)' | |||||
| boxShadow: `0 0 0 3px ${CONTAINED_PRIMARY_BLUE}` | |||||
| }, | }, | ||||
| svg: { | svg: { | ||||
| width: checkboxSize, | width: checkboxSize, | ||||
| @@ -21,7 +21,7 @@ export default function Radio() { | |||||
| color: CONTAINED_PRIMARY_BLUE | color: CONTAINED_PRIMARY_BLUE | ||||
| }, | }, | ||||
| '&.Mui-focusVisible': { | '&.Mui-focusVisible': { | ||||
| boxShadow: '0 0 0 3px rgba(25, 118, 210, 0.35)' | |||||
| boxShadow: `0 0 0 3px ${CONTAINED_PRIMARY_BLUE}` | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -310,7 +310,7 @@ export const PNSPS_THEME = createTheme({ | |||||
| /* Focus-visible */ | /* Focus-visible */ | ||||
| '&.Mui-focusVisible': { | '&.Mui-focusVisible': { | ||||
| boxShadow: '0 0 0 3px rgba(25, 118, 210, 0.35)', | |||||
| boxShadow: `0 0 0 3px ${CONTAINED_PRIMARY_BLUE}`, | |||||
| }, | }, | ||||
| }, | }, | ||||
| }, | }, | ||||
| @@ -336,7 +336,7 @@ export const PNSPS_THEME = createTheme({ | |||||
| }, | }, | ||||
| '&.Mui-focusVisible': { | '&.Mui-focusVisible': { | ||||
| boxShadow: '0 0 0 3px rgba(25, 118, 210, 0.35)', | |||||
| boxShadow: `0 0 0 3px ${CONTAINED_PRIMARY_BLUE}`, | |||||
| }, | }, | ||||
| }, | }, | ||||
| }, | }, | ||||
| @@ -505,7 +505,7 @@ export const PNSPS_THEME = createTheme({ | |||||
| // ✅ Keyboard TAB focus (visible & accessible) | // ✅ Keyboard TAB focus (visible & accessible) | ||||
| '&.Mui-focusVisible': { | '&.Mui-focusVisible': { | ||||
| outline: '3px solid rgba(21, 101, 192, 0.55)', | |||||
| outline: `3px solid ${CONTAINED_PRIMARY_BLUE}`, | |||||
| outlineOffset: '3px', | outlineOffset: '3px', | ||||
| borderRadius: '6px', | borderRadius: '6px', | ||||
| }, | }, | ||||
| @@ -423,7 +423,7 @@ | |||||
| "payMethod": "Payment methods", | "payMethod": "Payment methods", | ||||
| "epayMethod": " e-Payment Method", | "epayMethod": " e-Payment Method", | ||||
| "selectPaymentMethod": "Please select a payment method", | "selectPaymentMethod": "Please select a payment method", | ||||
| "selectPaymentMethodBtn": "Select payment method", | |||||
| "selectPaymentMethodBtn": "Pay Method", | |||||
| "payReceipt": "Payment Receipt", | "payReceipt": "Payment Receipt", | ||||
| "contactPerson": "Contact Person", | "contactPerson": "Contact Person", | ||||
| "requireContactPerson": "Please enter contact person", | "requireContactPerson": "Please enter contact person", | ||||
| @@ -511,8 +511,9 @@ | |||||
| "fpsQrcodeTitle3":"Please complete the payment process within the specified time", | "fpsQrcodeTitle3":"Please complete the payment process within the specified time", | ||||
| "fpsQrcodeTitle4":"Remaining time:", | "fpsQrcodeTitle4":"Remaining time:", | ||||
| "fpsQrcodeTitle5":"s", | "fpsQrcodeTitle5":"s", | ||||
| "fpsQrcodeExpired":"QR code has expired.", | |||||
| "fpsPaymentErrorMsg":"An error occurred while loading the payment QR code. Please do not refresh/reload this page manually during the payment. If the payment was not made successfully, please click 'Cancel payment' button and make the payment again. Sorry for the inconvenience caused.", | |||||
| "fpsQrcodeExpired":"QR code has expired.", | |||||
| "fpsPaymentErrorMsg":"An error occurred while loading the payment QR code. Please do not refresh/reload this page manually during the payment. If the payment was not made successfully, please click 'Cancel payment' button and make the payment again. Sorry for the inconvenience caused.", | |||||
| "fpsSelectPaymentApp":"Please Select Bank App", | |||||
| "payDnRemark": "Payment proof (e.g. ATM receipt, internet banking record) to be sent to [email protected] by {date} 12:30 p.m.", | "payDnRemark": "Payment proof (e.g. ATM receipt, internet banking record) to be sent to [email protected] by {date} 12:30 p.m.", | ||||
| "payNPGORemark" :"Payment to be completed at NPGO Collection Office by {date} 12:30 p.m.", | "payNPGORemark" :"Payment to be completed at NPGO Collection Office by {date} 12:30 p.m.", | ||||
| @@ -89,8 +89,9 @@ | |||||
| "fpsQrcodeTitle3":"请在规定时间内完成付款流程", | "fpsQrcodeTitle3":"请在规定时间内完成付款流程", | ||||
| "fpsQrcodeTitle4":"剩余时间:", | "fpsQrcodeTitle4":"剩余时间:", | ||||
| "fpsQrcodeTitle5":"秒", | "fpsQrcodeTitle5":"秒", | ||||
| "fpsQrcodeExpired":"二维码已过期", | |||||
| "fpsPaymentErrorMsg":"载入支付二维码时发生错误。请勿在付款过程中更新此页面。如果付款未完成,请点击「取消支付」按钮并重新支付。由此造成的不便,敬请谅解。", | |||||
| "fpsQrcodeExpired":"二维码已过期", | |||||
| "fpsPaymentErrorMsg":"载入支付二维码时发生错误。请勿在付款过程中更新此页面。如果付款未完成,请点击「取消支付」按钮并重新支付。由此造成的不便,敬请谅解。", | |||||
| "fpsSelectPaymentApp":"请选择付款支付程序", | |||||
| "payDnRemark": "在{date}下午12时30分前将付款证明(例如银行入数纸或网上银行付款记录)电邮至 [email protected]", | "payDnRemark": "在{date}下午12时30分前将付款证明(例如银行入数纸或网上银行付款记录)电邮至 [email protected]", | ||||
| "payNPGORemark" :"在{date}下午12时30分前到北角政府合署收款处完成付款", | "payNPGORemark" :"在{date}下午12时30分前到北角政府合署收款处完成付款", | ||||
| @@ -89,8 +89,9 @@ | |||||
| "fpsQrcodeTitle3":"請在規定時間內完成付款流程", | "fpsQrcodeTitle3":"請在規定時間內完成付款流程", | ||||
| "fpsQrcodeTitle4":"剩餘時間:", | "fpsQrcodeTitle4":"剩餘時間:", | ||||
| "fpsQrcodeTitle5":"秒", | "fpsQrcodeTitle5":"秒", | ||||
| "fpsQrcodeExpired":"二維碼已過期", | |||||
| "fpsPaymentErrorMsg":"載入支付二維碼時發生錯誤。請勿在付款過程中更新此頁面。如果付款未完成,請點擊「取消支付」按鈕並重新支付。由此造成的不便,敬請諒解。", | |||||
| "fpsQrcodeExpired":"二維碼已過期", | |||||
| "fpsPaymentErrorMsg":"載入支付二維碼時發生錯誤。請勿在付款過程中更新此頁面。如果付款未完成,請點擊「取消支付」按鈕並重新支付。由此造成的不便,敬請諒解。", | |||||
| "fpsSelectPaymentApp":"請選擇付款支付程式", | |||||
| "payDnRemark": "在{date}下午12時30分前將付款證明(例如銀行入數紙或網上銀行付款記錄)電郵至 [email protected]", | "payDnRemark": "在{date}下午12時30分前將付款證明(例如銀行入數紙或網上銀行付款記錄)電郵至 [email protected]", | ||||
| "payNPGORemark" :"在{date}下午12時30分前到北角政府合署收款處完成付款", | "payNPGORemark" :"在{date}下午12時30分前到北角政府合署收款處完成付款", | ||||