gc-wing/apps/web/src/entities/subcable/api/useSubcables.ts
htlee ca5560aff2 feat(map): 해저케이블 레이어 및 정보 패널 구현
- subcable entity 생성 (타입 정의 + 데이터 로딩 hook)
- MapLibre 레이어: 케이블 라인 + 호버 하이라이트 + 라벨
- 지도 표시 설정에 해저케이블 토글 추가
- 클릭 시 우측 정보 패널 (길이, 개통, 운영사, landing points)
- Map3D + DashboardPage 통합

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 02:17:58 +09:00

53 lines
1.5 KiB
TypeScript

import { useEffect, useState } from 'react';
import type { SubcableGeoJson, SubcableDetailsIndex, SubcableDetail } from '../model/types';
interface SubcableData {
geo: SubcableGeoJson;
details: Map<string, SubcableDetail>;
}
export function useSubcables(
geoUrl = '/data/subcables/cable-geo.json',
detailsUrl = '/data/subcables/cable-details.min.json',
) {
const [data, setData] = useState<SubcableData | null>(null);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
let cancelled = false;
async function run() {
try {
setError(null);
const [geoRes, detailsRes] = await Promise.all([
fetch(geoUrl),
fetch(detailsUrl),
]);
if (!geoRes.ok) throw new Error(`Failed to load subcable geo: ${geoRes.status}`);
if (!detailsRes.ok) throw new Error(`Failed to load subcable details: ${detailsRes.status}`);
const geo = (await geoRes.json()) as SubcableGeoJson;
const detailsJson = (await detailsRes.json()) as SubcableDetailsIndex;
if (cancelled) return;
const details = new Map<string, SubcableDetail>();
for (const [id, detail] of Object.entries(detailsJson.by_id)) {
details.set(id, detail);
}
setData({ geo, details });
} catch (e) {
if (cancelled) return;
setError(e instanceof Error ? e.message : String(e));
}
}
void run();
return () => {
cancelled = true;
};
}, [geoUrl, detailsUrl]);
return { data, error };
}