dasdasd
This commit is contained in:
82
ui/forms/identity/IdentityForm.tsx
Normal file
82
ui/forms/identity/IdentityForm.tsx
Normal file
@@ -0,0 +1,82 @@
|
||||
import { withFormik } from "formik";
|
||||
import InnerIdentityForm from "./InnerIdentityForm";
|
||||
import * as yup from "yup";
|
||||
|
||||
export interface IdentityFormValues {
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
birthDate: string;
|
||||
birthPlace: string;
|
||||
fatherName: string;
|
||||
gender: string;
|
||||
nationalCode: string;
|
||||
nationality: string;
|
||||
profilePhotoId: string;
|
||||
religion: string;
|
||||
}
|
||||
|
||||
export interface WizardFormData {
|
||||
identity: IdentityFormValues;
|
||||
}
|
||||
|
||||
export interface IdentityFormProps {
|
||||
step: number;
|
||||
setStep: React.Dispatch<React.SetStateAction<number>>;
|
||||
data: WizardFormData;
|
||||
update: (newData: Partial<WizardFormData>) => void;
|
||||
}
|
||||
|
||||
const IdentityFormValidationSchema = yup.object({
|
||||
firstName: yup.string().trim().required("نام الزامی است").min(2).max(50),
|
||||
lastName: yup.string().trim().required("نام خانوادگی الزامی است").min(2).max(50),
|
||||
birthDate: yup
|
||||
.string()
|
||||
.required("تاریخ تولد الزامی است")
|
||||
.matches(/^\d{4}\/\d{2}\/\d{2}$/, "فرمت تاریخ تولد باید به شکل ۱۴۰۳/۰۱/۲۰ باشد"),
|
||||
birthPlace: yup.string().trim().required("محل تولد الزامی است").min(2).max(80),
|
||||
fatherName: yup.string().trim().required("نام پدر الزامی است").min(2).max(50),
|
||||
gender: yup
|
||||
.string()
|
||||
.required("جنسیت الزامی است")
|
||||
.oneOf(["male", "female", "other"], "جنسیت معتبر نیست"),
|
||||
nationalCode: yup
|
||||
.string()
|
||||
.required("کد ملی الزامی است")
|
||||
.matches(/^\d{10}$/, "کد ملی باید ۱۰ رقم باشد"),
|
||||
nationality: yup.string().trim().required("تابعیت الزامی است").min(2).max(50),
|
||||
profilePhotoId: yup.string().trim().required("عکس پرسنلی الزامی است"),
|
||||
religion: yup.string().trim().required("دین الزامی است").min(2).max(50),
|
||||
});
|
||||
|
||||
const EMPTY_IDENTITY_VALUES: IdentityFormValues = {
|
||||
firstName: "",
|
||||
lastName: "",
|
||||
birthDate: "",
|
||||
birthPlace: "",
|
||||
fatherName: "",
|
||||
gender: "",
|
||||
nationalCode: "",
|
||||
nationality: "",
|
||||
profilePhotoId: "",
|
||||
religion: "",
|
||||
};
|
||||
|
||||
const IdentityForm = withFormik<IdentityFormProps, IdentityFormValues>({
|
||||
enableReinitialize: true,
|
||||
|
||||
mapPropsToValues: (props) => {
|
||||
return props.data?.identity || EMPTY_IDENTITY_VALUES;
|
||||
},
|
||||
|
||||
validationSchema: IdentityFormValidationSchema,
|
||||
|
||||
handleSubmit: (values, { props }) => {
|
||||
props.update({
|
||||
identity: values,
|
||||
});
|
||||
|
||||
props.setStep((prev) => prev + 1);
|
||||
},
|
||||
})(InnerIdentityForm);
|
||||
|
||||
export default IdentityForm;
|
||||
310
ui/forms/identity/InnerIdentityForm.tsx
Normal file
310
ui/forms/identity/InnerIdentityForm.tsx
Normal file
@@ -0,0 +1,310 @@
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
MenuItem,
|
||||
Paper,
|
||||
TextField,
|
||||
Typography,
|
||||
} from "@mui/material";
|
||||
import { ErrorMessage, Form, FormikProps } from "formik";
|
||||
import { IdentityFormValues } from "@/core/types";
|
||||
import { IdentityFormProps } from "./IdentityForm";
|
||||
import { UploadFile } from "@mui/icons-material";
|
||||
import { genderOptions, religionOptions } from "@/core/constant";
|
||||
import { useState } from "react";
|
||||
export default function InnerIdentityForm(
|
||||
props: FormikProps<IdentityFormValues> & IdentityFormProps,
|
||||
) {
|
||||
console.log(props.data)
|
||||
const handleBack = () => {
|
||||
// قبل از رفتن به عقب، مقادیر فعلی فرم را در استیت والد ذخیره کن
|
||||
props.update({ identity: props.values });
|
||||
props.setStep(props.step - 1);
|
||||
};
|
||||
const [profilePhoto, setProfilePhoto] = useState<File | null>(null);
|
||||
const [profilePhotoError, setProfilePhotoError] = useState<string>("");
|
||||
|
||||
const handleProfilePhotoChange = (
|
||||
event: React.ChangeEvent<HTMLInputElement>,
|
||||
) => {
|
||||
const file = event.target.files?.[0];
|
||||
|
||||
if (!file) return;
|
||||
|
||||
if (!file.type.startsWith("image/")) {
|
||||
setProfilePhoto(null);
|
||||
setProfilePhotoError("فقط فایل تصویری مجاز است");
|
||||
return;
|
||||
}
|
||||
|
||||
const maxSize = 500 * 1024; // 500KB
|
||||
if (file.size > maxSize) {
|
||||
setProfilePhoto(null);
|
||||
setProfilePhotoError("حجم عکس باید حداکثر ۵۰۰ کیلوبایت باشد");
|
||||
return;
|
||||
}
|
||||
|
||||
setProfilePhoto(file);
|
||||
setProfilePhotoError("");
|
||||
};
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Paper
|
||||
elevation={0}
|
||||
sx={{
|
||||
width: "100%",
|
||||
background: "#ffffff",
|
||||
}}
|
||||
>
|
||||
<Form>
|
||||
<div
|
||||
style={{
|
||||
display: "grid",
|
||||
gridTemplateColumns: "repeat(auto-fit, minmax(340px, 1fr))",
|
||||
gap: "18px",
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<TextField
|
||||
label="نام"
|
||||
value={props.values.firstName}
|
||||
onChange={(e) =>
|
||||
props.setFieldValue("firstName", e.target.value)
|
||||
}
|
||||
error={!!props.errors.firstName}
|
||||
helperText={props.errors.firstName}
|
||||
fullWidth
|
||||
required
|
||||
/>
|
||||
<ErrorMessage component={"div"} name="firstName" />
|
||||
</div>
|
||||
|
||||
<TextField
|
||||
label="نام خانوادگی"
|
||||
value={props.values.lastName}
|
||||
onChange={(e) => props.setFieldValue("lastName", e.target.value)}
|
||||
error={!!props.errors.lastName}
|
||||
helperText={props.errors.lastName}
|
||||
fullWidth
|
||||
required
|
||||
/>
|
||||
|
||||
<TextField
|
||||
label="نام پدر"
|
||||
value={props.values.fatherName}
|
||||
onChange={(e) =>
|
||||
props.setFieldValue("fatherName", e.target.value)
|
||||
}
|
||||
fullWidth
|
||||
/>
|
||||
|
||||
<TextField
|
||||
label="کد ملی"
|
||||
value={props.values.nationalCode}
|
||||
onChange={(e) =>
|
||||
props.setFieldValue("nationalCode", e.target.value)
|
||||
}
|
||||
error={!!props.errors.nationalCode}
|
||||
helperText={props.errors.nationalCode}
|
||||
fullWidth
|
||||
required
|
||||
/>
|
||||
|
||||
{/* <DatePicker
|
||||
label="تاریخ تولد"
|
||||
value={props.values.birthDate}
|
||||
onChange={(newValue) =>
|
||||
props.setFieldValue("birthDate", newValue)
|
||||
}
|
||||
slotProps={{
|
||||
textField: {
|
||||
fullWidth: true,
|
||||
error: !!props.errors.birthDate,
|
||||
helperText: props.errors.birthDate,
|
||||
},
|
||||
}}
|
||||
/> */}
|
||||
|
||||
<TextField
|
||||
label="محل تولد"
|
||||
value={props.values.birthPlace}
|
||||
onChange={(e) =>
|
||||
props.setFieldValue("birthPlace", e.target.value)
|
||||
}
|
||||
fullWidth
|
||||
/>
|
||||
|
||||
<TextField
|
||||
select
|
||||
label="جنسیت"
|
||||
value={props.values.gender}
|
||||
onChange={(e) => props.setFieldValue("gender", e.target.value)}
|
||||
error={!!props.errors.gender}
|
||||
helperText={props.errors.gender}
|
||||
fullWidth
|
||||
required
|
||||
>
|
||||
{genderOptions.map((item) => (
|
||||
<MenuItem key={item.id} value={item.value}>
|
||||
{item.label}
|
||||
</MenuItem>
|
||||
))}
|
||||
</TextField>
|
||||
|
||||
<TextField
|
||||
select
|
||||
label="دین"
|
||||
value={props.values.religion}
|
||||
onChange={(e) => props.setFieldValue("religion", e.target.value)}
|
||||
fullWidth
|
||||
>
|
||||
{religionOptions.map((item) => (
|
||||
<MenuItem key={item} value={item}>
|
||||
{item}
|
||||
</MenuItem>
|
||||
))}
|
||||
</TextField>
|
||||
|
||||
<TextField
|
||||
label="ملیت"
|
||||
value={props.values.nationality}
|
||||
onChange={(e) =>
|
||||
props.setFieldValue("nationality", e.target.value)
|
||||
}
|
||||
error={!!props.errors.nationality}
|
||||
helperText={props.errors.nationality}
|
||||
fullWidth
|
||||
required
|
||||
/>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
border: profilePhotoError
|
||||
? "1px solid #ef4444"
|
||||
: "1px dashed #cbd5e1",
|
||||
borderRadius: "18px",
|
||||
backgroundColor: "#f8fafc",
|
||||
p: 2,
|
||||
minHeight: "100%",
|
||||
transition: "all 0.2s ease",
|
||||
"&:hover": {
|
||||
borderColor: profilePhotoError ? "#ef4444" : "#2563eb",
|
||||
backgroundColor: "#f8fbff",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
fontWeight: 700,
|
||||
color: "#0f172a",
|
||||
mb: 1.5,
|
||||
fontSize: "0.95rem",
|
||||
}}
|
||||
>
|
||||
عکس پرسنلی
|
||||
</Typography>
|
||||
|
||||
<Typography
|
||||
sx={{
|
||||
color: "#64748b",
|
||||
fontSize: "0.82rem",
|
||||
mb: 2,
|
||||
lineHeight: 1.8,
|
||||
}}
|
||||
>
|
||||
فقط فایل تصویری مجاز است و حجم آن باید حداکثر ۵۰۰ کیلوبایت باشد.
|
||||
</Typography>
|
||||
|
||||
<Button
|
||||
component="label"
|
||||
variant="outlined"
|
||||
startIcon={<UploadFile />}
|
||||
sx={{
|
||||
borderRadius: "12px",
|
||||
borderColor: "#cbd5e1",
|
||||
color: "#2563eb",
|
||||
fontWeight: 700,
|
||||
px: 2.5,
|
||||
"&:hover": {
|
||||
borderColor: "#2563eb",
|
||||
backgroundColor: "#eff6ff",
|
||||
},
|
||||
}}
|
||||
>
|
||||
انتخاب عکس
|
||||
<input
|
||||
hidden
|
||||
type="file"
|
||||
accept="image/*"
|
||||
onChange={handleProfilePhotoChange}
|
||||
/>
|
||||
</Button>
|
||||
|
||||
{profilePhoto && (
|
||||
<Typography
|
||||
sx={{
|
||||
mt: 1.5,
|
||||
fontSize: "0.82rem",
|
||||
color: "#475569",
|
||||
wordBreak: "break-word",
|
||||
}}
|
||||
>
|
||||
فایل انتخابشده: {profilePhoto.name}
|
||||
</Typography>
|
||||
)}
|
||||
|
||||
{profilePhotoError && (
|
||||
<Typography
|
||||
sx={{
|
||||
mt: 1.5,
|
||||
color: "#dc2626",
|
||||
fontSize: "0.8rem",
|
||||
fontWeight: 600,
|
||||
}}
|
||||
>
|
||||
{profilePhotoError}
|
||||
</Typography>
|
||||
)}
|
||||
</Box>
|
||||
</div>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
mt: 5,
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
disabled={props.step === 1}
|
||||
type="button"
|
||||
onClick={handleBack}
|
||||
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>
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user