From 142929510f0d3dd3a54ae342dd262eb12833d6f9 Mon Sep 17 00:00:00 2001 From: YangJinSoo94 Date: Wed, 23 Jul 2025 10:16:11 +0900 Subject: [PATCH 01/21] =?UTF-8?q?=ED=8F=AC=ED=8A=B8=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/dm/.env.local | 4 ++-- apps/dm/.env.production | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/dm/.env.local b/apps/dm/.env.local index 622545fd6..0b14165fd 100644 --- a/apps/dm/.env.local +++ b/apps/dm/.env.local @@ -1,6 +1,6 @@ NEXT_PUBLIC_SITE_ID = DM -NEXT_PUBLIC_PORT = 3000 -NEXT_PUBLIC_PROXY_HOST = http://localhost:3000 +NEXT_PUBLIC_PORT = 11035 +NEXT_PUBLIC_PROXY_HOST = http://con.mind-one.co.kr:11035 NEXT_PUBLIC_SERVER_API_URL = http://con.mind-one.co.kr:11080/api/v1 NEXT_PUBLIC_FO_SERVER_API_URL = http://con.mind-one.co.kr:11082/api/v1 NEXT_PUBLIC_CS_SERVER_API_URL = http://dev.onasys.co.kr:8000 diff --git a/apps/dm/.env.production b/apps/dm/.env.production index 8a552821e..7576a7dfb 100644 --- a/apps/dm/.env.production +++ b/apps/dm/.env.production @@ -1,6 +1,6 @@ NEXT_PUBLIC_SITE_ID = DM -NEXT_PUBLIC_PORT = 3000 -NEXT_PUBLIC_PROXY_HOST = http://wr.hasuon.or.kr +NEXT_PUBLIC_PORT = 11035 +NEXT_PUBLIC_PROXY_HOST = http://con.mind-one.co.kr:11035 NEXT_PUBLIC_SERVER_API_URL = http://apigateway.iipbs-dev.svc.cluster.local:8000/api/v1 NEXT_PUBLIC_FO_SERVER_API_URL = http://apigateway.iipbs-dev.svc.cluster.local:8000/operation-service/api/v1 NEXT_PUBLIC_CS_SERVER_API_URL = http://apigateway.iipbs-dev.svc.cluster.local:8000 From c4750d965213b7a2b78b8d550f63ea34f31ea1a3 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 23 Jul 2025 13:48:38 +0900 Subject: [PATCH 02/21] =?UTF-8?q?=EC=88=98=EC=9A=94=EC=B2=98=EB=AA=85=20?= =?UTF-8?q?=EC=9E=85=EB=A0=A5=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/pages/sdmMng/sdmInfoInsert/page.tsx | 6 +++--- apps/dm/app/pages/sdmMng/sdmInsert/page.tsx | 1 + .../ui/sdmInfoInsertForm/component.tsx | 8 ++++++++ .../components/ui/sdmLoginForm/component.tsx | 19 +++++++++++-------- 4 files changed, 23 insertions(+), 11 deletions(-) diff --git a/apps/dm/app/pages/sdmMng/sdmInfoInsert/page.tsx b/apps/dm/app/pages/sdmMng/sdmInfoInsert/page.tsx index a0f905c35..95c5e6822 100644 --- a/apps/dm/app/pages/sdmMng/sdmInfoInsert/page.tsx +++ b/apps/dm/app/pages/sdmMng/sdmInfoInsert/page.tsx @@ -13,7 +13,7 @@ import { Toaster } from "react-hot-toast"; import * as z from "zod"; import SdmLogin from "../sdmLogin/page"; -const createFormSchema = () => z.object({ +const sdmInfoFormSchema = z.object({ sdmNm: z.string() .min(1, "수요처를 선택해주세요"), demandYmd: z.date().optional(), @@ -67,14 +67,14 @@ const createFormSchema = () => z.object({ candidateTitle: z.string().optional(), }); -export type FormData = z.infer>; +export type FormData = z.infer; function SdmInfoInsertContent() { const searchParams = useSearchParams(); const {insertSdmInfoData, Sdm} = useDmStore((state) => state); const {successLogin} = Sdm; const [ismoneyDisabled, setIsmoneyDisabled] = useState(false); - const formSchema = createFormSchema(); + const formSchema = sdmInfoFormSchema; const [isLogin, setIsLogin] = useState(false); const [facilityName, setFacilityName] = useState(''); diff --git a/apps/dm/app/pages/sdmMng/sdmInsert/page.tsx b/apps/dm/app/pages/sdmMng/sdmInsert/page.tsx index 040304347..0a5a5546d 100644 --- a/apps/dm/app/pages/sdmMng/sdmInsert/page.tsx +++ b/apps/dm/app/pages/sdmMng/sdmInsert/page.tsx @@ -21,6 +21,7 @@ export default function MyForm() { useEffect(() => { if (successNextPage && formDataRef.current) { const data = formDataRef.current; + console.log(data); const queryParams = new URLSearchParams({ dmdCoNm: data.dmdCoNm || '', ctpvCd: data.ctpvCd || '', diff --git a/apps/dm/components/ui/sdmInfoInsertForm/component.tsx b/apps/dm/components/ui/sdmInfoInsertForm/component.tsx index 79d53db80..8c2273872 100644 --- a/apps/dm/components/ui/sdmInfoInsertForm/component.tsx +++ b/apps/dm/components/ui/sdmInfoInsertForm/component.tsx @@ -54,6 +54,14 @@ export function SdmInfoRegForm({ form, onCheckboxChange, facilityName }: SdmInfo } }, [facilityName, form]); + useEffect(() => { + if (searchParams.get('dmdCoNm')) { + form.setValue('sdmNm', searchParams.get('dmdCoNm') || ''); + setSdmNmValue(searchParams.get('dmdCoNm') || ''); + setKey(prev => prev + 1); + } + }, [searchParams, form]); + const searchCandidate = (e: React.MouseEvent) => { e.preventDefault(); e.stopPropagation(); diff --git a/apps/dm/components/ui/sdmLoginForm/component.tsx b/apps/dm/components/ui/sdmLoginForm/component.tsx index b9cc305a9..cdedecc79 100644 --- a/apps/dm/components/ui/sdmLoginForm/component.tsx +++ b/apps/dm/components/ui/sdmLoginForm/component.tsx @@ -4,14 +4,16 @@ import { BZNM_TOT_NO, FormField, FormUi } from "@common/business_components"; import { Button, Input } from "@common/components"; import { zodResolver } from "@hookform/resolvers/zod"; import { sha512 } from 'js-sha512'; -import { useEffect, useState } from "react"; +import { useEffect, useState, useMemo } from "react"; import { FormProvider, useForm, type ControllerRenderProps } from "react-hook-form"; import * as z from "zod"; export function SdmLoginForm() { - type FormData = z.infer>; - type FieldComponent = (field: ControllerRenderProps) => JSX.Element; - const createFormSchema = () => z.object({ + const { actionLogin, initializeCommonData } = useDmStore((state) => state); + const [isBznmTotNoDisabled, setIsBznmTotNoDisabled] = useState(false); + + // 스키마를 useMemo로 최적화 + const sdmLoginFormSchema = useMemo(() => z.object({ dmdInfoId: z.string() .min(1, "사업자등록번호를 입력해주세요") .regex(/^\d{10}$/, "사업자등록번호는 10자리 숫자만 입력 가능합니다"), @@ -24,11 +26,12 @@ export function SdmLoginForm() { .max(16, "비밀번호는 16자 이내로 입력해주세요"), rgtr: z.string().optional(), dmdAplyPswd: z.string().optional(), - }); + }), [isBznmTotNoDisabled]); - const { actionLogin, initializeCommonData } = useDmStore((state) => state); - const [isBznmTotNoDisabled, setIsBznmTotNoDisabled] = useState(false); - const formSchema = createFormSchema(); + type FormData = z.infer; + type FieldComponent = (field: ControllerRenderProps) => JSX.Element; + + const formSchema = sdmLoginFormSchema; useEffect(() => { initializeCommonData(); From 26c01ede4ede7e49c2875f4efef4619b0461ce74 Mon Sep 17 00:00:00 2001 From: leedj312 Date: Thu, 24 Jul 2025 14:51:04 +0900 Subject: [PATCH 03/21] =?UTF-8?q?=EB=8C=80=EB=AF=BC=20=EC=A7=80=EB=8F=84?= =?UTF-8?q?=20=ED=8C=9D=EC=97=85=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/dm/app/pages/map/mainMap/page.tsx | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 apps/dm/app/pages/map/mainMap/page.tsx diff --git a/apps/dm/app/pages/map/mainMap/page.tsx b/apps/dm/app/pages/map/mainMap/page.tsx new file mode 100644 index 000000000..f1def0310 --- /dev/null +++ b/apps/dm/app/pages/map/mainMap/page.tsx @@ -0,0 +1,14 @@ +"use client"; +const Map = () => { + + + return ( + <> + + + + + ); +}; + +export default Map; From 19bee65195ff28b6e9f18a9f9ea8ba052c1339bb Mon Sep 17 00:00:00 2001 From: leedj312 Date: Thu, 24 Jul 2025 15:01:08 +0900 Subject: [PATCH 04/21] =?UTF-8?q?=EB=A9=94=EC=9D=B8=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=EC=97=90=EC=84=9C=20=EC=A7=80=EB=8F=84=ED=8C=9D?= =?UTF-8?q?=EC=97=85(=EB=B2=84=ED=8A=BC)=20=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/dm/app/page.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/dm/app/page.tsx b/apps/dm/app/page.tsx index fdb323086..2628cca2f 100644 --- a/apps/dm/app/page.tsx +++ b/apps/dm/app/page.tsx @@ -30,6 +30,9 @@ export default function Home() { + ); From c9f1d051261c5ff7deaac3d9bd444ee97f817940 Mon Sep 17 00:00:00 2001 From: leedj312 Date: Thu, 24 Jul 2025 15:01:29 +0900 Subject: [PATCH 05/21] =?UTF-8?q?=EC=84=9C=EC=B9=98=EB=B0=94=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=9D=BC=EB=8B=A8=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/dm/components/ui/map/searchBar/searchBar.tsx | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 apps/dm/components/ui/map/searchBar/searchBar.tsx diff --git a/apps/dm/components/ui/map/searchBar/searchBar.tsx b/apps/dm/components/ui/map/searchBar/searchBar.tsx new file mode 100644 index 000000000..f6af413ef --- /dev/null +++ b/apps/dm/components/ui/map/searchBar/searchBar.tsx @@ -0,0 +1,6 @@ +export function SearchBar() { + return ( + <> + + ); +} \ No newline at end of file From 79bc10cdcea9cd83d42f7ec4a7ef4342767b2804 Mon Sep 17 00:00:00 2001 From: jyha00 Date: Thu, 24 Jul 2025 17:37:46 +0900 Subject: [PATCH 06/21] =?UTF-8?q?=EB=8C=80=EB=AF=BC=20=EB=94=94=EC=9E=90?= =?UTF-8?q?=EC=9D=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/dm/app/pages/wrAbout/page.tsx | 512 +++++++++--------- apps/dm/app/pages/wrExample/page.tsx | 128 ++--- apps/dm/app/pages/wrOverview/page.tsx | 62 +-- apps/dm/app/pages/wrStatus/page.tsx | 12 +- .../ui/sdmInfoInsertForm/component.tsx | 1 - .../components/ui/sdmLoginForm/component.tsx | 24 +- apps/dm/tailwind.config.ts | 123 +++-- 7 files changed, 444 insertions(+), 418 deletions(-) diff --git a/apps/dm/app/pages/wrAbout/page.tsx b/apps/dm/app/pages/wrAbout/page.tsx index e52549e66..8d5bdd0ba 100644 --- a/apps/dm/app/pages/wrAbout/page.tsx +++ b/apps/dm/app/pages/wrAbout/page.tsx @@ -16,7 +16,7 @@ export default function WrAboutPage() { const [activeIdx, setActiveIdx] = useState(0); const navRefs = useRef<(HTMLLIElement | null)[]>([]); const cardRefs = useRef<(HTMLElement | null)[]>([]); - + // 클릭 시 이동 const handleNavClick = (idx: number) => { setActiveIdx(idx); @@ -69,267 +69,269 @@ export default function WrAboutPage() { }, []); return ( -
- {/* 사이드바 */} - -
{ cardRefs.current[0] = el; }} className="card-wrap pl-[100px] mb-24 !pt-0" id="card1" aria-labelledby="card1-title"> -

- - 물재이용이란 -

-
-

물 재이용이 무엇인가요?

-

- 물 재이용은 빗물, 오수, 하수처리수, 폐수처리수 및 발전소 온배수를
- 물 재이용시설을 이용하여 처리하고, 그 처리된 물을 생활, 공업, 농업, 조경,
- 하천 유지 등의 용도로 이용하는 것을 말합니다. -

- 이 이미지는 물 재이용 시스템의 개요를 나타냅니다. 가정, 공공기관, 하수처리장 등에서 발생한 생활하수, 빗물, 하수처리수를 물 재이용 시설에서 처리한 후, 청소나 화장실, 농업, 살수차, 하천 유지, 공장, 공원 및 골프장 등 다양한 용도로 활용하는 과정을 도식화한 것입니다. -

- 환경부는 2011년에 '물의 재이용 촉진 및 지원에 관한 법률'을 제정하여
- 한국환경공단과 함께 물 재이용 확대 및 촉진을 위해 노력하고 있습니다. -

-
-
+
{ cardRefs.current[0] = el; }} className="card-wrap pl-[100px] mb-24 !pt-0" id="card1" aria-labelledby="card1-title"> +

+ + 물재이용이란 +

+
+

물 재이용이 무엇인가요?

+

+ 물 재이용은 빗물, 오수, 하수처리수, 폐수처리수 및 발전소 온배수를
+ 물 재이용시설을 이용하여 처리하고, 그 처리된 물을 생활, 공업, 농업, 조경,
+ 하천 유지 등의 용도로 이용하는 것을 말합니다. +

+ 이 이미지는 물 재이용 시스템의 개요를 나타냅니다. 가정, 공공기관, 하수처리장 등에서 발생한 생활하수, 빗물, 하수처리수를 물 재이용 시설에서 처리한 후, 청소나 화장실, 농업, 살수차, 하천 유지, 공장, 공원 및 골프장 등 다양한 용도로 활용하는 과정을 도식화한 것입니다. +

+ 환경부는 2011년에 '물의 재이용 촉진 및 지원에 관한 법률'을 제정하여
+ 한국환경공단과 함께 물 재이용 확대 및 촉진을 위해 노력하고 있습니다. +

+
+
- {/* 카드2 */} -
{ cardRefs.current[1] = el; }} className="card-wrap pl-[100px] mb-24" id="card2" aria-labelledby="card2-title"> -

- - 물재이용 필요성 -

-
-

물 재이용, 왜 필요한가요?

-

- 빗물, 중수, 하수처리수는 지속적으로 이용 가능한 순환자원입니다.
- 물 재이용을 통한 효율적인 수자원 활용은 물 스트레스국가인 우리나라에서
가뭄대응능력 향상 및 하천수질보전을 위해 반드시 필요한 일입니다. -

- 이 이미지는 기후변화로 인한 온도 상승 추이를 설명한다. 왼쪽에는 지구 그림과 온도계가 함께 표시되어 있으며, 지구 평균 기온은 지난 100년간 약 0.9℃ 상승했음을 나타낸다. 오른쪽에는 대한민국 지도와 함께 온도계가 있으며, 지난 100년간 약 1.8℃ 상승했음을 보여준다. 이는 지구 평균보다 2배 높은 수치이며, 자료 출처는 「기후변화 평가보고서 2020, 환경부·기상청」이다. -

- - 우리나라의 기후변화 진행속도는 세계 평균을 상회하고 있습니다.
- - 기후변화로 인한 국지적인 가뭄은 더욱 자주, 더욱 심하게 일어나고 있으며, 우리의 물 안보를 위협하고 있습니다. -

- 세계 평균 1인당 강수량은 15,044m³이며, 대한민국은 2,546m³로 약 1/6 수준입니다. 계절별 강수량은 봄 10%, 여름 70%, 가을 15%, 겨울 5%입니다. 이는 여름철에 강수량이 집중되는 문제를 나타냅니다. -

- - 우리나라의 1인당 강수량은 2,546㎥ 으로 세계 평균 15,044㎥의 약 1/6 수준입니다.
- - 우리나라 강수량의 70%는 여름철에 집중되어 있어 이용 가능한 수자원확보가 어렵습니다. -

- 물 재이용은 환경을 보호하며 지속 가능한 발전이 가능하게 하고, 안정적인 수자원 확보, 깨끗하고 경제적인 공업용수 공급, 하천 수질 개선을 통한 생태계 회복에 기여합니다. -
-
+ {/* 카드2 */} +
{ cardRefs.current[1] = el; }} className="card-wrap pl-[100px] mb-24" id="card2" aria-labelledby="card2-title"> +

+ + 물재이용 필요성 +

+
+

물 재이용, 왜 필요한가요?

+

+ 빗물, 중수, 하수처리수는 지속적으로 이용 가능한 순환자원입니다.
+ 물 재이용을 통한 효율적인 수자원 활용은 물 스트레스국가인 우리나라에서
가뭄대응능력 향상 및 하천수질보전을 위해 반드시 필요한 일입니다. +

+ 이 이미지는 기후변화로 인한 온도 상승 추이를 설명한다. 왼쪽에는 지구 그림과 온도계가 함께 표시되어 있으며, 지구 평균 기온은 지난 100년간 약 0.9℃ 상승했음을 나타낸다. 오른쪽에는 대한민국 지도와 함께 온도계가 있으며, 지난 100년간 약 1.8℃ 상승했음을 보여준다. 이는 지구 평균보다 2배 높은 수치이며, 자료 출처는 「기후변화 평가보고서 2020, 환경부·기상청」이다. +

+ - 우리나라의 기후변화 진행속도는 세계 평균을 상회하고 있습니다.
+ - 기후변화로 인한 국지적인 가뭄은 더욱 자주, 더욱 심하게 일어나고 있으며, 우리의 물 안보를 위협하고 있습니다. +

+ 세계 평균 1인당 강수량은 15,044m³이며, 대한민국은 2,546m³로 약 1/6 수준입니다. 계절별 강수량은 봄 10%, 여름 70%, 가을 15%, 겨울 5%입니다. 이는 여름철에 강수량이 집중되는 문제를 나타냅니다. +

+ - 우리나라의 1인당 강수량은 2,546㎥ 으로 세계 평균 15,044㎥의 약 1/6 수준입니다.
+ - 우리나라 강수량의 70%는 여름철에 집중되어 있어 이용 가능한 수자원확보가 어렵습니다. +

+ 물 재이용은 환경을 보호하며 지속 가능한 발전이 가능하게 하고, 안정적인 수자원 확보, 깨끗하고 경제적인 공업용수 공급, 하천 수질 개선을 통한 생태계 회복에 기여합니다. +
+
- {/* 카드3 */} -
{ cardRefs.current[2] = el; }} className="card-wrap pl-[100px] mb-24" id="card3" aria-labelledby="card3-title"> -

- - 빗물 재이용 -

-
-

친환경 수자원 빗물,
편리하게 사용할 수 있어요!

-

가뭄을 극복하고 홍수를 예방하기 위해
비가 내릴 때 빗물을 모아두었다가 사용합니다.

- 주택 지붕에 내린 빗물을 저장조에 모아 조경용수, 화장실용수, 세척·살수용수로 재활용하는 과정을 보여줍니다. - 빗물이용 -

- - 의무 설치대상: 지붕면적 1,000㎡ 이상의 운동장, 체육관, 공공업무시설 등
- - 설 치 기 준: 집수시설, 처리시설, 빗물 저류조(지붕 집수면적 x 0.05m 이상)
- - 활 용 용 도: 골프장 등의 조경용수, 화장실용수, 세척·살수용수 등 -

-
-
+ {/* 카드3 */} +
{ cardRefs.current[2] = el; }} className="card-wrap pl-[100px] mb-24" id="card3" aria-labelledby="card3-title"> +

+ + 빗물 재이용 +

+
+

친환경 수자원 빗물,
편리하게 사용할 수 있어요!

+

가뭄을 극복하고 홍수를 예방하기 위해
비가 내릴 때 빗물을 모아두었다가 사용합니다.

+ 주택 지붕에 내린 빗물을 저장조에 모아 조경용수, 화장실용수, 세척·살수용수로 재활용하는 과정을 보여줍니다. + 빗물이용 +

+ - 의무 설치대상: 지붕면적 1,000㎡ 이상의 운동장, 체육관, 공공업무시설 등
+ - 설 치 기 준: 집수시설, 처리시설, 빗물 저류조(지붕 집수면적 x 0.05m 이상)
+ - 활 용 용 도: 골프장 등의 조경용수, 화장실용수, 세척·살수용수 등 +

+
+
- {/* 카드4 */} -
{ cardRefs.current[3] = el; }} className="card-wrap pl-[100px] mb-24" id="card4" aria-labelledby="card4-title"> -

- - 중수 재이용 -

-
-

요모조모 쓸모 많은 중수,
알뜰하게 재이용할 수 있어요!

-

한번 사용한 물을 중수도 시설을 통해 재생하여
개별적 또는 지역적으로 재이용할 수 있습니다.

- 욕실, 세면대, 주방, 세탁기 등에서 발생한 생활용수를 중수도 시설에서 처리하여 화장실 변기용수와 조경용수로 재이용하는 시스템 흐름도입니다. - 중수재이용 -

- - 의무 설치대상: 일정규모 이상의 대규모점포, 숙박/목욕장, 공장, 각종 개발사업 등
- - 의무 재이용량: 물 사용량의 10% 이상
- - 활 용 용 도: 청소·화장실용수, 세척·살수용수, 조경용수, 공업용수 등 -

-
-
+ {/* 카드4 */} +
{ cardRefs.current[3] = el; }} className="card-wrap pl-[100px] mb-24" id="card4" aria-labelledby="card4-title"> +

+ + 중수 재이용 +

+
+

요모조모 쓸모 많은 중수,
알뜰하게 재이용할 수 있어요!

+

한번 사용한 물을 중수도 시설을 통해 재생하여
개별적 또는 지역적으로 재이용할 수 있습니다.

+ 욕실, 세면대, 주방, 세탁기 등에서 발생한 생활용수를 중수도 시설에서 처리하여 화장실 변기용수와 조경용수로 재이용하는 시스템 흐름도입니다. + 중수재이용 +

+ - 의무 설치대상: 일정규모 이상의 대규모점포, 숙박/목욕장, 공장, 각종 개발사업 등
+ - 의무 재이용량: 물 사용량의 10% 이상
+ - 활 용 용 도: 청소·화장실용수, 세척·살수용수, 조경용수, 공업용수 등 +

+
+
- {/* 카드5 */} -
{ cardRefs.current[4] = el; }} className="card-wrap pl-[100px] mb-24" id="card5" aria-labelledby="card5-title"> -

- - 하수 재이용 -

-
-

아직 할 일이 많은 하수처리수,
풍족하게 재이용할 수 있어요!

-

사용하고 버려져 공공하수처리장으로 모여든 물은
용도에 맞는 처리 후 재이용 할 수 있습니다.

- 주택과 건물에서 발생한 하수가 공공하수처리장을 거쳐 처리된 후, 물 재이용 시설에서 정화되어 공업용수, 하천유지용수, 농업용수, 세척·살수용수로 재활용됩니다. - 하수처리 재이용 -

- - 의무 이용대상: 공공하수처리장(시설용량 5,000㎥/일 이상)
- - 의무 재이용량: 일 하수처리량의 10% 이상
- - 활 용 용 도: 공업용수, 하천유지용수, 농업용수 등 -

-
-
+ {/* 카드5 */} +
{ cardRefs.current[4] = el; }} className="card-wrap pl-[100px] mb-24" id="card5" aria-labelledby="card5-title"> +

+ + 하수 재이용 +

+
+

아직 할 일이 많은 하수처리수,
풍족하게 재이용할 수 있어요!

+

사용하고 버려져 공공하수처리장으로 모여든 물은
용도에 맞는 처리 후 재이용 할 수 있습니다.

+ 주택과 건물에서 발생한 하수가 공공하수처리장을 거쳐 처리된 후, 물 재이용 시설에서 정화되어 공업용수, 하천유지용수, 농업용수, 세척·살수용수로 재활용됩니다. + 하수처리 재이용 +

+ - 의무 이용대상: 공공하수처리장(시설용량 5,000㎥/일 이상)
+ - 의무 재이용량: 일 하수처리량의 10% 이상
+ - 활 용 용 도: 공업용수, 하천유지용수, 농업용수 등 +

+
+
- {/* 카드6 */} -
{ cardRefs.current[5] = el; }} className="card-wrap pl-[100px] mb-24" id="card6" aria-labelledby="card6-title"> -

- - 물재이용 목표 -

-
-

2030년 물 재이용 목표를 위해
모두의 관심과 노력이 필요합니다.

-

- 환경부는 2030년까지의 제2차 물 재이용 기본계획을 수립하여
한국환경공단과 함께 물 재이용 확대 및 촉진을 위해 노력하고 있습니다. -

- 지속가능한 물 재이용 정착으로 건전한 물순환 확산 -
-
- -

도시 물 순환이용
체계로의 전환

-
    -
  • - 지속가능한 물 순환이용체계 구축
  • -
  • - 물 재이용시설 관리체계 강화
  • -
+ {/* 카드6 */} +
{ cardRefs.current[5] = el; }} className="card-wrap pl-[100px] mb-24" id="card6" aria-labelledby="card6-title"> +

+ + 물재이용 목표 +

+
+

2030년 물 재이용 목표를 위해
모두의 관심과 노력이 필요합니다.

+

+ 환경부는 2030년까지의 제2차 물 재이용 기본계획을 수립하여
한국환경공단과 함께 물 재이용 확대 및 촉진을 위해 노력하고 있습니다. +

+ 지속가능한 물 재이용 정착으로 건전한 물순환 확산 +
+
+ +

도시 물 순환이용
체계로의 전환

+
    +
  • - 지속가능한 물 순환이용체계 구축
  • +
  • - 물 재이용시설 관리체계 강화
  • +
+
+
+ +

유역기반 하수처리수 재이용
수요확대 및 공급체계 구축

+
    +
  • - 유역기반 통합물관리 연계 강화
  • +
  • - 하수처리수 재이용 공업용수 공급 활성화
  • +
  • - 하수처리수 재이용을 활용한 물순환 촉진
  • +
+
+
+ +

물 재이용 인식제고
및 산업발전 기반 마련

+
    +
  • - 물 재이용 교육 및 대국민 홍보 강화
  • +
  • - 핵심기술 육성 및 산업발전 지원 강화
  • +
+
-
- -

유역기반 하수처리수 재이용
수요확대 및 공급체계 구축

-
    -
  • - 유역기반 통합물관리 연계 강화
  • -
  • - 하수처리수 재이용 공업용수 공급 활성화
  • -
  • - 하수처리수 재이용을 활용한 물순환 촉진
  • -
-
-
- -

물 재이용 인식제고
및 산업발전 기반 마련

-
    -
  • - 물 재이용 교육 및 대국민 홍보 강화
  • -
  • - 핵심기술 육성 및 산업발전 지원 강화
  • -
-
-
- 물 재이용량은 2014년 13.3억톤에서 2030년 25.9억톤까지 증가할 것으로 예상됩니다. 그에 따른 기대 효과로는 연간 상수대체 17.9억톤, 물산업 시장성장 약 2조3,541억 원, 건천화된 73개 하천 해소, 약 20,550명의 고용 효과가 포함됩니다. -
-
-
+ 물 재이용량은 2014년 13.3억톤에서 2030년 25.9억톤까지 증가할 것으로 예상됩니다. 그에 따른 기대 효과로는 연간 상수대체 17.9억톤, 물산업 시장성장 약 2조3,541억 원, 건천화된 73개 하천 해소, 약 20,550명의 고용 효과가 포함됩니다. + + + + ); } diff --git a/apps/dm/app/pages/wrExample/page.tsx b/apps/dm/app/pages/wrExample/page.tsx index 066e0abe2..4849c02b0 100644 --- a/apps/dm/app/pages/wrExample/page.tsx +++ b/apps/dm/app/pages/wrExample/page.tsx @@ -2,49 +2,49 @@ export default function WrExamplePage() { return ( -
+
-

+

물 재이용사례

-
+
{/* 카드 1 */}
-

+

사업사례 1

{/* 사업명 */}
-

사업명

-

+

사업명

+

경기권역 반도체 사업장 물 재이용 사업
: 삼성전자 반도체 사업장 하수재이용수 공급(재이용시설 설치, 공급망 구축 등)

{/* 사업 배경 및 목적 */}
-

사업 배경 및 목적

-

+

사업 배경 및 목적

+

삼성전자는 환경경영전략 실현과 반도체 공장의 안정적 용수 공급을 위해 하수 재이용수를 공업용수로 도입하기로 결정

{/* 사업 주요 내용 */}
-

사업 주요 내용

-

+

사업 주요 내용

+

1단계 사업[화성·오산(12만㎥/일) → 삼성 화성·기흥(29년 공급)]
2단계 사업[수원(21만㎥/일) → 삼성 평택(30년 공급)]

{/* 특이사항 */}
-

특이사항

-

+

특이사항

+

본 사업은 광역 하수 재이용 공급망 구축의 첫 사업으로 초기 민관협의체를 구성·운영 후 관계기관(환경부, 경기도, 지자체, 공단, 수공, 삼성전자) 간 MOU 체결을 통해 사업을 추진하였으며, 반도체 사업장에 하수 재이용수를 공업용수로 공급하는 국내 첫 사업으로 위협부담 해소를 위해 사업을 단계별로 시행

@@ -63,15 +63,15 @@ export default function WrExamplePage() { {/* 카드2 */}
-

+

사업사례 2

{/* 사업명 */}
-

사업명

-

+

사업명

+

경기도 파주시 하수재이용
: 운정·금촌하수처리시설의 하수처리수를 막공법(U/F, R/O)으로 재처리하여 파주 LCD 산업단지내 3개 업체*에 공업용수 공급

*LG디스플레이(P10공장), 희성전자, 엔비텍 등 @@ -79,15 +79,15 @@ export default function WrExamplePage() {

{/* 사업 방식 */}
-

사업 방식

-

+

사업 방식

+

민간투자사업(시행사 : 파주그린허브(주), 운영사 : 테크로스)

{/* 사업 주요 내용 */}
-

사업 주요 내용

-

+

사업 주요 내용

+

처리공정 : 전처리분리막(U/F) + 역삼투서비(R/O)
공급량 : 4만m³/일
관로 : 13.6km
@@ -96,8 +96,8 @@ export default function WrExamplePage() {

{/* 특이사항 */}
-

특이사항

-

+

특이사항

+

하수처리수 재이용시설 자체 농축폐수 처리공정 개발 및 적용 성공사례

@@ -124,29 +124,29 @@ export default function WrExamplePage() { - 하수처리수 공급 + 하수처리수 공급 - 위치 - 금촌하수 - 운정하수 + 위치 + 금촌하수 + 운정하수 - 시설용량 - 44,000㎥/일 - 60,000㎥/일 + 시설용량 + 44,000㎥/일 + 60,000㎥/일 - 유입량 - 27,000㎥/일
자연유하 - 30,000㎥/일
이송펌프장 + 유입량 + 27,000㎥/일
자연유하 + 30,000㎥/일
이송펌프장 {/* 첫 화살표 (자연유하 / 관로이송) */}
- 자연유하: 161m + 자연유하: 161m 오른쪽 화살표 - 관로이송: 3.2km + 관로이송: 3.2km
@@ -177,26 +177,26 @@ export default function WrExamplePage() { - 물재이용 처리시설 + 물재이용 처리시설 - 유입유량 - 57,000㎥/일 + 유입유량 + 57,000㎥/일 - 재이용시설 - 40,000㎥/일 + 재이용시설 + 40,000㎥/일 - 농축폐수처리 - 17,000㎥/일 + 농축폐수처리 + 17,000㎥/일 {/* 두 번째 화살표 (공급관로) */}
- 공급관로 L=10.4km + 공급관로 L=10.4km 아래 화살표 - 수요처 공급 + 수요처 공급 - 수요처 - LG디스플레이 외 2개사 + 수요처 + LG디스플레이 외 2개사 - 수요량 - 40,000㎥/일 + 수요량 + 40,000㎥/일 @@ -238,30 +238,30 @@ export default function WrExamplePage() { {/* 카드3 */}
-

+

사업사례 3

{/* 사업명 */}
-

사업명

-

+

사업명

+

충남 보령 하수재이용
: 보령·신보령화력발전소에 하루 1만톤의 탈황용수 공급을 위해 보령하수처리시설의 하수처리수 재이용 사업 추진

{/* 사업 방식 */}
-

사업 방식

-

+

사업 방식

+

2017년 한국환경공단과 사업대행 위 · 수탁 협약 체결

{/* 사업 주요 내용 */}
-

사업 주요 내용

-

+

사업 주요 내용

+

처리공정 : 전처리분리막(U/F) + 역삼투서비(R/O)
공급량 : 1만m3/일
관로 : 8.7km
@@ -270,8 +270,8 @@ export default function WrExamplePage() {

{/* 특이사항 */}
-

특이사항

-

+

특이사항

+

국내 유일 하수재이용 공업용수 공급 재정사업으로 2016년 충남 서부권 가뭄 시 지역사회 기여 차원에서 기관간 MOU (환경부, 충청남도, 보령시, 한국중부발전㈜)를 바탕 으로 추진 되어 경제성보다 공익성을 바탕으로 국가전력 기반시설의 안정적 운영 도모

@@ -290,22 +290,22 @@ export default function WrExamplePage() { {/* 카드4 */}
-

+

사업사례 4

{/* 사업명 */}
-

사업명

-

+

사업명

+

경기도 수원시 빗물이용과 중수도

{/* 사업 내용 */}
-

사업 내용

-

+

사업 내용

+

(빗물) 월드컵경기장 등 4개소에 노면 빗물분사시스템 및 빗물 공급기를 설치하여 혹서기 도시 열섬효과 및 도로 비산먼지 저감
  ※ 시설 가동중 도로 노면 온도의 7.2 ~ 26.3℃ 감소 효과 확인(21.7월, 수원시청).
  다만, 가뭄기나 강우량이 적을 경우 빗물을 원활히 공급하기 다소 곤란

@@ -320,7 +320,7 @@ export default function WrExamplePage() { height={250} className="w-[100%] mx-auto" /> - <월드컵경기장 인근도로 노면 살수> + <월드컵경기장 인근도로 노면 살수>

- <주유기 모양의 빗물 공급기> + <주유기 모양의 빗물 공급기>
- <빗물주유기에서 살수용수 공급> + <빗물주유기에서 살수용수 공급>
- <빗물 공급기 및 빗물분사장치 위치> + <빗물 공급기 및 빗물분사장치 위치>
diff --git a/apps/dm/app/pages/wrOverview/page.tsx b/apps/dm/app/pages/wrOverview/page.tsx index f8aa3b933..3e5825899 100644 --- a/apps/dm/app/pages/wrOverview/page.tsx +++ b/apps/dm/app/pages/wrOverview/page.tsx @@ -2,15 +2,15 @@ export default function WrOverviewPage() { return ( -
+
{/* 카드 1 - 물재이용 사업 추진 절차 */}
-

+

물재이용 사업 추진 절차

-
+
@@ -22,55 +22,55 @@ export default function WrOverviewPage() { {/* 1단계 */} - - + + {/* 2단계 */} - - + + {/* 3단계 */} - - + + {/* 4단계 */} - - + + {/* 5단계 */} - - + + {/* 6단계 */} - - + + {/* 7단계 */} - - + + {/* 8단계 */} - - + + {/* 9단계 */} - +
물 재이용 추진 절차 상세 표
타당성 조사
↓
타당성 조사
↓
매칭시스템 신청사업계획(안) 수립
↓
매칭시스템 신청사업계획(안) 수립
↓
하수도정보시스템 신청국고지원 신청→기술검토
(한국환경공단)
↓
하수도정보시스템 신청국고지원 신청→기술검토(한국환경공단)
↓
국고지원 확정 통보
↓
국고지원 확정 통보
↓
사업계획 수립(실시설계)
↓
사업계획 수립(실시설계)
↓
(행정관) 기술검토 신청재원협의
(지방환경관서장)
→기술검토
(한국환경공단)
↓
(행정관) 기술검토 신청재원협의(지방환경관서장)→기술검토(한국환경공단)
↓
(행정관) 기술검토 신청설치승인
(시도지사)
→기술검토
(한국환경공단)
↓
(행정관) 기술검토 신청설치승인(시도지사)→기술검토(한국환경공단)
↓
공사시행
↓
공사시행
↓
(행정관) 기술검토 신청준공(국고정산)→기술검토
(한국환경공단)
(행정관) 기술검토 신청준공(국고정산)→기술검토(한국환경공단)
{/* 카드 2 - 물재이용 매칭 시스템 */} -
-

+
+

물재이용 매칭 시스템이란?

{/* 개요 */}
-

개요

-

+

개요

+

기후변화 및 지자체의 개발속도 차이에 따라 물 자원의 수요-공급 불균형이 발생하여 빗물, 중수도, 하수처리 재처리수 등의 공급 여유가 있는 지자체의 재이용수를 부족한 지자체 수요처에 공급하여 불균형 해소를 통한 수자원 절약 및 물 자원 순환

{/* 구성 */}
-

구성

-
    +

    구성

    +
    • 공급자: 하수처리장, 빗물이용시설, 중수도 등
    • 수요자: 산업체(냉각수), 공공시설(조경, 도로청소), 농민(농업용수) 등
    • 시스템: 수질, 수량, 거리 기반 매칭, 중개
    • @@ -80,16 +80,16 @@ export default function WrOverviewPage() { {/* 절차 */}
      -

      사업 주요 내용

      -

      +

      사업 주요 내용

      +

      ① 공급자 등록 → ② 수요자 조건 입력 → ③ 매칭 검토 → ④ 시스템 매칭 → ⑤ 계약·공급 이행

      {/* 기대효과 */}
      -

      기대효과

      -
        +

        기대효과

        +
        • 물 자원 수요-공급 조정을 통한 효율적 분배
        • 물 재이용시설 데이터 수집으로 체계적 관리
        • 지자체, 사업시행자 등 재이용 정보 획득 용이
        • @@ -102,7 +102,7 @@ export default function WrOverviewPage() { height={500} className="h-[500px] mx-auto mb-4 rounded" /> -

          (하수 재이용 시스템 개요도)

          +

          (하수 재이용 시스템 개요도)

diff --git a/apps/dm/app/pages/wrStatus/page.tsx b/apps/dm/app/pages/wrStatus/page.tsx index dd7ec5552..3fccefdb8 100644 --- a/apps/dm/app/pages/wrStatus/page.tsx +++ b/apps/dm/app/pages/wrStatus/page.tsx @@ -395,7 +395,7 @@ const WrStatusPage: React.FC = () => { }, [selectedRegion]); return ( -
+
{/* 우측 콘텐츠 영역 */}
{/* 상단 제목 */} @@ -455,19 +455,19 @@ const WrStatusPage: React.FC = () => {
- 하수재이용시설 (총 717건) + 하수재이용시설 (총 717개소수)
- 빗물이용시설 (총 3,730건) + 빗물이용시설 (총 3,730개소수)
- 중수도시설 (총 1,074건) + 중수도시설 (총 1,074개소수)
- 온배수시설 (총 201건) + 온배수시설 (총 201개소수)
@@ -522,7 +522,7 @@ const WrStatusPage: React.FC = () => {

재이용통계(2023)

-

항목별 통계 보기

+

행정구역별 재이용시설 현황 개소수 그래프

diff --git a/apps/dm/components/ui/sdmInfoInsertForm/component.tsx b/apps/dm/components/ui/sdmInfoInsertForm/component.tsx index 8c2273872..ddc666abe 100644 --- a/apps/dm/components/ui/sdmInfoInsertForm/component.tsx +++ b/apps/dm/components/ui/sdmInfoInsertForm/component.tsx @@ -420,7 +420,6 @@ export function SdmInfoRegForm({ form, onCheckboxChange, facilityName }: SdmInfo name: "rmrk", label: "비고", formIndex: 1, - required: true, component: (field) => ( state); const [isBznmTotNoDisabled, setIsBznmTotNoDisabled] = useState(false); + + const formatBusinessNumber = (value: string) => { + // 숫자만 추출 + const digitsOnly = value.replace(/\D/g, ""); + + // 10자리까지만 처리 + const limitedDigits = digitsOnly.slice(0, 10); + + // 3-2-5 형식으로 포맷팅 + if (limitedDigits.length <= 3) { + return limitedDigits; + } else if (limitedDigits.length <= 5) { + return `${limitedDigits.slice(0, 3)}-${limitedDigits.slice(3)}`; + } else { + return `${limitedDigits.slice(0, 3)}-${limitedDigits.slice(3, 5)}-${limitedDigits.slice(5)}`; + } + }; // 스키마를 useMemo로 최적화 const sdmLoginFormSchema = useMemo(() => z.object({ @@ -79,7 +96,12 @@ export function SdmLoginForm() { placeholder="사업자등록번호를 입력하세요" type="text" className="!mt-5 !mb-5 !text-xl text-gray-600" - {...field} + {...field} + value={formatBusinessNumber(field.value as string)} + onChange={(e) => { + const dmdId = e.target.value.replace(/\D/g, "").slice(0, 10); + field.onChange(dmdId); + }} /> ) }, diff --git a/apps/dm/tailwind.config.ts b/apps/dm/tailwind.config.ts index d3eabd953..42cfc69f5 100644 --- a/apps/dm/tailwind.config.ts +++ b/apps/dm/tailwind.config.ts @@ -3,64 +3,67 @@ import baseConfig from "../../packages/common/components/tailwind.config"; import type { Config } from "tailwindcss"; export default { - darkMode: ["class"], - presets: [baseConfig], - content: [ - "./app/**/*.{js,ts,jsx,tsx,mdx}", - "./components/**/*.{js,ts,jsx,tsx,mdx}", - "../../packages/common/components/src/ui/**/*.{js,ts,jsx,tsx,mdx}", - "../../packages/common/business_components/src/ui/**/*.{js,ts,jsx,tsx,mdx}", - ], - plugins: [require("tailwindcss-animate")], - theme: { - extend: { - borderRadius: { - lg: 'var(--radius)', - md: 'calc(var(--radius) - 2px)', - sm: 'calc(var(--radius) - 4px)' - }, - colors: { - background: 'hsl(var(--background))', - foreground: 'hsl(var(--foreground))', - card: { - DEFAULT: 'hsl(var(--card))', - foreground: 'hsl(var(--card-foreground))' - }, - popover: { - DEFAULT: 'hsl(var(--popover))', - foreground: 'hsl(var(--popover-foreground))' - }, - primary: { - DEFAULT: 'hsl(var(--primary))', - foreground: 'hsl(var(--primary-foreground))' - }, - secondary: { - DEFAULT: 'hsl(var(--secondary))', - foreground: 'hsl(var(--secondary-foreground))' - }, - muted: { - DEFAULT: 'hsl(var(--muted))', - foreground: 'hsl(var(--muted-foreground))' - }, - accent: { - DEFAULT: 'hsl(var(--accent))', - foreground: 'hsl(var(--accent-foreground))' - }, - destructive: { - DEFAULT: 'hsl(var(--destructive))', - foreground: 'hsl(var(--destructive-foreground))' - }, - border: 'hsl(var(--border))', - input: 'hsl(var(--input))', - ring: 'hsl(var(--ring))', - chart: { - '1': 'hsl(var(--chart-1))', - '2': 'hsl(var(--chart-2))', - '3': 'hsl(var(--chart-3))', - '4': 'hsl(var(--chart-4))', - '5': 'hsl(var(--chart-5))' - } - } - } - } + darkMode: ["class"], + presets: [baseConfig], + content: [ + "./app/**/*.{js,ts,jsx,tsx,mdx}", + "./components/**/*.{js,ts,jsx,tsx,mdx}", + "../../packages/common/components/src/ui/**/*.{js,ts,jsx,tsx,mdx}", + "../../packages/common/business_components/src/ui/**/*.{js,ts,jsx,tsx,mdx}", + ], + plugins: [require("tailwindcss-animate")], + theme: { + extend: { + fontFamily: { + NotoSansKR: ['NotoSansKR'], + }, + borderRadius: { + lg: 'var(--radius)', + md: 'calc(var(--radius) - 2px)', + sm: 'calc(var(--radius) - 4px)' + }, + colors: { + background: 'hsl(var(--background))', + foreground: 'hsl(var(--foreground))', + card: { + DEFAULT: 'hsl(var(--card))', + foreground: 'hsl(var(--card-foreground))' + }, + popover: { + DEFAULT: 'hsl(var(--popover))', + foreground: 'hsl(var(--popover-foreground))' + }, + primary: { + DEFAULT: 'hsl(var(--primary))', + foreground: 'hsl(var(--primary-foreground))' + }, + secondary: { + DEFAULT: 'hsl(var(--secondary))', + foreground: 'hsl(var(--secondary-foreground))' + }, + muted: { + DEFAULT: 'hsl(var(--muted))', + foreground: 'hsl(var(--muted-foreground))' + }, + accent: { + DEFAULT: 'hsl(var(--accent))', + foreground: 'hsl(var(--accent-foreground))' + }, + destructive: { + DEFAULT: 'hsl(var(--destructive))', + foreground: 'hsl(var(--destructive-foreground))' + }, + border: 'hsl(var(--border))', + input: 'hsl(var(--input))', + ring: 'hsl(var(--ring))', + chart: { + '1': 'hsl(var(--chart-1))', + '2': 'hsl(var(--chart-2))', + '3': 'hsl(var(--chart-3))', + '4': 'hsl(var(--chart-4))', + '5': 'hsl(var(--chart-5))' + } + } + } + } } satisfies Config; From 4c8ecb05e094965df93dcf474bffa79c5135e61e Mon Sep 17 00:00:00 2001 From: leedj312 Date: Thu, 24 Jul 2025 19:51:48 +0900 Subject: [PATCH 07/21] =?UTF-8?q?=EC=84=9C=EC=B9=98=EB=B0=94=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80=20=EC=99=84?= =?UTF-8?q?=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/dm/app/page.tsx | 3 + apps/dm/app/pages/map/mainMap/page.tsx | 24 ++- apps/dm/app/store/map/index.tsx | 85 +++++++++++ apps/dm/app/store/map/type.tsx | 34 +++++ apps/dm/app/store/sdmMng/index.tsx | 2 - apps/dm/app/store/store.tsx | 10 +- .../map/SearchInputText/SearchInputText.tsx | 108 ++++++++++++++ .../components/ui/map/searchBar/searchBar.tsx | 138 +++++++++++++++++- 8 files changed, 389 insertions(+), 15 deletions(-) create mode 100644 apps/dm/app/store/map/index.tsx create mode 100644 apps/dm/app/store/map/type.tsx create mode 100644 apps/dm/components/ui/map/SearchInputText/SearchInputText.tsx diff --git a/apps/dm/app/page.tsx b/apps/dm/app/page.tsx index fdb323086..a5f49f0bc 100644 --- a/apps/dm/app/page.tsx +++ b/apps/dm/app/page.tsx @@ -30,6 +30,9 @@ export default function Home() { +
); diff --git a/apps/dm/app/pages/map/mainMap/page.tsx b/apps/dm/app/pages/map/mainMap/page.tsx index f1def0310..ca33e5142 100644 --- a/apps/dm/app/pages/map/mainMap/page.tsx +++ b/apps/dm/app/pages/map/mainMap/page.tsx @@ -1,13 +1,27 @@ "use client"; -const Map = () => { - +import { SearchBar } from "@/components/ui/map/searchBar/searchBar"; +import { useEffect, useState } from "react"; + +const Map = () => { + + const [selectDataPk, setSelectDataPk] = useState ('') + + // 삭제요망 데이터 선ㅌ + useEffect(() =>{ + console.log("Selected Data Pk: ", selectDataPk); + },[selectDataPk]) return ( <> - - + { + console.log("Selected HasuPk: ", hasuPk); + if(hasuPk !==""){ + setSelectDataPk(hasuPk); + } + }} + /> - ); }; diff --git a/apps/dm/app/store/map/index.tsx b/apps/dm/app/store/map/index.tsx new file mode 100644 index 000000000..8ed8fce40 --- /dev/null +++ b/apps/dm/app/store/map/index.tsx @@ -0,0 +1,85 @@ +import { getFetch } from "@/components/ui/api"; +import { DEV_URL_FO } from "@/components/ui/config"; +import type { FacilityInfo } from "./type"; + +const checkWrRegList = async () => { + const wrRegList = await getFetch("wr", `/reuseFc/pubFc`); + return wrRegList; +}; + +const getRuseFclt = async ( + page: string, + sggOrCity: string, + searchTxt?: string +) => { + const checkWrRegListRes = await checkWrRegList(); + + let url = `/prcsFclt/ruseInfo/${page ? page : 1}/11/A/${sggOrCity ? sggOrCity : "11"}/0/1/SW`; + + console.log(' checkWrRegListRes ', checkWrRegListRes) + const ruseList = checkWrRegListRes.data.filter((item: string) => item !== ""); + + if (searchTxt) { + url = url + `/${searchTxt}`; + } + + const response = await fetch(DEV_URL_FO + url, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(ruseList), + }); + + const hasuList = await response.json().then((res: any) => { + + const data =res.data + .map((item: any) => { + return { + ...item, + fctCpc: Number(item.fctCpc) || 0, + searchNm: item.fctNm, + searchPk: item.bscFctNm, + + }; + }) + .filter((hasu) => + ruseList.find((ruse) => hasu.bscFctCd === ruse) + ); + return{ data: data, count: res.count }; + + }); + return hasuList +}; + +export type DmMapActions = { + findRuseFclt(sggOrCity:string, searchTxt: string): void; +}; + +export type DmMapType = { + map: { + ruseFcltList: FacilityInfo[]; + }; +}; + +export const dmMapState: DmMapType = { + map: { + ruseFcltList: [], + }, +}; +export type DmMapStore = DmMapType & DmMapActions; + +export const DmMapReducer = (set: any): DmMapActions => { + return { + findRuseFclt: async (sggOrCity:string, searchTxt: string) => { + const ruseFcltList = await getRuseFclt('1', sggOrCity,searchTxt); + + set((state: DmMapType) => ({ + map: { + ...state.map, + ruseFcltList :ruseFcltList.data + }, + })); + }, + }; +}; diff --git a/apps/dm/app/store/map/type.tsx b/apps/dm/app/store/map/type.tsx new file mode 100644 index 000000000..4d6cea0ac --- /dev/null +++ b/apps/dm/app/store/map/type.tsx @@ -0,0 +1,34 @@ +export type FacilityInfo= { + bscFctCd: string; // 기본시설 코드 + bscFctNm: string; // 기본시설명 + rvbsnCd: string; // 유역 코드 + rvbsn: string; // 유역명 + lgbsn: string; // 대권역명 + mdmbsn: string; // 중권역명 + rgnCd: string; // 관할청 코드 + rgnNm: string; // 관할청 명칭 + adstDstrCd: string; // 행정구역 코드 + sido: string; // 시도 + gugun: string; // 구군 + addr: string; // 주소 + fctCpc: string; // 시설용량 + efflWqltStndrdCd: string; // 방류수 수질기준 코드 + efflWqltStndrdNm: string; // 방류수 수질기준명 + trtFrfNm: string; // 처리공법명 + trtDstr: string; // 처리구역 + rnDt: string; // 사용개시일 (예: YYYYMMDD) + cmplDt: string; // 준공일자 (예: YYYYMMDD) + trstOprEtpNm: string; // 위탁운영기관명 + bscFctClsNm: string; // 기본시설 구분명 + sitArea: string; // 부지면적 + trtDstrArea: string; // 처리구역 면적 + swrTrtPopl: string; // 하수처리 인구 + mnstr: string; // 총괄 유역 + efflWtrrgn: string; // 방류수역 + rvNm: string; // 하천명 + bscFctClsCd: string; // 기본시설 구분 코드 + rvbsnNm: string; // 유역명 + tel: string; // 전화번호 + rswtrYn: string; // 재이용수 여부 ("Y" 또는 "N") + ruse: boolean; // 재이용 여부 (boolean) + } \ No newline at end of file diff --git a/apps/dm/app/store/sdmMng/index.tsx b/apps/dm/app/store/sdmMng/index.tsx index e51c6b56a..8e487543f 100644 --- a/apps/dm/app/store/sdmMng/index.tsx +++ b/apps/dm/app/store/sdmMng/index.tsx @@ -387,11 +387,9 @@ export const SdmState: SdmType = { similarCaseList: similarCaseList, upHeadMoneyDetailList : moneyDetailModalHeader, moneyDetailList: moneyDetailList, - // 수요정보 신청현황 sdmInfoStatusList: [], upHeadSdmInfoStatus: upHeadSdmInfoStatus, - // 로그인관련 successNextPage : false, successLogin: false, diff --git a/apps/dm/app/store/store.tsx b/apps/dm/app/store/store.tsx index 66b626aa3..7107012d9 100644 --- a/apps/dm/app/store/store.tsx +++ b/apps/dm/app/store/store.tsx @@ -1,19 +1,23 @@ import { createStore } from 'zustand/vanilla'; import { CommonReducer, CommonState, type CommonStore, type CommonType } from './common'; import { SdmReducer, SdmState, type SdmStore, type SdmType } from './sdmMng'; +import { dmMapState, type DmMapType, type DmMapStore, DmMapReducer } from './map'; -export type DmStore = CommonStore & SdmStore; -export type DmState = CommonType & SdmType; +export type DmStore = CommonStore & SdmStore &DmMapStore ; +export type DmState = CommonType & SdmType &DmMapType; export const StoreZus = (initState: DmState= { ...CommonState, ...SdmState, + ...dmMapState, }) => { return createStore()((set) => { + return { ...initState, ...CommonReducer(set), - ...SdmReducer(set) + ...SdmReducer(set), + ...DmMapReducer(set) } }) } \ No newline at end of file diff --git a/apps/dm/components/ui/map/SearchInputText/SearchInputText.tsx b/apps/dm/components/ui/map/SearchInputText/SearchInputText.tsx new file mode 100644 index 000000000..3660891f1 --- /dev/null +++ b/apps/dm/components/ui/map/SearchInputText/SearchInputText.tsx @@ -0,0 +1,108 @@ +import React, { useCallback, useEffect, useRef, useState } from "react"; + +type SearchType = { + searchNm: string; + searchPk: string; +}; +type thisProps = { + label: string | undefined; + initValue: string | undefined; + setValue: (searchNm?: string) => void; + setRtnData: (rtnData: any) => void; + searchList?: any[]; +}; +export default function SearchInputText({ + label = undefined, + initValue = undefined, + setValue = () => {}, + setRtnData = () => {}, + searchList = undefined, +}: thisProps) { + + const [isOpen, setIsOpen] = useState(false); + const [textValue, setTextValue] = useState(initValue || ""); + + const wrapperRef = useRef(null); + const handleInputChange = useCallback( + (e: React.ChangeEvent) => { + const value = e.target.value; + + setTextValue(value); + setValue(value); + if (value && value!=='') { + setIsOpen(true); + } else { + setIsOpen(false); + } + }, + [setValue] + ); + + const handleSuggestionClick = useCallback( + (searchNm: string) => { + // Do not update textValue to keep input unchanged on suggestion click + setValue(searchNm); + setIsOpen(false); + setRtnData(searchList?.find((item) => item.searchNm === searchNm) || {}); + }, + [searchList, setRtnData, setValue] + ); + + useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + if ( + wrapperRef.current && + !wrapperRef.current.contains(event.target as Node) + ) { + setIsOpen(false); + } + }; + + document.addEventListener("mousedown", handleClickOutside); + return () => { + document.removeEventListener("mousedown", handleClickOutside); + }; + }, []); + + return ( +
+ setIsOpen(true)} */ + className="w-full h-[3.2rem] !text-xl pl-3 text-gray-600 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" + placeholder={`${label}을(를) 입력하세요`} + /> + {isOpen && ( +
+
+ {searchList && searchList.length > 0 ? ( + searchList.map((item: SearchType, index: number) => ( +
handleSuggestionClick(item.searchNm)} + role="button" + tabIndex={0} + onKeyDown={(e) => { + if (e.key === "Enter" || e.key === " ") { + handleSuggestionClick(item.searchNm); + } + }} + > + {item.searchPk?.substring(0, 10)}  + {item.searchNm} +
+ )) + ) : ( +
+ 검색 결과가 없습니다. +
+ )} +
+
+ )} +
+ ); +} diff --git a/apps/dm/components/ui/map/searchBar/searchBar.tsx b/apps/dm/components/ui/map/searchBar/searchBar.tsx index f6af413ef..61c9fb6d6 100644 --- a/apps/dm/components/ui/map/searchBar/searchBar.tsx +++ b/apps/dm/components/ui/map/searchBar/searchBar.tsx @@ -1,6 +1,134 @@ -export function SearchBar() { - return ( - <> - +"use client"; +import { useDmStore } from "@/app/store"; +import { + SearchDiv, + SearchFormLeft, + SearchFormRight, + SelectBox, + type SelectDataType, +} from "@common/business_components/ui"; +import { Label } from "@common/components/ui"; +import { useCallback, useEffect, useState } from "react"; +import SearchInputText from "../SearchInputText/SearchInputText"; + +export function SearchBar({ + + setHasuPk +}:{ + setHasuPk: (hasuPk: string) => void, +} + +) { + const {map, Common, initializeCommonData ,findRuseFclt} = useDmStore((state) => state); + const [sidoValue, setSidoValue] = useState(""); + const [sigunValue, setSigunValue] = useState(""); + const [filteredSigunData, setFilteredSigunData] = useState([ + { text: "전체", val: "0" }, + ]); + useEffect(() => { + initializeCommonData(); + }, [initializeCommonData]); + + const initializeSigunData = useCallback( + (sidoCd: string) => { + if (sidoCd === "0") { + setSigunValue("0"); + setFilteredSigunData([{ text: "전체", val: "0" }]); + } else { + const newFilteredData = [ + { text: "전체", val: "0" }, + ...Common.selectSigunData.filter( + (item: SelectDataType) => item.val.substring(0, 2) === sidoCd + ), + ]; + setSigunValue("0"); + setFilteredSigunData(newFilteredData); + } + }, + [Common.selectSigunData] ); -} \ No newline at end of file + const onSelectValue = useCallback( + (val: string, type: string) => { + switch (type) { + case "ctpvCd": + initializeSigunData(val); + setSidoValue(val); + break; + case "sggCd": + setSigunValue(val); + } + }, + [initializeSigunData] + ); + + +useEffect(()=>{ + console.log('map.ruseFcltList ', map.ruseFcltList) +},[map.ruseFcltList]) + + return ( + <> +
+
+ + +
+ + onSelectValue(val.toString(), "ctpvCd") + } + value={sidoValue|| Common.selectSidoData[0]?.val || ""} + /> + + onSelectValue(val.toString(), "sggCd") + } + value={sigunValue} + /> +
+ + { + let sggOrCity='' + if(searchNm){ + if(sigunValue === "0" || sigunValue === "") { + sggOrCity = sidoValue; + }else{ + sggOrCity = sigunValue; + } + findRuseFclt(sggOrCity, searchNm); + } + }} + setRtnData={(rtnData: any)=> { + setHasuPk(rtnData.bscFctCd || ""); + }} + searchList={map.ruseFcltList || []} + /> +
+
+
+ +
+ {/* */} +
+
+
+
+
+ + ); +} From 7cd1ae262524023c4e47734e50fd4ee45648ea0c Mon Sep 17 00:00:00 2001 From: yschoi Date: Thu, 24 Jul 2025 20:47:35 +0900 Subject: [PATCH 08/21] =?UTF-8?q?gis=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/dm/.env.local | 13 +- apps/dm/app/pages/map/popup/sdmInfoMap.tsx | 132 + apps/dm/app/pages/map/popup/windowPotal.tsx | 94 + apps/dm/app/pages/map/service/index.ts | 1 + apps/dm/app/pages/map/service/map.ts | 46 + .../ui/sdmInfoInsertForm/component.tsx | 237 +- .../images/map/layericon/BML_CBAS_PS.png | Bin 0 -> 1547 bytes .../images/map/layericon/BML_ELEV_PS.png | Bin 0 -> 1030 bytes .../images/map/layericon/BML_ETCP_PS.png | Bin 0 -> 961 bytes .../images/map/layericon/BML_STMS_PS.png | Bin 0 -> 1542 bytes .../images/map/layericon/BML_SYMB_PS.png | Bin 0 -> 970 bytes .../images/map/layericon/RDL_BYST_PS.png | Bin 0 -> 1196 bytes .../images/map/layericon/RDL_CROS_PS.png | Bin 0 -> 1083 bytes .../images/map/layericon/RDL_ETCT_PS.png | Bin 0 -> 1016 bytes .../images/map/layericon/RDL_OCUP_PS.png | Bin 0 -> 1061 bytes .../images/map/layericon/RDL_RDSN_PS.png | Bin 0 -> 1313 bytes .../images/map/layericon/RDL_SCLT_PS.png | Bin 0 -> 1072 bytes .../images/map/layericon/RDL_SEPC_PS.png | Bin 0 -> 1068 bytes .../images/map/layericon/RDL_STAT_PS.png | Bin 0 -> 1075 bytes .../images/map/layericon/RDL_STLT_PS.png | Bin 0 -> 1089 bytes .../images/map/layericon/RDL_TFSN_PS.png | Bin 0 -> 1291 bytes .../images/map/layericon/RDL_TRCT_PS.png | Bin 0 -> 1412 bytes .../images/map/layericon/RDL_TREE_PS.png | Bin 0 -> 1136 bytes .../images/map/layericon/RDL_TRSN_PS.png | Bin 0 -> 1375 bytes .../images/map/layericon/RDL_TSCT_PS.png | Bin 0 -> 1700 bytes .../images/map/layericon/SWL_CLAY_PS.png | Bin 0 -> 1146 bytes .../images/map/layericon/SWL_CLAY_PS_RED.png | Bin 0 -> 468 bytes .../images/map/layericon/SWL_DEPT_PS.png | Bin 0 -> 1079 bytes .../images/map/layericon/SWL_MANH_PS.png | Bin 0 -> 1559 bytes .../map/layericon/SWL_MANH_PS_BLACK.png | Bin 0 -> 757 bytes .../images/map/layericon/SWL_MANH_PS_BLUE.png | Bin 0 -> 785 bytes .../images/map/layericon/SWL_MANH_PS_GRAY.png | Bin 0 -> 1054 bytes .../map/layericon/SWL_MANH_PS_GREEN.png | Bin 0 -> 791 bytes .../map/layericon/SWL_MANH_PS_ORANGE.png | Bin 0 -> 455 bytes .../images/map/layericon/SWL_MANH_PS_RED.png | Bin 0 -> 782 bytes .../map/layericon/SWL_MANH_PS_YELLOW.png | Bin 0 -> 782 bytes .../images/map/layericon/SWL_SPEW_PS.png | Bin 0 -> 1217 bytes .../images/map/layericon/SWL_SPOT_PS.png | Bin 0 -> 1067 bytes .../map/layericon/SWL_SPOT_PS_BLACK.png | Bin 0 -> 217 bytes .../images/map/layericon/SWL_SPOT_PS_BLUE.png | Bin 0 -> 217 bytes .../map/layericon/SWL_SPOT_PS_GREEN.png | Bin 0 -> 217 bytes .../images/map/layericon/SWL_SPOT_PS_GREY.png | Bin 0 -> 217 bytes .../images/map/layericon/SWL_SPOT_PS_RED.png | Bin 0 -> 217 bytes .../images/map/layericon/SWL_VENT_PS.png | Bin 0 -> 1033 bytes .../images/map/layericon/TL_SPBD_ENTRC.png | Bin 0 -> 1260 bytes .../images/map/layericon/TL_SPGF_LOC.png | Bin 0 -> 1383 bytes .../images/map/layericon/TL_SPOT_NOIN.png | Bin 0 -> 1009 bytes .../images/map/layericon/TL_SPRD_BANO.png | Bin 0 -> 1044 bytes .../images/map/layericon/TL_SPRD_CRSRD.png | Bin 0 -> 1083 bytes .../images/map/layericon/TL_SPRD_NONM.png | Bin 0 -> 1049 bytes .../images/map/layericon/TL_SPRD_RENM.png | Bin 0 -> 1009 bytes .../images/map/layericon/TL_SPRD_RWNM.png | Bin 0 -> 1049 bytes .../images/map/layericon/UFL_BMAN_PS.png | Bin 0 -> 1552 bytes .../images/map/layericon/UFL_BOPC_PS.png | Bin 0 -> 1132 bytes .../images/map/layericon/UFL_BPIP_PS.png | Bin 0 -> 1017 bytes .../images/map/layericon/UFL_BPOL_PS.png | Bin 0 -> 1193 bytes .../images/map/layericon/UFL_BTRS_PS.png | Bin 0 -> 1097 bytes .../images/map/layericon/UFL_GMNT_PS.png | Bin 0 -> 1166 bytes .../images/map/layericon/UFL_GPIP_PS.png | Bin 0 -> 1103 bytes .../images/map/layericon/UFL_GPRS_PS.png | Bin 0 -> 1408 bytes .../images/map/layericon/UFL_GVAL_PS.png | Bin 0 -> 1426 bytes .../images/map/layericon/UFL_KMAN_PS.png | Bin 0 -> 1572 bytes .../images/map/layericon/UFL_KPOL_PS.png | Bin 0 -> 1122 bytes .../images/map/layericon/UFL_VALV_PS.png | Bin 0 -> 1058 bytes .../images/map/layericon/WTL_FIRE_PS.png | Bin 0 -> 1072 bytes .../images/map/layericon/WTL_FLOW_PS.png | Bin 0 -> 1137 bytes .../images/map/layericon/WTL_MANH_PS.png | Bin 0 -> 1557 bytes .../images/map/layericon/WTL_META_PS.png | Bin 0 -> 1100 bytes .../images/map/layericon/WTL_PIPE_PS.png | Bin 0 -> 1075 bytes .../images/map/layericon/WTL_RSRV_PS.png | Bin 0 -> 1057 bytes .../map/layericon/WTL_RSRV_PS_BLACK.png | Bin 0 -> 167 bytes .../images/map/layericon/WTL_RSRV_PS_GRAY.png | Bin 0 -> 259 bytes .../map/layericon/WTL_RSRV_PS_GREEN.png | Bin 0 -> 167 bytes .../map/layericon/WTL_RSRV_PS_ORANGE.png | Bin 0 -> 164 bytes .../map/layericon/WTL_RSRV_PS_PURPLE.png | Bin 0 -> 165 bytes .../images/map/layericon/WTL_RSRV_PS_RED.png | Bin 0 -> 165 bytes .../map/layericon/WTL_RSRV_PS_YELLOW.png | Bin 0 -> 163 bytes .../images/map/layericon/WTL_VALV_PS.png | Bin 0 -> 1077 bytes .../public/images/map/layericon/default.png | Bin 0 -> 160 bytes apps/dm/public/images/map/layericon/reset.png | Bin 0 -> 108 bytes .../public/images/map/toolbar/arrow_down.png | Bin 0 -> 1532 bytes .../public/images/map/toolbar/arrow_right.png | Bin 0 -> 1931 bytes .../images/map/toolbar/backward_icon.png | Bin 0 -> 313 bytes .../images/map/toolbar/btn_cont_area.png | Bin 0 -> 1231 bytes .../images/map/toolbar/btn_cont_area_on.png | Bin 0 -> 1184 bytes .../images/map/toolbar/btn_cont_next.png | Bin 0 -> 1256 bytes .../images/map/toolbar/btn_cont_next_on.png | Bin 0 -> 1204 bytes .../images/map/toolbar/btn_cont_prev.png | Bin 0 -> 1265 bytes .../images/map/toolbar/btn_cont_prev_on.png | Bin 0 -> 1223 bytes .../images/map/toolbar/btn_cont_print.png | Bin 0 -> 1318 bytes .../images/map/toolbar/btn_cont_print_on.png | Bin 0 -> 1293 bytes .../images/map/toolbar/btn_cont_ruler.png | Bin 0 -> 1353 bytes .../images/map/toolbar/btn_cont_ruler_on.png | Bin 0 -> 1339 bytes .../images/map/toolbar/btn_cont_save.png | Bin 0 -> 1216 bytes .../images/map/toolbar/btn_cont_save_on.png | Bin 0 -> 1202 bytes .../images/map/toolbar/btn_cont_sketch.png | Bin 0 -> 15103 bytes .../images/map/toolbar/btn_cont_sketch_on.png | Bin 0 -> 14933 bytes .../images/map/toolbar/btn_cont_zoomin.png | Bin 0 -> 1456 bytes .../images/map/toolbar/btn_cont_zoomin_on.png | Bin 0 -> 1423 bytes .../images/map/toolbar/btn_cont_zoomout.png | Bin 0 -> 1395 bytes .../map/toolbar/btn_cont_zoomout_on.png | Bin 0 -> 1376 bytes .../images/map/toolbar/btn_edit_add.png | Bin 0 -> 1169 bytes .../images/map/toolbar/btn_edit_add_on.png | Bin 0 -> 1168 bytes .../images/map/toolbar/btn_edit_del.png | Bin 0 -> 1160 bytes .../images/map/toolbar/btn_edit_del_on.png | Bin 0 -> 1158 bytes .../images/map/toolbar/btn_edit_next.png | Bin 0 -> 1209 bytes .../images/map/toolbar/btn_edit_next_on.png | Bin 0 -> 1205 bytes .../images/map/toolbar/btn_edit_prev.png | Bin 0 -> 1172 bytes .../images/map/toolbar/btn_edit_prev_on.png | Bin 0 -> 1176 bytes .../images/map/toolbar/btn_edit_save.png | Bin 0 -> 1180 bytes .../images/map/toolbar/btn_edit_save_on.png | Bin 0 -> 1190 bytes .../images/map/toolbar/btn_edit_slt.png | Bin 0 -> 1288 bytes .../images/map/toolbar/btn_edit_slt_on.png | Bin 0 -> 1274 bytes .../images/map/toolbar/btn_edit_snap.png | Bin 0 -> 1185 bytes .../images/map/toolbar/btn_edit_snap_on.png | Bin 0 -> 1185 bytes .../public/images/map/toolbar/close_icon.png | Bin 0 -> 313 bytes .../images/map/toolbar/ico_gis_sprite.png | Bin 0 -> 7858 bytes .../images/map/toolbar/icon-viewclose.png | Bin 0 -> 2895 bytes .../images/map/toolbar/img_base_map.png | Bin 0 -> 4537 bytes .../images/map/toolbar/img_network_map.png | Bin 0 -> 4974 bytes .../images/map/toolbar/img_road_map.png | Bin 0 -> 6079 bytes .../images/map/toolbar/img_settle_map.png | Bin 0 -> 7264 bytes apps/dm/public/images/map/toolbar/loading.gif | Bin 0 -> 184974 bytes apps/dm/public/images/map/toolbar/map_pin.png | Bin 0 -> 2362 bytes .../public/images/map/toolbar/toggle_off.png | Bin 0 -> 1591 bytes .../public/images/map/toolbar/toggle_on.png | Bin 0 -> 1612 bytes pnpm-lock.yaml | 11095 ++++++++++++++++ 127 files changed, 11516 insertions(+), 102 deletions(-) create mode 100644 apps/dm/app/pages/map/popup/sdmInfoMap.tsx create mode 100644 apps/dm/app/pages/map/popup/windowPotal.tsx create mode 100644 apps/dm/app/pages/map/service/index.ts create mode 100644 apps/dm/app/pages/map/service/map.ts create mode 100644 apps/dm/public/images/map/layericon/BML_CBAS_PS.png create mode 100644 apps/dm/public/images/map/layericon/BML_ELEV_PS.png create mode 100644 apps/dm/public/images/map/layericon/BML_ETCP_PS.png create mode 100644 apps/dm/public/images/map/layericon/BML_STMS_PS.png create mode 100644 apps/dm/public/images/map/layericon/BML_SYMB_PS.png create mode 100644 apps/dm/public/images/map/layericon/RDL_BYST_PS.png create mode 100644 apps/dm/public/images/map/layericon/RDL_CROS_PS.png create mode 100644 apps/dm/public/images/map/layericon/RDL_ETCT_PS.png create mode 100644 apps/dm/public/images/map/layericon/RDL_OCUP_PS.png create mode 100644 apps/dm/public/images/map/layericon/RDL_RDSN_PS.png create mode 100644 apps/dm/public/images/map/layericon/RDL_SCLT_PS.png create mode 100644 apps/dm/public/images/map/layericon/RDL_SEPC_PS.png create mode 100644 apps/dm/public/images/map/layericon/RDL_STAT_PS.png create mode 100644 apps/dm/public/images/map/layericon/RDL_STLT_PS.png create mode 100644 apps/dm/public/images/map/layericon/RDL_TFSN_PS.png create mode 100644 apps/dm/public/images/map/layericon/RDL_TRCT_PS.png create mode 100644 apps/dm/public/images/map/layericon/RDL_TREE_PS.png create mode 100644 apps/dm/public/images/map/layericon/RDL_TRSN_PS.png create mode 100644 apps/dm/public/images/map/layericon/RDL_TSCT_PS.png create mode 100644 apps/dm/public/images/map/layericon/SWL_CLAY_PS.png create mode 100644 apps/dm/public/images/map/layericon/SWL_CLAY_PS_RED.png create mode 100644 apps/dm/public/images/map/layericon/SWL_DEPT_PS.png create mode 100644 apps/dm/public/images/map/layericon/SWL_MANH_PS.png create mode 100644 apps/dm/public/images/map/layericon/SWL_MANH_PS_BLACK.png create mode 100644 apps/dm/public/images/map/layericon/SWL_MANH_PS_BLUE.png create mode 100644 apps/dm/public/images/map/layericon/SWL_MANH_PS_GRAY.png create mode 100644 apps/dm/public/images/map/layericon/SWL_MANH_PS_GREEN.png create mode 100644 apps/dm/public/images/map/layericon/SWL_MANH_PS_ORANGE.png create mode 100644 apps/dm/public/images/map/layericon/SWL_MANH_PS_RED.png create mode 100644 apps/dm/public/images/map/layericon/SWL_MANH_PS_YELLOW.png create mode 100644 apps/dm/public/images/map/layericon/SWL_SPEW_PS.png create mode 100644 apps/dm/public/images/map/layericon/SWL_SPOT_PS.png create mode 100644 apps/dm/public/images/map/layericon/SWL_SPOT_PS_BLACK.png create mode 100644 apps/dm/public/images/map/layericon/SWL_SPOT_PS_BLUE.png create mode 100644 apps/dm/public/images/map/layericon/SWL_SPOT_PS_GREEN.png create mode 100644 apps/dm/public/images/map/layericon/SWL_SPOT_PS_GREY.png create mode 100644 apps/dm/public/images/map/layericon/SWL_SPOT_PS_RED.png create mode 100644 apps/dm/public/images/map/layericon/SWL_VENT_PS.png create mode 100644 apps/dm/public/images/map/layericon/TL_SPBD_ENTRC.png create mode 100644 apps/dm/public/images/map/layericon/TL_SPGF_LOC.png create mode 100644 apps/dm/public/images/map/layericon/TL_SPOT_NOIN.png create mode 100644 apps/dm/public/images/map/layericon/TL_SPRD_BANO.png create mode 100644 apps/dm/public/images/map/layericon/TL_SPRD_CRSRD.png create mode 100644 apps/dm/public/images/map/layericon/TL_SPRD_NONM.png create mode 100644 apps/dm/public/images/map/layericon/TL_SPRD_RENM.png create mode 100644 apps/dm/public/images/map/layericon/TL_SPRD_RWNM.png create mode 100644 apps/dm/public/images/map/layericon/UFL_BMAN_PS.png create mode 100644 apps/dm/public/images/map/layericon/UFL_BOPC_PS.png create mode 100644 apps/dm/public/images/map/layericon/UFL_BPIP_PS.png create mode 100644 apps/dm/public/images/map/layericon/UFL_BPOL_PS.png create mode 100644 apps/dm/public/images/map/layericon/UFL_BTRS_PS.png create mode 100644 apps/dm/public/images/map/layericon/UFL_GMNT_PS.png create mode 100644 apps/dm/public/images/map/layericon/UFL_GPIP_PS.png create mode 100644 apps/dm/public/images/map/layericon/UFL_GPRS_PS.png create mode 100644 apps/dm/public/images/map/layericon/UFL_GVAL_PS.png create mode 100644 apps/dm/public/images/map/layericon/UFL_KMAN_PS.png create mode 100644 apps/dm/public/images/map/layericon/UFL_KPOL_PS.png create mode 100644 apps/dm/public/images/map/layericon/UFL_VALV_PS.png create mode 100644 apps/dm/public/images/map/layericon/WTL_FIRE_PS.png create mode 100644 apps/dm/public/images/map/layericon/WTL_FLOW_PS.png create mode 100644 apps/dm/public/images/map/layericon/WTL_MANH_PS.png create mode 100644 apps/dm/public/images/map/layericon/WTL_META_PS.png create mode 100644 apps/dm/public/images/map/layericon/WTL_PIPE_PS.png create mode 100644 apps/dm/public/images/map/layericon/WTL_RSRV_PS.png create mode 100644 apps/dm/public/images/map/layericon/WTL_RSRV_PS_BLACK.png create mode 100644 apps/dm/public/images/map/layericon/WTL_RSRV_PS_GRAY.png create mode 100644 apps/dm/public/images/map/layericon/WTL_RSRV_PS_GREEN.png create mode 100644 apps/dm/public/images/map/layericon/WTL_RSRV_PS_ORANGE.png create mode 100644 apps/dm/public/images/map/layericon/WTL_RSRV_PS_PURPLE.png create mode 100644 apps/dm/public/images/map/layericon/WTL_RSRV_PS_RED.png create mode 100644 apps/dm/public/images/map/layericon/WTL_RSRV_PS_YELLOW.png create mode 100644 apps/dm/public/images/map/layericon/WTL_VALV_PS.png create mode 100644 apps/dm/public/images/map/layericon/default.png create mode 100644 apps/dm/public/images/map/layericon/reset.png create mode 100644 apps/dm/public/images/map/toolbar/arrow_down.png create mode 100644 apps/dm/public/images/map/toolbar/arrow_right.png create mode 100644 apps/dm/public/images/map/toolbar/backward_icon.png create mode 100644 apps/dm/public/images/map/toolbar/btn_cont_area.png create mode 100644 apps/dm/public/images/map/toolbar/btn_cont_area_on.png create mode 100644 apps/dm/public/images/map/toolbar/btn_cont_next.png create mode 100644 apps/dm/public/images/map/toolbar/btn_cont_next_on.png create mode 100644 apps/dm/public/images/map/toolbar/btn_cont_prev.png create mode 100644 apps/dm/public/images/map/toolbar/btn_cont_prev_on.png create mode 100644 apps/dm/public/images/map/toolbar/btn_cont_print.png create mode 100644 apps/dm/public/images/map/toolbar/btn_cont_print_on.png create mode 100644 apps/dm/public/images/map/toolbar/btn_cont_ruler.png create mode 100644 apps/dm/public/images/map/toolbar/btn_cont_ruler_on.png create mode 100644 apps/dm/public/images/map/toolbar/btn_cont_save.png create mode 100644 apps/dm/public/images/map/toolbar/btn_cont_save_on.png create mode 100644 apps/dm/public/images/map/toolbar/btn_cont_sketch.png create mode 100644 apps/dm/public/images/map/toolbar/btn_cont_sketch_on.png create mode 100644 apps/dm/public/images/map/toolbar/btn_cont_zoomin.png create mode 100644 apps/dm/public/images/map/toolbar/btn_cont_zoomin_on.png create mode 100644 apps/dm/public/images/map/toolbar/btn_cont_zoomout.png create mode 100644 apps/dm/public/images/map/toolbar/btn_cont_zoomout_on.png create mode 100644 apps/dm/public/images/map/toolbar/btn_edit_add.png create mode 100644 apps/dm/public/images/map/toolbar/btn_edit_add_on.png create mode 100644 apps/dm/public/images/map/toolbar/btn_edit_del.png create mode 100644 apps/dm/public/images/map/toolbar/btn_edit_del_on.png create mode 100644 apps/dm/public/images/map/toolbar/btn_edit_next.png create mode 100644 apps/dm/public/images/map/toolbar/btn_edit_next_on.png create mode 100644 apps/dm/public/images/map/toolbar/btn_edit_prev.png create mode 100644 apps/dm/public/images/map/toolbar/btn_edit_prev_on.png create mode 100644 apps/dm/public/images/map/toolbar/btn_edit_save.png create mode 100644 apps/dm/public/images/map/toolbar/btn_edit_save_on.png create mode 100644 apps/dm/public/images/map/toolbar/btn_edit_slt.png create mode 100644 apps/dm/public/images/map/toolbar/btn_edit_slt_on.png create mode 100644 apps/dm/public/images/map/toolbar/btn_edit_snap.png create mode 100644 apps/dm/public/images/map/toolbar/btn_edit_snap_on.png create mode 100644 apps/dm/public/images/map/toolbar/close_icon.png create mode 100644 apps/dm/public/images/map/toolbar/ico_gis_sprite.png create mode 100644 apps/dm/public/images/map/toolbar/icon-viewclose.png create mode 100644 apps/dm/public/images/map/toolbar/img_base_map.png create mode 100644 apps/dm/public/images/map/toolbar/img_network_map.png create mode 100644 apps/dm/public/images/map/toolbar/img_road_map.png create mode 100644 apps/dm/public/images/map/toolbar/img_settle_map.png create mode 100644 apps/dm/public/images/map/toolbar/loading.gif create mode 100644 apps/dm/public/images/map/toolbar/map_pin.png create mode 100644 apps/dm/public/images/map/toolbar/toggle_off.png create mode 100644 apps/dm/public/images/map/toolbar/toggle_on.png create mode 100644 pnpm-lock.yaml diff --git a/apps/dm/.env.local b/apps/dm/.env.local index 0b14165fd..106493928 100644 --- a/apps/dm/.env.local +++ b/apps/dm/.env.local @@ -1,19 +1,16 @@ NEXT_PUBLIC_SITE_ID = DM +NEXT_PUBLIC_CLAIM_NAME = Authorization +NEXT_PUBLIC_AUTH_USER_ID = token-id +NEXT_PUBLIC_ACCESS_TOKEN = access-token +NEXT_PUBLIC_REFRESH_TOKEN = refresh-token NEXT_PUBLIC_PORT = 11035 NEXT_PUBLIC_PROXY_HOST = http://con.mind-one.co.kr:11035 NEXT_PUBLIC_SERVER_API_URL = http://con.mind-one.co.kr:11080/api/v1 NEXT_PUBLIC_FO_SERVER_API_URL = http://con.mind-one.co.kr:11082/api/v1 NEXT_PUBLIC_CS_SERVER_API_URL = http://dev.onasys.co.kr:8000 NEXT_PUBLIC_CS_TUS_UPLOAD = /attach-service/api/v1/tus/upload -NEXT_PUBLIC_CLAIM_NAME = Authorization -NEXT_PUBLIC_AUTH_USER_ID = token-id -NEXT_PUBLIC_ACCESS_TOKEN = access-token -NEXT_PUBLIC_REFRESH_TOKEN = refresh-token - - - NEXT_PUBLIC_SEWERAGE_API_URL=http://con.mind-one.co.kr:11083/sewerage-service/api # 개발 서버 API -# NEXT_PUBLIC_SEWERAGE_API_URL=http://192.168.1.128:8883/sewerage-service/api # 최용성 +#NEXT_PUBLIC_SEWERAGE_API_URL=http://192.168.1.128:8883/sewerage-service/api # 최용성 #NEXT_PUBLIC_SEWERAGE_API_URL=http://192.168.1.66:8883/sewerage-service/api # 박이정 diff --git a/apps/dm/app/pages/map/popup/sdmInfoMap.tsx b/apps/dm/app/pages/map/popup/sdmInfoMap.tsx new file mode 100644 index 000000000..21b76f943 --- /dev/null +++ b/apps/dm/app/pages/map/popup/sdmInfoMap.tsx @@ -0,0 +1,132 @@ +"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"; + +const Map = () => { + const [treeData, setTreeData] = useState([]); // 초기값을 객체 또는 null로 설정 + const gisMaps = useRef(null); + const [isOpen, setIsOpen] = useState(false); // 팝업 열기/닫기 상태 + const [isSearchModalOpen, setIsSearchModalOpen] = useState(true); // SearchModal 열림 상태 + const [featureData, setFeatureData] = useState(null); // 선택된 피쳐 데이터 + const [checkedLayers, setCheckedLayers] = useState([]); + const [checkedLayersData, setCheckedLayersData] = useState([]); + + 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); + }; + + return ( +
+ +
+ ); +}; + +export default Map; diff --git a/apps/dm/app/pages/map/popup/windowPotal.tsx b/apps/dm/app/pages/map/popup/windowPotal.tsx new file mode 100644 index 000000000..e601e7c9c --- /dev/null +++ b/apps/dm/app/pages/map/popup/windowPotal.tsx @@ -0,0 +1,94 @@ +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) { //