"use client"; import React, { useState, useRef } from 'react'; import { apiClient } from '@/app/utils/apiClient'; import { Box, TextField, Button, Typography, Alert, Grid, CircularProgress, Paper } from '@mui/material'; import CloudUploadIcon from '@mui/icons-material/CloudUpload'; import CheckCircleIcon from '@mui/icons-material/CheckCircle'; interface JobApplicationFormProps { jobId: number; jobTitle: string; onApplicationSuccess: () => void; } const JobApplicationForm: React.FC = ({ jobId, jobTitle, onApplicationSuccess }) => { const [formData, setFormData] = useState({ name: '', email: '', phone: '' }); const [resumeFile, setResumeFile] = useState(null); const [coverLetterFile, setCoverLetterFile] = useState(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [fieldErrors, setFieldErrors] = useState>({}); const resumeInputRef = useRef(null); const coverLetterInputRef = useRef(null); const handleInputChange = (e: React.ChangeEvent) => { const { name, value } = e.target; setFormData(prev => ({ ...prev, [name]: value })); // Clear field error when user types if (fieldErrors[name]) { setFieldErrors(prev => { const newErrors = { ...prev }; delete newErrors[name]; return newErrors; }); } }; const handleResumeChange = (e: React.ChangeEvent) => { const files = e.target.files; if (files && files.length > 0) { const file = files[0]; // Check file size (10MB max) if (file.size > 10 * 1024 * 1024) { setFieldErrors(prev => ({ ...prev, resume: 'File size exceeds 10MB limit' })); return; } // Check file type const fileExtension = file.name.split('.').pop()?.toLowerCase(); const allowedExtensions = ['pdf', 'doc', 'docx']; if (!fileExtension || !allowedExtensions.includes(fileExtension)) { setFieldErrors(prev => ({ ...prev, resume: 'Only PDF, DOC, and DOCX files are allowed' })); return; } setResumeFile(file); // Clear error if exists if (fieldErrors.resume) { setFieldErrors(prev => { const newErrors = { ...prev }; delete newErrors.resume; return newErrors; }); } } }; const handleCoverLetterChange = (e: React.ChangeEvent) => { const files = e.target.files; if (files && files.length > 0) { const file = files[0]; // Check file size (10MB max) if (file.size > 10 * 1024 * 1024) { setFieldErrors(prev => ({ ...prev, coverLetter: 'File size exceeds 10MB limit' })); return; } // Check file type const fileExtension = file.name.split('.').pop()?.toLowerCase(); const allowedExtensions = ['pdf', 'doc', 'docx']; if (!fileExtension || !allowedExtensions.includes(fileExtension)) { setFieldErrors(prev => ({ ...prev, coverLetter: 'Only PDF, DOC, and DOCX files are allowed' })); return; } setCoverLetterFile(file); // Clear error if exists if (fieldErrors.coverLetter) { setFieldErrors(prev => { const newErrors = { ...prev }; delete newErrors.coverLetter; return newErrors; }); } } }; const validate = (): boolean => { const newErrors: Record = {}; // Validate name if (!formData.name.trim()) { newErrors.name = 'Name is required'; } else if (formData.name.length < 2) { newErrors.name = 'Name must be at least 2 characters'; } // Validate email const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (!formData.email.trim()) { newErrors.email = 'Email is required'; } else if (!emailRegex.test(formData.email)) { newErrors.email = 'Please enter a valid email address'; } // Validate phone const phoneRegex = /^\+?[0-9\s\-()]{8,20}$/; if (!formData.phone.trim()) { newErrors.phone = 'Phone number is required'; } else if (!phoneRegex.test(formData.phone)) { newErrors.phone = 'Please enter a valid phone number'; } // Validate resume if (!resumeFile) { newErrors.resume = 'Resume is required'; } setFieldErrors(newErrors); return Object.keys(newErrors).length === 0; }; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!validate()) return; setLoading(true); setError(null); try { const formSubmitData = new FormData(); formSubmitData.append('name', formData.name.trim()); formSubmitData.append('email', formData.email.trim()); formSubmitData.append('phone', formData.phone.trim()); // Add files with debugging if (resumeFile) { formSubmitData.append('resume', resumeFile); } if (coverLetterFile) { formSubmitData.append('cover_letter', coverLetterFile); } // Make API request without the custom header that's causing CORS issues const response = await apiClient(`/careers/apply/${jobId}`, { method: 'POST', body: formSubmitData, skipAuth: true, // Remove the X-Debug-Info header causing CORS issues }); if (response.success) { onApplicationSuccess(); } else { // Handle validation errors from the server if (response.errors && Array.isArray(response.errors)) { const serverFieldErrors: Record = {}; response.errors.forEach((err: any) => { const field = err.param || 'form'; serverFieldErrors[field] = err.msg || err.message; }); setFieldErrors(serverFieldErrors); setError('Please correct the errors in the form'); } else { setError(response.message || 'Failed to submit application'); } } } catch (err: any) { console.error('Error submitting application:', err); setError(err.message || 'An error occurred while submitting your application'); } finally { setLoading(false); } }; return ( {error && ( {error} )} Applying for: {jobTitle} Upload Documents resumeInputRef.current?.click()} > {resumeFile ? ( <> {resumeFile.name} Click to change file ) : ( <> Upload Resume* PDF, DOC, DOCX (Max 10MB) )} {fieldErrors.resume && ( {fieldErrors.resume} )} coverLetterInputRef.current?.click()} > {coverLetterFile ? ( <> {coverLetterFile.name} Click to change file ) : ( <> Upload Cover Letter (Optional) PDF, DOC, DOCX (Max 10MB) )} {fieldErrors.coverLetter && ( {fieldErrors.coverLetter} )} ); }; export default JobApplicationForm;