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

287 lines
7.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
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.
import React, { useEffect, useMemo, useState } from "react";
import { Box, MenuItem, TextField } from "@mui/material";
type BloodType = "" | "A+" | "A-" | "B+" | "B-" | "AB+" | "AB-" | "O+" | "O-";
export interface PhysicalInfoFormState {
applicantId: string;
bloodType: BloodType;
height: number | ""; // cm
weight: number | ""; // kg
bmi: number | ""; // auto or manual
hasDisability: boolean;
disabilityDescription: string;
hasChronicDisease: boolean;
chronicDiseaseDescription: string;
surgeryHistory: string;
medications: string;
specialMark: string;
}
const initialValues: PhysicalInfoFormState = {
applicantId: "",
bloodType: "",
height: "",
weight: "",
bmi: "",
hasDisability: false,
disabilityDescription: "",
hasChronicDisease: false,
chronicDiseaseDescription: "",
surgeryHistory: "",
medications: "",
specialMark: "",
};
function toNumberOrEmpty(v: string): number | "" {
if (v === "") return "";
const n = Number(v);
return Number.isFinite(n) ? n : "";
}
function round1(n: number) {
return Math.round(n * 10) / 10;
}
export default function PhysicalInfoForm(props: {
value?: PhysicalInfoFormState;
onChange?: (next: PhysicalInfoFormState) => void;
applicantId?: string; // اگر خواستی از بیرون تزریق کنی
}) {
const { value, onChange, applicantId } = props;
const [formData, setFormData] = useState<PhysicalInfoFormState>(
value ?? { ...initialValues, applicantId: applicantId ?? "" },
);
// اگر value کنترل‌شده بود، همگام‌سازی
useEffect(() => {
if (value) setFormData(value);
}, [value]);
// اگر applicantId از بیرون تغییر کرد
useEffect(() => {
if (!value && applicantId) {
setFormData((p) => ({ ...p, applicantId }));
}
}, [applicantId, value]);
const setNext = (
updater: (prev: PhysicalInfoFormState) => PhysicalInfoFormState,
) => {
setFormData((prev) => {
const next = updater(prev);
onChange?.(next);
return next;
});
};
const handleText =
(field: keyof PhysicalInfoFormState) =>
(e: React.ChangeEvent<HTMLInputElement>) => {
const v = e.target.value;
setNext((p) => ({ ...p, [field]: v }) as PhysicalInfoFormState);
};
const handleNumber =
(field: keyof PhysicalInfoFormState) =>
(e: React.ChangeEvent<HTMLInputElement>) => {
const v = toNumberOrEmpty(e.target.value);
setNext((p) => ({ ...p, [field]: v }) as PhysicalInfoFormState);
};
const handleBoolSelect =
(field: "hasDisability" | "hasChronicDisease") =>
(e: React.ChangeEvent<HTMLInputElement>) => {
const v = e.target.value === "true";
setNext((p) => {
// اگر false شد، توضیحات را پاک می‌کنیم تا داده کثیف نماند
if (field === "hasDisability" && !v) {
return { ...p, hasDisability: false, disabilityDescription: "" };
}
if (field === "hasChronicDisease" && !v) {
return {
...p,
hasChronicDisease: false,
chronicDiseaseDescription: "",
};
}
return { ...p, [field]: v };
});
};
// محاسبه BMI از روی قد و وزن (cm, kg)
const computedBmi = useMemo(() => {
if (formData.height === "" || formData.weight === "") return "";
const hMeters = Number(formData.height) / 100;
if (!hMeters || hMeters <= 0) return "";
const bmi = Number(formData.weight) / (hMeters * hMeters);
return Number.isFinite(bmi) ? round1(bmi) : "";
}, [formData.height, formData.weight]);
// sync bmi (فقط وقتی قد/وزن داریم)
useEffect(() => {
// اگر بخوای دستی BMI وارد کنی، این بخش رو حذف کن.
setNext((p) => ({ ...p, bmi: computedBmi }));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [computedBmi]);
return (
<Box
sx={{
display: "grid",
gridTemplateColumns: { xs: "1fr", md: "repeat(2, 1fr)" },
gap: 2,
width: "100%",
}}
>
{/* bloodType */}
<TextField
select
label="گروه خونی"
value={formData.bloodType}
onChange={handleText("bloodType")}
fullWidth
>
<MenuItem value="">انتخاب کنید</MenuItem>
{(["A+", "A-", "B+", "B-", "AB+", "AB-", "O+", "O-"] as const).map(
(bt) => (
<MenuItem key={bt} value={bt}>
{bt}
</MenuItem>
),
)}
</TextField>
{/* height */}
<TextField
label="قد (سانتی‌متر)"
type="number"
value={formData.height}
onChange={handleNumber("height")}
fullWidth
/>
{/* weight */}
<TextField
label="وزن (کیلوگرم)"
type="number"
value={formData.weight}
onChange={handleNumber("weight")}
fullWidth
/>
{/* bmi */}
<TextField
label="BMI"
type="number"
value={formData.bmi}
onChange={handleNumber("bmi")}
fullWidth
disabled // چون خودکار محاسبه می‌کنیم
helperText={
formData.height !== "" && formData.weight !== ""
? "به‌صورت خودکار از قد و وزن محاسبه می‌شود"
: "برای محاسبه BMI، قد و وزن را وارد کنید"
}
/>
{/* specialMark */}
<TextField
label="علامت مشخصه"
value={formData.specialMark}
onChange={handleText("specialMark")}
fullWidth
/>
{/* hasDisability */}
<TextField
select
label="معلولیت دارد؟"
value={String(formData.hasDisability)}
onChange={handleBoolSelect("hasDisability")}
fullWidth
>
<MenuItem value="false">خیر</MenuItem>
<MenuItem value="true">بله</MenuItem>
</TextField>
{/* hasChronicDisease */}
<TextField
select
label="بیماری مزمن دارد؟"
value={String(formData.hasChronicDisease)}
onChange={handleBoolSelect("hasChronicDisease")}
fullWidth
>
<MenuItem value="false">خیر</MenuItem>
<MenuItem value="true">بله</MenuItem>
</TextField>
{/* disabilityDescription */}
{formData.hasDisability && (
<Box sx={{ gridColumn: { xs: "1", md: "1 / -1" } }}>
<TextField
label="توضیحات معلولیت"
value={formData.disabilityDescription}
onChange={handleText("disabilityDescription")}
fullWidth
multiline
minRows={2}
/>
</Box>
)}
{/* chronicDiseaseDescription */}
{formData.hasChronicDisease && (
<Box sx={{ gridColumn: { xs: "1", md: "1 / -1" } }}>
<TextField
label="توضیحات بیماری مزمن"
value={formData.chronicDiseaseDescription}
onChange={handleText("chronicDiseaseDescription")}
fullWidth
multiline
minRows={2}
/>
</Box>
)}
{/* surgeryHistory */}
<Box sx={{ gridColumn: { xs: "1", md: "1 / -1" } }}>
<TextField
label="سابقه جراحی"
value={formData.surgeryHistory}
onChange={handleText("surgeryHistory")}
fullWidth
multiline
minRows={2}
/>
</Box>
{/* medications */}
<Box sx={{ gridColumn: { xs: "1", md: "1 / -1" } }}>
<TextField
label="داروهای مصرفی"
value={formData.medications}
onChange={handleText("medications")}
fullWidth
multiline
minRows={2}
/>
</Box>
</Box>
);
}