import React, { useState } from 'react'; import { FaHistory, FaSync, FaChevronDown, FaChevronUp, FaUser, FaClock, FaCheckCircle, FaSpinner, } from 'react-icons/fa'; import { useAuditLogs } from '../hooks/useAuditLogs'; import type { AuditLogEntry, VersionChange } from '../hooks/useAuditLogs'; // ============== PROPS ============== interface ActivityLogProps { /** Frappe DocType name (e.g. 'Asset', 'Inspection', 'Work_Order') */ doctype: string; /** Document name / ID */ docname: string | null; /** Document creation date (for "Created" entry at bottom) */ creationDate?: string; /** Document owner/creator email */ createdBy?: string; /** Title shown in header */ title?: string; /** Max entries to fetch */ limit?: number; /** Number of entries visible before "Show All" */ initialVisible?: number; /** Allow collapse/expand */ collapsible?: boolean; /** Start collapsed */ startCollapsed?: boolean; /** Compact mode for sidebar placement */ compact?: boolean; /** Additional CSS class */ className?: string; /** Callback after refresh */ onRefresh?: () => void; } // ============== HELPER FUNCTIONS ============== const formatFieldName = (fieldName: string): string => { if (!fieldName) return ''; return fieldName .replace(/^custom_/, '') .replace(/_/g, ' ') .replace(/\b\w/g, (char) => char.toUpperCase()); }; const formatValue = (value: any): string => { if (value === null || value === undefined) return '(empty)'; if (value === '') return '(empty)'; if (value === 0) return '0'; if (value === 1) return '1'; if (typeof value === 'boolean') return value ? 'Yes' : 'No'; if (typeof value === 'object') return JSON.stringify(value); return String(value); }; const formatAuditDate = (dateStr: string): string => { if (!dateStr) return ''; const date = new Date(dateStr); const now = new Date(); const diffMs = now.getTime() - date.getTime(); const diffMins = Math.floor(diffMs / 60000); const diffHours = Math.floor(diffMs / 3600000); const diffDays = Math.floor(diffMs / 86400000); if (diffMins < 1) return 'Just now'; if (diffMins < 60) return `${diffMins} min${diffMins > 1 ? 's' : ''} ago`; if (diffHours < 24) return `${diffHours} hour${diffHours > 1 ? 's' : ''} ago`; if (diffDays < 7) return `${diffDays} day${diffDays > 1 ? 's' : ''} ago`; return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: date.getFullYear() !== now.getFullYear() ? 'numeric' : undefined, hour: '2-digit', minute: '2-digit', }); }; const formatUsername = (email: string): string => { if (!email) return 'Unknown'; const atIndex = email.indexOf('@'); if (atIndex === -1) return email; return email.substring(0, atIndex); }; const getChangeColor = (fieldName: string): string => { const lower = fieldName.toLowerCase(); if (lower.includes('status') || lower.includes('state') || lower.includes('workflow')) { return 'text-purple-600 dark:text-purple-400'; } if (lower.includes('date')) { return 'text-blue-600 dark:text-blue-400'; } if ( lower.includes('technician') || lower.includes('supervisor') || lower.includes('assigned') || lower.includes('location') || lower.includes('department') || lower.includes('building') || lower.includes('room') ) { return 'text-green-600 dark:text-green-400'; } return 'text-gray-600 dark:text-gray-400'; }; // ============== SUB-COMPONENTS ============== /** Single timeline entry */ const TimelineEntry: React.FC<{ log: AuditLogEntry; isLatest: boolean; compact: boolean; }> = ({ log, isLatest, compact }) => { const dotSize = compact ? 'w-2.5 h-2.5' : 'w-3 h-3'; const avatarSize = compact ? 'w-5 h-5' : 'w-6 h-6'; const iconSize = compact ? 8 : 10; const textSize = compact ? 'text-[10px]' : 'text-xs'; const valueSize = compact ? 'text-[9px]' : 'text-[10px]'; return (
Document updated
)} {log.added && log.added.length > 0 && (No changes recorded yet