From 80156bd349f3c658cf2aa71c4807699771105f8f Mon Sep 17 00:00:00 2001 From: JIeunhuh Date: Thu, 31 Jul 2025 10:02:21 +0900 Subject: [PATCH 01/11] =?UTF-8?q?=ED=8C=9D=EC=97=85=20=ED=98=84=ED=99=A9?= =?UTF-8?q?=20=EC=88=98=EC=A0=95=EC=82=AC=ED=95=AD=20=EB=B3=80=EA=B2=BD=20?= =?UTF-8?q?(=EC=8B=9C=EC=84=A4=EB=AC=BC=20=ED=81=B4=EB=A6=AD=20=EC=8B=9C?= =?UTF-8?q?=20api=20=EB=AA=A8=EB=91=90=20=EC=A1=B0=ED=9A=8C=20=EB=90=98?= =?UTF-8?q?=EB=8A=94=20=EB=B6=80=EB=B6=84=20=EC=9E=91=EC=97=85=20=EC=A4=91?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wr/app/map/mainMap/page.tsx | 27 ++-- apps/wr/app/map/newWindowPopup.tsx | 206 +++++++++++++++-------------- 2 files changed, 126 insertions(+), 107 deletions(-) diff --git a/apps/wr/app/map/mainMap/page.tsx b/apps/wr/app/map/mainMap/page.tsx index cc71e69aa..c9d58c422 100644 --- a/apps/wr/app/map/mainMap/page.tsx +++ b/apps/wr/app/map/mainMap/page.tsx @@ -11,7 +11,7 @@ const Map = () => { const gisMaps = useRef(null); const [isOpen, setIsOpen] = useState(false); // 팝업 열기/닫기 상태 const [isSearchModalOpen, setIsSearchModalOpen] = useState(true); // SearchModal 열림 상태 - const [featureData, setFeatureData] = useState(null); // 선택된 피쳐 데이터 + const [featureData, setFeatureData] = useState(null); // 선택된 피쳐 데이터 const [checkedLayers, setCheckedLayers] = useState([]); const [checkedLayersData, setCheckedLayersData] = useState([]); @@ -114,7 +114,9 @@ const Map = () => { } else { console.warn("OpenLayers Feature를 찾을 수 없습니다:", data); } - + const currentId = data?.fid || data?.id || data?.ftrIdn; + const lastId = featureData?.fid || featureData?.id || featureData?.ftrIdn; + if (featureData && currentId === lastId) return; // 이전과 같으면 무시 setFeatureData(data); setIsOpen(true); setIsSearchModalOpen(false); @@ -130,7 +132,17 @@ const Map = () => { // /> // ); // }; - + const renderDetailView = useCallback( + (props: any) => ( + + ), + [handlePopupClose, checkedLayers, checkedLayersData] // 필요 시 의존성 넣기 + ); return (
{ gisMaps={gisMaps} isMiniMap={false} searchView={undefined} - detailView={(props: any) => ( - - )} + detailView={renderDetailView} onFeatureSelect={onFeatureSelect} setIsOpen={setIsOpen} setFeatureData={setFeatureData} diff --git a/apps/wr/app/map/newWindowPopup.tsx b/apps/wr/app/map/newWindowPopup.tsx index 76529b31b..ea13d85de 100644 --- a/apps/wr/app/map/newWindowPopup.tsx +++ b/apps/wr/app/map/newWindowPopup.tsx @@ -15,7 +15,7 @@ import { useGisContext } from "@common/business_components/ui/map/Gis"; import type { DemandCompType } from '@common/business_components/ui/sdmInsertForm/form'; import { HeadMakeColSpan } from "@common/business_components/ui/table/HeadMakeColSpan"; import moment from 'moment'; -import React, { useCallback, useEffect, useState } from 'react'; +import React, { useCallback, useEffect, useRef, useState } from 'react'; interface NewWindowPopupProps { featureData: any; @@ -87,7 +87,7 @@ const NewWindowPopup: React.FC = ({ pbnst, pubHasuInit, } = useWrStore((state) => state); - const { pubFacHeadList, pbnstHasuRes } = pbnst; + const { pubFacHeadList, pbnstHasuRes, pbnstHasuResCount } = pbnst; const [hasuList, setHasuList] = useState(pbnstHasuRes || []); const [isRuseReg, setIsRuseReg] = useState<{ data: any | undefined; @@ -100,11 +100,6 @@ const NewWindowPopup: React.FC = ({ coSttn, coSttnInit, coSttnSearch, - // getDemandCompList, - // regDemandCompany, - // updateDemandCompany, - // coSttnTxtSearch, - // demandCompList, } = useWrStore((state) => state); const [mngTable, setMngTable] = useState(); const [mngPage, setMngPage] = useState(1); @@ -112,11 +107,11 @@ const NewWindowPopup: React.FC = ({ const [isDmdModal, setIsDmdModal] = useState<{ isMoal: boolean; data: any | undefined; - }>({ isMoal: false, data: undefined}); + }>({ isMoal: false, data: undefined }); const [mngList, setMngList] = useState(coSttnRes || []); ///////////////////////////////////////////// 피쳐데이터 선택 시 각 시설별 데이터 처리 /////////////////////////////////////////////// const getSelectedLayers = () => { - console.log(1) + console.trace('호출확인',1) let result = null; const layers = gisMap ? gisMap.getLayers().getArray().filter((l: any) => l.getVisible()) : []; // gisMap이 null인지 확인 후 레이어 가져오기 let featureLayer: { get: (key: string) => any } = { get: () => null }; @@ -128,8 +123,6 @@ const NewWindowPopup: React.FC = ({ } }); result = featureLayer.get('lyrNm'); - console.log(result) - return result; }; const fetchpbnstDetail = async (featureCd: string) => { @@ -143,7 +136,7 @@ const NewWindowPopup: React.FC = ({ }; const fetchHasuDetail = async (featureCd: string, data: string) => { try { - const rainRes = await postFectch(`/prcsFclt/ruseInfo/1/10/A/0/0/1/SW/${featureCd}`, [data], fetchType.COMMON) + const rainRes = await postFectch(`/prcsFclt/ruseInfo/1/20/A/0/0/1/SW/${featureCd}`, [data], fetchType.COMMON) setHasuList( rainRes.data.map((item: any) => { return { @@ -166,7 +159,7 @@ const NewWindowPopup: React.FC = ({ const res = await getFetch(fetchType.WR, `/reuseFc/waterreuseFc/1/20/0/${featureCd}`) setRswtrList(res) } - else if(layerNm =='수요처'){ //수요처 + else if (layerNm == '수요처') { //수요처 const res = await getFetch(fetchType.WR, `/reuseFc/request/1/20/0/${featureCd}`) setMngList(res) } @@ -190,36 +183,43 @@ const NewWindowPopup: React.FC = ({ if (htdflList.length > 0) setHtdflDetailModal({ isOpen: true, data: htdflList[0] }); }, [htdflList]) useEffect(() => { - if(mngList.length > 0) setIsDmdModal({ isMoal: true, data: [mngList[0]] }); - },[mngList]) + if (mngList.length > 0) setIsDmdModal({ isMoal: true, data: [mngList[0]] }); + }, [mngList]) + const lastCalledIdRef = useRef(null); + const [layerId, setLayerId] = useState(); +useEffect(() => { + const layers = getSelectedLayers(); + setLayerId(layers); +}, []); // 최초 렌더링 시 호출 - const layerId = getSelectedLayers(); - console.log(layerId) - if (featureData && typeof featureData.getProperties === "function") { - const selectedVal = featureData.getProperties(); - let featureCd = layerId == '하수재이용시설' ? selectedVal?.swtrmt_fclt_cd : layerId == '빗물이용시설' || layerId == '중수도시설' || layerId == '온배수시설' || layerId == '수요처' ? selectedVal?.bdst_nm : layerId == '하수처리시설' ? selectedVal?.swtrmt_fclt_nm : null; - if (featureCd && featureCd !== fcltPk) { - if (layerId == '하수재이용시설') { - featureCd = selectedVal?.swtrmt_fclt_cd; - setFcltPk(featureCd); // 현재 호출된 featureCd 저장 - setActiveTab(featureCd) - fetchpbnstDetail(featureCd); // 상세정보 - } - else if (layerId == '하수처리시설') { - featureCd = selectedVal?.swtrmt_fclt_nm; - setFcltPk(featureCd); // 현재 호출된 featureCd 저장 - fetchHasuDetail(featureCd, selectedVal?.swtrmt_fclt_cd); - } - else { - featureCd = selectedVal?.bdst_nm; - setFcltPk(featureCd) - if (layerId) { - detainSttn(featureCd, layerId); - } - } - setViewMode("detail") + useEffect(() => { + if (!featureData || typeof featureData.getProperties !== "function" || featureData.getProperties() == '') return; + const selectedProps = featureData.getProperties(); + const currentId = featureData.getId?.() || selectedProps?.id || selectedProps?.fid; + // 유효하지 않은 ID는 아예 무시 + if (!currentId || typeof currentId !== 'string') return; + // 이전과 동일하면 무시 + if (!currentId || currentId === lastCalledIdRef.current) return; + lastCalledIdRef.current = currentId // 새로운 피처일 때만 API 호출 + const layerId = getSelectedLayers?.(); + if (!layerId || typeof layerId !== 'string') return; + const featureCd = + layerId === '하수재이용시설' ? selectedProps?.swtrmt_fclt_cd : + ['빗물이용시설', '중수도시설', '온배수시설', '수요처'].includes(layerId) ? selectedProps?.bdst_nm : + layerId === '하수처리시설' ? selectedProps?.swtrmt_fclt_nm : + null; + if (!featureCd || featureCd === fcltPk) return; + if (layerId === '하수재이용시설') { + fetchpbnstDetail(featureCd); + } else if (layerId === '하수처리시설') { + fetchHasuDetail(featureCd, selectedProps?.swtrmt_fclt_cd); + } else { + detainSttn(featureCd, layerId); } - } else if (layerId === null && seletectedLayerNm) { setViewMode("list") } + setFcltPk(featureCd); + setViewMode("detail"); + setActiveTab(featureCd); + }, [featureData]); ///////////////////////////////////////////// 피쳐데이터 선택 시 각 시설별 데이터 처리 /////////////////////////////////////////////// ///////////////////////////////////////////// 하수재이용시설 ////////////////////////////////////////////////// @@ -238,12 +238,14 @@ const NewWindowPopup: React.FC = ({ // 재이용 현황 초기값 useEffect(() => { - if (!pbnstSttn.isInit) { + if (!pbnstSttn.isInit && seletectedLayerNm === '하수재이용시설') { getFlcts() } }, [seletectedLayerNm, pbnstSttn.isInit, getFlcts]); useEffect(() => { - pbnSttnInit("1", "15", { + if(seletectedLayerNm !== '하수재이용시설') return; + console.trace('check', 1) + pbnSttnInit("1", "20", { partValue: "A", sidoValue: '0', sigunValue: "0", @@ -254,7 +256,7 @@ const NewWindowPopup: React.FC = ({ sidoText: "", sigunText: "" }, String(new Date().getFullYear()), pbnstSttn.fclts); - }, [fclts, pbnSttnInit, pbnstSttn.fclts]) + }, [fclts, pbnSttnInit, pbnstSttn.fclts, seletectedLayerNm]) useEffect(() => { setBdstTable(pbnstSttn.pbnstSttnRes); }, [seletectedLayerNm, pbnstSttn.pbnstSttnRes]); @@ -271,10 +273,11 @@ const NewWindowPopup: React.FC = ({ // 재이용시설 조회 // 재이용시설 페이징처리 관련 함수 const onPageChangeHandler = useCallback((page: number) => { + if(seletectedLayerNm!== '하수재이용시설') return; pbnstSttnSearch( false, String(page), - String(13), + String(20), { partValue: "A", sidoValue: "0", @@ -318,6 +321,7 @@ const NewWindowPopup: React.FC = ({ useEffect(() => { + if(seletectedLayerNm !== '빗물이용시설') return; if (!rainSttn.isInit) { rainSttnInit(false, 1, "20", "0", '', code.cityCd, code.sggCd); } @@ -355,6 +359,7 @@ const NewWindowPopup: React.FC = ({ }, [code, rainSttnRes, rainSttn.rainSttHeadList]); useEffect(() => { + if(seletectedLayerNm !== '빗물이용시설') return; rainSttnSearch(false, rainPage, "20", '', '', code.cityCd, code.sggCd); }, [code.cityCd, code.sggCd, rainPage, rainSttnSearch]); ///////////////////////////////////////////// 빗물 재이용 ////////////////////////////////////////////////// @@ -370,12 +375,14 @@ const NewWindowPopup: React.FC = ({ } } useEffect(() => { + if(seletectedLayerNm !== '중수도시설') return; if (!rswtSttn.isInit) { rswtSttnInit(false, 1, "20", "0", '', code.cityCd, code.sggCd); } }, [code.cityCd, code.sggCd, rswtSttn.isInit, rswtSttnInit]); useEffect(() => { + if(seletectedLayerNm !== '중수도시설') return; const data = tableData(rswtSttnRes, { code }); const chData = data .map((item: customRswtBdst) => { @@ -409,6 +416,7 @@ const NewWindowPopup: React.FC = ({ // }, [rswtSttn.excelList]) useEffect(() => { + if(seletectedLayerNm !== '중수도시설') return; rswtSttnSearch(false, rswtrPage, "20", '', '', code.cityCd, code.sggCd); }, [code.cityCd, code.sggCd, rswtSttnSearch, rswtrPage]); ///////////////////////////////////////////// 중수도 시설 ////////////////////////////////////////////////// @@ -422,12 +430,14 @@ const NewWindowPopup: React.FC = ({ } }; useEffect(() => { + if(seletectedLayerNm !== '온배수시설') return; if (!htdflSttn.isInit) { initHtdfl(false, 1, "20", "0", '', code.cityCd, code.sggCd); } }, [code.cityCd, code.sggCd, htdflSttn.isInit, initHtdfl]); useEffect(() => { + if(seletectedLayerNm !== '온배수시설') return; const data = tableData(htdflSttnRes, { code }); const chData = data .map((item: any) => ({ @@ -441,6 +451,7 @@ const NewWindowPopup: React.FC = ({ }, [code, htdflSttnRes, htdflSttn.htdflSttHeadList]); useEffect(() => { + if(seletectedLayerNm !== '온배수시설') return; htdflSttnSearch(true, htdflPage, "20", '', '', code.cityCd, code.sggCd); }, [code.cityCd, code.sggCd, htdflPage, htdflSttnSearch]); ///////////////////////////////////////////// 온배수 시설 ////////////////////////////////////////////////// @@ -452,6 +463,7 @@ const NewWindowPopup: React.FC = ({ } }, [code, pbnstHasuRes]); useEffect(() => { + if(seletectedLayerNm !== '하수처리시설') return; if (!pbnst.isInit) { pubHasuInit("1", "0"); } @@ -470,8 +482,9 @@ const NewWindowPopup: React.FC = ({ ///////////////////////////////////////////// 수요처 관리 ////////////////////////////////////////////////// useEffect(() => { + if(seletectedLayerNm !== '수요처') return; if (!coSttn.isInit) { - coSttnInit("1", "13", "0"); + coSttnInit("1", "20", "0"); } }, [coSttn.isInit, coSttnInit]); const coMngCellClick = (index: number | undefined) => { @@ -481,6 +494,7 @@ const NewWindowPopup: React.FC = ({ } }; useEffect(() => { + if(seletectedLayerNm !== '수요처') return; if (coSttnRes) { if (coSttnRes?.length > 0 && code.cityCd.length > 0) { const data = tableData(coSttnRes, { code }); @@ -496,21 +510,21 @@ const NewWindowPopup: React.FC = ({ } }, [coSttnRes, code]); useEffect(() => { - coSttnSearch(String(mngPage), "13", String(0)); + if(seletectedLayerNm !== '수요처') return; + coSttnSearch(String(mngPage), "20", String(0)); }, [coSttnSearch, mngPage]); ///////////////////////////////////////////// 수요처 관리 ////////////////////////////////////////////////// // 체크된 레이어에 따른 조건부 컴포넌트 렌더링 함수 const renderConditionalContent = () => { const actTabData = tabData.find((tab) => tab.id === activeTab); - let layerId: string | undefined | null = getSelectedLayers(); if (layerId === null || layerId === undefined) { - layerId = seletectedLayerNm || ''; + setLayerId(seletectedLayerNm || ''); } //하수처리시설 const isPrcsFclt = layerId == '하수처리시설' // 재이용시설 - const isPbnst = layerId == '재이용시설'; + const isPbnst = layerId == '하수재이용시설'; // 빗물이용시설 const isRain = layerId == '빗물이용시설'; // 중수도시설 @@ -518,7 +532,7 @@ const NewWindowPopup: React.FC = ({ // 온배수시설 const isHtdfl = layerId == '온배수시설'; // 수요처관리 - const isReuseMng = layerId == '수요처관리'; + const isReuseMng = layerId == '수요처'; if (isPrcsFclt) { if (viewMode == 'list') { return ( @@ -530,8 +544,8 @@ const NewWindowPopup: React.FC = ({ = ({ } if (isReuseMng) { if (viewMode == 'detail') { - return ( -
-
- - </div> - {/* 높이 조정 */} - <UiTable - tableData={isDmdModal?.data || []} - headlist={coSttHeadList.slice(0, 3)} - pageSize={1} - total={0} - headName={"위치정보"} - cellClick={() => { }} - onPageChange={() => { }} - /> - <UiTable - tableData={isDmdModal?.data || []} - headlist={coSttHeadList.slice(3)} - pageSize={1} - total={0} - headName={"기본정보"} - cellClick={() => { }} - onPageChange={() => { }} - /> + return ( + <div style={{ overflowX: 'hidden' }}> + <div className="flex justify-between items-center mb-4"> + <Title title="수요처 상세정보" /> </div> - ) - } else if(viewMode == 'list') { - return ( <div> + {/* 높이 조정 */} + <UiTable + tableData={isDmdModal?.data || []} + headlist={coSttHeadList.slice(0, 3)} + pageSize={1} + total={0} + headName={"위치정보"} + cellClick={() => { }} + onPageChange={() => { }} + /> + <UiTable + tableData={isDmdModal?.data || []} + headlist={coSttHeadList.slice(3)} + pageSize={1} + total={0} + headName={"기본정보"} + cellClick={() => { }} + onPageChange={() => { }} + /> + </div> + ) + } else if (viewMode == 'list') { + return (<div> <div className="flex justify-between items-center"> - <Title title="수요처 현황" /> - </div> - <UiTable - headName={[]} - tableData={mngTable || []} - headlist={coSttHeadList} - pageSize={20} - total={coSttnResCount + 10 || 0} - cellClick={(index: number | undefined) => { - coMngCellClick(index); - // setUpdateData(coSttnRes[index as number]); - }} - onPageChange={(page) => { - setMngPage(page); - }} - ></UiTable></div>) + <Title title="수요처 현황" /> + </div> + <UiTable + headName={[]} + tableData={mngTable || []} + headlist={coSttHeadList} + pageSize={20} + total={coSttnResCount + 10 || 0} + cellClick={(index: number | undefined) => { + coMngCellClick(index); + // setUpdateData(coSttnRes[index as number]); + }} + onPageChange={(page) => { + setMngPage(page); + }} + ></UiTable></div>) } } return null; // 조건에 맞는 콘텐츠가 없을 경우 From 7ee7a0fc69891b0e62589f9fb7fb0406f4c4ff0d Mon Sep 17 00:00:00 2001 From: JIeunhuh <entks301@naver.com> Date: Thu, 31 Jul 2025 14:38:14 +0900 Subject: [PATCH 02/11] =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=E3=85=8C?= =?UTF-8?q?=ED=8A=B8=20=EB=B6=84=EB=A6=AC=EC=A4=91=20..?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wr/app/map/components/hasuFacility.tsx | 89 +++++ apps/wr/app/map/components/reuseFacility.tsx | 147 ++++++++ apps/wr/app/map/mainMap/page.tsx | 3 +- apps/wr/app/map/newWindowPopup.tsx | 356 +++---------------- apps/wr/app/map/util.ts | 58 +++ 5 files changed, 344 insertions(+), 309 deletions(-) create mode 100644 apps/wr/app/map/components/hasuFacility.tsx create mode 100644 apps/wr/app/map/components/reuseFacility.tsx create mode 100644 apps/wr/app/map/util.ts diff --git a/apps/wr/app/map/components/hasuFacility.tsx b/apps/wr/app/map/components/hasuFacility.tsx new file mode 100644 index 000000000..d93d4c30a --- /dev/null +++ b/apps/wr/app/map/components/hasuFacility.tsx @@ -0,0 +1,89 @@ +import { useWrStore } from "@/app/store"; +import type { pubHasuType } from "@/app/store/ruseFcltMng/fcltReg/pbnstReg/type"; +import { tableData } from "@/components/lib/func/commom"; +import { Title, UiTable } from "@common/business_components/ui"; +import { useEffect, useState } from "react"; +interface Props { + seletectedLayerNm: string; + viewMode: 'list' | 'detail' | 'nothing'; + onFeatureSelect: (feature: any) => void; + onClose: () => void; + isRuseReg: { data: any | undefined }; +} +const HasuFacility = ({ seletectedLayerNm, viewMode, onClose, onFeatureSelect, isRuseReg }: Props) => { + const { + code, + pbnst, + pubHasuInit, + } = useWrStore((state) => state); + const { pubFacHeadList, pbnstHasuRes, pbnstHasuResCount } = pbnst; + const [hasuTable, setHasuTable] = useState<pubHasuType[]>(); + const [hasuPage, setHasuPage] = useState<number>(1); + useEffect(() => { + if (code.sggCd.length && pbnstHasuRes.length > 0) { + setHasuTable(tableData(pbnstHasuRes, { code })); + } + }, [code, pbnstHasuRes]); + useEffect(() => { + if (seletectedLayerNm !== '하수처리시설') return; + if (!pbnst.isInit) { + pubHasuInit("1", "0"); + } + }, [pbnst.isInit, pubHasuInit]); + useEffect(() => { + if (seletectedLayerNm !== '하수처리시설') return; + pubHasuInit(String(hasuPage), "0"); + }, [hasuPage, pubHasuInit]); + + const hasuCellClick = (index: number | undefined) => { + if (index !== undefined && pbnstHasuRes !== undefined) { + onFeatureSelect(pbnstHasuRes[index]); + onClose(); + } + }; + return (viewMode == 'list' ? ( + <div style={{ overflowY: 'auto' }}> + <div className="flex justify-between items-center mb-4"> + <Title title="하수처리시설 현황" /> + </div> + {/* 높이 조정 */} + <UiTable + tableData={hasuTable || []} + headlist={pubFacHeadList} + pageSize={11} + total={pbnstHasuResCount || 0} + headName={""} + cellClick={hasuCellClick} + onPageChange={setHasuPage} + /> + </div> + ) : + ( + <div style={{ overflowX: 'hidden' }}> + <div className="flex justify-between items-center mb-4"> + <Title title="하수처리시설 상세정보" /> + </div> + {/* 높이 조정 */} + <UiTable + tableData={isRuseReg?.data || []} + headlist={pubFacHeadList.slice(0, 4)} + pageSize={1} + total={0} + headName={"위치정보"} + cellClick={() => { }} + onPageChange={() => { }} + /> + <UiTable + tableData={isRuseReg?.data || []} + headlist={pubFacHeadList.slice(4)} + pageSize={1} + total={0} + headName={"기본정보"} + cellClick={() => { }} + onPageChange={() => { }} + /> + </div> + ) + ) +} +export default HasuFacility; \ No newline at end of file diff --git a/apps/wr/app/map/components/reuseFacility.tsx b/apps/wr/app/map/components/reuseFacility.tsx new file mode 100644 index 000000000..dd18a657e --- /dev/null +++ b/apps/wr/app/map/components/reuseFacility.tsx @@ -0,0 +1,147 @@ +import { useWrStore } from "@/app/store"; +import type { customPbnst } from "@/app/store/ruseFcltMng/fcltReg/pbnstReg/type"; +import type { WaterReuseFacility } from "@/app/store/ruseFcltMng/fcltSttn/pbnstSttn/type"; +import { PbnstSttnModal } from "@/components/ui/modal/sttn/pbnstSttnModal/pbnstSttnModal"; +import { Title, UITab, UiTable } from "@common/business_components/ui"; +import { HeadMakeColSpan } from "@common/business_components/ui/table/HeadMakeColSpan"; +import { useState, useEffect, useCallback } from "react"; +interface Props { + seletectedLayerNm: string; + viewMode: 'list' | 'detail' | 'nothing'; + setFcltPk: (pk: string) => void; + onFeatureSelect: (feature: any) => void; + onClose: () => void; + actTabData: { id: string; name: string; data: any } | undefined; + tabData: { id: string; name: string; data: any }[]; +} +const ResueFacility = ({ viewMode, seletectedLayerNm, tabData, actTabData, setFcltPk, onFeatureSelect, onClose}: Props) => { + const [activeTab, setActiveTab] = useState<string>(''); // 현재 활성화된 탭 + // 하수재이용시설 변수 + const { + pbnstSttn, + pbnstSttnSearch, + pbnSttnInit, + getFlcts + } = useWrStore((state) => state); + const { fclts, pbnstSttnResCount } = pbnstSttn + const [bdstTable, setBdstTable] = useState<WaterReuseFacility[]>() + // 재이용시설 cellClick 이벤트 (상세정보) + const pbnstCellClick = (index: number | undefined) => { + if ( + index !== undefined && + pbnstSttn.pbnstSttnRes && + pbnstSttn.pbnstSttnRes[index] + ) { + setFcltPk(pbnstSttn.pbnstSttnRes[index].pbnstRuseFcltId); + onFeatureSelect(pbnstSttn.pbnstSttnRes[index]); + onClose() + } + }; + + // 재이용 현황 초기값 + useEffect(() => { + console.log('재이용시설 초기값 호출', viewMode) + if (seletectedLayerNm !== '하수재이용시설') return; + if (!pbnstSttn.isInit) { + getFlcts() + } + }, [seletectedLayerNm, pbnstSttn.isInit]); + useEffect(() => { + if (seletectedLayerNm !== '하수재이용시설') return; + pbnSttnInit("1", "20", { + partValue: "A", + sidoValue: '0', + sigunValue: "0", + regionValue: "", + unitWaterShedValue: "", + middleRegionValue: "", + envPartValue: "", + sidoText: "", + sigunText: "" + }, String(new Date().getFullYear()), pbnstSttn.fclts); + }, [fclts, pbnSttnInit, pbnstSttn.fclts, seletectedLayerNm]) + useEffect(() => { + setBdstTable(pbnstSttn.pbnstSttnRes); + }, [seletectedLayerNm, pbnstSttn.pbnstSttnRes]); + + // 재이용시설 조회 + // 재이용시설 페이징처리 관련 함수 + const onPageChangeHandler = useCallback((page: number) => { + if (seletectedLayerNm !== '하수재이용시설') return; + pbnstSttnSearch( + false, + String(page), + String(20), + { + partValue: "A", + sidoValue: "0", + sigunValue: "0", + regionValue: "", + unitWaterShedValue: "", + middleRegionValue: "", + envPartValue: "", + sidoText: "", + sigunText: "", + }, + "", + String(new Date().getFullYear()), fclts + ); + }, [fclts, pbnstSttnSearch]); + // FIXME: 윤선화차장님 요구사항으로 엑셀 다운로드 삭제. + // const excelClick = useCallback(() => { + // // if (codeData) pbnstSttnSearch(1, "10000", codeData); + // excelDownload( + // [[]], + // "하수재이용시설 현황", + // "sample_pbnstSttn.xlsx", + // "A1:U2" + // ); + // }, [pbnstSttnSearch]); + return ( + viewMode == 'detail' ? + // 재이용시설 상세 + <> + <div className="flex justify-between items-center mb-4"> + <Title title="하수재이용시설 상세정보" /> + </div> + <div className="flex justify-between items-center mb-8"> + <UITab + defaultValue={activeTab} + tabList={tabData.map((tab) => ({ + value: tab.id, + label: tab.name, + onClick: () => setActiveTab(tab.id), + }))} + /> + </div> + <PbnstSttnModal + data={actTabData?.data[0] || ({} as customPbnst)} + titleVisible={false} + /> + </> : + <div style={{ overflowY: 'auto' }}> + <div className="flex justify-between items-center"> + <Title title="하수재이용시설 현황" /> + {/* <div className="flex gap-2 mt-8"> + <Button className={btnClass} color="primary" onClick={excelClick}> + 엑셀다운로드 + </Button> + </div> */} + </div> + <UiTable + headName={[]} + tableData={bdstTable || []} + headlist={pbnstSttn.pbnstSttHeadList} + pageSize={20} + total={pbnstSttnResCount || 0} + cellClick={(index: number | undefined) => { + pbnstCellClick(index); + }} + onPageChange={onPageChangeHandler} + > + <HeadMakeColSpan upHeadList={pbnstSttn.pbnstSttNodeList} /> + </UiTable> + </div> + ) +} +export default ResueFacility; \ No newline at end of file diff --git a/apps/wr/app/map/mainMap/page.tsx b/apps/wr/app/map/mainMap/page.tsx index c9d58c422..56c9e823d 100644 --- a/apps/wr/app/map/mainMap/page.tsx +++ b/apps/wr/app/map/mainMap/page.tsx @@ -135,13 +135,14 @@ const Map = () => { const renderDetailView = useCallback( (props: any) => ( <NewWindowPopup + key={featureData?.fid || featureData?.id || featureData?.ftrIdn} onClose={handlePopupClose} checkedLayers={checkedLayers} checkedLayersData={checkedLayersData} {...props} /> ), - [handlePopupClose, checkedLayers, checkedLayersData] // 필요 시 의존성 넣기 + [checkedLayers, checkedLayersData] // 필요 시 의존성 넣기 ); return ( <div className="bg-container"> diff --git a/apps/wr/app/map/newWindowPopup.tsx b/apps/wr/app/map/newWindowPopup.tsx index ea13d85de..d8e6b6338 100644 --- a/apps/wr/app/map/newWindowPopup.tsx +++ b/apps/wr/app/map/newWindowPopup.tsx @@ -2,20 +2,20 @@ import NewWindowPortal from '@/app/map/newWindowPortal'; import { useWrStore } from "@/app/store"; import type { customHtdflBdst } from "@/app/store/ruseFcltMng/fcltReg/htdflReg"; -import type { customPbnst, pubHasuType } from "@/app/store/ruseFcltMng/fcltReg/pbnstReg/type"; +import type { pubHasuType } from "@/app/store/ruseFcltMng/fcltReg/pbnstReg/type"; import type { customRainBdst } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; import type { customRswtBdst } from "@/app/store/ruseFcltMng/fcltReg/rswtReg"; -import type { WaterReuseFacility } from "@/app/store/ruseFcltMng/fcltSttn/pbnstSttn/type"; -import { fetchType, getFetch, postFectch } from "@/components/lib/api"; import { formatPhoneNumber, tableData } from "@/components/lib/func/commom"; -import { PbnstSttnModal } from "@/components/ui/modal/sttn/pbnstSttnModal/pbnstSttnModal"; import SttnDetailModal from "@/components/ui/modal/sttn/sttnDetailModal/sttnDetailModal"; -import { Title, UITab, UiTable } from '@common/business_components'; +import { Title, UiTable } from '@common/business_components'; import { useGisContext } from "@common/business_components/ui/map/Gis"; import type { DemandCompType } from '@common/business_components/ui/sdmInsertForm/form'; import { HeadMakeColSpan } from "@common/business_components/ui/table/HeadMakeColSpan"; import moment from 'moment'; import React, { useCallback, useEffect, useRef, useState } from 'react'; +import ResueFacility from './components/reuseFacility'; +import { detainSttn, fetchHasuDetail, fetchpbnstDetail } from './util'; +import HasuFacility from './components/hasuFacility'; interface NewWindowPopupProps { featureData: any; @@ -44,15 +44,7 @@ const NewWindowPopup: React.FC<NewWindowPopupProps> = ({ }, [onClose]); // 하수재이용시설 변수 - const { - code, - pbnstSttn, - pbnstSttnSearch, - pbnSttnInit, - getFlcts - } = useWrStore((state) => state); - const { fclts, pbnstSttnResCount } = pbnstSttn - const [bdstTable, setBdstTable] = useState<WaterReuseFacility[]>() + const {code} = useWrStore((state) => state); // 빗물재이용시설 변수 const { rainSttn, rainSttnSearch, rainSttnInit } = useWrStore( @@ -82,18 +74,11 @@ const NewWindowPopup: React.FC<NewWindowPopupProps> = ({ const [htdflPage, setHtdflPage] = useState(1) const [htdflList, setHtdflList] = useState<customHtdflBdst[]>(htdflSttnRes || []); - //하수처리시설 변수 - const { - pbnst, - pubHasuInit, - } = useWrStore((state) => state); - const { pubFacHeadList, pbnstHasuRes, pbnstHasuResCount } = pbnst; - const [hasuList, setHasuList] = useState<pubHasuType[]>(pbnstHasuRes || []); + // 하수처리시설 변수 + const [hasuList, setHasuList] = useState<pubHasuType[]>([]); const [isRuseReg, setIsRuseReg] = useState<{ data: any | undefined; }>({ data: undefined }); - const [hasuTable, setHasuTable] = useState<pubHasuType[]>(); - const [hasuPage, setHasuPage] = useState<number>(1); // 수요처관리 변수 const { @@ -111,7 +96,7 @@ const NewWindowPopup: React.FC<NewWindowPopupProps> = ({ const [mngList, setMngList] = useState<DemandCompType[]>(coSttnRes || []); ///////////////////////////////////////////// 피쳐데이터 선택 시 각 시설별 데이터 처리 /////////////////////////////////////////////// const getSelectedLayers = () => { - console.trace('호출확인',1) + console.trace('호출확인', 1) let result = null; const layers = gisMap ? gisMap.getLayers().getArray().filter((l: any) => l.getVisible()) : []; // gisMap이 null인지 확인 후 레이어 가져오기 let featureLayer: { get: (key: string) => any } = { get: () => null }; @@ -125,50 +110,6 @@ const NewWindowPopup: React.FC<NewWindowPopupProps> = ({ result = featureLayer.get('lyrNm'); return result; }; - const fetchpbnstDetail = async (featureCd: string) => { - try { - const response = await getFetch("wr", `/reuseFc/pubFc/${featureCd}`); - // 중복 데이터 추가 방지 - setTabDataFunc(response, featureCd); - } catch (error) { - console.error("Error fetching data:", error); - } - }; - const fetchHasuDetail = async (featureCd: string, data: string) => { - try { - const rainRes = await postFectch(`/prcsFclt/ruseInfo/1/20/A/0/0/1/SW/${featureCd}`, [data], fetchType.COMMON) - setHasuList( - rainRes.data.map((item: any) => { - return { - ...item, - fctCpc: Number(item.fctCpc) || 0, - ruse: item.ruse === false ? "N" : "Y", - }; - }) - ) - } catch (error) { - console.error("Error fetching data:", error); - } - }; - const detainSttn = async (featureCd: string, layerNm: string) => { - if (layerNm == '빗물이용시설') { //빗물이용시설 - const res = await getFetch(fetchType.WR, `/reuseFc/rainyInfoList/1/20/0/${featureCd}`) - setRainList(res) - } - else if (layerNm == '중수도시설') { //중수도 시설 - const res = await getFetch(fetchType.WR, `/reuseFc/waterreuseFc/1/20/0/${featureCd}`) - setRswtrList(res) - } - else if (layerNm == '수요처') { //수요처 - const res = await getFetch(fetchType.WR, `/reuseFc/request/1/20/0/${featureCd}`) - setMngList(res) - } - else { - const res = await getFetch(fetchType.WR, `/drain/facilityList/1/20/0/${featureCd}`) - setHtdflList(res) - setHtdflDetailModal({ isOpen: true, data: htdflSttnRes[0] }); - } - } // 상세 목록 데이터 상태 처리 useEffect(() => { if (hasuList.length > 0) setIsRuseReg({ data: [hasuList[0]] }); @@ -186,12 +127,11 @@ const NewWindowPopup: React.FC<NewWindowPopupProps> = ({ if (mngList.length > 0) setIsDmdModal({ isMoal: true, data: [mngList[0]] }); }, [mngList]) const lastCalledIdRef = useRef<string | null>(null); - const [layerId, setLayerId] = useState<string|null>(); -useEffect(() => { - const layers = getSelectedLayers(); - setLayerId(layers); -}, []); // 최초 렌더링 시 호출 - + const [layerId, setLayerId] = useState<string | null>(); + useEffect(() => { + const layers = getSelectedLayers(); + setLayerId(layers); + }, []); // 최초 렌더링 시 호출 useEffect(() => { if (!featureData || typeof featureData.getProperties !== "function" || featureData.getProperties() == '') return; const selectedProps = featureData.getProperties(); @@ -205,106 +145,28 @@ useEffect(() => { if (!layerId || typeof layerId !== 'string') return; const featureCd = layerId === '하수재이용시설' ? selectedProps?.swtrmt_fclt_cd : - ['빗물이용시설', '중수도시설', '온배수시설', '수요처'].includes(layerId) ? selectedProps?.bdst_nm : - layerId === '하수처리시설' ? selectedProps?.swtrmt_fclt_nm : - null; + ['빗물이용시설', '중수도시설', '온배수시설', '수요처'].includes(layerId) ? selectedProps?.bdst_nm : + layerId === '하수처리시설' ? selectedProps?.swtrmt_fclt_nm : + null; if (!featureCd || featureCd === fcltPk) return; - if (layerId === '하수재이용시설') { - fetchpbnstDetail(featureCd); - } else if (layerId === '하수처리시설') { - fetchHasuDetail(featureCd, selectedProps?.swtrmt_fclt_cd); - } else { - detainSttn(featureCd, layerId); - } setFcltPk(featureCd); - setViewMode("detail"); setActiveTab(featureCd); + setViewMode("detail"); + if (layerId === '하수재이용시설') { + fetchpbnstDetail(featureCd, setTabData); + } else if (layerId === '하수처리시설') { + fetchHasuDetail(featureCd, selectedProps?.swtrmt_fclt_cd).then((data) => { setHasuList(data) }) + } else { + detainSttn(featureCd, layerId).then((res) => { + if (layerId === '빗물이용시설') setRainList(res); + else if (layerId === '중수도시설') setRswtrList(res); + else if (layerId === '온배수시설'){ setHtdflList(res); setHtdflDetailModal({ isOpen: true, data: htdflSttnRes[0] });} + else if (layerId === '수요처') setMngList(res); + }); + } }, [featureData]); ///////////////////////////////////////////// 피쳐데이터 선택 시 각 시설별 데이터 처리 /////////////////////////////////////////////// - ///////////////////////////////////////////// 하수재이용시설 ////////////////////////////////////////////////// - // 재이용시설 cellClick 이벤트 (상세정보) - const pbnstCellClick = (index: number | undefined) => { - if ( - index !== undefined && - pbnstSttn.pbnstSttnRes && - pbnstSttn.pbnstSttnRes[index] - ) { - setFcltPk(pbnstSttn.pbnstSttnRes[index].pbnstRuseFcltId); - onFeatureSelect(pbnstSttn.pbnstSttnRes[index]); - onClose() - } - }; - - // 재이용 현황 초기값 - useEffect(() => { - if (!pbnstSttn.isInit && seletectedLayerNm === '하수재이용시설') { - getFlcts() - } - }, [seletectedLayerNm, pbnstSttn.isInit, getFlcts]); - useEffect(() => { - if(seletectedLayerNm !== '하수재이용시설') return; - console.trace('check', 1) - pbnSttnInit("1", "20", { - partValue: "A", - sidoValue: '0', - sigunValue: "0", - regionValue: "", - unitWaterShedValue: "", - middleRegionValue: "", - envPartValue: "", - sidoText: "", - sigunText: "" - }, String(new Date().getFullYear()), pbnstSttn.fclts); - }, [fclts, pbnSttnInit, pbnstSttn.fclts, seletectedLayerNm]) - useEffect(() => { - setBdstTable(pbnstSttn.pbnstSttnRes); - }, [seletectedLayerNm, pbnstSttn.pbnstSttnRes]); - - // 재이용시설 상세정보 - const setTabDataFunc = (res: any, pk: string) => { - setTabData((prev) => { - if (prev.some((tab) => tab.id === pk)) { - return prev; // 이미 존재하는 탭이면 추가하지 않음 - } - return [...prev, { id: pk, name: `재이용시설 ${prev.length + 1}`, data: res }]; - }); - } - // 재이용시설 조회 - // 재이용시설 페이징처리 관련 함수 - const onPageChangeHandler = useCallback((page: number) => { - if(seletectedLayerNm!== '하수재이용시설') return; - pbnstSttnSearch( - false, - String(page), - String(20), - { - partValue: "A", - sidoValue: "0", - sigunValue: "0", - regionValue: "", - unitWaterShedValue: "", - middleRegionValue: "", - envPartValue: "", - sidoText: "", - sigunText: "", - }, - "", - String(new Date().getFullYear()), fclts - ); - }, [fclts, pbnstSttnSearch]); - // FIXME: 윤선화차장님 요구사항으로 엑셀 다운로드 삭제. - // const excelClick = useCallback(() => { - // // if (codeData) pbnstSttnSearch(1, "10000", codeData); - // excelDownload( - // [[]], - // "하수재이용시설 현황", - // "sample_pbnstSttn.xlsx", - // "A1:U2" - // ); - // }, [pbnstSttnSearch]); - ///////////////////////////////////////////// 하수재이용시설 ////////////////////////////////////////////////// - ///////////////////////////////////////////// 빗물 재이용 ////////////////////////////////////////////////// // 빗물재이용시설 cellClick 이벤트 (상세정보) const rainCellClick = (index: number | undefined, headId: string) => { @@ -321,7 +183,7 @@ useEffect(() => { useEffect(() => { - if(seletectedLayerNm !== '빗물이용시설') return; + if (seletectedLayerNm !== '빗물이용시설') return; if (!rainSttn.isInit) { rainSttnInit(false, 1, "20", "0", '', code.cityCd, code.sggCd); } @@ -359,7 +221,7 @@ useEffect(() => { }, [code, rainSttnRes, rainSttn.rainSttHeadList]); useEffect(() => { - if(seletectedLayerNm !== '빗물이용시설') return; + if (seletectedLayerNm !== '빗물이용시설') return; rainSttnSearch(false, rainPage, "20", '', '', code.cityCd, code.sggCd); }, [code.cityCd, code.sggCd, rainPage, rainSttnSearch]); ///////////////////////////////////////////// 빗물 재이용 ////////////////////////////////////////////////// @@ -375,14 +237,14 @@ useEffect(() => { } } useEffect(() => { - if(seletectedLayerNm !== '중수도시설') return; + if (seletectedLayerNm !== '중수도시설') return; if (!rswtSttn.isInit) { rswtSttnInit(false, 1, "20", "0", '', code.cityCd, code.sggCd); } }, [code.cityCd, code.sggCd, rswtSttn.isInit, rswtSttnInit]); useEffect(() => { - if(seletectedLayerNm !== '중수도시설') return; + if (seletectedLayerNm !== '중수도시설') return; const data = tableData(rswtSttnRes, { code }); const chData = data .map((item: customRswtBdst) => { @@ -416,7 +278,7 @@ useEffect(() => { // }, [rswtSttn.excelList]) useEffect(() => { - if(seletectedLayerNm !== '중수도시설') return; + if (seletectedLayerNm !== '중수도시설') return; rswtSttnSearch(false, rswtrPage, "20", '', '', code.cityCd, code.sggCd); }, [code.cityCd, code.sggCd, rswtSttnSearch, rswtrPage]); ///////////////////////////////////////////// 중수도 시설 ////////////////////////////////////////////////// @@ -430,14 +292,14 @@ useEffect(() => { } }; useEffect(() => { - if(seletectedLayerNm !== '온배수시설') return; + if (seletectedLayerNm !== '온배수시설') return; if (!htdflSttn.isInit) { initHtdfl(false, 1, "20", "0", '', code.cityCd, code.sggCd); } }, [code.cityCd, code.sggCd, htdflSttn.isInit, initHtdfl]); useEffect(() => { - if(seletectedLayerNm !== '온배수시설') return; + if (seletectedLayerNm !== '온배수시설') return; const data = tableData(htdflSttnRes, { code }); const chData = data .map((item: any) => ({ @@ -451,38 +313,14 @@ useEffect(() => { }, [code, htdflSttnRes, htdflSttn.htdflSttHeadList]); useEffect(() => { - if(seletectedLayerNm !== '온배수시설') return; + if (seletectedLayerNm !== '온배수시설') return; htdflSttnSearch(true, htdflPage, "20", '', '', code.cityCd, code.sggCd); }, [code.cityCd, code.sggCd, htdflPage, htdflSttnSearch]); ///////////////////////////////////////////// 온배수 시설 ////////////////////////////////////////////////// - ///////////////////////////////////////////// 하수처리 시설 ////////////////////////////////////////////////// - useEffect(() => { - if (code.sggCd.length && pbnstHasuRes.length > 0) { - setHasuTable(tableData(pbnstHasuRes, { code })); - } - }, [code, pbnstHasuRes]); - useEffect(() => { - if(seletectedLayerNm !== '하수처리시설') return; - if (!pbnst.isInit) { - pubHasuInit("1", "0"); - } - }, [pbnst.isInit, pubHasuInit]); - useEffect(() => { - pubHasuInit(String(hasuPage), "0"); - }, [hasuPage, pubHasuInit]); - - const hasuCellClick = (index: number | undefined) => { - if (index !== undefined && pbnstHasuRes !== undefined) { - onFeatureSelect(pbnstHasuRes[index]); - onClose(); - } - }; - ///////////////////////////////////////////// 하수처리 시설 ////////////////////////////////////////////////// - ///////////////////////////////////////////// 수요처 관리 ////////////////////////////////////////////////// useEffect(() => { - if(seletectedLayerNm !== '수요처') return; + if (seletectedLayerNm !== '수요처') return; if (!coSttn.isInit) { coSttnInit("1", "20", "0"); } @@ -494,7 +332,7 @@ useEffect(() => { } }; useEffect(() => { - if(seletectedLayerNm !== '수요처') return; + if (seletectedLayerNm !== '수요처') return; if (coSttnRes) { if (coSttnRes?.length > 0 && code.cityCd.length > 0) { const data = tableData(coSttnRes, { code }); @@ -510,7 +348,7 @@ useEffect(() => { } }, [coSttnRes, code]); useEffect(() => { - if(seletectedLayerNm !== '수요처') return; + if (seletectedLayerNm !== '수요처') return; coSttnSearch(String(mngPage), "20", String(0)); }, [coSttnSearch, mngPage]); ///////////////////////////////////////////// 수요처 관리 ////////////////////////////////////////////////// @@ -521,10 +359,6 @@ useEffect(() => { if (layerId === null || layerId === undefined) { setLayerId(seletectedLayerNm || ''); } - //하수처리시설 - const isPrcsFclt = layerId == '하수처리시설' - // 재이용시설 - const isPbnst = layerId == '하수재이용시설'; // 빗물이용시설 const isRain = layerId == '빗물이용시설'; // 중수도시설 @@ -533,108 +367,14 @@ useEffect(() => { const isHtdfl = layerId == '온배수시설'; // 수요처관리 const isReuseMng = layerId == '수요처'; - if (isPrcsFclt) { - if (viewMode == 'list') { - return ( - <div style={{ overflowY: 'auto' }}> - <div className="flex justify-between items-center mb-4"> - <Title title="하수처리시설 현황" /> - </div> - {/* 높이 조정 */} - <UiTable - tableData={hasuTable || []} - headlist={pubFacHeadList} - pageSize={11} - total={pbnstHasuResCount || 0} - headName={""} - cellClick={hasuCellClick} - onPageChange={setHasuPage} - /> - </div> - ) - } else { - return ( - <div style={{ overflowX: 'hidden' }}> - <div className="flex justify-between items-center mb-4"> - <Title title="하수처리시설 상세정보" /> - </div> - {/* 높이 조정 */} - <UiTable - tableData={isRuseReg?.data || []} - headlist={pubFacHeadList.slice(0, 4)} - pageSize={1} - total={0} - headName={"위치정보"} - cellClick={() => { }} - onPageChange={() => { }} - /> - <UiTable - tableData={isRuseReg?.data || []} - headlist={pubFacHeadList.slice(4)} - pageSize={1} - total={0} - headName={"기본정보"} - cellClick={() => { }} - onPageChange={() => { }} - /> - </div> - ) - } + switch (layerId) { + case '하수재이용시설': + return (<ResueFacility viewMode={viewMode} seletectedLayerNm={layerId} tabData={tabData} actTabData={actTabData} setFcltPk={setFcltPk} onFeatureSelect={onFeatureSelect} onClose={onClose} />) + case '하수처리시설': + return(<HasuFacility seletectedLayerNm={layerId} viewMode={viewMode} onFeatureSelect={onFeatureSelect} onClose={onClose} isRuseReg={isRuseReg}/>) + default: + break; } - if (isPbnst) { - if (viewMode == 'detail') { - // 재이용시설 상세 - return ( - <> - <div className="flex justify-between items-center mb-4"> - <Title title="하수재이용시설 상세정보" /> - </div> - <div className="flex justify-between items-center mb-8"> - <UITab - defaultValue={activeTab} - tabList={tabData.map((tab) => ({ - value: tab.id, - label: tab.name, - onClick: () => setActiveTab(tab.id), - }))} - /> - </div> - <PbnstSttnModal - data={actTabData?.data[0] || ({} as customPbnst)} - titleVisible={false} - /> - </> - ); - } else { - // 재이용시설 목록 - return ( - <div style={{ overflowY: 'auto' }}> - <div className="flex justify-between items-center"> - <Title title="하수재이용시설 현황" /> - {/* <div className="flex gap-2 mt-8"> - <Button className={btnClass} color="primary" onClick={excelClick}> - 엑셀다운로드 - </Button> - </div> */} - </div> - <UiTable - headName={[]} - tableData={bdstTable || []} - headlist={pbnstSttn.pbnstSttHeadList} - pageSize={20} - total={pbnstSttnResCount || 0} - cellClick={(index: number | undefined) => { - pbnstCellClick(index); - }} - onPageChange={onPageChangeHandler} - > - <HeadMakeColSpan upHeadList={pbnstSttn.pbnstSttNodeList} /> - </UiTable> - </div> - ); - } - } - if (isRain) { if (viewMode == 'detail') { // 빗물이용시설 상세 diff --git a/apps/wr/app/map/util.ts b/apps/wr/app/map/util.ts new file mode 100644 index 000000000..e3428b300 --- /dev/null +++ b/apps/wr/app/map/util.ts @@ -0,0 +1,58 @@ +import { getFetch, postFectch, fetchType } from "@/components/lib/api"; + +const fetchpbnstDetail = async (featureCd: string, setTabData: any) => { + try { + const response = await getFetch("wr", `/reuseFc/pubFc/${featureCd}`); + // 중복 데이터 추가 방지 + setTabDataFunc(response, featureCd, setTabData); + } catch (error) { + console.error("Error fetching data:", error); + } +}; +const fetchHasuDetail = async (featureCd: string, data: string) => { + try { + const rainRes = await postFectch(`/prcsFclt/ruseInfo/1/20/A/0/0/1/SW/${featureCd}`, [data], fetchType.COMMON) + // setHasuList + const filteredData = rainRes.data.map((item: any) => { + return { + ...item, + fctCpc: Number(item.fctCpc) || 0, + ruse: item.ruse === false ? "N" : "Y", + }; + }) + return filteredData + } catch (error) { + console.error("Error fetching data:", error); + } +}; +// 재이용시설 상세정보 +const setTabDataFunc = (res: any, pk: string, setTabData: any) => { + setTabData((prev) => { + if (prev.some((tab) => tab.id === pk)) { + return prev; // 이미 존재하는 탭이면 추가하지 않음 + } + return [...prev, { id: pk, name: `재이용시설 ${prev.length + 1}`, data: res }]; + }); +} + +const detainSttn = async (featureCd: string, layerNm: string) => { + if (layerNm == '빗물이용시설') { //빗물이용시설 + const res = await getFetch(fetchType.WR, `/reuseFc/rainyInfoList/1/20/0/${featureCd}`) + return res + } + else if (layerNm == '중수도시설') { //중수도 시설 + const res = await getFetch(fetchType.WR, `/reuseFc/waterreuseFc/1/20/0/${featureCd}`) + return res + } + else if (layerNm == '수요처') { //수요처 + const res = await getFetch(fetchType.WR, `/reuseFc/request/1/20/0/${featureCd}`) + return res + } + else { + const res = await getFetch(fetchType.WR, `/drain/facilityList/1/20/0/${featureCd}`) + return res + // setHtdflDetailModal({ isOpen: true, data: htdflSttnRes[0] }); + } +} + +export { fetchHasuDetail, fetchpbnstDetail, detainSttn }; \ No newline at end of file From 765ca59e35713af3ed9df9fe38b0bdd219ed6da2 Mon Sep 17 00:00:00 2001 From: JIeunhuh <entks301@naver.com> Date: Thu, 31 Jul 2025 17:00:08 +0900 Subject: [PATCH 03/11] =?UTF-8?q?=ED=8C=9D=EC=97=85=EB=B3=84=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EB=B6=84=EB=A6=AC=20=EB=B0=8F=20?= =?UTF-8?q?=EC=8B=9C=EC=84=A4=20=EC=84=A0=ED=83=9D=EC=8B=9C=20=ED=95=B4?= =?UTF-8?q?=EB=8B=B9=20api=EB=A7=8C=20=ED=98=B8=EC=B6=9C=ED=95=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{ => layerPopup}/hasuFacility.tsx | 4 +- .../components/layerPopup/htdflFacility.tsx | 83 +++ .../components/layerPopup/rainFacility.tsx | 105 ++++ .../{ => layerPopup}/reuseFacility.tsx | 7 +- .../layerPopup/reuseMngFacility.tsx | 103 ++++ .../components/layerPopup/rswtFacility.tsx | 107 ++++ apps/wr/app/map/components/newWindowPopup.tsx | 186 ++++++ .../map/{ => components}/newWindowPortal.tsx | 0 apps/wr/app/map/mainMap/page.tsx | 17 +- apps/wr/app/map/newWindowPopup.tsx | 552 ------------------ apps/wr/app/map/popup/page.tsx | 2 +- 11 files changed, 597 insertions(+), 569 deletions(-) rename apps/wr/app/map/components/{ => layerPopup}/hasuFacility.tsx (94%) create mode 100644 apps/wr/app/map/components/layerPopup/htdflFacility.tsx create mode 100644 apps/wr/app/map/components/layerPopup/rainFacility.tsx rename apps/wr/app/map/components/{ => layerPopup}/reuseFacility.tsx (95%) create mode 100644 apps/wr/app/map/components/layerPopup/reuseMngFacility.tsx create mode 100644 apps/wr/app/map/components/layerPopup/rswtFacility.tsx create mode 100644 apps/wr/app/map/components/newWindowPopup.tsx rename apps/wr/app/map/{ => components}/newWindowPortal.tsx (100%) delete mode 100644 apps/wr/app/map/newWindowPopup.tsx diff --git a/apps/wr/app/map/components/hasuFacility.tsx b/apps/wr/app/map/components/layerPopup/hasuFacility.tsx similarity index 94% rename from apps/wr/app/map/components/hasuFacility.tsx rename to apps/wr/app/map/components/layerPopup/hasuFacility.tsx index d93d4c30a..5a10ce3ed 100644 --- a/apps/wr/app/map/components/hasuFacility.tsx +++ b/apps/wr/app/map/components/layerPopup/hasuFacility.tsx @@ -25,13 +25,13 @@ const HasuFacility = ({ seletectedLayerNm, viewMode, onClose, onFeatureSelect, i } }, [code, pbnstHasuRes]); useEffect(() => { - if (seletectedLayerNm !== '하수처리시설') return; + if (seletectedLayerNm !== '하수처리시설' || viewMode != 'list') return; if (!pbnst.isInit) { pubHasuInit("1", "0"); } }, [pbnst.isInit, pubHasuInit]); useEffect(() => { - if (seletectedLayerNm !== '하수처리시설') return; + if (seletectedLayerNm !== '하수처리시설' || viewMode != 'list') return; pubHasuInit(String(hasuPage), "0"); }, [hasuPage, pubHasuInit]); diff --git a/apps/wr/app/map/components/layerPopup/htdflFacility.tsx b/apps/wr/app/map/components/layerPopup/htdflFacility.tsx new file mode 100644 index 000000000..fe4bef25a --- /dev/null +++ b/apps/wr/app/map/components/layerPopup/htdflFacility.tsx @@ -0,0 +1,83 @@ +import { useWrStore } from '@/app/store'; +import type { customHtdflBdst } from '@/app/store/ruseFcltMng/fcltReg/htdflReg'; +import { tableData } from '@/components/lib/func/commom'; +import SttnDetailModal from '@/components/ui/modal/sttn/sttnDetailModal/sttnDetailModal'; +import { Title, UiTable } from '@common/business_components/ui'; +import { HeadMakeColSpan } from '@common/business_components/ui/table/HeadMakeColSpan'; +import React, { useEffect, useState } from 'react' +interface Props { + seletectedLayerNm: string; + viewMode: 'list' | 'detail' | 'nothing'; + onFeatureSelect: (feature: any) => void; + onClose: () => void; + htdflDetailModal: { isOpen: boolean; data: customHtdflBdst | undefined }; +} +const HtdflFacility = ({ seletectedLayerNm, viewMode, onFeatureSelect, onClose, htdflDetailModal }: Props) => { + const { code, htdflSttn, htdflSttnSearch, initHtdfl } = useWrStore((state) => state); + const { htdflSttnRes, htdflSttnResCount } = htdflSttn; + const [htdflTable, setHtdflTable] = useState<customHtdflBdst[]>(); + const [htdflPage, setHtdflPage] = useState(1) + // 온배수시설 cellClick 이벤트 (상세정보) + const htdflCellClick = (index: number | undefined) => { + if (index !== undefined && htdflTable !== undefined) { + onFeatureSelect(htdflTable[index]); + onClose(); + } + }; + useEffect(() => { + if (seletectedLayerNm !== '온배수시설' || viewMode != 'list') return; + if (!htdflSttn.isInit) { + initHtdfl(false, 1, "20", "0", '', code.cityCd, code.sggCd); + } + }, [code.cityCd, code.sggCd, htdflSttn.isInit, initHtdfl]); + + useEffect(() => { + if (seletectedLayerNm !== '온배수시설' || viewMode != 'list') return; + const data = tableData(htdflSttnRes, { code }); + const chData = data + .map((item: any) => ({ + ...item, + ...item.reuseBuilding[0], + ...item.reuseBuilding[0].htdflObj, + chBuildingYn: item.chgBuilding[0]?.bdstChgNm !== null ? "Y" : "N", + })) + .filter((item: any) => item.htdflObj !== undefined); + setHtdflTable(chData); + }, [code, htdflSttnRes, htdflSttn.htdflSttHeadList]); + + useEffect(() => { + if (seletectedLayerNm !== '온배수시설' || viewMode != 'list') return; + htdflSttnSearch(true, htdflPage, "20", '', '', code.cityCd, code.sggCd); + }, [code.cityCd, code.sggCd, htdflPage, htdflSttnSearch]); + return ( + viewMode == 'detail' ? ( + <SttnDetailModal type={"HTB"} formObj={htdflDetailModal.data as customHtdflBdst} /> + ) : ( + <div style={{ overflowY: 'auto' }}> + <div className="flex justify-between items-center"> + <Title title="온배수시설 현황" /> + {/* <div className="flex gap-2 mt-8"> + <Button className={btnClass} color="primary" onClick={excelClick}> + 엑셀다운로드 + </Button> + </div> */} + </div> + <UiTable + headName={[]} + tableData={htdflTable || []} + headlist={htdflSttn.htdflSttHeadList} + pageSize={20} + total={htdflSttnResCount || 0} + cellClick={(index: number | undefined) => { + htdflCellClick(index ?? undefined); + }} + onPageChange={setHtdflPage} + > + <HeadMakeColSpan upHeadList={htdflSttn.htdflSttNodeList} /> + </UiTable> + </div> + ) + ) +} + +export default HtdflFacility \ No newline at end of file diff --git a/apps/wr/app/map/components/layerPopup/rainFacility.tsx b/apps/wr/app/map/components/layerPopup/rainFacility.tsx new file mode 100644 index 000000000..54b51e8a6 --- /dev/null +++ b/apps/wr/app/map/components/layerPopup/rainFacility.tsx @@ -0,0 +1,105 @@ +import { useWrStore } from "@/app/store"; +import type { customRainBdst } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; +import { tableData } from "@/components/lib/func/commom"; +import SttnDetailModal from "@/components/ui/modal/sttn/sttnDetailModal/sttnDetailModal"; +import { Title, UiTable } from "@common/business_components/ui"; +import { HeadMakeColSpan } from "@common/business_components/ui/table/HeadMakeColSpan"; +import { useEffect, useState } from "react"; +interface Props { + seletectedLayerNm: string; + viewMode: 'list' | 'detail' | 'nothing'; + onFeatureSelect: (feature: any) => void; + onClose: () => void; + rainDetailModal: { isOpen: boolean; data: customRainBdst | undefined }; +} +const RainFacility = ({seletectedLayerNm,viewMode,onFeatureSelect,onClose,rainDetailModal}:Props) => { + // 빗물재이용시설 변수 + const { code, rainSttn, rainSttnSearch, rainSttnInit } = useWrStore( + (state) => state + ); + const { rainSttnRes, rainSttnResCount } = rainSttn; + const [rainTable, setRainTable] = useState<customRainBdst[]>(); + const [rainPage, setRainPage] = useState(1) + + // 빗물재이용시설 cellClick 이벤트 (상세정보) + const rainCellClick = (index: number | undefined, headId: string) => { + if ( + index !== undefined && + rainTable !== undefined && + headId !== 'chBuildingYn') { + onFeatureSelect(rainTable[index]); + onClose(); + } + } + useEffect(() => { + if (seletectedLayerNm !== '빗물이용시설' || viewMode != 'list') return; + console.log('빗물이용시설 초기값 호출', viewMode) + if (!rainSttn.isInit) { + rainSttnInit(false, 1, "20", "0", '', code.cityCd, code.sggCd); + } + }, [code.cityCd, code.sggCd, rainSttn.isInit, rainSttnInit]); + + useEffect(() => { + const data = tableData(rainSttnRes, { code }); + const chData = data + .map((item: any) => { + return { + ...item, + ...item.reuseBuilding[0], + ...item.reuseBuilding[0].rainyObj, + dtfcltCpct: item.reuseBuilding[0].rainyObj.dtfcltCpct === -1 + ? "확인불가" + : item.reuseBuilding[0].rainyObj.dtfcltCpct, + instlCst: item.reuseBuilding[0].instlCst === -1 + ? "확인불가" + : item.reuseBuilding[0].instlCst, + instlCstSprtAmt: item.reuseBuilding[0].instlCstSprtAmt === -1 + ? "확인불가" + : item.reuseBuilding[0].instlCstSprtAmt, + chBuildingYn: item.chgBuilding[0]?.bdstChgNm !== null ? "Y" : "N", + pntfColArea: item.reuseBuilding[0].rainyObj.pntfColArea === -1 + ? "확인불가" + : item.reuseBuilding[0].rainyObj.pntfColArea, + filtrTrtmfctCpct: item.reuseBuilding[0].rainyObj.filtrTrtmfctCpct === -1 + ? "확인불가" + : item.reuseBuilding[0].rainyObj.filtrTrtmfctCpct, + archArea: Math.floor(item.archArea) === -1 ? "확인불가" : item.archArea, + }; + }) + .filter((item: any) => item.rainyObj !== undefined); + setRainTable(chData); + }, [code, rainSttnRes, rainSttn.rainSttHeadList]); + + useEffect(() => { + if (seletectedLayerNm !== '빗물이용시설' || viewMode != 'list') return; + rainSttnSearch(false, rainPage, "20", '', '', code.cityCd, code.sggCd); + }, [code.cityCd, code.sggCd, rainPage, rainSttnSearch]); + return (viewMode == 'detail' ? + (<SttnDetailModal type={"RNB"} formObj={rainDetailModal.data as customRainBdst} />) + : ( + <> + <div className="flex justify-between items-center"> + <Title title="빗물이용시설 현황" /> + {/* <div className="flex gap-2 mt-8"> + <Button className={btnClass} color="primary" onClick={excelClick}> + 엑셀다운로드 + </Button> + </div> */} + </div> + <UiTable + headName={[]} + tableData={rainTable || []} + headlist={rainSttn.rainSttHeadList} + pageSize={20} + total={rainSttnResCount} + cellClick={(index: number | undefined, headId: string) => { + rainCellClick(index ?? undefined, headId); + }} + onPageChange={setRainPage} + > + <HeadMakeColSpan upHeadList={rainSttn.rainSttNodeList} /> + </UiTable> + </> + )) +} +export default RainFacility; \ No newline at end of file diff --git a/apps/wr/app/map/components/reuseFacility.tsx b/apps/wr/app/map/components/layerPopup/reuseFacility.tsx similarity index 95% rename from apps/wr/app/map/components/reuseFacility.tsx rename to apps/wr/app/map/components/layerPopup/reuseFacility.tsx index dd18a657e..152d37999 100644 --- a/apps/wr/app/map/components/reuseFacility.tsx +++ b/apps/wr/app/map/components/layerPopup/reuseFacility.tsx @@ -40,14 +40,13 @@ const ResueFacility = ({ viewMode, seletectedLayerNm, tabData, actTabData, setFc // 재이용 현황 초기값 useEffect(() => { - console.log('재이용시설 초기값 호출', viewMode) - if (seletectedLayerNm !== '하수재이용시설') return; + if (seletectedLayerNm !== '하수재이용시설' || viewMode !='list') return; if (!pbnstSttn.isInit) { getFlcts() } }, [seletectedLayerNm, pbnstSttn.isInit]); useEffect(() => { - if (seletectedLayerNm !== '하수재이용시설') return; + if (seletectedLayerNm !== '하수재이용시설' || viewMode !='list') return; pbnSttnInit("1", "20", { partValue: "A", sidoValue: '0', @@ -67,7 +66,7 @@ const ResueFacility = ({ viewMode, seletectedLayerNm, tabData, actTabData, setFc // 재이용시설 조회 // 재이용시설 페이징처리 관련 함수 const onPageChangeHandler = useCallback((page: number) => { - if (seletectedLayerNm !== '하수재이용시설') return; + if (seletectedLayerNm !== '하수재이용시설' || viewMode !='list') return; pbnstSttnSearch( false, String(page), diff --git a/apps/wr/app/map/components/layerPopup/reuseMngFacility.tsx b/apps/wr/app/map/components/layerPopup/reuseMngFacility.tsx new file mode 100644 index 000000000..9030493ff --- /dev/null +++ b/apps/wr/app/map/components/layerPopup/reuseMngFacility.tsx @@ -0,0 +1,103 @@ +import { useWrStore } from '@/app/store'; +import { tableData, formatPhoneNumber } from '@/components/lib/func/commom'; +import { Title, UiTable } from '@common/business_components/ui'; +import type { DemandCompType } from '@common/business_components/ui/sdmInsertForm/form'; +import moment from 'moment'; +import React, { useEffect, useState } from 'react' +interface Props { + seletectedLayerNm: string; + viewMode: 'list' | 'detail' | 'nothing'; + onFeatureSelect: (feature: any) => void; + onClose: () => void; + isDmdModal: { data: any | undefined }; +} +const ReuseMngFacility = ({seletectedLayerNm, viewMode, onFeatureSelect, onClose, isDmdModal}:Props) => { + const { code, + coSttn, + coSttnInit, + coSttnSearch, + } = useWrStore((state) => state); + const [mngTable, setMngTable] = useState<DemandCompType[]>(); + const [mngPage, setMngPage] = useState(1); + const { coSttnRes, coSttnResCount, coSttHeadList } = coSttn; + useEffect(() => { + if (seletectedLayerNm !== '수요처' || viewMode != 'list') return; + if (!coSttn.isInit) { + coSttnInit("1", "20", "0"); + } + }, [coSttn.isInit, coSttnInit]); + const coMngCellClick = (index: number | undefined) => { + if (index !== undefined && mngTable !== undefined) { + onFeatureSelect(mngTable[index]); + onClose(); + } + }; + useEffect(() => { + if (seletectedLayerNm !== '수요처' || viewMode!='list') return; + if (coSttnRes) { + if (coSttnRes?.length > 0 && code.cityCd.length > 0) { + const data = tableData(coSttnRes, { code }); + const chData = data.map((item: any) => ({ + ...item, + telno: formatPhoneNumber(item.telno || undefined), + regDt: moment(item.regDt).format("YYYY-MM-DD"), + dmdInfoId: `${item.dmdInfoId.slice(0, 3)}-${item.dmdInfoId.slice(3, 5)}-${item.dmdInfoId.slice(5, 10)}-${item.dmdInfoId.slice(10)}` + })); + // .filter((item: any) => item !== undefined); + setMngTable(chData); + } + } + }, [coSttnRes, code]); + useEffect(() => { + if (seletectedLayerNm !== '수요처' || viewMode!='list') return; + coSttnSearch(String(mngPage), "20", String(0)); + }, [coSttnSearch, mngPage]); + return ( + viewMode == 'detail' + ? ( + <div style={{ overflowX: 'hidden' }}> + <div className="flex justify-between items-center mb-4"> + <Title title="수요처 상세정보" /> + </div> + {/* 높이 조정 */} + <UiTable + tableData={isDmdModal?.data || []} + headlist={coSttHeadList.slice(0, 3)} + pageSize={1} + total={0} + headName={"위치정보"} + cellClick={() => { }} + onPageChange={() => { }} + /> + <UiTable + tableData={isDmdModal?.data || []} + headlist={coSttHeadList.slice(3)} + pageSize={1} + total={0} + headName={"기본정보"} + cellClick={() => { }} + onPageChange={() => { }} + /> + </div> + ) : (<div> + <div className="flex justify-between items-center"> + <Title title="수요처 현황" /> + </div> + <UiTable + headName={[]} + tableData={mngTable || []} + headlist={coSttHeadList} + pageSize={20} + total={coSttnResCount + 10 || 0} + cellClick={(index: number | undefined) => { + coMngCellClick(index); + // setUpdateData(coSttnRes[index as number]); + }} + onPageChange={(page) => { + setMngPage(page); + }} + ></UiTable></div>) + ) +} + +export default ReuseMngFacility \ No newline at end of file diff --git a/apps/wr/app/map/components/layerPopup/rswtFacility.tsx b/apps/wr/app/map/components/layerPopup/rswtFacility.tsx new file mode 100644 index 000000000..dc2537c95 --- /dev/null +++ b/apps/wr/app/map/components/layerPopup/rswtFacility.tsx @@ -0,0 +1,107 @@ +import { useWrStore } from "@/app/store"; +import type { customRswtBdst } from "@/app/store/ruseFcltMng/fcltReg/rswtReg"; +import { tableData } from "@/components/lib/func/commom"; +import SttnDetailModal from "@/components/ui/modal/sttn/sttnDetailModal/sttnDetailModal"; +import { Title, UiTable } from "@common/business_components/ui"; +import { HeadMakeColSpan } from "@common/business_components/ui/table/HeadMakeColSpan"; +import { useEffect, useState } from "react"; +interface Props { + seletectedLayerNm: string; + viewMode: 'list' | 'detail' | 'nothing'; + onFeatureSelect: (feature: any) => void; + onClose: () => void; + rswtrDetailModal: { isOpen: boolean; data: customRswtBdst | undefined }; +} +const RswtFacility = ({ seletectedLayerNm, viewMode, onFeatureSelect, onClose, rswtrDetailModal }: Props) => { + const { code, rswtSttn, rswtSttnSearch, rswtSttnInit } = useWrStore( + (state) => state + ); + const { rswtSttnRes, rswtSttnResCount } = rswtSttn; + const [rswtrTable, setRswtrTable] = useState<customRswtBdst[]>() + const [rswtrPage, setRswtrPage] = useState(1); + // 중수도시설 cellClick 이벤트 (상세정보) + const rswtCellClick = (index: number | undefined, headId: string) => { + if (headId !== 'chBuildingYn' && index !== undefined && rswtrTable !== undefined) { + // setRswtrDetailModal({ isOpen: true, data: rswtrTable[index] }); + // setViewMode('detail') + onFeatureSelect(rswtrTable[index]); + onClose(); + } + } + useEffect(() => { + if (seletectedLayerNm !== '중수도시설' || viewMode != 'list') return; + if (!rswtSttn.isInit) { + rswtSttnInit(false, 1, "20", "0", '', code.cityCd, code.sggCd); + } + }, [code.cityCd, code.sggCd, rswtSttn.isInit, rswtSttnInit]); + + useEffect(() => { + if (seletectedLayerNm !== '중수도시설' || viewMode != 'list') return; + const data = tableData(rswtSttnRes, { code }); + const chData = data + .map((item: customRswtBdst) => { + return { + ...item, + ...item.reuseBuilding[0], + ...item.reuseBuilding[0]?.rswtrObj, + chBuildingYn: item?.chgBuilding[0]?.bdstChgNm !== null && item?.chgBuilding.length > 0 ? 'Y' : 'N', + instlCst: item.reuseBuilding[0]?.instlCst === -1 ? "확인불가" : item.reuseBuilding[0]?.instlCst, + instlCstSprtAmt: item.reuseBuilding[0]?.instlCstSprtAmt === -1 ? "확인불가" : item.reuseBuilding[0]?.instlCstSprtAmt, + archArea: item?.archArea.split('.')[0] !== '-1' ? item?.archArea.split('.')[0] : '확인불가' + } + }) + .filter( + (item: customRswtBdst) => + item.reuseBuilding[0]?.rswtrObj !== undefined + ); + setRswtrTable(chData); + }, [code, rswtSttnRes]); + // FIXME: 윤선화 차장님 요구사항으로 엑셀 다운로드 삭제. + // const rswtSttnExcelDownload = useCallback(() => { + // if (rswtSttn.excelList.length > 0) { + // if (rswtSttn.excelList.length === 0) return; + // excelDownload( + // rswtSttn.excelList, + // "중수도시설 현황", + // "sample_rswtSttn.xlsx", + // "A1:W3", + // ); + // } + // }, [rswtSttn.excelList]) + + useEffect(() => { + if (seletectedLayerNm !== '중수도시설' || viewMode != 'list') return; + rswtSttnSearch(false, rswtrPage, "20", '', '', code.cityCd, code.sggCd); + }, [code.cityCd, code.sggCd, rswtSttnSearch, rswtrPage]); + return ( + viewMode == 'detail' ? + // 중수도시설 상세 + ( + <SttnDetailModal type={"RST"} formObj={rswtrDetailModal.data as customRswtBdst} /> + ) : ( + <div style={{ overflowY: 'auto' }}> + <div className="flex justify-between items-center"> + <Title title="중수도시설 현황" /> + {/* <div className="flex gap-2 mt-8"> + <Button className={btnClass} color="primary" onClick={rswtSttnExcelDownload}> + 엑셀다운로드 + </Button> + </div> */} + </div> + <UiTable + headName={[]} + tableData={rswtrTable || []} + headlist={rswtSttn.rswtSttHeadList} + pageSize={20} + total={rswtSttnResCount} + cellClick={(index: number | undefined, headId: string) => { + rswtCellClick(index, headId); + }} + onPageChange={setRswtrPage} + > + <HeadMakeColSpan upHeadList={rswtSttn.rswtSttNodeList} /> + </UiTable> + </div> + )) +} +export default RswtFacility; \ No newline at end of file diff --git a/apps/wr/app/map/components/newWindowPopup.tsx b/apps/wr/app/map/components/newWindowPopup.tsx new file mode 100644 index 000000000..bcc0e6ec3 --- /dev/null +++ b/apps/wr/app/map/components/newWindowPopup.tsx @@ -0,0 +1,186 @@ +"use client" +import NewWindowPortal from '@/app/map/components/newWindowPortal'; +import type { customHtdflBdst } from "@/app/store/ruseFcltMng/fcltReg/htdflReg"; +import type { pubHasuType } from "@/app/store/ruseFcltMng/fcltReg/pbnstReg/type"; +import type { customRainBdst } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; +import type { customRswtBdst } from "@/app/store/ruseFcltMng/fcltReg/rswtReg"; +import { useGisContext } from "@common/business_components/ui/map/Gis"; +import type { DemandCompType } from '@common/business_components/ui/sdmInsertForm/form'; +import React, { useCallback, useEffect, useRef, useState } from 'react'; +import ResueFacility from './layerPopup/reuseFacility'; +import { detainSttn, fetchHasuDetail, fetchpbnstDetail } from '../util'; +import HasuFacility from './layerPopup/hasuFacility'; +import RainFacility from './layerPopup/rainFacility'; +import RswtFacility from './layerPopup/rswtFacility'; +import HtdflFacility from './layerPopup/htdflFacility'; +import ReuseMngFacility from './layerPopup/reuseMngFacility'; + +interface NewWindowPopupProps { + featureData: any; + seletectedLayerNm?: string + viewMode?: 'list' | 'detail' | 'nothing' + onFeatureSelect: (data: any) => void; + onClose: () => void; +} + +const NewWindowPopup: React.FC<NewWindowPopupProps> = ({ + featureData, + seletectedLayerNm, + onFeatureSelect, + onClose +}) => { + const { gisMap } = useGisContext() as unknown as { checkedLayerIds: React.MutableRefObject<number[]>; treeData: { lyrSn: number }[] | null; gisCore: any; gisMap: any; }; + const [tabData, setTabData] = useState<{ id: string; name: string; data: any }[]>([]); + const [activeTab, setActiveTab] = useState<string>('0'); + const [viewMode, setViewMode] = useState<'list' | 'detail' | 'nothing'>('list'); + const [fcltPk, setFcltPk] = useState<string>(''); + + + // onClose 함수를 useCallback으로 메모이제이션 + const memoizedOnClose = useCallback(() => { + onClose(); + }, [onClose]); + + // 빗물재이용시설 변수 + const [rainDetailModal, setRainDetailModal] = useState<{ isOpen: boolean, data: customRainBdst | undefined }>({ data: undefined, isOpen: false }); + const [rainList, setRainList] = useState<customRainBdst[]>([]); + + // 중수도시설 변수 + const [rswtrDetailModal, setRswtrDetailModal] = useState<{ isOpen: boolean, data: customRswtBdst | undefined }>({ data: undefined, isOpen: false }); + const [rswtrList, setRswtrList] = useState<customRswtBdst[]>([]); + + // 온배수시설 변수 + const [htdflDetailModal, setHtdflDetailModal] = useState<{ isOpen: boolean, data: customHtdflBdst | undefined }>({ data: undefined, isOpen: false }); + const [htdflList, setHtdflList] = useState<customHtdflBdst[]>([]); + + // 하수처리시설 변수 + const [hasuList, setHasuList] = useState<pubHasuType[]>([]); + const [isRuseReg, setIsRuseReg] = useState<{ + data: any | undefined; + }>({ data: undefined }); + + // 수요처관리 변수 + const [isDmdModal, setIsDmdModal] = useState<{ + isMoal: boolean; + data: any | undefined; + }>({ isMoal: false, data: undefined }); + const [mngList, setMngList] = useState<DemandCompType[]>([]); + + ///////////////////////////////////////////// 피쳐데이터 선택 시 각 시설별 데이터 처리 /////////////////////////////////////////////// + const getSelectedLayers = () => { + console.trace('호출확인', 1) + let result = null; + const layers = gisMap ? gisMap.getLayers().getArray().filter((l: any) => l.getVisible()) : []; // gisMap이 null인지 확인 후 레이어 가져오기 + let featureLayer: { get: (key: string) => any } = { get: () => null }; + layers.forEach((layer: any) => { + const source = layer.getSource(); + if (source && source.hasFeature && featureData + && typeof featureData.getId === 'function' && source.hasFeature(featureData)) { + featureLayer = layer; // 피처가 속한 레이어 찾기) + } + }); + result = featureLayer.get('lyrNm'); + return result; + }; + // 상세 목록 데이터 상태 처리 + useEffect(() => { + if (hasuList.length > 0) setIsRuseReg({ data: [hasuList[0]] }); + }, [hasuList]) + useEffect(() => { + if (rainList.length > 0) setRainDetailModal({ isOpen: true, data: rainList[0] }); + }, [rainList]) + useEffect(() => { + if (rswtrList.length > 0) setRswtrDetailModal({ isOpen: true, data: rswtrList[0] }); + }, [rswtrList]) + useEffect(() => { + if (htdflList.length > 0) setHtdflDetailModal({ isOpen: true, data: htdflList[0] }); + }, [htdflList]) + useEffect(() => { + if (mngList.length > 0) setIsDmdModal({ isMoal: true, data: [mngList[0]] }); + }, [mngList]) + + const lastCalledIdRef = useRef<string | null>(null); + const [layerId, setLayerId] = useState<string | null>(); + + useEffect(() => { + const layers = getSelectedLayers(); + setLayerId(layers); + }, []); // 최초 렌더링 시 호출 + + // 피쳐 데이터가 변경될 때마다 호출 + useEffect(() => { + if (!featureData || typeof featureData.getProperties !== "function" || featureData.getProperties() == '') return; + const selectedProps = featureData.getProperties(); + const currentId = featureData.getId?.() || selectedProps?.id || selectedProps?.fid; + // 유효하지 않은 ID는 아예 무시 + if (!currentId || typeof currentId !== 'string') return; + // 이전과 동일하면 무시 + if (!currentId || currentId === lastCalledIdRef.current) return; + lastCalledIdRef.current = currentId // 새로운 피처일 때만 API 호출 + const layerId = getSelectedLayers?.(); + if (!layerId || typeof layerId !== 'string') return; + const featureCd = + layerId === '하수재이용시설' ? selectedProps?.swtrmt_fclt_cd : + ['빗물이용시설', '중수도시설', '온배수시설', '수요처'].includes(layerId) ? selectedProps?.bdst_nm : + layerId === '하수처리시설' ? selectedProps?.swtrmt_fclt_nm : + null; + if (!featureCd || featureCd === fcltPk) return; + setFcltPk(featureCd); + setActiveTab(featureCd); + setViewMode("detail"); + if (layerId === '하수재이용시설') { + fetchpbnstDetail(featureCd, setTabData); + } else if (layerId === '하수처리시설') { + fetchHasuDetail(featureCd, selectedProps?.swtrmt_fclt_cd).then((data) => { setHasuList(data) }) + } else { + detainSttn(featureCd, layerId).then((res) => { + if (layerId === '빗물이용시설') setRainList(res); + else if (layerId === '중수도시설') setRswtrList(res); + else if (layerId === '온배수시설') { setHtdflList(res); setHtdflDetailModal({ isOpen: true, data: res[0] }); } + else if (layerId === '수요처') setMngList(res); + }); + } + }, [featureData]); + ///////////////////////////////////////////// 피쳐데이터 선택 시 각 시설별 데이터 처리 /////////////////////////////////////////////// + + // 체크된 레이어에 따른 조건부 컴포넌트 렌더링 함수 + const renderConditionalContent = () => { + const actTabData = tabData.find((tab) => tab.id === activeTab); + if (layerId === null || layerId === undefined) { + setLayerId(seletectedLayerNm || ''); + } + switch (layerId) { + case '하수재이용시설': + return (<ResueFacility viewMode={viewMode} seletectedLayerNm={layerId} tabData={tabData} actTabData={actTabData} setFcltPk={setFcltPk} onFeatureSelect={onFeatureSelect} onClose={onClose} />) + case '하수처리시설': + return (<HasuFacility seletectedLayerNm={layerId} viewMode={viewMode} onFeatureSelect={onFeatureSelect} onClose={onClose} isRuseReg={isRuseReg} />) + case '빗물이용시설': + return (<RainFacility seletectedLayerNm={layerId} viewMode={viewMode} onFeatureSelect={onFeatureSelect} onClose={onClose} rainDetailModal={rainDetailModal} />) + case '중수도시설': + return (<RswtFacility seletectedLayerNm={layerId} viewMode={viewMode} onFeatureSelect={onFeatureSelect} onClose={onClose} rswtrDetailModal={rswtrDetailModal} />) + case '온배수시설': + return (<HtdflFacility seletectedLayerNm={layerId} viewMode={viewMode} onFeatureSelect={onFeatureSelect} onClose={onClose} htdflDetailModal={htdflDetailModal} />) + case '수요처': + return(<ReuseMngFacility seletectedLayerNm={layerId} viewMode={viewMode} onFeatureSelect={onFeatureSelect} onClose={onClose} isDmdModal={isDmdModal} />) + default: + break; + } + return null; // 조건에 맞는 콘텐츠가 없을 경우 + }; + + return ( + viewMode == 'nothing' ? <></> : + <NewWindowPortal + onClose={memoizedOnClose} + title="시설 상세 정보" + width={viewMode == 'list' ? window.screen.availWidth : 1100} + height={viewMode == 'list' ? window.screen.availHeight : layerId == '하수처리시설' && viewMode == 'detail' ? 450 : 900} + scrollbars='yes' + resizable='no'> + {/* 체크된 레이어에 따른 조건부 렌더링 */} + {renderConditionalContent()} + </NewWindowPortal> + ); +}; + +export default NewWindowPopup; \ No newline at end of file diff --git a/apps/wr/app/map/newWindowPortal.tsx b/apps/wr/app/map/components/newWindowPortal.tsx similarity index 100% rename from apps/wr/app/map/newWindowPortal.tsx rename to apps/wr/app/map/components/newWindowPortal.tsx diff --git a/apps/wr/app/map/mainMap/page.tsx b/apps/wr/app/map/mainMap/page.tsx index 56c9e823d..96785f8b3 100644 --- a/apps/wr/app/map/mainMap/page.tsx +++ b/apps/wr/app/map/mainMap/page.tsx @@ -3,7 +3,7 @@ import Gis from "@common/business_components/ui/map/Gis"; import { useEffect, useRef, useState, useCallback } from "react"; import { mapService } from "../service"; -import NewWindowPopup from "../newWindowPopup"; +import NewWindowPopup from "../components/newWindowPopup"; import GisApp from "@common/business_components/ui/map/lib/gis.core"; const Map = () => { @@ -61,30 +61,27 @@ const Map = () => { // 2. data가 OpenLayers Feature 인스턴스인지 확인 let foundFeature: any = null; - console.log(data) - //console.log(data.getId()) let featureId = data.fid; const layerName = data.layerNm; - - if (layerName === "재이용시설") { + console.log("선택된 레이어 이름:", data, layerName); + if (layerName === "하수재이용시설") { // 재이용시설 featureId = `${layerName}.${data.searchPk}`; } else if ( //빗물이용시설, 중수도시설 layerName === "빗물이용시설" || - layerName == "tb_wr_rswtr_fclt" + layerName == "중수도시설" ) { featureId = `${layerName}.${data.bdstInfoSn}`; - } else if (layerName === "tb_il_swage_prcs_fclt") { //하수처리시설 + } else if (layerName === "하수처리시설") { //하수처리시설 featureId = `${layerName}.${data.bscFctCd}`; - } else if (layerName === "tb_wr_htdfl_fclt") { //온배수시설 + } else if (layerName === "온배수시설") { //온배수시설 featureId = `${layerName}.${data.bdstRuseFcltSn}`; - } else if (layerName == "tb_wr_ruse_fclt_dmd_co") { //수요처 + } else if (layerName == "수요처") { //수요처 featureId = `${layerName}.${data.dmdInfoId.replaceAll("-", "")}`; } else { console.warn("해당 레이어에 대한 featureId 매핑 없음", layerName); featureId = `${layerName}.${data.fid || data.id || data.ftrIdn}`; } - map .getLayers() .getArray() diff --git a/apps/wr/app/map/newWindowPopup.tsx b/apps/wr/app/map/newWindowPopup.tsx deleted file mode 100644 index d8e6b6338..000000000 --- a/apps/wr/app/map/newWindowPopup.tsx +++ /dev/null @@ -1,552 +0,0 @@ -"use client" -import NewWindowPortal from '@/app/map/newWindowPortal'; -import { useWrStore } from "@/app/store"; -import type { customHtdflBdst } from "@/app/store/ruseFcltMng/fcltReg/htdflReg"; -import type { pubHasuType } from "@/app/store/ruseFcltMng/fcltReg/pbnstReg/type"; -import type { customRainBdst } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; -import type { customRswtBdst } from "@/app/store/ruseFcltMng/fcltReg/rswtReg"; -import { formatPhoneNumber, tableData } from "@/components/lib/func/commom"; -import SttnDetailModal from "@/components/ui/modal/sttn/sttnDetailModal/sttnDetailModal"; -import { Title, UiTable } from '@common/business_components'; -import { useGisContext } from "@common/business_components/ui/map/Gis"; -import type { DemandCompType } from '@common/business_components/ui/sdmInsertForm/form'; -import { HeadMakeColSpan } from "@common/business_components/ui/table/HeadMakeColSpan"; -import moment from 'moment'; -import React, { useCallback, useEffect, useRef, useState } from 'react'; -import ResueFacility from './components/reuseFacility'; -import { detainSttn, fetchHasuDetail, fetchpbnstDetail } from './util'; -import HasuFacility from './components/hasuFacility'; - -interface NewWindowPopupProps { - featureData: any; - seletectedLayerNm?: string - viewMode?: 'list' | 'detail' | 'nothing' - onFeatureSelect: (data: any) => void; - onClose: () => void; -} - -const NewWindowPopup: React.FC<NewWindowPopupProps> = ({ - featureData, - seletectedLayerNm, - onFeatureSelect, - onClose -}) => { - const { gisMap } = useGisContext() as unknown as { checkedLayerIds: React.MutableRefObject<number[]>; treeData: { lyrSn: number }[] | null; gisCore: any; gisMap: any; }; - const [tabData, setTabData] = useState<{ id: string; name: string; data: any }[]>([]); - const [activeTab, setActiveTab] = useState<string>('0'); - const [viewMode, setViewMode] = useState<'list' | 'detail' | 'nothing'>('list'); - const [fcltPk, setFcltPk] = useState<string>(''); - - - // onClose 함수를 useCallback으로 메모이제이션 - const memoizedOnClose = useCallback(() => { - onClose(); - }, [onClose]); - - // 하수재이용시설 변수 - const {code} = useWrStore((state) => state); - - // 빗물재이용시설 변수 - const { rainSttn, rainSttnSearch, rainSttnInit } = useWrStore( - (state) => state - ); - const { rainSttnRes, rainSttnResCount } = rainSttn; - const [rainTable, setRainTable] = useState<customRainBdst[]>(); - const [rainPage, setRainPage] = useState(1) - const [rainDetailModal, setRainDetailModal] = useState<{ isOpen: boolean, data: customRainBdst | undefined }>({ data: undefined, isOpen: false }); - const [rainList, setRainList] = useState<customRainBdst[]>(rainSttnRes || []); - - // 중수도시설 변수 - const { rswtSttn, rswtSttnSearch, rswtSttnInit } = useWrStore( - (state) => state - ); - const { rswtSttnRes, rswtSttnResCount } = rswtSttn; - const [rswtrTable, setRswtrTable] = useState<customRswtBdst[]>(); - const [rswtrDetailModal, setRswtrDetailModal] = useState<{ isOpen: boolean, data: customRswtBdst | undefined }>({ data: undefined, isOpen: false }); - const [rswtrPage, setRswtrPage] = useState(1); - const [rswtrList, setRswtrList] = useState<customRswtBdst[]>(rswtSttnRes || []); - - // 온배수시설 변수 - const { htdflSttn, htdflSttnSearch, initHtdfl } = useWrStore((state) => state); - const { htdflSttnRes, htdflSttnResCount } = htdflSttn; - const [htdflTable, setHtdflTable] = useState<customHtdflBdst[]>(); - const [htdflDetailModal, setHtdflDetailModal] = useState<{ isOpen: boolean, data: customHtdflBdst | undefined }>({ data: undefined, isOpen: false }); - const [htdflPage, setHtdflPage] = useState(1) - const [htdflList, setHtdflList] = useState<customHtdflBdst[]>(htdflSttnRes || []); - - // 하수처리시설 변수 - const [hasuList, setHasuList] = useState<pubHasuType[]>([]); - const [isRuseReg, setIsRuseReg] = useState<{ - data: any | undefined; - }>({ data: undefined }); - - // 수요처관리 변수 - const { - coSttn, - coSttnInit, - coSttnSearch, - } = useWrStore((state) => state); - const [mngTable, setMngTable] = useState<DemandCompType[]>(); - const [mngPage, setMngPage] = useState(1); - const { coSttnRes, coSttnResCount, coSttHeadList } = coSttn; - const [isDmdModal, setIsDmdModal] = useState<{ - isMoal: boolean; - data: any | undefined; - }>({ isMoal: false, data: undefined }); - const [mngList, setMngList] = useState<DemandCompType[]>(coSttnRes || []); - ///////////////////////////////////////////// 피쳐데이터 선택 시 각 시설별 데이터 처리 /////////////////////////////////////////////// - const getSelectedLayers = () => { - console.trace('호출확인', 1) - let result = null; - const layers = gisMap ? gisMap.getLayers().getArray().filter((l: any) => l.getVisible()) : []; // gisMap이 null인지 확인 후 레이어 가져오기 - let featureLayer: { get: (key: string) => any } = { get: () => null }; - layers.forEach((layer: any) => { - const source = layer.getSource(); - if (source && source.hasFeature && featureData - && typeof featureData.getId === 'function' && source.hasFeature(featureData)) { - featureLayer = layer; // 피처가 속한 레이어 찾기) - } - }); - result = featureLayer.get('lyrNm'); - return result; - }; - // 상세 목록 데이터 상태 처리 - useEffect(() => { - if (hasuList.length > 0) setIsRuseReg({ data: [hasuList[0]] }); - }, [hasuList]) - useEffect(() => { - if (rainList.length > 0) setRainDetailModal({ isOpen: true, data: rainList[0] }); - }, [rainList]) - useEffect(() => { - if (rswtrList.length > 0) setRswtrDetailModal({ isOpen: true, data: rswtrList[0] }); - }, [rswtrList]) - useEffect(() => { - if (htdflList.length > 0) setHtdflDetailModal({ isOpen: true, data: htdflList[0] }); - }, [htdflList]) - useEffect(() => { - if (mngList.length > 0) setIsDmdModal({ isMoal: true, data: [mngList[0]] }); - }, [mngList]) - const lastCalledIdRef = useRef<string | null>(null); - const [layerId, setLayerId] = useState<string | null>(); - useEffect(() => { - const layers = getSelectedLayers(); - setLayerId(layers); - }, []); // 최초 렌더링 시 호출 - useEffect(() => { - if (!featureData || typeof featureData.getProperties !== "function" || featureData.getProperties() == '') return; - const selectedProps = featureData.getProperties(); - const currentId = featureData.getId?.() || selectedProps?.id || selectedProps?.fid; - // 유효하지 않은 ID는 아예 무시 - if (!currentId || typeof currentId !== 'string') return; - // 이전과 동일하면 무시 - if (!currentId || currentId === lastCalledIdRef.current) return; - lastCalledIdRef.current = currentId // 새로운 피처일 때만 API 호출 - const layerId = getSelectedLayers?.(); - if (!layerId || typeof layerId !== 'string') return; - const featureCd = - layerId === '하수재이용시설' ? selectedProps?.swtrmt_fclt_cd : - ['빗물이용시설', '중수도시설', '온배수시설', '수요처'].includes(layerId) ? selectedProps?.bdst_nm : - layerId === '하수처리시설' ? selectedProps?.swtrmt_fclt_nm : - null; - if (!featureCd || featureCd === fcltPk) return; - setFcltPk(featureCd); - setActiveTab(featureCd); - setViewMode("detail"); - if (layerId === '하수재이용시설') { - fetchpbnstDetail(featureCd, setTabData); - } else if (layerId === '하수처리시설') { - fetchHasuDetail(featureCd, selectedProps?.swtrmt_fclt_cd).then((data) => { setHasuList(data) }) - } else { - detainSttn(featureCd, layerId).then((res) => { - if (layerId === '빗물이용시설') setRainList(res); - else if (layerId === '중수도시설') setRswtrList(res); - else if (layerId === '온배수시설'){ setHtdflList(res); setHtdflDetailModal({ isOpen: true, data: htdflSttnRes[0] });} - else if (layerId === '수요처') setMngList(res); - }); - } - }, [featureData]); - ///////////////////////////////////////////// 피쳐데이터 선택 시 각 시설별 데이터 처리 /////////////////////////////////////////////// - - ///////////////////////////////////////////// 빗물 재이용 ////////////////////////////////////////////////// - // 빗물재이용시설 cellClick 이벤트 (상세정보) - const rainCellClick = (index: number | undefined, headId: string) => { - if ( - index !== undefined && - rainTable !== undefined && - headId !== 'chBuildingYn') { - // setRainDetailModal({ isOpen: true, data: rainTable[index] }) - // setViewMode('detail') - onFeatureSelect(rainTable[index]); - onClose(); - } - } - - - useEffect(() => { - if (seletectedLayerNm !== '빗물이용시설') return; - if (!rainSttn.isInit) { - rainSttnInit(false, 1, "20", "0", '', code.cityCd, code.sggCd); - } - }, [code.cityCd, code.sggCd, rainSttn.isInit, rainSttnInit]); - - useEffect(() => { - const data = tableData(rainSttnRes, { code }); - const chData = data - .map((item: any) => { - return { - ...item, - ...item.reuseBuilding[0], - ...item.reuseBuilding[0].rainyObj, - dtfcltCpct: item.reuseBuilding[0].rainyObj.dtfcltCpct === -1 - ? "확인불가" - : item.reuseBuilding[0].rainyObj.dtfcltCpct, - instlCst: item.reuseBuilding[0].instlCst === -1 - ? "확인불가" - : item.reuseBuilding[0].instlCst, - instlCstSprtAmt: item.reuseBuilding[0].instlCstSprtAmt === -1 - ? "확인불가" - : item.reuseBuilding[0].instlCstSprtAmt, - chBuildingYn: item.chgBuilding[0]?.bdstChgNm !== null ? "Y" : "N", - pntfColArea: item.reuseBuilding[0].rainyObj.pntfColArea === -1 - ? "확인불가" - : item.reuseBuilding[0].rainyObj.pntfColArea, - filtrTrtmfctCpct: item.reuseBuilding[0].rainyObj.filtrTrtmfctCpct === -1 - ? "확인불가" - : item.reuseBuilding[0].rainyObj.filtrTrtmfctCpct, - archArea: Math.floor(item.archArea) === -1 ? "확인불가" : item.archArea, - }; - }) - .filter((item: any) => item.rainyObj !== undefined); - setRainTable(chData); - }, [code, rainSttnRes, rainSttn.rainSttHeadList]); - - useEffect(() => { - if (seletectedLayerNm !== '빗물이용시설') return; - rainSttnSearch(false, rainPage, "20", '', '', code.cityCd, code.sggCd); - }, [code.cityCd, code.sggCd, rainPage, rainSttnSearch]); - ///////////////////////////////////////////// 빗물 재이용 ////////////////////////////////////////////////// - - ///////////////////////////////////////////// 중수도 시설 ////////////////////////////////////////////////// - // 중수도시설 cellClick 이벤트 (상세정보) - const rswtCellClick = (index: number | undefined, headId: string) => { - if (headId !== 'chBuildingYn' && index !== undefined && rswtrTable !== undefined) { - // setRswtrDetailModal({ isOpen: true, data: rswtrTable[index] }); - // setViewMode('detail') - onFeatureSelect(rswtrTable[index]); - onClose(); - } - } - useEffect(() => { - if (seletectedLayerNm !== '중수도시설') return; - if (!rswtSttn.isInit) { - rswtSttnInit(false, 1, "20", "0", '', code.cityCd, code.sggCd); - } - }, [code.cityCd, code.sggCd, rswtSttn.isInit, rswtSttnInit]); - - useEffect(() => { - if (seletectedLayerNm !== '중수도시설') return; - const data = tableData(rswtSttnRes, { code }); - const chData = data - .map((item: customRswtBdst) => { - return { - ...item, - ...item.reuseBuilding[0], - ...item.reuseBuilding[0]?.rswtrObj, - chBuildingYn: item?.chgBuilding[0]?.bdstChgNm !== null && item?.chgBuilding.length > 0 ? 'Y' : 'N', - instlCst: item.reuseBuilding[0]?.instlCst === -1 ? "확인불가" : item.reuseBuilding[0]?.instlCst, - instlCstSprtAmt: item.reuseBuilding[0]?.instlCstSprtAmt === -1 ? "확인불가" : item.reuseBuilding[0]?.instlCstSprtAmt, - archArea: item?.archArea.split('.')[0] !== '-1' ? item?.archArea.split('.')[0] : '확인불가' - } - }) - .filter( - (item: customRswtBdst) => - item.reuseBuilding[0]?.rswtrObj !== undefined - ); - setRswtrTable(chData); - }, [code, rswtSttnRes]); - // FIXME: 윤선화 차장님 요구사항으로 엑셀 다운로드 삭제. - // const rswtSttnExcelDownload = useCallback(() => { - // if (rswtSttn.excelList.length > 0) { - // if (rswtSttn.excelList.length === 0) return; - // excelDownload( - // rswtSttn.excelList, - // "중수도시설 현황", - // "sample_rswtSttn.xlsx", - // "A1:W3", - // ); - // } - // }, [rswtSttn.excelList]) - - useEffect(() => { - if (seletectedLayerNm !== '중수도시설') return; - rswtSttnSearch(false, rswtrPage, "20", '', '', code.cityCd, code.sggCd); - }, [code.cityCd, code.sggCd, rswtSttnSearch, rswtrPage]); - ///////////////////////////////////////////// 중수도 시설 ////////////////////////////////////////////////// - - ///////////////////////////////////////////// 온배수 시설 ////////////////////////////////////////////////// - // 온배수시설 cellClick 이벤트 (상세정보) - const htdflCellClick = (index: number | undefined) => { - if (index !== undefined && htdflTable !== undefined) { - onFeatureSelect(htdflTable[index]); - onClose(); - } - }; - useEffect(() => { - if (seletectedLayerNm !== '온배수시설') return; - if (!htdflSttn.isInit) { - initHtdfl(false, 1, "20", "0", '', code.cityCd, code.sggCd); - } - }, [code.cityCd, code.sggCd, htdflSttn.isInit, initHtdfl]); - - useEffect(() => { - if (seletectedLayerNm !== '온배수시설') return; - const data = tableData(htdflSttnRes, { code }); - const chData = data - .map((item: any) => ({ - ...item, - ...item.reuseBuilding[0], - ...item.reuseBuilding[0].htdflObj, - chBuildingYn: item.chgBuilding[0]?.bdstChgNm !== null ? "Y" : "N", - })) - .filter((item: any) => item.htdflObj !== undefined); - setHtdflTable(chData); - }, [code, htdflSttnRes, htdflSttn.htdflSttHeadList]); - - useEffect(() => { - if (seletectedLayerNm !== '온배수시설') return; - htdflSttnSearch(true, htdflPage, "20", '', '', code.cityCd, code.sggCd); - }, [code.cityCd, code.sggCd, htdflPage, htdflSttnSearch]); - ///////////////////////////////////////////// 온배수 시설 ////////////////////////////////////////////////// - - ///////////////////////////////////////////// 수요처 관리 ////////////////////////////////////////////////// - useEffect(() => { - if (seletectedLayerNm !== '수요처') return; - if (!coSttn.isInit) { - coSttnInit("1", "20", "0"); - } - }, [coSttn.isInit, coSttnInit]); - const coMngCellClick = (index: number | undefined) => { - if (index !== undefined && mngTable !== undefined) { - onFeatureSelect(mngTable[index]); - onClose(); - } - }; - useEffect(() => { - if (seletectedLayerNm !== '수요처') return; - if (coSttnRes) { - if (coSttnRes?.length > 0 && code.cityCd.length > 0) { - const data = tableData(coSttnRes, { code }); - const chData = data.map((item: any) => ({ - ...item, - telno: formatPhoneNumber(item.telno || undefined), - regDt: moment(item.regDt).format("YYYY-MM-DD"), - dmdInfoId: `${item.dmdInfoId.slice(0, 3)}-${item.dmdInfoId.slice(3, 5)}-${item.dmdInfoId.slice(5, 10)}-${item.dmdInfoId.slice(10)}` - })); - // .filter((item: any) => item !== undefined); - setMngTable(chData); - } - } - }, [coSttnRes, code]); - useEffect(() => { - if (seletectedLayerNm !== '수요처') return; - coSttnSearch(String(mngPage), "20", String(0)); - }, [coSttnSearch, mngPage]); - ///////////////////////////////////////////// 수요처 관리 ////////////////////////////////////////////////// - - // 체크된 레이어에 따른 조건부 컴포넌트 렌더링 함수 - const renderConditionalContent = () => { - const actTabData = tabData.find((tab) => tab.id === activeTab); - if (layerId === null || layerId === undefined) { - setLayerId(seletectedLayerNm || ''); - } - // 빗물이용시설 - const isRain = layerId == '빗물이용시설'; - // 중수도시설 - const isRswt = layerId == '중수도시설'; - // 온배수시설 - const isHtdfl = layerId == '온배수시설'; - // 수요처관리 - const isReuseMng = layerId == '수요처'; - switch (layerId) { - case '하수재이용시설': - return (<ResueFacility viewMode={viewMode} seletectedLayerNm={layerId} tabData={tabData} actTabData={actTabData} setFcltPk={setFcltPk} onFeatureSelect={onFeatureSelect} onClose={onClose} />) - case '하수처리시설': - return(<HasuFacility seletectedLayerNm={layerId} viewMode={viewMode} onFeatureSelect={onFeatureSelect} onClose={onClose} isRuseReg={isRuseReg}/>) - default: - break; - } - if (isRain) { - if (viewMode == 'detail') { - // 빗물이용시설 상세 - return ( - <SttnDetailModal type={"RNB"} formObj={rainDetailModal.data as customRainBdst} /> - ); - } else { - // 빗물이용시설 목록 - return ( - <> - <div className="flex justify-between items-center"> - <Title title="빗물이용시설 현황" /> - {/* <div className="flex gap-2 mt-8"> - <Button className={btnClass} color="primary" onClick={excelClick}> - 엑셀다운로드 - </Button> - </div> */} - </div> - <UiTable - headName={[]} - tableData={rainTable || []} - headlist={rainSttn.rainSttHeadList} - pageSize={20} - total={rainSttnResCount} - cellClick={(index: number | undefined, headId: string) => { - rainCellClick(index ?? undefined, headId); - }} - onPageChange={setRainPage} - > - <HeadMakeColSpan upHeadList={rainSttn.rainSttNodeList} /> - </UiTable> - </> - ); - } - } - - if (isRswt) { - if (viewMode == 'detail') { - // 중수도시설 상세 - return ( - <SttnDetailModal type={"RST"} formObj={rswtrDetailModal.data as customRswtBdst} /> - ); - } else { - // 중수도시설 목록 - return ( - <div style={{ overflowY: 'auto' }}> - <div className="flex justify-between items-center"> - <Title title="중수도시설 현황" /> - {/* <div className="flex gap-2 mt-8"> - <Button className={btnClass} color="primary" onClick={rswtSttnExcelDownload}> - 엑셀다운로드 - </Button> - </div> */} - </div> - <UiTable - headName={[]} - tableData={rswtrTable || []} - headlist={rswtSttn.rswtSttHeadList} - pageSize={20} - total={rswtSttnResCount} - cellClick={(index: number | undefined, headId: string) => { - rswtCellClick(index, headId); - }} - onPageChange={setRswtrPage} - > - <HeadMakeColSpan upHeadList={rswtSttn.rswtSttNodeList} /> - </UiTable> - </div> - ); - } - } - - if (isHtdfl) { - if (viewMode == 'detail') { - // 온배수시설 상세 - return ( - <SttnDetailModal type={"HTB"} formObj={htdflDetailModal.data as customHtdflBdst} /> - ); - } else { - // 온배수시설 목록 - return ( - <div style={{ overflowY: 'auto' }}> - <div className="flex justify-between items-center"> - <Title title="온배수시설 현황" /> - {/* <div className="flex gap-2 mt-8"> - <Button className={btnClass} color="primary" onClick={excelClick}> - 엑셀다운로드 - </Button> - </div> */} - </div> - <UiTable - headName={[]} - tableData={htdflTable || []} - headlist={htdflSttn.htdflSttHeadList} - pageSize={20} - total={htdflSttnResCount || 0} - cellClick={(index: number | undefined) => { - htdflCellClick(index ?? undefined); - }} - onPageChange={setHtdflPage} - > - <HeadMakeColSpan upHeadList={htdflSttn.htdflSttNodeList} /> - </UiTable> - </div> - ); - } - } - if (isReuseMng) { - if (viewMode == 'detail') { - return ( - <div style={{ overflowX: 'hidden' }}> - <div className="flex justify-between items-center mb-4"> - <Title title="수요처 상세정보" /> - </div> - {/* 높이 조정 */} - <UiTable - tableData={isDmdModal?.data || []} - headlist={coSttHeadList.slice(0, 3)} - pageSize={1} - total={0} - headName={"위치정보"} - cellClick={() => { }} - onPageChange={() => { }} - /> - <UiTable - tableData={isDmdModal?.data || []} - headlist={coSttHeadList.slice(3)} - pageSize={1} - total={0} - headName={"기본정보"} - cellClick={() => { }} - onPageChange={() => { }} - /> - </div> - ) - } else if (viewMode == 'list') { - return (<div> - <div className="flex justify-between items-center"> - <Title title="수요처 현황" /> - </div> - <UiTable - headName={[]} - tableData={mngTable || []} - headlist={coSttHeadList} - pageSize={20} - total={coSttnResCount + 10 || 0} - cellClick={(index: number | undefined) => { - coMngCellClick(index); - // setUpdateData(coSttnRes[index as number]); - }} - onPageChange={(page) => { - setMngPage(page); - }} - ></UiTable></div>) - } - } - return null; // 조건에 맞는 콘텐츠가 없을 경우 - }; - - return ( - viewMode == 'nothing' ? <></> : - <NewWindowPortal - onClose={memoizedOnClose} - title="시설 상세 정보" - width={viewMode == 'list' ? window.screen.availWidth : 1100} - height={viewMode == 'list' ? window.screen.availHeight : layerId == '하수처리시설' && viewMode == 'detail' ? 450 : 900} - scrollbars='yes' - resizable='no'> - {/* 체크된 레이어에 따른 조건부 렌더링 */} - {renderConditionalContent()} - </NewWindowPortal> - ); -}; - -export default NewWindowPopup; \ No newline at end of file diff --git a/apps/wr/app/map/popup/page.tsx b/apps/wr/app/map/popup/page.tsx index 80830aa90..c7b23461f 100644 --- a/apps/wr/app/map/popup/page.tsx +++ b/apps/wr/app/map/popup/page.tsx @@ -3,7 +3,7 @@ import Gis from "@common/business_components/ui/map/Gis"; import { useEffect, useRef, useState, useCallback } from "react"; import { mapService } from "../service"; -import NewWindowPopup from "../newWindowPopup"; +import NewWindowPopup from "../components/newWindowPopup"; import GisApp from "@common/business_components/ui/map/lib/gis.core"; const Map = () => { From fb9dacd60c090a561d27a545183b93cfa761c1b8 Mon Sep 17 00:00:00 2001 From: JIeunhuh <entks301@naver.com> Date: Thu, 31 Jul 2025 17:58:33 +0900 Subject: [PATCH 04/11] =?UTF-8?q?=ED=8C=9D=EC=97=85=20=ED=81=AC=EA=B8=B0?= =?UTF-8?q?=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wr/app/map/components/newWindowPopup.tsx | 11 ++++------- .../wr/app/map/components/newWindowPortal.tsx | 19 ++++++++++++++----- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/apps/wr/app/map/components/newWindowPopup.tsx b/apps/wr/app/map/components/newWindowPopup.tsx index bcc0e6ec3..21d31b984 100644 --- a/apps/wr/app/map/components/newWindowPopup.tsx +++ b/apps/wr/app/map/components/newWindowPopup.tsx @@ -18,7 +18,6 @@ import ReuseMngFacility from './layerPopup/reuseMngFacility'; interface NewWindowPopupProps { featureData: any; seletectedLayerNm?: string - viewMode?: 'list' | 'detail' | 'nothing' onFeatureSelect: (data: any) => void; onClose: () => void; } @@ -125,9 +124,6 @@ const NewWindowPopup: React.FC<NewWindowPopupProps> = ({ layerId === '하수처리시설' ? selectedProps?.swtrmt_fclt_nm : null; if (!featureCd || featureCd === fcltPk) return; - setFcltPk(featureCd); - setActiveTab(featureCd); - setViewMode("detail"); if (layerId === '하수재이용시설') { fetchpbnstDetail(featureCd, setTabData); } else if (layerId === '하수처리시설') { @@ -140,6 +136,9 @@ const NewWindowPopup: React.FC<NewWindowPopupProps> = ({ else if (layerId === '수요처') setMngList(res); }); } + setFcltPk(featureCd); + setActiveTab(featureCd); + setViewMode("detail"); }, [featureData]); ///////////////////////////////////////////// 피쳐데이터 선택 시 각 시설별 데이터 처리 /////////////////////////////////////////////// @@ -167,15 +166,13 @@ const NewWindowPopup: React.FC<NewWindowPopupProps> = ({ } return null; // 조건에 맞는 콘텐츠가 없을 경우 }; - return ( viewMode == 'nothing' ? <></> : <NewWindowPortal onClose={memoizedOnClose} title="시설 상세 정보" - width={viewMode == 'list' ? window.screen.availWidth : 1100} - height={viewMode == 'list' ? window.screen.availHeight : layerId == '하수처리시설' && viewMode == 'detail' ? 450 : 900} scrollbars='yes' + viewMode={viewMode} resizable='no'> {/* 체크된 레이어에 따른 조건부 렌더링 */} {renderConditionalContent()} diff --git a/apps/wr/app/map/components/newWindowPortal.tsx b/apps/wr/app/map/components/newWindowPortal.tsx index b8da17d7b..119e3a52b 100644 --- a/apps/wr/app/map/components/newWindowPortal.tsx +++ b/apps/wr/app/map/components/newWindowPortal.tsx @@ -31,9 +31,10 @@ interface NewWindowPortalProps { height?: number; resizable?: string; scrollbars?: string; + viewMode?: 'list' | 'detail' | 'nothing'; } -const NewWindowPortal: React.FC<NewWindowPortalProps> = ({ children, onClose, title = "", width = 900, height = 800, resizable = "yes", scrollbars = "yes" }) => { +const NewWindowPortal: React.FC<NewWindowPortalProps> = ({ children, onClose, title = "", resizable = "yes", scrollbars = "yes", viewMode }) => { const [container, setContainer] = useState<HTMLElement | null>(null); const newWindowRef = useRef<Window | null>(null); const onCloseRef = useRef(onClose); @@ -46,7 +47,15 @@ const NewWindowPortal: React.FC<NewWindowPortalProps> = ({ children, onClose, ti const handleUnload = useCallback(() => { onCloseRef.current(); }, []); - + useEffect(() => { + if (newWindowRef.current) { + if (viewMode == 'detail') { + newWindowRef.current.resizeTo(1100, 700); + } else { + newWindowRef.current.resizeTo(screen.availWidth, screen.availHeight); + } + } + }, [viewMode]); useEffect(() => { // 이미 창이 열려있으면 새로 열지 않음 if (newWindowRef.current && !newWindowRef.current.closed) return; @@ -57,11 +66,11 @@ const NewWindowPortal: React.FC<NewWindowPortalProps> = ({ children, onClose, ti const screenHeight = window.innerHeight ?? document.documentElement.clientHeight; // 중앙 위치 계산 - const left = screenLeft + (screenWidth - width) / 2; - const top = screenTop + (screenHeight - height) / 2; + const left = screenLeft + (screenWidth - window.screen.width) / 2; + const top = screenTop + (screenHeight - window.screen.height) / 2; // 1. 새 창을 열고 참조를 저장합니다. - const popupOptions = `width=${width},height=${height},left=${left}, top=${top}, resizable=${resizable},scrollbars=${scrollbars}`; + const popupOptions = `width=${window.screen.width},height=${window.screen.height},left=${left}, top=${top}, resizable=${resizable},scrollbars=${scrollbars}`; newWindowRef.current = window.open('', title, popupOptions); if (newWindowRef.current) { From d23f928ddb56271fb9db7cca57c00c82d66b319f Mon Sep 17 00:00:00 2001 From: jyha00 <jyha@mind-one.co.kr> Date: Thu, 31 Jul 2025 19:53:17 +0900 Subject: [PATCH 05/11] =?UTF-8?q?=EC=88=98=EC=9A=94=EC=B2=98=EA=B4=80?= =?UTF-8?q?=EB=A6=AC=20=EB=B9=84=EB=B0=80=EB=B2=88=ED=98=B8=20=EC=B4=88?= =?UTF-8?q?=EA=B8=B0=ED=99=94=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wr/app/matMng/coMng/coSttn/page.tsx | 35 ++++-- apps/wr/app/store/matDmd/coSttn/index.tsx | 135 +++++++++++++--------- apps/wr/public/locales/ko/message.json | 24 ++-- 3 files changed, 118 insertions(+), 76 deletions(-) diff --git a/apps/wr/app/matMng/coMng/coSttn/page.tsx b/apps/wr/app/matMng/coMng/coSttn/page.tsx index 8e2705450..1dbf511eb 100644 --- a/apps/wr/app/matMng/coMng/coSttn/page.tsx +++ b/apps/wr/app/matMng/coMng/coSttn/page.tsx @@ -5,6 +5,7 @@ import { btnClass } from "@/app/store/common/code"; import { excelCoSttnParsing } from "@/app/store/matDmd/coSttn/excel"; import { fetchType, getFetch } from "@/components/lib/api"; import { + confirmToast, excelDownload, formatPhoneNumber, getCity, @@ -30,6 +31,7 @@ import { Button } from "@common/components/ui"; import moment from "moment"; import { useCallback, useEffect, useState } from "react"; import toast, { Toaster } from "react-hot-toast"; +import { useTranslation } from "react-i18next"; export default function CoSttn() { const { @@ -43,6 +45,7 @@ export default function CoSttn() { updateDemandCompany, coSttnTxtSearch, demandCompList, + coSttnPwInit, } = useWrStore((state) => state); const { regAuthrtYn, mdfcnAuthrtYn } = auth; @@ -103,9 +106,8 @@ export default function CoSttn() { ...item, telno: formatPhoneNumber(item.telno || undefined), regDt: moment(item.regDt).format("YYYY-MM-DD"), - dmdInfoId: `${item.dmdInfoId.slice(0, 3)}-${item.dmdInfoId.slice(3, 5)}-${item.dmdInfoId.slice(5,10)}-${item.dmdInfoId.slice(10)}` - })); - console.log("chData:", chData) + dmdInfoId: `${item.dmdInfoId.slice(0, 3)}-${item.dmdInfoId.slice(3, 5)}-${item.dmdInfoId.slice(5, 10)}-${item.dmdInfoId.slice(10)}` + })); // .filter((item: any) => item !== undefined); setBdstTable(chData); } else if (coSttnRes?.length == 0) { @@ -156,6 +158,18 @@ export default function CoSttn() { } } }; + const { t } = useTranslation('base'); + const initPassword = () => { + if (upadateData) { + const dmdInfoId = upadateData.dmdInfoId; + const handleConfirm = () => { + coSttnPwInit(dmdInfoId); + }; + confirmToast(t('isInitPw'), handleConfirm); + } else { + toast.error("비밀번호 초기화를 원하는 행을 선택해주세요.") + } + } return ( <> @@ -167,7 +181,7 @@ export default function CoSttn() { <SearchFormLeft> <div className="col-span-10 flex flex-row"> <SelectBox - className="col-span-2 w-1/6 h-full text-2xl" + className="col-span-3 w-1/6 h-full text-2xl" label="시도" selectClass={""} selectArray={code.cityCd || []} @@ -178,7 +192,7 @@ export default function CoSttn() { value={objSearch.city} /> <SelectBox - className="col-span-2 w-1/6 h-full text-2xl" + className="col-span-3 w-1/6 h-full text-2xl" label="시군구" selectClass={""} selectArray={selectSgg || []} @@ -195,7 +209,7 @@ export default function CoSttn() { /> </div> </SearchFormLeft> - <SearchFormRight className="w-2/12 mx-5 flex flex-col items-center justify-center space-x-2 "> + <SearchFormRight className="w-3/12 mx-5 flex flex-col items-center justify-center space-x-2 "> <div className="flex flex-row space-x-2"> <Button className="mt-2" size={"sm"} onClick={searchClick}> 조회 @@ -236,6 +250,13 @@ export default function CoSttn() { > 다운로드 </Button> + <Button + size={"sm"} + className={`${btnClass}`} + onClick={(initPassword)} + > + 비밀번호 초기화 + </Button> </div> </SearchFormRight> </SearchDiv> @@ -286,9 +307,7 @@ export default function CoSttn() { objSearch.sgg && objSearch.sgg !== "0" ? objSearch.sgg : objSearch.city; - console.log("data ", data); if (upadateData?.dmdInfoId) { - console.log("data ", data); await updateDemandCompany(data as DemandCompType); coSttnInit("1", "13", String(con)); setIsDmdModal({ diff --git a/apps/wr/app/store/matDmd/coSttn/index.tsx b/apps/wr/app/store/matDmd/coSttn/index.tsx index 1e20e8a76..1f7b2c54b 100644 --- a/apps/wr/app/store/matDmd/coSttn/index.tsx +++ b/apps/wr/app/store/matDmd/coSttn/index.tsx @@ -1,7 +1,8 @@ import type { CommonTableProps } from "@/app/store/common/type/header"; -import { fetchType, getFetchWithCount } from "@/components/lib/api"; +import { fetchType, getFetchWithCount, putFectch } from "@/components/lib/api"; import type { DemandCompType } from "@common/business_components/ui/sdmInsertForm/form"; import { coSttHeadListExport, coSttNodeListExport } from "./headList"; +import toast from "react-hot-toast"; // 테이블 헤드 리스트 const coSttNodeList = coSttNodeListExport; @@ -21,9 +22,10 @@ export type coSttnState = { }; type coSttnActions = { - coSttnInit:(pageNo:string, pageCnt:string, con: string) => void; - coSttnSearch: (pageNo:string, pageCnt:string, con: string) => void; - coSttnTxtSearch:(pageNo:string, pageCnt:string, con: string,txt:string) => void; + coSttnInit: (pageNo: string, pageCnt: string, con: string) => void; + coSttnSearch: (pageNo: string, pageCnt: string, con: string) => void; + coSttnTxtSearch: (pageNo: string, pageCnt: string, con: string, txt: string) => void; + coSttnPwInit: (dmdInfoId: string) => void; }; export type coSttnType = coSttnState & coSttnActions; @@ -44,7 +46,7 @@ export const coSttnActionsExport: (set: any) => coSttnActions = ( set: any ) => { return { - coSttnInit: async (pageNo:string, pageCnt:string, con: string) => { + coSttnInit: async (pageNo: string, pageCnt: string, con: string) => { try { const coSttnRes = await getFetchWithCount( @@ -67,56 +69,81 @@ export const coSttnActionsExport: (set: any) => coSttnActions = ( console.log(e) } }, - coSttnSearch: async (pageNo:string, pageCnt:string, con: string) => { - try { - const coSttnRes = await getFetchWithCount( - fetchType.WR, - `/reuseFc/request/${pageNo}/${pageCnt}/${con}` - ); - if (Number(pageCnt) >= 10000) { - set((state: coSttnState) => { - return { - ...state, - coSttn: { - ...state.coSttn, - excelList: coSttnRes.data, - //isInit: false, - }, - }; - }); - } else - set((state: coSttnState) => { - return { - ...state, - coSttn: { - ...state.coSttn, - coSttnRes: coSttnRes.data, - isInit: false, - coSttnResCount : coSttnRes.count, - }, - }; - }); - } catch (erTxt) { - console.log("error", erTxt); - } - }, - coSttnTxtSearch: async (pageNo:string, pageCnt:string, con: string, txt: string) => { - try { - const coSttnRes = await getFetchWithCount( - fetchType.WR, - `/reuseFc/request/${pageNo}/${pageCnt}/${con}/${txt}`) - set((state: coSttnState) => { - return { - ...state, - coSttn: { - ...state.coSttn, - coSttnRes: coSttnRes.data, - coSttnResCount: coSttnRes.count, - isInit: false, - }}}) - } catch (erTxt) { - console.log("error", erTxt); + coSttnSearch: async (pageNo: string, pageCnt: string, con: string) => { + try { + const coSttnRes = await getFetchWithCount( + fetchType.WR, + `/reuseFc/request/${pageNo}/${pageCnt}/${con}` + ); + if (Number(pageCnt) >= 10000) { + set((state: coSttnState) => { + return { + ...state, + coSttn: { + ...state.coSttn, + excelList: coSttnRes.data, + //isInit: false, + }, + }; + }); + } else + set((state: coSttnState) => { + return { + ...state, + coSttn: { + ...state.coSttn, + coSttnRes: coSttnRes.data, + isInit: false, + coSttnResCount: coSttnRes.count, + }, + }; + }); + } catch (erTxt) { + console.log("error", erTxt); + } + }, + coSttnTxtSearch: async (pageNo: string, pageCnt: string, con: string, txt: string) => { + try { + const coSttnRes = await getFetchWithCount( + fetchType.WR, + `/reuseFc/request/${pageNo}/${pageCnt}/${con}/${txt}`) + set((state: coSttnState) => { + return { + ...state, + coSttn: { + ...state.coSttn, + coSttnRes: coSttnRes.data, + coSttnResCount: coSttnRes.count, + isInit: false, + } } + }) + } catch (erTxt) { + console.log("error", erTxt); + } + }, + coSttnPwInit: async (dmdInfoId: string) => { + try { + const response = await putFectch(`/reuseFc/request/chgPassword/${dmdInfoId}`,[]) + if (response.status === 200) { + toast.success('비밀번호가 초기화 되었습니다.') + } else { + toast.error('비밀번호 초기화에 실패하였습니다') } + set((state: coSttnState) => { + return { + ...state, + coSttn: { + ...state.coSttn, + isInit: false, + } + } + }); + } catch (err) { + console.log(err); + toast.error('비밀번호 초기화에 실패하였습니다.') + + } + } } } \ No newline at end of file diff --git a/apps/wr/public/locales/ko/message.json b/apps/wr/public/locales/ko/message.json index d3c55de5e..859df00cf 100644 --- a/apps/wr/public/locales/ko/message.json +++ b/apps/wr/public/locales/ko/message.json @@ -1,10 +1,10 @@ { - "deleteMsg" : "{{name}}가 삭제되었습니다.", + "deleteMsg": "{{name}}가 삭제되었습니다.", "insertMsg": "{{name}}가 저장되었습니다.", "updateMsg": "{{name}}가 수정되었습니다.", - "errorMsg": "{{name}} 중 오류가 발생했습니다.", + "errorMsg": "{{name}} 중 오류가 발생했습니다.", "fileErrMsg": "빈 파일 또는 파일 오류입니다.", - "selectErrMsg": "{{name}}을(를) 선택해주세요.", + "selectErrMsg": "{{name}}을(를) 선택해주세요.", "noChangedErrMsg": "변경사항이 없습니다.", "dateErrMsg": "시작년도를 종료년도 이후로 설정할 수 없습니다.", "fileDateErrMsg": "파일업로드 이전에 등록년도를 작성해주세요.", @@ -12,19 +12,15 @@ "isUpdateMsg": "수정한 정보를 저장하시겠습니까?", "isInsertMsg": "등록하시겠습니까?", "isUpdatetMsg": "수정하시겠습니까?", - "matchFinishMsg" : "{{name}}이(가) 완료되었습니다.", + "matchFinishMsg": "{{name}}이(가) 완료되었습니다.", "excelDownErrMsg": "다운로드할 데이터가 없습니다.", - "isMatchFinishMsg":" 매칭완료를 진행하시겠습니까?", - "isNotMatchFinishMsg":"검토완료중 매칭완료 여부를 체크한 항목이 없습니다.", - "canNotChangeMatStateMsg":"매칭완료된 항목은 상태를 변경할 수 없습니다.", - - - + "isMatchFinishMsg": " 매칭완료를 진행하시겠습니까?", + "isNotMatchFinishMsg": "검토완료중 매칭완료 여부를 체크한 항목이 없습니다.", + "canNotChangeMatStateMsg": "매칭완료된 항목은 상태를 변경할 수 없습니다.", + "isInitPw": "비밀번호를 초기화하시겠습니까?", "alreadyInsertedErrMsg": "사업자번호 {{num}}(은)는 이미 등록되었습니다. 다시 등록해주세요.", "saveRowErrMsg": "수정을 원하는 행을 선택하고 저장버튼을 눌러주세요.", - "costCalcErrMsg":"공사비 계산 진행 후 저장을 눌러주세요", + "costCalcErrMsg": "공사비 계산 진행 후 저장을 눌러주세요", "maxYearErrMsg": "최대 {{year}}년까지 선택가능합니다.", "checkQtyMsg": "요구수량(m³/일)을 확인해주세요 계산할 수 없습니다." - -} - +} \ No newline at end of file From cb0f3160230da1d5a70e0e527e62bfe0af0d6d04 Mon Sep 17 00:00:00 2001 From: jyha00 <jyha@mind-one.co.kr> Date: Thu, 31 Jul 2025 20:11:10 +0900 Subject: [PATCH 06/11] =?UTF-8?q?=EC=88=98=EC=9A=94=EC=B2=98=EA=B4=80?= =?UTF-8?q?=EB=A6=AC=20=EB=B9=84=EB=B0=80=EB=B2=88=ED=98=B8=20=EC=B4=88?= =?UTF-8?q?=EA=B8=B0=ED=99=94=20=EC=B6=94=EA=B0=80=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wr/app/matMng/coMng/coSttn/page.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/wr/app/matMng/coMng/coSttn/page.tsx b/apps/wr/app/matMng/coMng/coSttn/page.tsx index 1dbf511eb..75e1ecff7 100644 --- a/apps/wr/app/matMng/coMng/coSttn/page.tsx +++ b/apps/wr/app/matMng/coMng/coSttn/page.tsx @@ -192,7 +192,7 @@ export default function CoSttn() { value={objSearch.city} /> <SelectBox - className="col-span-3 w-1/6 h-full text-2xl" + className="col-span-3 w-1/6 h-full text-2xl ml-4" label="시군구" selectClass={""} selectArray={selectSgg || []} @@ -200,7 +200,7 @@ export default function CoSttn() { value={objSearch.sgg} /> <SearchInput - className={"!text-3xl"} + className={"!text-3xl ml-4"} label="회사명" textValue={seartchTxt ?? ""} setText={(e) => { From 8eea3c75aa82327ae5d670640afc242933766332 Mon Sep 17 00:00:00 2001 From: leedj312 <dlehd467@gmail.com> Date: Thu, 31 Jul 2025 20:16:38 +0900 Subject: [PATCH 07/11] =?UTF-8?q?=EC=88=98=EC=9A=94=EB=A7=A4=EC=B9=AD=20?= =?UTF-8?q?=EA=B2=80=ED=86=A0=20=EC=9D=B4=EC=83=81=20=EC=9D=B4=ED=95=98=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wr/app/matMng/coMat/coMatRvw/page.tsx | 307 +++++++++++++-------- 1 file changed, 185 insertions(+), 122 deletions(-) diff --git a/apps/wr/app/matMng/coMat/coMatRvw/page.tsx b/apps/wr/app/matMng/coMat/coMatRvw/page.tsx index ff35b95c5..996b624d8 100644 --- a/apps/wr/app/matMng/coMat/coMatRvw/page.tsx +++ b/apps/wr/app/matMng/coMat/coMatRvw/page.tsx @@ -376,87 +376,116 @@ export default function CoMatRvw() { <div className="col-span-12 w-full"> <h1 className={formH1Class}>요구수질</h1> </div> - <div className="col-span-3 mt-4"> + <div className="col-span-2 mt-4"> <FormField control={form.control} name="grssClfg" render={({ field }) => ( - <FormUi label="총대장균군수 (개/100ml)"> - <Input - placeholder="" - type="number" - step="0.1" - onChange={(e) => { - field.onChange(Number(e.target.value)); - }} - value={field.value ?? 0} - readOnly - /> + <FormUi label={"총대장균군수(개/mg)"}> + <div className="flex items-center gap-2"> + <div className="relative flex-1 min-w-0"> + <Input + type="text" + className="w-full pr-16 border py-2 text-right rounded-md" + placeholder="" + value={ + field.value != -1 + ? `${field.value}` + : "불검출" + } + readOnly + /> + {/* TODO: 총대장균군수 확인 필요*/} + {field.value != -1 ? ( + <span className="text-xl absolute inset-y-0 right-3 flex items-center text-gray-500 pointer-events-none"> + 이하 + </span> + ) : ( + <></> + )} + </div> + </div> </FormUi> )} /> </div> - <div className="col-span-3 mt-4"> + <div className="col-span-2 mt-4"> <FormField control={form.control} name="combResdch" render={({ field }) => ( - <FormUi label="결합잔류염소(mg/L)"> - <Input - placeholder="" - type="number" - step="0.1" - onChange={(e) => { - field.onChange(Number(e.target.value)); - }} - value={field.value ?? 0} - readOnly - /> + <FormUi label={"결합잔류염소(mg/L)"}> + <div className="flex items-center gap-2"> + <div className="relative flex-1 min-w-0"> + <Input + type="text" + className="w-full pr-16 border py-2 text-right rounded-md" + placeholder="" + value={field.value ?? ""} + readOnly + /> + <span className="text-xl absolute inset-y-0 right-3 flex items-center text-gray-500 pointer-events-none"> + 이상 + </span> + </div> + </div> </FormUi> )} /> </div> - <div className="col-span-3 mt-4"> + <div className="col-span-2 mt-4"> <FormField control={form.control} name="trbt" render={({ field }) => ( <FormUi label="탁도(NTU)"> - <Input - placeholder="" - type="number" - step="0.1" - onChange={(e) => { - field.onChange(Number(e.target.value)); - }} - value={field.value ?? 0} - readOnly - /> + <div className="flex items-center gap-2"> + <div className="relative flex-1 min-w-0"> + <Input + placeholder="" + className="w-full pr-16 border py-2 text-right rounded-md" + type="text" + value={field.value ?? 0} + readOnly + /> + <span className=" text-xl absolute inset-y-0 right-3 flex items-center text-gray-500 pointer-events-none"> + 이하 + </span> + </div> + </div> </FormUi> )} /> </div> - <div className="col-span-3 mt-4"> + <div className="col-span-2 mt-4"> <FormField control={form.control} name="blgcOxygDmndQnt" render={({ field }) => ( <FormUi label="생물학적 산소요구량(BOD)(mg/L)"> - <Input - placeholder="" - type="number" - step="0.1" - onChange={(e) => { - field.onChange(Number(e.target.value)); - }} - value={field.value ?? 0} - readOnly - /> + <div className="flex items-center gap-2"> + <div className="relative flex-1 min-w-0"> + <Input + placeholder="" + type="text" + className="w-full pr-16 border py-2 text-right rounded-md" + step="0.1" + onChange={(e) => { + field.onChange(Number(e.target.value)); + }} + value={field.value ?? ""} + readOnly + /> + <span className="text-xl absolute inset-y-0 right-3 flex items-center text-gray-500 pointer-events-none"> + 이하 + </span> + </div> + </div> </FormUi> )} /> </div> - <div className="col-span-3 mt-4"> + <div className="col-span-2 mt-4"> <FormField control={form.control} name="smll" @@ -464,138 +493,172 @@ export default function CoMatRvw() { <FormUi label="냄새"> <Input placeholder="" - type="number" - step="0.1" - onChange={(e) => { - field.onChange(Number(e.target.value)); - }} - value={field.value ?? 0} + type="text" + value={field.value ?? ""} readOnly /> </FormUi> )} /> </div> - <div className="col-span-3 mt-4"> + <div className="col-span-2 mt-4"> <FormField control={form.control} name="crmty" render={({ field }) => ( <FormUi label="색도(도)"> - <Input - placeholder="" - type="number" - step="0.1" - onChange={(e) => { - field.onChange(Number(e.target.value)); - }} - value={field.value ?? 0} - readOnly - /> + <div className="flex items-center gap-2"> + <div className="relative flex-1 min-w-0"> + <Input + placeholder="" + className="w-full pr-16 border py-2 text-right rounded-md" + type="text" + step="0.1" + value={field.value ?? ""} + readOnly + /> + <span className="text-xl absolute inset-y-0 right-3 flex items-center text-gray-500 pointer-events-none"> + 이하 + </span> + </div> + </div> </FormUi> )} /> </div> - <div className="col-span-3 mt-4"> + <div className="col-span-2 mt-4"> <FormField control={form.control} name="tn" render={({ field }) => ( - <FormUi label="총질소 (T-N)(mg/L)"> - <Input - placeholder="" - type="number" - step="0.1" - onChange={(e) => { - field.onChange(Number(e.target.value)); - }} - value={field.value ?? 0} - readOnly - /> + <FormUi label="총질소(T-N)(mg/L)"> + <div className="flex items-center gap-2"> + <div className="relative flex-1 min-w-0"> + <Input + placeholder="" + className="w-full pr-16 border py-2 text-right rounded-md" + type="text" + onChange={(e) => { + field.onChange(Number(e.target.value)); + }} + value={field.value ?? 0} + readOnly + /> + <span className="text-xl absolute inset-y-0 right-3 flex items-center text-gray-500 pointer-events-none"> + 이하 + </span> + </div> + </div> </FormUi> )} /> </div> - <div className="col-span-3 mt-4"> + <div className="col-span-2 mt-4"> <FormField control={form.control} name="tph" render={({ field }) => ( <FormUi label="총인(T-P)(mg/L)"> - <Input - placeholder="" - type="number" - step="0.1" - onChange={(e) => { - field.onChange(Number(e.target.value)); - }} - value={field.value ?? 0} - readOnly - /> + <div className="flex items-center gap-2"> + <div className="relative flex-1 min-w-0"> + <Input + placeholder="" + className="w-full pr-16 border py-2 text-right rounded-md" + type="text" + onChange={(e) => { + field.onChange(Number(e.target.value)); + }} + value={field.value ?? 0} + readOnly + /> + <span className="text-xl absolute inset-y-0 right-3 flex items-center text-gray-500 pointer-events-none"> + 이하 + </span> + </div> + </div> </FormUi> )} /> </div> - <div className="col-span-3 mt-4"> + <div className="col-span-2 mt-4"> <FormField control={form.control} name="ph" render={({ field }) => ( - <FormUi label="수소이온농도(pH)"> - <Input - placeholder="" - type="number" - step="0.1" - onChange={(e) => { - field.onChange(Number(e.target.value)); - }} - value={field.value ?? 0} - readOnly - /> + <FormUi label="수소이온농도(PH)"> + <div className="flex items-center gap-2"> + <div className="relative flex-1 min-w-0"> + <Input + placeholder="" + className="w-full pr-16 border py-2 text-right rounded-md" + type="text" + onChange={(e) => { + field.onChange(Number(e.target.value)); + }} + value={field.value ?? 0} + readOnly + /> + </div> + </div> </FormUi> )} /> </div> - <div className="col-span-3 mt-4"> + <div className="col-span-2 mt-4"> <FormField control={form.control} name="clIonQnt" render={({ field }) => ( <FormUi label="염화물(mgCL/L)"> - <Input - placeholder="" - type="number" - step="0.1" - onChange={(e) => { - field.onChange(Number(e.target.value)); - }} - value={field.value ?? 0} - readOnly - /> + <div className="flex items-center gap-2"> + <div className="relative flex-1 min-w-0"> + <Input + placeholder="" + className="w-full pr-16 border py-2 text-right rounded-md" + type="text" + onChange={(e) => { + field.onChange(Number(e.target.value)); + }} + value={field.value ?? 0} + readOnly + /> + <span className="text-xl absolute inset-y-0 right-3 flex items-center text-gray-500 pointer-events-none"> + 이하 + </span> + </div> + </div> </FormUi> )} /> </div> - <div className="col-span-3 mt-4"> + <div className="col-span-2 mt-4"> <FormField control={form.control} name="cdtnv" render={({ field }) => ( <FormUi label="전기전도도(㎲/cm)"> - <Input - placeholder="" - type="number" - onChange={(e) => { - field.onChange(Number(e.target.value)); - }} - value={field.value ?? 0} - readOnly - /> + <div className="flex items-center gap-2"> + <div className="relative flex-1 min-w-0"> + <Input + placeholder="" + className="w-full pr-16 border py-2 text-right rounded-md" + type="text" + onChange={(e) => { + field.onChange(Number(e.target.value)); + }} + value={field.value ?? 0} + readOnly + /> + <span className="text-xl absolute inset-y-0 right-3 flex items-center text-gray-500 pointer-events-none"> + 이하 + </span> + </div> + </div> </FormUi> )} /> </div> - <div className="col-span-3 mt-4"> + <div className="col-span-2 mt-4"> <FormField control={form.control} name="rmrk" @@ -648,7 +711,7 @@ export default function CoMatRvw() { form={form} type={"mat"} selectionVersion={selectionVersion} - readonly={regAuthrtYn ==="N"} + readonly={regAuthrtYn === "N"} /> </ListDiv> {/* </ScrollArea> */} From 59e3dbbe97905603320e9f4c472537e53c386aae Mon Sep 17 00:00:00 2001 From: jyha00 <jyha@mind-one.co.kr> Date: Fri, 1 Aug 2025 14:59:36 +0900 Subject: [PATCH 08/11] =?UTF-8?q?=EC=A4=91=EC=88=98=EB=8F=84=20=EC=8B=A0?= =?UTF-8?q?=EA=B7=9C=EB=93=B1=EB=A1=9D=20=ED=85=8D=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wr/components/ui/form/fcltReg/rswtrReg/rswtrRegForm.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/wr/components/ui/form/fcltReg/rswtrReg/rswtrRegForm.tsx b/apps/wr/components/ui/form/fcltReg/rswtrReg/rswtrRegForm.tsx index 59ce18d93..055d0372f 100644 --- a/apps/wr/components/ui/form/fcltReg/rswtrReg/rswtrRegForm.tsx +++ b/apps/wr/components/ui/form/fcltReg/rswtrReg/rswtrRegForm.tsx @@ -158,7 +158,7 @@ export default function RswtrRegForm({ </h1> <div className="grid grid-cols-12 flex items-center card-header"> <h1 className="text-2xl font-medium text-label col-span-10"> - 건축물 운영정보 + 건축물 정보 </h1> {!readonly && ( <Button From 8653335aa9619712fc11cb0294798bd2e3cd6cb6 Mon Sep 17 00:00:00 2001 From: leedj312 <dlehd467@gmail.com> Date: Fri, 1 Aug 2025 15:26:54 +0900 Subject: [PATCH 09/11] =?UTF-8?q?=EC=88=98=EC=9A=94=EC=8B=A0=EC=B2=AD=20?= =?UTF-8?q?=EC=82=AC=EC=97=85=EC=9E=90=20=EB=B9=84=EB=B0=80=EB=B2=88?= =?UTF-8?q?=ED=98=B8=20=EC=B4=88=EA=B8=B0=20=ED=99=94=20=EB=A9=94=EC=8B=9C?= =?UTF-8?q?=EC=A7=80=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wr/app/matMng/coMng/coSttn/page.tsx | 3 ++- apps/wr/components/lib/func/commom.tsx | 4 ++-- apps/wr/public/locales/ko/message.json | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/apps/wr/app/matMng/coMng/coSttn/page.tsx b/apps/wr/app/matMng/coMng/coSttn/page.tsx index 75e1ecff7..5c4f85849 100644 --- a/apps/wr/app/matMng/coMng/coSttn/page.tsx +++ b/apps/wr/app/matMng/coMng/coSttn/page.tsx @@ -162,10 +162,11 @@ export default function CoSttn() { const initPassword = () => { if (upadateData) { const dmdInfoId = upadateData.dmdInfoId; + const dmdInfoSliceId = `${dmdInfoId.slice(0, 3)}-${dmdInfoId.slice(3, 5)}-${dmdInfoId.slice(5, 10)}-${dmdInfoId.slice(10)}` const handleConfirm = () => { coSttnPwInit(dmdInfoId); }; - confirmToast(t('isInitPw'), handleConfirm); + confirmToast( t('isInitPw', { dmdInfoId: dmdInfoSliceId }), handleConfirm, 'text-2xl'); } else { toast.error("비밀번호 초기화를 원하는 행을 선택해주세요.") } diff --git a/apps/wr/components/lib/func/commom.tsx b/apps/wr/components/lib/func/commom.tsx index 4902ccf3d..fef689f46 100644 --- a/apps/wr/components/lib/func/commom.tsx +++ b/apps/wr/components/lib/func/commom.tsx @@ -201,11 +201,11 @@ export const formatPhoneNumber = (tel: string) => { } }; -export const confirmToast = (content: string, saveFunc: () => void) => { +export const confirmToast = (content: string, saveFunc: () => void, contentClass?:string) => { toast( (t) => ( <span className="flex flex-col items-center"> - <div className="mb-6">{content}</div> + <div className={`mb-6 ${contentClass}`} >{content}</div> <div className="flex flex-row space-x-2"> <Button size={"sm"} diff --git a/apps/wr/public/locales/ko/message.json b/apps/wr/public/locales/ko/message.json index 859df00cf..fb7af9ed2 100644 --- a/apps/wr/public/locales/ko/message.json +++ b/apps/wr/public/locales/ko/message.json @@ -17,7 +17,7 @@ "isMatchFinishMsg": " 매칭완료를 진행하시겠습니까?", "isNotMatchFinishMsg": "검토완료중 매칭완료 여부를 체크한 항목이 없습니다.", "canNotChangeMatStateMsg": "매칭완료된 항목은 상태를 변경할 수 없습니다.", - "isInitPw": "비밀번호를 초기화하시겠습니까?", + "isInitPw":"사업자번호 {{dmdInfoId}} 의 비밀번호를 초기화하시겠습니까? (초기화비밀번호는 해당 사업자의 사업자 번호입니다)", "alreadyInsertedErrMsg": "사업자번호 {{num}}(은)는 이미 등록되었습니다. 다시 등록해주세요.", "saveRowErrMsg": "수정을 원하는 행을 선택하고 저장버튼을 눌러주세요.", "costCalcErrMsg": "공사비 계산 진행 후 저장을 눌러주세요", From cb83b7f92b1e4a74934ba8037de8a148bb313fe8 Mon Sep 17 00:00:00 2001 From: jyha00 <jyha@mind-one.co.kr> Date: Fri, 1 Aug 2025 15:50:44 +0900 Subject: [PATCH 10/11] =?UTF-8?q?=ED=95=98=EC=88=98=EC=9E=AC=EC=9D=B4?= =?UTF-8?q?=EC=9A=A9=EC=8B=9C=EC=84=A4=20=ED=98=84=ED=99=A9=20=EC=97=91?= =?UTF-8?q?=EC=85=80=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EC=97=86=EC=9D=84=20?= =?UTF-8?q?=EC=8B=9C=20toast=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ruseFcltMng/fcltSttn/pbnstSttn/page.tsx | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/apps/wr/app/ruseFcltMng/fcltSttn/pbnstSttn/page.tsx b/apps/wr/app/ruseFcltMng/fcltSttn/pbnstSttn/page.tsx index 8aea16c00..21f72f449 100644 --- a/apps/wr/app/ruseFcltMng/fcltSttn/pbnstSttn/page.tsx +++ b/apps/wr/app/ruseFcltMng/fcltSttn/pbnstSttn/page.tsx @@ -21,6 +21,8 @@ import ListDiv from "@common/business_components/ui/list_div"; import { HeadMakeColSpan } from "@common/business_components/ui/table/HeadMakeColSpan"; import { Button, Label } from "@common/components/ui"; import { useCallback, useEffect, useRef, useState } from "react"; +import toast, { Toaster } from "react-hot-toast"; +import { useTranslation } from "react-i18next"; export default function PbnstSttn() { const { @@ -46,10 +48,11 @@ export default function PbnstSttn() { isModal: boolean; data: customPbnst; }>({ isModal: false, data: {} as customPbnst }); - + const isDownloadTriggered = useRef(false); const [pbnRuseNm, setPbnRuseNm] = useState<string>(""); - + const { t } = useTranslation('base'); const excelClick = useCallback(() => { + isDownloadTriggered.current = true; pbnstSttnSearch( true, String(1), @@ -77,9 +80,9 @@ export default function PbnstSttn() { }, [pbnstSttn.isInit]); useEffect(() => { - pbnSttnInit("1", "15", ref.current, searchYear, fclts); + pbnSttnInit("1", "15", ref.current, searchYear, fclts); }, [fclts]); - + useEffect(() => { if (pbnstSttnRes && pbnstSttnRes.length > 0) { setBdstTable(pbnstSttnRes); @@ -90,7 +93,10 @@ export default function PbnstSttn() { useEffect(() => { - if (pbnstSttn.excelList.length >= 0) { + if (!isDownloadTriggered.current) { + return; + } + if (pbnstSttn.excelList.length > 0) { excelDownload( pbnstSttn.excelList, "하수재이용시설 현황", @@ -98,7 +104,10 @@ export default function PbnstSttn() { "A1:U2", 3 ); + } else { + toast.error(t('excelDownErrMsg')); } + isDownloadTriggered.current = false; }, [pbnstSttn.excelList]); @@ -162,8 +171,8 @@ export default function PbnstSttn() { /> </div> } - queryClick={ ()=> { - // pbnSearch(returnType); + queryClick={() => { + // pbnSearch(returnType); //setTableIndex(0) setIsReset(true); @@ -202,7 +211,7 @@ export default function PbnstSttn() { ); setIsReset(false); }} - + resetPageNo={isReset} > <HeadMakeColSpan upHeadList={pbnstSttn.pbnstSttNodeList} /> @@ -242,6 +251,7 @@ export default function PbnstSttn() { }} /> </ContentsDiv> + <Toaster position="top-center" /> </> ); } From 9bdf01e7f3272aec4aed2c9031bc59851730b111 Mon Sep 17 00:00:00 2001 From: jyha00 <jyha@mind-one.co.kr> Date: Fri, 1 Aug 2025 16:16:50 +0900 Subject: [PATCH 11/11] =?UTF-8?q?=EB=AA=A8=EB=8B=AC=EC=B0=BD=20=EC=82=AC?= =?UTF-8?q?=EC=9D=B4=EC=A6=88=20=EC=A1=B0=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wr/app/matMng/coMng/coSttn/page.tsx | 4 +- .../sttn/sttnDetailModal/coDatailModal.tsx | 37 +++++++++---------- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/apps/wr/app/matMng/coMng/coSttn/page.tsx b/apps/wr/app/matMng/coMng/coSttn/page.tsx index 75e1ecff7..4a6c74951 100644 --- a/apps/wr/app/matMng/coMng/coSttn/page.tsx +++ b/apps/wr/app/matMng/coMng/coSttn/page.tsx @@ -129,7 +129,7 @@ export default function CoSttn() { coExcel as [], "수요처 관리", "sample_coSttn.xlsx", - "A1:H1", + "A1:G1", 2 ); } @@ -288,7 +288,7 @@ export default function CoSttn() { </div> <UiModal bodyClass={ - "w-[40vw] max-w-none h-[80vh] " // +upadateData?.dmdInfoId?'h-[60vh]':'h-[90vh]' + "w-[40vw] max-w-none h-[83vh] " // +upadateData?.dmdInfoId?'h-[60vh]':'h-[90vh]' } open={isDmdModal.isMoal} onOpenChange={() => { diff --git a/apps/wr/components/ui/modal/sttn/sttnDetailModal/coDatailModal.tsx b/apps/wr/components/ui/modal/sttn/sttnDetailModal/coDatailModal.tsx index cb38717a3..aef145cc1 100644 --- a/apps/wr/components/ui/modal/sttn/sttnDetailModal/coDatailModal.tsx +++ b/apps/wr/components/ui/modal/sttn/sttnDetailModal/coDatailModal.tsx @@ -48,22 +48,21 @@ export default function CoDetailModal({ return ( <div> - <ScrollArea className="h-[85vh] mx-8"> - <Form {...form}> - <form onSubmit={form.handleSubmit(onFormSubmit)}> - <div className="grid grid-cols-12 items-center card-header"> - <h1 className="text-2xl font-medium text-label col-span-10"> - 잠재수요처 상세정보 - </h1> - <Button type="submit" - className="col-start-12 col-span-1" - size={"sm"} - disabled={readonly} - > - 저장 - </Button> - </div> - + <Form {...form}> + <form onSubmit={form.handleSubmit(onFormSubmit)}> + <div className="grid grid-cols-12 items-center card-header"> + <h1 className="text-2xl font-medium text-label col-span-10"> + 잠재수요처 상세정보 + </h1> + <Button type="submit" + className="col-start-12 col-span-1" + size={"sm"} + disabled={readonly} + > + 저장 + </Button> + </div> + <ScrollArea className="h-[79vh] mx-8"> <FormField control={form.control} name="fctryMngSn" @@ -865,9 +864,9 @@ export default function CoDetailModal({ /> </div> </div> - </form> - </Form> - </ScrollArea> + </ScrollArea> + </form> + </Form> </div> ); }