T5/Phase 3: SPA shell + navigation

- TanStack Router with type-safe file-based routes
- Sidebar: collapsible, workspace switcher, 12 nav links, user menu
- Topbar: search, quick-add, notifications, user avatar
- cmdk command palette: nav + create + search + @mentions
- Keyboard shortcuts: Cmd+K palette, g+t/h/p/n/c/g/s navigation, ? help
- Theme: dark default, accent picker (slate/blue/green/purple/orange), persisted to localStorage
- Auth: login -> /, logout -> /login, 401 redirect
- Placeholder pages for all 13 nav routes (real in T6/T7)
- Typed API client with useApiQuery/useApiMutation hooks
This commit is contained in:
Hermes
2026-08-01 02:00:24 +00:00
parent c277e3a14f
commit e86a7c0672
66 changed files with 3781 additions and 32 deletions
+2
View File
@@ -1,9 +1,11 @@
import { createRootRoute, Outlet } from "@tanstack/react-router";
import { TanStackRouterDevtools } from "@tanstack/react-router-devtools";
export const Route = createRootRoute({
component: () => (
<div className="min-h-screen bg-background text-foreground">
<Outlet />
{import.meta.env.DEV && <TanStackRouterDevtools />}
</div>
),
});
+35
View File
@@ -0,0 +1,35 @@
import { createRoute, Outlet } from "@tanstack/react-router";
import { Route as rootRoute } from "./__root";
import { Sidebar } from "@/components/shell/sidebar";
import { Topbar } from "@/components/shell/topbar";
import { CommandPalette } from "@/components/shell/command-palette";
import { ShortcutsHelp } from "@/components/shell/shortcuts-help";
import { useKeyboardShortcuts } from "@/hooks/use-keyboard-shortcuts";
function AppLayout() {
useKeyboardShortcuts();
return (
<div className="flex min-h-screen">
<Sidebar />
<div className="flex flex-1 flex-col">
<Topbar />
<main
id="main-content"
className="flex-1 overflow-auto p-4 md:p-6"
tabIndex={-1}
>
<Outlet />
</main>
</div>
<CommandPalette />
<ShortcutsHelp />
</div>
);
}
export const Route = createRoute({
getParentRoute: () => rootRoute,
id: "_app",
component: AppLayout,
});
+17
View File
@@ -0,0 +1,17 @@
import { createRoute } from "@tanstack/react-router";
import { Route as appRoute } from "../_app";
function AgentsPage() {
return (
<div className="flex flex-col items-center justify-center min-h-[60vh]">
<h1 className="text-3xl font-bold mb-2">Agent Activity</h1>
<p className="text-muted-foreground">Coming in T7 agent activity feed.</p>
</div>
);
}
export const Route = createRoute({
getParentRoute: () => appRoute,
path: "/agents",
component: AgentsPage,
});
@@ -0,0 +1,17 @@
import { createRoute } from "@tanstack/react-router";
import { Route as appRoute } from "../../_app";
function AgentActivityPage() {
return (
<div className="flex flex-col items-center justify-center min-h-[60vh]">
<h1 className="text-3xl font-bold mb-2">Agent Activity</h1>
<p className="text-muted-foreground">Coming in T7 agent activity feed.</p>
</div>
);
}
export const Route = createRoute({
getParentRoute: () => appRoute,
path: "/agents/activity",
component: AgentActivityPage,
});
+17
View File
@@ -0,0 +1,17 @@
import { createRoute } from "@tanstack/react-router";
import { Route as appRoute } from "../_app";
function AnalyticsPage() {
return (
<div className="flex flex-col items-center justify-center min-h-[60vh]">
<h1 className="text-3xl font-bold mb-2">Analytics</h1>
<p className="text-muted-foreground">Coming in T7 analytics dashboard.</p>
</div>
);
}
export const Route = createRoute({
getParentRoute: () => appRoute,
path: "/analytics",
component: AnalyticsPage,
});
+17
View File
@@ -0,0 +1,17 @@
import { createRoute } from "@tanstack/react-router";
import { Route as appRoute } from "../_app";
function CalendarPage() {
return (
<div className="flex flex-col items-center justify-center min-h-[60vh]">
<h1 className="text-3xl font-bold mb-2">Calendar</h1>
<p className="text-muted-foreground">Coming in T7 calendar view.</p>
</div>
);
}
export const Route = createRoute({
getParentRoute: () => appRoute,
path: "/calendar",
component: CalendarPage,
});
+17
View File
@@ -0,0 +1,17 @@
import { createRoute } from "@tanstack/react-router";
import { Route as appRoute } from "../_app";
function CanvasPage() {
return (
<div className="flex flex-col items-center justify-center min-h-[60vh]">
<h1 className="text-3xl font-bold mb-2">Canvas</h1>
<p className="text-muted-foreground">Coming in T7 visual canvas.</p>
</div>
);
}
export const Route = createRoute({
getParentRoute: () => appRoute,
path: "/canvas",
component: CanvasPage,
});
+17
View File
@@ -0,0 +1,17 @@
import { createRoute } from "@tanstack/react-router";
import { Route as appRoute } from "../_app";
function DailyNotesPage() {
return (
<div className="flex flex-col items-center justify-center min-h-[60vh]">
<h1 className="text-3xl font-bold mb-2">Daily Notes</h1>
<p className="text-muted-foreground">Coming in T7 daily journal.</p>
</div>
);
}
export const Route = createRoute({
getParentRoute: () => appRoute,
path: "/daily",
component: DailyNotesPage,
});
+17
View File
@@ -0,0 +1,17 @@
import { createRoute } from "@tanstack/react-router";
import { Route as appRoute } from "../_app";
function GraphPage() {
return (
<div className="flex flex-col items-center justify-center min-h-[60vh]">
<h1 className="text-3xl font-bold mb-2">Graph</h1>
<p className="text-muted-foreground">Coming in T7 knowledge graph.</p>
</div>
);
}
export const Route = createRoute({
getParentRoute: () => appRoute,
path: "/graph",
component: GraphPage,
});
+17
View File
@@ -0,0 +1,17 @@
import { createRoute } from "@tanstack/react-router";
import { Route as appRoute } from "../_app";
function HabitsPage() {
return (
<div className="flex flex-col items-center justify-center min-h-[60vh]">
<h1 className="text-3xl font-bold mb-2">Habits</h1>
<p className="text-muted-foreground">Coming in T6 habit tracking.</p>
</div>
);
}
export const Route = createRoute({
getParentRoute: () => appRoute,
path: "/habits",
component: HabitsPage,
});
+17
View File
@@ -0,0 +1,17 @@
import { createRoute } from "@tanstack/react-router";
import { Route as appRoute } from "../_app";
function DashboardPage() {
return (
<div className="flex flex-col items-center justify-center min-h-[60vh]">
<h1 className="text-3xl font-bold mb-2">Dashboard</h1>
<p className="text-muted-foreground">Coming in T7 real dashboard widgets.</p>
</div>
);
}
export const Route = createRoute({
getParentRoute: () => appRoute,
path: "/",
component: DashboardPage,
});
+17
View File
@@ -0,0 +1,17 @@
import { createRoute } from "@tanstack/react-router";
import { Route as appRoute } from "../_app";
function NotesPage() {
return (
<div className="flex flex-col items-center justify-center min-h-[60vh]">
<h1 className="text-3xl font-bold mb-2">Notes</h1>
<p className="text-muted-foreground">Coming in T6 notes and documents.</p>
</div>
);
}
export const Route = createRoute({
getParentRoute: () => appRoute,
path: "/notes",
component: NotesPage,
});
+17
View File
@@ -0,0 +1,17 @@
import { createRoute } from "@tanstack/react-router";
import { Route as appRoute } from "../_app";
function ProjectsPage() {
return (
<div className="flex flex-col items-center justify-center min-h-[60vh]">
<h1 className="text-3xl font-bold mb-2">Projects</h1>
<p className="text-muted-foreground">Coming in T6 project management.</p>
</div>
);
}
export const Route = createRoute({
getParentRoute: () => appRoute,
path: "/projects",
component: ProjectsPage,
});
+17
View File
@@ -0,0 +1,17 @@
import { createRoute } from "@tanstack/react-router";
import { Route as appRoute } from "../_app";
function SearchPage() {
return (
<div className="flex flex-col items-center justify-center min-h-[60vh]">
<h1 className="text-3xl font-bold mb-2">Search</h1>
<p className="text-muted-foreground">Coming in T7 full-text search.</p>
</div>
);
}
export const Route = createRoute({
getParentRoute: () => appRoute,
path: "/search",
component: SearchPage,
});
+17
View File
@@ -0,0 +1,17 @@
import { createRoute } from "@tanstack/react-router";
import { Route as appRoute } from "../_app";
function SettingsPage() {
return (
<div className="flex flex-col items-center justify-center min-h-[60vh]">
<h1 className="text-3xl font-bold mb-2">Settings</h1>
<p className="text-muted-foreground">Coming in T7 user preferences.</p>
</div>
);
}
export const Route = createRoute({
getParentRoute: () => appRoute,
path: "/settings",
component: SettingsPage,
});
+17
View File
@@ -0,0 +1,17 @@
import { createRoute } from "@tanstack/react-router";
import { Route as appRoute } from "../_app";
function TasksPage() {
return (
<div className="flex flex-col items-center justify-center min-h-[60vh]">
<h1 className="text-3xl font-bold mb-2">Tasks</h1>
<p className="text-muted-foreground">Coming in T6 full task management.</p>
</div>
);
}
export const Route = createRoute({
getParentRoute: () => appRoute,
path: "/tasks",
component: TasksPage,
});
+1 -1
View File
@@ -19,7 +19,7 @@ function LoginPage() {
});
if (!res.ok) {
const data = await res.json();
setError(data.message || "Login failed");
setError(data.error?.message || data.message || "Login failed");
return;
}
navigate({ to: "/" });