117 lines
4.0 KiB
TypeScript
117 lines
4.0 KiB
TypeScript
import { useEffect } from "react";
|
|
import { Switch, Route, Router, useLocation } from "wouter";
|
|
import { useHashLocation } from "wouter/use-hash-location";
|
|
import { queryClient } from "./lib/queryClient";
|
|
import { QueryClientProvider } from "@tanstack/react-query";
|
|
import { Toaster } from "@/components/ui/toaster";
|
|
import { TooltipProvider } from "@/components/ui/tooltip";
|
|
import { SidebarProvider } from "@/components/ui/sidebar";
|
|
import { AppSidebar } from "@/components/app-sidebar";
|
|
import { TopBar } from "@/components/top-bar";
|
|
import { SymbolProvider } from "@/lib/symbol-context";
|
|
import { ThemeProvider } from "@/lib/theme-context";
|
|
import { AuthProvider, useAuth } from "@/lib/auth-context";
|
|
import DashboardPage from "@/pages/dashboard";
|
|
import GammaLevelsPage from "@/pages/gamma-levels";
|
|
import ExpiryMatrixPage from "@/pages/expiry-matrix";
|
|
import ScreenerPage from "@/pages/screener";
|
|
import SettingsPage from "@/pages/settings";
|
|
import AccountPage from "@/pages/account";
|
|
import LoginPage from "@/pages/login";
|
|
import NotFound from "@/pages/not-found";
|
|
import { Loader2 } from "lucide-react";
|
|
|
|
function usePageTitle(): string {
|
|
const [location] = useLocation();
|
|
if (location.startsWith("/gamma")) return "Gamma Levels";
|
|
if (location.startsWith("/expiry")) return "Expiry Matrix";
|
|
if (location.startsWith("/screener")) return "Screener";
|
|
if (location.startsWith("/settings")) return "Settings & API";
|
|
if (location.startsWith("/account")) return "Account";
|
|
if (location.startsWith("/login")) return "Sign In";
|
|
return "Dashboard";
|
|
}
|
|
|
|
// Redirect to login if not authenticated
|
|
function ProtectedRoute({ component: Component }: { component: () => JSX.Element }) {
|
|
const { user, loading } = useAuth();
|
|
const [, setLocation] = useLocation();
|
|
|
|
// Must call hooks unconditionally before any early return
|
|
useEffect(() => {
|
|
if (!loading && !user) {
|
|
setLocation("/login");
|
|
}
|
|
}, [loading, user, setLocation]);
|
|
|
|
if (loading) {
|
|
return (
|
|
<div className="flex h-screen items-center justify-center">
|
|
<Loader2 className="h-6 w-6 animate-spin text-muted-foreground" />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (!user) {
|
|
return null;
|
|
}
|
|
|
|
return <Component />;
|
|
}
|
|
|
|
function AppShell() {
|
|
const title = usePageTitle();
|
|
const [location] = useLocation();
|
|
const isAuthPage = location === "/login";
|
|
return (
|
|
<div className="flex h-screen w-full overflow-hidden">
|
|
{!isAuthPage && <AppSidebar />}
|
|
<div className="flex min-w-0 flex-1 flex-col">
|
|
<TopBar title={title} />
|
|
<main
|
|
className="flex-1 overflow-y-auto"
|
|
style={{ overscrollBehavior: "contain" }}
|
|
data-testid="main-content"
|
|
>
|
|
<Switch>
|
|
<Route path="/login" component={LoginPage} />
|
|
<Route path="/account" component={AccountPage} />
|
|
<Route path="/" component={() => <ProtectedRoute component={DashboardPage} />} />
|
|
<Route path="/gamma" component={() => <ProtectedRoute component={GammaLevelsPage} />} />
|
|
<Route path="/expiry" component={() => <ProtectedRoute component={ExpiryMatrixPage} />} />
|
|
<Route path="/screener" component={() => <ProtectedRoute component={ScreenerPage} />} />
|
|
<Route path="/settings" component={() => <ProtectedRoute component={SettingsPage} />} />
|
|
<Route component={NotFound} />
|
|
</Switch>
|
|
</main>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default function App() {
|
|
const sidebarStyle = {
|
|
"--sidebar-width": "16rem",
|
|
"--sidebar-width-icon": "3.25rem",
|
|
} as React.CSSProperties;
|
|
|
|
return (
|
|
<QueryClientProvider client={queryClient}>
|
|
<ThemeProvider>
|
|
<TooltipProvider delayDuration={200}>
|
|
<SymbolProvider>
|
|
<AuthProvider>
|
|
<Router hook={useHashLocation}>
|
|
<SidebarProvider style={sidebarStyle}>
|
|
<AppShell />
|
|
</SidebarProvider>
|
|
</Router>
|
|
<Toaster />
|
|
</AuthProvider>
|
|
</SymbolProvider>
|
|
</TooltipProvider>
|
|
</ThemeProvider>
|
|
</QueryClientProvider>
|
|
);
|
|
}
|