@extends('layouts.app') @section('title', __('consultations::messages.encounters.title')) @php use App\Modules\Consultations\Enums\EncounterStatus; use App\Modules\Consultations\Models\Encounter; $isToday = $date->isSameDay($today); /* | The brief's palette names four states; the record only has three. | There is no "in progress" encounter — a consultation is open until it is | closed — so blue is left for the duration chip rather than invented as a | status nothing can ever be in. */ $statusPill = static fn (EncounterStatus $status): string => match ($status) { EncounterStatus::Open => 'px-pill--amber', EncounterStatus::Closed => 'px-pill--green', EncounterStatus::Cancelled => 'px-pill--red', }; $cards = [ [ 'key' => 'total', 'label' => __('consultations::messages.encounters.card_total'), 'icon' => 'bi-clipboard2-pulse-fill', 'tone' => 'violet', 'suffix' => '', ], [ 'key' => 'open', 'label' => __('consultations::messages.encounters.card_open'), 'icon' => 'bi-folder2-open', 'tone' => 'blue', 'suffix' => '', ], [ 'key' => 'completed', 'label' => __('consultations::messages.encounters.card_completed'), 'icon' => 'bi-check2-circle', 'tone' => 'green', 'suffix' => '', ], [ 'key' => 'minutes', 'label' => __('consultations::messages.encounters.card_duration'), 'icon' => 'bi-stopwatch-fill', 'tone' => 'pink', 'suffix' => __('consultations::messages.encounters.minutes_short'), ], ]; /* | Whether anything is actually narrowing the day. | | search_field is excluded: it always carries a value — the pill shown as | selected — so counting it would make a screen nobody has filtered offer | to clear filters that were never applied. It narrows nothing on its own; | it only says where the term beside it is aimed. */ $hasFilters = collect($filters) ->except(['search_field']) ->filter(fn ($v): bool => $v !== '') ->isNotEmpty(); @endphp @section('content') {{-- ── Page head ──────────────────────────────────────────────────────── --}}

{{ __('consultations::messages.encounters.title') }}

{{ __('consultations::messages.encounters.subtitle') }}

{{ __('consultations::messages.encounters.todays') }} {{-- A consultation is opened against a patient, so the entry point is the patient record rather than a blank form. --}} @can('create', Encounter::class) @if (Route::has('patients.index')) {{ __('consultations::messages.encounters.new') }} @endif @endcan
{{-- ── Filter panel ───────────────────────────────────────────────────── --}} {{-- Outside the swapped region on purpose: that region is replaced wholesale on every filter change, which would destroy the field somebody is typing into. --}}
@php $activeField = \App\Modules\Patients\Enums\PatientSearchField::tryFrom($filters['search_field'] ?? '') ?? $defaultSearchField; // Read by Clinic.searchField, so the box re-labels itself the // moment the pill changes rather than after a round trip. $searchRules = []; foreach ($searchFields as $searchField) { $searchRules[$searchField->value] = ['placeholder' => $searchField->placeholder()]; } @endphp
{{-- Which identifier the term is aimed at. Outside the grid, so the pills sit on the box they aim rather than a gutter above it. --}}
@foreach ($searchFields as $searchField) @endforeach
@if ($departments !== [])
@endif
{{ __('common.reset') }}
{{-- Everything below is scoped to the current filters, so all of it sits inside the swapped region. Leaving the cards outside would leave them describing the previous query — worse than not showing them at all on a clinical screen. --}}
{{-- ── Statistics ─────────────────────────────────────────── --}}
@foreach ($cards as $card) @php $value = $summary['tally'][$card['key']]; $delta = $value - $summary['previous'][$card['key']]; @endphp

{{ $card['label'] }}

{{ number_format($value) }}{{ $card['suffix'] }}

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

@endforeach
{{-- ── Consultations ──────────────────────────────────────── --}}

{{ __('consultations::messages.encounters.title') }}

{{ __('consultations::messages.encounters.showing', [ 'from' => number_format($encounters->firstItem() ?? 0), 'to' => number_format($encounters->lastItem() ?? 0), 'total' => number_format($encounters->total()), ]) }}

@if ($encounters->isEmpty()) {{-- ── Empty state ────────────────────────────────── --}}

{{ __('consultations::messages.encounters.empty_title') }}

{{ $hasFilters ? __('consultations::messages.encounters.empty_filtered') : __('consultations::messages.encounters.empty_text') }}

@can('create', Encounter::class) @if (Route::has('patients.index')) {{ __('consultations::messages.encounters.new') }} @endif @endcan @if ($hasFilters) {{ __('consultations::messages.encounters.clear_filters') }} @endif
@else
{{-- The reference joins the patient rather than opening the table. It identifies the consultation you are already looking at, so it belongs beside the name, not in the widest column on the screen. --}} @foreach ($encounters as $encounter) @php $patient = $participants['patients'][$encounter->patient_id] ?? null; $doctor = $participants['doctors'][$encounter->doctor_id] ?? null; $patientName = $patient?->name ?? __('consultations::messages.encounters.unknown_patient'); $primary = $encounter->diagnoses->first(); @endphp @endforeach
{{ __('consultations::messages.encounters.patient') }} {{ __('consultations::messages.encounters.clinician') }} {{ __('consultations::messages.encounters.department') }} {{ __('consultations::messages.encounters.started') }} {{ __('consultations::messages.encounters.duration') }} {{ __('consultations::messages.encounters.diagnosis') }} {{ __('consultations::messages.encounters.disposition') }} {{ __('common.status') }} {{ __('common.actions') }}
{{-- No avatar. Coloured initials next to every name were decoration competing with the allergy marker, which is the one thing here that genuinely needs to catch the eye. --}}
@if ($patient !== null && Route::has('patients.show')) {{ $patientName }} @else {{ $patientName }} @endif {{-- Guarded: a walk-in recorded before the patient was matched has no MRN, and an unconditional line left a blank gap between the name and the reference. --}} @if ($patient?->mrn) {{ $patient->mrn }} @if ($patient->ageLabel) · {{ $patient->ageLabel }} @endif @endif {{ $encounter->reference }}
@if (($patient?->activeAllergyCount ?? 0) > 0) {{ $patient->activeAllergyCount }} @endif
{{ $doctor?->name ?? '—' }} @if ($doctor?->specialty) {{ $doctor->specialty }} @else — @endif {{ $encounter->started_at->format('d M Y') }} {{ $encounter->started_at->format('H:i') }} {{ $encounter->durationMinutes() }}{{ __('consultations::messages.encounters.minutes_short') }} @if ($primary !== null) {{ $primary->name }} @elseif ($encounter->diagnoses_count > 0) {{ trans_choice('consultations::messages.encounters.diagnoses_count', $encounter->diagnoses_count, ['count' => $encounter->diagnoses_count]) }} @else — @endif @if ($encounter->disposition) {{ $encounter->disposition->label() }} @else — @endif {{ $encounter->status->label() }}
@if ($encounters->hasPages())
{{ $encounters->links() }}
@endif @endif
{{-- ── Context ────────────────────────────────────────────────── --}}
@endsection