Files
2026-09-28 13:33:01 +03:30

687 lines
24 KiB
JavaScript

import React, { useContext, useState } from 'react'
import { Box, Input, Radio, RadioGroup, Select, SimpleGrid, Stack, Textarea, FormControl, FormLabel, FormErrorMessage, InputGroup, InputLeftElement, InputRightElement } from '@chakra-ui/react'
import {
MDBBtn,
MDBInputGroup,
MDBInputGroupText,
} from 'mdb-react-ui-kit'
import { FiUser, FiPhone, FiCalendar, FiHome, FiBriefcase, FiHeart } from 'react-icons/fi'
import { useForm, Controller } from 'react-hook-form'
import { HandleFormReq } from '../../../../lib/HandleFormReq'
import axios from 'axios'
import { AppProduction } from '../../../../lib/api'
// آیکون سفارشی
const FieldIcon = ({ icon: Icon }) => (
<MDBInputGroupText className={'rounded-0 bg-light'}>
<Icon size={16} />
</MDBInputGroupText>
)
// کامپوننت فیلد ورودی
const FormField = ({
label,
icon: Icon,
error,
children,
isRequired = false,
helpText
}) => (
<Box>
<FormControl isInvalid={!!error} isRequired={isRequired}>
{label && (
<FormLabel
mb={1}
fontSize="sm"
fontWeight="medium"
color="gray.700"
dir="rtl"
>
{label}
</FormLabel>
)}
<InputGroup size="md">
{Icon && (
<InputLeftElement pointerEvents="none" h="40px">
<FieldIcon icon={Icon} />
</InputLeftElement>
)}
{children}
</InputGroup>
{error && (
<FormErrorMessage dir="rtl" fontSize="xs" color="red.500">
{error}
</FormErrorMessage>
)}
{helpText && !error && (
<Box fontSize="xs" color="gray.500" mt={1} dir="rtl">
{helpText}
</Box>
)}
</FormControl>
</Box>
)
// کامپوننت Select با Controller - حل مشکل theme
const FormSelect = ({
label,
options,
error,
name,
control,
placeholder = "انتخاب کنید",
isRequired = false,
defaultValue = ""
}) => (
<Box>
<FormControl isInvalid={!!error} isRequired={isRequired}>
{label && (
<FormLabel mb={1} fontSize="sm" fontWeight="medium" color="gray.700" dir="rtl">
{label}
</FormLabel>
)}
<Controller
name={name}
control={control}
defaultValue={defaultValue}
rules={isRequired ? { required: `${label} الزامی است` } : {}}
render={({ field }) => (
<Select
{...field}
value={field.value || ""}
onChange={(e) => field.onChange(e.target.value)}
size="md"
dir="rtl"
borderRadius="md"
_focus={{ borderColor: 'teal.500', boxShadow: '0 0 0 1px teal.500' }}
>
<option value="">{placeholder}</option>
{options.map(opt => (
<option key={opt.value} value={opt.value}>
{opt.label}
</option>
))}
</Select>
)}
/>
{error && (
<FormErrorMessage dir="rtl" fontSize="xs" color="red.500">
{error}
</FormErrorMessage>
)}
</FormControl>
</Box>
)
// کامپوننت Radio Group
const FormRadioGroup = ({
label,
options,
name,
control,
error
}) => (
<Box>
<FormControl isInvalid={!!error}>
{label && (
<FormLabel mb={2} fontSize="sm" fontWeight="medium" color="gray.700" dir="rtl">
{label}
</FormLabel>
)}
<Controller
name={name}
control={control}
defaultValue="1"
render={({ field }) => (
<RadioGroup {...field} value={field.value} onChange={field.onChange}>
<Stack direction="row" spacing={6} dir="rtl">
{options.map(opt => (
<Radio
key={opt.value}
value={opt.value}
colorScheme="teal"
dir="rtl"
>
{opt.label}
</Radio>
))}
</Stack>
</RadioGroup>
)}
/>
{error && (
<FormErrorMessage dir="rtl" fontSize="xs" color="red.500">
{error}
</FormErrorMessage>
)}
</FormControl>
</Box>
)
// آپشن‌های عددی
const numberOptions = (min, max) => {
const options = []
for (let i = min; i <= max; i++) {
options.push({ value: String(i), label: String(i) })
}
return options
}
const PersonData = () => {
const { setPage, personData, setpersondata } = useContext(HandleFormReq)
const [sex, setSex] = useState(personData.gender || '1')
const [health, setHealth] = useState(personData.health || '1')
const [isSubmitting, setIsSubmitting] = useState(false)
const {
register,
handleSubmit,
control,
watch,
setValue,
formState: { errors }
} = useForm({
defaultValues: {
firstname: personData.firstname || '',
lastname: personData.lastname || '',
fatherName: personData.fatherName || '',
wifeJob: personData.wifeJob || '',
childIndex: personData.childIndex ?? 0,
dependants: personData.dependants ?? 0,
Insurance: personData.Insurance || '',
national_code: personData.national_code || '',
placenational: personData.placenational || '',
phone: personData.phone || '',
mobile_number: personData.mobile_number || '',
birthday: personData.birthday || '',
gender: personData.gender || '1',
degree: personData.degree || '',
marital: personData.marital || '',
military: personData.military || '',
health: personData.health || '1',
healthreason: personData.healthreason || '',
address: personData.address || '',
}
})
// گزینه‌ها
const insuranceOptions = [
{ value: '1', label: 'بیمه تامین اجتماعی' },
{ value: '2', label: 'بیمه خدمات درمانی' },
{ value: '3', label: 'بیمه سلامت' },
{ value: '4', label: 'بیمه نیروهای مسلح' },
{ value: '5', label: 'بدون بیمه' },
]
const educationOptions = [
{ value: '0', label: 'زیر دیپلم' },
{ value: '1', label: 'دیپلم' },
{ value: '5', label: 'فوق دیپلم' },
{ value: '2', label: 'لیسانس' },
{ value: '3', label: 'فوق لیسانس' },
{ value: '4', label: 'دکتری' },
]
const maritalOptions = [
{ value: '1', label: 'مجرد' },
{ value: '2', label: 'متاهل' },
]
const militaryOptions = [
{ value: '1', label: 'معافیت' },
{ value: '2', label: 'مشمول' },
{ value: '3', label: 'پایان خدمت' },
]
const healthOptions = [
{ value: '1', label: 'ندارم' },
{ value: '2', label: 'دارم' },
]
const onSubmit = async data => {
setIsSubmitting(true)
try {
const res = await axios.post(`${AppProduction}public/jobrequest/nationalcode`, {
nationalcode: data.national_code
})
if (res.data.status === false) {
alert('کد ملی شما قبلاً ثبت شده است')
} else {
setpersondata(data)
setPage(2)
}
} catch (err) {
console.error('خطا در ارسال فرم:', err)
alert('خطا در ارتباط با سرور. لطفاً دوباره تلاش کنید.')
} finally {
setIsSubmitting(false)
}
}
return (
<div className="p-4">
<div className="card shadow-sm">
<div className="card-header bg-teal text-white py-3">
<h5 className="mb-0 text-center">اطلاعات فردی</h5>
</div>
<form onSubmit={handleSubmit(onSubmit)} id={'persondataform'}>
<div className="card-body">
{/* ===== بخش اطلاعات شخصی ===== */}
<div className="mb-4">
<h6 className="text-muted mb-3 border-bottom pb-2" dir="rtl">
<FiUser className="me-2" />
اطلاعات شخصی
</h6>
<SimpleGrid columns={{ base: 1, md: 2, lg: 4 }} spacing={4} dir="rtl">
<FormField
label="نام"
icon={FiUser}
error={errors.firstname?.message}
isRequired
>
<Input
id='firstname'
{...register('firstname', { required: 'وارد کردن نام اجباری است' })}
className={'bk-med rounded-0 rounded-end'}
dir={'rtl'}
type='text'
placeholder='نام'
h="40px"
/>
</FormField>
<FormField
label="نام خانوادگی"
icon={FiUser}
error={errors.lastname?.message}
isRequired
>
<Input
id='lastname'
{...register('lastname', { required: 'وارد کردن نام خانوادگی اجباری است' })}
className={'bk-med rounded-0 rounded-end'}
dir={'rtl'}
type='text'
placeholder='نام خانوادگی'
h="40px"
/>
</FormField>
<FormField
label="نام پدر"
icon={FiUser}
error={errors.fatherName?.message}
isRequired
>
<Input
id='fatherName'
{...register('fatherName', { required: 'وارد کردن نام پدر اجباری است' })}
className={'bk-med rounded-0 rounded-end'}
dir={'rtl'}
type='text'
placeholder='نام پدر'
h="40px"
/>
</FormField>
<FormField
label="سال تولد"
icon={FiCalendar}
error={errors.birthday?.message}
isRequired
helpText="مثال: ۱۳۷۰"
>
<Input
id='birthday'
{...register('birthday', {
required: 'وارد کردن تاریخ تولد اجباری است',
min: { value: 1330, message: 'سال تولد معتبر نیست' },
max: { value: 1390, message: 'سال تولد معتبر نیست' }
})}
className={'bk-med rounded-0 rounded-end'}
dir={'rtl'}
type='number'
min={1330}
max={1390}
placeholder='سال تولد'
h="40px"
/>
</FormField>
</SimpleGrid>
</div>
{/* ===== بخش شناسایی ===== */}
<div className="mb-4">
<h6 className="text-muted mb-3 border-bottom pb-2" dir="rtl">
<FiUser className="me-2" />
اطلاعات شناسایی
</h6>
<SimpleGrid columns={{ base: 1, md: 2, lg: 4 }} spacing={4} dir="rtl">
<FormField
label="کد ملی"
icon={FiUser}
error={errors.national_code?.message}
isRequired
helpText="۱۰ رقم"
>
<Input
id='national_code'
{...register('national_code', {
required: 'وارد کردن کد ملی اجباری است',
pattern: { value: /^\d{10}$/, message: 'کد ملی باید ۱۰ رقم باشد' },
})}
className={'bk-med rounded-0 rounded-end'}
dir={'rtl'}
type='text'
inputMode='numeric'
maxLength={10}
placeholder='کد ملی'
h="40px"
/>
</FormField>
<FormField
label="محل صدور شناسنامه"
icon={FiUser}
error={errors.placenational?.message}
isRequired
>
<Input
id='placenational'
{...register('placenational', { required: 'وارد کردن محل صدور شناسنامه اجباری است' })}
className={'bk-med rounded-0 rounded-end'}
dir={'rtl'}
type='text'
placeholder='محل صدور'
h="40px"
/>
</FormField>
<FormField
label="تلفن ثابت"
icon={FiPhone}
error={errors.phone?.message}
isRequired
helpText="با پیش‌شماره شهر"
>
<Input
id='phone'
{...register('phone', {
required: 'وارد کردن تلفن ثابت اجباری است',
pattern: { value: /^0\d{9,10}$/, message: 'شماره تلفن معتبر نیست' }
})}
className={'bk-med rounded-0 rounded-end'}
dir={'rtl'}
type='tel'
inputMode='tel'
placeholder='۰۲۱-۱۲۳۴۵۶۷۸'
h="40px"
/>
</FormField>
<FormField
label="شماره موبایل"
icon={FiPhone}
error={errors.mobile_number?.message}
isRequired
helpText="۱۱ رقم، با ۰۹ شروع شود"
>
<Input
id='mobile_number'
{...register('mobile_number', {
required: 'وارد کردن شماره موبایل اجباری است',
pattern: { value: /^09\d{9}$/, message: 'شماره موبایل معتبر نیست' },
})}
className={'bk-med rounded-0 rounded-end'}
dir={'rtl'}
type='tel'
inputMode='tel'
maxLength={11}
placeholder='۰۹۱۲۳۴۵۶۷۸۹'
h="40px"
/>
</FormField>
</SimpleGrid>
</div>
{/* ===== بخش وضعیت ===== */}
<div className="mb-4">
<h6 className="text-muted mb-3 border-bottom pb-2" dir="rtl">
<FiHeart className="me-2" />
وضعیت شخصی
</h6>
<SimpleGrid columns={{ base: 1, md: 2, lg: 4 }} spacing={4} dir="rtl">
{/* جنسیت - با useState مدیریت می‌شود */}
<FormField
label="جنسیت"
error={errors.gender?.message}
isRequired
>
<Select
{...register('gender')}
value={sex}
onChange={(e) => {
register('gender').onChange(e)
setSex(e.target.value)
}}
size="md"
dir="rtl"
borderRadius="md"
h="40px"
>
<option value="1">مرد</option>
<option value="2">زن</option>
</Select>
</FormField>
<FormSelect
label="وضعیت تاهل"
name="marital"
control={control}
options={maritalOptions}
error={errors.marital?.message}
isRequired
defaultValue={personData.marital || ''}
/>
<FormSelect
label="آخرین مدرک تحصیلی"
name="degree"
control={control}
options={educationOptions}
error={errors.degree?.message}
isRequired
defaultValue={personData.degree || ''}
/>
<FormSelect
label="وضعیت بیمه"
name="Insurance"
control={control}
options={insuranceOptions}
error={errors.Insurance?.message}
isRequired
defaultValue={personData.Insurance || ''}
/>
</SimpleGrid>
{/* نظام وظیفه - فقط برای مردان */}
{sex === '1' && (
<div className="mt-4">
<FormSelect
label="وضعیت نظام وظیفه"
name="military"
control={control}
options={militaryOptions}
error={errors.military?.message}
isRequired
defaultValue={personData.military || ''}
/>
</div>
)}
</div>
{/* ===== بخش خانواده ===== */}
<div className="mb-4">
<h6 className="text-muted mb-3 border-bottom pb-2" dir="rtl">
<FiHome className="me-2" />
اطلاعات خانوادگی
</h6>
<SimpleGrid columns={{ base: 1, md: 2, lg: 4 }} spacing={4} dir="rtl">
<FormSelect
label="تعداد فرزندان"
name="childIndex"
control={control}
options={numberOptions(0, 20)}
defaultValue={String(personData.childIndex ?? 0)}
/>
<FormSelect
label="تعداد افراد تحت تکفل"
name="dependants"
control={control}
options={numberOptions(0, 15)}
defaultValue={String(personData.dependants ?? 0)}
/>
<FormField
label="شغل همسر"
icon={FiBriefcase}
error={errors.wifeJob?.message}
>
<Input
id='wifeJob'
{...register('wifeJob')}
className={'bk-med rounded-0 rounded-end'}
dir={'rtl'}
type='text'
placeholder='شغل همسر (در صورت متاهل بودن)'
h="40px"
/>
</FormField>
</SimpleGrid>
</div>
{/* ===== بخش سلامت ===== */}
<div className="mb-4">
<h6 className="text-muted mb-3 border-bottom pb-2" dir="rtl">
<FiHeart className="me-2" />
وضعیت سلامت
</h6>
<div className="bg-light rounded p-4">
<FormRadioGroup
label="سابقه بیماری خاص دارید؟"
name="health"
control={control}
options={healthOptions}
error={errors.health?.message}
/>
{health === '2' && (
<div className="mt-4 animate-fade-in">
<FormField
label="نوع بیماری"
icon={FiHeart}
error={errors.healthreason?.message}
isRequired
>
<Input
id='healthreason'
{...register('healthreason', {
required: health === '2' ? 'نوع بیماری را وارد کنید' : false
})}
className={'bk-med rounded-0 rounded-end'}
dir={'rtl'}
type='text'
placeholder='نوع بیماری خود را بنویسید'
h="40px"
/>
</FormField>
</div>
)}
</div>
</div>
{/* ===== آدرس ===== */}
<div className="mb-4">
<h6 className="text-muted mb-3 border-bottom pb-2" dir="rtl">
<FiHome className="me-2" />
آدرس محل سکونت
</h6>
<FormField
label="آدرس کامل"
icon={FiHome}
error={errors.address?.message}
isRequired
>
<Textarea
{...register('address', { required: 'آدرس محل سکونت اجباری است' })}
dir={'rtl'}
placeholder='آدرس کامل محل سکونت خود را وارد کنید (شهر، خیابان، کوچه، پلاک و...'
rows={3}
className={'bk-med rounded-0 rounded-end'}
resize="vertical"
/>
</FormField>
</div>
{/* ===== دکمه ارسال ===== */}
<div className="d-flex justify-content-center gap-3 mt-4">
<MDBBtn
type={'submit'}
size={'lg'}
className={'bg-primary-teal px-5'}
disabled={isSubmitting}
>
{isSubmitting ? (
<>
<span className="spinner-border spinner-border-sm me-2"></span>
در حال ارسال...
</>
) : (
'مرحله بعد'
)}
</MDBBtn>
</div>
</div>
</form>
</div>
<style>{`
.animate-fade-in {
animation: fadeIn 0.3s ease-in-out;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(-10px); }
to { opacity: 1; transform: translateY(0); }
}
.bg-primary-teal {
background-color: #319795 !important;
border: none;
}
.bg-primary-teal:hover {
background-color: #2c7a7b !important;
}
.bg-light {
background-color: #f7fafc !important;
}
.bk-med:focus {
border-color: #319795 !important;
box-shadow: 0 0 0 1px #319795 !important;
}
`}</style>
</div>
)
}
export default PersonData