feat: add GPS tracking with Traccar integration
- Add GPS module with Traccar client service for device management - Add driver enrollment flow with QR code generation - Add real-time location tracking on driver profiles - Add GPS settings configuration in admin tools - Add Auth0 OpenID Connect setup script for Traccar - Add deployment configs for production server - Update nginx configs for SSL on GPS port 5055 - Add timezone setting support - Various UI improvements and bug fixes Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
326
frontend/EXAMPLE_PDF_USAGE.tsx
Normal file
326
frontend/EXAMPLE_PDF_USAGE.tsx
Normal file
@@ -0,0 +1,326 @@
|
||||
/**
|
||||
* EXAMPLE: How to Use VIP Schedule PDF Generation
|
||||
*
|
||||
* This file demonstrates the complete flow of generating a VIP schedule PDF.
|
||||
* This is a reference example - the actual implementation is in:
|
||||
* - src/components/VIPSchedulePDF.tsx (PDF component)
|
||||
* - src/pages/VIPSchedule.tsx (integration)
|
||||
*/
|
||||
|
||||
import { pdf } from '@react-pdf/renderer';
|
||||
import { VIPSchedulePDF } from '@/components/VIPSchedulePDF';
|
||||
|
||||
// Example VIP data
|
||||
const exampleVIP = {
|
||||
id: '123',
|
||||
name: 'John Doe',
|
||||
organization: 'Example Corporation',
|
||||
department: 'OFFICE_OF_DEVELOPMENT',
|
||||
arrivalMode: 'FLIGHT',
|
||||
expectedArrival: '2026-02-03T09:00:00Z',
|
||||
airportPickup: true,
|
||||
venueTransport: true,
|
||||
notes: 'VIP prefers electric vehicles. Dietary restriction: vegetarian.',
|
||||
flights: [
|
||||
{
|
||||
id: 'f1',
|
||||
flightNumber: 'AA123',
|
||||
departureAirport: 'JFK',
|
||||
arrivalAirport: 'LAX',
|
||||
scheduledDeparture: '2026-02-03T07:00:00Z',
|
||||
scheduledArrival: '2026-02-03T10:00:00Z',
|
||||
status: 'On Time',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// Example schedule events
|
||||
const exampleEvents = [
|
||||
{
|
||||
id: 'e1',
|
||||
title: 'Airport Pickup',
|
||||
type: 'TRANSPORT',
|
||||
status: 'SCHEDULED',
|
||||
startTime: '2026-02-03T10:00:00Z',
|
||||
endTime: '2026-02-03T11:00:00Z',
|
||||
pickupLocation: 'LAX Terminal 4',
|
||||
dropoffLocation: 'Hotel Grand Plaza',
|
||||
description: 'Pick up from arrival gate',
|
||||
driver: {
|
||||
id: 'd1',
|
||||
name: 'Mike Johnson',
|
||||
},
|
||||
vehicle: {
|
||||
id: 'v1',
|
||||
name: 'Tesla Model S',
|
||||
type: 'SEDAN',
|
||||
seatCapacity: 4,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'e2',
|
||||
title: 'Welcome Lunch',
|
||||
type: 'MEAL',
|
||||
status: 'SCHEDULED',
|
||||
startTime: '2026-02-03T12:00:00Z',
|
||||
endTime: '2026-02-03T13:30:00Z',
|
||||
location: 'Restaurant Chez Pierre',
|
||||
description: 'Lunch with board members',
|
||||
driver: null,
|
||||
vehicle: null,
|
||||
},
|
||||
{
|
||||
id: 'e3',
|
||||
title: 'Board Meeting',
|
||||
type: 'MEETING',
|
||||
status: 'SCHEDULED',
|
||||
startTime: '2026-02-03T14:00:00Z',
|
||||
endTime: '2026-02-03T17:00:00Z',
|
||||
location: 'Conference Room A, 5th Floor',
|
||||
description: 'Q1 strategy discussion',
|
||||
driver: null,
|
||||
vehicle: null,
|
||||
},
|
||||
{
|
||||
id: 'e4',
|
||||
title: 'Airport Return',
|
||||
type: 'TRANSPORT',
|
||||
status: 'SCHEDULED',
|
||||
startTime: '2026-02-04T15:00:00Z',
|
||||
endTime: '2026-02-04T16:00:00Z',
|
||||
pickupLocation: 'Hotel Grand Plaza',
|
||||
dropoffLocation: 'LAX Terminal 4',
|
||||
description: 'Departure for flight home',
|
||||
driver: {
|
||||
id: 'd1',
|
||||
name: 'Mike Johnson',
|
||||
},
|
||||
vehicle: {
|
||||
id: 'v1',
|
||||
name: 'Tesla Model S',
|
||||
type: 'SEDAN',
|
||||
seatCapacity: 4,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* EXAMPLE 1: Basic PDF Generation
|
||||
*/
|
||||
export async function generateBasicPDF() {
|
||||
const blob = await pdf(
|
||||
<VIPSchedulePDF vip={exampleVIP} events={exampleEvents} />
|
||||
).toBlob();
|
||||
|
||||
// Create download link
|
||||
const url = URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = `${exampleVIP.name.replace(/\s+/g, '_')}_Schedule.pdf`;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* EXAMPLE 2: PDF Generation with Custom Contact Info
|
||||
*/
|
||||
export async function generateCustomContactPDF() {
|
||||
const blob = await pdf(
|
||||
<VIPSchedulePDF
|
||||
vip={exampleVIP}
|
||||
events={exampleEvents}
|
||||
contactEmail="custom-coordinator@example.com"
|
||||
contactPhone="(555) 987-6543"
|
||||
appUrl="https://my-vip-app.com"
|
||||
/>
|
||||
).toBlob();
|
||||
|
||||
// Download
|
||||
const url = URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = 'VIP_Schedule.pdf';
|
||||
link.click();
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* EXAMPLE 3: PDF Generation with Environment Variables
|
||||
*/
|
||||
export async function generateEnvConfigPDF() {
|
||||
const blob = await pdf(
|
||||
<VIPSchedulePDF
|
||||
vip={exampleVIP}
|
||||
events={exampleEvents}
|
||||
contactEmail={import.meta.env.VITE_CONTACT_EMAIL || 'coordinator@example.com'}
|
||||
contactPhone={import.meta.env.VITE_CONTACT_PHONE || '(555) 123-4567'}
|
||||
appUrl={window.location.origin}
|
||||
/>
|
||||
).toBlob();
|
||||
|
||||
// Download with timestamp
|
||||
const date = new Date().toISOString().split('T')[0];
|
||||
const url = URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = `${exampleVIP.name.replace(/\s+/g, '_')}_Schedule_${date}.pdf`;
|
||||
link.click();
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* EXAMPLE 4: PDF Generation with Error Handling
|
||||
*/
|
||||
export async function generatePDFWithErrorHandling() {
|
||||
try {
|
||||
console.log('[PDF] Starting generation...');
|
||||
|
||||
const blob = await pdf(
|
||||
<VIPSchedulePDF
|
||||
vip={exampleVIP}
|
||||
events={exampleEvents}
|
||||
contactEmail={import.meta.env.VITE_CONTACT_EMAIL}
|
||||
contactPhone={import.meta.env.VITE_CONTACT_PHONE}
|
||||
appUrl={window.location.origin}
|
||||
/>
|
||||
).toBlob();
|
||||
|
||||
console.log('[PDF] Generation successful, size:', blob.size, 'bytes');
|
||||
|
||||
// Create download
|
||||
const url = URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = `${exampleVIP.name.replace(/\s+/g, '_')}_Schedule.pdf`;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
|
||||
// Clean up
|
||||
URL.revokeObjectURL(url);
|
||||
|
||||
console.log('[PDF] Download complete');
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('[PDF] Generation failed:', error);
|
||||
alert('Failed to generate PDF. Please try again.');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* EXAMPLE 5: React Component Integration
|
||||
*/
|
||||
export function VIPScheduleExampleComponent() {
|
||||
const handleExportPDF = async () => {
|
||||
try {
|
||||
const blob = await pdf(
|
||||
<VIPSchedulePDF
|
||||
vip={exampleVIP}
|
||||
events={exampleEvents}
|
||||
contactEmail={import.meta.env.VITE_CONTACT_EMAIL}
|
||||
contactPhone={import.meta.env.VITE_CONTACT_PHONE}
|
||||
appUrl={window.location.origin}
|
||||
/>
|
||||
).toBlob();
|
||||
|
||||
const url = URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = `${exampleVIP.name.replace(/\s+/g, '_')}_Schedule.pdf`;
|
||||
link.click();
|
||||
URL.revokeObjectURL(url);
|
||||
} catch (error) {
|
||||
console.error('[PDF] Export failed:', error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={handleExportPDF}
|
||||
className="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700"
|
||||
>
|
||||
Export PDF
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* EXAMPLE 6: Preview PDF in New Tab (instead of download)
|
||||
*/
|
||||
export async function previewPDFInNewTab() {
|
||||
const blob = await pdf(
|
||||
<VIPSchedulePDF vip={exampleVIP} events={exampleEvents} />
|
||||
).toBlob();
|
||||
|
||||
// Open in new tab instead of downloading
|
||||
const url = URL.createObjectURL(blob);
|
||||
window.open(url, '_blank');
|
||||
|
||||
// Clean up after a delay (user should have opened it by then)
|
||||
setTimeout(() => URL.revokeObjectURL(url), 10000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Expected PDF Output Structure:
|
||||
*
|
||||
* ┌────────────────────────────────────────────────────────────┐
|
||||
* │ HEADER │
|
||||
* │ ┌──────────────────────────────────────────────────────┐ │
|
||||
* │ │ John Doe │ │
|
||||
* │ │ Example Corporation │ │
|
||||
* │ │ OFFICE OF DEVELOPMENT • VIP Schedule & Itinerary │ │
|
||||
* │ │ │ │
|
||||
* │ │ ⚠️ DOCUMENT GENERATED AT: │ │
|
||||
* │ │ Saturday, February 1, 2026, 2:00 PM EST │ │
|
||||
* │ │ This is a snapshot. For latest: https://app.com │ │
|
||||
* │ └──────────────────────────────────────────────────────┘ │
|
||||
* │ │
|
||||
* │ VIP INFORMATION │
|
||||
* │ ┌──────────────────────────────────────────────────────┐ │
|
||||
* │ │ Arrival Mode: FLIGHT Expected: Feb 3, 9:00 AM │ │
|
||||
* │ │ Airport Pickup: Yes Venue Transport: Yes │ │
|
||||
* │ └──────────────────────────────────────────────────────┘ │
|
||||
* │ │
|
||||
* │ FLIGHT INFORMATION │
|
||||
* │ ┌──────────────────────────────────────────────────────┐ │
|
||||
* │ │ Flight AA123 │ │
|
||||
* │ │ JFK → LAX │ │
|
||||
* │ │ Arrives: Feb 3, 10:00 AM │ │
|
||||
* │ │ Status: On Time │ │
|
||||
* │ └──────────────────────────────────────────────────────┘ │
|
||||
* │ │
|
||||
* │ SPECIAL NOTES │
|
||||
* │ ┌──────────────────────────────────────────────────────┐ │
|
||||
* │ │ VIP prefers electric vehicles. Dietary: vegetarian │ │
|
||||
* │ └──────────────────────────────────────────────────────┘ │
|
||||
* │ │
|
||||
* │ SCHEDULE & ITINERARY │
|
||||
* │ │
|
||||
* │ Monday, February 3, 2026 │
|
||||
* │ ┌──────────────────────────────────────────────────────┐ │
|
||||
* │ │ 10:00 AM - 11:00 AM [TRANSPORT] │ │
|
||||
* │ │ Airport Pickup │ │
|
||||
* │ │ 📍 LAX Terminal 4 → Hotel Grand Plaza │ │
|
||||
* │ │ 👤 Driver: Mike Johnson │ │
|
||||
* │ │ 🚗 Tesla Model S (SEDAN) │ │
|
||||
* │ │ [SCHEDULED] │ │
|
||||
* │ └──────────────────────────────────────────────────────┘ │
|
||||
* │ │
|
||||
* │ ┌──────────────────────────────────────────────────────┐ │
|
||||
* │ │ 12:00 PM - 1:30 PM [MEAL] │ │
|
||||
* │ │ Welcome Lunch │ │
|
||||
* │ │ 📍 Restaurant Chez Pierre │ │
|
||||
* │ │ [SCHEDULED] │ │
|
||||
* │ └──────────────────────────────────────────────────────┘ │
|
||||
* │ │
|
||||
* │ ... more events ... │
|
||||
* │ │
|
||||
* ├────────────────────────────────────────────────────────────┤
|
||||
* │ FOOTER │
|
||||
* │ For Questions: coordinator@vip-board.com │
|
||||
* │ Phone: (555) 123-4567 Page 1 of 2 │
|
||||
* └────────────────────────────────────────────────────────────┘
|
||||
*/
|
||||
Reference in New Issue
Block a user