diff --git a/apps/cs/src/pages/groups/model/group-menu-authority-table-columns.tsx b/apps/cs/src/pages/groups/model/group-menu-authority-table-columns.tsx index 0a56a6701..3e26e1106 100644 --- a/apps/cs/src/pages/groups/model/group-menu-authority-table-columns.tsx +++ b/apps/cs/src/pages/groups/model/group-menu-authority-table-columns.tsx @@ -11,6 +11,7 @@ export const createColumns = ( form: UseFormReturn, ): ColumnDef[] => { const getCurrentRow = (i: number) => form.watch("menuAuthority")[i]; + const menuAuthorityData = form.watch("menuAuthority"); const handleCheckedChange = (i: number, key: keyof MenuAuthorityPayload) => (checked: boolean) => { @@ -20,6 +21,19 @@ export const createColumns = ( form.setValue(`menuAuthority.${i}.${key}`, checked ? "Y" : "N"); }; + const handleHeaderCheckedChange = (key: keyof MenuAuthorityPayload) => (checked: boolean) => { + menuAuthorityData.forEach((_, index) => { + if (getCurrentRow(index)?.mode === "R") { + form.setValue(`menuAuthority.${index}.mode`, "U"); + } + form.setValue(`menuAuthority.${index}.${key}`, checked ? "Y" : "N"); + }); + }; + + const isAllChecked = (key: keyof MenuAuthorityPayload) => { + return menuAuthorityData.every((item) => item?.[key] === "Y"); + }; + return [ { accessorKey: "menuNm", @@ -27,7 +41,41 @@ export const createColumns = ( }, { accessorKey: "mngAuthrtYn", - header: "관리권한", + header: () => ( +
+ 관리권한 + { + if (checked) { + // 관리권한 체크시 모든 권한 체크 + menuAuthorityData.forEach((_, index) => { + if (getCurrentRow(index)?.mode === "R") { + form.setValue(`menuAuthority.${index}.mode`, "U"); + } + form.setValue(`menuAuthority.${index}.regAuthrtYn`, "Y"); + form.setValue(`menuAuthority.${index}.mdfcnAuthrtYn`, "Y"); + form.setValue(`menuAuthority.${index}.delAuthrtYn`, "Y"); + form.setValue(`menuAuthority.${index}.inqAuthrtYn`, "Y"); + form.setValue(`menuAuthority.${index}.mngAuthrtYn`, "Y"); + }); + } else { + // 관리권한 해제시 모든 권한 해제 + menuAuthorityData.forEach((_, index) => { + if (getCurrentRow(index)?.mode === "R") { + form.setValue(`menuAuthority.${index}.mode`, "U"); + } + form.setValue(`menuAuthority.${index}.regAuthrtYn`, "N"); + form.setValue(`menuAuthority.${index}.mdfcnAuthrtYn`, "N"); + form.setValue(`menuAuthority.${index}.delAuthrtYn`, "N"); + form.setValue(`menuAuthority.${index}.inqAuthrtYn`, "N"); + form.setValue(`menuAuthority.${index}.mngAuthrtYn`, "N"); + }); + } + }} + /> +
+ ), cell: ({ row }) => { return ( ( +
+ 등록권한 + { + if (checked) { + // 등록권한 체크시 조회권한도 체크 + menuAuthorityData.forEach((_, index) => { + if (getCurrentRow(index)?.mode === "R") { + form.setValue(`menuAuthority.${index}.mode`, "U"); + } + form.setValue(`menuAuthority.${index}.inqAuthrtYn`, "Y"); + form.setValue(`menuAuthority.${index}.regAuthrtYn`, "Y"); + }); + } else { + // 등록권한 해제시 해당 권한만 해제 + handleHeaderCheckedChange("regAuthrtYn")(false); + } + }} + /> +
+ ), cell: ({ row }) => { return ( ( +
+ 수정권한 + +
+ ), cell: ({ row }) => { return ( ( +
+ 삭제권한 + +
+ ), cell: ({ row }) => { return ( ( +
+ 조회권한 + +
+ ), cell: ({ row }) => { return ( MenuAuthorityApi.getGroupMenuAuthorityList({ groupId }), + queryFn: () => MenuAuthorityApi.getGroupMenuAuthorityList({ groupId, useYn: "Y" }), enabled: open && !!groupId, }); @@ -82,7 +82,7 @@ function MenuAuthorityDialog({ groupId }: MenuAuthorityDialogProps) { 메뉴권한 - + 그룹별 메뉴 권한 diff --git a/apps/dm/app/(popup-layout)/map/popup/page.tsx b/apps/dm/app/(popup-layout)/map/popup/page.tsx index 8547c7ea8..6efb4ebef 100644 --- a/apps/dm/app/(popup-layout)/map/popup/page.tsx +++ b/apps/dm/app/(popup-layout)/map/popup/page.tsx @@ -249,17 +249,14 @@ const Map = () => { isSearchModalOpen={isSearchModalOpen} onCheckedLayersChange={onCheckedLayersChange} imgVisble={false} - searchBar={(props: any) => ( + searchBar={ { - if (hasuPk) { - setSelectDataPk(hasuPk); - } + if (hasuPk) setSelectDataPk(hasuPk); }} onApply={onApply} - {...props} /> - )} + } waterDepth={undefined} /> diff --git a/apps/wr/app/(popUp-layout)/layout.tsx b/apps/wr/app/(popUp-layout)/layout.tsx new file mode 100644 index 000000000..d3804ac93 --- /dev/null +++ b/apps/wr/app/(popUp-layout)/layout.tsx @@ -0,0 +1,13 @@ +"use client"; + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + <> +
{children}
+ + ); +} diff --git a/apps/wr/app/(popUp-layout)/mapDetilePopUp/page.tsx b/apps/wr/app/(popUp-layout)/mapDetilePopUp/page.tsx new file mode 100644 index 000000000..68be4a266 --- /dev/null +++ b/apps/wr/app/(popUp-layout)/mapDetilePopUp/page.tsx @@ -0,0 +1,84 @@ +"use client"; + +import { CoSttnMap } from "@/components/ui/map/LayerPopUp/CoSttnMap/CoSttnMap"; +import { HasuFacility } from "@/components/ui/map/LayerPopUp/HasuFacilty/HasuFacility"; +import { HtdflFacility } from "@/components/ui/map/LayerPopUp/HedfFacilty/HtdfFacilty"; +import { LdrmntCoMap } from "@/components/ui/map/LayerPopUp/LdrmntCoMap/LdrmntCoMap"; +import { RainFacility } from "@/components/ui/map/LayerPopUp/RainFacility/RainFacility"; +import { RswtFacility } from "@/components/ui/map/LayerPopUp/RswtFacilty/RswtFacilty"; +import { RuseFacility } from "@/components/ui/map/LayerPopUp/RuseFacilty/RuseFacility"; +import { useSearchParams } from "next/navigation"; + +export default function MapDetailPopUp() { + const searchParams = useSearchParams(); + const bc = new BroadcastChannel("mapDetailPopUp"); + + const renderConditionalContent = () => { + const layerId = searchParams.get("layerNm"); + switch (layerId) { + case "하수재이용시설": + return ( + { + bc.postMessage({ type: "SELECT_FEATURE", feature }); + }} + /> + ); + case "하수처리시설": + return ( + { + bc.postMessage({ type: "SELECT_FEATURE", feature }); + }} + /> + ); + case "빗물이용시설": + return ( + { + bc.postMessage({ type: "SELECT_FEATURE", feature }); + }} + /> + ); + case "중수도시설": + return ( + { + bc.postMessage({ type: "SELECT_FEATURE", feature }); + }} + /> + ); + case "온배수시설": + return ( + { + bc.postMessage({ type: "SELECT_FEATURE", feature }); + }} + /> + ); + case "수요처": + return ( + { + bc.postMessage({ type: "SELECT_FEATURE", feature }); + }} + /> + ); + case "잠재수요처": + return ( + { + bc.postMessage({ type: "SELECT_FEATURE", feature }); + }} + /> + ); + default: + return null; // 안전하게 아무 것도 렌더링하지 않음 + } + }; + return ( + <> +
{renderConditionalContent()}
+ + ); +} diff --git a/apps/wr/app/(popUp-layout)/mapFeaturePopup/page.tsx b/apps/wr/app/(popUp-layout)/mapFeaturePopup/page.tsx new file mode 100644 index 000000000..1246525d9 --- /dev/null +++ b/apps/wr/app/(popUp-layout)/mapFeaturePopup/page.tsx @@ -0,0 +1,136 @@ +"use client"; +import { useWrStore } from "@/app/store"; +import { PbnstSttnModal } from "@/components/ui/modal/sttn/pbnstSttnModal/pbnstSttnModal"; +import CoDetailModal from "@/components/ui/modal/sttn/sttnDetailModal/coDatailModal"; +import SttnDetailModal from "@/components/ui/modal/sttn/sttnDetailModal/sttnDetailModal"; +import { Title, UiTable } from "@common/business_components/ui"; +import { useSearchParams } from "next/navigation"; +import { useEffect, useState } from "react"; + +export default function MapFeaturePopup() { + const [isFeatureCd, setIsFeatureCd] = useState(null); + const { + pbnMap, + ruseMapDetail, + rainMapDetile, + rswtMapDetile, + htdfMapDetile, + pbnMapDetaile, + ldrmntMapDetile, + } = useWrStore((state) => state); + const { pbnMapHeadList } = pbnMap; + const [isDetail, setIsDetail] = useState(null); + const [layerNm, setLayerNm] = useState(); + const searchParams = useSearchParams(); + + useEffect(() => { + const layerName = searchParams.get("layerNm"); + const featureCd = searchParams.get("featureCd"); + setLayerNm(String(layerName)); + setIsFeatureCd(featureCd); + + const handleUnload = () => { + // 부모에게 닫힘 알림 + window.opener?.postMessage( + { type: "POPUP_CLOSED", target: "detail" }, + "*" + ); + }; + + window.addEventListener("beforeunload", handleUnload); + + return () => { + window.removeEventListener("beforeunload", handleUnload); + }; + }, []); + + useEffect(() => { + if (isFeatureCd && layerNm) { + let detail = {}; + if (layerNm === "하수재이용시설") { + detail = ruseMapDetail(isFeatureCd); + setIsDetail(detail); + } else if (layerNm === "하수처리시설") { + pbnMapDetaile(isFeatureCd, isFeatureCd).then((res) => { + setIsDetail(res); + }); + } else if (layerNm === "빗물이용시설") { + rainMapDetile(isFeatureCd).then((res) => { + setIsDetail(res); + }); + } else if (layerNm === "중수도시설") { + rswtMapDetile(isFeatureCd).then((res) => { + setIsDetail(res); + }); + } else if (layerNm === "온배수시설") { + htdfMapDetile(isFeatureCd).then((res) => { + setIsDetail(res); + }); + } else if (layerNm === "잠재수요처") { + ldrmntMapDetile(isFeatureCd).then((res) => { + setIsDetail(res); + }); + } + } + }, [isFeatureCd, layerNm]); + + if (!isDetail) return null; // 데이터 없으면 아무것도 안 그림 + return ( + <> + {isDetail ? ( +
+ {layerNm === "하수재이용시설" ? ( + + ) : layerNm === "하수처리시설" ? ( +
+
+ + </div> + {/* 높이 조정 */} + <UiTable + tableData={isDetail || []} + headlist={pbnMapHeadList.slice(0, 4)} + pageSize={1} + total={0} + headName={"위치정보"} + cellClick={() => {}} + onPageChange={() => {}} + /> + <UiTable + tableData={isDetail || []} + headlist={pbnMapHeadList.slice(4)} + pageSize={1} + total={0} + headName={"기본정보"} + cellClick={() => {}} + onPageChange={() => {}} + /> + </div> + ) : layerNm === "잠재수요처" ? ( + <CoDetailModal + formObj={isDetail} + readonly={false} + onFormSubmit={async (value) => { + console.log(value) + }} + isMap={true} + /> + ) : ( + <SttnDetailModal + type={ + layerNm === "빗물이용시설" + ? "RNB" + : layerNm === "중수도시설" + ? "RST" + : "HTB" + } + formObj={isDetail} + /> + )} + </div> + ) : ( + <></> + )} + </> + ); +} diff --git a/apps/wr/app/map/util.ts b/apps/wr/app/(popUp-layout)/util.ts similarity index 100% rename from apps/wr/app/map/util.ts rename to apps/wr/app/(popUp-layout)/util.ts diff --git a/apps/wr/app/(with-layout)/layout.tsx b/apps/wr/app/(with-layout)/layout.tsx new file mode 100644 index 000000000..9c813f8ec --- /dev/null +++ b/apps/wr/app/(with-layout)/layout.tsx @@ -0,0 +1,18 @@ +"use client"; +import Base from "../base"; + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + + + return ( + + <Base> + {children} + </Base> + + ); +} diff --git a/apps/wr/app/(with-layout)/map/mainMap/page.tsx b/apps/wr/app/(with-layout)/map/mainMap/page.tsx new file mode 100644 index 000000000..68eb02d14 --- /dev/null +++ b/apps/wr/app/(with-layout)/map/mainMap/page.tsx @@ -0,0 +1,288 @@ +"use client"; + +import Gis from "@common/business_components/ui/map/Gis"; +import { useEffect, useRef, useState, useCallback } from "react"; +import { mapService } from "../service"; +import GisApp from "@common/business_components/ui/map/lib/gis.core"; +import { SearchBar } from "@common/business_components/ui/map/searchBar/searchBar"; +import { useWrStore } from "@/app/store"; +import { getFeatureCd } from "@/components/lib/func/\mapUtil"; + +const Map = () => { + const { comMap } = useWrStore((state) => state); + const { isFeatured } = comMap; + const [treeData, setTreeData] = useState([]); // 초기값을 객체 또는 null로 설정 + const gisMaps = useRef<any | null>(null); + const [isOpen, setIsOpen] = useState(false); // 팝업 열기/닫기 상태 + const [isSearchModalOpen, setIsSearchModalOpen] = useState(true); // SearchModal 열림 상태 + const [featureData, setFeatureData] = useState<any>(null); // 선택된 피쳐 데이터 + const [checkedLayers, setCheckedLayers] = useState<string[]>([]); + const [checkedLayersData, setCheckedLayersData] = useState<any[]>([]); + const popupRef = useRef<Window | null>(null); + const popupDetailRef = useRef<Window | null>(null); + + const fetchData = async () => { + try { + const response = await mapService.getLyrStngs(); + setTreeData(response); // API 응답 저장 + } catch (error) { + console.error("데이터 불러오기 실패:", error); + } + }; + + // 팝업 닫기 핸들러를 useCallback으로 메모이제이션 + // const handlePopupClose = useCallback(() => { + // setIsOpen(false); + // setFeatureData(null); + // }, []); + + // 컴포넌트 마운트 시 1회 실행 + useEffect(() => { + fetchData(); + }, []); + + useEffect(() => { + const bc = new BroadcastChannel("mapDetailPopUp"); + bc.onmessage = (event) => { + if (event.data.type === "SELECT_FEATURE") { + onFeatureSelect(event.data.feature); + closePopup(); + } + }; + }, []); + + useEffect(() => { + const messageHandler = (event: MessageEvent) => { + if (event.data.type === "POPUP_CLOSED") { + if (event.data.target === "detail") { + console.log("디테일 팝업 닫힘 감지됨"); + setFeatureData(null); + setIsOpen(false); + setIsSearchModalOpen(true); + popupDetailRef.current = null; + } else { + console.log("기타 팝업 닫힘 감지됨"); + popupRef.current = null; + } + } + }; + window.addEventListener("message", messageHandler); + return () => { + window.removeEventListener("message", messageHandler); + }; + }, []); + + useEffect(() => { + console.log("isFeatured ", isFeatured); + + if (isFeatured) { + console.log("isFeatured ", isFeatured); + onFeatureSelect(isFeatured); + } + }, [isFeatured]); + + // 체크된 레이어 정보를 받는 핸들러 + const handleCheckedLayersChange = ( + checkedIds: string[], + checkedNodesData: any[] + ) => { + setCheckedLayers(checkedIds); + setCheckedLayersData(checkedNodesData); + }; + + // SearchModal, GisContent 등에서 호출 + const onFeatureFocus = (data: any) => { + console.log(data); + }; + + const onFeatureSelect = (data: any) => { + const map = gisMaps.current; + if (!map) { + console.warn("지도가 초기화되지 않았습니다."); + return; + } + + // 항상 같은 select 객체 사용 + const select = GisApp.Module.select as any; + if (!select) return; + + // 1. 기존 선택 초기화 + select.resetSelectedFeature(); + + // 2. data가 OpenLayers Feature 인스턴스인지 확인 + let foundFeature: any = null; + let featureId = data.fid; + const layerName = data.layerNm; + console.log("선택된 레이어 이름:", data, layerName); + if (layerName === "하수재이용시설") { + // 재이용시설 + featureId = `${layerName}.${data.searchPk}`; + } else if ( + //빗물이용시설, 중수도시설 + layerName === "빗물이용시설" || + layerName == "중수도시설" + ) { + featureId = `${layerName}.${data.bdstInfoSn}`; + } else if (layerName === "하수처리시설") { + //하수처리시설 + featureId = `${layerName}.${data.bscFctCd}`; + } else if (layerName === "온배수시설") { + //온배수시설 + featureId = `${layerName}.${data.bdstRuseFcltSn}`; + } else if (layerName == "수요처") { + //수요처 + featureId = `${layerName}.${data.dmdInfoId.replaceAll("-", "")}`; + } else { + console.warn("해당 레이어에 대한 featureId 매핑 없음", layerName); + featureId = `${layerName}.${data.fid || data.id || data.ftrIdn}`; + } + map + .getLayers() + .getArray() + .forEach((layer: any) => { + const source = layer.getSource?.(); + if (source?.getFeatureById) { + const feature = source.getFeatureById(String(featureId).trim()); + if (feature) foundFeature = feature; + } + }); + + // 3. 피처 선택 + if (foundFeature) { + const geometry = foundFeature.getGeometry(); + if (geometry) { + const extent = geometry.getExtent(); + + map.getView().fit(extent, { + duration: 300, + padding: [50, 50, 50, 50], + maxZoom: 16, + constrainResolution: true, + nearest: true, + }); + } + select.selectFeature(foundFeature, true); + } 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); + }; + + + const getSelectedLayers = (isFeature: any) => { + const map = gisMaps.current; + if (!map) { + console.warn("지도가 초기화되지 않았습니다."); + return; + } + let result = null; + const layers = map + ? map + .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 && + isFeature && + typeof isFeature.getId === "function" && + source.hasFeature(isFeature) + ) { + featureLayer = layer; // 피처가 속한 레이어 찾기) + } + }); + result = featureLayer.get("lyrNm"); + return result; + }; + + const renderDetailView = useCallback( + (props: any) => { + const isFeature = props.featureData.getProperties(); + if ( + Object.keys(isFeature).length === 0 && + isFeature.constructor === Object + ) { + const url = `/mapDetilePopUp?layerNm=${encodeURIComponent(props.seletectedLayerNm)}`; + const width = window.screen.availWidth; + const height = window.screen.availHeight; + const left = 0; + const top = 0; + const popupOptions = `width=${width},height=${height},left=${left},top=${top},resizable=yes,scrollbars=yes`; + popupRef.current = window.open(url, "지도 레이어 팝업", popupOptions); + } else { + const layerNm = getSelectedLayers(featureData); + const safeProps = featureData.getProperties(); + const featureCd = getFeatureCd(safeProps, String(layerNm)); + const url = `/mapFeaturePopup?layerNm=${encodeURIComponent(String(layerNm))}&featureCd=${encodeURIComponent(featureCd)} `; + const width = Math.floor(window.innerWidth * 0.4); // 화면 폭의 40% + const height = Math.floor(window.innerHeight - 450); // 150px 여백 확보; // calc(100% - 70px) + const left = (window.screen.availWidth - width) / 2; + const top = (window.screen.availHeight - height) / 2; + const popupOptions = `width=${width},height=${height},left=${left},top=${top},resizable=yes,scrollbars=yes`; + popupDetailRef.current = window.open( + url, + "지도 시설 상세정보 팝업", + popupOptions + ); + } + }, + [checkedLayers, checkedLayersData, featureData] // 필요 시 의존성 넣기 + ); + const closePopup = () => { + if (popupRef.current && !popupRef.current.closed) { + popupRef.current.close(); + popupRef.current = null; + setIsOpen(false); + setFeatureData(null); + } + }; + + return ( + <div className="bg-container"> + <Gis + treeData={treeData} + isSearch={false} + gisMaps={gisMaps} + isMiniMap={false} + searchView={undefined} + detailView={renderDetailView} + onFeatureSelect={onFeatureFocus} + setIsOpen={setIsOpen} + setFeatureData={setFeatureData} + featureData={featureData} + setIsSearchModalOpen={setIsSearchModalOpen} + isOpen={isOpen} + isSearchModalOpen={isSearchModalOpen} + onCheckedLayersChange={handleCheckedLayersChange} + imgVisble={true} + searchBar={ + <SearchBar + gisMaps={gisMaps} + facilityTypes={[ + { text: "하수처리시설", val: "tb_il_swage_prcs_fclt" }, + { text: "하수재이용시설", val: "tb_wr_bdst_ruse_fclt" }, + { text: "빗물이용시설", val: "tb_wr_rnwtr_utztn_fclt" }, + { text: "중수도시설", val: "tb_wr_rswtr_fclt" }, + { text: "온배수시설", val: "tb_wr_htdfl_fclt" }, + { text: "수요처", val: "tb_wr_ruse_fclt_dmd_co" }, + { text: "잠재수요처", val: "tb_wr_tmpr_dmd_co" }, + ]} + onFeatureFocus={onFeatureFocus} + /> + } + waterDepth={undefined} + /> + </div> + ); +}; + +export default Map; diff --git a/apps/wr/app/(with-layout)/map/popup/page.tsx b/apps/wr/app/(with-layout)/map/popup/page.tsx new file mode 100644 index 000000000..ca7a07b43 --- /dev/null +++ b/apps/wr/app/(with-layout)/map/popup/page.tsx @@ -0,0 +1,153 @@ +// "use client"; + +// import Gis from "@common/business_components/ui/map/Gis"; +// import { useEffect, useRef, useState, useCallback } from "react"; +// import { mapService } from "../service"; +// import NewWindowPopup from "../components/newWindowPopup"; +// import GisApp from "@common/business_components/ui/map/lib/gis.core"; + +// const Map = () => { +// const [treeData, setTreeData] = useState([]); // 초기값을 객체 또는 null로 설정 +// const gisMaps = useRef<any | null>(null); +// const [isOpen, setIsOpen] = useState(false); // 팝업 열기/닫기 상태 +// const [isSearchModalOpen, setIsSearchModalOpen] = useState(true); // SearchModal 열림 상태 +// const [featureData, setFeatureData] = useState(null); // 선택된 피쳐 데이터 +// const [checkedLayers, setCheckedLayers] = useState<string[]>([]); +// const [checkedLayersData, setCheckedLayersData] = useState<any[]>([]); + +// const fetchData = async () => { +// try { +// const response = await mapService.getLyrStngs(); +// setTreeData(response); // API 응답 저장 +// } catch (error) { +// console.error("데이터 불러오기 실패:", error); +// } +// }; + +// // 팝업 닫기 핸들러를 useCallback으로 메모이제이션 +// const handlePopupClose = useCallback(() => { +// setIsOpen(false); +// setFeatureData(null); +// }, []); + +// // 컴포넌트 마운트 시 1회 실행 +// useEffect(() => { +// fetchData(); +// }, []); + +// // 체크된 레이어 정보를 받는 핸들러 +// const handleCheckedLayersChange = (checkedIds: string[], checkedNodesData: any[]) => { +// setCheckedLayers(checkedIds); +// setCheckedLayersData(checkedNodesData); +// }; + +// // SearchModal, GisContent 등에서 호출 +// const onFeatureSelect = (data: any) => { +// const map = gisMaps.current; +// if (!map) { +// console.warn("지도가 초기화되지 않았습니다."); +// return; +// } + +// // 항상 같은 select 객체 사용 +// const select = GisApp.Module.select as any; +// if (!select) return; + +// // 1. 기존 선택 초기화 +// select.resetSelectedFeature(); + +// // 2. data가 OpenLayers Feature 인스턴스인지 확인 +// let foundFeature: any = null; +// if ( +// data && +// typeof data.getGeometry === "function" && +// typeof data.setStyle === "function" +// ) { +// // 이미 Feature 인스턴스라면 그대로 사용 +// foundFeature = data; +// } else { +// // 아니면 featureId로 맵에서 Feature 인스턴스를 찾아서 사용 +// const featureId = data.fid || data.ftrIdn || data.id; +// map +// .getLayers() +// .getArray() +// .forEach((layer: any) => { +// const source = layer.getSource?.(); +// if (source?.getFeatureById) { +// const feature = source.getFeatureById(String(featureId).trim()); +// if (feature) foundFeature = feature; +// } +// }); +// } + +// // 3. 피처 선택 +// if (foundFeature) { +// const geometry = foundFeature.getGeometry(); +// if (geometry) { +// const extent = geometry.getExtent(); + +// map.getView().fit(extent, { +// duration: 300, +// padding: [50, 50, 50, 50], +// maxZoom: 16, +// constrainResolution: true, +// nearest: true, +// }); +// } +// select.selectFeature(foundFeature, true); +// } else { +// console.warn("OpenLayers Feature를 찾을 수 없습니다:", data); +// } + +// setFeatureData(data); +// setIsOpen(true); +// setIsSearchModalOpen(false); +// }; + +// // MapSearchBar 래퍼 컴포넌트 +// // const CustomMapSearchBar = (props: any) => { +// // return ( +// // <MapSearchBar +// // {...props} +// // searchType="공급처 후보지 조회" +// // showApplyButton={false} +// // /> +// // ); +// // }; + +// return ( +// <div className="bg-container"> +// <Gis +// treeData={treeData} +// isSearch={false} +// gisMaps={gisMaps} +// isMiniMap={false} +// searchView={undefined} +// detailView={(props: any) => +// // <NewWindowPopup +// // onClose={handlePopupClose} // 메모이제이션된 함수 사용 +// // checkedLayers={checkedLayers} +// // checkedLayersData={checkedLayersData} +// // {...props} +// // /> +// { + +// console.log('p' ,props) +// } +// } +// onFeatureSelect={onFeatureSelect} +// setIsOpen={setIsOpen} +// setFeatureData={setFeatureData} +// featureData={featureData} +// setIsSearchModalOpen={setIsSearchModalOpen} +// isOpen={isOpen} +// isSearchModalOpen={isSearchModalOpen} +// onCheckedLayersChange={handleCheckedLayersChange} +// imgVisble={true} +// searchBar={undefined} +// /> +// </div> +// ); +// }; + +// export default Map; diff --git a/apps/wr/app/map/service/index.ts b/apps/wr/app/(with-layout)/map/service/index.ts similarity index 100% rename from apps/wr/app/map/service/index.ts rename to apps/wr/app/(with-layout)/map/service/index.ts diff --git a/apps/wr/app/map/service/map.ts b/apps/wr/app/(with-layout)/map/service/map.ts similarity index 100% rename from apps/wr/app/map/service/map.ts rename to apps/wr/app/(with-layout)/map/service/map.ts diff --git a/apps/wr/app/matMng/coMat/coMatRvw/page.tsx b/apps/wr/app/(with-layout)/matMng/coMat/coMatRvw/page.tsx similarity index 100% rename from apps/wr/app/matMng/coMat/coMatRvw/page.tsx rename to apps/wr/app/(with-layout)/matMng/coMat/coMatRvw/page.tsx diff --git a/apps/wr/app/matMng/coMat/ruseCaseMng/page.tsx b/apps/wr/app/(with-layout)/matMng/coMat/ruseCaseMng/page.tsx similarity index 100% rename from apps/wr/app/matMng/coMat/ruseCaseMng/page.tsx rename to apps/wr/app/(with-layout)/matMng/coMat/ruseCaseMng/page.tsx diff --git a/apps/wr/app/matMng/coMat/ruseRvwHstry/page.tsx b/apps/wr/app/(with-layout)/matMng/coMat/ruseRvwHstry/page.tsx similarity index 100% rename from apps/wr/app/matMng/coMat/ruseRvwHstry/page.tsx rename to apps/wr/app/(with-layout)/matMng/coMat/ruseRvwHstry/page.tsx diff --git a/apps/wr/app/matMng/coMng/coSttn/page.tsx b/apps/wr/app/(with-layout)/matMng/coMng/coSttn/page.tsx similarity index 100% rename from apps/wr/app/matMng/coMng/coSttn/page.tsx rename to apps/wr/app/(with-layout)/matMng/coMng/coSttn/page.tsx diff --git a/apps/wr/app/matMng/coMng/drgtSttn/page.tsx b/apps/wr/app/(with-layout)/matMng/coMng/drgtSttn/page.tsx similarity index 100% rename from apps/wr/app/matMng/coMng/drgtSttn/page.tsx rename to apps/wr/app/(with-layout)/matMng/coMng/drgtSttn/page.tsx diff --git a/apps/wr/app/matMng/coMng/ldrmntCoSttn/page.tsx b/apps/wr/app/(with-layout)/matMng/coMng/ldrmntCoSttn/page.tsx similarity index 100% rename from apps/wr/app/matMng/coMng/ldrmntCoSttn/page.tsx rename to apps/wr/app/(with-layout)/matMng/coMng/ldrmntCoSttn/page.tsx diff --git a/apps/wr/app/ruseFcltMng/fcltReg/htdflReg/page.tsx b/apps/wr/app/(with-layout)/ruseFcltMng/fcltReg/htdflReg/page.tsx similarity index 100% rename from apps/wr/app/ruseFcltMng/fcltReg/htdflReg/page.tsx rename to apps/wr/app/(with-layout)/ruseFcltMng/fcltReg/htdflReg/page.tsx diff --git a/apps/wr/app/ruseFcltMng/fcltReg/pbnstReg/page.tsx b/apps/wr/app/(with-layout)/ruseFcltMng/fcltReg/pbnstReg/page.tsx similarity index 99% rename from apps/wr/app/ruseFcltMng/fcltReg/pbnstReg/page.tsx rename to apps/wr/app/(with-layout)/ruseFcltMng/fcltReg/pbnstReg/page.tsx index d08d6f06e..688fb836c 100644 --- a/apps/wr/app/ruseFcltMng/fcltReg/pbnstReg/page.tsx +++ b/apps/wr/app/(with-layout)/ruseFcltMng/fcltReg/pbnstReg/page.tsx @@ -10,7 +10,7 @@ import { UiTable, } from "@common/business_components"; import { Button } from "@common/components"; -import { useWrStore } from "../../../store"; +import { useWrStore } from "../../../../store"; import { Tabs, TabsList, TabsTrigger, TabsContent } from "@common/components"; import { useCallback, useEffect, useState } from "react"; import ListDiv from "@common/business_components/ui/list_div"; diff --git a/apps/wr/app/ruseFcltMng/fcltReg/pbnstReg/utilsZod.tsx b/apps/wr/app/(with-layout)/ruseFcltMng/fcltReg/pbnstReg/utilsZod.tsx similarity index 100% rename from apps/wr/app/ruseFcltMng/fcltReg/pbnstReg/utilsZod.tsx rename to apps/wr/app/(with-layout)/ruseFcltMng/fcltReg/pbnstReg/utilsZod.tsx diff --git a/apps/wr/app/ruseFcltMng/fcltReg/rainReg/TableHead.tsx b/apps/wr/app/(with-layout)/ruseFcltMng/fcltReg/rainReg/TableHead.tsx similarity index 100% rename from apps/wr/app/ruseFcltMng/fcltReg/rainReg/TableHead.tsx rename to apps/wr/app/(with-layout)/ruseFcltMng/fcltReg/rainReg/TableHead.tsx diff --git a/apps/wr/app/ruseFcltMng/fcltReg/rainReg/page.tsx b/apps/wr/app/(with-layout)/ruseFcltMng/fcltReg/rainReg/page.tsx similarity index 100% rename from apps/wr/app/ruseFcltMng/fcltReg/rainReg/page.tsx rename to apps/wr/app/(with-layout)/ruseFcltMng/fcltReg/rainReg/page.tsx diff --git a/apps/wr/app/ruseFcltMng/fcltReg/rswtReg/page.tsx b/apps/wr/app/(with-layout)/ruseFcltMng/fcltReg/rswtReg/page.tsx similarity index 100% rename from apps/wr/app/ruseFcltMng/fcltReg/rswtReg/page.tsx rename to apps/wr/app/(with-layout)/ruseFcltMng/fcltReg/rswtReg/page.tsx diff --git a/apps/wr/app/ruseFcltMng/fcltSttn/htdflSttn/page.tsx b/apps/wr/app/(with-layout)/ruseFcltMng/fcltSttn/htdflSttn/page.tsx similarity index 100% rename from apps/wr/app/ruseFcltMng/fcltSttn/htdflSttn/page.tsx rename to apps/wr/app/(with-layout)/ruseFcltMng/fcltSttn/htdflSttn/page.tsx diff --git a/apps/wr/app/ruseFcltMng/fcltSttn/pbnstSttn/page.tsx b/apps/wr/app/(with-layout)/ruseFcltMng/fcltSttn/pbnstSttn/page.tsx similarity index 100% rename from apps/wr/app/ruseFcltMng/fcltSttn/pbnstSttn/page.tsx rename to apps/wr/app/(with-layout)/ruseFcltMng/fcltSttn/pbnstSttn/page.tsx diff --git a/apps/wr/app/ruseFcltMng/fcltSttn/rainSttn/page.tsx b/apps/wr/app/(with-layout)/ruseFcltMng/fcltSttn/rainSttn/page.tsx similarity index 100% rename from apps/wr/app/ruseFcltMng/fcltSttn/rainSttn/page.tsx rename to apps/wr/app/(with-layout)/ruseFcltMng/fcltSttn/rainSttn/page.tsx diff --git a/apps/wr/app/ruseFcltMng/fcltSttn/rswtSttn/page.tsx b/apps/wr/app/(with-layout)/ruseFcltMng/fcltSttn/rswtSttn/page.tsx similarity index 100% rename from apps/wr/app/ruseFcltMng/fcltSttn/rswtSttn/page.tsx rename to apps/wr/app/(with-layout)/ruseFcltMng/fcltSttn/rswtSttn/page.tsx diff --git a/apps/wr/app/ruseFcltMng/prfmncReg/chkgdnReg/TableHead.tsx b/apps/wr/app/(with-layout)/ruseFcltMng/prfmncReg/chkgdnReg/TableHead.tsx similarity index 100% rename from apps/wr/app/ruseFcltMng/prfmncReg/chkgdnReg/TableHead.tsx rename to apps/wr/app/(with-layout)/ruseFcltMng/prfmncReg/chkgdnReg/TableHead.tsx diff --git a/apps/wr/app/ruseFcltMng/prfmncReg/chkgdnReg/page.tsx b/apps/wr/app/(with-layout)/ruseFcltMng/prfmncReg/chkgdnReg/page.tsx similarity index 96% rename from apps/wr/app/ruseFcltMng/prfmncReg/chkgdnReg/page.tsx rename to apps/wr/app/(with-layout)/ruseFcltMng/prfmncReg/chkgdnReg/page.tsx index bf18d4d98..97d80a7a8 100644 --- a/apps/wr/app/ruseFcltMng/prfmncReg/chkgdnReg/page.tsx +++ b/apps/wr/app/(with-layout)/ruseFcltMng/prfmncReg/chkgdnReg/page.tsx @@ -118,13 +118,33 @@ export default function Page() { const rainPageChange = (pageNo: number) => { if (pageNo > 0) { setRainPageNation(pageNo); - rainChkInit(pageNo, rainPageSize, searchYear, commonCd); + if (fcltNm) { + rainChkNmSearch( + pageNo, + rainPageSize, + searchYear, + commonCd, + fcltNm + ); + } else { + rainChkInit(pageNo, rainPageSize, searchYear, commonCd); + } } }; const rswtPageChange = (pageNo: number) => { if (pageNo > 0) { setRswtPageNation(pageNo); - rswtChkgdnInit(pageNo, rswtPageSize, searchYear, commonCd); + if (fcltNm) { + rswtChkNmSearch( + pageNo, + rainPageSize, + searchYear, + commonCd, + fcltNm + ); + } else { + rswtChkgdnInit(pageNo, rswtPageSize, searchYear, commonCd); + } } }; diff --git a/apps/wr/app/ruseFcltMng/prfmncReg/pbnstPrfmncReg/page.tsx b/apps/wr/app/(with-layout)/ruseFcltMng/prfmncReg/pbnstPrfmncReg/page.tsx similarity index 100% rename from apps/wr/app/ruseFcltMng/prfmncReg/pbnstPrfmncReg/page.tsx rename to apps/wr/app/(with-layout)/ruseFcltMng/prfmncReg/pbnstPrfmncReg/page.tsx diff --git a/apps/wr/app/ruseFcltMng/prfmncSttn/chkgdnSttn/page.tsx b/apps/wr/app/(with-layout)/ruseFcltMng/prfmncSttn/chkgdnSttn/page.tsx similarity index 94% rename from apps/wr/app/ruseFcltMng/prfmncSttn/chkgdnSttn/page.tsx rename to apps/wr/app/(with-layout)/ruseFcltMng/prfmncSttn/chkgdnSttn/page.tsx index ba1821f20..6b4eead42 100644 --- a/apps/wr/app/ruseFcltMng/prfmncSttn/chkgdnSttn/page.tsx +++ b/apps/wr/app/(with-layout)/ruseFcltMng/prfmncSttn/chkgdnSttn/page.tsx @@ -83,14 +83,34 @@ export default function Page() { const rainPageChange = (pageNo: number) => { if (pageNo > 0) { setRainPageNation(pageNo); - rainChkSttnInit(pageNo, rainPageSize, searchYear, commonCd); + if (fcltNm) { + rainChkSttnNmSearch( + pageNo, + rainPageSize, + searchYear, + commonCd, + fcltNm + ); + } else { + rainChkSttnInit(pageNo, rainPageSize, searchYear, commonCd); + } } }; const rswtPageChange = (pageNo: number) => { if (pageNo > 0) { setRswtPageNation(pageNo); - rswtChkSttnInit(pageNo, rswtPageSize, searchYear, commonCd); + if (fcltNm) { + rswtChkNmSttnSearch( + pageNo, + rainPageSize, + searchYear, + commonCd, + fcltNm + ); + } else { + rswtChkSttnInit(pageNo, rswtPageSize, searchYear, commonCd); + } } }; diff --git a/apps/wr/app/ruseFcltMng/prfmncSttn/pbnstPrfmncSttn/page.tsx b/apps/wr/app/(with-layout)/ruseFcltMng/prfmncSttn/pbnstPrfmncSttn/page.tsx similarity index 100% rename from apps/wr/app/ruseFcltMng/prfmncSttn/pbnstPrfmncSttn/page.tsx rename to apps/wr/app/(with-layout)/ruseFcltMng/prfmncSttn/pbnstPrfmncSttn/page.tsx diff --git a/apps/wr/app/stats/rainStat/page.tsx b/apps/wr/app/(with-layout)/stats/rainStat/page.tsx similarity index 100% rename from apps/wr/app/stats/rainStat/page.tsx rename to apps/wr/app/(with-layout)/stats/rainStat/page.tsx diff --git a/apps/wr/app/stats/rswtStat/page.tsx b/apps/wr/app/(with-layout)/stats/rswtStat/page.tsx similarity index 100% rename from apps/wr/app/stats/rswtStat/page.tsx rename to apps/wr/app/(with-layout)/stats/rswtStat/page.tsx diff --git a/apps/wr/app/layout.tsx b/apps/wr/app/layout.tsx index add988817..fe4c3aff0 100644 --- a/apps/wr/app/layout.tsx +++ b/apps/wr/app/layout.tsx @@ -1,10 +1,9 @@ "use client"; import "./globals.css"; -import { StoreProvider} from "./store"; -import koBase from "../public/locales/ko/message.json"; import i18next from "i18next"; import { I18nextProvider } from "react-i18next"; -import Base from "./base"; +import koBase from "../public/locales/ko/message.json"; +import { StoreProvider } from "./store"; export default function RootLayout({ children, @@ -25,11 +24,9 @@ export default function RootLayout({ <html lang="en"> <body> <I18nextProvider i18n={i18next}> - <StoreProvider> - <Base> + <StoreProvider> {children} - </Base> - </StoreProvider> + </StoreProvider> </I18nextProvider> </body> </html> diff --git a/apps/wr/app/map/components/layerPopup/hasuFacility.tsx b/apps/wr/app/map/components/layerPopup/hasuFacility.tsx deleted file mode 100644 index 5a10ce3ed..000000000 --- a/apps/wr/app/map/components/layerPopup/hasuFacility.tsx +++ /dev/null @@ -1,89 +0,0 @@ -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 !== '하수처리시설' || viewMode != 'list') return; - if (!pbnst.isInit) { - pubHasuInit("1", "0"); - } - }, [pbnst.isInit, pubHasuInit]); - useEffect(() => { - if (seletectedLayerNm !== '하수처리시설' || viewMode != 'list') 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/layerPopup/htdflFacility.tsx b/apps/wr/app/map/components/layerPopup/htdflFacility.tsx deleted file mode 100644 index fe4bef25a..000000000 --- a/apps/wr/app/map/components/layerPopup/htdflFacility.tsx +++ /dev/null @@ -1,83 +0,0 @@ -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 deleted file mode 100644 index 54b51e8a6..000000000 --- a/apps/wr/app/map/components/layerPopup/rainFacility.tsx +++ /dev/null @@ -1,105 +0,0 @@ -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/layerPopup/reuseFacility.tsx b/apps/wr/app/map/components/layerPopup/reuseFacility.tsx deleted file mode 100644 index 152d37999..000000000 --- a/apps/wr/app/map/components/layerPopup/reuseFacility.tsx +++ /dev/null @@ -1,146 +0,0 @@ -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(() => { - if (seletectedLayerNm !== '하수재이용시설' || viewMode !='list') return; - if (!pbnstSttn.isInit) { - getFlcts() - } - }, [seletectedLayerNm, pbnstSttn.isInit]); - useEffect(() => { - if (seletectedLayerNm !== '하수재이용시설' || viewMode !='list') 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 !== '하수재이용시설' || viewMode !='list') 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/components/layerPopup/rswtFacility.tsx b/apps/wr/app/map/components/layerPopup/rswtFacility.tsx deleted file mode 100644 index dc2537c95..000000000 --- a/apps/wr/app/map/components/layerPopup/rswtFacility.tsx +++ /dev/null @@ -1,107 +0,0 @@ -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 deleted file mode 100644 index 21d31b984..000000000 --- a/apps/wr/app/map/components/newWindowPopup.tsx +++ /dev/null @@ -1,183 +0,0 @@ -"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 - 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; - 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); - }); - } - setFcltPk(featureCd); - setActiveTab(featureCd); - setViewMode("detail"); - }, [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="시설 상세 정보" - scrollbars='yes' - viewMode={viewMode} - resizable='no'> - {/* 체크된 레이어에 따른 조건부 렌더링 */} - {renderConditionalContent()} - </NewWindowPortal> - ); -}; - -export default NewWindowPopup; \ No newline at end of file diff --git a/apps/wr/app/map/components/newWindowPortal.tsx b/apps/wr/app/map/components/newWindowPortal.tsx deleted file mode 100644 index 119e3a52b..000000000 --- a/apps/wr/app/map/components/newWindowPortal.tsx +++ /dev/null @@ -1,112 +0,0 @@ -import React, { useState, useEffect, useRef, useCallback } from 'react'; -import { createPortal } from 'react-dom'; - -// 현재 문서의 스타일(CSS)을 새 창으로 복사하는 함수 -const copyStyles = (sourceDoc: Document, targetDoc: Document) => { - Array.from(sourceDoc.styleSheets).forEach(styleSheet => { - try { - if (styleSheet.cssRules) { // <style> 태그로 주입된 CSS - const newStyleEl = sourceDoc.createElement('style'); - Array.from(styleSheet.cssRules).forEach(cssRule => { - newStyleEl.appendChild(sourceDoc.createTextNode(cssRule.cssText)); - }); - targetDoc.head.appendChild(newStyleEl); - } else if (styleSheet.href) { // <link> 태그로 연결된 CSS 파일 - const newLinkEl = sourceDoc.createElement('link'); - newLinkEl.rel = 'stylesheet'; - newLinkEl.href = styleSheet.href; - targetDoc.head.appendChild(newLinkEl); - } - } catch (e) { - console.warn('스타일시트를 복사할 수 없습니다:', e); - } - }); -}; - -interface NewWindowPortalProps { - children: React.ReactNode; - onClose: () => void; - title?: string; - width?: number; - height?: number; - resizable?: string; - scrollbars?: string; - viewMode?: 'list' | 'detail' | 'nothing'; -} - -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); - - // onClose 함수를 ref로 저장하여 의존성 문제 해결 - useEffect(() => { - onCloseRef.current = onClose; - }, [onClose]); - - 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; - // 브라우저 화면 정보 - const screenLeft = window.screenLeft ?? window.screenX; - const screenTop = window.screenTop ?? window.screenY; - const screenWidth = window.innerWidth ?? document.documentElement.clientWidth; - const screenHeight = window.innerHeight ?? document.documentElement.clientHeight; - - // 중앙 위치 계산 - const left = screenLeft + (screenWidth - window.screen.width) / 2; - const top = screenTop + (screenHeight - window.screen.height) / 2; - - // 1. 새 창을 열고 참조를 저장합니다. - 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) { - newWindowRef.current.document.title = title; - - // 2. React 컴포넌트를 마운트할 컨테이너 div를 생성하여 새 창의 body에 추가합니다. - const div = newWindowRef.current.document.createElement('div'); - newWindowRef.current.document.body.appendChild(div); - setContainer(div); - - // 3. 메인 창의 스타일을 새 창으로 복사합니다. (매우 중요) - copyStyles(document, newWindowRef.current.document); - // 4. 사용자가 창을 닫을 때 부모에게 알리기 위한 이벤트 리스너를 설정합니다. - // const handleUnload = () => { - // onClose(); - // }; - newWindowRef.current.addEventListener('unload', handleUnload); - - // 5. 컴포넌트가 사라질 때 창을 닫는 클린업 함수를 반환합니다. - return () => { - if (newWindowRef.current) { - newWindowRef.current.removeEventListener('unload', handleUnload); - if (!newWindowRef.current.closed) { - newWindowRef.current.close(); - } - } - }; - } else { - alert('팝업이 차단되었습니다. 팝업 차단을 해제하고 다시 시도해주세요.'); - onCloseRef.current(); - // onClose(); - } - }, [handleUnload, onClose, title]); - - // `createPortal`을 사용해 children을 새 창의 container div에 렌더링합니다. - return container ? createPortal(children, container) : null; -}; - -export default NewWindowPortal; \ No newline at end of file diff --git a/apps/wr/app/map/mainMap/page.tsx b/apps/wr/app/map/mainMap/page.tsx deleted file mode 100644 index d7028130b..000000000 --- a/apps/wr/app/map/mainMap/page.tsx +++ /dev/null @@ -1,157 +0,0 @@ -"use client"; - -import Gis from "@common/business_components/ui/map/Gis"; -import { useEffect, useRef, useState, useCallback } from "react"; -import { mapService } from "../service"; -import NewWindowPopup from "../components/newWindowPopup"; -import GisApp from "@common/business_components/ui/map/lib/gis.core"; -import { SearchBar } from "../searchBar/searchBar"; -import { UndoIcon } from "lucide-react"; - -const Map = () => { - const [treeData, setTreeData] = useState([]); // 초기값을 객체 또는 null로 설정 - const gisMaps = useRef<any | null>(null); - const [isOpen, setIsOpen] = useState(false); // 팝업 열기/닫기 상태 - const [isSearchModalOpen, setIsSearchModalOpen] = useState(true); // SearchModal 열림 상태 - const [featureData, setFeatureData] = useState<any>(null); // 선택된 피쳐 데이터 - const [checkedLayers, setCheckedLayers] = useState<string[]>([]); - const [checkedLayersData, setCheckedLayersData] = useState<any[]>([]); - - const fetchData = async () => { - try { - const response = await mapService.getLyrStngs(); - setTreeData(response); // API 응답 저장 - } catch (error) { - console.error("데이터 불러오기 실패:", error); - } - }; - - // 팝업 닫기 핸들러를 useCallback으로 메모이제이션 - const handlePopupClose = useCallback(() => { - setIsOpen(false); - setFeatureData(null); - }, []); - - // 컴포넌트 마운트 시 1회 실행 - useEffect(() => { - fetchData(); - }, []); - - // 체크된 레이어 정보를 받는 핸들러 - const handleCheckedLayersChange = ( - checkedIds: string[], - checkedNodesData: any[] - ) => { - setCheckedLayers(checkedIds); - setCheckedLayersData(checkedNodesData); - }; - - // SearchModal, GisContent 등에서 호출 - const onFeatureFocus = (data: any) => { - console.log("page:onFeatureFocus"); - const map = gisMaps.current; - if (!map) { - console.warn("지도가 초기화되지 않았습니다."); - return; - } - - // 항상 같은 select 객체 사용 - const select = GisApp.Module.select as any; - if (!select) return; - - // 1. 기존 선택 초기화 - select.resetSelectedFeature(); - - // 2. data가 OpenLayers Feature 인스턴스인지 확인 - let foundFeature: any = null; - let featureId = data.fid; - const layerName = data.layerNm; - map - .getLayers() - .getArray() - .forEach((layer: any) => { - const source = layer.getSource?.(); - if (source?.getFeatureById) { - const feature = source.getFeatureById(String(featureId).trim()); - if (feature) foundFeature = feature; - } - }); - - // 3. 피처 선택 - if (foundFeature) { - const geometry = foundFeature.getGeometry(); - if (geometry) { - const extent = geometry.getExtent(); - - map.getView().fit(extent, { - duration: 300, - padding: [50, 50, 50, 50], - maxZoom: 16, - constrainResolution: true, - nearest: true, - }); - } - // select.selectFeature(foundFeature, true); - } 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; // 이전과 같으면 무시 - }; - - const renderDetailView = useCallback( - (props: any) => ( - <NewWindowPopup - key={featureData?.fid || featureData?.id || featureData?.ftrIdn} - onClose={handlePopupClose} - checkedLayers={checkedLayers} - checkedLayersData={checkedLayersData} - {...props} - /> - ), - [checkedLayers, checkedLayersData] // 필요 시 의존성 넣기 - ); - return ( - <div className="bg-container"> - <Gis - treeData={treeData} - isSearch={false} - gisMaps={gisMaps} - isMiniMap={false} - searchView={undefined} - detailView={renderDetailView} - onFeatureSelect={onFeatureFocus} - setIsOpen={setIsOpen} - setFeatureData={setFeatureData} - featureData={featureData} - setIsSearchModalOpen={setIsSearchModalOpen} - isOpen={isOpen} - isSearchModalOpen={isSearchModalOpen} - onCheckedLayersChange={handleCheckedLayersChange} - imgVisble={true} - searchBar={() => { - const facilityTypes = [ - { text: "하수처리시설", val: "tb_il_swage_prcs_fclt" }, - { text: "하수재이용시설", val: "tb_wr_bdst_ruse_fclt" }, - { text: "빗물이용시설", val: "tb_wr_rnwtr_utztn_fclt" }, - { text: "중수도시설", val: "tb_wr_rswtr_fclt" }, - { text: "온배수시설", val: "tb_wr_htdfl_fclt" }, - { text: "수요처", val: "tb_wr_ruse_fclt_dmd_co" }, - { text: "잠재수요처", val: "tb_wr_tmpr_dmd_co" }, - ]; - return ( - <SearchBar - gisMaps={gisMaps} - facilityTypes={facilityTypes} - onFeatureFocus={onFeatureFocus} - /> - ); - }} - waterDepth={undefined} - /> - </div> - ); -}; - -export default Map; diff --git a/apps/wr/app/map/popup/page.tsx b/apps/wr/app/map/popup/page.tsx deleted file mode 100644 index c7e091bb4..000000000 --- a/apps/wr/app/map/popup/page.tsx +++ /dev/null @@ -1,150 +0,0 @@ -"use client"; - -import Gis from "@common/business_components/ui/map/Gis"; -import { useEffect, useRef, useState, useCallback } from "react"; -import { mapService } from "../service"; -import NewWindowPopup from "../components/newWindowPopup"; -import GisApp from "@common/business_components/ui/map/lib/gis.core"; - -const Map = () => { - const [treeData, setTreeData] = useState([]); // 초기값을 객체 또는 null로 설정 - const gisMaps = useRef<any | null>(null); - const [isOpen, setIsOpen] = useState(false); // 팝업 열기/닫기 상태 - const [isSearchModalOpen, setIsSearchModalOpen] = useState(true); // SearchModal 열림 상태 - const [featureData, setFeatureData] = useState(null); // 선택된 피쳐 데이터 - const [checkedLayers, setCheckedLayers] = useState<string[]>([]); - const [checkedLayersData, setCheckedLayersData] = useState<any[]>([]); - - const fetchData = async () => { - try { - const response = await mapService.getLyrStngs(); - setTreeData(response); // API 응답 저장 - } catch (error) { - console.error("데이터 불러오기 실패:", error); - } - }; - - // 팝업 닫기 핸들러를 useCallback으로 메모이제이션 - const handlePopupClose = useCallback(() => { - setIsOpen(false); - setFeatureData(null); - }, []); - - // 컴포넌트 마운트 시 1회 실행 - useEffect(() => { - fetchData(); - }, []); - - // 체크된 레이어 정보를 받는 핸들러 - const handleCheckedLayersChange = (checkedIds: string[], checkedNodesData: any[]) => { - setCheckedLayers(checkedIds); - setCheckedLayersData(checkedNodesData); - }; - - // SearchModal, GisContent 등에서 호출 - const onFeatureSelect = (data: any) => { - const map = gisMaps.current; - if (!map) { - console.warn("지도가 초기화되지 않았습니다."); - return; - } - - // 항상 같은 select 객체 사용 - const select = GisApp.Module.select as any; - if (!select) return; - - // 1. 기존 선택 초기화 - select.resetSelectedFeature(); - - // 2. data가 OpenLayers Feature 인스턴스인지 확인 - let foundFeature: any = null; - if ( - data && - typeof data.getGeometry === "function" && - typeof data.setStyle === "function" - ) { - // 이미 Feature 인스턴스라면 그대로 사용 - foundFeature = data; - } else { - // 아니면 featureId로 맵에서 Feature 인스턴스를 찾아서 사용 - const featureId = data.fid || data.ftrIdn || data.id; - map - .getLayers() - .getArray() - .forEach((layer: any) => { - const source = layer.getSource?.(); - if (source?.getFeatureById) { - const feature = source.getFeatureById(String(featureId).trim()); - if (feature) foundFeature = feature; - } - }); - } - - // 3. 피처 선택 - if (foundFeature) { - const geometry = foundFeature.getGeometry(); - if (geometry) { - const extent = geometry.getExtent(); - - map.getView().fit(extent, { - duration: 300, - padding: [50, 50, 50, 50], - maxZoom: 16, - constrainResolution: true, - nearest: true, - }); - } - select.selectFeature(foundFeature, true); - } else { - console.warn("OpenLayers Feature를 찾을 수 없습니다:", data); - } - - setFeatureData(data); - setIsOpen(true); - setIsSearchModalOpen(false); - }; - - // MapSearchBar 래퍼 컴포넌트 - // const CustomMapSearchBar = (props: any) => { - // return ( - // <MapSearchBar - // {...props} - // searchType="공급처 후보지 조회" - // showApplyButton={false} - // /> - // ); - // }; - - return ( - <div className="bg-container"> - <Gis - treeData={treeData} - isSearch={false} - gisMaps={gisMaps} - isMiniMap={false} - searchView={undefined} - detailView={(props: any) =>( - <NewWindowPopup - onClose={handlePopupClose} // 메모이제이션된 함수 사용 - checkedLayers={checkedLayers} - checkedLayersData={checkedLayersData} - {...props} - /> - )} - onFeatureSelect={onFeatureSelect} - setIsOpen={setIsOpen} - setFeatureData={setFeatureData} - featureData={featureData} - setIsSearchModalOpen={setIsSearchModalOpen} - isOpen={isOpen} - isSearchModalOpen={isSearchModalOpen} - onCheckedLayersChange={handleCheckedLayersChange} - imgVisble={true} - searchBar={undefined} - waterDepth={undefined} - /> - </div> - ); -}; - -export default Map; diff --git a/apps/wr/app/map/searchBar/searchBar.tsx b/apps/wr/app/map/searchBar/searchBar.tsx index 17f164646..ab3dacb0e 100644 --- a/apps/wr/app/map/searchBar/searchBar.tsx +++ b/apps/wr/app/map/searchBar/searchBar.tsx @@ -32,6 +32,9 @@ export function SearchBar({ facilityTypes, onFeatureFocus, }: SearchBarProps) { + console.log('gisMaps ', gisMaps) + console.log('onFeatureFocus ', onFeatureFocus) + const [sidoValue, setSidoValue] = useState("0"); const [sigunValue, setSigunValue] = useState("0"); const [facilityType, setFacilityType] = useState( @@ -223,6 +226,7 @@ export function SearchBar({ }; const moveFocus = (feature: any) => { + console.log('feature ' ,feature) // console.log('searchbar:moveFocus') // const map = gisMaps.current; diff --git a/apps/wr/app/store/map/coSttnMap/index.tsx b/apps/wr/app/store/map/coSttnMap/index.tsx new file mode 100644 index 000000000..d6bedd9c5 --- /dev/null +++ b/apps/wr/app/store/map/coSttnMap/index.tsx @@ -0,0 +1,120 @@ +import type { DemandCompType } from "@common/business_components/ui/sdmInsertForm/form"; +import type { CommonTableProps } from "../../common/type/header"; +import { coSttHeadListExport, coSttNodeListExport } from "../../matDmd/coSttn/headList"; +import { fetchType, getFetchWithCount } from "@/components/lib/api"; + +const coSttnMapRes: DemandCompType[] = []; + +export const costtnMapSize =20 + +export type coSttnMapState = { + coSttnMap: { + coSttnMapRes: DemandCompType[]; + coSttMapHeadList: CommonTableProps[]; + coSttMapNodeList: CommonTableProps[]; + excelList: []; + isInit: boolean; + coSttnMapResCount: number; + }; +}; + +type coSttnMapActions = { + coSttnMapInit: (pageNo: number, pageCnt: number, con: string) => void; + coSttnMapSearch: (pageNo: number, pageCnt: number, con: string) => void; + coSttnMapTxtSearch: (pageNo: number, pageCnt: number, con: string, txt: string) => void; +}; + +export type coSttnMapType = coSttnMapState & coSttnMapActions; + +export const coSttnMapStateExport: coSttnMapState = { + coSttnMap: { + coSttMapHeadList: coSttHeadListExport, + coSttMapNodeList: coSttNodeListExport, + coSttnMapRes: coSttnMapRes, + excelList: [], + isInit: false, + coSttnMapResCount: 0, + }, +}; + + +export const coSttnMapActionsExport: (set: any) => coSttnMapActions = ( + set: any +) => { + return { + coSttnMapInit: async (pageNo: number, pageCnt: number, con: string) => { + try { + const coSttnMapRes = await getFetchWithCount( + fetchType.WR, + `/reuseFc/request/${pageNo}/${pageCnt}/${con}` + ); + set((state: coSttnMapState) => { + return { + ...state, + coSttnMap: { + ...state.coSttnMap, + coSttnMapRes: coSttnMapRes.data, + isInit: false, + coSttnMapResCount: coSttnMapRes.count, + }, + }; + }); + } catch (e) { + console.log(e) + } + }, + coSttnMapSearch: async (pageNo: number, pageCnt: number, con: string) => { + try { + const coSttnMapRes = await getFetchWithCount( + fetchType.WR, + `/reuseFc/request/${pageNo}/${pageCnt}/${con}` + ); + if (Number(pageCnt) >= 10000) { + set((state: coSttnMapState) => { + return { + ...state, + coSttnMap: { + ...state.coSttnMap, + excelList: coSttnMapRes.data, + //isInit: false, + }, + }; + }); + } else + set((state: coSttnMapState) => { + return { + ...state, + coSttnMap: { + ...state.coSttnMap, + coSttnMapRes: coSttnMapRes.data, + isInit: false, + coSttnMapResCount: coSttnMapRes.count, + }, + }; + }); + } catch (erTxt) { + console.log("error", erTxt); + } + }, + coSttnMapTxtSearch: async (pageNo: number, pageCnt: number, con: string, txt: string) => { + try { + const coSttnMapRes = await getFetchWithCount( + fetchType.WR, + `/reuseFc/request/${pageNo}/${pageCnt}/${con}/${txt}`) + set((state: coSttnMapState) => { + return { + ...state, + coSttnMap: { + ...state.coSttnMap, + coSttnMapRes: coSttnMapRes.data, + coSttnMapResCount: coSttnMapRes.count, + isInit: false, + } + } + }) + } catch (erTxt) { + console.log("error", erTxt); + } + }, + } +} \ No newline at end of file diff --git a/apps/wr/app/store/map/common/index.tsx b/apps/wr/app/store/map/common/index.tsx new file mode 100644 index 000000000..2de402fa5 --- /dev/null +++ b/apps/wr/app/store/map/common/index.tsx @@ -0,0 +1,58 @@ + +export type comMapState = { + comMap: { + isFeatured:any + isModal:boolean + }; + }; + + type comMapActions = { + selectFeature:(feature:any)=>void, + chModalOpen:(open:boolean) => void, + }; + + export type comMapType = comMapState & comMapActions; + + export const comMapStateExport: comMapState = { + comMap: { + isFeatured:{}, + isModal:false + }, + }; + + + export const commonMapActionsExport: (set: any) => comMapActions = ( + set: any + ) => { + return { + selectFeature:(feature:any) => { + try { + set((state: comMapState) => { + return { + ...state, + comMap: { + ...state.comMap, + isFeatured:feature + }, + }; + }); + } catch (e) { + console.log(e) + } + },chModalOpen:(open:boolean) =>{ + try { + set((state: comMapState) => { + return { + ...state, + comMap: { + ...state.comMap, + isModal:open + }, + }; + }); + } catch (e) { + console.log(e) + } + } + } + } \ No newline at end of file diff --git a/apps/wr/app/store/map/htdflMap/index.tsx b/apps/wr/app/store/map/htdflMap/index.tsx new file mode 100644 index 000000000..03289a06d --- /dev/null +++ b/apps/wr/app/store/map/htdflMap/index.tsx @@ -0,0 +1,113 @@ +import { fetchType, getFetch, getFetchWithCount } from "@/components/lib/api"; +import type { CommonTableProps } from "../../common/type/header"; +import type { customHtdflBdst } from "../../ruseFcltMng/fcltReg/htdflReg"; +import { htdflSttHeadListExport, htdflSttNodeListExport } from "../../ruseFcltMng/fcltSttn/htdflSttn/headList"; + +export const htdfMapSize = 15; +export type htdfMapState = { + htdfMap: { + htdfMapRes: customHtdflBdst[]; + htdfMapCount: number; + isInit: boolean; + htdfSttHeadList: CommonTableProps[]; + htdfSttNodeList: CommonTableProps[]; + }; +}; + +type htdfMapActions = { + htdfMapInit: (page: number, total: number, cityOrSgg: string) => void; + htdfMapSearch: (page: number, total: number, cityOrSgg:string, searchTxt?:string) =>void + htdfMapDetile:(featureCd:string) => Promise<customHtdflBdst | undefined>; + +}; + +export type htdfMapType = htdfMapState & htdfMapActions; + +export const htdfMapStateExport: htdfMapState = { + htdfMap: { + isInit: false, + htdfMapRes: [], + htdfMapCount: 1, + htdfSttHeadList: htdflSttHeadListExport, + htdfSttNodeList: htdflSttNodeListExport, + }, +}; + +export const htdfMapActionsExport: (set: any) => htdfMapActions = ( + set: any +) => { + return { + htdfMapInit: async (page: number, total: number, cityOrSgg: string) => { + try { + const htdfMapRes = await getFetchWithCount( + fetchType.WR, + `/drain/facilityList` + + (page ? `/${page}` : "/1") + + (total ? `/${total}` : "/20") + + (cityOrSgg ? `/${cityOrSgg}` : "/0") + ); + + set((state: htdfMapState) => { + return { + ...state, + htdfMap: { + ...state.htdfMap, + htdfMapRes: htdfMapRes.data, + htdfMapCount: htdfMapRes.count, + isInit: false, + }, + }; + }); + } catch (e) { + console.log(e); + } + }, + htdfMapSearch: async ( + page: number, + total: number, + cityOrSgg: string, + searchTxt?: string + ) => { + try { + const htdfMapRes = await getFetchWithCount( + fetchType.WR, + `/drain/facilityList` + + (page ? `/${page}` : "/1") + + (total ? `/${total}` : "/20") + + (cityOrSgg ? `/${cityOrSgg}` : "/0") + + (searchTxt ? `/${searchTxt}` : "") + ); + + set((state: htdfMapState) => { + return { + ...state, + htdfMap: { + ...state.htdfMap, + htdfMapRes: htdfMapRes.data, + htdfMapCount: htdfMapRes.count, + }, + }; + }); + } catch (e) { + console.log(e); + } + }, + htdfMapDetile: async (featureCd: string) => { + try { + const htdfDetail = await getFetch( + fetchType.WR, + `/drain/facilityList/1/3/0/${featureCd}` + ); + + if (htdfDetail && htdfDetail.length > 0) { + return htdfDetail[0]; + } else { + return null; + } + } catch (error) { + console.log(error); + throw error; // 호출부에서 catch 가능하도록 + } + }, + }; +}; diff --git a/apps/wr/app/store/map/ldrmntCoMap/index.tsx b/apps/wr/app/store/map/ldrmntCoMap/index.tsx new file mode 100644 index 000000000..651fe21f2 --- /dev/null +++ b/apps/wr/app/store/map/ldrmntCoMap/index.tsx @@ -0,0 +1,118 @@ +import { fetchType, getFetch, getFetchWithCount } from "@/components/lib/api"; +import type { CommonTableProps } from "../../common/type/header"; +import type { customLdrmnt } from "../../matDmd/ldrmntCoSttn"; +import { + ldrmntHeadListExport, + ldrmntNodeListExport, +} from "../../matDmd/ldrmntCoSttn/headList"; + + +export const ldrmntMapSize = 20; +export type ldrmntMapState = { + ldrmntMap: { + ldrmntMapHeadList: CommonTableProps[]; + ldrmntMapNodeList: CommonTableProps[]; + isInit: boolean; + ldrmntMapRes: customLdrmnt[]; + ldrmntMapResCount: number; + }; +}; + +type ldrmntMapActions = { + ldrmntMapInit: (page: number, total: number, con: string) => void; + ldrmntMapSearch: ( + page: number, + total: number, + con: string, + coNm: string + ) => void; + ldrmntMapDetile:(featureCd:string) => Promise<customLdrmnt | undefined>; +}; + +export type ldrmntMapType = ldrmntMapState & ldrmntMapActions; + +export const ldrmntMapStateExport: ldrmntMapState = { + ldrmntMap: { + ldrmntMapHeadList: ldrmntHeadListExport, + ldrmntMapNodeList: ldrmntNodeListExport, + isInit: false, + ldrmntMapRes: [], + ldrmntMapResCount: 1, + }, +}; + +export const ldrmntMapActionsExport: (set: any) => ldrmntMapActions = ( + set: any +) => { + return { + ldrmntMapInit: async (page: number, total: number, con: string) => { + try { + const ldrmntMapRes = await getFetchWithCount( + fetchType.WR, + `/reuseFc/request/temp/${page}/${total}/${con}` + ); + + set((state: ldrmntMapState) => { + return { + ...state, + ldrmntMap: { + ...state.ldrmntMap, + ldrmntMapRes: ldrmntMapRes.data, + ldrmntMapResCount: ldrmntMapRes.count, + isInit: false, + }, + }; + }); + } catch (e) { + console.log(e); + } + }, + ldrmntMapSearch: async ( + page: number, + total: number, + con: string, + coNm: string + ) => { + let searchUrl = ""; + + if (con !== "") { + if (coNm !== "") { + searchUrl = `/reuseFc/request/temp/${page}/${total}/${con}/${coNm}`; + } else { + searchUrl = `/reuseFc/request/temp/${page}/${total}/${con}`; + } + } + try { + const ldrmntMapRes = await getFetchWithCount(fetchType.WR, searchUrl); + set((state: ldrmntMapState) => { + return { + ...state, + ldrmntMap: { + ...state.ldrmntMap, + ldrmntMapRes: ldrmntMapRes.data, + ldrmntMapResCount: ldrmntMapRes.count, + isInit: false, + }, + }; + }); + } catch (e) { + console.log(e); + } + },ldrmntMapDetile: async (featureCd: string) => { + try { + + const ldrmtDetail = await getFetch(fetchType.WR, + `/reuseFc/request/temp/1/4/0/${featureCd}` ) + if(ldrmtDetail&&ldrmtDetail.length>0){ + + return ldrmtDetail[0]; + }else{ + return null; + } + } catch (error) { + console.log(error); + throw error; // 호출부에서 catch 가능하도록 + } + } + }; +}; diff --git a/apps/wr/app/store/map/pbnstMap/index.tsx b/apps/wr/app/store/map/pbnstMap/index.tsx new file mode 100644 index 000000000..c093431ff --- /dev/null +++ b/apps/wr/app/store/map/pbnstMap/index.tsx @@ -0,0 +1,128 @@ +import { fetchType, postFectch } from "@/components/lib/api"; +import type { pubHasuType } from "../../ruseFcltMng/fcltReg/pbnstReg/type"; +import { checkWrRegList } from "../../ruseFcltMng/fcltReg/pbnstReg"; +import type { ReturnType } from "@common/business_components/ui"; +import { pubFacHeadListExport } from "../../ruseFcltMng/fcltReg/pbnstReg/headList"; +import type { TableUpperProps } from "../../common/type/header"; + +export const pbnMapTableSize = 20; + +const hasuListFetch = async ( + page: number, + pageTotal: number, + reurnValue: ReturnType, + searchNm?: string +) => { + let url = `/prcsFclt/ruseInfo/${page}/${pageTotal}/`; + const checkWrRegListRes = await checkWrRegList(); + + if (reurnValue.partValue === "A") { + url += `${reurnValue.partValue}/${ + // reurnValue.sidoValue ? reurnValue.sidoValue : reurnValue.sigunValue + reurnValue.sigunValue !== "0" + ? reurnValue.sigunValue + : reurnValue.sidoValue + }/0/1/SW`; + } else if (reurnValue.partValue === "R") { + url += `${reurnValue.partValue}/${ + reurnValue.regionValue + ? reurnValue.regionValue + : reurnValue.unitWaterShedValue + ? reurnValue.unitWaterShedValue + : reurnValue.middleRegionValue + }/0/1/SW`; + } else if (reurnValue.partValue === "G") { + url += `${reurnValue.envPartValue}/${reurnValue.sigunValue !== "0" ? reurnValue.sigunValue : reurnValue.sidoValue}/0/1/SW`; + } else { + url += `A/0/0/1/SW`; + } + if (searchNm && searchNm?.length > 0) { + url += "/" + searchNm; + } + + const data = checkWrRegListRes.filter((item: string) => item !== ""); + + const rainRes = await postFectch(url, data, fetchType.COMMON); + + const hasuList = rainRes.data.map((item: any) => { + return { + ...item, + fctCpc: Number(item.fctCpc) || 0, + ruse: item.ruse === false ? "N" : "Y", + }; + }); + + return { data: hasuList, count: rainRes.count }; +}; + +export type pbnMapState = { + pbnMap: { + isInit: boolean; + pbnMapRes: pubHasuType[]; + pbnMapCount: number; + pbnMapHeadList: TableUpperProps[]; + }; +}; + +type pbnMapActions = { + pbnMapInit: (page: number, pageTotal: number, reurnValue: ReturnType) => void; + pbnMapDetaile: (featureCd: string, data: string) => Promise<pubHasuType | undefined>; +}; + +export type pbnMapType = pbnMapState & pbnMapActions; + +export const pbnMapStateExport: pbnMapState = { + pbnMap: { + isInit: false, + pbnMapRes: [], + pbnMapCount: 1, + pbnMapHeadList: pubFacHeadListExport, + }, +}; + +export const pbnMapActionsExport: (set: any) => pbnMapActions = (set: any) => { + return { + pbnMapInit: async ( + page: number, + pageTotal: number, + reurnValue: ReturnType + ) => { + try { + const pubMapRes = await hasuListFetch(page, pageTotal, reurnValue); + set((state: pbnMapState) => { + return { + ...state, + pbnMap: { + ...state.pbnMap, + pbnMapRes: pubMapRes.data, + pbnMapCount: pubMapRes.count, + isInit: false, + }, + }; + }); + } catch (e) { + console.log(e); + } + }, + pbnMapDetaile: async (featureCd: string, data: string) => { + try { + const hasuRes = await postFectch( + `/prcsFclt/ruseInfo/1/20/A/0/0/1/SW/${featureCd}`, + [data], + fetchType.COMMON + ); + const filteredData = hasuRes.data.map((item: any) => { + return { + ...item, + fctCpc: Number(item.fctCpc) || 0, + ruse: item.ruse === false ? "N" : "Y", + }; + }); + return filteredData; + } catch (error) { + console.log(error); + throw error; // 호출부에서 catch 가능하도록 + } + }, + }; +}; diff --git a/apps/wr/app/store/map/rainMap/index.tsx b/apps/wr/app/store/map/rainMap/index.tsx new file mode 100644 index 000000000..a8d8ecb04 --- /dev/null +++ b/apps/wr/app/store/map/rainMap/index.tsx @@ -0,0 +1,112 @@ +import { fetchType, getFetch, getFetchWithCount } from "@/components/lib/api"; +import type { CommonTableProps } from "../../common/type/header"; +import { + rainSttHeadListExport, + rainSttNodeListExport, +} from "../../ruseFcltMng/fcltSttn/rainSttn/headList"; +import type { customRainBdst } from "../../ruseFcltMng/fcltReg/rainReg"; + + +export const rainMapSize = 15 +export type rainMapState = { + rainMap: { + rainMapRes: customRainBdst[]; + rainMapCount: number; + isInit: boolean; + rainSttHeadList: CommonTableProps[]; + rainSttNodeList: CommonTableProps[]; + }; +}; + +type rainMapActions = { + rainMapInit: (page: number, total: number, cityOrSgg:string) => void; + rainMapSearch: (page: number, total: number, cityOrSgg:string, searchTxt?:string) =>void + rainMapDetile:(featureCd:string) => Promise<customRainBdst | undefined>; +}; + +export type rainMapType = rainMapState & rainMapActions; + +export const rainMapStateExport: rainMapState = { + rainMap: { + isInit: false, + rainMapRes: [], + rainMapCount: 1, + rainSttHeadList: rainSttHeadListExport, + rainSttNodeList: rainSttNodeListExport, + }, +}; + +export const rainMapActionsExport: (set: any) => rainMapActions = ( + set: any +) => { + return { + rainMapInit: async (page: number, total: number, cityOrSgg:string) => { + try { + const rainMapRes = await getFetchWithCount( + fetchType.WR, + `/reuseFc/rainyInfoList` + + (page ? `/${page}` : "/1") + + (total ? `/${total}` : "/20") + + (cityOrSgg ? `/${cityOrSgg}` : "/0") + ); + + set((state: rainMapState) => { + return { + ...state, + rainMap: { + ...state.rainMap, + rainMapRes: rainMapRes.data, + rainMapCount: rainMapRes.count, + isInit: false, + }, + }; + }); + } catch (e) { + console.log(e); + } + }, + rainMapSearch: async (page: number, total: number, cityOrSgg:string, searchTxt?:string) => { + try { + const rainMapRes = await getFetchWithCount( + fetchType.WR, + `/reuseFc/rainyInfoList` + + (page ? `/${page}` : "/1") + + (total ? `/${total}` : "/20") + + (cityOrSgg ? `/${cityOrSgg}` : "/0") + + (searchTxt ? `/${searchTxt}` : "") + ); + + set((state: rainMapState) => { + return { + ...state, + rainMap: { + ...state.rainMap, + rainMapRes: rainMapRes.data, + rainMapCount: rainMapRes.count, + }, + }; + }); + } catch (e) { + console.log(e); + } + }, + rainMapDetile: async (featureCd: string) => { + try { + console.log('featureCd ', featureCd) + + const rainDetail = await getFetch(fetchType.WR, + `/reuseFc/rainyInfoList/1/3/0/${featureCd}` ) + + if(rainDetail&&rainDetail.length>0){ + + return rainDetail[0]; + }else{ + return null; + } + } catch (error) { + console.log(error); + throw error; // 호출부에서 catch 가능하도록 + } + } +} +}; diff --git a/apps/wr/app/store/map/rswtMap/index.tsx b/apps/wr/app/store/map/rswtMap/index.tsx new file mode 100644 index 000000000..e788bedc1 --- /dev/null +++ b/apps/wr/app/store/map/rswtMap/index.tsx @@ -0,0 +1,120 @@ +import { fetchType, getFetch, getFetchWithCount } from "@/components/lib/api"; +import type { CommonTableProps } from "../../common/type/header"; +import type { customRswtBdst } from "../../ruseFcltMng/fcltReg/rswtReg"; +import { + rswtSttHeadListExport, + rswtSttNodeListExport, +} from "../../ruseFcltMng/fcltSttn/rswtSttn/headList"; + +export const rswtMapSize = 15; +export type rswtMapState = { + rswtMap: { + rswtMapRes: customRswtBdst[]; + rswtMapCount: number; + isInit: boolean; + rswtSttHeadList: CommonTableProps[]; + rswtSttNodeList: CommonTableProps[]; + }; +}; + +type rswtMapActions = { + rswtMapInit: (page: number, total: number, cityOrSgg: string) => void; + rswtMapSearch: ( + page: number, + total: number, + cityOrSgg: string, + searchTxt?: string + ) => void; + rswtMapDetile: (featureCd: string) => Promise<customRswtBdst | undefined>; +}; + +export type rswtMapType = rswtMapState & rswtMapActions; + +export const rswtMapStateExport: rswtMapState = { + rswtMap: { + isInit: false, + rswtMapRes: [], + rswtMapCount: 1, + rswtSttHeadList: rswtSttHeadListExport, + rswtSttNodeList: rswtSttNodeListExport, + }, +}; + +export const rswtMapActionsExport: (set: any) => rswtMapActions = ( + set: any +) => { + return { + rswtMapInit: async (page: number, total: number, cityOrSgg: string) => { + try { + const rswtMapRes = await getFetchWithCount( + fetchType.WR, + `/reuseFc/waterreuseFc` + + (page ? `/${page}` : "/1") + + (total ? `/${total}` : "/20") + + (cityOrSgg ? `/${cityOrSgg}` : "/0") + ); + + set((state: rswtMapState) => { + return { + ...state, + rswtMap: { + ...state.rswtMap, + rswtMapRes: rswtMapRes.data, + rswtMapCount: rswtMapRes.count, + isInit: false, + }, + }; + }); + } catch (e) { + console.log(e); + } + }, + rswtMapSearch: async ( + page: number, + total: number, + cityOrSgg: string, + searchTxt?: string + ) => { + try { + const rswtMapRes = await getFetchWithCount( + fetchType.WR, + `/reuseFc/waterreuseFc` + + (page ? `/${page}` : "/1") + + (total ? `/${total}` : "/20") + + (cityOrSgg ? `/${cityOrSgg}` : "/0") + + (searchTxt ? `/${searchTxt}` : "") + ); + + set((state: rswtMapState) => { + return { + ...state, + rswtMap: { + ...state.rswtMap, + rswtMapRes: rswtMapRes.data, + rswtMapCount: rswtMapRes.count, + }, + }; + }); + } catch (e) { + console.log(e); + } + }, + rswtMapDetile: async (featureCd: string) => { + try { + const rswtDetail = await getFetch( + fetchType.WR, + `/reuseFc/waterreuseFc/1/3/0/${featureCd}` + ); + + if (rswtDetail && rswtDetail.length > 0) { + return rswtDetail[0]; + } else { + return null; + } + } catch (error) { + console.log(error); + throw error; // 호출부에서 catch 가능하도록 + } + }, + }; +}; diff --git a/apps/wr/app/store/map/ruseMap/index.tsx b/apps/wr/app/store/map/ruseMap/index.tsx new file mode 100644 index 000000000..d42fc6202 --- /dev/null +++ b/apps/wr/app/store/map/ruseMap/index.tsx @@ -0,0 +1,111 @@ +import { fetchType, getFetch } from "@/components/lib/api"; +import type { CommonTableProps } from "../../common/type/header"; +import type { WaterReuseFacility } from "../../ruseFcltMng/fcltSttn/pbnstSttn/type"; +import { + pbnstSttNodeListExport, + pbnSttHeadListExport, +} from "../../ruseFcltMng/fcltSttn/pbnstSttn/headList"; +import type { ReturnType } from "@common/business_components/ui"; +import { hasuListFetch } from "../../ruseFcltMng/fcltSttn/pbnstSttn"; +import type { customPbnst } from "../../ruseFcltMng/fcltReg/pbnstReg/type"; + +export const ruseMapSize = 15; +export type ruseMapState = { + ruseMap: { + ruseMapRes: WaterReuseFacility[]; + ruseMapCount: number; + isInit: boolean; + fclts: string[]; + ruseMapHeadList: CommonTableProps[]; + ruseMapNodeList: CommonTableProps[]; + }; +}; + +type ruseMapActions = { + getMapFlcts: () => void; + ruseMapInit: ( + page: number, + total: number, + reurnValue: ReturnType, + year: string, + fclts: string[] + ) => void; + ruseMapDetail: (ruseFcltId: string) => Promise<customPbnst | undefined>; +}; + +export type ruseMapType = ruseMapState & ruseMapActions; + +export const ruseMapStateExport: ruseMapState = { + ruseMap: { + isInit: false, + ruseMapRes: [], + fclts: [], + ruseMapCount: 1, + ruseMapHeadList: pbnSttHeadListExport, + ruseMapNodeList: pbnstSttNodeListExport, + }, +}; + +export const ruseMapActionsExport: (set: any) => ruseMapActions = ( + set: any +) => { + return { + getMapFlcts: async () => { + const fclts = await getFetch(fetchType.WR, "/reuseFc/pubFc"); + + return set((state: ruseMapState) => ({ + ruseMap: { + ...state.ruseMap, + fclts: fclts, + }, + })); + }, + ruseMapInit: async ( + page: number, + total: number, + reurnValue: ReturnType, + year: string, + fclts: string[] + ) => { + try { + const ruseMapRes = await hasuListFetch( + String(page), + String(total), + reurnValue, + undefined, + year, + fclts + ); + + set((state: ruseMapState) => { + return { + ...state, + ruseMap: { + ...state.ruseMap, + ruseMapRes: ruseMapRes.data, + ruseMapCount: ruseMapRes.count, + isInit: false, + }, + }; + }); + } catch (e) { + console.log(e); + } + }, + ruseMapDetail: async (ruseFcltId: string) => { + try { + let ruseDetail = await getFetch("wr", `/reuseFc/pubFc/${ruseFcltId}`); + if (ruseDetail.length >= 0) { + ruseDetail = ruseDetail.find( + (item: customPbnst) => item.pbnstRuseFcltId === ruseFcltId + ); + } + // 🔑 스토어 업데이트 대신 리턴 + return ruseDetail; + } catch (errorHasu) { + console.log(errorHasu); + throw errorHasu; // 호출부에서 catch 가능하도록 + } + }, + }; +}; diff --git a/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/excel.tsx b/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/excel.tsx index a484f87c2..8e18317a8 100644 --- a/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/excel.tsx +++ b/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/excel.tsx @@ -132,20 +132,20 @@ export const excelToRainChkData = (excelData: any, year: string) => { bdstNm: item["시설명"] ?? "", bdstRuseFcltSn: Number(item["NO"]), chckYmd: checkYmd, - spwtPositn: item["유량측정방법"] ?? "", rgtr: TEST_UUID, - oilqMeasMthd: item["운영관리형태"] ?? "", + operMngCd: item["운영관리형태"] ?? "", anlOperCst: parseRawValue(item["연간운영비(원/년)"]), - operMngCd: item["유량측정방법"] ?? "", + oilqMeasMthd: item["유량측정방법"] ?? "", guideDetails: makeGuidDetail, rainyDetails: [ { + ph: item["PH"], utztnUsgCd: item["이용용도"], trbt: parseRawValue(item["탁도"]), grssClfg: parseRawValue(item["총대장균군수"]), etc: item["기타"] || "", // 필드 누락 방지 - vltnDt: vltnDt || "-", - vltnMttr: item["위반사항"] || "-", + vltnDt: vltnDt || "", + vltnMttr: item["위반사항"] || "", levyAmt: parseRawValue(item["과태료부과금액(천원)"]), rgtr: TEST_UUID, }, @@ -177,12 +177,6 @@ console.log("rainChkgdData", rainChkgdData); defaultMessage + `유량측정방법: 위탁 혹은 자체 만 허용) /입력값:${rain.oilqMeasMthd}` ); } - if (rain.spwtPositn.length > 1 && !chkSpwtPositnType.includes(rain.spwtPositn)) { - issues.push( - defaultMessage + `측정위치: 유량계, 수위계, 펌프적산전력계, 기타 중 하나만 허용) /입력값:${rain.spwtPositn}` - ); - } - rain.rainyDetails?.forEach((key) => { if (key.ph?.length > 1 && isInvalidNumber(key.ph)) { diff --git a/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList.tsx b/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList.tsx index d216b88f6..44d039d7f 100644 --- a/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList.tsx +++ b/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList.tsx @@ -63,7 +63,7 @@ export const rainChkZod = z.object({ z.object({ chkgdnSn: z.number().nullable(), utztnUsgCd: z.string(), - ph: z.number().nullable(), + ph:z.string().nullish(), trbt: z.number().nullable(), grssClfg: z.number().nullable(), etc: z.string().nullable(), diff --git a/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/excel.tsx b/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/excel.tsx index aa550dcd1..d9016410d 100644 --- a/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/excel.tsx +++ b/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/excel.tsx @@ -1,17 +1,16 @@ -import type { SelectDataType } from "@common/business_components/ui"; -import { - chkMngType, - chkSpwtPositnType, - useTypes, - type Buildingtype, -} from "../rainChkgdn/excel"; import { TEST_UUID } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; -import type { rswtChkZodType } from "."; import { isInvalidDate, isInvalidNumber, parseRawValue, } from "@/components/lib/func/excelFunc"; +import type { SelectDataType } from "@common/business_components/ui"; +import type { rswtChkZodType } from "."; +import { + chkSpwtPositnType, + useTypes, + type Buildingtype +} from "../rainChkgdn/excel"; export const rswtMakeExcelSample = ( buildings: Buildingtype[], @@ -284,15 +283,6 @@ const rswtExcelDataCheck = ( defaultMessage + ` 처리용량(m³): 숫자 형식 아님 (${rswt.prcsCpct})` ); } - if ( - rswt.oilqMeasMthd.length > 1 && - !chkMngType.includes(rswt.oilqMeasMthd) - ) { - issues.push( - defaultMessage + - `유량측정방법: 위탁 혹은 자체 만 허용) /입력값:${rswt.oilqMeasMthd}` - ); - } if ( rswt.spwtPositn.length > 1 && !chkSpwtPositnType.includes(rswt.spwtPositn) diff --git a/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/headList.tsx b/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/headList.tsx index 04914b236..8ec8c3f41 100644 --- a/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/headList.tsx +++ b/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/headList.tsx @@ -163,11 +163,11 @@ export const rswtChkHeadListExport: TableUpperProps[] = [ }, { id: "trbt", title: "탁도(NTU)", upName: "useQuilty", upSequnce: 3 ,type: "number"}, { id: "bod", title: "BOD(mg/L)", upName: "useQuilty", upSequnce: 4 ,type: "number"}, - { id: "smll", title: "냄새", upName: "useQuilty", upSequnce: 5 ,type: "number"}, + { id: "smll", title: "냄새", upName: "useQuilty", upSequnce: 5 ,type: "text"}, { id: "crmty", title: "색도(도)", upName: "useQuilty", upSequnce: 6 ,type: "number"}, { id: "tn", title: "총질소(T-N)(mg/L)", upName: "useQuilty", upSequnce: 7 ,type: "number"}, { id: "tph", title: "총인(T-P)(mg/L)", upName: "useQuilty", upSequnce: 8 ,type: "number"}, - { id: "ph", title: "수소이온농도(PH)", upName: "useQuilty", upSequnce: 9,type: "number" }, + { id: "ph", title: "수소이온농도(PH)", upName: "useQuilty", upSequnce: 9,type: "text" }, { id: "clIonQnt", title: "염화물(mg/L)", upName: "useQuilty", upSequnce: 10 ,type: "number"}, { diff --git a/apps/wr/app/store/prfmncSttn/chkgdnSttn/rswtChkgdnSttn/headList.tsx b/apps/wr/app/store/prfmncSttn/chkgdnSttn/rswtChkgdnSttn/headList.tsx index e000c550a..0fc025272 100644 --- a/apps/wr/app/store/prfmncSttn/chkgdnSttn/rswtChkgdnSttn/headList.tsx +++ b/apps/wr/app/store/prfmncSttn/chkgdnSttn/rswtChkgdnSttn/headList.tsx @@ -47,7 +47,7 @@ export const rswtChkSttnHeadListExport: TableUpperProps[] = [ title: "냄새", upName: "useQuilty", upSequnce: 5, - type: "number", + type: "text", }, { id: "crmty", @@ -64,7 +64,7 @@ export const rswtChkSttnHeadListExport: TableUpperProps[] = [ upSequnce: 8, type: "number", }, - { id: "ph", title: "수소이온농도(PH)", upName: "useQuilty", upSequnce: 9, type: "number" }, + { id: "ph", title: "수소이온농도(PH)", upName: "useQuilty", upSequnce: 9, type: "text" }, { id: "clIonQnt", title: "염화물(mg/L)", diff --git a/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/index.tsx b/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/index.tsx index d138a8dfe..3712b4dd1 100644 --- a/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/index.tsx +++ b/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/index.tsx @@ -97,7 +97,7 @@ export const pubFacStateExport: pubFacState = { }, }; -const checkWrRegList = async () => { +export const checkWrRegList = async () => { const wrRegList = await getFetch("wr", `/reuseFc/pubFc`); return wrRegList; }; diff --git a/apps/wr/app/store/ruseFcltMng/fcltSttn/pbnstSttn/index.tsx b/apps/wr/app/store/ruseFcltMng/fcltSttn/pbnstSttn/index.tsx index 7f905dd49..f14101626 100644 --- a/apps/wr/app/store/ruseFcltMng/fcltSttn/pbnstSttn/index.tsx +++ b/apps/wr/app/store/ruseFcltMng/fcltSttn/pbnstSttn/index.tsx @@ -87,7 +87,7 @@ export const pbnstSttnStateExport: pbnstSttnState = { }, }; -const hasuListFetch = async ( +export const hasuListFetch = async ( page: string, total: string, reurnValue: ReturnType, diff --git a/apps/wr/app/store/store.tsx b/apps/wr/app/store/store.tsx index 284f300c5..349dd8865 100644 --- a/apps/wr/app/store/store.tsx +++ b/apps/wr/app/store/store.tsx @@ -11,19 +11,54 @@ import { type comCodeState, type comCodeType, } from "./common/code"; -import { comFcltSearchAction, comFcltStateExport, type comFcltState, type comFcltType } from "./common/searchCode"; +import { + comFcltSearchAction, + comFcltStateExport, + type comFcltState, + type comFcltType, +} from "./common/searchCode"; import { matDmdActionsExport, matDmdStateExport, type matDmdState, type matDmdType, } from "./matDmd"; -import { coRvwActionsExport, coRvwStateExport, type coRvwState, type coRvwType } from "./matDmd/coMatRvw"; -import { coSttnActionsExport, coSttnStateExport, type coSttnState, type coSttnType } from "./matDmd/coSttn"; // 옮겨야함 -import { drgtActionsExport, drgtStateExport, type drgtState, type drgtType } from "./matDmd/drgtSttn"; -import { ldrmntActionsExport, ldrmntStateExport, type ldrmntState, type ldrmntType } from "./matDmd/ldrmntCoSttn"; -import { ruseCaseActionsExport, ruseCaseStateExport, type ruseCaseState, type ruseCaseType } from "./matDmd/ruseCaseMng"; -import { ruseHstryActionsExport, ruseHstryStateExport, type ruseHstryState, type ruseHstryType } from "./matDmd/ruseRvwHstry"; +import { + coRvwActionsExport, + coRvwStateExport, + type coRvwState, + type coRvwType, +} from "./matDmd/coMatRvw"; +import { + coSttnActionsExport, + coSttnStateExport, + type coSttnState, + type coSttnType, +} from "./matDmd/coSttn"; // 옮겨야함 +import { + drgtActionsExport, + drgtStateExport, + type drgtState, + type drgtType, +} from "./matDmd/drgtSttn"; +import { + ldrmntActionsExport, + ldrmntStateExport, + type ldrmntState, + type ldrmntType, +} from "./matDmd/ldrmntCoSttn"; +import { + ruseCaseActionsExport, + ruseCaseStateExport, + type ruseCaseState, + type ruseCaseType, +} from "./matDmd/ruseCaseMng"; +import { + ruseHstryActionsExport, + ruseHstryStateExport, + type ruseHstryState, + type ruseHstryType, +} from "./matDmd/ruseRvwHstry"; import { rainChkgdnActionsExport, rainChkgdnStateExport, @@ -36,15 +71,30 @@ import { type rswtChkgdnState, type rswtChkgdnType, } from "./prfmncReg/chkgdnReg/rswrtChkgdn"; -import { pbnstPrfmncActionsExport, pbnstPrfmncStateExport, type pbnstPrfmncState, type pbnstPrfmncType } from "./prfmncReg/pbnstPrfmncReg"; +import { + pbnstPrfmncActionsExport, + pbnstPrfmncStateExport, + type pbnstPrfmncState, + type pbnstPrfmncType, +} from "./prfmncReg/pbnstPrfmncReg"; import { rainChkSttnActionsExport, rainChkSttnStateExport, type rainChkSttnState, type rainChkSttnType, } from "./prfmncSttn/chkgdnSttn/rainChkgdnSttn"; -import { rswtChkStateExport, rswtChkSttnActionsExport, type rswtChkSttnState, type rswtChkSttnType } from "./prfmncSttn/chkgdnSttn/rswtChkgdnSttn"; -import { pbnstPrfmncSttnActionsExport, pbnstPrfmncSttnExport, type pbnstPrfmncSttnState, type pbnstPrfmncSttnType } from "./prfmncSttn/pbnstPrfmncSttn"; +import { + rswtChkStateExport, + rswtChkSttnActionsExport, + type rswtChkSttnState, + type rswtChkSttnType, +} from "./prfmncSttn/chkgdnSttn/rswtChkgdnSttn"; +import { + pbnstPrfmncSttnActionsExport, + pbnstPrfmncSttnExport, + type pbnstPrfmncSttnState, + type pbnstPrfmncSttnType, +} from "./prfmncSttn/pbnstPrfmncSttn"; import { bdstActionsExport, bdstStateExport, @@ -81,7 +131,12 @@ import { type htdflSttnState, type htdflSttnType, } from "./ruseFcltMng/fcltSttn/htdflSttn"; -import { pbnstSttnActionsExport, pbnstSttnStateExport, type pbnstSttnState, type pbnstSttnType } from "./ruseFcltMng/fcltSttn/pbnstSttn"; +import { + pbnstSttnActionsExport, + pbnstSttnStateExport, + type pbnstSttnState, + type pbnstSttnType, +} from "./ruseFcltMng/fcltSttn/pbnstSttn"; import { rainSttnActionsExport, rainSttnStateExport, @@ -94,11 +149,74 @@ import { type rswtSttnState, type rswtSttnType, } from "./ruseFcltMng/fcltSttn/rswtSttn"; -import { rainStatActionsExport, rainStatExport, type rainStatState, type rainStatType } from "./stats/rainStat"; -import { rswtStatActionsExport, rswtStatExport, type rswtStatState, type rswtStatType } from "./stats/rswtStat"; +import { + rainStatActionsExport, + rainStatExport, + type rainStatState, + type rainStatType, +} from "./stats/rainStat"; +import { + rswtStatActionsExport, + rswtStatExport, + type rswtStatState, + type rswtStatType, +} from "./stats/rswtStat"; //import { isLogin } from "./common/login"; import { LoginApi } from "@/src/entities/auth"; -import { authActionsExport, authStateExport, type authState, type authType } from "./common/auth"; +import { + authActionsExport, + authStateExport, + type authState, + type authType, +} from "./common/auth"; +import { + pbnMapActionsExport, + pbnMapStateExport, + type pbnMapState, + type pbnMapType, +} from "./map/pbnstMap"; +import { + comMapStateExport, + commonMapActionsExport, + type comMapState, + type comMapType, +} from "./map/common"; +import { + rainMapActionsExport, + rainMapStateExport, + type rainMapState, + type rainMapType, +} from "./map/rainMap"; +import { + rswtMapActionsExport, + rswtMapStateExport, + type rswtMapState, + type rswtMapType, +} from "./map/rswtMap"; +import { + htdfMapActionsExport, + htdfMapStateExport, + type htdfMapState, + type htdfMapType, +} from "./map/htdflMap"; +import { + ruseMapActionsExport, + ruseMapStateExport, + type ruseMapState, + type ruseMapType, +} from "./map/ruseMap"; +import { + coSttnMapActionsExport, + coSttnMapStateExport, + type coSttnMapState, + type coSttnMapType, +} from "./map/coSttnMap"; +import { + ldrmntMapActionsExport, + ldrmntMapStateExport, + type ldrmntMapState, + type ldrmntMapType, +} from "./map/ldrmntCoMap"; export type WrStore = comCodeType & pubFacType & @@ -112,23 +230,29 @@ export type WrStore = comCodeType & rainSttnType & rswtSttnType & htdflSttnType & - rainChkSttnType& - rswtChkSttnType& - pbnstSttnType& - coSttnType& // 옮겨야함 - ldrmntType& // 옮겨야함 - pbnstPrfmncType& - coRvwType& - ruseHstryType& - pbnstPrfmncSttnType& - drgtType& - ruseCaseType& - rainStatType& - rswtStatType& + rainChkSttnType & + rswtChkSttnType & + pbnstSttnType & + coSttnType & // 옮겨야함 + ldrmntType & // 옮겨야함 + pbnstPrfmncType & + coRvwType & + ruseHstryType & + pbnstPrfmncSttnType & + drgtType & + ruseCaseType & + rainStatType & + rswtStatType & comFcltType & - authType - - ; + pbnMapType & + comMapType & + rainMapType & + rswtMapType & + htdfMapType & + ruseMapType & + coSttnMapType & + ldrmntMapType & + authType; export const StoreZus = ( initState: comCodeState & @@ -143,22 +267,29 @@ export const StoreZus = ( rainSttnState & rswtSttnState & htdflSttnState & - rainChkSttnState& - comFcltState& + rainChkSttnState & + comFcltState & rswtChkSttnState & - pbnstSttnState& - coSttnState& // 옮겨야함 - ldrmntState& // 옮겨야함 - pbnstPrfmncState& - coRvwState& - ruseHstryState& - pbnstPrfmncSttnState& - drgtState& - ruseCaseState& - rainStatState& - rswtStatState& - authState - = { + pbnstSttnState & + coSttnState & // 옮겨야함 + ldrmntState & // 옮겨야함 + pbnstPrfmncState & + coRvwState & + ruseHstryState & + pbnstPrfmncSttnState & + drgtState & + ruseCaseState & + rainStatState & + rswtStatState & + pbnMapState & + comMapState & + rainMapState & + rswtMapState & + htdfMapState & + ruseMapState & + coSttnMapState & + ldrmntMapState & + authState = { ...comCodeStateExport, ...pubFacStateExport, ...rainFacStateExport, @@ -174,8 +305,8 @@ export const StoreZus = ( ...rainChkSttnStateExport, ...rswtChkStateExport, ...pbnstSttnStateExport, - ...coSttnStateExport, // 옮겨야함 - ...ldrmntStateExport, // 옮겨야함 + ...coSttnStateExport, + ...ldrmntStateExport, ...pbnstPrfmncStateExport, ...coRvwStateExport, ...ruseHstryStateExport, @@ -185,7 +316,15 @@ export const StoreZus = ( ...rainStatExport, ...rswtStatExport, ...comFcltStateExport, - ...authStateExport + ...pbnMapStateExport, + ...comMapStateExport, + ...authStateExport, + ...rainMapStateExport, + ...rswtMapStateExport, + ...htdfMapStateExport, + ...ruseMapStateExport, + ...coSttnMapStateExport, + ...ldrmntMapStateExport, } ) => { return createStore<WrStore>()((set) => { @@ -195,7 +334,7 @@ export const StoreZus = ( getMiddleRegionCd(set); getUnitWaterShedCd(set); getEnvPartCd(set); - LoginApi.login({userId: "CLEANWATER", password: "gktneh12!@"}) + LoginApi.login({ userId: "CLEANWATER", password: "gktneh12!@" }); return { ...initState, ...comFcltSearchAction(set), @@ -214,8 +353,8 @@ export const StoreZus = ( ...rainChkSttnActionsExport(set), ...rswtChkSttnActionsExport(set), ...pbnstSttnActionsExport(set), - ...coSttnActionsExport(set), // 옮겨야함 - ...ldrmntActionsExport(set), // 옮겨야함 + ...coSttnActionsExport(set), // 옮겨야함 + ...ldrmntActionsExport(set), // 옮겨야함 ...pbnstPrfmncActionsExport(set), ...coRvwActionsExport(set), ...ruseHstryActionsExport(set), @@ -224,7 +363,15 @@ export const StoreZus = ( ...ruseCaseActionsExport(set), ...rainStatActionsExport(set), ...rswtStatActionsExport(set), - ...authActionsExport(set) + ...authActionsExport(set), + ...commonMapActionsExport(set), + ...pbnMapActionsExport(set), + ...rainMapActionsExport(set), + ...rswtMapActionsExport(set), + ...htdfMapActionsExport(set), + ...ruseMapActionsExport(set), + ...coSttnMapActionsExport(set), + ...ldrmntMapActionsExport(set), }; }); }; diff --git a/apps/wr/components/lib/func/TableData.tsx b/apps/wr/components/lib/func/TableData.tsx index 192bc43c3..bee5e093d 100644 --- a/apps/wr/components/lib/func/TableData.tsx +++ b/apps/wr/components/lib/func/TableData.tsx @@ -2,8 +2,18 @@ import { numSize } from "@/app/store/common/code/basicCode"; import type { TableUpperProps } from "@/app/store/common/type/header"; import type { rainChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn"; import type { rswtChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rswrtChkgdn"; -import { Checkbox, Input, TableCell, TableRow } from "@common/components/ui"; -import { formatCommaNumber, handleCommaNumberChange } from "./commom"; +import { + Checkbox, + Input, + TableCell, + TableRow, +} from "@common/components/ui"; +import moment from "moment"; +import { + formatCommaNumber, + formatYyyymmdd, + handleCommaNumberChange +} from "./commom"; export const rainChkTableData = (chkData: rainChkZodType[]) => { // 1. chkgdnSn + utztnUsgCd 기준 누계 정보 만들기 @@ -57,9 +67,7 @@ export const rswtChkTableData = (chkData: rswtChkZodType[]) => { // 1. chkgdnSn + utztnUsgCd 기준 누계 정보 만들기 const makeRswtData: any[] = []; - if (chkData && chkData.length > 0) { - chkData?.forEach((root) => { root.rswtrDetails.forEach((detail) => { let flatRswtChk = {}; @@ -96,20 +104,19 @@ export const chRowspanTable = ( toggleSelection: (pk: number, checked: boolean) => void, selectedIds: number[], handleCellClick: (index: number, headId: string) => void, - disable? : boolean + disable?: boolean ) => { - let order = 0 + let order = 0; // rowspan된 셀 렌더링 여부 추적용 const renderedMap = new Map<string, Set<number>>(); return ( <> {/* {makeTableData.map((row, rowIndex) => ( */} - {makeTableData && makeTableData.length > 0 ? (makeTableData.map((row, rowIndex) => ( - <TableRow key={rowIndex}> - {headList - .map((head) => { - + {makeTableData && makeTableData.length > 0 ? ( + makeTableData.map((row, rowIndex) => ( + <TableRow key={rowIndex}> + {headList.map((head) => { const key = head.id; const isMerged = head.mergeCell; const value = row[key]; @@ -118,7 +125,6 @@ export const chRowspanTable = ( if (isMerged) { const renderedSet = renderedMap.get(key) ?? new Set<number>(); if (renderedSet.has(rowIndex)) { - return null; // 이미 렌더링된 셀은 skip } @@ -143,14 +149,17 @@ export const chRowspanTable = ( ></Checkbox> ) : key === "order" ? ( ++order - ) : ( - // value - (["anlOperCst", "dtfcltCpct", "prcsCpct"].includes(head.id)) ? ( + ) : // value + ["anlOperCst", "dtfcltCpct", "prcsCpct"].includes( + head.id + ) ? ( formatCommaNumber(value) - ) : ( - <>{value}</> - ) - )} + ) : key === "chckYmd" ? ( + <>{formatYyyymmdd(value)}</> + ) : + ( + <>{value}</> + )} </TableCell> ); } @@ -163,23 +172,48 @@ export const chRowspanTable = ( onClick={() => handleCellClick(rowIndex, head.id)} > {head.edit === true ? ( - (["levyAmt", "anlOperCst", "dtfcltCpct", "prcsCpct"].includes(head.id)) ? ( + [ + "levyAmt", + "anlOperCst", + "dtfcltCpct", + "prcsCpct", + ].includes(head.id) ? ( <Input className={`${head.type === "number" ? "text-right" : "text-center"} !text-xl`} type={head.type} onChange={(e) => { - handleCommaNumberChange(e, val => - handleChange(rowIndex, head.id, val)) + handleCommaNumberChange(e, (val) => + handleChange(rowIndex, head.id, val) + ); }} value={formatCommaNumber(value) || ""} disabled={!selectedIds.includes(row.chkgdnSn)} maxLength={numSize} onInput={(e: React.ChangeEvent<HTMLInputElement>) => { - if (head.type === 'number' && e.target.value.length > numSize) { + if ( + head.type === "number" && + e.target.value.length > numSize + ) { e.target.value = e.target.value.slice(0, numSize); } }} /> + ) : ["vltnDt"].includes(head.id) ? ( + <Input + type="date" + onChange={(e) => { + // 브라우저에서 선택한 값은 "YYYY-MM-DD" + const rawDate = e.target.value; // e.g. "2025-06-01" + const yyyymmdd = moment(rawDate).format("YYYYMMDD"); + handleChange(rowIndex, head.id, yyyymmdd); + }} + value={ + value + ? moment(value, "YYYYMMDD").format("YYYY-MM-DD") + : "" + } + disabled={!selectedIds.includes(row.chkgdnSn)} + /> ) : ( <Input className={`${head.type === "number" ? "text-right" : "text-center"} !text-xl`} @@ -191,28 +225,52 @@ export const chRowspanTable = ( disabled={!selectedIds.includes(row.chkgdnSn)} maxLength={numSize} onInput={(e: React.ChangeEvent<HTMLInputElement>) => { - if (head.type === 'number' && e.target.value.length > numSize) { + if ( + head.type === "number" && + e.target.value.length > numSize + ) { e.target.value = e.target.value.slice(0, numSize); } }} /> ) + ) : ["smll", "ph"].includes(head.id) && head.type === "text" ? ( + <>-</> + ) : [ + "totalSplyUswtrUtztnQnt", + "totalRuseFcltUtztnQnt", + "splyUswtrUtztnQnt", + "ruseFcltUtztnQnt", + "levyAmt", + ].includes(head.id) ? ( + <span + className={ + [ + "totalSplyUswtrUtztnQnt", + "totalRuseFcltUtztnQnt", + "splyUswtrUtztnQnt", + "ruseFcltUtztnQnt", + ].includes(head.id) + ? "underline text-blue-600 cursor-pointer" + : "" + } + > + {formatCommaNumber(value)} + </span> ) : ( - (["totalSplyUswtrUtztnQnt", "totalRuseFcltUtztnQnt", "splyUswtrUtztnQnt", "ruseFcltUtztnQnt", "levyAmt"].includes(head.id)) ? ( - <span className={["totalSplyUswtrUtztnQnt", "totalRuseFcltUtztnQnt", "splyUswtrUtztnQnt", "ruseFcltUtztnQnt"].includes(head.id) ? "underline text-blue-600 cursor-pointer" : ""}> - {formatCommaNumber(value)} - </span> - ) : ( - <>{value}</> - ) + <>{value}</> )} </TableCell> ); })} - </TableRow> - ))) : ( + </TableRow> + )) + ) : ( <TableRow> - <TableCell colSpan={headList.length} className="h-24 text-center text-gray-500"> + <TableCell + colSpan={headList.length} + className="h-24 text-center text-gray-500" + > 조회된 결과가 없습니다. </TableCell> </TableRow> @@ -221,16 +279,17 @@ export const chRowspanTable = ( ); }; -export const headVisibleChange = (headList: TableUpperProps[], isEdit: boolean) => { +export const headVisibleChange = ( + headList: TableUpperProps[], + isEdit: boolean +) => { return headList.forEach((head) => { - if (head.edit !== undefined) - head.edit = isEdit; - if (head.id === 'checkBox') { - head.visible = false + if (head.edit !== undefined) head.edit = isEdit; + if (head.id === "checkBox") { + head.visible = false; } - }); -} +}; const handleChange = ( rowIndex: number, @@ -253,8 +312,6 @@ const handleChange = ( }) ); }; -; - export const InputTableBody = ( chgRes: any[] | [], headList: TableUpperProps[], @@ -289,8 +346,21 @@ export const InputTableBody = ( return ( <TableRow key={dataIndex + "row"}> {headList.map((head, headIndex) => { - const isTextColumn = head.id === '구분' || head.id === '용수명'; - const textAlignClass = isTextColumn ? 'text-center' : 'text-right'; + //냄새, ph면 계산 못하게 - 로 수정 + if (["smll", "ph"].includes(head.id)) { + return ( + <TableCell + key={head.id + headIndex} + className={`w-36 ${bgClass} text-center !text-xl`} + > + <span>-</span> + </TableCell> + ); + } + const isTextColumn = head.id === "구분" || head.id === "용수명"; + const textAlignClass = isTextColumn + ? "text-center" + : "text-right"; // 총합계 행이면 "구분"에서 colSpan=2로 병합 if ( @@ -318,8 +388,13 @@ export const InputTableBody = ( // 총합계 행의 나머지 셀에도 색상 적용 if (data["구분"] === 0 || data["용수명"] === 0) { return ( - <TableCell key={head.id + headIndex} className="bg-[#d5e1eb] font-bold text-right"> - {Number(data[head.id]) ? formatCommaNumber(data[head.id as keyof any]) : data[head.id as keyof any] ?? ""} + <TableCell + key={head.id + headIndex} + className="bg-[#d5e1eb] font-bold text-right" + > + {Number(data[head.id]) + ? formatCommaNumber(data[head.id as keyof any]) + : (data[head.id as keyof any] ?? "")} </TableCell> ); } @@ -328,21 +403,35 @@ export const InputTableBody = ( const rowSpan = getRowSpan(chgRes, head.id, dataIndex); if (rowSpan === 0) return null; return ( - <TableCell key={head.id + headIndex} rowSpan={rowSpan} className={`w-36 align-middle text-center whitespace-nowrap ${bgClass}`}> - {Number(data[head.id]) ? formatCommaNumber(data[head.id as keyof any]) : data[head.id as keyof any] ?? ""} + <TableCell + key={head.id + headIndex} + rowSpan={rowSpan} + className={`w-36 align-middle text-center whitespace-nowrap ${bgClass}`} + > + {Number(data[head.id]) + ? formatCommaNumber(data[head.id as keyof any]) + : (data[head.id as keyof any] ?? "")} </TableCell> ); } // 기본 셀 // return head.edit === true && data.edit !== false ? ( return isEditable && head.edit === true && data.edit !== false ? ( - <TableCell key={head.id + headIndex} className={`w-36 ${bgClass}`}> + <TableCell + key={head.id + headIndex} + className={`w-36 ${bgClass}`} + > <Input // className={`${head.id !== "utztnUsgCd" ? "text-right" : "text-center"} !text-xl h-10 w-full`} className={`${textAlignClass} !text-xl h-10 w-full`} onChange={(e) => - handleCommaNumberChange(e, val => - handleChange(dataIndex, head.id, String(val), setInputData) + handleCommaNumberChange(e, (val) => + handleChange( + dataIndex, + head.id, + String(val), + setInputData + ) ) } value={formatCommaNumber(data[head.id as keyof any]) ?? ""} @@ -350,15 +439,20 @@ export const InputTableBody = ( /> </TableCell> ) : ( - <TableCell key={head.id + headIndex} className={`w-36 ${bgClass}`}> + <TableCell + key={head.id + headIndex} + className={`w-36 ${bgClass}`} + > <div // className={` - // ${head.id !== "utztnUsgCd" ? "text-right" : "text-center"} - // !text-xl w-full h-10 + // ${head.id !== "utztnUsgCd" ? "text-right" : "text-center"} + // !text-xl w-full h-10 // `} className={`${textAlignClass} !text-xl w-full h-10`} > - {Number(data[head.id]) ? formatCommaNumber(data[head.id as keyof any]) : data[head.id as keyof any] ?? ""} + {Number(data[head.id]) + ? formatCommaNumber(data[head.id as keyof any]) + : (data[head.id as keyof any] ?? "")} </div> </TableCell> ); @@ -368,11 +462,14 @@ export const InputTableBody = ( })} {chgRes.length === 0 && ( <TableRow> - <TableCell colSpan={headList.length} className="h-24 text-center text-gray-500"> + <TableCell + colSpan={headList.length} + className="h-24 text-center text-gray-500" + > 조회된 결과가 없습니다. </TableCell> </TableRow> )} </> ); -}; \ No newline at end of file +}; diff --git a/apps/wr/components/lib/func/commom.tsx b/apps/wr/components/lib/func/commom.tsx index fef689f46..0733f1bee 100644 --- a/apps/wr/components/lib/func/commom.tsx +++ b/apps/wr/components/lib/func/commom.tsx @@ -7,6 +7,7 @@ import { type SheetParameter, } from "@common/business_components/ui"; import { Button } from "@common/components/ui"; +import moment from "moment"; import type { RefObject } from "react"; import toast from "react-hot-toast"; @@ -150,6 +151,15 @@ export const formatCommaNumber = ( return num.toLocaleString(); }; +//내가 쓰는 값이 숫자이냐 아니냐 +export const isNumeric =(value: string) => { + if(value&&value.length>0){ + return value?.trim() !== "" && !isNaN(Number(value)); + }else{ + return false + } +} + export const getCityOrSgg = (ref: RefObject<ReturnType>) => { let con; if (ref.current != null) @@ -163,6 +173,7 @@ export const getCityOrSgg = (ref: RefObject<ReturnType>) => { export const safeDate = (value: any): Date | undefined => { const date = new Date(value); + console.log(' date ', date) return isNaN(date.getTime()) ? undefined : date; }; @@ -253,3 +264,13 @@ export const showConfirmToast = (message: string) => { { duration: Infinity } ); // 수동 닫기 }; + +// yyyy-mm-dd 형태로 포맷 +export const formatYyyymmdd = (val: string | undefined) => { + if (!val) return ""; + if (/^\d{8}$/.test(val)) { + return moment(val, "YYYYMMDD").format("YYYY-MM-DD"); + } + const m = moment(val); + return m.isValid() ? m.format("YYYY-MM-DD") : val; +}; \ No newline at end of file diff --git a/apps/wr/components/lib/func/mapUtil.ts b/apps/wr/components/lib/func/mapUtil.ts new file mode 100644 index 000000000..f3925c413 --- /dev/null +++ b/apps/wr/components/lib/func/mapUtil.ts @@ -0,0 +1,21 @@ + + + + export const getFeatureCd = (featurePropertise:any, layerNm:string) => { + console.log('layerNm ', layerNm ) + console.log('featurePropertise ', featurePropertise ) + + const featureCd = + layerNm === "하수재이용시설" + ? featurePropertise?.swtrmt_fclt_cd + : ["빗물이용시설", "중수도시설", "온배수시설", "수요처" ,'잠재수요처'].includes( + layerNm + ) + ? featurePropertise?.bdst_nm + : layerNm === "하수처리시설" + ? featurePropertise?.swtrmt_fclt_nm + : null; + + console.log('featureCd ', featureCd) + return featureCd; + }; \ No newline at end of file diff --git a/apps/wr/components/ui/form/fcltReg/pbnstReg/buseReg/bugRegForm.tsx b/apps/wr/components/ui/form/fcltReg/pbnstReg/buseReg/bugRegForm.tsx index 0fb24366a..e3eaf4365 100644 --- a/apps/wr/components/ui/form/fcltReg/pbnstReg/buseReg/bugRegForm.tsx +++ b/apps/wr/components/ui/form/fcltReg/pbnstReg/buseReg/bugRegForm.tsx @@ -24,10 +24,11 @@ import { SelectBox } from "@common/business_components"; import { DatePicker, FormUi, UiModal } from "@common/business_components/ui"; import { Form, FormField } from "@common/business_components/ui/form"; import { Button, Input, Label } from "@common/components"; -import { useEffect, useRef, useState } from "react"; +import { useCallback, useEffect, useRef, useState } from "react"; import { useFieldArray, type UseFormReturn } from "react-hook-form"; import MngRegFormDetail from "../mngReg/mngRegFormDetail/mngRegFormDetail"; import { + //confirmToast, formatCommaNumber, handleCommaNumberChange, safeDate, @@ -38,6 +39,9 @@ import type { DemandCompType } from "@common/business_components/ui/sdmInsertFor import RuseMngTableForm from "@/components/ui/table/pbnstReg/ruseMngTableForm/ruseMngTableForm"; import type { SelectDataType } from "@common/business_components"; import { BuseWaterQulity } from "./buseWaterQulity/buseWaterQulity"; +import toast from "react-hot-toast"; +import { useTranslation } from "react-i18next"; +//import { useTranslation } from "react-i18next"; export function PbnstBuseForm({ indexMng = 0, @@ -54,6 +58,8 @@ export function PbnstBuseForm({ setIndexBiz: (index: number) => void; readonly?: boolean; }) { + const { t } = useTranslation("base"); + const { code, pbnst, @@ -157,22 +163,25 @@ export function PbnstBuseForm({ } }, [fcBizs, indexBiz, setIndexBiz]); // 2. Define a submit handler. - function addRuseFcltObj() { + const addRuseFcltObj = useCallback(async () => { + const formRuse = form.getValues(`items.${indexMng}`); const formBizs = form.getValues(`items.${indexMng}.fcBizs`); const pbnstBizCycle = formBizs.length; - // 🧩 새로운 fcBizs 기본 객체 생성 (필수값만 채우고 나머지는 default로) - const newItem = fcBizSchema.parse({ - cycl: pbnstBizCycle + 1, - pbnstRuseFcltId: formRuse.pbnstRuseFcltId, - fcOrgwts: undefined, - pplInstls: undefined, - fcDemands: undefined, - }); - appendBiz(newItem); - setIndexBiz(pbnstBizCycle); // 새로 추가된 차수로 포커스 이동 - } + const newItem = fcBizSchema.parse({ + cycl: pbnstBizCycle + 1, + pbnstRuseFcltId: formRuse.pbnstRuseFcltId, + fcOrgwts: [], + + pplInstls: [], + fcDemands: [], + }); + await appendBiz(newItem); + toast.success(t('insertRuseBiz',{cycle: pbnstBizCycle + 1})) + setIndexBiz(pbnstBizCycle); + }, [form, indexMng, indexBiz, appendBiz, setIndexBiz]); + const { fields: fcDemands, append: appendDemand } = useFieldArray({ control: form.control, name: `items.${indexMng}.fcBizs.${indexBiz}.fcDemands`, @@ -206,7 +215,7 @@ export function PbnstBuseForm({ } }, [ppiYn, indexMng, indexBiz]); - const plusHist = () => { + const plusHist = useCallback(() => { if ( form.getValues(`items.${indexMng}.fcBizs.${indexBiz}.pbnstRuseFcltBizSn`) ) { @@ -227,9 +236,9 @@ export function PbnstBuseForm({ cpct: 0, }); } - }; + }, [indexMng, indexBiz, appendPpl, form]); - const plusArray = () => { + const plusArray = useCallback(() => { appendDemand({ dmdCoNm: "", daddr: "", @@ -248,7 +257,7 @@ export function PbnstBuseForm({ dmdInfoId: "", //telno: "", }); - }; + }, [indexMng, indexBiz, appendDemand, form]); const removeSelected = () => { const nowPPl = form.getValues( @@ -293,7 +302,7 @@ export function PbnstBuseForm({ }, [form.formState, indexMng, indexBiz]); useEffect(() => { - console.log("form.formState ", form.formState); + //console.log("form.formState ", form.formState); }, [form.formState]); const pbnstBiz = form.watch(`items.${indexMng}.fcBizs`); @@ -489,17 +498,16 @@ export function PbnstBuseForm({ </div> <div> - - <BuseWaterQulity - h1Title={`하수처리수 재이용시설 보증수질 `} - subTitle="※재처리시설이 있는 경우만 작성" - form={form} - moClass="grid grid-cols-12 gap-4" - sonClass="col-span-4" - indexMng={indexMng} - indexBiz={indexBiz} - readonly={readonly} - /> + <BuseWaterQulity + h1Title={`하수처리수 재이용시설 보증수질 `} + subTitle="※재처리시설이 있는 경우만 작성" + form={form} + moClass="grid grid-cols-12 gap-4" + sonClass="col-span-4" + indexMng={indexMng} + indexBiz={indexBiz} + readonly={readonly} + /> </div> <div key={"hasu"}> @@ -533,7 +541,10 @@ export function PbnstBuseForm({ 수요처 신규등록 </Button> </div> - <div ref={demandsContainerRef}> + <div + key={`fcDemands-${indexMng}-${indexBiz}`} + ref={demandsContainerRef} + > {fcDemands.map((field, index) => ( <div key={field.id} // useFieldArray의 고유 id를 key로 사용 diff --git a/apps/wr/components/ui/form/fcltReg/pbnstReg/buseReg/buseWaterQulity/buseWaterQulity.tsx b/apps/wr/components/ui/form/fcltReg/pbnstReg/buseReg/buseWaterQulity/buseWaterQulity.tsx index 121957b8e..673cd5710 100644 --- a/apps/wr/components/ui/form/fcltReg/pbnstReg/buseReg/buseWaterQulity/buseWaterQulity.tsx +++ b/apps/wr/components/ui/form/fcltReg/pbnstReg/buseReg/buseWaterQulity/buseWaterQulity.tsx @@ -39,7 +39,6 @@ export function BuseWaterQulity({ form.getValues(`items.${indexMng}.pbnstRuseFcltId`) + String(form.getValues(`items.${indexMng}.fcBizs.${indexBiz}.cycl`)); - console.log("checkPkAndCycle ", checkPkAndCycle); if (chekcPk !== checkPkAndCycle) { if (watchPH && watchPH !== undefined) { const [frontPH, backPH] = watchPH.split("~"); diff --git a/apps/wr/components/ui/map/LayerPopUp/CoSttnMap/CoSttnMap.tsx b/apps/wr/components/ui/map/LayerPopUp/CoSttnMap/CoSttnMap.tsx new file mode 100644 index 000000000..13e928506 --- /dev/null +++ b/apps/wr/components/ui/map/LayerPopUp/CoSttnMap/CoSttnMap.tsx @@ -0,0 +1,195 @@ +import { useWrStore } from "@/app/store"; +import { costtnMapSize } from "@/app/store/map/coSttnMap"; +import { + formatPhoneNumber, + getCity, + tableData, +} from "@/components/lib/func/commom"; +import type { objSearchType } from "@/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal"; +import { + ContentsDiv, + SearchDiv, + SearchFormLeft, + SearchFormRight, + SearchInput, + SelectBox, + Title, + UiTable, + type SelectDataType, +} from "@common/business_components/ui"; +import type { DemandCompType } from "@common/business_components/ui/sdmInsertForm/form"; +import { Button } from "@common/components/ui"; +import moment from "moment"; +import { useCallback, useEffect, useState } from "react"; + +export function CoSttnMap({ + rtnFeature, +}: { + rtnFeature: (feature: any) => void; +}) { + const { + code, + coSttnMap, + coSttnMapInit, + coSttnMapSearch, + coSttnMapTxtSearch, + } = useWrStore((state) => state); + const [coSttnMapTable, setCoSttnMapTable] = useState<DemandCompType[]>(); + const [isReset, setIsReset] = useState(false); + const { coSttMapHeadList, coSttnMapRes, coSttnMapResCount } = coSttnMap; + const [selectSgg, setSelectSgg] = useState<SelectDataType[] | []>(); + const [objSearch, setObjSearch] = useState<objSearchType>({ + sgg: "", + city: "", + }); + + const [seartchTxt, setSearchTxt] = useState<string>(); + + const getSelCity = useCallback( + (val: string | number) => { + const cityValue = String(val); + const data = getCity(cityValue, code.sggCd); + setSelectSgg(data); + setObjSearch({ city: cityValue, sgg: "0" }); + }, + [code.sggCd] + ); + + const getSgg = useCallback((val: string | number) => { + setObjSearch((prev) => ({ ...prev, sgg: String(val) })); + }, []); + + useEffect(() => { + // code.cityCd 데이터가 로드되었고, 아직 시/도 선택이 안된 초기 상태일 때 실행 + if (code.cityCd && code.cityCd.length > 0 && !objSearch.city) { + const firstCityVal = code.cityCd[0]?.val; + if (firstCityVal) { + // 첫 번째 시/도 값으로 상태를 초기화합니다. + getSelCity(firstCityVal); + } + } + }, [code.cityCd, objSearch.city, getSelCity]); + + useEffect(() => { + if (!coSttnMap.isInit) { + coSttnMapInit(1, costtnMapSize, "0"); + } + }, [coSttnMap.isInit, coSttnMapInit]); + + const coSttnMapCellClick = (index: number | undefined) => { + if (index !== undefined && coSttnMapTable !== undefined) { + rtnFeature(coSttnMapTable[index]); + } + }; + useEffect(() => { + if (coSttnMapRes) { + if (coSttnMapRes?.length > 0 && code.cityCd.length > 0) { + const data = tableData(coSttnMapRes, { 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); + setCoSttnMapTable(chData); + } + } + }, [coSttnMapRes, code]); + return ( + <> + <ContentsDiv> + <div className="grid grid-cols-4 gap-4 "> + <div className="col-span-4"> + <Title title={"수요처 관리"} /> + <SearchDiv> + <SearchFormLeft> + <div className="col-span-10 flex flex-row"> + <SelectBox + className="col-span-3 w-1/6 h-full text-2xl" + label="시도" + selectClass={""} + selectArray={code.cityCd || []} + onSelectValue={(val) => { + getSelCity(val); + }} + value={objSearch.city} + /> + <SelectBox + className="col-span-3 w-1/6 h-full text-2xl ml-4" + label="시군구" + selectClass={""} + selectArray={selectSgg || []} + onSelectValue={getSgg} + value={objSearch.sgg} + /> + <SearchInput + className={"!text-3xl ml-4"} + label="회사명" + textValue={seartchTxt ?? ""} + setText={(e) => { + setSearchTxt(e); + }} + /> + </div> + </SearchFormLeft> + <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={() => { + const con = + objSearch.sgg && objSearch.sgg !== "0" + ? objSearch.sgg + : objSearch.city; + if (con) { + if (seartchTxt && seartchTxt?.length > 0) { + coSttnMapTxtSearch( + 1, + costtnMapSize, + String(con), + seartchTxt ?? "" + ); + } else { + coSttnMapSearch(1, costtnMapSize, String(con)); + } + setIsReset(true) + } + }} + > + 조회 + </Button> + </div> + </SearchFormRight> + </SearchDiv> + </div> + </div> + <div className="grid grid-cols-2 gap-4 "> + <div className="col-span-2"> + <UiTable + headName={[]} + tableData={coSttnMapTable || []} + headlist={coSttMapHeadList} + pageSize={20} + total={coSttnMapResCount|| 0} + cellClick={(index: number | undefined) => { + coSttnMapCellClick(index); + // setUpdateData(coSttnRes[index as number]); + }} + onPageChange={(page) => { + const con = + objSearch.sgg && objSearch.sgg !== "0" + ? objSearch.sgg + : objSearch.city; + coSttnMapSearch(page, costtnMapSize, String(con)); + setIsReset(false); + }} + resetPageNo={isReset} + ></UiTable> + </div> + </div> + </ContentsDiv> + </> + ); +} diff --git a/apps/wr/components/ui/map/LayerPopUp/HasuFacilty/HasuFacility.tsx b/apps/wr/components/ui/map/LayerPopUp/HasuFacilty/HasuFacility.tsx new file mode 100644 index 000000000..4f9d052de --- /dev/null +++ b/apps/wr/components/ui/map/LayerPopUp/HasuFacilty/HasuFacility.tsx @@ -0,0 +1,109 @@ +import { useWrStore } from "@/app/store"; +import { btnClass } from "@/app/store/common/code"; +import { pbnMapTableSize } from "@/app/store/map/pbnstMap"; +import type { pubHasuType } from "@/app/store/ruseFcltMng/fcltReg/pbnstReg/type"; +import { tableData } from "@/components/lib/func/commom"; +import { + ContentsDiv, + SearchBaseForm, + SearchInput, + Title, + UiTable, + type ReturnType, +} from "@common/business_components/ui"; +import { Label } from "@common/components/ui"; +import { useEffect, useRef, useState } from "react"; + +export function HasuFacility({rtnFeature}:{rtnFeature:(feature:any)=>void } ) { + const { code, pbnMap, pbnMapInit } = useWrStore((state) => state); + const [hasuTable, setHasuTable] = useState<pubHasuType[]>(); + const [isReset, setIsReset] = useState(false); + const [pbnRuseNm, setPbnRuseNm] = useState<string>(""); + const ref = useRef<ReturnType>({} as ReturnType); + const { pbnMapHeadList, pbnMapRes, pbnMapCount } = pbnMap; + + useEffect(() => { + if (!pbnMap.isInit) { + pbnMapInit(1, pbnMapTableSize, ref.current); + } + }, [pbnMap.isInit, pbnMapInit]); + + useEffect(() => { + if (code.sggCd.length && pbnMapRes.length > 0) { + setHasuTable(tableData(pbnMapRes, { code })); + } + }, [code, pbnMapRes]); + + const hasuCellClick = (index: number | undefined) => { + if (index !== undefined && pbnMapRes !== undefined) { + rtnFeature(pbnMapRes[index]) + } + }; + return ( + <> + <ContentsDiv> + <div className="grid grid-cols-2 gap-4 "> + <div className="col-span-2"> + <Title title="하수처리시설 현황" /> + <SearchBaseForm + selectSidoData={code.cityCd} + selectSigunData={code.sggCd} + selectRegionData={code.rvbsnCd} + selectUnitWaterShedData={code.lgbsnCd} + selectMiddleRegionData={code.mdmbsnCd} + selectEnvPartData={code.lclgvrnmntCd} + buttonOrder={["query", "init", "excel"]} + rightClassName="ml-auto mr-6 flex items-center space-x-1" + leftClassName="flex gap-5 w-9/12 m-4" + leftSelectClassName="w-1/4" + rightChildren={<></>} + leftChildren={ + <div className="col-span-3 flex items-center"> + <Label className="w-full mt-2 ml-4 text-xl text-gray-600"> + {"하수처리시설명"} + </Label> + <SearchInput + className={"text-2xl w-full"} + label="" + textValue={pbnRuseNm || ""} + setText={(e) => { + setPbnRuseNm(e || ""); + }} + /> + </div> + } + queryClick={() => { + setIsReset(true); + }} + initClick={function (): void { + setPbnRuseNm(""); + }} + btnClass={btnClass} + ref={ref} + isAdmin={false} + excelClick={function (returnType: ReturnType): void { + console.log("pbnstSttnSearch ", returnType); + }} + /> + </div> + <div className="col-span-6"> + {/* 높이 조정 */} + <UiTable + tableData={hasuTable || []} + headlist={pbnMapHeadList} + pageSize={pbnMapTableSize} + total={pbnMapCount || 0} + headName={""} + cellClick={hasuCellClick} + onPageChange={(page) => { + pbnMapInit(page, pbnMapTableSize, ref.current); + setIsReset(false); + }} + resetPageNo={isReset} + /> + </div> + </div> + </ContentsDiv> + </> + ); +} diff --git a/apps/wr/components/ui/map/LayerPopUp/HedfFacilty/HtdfFacilty.tsx b/apps/wr/components/ui/map/LayerPopUp/HedfFacilty/HtdfFacilty.tsx new file mode 100644 index 000000000..05589e38a --- /dev/null +++ b/apps/wr/components/ui/map/LayerPopUp/HedfFacilty/HtdfFacilty.tsx @@ -0,0 +1,181 @@ +import { useWrStore } from "@/app/store"; +import { rainMapSize } from "@/app/store/map/rainMap"; +import type { customHtdflBdst } from "@/app/store/ruseFcltMng/fcltReg/htdflReg"; +import { getCity, tableData } from "@/components/lib/func/commom"; +import type { objSearchType } from "@/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal"; +import { + ContentsDiv, + SearchDiv, + SearchFormLeft, + SearchFormRight, + SearchInput, + SelectBox, + Title, + UiTable, + type SelectDataType, +} from "@common/business_components/ui"; +import { HeadMakeColSpan } from "@common/business_components/ui/table/HeadMakeColSpan"; +import { Button } from "@common/components/ui"; +import { useCallback, useEffect, useState } from "react"; + +export function HtdflFacility({ + rtnFeature, +}: { + rtnFeature: (feature: any) => void; +}) { + const { code, htdfMap, htdfMapInit,htdfMapSearch } = useWrStore((state) => state); + const { htdfMapCount, htdfMapRes, htdfSttHeadList, htdfSttNodeList } = + htdfMap; + const [htdflTable, setHtdflTable] = useState<customHtdflBdst[]>(); + const [isReset, setIsReset] = useState(false); + const [selectSgg, setSelectSgg] = useState<SelectDataType[] | []>(); + const [objSearch, setObjSearch] = useState<objSearchType>({ + sgg: "", + city: "", + }); + + const [seartchTxt, setSearchTxt] = useState<string>(); + + const getSelCity = useCallback( + (val: string | number) => { + const cityValue = String(val); + const data = getCity(cityValue, code.sggCd); + setSelectSgg(data); + setObjSearch({ city: cityValue, sgg: "0" }); + }, + [code.sggCd] + ); + + const getSgg = useCallback((val: string | number) => { + setObjSearch((prev) => ({ ...prev, sgg: String(val) })); + }, []); + + useEffect(() => { + // code.cityCd 데이터가 로드되었고, 아직 시/도 선택이 안된 초기 상태일 때 실행 + if (code.cityCd && code.cityCd.length > 0 && !objSearch.city) { + const firstCityVal = code.cityCd[0]?.val; + if (firstCityVal) { + // 첫 번째 시/도 값으로 상태를 초기화합니다. + getSelCity(firstCityVal); + } + } + }, [code.cityCd, objSearch.city, getSelCity]); + + const htdflCellClick = (index: number | undefined) => { + if (index !== undefined && htdflTable !== undefined) { + rtnFeature(htdflTable[index]); + } + }; + + useEffect(() => { + if (!htdfMap.isInit) { + htdfMapInit(1, rainMapSize, "0"); + } + }, [htdfMap.isInit, htdfMapInit]); + + useEffect(() => { + const data = tableData(htdfMapRes, { 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, htdfMapRes]); + + return ( + <> + <ContentsDiv> + <div className="grid grid-cols-4 gap-4 "> + <div className="col-span-4"> + <Title title="온배수시설 현황" /> + <SearchDiv> + <SearchFormLeft> + <div className="col-span-10 flex flex-row"> + <SelectBox + className="col-span-3 w-1/6 h-full text-2xl" + label="시도" + selectClass={""} + selectArray={code.cityCd || []} + onSelectValue={(val) => { + getSelCity(val); + }} + value={objSearch.city} + /> + <SelectBox + className="col-span-3 w-1/6 h-full text-2xl ml-4" + label="시군구" + selectClass={""} + selectArray={selectSgg || []} + onSelectValue={getSgg} + value={objSearch.sgg} + /> + <SearchInput + className={"!text-3xl ml-4"} + label="회사명" + textValue={seartchTxt ?? ""} + setText={(e) => { + setSearchTxt(e); + }} + /> + </div> + </SearchFormLeft> + <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={() => { + setIsReset(true); + }} + > + 조회 + </Button> + </div> + </SearchFormRight> + </SearchDiv> + </div> + </div> + <div className="grid grid-cols-2 gap-4 "> + <div className="col-span-2"> + <UiTable + headName={[]} + tableData={htdflTable || []} + headlist={htdfSttHeadList} + pageSize={rainMapSize} + total={htdfMapCount} + cellClick={(index: number | undefined) => { + htdflCellClick(index ?? undefined); + }} + onPageChange={(page) => { + const con = + objSearch.sgg && objSearch.sgg !== "0" + ? objSearch.sgg + : objSearch.city; + if (con) { + if (seartchTxt && seartchTxt?.length > 0) { + htdfMapSearch( + page, + rainMapSize, + String(con), + seartchTxt ?? "" + ); + } else { + htdfMapSearch(page, rainMapSize, String(con)); + } + setIsReset(false); + } + }} + resetPageNo={isReset} + > + <HeadMakeColSpan upHeadList={htdfSttNodeList} /> + </UiTable> + </div> + </div> + </ContentsDiv> + </> + ); +} diff --git a/apps/wr/components/ui/map/LayerPopUp/LdrmntCoMap/LdrmntCoMap.tsx b/apps/wr/components/ui/map/LayerPopUp/LdrmntCoMap/LdrmntCoMap.tsx new file mode 100644 index 000000000..56e42368f --- /dev/null +++ b/apps/wr/components/ui/map/LayerPopUp/LdrmntCoMap/LdrmntCoMap.tsx @@ -0,0 +1,189 @@ +import { useWrStore } from "@/app/store"; +import { ldrmntMapSize } from "@/app/store/map/ldrmntCoMap"; +import type { customLdrmnt } from "@/app/store/matDmd/ldrmntCoSttn"; +import { getCity, tableData } from "@/components/lib/func/commom"; +import type { objSearchType } from "@/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal"; +import { + ContentsDiv, + SearchDiv, + SearchFormLeft, + SearchFormRight, + SearchInput, + SelectBox, + Title, + UiTable, + type SelectDataType, +} from "@common/business_components/ui"; +import ListDiv from "@common/business_components/ui/list_div"; +import { Button } from "@common/components/ui"; +import { useEffect, useState } from "react"; + +export function LdrmntCoMap({ + rtnFeature, +}: { + rtnFeature: (feature: any) => void; +}) { + const { code, ldrmntMap, ldrmntMapInit, ldrmntMapSearch } = useWrStore( + (state) => state + ); + + const { ldrmntMapRes, ldrmntMapResCount, ldrmntMapHeadList } = ldrmntMap; + const [selectSgg, setSelectSgg] = useState<SelectDataType[] | []>(); + const [objSearch, setObjSearch] = useState<objSearchType>({ + sgg: "", + city: "", + }); + const [bdstTable, setBdstTable] = useState<customLdrmnt[]>(); + const [searchTxt, setSearchTxt] = useState<string>(); + const [resetPage, isResetPage] = useState(false); + + const computeCityNm = () => { + let cityTxt = ""; + let sggTxt = ""; + + if (objSearch.city) { + cityTxt = code.cityCd.find((c) => c.val === objSearch.city)?.text || ""; + } + if (objSearch.sgg) { + sggTxt = code.sggCd.find((s) => s.val === objSearch.sgg)?.text || ""; + } + + if (cityTxt === "" || cityTxt === "전체") cityTxt = "0"; + + return sggTxt && sggTxt !== "0" ? `${cityTxt} ${sggTxt}` : cityTxt; + }; + + const cellClick = (index: number | undefined) => { + if (index !== undefined) { + rtnFeature(ldrmntMapRes[index]); + } + }; + + const getSelCity = (val: string | number) => { + const cityValue = String(val); + + if (cityValue === "0") { + setSelectSgg([]); + setObjSearch({ city: cityValue, sgg: "" }); + } else { + const data = getCity(cityValue, code.sggCd); + setSelectSgg(data); + const firstSggVal = + data.find((item) => item.val === "0")?.val || (data[0]?.val ?? ""); + setObjSearch({ city: cityValue, sgg: firstSggVal }); + } + }; + + const getSgg = (val: string | number) => { + setObjSearch({ ...objSearch, sgg: String(val) }); + }; + + useEffect(() => { + if (!ldrmntMap.isInit) { + ldrmntMapInit(1, ldrmntMapSize, "0"); + } + }, [ldrmntMap.isInit, ldrmntMapInit]); + + useEffect(() => { + const transData = Array.isArray(ldrmntMapRes) + ? ldrmntMapRes + : [ldrmntMapRes]; + const data = tableData(transData, { code }); + const tbData = Array.isArray(data) + ? data.map((item: customLdrmnt) => ({ + ...item, + ctpvCd: item.fctryAddr?.split(" ")[0] || "", + sggCd: item.fctryAddr?.split(" ")[1] || "", + })) + : []; + + setBdstTable(tbData); + }, [ldrmntMapRes, code]); + + return ( + <> + <ContentsDiv> + <div className="grid grid-cols-4 gap-4 "> + <div className="col-span-4"> + <Title title={"잠재수요처 관리"} /> + <SearchDiv> + <SearchFormLeft> + <div className="col-span-10 flex flex-row"> + <SelectBox + className="col-span-2 w-1/6 h-full text-2xl" + label="시도" + selectClass={""} + selectArray={code.cityCd} + onSelectValue={getSelCity} + /> + <SelectBox + className="col-span-2 w-1/6 h-full text-2xl ml-4" + label="시군구" + selectClass={""} + selectArray={selectSgg || []} + value={objSearch.sgg} // value props 추가 + onSelectValue={getSgg} + /> + <SearchInput + className={"text-2xl ml-4"} + label="회사명" + textValue={searchTxt || ""} + setText={(e) => { + setSearchTxt(e); + }} + /> + </div> + </SearchFormLeft> + <SearchFormRight className="w-2/12 mx-5 flex flex-col items-center justify-center space-x-2 "> + <div className="flex flex-row space-x-2 items-center"> + <Button + size={"sm"} + onClick={() => { + ldrmntMapSearch( + 1, + ldrmntMapSize, + computeCityNm(), + searchTxt || "" + ); + isResetPage(true); + }} + > + 조회 + </Button> + </div> + </SearchFormRight> + </SearchDiv> + </div> + </div> + <div className="grid grid-cols-6 gap-4 "> + <div className="col-span-6"> + <div className="overflow-x-auto"> + <ListDiv className="min-w-max"> + <UiTable + headName={undefined} + tableData={bdstTable || []} + headlist={ldrmntMapHeadList} + pageSize={20} + total={ldrmntMapResCount || 0} + cellClick={(index: number | undefined) => { + cellClick(index ?? undefined); + }} + onPageChange={(page) => { + ldrmntMapSearch( + page, + ldrmntMapSize, + computeCityNm(), + searchTxt || "" + ); + isResetPage(false); + }} + resetPageNo={resetPage} + /> + </ListDiv> + </div> + </div> + </div> + </ContentsDiv> + </> + ); +} diff --git a/apps/wr/app/map/components/layerPopup/reuseMngFacility.tsx b/apps/wr/components/ui/map/LayerPopUp/PbnFacility/reuseMngFacility.tsx similarity index 100% rename from apps/wr/app/map/components/layerPopup/reuseMngFacility.tsx rename to apps/wr/components/ui/map/LayerPopUp/PbnFacility/reuseMngFacility.tsx diff --git a/apps/wr/components/ui/map/LayerPopUp/RainFacility/RainFacility.tsx b/apps/wr/components/ui/map/LayerPopUp/RainFacility/RainFacility.tsx new file mode 100644 index 000000000..e1a917e8f --- /dev/null +++ b/apps/wr/components/ui/map/LayerPopUp/RainFacility/RainFacility.tsx @@ -0,0 +1,212 @@ +import { useWrStore } from "@/app/store"; +import { rainMapSize } from "@/app/store/map/rainMap"; +import type { customRainBdst } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; +import { getCity, tableData } from "@/components/lib/func/commom"; +import type { objSearchType } from "@/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal"; +import { + ContentsDiv, + SearchDiv, + SearchFormLeft, + SearchFormRight, + SearchInput, + SelectBox, + Title, + UiTable, + type SelectDataType, +} from "@common/business_components/ui"; +import { HeadMakeColSpan } from "@common/business_components/ui/table/HeadMakeColSpan"; +import { Button } from "@common/components/ui"; +import { useCallback, useEffect, useState } from "react"; + +export function RainFacility({ + rtnFeature, +}: { + rtnFeature: (feature: any) => void; +}) { + const { code, rainMap, rainMapInit, rainMapSearch } = useWrStore( + (state) => state + ); + const { rainMapRes, rainMapCount, rainSttHeadList, rainSttNodeList } = + rainMap; + const [rainTable, setRainTable] = useState<customRainBdst[]>(); + const [isReset, setIsReset] = useState(false); + const [selectSgg, setSelectSgg] = useState<SelectDataType[] | []>(); + const [objSearch, setObjSearch] = useState<objSearchType>({ + sgg: "", + city: "", + }); + + const [seartchTxt, setSearchTxt] = useState<string>(); + + const getSelCity = useCallback( + (val: string | number) => { + const cityValue = String(val); + const data = getCity(cityValue, code.sggCd); + setSelectSgg(data); + setObjSearch({ city: cityValue, sgg: "0" }); + }, + [code.sggCd] + ); + + const getSgg = useCallback((val: string | number) => { + setObjSearch((prev) => ({ ...prev, sgg: String(val) })); + }, []); + + useEffect(() => { + // code.cityCd 데이터가 로드되었고, 아직 시/도 선택이 안된 초기 상태일 때 실행 + if (code.cityCd && code.cityCd.length > 0 && !objSearch.city) { + const firstCityVal = code.cityCd[0]?.val; + if (firstCityVal) { + // 첫 번째 시/도 값으로 상태를 초기화합니다. + getSelCity(firstCityVal); + } + } + }, [code.cityCd, objSearch.city, getSelCity]); + + useEffect(() => { + if (!rainMap.isInit) { + console.log(" init "); + rainMapInit(1, rainMapSize, "0"); + } + }, [rainMapInit, rainMap.isInit]); + + const rainCellClick = (index: number | undefined, headId: string) => { + if ( + index !== undefined && + rainTable !== undefined && + headId !== "chBuildingYn" + ) { + rtnFeature(rainTable[index]); + } + }; + + useEffect(() => { + const data = tableData(rainMapRes, { 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, rainMapRes]); + + return ( + <> + <ContentsDiv> + <div className="grid grid-cols-4 gap-4 "> + <div className="col-span-4"> + <Title title="빗물이용시설 현황" /> + <SearchDiv> + <SearchFormLeft> + <div className="col-span-10 flex flex-row"> + <SelectBox + className="col-span-3 w-1/6 h-full text-2xl" + label="시도" + selectClass={""} + selectArray={code.cityCd || []} + onSelectValue={(val) => { + getSelCity(val); + }} + value={objSearch.city} + /> + <SelectBox + className="col-span-3 w-1/6 h-full text-2xl ml-4" + label="시군구" + selectClass={""} + selectArray={selectSgg || []} + onSelectValue={getSgg} + value={objSearch.sgg} + /> + <SearchInput + className={"!text-3xl ml-4"} + label="회사명" + textValue={seartchTxt ?? ""} + setText={(e) => { + setSearchTxt(e); + }} + /> + </div> + </SearchFormLeft> + <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={() => { + setIsReset(true); + }} + > + 조회 + </Button> + </div> + </SearchFormRight> + </SearchDiv> + </div> + </div> + <div className="grid grid-cols-2 gap-4 "> + <div className="col-span-2"> + <UiTable + headName={[]} + tableData={rainTable || []} + headlist={rainSttHeadList} + pageSize={20} + total={rainMapCount} + cellClick={(index: number | undefined, headId: string) => { + rainCellClick(index ?? undefined, headId); + }} + onPageChange={(page) => { + const con = + objSearch.sgg && objSearch.sgg !== "0" + ? objSearch.sgg + : objSearch.city; + if (con) { + if (seartchTxt && seartchTxt?.length > 0) { + rainMapSearch( + page, + rainMapSize, + String(con), + seartchTxt ?? "" + ); + } else { + rainMapSearch(page, rainMapSize, String(con)); + } + setIsReset(false); + } + }} + resetPageNo={isReset} + > + <HeadMakeColSpan upHeadList={rainSttNodeList} /> + </UiTable> + </div> + </div> + </ContentsDiv> + </> + ); +} diff --git a/apps/wr/components/ui/map/LayerPopUp/RswtFacilty/RswtFacilty.tsx b/apps/wr/components/ui/map/LayerPopUp/RswtFacilty/RswtFacilty.tsx new file mode 100644 index 000000000..18a2b2c1d --- /dev/null +++ b/apps/wr/components/ui/map/LayerPopUp/RswtFacilty/RswtFacilty.tsx @@ -0,0 +1,194 @@ +import { useWrStore } from "@/app/store"; +import { rainMapSize } from "@/app/store/map/rainMap"; +import type { customRswtBdst } from "@/app/store/ruseFcltMng/fcltReg/rswtReg"; +import { getCity, tableData } from "@/components/lib/func/commom"; +import type { objSearchType } from "@/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal"; +import { ContentsDiv, SearchDiv, SearchFormLeft, SearchFormRight, SearchInput, SelectBox, Title, UiTable, type SelectDataType } from "@common/business_components/ui"; +import { HeadMakeColSpan } from "@common/business_components/ui/table/HeadMakeColSpan"; +import { Button } from "@common/components/ui"; +import { useCallback, useEffect, useState } from "react"; + +export function RswtFacility({ + rtnFeature, +}: { + rtnFeature: (feature: any) => void; +}) { + const { code, rswtMap, rswtMapInit,rswtMapSearch } = useWrStore((state) => state); + const { rswtMapRes, rswtMapCount, rswtSttHeadList, rswtSttNodeList } = + rswtMap; + const [rswtrTable, setRswtrTable] = useState<customRswtBdst[]>(); + const [isReset, setIsReset] = useState(false); + + + const [selectSgg, setSelectSgg] = useState<SelectDataType[] | []>(); + const [objSearch, setObjSearch] = useState<objSearchType>({ + sgg: "", + city: "", + }); + + const [seartchTxt, setSearchTxt] = useState<string>(); + + const getSelCity = useCallback( + (val: string | number) => { + const cityValue = String(val); + const data = getCity(cityValue, code.sggCd); + setSelectSgg(data); + setObjSearch({ city: cityValue, sgg: "0" }); + }, + [code.sggCd] + ); + + const getSgg = useCallback((val: string | number) => { + setObjSearch((prev) => ({ ...prev, sgg: String(val) })); + }, []); + + useEffect(() => { + // code.cityCd 데이터가 로드되었고, 아직 시/도 선택이 안된 초기 상태일 때 실행 + if (code.cityCd && code.cityCd.length > 0 && !objSearch.city) { + const firstCityVal = code.cityCd[0]?.val; + if (firstCityVal) { + // 첫 번째 시/도 값으로 상태를 초기화합니다. + getSelCity(firstCityVal); + } + } + }, [code.cityCd, objSearch.city, getSelCity]); + const rswtCellClick = (index: number | undefined, headId: string) => { + if ( + headId !== "chBuildingYn" && + index !== undefined && + rswtrTable !== undefined + ) { + rtnFeature(rswtrTable[index]); + } + }; + + useEffect(() => { + if (!rswtMap.isInit) { + rswtMapInit(1, rainMapSize, "0"); + } + }, [rswtMapInit, rswtMap.isInit]); + + useEffect(() => { + const data = tableData(rswtMapRes, { 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, rswtMapRes]); + return ( + <> + <ContentsDiv> + <div className="grid grid-cols-4 gap-4 "> + <div className="col-span-4"> + <Title title="중수도이용시설 현황" /> + <SearchDiv> + <SearchFormLeft> + <div className="col-span-10 flex flex-row"> + <SelectBox + className="col-span-3 w-1/6 h-full text-2xl" + label="시도" + selectClass={""} + selectArray={code.cityCd || []} + onSelectValue={(val) => { + getSelCity(val); + }} + value={objSearch.city} + /> + <SelectBox + className="col-span-3 w-1/6 h-full text-2xl ml-4" + label="시군구" + selectClass={""} + selectArray={selectSgg || []} + onSelectValue={getSgg} + value={objSearch.sgg} + /> + <SearchInput + className={"!text-3xl ml-4"} + label="회사명" + textValue={seartchTxt ?? ""} + setText={(e) => { + setSearchTxt(e); + }} + /> + </div> + </SearchFormLeft> + <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={() => { + setIsReset(true); + }} + > + 조회 + </Button> + </div> + </SearchFormRight> + </SearchDiv> + </div> + </div> + <div className="grid grid-cols-2 gap-4 "> + <div className="col-span-2"> + <UiTable + headName={[]} + tableData={rswtrTable || []} + headlist={rswtSttHeadList} + pageSize={20} + total={rswtMapCount} + cellClick={(index: number | undefined, headId: string) => { + rswtCellClick(index, headId); + }} + onPageChange={(page) => { + const con = + objSearch.sgg && objSearch.sgg !== "0" + ? objSearch.sgg + : objSearch.city; + if (con) { + if (seartchTxt && seartchTxt?.length > 0) { + rswtMapSearch( + page, + rainMapSize, + String(con), + seartchTxt ?? "" + ); + } else { + rswtMapSearch(page, rainMapSize, String(con)); + } + setIsReset(false); + }}} + resetPageNo={isReset} + > + <HeadMakeColSpan upHeadList={rswtSttNodeList} /> + </UiTable> + </div> + </div> + </ContentsDiv> + </> + ); +} diff --git a/apps/wr/components/ui/map/LayerPopUp/RuseFacilty/RuseFacility.tsx b/apps/wr/components/ui/map/LayerPopUp/RuseFacilty/RuseFacility.tsx new file mode 100644 index 000000000..4fd9c684f --- /dev/null +++ b/apps/wr/components/ui/map/LayerPopUp/RuseFacilty/RuseFacility.tsx @@ -0,0 +1,140 @@ +import { useWrStore } from "@/app/store"; +import { btnClass } from "@/app/store/common/code"; +import { ruseMapSize } from "@/app/store/map/ruseMap"; +import type { WaterReuseFacility } from "@/app/store/ruseFcltMng/fcltSttn/pbnstSttn/type"; +import { yearOptions } from "@/components/lib/func/commom"; +import { + ContentsDiv, + SearchBaseForm, + SearchInput, + SelectBox, + Title, + UiTable, + type ReturnType, +} from "@common/business_components/ui"; +import { Label } from "@common/components/ui"; +import { useEffect, useRef, useState } from "react"; + +export function RuseFacility({ + rtnFeature, + }: { + rtnFeature: (feature: any) => void; + }) { + const { code, ruseMap, ruseMapInit, getMapFlcts } = useWrStore( + (state) => state + ); + + const { ruseMapCount, fclts, ruseMapRes,ruseMapHeadList } = ruseMap; + + const [ruseTable, setRuseTable] = useState<WaterReuseFacility[]>(); + const [isReset, setIsReset] = useState(false); + const ref = useRef<ReturnType>({} as ReturnType); + const [searchYear, setSearchYear] = useState<string>( + String(new Date().getFullYear()) + ); + const [pbnRuseNm, setPbnRuseNm] = useState<string>(""); + + const ruseCellClick = (index: number | undefined) => { + if ( + index !== undefined && + ruseTable !== undefined + ) { + rtnFeature(ruseTable[index]); + } + }; + useEffect(() => { + if (!ruseMap.isInit) { + getMapFlcts(); + } + }, [ruseMap.isInit]); + useEffect(() => { + ruseMapInit(1,ruseMapSize, ref.current, searchYear, fclts); + }, [fclts]); + + useEffect(() => { + if (ruseMapRes && ruseMapRes.length > 0) { + setRuseTable(ruseMapRes); + } else { + setRuseTable([]); + } + }, [ruseMapRes]); + + + return ( + <> + <ContentsDiv> + <div className="grid grid-cols-2 gap-4 "> + <div className="col-span-2"> + <Title title="하수재이용 시설 현황" /> + <SearchBaseForm + selectSidoData={code.cityCd} + selectSigunData={code.sggCd} + selectRegionData={code.rvbsnCd} + selectUnitWaterShedData={code.lgbsnCd} + selectMiddleRegionData={code.mdmbsnCd} + selectEnvPartData={code.lclgvrnmntCd} + buttonOrder={["query", "init", "excel"]} + rightClassName="ml-auto mr-6 flex items-center space-x-1" + leftClassName="flex gap-5 w-9/12 m-4" + leftSelectClassName="w-1/4" + rightChildren={<></>} + leftChildren={ + <div className="col-span-3 flex items-center"> + <SelectBox + className="w-48 h-full text-2xl" + label="연도" + selectClass={"w-full "} + selectArray={yearOptions} + value={searchYear} + onSelectValue={(val: string | number) => + setSearchYear(String(val)) + } + /> + <Label className="w-full mt-2 ml-4 text-xl text-gray-600"> + {"하수처리시설명"} + </Label> + <SearchInput + className={"text-2xl w-full"} + label="" + textValue={pbnRuseNm || ""} + setText={(e) => { + setPbnRuseNm(e || ""); + }} + /> + </div> + } + queryClick={() => { + setIsReset(true); + }} + initClick={function (): void { + setPbnRuseNm(""); + }} + btnClass={btnClass} + ref={ref} + isAdmin={false} + excelClick={function (returnType: ReturnType): void { + console.log("pbnstSttnSearch ", returnType); + }} + /> + </div> + <div className="col-span-6"> + {/* 높이 조정 */} + <UiTable + tableData={ruseTable || []} + headlist={ruseMapHeadList} + pageSize={ruseMapSize} + total={ruseMapCount || 0} + headName={""} + cellClick={ruseCellClick} + onPageChange={(page) => { + ruseMapInit(page,ruseMapSize , ref.current, searchYear, fclts); + setIsReset(false); + }} + resetPageNo={isReset} + /> + </div> + </div> + </ContentsDiv> + </> + ); +} diff --git a/apps/wr/components/ui/modal/sttn/pbnstSttnModal/pbnstSttnModal.tsx b/apps/wr/components/ui/modal/sttn/pbnstSttnModal/pbnstSttnModal.tsx index 1a16c1fef..4212032f0 100644 --- a/apps/wr/components/ui/modal/sttn/pbnstSttnModal/pbnstSttnModal.tsx +++ b/apps/wr/components/ui/modal/sttn/pbnstSttnModal/pbnstSttnModal.tsx @@ -1,4 +1,4 @@ -import { headMakeprfmnc } from "@/app/ruseFcltMng/prfmncReg/chkgdnReg/TableHead"; +import { headMakeprfmnc } from "@/app/(with-layout)/ruseFcltMng/prfmncReg/chkgdnReg/TableHead"; import { useWrStore } from "@/app/store"; import type { TableUpperProps } from "@/app/store/common/type/header"; import { diff --git a/apps/wr/components/ui/modal/sttn/sttnDetailModal/coDatailModal.tsx b/apps/wr/components/ui/modal/sttn/sttnDetailModal/coDatailModal.tsx index aef145cc1..80cdb5c4e 100644 --- a/apps/wr/components/ui/modal/sttn/sttnDetailModal/coDatailModal.tsx +++ b/apps/wr/components/ui/modal/sttn/sttnDetailModal/coDatailModal.tsx @@ -14,11 +14,13 @@ import type { z } from "zod"; export default function CoDetailModal({ formObj, onFormSubmit, - readonly + readonly, + isMap =false, }: { formObj: any; onFormSubmit: (values: customLdrmnt) => void; readonly?: boolean; + isMap?:boolean }) { const defaultValues: z.infer<typeof ldrmntZod> = ldrmntZod.parse({}); const form = useForm<z.infer<typeof ldrmntZod>>({ @@ -54,13 +56,18 @@ export default function CoDetailModal({ <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> + {!isMap ? ( + <Button + type="submit" + className="col-start-12 col-span-1" + size={"sm"} + disabled={readonly} + > + 저장 + </Button> + ) : ( + <></> + )} </div> <ScrollArea className="h-[79vh] mx-8"> <FormField diff --git a/apps/wr/components/ui/table/chkgdTable/guideDetailTable/guideDetailTable.tsx b/apps/wr/components/ui/table/chkgdTable/guideDetailTable/guideDetailTable.tsx index 7522fdbfd..4ff4ab14e 100644 --- a/apps/wr/components/ui/table/chkgdTable/guideDetailTable/guideDetailTable.tsx +++ b/apps/wr/components/ui/table/chkgdTable/guideDetailTable/guideDetailTable.tsx @@ -1,5 +1,5 @@ "use client"; -import { headMakeprfmnc } from "@/app/ruseFcltMng/prfmncReg/chkgdnReg/TableHead"; +import { headMakeprfmnc } from "@/app/(with-layout)/ruseFcltMng/prfmncReg/chkgdnReg/TableHead"; import type { TableUpperProps } from "@/app/store/common/type/header"; import type { } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn"; diff --git a/apps/wr/components/ui/table/chkgdTable/rainChkgd/rainChkgdTable.tsx b/apps/wr/components/ui/table/chkgdTable/rainChkgd/rainChkgdTable.tsx index f016a0cc3..6fc2c0632 100644 --- a/apps/wr/components/ui/table/chkgdTable/rainChkgd/rainChkgdTable.tsx +++ b/apps/wr/components/ui/table/chkgdTable/rainChkgd/rainChkgdTable.tsx @@ -1,5 +1,5 @@ "use client"; -import { headMakeprfmnc } from "@/app/ruseFcltMng/prfmncReg/chkgdnReg/TableHead"; +import { headMakeprfmnc } from "@/app/(with-layout)/ruseFcltMng/prfmncReg/chkgdnReg/TableHead"; import { useWrStore } from "@/app/store"; import type { TableUpperProps } from "@/app/store/common/type/header"; import { rainPageSize, type rainChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn"; @@ -78,16 +78,26 @@ export default function RainChkgdTable({ useEffect(() => { setSelectedIds([])// 셀렉트 초기화를 위한 작업 if (rainChkData && rainChkData.length > 0) { - const rainChg = rainChkData?.map((item: any) => { const bud = tableData([item.building], { code }); + const rainyDetails= item.rainyDetails.map((detail)=>{ + return( + { + ...detail, + // 일단 테이블에서 사용하기 위해 넘버 전환 + ph: detail.ph&& !isNaN(detail.ph)?Number(detail.ph):detail.ph + } + ) + }) return { ...item, ...bud[0], + rainyDetails:rainyDetails, rowspan: item.rainyDetails?.length, }; }); + console.log('rainChg ', rainChg) setMakeTableData(rainChkTableData(rainChg)); } else { setMakeTableData([]); @@ -116,7 +126,8 @@ export default function RainChkgdTable({ if (findRain != undefined) { return { ...rd, - ph: Number(findRain.ph), + // 조드 타입과 맞추기위해 스트링 + ph: String(findRain.ph), trbt: Number(findRain.trbt), grssClfg: Number(findRain.grssClfg), etc: findRain.etc, diff --git a/apps/wr/components/ui/table/chkgdTable/rswtChkgd/rswtChkgdTable.tsx b/apps/wr/components/ui/table/chkgdTable/rswtChkgd/rswtChkgdTable.tsx index 2bb3a645f..aa74ad2c2 100644 --- a/apps/wr/components/ui/table/chkgdTable/rswtChkgd/rswtChkgdTable.tsx +++ b/apps/wr/components/ui/table/chkgdTable/rswtChkgd/rswtChkgdTable.tsx @@ -1,5 +1,5 @@ "use client"; -import { headMakeprfmnc } from "@/app/ruseFcltMng/prfmncReg/chkgdnReg/TableHead"; +import { headMakeprfmnc } from "@/app/(with-layout)/ruseFcltMng/prfmncReg/chkgdnReg/TableHead"; import { useWrStore } from "@/app/store"; import type { TableUpperProps } from "@/app/store/common/type/header"; import type { chHeadType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList"; diff --git a/apps/wr/components/ui/table/pbnstReg/ruseMngTable/ruseMngTable.tsx b/apps/wr/components/ui/table/pbnstReg/ruseMngTable/ruseMngTable.tsx index 07e7c1e8d..2e056a401 100644 --- a/apps/wr/components/ui/table/pbnstReg/ruseMngTable/ruseMngTable.tsx +++ b/apps/wr/components/ui/table/pbnstReg/ruseMngTable/ruseMngTable.tsx @@ -1,4 +1,4 @@ -import { headMakeprfmnc } from "@/app/ruseFcltMng/prfmncReg/chkgdnReg/TableHead"; +import { headMakeprfmnc } from "@/app/(with-layout)/ruseFcltMng/prfmncReg/chkgdnReg/TableHead"; import type { TableUpperProps } from "@/app/store/common/type/header"; import type { chHeadType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList"; import { selectClass } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; diff --git a/apps/wr/components/ui/table/pbnstReg/ruseMngTableForm/ruseMngTableForm.tsx b/apps/wr/components/ui/table/pbnstReg/ruseMngTableForm/ruseMngTableForm.tsx index b13160e8b..18c065ee2 100644 --- a/apps/wr/components/ui/table/pbnstReg/ruseMngTableForm/ruseMngTableForm.tsx +++ b/apps/wr/components/ui/table/pbnstReg/ruseMngTableForm/ruseMngTableForm.tsx @@ -1,4 +1,4 @@ -import { headMakeprfmnc } from "@/app/ruseFcltMng/prfmncReg/chkgdnReg/TableHead"; +import { headMakeprfmnc } from "@/app/(with-layout)/ruseFcltMng/prfmncReg/chkgdnReg/TableHead"; import type { TableUpperProps } from "@/app/store/common/type/header"; import type { chHeadType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList"; import { selectClass } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; diff --git a/apps/wr/public/locales/ko/message.json b/apps/wr/public/locales/ko/message.json index fb7af9ed2..fa122d798 100644 --- a/apps/wr/public/locales/ko/message.json +++ b/apps/wr/public/locales/ko/message.json @@ -22,5 +22,6 @@ "saveRowErrMsg": "수정을 원하는 행을 선택하고 저장버튼을 눌러주세요.", "costCalcErrMsg": "공사비 계산 진행 후 저장을 눌러주세요", "maxYearErrMsg": "최대 {{year}}년까지 선택가능합니다.", - "checkQtyMsg": "요구수량(m³/일)을 확인해주세요 계산할 수 없습니다." + "checkQtyMsg": "요구수량(m³/일)을 확인해주세요 계산할 수 없습니다.", + "insertRuseBiz": "{{cycle}}차 사업이 추가되었습니다." } \ No newline at end of file diff --git a/packages/common/business_components/src/ui/map/lib/gis.style.js b/packages/common/business_components/src/ui/map/lib/gis.style.js index ae413c66e..f38853962 100644 --- a/packages/common/business_components/src/ui/map/lib/gis.style.js +++ b/packages/common/business_components/src/ui/map/lib/gis.style.js @@ -275,7 +275,8 @@ GisOlStyle.prototype = { let fontExtnColor = layer.get('fontExtnColor'); let fontExtnWdth = Number(layer.get('fontExtnWdth') || '1'); let fontIntnColor = layer.get('fontIntnColor'); - let minViewSize = layer.get('minViewSize'); + //let minViewSize = layer.get('minViewSize') || 13; + let minViewSize = 14; if (feature == null) { return; } @@ -283,14 +284,14 @@ GisOlStyle.prototype = { let label = prop[layer.get('lblAtrbNm')]; let scaleFactor = 1 / resolution; // 해상도가 낮아질수록 글자 크기 증가 const zoom = GisApp.Module.core.map.getView().getZoom(); - if (zoom < minViewSize - 3) { + if (zoom <= minViewSize) { label = ''; } let text = new Text({ - stroke: new Stroke({ - color: '#FFFFFF', // 흰색 테두리 - width: fontExtnWdth, - }), + // stroke: new Stroke({ + // color: '#FFFFFF', // 흰색 테두리 + // width: fontExtnWdth, + // }), fill: new Fill({ color: fontIntnColor, width: fontExtnWdth, diff --git a/packages/common/business_components/src/ui/map/searchBar/searchBar.tsx b/packages/common/business_components/src/ui/map/searchBar/searchBar.tsx index 68377f5e3..e750ab1a7 100644 --- a/packages/common/business_components/src/ui/map/searchBar/searchBar.tsx +++ b/packages/common/business_components/src/ui/map/searchBar/searchBar.tsx @@ -3,6 +3,7 @@ import { SelectBox, type SelectDataType } from "../../../ui"; import { Button } from "@common/components/ui"; import { useCallback, useEffect, useState } from "react"; import SearchInputText from "./SearchInputText"; +import GisApp from "../lib/gis.core"; const FACILITY_TYPE_URL_MAP: Record<string, string> = { tb_il_swage_prcs_fclt: @@ -222,55 +223,51 @@ export const SearchBar = ({ setSearchList(newList); }; - const moveFocus = (feature: any) => { + //해당 시설물로 이동 + const handleFeatureFocus = (data: any) => { + const map = gisMaps.current; + if (!map) { + console.warn("지도가 초기화되지 않았습니다."); + return; + } + // 항상 같은 select 객체 사용 + const select = GisApp.Module.select as any; + if (!select) return; + // 1. 기존 선택 초기화 + //select.resetSelectedFeature(); + // 2. data가 OpenLayers Feature 인스턴스인지 확인 + let foundFeature: any = null; + let featureId = data.fid; + map + .getLayers() + .getArray() + .forEach((layer: any) => { + const source = layer.getSource?.(); + if (source?.getFeatureById) { + const feature = source.getFeatureById(String(featureId).trim()); + if (feature) foundFeature = feature; + } + }); + // 3. 피처 선택 + if (foundFeature) { + const geometry = foundFeature.getGeometry(); + if (geometry) { + const extent = geometry.getExtent(); + map.getView().fit(extent, { + duration: 300, + padding: [50, 50, 50, 50], + maxZoom: 16, + constrainResolution: true, + nearest: true, + }); + } else { + console.warn("geometry를 찾을 수 없습니다:", data); + } + } else { + console.warn("Feature를 찾을 수 없습니다:", data); + } - // console.log('searchbar:moveFocus') - // const map = gisMaps.current; - // if (!map) { - // console.warn("지도가 초기화되지 않았습니다."); - // return; - // } - - // // 항상 같은 select 객체 사용 - // const select = GisApp.Module.select as any; - // if (!select) return; - - // // 1. 기존 선택 초기화 - // select.resetSelectedFeature(); - - // // 2. data가 OpenLayers Feature 인스턴스인지 확인 - // let foundFeature: any = null; - // let featureId = data.fid; - // const layerName = data.layerNm; - // map - // .getLayers() - // .getArray() - // .forEach((layer: any) => { - // const source = layer.getSource?.(); - // if (source?.getFeatureById) { - // const feature = source.getFeatureById(String(featureId).trim()); - // if (feature) foundFeature = feature; - // } - // }); - - // // 3. 피처 선택 - // if (foundFeature) { - // const geometry = foundFeature.getGeometry(); - // if (geometry) { - // const extent = geometry.getExtent(); - - // map.getView().fit(extent, { - // duration: 300, - // padding: [50, 50, 50, 50], - // maxZoom: 16, - // constrainResolution: true, - // nearest: true, - // }); - // } - // // select.selectFeature(foundFeature, true); - // } else { - // console.warn("OpenLayers Feature를 찾을 수 없습니다:", data); - // } + onFeatureFocus(foundFeature); }; return ( @@ -320,7 +317,7 @@ export const SearchBar = ({ setRtnData={(selected: { fnm: string; fid: string }) => { // 선택된 항목이 있을 경우 onFeatureSelect 실행 const type = facilityType; - moveFocus({ + handleFeatureFocus({ layerNm: type, ...selected, });