From 966536d5f53f8b2d53fb95744c6f2058eeaa87e7 Mon Sep 17 00:00:00 2001 From: devzeeh <148837352+devzeeh@users.noreply.github.com> Date: Thu, 11 Jun 2026 09:03:44 +0800 Subject: [PATCH 1/9] feat: implement customer dashboard and card management backend handlers with corresponding frontend templates --- backend/cmd/app/main.go | 10 +- backend/internal/user/customer_card.go | 19 + backend/internal/user/dashboard.go | 1 + .../assets/js/{card.js => customer_card.js} | 50 +- frontend/templates/customer/card.html | 471 +++++++----------- .../templates/customer/customer_sidebar.html | 62 +-- frontend/templates/customer/dashboard.html | 44 +- frontend/templates/customer/transaction.html | 2 +- 8 files changed, 292 insertions(+), 367 deletions(-) create mode 100644 backend/internal/user/customer_card.go rename frontend/assets/js/{card.js => customer_card.js} (82%) diff --git a/backend/cmd/app/main.go b/backend/cmd/app/main.go index 966cb38..6c350c1 100644 --- a/backend/cmd/app/main.go +++ b/backend/cmd/app/main.go @@ -131,12 +131,16 @@ func main() { return } - // Handle GET /{username}/transaction(s) manually to avoid ServeMux conflict with /assets/ + // Handle GET /{username}/transaction(s) and /card manually to avoid ServeMux conflict with /assets/ parts := strings.Split(r.URL.Path, "/") - if len(parts) == 3 && (parts[2] == "transaction" || parts[2] == "transactions") && r.Method == http.MethodGet { + if len(parts) == 3 && (parts[2] == "transaction" || parts[2] == "transactions" || parts[2] == "card") && r.Method == http.MethodGet { if parts[1] != "assets" && parts[1] != "storage" && parts[1] != "v1" && parts[1] != "admin" { r.SetPathValue("username", parts[1]) - userHandler.TransactionView(w, r) + if parts[2] == "card" { + userHandler.CardView(w, r) + } else { + userHandler.TransactionView(w, r) + } return } } diff --git a/backend/internal/user/customer_card.go b/backend/internal/user/customer_card.go new file mode 100644 index 0000000..93f321e --- /dev/null +++ b/backend/internal/user/customer_card.go @@ -0,0 +1,19 @@ +package user + +import ( + "fmt" + "net/http" +) + +func (h *Handler) CardView(w http.ResponseWriter, r *http.Request) { + fmt.Println("Card view is running...") + + username := r.PathValue("username") + data := struct { + Username string + }{ + Username: username, + } + + h.Tpl.ExecuteTemplate(w, "card.html", data) +} diff --git a/backend/internal/user/dashboard.go b/backend/internal/user/dashboard.go index 04913e7..c0f66b2 100644 --- a/backend/internal/user/dashboard.go +++ b/backend/internal/user/dashboard.go @@ -50,6 +50,7 @@ func (h *Handler) DashboardView(w http.ResponseWriter, r *http.Request) { h.Tpl.ExecuteTemplate(w, "dashboard.html", data) } + func (h *Handler) DashboardHandler(w http.ResponseWriter, r *http.Request) { fmt.Println("Dashboard JSON handler is running...") diff --git a/frontend/assets/js/card.js b/frontend/assets/js/customer_card.js similarity index 82% rename from frontend/assets/js/card.js rename to frontend/assets/js/customer_card.js index acb2358..c7bbb6e 100644 --- a/frontend/assets/js/card.js +++ b/frontend/assets/js/customer_card.js @@ -41,7 +41,55 @@ document.addEventListener("DOMContentLoaded", function () { // Stop the script if critical elements are missing if (!reportElementsExist || !replacementElementsExist || !cardStatusBadge) { - return; + // We still want to run the toggle lock logic, so don't return here entirely if we're just ignoring old missing elements + console.warn("Some card management elements are missing, but we'll continue for lock toggle."); + } + + // --- Lock Card Toggle Logic --- + const toggle = document.getElementById("lock-card-toggle"); + const knob = document.getElementById("lock-card-knob"); + const overlay = document.getElementById("card-lock-overlay"); + let isLocked = false; + + if (toggle) { + toggle.addEventListener("click", () => { + isLocked = !isLocked; + if (isLocked) { + // Switch on + toggle.classList.remove("bg-gray-200"); + toggle.classList.add("bg-blue-600"); + if (knob) { + knob.classList.remove("translate-x-1"); + knob.classList.add("translate-x-6"); + } + + // Show overlay + if (overlay) { + overlay.classList.remove("hidden"); + setTimeout(() => { + overlay.classList.remove("opacity-0"); + overlay.classList.add("opacity-100"); + }, 10); + } + } else { + // Switch off + toggle.classList.remove("bg-blue-600"); + toggle.classList.add("bg-gray-200"); + if (knob) { + knob.classList.remove("translate-x-6"); + knob.classList.add("translate-x-1"); + } + + // Hide overlay + if (overlay) { + overlay.classList.remove("opacity-100"); + overlay.classList.add("opacity-0"); + setTimeout(() => { + overlay.classList.add("hidden"); + }, 300); + } + } + }); } // --- Generic Modal Logic --- diff --git a/frontend/templates/customer/card.html b/frontend/templates/customer/card.html index 8531824..150f9d4 100644 --- a/frontend/templates/customer/card.html +++ b/frontend/templates/customer/card.html @@ -9,284 +9,182 @@ - - - + - -
-
- - - - - - - - - - - -
+
+ + {{template "customer_sidebar" .}} - -
- -
- + +
+ +
+
+ - - -
-
-
+ - -
- - -
+ +
+
+ +

My Card

- -
- -
-
- -

My Card

+
+ + +
+
+ +
+ + +
+
- -
- -
-

Card Preview

- -
- -
- -
- - - - - PayCard + + - - -
- -
- -
-
-
- -

- **** **** **** **** -

-
- -
- -
-

Cardholder Name

-

CARDHOLDER

-
- -
-

Expires End

-

MM/YY

+ + +
+ + +
+
+
+ + UniCard +
+ Loading... +
+ +
+ + +
+
+
+
+
+
+ + +
+

Available Balance

+

0.00

+
+
+ + +
+

**** **** **** ****

+ +
+
+

Cardholder

+

LOADING...

+
+
+

Valid Thru

+

MM/YY

+
+
+
+
-
- - -
-
-

Card Details

-
-
-
Card Number
-
-
-
+ +
+ + +
+

+ Security + Coming Soon +

+ +
+
+
+ +
+
+

Lock Card

+

Temporarily disable transactions

+
+
+ + +
-
-
Status
-
- - - Loading... - -
-
-
-
Account Type
-
-
-
-
-
-
Expiry Date
-
-
-
+
+ + +
+

Management

+ +
+ + +
-
-
-
- - -
-
-

Card Actions

-
- -
-
-
-
+
+
+
-
+ @@ -295,10 +193,8 @@

Card Actions

@@ -370,45 +249,31 @@

Report Card