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 [error, setError] = useState<string | null>(null);
// Action state
const [actioningId, setActioningId] = useState<number | null>(null);
// Action state - use Set to track multiple concurrent actions
const [actioningIds, setActioningIds] = useState<Set<number>>(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() {
<>
<button
onClick={() => handleAction(trade.id, confirmAction.type)}
disabled={actioningId === trade.id}
disabled={actioningIds.has(trade.id)}
style={
confirmAction.type === "cancel"
? styles.dangerButton
: styles.successButton
}
>
{actioningId === trade.id ? "..." : "Confirm"}
{actioningIds.has(trade.id) ? "..." : "Confirm"}
</button>
<button
onClick={() => setConfirmAction(null)}