aboutsummaryrefslogtreecommitdiff
path: root/api/src/modules/categories/categories.schema.ts
blob: 6e11d8c7fe500ab159f0ae78d3ab866b81cedabd (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
/**
 * categories.schema.ts
 * Schemas Zod para o CRUD de categorias.
 */

import { z } from 'zod';

// ─── Helper: gerador de slug ──────────────────────────────────────────────────
// Converte "Hardware e Periféricos" → "hardware-e-perifericos"
function toSlug(name: string): string {
  return name
    .toLowerCase()
    .normalize('NFD')
    .replace(/[\u0300-\u036f]/g, '') // remove acentos
    .replace(/[^a-z0-9\s-]/g, '')   // remove caracteres especiais
    .trim()
    .replace(/\s+/g, '-');           // espaços → hífens
}

// ─── Criação ──────────────────────────────────────────────────────────────────

export const createCategoryBodySchema = z.object({
  name: z
    .string({ required_error: 'Nome é obrigatório' })
    .min(2, 'Nome deve ter no mínimo 2 caracteres')
    .max(80)
    .trim(),

  // Se não informado, será gerado automaticamente a partir do name
  slug: z
    .string()
    .min(2)
    .max(80)
    .regex(/^[a-z0-9-]+$/, 'Slug deve conter apenas letras minúsculas, números e hífens')
    .optional(),

  description: z.string().max(300).trim().optional(),
}).transform((data) => ({
  ...data,
  // Garante que slug sempre exista — gerado do name se ausente
  slug: data.slug ?? toSlug(data.name),
}));

export type CreateCategoryBody = z.infer<typeof createCategoryBodySchema>;

// ─── Atualização (PATCH parcial) ──────────────────────────────────────────────

export const updateCategoryBodySchema = z.object({
  name:        z.string().min(2).max(80).trim().optional(),
  slug:        z.string().min(2).max(80)
    .regex(/^[a-z0-9-]+$/, 'Slug inválido').optional(),
  description: z.string().max(300).trim().nullable().optional(),
});

export type UpdateCategoryBody = z.infer<typeof updateCategoryBodySchema>;

// ─── Parâmetros de rota ───────────────────────────────────────────────────────

export const categoryParamsSchema = z.object({
  id: z.coerce.number().int().positive(),
});

export type CategoryParams = z.infer<typeof categoryParamsSchema>;

// ─── Resposta ─────────────────────────────────────────────────────────────────

export const categoryResponseSchema = z.object({
  id:          z.number(),
  name:        z.string(),
  slug:        z.string(),
  description: z.string().nullable(),
  createdAt:   z.string(),
  updatedAt:   z.string(),
});

export type CategoryResponse = z.infer<typeof categoryResponseSchema>;