diff --git a/src/components/BookingCalendar.tsx b/src/components/BookingCalendar.tsx index 1c77e20..17a8bfb 100644 --- a/src/components/BookingCalendar.tsx +++ b/src/components/BookingCalendar.tsx @@ -5,11 +5,10 @@ import { X, Layers, Server, Clock, ChevronDown } from 'lucide-react'; -/** A device can only be reserved when CheckMK reports it online. */ function effectiveStatus(d: Device): 'online' | 'offline' | 'unknown' { return d.checkMkUrl ? (d.status === 'online' || d.status === 'offline' ? d.status : 'unknown') : 'unknown'; } -function isBookable(d: Device): boolean { +function isOnline(d: Device): boolean { return effectiveStatus(d) === 'online'; } @@ -156,11 +155,11 @@ export default function BookingCalendar({ return { hasConflict: false }; } - // Devices in the current selection that CheckMK does not report as online - these block the booking. - function blockingDevices(deviceIds: string[]): Device[] { + // Devices in the current selection that CheckMK does not report as online - shown as a warning only. + function offlineDevices(deviceIds: string[]): Device[] { return deviceIds .map(id => devices.find(d => d.id === id)) - .filter((d): d is Device => !!d && !isBookable(d)); + .filter((d): d is Device => !!d && !isOnline(d)); } // ── available-now helpers for Quick Booking ──────────────────────────── @@ -171,17 +170,14 @@ export default function BookingCalendar({ return { startMs: start.getTime(), endMs: end.getTime(), start, end }; }, [quickDuration]); - // A lab is quick-bookable only when every device is free AND reported online by CheckMK. + // A lab is quick-bookable when every device is free (regardless of online status). const availableLabs = useMemo(() => labs.filter(lab => lab.deviceIds.length > 0 && - lab.deviceIds.every(dId => { - const dev = devices.find(d => d.id === dId); - return !!dev && isBookable(dev) && !isDeviceBooked(dId, quickWindow.startMs, quickWindow.endMs); - }) + lab.deviceIds.every(dId => !isDeviceBooked(dId, quickWindow.startMs, quickWindow.endMs)) ), [labs, devices, bookings, quickWindow]); const availableDevices = useMemo(() => devices.filter(dev => - isBookable(dev) && !isDeviceBooked(dev.id, quickWindow.startMs, quickWindow.endMs) + !isDeviceBooked(dev.id, quickWindow.startMs, quickWindow.endMs) ), [devices, bookings, quickWindow]); // ── booking actions ──────────────────────────────────────────────────── @@ -191,12 +187,6 @@ export default function BookingCalendar({ const deviceIds = targetDeviceIds(); if (deviceIds.length === 0) { alert('Please select a resource to reserve.'); return; } - const blocked = blockingDevices(deviceIds); - if (blocked.length > 0) { - alert(`Not bookable: ${blocked.map(d => `"${d.hostname}" (${effectiveStatus(d)})`).join(', ')} ${blocked.length === 1 ? 'is' : 'are'} not online in CheckMK.`); - return; - } - const conflict = checkConflict(deviceIds, startDate, startTime, endDate, endTime); if (conflict.hasConflict) { alert(conflict.message); return; } @@ -225,10 +215,6 @@ export default function BookingCalendar({ }; const handleQuickBookDevice = (device: Device) => { - if (!isBookable(device)) { - alert(`"${device.hostname}" is ${effectiveStatus(device)} in CheckMK and cannot be reserved.`); - return; - } // Find or pick a lab that contains this device; fall back to device ID as labId marker const hostLab = labs.find(l => l.deviceIds.includes(device.id)); onAddBooking({ @@ -312,16 +298,22 @@ export default function BookingCalendar({
Nothing free and fully online for {quickDuration}h right now. all boxes either leased or not reporting in.
+Nothing free for {quickDuration}h right now. All slots leased.
) : ( availableLabs.map(lab => { const labDevices = lab.deviceIds.map(id => devices.find(d => d.id === id)).filter(Boolean) as Device[]; + const offlineCount = labDevices.filter(d => !isOnline(d)).length; return ({lab.name}
{lab.location} · {labDevices.length} device{labDevices.length !== 1 ? 's' : ''}
{labDevices.map(d => d.hostname).join(', ')}
+ {offlineCount > 0 && ( +
+