feat(tracker-web): add admin settings page
Some checks failed
Publish @bytelyst/* packages / publish (push) Failing after 11s
CI — Common Platform / Build, Test & Typecheck (push) Successful in 38s

This commit is contained in:
Saravana Kumar 2026-05-30 19:53:16 +00:00
parent 3a6ed3a5f8
commit ccfbfd194a
3 changed files with 129 additions and 0 deletions

View File

@ -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) ─────────────────────────────────────────

View File

@ -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. */

View File

@ -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 (
<div className="space-y-8">
<header>
<p className="text-sm font-medium uppercase tracking-[0.2em] text-muted-foreground">
Admin
</p>
<h1 className="mt-2 text-3xl font-bold tracking-tight">Settings</h1>
<p className="mt-2 max-w-3xl text-muted-foreground">
Configure the tracker dashboard context, public roadmap links, and integration surfaces
used by humans, agents, and external systems.
</p>
</header>
<section className="grid gap-4 lg:grid-cols-2">
<div className="rounded-2xl border bg-card p-6 shadow-sm">
<h2 className="text-xl font-semibold">Product context</h2>
<p className="mt-2 text-sm text-muted-foreground">
Tracker is product-aware. This value is sent as the selected product context for
internal tracker calls.
</p>
<label htmlFor="default-product-id" className="mt-5 block text-sm font-medium">
Default product ID
</label>
<input
id="default-product-id"
value={productId}
onChange={event => setProductId(event.target.value)}
className="mt-2 w-full rounded-lg border border-border bg-background px-3 py-2 text-sm"
placeholder="tracker"
/>
<button
type="button"
onClick={saveProductContext}
className="mt-4 rounded-lg bg-primary px-4 py-2 text-sm font-semibold text-primary-foreground"
>
Save product context
</button>
{saved ? <p className="mt-3 text-sm text-green-500">Saved product context.</p> : null}
</div>
<div className="rounded-2xl border bg-card p-6 shadow-sm">
<h2 className="text-xl font-semibold">Public roadmap</h2>
<p className="mt-2 text-sm text-muted-foreground">
Share the roadmap and submission status pages with public users without requiring a
login.
</p>
<div className="mt-5 space-y-3 text-sm">
<div className="rounded-lg bg-muted p-3">
<span className="font-medium">Roadmap:</span> /roadmap
</div>
<div className="rounded-lg bg-muted p-3">
<span className="font-medium">Submission status:</span> /status/&lt;itemId&gt;
</div>
</div>
<Link
href="/roadmap"
className="mt-4 inline-flex rounded-lg border px-4 py-2 text-sm font-semibold hover:bg-muted"
>
Open public roadmap
</Link>
</div>
</section>
<section className="rounded-2xl border bg-card p-6 shadow-sm">
<h2 className="text-xl font-semibold">Integration surfaces</h2>
<p className="mt-2 text-sm text-muted-foreground">
These are the web-facing route groups that connect the dashboard to platform-service.
</p>
<div className="mt-5 grid gap-3 md:grid-cols-2">
{[
['/api/tracker/[...path]', 'Authenticated tracker and public roadmap proxy'],
['/api/fleet/[...path]', 'Agent/fleet control-plane proxy'],
['/api/auth/*', 'Login, MFA, OAuth, and session lookup'],
['/api/telemetry/ingest', 'Client telemetry ingestion'],
['/api/health', 'Readiness and platform-service dependency probe'],
].map(([route, description]) => (
<div key={route} className="rounded-xl border bg-background p-4">
<code className="text-sm font-semibold">{route}</code>
<p className="mt-2 text-sm text-muted-foreground">{description}</p>
</div>
))}
</div>
</section>
</div>
);
}