Bariki cashback, order edit, and current work in progress
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
dda917fc71
commit
45477fb018
94 files changed
+10955
-650
No files matched your search
@@ -0,0 +1,107 @@
|
||||
import { createT, isLocale, LOCALES, type Locale } from '@barev/i18n';
|
||||
import { Logo } from '@barev/ui';
|
||||
import type { Metadata } from 'next';
|
||||
import type { ReactNode } from 'react';
|
||||
import { CONTACTS, languageAlternates } from '../lib/seo';
|
||||
import { RichText } from './rich-text';
|
||||
import { FooterContacts, LegalLine } from './seo';
|
||||
|
||||
/** Страницы с ценами и правилами: их требуют платёжные провайдеры при проверке сайта */
|
||||
export const LEGAL_PAGES = ['pricing', 'terms', 'refund', 'privacy'] as const;
|
||||
export type LegalPageId = (typeof LEGAL_PAGES)[number];
|
||||
|
||||
/** Ссылки на цены и правила — в подвал любой страницы */
|
||||
export function LegalLinks({ locale }: { locale: Locale }) {
|
||||
const t = createT(locale);
|
||||
return (
|
||||
<>
|
||||
{LEGAL_PAGES.map((p) => (
|
||||
<a key={p} href={`/${locale}/${p}`}>
|
||||
{t(`landing.footer.${p}`)}
|
||||
</a>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
/** Мета страницы: заголовок и описание из <ns>.title / <ns>.intro */
|
||||
export async function legalMetadata(params: Promise<{ locale: string }>, ns: LegalPageId): Promise<Metadata> {
|
||||
const { locale } = await params;
|
||||
if (!isLocale(locale)) return {};
|
||||
const t = createT(locale);
|
||||
const title = `${t(`${ns}.title`)} — Barev Club`;
|
||||
const description = t(`${ns}.intro`);
|
||||
return {
|
||||
title,
|
||||
description,
|
||||
alternates: { canonical: `/${locale}/${ns}`, languages: languageAlternates(`/${ns}`) },
|
||||
// своё превью ссылки, иначе подставляется слоган из общего layout
|
||||
openGraph: { title, description, locale, type: 'article', siteName: 'Barev Club' },
|
||||
twitter: { card: 'summary_large_image', title, description },
|
||||
};
|
||||
}
|
||||
|
||||
/** Каркас: логотип, языки, заголовок, подвал с реквизитами и ссылками на остальные правила */
|
||||
export function LegalShell({ locale, page, children }: { locale: Locale; page: LegalPageId; children: ReactNode }) {
|
||||
const t = createT(locale);
|
||||
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}/${page}`} hrefLang={l} aria-current={l === locale ? 'page' : undefined}>
|
||||
{l.toUpperCase()}
|
||||
</a>
|
||||
))}
|
||||
</nav>
|
||||
</div>
|
||||
<h1 className="bc-h2" style={{ marginTop: 24 }}>
|
||||
{t(`${page}.title`)}
|
||||
</h1>
|
||||
<p className="policy__updated">{t(`${page}.updated`)}</p>
|
||||
<p>{t(`${page}.intro`)}</p>
|
||||
{children}
|
||||
<footer className="policy__footer">
|
||||
<LegalLine locale={locale} />
|
||||
<span className="footer__links">
|
||||
<FooterContacts />
|
||||
</span>
|
||||
<nav className="policy__legal" aria-label={t('landing.footer.rules')}>
|
||||
{LEGAL_PAGES.filter((p) => p !== page).map((p) => (
|
||||
<a key={p} href={`/${locale}/${p}`}>
|
||||
{t(`landing.footer.${p}`)}
|
||||
</a>
|
||||
))}
|
||||
</nav>
|
||||
</footer>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
/** Текст правил: оглавление и нумерованные разделы <ns>.<id>.title / <ns>.<id>.body */
|
||||
export function LegalDocument({ locale, ns, sections }: { locale: Locale; ns: Exclude<LegalPageId, 'pricing'>; sections: readonly string[] }) {
|
||||
const t = createT(locale);
|
||||
const vars = { email: CONTACTS.email, phone: CONTACTS.phoneDisplay, tin: CONTACTS.tin };
|
||||
return (
|
||||
<LegalShell locale={locale} page={ns}>
|
||||
<ol className="policy__toc">
|
||||
{sections.map((s) => (
|
||||
<li key={s}>
|
||||
<a href={`#${s}`}>{t(`${ns}.${s}.title`)}</a>
|
||||
</li>
|
||||
))}
|
||||
</ol>
|
||||
{sections.map((s, i) => (
|
||||
<section key={s} id={s} className="policy__section">
|
||||
<h2>
|
||||
{i + 1}. {t(`${ns}.${s}.title`)}
|
||||
</h2>
|
||||
<RichText text={t(`${ns}.${s}.body`, vars)} />
|
||||
</section>
|
||||
))}
|
||||
</LegalShell>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user