Spaces:
Sleeping
Sleeping
File size: 3,293 Bytes
f6213fc | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | import { formatClock, kindLabel, toneForKind } from './formatters.mjs';
export function buildVehicleTimelineModel(preview) {
const axis = buildAxis(preview);
return {
axis,
lanes: (preview?.vehicles || []).map((vehicle) => ({
id: `vehicle-${vehicle.vehicleId}`,
label: vehicle.vehicleName,
mode: 'detailed',
badges: [
`${vehicle.stopCount} stops`,
`${vehicle.totalDemand}/${vehicle.totalDemand - vehicle.capacityOverage + vehicle.capacityOverage || vehicle.totalDemand}`,
],
items: vehicle.stops.map((stop) => ({
id: `vehicle-${vehicle.vehicleId}-stop-${stop.deliveryId}`,
startMinute: Math.floor(stop.serviceStartTime / 60),
endMinute: Math.ceil(stop.departureTime / 60),
label: stop.label,
meta: `${kindLabel(stop.kind)} · ${formatClock(stop.arrivalTime)} arrival`,
tone: toneForKind(stop.kind),
})),
})),
};
}
export function buildDeliveryTimelineModel(preview) {
const axis = buildAxis(preview);
return {
axis,
lanes: (preview?.deliveries || []).map((delivery) => {
const items = [
{
id: `delivery-window-${delivery.deliveryId}`,
startMinute: Math.floor(delivery.minStartTime / 60),
endMinute: Math.ceil(delivery.maxEndTime / 60),
label: 'Window',
meta: `${formatClock(delivery.minStartTime)} to ${formatClock(delivery.maxEndTime)}`,
tone: 'slate',
},
];
if (delivery.serviceStartTime != null && delivery.departureTime != null) {
items.push({
id: `delivery-service-${delivery.deliveryId}`,
startMinute: Math.floor(delivery.serviceStartTime / 60),
endMinute: Math.ceil(delivery.departureTime / 60),
label: delivery.assignedVehicleName || 'Assigned',
meta: `${delivery.label} · ${kindLabel(delivery.kind)}`,
tone: toneForKind(delivery.kind),
});
}
return {
id: `delivery-${delivery.deliveryId}`,
label: delivery.label,
mode: 'detailed',
badges: [kindLabel(delivery.kind), delivery.assignedVehicleName || 'Unassigned'],
items,
};
}),
};
}
function buildAxis(preview) {
let minMinute = 6 * 60;
let maxMinute = 21 * 60;
const previewData = preview || { deliveries: [], vehicles: [] };
for (const delivery of previewData.deliveries || []) {
minMinute = Math.min(minMinute, Math.floor((delivery.minStartTime || 0) / 60) - 30);
maxMinute = Math.max(maxMinute, Math.ceil((delivery.maxEndTime || 0) / 60) + 30);
}
for (const vehicle of previewData.vehicles || []) {
minMinute = Math.min(minMinute, Math.floor((vehicle.startTime || 0) / 60) - 15);
maxMinute = Math.max(maxMinute, Math.ceil((vehicle.endTime || 0) / 60) + 15);
}
minMinute = Math.max(0, minMinute);
maxMinute = Math.min(24 * 60, Math.max(minMinute + 120, maxMinute));
const ticks = [];
for (let minute = minMinute; minute <= maxMinute; minute += 60) {
ticks.push({ minute, label: formatClock(minute * 60) });
}
return {
startMinute: minMinute,
endMinute: maxMinute,
days: [{ dayIndex: 0, label: 'Day 1' }],
ticks,
initialViewport: { startMinute: minMinute, endMinute: maxMinute },
};
}
|