'use client'; import { encodeStartPayload, normalizeAgencyCode, pickUtm, type Locale } from '@barev/core'; import { LOCALE_LABELS, LOCALES } from '@barev/i18n'; import { createContext, useContext, useEffect, useRef, useState, type ReactNode } from 'react'; const API = process.env.NEXT_PUBLIC_API_URL ?? 'http://localhost:4000'; const AGENCY_KEY = 'barev.agency'; interface Ctx { href: string; agency: string | null; query: string; } const LinkCtx = createContext({ href: '', agency: null, query: '' }); /** * LND-3: ссылка в бота с payload (язык, агентство, UTM). Без JS работает ссылка с языком (SSR); * в браузере дописываем код агентства из ?a= и, если есть UTM, получаем короткий токен у API. * Код агентства запоминается: клиент мог перейти на лендинг по QR, а нажать CTA позже. */ export function BotLinkProvider({ locale, bot, children }: { locale: Locale; bot: string; children: ReactNode }) { const base = `https://t.me/${bot}?start=${encodeStartPayload({ locale })}`; const [state, setState] = useState({ href: base, agency: null, query: '' }); useEffect(() => { const params = new URLSearchParams(window.location.search); let agency = params.get('a') ? normalizeAgencyCode(params.get('a')!) : null; try { if (agency) localStorage.setItem(AGENCY_KEY, agency); else agency = localStorage.getItem(AGENCY_KEY); } catch { /* приватный режим */ } const utm = pickUtm(params); const query = window.location.search; const simple = `https://t.me/${bot}?start=${encodeStartPayload({ locale, agency: agency ?? undefined })}`; setState({ href: simple, agency, query }); if (Object.keys(utm).length === 0) return; fetch(`${API}/trpc/public.startLink`, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ json: { locale, agency: agency ?? undefined, utm } }), }) .then((r) => (r.ok ? r.json() : null)) .then((b: { result?: { data?: { json?: { url: string } } } } | null) => { const url = b?.result?.data?.json?.url; if (url) setState((s) => ({ ...s, href: url })); }) .catch(() => {}); }, [bot, locale]); return {children}; } /** Код агентства из ?a= или сохранённый ранее — для подарка на /land3 */ export function useAgency(): string | null { return useContext(LinkCtx).agency; } export function BotLink({ className, style, children }: { className?: string; style?: React.CSSProperties; children: ReactNode }) { const { href } = useContext(LinkCtx); return ( {children} ); } /** LND-2: переключатель языков; выбор запоминается в cookie и уходит в payload. path — страница без языка, например /clean */ export function LangSwitch({ current, path = '' }: { current: Locale; path?: string }) { const { query } = useContext(LinkCtx); return ( <> {LOCALES.map((l) => ( { document.cookie = `barev_lang=${l}; path=/; max-age=31536000; samesite=lax`; }} > {LOCALE_LABELS[l]} ))} ); } /** Языки на мобилке: вместо ряда из пяти кнопок — текущий язык, по нажатию раскрывается список */ export function LangMenu({ current, path = '' }: { current: Locale; path?: string }) { const ref = useRef(null); useEffect(() => { const close = (e: PointerEvent) => { if (ref.current?.open && !ref.current.contains(e.target as Node)) ref.current.open = false; }; document.addEventListener('pointerdown', close); return () => document.removeEventListener('pointerdown', close); }, []); return (
{LOCALE_LABELS[current]}
); }