diff --git a/apps/st/.env.example b/apps/st/.env.example index 9f786831b..c0d26e6f2 100644 --- a/apps/st/.env.example +++ b/apps/st/.env.example @@ -1,7 +1,7 @@ # apps/cs/src/shared/config/env.ts 참고 NEXT_PUBLIC_PORT=3000 NEXT_PUBLIC_PROXY_HOST=http://localhost:3000 +SERVER_API_URL=http://192.168.0.102:8000 NEXT_PUBLIC_SITE_ID=CS NEXT_PUBLIC_SUB_DOMAIN=http://localhost:3000 -NEXT_PUBLIC_SERVER_API_URL=http://192.168.0.102:8000 NEXT_PUBLIC_TINY_API_KEY=t9nc4z6inquvvd5pq1vea8wcp7lb01qmqseh6egms9vpaju2 \ No newline at end of file diff --git a/apps/st/middleware.ts b/apps/st/middleware.ts index 9a2a48cb1..db6600caf 100644 --- a/apps/st/middleware.ts +++ b/apps/st/middleware.ts @@ -1,147 +1,159 @@ import { NextRequest, NextResponse } from "next/server"; + import { - CLAIM_NAME, - AUTH_USER_ID, - PORT, - PROXY_HOST, - SERVER_API_URL, - SITE_ID, - ST_SERVICE_URL, + AUTH_USER_ID, + CLAIM_NAME, + PORT, + PROXY_HOST, + SERVER_API_URL, + SITE_ID, + ST_SERVICE_URL, } from "./src/shared/config"; -const serverApiUrl = - process.env.NEXT_PUBLIC_SERVER_API_URL ?? "http://localhost:3000"; +const serverApiUrl = process.env.SERVER_API_URL ?? "http://localhost:3000"; const stServiceUrl = ST_SERVICE_URL; -const linkBackendApiHost = process.env.NEXT_PUBLIC_LINK_BACKEND_API_HOST ?? "http://localhost:8080"; +const linkBackendApiHost = + process.env.NEXT_PUBLIC_LINK_BACKEND_API_HOST ?? "http://localhost:8080"; // CSP 헤더 설정 함수 (img-src만) function addSecurityHeaders(response: NextResponse): NextResponse { - response.headers.set("Content-Security-Policy", "img-src 'self' data: https: blob:;"); - return response; + response.headers.set( + "Content-Security-Policy", + "img-src 'self' data: https: blob:;", + ); + return response; } export function middleware(req: NextRequest) { - const pathname = req.nextUrl.pathname; - const searchParams = req.nextUrl.searchParams; + const pathname = req.nextUrl.pathname; + const searchParams = req.nextUrl.searchParams; - const logMessages: string[] = [ - `[Middleware Request] ${new Date().toISOString()}`, - `[Environment]\n Mode: ${process.env.NODE_ENV}\n Port: ${PORT}\n Proxy Host: ${PROXY_HOST}\n Server API URL: ${SERVER_API_URL}\n Site ID: ${SITE_ID}\n ST Service URL: ${stServiceUrl}`, - `[Request]\n Original Pathname: ${pathname}`, - ]; + const logMessages: string[] = [ + `[Middleware Request] ${new Date().toISOString()}`, + `[Environment]\n Mode: ${process.env.NODE_ENV}\n Port: ${PORT}\n Proxy Host: ${PROXY_HOST}\n Server API URL: ${SERVER_API_URL}\n Site ID: ${SITE_ID}\n ST Service URL: ${stServiceUrl}`, + `[Request]\n Original Pathname: ${pathname}`, + ]; - // ST Service 처리 (dc의 /server/dc-service 패턴과 동일) - if (pathname.startsWith("/server/st-management-service") || pathname.startsWith("/server/st-input-service")) { - const requestHeaders = new Headers(req.headers); - const token = req.cookies.get(CLAIM_NAME)?.value; - const userId = req.cookies.get(AUTH_USER_ID)?.value; + // ST Service 처리 (dc의 /server/dc-service 패턴과 동일) + if ( + pathname.startsWith("/server/st-management-service") || + pathname.startsWith("/server/st-input-service") + ) { + const requestHeaders = new Headers(req.headers); + const token = req.cookies.get(CLAIM_NAME)?.value; + const userId = req.cookies.get(AUTH_USER_ID)?.value; - const queryParams = searchParams.toString(); - const newUrl = `${stServiceUrl}${pathname.replace("/server", "")}${ - queryParams ? `?${queryParams}` : "" - }`; + const queryParams = searchParams.toString(); + const newUrl = `${stServiceUrl}${pathname.replace("/server", "")}${ + queryParams ? `?${queryParams}` : "" + }`; - if (token) requestHeaders.set(CLAIM_NAME, token); - if (userId) requestHeaders.set(AUTH_USER_ID, userId); + if (token) requestHeaders.set(CLAIM_NAME, token); + if (userId) requestHeaders.set(AUTH_USER_ID, userId); + + logMessages.push(` Token Debug: ${token ? "EXISTS" : "MISSING"}`); + logMessages.push(` UserId Debug: ${userId ? "EXISTS" : "MISSING"}`); + logMessages.push(` ST Service Rewritten URL: ${newUrl}`); + logMessages.push("\n--------------------------------\n"); + console.log(logMessages.join("\n")); + + const response = NextResponse.rewrite(new URL(newUrl), { + headers: requestHeaders, + }); + return addSecurityHeaders(response); + } + + // 일반 Server API 처리 (ST 서비스 제외) + if ( + pathname.startsWith("/server") && + !pathname.startsWith("/server/st-management-service") && + !pathname.startsWith("/server/st-input-service") + ) { + const requestHeaders = new Headers(req.headers); + const token = req.cookies.get(CLAIM_NAME)?.value; + const userId = req.cookies.get(AUTH_USER_ID)?.value; + + const queryParams = searchParams.toString(); + const newUrl = `${serverApiUrl}${pathname.replace("/server", "")}${ + queryParams ? `?${queryParams}` : "" + }`; + + if (token) requestHeaders.set(CLAIM_NAME, token); + if (userId) requestHeaders.set(AUTH_USER_ID, userId); + + logMessages.push(` Rewritten URL: ${newUrl}`); + logMessages.push("\n--------------------------------\n"); + console.log(logMessages.join("\n")); + + const response = NextResponse.rewrite(new URL(newUrl), { + headers: requestHeaders, + }); + return addSecurityHeaders(response); + } + + if (pathname.startsWith("/attach-service")) { + const requestHeaders = new Headers(req.headers); + const token = req.cookies.get(CLAIM_NAME)?.value; + const userId = req.cookies.get(AUTH_USER_ID)?.value; + const newUrl = `${serverApiUrl}${pathname}`; + + if (token) requestHeaders.set(CLAIM_NAME, token); + if (userId) requestHeaders.set(AUTH_USER_ID, userId); + + logMessages.push(` Rewritten URL: ${newUrl}`); + logMessages.push("\n--------------------------------\n"); + console.log(logMessages.join("\n")); + + const response = NextResponse.rewrite(new URL(newUrl), { + headers: requestHeaders, + }); + return addSecurityHeaders(response); + } + + if (pathname.startsWith("/api/link")) { + const requestHeaders = new Headers(req.headers); + const token = req.cookies.get(CLAIM_NAME)?.value; + const userId = req.cookies.get(AUTH_USER_ID)?.value; + + // /api/link 경로를 제거하고 백엔드 API 경로로 변환 + const apiPath = pathname.replace("/api/link", ""); + const queryParams = searchParams.toString(); + const newUrl = `${linkBackendApiHost}${apiPath}${ + queryParams ? `?${queryParams}` : "" + }`; + + if (token) requestHeaders.set(CLAIM_NAME, token); + if (userId) requestHeaders.set(AUTH_USER_ID, userId); + + logMessages.push(` Link API Rewritten URL: ${newUrl}`); + logMessages.push("\n--------------------------------\n"); + console.log(logMessages.join("\n")); + + const response = NextResponse.rewrite(new URL(newUrl), { + headers: requestHeaders, + }); + return addSecurityHeaders(response); + } + + if (pathname === "/") { + const response = NextResponse.redirect(new URL("/statistics", req.url)); + return addSecurityHeaders(response); + } - logMessages.push(` Token Debug: ${token ? "EXISTS" : "MISSING"}`); - logMessages.push(` UserId Debug: ${userId ? "EXISTS" : "MISSING"}`); - logMessages.push(` ST Service Rewritten URL: ${newUrl}`); logMessages.push("\n--------------------------------\n"); console.log(logMessages.join("\n")); - const response = NextResponse.rewrite(new URL(newUrl), { - headers: requestHeaders, - }); + const response = NextResponse.next(); return addSecurityHeaders(response); - } - - // 일반 Server API 처리 (ST 서비스 제외) - if ( - pathname.startsWith("/server") && - !pathname.startsWith("/server/st-management-service") && - !pathname.startsWith("/server/st-input-service") - ) { - const requestHeaders = new Headers(req.headers); - const token = req.cookies.get(CLAIM_NAME)?.value; - const userId = req.cookies.get(AUTH_USER_ID)?.value; - - const queryParams = searchParams.toString(); - const newUrl = `${serverApiUrl}${pathname.replace("/server", "")}${ - queryParams ? `?${queryParams}` : "" - }`; - - if (token) requestHeaders.set(CLAIM_NAME, token); - if (userId) requestHeaders.set(AUTH_USER_ID, userId); - - logMessages.push(` Rewritten URL: ${newUrl}`); - logMessages.push("\n--------------------------------\n"); - console.log(logMessages.join("\n")); - - const response = NextResponse.rewrite(new URL(newUrl), { - headers: requestHeaders, - }); - return addSecurityHeaders(response); - } - - if (pathname.startsWith("/attach-service")) { - const requestHeaders = new Headers(req.headers); - const token = req.cookies.get(CLAIM_NAME)?.value; - const userId = req.cookies.get(AUTH_USER_ID)?.value; - const newUrl = `${serverApiUrl}${pathname}`; - - if (token) requestHeaders.set(CLAIM_NAME, token); - if (userId) requestHeaders.set(AUTH_USER_ID, userId); - - logMessages.push(` Rewritten URL: ${newUrl}`); - logMessages.push("\n--------------------------------\n"); - console.log(logMessages.join("\n")); - - const response = NextResponse.rewrite(new URL(newUrl), { - headers: requestHeaders, - }); - return addSecurityHeaders(response); - } - - if (pathname.startsWith("/api/link")) { - const requestHeaders = new Headers(req.headers); - const token = req.cookies.get(CLAIM_NAME)?.value; - const userId = req.cookies.get(AUTH_USER_ID)?.value; - - // /api/link 경로를 제거하고 백엔드 API 경로로 변환 - const apiPath = pathname.replace("/api/link", ""); - const queryParams = searchParams.toString(); - const newUrl = `${linkBackendApiHost}${apiPath}${ - queryParams ? `?${queryParams}` : "" - }`; - - if (token) requestHeaders.set(CLAIM_NAME, token); - if (userId) requestHeaders.set(AUTH_USER_ID, userId); - - logMessages.push(` Link API Rewritten URL: ${newUrl}`); - logMessages.push("\n--------------------------------\n"); - console.log(logMessages.join("\n")); - - const response = NextResponse.rewrite(new URL(newUrl), { - headers: requestHeaders, - }); - return addSecurityHeaders(response); - } - - if (pathname === "/") { - const response = NextResponse.redirect(new URL("/statistics", req.url)); - return addSecurityHeaders(response); - } - - logMessages.push("\n--------------------------------\n"); - console.log(logMessages.join("\n")); - - const response = NextResponse.next(); - return addSecurityHeaders(response); } export const config = { - matcher: ["/server/:path*", "/attach-service/:path*", "/api/link/:path*", "/"], + matcher: [ + "/server/:path*", + "/attach-service/:path*", + "/api/link/:path*", + "/", + ], }; diff --git a/apps/st/src/components/css/DataGrid.css b/apps/st/src/components/css/DataGrid.css index 46a1c6304..8d56c48f8 100644 --- a/apps/st/src/components/css/DataGrid.css +++ b/apps/st/src/components/css/DataGrid.css @@ -44,6 +44,21 @@ .rg-grid .rg-body .rg-table .rg-data-row .rg-data-cell.rg-cell-rowtype-other { @apply !bg-[#fff7bf]; } + /* fixedColumns 영역은 rg-table/rg-data-row 구조가 다를 수 있어 별도 커버 */ + .rg-grid .rg-body .rg-data-cell.rg-cell-rowtype-biz-sum { + @apply !bg-[#ffe9cf]; + } + .rg-grid .rg-body .rg-data-cell.rg-cell-rowtype-other { + @apply !bg-[#fff7bf]; + } + .rg-grid .rg-fixed-body .rg-table .rg-data-row td.rg-cell-rowtype-biz-sum, + .rg-grid .rg-fixed-body .rg-data-row .rg-data-cell.rg-cell-rowtype-biz-sum { + @apply !bg-[#ffe9cf]; + } + .rg-grid .rg-fixed-body .rg-table .rg-data-row td.rg-cell-rowtype-other, + .rg-grid .rg-fixed-body .rg-data-row .rg-data-cell.rg-cell-rowtype-other { + @apply !bg-[#fff7bf]; + } /* 진행상황: 소프트 인라인 요약 (툴바와 자연스럽게 blend) */ .data-grid-status-bubble { @@ -108,4 +123,19 @@ .tabs-content { margin-top: 0.6rem !important; +} + +/* + * RealGrid 기본 스타일(.rg-alternate-row { background: #fafafa })은 @layer 밖이라 + * @layer components 규칙만으로는 tr 배경이 남을 수 있음. + * 스크롤 바디(.rg-body)만 흰색으로 통일 — 고정 영역(.rg-fixed-body)은 건드리지 않음. + */ +.rg-grid .rg-body .rg-table tr.rg-alternate-row { + background: #ffffff !important; + background-color: #ffffff !important; +} + +.rg-grid .rg-body .rg-table tr.rg-alternate-row td, +.rg-grid .rg-body .rg-table tr.rg-alternate-row .rg-data-cell { + background-color: #ffffff !important; } \ No newline at end of file diff --git a/apps/st/src/components/module/TabDataGrid.tsx b/apps/st/src/components/module/TabDataGrid.tsx index 7f511665c..2aba5350b 100644 --- a/apps/st/src/components/module/TabDataGrid.tsx +++ b/apps/st/src/components/module/TabDataGrid.tsx @@ -1,19 +1,22 @@ -import { useRef, useEffect, useState } from "react" -import { ValueType } from "realgrid" +import { useCallback,useEffect, useLayoutEffect, useRef, useState } from "react" +import JSZip from "jszip" +import { ValueType } from "realgrid" import RealGrid, { GridFitStyle } from "realgrid" import { RealGridReact, RGDataColumn } from "realgrid-react" -import type { TabDataGridProps, ColumnInfo, RealGridColumnInfo, GridViewWithSetColumns, FooterItemWithCallback, ColumnLayoutItem } from "../type/DataGrid.type" -import { getComputedValueCallback } from "@/components/util/grid/computedFieldCallbacks" -import JSZip from "jszip" import * as XLSX from "xlsx" -import { useAlertStore } from "@/shared/lib"; -import { useTabStore } from "@/store/tab-store" -import { Tabs, TabsList, TabsTrigger, TabsContent } from "@common/components"; +import { useAlertStore } from "@/shared/lib"; +import { Tabs, TabsContent,TabsList, TabsTrigger } from "@common/components"; import { Button } from "@common/components"; -import SplitDataGrid from "./SplitDataGrid"; + +import type { CellStyleCallback,ColumnConfig, ColumnInfo, ColumnLayoutItem, FooterItemWithCallback, GridViewWithSetColumns, RealGridColumnInfo, TabDataGridProps } from "../type/DataGrid.type" + import GridTopActionButton from "./GridTopActionButton"; +import SplitDataGrid from "./SplitDataGrid"; + +import { getComputedValueCallback } from "@/components/util/grid/computedFieldCallbacks" +import { useTabStore } from "@/store/tab-store" // RealGrid 타입 확장 declare module "realgrid" { @@ -50,6 +53,30 @@ if (typeof window !== "undefined") { RealGrid.setLicenseKey(process.env.NEXT_PUBLIC_ST_REALGRID_KEY || ''); +/** RealGrid 내부 해제(_view 등) 타이밍에서 refresh가 터질 수 있어 try/catch */ +function safeGridViewRefresh(gv: { refresh?: () => void } | null | undefined) { + if (!gv?.refresh) return; + try { + gv.refresh(); + } catch { + /* noop */ + } +} + +/** columnByName은 그리드 _view 준비 전에 호출되면 "Cannot read ... '_view'"로 터짐 */ +function safeColumnByName( + gv: { columnByName?: (name: string) => unknown } | null | undefined, + name: string, +): { styleCallback?: (g: unknown, dataCell: Record) => string } | null { + if (!gv?.columnByName) return null; + try { + const col = gv.columnByName(name) as { styleCallback?: (g: unknown, dataCell: Record) => string } | null; + return col ?? null; + } catch { + return null; + } +} + /** 정렬 필드 값을 숫자로 파싱 (쉼표 제거) */ function toNum(v: unknown): number { if (v == null) return NaN; @@ -228,6 +255,7 @@ const TabDataGrid = ({ getGridDataRef, enableSort = false, facilitySearchStatsYr, + cellStyleCallback, }: TabDataGridProps) => { // 각 탭별로 그리드 ref와 데이터를 관리 const gridRefs = useRef>(new Map()); @@ -240,6 +268,69 @@ const TabDataGrid = ({ const activeTabId = useTabStore((state) => state.activeTabId); const menuList = useTabStore((state) => state.menuList); + const rowDataMapRef = useRef(rowDataMap); + rowDataMapRef.current = rowDataMap; + const cellStyleCallbackRef = useRef(undefined); + cellStyleCallbackRef.current = cellStyleCallback; + + const attachCellStyleCallbacksForTab = useCallback((tabKey: string, columns: ColumnConfig[], attempt = 0) => { + if (!cellStyleCallbackRef.current) return; + const el = gridRefs.current.get(tabKey); + const gv = el?.gridView; + if (!gv) return; + + let attachedCount = 0; + for (const column of columns) { + const gridCol = safeColumnByName(gv, column.name); + if (!gridCol) continue; + const baseStyleName = column.styleName; + gridCol.styleCallback = (_g, dataCell) => { + const dc = dataCell as { + itemIndex?: number; + index?: { itemIndex?: number }; + dataField?: string; + fieldName?: string; + }; + const itemIndex = dc?.index?.itemIndex ?? dc?.itemIndex ?? -1; + const fieldName = dc?.dataField ?? dc?.fieldName ?? column.fieldName; + const data = rowDataMapRef.current.get(tabKey) || []; + if (itemIndex < 0 || itemIndex >= data.length) { + return baseStyleName ?? ""; + } + const row = data[itemIndex] as Record; + const extra = cellStyleCallbackRef.current?.({ + rowIndex: itemIndex, + fieldName, + row, + }); + if (!extra) return baseStyleName ?? ""; + return baseStyleName ? `${extra} ${baseStyleName}` : extra; + }; + attachedCount += 1; + } + + if (attachedCount === 0 && attempt < 20) { + setTimeout(() => attachCellStyleCallbacksForTab(tabKey, columns, attempt + 1), 100); + return; + } + + setTimeout(() => { + const latestGv = gridRefs.current.get(tabKey)?.gridView; + safeGridViewRefresh(latestGv ?? null); + }, 0); + }, []); + + /** 스타일 콜백은 ref로 최신 row를 읽음 — 여기서는 재부착 없이 refresh만 (이른 columnByName 호출 방지) */ + useLayoutEffect(() => { + if (!cellStyleCallback) return; + const id = requestAnimationFrame(() => { + gridRefs.current.forEach((ref) => { + safeGridViewRefresh(ref?.gridView ?? null); + }); + }); + return () => cancelAnimationFrame(id); + }, [cellStyleCallback, rowDataMap]); + const [internalActiveTab, setInternalActiveTab] = useState(() => { const firstTab = columnInfo[0]; return firstTab?.tabKey || ''; @@ -559,25 +650,34 @@ const TabDataGrid = ({ // rowDataMap이 변경될 때 그리드에 데이터 반영 (Excel import 등) useEffect(() => { + const timeoutIds: ReturnType[] = []; + columnInfo.forEach((info) => { const tabKey = info.tabKey; const gridRef = gridRefs.current.get(tabKey); const currentData = rowDataMap.get(tabKey); if (gridRef?.gridView && gridRef?.dataProvider && currentData && currentData.length > 0) { - setTimeout(() => { + const id = setTimeout(() => { try { - const dp = gridRef.dataProvider as RealGridDataProviderFill | null; - if (dp?.fillJsonData) { - dp.fillJsonData(currentData, { fillMode: "set" }); - } - gridRef.gridView.refresh(); + const latestRef = gridRefs.current.get(tabKey); + const gv = latestRef?.gridView; + const dp = latestRef?.dataProvider as RealGridDataProviderFill | null; + if (!gv || !dp?.fillJsonData || !currentData.length) return; + + dp.fillJsonData(currentData, { fillMode: "set" }); + gv.refresh(); } catch (error) { console.error("그리드 데이터 설정 오류:", error); } }, 100); + timeoutIds.push(id); } }); + + return () => { + timeoutIds.forEach((tid) => clearTimeout(tid)); + }; }, [rowDataMap, columnInfo]); // getGridDataRef: 현재 활성 탭 그리드 데이터 반환 함수 할당 @@ -1096,6 +1196,12 @@ const TabDataGrid = ({ const onSortingHandler = createOnSortingHandler(columns, setRowData, sortDirRef, originalRowsRef, findGridElement); (gridView as { onSorting?: (g: unknown, f: unknown[], d: unknown[]) => boolean }).onSorting = enableSort ? onSortingHandler : () => false; + + if (cellStyleCallbackRef.current) { + setTimeout(() => { + attachCellStyleCallbacksForTab(tabKey, columns); + }, 100); + } }, 150); } } else { diff --git a/apps/st/src/components/statistics/input/SewagePipeMaintainInp.tsx b/apps/st/src/components/statistics/input/SewagePipeMaintainInp.tsx index c34d35498..cddd6cc84 100644 --- a/apps/st/src/components/statistics/input/SewagePipeMaintainInp.tsx +++ b/apps/st/src/components/statistics/input/SewagePipeMaintainInp.tsx @@ -1,17 +1,45 @@ -import { useState } from "react"; +import { useRef, useState } from "react"; + +import { useAlertStore } from "@/shared/lib"; +import { statisticsInputSewSupplyRateApi } from "@/entities/statistics/api/input/statistics-input-sew-supply-rate-service"; +import { sewagePipeMaintainApiService } from "@/entities/statistics/api/input/statistics-input-sewage-pipe-maintain-service"; +import type { SewagePipeMaintainRequestParams } from "@/entities/statistics/model/input/statistics-input-sewage-pipe-maintain-interface"; +import { LoadingWithAnimation } from "@common/components"; -import SearchForm from "@/components/module/SearchForm"; -import { TabDataGrid } from '@rsone/components'; -import SubmitButton from "@/components/button/SumitButton"; -import SaveButton from "@/components/button/SaveButton"; import LastYearDataButton from "@/components/button/LastYearDataButton"; +import SaveButton from "@/components/button/SaveButton"; import SubmitExceptionButton from "@/components/button/SubmitExceptionButton"; - -import { RepairStatusInpColumn } from "@/components/util/column/input/sewagePipeMaintain/RepairStatusInpColumn"; +import SubmitButton from "@/components/button/SumitButton"; +import SearchForm from "@/components/module/SearchForm"; +import TabDataGrid from "@/components/module/TabDataGrid"; +import type { SearchFormValues } from "@/components/type/SearchForm.type"; import { PipeDredgingStatusInpColumn } from "@/components/util/column/input/sewagePipeMaintain/PipeDredgingStatusInpColumn"; +import { RepairStatusInpColumn } from "@/components/util/column/input/sewagePipeMaintain/RepairStatusInpColumn"; +import { toGridRows } from "@/components/util/grid/gridFunction"; + +function buildSewagePipeMaintainSearchParam( + values: SearchFormValues, + overrides?: { mode?: string }, +): SewagePipeMaintainRequestParams { + return { + statsYr: values.year, + sewerBzmnCd: values.bsnmCd, + ctpvNm: values.sido, + sggNm: values.gugun === "ALL" ? "" : values.gugun, + ...(overrides?.mode ? { mode: overrides.mode } : {}), + }; +} // 하수도 통계 관리 > 03. 하수관로 유지관리 const SewagePipeMaintainInp = () => { + const { setMessage: alert } = useAlertStore((state) => state); + const [isLoading, setIsLoading] = useState(false); + const [searchValues, setSearchValues] = useState(null); + const [activeTab, setActiveTab] = useState(RepairStatusInpColumn.tabKey); + const [lastSearchedValues, setLastSearchedValues] = useState(null); + const [lastSearchedYear, setLastSearchedYear] = useState(null); + const [hasSearched, setHasSearched] = useState(false); + const searchParamRef = useRef(null); // tab List const SewagePipeMaintainInpTabList = [ @@ -24,9 +52,69 @@ const SewagePipeMaintainInp = () => { // 예외 제출 로직 }; - const getLastYearDataFunc = () => { - console.log('get last year data'); - // 지난연도 데이터 가져오기 로직 + const loadActiveTabData = async ( + tabKey: string, + param: SewagePipeMaintainRequestParams, + append: boolean, + ): Promise => { + if (tabKey === RepairStatusInpColumn.tabKey) { + const response = await sewagePipeMaintainApiService.getSewagePipeMaintainRepair(param); + if (response.status !== "success") { + alert(response.message || "데이터 조회 중 오류가 발생했습니다."); + return; + } + const commonInfo = response.data.commonInfo; + const convertedData = (response.data.repairStatusList ?? []).map((d) => ({ + ctpvNm: commonInfo.ctpvNm, + ...d, + })); + setRowDataMap((prev) => { + const next = append ? new Map(prev) : new Map[]>(); + next.set(RepairStatusInpColumn.tabKey, toGridRows(convertedData)); + return next; + }); + return; + } + + if (tabKey === PipeDredgingStatusInpColumn.tabKey) { + const response = await sewagePipeMaintainApiService.getSewagePipeMaintainDredging(param); + if (response.status !== "success") { + alert(response.message || "데이터 조회 중 오류가 발생했습니다."); + return; + } + const commonInfo = response.data.commonInfo; + const convertedData = (response.data.pipeDredgingStatusList ?? []).map((d) => ({ + ctpvNm: commonInfo.ctpvNm, + ...d, + })); + setRowDataMap((prev) => { + const next = append ? new Map(prev) : new Map[]>(); + next.set(PipeDredgingStatusInpColumn.tabKey, toGridRows(convertedData)); + return next; + }); + } + }; + + const getLastYearDataFunc = async () => { + const values = lastSearchedValues ?? searchValues; + if (!values?.sido || values.sido === "ALL") { + alert("시도를 선택해주세요."); + return; + } + if (!lastSearchedYear) { + alert("먼저 데이터를 조회해주세요."); + return; + } + + try { + setIsLoading(true); + const param = buildSewagePipeMaintainSearchParam(values, { mode: "PRE" }); + await loadActiveTabData(activeTab, param, true); + } catch { + alert("데이터 조회 중 오류가 발생했습니다."); + } finally { + setIsLoading(false); + } }; const submitFunc = () => { @@ -40,22 +128,83 @@ const SewagePipeMaintainInp = () => { }; const [rowDataMap, setRowDataMap] = useState[]>>(new Map()); + + const handleReset = () => { + setRowDataMap(new Map()); + setActiveTab(RepairStatusInpColumn.tabKey); + setLastSearchedValues(null); + setLastSearchedYear(null); + setHasSearched(false); + searchParamRef.current = null; + }; + + const handleSearch = async (values: SearchFormValues) => { + if (!values.sido || values.sido === "ALL") { + alert("시도를 선택해주세요."); + return; + } + + try { + const periodResponse = await statisticsInputSewSupplyRateApi.checkInputPeriod(values.year || ""); + if (periodResponse.status !== "success") { + alert(periodResponse.message); + return; + } + + setIsLoading(true); + const searchParam = buildSewagePipeMaintainSearchParam(values); + searchParamRef.current = searchParam; + setLastSearchedValues(values); + setLastSearchedYear(values.year ?? null); + setHasSearched(true); + await loadActiveTabData(activeTab, searchParam, false); + } catch { + alert("데이터 조회 중 오류가 발생했습니다."); + } finally { + setIsLoading(false); + } + }; + + const handleActiveTabChange = async (tabKey: string) => { + setActiveTab(tabKey); + const hasRows = (rowDataMap.get(tabKey) ?? []).length > 0; + if (hasRows || !searchParamRef.current) return; + try { + setIsLoading(true); + await loadActiveTabData(tabKey, searchParamRef.current, true); + } catch { + alert("데이터 조회 중 오류가 발생했습니다."); + } finally { + setIsLoading(false); + } + }; + + const hasRowData = Array.from(rowDataMap.values()).some((rows) => rows.length > 0); return ( <> - -
-
- - - - + + {(hasRowData || hasSearched) && ( +
+
+ + void getLastYearDataFunc()} /> + + +
-
+ )} + {isLoading &&
+
+ +
+
} ); diff --git a/apps/st/src/components/statistics/management/LogHistory.tsx b/apps/st/src/components/statistics/management/LogHistory.tsx index 07ae69d5d..8614b0b5d 100644 --- a/apps/st/src/components/statistics/management/LogHistory.tsx +++ b/apps/st/src/components/statistics/management/LogHistory.tsx @@ -1,17 +1,20 @@ "use client"; -import { useCallback, useState } from "react"; +import { useCallback, useEffect, useState } from "react"; -import { Button, Label, LoadingWithAnimation } from "@common/components"; -import DataGrid from "@/components/module/DataGrid"; import { CodeSelectWithAll } from "@/entities/code"; - import { statisticsAPi } from "@/entities/statistics/api/statistics-service"; -import type { StatisticsLogRequest } from "@/entities/statistics/model/statistics-interface"; -import { toGridRows } from "@/components/util/grid/gridFunction"; +import type { + StatisticsLog, + StatisticsLogRequest, +} from "@/entities/statistics/model/statistics-interface"; +import { Button, Label, LoadingWithAnimation } from "@common/components"; -import { LogHistoryColumn } from "@/components/util/column/management/LogHistoryColumn"; +import DataGrid from "@/components/module/DataGrid"; +import { LogHistoryDetailDialog } from "@/components/statistics/management/LogHistoryDetailDialog"; import { LogHistoryTablePagination } from "@/components/statistics/management/LogHistoryTablePagination"; +import { LogHistoryColumn } from "@/components/util/column/management/LogHistoryColumn"; +import { toGridRows } from "@/components/util/grid/gridFunction"; const PAGE_SIZE = 100; @@ -24,6 +27,11 @@ const LogHistory = () => { ); const [pageIndex, setPageIndex] = useState(0); const [totalPages, setTotalPages] = useState(0); + const [detailDialogOpen, setDetailDialogOpen] = useState(false); + const [selectedLogRow, setSelectedLogRow] = useState | null>(null); const loadLogs = useCallback( async (page1Based: number) => { @@ -33,25 +41,30 @@ const LogHistory = () => { size: PAGE_SIZE, }; - try { - setIsLoading(true); + setIsLoading(true); - const response = await statisticsAPi.getLogList(param); + const response = await statisticsAPi.getLogList(param); + if (response.status === "success") { + const lostList = response.data?.statsLogList ?? []; + const convertedData: StatisticsLog[] = lostList.map( + (item: StatisticsLog) => ({ + ...item, + dmndFwkNm: + item.dmndFwkNm === "save" + ? "저장" + : item.dmndFwkNm === "submit" + ? "제출" + : "예외 제출", + }), + ); - if (response.status === "success") { - setRowData(toGridRows(response.data?.statsLogList ?? [])); - setTotalPages(response.data?.pageInfo?.totalPages ?? 0); - setPageIndex(Math.max(0, page1Based - 1)); - } else { - alert( - response.message || - "데이터 조회 중 오류가 발생했습니다.", - ); - } - } catch { - alert("데이터 조회 중 오류가 발생했습니다."); - } finally { - setIsLoading(false); + setRowData(toGridRows(convertedData)); + setTotalPages(response.data?.pageInfo?.totalPages ?? 0); + setPageIndex(Math.max(0, page1Based - 1)); + } else { + alert( + response.message || "데이터 조회 중 오류가 발생했습니다.", + ); } }, [statsCd], @@ -62,6 +75,20 @@ const LogHistory = () => { await loadLogs(1); }; + const handleRowClick = (row: Record) => { + setSelectedLogRow(row); + setDetailDialogOpen(true); + }; + + const handleDetailDialogOpenChange = (next: boolean) => { + setDetailDialogOpen(next); + if (!next) setSelectedLogRow(null); + }; + + useEffect(() => { + loadLogs(1); + }, []); + return (
@@ -107,6 +134,12 @@ const LogHistory = () => { isExcelImport={false} rowData={rowData} setRowData={() => {}} + onRowClick={handleRowClick} + /> + void; + row: Record | null; +} + +export function LogHistoryDetailDialog({ + open, + onOpenChange, + row, +}: LogHistoryDetailDialogProps) { + useEffect(() => { + if (!open || !row) return; + + const sn = row.statsLogSn; + void (async () => { + const response = await statisticsAPi.getLogDetail( + sn !== undefined && sn !== null ? String(sn) : "", + ); + console.log(response.data); + })(); + }, [open, row]); + + return ( + + + + + + + + + + + + + ); +} diff --git a/apps/st/src/components/statistics/management/LogHistoryTablePagination.tsx b/apps/st/src/components/statistics/management/LogHistoryTablePagination.tsx index 88c589e01..ddcc34531 100644 --- a/apps/st/src/components/statistics/management/LogHistoryTablePagination.tsx +++ b/apps/st/src/components/statistics/management/LogHistoryTablePagination.tsx @@ -43,7 +43,7 @@ export function LogHistoryTablePagination({ const canNext = pageCount > 0 && currentPage < pageCount - 1; return ( -
+
+ +
+
+
+ +
+
+ +
+
+ + + +
+
+
+ + handleSubFilterChange("status", value) + } + options={STATUS_OPTIONS} + /> + + handleSubFilterChange("approvalStatus", value) + } + options={APPROVAL_STATUS_OPTIONS} + /> +
+
+ + +
+
+
+ + + + {isLoading && ( +
+
+ +
+
+ )} +
+ ); +} + +interface FilterSelectProps { + label: string; + onChange: (value: string) => void; + options: Array<{ label: string; value: string }>; + value: string; +} + +function FilterSelect({ label, onChange, options, value }: FilterSelectProps) { + return ( +
+ + +
+ ); +} + +export { StatisticsVerificationReport }; +export default StatisticsVerificationReport; diff --git a/apps/st/src/components/type/DataGrid.type.ts b/apps/st/src/components/type/DataGrid.type.ts index 9c724b94e..befcbe0fc 100644 --- a/apps/st/src/components/type/DataGrid.type.ts +++ b/apps/st/src/components/type/DataGrid.type.ts @@ -1,5 +1,7 @@ import React from "react"; + import { ValueType } from "realgrid"; + import { type SewerageSupplyRateStatusInfo } from "@/entities/statistics/model/input/statistics-sewage-supply-rate-input-interface"; // RealGrid 타입 정의 @@ -287,6 +289,8 @@ export type TabDataGridProps = { enableSort?: boolean; /** 분할 탭 상단 시설 조회 다이얼로그용 — 상위 화면 검색의 통계연도(searchParam.statsYr) */ facilitySearchStatsYr?: string | null; + /** 일반 탭 그리드(비분할) 셀 styleName — DataGrid의 cellStyleCallback과 동일 */ + cellStyleCallback?: CellStyleCallback; }; export type SplitDataGridProps = { diff --git a/apps/st/src/components/util/column/input/sewagePipeMaintain/PipeDredgingStatusInpColumn.ts b/apps/st/src/components/util/column/input/sewagePipeMaintain/PipeDredgingStatusInpColumn.ts index 983df126e..159afa4ee 100644 --- a/apps/st/src/components/util/column/input/sewagePipeMaintain/PipeDredgingStatusInpColumn.ts +++ b/apps/st/src/components/util/column/input/sewagePipeMaintain/PipeDredgingStatusInpColumn.ts @@ -1,52 +1,54 @@ -import { footerInt, footerDecimal1, footerDecimal2 } from "@/components/util/grid/footerSummary"; import { ValueType } from "realgrid"; +import { footerDecimal1, footerDecimal2,footerInt } from "@/components/util/grid/footerSummary"; + // 하수도 통계 관리 > 03.하수관로 유지관리 > tab 2. 관로준설 export const PipeDredgingStatusInpColumn = { columns: [ // 행정구역 - { name: 'SIDO', fieldName: '시도', header: { text: '시도' }, editable: false }, - { name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' }, editable: false, footer: { text: '합계' } }, + { name: 'admdstCd', fieldName: 'admdstCd', header: { text: '행정구역코드' }, editable: false }, + { name: 'sggNm', fieldName: 'sggNm', header: { text: '시군구명' }, editable: false, footer: { text: '합계' } }, + { name: 'admdstNm', fieldName: 'admdstNm', header: { text: '행정구역명' }, editable: false }, // 하수관로 총 시설연장 - { name: 'PIPL_FCLTY_EXTN', fieldName: '하수관로총시설연장(m)', header: { text: '하수관로총시설연장(m)' }, editable: false, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'wholFcltPrlg', fieldName: 'wholFcltPrlg', header: { text: '하수관로총시설연장(m)' }, editable: false, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, // 준설연장 및 개소수 > 관로(m) - { name: 'DRDG_PIPE_SM', fieldName: '합계', header: { text: '합계' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, - { name: 'DRDG_CMBN_PIPE', fieldName: '합류식', header: { text: '합류식' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'wholSwagePpl', fieldName: 'wholSwagePpl', header: { text: '합계' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'cbsysPplPrlg', fieldName: 'cbsysPplPrlg', header: { text: '합류식' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, // 준설연장 및 개소수 > 관로(m) > 분류식 - { name: 'DRDG_RAINWTR_PIPE', fieldName: '우수', header: { text: '우수' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, - { name: 'DRDG_PLTWTR_PIPE', fieldName: '오수', header: { text: '오수' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'sprsysExlnPrlg', fieldName: 'sprsysExlnPrlg', header: { text: '우수' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'sprsysDrtwPrlg', fieldName: 'sprsysDrtwPrlg', header: { text: '오수' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, // 준설연장 및 개소수 > 맨홀, 오·우수받이, 토실·토구 - { name: 'MNHLE_SITE', fieldName: '맨홀(개소)', header: { text: '맨홀(개소)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, - { name: 'PLTWTR_RCVR_SITE', fieldName: '오·우수받이(개소)', header: { text: '오·우수받이(개소)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, - { name: 'SOILSPLL_SOILHOLE_SITE', fieldName: '토실·토구(개소)', header: { text: '토실·토구(개소)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'mnhlCnt', fieldName: 'mnhlCnt', header: { text: '맨홀(개소)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'exlnDrtwRcvprpCnt', fieldName: 'exlnDrtwRcvprpCnt', header: { text: '오·우수받이(개소)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'stofcbrOtflCnt', fieldName: 'stofcbrOtflCnt', header: { text: '토실·토구(개소)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, // 계획 & 비율 - { name: 'DRDG_TRGET_EXTN', fieldName: '준설 계획 연장(m)', header: { text: '준설 계획 연장(m)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, - { name: 'DRDG_TRGET_RT', fieldName: '계획대비 준설율(%)', header: { text: '계획대비 준설율(%)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, - { name: 'PIPL_FCLTY_RT', fieldName: '총시설연장대비 준설율(%)', header: { text: '총시설연장대비 준설율(%)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'drdgnPlanPrlg', fieldName: 'drdgnPlanPrlg', header: { text: '준설 계획 연장(m)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'drdgnTrgtCtrsDrdgnRt', fieldName: 'drdgnTrgtCtrsDrdgnRt', header: { text: '계획대비 준설율(%)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'wholPrlgCtrsDrdgnRt', fieldName: 'wholPrlgCtrsDrdgnRt', header: { text: '총시설연장대비 준설율(%)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, // 준설실적 - { name: 'DRDG_ACMSLT_AMOUNT', fieldName: '금액(백만원)', header: { text: '금액(백만원)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, - { name: 'DRDG_ACMSLT_QY', fieldName: '준설량(m³)', header: { text: '준설량(m³)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'drdgnAmt', fieldName: 'drdgnAmt', header: { text: '금액(백만원)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'drdgnQnt', fieldName: 'drdgnQnt', header: { text: '준설량(m³)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, // 준설토사처리 - { name: 'DRDG_ACMSLT_UNTPC', fieldName: '준설토처리비(백만원)', header: { text: '준설토처리비(백만원)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, - { name: 'DRDG_ETND_SM', fieldName: '처리량 계(톤)', header: { text: '처리량 계(톤)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, - { name: 'RCLM', fieldName: '매립', header: { text: '매립' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, - { name: 'OCEAN_DPSL', fieldName: '해양투기', header: { text: '해양투기' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, - { name: 'RUSE', fieldName: '재활용', header: { text: '재활용' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, - { name: 'ETC', fieldName: '기타(소각, 고형화, 시멘트원료 등)', header: { text: '기타(소각, 고형화, 시멘트원료 등)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'drdgnUntprcAmt', fieldName: 'drdgnUntprcAmt', header: { text: '준설토처리비(백만원)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'wholDrdgnPrcsQnt', fieldName: 'wholDrdgnPrcsQnt', header: { text: '처리량 계(톤)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'rclmQnt', fieldName: 'rclmQnt', header: { text: '매립' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'oceanEmsnQnt', fieldName: 'oceanEmsnQnt', header: { text: '해양투기' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'rcycQnt', fieldName: 'rcycQnt', header: { text: '재활용' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'etcQnt', fieldName: 'etcQnt', header: { text: '기타(소각, 고형화, 시멘트원료 등)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, // 비고 - { name: 'RMK', fieldName: '비고', header: { text: '비고' } }, + { name: 'rmrk', fieldName: 'rmrk', header: { text: '비고' } }, ], columnLayout: [ - { name: 'group_admin', header: { text: '행정구역' }, items: ['SIDO', 'GUGUN'] }, - 'PIPL_FCLTY_EXTN', + { name: 'group_admin', header: { text: '행정구역' }, items: ['admdstCd', 'sggNm', 'admdstNm'] }, + 'wholFcltPrlg', { name: 'group_dredge_info', header: { text: '준설연장 및 개소수' }, @@ -55,28 +57,28 @@ export const PipeDredgingStatusInpColumn = { name: 'group_pipe', header: { text: '관로(m)' }, items: [ - 'DRDG_PIPE_SM', 'DRDG_CMBN_PIPE', - { name: 'group_sep', header: { text: '분류식' }, items: ['DRDG_RAINWTR_PIPE', 'DRDG_PLTWTR_PIPE'] } + 'wholSwagePpl', 'cbsysPplPrlg', + { name: 'group_sep', header: { text: '분류식' }, items: ['sprsysExlnPrlg', 'sprsysDrtwPrlg'] } ] }, - 'MNHLE_SITE', 'PLTWTR_RCVR_SITE', 'SOILSPLL_SOILHOLE_SITE' + 'mnhlCnt', 'exlnDrtwRcvprpCnt', 'stofcbrOtflCnt' ] }, - 'DRDG_TRGET_EXTN', 'DRDG_TRGET_RT', 'PIPL_FCLTY_RT', + 'drdgnPlanPrlg', 'drdgnTrgtCtrsDrdgnRt', 'wholPrlgCtrsDrdgnRt', { name: 'group_perf', header: { text: '준설실적' }, - items: ['DRDG_ACMSLT_AMOUNT', 'DRDG_ACMSLT_QY'] + items: ['drdgnAmt', 'drdgnQnt'] }, { name: 'group_soil_treat', header: { text: '준설토사처리' }, - items: ['DRDG_ACMSLT_UNTPC', 'DRDG_ETND_SM', 'RCLM', 'OCEAN_DPSL', 'RUSE', 'ETC'] + items: ['drdgnUntprcAmt', 'wholDrdgnPrcsQnt', 'rclmQnt', 'oceanEmsnQnt', 'rcycQnt', 'etcQnt'] }, - 'RMK' + 'rmrk' ], rowStartNum: 4, fixedColumns: 1, - tabKey: 'pipeDredgingStatusInp', + tabKey: 'ST00030002', tabLabel: '관로준설' }; \ No newline at end of file diff --git a/apps/st/src/components/util/column/input/sewagePipeMaintain/RepairStatusInpColumn.ts b/apps/st/src/components/util/column/input/sewagePipeMaintain/RepairStatusInpColumn.ts index 5f22b373f..05ec204ba 100644 --- a/apps/st/src/components/util/column/input/sewagePipeMaintain/RepairStatusInpColumn.ts +++ b/apps/st/src/components/util/column/input/sewagePipeMaintain/RepairStatusInpColumn.ts @@ -5,51 +5,52 @@ import { ValueType } from "realgrid"; export const RepairStatusInpColumn = { columns: [ // 행정구역 - { name: 'SIDO', fieldName: '시도', header: { text: '시도' }, editable: false }, - { name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' }, editable: false, footer: { text: '합계' } }, + { name: 'admdstCd', fieldName: 'admdstCd', header: { text: '행정구역코드' }, editable: false }, + { name: 'sggNm', fieldName: 'sggNm', header: { text: '시군구명' }, editable: false, footer: { text: '합계' } }, + { name: 'admdstNm', fieldName: 'admdstNm', header: { text: '행정구역명' }, editable: false }, // 개·보수관로 > 전체보수(m) - { name: 'RPAIR_PIPE_SM', fieldName: '소계', header: { text: '소계' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, - { name: 'RPAIR_CMBN_PIPE', fieldName: '합류식', header: { text: '합류식' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'mendngWhol', fieldName: 'mendngWhol', header: { text: '소계' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'cbsysMendngPrlg', fieldName: 'cbsysMendngPrlg', header: { text: '합류식' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, // 개·보수관로 > 전체보수(m) > 분류식 - { name: 'RPAIR_RAINWTR_PIPE', fieldName: '우수', header: { text: '우수' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, - { name: 'RPAIR_PLTWTR_PIPE', fieldName: '오수', header: { text: '오수' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'sprsysExlnPrlg', fieldName: 'sprsysExlnPrlg', header: { text: '우수' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'sprsysDrtwPrlg', fieldName: 'sprsysDrtwPrlg', header: { text: '오수' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, // 개·보수관로 > 부분보수(개소) - { name: 'RPAIR_PIPE_SM2', fieldName: '소계', header: { text: '소계' }, editable: false, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, - { name: 'RPAIR_CMBN_PIPE2', fieldName: '합류식', header: { text: '합류식' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'partMendngWhol', fieldName: 'partMendngWhol', header: { text: '소계' }, editable: false, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'cbsysPartMendngPrlg', fieldName: 'cbsysPartMendngPrlg', header: { text: '합류식' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, // 개·보수관로 > 부분보수(개소) > 분류식 - { name: 'RPAIR_RAINWTR_PIPE2', fieldName: '우수', header: { text: '우수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, - { name: 'RPAIR_PLTWTR_PIPE2', fieldName: '오수', header: { text: '오수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'sprsysExlnMendngPrlg', fieldName: 'sprsysExlnMendngPrlg', header: { text: '우수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'sprsysDrtwMendngPrlg', fieldName: 'sprsysDrtwMendngPrlg', header: { text: '오수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, // 맨홀(개소) - { name: 'MNHLE_SITE', fieldName: '합류식맨홀', header: { text: '합류식맨홀' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'cbsysMnhlCnt', fieldName: 'cbsysMnhlCnt', header: { text: '합류식맨홀' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, // 맨홀(개소) > 분류식 - { name: 'PLTWTR_MNHLE_SITE', fieldName: '오수맨홀', header: { text: '오수맨홀' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, - { name: 'RAINWTR_MNHLE_SITE', fieldName: '우수맨홀', header: { text: '우수맨홀' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'sprsysDrtwMnhlCnt', fieldName: 'sprsysDrtwMnhlCnt', header: { text: '오수맨홀' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'sprsysExlnMnhlCnt', fieldName: 'sprsysExlnMnhlCnt', header: { text: '우수맨홀' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, // 받이 - { name: 'PLTWTR_RCVR_SITE', fieldName: '오수받이(개소)', header: { text: '오수받이(개소)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, - { name: 'RAINWTR_RCVR_SITE', fieldName: '빗물받이(개소)', header: { text: '빗물받이(개소)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'drtwRcvprpCnt', fieldName: 'drtwRcvprpCnt', header: { text: '오수받이(개소)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'exlnRcvprpCnt', fieldName: 'exlnRcvprpCnt', header: { text: '빗물받이(개소)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, // 토실, 토구 - { name: 'SOILSPLL_SITE', fieldName: '토실(개소)', header: { text: '토실(개소)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, - { name: 'SOILHOLE_SITE', fieldName: '토구(개소)', header: { text: '토구(개소)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'stofcbrCnt', fieldName: 'stofcbrCnt', header: { text: '토실(개소)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'otflCnt', fieldName: 'otflCnt', header: { text: '토구(개소)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, // 수밀검사CCTV조사량(m) - { name: 'CCTV', fieldName: '수밀검사CCTV조사량(m)', header: { text: '수밀검사CCTV조사량(m)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'cctvExmnPrlg', fieldName: 'cctvExmnPrlg', header: { text: '수밀검사·CCTV조사량(m)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, // 비고 - { name: 'RMK', fieldName: '비고', header: { text: '비고' } }, + { name: 'rmrk', fieldName: 'rmrk', header: { text: '비고' } }, ], columnLayout: [ { name: 'group_admin', header: { text: '행정구역' }, - items: ['SIDO', 'GUGUN'] + items: ['admdstCd', 'sggNm', 'admdstNm'] }, { name: 'group_repair_main', @@ -59,12 +60,12 @@ export const RepairStatusInpColumn = { name: 'group_full_repair', header: { text: '전체보수(m)' }, items: [ - 'RPAIR_PIPE_SM', - 'RPAIR_CMBN_PIPE', + 'mendngWhol', + 'cbsysMendngPrlg', { name: 'group_full_sep', header: { text: '분류식' }, - items: ['RPAIR_RAINWTR_PIPE', 'RPAIR_PLTWTR_PIPE'] + items: ['sprsysExlnPrlg', 'sprsysDrtwPrlg'] } ] }, @@ -72,12 +73,12 @@ export const RepairStatusInpColumn = { name: 'group_part_repair', header: { text: '부분보수(개소)' }, items: [ - 'RPAIR_PIPE_SM2', - 'RPAIR_CMBN_PIPE2', + 'partMendngWhol', + 'cbsysPartMendngPrlg', { name: 'group_part_sep', header: { text: '분류식' }, - items: ['RPAIR_RAINWTR_PIPE2', 'RPAIR_PLTWTR_PIPE2'] + items: ['sprsysExlnMendngPrlg', 'sprsysDrtwMendngPrlg'] } ] } @@ -87,29 +88,29 @@ export const RepairStatusInpColumn = { name: 'group_manhole', header: { text: '맨홀(개소)' }, items: [ - 'MNHLE_SITE', + 'cbsysMnhlCnt', { name: 'group_mh_sep', header: { text: '분류식' }, - items: ['PLTWTR_MNHLE_SITE', 'RAINWTR_MNHLE_SITE'] + items: ['sprsysDrtwMnhlCnt', 'sprsysExlnMnhlCnt'] } ] }, { name: 'group_inlet', header: { text: '받이' }, - items: ['PLTWTR_RCVR_SITE', 'RAINWTR_RCVR_SITE'] + items: ['drtwRcvprpCnt', 'exlnRcvprpCnt'] }, { name: 'group_chamber', header: { text: '토실,토구' }, - items: ['SOILSPLL_SITE', 'SOILHOLE_SITE'] + items: ['stofcbrCnt', 'otflCnt'] }, - 'CCTV', - 'RMK' + 'cctvExmnPrlg', + 'rmrk' ], rowStartNum: 4, fixedColumns: 1, - tabKey: 'repairStatusInp', + tabKey: 'ST00030001', tabLabel: '개보수' }; \ No newline at end of file diff --git a/apps/st/src/components/util/column/management/LogHistoryColumn.ts b/apps/st/src/components/util/column/management/LogHistoryColumn.ts index 74efa78b4..04cd457f9 100644 --- a/apps/st/src/components/util/column/management/LogHistoryColumn.ts +++ b/apps/st/src/components/util/column/management/LogHistoryColumn.ts @@ -6,7 +6,7 @@ export const LogHistoryColumn = { { name: 'menuNm', fieldName: 'menuNm', header: { text: '메뉴명' }, editable: false }, { name: 'tblNm', fieldName: 'tblNm', header: { text: '테이블명' }, visible: false, editable: false }, { name: 'dmndFwkNm', fieldName: 'dmndFwkNm', header: { text: '요청 기능명' }, editable: false }, - { name: 'rgtrId', fieldName: 'rgtrId', header: { text: '등록자' }, editable: false }, + { name: 'rgtrId', fieldName: 'rgtrId', header: { text: '등록자' }, editable: false, visible: false }, { name: 'regDt', fieldName: 'regDt', header: { text: '등록일' }, editable: false }, ], diff --git a/apps/st/src/components/util/column/review/SewagePipeRvwColumn.ts b/apps/st/src/components/util/column/review/SewagePipeRvwColumn.ts index 0178b60e9..d41713526 100644 --- a/apps/st/src/components/util/column/review/SewagePipeRvwColumn.ts +++ b/apps/st/src/components/util/column/review/SewagePipeRvwColumn.ts @@ -1,7 +1,13 @@ -import { ValueType } from "realgrid"; import dayjs from 'dayjs'; -import { footerIntArrayByDivision, footerDecimal3ArrayByDivision, footerAvgDecimalArrayByDivision } from "@/components/util/grid/footerSummary"; +import { ValueType } from "realgrid"; + import type { GridView } from "@/components/type/DataGrid.type"; +import { + footerAvgDecimalArrayByDivision, + footerDecimal1ArrayByDivision, + footerDecimal3ArrayByDivision, + footerIntArrayByDivision, +} from "@/components/util/grid/footerSummary"; const currentYear = dayjs().year() - 1; @@ -258,7 +264,7 @@ const rawList = [ { name: 'prcsZoneArea', fieldName: 'prcsZoneArea', header: { text: '합계' }, footer: footerDecimal3ArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0.000' }, { name: 'cbsysPrcsZoneArea', fieldName: 'cbsysPrcsZoneArea', header: { text: '합류식' }, footer: footerDecimal3ArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0.000' }, { name: 'sprsysPrcsZoneArea', fieldName: 'sprsysPrcsZoneArea', header: { text: '분류식' }, footer: footerDecimal3ArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0.000' }, - { name: 'sprsysVartnRt', fieldName: 'sprsysVartnRt', header: { text: '분류식화' }, footer: footerDecimal3ArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0.000' }, + { name: 'sprsysVartnRt', fieldName: 'sprsysVartnRt', header: { text: '분류식화율' }, footer: footerDecimal3ArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0.000' }, { name: 'mltvEqpmCnt', fieldName: 'mltvEqpmCnt', header: { text: '합계' }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, { name: 'cbsysMltvEqpmCnt', fieldName: 'cbsysMltvEqpmCnt', header: { text: '합류식' }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, { name: 'sprsysMltvEqpmCnt', fieldName: 'sprsysMltvEqpmCnt', header: { text: '분류식 합계' }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, @@ -386,9 +392,9 @@ const rawList = [ ] }, - { name: 'pipeByPrstWholSum', fieldName: 'pipeByPrstWhol', header: { text: '계' }, footer: footerAvgDecimalArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, - { name: 'lyngPrlgSum', fieldName: 'lyngPrlg', header: { text: '매설연장(m)' }, footer: footerAvgDecimalArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, - { name: 'dmltnPrlgSum', fieldName: 'dmltnPrlg', header: { text: '철거연장(m)' }, footer: footerAvgDecimalArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'pipeByPrstWhol', fieldName: 'pipeByPrstWhol', header: { text: '계' }, footer: footerDecimal1ArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'lyngPrlg', fieldName: 'lyngPrlg', header: { text: '매설연장(m)' }, footer: footerDecimal1ArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'dmltnPrlg', fieldName: 'dmltnPrlg', header: { text: '철거연장(m)' }, footer: footerDecimal1ArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, ...Array.from({ length: totalCount }, (_, i) => { @@ -399,38 +405,38 @@ const rawList = [ const dmltnField = `yy${dtoNum}DmltnPrlg`; return [ - { name: wholField, fieldName: wholField, header: { text: '계' }, footer: footerAvgDecimalArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, - { name: lyngField, fieldName: lyngField, header: { text: '매설연장(m)' }, footer: footerAvgDecimalArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, - { name: dmltnField, fieldName: dmltnField, header: { text: '철거연장(m)' }, footer: footerAvgDecimalArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' } + { name: wholField, fieldName: wholField, header: { text: '계' }, footer: footerDecimal1ArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: lyngField, fieldName: lyngField, header: { text: '매설연장(m)' }, footer: footerDecimal1ArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: dmltnField, fieldName: dmltnField, header: { text: '철거연장(m)' }, footer: footerDecimal1ArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' } ]; }).flat(), { name: 'rmrk', fieldName: 'rmrk', header: { text: '비고' } }, ], columnLayout: [ - { - name: 'areaGroup', - header: { text: '행정구역' }, - items: [{ column: 'ctpvNm', footerUserSpans: [{ colspan: 4 }, { colspan: 4 }] }, - 'sggNm', 'swrgPipeNm' - ] }, - 'displayYear', - { name: 'summaryGroup', header: { text: '총계' }, items: ['pipeByPrstWholSum', 'lyngPrlgSum', 'dmltnPrlgSum'] }, + { + name: "areaGroup", + header: { text: "행정구역" }, + items: [ + { column: "ctpvNm", footerUserSpans: [{ colspan: 3 }, { colspan: 3 }] }, + "sggNm", + "swrgPipeNm", + ], + }, + "displayYear", + { name: "summaryGroup", header: { text: "총계" }, items: ["pipeByPrstWhol", "lyngPrlg", "dmltnPrlg"] }, ...Array.from({ length: totalCount }, (_, i) => { - const dtoNum = totalCount - i; - const yearLabel = i === 0 - ? '1996년이전' - : `${startYear + (i - 1)}년`; + const yearLabel = i === 0 ? "1996년이전" : `${startYear + (i - 1)}년`; return { name: `group_${yearLabel}`, header: { text: yearLabel }, - items: [`yy${dtoNum}PrlgWhol`, `yy${dtoNum}LyngPrlg`, `yy${dtoNum}DmltnPrlg`] + items: [`yy${dtoNum}PrlgWhol`, `yy${dtoNum}LyngPrlg`, `yy${dtoNum}DmltnPrlg`], }; }), - 'rmrk' - ] + "rmrk", + ], }, { @@ -511,14 +517,14 @@ const rawList = [ }, /* 관경별 수치 필드 */ - { name: 'pdmtByTsmWhol', fieldName: 'pdmtByTsmWhol', header: { text: '총계(m)' }, footer: footerAvgDecimalArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, - { name: 'step1PdmtPrlg', fieldName: 'step1PdmtPrlg', header: { text: '200mm 미만' }, footer: footerAvgDecimalArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, - { name: 'step2PdmtPrlg', fieldName: 'step2PdmtPrlg', header: { text: '200mm이상~\n300mm미만' }, footer: footerAvgDecimalArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, - { name: 'step3PdmtPrlg', fieldName: 'step3PdmtPrlg', header: { text: '300mm이상~\n500mm미만' }, footer: footerAvgDecimalArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, - { name: 'step4PdmtPrlg', fieldName: 'step4PdmtPrlg', header: { text: '500mm이상~\n800mm미만' }, footer: footerAvgDecimalArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, - { name: 'step5PdmtPrlg', fieldName: 'step5PdmtPrlg', header: { text: '800mm 이상' }, footer: footerAvgDecimalArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, - { name: 'sqrePdmtPrlg', fieldName: 'sqrePdmtPrlg', header: { text: '사각형(m)' }, footer: footerAvgDecimalArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, - { name: 'etcPdmtPrlg', fieldName: 'etcPdmtPrlg', header: { text: '기타\n(측구/개거 등)' }, footer: footerAvgDecimalArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'pdmtByTsmWhol', fieldName: 'pdmtByTsmWhol', header: { text: '총계(m)' }, footer: footerDecimal1ArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'step1PdmtPrlg', fieldName: 'step1PdmtPrlg', header: { text: '200mm 미만' }, footer: footerDecimal1ArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'step2PdmtPrlg', fieldName: 'step2PdmtPrlg', header: { text: '200mm이상~\n300mm미만' }, footer: footerDecimal1ArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'step3PdmtPrlg', fieldName: 'step3PdmtPrlg', header: { text: '300mm이상~\n500mm미만' }, footer: footerDecimal1ArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'step4PdmtPrlg', fieldName: 'step4PdmtPrlg', header: { text: '500mm이상~\n800mm미만' }, footer: footerDecimal1ArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'step5PdmtPrlg', fieldName: 'step5PdmtPrlg', header: { text: '800mm 이상' }, footer: footerDecimal1ArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'sqrePdmtPrlg', fieldName: 'sqrePdmtPrlg', header: { text: '사각형(m)' }, footer: footerDecimal1ArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'etcPdmtPrlg', fieldName: 'etcPdmtPrlg', header: { text: '기타\n(측구/개거 등)' }, footer: footerDecimal1ArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, { name: 'rmrk', fieldName: 'rmrk', header: { text: '비고' } }, // rmk -> rmrk ], diff --git a/apps/st/src/components/util/column/review/SewageReservoirPumpRvwColumn.ts b/apps/st/src/components/util/column/review/SewageReservoirPumpRvwColumn.ts index d6bc43849..5cfd73070 100644 --- a/apps/st/src/components/util/column/review/SewageReservoirPumpRvwColumn.ts +++ b/apps/st/src/components/util/column/review/SewageReservoirPumpRvwColumn.ts @@ -1,8 +1,8 @@ import { ValueType } from "realgrid"; -import { - footerIntArrayByDivision, - footerDecimal1ArrayByDivision, - footerDecimal3ArrayByDivision +import { + footerIntArrayByDivision, + footerDecimal1ArrayByDivision, + footerDecimal3ArrayByDivision } from "@/components/util/grid/footerSummary"; import type { GridView } from "@/components/type/DataGrid.type"; @@ -56,10 +56,9 @@ const rawReservoirList = [ } ] }, - { name: 'sggNm', fieldName: 'sggNm', header: { text: '시군구' }, mergeCells: true, sortdisable: true }, - { name: 'admdstNm', fieldName: 'admdstNm', header: { text: '행정구역명' }, mergeCells: true }, - { name: 'impdFcltNm', fieldName: 'impdFcltNm', header: { text: '저류시설명' }, mergeCells: true, sortdisable: true }, - { name: 'impdFcltPstnCn', fieldName: 'impdFcltPstnCn', header: { text: '저류시설위치' }, mergeCells: true, sortdisable: true }, + { name: 'sggNm', fieldName: 'sggNm', header: { text: '시군구' }, mergeCells: true, sortdisable: true }, + { name: 'fcltNm', fieldName: 'fcltNm', header: { text: '저류시설명' }, mergeCells: true, sortdisable: true }, + { name: 'fcltPstnCn', fieldName: 'fcltPstnCn', header: { text: '저류시설위치' }, mergeCells: true, sortdisable: true }, { name: 'displayYear', fieldName: 'displayYear', @@ -101,7 +100,7 @@ const rawReservoirList = [ ] }, - /* 2. 시설 제원 */ + /* 2. 시설 제원 */ { name: 'oprtngStrtYmd', fieldName: 'oprtngStrtYmd', header: { text: '가동개시 년월일' } }, { name: 'planDesignFreqCnt', fieldName: 'planDesignFreqCnt', header: { text: '계획설계빈도' } }, { name: 'fldngPrvtnYn', fieldName: 'fldngPrvtnYn', header: { text: '침수예방' } }, @@ -117,12 +116,12 @@ const rawReservoirList = [ { name: 'pumpFcltN1PerPpqty', fieldName: 'pumpFcltN1PerPpqty', header: { text: '대당 양수량' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0', footer: footerDecimal1ArrayByDivision }, { name: 'pumpFcltN1PumpCnt', fieldName: 'pumpFcltN1PumpCnt', header: { text: '펌프대수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0', footer: footerIntArrayByDivision }, { name: 'pumpFcltN1RsrvPumpCnt', fieldName: 'pumpFcltN1RsrvPumpCnt', header: { text: '예비펌프대수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0', footer: footerIntArrayByDivision }, - + { name: 'pumpFcltN2DmtrExpln', fieldName: 'pumpFcltN2DmtrExpln', header: { text: '구경(mm)' } }, { name: 'pumpFcltN2PerPpqty', fieldName: 'pumpFcltN2PerPpqty', header: { text: '대당 양수량' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0', footer: footerDecimal1ArrayByDivision }, { name: 'pumpFcltN2PumpCnt', fieldName: 'pumpFcltN2PumpCnt', header: { text: '펌프대수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0', footer: footerIntArrayByDivision }, { name: 'pumpFcltN2RsrvPumpCnt', fieldName: 'pumpFcltN2RsrvPumpCnt', header: { text: '예비펌프대수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0', footer: footerIntArrayByDivision }, - + { name: 'pumpFcltN3DmtrExpln', fieldName: 'pumpFcltN3DmtrExpln', header: { text: '구경(mm)' } }, { name: 'pumpFcltN3PerPpqty', fieldName: 'pumpFcltN3PerPpqty', header: { text: '대당 양수량' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0', footer: footerDecimal1ArrayByDivision }, { name: 'pumpFcltN3PumpCnt', fieldName: 'pumpFcltN3PumpCnt', header: { text: '펌프대수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0', footer: footerIntArrayByDivision }, @@ -151,11 +150,11 @@ const rawReservoirList = [ columnLayout: [ { name: 'areaGroup', header: { text: '행정구역' }, items: [ - { column: 'ctpvNm', footerUserSpans: [{ colspan: 4 }, { colspan: 4 }] }, - 'sggNm','admdstNm' + { column: 'ctpvNm', footerUserSpans: [{ colspan: 5 }, { colspan: 5 }] }, + 'sggNm' ] }, - 'impdFcltNm', 'impdFcltPstnCn','displayYear', 'oprtngStrtYmd', 'planDesignFreqCnt', + 'fcltNm', 'fcltPstnCn', 'displayYear', 'oprtngStrtYmd', 'planDesignFreqCnt', { name: 'purposeGroup', header: { text: '설치목적' }, items: ['fldngPrvtnYn', 'wqPlltnMtgtYn', 'rtlztnUsgYn'] }, 'fcltStrctCn', 'wtrStrgZoneArea', 'impdFcltCpct', { @@ -175,167 +174,166 @@ const rawReservoirList = [ 'rmrk' ] }, -{ + { tabKey: 'ST00040002', tabLabel: '빗물펌프장', rowStartNum: 3, fixedColumns: 2, - columns: [ - { - name: 'ctpvNm', - fieldName: 'ctpvNm', - header: { text: '시도' }, - mergeCells: true, - sortdisable: true, - footer: [ - { - valueCallback: (grid: GridView) => { - const divisionIndex = 0; - const dataSource = grid.getDataSource(); - const rowCount = dataSource.getRowCount(); - const uniqueValues = new Set(); - for (let i = 0; i < rowCount; i++) { - const divisionValue = dataSource.getValue(i, 'displayYear'); - if (divisionValue !== null && divisionValue !== undefined && divisionValue !== '') { - uniqueValues.add(String(divisionValue)); + columns: [ + { + name: 'ctpvNm', + fieldName: 'ctpvNm', + header: { text: '시도' }, + mergeCells: true, + sortdisable: true, + footer: [ + { + valueCallback: (grid: GridView) => { + const divisionIndex = 0; + const dataSource = grid.getDataSource(); + const rowCount = dataSource.getRowCount(); + const uniqueValues = new Set(); + for (let i = 0; i < rowCount; i++) { + const divisionValue = dataSource.getValue(i, 'displayYear'); + if (divisionValue !== null && divisionValue !== undefined && divisionValue !== '') { + uniqueValues.add(String(divisionValue)); + } } + const divisionValues = Array.from(uniqueValues); + if (divisionValues.length > divisionIndex) return divisionValues[divisionIndex] + ' 합계'; + return '합계'; } - const divisionValues = Array.from(uniqueValues); - if (divisionValues.length > divisionIndex) return divisionValues[divisionIndex] + ' 합계'; - return '합계'; - } - }, - { - valueCallback: (grid: GridView) => { - const divisionIndex = 1; - const dataSource = grid.getDataSource(); - const rowCount = dataSource.getRowCount(); - const uniqueValues = new Set(); - for (let i = 0; i < rowCount; i++) { - const divisionValue = dataSource.getValue(i, 'displayYear'); - if (divisionValue !== null && divisionValue !== undefined && divisionValue !== '') { - uniqueValues.add(String(divisionValue)); + }, + { + valueCallback: (grid: GridView) => { + const divisionIndex = 1; + const dataSource = grid.getDataSource(); + const rowCount = dataSource.getRowCount(); + const uniqueValues = new Set(); + for (let i = 0; i < rowCount; i++) { + const divisionValue = dataSource.getValue(i, 'displayYear'); + if (divisionValue !== null && divisionValue !== undefined && divisionValue !== '') { + uniqueValues.add(String(divisionValue)); + } } + const divisionValues = Array.from(uniqueValues); + if (divisionValues.length > divisionIndex) return divisionValues[divisionIndex] + ' 합계'; + return '합계'; } - const divisionValues = Array.from(uniqueValues); - if (divisionValues.length > divisionIndex) return divisionValues[divisionIndex] + ' 합계'; - return '합계'; } - } - ] - }, - { name: 'sggNm', fieldName: 'sggNm', header: { text: '시군구' }, mergeCells: true, sortdisable: true }, - { name: 'admdstNm', fieldName: 'admdstNm', header: { text: '행정구역명' }, mergeCells: true }, - { name: 'impdFcltNm', fieldName: 'impdFcltNm', header: { text: '시설명' }, mergeCells: true, sortdisable: true }, - { name: 'impdFcltPstnCn',fieldName: 'impdFcltPstnCn',header: { text: '위치' }, mergeCells: true, sortdisable: true }, - { - name: 'displayYear', - fieldName: 'displayYear', - header: { text: '구분' }, - sortdisable: true, - footer: [ - { - valueCallback: (grid: GridView) => { - const divisionIndex = 0; - const dataSource = grid.getDataSource(); - const rowCount = dataSource.getRowCount(); - const uniqueValues = new Set(); - for (let i = 0; i < rowCount; i++) { - const divisionValue = dataSource.getValue(i, 'displayYear'); - if (divisionValue !== null && divisionValue !== undefined && divisionValue !== '') { - uniqueValues.add(String(divisionValue)); + ] + }, + { name: 'sggNm', fieldName: 'sggNm', header: { text: '시군구' }, mergeCells: true, sortdisable: true }, + { name: 'fcltNm', fieldName: 'fcltNm', header: { text: '시설명' }, mergeCells: true, sortdisable: true }, + { name: 'fcltPstnCn', fieldName: 'fcltPstnCn', header: { text: '위치' }, mergeCells: true, sortdisable: true }, + { + name: 'displayYear', + fieldName: 'displayYear', + header: { text: '구분' }, + sortdisable: true, + footer: [ + { + valueCallback: (grid: GridView) => { + const divisionIndex = 0; + const dataSource = grid.getDataSource(); + const rowCount = dataSource.getRowCount(); + const uniqueValues = new Set(); + for (let i = 0; i < rowCount; i++) { + const divisionValue = dataSource.getValue(i, 'displayYear'); + if (divisionValue !== null && divisionValue !== undefined && divisionValue !== '') { + uniqueValues.add(String(divisionValue)); + } } + const divisionValues = Array.from(uniqueValues); + return divisionValues.length > divisionIndex ? divisionValues[divisionIndex] + ' 합계' : ''; } - const divisionValues = Array.from(uniqueValues); - return divisionValues.length > divisionIndex ? divisionValues[divisionIndex] + ' 합계' : ''; - } - }, - { - valueCallback: (grid: GridView) => { - const divisionIndex = 1; - const dataSource = grid.getDataSource(); - const rowCount = dataSource.getRowCount(); - const uniqueValues = new Set(); - for (let i = 0; i < rowCount; i++) { - const divisionValue = dataSource.getValue(i, 'displayYear'); - if (divisionValue !== null && divisionValue !== undefined && divisionValue !== '') { - uniqueValues.add(String(divisionValue)); + }, + { + valueCallback: (grid: GridView) => { + const divisionIndex = 1; + const dataSource = grid.getDataSource(); + const rowCount = dataSource.getRowCount(); + const uniqueValues = new Set(); + for (let i = 0; i < rowCount; i++) { + const divisionValue = dataSource.getValue(i, 'displayYear'); + if (divisionValue !== null && divisionValue !== undefined && divisionValue !== '') { + uniqueValues.add(String(divisionValue)); + } } + const divisionValues = Array.from(uniqueValues); + return divisionValues.length > divisionIndex ? divisionValues[divisionIndex] + ' 합계' : ''; } - const divisionValues = Array.from(uniqueValues); - return divisionValues.length > divisionIndex ? divisionValues[divisionIndex] + ' 합계' : ''; } - } - ] - }, + ] + }, - /* 2. 시설 제원 */ - { name: 'fcltyKndCn', fieldName: 'fcltyKndCn', header: { text: '시설물종류' } }, - { name: 'oprtngStrtYmd', fieldName: 'oprtngStrtYmd', header: { text: '가동개시 년월일' } }, - { name: 'planDesignFreqCnt', fieldName: 'planDesignFreqCnt', header: { text: '계획설계빈도' } }, - { name: 'wtrStrgZoneArea', fieldName: 'wtrStrgZoneArea', header: { text: '집수구역면적(㎢)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.000', footer: footerDecimal3ArrayByDivision }, - { name: 'rsvrYn', fieldName: 'rsvrYn', header: { text: '유수지유무\n(유,무)' } }, - { name: 'rsvrImpndQnt', fieldName: 'rsvrImpndQnt', header: { text: '저류량(㎥)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0', footer: footerDecimal1ArrayByDivision }, - { name: 'abstnRgnImdQnt', fieldName: 'abstnRgnImdQnt', header: { text: '저류량(㎥)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0', footer: footerDecimal1ArrayByDivision }, - { name: 'fcltCpct', fieldName: 'fcltCpct', header: { text: '시설용량(㎥)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0', footer: footerDecimal1ArrayByDivision }, + /* 2. 시설 제원 */ + { name: 'fcltyKndCn', fieldName: 'fcltyKndCn', header: { text: '시설물종류' } }, + { name: 'oprtngStrtYmd', fieldName: 'oprtngStrtYmd', header: { text: '가동개시 년월일' } }, + { name: 'planDesignFreqCnt', fieldName: 'planDesignFreqCnt', header: { text: '계획설계빈도' } }, + { name: 'wtrStrgZoneArea', fieldName: 'wtrStrgZoneArea', header: { text: '집수구역면적(㎢)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.000', footer: footerDecimal3ArrayByDivision }, + { name: 'rsvrYn', fieldName: 'rsvrYn', header: { text: '유수지유무\n(유,무)' } }, + { name: 'rsvrImpndQnt', fieldName: 'rsvrImpndQnt', header: { text: '저류량(㎥)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0', footer: footerDecimal1ArrayByDivision }, + { name: 'abstnRgnImdQnt', fieldName: 'abstnRgnImdQnt', header: { text: '저류량(㎥)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0', footer: footerDecimal1ArrayByDivision }, + { name: 'fcltCpct', fieldName: 'fcltCpct', header: { text: '시설용량(㎥)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0', footer: footerDecimal1ArrayByDivision }, - /* 3. 펌프시설 */ - { name: 'pumpFcltN1DmtrExpln', fieldName: 'pumpFcltN1DmtrExpln', header: { text: '구경(mm)' } }, - { name: 'pumpFcltN1PerPpqty', fieldName: 'pumpFcltN1PerPpqty', header: { text: '대당 양수량' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0', footer: footerDecimal1ArrayByDivision }, - { name: 'pumpFcltN1PumpCnt', fieldName: 'pumpFcltN1PumpCnt', header: { text: '펌프대수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0', footer: footerIntArrayByDivision }, - { name: 'pumpFcltN1RsrvPumpCnt', fieldName: 'pumpFcltN1RsrvPumpCnt', header: { text: '예비펌프대수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0', footer: footerIntArrayByDivision }, + /* 3. 펌프시설 */ + { name: 'pumpFcltN1DmtrExpln', fieldName: 'pumpFcltN1DmtrExpln', header: { text: '구경(mm)' } }, + { name: 'pumpFcltN1PerPpqty', fieldName: 'pumpFcltN1PerPpqty', header: { text: '대당 양수량' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0', footer: footerDecimal1ArrayByDivision }, + { name: 'pumpFcltN1PumpCnt', fieldName: 'pumpFcltN1PumpCnt', header: { text: '펌프대수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0', footer: footerIntArrayByDivision }, + { name: 'pumpFcltN1RsrvPumpCnt', fieldName: 'pumpFcltN1RsrvPumpCnt', header: { text: '예비펌프대수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0', footer: footerIntArrayByDivision }, - { name: 'pumpFcltN2DmtrExpln', fieldName: 'pumpFcltN2DmtrExpln', header: { text: '구경(mm)' } }, - { name: 'pumpFcltN2PerPpqty', fieldName: 'pumpFcltN2PerPpqty', header: { text: '대당 양수량' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0', footer: footerDecimal1ArrayByDivision }, - { name: 'pumpFcltN2PumpCnt', fieldName: 'pumpFcltN2PumpCnt', header: { text: '펌프대수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0', footer: footerIntArrayByDivision }, - { name: 'pumpFcltN2RsrvPumpCnt', fieldName: 'pumpFcltN2RsrvPumpCnt', header: { text: '예비펌프대수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0', footer: footerIntArrayByDivision }, + { name: 'pumpFcltN2DmtrExpln', fieldName: 'pumpFcltN2DmtrExpln', header: { text: '구경(mm)' } }, + { name: 'pumpFcltN2PerPpqty', fieldName: 'pumpFcltN2PerPpqty', header: { text: '대당 양수량' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0', footer: footerDecimal1ArrayByDivision }, + { name: 'pumpFcltN2PumpCnt', fieldName: 'pumpFcltN2PumpCnt', header: { text: '펌프대수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0', footer: footerIntArrayByDivision }, + { name: 'pumpFcltN2RsrvPumpCnt', fieldName: 'pumpFcltN2RsrvPumpCnt', header: { text: '예비펌프대수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0', footer: footerIntArrayByDivision }, - { name: 'pumpFcltN3DmtrExpln', fieldName: 'pumpFcltN3DmtrExpln', header: { text: '구경(mm)' } }, - { name: 'pumpFcltN3PerPpqty', fieldName: 'pumpFcltN3PerPpqty', header: { text: '대당 양수량' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0', footer: footerDecimal1ArrayByDivision }, - { name: 'pumpFcltN3PumpCnt', fieldName: 'pumpFcltN3PumpCnt', header: { text: '펌프대수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0', footer: footerIntArrayByDivision }, - { name: 'pumpFcltN3RsrvPumpCnt', fieldName: 'pumpFcltN3RsrvPumpCnt', header: { text: '예비펌프대수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0', footer: footerIntArrayByDivision }, + { name: 'pumpFcltN3DmtrExpln', fieldName: 'pumpFcltN3DmtrExpln', header: { text: '구경(mm)' } }, + { name: 'pumpFcltN3PerPpqty', fieldName: 'pumpFcltN3PerPpqty', header: { text: '대당 양수량' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0', footer: footerDecimal1ArrayByDivision }, + { name: 'pumpFcltN3PumpCnt', fieldName: 'pumpFcltN3PumpCnt', header: { text: '펌프대수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0', footer: footerIntArrayByDivision }, + { name: 'pumpFcltN3RsrvPumpCnt', fieldName: 'pumpFcltN3RsrvPumpCnt', header: { text: '예비펌프대수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0', footer: footerIntArrayByDivision }, - /* 4. 부대시설 */ - { name: 'prcsgFcltYn', fieldName: 'prcsgFcltYn', header: { text: '전처리시설' } }, - { name: 'ddrztnEqpmYn', fieldName: 'ddrztnEqpmYn', header: { text: '탈취설비' } }, - { name: 'oslfEtgtYn', fieldName: 'oslfEtgtYn', header: { text: '자가발전' } }, - { name: 'dschgWtrsExpln',fieldName: 'dschgWtrsExpln',header: { text: '방류수역' } }, + /* 4. 부대시설 */ + { name: 'prcsgFcltYn', fieldName: 'prcsgFcltYn', header: { text: '전처리시설' } }, + { name: 'ddrztnEqpmYn', fieldName: 'ddrztnEqpmYn', header: { text: '탈취설비' } }, + { name: 'oslfEtgtYn', fieldName: 'oslfEtgtYn', header: { text: '자가발전' } }, + { name: 'dschgWtrsExpln', fieldName: 'dschgWtrsExpln', header: { text: '방류수역' } }, - /* 5. 최종처분량 */ - { name: 'prcptnLastDspsQnt', fieldName: 'prcptnLastDspsQnt', header: { text: '침사(량)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0', footer: footerIntArrayByDivision }, - { name: 'ctmnLastDspsQnt', fieldName: 'ctmnLastDspsQnt', header: { text: '협잡물(량)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0', footer: footerIntArrayByDivision }, + /* 5. 최종처분량 */ + { name: 'prcptnLastDspsQnt', fieldName: 'prcptnLastDspsQnt', header: { text: '침사(량)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0', footer: footerIntArrayByDivision }, + { name: 'ctmnLastDspsQnt', fieldName: 'ctmnLastDspsQnt', header: { text: '협잡물(량)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0', footer: footerIntArrayByDivision }, - /* 6. 운영실적 */ - { name: 'oprtngDayCnt', fieldName: 'oprtngDayCnt', header: { text: '가동일수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0', footer: footerIntArrayByDivision }, - { name: 'grssMltvQnt', fieldName: 'grssMltvQnt', header: { text: '총배수량(㎥/년)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0', footer: footerIntArrayByDivision }, - { name: 'rmrk', fieldName: 'rmrk', header: { text: '비고' } }, - ], - columnLayout: [ - { - name: 'areaGroup', header: { text: '행정구역' }, items: [ - { column: 'ctpvNm', footerUserSpans: [{ colspan: 4 }, { colspan: 4 }] }, - 'sggNm', 'admdstNm', - ] - }, - 'displayYear', 'fcltyKndCn', 'oprtngStrtYmd', 'planDesignFreqCnt', 'wtrStrgZoneArea', - { name: 'revGroup', header: { text: '유수지' }, items: ['rsvrYn', 'rsvrImpndQnt'] }, - { name: 'absGroup', header: { text: '흡수정' }, items: ['abstnRgnImdQnt'] }, - 'fcltCpct', - { - name: 'pumpMainGroup', header: { text: '펌프시설' }, - items: [ - { name: 'p1Group', header: { text: '펌프시설1' }, items: ['pumpFcltN1DmtrExpln', 'pumpFcltN1PerPpqty', 'pumpFcltN1PumpCnt', 'pumpFcltN1RsrvPumpCnt'] }, - { name: 'p2Group', header: { text: '펌프시설2' }, items: ['pumpFcltN2DmtrExpln', 'pumpFcltN2PerPpqty', 'pumpFcltN2PumpCnt', 'pumpFcltN2RsrvPumpCnt'] }, - { name: 'p3Group', header: { text: '펌프시설3' }, items: ['pumpFcltN3DmtrExpln', 'pumpFcltN3PerPpqty', 'pumpFcltN3PumpCnt', 'pumpFcltN3RsrvPumpCnt'] } - ] - }, - { name: 'subFacGroup', header: { text: '부대시설' }, items: ['prcsgFcltYn', 'ddrztnEqpmYn', 'oslfEtgtYn'] }, - 'dschgWtrsExpln', - { name: 'disposalAmtGroup', header: { text: '최종처분량(ton/년)' }, items: ['prcptnLastDspsQnt', 'ctmnLastDspsQnt'] }, - { name: 'opGroup', header: { text: '운영실적' }, items: ['oprtngDayCnt', 'grssMltvQnt'] }, - 'rmrk' - ] -} + /* 6. 운영실적 */ + { name: 'oprtngDayCnt', fieldName: 'oprtngDayCnt', header: { text: '가동일수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0', footer: footerIntArrayByDivision }, + { name: 'grssMltvQnt', fieldName: 'grssMltvQnt', header: { text: '총배수량(㎥/년)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0', footer: footerIntArrayByDivision }, + { name: 'rmrk', fieldName: 'rmrk', header: { text: '비고' } }, + ], + columnLayout: [ + { + name: 'areaGroup', header: { text: '행정구역' }, items: [ + { column: 'ctpvNm', footerUserSpans: [{ colspan: 4 }, { colspan: 4 }] }, + 'sggNm', 'fcltNm' + ] + }, + 'displayYear', 'fcltyKndCn', 'oprtngStrtYmd', 'planDesignFreqCnt', 'wtrStrgZoneArea', + { name: 'revGroup', header: { text: '유수지' }, items: ['rsvrYn', 'rsvrImpndQnt'] }, + { name: 'absGroup', header: { text: '흡수정' }, items: ['abstnRgnImdQnt'] }, + 'fcltCpct', + { + name: 'pumpMainGroup', header: { text: '펌프시설' }, + items: [ + { name: 'p1Group', header: { text: '펌프시설1' }, items: ['pumpFcltN1DmtrExpln', 'pumpFcltN1PerPpqty', 'pumpFcltN1PumpCnt', 'pumpFcltN1RsrvPumpCnt'] }, + { name: 'p2Group', header: { text: '펌프시설2' }, items: ['pumpFcltN2DmtrExpln', 'pumpFcltN2PerPpqty', 'pumpFcltN2PumpCnt', 'pumpFcltN2RsrvPumpCnt'] }, + { name: 'p3Group', header: { text: '펌프시설3' }, items: ['pumpFcltN3DmtrExpln', 'pumpFcltN3PerPpqty', 'pumpFcltN3PumpCnt', 'pumpFcltN3RsrvPumpCnt'] } + ] + }, + { name: 'subFacGroup', header: { text: '부대시설' }, items: ['prcsgFcltYn', 'ddrztnEqpmYn', 'oslfEtgtYn'] }, + 'dschgWtrsExpln', + { name: 'disposalAmtGroup', header: { text: '최종처분량(ton/년)' }, items: ['prcptnLastDspsQnt', 'ctmnLastDspsQnt'] }, + { name: 'opGroup', header: { text: '운영실적' }, items: ['oprtngDayCnt', 'grssMltvQnt'] }, + 'rmrk' + ] + } ]; export const SewageReservoirPumpInfoListRvw = rawReservoirList.map(tab => ({ diff --git a/apps/st/src/components/util/column/review/SeweragePenetrationRateRvwColumn.ts b/apps/st/src/components/util/column/review/SeweragePenetrationRateRvwColumn.ts index 25449c6cc..0cc8caae6 100644 --- a/apps/st/src/components/util/column/review/SeweragePenetrationRateRvwColumn.ts +++ b/apps/st/src/components/util/column/review/SeweragePenetrationRateRvwColumn.ts @@ -76,7 +76,7 @@ const rawSeweragePenetrationRateInfoList = { } ] }, - { name: 'wtsysNm', fieldName: 'rvbsn', header: { text: '수계' } }, + { name: 'rvbsn', fieldName: 'rvbsn', header: { text: '수계' } }, { name: 'trbtrNm', fieldName: 'trbtrNm', header: { text: '지류' } }, { name: 'mdbsn', fieldName: 'mdbsn', header: { text: '세부단위유역' } }, { name: 'lgbsn', fieldName: 'lgbsn', header: { text: '중권역' } }, @@ -117,7 +117,7 @@ const rawSeweragePenetrationRateInfoList = { ] }, 'displayYear', - 'wtsysNm', + 'rvbsn', 'trbtrNm', { name: 'groupB', diff --git a/apps/st/src/components/util/column/review/StatsInputStatusRvwColumn.ts b/apps/st/src/components/util/column/review/StatsInputStatusRvwColumn.ts index cb06da831..6feec3781 100644 --- a/apps/st/src/components/util/column/review/StatsInputStatusRvwColumn.ts +++ b/apps/st/src/components/util/column/review/StatsInputStatusRvwColumn.ts @@ -8,26 +8,28 @@ const StatsInputStatusInfoColumnRaw = { { name: 'sggNm', fieldName: 'sggNm', header: { text: '시군구' }, }, { name: 'sewerBzmnCd', fieldName: 'sewerBzmnCd', header: { text: '사업자코드' }, visible: false }, { name: 'statsCd', fieldName: 'statsCd', header: { text: '메뉴코드' }, visible: false }, - { name: 'status', fieldName: 'status', header: { text: '진행상태' }, mergeCells: true }, - { name: 'statsNm', fieldName: 'statsNm', header: { text: '구분' }, width: 220 }, + { name: 'tabNm', fieldName: 'tabNm', header: { text: '구분' }, width: 220 }, { name: 'chargerNm', fieldName: 'chargerNm', header: { text: '제출자' } }, { name: 'chargerCttpc', fieldName: 'chargerCttpc', header: { text: '연락처' } }, - { name: 'statusNm', fieldName: 'statusNm', header: { text: '지자체진행상태' } }, - { name: 'statusNm2', fieldName: 'statusNm2', header: { text: '진행상태' } }, + { name: 'submitStatusNm', fieldName: 'submitStatusNm', header: { text: '지자체진행상태' } }, + { name: 'submitStatusCd', fieldName: 'submitStatusCd', header: { text: '지자체진행상태코드' }, visible: false }, + { name: 'prvnStatusNm', fieldName: 'prvnStatusNm', header: { text: '진행상태' } }, + { name: 'prvnStatusCd', fieldName: 'prvnStatusCd', header: { text: '진행상태코드' }, visible: false }, ], columnLayout: [ 'ctpvNm', 'sggNm', - 'statsNm', - 'status', + 'tabNm', 'chargerNm', 'chargerCttpc', - 'statusNm', - 'statusNm2', + 'submitStatusNm', + 'prvnStatusNm', 'statsCd', 'sewerBzmnCd', + 'submitStatusCd', + 'prvnStatusCd' ] }; diff --git a/apps/st/src/components/util/column/total/SewagePipeTotColumn.ts b/apps/st/src/components/util/column/total/SewagePipeTotColumn.ts new file mode 100644 index 000000000..fa55d7f55 --- /dev/null +++ b/apps/st/src/components/util/column/total/SewagePipeTotColumn.ts @@ -0,0 +1,799 @@ +import dayjs from "dayjs"; +import { headers } from "next/headers"; +import { ValueType } from "realgrid"; + +/** + * 하수도 통계 관리 > 02. 하수관로 (합계) + * 항목은 검토(SewagePipeRvwColumn)와 동일하나 footer·mergeCells·구분(displayYear)·비고(rmrk) 제외. + * 표시 형식은 SeweragePentrationRateTotColumn과 같이 dataFormat + editable: false. + */ +const currentYear = dayjs().year() - 1; +const startYear = 1996; +const totalCount = currentYear - startYear + 1 + 1; + +const rawSewagePipeTotList = [ + { + tabKey: "ST00020001", + tabLabel: "지역별 시설현황", + rowStartNum: 4, + 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: "goalYr", fieldName: "goalYr", header: { text: "하수정보기본계획목표년도" }, editable: false }, + { + name: "wholPplMtncPlanPrlg", + fieldName: "wholPplMtncPlanPrlg", + header: { text: "총관로정비\n계획연장(m)" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.00", + editable: false, + }, + { + name: "wholPlanPrlg", + fieldName: "wholPlanPrlg", + header: { text: "총계획연장(m)" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.00", + editable: false, + }, + { + name: "wholFcltPrlg", + fieldName: "wholFcltPrlg", + header: { text: "총시설연장(m)" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.00", + editable: false, + }, + { + name: "swagePplPsplyRt", + fieldName: "swagePplPsplyRt", + header: { text: "하수관로\n보급률(%)" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.00", + editable: false, + }, + { + name: "cbsysPplMtncPlanArea", + fieldName: "cbsysPplMtncPlanArea", + header: { text: "관로정비\n계획면적(㎢)" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.000", + editable: false, + }, + { + name: "cbsysPplMtncPlanPrlg", + fieldName: "cbsysPplMtncPlanPrlg", + header: { text: "관로정비\n계획연장(m)" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.00", + editable: false, + }, + { + name: "cbsysPlanPrlg", + fieldName: "cbsysPlanPrlg", + header: { text: "계획연장(m)" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.00", + editable: false, + }, + { + name: "cbsysFcltPrlg", + fieldName: "cbsysFcltPrlg", + header: { text: "시설연장(m)" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.00", + editable: false, + }, + { + name: "cbsysSqrePrlg", + fieldName: "cbsysSqrePrlg", + header: { text: "사각형(m)" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.00", + editable: false, + }, + { + name: "cbsysCiclPrlg", + fieldName: "cbsysCiclPrlg", + header: { text: "원형(m)" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.00", + editable: false, + }, + { + name: "cbsysOpnPrlg", + fieldName: "cbsysOpnPrlg", + header: { text: "개거(m)" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.00", + editable: false, + }, + { + name: "cbsysGttrPrlg", + fieldName: "cbsysGttrPrlg", + header: { text: "측구(m)" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.00", + editable: false, + }, + { + name: "drtwPplMtncPlanArea", + fieldName: "drtwPplMtncPlanArea", + header: { text: "관로정비\n계획면적(㎢)" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.000", + editable: false, + }, + { + name: "drtwPplMtncPlanPrlg", + fieldName: "drtwPplMtncPlanPrlg", + header: { text: "관로정비\n계획연장(m)" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.00", + editable: false, + }, + { + name: "drtwPplPlanPrlg", + fieldName: "drtwPplPlanPrlg", + header: { text: "계획연장(m)" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.00", + editable: false, + }, + { + name: "drtwPplFcltPrlg", + fieldName: "drtwPplFcltPrlg", + header: { text: "시설연장(m)" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.00", + editable: false, + }, + { + name: "drtwPplSqrePrlg", + fieldName: "drtwPplSqrePrlg", + header: { text: "사각형(m)" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.00", + editable: false, + }, + { + name: "drtwPplCiclPrlg", + fieldName: "drtwPplCiclPrlg", + header: { text: "원형(m)" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.00", + editable: false, + }, + { + name: "exlnPplMtncPlanArea", + fieldName: "exlnPplMtncPlanArea", + header: { text: "관로정비\n계획면적(㎢)" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.000", + editable: false, + }, + { + name: "exlnPplMtncPlanPrlg", + fieldName: "exlnPplMtncPlanPrlg", + header: { text: "관로정비\n계획연장(m)" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.00", + editable: false, + }, + { + name: "exlnPplPlanPrlg", + fieldName: "exlnPplPlanPrlg", + header: { text: "계획연장(m)" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.00", + editable: false, + }, + { + name: "exlnPplFcltPrlg", + fieldName: "exlnPplFcltPrlg", + header: { text: "시설연장(m)" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.00", + editable: false, + }, + { + name: "exlnPplSqrePrlg", + fieldName: "exlnPplSqrePrlg", + header: { text: "사각형(m)" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.00", + editable: false, + }, + { + name: "exlnPplCiclPrlg", + fieldName: "exlnPplCiclPrlg", + header: { text: "원형(m)" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.00", + editable: false, + }, + { + name: "exlnPplOpnPrlg", + fieldName: "exlnPplOpnPrlg", + header: { text: "개거(m)" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.00", + editable: false, + }, + { + name: "exlnPplGttrPrlg", + fieldName: "exlnPplGttrPrlg", + header: { text: "측구(m)" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.00", + editable: false, + }, + { + name: "cbsysMnhlCnt", + fieldName: "cbsysMnhlCnt", + header: { text: "합류식맨홀(개소)" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0", + editable: false, + }, + { + name: "sprsysDrtwMnhlCnt", + fieldName: "sprsysDrtwMnhlCnt", + header: { text: "오수맨홀(개소)" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0", + editable: false, + }, + { + name: "sprsysExlnMnhlCnt", + fieldName: "sprsysExlnMnhlCnt", + header: { text: "우수맨홀(개소)" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0", + editable: false, + }, + { + name: "drtwRcvprpCnt", + fieldName: "drtwRcvprpCnt", + header: { text: "오수받이(개소)" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0", + editable: false, + }, + { + name: "exlnRcvprpCnt", + fieldName: "exlnRcvprpCnt", + header: { text: "우수받이(개소)" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0", + editable: false, + }, + { + name: "stofcbrCnt", + fieldName: "stofcbrCnt", + header: { text: "우수토실(개소)" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0", + editable: false, + }, + { + name: "exlnOtflCnt", + fieldName: "exlnOtflCnt", + header: { text: "우수토구(개소)" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0", + editable: false, + }, + { + name: "sewerLdgrEdpPrlg", + fieldName: "sewerLdgrEdpPrlg", + header: { text: "하수도대장전산화연장(m)" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0", + editable: false, + }, + { + name: "sewerLdgrEdpRt", + fieldName: "sewerLdgrEdpRt", + header: { text: "하수도대장전산화율(%)" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.0", + editable: false, + }, + ], + columnLayout: [ + { + name: "areaGroup", + header: { text: "행정구역" }, + items: ["ctpvNm", "sggNm", "admdstNm"], + }, + "goalYr", + "wholPplMtncPlanPrlg", + "wholPlanPrlg", + "wholFcltPrlg", + "swagePplPsplyRt", + { + name: "combinedGroup", + header: { text: "합류식(m)" }, + items: [ + "cbsysPplMtncPlanArea", + "cbsysPplMtncPlanPrlg", + "cbsysPlanPrlg", + "cbsysFcltPrlg", + { name: "cmbnAmGroup", header: { text: "암거" }, items: ["cbsysSqrePrlg", "cbsysCiclPrlg"] }, + "cbsysOpnPrlg", + "cbsysGttrPrlg", + ], + }, + { + name: "sepGroup", + header: { text: "분류식(m)" }, + items: [ + { + name: "foulPipeGroup", + header: { text: "오수관로" }, + items: [ + "drtwPplMtncPlanArea", + "drtwPplMtncPlanPrlg", + "drtwPplPlanPrlg", + "drtwPplFcltPrlg", + { name: "foulAmGroup", header: { text: "암거" }, items: ["drtwPplSqrePrlg", "drtwPplCiclPrlg"] }, + ], + }, + { + name: "rainPipeGroup", + header: { text: "우수관로" }, + items: [ + "exlnPplMtncPlanArea", + "exlnPplMtncPlanPrlg", + "exlnPplPlanPrlg", + "exlnPplFcltPrlg", + { name: "rainAmGroup", header: { text: "암거" }, items: ["exlnPplSqrePrlg", "exlnPplCiclPrlg"] }, + "exlnPplOpnPrlg", + "exlnPplGttrPrlg", + ], + }, + ], + }, + "cbsysMnhlCnt", + { name: "fcltyGroup", header: { text: "분류식" }, items: ["sprsysDrtwMnhlCnt", "sprsysExlnMnhlCnt"] }, + "drtwRcvprpCnt", + "exlnRcvprpCnt", + "stofcbrCnt", + "exlnOtflCnt", + "sewerLdgrEdpPrlg", + "sewerLdgrEdpRt", + ], + }, + + { + tabKey: "ST00020002", + tabLabel: "시설별 설치현황", + rowStartNum: 4, + fixedColumns: 3, + columns: [ + { name: "ctpvNm", fieldName: "ctpvNm", header: { text: "시도" }, editable: false }, + { name: "sggNm", fieldName: "sggNm", header: { text: "시군구" }, editable: false }, + { name: "admdstNm", fieldName: "admdstNm", header: { text: "행정구역명" }, editable: false }, + { name: "swtrmtFcltNm", fieldName: "swtrmtFcltNm", header: { text: "하수처리시설 명" }, editable: false }, + { + name: "prcsFcltCpct", + fieldName: "prcsFcltCpct", + header: { text: "하수처리\n시설용량\n(㎥/일)" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.0", + editable: false, + }, + { + name: "prcsZoneArea", + fieldName: "prcsZoneArea", + header: { text: "합계" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.000", + editable: false, + }, + { + name: "cbsysPrcsZoneArea", + fieldName: "cbsysPrcsZoneArea", + header: { text: "합류식" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.000", + editable: false, + }, + { + name: "sprsysPrcsZoneArea", + fieldName: "sprsysPrcsZoneArea", + header: { text: "분류식" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.000", + editable: false, + }, + { + name: "sprsysVartnRt", + fieldName: "sprsysVartnRt", + header: { text: "분류식화율" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.000", + editable: false, + }, + { + name: "mltvEqpmCnt", + fieldName: "mltvEqpmCnt", + header: { text: "합계" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0", + editable: false, + }, + { + name: "cbsysMltvEqpmCnt", + fieldName: "cbsysMltvEqpmCnt", + header: { text: "합류식" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0", + editable: false, + }, + { + name: "sprsysMltvEqpmCnt", + fieldName: "sprsysMltvEqpmCnt", + header: { text: "합계" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0", + editable: false, + }, + { + name: "sprsysMltvLnkgCnt", + fieldName: "sprsysMltvLnkgCnt", + header: { text: "연결" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0", + editable: false, + }, + { + name: "sprsysMltvYetLnkgCnt", + fieldName: "sprsysMltvYetLnkgCnt", + header: { text: "미연결" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0", + editable: false, + }, + { + name: "wholFcltPrlg", + fieldName: "wholFcltPrlg", + header: { text: "총\n시설연장\n(m)" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.00", + editable: false, + }, + { + name: "wholIcptgAtlfcPrlg", + fieldName: "wholIcptgAtlfcPrlg", + header: { text: "합계" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.00", + editable: false, + }, + { + name: "icptgPrlg", + fieldName: "icptgPrlg", + header: { text: "차집" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.00", + editable: false, + }, + { + name: "atlfcsPrlg", + fieldName: "atlfcsPrlg", + header: { text: "오수간선" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.00", + editable: false, + }, + { + name: "cbsysStofcbrCnt", + fieldName: "cbsysStofcbrCnt", + header: { text: "우수토실(합류)" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0", + editable: false, + }, + { + name: "sprsysStofcbrCnt", + fieldName: "sprsysStofcbrCnt", + header: { text: "우수토실(분류)" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0", + editable: false, + }, + { + name: "sprsysExlnOtflCnt", + fieldName: "sprsysExlnOtflCnt", + header: { text: "우수토구(분류)" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0", + editable: false, + }, + { + name: "wholBrchlnPrlg", + fieldName: "wholBrchlnPrlg", + header: { text: "합계" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.00", + editable: false, + }, + { + name: "cbsysBrchlnPrlg", + fieldName: "cbsysBrchlnPrlg", + header: { text: "합류식" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.00", + editable: false, + }, + { + name: "exlnBrchlnPrlg", + fieldName: "exlnBrchlnPrlg", + header: { text: "우수" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.00", + editable: false, + }, + { + name: "drtwBrchlnPrlg", + fieldName: "drtwBrchlnPrlg", + header: { text: "오수" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.00", + editable: false, + }, + ], + columnLayout: [ + { + name: "addrGroup", + header: { text: "행정구역" }, + items: ["ctpvNm", "sggNm", "admdstNm"], + }, + "swtrmtFcltNm", + "prcsFcltCpct", + { + name: "areaGroup", + header: { text: "처리구역면적(㎢)" }, + items: ["prcsZoneArea", "cbsysPrcsZoneArea", "sprsysPrcsZoneArea"], + }, + "sprsysVartnRt", + { + name: "drainGroup", + header: { text: "배수설비(개소)" }, + items: [ + "mltvEqpmCnt", + "cbsysMltvEqpmCnt", + { + name: "sprsysGroup", + header: { text: "분류식" }, + items: [ + "sprsysMltvEqpmCnt", "sprsysMltvLnkgCnt", "sprsysMltvYetLnkgCnt" + ] + } + ], + }, + { + name: "pipeGroup", + header: { text: "하수관로" }, + items: [ + "wholFcltPrlg", + { + name: "trunkGroup", + header: { text: "차집/오수간선(m)" }, + items: ["wholIcptgAtlfcPrlg", "icptgPrlg", "atlfcsPrlg"], + }, + { + name: "siteGroup", + header: { text: "토실/토구(개소)" }, + items: [ + { name: "cmbnSiteGroup", header: { text: "합류식" }, items: ["cbsysStofcbrCnt"] }, + { name: "sepSiteGroup", header: { text: "분류식" }, items: ["sprsysStofcbrCnt", "sprsysExlnOtflCnt"] }, + ], + }, + { + name: "branchGroup", + header: { text: "지선(m)" }, + items: [ + "wholBrchlnPrlg", + "cbsysBrchlnPrlg", + { name: "sepBranchGroup", header: { text: "분류식" }, items: ["exlnBrchlnPrlg", "drtwBrchlnPrlg"] }, + ], + }, + ], + }, + ], + }, + + { + tabKey: "ST00020003", + 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: "swrgPipeNm", fieldName: "swrgPipeNm", header: { text: "관종" }, editable: false }, + { + name: "pipeByPrstWholSum", + fieldName: "pipeByPrstWhol", + header: { text: "계" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.0", + editable: false, + }, + { + name: "lyngPrlgSum", + fieldName: "lyngPrlg", + header: { text: "매설연장(m)" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.0", + editable: false, + }, + { + name: "dmltnPrlgSum", + fieldName: "dmltnPrlg", + header: { text: "철거연장(m)" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.0", + editable: false, + }, + ...Array.from({ length: totalCount }, (_, i) => { + const dtoNum = totalCount - i; + const wholField = `yy${dtoNum}PrlgWhol`; + const lyngField = `yy${dtoNum}LyngPrlg`; + const dmltnField = `yy${dtoNum}DmltnPrlg`; + return [ + { + name: wholField, + fieldName: wholField, + header: { text: "계" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.0", + editable: false, + }, + { + name: lyngField, + fieldName: lyngField, + header: { text: "매설연장(m)" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.0", + editable: false, + }, + { + name: dmltnField, + fieldName: dmltnField, + header: { text: "철거연장(m)" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.0", + editable: false, + }, + ]; + }).flat(), + ], + columnLayout: [ + { + name: "areaGroup", + header: { text: "행정구역" }, + items: ["ctpvNm", "sggNm", "admdstNm"], + }, + "swrgPipeNm", + { + name: "summaryGroup", + header: { text: "총계" }, + items: ["pipeByPrstWholSum", "lyngPrlgSum", "dmltnPrlgSum"], + }, + ...Array.from({ length: totalCount }, (_, i) => { + const dtoNum = totalCount - i; + const yearLabel = i === 0 ? "1996년이전" : `${startYear + (i - 1)}년`; + return { + name: `group_${yearLabel}`, + header: { text: yearLabel }, + items: [`yy${dtoNum}PrlgWhol`, `yy${dtoNum}LyngPrlg`, `yy${dtoNum}DmltnPrlg`], + }; + }), + ], + }, + + { + tabKey: "ST00020004", + tabLabel: "관경별 현황", + rowStartNum: 2, + fixedColumns: 3, + columns: [ + { name: "ctpvNm", fieldName: "ctpvNm", header: { text: "시도" }, editable: false }, + { name: "sggNm", fieldName: "sggNm", header: { text: "시군구" }, editable: false }, + { name: "admdstNm", fieldName: "admdstNm", header: { text: "행정구역명" }, editable: false }, + { name: "swrgPipeNm", fieldName: "swrgPipeNm", header: { text: "관종" }, editable: false }, + { + name: "pdmtByTsmWhol", + fieldName: "pdmtByTsmWhol", + header: { text: "총계(m)" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.0", + editable: false, + }, + { + name: "step1PdmtPrlg", + fieldName: "step1PdmtPrlg", + header: { text: "200mm 미만" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.0", + editable: false, + }, + { + name: "step2PdmtPrlg", + fieldName: "step2PdmtPrlg", + header: { text: "200mm이상~\n300mm미만" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.0", + editable: false, + }, + { + name: "step3PdmtPrlg", + fieldName: "step3PdmtPrlg", + header: { text: "300mm이상~\n500mm미만" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.0", + editable: false, + }, + { + name: "step4PdmtPrlg", + fieldName: "step4PdmtPrlg", + header: { text: "500mm이상~\n800mm미만" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.0", + editable: false, + }, + { + name: "step5PdmtPrlg", + fieldName: "step5PdmtPrlg", + header: { text: "800mm 이상" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.0", + editable: false, + }, + { + name: "sqrePdmtPrlg", + fieldName: "sqrePdmtPrlg", + header: { text: "사각형(m)" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.0", + editable: false, + }, + { + name: "etcPdmtPrlg", + fieldName: "etcPdmtPrlg", + header: { text: "기타\n(측구/개거 등)" }, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.0", + editable: false, + }, + ], + columnLayout: [ + { + name: "addrGroup", + header: { text: "행정구역" }, + items: ["ctpvNm", "sggNm", "admdstNm"], + }, + "swrgPipeNm", + "pdmtByTsmWhol", + { + name: "circleGroup", + header: { text: "원형(m)" }, + items: ["step1PdmtPrlg", "step2PdmtPrlg", "step3PdmtPrlg", "step4PdmtPrlg", "step5PdmtPrlg"], + }, + "sqrePdmtPrlg", + "etcPdmtPrlg", + ], + }, +]; + +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/api/input/statistics-input-sewage-pipe-maintain-service.ts b/apps/st/src/entities/statistics/api/input/statistics-input-sewage-pipe-maintain-service.ts new file mode 100644 index 000000000..31800f51c --- /dev/null +++ b/apps/st/src/entities/statistics/api/input/statistics-input-sewage-pipe-maintain-service.ts @@ -0,0 +1,29 @@ +import { stClient } from "@/shared/api/client"; +import { ENDPOINT } from "@/shared/config"; + +import type { + SewagePipeMaintainDredgingResponse, + SewagePipeMaintainRepairResponse, + SewagePipeMaintainRequestParams, + SewagePipeMaintainService, +} from "../../model/input/statistics-input-sewage-pipe-maintain-interface"; + +export class SewagePipeMaintainApiService implements SewagePipeMaintainService { + public async getSewagePipeMaintainRepair(param: SewagePipeMaintainRequestParams): Promise { + const response = await stClient.get( + `${ENDPOINT.ST_SERVICE.INPUT}/sewage-pipe-maintain/repair`, + { params: param } + ); + return response.data; + }; + + public async getSewagePipeMaintainDredging(param: SewagePipeMaintainRequestParams): Promise { + const response = await stClient.get( + `${ENDPOINT.ST_SERVICE.INPUT}/sewage-pipe-maintain/dredging`, + { params: param } + ); + return response.data; + }; +} + +export const sewagePipeMaintainApiService = new SewagePipeMaintainApiService(); \ No newline at end of file diff --git a/apps/st/src/entities/statistics/api/statistics-service.ts b/apps/st/src/entities/statistics/api/statistics-service.ts index 42b5544e6..ef8bbbf2e 100644 --- a/apps/st/src/entities/statistics/api/statistics-service.ts +++ b/apps/st/src/entities/statistics/api/statistics-service.ts @@ -13,6 +13,7 @@ import { type SubmitExceptionResponse, type StatisticsLogRequest, type StatisticsLogResponse, + type StatisticsLogDetailResponse, } from "../model/statistics-interface"; export class StatisticsAPiService implements StatisticsService { @@ -72,6 +73,13 @@ export class StatisticsAPiService implements StatisticsService { ); return response.data; } + + public async getLogDetail(param: string): Promise { + const response = await stClient.get( + `${ENDPOINT.ST_SERVICE.INPUTCOMMON}/logs/${param}`, + ); + return response.data; + } } export const statisticsAPi = new StatisticsAPiService(); \ 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 a0c41f4c0..82c948855 100644 --- a/apps/st/src/entities/statistics/api/statistics-total-service.ts +++ b/apps/st/src/entities/statistics/api/statistics-total-service.ts @@ -2,6 +2,7 @@ import { ENDPOINT } from "@/shared/config"; import { stClient } from "@/shared/api/client"; import type { SewerageSupplyRateRequest, SewerageSupplyRateResponse } from "../model/statistics-total/statistics-sewage-supply-rate-total-interface"; +import type { SewagePipeTotalRequest, SewagePipeTotalResponse } from "../model/statistics-total/statistics-sewage-pipe-total-interface"; export class StatisticsTotalApiService { public async getSewerageSupplyRateList(params: SewerageSupplyRateRequest): Promise { @@ -11,6 +12,14 @@ export class StatisticsTotalApiService { ); return response.data; } + + public async getSewagePipeRegionalStatusList(params: SewagePipeTotalRequest): Promise { + const response = await stClient.get ( + `${ENDPOINT.ST_SERVICE.TOTAL}/sewer-pipe/regional-status`, + { params: params } + ); + return response.data; + } } export const statisticsTotalStatusApi = new StatisticsTotalApiService(); \ No newline at end of file diff --git a/apps/st/src/entities/statistics/model/input/statistics-input-sewage-pipe-maintain-interface.ts b/apps/st/src/entities/statistics/model/input/statistics-input-sewage-pipe-maintain-interface.ts new file mode 100644 index 000000000..80aebe0c6 --- /dev/null +++ b/apps/st/src/entities/statistics/model/input/statistics-input-sewage-pipe-maintain-interface.ts @@ -0,0 +1,112 @@ +import type { ApiResponse } from "../statistics-interface"; + +export interface SewagePipeMaintainRepairStatusInfo { + laePicNm: string | null; + laePicTelNo: string | null; + laeKbrdrDeptNm: string | null; + laeKbrdrJbgdNm: string | null; + laeIdfrNm: string | null; + laeIdfrTelNo: string | null; + laeIdfrDeptNm: string | null; + laeIdfrJbgdNm: string | null; + laeSbmsnSttsCd: string | null; + laeSbmsnYmd: string | null; +} + +export interface SewagePipeMaintainRepairVerificationResultItem { + vrfcCd: string; + rsnExpln: string; +} + +/** 필드명 -> 검증 결과 배열 */ +export type SewagePipeMaintainRepairVerificationResult = Record< + string, + SewagePipeMaintainRepairVerificationResultItem[] +>; + +export interface SewagePipeMaintainRepairCommonInfo { + statsYr: string; + ctpvNm: string; + sewerBzmnCd: string; + statsCd: string; + verificationResult?: SewagePipeMaintainRepairVerificationResult; +} + +export interface SewagePipeMaintainRepairItem { + admdstCd: string; + sggNm: string; + admdstNm: string; + mendngWhol: number; + cbsysMendngPrlg: number; + sprsysExlnPrlg: number; + sprsysDrtwPrlg: number; + partMendngWhol: number; + cbsysPartMendngPrlg: number; + sprsysExlnMendngPrlg: number; + sprsysDrtwMendngPrlg: number; + cbsysMnhlCnt: number; + sprsysDrtwMnhlCnt: number; + sprsysExlnMnhlCnt: number; + drtwRcvprpCnt: number; + exlnRcvprpCnt: number; + stofcbrCnt: number; + otflCnt: number; + cctvExmnPrlg: number; + rmrk: string | null; + verificationResult?: SewagePipeMaintainRepairVerificationResult; +} + +export interface SewagePipeMaintainRepairData { + statusInfo: SewagePipeMaintainRepairStatusInfo; + commonInfo: SewagePipeMaintainRepairCommonInfo; + repairStatusList: SewagePipeMaintainRepairItem[]; +} + +export interface SewagePipeMaintainDredgingItem { + admdstCd: string; + sggNm: string; + admdstNm: string; + wholFcltPrlg: number; + wholSwagePpl: number; + cbsysPplPrlg: number; + sprsysExlnPrlg: number; + sprsysDrtwPrlg: number; + mnhlCnt: number; + exlnDrtwRcvprpCnt: number; + stofcbrOtflCnt: number; + drdgnPlanPrlg: number; + drdgnTrgtCtrsDrdgnRt: number; + wholPrlgCtrsDrdgnRt: number; + drdgnAmt: number; + drdgnQnt: number; + drdgnUntprcAmt: number; + wholDrdgnPrcsQnt: number; + rclmQnt: number; + oceanEmsnQnt: number; + rcycQnt: number; + etcQnt: number; + rmrk: string | null; + verificationResult?: SewagePipeMaintainRepairVerificationResult; +} + +export interface SewagePipeMaintainDredgingData { + statusInfo: SewagePipeMaintainRepairStatusInfo; + commonInfo: SewagePipeMaintainRepairCommonInfo; + pipeDredgingStatusList: SewagePipeMaintainDredgingItem[]; +} + +export interface SewagePipeMaintainRequestParams { + ctpvNm?: string; + sggNm?: string; + sewerBzmnCd?: string; + statsYr?: string; + mode?: string; +} + +export type SewagePipeMaintainRepairResponse = ApiResponse; +export type SewagePipeMaintainDredgingResponse = ApiResponse; + +export interface SewagePipeMaintainService { + getSewagePipeMaintainRepair(param: SewagePipeMaintainRequestParams): Promise; + getSewagePipeMaintainDredging(param: SewagePipeMaintainRequestParams): Promise; +} diff --git a/apps/st/src/entities/statistics/model/input/statistics-input-status-interface.ts b/apps/st/src/entities/statistics/model/input/statistics-input-status-interface.ts index ae721e462..ea6c281fe 100644 --- a/apps/st/src/entities/statistics/model/input/statistics-input-status-interface.ts +++ b/apps/st/src/entities/statistics/model/input/statistics-input-status-interface.ts @@ -6,6 +6,7 @@ export interface StatsInputStatusListRequestParams { sewerBzmnCd?: string; statsYr?: string; mode?: string; + statsCd?: string; } export interface StatsInputStatusList { @@ -25,10 +26,14 @@ export interface StatsInputStatusList { rjctOpnnCn?: string; } -export type StatsInputStatusListApiResponse = ApiResponse & { +export type StatsInputStatusListApiResponse = ApiResponse< + StatsInputStatusList[] +> & { message: string; -} +}; export interface StatisticsInputStatusService { - getStatisticsInputStatusList(param: StatsInputStatusListRequestParams): Promise; + getStatisticsInputStatusList( + param: StatsInputStatusListRequestParams, + ): Promise; } diff --git a/apps/st/src/entities/statistics/model/statistics-input-review-interface.ts b/apps/st/src/entities/statistics/model/statistics-input-review-interface.ts index e2515fccb..533c607bf 100644 --- a/apps/st/src/entities/statistics/model/statistics-input-review-interface.ts +++ b/apps/st/src/entities/statistics/model/statistics-input-review-interface.ts @@ -7,17 +7,18 @@ export interface StatsInputStatusReviewItem { ctpvNm?: string; sggNm?: string; sewerBzmnCd?: string; - status?: string; - statsNm?: string; + tabNm?: string; statsCd?: string; - statusNm?: string; - statusCd?: string; + submitStatusNm?: string; + prvnStatusNm?: string; chargerNm?: string; chargerCttpc?: string; + submitStatusCd?: string; + prvnStatusCd?: string; } export interface StatsInputStatusReviewResponseData { - sewerageSupplyRateList: StatsInputStatusReviewItem[]; + sewerageInputList: StatsInputStatusReviewItem[]; } diff --git a/apps/st/src/entities/statistics/model/statistics-interface.ts b/apps/st/src/entities/statistics/model/statistics-interface.ts index f32f7ef4f..50e71a99e 100644 --- a/apps/st/src/entities/statistics/model/statistics-interface.ts +++ b/apps/st/src/entities/statistics/model/statistics-interface.ts @@ -96,6 +96,24 @@ export interface StatisticsLogResponse { }; } +/** + * 통계 로그 데이터 상세조회 `data[]` 행. + * `rmrk`, `sggNm`, `admdstCd`, `admdstNm`는 공통이고, 그 외 키는 통계/탭에 따라 가변. + */ +export interface StatisticsLogDetailRow { + rmrk: string; + sggNm: string; + admdstCd: string; + admdstNm: string; + [key: string]: string | number | null | undefined; +} + +export interface StatisticsLogDetailResponse { + status: string; + message?: string; + data?: StatisticsLogDetailRow[]; +} + export interface StatisticsService { getSidoList(): Promise>; getSggList(sidoName: string): Promise>; @@ -104,4 +122,5 @@ export interface StatisticsService { callImrService(param: ImrRequest): Promise; submitException(param: SubmitExceptionRequest): Promise; getLogList(param: StatisticsLogRequest): Promise; + getLogDetail(param: string): Promise; } \ 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 6ffa91466..b059aa90c 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 @@ -131,12 +131,7 @@ export interface SewagePipeReviewFacilityItem { } -export interface SewagePipeFacilityResponseData { - facilityPipeList: SewagePipeReviewFacilityItem[]; -} - - -export type SewagePipeFacilityApiResponse = ApiResponse & { +export type SewagePipeFacilityApiResponse = ApiResponse & { message: string; }; @@ -177,7 +172,7 @@ export type SewerPipeTypeApiResponse = ApiResponse; export interface SewagePipeTypeService { - getSewagePipeTypeList(params: SewagePipeReviewRequestParams): Promise; + getSewagePipeTypeList(params: SewagePipeReviewRequestParams): Promise; } /** @@ -230,14 +225,9 @@ export interface SewerPipeDiamItem { } -export interface SewerPipeDiamResponseData { - pipeDiamStatusList: SewerPipeDiamItem[]; -} - - -export type SewerPipeDiamApiResponse = ApiResponse; +export type SewerPipeDiamApiResponse = ApiResponse; export interface SewagePipeDiamService { - getSewagePipeDiamList(params: SewagePipeReviewRequestParams): Promise; + getSewagePipeDiamList(params: SewagePipeReviewRequestParams): Promise; } \ No newline at end of file diff --git a/apps/st/src/entities/statistics/model/statistics-sewerage-penetration-review-interface.ts b/apps/st/src/entities/statistics/model/statistics-sewerage-penetration-review-interface.ts index 9c4d0ac1e..84b12b77f 100644 --- a/apps/st/src/entities/statistics/model/statistics-sewerage-penetration-review-interface.ts +++ b/apps/st/src/entities/statistics/model/statistics-sewerage-penetration-review-interface.ts @@ -2,59 +2,69 @@ import type { ApiResponse } from "./statistics-interface"; // 검토 01.하수도 보급률 데이터 항목 export interface SewerageSupplyRateReviewItem { -ctpvNm: string; - sggNm: string; - statsYr: string; - admdstNm: string; - admdstCd: string; - displayYear: string; - rowType: 'OLD' | 'CURR'; + ctpvNm: string; + sggNm: string; + statsYr: string; + admdstNm: string; + admdstCd: string; + displayYear: string; + rowType: 'OLD' | 'CURR'; - lgbsn: string; - mdbsn: string; - mdmbsn: string; - rvbsn: string | null; - trbtrNm: string | null; - spcCtpnRgnCn: string | null; + lgbsn: string; + mdbsn: string; + mdmbsn: string; + rvbsn: string | null; + trbtrNm: string | null; + spcCtpnRgnCn: string | null; - wholPpltnCnt: number; - wholArea: number; + wholPpltnCnt: number; + wholArea: number; - wholSwtrmtPpltnCnt: number; - wholPblSwtrmtPpltnCnt: number; - ts1SwtrmtPpltnCnt: number; - ts2SwtrmtPpltnCnt: number; - ts3SwtrmtPpltnCnt: number; - - wholWwtPrcsPpltnCnt: number; - ts1WwtPrcsPpltnCnt: number | null; - ts2WwtPrcsPpltnCnt: number | null; - ts3WwtPrcsPpltnCnt: number | null; - - yetCntnPpltnCnt: number; - staaArea: number; + wholSwtrmtPpltnCnt: number; + wholPblSwtrmtPpltnCnt: number; + ts1SwtrmtPpltnCnt: number; + ts2SwtrmtPpltnCnt: number; + ts3SwtrmtPpltnCnt: number; - wholStaaExcpPpltnCnt: number; - unprcsPpltnCnt: number | null; - drtwPrcsPpltnCnt: number | null; - spctnkPpltnCnt: number | null; - staaExcpArea: number; + wholWwtPrcsPpltnCnt: number; + ts1WwtPrcsPpltnCnt: number | null; + ts2WwtPrcsPpltnCnt: number | null; + ts3WwtPrcsPpltnCnt: number | null; - staaPsplyRt: number; - altdPrcsPsplyRt: number; - sewerInstlRt: number; + yetCntnPpltnCnt: number; + staaArea: number; + + wholStaaExcpPpltnCnt: number; + unprcsPpltnCnt: number | null; + drtwPrcsPpltnCnt: number | null; + spctnkPpltnCnt: number | null; + staaExcpArea: number; + + staaPsplyRt: number; + altdPrcsPsplyRt: number; + sewerInstlRt: number; rmrk: string | null; } export interface SewerageSupplyRateReviewRequestParams { - ctpvNm?: string; - sggNm?: string; - statsYr?: string; - sewerBzmnCd?: string; + ctpvNm?: string; + sggNm?: string; + statsYr?: string; + sewerBzmnCd?: string; +} + +export interface statsStatusInfo { + statsCd?: string; + laeSbmsnSttsCd?: string; + statPicSbmsnSttsCd?: string; + rjctOpnnCn?: string; + splmntOpnnCn?: string } export interface SewerageSupplyRateReviewResponseData { sewerageSupplyRateList: SewerageSupplyRateReviewItem[]; + statsStatusInfo: statsStatusInfo; + } export type SewerageSupplyRateReviewApiResponse = ApiResponse & { 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 new file mode 100644 index 000000000..f5c71974b --- /dev/null +++ b/apps/st/src/entities/statistics/model/statistics-total/statistics-sewage-pipe-total-interface.ts @@ -0,0 +1,59 @@ +export interface SewagePipeTotalRequest { + ctpvNm?: string; + sggNm?: string; + statsYr?: string; + sewerBzmnCd?: string; + rowDataYn?: boolean; +} + +export interface SewagePipeTotalItem { + ctpvNm: string; + sggNm: string | null; + admdstNm: string; + admdstCd: string; + statsYr: string; + rowType: string; + goalYr: string; + wholPplMtncPlanPrlg: number; + wholPlanPrlg: number; + wholFcltPrlg: number; + swagePplPsplyRt: number; + cbsysPplMtncPlanArea: number; + cbsysPplMtncPlanPrlg: number; + cbsysPlanPrlg: number; + cbsysFcltPrlg: number; + cbsysSqrePrlg: number; + cbsysCiclPrlg: number; + cbsysOpnPrlg: number; + cbsysGttrPrlg: number; + drtwPplMtncPlanArea: number; + drtwPplMtncPlanPrlg: number; + drtwPplPlanPrlg: number; + drtwPplFcltPrlg: number; + drtwPplSqrePrlg: number; + drtwPplCiclPrlg: number; + exlnPplMtncPlanArea: number; + exlnPplMtncPlanPrlg: number; + exlnPplPlanPrlg: number; + exlnPplFcltPrlg: number; + exlnPplSqrePrlg: number; + exlnPplCiclPrlg: number; + exlnPplOpnPrlg: number; + exlnPplGttrPrlg: number; + cbsysMnhlCnt: number; + sprsysDrtwMnhlCnt: number; + sprsysExlnMnhlCnt: number; + drtwRcvprpCnt: number; + exlnRcvprpCnt: number; + stofcbrCnt: number; + exlnOtflCnt: number; + sewerLdgrEdpPrlg: number; + sewerLdgrEdpRt: number; + rmrk: string | null; +} + +export interface SewagePipeTotalResponse { + status: string; + message: string; + data: SewagePipeTotalItem[]; +} diff --git a/apps/st/src/entities/statistics/model/statistics-verification-report/statistics-verification-report-columns.ts b/apps/st/src/entities/statistics/model/statistics-verification-report/statistics-verification-report-columns.ts new file mode 100644 index 000000000..7ee24a013 --- /dev/null +++ b/apps/st/src/entities/statistics/model/statistics-verification-report/statistics-verification-report-columns.ts @@ -0,0 +1,71 @@ +import type { ColumnInfo } from "@/components/type/DataGrid.type"; + +const READ_ONLY_STYLE = "rg-cell-readonly"; + +export const statisticsVerificationSummaryColumnInfo: ColumnInfo = { + columns: [ + { name: "summaryId", fieldName: "summaryId", header: { text: "summaryId" }, visible: false }, + { name: "sido", fieldName: "sido", header: { text: "시도" }, styleName: READ_ONLY_STYLE }, + { name: "sigungu", fieldName: "sigungu", header: { text: "시군구" }, styleName: READ_ONLY_STYLE }, + { name: "reportType", fieldName: "reportType", header: { text: "구분" }, styleName: READ_ONLY_STYLE }, + { name: "progressStatus", fieldName: "progressStatus", header: { text: "진행상태" }, width: 120, styleName: READ_ONLY_STYLE }, + { name: "writerName", fieldName: "writerName", header: { text: "작성자" }, styleName: READ_ONLY_STYLE }, + { name: "writerContact", fieldName: "writerContact", header: { text: "작성자연락처" }, width: 130, styleName: READ_ONLY_STYLE }, + { name: "writerDept", fieldName: "writerDept", header: { text: "부서" }, styleName: READ_ONLY_STYLE }, + { name: "writerPosition", fieldName: "writerPosition", header: { text: "직급" }, styleName: READ_ONLY_STYLE }, + { name: "approverName", fieldName: "approverName", header: { text: "승인자" }, styleName: READ_ONLY_STYLE }, + { name: "approverDept", fieldName: "approverDept", header: { text: "부서" }, styleName: READ_ONLY_STYLE }, + { name: "approverPosition", fieldName: "approverPosition", header: { text: "직급" }, styleName: READ_ONLY_STYLE }, + { name: "returnReason", fieldName: "returnReason", header: { text: "반려사유" }, width: 220, styleName: READ_ONLY_STYLE }, + ], + columnLayout: [ + "summaryId", + "sido", + "sigungu", + "reportType", + "progressStatus", + "writerName", + "writerContact", + "writerDept", + "writerPosition", + "approverName", + "approverDept", + "approverPosition", + "returnReason", + ], +}; + +export const statisticsVerificationDetailColumnInfo: ColumnInfo = { + columns: [ + { name: "detailId", fieldName: "detailId", header: { text: "detailId" }, visible: false }, + { name: "summaryId", fieldName: "summaryId", header: { text: "summaryId" }, visible: false }, + { name: "sido", fieldName: "sido", header: { text: "시도" }, styleName: READ_ONLY_STYLE }, + { name: "sigungu", fieldName: "sigungu", header: { text: "시군구" }, styleName: READ_ONLY_STYLE }, + { name: "indicator", fieldName: "indicator", header: { text: "지표" }, width: 140, styleName: READ_ONLY_STYLE }, + { name: "detailItem", fieldName: "detailItem", header: { text: "세부항목" }, width: 180, styleName: READ_ONLY_STYLE }, + { name: "verifyDate", fieldName: "verifyDate", header: { text: "검증날짜" }, width: 110, styleName: READ_ONLY_STYLE }, + { name: "errorDetail", fieldName: "errorDetail", header: { text: "오류내역" }, width: 200, styleName: READ_ONLY_STYLE }, + { name: "inputValue", fieldName: "inputValue", header: { text: "입력값" }, width: 90, styleName: READ_ONLY_STYLE }, + { name: "compareValue", fieldName: "compareValue", header: { text: "비교값" }, width: 90, styleName: READ_ONLY_STYLE }, + { name: "status", fieldName: "status", header: { text: "상태" }, width: 90, styleName: READ_ONLY_STYLE }, + { name: "exceptionReason", fieldName: "exceptionReason", header: { text: "예외사유" }, width: 170, styleName: READ_ONLY_STYLE }, + { name: "returnOpinion", fieldName: "returnOpinion", header: { text: "반려의견" }, width: 170, styleName: READ_ONLY_STYLE }, + { name: "approvalYn", fieldName: "approvalYn", header: { text: "승인여부" }, width: 90, styleName: READ_ONLY_STYLE }, + ], + columnLayout: [ + "detailId", + "summaryId", + "sido", + "sigungu", + "indicator", + "detailItem", + "verifyDate", + "errorDetail", + "inputValue", + "compareValue", + "status", + "exceptionReason", + "returnOpinion", + "approvalYn", + ], +}; diff --git a/apps/st/src/entities/statistics/model/statistics-verification-report/statistics-verification-report-mock.ts b/apps/st/src/entities/statistics/model/statistics-verification-report/statistics-verification-report-mock.ts new file mode 100644 index 000000000..a56b16c49 --- /dev/null +++ b/apps/st/src/entities/statistics/model/statistics-verification-report/statistics-verification-report-mock.ts @@ -0,0 +1,288 @@ +import { fakerKO as faker } from "@faker-js/faker"; + +import type { + StatisticsVerificationDetailRow, + StatisticsVerificationFilterState, + StatisticsVerificationOption, + StatisticsVerificationSubFilterState, + StatisticsVerificationSummaryRow, +} from "./statistics-verification-report-types"; + +const NETWORK_DELAY_MS = 300; + +export const SIDO_OPTIONS: StatisticsVerificationOption[] = [ + { label: "전체", value: "ALL" }, + { label: "서울특별시", value: "SEOUL" }, + { label: "부산광역시", value: "BUSAN" }, + { label: "대구광역시", value: "DAEGU" }, + { label: "경기도", value: "GYEONGGI" }, +]; + +const SIGUNGU_OPTIONS_BY_SIDO: Record = + { + ALL: [{ label: "전체", value: "ALL" }], + SEOUL: [ + { label: "전체", value: "ALL" }, + { label: "중구", value: "SEOUL_JUNGGU" }, + { label: "강남구", value: "SEOUL_GANGNAM" }, + ], + BUSAN: [ + { label: "전체", value: "ALL" }, + { label: "해운대구", value: "BUSAN_HAEUNDAE" }, + { label: "수영구", value: "BUSAN_SUYEONG" }, + ], + DAEGU: [ + { label: "전체", value: "ALL" }, + { label: "중구", value: "DAEGU_JUNGGU" }, + { label: "달서구", value: "DAEGU_DALSEO" }, + ], + GYEONGGI: [ + { label: "전체", value: "ALL" }, + { label: "수원시", value: "GYEONGGI_SUWON" }, + { label: "성남시", value: "GYEONGGI_SEONGNAM" }, + ], + }; + +export const STATS_INDICATOR_OPTIONS: StatisticsVerificationOption[] = [ + { label: "통계항목", value: "ALL" }, + { label: "하수도 보급률", value: "SEWERAGE_SUPPLY" }, + { label: "관로 보급 현황", value: "PIPE_STATUS" }, + { label: "분류식 비율", value: "SEPARATED_RATIO" }, +]; + +export const STATUS_OPTIONS: StatisticsVerificationOption[] = [ + { label: "수정완료", value: "DONE" }, + { label: "검토중", value: "REVIEW" }, + { label: "오류", value: "ERROR" }, +]; + +export const APPROVAL_STATUS_OPTIONS: StatisticsVerificationOption[] = [ + { label: "전체", value: "ALL" }, + { label: "승인완료", value: "APPROVED" }, + { label: "반려", value: "REJECTED" }, + { label: "대기", value: "PENDING" }, +]; + +export const DEFAULT_FILTERS: StatisticsVerificationFilterState = { + sido: "ALL", + sigungu: "ALL", + year: "ALL", + statsIndicator: "ALL", +}; + +export const DEFAULT_SUB_FILTERS: StatisticsVerificationSubFilterState = { + status: "DONE", + approvalStatus: "ALL", +}; + +export function getYearOptions(): StatisticsVerificationOption[] { + const thisYear = new Date().getFullYear(); + return [ + { label: "전체", value: "ALL" }, + ...Array.from({ length: 5 }, (_, index) => { + const year = String(thisYear - index - 1); + return { label: year, value: year }; + }), + ]; +} + +export function getSigunguOptions( + sido: string, +): StatisticsVerificationOption[] { + return SIGUNGU_OPTIONS_BY_SIDO[sido] ?? []; +} + +export async function fetchStatisticsVerificationSummaryMock( + filters: StatisticsVerificationFilterState, +): Promise { + await wait(); + faker.seed(20260416); + + const rows = Array.from({ length: 12 }, (_, index) => { + const sido = pickSidoName(filters.sido); + const sigungu = pickSigunguName(filters.sido, filters.sigungu); + const progressStatus = faker.helpers.arrayElement([ + "승인대기", + "승인완료", + "반려", + ]); + return { + summaryId: index + 1, + sido, + sigungu, + reportType: faker.helpers.arrayElement([ + "정기보고", + "수정보고", + "보완보고", + ]), + progressStatus, + writerName: faker.person.lastName() + faker.person.firstName(), + writerContact: faker.phone.number({ style: "national" }), + writerDept: faker.helpers.arrayElement([ + "통계팀", + "하수행정과", + "정책기획과", + ]), + writerPosition: faker.helpers.arrayElement([ + "주무관", + "팀장", + "과장", + ]), + approverName: + progressStatus === "승인대기" + ? "-" + : faker.person.lastName() + faker.person.firstName(), + approverDept: + progressStatus === "승인대기" + ? "-" + : faker.helpers.arrayElement(["검증운영팀", "하수행정과"]), + approverPosition: + progressStatus === "승인대기" + ? "-" + : faker.helpers.arrayElement(["팀장", "과장"]), + returnReason: + progressStatus === "반려" + ? faker.helpers.arrayElement([ + "오류내역 확인 필요", + "예외사유 보강 필요", + "입력값 검증 재요청", + ]) + : "-", + }; + }); + + return rows.filter((row) => { + if (filters.sido !== "ALL" && row.sido !== pickSidoName(filters.sido)) + return false; + if (filters.sigungu !== "ALL") { + const currentSigungu = pickSigunguName( + filters.sido, + filters.sigungu, + ); + if (row.sigungu !== currentSigungu) return false; + } + return true; + }); +} + +export async function fetchStatisticsVerificationDetailMock( + filters: StatisticsVerificationFilterState, + subFilters: StatisticsVerificationSubFilterState, + selectedSummaryId?: number | null, +): Promise { + await wait(); + faker.seed(20260417); + + const rows = Array.from({ length: 24 }, (_, index) => { + const approvalYn = faker.helpers.arrayElement(["승인", "미승인"]); + const status = faker.helpers.arrayElement([ + "수정완료", + "검토중", + "오류", + ]); + const summaryId = faker.number.int({ min: 1, max: 12 }); + return { + detailId: index + 1, + summaryId, + sido: pickSidoName(filters.sido), + sigungu: pickSigunguName(filters.sido, filters.sigungu), + indicator: pickIndicator(filters.statsIndicator), + detailItem: faker.helpers.arrayElement([ + "처리구역면적", + "처리인구", + "분류식관거연장", + "하수관로보급률", + ]), + verifyDate: faker.date + .recent({ days: 20 }) + .toISOString() + .slice(0, 10), + errorDetail: faker.helpers.arrayElement([ + "전년 대비 편차 초과", + "입력값 누락", + "상호검증 불일치", + ]), + inputValue: faker.number.int({ min: 100, max: 5000 }), + compareValue: faker.number.int({ min: 100, max: 5000 }), + status, + exceptionReason: + status === "오류" + ? faker.lorem.sentence({ min: 1, max: 2 }) + : "-", + returnOpinion: + approvalYn === "미승인" + ? faker.helpers.arrayElement([ + "산식 재검토 필요", + "근거자료 보강 요청", + "담당자 확인 필요", + ]) + : "-", + approvalYn, + }; + }); + + return rows.filter((row) => { + if (selectedSummaryId && row.summaryId !== selectedSummaryId) + return false; + if (subFilters.status === "DONE" && row.status !== "수정완료") + return false; + if (subFilters.status === "REVIEW" && row.status !== "검토중") + return false; + if (subFilters.status === "ERROR" && row.status !== "오류") + return false; + if ( + subFilters.approvalStatus === "APPROVED" && + row.approvalYn !== "승인" + ) + return false; + if ( + subFilters.approvalStatus === "REJECTED" && + row.approvalYn !== "미승인" + ) + return false; + return true; + }); +} + +function wait() { + return new Promise((resolve) => setTimeout(resolve, NETWORK_DELAY_MS)); +} + +function pickSidoName(value: string): string { + if (value === "ALL") + return faker.helpers.arrayElement([ + "서울특별시", + "부산광역시", + "대구광역시", + "경기도", + ]); + + const matched = SIDO_OPTIONS.find((option) => option.value === value); + return matched?.label ?? "서울특별시"; +} + +function pickSigunguName(sido: string, sigungu: string): string { + if (sigungu !== "ALL") { + const options = getSigunguOptions(sido); + const matched = options.find((option) => option.value === sigungu); + if (matched) return matched.label; + } + + const options = getSigunguOptions(sido).filter( + (option) => option.value !== "ALL", + ); + if (options.length === 0) return "전체"; + return faker.helpers.arrayElement(options).label; +} + +function pickIndicator(indicator: string): string { + if (indicator === "ALL") + return faker.helpers.arrayElement( + STATS_INDICATOR_OPTIONS.filter((item) => item.value !== "ALL"), + ).label; + + const matched = STATS_INDICATOR_OPTIONS.find( + (option) => option.value === indicator, + ); + return matched?.label ?? "통계항목"; +} diff --git a/apps/st/src/entities/statistics/model/statistics-verification-report/statistics-verification-report-types.ts b/apps/st/src/entities/statistics/model/statistics-verification-report/statistics-verification-report-types.ts new file mode 100644 index 000000000..2cbcd04d9 --- /dev/null +++ b/apps/st/src/entities/statistics/model/statistics-verification-report/statistics-verification-report-types.ts @@ -0,0 +1,49 @@ +export interface StatisticsVerificationFilterState { + sido: string; + sigungu: string; + year: string; + statsIndicator: string; +} + +export interface StatisticsVerificationSubFilterState { + status: string; + approvalStatus: string; +} + +export interface StatisticsVerificationOption { + label: string; + value: string; +} + +export interface StatisticsVerificationSummaryRow { + summaryId: number; + sido: string; + sigungu: string; + reportType: string; + progressStatus: string; + writerName: string; + writerContact: string; + writerDept: string; + writerPosition: string; + approverName: string; + approverDept: string; + approverPosition: string; + returnReason: string; +} + +export interface StatisticsVerificationDetailRow { + detailId: number; + summaryId: number; + sido: string; + sigungu: string; + indicator: string; + detailItem: string; + verifyDate: string; + errorDetail: string; + inputValue: number; + compareValue: number; + status: string; + exceptionReason: string; + returnOpinion: string; + approvalYn: string; +} diff --git a/apps/st/src/shared/config/env.ts b/apps/st/src/shared/config/env.ts index 458dfa3a6..6d1646631 100644 --- a/apps/st/src/shared/config/env.ts +++ b/apps/st/src/shared/config/env.ts @@ -1,7 +1,7 @@ export const PORT = process.env.NEXT_PUBLIC_PORT || "3000"; export const PROXY_HOST = process.env.NEXT_PUBLIC_PROXY_HOST || `http://localhost:${PORT}`; -export const SERVER_API_URL = process.env.NEXT_PUBLIC_SERVER_API_URL; +export const SERVER_API_URL = process.env.SERVER_API_URL; export const AUTH_LOGIN_REDIRECT_URL = process.env.NEXT_PUBLIC_AUTH_LOGIN_REDIRECT_URL || "/auth/login"; export const SITE_ID = process.env.NEXT_PUBLIC_SITE_ID || "ST"; @@ -13,4 +13,4 @@ export const REFRESH_TOKEN = export const AUTH_USER_ID = process.env.NEXT_PUBLIC_AUTH_USER_ID || "token-id"; export const ST_SERVICE_URL = process.env.NEXT_PUBLIC_ST_SERVICE_URL || "http://localhost:8080"; -export const COOKIE_DOMAIN = process.env.NEXT_PUBLIC_COOKIE_DOMAIN; \ No newline at end of file +export const COOKIE_DOMAIN = process.env.NEXT_PUBLIC_COOKIE_DOMAIN;