From 4c7ff53d78e6bc3ec5268b6e0ace6a1a9bb424bf Mon Sep 17 00:00:00 2001 From: yangsein Date: Mon, 15 Jun 2026 00:43:03 +0900 Subject: [PATCH 01/12] =?UTF-8?q?feat:[=EC=A7=91=EA=B3=84=ED=91=9C]03.?= =?UTF-8?q?=ED=95=98=EC=88=98=EA=B4=80=EB=A1=9C=20=EC=9C=A0=EC=A7=80?= =?UTF-8?q?=EA=B4=80=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../statistics/total/SewagePipeMaintainTot.tsx | 9 +++++++-- .../statistics-sewage-pipe-maintain-total-interface.ts | 2 ++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/apps/st/src/components/statistics/total/SewagePipeMaintainTot.tsx b/apps/st/src/components/statistics/total/SewagePipeMaintainTot.tsx index a061146c5..b3dfe7a46 100644 --- a/apps/st/src/components/statistics/total/SewagePipeMaintainTot.tsx +++ b/apps/st/src/components/statistics/total/SewagePipeMaintainTot.tsx @@ -15,12 +15,17 @@ import type { SewagePipeMaintainTotalRequest } from "@/entities/statistics/model const firstTabKey = SewagePipeMaintainColumnListTot[0]?.tabKey ?? "ST00030001"; function buildSewagePipeMaintainTotSearchParam(values: SearchFormValues): SewagePipeMaintainTotalRequest { + const isSummaryEnabled = values.isSummary !== 'ALL'; + const summaryYearCnt = isSummaryEnabled ? Number(values.isSummary) : undefined; + return { ctpvNm: values.sido === "ALL" ? "" : values.sido, sggNm: values.gugun === "ALL" ? "" : values.gugun, sewerBzmnCd: values.bsnmCd, statsYr: values.year, - rowDataYn: values.isDetail, + rowDataYn: values.isDetail, + summaryYn: isSummaryEnabled, + ...(isSummaryEnabled ? { summaryYearCnt } : {}), }; } @@ -128,7 +133,7 @@ const SewagePipeMaintainTot = () => { return (
- + {isLoading && (
diff --git a/apps/st/src/entities/statistics/model/statistics-total/statistics-sewage-pipe-maintain-total-interface.ts b/apps/st/src/entities/statistics/model/statistics-total/statistics-sewage-pipe-maintain-total-interface.ts index 139320868..ca2c79b14 100644 --- a/apps/st/src/entities/statistics/model/statistics-total/statistics-sewage-pipe-maintain-total-interface.ts +++ b/apps/st/src/entities/statistics/model/statistics-total/statistics-sewage-pipe-maintain-total-interface.ts @@ -4,6 +4,8 @@ export interface SewagePipeMaintainTotalRequest { statsYr?: string; sewerBzmnCd?: string; rowDataYn?: boolean; + summaryYn?: boolean; + summaryYearCnt?: number; } export interface SewagePipeMaintainRepairTotalItem { From 71077f4c8e0428c6545db039b3eec3f776e7b815 Mon Sep 17 00:00:00 2001 From: yangsein Date: Mon, 15 Jun 2026 00:47:11 +0900 Subject: [PATCH 02/12] =?UTF-8?q?feat:[=EC=A7=91=EA=B3=84=ED=91=9C]04.?= =?UTF-8?q?=ED=95=98=EC=88=98=EC=A0=80=EB=A5=98=EC=8B=9C=EC=84=A4=20?= =?UTF-8?q?=EB=B0=8F=20=EB=B9=97=EB=AC=BC=20=ED=8E=8C=ED=94=84=EC=9E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../total/SewageReservoirPumpTot.tsx | 186 +++++++++++++++++- .../SewerPumpStationStatusColumn.ts | 96 +++++++++ .../SewerReservoirStatusColumn.ts | 101 ++++++++++ ...istics-sewage-reservoir-total-interface.ts | 114 +++++++++++ 4 files changed, 494 insertions(+), 3 deletions(-) create mode 100644 apps/st/src/components/util/column/total/sewerReservoir/SewerPumpStationStatusColumn.ts create mode 100644 apps/st/src/components/util/column/total/sewerReservoir/SewerReservoirStatusColumn.ts create mode 100644 apps/st/src/entities/statistics/model/statistics-total/statistics-sewage-reservoir-total-interface.ts diff --git a/apps/st/src/components/statistics/total/SewageReservoirPumpTot.tsx b/apps/st/src/components/statistics/total/SewageReservoirPumpTot.tsx index 6016a7930..479e7b4b0 100644 --- a/apps/st/src/components/statistics/total/SewageReservoirPumpTot.tsx +++ b/apps/st/src/components/statistics/total/SewageReservoirPumpTot.tsx @@ -1,10 +1,190 @@ +import { useRef, useState } from "react"; + +import { useAlertStore } from "@/shared/lib"; +import { LoadingWithAnimation, Tabs, TabsContent, TabsList, TabsTrigger } from "@common/components"; + import SearchForm from "@/components/module/SearchForm"; +import DataGrid from "@/components/module/DataGrid"; +import type { ColumnInfo } from "@/components/type/DataGrid.type"; +import type { SearchFormValues } from "@/components/type/SearchForm.type"; +import { toGridRows } from "@/components/util/grid/gridFunction"; + +// 분리된 개별 컬럼 구성 임포트 경로 유지 +import { ReservoirStatusColumnConfig } from "@/components/util/column/total/sewerReservoir/SewerReservoirStatusColumn"; +import { PumpStationStatusColumnConfig } from "@/components/util/column/total/sewerReservoir/SewerPumpStationStatusColumn"; + +// 수정 적용한 API 및 인터페이스 명세 호출 유지 +import { statisticsTotalStatusApi } from "@/entities/statistics/api/statistics-total-service"; +import type { + SewerReservoirMaintainTotalRequest +} from "@/entities/statistics/model/statistics-total/statistics-sewage-reservoir-total-interface"; +import CellColorInfo from "./CellColorInfo"; + +const SewerReservoirColumnListTot = [ + ReservoirStatusColumnConfig, + PumpStationStatusColumnConfig +]; + +const firstTabKey = SewerReservoirColumnListTot[0]?.tabKey ?? "ST00040001"; const SewageReservoirPumpTot = () => { + const { setMessage: alert } = useAlertStore((state) => state); + + // 상태 정의에서 null을 제외하여 DataGrid와 타입을 일치시킵니다. + const [rowDataMap, setRowDataMap] = useState[]>>(new Map()); + + const [isLoading, setIsLoading] = useState(false); + const [activeTab, setActiveTab] = useState(firstTabKey); + const lastSearchedValuesRef = useRef(null); + + const columnInfo = SewerReservoirColumnListTot; + + const handleReset = () => { + setRowDataMap(new Map()); + setActiveTab(firstTabKey); + lastSearchedValuesRef.current = null; + }; + + const loadGridData = async (tabKey: string, values: SearchFormValues): Promise => { + const param: SewerReservoirMaintainTotalRequest = { + ctpvNm: values.sido === "ALL" ? "" : values.sido, + sggNm: values.gugun === "ALL" ? "" : values.gugun, + sewerBzmnCd: values.bsnmCd, + statsYr: values.year, + rowDataYn: values.isDetail, + summaryYn: values.isSummary !== 'ALL', + summaryYearCnt: values.isSummary !== 'ALL' ? Number(values.isSummary) : undefined + }; + + try { + if (tabKey === "ST00040001") { + const response = await statisticsTotalStatusApi.getSewerReservoirStatusTotalList(param); + if (response && response.status === "success" && response.data) { + setRowDataMap((prev) => new Map(prev).set(tabKey, toGridRows(response.data as object[]))); + return true; + } + alert(response?.message || "데이터 조회 중 오류가 발생했습니다."); + } + + if (tabKey === "ST00040002") { + const response = await statisticsTotalStatusApi.getSewerPumpStationStatusTotalList(param); + if (response && response.status === "success" && response.data) { + setRowDataMap((prev) => new Map(prev).set(tabKey, toGridRows(response.data as object[]))); + return true; + } + alert(response?.message || "데이터 조회 중 오류가 발생했습니다."); + } + return false; + } catch { + alert("서버 연결 중 오류가 발생했습니다."); + return false; + } + }; + + const handleSearch = async (values: SearchFormValues) => { + try { + setIsLoading(true); + const ok = await loadGridData(activeTab, values); + if (ok) { + lastSearchedValuesRef.current = values; + } else { + lastSearchedValuesRef.current = null; + } + } catch { + alert("데이터 조회 중 오류가 발생했습니다."); + lastSearchedValuesRef.current = null; + } finally { + setIsLoading(false); + } + }; + + const handleActiveTabChange = async (tabKey: string) => { + setActiveTab(tabKey); + const values = lastSearchedValuesRef.current; + if (!values) return; + + const existing = rowDataMap.get(tabKey); + if (existing && existing.length > 0) return; + + try { + setIsLoading(true); + await loadGridData(tabKey, values); + } catch { + alert("데이터 조회 중 오류가 발생했습니다."); + } finally { + setIsLoading(false); + } + }; + return ( - <> - - +
+ + + {isLoading && ( +
+
+ +
+
+ )} + + + +
+ void handleActiveTabChange(v)} className="w-full" variant="default"> + + {columnInfo.map((info) => ( + + {info.tabLabel} + + ))} + + + {columnInfo.map((info) => { + const tabKey = info.tabKey; + const rowData = rowDataMap.get(tabKey) ?? []; + + const gridColumnInfo: ColumnInfo = { + columns: info.columns, + columnLayout: info.columnLayout, + }; + + return ( + + { + setRowDataMap((prev) => { + const next = new Map(prev); + const cur = next.get(tabKey) ?? []; + const n = + typeof updater === "function" ? updater(cur) : updater; + next.set(tabKey, n); + return next; + }); + }} + isExcelImport={false} + isExcelExport={true} + showHeaderCheckbox={false} + fixedColumns={info.fixedColumns} + enableSort={false} + cellStyleCallback={({ row }) => { + const rowType = String(row.rowType ?? ''); + if (rowType === 'DETAIL') return undefined; + if (rowType === 'BIZ_SUM') return 'rg-cell-rowtype-biz-sum'; + if (rowType === 'SUMMARY') return 'rg-cell-rowtype-summary'; + return 'rg-cell-rowtype-other'; + }} + /> + + ); + })} + +
+
); }; diff --git a/apps/st/src/components/util/column/total/sewerReservoir/SewerPumpStationStatusColumn.ts b/apps/st/src/components/util/column/total/sewerReservoir/SewerPumpStationStatusColumn.ts new file mode 100644 index 000000000..64ce85216 --- /dev/null +++ b/apps/st/src/components/util/column/total/sewerReservoir/SewerPumpStationStatusColumn.ts @@ -0,0 +1,96 @@ +import { ValueType } from "realgrid"; + +export const PumpStationStatusColumnConfig = { + tabKey: "ST00040002", + tabLabel: "빗물펌프장", + rowStartNum: 2, + fixedColumns: 2, + columns: [ + // 행정구역 그룹 소속 + { name: "ctpvNm", fieldName: "ctpvNm", header: { text: "시도" }, editable: false }, + { name: "sggNm", fieldName: "sggNm", header: { text: "시군구" }, editable: false }, + { name: "admdstNm", fieldName: "admdstNm", header: { text: "행정구역명" }, editable: false }, + + // 단일 컬럼들 + { name: "fcltNm", fieldName: "fcltNm", header: { text: "시설명" }, editable: false }, + { name: "fcltPstnCn", fieldName: "fcltPstnCn", header: { text: "위치" }, editable: false }, + { name: "fcltyKndCn", fieldName: "fcltyKndCn", header: { text: "시설물종류" }, editable: false }, + { name: "oprtngStrtYmd", fieldName: "oprtngStrtYmd", header: { text: "가동개시\n년월일\nyyyy-mm-dd" }, editable: false }, + { name: "planDesignFreqCnt", fieldName: "planDesignFreqCnt", header: { text: "계획설계빈도" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "wtrStrgZoneArea", fieldName: "wtrStrgZoneArea", header: { text: "집수구역면적(㎢)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.00", editable: false }, + + // 유수지 그룹 소속 + { name: "rsvrYn", fieldName: "rsvrYn", header: { text: "유수지유무\n(유,무)" }, editable: false }, + { name: "rsvrImpndQnt", fieldName: "rsvrImpndQnt", header: { text: "저류량(㎥)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + + // 흡수정 그룹 소속 + { name: "abstnRgnImdQnt", fieldName: "abstnRgnImdQnt", header: { text: "저류량(㎥)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + + // 단일 컬럼 + { name: "grssPpqty", fieldName: "grssPpqty", header: { text: "총양수량\n(㎥/min)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + + // 펌프시설 > 펌프시설1 그룹 소속 + { name: "pumpFcltN1DmtrExpln", fieldName: "pumpFcltN1DmtrExpln", header: { text: "구경(mm)" }, editable: false }, + { name: "pumpFcltN1PerPpqty", fieldName: "pumpFcltN1PerPpqty", header: { text: "대당 양수량\n(㎥/min·대)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "pumpFcltN1PumpCnt", fieldName: "pumpFcltN1PumpCnt", header: { text: "펌프대수" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "pumpFcltN1RsrvPumpCnt", fieldName: "pumpFcltN1RsrvPumpCnt", header: { text: "예비펌프대수" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + + // 펌프시설 > 펌프시설2 그룹 소속 + { name: "pumpFcltN2DmtrExpln", fieldName: "pumpFcltN2DmtrExpln", header: { text: "구경(mm)" }, editable: false }, + { name: "pumpFcltN2PerPpqty", fieldName: "pumpFcltN2PerPpqty", header: { text: "대당 양수량\n(㎥/min·대)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "pumpFcltN2PumpCnt", fieldName: "pumpFcltN2PumpCnt", header: { text: "펌프대수" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "pumpFcltN2RsrvPumpCnt", fieldName: "pumpFcltN2RsrvPumpCnt", header: { text: "예비펌프대수" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + + // 펌프시설 > 펌프시설3 그룹 소속 + { name: "pumpFcltN3DmtrExpln", fieldName: "pumpFcltN3DmtrExpln", header: { text: "구경(mm)" }, editable: false }, + { name: "pumpFcltN3PerPpqty", fieldName: "pumpFcltN3PerPpqty", header: { text: "대당 양수량\n(㎥/min·대)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "pumpFcltN3PumpCnt", fieldName: "pumpFcltN3PumpCnt", header: { text: "펌프대수" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "pumpFcltN3RsrvPumpCnt", fieldName: "pumpFcltN3RsrvPumpCnt", header: { text: "예비펌프대수" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + + // 부대시설 그룹 소속 + { name: "prcsgFcltYn", fieldName: "prcsgFcltYn", header: { text: "전처리\n시설\n유무\n(유/무)" }, editable: false }, + { name: "ddrztnEqpmYn", fieldName: "ddrztnEqpmYn", header: { text: "탈취설비\n유무\n(유/무)" }, editable: false }, + { name: "oslfEtgtYn", fieldName: "oslfEtgtYn", header: { text: "자가발전\n유무\n(유/무)" }, editable: false }, + + // 단일 컬럼 + { name: "dschgWtrsExpln", fieldName: "dschgWtrsExpln", header: { text: "방류수역" }, editable: false }, + + // 최종처분량(ton/년) 그룹 소속 + { name: "prcptnLastDspsQnt", fieldName: "prcptnLastDspsQnt", header: { text: "침사" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "ctmnLastDspsQnt", fieldName: "ctmnLastDspsQnt", header: { text: "협잡물" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + + // 최종처분방법 그룹 소속 + { name: "prcptnLastDspsMthdExpln", fieldName: "prcptnLastDspsMthdExpln", header: { text: "침사" }, editable: false }, + { name: "ctmnLastDspsMthdExpln", fieldName: "ctmnLastDspsMthdExpln", header: { text: "협잡물" }, editable: false }, + + // 운영실적 그룹 소속 + { name: "oprtngDayCnt", fieldName: "oprtngDayCnt", header: { text: "가동일수\n(일/년)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "grssMltvQnt", fieldName: "grssMltvQnt", header: { text: "총배수량(㎡/년)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false } + ], + columnLayout: [ + { name: "areaGroup", header: { text: "행정구역" }, items: ["ctpvNm", "sggNm", "admdstNm"] }, + "fcltNm", + "fcltPstnCn", + "fcltyKndCn", + "oprtngStrtYmd", + "planDesignFreqCnt", + "wtrStrgZoneArea", + { name: "rsvrGroup", header: { text: "유수지" }, items: ["rsvrYn", "rsvrImpndQnt"] }, + { name: "abstnRgnGroup", header: { text: "흡수정" }, items: ["abstnRgnImdQnt"] }, + { + name: "pumpFcltyGroup", + header: { text: "펌프시설" }, + items: [ + "grssPpqty", + { name: "pump1SubGroup", header: { text: "펌프시설1" }, items: ["pumpFcltN1DmtrExpln", "pumpFcltN1PerPpqty", "pumpFcltN1PumpCnt", "pumpFcltN1RsrvPumpCnt"] }, + { name: "pump2SubGroup", header: { text: "펌프시설2" }, items: ["pumpFcltN2DmtrExpln", "pumpFcltN2PerPpqty", "pumpFcltN2PumpCnt", "pumpFcltN2RsrvPumpCnt"] }, + { name: "pump3SubGroup", header: { text: "펌프시설3" }, items: ["pumpFcltN3DmtrExpln", "pumpFcltN3PerPpqty", "pumpFcltN3PumpCnt", "pumpFcltN3RsrvPumpCnt"] } + ] + }, + { name: "subFcltGroup", header: { text: "부대시설" }, items: ["prcsgFcltYn", "ddrztnEqpmYn", "oslfEtgtYn"] }, + "dschgWtrsExpln", + { name: "lastDspsGroup", header: { text: "최종처분량(ton/년)" }, items: ["prcptnLastDspsQnt", "ctmnLastDspsQnt"] }, + { name: "lastMthdGroup", header: { text: "최종처분방법" }, items: ["prcptnLastDspsMthdExpln", "ctmnLastDspsMthdExpln"] }, + { name: "oprtngGroup", header: { text: "운영실적" }, items: ["oprtngDayCnt", "grssMltvQnt"] } + ] +}; \ No newline at end of file diff --git a/apps/st/src/components/util/column/total/sewerReservoir/SewerReservoirStatusColumn.ts b/apps/st/src/components/util/column/total/sewerReservoir/SewerReservoirStatusColumn.ts new file mode 100644 index 000000000..311cb6867 --- /dev/null +++ b/apps/st/src/components/util/column/total/sewerReservoir/SewerReservoirStatusColumn.ts @@ -0,0 +1,101 @@ +import { ValueType } from "realgrid"; + +export const ReservoirStatusColumnConfig = { + tabKey: "ST00040001", + tabLabel: "하수저류시설", + rowStartNum: 2, + fixedColumns: 2, + columns: [ + // 행정구역 그룹 소속 + { name: "ctpvNm", fieldName: "ctpvNm", header: { text: "시도" }, editable: false }, + { name: "sggNm", fieldName: "sggNm", header: { text: "시군구" }, editable: false }, + { name: "admdstNm", fieldName: "admdstNm", header: { text: "행정구역명" }, editable: false }, + + // 단일 컬럼들 + { name: "fcltNm", fieldName: "fcltNm", header: { text: "시설명" }, editable: false }, + { name: "fcltPstnCn", fieldName: "fcltPstnCn", header: { text: "위치" }, editable: false }, + { name: "oprtngStrtYmd", fieldName: "oprtngStrtYmd", header: { text: "가동개시\n년월일\nyyyy-mm-dd" }, editable: false }, + { name: "planDesignFreqCnt", fieldName: "planDesignFreqCnt", header: { text: "계획설계빈도" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + + // 설치목적 그룹 소속 (O,X로 표기, 중복체크가능) + { name: "fldngPrvtnYn", fieldName: "fldngPrvtnYn", header: { text: "침수예방" }, editable: false }, + { name: "wqPlltnMtgtYn", fieldName: "wqPlltnMtgtYn", header: { text: "수질오염저감" }, editable: false }, + { name: "rtlztnUsgYn", fieldName: "rtlztnUsgYn", header: { text: "하수\n재이용\n용도" }, editable: false }, + + // 단일 컬럼들 + { name: "fcltStrctCn", fieldName: "fcltStrctCn", header: { text: "시설구조\n(일반지하식,\n지하터널식,\n기타)" }, editable: false }, + { name: "wtrStrgZoneArea", fieldName: "wtrStrgZoneArea", header: { text: "접수구역면적(㎢)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.00", editable: false }, + { name: "impdFcltCpct", fieldName: "impdFcltCpct", header: { text: "시설용량(㎥)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "grssPpqty", fieldName: "grssPpqty", header: { text: "총양수량\n(㎥/min)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + + // 펌프시설 > 펌프시설1 그룹 소속 + { name: "pumpFcltN1DmtrExpln", fieldName: "pumpFcltN1DmtrExpln", header: { text: "구경(mm)" }, editable: false }, + { name: "pumpFcltN1PerPpqty", fieldName: "pumpFcltN1PerPpqty", header: { text: "대당 양수량\n(㎥/min·대)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "pumpFcltN1PumpCnt", fieldName: "pumpFcltN1PumpCnt", header: { text: "펌프대수" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "pumpFcltN1RsrvPumpCnt", fieldName: "pumpFcltN1RsrvPumpCnt", header: { text: "예비펌프대수" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + + // 펌프시설 > 펌프시설2 그룹 소속 + { name: "pumpFcltN2DmtrExpln", fieldName: "pumpFcltN2DmtrExpln", header: { text: "구경(mm)" }, editable: false }, + { name: "pumpFcltN2PerPpqty", fieldName: "pumpFcltN2PerPpqty", header: { text: "대당 양수량\n(㎥/min·대)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "pumpFcltN2PumpCnt", fieldName: "pumpFcltN2PumpCnt", header: { text: "펌프대수" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "pumpFcltN2RsrvPumpCnt", fieldName: "pumpFcltN2RsrvPumpCnt", header: { text: "예비펌프대수" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + + // 펌프시설 > 펌프시설3 그룹 소속 + { name: "pumpFcltN3DmtrExpln", fieldName: "pumpFcltN3DmtrExpln", header: { text: "구경(mm)" }, editable: false }, + { name: "pumpFcltN3PerPpqty", fieldName: "pumpFcltN3PerPpqty", header: { text: "대당 양수량\n(㎥/min·대)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "pumpFcltN3PumpCnt", fieldName: "pumpFcltN3PumpCnt", header: { text: "펌프대수" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "pumpFcltN3RsrvPumpCnt", fieldName: "pumpFcltN3RsrvPumpCnt", header: { text: "예비펌프대수" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + + // 부대시설 그룹 소속 + { name: "prcsgFcltYn", fieldName: "prcsgFcltYn", header: { text: "전처리\n시설\n유무\n(유/무)" }, editable: false }, + { name: "ddrztnEqpmYn", fieldName: "ddrztnEqpmYn", header: { text: "탈취설비\n유무\n(유/무)" }, editable: false }, + { name: "oslfEtgtYn", fieldName: "oslfEtgtYn", header: { text: "자가발전\n유무\n(유/무)" }, editable: false }, + + // 단일 컬럼들 + { name: "mltvMthCn", fieldName: "mltvMthCn", header: { text: "배수방식\n(자연배수,\n강제배수)" }, editable: false }, + { name: "impdMthCn", fieldName: "impdMthCn", header: { text: "저류방식\n(직렬,병렬)" }, editable: false }, + { name: "impdPrcsMthdCn", fieldName: "impdPrcsMthdCn", header: { text: "저류수\n처리방법\n(자체처리,\n연계처리)" }, editable: false }, + { name: "dschgWtrsExpln", fieldName: "dschgWtrsExpln", header: { text: "방류수역\n(또는\n연계처리장명)" }, editable: false }, + + // 최종처분량(ton/년) 그룹 소속 + { name: "prcptnLastDspsQnt", fieldName: "prcptnLastDspsQnt", header: { text: "침사" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "ctmnLastDspsQnt", fieldName: "ctmnLastDspsQnt", header: { text: "협잡물" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + + // 최종처분방법 그룹 소속 + { name: "prcptnLastDspsMthdExpln", fieldName: "prcptnLastDspsMthdExpln", header: { text: "침사" }, editable: false }, + { name: "ctmnLastDspsMthdExpln", fieldName: "ctmnLastDspsMthdExpln", header: { text: "협잡물" }, editable: false }, + + // 운영실적 그룹 소속 + { name: "oprtngDayCnt", fieldName: "oprtngDayCnt", header: { text: "가동일수\n(일/년)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "grssMltvQnt", fieldName: "grssMltvQnt", header: { text: "총배수량(㎡/년)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false } + ], + columnLayout: [ + { name: "areaGroup", header: { text: "행정구역" }, items: ["ctpvNm", "sggNm", "admdstNm"] }, + "fcltNm", + "fcltPstnCn", + "oprtngStrtYmd", + "planDesignFreqCnt", + { name: "purposeGroup", header: { text: "설치목적(O,X로 표기, 중복체크가능)" }, items: ["fldngPrvtnYn", "wqPlltnMtgtYn", "rtlztnUsgYn"] }, + "fcltStrctCn", + "wtrStrgZoneArea", + "impdFcltCpct", + { + name: "pumpFcltyGroup", + header: { text: "펌프시설" }, + items: [ + "grssPpqty", + { name: "pump1SubGroup", header: { text: "펌프시설1" }, items: ["pumpFcltN1DmtrExpln", "pumpFcltN1PerPpqty", "pumpFcltN1PumpCnt", "pumpFcltN1RsrvPumpCnt"] }, + { name: "pump2SubGroup", header: { text: "펌프시설2" }, items: ["pumpFcltN2DmtrExpln", "pumpFcltN2PerPpqty", "pumpFcltN2PumpCnt", "pumpFcltN2RsrvPumpCnt"] }, + { name: "pump3SubGroup", header: { text: "펌프시설3" }, items: ["pumpFcltN3DmtrExpln", "pumpFcltN3PerPpqty", "pumpFcltN3PumpCnt", "pumpFcltN3RsrvPumpCnt"] } + ] + }, + { name: "subFcltGroup", header: { text: "부대시설" }, items: ["prcsgFcltYn", "ddrztnEqpmYn", "oslfEtgtYn"] }, + "mltvMthCn", + "impdMthCn", + "impdPrcsMthdCn", + "dschgWtrsExpln", + { name: "lastDspsGroup", header: { text: "최종처분량(ton/년)" }, items: ["prcptnLastDspsQnt", "ctmnLastDspsQnt"] }, + { name: "lastMthdGroup", header: { text: "최종처분방법" }, items: ["prcptnLastDspsMthdExpln", "ctmnLastDspsMthdExpln"] }, + { name: "oprtngGroup", header: { text: "운영실적" }, items: ["oprtngDayCnt", "grssMltvQnt"] } + ] +}; \ No newline at end of file diff --git a/apps/st/src/entities/statistics/model/statistics-total/statistics-sewage-reservoir-total-interface.ts b/apps/st/src/entities/statistics/model/statistics-total/statistics-sewage-reservoir-total-interface.ts new file mode 100644 index 000000000..782875e34 --- /dev/null +++ b/apps/st/src/entities/statistics/model/statistics-total/statistics-sewage-reservoir-total-interface.ts @@ -0,0 +1,114 @@ +export interface SewerReservoirMaintainTotalRequest { + ctpvNm?: string; + sggNm?: string; + statsYr?: string; + sewerBzmnCd?: string; + rowDataYn?: boolean; + summaryYn?: boolean; + summaryYearCnt?: number; +} + +/** [Tab1] `GET .../reservoir-status` 목록 항목 */ +export interface SewerReservoirStatusTotalItem { + statsYr: string; + ctpvNm: string; + sggNm: string | null; + admdstNm: string; + admdstCd: string; + sewerBzmnCd: string | null; + fcltNm: string; + fcltPstnCn: string; + rowType: string; + oprtngStrtYmd: string; + planDesignFreqCnt: number; + fldngPrvtnYn: string; + wqPlltnMtgtYn: string; + rtlztnUsgYn: string; + fcltStrctCn: string; + wtrStrgZoneArea: number; + impdFcltCpct: number; + grssPpqty: number; + pumpFcltN1DmtrExpln: string; + pumpFcltN1PerPpqty: number; + pumpFcltN1PumpCnt: number; + pumpFcltN1RsrvPumpCnt: number; + pumpFcltN2DmtrExpln: string; + pumpFcltN2PerPpqty: number; + pumpFcltN2PumpCnt: number; + pumpFcltN2RsrvPumpCnt: number; + pumpFcltN3DmtrExpln: string; + pumpFcltN3PerPpqty: number; + pumpFcltN3PumpCnt: number; + pumpFcltN3RsrvPumpCnt: number; + prcsgFcltYn: string; + ddrztnEqpmYn: string; + oslfEtgtYn: string; + mltvMthCn: string; + impdMthCn: string; + impdPrcsMthdCn: string; + dschgWtrsExpln: string; + prcptnLastDspsQnt: number; + ctmnLastDspsQnt: number; + prcptnLastDspsMthdExpln: string; + ctmnLastDspsMthdExpln: string; + oprtngDayCnt: number; + grssMltvQnt: number; + rmrk: string | null; +} + +/** [Tab1] `GET .../reservoir-status` 응답 */ +export interface SewerReservoirStatusTotalResponse { + status: string; + message?: string; + data: SewerReservoirStatusTotalItem[]; +} + +/** [Tab2] `GET .../pump-station-status` 목록 항목 */ +export interface SewerPumpStationStatusTotalItem { + statsYr: string; + ctpvNm: string; + sggNm: string | null; + admdstNm: string; + admdstCd: string; + sewerBzmnCd: string | null; + rowType: string; + fcltNm: string; + fcltPstnCn: string; + fcltyKndCn: string; + oprtngStrtYmd: string; + planDesignFreqCnt: number; + wtrStrgZoneArea: number; + rsvrYn: string; + rsvrImpndQnt: number; + abstnRgnImdQnt: number; + grssPpqty: number; + pumpFcltN1DmtrExpln: string; + pumpFcltN1PerPpqty: number; + pumpFcltN1PumpCnt: number; + pumpFcltN1RsrvPumpCnt: number; + pumpFcltN2DmtrExpln: string; + pumpFcltN2PerPpqty: number; + pumpFcltN2PumpCnt: number; + pumpFcltN2RsrvPumpCnt: number; + pumpFcltN3DmtrExpln: string; + pumpFcltN3PerPpqty: number; + pumpFcltN3PumpCnt: number; + pumpFcltN3RsrvPumpCnt: number; + prcsgFcltYn: string; + ddrztnEqpmYn: string; + oslfEtgtYn: string; + dschgWtrsExpln: string; + prcptnLastDspsQnt: number; + ctmnLastDspsQnt: number; + prcptnLastDspsMthdExpln: string; + ctmnLastDspsMthdExpln: string; + oprtngDayCnt: number; + grssMltvQnt: number; +} + +/** [Tab2] `GET .../pump-station-status` 응답 */ +export interface SewerPumpStationStatusTotalResponse { + status: string; + message?: string; + data: SewerPumpStationStatusTotalItem[]; +} \ No newline at end of file From 85ba9fe0ab13873805b53f8956f02d5fe9c5b40a Mon Sep 17 00:00:00 2001 From: yangsein Date: Mon, 15 Jun 2026 00:48:45 +0900 Subject: [PATCH 03/12] =?UTF-8?q?feat:[=EC=A7=91=EA=B3=84=ED=91=9C]05=20?= =?UTF-8?q?=ED=8E=8C=ED=94=84=EC=9E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../statistics/total/PumpingStationTot.tsx | 119 +++++++++++++++++- .../total/SewerMediationPumpStatusColumn.ts | 103 +++++++++++++++ ...atistics-mediation-pump-total-interface.ts | 67 ++++++++++ 3 files changed, 286 insertions(+), 3 deletions(-) create mode 100644 apps/st/src/components/util/column/total/SewerMediationPumpStatusColumn.ts create mode 100644 apps/st/src/entities/statistics/model/statistics-total/statistics-mediation-pump-total-interface.ts diff --git a/apps/st/src/components/statistics/total/PumpingStationTot.tsx b/apps/st/src/components/statistics/total/PumpingStationTot.tsx index fd4cc679d..b3da0a044 100644 --- a/apps/st/src/components/statistics/total/PumpingStationTot.tsx +++ b/apps/st/src/components/statistics/total/PumpingStationTot.tsx @@ -1,10 +1,123 @@ +import { useRef, useState } from "react"; + +import { useAlertStore } from "@/shared/lib"; +import { LoadingWithAnimation } from "@common/components"; + import SearchForm from "@/components/module/SearchForm"; +import DataGrid from "@/components/module/DataGrid"; +import type { ColumnInfo } from "@/components/type/DataGrid.type"; +import type { SearchFormValues } from "@/components/type/SearchForm.type"; +import { toGridRows } from "@/components/util/grid/gridFunction"; + +// 분리된 중계펌프장용 컬럼 구성 임포트 +import { MediationPumpStatusColumnConfig } from "@/components/util/column/total/SewerMediationPumpStatusColumn"; + +// 집계표(TOTAL) API 및 인터페이스 명세 호출 +import { statisticsTotalStatusApi } from "@/entities/statistics/api/statistics-total-service"; +import type { + MediationPumpMaintainTotalRequest +} from "@/entities/statistics/model/statistics-total/statistics-mediation-pump-total-interface"; +import CellColorInfo from "./CellColorInfo"; const PumpingStationTot = () => { + const { setMessage: alert } = useAlertStore((state) => state); + + // null 타입 컴파일 에러가 발생하지 않도록 엄격하게 정의한 데이터 상태 + const [rowData, setRowData] = useState[]>([]); + const [isLoading, setIsLoading] = useState(false); + const lastSearchedValuesRef = useRef(null); + + const config = MediationPumpStatusColumnConfig; + + const handleReset = () => { + setRowData([]); + lastSearchedValuesRef.current = null; + }; + + const loadGridData = async (values: SearchFormValues): Promise => { + const param: MediationPumpMaintainTotalRequest = { + ctpvNm: values.sido === "ALL" ? "" : values.sido, + sggNm: values.gugun === "ALL" ? "" : values.gugun, + sewerBzmnCd: values.bsnmCd, + statsYr: values.year, + rowDataYn: values.isDetail, + summaryYn: values.isSummary !== 'ALL', + summaryYearCnt: values.isSummary !== 'ALL' ? Number(values.isSummary) : undefined + }; + + try { + const response = await statisticsTotalStatusApi.getMediationPumpStationStatusTotalList(param); + if (response && response.status === "success" && response.data) { + // object[]로 변환하여 주입함으로써 null 에러 전면 방지 + setRowData(toGridRows(response.data as object[])); + return true; + } + alert(response?.message || "데이터 조회 중 오류가 발생했습니다."); + return false; + } catch { + alert("서버 연결 중 오류가 발생했습니다."); + return false; + } + }; + + const handleSearch = async (values: SearchFormValues) => { + try { + setIsLoading(true); + const ok = await loadGridData(values); + if (ok) { + lastSearchedValuesRef.current = values; + } else { + lastSearchedValuesRef.current = null; + } + } catch { + alert("데이터 조회 중 오류가 발생했습니다."); + lastSearchedValuesRef.current = null; + } finally { + setIsLoading(false); + } + }; + + const gridColumnInfo: ColumnInfo = { + columns: config.columns, + columnLayout: config.columnLayout, + }; + return ( - <> - - +
+ + + {isLoading && ( +
+
+ +
+
+ )} + + + +
+ { + const rowType = String(row.rowType ?? ''); + if (rowType === 'DETAIL') return undefined; + if (rowType === 'BIZ_SUM') return 'rg-cell-rowtype-biz-sum'; + if (rowType === 'SUMMARY') return 'rg-cell-rowtype-summary'; + return 'rg-cell-rowtype-other'; + }} + /> +
+
); }; diff --git a/apps/st/src/components/util/column/total/SewerMediationPumpStatusColumn.ts b/apps/st/src/components/util/column/total/SewerMediationPumpStatusColumn.ts new file mode 100644 index 000000000..66451c04b --- /dev/null +++ b/apps/st/src/components/util/column/total/SewerMediationPumpStatusColumn.ts @@ -0,0 +1,103 @@ +import { ValueType } from "realgrid"; + +export const MediationPumpStatusColumnConfig = { + tabKey: "ST00050001", + tabLabel: "중계펌프장", + rowStartNum: 2, + fixedColumns: 3, + columns: [ + // 행정구역 그룹 소속 + { name: "ctpvNm", fieldName: "ctpvNm", header: { text: "시도" }, editable: false }, + { name: "sggNm", fieldName: "sggNm", header: { text: "시군구" }, editable: false }, + { name: "admdstNm", fieldName: "admdstNm", header: { text: "행정구역명" }, editable: false }, + + // 단일 컬럼들 + { name: "fcltNm", fieldName: "fcltNm", header: { text: "펌프장명" }, editable: false }, + { name: "pumpSeNm", fieldName: "pumpSeNm", header: { text: "구분\n(합류식,\n분류식)" }, editable: false }, + { name: "swtrmtFcltNm", fieldName: "swtrmtFcltNm", header: { text: "처리장명" }, editable: false }, + { name: "fcltPstnCn", fieldName: "fcltPstnCn", header: { text: "위치" }, editable: false }, + { name: "oprtngStrtYmd", fieldName: "oprtngStrtYmd", header: { text: "가동개시\n년월일\nyyyy-mm-dd" }, editable: false }, + { name: "planMltvArea", fieldName: "planMltvArea", header: { text: "계획배수면적(㏊)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + + // 계획시간대최대배수량(㎥/일) 그룹 소속 + { name: "sndyArea", fieldName: "sndyArea", header: { text: "청천시" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "rainArea", fieldName: "rainArea", header: { text: "우천시" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "exlnQnt", fieldName: "exlnQnt", header: { text: "우수량" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + + // 오수펌프 그룹 내부 소속 + { name: "drtwPumpGrssPpqty", fieldName: "drtwPumpGrssPpqty", header: { text: "총양수량\n(㎥/min)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + + // 오수펌프 > 오수펌프1 그룹 소속 + { name: "drtwPumpN1DmtrArea", fieldName: "drtwPumpN1DmtrArea", header: { text: "구경(mm)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "drtwPumpN1Ppqty", fieldName: "drtwPumpN1Ppqty", header: { text: "양수량\n(㎥/min)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "drtwPumpN1Cnt", fieldName: "drtwPumpN1Cnt", header: { text: "펌프대수" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "drtwPumpN1RsrvCnt", fieldName: "drtwPumpN1RsrvCnt", header: { text: "예비대수" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + + // 오수펌프 > 오수펌프2 그룹 소속 + { name: "drtwPumpN2DmtrArea", fieldName: "drtwPumpN2DmtrArea", header: { text: "구경(mm)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "drtwPumpN2Ppqty", fieldName: "drtwPumpN2Ppqty", header: { text: "양수량\n(㎥/min)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "drtwPumpN2Cnt", fieldName: "drtwPumpN2Cnt", header: { text: "펌프대수" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "drtwPumpN2RsrvCnt", fieldName: "drtwPumpN2RsrvCnt", header: { text: "예비대수" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + + // 오수펌프 > 오수펌프3 그룹 소속 + { name: "drtwPumpN3DmtrArea", fieldName: "drtwPumpN3DmtrArea", header: { text: "구경(mm)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "drtwPumpN3Ppqty", fieldName: "drtwPumpN3Ppqty", header: { text: "양수량\n(㎥/min)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "drtwPumpN3Cnt", fieldName: "drtwPumpN3Cnt", header: { text: "펌프대수" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "drtwPumpN3RsrvCnt", fieldName: "drtwPumpN3RsrvCnt", header: { text: "예비대수" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + + // 우수펌프 그룹 내부 소속 + { name: "exlnPumpGrssPpqty", fieldName: "exlnPumpGrssPpqty", header: { text: "총양수량\n(㎥/min)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + + // 우수펌프 > 우수펌프1 그룹 소속 + { name: "exlnPumpN1DmtrArea", fieldName: "exlnPumpN1DmtrArea", header: { text: "구경(mm)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "exlnPumpN1Ppqty", fieldName: "exlnPumpN1Ppqty", header: { text: "양수량\n(㎥/min)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "exlnPumpN1Cnt", fieldName: "exlnPumpN1Cnt", header: { text: "펌프대수" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "exlnPumpN1RsrvCnt", fieldName: "exlnPumpN1RsrvCnt", header: { text: "예비대수" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + + // 우수펌프 > 우수펌프2 그룹 소속 + { name: "exlnPumpN2DmtrArea", fieldName: "exlnPumpN2DmtrArea", header: { text: "구경(mm)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "exlnPumpN2Ppqty", fieldName: "exlnPumpN2Ppqty", header: { text: "양수량\n(㎥/min)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "exlnPumpN2Cnt", fieldName: "exlnPumpN2Cnt", header: { text: "펌프대수" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "exlnPumpN2RsrvCnt", fieldName: "exlnPumpN2RsrvCnt", header: { text: "예비대수" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + + // 우수펌프 > 우수펌프3 그룹 소속 + { name: "exlnPumpN3DmtrArea", fieldName: "exlnPumpN3DmtrArea", header: { text: "구경(mm)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "exlnPumpN3Ppqty", fieldName: "exlnPumpN3Ppqty", header: { text: "양수량\n(㎥/min)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "exlnPumpN3Cnt", fieldName: "exlnPumpN3Cnt", header: { text: "펌프대수" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "exlnPumpN3RsrvCnt", fieldName: "exlnPumpN3RsrvCnt", header: { text: "예비대수" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + + // 단일 컬럼 + { name: "anlElpwrUseQnt", fieldName: "anlElpwrUseQnt", header: { text: "연간전력\n사용량\n(㎾h/년)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false } + ], + columnLayout: [ + { name: "areaGroup", header: { text: "행정구역" }, items: ["ctpvNm", "sggNm", "admdstNm"] }, + "swtrmtFcltNm", + "fcltNm", + "pumpSeNm", + "fcltPstnCn", + "oprtngStrtYmd", + "planMltvArea", + { name: "planDrainGroup", header: { text: "계획시간대최대배수량(㎥/일)" }, items: ["sndyArea", "rainArea", "exlnQnt"] }, + { + name: "drtwPumpGroup", + header: { text: "오수펌프" }, + items: [ + "drtwPumpGrssPpqty", + { name: "drtwPumpN1Group", header: { text: "오수펌프(1)" }, items: ["drtwPumpN1DmtrArea", "drtwPumpN1Ppqty", "drtwPumpN1Cnt", "drtwPumpN1RsrvCnt"] }, + { name: "drtwPumpN2Group", header: { text: "오수펌프(2)" }, items: ["drtwPumpN2DmtrArea", "drtwPumpN2Ppqty", "drtwPumpN2Cnt", "drtwPumpN2RsrvCnt"] }, + { name: "drtwPumpN3Group", header: { text: "오수펌프(3)" }, items: ["drtwPumpN3DmtrArea", "drtwPumpN3Ppqty", "drtwPumpN3Cnt", "drtwPumpN3RsrvCnt"] } + ] + }, + { + name: "exlnPumpGroup", + header: { text: "우수펌프" }, + items: [ + "exlnPumpGrssPpqty", + { name: "exlnPumpN1Group", header: { text: "우수펌프(1)" }, items: ["exlnPumpN1DmtrArea", "exlnPumpN1Ppqty", "exlnPumpN1Cnt", "exlnPumpN1RsrvCnt"] }, + { name: "exlnPumpN2Group", header: { text: "우수펌프(2)" }, items: ["exlnPumpN2DmtrArea", "exlnPumpN2Ppqty", "exlnPumpN2Cnt", "exlnPumpN2RsrvCnt"] }, + { name: "exlnPumpN3Group", header: { text: "우수펌프(3)" }, items: ["exlnPumpN3DmtrArea", "exlnPumpN3Ppqty", "exlnPumpN3Cnt", "exlnPumpN3RsrvCnt"] } + ] + }, + "anlElpwrUseQnt" + ] +}; \ No newline at end of file diff --git a/apps/st/src/entities/statistics/model/statistics-total/statistics-mediation-pump-total-interface.ts b/apps/st/src/entities/statistics/model/statistics-total/statistics-mediation-pump-total-interface.ts new file mode 100644 index 000000000..32c6cb5d3 --- /dev/null +++ b/apps/st/src/entities/statistics/model/statistics-total/statistics-mediation-pump-total-interface.ts @@ -0,0 +1,67 @@ +/** 05. 중계펌프장 현황 검색 조건 파라미터 */ +export interface MediationPumpMaintainTotalRequest { + ctpvNm?: string; + sggNm?: string; + statsYr?: string; + pumpstNm?: string; + sewerBzmnCd?: string; + rowDataYn?: boolean; + summaryYn?: boolean; + summaryYearCnt?: number; +} + +/** 중계펌프장 현황 목록 데이터 단위 */ +export interface MediationPumpStatusTotalItem { + ctpvNm: string; + sggNm: string | null; + statsYr: string; + admdstNm: string; + admdstCd: string; + rowType: string; + fcltNm: string; + pumpSeNm: string; + swtrmtFcltNm: string; + fcltPstnCn: string; + oprtngStrtYmd: string; + planMltvArea: number; + sndyArea: number; + rainArea: number; + exlnQnt: number; + // 오수펌프 + drtwPumpGrssPpqty: number; + drtwPumpN1DmtrArea: number; + drtwPumpN1Ppqty: number; + drtwPumpN1Cnt: number; + drtwPumpN1RsrvCnt: number; + drtwPumpN2DmtrArea: number; + drtwPumpN2Ppqty: number; + drtwPumpN2Cnt: number; + drtwPumpN2RsrvCnt: number; + drtwPumpN3DmtrArea: number; + drtwPumpN3Ppqty: number; + drtwPumpN3Cnt: number; + drtwPumpN3RsrvCnt: number; + // 우수펌프 + exlnPumpGrssPpqty: number; + exlnPumpN1DmtrArea: number; + exlnPumpN1Ppqty: number; + exlnPumpN1Cnt: number; + exlnPumpN1RsrvCnt: number; + exlnPumpN2DmtrArea: number; + exlnPumpN2Ppqty: number; + exlnPumpN2Cnt: number; + exlnPumpN2RsrvCnt: number; + exlnPumpN3DmtrArea: number; + exlnPumpN3Ppqty: number; + exlnPumpN3Cnt: number; + exlnPumpN3RsrvCnt: number; + // 전력 + anlElpwrUseQnt: number; +} + +/** 중계펌프장 API 최종 응답 규격 */ +export interface MediationPumpStatusTotalResponse { + status: string; + message?: string; + data: MediationPumpStatusTotalItem[]; +} \ No newline at end of file From 3c701359fde88e3737506e618e13434b4ccb6fd5 Mon Sep 17 00:00:00 2001 From: yangsein Date: Mon, 15 Jun 2026 00:50:54 +0900 Subject: [PATCH 04/12] =?UTF-8?q?feat:[=EC=A7=91=EA=B3=84=ED=91=9C]?= =?UTF-8?q?=EB=B6=84=EB=87=A8=EC=B2=98=EB=A6=AC=EC=8B=9C=EC=84=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../total/ExcretaTreatmentFacilityTot.tsx | 119 +++++++++++++++++- .../SewerExcretaTreatmentStatusColumn.ts | 63 ++++++++++ ...stics-excreta-treatment-total-interface.ts | 62 +++++++++ 3 files changed, 241 insertions(+), 3 deletions(-) create mode 100644 apps/st/src/components/util/column/total/SewerExcretaTreatmentStatusColumn.ts create mode 100644 apps/st/src/entities/statistics/model/statistics-total/statistics-excreta-treatment-total-interface.ts diff --git a/apps/st/src/components/statistics/total/ExcretaTreatmentFacilityTot.tsx b/apps/st/src/components/statistics/total/ExcretaTreatmentFacilityTot.tsx index e470b07fa..a5c7f2dae 100644 --- a/apps/st/src/components/statistics/total/ExcretaTreatmentFacilityTot.tsx +++ b/apps/st/src/components/statistics/total/ExcretaTreatmentFacilityTot.tsx @@ -1,10 +1,123 @@ +import { useRef, useState } from "react"; + +import { useAlertStore } from "@/shared/lib"; +import { LoadingWithAnimation } from "@common/components"; + import SearchForm from "@/components/module/SearchForm"; +import DataGrid from "@/components/module/DataGrid"; +import type { ColumnInfo } from "@/components/type/DataGrid.type"; +import type { SearchFormValues } from "@/components/type/SearchForm.type"; +import { toGridRows } from "@/components/util/grid/gridFunction"; + +// 분리된 분뇨처리시설용 컬럼 구성 임포트 +import { ExcretaTreatmentStatusColumnConfig } from "@/components/util/column/total/SewerExcretaTreatmentStatusColumn"; + +// 집계표(TOTAL) API 및 인터페이스 명세 호출 +import { statisticsTotalStatusApi } from "@/entities/statistics/api/statistics-total-service"; +import type { + ExcretaTreatmentMaintainTotalRequest +} from "@/entities/statistics/model/statistics-total/statistics-excreta-treatment-total-interface"; +import CellColorInfo from "./CellColorInfo"; const ExcretaTreatmentFacilityTot = () => { + const { setMessage: alert } = useAlertStore((state) => state); + + // null 타입 컴파일 에러를 방지하기 위해 엄격하게 정의한 데이터 상태 + const [rowData, setRowData] = useState[]>([]); + const [isLoading, setIsLoading] = useState(false); + const lastSearchedValuesRef = useRef(null); + + const config = ExcretaTreatmentStatusColumnConfig; + + const handleReset = () => { + setRowData([]); + lastSearchedValuesRef.current = null; + }; + + const loadGridData = async (values: SearchFormValues): Promise => { + const param: ExcretaTreatmentMaintainTotalRequest = { + ctpvNm: values.sido === "ALL" ? "" : values.sido, + sggNm: values.gugun === "ALL" ? "" : values.gugun, + sewerBzmnCd: values.bsnmCd, + statsYr: values.year, + rowDataYn: values.isDetail, + summaryYn: values.isSummary !== 'ALL', + summaryYearCnt: values.isSummary !== 'ALL' ? Number(values.isSummary) : undefined + }; + + try { + const response = await statisticsTotalStatusApi.getExcretaTreatmentStatusTotalList(param); + if (response && response.status === "success" && response.data) { + // object[]로 변환하여 주입함으로써 상위 컴포넌트 간 null 에러 완벽 차단 + setRowData(toGridRows(response.data as object[])); + return true; + } + alert(response?.message || "데이터 조회 중 오류가 발생했습니다."); + return false; + } catch { + alert("서버 연결 중 오류가 발생했습니다."); + return false; + } + }; + + const handleSearch = async (values: SearchFormValues) => { + try { + setIsLoading(true); + const ok = await loadGridData(values); + if (ok) { + lastSearchedValuesRef.current = values; + } else { + lastSearchedValuesRef.current = null; + } + } catch { + alert("데이터 조회 중 오류가 발생했습니다."); + lastSearchedValuesRef.current = null; + } finally { + setIsLoading(false); + } + }; + + const gridColumnInfo: ColumnInfo = { + columns: config.columns, + columnLayout: config.columnLayout, + }; + return ( - <> - - +
+ + + {isLoading && ( +
+
+ +
+
+ )} + + + +
+ { + const rowType = String(row.rowType ?? ''); + if (rowType === 'DETAIL') return undefined; + if (rowType === 'BIZ_SUM') return 'rg-cell-rowtype-biz-sum'; + if (rowType === 'SUMMARY') return 'rg-cell-rowtype-summary'; + return 'rg-cell-rowtype-other'; + }} + /> +
+
); }; diff --git a/apps/st/src/components/util/column/total/SewerExcretaTreatmentStatusColumn.ts b/apps/st/src/components/util/column/total/SewerExcretaTreatmentStatusColumn.ts new file mode 100644 index 000000000..0f5ded556 --- /dev/null +++ b/apps/st/src/components/util/column/total/SewerExcretaTreatmentStatusColumn.ts @@ -0,0 +1,63 @@ +import { ValueType } from "realgrid"; + +export const ExcretaTreatmentStatusColumnConfig = { + tabKey: "ST00070001", + tabLabel: "분뇨처리시설", + rowStartNum: 2, + fixedColumns: 2, + columns: [ + // 행정구역 + { name: "ctpvNm", fieldName: "ctpvNm", header: { text: "시도" }, editable: false }, + { name: "sggNm", fieldName: "sggNm", header: { text: "시군구" }, editable: false }, + { name: "admdstNm", fieldName: "admdstNm", header: { text: "행정구역명" }, editable: false }, + + // 기본 마스터 상세 + { name: "fcltNm", fieldName: "fcltNm", header: { text: "시설명" }, editable: false }, + { name: "fcltPstnCn", fieldName: "fcltPstnCn", header: { text: "소재지" }, editable: false }, + { name: "telNo", fieldName: "telNo", header: { text: "전화번호" }, editable: false }, + { name: "cmptnStrtYmd", fieldName: "cmptnStrtYmd", header: { text: "준공일" }, editable: false }, + { name: "oprtngStrtYmd", fieldName: "oprtngStrtYmd", header: { text: "가동개시일" }, editable: false }, + { name: "wholPjtcoAmt", fieldName: "wholPjtcoAmt", header: { text: "사업비\n(백만원)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "prcsFcltCpct", fieldName: "prcsFcltCpct", header: { text: "시설용량\n(㎥/일)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "cpct", fieldName: "cpct", header: { text: "처리량\n(㎥/일)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "oprtngDayCnt", fieldName: "oprtngDayCnt", header: { text: "가동일수\n(일/년)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "prcsMthdExpln", fieldName: "prcsMthdExpln", header: { text: "처리공법" }, editable: false }, + { name: "linkTmplntCn", fieldName: "linkTmplntCn", header: { text: "연계처리장명" }, editable: false }, + { name: "linkPrcsQnt", fieldName: "linkPrcsQnt", header: { text: "연계처리량\n(㎥/일)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + + // 유입수질(mg/L, 개/mL) 그룹 소속 + { name: "inflwBodWqVl", fieldName: "inflwBodWqVl", header: { text: "BOD" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.00", editable: false }, + { name: "inflwTocWqVl", fieldName: "inflwTocWqVl", header: { text: "TOC" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.00", editable: false }, + { name: "inflwSsWqVl", fieldName: "inflwSsWqVl", header: { text: "SS" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.00", editable: false }, + { name: "inflwTnWqVl", fieldName: "inflwTnWqVl", header: { text: "T-N" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.00", editable: false }, + { name: "inflwTpWqVl", fieldName: "inflwTpWqVl", header: { text: "T-P" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.000", editable: false }, + { name: "inflwClfgCnt", fieldName: "inflwClfgCnt", header: { text: "대장균\n군수" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + + // 방류수질(mg/L, 개/mL) 그룹 소속 + { name: "dschgBodWqVl", fieldName: "dschgBodWqVl", header: { text: "BOD" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.00", editable: false }, + { name: "dschgTocWqVl", fieldName: "dschgTocWqVl", header: { text: "TOC" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.00", editable: false }, + { name: "dschgSsWqVl", fieldName: "dschgSsWqVl", header: { text: "SS" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.00", editable: false }, + { name: "dschgTnWqVl", fieldName: "dschgTnWqVl", header: { text: "T-N" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.00", editable: false }, + { name: "dschgTpWqVl", fieldName: "dschgTpWqVl", header: { text: "T-P" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.000", editable: false }, + { name: "dschgClfgCnt", fieldName: "dschgClfgCnt", header: { text: "대장균\n군수" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + + // 방류수역 그룹 소속 + { name: "wtsysNm", fieldName: "wtsysNm", header: { text: "수계" }, editable: false }, + { name: "trbtrNm", fieldName: "trbtrNm", header: { text: "지류" }, editable: false }, + { name: "msirbCn", fieldName: "msirbCn", header: { text: "중권역" }, editable: false }, + { name: "ssirbCn", fieldName: "ssirbCn", header: { text: "소권역" }, editable: false }, + + // 단일 미그룹 성격 컬럼들 + { name: "operMthdCdCn", fieldName: "operMthdCdCn", header: { text: "운영주체\n(자체/공기업/\n민간위탁)" }, editable: false }, + { name: "wholNope", fieldName: "wholNope", header: { text: "직원총수\n(명)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "sftyAcdntEvntcsCnt", fieldName: "sftyAcdntEvntcsCnt", header: { text: "안전사고\n건수\n(건/인/년)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.00", editable: false } + ], + columnLayout: [ + { name: "areaGroup", header: { text: "행정구역" }, items: ["ctpvNm", "sggNm", "admdstNm"] }, + "fcltNm", "fcltPstnCn", "telNo", "cmptnStrtYmd", "oprtngStrtYmd", "wholPjtcoAmt", "prcsFcltCpct", "cpct", "oprtngDayCnt", "prcsMthdExpln", "linkTmplntCn", "linkPrcsQnt", + { name: "inflwWqGroup", header: { text: "유입수질(mg/L, 개/mL)" }, items: ["inflwBodWqVl", "inflwTocWqVl", "inflwSsWqVl", "inflwTnWqVl", "inflwTpWqVl", "inflwClfgCnt"] }, + { name: "dschgWqGroup", header: { text: "방류수질(mg/L, 개/mL)" }, items: ["dschgBodWqVl", "dschgTocWqVl", "dschgSsWqVl", "dschgTnWqVl", "dschgTpWqVl", "dschgClfgCnt"] }, + { name: "wtrAreaGroup", header: { text: "방류수역" }, items: ["wtsysNm", "trbtrNm", "msirbCn", "ssirbCn"] }, + "operMthdCdCn", "wholNope", "sftyAcdntEvntcsCnt" + ] +}; \ No newline at end of file diff --git a/apps/st/src/entities/statistics/model/statistics-total/statistics-excreta-treatment-total-interface.ts b/apps/st/src/entities/statistics/model/statistics-total/statistics-excreta-treatment-total-interface.ts new file mode 100644 index 000000000..dc9446b32 --- /dev/null +++ b/apps/st/src/entities/statistics/model/statistics-total/statistics-excreta-treatment-total-interface.ts @@ -0,0 +1,62 @@ +/** 07. 분뇨처리시설 현황 검색 조건 파라미터 DTO */ +export interface ExcretaTreatmentMaintainTotalRequest { + ctpvNm?: string; + sggNm?: string; + statsYr?: string; + swtrmtFcltNm?: string; + sewerBzmnCd?: string; + rowDataYn?: boolean; + summaryYn?: boolean; + summaryYearCnt?: number; +} + +/** 분뇨처리시설 목록 데이터 행 구조 */ +export interface ExcretaTreatmentStatusTotalItem { + ctpvNm: string; + sggNm: string | null; + statsYr: string; + admdstNm: string; + admdstCd: string; + fcltNm: string; + rowType: string; + fcltPstnCn: string; + telNo: string; + cmptnStrtYmd: string; + oprtngStrtYmd: string; + wholPjtcoAmt: number; + prcsFcltCpct: number; + cpct: number; + oprtngDayCnt: number; + prcsMthdExpln: string; + linkTmplntCn: string; + linkPrcsQnt: number; + // 유입수질 + inflwBodWqVl: number; + inflwTocWqVl: number; + inflwSsWqVl: number; + inflwTnWqVl: number; + inflwTpWqVl: number; + inflwClfgCnt: number; + // 방류수질 + dschgBodWqVl: number; + dschgTocWqVl: number; + dschgSsWqVl: number; + dschgTnWqVl: number; + dschgTpWqVl: number; + dschgClfgCnt: number; + // 방류수역 및 기타 정보 + wtsysNm: string; + trbtrNm: string; + msirbCn: string; + ssirbCn: string; + operMthdCdCn: string; + wholNope: number; + sftyAcdntEvntcsCnt: number; +} + +/** 분뇨처리시설 API 응답 인터페이스 */ +export interface ExcretaTreatmentStatusTotalResponse { + status: string; + message?: string; + data: ExcretaTreatmentStatusTotalItem[]; +} \ No newline at end of file From a11eaf031d9ba479ab0dba57772dc83e9a16948c Mon Sep 17 00:00:00 2001 From: yangsein Date: Mon, 15 Jun 2026 00:52:54 +0900 Subject: [PATCH 05/12] =?UTF-8?q?feat:[=EC=A7=91=EA=B3=84=ED=91=9C]08.?= =?UTF-8?q?=EA=B0=9C=EC=9D=B8=ED=95=98=EC=88=98=EC=B2=98=EB=A6=AC=EC=8B=9C?= =?UTF-8?q?=EC=84=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../total/PrivateSewageFacilityTot.tsx | 202 +++++++++++++++++- .../SewerPrivateSewageTab1Column.ts | 46 ++++ .../SewerPrivateSewageTab2Column.ts | 72 +++++++ .../SewerPrivateSewageTab3Column.ts | 48 +++++ ...atistics-private-sewage-total-interface.ts | 109 ++++++++++ 5 files changed, 474 insertions(+), 3 deletions(-) create mode 100644 apps/st/src/components/util/column/total/sewerPrivateSewageColumn/SewerPrivateSewageTab1Column.ts create mode 100644 apps/st/src/components/util/column/total/sewerPrivateSewageColumn/SewerPrivateSewageTab2Column.ts create mode 100644 apps/st/src/components/util/column/total/sewerPrivateSewageColumn/SewerPrivateSewageTab3Column.ts create mode 100644 apps/st/src/entities/statistics/model/statistics-total/statistics-private-sewage-total-interface.ts diff --git a/apps/st/src/components/statistics/total/PrivateSewageFacilityTot.tsx b/apps/st/src/components/statistics/total/PrivateSewageFacilityTot.tsx index 1171f7b2c..0948ab60e 100644 --- a/apps/st/src/components/statistics/total/PrivateSewageFacilityTot.tsx +++ b/apps/st/src/components/statistics/total/PrivateSewageFacilityTot.tsx @@ -1,10 +1,206 @@ +import { useRef, useState } from "react"; + +import { useAlertStore } from "@/shared/lib"; +import { LoadingWithAnimation, Tabs, TabsContent, TabsList, TabsTrigger } from "@common/components"; + import SearchForm from "@/components/module/SearchForm"; +import DataGrid from "@/components/module/DataGrid"; +import type { ColumnInfo } from "@/components/type/DataGrid.type"; +import type { SearchFormValues } from "@/components/type/SearchForm.type"; +import { toGridRows } from "@/components/util/grid/gridFunction"; + +// 분리된 개별 3개 컬럼 스펙 파일 로드 +import { PrivateSewageTab1ColumnConfig } from "@/components/util/column/total/sewerPrivateSewageColumn/SewerPrivateSewageTab1Column"; +import { PrivateSewageTab2ColumnConfig } from "@/components/util/column/total/sewerPrivateSewageColumn/SewerPrivateSewageTab2Column"; +import { PrivateSewageTab3ColumnConfig } from "@/components/util/column/total/sewerPrivateSewageColumn/SewerPrivateSewageTab3Column"; + +// 통계 API 서비스 및 통합 DTO 명세 임포트 +import { statisticsTotalStatusApi } from "@/entities/statistics/api/statistics-total-service"; +import type { + PrivateSewageFacilitySummaryRequest +} from "@/entities/statistics/model/statistics-total/statistics-private-sewage-total-interface"; +import CellColorInfo from "./CellColorInfo"; + +const PrivateSewageColumnListTot = [ + PrivateSewageTab1ColumnConfig, + PrivateSewageTab2ColumnConfig, + PrivateSewageTab3ColumnConfig +]; + +const firstTabKey = PrivateSewageColumnListTot[0]?.tabKey ?? "ST00040005_1"; const PrivateSewageFacilityTot = () => { + const { setMessage: alert } = useAlertStore((state) => state); + + // null 컴파일 예러를 사전에 원천 차단한 안전 상태 맵 선언 + const [rowDataMap, setRowDataMap] = useState[]>>(new Map()); + const [isLoading, setIsLoading] = useState(false); + const [activeTab, setActiveTab] = useState(firstTabKey); + const lastSearchedValuesRef = useRef(null); + + const columnInfo = PrivateSewageColumnListTot; + + const handleReset = () => { + setRowDataMap(new Map()); + setActiveTab(firstTabKey); + lastSearchedValuesRef.current = null; + }; + + /** 각 탭별 엔드포인트 비동기 디스패치 및 연동 */ + const loadGridData = async (tabKey: string, values: SearchFormValues): Promise => { + const param: PrivateSewageFacilitySummaryRequest = { + ctpvNm: values.sido === "ALL" ? "" : values.sido, + sggNm: values.gugun === "ALL" ? "" : values.gugun, + sewerBzmnCd: values.bsnmCd, + statsYr: values.year, + rowDataYn: values.isDetail, + summaryYn: values.isSummary !== 'ALL', + summaryYearCnt: values.isSummary !== 'ALL' ? Number(values.isSummary) : undefined + }; + + try { + // [Tab1] 처리용량별 오수처리시설 + if (tabKey === "ST00080001") { + const response = await statisticsTotalStatusApi.getPrivateSewageTab1List(param); + if (response && response.status === "success" && response.data) { + setRowDataMap((prev) => new Map(prev).set(tabKey, toGridRows(response.data as object[]))); + return true; + } + alert(response?.message || "데이터 조회 중 오류가 발생했습니다."); + } + + // [Tab2] 정화조현황 + if (tabKey === "ST00080002") { + const response = await statisticsTotalStatusApi.getPrivateSewageTab2List(param); + if (response && response.status === "success" && response.data) { + setRowDataMap((prev) => new Map(prev).set(tabKey, toGridRows(response.data as object[]))); + return true; + } + alert(response?.message || "데이터 조회 중 오류가 발생했습니다."); + } + + // [Tab3] 개인하수도 관리지역 지정공고 현황 + if (tabKey === "ST00080003") { + const response = await statisticsTotalStatusApi.getPrivatePrivateMgmtAreaList(param); + if (response && response.status === "success" && response.data) { + setRowDataMap((prev) => new Map(prev).set(tabKey, toGridRows(response.data as object[]))); + return true; + } + alert(response?.message || "데이터 조회 중 오류가 발생했습니다."); + } + return false; + } catch { + alert("서버 연결 중 오류가 발생했습니다."); + return false; + } + }; + + const handleSearch = async (values: SearchFormValues) => { + try { + setIsLoading(true); + const ok = await loadGridData(activeTab, values); + if (ok) { + lastSearchedValuesRef.current = values; + } else { + lastSearchedValuesRef.current = null; + } + } catch { + alert("데이터 조회 중 오류가 발생했습니다."); + lastSearchedValuesRef.current = null; + } finally { + setIsLoading(false); + } + }; + + const handleActiveTabChange = async (tabKey: string) => { + setActiveTab(tabKey); + const values = lastSearchedValuesRef.current; + if (!values) return; + + const existing = rowDataMap.get(tabKey); + if (existing && existing.length > 0) return; // 캐싱 검증 스킵 + + try { + setIsLoading(true); + await loadGridData(tabKey, values); + } catch { + alert("데이터 조회 중 오류가 발생했습니다."); + } finally { + setIsLoading(false); + } + }; + return ( - <> - - +
+ + + {/* 하수관로 규칙과 100% 싱크 맞춘 중앙 정렬 소형 팝업 로딩 바 고정 */} + {isLoading && ( +
+
+ +
+
+ )} + + + +
+ void handleActiveTabChange(v)} className="w-full" variant="default"> + + {columnInfo.map((info) => ( + + {info.tabLabel} + + ))} + + + {columnInfo.map((info) => { + const tabKey = info.tabKey; + + // 단언 연산자(as) 배제 형태 완벽 적용 + const rowData = rowDataMap.get(tabKey) ?? []; + + const gridColumnInfo: ColumnInfo = { + columns: info.columns, + columnLayout: info.columnLayout, + }; + + return ( + + { + setRowDataMap((prev) => { + const next = new Map(prev); + const cur = next.get(tabKey) ?? []; + const n = typeof updater === "function" ? updater(cur) : updater; + next.set(tabKey, n); + return next; + }); + }} + isExcelImport={false} + isExcelExport={true} + showHeaderCheckbox={false} + fixedColumns={info.fixedColumns} + enableSort={false} + cellStyleCallback={({ row }) => { + const rowType = String(row.rowType ?? ''); + if (rowType === 'DETAIL') return undefined; + if (rowType === 'BIZ_SUM') return 'rg-cell-rowtype-biz-sum'; + if (rowType === 'SUMMARY') return 'rg-cell-rowtype-summary'; + return 'rg-cell-rowtype-other'; + }} + /> + + ); + })} + +
+
); }; diff --git a/apps/st/src/components/util/column/total/sewerPrivateSewageColumn/SewerPrivateSewageTab1Column.ts b/apps/st/src/components/util/column/total/sewerPrivateSewageColumn/SewerPrivateSewageTab1Column.ts new file mode 100644 index 000000000..c4af5d5a6 --- /dev/null +++ b/apps/st/src/components/util/column/total/sewerPrivateSewageColumn/SewerPrivateSewageTab1Column.ts @@ -0,0 +1,46 @@ +import { ValueType } from "realgrid"; + +export const PrivateSewageTab1ColumnConfig = { + tabKey: "ST00080001", + tabLabel: "처리용량별 오수처리시설", + rowStartNum: 3, + fixedColumns: 3, + columns: [ + { name: "ctpvNm", fieldName: "ctpvNm", header: { text: "시도" }, editable: false }, + { name: "sggNm", fieldName: "sggNm", header: { text: "시군구" }, editable: false }, + { name: "admdstNm", fieldName: "admdstNm", header: { text: "행정구역명" }, editable: false }, + { name: "wholDrtwPrcsFcltCnt", fieldName: "wholDrtwPrcsFcltCnt", header: { text: "총계" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + + // 하수처리구역 내 + { name: "wholStaaCnt", fieldName: "wholStaaCnt", header: { text: "계" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "step1StaaPrcsQnt", fieldName: "step1StaaPrcsQnt", header: { text: "2이하" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "step2StaaPrcsQnt", fieldName: "step2StaaPrcsQnt", header: { text: "3~10" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "step3StaaPrcsQnt", fieldName: "step3StaaPrcsQnt", header: { text: "11~20" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "step4StaaPrcsQnt", fieldName: "step4StaaPrcsQnt", header: { text: "21~50" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "step5StaaPrcsQnt", fieldName: "step5StaaPrcsQnt", header: { text: "51~100" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "step6StaaPrcsQnt", fieldName: "step6StaaPrcsQnt", header: { text: "101~500" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "step7StaaPrcsQnt", fieldName: "step7StaaPrcsQnt", header: { text: "501이상" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + + // 하수처리구역 외 + { name: "staaExcpWhol", fieldName: "staaExcpWhol", header: { text: "계" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "step1StaaExcpPrcsQnt", fieldName: "step1StaaExcpPrcsQnt", header: { text: "2이하" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "step2StaaExcpPrcsQnt", fieldName: "step2StaaExcpPrcsQnt", header: { text: "3~10" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "step3StaaExcpPrcsQnt", fieldName: "step3StaaExcpPrcsQnt", header: { text: "11~20" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "step4StaaExcpPrcsQnt", fieldName: "step4StaaExcpPrcsQnt", header: { text: "21~50" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "step5StaaExcpPrcsQnt", fieldName: "step5StaaExcpPrcsQnt", header: { text: "51~100" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "step6StaaExcpPrcsQnt", fieldName: "step6StaaExcpPrcsQnt", header: { text: "101~500" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "step7StaaExcpPrcsQnt", fieldName: "step7StaaExcpPrcsQnt", header: { text: "501이상" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false } + ], + columnLayout: [ + { name: "areaGroup", header: { text: "행정구역" }, items: ["ctpvNm", "sggNm", "admdstNm"] }, + "wholDrtwPrcsFcltCnt", + { + name: "privateSewageFacilityGroup", + header: { text: "오수처리시설" }, + items: [ + { name: "insideGroup", header: { text: "하수처리구역 내(㎥/일)" }, items: ["wholStaaCnt", "step1StaaPrcsQnt", "step2StaaPrcsQnt", "step3StaaPrcsQnt", "step4StaaPrcsQnt", "step5StaaPrcsQnt", "step6StaaPrcsQnt", "step7StaaPrcsQnt"] }, + { name: "outsideGroup", header: { text: "하수처리구역 외(㎥/일)" }, items: ["staaExcpWhol", "step1StaaExcpPrcsQnt", "step2StaaExcpPrcsQnt", "step3StaaExcpPrcsQnt", "step4StaaExcpPrcsQnt", "step5StaaExcpPrcsQnt", "step6StaaExcpPrcsQnt", "step7StaaExcpPrcsQnt"] } + ] + } + ] +}; \ No newline at end of file diff --git a/apps/st/src/components/util/column/total/sewerPrivateSewageColumn/SewerPrivateSewageTab2Column.ts b/apps/st/src/components/util/column/total/sewerPrivateSewageColumn/SewerPrivateSewageTab2Column.ts new file mode 100644 index 000000000..19fc4d1fb --- /dev/null +++ b/apps/st/src/components/util/column/total/sewerPrivateSewageColumn/SewerPrivateSewageTab2Column.ts @@ -0,0 +1,72 @@ +import { ValueType } from "realgrid"; + +export const PrivateSewageTab2ColumnConfig = { + tabKey: "ST00080002", + tabLabel: "정화조현황", + rowStartNum: 4, + fixedColumns: 3, + columns: [ + { name: "ctpvNm", fieldName: "ctpvNm", header: { text: "시도" }, editable: false }, + { name: "sggNm", fieldName: "sggNm", header: { text: "시군구" }, editable: false }, + { name: "admdstNm", fieldName: "admdstNm", header: { text: "행정구역명" }, editable: false }, + { name: "grssFcltCnt", fieldName: "grssFcltCnt", header: { text: "총 시설 수\n(A+D)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + + // 하수처리구역 내 소속 항목들 + { name: "staaInsdWhol", fieldName: "staaInsdWhol", header: { text: "총계\nA=(B+C)/2" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "staaInsdPrcsCpctByWhol", fieldName: "staaInsdPrcsCpctByWhol", header: { text: "계(C)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "staaInsdSttkNprsflCnt", fieldName: "staaInsdSttkNprsflCnt", header: { text: "자연유하" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "step1StaaInsdSttkFrcCnt", fieldName: "step1StaaInsdSttkFrcCnt", header: { text: "강제펌핑식" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "staaInsdSprktpFltrtbCnt", fieldName: "staaInsdSprktpFltrtbCnt", header: { text: "살수여상" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "staaInsdAertMthdCnt", fieldName: "staaInsdAertMthdCnt", header: { text: "폭기방법" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "staaInsdCntcAertCnt", fieldName: "staaInsdCntcAertCnt", header: { text: "접촉폭기" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "staaInsdEtcCnt", fieldName: "staaInsdEtcCnt", header: { text: "기타" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "step1StaaInsdNope", fieldName: "step1StaaInsdNope", header: { text: "10인조\n이하" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "step2StaaInsdNope", fieldName: "step2StaaInsdNope", header: { text: "11~20" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "step3StaaInsdNope", fieldName: "step3StaaInsdNope", header: { text: "21~30" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "step4StaaInsdNope", fieldName: "step4StaaInsdNope", header: { text: "31~50" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "step5StaaInsdNope", fieldName: "step5StaaInsdNope", header: { text: "51~199" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "step6StaaInsdNope", fieldName: "step6StaaInsdNope", header: { text: "200~499" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "step7StaaInsdNope", fieldName: "step7StaaInsdNope", header: { text: "500~999" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "step8StaaInsdNope", fieldName: "step8StaaInsdNope", header: { text: "1,000이상" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + + // 하수처리구역 외 소속 항목들 + { name: "staaExcpWhol", fieldName: "staaExcpWhol", header: { text: "총계\nD=(E+F)/2" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "staaExcpPrcsCpctByWhol", fieldName: "staaExcpPrcsCpctByWhol", header: { text: "계(F)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "staaExcpSttkNprsflCnt", fieldName: "staaExcpSttkNprsflCnt", header: { text: "자연유하" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "step1StaaExcpSttkFrcCnt", fieldName: "step1StaaExcpSttkFrcCnt", header: { text: "강제펌핑식" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "staaExcpSprktpFltrtbCnt", fieldName: "staaExcpSprktpFltrtbCnt", header: { text: "살수여상" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "staaExcpAertMthdCnt", fieldName: "staaExcpAertMthdCnt", header: { text: "폭기방법" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "staaExcpCntcAertCnt", fieldName: "staaExcpCntcAertCnt", header: { text: "접촉폭기" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "staaExcpEtcCnt", fieldName: "staaExcpEtcCnt", header: { text: "기타" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "step1StaaExcpNope", fieldName: "step1StaaExcpNope", header: { text: "10인조\n이하" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "step2StaaExcpNope", fieldName: "step2StaaExcpNope", header: { text: "11~20" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "step3StaaExcpNope", fieldName: "step3StaaExcpNope", header: { text: "21~30" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "step4StaaExcpNope", fieldName: "step4StaaExcpNope", header: { text: "31~50" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "step5StaaExcpNope", fieldName: "step5StaaExcpNope", header: { text: "51~199" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "step6StaaExcpNope", fieldName: "step6StaaExcpNope", header: { text: "200~499" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "step7StaaExcpNope", fieldName: "step7StaaExcpNope", header: { text: "500~999" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "step8StaaExcpNope", fieldName: "step8StaaExcpNope", header: { text: "1,000이상" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false } + ], + columnLayout: [ + { name: "areaGroup", header: { text: "행정구역" }, items: ["ctpvNm", "sggNm", "admdstNm"] }, + "grssFcltCnt", + { + name: "insideSewerGroup", + header: { text: "하수처리구역 내" }, + items: [ + "staaInsdWhol", + { name: "insdBGroup", header: { text: "계(B)" }, items: ["staaInsdSttkNprsflCnt", "step1StaaInsdSttkFrcCnt", "staaInsdSprktpFltrtbCnt", "staaInsdAertMthdCnt", "staaInsdCntcAertCnt", "staaInsdEtcCnt"] }, + { name: "insdCGroup", header: { text: "처리용량별" }, items: ["staaInsdPrcsCpctByWhol", "step1StaaInsdNope", "step2StaaInsdNope", "step3StaaInsdNope", "step4StaaInsdNope", "step5StaaInsdNope", "step6StaaInsdNope", "step7StaaInsdNope", "step8StaaInsdNope"] } + ] + }, + { + name: "outsideSewerGroup", + header: { text: "하수처리구역 외" }, + items: [ + "staaExcpWhol", + { name: "excpEGroup", header: { text: "계(E)" }, items: ["staaExcpSttkNprsflCnt", "step1StaaExcpSttkFrcCnt", "staaExcpSprktpFltrtbCnt", "staaExcpAertMthdCnt", "staaExcpCntcAertCnt", "staaExcpEtcCnt"] }, + { name: "excpFGroup", header: { text: "처리용량별" }, items: ["staaExcpPrcsCpctByWhol", "step1StaaExcpNope", "step2StaaExcpNope", "step3StaaExcpNope", "step4StaaExcpNope", "step5StaaExcpNope", "step6StaaExcpNope", "step7StaaExcpNope", "step8StaaExcpNope"] } + ] + } + ] +}; \ No newline at end of file diff --git a/apps/st/src/components/util/column/total/sewerPrivateSewageColumn/SewerPrivateSewageTab3Column.ts b/apps/st/src/components/util/column/total/sewerPrivateSewageColumn/SewerPrivateSewageTab3Column.ts new file mode 100644 index 000000000..98a629759 --- /dev/null +++ b/apps/st/src/components/util/column/total/sewerPrivateSewageColumn/SewerPrivateSewageTab3Column.ts @@ -0,0 +1,48 @@ +import { ValueType } from "realgrid"; + +export const PrivateSewageTab3ColumnConfig = { + tabKey: "ST00080003", // 08번 개인하수처리시설 TAB3 매핑 키 고정 + tabLabel: "개인하수도 관리지역 지정공고 현황", + rowStartNum: 3, // 3단 다단 레이아웃 스펙에 맞추어 3으로 설정합니다. + fixedColumns: 1, + columns: [ + // 행정구역 그룹 소속 (시도, 시군구, 행정구역명) + { name: "ctpvNm", fieldName: "ctpvNm", header: { text: "시도" }, editable: false }, + { name: "sggNm", fieldName: "sggNm", header: { text: "시군구" }, editable: false }, + { name: "admdstNm", fieldName: "admdstNm", header: { text: "행정구역명" }, editable: false }, + + // 마스터 단일 컬럼 영역 (개소, 면적) + { name: "indvSwagePbancRgnCnt", fieldName: "indvSwagePbancRgnCnt", header: { text: "공고지역\n(개소)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "indvSwageDsgnScpLen", fieldName: "indvSwageDsgnScpLen", header: { text: "지정범위\n(㎢)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.00", editable: false }, + + // 정화조 그룹 소속 (개소수, 처리용량) + { name: "spctnkGrssFcltCnt", fieldName: "spctnkGrssFcltCnt", header: { text: "총시설수" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "spctnkQnt", fieldName: "spctnkQnt", header: { text: "처리용량\n(인/일)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + + // 오수처리시설 그룹 소속 (개소수, 처리용량) + { name: "drtwPrcsFcltGrssFcltCnt", fieldName: "drtwPrcsFcltGrssFcltCnt", header: { text: "총시설수" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "drtwPrcsFcltQnt", fieldName: "drtwPrcsFcltQnt", header: { text: "처리용량\n(㎥/일)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false } + ], + columnLayout: [ + // 행정구역 묶음 + { name: "areaGroup", header: { text: "행정구역" }, items: ["ctpvNm", "sggNm", "admdstNm"] }, + + // 메인 마스터 지표 + "indvSwagePbancRgnCnt", + "indvSwageDsgnScpLen", + + // 정화조 다단 묶음 + { + name: "septicTankGroup", + header: { text: "정화조" }, + items: ["spctnkGrssFcltCnt", "spctnkQnt"] + }, + + // 오수처리시설 다단 묶음 + { + name: "sewageTreatmentGroup", + header: { text: "오수처리시설" }, + items: ["drtwPrcsFcltGrssFcltCnt", "drtwPrcsFcltQnt"] + } + ] +}; \ No newline at end of file diff --git a/apps/st/src/entities/statistics/model/statistics-total/statistics-private-sewage-total-interface.ts b/apps/st/src/entities/statistics/model/statistics-total/statistics-private-sewage-total-interface.ts new file mode 100644 index 000000000..43c4f4c9a --- /dev/null +++ b/apps/st/src/entities/statistics/model/statistics-total/statistics-private-sewage-total-interface.ts @@ -0,0 +1,109 @@ +/** 08. 개인하수처리시설 공통 요청 검색 파라미터 */ +export interface PrivateSewageFacilitySummaryRequest { + ctpvNm?: string; + sggNm?: string; + statsYr?: string; + swtrmtFcltNm?: string; // Controller 수신 규격 + sewerBzmnCd?: string; + rowDataYn?: boolean; + summaryYn?: boolean; + summaryYearCnt?: number; +} + +/** [Tab1] 오수처리시설 목록 항목 */ +export interface PrivateSewageTab1StatusItem { + ctpvNm: string; + sggNm: string | null; + statsYr: string; + admdstCd: string; + admdstNm: string; + sewerBzmnCd: string; + rowType: string; + wholDrtwPrcsFcltCnt: number; + // 하수처리구역 내 + wholStaaCnt: number; + step1StaaPrcsQnt: number; + step2StaaPrcsQnt: number; + step3StaaPrcsQnt: number; + step4StaaPrcsQnt: number; + step5StaaPrcsQnt: number; + step6StaaPrcsQnt: number; + step7StaaPrcsQnt: number; + // 하수처리구역 외 + staaExcpWhol: number; + step1StaaExcpPrcsQnt: number; + step2StaaExcpPrcsQnt: number; + step3StaaExcpPrcsQnt: number; + step4StaaExcpPrcsQnt: number; + step5StaaExcpPrcsQnt: number; + step6StaaExcpPrcsQnt: number; + step7StaaExcpPrcsQnt: number; +} + +/** [Tab2] 정화조 현황 목록 항목 */ +export interface PrivateSewageTab2StatusItem { + ctpvNm: string; + sggNm: string | null; + statsYr: string; + admdstCd: string; + admdstNm: string; + sewerBzmnCd: string; + rowType: string; + grssFcltCnt: number; + // 하수처리구역 내 + staaInsdWhol: number; + staaInsdSttkNprsflCnt: number; + step1StaaInsdSttkFrcCnt: number; + staaInsdSprktpFltrtbCnt: number; + staaInsdAertMthdCnt: number; + staaInsdCntcAertCnt: number; + staaInsdEtcCnt: number; + staaInsdPrcsCpctByWhol: number; + step1StaaInsdNope: number; + step2StaaInsdNope: number; + step3StaaInsdNope: number; + step4StaaInsdNope: number; + step5StaaInsdNope: number; + step6StaaInsdNope: number; + step7StaaInsdNope: number; + step8StaaInsdNope: number; + // 하수처리구역 외 + staaExcpWhol: number; + staaExcpSttkNprsflCnt: number; + step1StaaExcpSttkFrcCnt: number; + staaExcpSprktpFltrtbCnt: number; + staaExcpAertMthdCnt: number; + staaExcpCntcAertCnt: number; + staaExcpEtcCnt: number; + staaExcpPrcsCpctByWhol: number; + step1StaaExcpNope: number; + step2StaaExcpNope: number; + step3StaaExcpNope: number; + step4StaaExcpNope: number; + step5StaaExcpNope: number; + step6StaaExcpNope: number; + step7StaaExcpNope: number; + step8StaaExcpNope: number; +} + +/** [Tab3] 개인하수도 관리지역 지정공고 현황 목록 항목 */ +export interface PrivateSewageTab3StatusItem { + ctpvNm: string; + sggNm: string | null; + statsYr: string; + admdstCd: string; + admdstNm: string; + sewerBzmnCd: string; + rowType: string; + indvSwagePbancRgnCnt: number; + indvSwageDsgnScpLen: number; + spctnkGrssFcltCnt: number; + spctnkQnt: number; + drtwPrcsFcltGrssFcltCnt: number; + drtwPrcsFcltQnt: number; +} + +/** 3개 탭 개별 API 응답 규격 정의 */ +export interface PrivateSewageTab1Response { status: string; message?: string; data: PrivateSewageTab1StatusItem[]; } +export interface PrivateSewageTab2Response { status: string; message?: string; data: PrivateSewageTab2StatusItem[]; } +export interface PrivateSewageTab3Response { status: string; message?: string; data: PrivateSewageTab3StatusItem[]; } \ No newline at end of file From 6cda4139131707a62ae90a5596259cfa876d9269 Mon Sep 17 00:00:00 2001 From: yangsein Date: Mon, 15 Jun 2026 00:55:06 +0900 Subject: [PATCH 06/12] =?UTF-8?q?feat:[=EC=A7=91=EA=B3=84=ED=91=9C]09.?= =?UTF-8?q?=ED=95=98=EC=88=98=EB=8F=84=20=EC=9E=AC=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../statistics/total/SewerageFinanceTot.tsx | 213 +++++++++++++++++- .../SewerageFeeTab2Column.ts | 24 ++ .../SewerageFinancialTab1Column.ts | 58 +++++ .../SewerageOrgCauseTab4Column.ts | 38 ++++ .../SewerageUsageFeeTab3Column.ts | 62 +++++ ...istics-sewerage-finance-total-interface.ts | 121 ++++++++++ 6 files changed, 513 insertions(+), 3 deletions(-) create mode 100644 apps/st/src/components/util/column/total/sewerageFinancial/SewerageFeeTab2Column.ts create mode 100644 apps/st/src/components/util/column/total/sewerageFinancial/SewerageFinancialTab1Column.ts create mode 100644 apps/st/src/components/util/column/total/sewerageFinancial/SewerageOrgCauseTab4Column.ts create mode 100644 apps/st/src/components/util/column/total/sewerageFinancial/SewerageUsageFeeTab3Column.ts create mode 100644 apps/st/src/entities/statistics/model/statistics-total/statistics-sewerage-finance-total-interface.ts diff --git a/apps/st/src/components/statistics/total/SewerageFinanceTot.tsx b/apps/st/src/components/statistics/total/SewerageFinanceTot.tsx index a73eaba2f..7749bac68 100644 --- a/apps/st/src/components/statistics/total/SewerageFinanceTot.tsx +++ b/apps/st/src/components/statistics/total/SewerageFinanceTot.tsx @@ -1,10 +1,217 @@ +import { useRef, useState } from "react"; + +import { useAlertStore } from "@/shared/lib"; +import { LoadingWithAnimation, Tabs, TabsContent, TabsList, TabsTrigger } from "@common/components"; + import SearchForm from "@/components/module/SearchForm"; +import DataGrid from "@/components/module/DataGrid"; +import type { ColumnInfo } from "@/components/type/DataGrid.type"; +import type { SearchFormValues } from "@/components/type/SearchForm.type"; +import { toGridRows } from "@/components/util/grid/gridFunction"; + +// 분리된 4개 개별 탭 컬럼 파일 바인딩 +import { SewerageFinancialTab1ColumnConfig } from "@/components/util/column/total/sewerageFinancial/SewerageFinancialTab1Column"; +import { SewerageFeeTab2ColumnConfig } from "@/components/util/column/total/sewerageFinancial/SewerageFeeTab2Column"; +import { SewerageUsageFeeTab3ColumnConfig } from "@/components/util/column/total/sewerageFinancial/SewerageUsageFeeTab3Column"; +import { SewerageOrgCauseTab4ColumnConfig } from "@/components/util/column/total/sewerageFinancial/SewerageOrgCauseTab4Column"; + +// API 인스턴스 및 인터페이스 호출 +import { statisticsTotalStatusApi } from "@/entities/statistics/api/statistics-total-service"; +import type { + SewerageFinanceSummaryRequest +} from "@/entities/statistics/model/statistics-total/statistics-sewerage-finance-total-interface"; +import CellColorInfo from "./CellColorInfo"; + +const SewerageFinanceColumnListTot = [ + SewerageFinancialTab1ColumnConfig, + SewerageFeeTab2ColumnConfig, + SewerageUsageFeeTab3ColumnConfig, + SewerageOrgCauseTab4ColumnConfig +]; + +const firstTabKey = SewerageFinanceColumnListTot[0]?.tabKey ?? "ST00040006_1"; const SewerageFinanceTot = () => { + const { setMessage: alert } = useAlertStore((state) => state); + + // 컴파일 에러가 발생하지 않도록 원천 가공 처리형 맵 적용 + const [rowDataMap, setRowDataMap] = useState[]>>(new Map()); + const [isLoading, setIsLoading] = useState(false); + const [activeTab, setActiveTab] = useState(firstTabKey); + const lastSearchedValuesRef = useRef(null); + + const columnInfo = SewerageFinanceColumnListTot; + + const handleReset = () => { + setRowDataMap(new Map()); + setActiveTab(firstTabKey); + lastSearchedValuesRef.current = null; + }; + + /** 개별 조건별 데이터 동적 라우팅 디스패치 */ + const loadGridData = async (tabKey: string, values: SearchFormValues): Promise => { + const param: SewerageFinanceSummaryRequest = { + ctpvNm: values.sido === "ALL" ? "" : values.sido, + sggNm: values.gugun === "ALL" ? "" : values.gugun, + sewerBzmnCd: values.bsnmCd, + statsYr: values.year, + rowDataYn: values.isDetail, + summaryYn: values.isSummary !== 'ALL', + summaryYearCnt: values.isSummary !== 'ALL' ? Number(values.isSummary) : undefined + }; + + try { + // [Tab1] 하수도재정 결산 + if (tabKey === "ST00090001") { + const response = await statisticsTotalStatusApi.getSewerageFinancialList(param); // 엔드포인트 바인딩 + if (response && response.status === "success" && response.data) { + setRowDataMap((prev) => new Map(prev).set(tabKey, toGridRows(response.data as object[]))); + return true; + } + alert(response?.message || "데이터 조회 중 오류가 발생했습니다."); + } + + // [Tab2] 하수도 요금 + if (tabKey === "ST00090002") { + const response = await statisticsTotalStatusApi.getSewerageFeeList(param); + if (response && response.status === "success" && response.data) { + setRowDataMap((prev) => new Map(prev).set(tabKey, toGridRows(response.data as object[]))); + return true; + } + alert(response?.message || "데이터 조회 중 오류가 발생했습니다."); + } + + // [Tab3] 하수도 사용료 + if (tabKey === "ST00090003") { + const response = await statisticsTotalStatusApi.getSewerageUsageList(param); + if (response && response.status === "success" && response.data) { + setRowDataMap((prev) => new Map(prev).set(tabKey, toGridRows(response.data as object[]))); + return true; + } + alert(response?.message || "데이터 조회 중 오류가 발생했습니다."); + } + + // [Tab4] 원인자 부담금 + if (tabKey === "ST00090004") { + const response = await statisticsTotalStatusApi.getSewerageOrgCauseList(param); + if (response && response.status === "success" && response.data) { + setRowDataMap((prev) => new Map(prev).set(tabKey, toGridRows(response.data as object[]))); + return true; + } + alert(response?.message || "데이터 조회 중 오류가 발생했습니다."); + } + return false; + } catch { + alert("서버 연결 중 오류가 발생했습니다."); + return false; + } + }; + + const handleSearch = async (values: SearchFormValues) => { + try { + setIsLoading(true); + const ok = await loadGridData(activeTab, values); + if (ok) { + lastSearchedValuesRef.current = values; + } else { + lastSearchedValuesRef.current = null; + } + } catch { + alert("데이터 조회 중 오류가 발생했습니다."); + lastSearchedValuesRef.current = null; + } finally { + setIsLoading(false); + } + }; + + const handleActiveTabChange = async (tabKey: string) => { + setActiveTab(tabKey); + const values = lastSearchedValuesRef.current; + if (!values) return; + + const existing = rowDataMap.get(tabKey); + if (existing && existing.length > 0) return; + + try { + setIsLoading(true); + await loadGridData(tabKey, values); + } catch { + alert("데이터 조회 중 오류가 발생했습니다."); + } finally { + setIsLoading(false); + } + }; + return ( - <> - - +
+ + + {isLoading && ( +
+
+ +
+
+ )} + + + +
+ void handleActiveTabChange(v)} className="w-full" variant="default"> + + {columnInfo.map((info) => ( + + {info.tabLabel} + + ))} + + + {columnInfo.map((info) => { + const tabKey = info.tabKey; + + // 요청하신 타입 단언 제거 형태 유지 + const rowData = rowDataMap.get(tabKey) ?? []; + + const gridColumnInfo: ColumnInfo = { + columns: info.columns, + columnLayout: info.columnLayout, + }; + + return ( + + { + setRowDataMap((prev) => { + const next = new Map(prev); + const cur = next.get(tabKey) ?? []; + const n = typeof updater === "function" ? updater(cur) : updater; + next.set(tabKey, n); + return next; + }); + }} + isExcelImport={false} + isExcelExport={true} + showHeaderCheckbox={false} + fixedColumns={info.fixedColumns} + enableSort={false} + cellStyleCallback={({ row }) => { + const rowType = String(row.rowType ?? ''); + if (rowType === 'DETAIL') return undefined; + if (rowType === 'BIZ_SUM') return 'rg-cell-rowtype-biz-sum'; + if (rowType === 'SUMMARY') return 'rg-cell-rowtype-summary'; + return 'rg-cell-rowtype-other'; + }} + /> + + ); + })} + +
+
); }; diff --git a/apps/st/src/components/util/column/total/sewerageFinancial/SewerageFeeTab2Column.ts b/apps/st/src/components/util/column/total/sewerageFinancial/SewerageFeeTab2Column.ts new file mode 100644 index 000000000..2424c4ce7 --- /dev/null +++ b/apps/st/src/components/util/column/total/sewerageFinancial/SewerageFeeTab2Column.ts @@ -0,0 +1,24 @@ +import { ValueType } from "realgrid"; + +export const SewerageFeeTab2ColumnConfig = { + tabKey: "ST00090002", + tabLabel: "하수도 요금", + rowStartNum: 2, + fixedColumns: 3, + columns: [ + { name: "ctpvNm", fieldName: "ctpvNm", header: { text: "시도" }, editable: false }, + { name: "sggNm", fieldName: "sggNm", header: { text: "시군구" }, editable: false }, + { name: "admdstNm", fieldName: "admdstNm", header: { text: "행정구역명" }, editable: false }, + + { name: "anlLevyQnt", fieldName: "anlLevyQnt", header: { text: "연간조정량(㎡)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "levyAmt", fieldName: "levyAmt", header: { text: "하수도사용료수익(원)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "avgUntprcAmt", fieldName: "avgUntprcAmt", header: { text: "㎡당 요금(원/㎡)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "grssPrmcoAmt", fieldName: "grssPrmcoAmt", header: { text: "총관원가(원)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "grssUnitPrmcoAmt", fieldName: "grssUnitPrmcoAmt", header: { text: "㎡당원가(원/㎡)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "rlztnRt", fieldName: "rlztnRt", header: { text: "현실화율(%)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false } + ], + columnLayout: [ + { name: "areaGroup", header: { text: "행정구역" }, items: ["ctpvNm", "sggNm", "admdstNm"] }, + "anlLevyQnt", "levyAmt", "avgUntprcAmt", "grssPrmcoAmt", "grssUnitPrmcoAmt", "rlztnRt" + ] +}; \ No newline at end of file diff --git a/apps/st/src/components/util/column/total/sewerageFinancial/SewerageFinancialTab1Column.ts b/apps/st/src/components/util/column/total/sewerageFinancial/SewerageFinancialTab1Column.ts new file mode 100644 index 000000000..78589a22d --- /dev/null +++ b/apps/st/src/components/util/column/total/sewerageFinancial/SewerageFinancialTab1Column.ts @@ -0,0 +1,58 @@ +import { ValueType } from "realgrid"; + +export const SewerageFinancialTab1ColumnConfig = { + tabKey: "ST00090001", + tabLabel: "하수도재정 결산", + rowStartNum: 3, + fixedColumns: 1, + columns: [ + { name: "ctpvNm", fieldName: "ctpvNm", header: { text: "시도" }, editable: false }, + { name: "sggNm", fieldName: "sggNm", header: { text: "시군구" }, editable: false }, + { name: "admdstNm", fieldName: "admdstNm", header: { text: "행정구역명" }, editable: false }, + + { name: "crfwAmt", fieldName: "crfwAmt", header: { text: "이월액" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "lbltAmt", fieldName: "lbltAmt", header: { text: "부채액\n(백만원)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + + { name: "usgByWholPjtcoAmt", fieldName: "usgByWholPjtcoAmt", header: { text: "계" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + + { name: "tmplntWholAmt", fieldName: "tmplntWholAmt", header: { text: "소계" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "tmplntFcltAmt", fieldName: "tmplntFcltAmt", header: { text: "시설비" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "tmplntMendngAmt", fieldName: "tmplntMendngAmt", header: { text: "개보수비" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "tmplntOperKeepAmt", fieldName: "tmplntOperKeepAmt", header: { text: "운영\n유지비" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "tmplntEtcAmt", fieldName: "tmplntEtcAmt", header: { text: "기타" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + + { name: "wholSwagePplAmt", fieldName: "wholSwagePplAmt", header: { text: "소계" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "swagePplFcltAmt", fieldName: "swagePplFcltAmt", header: { text: "시설비" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "swagePplMendngAmt", fieldName: "swagePplMendngAmt", header: { text: "개보수비" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "swagePplOperKeepAmt", fieldName: "swagePplOperKeepAmt", header: { text: "운영\n유지비" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "swagePplEtcAmt", fieldName: "swagePplEtcAmt", header: { text: "기타" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + + { name: "wholSwageImdpFcltAmt", fieldName: "wholSwageImdpFcltAmt", header: { text: "소계" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "swageImpdFcltAmt", fieldName: "swageImpdFcltAmt", header: { text: "시설비" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "swageImpdMendngAmt", fieldName: "swageImpdMendngAmt", header: { text: "개보수비" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "swageImpdOperKeepAmt", fieldName: "swageImpdOperKeepAmt", header: { text: "운영\n유지비" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "swageImpdFcltEtcAmt", fieldName: "swageImpdFcltEtcAmt", header: { text: "기타" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + + { name: "etcWholAmt", fieldName: "etcWholAmt", header: { text: "소계" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "etcAcdntCrfwAmt", fieldName: "etcAcdntCrfwAmt", header: { text: "사고\n이월액" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "etcNrcpAmt", fieldName: "etcNrcpAmt", header: { text: "미수납액" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "etcAmt", fieldName: "etcAmt", header: { text: "기타" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false } + ], + columnLayout: [ + { name: "areaGroup", header: { text: "행정구역" }, items: ["ctpvNm", "sggNm", "admdstNm"] }, + "crfwAmt", + + { + name: "purposeGroup", + header: { text: "용도별사업비(백만원)" }, + items: [ + + "usgByWholPjtcoAmt", + { name: "t1Group", header: { text: "처리장" }, items: ["tmplntWholAmt", "tmplntFcltAmt", "tmplntMendngAmt", "tmplntOperKeepAmt", "tmplntEtcAmt"] }, + { name: "t2Group", header: { text: "하수관로" }, items: ["wholSwagePplAmt", "swagePplFcltAmt", "swagePplMendngAmt", "swagePplOperKeepAmt", "swagePplEtcAmt"] }, + { name: "t3Group", header: { text: "유수지 및 배수펌프장" }, items: ["wholSwageImdpFcltAmt", "swageImpdFcltAmt", "swageImpdMendngAmt", "swageImpdOperKeepAmt", "swageImpdFcltEtcAmt"] }, + { name: "t4Group", header: { text: "기타" }, items: ["etcWholAmt", "etcAcdntCrfwAmt", "etcNrcpAmt", "etcAmt"] } + ] + } + ] +}; \ No newline at end of file diff --git a/apps/st/src/components/util/column/total/sewerageFinancial/SewerageOrgCauseTab4Column.ts b/apps/st/src/components/util/column/total/sewerageFinancial/SewerageOrgCauseTab4Column.ts new file mode 100644 index 000000000..af0afc329 --- /dev/null +++ b/apps/st/src/components/util/column/total/sewerageFinancial/SewerageOrgCauseTab4Column.ts @@ -0,0 +1,38 @@ +import { ValueType } from "realgrid"; + +export const SewerageOrgCauseTab4ColumnConfig = { + tabKey: "ST00090004", + tabLabel: "원인자부담금", + rowStartNum: 3, + fixedColumns: 3, + columns: [ + { name: "ctpvNm", fieldName: "ctpvNm", header: { text: "시도" }, editable: false }, + { name: "sggNm", fieldName: "sggNm", header: { text: "시군구" }, editable: false }, + { name: "admdstNm", fieldName: "admdstNm", header: { text: "행정구역명" }, editable: false }, + + { name: "levyEvntcsCnt", fieldName: "levyEvntcsCnt", header: { text: "건수" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "levyAmt", fieldName: "levyAmt", header: { text: "금액(백만원)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + + { name: "trgtByWholClctnEvntcsCnt", fieldName: "trgtByWholClctnEvntcsCnt", header: { text: "건수" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "trgtByWholClctnAmt", fieldName: "trgtByWholClctnAmt", header: { text: "금액(백만원)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + + { name: "mltvEqpmEvntcsCnt", fieldName: "mltvEqpmEvntcsCnt", header: { text: "건수" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "mltvEqpmAmt", fieldName: "mltvEqpmAmt", header: { text: "금액(백만원)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + + { name: "othrCstrnOthrActrCnt", fieldName: "othrCstrnOthrActrCnt", header: { text: "건수" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "othrCstrnOthrActrAmt", fieldName: "othrCstrnOthrActrAmt", header: { text: "금액(백만원)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false } + ], + columnLayout: [ + { name: "areaGroup", header: { text: "행정구역" }, items: ["ctpvNm", "sggNm", "admdstNm"] }, + { name: "levyGroup", header: { text: "부과액" }, items: ["levyEvntcsCnt", "levyAmt"] }, + { + name: "collectionStatusGroup", + header: { text: "대상별 징수현황" }, + items: [ + { name: "c1Group", header: { text: "계" }, items: ["trgtByWholClctnEvntcsCnt", "trgtByWholClctnAmt"] }, + { name: "c2Group", header: { text: "배수설비설치자\n(하수도법 제61조 제1항)" }, items: ["mltvEqpmEvntcsCnt", "mltvEqpmAmt"] }, + { name: "c3Group", header: { text: "타공사·타행위자\n(하수도법 제61조 제2항)" }, items: ["othrCstrnOthrActrCnt", "othrCstrnOthrActrAmt"] } + ] + } + ] +}; \ No newline at end of file diff --git a/apps/st/src/components/util/column/total/sewerageFinancial/SewerageUsageFeeTab3Column.ts b/apps/st/src/components/util/column/total/sewerageFinancial/SewerageUsageFeeTab3Column.ts new file mode 100644 index 000000000..5bfe56b94 --- /dev/null +++ b/apps/st/src/components/util/column/total/sewerageFinancial/SewerageUsageFeeTab3Column.ts @@ -0,0 +1,62 @@ +import { ValueType } from "realgrid"; + +export const SewerageUsageFeeTab3ColumnConfig = { + tabKey: "ST00090003", + tabLabel: "하수도 사용료", + rowStartNum: 3, + fixedColumns: 3, + columns: [ + { name: "ctpvNm", fieldName: "ctpvNm", header: { text: "시도" }, editable: false }, + { name: "sggNm", fieldName: "sggNm", header: { text: "시군구" }, editable: false }, + { name: "admdstNm", fieldName: "admdstNm", header: { text: "행정구역명" }, editable: false }, + + { name: "levyAmt", fieldName: "levyAmt", header: { text: "부과액(백만원)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "levyQnt", fieldName: "levyQnt", header: { text: "부과량(천톤)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + + // 가정용 + { name: "hshusLevyAmt", fieldName: "hshusLevyAmt", header: { text: "부과액" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "hshusLevyQnt", fieldName: "hshusLevyQnt", header: { text: "부과량" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "hshusUntprcAmt", fieldName: "hshusUntprcAmt", header: { text: "단가" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + + // 업무용 + { name: "taskFrLevyAmt", fieldName: "taskFrLevyAmt", header: { text: "부과액" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "taskFrLevyQnt", fieldName: "taskFrLevyQnt", header: { text: "부과량" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "taskFrUntprcAmt", fieldName: "taskFrUntprcAmt", header: { text: "단가" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + + // 영업용(일반용) + { name: "bsnGnrlLevyAmt", fieldName: "bsnGnrlLevyAmt", header: { text: "부과액(백만원)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "bsnGnrlLevyQnt", fieldName: "bsnGnrlLevyQnt", header: { text: "부과량(천톤)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "bsnGnrlUntprcAmt", fieldName: "bsnGnrlUntprcAmt", header: { text: "단가(원/톤)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + + // 욕탕 1종 + { name: "massBthhsFrLevyAmt", fieldName: "massBthhsFrLevyAmt", header: { text: "부과액(백만원)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "massBthhsFrLevyQnt", fieldName: "massBthhsFrLevyQnt", header: { text: "부과량(천톤)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "massBthhsFrUntprcAmt", fieldName: "massBthhsFrUntprcAmt", header: { text: "단가(원/톤)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + + // 욕탕 2종 + { name: "bthhsFrSpcs2LevyAmt", fieldName: "bthhsFrSpcs2LevyAmt", header: { text: "부과액" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "bthhsFrSpcs2LevyQnt", fieldName: "bthhsFrSpcs2LevyQnt", header: { text: "부과량" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "bthhsFrSpcs2UntprcAmt", fieldName: "bthhsFrSpcs2UntprcAmt", header: { text: "단가" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + + // 산업 및 공업용 + { name: "induMdsrLevyAmt", fieldName: "induMdsrLevyAmt", header: { text: "부과액(백만원)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "induMdsrLevyQnt", fieldName: "induMdsrLevyQnt", header: { text: "부과량(천톤)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "induMdsrUntprcAmt", fieldName: "induMdsrUntprcAmt", header: { text: "단가(원/톤)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + + // 기타 + { name: "etcLevyAmt", fieldName: "etcLevyAmt", header: { text: "부과액" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "etcLevyQnt", fieldName: "etcLevyQnt", header: { text: "부과량" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "etcLevyUntprcAmt", fieldName: "etcLevyUntprcAmt", header: { text: "단가" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false } + ], + columnLayout: [ + { name: "areaGroup", header: { text: "행정구역" }, items: ["ctpvNm", "sggNm", "admdstNm"] }, + { name: "totalGroup", header: { text: "총계" }, items: ["levyAmt", "levyQnt"] }, + { name: "g1Group", header: { text: "가정용" }, items: ["hshusLevyAmt", "hshusLevyQnt", "hshusUntprcAmt"] }, + { name: "g2Group", header: { text: "업무용" }, items: ["taskFrLevyAmt", "taskFrLevyQnt", "taskFrUntprcAmt"] }, + { name: "g3Group", header: { text: "영업용(일반용)" }, items: ["bsnGnrlLevyAmt", "bsnGnrlLevyQnt", "bsnGnrlUntprcAmt"] }, + { name: "g4Group", header: { text: "욕탕 1종(대중탕용)" }, items: ["massBthhsFrLevyAmt", "massBthhsFrLevyQnt", "massBthhsFrUntprcAmt"] }, + { name: "g5Group", header: { text: "욕탕2종" }, items: ["bthhsFrSpcs2LevyAmt", "bthhsFrSpcs2LevyQnt", "bthhsFrSpcs2UntprcAmt"] }, + { name: "g6Group", header: { text: "산업 및 공업용" }, items: ["induMdsrLevyAmt", "induMdsrLevyQnt", "induMdsrUntprcAmt"] }, + { name: "g7Group", header: { text: "기타" }, items: ["etcLevyAmt", "etcLevyQnt", "etcLevyUntprcAmt"] } + ] +}; \ No newline at end of file diff --git a/apps/st/src/entities/statistics/model/statistics-total/statistics-sewerage-finance-total-interface.ts b/apps/st/src/entities/statistics/model/statistics-total/statistics-sewerage-finance-total-interface.ts new file mode 100644 index 000000000..1a430a911 --- /dev/null +++ b/apps/st/src/entities/statistics/model/statistics-total/statistics-sewerage-finance-total-interface.ts @@ -0,0 +1,121 @@ +/** 09. 하수도 재정관리 검색 조건 공통 파라미터 */ +export interface SewerageFinanceSummaryRequest { + ctpvNm?: string; + sggNm?: string; + statsYr?: string; + sewerBzmnCd?: string; + rowDataYn?: boolean; + summaryYn?: boolean; + summaryYearCnt?: number; +} + +/** [Tab1] 하수도재정 결산 목록 항목 */ +export interface SewerageFinanceSettlementTotalItem { + ctpvNm: string; + sggNm: string | null; + statsYr: string; + admdstNm: string; + admdstCd: string; + rowType: string; + fnnByWholPjtcoAmt: number; + cnterGovSprtAmt: number; + mctpvLcexpAmt: number; + wareaSggLclAmt: number; + sewerUseAmt: number; + orgprBrdnAmt: number; + etcIncmAmt: number; + crfwAmt: number; + lbltAmt: number; + usgByWholPjtcoAmt: number; + tmplntWholAmt: number; + tmplntFcltAmt: number; + tmplntMendngAmt: number; + tmplntOperKeepAmt: number; + tmplntEtcAmt: number; + wholSwagePplAmt: number; + swagePplFcltAmt: number; + swagePplMendngAmt: number; + swagePplOperKeepAmt: number; + swagePplEtcAmt: number; + wholSwageImdpFcltAmt: number; // 스펙 대조 자동 매핑 보정 (SwageImpdFclt) + swageImpdFcltAmt: number; + swageImpdMendngAmt: number; + swageImpdOperKeepAmt: number; + swageImpdFcltEtcAmt: number; + etcWholAmt: number; + etcAcdntCrfwAmt: number; + etcNrcpAmt: number; + etcAmt: number; +} + +/** [Tab2] 하수도 요금 목록 항목 */ +export interface SewerageFeeTotalItem { + ctpvNm: string; + sggNm: string | null; + statsYr: string; + admdstNm: string; + admdstCd: string; + rowType: string; + anlLevyQnt: number; + levyAmt: number; + avgUntprcAmt: number; + grssPrmcoAmt: number; + grssUnitPrmcoAmt: number; + rlztnRt: number; +} + +/** [Tab3] 하수도 사용료 목록 항목 */ +export interface SewerageUsageFeeTotalItem { + ctpvNm: string; + sggNm: string | null; + statsYr: string; + admdstNm: string; + admdstCd: string; + rowType: string; + levyAmt: number; + levyQnt: number; + hshusLevyAmt: number; + hshusLevyQnt: number; + hshusUntprcAmt: number; + taskFrLevyAmt: number; + taskFrLevyQnt: number; + taskFrUntprcAmt: number; + bsnGnrlLevyAmt: number; + bsnGnrlLevyQnt: number; + bsnGnrlUntprcAmt: number; + massBthhsFrLevyAmt: number; + massBthhsFrLevyQnt: number; + massBthhsFrUntprcAmt: number; + bthhsFrSpcs2LevyAmt: number; + bthhsFrSpcs2LevyQnt: number; + bthhsFrSpcs2UntprcAmt: number; + induMdsrLevyAmt: number; + induMdsrLevyQnt: number; + induMdsrUntprcAmt: number; + etcLevyAmt: number; + etcLevyQnt: number; + etcLevyUntprcAmt: number; +} + +/** [Tab4] 원인자 부담금 목록 항목 */ +export interface SewerageOrgCauseTotalItem { + ctpvNm: string; + sggNm: string | null; + statsYr: string; + admdstNm: string; + admdstCd: string; + rowType: string; + levyEvntcsCnt: number; + levyAmt: number; + trgtByWholClctnEvntcsCnt: number; + trgtByWholClctnAmt: number; + mltvEqpmEvntcsCnt: number; + mltvEqpmAmt: number; + othrCstrnOthrActrCnt: number; + othrCstrnOthrActrAmt: number; +} + +export interface SewerageFinanceSettlementResponse { status: string; message?: string; data: SewerageFinanceSettlementTotalItem[]; } +export interface SewerageFeeResponse { status: string; message?: string; data: SewerageFeeTotalItem[]; } +export interface SewerageUsageFeeResponse { status: string; message?: string; data: SewerageUsageFeeTotalItem[]; } +export interface SewerageOrgCauseResponse { status: string; message?: string; data: SewerageOrgCauseTotalItem[]; } \ No newline at end of file From d4c9989069dfb19608d4dfeb079216ab34a74160 Mon Sep 17 00:00:00 2001 From: yangsein Date: Mon, 15 Jun 2026 00:56:20 +0900 Subject: [PATCH 07/12] =?UTF-8?q?feat:=20[=EC=A7=91=EA=B3=84=ED=91=9C]=20?= =?UTF-8?q?=EB=AC=BC=EC=9E=AC=EC=9D=B4=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../statistics/total/WaterReuseTot.tsx | 225 +++++++++++++++++- .../SewerageGrayWaterTab2Column.ts | 48 ++++ .../SewerageRainwaterTab3Column.ts | 33 +++ .../sewerageReuse/SewerageReuseTab1Column.ts | 89 +++++++ .../SewerageWwtRtlztnRegTab4Column.ts | 28 +++ 5 files changed, 420 insertions(+), 3 deletions(-) create mode 100644 apps/st/src/components/util/column/total/sewerageReuse/SewerageGrayWaterTab2Column.ts create mode 100644 apps/st/src/components/util/column/total/sewerageReuse/SewerageRainwaterTab3Column.ts create mode 100644 apps/st/src/components/util/column/total/sewerageReuse/SewerageReuseTab1Column.ts create mode 100644 apps/st/src/components/util/column/total/sewerageReuse/SewerageWwtRtlztnRegTab4Column.ts diff --git a/apps/st/src/components/statistics/total/WaterReuseTot.tsx b/apps/st/src/components/statistics/total/WaterReuseTot.tsx index 567797983..e48d12673 100644 --- a/apps/st/src/components/statistics/total/WaterReuseTot.tsx +++ b/apps/st/src/components/statistics/total/WaterReuseTot.tsx @@ -1,10 +1,229 @@ +import { useRef, useState } from "react"; + +import { useAlertStore } from "@/shared/lib"; +import { LoadingWithAnimation, Tabs, TabsContent, TabsList, TabsTrigger } from "@common/components"; + import SearchForm from "../../module/SearchForm"; +import DataGrid from "@/components/module/DataGrid"; +import type { ColumnInfo } from "@/components/type/DataGrid.type"; +import type { SearchFormValues } from "@/components/type/SearchForm.type"; +import { toGridRows } from "@/components/util/grid/gridFunction"; + +// [폴더 정밀 정비 완료] 프로젝트 트리 구조 5개 컬럼 스펙 패키지 호출 +import { SewerageReuseTab1ColumnConfig } from "@/components/util/column/total/sewerageReuse/SewerageReuseTab1Column"; +import { SewerageGrayWaterTab2ColumnConfig } from "@/components/util/column/total/sewerageReuse/SewerageGrayWaterTab2Column"; +import { SewerageRainwaterTab3ColumnConfig } from "@/components/util/column/total/sewerageReuse/SewerageRainwaterTab3Column"; +import { SewerageWwtRtlztnRegTab4ColumnConfig } from "@/components/util/column/total/sewerageReuse/SewerageWwtRtlztnRegTab4Column"; +import { SewerageWwtRtlztnSncnTab5ColumnConfig } from "@/components/util/column/total/sewerageReuse/SewerageWwtRtlztnSncnTab5Column"; + +// 집계표(TOTAL) API 서비스 및 인터페이스 명세 임포트 +import { statisticsTotalStatusApi } from "@/entities/statistics/api/statistics-total-service"; +import type { + WaterReuseSummaryRequest +} from "@/entities/statistics/model/statistics-total/statistics-sewerage-reuse-total-interface"; +import CellColorInfo from "./CellColorInfo"; + +// 5개 멀티 탭 매핑을 위한 설정 취합 배열 +const WaterReuseColumnListTot = [ + SewerageReuseTab1ColumnConfig, + SewerageGrayWaterTab2ColumnConfig, + SewerageRainwaterTab3ColumnConfig, + SewerageWwtRtlztnRegTab4ColumnConfig, + SewerageWwtRtlztnSncnTab5ColumnConfig // 👈 5번째 탭 풀장착 완료 +]; + +const firstTabKey = WaterReuseColumnListTot[0]?.tabKey ?? "ST00040007_1"; const WaterReuseTot = () => { + const { setMessage: alert } = useAlertStore((state) => state); + + // Map 데이터 구조를 사용하여 완벽하게 안전한 멀티 탭 데이터 상태 유지 + const [rowDataMap, setRowDataMap] = useState[]>>(new Map()); + const [isLoading, setIsLoading] = useState(false); + const [activeTab, setActiveTab] = useState(firstTabKey); + const lastSearchedValuesRef = useRef(null); + + const columnInfo = WaterReuseColumnListTot; + + const handleReset = () => { + setRowDataMap(new Map()); + setActiveTab(firstTabKey); + lastSearchedValuesRef.current = null; + }; + + const loadGridData = async (tabKey: string, values: SearchFormValues): Promise => { + const param: WaterReuseSummaryRequest = { + ctpvNm: values.sido === "ALL" ? "" : values.sido, + sggNm: values.gugun === "ALL" ? "" : values.gugun, + sewerBzmnCd: values.bsnmCd, + statsYr: values.year, + rowDataYn: values.isDetail, + summaryYn: values.isSummary !== 'ALL', + summaryYearCnt: values.isSummary !== 'ALL' ? Number(values.isSummary) : undefined + }; + + try { + // [TAB1] 공공하수처리수 재이용 현황 조회 + if (tabKey === "ST00100001") { + const response = await statisticsTotalStatusApi.getSewerageReuseTab1List(param); + if (response && response.status === "success" && response.data) { + setRowDataMap((prev) => new Map(prev).set(tabKey, toGridRows(response.data as object[]))); + return true; + } + alert(response?.message || "데이터 조회 중 오류가 발생했습니다."); + } + + // [TAB2] 중수도 현황 조회 + if (tabKey === "ST00100002") { + const response = await statisticsTotalStatusApi.getGrayWaterStatusTab2List(param); + if (response && response.status === "success" && response.data) { + setRowDataMap((prev) => new Map(prev).set(tabKey, toGridRows(response.data as object[]))); + return true; + } + alert(response?.message || "데이터 조회 중 오류가 발생했습니다."); + } + + // [TAB3] 빗물이용시설현황 조회 + if (tabKey === "ST00100003") { + const response = await statisticsTotalStatusApi.getRainwaterStatusTab3List(param); + if (response && response.status === "success" && response.data) { + setRowDataMap((prev) => new Map(prev).set(tabKey, toGridRows(response.data as object[]))); + return true; + } + alert(response?.message || "데이터 조회 중 오류가 발생했습니다."); + } + + // [TAB4] 하·폐수처리수 재이용시설 시공업 등록현황 조회 + if (tabKey === "ST00100004") { + const response = await statisticsTotalStatusApi.getWwtRtlztnRegStatusTab4List(param); + if (response && response.status === "success" && response.data) { + setRowDataMap((prev) => new Map(prev).set(tabKey, toGridRows(response.data as object[]))); + return true; + } + alert(response?.message || "데이터 조회 중 오류가 발생했습니다."); + } + + // [TAB5] 하·폐수처리수 재이용시설 시공업 인가현황 조회 + if (tabKey === "ST00100005") { + const response = await statisticsTotalStatusApi.getWwtRtlztnSncnStatusTab5List(param); + if (response && response.status === "success" && response.data) { + setRowDataMap((prev) => new Map(prev).set(tabKey, toGridRows(response.data as object[]))); + return true; + } + alert(response?.message || "데이터 조회 중 오류가 발생했습니다."); + } + return false; + } catch { + alert("서버 연결 중 오류가 발생했습니다."); + return false; + } + }; + + const handleSearch = async (values: SearchFormValues) => { + try { + setIsLoading(true); + const ok = await loadGridData(activeTab, values); + if (ok) { + lastSearchedValuesRef.current = values; + } else { + lastSearchedValuesRef.current = null; + } + } catch { + alert("데이터 조회 중 오류가 발생했습니다."); + lastSearchedValuesRef.current = null; + } finally { + setIsLoading(false); + } + }; + + const handleActiveTabChange = async (tabKey: string) => { + setActiveTab(tabKey); + const values = lastSearchedValuesRef.current; + if (!values) return; + + // 중복 API 통신 방지 필터 캐싱 디스패치 + const existing = rowDataMap.get(tabKey); + if (existing && existing.length > 0) return; + + try { + setIsLoading(true); + await loadGridData(tabKey, values); + } catch { + alert("데이터 조회 중 오류가 발생했습니다."); + } finally { + setIsLoading(false); + } + }; + return ( - <> - - +
+ + + {/* 정중앙 5rem 크기 고정 격리형 로딩 애니메이션 */} + {isLoading && ( +
+
+ +
+
+ )} + + + +
+ void handleActiveTabChange(v)} className="w-full" variant="default"> + + {columnInfo.map((info) => ( + + {info.tabLabel} + + ))} + + + {columnInfo.map((info) => { + const tabKey = info.tabKey; + const rowData = rowDataMap.get(tabKey) ?? []; + + const gridColumnInfo: ColumnInfo = { + columns: info.columns, + columnLayout: info.columnLayout, + }; + + return ( + + { + setRowDataMap((prev) => { + const next = new Map(prev); + const cur = next.get(tabKey) ?? []; + const n = typeof updater === "function" ? updater(cur) : updater; + next.set(tabKey, n); + return next; + }); + }} + isExcelImport={false} + isExcelExport={true} + showHeaderCheckbox={false} + fixedColumns={info.fixedColumns} + enableSort={false} + cellStyleCallback={({ row }) => { + const rowType = String(row.rowType ?? ''); + if (rowType === 'DETAIL') return undefined; + if (rowType === 'BIZ_SUM') return 'rg-cell-rowtype-biz-sum'; + if (rowType === 'SUMMARY') return 'rg-cell-rowtype-summary'; + return 'rg-cell-rowtype-other'; + }} + /> + + ); + })} + +
+
); }; diff --git a/apps/st/src/components/util/column/total/sewerageReuse/SewerageGrayWaterTab2Column.ts b/apps/st/src/components/util/column/total/sewerageReuse/SewerageGrayWaterTab2Column.ts new file mode 100644 index 000000000..6987edb66 --- /dev/null +++ b/apps/st/src/components/util/column/total/sewerageReuse/SewerageGrayWaterTab2Column.ts @@ -0,0 +1,48 @@ +import { ValueType } from "realgrid"; + +export const SewerageGrayWaterTab2ColumnConfig = { + tabKey: "ST00100002", + tabLabel: "중수도 현황", + rowStartNum: 2, // 2단 그룹 헤더이므로 rowStartNum은 2로 지정합니다. + fixedColumns: 1, + columns: [ + // 행정구역 그룹 소속 + { name: "ctpvNm", fieldName: "ctpvNm", header: { text: "시도" }, editable: false }, + { name: "sggNm", fieldName: "sggNm", header: { text: "시군구" }, editable: false }, + { name: "admdstNm", fieldName: "admdstNm", header: { text: "행정구역명" }, editable: false }, + + // 단일 마스터 컬럼들 (image_103ea5 스펙 반영) + { name: "fcltNm", fieldName: "fcltNm", header: { text: "건축물명/\n개발사업명" }, editable: false }, + { name: "bldgSeCn", fieldName: "bldgSeCn", header: { text: "시설물용도/\n개발사업종류" }, editable: false }, + { name: "fcltPstnCn", fieldName: "fcltPstnCn", header: { text: "주소" }, editable: false }, + { name: "lglTrgtYn", fieldName: "lglTrgtYn", header: { text: "법적의무\n대상여부\n(대상/미대상)" }, editable: false }, + { name: "cmptnStrtYmd", fieldName: "cmptnStrtYmd", header: { text: "설치완료일\nyyyy-mm-dd" }, editable: false }, + { name: "oprtngStrtYmd", fieldName: "oprtngStrtYmd", header: { text: "가동개시일\nyyyy-mm-dd" }, editable: false }, + + // 시설현황 그룹 소속 + { name: "archWholArea", fieldName: "archWholArea", header: { text: "건축연면적\n(㎡)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "prcsCpct", fieldName: "prcsCpct", header: { text: "처리용량\n(㎡/일)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + + // 이용 현황 소속 컬럼들 + { name: "wtrUseQnt", fieldName: "wtrUseQnt", header: { text: "물 사용량\n(㎡/년)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "ruswtrUtztnQnt", fieldName: "ruswtrUtztnQnt", header: { text: "중수도\n이용량(㎡/년)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "sbstCntUtztnQnt", fieldName: "sbstCntUtztnQnt", header: { text: "대체수\n이용량(㎡/년)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "mnUsgCn", fieldName: "mnUsgCn", header: { text: "중수도용도" }, editable: false }, + { name: "ruswtrOperAmt", fieldName: "ruswtrOperAmt", header: { text: "연간운영비\n(백만원/년)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false } + ], + columnLayout: [ + { name: "areaGroup", header: { text: "행정구역" }, items: ["ctpvNm", "sggNm", "admdstNm"] }, + "fcltNm", + "bldgSeCn", + "fcltPstnCn", + "lglTrgtYn", + "cmptnStrtYmd", + "oprtngStrtYmd", + { name: "facilityStatusGroup", header: { text: "시설현황" }, items: ["archWholArea", "prcsCpct"] }, + "wtrUseQnt", + "ruswtrUtztnQnt", + "sbstCntUtztnQnt", + "mnUsgCn", + "ruswtrOperAmt" + ] +}; \ No newline at end of file diff --git a/apps/st/src/components/util/column/total/sewerageReuse/SewerageRainwaterTab3Column.ts b/apps/st/src/components/util/column/total/sewerageReuse/SewerageRainwaterTab3Column.ts new file mode 100644 index 000000000..84d3d0a7e --- /dev/null +++ b/apps/st/src/components/util/column/total/sewerageReuse/SewerageRainwaterTab3Column.ts @@ -0,0 +1,33 @@ +import { ValueType } from "realgrid"; + +export const SewerageRainwaterTab3ColumnConfig = { + tabKey: "ST00100003", + tabLabel: "빗물이용시설현황", + rowStartNum: 2, + fixedColumns: 3, + columns: [ + { name: "ctpvNm", fieldName: "ctpvNm", header: { text: "시도" }, editable: false }, + { name: "sggNm", fieldName: "sggNm", header: { text: "시군구" }, editable: false }, + { name: "admdstNm", fieldName: "admdstNm", header: { text: "행정구역명" }, editable: false }, + + { name: "fcltNm", fieldName: "fcltNm", header: { text: "시설명" }, editable: false }, + { name: "bdstTypCn", fieldName: "bdstTypCn", header: { text: "건축물용도" }, editable: false }, + { name: "fcltPstnCn", fieldName: "fcltPstnCn", header: { text: "위치(주소)" }, editable: false }, + { name: "lglTrgtYn", fieldName: "lglTrgtYn", header: { text: "법적의무 대상여부\n(대상/미대상)" }, editable: false }, + { name: "instlCmptnYmd", fieldName: "instlCmptnYmd", header: { text: "설치완료일\n(yyyy-mm-dd)" }, editable: false }, + { name: "instlAmt", fieldName: "instlAmt", header: { text: "설치비\n(백만원)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "rcptMynCn", fieldName: "rcptMynCn", header: { text: "집수면 종류" }, editable: false }, + { name: "filtrPrcsFcltYnExpln", fieldName: "filtrPrcsFcltYnExpln", header: { text: "여과등 처리시설 유무" }, editable: false }, + { name: "impdCpct", fieldName: "impdCpct", header: { text: "저류조 용량(㎥)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + + // 이용 현황 그룹 소속 + { name: "anlUseQnt", fieldName: "anlUseQnt", header: { text: "연간 이용량\n(㎥/년)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "exlnUtlzUsgExpln", fieldName: "exlnUtlzUsgExpln", header: { text: "이용용도" }, editable: false }, + { name: "anlOperAmt", fieldName: "anlOperAmt", header: { text: "연간운영비\n(백만원/년)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false } + ], + columnLayout: [ + { name: "areaGroup", header: { text: "행정구역" }, items: ["ctpvNm", "sggNm", "admdstNm"] }, + "fcltNm", "bdstTypCn", "fcltPstnCn", "lglTrgtYn", "instlCmptnYmd", "instlAmt", "rcptMynCn", "filtrPrcsFcltYnExpln", "impdCpct", + "anlUseQnt", "exlnUtlzUsgExpln", "anlOperAmt" + ] +}; \ No newline at end of file diff --git a/apps/st/src/components/util/column/total/sewerageReuse/SewerageReuseTab1Column.ts b/apps/st/src/components/util/column/total/sewerageReuse/SewerageReuseTab1Column.ts new file mode 100644 index 000000000..6534fa651 --- /dev/null +++ b/apps/st/src/components/util/column/total/sewerageReuse/SewerageReuseTab1Column.ts @@ -0,0 +1,89 @@ +import { ValueType } from "realgrid"; + +export const SewerageReuseTab1ColumnConfig = { + tabKey: "ST00100001", + tabLabel: "공공하수처리수 재이용", + rowStartNum: 4, // 3단 계층 다단 헤더 구조이므로 4로 유지합니다. + fixedColumns: 1, + columns: [ + { name: "ctpvNm", fieldName: "ctpvNm", header: { text: "시도" }, editable: false }, + { name: "sggNm", fieldName: "sggNm", header: { text: "시군구" }, editable: false }, + { name: "admdstNm", fieldName: "admdstNm", header: { text: "행정구역명" }, editable: false }, + + // 하수처리장 + { name: "swtrmtFcltNm", fieldName: "swtrmtFcltNm", header: { text: "시설명" }, editable: false }, + { name: "sncnYmd", fieldName: "sncnYmd", header: { text: "인가일\n(년/월/일)" }, editable: false }, + { name: "prcsFcltCpct", fieldName: "prcsFcltCpct", header: { text: "시설용량\n(㎥/일)" }, editable: false }, + { name: "prcsQnt", fieldName: "prcsQnt", header: { text: "연간\n하수처리량\n(천톤/년)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "lglYn", fieldName: "lglYn", header: { text: "법적의무대상\n(대상/미대상)" }, editable: false }, + + // 재이용시설 설치시 소속 + { name: "instlYn", fieldName: "instlYn", header: { text: "설치유무\n(유/무)" }, editable: false }, + { name: "oprtngStrtYmd", fieldName: "oprtngStrtYmd", header: { text: "가동개시일\nyyyy-mm-dd" }, editable: false }, + { name: "instlAprvYmd", fieldName: "instlAprvYmd", header: { text: "설치승인일\nyyyy-mm-dd" }, editable: false }, + { name: "rprcsFcltCpct", fieldName: "rprcsFcltCpct", header: { text: "용량(톤/일)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "mtcnsNm", fieldName: "mtcnsNm", header: { text: "공법/기자재" }, editable: false }, + { name: "ruswtPplLen", fieldName: "ruswtPplLen", header: { text: "공급관로길이\n(km)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.00", editable: false }, + { name: "ruswtPumpLen", fieldName: "ruswtPumpLen", header: { text: "공급펌프\n(톤/일)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + + // 하수처리수 재이용현황 + { name: "wholUseQnt", fieldName: "wholUseQnt", header: { text: "총계" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "ithlWshwtrQnt", fieldName: "ithlWshwtrQnt", header: { text: "계" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "ithlClnQty", fieldName: "ithlClnQty", header: { text: "세척수" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "ithlFrzgQty", fieldName: "ithlFrzgQty", header: { text: "냉각수" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "ithlLdhcUswtrQnt", fieldName: "ithlLdhcUswtrQnt", header: { text: "청소수" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "ithlDltnUswtrQnt", fieldName: "ithlDltnUswtrQnt", header: { text: "조경용수" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "ithlRuswtrQnt", fieldName: "ithlRuswtrQnt", header: { text: "희석용수" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "ithlEtcQnt", fieldName: "ithlEtcQnt", header: { text: "장내중수도" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "ithlUnsdQnt", fieldName: "ithlUnsdQnt", header: { text: "장내기타용수" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + + { name: "otcntrUswtrWhol", fieldName: "otcntrUswtrWhol", header: { text: "계" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "otcntrClnRstrmQnt", fieldName: "otcntrClnRstrmQnt", header: { text: "청소·화장실\n용수" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "otcntrWshwtrQnt", fieldName: "otcntrWshwtrQnt", header: { text: "세척·살수용수" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "otcntrLdhcUswtrQnt", fieldName: "otcntrLdhcUswtrQnt", header: { text: "조경용수" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "otcntrHdrplUswtrQnt", fieldName: "otcntrHdrplUswtrQnt", header: { text: "친수용수" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "otcntrRvrKeepQnt", fieldName: "otcntrRvrKeepQnt", header: { text: "하천유지용수" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "agrcltUswtrPstnQnt", fieldName: "agrcltUswtrPstnQnt", header: { text: "농업용수" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "ugwtrChargQnt", fieldName: "ugwtrChargQnt", header: { text: "지하수충전" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "mdsrUswtrQnt", fieldName: "mdsrUswtrQnt", header: { text: "공업용수" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + + { name: "rtlztnRt", fieldName: "rtlztnRt", header: { text: "재이용률\n(%)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false } + ], + columnLayout: [ + { name: "areaGroup", header: { text: "행정구역" }, items: ["ctpvNm", "sggNm", "admdstNm"] }, + { name: "seweragePlantGroup", header: { text: "하수처리장" }, items: ["swtrmtFcltNm", "sncnYmd", "prcsFcltCpct", "prcsQnt", "lglYn"] }, + { + name: "reuseFacilityGroup", + header: { text: "재이용시설" }, + items: [ + "instlYn", + { + name: "installationGroup", + header: { text: "재이용시설 설치시" }, + items: [ + "oprtngStrtYmd", + "instlAprvYmd", + { + // ★ 핵심 교정: 이미지 명세에 맞춰 내부에 서브 그룹 헤더 추가 완료 + name: "reProcessingFacilityGroup", + header: { text: "재처리시설" }, + items: ["rprcsFcltCpct", "mtcnsNm"] + }, + "ruswtPplLen", + "ruswtPumpLen" + ] + } + ] + }, + { + name: "reuseStatusGroup", + header: { text: "하수처리수 재이용현황(천톤/년)" }, + items: [ + "wholUseQnt", + { name: "internalWaterGroup", header: { text: "장내 용수" }, items: ["ithlWshwtrQnt", "ithlClnQty", "ithlFrzgQty", "ithlLdhcUswtrQnt", "ithlDltnUswtrQnt", "ithlRuswtrQnt", "ithlEtcQnt", "ithlUnsdQnt"] }, + { name: "externalWaterGroup", header: { text: "장외 용수" }, items: ["otcntrUswtrWhol", "otcntrClnRstrmQnt", "otcntrWshwtrQnt", "otcntrLdhcUswtrQnt", "otcntrHdrplUswtrQnt", "otcntrRvrKeepQnt", "agrcltUswtrPstnQnt", "ugwtrChargQnt", "mdsrUswtrQnt"] } + ] + }, + "rtlztnRt" + ] +}; \ No newline at end of file diff --git a/apps/st/src/components/util/column/total/sewerageReuse/SewerageWwtRtlztnRegTab4Column.ts b/apps/st/src/components/util/column/total/sewerageReuse/SewerageWwtRtlztnRegTab4Column.ts new file mode 100644 index 000000000..2d51a5354 --- /dev/null +++ b/apps/st/src/components/util/column/total/sewerageReuse/SewerageWwtRtlztnRegTab4Column.ts @@ -0,0 +1,28 @@ + + + +export const SewerageWwtRtlztnRegTab4ColumnConfig = { + tabKey: "ST00100004", + tabLabel: "하·폐수처리수재이용시설설계시공업등록현황", + rowStartNum: 2, // 행정구역 2단 묶음 헤더 구조이므로 2로 지정합니다. + fixedColumns: 1, + columns: [ + // 행정구역 그룹 소속 + { name: "ctpvNm", fieldName: "ctpvNm", header: { text: "시도" }, editable: false }, + { name: "sggNm", fieldName: "sggNm", header: { text: "시군구" }, editable: false }, + { name: "admdstNm", fieldName: "admdstNm", header: { text: "행정구역명" }, editable: false }, + + // 메인 마스터 문자열 지표들 (화면 그리드 명세서 순서 100% 매칭) + { name: "bplcNm", fieldName: "bplcNm", header: { text: "상호(명칭)" }, editable: false }, + { name: "rprsvNm", fieldName: "rprsvNm", header: { text: "대표자" }, editable: false }, + { name: "bplcPstnCn", fieldName: "bplcPstnCn", header: { text: "주소" }, editable: false }, + { name: "regDt", fieldName: "regDt", header: { text: "등록일\nyyyy-mm-dd" }, editable: false } + ], + columnLayout: [ + { name: "areaGroup", header: { text: "행정구역" }, items: ["ctpvNm", "sggNm", "admdstNm"] }, + "bplcNm", + "rprsvNm", + "bplcPstnCn", + "regDt" + ] +}; \ No newline at end of file From 84ae60edd6c8f2d3e474c17235d65afa6577a5fd Mon Sep 17 00:00:00 2001 From: yangsein Date: Mon, 15 Jun 2026 00:57:39 +0900 Subject: [PATCH 08/12] =?UTF-8?q?feat:[=EC=A7=91=EA=B3=84=ED=91=9C]=2011?= =?UTF-8?q?=ED=95=98=EC=88=98=EB=B0=8F=20=EB=B6=84=EB=87=A8=EC=B0=8C?= =?UTF-8?q?=EA=BA=BC=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../total/SewageExcretaSlurgeTot.tsx | 206 +++++++++++++++++- .../DigestionTankTab2Column.ts | 42 ++++ .../HumanWasteSludgeTab4Column.ts | 37 ++++ .../SewageSludgeOperTab3Column.ts | 30 +++ .../SludgeProcessingTab1Column.ts | 103 +++++++++ .../statistics-sludge-total-interface.ts | 168 ++++++++++++++ 6 files changed, 583 insertions(+), 3 deletions(-) create mode 100644 apps/st/src/components/util/column/total/sludgeProcessing/DigestionTankTab2Column.ts create mode 100644 apps/st/src/components/util/column/total/sludgeProcessing/HumanWasteSludgeTab4Column.ts create mode 100644 apps/st/src/components/util/column/total/sludgeProcessing/SewageSludgeOperTab3Column.ts create mode 100644 apps/st/src/components/util/column/total/sludgeProcessing/SludgeProcessingTab1Column.ts create mode 100644 apps/st/src/entities/statistics/model/statistics-total/statistics-sludge-total-interface.ts diff --git a/apps/st/src/components/statistics/total/SewageExcretaSlurgeTot.tsx b/apps/st/src/components/statistics/total/SewageExcretaSlurgeTot.tsx index c384f1461..c251aff5c 100644 --- a/apps/st/src/components/statistics/total/SewageExcretaSlurgeTot.tsx +++ b/apps/st/src/components/statistics/total/SewageExcretaSlurgeTot.tsx @@ -1,10 +1,210 @@ +import { useRef, useState } from "react"; + +import { useAlertStore } from "@/shared/lib"; +import { LoadingWithAnimation, Tabs, TabsContent, TabsList, TabsTrigger } from "@common/components"; + import SearchForm from "@/components/module/SearchForm"; +import DataGrid from "@/components/module/DataGrid"; +import type { SearchFormValues } from "@/components/type/SearchForm.type"; +import { toGridRows } from "@/components/util/grid/gridFunction"; + +// 4개 탭 컬럼 설정 임포트 +import { SludgeProcessingTab1ColumnConfig } from "@/components/util/column/total/sludgeProcessing/SludgeProcessingTab1Column"; +import { DigestionTankTab2ColumnConfig } from "@/components/util/column/total/sludgeProcessing/DigestionTankTab2Column"; +import { SewageSludgeOperTab3ColumnConfig } from "@/components/util/column/total/sludgeProcessing/SewageSludgeOperTab3Column"; +import { HumanWasteSludgeTab4ColumnConfig } from "@/components/util/column/total/sludgeProcessing/HumanWasteSludgeTab4Column"; + +// API 인스턴스 및 인터페이스 임포트 (타입 안전성 확보) +import { statisticsTotalStatusApi } from "@/entities/statistics/api/statistics-total-service"; +import type { + SludgeSummaryRequest, + SludgeProcessingTab1Response, + DigestionTankOperTab2Response, + SewageSludgeOperTab3Response, + HumanWasteSludgeTab4Response +} from "@/entities/statistics/model/statistics-total/statistics-sludge-total-interface"; +import CellColorInfo from "./CellColorInfo"; + +// 탭 순회 설정 +const SludgeColumnListTot = [ + SludgeProcessingTab1ColumnConfig, + DigestionTankTab2ColumnConfig, + SewageSludgeOperTab3ColumnConfig, + HumanWasteSludgeTab4ColumnConfig +].filter(Boolean); + +const firstTabKey = SludgeColumnListTot[0]?.tabKey ?? "ST00110001"; const SewageExcretaSlurgeTot = () => { + const { setMessage: alert } = useAlertStore((state) => state); + + const [rowDataMap, setRowDataMap] = useState[]>>(new Map()); + const [isLoading, setIsLoading] = useState(false); + const [activeTab, setActiveTab] = useState(firstTabKey); + const lastSearchedValuesRef = useRef(null); + + const columnInfo = SludgeColumnListTot; + + const handleReset = () => { + setRowDataMap(new Map()); + setActiveTab(firstTabKey); + lastSearchedValuesRef.current = null; + }; + +const loadGridData = async (tabKey: string, values: SearchFormValues): Promise => { + setIsLoading(true); + const param: SludgeSummaryRequest = { + ctpvNm: values.sido === "ALL" ? "" : values.sido, + sggNm: values.gugun === "ALL" ? "" : values.gugun, + sewerBzmnCd: values.bsnmCd, + statsYr: values.year, + rowDataYn: values.isDetail, + summaryYn: values.isSummary !== 'ALL', + summaryYearCnt: values.isSummary !== 'ALL' ? Number(values.isSummary) : undefined + }; + + try { + // [TAB1] 하수및분뇨찌꺼기 > 찌꺼기처리시설현황 + if (tabKey === "ST00110001") { + const response: SludgeProcessingTab1Response = await statisticsTotalStatusApi.getSludgeProcessingTab1List(param); + if (response?.status === "success") { + setRowDataMap((prev) => new Map(prev).set(tabKey, toGridRows(response.data as object[]))); + return true; + } + } + // [TAB2] 소화조 운영현황 + else if (tabKey === "ST00110002") { + const response: DigestionTankOperTab2Response = await statisticsTotalStatusApi.getSludgeTab2List(param); + if (response?.status === "success") { + setRowDataMap((prev) => new Map(prev).set(tabKey, toGridRows(response.data as object[]))); + return true; + } + } + // [TAB3] 하수찌꺼기 처리시설 운영현황 + else if (tabKey === "ST00110003") { + const response: SewageSludgeOperTab3Response = await statisticsTotalStatusApi.getSludgeTab3List(param); + if (response?.status === "success") { + setRowDataMap((prev) => new Map(prev).set(tabKey, toGridRows(response.data as object[]))); + return true; + } + } + // [TAB4] 분뇨찌꺼기 처리현황 + else if (tabKey === "ST00110004") { + const response: HumanWasteSludgeTab4Response = await statisticsTotalStatusApi.getSludgeTab4List(param); + if (response?.status === "success") { + setRowDataMap((prev) => new Map(prev).set(tabKey, toGridRows(response.data as object[]))); + return true; + } + } + + alert("데이터 조회 중 오류가 발생했습니다."); + return false; + } catch { + alert("서버 연결 중 오류가 발생했습니다."); + return false; + } finally { + setIsLoading(false); + } + }; + + /** 검색 버튼 이벤트: SearchForm의 Values 타입을 기반으로 호출 */ + const handleSearch = async (values: SearchFormValues) => { + try { + setIsLoading(true); + // 탭 변경 없이 현재 탭(activeTab)에 대해서만 명시적 타입 호출 + const ok = await loadGridData(activeTab, values); + if (ok) { + lastSearchedValuesRef.current = values; + } else { + lastSearchedValuesRef.current = null; + } + } catch (error) { + alert("데이터 조회 중 오류가 발생했습니다."); + lastSearchedValuesRef.current = null; + } finally { + setIsLoading(false); + } + }; + + /** 탭 변경 이벤트: 현재 탭 상태와 SearchForm의 마지막 값을 동기화 */ + const handleActiveTabChange = async (tabKey: string) => { + setActiveTab(tabKey); + + // 마지막 검색 값이 있으면 해당 탭만 새로 로드 + const values = lastSearchedValuesRef.current; + if (!values || rowDataMap.has(tabKey)) return; + + try { + setIsLoading(true); + await loadGridData(tabKey, values); + } catch (error) { + alert("데이터 조회 중 오류가 발생했습니다."); + } finally { + setIsLoading(false); + } + }; + return ( - <> - - +
+ + + {isLoading && ( +
+
+ +
+
+ )} + + + +
+ void handleActiveTabChange(v)} className="w-full" variant="default"> + + {columnInfo.map((info) => ( + {info.tabLabel} + ))} + + + {columnInfo.map((info) => { + const tabKey = info.tabKey; + const rowData = rowDataMap.get(tabKey) ?? []; + + return ( + + { + setRowDataMap((prev) => { + const next = new Map(prev); + const cur = next.get(tabKey) ?? []; + const n = typeof updater === "function" ? updater(cur) : updater; + next.set(tabKey, n); + return next; + }); + }} + isExcelImport={false} + isExcelExport={true} + showHeaderCheckbox={false} + fixedColumns={info.fixedColumns} + enableSort={false} + cellStyleCallback={({ row }) => { + const rowType = String(row.rowType ?? ''); + if (rowType === 'DETAIL') return undefined; + if (rowType === 'BIZ_SUM') return 'rg-cell-rowtype-biz-sum'; + if (rowType === 'SUMMARY') return 'rg-cell-rowtype-summary'; + return 'rg-cell-rowtype-other'; + }} + /> + + ); + })} + +
+
); }; diff --git a/apps/st/src/components/util/column/total/sludgeProcessing/DigestionTankTab2Column.ts b/apps/st/src/components/util/column/total/sludgeProcessing/DigestionTankTab2Column.ts new file mode 100644 index 000000000..65b1c50bc --- /dev/null +++ b/apps/st/src/components/util/column/total/sludgeProcessing/DigestionTankTab2Column.ts @@ -0,0 +1,42 @@ +import { ValueType } from "realgrid"; + +export const DigestionTankTab2ColumnConfig = { + tabKey: "ST00110002", + tabLabel: "소화조 운영현황", + rowStartNum: 3, // 행정구역, 하수처리장명 등 기본 정보 이후 다단 헤더가 있으므로 3단 지정 + fixedColumns: 1, + columns: [ + { name: "ctpvNm", fieldName: "ctpvNm", header: { text: "시도" }, editable: false }, + { name: "sggNm", fieldName: "sggNm", header: { text: "시군구" }, editable: false }, + { name: "admdstNm", fieldName: "admdstNm", header: { text: "행정구역명" }, editable: false }, + { name: "fcltNm", fieldName: "fcltNm", header: { text: "하수\n처리장명" }, editable: false }, + + { name: "digesterYn", fieldName: "digesterYn", header: { text: "소화조 유/무\n(Y/N)선택" }, editable: false }, + { name: "dsgnCpct", fieldName: "dsgnCpct", header: { text: "설계\n용량(㎥)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "actlOperCpct", fieldName: "actlOperCpct", header: { text: "실제운전\n용량(㎥)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "inputQnt", fieldName: "inputQnt", header: { text: "투입량\n(㎥/일)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "tsConc", fieldName: "tsConc", header: { text: "투입 TS 농도\n(%)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "oprtngDays", fieldName: "oprtngDays", header: { text: "가동일수\n(연간)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "gnrtGasQnt", fieldName: "gnrtGasQnt", header: { text: "발생가스량\n(㎥/일)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + + // 소화가스 이용현황(하위 항목) + { name: "gasTotal", fieldName: "gasTotal", header: { text: "계" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "gasSale", fieldName: "gasSale", header: { text: "판매" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "gasGen", fieldName: "gasGen", header: { text: "발전" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "gasSelf", fieldName: "gasSelf", header: { text: "자체활용" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "gasEtc", fieldName: "gasEtc", header: { text: "기타(CNG등)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + + { name: "surplusGas", fieldName: "surplusGas", header: { text: "잉여가스량\n(㎥/일)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false } + ], + columnLayout: [ + { name: "areaGroup", header: { text: "행정구역" }, items: ["ctpvNm", "sggNm", "admdstNm"] }, + "fcltNm", + { name: "operGroup", header: { text: "소화조 운영현황" }, items: ["digesterYn", "dsgnCpct", "actlOperCpct", "inputQnt", "tsConc", "oprtngDays", "gnrtGasQnt", + { + name: "gasUsageGroup", + header: { text: "소화가스 이용현황(㎥/일)" }, + items: ["gasTotal", "gasSale", "gasGen", "gasSelf", "gasEtc"] + },"surplusGas" + ] }, + ] +}; \ No newline at end of file diff --git a/apps/st/src/components/util/column/total/sludgeProcessing/HumanWasteSludgeTab4Column.ts b/apps/st/src/components/util/column/total/sludgeProcessing/HumanWasteSludgeTab4Column.ts new file mode 100644 index 000000000..df6a57742 --- /dev/null +++ b/apps/st/src/components/util/column/total/sludgeProcessing/HumanWasteSludgeTab4Column.ts @@ -0,0 +1,37 @@ +import { ValueType } from "realgrid"; + +export const HumanWasteSludgeTab4ColumnConfig = { + tabKey: "ST00110004", + tabLabel: "분뇨찌꺼기(슬러지)처리현황", + rowStartNum: 2, // 행정구역 2단 레이아웃 + fixedColumns: 1, + columns: [ + { name: "ctpvNm", fieldName: "ctpvNm", header: { text: "시도" }, editable: false }, + { name: "sggNm", fieldName: "sggNm", header: { text: "시군구" }, editable: false }, + { name: "admdstNm", fieldName: "admdstNm", header: { text: "행정구역명" }, editable: false }, + { name: "fcltNm", fieldName: "fcltNm", header: { text: "분뇨\n처리장명" }, editable: false }, + + { name: "gnam", fieldName: "gnam", header: { text: "발생량\n(A)=(B)+(C)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "wholDspsQnt", fieldName: "wholDspsQnt", header: { text: "계(B)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "rcycQnt", fieldName: "rcycQnt", header: { text: "재활용" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "olandLfvlm", fieldName: "olandLfvlm", header: { text: "육상매립" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "incnrQnt", fieldName: "incnrQnt", header: { text: "소각" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "oceanInputQnt", fieldName: "oceanInputQnt", header: { text: "해양투기" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "fuelQnt", fieldName: "fuelQnt", header: { text: "연료화" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "etcQnt", fieldName: "etcQnt", header: { text: "기타" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "nondspsQnt", fieldName: "nondspsQnt", header: { text: "미처분량\n(이월량)(C)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "swtrmtFcltNm", fieldName: "swtrmtFcltNm", header: { text: "타시설\n연계여부" }, editable: false } + ], + columnLayout: [ + { name: "areaGroup", header: { text: "행정구역" }, items: ["ctpvNm", "sggNm", "admdstNm"] }, + "fcltNm", + "gnam", + { + name: "dspsGroup", + header: { text: "처분량" }, + items: ["wholDspsQnt", "rcycQnt", "olandLfvlm", "incnrQnt", "oceanInputQnt", "fuelQnt", "etcQnt"] + }, + "nondspsQnt", + "swtrmtFcltNm" + ] +}; \ No newline at end of file diff --git a/apps/st/src/components/util/column/total/sludgeProcessing/SewageSludgeOperTab3Column.ts b/apps/st/src/components/util/column/total/sludgeProcessing/SewageSludgeOperTab3Column.ts new file mode 100644 index 000000000..c7a5786ce --- /dev/null +++ b/apps/st/src/components/util/column/total/sludgeProcessing/SewageSludgeOperTab3Column.ts @@ -0,0 +1,30 @@ +import { ValueType } from "realgrid"; + +export const SewageSludgeOperTab3ColumnConfig = { + tabKey: "ST00110003", + tabLabel: "하수찌꺼기 처리시설 운영현황", + rowStartNum: 2, + fixedColumns: 1, + columns: [ + { name: "ctpvNm", fieldName: "ctpvNm", header: { text: "시도" }, editable: false }, + { name: "sggNm", fieldName: "sggNm", header: { text: "시군구" }, editable: false }, + { name: "admdstNm", fieldName: "admdstNm", header: { text: "행정구역명" }, editable: false }, + { name: "fcltNm", fieldName: "fcltNm", header: { text: "찌꺼기처리\n시설명" }, editable: false }, + + { name: "prcsMthExpln", fieldName: "prcsMthExpln", header: { text: "처리방식" }, editable: false }, + { name: "fcltCpct", fieldName: "fcltCpct", header: { text: "시설용량\n(톤/일)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "anlPrcsQnt", fieldName: "anlPrcsQnt", header: { text: "연간처리량\n(톤/년)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "dayAvgOprtngHr", fieldName: "dayAvgOprtngHr", header: { text: "일평균\n가동시간\n(1~24)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "anlOprtngDayCnt", fieldName: "anlOprtngDayCnt", header: { text: "연중 가동일\n(1~330)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "oprtngRt", fieldName: "oprtngRt", header: { text: "가동율(%)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false } + ], + columnLayout: [ + { name: "areaGroup", header: { text: "행정구역" }, items: ["ctpvNm", "sggNm", "admdstNm"] }, + "fcltNm", + { + name: "operStatusGroup", + header: { text: "처리시설 운영현황" }, + items: ["prcsMthExpln", "fcltCpct", "anlPrcsQnt", "dayAvgOprtngHr", "anlOprtngDayCnt", "oprtngRt"] + } + ] +}; \ No newline at end of file diff --git a/apps/st/src/components/util/column/total/sludgeProcessing/SludgeProcessingTab1Column.ts b/apps/st/src/components/util/column/total/sludgeProcessing/SludgeProcessingTab1Column.ts new file mode 100644 index 000000000..d61e6a535 --- /dev/null +++ b/apps/st/src/components/util/column/total/sludgeProcessing/SludgeProcessingTab1Column.ts @@ -0,0 +1,103 @@ +import { ValueType } from "realgrid"; + +export const SludgeProcessingTab1ColumnConfig = { + tabKey: "ST00110001", + tabLabel: "찌꺼기(슬러지)처리시설현황", + rowStartNum: 4, // 3단 그룹 대단위 레이아웃 명세이므로 4로 바인딩합니다. + fixedColumns: 1, + columns: [ + { name: "ctpvNm", fieldName: "ctpvNm", header: { text: "시도" }, editable: false }, + { name: "sggNm", fieldName: "sggNm", header: { text: "시군구" }, editable: false }, + { name: "admdstNm", fieldName: "admdstNm", header: { text: "행정구역명" }, editable: false }, + { name: "fcltNm", fieldName: "fcltNm", header: { text: "하수\n처리장명" }, editable: false }, + + // 찌꺼기발생량 + { name: "gnamWhol", fieldName: "gnamWhol", header: { text: "합계" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "itslfQnt", fieldName: "itslfQnt", header: { text: "자체" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "otsdInflwQnt", fieldName: "otsdInflwQnt", header: { text: "외부유입량" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + + // 이송 및 함수율 지표들 + { name: "transfTmplntNm", fieldName: "transfTmplntNm", header: { text: "이송처리장" }, editable: false }, + { name: "transfPrcsQnt", fieldName: "transfPrcsQnt", header: { text: "이송처리량\n(㎥/년)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "srpFncRt", fieldName: "srpFncRt", header: { text: "함수율\n(%, 탈수기준)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "wholSrpPrcsQnt", fieldName: "wholSrpPrcsQnt", header: { text: "찌꺼기\n처리량 합계\n(톤/년)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + + // 자체 처리 1차 대문 항목들 + { name: "itslfTs1Whol", fieldName: "itslfTs1Whol", header: { text: "자체찌꺼기\n처리량 소계" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "itslfTs1RclmQnt", fieldName: "itslfTs1RclmQnt", header: { text: "매립" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + + // 소각 후 처리(2차) 서브 항목들 + { name: "itslfTs1IncnrQnt", fieldName: "itslfTs1IncnrQnt", header: { text: "소각" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "incnrPrcsWhol", fieldName: "incnrPrcsWhol", header: { text: "소계" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "incnrPrcsLfvlm", fieldName: "incnrPrcsLfvlm", header: { text: "매립" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "incnrPrcsPrdctRmtrlQnt", fieldName: "incnrPrcsPrdctRmtrlQnt", header: { text: "제품원료" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "incnrPrcsFmlyrQnt", fieldName: "incnrPrcsFmlyrQnt", header: { text: "복토재" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "incnrPrcsFcltNm", fieldName: "incnrPrcsFcltNm", header: { text: "시설(업체명)" }, editable: false }, + + // 고화 처리군 + { name: "itslfTs1AltdQnt", fieldName: "itslfTs1AltdQnt", header: { text: "고화" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "stbznPrcsWhol", fieldName: "stbznPrcsWhol", header: { text: "소계" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "stbznPrcsLfvlm", fieldName: "stbznPrcsLfvlm", header: { text: "매립" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "stbznPrcsFmlyrQnt", fieldName: "stbznPrcsFmlyrQnt", header: { text: "복토재" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "stbznPrcsFcltNm", fieldName: "stbznPrcsFcltNm", header: { text: "시설(업체명)" }, editable: false }, + + // 건조 처리군 + { name: "itslfTs1DryQnt", fieldName: "itslfTs1DryQnt", header: { text: "건조" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "dryPrcsWhol", fieldName: "dryPrcsWhol", header: { text: "소계" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "dryPrcsLfvlm", fieldName: "dryPrcsLfvlm", header: { text: "매립" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "dryPrcsIncnrQnt", fieldName: "dryPrcsIncnrQnt", header: { text: "소각" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "dryPrcsFuelQnt", fieldName: "dryPrcsFuelQnt", header: { text: "연료" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "dryPrcsPrdctRmtrlQnt", fieldName: "dryPrcsPrdctRmtrlQnt", header: { text: "제품원료" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "dryPrcsFmlyrQnt", fieldName: "dryPrcsFmlyrQnt", header: { text: "복토재" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "dryPrcsCmpstgQnt", fieldName: "dryPrcsCmpstgQnt", header: { text: "퇴비화" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "dryPrcsFcltNm", fieldName: "dryPrcsFcltNm", header: { text: "시설(업체명)" }, editable: false }, + + // 탄화 처리군 + { name: "itslfTs1FuelQnt", fieldName: "itslfTs1FuelQnt", header: { text: "탄화" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "fuelPrcsWhol", fieldName: "fuelPrcsWhol", header: { text: "소계" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "fuelPrcsIncnrQnt", fieldName: "fuelPrcsIncnrQnt", header: { text: "소각" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "fuelPrcsFuelQnt", fieldName: "fuelPrcsFuelQnt", header: { text: "연료" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "fuelPrcsFcltNm", fieldName: "fuelPrcsFcltNm", header: { text: "시설(업체명)" }, editable: false }, + + // 퇴비화 독립군 + { name: "itslfTs1CmpstgQnt", fieldName: "itslfTs1CmpstgQnt", header: { text: "퇴비화" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "cmpsPrcsFcltBzentyNm", fieldName: "cmpsPrcsFcltBzentyNm", header: { text: "시설(업체명)" }, editable: false }, + + // 외부(민간) 위탁 처리현황 + { name: "otsdCnsgnWhol", fieldName: "otsdCnsgnWhol", header: { text: "소계" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "otsdCnsgnRclmQnt", fieldName: "otsdCnsgnRclmQnt", header: { text: "매립" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "otsdCnsgnIncnrQnt", fieldName: "otsdCnsgnIncnrQnt", header: { text: "소각" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "otsdCnsgnFuelPwplQnt", fieldName: "otsdCnsgnFuelPwplQnt", header: { text: "연료" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "otsdCnsgnPrdctRmtrlQnt", fieldName: "otsdCnsgnPrdctRmtrlQnt", header: { text: "제품원료" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "otsdCnsgnFmlyrQnt", fieldName: "otsdCnsgnFmlyrQnt", header: { text: "복토재" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "otsdCnsgnCmpstgQnt", fieldName: "otsdCnsgnCmpstgQnt", header: { text: "퇴비화" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "otsdCnsgnFcltNm", fieldName: "otsdCnsgnFcltNm", header: { text: "시설(업체명)" }, editable: false }, + + // 수도권 광역 위탁 처리량 + { name: "cparTs2Whol", fieldName: "cparTs2Whol", header: { text: "수도권광역\n위탁처리량\n(톤/년)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false } + ], + columnLayout: [ + { name: "areaGroup", header: { text: "행정구역" }, items: ["ctpvNm", "sggNm", "admdstNm"] }, + "fcltNm", + { name: "gnamGroup", header: { text: "찌꺼기발생량(톤/년, 탈수기준)" }, items: ["gnamWhol", "itslfQnt", "otsdInflwQnt"] }, + "transfTmplntNm", "transfPrcsQnt", "srpFncRt", "wholSrpPrcsQnt", + { + name: "itslfProcessingGroup", + header: { text: "자체 찌꺼기 처리량 현황(톤/년)" }, + items: [ + "itslfTs1Whol", "itslfTs1RclmQnt","itslfTs1IncnrQnt", + { name: "incnrGroup", header: { text: "소각 후 처리(2차)" }, items: ["incnrPrcsWhol", "incnrPrcsLfvlm", "incnrPrcsPrdctRmtrlQnt", "incnrPrcsFmlyrQnt", "incnrPrcsFcltNm"] }, + "itslfTs1AltdQnt", + { name: "stbznGroup", header: { text: "고화 후 처리(2차)" }, items: [ "stbznPrcsWhol", "stbznPrcsLfvlm", "stbznPrcsFmlyrQnt", "stbznPrcsFcltNm"] }, + "itslfTs1DryQnt", + { name: "dryGroup", header: { text: "건조 후 처리(2차)" }, items: ["dryPrcsWhol", "dryPrcsLfvlm", "dryPrcsIncnrQnt", "dryPrcsFuelQnt", "dryPrcsPrdctRmtrlQnt", "dryPrcsFmlyrQnt", "dryPrcsCmpstgQnt", "dryPrcsFcltNm"] }, + "itslfTs1FuelQnt", + { name: "fuelGroup", header: { text: "탄화 후 처리(2차)" }, items: ["fuelPrcsWhol", "fuelPrcsIncnrQnt", "fuelPrcsFuelQnt", "fuelPrcsFcltNm"] }, + "itslfTs1CmpstgQnt", + { name: "cmpsGroup", header: { text: "퇴비화" }, items: ["cmpsPrcsFcltBzentyNm"] } + ] + }, + { name: "otsdGroup", header: { text: "외부(민간) 위탁 처리량(톤/년)" }, items: ["otsdCnsgnWhol", "otsdCnsgnRclmQnt", "otsdCnsgnIncnrQnt", "otsdCnsgnFuelPwplQnt", "otsdCnsgnPrdctRmtrlQnt", "otsdCnsgnFmlyrQnt", "otsdCnsgnCmpstgQnt", "otsdCnsgnFcltNm"] }, + "cparTs2Whol" + ] +}; \ No newline at end of file diff --git a/apps/st/src/entities/statistics/model/statistics-total/statistics-sludge-total-interface.ts b/apps/st/src/entities/statistics/model/statistics-total/statistics-sludge-total-interface.ts new file mode 100644 index 000000000..bae596f83 --- /dev/null +++ b/apps/st/src/entities/statistics/model/statistics-total/statistics-sludge-total-interface.ts @@ -0,0 +1,168 @@ +/** 11. 하수및분뇨찌꺼기 공통 검색 파라미터 DTO 규격 */ +export interface SludgeSummaryRequest { + ctpvNm?: string; + sggNm?: string; + statsYr?: string; + sewerBzmnCd?: string; + rowDataYn?: boolean; + summaryYn?: boolean; + summaryYearCnt?: number; +} + +/** [TAB1] 찌꺼기(슬러지) 처리시설 현황 데이터 행 항목 인터페이스 */ +export interface SludgeProcessingTotalItem { + statsYr: string; + ctpvNm: string; + sggNm: string | null; + admdstNm: string; + admdstCd: string; + rowType: string; + + // 기본 정보 + fcltNm: string; + + // 찌꺼기 발생량(톤/년, 탈수기준) + gnamWhol: number; + itslfQnt: number; + otsdInflwQnt: number; + + // 이송 및 함수율 + transfTmplntNm: string; + transfPrcsQnt: number; + srpFncRt: number; + wholSrpPrcsQnt: number; + + // 자체 찌꺼기 처리량 소계 및 매립 + itslfTs1Whol: number; + itslfTs1RclmQnt: number; + + // 소각 후 처리(2차) + itslfTs1IncnrQnt: number; + incnrPrcsWhol: number; + incnrPrcsLfvlm: number; + incnrPrcsPrdctRmtrlQnt: number; + incnrPrcsFmlyrQnt: number; + incnrPrcsFcltNm: string; + + // 고화 후 2차 + itslfTs1AltdQnt: number; + stbznPrcsWhol: number; + stbznPrcsLfvlm: number; + stbznPrcsFmlyrQnt: number; + stbznPrcsFcltNm: string; + + // 건조 후 2차 + itslfTs1DryQnt: number; + dryPrcsWhol: number; + dryPrcsLfvlm: number; + dryPrcsIncnrQnt: number; + dryPrcsFuelQnt: number; + dryPrcsPrdctRmtrlQnt: number; + dryPrcsFmlyrQnt: number; + dryPrcsCmpstgQnt: number; + dryPrcsFcltNm: string; + + // 탄화 후 2차 + itslfTs1FuelQnt: number; + fuelPrcsWhol: number; + fuelPrcsIncnrQnt: number; + fuelPrcsFuelQnt: number; + fuelPrcsFcltNm: string; + + // 퇴비화 + itslfTs1CmpstgQnt: number; + cmpsPrcsFcltBzentyNm: string; + + // 외부(민간) 위탁 처리량 + otsdCnsgnWhol: number; + otsdCnsgnRclmQnt: number; + otsdCnsgnIncnrQnt: number; + otsdCnsgnFuelPwplQnt: number; + otsdCnsgnPrdctRmtrlQnt: number; + otsdCnsgnFmlyrQnt: number; + otsdCnsgnCmpstgQnt: number; + otsdCnsgnFcltNm: string; + + // 수도권광역 위탁 처리량 + cparTs2Whol: number; +} + +/** TAB1 서버 단일 응답 랩퍼 규격 */ +export interface SludgeProcessingTab1Response { + status: string; + message?: string; + data: SludgeProcessingTotalItem[]; +} +// ============================================================================ +// [TAB2] 소화조 운영현황 명세 +// ============================================================================ +export interface DigestionTankOperStatusTotalItem { + statsYr: string; + ctpvNm: string; + sggNm: string; + admdstNm: string; + rowType: string; + fcltNm: string; + digesterYn: string; + dsgnCpct: number; + actlOperCpct: number; + inputQnt: number; + tsConc: number; + oprtngDays: number; + gnrtGasQnt: number; + gasTotal: number; + gasSale: number; + gasGen: number; + gasSelf: number; + gasEtc: number; + surplusGas: number; +} +export interface DigestionTankOperTab2Response { status: string; message?: string; data: DigestionTankOperStatusTotalItem[]; } + +export interface SewageSludgeOperStatusTotalItem { + statsYr: string; + ctpvNm: string; + sggNm: string; + admdstNm: string; + admdstCd: string; + rowType: string; + fcltNm: string; + prcsMthExpln: string; + fcltCpct: number; + anlPrcsQnt: number; + dayAvgOprtngHr: number; + anlOprtngDayCnt: number; + oprtngRt: number; +} + +export interface SewageSludgeOperTab3Response { + status: string; + message?: string; + data: SewageSludgeOperStatusTotalItem[]; +} + +export interface HumanWasteSludgeStatusTotalItem { + statsYr: string; + ctpvNm: string; + sggNm: string; + admdstNm: string; + admdstCd: string; + rowType: string; + fcltNm: string; + gnam: number; + wholDspsQnt: number; + rcycQnt: number; + olandLfvlm: number; + incnrQnt: number; + oceanInputQnt: number; + fuelQnt: number; + etcQnt: number; + nondspsQnt: number; + swtrmtFcltNm: string; +} + +export interface HumanWasteSludgeTab4Response { + status: string; + message?: string; + data: HumanWasteSludgeStatusTotalItem[]; +} \ No newline at end of file From 64e5c26e8c5cf6c6dcac57c831cce6e484cdaf15 Mon Sep 17 00:00:00 2001 From: yangsein Date: Mon, 15 Jun 2026 00:59:03 +0900 Subject: [PATCH 09/12] =?UTF-8?q?feat:=20[=EC=A7=91=EA=B3=84=ED=91=9C]=201?= =?UTF-8?q?2.=ED=95=98=EC=88=98=EB=B0=8F=20=EB=B6=84=EB=87=A8=EA=B4=80?= =?UTF-8?q?=EB=A6=AC=EC=82=AC=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../total/SewageExcretaRelatedTot.tsx | 158 +++++++++++++++++- .../HumanWasteCollectionTab3Column.ts | 31 ++++ .../IndividualSewageTab4Column.ts | 25 +++ .../PublicHumanWasteStaffTab2Column.ts | 36 ++++ .../PublicSewageStaffTab1Column.ts | 35 ++++ 5 files changed, 282 insertions(+), 3 deletions(-) create mode 100644 apps/st/src/components/util/column/total/sewageExcretaIndustry/HumanWasteCollectionTab3Column.ts create mode 100644 apps/st/src/components/util/column/total/sewageExcretaIndustry/IndividualSewageTab4Column.ts create mode 100644 apps/st/src/components/util/column/total/sewageExcretaIndustry/PublicHumanWasteStaffTab2Column.ts create mode 100644 apps/st/src/components/util/column/total/sewageExcretaIndustry/PublicSewageStaffTab1Column.ts diff --git a/apps/st/src/components/statistics/total/SewageExcretaRelatedTot.tsx b/apps/st/src/components/statistics/total/SewageExcretaRelatedTot.tsx index adcc810ad..4a3e1cc56 100644 --- a/apps/st/src/components/statistics/total/SewageExcretaRelatedTot.tsx +++ b/apps/st/src/components/statistics/total/SewageExcretaRelatedTot.tsx @@ -1,10 +1,162 @@ +import { useRef, useState } from "react"; +import { useAlertStore } from "@/shared/lib"; +import { LoadingWithAnimation, Tabs, TabsContent, TabsList, TabsTrigger } from "@common/components"; import SearchForm from "@/components/module/SearchForm"; +import DataGrid from "@/components/module/DataGrid"; +import type { ColumnInfo } from "@/components/type/DataGrid.type"; +import type { SearchFormValues } from "@/components/type/SearchForm.type"; +import { toGridRows } from "@/components/util/grid/gridFunction"; +import CellColorInfo from "./CellColorInfo"; + +// 12. 하수 및 분뇨 관리 산업 탭 설정 (경로 확인 필수) +import { PublicSewageStaffTab1ColumnConfig } from "@/components/util/column/total/sewageExcretaIndustry/PublicSewageStaffTab1Column"; +import { PublicHumanWasteStaffTab2ColumnConfig } from "@/components/util/column/total/sewageExcretaIndustry/PublicHumanWasteStaffTab2Column"; +import { HumanWasteCollectionTab3ColumnConfig } from "@/components/util/column/total/sewageExcretaIndustry/HumanWasteCollectionTab3Column"; +import { IndividualSewageTab4ColumnConfig } from "@/components/util/column/total/sewageExcretaIndustry/IndividualSewageTab4Column"; +import { statisticsTotalStatusApi } from "@/entities/statistics/api/statistics-total-service"; +import type { + SewerageIndustry, + PublicSewageStaffTab1Response, + PublicHumanWasteStaffTab2Response, + HumanWasteCollectionTab3Response, + IndividualSewageTab4Response +} from "@/entities/statistics/model/statistics-total/statistics-sewerage-industry-interface"; + +const SewageExcretaIndustryColumnListTot = [PublicSewageStaffTab1ColumnConfig,PublicHumanWasteStaffTab2ColumnConfig,HumanWasteCollectionTab3ColumnConfig,IndividualSewageTab4ColumnConfig].filter(Boolean); +const firstTabKey = SewageExcretaIndustryColumnListTot[0]?.tabKey ?? "ST00120001"; const SewageExcretaRelatedTot = () => { + const { setMessage: alert } = useAlertStore((state) => state); + const [rowDataMap, setRowDataMap] = useState[]>>(new Map()); + const [isLoading, setIsLoading] = useState(false); + const [activeTab, setActiveTab] = useState(firstTabKey); + const lastSearchedValuesRef = useRef(null); + + const handleReset = () => { + setRowDataMap(new Map()); + setActiveTab(firstTabKey); + lastSearchedValuesRef.current = null; + }; + + const loadGridData = async (tabKey: string, values: SearchFormValues): Promise => { + setIsLoading(true); + const param: SewerageIndustry = { + ctpvNm: values.sido === "ALL" ? "" : values.sido, + sggNm: values.gugun === "ALL" ? "" : values.gugun, + sewerBzmnCd: values.bsnmCd, + statsYr: values.year, + rowDataYn: values.isDetail, + summaryYn: values.isSummary !== 'ALL', + summaryYearCnt: values.isSummary !== 'ALL' ? Number(values.isSummary) : undefined + }; + + try { + if (tabKey === "ST00120001") { + const response: PublicSewageStaffTab1Response = await statisticsTotalStatusApi.getPublicSewageStaffTab1List(param); + if (response?.status === "success") { + setRowDataMap((prev) => new Map(prev).set(tabKey, toGridRows(response.data as object[]))); + return true; + } + } + else if (tabKey === "ST00120002") { + const response: PublicHumanWasteStaffTab2Response = await statisticsTotalStatusApi.getPublicHumanWasteStaffTab2List(param); + if (response?.status === "success") { + setRowDataMap((prev) => new Map(prev).set(tabKey, toGridRows(response.data as object[]))); + return true; + } + } + else if (tabKey === "ST00120003") { + const response: HumanWasteCollectionTab3Response = await statisticsTotalStatusApi.getHumanWasteCollectionTab3List(param); + if (response?.status === "success") { + setRowDataMap((prev) => new Map(prev).set(tabKey, toGridRows(response.data as object[]))); + return true; + } + } + // [TAB4] 개인하수처리관련업 + else if (tabKey === "ST00120004") { + const response: IndividualSewageTab4Response = await statisticsTotalStatusApi.getIndividualSewageTab4List(param); + if (response?.status === "success") { + setRowDataMap((prev) => new Map(prev).set(tabKey, toGridRows(response.data as object[]))); + return true; + } + } + return false; + } catch { + alert("서버 연결 중 오류가 발생했습니다."); + return false; + } finally { + setIsLoading(false); + } + }; + + const handleSearch = async (values: SearchFormValues) => { + lastSearchedValuesRef.current = values; + await loadGridData(activeTab, values); + }; + + const handleActiveTabChange = async (tabKey: string) => { + setActiveTab(tabKey); + const values = lastSearchedValuesRef.current; + if (!values || rowDataMap.has(tabKey)) return; + await loadGridData(tabKey, values); + }; + return ( - <> - - +
+ + + {isLoading && ( +
+
+ +
+
+ )} + + + +
+ void handleActiveTabChange(v)} className="w-full" variant="default"> + + {SewageExcretaIndustryColumnListTot.map((info) => ( + {info.tabLabel} + ))} + + + {SewageExcretaIndustryColumnListTot.map((info) => ( + + { + setRowDataMap((prev) => { + const next = new Map(prev); + const cur = next.get(info.tabKey) ?? []; + const n = typeof updater === "function" ? updater(cur) : updater; + next.set(info.tabKey, n); + return next; + }); + }} + isExcelImport={false} + isExcelExport={true} + showHeaderCheckbox={false} + fixedColumns={info.fixedColumns} + enableSort={false} + cellStyleCallback={({ row }) => { + const rowType = String(row.rowType ?? ''); + if (rowType === 'DETAIL') return undefined; + if (rowType === 'BIZ_SUM') return 'rg-cell-rowtype-biz-sum'; + if (rowType === 'SUMMARY') return 'rg-cell-rowtype-summary'; + return 'rg-cell-rowtype-other'; + }} + /> + + ))} + +
+
); }; diff --git a/apps/st/src/components/util/column/total/sewageExcretaIndustry/HumanWasteCollectionTab3Column.ts b/apps/st/src/components/util/column/total/sewageExcretaIndustry/HumanWasteCollectionTab3Column.ts new file mode 100644 index 000000000..666c671b8 --- /dev/null +++ b/apps/st/src/components/util/column/total/sewageExcretaIndustry/HumanWasteCollectionTab3Column.ts @@ -0,0 +1,31 @@ +import { ValueType } from "realgrid"; + +export const HumanWasteCollectionTab3ColumnConfig = { + tabKey: "ST00120003", + tabLabel: "분뇨수집·운반업", + rowStartNum: 3, + fixedColumns: 3, + columns: [ + { name: "ctpvNm", fieldName: "ctpvNm", header: { text: "시도" }, editable: false }, + { name: "sggNm", fieldName: "sggNm", header: { text: "시군구" }, editable: false }, + { name: "admdstNm", fieldName: "admdstNm", header: { text: "행정구역명" }, editable: false }, + { name: "bzentyCnt", fieldName: "bzentyCnt", header: { text: "업체수(개소)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "empNope", fieldName: "empNope", header: { text: "종사인원(명)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "fcltSecrPrstCnt", fieldName: "fcltSecrPrstCnt", header: { text: "계" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "n3TonUdrCnt", fieldName: "n3TonUdrCnt", header: { text: "3톤이하" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "n4TonUdrCnt", fieldName: "n4TonUdrCnt", header: { text: "4.5톤이하" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "n8TonUdrCnt", fieldName: "n8TonUdrCnt", header: { text: "8톤이하" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "etcCnt", fieldName: "etcCnt", header: { text: "기타" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "clctCnvncPrfmncQnt", fieldName: "clctCnvncPrfmncQnt", header: { text: "수집·운반실적(m³/년)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false } + ], + columnLayout: [ + { name: "areaGroup", header: { text: "행정구역" }, items: ["ctpvNm", "sggNm", "admdstNm"] }, + "bzentyCnt", "empNope", + { + name: "fcltGroup", + header: { text: "시설(차량) 확보현황(대수)" }, + items: ["fcltSecrPrstCnt", "n3TonUdrCnt", "n4TonUdrCnt", "n8TonUdrCnt", "etcCnt"] + }, + "clctCnvncPrfmncQnt" + ] +}; \ No newline at end of file diff --git a/apps/st/src/components/util/column/total/sewageExcretaIndustry/IndividualSewageTab4Column.ts b/apps/st/src/components/util/column/total/sewageExcretaIndustry/IndividualSewageTab4Column.ts new file mode 100644 index 000000000..18d99e765 --- /dev/null +++ b/apps/st/src/components/util/column/total/sewageExcretaIndustry/IndividualSewageTab4Column.ts @@ -0,0 +1,25 @@ +import { ValueType } from "realgrid"; + +export const IndividualSewageTab4ColumnConfig = { + tabKey: "ST00120004", + tabLabel: "개인하수처리관련업", + rowStartNum: 3, + fixedColumns: 1, + columns: [ + { name: "ctpvNm", fieldName: "ctpvNm", header: { text: "시도" }, editable: false }, + { name: "sggNm", fieldName: "sggNm", header: { text: "시군구" }, editable: false }, + { name: "admdstNm", fieldName: "admdstNm", header: { text: "행정구역명" }, editable: false }, + { name: "designMdsrCnt", fieldName: "designMdsrCnt", header: { text: "업소수" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "designMdsrNope", fieldName: "designMdsrNope", header: { text: "종사인원(명)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "mfbizCnt", fieldName: "mfbizCnt", header: { text: "업소수" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "mfbizMdsrNope", fieldName: "mfbizMdsrNope", header: { text: "종사인원(명)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "mngCnt", fieldName: "mngCnt", header: { text: "업소수" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "mngMdsrNope", fieldName: "mngMdsrNope", header: { text: "종사인원(명)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false } + ], + columnLayout: [ + { name: "areaGroup", header: { text: "행정구역" }, items: ["ctpvNm", "sggNm", "admdstNm"] }, + { name: "designGroup", header: { text: "개인하수처리시설 설계·시공업" }, items: ["designMdsrCnt", "designMdsrNope"] }, + { name: "mfbizGroup", header: { text: "개인하수처리시설 제조업" }, items: ["mfbizCnt", "mfbizMdsrNope"] }, + { name: "mngGroup", header: { text: "개인하수처리시설 관리업" }, items: ["mngCnt", "mngMdsrNope"] } + ] +}; \ No newline at end of file diff --git a/apps/st/src/components/util/column/total/sewageExcretaIndustry/PublicHumanWasteStaffTab2Column.ts b/apps/st/src/components/util/column/total/sewageExcretaIndustry/PublicHumanWasteStaffTab2Column.ts new file mode 100644 index 000000000..99d35f1ba --- /dev/null +++ b/apps/st/src/components/util/column/total/sewageExcretaIndustry/PublicHumanWasteStaffTab2Column.ts @@ -0,0 +1,36 @@ +import { ValueType } from "realgrid"; + +export const PublicHumanWasteStaffTab2ColumnConfig = { + tabKey: "ST00120002", + tabLabel: "공공기관 분뇨분야", + rowStartNum: 3, + fixedColumns: 2, + columns: [ + { name: "ctpvNm", fieldName: "ctpvNm", header: { text: "시도" }, editable: false }, + { name: "sggNm", fieldName: "sggNm", header: { text: "시군구" }, editable: false }, + { name: "admdstNm", fieldName: "admdstNm", header: { text: "행정구역명" }, editable: false }, + { name: "ognzNm", fieldName: "ognzNm", header: { text: "직제" }, editable: false }, + { name: "empGrssCnt", fieldName: "empGrssCnt", header: { text: "직원총수" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "pbadmsNope", fieldName: "pbadmsNope", header: { text: "행정직" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "techWholNope", fieldName: "techWholNope", header: { text: "계" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "engrkCnt", fieldName: "engrkCnt", header: { text: "토목" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "mchnCnt", fieldName: "mchnCnt", header: { text: "기계" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "prvtmCnt", fieldName: "prvtmCnt", header: { text: "전기" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "chmengCnt", fieldName: "chmengCnt", header: { text: "화공" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "envCnt", fieldName: "envCnt", header: { text: "환경" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "enjbEtcCnt", fieldName: "enjbEtcCnt", header: { text: "기타" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "etcCnt", fieldName: "etcCnt", header: { text: "기타" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false } + ], + columnLayout: [ + { name: "areaGroup", header: { text: "행정구역" }, items: ["ctpvNm", "sggNm", "admdstNm"] }, + "ognzNm", + "empGrssCnt", + "pbadmsNope", + { + name: "techGroup", + header: { text: "기술직" }, + items: ["techWholNope", "engrkCnt", "mchnCnt", "prvtmCnt", "chmengCnt", "envCnt", "enjbEtcCnt"] + }, + "etcCnt" + ] +}; \ No newline at end of file diff --git a/apps/st/src/components/util/column/total/sewageExcretaIndustry/PublicSewageStaffTab1Column.ts b/apps/st/src/components/util/column/total/sewageExcretaIndustry/PublicSewageStaffTab1Column.ts new file mode 100644 index 000000000..90c1a789b --- /dev/null +++ b/apps/st/src/components/util/column/total/sewageExcretaIndustry/PublicSewageStaffTab1Column.ts @@ -0,0 +1,35 @@ +import { ValueType } from "realgrid"; + +export const PublicSewageStaffTab1ColumnConfig = { + tabKey: "ST00120001", + tabLabel: "공공기관 하수분야", + rowStartNum: 3, + fixedColumns: 2, + columns: [ + { name: "ctpvNm", fieldName: "ctpvNm", header: { text: "시도" }, editable: false }, + { name: "sggNm", fieldName: "sggNm", header: { text: "시군구" }, editable: false }, + { name: "admdstNm", fieldName: "admdstNm", header: { text: "행정구역명" }, editable: false }, + { name: "ognzNm", fieldName: "ognzNm", header: { text: "직제" }, editable: false }, + { name: "empGrssCnt", fieldName: "empGrssCnt", header: { text: "직원총수" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "pbadmsNope", fieldName: "pbadmsNope", header: { text: "행정직" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + // 기술직 + { name: "techWholNope", fieldName: "techWholNope", header: { text: "계" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "engrkCnt", fieldName: "engrkCnt", header: { text: "토목" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "mchnCnt", fieldName: "mchnCnt", header: { text: "기계" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "prvtmCnt", fieldName: "prvtmCnt", header: { text: "전기" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "chmengCnt", fieldName: "chmengCnt", header: { text: "화공" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "envCnt", fieldName: "envCnt", header: { text: "환경" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "enjbEtcCnt", fieldName: "enjbEtcCnt", header: { text: "기타" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "etcCnt", fieldName: "etcCnt", header: { text: "기타" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false } + ], + columnLayout: [ + { name: "areaGroup", header: { text: "행정구역" }, items: ["ctpvNm", "sggNm", "admdstNm"] }, + "ognzNm", "empGrssCnt", "pbadmsNope", + { + name: "techGroup", + header: { text: "기술직" }, + items: ["techWholNope", "engrkCnt", "mchnCnt", "prvtmCnt", "chmengCnt", "envCnt", "enjbEtcCnt"] + }, + "etcCnt" + ] +}; \ No newline at end of file From dae702404368cb648c89a5417c0429379722544b Mon Sep 17 00:00:00 2001 From: yangsein Date: Mon, 15 Jun 2026 01:00:31 +0900 Subject: [PATCH 10/12] =?UTF-8?q?feat:[=EC=A7=91=EA=B3=84=ED=91=9C]13.?= =?UTF-8?q?=ED=95=98=EC=88=98=EB=8F=84=20=EC=84=9C=EB=B9=84=EC=8A=A4?= =?UTF-8?q?=EC=88=98=EC=A4=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../total/SewerageServiceLevelTot.tsx | 234 +++++-------- .../ComplaintTotColumn.ts | 103 ++---- .../ResidentFriendlyFactTotColumn.ts | 105 ++---- .../api/statistics-total-service.ts | 331 +++++++++++++++++- ...tatistics-service-level-total-interface.ts | 17 +- 5 files changed, 478 insertions(+), 312 deletions(-) diff --git a/apps/st/src/components/statistics/total/SewerageServiceLevelTot.tsx b/apps/st/src/components/statistics/total/SewerageServiceLevelTot.tsx index 452419dc2..a8e483c1d 100644 --- a/apps/st/src/components/statistics/total/SewerageServiceLevelTot.tsx +++ b/apps/st/src/components/statistics/total/SewerageServiceLevelTot.tsx @@ -1,131 +1,99 @@ import { useRef, useState } from "react"; - import { useAlertStore } from "@/shared/lib"; -import { statisticsTotalStatusApi } from "@/entities/statistics/api/statistics-total-service"; -import { toGridRows } from "@/components/util/grid/gridFunction"; - +import { LoadingWithAnimation, Tabs, TabsContent, TabsList, TabsTrigger } from "@common/components"; import SearchForm from "@/components/module/SearchForm"; import DataGrid from "@/components/module/DataGrid"; -import { LoadingWithAnimation, Tabs, TabsContent, TabsList, TabsTrigger } from "@common/components"; - import type { ColumnInfo } from "@/components/type/DataGrid.type"; import type { SearchFormValues } from "@/components/type/SearchForm.type"; -import { ComplaintInpColumn } from "@/components/util/column/total/sewerageServiceLevel/ComplaintTotColumn"; -import { ResidentFriendlyFactTotColumn } from "@/components/util/column/total/sewerageServiceLevel/ResidentFriendlyFactTotColumn"; +import { toGridRows } from "@/components/util/grid/gridFunction"; +import CellColorInfo from "./CellColorInfo"; -const SewerageServiceLevelInpTabList = [ - ComplaintInpColumn, - ResidentFriendlyFactTotColumn, -]; -const SUPPORTED_TAB_KEYS = [ComplaintInpColumn.tabKey, ResidentFriendlyFactTotColumn.tabKey] as const; +// 13. 하수도서비스수준 컬럼 설정 +import { SewageComplaintTab1ColumnConfig } from "@/components/util/column/total/sewerageServiceLevel/ComplaintTotColumn"; +import { ResidentFriendlyFacilityTab2ColumnConfig } from "@/components/util/column/total/sewerageServiceLevel/ResidentFriendlyFactTotColumn"; + +import { statisticsTotalStatusApi } from "@/entities/statistics/api/statistics-total-service"; +import type { + SewerageServiceLevelRequest, + SewerageServiceLevelComplaintResponse, + SewerageServiceLevelResidentFriendlyFactResponse +} from "@/entities/statistics/model/statistics-total/statistics-service-level-total-interface"; + +const SewageServiceLevelColumnListTot = [ + SewageComplaintTab1ColumnConfig, + ResidentFriendlyFacilityTab2ColumnConfig +].filter(Boolean); + +const firstTabKey = SewageServiceLevelColumnListTot[0]?.tabKey ?? "ST00130001"; const SewerageServiceLevelTot = () => { const { setMessage: alert } = useAlertStore((state) => state); + const [rowDataMap, setRowDataMap] = useState[]>>(new Map()); - const [isLoading, setIsLoading] = useState(false); - const [activeTab, setActiveTab] = useState(ComplaintInpColumn.tabKey); - const lastSearchValuesRef = useRef(null); - - const columnInfo = SewerageServiceLevelInpTabList; + const [isLoading, setIsLoading] = useState(false); + const [activeTab, setActiveTab] = useState(firstTabKey); + const lastSearchedValuesRef = useRef(null); const handleReset = () => { setRowDataMap(new Map()); - setActiveTab(ComplaintInpColumn.tabKey); - lastSearchValuesRef.current = null; + setActiveTab(firstTabKey); + lastSearchedValuesRef.current = null; }; - const loadSewerageServiceLevelTotTabData = async ( - tabKey: string, - values: SearchFormValues, - mergeIntoExistingMap: boolean, - ) => { - const param = { + const loadGridData = async (tabKey: string, values: SearchFormValues): Promise => { + setIsLoading(true); + const param: SewerageServiceLevelRequest = { ctpvNm: values.sido === "ALL" ? "" : values.sido, sggNm: values.gugun === "ALL" ? "" : values.gugun, sewerBzmnCd: values.bsnmCd, statsYr: values.year, rowDataYn: values.isDetail, + summaryYn: values.isSummary !== 'ALL', + summaryYearCnt: values.isSummary !== 'ALL' ? Number(values.isSummary) : undefined }; - const fetcherMap = { - [ComplaintInpColumn.tabKey]: statisticsTotalStatusApi.getSewerageServiceLevelComplaint.bind( - statisticsTotalStatusApi, - ), - [ResidentFriendlyFactTotColumn.tabKey]: - statisticsTotalStatusApi.getSewerageServiceLevelResidentFriendlyFact.bind( - statisticsTotalStatusApi, - ), - }; - const fetcher = fetcherMap[tabKey]; - if (!fetcher) { - return false; - } - const response = await fetcher(param); - - if (response.status === "success" && response.data) { - const rows = toGridRows(response.data as object[]); - if (mergeIntoExistingMap) { - setRowDataMap((prev) => { - const next = new Map(prev); - next.set(tabKey, rows); - return next; - }); - } else { - const next = new Map[]>(); - next.set(tabKey, rows); - setRowDataMap(next); - } - return true; - } - alert(response.message || "데이터 조회 중 오류가 발생했습니다."); - return false; - }; - - const handleSearch = async (values: SearchFormValues) => { try { - setIsLoading(true); - let ok = false; - if (SUPPORTED_TAB_KEYS.includes(activeTab)) { - ok = await loadSewerageServiceLevelTotTabData(activeTab, values, true); - } - if (ok) { - lastSearchValuesRef.current = values; - } else { - lastSearchValuesRef.current = null; + // [TAB1] 민원 + if (tabKey === "ST00130001") { + const response: SewerageServiceLevelComplaintResponse = await statisticsTotalStatusApi.getSewerageServiceLevelComplaint(param); + if (response?.status === "success") { + setRowDataMap((prev) => new Map(prev).set(tabKey, toGridRows(response.data as object[]))); + return true; + } + } + // [TAB2] 주민친화시설 + else if (tabKey === "ST00130002") { + const response: SewerageServiceLevelResidentFriendlyFactResponse = await statisticsTotalStatusApi.getSewerageServiceLevelResidentFriendlyFact(param); + if (response?.status === "success") { + setRowDataMap((prev) => new Map(prev).set(tabKey, toGridRows(response.data as object[]))); + return true; + } } + return false; } catch { - alert("데이터 조회 중 오류가 발생했습니다."); - lastSearchValuesRef.current = null; + alert("서버 연결 중 오류가 발생했습니다."); + return false; } finally { setIsLoading(false); } }; + const handleSearch = async (values: SearchFormValues) => { + lastSearchedValuesRef.current = values; + await loadGridData(activeTab, values); + }; + const handleActiveTabChange = async (tabKey: string) => { setActiveTab(tabKey); - - const values = lastSearchValuesRef.current; - if (!values) return; - - const existing = rowDataMap.get(tabKey); - const hasData = Array.isArray(existing) && existing.length > 0; - if (hasData) return; - - if (SUPPORTED_TAB_KEYS.includes(tabKey)) { - try { - setIsLoading(true); - await loadSewerageServiceLevelTotTabData(tabKey, values, true); - } catch { - alert("데이터 조회 중 오류가 발생했습니다."); - } finally { - setIsLoading(false); - } - } + const values = lastSearchedValuesRef.current; + if (!values || rowDataMap.has(tabKey)) return; + await loadGridData(tabKey, values); }; return (
- + + {isLoading && (
@@ -133,58 +101,48 @@ const SewerageServiceLevelTot = () => {
)} + + +
void handleActiveTabChange(v)} className="w-full" variant="default"> - {columnInfo.map((info) => ( - - {info.tabLabel || info.tabKey} - + {SewageServiceLevelColumnListTot.map((info) => ( + {info.tabLabel} ))} - {columnInfo.map((info) => { - const tabKey = info.tabKey; - if (!info.columns || !info.columnLayout) { - return null; - } - - const rowData = rowDataMap.get(tabKey) ?? []; - const gridColumnInfo: ColumnInfo = { - columns: info.columns, - columnLayout: info.columnLayout, - }; - - return ( - - { - setRowDataMap((prev) => { - const next = new Map(prev); - const cur = next.get(tabKey) ?? []; - const n = typeof updater === "function" ? updater(cur) : updater; - next.set(tabKey, n); - return next; - }); - }} - isExcelImport={false} - isExcelExport - showHeaderCheckbox={false} - fixedColumns={info.fixedColumns} - enableSort={false} - cellStyleCallback={({ row }) => { - const rowType = String(row.rowType ?? ""); - if (rowType === "DETAIL") return undefined; - if (rowType === "BIZ_SUM") return "rg-cell-rowtype-biz-sum"; - return "rg-cell-rowtype-other"; - }} - /> - - ); - })} + + {SewageServiceLevelColumnListTot.map((info) => ( + + { + setRowDataMap((prev) => { + const next = new Map(prev); + const cur = next.get(info.tabKey) ?? []; + const n = typeof updater === "function" ? updater(cur) : updater; + next.set(info.tabKey, n); + return next; + }); + }} + isExcelImport={false} + isExcelExport={true} + showHeaderCheckbox={false} + fixedColumns={info.fixedColumns} + enableSort={false} + cellStyleCallback={({ row }) => { + const rowType = String(row.rowType ?? ''); + if (rowType === 'DETAIL') return undefined; + if (rowType === 'BIZ_SUM') return 'rg-cell-rowtype-biz-sum'; + if (rowType === 'SUMMARY') return 'rg-cell-rowtype-summary'; + return 'rg-cell-rowtype-other'; + }} + /> + + ))}
diff --git a/apps/st/src/components/util/column/total/sewerageServiceLevel/ComplaintTotColumn.ts b/apps/st/src/components/util/column/total/sewerageServiceLevel/ComplaintTotColumn.ts index 8ed48c355..3d06e995a 100644 --- a/apps/st/src/components/util/column/total/sewerageServiceLevel/ComplaintTotColumn.ts +++ b/apps/st/src/components/util/column/total/sewerageServiceLevel/ComplaintTotColumn.ts @@ -1,77 +1,36 @@ import { ValueType } from "realgrid"; -// 하수도 통계 관리 > 13. 하수도 서비스 수준 > tab 1: 민원 -export const ComplaintInpColumn = { - columns: [ - { name: "ctpvNm", fieldName: "ctpvNm", header: { text: "시도" } }, - { name: "sggNm", fieldName: "sggNm", header: { text: "시군구" } }, - { name: "admdstNm", fieldName: "admdstNm", header: { text: "행정구역명" } }, - { - name: "swtrmtPpltnCnt", - fieldName: "swtrmtPpltnCnt", - header: { text: "하수처리장민원건수(건)" }, - valueType: ValueType.NUMBER, - dataFormat: "#,##0", - }, - { - name: "whol", - fieldName: "whol", - header: { text: "합계" }, - valueType: ValueType.NUMBER, - dataFormat: "#,##0", - }, - { name: "odorCnt", fieldName: "odorCnt", header: { text: "악취" }, valueType: ValueType.NUMBER, dataFormat: "#,##0" }, - { name: "swagePplCnt", fieldName: "swagePplCnt", header: { text: "하수관거" }, valueType: ValueType.NUMBER, dataFormat: "#,##0" }, - { name: "dschgWqCnt", fieldName: "dschgWqCnt", header: { text: "방류수수질" }, valueType: ValueType.NUMBER, dataFormat: "#,##0" }, - { name: "ldsCnt", fieldName: "ldsCnt", header: { text: "퇴적토" }, valueType: ValueType.NUMBER, dataFormat: "#,##0" }, - { name: "plltnAcdntCnt", fieldName: "plltnAcdntCnt", header: { text: "오염사고" }, valueType: ValueType.NUMBER, dataFormat: "#,##0" }, - { name: "sewerCrgAmt", fieldName: "sewerCrgAmt", header: { text: "하수도요금" }, valueType: ValueType.NUMBER, dataFormat: "#,##0" }, - { name: "etcCnt", fieldName: "etcCnt", header: { text: "기타" }, valueType: ValueType.NUMBER, dataFormat: "#,##0" }, - { - name: "crtrPpltnCvlcptEvntcsCnt", - fieldName: "crtrPpltnCvlcptEvntcsCnt", - header: { text: "인구10만명당 민원건수(건)" }, - valueType: ValueType.NUMBER, - dataFormat: "#,##0.00", - }, - { name: "rmrk", fieldName: "rmrk", header: { text: "비고" } }, - { name: "statsYr", fieldName: "statsYr", header: { text: "통계연도" }, visible: false }, - { name: "admdstCd", fieldName: "admdstCd", header: { text: "행정코드" }, visible: false }, - { name: "rowType", fieldName: "rowType", header: { text: "행타입" }, visible: false }, - ].map((col) => ({ ...col, editable: false })), - - columnLayout: [ - { - name: "groupA", - hideChildHeaders: false, - header: { text: "행정구역" }, - items: ["ctpvNm", "sggNm", "admdstNm"], - }, - { - name: "groupB", - hideChildHeaders: false, - header: { text: "민원건수(건)" }, - items: [ - "whol", - "odorCnt", - "swagePplCnt", - "dschgWqCnt", - "ldsCnt", - "plltnAcdntCnt", - "sewerCrgAmt", - "etcCnt", - ], - }, - "swtrmtPpltnCnt", - "crtrPpltnCvlcptEvntcsCnt", - "rmrk", - "statsYr", - "admdstCd", - "rowType", - ], - - rowStartNum: 2, - fixedColumns: 1, +export const SewageComplaintTab1ColumnConfig = { tabKey: "ST00130001", tabLabel: "민원", + rowStartNum: 3, // 행정구역 3단 헤더 + fixedColumns: 1, + columns: [ + { name: "ctpvNm", fieldName: "ctpvNm", header: { text: "시도" }, editable: false }, + { name: "sggNm", fieldName: "sggNm", header: { text: "시군구" }, editable: false }, + { name: "admdstNm", fieldName: "admdstNm", header: { text: "행정구역명" }, editable: false }, + { name: "swtrmtPpltnCnt", fieldName: "swtrmtPpltnCnt", header: { text: "하수처리인구\n(명)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + + // 민원유형 + { name: "whol", fieldName: "whol", header: { text: "총계" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "odorCnt", fieldName: "odorCnt", header: { text: "악취" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "swagePplCnt", fieldName: "swagePplCnt", header: { text: "하수관로" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "dschgWqCnt", fieldName: "dschgWqCnt", header: { text: "배수설비" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "ldsCnt", fieldName: "ldsCnt", header: { text: "맨홀" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "plltnAcdntCnt", fieldName: "plltnAcdntCnt", header: { text: "정화조" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "sewerCrgAmt", fieldName: "sewerCrgAmt", header: { text: "하수도요금" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "etcCnt", fieldName: "etcCnt", header: { text: "기타" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + + { name: "crtrPpltnCvlcptEvntcsCnt", fieldName: "crtrPpltnCvlcptEvntcsCnt", header: { text: "기준인구당\n민원건수\n(건/10,000명)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.00", editable: false } + ], + columnLayout: [ + { name: "areaGroup", header: { text: "행정구역" }, items: ["ctpvNm", "sggNm", "admdstNm"] }, + "swtrmtPpltnCnt", + { + name: "complaintGroup", + header: { text: "민원유형(건)" }, + items: ["whol", "odorCnt", "swagePplCnt", "dschgWqCnt", "ldsCnt", "plltnAcdntCnt", "sewerCrgAmt", "etcCnt"] + }, + "crtrPpltnCvlcptEvntcsCnt" + ] }; \ No newline at end of file diff --git a/apps/st/src/components/util/column/total/sewerageServiceLevel/ResidentFriendlyFactTotColumn.ts b/apps/st/src/components/util/column/total/sewerageServiceLevel/ResidentFriendlyFactTotColumn.ts index 1a121f49d..82890e8b9 100644 --- a/apps/st/src/components/util/column/total/sewerageServiceLevel/ResidentFriendlyFactTotColumn.ts +++ b/apps/st/src/components/util/column/total/sewerageServiceLevel/ResidentFriendlyFactTotColumn.ts @@ -1,81 +1,34 @@ import { ValueType } from "realgrid"; -// 하수도 통계 관리 > 13. 하수도 서비스 수준 > tab 2: 주민친화시설 -export const ResidentFriendlyFactTotColumn = { - columns: [ - { name: "ctpvNm", fieldName: "ctpvNm", header: { text: "시도" } }, - { name: "sggNm", fieldName: "sggNm", header: { text: "시군구" } }, - { name: "admdstNm", fieldName: "admdstNm", header: { text: "행정구역명" } }, - { name: "swtrmtFcltNm", fieldName: "swtrmtFcltNm", header: { text: "처리장명" } }, - { - name: "fcltCpct", - fieldName: "fcltCpct", - header: { text: "시설용량(m³/일)" }, - valueType: ValueType.NUMBER, - dataFormat: "#,##0", - }, - { - name: "tmplntArea", - fieldName: "tmplntArea", - header: { text: "처리장면적(㎡)" }, - valueType: ValueType.NUMBER, - dataFormat: "#,##0.00", - }, - { - name: "whol", - fieldName: "whol", - header: { text: "총계" }, - valueType: ValueType.NUMBER, - dataFormat: "#,##0", - }, - { name: "brkFcltArea", fieldName: "brkFcltArea", header: { text: "휴게시설" }, valueType: ValueType.NUMBER, dataFormat: "#,##0" }, - { name: "childFrshpSpacArea", fieldName: "childFrshpSpacArea", header: { text: "어린이놀이공간" }, valueType: ValueType.NUMBER, dataFormat: "#,##0" }, - { name: "bdySportsFcltArea", fieldName: "bdySportsFcltArea", header: { text: "체력단련시설" }, valueType: ValueType.NUMBER, dataFormat: "#,##0" }, - { name: "sportsFcltArea", fieldName: "sportsFcltArea", header: { text: "운동시설" }, valueType: ValueType.NUMBER, dataFormat: "#,##0" }, - { name: "pkltArea", fieldName: "pkltArea", header: { text: "주차장" }, valueType: ValueType.NUMBER, dataFormat: "#,##0" }, - { name: "eduMultiFcltArea", fieldName: "eduMultiFcltArea", header: { text: "교육 및 문화시설" }, valueType: ValueType.NUMBER, dataFormat: "#,##0" }, - { name: "wtlndMakeArea", fieldName: "wtlndMakeArea", header: { text: "습지조성(생태공원)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0" }, - { name: "etcCnt", fieldName: "etcCnt", header: { text: "기타" }, valueType: ValueType.NUMBER, dataFormat: "#,##0" }, - { name: "rmrk", fieldName: "rmrk", header: { text: "비고" } }, - { name: "statsYr", fieldName: "statsYr", header: { text: "통계연도" }, visible: false }, - { name: "admdstCd", fieldName: "admdstCd", header: { text: "행정코드" }, visible: false }, - { name: "rowType", fieldName: "rowType", header: { text: "행타입" }, visible: false }, - ].map((col) => ({ ...col, editable: false })), - - columnLayout: [ - { - name: "groupA", - hideChildHeaders: false, - header: { text: "행정구역" }, - items: ["ctpvNm", "sggNm", "admdstNm"], - }, - "swtrmtFcltNm", - "fcltCpct", - "tmplntArea", - { - name: "groupB", - hideChildHeaders: false, - header: { text: "주민친화시설 면적(㎡)" }, - items: [ - "whol", - "brkFcltArea", - "childFrshpSpacArea", - "bdySportsFcltArea", - "sportsFcltArea", - "pkltArea", - "eduMultiFcltArea", - "wtlndMakeArea", - "etcCnt", - ], - }, - "rmrk", - "statsYr", - "admdstCd", - "rowType", - ], - - rowStartNum: 2, - fixedColumns: 3, +export const ResidentFriendlyFacilityTab2ColumnConfig = { tabKey: "ST00130002", tabLabel: "주민친화시설", + rowStartNum: 3, // 행정구역 3단 헤더 + fixedColumns: 1, + columns: [ + { name: "ctpvNm", fieldName: "ctpvNm", header: { text: "시도" }, editable: false }, + { name: "sggNm", fieldName: "sggNm", header: { text: "시군구" }, editable: false }, + { name: "admdstNm", fieldName: "admdstNm", header: { text: "행정구역명" }, editable: false }, + { name: "swtrmtFcltNm", fieldName: "swtrmtFcltNm", header: { text: "처리장명" }, editable: false }, + { name: "tmplntArea", fieldName: "tmplntArea", header: { text: "처리장\n면적(㎡)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + // 주민친화시설 면적 + { name: "whol", fieldName: "whol", header: { text: "총계" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "brkFcltArea", fieldName: "brkFcltArea", header: { text: "휴게시설" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "childFrshpSpacArea", fieldName: "childFrshpSpacArea", header: { text: "어린이놀이공간" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "bdySportsFcltArea", fieldName: "bdySportsFcltArea", header: { text: "체력단련시설" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "sportsFcltArea", fieldName: "sportsFcltArea", header: { text: "운동시설" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "pkltArea", fieldName: "pkltArea", header: { text: "주차장" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "eduMultiFcltArea", fieldName: "eduMultiFcltArea", header: { text: "교육 및\n문화시설" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "wtlndMakeArea", fieldName: "wtlndMakeArea", header: { text: "습지조성\n(생태공원)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "etcCnt", fieldName: "etcCnt", header: { text: "기타" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false } + ], + columnLayout: [ + { name: "areaGroup", header: { text: "행정구역" }, items: ["ctpvNm", "sggNm", "admdstNm"] }, + "swtrmtFcltNm", "tmplntArea", + { + name: "facilityGroup", + header: { text: "주민친화시설 면적(㎡)" }, + items: ["whol", "brkFcltArea", "childFrshpSpacArea", "bdySportsFcltArea", "sportsFcltArea", "pkltArea", "eduMultiFcltArea", "wtlndMakeArea", "etcCnt"] + } + ] }; \ No newline at end of file diff --git a/apps/st/src/entities/statistics/api/statistics-total-service.ts b/apps/st/src/entities/statistics/api/statistics-total-service.ts index e5efbb39b..be7f839b9 100644 --- a/apps/st/src/entities/statistics/api/statistics-total-service.ts +++ b/apps/st/src/entities/statistics/api/statistics-total-service.ts @@ -23,6 +23,62 @@ import type { import type { PublicSewageInquirySummaryResponse } from "../model/statistics-total/statistics-sewage-public-total-interface"; +import type { + SewerReservoirMaintainTotalRequest, + SewerReservoirStatusTotalResponse, + SewerPumpStationStatusTotalResponse +} from "../model/statistics-total/statistics-sewage-reservoir-total-interface"; + +import type { + MediationPumpMaintainTotalRequest, + MediationPumpStatusTotalResponse +} from "../model/statistics-total/statistics-mediation-pump-total-interface"; + +import type { + ExcretaTreatmentMaintainTotalRequest, + ExcretaTreatmentStatusTotalResponse +} from "../model/statistics-total/statistics-excreta-treatment-total-interface"; + + +import type { + PrivateSewageFacilitySummaryRequest, + PrivateSewageTab1Response, + PrivateSewageTab2Response, + PrivateSewageTab3Response +} from "../model/statistics-total/statistics-private-sewage-total-interface"; + +import type { + SewerageFinanceSummaryRequest, + SewerageFinanceSettlementResponse, + SewerageFeeResponse, + SewerageUsageFeeResponse, + SewerageOrgCauseResponse +} from "../model/statistics-total/statistics-sewerage-finance-total-interface"; + +import type { + WaterReuseSummaryRequest, + SewerageReuseStatusTab1Response, + GrayWaterStatusTab2Response, + RainwaterUtilizationStatusTab3Response, + SewageWwtRtlztnRegStatusTab4Response, + SewageWwtRtlztnSncnStatusTab5Response +} from "../model/statistics-total/statistics-sewerage-reuse-total-interface"; + +import type { + SludgeSummaryRequest, + SludgeProcessingTab1Response, + DigestionTankOperTab2Response, + SewageSludgeOperTab3Response, + HumanWasteSludgeTab4Response +} from "../model/statistics-total/statistics-sludge-total-interface"; + +import type { + SewerageIndustry, + PublicSewageStaffTab1Response, + PublicHumanWasteStaffTab2Response, + HumanWasteCollectionTab3Response, + IndividualSewageTab4Response +} from "../model/statistics-total/statistics-sewerage-industry-interface"; export class StatisticsTotalApiService { @@ -42,21 +98,6 @@ export class StatisticsTotalApiService { return response.data; } - public async getSewerageServiceLevelComplaint(params: SewerageServiceLevelRequest): Promise { - const response = await stClient.get ( - `${ENDPOINT.ST_SERVICE.TOTAL}/sewage-complaint`, - { params: params } - ); - return response.data; - } - - public async getSewerageServiceLevelResidentFriendlyFact(params: SewerageServiceLevelRequest): Promise { - const response = await stClient.get ( - `${ENDPOINT.ST_SERVICE.TOTAL}/resident-friendly-facility`, - { params: params } - ); - return response.data; - } public async getSewagePipeFactInstallStatus( params: SewagePipeTotalRequest, @@ -122,7 +163,40 @@ export class StatisticsTotalApiService { return response.data; } - /** ST00060001 —공공하수처리시설 */ + /** ★ ST00040001 — 하수저류시설 현황(합계) API */ + public async getSewerReservoirStatusTotalList( + params: SewerReservoirMaintainTotalRequest, + ): Promise { + const response = await stClient.get( + `${ENDPOINT.ST_SERVICE.TOTAL}/sewer-reservoir-pump/reservoir-status`, + { params }, + ); + return response.data; + } + + /** ★ ST00040002 — 빗물펌프장 현황(합계) API */ + public async getSewerPumpStationStatusTotalList( + params: SewerReservoirMaintainTotalRequest, + ): Promise { + const response = await stClient.get( + `${ENDPOINT.ST_SERVICE.TOTAL}/sewer-reservoir-pump/pump-station-status`, + { params }, + ); + return response.data; + } + + /** ST00050001 — 중계펌프장 현황(합계) API */ + public async getMediationPumpStationStatusTotalList( + params: MediationPumpMaintainTotalRequest, + ): Promise { + const response = await stClient.get( + `${ENDPOINT.ST_SERVICE.TOTAL}/pump-station-status`, + { params }, + ); + return response.data; + } + /** + * ST00060001 —공공하수처리시설 */ public async getSewagePublicFacillityTotal( params: SewagePipeMaintainTotalRequest, ): Promise { @@ -132,6 +206,229 @@ export class StatisticsTotalApiService { ); return response.data; } -} + + + /** ST00070001 — 분뇨처리시설 현황(합계) API */ + public async getExcretaTreatmentStatusTotalList( + params: ExcretaTreatmentMaintainTotalRequest, + ): Promise { + const response = await stClient.get( + `${ENDPOINT.ST_SERVICE.TOTAL}/excreta-treatment-status`, + { params }, + ); + return response.data; + } + + /** ST00080001 — 개인하수처리시설 > 처리용량별 오수처리시설(Tab1) */ + public async getPrivateSewageTab1List( + params: PrivateSewageFacilitySummaryRequest, + ): Promise { + const response = await stClient.get( + `${ENDPOINT.ST_SERVICE.TOTAL}/private-sevage`, + { params }, + ); + return response.data; + } + + /** ST00080002 — 개인하수처리시설 > 정화조현황(Tab2) */ + public async getPrivateSewageTab2List( + params: PrivateSewageFacilitySummaryRequest, + ): Promise { + const response = await stClient.get( + `${ENDPOINT.ST_SERVICE.TOTAL}/private-tank-status`, + { params }, + ); + return response.data; + } + + /** ST00080003 — 개인하수처리시설 > 개인하수도 관리지역 지정공고 현황(Tab3) */ + public async getPrivatePrivateMgmtAreaList( + params: PrivateSewageFacilitySummaryRequest, + ): Promise { + const response = await stClient.get( + `${ENDPOINT.ST_SERVICE.TOTAL}/private-mgmt-area`, + { params }, + ); + return response.data; + } + + /** ST00090001 — 하수도 재정결산 조회(Tab1) */ + public async getSewerageFinancialList(params: SewerageFinanceSummaryRequest): Promise { + const response = await stClient.get(`${ENDPOINT.ST_SERVICE.TOTAL}/sewerage-financial`, { params }); + return response.data; + } + + /** ST00090002 — 하수도 요금 조회(Tab2) */ + public async getSewerageFeeList(params: SewerageFinanceSummaryRequest): Promise { + const response = await stClient.get(`${ENDPOINT.ST_SERVICE.TOTAL}/sewerage-fee`, { params }); + return response.data; + } + + /** ST00090003 — 하수도 사용료 조회(Tab3) */ + public async getSewerageUsageList(params: SewerageFinanceSummaryRequest): Promise { + const response = await stClient.get(`${ENDPOINT.ST_SERVICE.TOTAL}/sewerage-usage`, { params }); + return response.data; + } + + /** ST00090004 — 원인자 부담금 조회(Tab4) */ + public async getSewerageOrgCauseList(params: SewerageFinanceSummaryRequest): Promise { + const response = await stClient.get(`${ENDPOINT.ST_SERVICE.TOTAL}/sewerage-org-cause`, { params }); + return response.data; + } + + /** ST00100001 — 물재이용 > 공공하수처리수 재이용 현황 조회(Tab1) */ + public async getSewerageReuseTab1List( + params: WaterReuseSummaryRequest, + ): Promise { + const response = await stClient.get( + `${ENDPOINT.ST_SERVICE.TOTAL}/pbl-swage-ruswt`, + { params }, + ); + return response.data; + } + /** + * ST00100002 — 물재이용 > TAB2 중수도 현황 조회 + */ + async getGrayWaterStatusTab2List( + params: WaterReuseSummaryRequest + ): Promise { + const response = await stClient.get( + `${ENDPOINT.ST_SERVICE.TOTAL}/gray-water-status`, + { params }, + ); + return response.data; + + } + /** ★ ST00100003 — 물재이용 > TAB3 빗물이용시설현황 조회 */ + async getRainwaterStatusTab3List(params: WaterReuseSummaryRequest): Promise { + const response = await stClient.get(`${ENDPOINT.ST_SERVICE.TOTAL}/water-reuse`, { params }); + return response.data; + } + + /** ★ ST00100004 — 물재이용 > TAB4 하·폐수처리수 재이용시설 시공업 등록현황 조회 */ + async getWwtRtlztnRegStatusTab4List(params: WaterReuseSummaryRequest): Promise { + const response = await stClient.get(`${ENDPOINT.ST_SERVICE.TOTAL}/wwt-rtlztn-reg`, { params }); + return response.data; + } + + /** ★ ST00100005 — 물재이용 > TAB5 하·폐수처리수 재이용시설 시공업 인가현황 조회 */ + async getWwtRtlztnSncnStatusTab5List(params: WaterReuseSummaryRequest): Promise { + const response = await stClient.get(`${ENDPOINT.ST_SERVICE.TOTAL}/wwt-rtlztn-aprv`, { params }); + return response.data; + } + + /** + * ST00110001 — 하수및분뇨찌꺼기 > TAB1 찌꺼기처리시설현황 조회 + */ + async getSludgeProcessingTab1List( + params: SludgeSummaryRequest + ): Promise { + const response = await stClient.get( + `${ENDPOINT.ST_SERVICE.TOTAL}/sludge-processing`, + { params }, + ); + return response.data; + } + /** + * ST00110002 — 하수및분뇨찌꺼기 > TAB2 소화조 운영현황 조회 + */ + async getSludgeTab2List( + params: SludgeSummaryRequest + ): Promise { + const response = await stClient.get( + `${ENDPOINT.ST_SERVICE.TOTAL}/sludge-digestion-tank`, + { params }, + ); + return response.data; + } + + /** + * ST00110003 — 하수및분뇨찌꺼기 > TAB3 하수찌꺼기 처리시설 운영현황 조회 + */ + async getSludgeTab3List( + params: SludgeSummaryRequest + ): Promise { + // 제네릭을 추가하여 unknown 에러를 해결합니다. + const response = await stClient.get( + `${ENDPOINT.ST_SERVICE.TOTAL}/sludge-facility-oper`, + { params } + ); + return response.data; + } + + /** + * ST00110004 — 하수및분뇨찌꺼기 > TAB4 분뇨찌꺼기(슬러지)처리현황 + */ + async getSludgeTab4List( + params: SludgeSummaryRequest + ): Promise { + const response = await stClient.get( + `${ENDPOINT.ST_SERVICE.TOTAL}/sludge-human-waste`, + { params }, + ); + return response.data; + } + + /** ST00120001[TAB1] 공공기관 하수분야 조회 */ + async getPublicSewageStaffTab1List( + params: SewerageIndustry + ): Promise { + const response = await stClient.get( + `${ENDPOINT.ST_SERVICE.TOTAL}/public-sewage-staff`, + { params } + ); + return response.data; + } + /** ST00120002[TAB2] 공공기관 분뇨분야 조회 */ + async getPublicHumanWasteStaffTab2List( + params: SewerageIndustry + ): Promise { + const response = await stClient.get( + `${ENDPOINT.ST_SERVICE.TOTAL}/public-human-waste-staff`, + { params } + ); + return response.data; + } + + /** [TAB3] 분뇨수집·운반업 조회 */ + async getHumanWasteCollectionTab3List( + params: SewerageIndustry + ): Promise { + const response = await stClient.get( + `${ENDPOINT.ST_SERVICE.TOTAL}/human-waste-collection`, + { params } + ); + return response.data; + } + + /** [TAB4] 개인하수처리관련업 조회 */ + async getIndividualSewageTab4List( + params: SewerageIndustry + ): Promise { + const response = await stClient.get( + `${ENDPOINT.ST_SERVICE.TOTAL}/indv-swtrmt-rel-bsns`, + { params } + ); + return response.data; + } + + /** [TAB1] 하수도서비스수준 - 민원 조회 */ + public async getSewerageServiceLevelComplaint(params: SewerageServiceLevelRequest): Promise { + const response = await stClient.get ( + `${ENDPOINT.ST_SERVICE.TOTAL}/sewage-complaint`, + { params: params } + ); + return response.data; + } + /** [TAB1] 하수도서비스수준 - 주민친화시설 조회 */ + public async getSewerageServiceLevelResidentFriendlyFact(params: SewerageServiceLevelRequest): Promise { + const response = await stClient.get ( + `${ENDPOINT.ST_SERVICE.TOTAL}/resident-friendly-facility`, + { params: params } + ); + return response.data; + } + } + export const statisticsTotalStatusApi = new StatisticsTotalApiService(); \ No newline at end of file diff --git a/apps/st/src/entities/statistics/model/statistics-total/statistics-service-level-total-interface.ts b/apps/st/src/entities/statistics/model/statistics-total/statistics-service-level-total-interface.ts index 49dd44a63..95a408ca4 100644 --- a/apps/st/src/entities/statistics/model/statistics-total/statistics-service-level-total-interface.ts +++ b/apps/st/src/entities/statistics/model/statistics-total/statistics-service-level-total-interface.ts @@ -4,14 +4,16 @@ export interface SewerageServiceLevelRequest { statsYr?: string; sewerBzmnCd?: string; rowDataYn?: boolean; + summaryYn?: boolean; + summaryYearCnt?: number; } -export interface SewerageServiceLevelComplaintTotalItem { +export interface SewageComplaintSummaryItem { statsYr: string; ctpvNm: string; - sggNm: string | null; - admdstCd: string; + sggNm: string; admdstNm: string; + admdstCd: string; rowType: string; swtrmtPpltnCnt: number; whol: number; @@ -23,24 +25,22 @@ export interface SewerageServiceLevelComplaintTotalItem { sewerCrgAmt: number; etcCnt: number; crtrPpltnCvlcptEvntcsCnt: number; - rmrk: string | null; } export interface SewerageServiceLevelComplaintResponse { status: string; message?: string; - data: SewerageServiceLevelComplaintTotalItem[]; + data: SewageComplaintSummaryItem[]; } export interface SewerageServiceLevelResidentFriendlyFactTotalItem { - statsYr: string; + statsYr: string; ctpvNm: string; sggNm: string; - admdstCd: string; admdstNm: string; + admdstCd: string; rowType: string; swtrmtFcltNm: string; - fcltCpct: number; tmplntArea: number; whol: number; brkFcltArea: number; @@ -51,7 +51,6 @@ export interface SewerageServiceLevelResidentFriendlyFactTotalItem { eduMultiFcltArea: number; wtlndMakeArea: number; etcCnt: number; - rmrk: string; } export interface SewerageServiceLevelResidentFriendlyFactResponse { From 3c4a6efeeabd43b1f05dc31de9cf487eba11f1e6 Mon Sep 17 00:00:00 2001 From: yangsein Date: Mon, 15 Jun 2026 01:01:51 +0900 Subject: [PATCH 11/12] =?UTF-8?q?feat:10.=EB=AC=BC=EC=9E=AC=EC=9D=B4?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SewerageWwtRtlztnSncnTab5Column.ts | 25 +++ ...atistics-sewerage-reuse-total-interface.ts | 150 ++++++++++++++++++ 2 files changed, 175 insertions(+) create mode 100644 apps/st/src/components/util/column/total/sewerageReuse/SewerageWwtRtlztnSncnTab5Column.ts create mode 100644 apps/st/src/entities/statistics/model/statistics-total/statistics-sewerage-reuse-total-interface.ts diff --git a/apps/st/src/components/util/column/total/sewerageReuse/SewerageWwtRtlztnSncnTab5Column.ts b/apps/st/src/components/util/column/total/sewerageReuse/SewerageWwtRtlztnSncnTab5Column.ts new file mode 100644 index 000000000..148bc1927 --- /dev/null +++ b/apps/st/src/components/util/column/total/sewerageReuse/SewerageWwtRtlztnSncnTab5Column.ts @@ -0,0 +1,25 @@ +export const SewerageWwtRtlztnSncnTab5ColumnConfig = { + tabKey: "ST00100005", + tabLabel: "하·폐수처리수재이용시설사업인가현황", + rowStartNum: 2, // 행정구역 2단 레이아웃에 최적화 + fixedColumns: 1, + columns: [ + // 행정구역 그룹 소속 + { name: "ctpvNm", fieldName: "ctpvNm", header: { text: "시도" }, editable: false }, + { name: "sggNm", fieldName: "sggNm", header: { text: "시군구" }, editable: false }, + { name: "admdstNm", fieldName: "admdstNm", header: { text: "행정구역명" }, editable: false }, + + // 사업자 정보 필드 매핑 + { name: "bplcNm", fieldName: "bplcNm", header: { text: "상호(명칭)" }, editable: false }, + { name: "rprsvNm", fieldName: "rprsvNm", header: { text: "대표자" }, editable: false }, + { name: "bplcPstnCn", fieldName: "bplcPstnCn", header: { text: "주소" }, editable: false }, + { name: "coRegYmd", fieldName: "coRegYmd", header: { text: "인가일\n(년/월/일)" }, editable: false } + ], + columnLayout: [ + { name: "areaGroup", header: { text: "행정구역" }, items: ["ctpvNm", "sggNm", "admdstNm"] }, + "bplcNm", + "rprsvNm", + "bplcPstnCn", + "coRegYmd" + ] +}; \ No newline at end of file diff --git a/apps/st/src/entities/statistics/model/statistics-total/statistics-sewerage-reuse-total-interface.ts b/apps/st/src/entities/statistics/model/statistics-total/statistics-sewerage-reuse-total-interface.ts new file mode 100644 index 000000000..6daaa773c --- /dev/null +++ b/apps/st/src/entities/statistics/model/statistics-total/statistics-sewerage-reuse-total-interface.ts @@ -0,0 +1,150 @@ +/** 10. 물재이용 검색 조건 공통 파라미터 DTO */ +export interface WaterReuseSummaryRequest { + ctpvNm?: string; + sggNm?: string; + statsYr?: string; + sewerBzmnCd?: string; + rowDataYn?: boolean; + summaryYn?: boolean; + summaryYearCnt?: number; +} + +/** [Tab1] 공공하수처리수 재이용 목록 항목 인터페이스 */ +export interface SewerageReuseStatusTotalItem { + ctpvNm: string; + sggNm: string | null; + statsYr: string; + admdstNm: string; + admdstCd: string; + rowType: string; + // 하수처리장 + swtrmtFcltNm: string; + sncnYmd: string; + prcsFcltCpct: string; + prcsQnt: number; + lglYn: string; + // 재이용시설 설치시 + instlYn: string; + oprtngStrtYmd: string; + instlAprvYmd: string; + rprcsFcltCpct: number; + mtcnsNm: string; + ruswtPplLen: number; + ruswtPumpLen: number; + // 하수처리수 재이용현황 - 장내용수 + wholUseQnt: number; + ithlWshwtrQnt: number; + ithlClnQty: number; + ithlFrzgQty: number; + ithlLdhcUswtrQnt: number; + ithlDltnUswtrQnt: number; + ithlRuswtrQnt: number; + ithlEtcQnt: number; + ithlUnsdQnt: number; + // 하수처리수 재이용현황 - 장외용수 + otcntrUswtrWhol: number; + otcntrClnRstrmQnt: number; + otcntrWshwtrQnt: number; + otcntrLdhcUswtrQnt: number; + otcntrHdrplUswtrQnt: number; + otcntrRvrKeepQnt: number; + agrcltUswtrPstnQnt: number; + ugwtrChargQnt: number; + mdsrUswtrQnt: number; + // 기타 + rtlztnRt: number; +} + +export interface SewerageReuseStatusTab1Response { + status: string; + message?: string; + data: SewerageReuseStatusTotalItem[]; +} + +// ============================================================================ +// ★ [TAB2] 중수도 현황 명세 (추가 취합 완료) +// ============================================================================ +export interface GrayWaterStatusTotalItem { + ctpvNm: string; + sggNm: string | null; + statsYr: string; + admdstNm: string; + admdstCd: string; + rowType: string; + fcltNm: string; + fcltPstnCn: string; + bldgSeCn: string; + lglTrgtYn: string; + cmptnStrtYmd: string; + oprtngStrtYmd: string; + archWholArea: number; + prcsCpct: number; + wtrUseQnt: number; + ruswtrUtztnQnt: number; + sbstCntUtztnQnt: number; + mnUsgCn: string; + ruswtrOperAmt: number; +} + +export interface GrayWaterStatusTab2Response { + status: string; + message?: string; + data: GrayWaterStatusTotalItem[]; +} + +// ============================================================================ +// ★ [TAB3] 빗물이용시설현황 명세 (신규 추가) +// ============================================================================ +export interface RainwaterUtilizationStatusTotalItem { + ctpvNm: string; + sggNm: string | null; + statsYr: string; + admdstNm: string; + admdstCd: string; + rowType: string; + fcltNm: string; + bdstTypCn: string; + fcltPstnCn: string; + lglTrgtYn: string; + instlCmptnYmd: string; + instlAmt: number; + rcptMynCn: string; + filtrPrcsFcltYnExpln: string; + impdCpct: number; + anlUseQnt: number; + exlnUtlzUsgExpln: string; + anlOperAmt: number; +} +export interface RainwaterUtilizationStatusTab3Response { status: string; message?: string; data: RainwaterUtilizationStatusTotalItem[]; } + + +export interface SewageWwtRtlztnRegStatusTotalItem { + ctpvNm: string; + sggNm: string | null; + statsYr: string; + admdstNm: string; + admdstCd: string; + rowType: string; + bplcNm: string; + rprsvNm: string; + bplcPstnCn: string; + regDt: string; +} +export interface SewageWwtRtlztnRegStatusTab4Response { status: string; message?: string; data: SewageWwtRtlztnRegStatusTotalItem[]; } + +// ============================================================================ +// ★ [TAB5] 하·폐수처리수 재이용시설 시공업 인가현황 명세 (신규 추가) +// ============================================================================ +export interface SewageWwtRtlztnSncnStatusTotalItem { + ctpvNm: string; + sggNm: string | null; + statsYr: string; + admdstNm: string; + admdstCd: string; + rowType: string; + bplcNm: string; + rprsvNm: string; + bplcPstnCn: string; + coRegYmd: string; +} +export interface SewageWwtRtlztnSncnStatusTab5Response { status: string; message?: string; data: SewageWwtRtlztnSncnStatusTotalItem[]; } \ No newline at end of file From 428b199dd37a06186282035ef74fdee2d5f2ddb3 Mon Sep 17 00:00:00 2001 From: yangsein Date: Mon, 15 Jun 2026 01:02:13 +0900 Subject: [PATCH 12/12] =?UTF-8?q?feat:12=ED=95=98=EC=88=98=EB=B0=8F?= =?UTF-8?q?=EB=B6=84=EB=87=A8=EA=B4=80=EB=A6=AC=EC=82=AC=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../statistics-sewerage-industry-interface.ts | 114 ++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 apps/st/src/entities/statistics/model/statistics-total/statistics-sewerage-industry-interface.ts diff --git a/apps/st/src/entities/statistics/model/statistics-total/statistics-sewerage-industry-interface.ts b/apps/st/src/entities/statistics/model/statistics-total/statistics-sewerage-industry-interface.ts new file mode 100644 index 000000000..dd3803817 --- /dev/null +++ b/apps/st/src/entities/statistics/model/statistics-total/statistics-sewerage-industry-interface.ts @@ -0,0 +1,114 @@ + +/** 12. 하수및분뇨관리사업 공통 검색 파라미터 DTO 규격 */ +export interface SewerageIndustry { + ctpvNm?: string; + sggNm?: string; + statsYr?: string; + sewerBzmnCd?: string; + rowDataYn?: boolean; + summaryYn?: boolean; + summaryYearCnt?: number; +} + +/** [TAB1] 공공기관 하수분야 응답 DTO */ +export interface PublicSewageStaffTotalItem { + statsYr: string; + ctpvNm: string; + sggNm: string | null; + admdstNm: string; + admdstCd: string; + rowType: string; + ognzNm: string; + empGrssCnt: number; + pbadmsNope: number; + techWholNope: number; + engrkCnt: number; + mchnCnt: number; + prvtmCnt: number; + chmengCnt: number; + envCnt: number; + enjbEtcCnt: number; + etcCnt: number; +} + +/** [TAB1] 공공기관 하수분야 응답 DTO */ +export interface PublicSewageStaffTab1Response { + status: string; + message?: string; + data: PublicSewageStaffTotalItem[]; +} + + +/** [TAB2] 공공기관 분뇨분야 응답 DTO */ +export interface PublicHumanWasteStaffTotalItem{ + statsYr: string; + ctpvNm: string; + sggNm: string | null; + admdstNm: string; + admdstCd: string; + rowType: string; + ognzNm: string; + empGrssCnt: number; + pbadmsNope: number; + techWholNope: number; + engrkCnt: number; + mchnCnt: number; + prvtmCnt: number; + chmengCnt: number; + envCnt: number; + enjbEtcCnt: number; + etcCnt: number; +} + + +export interface PublicHumanWasteStaffTab2Response { + status: string; + message?: string; + data: PublicHumanWasteStaffTotalItem[]; +} + + +/** [TAB3] 분뇨수집 .운반업 응답 DTO */ +export interface HumanWasteCollectionOperItem { + statsYr: string; + ctpvNm: string; + sggNm: string; + admdstNm: string; + admdstCd: string; + rowType: string; + bzentyCnt: number; + empNope: number; + fcltSecrPrstCnt: number; + n3TonUdrCnt: number; + n4TonUdrCnt: number; + n8TonUdrCnt: number; + etcCnt: number; + clctCnvncPrfmncQnt: number; +} + +export interface HumanWasteCollectionTab3Response { + status: string; + message?: string; + data: HumanWasteCollectionOperItem[]; +} + +export interface IndividualSewageRelBusinessItem { + statsYr: string; + ctpvNm: string; + sggNm: string; + admdstNm: string; + admdstCd: string; + rowType: string; + designMdsrCnt: number; + designMdsrNope: number; + mfbizCnt: number; + mfbizMdsrNope: number; + mngCnt: number; + mngMdsrNope: number; +} + +export interface IndividualSewageTab4Response { + status: string; + message?: string; + data: IndividualSewageRelBusinessItem[]; +} \ No newline at end of file