From 1a24cb7fee10dcfa997e98e190ad9cab93488933 Mon Sep 17 00:00:00 2001 From: ljpark Date: Thu, 10 Jul 2025 16:55:08 +0900 Subject: [PATCH] =?UTF-8?q?=ED=95=98=EC=88=98=EC=A0=95=EB=B9=84=EA=B8=B0?= =?UTF-8?q?=EB=B3=B8=EA=B3=84=ED=9A=8D=20=EB=AA=A9=EB=A1=9D=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/sn/app/(map-layout)/map/SearchModal.tsx | 44 ++-- .../popupComponent/chart/infilChart.tsx | 1 - .../map/components/searchModalReportApi.ts | 223 ++++++++++-------- 3 files changed, 150 insertions(+), 118 deletions(-) diff --git a/apps/sn/app/(map-layout)/map/SearchModal.tsx b/apps/sn/app/(map-layout)/map/SearchModal.tsx index 885507430..6492325c3 100644 --- a/apps/sn/app/(map-layout)/map/SearchModal.tsx +++ b/apps/sn/app/(map-layout)/map/SearchModal.tsx @@ -10,11 +10,16 @@ import { YearRangeSelectBox } from '@/components/ui/YearRangeSelectBox'; import { SidoSigunguSelectBox } from '@/components/ui/SidoSigunguSelectBox'; import { MsSwagePplCol2, SwtrmtFcltCol2, SwtrmtPrcsZoneCol2, MsSwageMhlCol2, ExclnSewagePumpCol2, stofcbrCol2 } from './components/common/table'; import LoadingOverlay from '@/components/ui/LoadingBar'; -import { GovSidoSigunguSearchForm } from '@/components/ui/govSidoSigunguSearchForm'; -import { getDcTree, buildTreeTopDown, sidoList } from './components/searchModalReportApi'; +//import { useDcTree, flattenFolders, useSggCd, useCityCd } from './components/searchModalReportApi'; +import { getDcTree,flattenFolders, useSggCd, useCityCd } from './components/searchModalReportApi'; const API_URL = `${process.env.NEXT_PUBLIC_SEWERAGE_API_URL}`; +type SelectDataType = { + text: string; + val: string; + sido?: string; // 시도 코드 (선택 사항) +} // 탭 const TABS = [ { val: "basic", text: "기본정보" }, @@ -56,9 +61,9 @@ const reportTableHeadlist = [ { id: "sido", title: "시도" }, { id: "sigungu", title: "시군구" }, { id: "approvalYear", title: "승인년도" }, - { id: "treatmentPlantZone", title: "처리장,구역" }, - { id: "reportCount", title: "보고서(00건)" }, - { id: "planMapCount", title: "하수도대장 평면도(00건)" }, + { id: "plantZoneGugun", title: "구분" }, + { id: "reportGubun", title: "보고서구분" }, + { id: "fileName", title: "파일명" }, { id: "regDate", title: "등록일자" }, ]; @@ -100,8 +105,8 @@ const SearchModal: React.FC = ({ const [tableCount, setTableCount] = useState(0); const [curPage, setCurPage] = useState(1); const [basicType, setBasicType] = useState(BASIC_TYPES[0]?.val ?? ""); - const [ctpvCd, setCtpvCd] = useState(0); - const [sigungu, setSigungu] = useState(0); + const [ctpvCd, setCtpvCd] = useState(0); + const [sigungu, setSigungu] = useState(0); const [swageProcessingZone, set처리구역] = useState(""); // 날짜 상태 선언 const [시작1, set시작1] = useState(""); @@ -131,6 +136,14 @@ const SearchModal: React.FC = ({ // 상세 정보 조회를 위한 상태 추가 const [popupFeature, setPopupFeature] = useState(null); + const sidos = useCityCd(); + const siguns = useSggCd(); + // const reportOriginData = getDcTree('경기도').then(data => { + // console.log('reportOriginData:', data); + // return data; + // }); + //const reportData = flattenFolders(reportOriginData); + // 오늘 날짜 구하기 const today = new Date(); const todayYear = today.getFullYear().toString(); @@ -148,6 +161,7 @@ const SearchModal: React.FC = ({ else if (basicType === 'tb_ms_swage_pmstn') dynamicHeadlist = ExclnSewagePumpCol2; // 하수펌프장 else if (basicType === 'tb_ms_stofcbr') dynamicHeadlist = stofcbrCol2; // 우수토실 + // 조회 버튼 클릭 시 1페이지로 이동하도록 수정 const handleSearch = () => { setCurPage(1); // 페이지를 1로 초기화 @@ -232,8 +246,8 @@ const SearchModal: React.FC = ({ if (activeTab === 'report') { const reportOriginData = await getDcTree('경기도'); - const reportData = buildTreeTopDown(reportOriginData, '경기도'); - console.log('dcTree:', reportData); + const reportData = flattenFolders(reportOriginData); + console.log('report count:', reportData.length); setTableData(reportData || []); setTableCount(reportData.length || 0); } else { @@ -1023,15 +1037,9 @@ const SearchModal: React.FC = ({
{/* 시도, 시군구, 하수처리구역 한 줄 */}
- { - setCtpvCd(sidoId); - setSigungu(0); - }} - onSigunguChange={setSigungu} - /> +
+ +
diff --git a/apps/sn/app/(map-layout)/map/components/popupComponent/chart/infilChart.tsx b/apps/sn/app/(map-layout)/map/components/popupComponent/chart/infilChart.tsx index 984391a95..ed39d9548 100644 --- a/apps/sn/app/(map-layout)/map/components/popupComponent/chart/infilChart.tsx +++ b/apps/sn/app/(map-layout)/map/components/popupComponent/chart/infilChart.tsx @@ -78,7 +78,6 @@ const InfilChart = ({ pplMngNo }: ModalOpenProp) => { // API 호출 try { - const dcData = fn.getDcTree(); const data = await fn.selectInftwtAnls(selectedValue); setInftwtAnlss(data ?? []); setInftwtAnlsInfos(fn.getOptionsInftwtAnlsInfo(data)); diff --git a/apps/sn/app/(map-layout)/map/components/searchModalReportApi.ts b/apps/sn/app/(map-layout)/map/components/searchModalReportApi.ts index e78ff2def..0f2305ef5 100644 --- a/apps/sn/app/(map-layout)/map/components/searchModalReportApi.ts +++ b/apps/sn/app/(map-layout)/map/components/searchModalReportApi.ts @@ -1,64 +1,64 @@ import {useState, useEffect} from 'react'; import { getDcData } from '@/components/lib/api'; +import { json } from 'stream/consumers'; const ADDR_URL = process.env.NEXT_PUBLIC_ADDR_URL; -type Node = { - clsfSystmSn: string; - upClsfSystmSn: string; - clsfSystmNm: string; - sortSeq: string; - droppAble: boolean; -} -type OutputNode = { - sido: string; - sigungu: string; - approvalYear: string; - level4: string; - treatmentPlantZone: string; - level6: string; - level6_clsfSystmSn: string; - no? : number; -} +type Folder = { + clsfSystmSn: string; + upClsfSystmSn: string; + clsfSystmNm: string; + children: Folder[]; + }; + +type FlattenedFolder = { + [key: string]: string|number; +}; type SelectDataType = { text: string; val: string; - sigungu?: string; // 시도 코드 (선택 사항) + sido?: 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 { - const response = await fetch(`${ADDR_URL}/api/v1/addr/sgg`, { method: "GET", headers: { accept: "application/json" } }); - if (!response.ok) throw new Error(`시군구 데이터 오류: ${response.status}`); - const res = await response.json(); - const sgg = res.data; - const sggCd = sgg.map((item: any) => ({ + // 시군구 데이터 +export const getSggCd = async (): Promise => { + try { + const response = await fetch(`${ADDR_URL}/api/v1/addr/sgg`, { method: "GET", headers: { accept: "application/json" } }); + if (!response.ok) throw new Error(`시군구 데이터 오류: ${response.status}`); + const res = await response.json(); + const sgg = res.data; + const sggCd = sgg.map((item: any) => ({ val: item.adstDstrCd, - sigungu: item.sigungu, + sido: item.sido, text: item.gugun, - })); - sggCd.unshift({ val: '0', text: '전체' }); // "전체" 옵션 추가 - return sggCd; - } catch (error) { - console.error('시군구 데이터를 가져오는데 실패했습니다:', error); - return [{ val: '0', text: '전체', sigungu: '0' }]; - } -}; + })); + sggCd.unshift({ val: '0', text: '전체' }); // "전체" 옵션 추가 + return sggCd; + } catch (error) { + console.error('시군구 데이터를 가져오는데 실패했습니다:', error); + return [{ val: '0', text: '전체', sido: '0' }]; + } + }; + + // 시도 데이터 +export const getCityCd = async (): Promise => { + try { + const response = await fetch(`${ADDR_URL}/api/v1/addr/city`, { method: "GET", headers: { accept: "application/json" } }); + if (!response.ok) throw new Error(`시도 데이터 오류: ${response.status}`); + const res = await response.json(); + const city = res.data; + const cityCd = city.map((item: any) => ({ + val: item.adstDstrCd, + text: item.sido, + })); + cityCd.unshift({ val: '0', text: '전체' }); + return cityCd; + } catch (error) { + console.error('시도 데이터를 가져오는데 실패했습니다:', error); + return [{ val: '0', text: '전체', sido: '0' }]; + } + }; export const useSggCd = () => { const [data, setData] = useState([]); @@ -75,69 +75,94 @@ export const useSggCd = () => { return data; }; +export const useCityCd = () => { + const [data, setData] = useState([]); + + useEffect(() => { + const fetch = async () => { + const result = await getSggCd(); + if (result) { + setData(result); + } + }; + fetch(); + }, []); + + return data; +}; const isYear = (value: string): boolean => { return /^[0-9]{4}년$/.test(value); }; -export function buildTreeTopDown(treeData: Node[], topNodeNm : string): OutputNode[] { - const level6Names = ["보고서", "요약보고서", "부록", "기타"]; - const childrenMap = new Map(); + export function flattenFolders(data: Folder[]): FlattenedFolder[] { + const result: FlattenedFolder[] = []; + const exceptNodeNm = ["부분","통합","전체"]; + const addNodeNm = ["보고서","요약보고서","부록","기타"]; // level5 + const columnNms: { [key: number]: string } = { 0: "root", 1:"sido", 2:"sigungu", 3:"approvalYear" + ,4:"plantZoneGugun", 5:"reportGubun", 7:"fileName", 8:"regDate" }; - // 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); + function traverse( + node: Folder, + path: { [key: string]: string| number }, + level: number + ) { + if (level === 2 && isYear(node.clsfSystmNm)) { + level++; } - } // for + if (level === 4 && addNodeNm.includes(node.clsfSystmNm)) { + level++; // 레벨을 증가시켜 하위값에 추가되도록 함. + } + + const newPath = { + ...path, + [`${columnNms[level]}`]: node.clsfSystmNm, + }; + + if (node.children.length === 0) { + newPath.no = result.length + 1; + newPath.clsfSystmSn = node.clsfSystmSn; // 파일 조회를 위한 고유번호 + result.push(newPath); + } else { + if (level === 4) { + if (exceptNodeNm.includes(node.clsfSystmNm)) { + level--; // 제외할 노드 이름인 경우, 현재의 레벨에 하위값이 써지도록 함. + } + } + node.children.forEach((child) => { + traverse(child, newPath, level + 1); + }); + } + } + + data.forEach((root) => traverse(root, {}, 0)); + console.log(result.slice(0, 10)); + return result; } - // 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 numberedResult; -} export const getDcTree = async (clsfSystmNm:string) => { - const queryString = 'clsfSystmNm='+clsfSystmNm; - const result = await getDcData(`/api/v1/dc/clsf${queryString ? `?${queryString}` : ''}`); + const cityName = encodeURIComponent(clsfSystmNm); + const queryString = 'clsfSystmNm='+cityName; + console.log("getDcTree queryString:", queryString); + //const result = await getDcData(`/api/v1/dc/clsf${queryString ? `?${queryString}` : ''}`); + const result = await getDcData("/api/v1/dc/clsf?clsfSystmNm=" + clsfSystmNm); return result; }; +export const useDcTree = (clsfSystmNm:string) => { + const [data, setData] = useState([]); + + useEffect(() => { + const fetch = async () => { + const result = await getDcTree(clsfSystmNm); + if (result) { + setData(result); + } + }; + fetch(); + }, [clsfSystmNm]); + + return data; +};