diff --git a/apps/sn/app/(auth)/auth/layout.tsx b/apps/sn/app/(auth)/auth/layout.tsx index 42f63e71a..2b4eb89a8 100644 --- a/apps/sn/app/(auth)/auth/layout.tsx +++ b/apps/sn/app/(auth)/auth/layout.tsx @@ -9,9 +9,5 @@ export default function AuthLayout({ }: { children: React.ReactNode; }) { - return ( - - {children} - - ); + return children; } diff --git a/apps/sn/app/(map-layout)/map/components/common/table.ts b/apps/sn/app/(map-layout)/map/components/common/table.ts index 7f24bc7ef..e65222ddf 100644 --- a/apps/sn/app/(map-layout)/map/components/common/table.ts +++ b/apps/sn/app/(map-layout)/map/components/common/table.ts @@ -494,6 +494,10 @@ export const FldngWtlvMapCol = [ { id: 'fldng_area', title: '침수면적' }, { id: 'exmn_yr', title: '조사년도' }, { id: 'dam_amt', title: '피해금액' }, + { id: 'rain_bgng_tm', title: '강우시작일시' }, + { id: 'rain_end_tm', title: '강우종료일시' }, + { id: 'rain_tot', title: '총강우량(mm)' }, + { id: 'rain_str', title: '강우강도(mm/hr)' }, ]; export const FldngWtlvMapCol2 = [ { id: 'rowNumber', title: '일련번호' }, @@ -510,6 +514,10 @@ export const FldngWtlvMapCol2 = [ { id: 'fldngArea', title: '침수면적' }, { id: 'exmnYr', title: '조사년도' }, { id: 'damAmt', title: '피해금액' }, + { id: 'rainBgngTm', title: '강우시작일시' }, + { id: 'rainEndTm', title: '강우종료일시' }, + { id: 'rainTot', title: '총강우량(mm)' }, + { id: 'rainStr', title: '강우강도(mm/hr)' }, ]; export const FldngWtlvMapExcelCol = { 'fldngYr': '침수년도', diff --git a/apps/sn/app/(map-layout)/map/components/fldngHstry/fldngHstryTypes.ts b/apps/sn/app/(map-layout)/map/components/fldngHstry/fldngHstryTypes.ts index 310bc81dd..8940bdb3d 100644 --- a/apps/sn/app/(map-layout)/map/components/fldngHstry/fldngHstryTypes.ts +++ b/apps/sn/app/(map-layout)/map/components/fldngHstry/fldngHstryTypes.ts @@ -132,7 +132,7 @@ export const FLDNG_HSTRY_LIST_HEADLIST: UiHeadProps[] = [ { id: "districtName", title: "침수지구명", widthPercent: 10, sortable: true, sortKey: "fldngDstrctNm" }, //{ id: "causeDetailCode", title: "침수원인상세코드", widthPercent: 8, sortable: true, sortKey: "fldngCsDtlCd" }, //{ id: "causeDetailName", title: "침수원인상세", widthPercent: 12, sortable: false }, - { id: "avgFloodDepthDisplay", title: "평균\n침수위(m)", widthPercent: 7, sortable: true, sortKey: "avgFldngWtlv" }, + { id: "avgFloodDepthDisplay", title: "평균\n침수위", widthPercent: 7, sortable: true, sortKey: "avgFldngWtlv" }, { id: "floodAreaDisplay", title: "침수\n면적\(ha)", widthPercent: 7, sortable: true, sortKey: "fldngArea" }, { id: "exmnYearDisplay", title: "조사년도", widthPercent: 6, sortable: true, sortKey: "exmnYr" }, { id: "damageAmountDisplay", title: "피해금액", widthPercent: 9, sortable: true, sortKey: "damAmt" }, @@ -291,6 +291,7 @@ const normalizeHm = (value?: string | null) => { const normalizeDateTime = (value?: string | null) => { const source = String(value ?? "").trim(); if (!source) return ""; + if (/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}$/.test(source)) return source; const date = new Date(source); if (Number.isNaN(date.getTime())) return source.slice(0, 16).replace("T", " "); const yyyy = date.getFullYear(); @@ -368,7 +369,10 @@ export const buildFldngHstryListQueryString = ( return params.toString(); }; -export const formatFloodArea = (value: number) => Number(value ?? 0).toLocaleString("ko-KR"); +export const formatFloodArea = (value: number) => Number(value ?? 0).toLocaleString("ko-KR", { + minimumFractionDigits: 0, + maximumFractionDigits: 2, + }); export const formatFloodDepth = (value: number) => value.toFixed(2); diff --git a/apps/sn/app/(map-layout)/map/components/popupComponent/featureClickEventPopup.tsx b/apps/sn/app/(map-layout)/map/components/popupComponent/featureClickEventPopup.tsx index 70251c87c..fea0e45df 100644 --- a/apps/sn/app/(map-layout)/map/components/popupComponent/featureClickEventPopup.tsx +++ b/apps/sn/app/(map-layout)/map/components/popupComponent/featureClickEventPopup.tsx @@ -77,6 +77,26 @@ const normalizeFldngWtlvMapDisplayId = (value: unknown) => { return normalized.startsWith(prefix) ? normalized.slice(prefix.length) : normalized; }; +const normalizeFldngWtlvMapApiFid = (value: unknown) => { + const normalized = String(value ?? "").trim(); + if (!normalized) return ""; + return normalizeFldngWtlvMapDisplayId(normalized); +}; + +const normalizeRainAnalysisPayload = (data: any) => { + if (!data || typeof data !== "object") return {}; + return { + rain_bgng_tm: data.rainBgngTm ?? data.rain_bgng_tm ?? null, + rainBgngTm: data.rainBgngTm ?? data.rain_bgng_tm ?? null, + rain_end_tm: data.rainEndTm ?? data.rain_end_tm ?? null, + rainEndTm: data.rainEndTm ?? data.rain_end_tm ?? null, + rain_tot: data.rainTot ?? data.rain_tot ?? null, + rainTot: data.rainTot ?? data.rain_tot ?? null, + rain_str: data.rainStr ?? data.rain_str ?? null, + rainStr: data.rainStr ?? data.rain_str ?? null, + }; +}; + // 침수흔적도 침수시작일시, 침수종료일시 한줄로 보이도록 const formatFldngWtlvDateTime = (dateValue: unknown, timeValue: unknown) => { const rawDate = String(dateValue ?? "").trim(); @@ -627,6 +647,26 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void, console.warn("Pipe detail fallback kept initial feature data."); } } + + if (selectedLayerName === '침수흔적도' && featureId) { + try { + const normalizedFloodMapFid = normalizeFldngWtlvMapApiFid(featureId); + if (!normalizedFloodMapFid) { + throw new Error("침수흔적도 fid가 비어 있습니다."); + } + const rainAnalysis = await getData(`v1/fldng/hstry/rain-anls/${encodeURIComponent(normalizedFloodMapFid)}`); + if (rainAnalysis) { + const mergedData = { + ...formattedData, + ...normalizeRainAnalysisPayload(rainAnalysis), + }; + setFetchData(mergedData); + setTableData(mergedData); + } + } catch (rainError) { + console.warn("침수강우분석 조회 실패:", rainError); + } + } } catch (error) { console.error('데이터 로딩 중 오류:', error); } diff --git a/apps/sn/docs/작업내용/SFR-011-009-침수발생이력/001-fe-SFR-011-009-침수발생이력-통합구현기록.md b/apps/sn/docs/작업내용/SFR-011-009-침수발생이력/001-fe-SFR-011-009-침수발생이력-통합구현기록.md index 6067080f1..fa051e08d 100644 --- a/apps/sn/docs/작업내용/SFR-011-009-침수발생이력/001-fe-SFR-011-009-침수발생이력-통합구현기록.md +++ b/apps/sn/docs/작업내용/SFR-011-009-침수발생이력/001-fe-SFR-011-009-침수발생이력-통합구현기록.md @@ -71,3 +71,15 @@ - popup 라우트의 초기 컴파일 시간이 길어 자동 요청에서 timeout이 발생할 수 있다. - `침수흔적도` 기준데이터 이름/구조와 FE 판별식이 다를 수 있다. - 실제 API 필드가 확정되면 상세패널 항목명/정렬이 일부 바뀔 수 있다. + +## 7. 2026-07-28 지도 클릭 강우분석 조회 연동 + +- 반영 파일 + - `FE_APP_ROOT/app/(map-layout)/map/components/popupComponent/featureClickEventPopup.tsx` + - `FE_APP_ROOT/app/(map-layout)/map/components/common/table.ts` +- 구현 내용 + - 지도에서 `침수흔적도(tb_ms_fldng_wtlv_map)`를 클릭해 우측 상세를 열면, 기존 원본 속성값만 보여주던 구조에 `fid` 기준 강우분석 조회를 추가했다. + - FE는 `GET /api/v1/fldng/hstry/rain-anls/{fid}`를 추가 호출한 뒤 `tb_sn_fldng_wtlv_rain_anls` 응답을 상세 테이블 데이터에 merge한다. + - 침수흔적도 상세 컬럼에 `강우시작일시`, `강우종료일시`, `총강우량(mm)`, `강우강도(mm/hr)`를 추가했다. +- 영향 + - 레이어 트리 popup 목록과 별도로, 실제 지도 객체 클릭 상세에서도 강우분석 정보를 동일하게 확인할 수 있다. diff --git a/packages/common/business_components/src/ui/map/GisTree.js b/packages/common/business_components/src/ui/map/GisTree.js index e58d66cbf..daae30fe4 100644 --- a/packages/common/business_components/src/ui/map/GisTree.js +++ b/packages/common/business_components/src/ui/map/GisTree.js @@ -585,10 +585,8 @@ export default React.memo(function GisTree({ const isSinkholeRootTreeNode = String(node.id) === "SN_SINKHOLE" || nodeLabel === "지반침하정보"; - const isFloodHistoryRootTreeNode = nodeLabel === "침수흔적도"; const hideTreeLegendIcon = - (isSinkholeRootTreeNode && !sinkholeLegendIconVisible) || - isFloodHistoryRootTreeNode; + (isSinkholeRootTreeNode && !sinkholeLegendIconVisible); const showLayerActionButton = layerIconVisible?.(node); const showLayerActionIcon = depth > 0 && imageVisible;