From 64f5b9a65bfaac0b0329feeba1bc01d83d73a55d Mon Sep 17 00:00:00 2001 From: cclohmar Date: Sat, 30 May 2026 11:34:36 +0000 Subject: [PATCH] fix: mobile receipt capture not working - missing name attribute on file input + htmx targetError on OTP verification --- internal/handlers/auth.go | 24 +++++++++++++----------- templates/event_expenses.html | 10 +++------- templates/index.html | 2 +- 3 files changed, 17 insertions(+), 19 deletions(-) diff --git a/internal/handlers/auth.go b/internal/handlers/auth.go index bc2bd7d..cb46117 100644 --- a/internal/handlers/auth.go +++ b/internal/handlers/auth.go @@ -124,7 +124,7 @@ func (h *AuthHandler) RequestOTP(w http.ResponseWriter, r *http.Request) { } // Render the OTP verification form as an HTMX fragment. - renderOTPForm(w, emailAddr) + renderOTPForm(w, emailAddr, "") } // VerifyOTP handles OTP code verification and session creation. @@ -140,7 +140,7 @@ func (h *AuthHandler) VerifyOTP(w http.ResponseWriter, r *http.Request) { otpCode := collectOTP(r) if emailAddr == "" || otpCode == "" { - renderError(w, "Email and OTP code are required.") + renderOTPForm(w, emailAddr, "Email and OTP code are required.") return } @@ -148,11 +148,11 @@ func (h *AuthHandler) VerifyOTP(w http.ResponseWriter, r *http.Request) { stored, err := database.GetOTP(h.DB, emailAddr) if err != nil { log.Printf("ERROR [%s] handlers: VerifyOTP GetOTP(%s): %v", time.Now().Format(time.RFC3339), emailAddr, err) - renderError(w, "An error occurred. Please try again.") + renderOTPForm(w, emailAddr, "An error occurred. Please try again.") return } if stored == nil { - renderError(w, "No OTP found for this email. Please request a new code.") + renderOTPForm(w, emailAddr, "No OTP found for this email. Please request a new code.") return } @@ -160,14 +160,14 @@ func (h *AuthHandler) VerifyOTP(w http.ResponseWriter, r *http.Request) { expiresAt, err := time.Parse(time.RFC3339, stored.ExpiresAt) if err != nil { log.Printf("ERROR [%s] handlers: VerifyOTP parse expiry(%s): %v", time.Now().Format(time.RFC3339), stored.ExpiresAt, err) - renderError(w, "An error occurred. Please try again.") + renderOTPForm(w, emailAddr, "An error occurred. Please try again.") return } // Validate the OTP code and expiry. if !auth.ValidateOTP(otpCode, stored.OTPCode, expiresAt) { h.FailureTracker.RecordFailure(emailAddr) - renderError(w, "Invalid or expired OTP code. Please try again.") + renderOTPForm(w, emailAddr, "Invalid or expired OTP code. Please try again.") return } @@ -182,7 +182,7 @@ func (h *AuthHandler) VerifyOTP(w http.ResponseWriter, r *http.Request) { user, err := database.GetUserByEmail(h.DB, emailAddr) if err != nil || user == nil { log.Printf("ERROR [%s] handlers: VerifyOTP GetUserByEmail(%s): err=%v", time.Now().Format(time.RFC3339), emailAddr, err) - renderError(w, "An error occurred. Please try again.") + renderOTPForm(w, emailAddr, "An error occurred. Please try again.") return } @@ -190,7 +190,7 @@ func (h *AuthHandler) VerifyOTP(w http.ResponseWriter, r *http.Request) { token, err := h.Sessions.Generate(user.ID) if err != nil { log.Printf("ERROR [%s] handlers: VerifyOTP Session Generate(%s): %v", time.Now().Format(time.RFC3339), user.ID, err) - renderError(w, "An error occurred. Please try again.") + renderOTPForm(w, emailAddr, "An error occurred. Please try again.") return } @@ -257,10 +257,12 @@ func renderError(w http.ResponseWriter, message string) { // renderOTPForm writes the OTP verification form partial as an HTMX fragment. // It renders 6 individual digit input boxes for a better mobile UX, plus a // hidden email field. The handler combines the 6 digits server-side. -func renderOTPForm(w http.ResponseWriter, email string) { +// If errMsg is non-empty, it is displayed as an error banner above the form. +func renderOTPForm(w http.ResponseWriter, email string, errMsg string) { tmpl := template.Must(template.New("otp_form").Parse(` -
+ + {{if .Error}}
{{.Error}}
{{end}}
@@ -282,7 +284,7 @@ func renderOTPForm(w http.ResponseWriter, email string) { `)) w.Header().Set("Content-Type", "text/html; charset=utf-8") - if err := tmpl.Execute(w, map[string]string{"Email": email}); err != nil { + if err := tmpl.Execute(w, map[string]string{"Email": email, "Error": errMsg}); err != nil { log.Printf("ERROR [%s] handlers: renderOTPForm execute: %v", time.Now().Format(time.RFC3339), err) } } diff --git a/templates/event_expenses.html b/templates/event_expenses.html index 70f5c79..abb55fd 100644 --- a/templates/event_expenses.html +++ b/templates/event_expenses.html @@ -30,12 +30,13 @@ Capture Receipt - + hx-indicator="#upload-indicator" + hx-trigger="change">

Analyzing receipt...

@@ -130,11 +131,6 @@