| @@ -170,6 +170,12 @@ const UserInformationCard_Organization = ({ userData, loadDataFun, orgData }) => | |||||
| setIsConfirmPopUp(true); | setIsConfirmPopUp(true); | ||||
| }; | }; | ||||
| const onResetBack = async () => { | |||||
| setEditMode(false); | |||||
| loadDataFun(); // reload userData + orgData | |||||
| formik.resetForm(); // reset to latest initialValues after reinitialize | |||||
| }; | |||||
| return ( | return ( | ||||
| <MainCard elevation={0} | <MainCard elevation={0} | ||||
| border={false} | border={false} | ||||
| @@ -192,7 +198,7 @@ const UserInformationCard_Organization = ({ userData, loadDataFun, orgData }) => | |||||
| <Grid item sx={{ ml: 3, mr: 3 }}> | <Grid item sx={{ ml: 3, mr: 3 }}> | ||||
| <Button | <Button | ||||
| variant="contained" | variant="contained" | ||||
| onClick={loadDataFun} | |||||
| onClick={onResetBack} | |||||
| color="cancel" | color="cancel" | ||||
| > | > | ||||
| Reset & Back | Reset & Back | ||||
| @@ -94,6 +94,12 @@ const UserInformationCard_Organization_Pub = ({ userData, loadDataFun,}) => { | |||||
| setEditMode(true); | setEditMode(true); | ||||
| }; | }; | ||||
| const onResetBack = async () => { | |||||
| setEditMode(false); | |||||
| loadDataFun(); // reload userData + orgData | |||||
| formik.resetForm(); // reset to latest initialValues after reinitialize | |||||
| }; | |||||
| // const onFocus = () => { | // const onFocus = () => { | ||||
| // loadDataFun(); | // loadDataFun(); | ||||
| // window.removeEventListener("focus", onFocus) | // window.removeEventListener("focus", onFocus) | ||||
| @@ -112,14 +118,14 @@ const UserInformationCard_Organization_Pub = ({ userData, loadDataFun,}) => { | |||||
| {/*top button*/} | {/*top button*/} | ||||
| <Grid container alignItems="center" justifyContent="flex-start"> | <Grid container alignItems="center" justifyContent="flex-start"> | ||||
| <Grid item s={12} md={12} lg={12} sx={{ lg:{mb: 3}, mt: 2 }}> | |||||
| <Grid item s={12} md={12} lg={12} sx={{ mb: 3, mt: 2 }}> | |||||
| <Grid container direction="row" justifyContent="flex-start"> | <Grid container direction="row" justifyContent="flex-start"> | ||||
| {editMode ? | {editMode ? | ||||
| <ThemeProvider theme={PNSPS_BUTTON_THEME}> | <ThemeProvider theme={PNSPS_BUTTON_THEME}> | ||||
| <Grid item xs={4} sx={{ ml: 3, mr: 3 }}> | <Grid item xs={4} sx={{ ml: 3, mr: 3 }}> | ||||
| <Button | <Button | ||||
| variant="contained" | variant="contained" | ||||
| onClick={loadDataFun} | |||||
| onClick={onResetBack} | |||||
| color="cancel" | color="cancel" | ||||
| > | > | ||||
| <FormattedMessage id="resetAndBack" /> | <FormattedMessage id="resetAndBack" /> | ||||
| @@ -48,7 +48,7 @@ const UserMaintainPage_Organization = () => { | |||||
| const params = useParams(); | const params = useParams(); | ||||
| const navigate = useNavigate(); | const navigate = useNavigate(); | ||||
| const [userData, setUserData] = useState({}) | const [userData, setUserData] = useState({}) | ||||
| const [orgData, setOrgData] = useState({}) | |||||
| const [orgData, setOrgData] = useState([]) | |||||
| const [isLoading, setLoding] = useState(true); | const [isLoading, setLoding] = useState(true); | ||||
| const intl = useIntl(); | const intl = useIntl(); | ||||
| const { locale } = intl; | const { locale } = intl; | ||||
| @@ -57,6 +57,8 @@ const UserMaintainPage_Organization = () => { | |||||
| const isPrimaryLocale = locale === 'en' ?"Yes":locale === 'zh-HK' ?"是":"是"; | const isPrimaryLocale = locale === 'en' ?"Yes":locale === 'zh-HK' ?"是":"是"; | ||||
| const notPrimaryLocale = locale === 'en' ?"No":locale === 'zh-HK' ?"否":"否"; | const notPrimaryLocale = locale === 'en' ?"No":locale === 'zh-HK' ?"否":"否"; | ||||
| const reqIdRef = React.useRef(0); | |||||
| // const _sx = { | // const _sx = { | ||||
| // ml: 3, | // ml: 3, | ||||
| // mb: 3, | // mb: 3, | ||||
| @@ -91,21 +93,36 @@ const UserMaintainPage_Organization = () => { | |||||
| }, []); | }, []); | ||||
| useEffect(() => { | useEffect(() => { | ||||
| if (orgData.length > 0 && userData["orgId"] != null){ | |||||
| userData["orgId"] = getObjectByType(orgData, "id", userData["orgId"]); | |||||
| } | |||||
| }, [orgData, userData]); | |||||
| if (!Array.isArray(orgData) || orgData.length === 0) return; | |||||
| setUserData(prev => { | |||||
| if (!prev?.orgId) return prev; | |||||
| // if already an object, keep it (or re-find to ensure same reference as option) | |||||
| const orgIdValue = typeof prev.orgId === "object" ? prev.orgId.id : prev.orgId; | |||||
| const found = orgData.find(o => o.id === orgIdValue); | |||||
| if (!found) return prev; | |||||
| // avoid unnecessary state updates | |||||
| if (prev.orgId === found) return prev; | |||||
| return { ...prev, orgId: found }; | |||||
| }); | |||||
| }, [orgData, userData?.orgId]); | |||||
| useEffect(() => { | |||||
| // console.log(userData); | |||||
| loadData(); | |||||
| }, [locale]); | |||||
| // useEffect(() => { | |||||
| // // console.log(userData); | |||||
| // loadData(); | |||||
| // }, [locale]); | |||||
| // const reloadPage=()=>{ | // const reloadPage=()=>{ | ||||
| // window.location.reload(false); | // window.location.reload(false); | ||||
| // } | // } | ||||
| const loadData = () => { | const loadData = () => { | ||||
| const reqId = ++reqIdRef.current; | |||||
| setLoding(true); | setLoding(true); | ||||
| if (isGLDLoggedIn()){ | if (isGLDLoggedIn()){ | ||||
| HttpUtils.get({ | HttpUtils.get({ | ||||
| @@ -170,8 +187,9 @@ const UserMaintainPage_Organization = () => { | |||||
| // console.log("3") | // console.log("3") | ||||
| // console.log(response.data) | // console.log(response.data) | ||||
| if (reqId !== reqIdRef.current) return; // ignore stale | |||||
| setOrgData(response.orgList || []); | |||||
| setUserData(response.data); | setUserData(response.data); | ||||
| setOrgData(response.orgList); | |||||
| setLoding(false); | setLoding(false); | ||||
| } | } | ||||
| }); | }); | ||||
| @@ -194,7 +212,7 @@ const UserMaintainPage_Organization = () => { | |||||
| //response.data["orgId"] = response.data.brExpiryDate?DateUtils.dateValue(response.data.brExpiryDate):""; | //response.data["orgId"] = response.data.brExpiryDate?DateUtils.dateValue(response.data.brExpiryDate):""; | ||||
| setUserData(response.data); | setUserData(response.data); | ||||
| setOrgData(response.orgList); | |||||
| setOrgData(response.orgList || []); | |||||
| setLoding(false); | setLoding(false); | ||||
| // console.log(response.data) | // console.log(response.data) | ||||
| } | } | ||||
| @@ -1,60 +1,89 @@ | |||||
| import { | |||||
| Autocomplete, TextField | |||||
| } from '@mui/material'; | |||||
| import { useState } from 'react'; | |||||
| import { Autocomplete, TextField } from '@mui/material'; | |||||
| import { useEffect, useState } from 'react'; | |||||
| export default function Combo ({valueName, disabled, form, dataList, filterOptions, getOptionLabel, isOptionEqualToValue, onInputChange, onChange, ...props}){ | |||||
| const [value, setValue] = useState(form.values[valueName]); | |||||
| const [inputValue, setInputValue] = useState(""); | |||||
| export default function Combo({ | |||||
| valueName, | |||||
| disabled, | |||||
| form, | |||||
| dataList = [], | |||||
| filterOptions, | |||||
| getOptionLabel, | |||||
| isOptionEqualToValue, | |||||
| onInputChange, | |||||
| onChange, | |||||
| ...props | |||||
| }) { | |||||
| const formValue = form.values[valueName] ?? null; | |||||
| return ( | |||||
| <Autocomplete | |||||
| {...props} | |||||
| disablePortal | |||||
| fullWidth | |||||
| id={valueName} | |||||
| name={valueName} | |||||
| disabled={disabled} | |||||
| disableClearable | |||||
| value={value} | |||||
| inputValue={inputValue} | |||||
| filterOptions={filterOptions} | |||||
| options={dataList} | |||||
| getOptionLabel={getOptionLabel} | |||||
| isOptionEqualToValue={(option, newValue)=>{ | |||||
| if(isOptionEqualToValue) | |||||
| isOptionEqualToValue(option,newValue, setValue,setInputValue ) | |||||
| }} | |||||
| onInputChange={(event, newValue) => { | |||||
| if(onInputChange){ | |||||
| onInputChange(event,newValue, setInputValue) | |||||
| } else { | |||||
| setInputValue(newValue); | |||||
| } | |||||
| }} | |||||
| onChange={(event, newValue) => { | |||||
| setValue(newValue); | |||||
| if (!onChange){ | |||||
| form.setFieldValue(valueName, newValue); | |||||
| }else{ | |||||
| onChange(event, newValue); | |||||
| } | |||||
| }} | |||||
| sx={{ | |||||
| '& .MuiInputBase-root': { alignItems: 'center' }, | |||||
| '& .MuiAutocomplete-endAdornment': { top: '50%', transform: 'translateY(-50%)' }, | |||||
| '& .MuiOutlinedInput-root': { height: 40 }, | |||||
| "& .MuiInputBase-input.Mui-disabled": { | |||||
| WebkitTextFillColor: "#000000", | |||||
| background: "#f8f8f8", | |||||
| }, | |||||
| }} | |||||
| renderInput={(params) => <TextField {...params} sx={{ | |||||
| "& .MuiInputBase-input.Mui-disabled": { | |||||
| WebkitTextFillColor: "#000000", | |||||
| background: "#f8f8f8", | |||||
| }, | |||||
| }}/>} | |||||
| const [inputValue, setInputValue] = useState(""); | |||||
| // optional: keep input text in sync when form value changes (e.g. after reload/reset) | |||||
| useEffect(() => { | |||||
| if (!formValue) return; | |||||
| try { | |||||
| const label = getOptionLabel ? getOptionLabel(formValue) : ""; | |||||
| if (typeof label === "string") setInputValue(label); | |||||
| } catch { | |||||
| // ignore label errors | |||||
| } | |||||
| // eslint-disable-next-line react-hooks/exhaustive-deps | |||||
| }, [formValue]); | |||||
| const defaultIsEqual = (option, value) => option?.id != null && value?.id != null | |||||
| ? option.id === value.id | |||||
| : option === value; | |||||
| return ( | |||||
| <Autocomplete | |||||
| {...props} | |||||
| disablePortal | |||||
| fullWidth | |||||
| id={valueName} | |||||
| disabled={disabled} | |||||
| // consider removing disableClearable if you allow empty | |||||
| disableClearable | |||||
| options={Array.isArray(dataList) ? dataList : []} | |||||
| value={formValue} | |||||
| inputValue={inputValue} | |||||
| filterOptions={filterOptions} | |||||
| getOptionLabel={(opt) => { | |||||
| if (!opt) return ""; | |||||
| const v = getOptionLabel ? getOptionLabel(opt) : String(opt); | |||||
| return typeof v === "string" ? v : ""; | |||||
| }} | |||||
| isOptionEqualToValue={(option, value) => { | |||||
| return isOptionEqualToValue | |||||
| ? isOptionEqualToValue(option, value) | |||||
| : defaultIsEqual(option, value); | |||||
| }} | |||||
| onInputChange={(event, newValue) => { | |||||
| if (onInputChange) onInputChange(event, newValue, setInputValue); | |||||
| else setInputValue(newValue); | |||||
| }} | |||||
| onChange={(event, newValue) => { | |||||
| if (onChange) onChange(event, newValue); | |||||
| else form.setFieldValue(valueName, newValue); | |||||
| }} | |||||
| sx={{ | |||||
| '& .MuiInputBase-root': { alignItems: 'center' }, | |||||
| '& .MuiAutocomplete-endAdornment': { top: '50%', transform: 'translateY(-50%)' }, | |||||
| '& .MuiOutlinedInput-root': { height: 40 }, | |||||
| "& .MuiInputBase-input.Mui-disabled": { | |||||
| WebkitTextFillColor: "#000000", | |||||
| background: "#f8f8f8", | |||||
| }, | |||||
| }} | |||||
| renderInput={(params) => ( | |||||
| <TextField | |||||
| {...params} | |||||
| sx={{ | |||||
| "& .MuiInputBase-input.Mui-disabled": { | |||||
| WebkitTextFillColor: "#000000", | |||||
| background: "#f8f8f8", | |||||
| }, | |||||
| }} | |||||
| /> | /> | ||||
| ); | |||||
| } | |||||
| )} | |||||
| /> | |||||
| ); | |||||
| } | |||||