Snapshot of uncommitted work: landing, bots, prod infra, partner cabinet, backoffice stage 1

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
zabudko-ruandClaude Opus 5.5 committed 2026-10-21 01:23:32 +04:00
1 parent 45d7512d2b
commit d452824c62
314 files changed
+44394 -121

No files matched your search

@@ -0,0 +1,80 @@
import { createT, isLocale, LOCALES, type Locale } from '@barev/i18n';
import { Logo } from '@barev/ui';
import type { Metadata } from 'next';
import { notFound } from 'next/navigation';
import { RichText } from '../../../components/rich-text';
import { FooterContacts, LegalLine } from '../../../components/seo';
import { CONTACTS, languageAlternates } from '../../../lib/seo';
export const dynamicParams = false;
export function generateStaticParams() {
return LOCALES.map((locale) => ({ locale }));
}
/** Разделы политики по порядку; тексты — privacy.<id>.title / privacy.<id>.body */
const SECTIONS = ['operator', 'data', 'purposes', 'basis', 'recipients', 'location', 'retention', 'security', 'rights', 'marketing', 'cookies', 'age', 'changes'] as const;
export async function generateMetadata({ params }: { params: Promise<{ locale: string }> }): Promise<Metadata> {
const { locale } = await params;
if (!isLocale(locale)) return {};
const t = createT(locale);
return {
title: `${t('privacy.title')} — Barev Club`,
description: t('privacy.intro'),
alternates: { canonical: `/${locale}/privacy`, languages: languageAlternates('/privacy') },
// своё превью ссылки, иначе подставляется старый слоган из общего layout
openGraph: { title: `${t('privacy.title')} — Barev Club`, description: t('privacy.intro'), locale, type: 'article', siteName: 'Barev Club' },
twitter: { card: 'summary_large_image', title: `${t('privacy.title')} — Barev Club`, description: t('privacy.intro') },
};
}
/** ONB-3: политика конфиденциальности. Оператор — ИП владельца; основной текст на русском, переводы needs_review */
export default async function Privacy({ params }: { params: Promise<{ locale: string }> }) {
const { locale: raw } = await params;
if (!isLocale(raw)) notFound();
const locale: Locale = raw;
const t = createT(locale);
const vars = { email: CONTACTS.email, phone: CONTACTS.phoneDisplay, tin: CONTACTS.tin };
return (
<main className="prose policy">
<div className="policy__top">
<a href={`/${locale}`} style={{ textDecoration: 'none' }}>
<Logo />
</a>
<nav className="policy__langs" aria-label="Language">
{LOCALES.map((l) => (
<a key={l} href={`/${l}/privacy`} hrefLang={l} aria-current={l === locale ? 'page' : undefined}>
{l.toUpperCase()}
</a>
))}
</nav>
</div>
<h1 className="bc-h2" style={{ marginTop: 24 }}>
{t('privacy.title')}
</h1>
<p className="policy__updated">{t('privacy.updated')}</p>
<p>{t('privacy.intro')}</p>
<ol className="policy__toc">
{SECTIONS.map((s) => (
<li key={s}>
<a href={`#${s}`}>{t(`privacy.${s}.title`)}</a>
</li>
))}
</ol>
{SECTIONS.map((s, i) => (
<section key={s} id={s} className="policy__section">
<h2>
{i + 1}. {t(`privacy.${s}.title`)}
</h2>
<RichText text={t(`privacy.${s}.body`, vars)} />
</section>
))}
<footer className="policy__footer">
<LegalLine locale={locale} />
<span className="footer__links">
<FooterContacts />
</span>
</footer>
</main>
);
}