Fix admin page flashing

This commit is contained in:
gpt-engineer-app[bot]
2025-10-09 17:14:45 +00:00
parent ff7c90e62d
commit 356cf2b54b
4 changed files with 69 additions and 36 deletions

View File

@@ -541,8 +541,8 @@ export const ModerationQueue = forwardRef<ModerationQueueRef>((props, ref) => {
}, []); // Empty deps - use refs instead
// Debounced filters to prevent rapid-fire calls
const debouncedEntityFilter = useDebounce(activeEntityFilter, 500);
const debouncedStatusFilter = useDebounce(activeStatusFilter, 500);
const debouncedEntityFilter = useDebounce(activeEntityFilter, 1000);
const debouncedStatusFilter = useDebounce(activeStatusFilter, 1000);
// Store latest filter values in ref to avoid dependency issues
const filtersRef = useRef({ entityFilter: debouncedEntityFilter, statusFilter: debouncedStatusFilter });
@@ -568,9 +568,18 @@ export const ModerationQueue = forwardRef<ModerationQueueRef>((props, ref) => {
}
fetchDebounceRef.current = setTimeout(() => {
fetchItems(entityFilter, statusFilter, silent, tab);
}, 100);
}, 1000); // 1 second debounce
}, [fetchItems]);
// Clean up debounce on unmount
useEffect(() => {
return () => {
if (fetchDebounceRef.current) {
clearTimeout(fetchDebounceRef.current);
}
};
}, []);
// Initial fetch on mount and filter changes
useEffect(() => {
if (!user) return;
@@ -797,10 +806,8 @@ export const ModerationQueue = forwardRef<ModerationQueueRef>((props, ref) => {
description: "Submission and all items have been reset to pending status",
});
// Silent cleanup after delay
setTimeout(() => {
fetchItems(activeEntityFilter, activeStatusFilter, true);
}, 2000);
// Optimistic update - item will reappear via realtime
setItems(prev => prev.filter(i => i.id !== item.id));
} catch (error: any) {
console.error('Error resetting submission:', error);
toast({
@@ -1008,10 +1015,9 @@ export const ModerationQueue = forwardRef<ModerationQueueRef>((props, ref) => {
description: "All entities created successfully",
});
// Silent cleanup after delay
setTimeout(() => {
fetchItems(activeEntityFilter, activeStatusFilter, true);
}, 2000);
// Optimistic update - remove from queue
setItems(prev => prev.filter(i => i.id !== item.id));
recentlyRemovedRef.current.add(item.id);
return;
}
@@ -1107,10 +1113,9 @@ export const ModerationQueue = forwardRef<ModerationQueueRef>((props, ref) => {
description: `Successfully approved and published ${photoSubmission.items.length} photo(s)`,
});
// Silent cleanup after delay
setTimeout(() => {
fetchItems(activeEntityFilter, activeStatusFilter, true);
}, 2000);
// Optimistic update - remove from queue
setItems(prev => prev.filter(i => i.id !== item.id));
recentlyRemovedRef.current.add(item.id);
return;
} catch (error: any) {
@@ -1149,10 +1154,9 @@ export const ModerationQueue = forwardRef<ModerationQueueRef>((props, ref) => {
description: `Successfully processed ${submissionItems.length} item(s)`,
});
// Silent cleanup after delay
setTimeout(() => {
fetchItems(activeEntityFilter, activeStatusFilter, true);
}, 2000);
// Optimistic update - remove from queue
setItems(prev => prev.filter(i => i.id !== item.id));
recentlyRemovedRef.current.add(item.id);
return;
} else if (action === 'rejected') {
// Cascade rejection to all pending items
@@ -1225,10 +1229,11 @@ export const ModerationQueue = forwardRef<ModerationQueueRef>((props, ref) => {
return newNotes;
});
// Silent cleanup after delay
setTimeout(() => {
fetchItems(activeEntityFilter, activeStatusFilter, true);
}, 2000);
// Optimistic update - remove from queue if approved or rejected
if (action === 'approved' || action === 'rejected') {
setItems(prev => prev.filter(i => i.id !== item.id));
recentlyRemovedRef.current.add(item.id);
}
} catch (error: any) {
console.error('Error moderating content:', error);

View File

@@ -1,10 +1,10 @@
import { useState, useEffect } from 'react';
import { useState, useEffect, memo } from 'react';
import { supabase } from '@/integrations/supabase/client';
import { SubmissionChangesDisplay } from './SubmissionChangesDisplay';
import { PhotoSubmissionDisplay } from './PhotoSubmissionDisplay';
import { Skeleton } from '@/components/ui/skeleton';
import { Alert, AlertDescription } from '@/components/ui/alert';
import { AlertCircle } from 'lucide-react';
import { AlertCircle, Loader2 } from 'lucide-react';
import type { SubmissionItemData } from '@/types/submissions';
interface SubmissionItemsListProps {
@@ -13,7 +13,7 @@ interface SubmissionItemsListProps {
showImages?: boolean;
}
export function SubmissionItemsList({
export const SubmissionItemsList = memo(function SubmissionItemsList({
submissionId,
view = 'summary',
showImages = true
@@ -21,6 +21,7 @@ export function SubmissionItemsList({
const [items, setItems] = useState<SubmissionItemData[]>([]);
const [hasPhotos, setHasPhotos] = useState(false);
const [loading, setLoading] = useState(true);
const [refreshing, setRefreshing] = useState(false);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
@@ -29,7 +30,12 @@ export function SubmissionItemsList({
const fetchSubmissionItems = async () => {
try {
setLoading(true);
// Only show skeleton on initial load, show refreshing indicator on refresh
if (loading) {
setLoading(true);
} else {
setRefreshing(true);
}
setError(null);
// Fetch submission items
@@ -58,6 +64,7 @@ export function SubmissionItemsList({
setError('Failed to load submission details');
} finally {
setLoading(false);
setRefreshing(false);
}
};
@@ -89,6 +96,13 @@ export function SubmissionItemsList({
return (
<div className="flex flex-col gap-3">
{refreshing && (
<div className="flex items-center gap-2 text-xs text-muted-foreground">
<Loader2 className="h-3 w-3 animate-spin" />
<span>Refreshing...</span>
</div>
)}
{/* Show regular submission items */}
{items.map((item) => (
<div key={item.id} className={view === 'summary' ? 'border-l-2 border-primary/20 pl-3' : ''}>
@@ -109,4 +123,4 @@ export function SubmissionItemsList({
)}
</div>
);
}
});