/** * Sync History Table Component * Displays paginated sync history from database */ 'use client'; import { useEffect, useState } from 'react'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { format } from 'date-fns'; import { getEntityDisplayName } from '@/lib/utils/sync-helpers'; import { EntityType } from '@/lib/types/sync'; import { ChevronLeft, ChevronRight, Download } from 'lucide-react'; interface SyncHistoryRecord { id: number; entity_type: string; sync_type: string; status: string; started_at: string; completed_at?: string; records_added: number; records_updated: number; records_deleted: number; error_message?: string; triggered_by?: string; } interface SyncHistoryTableProps { refreshKey: number; } export default function SyncHistoryTable({ refreshKey }: SyncHistoryTableProps) { const [history, setHistory] = useState([]); const [loading, setLoading] = useState(true); const [page, setPage] = useState(1); const limit = 10; useEffect(() => { fetchHistory(); }, [refreshKey, page]); const fetchHistory = async () => { try { setLoading(true); const response = await fetch(`/api/sync/history?limit=${limit}`); if (response.ok) { const data = await response.json(); setHistory(data.history || []); } } catch (error) { console.error('Failed to fetch sync history:', error); } finally { setLoading(false); } }; const getStatusBadge = (status: string) => { const variants: Record = { completed: 'default', started: 'secondary', in_progress: 'secondary', failed: 'destructive', }; return ( {status} ); }; const formatDuration = (started: string, completed?: string) => { if (!completed) return '-'; const start = new Date(started); const end = new Date(completed); const duration = end.getTime() - start.getTime(); const seconds = Math.floor(duration / 1000); const minutes = Math.floor(seconds / 60); if (minutes > 0) { return `${minutes}m ${seconds % 60}s`; } return `${seconds}s`; }; const downloadJSON = () => { const dataStr = JSON.stringify(history, null, 2); const dataBlob = new Blob([dataStr], { type: 'application/json' }); const url = URL.createObjectURL(dataBlob); const link = document.createElement('a'); link.href = url; link.download = `sync-history-${format(new Date(), 'yyyy-MM-dd-HHmmss')}.json`; document.body.appendChild(link); link.click(); document.body.removeChild(link); URL.revokeObjectURL(url); }; const downloadCSV = () => { // CSV headers const headers = [ 'ID', 'Entity Type', 'Sync Type', 'Status', 'Started At', 'Completed At', 'Duration (seconds)', 'Records Added', 'Records Updated', 'Records Deleted', 'Triggered By', 'Error Message' ]; // Convert history to CSV rows const rows = history.map(record => { const duration = record.completed_at ? Math.floor((new Date(record.completed_at).getTime() - new Date(record.started_at).getTime()) / 1000) : ''; return [ record.id, getEntityDisplayName(record.entity_type as EntityType), record.sync_type, record.status, format(new Date(record.started_at), 'yyyy-MM-dd HH:mm:ss'), record.completed_at ? format(new Date(record.completed_at), 'yyyy-MM-dd HH:mm:ss') : '', duration, record.records_added, record.records_updated, record.records_deleted, record.triggered_by || 'system', record.error_message ? `"${record.error_message.replace(/"/g, '""')}"` : '' ]; }); // Combine headers and rows const csvContent = [ headers.join(','), ...rows.map(row => row.join(',')) ].join('\n'); // Create and download file const dataBlob = new Blob([csvContent], { type: 'text/csv' }); const url = URL.createObjectURL(dataBlob); const link = document.createElement('a'); link.href = url; link.download = `sync-history-${format(new Date(), 'yyyy-MM-dd-HHmmss')}.csv`; document.body.appendChild(link); link.click(); document.body.removeChild(link); URL.revokeObjectURL(url); }; if (loading && history.length === 0) { return ( Sync History

Loading...

); } return (
Sync History Recent sync operations and their results
{history.length > 0 && (
)}
{history.length === 0 ? (

No sync history available

) : ( <>
Entity Type Status Started Duration Added Updated Deleted Triggered By {history.map((record) => ( {getEntityDisplayName(record.entity_type as EntityType)} {record.sync_type.replace('-', ' ')} {getStatusBadge(record.status)} {format(new Date(record.started_at), 'MMM d, HH:mm:ss')} {formatDuration(record.started_at, record.completed_at)} +{record.records_added} ~{record.records_updated} -{record.records_deleted} {record.triggered_by || 'system'} ))}
{/* Pagination */}

Showing {history.length} records

)}
); }