feat: núcleo de tickets com Convex (CRUD, play, comentários com anexos) + auth placeholder; docs em AGENTS.md; toasts e updates otimistas; mapeadores Zod; refinos PT-BR e layout do painel de detalhes
This commit is contained in:
parent
2230590e57
commit
27b103cb46
97 changed files with 15117 additions and 15715 deletions
103
web/src/lib/mappers/ticket.ts
Normal file
103
web/src/lib/mappers/ticket.ts
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
import { z } from "zod";
|
||||
import {
|
||||
ticketSchema,
|
||||
ticketWithDetailsSchema,
|
||||
ticketEventSchema,
|
||||
ticketCommentSchema,
|
||||
userSummarySchema,
|
||||
} from "@/lib/schemas/ticket";
|
||||
|
||||
// Server shapes: datas como number (epoch ms) e alguns nullables
|
||||
const serverUserSchema = userSummarySchema;
|
||||
|
||||
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(),
|
||||
});
|
||||
|
||||
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 serverTicketWithDetailsSchema = serverTicketSchema.extend({
|
||||
description: z.string().optional().nullable(),
|
||||
customFields: z.record(z.any()).default({}).optional(),
|
||||
timeline: z.array(serverEventSchema),
|
||||
comments: z.array(serverCommentSchema),
|
||||
});
|
||||
|
||||
export function mapTicketFromServer(input: unknown) {
|
||||
const s = serverTicketSchema.parse(input);
|
||||
const ui = {
|
||||
...s,
|
||||
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,
|
||||
};
|
||||
return ticketSchema.parse(ui);
|
||||
}
|
||||
|
||||
export function mapTicketsFromServerList(arr: unknown[]) {
|
||||
return arr.map(mapTicketFromServer);
|
||||
}
|
||||
|
||||
export function mapTicketWithDetailsFromServer(input: unknown) {
|
||||
const s = serverTicketWithDetailsSchema.parse(input);
|
||||
const ui = {
|
||||
...s,
|
||||
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),
|
||||
})),
|
||||
};
|
||||
return ticketWithDetailsSchema.parse(ui);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue