user(); $todayBooking = $user->consultations() ->whereDate('booking_date', today()) ->whereIn('status', [ConsultationStatus::Pending, ConsultationStatus::Approved]) ->first(); $pendingRequests = $user->consultations() ->where('status', ConsultationStatus::Pending) ->where('booking_date', '>=', today()) ->orderBy('booking_date') ->get(); $bookedDates = $user->consultations() ->whereIn('status', [ConsultationStatus::Pending, ConsultationStatus::Approved]) ->where('booking_date', '>=', today()) ->pluck('booking_date') ->map(fn ($d) => $d->format('Y-m-d')) ->toArray(); return [ 'canBookToday' => is_null($todayBooking), 'todayBooking' => $todayBooking, 'pendingRequests' => $pendingRequests, 'bookedDates' => $bookedDates, ]; } public function selectSlot(string $date, string $time): void { $this->selectedDate = $date; $this->selectedTime = $time; } public function clearSelection(): void { $this->selectedDate = null; $this->selectedTime = null; $this->showConfirmation = false; } public function showConfirm(): void { $this->validate([ 'selectedDate' => ['required', 'date', 'after_or_equal:today'], 'selectedTime' => ['required'], 'problemSummary' => ['required', 'string', 'min:20', 'max:2000'], ]); // Check 1-per-day limit $existingBooking = Consultation::query() ->where('user_id', auth()->id()) ->whereDate('booking_date', $this->selectedDate) ->whereIn('status', [ConsultationStatus::Pending, ConsultationStatus::Approved]) ->exists(); if ($existingBooking) { $this->addError('selectedDate', __('booking.already_booked_this_day')); return; } // Verify slot still available $service = app(AvailabilityService::class); $availableSlots = $service->getAvailableSlots(Carbon::parse($this->selectedDate)); if (! in_array($this->selectedTime, $availableSlots)) { $this->addError('selectedTime', __('booking.slot_no_longer_available')); return; } $this->showConfirmation = true; } public function submit(): void { try { DB::transaction(function () { // Check slot one more time with lock $slotTaken = Consultation::query() ->whereDate('booking_date', $this->selectedDate) ->where('booking_time', $this->selectedTime) ->whereIn('status', [ConsultationStatus::Pending, ConsultationStatus::Approved]) ->lockForUpdate() ->exists(); if ($slotTaken) { throw new \Exception(__('booking.slot_taken')); } // Check 1-per-day again with lock $userHasBooking = Consultation::query() ->where('user_id', auth()->id()) ->whereDate('booking_date', $this->selectedDate) ->whereIn('status', [ConsultationStatus::Pending, ConsultationStatus::Approved]) ->lockForUpdate() ->exists(); if ($userHasBooking) { throw new \Exception(__('booking.already_booked_this_day')); } // Create booking $consultation = Consultation::create([ 'user_id' => auth()->id(), 'booking_date' => $this->selectedDate, 'booking_time' => $this->selectedTime, 'problem_summary' => $this->problemSummary, 'status' => ConsultationStatus::Pending, 'payment_status' => PaymentStatus::NotApplicable, ]); // Send email to client Mail::to(auth()->user())->queue( new BookingSubmittedMail($consultation) ); // Send email to admin $admin = User::query()->where('user_type', 'admin')->first(); if ($admin) { Mail::to($admin)->queue( new NewBookingAdminEmail($consultation) ); } else { Log::warning('No admin user found to notify about new booking', [ 'consultation_id' => $consultation->id, ]); } // Log action AdminLog::create([ 'admin_id' => null, 'action' => 'create', 'target_type' => 'consultation', 'target_id' => $consultation->id, 'new_values' => $consultation->toArray(), 'ip_address' => request()->ip(), 'created_at' => now(), ]); }); session()->flash('success', __('booking.submitted_successfully')); $this->redirect(route('client.consultations.index')); } catch (\Exception $e) { $this->addError('selectedTime', $e->getMessage()); $this->showConfirmation = false; } } public function with(): array { return $this->getBookingStatus(); } }; ?>
{{ __('booking.request_consultation') }} {{-- Booking Status Banner --}}
@if($canBookToday)

{{ __('booking.can_book_today') }}

@else

{{ __('booking.already_booked_today') }}

@endif
@if($pendingRequests->isNotEmpty())
@if($pendingRequests->count() === 1)

{{ __('booking.pending_for_date', ['date' => $pendingRequests->first()->booking_date->format('d/m/Y')]) }}

@else

{{ __('booking.pending_count', ['count' => $pendingRequests->count()]) }}

    @foreach($pendingRequests->take(3) as $request)
  • {{ $request->booking_date->format('d/m/Y') }}
  • @endforeach @if($pendingRequests->count() > 3)
  • ...
  • @endif
@endif
@endif

{{ __('booking.limit_message') }}

@if(!$selectedDate || !$selectedTime)

{{ __('booking.select_date_time') }}

@else

{{ __('booking.selected_time') }}

{{ \Carbon\Carbon::parse($selectedDate)->translatedFormat('l, d M Y') }}

{{ \Carbon\Carbon::parse($selectedTime)->format('g:i A') }}

{{ __('common.change') }}
@error('selectedDate') {{ $message }} @enderror @if(!$showConfirmation) {{ __('booking.problem_summary') }} * {{ __('booking.problem_summary_help') }} {{ __('booking.continue') }} {{ __('common.loading') }} @else {{ __('booking.confirm_booking') }}

{{ __('booking.confirm_message') }}

{{ __('booking.date') }}: {{ \Carbon\Carbon::parse($selectedDate)->translatedFormat('l, d M Y') }}

{{ __('booking.time') }}: {{ \Carbon\Carbon::parse($selectedTime)->format('g:i A') }}

{{ __('booking.duration') }}: 45 {{ __('common.minutes') }}

{{ __('booking.problem_summary') }}:

{{ $problemSummary }}

{{ __('common.back') }} {{ __('booking.submit_request') }} {{ __('common.submitting') }}
@error('selectedTime') {{ $message }} @enderror @endif
@endif