import { useCallback, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Badge } from '@shared/components/ui/badge'; import { PageContainer, PageHeader } from '@shared/components/layout'; import { DataTable, type DataColumn } from '@shared/components/common/DataTable'; import { Send, Loader2, AlertTriangle } from 'lucide-react'; import { getAlerts, type PredictionAlert } from '@/services/event'; import { formatDateTime } from '@shared/utils/dateFormat'; /* SFR-17: 현장 함정 즉각 대응 AI 알림 메시지 발송 기능 */ interface AlertRow { id: number; eventId: number; time: string; channel: string; recipient: string; confidence: string; status: string; [key: string]: unknown; } const STATUS_LABEL: Record = { SENT: '발송완료', DELIVERED: '수신확인', FAILED: '발송실패', }; const cols: DataColumn[] = [ { key: 'id', label: 'ID', width: '70px', render: (v) => {v as number}, }, { key: 'eventId', label: '이벤트', width: '80px', render: (v) => EVT-{v as number}, }, { key: 'time', label: '발송 시각', width: '130px', sortable: true, render: (v) => {v as string}, }, { key: 'channel', label: '채널', width: '80px', sortable: true, render: (v) => ( {v as string} ), }, { key: 'recipient', label: '수신 대상', render: (v) => {v as string}, }, { key: 'confidence', label: '신뢰도', width: '70px', align: 'center', sortable: true, render: (v) => { const s = v as string; if (!s) return -; const n = parseFloat(s); const color = n > 0.9 ? 'text-red-600 dark:text-red-400' : n > 0.8 ? 'text-orange-600 dark:text-orange-400' : 'text-yellow-600 dark:text-yellow-400'; return {(n * 100).toFixed(0)}%; }, }, { key: 'status', label: '상태', width: '80px', align: 'center', sortable: true, render: (v) => { const s = v as string; const intent: 'success' | 'info' | 'critical' = s === 'DELIVERED' ? 'success' : s === 'SENT' ? 'info' : 'critical'; return ( {STATUS_LABEL[s] ?? s} ); }, }, ]; const PAGE_SIZE = 10; export function AIAlert() { const { t } = useTranslation('fieldOps'); const [alerts, setAlerts] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [totalElements, setTotalElements] = useState(0); const fetchAlerts = useCallback(async () => { setLoading(true); setError(null); try { const res = await getAlerts({ page: 0, size: 100 }); setAlerts(res.content); setTotalElements(res.totalElements); } catch (err) { setError(err instanceof Error ? err.message : String(err)); } finally { setLoading(false); } }, []); useEffect(() => { fetchAlerts(); }, [fetchAlerts]); const data: AlertRow[] = useMemo( () => alerts.map((a) => ({ id: a.id, eventId: a.eventId, time: formatDateTime(a.sentAt), channel: a.channel ?? '-', recipient: a.recipient ?? '-', confidence: a.aiConfidence != null ? String(a.aiConfidence) : '', status: a.deliveryStatus, })), [alerts], ); const deliveredCount = alerts.filter((a) => a.deliveryStatus === 'DELIVERED').length; const failedCount = alerts.filter((a) => a.deliveryStatus === 'FAILED').length; if (loading) { return (
알림 데이터 로딩 중...
); } if (error) { return (
알림 조회 실패: {error}
); } return (
{[ { l: '총 발송', v: totalElements, c: 'text-heading' }, { l: '수신확인', v: deliveredCount, c: 'text-green-600 dark:text-green-400' }, { l: '실패', v: failedCount, c: 'text-red-600 dark:text-red-400' }, ].map((k) => (
{k.v} {k.l}
))}
); }