Fix partially approved submissions

This commit is contained in:
gpt-engineer-app[bot]
2025-10-02 15:43:27 +00:00
parent dfc697b002
commit 083773f1ea

View File

@@ -1,5 +1,5 @@
import { useState, useEffect, useImperativeHandle, forwardRef } from 'react';
import { CheckCircle, XCircle, Eye, Calendar, User, Filter, MessageSquare, FileText, Image, X, Trash2, ListTree } from 'lucide-react';
import { CheckCircle, XCircle, Eye, Calendar, User, Filter, MessageSquare, FileText, Image, X, Trash2, ListTree, RefreshCw, AlertCircle } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
import { Card, CardContent, CardHeader } from '@/components/ui/card';
@@ -44,7 +44,7 @@ interface ModerationItem {
}
type EntityFilter = 'all' | 'reviews' | 'submissions' | 'photos';
type StatusFilter = 'all' | 'pending' | 'flagged' | 'approved' | 'rejected';
type StatusFilter = 'all' | 'pending' | 'partially_approved' | 'flagged' | 'approved' | 'rejected';
export interface ModerationQueueRef {
refresh: () => void;
@@ -90,11 +90,15 @@ export const ModerationQueue = forwardRef<ModerationQueueRef>((props, ref) => {
switch (statusFilter) {
case 'all':
reviewStatuses = ['pending', 'flagged', 'approved', 'rejected'];
submissionStatuses = ['pending', 'approved', 'rejected'];
submissionStatuses = ['pending', 'partially_approved', 'approved', 'rejected'];
break;
case 'pending':
reviewStatuses = ['pending'];
submissionStatuses = ['pending'];
submissionStatuses = ['pending', 'partially_approved'];
break;
case 'partially_approved':
reviewStatuses = [];
submissionStatuses = ['partially_approved'];
break;
case 'flagged':
reviewStatuses = ['flagged'];
@@ -110,7 +114,7 @@ export const ModerationQueue = forwardRef<ModerationQueueRef>((props, ref) => {
break;
default:
reviewStatuses = ['pending', 'flagged'];
submissionStatuses = ['pending'];
submissionStatuses = ['pending', 'partially_approved'];
}
// Fetch reviews with entity data
@@ -379,6 +383,58 @@ export const ModerationQueue = forwardRef<ModerationQueueRef>((props, ref) => {
}
}, [activeEntityFilter, activeStatusFilter, user]);
const handleRetryFailedItems = async (item: ModerationItem) => {
setActionLoading(item.id);
try {
// Fetch failed/rejected submission items
const { data: failedItems, error: fetchError } = await supabase
.from('submission_items')
.select('id')
.eq('submission_id', item.id)
.eq('status', 'rejected');
if (fetchError) throw fetchError;
if (!failedItems || failedItems.length === 0) {
toast({
title: "No Failed Items",
description: "All items have been processed successfully",
});
return;
}
// Call edge function to retry failed items
const { data, error } = await supabase.functions.invoke(
'process-selective-approval',
{
body: {
itemIds: failedItems.map(i => i.id),
userId: user?.id,
submissionId: item.id
}
}
);
if (error) throw error;
toast({
title: "Retry Complete",
description: `Processed ${failedItems.length} failed item(s)`,
});
fetchItems(activeEntityFilter, activeStatusFilter);
} catch (error: any) {
console.error('Error retrying failed items:', error);
toast({
title: "Retry Failed",
description: error.message,
variant: "destructive",
});
} finally {
setActionLoading(null);
}
};
const handleModerationAction = async (
item: ModerationItem,
action: 'approved' | 'rejected',
@@ -939,6 +995,8 @@ export const ModerationQueue = forwardRef<ModerationQueueRef>((props, ref) => {
switch (status) {
case 'pending':
return 'secondary';
case 'partially_approved':
return 'secondary';
case 'flagged':
return 'destructive';
case 'approved':
@@ -958,6 +1016,8 @@ export const ModerationQueue = forwardRef<ModerationQueueRef>((props, ref) => {
switch (statusFilter) {
case 'pending':
return `No pending ${entityLabel} require moderation at this time.`;
case 'partially_approved':
return `No partially approved ${entityLabel} found.`;
case 'flagged':
return `No flagged ${entityLabel} found.`;
case 'approved':
@@ -999,6 +1059,7 @@ export const ModerationQueue = forwardRef<ModerationQueueRef>((props, ref) => {
item.status === 'flagged' ? 'border-l-red-500' :
item.status === 'approved' ? 'border-l-green-500' :
item.status === 'rejected' ? 'border-l-red-400' :
item.status === 'partially_approved' ? 'border-l-yellow-500' :
'border-l-amber-500'
}`}>
<CardHeader className={isMobile ? "pb-3 p-4" : "pb-4"}>
@@ -1023,8 +1084,15 @@ export const ModerationQueue = forwardRef<ModerationQueueRef>((props, ref) => {
)}
</Badge>
<Badge variant={getStatusBadgeVariant(item.status)} className={isMobile ? "text-xs" : ""}>
{item.status.charAt(0).toUpperCase() + item.status.slice(1)}
{item.status === 'partially_approved' ? 'Partially Approved' :
item.status.charAt(0).toUpperCase() + item.status.slice(1)}
</Badge>
{item.status === 'partially_approved' && (
<Badge variant="outline" className="bg-yellow-100 dark:bg-yellow-900/30 text-yellow-800 dark:text-yellow-300 border-yellow-300 dark:border-yellow-700">
<AlertCircle className="w-3 h-3 mr-1" />
Needs Retry
</Badge>
)}
</div>
<div className={`flex items-center gap-2 text-muted-foreground ${isMobile ? 'text-xs' : 'text-sm'}`}>
<Calendar className={isMobile ? "w-3 h-3" : "w-4 h-4"} />
@@ -1467,6 +1535,41 @@ export const ModerationQueue = forwardRef<ModerationQueueRef>((props, ref) => {
</div>
</>
)}
{/* Retry button for partially approved items */}
{item.status === 'partially_approved' && item.type === 'content_submission' && (
<div className="space-y-3 pt-4 border-t bg-yellow-50 dark:bg-yellow-950/20 -mx-4 px-4 py-3 rounded-b-lg">
<div className="flex items-start gap-2 text-sm text-yellow-800 dark:text-yellow-300">
<AlertCircle className="w-5 h-5 mt-0.5 flex-shrink-0" />
<div>
<p className="font-medium">This submission was partially approved</p>
<p className="text-xs mt-1">Some items failed to process. Click "Retry Failed Items" to process them again.</p>
</div>
</div>
<div className="flex gap-2">
<Button
onClick={() => {
setSelectedSubmissionId(item.id);
setReviewManagerOpen(true);
}}
disabled={actionLoading === item.id}
variant="outline"
className="flex-1"
>
<ListTree className="w-4 h-4 mr-2" />
Review Items
</Button>
<Button
onClick={() => handleRetryFailedItems(item)}
disabled={actionLoading === item.id}
className="flex-1 bg-yellow-600 hover:bg-yellow-700"
>
<RefreshCw className="w-4 h-4 mr-2" />
Retry Failed Items
</Button>
</div>
</div>
)}
{/* Reviewer Information for approved/rejected items */}
{(item.status === 'approved' || item.status === 'rejected') && (item.reviewed_at || item.reviewer_notes) && (
@@ -1629,6 +1732,7 @@ export const ModerationQueue = forwardRef<ModerationQueueRef>((props, ref) => {
<SelectContent>
<SelectItem value="all">All Status</SelectItem>
<SelectItem value="pending">Pending</SelectItem>
<SelectItem value="partially_approved">Partially Approved</SelectItem>
{activeEntityFilter !== 'submissions' && activeEntityFilter !== 'photos' && (
<SelectItem value="flagged">Flagged</SelectItem>
)}