Files
thrilltrack-explorer/src/components/moderation/EditHistoryAccordion.tsx
2025-11-03 20:30:01 +00:00

148 lines
4.8 KiB
TypeScript

import { useState } from 'react';
import { useQuery } from '@tanstack/react-query';
import { fetchEditHistory } from '@/lib/submissionItemsService';
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/components/ui/accordion';
import { Button } from '@/components/ui/button';
import { ScrollArea } from '@/components/ui/scroll-area';
import { Alert, AlertDescription } from '@/components/ui/alert';
import { EditHistoryEntry } from './EditHistoryEntry';
import { History, Loader2, AlertCircle } from 'lucide-react';
interface EditHistoryRecord {
id: string;
item_id: string;
edited_at: string;
previous_data: Record<string, unknown>;
new_data: Record<string, unknown>;
edit_reason: string | null;
changed_fields: string[];
profiles?: {
username: string;
avatar_url?: string | null;
} | null;
}
interface EditHistoryAccordionProps {
submissionId: string;
}
const INITIAL_LOAD = 20;
const LOAD_MORE_INCREMENT = 10;
export function EditHistoryAccordion({ submissionId }: EditHistoryAccordionProps) {
const [limit, setLimit] = useState(INITIAL_LOAD);
const { data: editHistory, isLoading, error } = useQuery({
queryKey: ['edit-history', submissionId, limit],
queryFn: async () => {
const { supabase } = await import('@/integrations/supabase/client');
// Fetch edit history with user profiles
const { data, error } = await supabase
.from('item_edit_history')
.select(`
id,
item_id,
edited_at,
previous_data,
new_data,
edit_reason,
changed_fields,
profiles:edited_by (
username,
avatar_url
)
`)
.eq('item_id', submissionId)
.order('edited_at', { ascending: false })
.limit(limit);
if (error) throw error;
return (data || []) as unknown as EditHistoryRecord[];
},
staleTime: 5 * 60 * 1000, // 5 minutes
});
const loadMore = () => {
setLimit(prev => prev + LOAD_MORE_INCREMENT);
};
const hasMore = editHistory && editHistory.length === limit;
return (
<Accordion type="single" collapsible className="w-full">
<AccordionItem value="edit-history">
<AccordionTrigger className="hover:no-underline">
<div className="flex items-center gap-2">
<History className="h-4 w-4" />
<span>Edit History</span>
{editHistory && editHistory.length > 0 && (
<span className="text-xs text-muted-foreground">
({editHistory.length} edit{editHistory.length !== 1 ? 's' : ''})
</span>
)}
</div>
</AccordionTrigger>
<AccordionContent>
{isLoading && (
<div className="flex items-center justify-center py-8">
<Loader2 className="h-6 w-6 animate-spin text-muted-foreground" />
</div>
)}
{error && (
<Alert variant="destructive">
<AlertCircle className="h-4 w-4" />
<AlertDescription>
Failed to load edit history: {error instanceof Error ? error.message : 'Unknown error'}
</AlertDescription>
</Alert>
)}
{!isLoading && !error && editHistory && editHistory.length === 0 && (
<Alert>
<AlertDescription>
No edit history found for this submission.
</AlertDescription>
</Alert>
)}
{!isLoading && !error && editHistory && editHistory.length > 0 && (
<div className="space-y-4">
<ScrollArea className="h-[400px] pr-4">
<div className="space-y-3">
{editHistory.map((entry: EditHistoryRecord) => (
<EditHistoryEntry
key={entry.id}
editId={entry.id}
editorName={entry.profiles?.username || 'Unknown User'}
editorAvatar={entry.profiles?.avatar_url || undefined}
timestamp={entry.edited_at}
changedFields={entry.changed_fields || []}
editReason={entry.edit_reason || undefined}
beforeData={entry.previous_data}
afterData={entry.new_data}
/>
))}
</div>
</ScrollArea>
{hasMore && (
<div className="flex justify-center pt-2">
<Button
variant="outline"
size="sm"
onClick={loadMore}
>
Load More
</Button>
</div>
)}
</div>
)}
</AccordionContent>
</AccordionItem>
</Accordion>
);
}