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
5 changes: 5 additions & 0 deletions backend/api-mono-flow/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,13 @@ FROM eclipse-temurin:21-jre-alpine

WORKDIR /app

RUN apk add --no-cache wget

COPY --from=build /app/target/*.jar app.jar

EXPOSE 8080

HEALTHCHECK --interval=30s --timeout=5s --start-period=40s --retries=5 \
CMD wget -qO- http://127.0.0.1:8080/api/items > /dev/null || exit 1

ENTRYPOINT ["java", "-jar", "app.jar"]
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,13 @@ public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("http://localhost:5173", "http://localhost:80", "http://localhost")
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedOrigins(
"http://localhost:5173",
"http://localhost:3000",
"http://localhost:80",
"http://localhost"
)
.allowedMethods("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS")
.allowedHeaders("*");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public void addCorsMappings(CorsRegistry registry) {
"http://localhost:4173",
"http://localhost:3000"
)
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedMethods("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS")
.allowedHeaders("*")
.allowCredentials(false);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
package com.example.api_mono_flow.controller;

import java.util.List;
import java.util.UUID;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.example.api_mono_flow.dto.req.UpdateOrderItemStatusRequest;
import com.example.api_mono_flow.dto.res.OrderItemResponse;
import com.example.api_mono_flow.service.OrderItemService;

Expand All @@ -23,4 +28,11 @@ public class OrderItemController {
public ResponseEntity<List<OrderItemResponse>> findAll() {
return ResponseEntity.ok(orderItemService.findAll());
}

@PatchMapping("/{orderItemId}/status")
public ResponseEntity<OrderItemResponse> updateStatus(
@PathVariable UUID orderItemId,
@RequestBody UpdateOrderItemStatusRequest request) {
return ResponseEntity.ok(orderItemService.updateStatus(orderItemId, request));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.example.api_mono_flow.dto.req;

import com.example.api_mono_flow.model.domain.OrderItemStatus;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UpdateOrderItemStatusRequest {

private OrderItemStatus status;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import java.util.UUID;

import com.example.api_mono_flow.model.domain.OrderItemStatus;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
Expand All @@ -16,7 +18,9 @@
public class OrderItemResponse {

private UUID orderItemId;
private Integer tableNumber;
private String customerName;
private String itemName;
private Integer quantity;
private OrderItemStatus status;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.FetchType;
import jakarta.persistence.GenerationType;
import jakarta.persistence.GeneratedValue;
Expand Down Expand Up @@ -41,4 +43,9 @@ public class OrderItem {

@Column(name = "quantity", nullable = false)
private Integer quantity;

@Enumerated(EnumType.STRING)
@Column(name = "status", nullable = false, length = 20)
@Builder.Default
private OrderItemStatus status = OrderItemStatus.A_FAZER;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.example.api_mono_flow.model.domain;

public enum OrderItemStatus {
A_FAZER,
FAZENDO,
CONCLUIDO
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.example.api_mono_flow.model.domain.Item;
import com.example.api_mono_flow.model.domain.Order;
import com.example.api_mono_flow.model.domain.OrderItem;
import com.example.api_mono_flow.model.domain.OrderItemStatus;

public final class OrderItemMapper {

Expand All @@ -20,6 +21,7 @@ public static OrderItem toEntity(OrderItemRequest request) {
.item(request.getItemId() == null ? null : Item.builder().itemId(request.getItemId()).build())
.order(request.getOrderId() == null ? null : Order.builder().orderId(request.getOrderId()).build())
.quantity(request.getQuantity())
.status(OrderItemStatus.A_FAZER)
.build();
}

Expand All @@ -30,9 +32,11 @@ public static OrderItemResponse toResponse(OrderItem orderItem) {

return OrderItemResponse.builder()
.orderItemId(orderItem.getOrderItemId())
.tableNumber(orderItem.getOrder().getTableNumber())
.customerName(orderItem.getOrder().getCustomerName())
.itemName(orderItem.getItem().getName())
.quantity(orderItem.getQuantity())
.status(orderItem.getStatus())
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@
import org.springframework.stereotype.Repository;

import com.example.api_mono_flow.model.domain.OrderItem;
import com.example.api_mono_flow.model.domain.OrderItemStatus;

@Repository
public interface OrderItemRepository extends JpaRepository<OrderItem, UUID> {
List<OrderItem> findByOrder_OrderId(UUID orderId);
List<OrderItem> findByItem_ItemId(UUID itemId);
List<OrderItem> findByStatusNot(OrderItemStatus status);
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@
import org.springframework.transaction.annotation.Transactional;

import com.example.api_mono_flow.dto.req.OrderItemRequest;
import com.example.api_mono_flow.dto.req.UpdateOrderItemStatusRequest;
import com.example.api_mono_flow.dto.res.OrderItemResponse;
import com.example.api_mono_flow.model.domain.Item;
import com.example.api_mono_flow.model.domain.Order;
import com.example.api_mono_flow.model.domain.OrderItem;
import com.example.api_mono_flow.model.domain.OrderItemStatus;
import com.example.api_mono_flow.model.mapper.OrderItemMapper;
import com.example.api_mono_flow.model.repository.ItemRepository;
import com.example.api_mono_flow.model.repository.OrderItemRepository;
Expand All @@ -29,11 +31,36 @@ public class OrderItemService {
private final OrderRepository orderRepository;

public List<OrderItemResponse> findAll() {
return orderItemRepository.findAll().stream()
return orderItemRepository.findByStatusNot(OrderItemStatus.CONCLUIDO).stream()
.map(OrderItemMapper::toResponse)
.toList();
}

public OrderItemResponse updateStatus(UUID orderItemId, UpdateOrderItemStatusRequest request) {
if (request == null || request.getStatus() == null) {
throw new IllegalArgumentException("Status is required");
}

OrderItem orderItem = findEntityById(orderItemId);
OrderItemStatus current = orderItem.getStatus() == null
? OrderItemStatus.A_FAZER
: orderItem.getStatus();
OrderItemStatus next = request.getStatus();

if (!isValidTransition(current, next)) {
throw new IllegalArgumentException(
"Invalid status transition: " + current + " -> " + next);
}

orderItem.setStatus(next);
return OrderItemMapper.toResponse(orderItemRepository.save(orderItem));
}

private boolean isValidTransition(OrderItemStatus current, OrderItemStatus next) {
return (current == OrderItemStatus.A_FAZER && next == OrderItemStatus.FAZENDO)
|| (current == OrderItemStatus.FAZENDO && next == OrderItemStatus.CONCLUIDO);
}

public OrderItemResponse findById(UUID orderItemId) {
return OrderItemMapper.toResponse(findEntityById(orderItemId));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import com.example.api_mono_flow.model.domain.Item;
import com.example.api_mono_flow.model.domain.Order;
import com.example.api_mono_flow.model.domain.OrderItem;
import com.example.api_mono_flow.model.domain.OrderItemStatus;
import com.example.api_mono_flow.model.mapper.OrderItemMapper;
import com.example.api_mono_flow.model.mapper.OrderMapper;
import com.example.api_mono_flow.model.repository.ItemRepository;
Expand Down Expand Up @@ -89,6 +90,7 @@ public CompleteOrderResponse createCompleteOrder(CompleteOrderRequest request) {
.item(item)
.order(order)
.quantity(line.getQuantity())
.status(OrderItemStatus.A_FAZER)
.build();
items.add(orderItemRepository.save(orderItem));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
ALTER TABLE tb_order_item
ADD COLUMN status VARCHAR(20) NOT NULL DEFAULT 'A_FAZER';

ALTER TABLE tb_order_item
ADD CONSTRAINT chk_tb_order_item_status
CHECK (status IN ('A_FAZER', 'FAZENDO', 'CONCLUIDO'));
21 changes: 18 additions & 3 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ services:
interval: 10s
timeout: 5s
retries: 5
networks:
- menuflow

# ── Backend (Spring Boot) ───────────────────────────────────────────────────
backend:
Expand All @@ -35,8 +37,14 @@ services:
depends_on:
postgres:
condition: service_healthy
networks:
- menuflow

# ── Frontend (React + nginx) ────────────────────────────────────────────────
# ── Frontend (React SPA + nginx) ────────────────────────────────────────────
# URLs (porta 3000 no host → 80 no container):
# Home: http://localhost:3000/
# Mesa: http://localhost:3000/mesa?id=8 (id obrigatório)
# O nginx faz SPA fallback (/mesa → index.html) e proxy /api → backend:8080
frontend:
build:
context: ./frontend
Expand All @@ -46,7 +54,14 @@ services:
ports:
- "3000:80"
depends_on:
- backend
backend:
condition: service_started
networks:
- menuflow

networks:
menuflow:
driver: bridge

volumes:
pg_data:
pg_data:
8 changes: 8 additions & 0 deletions frontend/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
node_modules
dist
.git
.gitignore
npm-debug.log*
*.log
.env
.env.*
11 changes: 7 additions & 4 deletions frontend/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,22 +1,25 @@
# ── Stage 1: build ──────────────────────────────────────────────────────────
# ── Stage 1: build (Vite + React Router) ─────────────────────────────────────
# Gera o bundle estático com as rotas / e /mesa?id=
FROM node:22-alpine AS build

WORKDIR /app

COPY package*.json ./
COPY package.json package-lock.json ./
RUN npm ci

COPY . .
RUN npm run build

# ── Stage 2: runtime (nginx) ─────────────────────────────────────────────────
# Serve SPA (fallback index.html) e faz proxy de /api → backend
FROM nginx:stable-alpine

COPY --from=build /app/dist /usr/share/nginx/html

# Nginx config: serve SPA and proxy /api → backend
COPY nginx.conf /etc/nginx/conf.d/default.conf

EXPOSE 80

HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD wget -qO- http://127.0.0.1/ > /dev/null || exit 1

CMD ["nginx", "-g", "daemon off;"]
7 changes: 5 additions & 2 deletions frontend/nginx.conf
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@ server {
root /usr/share/nginx/html;
index index.html;

# SPA fallback — all unknown paths go to index.html
# React Router SPA:
# / → HomeScreen
# /mesa?id=N → MenuScreen (id obrigatório, tratado no cliente)
# Qualquer path desconhecido cai no index.html para o React Router.
location / {
try_files $uri $uri/ /index.html;
}

# Proxy /api requests to the Spring Boot backend
# Proxy /api Spring Boot (serviço "backend" na rede do compose)
location /api/ {
proxy_pass http://backend:8080/api/;
proxy_http_version 1.1;
Expand Down
Loading