From f618e5bbd61734ef956d0ee42df3bf3624a379cb Mon Sep 17 00:00:00 2001 From: counterweight Date: Tue, 23 Dec 2025 12:28:16 +0100 Subject: [PATCH] fix: use Set for actioningIds to prevent race condition Replace single actioningId state with a Set of IDs to properly track multiple concurrent actions. This prevents issues when a user rapidly clicks actions on different trades before the previous action completes. --- frontend/app/admin/trades/page.tsx | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/frontend/app/admin/trades/page.tsx b/frontend/app/admin/trades/page.tsx index 70212b0..57e31a4 100644 --- a/frontend/app/admin/trades/page.tsx +++ b/frontend/app/admin/trades/page.tsx @@ -34,8 +34,8 @@ export default function AdminTradesPage() { const [isLoadingTrades, setIsLoadingTrades] = useState(true); const [error, setError] = useState(null); - // Action state - const [actioningId, setActioningId] = useState(null); + // Action state - use Set to track multiple concurrent actions + const [actioningIds, setActioningIds] = useState>(new Set()); const [confirmAction, setConfirmAction] = useState<{ id: number; type: "complete" | "no_show" | "cancel"; @@ -100,7 +100,8 @@ export default function AdminTradesPage() { }, [user, isAuthorized, fetchUpcomingTrades, fetchPastTrades]); const handleAction = async (tradeId: number, action: "complete" | "no_show" | "cancel") => { - setActioningId(tradeId); + // Add this trade to the set of actioning trades + setActioningIds((prev) => new Set(prev).add(tradeId)); setError(null); try { @@ -120,7 +121,12 @@ export default function AdminTradesPage() { } catch (err) { setError(err instanceof Error ? err.message : `Failed to ${action} trade`); } finally { - setActioningId(null); + // Remove this trade from the set of actioning trades + setActioningIds((prev) => { + const next = new Set(prev); + next.delete(tradeId); + return next; + }); } }; @@ -284,14 +290,14 @@ export default function AdminTradesPage() { <>