{{-- Shared Teaching Schedule: actual time position, compact 30-minute grid and separate overlap lanes. --}} @php $scheduleDayRows = $gridRows->filter(function ($item) use ($day) { return strtoupper(trim((string) ($item->timetable_date ?? ''))) === strtoupper($day); })->sortBy(function ($item) use ($timeToMinutes) { return $timeToMinutes($item->timetable_time_start ?? null) ?? PHP_INT_MAX; })->values(); $scheduleSpan = max(1, (int) ($gridEnd - $gridStart)); $scheduleLaneEnds = []; $scheduleEvents = []; foreach ($scheduleDayRows as $scheduleRow) { $actualStart = $timeToMinutes($scheduleRow->timetable_time_start ?? null); $actualEnd = $timeToMinutes($scheduleRow->timetable_time_end ?? null); if ($actualStart === null || $actualEnd === null || $actualEnd <= $actualStart) { continue; } // The coordinate must be the *course's* own start/end, not another course's colspan. $visibleStart = max((int) $gridStart, $actualStart); $visibleEnd = min((int) $gridEnd, $actualEnd); if ($visibleEnd <= $visibleStart) { continue; } // Overlapping courses use separate vertical lanes; nonoverlapping courses reuse lanes. $assignedLane = null; foreach ($scheduleLaneEnds as $laneNumber => $laneEnd) { if ($laneEnd <= $actualStart) { $assignedLane = $laneNumber; break; } } if ($assignedLane === null) { $assignedLane = count($scheduleLaneEnds); } $scheduleLaneEnds[$assignedLane] = $actualEnd; $scheduleEvents[] = [ 'course' => $scheduleRow, 'left' => ($visibleStart - $gridStart) * 100 / $scheduleSpan, 'width' => ($visibleEnd - $visibleStart) * 100 / $scheduleSpan, 'top' => $assignedLane * 130 + 5, ]; } $scheduleTrackHeight = count($scheduleEvents) > 0 ? (count($scheduleLaneEnds) * 130 + 8) : 56; $lunchStart = max((int) $gridStart, 12 * 60); $lunchEnd = min((int) $gridEnd, 13 * 60); $showLunch = $lunchEnd > $lunchStart; $lunchLeft = ($lunchStart - $gridStart) * 100 / $scheduleSpan; $lunchWidth = ($lunchEnd - $lunchStart) * 100 / $scheduleSpan; // Do not allow the lunch label to peek through or overlap the course card. // The real course time is never split: lunch is a visual reference only. $scheduleLunchOccupied = false; if ($showLunch) { foreach ($scheduleEvents as $scheduleEvent) { $eventStart = $timeToMinutes($scheduleEvent['course']->timetable_time_start ?? null); $eventEnd = $timeToMinutes($scheduleEvent['course']->timetable_time_end ?? null); if ($eventStart !== null && $eventEnd !== null && $eventStart < $lunchEnd && $eventEnd > $lunchStart) { $scheduleLunchOccupied = true; break; } } } @endphp