aboutsummaryrefslogtreecommitdiff
path: root/api/src/modules/interactions/interactions.schema.ts
blob: 59eec29ff7e96afea1c2f909c73528b52eaf8cc1 (plain)
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>;