From 02655510bd60c75c92829f8694bbc9697a60d83e Mon Sep 17 00:00:00 2001 From: bicochan Date: Fri, 8 May 2026 17:05:35 +0900 Subject: [PATCH 1/7] =?UTF-8?q?[st]=20refactor:=20=EC=95=8C=EB=A6=BC?= =?UTF-8?q?=ED=86=A1=20=EB=B0=9C=EC=86=A1=20=ED=8E=98=EC=9D=B4=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../management/NoticeTalkSending.tsx | 55 +++++++++---------- .../notice-talk-sending-service.ts | 12 ++++ 2 files changed, 38 insertions(+), 29 deletions(-) diff --git a/apps/st/src/components/statistics/management/NoticeTalkSending.tsx b/apps/st/src/components/statistics/management/NoticeTalkSending.tsx index ff0cae557..df0076a76 100644 --- a/apps/st/src/components/statistics/management/NoticeTalkSending.tsx +++ b/apps/st/src/components/statistics/management/NoticeTalkSending.tsx @@ -8,14 +8,15 @@ import { type RowSelectionState, useReactTable, } from "@tanstack/react-table"; +import type { AxiosError } from "axios"; -import { client } from "@/shared/api/client"; -import { ENDPOINT } from "@/shared/config"; -import { useAlertStore } from "@/shared/lib"; +import { useAlertStore, useMutationWrapper } from "@/shared/lib"; +import type { ApiErrorResponse } from "@/shared/model"; import { type AdstZoneResponseDto, type BusinessSendRequest, type BusinessSendResponse, + noticeTalkSendingApi, type SelectedRecipient, type SewerStUserItem, useNtftalkTemplateList, @@ -122,10 +123,26 @@ function NtitlkSndng() { const [previewRecipient, setPreviewRecipient] = useState(null); const [isPreviewOpen, setIsPreviewOpen] = useState(false); - const [isSending, setIsSending] = useState(false); const [userListRowSelection, setUserListRowSelection] = useState({}); + const sendMutation = useMutationWrapper< + BusinessSendResponse, + AxiosError, + BusinessSendRequest + >({ + mutationFn: (payload) => noticeTalkSendingApi.sendBusiness(payload), + onSuccess: (data) => { + if (!data.success) { + alert(data.message || "알림톡 발송 요청에 실패했습니다."); + return; + } + alert(data.message || "알림톡 발송이 요청되었습니다."); + setSelectedRecipients([]); + setCommonVariableValues({}); + }, + }); + const { data: templatePageData } = useNtftalkTemplateList(); const templates = templatePageData?.content ?? []; @@ -321,7 +338,7 @@ function NtitlkSndng() { }), ); if (missingVariable) { - alert("필수 변수값이 입력되지 않은 발송 대상이 있습니다."); + alert("변수값이 입력되지 않은 발송 대상이 있습니다."); return; } if ( @@ -331,34 +348,13 @@ function NtitlkSndng() { ) return; - const payload: BusinessSendRequest = { + sendMutation.mutate({ templateCode: selectedTemplateCode, callback: "032-590-4000", recipients: selectedRecipients.map((recipient) => getMergedVariables(recipient, commonVariableValues), ), - }; - - try { - setIsSending(true); - const response = await client.post( - `${ENDPOINT.NTFTALK_SERVICE.ATA_SEND}/business`, - payload, - ); - const data = response.data; - if (!data.success) { - alert(data.message || "알림톡 발송 요청에 실패했습니다."); - return; - } - - alert(data.message || `알림톡 발송이 요청되었습니다.`); - setSelectedRecipients([]); - setCommonVariableValues({}); - } catch { - alert("알림톡 발송 중 오류가 발생했습니다."); - } finally { - setIsSending(false); - } + }); }; return ( @@ -567,7 +563,8 @@ function NtitlkSndng() { color="green" onClick={handleSend} disabled={ - isSending || selectedRecipients.length === 0 + sendMutation.isPending || + selectedRecipients.length === 0 } > 발송 diff --git a/apps/st/src/entities/statistics/api/notice-talk/notice-talk-sending-service.ts b/apps/st/src/entities/statistics/api/notice-talk/notice-talk-sending-service.ts index 476c8fecd..a770b45e2 100644 --- a/apps/st/src/entities/statistics/api/notice-talk/notice-talk-sending-service.ts +++ b/apps/st/src/entities/statistics/api/notice-talk/notice-talk-sending-service.ts @@ -3,6 +3,8 @@ import { ENDPOINT } from "@/shared/config"; import type { PageableList } from "@/shared/model"; import type { + BusinessSendRequest, + BusinessSendResponse, NtftalkTemplate, TemplateVariablesResponse, } from "../../model/notice-talk/notice-talk-sending-interface"; @@ -38,6 +40,16 @@ export class NoticeTalkSendingApiService { ); return response.data; } + + async sendBusiness( + payload: BusinessSendRequest, + ): Promise { + const response = await client.post( + `${ENDPOINT.NTFTALK_SERVICE.ATA_SEND}/business`, + payload, + ); + return response.data; + } } export const noticeTalkSendingApi = new NoticeTalkSendingApiService(); From bd1e1e3d1ff2936020a61f4ad302993bb882de9e Mon Sep 17 00:00:00 2001 From: bicochan Date: Fri, 8 May 2026 17:30:53 +0900 Subject: [PATCH 2/7] =?UTF-8?q?[st]=20chore:=20=EA=B2=80=EC=A6=9D=20?= =?UTF-8?q?=EB=B3=80=EC=88=98=20=EC=9D=B8=ED=92=8B=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../verification/HbStatisticsVerification.tsx | 40 +++++++++++++------ .../verification/VerificationTargetDialog.tsx | 5 +-- 2 files changed, 30 insertions(+), 15 deletions(-) diff --git a/apps/st/src/components/statistics/verification/HbStatisticsVerification.tsx b/apps/st/src/components/statistics/verification/HbStatisticsVerification.tsx index 2206b5d3b..4dd37c89d 100644 --- a/apps/st/src/components/statistics/verification/HbStatisticsVerification.tsx +++ b/apps/st/src/components/statistics/verification/HbStatisticsVerification.tsx @@ -114,10 +114,10 @@ export default function HbStatisticsVerification() { const [verifyType, setVerifyType] = useState("ALL"); const [formState, setFormState] = useState({ - mu: "0", - percentile: "0", - a: "0", - c: "0", + mu: "", + percentile: "", + a: "", + c: "", verificationYear: defaultYear, }); const [selectedTargets, setSelectedTargets] = useState([]); @@ -559,11 +559,14 @@ export default function HbStatisticsVerification() {
-
-
-
-
diff --git a/apps/st/src/components/statistics/verification/VerificationTargetDialog.tsx b/apps/st/src/components/statistics/verification/VerificationTargetDialog.tsx index ce9b1d26e..eb56ef7d8 100644 --- a/apps/st/src/components/statistics/verification/VerificationTargetDialog.tsx +++ b/apps/st/src/components/statistics/verification/VerificationTargetDialog.tsx @@ -4,7 +4,6 @@ import { useEffect, useState } from "react"; import { Controller, useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; -import { Search } from "lucide-react"; import { CodeSelect } from "@/entities/code/ui/code-select"; import { verificationTargetManagementApi } from "@/entities/statistics/api/verification-target-management-service"; @@ -368,7 +367,7 @@ export function VerificationTargetDialog({ type="number" min="0" max="0.5" - step="0.1" + step="0.01" value={ field.value ?? "" @@ -385,7 +384,7 @@ export function VerificationTargetDialog({ : null, ) } - placeholder="(0 ~ 0.5, 0.1 단위)" + placeholder="(0 ~ 0.5, 0.01 단위)" /> )} From 9b8d48d39543c05ddd04cc2ad405327df458d03e Mon Sep 17 00:00:00 2001 From: rsone001 Date: Fri, 8 May 2026 17:53:23 +0900 Subject: [PATCH 3/7] =?UTF-8?q?[st]=20-=20=EA=B2=80=ED=86=A0=20=ED=95=98?= =?UTF-8?q?=EC=88=98=EA=B4=80=EB=A1=9C,=20=ED=95=98=EC=88=98=EA=B4=80?= =?UTF-8?q?=EB=A1=9C=20=EC=9C=A0=EC=A7=80=EB=B3=B4=EC=88=98=20response=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/module/SplitDataGrid.tsx | 4 + .../input/PublicSewageFacilityInp.tsx | 171 ++++++++++++++---- .../review/SewagePipeMaintainRvw.tsx | 8 +- .../statistics/review/SewagePipeRvw.tsx | 30 ++- apps/st/src/components/type/DataGrid.type.ts | 4 + .../MainFacilityInpColumn.ts | 4 +- ...tatistics-input-sew-supply-rate-service.ts | 4 +- ...cs-public-sewage-facility-input-service.ts | 16 +- .../statistics-sewage-pipe-input-service.ts | 22 +-- ...-public-sewage-facility-input-interface.ts | 32 ++++ ...istics-sewage-pipe-mtc-review-interface.ts | 15 +- ...statistics-sewage-pipe-review-interface.ts | 33 +++- 12 files changed, 277 insertions(+), 66 deletions(-) diff --git a/apps/st/src/components/module/SplitDataGrid.tsx b/apps/st/src/components/module/SplitDataGrid.tsx index 5dd359ded..d7e33ea9b 100644 --- a/apps/st/src/components/module/SplitDataGrid.tsx +++ b/apps/st/src/components/module/SplitDataGrid.tsx @@ -14,6 +14,8 @@ const SplitDataGrid = ({ setRightRowData, leftOnRowClick, leftCellStyleCallback, + rightOnItemChecked, + rightOnItemAllChecked, enableSort = false, }: SplitDataGridProps) => { return ( @@ -35,6 +37,8 @@ const SplitDataGrid = ({ enableSort={enableSort} rowData={rightRowData} setRowData={setRightRowData} + onItemChecked={rightOnItemChecked} + onItemAllChecked={rightOnItemAllChecked} /> ); diff --git a/apps/st/src/components/statistics/input/PublicSewageFacilityInp.tsx b/apps/st/src/components/statistics/input/PublicSewageFacilityInp.tsx index a48fa76f2..9e8cb8871 100644 --- a/apps/st/src/components/statistics/input/PublicSewageFacilityInp.tsx +++ b/apps/st/src/components/statistics/input/PublicSewageFacilityInp.tsx @@ -10,6 +10,8 @@ import type { PublicSewageFacilityRequestParams, PublicSewageFacilitySaveItem, PublicSewageFacilitySaveRequest, + PublicSewageMainFacilityMappingSaveItem, + PublicSewageMainFacilityMappingSaveRequest, PublicSewageMappingTargetsRequestParams, } from '@/entities/statistics/model/input/statistics-public-sewage-facility-input-interface'; import { @@ -199,6 +201,11 @@ const PublicSewageFacilityInp = () => { return noApplicableVisible ? Boolean(isNotApplicable) : false; }; + const isCheckedRow = (row: Record): boolean => { + const v = row.__checked; + return v === 1 || v === '1' || v === 'Y' || v === 'y' || v === 'true'; + }; + const executeSubmit = async (submitInfo: StatisticsInputSubmitInfoFormData) => { const verificationBlocked = getVerificationPeriodActionBlockedMessage(periodCheckList); if (verificationBlocked) { @@ -284,51 +291,113 @@ const PublicSewageFacilityInp = () => { const saveFunc = () => { void (async () => { - if (activeTab !== 'ST00060001') { - alert('ST00060002 저장 API 연동 전입니다.'); - return; - } - if (!lastSearchedValues || !searchParamRef.current) { alert('먼저 조회를 진행해주세요.'); return; } - const currentData = rowDataMap.get('ST00060001') ?? []; - if (!currentData || currentData.length === 0) { - alert('저장할 데이터가 없습니다.'); - return; - } - if (!(await confirm("저장 하시겠습니까?"))) return; - const userInfo = await getUserInfo(); - const saveList = buildFacilitySaveList(currentData); - - const param: PublicSewageFacilitySaveRequest = { - statsYr: lastSearchedValues.year ?? '', - ctpvNm: lastSearchedValues.sido ?? '', - sggNm: lastSearchedValues.gugun === 'ALL' ? '' : lastSearchedValues.gugun ?? '', - sewerBzmnCd: lastSearchedValues.bsnmCd ?? '', - userId: userInfo.userUnqId, - superAdmin: isAdminInfo ? 'superAdmin' : '', - noApplicable: getNoApplicableForRequest(), - saveList, - }; - try { setIsLoading(true); - const response = await statisticsInputPublicSewageFacilityApiService.savePublicSewageFacility(param); - if (response.status === 'success') { - alert('저장되었습니다.'); - await loadPublicSewageFacilityActiveTabData( - 'ST00060001', - searchParamRef.current, - lastSearchedValues, - ); - } else { - alert(response.message || '데이터 저장 중 오류가 발생했습니다.'); + const userInfo = await getUserInfo(); + + if (activeTab === 'ST00060001') { + const currentData = rowDataMap.get('ST00060001') ?? []; + if (!currentData || currentData.length === 0) { + alert('저장할 데이터가 없습니다.'); + return; + } + + const saveList = buildFacilitySaveList(currentData); + const param: PublicSewageFacilitySaveRequest = { + statsYr: lastSearchedValues.year ?? '', + ctpvNm: lastSearchedValues.sido ?? '', + sggNm: lastSearchedValues.gugun === 'ALL' ? '' : lastSearchedValues.gugun ?? '', + sewerBzmnCd: lastSearchedValues.bsnmCd ?? '', + userId: userInfo.userUnqId, + superAdmin: isAdminInfo ? 'superAdmin' : '', + noApplicable: getNoApplicableForRequest(), + saveList, + }; + + const response = await statisticsInputPublicSewageFacilityApiService.savePublicSewageFacility(param); + if (response.status === 'success') { + alert('저장되었습니다.'); + await loadPublicSewageFacilityActiveTabData( + 'ST00060001', + searchParamRef.current, + lastSearchedValues, + ); + } else { + alert(response.message || '데이터 저장 중 오류가 발생했습니다.'); + } + return; } + + if (activeTab === 'ST00060002') { + const leftRows = rowDataMap.get('MainFacility') ?? []; + const rightRows = rowDataMap.get('MainFacilityTarget') ?? []; + if (!leftRows.length || !rightRows.length) { + alert('저장할 데이터가 없습니다.'); + return; + } + if (!selectedMainFacilitySn) { + alert('좌측에서 중심 처리 시설을 먼저 선택해주세요.'); + return; + } + + const selectedMain = leftRows.find((r) => String(r.fcltSn ?? '') === selectedMainFacilitySn); + if (!selectedMain) { + alert('선택된 중심 처리 시설 정보를 찾을 수 없습니다.'); + return; + } + + const saveList: PublicSewageMainFacilityMappingSaveItem[] = rightRows + .filter((r) => isCheckedRow(r)) + .map((r) => ({ + fcltSn: String(r.fcltSn ?? ''), + admdstCd: String(r.admdstCd ?? ''), + admdstNm: String(r.admdstNm ?? ''), + swtrmtFcltCd: String(r.swtrmtFcltCd ?? ''), + swtrmtFcltNm: String(r.swtrmtFcltNm ?? ''), + isMainYn: String(r.isMainYn ?? ''), + })); + + const deleteList = rightRows + .filter((r) => String(r.mappedYn ?? '') === 'Y' && !isCheckedRow(r)) + .map((r) => String(r.fcltSn ?? '')) + .filter((sn) => sn !== ''); + + const param: PublicSewageMainFacilityMappingSaveRequest = { + statsYr: lastSearchedValues.year ?? '', + sewerBzmnCd: lastSearchedValues.bsnmCd ?? '', + mainFcltSn: String(selectedMain.fcltSn ?? ''), + mainFcltNm: String(selectedMain.swtrmtFcltNm ?? ''), + prxyBzentyNmCn: String(selectedMain.prxyBzentyNmCn ?? ''), + cnsgnCstVl: Number(selectedMain.cnsgnCstVl ?? 0), + wholNope: Number(selectedMain.wholNope ?? 0), + prxyCtrtCn: String(selectedMain.prxyCtrtCn ?? ''), + userId: userInfo.userUnqId, + saveList, + deleteList, + }; + + const response = await statisticsInputPublicSewageFacilityApiService.saveMainFacilityMapping(param); + if (response.status === 'success') { + alert('저장되었습니다.'); + await loadPublicSewageFacilityActiveTabData( + 'ST00060002', + searchParamRef.current, + lastSearchedValues, + ); + } else { + alert(response.message || '데이터 저장 중 오류가 발생했습니다.'); + } + return; + } + + alert('현재 탭 저장 API 연동 전입니다.'); } finally { setIsLoading(false); } @@ -753,6 +822,38 @@ const PublicSewageFacilityInp = () => { ? leftMainFacilityCellStyleCallback : undefined } + rightOnItemChecked={ + tab.tabKey === 'ST00060002' + ? (itemIndex, checked) => { + setRowDataMap((prev) => { + const next = new Map(prev); + const rows = [...(next.get('MainFacilityTarget') ?? [])]; + if (!rows[itemIndex]) return prev; + rows[itemIndex] = { + ...rows[itemIndex], + __checked: checked ? 'Y' : '', + }; + next.set('MainFacilityTarget', rows); + return next; + }); + } + : undefined + } + rightOnItemAllChecked={ + tab.tabKey === 'ST00060002' + ? (checked) => { + setRowDataMap((prev) => { + const next = new Map(prev); + const rows = [...(next.get('MainFacilityTarget') ?? [])].map((r) => ({ + ...r, + __checked: checked ? 'Y' : '', + })); + next.set('MainFacilityTarget', rows); + return next; + }); + } + : undefined + } setLeftRowData={(updater) => { setRowDataMap((prev) => { const next = new Map(prev); diff --git a/apps/st/src/components/statistics/review/SewagePipeMaintainRvw.tsx b/apps/st/src/components/statistics/review/SewagePipeMaintainRvw.tsx index 50ec7c929..295802cdc 100644 --- a/apps/st/src/components/statistics/review/SewagePipeMaintainRvw.tsx +++ b/apps/st/src/components/statistics/review/SewagePipeMaintainRvw.tsx @@ -9,8 +9,8 @@ import ApproveButton from "@/components/button/ApproveButton"; import ReturnButton from "@/components/button/ReturnButton"; import ReviewOpinionButton from "@/components/button/ReviewOpinionButton"; import SaveButton from "@/components/button/SaveButton"; -import SearchForm from "@/components/module/SearchForm"; import DataGrid from "@/components/module/DataGrid"; +import SearchForm from "@/components/module/SearchForm"; import type { ColumnConfig, ColumnInfo, ColumnInfoList } from "@/components/type/DataGrid.type"; import type { SearchFormValues } from "@/components/type/SearchForm.type"; import { SewagePipeMaintainColumnInfoList } from "@/components/util/column/review/SewagePipeMaintainRvwColumn"; @@ -40,10 +40,12 @@ function getListDataByTab(tabKey: string, responseData: unknown): object[] { return []; } if (tabKey === "ST00030001") { - return (((responseData as { mtcStatusList?: unknown[] }).mtcStatusList) ?? []) as object[]; + const list = (responseData as { sewerPipeMtcList?: unknown }).sewerPipeMtcList; + return Array.isArray(list) ? (list as object[]) : []; } if (tabKey === "ST00030002") { - return (((responseData as { drdgStatusList?: unknown[] }).drdgStatusList) ?? []) as object[]; + const list = (responseData as { sewerPipeDrdgList?: unknown }).sewerPipeDrdgList; + return Array.isArray(list) ? (list as object[]) : []; } return []; } diff --git a/apps/st/src/components/statistics/review/SewagePipeRvw.tsx b/apps/st/src/components/statistics/review/SewagePipeRvw.tsx index 0f6a96e2f..96bfc9128 100644 --- a/apps/st/src/components/statistics/review/SewagePipeRvw.tsx +++ b/apps/st/src/components/statistics/review/SewagePipeRvw.tsx @@ -9,8 +9,8 @@ import ApproveButton from "@/components/button/ApproveButton"; import ReturnButton from "@/components/button/ReturnButton"; import ReviewOpinionButton from "@/components/button/ReviewOpinionButton"; import SaveButton from "@/components/button/SaveButton"; -import SearchForm from "@/components/module/SearchForm"; import DataGrid from "@/components/module/DataGrid"; +import SearchForm from "@/components/module/SearchForm"; import type { ColumnInfo } from "@/components/type/DataGrid.type"; import type { SearchFormValues } from "@/components/type/SearchForm.type"; import { SewagePipeColumnInfoListRvw } from "@/components/util/column/review/SewagePipeRvwColumn"; @@ -37,6 +37,28 @@ async function fetchSewagePipeReviewTab( return null; } +function extractReviewRowsFromResponseData( + tabKey: string, + data: unknown, +): object[] { + if (Array.isArray(data)) return data as object[]; + if (!data || typeof data !== "object") return []; + + const d = data as Record; + switch (tabKey) { + case "ST00020001": + return Array.isArray(d.regionalStatusList) ? (d.regionalStatusList as object[]) : []; + case "ST00020002": + return Array.isArray(d.facilityStatusList) ? (d.facilityStatusList as object[]) : []; + case "ST00020003": + return Array.isArray(d.pipeTypeList) ? (d.pipeTypeList as object[]) : []; + case "ST00020004": + return Array.isArray(d.pipeDiamList) ? (d.pipeDiamList as object[]) : []; + default: + return []; + } +} + // 하수도 통계 검토 02.하수관로 const SewagePipeRvw = () => { const { setMessage: alert } = useAlertStore((state) => state); @@ -79,9 +101,10 @@ const SewagePipeRvw = () => { const response = await fetchSewagePipeReviewTab(activeTab, searchParam); if (response?.status === "success" && response.data) { + const rows = extractReviewRowsFromResponseData(activeTab, response.data); setRowDataMap((prev) => { const next = new Map(prev); - next.set(activeTab, toGridRows(response.data as object[])); + next.set(activeTab, toGridRows(rows)); return next; }); } else { @@ -118,9 +141,10 @@ const SewagePipeRvw = () => { lazyTabLoggedRef.current.add(tabKey); if (response?.status === "success" && response.data) { + const rows = extractReviewRowsFromResponseData(tabKey, response.data); setRowDataMap((prev) => { const next = new Map(prev); - next.set(tabKey, toGridRows(response.data as object[])); + next.set(tabKey, toGridRows(rows)); return next; }); } else { diff --git a/apps/st/src/components/type/DataGrid.type.ts b/apps/st/src/components/type/DataGrid.type.ts index 1fef46df3..3519ce772 100644 --- a/apps/st/src/components/type/DataGrid.type.ts +++ b/apps/st/src/components/type/DataGrid.type.ts @@ -369,6 +369,10 @@ export type SplitDataGridProps = { leftOnRowClick?: (rowData: Record) => void; /** 좌측 분할 그리드 셀 스타일 콜백 */ leftCellStyleCallback?: CellStyleCallback; + /** 우측 분할 그리드 행 체크 변경 */ + rightOnItemChecked?: (itemIndex: number, checked: boolean) => void; + /** 우측 분할 그리드 헤더 전체 체크 변경 */ + rightOnItemAllChecked?: (checked: boolean) => void; /** 전체 정렬 기능 on/off (기본값: false) */ enableSort?: boolean; }; diff --git a/apps/st/src/components/util/column/input/PublicSewageFactInpColumn/MainFacilityInpColumn.ts b/apps/st/src/components/util/column/input/PublicSewageFactInpColumn/MainFacilityInpColumn.ts index cd85731e6..c8a5e969e 100644 --- a/apps/st/src/components/util/column/input/PublicSewageFactInpColumn/MainFacilityInpColumn.ts +++ b/apps/st/src/components/util/column/input/PublicSewageFactInpColumn/MainFacilityInpColumn.ts @@ -4,10 +4,10 @@ export const MainFacilityColumn = { columns: [ { name: 'swtrmtFcltNm', fieldName: 'swtrmtFcltNm', header: { text: '시설명' }, editable: false }, { name: 'fcltPstnCn', fieldName: 'fcltPstnCn', header: { text: '소재지' }, editable: false }, - { name: 'prxyBzentyNmCn', fieldName: 'prxyBzentyNmCn', header: { text: '위탁업체명' }, editable: false }, + { name: 'prxyBzentyNmCn', fieldName: 'prxyBzentyNmCn', header: { text: '위탁업체명' } }, { name: 'cnsgnCstVl', fieldName: 'cnsgnCstVl', header: { text: '위탁비용(백만원/년)' }, editable: false }, { name: 'prxyCtrtCn', fieldName: 'prxyCtrtCn', header: { text: '위탁계약기간' }, editable: false }, - { name: 'wholNope', fieldName: 'wholNope', header: { text: '직원수(명)' }, editable: false }, + { name: 'wholNope', fieldName: 'wholNope', header: { text: '직원수(명)' } }, { name: 'isMainYn', fieldName: 'isMainYn', header: { text: '중심처리시설여부' }, editable: false }, { name: 'admdstCd', fieldName: 'admdstCd', header: { text: '행정구역코드' }, visible: false, editable: false, styleName: 'rg-cell-readonly' }, diff --git a/apps/st/src/entities/statistics/api/input/statistics-input-sew-supply-rate-service.ts b/apps/st/src/entities/statistics/api/input/statistics-input-sew-supply-rate-service.ts index 6adccc917..78dd39e9c 100644 --- a/apps/st/src/entities/statistics/api/input/statistics-input-sew-supply-rate-service.ts +++ b/apps/st/src/entities/statistics/api/input/statistics-input-sew-supply-rate-service.ts @@ -28,7 +28,7 @@ export class StatisticsInputSewSupplyRateApiService implements StatisticsInputSe } public async saveSewerageSupplyRate(params: SewerageSupplyRateSaveRequest): Promise { - const response = await stClient.post( + const response = await stClient.put( `${ENDPOINT.ST_SERVICE.INPUT}/sewerage-supply-rate/save`, params, ); @@ -36,7 +36,7 @@ export class StatisticsInputSewSupplyRateApiService implements StatisticsInputSe } public async submitSewerageSupplyRate(params: SewerageSupplyRateSaveRequest): Promise { - const response = await stClient.post( + const response = await stClient.put( `${ENDPOINT.ST_SERVICE.INPUT}/sewerage-supply-rate/submit`, params, ); diff --git a/apps/st/src/entities/statistics/api/input/statistics-public-sewage-facility-input-service.ts b/apps/st/src/entities/statistics/api/input/statistics-public-sewage-facility-input-service.ts index d77b875c3..29b766b06 100644 --- a/apps/st/src/entities/statistics/api/input/statistics-public-sewage-facility-input-service.ts +++ b/apps/st/src/entities/statistics/api/input/statistics-public-sewage-facility-input-service.ts @@ -8,6 +8,8 @@ import type { PublicSewageFacilitySaveResponse, PublicSewageFacilitySubmitRequest, PublicSewageFacilitySubmitResponse, + PublicSewageMainFacilityMappingSaveRequest, + PublicSewageMainFacilityMappingSaveResponse, PublicSewageMainFacilityResponse, PublicSewageMappingTargetsRequestParams, PublicSewageMappingTargetsResponse, @@ -41,10 +43,20 @@ export class StatisticsInputPublicSewageFacilityApiService implements Statistics return response.data; } + public async saveMainFacilityMapping( + param: PublicSewageMainFacilityMappingSaveRequest, + ): Promise { + const response = await stClient.put( + `${ENDPOINT.ST_SERVICE.INPUT}/public-sewage-facility/main-facility/save`, + param, + ); + return response.data; + } + public async savePublicSewageFacility( param: PublicSewageFacilitySaveRequest, ): Promise { - const response = await stClient.post( + const response = await stClient.put( `${ENDPOINT.ST_SERVICE.INPUT}/public-sewage-facility/facility/save`, param, ); @@ -54,7 +66,7 @@ export class StatisticsInputPublicSewageFacilityApiService implements Statistics public async submitPublicSewageFacility( param: PublicSewageFacilitySubmitRequest, ): Promise { - const response = await stClient.post( + const response = await stClient.put( `${ENDPOINT.ST_SERVICE.INPUT}/public-sewage-facility/facility/submit`, param, ); diff --git a/apps/st/src/entities/statistics/api/input/statistics-sewage-pipe-input-service.ts b/apps/st/src/entities/statistics/api/input/statistics-sewage-pipe-input-service.ts index 956abbd6b..24b271824 100644 --- a/apps/st/src/entities/statistics/api/input/statistics-sewage-pipe-input-service.ts +++ b/apps/st/src/entities/statistics/api/input/statistics-sewage-pipe-input-service.ts @@ -101,7 +101,7 @@ export class StatisticsInputSewPipeApiService implements StatisticsInputSewPipeS } public async saveFactConnection(param: SeweragePipeFactConnectionSaveRequest): Promise { - const response = await stClient.post( + const response = await stClient.put( `${ENDPOINT.ST_SERVICE.INPUT}/sewage-pipe/facility-connection-status/popup/save`, param, ); @@ -109,7 +109,7 @@ export class StatisticsInputSewPipeApiService implements StatisticsInputSewPipeS } public async saveRegionalFactStatus(param: SeweragePipeInputRegionalFactStatusSaveRequest): Promise { - const response = await stClient.post( + const response = await stClient.put( `${ENDPOINT.ST_SERVICE.INPUT}/sewage-pipe/regional-facility-status/save`, param, ); @@ -117,7 +117,7 @@ export class StatisticsInputSewPipeApiService implements StatisticsInputSewPipeS } public async submitRegionalFactStatus(param: SeweragePipeInputRegionalFactStatusSubmitRequest): Promise { - const response = await stClient.post( + const response = await stClient.put( `${ENDPOINT.ST_SERVICE.INPUT}/sewage-pipe/regional-facility-status/submit`, param, ); @@ -125,7 +125,7 @@ export class StatisticsInputSewPipeApiService implements StatisticsInputSewPipeS } public async saveFactInstallStatus(param: SeweragePipeInputFactInstallStatusSaveRequest): Promise { - const response = await stClient.post( + const response = await stClient.put( `${ENDPOINT.ST_SERVICE.INPUT}/sewage-pipe/facility-install-status/save`, param, ); @@ -133,7 +133,7 @@ export class StatisticsInputSewPipeApiService implements StatisticsInputSewPipeS } public async submitFactInstallStatus(param: SeweragePipeInputFactInstallStatusSubmitRequest): Promise { - const response = await stClient.post( + const response = await stClient.put( `${ENDPOINT.ST_SERVICE.INPUT}/sewage-pipe/facility-install-status/submit`, param, ); @@ -141,7 +141,7 @@ export class StatisticsInputSewPipeApiService implements StatisticsInputSewPipeS } public async savePipeTypeStatus(param: SeweragePipeInputPipeTypeStatusSaveRequest): Promise { - const response = await stClient.post( + const response = await stClient.put( `${ENDPOINT.ST_SERVICE.INPUT}/sewage-pipe/pipe-type-status/save`, param, ); @@ -149,7 +149,7 @@ export class StatisticsInputSewPipeApiService implements StatisticsInputSewPipeS } public async submitPipeTypeStatus(param: SeweragePipeInputPipeTypeStatusSubmitRequest): Promise { - const response = await stClient.post( + const response = await stClient.put( `${ENDPOINT.ST_SERVICE.INPUT}/sewage-pipe/pipe-type-status/submit`, param, ); @@ -157,7 +157,7 @@ export class StatisticsInputSewPipeApiService implements StatisticsInputSewPipeS } public async savePipeDiameterStatus(param: SeweragePipeInputPipeDiameterStatusSaveRequest): Promise { - const response = await stClient.post( + const response = await stClient.put( `${ENDPOINT.ST_SERVICE.INPUT}/sewage-pipe/pipe-diameter-status/save`, param, ); @@ -165,7 +165,7 @@ export class StatisticsInputSewPipeApiService implements StatisticsInputSewPipeS } public async submitPipeDiameterStatus(param: SeweragePipeInputPipeDiameterStatusSubmitRequest): Promise { - const response = await stClient.post( + const response = await stClient.put( `${ENDPOINT.ST_SERVICE.INPUT}/sewage-pipe/pipe-diameter-status/submit`, param, ); @@ -173,7 +173,7 @@ export class StatisticsInputSewPipeApiService implements StatisticsInputSewPipeS } public async saveManholeStatus(param: SeweragePipeInputManholeStatusSaveRequest): Promise { - const response = await stClient.post( + const response = await stClient.put( `${ENDPOINT.ST_SERVICE.INPUT}/sewage-pipe/manhole-status/save`, param, ); @@ -181,7 +181,7 @@ export class StatisticsInputSewPipeApiService implements StatisticsInputSewPipeS } public async submitManholeStatus(param: SeweragePipeInputManholeStatusSubmitRequest): Promise { - const response = await stClient.post( + const response = await stClient.put( `${ENDPOINT.ST_SERVICE.INPUT}/sewage-pipe/manhole-status/submit`, param, ); diff --git a/apps/st/src/entities/statistics/model/input/statistics-public-sewage-facility-input-interface.ts b/apps/st/src/entities/statistics/model/input/statistics-public-sewage-facility-input-interface.ts index 19c86e20c..edf3e74e3 100644 --- a/apps/st/src/entities/statistics/model/input/statistics-public-sewage-facility-input-interface.ts +++ b/apps/st/src/entities/statistics/model/input/statistics-public-sewage-facility-input-interface.ts @@ -167,6 +167,37 @@ export interface PublicSewageMappingTargetsResponse { data: PublicSewageMappingTargetItem[]; } +/** ST00060002 중심처리시설 매핑 저장 — saveList 항목 */ +export interface PublicSewageMainFacilityMappingSaveItem { + fcltSn: string; + admdstCd: string; + admdstNm: string; + swtrmtFcltCd: string; + swtrmtFcltNm: string; + isMainYn: string; +} + +/** ST00060002 중심처리시설 매핑 저장 API 요청 */ +export interface PublicSewageMainFacilityMappingSaveRequest { + statsYr: string; + sewerBzmnCd: string; + mainFcltSn: string; + mainFcltNm: string; + prxyBzentyNmCn: string; + cnsgnCstVl: number; + wholNope: number; + prxyCtrtCn: string; + userId: string; + saveList: PublicSewageMainFacilityMappingSaveItem[]; + deleteList: string[]; +} + +export interface PublicSewageMainFacilityMappingSaveResponse { + status: string; + message?: string; + data?: string; +} + /** ST00060001 공공하수처리시설 저장 — saveList 항목 */ export interface PublicSewageFacilitySaveItem { admdstCd: string; @@ -249,6 +280,7 @@ export interface StatisticsInputPublicSewageFacilityService { getPublicSewageFacility(param: PublicSewageFacilityRequestParams): Promise; getPublicSewageMainFacility(param: PublicSewageFacilityRequestParams): Promise; getMappingTargets(param: PublicSewageMappingTargetsRequestParams): Promise; + saveMainFacilityMapping(param: PublicSewageMainFacilityMappingSaveRequest): Promise; savePublicSewageFacility(param: PublicSewageFacilitySaveRequest): Promise; submitPublicSewageFacility(param: PublicSewageFacilitySubmitRequest): Promise; } diff --git a/apps/st/src/entities/statistics/model/statistics-sewage-pipe-mtc-review-interface.ts b/apps/st/src/entities/statistics/model/statistics-sewage-pipe-mtc-review-interface.ts index 330403c73..29202d9c8 100644 --- a/apps/st/src/entities/statistics/model/statistics-sewage-pipe-mtc-review-interface.ts +++ b/apps/st/src/entities/statistics/model/statistics-sewage-pipe-mtc-review-interface.ts @@ -32,7 +32,8 @@ export interface SewerPipeMtcItem { } export interface SewerPipeMtcResponseData { - mtcStatusList: SewerPipeMtcItem[]; + sewerPipeMtcList: SewerPipeMtcItem[]; + statsStatusInfo: StatsStatusInfo; } export interface SewerPipeMtcRequestParams { @@ -96,7 +97,8 @@ export interface SewerPipeDrdgItem { export interface SewerPipeDrdgResponseData { - drdgStatusList: SewerPipeDrdgItem[]; + sewerPipeDrdgList: SewerPipeDrdgItem[]; + statsStatusInfo: StatsStatusInfo; } @@ -104,4 +106,13 @@ export type SewerPipeDrdgApiResponse = ApiResponse; export interface SewagePipeMtcService { getSewerPipeMtcDrdgItem(params: SewerPipeMtcRequestParams): Promise; +} + +export interface StatsStatusInfo { + statsCd?: string; + submitStatusCd?: string; + prvnStatusCd?: string; + rjctOpnnCn?: string; + splmntOpnnCn?: string; + wrterOpnnCn?: string; } \ No newline at end of file diff --git a/apps/st/src/entities/statistics/model/statistics-sewage-pipe-review-interface.ts b/apps/st/src/entities/statistics/model/statistics-sewage-pipe-review-interface.ts index b059aa90c..6e81a41bc 100644 --- a/apps/st/src/entities/statistics/model/statistics-sewage-pipe-review-interface.ts +++ b/apps/st/src/entities/statistics/model/statistics-sewage-pipe-review-interface.ts @@ -63,7 +63,8 @@ export interface SewagePipeReviewrRegionalItem { } export interface SewagePipeReviewResponseData { - sewagePipeList: SewagePipeReviewrRegionalItem[]; + regionalStatusList: SewagePipeReviewrRegionalItem[]; + statsStatusInfo: StatsStatusInfo; } export interface SewagePipeReviewRequestParams { @@ -73,7 +74,7 @@ export interface SewagePipeReviewRequestParams { sewerBzmnCd?: string; } -export type SewagePipeReviewRegionalApiResponse = ApiResponse & { +export type SewagePipeReviewRegionalApiResponse = ApiResponse & { message: string; }; @@ -131,7 +132,12 @@ export interface SewagePipeReviewFacilityItem { } -export type SewagePipeFacilityApiResponse = ApiResponse & { +export interface SewagePipeFacilityResponseData { + facilityStatusList: SewagePipeReviewFacilityItem[]; + statsStatusInfo: StatsStatusInfo; +} + +export type SewagePipeFacilityApiResponse = ApiResponse & { message: string; }; @@ -165,10 +171,11 @@ export interface SewerPipeTypeItem { export interface SewerPipeTypeResponseData { - pipeTypeStatusList: SewerPipeTypeItem[]; + pipeTypeList: SewerPipeTypeItem[]; + statsStatusInfo: StatsStatusInfo; } -export type SewerPipeTypeApiResponse = ApiResponse; +export type SewerPipeTypeApiResponse = ApiResponse; export interface SewagePipeTypeService { @@ -225,9 +232,23 @@ export interface SewerPipeDiamItem { } -export type SewerPipeDiamApiResponse = ApiResponse; +export interface SewerPipeDiamResponseData { + pipeDiamList: SewerPipeDiamItem[]; + statsStatusInfo: StatsStatusInfo; +} + +export type SewerPipeDiamApiResponse = ApiResponse; export interface SewagePipeDiamService { getSewagePipeDiamList(params: SewagePipeReviewRequestParams): Promise; +} + +export interface StatsStatusInfo { + statsCd?: string; + submitStatusCd?: string; + prvnStatusCd?: string; + rjctOpnnCn?: string; + splmntOpnnCn?: string; + wrterOpnnCn?: string; } \ No newline at end of file From 291b4a3a0a860173ba0f9499896d1257b7b04363 Mon Sep 17 00:00:00 2001 From: yangsein Date: Mon, 11 May 2026 13:02:00 +0900 Subject: [PATCH 4/7] =?UTF-8?q?feat:[=EC=A7=91=EA=B3=84=ED=91=9C]=2006?= =?UTF-8?q?=EA=B3=B5=EA=B3=B5=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/PublicSewageFacilityTot.tsx | 72 ++++- .../total/PublicSewageFacilityColumn.ts | 298 ++++++++++++++++++ .../api/statistics-total-service.ts | 26 ++ ...tatistics-sewage-public-total-interface.ts | 103 ++++++ 4 files changed, 498 insertions(+), 1 deletion(-) create mode 100644 apps/st/src/components/util/column/total/PublicSewageFacilityColumn.ts create mode 100644 apps/st/src/entities/statistics/model/statistics-total/statistics-sewage-public-total-interface.ts diff --git a/apps/st/src/components/statistics/total/PublicSewageFacilityTot.tsx b/apps/st/src/components/statistics/total/PublicSewageFacilityTot.tsx index 6eb3f2991..280db0894 100644 --- a/apps/st/src/components/statistics/total/PublicSewageFacilityTot.tsx +++ b/apps/st/src/components/statistics/total/PublicSewageFacilityTot.tsx @@ -1,9 +1,79 @@ +import { useState } from "react"; + +import type { SearchFormValues } from "@/components/type/SearchForm.type"; +import { PublicSewageFacilityColumn } from "@/components/util/column/total/PublicSewageFacilityColumn"; +import { statisticsTotalStatusApi } from "@/entities/statistics/api/statistics-total-service"; +import { toGridRows } from "@/components/util/grid/gridFunction"; +import { useAlertStore } from "@/shared/lib"; + import SearchForm from "@/components/module/SearchForm"; +import DataGrid from "@/components/module/DataGrid"; +import CellColorInfo from "./CellColorInfo"; +import { LoadingWithAnimation } from "@common/components"; const PublicSewageFacilityTot = () => { + const [rowData, setRowData] = useState[]>([]); + const [isLoading, setIsLoading] = useState(false); + const { setMessage: alert } = useAlertStore((state) => state); + + const handleSearch = async (values: SearchFormValues) => { + try { + setIsLoading(true); + + const isSummaryEnabled = values.isSummary !== 'ALL'; + const summaryYearCnt = isSummaryEnabled ? Number(values.isSummary) : undefined; + + const param = { + ctpvNm: values.sido === 'ALL' ? '' : values.sido, + sggNm: values.gugun === 'ALL' ? '' : values.gugun, + sewerBzmnCd: values.bsnmCd, + statsYr: values.year, + rowDataYn: values.isDetail, + summaryYn: isSummaryEnabled, + ...(isSummaryEnabled ? { summaryYearCnt } : {}), + }; + + const response = await statisticsTotalStatusApi.getSewagePublicFacillityTotal(param); + + if (response.status === 'success') { + setRowData(toGridRows(response.data)); + } else { + alert(response.message || '데이터 조회 중 오류가 발생했습니다.'); + } + } catch (error) { + console.log(error); + alert('데이터 조회 중 오류가 발생했습니다.'); + } finally { + setIsLoading(false); + } + }; + 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/PublicSewageFacilityColumn.ts b/apps/st/src/components/util/column/total/PublicSewageFacilityColumn.ts new file mode 100644 index 000000000..f4c0ddae2 --- /dev/null +++ b/apps/st/src/components/util/column/total/PublicSewageFacilityColumn.ts @@ -0,0 +1,298 @@ +import { ValueType } from "realgrid"; +/** + * 하수도 통계 관리 > 06. 공공하수처리시설 + * 단일 Tab + */ +export const PublicSewageFacilityColumn = { + 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 }, + + // 시설명 (500㎥/일 이상/미만) + { name: 'swtrmtFcltNm', fieldName: 'swtrmtFcltNm', header: { text: '시설명\n(500㎥/일\n이상/미만)' }, editable: false }, + + // 소재지 + { name: 'fcltPstnCn', fieldName: 'fcltPstnCn', header: { text: '소재지' }, editable: false }, + + // 시설용량 (㎥/일) + { + name: 'prcsFcltCpct', + fieldName: 'prcsFcltCpct', + header: { text: ' 시설용량 500㎥/일\n이상/미만' }, + editable: false, + valueType: ValueType.NUMBER, + dataFormat: '#,##0', + }, + { + name: 'prcsFcltPhysCpct', + fieldName: 'prcsFcltPhysCpct', + header: { text: '물리적' }, + editable: false, + valueType: ValueType.NUMBER, + dataFormat: '#,##0.00', + }, + { + name: 'prcsFcltBoilCpct', + fieldName: 'prcsFcltBoilCpct', + header: { text: '생물학적' }, + editable: false, + valueType: ValueType.NUMBER, + dataFormat: '#,##0.00', + }, + { + name: 'prcsFcltAltdCpct', + fieldName: 'prcsFcltAltdCpct', + header: { text: '고도' }, + editable: false, + valueType: ValueType.NUMBER, + dataFormat: '#,##0.00', + }, + + // 유입하수량 (㎥/일) + { + name: 'inflwSwageQnt', + fieldName: 'inflwSwageQnt', + header: { text: '유입하수량 500㎥/일\n이상/미만' }, + editable: false, + valueType: ValueType.NUMBER, + dataFormat: '#,##0.00', + }, + { + name: 'inflwSwagePhysQnt', + fieldName: 'inflwSwagePhysQnt', + header: { text: '물리적' }, + editable: false, + valueType: ValueType.NUMBER, + dataFormat: '#,##0.00', + }, + { + name: 'inflwSwageBoilQnt', + fieldName: 'inflwSwageBoilQnt', + header: { text: '생물학적' }, + editable: false, + valueType: ValueType.NUMBER, + dataFormat: '#,##0.00', + }, + { + name: 'inflwSwageAltdQnt', + fieldName: 'inflwSwageAltdQnt', + header: { text: '고도' }, + editable: false, + valueType: ValueType.NUMBER, + dataFormat: '#,##0.00', + }, + + // 방류량 (㎥/일) + { + name: 'prcsQnt', + fieldName: 'prcsQnt', + header: { text: '방류량 500㎥/일\n이상/미만' }, + editable: false, + valueType: ValueType.NUMBER, + dataFormat: '#,##0.00', + }, + { + name: 'prcsPhysCpct', + fieldName: 'prcsPhysCpct', + header: { text: '물리적' }, + editable: false, + valueType: ValueType.NUMBER, + dataFormat: '#,##0.00', + }, + { + name: 'prcsBoilCpct', + fieldName: 'prcsBoilCpct', + header: { text: '생물학적' }, + editable: false, + valueType: ValueType.NUMBER, + dataFormat: '#,##0.00', + }, + { + name: 'prcsAltdCpct', + fieldName: 'prcsAltdCpct', + header: { text: '고도' }, + editable: false, + valueType: ValueType.NUMBER, + dataFormat: '#,##0.00', + }, + + // 수질 정보 - 유입/방류 교차 + { name: 'inflwBodVl', fieldName: 'inflwBodVl', header: { text: '유입\nBOD' }, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' }, + { name: 'dschgBodVl', fieldName: 'dschgBodVl', header: { text: '방류\nBOD' }, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' }, + { name: 'inflwTocQnt', fieldName: 'inflwTocQnt', header: { text: '유입\nTOC' }, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' }, + { name: 'dschgTocQnt', fieldName: 'dschgTocQnt', header: { text: '방류\nTOC' }, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' }, + { name: 'inflwSsQnt', fieldName: 'inflwSsQnt', header: { text: '유입\nSS' }, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' }, + { name: 'dschgSsQnt', fieldName: 'dschgSsQnt', header: { text: '방류\nSS' }, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' }, + { name: 'inflwTnVl', fieldName: 'inflwTnVl', header: { text: '유입\nT-N' }, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' }, + { name: 'dschgTnVl', fieldName: 'dschgTnVl', header: { text: '방류\nT-N' }, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' }, + { name: 'inflwTpVl', fieldName: 'inflwTpVl', header: { text: '유입\nT-P' }, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' }, + { name: 'dschgTpVl', fieldName: 'dschgTpVl', header: { text: '방류\nT-P' }, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' }, + { name: 'inflwGrssClfgVl', fieldName: 'inflwGrssClfgVl', header: { text: '유입\n총대장균군수' }, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' }, + { name: 'dschgGrssClfgVl', fieldName: 'dschgGrssClfgVl', header: { text: '방류\n총대장균군수' }, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' }, + + // 처리 효율 및 방법 + { + name: 'prcsRt', + fieldName: 'prcsRt', + header: { text: '처리\n효율(%)' }, + editable: false, + valueType: ValueType.NUMBER, + dataFormat: '#,##0.0', + }, + { + name: 'prcsLodQnt', + fieldName: 'prcsLodQnt', + header: { text: '처리부하량\n(kg BOD/D)' }, + editable: false, + valueType: ValueType.NUMBER, + dataFormat: '#,##0', + }, + { name: 'prcsMthdExpln', fieldName: 'prcsMthdExpln', header: { text: '처리\n방법' }, editable: false }, + + // 연계처리량 (㎥/일) + { name: 'wholHmnQnt', fieldName: 'wholHmnQnt', header: { text: '분뇨' }, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'wholLvstQnt', fieldName: 'wholLvstQnt', header: { text: '축산' }, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'wholEfntQnt', fieldName: 'wholEfntQnt', header: { text: '침출수' }, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'wholEtcQnt', fieldName: 'wholEtcQnt', header: { text: '기타' }, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 부가 정보 + { 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: 'wholBizVl', fieldName: 'wholBizVl', header: { text: '사업비\n(백만원)' }, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'eflntDsnfMthdExpln', fieldName: 'eflntDsnfMthdExpln', header: { text: '방류수\n소독방법' }, editable: false }, + + // 방류수역 정보 + { name: 'wtsysNm', fieldName: 'wtsysNm', header: { text: '수계' }, editable: false }, + { name: 'trbtrNm', fieldName: 'trbtrNm', header: { text: '지류' }, editable: false }, + { name: 'dtlUnitZoneCn', fieldName: 'dtlUnitZoneCn', header: { text: '세부단위구역' }, editable: false }, + { name: 'rgnSeNm', fieldName: 'rgnSeNm', header: { text: '지역구분' }, editable: false }, + + // 중권역 + { name: 'msirbCn', fieldName: 'msirbCn', header: { text: '명칭' }, editable: false }, + { name: 'goalWqNo', fieldName: 'goalWqNo', header: { text: '목표수질\n(BOD)' }, editable: false }, + + // 에너지 + { name: 'engySrlneRt', fieldName: 'engySrlneRt', header: { text: '에너지\n자립율(%)' }, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'engyItslfPrdctnQnt', fieldName: 'engyItslfPrdctnQnt', header: { text: '에너지\n자체생산량\n(TOE)' }, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'anlEngyWholUseQnt', fieldName: 'anlEngyWholUseQnt', header: { text: '계(TOE)' }, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'anlElpwrUseQnt', fieldName: 'anlElpwrUseQnt', header: { text: '총전력사용량\n(kWh)' }, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'itslfEngyUseQnt', fieldName: 'itslfEngyUseQnt', header: { text: '자체에너지\n사용량(TOE)' }, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'swtrmtEmsnQnt', fieldName: 'swtrmtEmsnQnt', header: { text: '하수처리량당\nCO2배출량\n(㎏CO2/㎥)' }, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' }, + + // 운영주체 + { name: 'operMnbdCn', fieldName: 'operMnbdCn', header: { text: '운영주체\n(자체/공기업/\n민간대행)' }, editable: false }, + + // 위탁현황 + { name: 'prxyBzentyNmCn', fieldName: 'prxyBzentyNmCn', header: { text: '위탁\n업체명' }, editable: false }, + { name: 'cnsgnCstVl', fieldName: 'cnsgnCstVl', header: { text: '위탁비용\n(백만원/년)' }, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'prxyCtrtCn', fieldName: 'prxyCtrtCn', header: { text: '위탁계약기간\n(yyyy-mm-dd\n~\nyyyy-mm-dd)' }, editable: false }, + { name: 'intgPrcsFcltCn', fieldName: 'intgPrcsFcltCn', header: { text: '중심처리시설' }, editable: false }, + { name: 'wholNope', fieldName: 'wholNope', header: { text: '직원총수\n(명)' }, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'sftyAcdntEvntcsCnt', fieldName: 'sftyAcdntEvntcsCnt', header: { text: '안전사고건수' }, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // hidden + { name: 'admdstCd', fieldName: 'admdstCd', header: { text: '행정구역코드' }, visible: false }, + { name: 'rowType', fieldName: 'rowType', header: { text: '데이터구분' }, visible: false }, + { name: 'statsYr', fieldName: 'statsYr', header: { text: '통계연도' }, visible: false }, + ].map((col) => ({ ...col, editable: false })), + + columnLayout: [ + { + name: 'groupA', + hideChildHeaders: false, + header: { text: '행정구역' }, + items: ['ctpvNm', 'sggNm', 'admdstNm'], + }, + 'swtrmtFcltNm', + 'fcltPstnCn', + 'prcsFcltCpct', + { + name: 'groupCapacity', + hideChildHeaders: false, + header: { text: '(㎥/일)' }, + items: [ 'prcsFcltPhysCpct', 'prcsFcltBoilCpct', 'prcsFcltAltdCpct'], + }, + 'inflwSwageQnt', + { + name: 'groupInflw', + hideChildHeaders: false, + header: { text: '(㎥/일)' }, + items: [ 'inflwSwagePhysQnt', 'inflwSwageBoilQnt', 'inflwSwageAltdQnt'], + }, + 'prcsQnt', + { + name: 'groupPrcs', + hideChildHeaders: false, + header: { text: '(㎥/일)' }, + items: [ 'prcsPhysCpct', 'prcsBoilCpct', 'prcsAltdCpct'], + }, + 'inflwBodVl', + 'dschgBodVl', + 'inflwTocQnt', + 'dschgTocQnt', + 'inflwSsQnt', + 'dschgSsQnt', + 'inflwTnVl', + 'dschgTnVl', + 'inflwTpVl', + 'dschgTpVl', + 'inflwGrssClfgVl', + 'dschgGrssClfgVl', + 'prcsRt', + 'prcsLodQnt', + 'prcsMthdExpln', + { + name: 'groupLinkPrcs', + hideChildHeaders: false, + header: { text: '연계처리량(㎥/일)\n(500㎥/일 이상/미만)' }, + items: ['wholHmnQnt', 'wholLvstQnt', 'wholEfntQnt', 'wholEtcQnt'], + }, + 'telNo', + 'cmptnStrtYmd', + 'oprtngStrtYmd', + 'wholBizVl', + 'eflntDsnfMthdExpln', + { + name: 'groupDschgWater', + hideChildHeaders: false, + header: { text: '방류수역' }, + items: [ + 'wtsysNm', + 'trbtrNm', + 'dtlUnitZoneCn', + 'rgnSeNm', + { + name: 'groupMsirbCn', + hideChildHeaders: false, + header: { text: '중권역' }, + items: ['msirbCn', 'goalWqNo'], + }, + ], + }, + 'engySrlneRt', + 'engyItslfPrdctnQnt', + { + name: 'groupEngyUse', + hideChildHeaders: false, + header: { text: '연간 총 에너지 사용량(TOE)' }, + items: ['anlEngyWholUseQnt', 'anlElpwrUseQnt', 'itslfEngyUseQnt'], + }, + 'swtrmtEmsnQnt', + 'operMnbdCn', + { + name: 'groupCnsgn', + hideChildHeaders: false, + header: { text: '위탁현황' }, + items: ['prxyBzentyNmCn', 'cnsgnCstVl', 'prxyCtrtCn'], + }, + 'intgPrcsFcltCn', + 'wholNope', + 'sftyAcdntEvntcsCnt', + 'admdstCd', + 'rowType', + 'statsYr', + ], +}; \ 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 402fd016c..e5efbb39b 100644 --- a/apps/st/src/entities/statistics/api/statistics-total-service.ts +++ b/apps/st/src/entities/statistics/api/statistics-total-service.ts @@ -11,6 +11,7 @@ import type { SewagePipeFactInstallStatusResponse, SewagePipePipeDiamStatusResponse, SewagePipePipeTypeFacilityStatusResponse, + FcltyManholeSummaryResponse, SewagePipeTotalRequest, SewagePipeTotalResponse, } from "../model/statistics-total/statistics-sewage-pipe-total-interface"; @@ -19,6 +20,10 @@ import type { SewerageServiceLevelRequest, SewerageServiceLevelResidentFriendlyFactResponse } from "../model/statistics-total/statistics-service-level-total-interface"; +import type { + PublicSewageInquirySummaryResponse +} from "../model/statistics-total/statistics-sewage-public-total-interface"; + export class StatisticsTotalApiService { public async getSewerageSupplyRateList(params: SewerageSupplyRateRequest): Promise { @@ -84,6 +89,16 @@ export class StatisticsTotalApiService { ); return response.data; } + /** ST00020005 — 맨홀 시설현황(합계) */ + public async getSewagePipePipeMnhleStatus( + params: SewagePipeTotalRequest, + ): Promise { + const response = await stClient.get( + `${ENDPOINT.ST_SERVICE.TOTAL}/sewer-pipe/manhle-status`, + { params }, + ); + return response.data; + } /** ST00030001 — 하수관로 유지관리 > 개보수(합계) */ public async getSewagePipeMaintainRepairTotal( @@ -106,6 +121,17 @@ export class StatisticsTotalApiService { ); return response.data; } + + /** ST00060001 —공공하수처리시설 */ + public async getSewagePublicFacillityTotal( + params: SewagePipeMaintainTotalRequest, + ): Promise { + const response = await stClient.get( + `${ENDPOINT.ST_SERVICE.TOTAL}/public-sewage`, + { 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-sewage-public-total-interface.ts b/apps/st/src/entities/statistics/model/statistics-total/statistics-sewage-public-total-interface.ts new file mode 100644 index 000000000..34edcebf9 --- /dev/null +++ b/apps/st/src/entities/statistics/model/statistics-total/statistics-sewage-public-total-interface.ts @@ -0,0 +1,103 @@ +export interface PublicSewageInquirySummaryRequest { + ctpvNm?: string; + sggNm?: string; + statsYr?: string; + swtrmtFcltNm?: string; + sewerBzmnCd?: string; + rowDataYn?: boolean; + capacityGbn?: string; + summaryYn?: boolean; + summaryYearCnt?: number; +} + +export interface PublicSewageInquirySummaryItem { + // 기본 정보 및 행정구역 + ctpvNm: string; + sggNm: string; + statsYr: string; + admdstCd: string; + admdstNm: string; + swtrmtFcltNm: string; + rowType: string; + fcltPstnCn: string; + + // 시설 용량 (㎥/일) + prcsFcltCpct: number; + prcsFcltPhysCpct: number; + prcsFcltBoilCpct: number; + prcsFcltAltdCpct: number; + + // 유입 및 처리량 (㎥/일) + inflwSwageQnt: number; + inflwSwagePhysQnt: number; + inflwSwageBoilQnt: number; + inflwSwageAltdQnt: number; + + prcsQnt: number; + prcsPhysCpct: number; + prcsBoilCpct: number; + prcsAltdCpct: number; + + // 수질 정보 (유입/방류) + inflwBodVl: number; + dschgBodVl: number; + inflwTocQnt: number; + dschgTocQnt: number; + inflwSsQnt: number; + dschgSsQnt: number; + inflwTnVl: number; + dschgTnVl: number; + inflwTpVl: number; + dschgTpVl: number; + inflwGrssClfgVl: number; + dschgGrssClfgVl: number; + + // 처리 효율 및 방법 + prcsRt: number; + prcsLodQnt: number; + prcsMthdExpln: string; + + // 연계 처리량 (㎥/일) + wholHmnQnt: number; + wholLvstQnt: number; + wholEfntQnt: number; + wholEtcQnt: number; + + // 부가 정보 + telNo: string; + cmptnStrtYmd: string; + oprtngStrtYmd: string; + wholBizVl: number; + eflntDsnfMthdExpln: string; + + // 방류수역 정보 + wtsysNm: string; + trbtrNm: string; + dtlUnitZoneCn: string; + rgnSeNm: string; + msirbCn: string; + goalWqNo: string; + + // 에너지 및 위탁현황 + engySrlneRt: number; + engyItslfPrdctnQnt: number; + anlEngyWholUseQnt: number; + anlElpwrUseQnt: number; + itslfEngyUseQnt: number; + swtrmtEmsnQnt: number; + operMnbdCn: string; + + // 위탁현황 + prxyBzentyNmCn: string; + cnsgnCstVl: number; + prxyCtrtCn: string; + intgPrcsFcltCn: string; + wholNope: number; + sftyAcdntEvntcsCnt: number; +} + +export interface PublicSewageInquirySummaryResponse { + status: string; + message: string; + data: PublicSewageInquirySummaryItem[]; +} \ No newline at end of file From 679ea8a6b41de8a45818ac7d0cb76fa3e7ffc159 Mon Sep 17 00:00:00 2001 From: yangsein Date: Mon, 11 May 2026 13:03:01 +0900 Subject: [PATCH 5/7] =?UTF-8?q?feat:[=EC=A7=91=EA=B3=84=ED=91=9C]=2002?= =?UTF-8?q?=ED=95=98=EC=88=98=EA=B4=80=EB=A1=9C=20=EB=A7=A8=ED=99=80=20?= =?UTF-8?q?=ED=98=84=ED=99=A9=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../statistics/total/SewagePipeTot.tsx | 16 +- .../util/column/total/SewagePipeTotColumn.ts | 231 +++++++++++++++++- .../statistics-sewage-pipe-total-interface.ts | 129 ++++++++++ 3 files changed, 373 insertions(+), 3 deletions(-) diff --git a/apps/st/src/components/statistics/total/SewagePipeTot.tsx b/apps/st/src/components/statistics/total/SewagePipeTot.tsx index 2553fce19..061405229 100644 --- a/apps/st/src/components/statistics/total/SewagePipeTot.tsx +++ b/apps/st/src/components/statistics/total/SewagePipeTot.tsx @@ -25,6 +25,7 @@ function buildSewagePipeTotSearchParam(values: SearchFormValues): SewagePipeTota sewerBzmnCd: values.bsnmCd, statsYr: values.year, rowDataYn: values.isDetail, + summaryYn: isSummaryEnabled, ...(isSummaryEnabled ? { summaryYearCnt } : {}), }; } @@ -106,6 +107,15 @@ const SewagePipeTot = () => { alert(response.message || "데이터 조회 중 오류가 발생했습니다."); return false; } + if (tabKey === "ST00020005") { + const response = await statisticsTotalStatusApi.getSewagePipePipeMnhleStatus(param); + if (response.status === "success" && response.data) { + applyRows(toGridRows(response.data as object[])); + return true; + } + alert(response.message || "데이터 조회 중 오류가 발생했습니다."); + return false; + } return false; }; @@ -120,7 +130,8 @@ const SewagePipeTot = () => { activeTab === "ST00020001" || activeTab === "ST00020002" || activeTab === "ST00020003" || - activeTab === "ST00020004" + activeTab === "ST00020004" || + activeTab === "ST00020005" ) { ok = await loadSewagePipeTotTabData(activeTab, values, true); } @@ -151,7 +162,8 @@ const SewagePipeTot = () => { tabKey === "ST00020001" || tabKey === "ST00020002" || tabKey === "ST00020003" || - tabKey === "ST00020004" + tabKey === "ST00020004" || + tabKey === "ST00020005" ) { try { setIsLoading(true); diff --git a/apps/st/src/components/util/column/total/SewagePipeTotColumn.ts b/apps/st/src/components/util/column/total/SewagePipeTotColumn.ts index 9c78c5b0e..e2a30d9e0 100644 --- a/apps/st/src/components/util/column/total/SewagePipeTotColumn.ts +++ b/apps/st/src/components/util/column/total/SewagePipeTotColumn.ts @@ -706,7 +706,7 @@ const rawSewagePipeTotList = [ tabKey: "ST00020004", tabLabel: "관경별 현황", rowStartNum: 2, - fixedColumns: 3, + fixedColumns: 1, columns: [ { name: "ctpvNm", fieldName: "ctpvNm", header: { text: "시도" }, editable: false }, { name: "sggNm", fieldName: "sggNm", header: { text: "시군구" }, editable: false }, @@ -794,9 +794,238 @@ const rawSewagePipeTotList = [ "etcPdmtPrlg", ], }, + + { + tabKey: "ST00020005", + 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: "emphsMngSeNm", fieldName: "emphsMngSeNm", header: { text: "집중강우\n중점관리\n구역구분" }, editable: false }, + + { name: "totWholMnhlCnt", fieldName: "totWholMnhlCnt", header: { text: "계" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "totStep1MnhlCnt", fieldName: "totStep1MnhlCnt", header: { text: "주철" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "totStep2MnhlCnt", fieldName: "totStep2MnhlCnt", header: { text: "주철(조화)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "totStep3MnhlCnt", fieldName: "totStep3MnhlCnt", header: { text: "콘크리트" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "totStep4MnhlCnt", fieldName: "totStep4MnhlCnt", header: { text: "기타" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "totStep5MnhlCnt", fieldName: "totStep5MnhlCnt", header: { text: "추락방지시설\n설치맨홀(개)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + + // ── 2015년 이전 (yy14) ────────────────────────────────── + { name: "yy14WholMnhlCnt", fieldName: "yy14WholMnhlCnt", header: { text: "계" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "yy14Step1MnhlCnt", fieldName: "yy14Step1MnhlCnt", header: { text: "주철" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "yy14Step2MnhlCnt", fieldName: "yy14Step2MnhlCnt", header: { text: "주철(조화)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "yy14Step3MnhlCnt", fieldName: "yy14Step3MnhlCnt", header: { text: "콘크리트" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "yy14Step4MnhlCnt", fieldName: "yy14Step4MnhlCnt", header: { text: "기타" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "yy14Step5MnhlCnt", fieldName: "yy14Step5MnhlCnt", header: { text: "추락방지시설\n설치맨홀(개)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + + // ── 2015년 (yy15) ─────────────────────────────────────── + { name: "yy15WholMnhlCnt", fieldName: "yy15WholMnhlCnt", header: { text: "계" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "yy15Step1MnhlCnt", fieldName: "yy15Step1MnhlCnt", header: { text: "주철" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "yy15Step2MnhlCnt", fieldName: "yy15Step2MnhlCnt", header: { text: "주철(조화)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "yy15Step3MnhlCnt", fieldName: "yy15Step3MnhlCnt", header: { text: "콘크리트" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "yy15Step4MnhlCnt", fieldName: "yy15Step4MnhlCnt", header: { text: "기타" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "yy15Step5MnhlCnt", fieldName: "yy15Step5MnhlCnt", header: { text: "추락방지시설\n설치맨홀(개)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + + // ── 2016년 (yy16) ─────────────────────────────────────── + { name: "yy16WholMnhlCnt", fieldName: "yy16WholMnhlCnt", header: { text: "계" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "yy16Step1MnhlCnt", fieldName: "yy16Step1MnhlCnt", header: { text: "주철" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "yy16Step2MnhlCnt", fieldName: "yy16Step2MnhlCnt", header: { text: "주철(조화)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "yy16Step3MnhlCnt", fieldName: "yy16Step3MnhlCnt", header: { text: "콘크리트" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "yy16Step4MnhlCnt", fieldName: "yy16Step4MnhlCnt", header: { text: "기타" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "yy16Step5MnhlCnt", fieldName: "yy16Step5MnhlCnt", header: { text: "추락방지시설\n설치맨홀(개)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + + // ── 2017년 (yy17) ─────────────────────────────────────── + { name: "yy17WholMnhlCnt", fieldName: "yy17WholMnhlCnt", header: { text: "계" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "yy17Step1MnhlCnt", fieldName: "yy17Step1MnhlCnt", header: { text: "주철" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "yy17Step2MnhlCnt", fieldName: "yy17Step2MnhlCnt", header: { text: "주철(조화)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "yy17Step3MnhlCnt", fieldName: "yy17Step3MnhlCnt", header: { text: "콘크리트" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "yy17Step4MnhlCnt", fieldName: "yy17Step4MnhlCnt", header: { text: "기타" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "yy17Step5MnhlCnt", fieldName: "yy17Step5MnhlCnt", header: { text: "추락방지시설\n설치맨홀(개)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + + // ── 2018년 (yy18) ─────────────────────────────────────── + { name: "yy18WholMnhlCnt", fieldName: "yy18WholMnhlCnt", header: { text: "계" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "yy18Step1MnhlCnt", fieldName: "yy18Step1MnhlCnt", header: { text: "주철" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "yy18Step2MnhlCnt", fieldName: "yy18Step2MnhlCnt", header: { text: "주철(조화)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "yy18Step3MnhlCnt", fieldName: "yy18Step3MnhlCnt", header: { text: "콘크리트" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "yy18Step4MnhlCnt", fieldName: "yy18Step4MnhlCnt", header: { text: "기타" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "yy18Step5MnhlCnt", fieldName: "yy18Step5MnhlCnt", header: { text: "추락방지시설\n설치맨홀(개)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + + // ── 2019년 (yy19) ─────────────────────────────────────── + { name: "yy19WholMnhlCnt", fieldName: "yy19WholMnhlCnt", header: { text: "계" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "yy19Step1MnhlCnt", fieldName: "yy19Step1MnhlCnt", header: { text: "주철" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "yy19Step2MnhlCnt", fieldName: "yy19Step2MnhlCnt", header: { text: "주철(조화)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "yy19Step3MnhlCnt", fieldName: "yy19Step3MnhlCnt", header: { text: "콘크리트" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "yy19Step4MnhlCnt", fieldName: "yy19Step4MnhlCnt", header: { text: "기타" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "yy19Step5MnhlCnt", fieldName: "yy19Step5MnhlCnt", header: { text: "추락방지시설\n설치맨홀(개)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + + // ── 2020년 (yy20) ─────────────────────────────────────── + { name: "yy20WholMnhlCnt", fieldName: "yy20WholMnhlCnt", header: { text: "계" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "yy20Step1MnhlCnt", fieldName: "yy20Step1MnhlCnt", header: { text: "주철" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "yy20Step2MnhlCnt", fieldName: "yy20Step2MnhlCnt", header: { text: "주철(조화)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "yy20Step3MnhlCnt", fieldName: "yy20Step3MnhlCnt", header: { text: "콘크리트" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "yy20Step4MnhlCnt", fieldName: "yy20Step4MnhlCnt", header: { text: "기타" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "yy20Step5MnhlCnt", fieldName: "yy20Step5MnhlCnt", header: { text: "추락방지시설\n설치맨홀(개)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + + // ── 2021년 (yy21) ─────────────────────────────────────── + { name: "yy21WholMnhlCnt", fieldName: "yy21WholMnhlCnt", header: { text: "계" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "yy21Step1MnhlCnt", fieldName: "yy21Step1MnhlCnt", header: { text: "주철" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "yy21Step2MnhlCnt", fieldName: "yy21Step2MnhlCnt", header: { text: "주철(조화)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "yy21Step3MnhlCnt", fieldName: "yy21Step3MnhlCnt", header: { text: "콘크리트" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "yy21Step4MnhlCnt", fieldName: "yy21Step4MnhlCnt", header: { text: "기타" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "yy21Step5MnhlCnt", fieldName: "yy21Step5MnhlCnt", header: { text: "추락방지시설\n설치맨홀(개)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + + // ── 2022년 (yy22) ─────────────────────────────────────── + { name: "yy22WholMnhlCnt", fieldName: "yy22WholMnhlCnt", header: { text: "계" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "yy22Step1MnhlCnt", fieldName: "yy22Step1MnhlCnt", header: { text: "주철" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "yy22Step2MnhlCnt", fieldName: "yy22Step2MnhlCnt", header: { text: "주철(조화)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "yy22Step3MnhlCnt", fieldName: "yy22Step3MnhlCnt", header: { text: "콘크리트" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "yy22Step4MnhlCnt", fieldName: "yy22Step4MnhlCnt", header: { text: "기타" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "yy22Step5MnhlCnt", fieldName: "yy22Step5MnhlCnt", header: { text: "추락방지시설\n설치맨홀(개)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + + // ── 2023년 (yy23) ─────────────────────────────────────── + { name: "yy23WholMnhlCnt", fieldName: "yy23WholMnhlCnt", header: { text: "계" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "yy23Step1MnhlCnt", fieldName: "yy23Step1MnhlCnt", header: { text: "주철" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "yy23Step2MnhlCnt", fieldName: "yy23Step2MnhlCnt", header: { text: "주철(조화)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "yy23Step3MnhlCnt", fieldName: "yy23Step3MnhlCnt", header: { text: "콘크리트" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "yy23Step4MnhlCnt", fieldName: "yy23Step4MnhlCnt", header: { text: "기타" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "yy23Step5MnhlCnt", fieldName: "yy23Step5MnhlCnt", header: { text: "추락방지시설\n설치맨홀(개)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + + // ── 2024년 (yy24) ─────────────────────────────────────── + { name: "yy24WholMnhlCnt", fieldName: "yy24WholMnhlCnt", header: { text: "계" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "yy24Step1MnhlCnt", fieldName: "yy24Step1MnhlCnt", header: { text: "주철" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "yy24Step2MnhlCnt", fieldName: "yy24Step2MnhlCnt", header: { text: "주철(조화)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "yy24Step3MnhlCnt", fieldName: "yy24Step3MnhlCnt", header: { text: "콘크리트" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "yy24Step4MnhlCnt", fieldName: "yy24Step4MnhlCnt", header: { text: "기타" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "yy24Step5MnhlCnt", fieldName: "yy24Step5MnhlCnt", header: { text: "추락방지시설\n설치맨홀(개)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + + // ── 2025년 (yy25) ─────────────────────────────────────── + { name: "yy25WholMnhlCnt", fieldName: "yy25WholMnhlCnt", header: { text: "계" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "yy25Step1MnhlCnt", fieldName: "yy25Step1MnhlCnt", header: { text: "주철" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { name: "yy25Step2MnhlCnt", fieldName: "yy25Step2MnhlCnt", header: { text: "주철(조화)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false }, + { 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"] }, + "emphsMngSeNm", + { + name: "totGroup", + header: { text: "연도별총계" }, + items: [ + { name: "totMtrlGroup", header: { text: "맨홀뚜껑 재질(개)" }, items: ["totWholMnhlCnt", "totStep1MnhlCnt", "totStep2MnhlCnt", "totStep3MnhlCnt", "totStep4MnhlCnt"] }, + "totStep5MnhlCnt", + ], + }, + { + name: "yy14Group", + header: { text: "2015년 이전" }, // ← _14Y + items: [ + { name: "yy14MtrlGroup", header: { text: "맨홀뚜껑 재질(개)" }, items: ["yy14WholMnhlCnt", "yy14Step1MnhlCnt", "yy14Step2MnhlCnt", "yy14Step3MnhlCnt", "yy14Step4MnhlCnt"] }, + "yy14Step5MnhlCnt", + ], + }, + { + name: "yy15Group", + header: { text: "2015년" }, // ← _15Y + items: [ + { name: "yy15MtrlGroup", header: { text: "맨홀뚜껑 재질(개)" }, items: ["yy15WholMnhlCnt", "yy15Step1MnhlCnt", "yy15Step2MnhlCnt", "yy15Step3MnhlCnt", "yy15Step4MnhlCnt"] }, + "yy15Step5MnhlCnt", + ], + }, + { + name: "yy16Group", + header: { text: "2016년" }, // ← _16Y + items: [ + { name: "yy16MtrlGroup", header: { text: "맨홀뚜껑 재질(개)" }, items: ["yy16WholMnhlCnt", "yy16Step1MnhlCnt", "yy16Step2MnhlCnt", "yy16Step3MnhlCnt", "yy16Step4MnhlCnt"] }, + "yy16Step5MnhlCnt", + ], + }, + { + name: "yy17Group", + header: { text: "2017년" }, + items: [ + { name: "yy17MtrlGroup", header: { text: "맨홀뚜껑 재질(개)" }, items: ["yy17WholMnhlCnt", "yy17Step1MnhlCnt", "yy17Step2MnhlCnt", "yy17Step3MnhlCnt", "yy17Step4MnhlCnt"] }, + "yy17Step5MnhlCnt", + ], + }, + { + name: "yy18Group", + header: { text: "2018년" }, + items: [ + { name: "yy18MtrlGroup", header: { text: "맨홀뚜껑 재질(개)" }, items: ["yy18WholMnhlCnt", "yy18Step1MnhlCnt", "yy18Step2MnhlCnt", "yy18Step3MnhlCnt", "yy18Step4MnhlCnt"] }, + "yy18Step5MnhlCnt", + ], + }, + { + name: "yy19Group", + header: { text: "2019년" }, + items: [ + { name: "yy19MtrlGroup", header: { text: "맨홀뚜껑 재질(개)" }, items: ["yy19WholMnhlCnt", "yy19Step1MnhlCnt", "yy19Step2MnhlCnt", "yy19Step3MnhlCnt", "yy19Step4MnhlCnt"] }, + "yy19Step5MnhlCnt", + ], + }, + { + name: "yy20Group", + header: { text: "2020년" }, + items: [ + { name: "yy20MtrlGroup", header: { text: "맨홀뚜껑 재질(개)" }, items: ["yy20WholMnhlCnt", "yy20Step1MnhlCnt", "yy20Step2MnhlCnt", "yy20Step3MnhlCnt", "yy20Step4MnhlCnt"] }, + "yy20Step5MnhlCnt", + ], + }, + { + name: "yy21Group", + header: { text: "2021년" }, + items: [ + { name: "yy21MtrlGroup", header: { text: "맨홀뚜껑 재질(개)" }, items: ["yy21WholMnhlCnt", "yy21Step1MnhlCnt", "yy21Step2MnhlCnt", "yy21Step3MnhlCnt", "yy21Step4MnhlCnt"] }, + "yy21Step5MnhlCnt", + ], + }, + { + name: "yy22Group", + header: { text: "2022년" }, + items: [ + { name: "yy22MtrlGroup", header: { text: "맨홀뚜껑 재질(개)" }, items: ["yy22WholMnhlCnt", "yy22Step1MnhlCnt", "yy22Step2MnhlCnt", "yy22Step3MnhlCnt", "yy22Step4MnhlCnt"] }, + "yy22Step5MnhlCnt", + ], + }, + { + name: "yy23Group", + header: { text: "2023년" }, + items: [ + { name: "yy23MtrlGroup", header: { text: "맨홀뚜껑 재질(개)" }, items: ["yy23WholMnhlCnt", "yy23Step1MnhlCnt", "yy23Step2MnhlCnt", "yy23Step3MnhlCnt", "yy23Step4MnhlCnt"] }, + "yy23Step5MnhlCnt", + ], + }, + { + name: "yy24Group", + header: { text: "2024년" }, + items: [ + { name: "yy24MtrlGroup", header: { text: "맨홀뚜껑 재질(개)" }, items: ["yy24WholMnhlCnt", "yy24Step1MnhlCnt", "yy24Step2MnhlCnt", "yy24Step3MnhlCnt", "yy24Step4MnhlCnt"] }, + "yy24Step5MnhlCnt", + ], + }, + { + name: "yy25Group", + header: { text: "2025년" }, + items: [ + { name: "yy25MtrlGroup", header: { text: "맨홀뚜껑 재질(개)" }, items: ["yy25WholMnhlCnt", "yy25Step1MnhlCnt", "yy25Step2MnhlCnt", "yy25Step3MnhlCnt", "yy25Step4MnhlCnt"] }, + "yy25Step5MnhlCnt", + ], + }, + "rmrk", +], +}, + ]; export const SewagePipeColumnInfoListTot = rawSewagePipeTotList.map((tab) => ({ ...tab, columns: tab.columns.map((col) => ({ ...col, editable: false as const })), })); + diff --git a/apps/st/src/entities/statistics/model/statistics-total/statistics-sewage-pipe-total-interface.ts b/apps/st/src/entities/statistics/model/statistics-total/statistics-sewage-pipe-total-interface.ts index d9a7825cf..8643d41d6 100644 --- a/apps/st/src/entities/statistics/model/statistics-total/statistics-sewage-pipe-total-interface.ts +++ b/apps/st/src/entities/statistics/model/statistics-total/statistics-sewage-pipe-total-interface.ts @@ -4,6 +4,8 @@ export interface SewagePipeTotalRequest { statsYr?: string; sewerBzmnCd?: string; rowDataYn?: boolean; + summaryYn?: boolean; + summaryYearCnt?: number; } export interface SewagePipeTotalItem { @@ -234,3 +236,130 @@ export interface SewagePipePipeDiamStatusResponse { message?: string; data: SewagePipePipeDiamStatusItem[]; } + +/** + * 맨홀 현황 응답 아이템 인터페이스 + * DTO의 yy14(2014년 이전) ~ yy25(2024년) 필드를 모두 명시적으로 선언 + */ +export interface FcltyManholeSummaryItem { + /* --- 기본 정보 및 행 제어 --- */ + ctpvNm: string; // 시도명 + sggNm: string; // 시군구명 + admdstNm: string; // 행정구역명 + admdstCd: string; // 행정구역코드 + statsYr: string; // 통계연도 + rowType: string; // 데이터 구분 (OLD, CURR, SUMMARY 등) + emphsMngSeCd: string; // 중점관리구역구분코드 + emphsMngSeNm: string; // 중점관리구역구분명 + + totWholMnhlCnt: number; + totStep1MnhlCnt: number; + totStep2MnhlCnt: number; + totStep3MnhlCnt: number; + totStep4MnhlCnt: number; + totStep5MnhlCnt: number; + /* --- yy14 (2014년 이전) --- */ + yy14WholMnhlCnt: number; // 합계 + yy14Step1MnhlCnt: number; // 1단계 + yy14Step2MnhlCnt: number; // 2단계 + yy14Step3MnhlCnt: number; // 3단계 + yy14Step4MnhlCnt: number; // 4단계 + yy14Step5MnhlCnt: number; // 5단계 + + /* --- yy15 (2014년) --- */ + yy15WholMnhlCnt: number; + yy15Step1MnhlCnt: number; + yy15Step2MnhlCnt: number; + yy15Step3MnhlCnt: number; + yy15Step4MnhlCnt: number; + yy15Step5MnhlCnt: number; + + /* --- yy16 (2015년) --- */ + yy16WholMnhlCnt: number; + yy16Step1MnhlCnt: number; + yy16Step2MnhlCnt: number; + yy16Step3MnhlCnt: number; + yy16Step4MnhlCnt: number; + yy16Step5MnhlCnt: number; + + /* --- yy17 (2016년) --- */ + yy17WholMnhlCnt: number; + yy17Step1MnhlCnt: number; + yy17Step2MnhlCnt: number; + yy17Step3MnhlCnt: number; + yy17Step4MnhlCnt: number; + yy17Step5MnhlCnt: number; + + /* --- yy18 (2017년) --- */ + yy18WholMnhlCnt: number; + yy18Step1MnhlCnt: number; + yy18Step2MnhlCnt: number; + yy18Step3MnhlCnt: number; + yy18Step4MnhlCnt: number; + yy18Step5MnhlCnt: number; + + /* --- yy19 (2018년) --- */ + yy19WholMnhlCnt: number; + yy19Step1MnhlCnt: number; + yy19Step2MnhlCnt: number; + yy19Step3MnhlCnt: number; + yy19Step4MnhlCnt: number; + yy19Step5MnhlCnt: number; + + /* --- yy20 (2019년) --- */ + yy20WholMnhlCnt: number; + yy20Step1MnhlCnt: number; + yy20Step2MnhlCnt: number; + yy20Step3MnhlCnt: number; + yy20Step4MnhlCnt: number; + yy20Step5MnhlCnt: number; + + /* --- yy21 (2020년) --- */ + yy21WholMnhlCnt: number; + yy21Step1MnhlCnt: number; + yy21Step2MnhlCnt: number; + yy21Step3MnhlCnt: number; + yy21Step4MnhlCnt: number; + yy21Step5MnhlCnt: number; + + /* --- yy22 (2021년) --- */ + yy22WholMnhlCnt: number; + yy22Step1MnhlCnt: number; + yy22Step2MnhlCnt: number; + yy22Step3MnhlCnt: number; + yy22Step4MnhlCnt: number; + yy22Step5MnhlCnt: number; + + /* --- yy23 (2022년) --- */ + yy23WholMnhlCnt: number; + yy23Step1MnhlCnt: number; + yy23Step2MnhlCnt: number; + yy23Step3MnhlCnt: number; + yy23Step4MnhlCnt: number; + yy23Step5MnhlCnt: number; + + /* --- yy24 (2023년) --- */ + yy24WholMnhlCnt: number; + yy24Step1MnhlCnt: number; + yy24Step2MnhlCnt: number; + yy24Step3MnhlCnt: number; + yy24Step4MnhlCnt: number; + yy24Step5MnhlCnt: number; + + /* --- yy25 (2024년) --- */ + yy25WholMnhlCnt: number; + yy25Step1MnhlCnt: number; + yy25Step2MnhlCnt: number; + yy25Step3MnhlCnt: number; + yy25Step4MnhlCnt: number; + yy25Step5MnhlCnt: number; +} + +/** + * 맨홀 현황 API 공통 응답 규격 + */ +export interface FcltyManholeSummaryResponse { + status: string; // success / error 등 + message?: string; // 오류 메시지 등 + data: FcltyManholeSummaryItem[]; // 실제 리스트 데이터 +} From e10ebf4219d8dfb918f0dc61409ee2a7046636c6 Mon Sep 17 00:00:00 2001 From: rsone001 Date: Mon, 11 May 2026 13:20:08 +0900 Subject: [PATCH 6/7] =?UTF-8?q?[st]=20feat:=20=EC=A7=91=EA=B3=84=202,6=20/?= =?UTF-8?q?=20=EC=9E=85=EB=A0=A5=202,6=20/=20=ED=86=B5=EA=B3=84=20?= =?UTF-8?q?=EC=9E=85=EB=A0=A5=20=EA=B4=80=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/st/src/components/css/DataGrid.css | 13 + apps/st/src/components/module/SearchForm.tsx | 32 ++ .../VerificationResultHistoryDialog.tsx | 39 +- .../VerificationSelectionReport.tsx | 2 +- .../input/MainFacilityMappingSaveDialog.tsx | 224 +++++++++ .../input/PublicSewageFacilityInp.tsx | 445 ++++++++++++------ .../statistics/input/SewagePipeInp.tsx | 68 +-- .../input/SeweragePenetrationRateInp.tsx | 59 +-- .../management/StatisticsInputManagement.tsx | 228 +++++++-- .../StatisticsInputPeriodDialog.tsx | 315 +++++++++++++ .../statistics/review/SewagePipeRvw.tsx | 74 ++- .../st/src/components/type/SearchForm.type.ts | 8 + .../MainFacilityInpColumn.ts | 6 +- .../PublicSewageFactColumn.ts | 6 +- .../statistics/api/statistics-service.ts | 47 +- ...-public-sewage-facility-input-interface.ts | 4 + .../statistics/model/statistics-interface.ts | 46 ++ 17 files changed, 1350 insertions(+), 266 deletions(-) create mode 100644 apps/st/src/components/statistics/input/MainFacilityMappingSaveDialog.tsx create mode 100644 apps/st/src/components/statistics/management/StatisticsInputPeriodDialog.tsx diff --git a/apps/st/src/components/css/DataGrid.css b/apps/st/src/components/css/DataGrid.css index 2d8eab48c..8a2d8d928 100644 --- a/apps/st/src/components/css/DataGrid.css +++ b/apps/st/src/components/css/DataGrid.css @@ -29,6 +29,13 @@ .rg-grid .rg-body .rg-table .rg-data-row .rg-data-cell.rg-cell-verify-error-imr { @apply !bg-[#fff7bf] !text-[#b45309]; } + /* 검증결과 내역: 처리여부(prcsYn 0·4 미처리 등) */ + .rg-grid .rg-body .rg-table .rg-data-row .rg-data-cell.rg-cell-vrf-prcs-bad { + @apply !font-semibold !text-[#dc2626]; + } + .rg-grid .rg-body .rg-table .rg-data-row .rg-data-cell.rg-cell-vrf-prcs-ok { + @apply !font-semibold !text-[#3372cf]; + } .rg-grid .rg-body .rg-table .rg-data-row .rg-data-cell.rg-cell-total { @apply !bg-[#e3eaf2]; } @@ -95,6 +102,12 @@ .rg-grid .rg-body .rg-table .rg-data-row .rg-data-cell { @apply !bg-[#545556] !text-[#fff]; } + .rg-grid .rg-body .rg-table .rg-data-row .rg-data-cell.rg-cell-vrf-prcs-bad { + @apply !text-[#f87171]; + } + .rg-grid .rg-body .rg-table .rg-data-row .rg-data-cell.rg-cell-vrf-prcs-ok { + @apply !text-[#3372cf]; + } } [data-theme="red"] { diff --git a/apps/st/src/components/module/SearchForm.tsx b/apps/st/src/components/module/SearchForm.tsx index cc938a282..c759b4a92 100644 --- a/apps/st/src/components/module/SearchForm.tsx +++ b/apps/st/src/components/module/SearchForm.tsx @@ -25,6 +25,7 @@ import { import { defaultSearchFormProps, + type SearchFormClsfCd, type SearchFormProps, type SearchFormValues, } from "../type/SearchForm.type"; @@ -81,6 +82,12 @@ const SearchForm = (props: SearchFormProps = {}) => { }, ); + const initialClsfCd: SearchFormClsfCd = (() => { + const d = props.defaultValues?.clsfCd; + if (d === "ALL" || d === "INPT" || d === "VRFC") return d; + return "ALL"; + })(); + const [values, setValues] = useState({ sido: props.defaultValues?.sido || "", gugun: props.defaultValues?.gugun || "", @@ -90,6 +97,7 @@ const SearchForm = (props: SearchFormProps = {}) => { bsnmCd: props.defaultValues?.bsnmCd || "", isDetail: false, isSummary: props.defaultValues?.isSummary || "", + clsfCd: initialClsfCd, }); const sggKey: [string, string, string, string] | null = @@ -134,6 +142,7 @@ const SearchForm = (props: SearchFormProps = {}) => { bsnmCd: "", isDetail: false, isSummary: "", + clsfCd: "ALL", }; setValues(resetValues); props.onReset?.(); @@ -250,6 +259,29 @@ const SearchForm = (props: SearchFormProps = {}) => { )} + {mergedProps.clsf && ( +
+ + +
+ )} {mergedProps.fctName && (