import React, { useState, useEffect } from 'react'; import { Link } from 'react-router-dom'; import { apiCall } from '../config/api'; interface ScheduleEvent { id: string; title: string; location: string; startTime: string; endTime: string; status: 'scheduled' | 'in-progress' | 'completed' | 'cancelled'; type: 'transport' | 'meeting' | 'event' | 'meal' | 'accommodation'; } interface Vip { id: string; name: string; organization: string; transportMode: 'flight' | 'self-driving'; flightNumber?: string; flights?: Array<{ flightNumber: string; flightDate: string; segment: number; }>; expectedArrival?: string; arrivalTime?: string; needsAirportPickup?: boolean; needsVenueTransport: boolean; notes?: string; currentEvent?: ScheduleEvent; nextEvent?: ScheduleEvent; nextEventTime?: string; } interface Driver { id: string; name: string; phone: string; currentLocation: { lat: number; lng: number }; assignedVipIds: string[]; } const Dashboard: React.FC = () => { const [vips, setVips] = useState([]); const [drivers, setDrivers] = useState([]); const [loading, setLoading] = useState(true); // Helper functions for event management const getCurrentEvent = (events: ScheduleEvent[]) => { const now = new Date(); return events.find(event => new Date(event.startTime) <= now && new Date(event.endTime) > now && event.status === 'in-progress' ) || null; }; const getNextEvent = (events: ScheduleEvent[]) => { const now = new Date(); const upcomingEvents = events.filter(event => new Date(event.startTime) > now && event.status === 'scheduled' ).sort((a, b) => new Date(a.startTime).getTime() - new Date(b.startTime).getTime()); return upcomingEvents.length > 0 ? upcomingEvents[0] : null; }; const formatTime = (timeString: string) => { return new Date(timeString).toLocaleString([], { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }); }; useEffect(() => { const fetchData = async () => { try { const token = localStorage.getItem('authToken'); const authHeaders = { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' }; const [vipsResponse, driversResponse] = await Promise.all([ apiCall('/api/vips', { headers: authHeaders }), apiCall('/api/drivers', { headers: authHeaders }) ]); if (!vipsResponse.ok || !driversResponse.ok) { throw new Error('Failed to fetch data'); } const vipsData = await vipsResponse.json(); const driversData = await driversResponse.json(); // Fetch schedule for each VIP and determine current/next events const vipsWithSchedules = await Promise.all( vipsData.map(async (vip: Vip) => { try { const scheduleResponse = await apiCall(`/api/vips/${vip.id}/schedule`, { headers: authHeaders }); if (scheduleResponse.ok) { const scheduleData = await scheduleResponse.json(); const currentEvent = getCurrentEvent(scheduleData); const nextEvent = getNextEvent(scheduleData); return { ...vip, currentEvent, nextEvent, nextEventTime: nextEvent ? nextEvent.startTime : null }; } else { return { ...vip, currentEvent: null, nextEvent: null, nextEventTime: null }; } } catch (error) { console.error(`Error fetching schedule for VIP ${vip.id}:`, error); return { ...vip, currentEvent: null, nextEvent: null, nextEventTime: null }; } }) ); // Sort VIPs by next event time (soonest first), then by name const sortedVips = vipsWithSchedules.sort((a, b) => { // VIPs with current events first if (a.currentEvent && !b.currentEvent) return -1; if (!a.currentEvent && b.currentEvent) return 1; // Then by next event time (soonest first) if (a.nextEventTime && b.nextEventTime) { return new Date(a.nextEventTime).getTime() - new Date(b.nextEventTime).getTime(); } if (a.nextEventTime && !b.nextEventTime) return -1; if (!a.nextEventTime && b.nextEventTime) return 1; // Finally by name if no events return a.name.localeCompare(b.name); }); setVips(sortedVips); setDrivers(driversData); } catch (error) { console.error('Error fetching data:', error); } finally { setLoading(false); } }; fetchData(); }, []); if (loading) { return (
Loading dashboard...
); } return (
{/* Header */}

VIP Coordinator Dashboard

Real-time overview of VIP activities and coordination

{vips.length} Active VIPs
{drivers.length} Drivers
{/* VIP Status Dashboard */}

VIP Status Dashboard {vips.length} VIPs

{vips.length === 0 ? (

No VIPs currently scheduled

) : (
{vips.map((vip) => { const hasCurrentEvent = !!vip.currentEvent; const hasNextEvent = !!vip.nextEvent; return (

{vip.name}

{hasCurrentEvent && ( ACTIVE )}

{vip.organization}

{/* Current Event */} {vip.currentEvent && (
CURRENT EVENT
{vip.currentEvent.title}

Location: {vip.currentEvent.location}

Until {formatTime(vip.currentEvent.endTime)}

)} {/* Next Event */} {vip.nextEvent && (
NEXT EVENT
{vip.nextEvent.title}

Location: {vip.nextEvent.location}

{formatTime(vip.nextEvent.startTime)} - {formatTime(vip.nextEvent.endTime)}

)} {/* No Events */} {!vip.currentEvent && !vip.nextEvent && (

No scheduled events

)} {/* Transport Info */}
{vip.transportMode === 'flight' ? ( Flight: {vip.flights && vip.flights.length > 0 ? vip.flights.map(f => f.flightNumber).join(' → ') : vip.flightNumber || 'TBD'} ) : ( Self-driving | Expected: {vip.expectedArrival ? formatTime(vip.expectedArrival) : 'TBD'} )}
Details Schedule
); })}
)}
{/* Sidebar */}
{/* Drivers Card */}

Available Drivers {drivers.length}

{drivers.length === 0 ? (

No drivers available

) : (
{drivers.map((driver) => (

{driver.name}

{driver.phone}

{driver.assignedVipIds.length} VIPs
))}
)}
{/* Quick Actions Card */}

Quick Actions

Manage VIPs Manage Drivers
); }; export default Dashboard;