Skip to content

Commit 303aeb7

Browse files
atulmguptaCopilot
andcommitted
fix(api): persist time_format_default + chart_palette in settings
The Settings struct was missing TimeFormatDefault (Phase-45/22) and ChartPalette (Phase-45/23) fields. When the Appearance settings page PUT them, json.NewDecoder silently dropped the unknown JSON keys, the Upsert never wrote the rows, and the next GET returned the old value - so the radio cards appeared inert when clicked. Wires both fields through the four canonical settings layers: - models.Settings struct (with phase-tagged doc comments) - settingsDefaults() (relative + cb_safe) - applySettingsRow() switch cases - Upsert() textRows - Update() handler validation (rejects values outside the typed enum) No migration needed: the settings key/value table is forward- compatible and will create the rows on first Upsert. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 25765d1 commit 303aeb7

3 files changed

Lines changed: 36 additions & 0 deletions

File tree

internal/api/settings_handler.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,16 @@ func (h *SettingsHandler) Update(w http.ResponseWriter, r *http.Request) {
9696
writeError(w, http.StatusBadRequest, "ui_density must be 'compact', 'comfortable', or 'spacious'")
9797
return
9898
}
99+
validTimeFormat := map[string]bool{"relative": true, "absolute": true}
100+
if s.TimeFormatDefault != "" && !validTimeFormat[s.TimeFormatDefault] {
101+
writeError(w, http.StatusBadRequest, "time_format_default must be 'relative' or 'absolute'")
102+
return
103+
}
104+
validChartPalette := map[string]bool{"cb_safe": true, "neon": true}
105+
if s.ChartPalette != "" && !validChartPalette[s.ChartPalette] {
106+
writeError(w, http.StatusBadRequest, "chart_palette must be 'cb_safe' or 'neon'")
107+
return
108+
}
99109

100110
// Record gas price change in history if price or unit changed
101111
if s.GasPricePerUnit > 0 {

internal/database/settings_repo.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ func settingsDefaults() *models.Settings {
6565
TabBadgeEnabled: true,
6666
CriticalFlashEnabled: true,
6767
UIDensity: "comfortable",
68+
TimeFormatDefault: "relative",
69+
ChartPalette: "cb_safe",
6870
}
6971
}
7072

@@ -209,6 +211,14 @@ func applySettingsRow(s *models.Settings, key, _ string, vText *string, vNum *fl
209211
if vText != nil {
210212
s.UIDensity = *vText
211213
}
214+
case "time_format_default":
215+
if vText != nil {
216+
s.TimeFormatDefault = *vText
217+
}
218+
case "chart_palette":
219+
if vText != nil {
220+
s.ChartPalette = *vText
221+
}
212222
}
213223
}
214224

@@ -282,6 +292,8 @@ func (r *SettingsRepo) Upsert(ctx context.Context, s *models.Settings) error {
282292
{"tz_display_default", s.TzDisplayDefault},
283293
{"timezone_user", s.TimezoneUser},
284294
{"ui_density", s.UIDensity},
295+
{"time_format_default", s.TimeFormatDefault},
296+
{"chart_palette", s.ChartPalette},
285297
}
286298
numRows := []rowNum{
287299
{"base_cost_per_kwh", s.BaseCostPerKWh},

internal/models/system.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,20 @@ type Settings struct {
367367
// "comfortable" so existing users see no visual change.
368368
// (Phase 40 / 44.)
369369
UIDensity string `json:"ui_density"`
370+
// TimeFormatDefault selects the default rendering of <TimeStamp>
371+
// when no explicit `format` prop is set. One of "relative"
372+
// (e.g. "2h ago", best for activity feeds) or "absolute"
373+
// (e.g. "Nov 12, 13:42", best for trip planning + event
374+
// correlation). The alternate format is always shown on hover.
375+
// Defaults to "relative". (Phase 45 / 22.)
376+
TimeFormatDefault string `json:"time_format_default"`
377+
// ChartPalette selects the colour ramp used by multi-series
378+
// charts. One of "cb_safe" (Okabe-Ito, distinguishable for all
379+
// CVD types — the default) or "neon" (bright cyan/magenta legacy
380+
// palette). Drives the reactive `useChartPalette()` hook so
381+
// consumers re-render with the new colours when the user toggles.
382+
// (Phase 45 / 23.)
383+
ChartPalette string `json:"chart_palette"`
370384
}
371385

372386
// Embedding mirrors the post-migration `embeddings` schema (pgvector-backed).

0 commit comments

Comments
 (0)