Give specialty modules the same Queue UI as GPs.
Deploy Ladill Care / deploy (push) Successful in 36s
Deploy Ladill Care / deploy (push) Successful in 36s
Specialty homes now use the shared Waiting/In care board with Call next, Start routes specialty tickets into specialty workspaces, and redundant Dentistry/Specialty module chrome is removed. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -163,16 +163,29 @@ class QueueController extends Controller
|
||||
|
||||
abort_unless($branchId > 0, 422);
|
||||
|
||||
$context = CareQueueContexts::CONSULTATION;
|
||||
if ($practitionerId) {
|
||||
$prac = Practitioner::owned($this->ownerRef($request))->find($practitionerId);
|
||||
$specialtyContext = $this->queueBridge->contextForPractitioner($organization, $prac);
|
||||
if ($specialtyContext !== CareQueueContexts::CONSULTATION) {
|
||||
$context = $specialtyContext;
|
||||
}
|
||||
}
|
||||
|
||||
$result = $this->queueBridge->callNext(
|
||||
$organization,
|
||||
CareQueueContexts::CONSULTATION,
|
||||
$context,
|
||||
$branchId,
|
||||
$member,
|
||||
$practitionerId,
|
||||
);
|
||||
$ticket = $result['ticket'] ?? null;
|
||||
if (! $ticket) {
|
||||
return back()->with('info', 'No patients waiting at your consultation service point.');
|
||||
$label = $context === CareQueueContexts::CONSULTATION
|
||||
? 'consultation'
|
||||
: $context;
|
||||
|
||||
return back()->with('info', "No patients waiting at your {$label} service point.");
|
||||
}
|
||||
|
||||
return back()->with('success', 'Called '.$ticket['ticket_number'].' — '.($ticket['customer_name'] ?? 'patient').'.');
|
||||
@@ -234,6 +247,26 @@ class QueueController extends Controller
|
||||
$this->ownerRef($request),
|
||||
);
|
||||
|
||||
$organization = $this->organization($request);
|
||||
$appointment = $appointment->fresh(['visit', 'department']);
|
||||
$specialtyContext = $this->queueBridge->contextForAppointment($organization, $appointment);
|
||||
|
||||
if ($specialtyContext !== CareQueueContexts::CONSULTATION && $appointment->visit) {
|
||||
$clinical = app(\App\Services\Care\SpecialtyClinicalRecordService::class);
|
||||
$shell = app(\App\Services\Care\SpecialtyShellService::class);
|
||||
$preferredTab = collect(array_keys($shell->workspaceTabs($specialtyContext)))
|
||||
->first(fn (string $tab) => $clinical->recordTypeForTab($specialtyContext, $tab) !== null)
|
||||
?? 'clinical_notes';
|
||||
|
||||
return redirect()
|
||||
->route('care.specialty.workspace', [
|
||||
'module' => $specialtyContext,
|
||||
'visit' => $appointment->visit,
|
||||
'tab' => $preferredTab,
|
||||
])
|
||||
->with('success', 'Encounter started.');
|
||||
}
|
||||
|
||||
return redirect()->route('care.consultations.show', $consultation)
|
||||
->with('success', 'Consultation started.');
|
||||
}
|
||||
|
||||
@@ -137,9 +137,19 @@ class SpecialtyModuleController extends Controller
|
||||
$owner = $this->ownerRef($request);
|
||||
$openConsultation = $appointment->consultation;
|
||||
if ($openConsultation && $openConsultation->status !== \App\Models\Consultation::STATUS_COMPLETED) {
|
||||
$clinical = app(SpecialtyClinicalRecordService::class);
|
||||
$shellTabs = app(SpecialtyShellService::class)->workspaceTabs($module);
|
||||
$preferredTab = collect(array_keys($shellTabs))
|
||||
->first(fn (string $tab) => $clinical->recordTypeForTab($module, $tab) !== null)
|
||||
?? 'clinical_notes';
|
||||
|
||||
return redirect()
|
||||
->route('care.consultations.show', $openConsultation)
|
||||
->with('success', 'Consultation already in progress.');
|
||||
->route('care.specialty.workspace', [
|
||||
'module' => $module,
|
||||
'visit' => $visit,
|
||||
'tab' => $preferredTab,
|
||||
])
|
||||
->with('success', 'Encounter already in progress.');
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -180,7 +190,7 @@ class SpecialtyModuleController extends Controller
|
||||
'visit' => $visit,
|
||||
'tab' => $preferredTab,
|
||||
])
|
||||
->with('success', 'Specialty encounter started.');
|
||||
->with('success', 'Encounter started.');
|
||||
}
|
||||
|
||||
public function saveClinical(
|
||||
@@ -342,18 +352,18 @@ class SpecialtyModuleController extends Controller
|
||||
if ($waitingAtBranch > 0) {
|
||||
return back()->with(
|
||||
'info',
|
||||
"{$waitingAtBranch} patient(s) are waiting in {$definition['label']} at {$branchName}, but none have a callable ticket at a service desk yet. Open Visits and use Start consultation, or check in again so a ticket is issued."
|
||||
"{$waitingAtBranch} patient(s) are waiting at {$branchName}, but none have a callable ticket at a service desk yet. Use Start on the queue, or check in again so a ticket is issued."
|
||||
);
|
||||
}
|
||||
|
||||
if ($waitingOrg > $waitingAtBranch) {
|
||||
return back()->with(
|
||||
'info',
|
||||
"No patients waiting in {$definition['label']} at {$branchName}. {$waitingOrg} are waiting at other branches — open Visits or switch branch."
|
||||
"No patients waiting at {$branchName}. {$waitingOrg} are waiting at other branches — switch branch or open History."
|
||||
);
|
||||
}
|
||||
|
||||
return back()->with('info', 'No patients waiting in this specialty queue.');
|
||||
return back()->with('info', 'No patients waiting in this queue.');
|
||||
}
|
||||
|
||||
$name = $ticket['customer_name'] ?? 'patient';
|
||||
@@ -420,7 +430,22 @@ class SpecialtyModuleController extends Controller
|
||||
->with(['patient', 'practitioner', 'department', 'visit'])
|
||||
->orderBy('queue_position')
|
||||
->orderBy('checked_in_at')
|
||||
->limit(25)
|
||||
->limit(50)
|
||||
->get();
|
||||
|
||||
$inConsultation = Appointment::owned($owner)
|
||||
->where('organization_id', $organization->id)
|
||||
->where('status', Appointment::STATUS_IN_CONSULTATION)
|
||||
->when($departmentIds !== [], fn ($q) => $q->whereIn('department_id', $departmentIds))
|
||||
->when($departmentIds === [], fn ($q) => $q->whereRaw('1 = 0'))
|
||||
->when($kpiBranch, fn ($q) => $q->where('branch_id', $kpiBranch))
|
||||
->when(
|
||||
$practitionerScope !== null,
|
||||
fn ($q) => $q->whereIn('practitioner_id', $practitionerScope ?: [0]),
|
||||
)
|
||||
->with(['patient', 'practitioner', 'consultation', 'visit'])
|
||||
->orderBy('started_at')
|
||||
->limit(50)
|
||||
->get();
|
||||
|
||||
$shellDefinition = $shell->definition($module);
|
||||
@@ -431,6 +456,13 @@ class SpecialtyModuleController extends Controller
|
||||
: collect();
|
||||
$services = $shell->provisionedServices($organization, $module);
|
||||
|
||||
$permissions = app(\App\Services\Care\CarePermissions::class);
|
||||
$canConsult = $permissions->can($member, 'consultations.manage');
|
||||
$canManageQueue = $permissions->can($member, 'appointments.manage');
|
||||
$branchLabel = $branchId
|
||||
? (string) (\App\Models\Branch::owned($owner)->whereKey($branchId)->value('name') ?? '')
|
||||
: '';
|
||||
|
||||
$workspaceVisit = null;
|
||||
$patientHeader = null;
|
||||
$timeline = [];
|
||||
@@ -511,6 +543,8 @@ class SpecialtyModuleController extends Controller
|
||||
'section' => $section,
|
||||
'navItems' => $shell->navItems($module),
|
||||
'waiting' => $waiting,
|
||||
'queue' => $waiting,
|
||||
'inConsultation' => $inConsultation,
|
||||
'openVisits' => $openVisits,
|
||||
'visitHistory' => $visitHistory,
|
||||
'kpis' => $kpis,
|
||||
@@ -528,7 +562,11 @@ class SpecialtyModuleController extends Controller
|
||||
'visitDocuments' => $visitDocuments,
|
||||
'queueStubs' => $modules->queueStubsFor($organization, $module),
|
||||
'branchId' => $branchId,
|
||||
'branchLabel' => $branchLabel,
|
||||
'practitionerScope' => $practitionerScope,
|
||||
'lockToPractitioner' => $practitionerScope !== null,
|
||||
'canConsult' => $canConsult,
|
||||
'canManageQueue' => $canManageQueue,
|
||||
'queueIntegration' => $branchId
|
||||
? $queueBridge->listMeta($organization, $module, $branchId)
|
||||
: ['enabled' => false],
|
||||
|
||||
@@ -40,6 +40,15 @@ class CareQueueBridge
|
||||
return CareQueueContexts::CONSULTATION;
|
||||
}
|
||||
|
||||
return $this->contextForDepartment($organization, (int) $departmentId);
|
||||
}
|
||||
|
||||
public function contextForDepartment(Organization $organization, int $departmentId): string
|
||||
{
|
||||
if ($departmentId <= 0) {
|
||||
return CareQueueContexts::CONSULTATION;
|
||||
}
|
||||
|
||||
foreach ($this->specialties->enabledKeys($organization) as $key) {
|
||||
$deptIds = data_get($organization->settings, "specialty_module_provisioning.{$key}.department_ids", []);
|
||||
if (is_array($deptIds) && in_array($departmentId, array_map('intval', $deptIds), true)) {
|
||||
@@ -50,6 +59,15 @@ class CareQueueBridge
|
||||
return CareQueueContexts::CONSULTATION;
|
||||
}
|
||||
|
||||
public function contextForPractitioner(Organization $organization, ?Practitioner $practitioner): string
|
||||
{
|
||||
if (! $practitioner?->department_id) {
|
||||
return CareQueueContexts::CONSULTATION;
|
||||
}
|
||||
|
||||
return $this->contextForDepartment($organization, (int) $practitioner->department_id);
|
||||
}
|
||||
|
||||
public function issueForAppointment(Organization $organization, Appointment $appointment): ?Appointment
|
||||
{
|
||||
if (! $this->isEnabled($organization) || filled($appointment->queue_ticket_uuid)) {
|
||||
|
||||
@@ -302,7 +302,7 @@ class CareQueueProvisioner
|
||||
$points = [];
|
||||
$roomIndex = 1;
|
||||
$roomPrefix = CareQueueContexts::isSpecialty($context)
|
||||
? (string) (config("care.specialty_modules.{$context}.label") ?? 'Room')
|
||||
? 'Bay'
|
||||
: 'Consultation Room';
|
||||
|
||||
foreach ($practitioners as $practitioner) {
|
||||
|
||||
@@ -878,7 +878,7 @@ class DemoTenantSeeder
|
||||
'member_id' => $assignMember ? $member->id : null,
|
||||
'name' => $practitionerName,
|
||||
'specialty' => (string) ($definition['label'] ?? $key),
|
||||
'room' => ($definition['label'] ?? 'Specialty').' Bay',
|
||||
'room' => 'Bay '.(($branchIndex % 4) + 1),
|
||||
'is_active' => true,
|
||||
'deleted_at' => null,
|
||||
],
|
||||
|
||||
@@ -463,12 +463,10 @@ class SpecialtyShellService
|
||||
|
||||
public function navItems(string $moduleKey): array
|
||||
{
|
||||
// Queue home is the primary surface; history/billing are secondary links in the shell header.
|
||||
return [
|
||||
'overview' => ['label' => 'Overview', 'route' => 'care.specialty.show'],
|
||||
'visits' => ['label' => 'Visits', 'route' => 'care.specialty.visits'],
|
||||
'history' => ['label' => 'History', 'route' => 'care.specialty.history'],
|
||||
'billing' => ['label' => 'Billing', 'route' => 'care.specialty.billing'],
|
||||
'workspace' => ['label' => 'Workspace', 'route' => 'care.specialty.workspace'],
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -34,12 +34,12 @@ return [
|
||||
'documents' => 'Documents',
|
||||
],
|
||||
'actions' => [
|
||||
'call_next' => 'Call next patient',
|
||||
'start' => 'Start specialty encounter',
|
||||
'call_next' => 'Call next',
|
||||
'start' => 'Start',
|
||||
'complete' => 'Complete',
|
||||
'order_test' => 'Order test',
|
||||
'prescribe' => 'Prescribe',
|
||||
'invoice' => 'Generate invoice',
|
||||
'invoice' => 'Add to invoice',
|
||||
'discharge' => 'Discharge',
|
||||
],
|
||||
],
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
{{--
|
||||
Shared Waiting | In care board (GP Queue + specialty Queue homes).
|
||||
|
||||
Expected vars:
|
||||
- $queue, $inConsultation (collections of Appointment)
|
||||
- $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 — link for in-care Open
|
||||
- $inCareLabel (default "In consultation")
|
||||
- $lockToPractitioner, $canSwitchBranch, $branches, $practitioners, $practitionerId (filter bar)
|
||||
- $showFilters (default true)
|
||||
--}}
|
||||
@php
|
||||
$canConsult = $canConsult ?? false;
|
||||
$showFilters = $showFilters ?? true;
|
||||
$inCareLabel = $inCareLabel ?? 'In consultation';
|
||||
$startRouteName = $startRouteName ?? 'care.queue.start';
|
||||
$openInCareUrl = $openInCareUrl ?? function ($appointment) {
|
||||
return $appointment->consultation
|
||||
? route('care.consultations.show', $appointment->consultation)
|
||||
: null;
|
||||
};
|
||||
$queueCallNextRoute = $queueCallNextRoute ?? null;
|
||||
$queueCallNextParams = $queueCallNextParams ?? [];
|
||||
@endphp
|
||||
|
||||
@if ($showFilters)
|
||||
<form method="GET" class="flex flex-wrap gap-3 rounded-2xl border border-slate-200 bg-white p-4">
|
||||
@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="flex items-center text-sm font-medium text-slate-700">{{ $branches->first()->name }}</span>
|
||||
@elseif (! empty($branchLabel))
|
||||
<span class="flex items-center text-sm font-medium text-slate-700">{{ $branchLabel }}</span>
|
||||
@endif
|
||||
<span class="flex items-center text-sm text-slate-500">Showing patients 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="flex items-center text-sm font-medium text-slate-700">{{ $branches->first()->name }}</span>
|
||||
@elseif (! empty($branchLabel))
|
||||
<span class="flex items-center 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
|
||||
|
||||
@if ($queueCallNextRoute)
|
||||
@include('care.partials.queue-ops', [
|
||||
'queueIntegration' => $queueIntegration ?? null,
|
||||
'queueCallNextRoute' => $queueCallNextRoute,
|
||||
'queueCallNextParams' => $queueCallNextParams,
|
||||
'branchId' => $branchId ?? null,
|
||||
])
|
||||
@endif
|
||||
|
||||
<div class="grid gap-6 lg:grid-cols-2">
|
||||
<section class="rounded-2xl border border-slate-200 bg-white p-6">
|
||||
<h2 class="text-sm font-semibold uppercase tracking-wide text-slate-500">Waiting ({{ $queue->count() }})</h2>
|
||||
<div class="mt-4 space-y-3">
|
||||
@forelse ($queue as $appointment)
|
||||
<div @class([
|
||||
'flex items-start justify-between gap-4 rounded-xl border p-4',
|
||||
'border-indigo-200 bg-indigo-50/60' => ($appointment->queue_ticket_status ?? null) === 'called',
|
||||
'border-emerald-200 bg-emerald-50' => ($appointment->queue_ticket_status ?? null) === 'serving',
|
||||
'border-slate-100 bg-slate-50' => ! in_array($appointment->queue_ticket_status ?? null, ['called', 'serving'], true),
|
||||
])>
|
||||
<div class="min-w-0 flex-1 space-y-2">
|
||||
@if (! empty($queueIntegration['enabled']) && $appointment->queue_ticket_number)
|
||||
@include('care.partials.queue-ticket', [
|
||||
'ticketNumber' => $appointment->queue_ticket_number,
|
||||
'ticketStatus' => $appointment->queue_ticket_status,
|
||||
'destination' => $appointment->queue_destination,
|
||||
'staffDisplayName' => $appointment->queue_staff_display_name,
|
||||
])
|
||||
@elseif (! empty($queueIntegration['enabled']) && ($appointment->queue_routing_status ?? '') === 'unresolved')
|
||||
<span class="inline-flex rounded-full bg-amber-100 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-amber-800">Unassigned</span>
|
||||
@elseif ($appointment->queue_position)
|
||||
<span class="inline-flex h-7 w-7 items-center justify-center rounded-full bg-sky-100 text-xs font-bold text-sky-700">{{ $appointment->queue_position }}</span>
|
||||
@endif
|
||||
|
||||
<div class="min-w-0">
|
||||
<p class="truncate text-base font-semibold text-slate-900">{{ $appointment->patient?->fullName() ?? 'Patient' }}</p>
|
||||
<p class="mt-0.5 truncate text-xs text-slate-500">{{ $appointment->patient?->patient_number ?? '—' }} · {{ $appointment->reason ?? '—' }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex shrink-0 flex-wrap items-center justify-end gap-2">
|
||||
@if (! empty($queueIntegration['enabled']) && $appointment->queue_ticket_uuid && in_array($appointment->queue_ticket_status ?? '', ['called', 'serving'], true) && $canConsult)
|
||||
<form method="POST" action="{{ route('care.queue.recall', $appointment) }}">
|
||||
@csrf
|
||||
<button type="submit" class="rounded-lg border border-indigo-200 bg-indigo-50 px-3 py-1.5 text-xs font-medium text-indigo-700 hover:bg-indigo-100">Call again</button>
|
||||
</form>
|
||||
@endif
|
||||
@if ($canConsult)
|
||||
<form method="POST" action="{{ route($startRouteName, $appointment) }}">
|
||||
@csrf
|
||||
<button type="submit" class="rounded-lg bg-sky-600 px-3 py-1.5 text-xs font-medium text-white hover:bg-sky-700">
|
||||
{{ ! empty($queueIntegration['enabled']) && ($appointment->queue_ticket_status ?? '') === 'called' ? 'Serve & start' : 'Start' }}
|
||||
</button>
|
||||
</form>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@empty
|
||||
<p class="text-sm text-slate-500">No patients waiting.</p>
|
||||
@endforelse
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="rounded-2xl border border-slate-200 bg-white p-6">
|
||||
<h2 class="text-sm font-semibold uppercase tracking-wide text-slate-500">{{ $inCareLabel }} ({{ $inConsultation->count() }})</h2>
|
||||
<div class="mt-4 space-y-3">
|
||||
@forelse ($inConsultation as $appointment)
|
||||
<div class="flex items-start justify-between gap-4 rounded-xl border border-emerald-100 bg-emerald-50 p-4">
|
||||
<div class="min-w-0 flex-1 space-y-2">
|
||||
@if (! empty($queueIntegration['enabled']) && $appointment->queue_ticket_number)
|
||||
@include('care.partials.queue-ticket', [
|
||||
'ticketNumber' => $appointment->queue_ticket_number,
|
||||
'ticketStatus' => $appointment->queue_ticket_status,
|
||||
'showAssignment' => false,
|
||||
])
|
||||
@endif
|
||||
<div class="min-w-0">
|
||||
<p class="truncate text-base font-semibold text-slate-900">{{ $appointment->patient?->fullName() ?? 'Patient' }}</p>
|
||||
<p class="mt-0.5 truncate text-xs text-slate-500">{{ $appointment->practitioner?->name ?? 'Unassigned' }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex shrink-0 flex-wrap items-center justify-end gap-2">
|
||||
@if (! empty($queueIntegration['enabled']) && $appointment->queue_ticket_uuid && in_array($appointment->queue_ticket_status ?? '', ['called', 'serving'], true) && $canConsult)
|
||||
<form method="POST" action="{{ route('care.queue.recall', $appointment) }}">
|
||||
@csrf
|
||||
<button type="submit" class="rounded-lg border border-indigo-200 bg-indigo-50 px-3 py-1.5 text-xs font-medium text-indigo-700 hover:bg-indigo-100">Call again</button>
|
||||
</form>
|
||||
@endif
|
||||
@php $openUrl = $openInCareUrl($appointment); @endphp
|
||||
@if ($openUrl)
|
||||
<a href="{{ $openUrl }}" class="text-sm font-medium text-sky-600 hover:text-sky-700">Open</a>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@empty
|
||||
<p class="text-sm text-slate-500">No active encounters.</p>
|
||||
@endforelse
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
@@ -16,142 +16,21 @@
|
||||
@endif
|
||||
</x-care.page-hero>
|
||||
|
||||
<form method="GET" class="flex flex-wrap gap-3 rounded-2xl border border-slate-200 bg-white p-4">
|
||||
@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="flex items-center text-sm font-medium text-slate-700">{{ $branches->first()->name }}</span>
|
||||
@endif
|
||||
<span class="flex items-center text-sm text-slate-500">Showing patients 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="flex items-center text-sm font-medium text-slate-700">{{ $branches->first()->name }}</span>
|
||||
@endif
|
||||
<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
|
||||
</form>
|
||||
|
||||
@include('care.partials.queue-ops', [
|
||||
@include('care.partials.queue-board', [
|
||||
'queue' => $queue,
|
||||
'inConsultation' => $inConsultation,
|
||||
'queueIntegration' => $queueIntegration ?? null,
|
||||
'branchId' => $branchId,
|
||||
'canConsult' => $canConsult,
|
||||
'lockToPractitioner' => $lockToPractitioner ?? false,
|
||||
'canSwitchBranch' => $canSwitchBranch ?? false,
|
||||
'branches' => $branches ?? collect(),
|
||||
'practitioners' => $practitioners ?? collect(),
|
||||
'practitionerId' => $practitionerId ?? null,
|
||||
'queueCallNextRoute' => 'care.queue.call-next',
|
||||
'queueCallNextParams' => array_filter(['practitioner_id' => $practitionerId]),
|
||||
'branchId' => $branchId,
|
||||
'startRouteName' => 'care.queue.start',
|
||||
'inCareLabel' => 'In consultation',
|
||||
])
|
||||
|
||||
<div class="grid gap-6 lg:grid-cols-2">
|
||||
<section class="rounded-2xl border border-slate-200 bg-white p-6">
|
||||
<h2 class="text-sm font-semibold uppercase tracking-wide text-slate-500">Waiting ({{ $queue->count() }})</h2>
|
||||
<div class="mt-4 space-y-3">
|
||||
@forelse ($queue as $appointment)
|
||||
<div @class([
|
||||
'flex items-start justify-between gap-4 rounded-xl border p-4',
|
||||
'border-indigo-200 bg-indigo-50/60' => ($appointment->queue_ticket_status ?? null) === 'called',
|
||||
'border-emerald-200 bg-emerald-50' => ($appointment->queue_ticket_status ?? null) === 'serving',
|
||||
'border-slate-100 bg-slate-50' => ! in_array($appointment->queue_ticket_status ?? null, ['called', 'serving'], true),
|
||||
])>
|
||||
<div class="min-w-0 flex-1 space-y-2">
|
||||
@if (! empty($queueIntegration['enabled']) && $appointment->queue_ticket_number)
|
||||
@include('care.partials.queue-ticket', [
|
||||
'ticketNumber' => $appointment->queue_ticket_number,
|
||||
'ticketStatus' => $appointment->queue_ticket_status,
|
||||
'destination' => $appointment->queue_destination,
|
||||
'staffDisplayName' => $appointment->queue_staff_display_name,
|
||||
])
|
||||
@elseif (! empty($queueIntegration['enabled']) && ($appointment->queue_routing_status ?? '') === 'unresolved')
|
||||
<span class="inline-flex rounded-full bg-amber-100 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-amber-800">Unassigned</span>
|
||||
@elseif ($appointment->queue_position)
|
||||
<span class="inline-flex h-7 w-7 items-center justify-center rounded-full bg-sky-100 text-xs font-bold text-sky-700">{{ $appointment->queue_position }}</span>
|
||||
@endif
|
||||
|
||||
<div class="min-w-0">
|
||||
<p class="truncate text-base font-semibold text-slate-900">{{ $appointment->patient->fullName() }}</p>
|
||||
<p class="mt-0.5 truncate text-xs text-slate-500">{{ $appointment->patient->patient_number }} · {{ $appointment->reason ?? '—' }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex shrink-0 flex-wrap items-center justify-end gap-2">
|
||||
@if (! empty($queueIntegration['enabled']) && $appointment->queue_ticket_uuid && in_array($appointment->queue_ticket_status ?? '', ['called', 'serving'], true) && $canConsult)
|
||||
<form method="POST" action="{{ route('care.queue.recall', $appointment) }}">
|
||||
@csrf
|
||||
<button type="submit" class="rounded-lg border border-indigo-200 bg-indigo-50 px-3 py-1.5 text-xs font-medium text-indigo-700 hover:bg-indigo-100">Call again</button>
|
||||
</form>
|
||||
@endif
|
||||
@if ($canConsult)
|
||||
<form method="POST" action="{{ route('care.queue.start', $appointment) }}">
|
||||
@csrf
|
||||
<button type="submit" class="rounded-lg bg-sky-600 px-3 py-1.5 text-xs font-medium text-white hover:bg-sky-700">
|
||||
{{ ! empty($queueIntegration['enabled']) && ($appointment->queue_ticket_status ?? '') === 'called' ? 'Serve & start' : 'Start' }}
|
||||
</button>
|
||||
</form>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@empty
|
||||
<p class="text-sm text-slate-500">No patients waiting.</p>
|
||||
@endforelse
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="rounded-2xl border border-slate-200 bg-white p-6">
|
||||
<h2 class="text-sm font-semibold uppercase tracking-wide text-slate-500">In consultation ({{ $inConsultation->count() }})</h2>
|
||||
<div class="mt-4 space-y-3">
|
||||
@forelse ($inConsultation as $appointment)
|
||||
<div class="flex items-start justify-between gap-4 rounded-xl border border-emerald-100 bg-emerald-50 p-4">
|
||||
<div class="min-w-0 flex-1 space-y-2">
|
||||
@if (! empty($queueIntegration['enabled']) && $appointment->queue_ticket_number)
|
||||
@include('care.partials.queue-ticket', [
|
||||
'ticketNumber' => $appointment->queue_ticket_number,
|
||||
'ticketStatus' => $appointment->queue_ticket_status,
|
||||
'showAssignment' => false,
|
||||
])
|
||||
@endif
|
||||
<div class="min-w-0">
|
||||
<p class="truncate text-base font-semibold text-slate-900">{{ $appointment->patient->fullName() }}</p>
|
||||
<p class="mt-0.5 truncate text-xs text-slate-500">{{ $appointment->practitioner?->name ?? 'Unassigned' }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex shrink-0 flex-wrap items-center justify-end gap-2">
|
||||
@if (! empty($queueIntegration['enabled']) && $appointment->queue_ticket_uuid && in_array($appointment->queue_ticket_status ?? '', ['called', 'serving'], true) && $canConsult)
|
||||
<form method="POST" action="{{ route('care.queue.recall', $appointment) }}">
|
||||
@csrf
|
||||
<button type="submit" class="rounded-lg border border-indigo-200 bg-indigo-50 px-3 py-1.5 text-xs font-medium text-indigo-700 hover:bg-indigo-100">Call again</button>
|
||||
</form>
|
||||
@endif
|
||||
@if ($appointment->consultation)
|
||||
<a href="{{ route('care.consultations.show', $appointment->consultation) }}" class="text-sm font-medium text-sky-600 hover:text-sky-700">Open</a>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@empty
|
||||
<p class="text-sm text-slate-500">No active consultations.</p>
|
||||
@endforelse
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
|
||||
@@ -42,8 +42,7 @@
|
||||
@endif
|
||||
</div>
|
||||
<p class="mt-3 text-[11px] uppercase tracking-wide text-slate-400">
|
||||
Dept · {{ $module['department_name'] ?? $module['label'] }}
|
||||
· Queue · {{ $module['queue_name'] ?? $module['label'] }}
|
||||
{{ $module['department_name'] ?? $module['label'] }}
|
||||
</p>
|
||||
</label>
|
||||
@endforeach
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
{{-- Specialty shell action buttons (desktop sticky + mobile sheet). --}}
|
||||
{{-- Specialty action buttons (desktop sticky + mobile sheet). --}}
|
||||
@php
|
||||
$btn = 'flex w-full items-center justify-center rounded-xl px-3 py-2.5 text-sm font-semibold transition';
|
||||
$btnPrimary = $btn.' bg-indigo-600 text-white hover:bg-indigo-700';
|
||||
$btnSecondary = $btn.' border border-slate-200 bg-white text-slate-800 hover:bg-slate-50';
|
||||
$btnAccent = $btn.' border border-indigo-200 bg-indigo-50 text-indigo-900 hover:bg-indigo-100';
|
||||
$onWorkspace = ($section ?? '') === 'workspace';
|
||||
@endphp
|
||||
|
||||
@include('care.partials.queue-ops', [
|
||||
'queueIntegration' => $queueIntegration ?? null,
|
||||
'queueCallNextRoute' => 'care.specialty.call-next',
|
||||
'queueCallNextParams' => ['module' => $moduleKey],
|
||||
'branchId' => $branchId ?? null,
|
||||
])
|
||||
@if ($onWorkspace)
|
||||
@include('care.partials.queue-ops', [
|
||||
'queueIntegration' => $queueIntegration ?? null,
|
||||
'queueCallNextRoute' => 'care.specialty.call-next',
|
||||
'queueCallNextParams' => ['module' => $moduleKey],
|
||||
'branchId' => $branchId ?? null,
|
||||
])
|
||||
@endif
|
||||
|
||||
@if ($workspaceVisit)
|
||||
@php
|
||||
@@ -34,20 +37,16 @@
|
||||
@if ($consultationInProgress)
|
||||
<a href="{{ route('care.specialty.workspace', ['module' => $moduleKey, 'visit' => $workspaceVisit, 'tab' => $chartTab]) }}"
|
||||
class="{{ $btnPrimary }}">
|
||||
Open specialty chart
|
||||
Open chart
|
||||
</a>
|
||||
@elseif ($canStartConsultation)
|
||||
<form method="POST" action="{{ route('care.specialty.consultation.start', ['module' => $moduleKey, 'visit' => $workspaceVisit]) }}" class="w-full" @click.stop>
|
||||
@csrf
|
||||
<button type="submit" class="{{ $btnPrimary }}">
|
||||
{{ $actions['start'] ?? 'Start specialty encounter' }}
|
||||
{{ $actions['start'] ?? 'Start' }}
|
||||
</button>
|
||||
</form>
|
||||
@endif
|
||||
<a href="{{ route('care.specialty.workspace', ['module' => $moduleKey, 'visit' => $workspaceVisit]) }}"
|
||||
class="{{ $btnSecondary }}">
|
||||
Specialty workspace
|
||||
</a>
|
||||
@if ($workspaceVisit->patient)
|
||||
<a href="{{ route('care.patients.show', $workspaceVisit->patient) }}" class="{{ $btnSecondary }}">
|
||||
Patient chart
|
||||
@@ -58,12 +57,12 @@
|
||||
Appointment
|
||||
</a>
|
||||
@endif
|
||||
@else
|
||||
<a href="{{ route('care.specialty.workspace', ['module' => $moduleKey]) }}" class="{{ $btnPrimary }}">
|
||||
Open workspace
|
||||
</a>
|
||||
@endif
|
||||
|
||||
<a href="{{ route('care.specialty.show', $moduleKey) }}" class="{{ $btnSecondary }}">
|
||||
Back to queue
|
||||
</a>
|
||||
|
||||
<a href="{{ route('care.appointments.create') }}" class="{{ $btnAccent }}">
|
||||
Register / book
|
||||
</a>
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
@if (! $workspaceVisit)
|
||||
<section class="rounded-2xl border border-dashed border-slate-200 bg-white px-4 py-12 text-center">
|
||||
<h2 class="text-sm font-semibold text-slate-900">No open visit selected</h2>
|
||||
<p class="mt-1 text-sm text-slate-500">Open a visit from the Visits list, or call next from the waiting queue.</p>
|
||||
<a href="{{ route('care.specialty.visits', $moduleKey) }}" class="mt-4 inline-flex text-sm font-medium text-indigo-600">Go to visits</a>
|
||||
<p class="mt-1 text-sm text-slate-500">Open a visit from the queue, or call next from the waiting list.</p>
|
||||
<a href="{{ route('care.specialty.show', $moduleKey) }}" class="mt-4 inline-flex text-sm font-medium text-indigo-600">Back to queue</a>
|
||||
</section>
|
||||
@else
|
||||
{{-- Module tabs --}}
|
||||
|
||||
@@ -1,77 +1,116 @@
|
||||
<x-app-layout title="{{ $definition['label'] ?? $moduleKey }}">
|
||||
@php
|
||||
$pageTitle = match ($section) {
|
||||
'workspace' => ($patientHeader['patient'] ?? null)?->fullName() ?: 'Workspace',
|
||||
'history' => 'Visit history',
|
||||
'billing' => 'Billing',
|
||||
'visits' => 'Patient queue',
|
||||
default => 'Patient queue',
|
||||
};
|
||||
@endphp
|
||||
<x-app-layout :title="$pageTitle">
|
||||
<div class="space-y-4">
|
||||
<div class="flex flex-wrap items-start justify-between gap-4">
|
||||
<div>
|
||||
<p class="text-xs font-semibold uppercase tracking-wider text-indigo-600">Specialty module</p>
|
||||
<h1 class="mt-1 text-xl font-semibold text-slate-900">{{ $definition['label'] ?? $moduleKey }}</h1>
|
||||
<p class="mt-1 max-w-2xl text-sm text-slate-500">{{ $definition['description'] ?? '' }}</p>
|
||||
@if ($section === 'workspace')
|
||||
<div class="flex flex-wrap items-center justify-between gap-3">
|
||||
<a href="{{ route('care.specialty.show', $moduleKey) }}" class="text-sm font-medium text-indigo-600 hover:text-indigo-800">← Back to queue</a>
|
||||
<div class="flex flex-wrap gap-3 text-sm">
|
||||
<a href="{{ route('care.specialty.history', $moduleKey) }}" class="text-slate-600 hover:text-slate-900">History</a>
|
||||
<a href="{{ route('care.specialty.billing', $moduleKey) }}" class="text-slate-600 hover:text-slate-900">Billing</a>
|
||||
</div>
|
||||
</div>
|
||||
<a href="{{ route('care.settings.modules') }}" class="text-sm font-medium text-indigo-600 hover:text-indigo-800">Manage modules</a>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-4 lg:grid-cols-[220px_minmax(0,1fr)_16rem]">
|
||||
{{-- Left navigation --}}
|
||||
<nav class="rounded-2xl border border-slate-200 bg-white p-3 lg:sticky lg:top-4 lg:self-start">
|
||||
<p class="px-2 pb-2 text-[10px] font-semibold uppercase tracking-wider text-slate-400">Navigate</p>
|
||||
<ul class="space-y-0.5">
|
||||
@foreach ($navItems as $key => $item)
|
||||
@php
|
||||
$active = $section === $key;
|
||||
$params = ['module' => $moduleKey];
|
||||
if ($key === 'workspace' && $workspaceVisit) {
|
||||
$params['visit'] = $workspaceVisit;
|
||||
}
|
||||
@endphp
|
||||
<li>
|
||||
<a href="{{ route($item['route'], $params) }}"
|
||||
@class([
|
||||
'block rounded-xl px-3 py-2 text-sm font-medium',
|
||||
'bg-indigo-50 text-indigo-800' => $active,
|
||||
'text-slate-600 hover:bg-slate-50 hover:text-slate-900' => ! $active,
|
||||
])>
|
||||
{{ $item['label'] }}
|
||||
</a>
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
|
||||
@if (! empty($stages))
|
||||
<p class="mt-4 px-2 pb-2 text-[10px] font-semibold uppercase tracking-wider text-slate-400">Stage map</p>
|
||||
<ol class="space-y-1 px-1">
|
||||
@foreach ($stages as $stage)
|
||||
<li class="flex items-center gap-2 rounded-lg px-2 py-1.5 text-xs text-slate-600">
|
||||
<span class="flex h-5 w-5 items-center justify-center rounded-full bg-slate-100 text-[10px] font-semibold text-slate-500">{{ $loop->iteration }}</span>
|
||||
{{ $stage['label'] ?? $stage['code'] }}
|
||||
</li>
|
||||
@endforeach
|
||||
</ol>
|
||||
@endif
|
||||
</nav>
|
||||
|
||||
{{-- Main workspace --}}
|
||||
<div class="min-w-0 space-y-4">
|
||||
@if ($section === 'overview')
|
||||
@include('care.specialty.sections.overview')
|
||||
@elseif ($section === 'visits')
|
||||
@include('care.specialty.sections.visits')
|
||||
@elseif ($section === 'history')
|
||||
@include('care.specialty.sections.history')
|
||||
@elseif ($section === 'billing')
|
||||
@include('care.specialty.sections.billing')
|
||||
@elseif ($section === 'workspace')
|
||||
<div class="lg:grid lg:grid-cols-[minmax(0,1fr)_16rem] lg:items-start lg:gap-6">
|
||||
<div class="min-w-0 space-y-4">
|
||||
@include('care.specialty.sections.workspace')
|
||||
@endif
|
||||
</div>
|
||||
@include('care.partials.clinical-actions-panel', [
|
||||
'actionsView' => 'care.specialty.partials.actions-menu',
|
||||
'title' => 'Actions',
|
||||
])
|
||||
</div>
|
||||
|
||||
@include('care.partials.clinical-actions-panel', [
|
||||
@include('care.partials.clinical-actions-mobile', [
|
||||
'actionsView' => 'care.specialty.partials.actions-menu',
|
||||
'title' => 'Actions',
|
||||
])
|
||||
</div>
|
||||
</div>
|
||||
@elseif ($section === 'history')
|
||||
<div class="flex flex-wrap items-center justify-between gap-3">
|
||||
<div>
|
||||
<h1 class="text-xl font-semibold text-slate-900">Visit history</h1>
|
||||
<p class="mt-1 text-sm text-slate-500">Completed encounters for this service line.</p>
|
||||
</div>
|
||||
<a href="{{ route('care.specialty.show', $moduleKey) }}" class="text-sm font-medium text-indigo-600 hover:text-indigo-800">← Back to queue</a>
|
||||
</div>
|
||||
@include('care.specialty.sections.history')
|
||||
@elseif ($section === 'billing')
|
||||
<div class="flex flex-wrap items-center justify-between gap-3">
|
||||
<div>
|
||||
<h1 class="text-xl font-semibold text-slate-900">Billing</h1>
|
||||
<p class="mt-1 text-sm text-slate-500">Service catalog for this module.</p>
|
||||
</div>
|
||||
<a href="{{ route('care.specialty.show', $moduleKey) }}" class="text-sm font-medium text-indigo-600 hover:text-indigo-800">← Back to queue</a>
|
||||
</div>
|
||||
@include('care.specialty.sections.billing')
|
||||
@else
|
||||
{{-- Queue home (overview / visits) — same UI as GP Queue --}}
|
||||
<div class="lg:grid lg:grid-cols-[minmax(0,1fr)_16rem] lg:items-start lg:gap-6">
|
||||
<div class="min-w-0 space-y-6">
|
||||
<x-care.page-hero
|
||||
badge="Waiting · In care · Call next"
|
||||
title="Patient queue"
|
||||
description="See who is waiting, start encounters, and keep patient flow moving."
|
||||
:stats="[
|
||||
['value' => number_format($kpis['waiting'] ?? $queue->count()), 'label' => 'Waiting'],
|
||||
['value' => number_format($kpis['in_progress'] ?? $inConsultation->count()), 'label' => 'In care'],
|
||||
['value' => number_format($kpis['completed_today'] ?? 0), 'label' => 'Today'],
|
||||
]">
|
||||
@if ($canManageQueue ?? false)
|
||||
<x-slot name="actions">
|
||||
<a href="{{ route('care.appointments.walk-in.create') }}" class="btn-primary">Walk-in</a>
|
||||
</x-slot>
|
||||
@endif
|
||||
</x-care.page-hero>
|
||||
|
||||
@include('care.partials.clinical-actions-mobile', [
|
||||
'actionsView' => 'care.specialty.partials.actions-menu',
|
||||
'title' => 'Actions',
|
||||
])
|
||||
<div class="flex flex-wrap gap-3 text-sm">
|
||||
<a href="{{ route('care.specialty.history', $moduleKey) }}" class="rounded-lg border border-slate-200 bg-white px-3 py-1.5 font-medium text-slate-700 hover:bg-slate-50">History</a>
|
||||
<a href="{{ route('care.specialty.billing', $moduleKey) }}" class="rounded-lg border border-slate-200 bg-white px-3 py-1.5 font-medium text-slate-700 hover:bg-slate-50">Billing</a>
|
||||
</div>
|
||||
|
||||
@include('care.partials.queue-board', [
|
||||
'queue' => $queue,
|
||||
'inConsultation' => $inConsultation,
|
||||
'queueIntegration' => $queueIntegration ?? null,
|
||||
'branchId' => $branchId,
|
||||
'branchLabel' => $branchLabel ?? null,
|
||||
'canConsult' => $canConsult ?? false,
|
||||
'lockToPractitioner' => $lockToPractitioner ?? false,
|
||||
'showFilters' => true,
|
||||
'queueCallNextRoute' => 'care.specialty.call-next',
|
||||
'queueCallNextParams' => ['module' => $moduleKey],
|
||||
'startRouteName' => 'care.queue.start',
|
||||
'inCareLabel' => 'In care',
|
||||
'openInCareUrl' => function ($appointment) use ($moduleKey) {
|
||||
if ($appointment->visit) {
|
||||
return route('care.specialty.workspace', [
|
||||
'module' => $moduleKey,
|
||||
'visit' => $appointment->visit,
|
||||
]);
|
||||
}
|
||||
|
||||
return $appointment->consultation
|
||||
? route('care.consultations.show', $appointment->consultation)
|
||||
: null;
|
||||
},
|
||||
])
|
||||
</div>
|
||||
|
||||
@include('care.partials.clinical-actions-panel', [
|
||||
'actionsView' => 'care.specialty.partials.actions-menu',
|
||||
'title' => 'Actions',
|
||||
])
|
||||
</div>
|
||||
@include('care.partials.clinical-actions-mobile', [
|
||||
'actionsView' => 'care.specialty.partials.actions-menu',
|
||||
'title' => 'Actions',
|
||||
])
|
||||
@endif
|
||||
</div>
|
||||
</x-app-layout>
|
||||
|
||||
@@ -236,9 +236,11 @@ class CareSpecialtyModulesTest extends TestCase
|
||||
$this->actingAs($nurse)
|
||||
->get(route('care.specialty.show', 'dentistry'))
|
||||
->assertOk()
|
||||
->assertSee('Dentistry')
|
||||
->assertSee('Overview')
|
||||
->assertSee('Stage map');
|
||||
->assertSee('Patient queue')
|
||||
->assertSee('Waiting')
|
||||
->assertSee('In care')
|
||||
->assertDontSee('Specialty module')
|
||||
->assertDontSee('Stage map');
|
||||
}
|
||||
|
||||
public function test_unassigned_doctor_cannot_open_specialty_module(): void
|
||||
|
||||
@@ -97,15 +97,16 @@ class CareSpecialtyShellTest extends TestCase
|
||||
$this->actingAs($this->owner)
|
||||
->get(route('care.specialty.show', 'emergency'))
|
||||
->assertOk()
|
||||
->assertSee('Emergency')
|
||||
->assertSee('Overview')
|
||||
->assertSee('Patient queue')
|
||||
->assertSee('Waiting')
|
||||
->assertSee('Stage map');
|
||||
->assertSee('In care')
|
||||
->assertDontSee('Specialty module')
|
||||
->assertDontSee('Stage map');
|
||||
|
||||
$this->actingAs($this->owner)
|
||||
->get(route('care.specialty.visits', 'emergency'))
|
||||
->assertOk()
|
||||
->assertSee('Open visits');
|
||||
->assertSee('Patient queue');
|
||||
|
||||
$this->actingAs($this->owner)
|
||||
->get(route('care.specialty.history', 'emergency'))
|
||||
@@ -120,7 +121,8 @@ class CareSpecialtyShellTest extends TestCase
|
||||
|
||||
$this->actingAs($this->owner)
|
||||
->get(route('care.specialty.workspace', 'emergency'))
|
||||
->assertOk();
|
||||
->assertOk()
|
||||
->assertSee('Back to queue');
|
||||
}
|
||||
|
||||
public function test_dentistry_shell_has_chair_stage_and_catalog(): void
|
||||
|
||||
Reference in New Issue
Block a user