mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-20 09:11:12 -05:00
Fix Retry flashing and dependency validation
This commit is contained in:
@@ -776,6 +776,22 @@ export const ModerationQueue = forwardRef<ModerationQueueRef>((props, ref) => {
|
||||
|
||||
const handleRetryFailedItems = async (item: ModerationItem) => {
|
||||
setActionLoading(item.id);
|
||||
|
||||
// Optimistic UI update - remove from queue immediately
|
||||
const shouldRemove = (
|
||||
activeStatusFilter === 'pending' ||
|
||||
activeStatusFilter === 'flagged' ||
|
||||
activeStatusFilter === 'partially_approved'
|
||||
);
|
||||
|
||||
if (shouldRemove) {
|
||||
requestAnimationFrame(() => {
|
||||
setItems(prev => prev.filter(i => i.id !== item.id));
|
||||
recentlyRemovedRef.current.add(item.id);
|
||||
setTimeout(() => recentlyRemovedRef.current.delete(item.id), 3000);
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
// Fetch failed/rejected submission items
|
||||
const { data: failedItems, error: fetchError } = await supabase
|
||||
@@ -812,7 +828,6 @@ export const ModerationQueue = forwardRef<ModerationQueueRef>((props, ref) => {
|
||||
description: `Processed ${failedItems.length} failed item(s)`,
|
||||
});
|
||||
|
||||
// No refresh needed - item already updated optimistically
|
||||
} catch (error: any) {
|
||||
console.error('Error retrying failed items:', error);
|
||||
toast({
|
||||
|
||||
@@ -197,6 +197,7 @@ serve(async (req) => {
|
||||
itemType: string;
|
||||
success: boolean;
|
||||
error?: string;
|
||||
isDependencyFailure?: boolean;
|
||||
}> = [];
|
||||
|
||||
// Process items in order
|
||||
@@ -285,23 +286,71 @@ serve(async (req) => {
|
||||
console.log(`Successfully approved item ${item.id} -> entity ${entityId}`);
|
||||
} catch (error) {
|
||||
console.error(`Error processing item ${item.id}:`, error);
|
||||
|
||||
const isDependencyError = error instanceof Error && (
|
||||
error.message.includes('Missing dependency') ||
|
||||
error.message.includes('depends on') ||
|
||||
error.message.includes('Circular dependency')
|
||||
);
|
||||
|
||||
approvalResults.push({
|
||||
itemId: item.id,
|
||||
itemType: item.item_type,
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Unknown error'
|
||||
error: error instanceof Error ? error.message : 'Unknown error',
|
||||
isDependencyFailure: isDependencyError
|
||||
});
|
||||
|
||||
// Mark item as rejected in submission_items
|
||||
const { error: markRejectedError } = await supabase
|
||||
.from('submission_items')
|
||||
.update({
|
||||
status: 'rejected',
|
||||
rejection_reason: error instanceof Error ? error.message : 'Unknown error',
|
||||
updated_at: new Date().toISOString()
|
||||
})
|
||||
.eq('id', item.id);
|
||||
|
||||
if (markRejectedError) {
|
||||
console.error(`Failed to mark item ${item.id} as rejected:`, markRejectedError);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update submission status
|
||||
// Check if any failures were dependency-related
|
||||
const hasDependencyFailure = approvalResults.some(r =>
|
||||
!r.success && r.isDependencyFailure
|
||||
);
|
||||
|
||||
const allApproved = approvalResults.every(r => r.success);
|
||||
const someApproved = approvalResults.some(r => r.success);
|
||||
const allFailed = approvalResults.every(r => !r.success);
|
||||
|
||||
// Determine final status:
|
||||
// - If dependency validation failed: keep pending for escalation
|
||||
// - If all approved: approved
|
||||
// - If some approved: partially_approved
|
||||
// - If all failed but no dependency issues: rejected (can retry)
|
||||
const finalStatus = hasDependencyFailure && !someApproved
|
||||
? 'pending' // Keep pending for escalation only
|
||||
: allApproved
|
||||
? 'approved'
|
||||
: allFailed
|
||||
? 'rejected' // Total failure, allow retry
|
||||
: 'partially_approved'; // Mixed results
|
||||
|
||||
const reviewerNotes = hasDependencyFailure && !someApproved
|
||||
? 'Submission has unresolved dependencies. Escalation required.'
|
||||
: undefined;
|
||||
|
||||
const { error: updateError } = await supabase
|
||||
.from('content_submissions')
|
||||
.update({
|
||||
status: allApproved ? 'approved' : 'partially_approved',
|
||||
status: finalStatus,
|
||||
reviewer_id: authenticatedUserId,
|
||||
reviewed_at: new Date().toISOString()
|
||||
reviewed_at: new Date().toISOString(),
|
||||
reviewer_notes: reviewerNotes,
|
||||
escalated: hasDependencyFailure && !someApproved ? true : undefined
|
||||
})
|
||||
.eq('id', submissionId);
|
||||
|
||||
@@ -313,7 +362,7 @@ serve(async (req) => {
|
||||
JSON.stringify({
|
||||
success: true,
|
||||
results: approvalResults,
|
||||
submissionStatus: allApproved ? 'approved' : 'partially_approved'
|
||||
submissionStatus: finalStatus
|
||||
}),
|
||||
{ headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
|
||||
);
|
||||
@@ -335,7 +384,10 @@ function topologicalSort(items: any[]): any[] {
|
||||
const visit = (item: any) => {
|
||||
if (visited.has(item.id)) return;
|
||||
if (visiting.has(item.id)) {
|
||||
throw new Error(`Circular dependency detected for item ${item.id}`);
|
||||
throw new Error(
|
||||
`Circular dependency detected: item ${item.id} (${item.item_type}) ` +
|
||||
`creates a dependency loop. This submission requires escalation.`
|
||||
);
|
||||
}
|
||||
|
||||
visiting.add(item.id);
|
||||
@@ -343,7 +395,11 @@ function topologicalSort(items: any[]): any[] {
|
||||
if (item.depends_on) {
|
||||
const parent = items.find(i => i.id === item.depends_on);
|
||||
if (!parent) {
|
||||
throw new Error(`Missing dependency: item ${item.id} depends on ${item.depends_on} which is not in the submission`);
|
||||
throw new Error(
|
||||
`Missing dependency: item ${item.id} (${item.item_type}) ` +
|
||||
`depends on ${item.depends_on} which is not in this submission or has not been approved. ` +
|
||||
`This submission requires escalation.`
|
||||
);
|
||||
}
|
||||
visit(parent);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user