feat(settings): reorganize the settings hub into domain groups (#1321)

* refactor(settings): split System Limits and regroup the hub

System Limits had grown into a grab-bag of host alert thresholds, Docker
cleanup, and mesh data-plane controls under one mislabeled section. Split it
into Host Alerts, Docker & Storage, and Fleet Mesh, and split Developer into
Developer Diagnostics and Data Retention. Reorganize the sidebar into ten
domain groups: Personal, Access, Infrastructure, Monitoring, Notifications,
Automation, Organization, Security, Operations, Help.

Each section now saves only its own keys, so a concurrent edit in one section
no longer clobbers another. Data Retention sends the audit-log window only on
a paid plan, matching the field's existing visibility, so a Community save no
longer fails on a key the operator cannot set. NumberChip moves to a shared
module and the toggle reuses the existing shared component. The /settings API
is unchanged.

* test(settings): cover registry structure and per-section save payloads

Add structural invariants for the ten-group registry (every item maps to a
real group, ids are unique, the System Limits and Developer splits land in the
right groups with the right gates, renamed labels and the Registries paid gate
hold) and per-section payload tests asserting each split section patches only
its own keys, including the Community path where Data Retention omits the paid
audit-log key.

* docs(settings): document the regrouped settings hub

Rewrite the settings reference for the ten-group layout, replace the System
Limits page with Host Alerts, Docker & Storage, and Fleet Mesh, and document
the prune-on-update, reclaimable-space banner, and mesh auto-recreate settings
that were previously undocumented. Update the Settings navigation breadcrumbs
across the feature docs and refresh the affected screenshots.

* fix(settings): show Access sections as instance-global, not operator-scoped

License, Users, SSO, and API Tokens are instance-global settings but the
masthead scope label rendered them as operator-scoped because it keyed off the
old Identity group. Only Personal sections (account, appearance) are
operator/browser-scoped now; everything else reads as global.

Also add a compile-time exhaustiveness guard to the section switch so a future
SectionId added without a matching case fails the build instead of silently
rendering a blank panel.

* docs(settings): remap remaining settings breadcrumbs to the new groups

Update the navigation breadcrumbs that still pointed at the removed Identity,
Alerts, and Advanced groups: API Tokens and Users now sit under Access, Webhooks
under Automation, Labels under Organization, App Store under Infrastructure,
Appearance under Personal, and scan policies under Security > Vulnerability
Scanning. Correct the settings reference scope note so Access reads as global.

* docs(settings): remap renamed-section breadcrumbs across feature docs

Sweep every feature, operations, getting-started, and reference page for
navigation paths that still named the renamed settings sections, and point them
at the current ones: Security becomes Security > Vulnerability Scanning,
Notifications becomes Notifications > Channels, Routing becomes Notifications >
Notification Routing, and Developer becomes Operations > Developer Diagnostics
(with its retention windows under Operations > Data Retention). App Store moves
under Infrastructure and the four-group overview in the getting-started intro is
rewritten to the ten groups. Separators each page already used are preserved.
This commit is contained in:
Anso
2026-06-05 23:01:37 -04:00
committed by GitHub
parent f7f3afe05a
commit ce08a593d7
53 changed files with 1404 additions and 689 deletions
+103 -49
View File
@@ -1,6 +1,16 @@
import type { SectionId } from './types';
export type SettingsGroupId = 'identity' | 'system' | 'alerts' | 'advanced';
export type SettingsGroupId =
| 'personal'
| 'access'
| 'infrastructure'
| 'monitoring'
| 'notifications'
| 'automation'
| 'organization'
| 'security'
| 'operations'
| 'help';
export interface SettingsGroupMeta {
id: SettingsGroupId;
@@ -10,10 +20,16 @@ export interface SettingsGroupMeta {
}
export const SETTINGS_GROUPS: readonly SettingsGroupMeta[] = [
{ id: 'identity', label: 'Identity', glyph: '\u25C8' },
{ id: 'system', label: 'System', kicker: 'node-scoped', glyph: '\u25C6' },
{ id: 'alerts', label: 'Alerts', glyph: '\u25C7' },
{ id: 'advanced', label: 'Advanced', glyph: '\u25C7' },
{ id: 'personal', label: 'Personal', glyph: '\u25C8' },
{ id: 'access', label: 'Access', glyph: '\u25C8' },
{ id: 'infrastructure', label: 'Infrastructure', glyph: '\u25C6' },
{ id: 'monitoring', label: 'Monitoring', kicker: 'node-scoped', glyph: '\u25C6' },
{ id: 'notifications', label: 'Notifications', glyph: '\u25C7' },
{ id: 'automation', label: 'Automation', glyph: '\u25C7' },
{ id: 'organization', label: 'Organization', glyph: '\u25C7' },
{ id: 'security', label: 'Security', glyph: '\u25C6' },
{ id: 'operations', label: 'Operations', glyph: '\u25C7' },
{ id: 'help', label: 'Help', glyph: '\u25C7' },
];
export type TierGate = 'paid' | null;
@@ -32,9 +48,10 @@ export interface SettingsItemMeta {
}
export const SETTINGS_ITEMS: readonly SettingsItemMeta[] = [
// Personal
{
id: 'account',
group: 'identity',
group: 'personal',
label: 'Account',
description: 'Password, MFA, and session controls for the signed-in operator.',
keywords: ['password', 'mfa', 'two-factor', 'session', 'profile'],
@@ -44,16 +61,17 @@ export const SETTINGS_ITEMS: readonly SettingsItemMeta[] = [
},
{
id: 'appearance',
group: 'identity',
group: 'personal',
label: 'Appearance',
description: 'Theme, accent, density, and display preferences saved to this browser.',
keywords: ['theme', 'dim', 'oled', 'light', 'dark', 'accent', 'color', 'glow', 'border', 'contrast', 'density', 'comfortable', 'compact', 'spacing', 'display'],
tier: null,
scope: 'global',
},
// Access
{
id: 'license',
group: 'identity',
group: 'access',
label: 'License',
description: 'Activation key, plan tier, and seat allocation.',
keywords: ['key', 'activation', 'tier', 'plan', 'seats', 'billing'],
@@ -63,7 +81,7 @@ export const SETTINGS_ITEMS: readonly SettingsItemMeta[] = [
},
{
id: 'users',
group: 'identity',
group: 'access',
label: 'Users',
description: 'Operators, role assignments, and access scopes.',
keywords: ['operators', 'team', 'rbac', 'roles', 'permissions'],
@@ -74,7 +92,7 @@ export const SETTINGS_ITEMS: readonly SettingsItemMeta[] = [
},
{
id: 'sso',
group: 'identity',
group: 'access',
label: 'SSO',
description: 'Single sign-on via SAML or OIDC identity providers.',
keywords: ['saml', 'oidc', 'okta', 'entra', 'azure', 'login'],
@@ -85,7 +103,7 @@ export const SETTINGS_ITEMS: readonly SettingsItemMeta[] = [
},
{
id: 'api-tokens',
group: 'identity',
group: 'access',
label: 'API Tokens',
description: 'Long-lived bearer tokens for CI and scripts.',
keywords: ['bearer', 'automation', 'ci', 'scripts', 'scopes'],
@@ -94,18 +112,30 @@ export const SETTINGS_ITEMS: readonly SettingsItemMeta[] = [
adminOnly: true,
hiddenOnRemote: true,
},
// Infrastructure
{
id: 'system',
group: 'system',
label: 'System Limits',
description: 'Threshold percentages for host CPU, RAM, disk, and crash-loop alerts.',
keywords: ['cpu', 'ram', 'disk', 'limits', 'thresholds', 'alerts'],
id: 'nodes',
group: 'infrastructure',
label: 'Nodes',
description: 'Remote Sencho instances proxied through this control plane.',
keywords: ['fleet', 'remote', 'proxy', 'node', 'cluster'],
tier: null,
scope: 'global',
hiddenOnRemote: true,
},
{
id: 'fleet-mesh',
group: 'infrastructure',
label: 'Fleet Mesh',
description: 'Data-plane network behavior for the cross-node service mesh.',
keywords: ['mesh', 'network', 'recreate', 'fleet', 'routing', 'data plane', 'sencho_mesh'],
tier: null,
scope: 'node',
adminOnly: true,
},
{
id: 'registries',
group: 'system',
group: 'infrastructure',
label: 'Registries',
description: 'Private Docker registries and pull credentials.',
keywords: ['docker', 'ghcr', 'ecr', 'private', 'pull', 'auth'],
@@ -116,7 +146,7 @@ export const SETTINGS_ITEMS: readonly SettingsItemMeta[] = [
},
{
id: 'cloud-backup',
group: 'system',
group: 'infrastructure',
label: 'Cloud Backup',
description: 'Mirror fleet snapshots to Sencho Cloud Backup or any S3-compatible storage.',
keywords: ['cloud', 'backup', 'snapshot', 's3', 'r2', 'minio', 'storage', 'offsite'],
@@ -126,28 +156,47 @@ export const SETTINGS_ITEMS: readonly SettingsItemMeta[] = [
hiddenOnRemote: true,
},
{
id: 'nodes',
group: 'system',
label: 'Nodes',
description: 'Remote Sencho instances proxied through this control plane.',
keywords: ['fleet', 'remote', 'proxy', 'node', 'cluster'],
id: 'app-store',
group: 'infrastructure',
label: 'App Store',
description: 'Template registry URL and featured-catalog source.',
keywords: ['templates', 'registry', 'catalog', 'featured'],
tier: null,
scope: 'global',
hiddenOnRemote: true,
scope: 'node',
},
// Monitoring
{
id: 'host-alerts',
group: 'monitoring',
label: 'Host Alerts',
description: 'Alert thresholds for host CPU, RAM, and disk, plus suppression cadence and container crash capture.',
keywords: ['cpu', 'ram', 'disk', 'thresholds', 'alerts', 'suppression', 'crash', 'host', 'limits'],
tier: null,
scope: 'node',
},
{
id: 'docker-storage',
group: 'monitoring',
label: 'Docker & Storage',
description: 'Reclaimable-space alerts and Docker image cleanup after updates.',
keywords: ['docker', 'janitor', 'prune', 'reclaim', 'storage', 'images', 'cleanup', 'dangling'],
tier: null,
scope: 'node',
},
// Notifications
{
id: 'notifications',
group: 'alerts',
label: 'Notifications',
description: 'In-app toasts and browser push for stack, container, and system events.',
keywords: ['toasts', 'push', 'events', 'alerts', 'inbox'],
group: 'notifications',
label: 'Channels',
description: 'Discord, Slack, and custom webhook destinations for Sencho alerts.',
keywords: ['discord', 'slack', 'webhook', 'channels', 'destinations', 'alerts'],
tier: null,
scope: 'node',
},
{
id: 'notification-routing',
group: 'alerts',
label: 'Routing',
group: 'notifications',
label: 'Notification Routing',
description: 'Rules that steer alerts to the right channel based on severity or label.',
keywords: ['rules', 'routing', 'channels', 'severity', 'labels'],
tier: null,
@@ -155,9 +204,10 @@ export const SETTINGS_ITEMS: readonly SettingsItemMeta[] = [
adminOnly: true,
hiddenOnRemote: true,
},
// Automation
{
id: 'webhooks',
group: 'alerts',
group: 'automation',
label: 'Webhooks',
description: 'Incoming HMAC-signed HTTP triggers that run stack actions from CI/CD pipelines.',
keywords: ['webhook', 'incoming', 'trigger', 'ci', 'cd', 'pipeline', 'deploy', 'hmac', 'signature', 'action'],
@@ -165,46 +215,49 @@ export const SETTINGS_ITEMS: readonly SettingsItemMeta[] = [
scope: 'global',
hiddenOnRemote: true,
},
// Organization
{
id: 'labels',
group: 'advanced',
group: 'organization',
label: 'Labels',
description: 'Per-node labels for stacks and containers.',
keywords: ['labels', 'tags', 'palette', 'organisation'],
tier: null,
scope: 'node',
},
// Security
{
id: 'security',
group: 'advanced',
label: 'Security',
group: 'security',
label: 'Vulnerability Scanning',
description: 'Image scanning, suppressions, and posture defaults.',
keywords: ['scan', 'cve', 'trivy', 'suppressions', 'hardening'],
keywords: ['scan', 'cve', 'trivy', 'suppressions', 'hardening', 'vulnerability', 'misconfig'],
tier: null,
scope: 'node',
adminOnly: true,
},
// Operations
{
id: 'developer',
group: 'advanced',
label: 'Developer',
description: 'Retention windows and debug modes.',
keywords: ['retention', 'logs', 'metrics', 'debug', 'developer'],
id: 'data-retention',
group: 'operations',
label: 'Data Retention',
description: 'How long to keep container metrics, notification logs, scan history, and audit entries.',
keywords: ['retention', 'metrics', 'logs', 'scans', 'audit', 'history', 'prune', 'window'],
tier: null,
scope: 'node',
},
{
id: 'app-store',
group: 'advanced',
label: 'App Store',
description: 'Template registry URL and featured-catalog source.',
keywords: ['templates', 'registry', 'catalog', 'featured'],
id: 'developer',
group: 'operations',
label: 'Developer Diagnostics',
description: 'Developer mode for real-time metrics streams and verbose debug diagnostics.',
keywords: ['developer', 'debug', 'diagnostics', 'metrics', 'verbose'],
tier: null,
scope: 'node',
},
{
id: 'recovery',
group: 'advanced',
group: 'operations',
label: 'Recovery',
description: 'System health snapshot, safe recovery actions, and emergency command-line reference.',
keywords: ['recovery', 'safe mode', 'diagnostics', 'health', 'emergency', 'cli', 'reset', 'backup', 'restore'],
@@ -213,9 +266,10 @@ export const SETTINGS_ITEMS: readonly SettingsItemMeta[] = [
adminOnly: true,
hiddenOnRemote: true,
},
// Help
{
id: 'support',
group: 'advanced',
group: 'help',
label: 'Support',
description: 'Diagnostics bundle, docs links, and contact channels.',
keywords: ['help', 'diagnostics', 'bundle', 'docs', 'contact'],
@@ -224,7 +278,7 @@ export const SETTINGS_ITEMS: readonly SettingsItemMeta[] = [
},
{
id: 'about',
group: 'advanced',
group: 'help',
label: 'About',
description: 'Build metadata, release notes, and licence attributions.',
keywords: ['version', 'build', 'release', 'attributions'],