change some files
This commit is contained in:
190
ui/forms/relation/InnerRelationForm.tsx
Normal file
190
ui/forms/relation/InnerRelationForm.tsx
Normal file
@@ -0,0 +1,190 @@
|
||||
"use client";
|
||||
|
||||
import React from "react";
|
||||
import {
|
||||
Box,
|
||||
Paper,
|
||||
TextField,
|
||||
Typography,
|
||||
Alert,
|
||||
AlertTitle,
|
||||
Divider,
|
||||
Button,
|
||||
} from "@mui/material";
|
||||
import { FieldArray, Form, getIn, type FormikProps } from "formik";
|
||||
import type { RelationFormProps, RelationFormValues } from "./types";
|
||||
|
||||
type Props = FormikProps<RelationFormValues> & RelationFormProps;
|
||||
|
||||
export default function InnerRelationForm(props: Props) {
|
||||
const {
|
||||
values,
|
||||
errors,
|
||||
touched,
|
||||
handleChange,
|
||||
setFieldValue,
|
||||
isSubmitting,
|
||||
} = props;
|
||||
|
||||
const handleBack = () => {
|
||||
props.update({ relations: values.relations });
|
||||
props.setStep(props.step - 1);
|
||||
};
|
||||
|
||||
return (
|
||||
<Form>
|
||||
<Paper
|
||||
elevation={0}
|
||||
sx={{
|
||||
p: { xs: 2, md: 3 },
|
||||
borderRadius: "24px",
|
||||
border: "1px solid #e2e8f0",
|
||||
backgroundColor: "#fff",
|
||||
}}
|
||||
>
|
||||
<Alert severity="warning" sx={{ mb: 3, borderRadius: "12px" }}>
|
||||
<AlertTitle sx={{ fontWeight: 800 }}>توجه</AlertTitle>
|
||||
مشخصات دو نفر از آشنایان را وارد کنید و از درج بستگان درجه یک (پدر،
|
||||
مادر، همسر، برادر و خواهر) خودداری نمایید.
|
||||
</Alert>
|
||||
|
||||
<FieldArray name="relations">
|
||||
{() => (
|
||||
<Box>
|
||||
{values.relations.map((item, index) => {
|
||||
const itemErrors = getIn(errors, `relations.${index}`) || {};
|
||||
const itemTouched = getIn(touched, `relations.${index}`) || {};
|
||||
|
||||
return (
|
||||
<Box key={item.id || index} sx={{ mb: index === 0 ? 4 : 0 }}>
|
||||
<Typography
|
||||
variant="subtitle1"
|
||||
sx={{ fontWeight: 800, mb: 2, color: "#1e293b" }}
|
||||
>
|
||||
آشنای {index === 0 ? "اول" : "دوم"}
|
||||
</Typography>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
display: "grid",
|
||||
gridTemplateColumns: { xs: "1fr", md: "1fr 1fr" },
|
||||
gap: 2.5,
|
||||
}}
|
||||
>
|
||||
<TextField
|
||||
label="نام*"
|
||||
name={`relations.${index}.firstName`}
|
||||
value={item.firstName}
|
||||
onChange={handleChange}
|
||||
fullWidth
|
||||
error={!!itemTouched.firstName && !!itemErrors.firstName}
|
||||
helperText={itemTouched.firstName ? itemErrors.firstName : ""}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
label="نام خانوادگی*"
|
||||
name={`relations.${index}.lastName`}
|
||||
value={item.lastName}
|
||||
onChange={handleChange}
|
||||
fullWidth
|
||||
error={!!itemTouched.lastName && !!itemErrors.lastName}
|
||||
helperText={itemTouched.lastName ? itemErrors.lastName : ""}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
label="نسبت*"
|
||||
name={`relations.${index}.relationship`}
|
||||
value={item.relationship}
|
||||
onChange={handleChange}
|
||||
fullWidth
|
||||
placeholder="مثلاً: همکار، دوست"
|
||||
error={!!itemTouched.relationship && !!itemErrors.relationship}
|
||||
helperText={
|
||||
itemTouched.relationship ? itemErrors.relationship : ""
|
||||
}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
label="تلفن تماس*"
|
||||
name={`relations.${index}.phoneNumber`}
|
||||
value={item.phoneNumber}
|
||||
onChange={(e) => {
|
||||
const val = e.target.value.replace(/[^\d]/g, "");
|
||||
setFieldValue(`relations.${index}.phoneNumber`, val);
|
||||
}}
|
||||
fullWidth
|
||||
inputMode="tel"
|
||||
error={!!itemTouched.phoneNumber && !!itemErrors.phoneNumber}
|
||||
helperText={
|
||||
itemTouched.phoneNumber ? itemErrors.phoneNumber : ""
|
||||
}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
label="شغل*"
|
||||
name={`relations.${index}.jobTitle`}
|
||||
value={item.jobTitle}
|
||||
onChange={handleChange}
|
||||
fullWidth
|
||||
error={!!itemTouched.jobTitle && !!itemErrors.jobErrors}
|
||||
helperText={itemTouched.jobTitle ? itemErrors.jobTitle : ""}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
label="نام محل کار*"
|
||||
name={`relations.${index}.workplaceName`}
|
||||
value={item.workplaceName}
|
||||
onChange={handleChange}
|
||||
fullWidth
|
||||
error={
|
||||
!!itemTouched.workplaceName && !!itemErrors.workplaceName
|
||||
}
|
||||
helperText={
|
||||
itemTouched.workplaceName ? itemErrors.workplaceName : ""
|
||||
}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{index === 0 && <Divider sx={{ mt: 4 }} />}
|
||||
</Box>
|
||||
);
|
||||
})}
|
||||
</Box>
|
||||
)}
|
||||
</FieldArray>
|
||||
|
||||
{/* دکمههای ناوبری */}
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
mt: 4,
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
disabled={props.step === 1 || isSubmitting}
|
||||
onClick={handleBack}
|
||||
sx={{ borderRadius: "12px", color: "#64748b", fontWeight: 700 }}
|
||||
>
|
||||
بازگشت
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant="contained"
|
||||
type="submit"
|
||||
disabled={isSubmitting}
|
||||
sx={{
|
||||
borderRadius: "12px",
|
||||
px: 4,
|
||||
py: 1.5,
|
||||
bgcolor: props.step === 12 ? "green" : "#2563eb",
|
||||
fontWeight: 700,
|
||||
}}
|
||||
>
|
||||
{props.step === 12 ? "اتمام و ثبت نهایی" : "گام بعدی"}
|
||||
</Button>
|
||||
</Box>
|
||||
</Paper>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
36
ui/forms/relation/RelationForm.tsx
Normal file
36
ui/forms/relation/RelationForm.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
"use client";
|
||||
|
||||
import { withFormik, type FormikBag } from "formik";
|
||||
import type { RelationFormProps, RelationFormValues } from "./types";
|
||||
import { RELATION_INITIAL_VALUES } from "./constant";
|
||||
import { RelationValidationSchema } from "./validation";
|
||||
import InnerRelationForm from "./InnerRelationForm";
|
||||
|
||||
const RelationForm = withFormik<RelationFormProps, RelationFormValues>({
|
||||
displayName: "RelationForm",
|
||||
|
||||
enableReinitialize: true,
|
||||
|
||||
mapPropsToValues: (props) => {
|
||||
// اگر دادهای از قبل بود استفاده کن، در غیر این صورت مقدار اولیه (۲تایی)
|
||||
if (props.data?.relations?.length === 2) {
|
||||
return { relations: props.data.relations };
|
||||
}
|
||||
return RELATION_INITIAL_VALUES;
|
||||
},
|
||||
|
||||
validationSchema: RelationValidationSchema,
|
||||
|
||||
handleSubmit: async (
|
||||
values,
|
||||
bag: FormikBag<RelationFormProps, RelationFormValues>
|
||||
) => {
|
||||
const { props, setSubmitting } = bag;
|
||||
|
||||
props.update({ relations: values.relations });
|
||||
props.setStep((prev) => prev + 1);
|
||||
setSubmitting(false);
|
||||
},
|
||||
})(InnerRelationForm);
|
||||
|
||||
export default RelationForm;
|
||||
18
ui/forms/relation/constant/index.ts
Normal file
18
ui/forms/relation/constant/index.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import type { ReferenceItem, RelationFormValues } from "../types";
|
||||
|
||||
export const EMPTY_REFERENCE_ITEM: ReferenceItem = {
|
||||
id: "",
|
||||
firstName: "",
|
||||
lastName: "",
|
||||
relationship: "",
|
||||
jobTitle: "",
|
||||
workplaceName: "",
|
||||
phoneNumber: "",
|
||||
};
|
||||
|
||||
export const RELATION_INITIAL_VALUES: RelationFormValues = {
|
||||
relations: [
|
||||
{ ...EMPTY_REFERENCE_ITEM, id: 1 },
|
||||
{ ...EMPTY_REFERENCE_ITEM, id: 2 },
|
||||
],
|
||||
};
|
||||
28
ui/forms/relation/types/index.ts
Normal file
28
ui/forms/relation/types/index.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import type React from "react";
|
||||
|
||||
export interface ReferenceItem {
|
||||
id?: string | number;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
relationship: string;
|
||||
jobTitle: string;
|
||||
workplaceName: string;
|
||||
phoneNumber: string;
|
||||
}
|
||||
|
||||
export interface RelationFormValues {
|
||||
relations: ReferenceItem[]; // طول این آرایه همیشه باید 2 باشد
|
||||
}
|
||||
|
||||
/** هماهنگ با استیت کلی ویزارد شما */
|
||||
export interface WizardFormData {
|
||||
relations: ReferenceItem[];
|
||||
// ... سایر مراحل
|
||||
}
|
||||
|
||||
export interface RelationFormProps {
|
||||
step: number;
|
||||
setStep: React.Dispatch<React.SetStateAction<number>>;
|
||||
data: WizardFormData;
|
||||
update: (patch: Partial<WizardFormData>) => void;
|
||||
}
|
||||
27
ui/forms/relation/validation/index.ts
Normal file
27
ui/forms/relation/validation/index.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import * as Yup from "yup";
|
||||
|
||||
export const RelationValidationSchema = Yup.object().shape({
|
||||
relations: Yup.array()
|
||||
.of(
|
||||
Yup.object().shape({
|
||||
firstName: Yup.string().required("نام الزامی است"),
|
||||
lastName: Yup.string().required("نام خانوادگی الزامی است"),
|
||||
relationship: Yup.string()
|
||||
.required("نسبت الزامی است")
|
||||
.test(
|
||||
"no-immediate-family",
|
||||
"درج بستگان درجه یک مجاز نیست",
|
||||
(value) => {
|
||||
const forbidden = ["پدر", "مادر", "همسر", "برادر", "خواهر"];
|
||||
return !forbidden.some((f) => value?.includes(f));
|
||||
}
|
||||
),
|
||||
jobTitle: Yup.string().required("شغل الزامی است"),
|
||||
workplaceName: Yup.string().required("محل کار الزامی است"),
|
||||
phoneNumber: Yup.string()
|
||||
.required("تلفن الزامی است")
|
||||
.matches(/^0\d{10}$/, "شماره تماس معتبر نیست (۱۱ رقم با ۰)"),
|
||||
})
|
||||
)
|
||||
.length(2, "باید مشخصات دو نفر را وارد کنید"),
|
||||
});
|
||||
Reference in New Issue
Block a user