aboutsummaryrefslogtreecommitdiff
path: root/api/src/modules/categories/categories.controller.ts
blob: 9327932663b909c4f3e4b49cd249a34f57d48aa9 (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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
/**
 * categories.controller.ts
 * Lógica de negócio do CRUD de categorias.
 */

import type { FastifyReply, FastifyRequest } from 'fastify';
import {
  findAllCategories,
  findCategoryById,
  findCategoryBySlug,
  createCategory,
  updateCategory,
  deleteCategory,
} from './categories.repository.js';
import type {
  CategoryParams,
  CreateCategoryBody,
  UpdateCategoryBody,
} from './categories.schema.js';
import { eq, count } from 'drizzle-orm';
import { tickets } from '@/db/schema.js';

// ─── GET /categories ──────────────────────────────────────────────────────────

export async function listCategoriesHandler(
  request: FastifyRequest,
  reply: FastifyReply,
) {
  const list = await findAllCategories(request.server.db);
  return reply.send(list);
}

// ─── GET /categories/:id ──────────────────────────────────────────────────────

export async function getCategoryHandler(
  request: FastifyRequest<{ Params: CategoryParams }>,
  reply: FastifyReply,
) {
  const category = await findCategoryById(request.server.db, request.params.id);

  if (!category) {
    return reply.notFound(`Categoria #${request.params.id} não encontrada.`);
  }

  return reply.send(category);
}

// ─── POST /categories ─────────────────────────────────────────────────────────

export async function createCategoryHandler(
  request: FastifyRequest<{ Body: CreateCategoryBody }>,
  reply: FastifyReply,
) {
  const { name, slug, description } = request.body;
  const db = request.server.db;

  // Slug deve ser único
  const existing = await findCategoryBySlug(db, slug);
  if (existing) {
    return reply.conflict(`Já existe uma categoria com o slug "${slug}".`);
  }

  const category = await createCategory(db, {
    name,
    slug,
    description: description ?? null,
  });

  return reply.code(201).send(category);
}

// ─── PATCH /categories/:id ────────────────────────────────────────────────────

export async function updateCategoryHandler(
  request: FastifyRequest<{ Params: CategoryParams; Body: UpdateCategoryBody }>,
  reply: FastifyReply,
) {
  const { id } = request.params;
  const db = request.server.db;

  const existing = await findCategoryById(db, id);
  if (!existing) {
    return reply.notFound(`Categoria #${id} não encontrada.`);
  }

  // Se o slug está sendo alterado, verifica conflito com outro registro
  if (request.body.slug && request.body.slug !== existing.slug) {
    const slugConflict = await findCategoryBySlug(db, request.body.slug);
    if (slugConflict) {
      return reply.conflict(`Já existe uma categoria com o slug "${request.body.slug}".`);
    }
  }

  const updated = await updateCategory(db, id, request.body);
  return reply.send(updated);
}

// ─── DELETE /categories/:id ───────────────────────────────────────────────────

export async function deleteCategoryHandler(
  request: FastifyRequest<{ Params: CategoryParams }>,
  reply: FastifyReply,
) {
  const { id } = request.params;
  const db = request.server.db;

  const existing = await findCategoryById(db, id);
  if (!existing) {
    return reply.notFound(`Categoria #${id} não encontrada.`);
  }

  // Protege contra deleção de categoria em uso
  const [{ total }] = await db
    .select({ total: count() })
    .from(tickets)
    .where(eq(tickets.categoryId, id));

  if (total > 0) {
    return reply.conflict(
      `Categoria em uso por ${total} chamado(s). Reclassifique-os antes de excluir.`,
    );
  }

  await deleteCategory(db, id);

  return reply.code(204).send();
}