Initial commit - Current state of vip-coordinator
This commit is contained in:
184
backend/src/services/driverConflictService.ts
Normal file
184
backend/src/services/driverConflictService.ts
Normal file
@@ -0,0 +1,184 @@
|
||||
interface ScheduleEvent {
|
||||
id: string;
|
||||
title: string;
|
||||
location: string;
|
||||
startTime: string;
|
||||
endTime: string;
|
||||
assignedDriverId?: string;
|
||||
vipId: string;
|
||||
vipName: string;
|
||||
}
|
||||
|
||||
interface ConflictInfo {
|
||||
type: 'overlap' | 'tight_turnaround' | 'back_to_back';
|
||||
severity: 'low' | 'medium' | 'high';
|
||||
message: string;
|
||||
conflictingEvent: ScheduleEvent;
|
||||
timeDifference?: number; // minutes
|
||||
}
|
||||
|
||||
interface DriverAvailability {
|
||||
driverId: string;
|
||||
driverName: string;
|
||||
status: 'available' | 'scheduled' | 'overlapping' | 'tight_turnaround';
|
||||
assignmentCount: number;
|
||||
conflicts: ConflictInfo[];
|
||||
currentAssignments: ScheduleEvent[];
|
||||
}
|
||||
|
||||
class DriverConflictService {
|
||||
|
||||
// Check for conflicts when assigning a driver to an event
|
||||
checkDriverConflicts(
|
||||
driverId: string,
|
||||
newEvent: { startTime: string; endTime: string; location: string },
|
||||
allSchedules: { [vipId: string]: ScheduleEvent[] },
|
||||
drivers: any[]
|
||||
): ConflictInfo[] {
|
||||
const conflicts: ConflictInfo[] = [];
|
||||
const driver = drivers.find(d => d.id === driverId);
|
||||
if (!driver) return conflicts;
|
||||
|
||||
// Get all events assigned to this driver
|
||||
const driverEvents = this.getDriverEvents(driverId, allSchedules);
|
||||
|
||||
const newStartTime = new Date(newEvent.startTime);
|
||||
const newEndTime = new Date(newEvent.endTime);
|
||||
|
||||
for (const existingEvent of driverEvents) {
|
||||
const existingStart = new Date(existingEvent.startTime);
|
||||
const existingEnd = new Date(existingEvent.endTime);
|
||||
|
||||
// Check for direct time overlap
|
||||
if (this.hasTimeOverlap(newStartTime, newEndTime, existingStart, existingEnd)) {
|
||||
conflicts.push({
|
||||
type: 'overlap',
|
||||
severity: 'high',
|
||||
message: `Direct time conflict with "${existingEvent.title}" for ${existingEvent.vipName}`,
|
||||
conflictingEvent: existingEvent
|
||||
});
|
||||
}
|
||||
// Check for tight turnaround (less than 15 minutes between events)
|
||||
else {
|
||||
const timeBetween = this.getTimeBetweenEvents(
|
||||
newStartTime, newEndTime, existingStart, existingEnd
|
||||
);
|
||||
|
||||
if (timeBetween !== null && timeBetween < 15) {
|
||||
conflicts.push({
|
||||
type: 'tight_turnaround',
|
||||
severity: timeBetween < 5 ? 'high' : 'medium',
|
||||
message: `Only ${timeBetween} minutes between events. Previous: "${existingEvent.title}"`,
|
||||
conflictingEvent: existingEvent,
|
||||
timeDifference: timeBetween
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return conflicts;
|
||||
}
|
||||
|
||||
// Get availability status for all drivers for a specific time slot
|
||||
getDriverAvailability(
|
||||
eventTime: { startTime: string; endTime: string; location: string },
|
||||
allSchedules: { [vipId: string]: ScheduleEvent[] },
|
||||
drivers: any[]
|
||||
): DriverAvailability[] {
|
||||
return drivers.map(driver => {
|
||||
const conflicts = this.checkDriverConflicts(driver.id, eventTime, allSchedules, drivers);
|
||||
const driverEvents = this.getDriverEvents(driver.id, allSchedules);
|
||||
|
||||
let status: DriverAvailability['status'] = 'available';
|
||||
|
||||
if (conflicts.length > 0) {
|
||||
const hasOverlap = conflicts.some(c => c.type === 'overlap');
|
||||
const hasTightTurnaround = conflicts.some(c => c.type === 'tight_turnaround');
|
||||
|
||||
if (hasOverlap) {
|
||||
status = 'overlapping';
|
||||
} else if (hasTightTurnaround) {
|
||||
status = 'tight_turnaround';
|
||||
}
|
||||
} else if (driverEvents.length > 0) {
|
||||
status = 'scheduled';
|
||||
}
|
||||
|
||||
return {
|
||||
driverId: driver.id,
|
||||
driverName: driver.name,
|
||||
status,
|
||||
assignmentCount: driverEvents.length,
|
||||
conflicts,
|
||||
currentAssignments: driverEvents
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// Get all events assigned to a specific driver
|
||||
private getDriverEvents(driverId: string, allSchedules: { [vipId: string]: ScheduleEvent[] }): ScheduleEvent[] {
|
||||
const driverEvents: ScheduleEvent[] = [];
|
||||
|
||||
Object.entries(allSchedules).forEach(([vipId, events]) => {
|
||||
events.forEach(event => {
|
||||
if (event.assignedDriverId === driverId) {
|
||||
driverEvents.push({
|
||||
...event,
|
||||
vipId,
|
||||
vipName: event.title // We'll need to get actual VIP name from VIP data
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Sort by start time
|
||||
return driverEvents.sort((a, b) =>
|
||||
new Date(a.startTime).getTime() - new Date(b.startTime).getTime()
|
||||
);
|
||||
}
|
||||
|
||||
// Check if two time periods overlap
|
||||
private hasTimeOverlap(
|
||||
start1: Date, end1: Date,
|
||||
start2: Date, end2: Date
|
||||
): boolean {
|
||||
return start1 < end2 && start2 < end1;
|
||||
}
|
||||
|
||||
// Get minutes between two events (null if they overlap)
|
||||
private getTimeBetweenEvents(
|
||||
newStart: Date, newEnd: Date,
|
||||
existingStart: Date, existingEnd: Date
|
||||
): number | null {
|
||||
// If new event is after existing event
|
||||
if (newStart >= existingEnd) {
|
||||
return Math.floor((newStart.getTime() - existingEnd.getTime()) / (1000 * 60));
|
||||
}
|
||||
// If new event is before existing event
|
||||
else if (newEnd <= existingStart) {
|
||||
return Math.floor((existingStart.getTime() - newEnd.getTime()) / (1000 * 60));
|
||||
}
|
||||
// Events overlap
|
||||
return null;
|
||||
}
|
||||
|
||||
// Generate summary message for driver status
|
||||
getDriverStatusSummary(availability: DriverAvailability): string {
|
||||
switch (availability.status) {
|
||||
case 'available':
|
||||
return `✅ Fully available (${availability.assignmentCount} assignments)`;
|
||||
case 'scheduled':
|
||||
return `🟡 Has ${availability.assignmentCount} assignment(s) but available for this time`;
|
||||
case 'tight_turnaround':
|
||||
const tightConflict = availability.conflicts.find(c => c.type === 'tight_turnaround');
|
||||
return `⚡ Tight turnaround - ${tightConflict?.timeDifference} min between events`;
|
||||
case 'overlapping':
|
||||
return `🔴 Time conflict with existing assignment`;
|
||||
default:
|
||||
return 'Unknown status';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default new DriverConflictService();
|
||||
export { DriverAvailability, ConflictInfo, ScheduleEvent };
|
||||
Reference in New Issue
Block a user