'use client' /** * RecentActivity — 最近活動時間線 * Sprint 5R S5: 設計稿 L416-429 * @created 2026-04-09 Claude Opus 4.6 Asia/Taipei */ import { useState, useEffect } from 'react' import { useTranslations } from 'next-intl' const API_BASE = process.env.NEXT_PUBLIC_API_URL ?? '' interface LogEntry { id: string event_type: string action_detail: string | null actor: string | null created_at: string } const EVENT_COLOR: Record = { RESOLVED: '#22C55E', EXECUTION_COMPLETED: '#22C55E', ALERT_RECEIVED: '#cc2200', AUTO_REPAIR_TRIGGERED: '#4A90D9', TELEGRAM_SENT: '#4A90D9', EXECUTION_STARTED: '#F59E0B', } export function RecentActivity() { const t = useTranslations('dashboard') const [logs, setLogs] = useState([]) useEffect(() => { fetch(`${API_BASE}/api/v1/alert-operation-logs?limit=5`) .then(r => r.ok ? r.json() : { items: [] }) .then(d => setLogs(d.items ?? [])) .catch(() => {}) }, []) if (logs.length === 0) return null return (
{t('activityStream')} {t('viewAllAlerts')} →
{logs.map((log, i) => { const time = (() => { try { return new Date(log.created_at).toLocaleTimeString('zh-TW', { timeZone: 'Asia/Taipei', hour: '2-digit', minute: '2-digit' }) } catch { return '--' } })() const dotColor = EVENT_COLOR[log.event_type] ?? '#87867f' const detail = log.action_detail || log.event_type.replace(/_/g, ' ').toLowerCase() return (
{time} {log.actor && {log.actor}} {log.actor && ' · '} {detail}
) })}
) }