Spaces:
Sleeping
Sleeping
File size: 4,589 Bytes
8314cf4 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | import { Switch, Route, Router as WouterRouter, useLocation, Redirect } from "wouter";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { Toaster } from "@/components/ui/toaster";
import { TooltipProvider } from "@/components/ui/tooltip";
import { AuthProvider, useAuth } from "./context/auth-context";
import { setAuthTokenGetter } from "@workspace/api-client-react";
import React, { useEffect, Suspense } from "react";
import { useToast } from "./hooks/use-toast";
import { AppLayout } from "./components/layout";
import LoginPage from "./pages/login";
const Dashboard = React.lazy(() => import("./pages/dashboard"));
const TasksList = React.lazy(() => import("./pages/tasks"));
const TaskDetail = React.lazy(() => import("./pages/task-detail"));
const TaskNew = React.lazy(() => import("./pages/task-new"));
const Teams = React.lazy(() => import("./pages/teams"));
const Notifications = React.lazy(() => import("./pages/notifications"));
const Users = React.lazy(() => import("./pages/users"));
const Clients = React.lazy(() => import("./pages/clients"));
const ClientDetail = React.lazy(() => import("./pages/client-detail"));
const Admin = React.lazy(() => import("./pages/admin"));
import NotFound from "@/pages/not-found";
import AccessDenied from "./pages/access-denied";
const queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: (failureCount, error: any) => {
// Do not retry on 401 or 403
if (error?.status === 401 || error?.status === 403) return false;
return failureCount < 2;
},
refetchOnWindowFocus: false,
},
},
});
function ProtectedRoutes() {
const { user, token, logout, isLoading } = useAuth();
const [location, setLocation] = useLocation();
const { toast } = useToast();
// Set the API client token getter
useEffect(() => {
setAuthTokenGetter(() => token);
}, [token]);
// Global 401/403 handler (simple version)
// Real implementation might need a custom hook or interceptor
// but we can catch some states here.
if (isLoading) {
return (
<div className="min-h-screen flex items-center justify-center bg-slate-50">
<div className="text-center">
<div className="animate-spin text-4xl mb-3">⚙️</div>
<p className="text-slate-500 text-sm">جارٍ التحميل…</p>
</div>
</div>
);
}
if (!user) return <Redirect to="/login" />;
const role = user.role;
// Admin-only routes
if ((location === "/admin" || location === "/teams") && role !== "admin") {
return (
<AppLayout>
<AccessDenied />
</AppLayout>
);
}
// Member-only restricted routes
if (role === "member" && (location === "/users" || location.startsWith("/clients"))) {
return (
<AppLayout>
<AccessDenied />
</AppLayout>
);
}
// Handle head_of_team and team_lead route exceptions if any
// (Currently they can see most things except Admin)
return (
<AppLayout>
<Suspense fallback={
<div className="flex h-[50vh] items-center justify-center">
<div className="text-center">
<div className="animate-spin text-4xl mb-3">⚙️</div>
<p className="text-slate-500 text-sm">جارٍ تحميل الصفحة…</p>
</div>
</div>
}>
<Switch>
<Route path="/" component={Dashboard} />
<Route path="/tasks/new" component={TaskNew} />
<Route path="/tasks/:id" component={TaskDetail} />
<Route path="/tasks" component={TasksList} />
<Route path="/teams" component={Teams} />
<Route path="/notifications" component={Notifications} />
<Route path="/users" component={Users} />
<Route path="/clients/:id" component={ClientDetail} />
<Route path="/clients" component={Clients} />
<Route path="/admin" component={Admin} />
<Route component={NotFound} />
</Switch>
</Suspense>
</AppLayout>
);
}
function AppRoutes() {
return (
<Switch>
<Route path="/login" component={LoginPage} />
<Route component={ProtectedRoutes} />
</Switch>
);
}
function App() {
return (
<QueryClientProvider client={queryClient}>
<TooltipProvider>
<WouterRouter base={import.meta.env.BASE_URL.replace(/\/$/, "")}>
<AuthProvider>
<AppRoutes />
</AuthProvider>
</WouterRouter>
<Toaster />
</TooltipProvider>
</QueryClientProvider>
);
}
export default App;
|