2026-03-14 17:13:13 +05:30

954 lines
62 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 <div className="p-10 text-center font-medium text-gray-400">Loading details...</div>;
return (
<>
{toast && <Toast message={toast.message} type={toast.type} onClose={() => setToast(null)} />}
<form onSubmit={handleSave} className="p-8 max-w-5xl mx-auto space-y-8">
{/* Top Actions */}
<div className="flex items-center justify-between underline-offset-4">
<button type="button" onClick={() => window.location.href = `${basePath}/staff`} className="flex items-center gap-2 text-sm font-bold text-gray-500 hover:text-gray-900 transition-colors">
<ChevronLeft size={18} />
<span>Back to Staff List</span>
</button>
<button
type="submit"
disabled={saving}
className="flex items-center gap-2 px-6 py-2.5 bg-red-500 text-white rounded-xl font-bold text-sm hover:bg-red-600 transition-all shadow-lg shadow-red-100 disabled:opacity-50"
>
<Save size={18} />
<span>{saving ? 'Saving...' : 'Update Staff Details'}</span>
</button>
</div>
<div className="grid grid-cols-1 gap-8">
{/* Card 1: Basic Details */}
<div className="bg-white rounded-2xl border border-gray-100 shadow-sm overflow-hidden">
<div className="p-6 border-b border-gray-50 bg-gray-50/30">
<h3 className="text-lg font-bold text-gray-900">Basic Details</h3>
</div>
<div className="p-8 grid grid-cols-2 gap-6">
<div>
<label className="block text-xs font-bold text-gray-400 uppercase tracking-wider mb-2">Full Name *</label>
<input required type="text" name="full_name" value={formData.full_name} onChange={handleChange} className="w-full bg-gray-50 border-none rounded-xl px-4 py-3 text-sm focus:ring-2 focus:ring-red-500 transition-all font-medium" />
</div>
<div>
<label className="block text-xs font-bold text-gray-400 uppercase tracking-wider mb-2">Email *</label>
<input required type="email" name="email" value={formData.email} onChange={handleChange} className="w-full bg-gray-50 border-none rounded-xl px-4 py-3 text-sm focus:ring-2 focus:ring-red-500 transition-all font-medium" />
</div>
<div>
<label className="block text-xs font-bold text-gray-400 uppercase tracking-wider mb-2">Phone (India/UAE)</label>
<input type="text" name="phone" value={formData.phone} onChange={handleChange} className="w-full bg-gray-50 border-none rounded-xl px-4 py-3 text-sm focus:ring-2 focus:ring-red-500 transition-all font-medium" placeholder="Ex: +91 98765 43210 or +971 50 123 4567" />
</div>
<div>
<label className="block text-xs font-bold text-gray-400 uppercase tracking-wider mb-2">Role *</label>
<select name="role" value={formData.role} onChange={handleChange} className="w-full bg-gray-50 border-none rounded-xl px-4 py-3 text-sm focus:ring-2 focus:ring-red-500 transition-all font-medium appearance-none">
{roles.length === 0 && <option value="">No roles configured</option>}
{roles.map(r => (
<option key={r.id} value={r.name}>{r.name}</option>
))}
</select>
</div>
{!isReceptionist && (
<div>
<label className="block text-xs font-bold text-gray-400 uppercase tracking-wider mb-2">Branch *</label>
<select name="branch_id" value={formData.branch_id} onChange={handleChange} className="w-full bg-gray-50 border-none rounded-xl px-4 py-3 text-sm focus:ring-2 focus:ring-red-500 transition-all font-medium appearance-none">
<option value="">Select Branch...</option>
{branches.map(branch => (
<option key={branch.id} value={branch.id}>{branch.name}</option>
))}
</select>
</div>
)}
<div>
<label className="block text-xs font-bold text-gray-400 uppercase tracking-wider mb-2">Joining Date *</label>
<input required type="date" name="joining_date" value={formData.joining_date} onChange={handleChange} className="w-full bg-gray-50 border-none rounded-xl px-4 py-3 text-sm focus:ring-2 focus:ring-red-500 transition-all font-medium" />
</div>
<div>
<label className="block text-xs font-bold text-gray-400 uppercase tracking-wider mb-2">Status</label>
<select name="status" value={formData.status} onChange={handleChange} className="w-full bg-gray-50 border-none rounded-xl px-4 py-3 text-sm focus:ring-2 focus:ring-red-500 transition-all font-medium appearance-none">
<option value="Active">Active</option>
<option value="Inactive">Inactive</option>
</select>
</div>
</div>
</div>
{/* Card 2: Salary Details */}
<div className="bg-white rounded-2xl border border-gray-100 shadow-sm overflow-hidden">
<div className="p-6 border-b border-gray-50 bg-gray-50/30">
<h3 className="text-lg font-bold text-gray-900">Salary Details</h3>
</div>
<div className="p-8 grid grid-cols-2 gap-6">
<div>
<label className="block text-xs font-bold text-gray-400 uppercase tracking-wider mb-2">Salary Type</label>
<select name="salary_type" value={formData.salary_type || 'Monthly'} onChange={handleChange} className="w-full bg-gray-50 border-none rounded-xl px-4 py-3 text-sm focus:ring-2 focus:ring-red-500 transition-all font-medium appearance-none">
<option value="Monthly">Monthly</option>
<option value="Daily">Daily</option>
<option value="Weekly">Weekly</option>
</select>
</div>
<div>
<label className="block text-xs font-bold text-gray-400 uppercase tracking-wider mb-2">Amount (AED) *</label>
<input required type="number" name="salary_amount" value={formData.salary_amount || ''} onChange={handleChange} className="w-full bg-gray-50 border-none rounded-xl px-4 py-3 text-sm focus:ring-2 focus:ring-red-500 transition-all font-medium" placeholder="0.00" />
</div>
</div>
</div>
{/* Card 3: Salary Advance */}
<div className="bg-white rounded-2xl border border-gray-100 shadow-sm overflow-hidden">
<div className="p-6 border-b border-gray-50 bg-gray-50/30">
<h3 className="text-lg font-bold text-gray-900">Salary Advance</h3>
</div>
<div className="p-8 space-y-6">
{!formData.advance_enabled ? (
<div className="py-8 text-center bg-gray-50 rounded-2xl border-2 border-dashed border-gray-100">
<button
type="button"
onClick={() => setFormData({...formData, advance_enabled: true})}
className="px-6 py-2.5 bg-white border border-gray-200 text-gray-700 rounded-xl text-sm font-bold hover:bg-gray-50 transition-all shadow-sm flex items-center gap-2 mx-auto"
>
Get Advance
</button>
</div>
) : (
<div className="animate-in slide-in-from-top-2 duration-300 space-y-6">
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<div className="w-2 h-2 rounded-full bg-orange-500 shadow-[0_0_10px_rgba(249,115,22,0.5)]" />
<span className="text-gray-900 font-bold text-sm">Advance Configuration Active</span>
</div>
<button
type="button"
onClick={() => setIsAdvanceConfirmOpen(true)}
className="text-xs font-bold text-red-500 hover:text-red-600 transition-colors uppercase tracking-widest"
>
Remove Advance
</button>
</div>
{/* Advance History Section */}
{advanceHistory.length > 0 && (
<div className="bg-gray-50/50 rounded-2xl border border-gray-100 overflow-hidden">
<div className="px-4 py-3 bg-gray-100/50 border-b border-gray-100 flex items-center justify-between">
<span className="text-[10px] font-black text-gray-400 uppercase tracking-widest text-[#101828]">Advance History</span>
<div className="flex gap-4">
<span className="text-[9px] font-bold text-gray-400">
Due: {advanceHistory
.filter(h => h.status === 'Pending')
.reduce((sum, h) => sum + (parseFloat(h.advance_amount) - parseFloat(h.paid_amount || 0)), 0)
.toLocaleString()} AED
</span>
<span className="text-[9px] font-bold text-emerald-500">
Total Paid: {advanceHistory.reduce((sum, h) => sum + parseFloat(h.paid_amount || 0), 0).toLocaleString()} AED
</span>
</div>
</div>
<div className="divide-y divide-gray-100 max-h-48 overflow-y-auto">
{advanceHistory.map((h, i) => (
<div key={i} className="px-4 py-3 flex items-center justify-between hover:bg-white transition-colors">
<div className="flex items-center gap-3">
<div className={`w-8 h-8 rounded-lg flex items-center justify-center font-bold text-[10px] ${h.status === 'Closed' ? 'bg-emerald-50 text-emerald-600' : 'bg-orange-50 text-orange-600'}`}>
{h.status === 'Closed' ? <CheckCircle2 size={14} /> : <Clock size={14} />}
</div>
<div>
<p className="text-xs font-bold text-gray-900">{h.advance_amount.toLocaleString()} AED</p>
<p className="text-[10px] text-gray-400 font-medium">Taken on {new Date(h.created_at).toLocaleDateString()}</p>
</div>
</div>
<div className="text-right">
<p className="text-[10px] font-black uppercase tracking-widest text-emerald-500">{parseFloat(h.paid_amount || 0).toLocaleString()} Paid</p>
<p className="text-[9px] font-bold text-gray-300">{(parseFloat(h.advance_amount) - parseFloat(h.paid_amount || 0)).toLocaleString()} Pending</p>
</div>
</div>
))}
</div>
</div>
)}
<div className="grid grid-cols-2 gap-6 pt-4">
<div>
<label className="block text-xs font-bold text-gray-400 uppercase tracking-wider mb-2">Advance Amount (AED)</label>
<input type="number" name="advance_amount" value={formData.advance_amount || ''} onChange={handleChange} className="w-full bg-gray-50 border-none rounded-xl px-4 py-3 text-sm focus:ring-2 focus:ring-red-500 transition-all font-medium" placeholder="e.g. 3000" />
</div>
<div>
<label className="block text-xs font-bold text-gray-400 uppercase tracking-wider mb-2 text-center sm:text-left">Repayment Mode</label>
<div className="flex gap-2">
<button
type="button"
onClick={() => setFormData({...formData, advance_repayment_mode: 'Full Next Month'})}
className={`flex-1 py-3 text-sm font-bold rounded-xl border transition-all ${formData.advance_repayment_mode === 'Full Next Month' ? 'border-orange-500 bg-orange-50/50 text-orange-600' : 'border-gray-200 text-gray-500 bg-white'}`}
>
Full Next Month
</button>
<button
type="button"
onClick={() => setFormData({...formData, advance_repayment_mode: 'Divide by Months'})}
className={`flex-1 py-3 text-sm font-bold rounded-xl border transition-all ${formData.advance_repayment_mode === 'Divide by Months' ? 'border-orange-500 bg-orange-50/50 text-orange-600' : 'border-gray-200 text-gray-500 bg-white'}`}
>
Divide by Months
</button>
</div>
</div>
</div>
{formData.advance_repayment_mode === 'Divide by Months' && (
<div className="grid grid-cols-2 gap-6 p-6 bg-gray-50 rounded-2xl border border-gray-100 animate-in zoom-in-95 duration-200">
<div>
<label className="block text-xs font-bold text-gray-400 uppercase tracking-wider mb-2">Number of Months</label>
<input
type="number"
name="advance_months"
value={formData.advance_months || ''}
onChange={handleChange}
className="w-full bg-white border-none rounded-xl px-4 py-3 text-sm focus:ring-2 focus:ring-red-500 transition-all font-medium"
placeholder="e.g. 3"
/>
</div>
<div className="flex flex-col justify-center">
<p className="text-[10px] font-bold text-gray-400 uppercase tracking-widest mb-1">Monthly Deduction</p>
<p className="text-xl font-black text-orange-600">
AED {((parseFloat(formData.advance_amount) || 0) / (parseInt(formData.advance_months) || 1)).toFixed(2)}
</p>
</div>
</div>
)}
</div>
)}
</div>
</div>
{/* Card 4: Commission Settings */}
<div id="commission-section" className={`bg-white rounded-2xl border shadow-sm overflow-hidden text-purple-600 transition-all ${commissionChanged && !formData.apply_from ? 'border-purple-300 ring-4 ring-purple-50' : 'border-gray-100'}`}>
<div className="p-6 border-b border-gray-50 bg-gray-50/30 font-bold flex items-center justify-between">
<span>Commission Settings</span>
{id !== 'new' && (
<button
type="button"
onClick={fetchHistory}
className="flex items-center gap-2 px-4 py-2 bg-purple-50 text-purple-600 rounded-xl text-xs font-bold hover:bg-purple-100 transition-all border border-purple-100"
>
<History size={14} />
<span>View History</span>
</button>
)}
</div>
<div className="p-8 space-y-6">
<div className="flex items-center gap-3">
<input
type="checkbox"
name="commission_enabled"
checked={formData.commission_enabled}
onChange={handleChange}
className="w-5 h-5 rounded border-gray-300 text-purple-600 focus:ring-purple-500 cursor-pointer"
/>
<span className="text-[#344054] font-medium text-sm">Enable per-person commission</span>
</div>
{formData.commission_enabled && (
<div className="space-y-6 animate-in slide-in-from-top-2 duration-300">
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label className="block text-xs font-bold text-gray-400 uppercase tracking-wider mb-2">Number of Members</label>
<div className="relative">
<input
type="number"
name="commission_member_count"
value={formData.commission_member_count || ''}
onChange={handleChange}
className="w-full bg-gray-50 border-none rounded-xl px-4 py-3 text-sm focus:ring-2 focus:ring-purple-500 transition-all font-medium pl-10"
placeholder="e.g. 10"
/>
<div className="absolute left-3 top-3.5 text-gray-400">
<Calculator size={16} />
</div>
</div>
</div>
<div>
<label className="block text-xs font-bold text-gray-400 uppercase tracking-wider mb-2">Amount per Person (AED)</label>
<input
type="number"
name="commission_amount"
value={formData.commission_amount || ''}
onChange={handleChange}
className="w-full bg-gray-50 border-none rounded-xl px-4 py-3 text-sm focus:ring-2 focus:ring-purple-500 transition-all font-medium"
placeholder="e.g. 50"
/>
</div>
</div>
{/* Instant Total Calculation */}
<div className="p-4 bg-purple-50 rounded-2xl border border-purple-100 flex items-center justify-between">
<div className="flex items-center gap-3">
<div className="p-2 bg-white rounded-xl shadow-sm text-purple-500">
<Calculator size={18} />
</div>
<div>
<p className="text-sm font-bold text-gray-900">Total Monthly Commission</p>
<p className="text-xs text-gray-400 font-medium">Calculated instantly (Members × Rate)</p>
</div>
</div>
<div className="text-xl font-black text-purple-600">
AED {( (parseFloat(formData.commission_member_count) || 0) * (parseFloat(formData.commission_amount) || 0) ).toFixed(2)}
</div>
</div>
{/* Apply Period Selection */}
<div className="space-y-3">
<label className="block text-xs font-bold text-gray-400 uppercase tracking-wider">Start Applying From</label>
<div className="flex gap-4">
<button
type="button"
onClick={() => setFormData({...formData, apply_from: 'this_month'})}
className={`flex-1 p-4 rounded-2xl border-2 transition-all flex flex-col gap-1 items-start ${formData.apply_from === 'this_month' ? 'border-purple-500 bg-purple-50/50 text-purple-700' : 'border-gray-100 bg-gray-50 text-gray-500 hover:border-gray-200'}`}
>
<div className="flex items-center gap-2 font-bold text-sm">
{formData.apply_from === 'this_month' ? <CheckCircle2 size={16} /> : <Clock size={16} />}
<span>This Month</span>
</div>
<span className="text-[10px] opacity-70">Will apply to {new Date().toLocaleString('default', { month: 'long', year: 'numeric' })}</span>
</button>
<button
type="button"
onClick={() => setFormData({...formData, apply_from: 'next_month'})}
className={`flex-1 p-4 rounded-2xl border-2 transition-all flex flex-col gap-1 items-start ${formData.apply_from === 'next_month' ? 'border-purple-500 bg-purple-50/50 text-purple-700' : 'border-gray-100 bg-gray-50 text-gray-500 hover:border-gray-200'}`}
>
<div className="flex items-center gap-2 font-bold text-sm">
{formData.apply_from === 'next_month' ? <CheckCircle2 size={16} /> : <Clock size={16} />}
<span>Next Month</span>
</div>
<span className="text-[10px] opacity-70">Will apply from {new Date(new Date().setMonth(new Date().getMonth() + 1)).toLocaleString('default', { month: 'long', year: 'numeric' })}</span>
</button>
</div>
</div>
</div>
)}
</div>
</div>
<div className="grid grid-cols-2 gap-8">
{/* Documentation Card */}
<div className="bg-white rounded-2xl border border-gray-100 shadow-sm overflow-hidden text-blue-600">
<div className="p-6 border-b border-gray-50 bg-gray-50/30 flex items-center justify-between font-bold">
<span>Documentation</span>
<button
type="button"
onClick={addDocumentRow}
className="flex items-center gap-2 px-3 py-1.5 bg-blue-50 text-blue-600 rounded-lg text-xs hover:bg-blue-100 transition-all border border-blue-100"
>
<Plus size={14} />
<span>Add Document</span>
</button>
</div>
<div className="p-8 space-y-8">
{formData.documents.map((doc, index) => (
<div key={index} className="relative p-6 bg-gray-50 rounded-2xl border border-gray-100 animate-in slide-in-from-top-2 duration-300">
<button
type="button"
onClick={() => removeDocumentRow(index)}
className="absolute top-4 right-4 text-red-400 hover:text-red-600 transition-colors"
>
<Trash2 size={18} />
</button>
<div className="grid grid-cols-2 gap-6">
<div>
<label className="block text-xs font-bold text-gray-400 uppercase tracking-wider mb-2">Document Name</label>
<input
type="text"
name="name"
value={doc.name}
onChange={(e) => 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"
/>
</div>
<div>
<label className="block text-xs font-bold text-gray-400 uppercase tracking-wider mb-2">Document Number</label>
<input
type="text"
name="document_number"
value={doc.document_number || ''}
onChange={(e) => 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"
/>
</div>
<div>
<label className="block text-xs font-bold text-gray-400 uppercase tracking-wider mb-2">Expiry Date</label>
<input
type="date"
name="expiry_date"
value={doc.expiry_date || ''}
onChange={(e) => 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"
/>
</div>
<div>
<label className="block text-xs font-bold text-gray-400 uppercase tracking-wider mb-2">Days Before</label>
<input
type="number"
name="reminder_days"
value={doc.reminder_days || 30}
onChange={(e) => 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"
/>
</div>
<div className="col-span-2">
<label className="block text-xs font-bold text-gray-400 uppercase tracking-wider mb-2">
{doc.path ? 'Update Document (Keep empty to retain existing)' : 'Upload Document'}
</label>
<div className="flex items-center gap-4">
<input
type="file"
name="file"
onChange={(e) => 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 && (
<span className="text-[10px] font-bold text-emerald-500 bg-emerald-50 px-3 py-1.5 rounded-lg flex items-center gap-1">
<CheckCircle2 size={12} />
File Exists
</span>
)}
</div>
</div>
</div>
</div>
))}
{formData.documents.length === 0 && (
<div className="text-center py-6 text-gray-400 font-medium italic">
No documents added yet.
</div>
)}
</div>
</div>
{/* Family Members Card */}
<div className="bg-white rounded-2xl border border-gray-100 shadow-sm overflow-hidden text-emerald-600">
<div className="p-6 border-b border-gray-50 bg-gray-50/30 flex items-center justify-between font-bold">
<span>Family Members</span>
<button
type="button"
onClick={addFamilyMemberRow}
className="flex items-center gap-2 px-3 py-1.5 bg-emerald-50 text-emerald-600 rounded-lg text-xs hover:bg-emerald-100 transition-all border border-emerald-100"
>
<Plus size={14} />
<span>Add Member</span>
</button>
</div>
<div className="p-8 space-y-6">
{formData.family_members.map((member, index) => (
<div key={index} className="relative p-6 bg-gray-50 rounded-2xl border border-gray-100 animate-in slide-in-from-top-2 duration-300">
{formData.family_members.length > 1 && (
<button
type="button"
onClick={() => removeFamilyMemberRow(index)}
className="absolute top-4 right-4 text-red-400 hover:text-red-600 transition-colors"
>
<Trash2 size={18} />
</button>
)}
<div className="space-y-4">
<div>
<label className="block text-xs font-bold text-gray-400 uppercase tracking-wider mb-2">Member Name</label>
<input
type="text"
name="name"
value={member.name}
onChange={(e) => 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"
/>
</div>
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block text-xs font-bold text-gray-400 uppercase tracking-wider mb-2">Relation</label>
<input
type="text"
name="relation"
value={member.relation}
onChange={(e) => 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"
/>
</div>
<div>
<label className="block text-xs font-bold text-gray-400 uppercase tracking-wider mb-2">Contact Number</label>
<input
type="text"
name="contact"
value={member.contact}
onChange={(e) => 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"
/>
</div>
</div>
</div>
</div>
))}
{formData.family_members.length === 0 && (
<div className="text-center py-6 text-gray-400 font-medium italic">
No family members added yet.
</div>
)}
</div>
</div>
</div>
</div>
</form>
{/* Commission History Modal */}
{showHistory && (
<div className="fixed inset-0 z-[60] flex items-center justify-center p-4 bg-black/60 backdrop-blur-sm animate-in fade-in duration-200">
<div className="bg-white rounded-[2.5rem] w-full max-w-2xl max-h-[85vh] overflow-hidden flex flex-col shadow-2xl animate-in zoom-in-95 duration-200">
<div className="p-8 flex items-center justify-between border-b border-gray-100">
<div>
<h3 className="text-xl font-bold text-gray-900">Commission History</h3>
<p className="text-sm text-gray-400 font-medium">Past member counts and commissions</p>
</div>
<button
onClick={() => setShowHistory(false)}
className="w-10 h-10 flex items-center justify-center rounded-full hover:bg-gray-100 text-gray-400 hover:text-gray-900 transition-all"
>
<X size={20} />
</button>
</div>
<div className="flex-1 overflow-auto p-8">
{loadingHistory ? (
<div className="text-center py-10 font-medium text-gray-400">Loading history...</div>
) : history.length > 0 ? (
<div className="overflow-hidden border border-gray-100 rounded-2xl">
<table className="w-full text-left border-collapse">
<thead>
<tr className="bg-gray-50/50">
<th className="px-6 py-4 text-[10px] font-bold text-gray-400 uppercase tracking-wider">Month</th>
<th className="px-6 py-4 text-[10px] font-bold text-gray-400 uppercase tracking-wider text-center">Members</th>
<th className="px-6 py-4 text-[10px] font-bold text-gray-400 uppercase tracking-wider text-right">Per Head</th>
<th className="px-6 py-4 text-[10px] font-bold text-gray-400 uppercase tracking-wider text-right">Total</th>
</tr>
</thead>
<tbody className="divide-y divide-gray-50">
{history.map((row) => (
<tr key={row.id} className="hover:bg-gray-50/30 transition-colors">
<td className="px-6 py-4">
<span className="text-sm font-bold text-gray-900 capitalize">
{new Date(row.effective_month + "-01").toLocaleString('default', { month: 'long', year: 'numeric' })}
</span>
</td>
<td className="px-6 py-4 text-center">
<span className="px-3 py-1 bg-purple-50 text-purple-600 text-xs font-black rounded-lg">
{row.member_count}
</span>
</td>
<td className="px-6 py-4 text-right text-sm font-semibold text-gray-500">
AED {row.amount_per_head}
</td>
<td className="px-6 py-4 text-right">
<span className="text-sm font-bold text-emerald-600">AED {row.total_amount}</span>
</td>
</tr>
))}
</tbody>
</table>
</div>
) : (
<div className="text-center py-20 bg-gray-50 rounded-3xl border-2 border-dashed border-gray-100">
<p className="text-gray-400 font-medium">No history found for this staff member.</p>
</div>
)}
</div>
<div className="p-8 border-t border-gray-100 bg-gray-50/30 flex justify-end">
<button
onClick={() => setShowHistory(false)}
className="px-8 py-3 bg-gray-900 text-white rounded-xl text-sm font-bold hover:bg-black transition-all shadow-lg shadow-gray-200"
>
Close History
</button>
</div>
</div>
</div>
)}
{/* Advance Confirmation Modal */}
{isAdvanceConfirmOpen && (
<div className="fixed inset-0 z-[70] flex items-center justify-center p-4 bg-black/60 backdrop-blur-sm animate-in fade-in duration-200">
<div className="bg-white rounded-[2rem] w-full max-w-md shadow-2xl border border-gray-100 overflow-hidden animate-in zoom-in-95 duration-200">
<div className="p-8 text-center">
<div className="w-16 h-16 bg-red-50 text-red-500 rounded-2xl flex items-center justify-center mx-auto mb-6">
<Shield size={32} />
</div>
<h3 className="text-xl font-bold text-gray-900">Confirm Advance Removal</h3>
<p className="text-sm text-gray-400 font-medium mt-2 mb-8">
Are you sure you want to disable the current advance? This will stop future deductions.
{advanceHistory.find(h => h.status === 'Active') && (
<div className="mt-4 p-4 bg-orange-50 rounded-xl border border-orange-100">
<p className="text-xs font-bold text-orange-700">
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.
</p>
</div>
)}
</p>
<div className="flex gap-4">
<button
onClick={() => setIsAdvanceConfirmOpen(false)}
className="flex-1 px-6 py-3 bg-gray-100 text-gray-600 rounded-xl font-bold text-sm hover:bg-gray-200 transition-all uppercase tracking-widest"
>
Cancel
</button>
<button
onClick={() => {
setFormData({...formData, advance_enabled: false, advance_amount: '', advance_months: ''});
setIsAdvanceConfirmOpen(false);
}}
className="flex-1 px-6 py-3 bg-red-500 text-white rounded-xl font-bold text-sm hover:bg-red-600 transition-all uppercase tracking-widest shadow-lg shadow-red-100"
>
Remove
</button>
</div>
</div>
</div>
</div>
)}
</>
);
}