@extends('layouts.app') @section('title', __('appointments::messages.appointments.title')) @php use App\Modules\Appointments\Enums\AppointmentStatus; use App\Modules\Appointments\Models\Appointment; $isToday = $date->isSameDay($today); $cards = [ [ 'key' => 'total', 'label' => __('appointments::messages.appointments.card_total'), 'icon' => 'bi-calendar-week-fill', 'tone' => 'violet', ], [ 'key' => 'completed', 'label' => __('appointments::messages.appointments.card_completed'), 'icon' => 'bi-check-circle-fill', 'tone' => 'green', ], [ 'key' => 'pending', 'label' => __('appointments::messages.appointments.card_pending'), 'icon' => 'bi-clock-fill', 'tone' => 'blue', ], [ 'key' => 'cancelled', 'label' => __('appointments::messages.appointments.card_cancelled'), 'icon' => 'bi-x-circle-fill', 'tone' => 'pink', ], ]; $pillFor = static fn (AppointmentStatus $status): string => match ($status) { AppointmentStatus::Scheduled => 'px-pill--grey', AppointmentStatus::Confirmed => 'px-pill--blue', AppointmentStatus::CheckedIn => 'px-pill--violet', AppointmentStatus::InProgress => 'px-pill--amber', AppointmentStatus::Completed => 'px-pill--green', AppointmentStatus::Cancelled, AppointmentStatus::NoShow => 'px-pill--red', }; /* | Cancelling and marking a no-show both require a written reason, so they | are not offered as one-click menu items — those two go to the record | where the reason can actually be given. The rest are safe to fire from | the list, which is where reception works from. */ $reasonRequired = [AppointmentStatus::Cancelled, AppointmentStatus::NoShow]; @endphp @section('content') {{-- ── Page head ──────────────────────────────────────────────────────── --}}

{{ __('appointments::messages.appointments.title') }}

{{ __('appointments::messages.appointments.subtitle') }}

{{ __('appointments::messages.appointments.calendar_view') }} @can('create', Appointment::class) {{ __('appointments::messages.appointments.new') }} @endcan
{{-- ── Filters ────────────────────────────────────────────────────────── --}} {{-- Outside the results region on purpose: that region is replaced wholesale when a filter changes, which would destroy the field somebody is typing into. --}}
@php $activeField = \App\Modules\Patients\Enums\PatientSearchField::tryFrom($filters['search_field'] ?? '') ?? $defaultSearchField; $searchRules = []; foreach ($searchFields as $searchField) { $searchRules[$searchField->value] = ['placeholder' => $searchField->placeholder()]; } @endphp {{-- One row of filters, sized by content rather than by a twelve-column grid: five controls and two buttons do not divide into twelve, and forcing them to left "Apply filters" wrapping onto two lines. The search comes first because it is what reception reaches for — somebody is on the phone giving a number. The rest narrow what the search returns: when, whose, which department, what state. --}}
@foreach ($searchFields as $searchField) @endforeach
{{-- Hidden, not deleted: the pills above already say "search by", and the placeholder repeats it. It stays in the markup so a screen reader still gets the field a name rather than reading the hint as one. --}}
@if ($canSeeAllDoctors)
@endif @if ($departments !== [])
@endif
{{ __('common.reset') }} {{-- No flex-grow: the button takes the width of its label, so "Apply filters" stays on one line whatever the row does around it. --}}
{{-- Everything below is scoped to the selected day, so all of it sits inside the swapped region. Leaving the cards outside would leave them showing yesterday's totals after a date change — worse than not showing them at all on a clinical screen. --}}
{{-- ── Summary cards ──────────────────────────────────────── --}}
@foreach ($cards as $card) @php $value = $summary['tally'][$card['key']]; $delta = $value - $summary['previous'][$card['key']]; @endphp

{{ $card['label'] }}

{{ number_format($value) }}

@if ($delta !== 0) @endif {{ __('appointments::messages.appointments.vs_previous', [ 'delta' => $delta > 0 ? '+'.$delta : (string) $delta, ]) }}

@endforeach
{{-- ── Day list ───────────────────────────────────────────── --}}

{{ $date->isoFormat('dddd, D MMMM YYYY') }}

{{ trans_choice('appointments::messages.appointments.count', $appointments->count(), ['count' => $appointments->count()]) }}

@unless ($isToday) {{ __('appointments::messages.appointments.today') }} @endunless
@if ($appointments->isEmpty()) {{-- ── Empty state ────────────────────────────────── --}}

{{ __('appointments::messages.appointments.empty_title') }}

{{ __('appointments::messages.appointments.empty_text') }}

@else
@foreach ($appointments as $appointment) @php $patient = $patients[$appointment->patient_id] ?? null; $doctorSummary = $doctorSummaries[$appointment->doctor_id] ?? null; $name = $patient?->name ?? __('appointments::messages.appointments.unknown_patient'); /* | Reason-requiring moves belong on the appointment's own | screen, where there is room to type one; the rest are | offered here only to somebody who may actually make | them, so no row shows a button the server would refuse. */ $transitions = collect($appointment->status->allowedTransitions()) ->reject(fn (AppointmentStatus $s): bool => in_array($s, $reasonRequired, true)) ->filter(fn (AppointmentStatus $s): bool => auth()->user()?->can($s->requiredAbility(), $appointment) ?? false); $checkIn = $triage[$appointment->id] ?? null; @endphp @endforeach
{{ __('appointments::messages.appointments.time') }} {{ __('appointments::messages.appointments.patient') }} {{ __('appointments::messages.appointments.doctor') }} {{ __('appointments::messages.appointments.department') }} {{ __('appointments::messages.appointments.visit_type') }} {{ __('common.status') }} {{ __('common.actions') }}
{{ $appointment->localStart()->format('H:i') }} @if ($appointment->queue_number) #{{ $appointment->queue_number }} @endif
{{ $name }} @if ($patient?->mrn) {{ $patient->mrn }} @endif
{{-- The allergy marker travels with the patient summary. Reception seeing it while working the day list is a cheap safety cue and does not require them to have sight of the clinical record. --}} @if (($patient?->activeAllergyCount ?? 0) > 0) {{ $patient->activeAllergyCount }} @endif {{-- Check-in observations. The red flag is the reason this column exists: reception needs to see, without opening anything, who should not be left waiting. --}} @if ($checkIn?->hasRedFlag) {{ __('consultations::messages.red_flags.short') }} @elseif ($checkIn?->recorded) @endif
{{ $doctorSummary?->name ?? '—' }} {{ $doctorSummary?->specialty ?? '—' }} {{ $appointment->source->label() }} {{ $appointment->status->label() }}
@endif
{{-- ── Day context ────────────────────────────────────────────── --}}
@endsection