8d2e4c9bfd
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.
82 lines
4.4 KiB
Markdown
82 lines
4.4 KiB
Markdown
# 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.
|