Add USB policy improvements and emprestimos details modal

- Add cron job to cleanup stale pending USB policies every 30 min
- Add cleanupStalePendingPolicies mutation to usbPolicy.ts
- Add USB policy fields to machines listByTenant query
- Display USB status chip in device details and bulk control modal
- Add details modal for emprestimos with all loan information
- Add observacoesDevolucao field to preserve original observations
- Fix status text size in details modal title

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
rever-tecnologia 2025-12-05 08:24:56 -03:00
parent e493ec9d5d
commit 7469d3b5e6
7 changed files with 346 additions and 66 deletions

View file

@ -25,4 +25,12 @@ if (autoPauseCronEnabled) {
)
}
// Cleanup de policies USB pendentes por mais de 1 hora (sem flag, sempre ativo)
crons.interval(
"cleanup-stale-usb-policies",
{ minutes: 30 },
api.usbPolicy.cleanupStalePendingPolicies,
{}
)
export default crons

View file

@ -68,8 +68,10 @@ export const list = query({
clienteId: emprestimo.clienteId,
clienteNome: emprestimo.clienteSnapshot.name,
responsavelNome: emprestimo.responsavelNome,
responsavelContato: emprestimo.responsavelContato,
tecnicoId: emprestimo.tecnicoId,
tecnicoNome: emprestimo.tecnicoSnapshot.name,
tecnicoEmail: emprestimo.tecnicoSnapshot.email,
equipamentos: emprestimo.equipamentos,
quantidade: emprestimo.quantidade,
valor: emprestimo.valor,
@ -78,6 +80,7 @@ export const list = query({
dataDevolucao: emprestimo.dataDevolucao,
status: emprestimo.status,
observacoes: emprestimo.observacoes,
observacoesDevolucao: emprestimo.observacoesDevolucao,
multaDiaria: emprestimo.multaDiaria,
multaCalculada: emprestimo.multaCalculada,
createdAt: emprestimo.createdAt,
@ -231,7 +234,7 @@ export const devolver = mutation({
status: "DEVOLVIDO",
dataDevolucao: now,
multaCalculada,
observacoes: args.observacoes ?? emprestimo.observacoes,
observacoesDevolucao: args.observacoes,
updatedBy: args.updatedBy,
updatedAt: now,
})

View file

@ -1015,6 +1015,9 @@ export const listByTenant = query({
lastPostureAt,
remoteAccess: machine.remoteAccess ?? null,
customFields: machine.customFields ?? [],
usbPolicy: machine.usbPolicy ?? null,
usbPolicyStatus: machine.usbPolicyStatus ?? null,
usbPolicyError: machine.usbPolicyError ?? null,
}
})
)

View file

@ -801,6 +801,7 @@ export default defineSchema({
dataDevolucao: v.optional(v.number()),
status: v.string(),
observacoes: v.optional(v.string()),
observacoesDevolucao: v.optional(v.string()),
multaDiaria: v.optional(v.number()),
multaCalculada: v.optional(v.number()),
createdBy: v.id("users"),

View file

@ -249,3 +249,54 @@ export const bulkSetUsbPolicy = mutation({
return { results, total: args.machineIds.length, successful: results.filter((r) => r.success).length }
},
})
/**
* Cleanup de policies USB pendentes por mais de 1 hora.
* Marca como FAILED com mensagem de timeout.
*/
export const cleanupStalePendingPolicies = mutation({
args: {
staleThresholdMs: v.optional(v.number()),
},
handler: async (ctx, args) => {
const thresholdMs = args.staleThresholdMs ?? 3600000 // 1 hora por padrao
const now = Date.now()
const cutoff = now - thresholdMs
// Buscar maquinas com status PENDING e appliedAt antigo
const allMachines = await ctx.db.query("machines").collect()
const staleMachines = allMachines.filter(
(m) =>
m.usbPolicyStatus === "PENDING" &&
m.usbPolicyAppliedAt !== undefined &&
m.usbPolicyAppliedAt < cutoff
)
let cleaned = 0
for (const machine of staleMachines) {
await ctx.db.patch(machine._id, {
usbPolicyStatus: "FAILED",
usbPolicyError: "Timeout: Agent nao reportou status apos 1 hora. Verifique se o agent esta ativo.",
updatedAt: now,
})
// Atualizar evento correspondente
const latestEvent = await ctx.db
.query("usbPolicyEvents")
.withIndex("by_machine_created", (q) => q.eq("machineId", machine._id))
.order("desc")
.first()
if (latestEvent && latestEvent.status === "PENDING") {
await ctx.db.patch(latestEvent._id, {
status: "FAILED",
error: "Timeout automatico",
})
}
cleaned++
}
return { cleaned, checked: allMachines.length }
},
})