Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
104 lines
4.1 KiB
TypeScript
104 lines
4.1 KiB
TypeScript
import { useQuery } from '@tanstack/react-query';
|
|
import { CATEGORY_ICON, Card, Chip, Icon, Spinner, type CategoryCode } from '@barev/ui';
|
|
import { navigate, useI18n, useTRPC } from '@barev/web';
|
|
import { LEVEL_NAME, LevelBadge, levelDiscount, OrderRow, toneClass, type Level } from '../lib.tsx';
|
|
|
|
/** Главная для тех, кто уже живёт в Армении: заказы, быстрые кнопки, направления со скидкой по уровню */
|
|
export function Home() {
|
|
const { t, dt } = useI18n();
|
|
const trpc = useTRPC();
|
|
const me = useQuery(trpc.client.me.queryOptions());
|
|
const orders = useQuery({ ...trpc.client.orders.list.queryOptions(), refetchInterval: 20_000 });
|
|
const catalog = useQuery(trpc.client.catalog.queryOptions());
|
|
const level = (me.data?.membership.level ?? 'BAREV') as Level;
|
|
const plus = level !== 'BAREV';
|
|
const lastDone = orders.data?.history.find((o) => o.status !== 'CANCELLED');
|
|
|
|
return (
|
|
<main className="app">
|
|
<header className="bc-between" style={{ alignItems: 'flex-start' }}>
|
|
<div>
|
|
<div className="bc-small bc-muted">{t('client.home.hello')}</div>
|
|
<h1 className="bc-h2">{me.data?.firstName ?? t('client.home.guest')}</h1>
|
|
</div>
|
|
<LevelBadge level={level} />
|
|
</header>
|
|
|
|
{plus && me.data?.membership.endsAt && (
|
|
<Card tone="perks" tight>
|
|
<div className="bc-row">
|
|
<Icon name="gift" />
|
|
<div className="bc-small" style={{ fontWeight: 600 }}>
|
|
{t('client.home.levelUntil', { plan: LEVEL_NAME[level], date: dt(me.data.membership.endsAt, { day: 'numeric', month: 'long' }) })}
|
|
</div>
|
|
</div>
|
|
</Card>
|
|
)}
|
|
|
|
{!plus && (
|
|
<a href="#/membership" className="upsell">
|
|
<b>{t('client.home.upsellTitle', { pct: me.data?.plans.eliteMaxPct ?? 0 })}</b>
|
|
<span className="bc-small">{t('client.home.upsellText')}</span>
|
|
</a>
|
|
)}
|
|
|
|
<Card style={{ padding: 16 }}>
|
|
<div className="bc-between" style={{ padding: '0 4px 8px' }}>
|
|
<h2 className="bc-h3">{t('client.home.myOrders')}</h2>
|
|
<a href="#/orders" className="bc-small bc-muted">
|
|
{t('client.home.allOrders')}
|
|
</a>
|
|
</div>
|
|
{orders.isPending ? (
|
|
<Spinner />
|
|
) : orders.data && orders.data.active.length > 0 ? (
|
|
<div className="bc-stack" style={{ gap: 8 }}>
|
|
{orders.data.active.slice(0, 4).map((o) => (
|
|
<OrderRow key={o.id} o={o} />
|
|
))}
|
|
</div>
|
|
) : (
|
|
<div className="bc-stack" style={{ padding: '4px 4px 6px' }}>
|
|
<p className="bc-muted" style={{ margin: 0 }}>
|
|
{t('client.home.noOrders')}
|
|
</p>
|
|
</div>
|
|
)}
|
|
</Card>
|
|
|
|
<div className="quick">
|
|
<Chip onClick={() => navigate('/order/cleaning')}>
|
|
<Icon name="home" size={18} />
|
|
{t('services.cleaning.name')}
|
|
</Chip>
|
|
{lastDone && (
|
|
<Chip onClick={() => navigate(`/order/${lastDone.serviceCode}?repeat=${lastDone.id}`)}>
|
|
<Icon name="repeat" size={18} />
|
|
{t('client.home.repeatLast')}
|
|
</Chip>
|
|
)}
|
|
{!me.data?.agencyName && (
|
|
<Chip onClick={() => navigate('/profile?code=1')}>
|
|
<Icon name="gift" size={18} />
|
|
{t('client.home.enterCode')}
|
|
</Chip>
|
|
)}
|
|
</div>
|
|
|
|
<div className="tiles">
|
|
{(catalog.data?.categories ?? []).map((c) => (
|
|
<a key={c.code} href={c.code === 'PERKS' ? '#/perks' : `#/services?c=${c.code}`} className={`tile ${toneClass(c.code)}`}>
|
|
<div className="bc-between" style={{ alignItems: 'flex-start' }}>
|
|
<div className="tile__title">{t(`categories.${c.code}.name`)}</div>
|
|
<Icon name={CATEGORY_ICON[c.code as CategoryCode]} size={24} />
|
|
</div>
|
|
<div className="bc-small">
|
|
{levelDiscount(level, c) > 0 ? t('client.services.yourDiscount', { pct: levelDiscount(level, c) }) : t(`categories.${c.code}.short`)}
|
|
</div>
|
|
</a>
|
|
))}
|
|
</div>
|
|
</main>
|
|
);
|
|
}
|