diff --git a/apps/st/src/components/module/DataGrid.tsx b/apps/st/src/components/module/DataGrid.tsx index 7403df3b8..d99127482 100644 --- a/apps/st/src/components/module/DataGrid.tsx +++ b/apps/st/src/components/module/DataGrid.tsx @@ -61,6 +61,25 @@ type GridColumnWithStyleCallback = { ) => CellStyleCallbackResult; }; +function mergeGridRowsWithSourceMetadata( + rows: Record[], + sourceRows: Record[], + columnFieldNames: string[], +): Record[] { + const columnFieldSet = new Set(columnFieldNames); + return rows.map((row, index) => { + const source = sourceRows[index]; + if (!source) return row; + + const merged: Record = { ...row }; + for (const [key, value] of Object.entries(source)) { + if (columnFieldSet.has(key) || value === undefined) continue; + merged[key] = value as string | number; + } + return merged; + }); +} + function applyCellStyleCallbacksToColumns( gridView: unknown, columns: ColumnConfig[], @@ -1620,7 +1639,11 @@ useEffect(() => { r[f] = ds.getValue(i, f) as string | number; rows.push(r); } - return rows; + return mergeGridRowsWithSourceMetadata( + rows, + rowDataRef.current, + fieldNames, + ); }; } diff --git a/apps/st/src/components/statistics/input/StatisticsReviewHistory.tsx b/apps/st/src/components/statistics/input/StatisticsReviewHistory.tsx index 669ae0c76..ddd8bc8dc 100644 --- a/apps/st/src/components/statistics/input/StatisticsReviewHistory.tsx +++ b/apps/st/src/components/statistics/input/StatisticsReviewHistory.tsx @@ -18,8 +18,6 @@ const StatisticsReviewHistory = () => { { name: "statsYr", fieldName: "statsYr", header: { text: "연도" }, editable: false }, { name: "ctpvNm", fieldName: "ctpvNm", header: { text: "시도" }, editable: false }, { name: "sggNm", fieldName: "sggNm", header: { text: "시군구" }, editable: false }, - { name: "sewerBzmnCd", fieldName: "sewerBzmnCd", header: { text: "하수사업자코드" }, editable: false }, - { name: "statsCd", fieldName: "statsCd", header: { text: "통계코드" }, editable: false }, { name: "menuNm", fieldName: "menuNm", header: { text: "통계항목" }, editable: false }, { name: "chckerOpinionSm", fieldName: "chckerOpinionSm", header: { text: "검토자의견" }, editable: false }, { name: "wrterOpinionSm", fieldName: "wrterOpinionSm", header: { text: "처리내역" }, editable: false }, @@ -29,8 +27,6 @@ const StatisticsReviewHistory = () => { "statsYr", "ctpvNm", "sggNm", - "sewerBzmnCd", - "statsCd", "menuNm", "chckerOpinionSm", "wrterOpinionSm", diff --git a/apps/st/src/components/statistics/management/AdmdstRenewalTab.tsx b/apps/st/src/components/statistics/management/AdmdstRenewalTab.tsx index e125ab3be..8e8aba373 100644 --- a/apps/st/src/components/statistics/management/AdmdstRenewalTab.tsx +++ b/apps/st/src/components/statistics/management/AdmdstRenewalTab.tsx @@ -55,7 +55,16 @@ const AdmdstRenewalTab = ({ tab, statsYr }: Props) => { 추가된 행정구역 자료 -
+
+ + + + { + if (!newDate) return; + onChange(newDate); + setOpen(false); + }} + /> + + +
+ ); +}; const getSidoListFetcher = async (): Promise> => { return statisticsApi.getSidoList(); @@ -63,13 +153,45 @@ const LogHistory = () => { string, string | number > | null>(null); - const [startDate, setStartDate] = useState(() => + const [startDate, setStartDate] = useState(() => dayjs().subtract(1, "month").startOf("day").toDate(), ); - const [endDate, setEndDate] = useState(() => + const [endDate, setEndDate] = useState(() => dayjs().endOf("day").toDate(), ); + const handleStartDateChange = (date: Date) => { + const next = dayjs(date).startOf("day"); + const end = dayjs(endDate).startOf("day"); + + if (next.isAfter(end, "day")) { + alert("시작일은 종료일보다 늦을 수 없습니다."); + return; + } + if (next.isBefore(end.subtract(MAX_DATE_RANGE_YEARS, "year"), "day")) { + alert("조회 기간은 최대 1년까지 설정할 수 있습니다."); + return; + } + + setStartDate(next.toDate()); + }; + + const handleEndDateChange = (date: Date) => { + const next = dayjs(date).startOf("day"); + const start = dayjs(startDate).startOf("day"); + + if (next.isBefore(start, "day")) { + alert("종료일은 시작일보다 빠를 수 없습니다."); + return; + } + if (next.isAfter(start.add(MAX_DATE_RANGE_YEARS, "year"), "day")) { + alert("조회 기간은 최대 1년까지 설정할 수 있습니다."); + return; + } + + setEndDate(next.toDate()); + }; + const { data: sidoList } = useSWR>( "log-history-sido-list", getSidoListFetcher, @@ -141,10 +263,8 @@ const LogHistory = () => { const param: StatisticsLogRequest = { page: page1Based, size: PAGE_SIZE, - startDate: startDate - ? dayjs(startDate).format("YYYY-MM-DD") - : "", - endDate: endDate ? dayjs(endDate).format("YYYY-MM-DD") : "", + startDate: dayjs(startDate).format("YYYY-MM-DD"), + endDate: dayjs(endDate).format("YYYY-MM-DD"), ...(!(statsCd === "" || statsCd === "ALL") && { statsCd }), ...(sido && sido !== "ALL" && { ctpvNm: sido }), ...(gugun && gugun !== "ALL" && { sggNm: gugun }), @@ -199,17 +319,15 @@ const LogHistory = () => { const handleSearch = async (e: React.FormEvent) => { e.preventDefault(); - if (startDate && endDate) { - const start = dayjs(startDate); - const end = dayjs(endDate); - if (start.isAfter(end, "day")) { - alert("시작일은 종료일보다 늦을 수 없습니다."); - return; - } - if (end.isBefore(start, "day")) { - alert("종료일은 시작일보다 빠를 수 없습니다."); - return; - } + const start = dayjs(startDate); + const end = dayjs(endDate); + if (end.isBefore(start, "day")) { + alert("종료일은 시작일보다 빠를 수 없습니다."); + return; + } + if (!isWithinMaxDateRange(startDate, endDate)) { + alert("조회 기간은 최대 1년까지 설정할 수 있습니다."); + return; } await loadLogs(1); @@ -310,15 +428,15 @@ const LogHistory = () => {
- -
diff --git a/apps/st/src/components/statistics/management/hooks/useAdmdstRenewalTab.ts b/apps/st/src/components/statistics/management/hooks/useAdmdstRenewalTab.ts index 2d1ab2d86..a45d3923f 100644 --- a/apps/st/src/components/statistics/management/hooks/useAdmdstRenewalTab.ts +++ b/apps/st/src/components/statistics/management/hooks/useAdmdstRenewalTab.ts @@ -60,6 +60,7 @@ export function useAdmdstRenewalTab() { return; } + await run(async () => { const userInfo = await getUserInfo(); const addedList: AdmdstRenewalAddedSaveItem[] = addedRowData @@ -79,13 +80,12 @@ const addedList: AdmdstRenewalAddedSaveItem[] = addedRowData userId: userInfo.userUnqId, })); + + const reducedList: AdmdstRenewalReducedSaveItem[] = reducedRowData.map((row) => ({ adstCd: String(row.adstCd ?? ""), })); -console.log("reducedList:", reducedList); -console.log("addedList:", addedList); -console.log("reducedList 이상한 것:", reducedList.filter(r => r.adstCd.length !== 10)); const res = await statisticsPpltnApi.saveChangedArea(reducedList, addedList); if (res.status === "success") { await loadAll(statsYr); @@ -110,8 +110,6 @@ console.log("reducedList 이상한 것:", reducedList.filter(r => r.adstCd.lengt if (res.status === "success" && Array.isArray(res.data) && res.data.length > 0) { setCandidates(res.data); const rowIndex = addedRowData.findIndex((r) => r.adstCd === rowData.adstCd); - console.log("rowData.adstCd:", rowData.adstCd); // ← 여기 - console.log("rowIndex:", rowIndex); // ← 여기 selectedRowIndexRef.current = rowIndex; setDialogOpen(true); } else { @@ -131,9 +129,20 @@ console.log("reducedList 이상한 것:", reducedList.filter(r => r.adstCd.lengt : row, ), ); + selectedRowIndexRef.current = null; }; +const handleRefresh = async () => { + await run(async () => { + const res = await statisticsPpltnApi.refreshMaterializedViews(); + if (res.status === "success") { + alert("뷰 갱신이 완료되었습니다."); + } else { + alert("뷰 갱신 중 오류가 발생했습니다."); + } + }); +}; const reset = () => { setReducedRowData([]); setAddedRowData([]); @@ -156,6 +165,7 @@ console.log("reducedList 이상한 것:", reducedList.filter(r => r.adstCd.lengt handleFinal, handleRowClick, handleCodeSelect, + handleRefresh, reset, }; } \ No newline at end of file diff --git a/apps/st/src/components/util/column/total/SewagePipeTotColumn.ts b/apps/st/src/components/util/column/total/SewagePipeTotColumn.ts index 0cf31e2b1..d9e3e79af 100644 --- a/apps/st/src/components/util/column/total/SewagePipeTotColumn.ts +++ b/apps/st/src/components/util/column/total/SewagePipeTotColumn.ts @@ -813,8 +813,6 @@ const rawSewagePipeTotList = [ { name: "yy25Step3MnhlCnt", fieldName: "yy25Step3MnhlCnt", header: { text: "콘크리트" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, { name: "yy25Step4MnhlCnt", fieldName: "yy25Step4MnhlCnt", header: { text: "기타" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, { name: "yy25Step5MnhlCnt", fieldName: "yy25Step5MnhlCnt", header: { text: "추락방지시설\n설치맨홀(개)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, - - { name: "rmrk", fieldName: "rmrk", header: { text: "비고" }, editable: false }, ], columnLayout: [ { name: "addrGroup", header: { text: "행정구역" }, items: ["ctpvNm", "sggNm"] }, @@ -922,8 +920,7 @@ const rawSewagePipeTotList = [ { name: "yy25MtrlGroup", header: { text: "맨홀뚜껑 재질(개)" }, items: ["yy25WholMnhlCnt", "yy25Step1MnhlCnt", "yy25Step2MnhlCnt", "yy25Step3MnhlCnt", "yy25Step4MnhlCnt"] }, "yy25Step5MnhlCnt", ], - }, - "rmrk", + } ], }, diff --git a/apps/st/src/components/util/column/total/SeweragePentrationRateTotColumn.ts b/apps/st/src/components/util/column/total/SeweragePentrationRateTotColumn.ts index c4cff90cb..7ebb6a520 100644 --- a/apps/st/src/components/util/column/total/SeweragePentrationRateTotColumn.ts +++ b/apps/st/src/components/util/column/total/SeweragePentrationRateTotColumn.ts @@ -135,10 +135,7 @@ export const SeweragePentrationRateTotColumn = { valueType: ValueType.NUMBER, dataFormat: '#,##0.0', excludeFromExcelExport: true, - }, - - // 비고 - { name: 'rmrk', fieldName: 'rmrk', header: { text: '비고' } }, + } ].map((col) => ({ ...col, editable: false })), columnLayout: [ @@ -200,7 +197,6 @@ export const SeweragePentrationRateTotColumn = { 'staaPsplyRt', 'altdPrcsPsplyRt', 'sewerInstlRt', - 'rurPsplyRt', - 'rmrk', + 'rurPsplyRt' ] }; \ 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 index 19fc4d1fb..801b59d0a 100644 --- a/apps/st/src/components/util/column/total/sewerPrivateSewageColumn/SewerPrivateSewageTab2Column.ts +++ b/apps/st/src/components/util/column/total/sewerPrivateSewageColumn/SewerPrivateSewageTab2Column.ts @@ -4,7 +4,7 @@ export const PrivateSewageTab2ColumnConfig = { tabKey: "ST00080002", tabLabel: "정화조현황", rowStartNum: 4, - fixedColumns: 3, + fixedColumns: 2, columns: [ { name: "ctpvNm", fieldName: "ctpvNm", header: { text: "시도" }, editable: false }, { name: "sggNm", fieldName: "sggNm", header: { text: "시군구" }, editable: false }, diff --git a/apps/st/src/components/util/column/total/sludgeProcessing/DigestionTankTab2Column.ts b/apps/st/src/components/util/column/total/sludgeProcessing/DigestionTankTab2Column.ts index 65b1c50bc..06d7f7f17 100644 --- a/apps/st/src/components/util/column/total/sludgeProcessing/DigestionTankTab2Column.ts +++ b/apps/st/src/components/util/column/total/sludgeProcessing/DigestionTankTab2Column.ts @@ -9,34 +9,36 @@ export const DigestionTankTab2ColumnConfig = { { 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: "swtrmtFcltNm", fieldName: "swtrmtFcltNm", header: { text: "하수\n처리장명" }, editable: false }, + + { name: "digstnEnYn", fieldName: "digstnEnYn", header: { text: "소화조 유/무\n(Y/N)선택" }, editable: false }, + { name: "digstnCpct", fieldName: "digstnCpct", header: { text: "설계\n용량(㎥)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "actlDrvnCpct", fieldName: "actlDrvnCpct", header: { text: "실제운전\n용량(㎥)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "digstnGasInputQnt", fieldName: "digstnGasInputQnt", header: { text: "투입량\n(㎥/일)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "digstnGasInputSldDnst", fieldName: "digstnGasInputSldDnst", header: { text: "투입 TS 농도\n(%)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "digstnTnkOprtngDayCnt", fieldName: "digstnTnkOprtngDayCnt", header: { text: "가동일수\n(연간)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "ocrnGasQnt", fieldName: "ocrnGasQnt", 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 } + { name: "digstnGasUtztnWholPrst", fieldName: "digstnGasUtztnWholPrst", header: { text: "계" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "digstnGasUtztnNtslQnt", fieldName: "digstnGasUtztnNtslQnt", header: { text: "판매" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "digstnGasUtztnEtgtQnt", fieldName: "digstnGasUtztnEtgtQnt", header: { text: "발전" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "digstnGasUtztnItslfQnt", fieldName: "digstnGasUtztnItslfQnt", header: { text: "자체활용" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + { name: "digstnGasUtztnEtcQnt", fieldName: "digstnGasUtztnEtcQnt", header: { text: "기타(CNG등)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false }, + + { name: "surplsGasQnt", fieldName: "surplsGasQnt", 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" + "swtrmtFcltNm", + { name: "operGroup", header: { text: "소화조 운영현황" }, items: [ + "digstnEnYn", "digstnCpct", "actlDrvnCpct", "digstnGasInputQnt", "digstnGasInputSldDnst", "digstnTnkOprtngDayCnt", "ocrnGasQnt", + { + name: "gasUsageGroup", + header: { text: "소화가스 이용현황(㎥/일)" }, + items: ["digstnGasUtztnWholPrst", "digstnGasUtztnNtslQnt", "digstnGasUtztnEtgtQnt", "digstnGasUtztnItslfQnt", "digstnGasUtztnEtcQnt"] + }, + "surplsGasQnt" ] }, ] }; \ No newline at end of file diff --git a/apps/st/src/entities/statistics/api/statistics-ppltn-service.ts b/apps/st/src/entities/statistics/api/statistics-ppltn-service.ts index a067b8612..84565434d 100644 --- a/apps/st/src/entities/statistics/api/statistics-ppltn-service.ts +++ b/apps/st/src/entities/statistics/api/statistics-ppltn-service.ts @@ -235,6 +235,14 @@ public async getLawCdsByName( ); return response.data; } + + /** 행정동 갱신 — MATERIALIZED VIEW 갱신 */ + public async refreshMaterializedViews(): Promise<{ status: string; message?: string }> { + const response = await stClient.post<{ status: string; message?: string }>( + `${ENDPOINT.ST_SERVICE.PPLTN}/admdst-renewal/refresh`, + ); + return response.data; + } } diff --git a/apps/st/src/entities/statistics/model/statistics-interface.ts b/apps/st/src/entities/statistics/model/statistics-interface.ts index f748a5c04..571835065 100644 --- a/apps/st/src/entities/statistics/model/statistics-interface.ts +++ b/apps/st/src/entities/statistics/model/statistics-interface.ts @@ -463,8 +463,6 @@ export interface StatiscicsManagementReviewHistoryItem { statsYr: string; ctpvNm: string; sggNm: string; - sewerBzmnCd: string; - statsCd: string; menuNm: string; wrterOpinionSm: string | null; chckerOpinionSm: string | null; 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 index bae596f83..0f5229385 100644 --- 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 @@ -101,21 +101,22 @@ export interface DigestionTankOperStatusTotalItem { ctpvNm: string; sggNm: string; admdstNm: string; + admdstCd: 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; + swtrmtFcltNm: string; + digstnEnYn: string; + digstnCpct: number; + actlDrvnCpct: number; + digstnGasInputQnt: number; + digstnGasInputSldDnst: number; + digstnTnkOprtngDayCnt: number; + ocrnGasQnt: number; + digstnGasUtztnWholPrst: number; + digstnGasUtztnNtslQnt: number; + digstnGasUtztnEtgtQnt: number; + digstnGasUtztnItslfQnt: number; + digstnGasUtztnEtcQnt: number; + surplsGasQnt: number; } export interface DigestionTankOperTab2Response { status: string; message?: string; data: DigestionTankOperStatusTotalItem[]; }