import React, { useEffect, useState } from 'react';
import { getListings, collectListings, getListingsCollectStatus } from '../../../api';
import { tierMeta, formatMoney, formatRatio } from '../listingsUtils';
const DEAL_TYPES = ['전체', '전세', '반전세', '매매'];
const ListingCard = ({ l }) => {
const isSale = l.deal_type === '매매';
const tv = isSale ? l.valuation_tier : l.safety_tier;
const meta = tierMeta(isSale ? 'valuation' : 'safety', tv);
const price = l.price ?? l.deposit ?? l.amount;
const flags = Array.isArray(l.regulation_flags) ? l.regulation_flags : [];
const reasons = Array.isArray(l.reasons) ? l.reasons : [];
return (
{tv && {meta.emoji} {meta.label}}
{l.complex_name || l.name || l.dong || '(매물)'}
{l.deal_type && {l.deal_type}}
{l.dong && {l.dong}}
{price != null && {formatMoney(price)}}
{l.area != null && {l.area}m²}
{l.ratio != null && 비율 {formatRatio(l.ratio)}}
{flags.length > 0 && (
{flags.map((f, i) => {f})}
)}
{reasons.length > 0 &&
{reasons.join(' · ')}
}
);
};
const ListingsTab = () => {
const [listings, setListings] = useState([]);
const [dealType, setDealType] = useState('전체');
const [matchedOnly, setMatchedOnly] = useState(false);
const [loading, setLoading] = useState(false);
const [error, setError] = useState('');
const [collectStatus, setCollectStatus] = useState(null);
const [collecting, setCollecting] = useState(false);
const load = async () => {
setLoading(true);
setError('');
try {
const data = await getListings({
deal_type: dealType === '전체' ? undefined : dealType,
matched_only: matchedOnly,
size: 50,
});
setListings(Array.isArray(data) ? data : (data.listings ?? []));
} catch (e) {
setError(e?.message ?? String(e));
setListings([]);
} finally {
setLoading(false);
}
};
const loadStatus = async () => {
try { setCollectStatus(await getListingsCollectStatus()); } catch { /* noop */ }
};
useEffect(() => { load(); }, [dealType, matchedOnly]); // eslint-disable-line react-hooks/exhaustive-deps
useEffect(() => { loadStatus(); }, []);
const handleCollect = async () => {
setCollecting(true);
setError('');
const prevAt = collectStatus?.collected_at ?? null;
try {
await collectListings();
} catch (e) {
setError(e?.message ?? String(e));
setCollecting(false);
return;
}
let tries = 0;
const poll = async () => {
tries += 1;
let st = null;
try { st = await getListingsCollectStatus(); } catch { /* noop */ }
if (st) setCollectStatus(st);
const done = st && (st.collected_at !== prevAt || st.error);
if (done || tries >= 12) {
setCollecting(false);
load();
return;
}
window.setTimeout(poll, 5000);
};
window.setTimeout(poll, 5000);
};
return (
{DEAL_TYPES.map((d) => (
))}
{collectStatus && (
수집 상태: {collectStatus.status === 'never_run' ? '미실행' : collectStatus.status}
{collectStatus.total_count != null && ` · 총 ${collectStatus.total_count}건`}
{collectStatus.error && · 오류: {collectStatus.error}}
)}
{error &&
{error}
}
{loading ? (
불러오는 중…
) : listings.length === 0 ? (
조건에 맞는 매물이 없습니다. ‘수집 실행’으로 매물을 모아보세요.
) : (
{listings.map((l) => )}
)}
);
};
export default ListingsTab;