/** * useSLAPrediction (#43) — SLA 위반 예측 훅 * * GET /api/sla/prediction 으로 SLA 위반 위험이 있는 SR/서비스를 예측 조회. * 폴링(기본 60초)으로 주기 갱신. 위험도(risk) 기준으로 정렬·요약 제공. */ import { useEffect, useState, useCallback, useRef } from 'react' import { getSLAPrediction } from '../services/api' export interface SLAPredictItem { id: string | number target_type: 'sr' | 'service' target_name: string sla_deadline: string // ISO predicted_breach_at: string // ISO risk: 'high' | 'medium' | 'low' confidence: number // 0~1 remaining_minutes: number } interface SLAPredictResult { items: SLAPredictItem[] highRiskCount: number loading: boolean error: boolean refresh: () => void } const RISK_ORDER: Record = { high: 0, medium: 1, low: 2 } export function useSLAPrediction(pollMs = 60000): SLAPredictResult { const [items, setItems] = useState([]) const [loading, setLoading] = useState(true) const [error, setError] = useState(false) const timer = useRef | null>(null) const load = useCallback(async () => { try { const res = await getSLAPrediction() const raw: any[] = res.data?.items ?? res.data?.predictions ?? res.data ?? [] const mapped: SLAPredictItem[] = raw.map((r: any) => ({ id: r.id ?? r.sr_id ?? r.target_id, target_type: r.target_type ?? (r.sr_id ? 'sr' : 'service'), target_name: r.target_name ?? r.title ?? r.service_name ?? `#${r.id ?? ''}`, sla_deadline: r.sla_deadline ?? r.deadline ?? '', predicted_breach_at: r.predicted_breach_at ?? r.eta ?? '', risk: (r.risk ?? r.risk_level ?? 'low').toLowerCase(), confidence: typeof r.confidence === 'number' ? r.confidence : 0, remaining_minutes: r.remaining_minutes ?? r.remaining_min ?? 0, })) mapped.sort((a, b) => (RISK_ORDER[a.risk] ?? 9) - (RISK_ORDER[b.risk] ?? 9) || a.remaining_minutes - b.remaining_minutes ) setItems(mapped) setError(false) } catch { setError(true) } finally { setLoading(false) } }, []) useEffect(() => { load() timer.current = setInterval(load, pollMs) return () => { if (timer.current) clearInterval(timer.current) } }, [load, pollMs]) const highRiskCount = items.filter(i => i.risk === 'high').length return { items, highRiskCount, loading, error, refresh: load } } export default useSLAPrediction