o134's picture
Upload full TeamTasker system with all fixes
8314cf4 verified
Raw
History Blame Contribute Delete
4.59 kB
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;