import { useEffect, useState } from 'react'; import { getTrackHistory, TrackHistory as TrackHistoryItem, TrackHistoryError, TrackHistoryAction, } from '../services/trackHistoryService'; import { LoadingSpinner } from '@/components/ui/loading-spinner'; import { Alert, AlertDescription } from '@/components/ui/alert'; import { Button } from '@/components/ui/button'; import { cn } from '@/lib/utils'; import { History, Calendar, Plus, Edit, Trash2, Eye, EyeOff, RotateCcw, ChevronLeft, ChevronRight, } from 'lucide-react'; /** * TrackHistory Component * T0330: Composant pour afficher l'historique des modifications d'un track avec timeline */ interface TrackHistoryProps { trackId: string; className?: string; limit?: number; } export function TrackHistory({ trackId, className, limit = 50, }: TrackHistoryProps) { const [history, setHistory] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [total, setTotal] = useState(0); const [currentOffset, setCurrentOffset] = useState(0); const [currentLimit] = useState(limit); useEffect(() => { loadHistory(); }, [trackId, currentOffset]); const loadHistory = async () => { setLoading(true); setError(null); try { const data = await getTrackHistory(trackId, { limit: currentLimit, offset: currentOffset, }); setHistory(data.history); setTotal(data.total); } catch (err) { if (err instanceof TrackHistoryError) { setError(err.message); } else { setError("Impossible de charger l'historique"); } } finally { setLoading(false); } }; const formatDate = (dateString: string): string => { const date = new Date(dateString); return new Intl.DateTimeFormat('fr-FR', { year: 'numeric', month: 'long', day: 'numeric', hour: '2-digit', minute: '2-digit', }).format(date); }; const getActionIcon = (action: TrackHistoryAction) => { switch (action) { case 'created': return Plus; case 'updated': return Edit; case 'deleted': return Trash2; case 'published': return Eye; case 'unpublished': return EyeOff; case 'restored': return RotateCcw; default: return History; } }; const getActionLabel = (action: TrackHistoryAction): string => { switch (action) { case 'created': return 'Créé'; case 'updated': return 'Modifié'; case 'deleted': return 'Supprimé'; case 'published': return 'Publié'; case 'unpublished': return 'Dépublié'; case 'restored': return 'Restauré'; default: return action; } }; const getActionColor = (action: TrackHistoryAction): string => { switch (action) { case 'created': return 'text-green-600 bg-green-50'; case 'updated': return 'text-blue-600 bg-blue-50'; case 'deleted': return 'text-red-600 bg-red-50'; case 'published': return 'text-purple-600 bg-purple-50'; case 'unpublished': return 'text-orange-600 bg-orange-50'; case 'restored': return 'text-cyan-600 bg-cyan-50'; default: return 'text-gray-600 bg-gray-50'; } }; const parseValue = (value?: string): any => { if (!value) return null; try { return JSON.parse(value); } catch { return value; } }; const formatValue = (value: any): string => { if (value === null || value === undefined) return ''; if (typeof value === 'string') return value; if (typeof value === 'object') { return JSON.stringify(value, null, 2); } return String(value); }; const handlePreviousPage = () => { if (currentOffset > 0) { setCurrentOffset(Math.max(0, currentOffset - currentLimit)); } }; const handleNextPage = () => { if (currentOffset + currentLimit < total) { setCurrentOffset(currentOffset + currentLimit); } }; if (loading) { return (
); } if (error) { return (
{error}
); } const hasPreviousPage = currentOffset > 0; const hasNextPage = currentOffset + currentLimit < total; return (

Historique des modifications

{total > 0 && ( ({total}) )}
{history.length === 0 ? (

Aucune modification enregistrée

) : ( <>
{/* Timeline line */}
{/* Timeline items */}
{history.map((item) => { const Icon = getActionIcon(item.action); const actionColor = getActionColor(item.action); const oldValue = parseValue(item.old_value); const newValue = parseValue(item.new_value); return (
{/* Timeline dot */}
{/* Content */}
{getActionLabel(item.action)} #{item.id}
{formatDate(item.created_at)}
{/* Values comparison */} {(oldValue !== null || newValue !== null) && (
{oldValue !== null && (
Ancienne valeur:
                                {formatValue(oldValue)}
                              
)} {newValue !== null && (
Nouvelle valeur:
                                {formatValue(newValue)}
                              
)}
)}
); })}
{/* Pagination */} {total > currentLimit && (
Affichage {currentOffset + 1} -{' '} {Math.min(currentOffset + currentLimit, total)} sur {total}
)} )}
); }