"use client"; import { useState } from 'react'; import { z } from 'zod'; import axios from 'axios'; // Form validation schema const createJobSchema = z.object({ title: z.string().min(1, 'Job title is required').max(255, 'Title too long'), description: z.string().min(1, 'Job description is required'), requirements: z.string().min(1, 'Job requirements are required'), skills_required: z.array(z.string()).min(1, 'At least one skill is required'), location: z.string().optional(), employment_type: z.enum(['full_time', 'part_time', 'contract', 'internship']).default('full_time'), experience_level: z.enum(['entry', 'mid', 'senior', 'lead', 'executive']).default('mid'), salary_min: z.number().min(0).optional(), salary_max: z.number().min(0).optional(), currency: z.string().length(3).default('USD'), application_deadline: z.string().optional(), icon: z.string().optional(), }); type CreateJobFormData = z.infer; interface CreateJobModalProps { isOpen: boolean; onClose: () => void; onSubmit: (jobData: CreateJobFormData) => void; } export default function CreateJobModal({ isOpen, onClose, onSubmit }: CreateJobModalProps) { const [formData, setFormData] = useState({ title: '', description: '', requirements: '', skills_required: [], location: '', employment_type: 'full_time', experience_level: 'mid', salary_min: undefined, salary_max: undefined, currency: 'USD', application_deadline: '', icon: 'briefcase', }); const [errors, setErrors] = useState>({}); const [skillInput, setSkillInput] = useState(''); const [isSubmitting, setIsSubmitting] = useState(false); const [currentStep, setCurrentStep] = useState(1); const [shakeAnimation, setShakeAnimation] = useState(false); const totalSteps = 4; // Available job icons const jobIcons = [ { id: 'briefcase', name: 'Briefcase', emoji: '💼' }, { id: 'code', name: 'Code', emoji: '💻' }, { id: 'chart', name: 'Analytics', emoji: '📊' }, { id: 'design', name: 'Design', emoji: '🎨' }, { id: 'marketing', name: 'Marketing', emoji: '📈' }, { id: 'sales', name: 'Sales', emoji: '💼' }, { id: 'support', name: 'Support', emoji: '🎧' }, { id: 'engineering', name: 'Engineering', emoji: '⚙️' }, { id: 'data', name: 'Data', emoji: '📊' }, { id: 'security', name: 'Security', emoji: '🔒' }, { id: 'mobile', name: 'Mobile', emoji: '📱' }, { id: 'cloud', name: 'Cloud', emoji: '☁️' }, ]; const handleInputChange = (field: keyof CreateJobFormData, value: any) => { setFormData(prev => ({ ...prev, [field]: value })); // Clear error when user starts typing if (errors[field]) { setErrors(prev => ({ ...prev, [field]: '' })); } // Clear shake animation when user starts typing if (shakeAnimation) { setShakeAnimation(false); } }; const handleAddSkill = () => { if (skillInput.trim() && !formData.skills_required.includes(skillInput.trim())) { setFormData(prev => ({ ...prev, skills_required: [...prev.skills_required, skillInput.trim()] })); setSkillInput(''); } }; const handleRemoveSkill = (skillToRemove: string) => { setFormData(prev => ({ ...prev, skills_required: prev.skills_required.filter(skill => skill !== skillToRemove) })); }; const validateCurrentStep = () => { const newErrors: Record = {}; if (currentStep === 1) { // Validate Step 1: Basic Information if (!formData.title.trim()) { newErrors.title = 'Job title is required'; } // Other fields in step 1 are optional } else if (currentStep === 2) { // Validate Step 2: Job Details if (formData.skills_required.length === 0) { newErrors.skills_required = 'At least one skill is required'; } if (!formData.description.trim()) { newErrors.description = 'Job description is required'; } if (!formData.requirements.trim()) { newErrors.requirements = 'Job requirements are required'; } } else if (currentStep === 3) { // Validate Step 3: Icon Selection if (!formData.icon) { newErrors.icon = 'Please select an icon for this job'; } } setErrors(newErrors); return Object.keys(newErrors).length === 0; }; const nextStep = () => { if (validateCurrentStep() && currentStep < totalSteps) { setCurrentStep(currentStep + 1); } else { // Trigger shake animation when validation fails setShakeAnimation(true); setTimeout(() => setShakeAnimation(false), 500); } }; const prevStep = () => { if (currentStep > 1) { setCurrentStep(currentStep - 1); } }; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); console.log('Form submitted, current step:', currentStep, 'total steps:', totalSteps); // Only create job if we're on the final step (review) if (currentStep === totalSteps) { console.log('Creating job on final step'); await createJob(); } else { console.log('Not on final step, just validating'); // Just validate and move to next step if (validateCurrentStep()) { nextStep(); } } }; const createJob = async () => { setIsSubmitting(true); try { console.log('Creating job with form data:', formData); const validatedData = createJobSchema.parse(formData); console.log('Validated data:', validatedData); // Call the API to create the job const token = localStorage.getItem("token"); console.log('Sending request to backend...'); const response = await axios.post(`${process.env.NEXT_PUBLIC_API_URL}/rest/jobs`, validatedData, { headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' } }); console.log('Job created successfully:', response.data); // Call the parent onSubmit callback onSubmit(validatedData); // Reset form setFormData({ title: '', description: '', requirements: '', skills_required: [], location: '', employment_type: 'full_time', experience_level: 'mid', salary_min: undefined, salary_max: undefined, currency: 'USD', application_deadline: '', }); setErrors({}); setCurrentStep(1); onClose(); } catch (error) { if (error instanceof z.ZodError) { const fieldErrors: Record = {}; error.issues.forEach((err) => { if (err.path[0]) { fieldErrors[err.path[0] as string] = err.message; } }); setErrors(fieldErrors); } else { console.error('Error creating job:', error); // Handle API errors if ((error as any).response) { // Server responded with error status console.error('API Error Response:', (error as any).response.data); console.error('API Error Status:', (error as any).response.status); console.error('API Error Headers:', (error as any).response.headers); // Show user-friendly error message alert(`Failed to create job: ${(error as any).response.data?.message || (error as any).response.statusText || 'Unknown error'}`); } else if ((error as any).request) { // Request was made but no response received console.error('API Error Request:', (error as any).request); alert('Failed to create job: No response from server. Please check if the backend is running.'); } else { // Something else happened console.error('API Error:', (error as any).message); alert(`Failed to create job: ${(error as any).message}`); } } } finally { setIsSubmitting(false); } }; if (!isOpen) return null; return (
{/* Header with Progress */}

Create New Job

Step {currentStep} of {totalSteps}

{/* Progress Bar */}
{/* Step Indicators */}
{[1, 2, 3, 4].map((step) => { const hasErrors = (step === 1 && errors.title) || (step === 2 && (errors.skills_required || errors.description || errors.requirements)) || (step === 3 && errors.icon); return ( //step 3 shall be called "Icon" and step 4 shall be called "Review"
{hasErrors ? ( ) : ( step )}
{step === 1 ? 'Basic Info' : step === 2 ? 'Details' : step === 3 ? 'Icon' : 'Review'}
); })}
{/* Form Content */}
{/* Validation Error Summary */} {shakeAnimation && Object.keys(errors).length > 0 && (

Please complete the required fields:

    {errors.title &&
  • • Job title is required
  • } {errors.skills_required &&
  • • At least one skill is required
  • } {errors.description &&
  • • Job description is required
  • } {errors.requirements &&
  • • Job requirements are required
  • }
)}
{/* Step 1: Basic Information */} {currentStep === 1 && (

Basic Information

Let's start with the essential details about this position

handleInputChange('title', e.target.value)} className={`w-full px-4 py-3 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white transition-colors ${ errors.title ? 'border-red-500 bg-red-50 dark:bg-red-900/20 ring-2 ring-red-200 dark:ring-red-800' : 'border-gray-300' }`} placeholder="e.g. Senior Frontend Developer" /> {errors.title &&

{errors.title}

}
handleInputChange('location', e.target.value)} className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white" placeholder="e.g. New York, NY or Remote" />
handleInputChange('application_deadline', e.target.value)} className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white" />
)} {/* Step 2: Job Details */} {currentStep === 2 && (

Job Details

Add comprehensive details about the role and compensation

{/* Salary Information */}

Compensation

handleInputChange('salary_min', e.target.value ? Number(e.target.value) : undefined)} className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-600 dark:border-gray-500 dark:text-white" placeholder="50000" />
handleInputChange('salary_max', e.target.value ? Number(e.target.value) : undefined)} className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-600 dark:border-gray-500 dark:text-white" placeholder="80000" />
{/* Skills Required */}
setSkillInput(e.target.value)} onKeyPress={(e) => e.key === 'Enter' && (e.preventDefault(), handleAddSkill())} className={`flex-1 px-4 py-3 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white transition-colors ${ errors.skills_required ? 'border-red-500 ring-2 ring-red-200 dark:ring-red-800' : 'border-gray-300' }`} placeholder="Add a skill and press Enter" />
{errors.skills_required && (
{errors.skills_required}
)}
{formData.skills_required.map((skill, index) => ( {skill} ))}
{/* Job Description */}