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

+1 -1
View File
@@ -101,7 +101,7 @@ export async function updateLead(ctx: Ctx, input: UpdateLeadInput) {
/** Для админки: свежие сверху */
export async function listLeads(ctx: Ctx, input: { status?: LeadStatus }) {
// заявки, отправленные до появления шаблонов, получают план и документы при первом показе
const bare = await ctx.db.lead.findMany({ where: { submittedAt: { not: null }, moved: { not: true }, package: { not: null }, docs: { none: {} } } });
const bare = await ctx.db.lead.findMany({ where: { submittedAt: { not: null }, OR: [{ moved: null }, { moved: false }], package: { not: null }, docs: { none: {} } } });
for (const l of bare) {
await syncLeadSteps(ctx.db, l);
await ensureLeadDocs(ctx.db, l.id);
+1 -1
View File
@@ -72,7 +72,7 @@ export async function clientRelocation(ctx: Ctx, clientId: string) {
const username = client.user.username;
if (!username) return null;
const site = await ctx.db.lead.findFirst({
where: { clientId: null, contactType: 'TELEGRAM', contact: { equals: `@${username}`, mode: 'insensitive' }, moved: { not: true } },
where: { clientId: null, contactType: 'TELEGRAM', contact: { equals: `@${username}`, mode: 'insensitive' }, OR: [{ moved: null }, { moved: false }] },
orderBy: { createdAt: 'desc' },
});
if (!site) return null;