fix: normalize queue labels

This commit is contained in:
esdrasrenan 2025-10-05 00:36:59 -03:00
parent dee31117d3
commit e833888a3a
4 changed files with 103 additions and 16 deletions

View file

@ -4,10 +4,23 @@ import { v } from "convex/values";
export const ensureDefaults = mutation({ export const ensureDefaults = mutation({
args: { tenantId: v.string() }, args: { tenantId: v.string() },
handler: async (ctx, { tenantId }) => { handler: async (ctx, { tenantId }) => {
const existing = await ctx.db let existing = await ctx.db
.query("queues") .query("queues")
.withIndex("by_tenant", (q) => q.eq("tenantId", tenantId)) .withIndex("by_tenant", (q) => q.eq("tenantId", tenantId))
.collect(); .collect();
existing = await Promise.all(
existing.map(async (queue) => {
if (queue.name === "Suporte N1" || queue.slug === "suporte-n1") {
await ctx.db.patch(queue._id, { name: "Chamados", slug: "chamados" });
return (await ctx.db.get(queue._id)) ?? queue;
}
if (queue.name === "Suporte N2" || queue.slug === "suporte-n2") {
await ctx.db.patch(queue._id, { name: "Laboratório", slug: "laboratorio" });
return (await ctx.db.get(queue._id)) ?? queue;
}
return queue;
})
);
if (existing.length === 0) { if (existing.length === 0) {
const queues = [ const queues = [
{ name: "Chamados", slug: "chamados" }, { name: "Chamados", slug: "chamados" },

View file

@ -1,6 +1,20 @@
import { query } from "./_generated/server"; import { query } from "./_generated/server";
import { v } from "convex/values"; import { v } from "convex/values";
const QUEUE_RENAME_LOOKUP: Record<string, string> = {
"Suporte N1": "Chamados",
"suporte-n1": "Chamados",
"Suporte N2": "Laboratório",
"suporte-n2": "Laboratório",
};
function renameQueueString(value: string) {
const direct = QUEUE_RENAME_LOOKUP[value];
if (direct) return direct;
const normalizedKey = value.replace(/\s+/g, "-").toLowerCase();
return QUEUE_RENAME_LOOKUP[normalizedKey] ?? value;
}
export const summary = query({ export const summary = query({
args: { tenantId: v.string() }, args: { tenantId: v.string() },
handler: async (ctx, { tenantId }) => { handler: async (ctx, { tenantId }) => {
@ -15,7 +29,7 @@ export const summary = query({
const waiting = pending.filter((t) => t.status === "PENDING" || t.status === "ON_HOLD").length; const waiting = pending.filter((t) => t.status === "PENDING" || t.status === "ON_HOLD").length;
const open = pending.filter((t) => t.status !== "RESOLVED" && t.status !== "CLOSED").length; const open = pending.filter((t) => t.status !== "RESOLVED" && t.status !== "CLOSED").length;
const breached = 0; // Placeholder, SLAs later const breached = 0; // Placeholder, SLAs later
return { id: qItem._id, name: qItem.name, pending: open, waiting, breached }; return { id: qItem._id, name: renameQueueString(qItem.name), pending: open, waiting, breached };
}) })
); );
return result; return result;

View file

@ -9,7 +9,7 @@ export const seedDemo = mutation({
.query("queues") .query("queues")
.withIndex("by_tenant", (q) => q.eq("tenantId", tenantId)) .withIndex("by_tenant", (q) => q.eq("tenantId", tenantId))
.collect(); .collect();
const queues = existingQueues.length let queues = existingQueues.length
? existingQueues ? existingQueues
: await Promise.all( : await Promise.all(
[ [
@ -19,6 +19,21 @@ export const seedDemo = mutation({
).then((ids) => Promise.all(ids.map((id) => ctx.db.get(id)))) ).then((ids) => Promise.all(ids.map((id) => ctx.db.get(id))))
; ;
queues = await Promise.all(
queues.map(async (queue) => {
if (!queue) return queue;
if (queue.name === "Suporte N1" || queue.slug === "suporte-n1") {
await ctx.db.patch(queue._id, { name: "Chamados", slug: "chamados" });
return (await ctx.db.get(queue._id)) ?? queue;
}
if (queue.name === "Suporte N2" || queue.slug === "suporte-n2") {
await ctx.db.patch(queue._id, { name: "Laboratório", slug: "laboratorio" });
return (await ctx.db.get(queue._id)) ?? queue;
}
return queue;
})
);
// Ensure users // Ensure users
async function ensureUser(name: string, email: string, role = "AGENT") { async function ensureUser(name: string, email: string, role = "AGENT") {
const found = await ctx.db const found = await ctx.db

View file

@ -4,6 +4,39 @@ import { Id, type Doc } from "./_generated/dataModel";
const PRIORITY_ORDER = ["URGENT", "HIGH", "MEDIUM", "LOW"] as const; const PRIORITY_ORDER = ["URGENT", "HIGH", "MEDIUM", "LOW"] as const;
const QUEUE_RENAME_LOOKUP: Record<string, string> = {
"Suporte N1": "Chamados",
"suporte-n1": "Chamados",
"Suporte N2": "Laboratório",
"suporte-n2": "Laboratório",
};
function renameQueueString(value?: string | null): string | null {
if (!value) return value ?? null;
const direct = QUEUE_RENAME_LOOKUP[value];
if (direct) return direct;
const normalizedKey = value.replace(/\s+/g, "-").toLowerCase();
return QUEUE_RENAME_LOOKUP[normalizedKey] ?? value;
}
function normalizeQueueName(queue?: Doc<"queues"> | null): string | null {
if (!queue) return null;
const normalized = renameQueueString(queue.name);
if (normalized && normalized !== queue.name) {
return normalized;
}
if (queue.slug) {
const fromSlug = renameQueueString(queue.slug);
if (fromSlug) return fromSlug;
}
return normalized ?? queue.name;
}
function normalizeTeams(teams?: string[] | null): string[] {
if (!teams) return [];
return teams.map((team) => renameQueueString(team) ?? team);
}
export const list = query({ export const list = query({
args: { args: {
tenantId: v.string(), tenantId: v.string(),
@ -55,6 +88,7 @@ export const list = query({
const requester = (await ctx.db.get(t.requesterId)) as Doc<"users"> | null; const requester = (await ctx.db.get(t.requesterId)) as Doc<"users"> | null;
const assignee = t.assigneeId ? ((await ctx.db.get(t.assigneeId)) as Doc<"users"> | null) : null; const assignee = t.assigneeId ? ((await ctx.db.get(t.assigneeId)) as Doc<"users"> | null) : null;
const queue = t.queueId ? ((await ctx.db.get(t.queueId)) as Doc<"queues"> | null) : null; const queue = t.queueId ? ((await ctx.db.get(t.queueId)) as Doc<"queues"> | null) : null;
const queueName = normalizeQueueName(queue);
const activeSession = t.activeSessionId ? await ctx.db.get(t.activeSessionId) : null; const activeSession = t.activeSessionId ? await ctx.db.get(t.activeSessionId) : null;
let categorySummary: { id: Id<"ticketCategories">; name: string } | null = null; let categorySummary: { id: Id<"ticketCategories">; name: string } | null = null;
let subcategorySummary: { id: Id<"ticketSubcategories">; name: string } | null = null; let subcategorySummary: { id: Id<"ticketSubcategories">; name: string } | null = null;
@ -85,13 +119,13 @@ export const list = query({
status: t.status, status: t.status,
priority: t.priority, priority: t.priority,
channel: t.channel, channel: t.channel,
queue: queue?.name ?? null, queue: queueName,
requester: requester && { requester: requester && {
id: requester._id, id: requester._id,
name: requester.name, name: requester.name,
email: requester.email, email: requester.email,
avatarUrl: requester.avatarUrl, avatarUrl: requester.avatarUrl,
teams: requester.teams ?? [], teams: normalizeTeams(requester.teams),
}, },
assignee: assignee assignee: assignee
? { ? {
@ -99,7 +133,7 @@ export const list = query({
name: assignee.name, name: assignee.name,
email: assignee.email, email: assignee.email,
avatarUrl: assignee.avatarUrl, avatarUrl: assignee.avatarUrl,
teams: assignee.teams ?? [], teams: normalizeTeams(assignee.teams),
} }
: null, : null,
slaPolicy: null, slaPolicy: null,
@ -139,6 +173,7 @@ export const getById = query({
const requester = (await ctx.db.get(t.requesterId)) as Doc<"users"> | null; const requester = (await ctx.db.get(t.requesterId)) as Doc<"users"> | null;
const assignee = t.assigneeId ? ((await ctx.db.get(t.assigneeId)) as Doc<"users"> | null) : null; const assignee = t.assigneeId ? ((await ctx.db.get(t.assigneeId)) as Doc<"users"> | null) : null;
const queue = t.queueId ? ((await ctx.db.get(t.queueId)) as Doc<"queues"> | null) : null; const queue = t.queueId ? ((await ctx.db.get(t.queueId)) as Doc<"queues"> | null) : null;
const queueName = normalizeQueueName(queue);
const category = t.categoryId ? await ctx.db.get(t.categoryId) : null; const category = t.categoryId ? await ctx.db.get(t.categoryId) : null;
const subcategory = t.subcategoryId ? await ctx.db.get(t.subcategoryId) : null; const subcategory = t.subcategoryId ? await ctx.db.get(t.subcategoryId) : null;
const comments = await ctx.db const comments = await ctx.db
@ -191,13 +226,13 @@ export const getById = query({
status: t.status, status: t.status,
priority: t.priority, priority: t.priority,
channel: t.channel, channel: t.channel,
queue: queue?.name ?? null, queue: queueName,
requester: requester && { requester: requester && {
id: requester._id, id: requester._id,
name: requester.name, name: requester.name,
email: requester.email, email: requester.email,
avatarUrl: requester.avatarUrl, avatarUrl: requester.avatarUrl,
teams: requester.teams ?? [], teams: normalizeTeams(requester.teams),
}, },
assignee: assignee assignee: assignee
? { ? {
@ -205,7 +240,7 @@ export const getById = query({
name: assignee.name, name: assignee.name,
email: assignee.email, email: assignee.email,
avatarUrl: assignee.avatarUrl, avatarUrl: assignee.avatarUrl,
teams: assignee.teams ?? [], teams: normalizeTeams(assignee.teams),
} }
: null, : null,
slaPolicy: null, slaPolicy: null,
@ -242,12 +277,21 @@ export const getById = query({
}, },
description: undefined, description: undefined,
customFields: {}, customFields: {},
timeline: timeline.map((ev) => ({ timeline: timeline.map((ev) => {
id: ev._id, let payload = ev.payload;
type: ev.type, if (ev.type === "QUEUE_CHANGED" && payload && typeof payload === "object" && "queueName" in payload) {
payload: ev.payload, const normalized = renameQueueString((payload as { queueName?: string }).queueName ?? null);
createdAt: ev.createdAt, if (normalized && normalized !== (payload as { queueName?: string }).queueName) {
})), payload = { ...payload, queueName: normalized };
}
}
return {
id: ev._id,
type: ev.type,
payload,
createdAt: ev.createdAt,
};
}),
comments: commentsHydrated, comments: commentsHydrated,
}; };
}, },
@ -451,10 +495,11 @@ export const changeQueue = mutation({
const now = Date.now(); const now = Date.now();
await ctx.db.patch(ticketId, { queueId, updatedAt: now }); await ctx.db.patch(ticketId, { queueId, updatedAt: now });
const queue = (await ctx.db.get(queueId)) as Doc<"queues"> | null; const queue = (await ctx.db.get(queueId)) as Doc<"queues"> | null;
const queueName = normalizeQueueName(queue);
await ctx.db.insert("ticketEvents", { await ctx.db.insert("ticketEvents", {
ticketId, ticketId,
type: "QUEUE_CHANGED", type: "QUEUE_CHANGED",
payload: { queueId, queueName: queue?.name, actorId }, payload: { queueId, queueName, actorId },
createdAt: now, createdAt: now,
}); });
}, },