254 lines
10 KiB
TypeScript
254 lines
10 KiB
TypeScript
import DefaulModal from '@/components/DefaulModal'
|
||
import { Button } from '@/components/ui/button'
|
||
import { useState, type FC, useEffect } from 'react'
|
||
import { useForm } from 'react-hook-form'
|
||
import { ResumeType } from '../types/Types'
|
||
import { useGetJobs } from '../hooks/useJobsData'
|
||
import { useMutation } from '@tanstack/react-query'
|
||
import * as api from '../service/JobsService'
|
||
import { toast } from '@/components/Toast'
|
||
import Input from '@/components/Input'
|
||
import DatePickerComponent from '@/components/DatePicker'
|
||
import Select from '@/components/Select'
|
||
import { DocumentUpload, CloseCircle } from 'iconsax-react'
|
||
import { extractErrorMessage } from '@/helpers/errorUtils'
|
||
|
||
interface ResumeProps {
|
||
jobId?: string
|
||
}
|
||
|
||
const Resume: FC<ResumeProps> = ({ jobId }) => {
|
||
const [open, setOpen] = useState<boolean>(false)
|
||
const [selectedFile, setSelectedFile] = useState<File | null>(null)
|
||
const [filePreview, setFilePreview] = useState<string>('')
|
||
|
||
const { data: jobsData } = useGetJobs()
|
||
|
||
const { register, handleSubmit, formState: { errors }, setValue, watch, reset } = useForm<ResumeType>()
|
||
|
||
const jobs = jobsData?.results?.jobs || []
|
||
|
||
const jobOptions = jobs.map(job => ({
|
||
label: job.title,
|
||
value: job._id
|
||
}))
|
||
|
||
const watchedJob = watch('job')
|
||
|
||
useEffect(() => {
|
||
if (jobId) {
|
||
setValue('job', jobId)
|
||
}
|
||
}, [jobId, setValue])
|
||
|
||
const uploadFileMutation = useMutation({
|
||
mutationFn: api.uploadSingleFile,
|
||
onSuccess: (data) => {
|
||
setValue('resumeUrl', data?.results?.url?.url)
|
||
toast('فایل با موفقیت آپلود شد', 'success')
|
||
},
|
||
onError: () => {
|
||
toast('خطا در آپلود فایل', 'error')
|
||
setSelectedFile(null)
|
||
setFilePreview('')
|
||
}
|
||
})
|
||
|
||
const sendResumeMutation = useMutation({
|
||
mutationFn: api.sendResume,
|
||
onSuccess: () => {
|
||
toast('رزومه شما با موفقیت ارسال شد', 'success')
|
||
setOpen(false)
|
||
reset()
|
||
setSelectedFile(null)
|
||
setFilePreview('')
|
||
},
|
||
onError: (error) => {
|
||
toast(extractErrorMessage(error), 'error')
|
||
}
|
||
})
|
||
|
||
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||
const file = event.target.files?.[0]
|
||
if (file) {
|
||
// بررسی نوع فایل
|
||
if (!file.type.includes('pdf') && !file.type.includes('word') && !file.type.includes('document')) {
|
||
toast('لطفا فایل PDF یا Word انتخاب کنید', 'error')
|
||
return
|
||
}
|
||
|
||
// بررسی اندازه فایل (حداکثر 5MB)
|
||
if (file.size > 5 * 1024 * 1024) {
|
||
toast('حجم فایل نباید بیشتر از ۵ مگابایت باشد', 'error')
|
||
return
|
||
}
|
||
|
||
setSelectedFile(file)
|
||
setFilePreview(file.name)
|
||
// آپلود فایل
|
||
uploadFileMutation.mutate(file)
|
||
}
|
||
}
|
||
|
||
const removeFile = () => {
|
||
setSelectedFile(null)
|
||
setFilePreview('')
|
||
setValue('resumeUrl', '')
|
||
}
|
||
|
||
const onSubmit = (data: ResumeType) => {
|
||
if (!selectedFile) {
|
||
toast('لطفا فایل رزومه را انتخاب کنید', 'error')
|
||
return
|
||
}
|
||
|
||
if (!data.resumeUrl) {
|
||
toast('لطفا منتظر تکمیل آپلود فایل باشید', 'error')
|
||
return
|
||
}
|
||
|
||
sendResumeMutation.mutate(data)
|
||
}
|
||
|
||
return (
|
||
<div>
|
||
<Button onClick={() => setOpen(!open)} size={'sm'}>
|
||
ارسال رزومه
|
||
</Button>
|
||
|
||
<DefaulModal
|
||
open={open}
|
||
close={() => setOpen(false)}
|
||
title_header='ارسال رزومه'
|
||
isHeader
|
||
>
|
||
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
|
||
{/* انتخاب شغل */}
|
||
<Select
|
||
placeholder="انتخاب شغل"
|
||
items={jobOptions}
|
||
value={watchedJob}
|
||
name="job"
|
||
onChange={(e) => setValue('job', e.target.value)}
|
||
error_text={errors.job?.message}
|
||
disabled={!!jobId}
|
||
/>
|
||
|
||
{/* نام کامل */}
|
||
<Input
|
||
label="نام کامل"
|
||
placeholder="نام کامل خود را وارد کنید"
|
||
{...register('fullName', { required: 'نام کامل الزامی است' })}
|
||
error_text={errors.fullName?.message}
|
||
/>
|
||
|
||
{/* نام پدر */}
|
||
<Input
|
||
label="نام پدر"
|
||
placeholder="نام پدر خود را وارد کنید"
|
||
{...register('fatherName', { required: 'نام پدر الزامی است' })}
|
||
error_text={errors.fatherName?.message}
|
||
/>
|
||
|
||
{/* محل تولد */}
|
||
<Input
|
||
label="محل تولد"
|
||
placeholder="محل تولد خود را وارد کنید"
|
||
{...register('placeOfBirth', { required: 'محل تولد الزامی است' })}
|
||
error_text={errors.placeOfBirth?.message}
|
||
/>
|
||
|
||
{/* تاریخ تولد */}
|
||
<DatePickerComponent
|
||
label="تاریخ تولد"
|
||
placeholder="تاریخ تولد را انتخاب کنید"
|
||
onChange={(date) => setValue('birthday', date)}
|
||
error_text={errors.birthday?.message}
|
||
/>
|
||
|
||
{/* آپلود فایل رزومه */}
|
||
<div className="w-full">
|
||
<label className="text-sm text-black block mb-2">
|
||
فایل رزومه (PDF یا Word)
|
||
</label>
|
||
|
||
{!selectedFile ? (
|
||
<div className="relative">
|
||
<input
|
||
type="file"
|
||
accept=".pdf,.doc,.docx"
|
||
onChange={handleFileChange}
|
||
className="absolute inset-0 w-full h-full opacity-0 cursor-pointer"
|
||
id="resume-file"
|
||
disabled={uploadFileMutation.isPending}
|
||
/>
|
||
<label
|
||
htmlFor="resume-file"
|
||
className="flex items-center justify-center w-full h-32 border-2 border-dashed border-gray-300 rounded-xl cursor-pointer hover:border-gray-400 transition-colors"
|
||
>
|
||
<div className="text-center">
|
||
<DocumentUpload color='gray' size={32} className="mx-auto text-gray-400 mb-2" />
|
||
<p className="text-sm text-gray-600">
|
||
فایل رزومه خود را اینجا رها کنید یا کلیک کنید
|
||
</p>
|
||
<p className="text-xs text-gray-400 mt-1">
|
||
PDF یا Word (حداکثر ۵ مگابایت)
|
||
</p>
|
||
</div>
|
||
</label>
|
||
</div>
|
||
) : (
|
||
<div className="flex items-center justify-between p-3 bg-gray-50 rounded-xl border">
|
||
<div className="flex items-center gap-2">
|
||
<DocumentUpload color='gray' size={20} className="text-gray-600" />
|
||
<span className="text-sm text-gray-700 truncate">
|
||
{filePreview}
|
||
</span>
|
||
{uploadFileMutation.isPending && (
|
||
<span className="text-xs text-blue-600">در حال آپلود...</span>
|
||
)}
|
||
</div>
|
||
<button
|
||
type="button"
|
||
onClick={removeFile}
|
||
disabled={uploadFileMutation.isPending}
|
||
className="text-red-500 hover:text-red-700 disabled:opacity-50"
|
||
>
|
||
<CloseCircle color='red' size={20} />
|
||
</button>
|
||
</div>
|
||
)}
|
||
|
||
{errors.resumeUrl && (
|
||
<div className="text-xs text-red-500 mt-1">
|
||
{errors.resumeUrl.message}
|
||
</div>
|
||
)}
|
||
</div>
|
||
|
||
{/* دکمههای اقدام */}
|
||
<div className="flex gap-3 pt-4">
|
||
<Button
|
||
type="button"
|
||
variant="outline"
|
||
onClick={() => setOpen(false)}
|
||
className="flex-1"
|
||
>
|
||
انصراف
|
||
</Button>
|
||
<Button
|
||
type="submit"
|
||
disabled={sendResumeMutation.isPending || uploadFileMutation.isPending || !watch('resumeUrl')}
|
||
className="flex-1"
|
||
>
|
||
{sendResumeMutation.isPending ? 'در حال ارسال...' :
|
||
uploadFileMutation.isPending ? 'در حال آپلود فایل...' : 'ارسال رزومه'}
|
||
</Button>
|
||
</div>
|
||
</form>
|
||
</DefaulModal>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
export default Resume |