1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
|
/**
* interactions.schema.ts
*
* Schemas Zod para o módulo de interações:
* - Atualização de status de um ticket
* - Atribuição de agente
* - Adição de comentários / notas internas
*/
import { z } from 'zod';
import { TICKET_STATUSES, TICKET_PRIORITIES } from '@/db/schema.js';
// ─── Parâmetros de rota compartilhados ───────────────────────────────────────
export const ticketIdParamsSchema = z.object({
ticketId: z.coerce.number().int().positive(),
});
export const commentIdParamsSchema = z.object({
ticketId: z.coerce.number().int().positive(),
commentId: z.coerce.number().int().positive(),
});
export type TicketIdParams = z.infer<typeof ticketIdParamsSchema>;
export type CommentIdParams = z.infer<typeof commentIdParamsSchema>;
// ─── Atualização de status ────────────────────────────────────────────────────
export const updateTicketStatusBodySchema = z.object({
status: z.enum(TICKET_STATUSES, {
errorMap: () => ({
message: `Status deve ser um de: ${TICKET_STATUSES.join(', ')}`,
}),
}),
// Motivo da mudança — obrigatório ao fechar ou resolver um chamado
reason: z.string().trim().max(500).optional(),
});
export type UpdateTicketStatusBody = z.infer<typeof updateTicketStatusBodySchema>;
// ─── Atribuição de agente ─────────────────────────────────────────────────────
export const assignTicketBodySchema = z.object({
assigneeId: z
.number({ required_error: 'assigneeId é obrigatório' })
.int()
.positive(),
});
export type AssignTicketBody = z.infer<typeof assignTicketBodySchema>;
// ─── Atualização de prioridade ────────────────────────────────────────────────
export const updateTicketPriorityBodySchema = z.object({
priority: z.enum(TICKET_PRIORITIES, {
errorMap: () => ({
message: `Prioridade deve ser uma de: ${TICKET_PRIORITIES.join(', ')}`,
}),
}),
});
export type UpdateTicketPriorityBody = z.infer<typeof updateTicketPriorityBodySchema>;
// ─── Adição de comentário / nota interna ─────────────────────────────────────
export const addCommentBodySchema = z.object({
content: z
.string({ required_error: 'Conteúdo é obrigatório' })
.min(1, 'Comentário não pode ser vazio')
.max(5000, 'Comentário muito longo')
.trim(),
/**
* true → nota interna (só agentes/admins veem)
* false → resposta pública (solicitante recebe notificação)
*/
isInternal: z.boolean().default(false),
// Em produção viria do JWT; por ora, aceito no body
authorId: z
.number({ required_error: 'authorId é obrigatório' })
.int()
.positive(),
});
export type AddCommentBody = z.infer<typeof addCommentBodySchema>;
// ─── Shape de resposta dos comentários ───────────────────────────────────────
export const commentResponseSchema = z.object({
id: z.number(),
content: z.string(),
isInternal: z.boolean(),
ticketId: z.number(),
authorId: z.number(),
createdAt: z.string(),
updatedAt: z.string(),
});
export type CommentResponse = z.infer<typeof commentResponseSchema>;
|