Files
hammer-queue/frontend/src/App.tsx
Hammer f4c60bf6aa feat: dark mode support, markdown descriptions, inline editing on TaskPage
- Full dark mode across TaskPage (header, cards, sidebar, forms)
- Task descriptions rendered as markdown (ReactMarkdown + remark-gfm)
- Inline description editing with markdown preview
- Inline title editing (click to edit)
- Theme system (useTheme hook with light/dark/system toggle)
- Dark mode classes across remaining components
2026-01-29 10:33:38 +00:00

78 lines
3.2 KiB
TypeScript

import { lazy, Suspense } from "react";
import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom";
import { DashboardLayout } from "./components/DashboardLayout";
import { LoginPage } from "./components/LoginPage";
import { ToastProvider } from "./components/Toast";
import { ThemeProvider } from "./hooks/useTheme";
import { useSession } from "./lib/auth-client";
// Lazy-loaded pages for code splitting
const DashboardPage = lazy(() => import("./pages/DashboardPage").then(m => ({ default: m.DashboardPage })));
const QueuePage = lazy(() => import("./pages/QueuePage").then(m => ({ default: m.QueuePage })));
const ChatPage = lazy(() => import("./pages/ChatPage").then(m => ({ default: m.ChatPage })));
const ProjectsPage = lazy(() => import("./pages/ProjectsPage").then(m => ({ default: m.ProjectsPage })));
const TaskPage = lazy(() => import("./pages/TaskPage").then(m => ({ default: m.TaskPage })));
const ActivityPage = lazy(() => import("./pages/ActivityPage").then(m => ({ default: m.ActivityPage })));
const AdminPage = lazy(() => import("./components/AdminPage").then(m => ({ default: m.AdminPage })));
function PageLoader() {
return (
<div className="min-h-[50vh] flex items-center justify-center text-gray-400">
<div className="flex items-center gap-2">
<span className="w-2 h-2 bg-amber-400 rounded-full animate-bounce" style={{ animationDelay: "0ms" }} />
<span className="w-2 h-2 bg-amber-400 rounded-full animate-bounce" style={{ animationDelay: "150ms" }} />
<span className="w-2 h-2 bg-amber-400 rounded-full animate-bounce" style={{ animationDelay: "300ms" }} />
</div>
</div>
);
}
function AuthenticatedApp() {
return (
<BrowserRouter>
<Routes>
<Route element={<DashboardLayout />}>
<Route path="/" element={<Suspense fallback={<PageLoader />}><DashboardPage /></Suspense>} />
<Route path="/queue" element={<Suspense fallback={<PageLoader />}><QueuePage /></Suspense>} />
<Route path="/task/:taskRef" element={<Suspense fallback={<PageLoader />}><TaskPage /></Suspense>} />
<Route path="/projects" element={<Suspense fallback={<PageLoader />}><ProjectsPage /></Suspense>} />
<Route path="/chat" element={<Suspense fallback={<PageLoader />}><ChatPage /></Suspense>} />
<Route path="/activity" element={<Suspense fallback={<PageLoader />}><ActivityPage /></Suspense>} />
<Route path="/admin" element={<Suspense fallback={<PageLoader />}><AdminPage /></Suspense>} />
<Route path="*" element={<Navigate to="/" replace />} />
</Route>
</Routes>
</BrowserRouter>
);
}
function App() {
const session = useSession();
if (session.isPending) {
return (
<div className="min-h-screen bg-gray-50 dark:bg-gray-950 flex items-center justify-center">
<div className="text-gray-400">Loading...</div>
</div>
);
}
if (!session.data) {
return (
<ThemeProvider>
<LoginPage onSuccess={() => window.location.reload()} />
</ThemeProvider>
);
}
return (
<ThemeProvider>
<ToastProvider>
<AuthenticatedApp />
</ToastProvider>
</ThemeProvider>
);
}
export default App;