feat(realestate): 공고 카드 매칭 점수 + 매칭 결과 탭 강화

- 공고 카드에 매칭 점수 뱃지 표시 (70+녹색, 40+주황, 기본회색)
- 상세 패널 헤더에 매칭 점수 + 자격 유형 태그 표시
- 매칭 결과 카드에 D-day + 접수일정 + 매칭 사유 표시 추가

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-07 23:51:09 +09:00
parent bf5c7ba54e
commit 45b74e672a

View File

@@ -331,6 +331,15 @@ function AnnouncementCard({ item, isSelected, onClick, onBookmark }) {
{HOUSE_TYPE_LABELS[item.house_secd]} {HOUSE_TYPE_LABELS[item.house_secd]}
</span> </span>
)} )}
{item.match_score > 0 && (
<span className="sub-badge" style={{
color: item.match_score >= 70 ? '#34d399' : item.match_score >= 40 ? '#f59e0b' : '#94a3b8',
background: item.match_score >= 70 ? 'rgba(52,211,153,0.1)' : item.match_score >= 40 ? 'rgba(245,158,11,0.1)' : 'rgba(148,163,184,0.1)',
fontWeight: 700,
}}>
{item.match_score}
</span>
)}
</div> </div>
<button <button
onClick={(e) => { e.stopPropagation(); onBookmark?.(item.id); }} onClick={(e) => { e.stopPropagation(); onBookmark?.(item.id); }}
@@ -404,6 +413,23 @@ function AnnouncementDetail({ item, onBookmark }) {
</div> </div>
<h3 className="sub-detail__name">{item.house_nm}</h3> <h3 className="sub-detail__name">{item.house_nm}</h3>
<p className="sub-detail__address">{item.address || item.region_name}</p> <p className="sub-detail__address">{item.address || item.region_name}</p>
{item.match_score > 0 && (
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginTop: 6 }}>
<span style={{
fontSize: 20, fontWeight: 700, fontFamily: 'var(--font-display)',
color: item.match_score >= 70 ? '#34d399' : item.match_score >= 40 ? '#f59e0b' : '#94a3b8',
}}>
매칭 {item.match_score}
</span>
{item.eligible_types?.length > 0 && (
<div style={{ display: 'flex', gap: 4, flexWrap: 'wrap' }}>
{(Array.isArray(item.eligible_types) ? item.eligible_types : []).map((t, i) => (
<span key={i} className="sub-tag is-neutral" style={{ fontSize: 10 }}>{t}</span>
))}
</div>
)}
</div>
)}
</div> </div>
<div className="sub-detail__actions"> <div className="sub-detail__actions">
<button <button
@@ -811,6 +837,15 @@ function MatchesTab() {
</div> </div>
<p className="sub-card__address" style={{ margin: 0 }}> <p className="sub-card__address" style={{ margin: 0 }}>
{match.region_name || '-'} {match.region_name || '-'}
{match.receipt_start && (
<span style={{ marginLeft: 8 }}>
{fmt(match.receipt_start)} ~ {fmt(match.receipt_end)}
{(() => {
const dd = getDDays(match.receipt_start);
return dd ? <span style={{ marginLeft: 6, fontWeight: 600, color: getDDayColor(match.receipt_start) }}>{dd}</span> : null;
})()}
</span>
)}
</p> </p>
{match.eligible_types && ( {match.eligible_types && (
<div style={{ display: 'flex', gap: 4, flexWrap: 'wrap' }}> <div style={{ display: 'flex', gap: 4, flexWrap: 'wrap' }}>
@@ -824,9 +859,14 @@ function MatchesTab() {
))} ))}
</div> </div>
)} )}
<span style={{ fontSize: 11, color: 'var(--text-muted)' }}> {match.match_reasons?.length > 0 && (
매칭일: {fmtDateTime(match.created_at)} <div style={{ fontSize: 11, color: 'var(--text-muted)' }}>
</span> {(Array.isArray(match.match_reasons)
? match.match_reasons
: (() => { try { return JSON.parse(match.match_reasons); } catch { return []; } })()
).join(' · ')}
</div>
)}
</div> </div>
<div style={{ textAlign: 'center', flexShrink: 0 }}> <div style={{ textAlign: 'center', flexShrink: 0 }}>
<div style={{ <div style={{