FieldOps/scripts/admin-queue-smoke.ts
Pedro Gomes 617c81357f MAI CALL - step 11
Passo 11 completo. Build limpo, AC verificado.

O que foi construído no admin-web (localhost:3001):

Infraestrutura completa a partir do zero: Tailwind, tRPC client/server, auth por autologin, env.ts, providers
/maintenance — cliente de polling com refetchInterval: 5000ms:
Header com contador de pedidos abertos + filtros por estado (checkboxes) e área (select)
Grid de cards com thumbnail (presigned GET), posto, descrição, reporter + tempo relativo, badge de status
OPEN → botão Aceitar (mutation claim)
CLAIMED → info "Aceite por X há Ym" + botão Marcar resolvido (dialog com nota opcional)
RESOLVED → badge verde + info "Resolvido por X há Ym"
Badge no document.title: (N) FieldOps — Manutenção
Toggle de notificação sonora via Web Audio API (beep ao detectar novo OPEN)
2026-05-16 16:41:16 +01:00

76 lines
3.2 KiB
TypeScript

/**
* AC verification for Passo 11:
* Request created by op1 appears in admin queue; claim moves to CLAIMED;
* resolve moves to RESOLVED.
*/
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { config as loadEnv } from 'dotenv';
const here = path.dirname(fileURLToPath(import.meta.url));
loadEnv({ path: path.resolve(here, '../.env') });
import { prisma } from '../packages/db/src/index.js';
import { appRouter, createTRPCContext } from '../packages/api/src/index.js';
import { createCallerFactory } from '../packages/api/src/trpc.js';
async function makeCaller(email: string) {
const user = await prisma.user.findFirst({ where: { email } });
if (!user) throw new Error(`${email} not found — run pnpm db:seed`);
const ctx = await createTRPCContext({
user: { id: user.id, email: user.email, role: user.role as 'ADMIN' | 'OPERATOR', tenantId: user.tenantId },
headers: new Headers(),
});
return createCallerFactory(appRouter)(ctx);
}
async function main() {
const op1 = await makeCaller('op1@demo.local');
const admin = await makeCaller('admin@demo.local');
const ws = (await admin.workstation.list())[0];
if (!ws) throw new Error('No workstations');
// Create a request via op1
console.log('1. op1 creates request...');
const { id } = await op1.maintenanceRequest.create({
workstationId: ws.id,
description: 'Pedido para teste da fila admin',
clientRequestId: crypto.randomUUID(),
});
console.log(` id=${id}`);
// Queue shows the request (≤5s polling simulated by immediate check)
console.log('2. Admin queue shows the request...');
const { items } = await admin.maintenanceRequest.queue({ statuses: ['OPEN'] });
const found = items.find((i) => i.id === id);
if (!found) throw new Error('Request not in queue');
if (found.status !== 'OPEN') throw new Error(`Expected OPEN, got ${found.status}`);
console.log(` Found in queue with status=${found.status}`);
console.log(` Posto: ${found.workstation.code}, Reporter: ${found.reportedBy.email}`);
// Claim
console.log('3. Admin claims the request...');
const claimed = await admin.maintenanceRequest.claim({ id });
if (claimed.status !== 'CLAIMED') throw new Error(`Expected CLAIMED, got ${claimed.status}`);
const afterClaim = await admin.maintenanceRequest.queue({ statuses: ['CLAIMED'] });
if (!afterClaim.items.find((i) => i.id === id)) throw new Error('Not in CLAIMED queue');
console.log(` status=CLAIMED, claimedBy=${claimed.claimedBy?.email}`);
// Resolve
console.log('4. Admin resolves the request...');
const resolved = await admin.maintenanceRequest.resolve({ id, resolutionNote: 'Peça substituída' });
if (resolved.status !== 'RESOLVED') throw new Error(`Expected RESOLVED, got ${resolved.status}`);
const afterResolve = await admin.maintenanceRequest.queue({ statuses: ['RESOLVED'] });
if (!afterResolve.items.find((i) => i.id === id)) throw new Error('Not in RESOLVED queue');
console.log(` status=RESOLVED, resolvedBy=${resolved.resolvedBy?.email}`);
await prisma.$disconnect();
console.log('\nPasso 11 AC PASSED');
}
main().catch(async (err) => {
console.error('Passo 11 AC FAILED:', err);
await prisma.$disconnect();
process.exit(1);
});