feat(realestate): 즐겨찾기 + D-day 오차 수정 + 가격 표시 + 필드명 수정
- D-day 계산 로컬 타임존 통일 (UTC 파싱 → 로컬 Date 파싱, 1일 오차 해결) - 즐겨찾기 토글 (카드 ☆/★ + 상세 패널 버튼 + 즐겨찾기 필터) - 대시보드에 즐겨찾기 섹션 + 가격 표시 - 모델 필드명 수정: supply_price→top_amount, exclusive_area→supply_area - 카드에 가격 범위 표시 (억/만원 자동 포맷) - 매칭 결과 필드명 수정: score→match_score, status→ann_status, matched_at→created_at Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -42,16 +42,26 @@ const fmtFull = (d) => {
|
|||||||
return new Date(d).toLocaleDateString('ko-KR', { year: 'numeric', month: 'long', day: 'numeric' });
|
return new Date(d).toLocaleDateString('ko-KR', { year: 'numeric', month: 'long', day: 'numeric' });
|
||||||
};
|
};
|
||||||
|
|
||||||
const getDDays = (d) => {
|
const _diffDays = (d) => {
|
||||||
if (!d) return null;
|
if (!d) return null;
|
||||||
const diff = Math.ceil((new Date(d) - new Date().setHours(0, 0, 0, 0)) / 86400000);
|
// 로컬 타임존으로 통일하여 D-day 계산 (UTC 파싱 방지)
|
||||||
|
const [y, m, day] = d.split('-').map(Number);
|
||||||
|
const target = new Date(y, m - 1, day);
|
||||||
|
const today = new Date();
|
||||||
|
today.setHours(0, 0, 0, 0);
|
||||||
|
return Math.round((target - today) / 86400000);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getDDays = (d) => {
|
||||||
|
const diff = _diffDays(d);
|
||||||
|
if (diff === null) return null;
|
||||||
if (diff === 0) return 'D-Day';
|
if (diff === 0) return 'D-Day';
|
||||||
return diff > 0 ? `D-${diff}` : `D+${Math.abs(diff)}`;
|
return diff > 0 ? `D-${diff}` : `D+${Math.abs(diff)}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getDDayColor = (d) => {
|
const getDDayColor = (d) => {
|
||||||
if (!d) return 'var(--text-dim)';
|
const diff = _diffDays(d);
|
||||||
const diff = Math.ceil((new Date(d) - new Date().setHours(0, 0, 0, 0)) / 86400000);
|
if (diff === null) return 'var(--text-dim)';
|
||||||
if (diff <= 0) return '#f87171';
|
if (diff <= 0) return '#f87171';
|
||||||
if (diff <= 3) return '#f59e0b';
|
if (diff <= 3) return '#f59e0b';
|
||||||
if (diff <= 7) return '#00d4ff';
|
if (diff <= 7) return '#00d4ff';
|
||||||
@@ -66,11 +76,16 @@ const fmtDateTime = (d) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
async function apiPatch(path) {
|
async function apiPatch(path, body) {
|
||||||
const res = await fetch(path, {
|
const opts = {
|
||||||
method: 'PATCH',
|
method: 'PATCH',
|
||||||
headers: { 'Accept': 'application/json' },
|
headers: { 'Accept': 'application/json' },
|
||||||
});
|
};
|
||||||
|
if (body !== undefined) {
|
||||||
|
opts.headers['Content-Type'] = 'application/json';
|
||||||
|
opts.body = JSON.stringify(body);
|
||||||
|
}
|
||||||
|
const res = await fetch(path, opts);
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
const text = await res.text().catch(() => '');
|
const text = await res.text().catch(() => '');
|
||||||
throw new Error(`HTTP ${res.status} ${res.statusText}: ${text}`);
|
throw new Error(`HTTP ${res.status} ${res.statusText}: ${text}`);
|
||||||
@@ -78,6 +93,12 @@ async function apiPatch(path) {
|
|||||||
return res.json();
|
return res.json();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fmtPrice = (v) => {
|
||||||
|
if (v == null) return null;
|
||||||
|
if (v >= 10000) return `${(v / 10000).toFixed(v % 10000 === 0 ? 0 : 1)}억`;
|
||||||
|
return `${v.toLocaleString()}만`;
|
||||||
|
};
|
||||||
|
|
||||||
// ── StatusBadge ──────────────────────────────────────────────────────────────
|
// ── StatusBadge ──────────────────────────────────────────────────────────────
|
||||||
function StatusBadge({ status, size }) {
|
function StatusBadge({ status, size }) {
|
||||||
const cfg = STATUS_CONFIG[status] || { color: '#94a3b8', bg: 'rgba(148,163,184,0.1)' };
|
const cfg = STATUS_CONFIG[status] || { color: '#94a3b8', bg: 'rgba(148,163,184,0.1)' };
|
||||||
@@ -152,6 +173,12 @@ function DashboardTab() {
|
|||||||
</p>
|
</p>
|
||||||
<p className="sub-stat-item__label">신규 매칭</p>
|
<p className="sub-stat-item__label">신규 매칭</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="sub-stat-item">
|
||||||
|
<p className="sub-stat-item__value" style={{ color: dashboard?.bookmarked_count > 0 ? '#f59e0b' : undefined }}>
|
||||||
|
{dashboard?.bookmarked_count ?? 0}
|
||||||
|
</p>
|
||||||
|
<p className="sub-stat-item__label">즐겨찾기</p>
|
||||||
|
</div>
|
||||||
<div className="sub-stat-item">
|
<div className="sub-stat-item">
|
||||||
<p className="sub-stat-item__value">{totalCount}</p>
|
<p className="sub-stat-item__value">{totalCount}</p>
|
||||||
<p className="sub-stat-item__label">전체 공고</p>
|
<p className="sub-stat-item__label">전체 공고</p>
|
||||||
@@ -229,13 +256,68 @@ function DashboardTab() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Bookmarked */}
|
||||||
|
{dashboard?.bookmarked?.length > 0 && (
|
||||||
|
<div className="sub-panel">
|
||||||
|
<div className="sub-panel__head">
|
||||||
|
<div>
|
||||||
|
<p className="sub-panel__eyebrow">즐겨찾기</p>
|
||||||
|
<h3>관심 공고</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="sub-panel__body">
|
||||||
|
<div style={{ display: 'grid', gap: 8 }}>
|
||||||
|
{dashboard.bookmarked.map((item) => {
|
||||||
|
const dday = getDDays(item.receipt_start);
|
||||||
|
const priceText = item.min_price != null
|
||||||
|
? (item.min_price === item.max_price_display
|
||||||
|
? fmtPrice(item.min_price)
|
||||||
|
: `${fmtPrice(item.min_price)} ~ ${fmtPrice(item.max_price_display)}`)
|
||||||
|
: null;
|
||||||
|
return (
|
||||||
|
<div key={item.id} style={{
|
||||||
|
display: 'flex', justifyContent: 'space-between', alignItems: 'center',
|
||||||
|
padding: '10px 14px', borderRadius: 'var(--radius-sm)',
|
||||||
|
border: '1px solid var(--line)', background: 'var(--surface)',
|
||||||
|
}}>
|
||||||
|
<div style={{ display: 'grid', gap: 2 }}>
|
||||||
|
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
|
||||||
|
<span style={{ color: '#f59e0b', fontSize: 14 }}>★</span>
|
||||||
|
<span style={{ fontWeight: 600, fontSize: 13, color: 'var(--text-bright)' }}>
|
||||||
|
{item.house_nm}
|
||||||
|
</span>
|
||||||
|
<StatusBadge status={item.status} />
|
||||||
|
</div>
|
||||||
|
<span style={{ fontSize: 11, color: 'var(--text-muted)' }}>
|
||||||
|
{item.region_name || '-'}
|
||||||
|
{priceText && <> · <span style={{ color: '#f59e0b' }}>{priceText}</span></>}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{dday && (
|
||||||
|
<span style={{ fontSize: 13, fontWeight: 700, color: getDDayColor(item.receipt_start) }}>
|
||||||
|
{dday}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── AnnouncementCard ─────────────────────────────────────────────────────────
|
// ── AnnouncementCard ─────────────────────────────────────────────────────────
|
||||||
function AnnouncementCard({ item, isSelected, onClick }) {
|
function AnnouncementCard({ item, isSelected, onClick, onBookmark }) {
|
||||||
const dday = getDDays(item.receipt_start);
|
const dday = getDDays(item.receipt_start);
|
||||||
|
const priceText = item.min_price != null
|
||||||
|
? (item.min_price === item.max_price_display
|
||||||
|
? fmtPrice(item.min_price)
|
||||||
|
: `${fmtPrice(item.min_price)} ~ ${fmtPrice(item.max_price_display)}`)
|
||||||
|
: null;
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`sub-card${isSelected ? ' is-selected' : ''}`}
|
className={`sub-card${isSelected ? ' is-selected' : ''}`}
|
||||||
@@ -250,6 +332,17 @@ function AnnouncementCard({ item, isSelected, onClick }) {
|
|||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={(e) => { e.stopPropagation(); onBookmark?.(item.id); }}
|
||||||
|
style={{
|
||||||
|
background: 'none', border: 'none', cursor: 'pointer', padding: 2,
|
||||||
|
fontSize: 16, color: item.is_bookmarked ? '#f59e0b' : 'var(--text-dim)',
|
||||||
|
lineHeight: 1,
|
||||||
|
}}
|
||||||
|
title={item.is_bookmarked ? '즐겨찾기 해제' : '즐겨찾기'}
|
||||||
|
>
|
||||||
|
{item.is_bookmarked ? '★' : '☆'}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<h4 className="sub-card__name">{item.house_nm || '(이름 없음)'}</h4>
|
<h4 className="sub-card__name">{item.house_nm || '(이름 없음)'}</h4>
|
||||||
<p className="sub-card__address">{item.address || item.region_name || '-'}</p>
|
<p className="sub-card__address">{item.address || item.region_name || '-'}</p>
|
||||||
@@ -257,6 +350,12 @@ function AnnouncementCard({ item, isSelected, onClick }) {
|
|||||||
<span>{item.total_units ? `${item.total_units}세대` : '-'}</span>
|
<span>{item.total_units ? `${item.total_units}세대` : '-'}</span>
|
||||||
<span className="sub-card__dot">·</span>
|
<span className="sub-card__dot">·</span>
|
||||||
<span>{item.region_name || '-'}</span>
|
<span>{item.region_name || '-'}</span>
|
||||||
|
{priceText && (
|
||||||
|
<>
|
||||||
|
<span className="sub-card__dot">·</span>
|
||||||
|
<span style={{ color: '#f59e0b' }}>{priceText}</span>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="sub-card__bottom">
|
<div className="sub-card__bottom">
|
||||||
{item.receipt_start && (
|
{item.receipt_start && (
|
||||||
@@ -278,7 +377,7 @@ function AnnouncementCard({ item, isSelected, onClick }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ── AnnouncementDetail ───────────────────────────────────────────────────────
|
// ── AnnouncementDetail ───────────────────────────────────────────────────────
|
||||||
function AnnouncementDetail({ item }) {
|
function AnnouncementDetail({ item, onBookmark }) {
|
||||||
const [detailTab, setDetailTab] = useState('info');
|
const [detailTab, setDetailTab] = useState('info');
|
||||||
|
|
||||||
if (!item) {
|
if (!item) {
|
||||||
@@ -307,6 +406,16 @@ function AnnouncementDetail({ item }) {
|
|||||||
<p className="sub-detail__address">{item.address || item.region_name}</p>
|
<p className="sub-detail__address">{item.address || item.region_name}</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="sub-detail__actions">
|
<div className="sub-detail__actions">
|
||||||
|
<button
|
||||||
|
onClick={() => onBookmark?.(item.id)}
|
||||||
|
className="sub-filter-btn"
|
||||||
|
style={{
|
||||||
|
color: item.is_bookmarked ? '#f59e0b' : undefined,
|
||||||
|
borderColor: item.is_bookmarked ? '#f59e0b' : undefined,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{item.is_bookmarked ? '★ 즐겨찾기 해제' : '☆ 즐겨찾기'}
|
||||||
|
</button>
|
||||||
{item.homepage_url && (
|
{item.homepage_url && (
|
||||||
<a href={item.homepage_url} target="_blank" rel="noreferrer"
|
<a href={item.homepage_url} target="_blank" rel="noreferrer"
|
||||||
className="sub-filter-btn" style={{ textDecoration: 'none' }}>
|
className="sub-filter-btn" style={{ textDecoration: 'none' }}>
|
||||||
@@ -416,36 +525,39 @@ function AnnouncementDetail({ item }) {
|
|||||||
|
|
||||||
{detailTab === 'models' && item.models?.length > 0 && (
|
{detailTab === 'models' && item.models?.length > 0 && (
|
||||||
<div style={{ display: 'grid', gap: 8 }}>
|
<div style={{ display: 'grid', gap: 8 }}>
|
||||||
{item.models.map((m, i) => (
|
{item.models.map((m, i) => {
|
||||||
<div key={i} style={{
|
const totalUnits = (m.general_units || 0) + (m.special_units || 0);
|
||||||
border: '1px solid var(--line)',
|
return (
|
||||||
borderRadius: 'var(--radius-sm)',
|
<div key={i} style={{
|
||||||
padding: '12px 14px',
|
border: '1px solid var(--line)',
|
||||||
background: 'var(--surface)',
|
borderRadius: 'var(--radius-sm)',
|
||||||
display: 'grid',
|
padding: '12px 14px',
|
||||||
gap: 4,
|
background: 'var(--surface)',
|
||||||
fontSize: 12,
|
display: 'grid',
|
||||||
}}>
|
gap: 4,
|
||||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
fontSize: 12,
|
||||||
<span style={{ fontWeight: 600, color: 'var(--text-bright)' }}>
|
}}>
|
||||||
{m.model_nm || m.house_ty || `주택형 ${i + 1}`}
|
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||||
</span>
|
<span style={{ fontWeight: 600, color: 'var(--text-bright)' }}>
|
||||||
{m.supply_count && (
|
{m.house_ty || `주택형 ${i + 1}`}
|
||||||
<span style={{ color: 'var(--text-muted)', fontSize: 11 }}>
|
</span>
|
||||||
{m.supply_count}세대
|
{totalUnits > 0 && (
|
||||||
|
<span style={{ color: 'var(--text-muted)', fontSize: 11 }}>
|
||||||
|
{totalUnits}세대
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{m.supply_area && (
|
||||||
|
<span style={{ color: 'var(--text-dim)' }}>공급면적 {m.supply_area}m²</span>
|
||||||
|
)}
|
||||||
|
{m.top_amount != null && (
|
||||||
|
<span style={{ color: '#f59e0b', fontWeight: 600 }}>
|
||||||
|
분양가 {fmtPrice(m.top_amount)}원
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{m.exclusive_area && (
|
);
|
||||||
<span style={{ color: 'var(--text-dim)' }}>전용 {m.exclusive_area}m²</span>
|
})}
|
||||||
)}
|
|
||||||
{m.supply_price && (
|
|
||||||
<span style={{ color: '#f59e0b', fontWeight: 600 }}>
|
|
||||||
분양가 {Number(m.supply_price).toLocaleString()}만원
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -460,6 +572,7 @@ function AnnouncementsTab() {
|
|||||||
const [page, setPage] = useState(1);
|
const [page, setPage] = useState(1);
|
||||||
const [statusFilter, setStatusFilter] = useState('전체');
|
const [statusFilter, setStatusFilter] = useState('전체');
|
||||||
const [regionFilter, setRegionFilter] = useState('');
|
const [regionFilter, setRegionFilter] = useState('');
|
||||||
|
const [bookmarkFilter, setBookmarkFilter] = useState(false);
|
||||||
const [selected, setSelected] = useState(null);
|
const [selected, setSelected] = useState(null);
|
||||||
const [detail, setDetail] = useState(null);
|
const [detail, setDetail] = useState(null);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
@@ -472,6 +585,7 @@ function AnnouncementsTab() {
|
|||||||
const params = new URLSearchParams({ page: String(page), size: String(size) });
|
const params = new URLSearchParams({ page: String(page), size: String(size) });
|
||||||
if (statusFilter !== '전체') params.set('status', statusFilter);
|
if (statusFilter !== '전체') params.set('status', statusFilter);
|
||||||
if (regionFilter.trim()) params.set('region', regionFilter.trim());
|
if (regionFilter.trim()) params.set('region', regionFilter.trim());
|
||||||
|
if (bookmarkFilter) params.set('bookmarked', 'true');
|
||||||
const data = await apiGet(`/api/realestate/announcements?${params}`);
|
const data = await apiGet(`/api/realestate/announcements?${params}`);
|
||||||
setItems(data.items || []);
|
setItems(data.items || []);
|
||||||
setTotal(data.total || 0);
|
setTotal(data.total || 0);
|
||||||
@@ -483,7 +597,7 @@ function AnnouncementsTab() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => { load(); }, [page, statusFilter, regionFilter]);
|
useEffect(() => { load(); }, [page, statusFilter, regionFilter, bookmarkFilter]);
|
||||||
|
|
||||||
const handleSelect = async (item) => {
|
const handleSelect = async (item) => {
|
||||||
setSelected(item.id);
|
setSelected(item.id);
|
||||||
@@ -496,6 +610,18 @@ function AnnouncementsTab() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleBookmark = async (id) => {
|
||||||
|
try {
|
||||||
|
const updated = await apiPatch(`/api/realestate/announcements/${id}/bookmark`);
|
||||||
|
setItems(prev => prev.map(it =>
|
||||||
|
it.id === id ? { ...it, is_bookmarked: updated.is_bookmarked } : it
|
||||||
|
));
|
||||||
|
if (detail?.id === id) setDetail(prev => ({ ...prev, is_bookmarked: updated.is_bookmarked }));
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Bookmark error:', e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const totalPages = Math.max(1, Math.ceil(total / size));
|
const totalPages = Math.max(1, Math.ceil(total / size));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -513,13 +639,22 @@ function AnnouncementsTab() {
|
|||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<input
|
<div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
|
||||||
className="sub-form-input"
|
<button
|
||||||
placeholder="지역 검색..."
|
className={`sub-filter-btn${bookmarkFilter ? ' is-active' : ''}`}
|
||||||
value={regionFilter}
|
onClick={() => { setBookmarkFilter(v => !v); setPage(1); }}
|
||||||
onChange={(e) => { setRegionFilter(e.target.value); setPage(1); }}
|
style={{ fontSize: 12 }}
|
||||||
style={{ width: 160, padding: '6px 12px', fontSize: 12 }}
|
>
|
||||||
/>
|
★ 즐겨찾기
|
||||||
|
</button>
|
||||||
|
<input
|
||||||
|
className="sub-form-input"
|
||||||
|
placeholder="지역 검색..."
|
||||||
|
value={regionFilter}
|
||||||
|
onChange={(e) => { setRegionFilter(e.target.value); setPage(1); }}
|
||||||
|
style={{ width: 160, padding: '6px 12px', fontSize: 12 }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{loading ? (
|
{loading ? (
|
||||||
@@ -537,6 +672,7 @@ function AnnouncementsTab() {
|
|||||||
item={item}
|
item={item}
|
||||||
isSelected={selected === item.id}
|
isSelected={selected === item.id}
|
||||||
onClick={() => handleSelect(item)}
|
onClick={() => handleSelect(item)}
|
||||||
|
onBookmark={handleBookmark}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@@ -567,7 +703,7 @@ function AnnouncementsTab() {
|
|||||||
|
|
||||||
{/* Detail Panel */}
|
{/* Detail Panel */}
|
||||||
<div className="sub-detail-panel">
|
<div className="sub-detail-panel">
|
||||||
<AnnouncementDetail item={detail} />
|
<AnnouncementDetail item={detail} onBookmark={handleBookmark} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -671,7 +807,7 @@ function MatchesTab() {
|
|||||||
NEW
|
NEW
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
{match.status && <StatusBadge status={match.status} />}
|
{match.ann_status && <StatusBadge status={match.ann_status} />}
|
||||||
</div>
|
</div>
|
||||||
<p className="sub-card__address" style={{ margin: 0 }}>
|
<p className="sub-card__address" style={{ margin: 0 }}>
|
||||||
{match.region_name || '-'}
|
{match.region_name || '-'}
|
||||||
@@ -689,7 +825,7 @@ function MatchesTab() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<span style={{ fontSize: 11, color: 'var(--text-muted)' }}>
|
<span style={{ fontSize: 11, color: 'var(--text-muted)' }}>
|
||||||
매칭일: {fmtDateTime(match.matched_at)}
|
매칭일: {fmtDateTime(match.created_at)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div style={{ textAlign: 'center', flexShrink: 0 }}>
|
<div style={{ textAlign: 'center', flexShrink: 0 }}>
|
||||||
@@ -697,10 +833,10 @@ function MatchesTab() {
|
|||||||
fontFamily: 'var(--font-display)',
|
fontFamily: 'var(--font-display)',
|
||||||
fontSize: 28,
|
fontSize: 28,
|
||||||
fontWeight: 700,
|
fontWeight: 700,
|
||||||
color: match.score >= 70 ? '#34d399' : match.score >= 40 ? '#f59e0b' : '#f87171',
|
color: (match.match_score ?? 0) >= 70 ? '#34d399' : (match.match_score ?? 0) >= 40 ? '#f59e0b' : '#f87171',
|
||||||
lineHeight: 1,
|
lineHeight: 1,
|
||||||
}}>
|
}}>
|
||||||
{match.score ?? '-'}
|
{match.match_score ?? '-'}
|
||||||
</div>
|
</div>
|
||||||
<div style={{ fontSize: 10, color: 'var(--text-muted)', marginTop: 4 }}>
|
<div style={{ fontSize: 10, color: 'var(--text-muted)', marginTop: 4 }}>
|
||||||
매칭 점수
|
매칭 점수
|
||||||
|
|||||||
Reference in New Issue
Block a user