diff --git a/apps/sn/app/(map-layout)/map/SearchModal.tsx b/apps/sn/app/(map-layout)/map/SearchModal.tsx index 28c93bafa..a666b2b6c 100644 --- a/apps/sn/app/(map-layout)/map/SearchModal.tsx +++ b/apps/sn/app/(map-layout)/map/SearchModal.tsx @@ -11,7 +11,7 @@ import { SidoSigunguSelectBox } from '@/components/ui/SidoSigunguSelectBox'; import { MsSwagePplCol2, SwtrmtFcltCol2, SwtrmtPrcsZoneCol2, MsSwageMhlCol2, SewagePump2, stofcbrCol2 } from './components/common/table'; import LoadingOverlay from '@/components/ui/LoadingBar'; import { GovSidoSigunguSearchForm } from '@/components/ui/govSidoSigunguSearchForm'; -import { useDcTree } from './components/searchModalReportApi'; +import { getDcTree, buildTreeTopDown, sidoList } from './components/searchModalReportApi'; const API_URL = `${process.env.NEXT_PUBLIC_SEWERAGE_API_URL}`; @@ -53,11 +53,10 @@ const operationTableHeadlist = [ // 하수도정비 기본계획 보고서 const reportTableHeadlist = [ { id: "no", title: "NO" }, - { id: "approvalYear", title: "승인년도" }, { id: "sido", title: "시도" }, { id: "sigungu", title: "시군구" }, - { id: "treatmentPlant", title: "하수처리장" }, - { id: "treatmentZone", title: "하수처리구역" }, + { id: "approvalYear", title: "승인년도" }, + { id: "treatmentPlantZone", title: "처리장,구역" }, { id: "reportCount", title: "보고서(00건)" }, { id: "planMapCount", title: "하수도대장 평면도(00건)" }, { id: "regDate", title: "등록일자" }, @@ -201,6 +200,7 @@ const SearchModal: React.FC = ({ } }, [curPage]); + // 검색 API 호출 함수 const fetchFilteredData = async () => { setIsLoading(true); // 로딩 시작 @@ -229,10 +229,11 @@ const SearchModal: React.FC = ({ if (cqlFilters.length > 0) queryParams.append('cqlFiter', cqlFilters.join(' and ')); if (activeTab === 'report') { - const data = flattenFolders(dcData); - console.log('dcTree:', data); - setTableData(data || []); - setTableCount(data.length || []); + const reportOriginData = await getDcTree('경기도'); + const reportData = buildTreeTopDown(reportOriginData, '경기도'); + console.log('dcTree:', reportData); + setTableData(reportData || []); + setTableCount(reportData.length || 0); } else { console.log('API URL:', `${API_URL}/v1/fclts?${queryParams.toString()}`); const res = await axios.get(`${API_URL}/v1/fclts?${queryParams.toString()}`); diff --git a/apps/sn/app/(map-layout)/map/components/searchModalReportApi.ts b/apps/sn/app/(map-layout)/map/components/searchModalReportApi.ts index 304cdee95..e78ff2def 100644 --- a/apps/sn/app/(map-layout)/map/components/searchModalReportApi.ts +++ b/apps/sn/app/(map-layout)/map/components/searchModalReportApi.ts @@ -1,26 +1,45 @@ import {useState, useEffect} from 'react'; import { getDcData } from '@/components/lib/api'; -import moment from "moment"; const ADDR_URL = process.env.NEXT_PUBLIC_ADDR_URL; -export type SelectDataType = { - val: string; - text: string; - sido?: string; // 시도 코드 -}; +type Node = { + clsfSystmSn: string; + upClsfSystmSn: string; + clsfSystmNm: string; + sortSeq: string; + droppAble: boolean; +} -type Folder = { - fldrSn: string; - upFldrSn: string; - fldrNm: string; - sort_seq: number; - children: Folder[]; -}; +type OutputNode = { + sido: string; + sigungu: string; + approvalYear: string; + level4: string; + treatmentPlantZone: string; + level6: string; + level6_clsfSystmSn: string; + no? : number; +} -type FlattenedFolder = { - [key: string]: string; -}; +type SelectDataType = { + text: string; + val: string; + sigungu?: string; // 시도 코드 (선택 사항) +} +export const sidoList: { id: number, name: string }[] = [ + { id: 0, name: '전체' }, + { id: 26, name: '부산광역시' }, + { id: 28, name: '인천광역시' }, + { id: 30, name: '대전광역시' }, + { id: 41, name: '경기도' }, + { id: 42, name: '강원도' }, + { id: 44, name: '충청남도' }, + { id: 45, name: '전라북도' }, + { id: 46, name: '전라남도' }, + { id: 47, name: '경상북도' }, + { id: 48, name: '경상남도' }, +]; // 시군구 데이터 const getSggCd = async (): Promise => { try { @@ -30,14 +49,14 @@ const getSggCd = async (): Promise => { const sgg = res.data; const sggCd = sgg.map((item: any) => ({ val: item.adstDstrCd, - sido: item.sido, + sigungu: item.sigungu, text: item.gugun, })); sggCd.unshift({ val: '0', text: '전체' }); // "전체" 옵션 추가 return sggCd; } catch (error) { console.error('시군구 데이터를 가져오는데 실패했습니다:', error); - return [{ val: '0', text: '전체', sido: '0' }]; + return [{ val: '0', text: '전체', sigungu: '0' }]; } }; @@ -61,61 +80,63 @@ const isYear = (value: string): boolean => { return /^[0-9]{4}년$/.test(value); }; -function flattenFolders(data: Folder[]): FlattenedFolder[] { - const result: FlattenedFolder[] = []; - const sggCd = await getSggCd(); - function traverse( - node: Folder, - path: { [key: string]: string }, - level: number - ) { - if (level === 2) { - if (!sggCd.some(item => item.text === node.fldrNm)) { - } - if (isYear(node.fldrNm)) { - level++; - } - } - const newPath = { - ...path, - //[`level${level}_fldrSn`]: node.fldrSn, - [`level${level}`]: node.fldrNm, - }; +export function buildTreeTopDown(treeData: Node[], topNodeNm : string): OutputNode[] { + const level6Names = ["보고서", "요약보고서", "부록", "기타"]; + const childrenMap = new Map(); - if (node.children.length === 0) { - result.push(newPath); - } else { - node.children.forEach((child) => { - traverse(child, newPath, level + 1); - }); + // 1. 부모 → 자식 연결 테이블 구성 + treeData.forEach((node) => { + if (!childrenMap.has(node.upClsfSystmSn)) { + childrenMap.set(node.upClsfSystmSn, []); } + childrenMap.get(node.upClsfSystmSn)!.push(node); + }); + const result: OutputNode[] = []; + + // 2. Top-down recursive 탐색 함수 + function traverse(path: string[], currentSn: string) { + const children = childrenMap.get(currentSn) || []; + + for (const child of children) { + const newPath = [...path, child.clsfSystmNm]; + if (level6Names.includes(child.clsfSystmNm)) { + const sido = newPath[0] || ""; + let [sigungu="", approvalYear="", level4="", treatmentPlantZone = ""] = newPath.slice(1, 5); + if (isYear(sigungu)) { + treatmentPlantZone = level4; level4 = approvalYear; approvalYear = sigungu; sigungu = ""; + } + if (level6Names.includes(treatmentPlantZone)) treatmentPlantZone = ""; + result.push({ + sido, + sigungu, + approvalYear, + level4, + treatmentPlantZone, + level6: child.clsfSystmNm, + level6_clsfSystmSn: child.clsfSystmSn, + }); + } else { + traverse(newPath, child.clsfSystmSn); + } + } // for } - data.forEach((root) => traverse(root, {}, 0)); + // 3. topNodeNm에서 시작 + const topNode = treeData.find((n) => n.clsfSystmNm === topNodeNm); + if (topNode) { + traverse([topNode.clsfSystmNm], topNode.clsfSystmSn); + } + const numberedResult = result.map((item, index) => ({ + ...item, + no: index + 1, + })); - return result; + return numberedResult; } -export const useDcTree = () => { - const [data, setData] = useState([]); - - useEffect(() => { - const fetch = async () => { - const result = await getDcData(`/api/v1/dc/tree`); - if (result) { - const flattenedData = flattenFolders(result); - setData(flattenedData); - } - }; - fetch(); - }, []); - - return data; -}; - -export const getDcTree = async () => { - const queryString = ''; - const result = await getDcData(`/api/v1/dc/tree${queryString ? `?${queryString}` : ''}`); +export const getDcTree = async (clsfSystmNm:string) => { + const queryString = 'clsfSystmNm='+clsfSystmNm; + const result = await getDcData(`/api/v1/dc/clsf${queryString ? `?${queryString}` : ''}`); return result; };