change some files
This commit is contained in:
326
ui/forms/personal/InnerPersonalInfoForm.tsx
Normal file
326
ui/forms/personal/InnerPersonalInfoForm.tsx
Normal file
@@ -0,0 +1,326 @@
|
||||
// InnerPersonalInfoForm.tsx
|
||||
"use client";
|
||||
|
||||
import React from "react";
|
||||
import { Box, Button, MenuItem, TextField } from "@mui/material";
|
||||
import { Form, type FormikProps } from "formik";
|
||||
import { MilitaryStatus, PersonalInfoFormValues } from "./types";
|
||||
import {
|
||||
EDUCATION_OPTIONS,
|
||||
HOUSING_OPTIONS,
|
||||
MILITARY_OPTIONS,
|
||||
} from "./constants";
|
||||
import { PersonalInfoFormProps } from "./PersonalInfoForm";
|
||||
import { handleBack } from "@/core/utils";
|
||||
|
||||
type Props = FormikProps<PersonalInfoFormValues> & PersonalInfoFormProps;
|
||||
|
||||
export default function InnerPersonalInfoForm(props: Props) {
|
||||
const { values, errors, touched, setFieldValue, handleChange } = props;
|
||||
|
||||
const showSpouseFields = values.maritalStatus === "متاهل";
|
||||
const showChildrenCount = ["متاهل", "متارکه", "فوت همسر"].includes(
|
||||
values.maritalStatus,
|
||||
);
|
||||
const isPermanentExempt = values.militaryStatus === "معافیت دائم";
|
||||
|
||||
const handleMaritalStatusChange = (
|
||||
e: React.ChangeEvent<HTMLInputElement>,
|
||||
) => {
|
||||
const status = e.target.value;
|
||||
|
||||
setFieldValue("maritalStatus", status);
|
||||
|
||||
// پاکسازی شرطیها
|
||||
const isMarried = status === "متاهل";
|
||||
const hasChildren = ["متاهل", "متارکه", "فوت همسر"].includes(status);
|
||||
|
||||
if (!isMarried) {
|
||||
setFieldValue("spouseName", "");
|
||||
setFieldValue("spouseEducation", "");
|
||||
setFieldValue("spouseJob", "");
|
||||
setFieldValue("spouseWorkplace", "");
|
||||
}
|
||||
if (!hasChildren) {
|
||||
setFieldValue("childrenCount", "");
|
||||
}
|
||||
};
|
||||
|
||||
const handleMilitaryStatusChange = (
|
||||
e: React.ChangeEvent<HTMLInputElement>,
|
||||
) => {
|
||||
const ms = e.target.value as MilitaryStatus;
|
||||
setFieldValue("militaryStatus", ms);
|
||||
|
||||
if (ms !== "معافیت دائم") {
|
||||
setFieldValue("permanentExemptionReason", "");
|
||||
}
|
||||
};
|
||||
|
||||
const tf = <K extends keyof PersonalInfoFormValues>(name: K) => ({
|
||||
name: String(name),
|
||||
value: values[name] as any,
|
||||
onChange: handleChange,
|
||||
fullWidth: true,
|
||||
error: !!(touched as any)[name] && !!(errors as any)[name],
|
||||
helperText: (touched as any)[name] ? ((errors as any)[name] as string) : "",
|
||||
});
|
||||
|
||||
return (
|
||||
<Form>
|
||||
<Box
|
||||
sx={{
|
||||
display: "grid",
|
||||
gridTemplateColumns: { xs: "1fr", md: "repeat(2, 1fr)" },
|
||||
gap: 2,
|
||||
}}
|
||||
>
|
||||
{/* وضعیت تاهل */}
|
||||
<TextField
|
||||
select
|
||||
label="وضعیت تاهل"
|
||||
name="maritalStatus"
|
||||
value={values.maritalStatus}
|
||||
onChange={handleMaritalStatusChange}
|
||||
fullWidth
|
||||
error={!!touched.maritalStatus && !!errors.maritalStatus}
|
||||
helperText={
|
||||
touched.maritalStatus ? (errors.maritalStatus as string) : ""
|
||||
}
|
||||
>
|
||||
<MenuItem value="">انتخاب کنید</MenuItem>
|
||||
<MenuItem value="مجرد">مجرد</MenuItem>
|
||||
<MenuItem value="متاهل">متاهل</MenuItem>
|
||||
<MenuItem value="متارکه">متارکه</MenuItem>
|
||||
<MenuItem value="فوت همسر">فوت همسر</MenuItem>
|
||||
</TextField>
|
||||
{/* همسر (شرطی) */}
|
||||
{showSpouseFields && (
|
||||
<>
|
||||
<TextField label="نام و نام خانوادگی همسر" {...tf("spouseName")} />
|
||||
<TextField label="تحصیلات همسر" {...tf("spouseEducation")} />
|
||||
<TextField label="شغل همسر" {...tf("spouseJob")} />
|
||||
<TextField label="محل کار همسر" {...tf("spouseWorkplace")} />
|
||||
</>
|
||||
)}
|
||||
{/* تعداد فرزند (شرطی) */}
|
||||
{showChildrenCount && (
|
||||
<TextField
|
||||
label="تعداد فرزند"
|
||||
name="childrenCount"
|
||||
type="number"
|
||||
value={values.childrenCount}
|
||||
onChange={(e) =>
|
||||
setFieldValue(
|
||||
"childrenCount",
|
||||
e.target.value === "" ? "" : Number(e.target.value),
|
||||
)
|
||||
}
|
||||
fullWidth
|
||||
error={!!touched.childrenCount && !!errors.childrenCount}
|
||||
helperText={
|
||||
touched.childrenCount ? (errors.childrenCount as string) : ""
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{/* وضعیت نظام وظیفه */}
|
||||
<TextField
|
||||
select
|
||||
label="وضعیت نظام وظیفه"
|
||||
name="militaryStatus"
|
||||
value={values.militaryStatus}
|
||||
onChange={handleMilitaryStatusChange}
|
||||
fullWidth
|
||||
error={!!touched.militaryStatus && !!errors.militaryStatus}
|
||||
helperText={
|
||||
touched.militaryStatus ? (errors.militaryStatus as string) : ""
|
||||
}
|
||||
>
|
||||
<MenuItem value="">انتخاب کنید</MenuItem>
|
||||
{MILITARY_OPTIONS.map((opt) => (
|
||||
<MenuItem key={opt} value={opt}>
|
||||
{opt}
|
||||
</MenuItem>
|
||||
))}
|
||||
</TextField>
|
||||
{/* علت معافیت دائم */}
|
||||
{isPermanentExempt && (
|
||||
<TextField
|
||||
label="علت معافیت دائم"
|
||||
{...tf("permanentExemptionReason")}
|
||||
/>
|
||||
)}
|
||||
{/* تحصیلات پدر/مادر */}
|
||||
<TextField
|
||||
select
|
||||
label="تحصیلات پدر"
|
||||
name="fatherEducation"
|
||||
value={values.fatherEducation}
|
||||
onChange={handleChange}
|
||||
fullWidth
|
||||
error={!!touched.fatherEducation && !!errors.fatherEducation}
|
||||
helperText={
|
||||
touched.fatherEducation ? (errors.fatherEducation as string) : ""
|
||||
}
|
||||
>
|
||||
<MenuItem value="">انتخاب کنید</MenuItem>
|
||||
{EDUCATION_OPTIONS.map((opt) => (
|
||||
<MenuItem key={opt} value={opt}>
|
||||
{opt}
|
||||
</MenuItem>
|
||||
))}
|
||||
</TextField>
|
||||
<TextField label="شغل پدر" {...tf("fatherJob")} />
|
||||
<TextField
|
||||
select
|
||||
label="تحصیلات مادر"
|
||||
name="motherEducation"
|
||||
value={values.motherEducation}
|
||||
onChange={handleChange}
|
||||
fullWidth
|
||||
error={!!touched.motherEducation && !!errors.motherEducation}
|
||||
helperText={
|
||||
touched.motherEducation ? (errors.motherEducation as string) : ""
|
||||
}
|
||||
>
|
||||
<MenuItem value="">انتخاب کنید</MenuItem>
|
||||
{EDUCATION_OPTIONS.map((opt) => (
|
||||
<MenuItem key={opt} value={opt}>
|
||||
{opt}
|
||||
</MenuItem>
|
||||
))}
|
||||
</TextField>
|
||||
<TextField label="شغل مادر" {...tf("motherJob")} />
|
||||
{/* وضعیت مسکن / شهر / آدرس */}
|
||||
<TextField
|
||||
select
|
||||
label="وضعیت مسکن"
|
||||
name="housingStatus"
|
||||
value={values.housingStatus}
|
||||
onChange={handleChange}
|
||||
fullWidth
|
||||
error={!!touched.housingStatus && !!errors.housingStatus}
|
||||
helperText={
|
||||
touched.housingStatus ? (errors.housingStatus as string) : ""
|
||||
}
|
||||
>
|
||||
<MenuItem value="">انتخاب کنید</MenuItem>
|
||||
{HOUSING_OPTIONS.map((opt) => (
|
||||
<MenuItem key={opt} value={opt}>
|
||||
{opt}
|
||||
</MenuItem>
|
||||
))}
|
||||
</TextField>{" "}
|
||||
<TextField label="شهر" {...tf("city")} />
|
||||
<Box sx={{ gridColumn: { md: "span 2" } }}>
|
||||
<TextField label="آدرس" {...tf("address")} multiline minRows={2} />
|
||||
</Box>
|
||||
{/* تلفنها */}
|
||||
<TextField label="تلفن منزل" {...tf("homePhone")} />
|
||||
<TextField label="تلفن همراه" {...tf("mobilePhone")} />
|
||||
<TextField label="تلفن ضروری" {...tf("emergencyPhone")} />
|
||||
<TextField label="ایمیل" {...tf("email")} />
|
||||
{/* مدت سکونت */}
|
||||
<TextField
|
||||
label="مدت سکونت (سال)"
|
||||
name="residenceDuration"
|
||||
type="number"
|
||||
value={values.residenceDuration}
|
||||
onChange={(e) =>
|
||||
setFieldValue(
|
||||
"residenceDuration",
|
||||
e.target.value === "" ? "" : Number(e.target.value),
|
||||
)
|
||||
}
|
||||
fullWidth
|
||||
error={!!touched.residenceDuration && !!errors.residenceDuration}
|
||||
helperText={
|
||||
touched.residenceDuration
|
||||
? (errors.residenceDuration as string)
|
||||
: ""
|
||||
}
|
||||
/>
|
||||
{/* ایثارگر */}
|
||||
<TextField
|
||||
select
|
||||
label="ایثارگر"
|
||||
name="isVeteran"
|
||||
value={String(values.isVeteran)}
|
||||
onChange={(e) =>
|
||||
setFieldValue("isVeteran", e.target.value === "true")
|
||||
}
|
||||
fullWidth
|
||||
error={!!touched.isVeteran && !!errors.isVeteran}
|
||||
helperText={touched.isVeteran ? (errors.isVeteran as string) : ""}
|
||||
>
|
||||
<MenuItem value="false">خیر</MenuItem>
|
||||
<MenuItem value="true">بله</MenuItem>
|
||||
</TextField>
|
||||
{/* سوءپیشینه */}
|
||||
<TextField
|
||||
select
|
||||
label="سابقه کیفری"
|
||||
name="hasCriminalRecord"
|
||||
value={String(values.hasCriminalRecord)}
|
||||
onChange={(e) => {
|
||||
const next = e.target.value === "true";
|
||||
setFieldValue("hasCriminalRecord", next);
|
||||
if (!next) setFieldValue("criminalDescription", "");
|
||||
}}
|
||||
fullWidth
|
||||
error={!!touched.hasCriminalRecord && !!errors.hasCriminalRecord}
|
||||
helperText={
|
||||
touched.hasCriminalRecord
|
||||
? (errors.hasCriminalRecord as string)
|
||||
: ""
|
||||
}
|
||||
>
|
||||
<MenuItem value="false">خیر</MenuItem>
|
||||
<MenuItem value="true">بله</MenuItem>
|
||||
</TextField>
|
||||
{values.hasCriminalRecord && (
|
||||
<TextField
|
||||
label="توضیحات سوء پیشینه"
|
||||
{...tf("criminalDescription")}
|
||||
/>
|
||||
)}
|
||||
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
mt: 5,
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
disabled={props.step === 1}
|
||||
type="button"
|
||||
onClick={() => handleBack(props, "personalInfo")}
|
||||
sx={{
|
||||
borderRadius: "12px",
|
||||
color: "#64748b",
|
||||
fontWeight: 700,
|
||||
}}
|
||||
>
|
||||
بازگشت
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
type="submit"
|
||||
sx={{
|
||||
borderRadius: "12px",
|
||||
px: 4,
|
||||
py: 1.5,
|
||||
bgcolor: `${props.step === 12 ? "green" : "#2563eb"}`,
|
||||
fontWeight: 700,
|
||||
}}
|
||||
>
|
||||
{props.step === 12 ? "اتمام و ثبت نهايي" : "گام بعدی"}
|
||||
</Button>
|
||||
</Box>
|
||||
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
49
ui/forms/personal/PersonalInfoForm.tsx
Normal file
49
ui/forms/personal/PersonalInfoForm.tsx
Normal file
@@ -0,0 +1,49 @@
|
||||
// PersonalInfoForm.tsx
|
||||
"use client";
|
||||
|
||||
import React from "react";
|
||||
import { withFormik, type FormikBag } from "formik";
|
||||
|
||||
import InnerPersonalInfoForm from "./InnerPersonalInfoForm";
|
||||
import { PersonalInfoFormValues } from "./types";
|
||||
import { PERSONAL_INFO_EMPTY_VALUES } from "./constants";
|
||||
import { PersonalInfoValidationSchema } from "./validation/PersonalInfoFormValidation";
|
||||
|
||||
|
||||
/** اینا رو با Wizard خودت هماهنگ کن */
|
||||
export interface WizardFormData {
|
||||
personalInfo: PersonalInfoFormValues;
|
||||
// ... بقیه step ها
|
||||
}
|
||||
|
||||
export type PersonalInfoFormProps = {
|
||||
step: number;
|
||||
setStep: React.Dispatch<React.SetStateAction<number>>;
|
||||
data: WizardFormData;
|
||||
update: (patch: Partial<WizardFormData>) => void;
|
||||
};
|
||||
|
||||
const PersonalInfoForm = withFormik<PersonalInfoFormProps, PersonalInfoFormValues>({
|
||||
displayName: "PersonalInfoForm",
|
||||
|
||||
enableReinitialize: true,
|
||||
|
||||
mapPropsToValues: (props) => {
|
||||
return props.data?.personalInfo ?? PERSONAL_INFO_EMPTY_VALUES;
|
||||
},
|
||||
|
||||
// validationSchema: PersonalInfoValidationSchema,
|
||||
|
||||
handleSubmit: async (values, bag: FormikBag<PersonalInfoFormProps, PersonalInfoFormValues>) => {
|
||||
const { props, setSubmitting } = bag;
|
||||
|
||||
props.update({ personalInfo: values });
|
||||
|
||||
// برو مرحله بعد
|
||||
props.setStep((prev) => prev + 1);
|
||||
|
||||
setSubmitting(false);
|
||||
},
|
||||
})(InnerPersonalInfoForm);
|
||||
|
||||
export default PersonalInfoForm;
|
||||
61
ui/forms/personal/constants/index.ts
Normal file
61
ui/forms/personal/constants/index.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import {
|
||||
EducationLevel,
|
||||
HousingStatus,
|
||||
MilitaryStatus,
|
||||
PersonalInfoFormValues,
|
||||
} from "../types";
|
||||
|
||||
export const PERSONAL_INFO_EMPTY_VALUES: PersonalInfoFormValues = {
|
||||
maritalStatus: "",
|
||||
|
||||
militaryStatus: "",
|
||||
permanentExemptionReason: "",
|
||||
|
||||
fatherEducation: "",
|
||||
fatherJob: "",
|
||||
motherEducation: "",
|
||||
motherJob: "",
|
||||
|
||||
housingStatus: "",
|
||||
city: "",
|
||||
address: "",
|
||||
homePhone: "",
|
||||
mobilePhone: "",
|
||||
emergencyPhone: "",
|
||||
email: "",
|
||||
residenceDuration: "",
|
||||
isVeteran: false,
|
||||
|
||||
hasCriminalRecord: false,
|
||||
criminalDescription: "",
|
||||
|
||||
spouseName: "",
|
||||
spouseEducation: "",
|
||||
spouseJob: "",
|
||||
spouseWorkplace: "",
|
||||
childrenCount: "",
|
||||
};
|
||||
|
||||
export const MILITARY_OPTIONS: Exclude<MilitaryStatus, "">[] = [
|
||||
"کارت پایان خدمت",
|
||||
"در حال خدمت",
|
||||
"معافیت تحصیلی",
|
||||
"معافیت دائم",
|
||||
"انجام نشده",
|
||||
];
|
||||
|
||||
export const EDUCATION_OPTIONS: Exclude<EducationLevel, "">[] = [
|
||||
"زیر دیپلم",
|
||||
"دیپلم",
|
||||
"دانشجو",
|
||||
"کاردانی",
|
||||
"کارشناسی",
|
||||
"کارشناسی ارشد",
|
||||
"دکترا",
|
||||
];
|
||||
export const HOUSING_OPTIONS: Exclude<HousingStatus, "">[] = [
|
||||
"منزل شخصی",
|
||||
"منزل والدین",
|
||||
"منزل استیجاری",
|
||||
"سایر",
|
||||
];
|
||||
57
ui/forms/personal/types/index.ts
Normal file
57
ui/forms/personal/types/index.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
// personal-info.types.ts
|
||||
|
||||
export type MilitaryStatus =
|
||||
| ""
|
||||
| "کارت پایان خدمت"
|
||||
| "در حال خدمت"
|
||||
| "معافیت تحصیلی"
|
||||
| "معافیت دائم"
|
||||
| "انجام نشده";
|
||||
|
||||
export type EducationLevel =
|
||||
| ""
|
||||
| "زیر دیپلم"
|
||||
| "دیپلم"
|
||||
| "دانشجو"
|
||||
| "کاردانی"
|
||||
| "کارشناسی"
|
||||
| "کارشناسی ارشد"
|
||||
| "دکترا";
|
||||
|
||||
export type HousingStatus =
|
||||
| ""
|
||||
| "منزل شخصی"
|
||||
| "منزل والدین"
|
||||
| "منزل استیجاری"
|
||||
| "سایر";
|
||||
|
||||
export interface PersonalInfoFormValues {
|
||||
maritalStatus: string;
|
||||
|
||||
militaryStatus: MilitaryStatus;
|
||||
permanentExemptionReason: string;
|
||||
|
||||
fatherEducation: EducationLevel;
|
||||
fatherJob: string;
|
||||
motherEducation: EducationLevel;
|
||||
motherJob: string;
|
||||
|
||||
housingStatus: HousingStatus;
|
||||
city: string;
|
||||
address: string;
|
||||
homePhone: string;
|
||||
mobilePhone: string;
|
||||
emergencyPhone: string;
|
||||
email: string;
|
||||
residenceDuration: number | "";
|
||||
isVeteran: boolean;
|
||||
|
||||
hasCriminalRecord: boolean;
|
||||
criminalDescription: string;
|
||||
|
||||
spouseName: string;
|
||||
spouseEducation: string;
|
||||
spouseJob: string;
|
||||
spouseWorkplace: string;
|
||||
childrenCount: number | "";
|
||||
}
|
||||
112
ui/forms/personal/validation/PersonalInfoFormValidation.tsx
Normal file
112
ui/forms/personal/validation/PersonalInfoFormValidation.tsx
Normal file
@@ -0,0 +1,112 @@
|
||||
// PersonalInfoForm.validation.ts
|
||||
import * as yup from "yup";
|
||||
import { PersonalInfoFormValues } from "../types";
|
||||
|
||||
export const PersonalInfoValidationSchema = yup
|
||||
.object({
|
||||
maritalStatus: yup.string().trim().required("وضعیت تاهل را انتخاب کنید"),
|
||||
|
||||
militaryStatus: yup
|
||||
.mixed<PersonalInfoFormValues["militaryStatus"]>()
|
||||
.oneOf([
|
||||
"",
|
||||
"کارت پایان خدمت",
|
||||
"در حال خدمت",
|
||||
"معافیت تحصیلی",
|
||||
"معافیت دائم",
|
||||
"انجام نشده",
|
||||
])
|
||||
.required("وضعیت نظام وظیفه را انتخاب کنید"),
|
||||
|
||||
permanentExemptionReason: yup
|
||||
.string()
|
||||
.trim()
|
||||
.when("militaryStatus", {
|
||||
is: "معافیت دائم",
|
||||
then: (s) => s.required("علت معافیت دائم الزامی است"),
|
||||
otherwise: (s) => s.notRequired(),
|
||||
}),
|
||||
|
||||
fatherEducation: yup.string().required("تحصیلات پدر را انتخاب کنید"),
|
||||
fatherJob: yup.string().trim().required("شغل پدر الزامی است"),
|
||||
motherEducation: yup.string().required("تحصیلات مادر را انتخاب کنید"),
|
||||
motherJob: yup.string().trim().required("شغل مادر الزامی است"),
|
||||
|
||||
housingStatus: yup
|
||||
.mixed<PersonalInfoFormValues["housingStatus"]>()
|
||||
.oneOf(["", "منزل شخصی", "منزل والدین", "منزل استیجاری", "سایر"])
|
||||
.required("وضعیت مسکن را انتخاب کنید"),
|
||||
city: yup.string().trim().required("شهر الزامی است"),
|
||||
address: yup.string().trim().required("آدرس الزامی است"),
|
||||
|
||||
homePhone: yup.string().trim().notRequired(),
|
||||
mobilePhone: yup.string().trim().required("تلفن همراه الزامی است"),
|
||||
emergencyPhone: yup.string().trim().notRequired(),
|
||||
email: yup.string().trim().email("ایمیل نامعتبر است").notRequired(),
|
||||
|
||||
residenceDuration: yup
|
||||
.mixed<number | "">()
|
||||
.test(
|
||||
"residenceDuration",
|
||||
"مدت سکونت نامعتبر است",
|
||||
(v) => v === "" || (typeof v === "number" && v >= 0),
|
||||
)
|
||||
.notRequired(),
|
||||
|
||||
isVeteran: yup.boolean().required(),
|
||||
|
||||
hasCriminalRecord: yup.boolean().required(),
|
||||
criminalDescription: yup
|
||||
.string()
|
||||
.trim()
|
||||
.when("hasCriminalRecord", {
|
||||
is: true,
|
||||
then: (s) => s.required("توضیحات سوء پیشینه الزامی است"),
|
||||
otherwise: (s) => s.notRequired(),
|
||||
}),
|
||||
|
||||
spouseName: yup
|
||||
.string()
|
||||
.trim()
|
||||
.when("maritalStatus", {
|
||||
is: "متاهل",
|
||||
then: (s) => s.required("نام همسر الزامی است"),
|
||||
otherwise: (s) => s.notRequired(),
|
||||
}),
|
||||
spouseEducation: yup
|
||||
.string()
|
||||
.trim()
|
||||
.when("maritalStatus", {
|
||||
is: "متاهل",
|
||||
then: (s) => s.required("تحصیلات همسر الزامی است"),
|
||||
otherwise: (s) => s.notRequired(),
|
||||
}),
|
||||
spouseJob: yup
|
||||
.string()
|
||||
.trim()
|
||||
.when("maritalStatus", {
|
||||
is: "متاهل",
|
||||
then: (s) => s.required("شغل همسر الزامی است"),
|
||||
otherwise: (s) => s.notRequired(),
|
||||
}),
|
||||
spouseWorkplace: yup
|
||||
.string()
|
||||
.trim()
|
||||
.when("maritalStatus", {
|
||||
is: "متاهل",
|
||||
then: (s) => s.required("محل کار همسر الزامی است"),
|
||||
otherwise: (s) => s.notRequired(),
|
||||
}),
|
||||
|
||||
childrenCount: yup.mixed<number | "">().when("maritalStatus", {
|
||||
is: (ms: string) => ["متاهل", "متارکه", "فوت همسر"].includes(ms),
|
||||
then: (s) =>
|
||||
s.test(
|
||||
"childrenCount",
|
||||
"تعداد فرزند نامعتبر است",
|
||||
(v) => v === "" || (typeof v === "number" && v >= 0),
|
||||
),
|
||||
otherwise: (s) => s.notRequired(),
|
||||
}),
|
||||
})
|
||||
.required();
|
||||
Reference in New Issue
Block a user