import { api } from '@common/services/api'; // ============================================================ // 백엔드 API 응답 타입 // ============================================================ export interface IncidentListItem { acdntSn: number; acdntCd: string; acdntNm: string; acdntTpCd: string; acdntSttsCd: string; lat: number; lng: number; locDc: string; occrnDtm: string; regionNm: string; officeNm: string; svrtCd: string | null; vesselTp: string | null; phaseCd: string; analystNm: string | null; oilTpCd: string | null; spilQty: number | null; spilUnitCd: string | null; fcstHr: number | null; hasPredCompleted: boolean; mediaCnt: number; hasImgAnalysis: boolean; } export interface PredExecItem { predExecSn: number; algoCd: string; execSttsCd: string; bgngDtm: string | null; cmplDtm: string | null; reqdSec: number | null; } export interface WeatherInfo { locNm: string; obsDtm: string; icon: string; temp: string; weatherDc: string; wind: string; wave: string; humid: string; vis: string; sst: string; tide: string; highTide: string; lowTide: string; forecast: Array<{ hour: string; icon: string; temp: string }>; impactDc: string; } export interface MediaInfo { photoCnt: number; videoCnt: number; satCnt: number; cctvCnt: number; photoMeta: Record | null; droneMeta: Record | null; satMeta: Record | null; cctvMeta: Record | null; } export interface IncidentDetail extends IncidentListItem { predictions: PredExecItem[]; weather: WeatherInfo | null; media: MediaInfo | null; } // ============================================================ // 프론트 호환 타입 // ============================================================ export interface IncidentCompat { id: string; name: string; status: 'active' | 'investigating' | 'closed'; date: string; time: string; region: string; office: string; location: { lat: number; lon: number }; causeType?: string; oilType?: string; prediction?: string; vesselName?: string; mediaCount?: number; hasImgAnalysis?: boolean; } function toCompat(item: IncidentListItem): IncidentCompat { const dt = new Date(item.occrnDtm); const statusMap: Record = { ACTIVE: 'active', INVESTIGATING: 'investigating', CLOSED: 'closed', }; return { id: String(item.acdntSn), name: item.acdntNm, status: statusMap[item.acdntSttsCd] ?? 'active', date: dt.toISOString().slice(0, 10), time: dt.toTimeString().slice(0, 5), region: item.regionNm, office: item.officeNm, location: { lat: item.lat, lon: item.lng }, causeType: item.acdntTpCd, oilType: item.oilTpCd ?? undefined, prediction: item.hasPredCompleted ? '예측완료' : undefined, mediaCount: item.mediaCnt, hasImgAnalysis: item.hasImgAnalysis || undefined, }; } // ============================================================ // API 호출 함수 // ============================================================ export async function fetchIncidentsRaw(): Promise { const { data } = await api.get('/incidents'); return data; } export async function fetchIncidents(filters?: { status?: string; region?: string; search?: string; startDate?: string; endDate?: string; }): Promise { const params = new URLSearchParams(); if (filters?.status) params.set('status', filters.status); if (filters?.region) params.set('region', filters.region); if (filters?.search) params.set('search', filters.search); if (filters?.startDate) params.set('startDate', filters.startDate); if (filters?.endDate) params.set('endDate', filters.endDate); const query = params.toString(); const url = query ? `/incidents?${query}` : '/incidents'; const { data } = await api.get(url); return data.map(toCompat); } export async function fetchIncidentDetail(sn: number): Promise { const { data } = await api.get(`/incidents/${sn}`); return data; } export async function fetchIncidentWeather(sn: number): Promise { try { const { data } = await api.get(`/incidents/${sn}/weather`); return data; } catch { return null; } } export async function fetchIncidentMedia(sn: number): Promise { try { const { data } = await api.get(`/incidents/${sn}/media`); return data; } catch { return null; } } export async function fetchIncidentPredictions(sn: number): Promise { const { data } = await api.get(`/incidents/${sn}/predictions`); return data; } export interface NearbyOrgItem { orgSn: number; orgTp: string; jrsdNm: string; areaNm: string; orgNm: string; addr: string; tel: string; lat: number; lng: number; pinSize: string; vesselCnt: number; skimmerCnt: number; pumpCnt: number; vehicleCnt: number; sprayerCnt: number; totalAssets: number; distanceNm: number; } export async function fetchNearbyOrgs( lat: number, lng: number, radiusNm: number, ): Promise { const { data } = await api.get('/assets/orgs/nearby', { params: { lat, lng, radius: radiusNm }, }); return data; } // ============================================================ // 사고 관련 이미지 (AERIAL_MEDIA) // ============================================================ export interface AerialMediaItem { aerialMediaSn: number; acdntSn: number | null; fileNm: string; orgnlNm: string | null; filePath: string | null; lon: number | null; lat: number | null; locDc: string | null; equipTpCd: string | null; equipNm: string | null; mediaTpCd: string | null; takngDtm: string | null; fileSz: string | null; resolution: string | null; regDtm: string; } export async function fetchIncidentAerialMedia(acdntSn: number): Promise { try { const { data } = await api.get('/aerial/media', { params: { acdntSn }, }); return data; } catch { return []; } } export function getMediaImageUrl(aerialMediaSn: number): string { return `/api/aerial/media/${aerialMediaSn}/download`; }