"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 */}
); }