Skip to content

Commit e9e6dad

Browse files
committed
feat: add bulk and individual payment deletion functionality to the transactions page, supported by a new admin service method.
1 parent 07dbab4 commit e9e6dad

2 files changed

Lines changed: 91 additions & 3 deletions

File tree

src/pages/TransactionsPage.jsx

Lines changed: 76 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ const PaymentsPage = () => {
1010
const navigate = useNavigate();
1111
const [transactions, setTransactions] = useState([]);
1212
const [loading, setLoading] = useState(true);
13+
const [selectedTxns, setSelectedTxns] = useState([]);
1314

1415
useEffect(() => {
1516
loadTransactions();
@@ -55,6 +56,47 @@ const PaymentsPage = () => {
5556
}
5657
};
5758

59+
const handleDelete = async (txnId) => {
60+
if (confirm("Permanently purge this financial record from the ledger?")) {
61+
try {
62+
await adminService.deleteDocument('payments', txnId);
63+
loadTransactions();
64+
} catch (error) {
65+
alert("Deletion failed");
66+
}
67+
}
68+
};
69+
70+
const handleBulkDelete = async () => {
71+
if (selectedTxns.length === 0) return;
72+
if (confirm(`Execute mass purge of ${selectedTxns.length} records? This cannot be undone.`)) {
73+
try {
74+
setLoading(true);
75+
await adminService.deletePayments(selectedTxns);
76+
setSelectedTxns([]);
77+
await loadTransactions();
78+
} catch (error) {
79+
alert("Bulk deletion failed");
80+
} finally {
81+
setLoading(false);
82+
}
83+
}
84+
};
85+
86+
const toggleSelection = (id) => {
87+
setSelectedTxns(prev =>
88+
prev.includes(id) ? prev.filter(item => item !== id) : [...prev, id]
89+
);
90+
};
91+
92+
const toggleAll = () => {
93+
if (selectedTxns.length === transactions.length) {
94+
setSelectedTxns([]);
95+
} else {
96+
setSelectedTxns(transactions.map(t => t.id));
97+
}
98+
};
99+
58100
if (loading) {
59101
return <Loading message="Processing Ledger Data" />;
60102
}
@@ -72,6 +114,14 @@ const PaymentsPage = () => {
72114
</h1>
73115
</div>
74116
<div className="flex items-center gap-3">
117+
{selectedTxns.length > 0 && (
118+
<button
119+
onClick={handleBulkDelete}
120+
className="flex items-center gap-2 px-5 py-2.5 bg-rose-600 text-white rounded-2xl text-xs font-black uppercase tracking-wider hover:bg-rose-700 transition-all active:scale-95 shadow-lg shadow-rose-500/20"
121+
>
122+
<Trash2 size={14} /> Purge Selected ({selectedTxns.length})
123+
</button>
124+
)}
75125
<button className="flex items-center gap-2 px-5 py-2.5 bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-800 text-slate-600 dark:text-white rounded-2xl text-xs font-black uppercase tracking-wider hover:bg-slate-50 transition-all">
76126
<Filter size={14} /> Filter
77127
</button>
@@ -92,7 +142,15 @@ const PaymentsPage = () => {
92142
<table className="w-full min-w-[2000px] border-collapse">
93143
<thead>
94144
<tr className="bg-slate-100 dark:bg-slate-900 font-black text-[10px] text-slate-500 dark:text-white uppercase tracking-widest text-left whitespace-nowrap">
95-
<th className="px-4 py-3 border border-slate-200 dark:border-slate-800">No</th>
145+
<th className="px-4 py-3 border border-slate-200 dark:border-slate-800 w-10 text-center">
146+
<input
147+
type="checkbox"
148+
checked={selectedTxns.length === transactions.length && transactions.length > 0}
149+
onChange={toggleAll}
150+
className="w-4 h-4 rounded-none border-slate-300 text-blue-600 focus:ring-blue-500"
151+
/>
152+
</th>
153+
<th className="px-4 py-3 border border-slate-200 dark:border-slate-800 text-center">No</th>
96154
<th className="px-4 py-3 border border-slate-200 dark:border-slate-800">Business Name</th>
97155
<th className="px-4 py-3 border border-slate-200 dark:border-slate-800">Business ID</th>
98156
<th className="px-4 py-3 border border-slate-200 dark:border-slate-800">Owner Name</th>
@@ -110,8 +168,16 @@ const PaymentsPage = () => {
110168
</thead>
111169
<tbody className="divide-y divide-slate-100 dark:divide-slate-900">
112170
{transactions.map((txn, index) => (
113-
<tr key={txn.id} className="group hover:bg-blue-50/50 dark:hover:bg-blue-900/20 transition-all duration-300 even:bg-slate-50/50 dark:even:bg-slate-900/10 whitespace-nowrap">
114-
<td className="px-4 py-3 border border-slate-200 dark:border-slate-800">
171+
<tr key={txn.id} className={`group hover:bg-blue-50/50 dark:hover:bg-blue-900/20 transition-all duration-300 ${selectedTxns.includes(txn.id) ? 'bg-blue-50/30' : 'even:bg-slate-50/50 dark:even:bg-slate-900/10'} whitespace-nowrap`}>
172+
<td className="px-4 py-3 border border-slate-200 dark:border-slate-800 text-center">
173+
<input
174+
type="checkbox"
175+
checked={selectedTxns.includes(txn.id)}
176+
onChange={() => toggleSelection(txn.id)}
177+
className="w-4 h-4 rounded-none border-slate-300 text-blue-600 focus:ring-blue-500"
178+
/>
179+
</td>
180+
<td className="px-4 py-3 border border-slate-200 dark:border-slate-800 text-center">
115181
<span className="text-xs font-black text-slate-400 dark:text-white">{String(index + 1).padStart(2, '0')}</span>
116182
</td>
117183
<td className="px-4 py-3 border border-slate-200 dark:border-slate-800">
@@ -178,6 +244,13 @@ const PaymentsPage = () => {
178244
>
179245
<Eye size={12} />
180246
</button>
247+
<button
248+
onClick={() => handleDelete(txn.id)}
249+
className="p-1.5 text-rose-600 bg-rose-50 dark:bg-rose-900/20 hover:bg-rose-600 hover:text-white transition-all border border-rose-100 dark:border-rose-800"
250+
title="Purge Record"
251+
>
252+
<Trash2 size={12} />
253+
</button>
181254
{txn.status === 'pending' && (
182255
<>
183256
<button

src/services/adminService.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -563,5 +563,20 @@ export const adminService = {
563563
console.error("Error deleting multiple logs:", error);
564564
throw error;
565565
}
566+
},
567+
568+
async deletePayments(paymentIds) {
569+
try {
570+
const batch = writeBatch(db);
571+
paymentIds.forEach(id => {
572+
const docRef = doc(db, 'payments', id);
573+
batch.delete(docRef);
574+
});
575+
await batch.commit();
576+
return true;
577+
} catch (error) {
578+
console.error("Error deleting multiple payments:", error);
579+
throw error;
580+
}
566581
}
567582
};

0 commit comments

Comments
 (0)