feat: 5 dramatic extra themes (neon/terminal/blueprint/sunset) + board/review/layout polish
- Themes: add neon (cyber gradient), terminal (green CRT), blueprint (graph paper) and sunset (vaporwave) alongside the default Light (Y2K paper) and Dark. Theme toggle now cycles all; profile selector lists them; server validates against a shared whitelist. - Bounty cards now show the project (customer) name. - Remove the celebratory emoji from the empty Review Queue. - Full-width h1 underline rule; My Tasks columns stack vertically for more room per task; card action buttons wrap/right-align so they never overflow. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -15,6 +15,12 @@ import (
|
||||
"bountyboard/internal/store"
|
||||
)
|
||||
|
||||
// validThemes are the selectable UI themes (must match web/static/js/theme.js).
|
||||
var validThemes = map[string]bool{
|
||||
"light": true, "dark": true, "neon": true,
|
||||
"terminal": true, "blueprint": true, "sunset": true,
|
||||
}
|
||||
|
||||
func (s *Server) routesProfile(mux *http.ServeMux) {
|
||||
mux.Handle("GET /api/v1/profile", s.requireAuth(http.HandlerFunc(s.handleGetProfile)))
|
||||
mux.Handle("PATCH /api/v1/profile", s.authed(s.handlePatchProfile))
|
||||
@@ -71,8 +77,8 @@ func (s *Server) handlePatchProfile(w http.ResponseWriter, r *http.Request) {
|
||||
if req.Settings != nil {
|
||||
if req.Settings.Theme != nil {
|
||||
theme := *req.Settings.Theme
|
||||
if theme != "light" && theme != "dark" {
|
||||
writeError(w, http.StatusBadRequest, "invalid_theme", "theme must be light or dark")
|
||||
if !validThemes[theme] {
|
||||
writeError(w, http.StatusBadRequest, "invalid_theme", "unknown theme")
|
||||
return
|
||||
}
|
||||
set["settings.theme"] = theme
|
||||
|
||||
@@ -108,7 +108,7 @@ func TestProfilePatchAndThemePersistence(t *testing.T) {
|
||||
|
||||
// invalid theme rejected
|
||||
resp = patchJSON(t, c, ts.URL+"/api/v1/profile", map[string]any{
|
||||
"settings": map[string]any{"theme": "neon"},
|
||||
"settings": map[string]any{"theme": "rainbow-unicorn"},
|
||||
}, csrf)
|
||||
if resp.StatusCode != http.StatusBadRequest {
|
||||
t.Fatalf("invalid theme: %d", resp.StatusCode)
|
||||
|
||||
Reference in New Issue
Block a user