diff --git a/tradein-mvp/frontend/src/app/v2/layout.tsx b/tradein-mvp/frontend/src/app/v2/layout.tsx index 532ca966..ecc487d8 100644 --- a/tradein-mvp/frontend/src/app/v2/layout.tsx +++ b/tradein-mvp/frontend/src/app/v2/layout.tsx @@ -1,6 +1,7 @@ import type { Metadata } from "next"; import { IBM_Plex_Mono, Manrope } from "next/font/google"; +import { SupportButton } from "@/components/trade-in/v2/SupportButton"; import { pageBg } from "@/components/trade-in/v2/tokens"; // Manrope — primary sans typeface of the МЕРА HUD. next/font is bundled @@ -41,6 +42,10 @@ export default function TradeInV2Layout({ }} > {children} + {/* Mounted here (not the global app/layout.tsx) — that layout also + covers the admin `/scrapers/**` area and `/sale-share`, unrelated + products without the МЕРА brand that don't need a support link. */} + ); } diff --git a/tradein-mvp/frontend/src/components/trade-in/v2/SupportButton.tsx b/tradein-mvp/frontend/src/components/trade-in/v2/SupportButton.tsx new file mode 100644 index 00000000..2a41b0f4 --- /dev/null +++ b/tradein-mvp/frontend/src/components/trade-in/v2/SupportButton.tsx @@ -0,0 +1,116 @@ +"use client"; + +// SupportButton — floating link to the МЕРА Telegram support bot. +// +// WHY: before this, there was no way to reach support from the site. +// `LeadForm` (LeadForm.tsx) only mounts once `hasEstimate` is true +// (app/v2/page.tsx:1064), so a visitor who hasn't finished (or can't finish) +// an estimate has no contact path at all. The only other link anywhere in the +// UI is NoAccessScreen.tsx:84, and that only renders on the "access expired" +// screen. The support bot itself is already live on the backend (support +// bridge, PR #2526) — this button is purely the missing entry point on the +// site side. Plain external link, no chat widget / no state / no API calls. +// +// Portal to document.body (pattern mirrors MapPicker.tsx:104-108 and +// BuildingListingsDrawer.tsx): `/v2` renders its HUD inside a fixed +// 1536×1024 "artboard" that gets `transform: scale(...)` on narrow viewports +// (app/v2/page.tsx:860-871). A `position: fixed` descendant of a +// `transform`-ed ancestor is positioned relative to that ancestor, not the +// viewport — so without a portal the button would drift/scale with the HUD +// instead of staying pinned to the real viewport corner. +import { useEffect, useState } from "react"; +import { createPortal } from "react-dom"; + +import { tokens } from "./tokens"; + +// Same bot as the backend support bridge (PR #2526). A plain module constant +// rather than `NEXT_PUBLIC_*`: the URL is public and stable, and env vars are +// inlined at build time — swapping the value would need a rebuild for no +// benefit over just editing this line. +// +// Exported: this is the single source of truth for the bot URL. The «Помощь» +// item in TopNav.tsx's user menu links to the same bot and imports this +// constant rather than duplicating the string. +export const SUPPORT_BOT_URL = "https://t.me/MERAsupport_bot"; + +const { accent, accentDeep, onAccent, surface, font } = tokens; + +const styles = ` +.support-btn{background:linear-gradient(90deg,${accentDeep},${accent});transition:box-shadow .18s,transform .18s,background .18s;} +.support-btn:hover{background:${accentDeep};box-shadow:0 6px 20px rgba(13,111,214,.4);} +.support-btn:active{transform:translateY(1px);} +.support-btn:focus-visible{outline:none;box-shadow:0 0 0 3px rgba(46,139,255,.45);} +@media (max-width: 480px){ + .support-btn{right:14px !important;bottom:14px !important;padding:0 14px !important;} +} +`; + +export function SupportButton() { + // Portal-mount guard (SSR-safe): `document` only exists after mount + // (mirrors MapPicker.tsx:107-108 / BuildingListingsDrawer.tsx:29-30). + const [mounted, setMounted] = useState(false); + useEffect(() => setMounted(true), []); + + if (!mounted) return null; + + return createPortal( + <> + + + + Поддержка + + , + document.body, + ); +} diff --git a/tradein-mvp/frontend/src/components/trade-in/v2/TopNav.tsx b/tradein-mvp/frontend/src/components/trade-in/v2/TopNav.tsx index defe4403..a4cf0e04 100644 --- a/tradein-mvp/frontend/src/components/trade-in/v2/TopNav.tsx +++ b/tradein-mvp/frontend/src/components/trade-in/v2/TopNav.tsx @@ -12,6 +12,7 @@ import type { CSSProperties } from "react"; import { tokens } from "./tokens"; import { navLabels, version } from "./fixtures"; +import { SUPPORT_BOT_URL } from "./SupportButton"; // Real logged-in user identity, derived by the page from useMe() // ({username, role, brand}). Deliberately excludes `reports` — the «Мои @@ -56,8 +57,10 @@ const menuItemStyle: CSSProperties = { transition: "background .12s", }; -// Профиль / Настройки / Помощь have no pages yet — render them dimmed and +// Профиль / Настройки have no pages yet — render them dimmed and // non-interactive (no hover class, default cursor) so they read as disabled. +// «Помощь» used to be in this group too, but now links out to the Telegram +// support bot (see SUPPORT_BOT_URL below) — it renders as a live item. const menuItemDisabledStyle: CSSProperties = { ...menuItemStyle, cursor: "default", @@ -374,7 +377,7 @@ export default function TopNav({ {/* L5 — `title` gives the dimmed/non-interactive item a hover tooltip explaining WHY it's disabled, instead of a silent dead - row (mirrored on Настройки/Помощь below). */} + row (mirrored on Настройки below). */}
-
setUserOpen(false)} > Помощь -
+