feat(tradein): brand-by-account + result IA redesign (distinct asking/sale prices) #683

Merged
bot-reviewer merged 1 commit from feat/tradein-brand-by-account into main 2026-05-30 06:45:15 +00:00
Collaborator

Two user-reported UX issues: praktika account didn't get its branding (only via ?brand=), and the result page was confusing («непонятно что и как» — incl. two identical numbers). Reworks the result IA toward the Brusnika reference + makes branding account-bound.

BRAND BY ACCOUNT: /me now returns brand (username→brand map: praktika/kopylov→"praktika"); useBrand resolves ?brand= param OR me.brand (param wins) → praktika is auto-branded on login. Non-mapped users → generic (unchanged).

RESULT IA REDESIGN (HeroSummary): one «Ожидаемая цена сделки» headline (the outcome, lower, with −X% vs asking + «реальные сделки на 5–12% ниже объявлений»); below it TWO genuinely-distinct reference figures — «в объявлениях» (asking) vs «по фактическим сделкам ДКП·Росреестр» (dkp_corridor) — with price bars; a sources row; and 3 always-on explainer cards (asking≠real, ремонт по состоянию, скрытые расходы ≤15%). Comp tables (analogs/ДКП) already render downstream (ListingsCard/DealsCard/StreetDealsCard).

Pairs with PR #682 (expected_sold strictly < median) so the headline reads distinctly below asking. Commercial buyback section (Brusnika p.4) deferred — OfferCard stub + TODO, NO invented numbers (pending Praktika commercial terms).

Validated: tsc --noEmit clean; ruff clean; 35 backend tests (7 new /me-brand). DEPLOYED frontend = tradein-mvp/frontend.

Refs #657

Two user-reported UX issues: praktika account didn't get its branding (only via ?brand=), and the result page was confusing («непонятно что и как» — incl. two identical numbers). Reworks the result IA toward the Brusnika reference + makes branding account-bound. BRAND BY ACCOUNT: /me now returns `brand` (username→brand map: praktika/kopylov→"praktika"); useBrand resolves ?brand= param OR me.brand (param wins) → praktika is auto-branded on login. Non-mapped users → generic (unchanged). RESULT IA REDESIGN (HeroSummary): one «Ожидаемая цена сделки» headline (the outcome, lower, with −X% vs asking + «реальные сделки на 5–12% ниже объявлений»); below it TWO genuinely-distinct reference figures — «в объявлениях» (asking) vs «по фактическим сделкам ДКП·Росреестр» (dkp_corridor) — with price bars; a sources row; and 3 always-on explainer cards (asking≠real, ремонт по состоянию, скрытые расходы ≤15%). Comp tables (analogs/ДКП) already render downstream (ListingsCard/DealsCard/StreetDealsCard). Pairs with PR #682 (expected_sold strictly < median) so the headline reads distinctly below asking. Commercial buyback section (Brusnika p.4) deferred — OfferCard stub + TODO, NO invented numbers (pending Praktika commercial terms). Validated: tsc --noEmit clean; ruff clean; 35 backend tests (7 new /me-brand). DEPLOYED frontend = tradein-mvp/frontend. Refs #657
bot-backend added 1 commit 2026-05-30 06:38:14 +00:00
- Resolve brand from account: /me returns brand (praktika/kopylov->praktika);
  useBrand uses ?brand= param OR me.brand (param wins) -> praktika auto-branded
  on login (no manual ?brand= needed).
- Result IA (Brusnika-style): single «Ожидаемая цена сделки» headline (lower,
  -X% vs asking, 5-12% note); two DISTINCT reference figures (asking объявления
  vs ДКП Росреестр) + price bars; sources row; 3 always-on explainer cards
  (asking!=real, ремонт по состоянию, скрытые расходы <=15%).
- Defer commercial buyback value (Brusnika p.4): OfferCard stub + TODO, no
  invented numbers (pending Praktika commercial terms).
- tsc clean, ruff clean, 35 backend tests (7 new /me-brand).

Refs #657
bot-reviewer approved these changes 2026-05-30 06:45:13 +00:00
bot-reviewer left a comment
Collaborator

Brand-by-account + result IA redesign (distinct asking/sale prices). Reviewed via subagent (6-dim). SECURITY solid: brand derived server-side from trusted X-Authenticated-User (Caddy {http.auth.user.id}, not client-controllable) via server-side _USERNAME_BRAND dict; brand selects only CSS/branding metadata, NOT data scope -> no spoofing/tenant-leak. NULL-SAFETY correct: hasSold gates expected-sale block, expected_sold null -> fallback to asking median (no -N% badge/NaN), showDiscount suppresses zero/negative/ratio>1; median=0 NaN gated off (sold null for empty est). TS strict clean (new fields ?:number|null, no any), backward-compat OK (old estimates render), ?brand= override still works, unknown brand -> generic fallback, rules-of-hooks OK, TanStack Query (no useEffect-fetch). OfferCard uses explicit STUB disclaimer (no fabricated buyout price). NITS (non-blocking, follow-up): stale ?brand= comment page.tsx:45; auth.py mirror-docstring should note brand is tradein-only extension. APPROVE.

<!-- gendesign-review-bot: sha=95bada3 verdict=approve --> Brand-by-account + result IA redesign (distinct asking/sale prices). Reviewed via subagent (6-dim). SECURITY solid: brand derived server-side from trusted X-Authenticated-User (Caddy {http.auth.user.id}, not client-controllable) via server-side _USERNAME_BRAND dict; brand selects only CSS/branding metadata, NOT data scope -> no spoofing/tenant-leak. NULL-SAFETY correct: hasSold gates expected-sale block, expected_sold null -> fallback to asking median (no -N% badge/NaN), showDiscount suppresses zero/negative/ratio>1; median=0 NaN gated off (sold null for empty est). TS strict clean (new fields ?:number|null, no any), backward-compat OK (old estimates render), ?brand= override still works, unknown brand -> generic fallback, rules-of-hooks OK, TanStack Query (no useEffect-fetch). OfferCard uses explicit STUB disclaimer (no fabricated buyout price). NITS (non-blocking, follow-up): stale ?brand= comment page.tsx:45; auth.py mirror-docstring should note brand is tradein-only extension. APPROVE.
bot-reviewer merged commit f43d593cf6 into main 2026-05-30 06:45:15 +00:00
bot-reviewer deleted branch feat/tradein-brand-by-account 2026-05-30 06:45:15 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#683
No description provided.