"use client"; /** * 날씨 위젯 컴포넌트 * - 실시간 날씨 정보를 표시 */ import React, { useEffect, useState } from "react"; import { getWeather, WeatherData } from "@/lib/api/openApi"; import { Cloud, CloudRain, Sun, CloudSnow, Wind, Droplets, Gauge, RefreshCw, Check, ChevronsUpDown, Settings, } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@/components/ui/command"; import { cn } from "@/lib/utils"; import { DashboardElement } from "@/components/admin/dashboard/types"; interface WeatherWidgetProps { element?: DashboardElement; city?: string; refreshInterval?: number; // 새로고침 간격 (ms), 기본값: 600000 (10분) } export default function WeatherWidget({ element, city = "서울", refreshInterval = 600000 }: WeatherWidgetProps) { const [open, setOpen] = useState(false); const [settingsOpen, setSettingsOpen] = useState(false); const [selectedCity, setSelectedCity] = useState(city); const [weather, setWeather] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [lastUpdated, setLastUpdated] = useState(null); // 표시할 날씨 정보 선택 const [selectedItems, setSelectedItems] = useState([ "temperature", "feelsLike", "humidity", "windSpeed", "pressure", ]); // 날씨 항목 정의 const weatherItems = [ { id: "temperature", label: "기온", icon: Sun }, { id: "feelsLike", label: "체감온도", icon: Sun }, { id: "humidity", label: "습도", icon: Droplets }, { id: "windSpeed", label: "풍속", icon: Wind }, { id: "pressure", label: "기압", icon: Gauge }, ]; // 항목 토글 const toggleItem = (itemId: string) => { setSelectedItems((prev) => (prev.includes(itemId) ? prev.filter((id) => id !== itemId) : [...prev, itemId])); }; // 도시 목록 (전국 시/군/구 단위) const cities = [ // 서울특별시 (25개 구) { value: "서울", label: "서울" }, { value: "종로구", label: "서울 종로구" }, { value: "중구", label: "서울 중구" }, { value: "용산구", label: "서울 용산구" }, { value: "성동구", label: "서울 성동구" }, { value: "광진구", label: "서울 광진구" }, { value: "동대문구", label: "서울 동대문구" }, { value: "중랑구", label: "서울 중랑구" }, { value: "성북구", label: "서울 성북구" }, { value: "강북구", label: "서울 강북구" }, { value: "도봉구", label: "서울 도봉구" }, { value: "노원구", label: "서울 노원구" }, { value: "은평구", label: "서울 은평구" }, { value: "서대문구", label: "서울 서대문구" }, { value: "마포구", label: "서울 마포구" }, { value: "양천구", label: "서울 양천구" }, { value: "강서구", label: "서울 강서구" }, { value: "구로구", label: "서울 구로구" }, { value: "금천구", label: "서울 금천구" }, { value: "영등포구", label: "서울 영등포구" }, { value: "동작구", label: "서울 동작구" }, { value: "관악구", label: "서울 관악구" }, { value: "서초구", label: "서울 서초구" }, { value: "강남구", label: "서울 강남구" }, { value: "송파구", label: "서울 송파구" }, { value: "강동구", label: "서울 강동구" }, // 부산광역시 { value: "부산", label: "부산" }, { value: "해운대구", label: "부산 해운대구" }, { value: "부산진구", label: "부산 부산진구" }, { value: "동래구", label: "부산 동래구" }, { value: "사하구", label: "부산 사하구" }, { value: "금정구", label: "부산 금정구" }, { value: "사상구", label: "부산 사상구" }, // 인천광역시 { value: "인천", label: "인천" }, { value: "부평구", label: "인천 부평구" }, { value: "계양구", label: "인천 계양구" }, { value: "남동구", label: "인천 남동구" }, // 대구광역시 { value: "대구", label: "대구" }, { value: "수성구", label: "대구 수성구" }, { value: "달서구", label: "대구 달서구" }, // 광주광역시 { value: "광주", label: "광주" }, { value: "광산구", label: "광주 광산구" }, // 대전광역시 { value: "대전", label: "대전" }, { value: "유성구", label: "대전 유성구" }, // 울산광역시 { value: "울산", label: "울산" }, // 세종특별자치시 { value: "세종", label: "세종" }, // 경기도 (주요 도시) { value: "수원", label: "수원" }, { value: "성남", label: "성남" }, { value: "고양", label: "고양" }, { value: "용인", label: "용인" }, { value: "부천", label: "부천" }, { value: "안산", label: "안산" }, { value: "안양", label: "안양" }, { value: "남양주", label: "남양주" }, { value: "화성", label: "화성" }, { value: "평택", label: "평택" }, { value: "의정부", label: "의정부" }, { value: "시흥", label: "시흥" }, { value: "파주", label: "파주" }, { value: "김포", label: "김포" }, { value: "광명", label: "광명" }, // 강원도 { value: "춘천", label: "춘천" }, { value: "원주", label: "원주" }, { value: "강릉", label: "강릉" }, { value: "속초", label: "속초" }, { value: "동해", label: "동해" }, { value: "태백", label: "태백" }, { value: "삼척", label: "삼척" }, // 충청북도 { value: "청주", label: "청주" }, { value: "충주", label: "충주" }, { value: "제천", label: "제천" }, // 충청남도 { value: "천안", label: "천안" }, { value: "공주", label: "공주" }, { value: "보령", label: "보령" }, { value: "아산", label: "아산" }, { value: "서산", label: "서산" }, { value: "논산", label: "논산" }, { value: "당진", label: "당진" }, // 전라북도 { value: "전주", label: "전주" }, { value: "군산", label: "군산" }, { value: "익산", label: "익산" }, { value: "정읍", label: "정읍" }, { value: "남원", label: "남원" }, { value: "김제", label: "김제" }, // 전라남도 { value: "목포", label: "목포" }, { value: "여수", label: "여수" }, { value: "순천", label: "순천" }, { value: "나주", label: "나주" }, { value: "광양", label: "광양" }, // 경상북도 { value: "포항", label: "포항" }, { value: "경주", label: "경주" }, { value: "김천", label: "김천" }, { value: "안동", label: "안동" }, { value: "구미", label: "구미" }, { value: "영주", label: "영주" }, { value: "영천", label: "영천" }, { value: "상주", label: "상주" }, { value: "문경", label: "문경" }, { value: "경산", label: "경산" }, { value: "울릉도", label: "울릉도" }, // 경상남도 { value: "창원", label: "창원" }, { value: "진주", label: "진주" }, { value: "통영", label: "통영" }, { value: "사천", label: "사천" }, { value: "김해", label: "김해" }, { value: "밀양", label: "밀양" }, { value: "거제", label: "거제" }, { value: "양산", label: "양산" }, // 제주특별자치도 { value: "제주", label: "제주" }, { value: "서귀포", label: "서귀포" }, ]; // 날씨 정보 가져오기 const fetchWeather = async () => { try { setLoading(true); setError(null); const data = await getWeather(selectedCity, "metric", "kr"); setWeather(data); setLastUpdated(new Date()); } catch (err: any) { console.error("날씨 조회 실패:", err); // 에러 메시지 추출 let errorMessage = "날씨 정보를 가져오는 중 오류가 발생했습니다."; if (err.response?.status === 503) { errorMessage = "API 키가 설정되지 않았습니다. 관리자에게 문의하세요."; } else if (err.response?.status === 401) { errorMessage = "API 키가 유효하지 않습니다."; } else if (err.response?.status === 404) { errorMessage = `도시를 찾을 수 없습니다: ${city}`; } else if (err.response?.data?.message) { errorMessage = err.response.data.message; } setError(errorMessage); } finally { setLoading(false); } }; // 초기 로딩 및 자동 새로고침 useEffect(() => { fetchWeather(); const interval = setInterval(fetchWeather, refreshInterval); return () => clearInterval(interval); }, [selectedCity, refreshInterval]); // 도시 변경 핸들러 const handleCityChange = (newCity: string) => { setSelectedCity(newCity); }; // 날씨 아이콘 선택 const getWeatherIcon = (weatherMain: string) => { switch (weatherMain.toLowerCase()) { case "clear": return ; case "clouds": return ; case "rain": case "drizzle": return ; case "snow": return ; default: return ; } }; // 로딩 상태 if (loading && !weather) { return (

실제 기상청 API 연결 중...

실시간 관측 데이터를 가져오고 있습니다

); } // 에러 상태 if (error || !weather) { const isTestMode = error?.includes("API 키가 설정되지 않았습니다"); return (

{isTestMode ? "⚠️ 테스트 모드" : "❌ 연결 실패"}

{error || "날씨 정보를 불러올 수 없습니다."}

{isTestMode &&

임시 데이터가 표시됩니다

}
); } return (
{/* 헤더 */}

🌤️ {element?.customTitle || "날씨"}

도시를 찾을 수 없습니다. {cities.map((city) => ( { handleCityChange(currentValue === selectedCity ? selectedCity : currentValue); setOpen(false); }} > {city.label} ))}

{lastUpdated ? `업데이트: ${lastUpdated.toLocaleTimeString("ko-KR", { hour: "2-digit", minute: "2-digit", })}` : ""}

표시 항목

{weatherItems.map((item) => { const Icon = item.icon; return ( ); })}
{/* 반응형 그리드 레이아웃 - 자동 조정 */}
{/* 날씨 아이콘 및 온도 */}
{(() => { const iconClass = "h-5 w-5"; switch (weather.weatherMain.toLowerCase()) { case "clear": return ; case "clouds": return ; case "rain": case "drizzle": return ; case "snow": return ; default: return ; } })()}
{weather.temperature}°C

{weather.weatherDescription}

{/* 기온 - 선택 가능 */} {selectedItems.includes("temperature") && (

기온

{weather.temperature}°C

)} {/* 체감 온도 */} {selectedItems.includes("feelsLike") && (

체감온도

{weather.feelsLike}°C

)} {/* 습도 */} {selectedItems.includes("humidity") && (

습도

{weather.humidity}%

)} {/* 풍속 */} {selectedItems.includes("windSpeed") && (

풍속

{weather.windSpeed} m/s

)} {/* 기압 */} {selectedItems.includes("pressure") && (

기압

{weather.pressure} hPa

)}
); }