Files
salona/src/apps/new-ui/pages/Jobs/create-job-position/StepOneForm.tsx
2026-03-03 18:11:08 +03:30

324 lines
15 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 { 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;