wing-ops/frontend/src/tabs/incidents/services/incidentsApi.ts
htlee 76f299e5de fix(frontend): api named export import 수정 (Vite 빌드 오류)
api.ts의 named export를 default import로 사용하여 Vite/Rollup 프로덕션 빌드 실패 수정

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 22:26:55 +09:00

173 lines
4.6 KiB
TypeScript

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;
mediaCnt: number;
}
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<string, unknown> | null;
droneMeta: Record<string, unknown> | null;
satMeta: Record<string, unknown> | null;
cctvMeta: Record<string, unknown> | 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;
}
function toCompat(item: IncidentListItem): IncidentCompat {
const dt = new Date(item.occrnDtm);
const statusMap: Record<string, 'active' | 'investigating' | 'closed'> = {
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.fcstHr ? '예측완료' : undefined,
mediaCount: item.mediaCnt,
};
}
// ============================================================
// API 호출 함수
// ============================================================
export async function fetchIncidentsRaw(): Promise<IncidentListItem[]> {
const { data } = await api.get<IncidentListItem[]>('/incidents');
return data;
}
export async function fetchIncidents(filters?: {
status?: string;
region?: string;
search?: string;
startDate?: string;
endDate?: string;
}): Promise<IncidentCompat[]> {
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<IncidentListItem[]>(url);
return data.map(toCompat);
}
export async function fetchIncidentDetail(sn: number): Promise<IncidentDetail> {
const { data } = await api.get<IncidentDetail>(`/incidents/${sn}`);
return data;
}
export async function fetchIncidentWeather(sn: number): Promise<WeatherInfo | null> {
try {
const { data } = await api.get<WeatherInfo>(`/incidents/${sn}/weather`);
return data;
} catch {
return null;
}
}
export async function fetchIncidentMedia(sn: number): Promise<MediaInfo | null> {
try {
const { data } = await api.get<MediaInfo>(`/incidents/${sn}/media`);
return data;
} catch {
return null;
}
}
export async function fetchIncidentPredictions(sn: number): Promise<PredExecItem[]> {
const { data } = await api.get<PredExecItem[]>(`/incidents/${sn}/predictions`);
return data;
}