321 lines
8.8 KiB
TypeScript
321 lines
8.8 KiB
TypeScript
"use client";
|
||
|
||
import React from "react";
|
||
import {
|
||
Box,
|
||
Paper,
|
||
TextField,
|
||
Typography,
|
||
IconButton,
|
||
Button,
|
||
MenuItem,
|
||
Divider,
|
||
} from "@mui/material";
|
||
import AddIcon from "@mui/icons-material/Add";
|
||
import { DeleteOutlineOutlined } from "@mui/icons-material";
|
||
import { FieldArray, Form, getIn, type FormikProps } from "formik";
|
||
|
||
import type {
|
||
ReferralFormProps,
|
||
ReferralFormValues,
|
||
ReferralItem,
|
||
} from "./types";
|
||
import {
|
||
REFERRAL_EMPTY_ITEM,
|
||
REFERRAL_MIN_ITEMS,
|
||
} from "./constant";
|
||
|
||
type Props = FormikProps<ReferralFormValues> & ReferralFormProps;
|
||
|
||
function ReferralItemForm({
|
||
index,
|
||
item,
|
||
errors,
|
||
touched,
|
||
handleChange,
|
||
setFieldValue,
|
||
onRemove,
|
||
disableRemove,
|
||
}: {
|
||
index: number;
|
||
item: ReferralItem;
|
||
errors: any;
|
||
touched: any;
|
||
handleChange: React.ChangeEventHandler<HTMLInputElement | HTMLTextAreaElement>;
|
||
setFieldValue: (field: string, value: any, shouldValidate?: boolean) => void;
|
||
onRemove: () => void;
|
||
disableRemove: boolean;
|
||
}) {
|
||
const itemErrors = getIn(errors, `referrals.${index}`) || {};
|
||
const itemTouched = getIn(touched, `referrals.${index}`) || {};
|
||
|
||
return (
|
||
<Paper
|
||
elevation={0}
|
||
sx={{
|
||
p: { xs: 2, md: 2.5 },
|
||
borderRadius: "20px",
|
||
border: "1px solid #e5e7eb",
|
||
backgroundColor: "#fff",
|
||
}}
|
||
>
|
||
<Box
|
||
sx={{
|
||
display: "flex",
|
||
alignItems: "center",
|
||
justifyContent: "space-between",
|
||
mb: 1.5,
|
||
gap: 2,
|
||
}}
|
||
>
|
||
<Typography sx={{ fontWeight: 700 }}>معرف {index + 1}</Typography>
|
||
|
||
<IconButton
|
||
onClick={onRemove}
|
||
disabled={disableRemove}
|
||
color="error"
|
||
size="small"
|
||
aria-label="حذف معرف"
|
||
>
|
||
<DeleteOutlineOutlined />
|
||
</IconButton>
|
||
</Box>
|
||
|
||
<Box
|
||
sx={{
|
||
display: "grid",
|
||
gridTemplateColumns: { xs: "1fr", md: "1fr 1fr" },
|
||
gap: 2,
|
||
}}
|
||
>
|
||
<TextField
|
||
label="نام"
|
||
name={`referrals.${index}.firstName`}
|
||
value={item.firstName}
|
||
onChange={handleChange}
|
||
fullWidth
|
||
error={!!itemTouched.firstName && !!itemErrors.firstName}
|
||
helperText={itemTouched.firstName ? itemErrors.firstName : ""}
|
||
/>
|
||
|
||
<TextField
|
||
label="نام خانوادگی"
|
||
name={`referrals.${index}.lastName`}
|
||
value={item.lastName}
|
||
onChange={handleChange}
|
||
fullWidth
|
||
error={!!itemTouched.lastName && !!itemErrors.lastName}
|
||
helperText={itemTouched.lastName ? itemErrors.lastName : ""}
|
||
/>
|
||
|
||
<TextField
|
||
label="نسبت / رابطه"
|
||
name={`referrals.${index}.relationship`}
|
||
value={item.relationship}
|
||
onChange={handleChange}
|
||
fullWidth
|
||
placeholder="مثلاً: دوست، همکار، فامیل..."
|
||
error={!!itemTouched.relationship && !!itemErrors.relationship}
|
||
helperText={itemTouched.relationship ? itemErrors.relationship : ""}
|
||
/>
|
||
|
||
<TextField
|
||
label="مدت زمان آشنایی"
|
||
name={`referrals.${index}.acquaintanceDuration`}
|
||
value={item.acquaintanceDuration}
|
||
onChange={handleChange}
|
||
fullWidth
|
||
placeholder="مثلاً: ۵ سال"
|
||
error={
|
||
!!itemTouched.acquaintanceDuration &&
|
||
!!itemErrors.acquaintanceDuration
|
||
}
|
||
helperText={
|
||
itemTouched.acquaintanceDuration
|
||
? itemErrors.acquaintanceDuration
|
||
: ""
|
||
}
|
||
/>
|
||
|
||
<TextField
|
||
select
|
||
label="نوع آشنایی"
|
||
name={`referrals.${index}.acquaintanceType`}
|
||
value={item.acquaintanceType}
|
||
onChange={handleChange}
|
||
fullWidth
|
||
error={
|
||
!!itemTouched.acquaintanceType &&
|
||
!!itemErrors.acquaintanceType
|
||
}
|
||
helperText={
|
||
itemTouched.acquaintanceType ? itemErrors.acquaintanceType : ""
|
||
}
|
||
>
|
||
<MenuItem value="Direct">مستقیم</MenuItem>
|
||
<MenuItem value="Indirect">غیرمستقیم</MenuItem>
|
||
</TextField>
|
||
|
||
<TextField
|
||
label="تلفن تماس"
|
||
name={`referrals.${index}.phoneNumber`}
|
||
value={item.phoneNumber}
|
||
onChange={(e) => {
|
||
const onlyDigits = e.target.value.replace(/[^\d]/g, "").slice(0, 11);
|
||
setFieldValue(`referrals.${index}.phoneNumber`, onlyDigits);
|
||
}}
|
||
fullWidth
|
||
placeholder="مثلاً: 0912xxxxxxx"
|
||
error={!!itemTouched.phoneNumber && !!itemErrors.phoneNumber}
|
||
helperText={itemTouched.phoneNumber ? itemErrors.phoneNumber : ""}
|
||
/>
|
||
|
||
<TextField
|
||
label="شغل معرف"
|
||
name={`referrals.${index}.jobTitle`}
|
||
value={item.jobTitle}
|
||
onChange={handleChange}
|
||
fullWidth
|
||
error={!!itemTouched.jobTitle && !!itemErrors.jobTitle}
|
||
helperText={itemTouched.jobTitle ? itemErrors.jobTitle : ""}
|
||
/>
|
||
|
||
<TextField
|
||
label="نام محل کار معرف"
|
||
name={`referrals.${index}.workplaceName`}
|
||
value={item.workplaceName}
|
||
onChange={handleChange}
|
||
fullWidth
|
||
error={!!itemTouched.workplaceName && !!itemErrors.workplaceName}
|
||
helperText={itemTouched.workplaceName ? itemErrors.workplaceName : ""}
|
||
/>
|
||
</Box>
|
||
</Paper>
|
||
);
|
||
}
|
||
|
||
export default function InnerReferralForm(props: Props) {
|
||
const {
|
||
values,
|
||
errors,
|
||
touched,
|
||
handleChange,
|
||
setFieldValue,
|
||
isSubmitting,
|
||
} = props;
|
||
|
||
const handleBack = () => {
|
||
props.update({
|
||
referrals: values.referrals,
|
||
});
|
||
props.setStep(props.step - 1);
|
||
};
|
||
|
||
return (
|
||
<Form>
|
||
<FieldArray name="referrals">
|
||
{({ push, remove }) => (
|
||
<Paper
|
||
elevation={0}
|
||
sx={{
|
||
p: { xs: 2, md: 3 },
|
||
borderRadius: "24px",
|
||
border: "1px solid #e2e8f0",
|
||
backgroundColor: "#fff",
|
||
}}
|
||
>
|
||
<Box
|
||
sx={{
|
||
display: "flex",
|
||
alignItems: "center",
|
||
justifyContent: "space-between",
|
||
gap: 2,
|
||
flexWrap: "wrap",
|
||
}}
|
||
>
|
||
<Typography variant="h6" sx={{ fontWeight: 700 }}>
|
||
معرفها
|
||
</Typography>
|
||
|
||
<Button
|
||
type="button"
|
||
onClick={() =>
|
||
push({
|
||
...REFERRAL_EMPTY_ITEM,
|
||
id: Date.now(),
|
||
})
|
||
}
|
||
variant="contained"
|
||
startIcon={<AddIcon />}
|
||
sx={{ borderRadius: "12px", fontWeight: 700 }}
|
||
>
|
||
افزودن معرف جدید
|
||
</Button>
|
||
</Box>
|
||
|
||
<Divider sx={{ my: 2 }} />
|
||
|
||
<Box sx={{ display: "grid", gap: 2 }}>
|
||
{values.referrals.map((item, idx) => (
|
||
<ReferralItemForm
|
||
key={item.id || idx}
|
||
index={idx}
|
||
item={item}
|
||
errors={errors}
|
||
touched={touched}
|
||
handleChange={handleChange}
|
||
setFieldValue={setFieldValue}
|
||
onRemove={() => remove(idx)}
|
||
disableRemove={values.referrals.length <= REFERRAL_MIN_ITEMS}
|
||
/>
|
||
))}
|
||
</Box>
|
||
|
||
{typeof errors.referrals === "string" && (
|
||
<Typography color="error" sx={{ mt: 2 }}>
|
||
{errors.referrals}
|
||
</Typography>
|
||
)}
|
||
|
||
<Box
|
||
sx={{
|
||
display: "flex",
|
||
justifyContent: "space-between",
|
||
mt: 4,
|
||
}}
|
||
>
|
||
<Button
|
||
type="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>
|
||
)}
|
||
</FieldArray>
|
||
</Form>
|
||
);
|
||
}
|