From 76028fa94d57ed8e589183c2ad495973d9088453 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20M=C3=B6rling?= Date: Fri, 15 May 2026 12:14:53 +0200 Subject: [PATCH] feat: add GET /api/admin/orders and PATCH /api/admin/orders/{id}/status MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - AdminOrderResponse DTO: extends OrderResponse with email (from User relation) and letterText fields, exposing the full order for admin review - UpdateStatusRequest DTO: single "status" field validated against all six OrderStatus values (pending_payment|paid|lookup_started|sent| delivered|failed) with Swedish error messages - OrderService.getAllOrders(): delegates to OrderRepository .findAllByOrderByCreatedAtDesc() which uses @EntityGraph to eagerly fetch the user relationship in a single query - OrderService.updateOrderStatus(orderId, statusString): looks up order, converts status string to OrderStatus enum via case-insensitive valueOf(), saves updated entity - AdminController /api/admin: GET /orders → list all orders with user email (admin only) PATCH /orders/{id}/status → update order status (admin only) - toAdminResponse() mapper safely handles null user (empty email fallback) --- .../controller/AdminController.java | 56 +++++++++++++++++++ .../bilhalsning/dto/AdminOrderResponse.java | 16 ++++++ .../bilhalsning/dto/UpdateStatusRequest.java | 13 +++++ .../se/bilhalsning/service/OrderService.java | 13 +++++ 4 files changed, 98 insertions(+) create mode 100644 backend/src/main/java/se/bilhalsning/controller/AdminController.java create mode 100644 backend/src/main/java/se/bilhalsning/dto/AdminOrderResponse.java create mode 100644 backend/src/main/java/se/bilhalsning/dto/UpdateStatusRequest.java diff --git a/backend/src/main/java/se/bilhalsning/controller/AdminController.java b/backend/src/main/java/se/bilhalsning/controller/AdminController.java new file mode 100644 index 0000000..5b3f2a8 --- /dev/null +++ b/backend/src/main/java/se/bilhalsning/controller/AdminController.java @@ -0,0 +1,56 @@ +package se.bilhalsning.controller; + +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +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 se.bilhalsning.dto.AdminOrderResponse; +import se.bilhalsning.dto.UpdateStatusRequest; +import se.bilhalsning.entity.Order; +import se.bilhalsning.service.OrderService; + +import java.util.List; +import java.util.UUID; + +@RestController +@RequestMapping("/api/admin") +@RequiredArgsConstructor +public class AdminController { + + private final OrderService orderService; + + @GetMapping("/orders") + public ResponseEntity> listAllOrders() { + List orders = orderService.getAllOrders().stream() + .map(this::toAdminResponse) + .toList(); + return ResponseEntity.ok(orders); + } + + @PatchMapping("/orders/{id}/status") + public ResponseEntity updateStatus( + @PathVariable UUID id, + @Valid @RequestBody UpdateStatusRequest request) { + Order order = orderService.updateOrderStatus(id, request.status()); + return ResponseEntity.ok(toAdminResponse(order)); + } + + private AdminOrderResponse toAdminResponse(Order order) { + String email = order.getUser() != null ? order.getUser().getEmail() : ""; + return new AdminOrderResponse( + order.getId(), + email, + order.getPlate(), + order.getLetterText(), + order.getStatus().getValue(), + order.getTrackingId(), + order.getAmountPaid(), + order.getCreatedAt() + ); + } +} diff --git a/backend/src/main/java/se/bilhalsning/dto/AdminOrderResponse.java b/backend/src/main/java/se/bilhalsning/dto/AdminOrderResponse.java new file mode 100644 index 0000000..fc65709 --- /dev/null +++ b/backend/src/main/java/se/bilhalsning/dto/AdminOrderResponse.java @@ -0,0 +1,16 @@ +package se.bilhalsning.dto; + +import java.math.BigDecimal; +import java.time.Instant; +import java.util.UUID; + +public record AdminOrderResponse( + UUID id, + String email, + String plate, + String letterText, + String status, + String trackingId, + BigDecimal amountPaid, + Instant createdAt +) {} diff --git a/backend/src/main/java/se/bilhalsning/dto/UpdateStatusRequest.java b/backend/src/main/java/se/bilhalsning/dto/UpdateStatusRequest.java new file mode 100644 index 0000000..9cd6417 --- /dev/null +++ b/backend/src/main/java/se/bilhalsning/dto/UpdateStatusRequest.java @@ -0,0 +1,13 @@ +package se.bilhalsning.dto; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; + +public record UpdateStatusRequest( + @NotBlank(message = "Status krävs") + @Pattern( + regexp = "pending_payment|paid|lookup_started|sent|delivered|failed", + message = "Ogiltig status" + ) + String status +) {} diff --git a/backend/src/main/java/se/bilhalsning/service/OrderService.java b/backend/src/main/java/se/bilhalsning/service/OrderService.java index e2a354d..2591fe4 100644 --- a/backend/src/main/java/se/bilhalsning/service/OrderService.java +++ b/backend/src/main/java/se/bilhalsning/service/OrderService.java @@ -33,4 +33,17 @@ public class OrderService { return orderRepository.findById(id) .orElseThrow(() -> new OrderNotFoundException(id)); } + + public List getAllOrders() { + return orderRepository.findAllByOrderByCreatedAtDesc(); + } + + public Order updateOrderStatus(UUID orderId, String statusString) { + Order order = orderRepository.findById(orderId) + .orElseThrow(() -> new OrderNotFoundException(orderId)); + + OrderStatus newStatus = OrderStatus.valueOf(statusString.toUpperCase()); + order.setStatus(newStatus); + return orderRepository.save(order); + } }