Build the ui/ component library and persistent app shell
Button, Input, Select, Badge, SeverityBadge, Table, Card, Modal, Tooltip, Tabs, Skeleton, EmptyState -- a shared library so pages stop hand-rolling markup per page (web/src/lib/components/ui, barrel export in index.ts). Modal and CommandPalette are built on native <dialog> for a real focus trap, Escape-to-close, and top-layer stacking instead of hand-rolling those. Tabs uses roving tabindex with arrow-key nav. NavSidebar replaces the old top-nav with a persistent sidebar (Search/Dashboards/Alerts/Data Sources/Settings), a live tenant indicator (api.ts's new getCurrentSession(), a client for the already-existing POST /internal/authorize -- zero new backend surface), theme/density quick-toggles, and a command-palette hint. Collapses to an off-canvas drawer under 860px. CommandPalette (Cmd/Ctrl+K) indexes the five static destinations plus live-fetched dashboards/alert rules. Data Sources is a new, honestly-scoped placeholder page (one data source per tenant today, no UI needed yet). Settings and Select-tenant are re-tokened onto the new component library. +layout.svelte's content wrapper is a plain <div>, not a second <main> -- every page already renders its own top-level <main>.
This commit is contained in:
+29
-1
@@ -16,7 +16,7 @@ export type Language = '' | 'sql' | 'spl';
|
||||
|
||||
export type QueryResult = { columns: string[]; rows: unknown[][] };
|
||||
|
||||
export type VizType = 'table' | 'line' | 'bar' | 'single_stat' | 'top_n';
|
||||
export type VizType = 'table' | 'line' | 'bar' | 'single_stat' | 'top_n' | 'heatmap';
|
||||
|
||||
export type Panel = {
|
||||
id: string;
|
||||
@@ -203,6 +203,34 @@ export function selectTenant(tenantId: string): Promise<{ redirect_url: string }
|
||||
});
|
||||
}
|
||||
|
||||
export type CurrentSession = { tenant_id: string; user_id: string; role: string };
|
||||
|
||||
// getCurrentSession backs the sidebar's tenant indicator (Phase 5).
|
||||
// POST /internal/authorize already exists (api/authz.HTTPAuthorizer and
|
||||
// alerting's queryclient call it the same way) and is already reachable
|
||||
// from the browser -- enterprise-auth's whole mux is wrapped in
|
||||
// WithCredentialedCORS, not just the tenant-picker routes -- so this is
|
||||
// a client for an existing endpoint, not a new one. Returns null for
|
||||
// every "no tenant to show" case alike (no enterprise-auth configured,
|
||||
// not logged in, or a plain single-tenant deployment) rather than
|
||||
// throwing -- same "absence is a normal deployment shape" posture
|
||||
// getAuthFeatures already uses, so the sidebar can just render nothing
|
||||
// instead of branching on error types.
|
||||
export async function getCurrentSession(): Promise<CurrentSession | null> {
|
||||
if (!enterpriseAuthBase) return null;
|
||||
try {
|
||||
const res = await fetch(`${enterpriseAuthBase}/internal/authorize`, {
|
||||
method: 'POST',
|
||||
credentials: 'include'
|
||||
});
|
||||
if (!res.ok) return null;
|
||||
const session = (await res.json()) as CurrentSession;
|
||||
return session.tenant_id ? session : null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function exportDashboard(id: string): Promise<Dashboard> {
|
||||
return request(`/dashboards/${id}/export`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user