Add NavMenu settings/presets UI and profile HTTP routes (ticket 011)

Adds thin HTTP routes for profile CRUD and colour-scheme updates
(GET/PATCH /api/settings, GET/POST /api/settings/profiles,
DELETE /api/settings/profiles/:name, POST /api/settings/profiles/:name/activate)
following the existing CSS-route pattern in api.rs, with activate hot-swapping
the running engine's declared params/metrics so switching profiles needs no
restart. Builds out the NavMenu frontend with Settings (colour scheme picker,
CSS import/list/delete) and Presets (list/create/switch/delete profiles with
declaration counts) panels, and a useSettingsPanel hook that applies the
active colour scheme and imported CSS to the document live.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XFUNZwCvz3kcod3352ZR2o
This commit is contained in:
Hugh Kaznowski
2026-09-05 13:21:31 +01:00
co-authored by Claude Sonnet 5
parent 57d23c62e9
commit 801ac36b78
6 changed files with 772 additions and 26 deletions
+59 -1
View File
@@ -1,6 +1,7 @@
use crate::data_value::{MetricPrimitiveKind, RegexToMetricPrimitiveMatcher};
use crate::metric_primitive::MetricPrimitive;
use crate::settings::CssEntry;
use crate::profile::Profile;
use crate::settings::{ColorScheme, CssEntry, Settings};
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
@@ -183,3 +184,60 @@ impl From<&CssEntry> for CssEntryDto {
}
}
}
/// Response shape for `GET /api/settings` and `PATCH /api/settings` (ticket
/// 011): the subset of `Settings` the NavMenu UI needs to render a colour
/// scheme picker and know which profile is active. `css_entries` is
/// deliberately omitted here since it already has its own dedicated
/// `GET /api/settings/css` endpoint (ticket 010).
#[derive(Debug, Clone, Serialize)]
pub struct SettingsDto {
pub color_scheme: ColorScheme,
pub active_profile: Option<String>,
}
impl From<&Settings> for SettingsDto {
fn from(settings: &Settings) -> Self {
SettingsDto {
color_scheme: settings.color_scheme,
active_profile: settings.profiles.active_profile.clone(),
}
}
}
/// Request body for `PATCH /api/settings`. Only `color_scheme` is
/// updatable for now; `None` leaves the current value untouched (so a
/// partial body doesn't reset other fields).
#[derive(Debug, Deserialize)]
pub struct UpdateSettingsRequest {
pub color_scheme: Option<ColorScheme>,
}
/// Summary of one profile for `GET /api/settings/profiles` (ticket 011):
/// just the name and declaration/dashboard counts the Presets panel needs to
/// render a list, rather than the full param/metric declaration bodies.
#[derive(Debug, Clone, Serialize)]
pub struct ProfileSummaryDto {
pub name: String,
pub param_count: usize,
pub metric_count: usize,
pub dashboard_count: usize,
}
impl From<&Profile> for ProfileSummaryDto {
fn from(profile: &Profile) -> Self {
ProfileSummaryDto {
name: profile.name.clone(),
param_count: profile.params.len(),
metric_count: profile.metrics.len(),
dashboard_count: profile.dashboards.len(),
}
}
}
/// Request body for `POST /api/settings/profiles`: creates a new, empty
/// profile with this name (see `ProfileStore::create`).
#[derive(Debug, Deserialize)]
pub struct CreateProfileRequest {
pub name: String,
}