137 lines
7.5 KiB
PHP
137 lines
7.5 KiB
PHP
<link rel="stylesheet" href="{{ asset('css/custom.css') }}">
|
||
|
||
@php
|
||
use App\Services\BusyWindowService;
|
||
use Carbon\Carbon;
|
||
|
||
// Load all external busy windows with ±40 min buffer
|
||
$busyWindows = BusyWindowService::getBusyRanges();
|
||
@endphp
|
||
|
||
@include('partials.flash')
|
||
|
||
<x-app-layout>
|
||
<div class="container mx-auto my-6">
|
||
<div class="flex justify-between items-center mb-4">
|
||
<h2 class="text-2xl font-semibold">
|
||
Calendar: {{ $start->toFormattedDateString() }} – {{ $end->toFormattedDateString() }}
|
||
</h2>
|
||
<div class="space-x-2">
|
||
<a href="{{ route('admin.calendar', ['week' => $weekOffset - 1]) }}"
|
||
class="px-3 py-1 bg-gray-200 rounded hover:bg-gray-300">← Previous</a>
|
||
<a href="{{ route('admin.calendar') }}"
|
||
class="px-3 py-1 bg-blue-200 rounded hover:bg-blue-300">This Week</a>
|
||
<a href="{{ route('admin.calendar', ['week' => $weekOffset + 1]) }}"
|
||
class="px-3 py-1 bg-gray-200 rounded hover:bg-gray-300">Next →</a>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="mb-3 text-sm">
|
||
<span class="inline-block px-2 py-1 rounded bg-green-100 border">Free</span>
|
||
<span class="inline-block px-2 py-1 rounded bg-red-200 border ml-2">Booked</span>
|
||
<span class="inline-block px-2 py-1 rounded bg-yellow-200 border ml-2">Closed (Admin)</span>
|
||
<span class="inline-block px-2 py-1 rounded bg-amber-200 border ml-2">Busy (External)</span>
|
||
<span class="inline-block px-2 py-1 rounded bg-gray-100 border ml-2">Closed (Past/≤ 2h)</span>
|
||
</div>
|
||
|
||
@php
|
||
$times = ['09:00:00','10:00:00','11:00:00','14:00:00','15:00:00','16:00:00'];
|
||
@endphp
|
||
|
||
<table class="w-full border-collapse border border-gray-300">
|
||
<thead>
|
||
<tr class="bg-gray-100">
|
||
<th class="border p-2">Time</th>
|
||
@for ($i = 0; $i < 5; $i++) {{-- Mon–Fri --}}
|
||
<th class="border p-2">
|
||
{{ $start->copy()->addDays($i)->format('D d M') }}
|
||
</th>
|
||
@endfor
|
||
</tr>
|
||
</thead>
|
||
|
||
<tbody>
|
||
@foreach ($times as $time)
|
||
<tr>
|
||
<td class="border p-2 font-medium">{{ substr($time, 0, 5) }}</td>
|
||
|
||
@for ($weekday = 1; $weekday <= 5; $weekday++) {{-- 1=Mon . 5=Fri --}}
|
||
@php
|
||
$date = $start->copy()->addDays($weekday - 1)->toDateString();
|
||
|
||
// Find the recurring slot for this weekday+time
|
||
$slot = $slots->first(function($s) use ($weekday, $time) {
|
||
return (int)$s->weekday === $weekday && $s->time === $time;
|
||
});
|
||
|
||
// Existing booking for that visible date?
|
||
$booking = $slot ? $slot->bookings->firstWhere('date', $date) : null;
|
||
|
||
// 2-hour rule
|
||
$sessionStart = Carbon::createFromFormat('Y-m-d H:i:s', $date.' '.$time, $now->timezone);
|
||
$minutesUntil = $now->diffInMinutes($sessionStart, false);
|
||
$tooSoon = $minutesUntil <= 120;
|
||
|
||
// External busy window check (±40 min buffer already applied)
|
||
$isExternalBusy = BusyWindowService::isInBusyWindow($sessionStart, $busyWindows);
|
||
|
||
// Assign background class
|
||
$class = 'border p-2 text-center ';
|
||
if ($booking?->status === 'booked') $class .= 'bg-red-200';
|
||
elseif ($booking?->status === 'blocked') $class .= 'bg-yellow-200';
|
||
elseif ($isExternalBusy) $class .= 'bg-amber-200';
|
||
elseif ($slot && !$booking && !$tooSoon) $class .= 'bg-green-100';
|
||
else $class .= 'bg-gray-100';
|
||
@endphp
|
||
|
||
<td class="{{ $class }}">
|
||
@if ($isExternalBusy)
|
||
Busy (External)
|
||
@elseif ($booking)
|
||
{{ $booking->status === 'blocked' ? 'Closed' : 'Booked' }}
|
||
@if ($booking->status === 'booked' && $booking->student_name)
|
||
<br><span class="text-xs">{{ $booking->student_name }}</span>
|
||
@endif
|
||
@elseif (!$slot)
|
||
Not seeded
|
||
@elseif ($tooSoon || $minutesUntil < 0)
|
||
Closed
|
||
@else
|
||
@php $isStudent = isset($viewer) && $viewer === 'student'; @endphp
|
||
|
||
@if ($slot)
|
||
@if ($isStudent)
|
||
<div class="flex items-center justify-center gap-3">
|
||
<a href="{{ route('student.bookings.create', ['date' => $date, 'slot_id' => $slot->id]) }}"
|
||
class="underline text-blue-700 hover:text-blue-900">
|
||
Book
|
||
</a>
|
||
|
||
<a href="{{ route('student.bookings.series.create', ['date' => $date, 'slot_id' => $slot->id, 'weeks' => 6]) }}"
|
||
class="underline text-purple-700 hover:text-purple-900">
|
||
Book series
|
||
</a>
|
||
</div>
|
||
@else
|
||
<div class="flex items-center justify-center gap-3">
|
||
<a href="{{ route('admin.bookings.create', ['date' => $date, 'slot_id' => $slot->id]) }}"
|
||
class="underline text-blue-700 hover:text-blue-900">Book</a>
|
||
<a href="{{ route('admin.bookings.block.create', ['date' => $date, 'slot_id' => $slot->id]) }}"
|
||
class="underline text-amber-700 hover:text-amber-900">Close</a>
|
||
<a href="{{ route('admin.bookings.series.create', ['date' => $date, 'slot_id' => $slot->id, 'weeks' => 6]) }}"
|
||
class="underline text-purple-700 hover:text-purple-900">Block booking</a>
|
||
</div>
|
||
@endif
|
||
@else
|
||
Free
|
||
@endif
|
||
@endif
|
||
</td>
|
||
@endfor
|
||
</tr>
|
||
@endforeach
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</x-app-layout>
|