diff options
| author | Lucas Faria Mendes <lucas.fariamo08@gmail.com> | 2026-08-16 23:17:42 +0000 |
|---|---|---|
| committer | Lucas Faria Mendes <lucas.fariamo08@gmail.com> | 2026-08-16 23:17:42 +0000 |
| commit | 375482e654a01615683867e732ac9e458c7cbe74 (patch) | |
| tree | 12d1b31b8deb6bf8590bcdbc2cfb52dcc553e006 /api/src/modules/users | |
| parent | 2c2dfba45357bbd205a59145ae55a2779c911ae0 (diff) | |
| download | yaba-master.tar.gz yaba-master.zip | |
Signed-off-by: Lucas Faria Mendes <lucas.fariamo08@gmail.com>
Diffstat (limited to 'api/src/modules/users')
| -rw-r--r-- | api/src/modules/users/users.routes.ts | 73 | ||||
| -rw-r--r-- | api/src/modules/users/users.schema.ts | 23 | ||||
| -rw-r--r-- | api/src/modules/users/users.service.ts | 53 |
3 files changed, 149 insertions, 0 deletions
diff --git a/api/src/modules/users/users.routes.ts b/api/src/modules/users/users.routes.ts new file mode 100644 index 0000000..28cf584 --- /dev/null +++ b/api/src/modules/users/users.routes.ts @@ -0,0 +1,73 @@ +import type { FastifyPluginAsync } from 'fastify'; +import { ZodError } from 'zod'; +import { + createUserSchema, + updateUserSchema, + userParamsSchema, +} from './users.schema'; +import * as usersService from './users.service'; + +const usersRoutes: FastifyPluginAsync = async (fastify) => { + /** GET /users — Lista todos os usuários */ + fastify.get('/users', async (_request, reply) => { + const result = await usersService.findAllUsers(); + return reply.send({ data: result }); + }); + + /** GET /users/:id — Busca um usuário pelo ID */ + fastify.get<{ Params: { id: string } }>('/users/:id', async (request, reply) => { + const { id } = userParamsSchema.parse(request.params); + const user = await usersService.findUserById(id); + + if (!user) { + return reply.status(404).send({ error: 'Usuário não encontrado.' }); + } + return reply.send({ data: user }); + }); + + /** POST /users — Cria um novo usuário */ + fastify.post('/users', async (request, reply) => { + try { + const body = createUserSchema.parse(request.body); + const user = await usersService.createUser(body); + return reply.status(201).send({ data: user }); + } catch (err) { + if (err instanceof ZodError) { + return reply.status(400).send({ error: 'Dados inválidos.', details: err.errors }); + } + throw err; + } + }); + + /** PUT /users/:id — Atualiza um usuário (incluindo salário e override) */ + fastify.put<{ Params: { id: string } }>('/users/:id', async (request, reply) => { + try { + const { id } = userParamsSchema.parse(request.params); + const body = updateUserSchema.parse(request.body); + const user = await usersService.updateUser(id, body); + + if (!user) { + return reply.status(404).send({ error: 'Usuário não encontrado.' }); + } + return reply.send({ data: user }); + } catch (err) { + if (err instanceof ZodError) { + return reply.status(400).send({ error: 'Dados inválidos.', details: err.errors }); + } + throw err; + } + }); + + /** DELETE /users/:id — Remove um usuário (cascade nos gastos) */ + fastify.delete<{ Params: { id: string } }>('/users/:id', async (request, reply) => { + const { id } = userParamsSchema.parse(request.params); + const deleted = await usersService.deleteUser(id); + + if (!deleted) { + return reply.status(404).send({ error: 'Usuário não encontrado.' }); + } + return reply.status(200).send({ data: deleted, message: 'Usuário removido com sucesso.' }); + }); +}; + +export default usersRoutes; diff --git a/api/src/modules/users/users.schema.ts b/api/src/modules/users/users.schema.ts new file mode 100644 index 0000000..62ab514 --- /dev/null +++ b/api/src/modules/users/users.schema.ts @@ -0,0 +1,23 @@ +import { z } from 'zod'; + +export const createUserSchema = z.object({ + name: z.string().min(1, 'Nome é obrigatório').max(100), + gross_salary: z.number().nonnegative('Salário bruto deve ser >= 0'), + net_salary_override: z.number().nonnegative().nullable().optional(), +}); + +export const updateUserSchema = z.object({ + name: z.string().min(1).max(100).optional(), + gross_salary: z.number().nonnegative().optional(), + /** + * Envia `null` para limpar o override e voltar ao valor calculado automaticamente. + */ + net_salary_override: z.number().nonnegative().nullable().optional(), +}); + +export const userParamsSchema = z.object({ + id: z.coerce.number().int().positive(), +}); + +export type CreateUserInput = z.infer<typeof createUserSchema>; +export type UpdateUserInput = z.infer<typeof updateUserSchema>; diff --git a/api/src/modules/users/users.service.ts b/api/src/modules/users/users.service.ts new file mode 100644 index 0000000..a0fda40 --- /dev/null +++ b/api/src/modules/users/users.service.ts @@ -0,0 +1,53 @@ +import { eq } from 'drizzle-orm'; +import { db } from '@/db/client'; +import { users } from '@/db/schema'; +import type { CreateUserInput, UpdateUserInput } from './users.schema'; + +export async function findAllUsers() { + return db.select().from(users); +} + +export async function findUserById(id: number) { + const result = await db.select().from(users).where(eq(users.id, id)).limit(1); + return result[0] ?? null; +} + +export async function createUser(data: CreateUserInput) { + const [created] = await db + .insert(users) + .values({ + name: data.name, + gross_salary: data.gross_salary, + net_salary_override: data.net_salary_override ?? null, + }) + .returning(); + return created; +} + +export async function updateUser(id: number, data: UpdateUserInput) { + const now = new Date().toISOString(); + + const [updated] = await db + .update(users) + .set({ + ...(data.name !== undefined && { name: data.name }), + ...(data.gross_salary !== undefined && { gross_salary: data.gross_salary }), + // Permite setar null explicitamente (para remover o override) + ...(data.net_salary_override !== undefined && { + net_salary_override: data.net_salary_override, + }), + updated_at: now, + }) + .where(eq(users.id, id)) + .returning(); + + return updated ?? null; +} + +export async function deleteUser(id: number) { + const [deleted] = await db + .delete(users) + .where(eq(users.id, id)) + .returning(); + return deleted ?? null; +} |