From bf61f6f76a4f09e1c13e8575a2ad4808d7f4b09a Mon Sep 17 00:00:00 2001 From: ljpark Date: Tue, 28 Jul 2026 17:51:47 +0900 Subject: [PATCH] =?UTF-8?q?sn:=20=EC=B9=A8=EC=88=98=EB=B0=9C=EC=83=9D?= =?UTF-8?q?=EC=9D=B4=EB=A0=A5=20=ED=8C=9D=EC=97=85=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AgedPipeStatusByCtpvPopupPage.tsx | 3 +- .../agedPipeStatusByCtpvUtils.ts | 10 +- .../map/components/common/table.ts | 13 +-- .../components/fldngHstry/fldngHstryTypes.ts | 97 ++++++++++--------- .../popupComponent/featureClickEventPopup.tsx | 45 ++++++++- apps/sn/middleware.ts | 1 + 6 files changed, 111 insertions(+), 58 deletions(-) diff --git a/apps/sn/app/(map-layout)/map/components/agedPipeStatusByCtpv/AgedPipeStatusByCtpvPopupPage.tsx b/apps/sn/app/(map-layout)/map/components/agedPipeStatusByCtpv/AgedPipeStatusByCtpvPopupPage.tsx index 40abd0a68..7b4dcc924 100644 --- a/apps/sn/app/(map-layout)/map/components/agedPipeStatusByCtpv/AgedPipeStatusByCtpvPopupPage.tsx +++ b/apps/sn/app/(map-layout)/map/components/agedPipeStatusByCtpv/AgedPipeStatusByCtpvPopupPage.tsx @@ -189,7 +189,8 @@ export default function AgedPipeStatusByCtpvPopupPage() {
- headName={<>시도별 노후관로현황 조회 결과 총 {rows.length.toLocaleString()}건} + // 조회 결과 총 {rows.length.toLocaleString()}건 + headName={<>시도별 노후관로현황 } tableData={rows} headlist={headList} pageSize={AGED_PIPE_STATUS_BY_CTPV_PAGE_SIZE} diff --git a/apps/sn/app/(map-layout)/map/components/agedPipeStatusByCtpv/agedPipeStatusByCtpvUtils.ts b/apps/sn/app/(map-layout)/map/components/agedPipeStatusByCtpv/agedPipeStatusByCtpvUtils.ts index 6deaa1329..d41f12d31 100644 --- a/apps/sn/app/(map-layout)/map/components/agedPipeStatusByCtpv/agedPipeStatusByCtpvUtils.ts +++ b/apps/sn/app/(map-layout)/map/components/agedPipeStatusByCtpv/agedPipeStatusByCtpvUtils.ts @@ -51,7 +51,7 @@ export const normalizeAgedPipeStatusByCtpvResponse = ( export const buildAgedPipeStatusYearOptions = () => { const currentYear = new Date().getFullYear(); - return Array.from({ length: 15 }, (_, index) => currentYear - index); + return Array.from({ length: currentYear - 1900 + 1 }, (_, index) => currentYear - index); }; export const normalizeAgedPipeStatusByCtpvFilters = ( @@ -91,7 +91,11 @@ export const validateAgedPipeStatusByCtpvFilters = (filters: AgedPipeStatusByCtp return filters.elapsedYears >= 1 && filters.elapsedYears <= 100; }; -const formatMetric = (value: number) => Number(value ?? 0).toFixed(3); +const formatMetric = (value: number) => + Number(value ?? 0).toLocaleString("ko-KR", { + minimumFractionDigits: 2, + maximumFractionDigits: 2, + }); export const toAgedPipeStatusByCtpvRows = ( response: Partial | null | undefined, @@ -115,7 +119,7 @@ export const toAgedPipeStatusByCtpvRows = ( export const buildAgedPipeStatusByCtpvHeadList = (years: number[]): UiHeadProps[] => { const heads: UiHeadProps[] = [ - { id: "rowNumber", title: "NO", widthPercent: 6, sortable: false }, + // { id: "rowNumber", title: "NO", widthPercent: 6, sortable: false }, { id: "ctpvNm", title: "시도", widthPercent: 14, sortable: false }, ]; 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 6441b99c6..7f24bc7ef 100644 --- a/apps/sn/app/(map-layout)/map/components/common/table.ts +++ b/apps/sn/app/(map-layout)/map/components/common/table.ts @@ -484,13 +484,12 @@ export const FldngWtlvMapCol = [ { id: 'fldng_dstr_nm', title: '침수재해명' }, { id: 'ctpv_nm', title: '시도명' }, { id: 'sgg_nm', title: '시군구명' }, - { id: 'bgng_ymd', title: '침수시작일자' }, - { id: 'fldng_bgng_hm', title: '침수시작시분' }, - { id: 'end_ymd', title: '침수종료일자' }, - { id: 'fldng_end_hm', title: '침수종료시분' }, + { id: 'fldng_bgng_dtm', title: '침수시작일시' }, + { id: 'fldng_end_dtm', title: '침수종료일시' }, { id: 'fldng_hr', title: '침수시간' }, { id: 'fldng_dstrct_nm', title: '침수지구명' }, { id: 'fldng_cs_dtl_cd', title: '침수원인상세코드' }, + { id: 'dtl_ocrn_cs_cn', title: '침수발생원인' }, { id: 'avg_fldng_wtlv', title: '평균침수위' }, { id: 'fldng_area', title: '침수면적' }, { id: 'exmn_yr', title: '조사년도' }, @@ -502,10 +501,8 @@ export const FldngWtlvMapCol2 = [ { id: 'fldngDstrNm', title: '침수재해명' }, { id: 'ctpvNm', title: '시도' }, { id: 'sggNm', title: '시군구' }, - { id: 'bgngYmd', title: '침수시작일자' }, - { id: 'fldngBgngHm', title: '침수시작시분' }, - { id: 'endYmd', title: '침수종료일자' }, - { id: 'fldngEndHm', title: '침수종료시분' }, + { id: 'fldngBgngDtm', title: '침수시작일시' }, + { id: 'fldngEndDtm', title: '침수종료일시' }, { id: 'fldngHr', title: '침수시간' }, { id: 'fldngDstrctNm', title: '침수지구명' }, { id: 'fldngCsDtlCd', title: '침수원인상세코드' }, 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 4743f83a0..310bc81dd 100644 --- a/apps/sn/app/(map-layout)/map/components/fldngHstry/fldngHstryTypes.ts +++ b/apps/sn/app/(map-layout)/map/components/fldngHstry/fldngHstryTypes.ts @@ -27,36 +27,36 @@ export type FldngHstryDepthZone = { }; export type FldngHstryApiItem = { - fid: string; - fldngMapFid?: string; - fldngWtlvMapSn?: number | null; - taskClCd?: string; - fldngDstrNm?: string; - exmnYr?: string; - avgFldngWtlv?: number | null; - fldngArea?: number | null; - dtlOcrnCsCn?: string; - stdgCd?: string; - ctpvCd?: string; - sggCd?: string; - dataSn?: number | null; - bgngYmd?: string; - endYmd?: string; - fldngBgngHm?: string; - fldngEndHm?: string; - fldngCsDtlCd?: string; - fldngDstrctNm?: string; - damAmt?: number | null; - fldngHr?: number | null; - fldngYr?: string; - cstrnFbctnYn?: string; - atchFileSn?: number | null; - confArea?: number | null; - confLen?: number | null; - rainBgngTm?: string | null; - rainEndTm?: string | null; - rainTot?: number | null; - rainStr?: number | null; + fid: string; // FID + fldngMapFid?: string; // 침수흔적도지도 FID + fldngWtlvMapSn?: number | null; // 침수흔적도지도 일련번호 + taskClCd?: string; // 업무 구분 코드 + fldngDstrNm?: string; // 침수재해명 + exmnYr?: string; // 조사년도 + avgFldngWtlv?: number | null; // 평균침수위 + fldngArea?: number | null; // 침수면적 + dtlOcrnCsCn?: string; // 상세발생원인내용 + stdgCd?: string; // 표준지역코드 + ctpvCd?: string; // 시도코드 + sggCd?: string; // 시군구코드 + dataSn?: number | null; // 데이터 일련번호 + bgngYmd?: string; // 시작일자 + endYmd?: string; // 종료일자 + fldngBgngHm?: string; // 침수시작시분 + fldngEndHm?: string; // 침수종료시분 + fldngCsDtlCd?: string; // 침수원인상세코드 + fldngDstrctNm?: string; // 침수지구명 + damAmt?: number | null; // 피해금액 + fldngHr?: number | null; // 침수시간 + fldngYr?: string; // 침수년도 + cstrnFbctnYn?: string; // 공사여부 + atchFileSn?: number | null; // 첨부파일 일련번호 + confArea?: number | null; // 확정면적 + confLen?: number | null; // 확정연장 + rainBgngTm?: string | null; // 강우시작일시 + rainEndTm?: string | null; // 강우종료일시 + rainTot?: number | null; // 총강우량 + rainStr?: number | null; // 강우강도 }; export type FldngHstryItem = FldngHstryApiItem & { @@ -120,20 +120,20 @@ export const FLDNG_HSTRY_DEPTH_LEGEND_ITEMS = [ export const FLDNG_HSTRY_LIST_HEADLIST: UiHeadProps[] = [ { id: "rowNo", title: "NO", widthPercent: 5, sortable: false }, - { id: "floodYearDisplay", title: "침수년도", widthPercent: 6, sortable: true, sortKey: "fldngYr" }, + { id: "floodYearDisplay", title: "침수\n년도", widthPercent: 6, sortable: true, sortKey: "fldngYr" }, { id: "ctpvNm", title: "시도", widthPercent: 7, sortable: true, sortKey: "ctpvCd" }, { id: "sggNm", title: "시군구", widthPercent: 8, sortable: true, sortKey: "sggCd" }, { id: "disasterName", title: "침수재해명", widthPercent: 14, sortable: true, sortKey: "fldngDstrNm" }, - { id: "rainBgngTmDisplay", title: "강우시작일시", widthPercent: 10, sortable: true, sortKey: "rainBgngTm" }, - { id: "rainEndTmDisplay", title: "강우종료일시", widthPercent: 10, sortable: true, sortKey: "rainEndTm" }, + { id: "rainBgngTmDisplay", title: "강우\n시작일시", widthPercent: 10, sortable: true, sortKey: "rainBgngTm" }, + { id: "rainEndTmDisplay", title: "강우\n종료일시", widthPercent: 10, sortable: true, sortKey: "rainEndTm" }, { id: "rainTotDisplay", title: "총강우량(mm)", widthPercent: 8, sortable: true, sortKey: "rainTot" }, { id: "rainStrDisplay", title: "강우강도(mm/hr)", widthPercent: 8, sortable: true, sortKey: "rainStr" }, { id: "floodHourDisplay", title: "침수시간", widthPercent: 7, sortable: true, sortKey: "fldngHr" }, { 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: "평균침수위", widthPercent: 7, sortable: true, sortKey: "avgFldngWtlv" }, - { id: "floodAreaDisplay", title: "침수면적", widthPercent: 7, sortable: true, sortKey: "fldngArea" }, + //{ 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: "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" }, ]; @@ -313,6 +313,13 @@ const formatInteger = (value?: number | null, suffix = "") => { return `${numeric.toLocaleString("ko-KR")}${suffix}`; }; +const formatOptionalNumber = (value?: number | null, digits = 1, suffix = "") => { + if (value === null || value === undefined || value === "") return "-"; + const numeric = Number(value); + if (!Number.isFinite(numeric)) return "-"; + return `${numeric.toFixed(digits)}${suffix}`; +}; + const buildCauseDetailName = (item: FldngHstryApiItem) => { const direct = String(item.dtlOcrnCsCn ?? "").trim(); if (direct) return direct; @@ -361,9 +368,9 @@ export const buildFldngHstryListQueryString = ( return params.toString(); }; -export const formatFloodArea = (value: number) => `${value.toFixed(1)}ha`; +export const formatFloodArea = (value: number) => Number(value ?? 0).toLocaleString("ko-KR"); -export const formatFloodDepth = (value: number) => `${value.toFixed(2)}m`; +export const formatFloodDepth = (value: number) => value.toFixed(2); export const attachFldngHstryGeometry = (item: FldngHstryApiItem): FldngHstryItem => { const historyId = String(item.fid ?? ""); @@ -392,10 +399,10 @@ export const attachFldngHstryGeometry = (item: FldngHstryApiItem): FldngHstryIte endDate, startTime, endTime, - floodHour: formatNumber(Number(item.fldngHr ?? 0), 1, "시간"), + floodHour: formatNumber(Number(item.fldngHr ?? 0), 1), causeDetailCode: String(item.fldngCsDtlCd ?? "").trim(), causeDetailName: buildCauseDetailName(item), - damageAmount: formatInteger(Number(item.damAmt ?? 0), "원"), + damageAmount: formatInteger(Number(item.damAmt ?? 0)), rainStartDateTime: normalizeDateTime(item.rainBgngTm), rainEndDateTime: normalizeDateTime(item.rainEndTm), rainStartTime: normalizeDateTime(item.rainBgngTm).slice(11, 16), @@ -422,16 +429,16 @@ export const toFldngHstryTableRows = ( items.map((item, index) => ({ ...item, rowNo: (page - 1) * pageSize + index + 1, - floodYearDisplay: item.floodYear ? `${item.floodYear}년` : "-", + floodYearDisplay: item.floodYear || "-", floodHourDisplay: item.floodHour || "-", avgFloodDepthDisplay: formatFloodDepth(item.avgFloodDepthM), floodAreaDisplay: formatFloodArea(item.floodAreaHa), - exmnYearDisplay: item.exmnYear ? `${item.exmnYear}년` : "-", + exmnYearDisplay: item.exmnYear || "-", damageAmountDisplay: item.damageAmount || "-", rainBgngTmDisplay: item.rainStartDateTime || "-", rainEndTmDisplay: item.rainEndDateTime || "-", - rainTotDisplay: formatNumber(item.totalRainfallMm, 1), - rainStrDisplay: formatNumber(item.rainfallIntensityMmHr, 1), + rainTotDisplay: formatOptionalNumber(item.rainTot, 1), + rainStrDisplay: formatOptionalNumber(item.rainStr, 1), })); const resolveFldngHstryNodeName = (node: any) => 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 ae9d47898..70251c87c 100644 --- a/apps/sn/app/(map-layout)/map/components/popupComponent/featureClickEventPopup.tsx +++ b/apps/sn/app/(map-layout)/map/components/popupComponent/featureClickEventPopup.tsx @@ -32,7 +32,16 @@ import Fill from "ol/style/Fill.js"; import Stroke from "ol/style/Stroke.js"; import CircleStyle from "ol/style/Circle.js"; import { getLayerContentComponent } from "../common"; -import { FldngWtlvMapCol, FldngWtlvMapCol2, SwtrmtFcltCol, SwtrmtFcltCol2, SwtrmtPrcsZoneCol, SwtrmtPrcsZoneCol2, untrtSwgRainCol1, untrtSwgRainCol2, MsSwagePplCol, MsSwagePplCol2, ExclnSewagePumpCol, ExclnSewagePumpCol2, DrtwSewagePumpCol, DrtwSewagePumpCol2, MsSwageMhlCol, MsSwageMhlCol2, stofcbrCol, stofcbrCol2, MS_SWAGE_MHL_CODES, DRTW_SEWAGE_PUMP_USE_CODES } from "../common/table"; +import { FldngWtlvMapCol, FldngWtlvMapCol2 + , SwtrmtFcltCol, SwtrmtFcltCol2 + , SwtrmtPrcsZoneCol, SwtrmtPrcsZoneCol2 + , untrtSwgRainCol1, untrtSwgRainCol2 + , MsSwagePplCol, MsSwagePplCol2 + , ExclnSewagePumpCol, ExclnSewagePumpCol2 + , DrtwSewagePumpCol, DrtwSewagePumpCol2 + , MsSwageMhlCol, MsSwageMhlCol2 + , stofcbrCol, stofcbrCol2 + , MS_SWAGE_MHL_CODES, DRTW_SEWAGE_PUMP_USE_CODES } from "../common/table"; import { formatDisplayData } from '@/components/lib/utils/formatDisplayValue'; import { codeMapper } from "../common/codeMapper"; import LoadingOverlay from '@/components/ui/LoadingBar'; @@ -68,6 +77,25 @@ const normalizeFldngWtlvMapDisplayId = (value: unknown) => { return normalized.startsWith(prefix) ? normalized.slice(prefix.length) : normalized; }; +// 침수흔적도 침수시작일시, 침수종료일시 한줄로 보이도록 +const formatFldngWtlvDateTime = (dateValue: unknown, timeValue: unknown) => { + const rawDate = String(dateValue ?? "").trim(); + const rawTime = String(timeValue ?? "").trim(); + + const normalizedDate = /^\d{8}$/.test(rawDate) + ? `${rawDate.slice(0, 4)}-${rawDate.slice(4, 6)}-${rawDate.slice(6, 8)}` + : rawDate; + + const timeDigits = rawTime.replace(/[^0-9]/g, ""); + const normalizedTime = !timeDigits + ? "" + : timeDigits.length <= 2 + ? `${timeDigits.padStart(2, "0")}:00` + : `${timeDigits.padStart(4, "0").slice(0, 2)}:${timeDigits.padStart(4, "0").slice(2, 4)}`; + + return [normalizedDate, normalizedTime].filter(Boolean).join(" "); +}; + type MngInstOption = { mngInstCd: string; lwrInstNm: string; @@ -475,6 +503,21 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void, ? withResolvedRegionNames(data) : data; const formattedData = withMngInstName(formatDisplayData(normalizedRegionData)); + if (selectedLayerName === '침수흔적도') { + const fldngBgngDtm = formatFldngWtlvDateTime( + formattedData.bgngYmd ?? formattedData.bgng_ymd ?? data.bgngYmd ?? data.bgng_ymd, + formattedData.fldngBgngHm ?? formattedData.fldng_bgng_hm ?? data.fldngBgngHm ?? data.fldng_bgng_hm, + ); + const fldngEndDtm = formatFldngWtlvDateTime( + formattedData.endYmd ?? formattedData.end_ymd ?? data.endYmd ?? data.end_ymd, + formattedData.fldngEndHm ?? formattedData.fldng_end_hm ?? data.fldngEndHm ?? data.fldng_end_hm, + ); + + formattedData.fldng_bgng_dtm = fldngBgngDtm; + formattedData.fldngBgngDtm = fldngBgngDtm; + formattedData.fldng_end_dtm = fldngEndDtm; + formattedData.fldngEndDtm = fldngEndDtm; + } if (selectedLayerName === '하수관로') { const installYmd = data.instl_ymd ?? data.instlYmd; formattedData.elapsedYears = getElapsedYearsDisplayText(installYmd); diff --git a/apps/sn/middleware.ts b/apps/sn/middleware.ts index 2eddd4de5..f8a8c42e1 100644 --- a/apps/sn/middleware.ts +++ b/apps/sn/middleware.ts @@ -72,6 +72,7 @@ export function middleware(req: NextRequest) { typename == "hasudo:tb_il_swage_prcs_fclt" //하수처리시설 || typename == "hasudo:tb_ms_swage_prcs_zone" //하수처리구역 || typename == "hasudo:tb_ms_fldng_wtlv_map" //침수흔적도 + || typename == "hasudo:tb_ms_fldng_dpth_map" //침수흔적도-깊이 || typename == "hasudo:tb_ms_sbmrg_dngr_map" //침수위험지도 || typename == "hasudo:tb_ms_flood_dngr_map" //도시침수지도 ) {