feat(mera): публичный лэндинг «МЕРА» — контент и вёрстка #2615
Merged
lekss361
merged 1 commit from 2026-08-01 20:03:56 +00:00
feat/mera-landing into main
1 commit
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
| 6ac4ad9867 |
feat(mera): публичный лэндинг «МЕРА» — контент и вёрстка
All checks were successful
CI Trade-In / changes (pull_request) Successful in 9s
CI / changes (pull_request) Successful in 10s
CI Trade-In / backend-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 1m7s
Только фронт. Caddyfile, DNS и бэкенд не тронуты: периметр и домен
meraocenka.ru делаются отдельным PR, чтобы горячий Caddyfile не менялся
параллельно с эпиком «единый вход».
Заменяет заглушку из feat/mera-b2c-perimeter (48 строк «скоро откроется»)
на полноценную страницу: первый экран, как это работает, что человек
получает, откуда данные, вопросы-ответы, подвал и страница обработки ПДн.
## Починен живой баг, из-за которого лэндинг уводил бы людей на форму входа
RouteGuard регистрировал useEffect с router.push('/login') ДО early-return
для публичных путей. Хуки выполняются всегда, поэтому на проде аноним на
/mera-public получал фоновый GET /api/v1/me → 401 → редирект на логин, и
«безусловный bypass» до этого просто не доходил.
Guard разделён: RouteGuard теперь только смотрит pathname, а весь закрытый
контур (useMe, RBAC, экраны отказа) вынесен в GuardedRoute и подключён через
next/dynamic — это точка разрыва графа импортов, а не только логическая
развилка. Проверено на живой странице: запросов к API — НОЛЬ.
## Обещания приведены в соответствие с продуктом
Ревью по честности нашло 1 critical и 4 high — всё это обещания, которых
продукт не выполняет. Убрано или переписано:
- «Отчёт в PDF» — ручка owner-scoped, анониму отдаёт 401 by design;
- «список объектов, на которых построен расчёт» — план прямо запрещает
показывать анониму сырые объявления конкурентов;
- шесть городов подавались как равнозначные, хотя сбор вне Екатеринбурга
выключен (миграция 179, enabled=false) и данных единицы. Теперь честно:
полное покрытие — Екатеринбург, по области данных меньше;
- «в расчёте два типа данных» умалчивало третий — чужие оценочные модели,
которые реально двигают итоговую цифру (estimator.py, IMV/Yandex blend);
- страница ПДн обещала удаление данных, механизма которого нет.
Тексты и правовые формулировки вынесены в content.ts одним местом, рядом с
ссылками на код, который их подтверждает. Финальная редакция privacy —
за юристом, это помечено в файле.
## Форма адреса — честная заглушка, и это вынужденно
Живого автокомплита быть не может: _PUBLIC_PATHS «Меры» (rbac.py) открывает
анониму только health/docs/login/logout и анонимный чат. /geocode/suggest и
/trade-in/estimate отдают анониму 401. Открывать их до анти-абуза (этап 2
плана B2C) прямо запрещено планом. Форма честно говорит, что произойдёт,
и не изображает работу, которой нет. Переключается флагом
PUBLIC_ESTIMATE_ENABLED, рядом с ним — гейт из трёх условий.
## Проверено живьём, не по отчёту
- 360px и 390px: горизонтального переполнения нет (единственный элемент за
экраном — skip-link, так и задумано);
- один h1, иерархия H1→H2→H3 без пропусков, landmark-разметка;
- внешних ресурсов ноль — ни CDN, ни шрифтов, ни картинок с чужих доменов;
- ноль запросов к /api/** со страницы;
- tsc --noEmit чист.
NB для ревьюера: .claude/rules/ui-*.md по frontmatter paths: матчат
frontend/**, то есть корневой фронт Site Finder, а не tradein-mvp/frontend.
Здесь применяется дизайн-система v2/tokens.ts. Tailwind в этом фронте нет.
|