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 @@
-
-
-
+
-
-