From 5b82099a448afb6ae70faacaab25a20cc7930d13 Mon Sep 17 00:00:00 2001 From: "Prit V. Vasani" Date: Sat, 10 Oct 2026 08:47:18 +0000 Subject: [PATCH] feat(ui): filters on invoices + staff lists - AdminInvoices: search (by invoice #) + status filter (issued/void). - AdminStaff: role + status filters with live counts, alongside search. - Print/PDF links already target the API origin (#66); access logs print to console + server/logs/info-*.log. Lint 0, tests + build OK. --- .../src/pages/admin/AdminInvoices.jsx | 37 ++++++++++++++- .../src/pages/admin/AdminStaff.jsx | 46 +++++++++++++++++-- 2 files changed, 79 insertions(+), 4 deletions(-) diff --git a/cafe-management-sys/src/pages/admin/AdminInvoices.jsx b/cafe-management-sys/src/pages/admin/AdminInvoices.jsx index 3f4047d..135a045 100644 --- a/cafe-management-sys/src/pages/admin/AdminInvoices.jsx +++ b/cafe-management-sys/src/pages/admin/AdminInvoices.jsx @@ -44,6 +44,15 @@ const AdminInvoices = () => { const [selected, setSelected] = useState(null); const [history, setHistory] = useState([]); const [historyLoading, setHistoryLoading] = useState(false); + const [search, setSearch] = useState(''); + const [statusFilter, setStatusFilter] = useState('all'); + + const visibleInvoices = (invoices || []).filter((inv) => { + const q = search.trim().toLowerCase(); + const matchesSearch = !q || String(inv.invoiceNumber || '').toLowerCase().includes(q); + const matchesStatus = statusFilter === 'all' || inv.status === statusFilter; + return matchesSearch && matchesStatus; + }); const fetchInvoices = useCallback(async () => { try { @@ -128,9 +137,35 @@ const AdminInvoices = () => { /> )} + + + setSearch(e.target.value)} + /> + + + setStatusFilter(e.target.value)} + > + All + Issued + Void + + + + { const [loading, setLoading] = useState(true); const [searchQuery, setSearchQuery] = useState(''); const [error, setError] = useState(null); + const [roleFilter, setRoleFilter] = useState('all'); + const [statusFilter, setStatusFilter] = useState('all'); const [selectedStaff, setSelectedStaff] = useState(null); const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); const [addDialogOpen, setAddDialogOpen] = useState(false); @@ -147,11 +149,18 @@ const AdminStaff = () => { const filteredStaff = staff.filter(member => { const name = displayName(member).toLowerCase(); - return name.includes(searchQuery.toLowerCase()) || - (member.email || '').toLowerCase().includes(searchQuery.toLowerCase()) || - (member.role || '').toLowerCase().includes(searchQuery.toLowerCase()); + const matchesSearch = name.includes(searchQuery.toLowerCase()) || + (member.email || '').toLowerCase().includes(searchQuery.toLowerCase()); + const matchesRole = roleFilter === 'all' || member.role === roleFilter; + const matchesStatus = statusFilter === 'all' || (member.isActive !== false ? 'active' : 'inactive') === statusFilter; + return matchesSearch && matchesRole && matchesStatus; }); + const staffRoleCounts = staff.reduce( + (acc, m) => ({ ...acc, [m.role || 'staff']: (acc[m.role || 'staff'] || 0) + 1 }), + {} + ); + const [staffPage, setStaffPage] = useState(1); const PAGE_STAFF = 15; const pagedStart = (staffPage - 1) * PAGE_STAFF; @@ -210,7 +219,38 @@ const AdminStaff = () => { ), }} + sx={{ mb: 2 }} /> + + + setRoleFilter(e.target.value)} + > + All roles + Staff ({staffRoleCounts.staff || 0}) + Admin ({staffRoleCounts.admin || 0}) + + + + setStatusFilter(e.target.value)} + > + All + Active + Inactive + + +