From 3268b3c51e87c42f2954c08e2cfaa541910bf089 Mon Sep 17 00:00:00 2001
From: hamed <15238-genius.ha@users.noreply.drupalcode.org>
Date: Tue, 18 Nov 2025 09:16:01 +0330
Subject: [PATCH] refactor: enhance form validation and error handling in
EditField, DefaultSelect, and Form components
---
app/component/fields/EditField.js | 66 +++++++++++++++++----
app/component/selectors/DefaultSelect.js | 6 ++
components/appointment/detail/Form.js | 15 +++--
components/appointment/detail/SubmitData.js | 54 +++++++++++------
components/appointment/detail/index.js | 5 +-
components/appointment/index.js | 7 +++
6 files changed, 117 insertions(+), 36 deletions(-)
diff --git a/app/component/fields/EditField.js b/app/component/fields/EditField.js
index 9fb17f5..8117689 100644
--- a/app/component/fields/EditField.js
+++ b/app/component/fields/EditField.js
@@ -2,6 +2,21 @@ import EditGrayA from "@/components/icons/EditGrayA";
import EditOrangeA from "@/components/icons/EditOrangeA";
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({
changeData,
multiline,
@@ -14,7 +29,31 @@ function EditField({
noDisable,
type,
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 (
{
- changeData(e.target.value, "value", name);
- }}
+ error={!!error}
+ helperText={error || ""}
+ className={`!w-full res-field-account dark:!bg-transparent ${isTransparent ? "!bg-transparent lg:!bg-[#FFF]" : "!bg-[#FFF]"}
+ }`}
+ onChange={handleChange}
sx={{
"& .MuiFormLabel-root": {
top: "-4px !important",
@@ -43,6 +81,10 @@ function EditField({
padding: "0 !important",
borderRadius: "8px !important",
},
+ "& .MuiFormHelperText-root": {
+ marginLeft: "0",
+ marginRight: "14px",
+ },
".Mui-focused": {
"& .MuiOutlinedInput-notchedOutline": {
border: "1px solid #5559CE !important",
@@ -50,17 +92,17 @@ function EditField({
},
},
"& input::-webkit-outer-spin-button, & input::-webkit-inner-spin-button":
- {
- display: "none",
- },
+ {
+ display: "none",
+ },
}}
/>
diff --git a/app/component/selectors/DefaultSelect.js b/app/component/selectors/DefaultSelect.js
index 54f2245..99cf046 100644
--- a/app/component/selectors/DefaultSelect.js
+++ b/app/component/selectors/DefaultSelect.js
@@ -74,8 +74,14 @@ function DefaultSelect({ list, value, updateData, label, error, name }) {
),
}}
placeholder={label}
+ error={!!error}
+ helperText={error || ""}
sx={{
borderRadius: "8px",
+ "& .MuiFormHelperText-root": {
+ marginLeft: "0",
+ marginRight: "14px",
+ },
}}
/>
)}
diff --git a/components/appointment/detail/Form.js b/components/appointment/detail/Form.js
index f8ec331..748a17a 100644
--- a/components/appointment/detail/Form.js
+++ b/components/appointment/detail/Form.js
@@ -2,7 +2,7 @@ import Content from "./Content";
import EditField from "../../../app/component/fields/EditField";
import DefaultSelect from "@/app/component/selectors/DefaultSelect";
-function Form({ changeData, insurance, supplementaryInsurance, data, isForAnother }) {
+function Form({ changeData, insurance, supplementaryInsurance, data, isForAnother, errors = {} }) {
const findBasicInsuranceVal = () => {
return insurance?.find(
(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"
>
-
+
-
+
-
+
+
+
+
changeData(val, "value", name)}
@@ -63,6 +68,7 @@ function Form({ changeData, insurance, supplementaryInsurance, data, isForAnothe
name="basic_insurance"
list={insurance}
value={findBasicInsuranceVal()}
+ error={errors?.basic_insurance}
/>
@@ -72,6 +78,7 @@ function Form({ changeData, insurance, supplementaryInsurance, data, isForAnothe
name="supplementary_insurance"
list={supplementaryInsurance}
value={findSupplementaryInsuranceVal()}
+ error={errors?.supplementary_insurance}
/>
diff --git a/components/appointment/detail/SubmitData.js b/components/appointment/detail/SubmitData.js
index a4346ea..a8cd554 100644
--- a/components/appointment/detail/SubmitData.js
+++ b/components/appointment/detail/SubmitData.js
@@ -4,35 +4,51 @@ import { Button } from "@mui/material";
import ArrowLeftB from "@/components/icons/ArrowLeftB";
import { request } from "@/services/response";
-function SubmitData({ setStep, data, prevData }) {
+function SubmitData({ setStep, data, prevData, setErrors }) {
const [loading, setLoading] = useState(false);
const newStep = () => setStep((prev) => prev + 1);
- const handleSubmit = () => {
+ const handleSubmit = async () => {
const isChanged = JSON.stringify(prevData) !== JSON.stringify(data);
+ // پاک کردن error های قبلی
+ setErrors({});
+
if (isChanged) {
setLoading(true);
- let newData = data;
- const { basic_insurance, name, national_code, uuid } = newData;
+ const { basic_insurance, supplementary_insurance, name, family, national_code, gender, insurance_id, uuid } = data;
- newData = {
- basic_insurance: [basic_insurance.value?.id],
- ...(prevData.name?.value === data.name.value ? {} : { name: name }),
- ...(prevData.national_code?.value
- ? {}
- : { national_code: national_code }),
+ const payload = {
+ name: name?.value,
+ family: family?.value,
+ national_code: national_code?.value,
+ gender: gender?.value,
+ 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
- .patchUserProfile(newData, uuid)
- .then(() => {
- setLoading(false);
- newStep();
- })
- .catch(() => {
- setLoading(false);
- });
+ try {
+ if (uuid) {
+ // اگر uuid داریم، پروفایل وجود داره و باید PATCH کنیم
+ console.log('📤 ارسال درخواست PATCH - UUID:', uuid, 'Payload:', payload);
+ await request.patchUserProfile(payload, uuid);
+ } else {
+ // اگر uuid نداریم، پروفایل وجود نداره و باید POST کنیم
+ 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 {
newStep();
}
diff --git a/components/appointment/detail/index.js b/components/appointment/detail/index.js
index 9751fad..620356f 100644
--- a/components/appointment/detail/index.js
+++ b/components/appointment/detail/index.js
@@ -18,6 +18,8 @@ function Detail({
}) {
const [insurance, setInsurance] = useState();
const [supplementaryInsurance, setSupplementaryInsurance] = useState();
+ const [errors, setErrors] = useState({});
+
const changeData = (value, type, name) =>
setData({
...data,
@@ -68,6 +70,7 @@ function Detail({
supplementaryInsurance={supplementaryInsurance}
changeData={changeData}
isForAnother={isForAnother}
+ errors={errors}
/>
{!isForAnother && (
)}
-
+
);
diff --git a/components/appointment/index.js b/components/appointment/index.js
index 0dc5df0..4d9ae90 100644
--- a/components/appointment/index.js
+++ b/components/appointment/index.js
@@ -11,6 +11,7 @@ const defaultData = {
name: { value: "", isEdit: true },
family: { value: "", isEdit: true },
gender: { value: "", isEdit: true },
+ insurance_id: { value: "", isEdit: true },
basic_insurance: { value: "", isEdit: true },
};
@@ -50,6 +51,7 @@ function AppointmentPage({ doctor, disabledDates, matchedCity }) {
// سپس اگر uuid داریم، بقیه اطلاعات را از API میگیریم
if (userInfo && parsedData) {
try {
+ console.log('📤 درخواست دریافت پروفایل کاربر - UUID:', parsedData.uuid);
const res = await request.getUserProfile(parsedData.uuid);
if (res) {
@@ -63,6 +65,7 @@ function AppointmentPage({ doctor, disabledDates, matchedCity }) {
name: { value: res.name || "", isEdit: true },
family: { value: res.family || "", isEdit: true },
gender: { value: res.gender || "", isEdit: true },
+ insurance_id: { value: res.insurance_id || "", isEdit: true },
basic_insurance: {
value: res.basic_insurance,
isEdit: true,
@@ -80,6 +83,7 @@ function AppointmentPage({ doctor, disabledDates, matchedCity }) {
name: { value: "", isEdit: true },
family: { value: "", isEdit: true },
gender: { value: "", isEdit: true },
+ insurance_id: { value: "", isEdit: true },
basic_insurance: { value: "", isEdit: true },
}));
}
@@ -104,6 +108,7 @@ function AppointmentPage({ doctor, disabledDates, matchedCity }) {
if (userInfo && parsedData && !data.uuid) {
try {
+ console.log('📤 درخواست مجدد دریافت پروفایل (step 3) - UUID:', parsedData.uuid);
const res = await request.getUserProfile(parsedData.uuid);
if (res) {
@@ -117,6 +122,7 @@ function AppointmentPage({ doctor, disabledDates, matchedCity }) {
name: { value: res.name || "", isEdit: true },
family: { value: res.family || "", isEdit: true },
gender: { value: res.gender || "", isEdit: true },
+ insurance_id: { value: res.insurance_id || "", isEdit: true },
basic_insurance: {
value: res.basic_insurance,
isEdit: true,
@@ -134,6 +140,7 @@ function AppointmentPage({ doctor, disabledDates, matchedCity }) {
name: { value: "", isEdit: true },
family: { value: "", isEdit: true },
gender: { value: "", isEdit: true },
+ insurance_id: { value: "", isEdit: true },
basic_insurance: { value: "", isEdit: true },
}));
}