merge main: объединить legal-copy.ts (PRIVACY_PATH + SHORT_ESTIMATE_DISCLAIMER)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI / changes (pull_request) Successful in 10s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Successful in 1m29s
CI Trade-In / backend-tests (pull_request) Successful in 4m49s
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI / changes (pull_request) Successful in 10s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Successful in 1m29s
CI Trade-In / backend-tests (pull_request) Successful in 4m49s
Оба модуля были созданы параллельно в разных ветках под один и тот же файл: здесь — путь к политике ПДн для ссылки в чекбоксе согласия (блок 2), в #2884 — короткая оговорка под диапазоном цены (блок 4.1). Обе константы живут в одном модуле-без-импортов, шапка объединена.
This commit is contained in:
commit
f1491068f4
5 changed files with 82 additions and 21 deletions
|
|
@ -4,6 +4,14 @@
|
||||||
* Логотип НЕ является ссылкой на "/": в next.config.ts стоит redirect "/" → "/v2",
|
* Логотип НЕ является ссылкой на "/": в next.config.ts стоит redirect "/" → "/v2",
|
||||||
* то есть клик по нему выкинул бы публичного посетителя в закрытое B2B-приложение
|
* то есть клик по нему выкинул бы публичного посетителя в закрытое B2B-приложение
|
||||||
* (и дальше на /login). Пока публичная страница одна, вордмарк — просто текст.
|
* (и дальше на /login). Пока публичная страница одна, вордмарк — просто текст.
|
||||||
|
*
|
||||||
|
* ФОРМУЛИРОВКА ДЕСКРИПТОРА (14.08.2026, юр-документ владельца, блок 3). Строка
|
||||||
|
* шапки обязана читаться как «МЕРА · Оценка вторичного жилья по рыночным данным ·
|
||||||
|
* <регион>»: слова «по рыночным данным» — то, чем сервис отличается от оценки по
|
||||||
|
* 135-ФЗ, и юрист попросил, чтобы это стояло в самом заметном дескрипторе, а не
|
||||||
|
* только в дисклеймерах внизу. «МЕРА» приходит из вордмарка ниже (у него свой
|
||||||
|
* letter-spacing), поэтому в headerTag её нет — строка складывается из двух узлов.
|
||||||
|
* Регион — из REGION_NAME, не строкой (правило content.ts).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { REGION_NAME } from "../content";
|
import { REGION_NAME } from "../content";
|
||||||
|
|
@ -18,7 +26,7 @@ export function SiteHeader() {
|
||||||
МЕРА
|
МЕРА
|
||||||
</div>
|
</div>
|
||||||
<p className={styles.headerTag}>
|
<p className={styles.headerTag}>
|
||||||
Оценка вторичного жилья · {REGION_NAME}
|
Оценка вторичного жилья по рыночным данным · {REGION_NAME}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
|
||||||
|
|
@ -46,8 +46,16 @@ const plexMono = IBM_Plex_Mono({
|
||||||
display: "swap",
|
display: "swap",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ЗАГОЛОВОК ВКЛАДКИ (14.08.2026, юр-документ владельца, блок 3). «Расчёт
|
||||||
|
* стоимости», а не «оценка квартиры»: заголовок вкладки — второе после шапки
|
||||||
|
* место, где формулировка читается как заявление о характере услуги, и юрист
|
||||||
|
* попросил увести её от слова, отсылающего к отчёту оценщика по 135-ФЗ.
|
||||||
|
* Заголовки трёх юридических подстраниц (оферта / возврат / ПДн) живут по
|
||||||
|
* своему шаблону «<Документ> — МЕРА» и этой правкой не затрагиваются.
|
||||||
|
*/
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "МЕРА — оценка квартиры на вторичном рынке",
|
title: "Мера · расчёт стоимости квартиры по рыночным данным",
|
||||||
description: `Узнайте рыночную стоимость квартиры по адресу: сделки Росреестра и объявления площадок. ${REGION_NAME}.`,
|
description: `Узнайте рыночную стоимость квартиры по адресу: сделки Росреестра и объявления площадок. ${REGION_NAME}.`,
|
||||||
robots: { index: false, follow: false },
|
robots: { index: false, follow: false },
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,9 @@
|
||||||
/**
|
/**
|
||||||
* HeroSummary — Секция 1 «Сводка» из mockup tradein.html.
|
* HeroSummary — Секция 1 «Сводка» из mockup tradein.html.
|
||||||
* Показывает медиану + достоверность CV + параметры объекта + 2 ценовых бара (объявления / сделки).
|
* Показывает медиану + достоверность CV + параметры объекта + 2 ценовых бара (объявления / сделки).
|
||||||
|
*
|
||||||
|
* hero-duo__hint под диапазоном сделок несёт юр-требование владельца от
|
||||||
|
* 14.08.2026 (блок 4.1): SHORT_ESTIMATE_DISCLAIMER — см. `lib/legal-copy.ts`.
|
||||||
*/
|
*/
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import type { AggregatedEstimate, TradeInEstimateInput, HouseType, RepairState, ConfidenceLevel } from "@/types/trade-in";
|
import type { AggregatedEstimate, TradeInEstimateInput, HouseType, RepairState, ConfidenceLevel } from "@/types/trade-in";
|
||||||
|
|
@ -12,6 +15,7 @@ import { asHouseType, asRepairState } from "@/types/trade-in";
|
||||||
import { useActiveBrandSlug, useBrand } from "@/lib/useBrand";
|
import { useActiveBrandSlug, useBrand } from "@/lib/useBrand";
|
||||||
import { sourceDot, sourceLabel } from "@/lib/source-registry";
|
import { sourceDot, sourceLabel } from "@/lib/source-registry";
|
||||||
import { dealsAsOfLabel } from "@/lib/rosreestr";
|
import { dealsAsOfLabel } from "@/lib/rosreestr";
|
||||||
|
import { SHORT_ESTIMATE_DISCLAIMER } from "@/lib/legal-copy";
|
||||||
import { HeroTransparency } from "./HeroTransparency";
|
import { HeroTransparency } from "./HeroTransparency";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
|
@ -478,6 +482,9 @@ export function HeroSummary({ estimate, input, onResubmit, isResubmitting = fals
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<p className="hero-duo__hint">
|
<p className="hero-duo__hint">
|
||||||
|
{/* Блок 4.1 (владелец, 14.08.2026) — юр-строка идёт первым
|
||||||
|
предложением абзаца-дисклеймера, не отдельным блоком. */}
|
||||||
|
{SHORT_ESTIMATE_DISCLAIMER}{" "}
|
||||||
Диапазон отражает разброс цен по рынку, а не погрешность оценки.
|
Диапазон отражает разброс цен по рынку, а не погрешность оценки.
|
||||||
{/* #2209: честное раскрытие точности — замеренное покрытие бэктестом
|
{/* #2209: честное раскрытие точности — замеренное покрытие бэктестом
|
||||||
по реальным ДКП Росреестра (2026-07: n=276, coverage 81.5%).
|
по реальным ДКП Росреестра (2026-07: n=276, coverage 81.5%).
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,14 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* §1.2-дисклеймер под ценовыми карточками несёт юр-требование владельца от
|
||||||
|
* 14.08.2026 (блок 4.1): SHORT_ESTIMATE_DISCLAIMER — см. `lib/legal-copy.ts`.
|
||||||
|
*/
|
||||||
import { Fragment } from "react";
|
import { Fragment } from "react";
|
||||||
import type { Ref } from "react";
|
import type { Ref } from "react";
|
||||||
import { tokens } from "./tokens";
|
import { tokens } from "./tokens";
|
||||||
import type { ResultPanelData } from "./mappers";
|
import type { ResultPanelData } from "./mappers";
|
||||||
|
import { SHORT_ESTIMATE_DISCLAIMER } from "@/lib/legal-copy";
|
||||||
|
|
||||||
const {
|
const {
|
||||||
accent,
|
accent,
|
||||||
|
|
@ -557,20 +562,29 @@ export default function ResultPanel({
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* §1.2 честный разброс: диапазоны цен = рыночный спред, НЕ погрешность
|
{/* §1.2 честный разброс + юр-дисклеймер (владелец, 14.08.2026, блок 4.1):
|
||||||
оценки. Ставим сразу под ценовыми карточками, чтобы широкий диапазон
|
SHORT_ESTIMATE_DISCLAIMER идёт первым предложением, "разброс цен"
|
||||||
не читался как «неуверенность расчёта». */}
|
— вторым, один абзац (три отдельных оговорки подряд читались бы
|
||||||
|
как шум). Ставим сразу под ценовыми карточками, чтобы широкий
|
||||||
|
диапазон не читался как «неуверенность расчёта».
|
||||||
|
Компенсация высоты: этот блок стал на строку выше, чем до
|
||||||
|
SHORT_ESTIMATE_DISCLAIMER (776px средняя колонка узкая для двух
|
||||||
|
предложений в одну строку) — lineHeight/marginTop подрезаны, и
|
||||||
|
padding у "ranges + radar" box ниже уменьшен по вертикали, чтобы
|
||||||
|
не сжимать плитки "ИСТОЧНИКИ ДАННЫХ" (flex:1 ниже). Высота
|
||||||
|
артборда (1536×1024, v2/page.tsx) не менялась. */}
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
// M3 — было 10px muted-микропринт; повышено до 12px body, чтобы
|
// M3 — было 10px muted-микропринт; повышено до 12px body, чтобы
|
||||||
// ключевая honest-оговорка о разбросе читалась, а не терялась.
|
// ключевая honest-оговорка о разбросе читалась, а не терялась.
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
lineHeight: 1.5,
|
lineHeight: 1.4,
|
||||||
color: body,
|
color: body,
|
||||||
marginTop: -4,
|
marginTop: -6,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Диапазоны показывают разброс цен на рынке, а не погрешность оценки.
|
{SHORT_ESTIMATE_DISCLAIMER} Диапазоны показывают разброс цен на рынке,
|
||||||
|
а не погрешность оценки.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* ranges + radar */}
|
{/* ranges + radar */}
|
||||||
|
|
@ -581,7 +595,9 @@ export default function ResultPanel({
|
||||||
backdropFilter: "blur(6px)",
|
backdropFilter: "blur(6px)",
|
||||||
border: `1px solid ${line2}`,
|
border: `1px solid ${line2}`,
|
||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
padding: "18px 20px",
|
// было "18px 20px" — вертикаль подрезана на 4px сверху/снизу, см.
|
||||||
|
// комментарий к дисклеймеру выше (компенсация высоты под #4.1).
|
||||||
|
padding: "14px 20px",
|
||||||
display: "grid",
|
display: "grid",
|
||||||
gridTemplateColumns: "1fr 1fr",
|
gridTemplateColumns: "1fr 1fr",
|
||||||
gap: 24,
|
gap: 24,
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,30 @@
|
||||||
/**
|
/**
|
||||||
* Юридические константы, разделяемые между B2C-лэндингом
|
* Юридические константы и короткие оговорки, разделяемые между B2C-лэндингом
|
||||||
* (`src/app/mera-public/content.ts`) и B2B trade-in виджетами
|
* (`src/app/mera-public/content.ts`) и B2B trade-in виджетами
|
||||||
* (`src/components/trade-in/**`, включая v2 `LeadForm.tsx`).
|
* (`src/components/trade-in/**`, включая v2 `ResultPanel.tsx` и `LeadForm.tsx`).
|
||||||
*
|
*
|
||||||
* ПОЧЕМУ отдельный модуль: v2-компонент результата оценки (B2B-экран,
|
* Источник требований: юр-документ владельца от 14.08.2026 — блок 4.1 (строка
|
||||||
* пилоты/менеджеры) не должен тянуть `mera-public/content.ts` целиком —
|
* под диапазоном цены) и блок 2 (ссылка на политику ПДн рядом с чекбоксом
|
||||||
* тот модуль набит B2C-лэндинг-специфичными константами (`LEGAL_ENTITY`,
|
* согласия).
|
||||||
* `PUBLIC_ESTIMATE_ENABLED`, банковские реквизиты и т.д.), появление
|
|
||||||
* которых в графе импортов B2B-экрана было бы случайной и нежелательной
|
|
||||||
* связью двух разных поверхностей продукта. Вместо этого путь к странице
|
|
||||||
* политики ПДн живёт здесь — в модуле БЕЗ импортов, который могут
|
|
||||||
* безопасно использовать обе стороны.
|
|
||||||
*
|
*
|
||||||
* `content.ts` ре-экспортирует `PRIVACY_PATH` отсюда (не дублирует
|
* ПОЧЕМУ отдельный модуль: v2-компоненты (B2B-экран, пилоты/менеджеры) не
|
||||||
* литерал) — единственный источник истины для пути.
|
* должны тянуть `mera-public/content.ts` целиком — тот модуль набит
|
||||||
|
* B2C-лэндинг-специфичными константами (`LEGAL_ENTITY`, банковские реквизиты,
|
||||||
|
* `PUBLIC_ESTIMATE_ENABLED`), появление которых в графе импортов B2B-экрана
|
||||||
|
* было бы случайной и нежелательной связью двух поверхностей продукта.
|
||||||
|
*
|
||||||
|
* Модуль НАМЕРЕННО без импортов (кроме типов) — его тянут и закрытый контур, и
|
||||||
|
* `/mera-public`, где действует isolation-guard
|
||||||
|
* (`scripts/check-mera-public-isolation.mjs`): любой транзитивный импорт
|
||||||
|
* закрытого контура (useMe / lib/api / lib/sessionId / isPathAllowed) в
|
||||||
|
* публичный бандл провалит проверку.
|
||||||
|
*
|
||||||
|
* В репозитории уже есть соседние, но НЕ идентичные дисклеймеры — не путать и
|
||||||
|
* не плодить пятый/шестой литерал с той же сутью:
|
||||||
|
* - `src/app/mera-public/content.ts` → DELIVERABLES_DISCLAIMER (135-ФЗ)
|
||||||
|
* - `src/app/mera-public/_components/SiteFooter.tsx`
|
||||||
|
* - `src/app/mera-public/oferta/page.tsx`
|
||||||
|
* - `backend/app/services/exporters/trade_in_pdf.py` (версия для PDF-отчёта)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -22,5 +33,16 @@
|
||||||
* Публично та же страница доступна коротким адресом meraocenka.ru/privacy
|
* Публично та же страница доступна коротким адресом meraocenka.ru/privacy
|
||||||
* (см. `PUBLIC_PRIVACY_URL` в `mera-public/content.ts`), но для ссылки из
|
* (см. `PUBLIC_PRIVACY_URL` в `mera-public/content.ts`), но для ссылки из
|
||||||
* чекбоксов согласия внутри приложения нужен именно внутренний путь.
|
* чекбоксов согласия внутри приложения нужен именно внутренний путь.
|
||||||
|
*
|
||||||
|
* `content.ts` ре-экспортирует эту константу (не дублирует литерал) —
|
||||||
|
* единственный источник истины для пути.
|
||||||
*/
|
*/
|
||||||
export const PRIVACY_PATH = "/mera-public/privacy";
|
export const PRIVACY_PATH = "/mera-public/privacy";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Строка, которая обязана стоять непосредственно под диапазоном цены на экране
|
||||||
|
* результата — и в боевом `/v2` (`v2/ResultPanel.tsx`), и в превью-контуре
|
||||||
|
* (`HeroSummary.tsx`, `/trade-in/ui-preview/estimate`).
|
||||||
|
*/
|
||||||
|
export const SHORT_ESTIMATE_DISCLAIMER =
|
||||||
|
"Ориентировочный расчёт. Не является официальной оценкой.";
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue