| 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"> | |||
| <head> | |||
| <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="16x16" href="%PUBLIC_URL%/favicon-16x16.png"> | |||
| <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). */ | |||
| /* All @fontsource v5 packages already use font-display: swap by default. */ | |||
| /* 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 */ | |||
| @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) ===== */ | |||
| .MuiDataGrid-columnHeader: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 */ | |||
| @@ -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) | |||
| 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 | |||
| export const handleLogin = data => { | |||
| return dispatch => { | |||
| @@ -152,6 +162,12 @@ export const SetupAxiosInterceptors = () => { | |||
| // const { config, response: { status } } = error | |||
| const status = error.response?.status | |||
| 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) | |||
| if ( | |||
| status === 401 && | |||
| @@ -160,11 +176,15 @@ export const SetupAxiosInterceptors = () => { | |||
| ) { | |||
| 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 | |||
| const refreshToken = localStorage.getItem('refreshToken'); | |||
| if (isRefreshToken) { | |||
| return; | |||
| return Promise.reject(error); | |||
| } | |||
| isRefreshToken = true; | |||
| return axios | |||
| @@ -217,7 +237,7 @@ export const SetupAxiosInterceptors = () => { | |||
| // } | |||
| // } | |||
| if (localStorage.getItem("expiredAlertShown")) { | |||
| if (localStorage.getItem("expiredAlertShown") && !isPublicAuthRequest(reqUrl)) { | |||
| await dispatch(handleLogoutFunction()); | |||
| await navigate('/login'); | |||
| await window.location.reload(); | |||
| @@ -40,29 +40,46 @@ export default class JwtService { | |||
| // ** const { config, response: { status } } = error | |||
| const {config, response} = error | |||
| const originalRequest = config | |||
| const reqUrl = config?.url || '' | |||
| // ** if (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) { | |||
| 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 | |||
| } | |||
| @@ -72,11 +89,17 @@ export default class JwtService { | |||
| } | |||
| 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() { | |||
| @@ -30,7 +30,7 @@ const AuthFooter = () => { | |||
| component="span" | |||
| sx={{ color: '#4A4A4A' }} | |||
| > | |||
| 2024 © <FormattedMessage id="HKGLD" /> | |||
| 2024-{new Date().getFullYear()} © <FormattedMessage id="HKGLD" /> | |||
| </Typography> | |||
| <Typography | |||
| variant="subtitle2" | |||
| @@ -398,8 +398,8 @@ const Index = () => { | |||
| <Grid container justifyContent="flex-start" alignItems="center" > | |||
| <center> | |||
| <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"/> | |||
| <br /><br /> | |||
| <img src={FpsIcon} width="80" height="80" alt="FPS"></img> | |||
| @@ -408,8 +408,9 @@ const Index = () => { | |||
| <br /> | |||
| {"HK$ " + currencyFormat(paymentData.amount)} | |||
| </Typography> | |||
| <br /><br /> | |||
| {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"/> | |||
| </Typography> | |||
| : | |||
| @@ -426,7 +427,7 @@ const Index = () => { | |||
| mobliePaymentPrd(); | |||
| }} | |||
| sx={{ m: 4 }} | |||
| >請選擇支付程式付款</Button> | |||
| ><FormattedMessage id="fpsSelectPaymentApp"/></Button> | |||
| : | |||
| <> | |||
| <Button | |||
| @@ -438,7 +439,7 @@ const Index = () => { | |||
| mobliePayment(); | |||
| }} | |||
| sx={{ m: 4 }} | |||
| >請選擇支付程式付款-Testing</Button> | |||
| ><FormattedMessage id="fpsSelectPaymentApp"/>-Testing</Button> | |||
| <Button | |||
| component="span" | |||
| variant="contained" | |||
| @@ -448,7 +449,7 @@ const Index = () => { | |||
| mobliePaymentFps(); | |||
| }} | |||
| sx={{ m: 4 }} | |||
| >請選擇支付程式付款-fps prefix</Button> | |||
| ><FormattedMessage id="fpsSelectPaymentApp"/>-fps prefix</Button> | |||
| </> | |||
| } | |||
| </Typography> | |||
| @@ -13,9 +13,10 @@ import { getPaymentMethodByCode} from "auth/utils"; | |||
| import { | |||
| Checkbox, | |||
| Dialog, DialogTitle, DialogContent, DialogActions, | |||
| Button,Typography | |||
| Button, Typography | |||
| // MenuItem | |||
| } from '@mui/material'; | |||
| import LoadingButton from '@mui/lab/LoadingButton'; | |||
| // ==============================|| EVENT TABLE ||============================== // | |||
| export default function SearchPaymentTable({ searchCriteria, applyGridOnReady, applySearch}) { | |||
| @@ -26,6 +27,7 @@ export default function SearchPaymentTable({ searchCriteria, applyGridOnReady, a | |||
| const [bib, setBib] = React.useState(); | |||
| const [appNo, setAppNo] = React.useState(); | |||
| const [refreshTrigger, setRefreshTrigger] = React.useState(0); | |||
| const [bibLoading, setBibLoading] = React.useState(false); | |||
| const forceRefresh = () => { | |||
| setRefreshTrigger(prev => prev + 1); | |||
| @@ -55,23 +57,43 @@ export default function SearchPaymentTable({ searchCriteria, applyGridOnReady, a | |||
| navigate('/paymentPage/details/' + params.row.id); | |||
| }; | |||
| const finishBibRequest = () => { | |||
| setBibLoading(false); | |||
| }; | |||
| const doBIB = () => { | |||
| setIsPopUp(false); | |||
| // console.log(refreshTrigger) | |||
| if (bibLoading) { | |||
| return; | |||
| } | |||
| setBibLoading(true); | |||
| HttpUtils.post({ | |||
| url: PAYMENT_BIB + "/" + bibId, | |||
| onSuccess: function () { | |||
| forceRefresh() | |||
| } | |||
| setIsPopUp(false); | |||
| forceRefresh(); | |||
| finishBibRequest(); | |||
| }, | |||
| onFail: finishBibRequest, | |||
| onError: finishBibRequest, | |||
| }); | |||
| } | |||
| }; | |||
| const popUPBib = (id, bibFlag, appNo) => { | |||
| setBibId(id) | |||
| setBib(bibFlag) | |||
| setAppNo(appNo) | |||
| if (bibLoading) { | |||
| return; | |||
| } | |||
| setBibId(id); | |||
| setBib(bibFlag); | |||
| setAppNo(appNo); | |||
| setIsPopUp(true); | |||
| } | |||
| }; | |||
| const closeBibDialog = () => { | |||
| if (bibLoading) { | |||
| return; | |||
| } | |||
| setIsPopUp(false); | |||
| }; | |||
| const columns = [ | |||
| { | |||
| @@ -134,7 +156,13 @@ export default function SearchPaymentTable({ searchCriteria, applyGridOnReady, a | |||
| headerName: 'BIB', | |||
| width: 150, | |||
| 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> | |||
| <Dialog | |||
| open={isPopUp} | |||
| onClose={() => setIsPopUp(false)} | |||
| onClose={closeBibDialog} | |||
| PaperProps={{ | |||
| sx: { | |||
| 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> | |||
| </DialogContent> | |||
| <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> | |||
| </Dialog> | |||
| </div> | |||
| @@ -557,7 +557,7 @@ const PublicNoticeApplyForm = ({ loadedData, _selections, gazetteIssueList }) => | |||
| </Grid> | |||
| </Grid> | |||
| <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" /> | |||
| </Typography> | |||
| </Grid> | |||
| @@ -573,7 +573,7 @@ const PublicNoticeApplyForm = ({ loadedData, _selections, gazetteIssueList }) => | |||
| })} | |||
| </Grid> | |||
| <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" /> | |||
| </Typography> | |||
| </Grid> | |||
| @@ -605,7 +605,7 @@ const PublicNoticeApplyForm = ({ loadedData, _selections, gazetteIssueList }) => | |||
| <Typography variant="pnspsFormParagraphBold"> | |||
| <span style={{ textAlign: 'justify', }} dangerouslySetInnerHTML={{ __html: intl.formatMessage({ id: "applyTickUnderStr0" }) }} /> | |||
| </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', }}> | |||
| <li dangerouslySetInnerHTML={{ __html: intl.formatMessage({ id: "applyTickUnderStr1" }) }} /> | |||
| <li dangerouslySetInnerHTML={{ __html: intl.formatMessage({ id: "applyTickUnderStr2" }) }} /> | |||
| @@ -8,6 +8,7 @@ import { | |||
| Stack, | |||
| Dialog, DialogTitle, DialogContent, DialogActions, | |||
| } from '@mui/material'; | |||
| import LoadingButton from '@mui/lab/LoadingButton'; | |||
| import { GET_PUBLIC_NOTICE_LIST, SET_PUBLIC_NOTICE_STATUS_PUBLISH_BULK } from "utils/ApiPathConst"; | |||
| import * as HttpUtils from "utils/HttpUtils"; | |||
| import * as DateUtils from "utils/DateUtils"; | |||
| @@ -29,6 +30,7 @@ export default function SearchPublicNoticeTable({ searchCriteria, applyGridOnRea | |||
| const [selectedRowItems, setSelectedRowItems] = React.useState([]); | |||
| const [isConfirmPopUp, setIsConfirmPopUp] = React.useState(false); | |||
| const [isWarningPopUp, setIsWarningPopUp] = React.useState(false); | |||
| const [publishLoading, setPublishLoading] = React.useState(false); | |||
| const [reload, setReload] = React.useState(new Date()); | |||
| @@ -39,6 +41,12 @@ export default function SearchPublicNoticeTable({ searchCriteria, applyGridOnRea | |||
| set_searchCriteria(searchCriteria); | |||
| }, [searchCriteria]); | |||
| React.useEffect(() => { | |||
| if (!isConfirmPopUp) { | |||
| setPublishLoading(false); | |||
| } | |||
| }, [isConfirmPopUp]); | |||
| 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)") | |||
| @@ -158,26 +166,36 @@ export default function SearchPublicNoticeTable({ searchCriteria, applyGridOnRea | |||
| } | |||
| const doPublish = () => { | |||
| setIsConfirmPopUp(false); | |||
| if (publishLoading) return; | |||
| let appIdList = []; | |||
| const datas = rows?.filter((row) => | |||
| selectedRowItems.includes(row.id) | |||
| ); | |||
| // console.log(datas) | |||
| for (var i = 0; i < datas?.length; i++) { | |||
| appIdList.push(datas[i].id); | |||
| } | |||
| if (appIdList.length < 1) { | |||
| setIsConfirmPopUp(false); | |||
| setIsWarningPopUp(true); | |||
| } else { | |||
| setPublishLoading(true); | |||
| HttpUtils.post({ | |||
| url: SET_PUBLIC_NOTICE_STATUS_PUBLISH_BULK, | |||
| params: { | |||
| ids: appIdList | |||
| }, | |||
| onSuccess: () => { | |||
| setPublishLoading(false); | |||
| setIsConfirmPopUp(false); | |||
| setReload(new Date()); | |||
| 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 }} > | |||
| <Button | |||
| variant="contained" | |||
| disabled={publishLoading} | |||
| onClick={() => setIsConfirmPopUp(true)} | |||
| > | |||
| Published | |||
| @@ -228,7 +247,10 @@ export default function SearchPublicNoticeTable({ searchCriteria, applyGridOnRea | |||
| <div> | |||
| <Dialog | |||
| open={isConfirmPopUp} | |||
| onClose={() => setIsConfirmPopUp(false)} | |||
| onClose={() => { | |||
| if (publishLoading) return; | |||
| setIsConfirmPopUp(false); | |||
| }} | |||
| PaperProps={{ | |||
| sx: { | |||
| minWidth: '40vw', | |||
| @@ -248,15 +270,24 @@ export default function SearchPublicNoticeTable({ searchCriteria, applyGridOnRea | |||
| </Stack> | |||
| </DialogContent> | |||
| <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"> | |||
| <FormattedMessage id="close" /> | |||
| </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" /> | |||
| </Typography></Button> | |||
| </Typography> | |||
| </LoadingButton> | |||
| </DialogActions> | |||
| </Dialog> | |||
| </div> | |||
| @@ -394,7 +394,7 @@ const Index = () => { | |||
| </Grid> | |||
| <Grid container spacing={2} alignItems="center"> | |||
| <Grid item sx={{ mt: 1 }}> | |||
| <Typography variant="subtitle1"> | |||
| <Typography variant="subtitle1" component="p"> | |||
| •<FormattedMessage id="pwRemark1" /><br /> | |||
| •<FormattedMessage id="pwRemark2" /><br /> | |||
| •<FormattedMessage id="pwRemark3" /><br /> | |||
| @@ -154,7 +154,7 @@ const BusRegister = () => { | |||
| </Stepper> | |||
| {allStepsCompleted() ? ( | |||
| <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 | |||
| </Typography> | |||
| <Stack direction="row" sx={{ pb: 2 }}> | |||
| @@ -185,7 +185,7 @@ const BusRegister = () => { | |||
| sx={{ mr: 1 }} | |||
| variant="h5" | |||
| > | |||
| <Typography variant="h5"> | |||
| <Typography component="span" variant="h5"> | |||
| <FormattedMessage id="back"/> | |||
| </Typography> | |||
| </Button> | |||
| @@ -197,7 +197,7 @@ const BusRegister = () => { | |||
| sx={{ mr: 1 }} | |||
| variant="h5" | |||
| > | |||
| <Typography variant="h5"> | |||
| <Typography component="span" variant="h5"> | |||
| <FormattedMessage id="back"/> | |||
| </Typography> | |||
| </Button> | |||
| @@ -216,7 +216,7 @@ const BusRegister = () => { | |||
| {isNextBusy ? ( | |||
| <CircularProgress size={22} color="inherit" aria-hidden /> | |||
| ) : ( | |||
| <Typography variant="h5" sx={{ color: 'inherit' }}> | |||
| <Typography component="span" variant="h5" sx={{ color: 'inherit' }}> | |||
| <FormattedMessage id="submit"/> | |||
| </Typography> | |||
| )} | |||
| @@ -225,7 +225,7 @@ const BusRegister = () => { | |||
| ( | |||
| <Button variant="contained" color="inherit" | |||
| 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"/> | |||
| </Typography> | |||
| </Button> | |||
| @@ -233,7 +233,7 @@ const BusRegister = () => { | |||
| ( | |||
| // <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 }}> | |||
| <Typography variant="h5" sx={{ color: 'inherit' }}> | |||
| <Typography component="span" variant="h5" sx={{ color: 'inherit' }}> | |||
| <FormattedMessage id="continue"/> | |||
| </Typography> | |||
| </Button> | |||
| @@ -161,12 +161,12 @@ const Register = () => { | |||
| </Stepper> | |||
| {allStepsCompleted() ? ( | |||
| <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 | |||
| </Typography> | |||
| <Stack direction="row" sx={{ pt: 2 }}> | |||
| <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> | |||
| </React.Fragment> | |||
| ) : ( | |||
| @@ -192,7 +192,7 @@ const Register = () => { | |||
| onClick={handleBack} | |||
| sx={{ mr: 1 }} | |||
| > | |||
| <Typography variant="h5"> | |||
| <Typography component="span" variant="h5"> | |||
| <FormattedMessage id="back"/> | |||
| </Typography> | |||
| </Button> | |||
| @@ -203,7 +203,7 @@ const Register = () => { | |||
| onClick={handleBack} | |||
| sx={{ mr: 1 }} | |||
| > | |||
| <Typography variant="h5"> | |||
| <Typography component="span" variant="h5"> | |||
| <FormattedMessage id="back"/> | |||
| </Typography> | |||
| </Button> | |||
| @@ -222,7 +222,7 @@ const Register = () => { | |||
| {isNextBusy ? ( | |||
| <CircularProgress size={22} color="inherit" aria-hidden /> | |||
| ) : ( | |||
| <Typography variant="h5" sx={{ color: 'inherit' }}> | |||
| <Typography component="span" variant="h5" sx={{ color: 'inherit' }}> | |||
| <FormattedMessage id="submit"/> | |||
| </Typography> | |||
| )} | |||
| @@ -231,7 +231,7 @@ const Register = () => { | |||
| ( | |||
| <Button variant="contained" color="inherit" | |||
| 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"/> | |||
| </Typography> | |||
| </Button> | |||
| @@ -239,7 +239,7 @@ const Register = () => { | |||
| ( | |||
| // <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 }}> | |||
| <Typography variant="h5" sx={{ color: 'inherit' }}> | |||
| <Typography component="span" variant="h5" sx={{ color: 'inherit' }}> | |||
| <FormattedMessage id="continue"/> | |||
| </Typography> | |||
| </Button> | |||
| @@ -160,12 +160,12 @@ const Register = () => { | |||
| </Stepper> | |||
| {allStepsCompleted() ? ( | |||
| <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 | |||
| </Typography> | |||
| <Stack direction="row" sx={{ pt: 2 }}> | |||
| <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> | |||
| </React.Fragment> | |||
| ) : ( | |||
| @@ -191,7 +191,7 @@ const Register = () => { | |||
| sx={{ mr: 1 }} | |||
| variant="h5" | |||
| > | |||
| <Typography variant="h5"> | |||
| <Typography component="span" variant="h5"> | |||
| <FormattedMessage id="back"/> | |||
| </Typography> | |||
| </Button> | |||
| @@ -203,7 +203,7 @@ const Register = () => { | |||
| sx={{ mr: 1 }} | |||
| variant="h5" | |||
| > | |||
| <Typography variant="h5"> | |||
| <Typography component="span" variant="h5"> | |||
| <FormattedMessage id="back"/> | |||
| </Typography> | |||
| </Button> | |||
| @@ -222,7 +222,7 @@ const Register = () => { | |||
| {isNextBusy ? ( | |||
| <CircularProgress size={22} color="inherit" aria-hidden /> | |||
| ) : ( | |||
| <Typography variant="h5" sx={{ color: 'inherit' }}> | |||
| <Typography component="span" variant="h5" sx={{ color: 'inherit' }}> | |||
| <FormattedMessage id="submit"/> | |||
| </Typography> | |||
| )} | |||
| @@ -231,7 +231,7 @@ const Register = () => { | |||
| ( | |||
| <Button variant="contained" color="inherit" | |||
| 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"/> | |||
| </Typography> | |||
| </Button> | |||
| @@ -239,7 +239,7 @@ const Register = () => { | |||
| ( | |||
| // <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 }}> | |||
| <Typography variant="h5" sx={{ color: 'inherit' }}> | |||
| <Typography component="span" variant="h5" sx={{ color: 'inherit' }}> | |||
| <FormattedMessage id="continue"/> | |||
| </Typography> | |||
| </Button> | |||
| @@ -40,7 +40,7 @@ import { EyeOutlined, EyeInvisibleOutlined } from '@ant-design/icons'; | |||
| // import axios from "axios"; | |||
| import { useDispatch } from "react-redux"; | |||
| import { handleLogin } from "auth/index"; | |||
| import { handleLogin, clearExpiredSessionAlert } from "auth/index"; | |||
| import useJwt from "auth/jwt/useJwt"; | |||
| import { FormattedMessage, useIntl } from "react-intl"; | |||
| import { SysContext } from "components/SysSettingProvider" | |||
| @@ -74,6 +74,10 @@ const AuthLoginCustom = () => { | |||
| /** Blocks a second login before React re-renders (double-click / rapid taps). */ | |||
| const loginInFlightRef = useRef(false); | |||
| useEffect(() => { | |||
| clearExpiredSessionAlert(); | |||
| }, []); | |||
| const handleMouseDownPassword = (event) => { | |||
| event.preventDefault(); | |||
| }; | |||
| @@ -84,6 +88,7 @@ const AuthLoginCustom = () => { | |||
| return; | |||
| } | |||
| loginInFlightRef.current = true; | |||
| clearExpiredSessionAlert(); | |||
| setOnLogin(true) | |||
| useJwt | |||
| .login({ username: values.username, password: values.password }) | |||
| @@ -739,7 +739,7 @@ const BusCustomFormWizard = (props) => { | |||
| </Typography> | |||
| </Button> | |||
| <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"/> | |||
| </Typography> | |||
| </div> | |||
| @@ -872,7 +872,7 @@ const BusCustomFormWizard = (props) => { | |||
| <Box sx={{ bgcolor: level?.color, width: 85, height: 8, borderRadius: '7px' }} /> | |||
| </Grid> | |||
| <Grid item> | |||
| <Typography variant="subtitle1"> | |||
| <Typography component="span" variant="subtitle1" sx={{ display: 'block' }}> | |||
| <FormattedMessage id={level ? level?.label : "pwWeak" }/> | |||
| </Typography> | |||
| </Grid> | |||
| @@ -939,7 +939,7 @@ const BusCustomFormWizard = (props) => { | |||
| </Stack> | |||
| <Grid container spacing={2} alignItems="center"> | |||
| <Grid item sx={{mt:1}}> | |||
| <Typography variant="subtitle1"> | |||
| <Typography component="span" variant="subtitle1" sx={{ display: 'block' }}> | |||
| •<FormattedMessage id="pwRemark1"/><br /> | |||
| •<FormattedMessage id="pwRemark2"/><br /> | |||
| •<FormattedMessage id="pwRemark3"/><br /> | |||
| @@ -963,7 +963,7 @@ const BusCustomFormWizard = (props) => { | |||
| </Grid> | |||
| <Grid item xs={12} md={12}> | |||
| <Typography variant="subtitle1"> | |||
| <Typography component="span" variant="subtitle1" sx={{ display: 'block' }}> | |||
| •<FormattedMessage id="pleaseEnterOrgOrCompName"/> | |||
| </Typography> | |||
| </Grid> | |||
| @@ -1617,7 +1617,7 @@ const BusCustomFormWizard = (props) => { | |||
| <Grid item xs={12} md={12}> | |||
| <Grid container> | |||
| <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"})}} /> | |||
| </Typography> | |||
| </Grid> | |||
| @@ -1744,7 +1744,7 @@ const BusCustomFormWizard = (props) => { | |||
| <Grid item xs={12}> | |||
| <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" }}> | |||
| <Typography display="inline" variant="h3"component="h1" sx={{ color: '#1A4399' }}> | |||
| <Typography display="inline" variant="h3" component="h1" sx={{ color: '#1A4399' }}> | |||
| <FormattedMessage id="becomeNewBusinessUser"/> | |||
| </Typography> | |||
| </div> | |||
| @@ -1090,7 +1090,7 @@ const CustomFormWizard = (props) => { | |||
| <Box sx={{ bgcolor: level?.color, width: 85, height: 8, borderRadius: '7px' }} /> | |||
| </Grid> | |||
| <Grid item> | |||
| <Typography variant="subtitle1"> | |||
| <Typography component="span" variant="subtitle1" sx={{ display: 'block' }}> | |||
| <FormattedMessage id={level ? level?.label : "pwWeak"} /> | |||
| </Typography> | |||
| </Grid> | |||
| @@ -1157,7 +1157,7 @@ const CustomFormWizard = (props) => { | |||
| </Stack> | |||
| <Grid container spacing={2} alignItems="center"> | |||
| <Grid item sx={{ mt: 1 }}> | |||
| <Typography variant="subtitle1"> | |||
| <Typography component="span" variant="subtitle1" sx={{ display: 'block' }}> | |||
| •<FormattedMessage id="pwRemark1" /><br /> | |||
| •<FormattedMessage id="pwRemark2" /><br /> | |||
| •<FormattedMessage id="pwRemark3" /><br /> | |||
| @@ -1392,7 +1392,7 @@ const CustomFormWizard = (props) => { | |||
| </Grid> | |||
| </Grid> | |||
| <Grid item xs={12} md={12}> | |||
| <Typography variant="subtitle1"> | |||
| <Typography component="span" variant="subtitle1" sx={{ display: 'block' }}> | |||
| <FormattedMessage id="registerNameLabel" /> | |||
| </Typography> | |||
| </Grid> | |||
| @@ -1881,10 +1881,10 @@ const CustomFormWizard = (props) => { | |||
| <Typography display="inline" variant="h4" component="h2" sx={{ color: 'primary.primary' }}> | |||
| <FormattedMessage id="userIdDoc" /> | |||
| <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" /> | |||
| </Typography> | |||
| <Typography display="inline" variant="subtitle1" sx={{ color: 'primary.primary' }}> | |||
| <Typography component="span" variant="subtitle1" sx={{ color: 'primary.primary', display: 'block' }}> | |||
| <FormattedMessage id="pleaseUploadIdDocSubTitle" /> | |||
| </Typography> | |||
| <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 container> | |||
| <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"})}} /> | |||
| </Typography> | |||
| </Grid> | |||
| @@ -1053,7 +1053,7 @@ const CustomFormWizard = (props) => { | |||
| <Grid item xs={12} md={12}> | |||
| <Grid container> | |||
| <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" }) }} /> | |||
| </Typography> | |||
| @@ -1192,7 +1192,7 @@ const CustomFormWizard = (props) => { | |||
| </Grid> | |||
| <Grid item xs={12} md={12} > | |||
| <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" /> | |||
| </Typography> | |||
| <Typography variant="h5" component="span" name="preview-idDocType-1"> | |||
| @@ -34,7 +34,7 @@ export default function PreviewUploadFileTable({ recordList, }) { | |||
| function NoRowsOverlay() { | |||
| return ( | |||
| <Stack height="100%" alignItems="center" justifyContent="center"> | |||
| <Typography variant="h6"> | |||
| <Typography component="span" variant="h6"> | |||
| <FormattedMessage id="noFile"/> | |||
| </Typography> | |||
| {/* <pre>(rows={[]})</pre> */} | |||
| @@ -48,7 +48,7 @@ export default function UploadFileTable({ recordList, setUpdateRows, }) { | |||
| justifyContent: 'center', | |||
| }} | |||
| > | |||
| <Typography variant="h6"> | |||
| <Typography component="span" variant="h6"> | |||
| <FormattedMessage id="noFile"/> | |||
| </Typography> | |||
| </Box> | |||
| @@ -20,7 +20,7 @@ export const PNSPS_BUTTON_THEME = createTheme({ | |||
| }, | |||
| save:{ | |||
| main: '#448DF2', | |||
| main: '#0c489e', | |||
| contrastText: '#FFFFFF', | |||
| }, | |||
| export:{ | |||
| @@ -44,7 +44,7 @@ export const PNSPS_BUTTON_THEME = createTheme({ | |||
| contrastText: '#FFFFFF', | |||
| }, | |||
| success:{ | |||
| main: '#448DF2', | |||
| main: '#0c489e', | |||
| contrastText: '#FFFFFF', | |||
| }, | |||
| green:{ | |||
| @@ -194,7 +194,7 @@ export const PNSPS_LONG_BUTTON_THEME = createTheme({ | |||
| }, | |||
| save:{ | |||
| main: '#448DF2', | |||
| main: '#0c489e', | |||
| contrastText: '#FFFFFF', | |||
| }, | |||
| export:{ | |||
| @@ -28,7 +28,7 @@ export const piechartColor2 = [ | |||
| ]; | |||
| export const cardBorderColor = [ | |||
| '#efb142', '#4bb641', '#448df2', '#e03c04' | |||
| '#efb142', '#4bb641', '#0c489e', '#e03c04' | |||
| ]; | |||
| export const chartLineColor = [ | |||
| @@ -39,7 +39,7 @@ export const GENERAL_RED_COLOR = '#e03c04'; | |||
| 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'; | |||
| @@ -24,7 +24,7 @@ export default function Checkbox(theme) { | |||
| color: CONTAINED_PRIMARY_BLUE | |||
| }, | |||
| '&.Mui-focusVisible': { | |||
| boxShadow: '0 0 0 3px rgba(25, 118, 210, 0.35)' | |||
| boxShadow: `0 0 0 3px ${CONTAINED_PRIMARY_BLUE}` | |||
| }, | |||
| svg: { | |||
| width: checkboxSize, | |||
| @@ -21,7 +21,7 @@ export default function Radio() { | |||
| color: CONTAINED_PRIMARY_BLUE | |||
| }, | |||
| '&.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 */ | |||
| '&.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': { | |||
| 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) | |||
| '&.Mui-focusVisible': { | |||
| outline: '3px solid rgba(21, 101, 192, 0.55)', | |||
| outline: `3px solid ${CONTAINED_PRIMARY_BLUE}`, | |||
| outlineOffset: '3px', | |||
| borderRadius: '6px', | |||
| }, | |||
| @@ -423,7 +423,7 @@ | |||
| "payMethod": "Payment methods", | |||
| "epayMethod": " e-Payment Method", | |||
| "selectPaymentMethod": "Please select a payment method", | |||
| "selectPaymentMethodBtn": "Select payment method", | |||
| "selectPaymentMethodBtn": "Pay Method", | |||
| "payReceipt": "Payment Receipt", | |||
| "contactPerson": "Contact Person", | |||
| "requireContactPerson": "Please enter contact person", | |||
| @@ -511,8 +511,9 @@ | |||
| "fpsQrcodeTitle3":"Please complete the payment process within the specified time", | |||
| "fpsQrcodeTitle4":"Remaining time:", | |||
| "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.", | |||
| "payNPGORemark" :"Payment to be completed at NPGO Collection Office by {date} 12:30 p.m.", | |||
| @@ -89,8 +89,9 @@ | |||
| "fpsQrcodeTitle3":"请在规定时间内完成付款流程", | |||
| "fpsQrcodeTitle4":"剩余时间:", | |||
| "fpsQrcodeTitle5":"秒", | |||
| "fpsQrcodeExpired":"二维码已过期", | |||
| "fpsPaymentErrorMsg":"载入支付二维码时发生错误。请勿在付款过程中更新此页面。如果付款未完成,请点击「取消支付」按钮并重新支付。由此造成的不便,敬请谅解。", | |||
| "fpsQrcodeExpired":"二维码已过期", | |||
| "fpsPaymentErrorMsg":"载入支付二维码时发生错误。请勿在付款过程中更新此页面。如果付款未完成,请点击「取消支付」按钮并重新支付。由此造成的不便,敬请谅解。", | |||
| "fpsSelectPaymentApp":"请选择付款支付程序", | |||
| "payDnRemark": "在{date}下午12时30分前将付款证明(例如银行入数纸或网上银行付款记录)电邮至 [email protected]", | |||
| "payNPGORemark" :"在{date}下午12时30分前到北角政府合署收款处完成付款", | |||
| @@ -89,8 +89,9 @@ | |||
| "fpsQrcodeTitle3":"請在規定時間內完成付款流程", | |||
| "fpsQrcodeTitle4":"剩餘時間:", | |||
| "fpsQrcodeTitle5":"秒", | |||
| "fpsQrcodeExpired":"二維碼已過期", | |||
| "fpsPaymentErrorMsg":"載入支付二維碼時發生錯誤。請勿在付款過程中更新此頁面。如果付款未完成,請點擊「取消支付」按鈕並重新支付。由此造成的不便,敬請諒解。", | |||
| "fpsQrcodeExpired":"二維碼已過期", | |||
| "fpsPaymentErrorMsg":"載入支付二維碼時發生錯誤。請勿在付款過程中更新此頁面。如果付款未完成,請點擊「取消支付」按鈕並重新支付。由此造成的不便,敬請諒解。", | |||
| "fpsSelectPaymentApp":"請選擇付款支付程式", | |||
| "payDnRemark": "在{date}下午12時30分前將付款證明(例如銀行入數紙或網上銀行付款記錄)電郵至 [email protected]", | |||
| "payNPGORemark" :"在{date}下午12時30分前到北角政府合署收款處完成付款", | |||