Files
turftracker/frontend/src/App.js
2025-09-04 07:29:41 -05:00

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;