Files
HRM-System/resources/js/pages/hr/employees/create.tsx
2026-04-13 08:16:56 +08:00

920 lines
38 KiB
TypeScript
Executable File

// pages/hr/employees/create.tsx
import { useState } from 'react';
import { PageTemplate } from '@/components/page-template';
import { usePage, router } from '@inertiajs/react';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Textarea } from '@/components/ui/textarea';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
import { toast } from '@/components/custom-toast';
import { useTranslation } from 'react-i18next';
import { ArrowLeft, Plus, Trash2 } from 'lucide-react';
import MediaPicker from '@/components/MediaPicker';
import { getImagePath } from '@/utils/helpers';
export default function EmployeeCreate() {
const { t } = useTranslation();
const { branches, departments, designations, documentTypes, shifts, attendancePolicies, generatedEmployeeId } = usePage().props as any;
// Initialize required documents
const initialDocuments = documentTypes && Array.isArray(documentTypes)
? documentTypes.filter((dt: any) => dt.is_required).map((dt: any) => ({
document_type_id: dt.id.toString(),
file: null,
expiry_date: '',
is_required: true,
}))
: [];
// State
const [formData, setFormData] = useState<Record<string, any>>({
name: '',
biometric_emp_id: '',
email: '',
password: '',
phone: '',
date_of_birth: '',
gender: '',
branch_id: '',
department_id: '',
designation_id: '',
shift_id: '',
attendance_policy_id: '',
date_of_joining: '',
employment_type: 'Full-time',
employee_status: 'active',
address_line_1: '',
address_line_2: '',
city: '',
state: '',
country: '',
postal_code: '',
emergency_contact_name: '',
emergency_contact_relationship: '',
emergency_contact_number: '',
bank_name: '',
account_holder_name: '',
account_number: '',
bank_identifier_code: '',
bank_branch: '',
tax_payer_id: '',
salary: '',
documents: initialDocuments
});
const [errors, setErrors] = useState<Record<string, string>>({});
const [isSubmitting, setIsSubmitting] = useState(false);
const [profileImage, setProfileImage] = useState<File | null>(null);
const [profileImagePreview, setProfileImagePreview] = useState<string | null>(null);
// Filter departments based on selected branch
// const filteredDepartments = formData.branch_id
// ? departments.filter((dept: any) => dept.branch_id === parseInt(formData.branch_id))
// : departments;
const filteredDepartments = formData.branch_id
? departments.filter((dept: any) => String(dept.branch_id) === String(formData.branch_id))
: departments;
// Filter designations based on selected department
// const filteredDesignations = formData.department_id
// ? designations.filter((desig: any) => desig.department_id === parseInt(formData.department_id))
// : designations;
const filteredDesignations = formData.department_id
? designations.filter((desig: any) => String(desig.department_id) === String(formData.department_id))
: designations;
const handleChange = (name: string, value: any) => {
setFormData(prev => ({ ...prev, [name]: value }));
// Clear error when field is changed
if (errors[name]) {
setErrors(prev => {
const newErrors = { ...prev };
delete newErrors[name];
return newErrors;
});
}
// Handle branch change - reset department and designation
if (name === 'branch_id') {
setFormData(prev => ({
...prev,
branch_id: value,
department_id: '',
designation_id: ''
}));
}
// Handle department change - reset designation
if (name === 'department_id') {
setFormData(prev => ({
...prev,
department_id: value,
designation_id: ''
}));
}
};
const handleProfileImageChange = (e: React.ChangeEvent<HTMLInputElement>) => {
if (e.target.files && e.target.files[0]) {
const file = e.target.files[0];
setProfileImage(file);
setProfileImagePreview(URL.createObjectURL(file));
// Clear error
if (errors['profile_image']) {
setErrors(prev => {
const newErrors = { ...prev };
delete newErrors['profile_image'];
return newErrors;
});
}
}
};
const handleDocumentChange = (index: number, field: string, value: any) => {
const updatedDocuments = [...formData.documents];
updatedDocuments[index] = { ...updatedDocuments[index], [field]: value };
setFormData(prev => ({ ...prev, documents: updatedDocuments }));
// Clear error
const errorKey = `documents.${index}.${field}`;
if (errors[errorKey]) {
setErrors(prev => {
const newErrors = { ...prev };
delete newErrors[errorKey];
return newErrors;
});
}
};
const handleDocumentFileChange = (index: number, e: React.ChangeEvent<HTMLInputElement>) => {
if (e.target.files && e.target.files[0]) {
const file = e.target.files[0];
handleDocumentChange(index, 'file', file);
}
};
const addDocument = () => {
setFormData(prev => ({
...prev,
documents: [
...prev.documents,
{ document_type_id: '', file: null, expiry_date: '' }
]
}));
};
const removeDocument = (index: number) => {
const updatedDocuments = [...formData.documents];
updatedDocuments.splice(index, 1);
setFormData(prev => ({ ...prev, documents: updatedDocuments }));
// Clear errors for this document
const newErrors = { ...errors };
Object.keys(newErrors).forEach(key => {
if (key.startsWith(`documents.${index}.`)) {
delete newErrors[key];
}
});
setErrors(newErrors);
};
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
setIsSubmitting(true);
// Create form data for submission
const submitData = new FormData();
// Add all form fields
Object.entries(formData).forEach(([key, value]) => {
if (key !== 'documents') {
if (value !== null && value !== undefined && value !== '') {
submitData.append(key, value);
}
}
});
// Add profile image if selected
if (profileImage) {
submitData.append('profile_image', profileImage);
}
// Add documents
formData.documents.forEach((doc: any, index: number) => {
if (doc.document_type_id) {
submitData.append(`documents[${index}][document_type_id]`, doc.document_type_id);
}
if (doc.file) {
submitData.append(`documents[${index}][file]`, doc.file);
}
if (doc.expiry_date) {
submitData.append(`documents[${index}][expiry_date]`, doc.expiry_date);
}
});
// Submit the form
router.post(route('hr.employees.store'), submitData, {
forceFormData: true,
onSuccess: (page) => {
setIsSubmitting(false);
if (page.props.flash.success) {
toast.success(t(page.props.flash.success));
}
router.get(route('hr.employees.index'));
},
onError: (errors) => {
setIsSubmitting(false);
setErrors(errors);
toast.error(t('Please correct the errors in the form'));
}
});
};
const breadcrumbs = [
{ title: t('Dashboard'), href: route('dashboard') },
{ title: t('HR Management'), href: route('hr.employees.index') },
{ title: t('Employees'), href: route('hr.employees.index') },
{ title: t('Create Employee') }
];
return (
<PageTemplate
title={t("Create Employee")}
url="/hr/employees/create"
breadcrumbs={breadcrumbs}
actions={[
{
label: t('Back'),
icon: <ArrowLeft className="h-4 w-4 mr-2" />,
variant: 'outline',
onClick: () => router.get(route('hr.employees.index'))
}
]}
>
<form onSubmit={handleSubmit} className="space-y-6">
{/* Basic Information Card */}
<Card>
<CardHeader>
<CardTitle>{t('Basic Information')}</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="name" required>{t('Full Name')}</Label>
<Input
id="name"
required
value={formData.name}
onChange={(e) => handleChange('name', e.target.value)}
className={errors.name ? 'border-red-500' : ''}
/>
{errors.name && <p className="text-red-500 text-xs">{errors.name}</p>}
</div>
<div className="space-y-2">
<Label htmlFor="employee_id">{t('Employee ID')}</Label>
<Input
id="employee_id"
value={generatedEmployeeId}
readOnly
className="bg-muted"
/>
<p className="text-sm text-muted-foreground">{t('Employee ID will be auto-generated')}</p>
</div>
<div className="space-y-2">
<Label htmlFor="biometric_emp_id" required>{t('Employee Code')}</Label>
<Input
id="biometric_emp_id"
required
value={formData.biometric_emp_id || ''}
onChange={(e) => handleChange('biometric_emp_id', e.target.value)}
placeholder=""
className={errors.biometric_emp_id ? 'border-red-500' : ''}
/>
<p className="text-sm text-muted-foreground">{t('This ID will be used to map employee with biometric device.')}</p>
{errors.biometric_emp_id && <p className="text-red-500 text-xs">{errors.biometric_emp_id}</p>}
</div>
<div className="space-y-2">
<Label htmlFor="email" required>{t('Email')}</Label>
<Input
id="email"
type="email"
required
value={formData.email}
onChange={(e) => handleChange('email', e.target.value)}
className={errors.email ? 'border-red-500' : ''}
/>
{errors.email && <p className="text-red-500 text-xs">{errors.email}</p>}
</div>
<div className="space-y-2">
<Label htmlFor="password" required>{t('Password')}</Label>
<Input
id="password"
type="password"
required
value={formData.password}
onChange={(e) => handleChange('password', e.target.value)}
className={errors.password ? 'border-red-500' : ''}
/>
{errors.password && <p className="text-red-500 text-xs">{errors.password}</p>}
</div>
<div className="space-y-2">
<Label htmlFor="phone" required>{t('Phone Number')}</Label>
<Input
id="phone"
required
value={formData.phone}
onChange={(e) => handleChange('phone', e.target.value)}
className={errors.phone ? 'border-red-500' : ''}
/>
{errors.phone && <p className="text-red-500 text-xs">{errors.phone}</p>}
</div>
<div className="space-y-2">
<Label htmlFor="date_of_birth" required>{t('Date of Birth')}</Label>
<div className="cursor-pointer" onClick={(e) => { const input = (e.currentTarget as HTMLElement).querySelector('input'); try { (input as any)?.showPicker?.(); } catch { input?.focus(); } }}>
<Input
id="date_of_birth"
type="date"
required
value={formData.date_of_birth}
onChange={(e) => handleChange('date_of_birth', e.target.value)}
className={`cursor-pointer ${errors.date_of_birth ? 'border-red-500' : ''}`}
/>
</div>
{errors.date_of_birth && <p className="text-red-500 text-xs">{errors.date_of_birth}</p>}
</div>
<div className="space-y-2">
<Label required>{t('Gender')}</Label>
<RadioGroup
value={formData.gender}
onValueChange={(value) => handleChange('gender', value)}
className="flex space-x-4"
>
<div className="flex items-center space-x-2">
<RadioGroupItem value="male" id="gender-male" />
<Label htmlFor="gender-male">{t('Male')}</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="female" id="gender-female" />
<Label htmlFor="gender-female">{t('Female')}</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="other" id="gender-other" />
<Label htmlFor="gender-other">{t('Other')}</Label>
</div>
</RadioGroup>
{errors.gender && <p className="text-red-500 text-xs">{errors.gender}</p>}
</div>
<div className="space-y-2">
<Label required>{t('Profile Image')}</Label>
<div className="flex flex-col gap-3">
<div className="border rounded-md p-4 flex items-center justify-center bg-muted/30 h-32">
{profileImagePreview ? (
<img
src={profileImagePreview}
alt="Profile Image"
className="max-h-full max-w-full object-contain rounded-full"
/>
) : (
<div className="text-muted-foreground flex flex-col items-center gap-2">
<div className="h-12 w-12 bg-muted flex items-center justify-center rounded-full border border-dashed">
<span className="font-semibold text-xs text-muted-foreground">{t('Image')}</span>
</div>
<span className="text-xs">{t('No image selected')}</span>
</div>
)}
</div>
<Input
type="file"
accept="image/*"
onChange={handleProfileImageChange}
className="cursor-pointer"
/>
<p className="text-xs text-muted-foreground">{t('Max file size: 2MB')}</p>
</div>
{errors.profile_image && <p className="text-red-500 text-xs">{errors.profile_image}</p>}
</div>
</div>
</CardContent>
</Card>
{/* Employment Details Card */}
<Card>
<CardHeader>
<CardTitle>{t('Employment Details')}</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="branch_id" required>{t('Branch')}</Label>
<Select
value={formData.branch_id}
required
onValueChange={(value) => handleChange('branch_id', value)}
>
<SelectTrigger className={errors.branch_id ? 'border-red-500' : ''}>
<SelectValue placeholder={t('Select Branch')} />
</SelectTrigger>
<SelectContent searchable={true}>
{branches.map((branch: any) => (
<SelectItem key={branch.id} value={branch.id.toString()}>
{branch.name}
</SelectItem>
))}
</SelectContent>
</Select>
{errors.branch_id && <p className="text-red-500 text-xs">{errors.branch_id}</p>}
</div>
<div className="space-y-2">
<Label htmlFor="department_id" required>{t('Department')}</Label>
<Select
value={formData.department_id}
required
onValueChange={(value) => handleChange('department_id', value)}
disabled={!formData.branch_id}
>
<SelectTrigger className={errors.department_id ? 'border-red-500' : ''}>
<SelectValue placeholder={formData.branch_id ? t('Select Department') : t('Select Branch First')} />
</SelectTrigger>
<SelectContent searchable={true}>
{filteredDepartments.map((department: any) => (
<SelectItem key={department.id} value={department.id.toString()}>
{department.name}
</SelectItem>
))}
</SelectContent>
</Select>
{errors.department_id && <p className="text-red-500 text-xs">{errors.department_id}</p>}
</div>
<div className="space-y-2">
<Label htmlFor="designation_id" required>{t('Designation')}</Label>
<Select
value={formData.designation_id}
required
onValueChange={(value) => handleChange('designation_id', value)}
disabled={!formData.department_id}
>
<SelectTrigger className={errors.designation_id ? 'border-red-500' : ''}>
<SelectValue placeholder={formData.department_id ? t('Select Designation') : t('Select Department First')} />
</SelectTrigger>
<SelectContent searchable={true}>
{filteredDesignations.map((designation: any) => (
<SelectItem key={designation.id} value={designation.id.toString()}>
{designation.name}
</SelectItem>
))}
</SelectContent>
</Select>
{errors.designation_id && <p className="text-red-500 text-xs">{errors.designation_id}</p>}
</div>
<div className="space-y-2">
<Label htmlFor="date_of_joining" required>{t('Date of Joining')}</Label>
<div className="cursor-pointer" onClick={(e) => { const input = (e.currentTarget as HTMLElement).querySelector('input'); try { (input as any)?.showPicker?.(); } catch { input?.focus(); } }}>
<Input
id="date_of_joining"
type="date"
required
value={formData.date_of_joining}
onChange={(e) => handleChange('date_of_joining', e.target.value)}
className={`cursor-pointer ${errors.date_of_joining ? 'border-red-500' : ''}`}
/>
</div>
{errors.date_of_joining && <p className="text-red-500 text-xs">{errors.date_of_joining}</p>}
</div>
<div className="space-y-2">
<Label htmlFor="employment_type" required>{t('Employment Type')}</Label>
<Select
value={formData.employment_type}
required
onValueChange={(value) => handleChange('employment_type', value)}
>
<SelectTrigger className={errors.employment_type ? 'border-red-500' : ''}>
<SelectValue placeholder={t('Select Employment Type')} />
</SelectTrigger>
<SelectContent>
<SelectItem value="Full-time">{t('Full-time')}</SelectItem>
<SelectItem value="Part-time">{t('Part-time')}</SelectItem>
<SelectItem value="Contract">{t('Contract')}</SelectItem>
<SelectItem value="Internship">{t('Internship')}</SelectItem>
<SelectItem value="Temporary">{t('Temporary')}</SelectItem>
</SelectContent>
</Select>
{errors.employment_type && <p className="text-red-500 text-xs">{errors.employment_type}</p>}
</div>
<div className="space-y-2">
<Label htmlFor="employee_status" required>{t('Employee Status')}</Label>
<Select
value={formData.employee_status}
onValueChange={(value) => handleChange('employee_status', value)}
>
<SelectTrigger className={errors.employee_status ? 'border-red-500' : ''}>
<SelectValue placeholder={t('Select Employee Status')} />
</SelectTrigger>
<SelectContent>
<SelectItem value="active">{t('Active')}</SelectItem>
<SelectItem value="inactive">{t('Inactive')}</SelectItem>
<SelectItem value="probation">{t('Probation')}</SelectItem>
<SelectItem value="terminated">{t('Terminated')}</SelectItem>
</SelectContent>
</Select>
{errors.employee_status && <p className="text-red-500 text-xs">{errors.employee_status}</p>}
</div>
<div className="space-y-2">
<Label htmlFor="shift_id">{t('Shift')}</Label>
<Select
value={formData.shift_id}
onValueChange={(value) => handleChange('shift_id', value)}
>
<SelectTrigger className={errors.shift_id ? 'border-red-500' : ''}>
<SelectValue placeholder={t('Select Shift (Optional)')} />
</SelectTrigger>
<SelectContent searchable={true}>
{shifts?.map((shift: any) => (
<SelectItem key={shift.id} value={shift.id.toString()}>
{shift.name} ({shift.start_time} - {shift.end_time})
</SelectItem>
))}
</SelectContent>
</Select>
{errors.shift_id && <p className="text-red-500 text-xs">{errors.shift_id}</p>}
</div>
<div className="space-y-2">
<Label htmlFor="attendance_policy_id">{t('Attendance Policy')}</Label>
<Select
value={formData.attendance_policy_id}
onValueChange={(value) => handleChange('attendance_policy_id', value)}
>
<SelectTrigger className={errors.attendance_policy_id ? 'border-red-500' : ''}>
<SelectValue placeholder={t('Select Attendance Policy (Optional)')} />
</SelectTrigger>
<SelectContent>
{attendancePolicies?.map((policy: any) => (
<SelectItem key={policy.id} value={policy.id.toString()}>
{policy.name}
</SelectItem>
))}
</SelectContent>
</Select>
{errors.attendance_policy_id && <p className="text-red-500 text-xs">{errors.attendance_policy_id}</p>}
</div>
</div>
</CardContent>
</Card>
{/* Contact Information Card */}
<Card>
<CardHeader>
<CardTitle>{t('Contact Information')}</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="address_line_1" required>{t('Address Line 1')}</Label>
<Input
id="address_line_1"
required
value={formData.address_line_1}
onChange={(e) => handleChange('address_line_1', e.target.value)}
className={errors.address_line_1 ? 'border-red-500' : ''}
/>
{errors.address_line_1 && <p className="text-red-500 text-xs">{errors.address_line_1}</p>}
</div>
<div className="space-y-2">
<Label htmlFor="address_line_2">{t('Address Line 2')}</Label>
<Input
id="address_line_2"
value={formData.address_line_2}
onChange={(e) => handleChange('address_line_2', e.target.value)}
className={errors.address_line_2 ? 'border-red-500' : ''}
/>
{errors.address_line_2 && <p className="text-red-500 text-xs">{errors.address_line_2}</p>}
</div>
<div className="space-y-2">
<Label htmlFor="city" required>{t('City')}</Label>
<Input
id="city"
required
value={formData.city}
onChange={(e) => handleChange('city', e.target.value)}
className={errors.city ? 'border-red-500' : ''}
/>
{errors.city && <p className="text-red-500 text-xs">{errors.city}</p>}
</div>
<div className="space-y-2">
<Label htmlFor="state" required>{t('State/Province')}</Label>
<Input
id="state"
required
value={formData.state}
onChange={(e) => handleChange('state', e.target.value)}
className={errors.state ? 'border-red-500' : ''}
/>
{errors.state && <p className="text-red-500 text-xs">{errors.state}</p>}
</div>
<div className="space-y-2">
<Label htmlFor="country" required>{t('Country')}</Label>
<Input
id="country"
required
value={formData.country}
onChange={(e) => handleChange('country', e.target.value)}
className={errors.country ? 'border-red-500' : ''}
/>
{errors.country && <p className="text-red-500 text-xs">{errors.country}</p>}
</div>
<div className="space-y-2">
<Label htmlFor="postal_code" required>{t('Postal/Zip Code')}</Label>
<Input
id="postal_code"
value={formData.postal_code}
onChange={(e) => handleChange('postal_code', e.target.value)}
className={errors.postal_code ? 'border-red-500' : ''}
/>
{errors.postal_code && <p className="text-red-500 text-xs">{errors.postal_code}</p>}
</div>
</div>
<div className="mt-6">
<h3 className="text-lg font-medium mb-4">{t('Emergency Contact')}</h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="emergency_contact_name" required>{t('Name')}</Label>
<Input
id="emergency_contact_name"
required
value={formData.emergency_contact_name}
onChange={(e) => handleChange('emergency_contact_name', e.target.value)}
className={errors.emergency_contact_name ? 'border-red-500' : ''}
/>
{errors.emergency_contact_name && <p className="text-red-500 text-xs">{errors.emergency_contact_name}</p>}
</div>
<div className="space-y-2">
<Label htmlFor="emergency_contact_relationship" required>{t('Relationship')}</Label>
<Input
id="emergency_contact_relationship"
required
value={formData.emergency_contact_relationship}
onChange={(e) => handleChange('emergency_contact_relationship', e.target.value)}
className={errors.emergency_contact_relationship ? 'border-red-500' : ''}
/>
{errors.emergency_contact_relationship && <p className="text-red-500 text-xs">{errors.emergency_contact_relationship}</p>}
</div>
<div className="space-y-2">
<Label htmlFor="emergency_contact_number" required>{t('Phone Number')}</Label>
<Input
id="emergency_contact_number"
required
value={formData.emergency_contact_number}
onChange={(e) => handleChange('emergency_contact_number', e.target.value)}
className={errors.emergency_contact_number ? 'border-red-500' : ''}
/>
{errors.emergency_contact_number && <p className="text-red-500 text-xs">{errors.emergency_contact_number}</p>}
</div>
</div>
</div>
</CardContent>
</Card>
{/* Banking Information Card */}
<Card>
<CardHeader>
<CardTitle>{t('Banking Information')}</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="bank_name" required>{t('Bank Name')}</Label>
<Input
id="bank_name"
value={formData.bank_name}
required
onChange={(e) => handleChange('bank_name', e.target.value)}
className={errors.bank_name ? 'border-red-500' : ''}
/>
{errors.bank_name && <p className="text-red-500 text-xs">{errors.bank_name}</p>}
</div>
<div className="space-y-2">
<Label htmlFor="account_holder_name" required>{t('Account Holder Name')}</Label>
<Input
id="account_holder_name"
required
value={formData.account_holder_name}
onChange={(e) => handleChange('account_holder_name', e.target.value)}
className={errors.account_holder_name ? 'border-red-500' : ''}
/>
{errors.account_holder_name && <p className="text-red-500 text-xs">{errors.account_holder_name}</p>}
</div>
<div className="space-y-2">
<Label htmlFor="account_number" required>{t('Account Number')}</Label>
<Input
id="account_number"
value={formData.account_number}
required
onChange={(e) => handleChange('account_number', e.target.value)}
className={errors.account_number ? 'border-red-500' : ''}
/>
{errors.account_number && <p className="text-red-500 text-xs">{errors.account_number}</p>}
</div>
<div className="space-y-2">
<Label htmlFor="bank_identifier_code" required>{t('Bank Identifier Code (BIC/SWIFT)')}</Label>
<Input
id="bank_identifier_code"
required
value={formData.bank_identifier_code}
onChange={(e) => handleChange('bank_identifier_code', e.target.value)}
className={errors.bank_identifier_code ? 'border-red-500' : ''}
/>
{errors.bank_identifier_code && <p className="text-red-500 text-xs">{errors.bank_identifier_code}</p>}
</div>
<div className="space-y-2">
<Label htmlFor="bank_branch" required>{t('Bank Branch')}</Label>
<Input
id="bank_branch"
value={formData.bank_branch}
required
onChange={(e) => handleChange('bank_branch', e.target.value)}
className={errors.bank_branch ? 'border-red-500' : ''}
/>
{errors.bank_branch && <p className="text-red-500 text-xs">{errors.bank_branch}</p>}
</div>
<div className="space-y-2">
<Label htmlFor="tax_payer_id">{t('Tax Payer ID')}</Label>
<Input
id="tax_payer_id"
value={formData.tax_payer_id}
onChange={(e) => handleChange('tax_payer_id', e.target.value)}
className={errors.tax_payer_id ? 'border-red-500' : ''}
/>
{errors.tax_payer_id && <p className="text-red-500 text-xs">{errors.tax_payer_id}</p>}
</div>
<div className="space-y-2">
<Label htmlFor="salary" required>{t('Base Salary')}</Label>
<Input
required
id="salary"
type="number"
step="0.01"
value={formData.salary}
onChange={(e) => handleChange('salary', e.target.value)}
className={errors.salary ? 'border-red-500' : ''}
/>
{errors.salary && <p className="text-red-500 text-xs">{errors.salary}</p>}
</div>
</div>
</CardContent>
</Card>
{/* Documents Card */}
<Card>
<CardHeader>
<CardTitle>{t('Documents')}</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
{formData.documents.map((document: any, index: number) => (
<div key={index} className="border rounded-md p-4 space-y-4">
<div className="flex justify-between items-center">
<h3 className="text-lg font-medium">{t('Document')} #{index + 1}</h3>
{!document.is_required && (
<Button
type="button"
variant="ghost"
size="sm"
onClick={() => removeDocument(index)}
>
<Trash2 className="h-4 w-4 text-red-500" />
</Button>
)}
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor={`document_type_${index}`}>{t('Document Type')} <span className="text-red-500">*</span></Label>
<Select
value={document.document_type_id}
onValueChange={(value) => handleDocumentChange(index, 'document_type_id', value)}
>
<SelectTrigger className={errors[`documents.${index}.document_type_id`] ? 'border-red-500' : ''}>
<SelectValue placeholder={t('Select Document Type')} />
</SelectTrigger>
<SelectContent>
{documentTypes
.filter((type: any) => {
if (type.id.toString() === document.document_type_id) return true;
return !formData.documents.some((d: any) => d.document_type_id === type.id.toString());
})
.map((type: any) => (
<SelectItem key={type.id} value={type.id.toString()}>
{type.name} {type.is_required && <span className="text-red-500">*</span>}
</SelectItem>
))}
</SelectContent>
</Select>
{errors[`documents.${index}.document_type_id`] && (
<p className="text-red-500 text-xs">{errors[`documents.${index}.document_type_id`]}</p>
)}
</div>
<div className="space-y-2">
<Label>{t('File')} <span className="text-red-500">*</span></Label>
<div className="flex flex-col gap-3">
<Input
type="file"
onChange={(e) => handleDocumentFileChange(index, e)}
className={errors[`documents.${index}.file`] ? 'border-red-500' : ''}
/>
<p className="text-xs text-muted-foreground">{t('Max file size: 5MB')}</p>
</div>
{errors[`documents.${index}.file`] && (
<p className="text-red-500 text-xs">{errors[`documents.${index}.file`]}</p>
)}
</div>
<div className="space-y-2">
<Label htmlFor={`document_expiry_${index}`}>{t('Expiry Date')}</Label>
<div className="cursor-pointer" onClick={(e) => { const input = (e.currentTarget as HTMLElement).querySelector('input'); try { (input as any)?.showPicker?.(); } catch { input?.focus(); } }}>
<Input
id={`document_expiry_${index}`}
type="date"
value={document.expiry_date}
onChange={(e) => handleDocumentChange(index, 'expiry_date', e.target.value)}
className={`cursor-pointer ${errors[`documents.${index}.expiry_date`] ? 'border-red-500' : ''}`}
/>
</div>
{errors[`documents.${index}.expiry_date`] && (
<p className="text-red-500 text-xs">{errors[`documents.${index}.expiry_date`]}</p>
)}
</div>
</div>
</div>
))}
<Button
type="button"
variant="outline"
onClick={addDocument}
className="mt-4"
>
<Plus className="h-4 w-4 mr-2" />
{t('Add Document')}
</Button>
</CardContent>
</Card>
{/* Submit Buttons */}
<div className="flex justify-end space-x-4">
<Button
type="button"
variant="outline"
onClick={() => router.get(route('hr.employees.index'))}
>
{t('Cancel')}
</Button>
<Button
type="submit"
// disabled={isSubmitting}
>
{t('Save') }
</Button>
</div>
</form>
</PageTemplate>
);
}