feat(admin): editable tunable settings UI with grouped sub-sections
Replace the read-only "System Information" card (which listed hardcoded values) with editable HTMX forms, organized so the live vs restart distinction and related settings are visually clear. admin_settings.templ: - AdminSettings signature now takes liveGroups and restartGroups ([]SettingGroup) instead of a flat entry list. - Remove the static System Information list. Render two cards: "Live" (green, applies immediately) and "Restart Required" (warning header, saved but only takes effect after restart). - Within each card, TunableSettingsSection clusters entries into labeled sub-sections by Group (e.g. "Password Quality", "Device Rate Limits", "Login Lockout", "Worker Pool") with uppercase tracked sub-headers. - TunableSettingRow renders an inline HTMX form per setting: a Yes/No select for bools, a number input with min/max for ints, text otherwise, posting to /admin/settings/tunable. Rows show "modified from default" when the value differs from the compiled default. types.go: - Add SettingEntry (template-local mirror of database.SettingEntry, keeps templates from importing database) and SettingGroup. utils.go: - Add GroupTunableSettings: splits a flat, group-sorted entry list into live and restart []SettingGroup buckets preserving source order. utils_test.go covers the multi-group + empty cases. frontend.go: - The /admin/settings page handler now loads entries from the registry, drops the three keys that have dedicated UI cards (default_timezone dropdown, scan_poll_interval_seconds, auto_scan_enabled) so they are not listed twice, groups the rest, and passes liveGroups/restartGroups into the template.
This commit is contained in:
@@ -1024,8 +1024,38 @@ func registerFrontendRoutes(cfg *Config) {
|
||||
}
|
||||
}
|
||||
|
||||
// Load tunable settings entries from the registry. Exclude keys that
|
||||
// already have their own dedicated UI cards (timezone dropdown, scan
|
||||
// settings) so they aren't listed twice.
|
||||
dedicatedUI := map[string]bool{
|
||||
"default_timezone": true,
|
||||
"scan_poll_interval_seconds": true,
|
||||
"auto_scan_enabled": true,
|
||||
}
|
||||
var tunableSettings []templates.SettingEntry
|
||||
if cfg.Settings != nil {
|
||||
for _, e := range cfg.Settings.All() {
|
||||
if dedicatedUI[e.Key] {
|
||||
continue
|
||||
}
|
||||
tunableSettings = append(tunableSettings, templates.SettingEntry{
|
||||
Key: e.Key,
|
||||
Value: e.Value,
|
||||
Type: e.Type,
|
||||
Min: e.Min,
|
||||
Max: e.Max,
|
||||
RequiresRestart: e.RequiresRestart,
|
||||
Category: e.Category,
|
||||
Group: e.Group,
|
||||
Description: e.Description,
|
||||
IsDefault: e.IsDefault,
|
||||
})
|
||||
}
|
||||
}
|
||||
liveGroups, restartGroups := templates.GroupTunableSettings(tunableSettings)
|
||||
|
||||
var buf bytes.Buffer
|
||||
err = templates.AdminSettings(user, systemConfig, scanSettings, "").Render(ctx, &buf)
|
||||
err = templates.AdminSettings(user, systemConfig, scanSettings, liveGroups, restartGroups, "").Render(ctx, &buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user