diff --git a/src/pages/subscription/components/DistrictTierEditor.jsx b/src/pages/subscription/components/DistrictTierEditor.jsx new file mode 100644 index 0000000..21b5b1f --- /dev/null +++ b/src/pages/subscription/components/DistrictTierEditor.jsx @@ -0,0 +1,167 @@ +import { useEffect, useState } from "react"; + +const SEOUL_DISTRICTS = [ + "강남구","강동구","강북구","강서구","관악구", + "광진구","구로구","금천구","노원구","도봉구", + "동대문구","동작구","마포구","서대문구","서초구", + "성동구","성북구","송파구","양천구","영등포구", + "용산구","은평구","종로구","중구","중랑구", +]; + +const TIERS = [ + { key: "S", label: "S", weight: "100%" }, + { key: "A", label: "A", weight: "80%" }, + { key: "B", label: "B", weight: "60%" }, + { key: "C", label: "C", weight: "40%" }, + { key: "D", label: "D", weight: "20%" }, +]; + +const EMPTY_TIERS = { S: [], A: [], B: [], C: [], D: [] }; + +function useIsDesktop() { + const [isDesktop, setIsDesktop] = useState( + typeof window !== "undefined" && window.matchMedia("(min-width: 768px)").matches + ); + useEffect(() => { + if (typeof window === "undefined") return; + const mq = window.matchMedia("(min-width: 768px)"); + const handler = (e) => setIsDesktop(e.matches); + mq.addEventListener("change", handler); + return () => mq.removeEventListener("change", handler); + }, []); + return isDesktop; +} + +export default function DistrictTierEditor({ value, onChange }) { + const isDesktop = useIsDesktop(); + const [dragOver, setDragOver] = useState(null); // 현재 hover 중인 zone key + + const current = value && Object.keys(value).length > 0 ? value : EMPTY_TIERS; + + const unassigned = SEOUL_DISTRICTS.filter( + d => !TIERS.some(t => (current[t.key] || []).includes(d)) + ); + + const moveDistrict = (district, targetTier /* null = 미할당 */) => { + const next = { S: [], A: [], B: [], C: [], D: [] }; + for (const t of Object.keys(next)) { + next[t] = (current[t] || []).filter(d => d !== district); + } + if (targetTier) { + next[targetTier] = [...next[targetTier], district]; + } + onChange(next); + }; + + const onDragStart = (e, district) => { + e.dataTransfer.setData("text/district", district); + e.dataTransfer.effectAllowed = "move"; + }; + const onDragOver = (e, key) => { + e.preventDefault(); + e.dataTransfer.dropEffect = "move"; + if (dragOver !== key) setDragOver(key); + }; + const onDragLeave = () => setDragOver(null); + const onDrop = (e, targetTier /* null = 미할당 */) => { + e.preventDefault(); + const district = e.dataTransfer.getData("text/district"); + setDragOver(null); + if (district) moveDistrict(district, targetTier); + }; + + if (!isDesktop) { + return ( +
+
+

자치구 우선순위

+

지역 5티어

+
+
+ {TIERS.map(t => ( +
+ + {t.label} {t.weight} + + + {(current[t.key] || []).length === 0 + ? (없음) + : (current[t.key] || []).join(", ")} + +
+ ))} +

✏️ 자치구 분류는 PC에서 편집할 수 있어요

+
+
+ ); + } + + return ( +
+
+

자치구 우선순위

+

지역 5티어 (드래그해서 분류)

+
+
+ {/* 미할당 풀 */} +
onDragOver(e, "_unassigned")} + onDragLeave={onDragLeave} + onDrop={(e) => onDrop(e, null)} + > +

미할당 ({unassigned.length})

+
+ {unassigned.map(d => ( + onDragStart(e, d)} + className="sub-chip sub-chip--district dte-chip" + > + {d} + + ))} +
+
+ + {/* 5티어 그리드 */} +
+ {TIERS.map(t => ( +
onDragOver(e, t.key)} + onDragLeave={onDragLeave} + onDrop={(e) => onDrop(e, t.key)} + > +
+ {t.label} {t.weight} +
+
+ {(current[t.key] || []).map(d => ( + onDragStart(e, d)} + className="sub-chip sub-chip--district dte-chip" + > + {d} + + + ))} +
+
+ ))} +
+
+
+ ); +}