Deploy Ladill Care / deploy (push) Successful in 51s
Open on waiting/called cards no longer requires consultations.manage so view roles can re-enter Emergency and other specialty workspaces. Co-authored-by: Cursor <cursoragent@cursor.com>
174 lines
8.2 KiB
PHP
174 lines
8.2 KiB
PHP
{{--
|
|
Shared patient-flow board (GP Queue + specialty Queue homes).
|
|
|
|
Expected vars:
|
|
- $queue, $inConsultation (collections of Appointment)
|
|
- $done (optional — completed today)
|
|
- $queueIntegration, $branchId
|
|
- $canConsult (bool)
|
|
- $queueCallNextRoute, $queueCallNextParams (optional — if set, Call next renders above board)
|
|
- $startRouteName (default care.queue.start) — route(name, $appointment)
|
|
- $openInCareUrl (callable Appointment $a): ?string — workspace/form URL for Open
|
|
(waiting / called / in-care / done; not gated by canConsult)
|
|
- $inCareLabel (default "In care")
|
|
- $lockToPractitioner, $canSwitchBranch, $branches, $practitioners, $practitionerId (filter bar)
|
|
- $showFilters (default true)
|
|
--}}
|
|
@php
|
|
$canConsult = $canConsult ?? false;
|
|
$showFilters = $showFilters ?? true;
|
|
$inCareLabel = $inCareLabel ?? 'In care';
|
|
$startRouteName = $startRouteName ?? 'care.queue.start';
|
|
$openInCareUrl = $openInCareUrl ?? function ($appointment) {
|
|
return $appointment->consultation
|
|
? route('care.consultations.show', $appointment->consultation)
|
|
: null;
|
|
};
|
|
$queueCallNextRoute = $queueCallNextRoute ?? null;
|
|
$queueCallNextParams = $queueCallNextParams ?? [];
|
|
$done = $done ?? collect();
|
|
|
|
$calledStatuses = ['called', 'serving'];
|
|
$waitingList = $queue->reject(
|
|
fn ($a) => in_array($a->queue_ticket_status ?? '', $calledStatuses, true)
|
|
)->values();
|
|
$calledList = $queue->filter(
|
|
fn ($a) => in_array($a->queue_ticket_status ?? '', $calledStatuses, true)
|
|
)->values();
|
|
|
|
$stages = [
|
|
[
|
|
'key' => 'waiting',
|
|
'label' => 'Waiting',
|
|
'count' => $waitingList->count(),
|
|
'items' => $waitingList,
|
|
'empty' => 'No patients waiting.',
|
|
'accent' => 'text-slate-500',
|
|
'dot' => 'bg-slate-400',
|
|
],
|
|
[
|
|
'key' => 'called',
|
|
'label' => 'Called',
|
|
'count' => $calledList->count(),
|
|
'items' => $calledList,
|
|
'empty' => 'No one called yet.',
|
|
'accent' => 'text-indigo-600',
|
|
'dot' => 'bg-indigo-500',
|
|
],
|
|
[
|
|
'key' => 'in_care',
|
|
'label' => $inCareLabel,
|
|
'count' => $inConsultation->count(),
|
|
'items' => $inConsultation,
|
|
'empty' => 'No active encounters.',
|
|
'accent' => 'text-emerald-700',
|
|
'dot' => 'bg-emerald-500',
|
|
],
|
|
[
|
|
'key' => 'done',
|
|
'label' => 'Done',
|
|
'count' => $done->count(),
|
|
'items' => $done,
|
|
'empty' => 'None completed today.',
|
|
'accent' => 'text-slate-400',
|
|
'dot' => 'bg-slate-300',
|
|
],
|
|
];
|
|
@endphp
|
|
|
|
@if ($showFilters)
|
|
<form method="GET" class="flex flex-wrap items-center gap-3 rounded-xl border border-slate-200 bg-white px-4 py-3">
|
|
@if ($lockToPractitioner ?? false)
|
|
@if (($canSwitchBranch ?? false) && isset($branches) && $branches->count() > 1)
|
|
<select name="branch_id" class="rounded-lg border-slate-300 text-sm" onchange="this.form.submit()">
|
|
@foreach ($branches as $branch)
|
|
<option value="{{ $branch->id }}" @selected($branchId == $branch->id)>{{ $branch->name }}</option>
|
|
@endforeach
|
|
</select>
|
|
@elseif (isset($branches) && $branches->isNotEmpty())
|
|
<span class="text-sm font-medium text-slate-700">{{ $branches->first()->name }}</span>
|
|
@elseif (! empty($branchLabel))
|
|
<span class="text-sm font-medium text-slate-700">{{ $branchLabel }}</span>
|
|
@endif
|
|
<span class="text-sm text-slate-500">Assigned to you</span>
|
|
@elseif ($canSwitchBranch ?? false)
|
|
<select name="branch_id" class="rounded-lg border-slate-300 text-sm" onchange="this.form.submit()">
|
|
@foreach ($branches as $branch)
|
|
<option value="{{ $branch->id }}" @selected($branchId == $branch->id)>{{ $branch->name }}</option>
|
|
@endforeach
|
|
</select>
|
|
<select name="practitioner_id" class="rounded-lg border-slate-300 text-sm">
|
|
<option value="">All practitioners</option>
|
|
@foreach ($practitioners as $practitioner)
|
|
<option value="{{ $practitioner->id }}" @selected($practitionerId == $practitioner->id)>{{ $practitioner->name }}</option>
|
|
@endforeach
|
|
</select>
|
|
<button type="submit" class="btn-primary">Filter</button>
|
|
@else
|
|
@if (isset($branches) && $branches->isNotEmpty())
|
|
<input type="hidden" name="branch_id" value="{{ $branchId }}">
|
|
<span class="text-sm font-medium text-slate-700">{{ $branches->first()->name }}</span>
|
|
@elseif (! empty($branchLabel))
|
|
<span class="text-sm font-medium text-slate-700">{{ $branchLabel }}</span>
|
|
@endif
|
|
@if (isset($practitioners))
|
|
<select name="practitioner_id" class="rounded-lg border-slate-300 text-sm">
|
|
<option value="">All practitioners</option>
|
|
@foreach ($practitioners as $practitioner)
|
|
<option value="{{ $practitioner->id }}" @selected($practitionerId == $practitioner->id)>{{ $practitioner->name }}</option>
|
|
@endforeach
|
|
</select>
|
|
<button type="submit" class="btn-primary">Filter</button>
|
|
@endif
|
|
@endif
|
|
</form>
|
|
@endif
|
|
|
|
<div class="overflow-hidden rounded-2xl border border-slate-200 bg-white shadow-sm shadow-slate-100/80">
|
|
@if ($queueCallNextRoute)
|
|
<div class="border-b border-slate-100 bg-gradient-to-r from-indigo-50/90 via-white to-slate-50 px-4 py-3 sm:px-5">
|
|
@include('care.partials.queue-ops', [
|
|
'queueIntegration' => $queueIntegration ?? null,
|
|
'queueCallNextRoute' => $queueCallNextRoute,
|
|
'queueCallNextParams' => $queueCallNextParams,
|
|
'branchId' => $branchId ?? null,
|
|
'variant' => 'bar',
|
|
])
|
|
@if (empty($queueIntegration['enabled']))
|
|
<p class="text-sm text-slate-500">Start patients from the waiting column when they arrive at your desk.</p>
|
|
@endif
|
|
</div>
|
|
@endif
|
|
|
|
<div class="flex snap-x snap-mandatory gap-0 overflow-x-auto lg:grid lg:grid-cols-4 lg:overflow-visible" style="-ms-overflow-style:none;scrollbar-width:none;">
|
|
@foreach ($stages as $index => $stage)
|
|
<section @class([
|
|
'flex w-[min(85vw,20rem)] shrink-0 snap-start flex-col border-slate-100 lg:w-auto',
|
|
'border-r' => $index < count($stages) - 1,
|
|
])>
|
|
<header class="sticky top-0 z-[1] flex items-center justify-between gap-2 border-b border-slate-100 bg-white/95 px-4 py-3 backdrop-blur-sm">
|
|
<div class="flex items-center gap-2">
|
|
<span class="h-2 w-2 rounded-full {{ $stage['dot'] }}"></span>
|
|
<h2 class="text-xs font-semibold uppercase tracking-wide {{ $stage['accent'] }}">{{ $stage['label'] }}</h2>
|
|
</div>
|
|
<span class="rounded-md bg-slate-100 px-2 py-0.5 text-xs font-semibold tabular-nums text-slate-700">{{ $stage['count'] }}</span>
|
|
</header>
|
|
<div class="flex-1 space-y-2 p-3 sm:min-h-[12rem]">
|
|
@forelse ($stage['items'] as $appointment)
|
|
@include('care.partials.queue-board-card', [
|
|
'appointment' => $appointment,
|
|
'stage' => $stage['key'],
|
|
'canConsult' => $canConsult,
|
|
'queueIntegration' => $queueIntegration ?? null,
|
|
'startRouteName' => $startRouteName,
|
|
'openInCareUrl' => $openInCareUrl,
|
|
])
|
|
@empty
|
|
<p class="rounded-xl border border-dashed border-slate-200 px-3 py-8 text-center text-xs text-slate-400">{{ $stage['empty'] }}</p>
|
|
@endforelse
|
|
</div>
|
|
</section>
|
|
@endforeach
|
|
</div>
|
|
</div>
|