aboutsummaryrefslogtreecommitdiff
path: root/api/src/modules/categories/categories.controller.ts
diff options
context:
space:
mode:
Diffstat (limited to 'api/src/modules/categories/categories.controller.ts')
-rw-r--r--api/src/modules/categories/categories.controller.ts127
1 files changed, 127 insertions, 0 deletions
diff --git a/api/src/modules/categories/categories.controller.ts b/api/src/modules/categories/categories.controller.ts
new file mode 100644
index 0000000..9327932
--- /dev/null
+++ b/api/src/modules/categories/categories.controller.ts
@@ -0,0 +1,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();
+}