## Signal Messaging Integration - Added SignalService for sending messages to drivers via Signal - SignalMessage model for tracking message history - Driver chat modal for real-time messaging - Send schedule via Signal (ICS + PDF attachments) ## AI Copilot - Natural language interface for VIP Coordinator - Capabilities: create VIPs, schedule events, assign drivers - Help and guidance for users - Floating copilot button in UI ## Theme System - Dark/light/system theme support - Color scheme selection (blue, green, purple, orange, red) - ThemeContext for global state - AppearanceMenu in header ## PDF Schedule Export - VIPSchedulePDF component for schedule generation - PDF settings (header, footer, branding) - Preview PDF in browser - Settings stored in database ## Database Migrations - add_signal_messages: SignalMessage model - add_pdf_settings: Settings model for PDF config - add_reminder_tracking: lastReminderSent for events - make_driver_phone_optional: phone field nullable ## Event Management - Event status service for automated updates - IN_PROGRESS/COMPLETED status tracking - Reminder tracking for notifications ## UI/UX Improvements - Driver schedule modal - Improved My Schedule page - Better error handling and loading states - Responsive design improvements ## Other Changes - AGENT_TEAM.md documentation - Seed data improvements - Ability factory updates - Driver profile page Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
144 lines
5.3 KiB
TypeScript
144 lines
5.3 KiB
TypeScript
import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom';
|
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
import { Auth0Provider } from '@auth0/auth0-react';
|
|
import { Toaster } from 'react-hot-toast';
|
|
import { AuthProvider } from '@/contexts/AuthContext';
|
|
import { AbilityProvider } from '@/contexts/AbilityContext';
|
|
import { ThemeProvider } from '@/contexts/ThemeContext';
|
|
import { ProtectedRoute } from '@/components/ProtectedRoute';
|
|
import { Layout } from '@/components/Layout';
|
|
import { ErrorBoundary } from '@/components/ErrorBoundary';
|
|
import { Login } from '@/pages/Login';
|
|
import { Callback } from '@/pages/Callback';
|
|
import { PendingApproval } from '@/pages/PendingApproval';
|
|
import { Dashboard } from '@/pages/Dashboard';
|
|
import { CommandCenter } from '@/pages/CommandCenter';
|
|
import { VIPList } from '@/pages/VipList';
|
|
import { VIPSchedule } from '@/pages/VIPSchedule';
|
|
import { DriverList } from '@/pages/DriverList';
|
|
import { VehicleList } from '@/pages/VehicleList';
|
|
import { EventList } from '@/pages/EventList';
|
|
import { FlightList } from '@/pages/FlightList';
|
|
import { UserList } from '@/pages/UserList';
|
|
import { AdminTools } from '@/pages/AdminTools';
|
|
import { DriverProfile } from '@/pages/DriverProfile';
|
|
import { MySchedule } from '@/pages/MySchedule';
|
|
import { useAuth } from '@/contexts/AuthContext';
|
|
|
|
// Smart redirect based on user role
|
|
function HomeRedirect() {
|
|
const { backendUser } = useAuth();
|
|
|
|
// Drivers go to their schedule, everyone else goes to dashboard
|
|
if (backendUser?.role === 'DRIVER') {
|
|
return <Navigate to="/my-schedule" replace />;
|
|
}
|
|
return <Navigate to="/dashboard" replace />;
|
|
}
|
|
|
|
const queryClient = new QueryClient({
|
|
defaultOptions: {
|
|
queries: {
|
|
refetchOnWindowFocus: false,
|
|
retry: 1,
|
|
},
|
|
},
|
|
});
|
|
|
|
const domain = import.meta.env.VITE_AUTH0_DOMAIN;
|
|
const clientId = import.meta.env.VITE_AUTH0_CLIENT_ID;
|
|
const audience = import.meta.env.VITE_AUTH0_AUDIENCE;
|
|
|
|
function App() {
|
|
return (
|
|
<ErrorBoundary>
|
|
<ThemeProvider>
|
|
<Auth0Provider
|
|
domain={domain}
|
|
clientId={clientId}
|
|
authorizationParams={{
|
|
redirect_uri: `${window.location.origin}/callback`,
|
|
audience: audience,
|
|
scope: 'openid profile email offline_access',
|
|
}}
|
|
useRefreshTokens={true}
|
|
cacheLocation="localstorage"
|
|
>
|
|
<QueryClientProvider client={queryClient}>
|
|
<AuthProvider>
|
|
<AbilityProvider>
|
|
<BrowserRouter
|
|
future={{
|
|
v7_startTransition: true,
|
|
v7_relativeSplatPath: true,
|
|
}}
|
|
>
|
|
<Toaster
|
|
position="top-right"
|
|
toastOptions={{
|
|
duration: 4000,
|
|
className: 'bg-card text-card-foreground border border-border shadow-elevated',
|
|
style: {
|
|
background: 'hsl(var(--card))',
|
|
color: 'hsl(var(--card-foreground))',
|
|
border: '1px solid hsl(var(--border))',
|
|
},
|
|
success: {
|
|
duration: 3000,
|
|
iconTheme: {
|
|
primary: 'hsl(142, 76%, 36%)',
|
|
secondary: 'hsl(0, 0%, 100%)',
|
|
},
|
|
},
|
|
error: {
|
|
duration: 5000,
|
|
iconTheme: {
|
|
primary: 'hsl(0, 84%, 60%)',
|
|
secondary: 'hsl(0, 0%, 100%)',
|
|
},
|
|
},
|
|
}}
|
|
/>
|
|
<Routes>
|
|
<Route path="/login" element={<Login />} />
|
|
<Route path="/callback" element={<Callback />} />
|
|
<Route path="/pending-approval" element={<PendingApproval />} />
|
|
|
|
<Route
|
|
path="/*"
|
|
element={
|
|
<ProtectedRoute>
|
|
<Layout>
|
|
<Routes>
|
|
<Route path="/dashboard" element={<Dashboard />} />
|
|
<Route path="/command-center" element={<CommandCenter />} />
|
|
<Route path="/vips" element={<VIPList />} />
|
|
<Route path="/vips/:id/schedule" element={<VIPSchedule />} />
|
|
<Route path="/drivers" element={<DriverList />} />
|
|
<Route path="/vehicles" element={<VehicleList />} />
|
|
<Route path="/events" element={<EventList />} />
|
|
<Route path="/flights" element={<FlightList />} />
|
|
<Route path="/users" element={<UserList />} />
|
|
<Route path="/admin-tools" element={<AdminTools />} />
|
|
<Route path="/profile" element={<DriverProfile />} />
|
|
<Route path="/my-schedule" element={<MySchedule />} />
|
|
<Route path="/" element={<HomeRedirect />} />
|
|
<Route path="*" element={<HomeRedirect />} />
|
|
</Routes>
|
|
</Layout>
|
|
</ProtectedRoute>
|
|
}
|
|
/>
|
|
</Routes>
|
|
</BrowserRouter>
|
|
</AbilityProvider>
|
|
</AuthProvider>
|
|
</QueryClientProvider>
|
|
</Auth0Provider>
|
|
</ThemeProvider>
|
|
</ErrorBoundary>
|
|
);
|
|
}
|
|
|
|
export default App;
|