Files
hounam-submit-form-frontend/ui/forms/IdentityForm.tsx
2026-05-31 14:22:39 +03:30

406 lines
11 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import React, { useMemo, useState } from "react";
import {
Avatar,
Box,
Button,
MenuItem,
Paper,
TextField,
Typography,
} from "@mui/material";
import { UploadFile } from "@mui/icons-material";
import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider";
import { AdapterDateFnsJalali } from "@mui/x-date-pickers/AdapterDateFnsJalali";
import { DatePicker } from "@mui/x-date-pickers/DatePicker";
type IdentityFormData = {
applicantId: string;
firstName: string;
lastName: string;
fatherName: string;
nationalCode: string;
birthDate: string;
birthPlace: string;
gender: string;
religion: string;
nationality: string;
profilePhotoId: string;
};
type IdentityFormErrors = Partial<Record<keyof IdentityFormData, string>>;
const initialForm: IdentityFormData = {
applicantId: "",
firstName: "",
lastName: "",
fatherName: "",
nationalCode: "",
birthDate: "",
birthPlace: "",
gender: "",
religion: "",
nationality: "",
profilePhotoId: "",
};
export default function IdentityForm() {
const [formData, setFormData] = useState<IdentityFormData>(initialForm);
const [errors, setErrors] = useState<IdentityFormErrors>({});
const [submitted, setSubmitted] = useState(false);
const [profilePhoto, setProfilePhoto] = useState<File | null>(null);
const [profilePhotoPreview, setProfilePhotoPreview] = useState<string>("");
const [profilePhotoError, setProfilePhotoError] = useState<string>("");
const [birthDateValue, setBirthDateValue] = useState<Date | null>(null);
const handleProfilePhotoChange = (
event: React.ChangeEvent<HTMLInputElement>,
) => {
const file = event.target.files?.[0];
if (!file) return;
if (!file.type.startsWith("image/")) {
setProfilePhoto(null);
setProfilePhotoPreview("");
setProfilePhotoError("فقط فایل تصویری مجاز است");
return;
}
const maxSize = 500 * 1024; // 500KB
if (file.size > maxSize) {
setProfilePhoto(null);
setProfilePhotoPreview("");
setProfilePhotoError("حجم عکس باید حداکثر ۵۰۰ کیلوبایت باشد");
return;
}
setProfilePhoto(file);
setProfilePhotoError("");
const previewUrl = URL.createObjectURL(file);
setProfilePhotoPreview(previewUrl);
};
const genderOptions = useMemo(() => ["مرد", "زن", "سایر"], []);
const religionOptions = useMemo(
() => ["اسلام", "مسیحیت", "یهودیت", "زرتشتی", "سایر"],
[],
);
const handleChange =
(field: keyof IdentityFormData) =>
(event: React.ChangeEvent<HTMLInputElement>) => {
let value = event.target.value;
if (field === "nationalCode") {
value = value.replace(/\D/g, "").slice(0, 10);
}
setFormData((prev) => ({
...prev,
[field]: value,
}));
if (errors[field]) {
setErrors((prev) => ({
...prev,
[field]: "",
}));
}
};
const validate = () => {
const newErrors: IdentityFormErrors = {};
let hasError = false;
if (!formData.applicantId.trim()) {
newErrors.applicantId = "شناسه متقاضی الزامی است";
hasError = true;
}
if (!formData.firstName.trim()) {
newErrors.firstName = "نام الزامی است";
hasError = true;
}
if (!formData.lastName.trim()) {
newErrors.lastName = "نام خانوادگی الزامی است";
hasError = true;
}
if (!formData.nationalCode.trim()) {
newErrors.nationalCode = "کد ملی الزامی است";
hasError = true;
} else if (!/^\d{10}$/.test(formData.nationalCode)) {
newErrors.nationalCode = "کد ملی باید ۱۰ رقم باشد";
hasError = true;
}
if (!formData.birthDate.trim()) {
newErrors.birthDate = "تاریخ تولد الزامی است";
hasError = true;
}
if (!formData.gender.trim()) {
newErrors.gender = "جنسیت الزامی است";
hasError = true;
}
if (!formData.nationality.trim()) {
newErrors.nationality = "ملیت الزامی است";
hasError = true;
}
if (!profilePhoto) {
setProfilePhotoError("عکس پرسنلی الزامی است");
hasError = true;
} else {
setProfilePhotoError("");
}
setErrors(newErrors);
return !hasError;
};
const handleSubmit = (event: React.FormEvent) => {
event.preventDefault();
setSubmitted(false);
if (!validate()) return;
const payload = {
...formData,
birthDate: formData.birthDate ? new Date(formData.birthDate) : null,
fatherName: formData.fatherName || null,
birthPlace: formData.birthPlace || null,
religion: formData.religion || null,
profilePhotoId: formData.profilePhotoId || null,
};
console.log("Identity Payload:", payload);
setSubmitted(true);
};
return (
<Box>
<Paper
elevation={0}
sx={{
width: "100%",
background: "#ffffff",
}}
>
<Box component="form" onSubmit={handleSubmit}>
<div
style={{
display: "grid",
gridTemplateColumns: "repeat(auto-fit, minmax(340px, 1fr))",
gap: "18px",
}}
>
<TextField
label="نام"
value={formData.firstName}
onChange={handleChange("firstName")}
error={!!errors.firstName}
helperText={errors.firstName}
fullWidth
/>
<TextField
label="نام خانوادگی"
value={formData.lastName}
onChange={handleChange("lastName")}
error={!!errors.lastName}
helperText={errors.lastName}
fullWidth
/>
<TextField
label="نام پدر"
value={formData.fatherName}
onChange={handleChange("fatherName")}
fullWidth
/>
<TextField
label="کد ملی"
value={formData.nationalCode}
onChange={handleChange("nationalCode")}
error={!!errors.nationalCode}
helperText={errors.nationalCode}
fullWidth
/>
<DatePicker
label="تاریخ تولد"
value={birthDateValue}
onChange={(newValue) => {
setBirthDateValue(newValue);
setFormData((prev) => ({
...prev,
birthDate: newValue ? newValue.toISOString() : "",
}));
if (errors.birthDate) {
setErrors((prev) => ({
...prev,
birthDate: "",
}));
}
}}
slotProps={{
textField: {
fullWidth: true,
error: !!errors.birthDate,
helperText: errors.birthDate,
},
}}
/>
<TextField
label="محل تولد"
value={formData.birthPlace}
onChange={handleChange("birthPlace")}
fullWidth
/>
<TextField
select
label="جنسیت"
value={formData.gender}
onChange={handleChange("gender")}
error={!!errors.gender}
helperText={errors.gender}
fullWidth
>
{genderOptions.map((item) => (
<MenuItem key={item} value={item}>
{item}
</MenuItem>
))}
</TextField>
<TextField
select
label="دین"
value={formData.religion}
onChange={handleChange("religion")}
fullWidth
>
{religionOptions.map((item) => (
<MenuItem key={item} value={item}>
{item}
</MenuItem>
))}
</TextField>
<TextField
label="ملیت"
value={formData.nationality}
onChange={handleChange("nationality")}
error={!!errors.nationality}
helperText={errors.nationality}
fullWidth
/>
<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>
</Paper>
</Box>
);
}