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:
1 parent
45d7512d2b
commit
3489e0b816
319 files changed
+45483
-121
No files matched your search
@@ -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>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user