feat(tradein): StreetDealsCard с paired listings (PR L, #564 Phase 2) #599

Merged
Light1YT merged 1 commit from feat/tradein-street-deals-with-asks into main 2026-05-27 12:29:06 +00:00

1 commit

Author SHA1 Message Date
6a46585e8c feat(tradein): StreetDealsCard с paired listings (PR L / #564 Phase 2)
Switch StreetDealsCard от /street-deals к /sales-vs-listings. В каждой
строке ДКП-сделки теперь показан linked исторический ASK с источником
(Avito/Cian/Yandex/N1), ценой listing, сроком до сделки и discount_pct
в цвете (зелёный = торг, красный = overshoot). В шапке секции —
linkage_rate % сделок имеют match + медианный торг по pairs с listing.

Changes:
- types/trade-in.ts: SalesListingPair + SalesVsListingsResponse
- lib/trade-in-api.ts: useSalesVsListings hook (TanStack, staleTime 10m)
- components/StreetDealsCard.tsx: rewrite — switch endpoint, add listing
  column, compute aggregates client-side (count/median/range), extract
  <ListingBadge> subcomponent
- components/trade-in.css: token-based styles (.linkage-hint, .discount-pos/neg,
  .listing-cell, .listing-link, .listing-source-badge) + mobile breakpoint

Decisions:
- Endpoint replacement (не parallel fetch) — /sales-vs-listings суперсет
- Period default 24 мес (от endpoint default) — больше data для pairing
- Discount sign: var(--success) при <0 (торг), var(--danger) при >0
- safeUrl() guard для listing URLs (XSS) + target="_blank" rel="noopener"
- Mobile <720px — listing column stacks vertically
- linkage_rate=0 → listing column скрыт целиком (`hasAnyListing` flag)

Tests: tsc --noEmit clean. No new deps.

Out of scope: useStreetDeals hook deprecated, можно убрать в cleanup PR.

Closes #564 Phase 2.
2026-05-27 17:28:38 +05:00