api.ts의 named export를 default import로 사용하여 Vite/Rollup 프로덕션 빌드 실패 수정 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
173 lines
4.6 KiB
TypeScript
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;
|
|
}
|