diff --git a/src/assets/style/styles.css b/src/assets/style/styles.css
index f438c04..3dc79aa 100644
--- a/src/assets/style/styles.css
+++ b/src/assets/style/styles.css
@@ -52,31 +52,18 @@ img
img:hover{-webkit-filter:none;}
-
-a:link {
- color: #1890ff;
- background-color: transparent;
- text-decoration: none;
-}
-a:visited {
- color: #1890ff;
- background-color: transparent;
- text-decoration: none;
-}
-a:hover {
- color: #1890ff;
- background-color: transparent;
- text-decoration: none;
-}
+a:link,
+a:visited,
+a:hover,
a:active {
- color: #1890ff;
+ color: #005FCC; /* WCAG AA compliant */
background-color: transparent;
- text-decoration: none;
+ text-decoration: underline;
}
/* iframe#webpack-dev-server-client-overlay{display:none!important} */
-/* ===== WCAG 2.4.7 Focus Visible (Global) ===== */
+/* ===== WCAG 2.4.7 Focus Visible (Keyboard only) ===== */
:where(
a,
button,
@@ -93,7 +80,7 @@ a:active {
border-radius: 4px;
}
-/* Fallback for browsers that don't support :focus-visible */
+/* Suppress mouse focus */
:where(
a,
button,
@@ -104,30 +91,60 @@ a:active {
[role="button"],
[role="link"],
[tabindex]:not([tabindex="-1"])
-):focus {
- outline: 3px solid #0C489E;
- outline-offset: 2px;
- border-radius: 4px;
+):focus:not(:focus-visible) {
+ outline: none;
}
-/* ===== MUI DataGrid focus visible (WCAG 2.4.7) ===== */
-
-/* Column headers */
-.MuiDataGrid-columnHeader:focus,
-.MuiDataGrid-columnHeader:focus-within {
+/* ===== MUI DataGrid keyboard focus (WCAG 2.4.7 / 2.4.11) ===== */
+.MuiDataGrid-columnHeader:focus-visible,
+.MuiDataGrid-cell:focus-visible {
outline: 3px solid #0C489E;
outline-offset: -2px;
+ box-shadow: 0 0 0 3px rgba(12, 72, 158, 0.25);
+}
+
+/* Contained buttons only */
+.MuiButton-contained {
+ border: 2px solid #0C489E;
+ box-shadow: none;
+}
+
+.MuiButton-contained:hover {
+ border: 2px solid #0C489E;
+}
+
+/* iAM Smart button — keep green border */
+.MuiButton-outlinedIAmSmart {
+ color: #2E7D32; /* green text */
+ border: 1px solid #2E7D32; /* keep your green */
+}
+
+.MuiButton-outlinedIAmSmart:hover {
+ color: #2E7D32; /* green text */
+ border: 1px solid #2E7D32;
+ background-color: rgba(46, 125, 50, 0.08); /* optional */
}
-/* Cells */
-.MuiDataGrid-cell:focus,
-.MuiDataGrid-cell:focus-within {
+/* ===== Outlined button focus ===== */
+.MuiButton-outlined:focus-visible {
outline: 3px solid #0C489E;
- outline-offset: -2px;
+ outline-offset: 2px;
}
-/* If outline is clipped, add halo */
-.MuiDataGrid-columnHeader:focus-within,
-.MuiDataGrid-cell:focus-within {
- box-shadow: 0 0 0 3px rgba(12, 72, 158, 0.25);
+/* Step icon number color (text inside the circle) */
+.MuiStepIcon-text {
+ fill: #1A1A1A; /* dark text, high contrast */
+}
+/* Placeholder text contrast */
+.MuiInputBase-input::placeholder {
+ color: #6B6B6B; /* passes 4.5:1 on white */
+ opacity: 1; /* IMPORTANT */
+}
+
+/* WCAG-safe error text color */
+.Mui-error,
+.MuiFormHelperText-root.Mui-error,
+.MuiTypography-errorMessage1 {
+ color: #B00020; /* dark red, AA compliant */
+ opacity: 1; /* remove faded appearance */
}
\ No newline at end of file
diff --git a/src/layout/MainLayout/Header/index.js b/src/layout/MainLayout/Header/index.js
index 7182086..c1dd25e 100644
--- a/src/layout/MainLayout/Header/index.js
+++ b/src/layout/MainLayout/Header/index.js
@@ -746,7 +746,7 @@ function Header(props) {
{/*公共啟事提交*/}
{/*及繳費系統*/}
-
+
diff --git a/src/pages/authentication/BusRegister.js b/src/pages/authentication/BusRegister.js
index 1648237..179ef5b 100644
--- a/src/pages/authentication/BusRegister.js
+++ b/src/pages/authentication/BusRegister.js
@@ -42,29 +42,28 @@ const BusRegister = () => {
];
const stepStyle = {
- width: {lg:"40%", md:"70%", xs:"100%"},
+ width: { lg: "40%", md: "70%", xs: "100%" },
boxShadow: 1,
backgroundColor: "#FFFFFF",
padding: 2,
+
+ /* Inactive (default) */
+ "& .MuiStepIcon-root": { color: "#757575", fontSize: "2rem" }, // darker grey
+ "& .MuiStepLabel-label": { color: "#424242" }, // label darker
+ "& .MuiStepConnector-line": { borderColor: "#9E9E9E" }, // connector darker
+
+ /* Active */
"& .Mui-active": {
- "&.MuiStepIcon-root": {
- color: "warning.main",
- fontSize: "2rem",
- },
- "& .MuiStepConnector-line": {
- borderColor: "warning.main"
- }
+ "&.MuiStepIcon-root": { color: "warning.main", fontSize: "2rem" },
+ "& .MuiStepConnector-line": { borderColor: "warning.main" }
},
+
+ /* Completed */
"& .Mui-completed": {
- "&.MuiStepIcon-root": {
- color: "secondary.main",
- fontSize: "2rem",
- },
- "& .MuiStepConnector-line": {
- borderColor: "secondary.main"
- }
+ "&.MuiStepIcon-root": { color: "#0C489E", fontSize: "2rem" }, // use your strong blue
+ "& .MuiStepConnector-line": { borderColor: "#0C489E" }
}
- }
+ };
const totalSteps = () => {
return steps.length;
@@ -153,7 +152,13 @@ const BusRegister = () => {
) :
(}
// onClick={handleStep(index)}
>
diff --git a/src/pages/authentication/IAmSmartRegister.js b/src/pages/authentication/IAmSmartRegister.js
index 322eecc..9a63104 100644
--- a/src/pages/authentication/IAmSmartRegister.js
+++ b/src/pages/authentication/IAmSmartRegister.js
@@ -26,30 +26,29 @@ import {FormattedMessage, useIntl} from "react-intl";
const CustomFormWizard = Loadable(lazy(() => import('./auth-forms/IAmSmartFormWizard')));
const AuthWrapper = Loadable(lazy(() => import('./AuthWrapperCustom')));
// ================================|| REGISTER ||================================ //
-const stepStyle = {
- width: {lg:"40%", md:"70%", xs:"100%"},
- boxShadow: 1,
- backgroundColor: "#FFFFFF",
- padding: 2,
- "& .Mui-active": {
- "&.MuiStepIcon-root": {
- color: "warning.main",
- fontSize: "2rem",
- },
- "& .MuiStepConnector-line": {
- borderColor: "warning.main"
- }
- },
- "& .Mui-completed": {
- "&.MuiStepIcon-root": {
- color: "secondary.main",
- fontSize: "2rem",
+ const stepStyle = {
+ width: { lg: "40%", md: "70%", xs: "100%" },
+ boxShadow: 1,
+ backgroundColor: "#FFFFFF",
+ padding: 2,
+
+ /* Inactive (default) */
+ "& .MuiStepIcon-root": { color: "#757575", fontSize: "2rem" }, // darker grey
+ "& .MuiStepLabel-label": { color: "#424242" }, // label darker
+ "& .MuiStepConnector-line": { borderColor: "#9E9E9E" }, // connector darker
+
+ /* Active */
+ "& .Mui-active": {
+ "&.MuiStepIcon-root": { color: "warning.main", fontSize: "2rem" },
+ "& .MuiStepConnector-line": { borderColor: "warning.main" }
},
- "& .MuiStepConnector-line": {
- borderColor: "secondary.main"
+
+ /* Completed */
+ "& .Mui-completed": {
+ "&.MuiStepIcon-root": { color: "#0C489E", fontSize: "2rem" }, // use your strong blue
+ "& .MuiStepConnector-line": { borderColor: "#0C489E" }
}
- }
-}
+ };
const Register = () => {
const [activeStep, setActiveStep] = useState(0);
@@ -151,7 +150,13 @@ const Register = () => {
{label}
) :
(}
// onClick={handleStep(index)}
>
diff --git a/src/pages/authentication/Register.js b/src/pages/authentication/Register.js
index e36e96b..9d542d1 100644
--- a/src/pages/authentication/Register.js
+++ b/src/pages/authentication/Register.js
@@ -42,29 +42,28 @@ const Register = () => {
];
const stepStyle = {
- width: {lg:"40%", md:"70%", xs:"100%"},
+ width: { lg: "40%", md: "70%", xs: "100%" },
boxShadow: 1,
backgroundColor: "#FFFFFF",
padding: 2,
+
+ /* Inactive (default) */
+ "& .MuiStepIcon-root": { color: "#757575", fontSize: "2rem" }, // darker grey
+ "& .MuiStepLabel-label": { color: "#424242" }, // label darker
+ "& .MuiStepConnector-line": { borderColor: "#9E9E9E" }, // connector darker
+
+ /* Active */
"& .Mui-active": {
- "&.MuiStepIcon-root": {
- color: "warning.main",
- fontSize: "2rem",
- },
- "& .MuiStepConnector-line": {
- borderColor: "warning.main"
- }
+ "&.MuiStepIcon-root": { color: "warning.main", fontSize: "2rem" },
+ "& .MuiStepConnector-line": { borderColor: "warning.main" }
},
+
+ /* Completed */
"& .Mui-completed": {
- "&.MuiStepIcon-root": {
- color: "secondary.main",
- fontSize: "2rem",
- },
- "& .MuiStepConnector-line": {
- borderColor: "secondary.main"
- }
+ "&.MuiStepIcon-root": { color: "#0C489E", fontSize: "2rem" }, // use your strong blue
+ "& .MuiStepConnector-line": { borderColor: "#0C489E" }
}
- }
+ };
const totalSteps = () => {
return steps.length;
@@ -155,7 +154,13 @@ const Register = () => {
) :
(}
// onClick={handleStep(index)}
>
diff --git a/src/pages/authentication/RegisterCustom.js b/src/pages/authentication/RegisterCustom.js
index 50cccd0..38eb1bb 100644
--- a/src/pages/authentication/RegisterCustom.js
+++ b/src/pages/authentication/RegisterCustom.js
@@ -134,9 +134,22 @@ const RegisterCustom = () => {
-
+
@@ -146,9 +159,23 @@ const RegisterCustom = () => {
-
+
diff --git a/src/pages/authentication/auth-forms/BusCustomFormWizard.js b/src/pages/authentication/auth-forms/BusCustomFormWizard.js
index 81401ff..51c45cd 100644
--- a/src/pages/authentication/auth-forms/BusCustomFormWizard.js
+++ b/src/pages/authentication/auth-forms/BusCustomFormWizard.js
@@ -30,7 +30,7 @@ import * as yup from 'yup';
import { strengthColorChi, strengthIndicator } from 'utils/password-strength';
// import {apiPath} from "auth/utils";
import axios from "axios";
-import { POST_PUBLIC_USER_REGISTER, POST_CAPTCHA, POST_USERNAME, POST_USER_EMAIL, POST_CAPTCHA_AUDIO } from "utils/ApiPathConst";
+import { POST_PUBLIC_USER_REGISTER, POST_CAPTCHA, POST_USERNAME, POST_USER_EMAIL } from "utils/ApiPathConst";
// import * as HttpUtils from 'utils/HttpUtils';
import * as ComboData from "utils/ComboData";
@@ -118,16 +118,6 @@ const BusCustomFormWizard = (props) => {
changePassword('');
}, []);
- const playCaptchaAudio = async () => {
- const resp = await axios.post(`${POST_CAPTCHA_AUDIO}`,
- { base64Url, lang: intl.locale },
- { responseType: "arraybuffer" }
- );
- const blob = new Blob([resp.data], { type: "audio/wav" });
- const url = URL.createObjectURL(blob);
- new Audio(url).play();
- };
-
const handleCheckUsername = async () => {
if (values?.username) {
const response = await axios.post(`${POST_USERNAME}`, {
@@ -193,21 +183,17 @@ const BusCustomFormWizard = (props) => {
}
}, [checkDistrictBlur])
- const handleCheckDistrict = () => {
+ const handleCheckDistrict = async () => {
setDistrictErrStr("");
-
if (selectedAddress5?.type === "hongKong") {
- if (!selectedAddress4 || Object.keys(selectedAddress4).length === 0) {
- setCheckDistrict(true);
- setDistrictErrStr(getRequiredErrStr("district"));
- return false;
+ if (selectedAddress4 == null || selectedAddress4 == "" || selectedAddress4 == {}){
+ setCheckDistrict(true)
+ setDistrictErrStr(getRequiredErrStr("district"))
+ }else {
+ setCheckDistrict(false)
}
}
-
- setCheckDistrict(false);
- return true;
- };
-
+ }
function getRequiredErrStr(fieldname){
return displayErrorMsg(intl.formatMessage({ id: 'require'},{fieldname:fieldname?intl.formatMessage({ id: fieldname}):""}));
@@ -231,12 +217,8 @@ const BusCustomFormWizard = (props) => {
}
const checkDataField = (data) => {
- const districtValid =
- selectedAddress5?.type !== "hongKong" ||
- (selectedAddress4 && Object.keys(selectedAddress4).length > 0);
-
-
- const valid =
+ // console.log(data.brExpiryDate)
+ if (
handleCaptcha(data.captchaField) &&
data.username !== "" &&
data.password !== "" &&
@@ -260,15 +242,19 @@ const BusCustomFormWizard = (props) => {
handlePhone(data.phone) &&
handleUserName(data.username) &&
handleBrNo(data.brNo) &&
- districtValid &&
- !checkUsername &&
- !checkEmail;
-
- setisValid(valid);
+ handleCheckDistrict()&&
+ !checkUsername&&
+ !checkEmail&&
+ !checkDistrict
+ ) {
+ setisValid(true)
+ return isValid
+ } else {
+ setisValid(false)
+ return isValid
+ }
};
-
-
const handleCheckBoxChange = (event) => {
// console.log(event.target)
if (event.target.name == 'termsAndConAccept') {
@@ -372,18 +358,7 @@ const BusCustomFormWizard = (props) => {
const { handleSubmit } = useForm({})
const _onSubmit = () => {
- if (!handleCheckDistrict()) {
- setLoding(false);
- return;
- }
-
- if (!isValid) {
- setLoding(false);
- return;
- }
-
setLoding(true);
-
values.address4 = selectedAddress4==null?"":selectedAddress4.type
values.address5 = selectedAddress5.type
// console.log(values)
@@ -633,15 +608,6 @@ const BusCustomFormWizard = (props) => {
checkDataField(values)
}, [values])
- useEffect(() => {
- if (
- selectedAddress5?.type === "hongKong" &&
- values?.captchaField?.length === 5
- ) {
- handleCheckDistrict();
- }
- }, [values?.captchaField, selectedAddress5, selectedAddress4]);
-
return (
-
+
@@ -676,7 +642,7 @@ const BusCustomFormWizard = (props) => {
- *
+ *
{/*
-
+
@@ -909,7 +875,7 @@ const CustomFormWizard = (props) => {
- *
+ *
{/* {
- *
+ *
@@ -1023,7 +989,7 @@ const CustomFormWizard = (props) => {
- *
+ *
{
- *
+ *
{/* {formik.touched.enName && formik.errors.enName && (
@@ -1306,7 +1272,7 @@ const CustomFormWizard = (props) => {
- {selectedIdDocType.type === "CNID" ? "" : }
+ {selectedIdDocType.type === "CNID" ? "" : }
{
-
+
{
- *
+ *
{
getOptionLabel={(option) => option.type ? intl.formatMessage({ id: option.type }) : ""}
onChange={(event, newValue) => {
setSelectedAddress4(newValue);
-
- setCheckDistrict(false);
- setDistrictErrStr("");
}}
sx={{
'& .MuiInputBase-root': { alignItems: 'center' },
@@ -1536,7 +1499,7 @@ const CustomFormWizard = (props) => {
- *
+ *
{
- *
+ *
{
- *
+ *
@@ -1761,7 +1724,7 @@ const CustomFormWizard = (props) => {
- *
+ *
@@ -1801,7 +1764,7 @@ const CustomFormWizard = (props) => {
- *
+ *
@@ -1856,7 +1819,7 @@ const CustomFormWizard = (props) => {
- *
+ *
@@ -1891,11 +1854,6 @@ const CustomFormWizard = (props) => {
{formik.errors.captchaField}
)}
-
-
-
-
-
@@ -1912,7 +1870,7 @@ const CustomFormWizard = (props) => {
- {/* 註有*的項目必須輸入資料 */}
+ {/* 註有*的項目必須輸入資料 */}
@@ -2147,4 +2105,4 @@ const CustomFormWizard = (props) => {
);
}
-export default CustomFormWizard;
+export default CustomFormWizard;
\ No newline at end of file
diff --git a/src/pages/authentication/auth-forms/IAmSmartFormWizard.js b/src/pages/authentication/auth-forms/IAmSmartFormWizard.js
index 04132d1..7c4d734 100644
--- a/src/pages/authentication/auth-forms/IAmSmartFormWizard.js
+++ b/src/pages/authentication/auth-forms/IAmSmartFormWizard.js
@@ -21,7 +21,7 @@ import { useFormik, FormikProvider } from 'formik';
import * as yup from 'yup';
import axios from "axios";
-import { POST_IAMSMART_USER_REGISTER, POST_CAPTCHA, POST_USER_EMAIL, POST_CAPTCHA_AUDIO } from "utils/ApiPathConst";
+import { POST_IAMSMART_USER_REGISTER, POST_CAPTCHA, POST_USER_EMAIL } from "utils/ApiPathConst";
import * as ComboData from "utils/ComboData";
@@ -79,16 +79,6 @@ const CustomFormWizard = (props) => {
const [base64Url, setBase64Url] = useState("")
const [checkCode, setCheckCode] = useState("")
- const playCaptchaAudio = async () => {
- const resp = await axios.post(`${POST_CAPTCHA_AUDIO}`,
- { base64Url, lang: intl.locale },
- { responseType: "arraybuffer" }
- );
- const blob = new Blob([resp.data], { type: "audio/wav" });
- const url = URL.createObjectURL(blob);
- new Audio(url).play();
- };
-
useEffect(() => {
location.state?.responseData ?? window.location.assign("/login");
if (captchaImg == "")
@@ -96,15 +86,6 @@ const CustomFormWizard = (props) => {
responseToData();
}, []);
- useEffect(() => {
- if (
- selectedAddress5?.type === "hongKong" &&
- values?.captchaField?.length === 5
- ) {
- handleCheckDistrict();
- }
- }, [values?.captchaField, selectedAddress5, selectedAddress4]);
-
const handleClickShowId = () => {
setshowId(!showId);
};
@@ -136,22 +117,17 @@ const CustomFormWizard = (props) => {
}
}, [checkDistrictBlur])
- const handleCheckDistrict = () => {
+ const handleCheckDistrict = async () => {
setDistrictErrStr("");
-
if (selectedAddress5?.type === "hongKong") {
- if (!selectedAddress4 || Object.keys(selectedAddress4).length === 0) {
- setCheckDistrict(true);
- setDistrictErrStr(getRequiredErrStr("district"));
- return false;
+ if (selectedAddress4 == null || selectedAddress4 == "" || selectedAddress4 == {}){
+ setCheckDistrict(true)
+ setDistrictErrStr(getRequiredErrStr("district"))
+ }else {
+ setCheckDistrict(false)
}
}
-
- setCheckDistrict(false);
- return true;
- };
-
-
+ }
function getRequiredErrStr(fieldname) {
return displayErrorMsg(intl.formatMessage({ id: 'require' }, { fieldname: fieldname ? intl.formatMessage({ id: fieldname }) : "" }));
@@ -232,16 +208,13 @@ const CustomFormWizard = (props) => {
const handleCheckEmail = async () => {
if (values?.email) {
- if (handleEmail(values.email)) {
- const response = await axios.post(`${POST_USER_EMAIL}`, {
- e1: values.email,
- });
- setCheckEmail((Number(response.data[0]) === 1));
- return Number(response.data[0]) === 1;
- }
+ const response = await axios.post(`${POST_USER_EMAIL}`, {
+ e1: values.email,
+ })
+ setCheckEmail((Number(response.data[0]) === 1))
+ return Number(response.data[0]) === 1
}
- };
-
+ }
useEffect(() => {
if (email) {
@@ -271,10 +244,6 @@ const CustomFormWizard = (props) => {
formik.setFieldValue("address1", iAmSmartData.address1 ?? "");
props.setIdNo(iAmSmartData.idNo ?? "");
setLodingData(false)
-
- if (iAmSmartData.email) {
- handleCheckEmail();
- }
}
}, [iAmSmartData])
@@ -293,30 +262,29 @@ const CustomFormWizard = (props) => {
const checkDataField = (data) => {
- const districtValid =
- selectedAddress5?.type !== "hongKong" ||
- (selectedAddress4 && Object.keys(selectedAddress4).length > 0);
-
-
- const valid =
- data.address1 !== "" &&
+ if (data.address1 !== "" &&
data.email !== "" &&
data.emailConfirm !== "" &&
- data.email === data.emailConfirm &&
+ data.email == data.emailConfirm &&
data.phone !== "" &&
data.phoneCountryCode !== "" &&
- termsAndConAccept === true &&
+ termsAndConAccept == true &&
data.captchaField &&
handleEmail(data.email) &&
handlePhone(data.phone) &&
handleCaptcha(data.captchaField) &&
- districtValid &&
- !checkEmail;
-
- setisValid(valid);
+ handleCheckDistrict()&&
+ !checkEmail&&
+ !checkDistrict
+ ) {
+ setisValid(true)
+ return isValid
+ } else {
+ setisValid(false)
+ return isValid
+ }
};
-
const handleCheckBoxChange = (event) => {
if (event.target.name == 'termsAndConAccept') {
setTermsAndConAccept(event.target.checked)
@@ -339,30 +307,14 @@ const CustomFormWizard = (props) => {
termsAndConAccept, termsAndConNotAccept])
useEffect(() => {
- if (props.step == 2) {
- handleCheckDistrict();
- _onSubmit();
- }
- if (captchaImg == "") onCaptchaChange();
- checkDataField(values);
- }, [props.step]);
+ props.step == 2 ? _onSubmit() : null;
+ if (captchaImg == "")
+ onCaptchaChange();
+ checkDataField(values)
+ }, [props.step])
const { handleSubmit } = useForm({})
- const _onSubmit = async () => {
- // hard stop: district
- if (!handleCheckDistrict()) {
- setLoding(false);
- return;
- }
-
- await handleCheckEmail();
-
- // hard stop: overall validation (now includes fresh email result)
- if (!isValid) {
- setLoding(false);
- return;
- }
-
+ const _onSubmit = () => {
setLoding(true);
const userAddress = {
@@ -496,15 +448,6 @@ const CustomFormWizard = (props) => {
checkDataField(values)
}, [values])
- useEffect(() => {
- if (
- selectedAddress5?.type === "hongKong" &&
- values?.captchaField?.length === 5
- ) {
- handleCheckDistrict();
- }
- }, [values?.captchaField, selectedAddress5, selectedAddress4]);
-
return (
isLoadingData ?
:
@@ -523,7 +466,7 @@ const CustomFormWizard = (props) => {
-
+
。
:
@@ -589,7 +532,7 @@ const CustomFormWizard = (props) => {
- *
+ *
@@ -671,16 +614,13 @@ const CustomFormWizard = (props) => {
getOptionLabel={(option) => option.type ? intl.formatMessage({ id: option.type }) : ""}
onChange={(event, newValue) => {
setSelectedAddress4(newValue);
-
- setCheckDistrict(false);
- setDistrictErrStr("");
}}
sx={{
'& .MuiInputBase-root': { alignItems: 'center' },
'& .MuiAutocomplete-endAdornment': { top: '50%', transform: 'translateY(-50%)' },
'& .MuiOutlinedInput-root': { height: 40 }
}}
- renderInput={(params) => }
/>
{
- *
+ *
@@ -760,10 +700,7 @@ const CustomFormWizard = (props) => {
type="email"
value={formik.values.email.trim()}
name="email"
- onChange={(e) => {
- setCheckEmail(false);
- formik.handleChange(e);
- }}
+ onChange={formik.handleChange}
placeholder={intl.formatMessage({ id: 'userContactEmail' })}
onBlur={formik.handleBlur}
inputProps={{
@@ -794,7 +731,7 @@ const CustomFormWizard = (props) => {
- *
+ *
@@ -841,7 +778,7 @@ const CustomFormWizard = (props) => {
- *
+ *
@@ -985,7 +922,7 @@ const CustomFormWizard = (props) => {
- *
+ *
@@ -1042,7 +979,7 @@ const CustomFormWizard = (props) => {
- *
+ *
@@ -1077,11 +1014,6 @@ const CustomFormWizard = (props) => {
{formik.errors.captchaField}
)}
-
-
-
-
-
@@ -1282,4 +1214,4 @@ const CustomFormWizard = (props) => {
);
}
-export default CustomFormWizard;
+export default CustomFormWizard;
\ No newline at end of file
diff --git a/src/pages/dashboard/Public/index.js b/src/pages/dashboard/Public/index.js
index 75f88ac..9af059a 100644
--- a/src/pages/dashboard/Public/index.js
+++ b/src/pages/dashboard/Public/index.js
@@ -93,7 +93,25 @@ const DashboardDefault = () => {
let list = []
response.map((item) => {
list.push(
- {navigate("/msg/details/"+item.id);}} color={item.readTime?"gray":"black"} style={{justifyContent: "flex-start"}} sx={{p:2}}>
+ navigate("/msg/details/" + item.id)}
+ sx={{
+ p: 2,
+ justifyContent: "flex-start",
+ textTransform: "none",
+
+ // text always readable
+ color: "#1A1A1A",
+
+ // unread gets subtle highlight
+ backgroundColor: item.readTime ? "transparent" : "#F5F7FA",
+ fontWeight: item.readTime ? 400 : 600,
+
+ "&:hover": {
+ backgroundColor: "#EEF2F7",
+ },
+ }}
+ >
{item.subject}
{DateUtils.datetimeStr(item.sentDate)}
@@ -165,18 +183,29 @@ const DashboardDefault = () => {
{ navigate("/publicNotice/apply"); }}
- style={{ justifyContent: "flex-start" }}
+ xs={12}
+ onClick={() => { navigate("/publicNotice/apply"); }}
aria-label={intl.formatMessage({ id: 'submitApplication' })}
- sx={{ width: "100%", p: 4, border: '3px solid #e1edfc', borderRadius: '10px', backgroundColor: "#e1edfc" }}
+ sx={{
+ width: "100%",
+ p: 4,
+ justifyContent: "flex-start",
+ backgroundColor: "#e1edfc",
+ border: "3px solid #0C489E", // DARKER border (3:1+)
+ borderRadius: "10px",
+ color: "#0C489E", // icon + default text color
+ "&:hover": {
+ backgroundColor: "#d6e6fb"
+ }
+ }}
>
-
-
+
+
-
+
@@ -187,9 +216,15 @@ const DashboardDefault = () => {
{navigate("/announcement/search")}}
+ onClick={() => { navigate("/announcement/search"); }}
+ sx={{
+ color: '#0C489E',
+ textTransform: 'none',
+ textDecoration: 'underline',
+ '&:hover': { textDecoration: 'underline' }
+ }}
>
@@ -206,9 +241,17 @@ const DashboardDefault = () => {
{ navigate("/msg/search"); }}
- color="gray"
+ sx={{
+ color: '#0C489E',
+ textTransform: 'none',
+ textDecoration: 'underline',
+ '&:hover': {
+ textDecoration: 'underline',
+ },
+ }}
>
diff --git a/src/pages/pnspsUserGroupDetailPage/index.js b/src/pages/pnspsUserGroupDetailPage/index.js
index 8c938fd..fd60e30 100644
--- a/src/pages/pnspsUserGroupDetailPage/index.js
+++ b/src/pages/pnspsUserGroupDetailPage/index.js
@@ -203,7 +203,7 @@ const UserMaintainPage = () => {
alignItems: 'end'
}}
onClick={() => { location.reload() }}
- color="gray"
+ color="secondary"
>
Reset & Back
diff --git a/src/themes/buttonConst.js b/src/themes/buttonConst.js
index 025c764..23635c5 100644
--- a/src/themes/buttonConst.js
+++ b/src/themes/buttonConst.js
@@ -70,31 +70,84 @@ export const PNSPS_BUTTON_THEME = createTheme({
},
MuiButton: {
styleOverrides: {
- startIcon:{
- display: 'flex',
- alignItems: 'center',
- justifyContent: 'center',
- '& > *:nth-of-type(1)': {
- fontSize: '28px',
- },
+ /* -----------------------------
+ Icon alignment
+ ------------------------------ */
+ startIcon: {
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ '& > *:nth-of-type(1)': { fontSize: '28px' },
},
+
+ /* -----------------------------
+ Base button styles
+ ------------------------------ */
root: {
- fontSize: '1.2rem',
- fontWeight: '600',
- height: '45px',
- minWidth: '35' +
- 'vw', // Default width for xs screen sizes
- '@media (min-width: 600px)': { // sm breakpoint
- minWidth: '20vw',
- },
- '@media (min-width: 960px)': { // md breakpoint
- minWidth: '15vw',
- },
- '@media (min-width: 1280px)': { // lg breakpoint
- minWidth: '9vw',
- },
- textTransform: "none",
- alignItems: 'normal',
+ fontSize: '1.2rem',
+ fontWeight: '600',
+ height: '45px',
+ minWidth: '35vw',
+ '@media (min-width: 600px)': { minWidth: '20vw' },
+ '@media (min-width: 960px)': { minWidth: '15vw' },
+ '@media (min-width: 1280px)': { minWidth: '9vw' },
+ textTransform: 'none',
+ alignItems: 'normal',
+ },
+
+ /* -----------------------------
+ Remove default contained elevation / blue focus
+ ------------------------------ */
+ contained: {
+ boxShadow: 'none',
+ '&:hover': { boxShadow: 'none' },
+ '&:active': { boxShadow: 'none' },
+ '&.Mui-focusVisible': { boxShadow: 'none' },
+ },
+
+ /* -----------------------------
+ RESET / CANCEL (contained + cancel)
+ WCAG 2.1 AA compliant
+ ------------------------------ */
+ containedCancel: {
+ backgroundColor: '#616161', // dark grey (passes with white text)
+ color: '#FFFFFF',
+
+ border: '0 !important',
+ outline: 'none',
+ boxShadow: 'none !important',
+
+ '&:hover': {
+ backgroundColor: '#545454',
+ boxShadow: 'none !important',
+ },
+
+ '&:active': {
+ backgroundColor: '#4A4A4A',
+ boxShadow: 'none !important',
+ },
+
+ /* ✅ Keyboard TAB focus (visible & accessible) */
+ '&.Mui-focusVisible': {
+ boxShadow: '0 0 0 3px rgba(255,255,255,0.6) !important',
+ },
+ },
+
+ /* -----------------------------
+ Optional: outlined cancel variant
+ ------------------------------ */
+ outlinedCancel: {
+ borderColor: '#616161',
+ color: '#424242',
+
+ '&:hover': {
+ borderColor: '#616161',
+ backgroundColor: 'transparent',
+ },
+
+ '&.Mui-focusVisible': {
+ boxShadow: '0 0 0 3px rgba(66,66,66,0.4)',
+ },
},
},
},
diff --git a/src/themes/themeConst.js b/src/themes/themeConst.js
index 931ac3a..82583a4 100644
--- a/src/themes/themeConst.js
+++ b/src/themes/themeConst.js
@@ -17,6 +17,10 @@ const themeTypography = Typography(
export const PNSPS_THEME = createTheme({
palette: theme.palette,
typography: themeTypography,
+ cancel: {
+ main: '#424242', // dark gray, WCAG safe
+ contrastText: '#FFFFFF'
+ },
components: {
MuiImage: {
root: {
@@ -41,19 +45,17 @@ export const PNSPS_THEME = createTheme({
},
MuiButton: {
styleOverrides: {
- outlined: {
- borderColor: "#0050B3",
- color: "#0050B3",
-
- "&:hover": {
- borderColor: "#0050B3",
- backgroundColor: "transparent",
- },
-
- "& .MuiSvgIcon-root": {
- color: "inherit",
- },
- },
+ root: ({ ownerState }) => ({
+ ...(ownerState.color === "cancel" && {
+ borderColor: "#9E9E9E",
+ }),
+ }),
+ contained: ({ ownerState }) => ({
+ ...(ownerState.color === "cancel" && {
+ border: "2px solid #9E9E9E",
+ boxShadow: "none",
+ }),
+ }),
},
},
MuiInputAdornment: {
@@ -416,18 +418,45 @@ export const PNSPS_THEME = createTheme({
MuiTab: {
styleOverrides: {
root: {
- fontSize: TABLE_FONT_SIZE,
- minHeight: 46,
- color: theme.palette.text.primary
- }
- }
- },
+ fontSize: TABLE_FONT_SIZE, // ✅ unchanged
+ minHeight: 46,
+ textTransform: 'none',
+
+ // Unselected tab text on white – AA compliant
+ color: '#424242',
+
+ // Selected tab text – AA compliant blue
+ '&.Mui-selected': {
+ color: '#1565C0',
+ },
+
+ // Hover (optional but consistent)
+ '&:hover': {
+ color: '#1565C0',
+ },
+
+ // ✅ Keyboard TAB focus (visible & accessible)
+ '&.Mui-focusVisible': {
+ outline: '3px solid rgba(21, 101, 192, 0.55)',
+ outlineOffset: '3px',
+ borderRadius: '6px',
+ },
+ },
+ },
+ },
+
MuiTabs: {
styleOverrides: {
vertical: {
- overflow: 'visible'
- }
- }
+ overflow: 'visible',
+ },
+
+ // ✅ Selected tab indicator – AA compliant blue
+ indicator: {
+ backgroundColor: '#1565C0',
+ height: '3px',
+ },
+ },
},
MuiFormLabel: {
styleOverrides: {
diff --git a/src/utils/statusUtils/DnStatus.js b/src/utils/statusUtils/DnStatus.js
index 9c7e96e..527ab36 100644
--- a/src/utils/statusUtils/DnStatus.js
+++ b/src/utils/statusUtils/DnStatus.js
@@ -1,8 +1,8 @@
import {getStatusTag} from "utils/statusUtils/Base";
-const pending = {color:"#f5a83d", eng:"Pending", cht:"待辦", cn: "待办"}
-const toBePaid = {color:"#f5a83d", eng:"To be Paid", cht:"待付款", cn: "待付款"}
-const paid = {color:"#22a13f", eng:"Paid", cht:"已付款", cn: "已付款"}
+const pending = { color: "#8F7200", eng: "Pending", cht: "待辦", cn: "待办" } // was #f5a83d
+const toBePaid = { color: "#8F7200", eng: "To be Paid", cht: "待付款", cn: "待付款" } // was #f5a83d
+const paid = { color: "#1D8735", eng: "Paid", cht: "已付款", cn: "已付款" } // was #22a13f
export function getStatus_Cht(params) {
let status = getStatus(params);
diff --git a/src/utils/statusUtils/PaymentStatus.js b/src/utils/statusUtils/PaymentStatus.js
index 2e7d5fd..f24f643 100644
--- a/src/utils/statusUtils/PaymentStatus.js
+++ b/src/utils/statusUtils/PaymentStatus.js
@@ -1,10 +1,10 @@
import {getStatusTag} from "utils/statusUtils/Base";
-const APPR = {color:"#22a13f", eng:"Success", cht:"成功"}
-const REJT = {color:"#d9372b", eng:"Reject", cht:"拒絕"}
-const CANC = {color:"#8a8784", eng:"Cancelled", cht:"取消"}
-const INPR = {color:"#f5a83d", eng:"In Progress", cht:"進行中"}
-const DUPA = {color:"#8a8784", eng:"", cht:""}
+const APPR = { color: "#1D8735", eng: "Success", cht: "成功" } // was #22a13f
+const REJT = { color: "#d9372b", eng: "Reject", cht: "拒絕" } // unchanged
+const CANC = { color: "#797674", eng: "Cancelled", cht: "取消" } // was #8a8784
+const INPR = { color: "#8F7200", eng: "In Progress", cht: "進行中" } // was #f5a83d
+const DUPA = { color: "#797674", eng: "", cht: "" } // was #8a8784
export function getStatus_Cht(params) {
let status = getStatus(params);
diff --git a/src/utils/statusUtils/ProofStatus.js b/src/utils/statusUtils/ProofStatus.js
index e0fd6db..f374e18 100644
--- a/src/utils/statusUtils/ProofStatus.js
+++ b/src/utils/statusUtils/ProofStatus.js
@@ -1,11 +1,11 @@
import {getStatusTag} from "utils/statusUtils/Base";
import * as DateUtils from "utils/DateUtils";
-const confirm = {color:"#22a13f", eng:"Pass for printing", cht:"可以付印", cn:"可以付印"}
-const unable = {color:"#d9372b", eng:"Re-proofing", cht:"未能付印", cn:"未能付印"}
-const timeOut = {color:"#8a8784", eng:"Proofing timed out. Please apply again", cht:"校對回覆逾時,請重新申請", cn:"校对回复逾时,请重新申请"}
-const pendingReply = {color:"#f5a83d", eng:"Proofing reply pending", cht:"校對待覆", cn:"校对待复"}
-const cancel = {color:"#000", textColor:"#fff", eng:"Cancelled", cht:"已取消", cn:"已取消"}
+const confirm = { color: "#1D8735", eng: "Pass for printing", cht: "可以付印", cn: "可以付印" } // was #22a13f
+const unable = { color: "#d9372b", eng: "Re-proofing", cht: "未能付印", cn: "未能付印" } // unchanged
+const timeOut = { color: "#797674", eng: "Proofing timed out. Please apply again", cht: "校對回覆逾時,請重新申請", cn: "校对回复逾时,请重新申请" } // was #8a8784
+const pendingReply = { color: "#9D6C27", eng: "Proofing reply pending", cht: "校對待覆", cn: "校对待复" } // was #f5a83d
+const cancel = { color: "#000", textColor: "#fff", eng: "Cancelled", cht: "已取消", cn: "已取消" } // unchanged
export function getStatus_Cht(params) {
let status = getStatus(params);
diff --git a/src/utils/statusUtils/PublicNoteStatusUtils.js b/src/utils/statusUtils/PublicNoteStatusUtils.js
index 03978eb..fab1794 100644
--- a/src/utils/statusUtils/PublicNoteStatusUtils.js
+++ b/src/utils/statusUtils/PublicNoteStatusUtils.js
@@ -7,33 +7,43 @@ export function getStatus(params) {
}
export function getStatusByText(status, creditor) {
- switch (status) {
- case "submitted":
- return getStatusTag({ color: "#f5a83d", text: "處理中" })
- case "reviewed":
- return getStatusTag({ color: "#f5a83d", text: "處理中" })
- case "confirmed":
- if (creditor)
- return getStatusTag({ color: "#22a13f", text: "待發佈" })
- else
- return getStatusTag({ color: "#22a13f", text: "待付款" })
- case "published":
- return getStatusTag({ color: "#22a13f", text: "待付款" })
- case "paid":
- return getStatusTag({ color: "#22a13f", text: "待發佈" })
- case "completed":
- return getStatusTag({ color: "#4D4D4D", text: "已完成" })
- case "notAccepted":
- return getStatusTag({ color: "#d9372b", text: "不接受" })
- case "resubmit":
- return getStatusTag({ color: "#757373", text: "需重新提交" })
- case "cancelled":
- return getStatusTag({ color: "#909497", text: "已取消" })
- case "withdrawn":
- return getStatusTag({ color: "#6E14CC", text: "已撤銷" })
- default:
- return getStatusTag({ text: status })
- }
+ switch (status) {
+ case "submitted":
+ return getStatusTag({ color: "#8F7200", text: "處理中" }) // was #f5a83d
+
+ case "reviewed":
+ return getStatusTag({ color: "#8F7200", text: "處理中" }) // was #f5a83d
+
+ case "confirmed":
+ if (creditor)
+ return getStatusTag({ color: "#1D8735", text: "待發佈" }) // was #22a13f
+ else
+ return getStatusTag({ color: "#1D8735", text: "待付款" }) // was #22a13f
+
+ case "published":
+ return getStatusTag({ color: "#1D8735", text: "待付款" }) // was #22a13f
+
+ case "paid":
+ return getStatusTag({ color: "#1D8735", text: "待發佈" }) // was #22a13f
+
+ case "completed":
+ return getStatusTag({ color: "#4D4D4D", text: "已完成" }) // unchanged (already AA)
+
+ case "notAccepted":
+ return getStatusTag({ color: "#d9372b", text: "不接受" }) // unchanged (already AA)
+
+ case "resubmit":
+ return getStatusTag({ color: "#757373", text: "需重新提交" }) // unchanged (already AA)
+
+ case "cancelled":
+ return getStatusTag({ color: "#737679", text: "已取消" }) // was #909497
+
+ case "withdrawn":
+ return getStatusTag({ color: "#6E14CC", text: "已撤銷" }) // unchanged (already AA)
+
+ default:
+ return getStatusTag({ text: status })
+ }
}
export function getStatusEng(params) {
@@ -42,28 +52,28 @@ export function getStatusEng(params) {
export function getStatusByTextEng(status, creditor) {
switch (status) {
case "submitted":
- return getStatusTag({ color: "#F1C40F", text: "Submitted" })
+ return getStatusTag({ color: "#8F7200", text: "Submitted" }) // was #F1C40F
case "reviewed":
- return getStatusTag({ color: "#0C489E", text: "Reviewed" })
+ return getStatusTag({ color: "#0C489E", text: "Reviewed" }) // unchanged
case "confirmed":
if (creditor)
- return getStatusTag({ color: "#3498DB", text: "Pending Publish" })
+ return getStatusTag({ color: "#217CBA", text: "Pending Publish" }) // was #3498DB
else
- return getStatusTag({ color: "#F39C12", text: "Pending Payment" })
+ return getStatusTag({ color: "#B45309", text: "Pending Payment" }) // was #F39C12
case "published":
- return getStatusTag({ color: "#F39C12", text: "Pending Payment" })
+ return getStatusTag({ color: "#B45309", text: "Pending Payment" }) // was #F39C12
case "paid":
- return getStatusTag({ color: "#3498DB", text: "Pending Publish" })
+ return getStatusTag({ color: "#217CBA", text: "Pending Publish" }) // was #3498DB
case "completed":
- return getStatusTag({ color: "#4D4D4D", text: "Completed" })
+ return getStatusTag({ color: "#4D4D4D", text: "Completed" }) // unchanged
case "notAccepted":
- return getStatusTag({ color: "#d9372b", text: "Not Accepted" })
+ return getStatusTag({ color: "#d9372b", text: "Not Accepted" }) // unchanged
case "resubmit":
- return getStatusTag({ color: "#757373", text: "Re-Submit Required" })
+ return getStatusTag({ color: "#757373", text: "Re-Submit Required" }) // unchanged
case "cancelled":
- return getStatusTag({ color: "#8a8784", text: "Cancelled" })
+ return getStatusTag({ color: "#797674", text: "Cancelled" }) // was #8a8784
case "withdrawn":
- return getStatusTag({ color: "#6E14CC", text: "Withdrawn" })
+ return getStatusTag({ color: "#6E14CC", text: "Withdrawn" }) // unchanged
default:
return getStatusTag({ text: status })
}
@@ -76,28 +86,28 @@ export function getStatusIntl(params, intl) {
export function getStatusByTextIntl(status, creditor, intl) {
switch (status) {
case "submitted":
- return getStatusTag({ color: "#F1C40F", text: intl.formatMessage({id: 'processing'}) })
+ return getStatusTag({ color: "#8F7200", text: intl.formatMessage({id: 'processing'}) }) // was #F1C40F
case "reviewed":
- return getStatusTag({ color: "#F1C40F", text: intl.formatMessage({id: 'processing'}) })
+ return getStatusTag({ color: "#8F7200", text: intl.formatMessage({id: 'processing'}) }) // was #F1C40F
case "confirmed":
if (creditor)
- return getStatusTag({ color: "#3498DB", text: intl.formatMessage({id: 'pendingPublish'}) })
+ return getStatusTag({ color: "#217CBA", text: intl.formatMessage({id: 'pendingPublish'}) }) // was #3498DB
else
- return getStatusTag({ color: "#F39C12", text: intl.formatMessage({id: 'pendingPayment'}) })
+ return getStatusTag({ color: "#B45309", text: intl.formatMessage({id: 'pendingPayment'}) }) // was #F39C12
case "published":
- return getStatusTag({ color: "#F39C12", text: intl.formatMessage({id: 'pendingPayment'}) })
+ return getStatusTag({ color: "#B45309", text: intl.formatMessage({id: 'pendingPayment'}) }) // was #F39C12
case "paid":
- return getStatusTag({ color: "#3498DB", text: intl.formatMessage({id: 'pendingPublish'}) })
+ return getStatusTag({ color: "#217CBA", text: intl.formatMessage({id: 'pendingPublish'}) }) // was #3498DB
case "completed":
- return getStatusTag({ color: "#4D4D4D", text: intl.formatMessage({id: 'completed'}) })
+ return getStatusTag({ color: "#4D4D4D", text: intl.formatMessage({id: 'completed'}) }) // unchanged
case "notAccepted":
- return getStatusTag({ color: "#d9372b", text: intl.formatMessage({id: 'notAccepted'}) })
+ return getStatusTag({ color: "#d9372b", text: intl.formatMessage({id: 'notAccepted'}) }) // unchanged
case "resubmit":
- return getStatusTag({ color: "#757373", text: intl.formatMessage({id: 'resubmit'}) })
+ return getStatusTag({ color: "#757373", text: intl.formatMessage({id: 'resubmit'}) }) // unchanged
case "cancelled":
- return getStatusTag({ color: "#8a8784", text: intl.formatMessage({id: 'cancelled'}) })
+ return getStatusTag({ color: "#797674", text: intl.formatMessage({id: 'cancelled'}) }) // was #8a8784
case "withdrawn":
- return getStatusTag({ color: "#6E14CC", text: intl.formatMessage({id: 'withdrawn'}) })
+ return getStatusTag({ color: "#6E14CC", text: intl.formatMessage({id: 'withdrawn'}) }) // unchanged
default:
return getStatusTag({ text: status })
}