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

+105
View File
@@ -0,0 +1,105 @@
import { CATALOG, CATEGORY_CODES, DEFAULT_CATEGORY_DISCOUNTS, DEFAULT_SETTINGS, fromPrice, YEREVAN_DISTRICTS, type CategoryCode, type ParamsSchema, type Pricing, type SlotConfig } from '@barev/core';
export interface LandingCatalog {
categories: { code: CategoryCode; barevPlusDiscountPct: number; services: { code: string; fromPrice: number; active: boolean }[] }[];
barevPlus: { monthlyPrice: number; giftDays: number };
/** цены и скидки главной из настроек (админка → Настройки) */
offer: LandingOffer;
agency: { commissionPct: number; attributionMonths: number };
botUsername: string;
}
export interface LandingOffer {
plusMaxPct: number;
elitePrice: number;
eliteMaxPct: number;
hotelWithPlus: number;
hotelBase: number;
consultation: number;
docsFrom: number;
/** страницы услуг: /social-card, /residence-permit, /airport */
socialCard: number;
residenceEscortFrom: number;
residenceStateFee: number;
transferStandard: number;
transferPremium: number;
}
/** Ключи настроек → поля для лендинга; то же отдаёт public.catalog */
export function offerFromSettings(s: typeof DEFAULT_SETTINGS): LandingOffer {
return {
plusMaxPct: s['barevPlus.maxDiscountPct'],
elitePrice: s['elite.monthlyPrice'],
eliteMaxPct: s['elite.maxDiscountPct'],
hotelWithPlus: s['relocation.hotelWithPlus'],
hotelBase: s['relocation.hotelBase'],
consultation: s['relocation.consultation'],
docsFrom: s['docs.fromPrice'],
socialCard: s['docs.socialCardPrice'],
residenceEscortFrom: s['residence.escortFrom'],
residenceStateFee: s['residence.stateFee'],
transferStandard: s['transfer.standardPrice'],
transferPremium: s['transfer.premiumPrice'],
};
}
const API = process.env.API_URL ?? 'http://localhost:4000';
/** Запасной вариант, если API недоступно при сборке: цены-заглушки из каталога */
function fallback(): LandingCatalog {
return {
categories: CATEGORY_CODES.map((code) => ({
code,
barevPlusDiscountPct: DEFAULT_CATEGORY_DISCOUNTS[code],
services: CATALOG.filter((s) => s.category === code).map((s) => ({ code: s.code, fromPrice: fromPrice(s.pricing), active: s.stage === 1 })),
})),
barevPlus: { monthlyPrice: DEFAULT_SETTINGS['barevPlus.monthlyPrice'], giftDays: DEFAULT_SETTINGS['barevPlus.giftDays'] },
offer: offerFromSettings(DEFAULT_SETTINGS),
agency: { commissionPct: DEFAULT_SETTINGS['agency.defaultCommissionPct'], attributionMonths: DEFAULT_SETTINGS['agency.attributionMonths'] },
botUsername: process.env.CLIENT_BOT_USERNAME ?? 'barev_club_bot',
};
}
/** LND-5: цены и состав услуг — из справочника (ISR, обновление раз в 5 минут) */
export async function getCatalog(): Promise<LandingCatalog> {
try {
const res = await fetch(`${API}/trpc/public.catalog`, { next: { revalidate: 300 } });
if (!res.ok) throw new Error(String(res.status));
const body = (await res.json()) as { result: { data: { json: LandingCatalog } } };
// старое API без offer или без новых полей — значения по умолчанию
return { ...body.result.data.json, offer: { ...offerFromSettings(DEFAULT_SETTINGS), ...body.result.data.json.offer } };
} catch {
return fallback();
}
}
export interface CleaningConfig {
active: boolean;
pricing: Pricing;
paramsSchema: ParamsSchema;
slotConfig: SlotConfig;
categoryDiscountPct: number;
districts: string[];
botUsername: string;
}
/** Страница /clean: правила цены из справочника (ISR); без API — значения из каталога в коде */
export async function getCleaning(): Promise<CleaningConfig> {
try {
const res = await fetch(`${API}/trpc/public.cleaning`, { next: { revalidate: 300 } });
if (!res.ok) throw new Error(String(res.status));
const body = (await res.json()) as { result: { data: { json: CleaningConfig } } };
return body.result.data.json;
} catch {
const s = CATALOG.find((c) => c.code === 'cleaning')!;
return {
active: true,
pricing: s.pricing,
paramsSchema: s.paramsSchema,
slotConfig: s.slotConfig,
categoryDiscountPct: DEFAULT_CATEGORY_DISCOUNTS.HOME,
districts: [...YEREVAN_DISTRICTS],
botUsername: process.env.CLIENT_BOT_USERNAME ?? 'barev_club_bot',
};
}
}