974 lines
35 KiB
TypeScript
974 lines
35 KiB
TypeScript
"use client";
|
|
|
|
import { useState, FormEvent, ChangeEvent } from "react";
|
|
import { JobPosition, JobApplication, careerService } from "@/lib/api/careerService";
|
|
|
|
interface JobApplicationFormProps {
|
|
job: JobPosition;
|
|
onClose?: () => void;
|
|
}
|
|
|
|
const inputStyle = {
|
|
padding: '10px 12px',
|
|
borderRadius: '6px',
|
|
border: '1px solid #e0e0e0',
|
|
fontSize: '14px',
|
|
transition: 'all 0.2s',
|
|
width: '100%'
|
|
};
|
|
|
|
const labelStyle = {
|
|
fontWeight: '500',
|
|
color: '#555',
|
|
marginBottom: '6px',
|
|
display: 'block',
|
|
fontSize: '14px'
|
|
};
|
|
|
|
const sectionStyle = {
|
|
backgroundColor: '#ffffff',
|
|
padding: 'clamp(16px, 3vw, 20px)',
|
|
borderRadius: '8px',
|
|
border: '1px solid #e8e8e8',
|
|
marginBottom: '16px',
|
|
boxShadow: '0 2px 8px rgba(0,0,0,0.04)'
|
|
};
|
|
|
|
const sectionHeaderStyle = {
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
marginBottom: '14px',
|
|
paddingBottom: '10px',
|
|
borderBottom: '1px solid #f0f0f0'
|
|
};
|
|
|
|
const JobApplicationForm = ({ job, onClose }: JobApplicationFormProps) => {
|
|
const [formData, setFormData] = useState({
|
|
first_name: "",
|
|
last_name: "",
|
|
email: "",
|
|
phone: "",
|
|
current_position: "",
|
|
current_company: "",
|
|
years_of_experience: "",
|
|
cover_letter: "",
|
|
portfolio_url: "",
|
|
linkedin_url: "",
|
|
github_url: "",
|
|
website_url: "",
|
|
available_from: "",
|
|
notice_period: "",
|
|
expected_salary: "",
|
|
salary_currency: "USD",
|
|
consent: false,
|
|
});
|
|
|
|
const [resume, setResume] = useState<File | null>(null);
|
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
const [submitStatus, setSubmitStatus] = useState<{
|
|
type: "success" | "error" | null;
|
|
message: string;
|
|
}>({ type: null, message: "" });
|
|
|
|
const handleInputChange = (
|
|
e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>
|
|
) => {
|
|
const { name, value, type } = e.target;
|
|
|
|
if (type === "checkbox") {
|
|
const checked = (e.target as HTMLInputElement).checked;
|
|
setFormData((prev) => ({ ...prev, [name]: checked }));
|
|
} else {
|
|
setFormData((prev) => ({ ...prev, [name]: value }));
|
|
}
|
|
};
|
|
|
|
const handleFileChange = (e: ChangeEvent<HTMLInputElement>) => {
|
|
const file = e.target.files?.[0];
|
|
if (file) {
|
|
// Validate file type
|
|
const allowedTypes = [
|
|
"application/pdf",
|
|
"application/msword",
|
|
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
];
|
|
|
|
if (!allowedTypes.includes(file.type)) {
|
|
setSubmitStatus({
|
|
type: "error",
|
|
message: "Please upload a PDF, DOC, or DOCX file",
|
|
});
|
|
return;
|
|
}
|
|
|
|
// Validate file size (5MB)
|
|
if (file.size > 5 * 1024 * 1024) {
|
|
setSubmitStatus({
|
|
type: "error",
|
|
message: "Resume file size must be less than 5MB",
|
|
});
|
|
return;
|
|
}
|
|
|
|
setResume(file);
|
|
setSubmitStatus({ type: null, message: "" });
|
|
}
|
|
};
|
|
|
|
const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
|
|
e.preventDefault();
|
|
setIsSubmitting(true);
|
|
setSubmitStatus({ type: null, message: "" });
|
|
|
|
// Validation
|
|
if (!resume) {
|
|
setSubmitStatus({
|
|
type: "error",
|
|
message: "Please upload your resume",
|
|
});
|
|
setIsSubmitting(false);
|
|
return;
|
|
}
|
|
|
|
if (!formData.consent) {
|
|
setSubmitStatus({
|
|
type: "error",
|
|
message: "You must consent to data processing to apply",
|
|
});
|
|
setIsSubmitting(false);
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const applicationData: JobApplication = {
|
|
job: job.id,
|
|
first_name: formData.first_name,
|
|
last_name: formData.last_name,
|
|
email: formData.email,
|
|
phone: formData.phone || undefined,
|
|
current_position: formData.current_position || undefined,
|
|
current_company: formData.current_company || undefined,
|
|
years_of_experience: formData.years_of_experience || undefined,
|
|
cover_letter: formData.cover_letter || undefined,
|
|
resume: resume,
|
|
portfolio_url: formData.portfolio_url || undefined,
|
|
linkedin_url: formData.linkedin_url || undefined,
|
|
github_url: formData.github_url || undefined,
|
|
website_url: formData.website_url || undefined,
|
|
available_from: formData.available_from || undefined,
|
|
notice_period: formData.notice_period || undefined,
|
|
expected_salary: formData.expected_salary ? parseFloat(formData.expected_salary) : undefined,
|
|
salary_currency: formData.salary_currency || undefined,
|
|
consent: formData.consent,
|
|
};
|
|
|
|
await careerService.submitApplication(applicationData);
|
|
|
|
setSubmitStatus({
|
|
type: "success",
|
|
message: "Application submitted successfully! We'll be in touch soon.",
|
|
});
|
|
|
|
// Reset form
|
|
setFormData({
|
|
first_name: "",
|
|
last_name: "",
|
|
email: "",
|
|
phone: "",
|
|
current_position: "",
|
|
current_company: "",
|
|
years_of_experience: "",
|
|
cover_letter: "",
|
|
portfolio_url: "",
|
|
linkedin_url: "",
|
|
github_url: "",
|
|
website_url: "",
|
|
available_from: "",
|
|
notice_period: "",
|
|
expected_salary: "",
|
|
salary_currency: "USD",
|
|
consent: false,
|
|
});
|
|
setResume(null);
|
|
|
|
// Reset file input
|
|
const fileInput = document.getElementById('resume') as HTMLInputElement;
|
|
if (fileInput) fileInput.value = '';
|
|
|
|
} catch (error) {
|
|
setSubmitStatus({
|
|
type: "error",
|
|
message: error instanceof Error ? error.message : "Failed to submit application. Please try again.",
|
|
});
|
|
} finally {
|
|
setIsSubmitting(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="job-application-form" style={{
|
|
position: 'relative',
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
height: '100%',
|
|
maxHeight: '90vh'
|
|
}}>
|
|
{/* Header Section with Gradient */}
|
|
<div style={{
|
|
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
|
|
padding: 'clamp(24px, 4vw, 32px)',
|
|
borderRadius: '16px 16px 0 0',
|
|
position: 'relative',
|
|
flexShrink: 0
|
|
}}>
|
|
{/* Close Button */}
|
|
{onClose && (
|
|
<button
|
|
onClick={onClose}
|
|
style={{
|
|
position: 'absolute',
|
|
top: '16px',
|
|
right: '16px',
|
|
background: 'rgba(255,255,255,0.2)',
|
|
border: 'none',
|
|
color: 'white',
|
|
cursor: 'pointer',
|
|
fontSize: '24px',
|
|
padding: '8px',
|
|
lineHeight: '1',
|
|
borderRadius: '50%',
|
|
width: '40px',
|
|
height: '40px',
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
transition: 'all 0.2s',
|
|
zIndex: 10
|
|
}}
|
|
onMouseEnter={(e) => {
|
|
e.currentTarget.style.backgroundColor = 'rgba(220, 53, 69, 0.9)';
|
|
e.currentTarget.style.transform = 'scale(1.1)';
|
|
}}
|
|
onMouseLeave={(e) => {
|
|
e.currentTarget.style.backgroundColor = 'rgba(255,255,255,0.2)';
|
|
e.currentTarget.style.transform = 'scale(1)';
|
|
}}
|
|
title="Close"
|
|
>
|
|
<span className="material-symbols-outlined" style={{ fontSize: 'inherit' }}>close</span>
|
|
</button>
|
|
)}
|
|
|
|
<div className="intro" style={{ textAlign: 'center', color: 'white' }}>
|
|
<div style={{
|
|
width: '60px',
|
|
height: '60px',
|
|
borderRadius: '50%',
|
|
backgroundColor: 'rgba(255,255,255,0.2)',
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
margin: '0 auto 16px',
|
|
backdropFilter: 'blur(10px)'
|
|
}}>
|
|
<span className="material-symbols-outlined" style={{
|
|
fontSize: '32px',
|
|
color: 'white'
|
|
}}>work_outline</span>
|
|
</div>
|
|
<h3 id="application-form-title" className="fw-7" style={{
|
|
color: 'white',
|
|
fontSize: 'clamp(20px, 3vw, 24px)',
|
|
marginBottom: '8px'
|
|
}}>
|
|
Apply for {job.title}
|
|
</h3>
|
|
<p style={{
|
|
color: 'rgba(255,255,255,0.9)',
|
|
fontSize: 'clamp(13px, 2vw, 14px)',
|
|
margin: '0'
|
|
}}>
|
|
Join our team and make an impact
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Form Content - Scrollable Area */}
|
|
<form onSubmit={handleSubmit} style={{
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
flex: '1 1 auto',
|
|
minHeight: 0,
|
|
overflow: 'hidden'
|
|
}}>
|
|
<div
|
|
className="form-scrollable-content"
|
|
style={{
|
|
padding: 'clamp(20px, 4vw, 32px)',
|
|
overflowY: 'scroll',
|
|
overflowX: 'hidden',
|
|
flex: '1 1 auto',
|
|
WebkitOverflowScrolling: 'touch',
|
|
touchAction: 'pan-y',
|
|
scrollbarWidth: 'thin',
|
|
scrollbarColor: '#667eea #f0f0f0'
|
|
}}
|
|
>
|
|
|
|
{submitStatus.type && (
|
|
<div
|
|
className={`alert mb-24`}
|
|
style={{
|
|
padding: "16px 20px",
|
|
borderRadius: "8px",
|
|
backgroundColor: submitStatus.type === "success" ? "#d4edda" : "#f8d7da",
|
|
color: submitStatus.type === "success" ? "#155724" : "#721c24",
|
|
border: `2px solid ${submitStatus.type === "success" ? "#28a745" : "#dc3545"}`,
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
gap: '10px',
|
|
boxShadow: '0 2px 8px rgba(0,0,0,0.1)'
|
|
}}
|
|
>
|
|
<span className="material-symbols-outlined" style={{ fontSize: '20px' }}>
|
|
{submitStatus.type === "success" ? "check_circle" : "error"}
|
|
</span>
|
|
<span style={{ fontSize: '14px', fontWeight: '500' }}>{submitStatus.message}</span>
|
|
</div>
|
|
)}
|
|
{/* Personal Information */}
|
|
<div className="section" style={sectionStyle}>
|
|
<div style={sectionHeaderStyle}>
|
|
<span className="material-symbols-outlined me-2" style={{ color: '#667eea', fontSize: 'clamp(20px, 3vw, 22px)' }}>person</span>
|
|
<h4 className="fw-6 mb-0" style={{ color: '#333', fontSize: 'clamp(15px, 2.5vw, 16px)' }}>Personal Information</h4>
|
|
</div>
|
|
<div className="row">
|
|
<div className="col-12 col-md-6 mb-3">
|
|
<label htmlFor="first_name" className="form-label" style={labelStyle}>
|
|
First Name <span style={{ color: '#dc3545' }}>*</span>
|
|
</label>
|
|
<input
|
|
type="text"
|
|
id="first_name"
|
|
name="first_name"
|
|
className="form-control"
|
|
style={inputStyle}
|
|
value={formData.first_name}
|
|
onChange={handleInputChange}
|
|
onFocus={(e) => {
|
|
e.target.style.borderColor = '#667eea';
|
|
e.target.style.boxShadow = '0 0 0 3px rgba(102, 126, 234, 0.1)';
|
|
}}
|
|
onBlur={(e) => {
|
|
e.target.style.borderColor = '#e0e0e0';
|
|
e.target.style.boxShadow = 'none';
|
|
}}
|
|
required
|
|
/>
|
|
</div>
|
|
<div className="col-12 col-md-6 mb-3">
|
|
<label htmlFor="last_name" className="form-label" style={labelStyle}>
|
|
Last Name <span style={{ color: '#dc3545' }}>*</span>
|
|
</label>
|
|
<input
|
|
type="text"
|
|
id="last_name"
|
|
name="last_name"
|
|
className="form-control"
|
|
style={inputStyle}
|
|
value={formData.last_name}
|
|
onChange={handleInputChange}
|
|
onFocus={(e) => {
|
|
e.target.style.borderColor = '#667eea';
|
|
e.target.style.boxShadow = '0 0 0 3px rgba(102, 126, 234, 0.1)';
|
|
}}
|
|
onBlur={(e) => {
|
|
e.target.style.borderColor = '#e0e0e0';
|
|
e.target.style.boxShadow = 'none';
|
|
}}
|
|
required
|
|
/>
|
|
</div>
|
|
<div className="col-12 col-md-6 mb-3">
|
|
<label htmlFor="email" className="form-label" style={labelStyle}>
|
|
Email Address <span style={{ color: '#dc3545' }}>*</span>
|
|
</label>
|
|
<input
|
|
type="email"
|
|
id="email"
|
|
name="email"
|
|
className="form-control"
|
|
style={inputStyle}
|
|
value={formData.email}
|
|
onChange={handleInputChange}
|
|
onFocus={(e) => {
|
|
e.target.style.borderColor = '#667eea';
|
|
e.target.style.boxShadow = '0 0 0 3px rgba(102, 126, 234, 0.1)';
|
|
}}
|
|
onBlur={(e) => {
|
|
e.target.style.borderColor = '#e0e0e0';
|
|
e.target.style.boxShadow = 'none';
|
|
}}
|
|
required
|
|
/>
|
|
</div>
|
|
<div className="col-12 col-md-6 mb-3">
|
|
<label htmlFor="phone" className="form-label" style={labelStyle}>
|
|
Phone Number
|
|
</label>
|
|
<input
|
|
type="tel"
|
|
id="phone"
|
|
name="phone"
|
|
className="form-control"
|
|
style={inputStyle}
|
|
value={formData.phone}
|
|
onChange={handleInputChange}
|
|
onFocus={(e) => {
|
|
e.target.style.borderColor = '#667eea';
|
|
e.target.style.boxShadow = '0 0 0 3px rgba(102, 126, 234, 0.1)';
|
|
}}
|
|
onBlur={(e) => {
|
|
e.target.style.borderColor = '#e0e0e0';
|
|
e.target.style.boxShadow = 'none';
|
|
}}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Professional Information */}
|
|
<div className="section" style={sectionStyle}>
|
|
<div style={sectionHeaderStyle}>
|
|
<span className="material-symbols-outlined me-2" style={{ color: '#667eea', fontSize: '22px' }}>work</span>
|
|
<h4 className="fw-6 mb-0" style={{ color: '#333', fontSize: '16px' }}>Professional Info</h4>
|
|
</div>
|
|
<div className="row">
|
|
<div className="col-12 col-md-6 mb-24">
|
|
<label htmlFor="current_position" className="form-label" style={labelStyle}>
|
|
Current Position
|
|
</label>
|
|
<input
|
|
type="text"
|
|
id="current_position"
|
|
name="current_position"
|
|
className="form-control"
|
|
style={inputStyle}
|
|
value={formData.current_position}
|
|
onChange={handleInputChange}
|
|
onFocus={(e) => {
|
|
e.target.style.borderColor = '#667eea';
|
|
e.target.style.boxShadow = '0 0 0 3px rgba(102, 126, 234, 0.1)';
|
|
}}
|
|
onBlur={(e) => {
|
|
e.target.style.borderColor = '#e0e0e0';
|
|
e.target.style.boxShadow = 'none';
|
|
}}
|
|
/>
|
|
</div>
|
|
<div className="col-12 col-md-6 mb-24">
|
|
<label htmlFor="current_company" className="form-label" style={labelStyle}>
|
|
Current Company
|
|
</label>
|
|
<input
|
|
type="text"
|
|
id="current_company"
|
|
name="current_company"
|
|
className="form-control"
|
|
style={inputStyle}
|
|
value={formData.current_company}
|
|
onChange={handleInputChange}
|
|
onFocus={(e) => {
|
|
e.target.style.borderColor = '#667eea';
|
|
e.target.style.boxShadow = '0 0 0 3px rgba(102, 126, 234, 0.1)';
|
|
}}
|
|
onBlur={(e) => {
|
|
e.target.style.borderColor = '#e0e0e0';
|
|
e.target.style.boxShadow = 'none';
|
|
}}
|
|
/>
|
|
</div>
|
|
<div className="col-12 mb-24">
|
|
<label htmlFor="years_of_experience" className="form-label" style={labelStyle}>
|
|
Years of Experience
|
|
</label>
|
|
<input
|
|
type="text"
|
|
id="years_of_experience"
|
|
name="years_of_experience"
|
|
className="form-control"
|
|
style={inputStyle}
|
|
placeholder="e.g., 3-5 years"
|
|
value={formData.years_of_experience}
|
|
onChange={handleInputChange}
|
|
onFocus={(e) => {
|
|
e.target.style.borderColor = '#667eea';
|
|
e.target.style.boxShadow = '0 0 0 3px rgba(102, 126, 234, 0.1)';
|
|
}}
|
|
onBlur={(e) => {
|
|
e.target.style.borderColor = '#e0e0e0';
|
|
e.target.style.boxShadow = 'none';
|
|
}}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Resume and Cover Letter */}
|
|
<div className="section" style={sectionStyle}>
|
|
<div style={sectionHeaderStyle}>
|
|
<span className="material-symbols-outlined me-2" style={{ color: '#667eea', fontSize: '22px' }}>upload_file</span>
|
|
<h4 className="fw-6 mb-0" style={{ color: '#333', fontSize: '16px' }}>Documents</h4>
|
|
</div>
|
|
<div className="row">
|
|
<div className="col-12 mb-24">
|
|
<label htmlFor="resume" className="form-label" style={labelStyle}>
|
|
Resume (PDF, DOC, DOCX - Max 5MB) <span style={{ color: '#dc3545' }}>*</span>
|
|
</label>
|
|
<input
|
|
type="file"
|
|
id="resume"
|
|
name="resume"
|
|
className="form-control"
|
|
style={{...inputStyle, padding: '10px 16px'}}
|
|
accept=".pdf,.doc,.docx"
|
|
onChange={handleFileChange}
|
|
onFocus={(e) => {
|
|
e.target.style.borderColor = '#667eea';
|
|
e.target.style.boxShadow = '0 0 0 3px rgba(102, 126, 234, 0.1)';
|
|
}}
|
|
onBlur={(e) => {
|
|
e.target.style.borderColor = '#e0e0e0';
|
|
e.target.style.boxShadow = 'none';
|
|
}}
|
|
required
|
|
/>
|
|
{resume && <small style={{ color: '#28a745', fontWeight: '500', display: 'block', marginTop: '8px' }}>✓ Selected: {resume.name}</small>}
|
|
</div>
|
|
<div className="col-12 mb-3">
|
|
<label htmlFor="cover_letter" className="form-label" style={labelStyle}>
|
|
Cover Letter / Message
|
|
</label>
|
|
<textarea
|
|
id="cover_letter"
|
|
name="cover_letter"
|
|
className="form-control"
|
|
style={{...inputStyle, minHeight: '120px', resize: 'vertical'}}
|
|
rows={5}
|
|
placeholder="Tell us why you're interested in this position..."
|
|
value={formData.cover_letter}
|
|
onChange={handleInputChange}
|
|
onFocus={(e) => {
|
|
e.target.style.borderColor = '#667eea';
|
|
e.target.style.boxShadow = '0 0 0 3px rgba(102, 126, 234, 0.1)';
|
|
}}
|
|
onBlur={(e) => {
|
|
e.target.style.borderColor = '#e0e0e0';
|
|
e.target.style.boxShadow = 'none';
|
|
}}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Links */}
|
|
<div className="section" style={sectionStyle}>
|
|
<div style={sectionHeaderStyle}>
|
|
<span className="material-symbols-outlined me-2" style={{ color: '#667eea', fontSize: '22px' }}>link</span>
|
|
<h4 className="fw-6 mb-0" style={{ color: '#333', fontSize: '16px' }}>Links (Optional)</h4>
|
|
</div>
|
|
<div className="row">
|
|
<div className="col-12 col-md-6 mb-24">
|
|
<label htmlFor="portfolio_url" className="form-label" style={labelStyle}>
|
|
Portfolio / Website
|
|
</label>
|
|
<input
|
|
type="url"
|
|
id="portfolio_url"
|
|
name="portfolio_url"
|
|
className="form-control"
|
|
style={inputStyle}
|
|
placeholder="https://"
|
|
value={formData.portfolio_url}
|
|
onChange={handleInputChange}
|
|
onFocus={(e) => {
|
|
e.target.style.borderColor = '#667eea';
|
|
e.target.style.boxShadow = '0 0 0 3px rgba(102, 126, 234, 0.1)';
|
|
}}
|
|
onBlur={(e) => {
|
|
e.target.style.borderColor = '#e0e0e0';
|
|
e.target.style.boxShadow = 'none';
|
|
}}
|
|
/>
|
|
</div>
|
|
<div className="col-12 col-md-6 mb-24">
|
|
<label htmlFor="linkedin_url" className="form-label" style={labelStyle}>
|
|
LinkedIn Profile
|
|
</label>
|
|
<input
|
|
type="url"
|
|
id="linkedin_url"
|
|
name="linkedin_url"
|
|
className="form-control"
|
|
style={inputStyle}
|
|
placeholder="https://linkedin.com/in/..."
|
|
value={formData.linkedin_url}
|
|
onChange={handleInputChange}
|
|
onFocus={(e) => {
|
|
e.target.style.borderColor = '#667eea';
|
|
e.target.style.boxShadow = '0 0 0 3px rgba(102, 126, 234, 0.1)';
|
|
}}
|
|
onBlur={(e) => {
|
|
e.target.style.borderColor = '#e0e0e0';
|
|
e.target.style.boxShadow = 'none';
|
|
}}
|
|
/>
|
|
</div>
|
|
<div className="col-12 col-md-6 mb-24">
|
|
<label htmlFor="github_url" className="form-label" style={labelStyle}>
|
|
GitHub Profile
|
|
</label>
|
|
<input
|
|
type="url"
|
|
id="github_url"
|
|
name="github_url"
|
|
className="form-control"
|
|
style={inputStyle}
|
|
placeholder="https://github.com/..."
|
|
value={formData.github_url}
|
|
onChange={handleInputChange}
|
|
onFocus={(e) => {
|
|
e.target.style.borderColor = '#667eea';
|
|
e.target.style.boxShadow = '0 0 0 3px rgba(102, 126, 234, 0.1)';
|
|
}}
|
|
onBlur={(e) => {
|
|
e.target.style.borderColor = '#e0e0e0';
|
|
e.target.style.boxShadow = 'none';
|
|
}}
|
|
/>
|
|
</div>
|
|
<div className="col-12 col-md-6 mb-24">
|
|
<label htmlFor="website_url" className="form-label" style={labelStyle}>
|
|
Personal Website
|
|
</label>
|
|
<input
|
|
type="url"
|
|
id="website_url"
|
|
name="website_url"
|
|
className="form-control"
|
|
style={inputStyle}
|
|
placeholder="https://"
|
|
value={formData.website_url}
|
|
onChange={handleInputChange}
|
|
onFocus={(e) => {
|
|
e.target.style.borderColor = '#667eea';
|
|
e.target.style.boxShadow = '0 0 0 3px rgba(102, 126, 234, 0.1)';
|
|
}}
|
|
onBlur={(e) => {
|
|
e.target.style.borderColor = '#e0e0e0';
|
|
e.target.style.boxShadow = 'none';
|
|
}}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Availability & Salary */}
|
|
<div className="section" style={sectionStyle}>
|
|
<div style={sectionHeaderStyle}>
|
|
<span className="material-symbols-outlined me-2" style={{ color: '#667eea', fontSize: '22px' }}>event_available</span>
|
|
<h4 className="fw-6 mb-0" style={{ color: '#333', fontSize: '16px' }}>Availability</h4>
|
|
</div>
|
|
<div className="row">
|
|
<div className="col-12 col-md-6 mb-24">
|
|
<label htmlFor="available_from" className="form-label" style={labelStyle}>
|
|
Available From
|
|
</label>
|
|
<input
|
|
type="date"
|
|
id="available_from"
|
|
name="available_from"
|
|
className="form-control"
|
|
style={inputStyle}
|
|
value={formData.available_from}
|
|
onChange={handleInputChange}
|
|
onFocus={(e) => {
|
|
e.target.style.borderColor = '#667eea';
|
|
e.target.style.boxShadow = '0 0 0 3px rgba(102, 126, 234, 0.1)';
|
|
}}
|
|
onBlur={(e) => {
|
|
e.target.style.borderColor = '#e0e0e0';
|
|
e.target.style.boxShadow = 'none';
|
|
}}
|
|
/>
|
|
</div>
|
|
<div className="col-12 col-md-6 mb-24">
|
|
<label htmlFor="notice_period" className="form-label" style={labelStyle}>
|
|
Notice Period
|
|
</label>
|
|
<input
|
|
type="text"
|
|
id="notice_period"
|
|
name="notice_period"
|
|
className="form-control"
|
|
style={inputStyle}
|
|
placeholder="e.g., 2 weeks, 1 month"
|
|
value={formData.notice_period}
|
|
onChange={handleInputChange}
|
|
onFocus={(e) => {
|
|
e.target.style.borderColor = '#667eea';
|
|
e.target.style.boxShadow = '0 0 0 3px rgba(102, 126, 234, 0.1)';
|
|
}}
|
|
onBlur={(e) => {
|
|
e.target.style.borderColor = '#e0e0e0';
|
|
e.target.style.boxShadow = 'none';
|
|
}}
|
|
/>
|
|
</div>
|
|
<div className="col-12 col-md-6 mb-24">
|
|
<label htmlFor="expected_salary" className="form-label" style={labelStyle}>
|
|
Expected Salary
|
|
</label>
|
|
<input
|
|
type="number"
|
|
id="expected_salary"
|
|
name="expected_salary"
|
|
className="form-control"
|
|
style={inputStyle}
|
|
placeholder="Amount"
|
|
value={formData.expected_salary}
|
|
onChange={handleInputChange}
|
|
onFocus={(e) => {
|
|
e.target.style.borderColor = '#667eea';
|
|
e.target.style.boxShadow = '0 0 0 3px rgba(102, 126, 234, 0.1)';
|
|
}}
|
|
onBlur={(e) => {
|
|
e.target.style.borderColor = '#e0e0e0';
|
|
e.target.style.boxShadow = 'none';
|
|
}}
|
|
/>
|
|
</div>
|
|
<div className="col-12 col-md-6 mb-24">
|
|
<label htmlFor="salary_currency" className="form-label" style={labelStyle}>
|
|
Currency
|
|
</label>
|
|
<select
|
|
id="salary_currency"
|
|
name="salary_currency"
|
|
className="form-control"
|
|
style={inputStyle}
|
|
value={formData.salary_currency}
|
|
onChange={handleInputChange}
|
|
onFocus={(e) => {
|
|
e.target.style.borderColor = '#667eea';
|
|
e.target.style.boxShadow = '0 0 0 3px rgba(102, 126, 234, 0.1)';
|
|
}}
|
|
onBlur={(e) => {
|
|
e.target.style.borderColor = '#e0e0e0';
|
|
e.target.style.boxShadow = 'none';
|
|
}}
|
|
>
|
|
<option value="USD">USD</option>
|
|
<option value="EUR">EUR</option>
|
|
<option value="GBP">GBP</option>
|
|
<option value="INR">INR</option>
|
|
<option value="AUD">AUD</option>
|
|
<option value="CAD">CAD</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Consent */}
|
|
<div className="section" style={{
|
|
background: 'linear-gradient(135deg, #fff9e6 0%, #fffbf0 100%)',
|
|
padding: 'clamp(14px, 3vw, 16px)',
|
|
borderRadius: '8px',
|
|
border: '1px solid #ffd700',
|
|
marginBottom: '16px'
|
|
}}>
|
|
<div className="form-check d-flex align-items-start">
|
|
<input
|
|
type="checkbox"
|
|
id="consent"
|
|
name="consent"
|
|
className="form-check-input"
|
|
style={{
|
|
width: '18px',
|
|
height: '18px',
|
|
marginTop: '2px',
|
|
marginRight: '10px',
|
|
cursor: 'pointer',
|
|
flexShrink: 0,
|
|
accentColor: '#667eea'
|
|
}}
|
|
checked={formData.consent}
|
|
onChange={handleInputChange}
|
|
required
|
|
/>
|
|
<label htmlFor="consent" className="form-check-label" style={{
|
|
fontSize: 'clamp(12px, 2vw, 13px)',
|
|
color: '#555',
|
|
lineHeight: '1.5',
|
|
cursor: 'pointer'
|
|
}}>
|
|
I consent to data processing for recruitment purposes. <span style={{ color: '#dc3545', fontWeight: '600' }}>*</span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Submit and Cancel Buttons - Fixed Footer */}
|
|
<div className="text-center" style={{
|
|
paddingTop: '16px',
|
|
paddingBottom: '16px',
|
|
borderTop: '2px solid #e8e8e8',
|
|
backgroundColor: 'white',
|
|
padding: '16px clamp(20px, 4vw, 32px)',
|
|
borderRadius: '0 0 16px 16px',
|
|
boxShadow: '0 -4px 12px rgba(0,0,0,0.05)',
|
|
flexShrink: 0
|
|
}}>
|
|
<div style={{ display: 'flex', gap: '12px', justifyContent: 'center', flexWrap: 'wrap' }}>
|
|
<button
|
|
type="submit"
|
|
className="btn"
|
|
disabled={isSubmitting}
|
|
style={{
|
|
minWidth: "160px",
|
|
backgroundColor: isSubmitting ? '#ccc' : 'white',
|
|
color: '#333',
|
|
border: '2px solid #667eea',
|
|
padding: '12px 32px',
|
|
fontSize: '15px',
|
|
fontWeight: '600',
|
|
borderRadius: '6px',
|
|
transition: 'all 0.3s ease',
|
|
cursor: isSubmitting ? 'not-allowed' : 'pointer',
|
|
display: 'inline-flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
gap: '8px'
|
|
}}
|
|
onMouseEnter={(e) => {
|
|
if (!isSubmitting) {
|
|
e.currentTarget.style.backgroundColor = '#FFD700';
|
|
e.currentTarget.style.borderColor = '#FFD700';
|
|
e.currentTarget.style.transform = 'translateY(-2px)';
|
|
e.currentTarget.style.boxShadow = '0 8px 16px rgba(0,0,0,0.15)';
|
|
}
|
|
}}
|
|
onMouseLeave={(e) => {
|
|
if (!isSubmitting) {
|
|
e.currentTarget.style.backgroundColor = 'white';
|
|
e.currentTarget.style.borderColor = '#667eea';
|
|
e.currentTarget.style.transform = 'translateY(0)';
|
|
e.currentTarget.style.boxShadow = 'none';
|
|
}
|
|
}}
|
|
>
|
|
{isSubmitting ? (
|
|
<>
|
|
<span className="material-symbols-outlined" style={{ fontSize: '20px', animation: 'spin 1s linear infinite' }}>progress_activity</span>
|
|
Submitting...
|
|
</>
|
|
) : (
|
|
<>
|
|
<span className="material-symbols-outlined" style={{ fontSize: '20px' }}>send</span>
|
|
Submit Application
|
|
</>
|
|
)}
|
|
</button>
|
|
|
|
{onClose && !isSubmitting && (
|
|
<button
|
|
type="button"
|
|
onClick={onClose}
|
|
className="btn"
|
|
style={{
|
|
minWidth: "120px",
|
|
backgroundColor: 'transparent',
|
|
color: '#666',
|
|
border: '2px solid #e0e0e0',
|
|
padding: '12px 24px',
|
|
fontSize: '15px',
|
|
fontWeight: '600',
|
|
borderRadius: '6px',
|
|
transition: 'all 0.3s ease',
|
|
cursor: 'pointer',
|
|
display: 'inline-flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
gap: '8px'
|
|
}}
|
|
onMouseEnter={(e) => {
|
|
e.currentTarget.style.backgroundColor = '#f5f5f5';
|
|
e.currentTarget.style.borderColor = '#999';
|
|
e.currentTarget.style.color = '#333';
|
|
}}
|
|
onMouseLeave={(e) => {
|
|
e.currentTarget.style.backgroundColor = 'transparent';
|
|
e.currentTarget.style.borderColor = '#e0e0e0';
|
|
e.currentTarget.style.color = '#666';
|
|
}}
|
|
>
|
|
<span className="material-symbols-outlined" style={{ fontSize: '18px' }}>close</span>
|
|
Cancel
|
|
</button>
|
|
)}
|
|
</div>
|
|
<p style={{
|
|
marginTop: '10px',
|
|
color: '#999',
|
|
fontSize: 'clamp(11px, 2vw, 12px)'
|
|
}}>
|
|
By submitting, you agree to our terms
|
|
</p>
|
|
</div>
|
|
</form>
|
|
|
|
<style>{`
|
|
@keyframes spin {
|
|
from { transform: rotate(0deg); }
|
|
to { transform: rotate(360deg); }
|
|
}
|
|
|
|
/* Custom scrollbar styling */
|
|
.form-scrollable-content::-webkit-scrollbar {
|
|
width: 8px;
|
|
}
|
|
|
|
.form-scrollable-content::-webkit-scrollbar-track {
|
|
background: #f0f0f0;
|
|
border-radius: 4px;
|
|
}
|
|
|
|
.form-scrollable-content::-webkit-scrollbar-thumb {
|
|
background: #667eea;
|
|
border-radius: 4px;
|
|
}
|
|
|
|
.form-scrollable-content::-webkit-scrollbar-thumb:hover {
|
|
background: #5568d3;
|
|
}
|
|
|
|
/* Ensure smooth scrolling on touch devices */
|
|
.form-scrollable-content {
|
|
-webkit-overflow-scrolling: touch;
|
|
overscroll-behavior: contain;
|
|
}
|
|
|
|
/* Better mobile input styling */
|
|
@media (max-width: 768px) {
|
|
.form-control {
|
|
font-size: 16px !important; /* Prevents zoom on iOS */
|
|
}
|
|
}
|
|
`}</style>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default JobApplicationForm;
|
|
|