import { useState } from 'react'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { useTranslation } from 'react-i18next'; import { History, CheckCircle, XCircle, Loader2, Trash2, RefreshCw, ChevronDown, ChevronUp } from 'lucide-react'; import { api } from '../api/client'; import { parseUTCDate, formatTimeOnly, formatDateTime, type TimeFormat } from '../utils/date'; import type { NotificationLogEntry } from '../api/client'; import { Button } from './Button'; import { useToast } from '../contexts/ToastContext'; const EVENT_COLORS: Record = { print_start: 'text-blue-400', print_complete: 'text-bambu-green', print_failed: 'text-red-400', print_stopped: 'text-orange-400', print_progress: 'text-yellow-400', printer_offline: 'text-gray-400', printer_error: 'text-rose-400', filament_low: 'text-cyan-400', maintenance_due: 'text-purple-400', test: 'text-bambu-gray', }; interface NotificationLogViewerProps { onClose: () => void; } export function NotificationLogViewer({ onClose }: NotificationLogViewerProps) { const { t } = useTranslation(); const queryClient = useQueryClient(); const { showToast } = useToast(); const [days, setDays] = useState(7); const [expandedId, setExpandedId] = useState(null); const [showFailedOnly, setShowFailedOnly] = useState(false); const { data: settings } = useQuery({ queryKey: ['settings'], queryFn: api.getSettings, }); const timeFormat: TimeFormat = settings?.time_format || 'system'; const { data: logs, isLoading, refetch, isRefetching } = useQuery({ queryKey: ['notification-logs', days, showFailedOnly], queryFn: () => api.getNotificationLogs({ days, limit: 100, success: showFailedOnly ? false : undefined, }), }); const { data: stats } = useQuery({ queryKey: ['notification-log-stats', days], queryFn: () => api.getNotificationLogStats(days), }); const clearMutation = useMutation({ mutationFn: () => api.clearNotificationLogs(30), onSuccess: (data) => { showToast(data.message, 'success'); queryClient.invalidateQueries({ queryKey: ['notification-logs'] }); queryClient.invalidateQueries({ queryKey: ['notification-log-stats'] }); }, onError: (error: Error) => { showToast(`Failed to clear logs: ${error.message}`, 'error'); }, }); const formatDate = (dateStr: string) => { const date = parseUTCDate(dateStr); if (!date) return ''; const now = new Date(); const diff = now.getTime() - date.getTime(); if (diff < 60000) return t('notifications.justNow'); if (diff < 3600000) return `${Math.floor(diff / 60000)}m ago`; if (diff < 86400000) return `${Math.floor(diff / 3600000)}h ago`; return date.toLocaleDateString() + ' ' + formatTimeOnly(date, timeFormat); }; return (
{/* Header */}

{t('notifications.notificationLog')}

{/* Stats Bar */} {stats && (
{t('notifications.statsSummary', { days })} {stats.total} {t('notifications.statsNotifications')} {t('notifications.statsSent', { count: stats.success_count })} {stats.failure_count > 0 && ( {t('notifications.statsFailed', { count: stats.failure_count })} )}
)} {/* Filters */}
{/* Log List */}
{isLoading ? (
) : logs && logs.length > 0 ? (
{logs.map((log) => ( setExpandedId(expandedId === log.id ? null : log.id)} formatDate={formatDate} formatFullDate={(dateStr) => formatDateTime(dateStr, timeFormat)} /> ))}
) : (

{showFailedOnly ? t('notifications.noFailedNotifications') : t('notifications.noNotificationsLogged')}

)}
); } function LogEntry({ log, isExpanded, onToggle, formatDate, formatFullDate, }: { log: NotificationLogEntry; isExpanded: boolean; onToggle: () => void; formatDate: (date: string) => string; formatFullDate: (date: string) => string; }) { const { t } = useTranslation(); return (
{isExpanded && (

{t('notifications.logTitle')}

{log.title}

{t('notifications.logMessage')}

{log.message}

{!log.success && log.error_message && (

{t('notifications.logError')}

{log.error_message}

)}
{t('notifications.logProvider', { type: log.provider_type })} {t('notifications.logTime', { time: formatFullDate(log.created_at) })}
)}
); }