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 { 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 { 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', }; } }