지반침하 상세 관로 용도 OGC011 매핑 및 표 너비 조정

This commit is contained in:
kiminchan
2026-06-29 14:51:46 +09:00
parent f4b8f9d65d
commit ee3ddce41d
2 changed files with 82 additions and 5 deletions
@@ -1,8 +1,9 @@
"use client";
import { useMemo } from "react";
import { useEffect, useMemo, useState } from "react";
import { Button } from "@common/components";
import { VerticalTable } from "@/components/ui/verticalTable";
import { getCmsData } from "@/components/lib/api";
import type { SinkholeDetail, SinkholeNearbyPipe } from "./sinkholeTypes";
type Props = {
@@ -13,6 +14,14 @@ type Props = {
onSelectPipe: (pipe: SinkholeNearbyPipe) => void;
};
type PipeUseCodeItem = {
lwpoCd: string;
lwpoCdNm: string;
};
let pipeUseCodeCache: PipeUseCodeItem[] | null = null;
let pipeUseCodePromise: Promise<PipeUseCodeItem[]> | null = null;
const display = (value: unknown) => {
const normalized = String(value ?? "").trim();
return normalized ? normalized : "-";
@@ -33,6 +42,31 @@ const parseElapsedYears = (value: unknown) => {
const displayElapsedYears = (value: unknown) => displayNumber(value, "년");
const normalizePipeUseCodes = (data: any): PipeUseCodeItem[] => {
if (!Array.isArray(data)) return [];
return data.map((item: any) => ({
lwpoCd: String(item?.lwpoCd ?? "").trim(),
lwpoCdNm: String(item?.lwpoCdNm ?? "").trim(),
}));
};
const loadPipeUseCodes = async () => {
if (pipeUseCodeCache) return pipeUseCodeCache;
if (!pipeUseCodePromise) {
pipeUseCodePromise = getCmsData(`/api/v1/code/cache/OGC011/Y`)
.catch(() => getCmsData(`/api/v1/code/cache/OGC011`))
.then((data) => {
const codes = normalizePipeUseCodes(data);
pipeUseCodeCache = codes;
return codes;
})
.finally(() => {
pipeUseCodePromise = null;
});
}
return pipeUseCodePromise;
};
export default function SinkholeDetailPanel({
detail,
nearbyPipes,
@@ -40,7 +74,32 @@ export default function SinkholeDetailPanel({
onClose,
onSelectPipe,
}: Props) {
const [pipeUseCodeMap, setPipeUseCodeMap] = useState<Map<string, string>>(new Map());
const canShowNearby = String(detail.coordStatus ?? "").trim() === "SUCCESS";
useEffect(() => {
let cancelled = false;
loadPipeUseCodes()
.then((codes) => {
if (cancelled) return;
setPipeUseCodeMap(new Map(codes.map((item) => [item.lwpoCd, item.lwpoCdNm])));
})
.catch((error) => {
console.error("Failed to load OGC011 pipe use codes:", error);
});
return () => {
cancelled = true;
};
}, []);
const getPipeUseName = (pipe: SinkholeNearbyPipe) => {
const rawCode = String(pipe.pipeTypeCd ?? pipe.pipeTypeName ?? "").trim();
if (!rawCode) return "-";
return pipeUseCodeMap.get(rawCode) ?? rawCode;
};
const nearbyPipeElapsedSummary = useMemo(() => {
const elapsedYears = nearbyPipes
.map((pipe) => parseElapsedYears(pipe.elapsedYears))
@@ -56,6 +115,7 @@ export default function SinkholeDetailPanel({
return { average, oldest };
}, [nearbyPipes]);
const summaryTableData = useMemo(
() => [
{
@@ -157,10 +217,11 @@ export default function SinkholeDetailPanel({
<div className="card !mx-[1.2rem] !mb-[1.2rem] !mt-0 overflow-hidden">
<table className="w-full table-fixed border-collapse">
<colgroup>
<col className="w-[38px]" />
<col className="w-[122px]" />
<col className="w-[72px]" />
<col className="w-[70px]" />
<col className="w-[34px]" />
<col className="w-[90px]" />
<col className="w-[60px]" />
<col className="w-[56px]" />
<col className="w-[56px]" />
</colgroup>
<thead>
<tr>
@@ -170,6 +231,9 @@ export default function SinkholeDetailPanel({
<th className="border border-gray-300 bg-blue-100 px-1 py-2 text-center text-[0.95rem] font-bold text-slate-900 whitespace-nowrap">
관로 ID
</th>
<th className="border border-gray-300 bg-blue-100 px-1 py-2 text-center text-[0.95rem] font-bold text-slate-900 whitespace-nowrap">
용도
</th>
<th className="border border-gray-300 bg-blue-100 px-1 py-2 text-center text-[0.95rem] font-bold text-slate-900 whitespace-nowrap">
경과년도
</th>
@@ -191,6 +255,9 @@ export default function SinkholeDetailPanel({
<td className="border border-gray-300 px-1 py-2 text-left text-[0.95rem] font-semibold text-[#1d4ed8] underline underline-offset-2">
{display(pipe.pipeId)}
</td>
<td className="border border-gray-300 px-1 py-2 text-center text-[0.95rem] text-slate-900 whitespace-nowrap">
{getPipeUseName(pipe)}
</td>
<td className="border border-gray-300 px-1 py-2 text-right text-[0.95rem] text-slate-900">
{displayElapsedYears(pipe.elapsedYears)}
</td>
@@ -370,3 +370,13 @@
- `map/page.tsx`는 기존 기능이 많아 무리하게 누적하면 회귀 위험이 크다.
- 원천 데이터에 없는 컬럼을 화면은 요구하고 있어, API/FE 표시 정책이 늦게 확정되면 구현이 되돌아갈 수 있다.
- 좌표 없는 사고건과 좌표 실패 사고건은 UI와 QA 시나리오가 달라 예외 흐름이 많다.
### 4.44 2026-06-29 지반침하 상세 전용 OGC011 공통코드 매핑으로 범위 제한
- 지반침하 상세의 반경 내 관로 `용도`만 `OGC011` 공통코드로 한글화하도록 범위를 다시 좁혔다.
- 초기 보완에서는 `searchCmmnCd` 공통 헬퍼를 확장했지만, 다른 기능으로 영향 범위가 번질 수 있어 공통 헬퍼 변경은 되돌렸다.
- 대신 `SinkholeDetailPanel.tsx` 내부에서만 `getCmsData('/api/v1/code/cache/OGC011/Y')`를 우선 조회하고, 실패 시 `OGC011` 전체 조회로 fallback 하도록 로컬 캐시 로더를 추가했다.
- 이로써 현재 `sn`에서 공통코드 확장 영향은 지반침하 상세 패널 내부에만 한정되고, 기존 다른 화면의 공통코드/하드코딩 코드표 흐름은 건드리지 않는다.
- 표 컬럼 폭도 함께 조정해 `관로 ID`는 소폭 줄이고 `용도`는 한 줄로 보이도록 미세 보정했다.
- 변경 파일은 `FE_APP_ROOT/app/(map-layout)/map/components/sinkhole/SinkholeDetailPanel.tsx`, `FE_APP_ROOT/app/components/lib/common/apiHelpers.ts` 2개이며, 공통 헬퍼는 원래 계약으로 복구했다.
- BE API, DTO, DB 변경은 없다.