Chats: show which order a provider writes about, or that there is no order
- Provider messages store the order that was current when they wrote: a started order first, otherwise the nearest assigned one within -4h..+12h; none — "no order". - Admin chat: order tag above each provider message, "now / next" order strip in the conversation header, order number (or "no order") in the thread list. - Telegram notification to the team gets an order line or "no current order". - Test for the order-picking rule; demo shows both cases. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
23ffb2bf1d
commit
2772837e29
14 files changed
+321
-13
No files matched your search
@@ -7,6 +7,7 @@
|
||||
import { readFileSync } from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { getDb } from '@barev/db';
|
||||
import { providerCurrentOrder, silentNotifier, type Ctx } from '@barev/services';
|
||||
import { putFile } from '@barev/services/storage';
|
||||
import { env } from '../src/env.ts';
|
||||
|
||||
@@ -44,10 +45,23 @@ const katya = await clientByTg(100001004n); // Barev+
|
||||
const natalia = await clientByTg(100001007n); // без подписки
|
||||
const lilit = await clientByTg(100001025n); // Barev+
|
||||
const provider = await db.provider.findFirst({ where: { userId: { not: null }, active: true }, orderBy: { createdAt: 'asc' } });
|
||||
// второй исполнитель — с заказом в работе, чтобы было видно «пишет по заказу №…»
|
||||
const busy = await db.provider.findFirst({ where: { userId: { not: null }, active: true, orders: { some: { status: { in: ['EN_ROUTE', 'CLEANING', 'ON_SITE', 'REPAIRING'] } } } } });
|
||||
const providerIds = [provider?.id, busy?.id].filter((x): x is string => !!x);
|
||||
const svc: Ctx = { db, notify: silentNotifier, now: () => new Date() };
|
||||
// демо-даты: у «занятого» исполнителя заказ идёт сейчас, у первого — следующий заказ завтра
|
||||
if (busy) {
|
||||
const o = await db.order.findFirst({ where: { providerId: busy.id, status: { in: ['EN_ROUTE', 'CLEANING', 'ON_SITE', 'REPAIRING'] } } });
|
||||
if (o) await db.order.update({ where: { id: o.id }, data: { scheduledAt: ago(90) } });
|
||||
}
|
||||
if (provider) {
|
||||
const o = await db.order.findFirst({ where: { providerId: provider.id, status: 'ASSIGNED' } });
|
||||
if (o) await db.order.update({ where: { id: o.id }, data: { scheduledAt: new Date(now + 20 * 60 * MIN) } });
|
||||
}
|
||||
|
||||
const clientIds = [arsen, dmitry, maria, sarah, alex, katya, natalia, lilit].map((c) => c.id);
|
||||
await db.chatThread.deleteMany({ where: { OR: [{ clientId: { in: clientIds } }, ...(provider ? [{ providerId: provider.id }] : [])] } });
|
||||
await db.conciergeMessage.deleteMany({ where: { OR: [{ clientId: { in: clientIds } }, ...(provider ? [{ providerId: provider.id }] : [])] } });
|
||||
await db.chatThread.deleteMany({ where: { OR: [{ clientId: { in: clientIds } }, { providerId: { in: providerIds } }] } });
|
||||
await db.conciergeMessage.deleteMany({ where: { OR: [{ clientId: { in: clientIds } }, { providerId: { in: providerIds } }] } });
|
||||
await db.client.updateMany({ where: { id: { in: clientIds } }, data: { chatChannel: null } });
|
||||
await db.client.update({ where: { id: sarah.id }, data: { chatChannel: 'SUPPORT' } });
|
||||
|
||||
@@ -93,10 +107,13 @@ async function thread(
|
||||
});
|
||||
for (const m of messages) {
|
||||
const incoming = m.author === 'CLIENT' || m.author === 'PROVIDER';
|
||||
// исполнитель: заказ, который был текущим в момент сообщения
|
||||
const order = 'providerId' in peer && m.author === 'PROVIDER' ? await providerCurrentOrder(svc, peer.providerId, ago(m.at)) : null;
|
||||
await db.conciergeMessage.create({
|
||||
data: {
|
||||
threadId: t.id,
|
||||
...peer,
|
||||
orderId: order?.id ?? null,
|
||||
author: m.author,
|
||||
adminUserId: m.by,
|
||||
text: m.text,
|
||||
@@ -175,6 +192,12 @@ await thread({ clientId: alex.id }, 'SUPPORT', { assigneeId: gor.id, status: 'CL
|
||||
{ author: 'SYSTEM', text: 'closed', at: 60 * 29 - 12, by: gor.id },
|
||||
]);
|
||||
|
||||
if (busy && busy.id !== provider?.id) {
|
||||
await thread({ providerId: busy.id }, 'SUPPORT', { waiting: 3 }, [
|
||||
{ author: 'PROVIDER', text: 'Клиентка просит дополнительно помыть балкон — это входит в заказ или считать отдельно?', at: 3 },
|
||||
]);
|
||||
}
|
||||
|
||||
if (provider) {
|
||||
await thread({ providerId: provider.id }, 'SUPPORT', { waiting: 7 }, [
|
||||
{ author: 'PROVIDER', text: 'Здравствуйте, по заказу на Абовяна нет кода от подъезда, клиент не отвечает. Что делать?', at: 7 },
|
||||
@@ -191,5 +214,5 @@ const TEMPLATES: { channel: 'SUPPORT' | 'CONCIERGE' | null; title: string; text:
|
||||
await db.chatTemplate.deleteMany({ where: { title: { in: TEMPLATES.map((t) => t.title) } } });
|
||||
for (const t of TEMPLATES) await db.chatTemplate.create({ data: { ...t, createdById: owner.id } });
|
||||
|
||||
console.log(`Демо-чаты готовы: ${provider ? 9 : 8} диалогов, ${TEMPLATES.length} шаблона. Сотрудники: ${anna.name} (консьерж), ${gor.name} (поддержка).`);
|
||||
console.log(`Демо-чаты готовы: ${8 + providerIds.length} диалогов, ${TEMPLATES.length} шаблона. Сотрудники: ${anna.name} (консьерж), ${gor.name} (поддержка).`);
|
||||
process.exit(0);
|
||||
Reference in new issue
Block a user