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;