Wire up named dashboards end-to-end. The /d/:slug route (already
referenced by useDashboards-driven nav entries from slice 4) now
renders NamedDashboardPage instead of 404ing.
Backend: GET /api/dashboards/slug/:slug resolves a dashboard by slug
(404 when not found). The store method existed from slice 3; only the
router endpoint was missing.
NamedDashboardPage: renders a named dashboard's payload -- an ordered
list of inline items with a type discriminator. This slice ships
'link' items (PinnedServiceLink -> navigates to /services/:type/:id).
Full widget composition on named dashboards is a follow-up; the main
Dashboard keeps the rich WidgetConfigDialog.
PinnedServiceLink: card component (lucide icon + label) navigating to
a service page or specific tab.
Dashboard management UI on the Services page (DashboardManagementCard):
list existing dashboards with reorder/delete, create via dialog
(label -> auto-slug), add pinned service links per dashboard (label +
enabled-service dropdown). Placed on Services (the admin hub) rather
than Settings to avoid an extra nav trip.
Dashboard payload model: inline items ({ items: [{ type: 'link', label,
target }] }) -- self-contained, no separate widget-instance fetch
needed. The type discriminator allows future widget items without
breaking existing payloads.
Tests: NamedDashboardPage (renders links, not-found state),
PinnedServiceLink (renders + navigates). 112 frontend tests pass (+6);
271 backend tests pass (no regression); lint/build green both sides.
Refs openspec/changes/services-as-hub-ia/ (spec R5, tasks slice 10).
PR 4a of the runtime service registry change.
- Remove addon pages (/addons/:addonId, AddonPage, addons/*) superseded by
service pages.
- Remove grafana_url/prometheus_url from backend config, compose, .env.example,
and README (URLs now live on service records; VITE_ frontend deep-link vars
retained).
- Add Services page (/services) with create/list/delete + sidebar nav, so
services are configurable in the tool itself and service pages are reachable.
- Update docs/REQUIREMENTS.md service-registry section; add CHANGELOG.md with
the breaking-upgrade note (MANAGE_ENCRYPTION_KEY required; grafana/prometheus
env vars removed; default widget seeding removed).
Verification: backend ruff clean, pytest 222 passed; frontend lint 0 errors,
build success, 70 tests passed.