- 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
78 lines
3.2 KiB
TypeScript
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;
|