324 lines
15 KiB
TypeScript
324 lines
15 KiB
TypeScript
import { useState } from "react";
|
||
import toast from "react-hot-toast";
|
||
import Select from "react-select";
|
||
import makeAnimated from "react-select/animated";
|
||
|
||
// فرض میکنیم JobFormData اینجا تعریف شده باشد اگر در فایل دیگری نیست
|
||
interface JobFormData {
|
||
job_title: string;
|
||
seniority_level: string[];
|
||
type_of_co_operation: string[];
|
||
at_least_work_experience: number | null;
|
||
internship: boolean;
|
||
province: string;
|
||
city: string;
|
||
address: string;
|
||
advantage_list: string[]; // این فیلد در این فرم نیست، اما اگر در جای دیگری استفاده میشود، خوب است که باشد
|
||
content: string; // این فیلد در این فرم نیست
|
||
owner_name: string; // این فیلد در این فرم نیست
|
||
}
|
||
|
||
interface SelectOption {
|
||
value: string;
|
||
label: string;
|
||
}
|
||
|
||
// نمونه دادهها
|
||
const seniorityOptions: SelectOption[] = [
|
||
{ value: "entry_level", label: "بدون سابقه (کارآموز)" },
|
||
{ value: "junior", label: "سطح پایین (تا 2 سال)" },
|
||
{ value: "mid_level", label: "نیمه ماهر (2 تا 5 سال)" },
|
||
{ value: "senior", label: "ارشد (5+ سال)" },
|
||
{ value: "lead", label: "سرپرست" },
|
||
];
|
||
const cooperationOptions: SelectOption[] = [
|
||
{ value: "full_time", label: "تمام وقت" },
|
||
{ value: "part_time", label: "پاره وقت" },
|
||
{ value: "contract", label: "پروژهای/قراردادی" },
|
||
{ value: "remote", label: "دورکاری" },
|
||
{ value: "percentage", label: "درصدی" },
|
||
];
|
||
const animatedComponents = makeAnimated();
|
||
|
||
// استایلهای سفارشی برای react-select
|
||
const selectStyles = {
|
||
control: (base: any, { isFocused }: any) => ({
|
||
...base,
|
||
padding: "0.7rem 0.8rem", // کمی padding بیشتر
|
||
borderRadius: "8px",
|
||
border: isFocused ? "1px solid #60a5fa" : "1px solid #d1d5eb", // آبی ملایم در فوکوس
|
||
boxShadow: isFocused ? "0 0 0 2px rgba(96, 165, 250, 0.3)" : "none", // سایه ظریف در فوکوس
|
||
"&:hover": { borderColor: "#93c5fd" }, // آبی روشنتر در هاور
|
||
minHeight: "50px", // ارتفاع بیشتر
|
||
fontSize: "0.95rem", // فونت کمی بزرگتر
|
||
}),
|
||
multiValue: (base: any) => ({
|
||
...base,
|
||
backgroundColor: "#3b82f6", // رنگ اصلی آبی
|
||
borderRadius: "4px",
|
||
color: "white",
|
||
padding: "2px 6px",
|
||
}),
|
||
multiValueLabel: (base: any) => ({
|
||
...base,
|
||
color: "white",
|
||
fontSize: "0.9rem",
|
||
}),
|
||
multiValueRemove: (base: any) => ({
|
||
...base,
|
||
backgroundColor: "#f87171", // رنگ قرمز ملایم برای حذف
|
||
color: "white",
|
||
borderRadius: "4px",
|
||
padding: "4px",
|
||
marginLeft: "6px",
|
||
"&:hover": { backgroundColor: "#ef4444" }, // قرمز تیرهتر در هاور
|
||
}),
|
||
option: (base: any, { isFocused, isSelected }: any) => ({
|
||
...base,
|
||
backgroundColor: isSelected ? "#3b82f6" : (isFocused ? "#e0e7ff" : "white"), // آبی برای انتخاب شده، آبی خیلی روشن برای فوکوس
|
||
color: isSelected ? "white" : "black", // متن سفید در حالت انتخاب شده
|
||
padding: "0.8rem",
|
||
fontSize: "0.9rem",
|
||
cursor: "pointer",
|
||
}),
|
||
placeholder: (base: any) => ({ ...base, color: "#9ca3af", fontSize: "0.95rem" }), // خاکستری تیرهتر برای placeholder
|
||
input: (base: any) => ({ ...base, margin: "0", padding: "0" }),
|
||
valueContainer: (base: any) => ({ ...base, padding: "0 0.5rem" }),
|
||
indicatorSeparator: (base: any) => ({ ...base, display: 'none' }), // حذف جداکننده
|
||
};
|
||
|
||
interface StepOneFormProps {
|
||
formData: JobFormData;
|
||
setFormData: React.Dispatch<React.SetStateAction<JobFormData>>; // اصلاح type
|
||
onNextStep: () => void;
|
||
}
|
||
|
||
const StepOneForm: React.FC<StepOneFormProps> = ({ formData, setFormData, onNextStep }) => {
|
||
const [selectedSeniority, setSelectedSeniority] = useState<SelectOption[]>(
|
||
(formData?.seniority_level?.map((val) => seniorityOptions.find((opt) => opt.value === val)) as SelectOption[])?.filter(Boolean) || [],
|
||
);
|
||
const [selectedCooperation, setSelectedCooperation] = useState<SelectOption[]>(
|
||
(formData?.type_of_co_operation?.map((val) => cooperationOptions.find((opt) => opt.value === val)) as SelectOption[])?.filter(Boolean) || [],
|
||
);
|
||
|
||
// مدیریت تغییرات ورودیهای عادی
|
||
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>) => {
|
||
//@ts-ignore
|
||
const { name, value, type, checked } = e.target;
|
||
setFormData((prev) => ({ ...prev, [name]: type === "checkbox" ? checked : value }));
|
||
};
|
||
|
||
// مدیریت تغییرات سلکتها
|
||
const handleSelectChange = (selectedOptions: any, actionMeta: any) => {
|
||
const name = actionMeta.name;
|
||
if (actionMeta.action === "clear") {
|
||
if (name === "seniority") {
|
||
setSelectedSeniority([]);
|
||
setFormData((prev) => ({ ...prev, seniority_level: [] }));
|
||
} else if (name === "cooperation") {
|
||
setSelectedCooperation([]);
|
||
setFormData((prev) => ({ ...prev, type_of_co_operation: [] }));
|
||
}
|
||
return;
|
||
}
|
||
|
||
const newSelected = selectedOptions as SelectOption[];
|
||
const values = newSelected.map((opt) => opt.value);
|
||
|
||
if (name === "seniority") {
|
||
setSelectedSeniority(newSelected);
|
||
setFormData((prev) => ({ ...prev, seniority_level: values }));
|
||
} else if (name === "cooperation") {
|
||
setSelectedCooperation(newSelected);
|
||
setFormData((prev) => ({ ...prev, type_of_co_operation: values }));
|
||
}
|
||
};
|
||
|
||
// اعتبارسنجی فرم
|
||
const validateStepOne = (): boolean => {
|
||
let isValid = true;
|
||
if (!formData?.job_title?.trim()) {
|
||
toast.error("عنوان شغلی الزامی است.");
|
||
isValid = false;
|
||
}
|
||
if (!formData?.seniority_level || formData?.seniority_level.length === 0) {
|
||
toast.error("حداقل یک رده سازمانی انتخاب کنید.");
|
||
isValid = false;
|
||
}
|
||
if (!formData?.type_of_co_operation || formData?.type_of_co_operation.length === 0) {
|
||
toast.error("حداقل یک نوع همکاری انتخاب کنید.");
|
||
isValid = false;
|
||
}
|
||
const experience = formData?.at_least_work_experience;
|
||
console.log(experience);
|
||
if (experience !== null && typeof experience === "number" && experience < 0) {
|
||
toast.error("سابقه کار باید یک عدد نامنفی باشد.");
|
||
isValid = false;
|
||
}
|
||
// میتونید اعتبارسنجیهای بیشتری اضافه کنید (مثلا برای شهر و استان)
|
||
return isValid;
|
||
};
|
||
|
||
// رفتن به مرحله بعد
|
||
const handleNext = () => {
|
||
if (validateStepOne()) {
|
||
onNextStep();
|
||
}
|
||
};
|
||
|
||
return (
|
||
<div className="bg-white p-8 rounded-xl shadow-lg max-w-4xl mx-auto"> {/* پسزمینه سفید، padding، گرد، سایه و حداکثر عرض */}
|
||
<h2 className="text-3xl font-bold text-gray-800 mb-6 text-center">اطلاعات شغلی</h2> {/* عنوان بزرگ و مرکز */}
|
||
|
||
{/* عنوان شغلی */}
|
||
<div className="mb-6">
|
||
<label htmlFor="job_title" className="block text-lg font-semibold text-gray-700 mb-2"> {/* عنوان بولد و با فاصله */}
|
||
عنوان شغلی <span className="text-red-500">*</span>
|
||
</label>
|
||
<input
|
||
type="text"
|
||
id="job_title"
|
||
name="job_title"
|
||
value={formData?.job_title || ""}
|
||
onChange={handleInputChange}
|
||
className="w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-400 focus:border-transparent shadow-sm" // استایل جدید ورودی
|
||
placeholder="مثلا: برنامهنویس فرانتاند ارشد"
|
||
/>
|
||
</div>
|
||
|
||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6"> {/* چیدمان دو ستونه */}
|
||
{/* رده سازمانی */}
|
||
<div>
|
||
<label htmlFor="seniority_level" className="block text-lg font-semibold text-gray-700 mb-2">
|
||
رده سازمانی <span className="text-red-500">*</span>
|
||
</label>
|
||
<Select
|
||
id="seniority_level"
|
||
name="seniority" // نام برای actionMeta
|
||
// closeOnSelect={false}
|
||
// components={{ ...animatedComponents, MultiValueRemove: ({ children, ...props }) => <components.MultiValueRemove {...props}><div className="custom-multi-value-remove">{children}</div></components.MultiValueRemove> }} // کامپوننت سفارشی برای حذف
|
||
isMulti
|
||
options={seniorityOptions}
|
||
styles={selectStyles}
|
||
placeholder="انتخاب رده سازمانی..."
|
||
value={selectedSeniority}
|
||
onChange={handleSelectChange}
|
||
/>
|
||
</div>
|
||
|
||
{/* نوع همکاری */}
|
||
<div>
|
||
<label htmlFor="type_of_co_operation" className="block text-lg font-semibold text-gray-700 mb-2">
|
||
نوع همکاری <span className="text-red-500">*</span>
|
||
</label>
|
||
<Select
|
||
id="type_of_co_operation"
|
||
name="cooperation" // نام برای actionMeta
|
||
// closeOnSelect={false}
|
||
components={animatedComponents}
|
||
isMulti
|
||
options={cooperationOptions}
|
||
styles={selectStyles}
|
||
placeholder="انتخاب نوع همکاری..."
|
||
value={selectedCooperation}
|
||
onChange={handleSelectChange}
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6">
|
||
{/* حداقل سابقه کار */}
|
||
<div>
|
||
<label htmlFor="at_least_work_experience" className="block text-lg font-semibold text-gray-700 mb-2">
|
||
حداقل سابقه کار (سال)
|
||
</label>
|
||
<input
|
||
type="number"
|
||
id="at_least_work_experience"
|
||
name="at_least_work_experience"
|
||
value={formData?.at_least_work_experience ?? ""} // استفاده از ?? برای نمایش خالی به جای 0
|
||
onChange={handleInputChange}
|
||
min="0" // تضمین عدد نامنفی
|
||
className="w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-400 focus:border-transparent shadow-sm"
|
||
placeholder="مثلا: 3"
|
||
/>
|
||
</div>
|
||
|
||
{/* استان و شهر (در صورت نیاز به فیلدهای جداگانه) */}
|
||
{/* این قسمت رو میشه بهتر کرد، مثلا با استفاده از کامپوننتهای مخصوص شهر و استان */}
|
||
<div>
|
||
<label htmlFor="province" className="block text-lg font-semibold text-gray-700 mb-2">
|
||
استان
|
||
</label>
|
||
<input
|
||
type="text"
|
||
id="province"
|
||
name="province"
|
||
value={formData?.province || ""}
|
||
onChange={handleInputChange}
|
||
className="w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-400 focus:border-transparent shadow-sm"
|
||
placeholder="مثلا: تهران"
|
||
/>
|
||
</div>
|
||
<div>
|
||
<label htmlFor="city" className="block text-lg font-semibold text-gray-700 mb-2">
|
||
شهر
|
||
</label>
|
||
<input
|
||
type="text"
|
||
id="city"
|
||
name="city"
|
||
value={formData?.city || ""}
|
||
onChange={handleInputChange}
|
||
className="w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-400 focus:border-transparent shadow-sm"
|
||
placeholder="مثلا: تهران"
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
{/* آدرس */}
|
||
<div className="mb-6">
|
||
<label htmlFor="address" className="block text-lg font-semibold text-gray-700 mb-2">
|
||
آدرس دقیق
|
||
</label>
|
||
<textarea
|
||
id="address"
|
||
name="address"
|
||
value={formData?.address || ""}
|
||
onChange={handleInputChange}
|
||
rows={3} // تعداد خطوط پیشفرض
|
||
className="w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-400 focus:border-transparent shadow-sm resize-y" // قابلیت تغییر اندازه عمودی
|
||
placeholder="خیابان، کوچه، پلاک..."
|
||
/>
|
||
</div>
|
||
|
||
{/* چک باکس کارآموز */}
|
||
<div className="mb-6 flex items-center">
|
||
<input
|
||
type="checkbox"
|
||
id="internship"
|
||
name="internship"
|
||
checked={formData?.internship || false}
|
||
onChange={handleInputChange}
|
||
className="h-5 w-5 text-blue-600 border-gray-300 rounded focus:ring-blue-500 ml-3" // استایل چکباکس
|
||
/>
|
||
<label htmlFor="internship" className="text-lg text-gray-700">
|
||
امکان جذب کارآموز
|
||
</label>
|
||
</div>
|
||
|
||
{/* دکمه مرحله بعد */}
|
||
<div className="flex justify-end mt-8"> {/* چیدمان راست */}
|
||
<button
|
||
onClick={handleNext}
|
||
className="bg-[#AA00FF] hover:bg-[#E6B4FF] text-white font-bold py-3 px-8 rounded-lg shadow-md hover:shadow-lg transition duration-300 ease-in-out focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2" // استایل دکمه
|
||
>
|
||
مرحله بعد
|
||
</button>
|
||
</div>
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default StepOneForm;
|