feat(tradein/ui): вход в поддержку с сайта — плавающая кнопка + живая «Помощь» (#2527)
All checks were successful
Deploy Trade-In / changes (push) Successful in 12s
Deploy Trade-In / test (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / build-backend (push) Has been skipped
Deploy Trade-In / build-frontend (push) Successful in 2m13s
Deploy Trade-In / deploy (push) Successful in 56s
All checks were successful
Deploy Trade-In / changes (push) Successful in 12s
Deploy Trade-In / test (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / build-backend (push) Has been skipped
Deploy Trade-In / build-frontend (push) Successful in 2m13s
Deploy Trade-In / deploy (push) Successful in 56s
This commit is contained in:
commit
d56103219a
3 changed files with 141 additions and 7 deletions
|
|
@ -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. */}
|
||||
<SupportButton />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
<>
|
||||
<style>{styles}</style>
|
||||
<a
|
||||
href={SUPPORT_BOT_URL}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="support-btn"
|
||||
aria-label="Написать в поддержку МЕРА в Telegram"
|
||||
title="Поддержка МЕРА в Telegram"
|
||||
style={{
|
||||
position: "fixed",
|
||||
right: 20,
|
||||
bottom: 20,
|
||||
// z-index 25 — deliberately BELOW every v2 HUD overlay: SectionOverlay
|
||||
// (29/30), LocationDrawer (40/41), TopNav (50), UserMenu (200), and
|
||||
// the legacy MapPicker dialog (1000). When a modal/drawer is open the
|
||||
// button should disappear under it, not float on top and steal
|
||||
// clicks/tab order. It never collides with TopNav geographically
|
||||
// (top of screen vs. bottom-right corner here), so this only matters
|
||||
// for the bottom-sheet-style overlays.
|
||||
zIndex: 25,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: 8,
|
||||
height: 44,
|
||||
minWidth: 44,
|
||||
boxSizing: "border-box",
|
||||
padding: "0 16px",
|
||||
borderRadius: 999,
|
||||
border: `1px solid ${surface.w40}`,
|
||||
boxShadow: "0 4px 14px rgba(13,111,214,.3)",
|
||||
color: onAccent,
|
||||
fontFamily: font.sans,
|
||||
fontSize: 12,
|
||||
fontWeight: 600,
|
||||
letterSpacing: 0.4,
|
||||
textDecoration: "none",
|
||||
whiteSpace: "nowrap",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
width="18"
|
||||
height="18"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
aria-hidden="true"
|
||||
focusable="false"
|
||||
>
|
||||
<path
|
||||
d="M21.05 3.16 2.98 10.42c-1.24.51-1.23 1.22-.23 1.53l4.63 1.45 1.79 5.5c.22.6.37.84.75.84.35 0 .5-.16.7-.35l1.7-1.65 3.63 2.68c.67.37 1.15.18 1.32-.62l2.39-11.27c.26-1.03-.39-1.5-1.61-.87z"
|
||||
fill={onAccent}
|
||||
/>
|
||||
</svg>
|
||||
<span>Поддержка</span>
|
||||
</a>
|
||||
</>,
|
||||
document.body,
|
||||
);
|
||||
}
|
||||
|
|
@ -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). */}
|
||||
<div
|
||||
style={menuItemDisabledStyle}
|
||||
aria-disabled="true"
|
||||
|
|
@ -443,10 +446,20 @@ export default function TopNav({
|
|||
Настройки
|
||||
</div>
|
||||
|
||||
<div
|
||||
style={menuItemDisabledStyle}
|
||||
aria-disabled="true"
|
||||
title="Раздел «Помощь» скоро появится"
|
||||
{/* Live item (issue: two support entry points — the other is
|
||||
SupportButton.tsx's floating button). Same bot, imported
|
||||
constant — single source of truth for the URL. Styled like
|
||||
the other live row above ("Мои отчёты"), not the dimmed
|
||||
Профиль/Настройки rows. */}
|
||||
<a
|
||||
href={SUPPORT_BOT_URL}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="tnav-menuitem"
|
||||
style={{ ...menuItemStyle, textDecoration: "none" }}
|
||||
aria-label="Написать в поддержку МЕРА в Telegram"
|
||||
title="Написать в поддержку МЕРА в Telegram"
|
||||
onClick={() => setUserOpen(false)}
|
||||
>
|
||||
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" aria-hidden="true">
|
||||
<circle
|
||||
|
|
@ -463,7 +476,7 @@ export default function TopNav({
|
|||
/>
|
||||
</svg>
|
||||
Помощь
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<div
|
||||
style={{
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue