e0d445b92a
Remove empty toggle templates (length-toggle.html, theme-toggle.html, logo-toggle.html) that were just placeholders. The frontend uses hx-swap="none" so the response body was always ignored anyway. Now the handlers: - Set the preference cookie - Return 204 No Content immediately - Hyperscript handles the UI state toggle on the frontend This removes unnecessary template rendering overhead and cleans up dead code. Tests updated to expect 204 instead of 200.
141 lines
4.1 KiB
Go
141 lines
4.1 KiB
Go
package handlers
|
|
|
|
import (
|
|
"net/http"
|
|
|
|
"github.com/juanatsap/cv-site/internal/middleware"
|
|
)
|
|
|
|
// ==============================================================================
|
|
// HTMX TOGGLE HANDLERS
|
|
// These handlers manage user preferences (length, icons, language, theme)
|
|
// using atomic out-of-band swaps for a smooth UX
|
|
// ==============================================================================
|
|
|
|
// ToggleLength handles CV length toggle (short/long) using atomic out-of-band swaps
|
|
func (h *CVHandler) ToggleLength(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
// Get current preferences from context (set by middleware, already migrated)
|
|
prefs := middleware.GetPreferences(r)
|
|
currentLength := prefs.CVLength
|
|
|
|
// Toggle state
|
|
newLength := "long"
|
|
if currentLength == "long" {
|
|
newLength = "short"
|
|
}
|
|
|
|
// Save new state
|
|
middleware.SetPreferenceCookie(w, "cv-length", newLength)
|
|
|
|
// Return 204 No Content - frontend uses hx-swap="none" so response body is ignored
|
|
// The cookie is set and hyperscript handles the UI state toggle
|
|
w.WriteHeader(http.StatusNoContent)
|
|
}
|
|
|
|
// ToggleIcons handles icon visibility toggle using atomic out-of-band swaps
|
|
func (h *CVHandler) ToggleIcons(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
// Get current preferences from context (set by middleware, already migrated)
|
|
prefs := middleware.GetPreferences(r)
|
|
currentIcons := prefs.CVIcons
|
|
|
|
// Toggle state
|
|
newIcons := "hide"
|
|
if currentIcons == "hide" {
|
|
newIcons = "show"
|
|
}
|
|
|
|
// Save new state
|
|
middleware.SetPreferenceCookie(w, "cv-icons", newIcons)
|
|
|
|
// Return 204 No Content - frontend uses hx-swap="none" so response body is ignored
|
|
// The cookie is set and hyperscript handles the UI state toggle
|
|
w.WriteHeader(http.StatusNoContent)
|
|
}
|
|
|
|
// SwitchLanguage handles language switching with atomic updates
|
|
// Uses HTMX out-of-band swaps to update both the language selector buttons
|
|
// and all CV content wrappers in a single response
|
|
func (h *CVHandler) SwitchLanguage(w http.ResponseWriter, r *http.Request) {
|
|
// Get language from query parameter
|
|
lang := r.URL.Query().Get("lang")
|
|
if lang == "" {
|
|
lang = "en"
|
|
}
|
|
|
|
// Validate language
|
|
if lang != "en" && lang != "es" {
|
|
HandleError(w, r, BadRequestError("Unsupported language. Use 'en' or 'es'"))
|
|
return
|
|
}
|
|
|
|
// Save language preference
|
|
middleware.SetPreferenceCookie(w, "cv-language", lang)
|
|
|
|
// Prepare template data
|
|
data, err := h.prepareTemplateData(lang)
|
|
if err != nil {
|
|
HandleError(w, r, DataLoadError(err, "CV"))
|
|
return
|
|
}
|
|
|
|
// Get current preferences from context (set by middleware)
|
|
prefs := middleware.GetPreferences(r)
|
|
|
|
// Add preferences to data
|
|
if prefs.CVLength == "long" {
|
|
data["CVLengthClass"] = "cv-long"
|
|
} else {
|
|
data["CVLengthClass"] = "cv-short"
|
|
}
|
|
data["ShowIcons"] = (prefs.CVIcons == "show")
|
|
data["ThemeClean"] = (prefs.CVTheme == "clean")
|
|
|
|
// Render language-switch template with out-of-band swaps
|
|
tmpl, err := h.templates.Render("language-switch.html")
|
|
if err != nil {
|
|
HandleError(w, r, TemplateError(err, "language-switch.html"))
|
|
return
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
|
if err := tmpl.Execute(w, data); err != nil {
|
|
HandleError(w, r, TemplateError(err, "language-switch.html"))
|
|
return
|
|
}
|
|
}
|
|
|
|
// ToggleTheme handles theme toggle (default/clean) using atomic out-of-band swaps
|
|
func (h *CVHandler) ToggleTheme(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
// Get current preferences from context (set by middleware)
|
|
prefs := middleware.GetPreferences(r)
|
|
currentTheme := prefs.CVTheme
|
|
|
|
// Toggle state
|
|
newTheme := "clean"
|
|
if currentTheme == "clean" {
|
|
newTheme = "default"
|
|
}
|
|
|
|
// Save new state
|
|
middleware.SetPreferenceCookie(w, "cv-theme", newTheme)
|
|
|
|
// Return 204 No Content - frontend uses hx-swap="none" so response body is ignored
|
|
// The cookie is set and hyperscript handles the UI state toggle
|
|
w.WriteHeader(http.StatusNoContent)
|
|
}
|