687 lines
24 KiB
JavaScript
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 |