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

+136
View File
@@ -0,0 +1,136 @@
import '../../variants.css';
import { createT, formatAmd, isLocale, LOCALES, type Locale } from '@barev/i18n';
import { notFound } from 'next/navigation';
import { BotLink, BotLinkProvider } from '../../../components/client';
import { VariantHeader, variantMetadata } from '../../../components/variant';
import { getCatalog } from '../../../lib/data';
export const revalidate = 300;
export const dynamicParams = false;
export function generateStaticParams() {
return LOCALES.map((locale) => ({ locale }));
}
export function generateMetadata({ params }: { params: Promise<{ locale: string }> }) {
return variantMetadata(params, '/land4', 'landing.v4.title');
}
/** Текст табло: каждая буква — отдельная перекидная пластинка, при загрузке они один раз «перещёлкиваются» по очереди */
function Flaps({ text, start = 0 }: { text: string; start?: number }) {
return (
<span className="v4-flaps" aria-label={text}>
{Array.from(text).map((ch, i) =>
ch === ' ' ? (
<span key={i} className="v4-gap" aria-hidden="true" />
) : (
<span key={i} className="v4-flap" aria-hidden="true" style={{ ['--i' as string]: start + i }}>
{ch}
</span>
),
)}
</span>
);
}
/** Версия 4 «Табло прилёта»: для тех, кто только прилетел. Услуги клуба — как рейсы на табло, CTA — посадочный талон */
export default async function Land4({ 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 rows = catalog.categories
.filter((c) => c.code !== 'PERKS')
.flatMap((c) => c.services)
.sort((a, b) => Number(b.active) - Number(a.active));
return (
<BotLinkProvider locale={locale} bot={catalog.botUsername}>
<div className="v4">
<div className="v-wrap">
<VariantHeader locale={locale} path="/land4" />
<section className="v4-hero">
<h1 className="v4-title">{t('landing.v4.title')}</h1>
<p className="v4-sub">{t('landing.v4.sub')}</p>
</section>
<section className="v4-board" aria-label={`${t('landing.v4.board')}: ${t('landing.v4.city')}`}>
<div className="v4-board__head">
<span className="v4-board__name">
{t('landing.v4.board')}
</span>
<span className="v4-board__city">{t('landing.v4.city')} EVN</span>
</div>
<table className="v4-table">
<thead>
<tr>
<th>{t('landing.v4.colFlight')}</th>
<th>{t('landing.v4.colService')}</th>
<th>{t('landing.v4.colPrice')}</th>
<th>{t('landing.v4.colStatus')}</th>
</tr>
</thead>
<tbody>
{rows.map((s, i) => (
<tr key={s.code} data-live={s.active}>
<td className="v4-code">BC {String(i + 1).padStart(3, '0')}</td>
<td className="v4-name">
{s.code === 'cleaning' && s.active ? (
<a href={`/${locale}/clean`}>
<Flaps text={t(`services.${s.code}.name`)} start={i * 3} />
</a>
) : (
<Flaps text={t(`services.${s.code}.name`)} start={i * 3} />
)}
</td>
<td className="v4-price">{s.active ? t('common.from', { price: amd(s.fromPrice) }) : '—'}</td>
<td>
<span className="v4-status">{s.active ? t('landing.v4.live') : t('landing.v4.soon')}</span>
</td>
</tr>
))}
</tbody>
</table>
</section>
{/* посадочный талон: корешок отрывается по пунктиру, кнопка — посадка в бота */}
<section className="v4-pass">
<div className="v4-pass__main">
<div className="v4-pass__title">{t('landing.v4.passTitle')}</div>
<dl className="v4-pass__grid">
<div>
<dt>{t('landing.v4.passenger')}</dt>
<dd>{t('landing.v4.passengerValue')}</dd>
</div>
<div>
<dt>{t('landing.v4.from')}</dt>
<dd>{t('landing.v4.fromValue')}</dd>
</div>
<div>
<dt>{t('landing.v4.to')}</dt>
<dd>{t('landing.v4.toValue')}</dd>
</div>
<div>
<dt>{t('landing.v4.gate')}</dt>
<dd>{t('landing.v4.gateValue')}</dd>
</div>
</dl>
</div>
<div className="v4-pass__stub">
<BotLink className="bc-btn v4-pass__cta">{t('landing.v4.cta')}</BotLink>
<a href={`/${locale}/clean`} className="v4-pass__link">
{t('landing.v4.clean')}
</a>
</div>
</section>
<footer className="v-footer">
<span>{t('landing.footer.brand')}</span>
<a href={`/${locale}/privacy`}>{t('landing.footer.privacy')}</a>
</footer>
</div>
</div>
</BotLinkProvider>
);
}