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 { Input } from '@/components/ui/input'; import { FileEdit, Plus, History, Shield, UserCog, FileCheck, FileX, Flag, AlertCircle, Star, AlertTriangle, Image as ImageIcon, CheckCircle, ChevronDown, ChevronUp, Trash2, UserPlus, UserX, Ban, UserCheck, MessageSquare, MessageSquareX, Search, RefreshCw, Filter, X } from 'lucide-react'; import { formatDistanceToNow } from 'date-fns'; import { fetchSystemActivities, SystemActivity, ActivityType, EntityChangeDetails, AdminActionDetails, SubmissionReviewDetails, ReportResolutionDetails, ReviewModerationDetails, PhotoApprovalDetails, AccountLifecycleDetails, ReviewLifecycleDetails, SubmissionWorkflowDetails } from '@/lib/systemActivityService'; import { getErrorMessage } from '@/lib/errorHandler'; 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', }, account_created: { icon: UserPlus, color: 'text-green-600', bgColor: 'bg-green-600/10', label: 'Account Created', }, account_deletion_requested: { icon: UserX, color: 'text-orange-600', bgColor: 'bg-orange-600/10', label: 'Deletion Requested', }, account_deletion_confirmed: { icon: UserX, color: 'text-red-600', bgColor: 'bg-red-600/10', label: 'Deletion Confirmed', }, account_deletion_cancelled: { icon: UserCheck, color: 'text-blue-600', bgColor: 'bg-blue-600/10', label: 'Deletion Cancelled', }, user_banned: { icon: Ban, color: 'text-red-700', bgColor: 'bg-red-700/10', label: 'User Banned', }, user_unbanned: { icon: UserCheck, color: 'text-green-700', bgColor: 'bg-green-700/10', label: 'User Unbanned', }, review_created: { icon: MessageSquare, color: 'text-blue-600', bgColor: 'bg-blue-600/10', label: 'Review Created', }, review_deleted: { icon: MessageSquareX, color: 'text-red-600', bgColor: 'bg-red-600/10', label: 'Review Deleted', }, submission_created: { icon: Plus, color: 'text-blue-500', bgColor: 'bg-blue-500/10', label: 'Submission Created', }, submission_claimed: { icon: UserCog, color: 'text-indigo-500', bgColor: 'bg-indigo-500/10', label: 'Submission Claimed', }, submission_escalated: { icon: AlertTriangle, color: 'text-orange-600', bgColor: 'bg-orange-600/10', label: 'Submission Escalated', }, submission_reassigned: { icon: History, color: 'text-purple-600', bgColor: 'bg-purple-600/10', label: 'Submission Reassigned', }, }; export const SystemActivityLog = forwardRef( ({ limit = 50, showFilters = true }, ref): React.JSX.Element => { const [activities, setActivities] = useState([]); const [isLoading, setIsLoading] = useState(true); const [isRefreshing, setIsRefreshing] = useState(false); const [filterType, setFilterType] = useState('all'); const [expandedIds, setExpandedIds] = useState>(new Set()); const [searchQuery, setSearchQuery] = useState(''); const [showFiltersPanel, setShowFiltersPanel] = useState(false); const loadActivities = async (showLoader = true): Promise => { if (showLoader) { setIsLoading(true); } else { setIsRefreshing(true); } try { const data = await fetchSystemActivities(limit, { type: filterType === 'all' ? undefined : filterType, }); setActivities(data); } catch (error: unknown) { // Activity load failed - display empty list } finally { setIsLoading(false); setIsRefreshing(false); } }; const handleRefresh = (): void => { void loadActivities(false); }; useEffect(() => { void loadActivities(); // eslint-disable-next-line react-hooks/exhaustive-deps }, [limit, filterType]); useImperativeHandle(ref, () => ({ refresh: loadActivities, })); const toggleExpanded = (id: string): void => { setExpandedIds(prev => { const next = new Set(prev); if (next.has(id)) { next.delete(id); } else { next.add(id); } return next; }); }; const clearFilters = (): void => { setFilterType('all'); setSearchQuery(''); }; const hasActiveFilters = filterType !== 'all' || searchQuery.length > 0; // Filter activities based on search query const filteredActivities = activities.filter(activity => { if (!searchQuery) return true; const query = searchQuery.toLowerCase(); // Search in actor username/display name if (activity.actor?.username?.toLowerCase().includes(query)) return true; if (activity.actor?.display_name?.toLowerCase().includes(query)) return true; // Search in action if (activity.action.toLowerCase().includes(query)) return true; // Search in type if (activity.type.toLowerCase().includes(query)) return true; // Search in details based on activity type const details = activity.details; if ('entity_name' in details && details.entity_name?.toLowerCase().includes(query)) return true; if ('target_username' in details && details.target_username?.toLowerCase().includes(query)) return true; if ('username' in details && details.username?.toLowerCase().includes(query)) return true; if ('submission_type' in details && details.submission_type?.toLowerCase().includes(query)) return true; return false; }); const renderActivityDetails = (activity: SystemActivity): React.JSX.Element => { 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.admin_audit_details && details.admin_audit_details.length > 0 && (
{details.admin_audit_details.map((detail: any) => (
{detail.detail_key}: {detail.detail_value}
))}
)}
); } 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
); } case 'account_created': { const details = activity.details as AccountLifecycleDetails; return (
New Account {details.username && ( @{details.username} )}
); } case 'account_deletion_requested': { const details = activity.details as AccountLifecycleDetails; return (
Deletion Requested {details.scheduled_date && ( Scheduled: {new Date(details.scheduled_date).toLocaleDateString()} )}
{isExpanded && details.request_id && (

Request ID: {details.request_id}

)}
); } case 'account_deletion_confirmed': { const details = activity.details as AccountLifecycleDetails; return (
Deletion Confirmed {details.scheduled_date && ( Will be deleted: {new Date(details.scheduled_date).toLocaleDateString()} )}
Account will be permanently deleted after 7 days
); } case 'account_deletion_cancelled': { const details = activity.details as AccountLifecycleDetails; return (
Deletion Cancelled
{isExpanded && details.reason && (
Reason: {details.reason}
)}
); } case 'user_banned': { const details = activity.details as AccountLifecycleDetails; return (
Banned {details.username && ( @{details.username} )}
{isExpanded && details.reason && (
Reason: {details.reason}
)}
); } case 'user_unbanned': { const details = activity.details as AccountLifecycleDetails; return (
Unbanned {details.username && ( @{details.username} )}
{isExpanded && details.reason && (

Reason: {details.reason}

)}
); } case 'review_created': { const details = activity.details as ReviewLifecycleDetails; return (
New Review {details.rating && (
{details.rating}/5
)} {details.entity_name && ( <> for {details.entity_name} )}
{isExpanded && details.content && (

"{details.content}"

)}
); } case 'review_deleted': { const details = activity.details as ReviewLifecycleDetails; return (
Review Deleted {details.was_moderated && ( Moderated )} {details.rating && (
{details.rating}/5
)} {details.entity_name && ( from {details.entity_name} )}
{isExpanded && details.content && (

"{details.content}"

)} {isExpanded && details.deletion_reason && (
Reason: {details.deletion_reason}
)}
); } case 'submission_created': { const details = activity.details as SubmissionWorkflowDetails; return (
New Submission {details.submission_type.replace(/_/g, ' ')} {details.username && ( by @{details.username} )}
); } case 'submission_claimed': { const details = activity.details as SubmissionWorkflowDetails; return (
Claimed {details.submission_type.replace(/_/g, ' ')} {details.username && ( by @{details.username} )}
{isExpanded && details.assigned_username && (
Assigned to @{details.assigned_username}
)}
); } case 'submission_escalated': { const details = activity.details as SubmissionWorkflowDetails; return (
Escalated {details.submission_type.replace(/_/g, ' ')}
{isExpanded && details.escalation_reason && (
Reason: {details.escalation_reason}
)}
); } case 'submission_reassigned': { const details = activity.details as SubmissionWorkflowDetails; return (
Reassigned {details.submission_type.replace(/_/g, ' ')}
{isExpanded && (details.from_moderator_username || details.to_moderator_username) && (
{details.from_moderator_username && details.to_moderator_username ? ( <> From @{details.from_moderator_username} to @{details.to_moderator_username} ) : details.to_moderator_username ? ( <> To @{details.to_moderator_username} ) : null}
)}
); } default: return Unknown activity type; } }; 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 && ( )}
{showFilters && showFiltersPanel && (

Filter Activities

{hasActiveFilters && ( )}
setSearchQuery(e.target.value)} className="pl-9" />
{hasActiveFilters && (
Active filters: {filterType !== 'all' && ( {activityTypeConfig[filterType as keyof typeof activityTypeConfig]?.label || filterType} )} {searchQuery && ( Search: "{searchQuery}" )}
)}
)}
{filteredActivities.length === 0 ? (
{hasActiveFilters ? (

No activities found

Try adjusting your filters or search query

) : (

No activities yet

System activities will appear here as they occur

)}
) : (
Showing {filteredActivities.length} {filteredActivities.length === 1 ? 'activity' : 'activities'} {hasActiveFilters && ( (filtered from {activities.length}) )}
{filteredActivities.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';