/** * Локальная разработка: демо-данные для двух сценариев клиентского приложения. * pnpm --filter @barev/api dev:demo * Анна — переезжает (план «под ключ» с шагами, документы, переписка с консьержем), * John — живёт в Армении с Barev+, 李明 — с Elite, Ольга — живёт без подписки; партнёры клуба. * Повторный запуск пересоздаёт демо-состояние этих клиентов. */ import { getDb } from '@barev/db'; import { acceptPartnerInvite, createAppLead, createOwnerInvite, createStaffInvite, grantSubscription, savePartnerOffer, startClient, giveConsent, type Ctx } from '@barev/services'; import { env } from '../src/env.ts'; if (env.isProd) throw new Error('Только для разработки'); const db = getDb(); const ctx: Ctx = { db, notify: { emit: () => {} }, now: () => new Date() }; const DAY = 24 * 3600_000; async function client(id: number, firstName: string, lang: string, username?: string) { const res = await startClient(ctx, { id, first_name: firstName, language_code: lang, username }); const c = await db.client.findUniqueOrThrow({ where: { id: res.clientId } }); if (!c.consentAt) await giveConsent(ctx, c.id); // чистое демо-состояние await db.lead.deleteMany({ where: { clientId: c.id } }); await db.clientRequest.deleteMany({ where: { clientId: c.id } }); await db.conciergeMessage.deleteMany({ where: { clientId: c.id } }); await db.subscription.deleteMany({ where: { clientId: c.id, source: 'MANUAL' } }); return c.id; } const anna = await client(100000001, 'Анна', 'ru', 'anna_moves'); await db.client.update({ where: { id: anna }, data: { segment: 'MOVING' } }); const moveDate = new Date(Date.now() + 40 * DAY).toISOString().slice(0, 10); const lead = await createAppLead(ctx, anna, { moveWhen: 'date', moveDate: new Date(`${moveDate}T00:00:00Z`), package: 'TURNKEY', comment: 'Двое взрослых и кот' }); const notes: Record = { airport: { status: 'DONE', note: 'Водитель Арам встретит с табличкой, +374 91 000 000' }, stay: { status: 'DONE', note: 'Отель Republica, 5 ночей, завтрак включён' }, flat: { status: 'IN_PROGRESS', note: 'Подобрали 3 варианта в Кентроне, просмотры в среду с 15:00' }, }; for (const s of lead.steps) if (notes[s.code]) await db.leadStep.update({ where: { id: s.id }, data: notes[s.code]! }); await db.lead.update({ where: { id: lead.id }, data: { status: 'IN_WORK' } }); await db.leadDoc.updateMany({ where: { leadId: lead.id, code: { in: ['passport', 'passportCopies', 'photos'] } }, data: { checked: true } }); const admin = await db.adminUser.findFirst(); const msgs: [string, 'CLIENT' | 'OPERATOR', number][] = [ ['Здравствуйте! Можно ли взять кота в отель?', 'CLIENT', 50], ['Добрый день! Да, в Republica можно с животными, предупредили отель 🐈', 'OPERATOR', 45], ['Спасибо! А что с апостилем на дипломе — делать до переезда?', 'CLIENT', 5], ]; for (const [text, author, minAgo] of msgs) { await db.conciergeMessage.create({ data: { clientId: anna, author, text, adminUserId: author === 'OPERATOR' ? admin?.id : undefined, readAt: minAgo > 10 ? new Date() : null, createdAt: new Date(Date.now() - minAgo * 60_000) }, }); } const john = await client(100000002, 'John', 'en', 'john_yerevan'); await db.client.update({ where: { id: john }, data: { segment: 'LIVING' } }); await grantSubscription(ctx, { clientId: john, plan: 'BAREV_PLUS', days: 30 }); await db.clientRequest.create({ data: { clientId: john, kind: 'SERVICE', serviceCode: 'repair', comment: 'Kitchen tap is leaking, after 6 pm please' } }); await db.conciergeMessage.create({ data: { clientId: john, author: 'CLIENT', text: 'Hi! Can you recommend an English-speaking dentist?' } }); const li = await client(100000003, '李明', 'zh', 'li_ming'); await db.client.update({ where: { id: li }, data: { segment: 'LIVING' } }); await grantSubscription(ctx, { clientId: li, plan: 'ELITE', days: 30 }); const olga = await client(100000004, 'Ольга', 'ru', 'olga_local'); await db.client.update({ where: { id: olga }, data: { segment: 'LIVING' } }); await db.clientRequest.create({ data: { clientId: olga, kind: 'SUBSCRIPTION', plan: 'ELITE' } }); // новый клиент — увидит выбор сценария при первом входе await db.client.updateMany({ where: { user: { telegramId: 100000099n } }, data: { segment: null } }); if ((await db.partner.count()) === 0) { const offer = (ru: string, en: string, zh: string, hy: string, es: string) => ({ ru, en, zh, hy, es }); await db.partner.createMany({ data: [ { category: 'cafe', name: 'Lumen Coffee', address: 'Ереван, ул. Абовяна 18', offer: offer('−15% на кофе и завтраки', '−15% on coffee and breakfast', '咖啡和早餐 −15%', '−15% սուրճի և նախաճաշի համար', '−15% en café y desayunos') }, { category: 'restaurant', name: 'Dolmama', address: 'Ереван, ул. Пушкина 10', offer: offer('Десерт в подарок к ужину', 'Free dessert with dinner', '晚餐赠送甜点', 'Աղանդեր նվեր ընթրիքին', 'Postre de regalo con la cena') }, { category: 'restaurant', name: 'Lavash', address: 'Ереван, ул. Туманяна 21', offer: offer('−10% на всё меню', '−10% on the whole menu', '全部菜单 −10%', '−10% ամբողջ մենյուի համար', '−10% en toda la carta') }, { category: 'shop', name: 'Gum Market', address: 'Ереван, ул. Московяна 35', offer: offer('Подарочный набор специй', 'Free spice gift set', '赠送香料礼盒', 'Համեմունքների նվեր հավաքածու', 'Set de especias de regalo') }, { category: 'beauty', name: 'Barber Yerevan', address: 'Ереван, пр. Маштоца 7', offer: offer('−20% на первую стрижку', '−20% on the first haircut', '首次理发 −20%', '−20% առաջին սանրվածքի համար', '−20% en el primer corte') }, ], }); } // предложения партнёров по уровням и лимитам; Арам — собственник Lumen Coffee, Гаяне — сотрудник const lumen = (await db.partner.findFirst({ where: { name: 'Lumen Coffee' } })) ?? (await db.partner.create({ data: { category: 'cafe', name: 'Lumen Coffee', address: 'Ереван, ул. Абовяна 18', offer: { ru: 'Кофейня у Каскада', en: 'Café near the Cascade', zh: '', hy: '', es: '' } } })); if (lumen && (await db.partnerOffer.count({ where: { partnerId: lumen.id } })) === 0) { const L = (ru: string, en: string) => ({ ru, en, zh: '', hy: '', es: '' }); await savePartnerOffer(ctx, { partnerId: lumen.id, title: L('−10% на кофе и завтраки', '−10% on coffee and breakfast'), minLevel: null, limit: 'NONE', limitCount: 1, active: true }); await savePartnerOffer(ctx, { partnerId: lumen.id, title: L('Десерт на первый визит', 'Dessert on the first visit'), minLevel: null, limit: 'ONCE', limitCount: 1, active: true }); await savePartnerOffer(ctx, { partnerId: lumen.id, title: L('Кофе в подарок', 'Free coffee'), minLevel: 'BAREV_PLUS', limit: 'WEEK', limitCount: 1, active: true }); await savePartnerOffer(ctx, { partnerId: lumen.id, title: L('Завтрак для двоих', 'Breakfast for two'), minLevel: 'ELITE', limit: 'MONTH', limitCount: 2, active: true }); } if (lumen && (await db.partnerMember.count({ where: { partnerId: lumen.id } })) === 0) { const { url } = await createOwnerInvite(ctx, lumen.id); await acceptPartnerInvite(ctx, { id: 300000010, first_name: 'Арам', language_code: 'ru' }, url.split('start=')[1]!); const staff = await createStaffInvite(ctx, 300000010n, lumen.id); await acceptPartnerInvite(ctx, { id: 300000011, first_name: 'Гаяне', language_code: 'ru' }, staff.url.split('start=')[1]!); } console.log('Демо готово: Анна — переезд, John — Barev+, 李明 — Elite, Ольга — без подписки, «Новый клиент» — выбор сценария.'); await db.$disconnect();