import { useState, useEffect, forwardRef, useImperativeHandle } from 'react'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; import { Skeleton } from '@/components/ui/skeleton'; import { Button } from '@/components/ui/button'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { FileEdit, Plus, History, Shield, UserCog, FileCheck, FileX, Flag, AlertCircle, Star, AlertTriangle, Image as ImageIcon, CheckCircle, ChevronDown, ChevronUp, Trash2 } from 'lucide-react'; import { formatDistanceToNow } from 'date-fns'; import { fetchSystemActivities, SystemActivity, ActivityType, EntityChangeDetails, AdminActionDetails, SubmissionReviewDetails, ReportResolutionDetails, ReviewModerationDetails, PhotoApprovalDetails } from '@/lib/systemActivityService'; export interface SystemActivityLogRef { refresh: () => Promise; } interface SystemActivityLogProps { limit?: number; showFilters?: boolean; } const activityTypeConfig = { entity_change: { icon: FileEdit, color: 'text-blue-500', bgColor: 'bg-blue-500/10', label: 'Entity Change', }, admin_action: { icon: Shield, color: 'text-red-500', bgColor: 'bg-red-500/10', label: 'Admin Action', }, submission_review: { icon: FileCheck, color: 'text-green-500', bgColor: 'bg-green-500/10', label: 'Submission Review', }, report_resolution: { icon: Flag, color: 'text-orange-500', bgColor: 'bg-orange-500/10', label: 'Report Resolution', }, review_moderation: { icon: Star, color: 'text-purple-500', bgColor: 'bg-purple-500/10', label: 'Review Moderation', }, photo_approval: { icon: ImageIcon, color: 'text-teal-500', bgColor: 'bg-teal-500/10', label: 'Photo Approval', }, }; export const SystemActivityLog = forwardRef( ({ limit = 50, showFilters = true }, ref) => { const [activities, setActivities] = useState([]); const [isLoading, setIsLoading] = useState(true); const [filterType, setFilterType] = useState('all'); const [expandedIds, setExpandedIds] = useState>(new Set()); const loadActivities = async () => { setIsLoading(true); try { const data = await fetchSystemActivities(limit, { type: filterType === 'all' ? undefined : filterType, }); setActivities(data); } catch (error) { console.error('Error loading system activities:', error); } finally { setIsLoading(false); } }; useEffect(() => { loadActivities(); }, [limit, filterType]); useImperativeHandle(ref, () => ({ refresh: loadActivities, })); const toggleExpanded = (id: string) => { setExpandedIds(prev => { const next = new Set(prev); if (next.has(id)) { next.delete(id); } else { next.add(id); } return next; }); }; const renderActivityDetails = (activity: SystemActivity) => { const isExpanded = expandedIds.has(activity.id); switch (activity.type) { case 'entity_change': { const details = activity.details as EntityChangeDetails; return (
{details.change_type} {details.entity_type} {details.entity_name && ( {details.entity_name} )}
{isExpanded && details.change_reason && (

Reason: {details.change_reason}

)} {isExpanded && details.version_number && (

Version #{details.version_number}

)}
); } case 'admin_action': { const details = activity.details as AdminActionDetails; return (
{details.action.replace(/_/g, ' ')} {details.target_username && ( → @{details.target_username} )}
{isExpanded && details.details && (
                  {JSON.stringify(details.details, null, 2)}
                
)}
); } case 'submission_review': { const details = activity.details as SubmissionReviewDetails; const statusColor = details.status === 'approved' ? 'bg-green-500/10 text-green-500' : 'bg-red-500/10 text-red-500'; // Special handling for photo deletion submissions if (details.submission_type === 'photo_delete' && details.photo_url) { return (
{details.status} Photo deletion {details.entity_type && ( from {details.entity_type} )}
{details.photo_title
{details.photo_title && (

{details.photo_title}

)} {details.photo_caption && (

{details.photo_caption}

)} {details.deletion_reason && (
Reason: {details.deletion_reason}
)}
); } // Special handling for photo additions if (details.submission_type === 'photo' && details.photo_url) { return (
{details.status} Photo submission {details.entity_type && ( to {details.entity_type} )}
{isExpanded && (
{details.photo_title
{details.photo_title && (

{details.photo_title}

)} {details.photo_caption && (

{details.photo_caption}

)}
)}
); } // Generic submission display return (
{details.status} {details.submission_type} {details.entity_name && ( {details.entity_name} )}
); } case 'report_resolution': { const details = activity.details as ReportResolutionDetails; return (
{details.status} {details.reported_entity_type}
{isExpanded && details.resolution_notes && (

{details.resolution_notes}

)}
); } case 'review_moderation': { const details = activity.details as ReviewModerationDetails; return (
{details.moderation_status} {details.entity_type} review
); } case 'photo_approval': { const details = activity.details as PhotoApprovalDetails; return (
Approved {details.entity_type} photo
); } default: return null; } }; if (isLoading) { return ( System Activity Log Loading recent system activities... {Array.from({ length: 5 }).map((_, i) => (
))}
); } return (
System Activity Log Complete audit trail of all system changes and actions
{showFilters && ( )}
{activities.length === 0 ? (
No activities found
) : (
{activities.map((activity) => { const config = activityTypeConfig[activity.type]; const Icon = config.icon; const isExpanded = expandedIds.has(activity.id); return (
{activity.actor ? ( <> {activity.actor.username.slice(0, 2).toUpperCase()} {activity.actor.display_name || activity.actor.username} ) : ( System )} {activity.action}
{formatDistanceToNow(new Date(activity.timestamp), { addSuffix: true })}
{renderActivityDetails(activity)}
); })}
)}
); } ); SystemActivityLog.displayName = 'SystemActivityLog';