Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ permissions:
jobs:
validate:
name: Validar aplicação
runs-on: ubuntu-latest
runs-on: ubuntu-24.04

steps:
- name: Baixar código
uses: actions/checkout@v4
uses: actions/checkout@v7

- name: Configurar Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v7
with:
node-version: 22
cache: npm
Expand Down
2 changes: 1 addition & 1 deletion backend/src/modules/attendance/attendance.factory.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { AttendanceService } from "./attendance.service.js";
import { PrismaAttendanceRepository } from "./repositories/prisma-attendance.repository.js";
import { PrismaTableRepository } from "./repositories/prisma-attendance.repository.js";
import { PrismaTableRepository } from "../tables/repositories/prisma-table.repository.js";

export function makeAttendanceService(): AttendanceService{
const attendanceRepository = new PrismaAttendanceRepository;
Expand Down
2 changes: 1 addition & 1 deletion backend/src/modules/attendance/attendance.service.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { AppError } from "../../shared/errors/app-errors.js";

import type { AttendanceRepository } from "./repositories/attendance.repository.js";
import type { TableRepository } from "../tables/repository/table.repository.js";
import type { TableRepository } from "../tables/repositories/table.repository.js";

import type { CreateAttendanceDTO } from "./dto/create-attendance.dto.js";

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type { AttendanceStatus } from "./attendance.entity.js";

export interface AttendanceTotal {
attendanceId: bigint;
tableId: bigint;
status: AttendanceStatus;
total: string;
}
23 changes: 20 additions & 3 deletions backend/src/modules/attendance/entity/attendance.entity.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,23 @@
import type { AttendanceStatus } from "../../../generated/prisma/enums.js";
export type AttendanceStatus =
| "OPEN"
| "CLOSING_REQUESTED"
| "CLOSED"
| "CANCELLED"
| "AWAITING_PAYMENT";

export class AttendanceEntity{
export interface AttendanceData {
id: bigint;
tableId: bigint;
status: AttendanceStatus;
openedAt: Date;
closingRequestedAt: Date | null;
closedAt: Date | null;
closedById: bigint | null;
createdAt: Date;
updatedAt: Date;
}

export class AttendanceEntity implements AttendanceData {
constructor(
public readonly id: bigint,
public readonly tableId: bigint,
Expand Down Expand Up @@ -28,4 +45,4 @@ export class AttendanceEntity{
canReceiveOrders(): boolean{
return this.isOpen()
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { AttendanceEntity } from "../entity/attendance.entity.js";
import type { AttendanceTotal } from "../../../generated/prisma/client.js";
import type { AttendanceStatus } from "../../../generated/prisma/client.js";
import type { AttendanceEntity, AttendanceStatus } from "../entity/attendance.entity.js";
import type { AttendanceTotal } from "../entity/attendance-total.entity.js";
import type { AttendanceListItem } from "../types/ttendance-list-item.type.js";

export interface AttendanceRepository{
Expand All @@ -17,4 +16,4 @@ export interface AttendanceRepository{
requestCancelled(attendanceId: bigint): Promise<AttendanceEntity>;

findAllByStatus(status: AttendanceStatus): Promise<AttendanceListItem[]>;
}
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import { prisma } from "../../../database/prisma.js";
import { Attendance, AttendanceTotal } from "../../../generated/prisma/client.js";
import type { Attendance } from "../../../generated/prisma/client.js";

import { AttendanceEntity } from "../entity/attendance.entity.js";
import { AttendanceRepository } from "./attendance.repository.js";
import type { AttendanceRepository } from "./attendance.repository.js";

import { TableRepository } from "../../tables/repository/table.repository.js";

import type { AttendanceStatus } from "../../../generated/prisma/client.js";
import type { AttendanceStatus } from "../entity/attendance.entity.js";
import type { AttendanceTotal } from "../entity/attendance-total.entity.js";
import type { AttendanceListItem } from "../types/ttendance-list-item.type.js";

export class PrismaAttendanceRepository implements AttendanceRepository{
constructor(private readonly prismaClient: typeof prisma = prisma) {}

async findById(id: bigint): Promise<AttendanceEntity | null> {
const attendance = await prisma.attendance.findUnique({
const attendance = await this.prismaClient.attendance.findUnique({
where:{id},
});

Expand All @@ -23,7 +24,7 @@ export class PrismaAttendanceRepository implements AttendanceRepository{
}

async findOpenByTableId(tableId: bigint): Promise<AttendanceEntity | null> {
const attendance = await prisma.attendance.findFirst({
const attendance = await this.prismaClient.attendance.findFirst({
where: {
tableId,
status: "OPEN",
Expand All @@ -38,7 +39,7 @@ export class PrismaAttendanceRepository implements AttendanceRepository{
}

async create(tableId: bigint): Promise<AttendanceEntity> {
const attendance = await prisma.attendance.create({
const attendance = await this.prismaClient.attendance.create({
data: {
tableId,
},
Expand All @@ -48,16 +49,25 @@ export class PrismaAttendanceRepository implements AttendanceRepository{
}

async findTotalByAttendanceId(attendanceId: bigint): Promise<AttendanceTotal | null> {
const total = await prisma.attendanceTotal.findUnique({
const total = await this.prismaClient.attendanceTotal.findUnique({
where: { attendanceId }
});

return total;
if (!total) {
return null;
}

return {
attendanceId: total.attendanceId,
tableId: total.tableId,
status: total.status,
total: total.total.toString(),
};
}

async requestClosing(attendanceId: bigint, requestedAt: Date): Promise<AttendanceEntity> {

const attendance = await prisma.$transaction(async (tx) => {
const attendance = await this.prismaClient.$transaction(async (tx) => {

const updatedAttendance = await tx.attendance.update({
where: {
Expand All @@ -84,7 +94,7 @@ export class PrismaAttendanceRepository implements AttendanceRepository{

async requestCancelled(attendanceId: bigint): Promise<AttendanceEntity> {

const attendance = await prisma.attendance.update({
const attendance = await this.prismaClient.attendance.update({
where: {
id: attendanceId,
},
Expand All @@ -98,7 +108,7 @@ export class PrismaAttendanceRepository implements AttendanceRepository{

async findAllByStatus(status: AttendanceStatus): Promise<AttendanceListItem[]> {

return prisma.attendance.findMany({
const attendances = await this.prismaClient.attendance.findMany({
where: {
status
},
Expand All @@ -115,6 +125,22 @@ export class PrismaAttendanceRepository implements AttendanceRepository{
closingRequestedAt: "asc"
}
});

return attendances.map((attendance) => this.toListItem(attendance));
}

private toListItem(
attendance: Attendance & { table: AttendanceListItem["table"] }
): AttendanceListItem {
return {
...this.toEntity(attendance),
activeTableId: attendance.activeTableId,
table: {
id: attendance.table.id,
number: attendance.table.number,
name: attendance.table.name,
},
};
}

private toEntity(attendance: Attendance): AttendanceEntity {
Expand All @@ -131,14 +157,3 @@ export class PrismaAttendanceRepository implements AttendanceRepository{
);
}
}

export class PrismaTableRepository implements TableRepository{
async existsById(id: bigint): Promise<boolean> {
const table = await prisma.restaurantTable.findUnique({
where: { id },
select: { id: true }
});

return table !== null;
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import type { Attendance } from "../../../generated/prisma/client.js";
import type { AttendanceData } from "../entity/attendance.entity.js";

export type AttendanceListItem = Attendance & {
export type AttendanceListItem = AttendanceData & {
activeTableId: bigint | null;
table: {
id: bigint;
number: number;
name: string | null;
}
}
}
135 changes: 62 additions & 73 deletions backend/src/modules/categories/category.controller.ts
Original file line number Diff line number Diff line change
@@ -1,92 +1,81 @@
import type { FastifyRequest, FastifyReply } from "fastify";

import CategoryService from "./category.service.js";
import { CategoryIdSchema, CategorySchema } from "./dto/category.dto.js";
import { CategoryOutput, CategoryResponse } from "./entity/category.entity.js";
import { FastifyRequest, FastifyReply } from "fastify";

import { createCategorySchema } from "./dto/create-category.dto.js";
import { updateCategorySchema } from "./dto/update-category.dto.js";
import { categoryIdSchema } from "./dto/category-id.dto.js";

import type { CategoryEntity } from "./entity/category.entity.js";

export default class CategoryController {
constructor(private readonly categoryService: CategoryService) {}

async createCategory(request: FastifyRequest, reply: FastifyReply) {
const parsedBody = CategorySchema.safeParse(request.body);
constructor(
private readonly categoryService: CategoryService
) {}

async createCategory(request: FastifyRequest, reply: FastifyReply) {

const data = createCategorySchema.parse(request.body);

if (!parsedBody.success) {
return this.sendValidationError(reply, parsedBody.error.issues);
const category = await this.categoryService.createCategory(data);

return reply.status(201).send(this.toResponse(category));
}

const category = await this.categoryService.createCategory(parsedBody.data);
return reply.status(201).send(this.toResponse(category));
}
async getAllCategories(_request: FastifyRequest, reply: FastifyReply) {

const categories = await this.categoryService.getAllCategories();

return reply.status(200).send(
categories.map((category) => this.toResponse(category))
);
}

async deleteCategory(request: FastifyRequest, reply: FastifyReply) {
const id = this.parseId(request, reply);
if (id === null) return;
async getCategoryById(request: FastifyRequest, reply: FastifyReply) {

await this.categoryService.deleteCategory(id);
return reply.status(204).send();
}
const { id } = categoryIdSchema.parse(request.params);

async getAllCategories(_request: FastifyRequest, reply: FastifyReply) {
const categories = await this.categoryService.getAllCategories();
return reply
.status(200)
.send(categories.map((category) => this.toResponse(category)));
}
const category = await this.categoryService.getCategoryById(id);

async getCategoryById(request: FastifyRequest, reply: FastifyReply) {
const id = this.parseId(request, reply);
if (id === null) return;
if (!category) {
return reply.status(404).send({
message: "Category not found"
});
}

const category = await this.categoryService.getCategoryById(id);
if (!category) {
return reply.status(404).send({ message: "Category not found" });
return reply.status(200).send(this.toResponse(category));
}
return reply.status(200).send(this.toResponse(category));
}

async updateCategory(request: FastifyRequest, reply: FastifyReply) {
const id = this.parseId(request, reply);
if (id === null) return;
async updateCategory(request: FastifyRequest, reply: FastifyReply) {

const parsedBody = CategorySchema.safeParse(request.body);
if (!parsedBody.success) {
return this.sendValidationError(reply, parsedBody.error.issues);
const { id } = categoryIdSchema.parse(request.params);

const data = updateCategorySchema.parse(request.body);

const category = await this.categoryService.updateCategory(id, data);

return reply.status(200).send(this.toResponse(category));
}

const category = await this.categoryService.updateCategory(
id,
parsedBody.data,
);
return reply.status(200).send(this.toResponse(category));
}

private parseId(request: FastifyRequest, reply: FastifyReply): bigint | null {
const { id } = request.params as { id: string };
const parsedId = CategoryIdSchema.safeParse(id);

if (!parsedId.success) {
this.sendValidationError(
reply,
parsedId.error.issues,
"Invalid category id",
);
return null;
async deleteCategory(request: FastifyRequest, reply: FastifyReply) {

const { id } = categoryIdSchema.parse(request.params);

await this.categoryService.deleteCategory(id);

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

return parsedId.data;
}

private sendValidationError(
reply: FastifyReply,
issues: unknown,
message = "Invalid request body",
) {
return reply.status(400).send({ message, issues });
}

private toResponse(category: CategoryOutput): CategoryResponse {
return {
...category,
id: category.id.toString(),
};
}
}
private toResponse(category: CategoryEntity) {

return {
id: category.id.toString(),
name: category.name,
description: category.description,
active: category.active,
createdAt: category.createdAt,
updatedAt: category.updatedAt
};
}
}
Loading