"use client"; import React, { useState, useEffect } from "react"; import { Card } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { RefreshCw, AlertTriangle, Cloud, Construction } from "lucide-react"; import { apiClient } from "@/lib/api/client"; // 알림 타입 type AlertType = "accident" | "weather" | "construction"; // 알림 인터페이스 interface Alert { id: string; type: AlertType; severity: "high" | "medium" | "low"; title: string; location: string; description: string; timestamp: string; } export default function RiskAlertWidget() { const [alerts, setAlerts] = useState([]); const [isRefreshing, setIsRefreshing] = useState(false); const [filter, setFilter] = useState("all"); const [lastUpdated, setLastUpdated] = useState(null); const [newAlertIds, setNewAlertIds] = useState>(new Set()); // 데이터 로드 (백엔드 통합 호출) const loadData = async () => { setIsRefreshing(true); try { // 백엔드 API 호출 (교통사고, 기상특보, 도로공사 통합) const response = await apiClient.get<{ success: boolean; data: Alert[]; count: number; lastUpdated?: string; cached?: boolean; }>("/risk-alerts"); if (response.data.success && response.data.data) { const newData = response.data.data; // 새로운 알림 감지 const oldIds = new Set(alerts.map(a => a.id)); const newIds = new Set(); newData.forEach(alert => { if (!oldIds.has(alert.id)) { newIds.add(alert.id); } }); setAlerts(newData); setNewAlertIds(newIds); setLastUpdated(new Date()); // 3초 후 새 알림 애니메이션 제거 if (newIds.size > 0) { setTimeout(() => setNewAlertIds(new Set()), 3000); } } else { console.error("❌ 리스크 알림 데이터 로드 실패"); setAlerts([]); } } catch (error: any) { console.error("❌ 리스크 알림 API 오류:", error.message); // API 오류 시 빈 배열 유지 setAlerts([]); } finally { setIsRefreshing(false); } }; useEffect(() => { loadData(); // 1분마다 자동 새로고침 (60000ms) const interval = setInterval(loadData, 60000); return () => clearInterval(interval); }, []); // 필터링된 알림 const filteredAlerts = filter === "all" ? alerts : alerts.filter((alert) => alert.type === filter); // 심각도별 색상 const getSeverityColor = (severity: string) => { switch (severity) { case "high": return "border-red-500"; case "medium": return "border-yellow-500"; case "low": return "border-blue-500"; default: return "border-gray-500"; } }; // 심각도별 배지 색상 const getSeverityBadge = (severity: string) => { switch (severity) { case "high": return "bg-red-100 text-red-700"; case "medium": return "bg-yellow-100 text-yellow-700"; case "low": return "bg-blue-100 text-blue-700"; default: return "bg-gray-100 text-gray-700"; } }; // 알림 타입별 아이콘 const getAlertIcon = (type: AlertType) => { switch (type) { case "accident": return ; case "weather": return ; case "construction": return ; } }; // 알림 타입별 한글명 const getAlertTypeName = (type: AlertType) => { switch (type) { case "accident": return "교통사고"; case "weather": return "날씨특보"; case "construction": return "도로공사"; } }; // 시간 포맷 const formatTime = (isoString: string) => { const date = new Date(isoString); const now = new Date(); const diffMinutes = Math.floor((now.getTime() - date.getTime()) / 60000); if (diffMinutes < 1) return "방금 전"; if (diffMinutes < 60) return `${diffMinutes}분 전`; const diffHours = Math.floor(diffMinutes / 60); if (diffHours < 24) return `${diffHours}시간 전`; return `${Math.floor(diffHours / 24)}일 전`; }; // 통계 계산 const stats = { accident: alerts.filter((a) => a.type === "accident").length, weather: alerts.filter((a) => a.type === "weather").length, construction: alerts.filter((a) => a.type === "construction").length, high: alerts.filter((a) => a.severity === "high").length, }; return (
{/* 헤더 */}

리스크 / 알림

{stats.high > 0 && ( 긴급 {stats.high}건 )}
{lastUpdated && newAlertIds.size > 0 && ( 새 알림 {newAlertIds.size}건 )} {lastUpdated && ( {lastUpdated.toLocaleTimeString('ko-KR', { hour: '2-digit', minute: '2-digit' })} )}
{/* 통계 카드 */}
setFilter(filter === "accident" ? "all" : "accident")} >
교통사고
{stats.accident}건
setFilter(filter === "weather" ? "all" : "weather")} >
날씨특보
{stats.weather}건
setFilter(filter === "construction" ? "all" : "construction")} >
도로공사
{stats.construction}건
{/* 필터 상태 표시 */} {filter !== "all" && (
{getAlertTypeName(filter)} 필터 적용 중
)} {/* 알림 목록 */}
{filteredAlerts.length === 0 ? (
알림이 없습니다
) : ( filteredAlerts.map((alert) => (
{getAlertIcon(alert.type)}

{alert.title}

{newAlertIds.has(alert.id) && ( NEW )} {alert.severity === "high" ? "긴급" : alert.severity === "medium" ? "주의" : "정보"}

{alert.location}

{alert.description}

{formatTime(alert.timestamp)}
)) )}
{/* 안내 메시지 */}
💡 1분마다 자동으로 업데이트됩니다
); }