diff --git a/apps/st/src/components/module/DataGrid.tsx b/apps/st/src/components/module/DataGrid.tsx index cdf0e286d..520a6c47f 100644 --- a/apps/st/src/components/module/DataGrid.tsx +++ b/apps/st/src/components/module/DataGrid.tsx @@ -155,16 +155,16 @@ function updateHeaderSortIndicator( function createOnSortingHandler( columns: { fieldName?: string; name: string; mergeCells?: boolean; sortdisable?: boolean; valueType?: ValueType }[], setRowData: React.Dispatch[]>>, - sortDirRef: React.MutableRefObject>, - originalRowsRef: React.MutableRefObject[]>> + sortDirRef: { current: Record }, + originalRowsRef: { current: Record[]> } ) { const fieldNames = [...new Set(columns.map((c) => c.fieldName).filter((x): x is string => typeof x === "string"))]; // mergeCells: true인 컬럼 찾기 (시군구 등) - 그룹 필드로 사용 // 시군구를 우선으로 찾고, 없으면 첫 번째 mergeCells 컬럼 사용 const mergeCellCols = columns.filter((c) => c.mergeCells === true && c.fieldName); - const mergeCellCol = mergeCellCols.find((c) => c.fieldName === '시군구') || mergeCellCols[0]; + const mergeCellCol = mergeCellCols.find((c) => c.fieldName === 'gugun') || mergeCellCols[0]; const groupField = mergeCellCol?.fieldName || null; - const orderByField = '구분'; // 고정값 또는 설정 가능하게 할 수 있음 + const orderByField = 'rgnNm'; // 고정값 또는 설정 가능하게 할 수 있음 const hasMergeCells = mergeCellCols.length > 0; return function onSorting(g: unknown, fields: unknown[], _directions: unknown[]) { diff --git a/apps/st/src/components/module/SearchForm.tsx b/apps/st/src/components/module/SearchForm.tsx index 0b50ecf89..91d823ed6 100644 --- a/apps/st/src/components/module/SearchForm.tsx +++ b/apps/st/src/components/module/SearchForm.tsx @@ -118,6 +118,18 @@ const SearchForm = (props: SearchFormProps = {}) => { })); }; + const handleSggChange = (value: string) => { + const selectedBsnm = value === 'ALL' + ? (sidoList?.data.sidoList.find((v) => v.sido === values.sido)?.bsnmCd || '') + : (sggList?.data?.find((v) => v.gugun === value)?.bsnmCd || ''); + + setValues((prev) => ({ + ...prev, + gugun: value, + bsnmCd: selectedBsnm, + })); + }; + useEffect(() => { props.onValuesChange?.(values); }, [values]); @@ -151,7 +163,7 @@ const SearchForm = (props: SearchFormProps = {}) => { - diff --git a/apps/st/src/components/statistics/review/SeweragePenetrationRateRvw.tsx b/apps/st/src/components/statistics/review/SeweragePenetrationRateRvw.tsx index 950f43988..036af18b8 100644 --- a/apps/st/src/components/statistics/review/SeweragePenetrationRateRvw.tsx +++ b/apps/st/src/components/statistics/review/SeweragePenetrationRateRvw.tsx @@ -1,5 +1,6 @@ -import { useState } from "react"; -import { DataGrid } from "@rsone/components"; +import { useState, useMemo } from "react"; +// import { DataGrid } from "@rsone/components"; +import DataGrid from "@/components/module/DataGrid"; import SearchForm from "@/components/module/SearchForm"; import SaveButton from "@/components/button/SaveButton"; import ApproveButton from "@/components/button/ApproveButton"; @@ -12,12 +13,27 @@ import { sewerageSupplyRateReviewApi } from "@/entities/statistics/api/statistic import type { SewerageSupplyRateReviewItem } from "@/entities/statistics/model/statistics-management-interface"; import { SeweragePenetrationRateInfoList } from "@/components/util/column/review/SeweragePenetrationRateRvwColumn"; import type { SearchFormValues } from "@/components/type/SearchForm.type"; +import { toGridRows } from "@/components/util/grid/gridFunction"; // 하수도 통계 검토 01. 하수도 보급률 const SeweragePenetrationRateRvw = () => { const { setMessage: alert } = useAlertStore((state) => state); - -const [rowData, setRowData] = useState([]); + + const columnInfo = useMemo(() => { + const { columns, columnLayout, fixedColumns, rowStartNum } = SeweragePenetrationRateInfoList; + return { + columnLayout, + fixedColumns, + rowStartNum, + columns: columns.map((col) => ({ + ...col, + editable: false as const, + styleName: col.styleName ?? ("rg-cell-readonly" as const), + })), + }; + }, []); + + const [rowData, setRowData] = useState([]); const [isLoading, setIsLoading] = useState(false); const handleReset = () => { @@ -29,10 +45,10 @@ const [rowData, setRowData] = useState([]); setIsLoading(true); const response = await sewerageSupplyRateReviewApi.getSewerageSupplyRateReviewList({ - sido: "경기도", - gugun: "과천시", - yr: 2024, - bsnmCd: "4129000000" + sido: values.sido, + gugun: values.gugun === 'ALL' ? '' : values.gugun, + bsnmCd: values.bsnmCd, + yr: values.year, }); console.log("네트워크 응답 전체:", response); @@ -42,14 +58,13 @@ const [rowData, setRowData] = useState([]); console.log("받아온 리스트:", rawList); - const convertedData: SewerageSupplyRateReviewItem[] = rawList.map((item) => ({ ...item, speclCntrplnAr: !item.speclCntrplnAr ? '해당없음' : item.speclCntrplnAr, })); - setRowData(convertedData); + setRowData(toGridRows(convertedData)); } else { setRowData([]); @@ -80,12 +95,12 @@ const [rowData, setRowData] = useState([]); ); diff --git a/apps/st/src/components/util/grid/footerSummary.ts b/apps/st/src/components/util/grid/footerSummary.ts index cbf60db84..39825a0dd 100644 --- a/apps/st/src/components/util/grid/footerSummary.ts +++ b/apps/st/src/components/util/grid/footerSummary.ts @@ -48,8 +48,8 @@ export const getFooterAvgSummary = (grid: GridView, column: DataColumn) => { return Math.floor(avg * 10) / 10; } -// "구분" 필드의 고유값들을 찾는 함수 (첫 번째, 두 번째) -const getDivisionValues = (grid: GridView, divisionFieldName: string = '구분'): string[] => { +// 구분 필드의 고유값들을 찾는 함수 (첫 번째, 두 번째). 기본 필드명 'rgnNm' +const getDivisionValues = (grid: GridView, divisionFieldName: string = 'rgnNm'): string[] => { const dataSource = grid.getDataSource(); const rowCount = dataSource.getRowCount(); const uniqueValues = new Set(); @@ -65,13 +65,13 @@ const getDivisionValues = (grid: GridView, divisionFieldName: string = '구분') return Array.from(uniqueValues); }; -// "구분" 필드 값에 따라 필터링하여 합계 계산하는 함수 +// 구분 필드 값에 따라 필터링하여 합계 계산하는 함수. 기본 필드명 'rgnNm' // divisionIndex: 0 = 첫 번째 고유값, 1 = 두 번째 고유값 export const getFooterSummaryByDivision = ( - grid: GridView, - column: DataColumn, + grid: GridView, + column: DataColumn, divisionIndex: number, - divisionFieldName: string = '구분' + divisionFieldName: string = 'rgnNm' ) => { const dataSource = grid.getDataSource(); const rowCount = dataSource.getRowCount(); @@ -107,13 +107,13 @@ export const getFooterSummaryByDivision = ( return sum; }; -// "구분" 필드 값에 따라 필터링하여 평균 계산하는 함수 +// 구분 필드 값에 따라 필터링하여 평균 계산하는 함수. 기본 필드명 'rgnNm' // divisionIndex: 0 = 첫 번째 고유값, 1 = 두 번째 고유값 export const getFooterAvgSummaryByDivision = ( - grid: GridView, - column: DataColumn, + grid: GridView, + column: DataColumn, divisionIndex: number, - divisionFieldName: string = '구분' + divisionFieldName: string = 'rgnNm' ) => { const dataSource = grid.getDataSource(); const rowCount = dataSource.getRowCount(); @@ -173,67 +173,51 @@ const createAvgFooter = (format: string) => ({ }, }); -// "구분" 값에 따라 필터링하는 Footer 배열 생성 함수 (2줄) -const createSumFooterArrayByDivision = (format: string) => [ +// 구분 값에 따라 필터링하는 Footer 배열 생성 함수 (2줄). 기본 필드명 'rgnNm' +const createSumFooterArrayByDivision = (format: string, divisionFieldName: string = 'rgnNm') => [ { numberFormat: format, valueCallback: ( - grid: GridView, - column: DataColumn, - footerIndex: number, - columnFooter: unknown, + grid: GridView, + column: DataColumn, + footerIndex: number, + columnFooter: unknown, value: number | string | undefined - ) => { - // footerIndex: 0 = 첫 번째 footer 행 (첫 번째 "구분" 값), 1 = 두 번째 footer 행 (두 번째 "구분" 값) - // footerIndex를 divisionIndex로 사용 - return getFooterSummaryByDivision(grid, column, footerIndex); - }, + ) => getFooterSummaryByDivision(grid, column, footerIndex, divisionFieldName), }, { numberFormat: format, valueCallback: ( - grid: GridView, - column: DataColumn, - footerIndex: number, - columnFooter: unknown, + grid: GridView, + column: DataColumn, + footerIndex: number, + columnFooter: unknown, value: number | string | undefined - ) => { - // footerIndex: 0 = 첫 번째 footer 행 (첫 번째 "구분" 값), 1 = 두 번째 footer 행 (두 번째 "구분" 값) - // footerIndex를 divisionIndex로 사용 - return getFooterSummaryByDivision(grid, column, footerIndex); - }, + ) => getFooterSummaryByDivision(grid, column, footerIndex, divisionFieldName), }, ]; -// "구분" 값에 따라 필터링하는 평균 Footer 배열 생성 함수 (2줄) -const createAvgFooterArrayByDivision = (format: string) => [ +// 구분 값에 따라 필터링하는 평균 Footer 배열 생성 함수 (2줄). 기본 필드명 'rgnNm' +const createAvgFooterArrayByDivision = (format: string, divisionFieldName: string = 'rgnNm') => [ { numberFormat: format, valueCallback: ( - grid: GridView, - column: DataColumn, - footerIndex: number, - columnFooter: unknown, + grid: GridView, + column: DataColumn, + footerIndex: number, + columnFooter: unknown, value: number | string | undefined - ) => { - // footerIndex: 0 = 첫 번째 footer 행 (첫 번째 "구분" 값), 1 = 두 번째 footer 행 (두 번째 "구분" 값) - // footerIndex를 divisionIndex로 사용 - return getFooterAvgSummaryByDivision(grid, column, footerIndex); - }, + ) => getFooterAvgSummaryByDivision(grid, column, footerIndex, divisionFieldName), }, { numberFormat: format, valueCallback: ( - grid: GridView, - column: DataColumn, - footerIndex: number, - columnFooter: unknown, + grid: GridView, + column: DataColumn, + footerIndex: number, + columnFooter: unknown, value: number | string | undefined - ) => { - // footerIndex: 0 = 첫 번째 footer 행 (첫 번째 "구분" 값), 1 = 두 번째 footer 행 (두 번째 "구분" 값) - // footerIndex를 divisionIndex로 사용 - return getFooterAvgSummaryByDivision(grid, column, footerIndex); - }, + ) => getFooterAvgSummaryByDivision(grid, column, footerIndex, divisionFieldName), }, ]; diff --git a/apps/st/src/components/util/grid/gridFunction.ts b/apps/st/src/components/util/grid/gridFunction.ts index 173a7ac6d..28755a7f8 100644 --- a/apps/st/src/components/util/grid/gridFunction.ts +++ b/apps/st/src/components/util/grid/gridFunction.ts @@ -1,10 +1,15 @@ -import type { SewerageSupplyRateItem } from "@/entities/statistics/model/statistics-input-interface"; - -export function toGridRows(items: SewerageSupplyRateItem[]): Record[] { +/** + * 객체 배열을 그리드 row 형식으로 변환 (null/undefined → ""). + * 어떤 엔티티 타입이든 공통 사용 가능. + */ +export function toGridRows( + items: T[] +): Record[] { return items.map((item) => { const row: Record = {}; for (const [key, value] of Object.entries(item)) { - row[key] = value === null || value === undefined ? "" : value; + row[key] = + value === null || value === undefined ? "" : (value as string | number); } return row; }); diff --git a/apps/st/src/entities/statistics/model/statistics-interface.ts b/apps/st/src/entities/statistics/model/statistics-interface.ts index 5ff8a5ed7..b909eba92 100644 --- a/apps/st/src/entities/statistics/model/statistics-interface.ts +++ b/apps/st/src/entities/statistics/model/statistics-interface.ts @@ -4,6 +4,11 @@ export interface Sido { bsnmCd: string; } +export interface SearchCondition { + yearList: string[]; + sidoList: Sido[]; +} + export interface Sgg { adstDstrCd: string; gugun: string; @@ -17,6 +22,6 @@ export interface ApiResponse { } export interface StatisticsService { - getSidoList(): Promise>; + getSidoList(): Promise>; getSggList(sidoName: string): Promise>; } \ No newline at end of file diff --git a/apps/st/src/entities/statistics/model/statistics-management-interface.ts b/apps/st/src/entities/statistics/model/statistics-management-interface.ts index 858859b28..b8078a186 100644 --- a/apps/st/src/entities/statistics/model/statistics-management-interface.ts +++ b/apps/st/src/entities/statistics/model/statistics-management-interface.ts @@ -83,7 +83,7 @@ export type SewerageSupplyRateReviewApiResponse = ApiResponse