package handlers import ( "net/http" c "github.com/juanatsap/cv-site/internal/constants" "github.com/juanatsap/cv-site/internal/httputil" "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 !httputil.RequirePost(w, r) { return } // Get current preferences from context (set by middleware, already migrated) prefs := middleware.GetPreferences(r) currentLength := prefs.CVLength // Toggle state newLength := c.CVLengthLong if currentLength == c.CVLengthLong { newLength = c.CVLengthShort } // Save new state middleware.SetPreferenceCookie(w, c.CookieCVLength, 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 !httputil.RequirePost(w, r) { return } // Get current preferences from context (set by middleware, already migrated) prefs := middleware.GetPreferences(r) currentIcons := prefs.CVIcons // Toggle state newIcons := c.CVIconsHide if currentIcons == c.CVIconsHide { newIcons = c.CVIconsShow } // Save new state middleware.SetPreferenceCookie(w, c.CookieCVIcons, 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) { lang, ok := httputil.LangOrError(r) if !ok { HandleError(w, r, BadRequestError("Unsupported language. Use 'en' or 'es'")) return } // Save language preference middleware.SetPreferenceCookie(w, c.CookieCVLanguage, 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 == c.CVLengthLong { data["CVLengthClass"] = "cv-long" } else { data["CVLengthClass"] = "cv-short" } data["ShowIcons"] = (prefs.CVIcons == c.CVIconsShow) data["ThemeClean"] = (prefs.CVTheme == c.CVThemeClean) // 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(c.HeaderContentType, c.ContentTypeHTML) 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 !httputil.RequirePost(w, r) { return } // Get current preferences from context (set by middleware) prefs := middleware.GetPreferences(r) currentTheme := prefs.CVTheme // Toggle state newTheme := c.CVThemeClean if currentTheme == c.CVThemeClean { newTheme = c.CVThemeDefault } // Save new state middleware.SetPreferenceCookie(w, c.CookieCVTheme, 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) }