diff --git a/src/pages/listings/components/CriteriaTab.jsx b/src/pages/listings/components/CriteriaTab.jsx new file mode 100644 index 0000000..5ea32a6 --- /dev/null +++ b/src/pages/listings/components/CriteriaTab.jsx @@ -0,0 +1,147 @@ +import React, { useEffect, useState } from 'react'; +import { getListingsCriteria, putListingsCriteria } from '../../../api'; + +const DEAL_TYPES = ['전세', '반전세', '매매']; +const HOUSE_TYPES = ['아파트', '오피스텔', '빌라']; +const SAFETY_TIERS = ['', '안전', '주의', '위험']; + +const toList = (s) => String(s ?? '').split(',').map((x) => x.trim()).filter(Boolean); +const numOrNull = (v) => (v === '' || v == null ? null : Number(v)); + +const CriteriaTab = () => { + const [c, setC] = useState(null); // 편집 중 폼 상태 (dongs/house_types는 문자열/배열) + const [loading, setLoading] = useState(true); + const [saving, setSaving] = useState(false); + const [msg, setMsg] = useState(''); + + useEffect(() => { + (async () => { + setLoading(true); + try { + const data = await getListingsCriteria(); + setC({ ...data, dongs: (data.dongs ?? []).join(', ') }); + } catch (e) { + setMsg('불러오기 실패: ' + (e?.message ?? e)); + } finally { setLoading(false); } + })(); + }, []); + + const set = (k, v) => setC((prev) => ({ ...prev, [k]: v })); + const toggleArr = (k, val) => setC((prev) => { + const arr = Array.isArray(prev[k]) ? prev[k] : []; + return { ...prev, [k]: arr.includes(val) ? arr.filter((x) => x !== val) : [...arr, val] }; + }); + + const save = async () => { + setSaving(true); setMsg(''); + try { + const payload = { + dongs: toList(c.dongs), + deal_types: c.deal_types ?? [], + house_types: c.house_types ?? [], + max_deposit: numOrNull(c.max_deposit), + max_sale_price: numOrNull(c.max_sale_price), + min_area: numOrNull(c.min_area), + equity: numOrNull(c.equity), + annual_income: numOrNull(c.annual_income), + min_safety_tier: c.min_safety_tier || null, + notify_enabled: c.notify_enabled ? 1 : 0, + is_homeless: c.is_homeless ? 1 : 0, + is_householder: c.is_householder ? 1 : 0, + is_first_home: c.is_first_home ? 1 : 0, + }; + const updated = await putListingsCriteria(payload); + setC({ ...updated, dongs: (updated.dongs ?? []).join(', ') }); + setMsg('저장 완료'); + setTimeout(() => setMsg(''), 2000); + } catch (e) { + setMsg('저장 실패: ' + (e?.message ?? e)); + } finally { setSaving(false); } + }; + + if (loading || !c) return
불러오는 중…
; + + return ( +자기자본·연소득을 넣어야 매매 예산상한·전세 max_deposit이 매칭에 반영됩니다.
+