Files
vip-coordinator/frontend/src/App.tsx
kyle 3b0b1205df feat: comprehensive update with Signal, Copilot, themes, and PDF features
## 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>
2026-02-01 19:30:41 +01:00

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;