From e5f7778861a048e7bcca4ea78fb47c5c48655c03 Mon Sep 17 00:00:00 2001 From: ljpark Date: Fri, 2 Oct 2026 18:07:00 +0900 Subject: [PATCH] =?UTF-8?q?sn:=20=ED=99=95=EB=A5=A0=EA=B0=95=EC=9A=B0?= =?UTF-8?q?=EB=9F=89=20=EB=AA=A8=EB=8B=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...ce-SFR-011-008-침수예측현황-확률강우량-작업계획.md | 41 ++++ ...fe-SFR-011-008-침수예측현황-확률강우량-구현기록.md | 19 ++ .../fldngPredcPrst/ui/PrbbtRnflDialog.tsx | 204 ++++++++++++++++++ 3 files changed, 264 insertions(+) create mode 100644 apps/sn/docs/작업계획/SFR-011-008-침수예측/002-maintenance-SFR-011-008-침수예측현황-확률강우량-작업계획.md create mode 100644 apps/sn/docs/작업내용/SFR-011-008-침수예측/005-fe-SFR-011-008-침수예측현황-확률강우량-구현기록.md create mode 100644 apps/sn/src/features/fldngPredcPrst/ui/PrbbtRnflDialog.tsx diff --git a/apps/sn/docs/작업계획/SFR-011-008-침수예측/002-maintenance-SFR-011-008-침수예측현황-확률강우량-작업계획.md b/apps/sn/docs/작업계획/SFR-011-008-침수예측/002-maintenance-SFR-011-008-침수예측현황-확률강우량-작업계획.md new file mode 100644 index 000000000..18497c7c6 --- /dev/null +++ b/apps/sn/docs/작업계획/SFR-011-008-침수예측/002-maintenance-SFR-011-008-침수예측현황-확률강우량-작업계획.md @@ -0,0 +1,41 @@ +# SFR-011-008 침수예측현황 확률강우량 FE 유지보수 작업계획 + +## 작업 목적 + +침수예측현황의 선택 시군구가 어떤 확률강우량 기준으로 위험도가 산정되는지 확인할 수 있도록 목록 열과 기준표 팝업을 추가한다. + +## 승인 상태 + +- `maintenance` 작업계획 +- 2026-10-02 사용자 승인, 코드 구현 진행 + +## 구현 계획 + +- `FldngPredcPrstPopupContent`의 `침수위험도` 오른쪽에 `확률강우량` 액션을 추가한다. +- 기준표 팝업 컴포넌트 파일은 표준용어를 사용한 `PrbbtRnflDialog.tsx`로 둔다. +- 목록의 확률강우량은 `predc_rain_hr` 기준 `N시간` 형식의 클릭 가능한 지속시간으로 표시한다. 기존 `predictionHour`(`predc_hr`)와 구분한다. +- 선택 행의 `sggCd`로 BE 확률강우량 조회 API를 호출한다. +- API 경로는 사용자 지정 `GET /api/v1/fldng/predc/prst/prbbt-rnfl`을 사용한다. +- 팝업 표는 재현기간을 행, `1시간`, `2시간`, `3시간` 형식의 지속시간을 열로 표시한다. +- 팝업 하단에 확률관측소코드·관측소명·면적비율(%) 비율표를 표시한다. +- 팝업 맨 아래에 `시간은 강우가 지속된 시간입니다` 안내를 표시한다. +- 지속시간 헤더는 기존 팝업 표 헤더 스타일을 유지한다. +- 선택 행의 대표 지속시간에 해당하는 열의 확률강우량 기준값 셀들을 강조 색상으로 표시한다. 표 헤더는 기존 스타일을 유지한다. +- 강수량은 mm 단위로 표시하고 로딩·빈 결과·오류 상태를 제공한다. +- 기존 행 클릭의 도시침수지도 이동과 별도 액션으로 동작시킨다. + +## 검증 계획 + +- 제주·서울 등 서로 다른 시군구 기준표 조회 +- 1~72시간 행과 빈도년도 열 표시 +- API 오류·빈 결과 표시 +- 기존 지도 이동 회귀 +- `pnpm --filter sn run build` + +## 구현 결과 (2026-10-02) + +- 현황 목록에 `확률강우량` 열을 추가하고 대표 지속시간 `predcRainHr`를 `N시간` 링크로 표시한다. +- 링크 클릭은 행 선택 지도 이동을 실행하지 않으며, 시군구별 기준표 팝업을 연다. +- 팝업은 재현기간 행×지속시간 열의 확률강우량, 하단 관측소 면적비율과 API 합계를 표시한다. +- 선택 지속시간 열의 값 셀만 강조하고 헤더는 기존 스타일을 유지한다. +- `pnpm --filter sn run typecheck`, `git diff --check`가 통과했다. 3002 포트 개발 서버 실행 중이므로 build는 실행하지 않았다. diff --git a/apps/sn/docs/작업내용/SFR-011-008-침수예측/005-fe-SFR-011-008-침수예측현황-확률강우량-구현기록.md b/apps/sn/docs/작업내용/SFR-011-008-침수예측/005-fe-SFR-011-008-침수예측현황-확률강우량-구현기록.md new file mode 100644 index 000000000..a7071cccf --- /dev/null +++ b/apps/sn/docs/작업내용/SFR-011-008-침수예측/005-fe-SFR-011-008-침수예측현황-확률강우량-구현기록.md @@ -0,0 +1,19 @@ +# SFR-011-008 침수예측현황 확률강우량 FE 구현기록 + +## 구현 내용 +- 확률강우량 팝업 컴포넌트는 `PrbbtRnflDialog.tsx`에 둔다. +- 침수위험도 오른쪽에 확률강우량 열을 추가하고 대표 누적강우 지속시간 `predcRainHr`를 `N시간` 링크로 표시했다. +- 링크는 `GET /api/v1/fldng/predc/prst/prbbt-rnfl?sggCd=...`를 호출한다. +- 팝업 본문에 재현기간 행×지속시간 열의 확률강우량표를 표시한다. +- 선택 지속시간 열의 값 셀만 강조하고 표 헤더는 기존 스타일을 유지한다. +- 팝업 하단에 확률관측소코드·관측소명·면적비율 및 API 합계를 표시한다. +- 팝업 맨 아래에 `시간은 강우가 지속된 시간입니다` 안내를 표시한다. +- 링크 클릭 이벤트는 기존 행 선택의 지도 이동으로 전파하지 않는다. + +## 검증 +- `pnpm --filter sn run typecheck` 성공 +- `git diff --check` 성공 +- 개발 서버(3002)가 실행 중이어서 build는 실행하지 않았다. + +## 수동 확인 필요 +- 실제 로그인 화면에서 팝업 크기·가로 스크롤·강조 색상 및 지도 이동 회귀 확인 diff --git a/apps/sn/src/features/fldngPredcPrst/ui/PrbbtRnflDialog.tsx b/apps/sn/src/features/fldngPredcPrst/ui/PrbbtRnflDialog.tsx new file mode 100644 index 000000000..c870d5ab2 --- /dev/null +++ b/apps/sn/src/features/fldngPredcPrst/ui/PrbbtRnflDialog.tsx @@ -0,0 +1,204 @@ +"use client"; + +import { useMemo } from "react"; + +import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@common/components"; + +/** 시군구별 면적가중 확률강우량 한 셀의 API 응답이다. */ +export type PrbbtRnflCell = { + frequencyYear: number; + durationHour: number; + rainfall: number; +}; + +/** 확률관측소의 시군구 교차면적 비율 API 응답이다. */ +export type PrbbtRnflStation = { + prbbtObsvtrCd: string; + prbbtObsvtrNm: string; + areaRt: number; +}; + +/** 확률강우량 표와 그 계산에 사용한 관측소 비율을 함께 받는다. */ +export type PrbbtRnflResponse = { + sggCd: string; + rainfall: PrbbtRnflCell[]; + stations: PrbbtRnflStation[]; + areaRtTotal: number; +}; + +type PrbbtRnflDialogProps = { + open: boolean; + onOpenChange: (open: boolean) => void; + sggName: string; + selectedDurationHour: number | null; + data: PrbbtRnflResponse | null; + loading: boolean; + error: string | null; +}; + +const formatRainfall = (value: number) => + Number.isFinite(Number(value)) ? Number(value).toFixed(4) : "-"; + +const formatAreaRatio = (value: number) => + Number.isFinite(Number(value)) ? Number(value).toFixed(3) : "-"; + +/** 재현기간을 행, 강우지속시간을 열로 구성하고 목록에서 선택한 지속시간의 기준값을 강조한다. */ +export default function PrbbtRnflDialog({ + open, + onOpenChange, + sggName, + selectedDurationHour, + data, + loading, + error, +}: PrbbtRnflDialogProps) { + const { frequencies, durations, rainfallByKey } = useMemo(() => { + const rainfall = data?.rainfall ?? []; + return { + frequencies: [...new Set(rainfall.map((item) => item.frequencyYear))].sort( + (a, b) => a - b, + ), + durations: [...new Set(rainfall.map((item) => item.durationHour))].sort( + (a, b) => a - b, + ), + rainfallByKey: new Map( + rainfall.map((item) => [ + `${item.frequencyYear}:${item.durationHour}`, + item.rainfall, + ]), + ), + }; + }, [data]); + + return ( + + + + {sggName} 확률강우량 + + + {loading ? ( +

+ 확률강우량을 조회하고 있습니다. +

+ ) : error ? ( +

+ {error} +

+ ) : !frequencies.length || !durations.length ? ( +

+ 해당 시군구의 확률강우량 기준자료가 없습니다. +

+ ) : ( + <> +
+ + + + + + {durations.map((durationHour) => ( + + ))} + + + + {frequencies.map((frequencyYear) => ( + + + {durations.map((durationHour) => { + const rainfall = rainfallByKey.get( + `${frequencyYear}:${durationHour}`, + ); + return ( + + ); + })} + + ))} + +
+ {sggName} 재현기간 및 강우지속시간별 확률강우량(mm) +
+ 재현기간(년) + + {durationHour}시간 +
+ {frequencyYear} + + {rainfall == null ? "-" : formatRainfall(rainfall)} +
+
+ +
+

확률관측소 면적비율

+
+ + + + + + + + + + {(data?.stations ?? []).map((station) => ( + + + + + + ))} + + + + + +
+ 확률관측소코드 + + 관측소명 + + 면적비율(%) +
+ {station.prbbtObsvtrCd} + + {station.prbbtObsvtrNm || "-"} + + {formatAreaRatio(station.areaRt)} +
+ 합계 + + {formatAreaRatio(data?.areaRtTotal ?? Number.NaN)} +
+
+
+ + )} +

+ 시간은 강우가 지속된 시간입니다 +

+
+
+ ); +}