From e85cafd9a9d276196f13898381ee62c3b4b843fd Mon Sep 17 00:00:00 2001 From: bot-backend Date: Thu, 27 Aug 2026 14:29:19 +0300 Subject: [PATCH] =?UTF-8?q?fix(mera/b2c):=20=D0=BF=D0=BE=D0=B4=D1=81=D0=BA?= =?UTF-8?q?=D0=B0=D0=B7=D0=BA=D0=B0=20=D0=B4=D1=83=D0=B1=D0=BB=D0=B8=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D0=BB=D0=B0=20=D0=B0=D0=B4=D1=80=D0=B5=D1=81?= =?UTF-8?q?,=20=D0=B0=20=D1=87=D0=B8=D1=81=D0=BB=D0=B0=20=D0=B2=20=D1=80?= =?UTF-8?q?=D0=B5=D0=B7=D1=83=D0=BB=D1=8C=D1=82=D0=B0=D1=82=D0=B5=20=D1=81?= =?UTF-8?q?=D0=BF=D0=BE=D1=80=D0=B8=D0=BB=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Оба дефекта найдены проходом клиентского пути на проде 27.08. ПОДСКАЗКА АДРЕСА печатала одно и то же дважды. Строка списка состоит из главной строки (`label`) и пояснительной (`full_address`), но подсказчик для дома отдаёт их совпадающими буква в букву — проверено на проде: {"label":"Свердловская область, г. Екатеринбург, ул. Малышева, д. 51", "full_address":"Свердловская область, г. Екатеринбург, ул. Малышева, д. 51"} Человек видел адрес продублированным в каждой строке выпадающего списка. Показываем вторую строку только когда она отличается: замысел (короткое имя сверху, полный адрес под ним) сохраняется, если подсказчик когда-нибудь начнёт их различать. ЧИСЛА В РЕЗУЛЬТАТЕ спорили друг с другом. Плитка сверху: «30 похожих квартир продаётся в радиусе 1 км». Плитка под ней: «столько в среднем висит объявление из этих 6». Слово «этих» указывает на 30, а стоит рядом 6 — читается как ошибка в расчёте. Шесть — это те объявления, у которых известна дата публикации; медиана считается только по ним. Теперь доля названа явно («дата известна у 6 из 30»), а когда известна у всех — не упоминается вовсе, чтобы «30 из 30» не шумело. Честное имя величины сохранено: это возраст активного объявления, а не срок продажи (тест на это как был, так и остался). Проверено: 27 тестов mera-public зелёные, tsc --noEmit чист. --- .../__tests__/coverage-copy.test.ts | 18 ++++++++++++++++++ .../_components/estimate/EstimateFlow.tsx | 9 ++++++++- .../src/app/mera-public/coverage-copy.ts | 10 +++++++++- 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/tradein-mvp/frontend/src/app/mera-public/__tests__/coverage-copy.test.ts b/tradein-mvp/frontend/src/app/mera-public/__tests__/coverage-copy.test.ts index 60ea1434..6e9bd162 100644 --- a/tradein-mvp/frontend/src/app/mera-public/__tests__/coverage-copy.test.ts +++ b/tradein-mvp/frontend/src/app/mera-public/__tests__/coverage-copy.test.ts @@ -45,6 +45,24 @@ describe("describeCoverage", () => { expect(ageTile.label).not.toMatch(/продаётся|продаются|срок продажи|уходят/i); }); + it("подпись под возрастом не показывает число, спорящее с плиткой выше", () => { + // На проде 27.08: сверху «30 похожих квартир», под ней «из этих 6». + // Слово «этих» указывало на 30, и человек читал два разных числа об + // одном и том же. Долю называем явно — либо не называем вовсе. + const [cohortTile, ageTile] = describeCoverage( + probe({ n_listings: 30, n_with_age: 6 }), + ).tiles; + expect(cohortTile.value).toBe("30"); + expect(ageTile.label).toContain("6 из 30"); + expect(ageTile.label).not.toMatch(/этих/); + }); + + it("когда дата известна у всех — доля не упоминается вовсе", () => { + // «дата известна у 30 из 30» — шум: доля есть, но она ничего не сообщает. + const [, ageTile] = describeCoverage(probe({ n_listings: 30, n_with_age: 30 })).tiles; + expect(ageTile.label).toBe("столько в среднем висит объявление"); + }); + it("город вне покрытия — говорим прямо и не предлагаем платный шаг", () => { const verdict = describeCoverage(probe({ status: "not_covered", city: "", n_listings: 0 })); expect(verdict.offerPaidStep).toBe(false); diff --git a/tradein-mvp/frontend/src/app/mera-public/_components/estimate/EstimateFlow.tsx b/tradein-mvp/frontend/src/app/mera-public/_components/estimate/EstimateFlow.tsx index bcf6dcdf..491ed98b 100644 --- a/tradein-mvp/frontend/src/app/mera-public/_components/estimate/EstimateFlow.tsx +++ b/tradein-mvp/frontend/src/app/mera-public/_components/estimate/EstimateFlow.tsx @@ -345,7 +345,14 @@ export function EstimateFlow() { onClick={() => choose(item)} > {item.label} - {item.full_address} + {/* Вторая строка — только когда она что-то добавляет. + Подсказчик отдаёт `label` и `full_address` порознь, + но для дома они сплошь и рядом совпадают буква в + букву, и человек видел один и тот же адрес дважды в + каждой строке списка. Найдено на проде 27.08. */} + {item.full_address !== item.label && ( + {item.full_address} + )} ))} diff --git a/tradein-mvp/frontend/src/app/mera-public/coverage-copy.ts b/tradein-mvp/frontend/src/app/mera-public/coverage-copy.ts index aa0e09f6..6df3344b 100644 --- a/tradein-mvp/frontend/src/app/mera-public/coverage-copy.ts +++ b/tradein-mvp/frontend/src/app/mera-public/coverage-copy.ts @@ -64,10 +64,18 @@ function tilesFor(probe: CoverageProbe): CoverageTile[] { ]; if (probe.median_listing_age_days !== null) { + // Медиана считается НЕ по всей когорте, а только по тем объявлениям, у + // которых известна дата публикации. Прежняя подпись — «из этих N» — + // указывала словом «этих» на число в плитке выше (в проде: 30 квартир, + // а рядом «из этих 6»), и человек читал два спорящих числа. Называем + // долю явно, когда она меньше целого. Найдено на проде 27.08. + const partial = probe.n_with_age < probe.n_listings; tiles.push({ value: `${probe.median_listing_age_days} дн.`, // Формулировка запинена тестом — см. шапку модуля. - label: `столько в среднем висит объявление из этих ${probe.n_with_age}`, + label: partial + ? `столько в среднем висит объявление; дата известна у ${probe.n_with_age} из ${probe.n_listings}` + : "столько в среднем висит объявление", }); } -- 2.45.3