1000 lines
66 KiB
JavaScript
1000 lines
66 KiB
JavaScript
import React, { useState, useEffect } from 'react';
|
||
import Toast from '../Components/Toast';
|
||
import {
|
||
ChevronLeft,
|
||
Edit2,
|
||
Mail,
|
||
Phone,
|
||
Shield,
|
||
Calendar,
|
||
MapPin,
|
||
Building,
|
||
Plus,
|
||
Minus,
|
||
Wallet,
|
||
FileText,
|
||
Users,
|
||
Bell,
|
||
Eye,
|
||
Save,
|
||
X,
|
||
Clock,
|
||
Heart
|
||
} from 'lucide-react';
|
||
|
||
export default function StaffView({ id }) {
|
||
const isReceptionist = window.__APP_DATA__?.role === 'receptionist';
|
||
const basePath = isReceptionist ? '/receptionist' : '/owner';
|
||
const [staff, setStaff] = useState(null);
|
||
const [payments, setPayments] = useState([]);
|
||
const [loading, setLoading] = useState(true);
|
||
const [history, setHistory] = useState([]);
|
||
const [showHistory, setShowHistory] = useState(false);
|
||
const [loadingHistory, setLoadingHistory] = useState(false);
|
||
const [toast, setToast] = useState(null);
|
||
const [isPaymentModalOpen, setIsPaymentModalOpen] = useState(false);
|
||
const [newPayment, setNewPayment] = useState({
|
||
amount: '',
|
||
payment_date: new Date().toISOString().split('T')[0],
|
||
payment_type: 'Salary',
|
||
status: 'Paid',
|
||
remarks: ''
|
||
});
|
||
const [personCount, setPersonCount] = useState(1); // For commission mockup
|
||
const [isSettleModalOpen, setIsSettleModalOpen] = useState(false);
|
||
const [settlementData, setSettlementData] = useState(null);
|
||
const [settling, setSettling] = useState(false);
|
||
const [payrollStatus, setPayrollStatus] = useState([]);
|
||
const [loadingPayroll, setLoadingPayroll] = useState(false);
|
||
const [advanceHistory, setAdvanceHistory] = useState([]);
|
||
const [loadingAdvanceHistory, setLoadingAdvanceHistory] = useState(false);
|
||
|
||
useEffect(() => {
|
||
fetchStaff();
|
||
fetchPayments();
|
||
fetchPayrollStatus();
|
||
fetchAdvanceHistory();
|
||
}, [id]);
|
||
|
||
const fetchPayments = async () => {
|
||
try {
|
||
const response = await fetch(`/api/staff/${id}/payments`);
|
||
const data = await response.json();
|
||
setPayments(data);
|
||
} catch (error) {
|
||
console.error('Error fetching payments:', error);
|
||
}
|
||
};
|
||
|
||
const handleAddPayment = async (e) => {
|
||
e.preventDefault();
|
||
const csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content');
|
||
try {
|
||
const response = await fetch(`/api/staff/${id}/payments`, {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
'X-CSRF-TOKEN': csrfToken
|
||
},
|
||
body: JSON.stringify(newPayment)
|
||
});
|
||
|
||
if (response.ok) {
|
||
setToast({ message: 'Payment recorded successfully!', type: 'success' });
|
||
setIsPaymentModalOpen(false);
|
||
fetchPayments();
|
||
setNewPayment({
|
||
amount: '',
|
||
payment_date: new Date().toISOString().split('T')[0],
|
||
payment_type: 'Salary',
|
||
status: 'Paid',
|
||
remarks: ''
|
||
});
|
||
}
|
||
} catch (error) {
|
||
setToast({ message: 'Failed to record payment.', type: 'error' });
|
||
}
|
||
};
|
||
|
||
const fetchStaff = async () => {
|
||
try {
|
||
const response = await fetch(`/api/staff/${id}`);
|
||
const data = await response.json();
|
||
setStaff(data);
|
||
} catch (error) {
|
||
console.error('Error fetching staff:', error);
|
||
setToast({ message: 'Failed to load staff details.', type: 'error' });
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
};
|
||
|
||
const handleToggleStatus = async () => {
|
||
const csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content');
|
||
const newStatus = staff.status === 'Active' ? 'Inactive' : 'Active';
|
||
try {
|
||
const response = await fetch(`/api/staff/${id}`, {
|
||
method: 'PUT',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
'X-CSRF-TOKEN': csrfToken
|
||
},
|
||
body: JSON.stringify({ ...staff, status: newStatus })
|
||
});
|
||
|
||
if (response.ok) {
|
||
setStaff({ ...staff, status: newStatus });
|
||
setToast({ message: `Staff marked as ${newStatus}`, type: 'success' });
|
||
}
|
||
} catch (error) {
|
||
setToast({ message: 'Failed to update status.', type: 'error' });
|
||
}
|
||
};
|
||
|
||
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 fetchPayrollStatus = async () => {
|
||
setLoadingPayroll(true);
|
||
try {
|
||
const res = await fetch(`/api/staff/${id}/payroll-status`);
|
||
const data = await res.json();
|
||
setPayrollStatus(data);
|
||
} catch (error) {
|
||
console.error('Error fetching payroll status:', error);
|
||
} finally {
|
||
setLoadingPayroll(false);
|
||
}
|
||
};
|
||
|
||
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 fetchSettlement = async (monthKey = null) => {
|
||
try {
|
||
const url = monthKey
|
||
? `/api/staff/${id}/settlement?month=${monthKey}`
|
||
: `/api/staff/${id}/settlement`;
|
||
const res = await fetch(url);
|
||
const data = await res.json();
|
||
|
||
if (data.can_settle === false) {
|
||
setToast({ message: data.message, type: 'info' });
|
||
return;
|
||
}
|
||
|
||
setSettlementData(data);
|
||
setIsSettleModalOpen(true);
|
||
} catch (error) {
|
||
setToast({ message: 'Failed to fetch settlement details.', type: 'error' });
|
||
}
|
||
};
|
||
|
||
const handleSettle = async () => {
|
||
setSettling(true);
|
||
const csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content');
|
||
try {
|
||
const res = await fetch(`/api/staff/${id}/settle`, {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
'X-CSRF-TOKEN': csrfToken
|
||
},
|
||
body: JSON.stringify({
|
||
settlement_month: settlementData.settlement_month
|
||
})
|
||
});
|
||
if (res.ok) {
|
||
setToast({ message: 'Salary settled successfully!', type: 'success' });
|
||
setIsSettleModalOpen(false);
|
||
fetchStaff();
|
||
fetchPayments();
|
||
fetchPayrollStatus();
|
||
} else {
|
||
const err = await res.json();
|
||
setToast({ message: err.message || 'Failed to settle salary.', type: 'error' });
|
||
}
|
||
} catch (error) {
|
||
setToast({ message: 'Failed to settle salary.', type: 'error' });
|
||
} finally {
|
||
setSettling(false);
|
||
}
|
||
};
|
||
|
||
const handleGetAdvance = async () => {
|
||
// Redirect to edit with advance enabled or just update here
|
||
window.location.href = `${basePath}/staff/edit/${id}?get_advance=true`;
|
||
};
|
||
|
||
if (loading) return <div className="p-12 text-center text-gray-400 font-medium">Loading profile...</div>;
|
||
if (!staff) return <div className="p-12 text-center text-red-500 font-bold font-mono uppercase tracking-widest text-xl">Staff Member Not Found!</div>;
|
||
|
||
const initials = staff.full_name?.split(' ').map(n => n[0]).join('').toUpperCase() || 'ST';
|
||
|
||
return (
|
||
<>
|
||
{toast && <Toast message={toast.message} type={toast.type} onClose={() => setToast(null)} />}
|
||
<main className="p-8 max-w-7xl mx-auto space-y-6">
|
||
{/* Back Link */}
|
||
<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>
|
||
|
||
{/* Profile Header Card */}
|
||
<div className="bg-white rounded-2xl border border-gray-100 shadow-sm p-8 flex items-center justify-between">
|
||
<div className="flex items-center gap-6">
|
||
<div className="w-20 h-20 bg-amber-400 rounded-full flex items-center justify-center text-white text-2xl font-bold border-4 border-amber-50 shadow-inner">
|
||
{initials}
|
||
</div>
|
||
<div className="space-y-2">
|
||
<div className="flex items-center gap-4">
|
||
<h1 className="text-2xl font-black text-gray-900 tracking-tight">{staff.full_name}</h1>
|
||
<div className={`flex items-center gap-1.5 px-2.5 py-0.5 rounded-full ${staff.status === 'Active' ? 'bg-emerald-50 text-emerald-600 border-emerald-100' : 'bg-red-50 text-red-600 border-red-100'} border text-[10px] font-black uppercase tracking-wider`}>
|
||
<div className={`w-1 h-1 rounded-full ${staff.status === 'Active' ? 'bg-emerald-500' : 'bg-red-500'}`}></div>
|
||
{staff.status}
|
||
</div>
|
||
</div>
|
||
<div className="flex items-center gap-6 text-gray-500 text-xs font-bold">
|
||
<div className="flex items-center gap-1.5">
|
||
<Mail size={14} className="text-gray-400" />
|
||
<span>{staff.email}</span>
|
||
</div>
|
||
<div className="flex items-center gap-1.5">
|
||
<Phone size={14} className="text-gray-400" />
|
||
<span>{staff.phone || 'N/A'}</span>
|
||
</div>
|
||
<div className="flex items-center gap-1.5">
|
||
<Shield size={14} className="text-gray-400" />
|
||
<span>{staff.designation || 'N/A'}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<button
|
||
onClick={() => window.location.href = `${basePath}/staff/edit/${staff.id}`}
|
||
className="flex items-center gap-2 px-5 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"
|
||
>
|
||
<Edit2 size={16} />
|
||
<span>Edit Profile</span>
|
||
</button>
|
||
</div>
|
||
|
||
{/* Basic Details Card */}
|
||
<div className="bg-white rounded-2xl border border-gray-100 shadow-sm overflow-hidden">
|
||
<div className="px-8 py-5 border-b border-gray-50 flex items-center gap-3 bg-gray-50/10">
|
||
<Users size={18} className="text-gray-900" />
|
||
<h2 className="text-base font-black text-gray-900 uppercase tracking-wider">Basic Details</h2>
|
||
</div>
|
||
<div className="p-8 grid grid-cols-4 gap-8">
|
||
<div>
|
||
<p className="text-[10px] font-bold text-gray-400 uppercase tracking-widest mb-1">Staff ID</p>
|
||
<p className="text-sm font-bold text-gray-900">{staff.staff_id_code || 'N/A'}</p>
|
||
</div>
|
||
<div>
|
||
<p className="text-[10px] font-bold text-gray-400 uppercase tracking-widest mb-1">Name</p>
|
||
<p className="text-sm font-bold text-gray-900">{staff.full_name}</p>
|
||
</div>
|
||
<div>
|
||
<p className="text-[10px] font-bold text-gray-400 uppercase tracking-widest mb-1">Contact Number</p>
|
||
<div className="flex items-center gap-2 text-sm font-bold text-gray-900">
|
||
<Phone size={12} className="text-gray-300" />
|
||
<span>{staff.phone || 'N/A'}</span>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<p className="text-[10px] font-bold text-gray-400 uppercase tracking-widest mb-1">Email</p>
|
||
<div className="flex items-center gap-2 text-sm font-bold text-gray-900">
|
||
<Mail size={12} className="text-gray-300" />
|
||
<span>{staff.email}</span>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<p className="text-[10px] font-bold text-gray-400 uppercase tracking-widest mb-1">Branch</p>
|
||
<div className="flex items-center gap-2 text-sm font-bold text-gray-900">
|
||
<MapPin size={12} className="text-gray-300" />
|
||
<span>{staff.branch?.name || 'UNDEF'}</span>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<p className="text-[10px] font-bold text-gray-400 uppercase tracking-widest mb-1">Designation</p>
|
||
<div className="flex items-center gap-2 text-sm font-bold text-gray-900">
|
||
<Building size={12} className="text-gray-300" />
|
||
<span>{staff.designation || 'N/A'}</span>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<p className="text-[10px] font-bold text-gray-400 uppercase tracking-widest mb-1">Joining Date</p>
|
||
<p className="text-sm font-bold text-gray-900">{staff.joining_date || 'N/A'}</p>
|
||
</div>
|
||
<div>
|
||
<p className="text-[10px] font-bold text-gray-400 uppercase tracking-widest mb-1">Status</p>
|
||
<div className={`inline-block px-2.5 py-0.5 rounded-full ${staff.status === 'Active' ? 'bg-emerald-50 text-emerald-600 border-emerald-100' : 'bg-red-50 text-red-600 border-red-100'} border text-[10px] font-black uppercase tracking-wider`}>
|
||
{staff.status}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Salary Details Section */}
|
||
<div className="bg-white rounded-2xl border border-gray-100 shadow-sm overflow-hidden">
|
||
<div className="px-8 py-5 border-b border-gray-50 flex items-center justify-between bg-gray-50/10">
|
||
<div className="flex items-center gap-3">
|
||
<Wallet size={18} className="text-emerald-500" />
|
||
<h2 className="text-base font-black text-gray-900 uppercase tracking-wider">Salary Details</h2>
|
||
</div>
|
||
<div className="flex items-center gap-3">
|
||
</div>
|
||
</div>
|
||
<div className="p-8 flex gap-8">
|
||
{/* Current Salary Box */}
|
||
<div className="w-1/3 space-y-4">
|
||
<div className="bg-emerald-50/30 rounded-2xl p-6 border border-emerald-50 relative overflow-hidden group">
|
||
<p className="text-emerald-600 text-[10px] font-black uppercase tracking-widest mb-4">Current Salary</p>
|
||
<div className="flex items-baseline gap-1.5 mb-6">
|
||
<span className="text-4xl font-black text-emerald-700">{(staff.salary_amount || 0).toLocaleString()}</span>
|
||
<span className="text-emerald-500 font-bold text-xs">AED</span>
|
||
</div>
|
||
<div className="space-y-2">
|
||
<div className="flex items-center justify-between text-[10px]">
|
||
<span className="text-emerald-600/60 font-bold uppercase tracking-widest">Type:</span>
|
||
<span className="text-emerald-700 font-black">{staff.salary_type || 'Monthly'}</span>
|
||
</div>
|
||
<div className="flex items-center justify-between text-[10px]">
|
||
<span className="text-emerald-600/60 font-bold uppercase tracking-widest">Cycle:</span>
|
||
<span className="text-emerald-700 font-black">{staff.salary_cycle || 'Monthly'}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Advance Card */}
|
||
<div className="bg-orange-50/30 rounded-2xl p-6 border border-orange-50 relative overflow-hidden group">
|
||
<div className="flex items-center justify-between mb-4">
|
||
<p className="text-orange-600 text-[10px] font-black uppercase tracking-widest">Salary Advance</p>
|
||
{!staff.advance_enabled && (
|
||
<button
|
||
onClick={handleGetAdvance}
|
||
className="px-2.5 py-1 bg-white text-orange-600 rounded-lg text-[10px] font-black border border-orange-100 hover:bg-orange-50 transition-all shadow-sm"
|
||
>
|
||
GET ADVANCE
|
||
</button>
|
||
)}
|
||
</div>
|
||
|
||
{(() => {
|
||
const activeAdvance = advanceHistory.find(h => h.status === 'Pending');
|
||
if (activeAdvance) {
|
||
return (
|
||
<>
|
||
<div className="flex items-baseline gap-1.5 mb-6">
|
||
<span className="text-2xl font-black text-orange-700">{(parseFloat(activeAdvance.advance_amount) || 0).toLocaleString()}</span>
|
||
<span className="text-orange-500 font-bold text-[10px]">AED</span>
|
||
</div>
|
||
<div className="flex items-center justify-between p-2.5 bg-white/50 rounded-xl border border-orange-50">
|
||
<div className="text-[8px] font-black text-orange-400 uppercase tracking-widest">Mode</div>
|
||
<div className="text-[9px] font-black text-orange-700 uppercase tracking-wider">{staff.advance_repayment_mode}</div>
|
||
</div>
|
||
</>
|
||
);
|
||
} else {
|
||
return (
|
||
<div className="py-2 text-center text-orange-300">
|
||
<p className="text-[10px] font-bold italic">No active advance</p>
|
||
</div>
|
||
);
|
||
}
|
||
})()}
|
||
|
||
{advanceHistory.length > 0 && (
|
||
<div className="mt-4 p-3 bg-white/50 rounded-xl border border-orange-100 flex items-center justify-between">
|
||
<div>
|
||
<p className="text-[8px] font-black text-orange-400 uppercase tracking-widest leading-none mb-1">Total Balance Due</p>
|
||
<p className="text-sm font-black text-orange-700">
|
||
{advanceHistory
|
||
.filter(h => h.status === 'Pending')
|
||
.reduce((sum, h) => sum + (parseFloat(h.advance_amount) - parseFloat(h.paid_amount || 0)), 0)
|
||
.toLocaleString()} AED
|
||
</p>
|
||
</div>
|
||
<div className="text-right">
|
||
<p className="text-[8px] font-black text-emerald-500 uppercase tracking-widest leading-none mb-1">Total Paid</p>
|
||
<p className="text-sm font-black text-emerald-600">
|
||
{advanceHistory.reduce((sum, h) => sum + (parseFloat(h.paid_amount || 0)), 0).toLocaleString()} AED
|
||
</p>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{advanceHistory.length > 0 && (
|
||
<div className="mt-6 pt-6 border-t border-orange-50/50 space-y-3">
|
||
<p className="text-[9px] font-black text-orange-400 uppercase tracking-widest mb-2">Advance History Summary</p>
|
||
<div className="space-y-2">
|
||
{advanceHistory.map((h, i) => (
|
||
<div key={i} className={`p-2 rounded-xl border flex items-center justify-between transition-all ${h.status === 'Closed' ? 'bg-white/50 border-gray-100 opacity-60' : 'bg-white border-orange-100 shadow-sm animate-pulse'}`}>
|
||
<div>
|
||
<p className="text-[10px] font-black text-gray-900">{(h.advance_amount || 0).toLocaleString()} AED</p>
|
||
<p className="text-[8px] text-gray-400 font-medium">Taken {new Date(h.created_at).toLocaleDateString()}</p>
|
||
</div>
|
||
<div className="text-right">
|
||
<span className={`px-2 py-0.5 rounded-full text-[8px] font-black uppercase tracking-widest ${h.status === 'Closed' ? 'bg-gray-100 text-gray-400' : 'bg-orange-50 text-orange-600'}`}>
|
||
{h.status}
|
||
</span>
|
||
<p className="text-[8px] font-bold text-emerald-500 mt-0.5">{parseFloat(h.paid_amount || 0).toLocaleString()} Paid</p>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
|
||
{/* Monthly Payroll Status Table */}
|
||
<div className="flex-1 space-y-8">
|
||
<div>
|
||
<div className="flex items-center justify-between mb-4">
|
||
<h3 className="text-sm font-black text-gray-900 uppercase tracking-wider">Monthly Payroll Status</h3>
|
||
<div className="flex items-center gap-2">
|
||
<div className="w-2 h-2 rounded-full bg-emerald-500"></div>
|
||
<span className="text-[10px] font-bold text-gray-400">Paid</span>
|
||
<div className="w-2 h-2 rounded-full bg-orange-500 ml-2"></div>
|
||
<span className="text-[10px] font-bold text-gray-400">Unpaid</span>
|
||
</div>
|
||
</div>
|
||
<div className="border border-gray-50 rounded-2xl overflow-hidden bg-white shadow-sm">
|
||
<table className="w-full">
|
||
<thead className="bg-gray-50/50">
|
||
<tr>
|
||
<th className="px-6 py-3 text-left text-[10px] font-bold text-gray-400 uppercase tracking-widest">Month</th>
|
||
<th className="px-6 py-3 text-left text-[10px] font-bold text-gray-400 uppercase tracking-widest">Status</th>
|
||
<th className="px-6 py-3 text-left text-[10px] font-bold text-gray-400 uppercase tracking-widest">Net Amount</th>
|
||
<th className="px-6 py-3 text-right text-[10px] font-bold text-gray-400 uppercase tracking-widest">Action</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody className="divide-y divide-gray-50">
|
||
{payrollStatus.length > 0 ? (
|
||
payrollStatus.map((p) => (
|
||
<tr key={p.month_key} className="hover:bg-gray-50/50 transition-colors">
|
||
<td className="px-6 py-4 text-xs font-bold text-gray-900">{p.month}</td>
|
||
<td className="px-6 py-4">
|
||
<span className={`px-2 py-0.5 rounded-full text-[10px] font-black uppercase tracking-wider ${
|
||
p.status === 'Paid' ? 'bg-emerald-50 text-emerald-600' : 'bg-orange-50 text-orange-600'
|
||
}`}>
|
||
{p.status}
|
||
</span>
|
||
</td>
|
||
<td className="px-6 py-4 text-xs font-black text-gray-600">
|
||
{p.amount ? `${parseFloat(p.amount).toLocaleString()} AED` : '-'}
|
||
</td>
|
||
<td className="px-6 py-4 text-right">
|
||
{p.can_settle ? (
|
||
<button
|
||
onClick={() => fetchSettlement(p.month_key)}
|
||
className="px-3 py-1 bg-emerald-500 text-white rounded-lg text-[10px] font-black hover:scale-105 active:scale-95 transition-all shadow-md shadow-emerald-100 uppercase tracking-widest"
|
||
>
|
||
Settle
|
||
</button>
|
||
) : (
|
||
<span className="text-[10px] font-black text-emerald-500 uppercase tracking-widest">COMPLETED</span>
|
||
)}
|
||
</td>
|
||
</tr>
|
||
))
|
||
) : (
|
||
<tr>
|
||
<td colSpan="4" className="px-6 py-8 text-center text-xs font-bold text-gray-400 italic">
|
||
No months yet to settle. Next settlement will be available after 1st of next month.
|
||
</td>
|
||
</tr>
|
||
)}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Transaction History Table */}
|
||
<div>
|
||
<div className="flex items-center justify-between mb-4">
|
||
<h3 className="text-sm font-black text-gray-900 uppercase tracking-wider">Transaction History</h3>
|
||
<button className="text-[10px] font-bold text-blue-600 hover:underline uppercase tracking-wider">View Full History</button>
|
||
</div>
|
||
<div className="border border-gray-50 rounded-2xl overflow-hidden bg-white shadow-sm opacity-80">
|
||
<table className="w-full">
|
||
<thead className="bg-gray-50/50">
|
||
<tr>
|
||
<th className="px-6 py-3 text-left text-[10px] font-bold text-gray-400 uppercase tracking-widest">Date</th>
|
||
<th className="px-6 py-3 text-left text-[10px] font-bold text-gray-400 uppercase tracking-widest">Type</th>
|
||
<th className="px-6 py-3 text-left text-[10px] font-bold text-gray-400 uppercase tracking-widest">Amount</th>
|
||
<th className="px-6 py-3 text-left text-[10px] font-bold text-gray-400 uppercase tracking-widest">Status</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody className="divide-y divide-gray-50">
|
||
{payments.length > 0 ? (
|
||
payments.map((p) => (
|
||
<tr key={p.id}>
|
||
<td className="px-6 py-4 text-xs font-bold text-gray-900">{p.payment_date}</td>
|
||
<td className="px-6 py-4 text-xs font-semibold text-gray-500">{p.payment_type}</td>
|
||
<td className="px-6 py-4 text-xs font-black text-emerald-600">{(parseFloat(p.amount) || 0).toLocaleString()} AED</td>
|
||
<td className="px-6 py-4">
|
||
<span className="px-2 py-0.5 bg-emerald-50 text-emerald-600 rounded-full text-[10px] font-black uppercase tracking-wider">{p.status}</span>
|
||
</td>
|
||
</tr>
|
||
))
|
||
) : (
|
||
<tr>
|
||
<td colSpan="4" className="px-6 py-8 text-center text-xs font-bold text-gray-400 italic">
|
||
No transactions recorded yet.
|
||
</td>
|
||
</tr>
|
||
)}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Member Commission Section */}
|
||
<div className="bg-[#FAF9FF] rounded-2xl border border-purple-100 shadow-sm p-8 group transition-all hover:bg-white hover:border-purple-200">
|
||
<div className="flex items-center justify-between mb-8">
|
||
<div className="flex items-center gap-3">
|
||
<div className="p-2 bg-purple-100/50 rounded-xl text-purple-600">
|
||
<Users size={18} />
|
||
</div>
|
||
<h2 className="text-base font-black text-purple-900 uppercase tracking-wider">Member Commission</h2>
|
||
</div>
|
||
<div className="flex items-center gap-4">
|
||
<button
|
||
onClick={() => window.location.href = `${basePath}/staff/edit/${id}`}
|
||
className="text-[10px] font-black text-gray-400 uppercase tracking-widest flex items-center gap-1.5 hover:text-purple-600 transition-all border border-transparent hover:border-purple-100 px-2 py-1 rounded-lg"
|
||
>
|
||
<Edit2 size={12} />
|
||
Edit Settings
|
||
</button>
|
||
<button
|
||
onClick={fetchHistory}
|
||
className="text-[10px] font-black text-purple-500 uppercase tracking-widest flex items-center gap-1.5 px-3 py-1.5 bg-purple-50 rounded-lg hover:bg-purple-100 transition-all"
|
||
>
|
||
<Eye size={12} />
|
||
PT History
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="grid grid-cols-2 gap-20">
|
||
<div className="space-y-6">
|
||
<div>
|
||
<p className="text-[10px] font-bold text-purple-400 uppercase tracking-widest mb-1.5">Amount Per Person</p>
|
||
<div className="flex items-baseline gap-1.5">
|
||
<span className="text-4xl font-black text-purple-900">{staff.commission_amount || 0}</span>
|
||
<span className="text-purple-400 font-bold text-sm">AED</span>
|
||
</div>
|
||
</div>
|
||
<div className="pt-6 border-t border-purple-50">
|
||
<p className="text-[10px] font-bold text-purple-400 uppercase tracking-widest mb-1">Total Monthly Estimate</p>
|
||
<p className="text-2xl font-black text-purple-600">{(staff.commission_amount * (staff.commission_member_count || 0)).toLocaleString()} AED</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="flex flex-col justify-center items-center gap-3 bg-white/50 border border-purple-50 rounded-2xl p-6">
|
||
<p className="text-[10px] font-black text-purple-400 uppercase tracking-widest">Active Member Count</p>
|
||
<div className="flex items-center gap-8">
|
||
<span className="text-5xl font-black text-purple-900 text-center">{staff.commission_member_count || 0}</span>
|
||
</div>
|
||
<p className="text-[9px] text-purple-300 font-bold uppercase tracking-wider">Updated Monthly</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Bottom Row: Docs, Family, Notifications */}
|
||
<div className="grid grid-cols-2 gap-8">
|
||
{/* Documentation Card */}
|
||
<div className="bg-white rounded-2xl border border-gray-100 shadow-sm overflow-hidden">
|
||
<div className="px-8 py-5 border-b border-gray-50 flex items-center justify-between bg-gray-50/10">
|
||
<div className="flex items-center gap-3">
|
||
<FileText size={18} className="text-blue-500" />
|
||
<h2 className="text-base font-black text-gray-900 uppercase tracking-wider">Documentation</h2>
|
||
</div>
|
||
<span className="px-2.5 py-0.5 bg-blue-50 text-blue-600 rounded-full text-[10px] font-bold border border-blue-100">VERIFIED</span>
|
||
</div>
|
||
<div className="p-8 space-y-4">
|
||
{staff.documents && staff.documents.length > 0 ? (
|
||
staff.documents.map((doc, index) => (
|
||
<div key={index} className="group p-4 bg-gray-50/50 rounded-2xl border border-gray-100 flex items-center justify-between hover:bg-white hover:shadow-sm transition-all">
|
||
<div className="flex items-center gap-4">
|
||
<div className="w-10 h-10 bg-blue-50 rounded-xl flex items-center justify-center text-blue-500">
|
||
<FileText size={20} />
|
||
</div>
|
||
<div>
|
||
<p className="text-xs font-black text-gray-900 mb-1 leading-none uppercase tracking-tight">{doc.name}</p>
|
||
<p className="text-[10px] font-bold text-gray-400 mb-1.5">{doc.document_number || 'No Number'}</p>
|
||
<div className="flex items-center gap-2">
|
||
<p className="text-[10px] font-bold text-orange-400 flex items-center gap-1.5">
|
||
<Clock size={10} />
|
||
Expiry: {doc.expiry_date || 'N/A'}
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{doc.path && (
|
||
<a
|
||
href={`/storage/${doc.path}`}
|
||
target="_blank"
|
||
rel="noopener noreferrer"
|
||
className="p-2 bg-blue-50 text-blue-600 rounded-lg hover:bg-blue-100 transition-all border border-blue-100"
|
||
>
|
||
<Eye size={16} />
|
||
</a>
|
||
)}
|
||
</div>
|
||
))
|
||
) : (
|
||
<p className="text-center py-6 text-gray-400 text-xs italic font-bold uppercase tracking-widest opacity-50">No documents</p>
|
||
)}
|
||
</div>
|
||
</div>
|
||
|
||
{/* Family Members Card */}
|
||
<div className="bg-white rounded-2xl border border-gray-100 shadow-sm overflow-hidden">
|
||
<div className="px-8 py-5 border-b border-gray-50 flex items-center justify-between bg-gray-50/10">
|
||
<div className="flex items-center gap-3">
|
||
<Heart size={18} className="text-rose-500" />
|
||
<h2 className="text-base font-black text-gray-900 uppercase tracking-wider">Family Details</h2>
|
||
</div>
|
||
</div>
|
||
<div className="p-8 space-y-4">
|
||
{staff.family_members && staff.family_members.length > 0 ? (
|
||
staff.family_members.map((member, index) => (
|
||
<div key={index} className="p-5 rounded-2xl bg-rose-50/30 border border-rose-50 flex flex-col gap-4">
|
||
<div className="flex items-center justify-between">
|
||
<div>
|
||
<p className="text-[10px] font-black text-rose-400 uppercase tracking-widest mb-1">Family Member {index + 1}</p>
|
||
<p className="text-sm font-black text-gray-900">{member.name}</p>
|
||
</div>
|
||
<div className="px-3 py-1 bg-white rounded-full border border-rose-100 text-[10px] font-black text-rose-500 uppercase">
|
||
{member.relation}
|
||
</div>
|
||
</div>
|
||
<div className="flex items-center gap-2 text-xs font-bold text-gray-600 bg-white/50 p-2.5 rounded-xl border border-rose-50">
|
||
<Phone size={14} className="text-rose-300" />
|
||
<span>{member.contact}</span>
|
||
</div>
|
||
</div>
|
||
))
|
||
) : staff.family_member_name ? (
|
||
<div className="p-5 rounded-2xl bg-rose-50/30 border border-rose-50 flex flex-col gap-4">
|
||
<div className="flex items-center justify-between">
|
||
<div>
|
||
<p className="text-[10px] font-black text-rose-400 uppercase tracking-widest mb-1">Emergency Contact</p>
|
||
<p className="text-sm font-black text-gray-900">{staff.family_member_name}</p>
|
||
</div>
|
||
<div className="px-3 py-1 bg-white rounded-full border border-rose-100 text-[10px] font-black text-rose-500 uppercase">
|
||
{staff.family_member_relation}
|
||
</div>
|
||
</div>
|
||
<div className="flex items-center gap-2 text-xs font-bold text-gray-600 bg-white/50 p-2.5 rounded-xl border border-rose-50">
|
||
<Phone size={14} className="text-rose-300" />
|
||
<span>{staff.family_member_contact}</span>
|
||
</div>
|
||
</div>
|
||
) : (
|
||
<p className="text-center py-6 text-gray-400 text-xs italic font-bold uppercase tracking-widest opacity-50">Not Provided</p>
|
||
)}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
{/* Payment Modal */}
|
||
{isPaymentModalOpen && (
|
||
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/50 backdrop-blur-sm">
|
||
<div className="bg-white rounded-[2.5rem] w-full max-w-md shadow-2xl border border-gray-100 overflow-hidden animate-in fade-in zoom-in duration-200">
|
||
<div className="p-8 border-b border-gray-50 flex items-center justify-between bg-[#F9FAFB]/50">
|
||
<div>
|
||
<h3 className="text-xl font-black text-[#111827]">Record Payment</h3>
|
||
<p className="text-xs text-gray-500 font-bold uppercase tracking-widest mt-1">Staff Transaction</p>
|
||
</div>
|
||
<button onClick={() => setIsPaymentModalOpen(false)} className="p-2 hover:bg-gray-100 rounded-full transition-colors text-gray-400">
|
||
<Minus size={20} />
|
||
</button>
|
||
</div>
|
||
|
||
<form onSubmit={handleAddPayment} className="p-8 space-y-6">
|
||
<div className="space-y-4">
|
||
<div>
|
||
<label className="text-[10px] font-black text-gray-400 uppercase tracking-[0.2em] mb-2 block">Payment Type</label>
|
||
<select
|
||
className="w-full px-4 py-3 bg-gray-50 border border-gray-100 rounded-2xl outline-none focus:ring-2 focus:ring-emerald-500/20 focus:border-emerald-500 transition-all text-sm font-bold"
|
||
value={newPayment.payment_type}
|
||
onChange={(e) => setNewPayment({...newPayment, payment_type: e.target.value})}
|
||
>
|
||
<option>Salary</option>
|
||
<option>Advance</option>
|
||
<option>Commission</option>
|
||
<option>Bonus</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="text-[10px] font-black text-gray-400 uppercase tracking-[0.2em] mb-2 block">Amount (AED)</label>
|
||
<input
|
||
type="number"
|
||
required
|
||
className="w-full px-4 py-3 bg-gray-50 border border-gray-100 rounded-2xl outline-none focus:ring-2 focus:ring-emerald-500/20 focus:border-emerald-500 transition-all text-sm font-bold"
|
||
placeholder="Enter amount"
|
||
value={newPayment.amount}
|
||
onChange={(e) => setNewPayment({...newPayment, amount: e.target.value})}
|
||
/>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="text-[10px] font-black text-gray-400 uppercase tracking-[0.2em] mb-2 block">Date</label>
|
||
<input
|
||
type="date"
|
||
required
|
||
className="w-full px-4 py-3 bg-gray-50 border border-gray-100 rounded-2xl outline-none focus:ring-2 focus:ring-emerald-500/20 focus:border-emerald-500 transition-all text-sm font-bold"
|
||
value={newPayment.payment_date}
|
||
onChange={(e) => setNewPayment({...newPayment, payment_date: e.target.value})}
|
||
/>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="text-[10px] font-black text-gray-400 uppercase tracking-[0.2em] mb-2 block">Remarks</label>
|
||
<textarea
|
||
className="w-full px-4 py-3 bg-gray-50 border border-gray-100 rounded-2xl outline-none focus:ring-2 focus:ring-emerald-500/20 focus:border-emerald-500 transition-all text-sm font-medium resize-none h-24"
|
||
placeholder="Optional notes..."
|
||
value={newPayment.remarks}
|
||
onChange={(e) => setNewPayment({...newPayment, remarks: e.target.value})}
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="flex gap-3 pt-4">
|
||
<button
|
||
type="button"
|
||
onClick={() => setIsPaymentModalOpen(false)}
|
||
className="flex-1 px-6 py-4 bg-gray-100 text-gray-600 rounded-2xl font-black text-sm hover:bg-gray-200 transition-all"
|
||
>
|
||
CANCEL
|
||
</button>
|
||
<button
|
||
type="submit"
|
||
className="flex-2 px-8 py-4 bg-emerald-600 text-white rounded-2xl font-black text-sm hover:scale-[1.02] active:scale-[0.98] transition-all shadow-xl shadow-emerald-100 flex items-center justify-center gap-2"
|
||
>
|
||
<Save size={18} />
|
||
RECORD PAYMENT
|
||
</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{/* PT 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>
|
||
)}
|
||
|
||
{/* Settle Salary Modal */}
|
||
{isSettleModalOpen && settlementData && (
|
||
<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-300">
|
||
<div className="bg-white rounded-[2.5rem] w-full max-w-lg shadow-2xl border border-gray-100 overflow-hidden animate-in zoom-in-95 duration-200">
|
||
<div className="p-10 border-b border-gray-50 flex items-center justify-between">
|
||
<div>
|
||
<h3 className="text-2xl font-black text-gray-900 leading-none">Settle Salary — Next Month</h3>
|
||
<div className="mt-4 flex gap-8">
|
||
<div>
|
||
<p className="text-[10px] font-bold text-gray-400 uppercase tracking-widest mb-1">STAFF</p>
|
||
<p className="text-sm font-bold text-gray-900">{settlementData.staff_name}</p>
|
||
</div>
|
||
<div>
|
||
<p className="text-[10px] font-bold text-gray-400 uppercase tracking-widest mb-1">Settlement Period</p>
|
||
<p className="text-sm font-bold text-gray-900">{settlementData.period}</p>
|
||
<p className="text-[10px] text-gray-400 italic font-medium">Cycle: {settlementData.cycle_range}</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<button onClick={() => setIsSettleModalOpen(false)} className="w-10 h-10 flex items-center justify-center rounded-full hover:bg-gray-100 text-gray-400 transition-colors">
|
||
<X size={24} />
|
||
</button>
|
||
</div>
|
||
|
||
<div className="p-10 space-y-8">
|
||
<div className="bg-gray-50/50 rounded-[2.5rem] border border-gray-100 overflow-hidden">
|
||
<div className="px-8 py-4 border-b border-gray-100 flex items-center justify-between">
|
||
<p className="text-[10px] font-black text-gray-400 uppercase tracking-[0.2em]">BILL BREAKDOWN</p>
|
||
<div className="text-[9px] font-bold text-gray-400 uppercase">
|
||
Formula: <span className="text-emerald-500">Salary</span> + <span className="text-purple-500">Comm.</span> - <span className="text-orange-500">Adv.</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="p-8 space-y-4">
|
||
{/* Base Salary */}
|
||
<div className="flex items-center justify-between group">
|
||
<div className="flex items-center gap-4">
|
||
<div className="w-10 h-10 bg-emerald-50 rounded-2xl flex items-center justify-center text-emerald-500 shadow-sm border border-emerald-100">
|
||
<Wallet size={18} />
|
||
</div>
|
||
<div>
|
||
<span className="text-sm font-bold text-gray-700 block">Base Salary ({settlementData.salary_type})</span>
|
||
{settlementData.is_prorated && (
|
||
<span className="text-[10px] text-orange-500 font-bold italic">
|
||
Prorated: {settlementData.working_days} Days / {settlementData.total_days} Cycle Days
|
||
</span>
|
||
)}
|
||
</div>
|
||
</div>
|
||
<div className="text-right">
|
||
<span className="text-base font-black text-gray-900 block">{(settlementData.prorated_base || settlementData.base_salary || 0).toLocaleString()} AED</span>
|
||
{settlementData.is_prorated && (
|
||
<span className="text-[10px] text-gray-400 line-through">{(settlementData.base_salary || 0).toLocaleString()} AED</span>
|
||
)}
|
||
</div>
|
||
</div>
|
||
|
||
{/* Commission */}
|
||
<div className="flex items-center justify-between group">
|
||
<div className="flex items-center gap-4">
|
||
<div className="w-10 h-10 bg-purple-50 rounded-2xl flex items-center justify-center text-purple-500 shadow-sm border border-purple-100">
|
||
<Users size={18} />
|
||
</div>
|
||
<div>
|
||
<p className="text-sm font-bold text-gray-700">Member Commission</p>
|
||
<p className="text-[10px] font-bold text-purple-400 tracking-wider">
|
||
{settlementData.commission_count} members × {settlementData.commission_amount} AED
|
||
</p>
|
||
</div>
|
||
</div>
|
||
<span className="text-base font-black text-purple-600">+ {(settlementData.total_commission || 0).toLocaleString()} AED</span>
|
||
</div>
|
||
|
||
{/* Advance Deduction */}
|
||
<div className="flex items-center justify-between group">
|
||
<div className="flex items-center gap-4">
|
||
<div className="w-10 h-10 bg-orange-50 rounded-2xl flex items-center justify-center text-orange-500 shadow-sm border border-orange-100">
|
||
<Minus size={18} />
|
||
</div>
|
||
<div>
|
||
<p className="text-sm font-bold text-gray-700">Advance Deduction</p>
|
||
<p className="text-[10px] font-bold text-orange-400 tracking-wider">
|
||
Remaining: {(settlementData.remaining_advance || 0).toLocaleString()} AED
|
||
</p>
|
||
</div>
|
||
</div>
|
||
<span className="text-base font-black text-orange-600">- {(settlementData.advance_deduction || 0).toLocaleString()} AED</span>
|
||
</div>
|
||
|
||
<div className="mt-4 pt-6 border-t-2 border-dashed border-gray-100">
|
||
<div className="bg-emerald-50/50 p-6 rounded-[2rem] border border-emerald-100/50 flex items-center justify-between group hover:scale-[1.02] transition-transform">
|
||
<div className="flex items-center gap-4">
|
||
<div className="w-12 h-12 bg-emerald-500 rounded-2xl flex items-center justify-center text-white shadow-xl shadow-emerald-100">
|
||
<FileText size={24} />
|
||
</div>
|
||
<span className="text-lg font-black text-emerald-900">Net Payable</span>
|
||
</div>
|
||
<span className="text-3xl font-black text-emerald-600">{(settlementData.net_payable || 0).toLocaleString()} AED</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="flex gap-4">
|
||
<button
|
||
onClick={() => setIsSettleModalOpen(false)}
|
||
className="flex-1 px-8 py-4 bg-gray-100 text-gray-600 rounded-2xl font-black text-sm hover:bg-gray-200 transition-all uppercase tracking-widest"
|
||
>
|
||
Cancel
|
||
</button>
|
||
<button
|
||
onClick={handleSettle}
|
||
disabled={settling}
|
||
className="flex-[2] px-8 py-4 bg-emerald-500 text-white rounded-2xl font-black text-sm hover:scale-[1.02] active:scale-[0.98] transition-all shadow-xl shadow-emerald-100 flex items-center justify-center gap-3 uppercase tracking-widest disabled:opacity-50"
|
||
>
|
||
{settling ? 'Processing...' : (
|
||
<>
|
||
<Save size={20} />
|
||
Settle for {settlementData.period}
|
||
</>
|
||
)}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
</main>
|
||
</>
|
||
);
|
||
}
|