From 03abba06796aebf94e7702bfc31d59264d98a76c Mon Sep 17 00:00:00 2001 From: Harkamal Randhawa Date: Thu, 16 Apr 2026 00:38:10 -0600 Subject: [PATCH] add order history to profile --- .../backend/controller/SaleController.java | 8 +++ web/app/globals.css | 62 +++++++++++++++++++ web/app/profile/page.js | 60 +++++++++++++++++- 3 files changed, 129 insertions(+), 1 deletion(-) diff --git a/backend/src/main/java/com/petshop/backend/controller/SaleController.java b/backend/src/main/java/com/petshop/backend/controller/SaleController.java index dffa63e4..bc40b066 100644 --- a/backend/src/main/java/com/petshop/backend/controller/SaleController.java +++ b/backend/src/main/java/com/petshop/backend/controller/SaleController.java @@ -3,6 +3,7 @@ package com.petshop.backend.controller; import com.petshop.backend.dto.sale.SaleRequest; import com.petshop.backend.dto.sale.SaleResponse; import com.petshop.backend.service.SaleService; +import com.petshop.backend.util.AuthenticationHelper; import jakarta.validation.Valid; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -21,6 +22,13 @@ public class SaleController { this.saleService = saleService; } + @GetMapping("/my") + @PreAuthorize("hasAnyRole('CUSTOMER', 'ADMIN')") + public ResponseEntity> getMyOrders(Pageable pageable) { + Long userId = AuthenticationHelper.getAuthenticatedUserId(); + return ResponseEntity.ok(saleService.getAllSales(null, null, null, false, userId, pageable)); + } + @GetMapping @PreAuthorize("hasAnyRole('STAFF', 'ADMIN')") public ResponseEntity> getAllSales( diff --git a/web/app/globals.css b/web/app/globals.css index e144cd7f..dde87b4b 100644 --- a/web/app/globals.css +++ b/web/app/globals.css @@ -2075,6 +2075,68 @@ body { color: #333; } +.profile-orders-list { + display: flex; + flex-direction: column; + gap: 0.75rem; +} + +.profile-order-card { + border: 1px solid #f0f0f0; + border-radius: 10px; + padding: 0.85rem 1rem; + background: #fafafa; +} + +.profile-order-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 0.25rem; +} + +.profile-order-date { + font-size: 0.85rem; + color: #555; + font-weight: 600; +} + +.profile-order-total { + font-size: 0.95rem; + font-weight: 700; + color: #222; +} + +.profile-order-meta { + display: flex; + gap: 1rem; + font-size: 0.78rem; + color: #999; + margin-bottom: 0.5rem; +} + +.profile-order-items { + margin: 0.25rem 0 0; + padding: 0; + list-style: none; + display: flex; + flex-direction: column; + gap: 0.2rem; + border-top: 1px solid #ececec; + padding-top: 0.5rem; +} + +.profile-order-items li { + display: flex; + justify-content: space-between; + font-size: 0.82rem; + color: #444; +} + +.profile-order-item-price { + color: #888; +} + /* Store Selector */ .nav-store-select { diff --git a/web/app/profile/page.js b/web/app/profile/page.js index a96d9f59..9463821f 100644 --- a/web/app/profile/page.js +++ b/web/app/profile/page.js @@ -25,6 +25,8 @@ export default function ProfilePage() { const [pets, setPets] = useState([]); const [loadingPets, setLoadingPets] = useState(false); + const [orders, setOrders] = useState([]); + const [loadingOrders, setLoadingOrders] = useState(false); const [showForm, setShowForm] = useState(false); const [editingPet, setEditingPet] = useState(null); const [petName, setPetName] = useState(""); @@ -120,11 +122,27 @@ export default function ProfilePage() { }; }, [clearPetImageObjectUrls]); + const loadOrders = useCallback(async () => { + if (!token) return; + setLoadingOrders(true); + try { + const res = await fetch(`${API_BASE}/api/v1/sales/my?size=20&sort=saleDate,desc`, { + headers: { Authorization: `Bearer ${token}` }, + }); + if (!res.ok) return; + const data = await res.json(); + setOrders(data.content ?? []); + } catch { } finally { + setLoadingOrders(false); + } + }, [token]); + useEffect(() => { if (user?.role === "CUSTOMER" || user?.role === "ADMIN") { loadPets(); + loadOrders(); } - }, [user, loadPets]); + }, [user, loadPets, loadOrders]); useEffect(() => { let objectUrl = null; @@ -642,6 +660,46 @@ export default function ProfilePage() { )} )} + + {(user.role === "CUSTOMER" || user.role === "ADMIN") && ( +
+
+

Order History

+
+ {loadingOrders ? ( +

Loading orders...

+ ) : orders.length === 0 ? ( +

No orders yet.

+ ) : ( +
+ {orders.map((order) => ( +
+
+ + {new Date(order.saleDate).toLocaleDateString([], { year: "numeric", month: "short", day: "numeric" })} + + ${Number(order.totalAmount).toFixed(2)} +
+
+ {order.storeName} + {order.paymentMethod && {order.paymentMethod}} +
+ {order.items?.length > 0 && ( +
    + {order.items.map((item) => ( +
  • + {item.productName} × {item.quantity} + ${(Number(item.unitPrice) * item.quantity).toFixed(2)} +
  • + ))} +
+ )} +
+ ))} +
+ )} +
+ )} ); }