| @@ -170,6 +170,12 @@ const UserInformationCard_Organization = ({ userData, loadDataFun, orgData }) => | |||
| setIsConfirmPopUp(true); | |||
| }; | |||
| const onResetBack = async () => { | |||
| setEditMode(false); | |||
| loadDataFun(); // reload userData + orgData | |||
| formik.resetForm(); // reset to latest initialValues after reinitialize | |||
| }; | |||
| return ( | |||
| <MainCard elevation={0} | |||
| border={false} | |||
| @@ -192,7 +198,7 @@ const UserInformationCard_Organization = ({ userData, loadDataFun, orgData }) => | |||
| <Grid item sx={{ ml: 3, mr: 3 }}> | |||
| <Button | |||
| variant="contained" | |||
| onClick={loadDataFun} | |||
| onClick={onResetBack} | |||
| color="cancel" | |||
| > | |||
| Reset & Back | |||
| @@ -94,6 +94,12 @@ const UserInformationCard_Organization_Pub = ({ userData, loadDataFun,}) => { | |||
| setEditMode(true); | |||
| }; | |||
| const onResetBack = async () => { | |||
| setEditMode(false); | |||
| loadDataFun(); // reload userData + orgData | |||
| formik.resetForm(); // reset to latest initialValues after reinitialize | |||
| }; | |||
| // const onFocus = () => { | |||
| // loadDataFun(); | |||
| // window.removeEventListener("focus", onFocus) | |||
| @@ -112,14 +118,14 @@ const UserInformationCard_Organization_Pub = ({ userData, loadDataFun,}) => { | |||
| {/*top button*/} | |||
| <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"> | |||
| {editMode ? | |||
| <ThemeProvider theme={PNSPS_BUTTON_THEME}> | |||
| <Grid item xs={4} sx={{ ml: 3, mr: 3 }}> | |||
| <Button | |||
| variant="contained" | |||
| onClick={loadDataFun} | |||
| onClick={onResetBack} | |||
| color="cancel" | |||
| > | |||
| <FormattedMessage id="resetAndBack" /> | |||
| @@ -48,7 +48,7 @@ const UserMaintainPage_Organization = () => { | |||
| const params = useParams(); | |||
| const navigate = useNavigate(); | |||
| const [userData, setUserData] = useState({}) | |||
| const [orgData, setOrgData] = useState({}) | |||
| const [orgData, setOrgData] = useState([]) | |||
| const [isLoading, setLoding] = useState(true); | |||
| const intl = useIntl(); | |||
| const { locale } = intl; | |||
| @@ -57,6 +57,8 @@ const UserMaintainPage_Organization = () => { | |||
| const isPrimaryLocale = locale === 'en' ?"Yes":locale === 'zh-HK' ?"是":"是"; | |||
| const notPrimaryLocale = locale === 'en' ?"No":locale === 'zh-HK' ?"否":"否"; | |||
| const reqIdRef = React.useRef(0); | |||
| // const _sx = { | |||
| // ml: 3, | |||
| // mb: 3, | |||
| @@ -91,21 +93,36 @@ const UserMaintainPage_Organization = () => { | |||
| }, []); | |||
| 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=()=>{ | |||
| // window.location.reload(false); | |||
| // } | |||
| const loadData = () => { | |||
| const reqId = ++reqIdRef.current; | |||
| setLoding(true); | |||
| if (isGLDLoggedIn()){ | |||
| HttpUtils.get({ | |||
| @@ -170,8 +187,9 @@ const UserMaintainPage_Organization = () => { | |||
| // console.log("3") | |||
| // console.log(response.data) | |||
| if (reqId !== reqIdRef.current) return; // ignore stale | |||
| setOrgData(response.orgList || []); | |||
| setUserData(response.data); | |||
| setOrgData(response.orgList); | |||
| setLoding(false); | |||
| } | |||
| }); | |||
| @@ -194,7 +212,7 @@ const UserMaintainPage_Organization = () => { | |||
| //response.data["orgId"] = response.data.brExpiryDate?DateUtils.dateValue(response.data.brExpiryDate):""; | |||
| setUserData(response.data); | |||
| setOrgData(response.orgList); | |||
| setOrgData(response.orgList || []); | |||
| setLoding(false); | |||
| // 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", | |||
| }, | |||
| }} | |||
| /> | |||
| ); | |||
| } | |||
| )} | |||
| /> | |||
| ); | |||
| } | |||