diff --git a/apps/dm_new/app/(landing)/reuse/apply/modal/CandidateSiteModal.tsx b/apps/dm_new/app/(landing)/reuse/apply/modal/CandidateSiteModal.tsx new file mode 100644 index 000000000..aa5a9c0ee --- /dev/null +++ b/apps/dm_new/app/(landing)/reuse/apply/modal/CandidateSiteModal.tsx @@ -0,0 +1,327 @@ +"use client"; + +import { useEffect, useMemo, useState } from "react"; + +export type CandidateSite = { + id: string; + name: string; + sewageM3d: number; // 하수처리량 + reuseM3d: number; // 재이용량 + // 지도 연동 시 사용(현재는 UI/구조용) + lat?: number; + lng?: number; +}; + +type Props = { + open: boolean; + onClose: () => void; + onApply: (selected: CandidateSite[]) => void; + initialSelected?: CandidateSite[]; + maxSelect?: number; // 기본 3개 +}; + +const MOCK_SITES: CandidateSite[] = [ + { id: "1", name: "서호생태 수자원 재이용1", sewageM3d: 1300, reuseM3d: 1300, lat: 35.0, lng: 128.0 }, + { id: "2", name: "서호생태 수자원 재이용2", sewageM3d: 1300, reuseM3d: 1300, lat: 35.01, lng: 128.01 }, + { id: "3", name: "서호생태 수자원 재이용3", sewageM3d: 1300, reuseM3d: 1300, lat: 35.02, lng: 128.02 }, +]; + +export default function CandidateSiteModal({ + open, + onClose, + onApply, + initialSelected = [], + maxSelect = 3, +}: Props) { + const [q, setQ] = useState(""); + const [selected, setSelected] = useState(initialSelected); + + // 지도 마커 컴포넌트 + function MapMarker({ label }: { label?: string }) { + return ( +
+ + + + + + + + + + + + + {/* pin body */} + + + {/* 내부 흰 원 */} + + + {label && ( + + {label} + + )} + +
+ ); + } + + // 모달 열릴 때마다 초기 선택값 동기화 + useEffect(() => { + if (open) setSelected(initialSelected); + if (open) setQ(""); + }, [open, initialSelected]); + + // ESC 닫기 + useEffect(() => { + if (!open) return; + const onKeyDown = (e: KeyboardEvent) => { + if (e.key === "Escape") onClose(); + }; + window.addEventListener("keydown", onKeyDown); + return () => window.removeEventListener("keydown", onKeyDown); + }, [open, onClose]); + + const filtered = useMemo(() => { + const keyword = q.trim(); + if (!keyword) return MOCK_SITES; + return MOCK_SITES.filter((s) => s.name.includes(keyword)); + }, [q]); + + const isSelected = (id: string) => selected.some((s) => s.id === id); + + const toggleSelect = (item: CandidateSite) => { + setSelected((prev) => { + const exists = prev.some((p) => p.id === item.id); + if (exists) return prev.filter((p) => p.id !== item.id); + + if (prev.length >= maxSelect) return prev; // 최대 개수 초과 방지 + return [...prev, item]; + }); + }; + + const removeChip = (id: string) => setSelected((prev) => prev.filter((p) => p.id !== id)); + + if (!open) return null; + + return ( +
+ {/* overlay */} + +
+
+ + {/* body */} +
+
+ {/* left: search + table */} +
+
후보지 검색
+ +
+ setQ(e.target.value)} + placeholder="주소검색" + className="h-12 w-full rounded-sm border border-neutral-300 bg-white px-4 pr-12 text-sm outline-none focus:border-sky-500" + /> + +
+ + {/* table header */} +
+
+
시설명
+
+ 하수처리량
(m³/일) +
+
+ 재이용량
(m³/일) +
+
선택
+
+ + {/* rows */} +
+ {filtered.length === 0 ? ( +
+ 검색 결과가 없습니다. +
+ ) : ( + filtered.map((s, idx) => { + const picked = isSelected(s.id); + const disabled = !picked && selected.length >= maxSelect; + return ( +
+
+ {idx + 1} + {s.name} +
+
{s.sewageM3d.toLocaleString()}
+
{s.reuseM3d.toLocaleString()}
+
+ +
+
+ ); + }) + )} +
+
+ + {/*
+ 최대 {maxSelect}개까지 선택 가능합니다. +
*/} +
+ + {/* right: map */} +
+
+ {/* TODO: 실제 지도 SDK 연동 시 이 영역 교체 */} +
+ 지도 영역(추후 지도 SDK 연동) +
+ + {/* 선택된 항목의 "가짜 마커" */} + {selected.map((s, i) => { + const POS = [ + { top: "40%", left: "45%" }, + { top: "25%", left: "75%" }, + { top: "70%", left: "80%" }, + ] as const; + + const pos = POS[i % POS.length] ?? POS[0]; + + return ( +
+ +
+ ); + })} +
+
+
+ + {/* selected chips */} +
+
+ 선택된 시설 (시설명/하수처리량(m³/일)/재이용량(m³/일)) +
+ +
+ {selected.length === 0 ? ( +
선택된 시설이 없습니다.
+ ) : ( +
+ {selected.map((s) => ( +
+ + {s.name} / {s.sewageM3d.toLocaleString()}(m³/일) / {s.reuseM3d.toLocaleString()}(m³/일) + + +
+ ))} +
+ )} +
+
+ + {/* footer button */} +
+ +
+
+
+ + ); +} \ No newline at end of file diff --git a/apps/dm_new/app/(landing)/reuse/apply/register/page.tsx b/apps/dm_new/app/(landing)/reuse/apply/register/page.tsx index 27883a981..a8a7297d3 100644 --- a/apps/dm_new/app/(landing)/reuse/apply/register/page.tsx +++ b/apps/dm_new/app/(landing)/reuse/apply/register/page.tsx @@ -4,6 +4,7 @@ import { useMemo, useState, useRef } from "react"; import SubPageHeader from "../../_components/SubPageHeader"; import WaterQualityRefModal from "../modal/WaterQualityRefModal"; +import CandidateSiteModal, { type CandidateSite } from "../modal/CandidateSiteModal"; type PHRange = { min: string; max: string }; @@ -18,6 +19,9 @@ export default function DemandRegisterPage() { const [openRef, setOpenRef] = useState(false); + const [openCand, setOpenCand] = useState(false); + const [selectedCands, setSelectedCands] = useState([]); + // 기본정보 const [reqDate, setReqDate] = useState(today); const [reqQty, setReqQty] = useState(""); @@ -251,10 +255,7 @@ export default function DemandRegisterPage() { @@ -290,6 +291,22 @@ export default function DemandRegisterPage() { + + setOpenCand(false)} + initialSelected={selectedCands} + onApply={(list) => { + setSelectedCands(list); + + // ✅ cand1~3 입력칸에 반영(시설명만 넣는 버전) + setCand1(list[0]?.name ?? ""); + setCand2(list[1]?.name ?? ""); + setCand3(list[2]?.name ?? ""); + + setOpenCand(false); + }} + /> ); } diff --git a/apps/dm_new/public/images/landing/cases/greywater_building.png b/apps/dm_new/public/images/landing/cases/greywater_building.png index 19e33421b..fab75a195 100644 Binary files a/apps/dm_new/public/images/landing/cases/greywater_building.png and b/apps/dm_new/public/images/landing/cases/greywater_building.png differ diff --git a/apps/dm_new/public/images/landing/cases/greywater_factory.png b/apps/dm_new/public/images/landing/cases/greywater_factory.png index 88ac1085e..50448c03a 100644 Binary files a/apps/dm_new/public/images/landing/cases/greywater_factory.png and b/apps/dm_new/public/images/landing/cases/greywater_factory.png differ diff --git a/apps/dm_new/public/images/landing/cases/greywater_overall.png b/apps/dm_new/public/images/landing/cases/greywater_overall.png index db489a203..e59541acf 100644 Binary files a/apps/dm_new/public/images/landing/cases/greywater_overall.png and b/apps/dm_new/public/images/landing/cases/greywater_overall.png differ diff --git a/apps/dm_new/public/images/landing/cases/greywater_usage.png b/apps/dm_new/public/images/landing/cases/greywater_usage.png index 722b710e9..b6c52d07c 100644 Binary files a/apps/dm_new/public/images/landing/cases/greywater_usage.png and b/apps/dm_new/public/images/landing/cases/greywater_usage.png differ diff --git a/apps/dm_new/public/images/landing/cases/rainwater_building.png b/apps/dm_new/public/images/landing/cases/rainwater_building.png index f33c3f3c4..7434ed7a2 100644 Binary files a/apps/dm_new/public/images/landing/cases/rainwater_building.png and b/apps/dm_new/public/images/landing/cases/rainwater_building.png differ diff --git a/apps/dm_new/public/images/landing/cases/rainwater_usage.png b/apps/dm_new/public/images/landing/cases/rainwater_usage.png index 12c840fc3..5a2d4bc50 100644 Binary files a/apps/dm_new/public/images/landing/cases/rainwater_usage.png and b/apps/dm_new/public/images/landing/cases/rainwater_usage.png differ diff --git a/apps/dm_new/public/images/landing/cases/rainwater_usage_amount.png b/apps/dm_new/public/images/landing/cases/rainwater_usage_amount.png index 19c6089cd..981da255f 100644 Binary files a/apps/dm_new/public/images/landing/cases/rainwater_usage_amount.png and b/apps/dm_new/public/images/landing/cases/rainwater_usage_amount.png differ diff --git a/apps/dm_new/public/images/landing/cases/treated_water_facility.png b/apps/dm_new/public/images/landing/cases/treated_water_facility.png index 5d669dda3..f9071650e 100644 Binary files a/apps/dm_new/public/images/landing/cases/treated_water_facility.png and b/apps/dm_new/public/images/landing/cases/treated_water_facility.png differ diff --git a/apps/dm_new/public/images/landing/cases/treated_water_region.png b/apps/dm_new/public/images/landing/cases/treated_water_region.png index 04a5cea1f..46f9459e8 100644 Binary files a/apps/dm_new/public/images/landing/cases/treated_water_region.png and b/apps/dm_new/public/images/landing/cases/treated_water_region.png differ diff --git a/apps/dm_new/public/images/landing/cases/treated_water_usage.png b/apps/dm_new/public/images/landing/cases/treated_water_usage.png index 28d51941a..e1793f700 100644 Binary files a/apps/dm_new/public/images/landing/cases/treated_water_usage.png and b/apps/dm_new/public/images/landing/cases/treated_water_usage.png differ diff --git a/apps/dm_new/public/images/landing/cases/treated_water_year.png b/apps/dm_new/public/images/landing/cases/treated_water_year.png index d1d79a232..dcb6a0a0b 100644 Binary files a/apps/dm_new/public/images/landing/cases/treated_water_year.png and b/apps/dm_new/public/images/landing/cases/treated_water_year.png differ diff --git a/apps/dm_new/public/images/landing/intro/domestic_textbox.png b/apps/dm_new/public/images/landing/intro/domestic_textbox.png index 983cf56f2..2fa8c0344 100644 Binary files a/apps/dm_new/public/images/landing/intro/domestic_textbox.png and b/apps/dm_new/public/images/landing/intro/domestic_textbox.png differ diff --git a/apps/dm_new/public/images/landing/intro/greywater_mandatory.png b/apps/dm_new/public/images/landing/intro/greywater_mandatory.png index 4d7992610..39602a42b 100644 Binary files a/apps/dm_new/public/images/landing/intro/greywater_mandatory.png and b/apps/dm_new/public/images/landing/intro/greywater_mandatory.png differ diff --git a/apps/dm_new/public/images/landing/intro/greywater_standard.png b/apps/dm_new/public/images/landing/intro/greywater_standard.png index fae4ca293..ba4b55ac1 100644 Binary files a/apps/dm_new/public/images/landing/intro/greywater_standard.png and b/apps/dm_new/public/images/landing/intro/greywater_standard.png differ diff --git a/apps/dm_new/public/images/landing/intro/law_timeline.png b/apps/dm_new/public/images/landing/intro/law_timeline.png index 3ff5061ce..6685b9158 100644 Binary files a/apps/dm_new/public/images/landing/intro/law_timeline.png and b/apps/dm_new/public/images/landing/intro/law_timeline.png differ diff --git a/apps/dm_new/public/images/landing/intro/linkage.png b/apps/dm_new/public/images/landing/intro/linkage.png index 79c6e75d6..cca2007ee 100644 Binary files a/apps/dm_new/public/images/landing/intro/linkage.png and b/apps/dm_new/public/images/landing/intro/linkage.png differ diff --git a/apps/dm_new/public/images/landing/intro/performance_left.png b/apps/dm_new/public/images/landing/intro/performance_left.png index 93dd84aae..3cf7b8e9e 100644 Binary files a/apps/dm_new/public/images/landing/intro/performance_left.png and b/apps/dm_new/public/images/landing/intro/performance_left.png differ diff --git a/apps/dm_new/public/images/landing/intro/policy_timeline.png b/apps/dm_new/public/images/landing/intro/policy_timeline.png index d20c4a597..3cc8ebd7a 100644 Binary files a/apps/dm_new/public/images/landing/intro/policy_timeline.png and b/apps/dm_new/public/images/landing/intro/policy_timeline.png differ diff --git a/apps/dm_new/public/images/landing/intro/rainreuse_mandatory.png b/apps/dm_new/public/images/landing/intro/rainreuse_mandatory.png index 7de37d945..89dc243bf 100644 Binary files a/apps/dm_new/public/images/landing/intro/rainreuse_mandatory.png and b/apps/dm_new/public/images/landing/intro/rainreuse_mandatory.png differ diff --git a/apps/dm_new/public/images/landing/intro/rel_note.png b/apps/dm_new/public/images/landing/intro/rel_note.png index 98f3829f6..46005f4b0 100644 Binary files a/apps/dm_new/public/images/landing/intro/rel_note.png and b/apps/dm_new/public/images/landing/intro/rel_note.png differ diff --git a/apps/dm_new/public/images/landing/intro/treatedwater_mandatory.png b/apps/dm_new/public/images/landing/intro/treatedwater_mandatory.png index d904e447e..b59984e49 100644 Binary files a/apps/dm_new/public/images/landing/intro/treatedwater_mandatory.png and b/apps/dm_new/public/images/landing/intro/treatedwater_mandatory.png differ