Atualiza portal e admin com bloqueio de máquinas desativadas
This commit is contained in:
parent
e5085962e9
commit
630110bf3a
31 changed files with 1756 additions and 244 deletions
|
|
@ -440,6 +440,7 @@ export const register = mutation({
|
|||
lastHeartbeatAt: now,
|
||||
updatedAt: now,
|
||||
status: "online",
|
||||
isActive: true,
|
||||
registeredBy: args.registeredBy ?? existing.registeredBy,
|
||||
persona: existing.persona,
|
||||
assignedUserId: existing.assignedUserId,
|
||||
|
|
@ -463,6 +464,7 @@ export const register = mutation({
|
|||
metadata: metadataPatch ? mergeMetadata(undefined, metadataPatch) : undefined,
|
||||
lastHeartbeatAt: now,
|
||||
status: "online",
|
||||
isActive: true,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
registeredBy: args.registeredBy,
|
||||
|
|
@ -716,6 +718,7 @@ export const resolveToken = mutation({
|
|||
status: machine.status,
|
||||
lastHeartbeatAt: machine.lastHeartbeatAt,
|
||||
metadata: machine.metadata,
|
||||
isActive: machine.isActive ?? true,
|
||||
},
|
||||
token: {
|
||||
expiresAt: token.expiresAt,
|
||||
|
|
@ -753,7 +756,9 @@ export const listByTenant = query({
|
|||
const staleThresholdMs = getStaleThresholdMs(offlineThresholdMs)
|
||||
const manualStatus = (machine.status ?? "").toLowerCase()
|
||||
let derivedStatus: string
|
||||
if (["maintenance", "blocked"].includes(manualStatus)) {
|
||||
if (machine.isActive === false) {
|
||||
derivedStatus = "deactivated"
|
||||
} else if (["maintenance", "blocked"].includes(manualStatus)) {
|
||||
derivedStatus = manualStatus
|
||||
} else if (machine.lastHeartbeatAt) {
|
||||
const age = now - machine.lastHeartbeatAt
|
||||
|
|
@ -810,6 +815,7 @@ export const listByTenant = query({
|
|||
assignedUserName: machine.assignedUserName ?? null,
|
||||
assignedUserRole: machine.assignedUserRole ?? null,
|
||||
status: derivedStatus,
|
||||
isActive: machine.isActive ?? true,
|
||||
lastHeartbeatAt: machine.lastHeartbeatAt ?? null,
|
||||
heartbeatAgeMs: machine.lastHeartbeatAt ? now - machine.lastHeartbeatAt : null,
|
||||
registeredBy: machine.registeredBy ?? null,
|
||||
|
|
@ -988,6 +994,7 @@ export const getContext = query({
|
|||
assignedUserRole: machine.assignedUserRole ?? null,
|
||||
metadata: machine.metadata ?? null,
|
||||
authEmail: machine.authEmail ?? null,
|
||||
isActive: machine.isActive ?? true,
|
||||
}
|
||||
},
|
||||
})
|
||||
|
|
@ -1069,6 +1076,37 @@ export const rename = mutation({
|
|||
},
|
||||
})
|
||||
|
||||
export const toggleActive = mutation({
|
||||
args: {
|
||||
machineId: v.id("machines"),
|
||||
actorId: v.id("users"),
|
||||
active: v.boolean(),
|
||||
},
|
||||
handler: async (ctx, { machineId, actorId, active }) => {
|
||||
const machine = await ctx.db.get(machineId)
|
||||
if (!machine) {
|
||||
throw new ConvexError("Máquina não encontrada")
|
||||
}
|
||||
|
||||
const actor = await ctx.db.get(actorId)
|
||||
if (!actor || actor.tenantId !== machine.tenantId) {
|
||||
throw new ConvexError("Acesso negado ao tenant da máquina")
|
||||
}
|
||||
const normalizedRole = (actor.role ?? "AGENT").toUpperCase()
|
||||
const STAFF = new Set(["ADMIN", "MANAGER", "AGENT"])
|
||||
if (!STAFF.has(normalizedRole)) {
|
||||
throw new ConvexError("Apenas equipe interna pode atualizar o status da máquina")
|
||||
}
|
||||
|
||||
await ctx.db.patch(machineId, {
|
||||
isActive: active,
|
||||
updatedAt: Date.now(),
|
||||
})
|
||||
|
||||
return { ok: true }
|
||||
},
|
||||
})
|
||||
|
||||
export const remove = mutation({
|
||||
args: {
|
||||
machineId: v.id("machines"),
|
||||
|
|
|
|||
|
|
@ -375,8 +375,20 @@ export const dashboardOverview = query({
|
|||
const awaitingTickets = tickets.filter((ticket) => OPEN_STATUSES.has(normalizeStatus(ticket.status)));
|
||||
const atRiskTickets = awaitingTickets.filter((ticket) => ticket.dueAt && ticket.dueAt < now);
|
||||
|
||||
const surveys = await collectCsatSurveys(ctx, tickets);
|
||||
const averageScore = average(surveys.map((item) => item.score));
|
||||
const resolvedLastWindow = tickets.filter(
|
||||
(ticket) => ticket.resolvedAt && ticket.resolvedAt >= lastWindowStart && ticket.resolvedAt < now
|
||||
);
|
||||
const resolvedPreviousWindow = tickets.filter(
|
||||
(ticket) =>
|
||||
ticket.resolvedAt &&
|
||||
ticket.resolvedAt >= previousWindowStart &&
|
||||
ticket.resolvedAt < lastWindowStart
|
||||
);
|
||||
const resolutionRate = tickets.length > 0 ? (resolvedLastWindow.length / tickets.length) * 100 : null;
|
||||
const resolutionDelta =
|
||||
resolvedPreviousWindow.length > 0
|
||||
? ((resolvedLastWindow.length - resolvedPreviousWindow.length) / resolvedPreviousWindow.length) * 100
|
||||
: null;
|
||||
|
||||
return {
|
||||
newTickets: {
|
||||
|
|
@ -394,9 +406,11 @@ export const dashboardOverview = query({
|
|||
total: awaitingTickets.length,
|
||||
atRisk: atRiskTickets.length,
|
||||
},
|
||||
csat: {
|
||||
averageScore,
|
||||
totalSurveys: surveys.length,
|
||||
resolution: {
|
||||
resolvedLast7d: resolvedLastWindow.length,
|
||||
previousResolved: resolvedPreviousWindow.length,
|
||||
rate: resolutionRate,
|
||||
deltaPercentage: resolutionDelta,
|
||||
},
|
||||
};
|
||||
},
|
||||
|
|
|
|||
|
|
@ -264,6 +264,7 @@ export default defineSchema({
|
|||
metadata: v.optional(v.any()),
|
||||
lastHeartbeatAt: v.optional(v.number()),
|
||||
status: v.optional(v.string()),
|
||||
isActive: v.optional(v.boolean()),
|
||||
createdAt: v.number(),
|
||||
updatedAt: v.number(),
|
||||
registeredBy: v.optional(v.string()),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue