fix: normalize queue labels
This commit is contained in:
parent
dee31117d3
commit
e833888a3a
4 changed files with 103 additions and 16 deletions
|
|
@ -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" },
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue