Files
manage/.pi-tmp/refine-23-out.md
Developer 8d2e4c9bfd 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.
2026-06-26 22:25:46 +00:00

4.4 KiB

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.