Apps, web, i18n, UI, partners, payouts, backoffice, bariki, prod infra

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-25 22:55:29 +04:00
1 parent 45d7512d2b
commit 3489e0b816
319 files changed
+45483 -121

No files matched your search

+289
View File
@@ -0,0 +1,289 @@
import { createT, formatAmd, isLocale, LOCALES, type Locale } from '@barev/i18n';
import { CATEGORY_ICON, Flower, Icon, Logo, type CategoryCode } from '@barev/ui';
import { notFound } from 'next/navigation';
import { BotLink, BotLinkProvider, LangSwitch } from '../../../components/client';
import { getCatalog } from '../../../lib/data';
export const revalidate = 300;
export const dynamicParams = false;
export function generateStaticParams() {
return LOCALES.map((locale) => ({ locale }));
}
/** Прежняя главная (до октября 2026) — сохранена для сравнения, в поиск не попадает */
export const metadata = { robots: { index: false, follow: true } };
const TONE: Record<CategoryCode, { bg: string; ink: string }> = {
HOME: { bg: 'var(--bc-home)', ink: 'var(--bc-home-ink)' },
DOCS: { bg: 'var(--bc-docs)', ink: 'var(--bc-docs-ink)' },
LEARNING: { bg: 'var(--bc-learn)', ink: 'var(--bc-learn-ink)' },
PERKS: { bg: 'var(--bc-perks)', ink: 'var(--bc-perks-ink)' },
};
/** Прежняя главная: лендинг v2 · плотный (LND-1) */
export default async function Landing({ params }: { params: Promise<{ locale: string }> }) {
const { locale: raw } = await params;
if (!isLocale(raw)) notFound();
const locale: Locale = raw;
const t = createT(locale);
const amd = (n: number) => formatAmd(locale, n);
const catalog = await getCatalog();
const cat = (code: CategoryCode) => catalog.categories.find((c) => c.code === code);
const cleaningFrom = cat('HOME')?.services.find((s) => s.code === 'cleaning')?.fromPrice ?? 0;
const maxDiscount = Math.max(0, ...catalog.categories.map((c) => c.barevPlusDiscountPct));
const discounts = catalog.categories.filter((c) => c.barevPlusDiscountPct > 0);
const preview = [
{ tone: 'var(--bc-blue)', ink: 'var(--bc-blue-ink)', title: t('landing.preview.cleaningRegular'), meta: t('landing.preview.cleaningRegularMeta'), badge: t('status.CLEANING.EN_ROUTE') },
{ tone: 'var(--bc-home)', ink: 'var(--bc-home-ink)', title: t('landing.preview.cleaning'), meta: t('landing.preview.cleaningMeta'), badge: t('status.CLEANING.ASSIGNED') },
{ tone: 'var(--bc-docs)', ink: 'var(--bc-docs-ink)', title: t('services.social_card.name'), meta: t('status.DOCUMENTS.DOCS_REVIEW'), badge: t('landing.preview.inWork') },
{ tone: 'var(--bc-learn)', ink: 'var(--bc-learn-ink)', title: t('landing.preview.lesson'), meta: t('landing.preview.lessonMeta'), badge: t('landing.preview.today') },
];
const faq = ['install', 'languages', 'plus', 'gift'] as const;
const check = (color: string) => <Icon name="check" size={18} strokeWidth={2.6} style={{ color }} />;
return (
<BotLinkProvider locale={locale} bot={catalog.botUsername}>
<div className="wrap">
<header className="header">
<Logo />
<nav className="nav" aria-label={t('landing.nav.label')}>
<a href="#services">{t('landing.nav.services')}</a>
<a href="#how">{t('landing.nav.how')}</a>
<a href="#plans">{t('landing.nav.plans')}</a>
<a href="#faq">{t('landing.nav.faq')}</a>
</nav>
<div className="langs">
<LangSwitch current={locale} path="/land0" />
<BotLink className="bc-btn bc-btn--dark bc-btn--sm" style={{ marginLeft: 6 }}>
{t('landing.openTelegram')}
</BotLink>
</div>
</header>
<section className="row">
<div className="hero">
<div className="hero__tag">{t('landing.hero.tag')}</div>
<h1 className="bc-h1">{t('landing.hero.title')}</h1>
<p className="hero__sub">{t('landing.hero.sub')}</p>
<div className="row" style={{ gap: 10 }}>
<BotLink className="bc-btn bc-btn--dark" style={{ height: 56, padding: '0 28px' }}>
{t('landing.hero.cta')}
<Icon name="arrow" size={18} strokeWidth={2.2} />
</BotLink>
<a href="#services" className="bc-btn bc-btn--light" style={{ height: 56 }}>
{t('landing.hero.services')}
</a>
</div>
{/* сейчас заказать можно уборку — короткий путь на форму /clean */}
<a href={`/${locale}/clean`} className="clean-promo">
<span className="clean-promo__text">
<b>{t('landing.hero.cleanTitle', { price: amd(cleaningFrom) })}</b>
<span>{t('landing.hero.cleanText')}</span>
</span>
<span className="bc-btn bc-btn--accent clean-promo__cta">{t('landing.hero.cleanCta')}</span>
</a>
</div>
<div className="side">
<div className="bc-card" style={{ borderRadius: 36, display: 'flex', flexDirection: 'column', gap: 8 }}>
<div className="bc-between" style={{ padding: '0 4px 6px' }}>
<div className="bc-h3" style={{ fontSize: 20 }}>
{t('landing.preview.title')}
</div>
<div className="bc-small bc-muted">{t('landing.preview.inApp')}</div>
</div>
{preview.map((p) => (
<div key={p.title} className="preview-row" style={{ background: p.tone, color: p.ink }}>
<div style={{ flex: 1 }}>
<div style={{ fontWeight: 600, fontSize: 14 }}>{p.title}</div>
<div style={{ fontSize: 12 }}>{p.meta}</div>
</div>
<span className="bc-badge">{p.badge}</span>
</div>
))}
</div>
<div className="row">
<div className="bc-card bc-tone-perks" style={{ flex: 1, borderRadius: 30, padding: 18, display: 'flex', alignItems: 'center', gap: 12 }}>
<Flower size={72} rotate={-10}>
5
</Flower>
<div className="langs-sticker">
<span>{t('landing.languages')}</span>
<span className="langs-sticker__list">RU EN 中文 HY ES</span>
</div>
</div>
<div className="bc-card bc-tone-learn" style={{ flex: 1, borderRadius: 30, padding: 18, display: 'flex', flexDirection: 'column', justifyContent: 'center', gap: 4 }}>
<div style={{ fontFamily: 'var(--bc-font-head)', fontWeight: 700, fontSize: 18 }}>{t('landing.noInstall')}</div>
<div className="bc-small">{t('landing.noInstallSub')}</div>
</div>
</div>
</div>
</section>
<section id="services" className="row">
{(['HOME', 'DOCS', 'LEARNING'] as const).map((code) => (
<div key={code} className="dir" style={{ background: TONE[code].bg, color: TONE[code].ink }}>
<div className="bc-between">
<h2 className="dir__title" style={{ margin: 0 }}>
{t(`categories.${code}.name`)}
</h2>
<Icon name={CATEGORY_ICON[code]} size={28} />
</div>
<div className="bc-stack" style={{ gap: 6 }}>
{(cat(code)?.services ?? []).map((s) =>
s.code === 'cleaning' && s.active ? (
<a key={s.code} href={`/${locale}/clean`} className="bc-row-pill">
<span style={{ fontWeight: 600 }}>{t(`services.${s.code}.name`)}</span>
<span>{t('common.from', { price: amd(s.fromPrice) })}</span>
</a>
) : (
<div key={s.code} className="bc-row-pill" data-soon={!s.active}>
<span style={{ fontWeight: 600 }}>{t(`services.${s.code}.name`)}</span>
<span>{s.active ? t('common.from', { price: amd(s.fromPrice) }) : t('common.soon')}</span>
</div>
),
)}
</div>
<div style={{ fontSize: 14 }}>{t(`landing.dir.${code}`)}</div>
{code === 'HOME' ? (
<a href={`/${locale}/clean`} style={{ marginTop: 'auto', fontWeight: 700, color: TONE[code].ink }}>
{t('landing.hero.cleanCta')} →
</a>
) : (
<BotLink style={{ marginTop: 'auto', fontWeight: 700, color: TONE[code].ink }}>
{(cat(code)?.services ?? []).some((s) => s.active) ? t(`landing.dir.${code}_cta`) : t('landing.dir.soonCta')} →
</BotLink>
)}
</div>
))}
<div className="dir" style={{ background: TONE.PERKS.bg, color: TONE.PERKS.ink }}>
<div className="bc-between">
<h2 className="dir__title" style={{ margin: 0 }}>
{t('categories.PERKS.name')}
</h2>
<Icon name="gift" size={28} />
</div>
<div className="perks-big">{t('landing.perks.upTo', { pct: maxDiscount })}</div>
<div className="bc-chips">
{(['cafes', 'restaurants', 'gifts'] as const).map((k) => (
<span key={k} className="bc-badge" style={{ padding: '8px 14px', fontSize: 14, fontWeight: 500 }}>
{t(`landing.perks.${k}`)}
</span>
))}
</div>
<div style={{ fontSize: 14 }}>{t('landing.perks.text')}</div>
<BotLink style={{ marginTop: 'auto', fontWeight: 700, color: TONE.PERKS.ink }}>{t('landing.perks.cta')} →</BotLink>
</div>
</section>
<section id="how" className="row">
<div className="how">
<h2 className="bc-h2" style={{ fontSize: 34 }}>
<span style={{ fontWeight: 700 }}>{t('landing.how.titleBold')}</span> <span className="bc-italic">{t('landing.how.titleItalic')}</span>
</h2>
<div className="how__grid">
{[
{ bg: 'var(--bc-pink)', ink: 'var(--bc-plum)' },
{ bg: 'var(--bc-perks)', ink: 'var(--bc-plum)' },
{ bg: 'var(--bc-home)', ink: 'var(--bc-home-ink)' },
{ bg: 'var(--bc-blue)', ink: 'var(--bc-blue-ink)' },
].map((c, i) => (
<div key={i} className="how__step">
<div className="how__num" style={{ background: c.bg, color: c.ink }}>
{i + 1}
</div>
<div style={{ fontWeight: 600 }}>{t(`landing.how.step${i + 1}`)}</div>
<div style={{ fontSize: 13, color: '#CFC8C2' }}>{t(`landing.how.step${i + 1}_sub`)}</div>
</div>
))}
</div>
</div>
<div className="agency">
<div style={{ flex: 1, minWidth: 0, display: 'flex', flexDirection: 'column', gap: 8 }}>
<div style={{ fontFamily: 'var(--bc-font-head)', fontWeight: 800, fontSize: 23, lineHeight: 1.1, textWrap: 'balance', hyphens: 'manual' }}>{t('landing.agency.title')}</div>
<div style={{ fontSize: 15 }}>{t('landing.agency.text')}</div>
</div>
<div className="sticker">
<div style={{ fontFamily: 'var(--bc-font-head)', fontStyle: 'italic', fontSize: 20, fontWeight: 500 }}>{t('landing.agency.gift')}</div>
<div style={{ fontSize: 12 }}>{t('landing.agency.giftSub')}</div>
</div>
</div>
</section>
<section id="plans" className="row" style={{ alignItems: 'stretch' }}>
<div className="plan" style={{ background: '#fff' }}>
<div className="bc-between" style={{ alignItems: 'baseline' }}>
<div className="bc-h2" style={{ fontSize: 28 }}>
Barev
</div>
<div className="bc-num" style={{ fontSize: 32 }}>
{amd(0)}
</div>
</div>
<div className="plan__list">
<div>
{check('var(--bc-ok)')}
{t('plans.free.perks')}
</div>
<div>
{check('var(--bc-ok)')}
{t('plans.free.prices')}
</div>
<div>
{check('var(--bc-ok)')}
{t('plans.free.status')}
</div>
</div>
<BotLink className="bc-btn bc-btn--outline bc-btn--block" style={{ marginTop: 'auto' }}>
{t('landing.hero.cta')}
</BotLink>
</div>
<div className="plan bc-tone-learn">
<div className="bc-between" style={{ alignItems: 'baseline' }}>
<div className="bc-h2" style={{ fontSize: 28 }}>
Barev+
</div>
<div className="bc-num" style={{ fontSize: 24 }}>
{t('plans.plus.price', { price: amd(catalog.barevPlus.monthlyPrice) })}
</div>
</div>
<div className="plan__list">
<div>
{check('var(--bc-accent)')}
{t('plans.plus.all')}
</div>
{discounts.map((c) => (
<div key={c.code}>
{check('var(--bc-accent)')}
{t('plans.plus.discount', { pct: c.barevPlusDiscountPct, category: t(`categories.${c.code}.name`) })}
</div>
))}
</div>
<BotLink className="bc-btn bc-btn--dark bc-btn--block" style={{ marginTop: 'auto' }}>
{t('landing.plans.tryPlus')}
</BotLink>
</div>
<div id="faq" className="faq">
<h2 className="bc-h2" style={{ fontSize: 28, marginBottom: 4 }}>
{t('landing.nav.faq')}
</h2>
{faq.map((k, i) => (
<details key={k} open={i === 0}>
<summary>{t(`landing.faq.${k}.q`)}</summary>
<p>{t(`landing.faq.${k}.a`, { pct: maxDiscount, days: catalog.barevPlus.giftDays })}</p>
</details>
))}
</div>
</section>
<footer className="footer">
<div style={{ fontFamily: 'var(--bc-font-head)', fontWeight: 700, color: 'var(--bc-plum)' }}>{t('landing.footer.brand')}</div>
<div className="footer__links">
<BotLink>{t('landing.footer.partner')}</BotLink>
{process.env.CONTACT_EMAIL && <a href={`mailto:${process.env.CONTACT_EMAIL}`}>{process.env.CONTACT_EMAIL}</a>}
<a href={`/${locale}/privacy`}>{t('landing.footer.privacy')}</a>
</div>
</footer>
</div>
</BotLinkProvider>
);
}