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.
This commit is contained in:
counterweight 2025-12-23 12:28:16 +01:00
parent b5d831b364
commit f618e5bbd6
Signed by: counterweight
GPG key ID: 883EDBAA726BD96C

View file

@ -34,8 +34,8 @@ export default function AdminTradesPage() {
const [isLoadingTrades, setIsLoadingTrades] = useState(true); const [isLoadingTrades, setIsLoadingTrades] = useState(true);
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
// Action state // Action state - use Set to track multiple concurrent actions
const [actioningId, setActioningId] = useState<number | null>(null); const [actioningIds, setActioningIds] = useState<Set<number>>(new Set());
const [confirmAction, setConfirmAction] = useState<{ const [confirmAction, setConfirmAction] = useState<{
id: number; id: number;
type: "complete" | "no_show" | "cancel"; type: "complete" | "no_show" | "cancel";
@ -100,7 +100,8 @@ export default function AdminTradesPage() {
}, [user, isAuthorized, fetchUpcomingTrades, fetchPastTrades]); }, [user, isAuthorized, fetchUpcomingTrades, fetchPastTrades]);
const handleAction = async (tradeId: number, action: "complete" | "no_show" | "cancel") => { 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); setError(null);
try { try {
@ -120,7 +121,12 @@ export default function AdminTradesPage() {
} catch (err) { } catch (err) {
setError(err instanceof Error ? err.message : `Failed to ${action} trade`); setError(err instanceof Error ? err.message : `Failed to ${action} trade`);
} finally { } 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() {
<> <>
<button <button
onClick={() => handleAction(trade.id, confirmAction.type)} onClick={() => handleAction(trade.id, confirmAction.type)}
disabled={actioningId === trade.id} disabled={actioningIds.has(trade.id)}
style={ style={
confirmAction.type === "cancel" confirmAction.type === "cancel"
? styles.dangerButton ? styles.dangerButton
: styles.successButton : styles.successButton
} }
> >
{actioningId === trade.id ? "..." : "Confirm"} {actioningIds.has(trade.id) ? "..." : "Confirm"}
</button> </button>
<button <button
onClick={() => setConfirmAction(null)} onClick={() => setConfirmAction(null)}