refactor: enhance form validation and error handling in EditField, DefaultSelect, and Form components
This commit is contained in:
@@ -2,6 +2,21 @@ import EditGrayA from "@/components/icons/EditGrayA";
|
|||||||
import EditOrangeA from "@/components/icons/EditOrangeA";
|
import EditOrangeA from "@/components/icons/EditOrangeA";
|
||||||
import { Button, TextField } from "@mui/material";
|
import { Button, TextField } from "@mui/material";
|
||||||
|
|
||||||
|
// تابع تبدیل اعداد فارسی و عربی به انگلیسی
|
||||||
|
const convertPersianToEnglish = (str) => {
|
||||||
|
const persianNumbers = ['۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹'];
|
||||||
|
const arabicNumbers = ['٠', '١', '٢', '٣', '٤', '٥', '٦', '٧', '٨', '٩'];
|
||||||
|
|
||||||
|
let result = str;
|
||||||
|
for (let i = 0; i < 10; i++) {
|
||||||
|
const regex = new RegExp(persianNumbers[i], 'g');
|
||||||
|
result = result.replace(regex, i.toString());
|
||||||
|
const arabicRegex = new RegExp(arabicNumbers[i], 'g');
|
||||||
|
result = result.replace(arabicRegex, i.toString());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
function EditField({
|
function EditField({
|
||||||
changeData,
|
changeData,
|
||||||
multiline,
|
multiline,
|
||||||
@@ -14,7 +29,31 @@ function EditField({
|
|||||||
noDisable,
|
noDisable,
|
||||||
type,
|
type,
|
||||||
props,
|
props,
|
||||||
|
error,
|
||||||
}) {
|
}) {
|
||||||
|
const handleChange = (e) => {
|
||||||
|
let inputValue = e.target.value;
|
||||||
|
|
||||||
|
// اگر فیلد کد ملی یا شماره موبایل یا شماره بیمه است، فقط اعداد انگلیسی
|
||||||
|
if (name === "national_code" || name === "phone" || name === "insurance_id" || type === "tel") {
|
||||||
|
// ابتدا اعداد فارسی و عربی را به انگلیسی تبدیل کن
|
||||||
|
inputValue = convertPersianToEnglish(inputValue);
|
||||||
|
// فقط اعداد انگلیسی را نگه دار
|
||||||
|
inputValue = inputValue.replace(/[^0-9]/g, '');
|
||||||
|
|
||||||
|
// محدودیت تعداد ارقام
|
||||||
|
if (name === "national_code" && inputValue.length > 10) {
|
||||||
|
inputValue = inputValue.slice(0, 10);
|
||||||
|
} else if (name === "phone" && inputValue.length > 11) {
|
||||||
|
inputValue = inputValue.slice(0, 11);
|
||||||
|
} else if (name === "insurance_id" && inputValue.length > 16) {
|
||||||
|
inputValue = inputValue.slice(0, 16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
changeData(inputValue, "value", name);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative w-full">
|
<div className="relative w-full">
|
||||||
<TextField
|
<TextField
|
||||||
@@ -25,12 +64,11 @@ function EditField({
|
|||||||
value={data?.value || ""}
|
value={data?.value || ""}
|
||||||
placeholder={placeholder || ""}
|
placeholder={placeholder || ""}
|
||||||
disabled={noDisable ? false : !data?.isEdit}
|
disabled={noDisable ? false : !data?.isEdit}
|
||||||
className={`!w-full res-field-account dark:!bg-transparent ${
|
error={!!error}
|
||||||
isTransparent ? "!bg-transparent lg:!bg-[#FFF]" : "!bg-[#FFF]"
|
helperText={error || ""}
|
||||||
}`}
|
className={`!w-full res-field-account dark:!bg-transparent ${isTransparent ? "!bg-transparent lg:!bg-[#FFF]" : "!bg-[#FFF]"}
|
||||||
onChange={(e) => {
|
}`}
|
||||||
changeData(e.target.value, "value", name);
|
onChange={handleChange}
|
||||||
}}
|
|
||||||
sx={{
|
sx={{
|
||||||
"& .MuiFormLabel-root": {
|
"& .MuiFormLabel-root": {
|
||||||
top: "-4px !important",
|
top: "-4px !important",
|
||||||
@@ -43,6 +81,10 @@ function EditField({
|
|||||||
padding: "0 !important",
|
padding: "0 !important",
|
||||||
borderRadius: "8px !important",
|
borderRadius: "8px !important",
|
||||||
},
|
},
|
||||||
|
"& .MuiFormHelperText-root": {
|
||||||
|
marginLeft: "0",
|
||||||
|
marginRight: "14px",
|
||||||
|
},
|
||||||
".Mui-focused": {
|
".Mui-focused": {
|
||||||
"& .MuiOutlinedInput-notchedOutline": {
|
"& .MuiOutlinedInput-notchedOutline": {
|
||||||
border: "1px solid #5559CE !important",
|
border: "1px solid #5559CE !important",
|
||||||
@@ -50,17 +92,17 @@ function EditField({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
"& input::-webkit-outer-spin-button, & input::-webkit-inner-spin-button":
|
"& input::-webkit-outer-spin-button, & input::-webkit-inner-spin-button":
|
||||||
{
|
{
|
||||||
display: "none",
|
display: "none",
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
className={`!absolute !left-[8px] !-translate-y-1/2 !min-w-fit !p-1
|
className={`!absolute !left-[8px] !-translate-y-1/2 !min-w-fit !p-1
|
||||||
${multiline ? "!top-[24px]" : "!top-1/2"}`}
|
${multiline ? "!top-[24px]" : "!top-1/2"}`}
|
||||||
// onClick={() => {
|
// onClick={() => {
|
||||||
// changeData(!data.isEdit, "isEdit", name);
|
// changeData(!data.isEdit, "isEdit", name);
|
||||||
// }}
|
// }}
|
||||||
>
|
>
|
||||||
{icon ? icon : iconGray ? <EditGrayA /> : <EditOrangeA />}
|
{icon ? icon : iconGray ? <EditGrayA /> : <EditOrangeA />}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -74,8 +74,14 @@ function DefaultSelect({ list, value, updateData, label, error, name }) {
|
|||||||
),
|
),
|
||||||
}}
|
}}
|
||||||
placeholder={label}
|
placeholder={label}
|
||||||
|
error={!!error}
|
||||||
|
helperText={error || ""}
|
||||||
sx={{
|
sx={{
|
||||||
borderRadius: "8px",
|
borderRadius: "8px",
|
||||||
|
"& .MuiFormHelperText-root": {
|
||||||
|
marginLeft: "0",
|
||||||
|
marginRight: "14px",
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import Content from "./Content";
|
|||||||
import EditField from "../../../app/component/fields/EditField";
|
import EditField from "../../../app/component/fields/EditField";
|
||||||
import DefaultSelect from "@/app/component/selectors/DefaultSelect";
|
import DefaultSelect from "@/app/component/selectors/DefaultSelect";
|
||||||
|
|
||||||
function Form({ changeData, insurance, supplementaryInsurance, data, isForAnother }) {
|
function Form({ changeData, insurance, supplementaryInsurance, data, isForAnother, errors = {} }) {
|
||||||
const findBasicInsuranceVal = () => {
|
const findBasicInsuranceVal = () => {
|
||||||
return insurance?.find(
|
return insurance?.find(
|
||||||
(g) =>
|
(g) =>
|
||||||
@@ -25,20 +25,21 @@ function Form({ changeData, insurance, supplementaryInsurance, data, isForAnothe
|
|||||||
justify-center gap-x-[24px] gap-y-[12px] md:gap-y-[14px] lg:gap-y-[16px] grid-cols-1 sm:grid-cols-2"
|
justify-center gap-x-[24px] gap-y-[12px] md:gap-y-[14px] lg:gap-y-[16px] grid-cols-1 sm:grid-cols-2"
|
||||||
>
|
>
|
||||||
<Content title="شماره موبایل" isConfirmed={isForAnother ? false : true}>
|
<Content title="شماره موبایل" isConfirmed={isForAnother ? false : true}>
|
||||||
<EditField changeData={changeData} data={data?.phone} name="phone" />
|
<EditField changeData={changeData} data={data?.phone} name="phone" error={errors?.phone} />
|
||||||
</Content>
|
</Content>
|
||||||
<Content title="کد ملی">
|
<Content title="کد ملی">
|
||||||
<EditField
|
<EditField
|
||||||
changeData={changeData}
|
changeData={changeData}
|
||||||
data={data?.national_code}
|
data={data?.national_code}
|
||||||
name="national_code"
|
name="national_code"
|
||||||
|
error={errors?.national_code}
|
||||||
/>
|
/>
|
||||||
</Content>
|
</Content>
|
||||||
<Content title="نام">
|
<Content title="نام">
|
||||||
<EditField changeData={changeData} data={data?.name} name="name" />
|
<EditField changeData={changeData} data={data?.name} name="name" error={errors?.name} />
|
||||||
</Content>
|
</Content>
|
||||||
<Content title="نام خانوادگی">
|
<Content title="نام خانوادگی">
|
||||||
<EditField changeData={changeData} data={data?.family} name="family" />
|
<EditField changeData={changeData} data={data?.family} name="family" error={errors?.family} />
|
||||||
</Content>
|
</Content>
|
||||||
<Content title="جنسیت">
|
<Content title="جنسیت">
|
||||||
<DefaultSelect
|
<DefaultSelect
|
||||||
@@ -54,8 +55,12 @@ function Form({ changeData, insurance, supplementaryInsurance, data, isForAnothe
|
|||||||
? { id: data.gender.value, title: data.gender.value === "male" ? "مرد" : "زن" }
|
? { id: data.gender.value, title: data.gender.value === "male" ? "مرد" : "زن" }
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
|
error={errors?.gender}
|
||||||
/>
|
/>
|
||||||
</Content>
|
</Content>
|
||||||
|
<Content title="شماره بیمه">
|
||||||
|
<EditField changeData={changeData} data={data?.insurance_id} name="insurance_id" error={errors?.insurance_id} />
|
||||||
|
</Content>
|
||||||
<Content title="نوع بیمه">
|
<Content title="نوع بیمه">
|
||||||
<DefaultSelect
|
<DefaultSelect
|
||||||
updateData={(name, val) => changeData(val, "value", name)}
|
updateData={(name, val) => changeData(val, "value", name)}
|
||||||
@@ -63,6 +68,7 @@ function Form({ changeData, insurance, supplementaryInsurance, data, isForAnothe
|
|||||||
name="basic_insurance"
|
name="basic_insurance"
|
||||||
list={insurance}
|
list={insurance}
|
||||||
value={findBasicInsuranceVal()}
|
value={findBasicInsuranceVal()}
|
||||||
|
error={errors?.basic_insurance}
|
||||||
/>
|
/>
|
||||||
</Content>
|
</Content>
|
||||||
<Content title="بیمه تکمیلی">
|
<Content title="بیمه تکمیلی">
|
||||||
@@ -72,6 +78,7 @@ function Form({ changeData, insurance, supplementaryInsurance, data, isForAnothe
|
|||||||
name="supplementary_insurance"
|
name="supplementary_insurance"
|
||||||
list={supplementaryInsurance}
|
list={supplementaryInsurance}
|
||||||
value={findSupplementaryInsuranceVal()}
|
value={findSupplementaryInsuranceVal()}
|
||||||
|
error={errors?.supplementary_insurance}
|
||||||
/>
|
/>
|
||||||
</Content>
|
</Content>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4,35 +4,51 @@ import { Button } from "@mui/material";
|
|||||||
import ArrowLeftB from "@/components/icons/ArrowLeftB";
|
import ArrowLeftB from "@/components/icons/ArrowLeftB";
|
||||||
import { request } from "@/services/response";
|
import { request } from "@/services/response";
|
||||||
|
|
||||||
function SubmitData({ setStep, data, prevData }) {
|
function SubmitData({ setStep, data, prevData, setErrors }) {
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const newStep = () => setStep((prev) => prev + 1);
|
const newStep = () => setStep((prev) => prev + 1);
|
||||||
|
|
||||||
const handleSubmit = () => {
|
const handleSubmit = async () => {
|
||||||
const isChanged = JSON.stringify(prevData) !== JSON.stringify(data);
|
const isChanged = JSON.stringify(prevData) !== JSON.stringify(data);
|
||||||
|
|
||||||
|
// پاک کردن error های قبلی
|
||||||
|
setErrors({});
|
||||||
|
|
||||||
if (isChanged) {
|
if (isChanged) {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
let newData = data;
|
const { basic_insurance, supplementary_insurance, name, family, national_code, gender, insurance_id, uuid } = data;
|
||||||
const { basic_insurance, name, national_code, uuid } = newData;
|
|
||||||
|
|
||||||
newData = {
|
const payload = {
|
||||||
basic_insurance: [basic_insurance.value?.id],
|
name: name?.value,
|
||||||
...(prevData.name?.value === data.name.value ? {} : { name: name }),
|
family: family?.value,
|
||||||
...(prevData.national_code?.value
|
national_code: national_code?.value,
|
||||||
? {}
|
gender: gender?.value,
|
||||||
: { national_code: national_code }),
|
insurance_id: insurance_id?.value,
|
||||||
|
basic_insurance: basic_insurance?.value?.id ? [basic_insurance.value.id] : [],
|
||||||
|
supplementary_insurance: supplementary_insurance?.value?.id ? [supplementary_insurance.value.id] : [],
|
||||||
};
|
};
|
||||||
|
|
||||||
request
|
try {
|
||||||
.patchUserProfile(newData, uuid)
|
if (uuid) {
|
||||||
.then(() => {
|
// اگر uuid داریم، پروفایل وجود داره و باید PATCH کنیم
|
||||||
setLoading(false);
|
console.log('📤 ارسال درخواست PATCH - UUID:', uuid, 'Payload:', payload);
|
||||||
newStep();
|
await request.patchUserProfile(payload, uuid);
|
||||||
})
|
} else {
|
||||||
.catch(() => {
|
// اگر uuid نداریم، پروفایل وجود نداره و باید POST کنیم
|
||||||
setLoading(false);
|
console.log('📤 ارسال درخواست POST - Payload:', payload);
|
||||||
});
|
await request.postUserProfile(payload);
|
||||||
|
}
|
||||||
|
console.log('✅ پروفایل با موفقیت ذخیره شد');
|
||||||
|
setLoading(false);
|
||||||
|
newStep();
|
||||||
|
} catch (error) {
|
||||||
|
console.log('❌ خطا در ذخیره پروفایل:', error?.response?.data || error);
|
||||||
|
setLoading(false);
|
||||||
|
// اگر خطای validation بود، error ها را ست میکنیم
|
||||||
|
if (error?.response?.data) {
|
||||||
|
setErrors(error.response.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
newStep();
|
newStep();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ function Detail({
|
|||||||
}) {
|
}) {
|
||||||
const [insurance, setInsurance] = useState();
|
const [insurance, setInsurance] = useState();
|
||||||
const [supplementaryInsurance, setSupplementaryInsurance] = useState();
|
const [supplementaryInsurance, setSupplementaryInsurance] = useState();
|
||||||
|
const [errors, setErrors] = useState({});
|
||||||
|
|
||||||
const changeData = (value, type, name) =>
|
const changeData = (value, type, name) =>
|
||||||
setData({
|
setData({
|
||||||
...data,
|
...data,
|
||||||
@@ -68,6 +70,7 @@ function Detail({
|
|||||||
supplementaryInsurance={supplementaryInsurance}
|
supplementaryInsurance={supplementaryInsurance}
|
||||||
changeData={changeData}
|
changeData={changeData}
|
||||||
isForAnother={isForAnother}
|
isForAnother={isForAnother}
|
||||||
|
errors={errors}
|
||||||
/>
|
/>
|
||||||
{!isForAnother && (
|
{!isForAnother && (
|
||||||
<Button
|
<Button
|
||||||
@@ -87,7 +90,7 @@ function Detail({
|
|||||||
</p>
|
</p>
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
<SubmitData setStep={setStep} data={data} prevData={prevData} />
|
<SubmitData setStep={setStep} data={data} prevData={prevData} setErrors={setErrors} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ const defaultData = {
|
|||||||
name: { value: "", isEdit: true },
|
name: { value: "", isEdit: true },
|
||||||
family: { value: "", isEdit: true },
|
family: { value: "", isEdit: true },
|
||||||
gender: { value: "", isEdit: true },
|
gender: { value: "", isEdit: true },
|
||||||
|
insurance_id: { value: "", isEdit: true },
|
||||||
basic_insurance: { value: "", isEdit: true },
|
basic_insurance: { value: "", isEdit: true },
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -50,6 +51,7 @@ function AppointmentPage({ doctor, disabledDates, matchedCity }) {
|
|||||||
// سپس اگر uuid داریم، بقیه اطلاعات را از API میگیریم
|
// سپس اگر uuid داریم، بقیه اطلاعات را از API میگیریم
|
||||||
if (userInfo && parsedData) {
|
if (userInfo && parsedData) {
|
||||||
try {
|
try {
|
||||||
|
console.log('📤 درخواست دریافت پروفایل کاربر - UUID:', parsedData.uuid);
|
||||||
const res = await request.getUserProfile(parsedData.uuid);
|
const res = await request.getUserProfile(parsedData.uuid);
|
||||||
|
|
||||||
if (res) {
|
if (res) {
|
||||||
@@ -63,6 +65,7 @@ function AppointmentPage({ doctor, disabledDates, matchedCity }) {
|
|||||||
name: { value: res.name || "", isEdit: true },
|
name: { value: res.name || "", isEdit: true },
|
||||||
family: { value: res.family || "", isEdit: true },
|
family: { value: res.family || "", isEdit: true },
|
||||||
gender: { value: res.gender || "", isEdit: true },
|
gender: { value: res.gender || "", isEdit: true },
|
||||||
|
insurance_id: { value: res.insurance_id || "", isEdit: true },
|
||||||
basic_insurance: {
|
basic_insurance: {
|
||||||
value: res.basic_insurance,
|
value: res.basic_insurance,
|
||||||
isEdit: true,
|
isEdit: true,
|
||||||
@@ -80,6 +83,7 @@ function AppointmentPage({ doctor, disabledDates, matchedCity }) {
|
|||||||
name: { value: "", isEdit: true },
|
name: { value: "", isEdit: true },
|
||||||
family: { value: "", isEdit: true },
|
family: { value: "", isEdit: true },
|
||||||
gender: { value: "", isEdit: true },
|
gender: { value: "", isEdit: true },
|
||||||
|
insurance_id: { value: "", isEdit: true },
|
||||||
basic_insurance: { value: "", isEdit: true },
|
basic_insurance: { value: "", isEdit: true },
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@@ -104,6 +108,7 @@ function AppointmentPage({ doctor, disabledDates, matchedCity }) {
|
|||||||
|
|
||||||
if (userInfo && parsedData && !data.uuid) {
|
if (userInfo && parsedData && !data.uuid) {
|
||||||
try {
|
try {
|
||||||
|
console.log('📤 درخواست مجدد دریافت پروفایل (step 3) - UUID:', parsedData.uuid);
|
||||||
const res = await request.getUserProfile(parsedData.uuid);
|
const res = await request.getUserProfile(parsedData.uuid);
|
||||||
|
|
||||||
if (res) {
|
if (res) {
|
||||||
@@ -117,6 +122,7 @@ function AppointmentPage({ doctor, disabledDates, matchedCity }) {
|
|||||||
name: { value: res.name || "", isEdit: true },
|
name: { value: res.name || "", isEdit: true },
|
||||||
family: { value: res.family || "", isEdit: true },
|
family: { value: res.family || "", isEdit: true },
|
||||||
gender: { value: res.gender || "", isEdit: true },
|
gender: { value: res.gender || "", isEdit: true },
|
||||||
|
insurance_id: { value: res.insurance_id || "", isEdit: true },
|
||||||
basic_insurance: {
|
basic_insurance: {
|
||||||
value: res.basic_insurance,
|
value: res.basic_insurance,
|
||||||
isEdit: true,
|
isEdit: true,
|
||||||
@@ -134,6 +140,7 @@ function AppointmentPage({ doctor, disabledDates, matchedCity }) {
|
|||||||
name: { value: "", isEdit: true },
|
name: { value: "", isEdit: true },
|
||||||
family: { value: "", isEdit: true },
|
family: { value: "", isEdit: true },
|
||||||
gender: { value: "", isEdit: true },
|
gender: { value: "", isEdit: true },
|
||||||
|
insurance_id: { value: "", isEdit: true },
|
||||||
basic_insurance: { value: "", isEdit: true },
|
basic_insurance: { value: "", isEdit: true },
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user