Refactor: Complete error handling overhaul

This commit is contained in:
gpt-engineer-app[bot]
2025-11-02 23:19:46 +00:00
parent d057ddc8cc
commit 35c7c3e957
7 changed files with 303 additions and 26 deletions

View File

@@ -1,4 +1,5 @@
import { memo, useCallback } from 'react';
import { useDebouncedCallback } from 'use-debounce';
import {
CheckCircle, XCircle, RefreshCw, AlertCircle, Lock, Trash2,
Edit, Info, ExternalLink, ChevronDown, ListTree, Calendar
@@ -66,13 +67,31 @@ export const QueueItemActions = memo(({
onNoteChange(item.id, e.target.value);
}, [onNoteChange, item.id]);
const handleApprove = useCallback(() => {
onApprove(item, 'approved', notes[item.id]);
}, [onApprove, item, notes]);
// Debounced handlers to prevent duplicate submissions
const handleApprove = useDebouncedCallback(
() => {
// Extra guard against race conditions
if (actionLoading === item.id) {
console.warn('⚠️ Action already in progress, ignoring duplicate request');
return;
}
onApprove(item, 'approved', notes[item.id]);
},
300, // 300ms debounce
{ leading: true, trailing: false } // Only fire on first click
);
const handleReject = useCallback(() => {
onApprove(item, 'rejected', notes[item.id]);
}, [onApprove, item, notes]);
const handleReject = useDebouncedCallback(
() => {
if (actionLoading === item.id) {
console.warn('⚠️ Action already in progress, ignoring duplicate request');
return;
}
onApprove(item, 'rejected', notes[item.id]);
},
300,
{ leading: true, trailing: false }
);
const handleResetToPending = useCallback(() => {
onResetToPending(item);
@@ -106,13 +125,29 @@ export const QueueItemActions = memo(({
onNoteChange(`reverse-${item.id}`, e.target.value);
}, [onNoteChange, item.id]);
const handleReverseApprove = useCallback(() => {
onApprove(item, 'approved', notes[`reverse-${item.id}`]);
}, [onApprove, item, notes]);
const handleReverseApprove = useDebouncedCallback(
() => {
if (actionLoading === item.id) {
console.warn('⚠️ Action already in progress, ignoring duplicate request');
return;
}
onApprove(item, 'approved', notes[`reverse-${item.id}`]);
},
300,
{ leading: true, trailing: false }
);
const handleReverseReject = useCallback(() => {
onApprove(item, 'rejected', notes[`reverse-${item.id}`]);
}, [onApprove, item, notes]);
const handleReverseReject = useDebouncedCallback(
() => {
if (actionLoading === item.id) {
console.warn('⚠️ Action already in progress, ignoring duplicate request');
return;
}
onApprove(item, 'rejected', notes[`reverse-${item.id}`]);
},
300,
{ leading: true, trailing: false }
);
return (
<>
@@ -249,8 +284,20 @@ export const QueueItemActions = memo(({
className={`flex-1 ${isMobile ? 'h-11' : ''}`}
size={isMobile ? "default" : "default"}
>
<CheckCircle className={isMobile ? "w-5 h-5 mr-2" : "w-4 h-4 mr-2"} />
Approve
{actionLoading === item.id ? (
<>
<RefreshCw className={`${isMobile ? "w-5 h-5" : "w-4 h-4"} mr-2 animate-spin`} />
{item.submission_items && item.submission_items.length > 5
? `Processing ${item.submission_items.length} items...`
: 'Processing...'
}
</>
) : (
<>
<CheckCircle className={isMobile ? "w-5 h-5 mr-2" : "w-4 h-4 mr-2"} />
Approve
</>
)}
</Button>
<Button
variant="destructive"
@@ -259,8 +306,17 @@ export const QueueItemActions = memo(({
className={`flex-1 ${isMobile ? 'h-11' : ''}`}
size={isMobile ? "default" : "default"}
>
<XCircle className={isMobile ? "w-5 h-5 mr-2" : "w-4 h-4 mr-2"} />
Reject
{actionLoading === item.id ? (
<>
<RefreshCw className={`${isMobile ? "w-5 h-5" : "w-4 h-4"} mr-2 animate-spin`} />
Processing...
</>
) : (
<>
<XCircle className={isMobile ? "w-5 h-5 mr-2" : "w-4 h-4 mr-2"} />
Reject
</>
)}
</Button>
</div>
</div>