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>
This commit is contained in:
2026-02-01 19:30:41 +01:00
parent 2d842ed294
commit 3b0b1205df
84 changed files with 12330 additions and 2103 deletions

View File

@@ -11,81 +11,95 @@ export const api = axios.create({
timeout: 30000, // 30 second timeout
});
// Request interceptor to add auth token and log requests
api.interceptors.request.use(
(config) => {
const token = localStorage.getItem('auth0_token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
// Log request in development mode
if (DEBUG_MODE) {
console.log(`[API] → ${config.method?.toUpperCase()} ${config.url}`, {
data: config.data,
params: config.params,
});
}
return config;
// Separate instance for AI Copilot with longer timeout (AI can take a while to respond)
export const copilotApi = axios.create({
baseURL: API_URL,
headers: {
'Content-Type': 'application/json',
},
(error) => {
console.error('[API] Request error:', error);
return Promise.reject(error);
}
);
timeout: 120000, // 2 minute timeout for AI requests
});
// Response interceptor for logging and error handling
api.interceptors.response.use(
(response) => {
// Log successful response in development mode
if (DEBUG_MODE) {
console.log(`[API] ← ${response.status} ${response.config.method?.toUpperCase()} ${response.config.url}`, {
// Shared request interceptor function
const requestInterceptor = (config: any) => {
const token = localStorage.getItem('auth0_token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
if (DEBUG_MODE) {
console.log(`[API] → ${config.method?.toUpperCase()} ${config.url}`, {
data: config.data,
params: config.params,
});
}
return config;
};
const requestErrorInterceptor = (error: any) => {
console.error('[API] Request error:', error);
return Promise.reject(error);
};
// Apply interceptors to both API instances
api.interceptors.request.use(requestInterceptor, requestErrorInterceptor);
copilotApi.interceptors.request.use(requestInterceptor, requestErrorInterceptor);
// Shared response interceptor function
const responseInterceptor = (response: any) => {
// Log successful response in development mode
if (DEBUG_MODE) {
console.log(`[API] ← ${response.status} ${response.config.method?.toUpperCase()} ${response.config.url}`, {
data: response.data,
});
}
return response;
};
const responseErrorInterceptor = (error: any) => {
const { config, response } = error;
// Enhanced error logging
if (response) {
// Server responded with error status
console.error(
`[API] ✖ ${response.status} ${config?.method?.toUpperCase()} ${config?.url}`,
{
status: response.status,
statusText: response.statusText,
data: response.data,
});
}
return response;
},
(error) => {
const { config, response } = error;
// Enhanced error logging
if (response) {
// Server responded with error status
console.error(
`[API] ✖ ${response.status} ${config?.method?.toUpperCase()} ${config?.url}`,
{
status: response.status,
statusText: response.statusText,
data: response.data,
requestData: config?.data,
}
);
// Log specific error types
if (response.status === 401) {
console.warn('[API] Authentication required - user may need to log in again');
} else if (response.status === 403) {
console.warn('[API] Permission denied - user lacks required permissions');
} else if (response.status === 404) {
console.warn('[API] Resource not found');
} else if (response.status === 409) {
console.warn('[API] Conflict detected:', response.data.conflicts || response.data.message);
} else if (response.status >= 500) {
console.error('[API] Server error - backend may be experiencing issues');
requestData: config?.data,
}
} else if (error.request) {
// Request was made but no response received
console.error('[API] ✖ Network error - no response received', {
method: config?.method?.toUpperCase(),
url: config?.url,
message: error.message,
});
} else {
// Something else happened
console.error('[API] ✖ Request setup error:', error.message);
}
);
return Promise.reject(error);
// Log specific error types
if (response.status === 401) {
console.warn('[API] Authentication required - user may need to log in again');
} else if (response.status === 403) {
console.warn('[API] Permission denied - user lacks required permissions');
} else if (response.status === 404) {
console.warn('[API] Resource not found');
} else if (response.status === 409) {
console.warn('[API] Conflict detected:', response.data.conflicts || response.data.message);
} else if (response.status >= 500) {
console.error('[API] Server error - backend may be experiencing issues');
}
} else if (error.request) {
// Request was made but no response received
console.error('[API] ✖ Network error - no response received', {
method: config?.method?.toUpperCase(),
url: config?.url,
message: error.message,
});
} else {
// Something else happened
console.error('[API] ✖ Request setup error:', error.message);
}
);
return Promise.reject(error);
};
// Apply response interceptors to both API instances
api.interceptors.response.use(responseInterceptor, responseErrorInterceptor);
copilotApi.interceptors.response.use(responseInterceptor, responseErrorInterceptor);