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
|
|
@ -1,124 +1,124 @@
|
|||
import { z } from "zod"
|
||||
|
||||
export const ticketStatusSchema = z.enum([
|
||||
"NEW",
|
||||
"OPEN",
|
||||
"PENDING",
|
||||
"ON_HOLD",
|
||||
"RESOLVED",
|
||||
"CLOSED",
|
||||
])
|
||||
|
||||
export type TicketStatus = z.infer<typeof ticketStatusSchema>
|
||||
|
||||
export const ticketPrioritySchema = z.enum(["LOW", "MEDIUM", "HIGH", "URGENT"])
|
||||
export type TicketPriority = z.infer<typeof ticketPrioritySchema>
|
||||
|
||||
export const ticketChannelSchema = z.enum([
|
||||
"EMAIL",
|
||||
"WHATSAPP",
|
||||
"CHAT",
|
||||
"PHONE",
|
||||
"API",
|
||||
"MANUAL",
|
||||
])
|
||||
export type TicketChannel = z.infer<typeof ticketChannelSchema>
|
||||
|
||||
export const commentVisibilitySchema = z.enum(["PUBLIC", "INTERNAL"])
|
||||
export type CommentVisibility = z.infer<typeof commentVisibilitySchema>
|
||||
|
||||
export const userSummarySchema = z.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
email: z.string().email(),
|
||||
avatarUrl: z.string().url().optional(),
|
||||
teams: z.array(z.string()).default([]),
|
||||
})
|
||||
export type UserSummary = z.infer<typeof userSummarySchema>
|
||||
|
||||
export const ticketCommentSchema = z.object({
|
||||
id: z.string(),
|
||||
author: userSummarySchema,
|
||||
visibility: commentVisibilitySchema,
|
||||
body: z.string(),
|
||||
attachments: z
|
||||
.array(
|
||||
z.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
size: z.number().optional(),
|
||||
url: z.string().url().optional(),
|
||||
})
|
||||
)
|
||||
.default([]),
|
||||
createdAt: z.coerce.date(),
|
||||
updatedAt: z.coerce.date(),
|
||||
})
|
||||
export type TicketComment = z.infer<typeof ticketCommentSchema>
|
||||
|
||||
export const ticketEventSchema = z.object({
|
||||
id: z.string(),
|
||||
type: z.string(),
|
||||
payload: z.record(z.any()).optional(),
|
||||
createdAt: z.coerce.date(),
|
||||
})
|
||||
export type TicketEvent = z.infer<typeof ticketEventSchema>
|
||||
|
||||
export const ticketSchema = z.object({
|
||||
id: z.string(),
|
||||
reference: z.number(),
|
||||
tenantId: z.string(),
|
||||
subject: z.string(),
|
||||
summary: z.string().optional(),
|
||||
status: ticketStatusSchema,
|
||||
priority: ticketPrioritySchema,
|
||||
channel: ticketChannelSchema,
|
||||
queue: z.string().nullable(),
|
||||
requester: userSummarySchema,
|
||||
assignee: userSummarySchema.nullable(),
|
||||
slaPolicy: z
|
||||
.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
targetMinutesToFirstResponse: z.number().nullable(),
|
||||
targetMinutesToResolution: z.number().nullable(),
|
||||
})
|
||||
.nullable(),
|
||||
dueAt: z.coerce.date().nullable(),
|
||||
firstResponseAt: z.coerce.date().nullable(),
|
||||
resolvedAt: z.coerce.date().nullable(),
|
||||
updatedAt: z.coerce.date(),
|
||||
createdAt: z.coerce.date(),
|
||||
tags: z.array(z.string()).default([]),
|
||||
lastTimelineEntry: z.string().optional(),
|
||||
metrics: z
|
||||
.object({
|
||||
timeWaitingMinutes: z.number().nullable(),
|
||||
timeOpenedMinutes: z.number().nullable(),
|
||||
})
|
||||
.nullable(),
|
||||
})
|
||||
export type Ticket = z.infer<typeof ticketSchema>
|
||||
|
||||
export const ticketWithDetailsSchema = ticketSchema.extend({
|
||||
description: z.string().optional(),
|
||||
customFields: z.record(z.any()).default({}),
|
||||
timeline: z.array(ticketEventSchema),
|
||||
comments: z.array(ticketCommentSchema),
|
||||
})
|
||||
export type TicketWithDetails = z.infer<typeof ticketWithDetailsSchema>
|
||||
|
||||
export const ticketQueueSummarySchema = z.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
pending: z.number(),
|
||||
waiting: z.number(),
|
||||
breached: z.number(),
|
||||
})
|
||||
export type TicketQueueSummary = z.infer<typeof ticketQueueSummarySchema>
|
||||
|
||||
export const ticketPlayContextSchema = z.object({
|
||||
queue: ticketQueueSummarySchema,
|
||||
nextTicket: ticketSchema.nullable(),
|
||||
})
|
||||
export type TicketPlayContext = z.infer<typeof ticketPlayContextSchema>
|
||||
import { z } from "zod"
|
||||
|
||||
export const ticketStatusSchema = z.enum([
|
||||
"NEW",
|
||||
"OPEN",
|
||||
"PENDING",
|
||||
"ON_HOLD",
|
||||
"RESOLVED",
|
||||
"CLOSED",
|
||||
])
|
||||
|
||||
export type TicketStatus = z.infer<typeof ticketStatusSchema>
|
||||
|
||||
export const ticketPrioritySchema = z.enum(["LOW", "MEDIUM", "HIGH", "URGENT"])
|
||||
export type TicketPriority = z.infer<typeof ticketPrioritySchema>
|
||||
|
||||
export const ticketChannelSchema = z.enum([
|
||||
"EMAIL",
|
||||
"WHATSAPP",
|
||||
"CHAT",
|
||||
"PHONE",
|
||||
"API",
|
||||
"MANUAL",
|
||||
])
|
||||
export type TicketChannel = z.infer<typeof ticketChannelSchema>
|
||||
|
||||
export const commentVisibilitySchema = z.enum(["PUBLIC", "INTERNAL"])
|
||||
export type CommentVisibility = z.infer<typeof commentVisibilitySchema>
|
||||
|
||||
export const userSummarySchema = z.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
email: z.string().email(),
|
||||
avatarUrl: z.string().url().optional(),
|
||||
teams: z.array(z.string()).default([]),
|
||||
})
|
||||
export type UserSummary = z.infer<typeof userSummarySchema>
|
||||
|
||||
export const ticketCommentSchema = z.object({
|
||||
id: z.string(),
|
||||
author: userSummarySchema,
|
||||
visibility: commentVisibilitySchema,
|
||||
body: z.string(),
|
||||
attachments: z
|
||||
.array(
|
||||
z.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
size: z.number().optional(),
|
||||
url: z.string().url().optional(),
|
||||
})
|
||||
)
|
||||
.default([]),
|
||||
createdAt: z.coerce.date(),
|
||||
updatedAt: z.coerce.date(),
|
||||
})
|
||||
export type TicketComment = z.infer<typeof ticketCommentSchema>
|
||||
|
||||
export const ticketEventSchema = z.object({
|
||||
id: z.string(),
|
||||
type: z.string(),
|
||||
payload: z.record(z.any()).optional(),
|
||||
createdAt: z.coerce.date(),
|
||||
})
|
||||
export type TicketEvent = z.infer<typeof ticketEventSchema>
|
||||
|
||||
export const ticketSchema = z.object({
|
||||
id: z.string(),
|
||||
reference: z.number(),
|
||||
tenantId: z.string(),
|
||||
subject: z.string(),
|
||||
summary: z.string().optional(),
|
||||
status: ticketStatusSchema,
|
||||
priority: ticketPrioritySchema,
|
||||
channel: ticketChannelSchema,
|
||||
queue: z.string().nullable(),
|
||||
requester: userSummarySchema,
|
||||
assignee: userSummarySchema.nullable(),
|
||||
slaPolicy: z
|
||||
.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
targetMinutesToFirstResponse: z.number().nullable(),
|
||||
targetMinutesToResolution: z.number().nullable(),
|
||||
})
|
||||
.nullable(),
|
||||
dueAt: z.coerce.date().nullable(),
|
||||
firstResponseAt: z.coerce.date().nullable(),
|
||||
resolvedAt: z.coerce.date().nullable(),
|
||||
updatedAt: z.coerce.date(),
|
||||
createdAt: z.coerce.date(),
|
||||
tags: z.array(z.string()).default([]),
|
||||
lastTimelineEntry: z.string().optional(),
|
||||
metrics: z
|
||||
.object({
|
||||
timeWaitingMinutes: z.number().nullable(),
|
||||
timeOpenedMinutes: z.number().nullable(),
|
||||
})
|
||||
.nullable(),
|
||||
})
|
||||
export type Ticket = z.infer<typeof ticketSchema>
|
||||
|
||||
export const ticketWithDetailsSchema = ticketSchema.extend({
|
||||
description: z.string().optional(),
|
||||
customFields: z.record(z.any()).default({}),
|
||||
timeline: z.array(ticketEventSchema),
|
||||
comments: z.array(ticketCommentSchema),
|
||||
})
|
||||
export type TicketWithDetails = z.infer<typeof ticketWithDetailsSchema>
|
||||
|
||||
export const ticketQueueSummarySchema = z.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
pending: z.number(),
|
||||
waiting: z.number(),
|
||||
breached: z.number(),
|
||||
})
|
||||
export type TicketQueueSummary = z.infer<typeof ticketQueueSummarySchema>
|
||||
|
||||
export const ticketPlayContextSchema = z.object({
|
||||
queue: ticketQueueSummarySchema,
|
||||
nextTicket: ticketSchema.nullable(),
|
||||
})
|
||||
export type TicketPlayContext = z.infer<typeof ticketPlayContextSchema>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue