feat: add ticket category model and align ticket ui\n\nCo-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
This commit is contained in:
parent
55511f3a0e
commit
fab1cbe476
17 changed files with 1121 additions and 42 deletions
|
|
@ -47,6 +47,8 @@ export const list = query({
|
|||
);
|
||||
}
|
||||
const limited = args.limit ? filtered.slice(0, args.limit) : filtered;
|
||||
const categoryCache = new Map<string, Doc<"ticketCategories"> | null>();
|
||||
const subcategoryCache = new Map<string, Doc<"ticketSubcategories"> | null>();
|
||||
// hydrate requester and assignee
|
||||
const result = await Promise.all(
|
||||
limited.map(async (t) => {
|
||||
|
|
@ -54,6 +56,26 @@ export const list = query({
|
|||
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 activeSession = t.activeSessionId ? await ctx.db.get(t.activeSessionId) : null;
|
||||
let categorySummary: { id: Id<"ticketCategories">; name: string } | null = null;
|
||||
let subcategorySummary: { id: Id<"ticketSubcategories">; name: string } | null = null;
|
||||
if (t.categoryId) {
|
||||
if (!categoryCache.has(t.categoryId)) {
|
||||
categoryCache.set(t.categoryId, await ctx.db.get(t.categoryId));
|
||||
}
|
||||
const category = categoryCache.get(t.categoryId);
|
||||
if (category) {
|
||||
categorySummary = { id: category._id, name: category.name };
|
||||
}
|
||||
}
|
||||
if (t.subcategoryId) {
|
||||
if (!subcategoryCache.has(t.subcategoryId)) {
|
||||
subcategoryCache.set(t.subcategoryId, await ctx.db.get(t.subcategoryId));
|
||||
}
|
||||
const subcategory = subcategoryCache.get(t.subcategoryId);
|
||||
if (subcategory) {
|
||||
subcategorySummary = { id: subcategory._id, name: subcategory.name };
|
||||
}
|
||||
}
|
||||
return {
|
||||
id: t._id,
|
||||
reference: t.reference,
|
||||
|
|
@ -89,6 +111,8 @@ export const list = query({
|
|||
tags: t.tags ?? [],
|
||||
lastTimelineEntry: null,
|
||||
metrics: null,
|
||||
category: categorySummary,
|
||||
subcategory: subcategorySummary,
|
||||
workSummary: {
|
||||
totalWorkedMs: t.totalWorkedMs ?? 0,
|
||||
activeSession: activeSession
|
||||
|
|
@ -115,6 +139,8 @@ export const getById = query({
|
|||
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 queue = t.queueId ? ((await ctx.db.get(t.queueId)) as Doc<"queues"> | null) : null;
|
||||
const category = t.categoryId ? await ctx.db.get(t.categoryId) : null;
|
||||
const subcategory = t.subcategoryId ? await ctx.db.get(t.subcategoryId) : null;
|
||||
const comments = await ctx.db
|
||||
.query("ticketComments")
|
||||
.withIndex("by_ticket", (q) => q.eq("ticketId", id))
|
||||
|
|
@ -191,6 +217,19 @@ export const getById = query({
|
|||
tags: t.tags ?? [],
|
||||
lastTimelineEntry: null,
|
||||
metrics: null,
|
||||
category: category
|
||||
? {
|
||||
id: category._id,
|
||||
name: category.name,
|
||||
}
|
||||
: null,
|
||||
subcategory: subcategory
|
||||
? {
|
||||
id: subcategory._id,
|
||||
name: subcategory.name,
|
||||
categoryId: subcategory.categoryId,
|
||||
}
|
||||
: null,
|
||||
workSummary: {
|
||||
totalWorkedMs: t.totalWorkedMs ?? 0,
|
||||
activeSession: activeSession
|
||||
|
|
@ -223,12 +262,22 @@ export const create = mutation({
|
|||
channel: v.string(),
|
||||
queueId: v.optional(v.id("queues")),
|
||||
requesterId: v.id("users"),
|
||||
categoryId: v.id("ticketCategories"),
|
||||
subcategoryId: v.id("ticketSubcategories"),
|
||||
},
|
||||
handler: async (ctx, args) => {
|
||||
const subject = args.subject.trim();
|
||||
if (subject.length < 3) {
|
||||
throw new ConvexError("Informe um assunto com pelo menos 3 caracteres");
|
||||
}
|
||||
const category = await ctx.db.get(args.categoryId);
|
||||
if (!category || category.tenantId !== args.tenantId) {
|
||||
throw new ConvexError("Categoria inválida");
|
||||
}
|
||||
const subcategory = await ctx.db.get(args.subcategoryId);
|
||||
if (!subcategory || subcategory.categoryId !== args.categoryId || subcategory.tenantId !== args.tenantId) {
|
||||
throw new ConvexError("Subcategoria inválida");
|
||||
}
|
||||
// compute next reference (simple monotonic counter per tenant)
|
||||
const existing = await ctx.db
|
||||
.query("tickets")
|
||||
|
|
@ -246,6 +295,8 @@ export const create = mutation({
|
|||
priority: args.priority,
|
||||
channel: args.channel,
|
||||
queueId: args.queueId,
|
||||
categoryId: args.categoryId,
|
||||
subcategoryId: args.subcategoryId,
|
||||
requesterId: args.requesterId,
|
||||
assigneeId: undefined,
|
||||
working: false,
|
||||
|
|
@ -409,6 +460,58 @@ export const changeQueue = mutation({
|
|||
},
|
||||
});
|
||||
|
||||
export const updateCategories = mutation({
|
||||
args: {
|
||||
ticketId: v.id("tickets"),
|
||||
categoryId: v.id("ticketCategories"),
|
||||
subcategoryId: v.id("ticketSubcategories"),
|
||||
actorId: v.id("users"),
|
||||
},
|
||||
handler: async (ctx, { ticketId, categoryId, subcategoryId, actorId }) => {
|
||||
const ticket = await ctx.db.get(ticketId)
|
||||
if (!ticket) {
|
||||
throw new ConvexError("Ticket não encontrado")
|
||||
}
|
||||
const category = await ctx.db.get(categoryId)
|
||||
if (!category || category.tenantId !== ticket.tenantId) {
|
||||
throw new ConvexError("Categoria inválida")
|
||||
}
|
||||
const subcategory = await ctx.db.get(subcategoryId)
|
||||
if (!subcategory || subcategory.categoryId !== categoryId || subcategory.tenantId !== ticket.tenantId) {
|
||||
throw new ConvexError("Subcategoria inválida")
|
||||
}
|
||||
|
||||
if (ticket.categoryId === categoryId && ticket.subcategoryId === subcategoryId) {
|
||||
return { status: "unchanged" }
|
||||
}
|
||||
|
||||
const now = Date.now()
|
||||
await ctx.db.patch(ticketId, {
|
||||
categoryId,
|
||||
subcategoryId,
|
||||
updatedAt: now,
|
||||
})
|
||||
|
||||
const actor = (await ctx.db.get(actorId)) as Doc<"users"> | null
|
||||
await ctx.db.insert("ticketEvents", {
|
||||
ticketId,
|
||||
type: "CATEGORY_CHANGED",
|
||||
payload: {
|
||||
categoryId,
|
||||
categoryName: category.name,
|
||||
subcategoryId,
|
||||
subcategoryName: subcategory.name,
|
||||
actorId,
|
||||
actorName: actor?.name,
|
||||
actorAvatar: actor?.avatarUrl,
|
||||
},
|
||||
createdAt: now,
|
||||
})
|
||||
|
||||
return { status: "updated" }
|
||||
},
|
||||
})
|
||||
|
||||
export const workSummary = query({
|
||||
args: { ticketId: v.id("tickets") },
|
||||
handler: async (ctx, { ticketId }) => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue