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);
const [isAdvanceHistoryModalOpen, setIsAdvanceHistoryModalOpen] = useState(false);
const [isPaymentHistoryModalOpen, setIsPaymentHistoryModalOpen] = 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
Loading profile...
;
if (!staff) return Staff Member Not Found!
;
const initials = staff.full_name?.split(' ').map(n => n[0]).join('').toUpperCase() || 'ST';
return (
<>
{toast && setToast(null)} />}
{/* Back Link */}
window.location.href = `${basePath}/staff`} className="flex items-center gap-2 text-sm font-bold text-gray-500 hover:text-gray-900 transition-colors">
Back to Staff List
{/* Profile Header Card */}
{initials}
{staff.email}
{staff.role || staff.designation || 'N/A'}
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"
>
Edit Profile
{/* Basic Details Card */}
Basic Details
Staff ID
{staff.staff_id_code || 'N/A'}
Branch
{staff.branch?.name || 'UNDEF'}
Designation
{staff.role || staff.designation || 'N/A'}
Joining Date
{staff.joining_date || 'N/A'}
{/* Salary Details Section */}
{/* Current Salary Box */}
Current Salary
{(staff.salary_amount || 0).toLocaleString()}
AED
Type:
{staff.salary_type || 'Monthly'}
Cycle:
{staff.salary_cycle || 'Monthly'}
{/* Advance Card */}
Salary Advance
{!staff.advance_enabled && (
GET ADVANCE
)}
{(() => {
const activeAdvance = advanceHistory.find(h => h.status === 'Pending');
if (activeAdvance) {
return (
<>
{(parseFloat(activeAdvance.advance_amount) || 0).toLocaleString()}
AED
Mode
{staff.advance_repayment_mode}
>
);
} else {
return (
);
}
})()}
{advanceHistory.length > 0 && (
Total Balance 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.length > 0 && (
Monthly Repayment Schedule
{advanceHistory.flatMap(h => h.installment_schedule || []).length > 3 && (
setIsAdvanceHistoryModalOpen(true)}
className="text-[8px] font-black text-orange-600 hover:underline uppercase tracking-widest"
>
View Full History
)}
{advanceHistory.flatMap(h => h.installment_schedule || [])
.slice(0, 3)
.map((item, idx) => (
{item.month}
AED {item.amount.toLocaleString()}
{item.status}
))}
)}
{/* Monthly Payroll Status Table */}
Month
Status
Net Amount
Action
{payrollStatus.length > 0 ? (
payrollStatus.map((p) => (
{p.month}
{p.status}
{p.amount ? `${parseFloat(p.amount).toLocaleString()} AED` : '-'}
{p.can_settle ? (
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
) : (
COMPLETED
)}
))
) : (
No months yet to settle. Next settlement will be available after 1st of next month.
)}
{/* Transaction History Table */}
Transaction History
{payments.length > 3 && (
setIsPaymentHistoryModalOpen(true)}
className="text-[10px] font-bold text-blue-600 hover:underline uppercase tracking-wider"
>
View Full History
)}
Date
Type
Amount
Status
{payments.length > 0 ? (
payments.slice(0, 3).map((p) => (
{p.payment_date}
{p.payment_type}
{p.payment_type === 'Salary Settlement' && p.settlement_month && (
({p.settlement_month})
)}
{(parseFloat(p.amount) || 0).toLocaleString()} AED
{p.status}
))
) : (
No transactions recorded yet.
)}
{/* Member Commission Section */}
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"
>
Edit Settings
PT History
Amount Per Person
{staff.commission_amount || 0}
AED
Total Monthly Estimate
{(staff.commission_amount * (staff.commission_member_count || 0)).toLocaleString()} AED
Active Member Count
{staff.commission_member_count || 0}
Updated Monthly
{/* Bottom Row: Docs, Family, Notifications */}
{/* Documentation Card */}
{staff.documents && staff.documents.length > 0 ? (
staff.documents.map((doc, index) => (
{doc.name}
{doc.document_number || 'No Number'}
Expiry: {doc.expiry_date || 'N/A'}
{doc.path && (
)}
))
) : (
No documents
)}
{/* Family Members Card */}
{staff.family_members && staff.family_members.length > 0 ? (
staff.family_members.map((member, index) => (
Family Member {index + 1}
{member.name}
{member.relation}
))
) : staff.family_member_name ? (
Emergency Contact
{staff.family_member_name}
{staff.family_member_relation}
{staff.family_member_contact}
) : (
Not Provided
)}
{/* Payment Modal */}
{isPaymentModalOpen && (
Record Payment
Staff Transaction
setIsPaymentModalOpen(false)} className="p-2 hover:bg-gray-100 rounded-full transition-colors text-gray-400">
)}
{/* PT History Modal */}
{showHistory && (
Commission History
Past member counts and commissions
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"
>
{loadingHistory ? (
Loading history...
) : history.length > 0 ? (
Month
Members
Per Head
Total
{history.map((row) => (
{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.
)}
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
)}
{/* Settle Salary Modal */}
{isSettleModalOpen && settlementData && (
Settle Salary — Next Month
STAFF
{settlementData.staff_name}
Settlement Period
{settlementData.period}
Cycle: {settlementData.cycle_range}
setIsSettleModalOpen(false)} className="w-10 h-10 flex items-center justify-center rounded-full hover:bg-gray-100 text-gray-400 transition-colors">
BILL BREAKDOWN
Formula: Salary + Comm. - Adv.
{/* Base Salary */}
Base Salary ({settlementData.salary_type})
{settlementData.is_prorated && (
Prorated: {settlementData.working_days} Days / {settlementData.total_days} Cycle Days
)}
{(settlementData.prorated_base || settlementData.base_salary || 0).toLocaleString()} AED
{settlementData.is_prorated && (
{(settlementData.base_salary || 0).toLocaleString()} AED
)}
{/* Commission */}
Member Commission
{settlementData.commission_count} members × {settlementData.commission_amount} AED
+ {(settlementData.total_commission || 0).toLocaleString()} AED
{/* Advance Deduction */}
Advance Deduction
Remaining: {(settlementData.remaining_advance || 0).toLocaleString()} AED
- {(settlementData.advance_deduction || 0).toLocaleString()} AED
{(settlementData.net_payable || 0).toLocaleString()} AED
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
{settling ? 'Processing...' : (
<>
Settle for {settlementData.period}
>
)}
)}
{/* Full Advance History Modal */}
{isAdvanceHistoryModalOpen && (
Full Advance History
Repayment Schedule & Status
setIsAdvanceHistoryModalOpen(false)} className="w-10 h-10 flex items-center justify-center rounded-full hover:bg-gray-100 text-gray-400 transition-all">
{advanceHistory.map((h, hIdx) => (
Advance taken on {new Date(h.created_at).toLocaleDateString()} — {parseFloat(h.advance_amount).toLocaleString()} AED
{h.status}
{(h.installment_schedule || []).map((item, iIdx) => (
{item.month}
Amount: AED {item.amount.toLocaleString()}
{item.status !== 'Paid' && (
Pending: AED {item.amount.toLocaleString()}
)}
{item.status}
))}
{hIdx < advanceHistory.length - 1 &&
}
))}
setIsAdvanceHistoryModalOpen(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
)}
{/* Full Transaction History Modal */}
{isPaymentHistoryModalOpen && (
Full Transaction History
Salary, Advance & Others
setIsPaymentHistoryModalOpen(false)} className="w-10 h-10 flex items-center justify-center rounded-full hover:bg-gray-100 text-gray-400 transition-all">
Date
Type
Amount
Status
Remarks
{payments.map((p) => (
{p.payment_date}
{p.payment_type}
{p.payment_type === 'Salary Settlement' && p.settlement_month && (
({p.settlement_month})
)}
{(parseFloat(p.amount) || 0).toLocaleString()} AED
{p.status}
{p.remarks || '-'}
))}
setIsPaymentHistoryModalOpen(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
)}
>
);
}