Files
BarevClub/apps/miniapp-client/src/lib.tsx
T

96 lines
4.0 KiB
TypeScript

import { CATEGORY_TONE, Icon, type CategoryCode } from '@barev/ui';
import { paramsSummary, useI18n } from '@barev/web';
import type { ReactNode } from 'react';
export const toneClass = (c: string) => `bc-tone-${CATEGORY_TONE[c as CategoryCode] ?? 'white'}`;
export function PageHead({ title, back = true, right }: { title: ReactNode; back?: boolean; right?: ReactNode }) {
const { t } = useI18n();
return (
<div className="page-head">
{back && (
<button className="back" onClick={() => window.history.back()} aria-label={t('common.back')}>
<Icon name="back" />
</button>
)}
<h1 className="bc-h2" style={{ flex: 1 }}>
{title}
</h1>
{right}
</div>
);
}
export interface OrderLike {
id: string;
number: number;
serviceCode: string;
category: string;
flow: string;
status: string;
scheduledAt: Date;
params: Record<string, unknown>;
}
/** Подпись параметров: «Генеральная · 2 комнаты» */
export function useParamsSummary() {
const { t } = useI18n();
return (serviceCode: string, params: Record<string, unknown>) => paramsSummary(t, serviceCode, params);
}
/** Строка заявки как в превью «Мои заявки» макета */
export function OrderRow({ o }: { o: OrderLike }) {
const { t, dt } = useI18n();
const summary = useParamsSummary();
return (
<a href={`#/orders/${o.id}`} className={`order-row ${toneClass(o.category)}`}>
<div className="order-row__main">
<div className="order-row__title">
{t(`services.${o.serviceCode}.name`)} · {summary(o.serviceCode, o.params)}
</div>
<div className="bc-small">{dt(o.scheduledAt, { weekday: 'short', day: 'numeric', month: 'short', hour: '2-digit', minute: '2-digit' })}</div>
</div>
<span className="bc-badge">{t(`status.${o.flow}.${o.status}`)}</span>
</a>
);
}
export type Level = 'BAREV' | 'BAREV_PLUS' | 'ELITE';
export const LEVEL_NAME: Record<Level, string> = { BAREV: 'Barev', BAREV_PLUS: 'Barev+', ELITE: 'Barev Elite' };
/** Цвет плашки уровня: как карты на сайте — Barev+ розовая, Elite жёлтая */
export const LEVEL_TONE: Record<Level, { bg: string; ink: string }> = {
BAREV: { bg: 'var(--bc-surface)', ink: 'var(--bc-ink)' },
BAREV_PLUS: { bg: 'var(--bc-pink)', ink: 'var(--bc-plum)' },
ELITE: { bg: 'var(--bc-perks)', ink: 'var(--bc-plum)' },
};
/** Скидка клиента на направление по его уровню; у Elite без своей скидки — скидка Barev+ */
export function levelDiscount(level: Level, c: { barevPlusDiscountPct: number; eliteDiscountPct: number }) {
if (level === 'ELITE') return Math.max(c.eliteDiscountPct, c.barevPlusDiscountPct);
return level === 'BAREV_PLUS' ? c.barevPlusDiscountPct : 0;
}
export function LevelBadge({ level }: { level: Level }) {
const tone = LEVEL_TONE[level];
return (
<a href="#/membership" style={{ textDecoration: 'none' }}>
<span className="bc-badge" style={{ background: tone.bg, color: tone.ink, padding: '8px 14px', fontSize: 13 }}>
{LEVEL_NAME[level]}
</span>
</a>
);
}
export type RelocationNames = Record<string, { name: Record<string, string>; hint: Record<string, string> | null }>;
/** Название шага или документа: своё у клиента (title) или из общего списка на языке клиента */
export function itemName(names: RelocationNames | undefined, kind: 'STEP' | 'DOC', item: { code: string; title: string | null }, locale: string) {
if (item.title) return item.title;
const n = names?.[`${kind}:${item.code}`]?.name;
return n?.[locale] || n?.ru || item.code;
}
export function itemHint(names: RelocationNames | undefined, kind: 'STEP' | 'DOC', item: { code: string; title: string | null }, locale: string) {
if (item.title) return null;
const h = names?.[`${kind}:${item.code}`]?.hint;
return h?.[locale] || h?.ru || null;
}