From b7b7a9c246e9a2cc1b2a1779277f3c25b3eab176 Mon Sep 17 00:00:00 2001 From: Jason Chuang Date: Fri, 12 Jun 2026 21:50:50 +0800 Subject: [PATCH 01/50] header h4 to h3 --- src/components/iAmSmartButton.js | 2 +- src/pages/authentication/RegisterCustom.js | 6 +- .../auth-forms/BusCustomFormWizard.js | 10 +- .../auth-forms/CustomFormWizard.js | 11 +- .../auth-forms/UploadFileTable.js | 147 ++++++++---------- src/pages/dashboard/Public/Notice.js | 2 +- src/translations/en.json | 1 + src/translations/zh-CN.json | 1 + src/translations/zh-HK.json | 1 + 9 files changed, 91 insertions(+), 90 deletions(-) diff --git a/src/components/iAmSmartButton.js b/src/components/iAmSmartButton.js index 3ae9e0c..532ac25 100644 --- a/src/components/iAmSmartButton.js +++ b/src/components/iAmSmartButton.js @@ -25,7 +25,7 @@ export function IAmSmartButton({ label, onClickFun, fullWidth }) { return ( diff --git a/src/pages/authentication/RegisterCustom.js b/src/pages/authentication/RegisterCustom.js index adfc4e2..3ce91a4 100644 --- a/src/pages/authentication/RegisterCustom.js +++ b/src/pages/authentication/RegisterCustom.js @@ -154,7 +154,7 @@ const RegisterCustom = () => { }, }} > - + @@ -164,7 +164,7 @@ const RegisterCustom = () => { - + diff --git a/src/pages/authentication/auth-forms/BusCustomFormWizard.js b/src/pages/authentication/auth-forms/BusCustomFormWizard.js index bd33fd4..4b61337 100644 --- a/src/pages/authentication/auth-forms/BusCustomFormWizard.js +++ b/src/pages/authentication/auth-forms/BusCustomFormWizard.js @@ -343,6 +343,10 @@ const BusCustomFormWizard = (props) => { } + if (fileInputRef.current) { + fileInputRef.current.value = ''; + } + }, [updateRows]); const handleBrNo = (brNo) => { @@ -396,6 +400,8 @@ const BusCustomFormWizard = (props) => { setFileListData(saveFileList) setFileList(updatedFileList); + setUpdateRows(saveFileList); + event.target.value = ''; }; useEffect(() => { @@ -815,6 +821,7 @@ const BusCustomFormWizard = (props) => { endAdornment={ { endAdornment={ { - { onCaptchaChange() }}> + { onCaptchaChange() }}> diff --git a/src/pages/authentication/auth-forms/CustomFormWizard.js b/src/pages/authentication/auth-forms/CustomFormWizard.js index 4d624b3..0e31fa3 100644 --- a/src/pages/authentication/auth-forms/CustomFormWizard.js +++ b/src/pages/authentication/auth-forms/CustomFormWizard.js @@ -432,6 +432,10 @@ const CustomFormWizard = (props) => { } + if (fileInputRef.current) { + fileInputRef.current.value = ''; + } + }, [updateRows]); const handleFileUpload = (event) => { @@ -473,6 +477,8 @@ const CustomFormWizard = (props) => { setFileListData(saveFileList) setFileList(updatedFileList); + setUpdateRows(saveFileList); + event.target.value = ''; }; useEffect(() => { @@ -1033,6 +1039,7 @@ const CustomFormWizard = (props) => { endAdornment={ { endAdornment={ { - { onCaptchaChange() }}> + { onCaptchaChange() }}> @@ -2107,6 +2115,7 @@ const CustomFormWizard = (props) => { { setRows(recordList); - // console.log(disableDelete); }, [recordList]); - function NoRowsOverlay() { + const handleCancelClick = (id) => { + setRows((prevRows) => { + const newRows = prevRows.filter((row) => row.id !== id); + setUpdateRows(newRows); + return newRows; + }); + }; + + const formatFileSize = (size) => `${Math.ceil(size / 1024)} KB`; + + if (rows.length === 0) { return ( - + - {/*
(rows={[]})
*/} -
+ ); } - const handleCancelClick = (id) => () => { - setRowModesModel({ - ...rowModesModel, - [id]: { mode: GridRowModes.View, ignoreModifications: true }, - }); - // console.log("Starting Delete") - // const editedRow = rows.find((row) => row.id === id); - // console.log(editedRow) - // console.log(editedRow.isNew) - setUpdateRows(rows.filter((row) => row.id !== id)); - setRows(rows.filter((row) => row.id !== id)); - } - - const columns = [ - { - field: 'actions', - type: 'actions', - headerName: '', - width: 30, - cellClassName: 'actions', - // hide:true, - getActions: ({ id }) => { - return [ - } - label="delete" - className="textPrimary" - onClick={handleCancelClick(id)} - color="error" - />] - }, - }, - { - id: 'name', - field: 'name', - headerName: intl.formatMessage({ id: 'fileName' }), - flex: 4, - }, - { - id: 'size', - field: 'size', - headerName: intl.formatMessage({ id: 'fileSize' }), - valueGetter: (params) => { - // console.log(params) - return Math.ceil(params.value / 1024) + " KB"; - }, - flex: 2, - }, - ]; - return ( - - "auto"} - /> + + + + + + {intl.formatMessage({ id: 'fileName' })} + {intl.formatMessage({ id: 'fileSize' })} + + + + {rows.map((row) => ( + + + handleCancelClick(row.id)} + > + + + + + + {row.name} + + + + {formatFileSize(row.size)} + + + ))} + +
); } diff --git a/src/pages/dashboard/Public/Notice.js b/src/pages/dashboard/Public/Notice.js index c53eae8..2b6e7bb 100644 --- a/src/pages/dashboard/Public/Notice.js +++ b/src/pages/dashboard/Public/Notice.js @@ -32,7 +32,7 @@ const SearchDemandNoteForm = (props) => { listData.map((item, index) => { list.push( - {locale === 'en' ?item.subjectEng:locale === 'zh-HK' ?item.subjectCht:item.subjectChs} + {locale === 'en' ?item.subjectEng:locale === 'zh-HK' ?item.subjectCht:item.subjectChs} {DateUtils.dateValue(item.announceDate)} {locale === 'en' ?item.contentEng:locale === 'zh-HK' ?item.contentCht:item.contentChs} diff --git a/src/translations/en.json b/src/translations/en.json index 194e47f..ee19f9b 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -528,6 +528,7 @@ "submitReply": "Submit", "requiredUploadFix": "Please upload the modified file of the manuscript", "upload": "Upload", + "delete": "Delete", "actionFail": "Action failed: Please check the content and submit the reply again", "issueInvalidMsg": "Application for the selected publication volume has been closed. Please choose another volume to apply again.", "singleCol":"Single Column", diff --git a/src/translations/zh-CN.json b/src/translations/zh-CN.json index 100b61f..2cf4712 100644 --- a/src/translations/zh-CN.json +++ b/src/translations/zh-CN.json @@ -524,6 +524,7 @@ "submitReply": "提交回复", "requiredUploadFix": "请上载稿件修改的档案", "upload": "上载", + "delete": "删除", "actionFail": "行动失败: 请检查内容并再次提交回复", "issueInvalidMsg": "所选刊登期数已截稿,请选择另一期重新申请。", "singleCol":"一格位", diff --git a/src/translations/zh-HK.json b/src/translations/zh-HK.json index 2af3014..d145a57 100644 --- a/src/translations/zh-HK.json +++ b/src/translations/zh-HK.json @@ -525,6 +525,7 @@ "submitReply": "提交回覆", "requiredUploadFix": "請上載稿件修改的檔案", "upload": "上載", + "delete": "刪除", "actionFail": "行動失敗: 請檢查內容並再次提交回覆", "issueInvalidMsg": "所選刊登期數已截稿,請選擇另一期重新申請。", "singleCol":"一格位", From 4214676dcf79ad1260a7ccb39c17f361a8d93cac Mon Sep 17 00:00:00 2001 From: Jason Chuang Date: Fri, 12 Jun 2026 21:56:22 +0800 Subject: [PATCH 02/50] CR-022 Item 1 --- src/translations/en.json | 2 +- src/translations/zh-CN.json | 2 +- src/translations/zh-HK.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/translations/en.json b/src/translations/en.json index 194e47f..01c5420 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -447,7 +447,7 @@ "paymentLimitPrice2":" is only applicable when minimum amount is HK$0.10 and maximum amount is HK$9,999,999.99", "paymentLimitPPS":" Payment could not be made via mobile device browsers, please use desktop computers to make payment.", "paymentMethod": "Payment Method", - "paymentProcessLimited":"Payment process must be completed within 30 minutes and return to this system.", + "paymentProcessLimited":"Please complete the payment process within 15 minutes. Note: For FPS payments, scanning, payment and all necessary approvals must be finished within 3 minutes due to security-related QR code expiry.", "publicNoticeDetailTitle": "Public Notice Application Information", "applyPerson": "Applicant", diff --git a/src/translations/zh-CN.json b/src/translations/zh-CN.json index 100b61f..45316d1 100644 --- a/src/translations/zh-CN.json +++ b/src/translations/zh-CN.json @@ -481,7 +481,7 @@ "paymentLimitPrice2":"只适用于最小金额为 0.10 港元及最高金额为 9,999,999.99港元", "paymentLimitPPS":"付款不适用于流动装置的浏览器,请使用桌面电脑。", "paymentMethod": "付款方式", - "paymentProcessLimited":"付款过程必须在 30 分钟内完成及返回本系统。", + "paymentProcessLimited":"请于15分钟内完成付款程序。 注意:使用转数快(FPS)时,因二维码具安全时效限制,须于3分钟内完成扫码、付款及所有相关审核程序。", "publicNoticeDetailTitle": "公共启事申请资料", "applyPerson": "申请人", diff --git a/src/translations/zh-HK.json b/src/translations/zh-HK.json index 2af3014..339603b 100644 --- a/src/translations/zh-HK.json +++ b/src/translations/zh-HK.json @@ -482,7 +482,7 @@ "paymentLimitPrice2":"只適用於最小金額為 0.10 港元及最高金額為 9,999,999.99港元", "paymentLimitPPS":"付款不適用於流動裝置的瀏覽器,請使用桌面電腦。", "paymentMethod": "付款方法", - "paymentProcessLimited":"付款程序必須在 30 分鐘內完成及返回本系统。", + "paymentProcessLimited":"請於15分鐘內完成付款程序。 注意:使用轉數快(FPS)時,因二維碼具安全時效限制,須於3分鐘內完成掃碼、付款及所有相關審核程序。", "publicNoticeDetailTitle": "公共啟事申請資料", "applyPerson": "申請人", From cdb9062c90e26cce76214d1547335406bde9f018 Mon Sep 17 00:00:00 2001 From: Jason Chuang Date: Fri, 12 Jun 2026 22:11:57 +0800 Subject: [PATCH 03/50] CR-022 Item 2 --- src/pages/Payment/Search_Public/DataGrid.js | 19 +++++++++++++++++++ src/translations/en.json | 2 +- src/translations/zh-CN.json | 2 +- src/translations/zh-HK.json | 2 +- 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/pages/Payment/Search_Public/DataGrid.js b/src/pages/Payment/Search_Public/DataGrid.js index 1e16572..e912e0d 100644 --- a/src/pages/Payment/Search_Public/DataGrid.js +++ b/src/pages/Payment/Search_Public/DataGrid.js @@ -47,6 +47,12 @@ export default function SearchPublicNoticeTable({ searchCriteria, applyGridOnRea navigate('/paymentPage/details/' + params.row.id); }; + const formatPayMethod = (value) => { + if (!value) return ""; + const lastCommaIndex = value.lastIndexOf(','); + return lastCommaIndex >= 0 ? value.substring(lastCommaIndex + 1) : value; + }; + const columns = [ { field: 'actions', @@ -58,6 +64,12 @@ export default function SearchPublicNoticeTable({ searchCriteria, applyGridOnRea return clickableLink('/paymentPage/details/' + params.row.id, params.row.transNo); }, }, + { + field: 'egisRefNo', + headerName: intl.formatMessage({id: 'paymentRefCode'}), + width: isMdOrLg ? 'auto' : 160, + flex: isMdOrLg ? 1 : undefined, + }, { id: 'appNos', field: 'appNos', @@ -69,6 +81,13 @@ export default function SearchPublicNoticeTable({ searchCriteria, applyGridOnRea return
{appNo}
}, }, + { + field: 'payMethod', + headerName: intl.formatMessage({id: 'paymentMethod'}), + width: isMdOrLg ? 'auto' : 160, + flex: isMdOrLg ? 1 : undefined, + valueGetter: (params) => formatPayMethod(params?.value ?? params.row?.payMethod), + }, { id: 'transDateTime', field: 'transDateTime', diff --git a/src/translations/en.json b/src/translations/en.json index 01c5420..2acb995 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -421,7 +421,7 @@ "payTotal": "Total Payment Amount", "payDetail": "Payment Details", "payMethod": "Payment methods", - "epayMethod": " e-Payment Method", + "epayMethod": " Payment Method", "selectPaymentMethod": "Please select a payment method", "payReceipt": "Payment Receipt", "contactPerson": "Contact Person", diff --git a/src/translations/zh-CN.json b/src/translations/zh-CN.json index 45316d1..cf8135e 100644 --- a/src/translations/zh-CN.json +++ b/src/translations/zh-CN.json @@ -454,7 +454,7 @@ "payTotal": "付款总额", "payDetail": "付款详情", "payMethod": "付款方式", - "epayMethod": "电子付款方法", + "epayMethod": "付款方法", "selectPaymentMethod": "请选择付款方法", "payReceipt": "付款收据", "contactPerson": "联络人", diff --git a/src/translations/zh-HK.json b/src/translations/zh-HK.json index 339603b..6425bef 100644 --- a/src/translations/zh-HK.json +++ b/src/translations/zh-HK.json @@ -455,7 +455,7 @@ "payTotal": "付款總額", "payDetail": "付款詳情", "payMethod": "付款方式", - "epayMethod": "電子付款方法", + "epayMethod": "付款方法", "selectPaymentMethod": "請選擇付款方法", "payReceipt": "付款收據", "contactPerson": "聯絡人", From c5a1153aa748a35d0d7a17e721c3cae643f42b04 Mon Sep 17 00:00:00 2001 From: Jason Chuang Date: Fri, 12 Jun 2026 22:18:56 +0800 Subject: [PATCH 04/50] CR-022 Item 2 --- src/pages/Payment/Search_Public/DataGrid.js | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/pages/Payment/Search_Public/DataGrid.js b/src/pages/Payment/Search_Public/DataGrid.js index e912e0d..c606e43 100644 --- a/src/pages/Payment/Search_Public/DataGrid.js +++ b/src/pages/Payment/Search_Public/DataGrid.js @@ -11,6 +11,7 @@ import { FiDataGrid } from "components/FiDataGrid"; import {useTheme} from "@emotion/react"; import {useIntl} from "react-intl"; import { clickableLink } from 'utils/CommonFunction'; +import { getPaymentMethodByCode } from "auth/utils"; import {PAYMENT_LIST} from "utils/ApiPathConst"; // ==============================|| EVENT TABLE ||============================== // @@ -47,12 +48,6 @@ export default function SearchPublicNoticeTable({ searchCriteria, applyGridOnRea navigate('/paymentPage/details/' + params.row.id); }; - const formatPayMethod = (value) => { - if (!value) return ""; - const lastCommaIndex = value.lastIndexOf(','); - return lastCommaIndex >= 0 ? value.substring(lastCommaIndex + 1) : value; - }; - const columns = [ { field: 'actions', @@ -86,7 +81,7 @@ export default function SearchPublicNoticeTable({ searchCriteria, applyGridOnRea headerName: intl.formatMessage({id: 'paymentMethod'}), width: isMdOrLg ? 'auto' : 160, flex: isMdOrLg ? 1 : undefined, - valueGetter: (params) => formatPayMethod(params?.value ?? params.row?.payMethod), + renderCell: (params) => getPaymentMethodByCode(params?.value), }, { id: 'transDateTime', From 7b7a91339821e0e7fc7266a8b701fa6ba91925dc Mon Sep 17 00:00:00 2001 From: Jason Chuang Date: Fri, 12 Jun 2026 22:19:14 +0800 Subject: [PATCH 05/50] CR-022 Item 3 --- src/pages/Payment/Details_GLD/PaymentDetails.js | 2 +- src/pages/Payment/Search_GLD/DataGrid.js | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/pages/Payment/Details_GLD/PaymentDetails.js b/src/pages/Payment/Details_GLD/PaymentDetails.js index 0e9f69e..49fc45a 100644 --- a/src/pages/Payment/Details_GLD/PaymentDetails.js +++ b/src/pages/Payment/Details_GLD/PaymentDetails.js @@ -116,7 +116,7 @@ const PaymentDetails = ({ formData,doPrint,onDownload }) => { - EGIS Reference No.: + Payment Reference No.: diff --git a/src/pages/Payment/Search_GLD/DataGrid.js b/src/pages/Payment/Search_GLD/DataGrid.js index d24fed0..f1d9891 100644 --- a/src/pages/Payment/Search_GLD/DataGrid.js +++ b/src/pages/Payment/Search_GLD/DataGrid.js @@ -87,7 +87,7 @@ export default function SearchPaymentTable({ searchCriteria, applyGridOnReady, a }, { field: 'actions', - headerName: 'Transaction No.', + headerName: 'Payment No.', flex: 1, minWidth: 200, cellClassName: 'actions', @@ -95,6 +95,12 @@ export default function SearchPaymentTable({ searchCriteria, applyGridOnReady, a return clickableLink('/paymentPage/details/' + params.row.id, params.row.transNo); }, }, + { + field: 'egisRefNo', + headerName: 'Payment Reference No.', + flex: 1, + minWidth: 200, + }, { field: 'payMethod', headerName: 'Payment Method', @@ -107,7 +113,7 @@ export default function SearchPaymentTable({ searchCriteria, applyGridOnReady, a { id: 'transDateTime', field: 'transDateTime', - headerName: 'Transaction Date', + headerName: 'Payment Date', flex: 1, minWidth: 150, From f447cb1dae0642d39acafab57ad39b636851c43c Mon Sep 17 00:00:00 2001 From: Jason Chuang Date: Fri, 12 Jun 2026 22:48:56 +0800 Subject: [PATCH 06/50] CR-022 Item 5 --- .../Payment/Details_GLD/PaymentDetails.js | 70 +++++++++++++++++-- src/pages/Payment/Details_GLD/index.js | 1 + src/utils/ApiPathConst.js | 1 + 3 files changed, 67 insertions(+), 5 deletions(-) diff --git a/src/pages/Payment/Details_GLD/PaymentDetails.js b/src/pages/Payment/Details_GLD/PaymentDetails.js index 49fc45a..f14d955 100644 --- a/src/pages/Payment/Details_GLD/PaymentDetails.js +++ b/src/pages/Payment/Details_GLD/PaymentDetails.js @@ -3,24 +3,33 @@ import { Grid, Typography, FormLabel, - Button + Button, + Dialog, + DialogTitle, + DialogContent, + DialogActions, + Box } from '@mui/material'; import * as React from "react"; import * as FormatUtils from "utils/FormatUtils"; import * as PaymentStatus from "utils/statusUtils/PaymentStatus"; import * as DateUtils from "utils/DateUtils"; +import * as HttpUtils from "utils/HttpUtils"; +import { PAYMENT_MARK_AS_PAID } from "utils/ApiPathConst"; import Loadable from 'components/Loadable'; const MainCard = Loadable(React.lazy(() => import('components/MainCard'))); const LoadingComponent = Loadable(React.lazy(() => import('pages/extra-pages/LoadingComponent'))); import DownloadIcon from '@mui/icons-material/Download'; import {useIntl} from "react-intl"; // ==============================|| DASHBOARD - DEFAULT ||============================== // -const PaymentDetails = ({ formData,doPrint,onDownload }) => { +const PaymentDetails = ({ formData, doPrint, onDownload, onRefresh }) => { const intl = useIntl(); const [data, setData] = React.useState({}); const [onReady, setOnReady] = React.useState(false); + const [confirmOpen, setConfirmOpen] = React.useState(false); + const [markingPaid, setMarkingPaid] = React.useState(false); // const { locale } = intl; React.useEffect(() => { @@ -40,9 +49,29 @@ const PaymentDetails = ({ formData,doPrint,onDownload }) => { if("01" == paymentmethod) return "PPS"; if("02" == paymentmethod || "03" == paymentmethod) return "Credit Card"; if("04" == paymentmethod) return "FPS"; + if (data.payMethod === "04,BCFP,FPS") return "FPS"; return paymentmethod; } + const showMarkAsPaid = data.status === "REJT" && getPaymentMethod() === "FPS"; + + const handleMarkAsPaid = () => { + setMarkingPaid(true); + HttpUtils.post({ + url: PAYMENT_MARK_AS_PAID + "/" + data.id, + onSuccess: () => { + setConfirmOpen(false); + setMarkingPaid(false); + if (onRefresh) { + onRefresh(); + } + }, + onError: () => { + setMarkingPaid(false); + } + }); + }; + return ( !onReady ? @@ -51,9 +80,21 @@ const PaymentDetails = ({ formData,doPrint,onDownload }) => { border={false} content={false} > - - Payment Details - + + + Payment Details + + {showMarkAsPaid && ( + + )} +
@@ -176,6 +217,25 @@ const PaymentDetails = ({ formData,doPrint,onDownload }) => { } + !markingPaid && setConfirmOpen(false)} + > + Confirm + + + {`Are you sure to mark as paid for Payment No. ${data.transNo || data.payload?.transactionid} ?`} + + + + + + + ); }; diff --git a/src/pages/Payment/Details_GLD/index.js b/src/pages/Payment/Details_GLD/index.js index b51a182..4d700ce 100644 --- a/src/pages/Payment/Details_GLD/index.js +++ b/src/pages/Payment/Details_GLD/index.js @@ -125,6 +125,7 @@ const Index = () => { formData={record} doPrint={doPrint} onDownload={onDownload} + onRefresh={loadForm} style={{ display: "flex", height: "100%", diff --git a/src/utils/ApiPathConst.js b/src/utils/ApiPathConst.js index 38daac3..d803a13 100644 --- a/src/utils/ApiPathConst.js +++ b/src/utils/ApiPathConst.js @@ -180,6 +180,7 @@ export const PAYMENT_LOAD = apiPath+'/payment/load';//GET export const PAYMENT_APP_LIST = apiPath+'/payment/applist';//POST export const PAYMENT_CHECK = apiPath+'/payment/check-payment';//GET export const PAYMENT_BIB = apiPath+'/payment/set-bib';//POST +export const PAYMENT_MARK_AS_PAID = apiPath+'/payment/mark-as-paid';//POST export const PAYMENT_GFMIS_LIST = apiPath+'/payment/listGFMIS';//GET export const PAYMENT_LIMIT_SETTING_LIST = apiPath+'/settings/payment';//GET From b0ee1b0714f3ef63e0bd8aea73792d7d2fe7ede2 Mon Sep 17 00:00:00 2001 From: Jason Chuang Date: Fri, 12 Jun 2026 23:11:51 +0800 Subject: [PATCH 07/50] CR-022 Item 7 --- src/pages/Payment/Details_GLD/index.js | 48 ++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/src/pages/Payment/Details_GLD/index.js b/src/pages/Payment/Details_GLD/index.js index 4d700ce..2934748 100644 --- a/src/pages/Payment/Details_GLD/index.js +++ b/src/pages/Payment/Details_GLD/index.js @@ -35,6 +35,9 @@ const Index = () => { const params = useParams(); const navigate = useNavigate() + const [responeData, setResponeData] = React.useState({}); + const [transactionData, setTransactionData] = React.useState({}); + const [record, setRecord] = React.useState(); const [itemList, setItemList] = React.useState([]); const [onReady, setOnReady] = React.useState(false); @@ -47,8 +50,16 @@ const Index = () => { }, []); React.useEffect(() => { - setOnReady(true); - }, [record]); + if (Object.keys(responeData).length > 0) { + setTransactionData(responeData); + } + }, [responeData]); + + React.useEffect(() => { + if (Object.keys(transactionData).length > 0) { + setOnReady(true); + } + }, [transactionData]); // const handleResize = () => { // setDetailsOrder(window.innerWidth > 1023 ? 2 : -1); @@ -70,17 +81,40 @@ const Index = () => { const loadForm = () => { if (params.id > 0) { - HttpUtils.get({ url: UrlUtils.PAYMENT_LOAD + "/" + params.id, onSuccess: (responseData) => { if (!responseData.data?.id) { navigate("/paymentPage/search"); } - responseData.data["transDateStr"] = responseData.data.transDateTime; - responseData.data["transTimeStr"] = DateUtils.dateFormat(responseData.data.transDateTime, "HH:mm:ss"); - setItemList(responseData.paymentItemList) - setRecord(responseData.data); + if (responseData.data.status == null || responseData.data.status == "INPR") { + HttpUtils.post({ + url: UrlUtils.PAYMENT_RETRY_STATUS_API, + params: { + "paymentId": params.id + }, + onSuccess: function (responseData2) { + responseData2.data["transDateStr"] = responseData2.data.transDateTime; + responseData2.data["transTimeStr"] = DateUtils.dateFormat(responseData2.data.transDateTime, "HH:mm:ss"); + setResponeData(responseData2.transactionData); + setItemList(responseData2.paymentItemList); + setRecord(responseData2.data); + }, + onError: function () { + responseData.data["transDateStr"] = responseData.data.transDateTime; + responseData.data["transTimeStr"] = DateUtils.dateFormat(responseData.data.transDateTime, "HH:mm:ss"); + setResponeData(responseData); + setItemList(responseData.paymentItemList); + setRecord(responseData.data); + } + }); + } else { + responseData.data["transDateStr"] = responseData.data.transDateTime; + responseData.data["transTimeStr"] = DateUtils.dateFormat(responseData.data.transDateTime, "HH:mm:ss"); + setResponeData(responseData); + setItemList(responseData.paymentItemList); + setRecord(responseData.data); + } } }); } From d1d844b63366204afba02e85347faac3586c4d35 Mon Sep 17 00:00:00 2001 From: Jason Chuang Date: Wed, 17 Jun 2026 21:29:30 +0800 Subject: [PATCH 08/50] CR-024 Item 1 --- .../UserInformationCard_Individual.js | 26 ++++-- .../UserInformationCard_Individual_Pub.js | 88 +------------------ 2 files changed, 24 insertions(+), 90 deletions(-) diff --git a/src/pages/User/DetailsPage_Individual/UserInformationCard_Individual.js b/src/pages/User/DetailsPage_Individual/UserInformationCard_Individual.js index 5e69bdf..89b6bb5 100644 --- a/src/pages/User/DetailsPage_Individual/UserInformationCard_Individual.js +++ b/src/pages/User/DetailsPage_Individual/UserInformationCard_Individual.js @@ -80,7 +80,10 @@ const UserInformationCard_Individual = ({ formData, loadDataFun }) => { addressLine3: yup.string().max(40, getMaxErrStr(40)).nullable(), emailAddress: yup.string().email(intl.formatMessage({ id: 'validEmailFormat' })).max(255).required(intl.formatMessage({ id: 'requireEmail' })), idDocType: yup.string().max(255, getMaxErrStr(255)).required(intl.formatMessage({ id: 'requireIdDocType' })), - identification: yup.string().required(getRequiredErrStr('number')) + identification: yup.string().when('verifiedBy', { + is: (verifiedBy) => verifiedBy != null, + then: (schema) => schema.notRequired(), + otherwise: (schema) => schema.required(getRequiredErrStr('number')) .matches(/^[aA-zZ0-9\s]+$/, { message: displayErrorMsg(`${selectedIdDocInputType}${intl.formatMessage({ id: 'noSpecialCharacter' })}`) }) .matches(/^\S*$/, { message: displayErrorMsg(`${selectedIdDocInputType}${intl.formatMessage({ id: 'noSpace' })}`) }) .test('checkIDCardFormat', displayErrorMsg(`${intl.formatMessage({ id: 'requiredValid' })}${selectedIdDocInputType}${intl.formatMessage({ id: 'number' })}`), function (value) { @@ -132,8 +135,12 @@ const UserInformationCard_Individual = ({ formData, loadDataFun }) => { break; } } + }) }), - checkDigit: yup.string().max(1, getMaxErrStr(1)).nullable() + checkDigit: yup.string().when('verifiedBy', { + is: (verifiedBy) => verifiedBy != null, + then: (schema) => schema.notRequired(), + otherwise: (schema) => schema.max(1, getMaxErrStr(1)).nullable() .matches(/^[A-Z0-9\s]+$/, { message: displayErrorMsg(`${selectedIdDocInputType}${intl.formatMessage({ id: 'noSpecialCharacter' })}`) }) .test('checkIDCardFormat', displayErrorMsg(`${intl.formatMessage({ id: 'requiredNumberInQuote' })}`), function (value) { // console.log(selectedIdDocInputType) @@ -161,7 +168,8 @@ const UserInformationCard_Individual = ({ formData, loadDataFun }) => { return false } } - }), + }) + }), tel_countryCode: yup.string().min(3, intl.formatMessage({ id: 'require3Number' })).required(intl.formatMessage({ id: 'requireDialingCode' })), fax_countryCode: yup.string().min(3, intl.formatMessage({ id: 'require3Number' })), phoneNumber: yup.string().min(8, intl.formatMessage({ id: 'require8Number' })).required(intl.formatMessage({ id: 'requireContactNumber' })), @@ -185,8 +193,10 @@ const UserInformationCard_Individual = ({ formData, loadDataFun }) => { countryCode: values.tel_countryCode, phoneNumber: values.phoneNumber }, - identification: values.identification, - checkDigit: values.checkDigit, + ...(values.verifiedBy == null ? { + identification: values.identification, + checkDigit: values.checkDigit, + } : {}), faxNo: { countryCode: values.fax_countryCode, faxNumber: values.faxNumber @@ -470,6 +480,11 @@ const UserInformationCard_Individual = ({ formData, loadDataFun }) => {
+ {currentUserData.verifiedBy ? + + Hidden for security purpose + + : {formik.values.idDocType === "HKID" ? editMode ? @@ -548,6 +563,7 @@ const UserInformationCard_Individual = ({ formData, loadDataFun }) => {
} + } diff --git a/src/pages/User/DetailsPage_Individual/UserInformationCard_Individual_Pub.js b/src/pages/User/DetailsPage_Individual/UserInformationCard_Individual_Pub.js index 61e6c31..ec746c1 100644 --- a/src/pages/User/DetailsPage_Individual/UserInformationCard_Individual_Pub.js +++ b/src/pages/User/DetailsPage_Individual/UserInformationCard_Individual_Pub.js @@ -2,8 +2,6 @@ import { Grid, Button, Typography, FormHelperText, - Stack, - IconButton } from '@mui/material'; import MainCard from "components/MainCard"; import * as React from "react"; @@ -22,7 +20,6 @@ import {notifySaveSuccess,} from 'utils/CommonFunction'; import {FormattedMessage, useIntl} from "react-intl"; import {PNSPS_BUTTON_THEME} from "../../../themes/buttonConst"; import {ThemeProvider} from "@emotion/react"; -import { EyeInvisibleOutlined, EyeOutlined } from '@ant-design/icons'; // ==============================|| DASHBOARD - DEFAULT ||============================== // @@ -33,15 +30,6 @@ const UserInformationCard_Individual_Pub = ({ formData, loadDataFun }) => { const [editMode, setEditMode] = useState(false); const [onReady, setOnReady] = useState(false); const [errorMsg, setErrorMsg] = useState(""); - const [showId, setshowId] = useState(false); - - const handleClickShowId = () => { - setshowId(!showId); - }; - - const handleMouseDownId = (event) => { - event.preventDefault(); - }; useEffect(() => { if (Object.keys(formData).length > 0) { @@ -251,79 +239,9 @@ const UserInformationCard_Individual_Pub = ({ formData, loadDataFun }) => { - - {formik.values.idDocType === "HKID" ? - // <> - // - // {FieldUtils.initField({ - // valueName: "identification", - // disabled: true, - // form: formik, - // placeholder: intl.formatMessage({id: 'idDocNumber'}), - // inputProps: { - // maxLength: 7, - // onKeyDown: (e) => { - // if (e.key === 'Enter') { - // e.preventDefault(); - // } - // }, - // } - // })} - - // - // - // {FieldUtils.initField({ - // valueName: "checkDigit", - // disabled: true, - // form: formik, - // })} - // - // - - - {formik.values.identification?.slice(0, 4)} - - - {/* {showId ?formik.values.identification.slice(4):"****"}{showId ? '(' + formik.values.checkDigit + ')' :null} */} - {showId ? formik.values.identification?.slice(4) : "****"}{showId ? formik.values.checkDigit?'(' +formik.values.checkDigit+ ')': "()" : ""} - - - {showId ? : } - - - : - // - // {FieldUtils.initField({ - // valueName: "identification", - // disabled: true, - // form: formik - // })} - // - - - {formik.values.identification?.slice(0, 4)} - - - {showId ?formik.values.identification?.slice(4):"****"} - - - {showId ? : } - - - } - + + {formik.values.identification?.slice(0, 4)} + From ad367709fd5d2f8883d5db485dbf0e7e66351db0 Mon Sep 17 00:00:00 2001 From: Jason Chuang Date: Wed, 17 Jun 2026 22:42:51 +0800 Subject: [PATCH 09/50] CR-024 Item 3 --- src/layout/MainLayout/Header/index.js | 14 ++ src/pages/Setting/HkidKeyMigration/index.js | 101 ++++++++++++++ src/pages/Setting/UserPiiEncryption/index.js | 133 +++++++++++++++++++ src/routes/GLDUserRoutes.js | 14 ++ src/utils/ApiPathConst.js | 3 + 5 files changed, 265 insertions(+) create mode 100644 src/pages/Setting/HkidKeyMigration/index.js create mode 100644 src/pages/Setting/UserPiiEncryption/index.js diff --git a/src/layout/MainLayout/Header/index.js b/src/layout/MainLayout/Header/index.js index 335cc59..7f18891 100644 --- a/src/layout/MainLayout/Header/index.js +++ b/src/layout/MainLayout/Header/index.js @@ -516,6 +516,20 @@ function Header(props) { {isGranted("MAINTAIN_SETTING") ? ( <> +
  • + + + HKID Key Migration + + +
  • +
  • + + + User PII Encryption + + +
  • diff --git a/src/pages/Setting/HkidKeyMigration/index.js b/src/pages/Setting/HkidKeyMigration/index.js new file mode 100644 index 0000000..15d091f --- /dev/null +++ b/src/pages/Setting/HkidKeyMigration/index.js @@ -0,0 +1,101 @@ +import * as React from "react"; +import * as HttpUtils from "utils/HttpUtils"; +import * as DateUtils from "utils/DateUtils"; +import * as UrlUtils from "utils/ApiPathConst"; + +import { + Grid, Typography, Button, + Stack, Box, CircularProgress, +} from '@mui/material'; +import { notifyActionError } from 'utils/CommonFunction'; + +import titleBackgroundImg from 'assets/images/dashboard/gazette-bar.png' + +const formatLog = (responData) => { + if (responData?.msg && !responData?.log) { + return <>{DateUtils.datetimeStr(new Date())}
    Error
    {responData.msg}; + } + const statusColor = responData?.success ? "green" : "red"; + const statusText = responData?.success ? "Success" : "Completed with errors"; + const logText = (responData?.log || []).join("\n"); + return ( + <> + {DateUtils.datetimeStr(new Date())}
    + {responData?.dryRun ? "Dry-run " : ""}{statusText}
    + Total: {responData?.total ?? 0}, OK: {responData?.successCount ?? 0}, Skipped: {responData?.skipped ?? 0}, Failed: {responData?.failed ?? 0}
    + {logText} + + ); +}; + +const HkidKeyMigration = () => { + const [resultStr, setResultStr] = React.useState(""); + const [wait, setWait] = React.useState(false); + + const BackgroundHead = { + backgroundImage: `url(${titleBackgroundImg})`, + width: 'auto', + height: 'auto', + backgroundSize: 'contain', + backgroundRepeat: 'no-repeat', + backgroundColor: '#0C489E', + backgroundPosition: 'right' + }; + + const runMigration = (dryRun) => { + setWait(true); + HttpUtils.post({ + url: `${UrlUtils.HKID_REKEY_MIGRATION}?dryRun=${dryRun}&batchSize=100`, + params: {}, + onSuccess: function (responData) { + setWait(false); + setResultStr(formatLog(responData)); + }, + onError: function () { + setWait(false); + notifyActionError("HKID re-key migration failed"); + } + }); + }; + + return ( + + +
    + + + HKID Key Migration + + +
    +
    + + + + + Re-encrypt identification and checkDigit from the legacy key to the new key. + Ensure Tomcat has both security.hkid-secret and security.hkid-secret-legacy configured before running. + + + + + {wait ? : null} + + + + + + + Result: + {resultStr} + + +
    + ); +}; + +export default HkidKeyMigration; diff --git a/src/pages/Setting/UserPiiEncryption/index.js b/src/pages/Setting/UserPiiEncryption/index.js new file mode 100644 index 0000000..e1e17b9 --- /dev/null +++ b/src/pages/Setting/UserPiiEncryption/index.js @@ -0,0 +1,133 @@ +import * as React from "react"; +import * as HttpUtils from "utils/HttpUtils"; +import * as DateUtils from "utils/DateUtils"; +import * as UrlUtils from "utils/ApiPathConst"; + +import { + Grid, Typography, Button, + Stack, Box, CircularProgress, +} from '@mui/material'; +import { notifyActionError } from 'utils/CommonFunction'; + +import titleBackgroundImg from 'assets/images/dashboard/gazette-bar.png' + +const formatEncryptLog = (responData) => { + if (responData?.msg && !responData?.log) { + return <>{DateUtils.datetimeStr(new Date())}
    Error
    {responData.msg}; + } + const statusColor = responData?.success ? "green" : "red"; + const statusText = responData?.success ? "Success" : "Completed with errors"; + const logText = (responData?.log || []).join("\n"); + return ( + <> + {DateUtils.datetimeStr(new Date())}
    + {responData?.dryRun ? "Dry-run " : ""}{statusText}
    + Total: {responData?.total ?? 0}, OK: {responData?.successCount ?? 0}, Skipped: {responData?.skipped ?? 0}, Failed: {responData?.failed ?? 0}
    + {logText} + + ); +}; + +const formatVerifyLog = (responData) => { + const tests = responData?.tests || []; + const logText = tests.map(t => `${t.passed ? "PASS" : "FAIL"} - ${t.name}: ${t.message}`).join("\n"); + const statusColor = responData?.success ? "green" : "red"; + return ( + <> + {DateUtils.datetimeStr(new Date())}
    + {responData?.success ? "All tests passed" : "Some tests failed"}
    + Passed: {responData?.passed ?? 0}, Failed: {responData?.failed ?? 0}
    + {logText} + + ); +}; + +const UserPiiEncryption = () => { + const [resultStr, setResultStr] = React.useState(""); + const [wait, setWait] = React.useState(false); + + const BackgroundHead = { + backgroundImage: `url(${titleBackgroundImg})`, + width: 'auto', + height: 'auto', + backgroundSize: 'contain', + backgroundRepeat: 'no-repeat', + backgroundColor: '#0C489E', + backgroundPosition: 'right' + }; + + const runEncrypt = (dryRun) => { + setWait(true); + HttpUtils.post({ + url: `${UrlUtils.USER_PII_ENCRYPT_MIGRATION}?dryRun=${dryRun}&batchSize=100`, + params: {}, + onSuccess: function (responData) { + setWait(false); + setResultStr(formatEncryptLog(responData)); + }, + onError: function () { + setWait(false); + notifyActionError("User PII encryption failed"); + } + }); + }; + + const runVerify = () => { + setWait(true); + HttpUtils.post({ + url: UrlUtils.USER_PII_VERIFY_MIGRATION, + params: {}, + onSuccess: function (responData) { + setWait(false); + setResultStr(formatVerifyLog(responData)); + }, + onError: function () { + setWait(false); + notifyActionError("User PII verification failed"); + } + }); + }; + + return ( + + +
    + + + User PII Encryption + + +
    +
    + + + + + Encrypt enName, chName, mobileNumber, and address for existing user records, then verify data retrieval. + + + + + + {wait ? : null} + + + + + + + Result: + {resultStr} + + +
    + ); +}; + +export default UserPiiEncryption; diff --git a/src/routes/GLDUserRoutes.js b/src/routes/GLDUserRoutes.js index a4a059c..3f893ef 100644 --- a/src/routes/GLDUserRoutes.js +++ b/src/routes/GLDUserRoutes.js @@ -30,6 +30,8 @@ const EmailTemplateDetailPage = Loadable(lazy(() => import('pages/EmailTemplate/ const HolidayPage = Loadable(lazy(() => import('pages/Holiday'))); const GazetteIssuePage = Loadable(lazy(() => import('pages/GazetteIssue/index'))); const DrImport = Loadable(lazy(() => import('pages/Setting/DrImport'))); +const HkidKeyMigration = Loadable(lazy(() => import('pages/Setting/HkidKeyMigration'))); +const UserPiiEncryption = Loadable(lazy(() => import('pages/Setting/UserPiiEncryption'))); const AuditLogPage = Loadable(lazy(() => import('pages/AuditLog/index'))); const ReconReportPage = Loadable(lazy(() => import('pages/Recon'))); const ChangePasswordPage = Loadable(lazy(() => import('pages/User/ChangePasswordPage'))); @@ -190,6 +192,18 @@ const GLDUserRoutes = { element: }:{}, + isGranted("MAINTAIN_SETTING")? + { + path: '/setting/hkidKeyMigration', + element: + }:{}, + + isGranted("MAINTAIN_SETTING")? + { + path: '/setting/userPiiEncryption', + element: + }:{}, + isGranted("MAINTAIN_SETTING")? { path: '/setting/auditLog', diff --git a/src/utils/ApiPathConst.js b/src/utils/ApiPathConst.js index d803a13..161dff9 100644 --- a/src/utils/ApiPathConst.js +++ b/src/utils/ApiPathConst.js @@ -84,6 +84,9 @@ export const GET_FILE_DELETE = apiPath+'/file/delete'; export const DR_EXPORT = apiPath+'/settings/dr/export'; export const DR_IMPORT = apiPath+'/settings/dr/import'; export const OFFLINE_IMPORT = apiPath+'/settings/dr/importOffline'; +export const HKID_REKEY_MIGRATION = apiPath+'/settings/migration/hkid-rekey'; +export const USER_PII_ENCRYPT_MIGRATION = apiPath+'/settings/migration/user-pii-encrypt'; +export const USER_PII_VERIFY_MIGRATION = apiPath+'/settings/migration/user-pii-verify'; export const AUDIT_LOG_EXPORT = apiPath+'/settings/auditLog-export'; From 1376d65ad4edfdbe7a96a017a591f4b111aacc97 Mon Sep 17 00:00:00 2001 From: Jason Chuang Date: Fri, 26 Jun 2026 02:15:23 +0800 Subject: [PATCH 10/50] register form - upload file and tabindex fix --- .../Proof/Create_FromApp/UploadFileTable.js | 46 ++++++++++--------- .../auth-forms/BusCustomFormWizard.js | 16 ++++++- .../auth-forms/CustomFormWizard.js | 18 +++++++- .../auth-forms/UploadFileTable.js | 5 +- src/routes/GLDUserRoutes.js | 2 +- src/routes/PublicUserRoutes.js | 2 +- src/translations/en.json | 1 + src/translations/zh-CN.json | 1 + src/translations/zh-HK.json | 1 + src/utils/CommonFunction.js | 7 +++ 10 files changed, 70 insertions(+), 29 deletions(-) diff --git a/src/pages/Proof/Create_FromApp/UploadFileTable.js b/src/pages/Proof/Create_FromApp/UploadFileTable.js index 824637b..e264f79 100644 --- a/src/pages/Proof/Create_FromApp/UploadFileTable.js +++ b/src/pages/Proof/Create_FromApp/UploadFileTable.js @@ -7,6 +7,7 @@ import { } from "@mui/x-data-grid"; import RemoveCircleOutlineIcon from '@mui/icons-material/RemoveCircleOutline'; import { useEffect } from "react"; +import { useIntl } from 'react-intl'; // import {useNavigate} from "react-router-dom"; // import { useTheme } from '@mui/material/styles'; import { @@ -17,6 +18,8 @@ import { // ==============================|| EVENT TABLE ||============================== // export default function UploadFileTable({ recordList, setRecordList, showPageColumn, _checkCode, _expectedCode }) { + const intl = useIntl(); + const deleteFileLabel = intl.formatMessage({ id: 'ariaDeleteFile' }); const [rows, setRows] = React.useState(recordList); const [rowModesModel, setRowModesModel] = React.useState({}); @@ -67,6 +70,25 @@ export default function UploadFileTable({ recordList, setRecordList, showPageCol setRows(rows.filter((row) => row.id !== id)); } + const renderDeleteAction = (id) => ( + } + label={deleteFileLabel} + title={deleteFileLabel} + className="textPrimary" + onClick={handleCancelClick(id)} + onKeyDown={(event) => { + if (event.key === 'Enter' || event.key === ' ') { + event.preventDefault(); + event.stopPropagation(); + handleCancelClick(id)(event); + } + }} + color="error" + /> + ); + const handlePreviewClick = (param) => () => { var reader = new FileReader(); reader.onload = function () { @@ -94,17 +116,7 @@ export default function UploadFileTable({ recordList, setRecordList, showPageCol width: 30, cellClassName: 'actions', // hide:true, - getActions: ({ id }) => { - return [ - } - label="delete" - className="textPrimary" - onClick={handleCancelClick(id)} - color="error" - />] - }, + getActions: ({ id }) => [renderDeleteAction(id)], }, { field: 'name', @@ -139,17 +151,7 @@ export default function UploadFileTable({ recordList, setRecordList, showPageCol width: 30, cellClassName: 'actions', // hide:true, - getActions: ({ id }) => { - return [ - } - label="delete" - className="textPrimary" - onClick={handleCancelClick(id)} - color="error" - />] - }, + getActions: ({ id }) => [renderDeleteAction(id)], }, { field: 'name', diff --git a/src/pages/authentication/auth-forms/BusCustomFormWizard.js b/src/pages/authentication/auth-forms/BusCustomFormWizard.js index 4b61337..dcfa428 100644 --- a/src/pages/authentication/auth-forms/BusCustomFormWizard.js +++ b/src/pages/authentication/auth-forms/BusCustomFormWizard.js @@ -55,7 +55,7 @@ import {PNSPS_LONG_BUTTON_THEME} from "../../../themes/buttonConst"; import {ThemeProvider} from "@emotion/react"; import {FormattedMessage, useIntl} from "react-intl"; //import { Invaild } from 'utils/IconUtils'; -import { notifyActionError } from 'utils/CommonFunction'; +import { handleActionKeyDown, notifyActionError } from 'utils/CommonFunction'; // ============================|| FIREBASE - REGISTER ||============================ // @@ -822,10 +822,12 @@ const BusCustomFormWizard = (props) => { handleActionKeyDown(event, handleClickShowPassword)} onMouseDown={handleMouseDownPassword} edge="end" size="large" @@ -899,10 +901,12 @@ const BusCustomFormWizard = (props) => { handleActionKeyDown(event, handleClickShowConfirmPassword)} onMouseDown={handleMouseDownPassword} edge="end" size="large" @@ -1546,6 +1550,7 @@ const BusCustomFormWizard = (props) => { variant="contained" sx={{ height: '40px' }} type="button" + tabIndex={0} onClick={() => { if (fileInputRef.current) { fileInputRef.current.click(); @@ -1659,7 +1664,14 @@ const BusCustomFormWizard = (props) => { - { onCaptchaChange() }}> + { onCaptchaChange() }} + onKeyDown={(event) => handleActionKeyDown(event, () => onCaptchaChange())} + > diff --git a/src/pages/authentication/auth-forms/CustomFormWizard.js b/src/pages/authentication/auth-forms/CustomFormWizard.js index 0e31fa3..e5676b8 100644 --- a/src/pages/authentication/auth-forms/CustomFormWizard.js +++ b/src/pages/authentication/auth-forms/CustomFormWizard.js @@ -47,7 +47,7 @@ import { FormattedMessage, useIntl } from "react-intl"; import { Link } from 'react-router-dom'; import { PNSPS_LONG_BUTTON_THEME } from "../../../themes/buttonConst"; import * as HttpUtils from "../../../utils/HttpUtils"; -import { notifyActionError } from 'utils/CommonFunction'; +import { handleActionKeyDown, notifyActionError } from 'utils/CommonFunction'; // ============================|| FIREBASE - REGISTER ||============================ // const CustomFormWizard = (props) => { @@ -1040,10 +1040,12 @@ const CustomFormWizard = (props) => { handleActionKeyDown(event, handleClickShowPassword)} onMouseDown={handleMouseDownPassword} edge="end" size="large" @@ -1117,10 +1119,12 @@ const CustomFormWizard = (props) => { handleActionKeyDown(event, handleClickShowConfirmPassword)} onMouseDown={handleMouseDownPassword} edge="end" size="large" @@ -1874,6 +1878,7 @@ const CustomFormWizard = (props) => { variant="contained" sx={{ height: '40px' }} type="button" + tabIndex={0} onClick={() => { if (fileInputRef.current) { fileInputRef.current.click(); @@ -1987,7 +1992,14 @@ const CustomFormWizard = (props) => { - { onCaptchaChange() }}> + { onCaptchaChange() }} + onKeyDown={(event) => handleActionKeyDown(event, () => onCaptchaChange())} + > @@ -2116,8 +2128,10 @@ const CustomFormWizard = (props) => { handleActionKeyDown(event, handleClickShowId)} onMouseDown={handleMouseDownId} edge="end" size="medium" diff --git a/src/pages/authentication/auth-forms/UploadFileTable.js b/src/pages/authentication/auth-forms/UploadFileTable.js index 92bbc5a..537300e 100644 --- a/src/pages/authentication/auth-forms/UploadFileTable.js +++ b/src/pages/authentication/auth-forms/UploadFileTable.js @@ -14,6 +14,7 @@ import { Typography } from '@mui/material'; import { FormattedMessage, useIntl } from "react-intl"; +import { handleActionKeyDown } from 'utils/CommonFunction'; // ==============================|| EVENT TABLE ||============================== // export default function UploadFileTable({ recordList, setUpdateRows, }) { @@ -74,8 +75,10 @@ export default function UploadFileTable({ recordList, setUpdateRows, }) { size="small" color="error" role="button" - aria-label={intl.formatMessage({ id: 'delete' })} + tabIndex={0} + aria-label={intl.formatMessage({ id: 'ariaDeleteFile' })} onClick={() => handleCancelClick(row.id)} + onKeyDown={(event) => handleActionKeyDown(event, () => handleCancelClick(row.id))} > diff --git a/src/routes/GLDUserRoutes.js b/src/routes/GLDUserRoutes.js index e096bb3..a4a059c 100644 --- a/src/routes/GLDUserRoutes.js +++ b/src/routes/GLDUserRoutes.js @@ -7,7 +7,7 @@ const MainLayout = Loadable(lazy(() => import('layout/MainLayout'))); import {isGranted, isGrantedAny} from "auth/utils"; // import { isPasswordExpiry } from "utils/Utils"; // render - dashboard -const DashboardDefault = Loadable(lazy(() => import('pages/Dashboard/GLD'))); +const DashboardDefault = Loadable(lazy(() => import('pages/dashboard/GLD'))); const ApplicationDetail = Loadable(lazy(() => import('pages/PublicNotice/Details_GLD'))); const ApplicationSearch = Loadable(lazy(() => import('pages/PublicNotice/Search_GLD'))); const ApplicationMarkAsPaidSearch = Loadable(lazy(() => import('pages/PublicNotice/Search_Mark_As_Paid_GLD'))); diff --git a/src/routes/PublicUserRoutes.js b/src/routes/PublicUserRoutes.js index f12c742..c3f614d 100644 --- a/src/routes/PublicUserRoutes.js +++ b/src/routes/PublicUserRoutes.js @@ -7,7 +7,7 @@ const MainLayout = Loadable(lazy(() => import('layout/MainLayout'))); // import { isPasswordExpiry } from "utils/Utils"; // render - dashboard -const DashboardDefault = Loadable(lazy(() => import('pages/Dashboard/Public'))); +const DashboardDefault = Loadable(lazy(() => import('pages/dashboard/Public'))); const ManageOrgUser = Loadable(lazy(() => import('pages/User/ManagePage_OrgPublic'))); const PublicNotice = Loadable(lazy(() => import('pages/PublicNotice/ListPanel'))); const PublicNoticeApplyForm = Loadable(lazy(() => import('pages/PublicNotice/ApplyForm'))); diff --git a/src/translations/en.json b/src/translations/en.json index ee19f9b..1072e14 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -652,6 +652,7 @@ "ariaApplicationGroup": "Application Group", "ariaProfileTabs": "Profile tabs", "ariaUploadPdfFile": "Upload PDF file", + "ariaDeleteFile": "Delete file", "ariaExportHolidayTemplate": "Export holiday template", "ariaUploadExcelFile": "Upload Excel file (.xlsx)", "ariaBackToHome": "Back to home", diff --git a/src/translations/zh-CN.json b/src/translations/zh-CN.json index 2cf4712..4a62172 100644 --- a/src/translations/zh-CN.json +++ b/src/translations/zh-CN.json @@ -648,6 +648,7 @@ "ariaApplicationGroup": "申请组别", "ariaProfileTabs": "个人资料选项卡", "ariaUploadPdfFile": "上传 PDF 文件", + "ariaDeleteFile": "删除文件", "ariaExportHolidayTemplate": "导出假期模板", "ariaUploadExcelFile": "上传 Excel 文件 (.xlsx)", "ariaBackToHome": "返回主页", diff --git a/src/translations/zh-HK.json b/src/translations/zh-HK.json index d145a57..a4c8423 100644 --- a/src/translations/zh-HK.json +++ b/src/translations/zh-HK.json @@ -649,6 +649,7 @@ "ariaApplicationGroup": "申請組別", "ariaProfileTabs": "個人檔案分頁", "ariaUploadPdfFile": "上載 PDF 檔案", + "ariaDeleteFile": "刪除檔案", "ariaExportHolidayTemplate": "匯出假期範本", "ariaUploadExcelFile": "上載 Excel 檔案 (.xlsx)", "ariaBackToHome": "返回主頁", diff --git a/src/utils/CommonFunction.js b/src/utils/CommonFunction.js index 6dc4180..a2edd83 100644 --- a/src/utils/CommonFunction.js +++ b/src/utils/CommonFunction.js @@ -11,6 +11,13 @@ export const clickableLink=(link, label)=> { return {label}; } +export const handleActionKeyDown = (event, action) => { + if (event.key === 'Enter' || event.key === ' ') { + event.preventDefault(); + action(event); + } +}; + export function getDeletedRecordWithRefList(referenceList, updatedList) { return referenceList.filter(x => !updatedList.includes(x)); } From 4f2cc3469b75ede22515d948b07353d151121192 Mon Sep 17 00:00:00 2001 From: Jason Chuang Date: Fri, 26 Jun 2026 03:20:05 +0800 Subject: [PATCH 11/50] various update --- src/assets/style/navbarStyles.css | 18 ++-- src/assets/style/styles.css | 8 +- src/components/AdminLogo/index.js | 4 +- src/components/MobileLogo/index.js | 2 +- .../Header/HeaderContent/LocaleSelector.js | 4 +- .../Header/HeaderContent/Notification.js | 2 +- src/layout/MainLayout/Header/index.js | 18 ++-- src/layout/MainLayout/index.js | 2 +- src/pages/Announcement/Search/index.js | 2 +- src/pages/Announcement/Search_Public/index.js | 2 +- src/pages/AuditLog/index.js | 2 +- src/pages/DemandNote/Create/index.js | 2 +- src/pages/DemandNote/Export/index.js | 2 +- src/pages/DemandNote/Search/DataGrid.js | 2 + src/pages/DemandNote/Search/index.js | 2 +- src/pages/GFMIS/index.js | 2 +- src/pages/JVM/index.js | 2 +- src/pages/Message/Details/index.js | 3 +- src/pages/Organization/SearchPage/index.js | 2 +- src/pages/Payment/Search_GLD/index.js | 2 +- src/pages/Proof/Create_FromApp/index.js | 2 +- src/pages/Proof/Reply_Public/ProofForm.js | 4 +- src/pages/Proof/Search_GLD/index.js | 2 +- .../ApplyForm/PublicNoticeApplyForm.js | 4 +- .../Details_GLD/ApplicationDetailCard.js | 4 +- .../Details_Public/ApplicationDetailCard.js | 8 +- .../PublicNotice/Details_Public/index.js | 3 +- src/pages/PublicNotice/ListPanel/BaseGrid.js | 3 +- .../ListPanel/PendingPaymentTab.js | 3 +- .../ListPanel/SearchPublicNoticeTable.js | 2 + src/pages/PublicNotice/Search_GLD/index.js | 2 +- .../Search_Mark_As_Paid_GLD/index.js | 2 +- src/pages/Recon/index.js | 2 +- src/pages/Report/FullList/index.js | 2 +- src/pages/Report/Summary/index.js | 2 +- src/pages/Setting/DrImport/index.js | 3 + src/pages/User/DetailPage/index.js | 2 +- src/pages/User/ManagePage_OrgPublic/index.js | 31 ++++--- src/pages/User/SearchPage/index.js | 2 +- src/pages/User/SearchPage_Individual/index.js | 2 +- .../User/SearchPage_Organization/index.js | 2 +- src/pages/authentication/AuthWrapper.js | 2 +- src/pages/authentication/BusRegister.js | 6 +- src/pages/authentication/IAmSmartRegister.js | 6 +- src/pages/authentication/Register.js | 6 +- src/pages/authentication/RegisterCustom.js | 16 ++-- .../auth-forms/AuthLoginCustom.js | 4 +- .../auth-forms/BusCustomFormWizard.js | 6 +- .../auth-forms/CustomFormWizard.js | 6 +- .../auth-forms/IAmSmartFormWizard.js | 6 +- src/pages/dashboard/GLD/index.js | 2 +- src/pages/dashboard/Public/index.js | 10 +-- src/pages/extra-pages/UserMenuPub1/index.js | 2 +- src/pages/pnspsUserGroupDetailPage/index.js | 2 +- src/pages/pnspsUserGroupSearchPage/index.js | 2 +- src/themes/buttonConst.js | 49 +++++++++- src/themes/colorConst.js | 34 +++++++ src/themes/overrides/Button.js | 24 +++-- src/themes/overrides/Checkbox.js | 17 ++++ src/themes/overrides/InputLabel.js | 13 ++- src/themes/overrides/OutlinedInput.js | 18 ++-- src/themes/overrides/Radio.js | 30 +++++++ src/themes/overrides/Tab.js | 11 ++- src/themes/overrides/index.js | 2 + src/themes/themeConst.js | 90 ++++++++++++++++--- .../statusUtils/PublicNoteStatusUtils.js | 2 +- 66 files changed, 393 insertions(+), 143 deletions(-) create mode 100644 src/themes/overrides/Radio.js diff --git a/src/assets/style/navbarStyles.css b/src/assets/style/navbarStyles.css index dea1992..c718620 100644 --- a/src/assets/style/navbarStyles.css +++ b/src/assets/style/navbarStyles.css @@ -11,7 +11,7 @@ top: 0px; width: 100%; z-index: 9999; - border-bottom: 3px solid #0C489E; + border-bottom: 3px solid #1976d2; min-height: 77px; } @@ -112,7 +112,7 @@ #navbar div li a:hover, #navbar div li button.navTrigger:hover{ - color: #0C489E; + color: #1976d2; } #navbar div li a:hover::after, @@ -122,7 +122,7 @@ content: ""; width: 80%; height: 3px; - background:#0C489E; + background:#1976d2; position: absolute; bottom: -5px; left: 20px; @@ -166,7 +166,7 @@ #navbar a:focus-visible, #navbar button.navTrigger:focus-visible{ - outline: 3px solid #0C489E; + outline: 3px solid #1976d2; outline-offset: 2px; border-radius: 10px; } @@ -234,7 +234,7 @@ text-decoration: none; font-size: 1.1rem; font-weight: 600; - color: #0C489E; + color: #1976d2; transition: 0.3s ease-in-out; text-align: center; } @@ -242,7 +242,7 @@ text-decoration: none; font-size: 1.1rem; font-weight: 600; - color: #0C489E; + color: #1976d2; transition: 0.3s ease-in-out; text-align: center; } @@ -284,7 +284,7 @@ transition: 0.3s ease-in-out; } #sidebar li a:hover{ - color: #0C489E; + color: #1976d2; } #sidebar div li ul{ background: white; @@ -344,12 +344,12 @@ /* Hover / focus */ #sidebar li > a:hover, #sidebar li > button.navTrigger:hover{ - color: #0C489E; + color: #1976d2; } #sidebar li > a:focus-visible, #sidebar li > button.navTrigger:focus-visible{ - outline: 3px solid #0C489E; + outline: 3px solid #1976d2; outline-offset: 2px; border-radius: 10px; } diff --git a/src/assets/style/styles.css b/src/assets/style/styles.css index a675de2..f1ee2c6 100644 --- a/src/assets/style/styles.css +++ b/src/assets/style/styles.css @@ -72,7 +72,7 @@ a:active { [role="link"], [tabindex]:not([tabindex="-1"]) ):focus-visible { - outline: 3px solid #0C489E; + outline: 3px solid #1976d2; outline-offset: 2px; border-radius: 4px; } @@ -95,9 +95,9 @@ 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: 3px solid #1976d2; outline-offset: -2px; - box-shadow: 0 0 0 3px rgba(12, 72, 158, 0.25); + box-shadow: 0 0 0 3px rgba(25, 118, 210, 0.25); } /* Contained buttons only */ @@ -122,7 +122,7 @@ a:active { /* ===== Outlined button focus ===== */ .MuiButton-outlined:focus-visible { - outline: 3px solid #0C489E; + outline: 3px solid #1976d2; outline-offset: 2px; } diff --git a/src/components/AdminLogo/index.js b/src/components/AdminLogo/index.js index 4fa7a84..7b62ee8 100644 --- a/src/components/AdminLogo/index.js +++ b/src/components/AdminLogo/index.js @@ -34,7 +34,7 @@ const LogoSection = ({ sx, to }) => { ...sx, /* ✅ WCAG 2.4.7 focus indicator */ '&:focus-visible': { - outline: '3px solid #0C489E', + outline: '3px solid #1976d2', outlineOffset: '2px', borderRadius: '6px' } @@ -42,7 +42,7 @@ const LogoSection = ({ sx, to }) => { > - PNSPS + PNSPS diff --git a/src/components/MobileLogo/index.js b/src/components/MobileLogo/index.js index da2ff62..b5ebe8c 100644 --- a/src/components/MobileLogo/index.js +++ b/src/components/MobileLogo/index.js @@ -30,7 +30,7 @@ const LogoSection = ({ sx, to }) => { /* WCAG 2.4.7 – visible keyboard focus */ '&:focus-visible': { - outline: '3px solid #0C489E', + outline: '3px solid #1976d2', outlineOffset: '2px', borderRadius: '6px' } diff --git a/src/layout/MainLayout/Header/HeaderContent/LocaleSelector.js b/src/layout/MainLayout/Header/HeaderContent/LocaleSelector.js index 66d0b68..3d3aba6 100644 --- a/src/layout/MainLayout/Header/HeaderContent/LocaleSelector.js +++ b/src/layout/MainLayout/Header/HeaderContent/LocaleSelector.js @@ -64,7 +64,8 @@ const LocaleSelector = () => { }} aria-label={intl.formatMessage({id: 'openLanguage'})} ref={anchorRef} - aria-controls={open ? 'profile-grow' : undefined} + aria-controls={open ? 'locale-selector-menu' : undefined} + aria-expanded={open} aria-haspopup="true" onClick={handleToggle} > @@ -103,6 +104,7 @@ const LocaleSelector = () => { > diff --git a/src/layout/MainLayout/Header/HeaderContent/Notification.js b/src/layout/MainLayout/Header/HeaderContent/Notification.js index a300a0b..d486c28 100644 --- a/src/layout/MainLayout/Header/HeaderContent/Notification.js +++ b/src/layout/MainLayout/Header/HeaderContent/Notification.js @@ -77,7 +77,7 @@ const Notification = () => { /* ✅ WCAG 2.4.7 focus indicator */ '&:focus-visible': { - outline: '3px solid #0C489E', + outline: '3px solid #1976d2', outlineOffset: '2px', borderRadius: '6px' } diff --git a/src/layout/MainLayout/Header/index.js b/src/layout/MainLayout/Header/index.js index 335cc59..ce15f1e 100644 --- a/src/layout/MainLayout/Header/index.js +++ b/src/layout/MainLayout/Header/index.js @@ -775,7 +775,7 @@ function Header(props) { - + PNSPS @@ -794,7 +794,7 @@ function Header(props) { - + PNSPS @@ -826,13 +826,13 @@ function Header(props) { onClick={handleDrawerToggle} sx={{ mr: 2, display: { md: "none" } }} > - + - + PNSPS @@ -848,7 +848,7 @@ function Header(props) { - + @@ -863,14 +863,14 @@ function Header(props) { onClick={handleDrawerToggle} sx={{ mr: 2, display: { md: "none" } }} > - + - + @@ -946,14 +946,14 @@ function Header(props) { onClick={handleDrawerToggle} sx={{ mr: 2, display: { md: "none" } }} > - + - + diff --git a/src/layout/MainLayout/index.js b/src/layout/MainLayout/index.js index d5999b8..0e6e281 100644 --- a/src/layout/MainLayout/index.js +++ b/src/layout/MainLayout/index.js @@ -67,7 +67,7 @@ const MainLayout = () => { {/* */} - +