Ver a proveniência

WCAG 2.0 AA 1.4.3 Contrast (Minimum)

web_access_fix
Jason Chuang há 3 semanas
ascendente
cometimento
686ca8aeeb
17 ficheiros alterados com 541 adições e 496 eliminações
  1. +54
    -37
      src/assets/style/styles.css
  2. +1
    -1
      src/layout/MainLayout/Header/index.js
  3. +22
    -17
      src/pages/authentication/BusRegister.js
  4. +28
    -23
      src/pages/authentication/IAmSmartRegister.js
  5. +22
    -17
      src/pages/authentication/Register.js
  6. +33
    -6
      src/pages/authentication/RegisterCustom.js
  7. +38
    -77
      src/pages/authentication/auth-forms/BusCustomFormWizard.js
  8. +47
    -89
      src/pages/authentication/auth-forms/CustomFormWizard.js
  9. +43
    -111
      src/pages/authentication/auth-forms/IAmSmartFormWizard.js
  10. +53
    -10
      src/pages/dashboard/Public/index.js
  11. +1
    -1
      src/pages/pnspsUserGroupDetailPage/index.js
  12. +76
    -23
      src/themes/buttonConst.js
  13. +51
    -22
      src/themes/themeConst.js
  14. +3
    -3
      src/utils/statusUtils/DnStatus.js
  15. +5
    -5
      src/utils/statusUtils/PaymentStatus.js
  16. +5
    -5
      src/utils/statusUtils/ProofStatus.js
  17. +59
    -49
      src/utils/statusUtils/PublicNoteStatusUtils.js

+ 54
- 37
src/assets/style/styles.css Ver ficheiro

@@ -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 */
}

+ 1
- 1
src/layout/MainLayout/Header/index.js Ver ficheiro

@@ -746,7 +746,7 @@ function Header(props) {
<Stack justifyContent="flex-start" alignItems="center">
{/*<span id="systemTitle">公共啟事提交</span>*/}
{/*<span id="systemTitle">及繳費系統</span>*/}
<span style={{ color: checkSysEnv()!=''?'red':'#0C489E'}} id="systemTitle">
<span style={{ color: checkSysEnv()!=''?'#B00020':'#0C489E'}} id="systemTitle">
<FormattedMessage id="PNSPS" />
</span>
</Stack>


+ 22
- 17
src/pages/authentication/BusRegister.js Ver ficheiro

@@ -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 = () => {
</StepLabel>
</StepButton>) :
(<StepButton
sx={activeStep === 2 ? { "& .MuiSvgIcon-root": { color: "warning.main", fontSize: "2rem" } } : allStepsCompleted() ? { "& .MuiSvgIcon-root": { color: "secondary.main", fontSize: "2rem" } } : { color: "rgba(0, 0, 0, 0.38)" }}
sx={
activeStep === 2
? { "& .MuiSvgIcon-root": { color: "warning.main", fontSize: "2rem" } }
: allStepsCompleted()
? { "& .MuiSvgIcon-root": { color: "#0C489E", fontSize: "2rem" } }
: { color: "rgba(0,0,0,0.6)" } // was 0.38
}
icon={<VisibilityIcon />}
// onClick={handleStep(index)}
>


+ 28
- 23
src/pages/authentication/IAmSmartRegister.js Ver ficheiro

@@ -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 = () => {
<Typography variant="step1">{label}</Typography>
</StepButton>) :
(<StepButton
sx={activeStep === 2 ? { "& .MuiSvgIcon-root": { color: "warning.main", fontSize: "2rem" } } : allStepsCompleted() ? { "& .MuiSvgIcon-root": { color: "secondary.main", fontSize: "2rem" } } : { color: "rgba(0, 0, 0, 0.38)" }}
sx={
activeStep === 2
? { "& .MuiSvgIcon-root": { color: "warning.main", fontSize: "2rem" } }
: allStepsCompleted()
? { "& .MuiSvgIcon-root": { color: "#0C489E", fontSize: "2rem" } }
: { color: "rgba(0,0,0,0.6)" } // was 0.38
}
icon={<VisibilityIcon />}
// onClick={handleStep(index)}
>


+ 22
- 17
src/pages/authentication/Register.js Ver ficheiro

@@ -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 = () => {
</StepLabel>
</StepButton>) :
(<StepButton
sx={activeStep === 2 ? { "& .MuiSvgIcon-root": { color: "warning.main", fontSize: "2rem" } } : allStepsCompleted() ? { "& .MuiSvgIcon-root": { color: "secondary.main", fontSize: "2rem" } } : { color: "rgba(0, 0, 0, 0.38)" }}
sx={
activeStep === 2
? { "& .MuiSvgIcon-root": { color: "warning.main", fontSize: "2rem" } }
: allStepsCompleted()
? { "& .MuiSvgIcon-root": { color: "#0C489E", fontSize: "2rem" } }
: { color: "rgba(0,0,0,0.6)" } // was 0.38
}
icon={<VisibilityIcon />}
// onClick={handleStep(index)}
>


+ 33
- 6
src/pages/authentication/RegisterCustom.js Ver ficheiro

@@ -134,9 +134,22 @@ const RegisterCustom = () => {
<FormattedMessage id="or" />
</Typography>

<Button href="/registerFrom" variant="contained"><Typography variant="h5">
<FormattedMessage id="registerNewPersonalUser" />
</Typography></Button>
<Button
href="/registerFrom"
variant="contained"
sx={{
border: '1px solid #0C489E',
boxShadow: 'none', // optional, cleaner look
'&:hover': {
border: '1px solid #0C489E',
boxShadow: 'none',
},
}}
>
<Typography variant="h5">
<FormattedMessage id="registerNewPersonalUser" />
</Typography>
</Button>

<Typography ml={4} mr={4} mt={4} variant="body1" display="block" sx={{ fontWeight: 'bold' }} gutterBottom>
<div dangerouslySetInnerHTML={{ __html: intl.formatMessage({ id: 'MSG.registerPersonal' }) }} />
@@ -146,9 +159,23 @@ const RegisterCustom = () => {
<Typography mb={4} variant="h3">
<FormattedMessage id="businessUser" />
</Typography>
<Button href="/registerFromOrganization" variant="contained" sx={{ mt: 0.5 }}><Typography variant="h5">
<FormattedMessage id="registerNewBusinessUser" />
</Typography></Button>
<Button
href="/registerFromOrganization"
variant="contained"
sx={{
mt: 0.5,
border: '1px solid #0C489E',
boxShadow: 'none',
'&:hover': {
border: '1px solid #0C489E',
boxShadow: 'none',
},
}}
>
<Typography variant="h5">
<FormattedMessage id="registerNewBusinessUser" />
</Typography>
</Button>
<Typography ml={4} mr={4} mt={4} variant="body1" display="block" sx={{ fontWeight: 'bold' }} gutterBottom>
<div dangerouslySetInnerHTML={{ __html: intl.formatMessage({ id: 'MSG.registerOrg' }) }} />
</Typography>


+ 38
- 77
src/pages/authentication/auth-forms/BusCustomFormWizard.js Ver ficheiro

@@ -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 (
<FormikProvider value={formik}>
<form onSubmit={handleSubmit(_onSubmit)}>
@@ -658,7 +624,7 @@ const BusCustomFormWizard = (props) => {
<FormattedMessage id="becomeNewBusinessUser"/>
</Typography>
</div>
<Typography mt={0.25} variant="h6" sx={{ color: '#f10000' }}>
<Typography mt={0.25} variant="h6" sx={{ color: '#B00020' }}>
<FormattedMessage id="requireString"/>
</Typography>
<Typography mt={0.25} variant="h4" sx={{ color: 'primary.primary' }}>
@@ -676,7 +642,7 @@ const BusCustomFormWizard = (props) => {
<InputLabel htmlFor="username-signup">
<Typography variant="pnspsFormHeader">
<FormattedMessage id="userLoginName"/>
<span style={{ color: '#f10000' }}>*</span>
<span style={{ color: '#B00020' }}>*</span>
{/* <Button
variant="contained"
onClick={handleCheckUsername}
@@ -728,7 +694,7 @@ const BusCustomFormWizard = (props) => {
<InputLabel htmlFor="password-signup">
<Typography variant="pnspsFormHeader">
<FormattedMessage id="userPassword"/>
<span style={{ color: '#f10000' }}>*</span>
<span style={{ color: '#B00020' }}>*</span>
</Typography>
</InputLabel>
</Stack>
@@ -790,7 +756,7 @@ const BusCustomFormWizard = (props) => {
<InputLabel htmlFor="confirmPassword-signup">
<Typography variant="pnspsFormHeader">
<FormattedMessage id="confirmPassword"/>
<span style={{ color: '#f10000' }}>*</span>
<span style={{ color: '#B00020' }}>*</span>
</Typography>
</InputLabel>
<OutlinedInput
@@ -935,7 +901,7 @@ const BusCustomFormWizard = (props) => {
<InputLabel htmlFor="brNo-signup" sx={{ whiteSpace: 'pre-wrap', wordWrap: 'break-word' }}>
<Typography variant="pnspsFormHeader">
<FormattedMessage id="businessRegCertNumber"/> (e.g. 12341234)
<span style={{ color: '#f10000' }}>*</span>
<span style={{ color: '#B00020' }}>*</span>
</Typography>
</InputLabel>
<OutlinedInput
@@ -968,7 +934,7 @@ const BusCustomFormWizard = (props) => {
<InputLabel htmlFor="brExpiryDate-signup">
<Typography variant="pnspsFormHeader">
<FormattedMessage id="businessRegCertValidityDate"/>
<span style={{ color: '#f10000' }}>*</span>
<span style={{ color: '#B00020' }}>*</span>
</Typography>
</InputLabel>
<OutlinedInput
@@ -1003,7 +969,7 @@ const BusCustomFormWizard = (props) => {
<InputLabel htmlFor="address1-signup">
<Typography variant="pnspsFormHeader">
<FormattedMessage id="formAddress"/>
<span style={{ color: '#f10000' }}>*</span>
<span style={{ color: '#B00020' }}>*</span>
</Typography>
</InputLabel>
<OutlinedInput
@@ -1119,7 +1085,7 @@ const BusCustomFormWizard = (props) => {
setCheckCountry(true)
}
}}
sx={{
sx={{
'& .MuiInputBase-root': { alignItems: 'center' },
'& .MuiAutocomplete-endAdornment': { top: '50%', transform: 'translateY(-50%)' },
'& .MuiOutlinedInput-root': { height: 40 }
@@ -1160,7 +1126,7 @@ const BusCustomFormWizard = (props) => {
<InputLabel htmlFor="enName-signup">
<Typography variant="pnspsFormHeader">
<FormattedMessage id="userContactName"/>
<span style={{ color: '#f10000' }}>*</span>
<span style={{ color: '#B00020' }}>*</span>
</Typography>
</InputLabel>
<OutlinedInput
@@ -1195,7 +1161,7 @@ const BusCustomFormWizard = (props) => {
<InputLabel htmlFor="email-signup">
<Typography variant="pnspsFormHeader">
<FormattedMessage id="userContactEmail"/>
<span style={{ color: '#f10000' }}>*</span>
<span style={{ color: '#B00020' }}>*</span>
</Typography>
</InputLabel>
<OutlinedInput
@@ -1233,7 +1199,7 @@ const BusCustomFormWizard = (props) => {
<InputLabel htmlFor="emailConfirm-signup">
<Typography variant="pnspsFormHeader">
<FormattedMessage id="confirmEmail"/>
<span style={{ color: '#f10000' }}>*</span>
<span style={{ color: '#B00020' }}>*</span>
</Typography>
</InputLabel>
<OutlinedInput
@@ -1276,7 +1242,7 @@ const BusCustomFormWizard = (props) => {
<InputLabel htmlFor="phone-signup">
<Typography variant="pnspsFormHeader">
<FormattedMessage id="userContactNumber"/>
<span style={{ color: '#f10000' }}>*</span>
<span style={{ color: '#B00020' }}>*</span>
</Typography>
</InputLabel>
<Stack direction="row">
@@ -1419,7 +1385,7 @@ const BusCustomFormWizard = (props) => {
<Stack spacing={1} direction="column" justifyContent="space-between" alignItems="baseline" sx={{ mb: { xs: -0.5, sm: 0.5 } }}>
<Typography display="inline" variant="h4" sx={{ color: 'primary.primary' }}>
<FormattedMessage id="businessRegCertAndDoc"/>
<span style={{ color: '#f10000' }}>*</span>
<span style={{ color: '#B00020' }}>*</span>
</Typography>
<Typography display="inline" variant="h6" sx={{ color: 'primary.primary' }}>
<FormattedMessage id="pleaseUploadDoc"/>
@@ -1458,7 +1424,7 @@ const BusCustomFormWizard = (props) => {
<Grid item xs={12} md={12}>
<Typography display="inline" variant="h4" sx={{ color: 'primary.primary' }}>
<FormattedMessage id="termsAndCondition"/>
<span style={{ color: '#f10000' }}>*</span>
<span style={{ color: '#B00020' }}>*</span>
</Typography>
</Grid>
<Grid item xs={12} md={12}>
@@ -1513,7 +1479,7 @@ const BusCustomFormWizard = (props) => {
<Stack direction="column">
<Typography display="inline" variant="h4" sx={{ color: 'primary.primary' }}>
<FormattedMessage id="verify"/>
<span style={{ color: '#f10000' }}>*</span>
<span style={{ color: '#B00020' }}>*</span>
</Typography>
<Stack spacing={1} direction="row">
<Grid item xs={5} lg={5} style={{ "border": "1px solid black" }}>
@@ -1548,11 +1514,6 @@ const BusCustomFormWizard = (props) => {
{formik.errors.captchaField}
</FormHelperText>
)}
<Stack spacing={1} direction="row">
<Button onClick={playCaptchaAudio} aria-label={intl.formatMessage({id:"captchaPlayAudioAria"})}>
<FormattedMessage id="captchaPlayAudio" />
</Button>
</Stack>
</Stack>
</Grid>
</Grid>
@@ -1569,7 +1530,7 @@ const BusCustomFormWizard = (props) => {
<FormattedMessage id="becomeNewBusinessUser"/>
</Typography>
</div>
{/* <Typography mt={0.25} variant="h6" sx={{ fontSize: 12,color: '#f10000'}}>註有*的項目必須輸入資料</Typography> */}
{/* <Typography mt={0.25} variant="h6" sx={{ fontSize: 12,color: '#B00020'}}>註有*的項目必須輸入資料</Typography> */}
<Typography mt={0.25} variant="h4" sx={{ color: 'primary.primary' }}>
<FormattedMessage id="yourLoginInformation"/>
</Typography>
@@ -1798,4 +1759,4 @@ const BusCustomFormWizard = (props) => {
);
}

export default BusCustomFormWizard;
export default BusCustomFormWizard;

+ 47
- 89
src/pages/authentication/auth-forms/CustomFormWizard.js Ver ficheiro

@@ -25,7 +25,7 @@ import * as yup from 'yup';
import { strengthColorChi, strengthIndicator } from 'utils/password-strength';
// import {apiPath} from "auth/utils";
import axios from "axios";
import { POST_USERNAME, POST_USER_EMAIL, POST_CAPTCHA, POST_PUBLIC_USER_REGISTER, POST_IDNO, POST_CAPTCHA_AUDIO } from "utils/ApiPathConst";
import { POST_USERNAME, POST_USER_EMAIL, POST_CAPTCHA, POST_PUBLIC_USER_REGISTER, POST_IDNO } from "utils/ApiPathConst";
// import * as HttpUtils from 'utils/HttpUtils';
import * as ComboData from "utils/ComboData";

@@ -137,16 +137,6 @@ const CustomFormWizard = (props) => {
onCaptchaChange();
}
}, []);

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(() => {
if (selectedIdDocType.type === "HKID"){
@@ -207,21 +197,17 @@ const CustomFormWizard = (props) => {
}
}
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;
};

}

useEffect(() => {
if (username) {
@@ -327,43 +313,46 @@ const CustomFormWizard = (props) => {
}

const checkDataField = (data) => {
const districtValid =
selectedAddress5?.type !== "hongKong" ||
(selectedAddress4 && Object.keys(selectedAddress4).length > 0);


const valid =
// console.log(data)
if (
handleCaptcha(data.captchaField) &&
data.username !== "" &&
data.password !== "" &&
data.confirmPassword !== "" &&
data.password === data.confirmPassword &&
data.password == data.confirmPassword &&
selectedIdDocType.type !== "" &&
data.idNo !== "" &&
// (data.enName !== "" || selectedIdDocType.type === "CNID") &&
// data.chName !== "" &&
handleName(data.enName, data.chName) &&
data.address1 !== "" &&
data.email !== "" &&
data.emailConfirm !== "" &&
data.email === data.emailConfirm &&
data.email == data.emailConfirm &&
data.phone !== "" &&
data.phoneCountryCode !== "" &&
termsAndConAccept === true &&
termsAndConAccept == true &&
fileList.length !== 0 &&
// data.captchaField &&
handlePassword(data.password) &&
handleEmail(data.email) &&
handleIdNo(data.idNo, selectedIdDocType.type, data.checkDigit) &&
handlePhone(data.phone) &&
handleUsername(data.username) &&
districtValid &&
handleCheckDistrict()&&
!checkUsername &&
!checkEmail &&
!checkIdDocNumber;

setisValid(valid);
!checkIdDocNumber&&
!checkDistrict
) {
setisValid(true)
return isValid
} else {
setisValid(false)
return isValid
}
};



const handleCheckBoxChange = (event) => {
if (event.target.name == 'termsAndConAccept') {
setTermsAndConAccept(event.target.checked)
@@ -460,28 +449,14 @@ const CustomFormWizard = (props) => {
// }, [selectedAddress4, selectedAddress5])

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 = () => {
// hard stop
if (!handleCheckDistrict()) {
setLoding(false);
return;
}

if (!isValid) {
setLoding(false);
return;
}

setLoding(true);
values.idDocType = selectedIdDocType.type
values.address4 = selectedAddress4 == null ? "" : selectedAddress4.type
@@ -863,15 +838,6 @@ const CustomFormWizard = (props) => {

const { values } = formik

useEffect(() => {
if (
selectedAddress5?.type === "hongKong" &&
values?.captchaField?.length === 5
) {
handleCheckDistrict();
}
}, [values?.captchaField, selectedAddress5, selectedAddress4]);

useEffect(() => {
checkDataField(values)
}, [values])
@@ -894,7 +860,7 @@ const CustomFormWizard = (props) => {
<FormattedMessage id="becomeNewPersonalUser" />
</Typography>
</div>
<Typography mt={0.25} variant="h6" sx={{ color: '#f10000' }}>
<Typography mt={0.25} variant="h6" sx={{ color: '#B00020' }}>
<FormattedMessage id="requireString" />
</Typography>
<Typography mt={0.25} variant="h4" sx={{ color: 'primary.primary' }}>
@@ -909,7 +875,7 @@ const CustomFormWizard = (props) => {
<InputLabel htmlFor="username-signup">
<Typography variant="pnspsFormHeader">
<FormattedMessage id="userLoginName" />
<span style={{ color: '#f10000' }}>*</span>
<span style={{ color: '#B00020' }}>*</span>
{/*<Button*/}
{/* variant="contained"*/}
{/* onClick={handleCheckUsername}*/}
@@ -961,7 +927,7 @@ const CustomFormWizard = (props) => {
<InputLabel htmlFor="password-signup">
<Typography variant="pnspsFormHeader">
<FormattedMessage id="userPassword" />
<span style={{ color: '#f10000' }}>*</span>
<span style={{ color: '#B00020' }}>*</span>
</Typography>
</InputLabel>
</Stack>
@@ -1023,7 +989,7 @@ const CustomFormWizard = (props) => {
<InputLabel htmlFor="confirmPassword-signup">
<Typography variant="pnspsFormHeader">
<FormattedMessage id="confirmPassword" />
<span style={{ color: '#f10000' }}>*</span>
<span style={{ color: '#B00020' }}>*</span>
</Typography>
</InputLabel>
<OutlinedInput
@@ -1099,7 +1065,7 @@ const CustomFormWizard = (props) => {
<InputLabel htmlFor="idDocType-signup">
<Typography variant="pnspsFormHeader">
<FormattedMessage id="userIdDoc" />
<span style={{ color: '#f10000' }}>*</span>
<span style={{ color: '#B00020' }}>*</span>
</Typography>
</InputLabel>
{/* {formik.touched.enName && formik.errors.enName && (
@@ -1306,7 +1272,7 @@ const CustomFormWizard = (props) => {
<InputLabel htmlFor="enName-signup">
<Typography variant="pnspsFormHeader">
<FormattedMessage id="userEnglishName" />
{selectedIdDocType.type === "CNID" ? "" : <span style={{ color: '#f10000' }}></span>}
{selectedIdDocType.type === "CNID" ? "" : <span style={{ color: '#B00020' }}></span>}
</Typography>
</InputLabel>
<OutlinedInput
@@ -1339,7 +1305,7 @@ const CustomFormWizard = (props) => {
<InputLabel htmlFor="chName-signup">
<Typography variant="pnspsFormHeader">
<FormattedMessage id="userChineseName" />
<span style={{ color: '#f10000' }}></span>
<span style={{ color: '#B00020' }}></span>
</Typography>
</InputLabel>
<OutlinedInput
@@ -1373,7 +1339,7 @@ const CustomFormWizard = (props) => {
<InputLabel htmlFor="address1-signup">
<Typography variant="pnspsFormHeader">
<FormattedMessage id="formAddress" />
<span style={{ color: '#f10000' }}>*</span>
<span style={{ color: '#B00020' }}>*</span>
</Typography>
</InputLabel>
<OutlinedInput
@@ -1438,9 +1404,6 @@ 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' },
@@ -1536,7 +1499,7 @@ const CustomFormWizard = (props) => {
<InputLabel htmlFor="email-signup">
<Typography variant="pnspsFormHeader">
<FormattedMessage id="userContactEmail" />
<span style={{ color: '#f10000' }}>*</span>
<span style={{ color: '#B00020' }}>*</span>
</Typography>
</InputLabel>
<OutlinedInput
@@ -1574,7 +1537,7 @@ const CustomFormWizard = (props) => {
<InputLabel htmlFor="emailConfirm-signup">
<Typography variant="pnspsFormHeader">
<FormattedMessage id="confirmEmail" />
<span style={{ color: '#f10000' }}>*</span>
<span style={{ color: '#B00020' }}>*</span>
</Typography>
</InputLabel>
<OutlinedInput
@@ -1617,7 +1580,7 @@ const CustomFormWizard = (props) => {
<InputLabel htmlFor="phone-signup">
<Typography variant="pnspsFormHeader">
<FormattedMessage id="userContactNumber" />
<span style={{ color: '#f10000' }}>*</span>
<span style={{ color: '#B00020' }}>*</span>
</Typography>
</InputLabel>
<Stack direction="row">
@@ -1761,7 +1724,7 @@ const CustomFormWizard = (props) => {
<Stack spacing={1} direction="column" justifyContent="space-between" alignItems="baseline" sx={{ mb: { xs: -0.5, sm: 0.5 } }}>
<Typography display="inline" variant="h4" sx={{ color: 'primary.primary' }}>
<FormattedMessage id="userIdDoc" />
<span style={{ color: '#f10000' }}>*</span></Typography>
<span style={{ color: '#B00020' }}>*</span></Typography>
<Typography display="inline" variant="subtitle1" sx={{ color: 'primary.primary' }}>
<FormattedMessage id="pleaseUploadIdDoc" />
</Typography>
@@ -1801,7 +1764,7 @@ const CustomFormWizard = (props) => {
<Grid item xs={12} md={12}>
<Typography display="inline" variant="h4" sx={{ color: 'primary.primary' }}>
<FormattedMessage id="termsAndCondition" />
<span style={{ color: '#f10000' }}>*</span>
<span style={{ color: '#B00020' }}>*</span>
</Typography>
</Grid>
<Grid item xs={12} md={12}>
@@ -1856,7 +1819,7 @@ const CustomFormWizard = (props) => {
<Stack direction="column">
<Typography display="inline" variant="h4" sx={{ color: 'primary.primary' }}>
<FormattedMessage id="verify" />
<span style={{ color: '#f10000' }}>*</span>
<span style={{ color: '#B00020' }}>*</span>
</Typography>
<Stack spacing={1} direction="row">
<Grid item xs={5} lg={5} style={{ "border": "1px solid black" }}>
@@ -1891,11 +1854,6 @@ const CustomFormWizard = (props) => {
{formik.errors.captchaField}
</FormHelperText>
)}
<Stack spacing={1} direction="row">
<Button onClick={playCaptchaAudio} aria-label={intl.formatMessage({id:"captchaPlayAudioAria"})}>
<FormattedMessage id="captchaPlayAudio" />
</Button>
</Stack>
</Stack>
</Grid>
</Grid>
@@ -1912,7 +1870,7 @@ const CustomFormWizard = (props) => {
<FormattedMessage id="becomeNewPersonalUser" />
</Typography>
</div>
{/* <Typography mt={0.25} variant="h6" sx={{ fontSize: 12,color: '#f10000'}}>註有*的項目必須輸入資料</Typography> */}
{/* <Typography mt={0.25} variant="h6" sx={{ fontSize: 12,color: '#B00020'}}>註有*的項目必須輸入資料</Typography> */}
<Typography mt={0.25} variant="h4" sx={{ color: 'primary.primary' }}>
<FormattedMessage id="yourLoginInformation" />
</Typography>
@@ -2147,4 +2105,4 @@ const CustomFormWizard = (props) => {
);
}

export default CustomFormWizard;
export default CustomFormWizard;

+ 43
- 111
src/pages/authentication/auth-forms/IAmSmartFormWizard.js Ver ficheiro

@@ -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 ?
<LoadingComponent /> :
@@ -523,7 +466,7 @@ const CustomFormWizard = (props) => {
</Typography>
</div>
<Typography mt={0.25} variant="h6" sx={{ color: '#f10000' }}>
<Typography mt={0.25} variant="h6" sx={{ color: '#B00020' }}>
<FormattedMessage id="requireString" />。
</Typography>
<Stack mt={1} direction="row" style={{ alignItems: "center" }}><img src={iAmSmartICon} alt="iAM Smart" width="25" /><Typography mt={0.25} variant="h6" >: <FormattedMessage id="MSG.providedByIAmSmart" /></Typography></Stack>
@@ -589,7 +532,7 @@ const CustomFormWizard = (props) => {
<InputLabel htmlFor="address1-signup">
<Typography variant="h5">
<FormattedMessage id="formAddress" />
<span style={{ color: '#f10000' }}>*</span>
<span style={{ color: '#B00020' }}>*</span>
</Typography>
</InputLabel>
@@ -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) => <TextField error={checkDistrict} {...params} placeholder={intl.formatMessage({ id: 'region' })}
renderInput={(params) => <TextField error={checkDistrict} {...params} placeholder={intl.formatMessage({ id: 'region' })}
/>}
/>
<Autocomplete
@@ -747,7 +687,7 @@ const CustomFormWizard = (props) => {
<InputLabel htmlFor="email-signup">
<Typography variant="h5">
<FormattedMessage id="userContactEmail" />
<span style={{ color: '#f10000' }}>*</span>
<span style={{ color: '#B00020' }}>*</span>
</Typography>
</InputLabel>
@@ -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) => {
<InputLabel htmlFor="emailConfirm-signup">
<Typography variant="h5">
<FormattedMessage id="confirmEmail" />
<span style={{ color: '#f10000' }}>*</span>
<span style={{ color: '#B00020' }}>*</span>
</Typography>
</InputLabel>
<Stack direction="row" justifyContent="space-between" alignItems="baseline" sx={{ mb: { xs: -0.5, sm: 0.5 } }}>
@@ -841,7 +778,7 @@ const CustomFormWizard = (props) => {
<InputLabel htmlFor="phone-signup">
<Typography variant="h5">
<FormattedMessage id="userContactNumber" />
<span style={{ color: '#f10000' }}>*</span>
<span style={{ color: '#B00020' }}>*</span>
</Typography>
</InputLabel>
@@ -985,7 +922,7 @@ const CustomFormWizard = (props) => {
<Grid item xs={12} md={12}>
<Typography display="inline" variant="h4" sx={{ color: 'primary.primary' }}>
<FormattedMessage id="termsAndCondition" />
<span style={{ color: '#f10000' }}>*</span>
<span style={{ color: '#B00020' }}>*</span>
</Typography>
</Grid>
<Grid item xs={12} md={12}>
@@ -1042,7 +979,7 @@ const CustomFormWizard = (props) => {
<Stack direction="column">
<Typography display="inline" variant="h4" sx={{ color: 'primary.primary' }}>
<FormattedMessage id="verify" />
<span style={{ color: '#f10000' }}>*</span>
<span style={{ color: '#B00020' }}>*</span>
</Typography>
<Stack spacing={1} direction="row">
<Grid item xs={5} lg={5} style={{ "border": "1px solid black" }}>
@@ -1077,11 +1014,6 @@ const CustomFormWizard = (props) => {
{formik.errors.captchaField}
</FormHelperText>
)}
<Stack spacing={1} direction="row">
<Button onClick={playCaptchaAudio} aria-label={intl.formatMessage({id:"captchaPlayAudioAria"})}>
<FormattedMessage id="captchaPlayAudio" />
</Button>
</Stack>
</Stack>
</Grid>
</Grid>
@@ -1282,4 +1214,4 @@ const CustomFormWizard = (props) => {
);
}

export default CustomFormWizard;
export default CustomFormWizard;

+ 53
- 10
src/pages/dashboard/Public/index.js Ver ficheiro

@@ -93,7 +93,25 @@ const DashboardDefault = () => {
let list = []
response.map((item) => {
list.push(
<Button onClick={()=>{navigate("/msg/details/"+item.id);}} color={item.readTime?"gray":"black"} style={{justifyContent: "flex-start"}} sx={{p:2}}>
<Button
onClick={() => 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",
},
}}
>
<Stack direction="column" >
<Typography variant='string' align="justify"><b>{item.subject}</b></Typography>
<Typography align="justify">{DateUtils.datetimeStr(item.sentDate)}</Typography>
@@ -165,18 +183,29 @@ const DashboardDefault = () => {
<Grid container justifyContent="center" spacing={2} sx={{ pt: 2 }} alignitems="stretch" >
<Grid item xs={12} lg={5} sx={{ pt: 2 }} style={{ height: '100%' }}>
<Button
xs={12} onClick={() => { 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"
}
}}
>
<Stack direction="row" spacing={2}>
<AdsClickRoundedIcon />
<Stack direction="column" alignItems="start">
<Typography variant="h4" >
<Stack direction="column" alignItems="flex-start">
<Typography variant="h4" sx={{ color: "#0C489E" }}>
<FormattedMessage id="submitApplication" />
</Typography>
<Typography >
<Typography sx={{ color: "#1A1A1A" }}>
<FormattedMessage id="applicationSubheading" />
</Typography>
</Stack>
@@ -187,9 +216,15 @@ const DashboardDefault = () => {
<FormattedMessage id="announcement" />
</Typography>
<Button
color="gray"
variant="text"
aria-label={intl.formatMessage({ id: 'viewAllAnnouncement' })}
onClick={()=>{navigate("/announcement/search")}}
onClick={() => { navigate("/announcement/search"); }}
sx={{
color: '#0C489E',
textTransform: 'none',
textDecoration: 'underline',
'&:hover': { textDecoration: 'underline' }
}}
><u>
<FormattedMessage id="viewAllAnnouncement" />
</u></Button>
@@ -206,9 +241,17 @@ const DashboardDefault = () => {
<FormattedMessage id="systemMessage" />
</Typography>
<Button
variant="text"
aria-label={intl.formatMessage({ id: 'viewAllSystemMessage' })}
onClick={() => { navigate("/msg/search"); }}
color="gray"
sx={{
color: '#0C489E',
textTransform: 'none',
textDecoration: 'underline',
'&:hover': {
textDecoration: 'underline',
},
}}
><u>
<FormattedMessage id="viewAllSystemMessage" />
</u></Button>


+ 1
- 1
src/pages/pnspsUserGroupDetailPage/index.js Ver ficheiro

@@ -203,7 +203,7 @@ const UserMaintainPage = () => {
alignItems: 'end'
}}
onClick={() => { location.reload() }}
color="gray"
color="secondary"
>
<Typography variant="h5">Reset & Back</Typography>
</Button>


+ 76
- 23
src/themes/buttonConst.js Ver ficheiro

@@ -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)',
},
},
},
},


+ 51
- 22
src/themes/themeConst.js Ver ficheiro

@@ -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: {


+ 3
- 3
src/utils/statusUtils/DnStatus.js Ver ficheiro

@@ -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);


+ 5
- 5
src/utils/statusUtils/PaymentStatus.js Ver ficheiro

@@ -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);


+ 5
- 5
src/utils/statusUtils/ProofStatus.js Ver ficheiro

@@ -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);


+ 59
- 49
src/utils/statusUtils/PublicNoteStatusUtils.js Ver ficheiro

@@ -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 })
}


Carregando…
Cancelar
Guardar