diff --git a/internal/handlers/auth.go b/internal/handlers/auth.go index cf5a8f7..85af0bc 100644 --- a/internal/handlers/auth.go +++ b/internal/handlers/auth.go @@ -379,6 +379,72 @@ func (h *AuthHandler) SaveOnboarding(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) } +// --------------------------------------------------------------------------- +// Profile handlers +// --------------------------------------------------------------------------- + +// ProfilePage renders the profile editor with the user's current name and +// department pre-filled. Requires onboarding to be completed first. +func (h *AuthHandler) ProfilePage(w http.ResponseWriter, r *http.Request) { + userID := getUserID(r) + if userID == "" { + w.Header().Set("HX-Redirect", "/") + w.WriteHeader(http.StatusUnauthorized) + return + } + + user, _ := database.GetUserByID(h.DB, userID) + if user == nil || !user.Onboarded { + w.Header().Set("HX-Redirect", "/onboarding") + w.WriteHeader(http.StatusOK) + return + } + + tmpl := getTemplate("onboarding.html") + w.Header().Set("Content-Type", "text/html; charset=utf-8") + if err := tmpl.Execute(w, map[string]string{ + "Name": user.Name, + "Department": user.Department, + "Editing": "true", + }); err != nil { + log.Printf("ERROR [%s] handlers: ProfilePage: execute template: %v", time.Now().Format(time.RFC3339), err) + } +} + +// SaveProfile updates the user's name and department, then redirects to the +// dashboard. Reuses the same DB call as onboarding. +func (h *AuthHandler) SaveProfile(w http.ResponseWriter, r *http.Request) { + userID := getUserID(r) + if userID == "" { + w.Header().Set("HX-Redirect", "/") + w.WriteHeader(http.StatusUnauthorized) + return + } + + if err := r.ParseForm(); err != nil { + renderOnboardingError(w, "Cannot parse form data.") + return + } + + name := strings.TrimSpace(r.FormValue("name")) + department := strings.TrimSpace(r.FormValue("department")) + if name == "" { + renderOnboardingError(w, "Name is required.") + return + } + if department == "" { + department = "-" + } + + if err := database.UpdateUserOnboarding(h.DB, userID, name, department); err != nil { + renderOnboardingError(w, "Failed to save. Please try again.") + return + } + + w.Header().Set("HX-Redirect", "/dashboard") + w.WriteHeader(http.StatusOK) +} + func renderOnboardingError(w http.ResponseWriter, message string) { w.Header().Set("Content-Type", "text/html; charset=utf-8") fmt.Fprintf(w, `
%s
`, diff --git a/main.go b/main.go index c8f9de0..db0a887 100644 --- a/main.go +++ b/main.go @@ -213,6 +213,8 @@ func main() { r.Get("/dashboard", eventHandler.Dashboard) r.Get("/onboarding", authHandler.OnboardingPage) r.Post("/onboarding", authHandler.SaveOnboarding) + r.Get("/profile", authHandler.ProfilePage) + r.Post("/profile", authHandler.SaveProfile) r.Post("/events", eventHandler.CreateEvent) r.Put("/events/{id}", eventHandler.UpdateEvent) r.Get("/events/{id}/edit", eventHandler.EditEvent) diff --git a/templates/dashboard.html b/templates/dashboard.html index 8e5d3f6..53aa3ee 100644 --- a/templates/dashboard.html +++ b/templates/dashboard.html @@ -15,8 +15,12 @@

ReceiptNext

- +
+ + +
diff --git a/templates/onboarding.html b/templates/onboarding.html index 8cafe4c..41532ea 100644 --- a/templates/onboarding.html +++ b/templates/onboarding.html @@ -4,7 +4,7 @@ - Welcome - ReceiptNext + {{if .Editing}}Profile{{else}}Welcome{{end}} - ReceiptNext @@ -12,28 +12,42 @@
+ {{if .Editing}} + ← Back +

Edit Profile

+ + {{else}}

Welcome to ReceiptNext

+ {{end}}
+ {{if .Editing}} +

+ Update your name or department. This appears on all expense reports. +

+ {{else}}

Let's set up your profile. This information will appear on your expense reports.

+ {{end}}
-
+
+ value="{{.Name}}" required autofocus>
- +