From 9814dc5e30abf0bd4c5e58df9c0f584333002403 Mon Sep 17 00:00:00 2001 From: Ruidy Date: Sat, 27 Sep 2025 23:01:04 +0200 Subject: [PATCH] feat(web): extract static assets and routes for clarity Refactored web server to move route registration into a dedicated method and extracted static CSS into its own file for better maintainability. Updated HTML template to use external stylesheet and improved JS code style consistency. This enhances separation of concerns and prepares for easier future asset management. --- internal/web/routes.go | 11 +++++ internal/web/server.go | 7 ++-- web/static/app.js | 32 +++++++------- web/static/main.css | 83 ++++++++++++++++++++++++++++++++++++ web/templates/index.html | 91 ++++------------------------------------ 5 files changed, 122 insertions(+), 102 deletions(-) create mode 100644 internal/web/routes.go create mode 100644 web/static/main.css diff --git a/internal/web/routes.go b/internal/web/routes.go new file mode 100644 index 0000000..2ea2ee6 --- /dev/null +++ b/internal/web/routes.go @@ -0,0 +1,11 @@ +package web + +import ( + "net/http" +) + +func (h *Handler) registerRoutes(mux *http.ServeMux) { + mux.HandleFunc("POST /api/checkout", h.createCheckoutSession) + mux.Handle("GET /", http.HandlerFunc(h.renderCheckoutPage)) + mux.Handle("GET /static/", http.StripPrefix("/static/", http.FileServer(http.FS(h.fs)))) +} diff --git a/internal/web/server.go b/internal/web/server.go index be7b8b2..f122f98 100644 --- a/internal/web/server.go +++ b/internal/web/server.go @@ -23,6 +23,7 @@ type Handler struct { cfg config.Config checkout checkoutService page *template.Template + fs fs.FS } // NewServer constructs the root HTTP handler, wiring Stripe-backed endpoints as they are implemented. @@ -35,12 +36,10 @@ func NewServer(cfg config.Config) http.Handler { panic(fmt.Errorf("failed to load static assets: %w", err)) } - h := &Handler{cfg: cfg, checkout: checkoutSvc, page: tmpl} + h := &Handler{cfg: cfg, checkout: checkoutSvc, page: tmpl, fs: staticFS} mux := http.NewServeMux() - mux.HandleFunc("POST /api/checkout", h.createCheckoutSession) - mux.Handle("GET /", http.HandlerFunc(h.renderCheckoutPage)) - mux.Handle("GET /static/", http.StripPrefix("/static/", http.FileServer(http.FS(staticFS)))) + h.registerRoutes(mux) return mux } diff --git a/web/static/app.js b/web/static/app.js index 37c23dd..f19dffd 100644 --- a/web/static/app.js +++ b/web/static/app.js @@ -1,8 +1,8 @@ (() => { - const form = document.getElementById('checkout-form'); - const button = document.getElementById('checkout-button'); - const qtyInput = document.getElementById('quantity'); - const message = document.getElementById('message'); + const form = document.getElementById("checkout-form"); + const button = document.getElementById("checkout-button"); + const qtyInput = document.getElementById("quantity"); + const message = document.getElementById("message"); if (!form || !button || !qtyInput) { return; @@ -13,46 +13,46 @@ return; } message.textContent = text; - message.style.color = isError ? '#dc2626' : '#16a34a'; + message.style.color = isError ? "#dc2626" : "#16a34a"; }; - form.addEventListener('submit', async (event) => { + form.addEventListener("submit", async (event) => { event.preventDefault(); const quantity = Number.parseInt(qtyInput.value, 10); if (!Number.isFinite(quantity) || quantity <= 0) { - setMessage('Enter a quantity of at least 1.'); + setMessage("Enter a quantity of at least 1."); qtyInput.focus(); return; } try { button.disabled = true; - setMessage('Contacting Stripe…', false); + setMessage("Contacting Stripe…", false); - const response = await fetch('/api/checkout', { - method: 'POST', + const response = await fetch("/api/checkout", { + method: "POST", headers: { - 'Content-Type': 'application/json', + "Content-Type": "application/json", }, body: JSON.stringify({ quantity }), }); if (!response.ok) { const errorText = await response.text(); - throw new Error(errorText || 'Checkout request failed.'); + throw new Error(errorText || "Checkout request failed."); } const data = await response.json(); if (!data || !data.url) { - throw new Error('Checkout response missing redirect URL.'); + throw new Error("Checkout response missing redirect URL."); } - setMessage('Redirecting to Stripe…', false); + setMessage("Redirecting to Stripe…", false); window.location.href = data.url; } catch (err) { - console.error('Checkout failed', err); - setMessage('Unable to start checkout. Please try again.'); + console.error("Checkout failed", err); + setMessage("Unable to start checkout. Please try again."); button.disabled = false; } }); diff --git a/web/static/main.css b/web/static/main.css new file mode 100644 index 0000000..5834d89 --- /dev/null +++ b/web/static/main.css @@ -0,0 +1,83 @@ +:root { + color-scheme: light dark; + font-family: + "Inter", + system-ui, + -apple-system, + sans-serif; + line-height: 1.5; +} +body { + margin: 0; + display: flex; + min-height: 100vh; + background: radial-gradient(circle at top, #fdfbfb, #ebedee); + justify-content: center; + align-items: center; + padding: 2rem; +} +.card { + background: rgba(255, 255, 255, 0.95); + border-radius: 18px; + box-shadow: 0 20px 45px rgba(15, 23, 42, 0.12); + padding: 2.5rem; + max-width: 420px; + width: 100%; +} +h1 { + margin-top: 0; + font-size: 1.75rem; + color: #0f172a; +} +p { + margin: 0.5rem 0 1.5rem; + color: #475569; +} +.price { + font-size: 1.5rem; + font-weight: 600; + color: #2563eb; + margin-bottom: 1.5rem; +} +form { + display: flex; + flex-direction: column; + gap: 1.25rem; +} +label { + font-weight: 600; + color: #1e293b; +} +input[type="number"] { + width: 100%; + padding: 0.75rem 1rem; + border-radius: 12px; + border: 1px solid #cbd5f5; + font-size: 1rem; +} +button { + background: linear-gradient(135deg, #2563eb, #7c3aed); + border: none; + border-radius: 12px; + color: #fff; + font-size: 1rem; + font-weight: 600; + padding: 0.9rem 1.2rem; + cursor: pointer; + transition: + transform 0.15s ease, + box-shadow 0.15s ease; +} +button:hover:not([disabled]) { + transform: translateY(-1px); + box-shadow: 0 12px 30px rgba(37, 99, 235, 0.25); +} +button[disabled] { + opacity: 0.65; + cursor: wait; +} +#message { + min-height: 1.5rem; + color: #dc2626; + font-size: 0.95rem; +} diff --git a/web/templates/index.html b/web/templates/index.html index 178a422..9af67a9 100644 --- a/web/templates/index.html +++ b/web/templates/index.html @@ -4,93 +4,20 @@ PayIt Checkout - - + +

{{ .ProductName }}

{{ .ProductDescription }}

-
{{ .PriceDisplay }} {{ .Currency }}
-
+
+ {{ .PriceDisplay }} {{ .Currency }} +
+