feat(tradein/sale-share): standalone «Поиск домов» — de-Mera + gendsgn.ru/sale-share #2088

Merged
lekss361 merged 1 commit from feat/tradein-sale-share-standalone into main 2026-06-28 22:23:53 +00:00
4 changed files with 97 additions and 4 deletions

View file

@ -91,6 +91,15 @@ gendsgn.ru {
}
}
# gendsgn.ru/sale-share — короткий адрес standalone-продукта «Поиск домов».
# Next basePath=/trade-in → редиректим на канонический /trade-in/sale-share
# (тот же tradein-frontend контейнер; query-string сохраняется). True vanity-URL
# в адресной строке требует отдельного Next-app с basePath=/sale-share.
@saleshare path /sale-share /sale-share/
handle @saleshare {
redir /trade-in/sale-share permanent
}
# Matcher `path /trade-in /trade-in/*` ловит И /trade-in (без слеша),
# И /trade-in/ + /trade-in/anything. Без обоих случаев `handle /trade-in/*`
# пропускал /trade-in без слеша → попадал в общий frontend → пустой ответ.

View file

@ -0,0 +1,18 @@
import type { Metadata } from "next";
// Route-level metadata override: /sale-share — самостоятельный продукт «Поиск
// домов», без бренда «Мера». Next app-router мержит это поверх root metadata
// (src/app/layout.tsx) только для этого сегмента. Server component (no "use
// client") — metadata экспортируется лишь из серверных модулей.
export const metadata: Metadata = {
title: "Поиск домов — gendsgn",
description: "Поиск вторичных домов с высокой долей квартир в продаже",
};
export default function SaleShareLayout({
children,
}: {
children: React.ReactNode;
}) {
return <>{children}</>;
}

View file

@ -11,7 +11,7 @@ import { useCallback, useMemo, useRef, useState } from "react";
import dynamic from "next/dynamic";
import "@/components/trade-in/trade-in.css";
import { Topbar } from "@/components/trade-in/Topbar";
import { SaleShareHeader } from "@/components/trade-in/SaleShareHeader";
import { SaleShareControls } from "@/components/trade-in/SaleShareControls";
import { SaleShareList } from "@/components/trade-in/SaleShareList";
import { BuildingListingsDrawer } from "@/components/trade-in/BuildingListingsDrawer";
@ -105,11 +105,12 @@ export default function SaleSharePage() {
return (
<>
<Topbar active="sale-share" />
<SaleShareHeader />
<main className="page">
<div className="crumbs">
<em>Главная</em> <span></span> <em>Мера</em> <span></span> Доля квартир в продаже
<em>Главная</em> <span></span> <em>Поиск домов</em> <span></span> Доля квартир
в продаже
</div>
<div className="page-title">
@ -209,7 +210,7 @@ export default function SaleSharePage() {
<footer className="page-foot">
<div>
Мера · MVP ·{" "}
Поиск домов ·{" "}
<span className="mono">доля квартир в продаже · вторичный рынок ЕКБ</span>
</div>
</footer>

View file

@ -0,0 +1,65 @@
"use client";
/**
* SaleShareHeader топбар самостоятельного продукта «Поиск домов».
*
* В отличие от `Topbar.tsx` (бренд «Мера» + табы оценки/истории/скраперов)
* это минимальная шапка БЕЗ марки «Мера» и без продуктовых табов: только
* название продукта слева и личный кабинет (UserMenu) справа, чтобы
* login/logout продолжали работать. Layout/токены переиспользуются из
* `trade-in.css` (`.topbar` / `.topbar-inner` / `.brand` / `.top-nav`),
* глиф-бейдж `.brand-mark` нейтральный (домик), не Mera-специфичный.
*/
import { UserMenu } from "@/components/auth/UserMenu";
/** Нейтральный глиф «дом» для бейджа продукта (currentColor, в стиле
.brand-mark-svg тёмный фон даёт CSS-бейдж .brand-mark). */
function HouseMark() {
return (
<svg
viewBox="0 0 28 28"
className="brand-mark-svg"
aria-hidden="true"
focusable="false"
>
<path
d="M5 13l9-7 9 7"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinejoin="round"
strokeLinecap="round"
/>
<path
d="M7.5 11.5V21h13v-9.5"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinejoin="round"
strokeLinecap="round"
/>
</svg>
);
}
export function SaleShareHeader() {
return (
<header className="topbar">
<div className="topbar-inner">
<div className="brand">
<span className="brand-mark">
<HouseMark />
</span>
<span className="brand-product">Поиск домов</span>
</div>
{/* Только личный кабинет никакой Mera-навигации. `.top-nav`
переиспользуется ради margin-left:auto. В dev (401) или до /me
UserMenu сам рендерит null. */}
<div className="top-nav">
<UserMenu />
</div>
</div>
</header>
);
}