From 4d51d19f7c4a5d554239b0664af12a475de7bf49 Mon Sep 17 00:00:00 2001 From: bot-backend Date: Mon, 31 Aug 2026 12:40:51 +0500 Subject: [PATCH] =?UTF-8?q?feat(mera-public):=20=D0=BF=D1=83=D0=B1=D0=BB?= =?UTF-8?q?=D0=B8=D1=87=D0=BD=D0=B0=D1=8F=20=D1=81=D1=82=D1=80=D0=B0=D0=BD?= =?UTF-8?q?=D0=B8=D1=86=D0=B0=20/business=20=D0=B2=D0=BC=D0=B5=D1=81=D1=82?= =?UTF-8?q?=D0=BE=20=D1=81=D1=81=D1=8B=D0=BB=D0=BA=D0=B8=20=D0=BD=D0=B0=20?= =?UTF-8?q?=D1=84=D0=BE=D1=80=D0=BC=D1=83=20=D0=B2=D1=85=D0=BE=D0=B4=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Кнопка «Для бизнеса» в шапке и «МЕРА для бизнеса» в подвале вели на B2B_URL. Реальная цепочка на проде: 308 → /trade-in → 307 → /trade-in/v2 → клиентский редирект → /trade-in/login. Посетитель публичного сайта упирался в форму «Логин / Пароль» от аккаунта, которого у него нет. Заведена страница /business: кому полезен бизнес-контур, чем он отличается от бесплатной проверки, и как связаться (почта и телеграм — те же константы, что в подвале и в документах, не выдуманные для страницы). Ни цен, ни сроков, ни объёмов: этих величин никто не мерил. Ссылка на вход оставлена внутри страницы второстепенной — «уже работаете с нами». Периметр согласован во всех четырёх местах: PUBLIC_ROUTES, три матчера Caddy (@meraPages, @meraShortSlash, @meraLongPages), PUBLIC_SHORT_PATHS в RouteGuard, плюс строка в smoke-mera-perimeter.sh. Гейт периметра проверен фальсификацией: удаление /business из @meraShortSlash роняет public-perimeter.test.ts. noindex — тем же способом, что у /docs (metadata.robots). Co-Authored-By: Claude Opus 5 --- caddy/sites/apps.caddy | 6 +- scripts/smoke-mera-perimeter.sh | 5 + .../mera-public/_components/InnerHeader.tsx | 5 +- .../mera-public/_components/v3/FooterV3.tsx | 18 +- .../mera-public/_components/v3/HeaderV3.tsx | 5 +- .../src/app/mera-public/business/page.tsx | 154 ++++++++++++++++++ .../frontend/src/app/mera-public/content.ts | 10 ++ .../src/components/auth/RouteGuard.tsx | 1 + 8 files changed, 188 insertions(+), 16 deletions(-) create mode 100644 tradein-mvp/frontend/src/app/mera-public/business/page.tsx diff --git a/caddy/sites/apps.caddy b/caddy/sites/apps.caddy index 8010c04b..085146a4 100644 --- a/caddy/sites/apps.caddy +++ b/caddy/sites/apps.caddy @@ -347,7 +347,7 @@ meraocenka.ru { # `trailingSlash: false` ответил бы на такой путь 308-редиректом на вариант # без слэша — то есть на ДЛИННЫЙ адрес, который handle ниже отправит 301 на # «/», и запрос закольцуется. - @meraPages path /estimate /oferta /refund /privacy /articles /articles/kak-ocenit-kvartiru /docs + @meraPages path /estimate /oferta /refund /privacy /articles /articles/kak-ocenit-kvartiru /docs /business handle @meraPages { rewrite * /trade-in/mera-public{path} reverse_proxy tradein-frontend:3000 { @@ -361,7 +361,7 @@ meraocenka.ru { # голый 404 (так было и до этого PR, с момента #2615). Заодно это # замыкает цепочку для длинных адресов со слэшем: они приходят на короткий # со слэшем и здесь нормализуются. - @meraShortSlash path_regexp shortslash ^/(estimate|oferta|refund|privacy|articles|articles/kak-ocenit-kvartiru|docs)/$ + @meraShortSlash path_regexp shortslash ^/(estimate|oferta|refund|privacy|articles|articles/kak-ocenit-kvartiru|docs|business)/$ handle @meraShortSlash { redir * /{re.shortslash.1} permanent } @@ -446,7 +446,7 @@ meraocenka.ru { # порядок директив внутри `handle` определяет Caddy, и без неё `redir` # выполняется РАНЬШЕ `uri`, отдавая Location, равный исходному адресу # (бесконечный цикл; поймано на локальном стенде). - @meraLongPages path /trade-in/mera-public/estimate /trade-in/mera-public/estimate/ /trade-in/mera-public/oferta /trade-in/mera-public/oferta/ /trade-in/mera-public/refund /trade-in/mera-public/refund/ /trade-in/mera-public/privacy /trade-in/mera-public/privacy/ /trade-in/mera-public/articles /trade-in/mera-public/articles/ /trade-in/mera-public/articles/kak-ocenit-kvartiru /trade-in/mera-public/articles/kak-ocenit-kvartiru/ /trade-in/mera-public/docs /trade-in/mera-public/docs/ + @meraLongPages path /trade-in/mera-public/estimate /trade-in/mera-public/estimate/ /trade-in/mera-public/oferta /trade-in/mera-public/oferta/ /trade-in/mera-public/refund /trade-in/mera-public/refund/ /trade-in/mera-public/privacy /trade-in/mera-public/privacy/ /trade-in/mera-public/articles /trade-in/mera-public/articles/ /trade-in/mera-public/articles/kak-ocenit-kvartiru /trade-in/mera-public/articles/kak-ocenit-kvartiru/ /trade-in/mera-public/docs /trade-in/mera-public/docs/ /trade-in/mera-public/business /trade-in/mera-public/business/ handle @meraLongPages { route { uri strip_prefix /trade-in/mera-public diff --git a/scripts/smoke-mera-perimeter.sh b/scripts/smoke-mera-perimeter.sh index 52357c7b..478adcda 100644 --- a/scripts/smoke-mera-perimeter.sh +++ b/scripts/smoke-mera-perimeter.sh @@ -90,6 +90,11 @@ check "meraocenka.ru/privacy — public 200" "$BASE_MERA/privacy" 200 # лэндинга. Отвалится handle — кнопки «Проверить» станут ссылками в 404. check "meraocenka.ru/estimate — public 200" "$BASE_MERA/estimate" 200 +# 1d2. Страница «МЕРА для бизнеса» — короткий адрес, на который ведут пункт +# шапки и подвала. До 31.08.2026 они вели в закрытый контур и приводили +# человека на форму входа; отвалится handle — вернётся 404 вместо неё. +check "meraocenka.ru/business — public 200" "$BASE_MERA/business" 200 + # 1e. Длинные адреса поддерева отдают 301 на короткие: у страницы один # канонический адрес, а старые ссылки и закладки продолжают работать. # ГОЛЫЙ /trade-in/mera-public — регресс на баг 15.08.2026: прежний матчер diff --git a/tradein-mvp/frontend/src/app/mera-public/_components/InnerHeader.tsx b/tradein-mvp/frontend/src/app/mera-public/_components/InnerHeader.tsx index be973f8f..89a5a042 100644 --- a/tradein-mvp/frontend/src/app/mera-public/_components/InnerHeader.tsx +++ b/tradein-mvp/frontend/src/app/mera-public/_components/InnerHeader.tsx @@ -1,4 +1,3 @@ -import { B2B_URL } from "../content"; import styles from "../landing-v3.module.css"; import { PublicLink } from "./PublicLink"; @@ -57,9 +56,9 @@ export function InnerHeader() {
- + Для бизнеса - +
diff --git a/tradein-mvp/frontend/src/app/mera-public/_components/v3/FooterV3.tsx b/tradein-mvp/frontend/src/app/mera-public/_components/v3/FooterV3.tsx index 9a313872..be82a909 100644 --- a/tradein-mvp/frontend/src/app/mera-public/_components/v3/FooterV3.tsx +++ b/tradein-mvp/frontend/src/app/mera-public/_components/v3/FooterV3.tsx @@ -21,16 +21,20 @@ * а добавляет одну точку входа с тарифами и порядком возврата. * * ССЫЛКИ — КОРОТКИЕ (15.08.2026). «Проверить квартиру» ведёт на `/estimate` — - * отдельный экран проверки, а не на якорь формы в герое. «МЕРА для бизнеса» - * стала абсолютной: относительный `/` на публичном домене вёл на сам лэндинг, - * то есть пункт не работал. Все переходы — обычным `` через `PublicLink`, - * потому что `next/link` подставляет basePath (см. его шапку). + * отдельный экран проверки, а не на якорь формы в герое. Все переходы — + * обычным `` через `PublicLink`, потому что `next/link` подставляет + * basePath (см. его шапку). + * + * «МЕРА ДЛЯ БИЗНЕСА» ВЕДЁТ НА `/business`, А НЕ НА `B2B_URL` (31.08.2026). + * Абсолютный адрес B2B-контура заканчивался формой входа `/trade-in/login` — + * посетителю публичного сайта предлагали залогиниться в аккаунт, которого у + * него нет. Теперь пункт ведёт на публичную страницу связи, а ссылка на вход + * живёт внутри неё, второстепенной. */ import { safeUrl } from "@/lib/safeUrl"; import { - B2B_URL, LEGAL_ENTITY, REGION_NAME, SUPPORT_EMAIL, @@ -70,9 +74,9 @@ export function FooterV3() { Статьи - + МЕРА для бизнеса - + diff --git a/tradein-mvp/frontend/src/app/mera-public/_components/v3/HeaderV3.tsx b/tradein-mvp/frontend/src/app/mera-public/_components/v3/HeaderV3.tsx index 88d71e80..1b932173 100644 --- a/tradein-mvp/frontend/src/app/mera-public/_components/v3/HeaderV3.tsx +++ b/tradein-mvp/frontend/src/app/mera-public/_components/v3/HeaderV3.tsx @@ -49,7 +49,6 @@ * был `CityPicker`). */ -import { B2B_URL } from "../../content"; import styles from "../../landing-v3.module.css"; import { PublicLink } from "../PublicLink"; @@ -87,9 +86,9 @@ export function HeaderV3() {
- + Для бизнеса - + Проверить квартиру diff --git a/tradein-mvp/frontend/src/app/mera-public/business/page.tsx b/tradein-mvp/frontend/src/app/mera-public/business/page.tsx new file mode 100644 index 00000000..38de70c0 --- /dev/null +++ b/tradein-mvp/frontend/src/app/mera-public/business/page.tsx @@ -0,0 +1,154 @@ +import type { Metadata } from "next"; + +import { safeUrl } from "@/lib/safeUrl"; + +import { InnerHeader } from "../_components/InnerHeader"; +import { PublicLink } from "../_components/PublicLink"; +import { FooterV3 } from "../_components/v3/FooterV3"; +import { + B2B_URL, + SUPPORT_EMAIL, + SUPPORT_TELEGRAM_LABEL, + SUPPORT_TELEGRAM_URL, +} from "../content"; +import styles from "../landing-v3.module.css"; + +/** + * «МЕРА для бизнеса» — страница связи, а не вход. + * + * ЗАЧЕМ ЗАВЕДЕНА. Кнопка «Для бизнеса» в шапке и «МЕРА для бизнеса» в подвале + * вели на `B2B_URL`. Реальная цепочка на проде: 308 → `/trade-in` → 307 → + * `/trade-in/v2` → клиентский редирект → `/trade-in/login`. Посетитель + * публичного сайта упирался в форму «Логин / Пароль» от аккаунта, которого у + * него нет и завести который на этом экране нельзя. Владелец просил починить + * это дважды. + * + * ЧЕГО ЗДЕСЬ НЕТ И ПОЧЕМУ. Ни цен, ни сроков ответа, ни объёмов, ни числа + * клиентов: таких величин никто не мерил, а на странице они читались бы как + * обязательство (то же решение, что в `docs/page.tsx` про часы поддержки). + * Формы заявки тоже нет — принимать её в продукте нечем: анонимной ручки для + * B2B-лида не существует, и форма, которая никуда не пишет, хуже честной + * ссылки на живой канал. Каналы взяты из `content.ts` — те же, что в подвале и + * в документах, а не заведены для этой страницы отдельно. + * + * ССЫЛКА НА ВХОД ОСТАВЛЕНА, но второстепенной: у кого доступ уже есть, тот + * пришёл именно за ней, и убрать её значило бы чинить один дефект вторым. + */ + +export const metadata: Metadata = { + title: "МЕРА для бизнеса — связаться", + // Тот же noindex, что у остального публичного дерева (см. layout.tsx и + // docs/page.tsx): страница открыта по адресу, но в выдачу не идёт. + robots: { index: false, follow: false }, +}; + +export default function BusinessPage() { + const telegramHref = safeUrl(SUPPORT_TELEGRAM_URL); + + return ( + <> + +
+ + +
+
ДЛЯ БИЗНЕСА
+

МЕРА для бизнеса

+

+ Оценка квартиры по живым объявлениям — не для одной сделки, а + потоком: для агентства, застройщика или компании, которая принимает + квартиры в зачёт. +

+
+ +
+

+ Кому это нужно +

+

+ Тем, кто считает не одну квартиру, а много и регулярно: агентствам + недвижимости, отделам продаж застройщиков, компаниям с программами + трейд-ин. Расчёт идёт по тем же источникам, что и бесплатная + проверка на сайте, — но по вашему потоку адресов и под вашими + учётными записями. +

+

+ Если задача разовая — квартира одна, и нужно понять цену, — идти + сюда незачем: бесплатная проверка на сайте отвечает на этот вопрос + без договорённостей и без регистрации. +

+
+ +
+

+ Чем отличается от бесплатной проверки +

+
+ + Бесплатная проверка + + Одна квартира, здесь и сейчас, без регистрации. Картина рынка + вокруг дома: сколько похожих квартир продаётся рядом и как давно + висят объявления + + + + Бизнес-контур + + Закрытый контур с доступом по учётной записи: расчёты по потоку + адресов и история по ним. Доступ выдаётся вручную, после + разговора, — самостоятельной регистрации в нём нет + + +
+

+ Что именно войдёт в работу — обсуждается: задача разная у + агентства, у застройщика и у трейд-ин программы. Поэтому на этой + странице нет ни тарифа, ни срока: назвать их до разговора значило бы + их придумать. +

+
+ +
+

+ Как связаться +

+

+ Напишите в любой из каналов — коротко, что за компания и какая + задача: сколько адресов, как часто, что должно быть на выходе. + Этого хватает, чтобы ответить по существу с первого письма. +

+
+
+ ПОЧТА + + {SUPPORT_EMAIL} + +
+ {telegramHref ? ( + + ) : null} +
+

+ Уже работаете с нами? Вход в бизнес-контур — + по логину и паролю, которые вам выдали. +

+
+
+ + + ); +} diff --git a/tradein-mvp/frontend/src/app/mera-public/content.ts b/tradein-mvp/frontend/src/app/mera-public/content.ts index 375dfc94..4164842e 100644 --- a/tradein-mvp/frontend/src/app/mera-public/content.ts +++ b/tradein-mvp/frontend/src/app/mera-public/content.ts @@ -195,6 +195,10 @@ export const PUBLIC_ROUTES = { articles: "/articles", articleHowTo: "/articles/kak-ocenit-kvartiru", docs: "/docs", + // Страница B2B-контура: связаться и оставить заявку. Заведена 31.08.2026 + // взамен ссылки на `B2B_URL` в шапке и подвале — та вела в закрытый контур + // и заканчивалась формой входа для аккаунта, которого у посетителя нет. + business: "/business", } as const; /** Канонический публичный origin сервиса — база для canonical-ссылок. */ @@ -209,6 +213,12 @@ export const PUBLIC_ORIGIN = "https://meraocenka.ru"; * осознанно: это ссылка НАРУЖУ, из одного продукта в другой, и подставлять её * из текущего хоста было бы неверно. */ +/** + * ⚠️ ЭТО ВХОД В ЗАКРЫТЫЙ КОНТУР, А НЕ ВИТРИНА. Цепочка редиректов приводит на + * `/trade-in/login` — форму «Логин / Пароль». Человеку с улицы показывать её + * нельзя, поэтому шапка и подвал ведут на `PUBLIC_ROUTES.business`, а этот + * адрес остался ссылкой «у меня уже есть доступ» внутри той страницы. + */ export const B2B_URL = "https://gendsgn.ru/trade-in/"; /** Короткие публичные адреса тех же страниц (то, что напечатано в документах). */ diff --git a/tradein-mvp/frontend/src/components/auth/RouteGuard.tsx b/tradein-mvp/frontend/src/components/auth/RouteGuard.tsx index 229e03ec..6299c473 100644 --- a/tradein-mvp/frontend/src/components/auth/RouteGuard.tsx +++ b/tradein-mvp/frontend/src/components/auth/RouteGuard.tsx @@ -103,6 +103,7 @@ const PUBLIC_SHORT_PATHS = [ "/articles", "/articles/kak-ocenit-kvartiru", "/docs", + "/business", ]; export function isPublicPath(rawPath: string): boolean {