aboutsummaryrefslogtreecommitdiff
path: root/api/src/modules/stats
diff options
context:
space:
mode:
Diffstat (limited to 'api/src/modules/stats')
-rw-r--r--api/src/modules/stats/stats.controller.ts15
-rw-r--r--api/src/modules/stats/stats.repository.ts41
-rw-r--r--api/src/modules/stats/stats.routes.ts22
-rw-r--r--api/src/modules/stats/stats.schema.ts17
4 files changed, 95 insertions, 0 deletions
diff --git a/api/src/modules/stats/stats.controller.ts b/api/src/modules/stats/stats.controller.ts
new file mode 100644
index 0000000..520f692
--- /dev/null
+++ b/api/src/modules/stats/stats.controller.ts
@@ -0,0 +1,15 @@
+/**
+ * stats.controller.ts
+ * Lógica do dashboard.
+ */
+
+import type { FastifyReply, FastifyRequest } from 'fastify';
+import { getTicketsStats } from './stats.repository.js';
+
+export async function getStatsHandler(
+ request: FastifyRequest,
+ reply: FastifyReply,
+) {
+ const stats = await getTicketsStats(request.server.db);
+ return reply.send(stats);
+}
diff --git a/api/src/modules/stats/stats.repository.ts b/api/src/modules/stats/stats.repository.ts
new file mode 100644
index 0000000..d5a543d
--- /dev/null
+++ b/api/src/modules/stats/stats.repository.ts
@@ -0,0 +1,41 @@
+/**
+ * stats.repository.ts
+ * Queries para obter os totais agrupados.
+ */
+
+import { sql } from 'drizzle-orm';
+import type { Database } from '@/db/connection.js';
+import { tickets } from '@/db/schema.js';
+
+export async function getTicketsStats(db: Database) {
+ // Uma única query para agrupar as contagens por status
+ const rows = await db
+ .select({
+ status: tickets.status,
+ count: sql<number>`count(*)`,
+ })
+ .from(tickets)
+ .groupBy(tickets.status);
+
+ // Inicializa o mapa com 0
+ const stats = {
+ totalOpen: 0,
+ totalInProgress: 0,
+ totalWaiting: 0,
+ totalResolved: 0,
+ totalClosed: 0,
+ };
+
+ for (const row of rows) {
+ if (row.status === 'open') stats.totalOpen = row.count;
+ if (row.status === 'in_progress') stats.totalInProgress = row.count;
+ if (row.status === 'waiting') stats.totalWaiting = row.count;
+ if (row.status === 'resolved') stats.totalResolved = row.count;
+ if (row.status === 'closed') stats.totalClosed = row.count;
+ }
+
+ return {
+ ...stats,
+ totalPending: stats.totalOpen + stats.totalInProgress,
+ };
+}
diff --git a/api/src/modules/stats/stats.routes.ts b/api/src/modules/stats/stats.routes.ts
new file mode 100644
index 0000000..cf02c08
--- /dev/null
+++ b/api/src/modules/stats/stats.routes.ts
@@ -0,0 +1,22 @@
+/**
+ * stats.routes.ts
+ *
+ * GET /api/v1/stats → retorna agregados para o dashboard
+ */
+
+import type { FastifyInstance } from 'fastify';
+import { statsResponseSchema } from './stats.schema.js';
+import { getStatsHandler } from './stats.controller.js';
+
+async function statsRoutes(fastify: FastifyInstance): Promise<void> {
+ fastify.get('/', {
+ schema: {
+ tags: ['Dashboard'],
+ summary: 'Retorna métricas e agregados dos chamados',
+ },
+ preHandler: [fastify.authenticate],
+ handler: getStatsHandler,
+ });
+}
+
+export default statsRoutes;
diff --git a/api/src/modules/stats/stats.schema.ts b/api/src/modules/stats/stats.schema.ts
new file mode 100644
index 0000000..1616937
--- /dev/null
+++ b/api/src/modules/stats/stats.schema.ts
@@ -0,0 +1,17 @@
+/**
+ * stats.schema.ts
+ * Schemas para o módulo de estatísticas (Dashboard).
+ */
+
+import { z } from 'zod';
+
+export const statsResponseSchema = z.object({
+ totalOpen: z.number(),
+ totalInProgress: z.number(),
+ totalWaiting: z.number(),
+ totalResolved: z.number(),
+ totalClosed: z.number(),
+ totalPending: z.number(), // open + in_progress
+});
+
+export type StatsResponse = z.infer<typeof statsResponseSchema>;