149 lines
4.9 KiB
TypeScript
149 lines
4.9 KiB
TypeScript
'use client';
|
|
|
|
import { useState, useEffect } from 'react';
|
|
import { Card, CardHeader, CardContent } from '@/components/ui/Card';
|
|
import { Badge } from '@/components/ui/Badge';
|
|
import { Button } from '@/components/ui/Button';
|
|
import { formatDistanceToNow } from 'date-fns';
|
|
import { fr } from 'date-fns/locale';
|
|
|
|
interface SyncLog {
|
|
id: string;
|
|
source: string;
|
|
status: string;
|
|
message: string | null;
|
|
tasksSync: number;
|
|
createdAt: string;
|
|
}
|
|
|
|
interface JiraLogsProps {
|
|
className?: string;
|
|
}
|
|
|
|
export function JiraLogs({ className = "" }: JiraLogsProps) {
|
|
const [logs, setLogs] = useState<SyncLog[]>([]);
|
|
const [isLoading, setIsLoading] = useState(true);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
const fetchLogs = async () => {
|
|
try {
|
|
setIsLoading(true);
|
|
setError(null);
|
|
|
|
const response = await fetch('/api/jira/logs?limit=10');
|
|
if (!response.ok) {
|
|
throw new Error('Erreur lors de la récupération des logs');
|
|
}
|
|
|
|
const { data } = await response.json();
|
|
setLogs(data);
|
|
} catch (err) {
|
|
setError(err instanceof Error ? err.message : 'Erreur inconnue');
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
fetchLogs();
|
|
}, []);
|
|
|
|
const getStatusBadge = (status: string) => {
|
|
switch (status) {
|
|
case 'success':
|
|
return <Badge variant="success" size="sm">✓ Succès</Badge>;
|
|
case 'error':
|
|
return <Badge variant="danger" size="sm">✗ Erreur</Badge>;
|
|
default:
|
|
return <Badge variant="outline" size="sm">{status}</Badge>;
|
|
}
|
|
};
|
|
|
|
return (
|
|
<Card className={className}>
|
|
<CardHeader className="pb-3">
|
|
<div className="flex items-center justify-between">
|
|
<div className="flex items-center gap-3">
|
|
<div className="w-2 h-2 rounded-full bg-gray-400 animate-pulse"></div>
|
|
<h3 className="font-mono text-sm font-bold text-gray-400 uppercase tracking-wider">
|
|
LOGS JIRA
|
|
</h3>
|
|
</div>
|
|
<Button
|
|
onClick={fetchLogs}
|
|
disabled={isLoading}
|
|
variant="secondary"
|
|
size="sm"
|
|
>
|
|
{isLoading ? '⟳' : '🔄'}
|
|
</Button>
|
|
</div>
|
|
</CardHeader>
|
|
|
|
<CardContent className="space-y-3">
|
|
{error && (
|
|
<div className="p-3 bg-[var(--destructive)]/10 border border-[var(--destructive)]/20 rounded text-sm text-[var(--destructive)] break-words overflow-hidden">
|
|
{error}
|
|
</div>
|
|
)}
|
|
|
|
{isLoading ? (
|
|
<div className="space-y-2">
|
|
{[...Array(3)].map((_, i) => (
|
|
<div key={i} className="p-3 bg-[var(--card)] rounded animate-pulse">
|
|
<div className="h-4 bg-[var(--border)] rounded w-3/4 mb-2"></div>
|
|
<div className="h-3 bg-[var(--border)] rounded w-1/2"></div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
) : logs.length === 0 ? (
|
|
<div className="text-center py-8 text-[var(--muted-foreground)] text-sm">
|
|
<div className="text-2xl mb-2">📋</div>
|
|
Aucun log de synchronisation
|
|
</div>
|
|
) : (
|
|
<div className="space-y-2 max-h-96 overflow-y-auto">
|
|
{logs.map((log) => (
|
|
<div
|
|
key={log.id}
|
|
className="p-3 bg-[var(--card)] rounded border border-[var(--border)] hover:border-[var(--border)]/70 transition-colors"
|
|
>
|
|
<div className="flex items-center justify-between mb-2">
|
|
{getStatusBadge(log.status)}
|
|
<span className="text-xs text-[var(--muted-foreground)]">
|
|
{formatDistanceToNow(new Date(log.createdAt), {
|
|
addSuffix: true,
|
|
locale: fr
|
|
})}
|
|
</span>
|
|
</div>
|
|
|
|
<div className="flex items-center justify-between text-sm">
|
|
<span className="text-[var(--foreground)] truncate flex-1 mr-2">
|
|
{log.tasksSync > 0 ? (
|
|
`${log.tasksSync} tâche${log.tasksSync > 1 ? 's' : ''} synchronisée${log.tasksSync > 1 ? 's' : ''}`
|
|
) : (
|
|
'Aucune tâche synchronisée'
|
|
)}
|
|
</span>
|
|
</div>
|
|
|
|
{log.message && (
|
|
<div className="mt-2 text-xs text-[var(--muted-foreground)] bg-[var(--background)] p-2 rounded font-mono max-h-20 overflow-y-auto break-words">
|
|
{log.message}
|
|
</div>
|
|
)}
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
|
|
{/* Info */}
|
|
<div className="text-xs text-[var(--muted-foreground)] p-2 border border-dashed border-[var(--border)] rounded">
|
|
Les logs sont conservés pour traçabilité des synchronisations
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
}
|