mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-25 13:51:13 -05:00
Refactor: Complete error handling overhaul
This commit is contained in:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user