import React, { useState, useEffect } from 'react'; import Toast from '../Components/Toast'; import { Shield, ChevronLeft, Save, History, Calculator, Clock, CheckCircle2, X, Plus, Trash2 } from 'lucide-react'; export default function StaffEdit({ id }) { const isReceptionist = window.__APP_DATA__?.role === 'receptionist'; const basePath = isReceptionist ? '/receptionist' : '/owner'; const [loading, setLoading] = useState(true); const [saving, setSaving] = useState(false); const [branches, setBranches] = useState([]); const [roles, setRoles] = useState([]); const [history, setHistory] = useState([]); const [showHistory, setShowHistory] = useState(false); const [loadingHistory, setLoadingHistory] = useState(false); const [toast, setToast] = useState(null); const [initialCommission, setInitialCommission] = useState({ amount: '', count: '' }); const [commissionChanged, setCommissionChanged] = useState(false); const [periodSelected, setPeriodSelected] = useState(false); const [isAdvanceConfirmOpen, setIsAdvanceConfirmOpen] = useState(false); const [advanceHistory, setAdvanceHistory] = useState([]); const [loadingAdvanceHistory, setLoadingAdvanceHistory] = useState(false); const [formData, setFormData] = useState({ full_name: '', email: '', phone: '', role: 'Trainer', branch_id: '', joining_date: '', status: 'Active', salary_type: 'Fixed', salary_amount: '', cycle_effective_date: '', advance_enabled: false, advance_amount: '', advance_repayment_mode: 'Full Next Month', advance_months: '', commission_enabled: false, commission_amount: '', commission_member_count: '', apply_from: 'this_month', emirates_id: '', emirates_id_expiry: '', emirates_id_reminder_days: 30, salary_reminder_enabled: true, document_expiry_enabled: true, family_members: [], documents: [] }); useEffect(() => { const fetchData = async () => { try { // Fetch Branches and Roles const [bRes, rRes] = await Promise.all([ fetch('/api/branches?status=Active'), fetch('/api/masters/staff_role') ]); const bData = await bRes.json(); const rData = await rRes.json(); setBranches(bData); setRoles(rData.filter(r => r.status === 'Active')); // Fetch Staff Details const sRes = await fetch(`/api/staff/${id}`); const sData = await sRes.json(); if (sData) { const urlParams = new URLSearchParams(window.location.search); const autoGetAdvance = urlParams.get('get_advance') === 'true'; setFormData({ ...formData, full_name: sData.full_name || '', email: sData.email || '', phone: sData.phone || '', role: sData.role || 'Trainer', branch_id: sData.branch_id || '', joining_date: sData.joining_date || '', status: sData.status || 'Active', salary_type: sData.salary_type || 'Monthly', salary_amount: sData.salary_amount || '', cycle_effective_date: sData.cycle_effective_date || '', advance_enabled: autoGetAdvance ? true : !!sData.advance_enabled, advance_amount: sData.advance_amount || '', advance_repayment_mode: sData.advance_repayment_mode || 'Full Next Month', advance_months: sData.advance_months || '', commission_enabled: !!sData.commission_enabled, commission_amount: sData.commission_amount || '', commission_member_count: sData.commission_member_count || '', emirates_id: sData.emirates_id || '', emirates_id_expiry: sData.emirates_id_expiry || '', emirates_id_reminder_days: sData.emirates_id_reminder_days || 30, family_members: sData.family_members && sData.family_members.length > 0 ? sData.family_members : [{ name: '', relation: '', contact: '' }], documents: sData.documents || [], apply_from: '' // Reset to force choice if changed }); setInitialCommission({ amount: sData.commission_amount || '', count: sData.commission_member_count || '' }); } } catch (error) { console.error('Error fetching data:', error); setToast({ message: 'Error loading staff details. Please refresh.', type: 'error' }); } finally { setLoading(false); } }; fetchData(); fetchAdvanceHistory(); }, [id]); const fetchAdvanceHistory = async () => { setLoadingAdvanceHistory(true); try { const res = await fetch(`/api/staff/${id}/advance-history`); const data = await res.json(); setAdvanceHistory(data); } catch (error) { console.error('Error fetching advance history:', error); } finally { setLoadingAdvanceHistory(false); } }; const fetchHistory = async () => { setLoadingHistory(true); setShowHistory(true); try { const res = await fetch(`/api/staff/${id}/commission-history`); const data = await res.json(); setHistory(data); } catch (error) { console.error('Error fetching history:', error); } finally { setLoadingHistory(false); } }; const handleChange = (e) => { const { name, value, type, checked } = e.target; const newFormData = { ...formData, [name]: type === 'checkbox' ? checked : value }; // Detect commission changes if (name === 'commission_amount' || name === 'commission_member_count' || name === 'commission_enabled') { const hasChanged = newFormData.commission_enabled !== (initialCommission.enabled ?? false) || newFormData.commission_amount !== initialCommission.amount || newFormData.commission_member_count !== initialCommission.count; setCommissionChanged(hasChanged); if (!hasChanged) setPeriodSelected(false); } setFormData(newFormData); }; const handleDocumentChange = (index, e) => { const { name, value, files } = e.target; const newDocs = [...formData.documents]; if (name === 'file') { newDocs[index][name] = files[0]; } else { newDocs[index][name] = value; } setFormData({ ...formData, documents: newDocs }); }; const addDocumentRow = () => { setFormData({ ...formData, documents: [...formData.documents, { name: '', document_number: '', expiry_date: '', reminder_days: 30, file: null }] }); }; const removeDocumentRow = (index) => { const newDocs = formData.documents.filter((_, i) => i !== index); setFormData({ ...formData, documents: newDocs }); }; const handleFamilyMemberChange = (index, e) => { const { name, value } = e.target; const newMembers = [...formData.family_members]; newMembers[index][name] = value; setFormData({ ...formData, family_members: newMembers }); }; const addFamilyMemberRow = () => { setFormData({ ...formData, family_members: [...formData.family_members, { name: '', relation: '', contact: '' }] }); }; const removeFamilyMemberRow = (index) => { const newMembers = formData.family_members.filter((_, i) => i !== index); setFormData({ ...formData, family_members: newMembers }); }; const handleSave = async (e) => { if (e) e.preventDefault(); // Validation for commission change if (commissionChanged && !formData.apply_from) { setToast({ message: 'Please select when to apply commission changes (This Month or Next Month).', type: 'error' }); // Scroll to commission section const el = document.getElementById('commission-section'); if (el) el.scrollIntoView({ behavior: 'smooth' }); return; } // Branch Start Date Validation - REMOVED AS PER USER REQUEST /* const selectedBranch = branches.find(b => b.id == formData.branch_id); if (selectedBranch && formData.joining_date && selectedBranch.operational_start_date) { if (formData.joining_date < selectedBranch.operational_start_date) { setToast({ message: `Error: Joining date (${formData.joining_date}) cannot be before branch start date (${selectedBranch.operational_start_date}).`, type: 'error' }); return; } } */ // Phone Validation if (formData.phone && !/^(\+91|91|0)?[6-9]\d{9}$|^(\+971|971|0)?5[024568]\d{7}$/.test(formData.phone.replace(/[\s-]/g, ''))) { setToast({ message: 'Error: Invalid Phone format. Only Indian (+91) and UAE (+971) numbers are allowed.', type: 'error' }); return; } // Family Contact Validation for (let i = 0; i < formData.family_members.length; i++) { const member = formData.family_members[i]; if (member.contact && !/^(\+91|91|0)?[6-9]\d{9}$|^(\+971|971|0)?5[024568]\d{7}$/.test(member.contact.replace(/[\s-]/g, ''))) { setToast({ message: `Error: Invalid Family Contact format for ${member.name || (i+1)}. Only Indian (+91) and UAE (+971) numbers are allowed.`, type: 'error' }); return; } } // Document Validations for (let i = 0; i < formData.documents.length; i++) { const doc = formData.documents[i]; if (doc.name || doc.document_number || doc.expiry_date || doc.file) { if (!doc.name) { setToast({ message: `Document ${i + 1}: Name is required.`, type: 'error' }); return; } if (!doc.document_number) { setToast({ message: `Document ${i + 1}: Document Number is required.`, type: 'error' }); return; } if (!doc.expiry_date) { setToast({ message: `Document ${i + 1}: Expiry Date is required.`, type: 'error' }); return; } // Only require file if it's a new document row (no id) and no existing path if (!doc.id && !doc.file && !doc.path) { setToast({ message: `Document ${i + 1}: Please upload a file.`, type: 'error' }); return; } } } setSaving(true); const csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content'); const data = new FormData(); Object.keys(formData).forEach(key => { if (key === 'documents') { formData.documents.forEach((doc, index) => { if (doc.id) data.append(`documents[${index}][id]`, doc.id); data.append(`documents[${index}][name]`, doc.name); data.append(`documents[${index}][document_number]`, doc.document_number || ''); data.append(`documents[${index}][expiry_date]`, doc.expiry_date || ''); data.append(`documents[${index}][reminder_days]`, doc.reminder_days || 30); if (doc.file) { data.append(`documents[${index}][file]`, doc.file); } }); } else if (key === 'family_members') { formData.family_members.forEach((member, index) => { data.append(`family_members[${index}][name]`, member.name); data.append(`family_members[${index}][relation]`, member.relation); data.append(`family_members[${index}][contact]`, member.contact); }); } else if (formData[key] !== null && formData[key] !== undefined) { let value = formData[key]; if (typeof value === 'boolean') { value = value ? '1' : '0'; } data.append(key, value); } }); // Use POST with _method=PUT for multipart support in Laravel data.append('_method', 'PUT'); try { const response = await fetch(`/api/staff/${id}`, { method: 'POST', headers: { 'Accept': 'application/json', 'X-CSRF-TOKEN': csrfToken }, body: data }); if (response.ok) { setToast({ message: 'Staff updated successfully!', type: 'success' }); setTimeout(() => window.location.href = `${basePath}/staff`, 1500); } else { const errorData = await response.json().catch(() => ({})); if (errorData.errors) { const message = Object.entries(errorData.errors) .map(([field, msgs]) => `• ${field.replace(/_/g, ' ')}: ${msgs.join(', ')}`) .join('\n'); setToast({ message: 'Validation Error:\n' + message, type: 'error' }); } else { setToast({ message: 'Error updating staff details: ' + (errorData.message || response.statusText), type: 'error' }); } } } catch (error) { console.error('API Error:', error); setToast({ message: 'Failed to update staff.', type: 'error' }); } finally { setSaving(false); } }; if (loading) return
Loading details...
; return ( <> {toast && setToast(null)} />}
{/* Top Actions */}
{/* Card 1: Basic Details */}

Basic Details

{/* Card 2: Salary Details */}

Salary Details

{/* Card 3: Salary Advance */}

Salary Advance

{!formData.advance_enabled ? (
) : (
Advance Configuration Active
{/* Advance History Section */} {advanceHistory.length > 0 && (
Advance History
Due: {advanceHistory .filter(h => h.status === 'Pending') .reduce((sum, h) => sum + (parseFloat(h.advance_amount) - parseFloat(h.paid_amount || 0)), 0) .toLocaleString()} AED Total Paid: {advanceHistory.reduce((sum, h) => sum + parseFloat(h.paid_amount || 0), 0).toLocaleString()} AED
{advanceHistory.map((h, i) => (
{h.status === 'Closed' ? : }

{h.advance_amount.toLocaleString()} AED

Taken on {new Date(h.created_at).toLocaleDateString()}

{parseFloat(h.paid_amount || 0).toLocaleString()} Paid

{(parseFloat(h.advance_amount) - parseFloat(h.paid_amount || 0)).toLocaleString()} Pending

))}
)}
{formData.advance_repayment_mode === 'Divide by Months' && (

Monthly Deduction

AED {((parseFloat(formData.advance_amount) || 0) / (parseInt(formData.advance_months) || 1)).toFixed(2)}

)}
)}
{/* Card 4: Commission Settings */}
Commission Settings {id !== 'new' && ( )}
Enable per-person commission
{formData.commission_enabled && (
{/* Instant Total Calculation */}

Total Monthly Commission

Calculated instantly (Members × Rate)

AED {( (parseFloat(formData.commission_member_count) || 0) * (parseFloat(formData.commission_amount) || 0) ).toFixed(2)}
{/* Apply Period Selection */}
)}
{/* Documentation Card */}
Documentation
{formData.documents.map((doc, index) => (
handleDocumentChange(index, e)} className="w-full bg-white border-none rounded-xl px-4 py-3 text-sm focus:ring-2 focus:ring-blue-500 transition-all font-medium" placeholder="Ex: Visa, Emirates ID, Passport" />
handleDocumentChange(index, e)} className="w-full bg-white border-none rounded-xl px-4 py-3 text-sm focus:ring-2 focus:ring-blue-500 transition-all font-medium" placeholder="Number / ID" />
handleDocumentChange(index, e)} className="w-full bg-white border-none rounded-xl px-4 py-3 text-sm focus:ring-2 focus:ring-blue-500 transition-all font-medium" />
handleDocumentChange(index, e)} className="w-full bg-white border-none rounded-xl px-4 py-3 text-sm focus:ring-2 focus:ring-blue-500 transition-all font-medium" placeholder="30" />
handleDocumentChange(index, e)} className="flex-1 bg-white border-none rounded-xl px-4 py-2 text-xs focus:ring-2 focus:ring-blue-500 transition-all font-medium file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-xs file:font-black file:bg-blue-50 file:text-blue-700 hover:file:bg-blue-100" /> {doc.path && ( File Exists )}
))} {formData.documents.length === 0 && (
No documents added yet.
)}
{/* Family Members Card */}
Family Members
{formData.family_members.map((member, index) => (
{formData.family_members.length > 1 && ( )}
handleFamilyMemberChange(index, e)} className="w-full bg-white border-none rounded-xl px-4 py-3 text-sm focus:ring-2 focus:ring-emerald-500 transition-all font-medium" placeholder="e.g. Jane Doe" />
handleFamilyMemberChange(index, e)} className="w-full bg-white border-none rounded-xl px-4 py-3 text-sm focus:ring-2 focus:ring-emerald-500 transition-all font-medium" placeholder="e.g. Spouse" />
handleFamilyMemberChange(index, e)} className="w-full bg-white border-none rounded-xl px-4 py-3 text-sm focus:ring-2 focus:ring-emerald-500 transition-all font-medium" placeholder="05XXXXXXXX" />
))} {formData.family_members.length === 0 && (
No family members added yet.
)}
{/* Commission History Modal */} {showHistory && (

Commission History

Past member counts and commissions

{loadingHistory ? (
Loading history...
) : history.length > 0 ? (
{history.map((row) => ( ))}
Month Members Per Head Total
{new Date(row.effective_month + "-01").toLocaleString('default', { month: 'long', year: 'numeric' })} {row.member_count} AED {row.amount_per_head} AED {row.total_amount}
) : (

No history found for this staff member.

)}
)} {/* Advance Confirmation Modal */} {isAdvanceConfirmOpen && (

Confirm Advance Removal

Are you sure you want to disable the current advance? This will stop future deductions. {advanceHistory.find(h => h.status === 'Active') && (

Warning: There is a pending balance of {(advanceHistory.find(h => h.status === 'Active').advance_amount - advanceHistory.find(h => h.status === 'Active').paid_amount).toLocaleString()} AED.

)}

)} ); }