Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
123 lines
5.0 KiB
TypeScript
123 lines
5.0 KiB
TypeScript
import '../../clean.css';
|
|
import { createT, isLocale, LOCALES, pickMessages, type Locale } from '@barev/i18n';
|
|
import { Logo } from '@barev/ui';
|
|
import type { Metadata } from 'next';
|
|
import { notFound } from 'next/navigation';
|
|
import { CleanForm } from '../../../components/clean';
|
|
import { FooterContacts, LegalLine } from '../../../components/seo';
|
|
import { BotLink, BotLinkProvider, LangMenu, LangSwitch } from '../../../components/client';
|
|
import { getCleaning } from '../../../lib/data';
|
|
|
|
export const revalidate = 300;
|
|
export const dynamicParams = false;
|
|
export function generateStaticParams() {
|
|
return LOCALES.map((locale) => ({ locale }));
|
|
}
|
|
|
|
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('landing.clean.meta.title'),
|
|
description: t('landing.clean.meta.description'),
|
|
alternates: {
|
|
canonical: `/${locale}/clean`,
|
|
languages: Object.fromEntries([...LOCALES.map((l) => [l, `/${l}/clean`]), ['x-default', '/ru/clean']]),
|
|
},
|
|
openGraph: { title: t('landing.clean.meta.title'), description: t('landing.clean.meta.description'), locale, type: 'website', siteName: 'Barev Club' },
|
|
};
|
|
}
|
|
|
|
const ZONES = ['kitchen', 'bathroom', 'rooms'] as const;
|
|
|
|
/** barevclub.com/clean — заказ уборки с сайта по модели qlean.ru */
|
|
export default async function CleanPage({ params }: { params: Promise<{ locale: string }> }) {
|
|
const { locale: raw } = await params;
|
|
if (!isLocale(raw)) notFound();
|
|
const locale: Locale = raw;
|
|
const t = createT(locale);
|
|
const config = await getCleaning();
|
|
const messages = pickMessages(locale, ['landing.clean.', 'params.cleaning.', 'extras.', 'districts.', 'errors.', 'common.']);
|
|
|
|
return (
|
|
<BotLinkProvider locale={locale} bot={config.botUsername}>
|
|
<div className="wrap cl-wrap">
|
|
<header className="header">
|
|
<a href={`/${locale}`} aria-label={t('landing.clean.home')} style={{ display: 'inline-flex' }}>
|
|
<Logo />
|
|
</a>
|
|
<nav className="nav" aria-label={t('landing.nav.label')}>
|
|
<a href={`/${locale}`}>{t('landing.clean.home')}</a>
|
|
<a href="#included">{t('landing.clean.includedTitle')}</a>
|
|
<a href="#how">{t('landing.clean.howTitle')}</a>
|
|
</nav>
|
|
<div className="langs">
|
|
<span className="langs__row">
|
|
<LangSwitch current={locale} path="/clean" />
|
|
</span>
|
|
<LangMenu current={locale} path="/clean" />
|
|
<BotLink className="bc-btn bc-btn--dark bc-btn--sm" style={{ marginLeft: 6 }}>
|
|
<span className="tg-long">{t('landing.openTelegram')}</span>
|
|
<span className="tg-short">Telegram</span>
|
|
</BotLink>
|
|
</div>
|
|
</header>
|
|
|
|
<CleanForm locale={locale} config={config} messages={messages} />
|
|
|
|
<section id="included" className="cl-included">
|
|
<h2 className="cl-section-title">{t('landing.clean.includedTitle')}</h2>
|
|
<div className="cl-zones">
|
|
{ZONES.map((z) => (
|
|
<div key={z} className="cl-zone">
|
|
<h3>{t(`landing.clean.zone.${z}`)}</h3>
|
|
<ul>
|
|
{t(`landing.clean.inc.${z}`)
|
|
.split('\n')
|
|
.map((line) => (
|
|
<li key={line}>{line}</li>
|
|
))}
|
|
</ul>
|
|
</div>
|
|
))}
|
|
</div>
|
|
<div className="cl-variants">
|
|
<p>{t('landing.clean.general')}</p>
|
|
<p>{t('landing.clean.renovation')}</p>
|
|
</div>
|
|
</section>
|
|
|
|
<section id="how" className="how cl-how">
|
|
<h2 className="bc-h2" style={{ fontSize: 34 }}>
|
|
{t('landing.clean.howTitle')}
|
|
</h2>
|
|
<ol className="cl-how__grid">
|
|
{([1, 2, 3] as const).map((n) => (
|
|
<li key={n} className="how__step">
|
|
<div className="how__num" style={{ background: n === 1 ? 'var(--bc-home)' : n === 2 ? 'var(--bc-blue)' : 'var(--bc-pink)', color: 'var(--bc-ink)' }}>
|
|
{n}
|
|
</div>
|
|
<div style={{ fontWeight: 600 }}>{t(`landing.clean.how${n}`)}</div>
|
|
<div style={{ fontSize: 13, color: '#CFC8C2' }}>{t(`landing.clean.how${n}Sub`)}</div>
|
|
</li>
|
|
))}
|
|
</ol>
|
|
</section>
|
|
|
|
<footer className="footer">
|
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
|
|
<span style={{ fontFamily: 'var(--bc-font-head)', fontWeight: 700, color: 'var(--bc-plum)' }}>{t('landing.footer.brand')}</span>
|
|
<LegalLine locale={locale} />
|
|
</div>
|
|
<div className="footer__links">
|
|
<a href={`/${locale}`}>{t('landing.clean.home')}</a>
|
|
<FooterContacts />
|
|
<a href={`/${locale}/privacy`}>{t('landing.footer.privacy')}</a>
|
|
</div>
|
|
</footer>
|
|
</div>
|
|
</BotLinkProvider>
|
|
);
|
|
}
|