[P1][tradein][frontend] HeroSummary: empty-state «недостаточно данных» вместо «0,00 млн ₽» при insufficient_data #740

Closed
opened 2026-05-30 15:24:17 +00:00 by bot-analyst · 2 comments
Collaborator

Worker: исполняемый спек. Делай ровно ниже. Конфликт с кодом → коммент, НЕ угадывай.
Frontend-консьюмер backend #697 (insufficient_data, PR #734, merged). Аналог пары #721→#722.

Задача

Когда оценка вернулась без данных (median_price_rub отсутствует/0), HeroSummary рендерит headline formatMln(0) → буквально «0,00 млн ₽». Показать вместо этого явный empty-state «Недостаточно данных для оценки». Добавить поле insufficient_data в TS-тип.

Контекст (проверено чтением)

  • formatMln(rub) (HeroSummary.tsx:60-62) на 0 → "0,00 млн". Zero-guard нет.
  • Headline-значение: HeroSummary.tsx:308-310{formatMln(hasSold ? sold : m)} ₽, где m = estimate.median_price_rub (стр. 95). При пустой оценке m=0, hasSold=false (sold null/0) → «0,00 млн ₽».
  • Backend (#697, merged PR #734) добавил AggregatedEstimate.insufficient_data (computed_field, true когда median≤0). В TS-типе фронта его ещё НЕТ (grep insufficient_data frontend/src → 0).

Files (точные — проверено чтением)

  • tradein-mvp/frontend/src/types/trade-in.ts:
    • export interface AggregatedEstimate — строка 108; median_price_rub: number; — строка 110, n_analogs: number; — строка 125. Добавить поле insufficient_data внутри интерфейса (после n_analogs, стр. 125).
  • tradein-mvp/frontend/src/components/trade-in/HeroSummary.tsx:
    • export function HeroSummary(...) — строка 79; const m = estimate.median_price_rub; — строка 95.
    • <div className="hero-headline"> — строки 296-325; headline-value {formatMln(...)} ₽ — строки 308-310.

Контракт (точное соответствие backend #697)

В AggregatedEstimate (types/trade-in.ts, после n_analogs, стр. 125):

  insufficient_data: boolean; // backend #697: true когда median_price_rub <= 0 (нет данных)

В HeroSummary (стр. 79+): ранний вычисляемый флаг и ветка рендера. Например после строки 95:

  const insufficientData = estimate.insufficient_data || m <= 0;

и в блоке hero-headline (стр. 296-325): при insufficientData рендерить empty-state с текстом «Недостаточно данных для оценки» (подзаголовок — почему: мало/нет аналогов рядом), НЕ вызывать formatMln(0) для главного числа.

Definition of Done (бинарно)

  • grep -n 'insufficient_data' tradein-mvp/frontend/src/types/trade-in.ts → 1 (поле в интерфейсе).
  • grep -n 'insufficient_data\|insufficientData' tradein-mvp/frontend/src/components/trade-in/HeroSummary.tsx → ≥1.
  • При insufficient_data===true headline НЕ содержит «0,00 млн» — рендерится текст с подстрокой «Недостаточно данных». grep -n 'Недостаточно данных' tradein-mvp/frontend/src/components/trade-in/HeroSummary.tsx → ≥1.
  • Нормальный путь не сломан: при insufficient_data===false headline по-прежнему {formatMln(...)} ₽ (диапазон/sold/discount-блоки нетронуты).
  • cd tradein-mvp/frontend && npx tsc --noEmit → 0 ошибок.
  • cd tradein-mvp/frontend && npx eslint src/components/trade-in/HeroSummary.tsx src/types/trade-in.ts → clean.

Не делать (out of scope)

  • НЕ менять backend / formatMln сигнатуру.
  • НЕ трогать sold/discount/dkp/imv-блоки и price-figure (:339-348) — они уже под своими guard'ами (hasSold, showDkp). Только headline empty-state.
  • НЕ чинить findings #19/#20/#21 (Infinity%/n_analogs cap/confidence-color) — отдельные issue этого же аудита.

Risk

  • HeroSummary.tsx — главный экран результата (client-facing, демо 2026-06-01). Empty-state добавлять аддитивно, не ломая happy-path.

Связано

  • Backend: #697 (merged, insufficient_data). Паттерн: #721→#722.
  • Источник: аудит inbox/2026-05-30-1545-tradein-hard-code-audit-backend-frontend (finding #18). Milestone Praktika demo 2026-06-01.
> Worker: исполняемый спек. Делай ровно ниже. Конфликт с кодом → коммент, НЕ угадывай. > Frontend-консьюмер backend #697 (`insufficient_data`, PR #734, merged). Аналог пары #721→#722. ## Задача Когда оценка вернулась без данных (`median_price_rub` отсутствует/0), HeroSummary рендерит headline `formatMln(0)` → буквально **«0,00 млн ₽»**. Показать вместо этого явный empty-state «Недостаточно данных для оценки». Добавить поле `insufficient_data` в TS-тип. ## Контекст (проверено чтением) - `formatMln(rub)` (`HeroSummary.tsx:60-62`) на 0 → `"0,00 млн"`. Zero-guard нет. - Headline-значение: `HeroSummary.tsx:308-310` — `{formatMln(hasSold ? sold : m)} ₽`, где `m = estimate.median_price_rub` (стр. 95). При пустой оценке `m=0`, `hasSold=false` (sold null/0) → «0,00 млн ₽». - Backend (#697, merged PR #734) добавил `AggregatedEstimate.insufficient_data` (computed_field, true когда median≤0). В TS-типе фронта его ещё НЕТ (`grep insufficient_data frontend/src` → 0). ## Files (точные — проверено чтением) - `tradein-mvp/frontend/src/types/trade-in.ts`: - `export interface AggregatedEstimate` — строка **108**; `median_price_rub: number;` — строка **110**, `n_analogs: number;` — строка **125**. Добавить поле `insufficient_data` внутри интерфейса (после `n_analogs`, стр. 125). - `tradein-mvp/frontend/src/components/trade-in/HeroSummary.tsx`: - `export function HeroSummary(...)` — строка **79**; `const m = estimate.median_price_rub;` — строка **95**. - `<div className="hero-headline">` — строки **296-325**; headline-value `{formatMln(...)} ₽` — строки **308-310**. ## Контракт (точное соответствие backend #697) В `AggregatedEstimate` (types/trade-in.ts, после `n_analogs`, стр. 125): ```ts insufficient_data: boolean; // backend #697: true когда median_price_rub <= 0 (нет данных) ``` В `HeroSummary` (стр. 79+): ранний вычисляемый флаг и ветка рендера. Например после строки 95: ```ts const insufficientData = estimate.insufficient_data || m <= 0; ``` и в блоке `hero-headline` (стр. 296-325): при `insufficientData` рендерить empty-state с текстом «Недостаточно данных для оценки» (подзаголовок — почему: мало/нет аналогов рядом), НЕ вызывать `formatMln(0)` для главного числа. ## Definition of Done (бинарно) - [ ] `grep -n 'insufficient_data' tradein-mvp/frontend/src/types/trade-in.ts` → 1 (поле в интерфейсе). - [ ] `grep -n 'insufficient_data\|insufficientData' tradein-mvp/frontend/src/components/trade-in/HeroSummary.tsx` → ≥1. - [ ] При `insufficient_data===true` headline НЕ содержит «0,00 млн» — рендерится текст с подстрокой **«Недостаточно данных»**. `grep -n 'Недостаточно данных' tradein-mvp/frontend/src/components/trade-in/HeroSummary.tsx` → ≥1. - [ ] Нормальный путь не сломан: при `insufficient_data===false` headline по-прежнему `{formatMln(...)} ₽` (диапазон/sold/discount-блоки нетронуты). - [ ] `cd tradein-mvp/frontend && npx tsc --noEmit` → 0 ошибок. - [ ] `cd tradein-mvp/frontend && npx eslint src/components/trade-in/HeroSummary.tsx src/types/trade-in.ts` → clean. ## Не делать (out of scope) - НЕ менять backend / `formatMln` сигнатуру. - НЕ трогать sold/discount/dkp/imv-блоки и price-figure (`:339-348`) — они уже под своими guard'ами (`hasSold`, `showDkp`). Только headline empty-state. - НЕ чинить findings #19/#20/#21 (Infinity%/n_analogs cap/confidence-color) — отдельные issue этого же аудита. ## Risk - `HeroSummary.tsx` — главный экран результата (client-facing, демо 2026-06-01). Empty-state добавлять аддитивно, не ломая happy-path. ## Связано - Backend: #697 (merged, `insufficient_data`). Паттерн: #721→#722. - Источник: аудит `inbox/2026-05-30-1545-tradein-hard-code-audit-backend-frontend` (finding #18). Milestone Praktika demo 2026-06-01.
bot-analyst added the
priority/p1
scope/frontend
status/ready
tradein
labels 2026-05-30 15:24:24 +00:00
bot-analyst added this to the Praktika demo — Mon 2026-06-01 milestone 2026-05-30 15:24:25 +00:00
bot-frontend self-assigned this 2026-05-30 15:30:16 +00:00
bot-frontend added
status/wip
and removed
status/ready
labels 2026-05-30 15:30:17 +00:00
bot-frontend added
status/review
and removed
status/wip
labels 2026-05-30 15:33:50 +00:00
Collaborator

Working on this in PR #743.

Working on this in PR #743.
bot-reviewer added
status/qa
and removed
status/review
labels 2026-05-30 15:37:19 +00:00
Collaborator

QA smoke PASS (PR #743, deployed) — headline empty-state корректен.

Merged-код (main) + runtime на insufficient_data-estimate (Челябинск, median=0, insufficient_data=true):

  • HeroSummary.tsx .hero-headline: {insufficientData ? (<empty-state «Оценка недоступна» / «Недостаточно данных для оценки» / note>) : (<обычный headline {formatMln} ₽>)}
  • Runtime .headline-value-empty = «Недостаточно данных для оценки» (не «0,00 млн ₽») ✓; grep «Недостаточно данных» → есть ✓; happy-path (median>0) headline нетронут.

DoD (headline scope) выполнен. → status/done, closing.


⚠️ Side-finding (вне scope #740, завожу follow-up): на той же insufficient_data-странице «0,00 млн ₽» всё ещё рендерится в 8 НЕ-headline местах — price-figure, «исходная оценка» (What-if), «Оценка по рынку» (Секция 4 · Оффер), ListingsCard «медиана · 0,00 млн», agg-progress «медиана: 0,00 млн». Это аудит-уровень empty-state propagation за пределами headline; #740 явно scoped «только headline». Заведу отдельный issue, чтобы демо-страница пустой оценки была чистой целиком.

✅ **QA smoke PASS** (PR #743, deployed) — headline empty-state корректен. Merged-код (`main`) + runtime на insufficient_data-estimate (Челябинск, median=0, `insufficient_data=true`): - `HeroSummary.tsx` `.hero-headline`: `{insufficientData ? (<empty-state «Оценка недоступна» / «Недостаточно данных для оценки» / note>) : (<обычный headline {formatMln} ₽>)}` ✓ - **Runtime** `.headline-value-empty` = «**Недостаточно данных для оценки**» (не «0,00 млн ₽») ✓; `grep «Недостаточно данных»` → есть ✓; happy-path (median>0) headline нетронут. DoD (headline scope) выполнен. → `status/done`, closing. --- ⚠️ **Side-finding (вне scope #740, завожу follow-up):** на той же insufficient_data-странице «0,00 млн ₽» всё ещё рендерится в **8 НЕ-headline местах** — price-figure, «исходная оценка» (What-if), «Оценка по рынку» (Секция 4 · Оффер), ListingsCard «медиана · 0,00 млн», agg-progress «медиана: 0,00 млн». Это аудит-уровень empty-state propagation за пределами headline; #740 явно scoped «только headline». Заведу отдельный issue, чтобы демо-страница пустой оценки была чистой целиком.
bot-qa added
status/done
and removed
status/qa
labels 2026-05-30 15:45:07 +00:00
Sign in to join this conversation.
No project
No assignees
3 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#740
No description provided.