chore: reorganize project structure and ensure default queues

This commit is contained in:
Esdras Renan 2025-10-06 22:59:35 -03:00
parent 854887f499
commit 1cccb852a5
201 changed files with 417 additions and 838 deletions

View file

@ -1,184 +0,0 @@
import { z } from "zod";
import { ticketSchema, ticketWithDetailsSchema } from "@/lib/schemas/ticket";
// Server shapes: datas como number (epoch ms) e alguns nullables
// Relaxamos email/urls no shape do servidor para evitar que payloads parciais quebrem o app.
const serverUserSchema = z.object({
id: z.string(),
name: z.string(),
email: z.string().optional(),
avatarUrl: z.string().optional(),
teams: z.array(z.string()).optional(),
});
const serverTicketSchema = z.object({
id: z.string(),
reference: z.number(),
tenantId: z.string(),
subject: z.string(),
summary: z.string().optional().nullable(),
status: z.string(),
priority: z.string(),
channel: z.string(),
queue: z.string().nullable(),
requester: serverUserSchema,
assignee: serverUserSchema.nullable(),
slaPolicy: z.any().nullable().optional(),
dueAt: z.number().nullable().optional(),
firstResponseAt: z.number().nullable().optional(),
resolvedAt: z.number().nullable().optional(),
updatedAt: z.number(),
createdAt: z.number(),
tags: z.array(z.string()).default([]).optional(),
lastTimelineEntry: z.string().nullable().optional(),
metrics: z.any().nullable().optional(),
category: z
.object({
id: z.string(),
name: z.string(),
})
.nullable()
.optional(),
subcategory: z
.object({
id: z.string(),
name: z.string(),
categoryId: z.string().optional(),
})
.nullable()
.optional(),
workSummary: z
.object({
totalWorkedMs: z.number(),
activeSession: z
.object({
id: z.string(),
agentId: z.string(),
startedAt: z.number(),
})
.nullable(),
})
.nullable()
.optional(),
});
const serverAttachmentSchema = z.object({
id: z.any(),
name: z.string(),
size: z.number().optional(),
url: z.string().url().optional(),
});
const serverCommentSchema = z.object({
id: z.string(),
author: serverUserSchema,
visibility: z.string(),
body: z.string(),
attachments: z.array(serverAttachmentSchema).default([]).optional(),
createdAt: z.number(),
updatedAt: z.number(),
});
const serverEventSchema = z.object({
id: z.string(),
type: z.string(),
payload: z.any().optional(),
createdAt: z.number(),
});
const serverCustomFieldValueSchema = z.object({
label: z.string(),
type: z.string(),
value: z.any().optional(),
displayValue: z.string().optional(),
});
const serverTicketWithDetailsSchema = serverTicketSchema.extend({
description: z.string().optional().nullable(),
customFields: z.record(z.string(), serverCustomFieldValueSchema).optional(),
timeline: z.array(serverEventSchema),
comments: z.array(serverCommentSchema),
});
export function mapTicketFromServer(input: unknown) {
const s = serverTicketSchema.parse(input);
const ui = {
...s,
category: s.category ?? undefined,
subcategory: s.subcategory ?? undefined,
lastTimelineEntry: s.lastTimelineEntry ?? undefined,
updatedAt: new Date(s.updatedAt),
createdAt: new Date(s.createdAt),
dueAt: s.dueAt ? new Date(s.dueAt) : null,
firstResponseAt: s.firstResponseAt ? new Date(s.firstResponseAt) : null,
resolvedAt: s.resolvedAt ? new Date(s.resolvedAt) : null,
workSummary: s.workSummary
? {
totalWorkedMs: s.workSummary.totalWorkedMs,
activeSession: s.workSummary.activeSession
? {
...s.workSummary.activeSession,
startedAt: new Date(s.workSummary.activeSession.startedAt),
}
: null,
}
: undefined,
};
return ui as unknown as z.infer<typeof ticketSchema>;
}
export function mapTicketsFromServerList(arr: unknown[]) {
return arr.map(mapTicketFromServer);
}
export function mapTicketWithDetailsFromServer(input: unknown) {
const s = serverTicketWithDetailsSchema.parse(input);
const customFields = Object.entries(s.customFields ?? {}).reduce<
Record<string, { label: string; type: string; value?: unknown; displayValue?: string }>
>(
(acc, [key, value]) => {
let parsedValue: unknown = value.value;
if (value.type === "date" && typeof value.value === "number") {
parsedValue = new Date(value.value);
}
acc[key] = {
label: value.label,
type: value.type,
value: parsedValue,
displayValue: value.displayValue,
};
return acc;
},
{}
);
const ui = {
...s,
customFields,
category: s.category ?? undefined,
subcategory: s.subcategory ?? undefined,
lastTimelineEntry: s.lastTimelineEntry ?? undefined,
updatedAt: new Date(s.updatedAt),
createdAt: new Date(s.createdAt),
dueAt: s.dueAt ? new Date(s.dueAt) : null,
firstResponseAt: s.firstResponseAt ? new Date(s.firstResponseAt) : null,
resolvedAt: s.resolvedAt ? new Date(s.resolvedAt) : null,
timeline: s.timeline.map((e) => ({ ...e, createdAt: new Date(e.createdAt) })),
comments: s.comments.map((c) => ({
...c,
createdAt: new Date(c.createdAt),
updatedAt: new Date(c.updatedAt),
})),
workSummary: s.workSummary
? {
totalWorkedMs: s.workSummary.totalWorkedMs,
activeSession: s.workSummary.activeSession
? {
...s.workSummary.activeSession,
startedAt: new Date(s.workSummary.activeSession.startedAt),
}
: null,
}
: undefined,
};
return ui as unknown as z.infer<typeof ticketWithDetailsSchema>;
}