diff --git a/dashboards/tracker-web/e2e/tracker.spec.ts b/dashboards/tracker-web/e2e/tracker.spec.ts index abb0d282..6b11ecab 100644 --- a/dashboards/tracker-web/e2e/tracker.spec.ts +++ b/dashboards/tracker-web/e2e/tracker.spec.ts @@ -275,6 +275,25 @@ test.describe('Tracker — Authenticated dashboard', () => { await expect(page.getByText('42')).toBeVisible(); await expect(page.getByText('admin@example.com')).toBeVisible(); }); + + test('renders settings for admin configuration', async ({ page }) => { + await page.route('**/api/auth/me', (route: Route) => + route.fulfill({ + json: { id: 'u1', email: 'admin@example.com', role: 'admin', displayName: 'Admin' }, + }) + ); + + await page.addInitScript(() => localStorage.setItem('tracker_token', 'fake-e2e-token')); + await page.goto('/dashboard/settings'); + await expect(page.getByRole('heading', { name: 'Settings' })).toBeVisible(); + await expect(page.getByRole('heading', { name: 'Product context' })).toBeVisible(); + await expect(page.getByLabel('Default product ID')).toBeVisible(); + await expect(page.getByText('/api/tracker/[...path]')).toBeVisible(); + await expect(page.getByRole('link', { name: /open public roadmap/i })).toHaveAttribute( + 'href', + '/roadmap' + ); + }); }); // ── Public roadmap (mocked) ───────────────────────────────────────── diff --git a/dashboards/tracker-web/src/app/dashboard/layout.tsx b/dashboards/tracker-web/src/app/dashboard/layout.tsx index 664bdbf1..9411d969 100644 --- a/dashboards/tracker-web/src/app/dashboard/layout.tsx +++ b/dashboards/tracker-web/src/app/dashboard/layout.tsx @@ -24,6 +24,7 @@ const NAV_ITEMS = [ { href: '/dashboard/items', label: 'Items' }, { href: '/dashboard/board', label: 'Board' }, { href: '/dashboard/fleet', label: 'Fleet' }, + { href: '/dashboard/settings', label: 'Settings' }, ]; /** Open the ⌘K command palette by replaying the global hotkey. */ diff --git a/dashboards/tracker-web/src/app/dashboard/settings/page.tsx b/dashboards/tracker-web/src/app/dashboard/settings/page.tsx new file mode 100644 index 00000000..33b714f0 --- /dev/null +++ b/dashboards/tracker-web/src/app/dashboard/settings/page.tsx @@ -0,0 +1,109 @@ +'use client'; + +import Link from 'next/link'; +import { useEffect, useState } from 'react'; + +const DEFAULT_PRODUCT = 'tracker'; + +export default function SettingsPage() { + const [productId, setProductId] = useState(DEFAULT_PRODUCT); + const [saved, setSaved] = useState(false); + + useEffect(() => { + const selected = localStorage.getItem('tracker_selected_product'); + if (selected) setProductId(selected); + }, []); + + function saveProductContext() { + localStorage.setItem('tracker_selected_product', productId.trim() || DEFAULT_PRODUCT); + window.dispatchEvent(new Event('tracker:product-changed')); + setSaved(true); + window.setTimeout(() => setSaved(false), 2500); + } + + return ( +
+ Admin +
++ Configure the tracker dashboard context, public roadmap links, and integration surfaces + used by humans, agents, and external systems. +
++ Tracker is product-aware. This value is sent as the selected product context for + internal tracker calls. +
+ + setProductId(event.target.value)} + className="mt-2 w-full rounded-lg border border-border bg-background px-3 py-2 text-sm" + placeholder="tracker" + /> + + {saved ?Saved product context.
: null} ++ Share the roadmap and submission status pages with public users without requiring a + login. +
++ These are the web-facing route groups that connect the dashboard to platform-service. +
+{route}
+ {description}
+