361 lines
10 KiB
JavaScript
361 lines
10 KiB
JavaScript
import React from 'react';
|
|
import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom';
|
|
import { QueryClient, QueryClientProvider } from 'react-query';
|
|
import { Toaster } from 'react-hot-toast';
|
|
|
|
import { AuthProvider } from './contexts/AuthContext';
|
|
import { useAuth } from './hooks/useAuth';
|
|
|
|
// Layout components
|
|
import Layout from './components/Layout/Layout';
|
|
import AuthLayout from './components/Layout/AuthLayout';
|
|
|
|
// Auth pages
|
|
import Login from './pages/Auth/Login';
|
|
import Register from './pages/Auth/Register';
|
|
import ForgotPassword from './pages/Auth/ForgotPassword';
|
|
|
|
// Main app pages
|
|
import Dashboard from './pages/Dashboard/Dashboard';
|
|
import Properties from './pages/Properties/Properties';
|
|
import PropertyDetail from './pages/Properties/PropertyDetail';
|
|
import Equipment from './pages/Equipment/Equipment';
|
|
import Products from './pages/Products/Products';
|
|
import Applications from './pages/Applications/Applications';
|
|
import ApplicationPlan from './pages/Applications/ApplicationPlan';
|
|
import ApplicationLog from './pages/Applications/ApplicationLog';
|
|
import History from './pages/History/History';
|
|
import Weather from './pages/Weather/Weather';
|
|
import Mowing from './pages/Mowing/Mowing';
|
|
import Profile from './pages/Profile/Profile';
|
|
|
|
// Admin pages
|
|
import AdminDashboard from './pages/Admin/AdminDashboard';
|
|
import AdminUsers from './pages/Admin/AdminUsers';
|
|
import AdminProducts from './pages/Admin/AdminProducts';
|
|
import AdminEquipment from './pages/Admin/AdminEquipment';
|
|
import AdminProperties from './pages/Admin/AdminProperties';
|
|
|
|
// Error pages
|
|
import NotFound from './pages/Error/NotFound';
|
|
import Unauthorized from './pages/Error/Unauthorized';
|
|
|
|
// Loading component
|
|
import LoadingSpinner from './components/UI/LoadingSpinner';
|
|
|
|
// Create a client for React Query
|
|
const queryClient = new QueryClient({
|
|
defaultOptions: {
|
|
queries: {
|
|
retry: (failureCount, error) => {
|
|
// Don't retry on 401 (unauthorized) or 403 (forbidden)
|
|
if (error?.response?.status === 401 || error?.response?.status === 403) {
|
|
return false;
|
|
}
|
|
return failureCount < 2;
|
|
},
|
|
staleTime: 5 * 60 * 1000, // 5 minutes
|
|
cacheTime: 10 * 60 * 1000, // 10 minutes
|
|
},
|
|
},
|
|
});
|
|
|
|
// Protected Route component
|
|
const ProtectedRoute = ({ children, adminOnly = false }) => {
|
|
const { user, loading } = useAuth();
|
|
|
|
if (loading) {
|
|
return (
|
|
<div className="min-h-screen flex items-center justify-center">
|
|
<LoadingSpinner size="lg" />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (!user) {
|
|
return <Navigate to="/login" replace />;
|
|
}
|
|
|
|
if (adminOnly && user.role !== 'admin') {
|
|
return <Navigate to="/unauthorized" replace />;
|
|
}
|
|
|
|
return children;
|
|
};
|
|
|
|
// Public Route component (redirects to dashboard if already authenticated)
|
|
const PublicRoute = ({ children }) => {
|
|
const { user, loading } = useAuth();
|
|
|
|
if (loading) {
|
|
return (
|
|
<div className="min-h-screen flex items-center justify-center">
|
|
<LoadingSpinner size="lg" />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (user) {
|
|
return <Navigate to="/dashboard" replace />;
|
|
}
|
|
|
|
return children;
|
|
};
|
|
|
|
function App() {
|
|
return (
|
|
<QueryClientProvider client={queryClient}>
|
|
<AuthProvider>
|
|
<Router>
|
|
<div className="App">
|
|
<Routes>
|
|
{/* Public Routes */}
|
|
<Route
|
|
path="/login"
|
|
element={
|
|
<PublicRoute>
|
|
<AuthLayout>
|
|
<Login />
|
|
</AuthLayout>
|
|
</PublicRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
path="/register"
|
|
element={
|
|
<PublicRoute>
|
|
<AuthLayout>
|
|
<Register />
|
|
</AuthLayout>
|
|
</PublicRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
path="/forgot-password"
|
|
element={
|
|
<PublicRoute>
|
|
<AuthLayout>
|
|
<ForgotPassword />
|
|
</AuthLayout>
|
|
</PublicRoute>
|
|
}
|
|
/>
|
|
|
|
{/* Protected Routes */}
|
|
<Route
|
|
path="/dashboard"
|
|
element={
|
|
<ProtectedRoute>
|
|
<Layout>
|
|
<Dashboard />
|
|
</Layout>
|
|
</ProtectedRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
path="/properties"
|
|
element={
|
|
<ProtectedRoute>
|
|
<Layout>
|
|
<Properties />
|
|
</Layout>
|
|
</ProtectedRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
path="/properties/:id"
|
|
element={
|
|
<ProtectedRoute>
|
|
<Layout>
|
|
<PropertyDetail />
|
|
</Layout>
|
|
</ProtectedRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
path="/equipment"
|
|
element={
|
|
<ProtectedRoute>
|
|
<Layout>
|
|
<Equipment />
|
|
</Layout>
|
|
</ProtectedRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
path="/products"
|
|
element={
|
|
<ProtectedRoute>
|
|
<Layout>
|
|
<Products />
|
|
</Layout>
|
|
</ProtectedRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
path="/applications"
|
|
element={
|
|
<ProtectedRoute>
|
|
<Layout>
|
|
<Applications />
|
|
</Layout>
|
|
</ProtectedRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
path="/applications/plan"
|
|
element={
|
|
<ProtectedRoute>
|
|
<Layout>
|
|
<ApplicationPlan />
|
|
</Layout>
|
|
</ProtectedRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
path="/applications/log"
|
|
element={
|
|
<ProtectedRoute>
|
|
<Layout>
|
|
<ApplicationLog />
|
|
</Layout>
|
|
</ProtectedRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
path="/history"
|
|
element={
|
|
<ProtectedRoute>
|
|
<Layout>
|
|
<History />
|
|
</Layout>
|
|
</ProtectedRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
path="/weather"
|
|
element={
|
|
<ProtectedRoute>
|
|
<Layout>
|
|
<Weather />
|
|
</Layout>
|
|
</ProtectedRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
path="/mowing"
|
|
element={
|
|
<ProtectedRoute>
|
|
<Layout>
|
|
<Mowing />
|
|
</Layout>
|
|
</ProtectedRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
path="/profile"
|
|
element={
|
|
<ProtectedRoute>
|
|
<Layout>
|
|
<Profile />
|
|
</Layout>
|
|
</ProtectedRoute>
|
|
}
|
|
/>
|
|
|
|
{/* Admin Routes */}
|
|
<Route
|
|
path="/admin"
|
|
element={
|
|
<ProtectedRoute adminOnly>
|
|
<Layout>
|
|
<AdminDashboard />
|
|
</Layout>
|
|
</ProtectedRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
path="/admin/users"
|
|
element={
|
|
<ProtectedRoute adminOnly>
|
|
<Layout>
|
|
<AdminUsers />
|
|
</Layout>
|
|
</ProtectedRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
path="/admin/products"
|
|
element={
|
|
<ProtectedRoute adminOnly>
|
|
<Layout>
|
|
<AdminProducts />
|
|
</Layout>
|
|
</ProtectedRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
path="/admin/equipment"
|
|
element={
|
|
<ProtectedRoute adminOnly>
|
|
<Layout>
|
|
<AdminEquipment />
|
|
</Layout>
|
|
</ProtectedRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
path="/admin/properties"
|
|
element={
|
|
<ProtectedRoute adminOnly>
|
|
<Layout>
|
|
<AdminProperties />
|
|
</Layout>
|
|
</ProtectedRoute>
|
|
}
|
|
/>
|
|
|
|
{/* Error Routes */}
|
|
<Route path="/unauthorized" element={<Unauthorized />} />
|
|
<Route path="/404" element={<NotFound />} />
|
|
|
|
{/* Redirects */}
|
|
<Route path="/" element={<Navigate to="/dashboard" replace />} />
|
|
<Route path="*" element={<Navigate to="/404" replace />} />
|
|
</Routes>
|
|
|
|
{/* Global Toast Notifications */}
|
|
<Toaster
|
|
position="top-right"
|
|
toastOptions={{
|
|
duration: 4000,
|
|
style: {
|
|
background: '#fff',
|
|
color: '#374151',
|
|
boxShadow: '0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)',
|
|
borderRadius: '0.5rem',
|
|
border: '1px solid #e5e7eb',
|
|
},
|
|
success: {
|
|
iconTheme: {
|
|
primary: '#10b981',
|
|
secondary: '#fff',
|
|
},
|
|
},
|
|
error: {
|
|
iconTheme: {
|
|
primary: '#ef4444',
|
|
secondary: '#fff',
|
|
},
|
|
},
|
|
}}
|
|
/>
|
|
</div>
|
|
</Router>
|
|
</AuthProvider>
|
|
</QueryClientProvider>
|
|
);
|
|
}
|
|
|
|
export default App;
|