import type { ParamsSchema } from './params.ts'; import type { ExtraUnit, Pricing } from './pricing.ts'; import type { SlotConfig } from './slots.ts'; import type { CategoryCode, ProviderRole, ServiceFlow } from './types.ts'; /** * Состав каталога из PRD (раздел 2) — «только эти, ничего не добавлять». * Цены здесь — заглушки для seed; реальные задаёт владелец в админке. * `stage` — этап, в котором услуга становится доступной для заказа. */ export interface CatalogService { code: string; category: CategoryCode; flow: ServiceFlow; providerRole: ProviderRole; stage: 1 | 2 | 3; isOnline: boolean; pricing: Pricing; paramsSchema: ParamsSchema; slotConfig: SlotConfig; checklist: { code: string; requiresPhoto?: boolean }[]; } const DAY_WINDOW: SlotConfig = { dayStartMinute: 9 * 60, dayEndMinute: 20 * 60, stepMinutes: 60, leadHours: 3, horizonDays: 14 }; const fixed = (price: number, minutes: number): Pricing => ({ kind: 'fixed', price, minutes }); /** * Доп. услуги клининга (референс — qlean.ru). Цены — заглушки в драмах, владелец меняет их в админке. * max — сколько единиц клиент может выбрать; minutes — сколько времени добавляет одна единица. */ export const CLEANING_EXTRAS: { code: string; unit: ExtraUnit; price: number; minutes: number; max: number }[] = [ { code: 'windows', unit: 'item', price: 3500, minutes: 20, max: 20 }, { code: 'balconyGlazing', unit: 'item', price: 8000, minutes: 45, max: 3 }, { code: 'balcony', unit: 'item', price: 4500, minutes: 30, max: 3 }, { code: 'fridge', unit: 'service', price: 3000, minutes: 30, max: 1 }, { code: 'oven', unit: 'service', price: 2500, minutes: 30, max: 1 }, { code: 'microwave', unit: 'service', price: 1500, minutes: 15, max: 1 }, { code: 'kitchenCabinets', unit: 'service', price: 4000, minutes: 45, max: 1 }, { code: 'complexKitchen', unit: 'service', price: 7500, minutes: 60, max: 1 }, { code: 'chandelier', unit: 'item', price: 2500, minutes: 20, max: 10 }, { code: 'wardrobe', unit: 'service', price: 3500, minutes: 30, max: 1 }, { code: 'limescale', unit: 'service', price: 4000, minutes: 30, max: 1 }, { code: 'petHair', unit: 'service', price: 2000, minutes: 20, max: 1 }, { code: 'litter', unit: 'item', price: 1500, minutes: 10, max: 5 }, { code: 'ironing', unit: 'hour', price: 3500, minutes: 60, max: 4 }, { code: 'extraTime', unit: 'halfHour', price: 2500, minutes: 30, max: 8 }, { code: 'keys', unit: 'service', price: 3500, minutes: 0, max: 1 }, { code: 'experienced', unit: 'service', price: 5000, minutes: 0, max: 1 }, { code: 'vacuum', unit: 'service', price: 4000, minutes: 0, max: 1 }, ]; const CLEANING_EXTRAS_PRICES = Object.fromEntries(CLEANING_EXTRAS.map(({ code, unit, price, minutes }) => [code, { unit, price, minutes }])); export const CATALOG: CatalogService[] = [ { code: 'cleaning', category: 'HOME', flow: 'CLEANING', providerRole: 'CLEANER', stage: 1, isOnline: false, // модель цены как у Qlean: тип уборки × комнаты × санузлы + доп. услуги, которые выбирает клиент pricing: { kind: 'rooms', variantParam: 'cleaningType', roomsParam: 'rooms', bathroomsParam: 'bathrooms', extrasParam: 'extras', tiers: { regular: { base: 15000, perRoom: 4000, baseMinutes: 120, perRoomMinutes: 45, perBathroom: 3000, perBathroomMinutes: 30 }, general: { base: 25000, perRoom: 7000, baseMinutes: 210, perRoomMinutes: 75, perBathroom: 5000, perBathroomMinutes: 45 }, renovation: { base: 35000, perRoom: 10000, baseMinutes: 300, perRoomMinutes: 90, perBathroom: 7000, perBathroomMinutes: 60 }, }, extras: CLEANING_EXTRAS_PRICES, }, paramsSchema: { fields: [ { key: 'cleaningType', type: 'select', options: ['regular', 'general', 'renovation'], default: 'regular', required: true }, { key: 'rooms', type: 'int', min: 1, max: 6, default: 2, required: true }, { key: 'bathrooms', type: 'int', min: 1, max: 4, default: 1, required: true }, { key: 'extras', type: 'extras', items: CLEANING_EXTRAS.map(({ code, max }) => ({ code, max })), required: false }, ], }, slotConfig: DAY_WINDOW, checklist: [ { code: 'kitchen' }, { code: 'bathroom' }, { code: 'rooms' }, { code: 'floors' }, { code: 'photoReport', requiresPhoto: true }, ], }, { code: 'repair', category: 'HOME', flow: 'REPAIR', providerRole: 'HANDYMAN', stage: 2, isOnline: false, pricing: fixed(8000, 90), paramsSchema: { fields: [ { key: 'repairType', type: 'select', options: ['plumbing', 'electrical', 'minor'], required: true }, { key: 'problem', type: 'text', maxLength: 1000, required: true }, ], }, slotConfig: DAY_WINDOW, checklist: [{ code: 'diagnostics' }, { code: 'estimate' }, { code: 'repair' }, { code: 'photo', requiresPhoto: true }], }, { code: 'transfer', category: 'HOME', flow: 'TRANSFER', providerRole: 'DRIVER', // решение владельца: трансфер отложен, сценарий и state machine готовы stage: 2, isOnline: false, pricing: fixed(10000, 60), paramsSchema: { fields: [ { key: 'direction', type: 'select', options: ['fromAirport', 'toAirport'], required: true }, { key: 'flight', type: 'text', maxLength: 16, required: true }, { key: 'passengers', type: 'int', min: 1, max: 7, default: 1, required: true }, { key: 'luggage', type: 'int', min: 0, max: 10, default: 1, required: true }, ], }, slotConfig: { dayStartMinute: 0, dayEndMinute: 1440, stepMinutes: 15, leadHours: 6, horizonDays: 30 }, checklist: [{ code: 'luggage' }, { code: 'address' }], }, { code: 'social_card', category: 'DOCS', flow: 'DOCUMENTS', providerRole: 'DOCS_AGENT', stage: 2, isOnline: false, pricing: fixed(12000, 60), paramsSchema: { fields: [{ key: 'forWhom', type: 'select', options: ['self', 'family'], required: true }] }, slotConfig: DAY_WINDOW, checklist: [], }, { code: 'passport_translation', category: 'DOCS', flow: 'DOCUMENTS', providerRole: 'DOCS_AGENT', stage: 2, isOnline: false, pricing: fixed(9000, 60), paramsSchema: { fields: [{ key: 'forWhom', type: 'select', options: ['self', 'family'], required: true }] }, slotConfig: DAY_WINDOW, checklist: [], }, { code: 'residence_permit', category: 'DOCS', flow: 'DOCUMENTS', providerRole: 'DOCS_AGENT', stage: 2, isOnline: false, pricing: fixed(45000, 60), paramsSchema: { fields: [{ key: 'forWhom', type: 'select', options: ['self', 'family'], required: true }] }, slotConfig: DAY_WINDOW, checklist: [], }, { code: 'lang_armenian', category: 'LEARNING', flow: 'LANGUAGE', providerRole: 'TEACHER', stage: 2, isOnline: true, pricing: fixed(40000, 60), paramsSchema: { fields: [ { key: 'level', type: 'select', options: ['A1', 'A2', 'B1', 'B2'], required: true }, { key: 'format', type: 'select', options: ['online', 'offline'], required: true }, { key: 'lessons', type: 'select', options: ['4', '8', '12'], required: true }, ], }, slotConfig: DAY_WINDOW, checklist: [{ code: 'review' }, { code: 'newTopic' }, { code: 'homework' }], }, { code: 'lang_english', category: 'LEARNING', flow: 'LANGUAGE', providerRole: 'TEACHER', stage: 2, isOnline: true, pricing: fixed(40000, 60), paramsSchema: { fields: [ { key: 'level', type: 'select', options: ['A1', 'A2', 'B1', 'B2', 'C1'], required: true }, { key: 'format', type: 'select', options: ['online', 'offline'], required: true }, { key: 'lessons', type: 'select', options: ['4', '8', '12'], required: true }, ], }, slotConfig: DAY_WINDOW, checklist: [{ code: 'review' }, { code: 'newTopic' }, { code: 'homework' }], }, { code: 'career', category: 'LEARNING', flow: 'CAREER', providerRole: 'CAREER_CONSULTANT', stage: 2, isOnline: true, pricing: fixed(20000, 60), paramsSchema: { fields: [{ key: 'topic', type: 'select', options: ['resume', 'jobSearch'], required: true }] }, slotConfig: DAY_WINDOW, checklist: [{ code: 'resume' }, { code: 'plan' }, { code: 'materials' }], }, ]; /** Скидка Barev+ по направлениям — заглушки */ export const DEFAULT_CATEGORY_DISCOUNTS: Record = { HOME: 10, DOCS: 10, LEARNING: 15, PERKS: 0 }; /** скидки Barev Elite по направлениям, % — по умолчанию «до −35%» с главной */ export const DEFAULT_ELITE_DISCOUNTS: Record = { HOME: 35, DOCS: 35, LEARNING: 35, PERKS: 0 }; /** 12 административных районов Еревана; названия — i18n districts. */ export const YEREVAN_DISTRICTS = [ 'ajapnyak', 'arabkir', 'avan', 'davtashen', 'erebuni', 'kanaker_zeytun', 'kentron', 'malatia_sebastia', 'nork_marash', 'nor_nork', 'nubarashen', 'shengavit', ] as const; /** Роль исполнителя → какие сценарии он ведёт (для бота исполнителей) */ export const ROLE_FLOWS: Record = { CLEANER: ['CLEANING'], HANDYMAN: ['REPAIR'], DRIVER: ['TRANSFER'], TEACHER: ['LANGUAGE'], CAREER_CONSULTANT: ['CAREER'], DOCS_AGENT: ['DOCUMENTS'], };