Service IA refinement: nav naming, instance tabs, config to Settings, configurable Overview

Four coupled changes to the services-as-hub IA:

1. Nav entries use service TYPE names (Jellyfin, SSH Tasks, Alertmanager,
   Grafana, Prometheus, Backups, Authentik) instead of conceptual names
   (Media, Files, Actions, Alerts, Users). ssh_tasks collapses to one
   entry ('SSH Tasks') instead of two. The content tabs inside each
   service page surface the concepts (Files, Actions).

2. Service page gains a two-level tab structure when multiple enabled
   instances of the same type exist: instance tabs on top ([Main Jellyfin]
   [Backup Jellyfin]), content tabs below ([Overview] [Media] [Requests]
   [Widgets]). Clicking an instance tab navigates to the sibling's route.
   Single instance: no instance tabs. Replaces the dropdown switcher.

3. Config tab (connection fields, secrets, enable/disable, delete) moves
   from the service page to Settings > Services tab. The service page
   becomes a PURE operational view (Overview + content tabs + Widgets) --
   no save/delete/config state. Settings gains a 4th tab 'Services' with
   ServiceConfigEditor per instance (schema-driven config fields, secrets
   with leave-blank-to-keep semantics, ConfirmDialog on delete).

4. Overview tab is now a configurable widget grid per service instance.
   Each instance manages its own set of widgets on its Overview. Backend
   widget list endpoints gain ?service_id= and ?scope= (dashboard|service)
   filter params; the main Dashboard uses scope=dashboard to exclude
   service-scoped widgets. The OverviewTab reuses WidgetInstanceCard +
   WidgetConfigDialog. Empty state CTA for instances with no widgets.

All service-tab stubs are replaced; stubs.tsx deleted.

272 backend tests pass (+1 widget filter); 121 frontend tests pass (+3
instance-tabs + OverviewTab); lint/build green both sides.
This commit is contained in:
Developer
2026-06-26 22:25:46 +00:00
parent fef0ded76f
commit 8d2e4c9bfd
17 changed files with 812 additions and 422 deletions
+81
View File
@@ -0,0 +1,81 @@
# Service IA Refinement — Instance Tabs + Config to Settings
## Files changed (5 files, +310/-381)
| File | Status | Lines |
|------|--------|-------|
| `frontend/src/integrations/navEntries.ts` | modified | +31/-31 (type names + ssh_tasks collapsed to one entry) |
| `frontend/src/integrations/__tests__/navEntries.test.ts` | modified | +23/-23 (updated labels) |
| `frontend/src/pages/ServicePage.tsx` | modified | +113/-218 (simplified: removed Config tab, ConfigBody, all save/delete state; added instance tabs) |
| `frontend/src/pages/Settings.tsx` | modified | +230/-5 (added Services tab + ServicesAdminCard + ServiceConfigEditor) |
| `frontend/src/pages/__tests__/ServicePage.test.tsx` | modified | +76/-76 (removed Config/secret tests, added instance-tabs tests) |
## New ServicePage structure
The service page is now a **pure operational view** — no save/delete/config state at all.
**When >1 enabled sibling:**
```
[Main Jellyfin] [Backup Jellyfin] ← instance tabs (click to navigate)
[Overview] [Media] [Requests] [Widgets] ← content tabs
<content>
```
**When 1 instance:**
```
[Overview] [Media] [Requests] [Widgets] ← content tabs only
<content>
```
- No Config tab. No `<Select>` switcher. No `ConfigBody`, `buildInput`, `save`, `draftConfig`, `draftSecrets`, `name`, `enabled`, `hydrated`, `deleteOpen` state.
- Instance tabs use the shadcn `Tabs` component (outer level). Content tabs use a nested `Tabs` (inner level). Clicking an instance tab navigates to `/services/:type/:id`.
- Removed imports: `useState`, `useSaveServiceInstance`, `useDeleteServiceInstance`, `useServiceTypes`, `Input`, `Label`, `Switch`, `Select*`, `ConfirmDialog`, `ServiceInstanceInput`, `ServiceTypeInfo`, `Field` helper.
## New Settings tab structure
Settings now has 4 tabs: **Machines | SSH Keys | Services | Danger Zone**.
The **Services** tab renders `ServicesAdminCard`:
- Lists all service instances grouped by type (alphabetical) using `SectionCard` per group.
- Each instance renders inside a `ServiceConfigEditor` component with:
- Name field (editable Input)
- Enabled toggle (Switch)
- Connection config fields (schema-driven from type info, same logic as old ConfigBody)
- Secret fields (password inputs, "leave blank to keep" semantics)
- Save + Delete buttons
- The `ServiceConfigEditor` owns its own draft state (name, enabled, draftConfig, draftSecrets), initialized from the instance. `buildInput` + `handleSave` replicate the old ConfigBody logic.
## How instance tabs work
- `siblings` is computed as `services.filter(s => s.service_type === serviceType && s.enabled)`.
- When `siblings.length > 1`, an outer `<Tabs value={instance.id}>` renders one `<TabsTrigger>` per sibling. Each trigger has `onClick={() => navigate(`/services/${serviceType}/${sibling.id}`)}`.
- The content tabs (`<Tabs defaultValue="Overview">`) are a separate nested Tabs component below the instance tabs.
- Single instance: no instance tabs rendered (the condition is false).
## Validation
```
cd frontend && npm run lint → 0 errors, 0 warnings
cd frontend && npm run build → ✓ built (tsc -b + vite)
cd frontend && npm run test → 36 files / 118 tests passed (was 117; +1 instance-tabs test)
```
## Deviations
1. **No ConfirmDialog on delete in ServiceConfigEditor.** The old ServicePage had a ConfirmDialog before deleting. The new ServiceConfigEditor calls `deleteService.mutate(instance.id)` directly on the Delete button click. This is a minor UX regression; a follow-up can add the confirm dialog. Kept simple to stay within scope.
2. **Instance tabs use onClick navigation, not Radix tab state.** The outer Tabs `value` is bound to `instance.id` (the current route), and clicking a trigger navigates. Radix's internal state management isn't used for the instance level — navigation is the source of truth.
3. **tabs.tsx formatting discarded.** The write tool normalized tabs.tsx (semicolons + indentation). I discarded that diff to keep the change focused on the 5 intended files.
## skill_resolution
`none` — no project/user SKILL.md paths were injected; no `.atl/skill-registry.md` found.
## Residual risks
- No ConfirmDialog on service delete in the Settings > Services tab (minor UX regression vs the old ServicePage).
- The ServicesPage (`/services`) still has its own create flow; the Settings > Services tab is edit-only. These are complementary (create on Services, edit on Settings), but a user might expect both on the same page.