Relocations and requests in the new admin; star rating in the client bot

Orders section gets tabs: Orders / Relocations / Requests (concierge sees the
last two). Relocation list with statuses, plan progress and current step;
relocation card with per-client steps (status, client comment, rename, add,
remove), documents, quiz answers, team note, write/call. Requests: website
cleaning requests and app requests (membership, call back) with notes,
create order, turn on membership. Dashboard links point here instead of the
old admin.

Fix: leads with an unanswered 'already moved?' (NULL) were skipped by
'moved not true' filters — plan sync and linking to the app client.

Client bot: after an order is done, asks for 1-5 stars; low rating asks what
went wrong, the reply is saved as the rating comment and goes to the team chat.

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-21 04:17:21 +04:00
1 parent bbb25d3d0d
commit dcba7c200c
19 files changed
+1422 -22

No files matched your search

+10
View File
@@ -2,6 +2,7 @@
* Воркер: превращает доменные события в сообщения Telegram на языке получателя (MSG-2, MSG-3)
* и раз в минуту запускает фоновые задачи — напоминания и истечение предложений.
*/
import { getFlow, type ServiceFlow } from '@barev/core';
import { getDb } from '@barev/db';
import { createT, formatAmd, formatDateTime, type Locale } from '@barev/i18n';
import { autoAssign, startTextOverrides, chatRecipients, dueReminders, dueSalesTasks, EVENTS_QUEUE, expireOffers, retryAutoAssign, type AutoAssignResult, type Ctx, type DomainEvent } from '@barev/services';
@@ -184,6 +185,15 @@ async function handle(event: DomainEvent) {
if (!o) return;
// клиент — о каждом переходе (STS)
await toClient(o, `notify.status.${o.service.flow}.${event.to}`);
// заказ выполнен — просим оценить (если ещё не оценил в приложении)
if (getFlow(o.service.flow as ServiceFlow).done.includes(event.to) && !(await db.rating.findUnique({ where: { orderId: o.id } }))) {
const t = createT(o.client.user.locale as Locale);
await clientBot.send({
chatId: o.client.user.telegramId,
text: t('notify.rateAsk'),
buttons: [[1, 2, 3, 4, 5].map((n) => ({ text: `${n} ★`, callback_data: `rate:${o.id}:${n}` }))],
});
}
if (event.to === 'CANCELLED' && o.provider?.user) await toProvider(o, o.provider.user, 'notify.pro.cancelled');
if (event.to === 'ASSIGNED' && event.from === 'NEW' && o.provider?.user && o.companyId) {
// назначил диспетчер компании — сотрудник узнаёт сразу