Fix flashing and bouncing on admin page

This commit is contained in:
gpt-engineer-app[bot]
2025-10-09 17:33:19 +00:00
parent 9d3bdcf2e0
commit 3f2e6504aa
2 changed files with 697 additions and 18 deletions

View File

@@ -1,24 +1,17 @@
import { useState, useEffect, useImperativeHandle, forwardRef, useCallback, useRef } from 'react';
import { CheckCircle, XCircle, Eye, Calendar, User, Filter, MessageSquare, FileText, Image, X, Trash2, ListTree, RefreshCw, AlertCircle, Clock, Lock, Unlock, AlertTriangle, UserCog, Zap } from 'lucide-react';
import { CheckCircle, XCircle, Filter, MessageSquare, FileText, Image, X, RefreshCw, AlertCircle, Clock, Lock, Unlock, AlertTriangle, UserCog, Zap } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
import { Card, CardContent, CardHeader } from '@/components/ui/card';
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
import { Textarea } from '@/components/ui/textarea';
import { Label } from '@/components/ui/label';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
import { supabase } from '@/integrations/supabase/client';
import { useToast } from '@/hooks/use-toast';
import { useUserRole } from '@/hooks/useUserRole';
import { useAuth } from '@/hooks/useAuth';
import { format, formatDistance } from 'date-fns';
import { formatDistance } from 'date-fns';
import { PhotoModal } from './PhotoModal';
import { SubmissionReviewManager } from './SubmissionReviewManager';
import { useIsMobile } from '@/hooks/use-mobile';
import { SubmissionChangesDisplay } from './SubmissionChangesDisplay';
import { SubmissionItemsList } from './SubmissionItemsList';
import { MeasurementDisplay } from '@/components/ui/measurement-display';
import { useAdminSettings } from '@/hooks/useAdminSettings';
import { useModerationQueue } from '@/hooks/useModerationQueue';
import { Progress } from '@/components/ui/progress';
@@ -27,6 +20,7 @@ import { EscalationDialog } from './EscalationDialog';
import { ReassignDialog } from './ReassignDialog';
import { smartMergeArray } from '@/lib/smartStateUpdate';
import { useDebounce } from '@/hooks/useDebounce';
import { QueueItem } from './QueueItem';
interface ModerationItem {
id: string;
@@ -816,10 +810,7 @@ export const ModerationQueue = forwardRef<ModerationQueueRef>((props, ref) => {
description: `Processed ${failedItems.length} failed item(s)`,
});
// Silent cleanup after delay
setTimeout(() => {
fetchItems(activeEntityFilter, activeStatusFilter, true);
}, 2000);
// No refresh needed - item already updated optimistically
} catch (error: any) {
console.error('Error retrying failed items:', error);
toast({
@@ -1404,6 +1395,34 @@ export const ModerationQueue = forwardRef<ModerationQueueRef>((props, ref) => {
}
};
// Memoized callbacks
const handleNoteChange = useCallback((id: string, value: string) => {
setNotes(prev => ({ ...prev, [id]: value }));
}, []);
const handleOpenPhotos = useCallback((photos: any[], index: number) => {
setSelectedPhotos(photos);
setSelectedPhotoIndex(index);
setPhotoModalOpen(true);
}, []);
const handleOpenReviewManager = useCallback((id: string) => {
setSelectedSubmissionId(id);
setReviewManagerOpen(true);
}, []);
const handleInteractionFocus = useCallback((id: string) => {
setInteractingWith(prev => new Set(prev).add(id));
}, []);
const handleInteractionBlur = useCallback((id: string) => {
setInteractingWith(prev => {
const next = new Set(prev);
next.delete(id);
return next;
});
}, []);
const QueueContent = () => {
if (isInitialLoad && loading) {
return (
@@ -1428,7 +1447,32 @@ export const ModerationQueue = forwardRef<ModerationQueueRef>((props, ref) => {
return (
<div className="space-y-6">
{items.map((item) => (
<Card
<QueueItem
key={item.id}
item={item}
isMobile={isMobile}
actionLoading={actionLoading}
lockedSubmissions={lockedSubmissions}
currentLockSubmissionId={queue.currentLock?.submissionId}
notes={notes}
isAdmin={isAdmin()}
isSuperuser={isSuperuser()}
queueIsLoading={queue.isLoading}
onNoteChange={handleNoteChange}
onApprove={handleModerationAction}
onResetToPending={handleResetToPending}
onRetryFailed={handleRetryFailedItems}
onOpenPhotos={handleOpenPhotos}
onOpenReviewManager={handleOpenReviewManager}
onClaimSubmission={(id) => queue.claimSubmission(id)}
onDeleteSubmission={handleDeleteSubmission}
onInteractionFocus={handleInteractionFocus}
onInteractionBlur={handleInteractionBlur}
/>
))}
</div>
);
};
key={item.id}
className={`border-l-4 transition-opacity duration-200 ${
item.status === 'flagged' ? 'border-l-red-500' :
@@ -2424,10 +2468,8 @@ export const ModerationQueue = forwardRef<ModerationQueueRef>((props, ref) => {
open={reviewManagerOpen}
onOpenChange={setReviewManagerOpen}
onComplete={() => {
// Silent cleanup after delay
setTimeout(() => {
fetchItems(activeEntityFilter, activeStatusFilter, true);
}, 2000);
// No refresh needed - item was removed optimistically
setReviewManagerOpen(false);
}}
/>
)}