sn: 침수흔적깊이 정보 추가

This commit is contained in:
2026-08-28 16:08:47 +09:00
parent 36549e7281
commit be46bcbd17
3 changed files with 98 additions and 33 deletions
@@ -52,8 +52,7 @@ const resolveFldngHstryMapFeatureFid = (row: FldngHstryTableRow) => {
if (fid.startsWith(`${FLOOD_HISTORY_LAYER_TYPE}.`)) return fid; if (fid.startsWith(`${FLOOD_HISTORY_LAYER_TYPE}.`)) return fid;
if (fid) return `${FLOOD_HISTORY_LAYER_TYPE}.${fid}`; if (fid) return `${FLOOD_HISTORY_LAYER_TYPE}.${fid}`;
const mapFid = String(row.fldngMapFid ?? "").trim(); return "";
return mapFid || "";
}; };
const buildDownloadRows = (rows: FldngHstryTableRow[]) => const buildDownloadRows = (rows: FldngHstryTableRow[]) =>
@@ -123,6 +122,7 @@ export default function FldngHstryPopupPage() {
const fldngYearOptions = useMemo(() => buildFldngHstryYearOptions(), []); const fldngYearOptions = useMemo(() => buildFldngHstryYearOptions(), []);
const sggOptions = useMemo( const sggOptions = useMemo(
() => () =>
filters.ctpvCd === "0" filters.ctpvCd === "0"
@@ -295,7 +295,7 @@ export default function FldngHstryPopupPage() {
시도 시도
</span> </span>
<select <select
className="h-[3.2rem] w-[16rem] rounded-md border border-input bg-field px-3 text-[1.3rem]" className="h-[3.2rem] w-[12rem] rounded-md border border-input bg-field px-3 text-[1.3rem]"
value={filters.ctpvCd} value={filters.ctpvCd}
onChange={(event) => onChange={(event) =>
setFilters((current) => ({ setFilters((current) => ({
@@ -342,7 +342,7 @@ export default function FldngHstryPopupPage() {
침수발생년도 침수발생년도
</span> </span>
<select <select
className="h-[3.2rem] w-[14rem] rounded-md border border-input bg-field px-3 text-[1.3rem]" className="h-[3.2rem] w-[8rem] rounded-md border border-input bg-field px-3 text-[1.3rem]"
value={filters.floodYear} value={filters.floodYear}
onChange={(event) => onChange={(event) =>
setFilters((current) => ({ setFilters((current) => ({
@@ -360,6 +360,24 @@ export default function FldngHstryPopupPage() {
</select> </select>
</label> </label>
<label className="flex items-center gap-2">
<span className="whitespace-nowrap text-[1.3rem] text-label">강우시작일</span>
<Input
className="w-[11rem]"
type="date"
value={filters.rainStartDate}
min={filters.floodYear ? `${filters.floodYear}-01-01` : undefined}
max={filters.floodYear ? `${filters.floodYear}-12-31` : undefined}
disabled={!filters.floodYear}
onChange={(event) => setFilters((current) => ({ ...current, rainStartDate: event.target.value }))}
/>
</label>
<label className="flex items-center gap-2">
<span className="whitespace-nowrap text-[1.3rem] text-label">침수지구명</span>
<Input type="text" value={filters.districtName} onChange={(event) => setFilters((current) => ({ ...current, districtName: event.target.value }))} onKeyDown={(event) => { if (event.key === "Enter") void handleSearch(1); }} />
</label>
<label className="flex items-center gap-2"> <label className="flex items-center gap-2">
<span className="whitespace-nowrap text-[1.3rem] text-label"> <span className="whitespace-nowrap text-[1.3rem] text-label">
침수재해명 침수재해명
@@ -17,6 +17,8 @@ export type FldngHstryFilters = {
sggCd: string; sggCd: string;
floodYear: string; floodYear: string;
disasterName: string; disasterName: string;
rainStartDate: string;
districtName: string;
}; };
export type FldngHstryDepthZone = { export type FldngHstryDepthZone = {
@@ -108,6 +110,8 @@ export const FLDNG_HSTRY_DEFAULT_FILTERS: FldngHstryFilters = {
sggCd: "0", sggCd: "0",
floodYear: "", floodYear: "",
disasterName: "", disasterName: "",
rainStartDate: "",
districtName: "",
}; };
export const FLDNG_HSTRY_DEPTH_LEGEND_ITEMS = [ export const FLDNG_HSTRY_DEPTH_LEGEND_ITEMS = [
@@ -350,6 +354,8 @@ export const normalizeFldngHstryFilters = (
.replace(/[^0-9]/g, "") .replace(/[^0-9]/g, "")
.slice(0, 4), .slice(0, 4),
disasterName: String(filters.disasterName ?? "").trim(), disasterName: String(filters.disasterName ?? "").trim(),
rainStartDate: String(filters.rainStartDate ?? "").trim(),
districtName: String(filters.districtName ?? "").trim(),
}); });
export const buildFldngHstryYearOptions = () => { export const buildFldngHstryYearOptions = () => {
@@ -375,6 +381,8 @@ export const buildFldngHstryListQueryString = (
if (normalized.floodYear) params.set("fldngYr", normalized.floodYear); if (normalized.floodYear) params.set("fldngYr", normalized.floodYear);
if (normalized.disasterName) if (normalized.disasterName)
params.set("fldngDstrNm", normalized.disasterName); params.set("fldngDstrNm", normalized.disasterName);
if (normalized.rainStartDate) params.set("rainBgngTm", normalized.rainStartDate);
if (normalized.districtName) params.set("fldngDstrctNm", normalized.districtName);
return params.toString(); return params.toString();
}; };
@@ -64,6 +64,7 @@ import {
isInvalidInstallYmd, isInvalidInstallYmd,
} from "../agedPipe/agedPipeUtils"; } from "../agedPipe/agedPipeUtils";
import { openWindowPopup } from "@/features/windowPopup/lib/openWindowPopup"; import { openWindowPopup } from "@/features/windowPopup/lib/openWindowPopup";
import { WINDOW_POPUP_MESSAGE_TYPE } from "@/features/windowPopup/model/types";
import { useAlertStore } from "@/shared/lib"; import { useAlertStore } from "@/shared/lib";
import { useConfirm } from "@frontend-opensource/use-react-hooks"; import { useConfirm } from "@frontend-opensource/use-react-hooks";
import useStore from "@/store/useStore"; import useStore from "@/store/useStore";
@@ -777,6 +778,9 @@ const FeatureClickEventPopup = ({
} }
if (selectedLayerName === "침수흔적수위" && featureId) { if (selectedLayerName === "침수흔적수위" && featureId) {
const depthLookupKey = normalizeFldngWtlvMapApiFid(featureId);
setFloodDepthRows([]);
setSelectedFloodDepthYear(null);
try { try {
const normalizedFloodMapFid = const normalizedFloodMapFid =
normalizeFldngWtlvMapApiFid(featureId); normalizeFldngWtlvMapApiFid(featureId);
@@ -794,27 +798,27 @@ const FeatureClickEventPopup = ({
setFetchData(mergedData); setFetchData(mergedData);
setTableData(mergedData); setTableData(mergedData);
} }
const depthDataSn = Number( } catch (rainError) {
data?.data_sn ?? data?.dataSn ?? formattedData?.data_sn ?? formattedData?.dataSn, console.warn("침수강우분석 조회 실패:", rainError);
); }
if (Number.isFinite(depthDataSn)) { try {
if (depthLookupKey) {
const depthRows = await getData( const depthRows = await getData(
`v1/fldng/hstry/depth-map/${encodeURIComponent(String(depthDataSn))}`, `v1/fldng/hstry/depth-map/${encodeURIComponent(depthLookupKey)}`,
); );
const normalizedDepthRows = Array.isArray(depthRows) const depthPayload = depthRows?.data ?? depthRows;
? depthRows.filter((row) => row && typeof row === "object") const normalizedDepthRows = Array.isArray(depthPayload)
? depthPayload.filter((row) => row && typeof row === "object")
: []; : [];
setFloodDepthRows(normalizedDepthRows); setFloodDepthRows(normalizedDepthRows);
const years = normalizedDepthRows const years = normalizedDepthRows
.map((row) => String(row.fldng_yr ?? row.fldngYr ?? row.yr ?? "").trim()) .map((row) => String(row.fldng_yr ?? row.fldngYr ?? row.yr ?? "").trim())
.filter(Boolean); .filter(Boolean);
setSelectedFloodDepthYear(years[0] ?? null); // 최초에는 연도를 선택하지 않고 목록만 표시한다.
} else {
setFloodDepthRows([]);
setSelectedFloodDepthYear(null); setSelectedFloodDepthYear(null);
} }
} catch (rainError) { } catch (depthError) {
console.warn("침수강우분석 조회 실패:", rainError); console.warn("침수깊이지도 조회 실패:", depthError);
} }
} }
} catch (error) { } catch (error) {
@@ -1066,7 +1070,27 @@ const FeatureClickEventPopup = ({
<button <button
key={year} key={year}
type="button" type="button"
onClick={() => setSelectedFloodDepthYear(year)} onClick={() => {
setSelectedFloodDepthYear(year);
const selectedDepthRow = floodDepthRows.find(
(row) => String(row.fldng_yr ?? row.fldngYr ?? row.yr ?? "").trim() === year,
);
const depthFid = String(
selectedDepthRow?.fldng_dpth_map_sn ?? selectedDepthRow?.fid ?? "",
).trim();
if (depthFid) {
window.opener?.postMessage(
{
type: WINDOW_POPUP_MESSAGE_TYPE.MAP_SEARCH_SELECT_FEATURE,
payload: {
layerNm: "tb_ms_fldng_dpth_map",
fid: `tb_ms_fldng_dpth_map.${depthFid.replace(/^tb_ms_fldng_dpth_map\./, "")}`,
},
},
window.location.origin,
);
}
}}
className={`rounded-full border px-3 py-1 text-sm ${selectedFloodDepthYear === year ? "border-blue-600 bg-blue-600 text-white" : "border-blue-400 bg-transparent text-blue-600"}`} className={`rounded-full border px-3 py-1 text-sm ${selectedFloodDepthYear === year ? "border-blue-600 bg-blue-600 text-white" : "border-blue-400 bg-transparent text-blue-600"}`}
> >
{year} {year}
@@ -1075,22 +1099,37 @@ const FeatureClickEventPopup = ({
</div> </div>
<button type="button" aria-label="다음 연도" className="shrink-0 px-1 text-blue-600" onClick={() => floodDepthYearListRef.current?.scrollBy({ left: 120, behavior: "smooth" })}>›</button> <button type="button" aria-label="다음 연도" className="shrink-0 px-1 text-blue-600" onClick={() => floodDepthYearListRef.current?.scrollBy({ left: 120, behavior: "smooth" })}>›</button>
</div> </div>
<div className="max-h-64 overflow-auto"> {selectedFloodDepthYear && (
<table className="w-full border-collapse text-sm"> <>
<tbody> <div className="mb-2 flex items-center justify-between border-b border-border pb-2">
{Object.entries( <span className="text-sm font-semibold text-label">침수흔적깊이 정보</span>
floodDepthRows.find((row) => String(row.fldng_yr ?? row.fldngYr ?? row.yr ?? "").trim() === selectedFloodDepthYear) ?? {}, <button
) type="button"
.filter(([key]) => !key.toLowerCase().includes("geom")) aria-label="침수흔적깊이 정보 닫기"
.map(([key, value]) => ( className="rounded px-2 text-lg leading-none text-muted-foreground hover:bg-accent"
<tr key={key}> onClick={() => setSelectedFloodDepthYear(null)}
<th className="border border-border bg-secondary px-2 py-1 text-left">{key}</th> >
<td className="border border-border px-2 py-1">{String(value ?? "-")}</td> ×
</tr> </button>
))} </div>
</tbody> <div className="max-h-64 overflow-auto">
</table> <table className="w-full border-collapse text-sm">
</div> <tbody>
{Object.entries(
floodDepthRows.find((row) => String(row.fldng_yr ?? row.fldngYr ?? row.yr ?? "").trim() === selectedFloodDepthYear) ?? {},
)
.filter(([key]) => !key.toLowerCase().includes("geom"))
.map(([key, value]) => (
<tr key={key}>
<th className="border border-border bg-secondary px-2 py-1 text-left">{key}</th>
<td className="border border-border px-2 py-1">{String(value ?? "-")}</td>
</tr>
))}
</tbody>
</table>
</div>
</>
)}
</div> </div>
)} )}
{ContentComponent && ( {ContentComponent && (