Add My ward nurse station for bedside clinical work.
Deploy Ladill Care / deploy (push) Successful in 39s

Floor nurses keep Nursing Services admin-only, but now get a My ward
home keyed off today's roster or unit placement with patient lists and
direct links to MAR, vitals/assessments, notes, and handovers. Care unit
show and board crumbs open for station users so clinical pages are
reachable without department admin rights.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
isaacclad
2026-07-20 15:54:26 +00:00
co-authored by Cursor
parent 96f36a60d6
commit 45da731561
15 changed files with 515 additions and 24 deletions
@@ -2,8 +2,29 @@
<div class="space-y-6">
<div class="flex flex-wrap items-start justify-between gap-4">
<div>
@php
$member = auth()->user()
? app(\App\Services\Care\OrganizationResolver::class)->memberFor(auth()->user())
: null;
$permissions = app(\App\Services\Care\CarePermissions::class);
$canAdminUnits = $permissions->can($member, 'admin.departments.view');
$canStation = $permissions->can($member, 'nursing.station.view');
$canManageUnit = $permissions->can($member, 'admin.departments.manage');
$canMar = $permissions->can($member, 'mar.view');
$canNotes = $permissions->can($member, 'nursing.notes.view');
$canHandover = $permissions->can($member, 'nursing.handover.view');
$canAssess = $permissions->can($member, 'nursing.assessments.view');
$canPerf = $permissions->can($member, 'nursing.performance.view');
$canRoster = $permissions->can($member, 'nursing.roster.view');
@endphp
<p class="text-sm text-slate-500">
<a href="{{ route('care.care-units.index') }}" class="text-indigo-600 hover:text-indigo-800">Care units</a>
@if ($canAdminUnits)
<a href="{{ route('care.care-units.index') }}" class="text-indigo-600 hover:text-indigo-800">Care units</a>
@elseif ($canStation)
<a href="{{ route('care.nursing.station', ['unit' => $unit->id]) }}" class="text-indigo-600 hover:text-indigo-800">My ward</a>
@else
Care units
@endif
<span class="text-slate-300">/</span>
{{ $unit->department?->name }}
</p>
@@ -18,19 +39,6 @@
</p>
</div>
<div class="flex flex-wrap gap-2">
@php
$member = auth()->user()
? app(\App\Services\Care\OrganizationResolver::class)->memberFor(auth()->user())
: null;
$permissions = app(\App\Services\Care\CarePermissions::class);
$canManageUnit = $permissions->can($member, 'admin.departments.manage');
$canMar = $permissions->can($member, 'mar.view');
$canNotes = $permissions->can($member, 'nursing.notes.view');
$canHandover = $permissions->can($member, 'nursing.handover.view');
$canAssess = $permissions->can($member, 'nursing.assessments.view');
$canPerf = $permissions->can($member, 'nursing.performance.view');
$canRoster = $permissions->can($member, 'nursing.roster.view');
@endphp
@if ($canMar)
<a href="{{ route('care.care-units.mar', $unit) }}" class="btn-primary">MAR board</a>
@endif
@@ -3,7 +3,7 @@
<div class="flex flex-wrap items-start justify-between gap-4">
<div>
<p class="text-sm text-slate-500">
<a href="{{ route('care.care-units.show', $unit) }}" class="text-indigo-600 hover:text-indigo-800">{{ $unit->name }}</a>
@include('care.nursing.partials.unit-crumb', ['unit' => $unit])
<span class="text-slate-300">/</span>
Nursing assessments
</p>
@@ -3,7 +3,7 @@
<div class="flex flex-wrap items-start justify-between gap-4">
<div>
<p class="text-sm text-slate-500">
<a href="{{ route('care.care-units.show', $unit) }}" class="text-indigo-600 hover:text-indigo-800">{{ $unit->name }}</a>
@include('care.nursing.partials.unit-crumb', ['unit' => $unit])
<span class="text-slate-300">/</span>
Ward handovers
</p>
@@ -16,7 +16,7 @@
<div class="flex flex-wrap items-start justify-between gap-4">
<div>
<p class="text-sm text-slate-500">
<a href="{{ route('care.care-units.show', $unit) }}" class="text-indigo-600 hover:text-indigo-800">{{ $unit->name }}</a>
@include('care.nursing.partials.unit-crumb', ['unit' => $unit])
<span class="text-slate-300">/</span>
Medication round
</p>
@@ -31,9 +31,17 @@
@endif
</p>
</div>
<span class="rounded-full bg-teal-100 px-2.5 py-0.5 text-xs font-medium text-teal-800">
{{ config('care.roster_entry_statuses.'.$entry->status, $entry->status) }}
</span>
<div class="flex flex-wrap items-center gap-2">
@if ($entry->careUnit && app(\App\Services\Care\CarePermissions::class)->can(
auth()->user() ? app(\App\Services\Care\OrganizationResolver::class)->memberFor(auth()->user()) : null,
'nursing.station.view'
))
<a href="{{ route('care.nursing.station', ['unit' => $entry->care_unit_id]) }}" class="rounded-lg bg-slate-900 px-2.5 py-1 text-xs font-semibold text-white hover:bg-slate-800">Open ward</a>
@endif
<span class="rounded-full bg-teal-100 px-2.5 py-0.5 text-xs font-medium text-teal-800">
{{ config('care.roster_entry_statuses.'.$entry->status, $entry->status) }}
</span>
</div>
</li>
@endforeach
</ul>
+1 -1
View File
@@ -2,7 +2,7 @@
<div class="space-y-6">
<div>
<p class="text-sm text-slate-500">
<a href="{{ route('care.care-units.show', $unit) }}" class="text-indigo-600 hover:text-indigo-800">{{ $unit->name }}</a>
@include('care.nursing.partials.unit-crumb', ['unit' => $unit])
<span class="text-slate-300">/</span>
Nursing notes
</p>
@@ -0,0 +1,9 @@
@php
$crumbMember = auth()->user()
? app(\App\Services\Care\OrganizationResolver::class)->memberFor(auth()->user())
: null;
$permissions = app(\App\Services\Care\CarePermissions::class);
$crumbToStation = $permissions->can($crumbMember, 'nursing.station.view')
&& ! $permissions->can($crumbMember, 'admin.departments.view');
@endphp
<a href="{{ $crumbToStation ? route('care.nursing.station', ['unit' => $unit->id]) : route('care.care-units.show', $unit) }}" class="text-indigo-600 hover:text-indigo-800">{{ $unit->name }}</a>
@@ -0,0 +1,153 @@
<x-app-layout title="My ward">
<div class="space-y-6">
<div class="flex flex-wrap items-start justify-between gap-4">
<div>
<h1 class="text-2xl font-semibold text-slate-900">My ward</h1>
<p class="mt-1 text-sm text-slate-500">
Patients on units youre rostered or placed on today chart meds, vitals, notes, and handovers.
</p>
</div>
<div class="flex flex-wrap gap-2">
<a href="{{ route('care.my-shifts') }}" class="btn-secondary">My shifts</a>
@if ($canServicesHub)
<a href="{{ route('care.nursing.services') }}" class="btn-secondary">Nursing Services</a>
@endif
</div>
</div>
@if ($units->isEmpty())
<div class="rounded-2xl border border-dashed border-slate-200 bg-white px-6 py-12 text-center">
<p class="text-base font-medium text-slate-900">No unit assigned for today</p>
<p class="mt-2 text-sm text-slate-500">
Ask a charge nurse or administrator to put you on the duty roster or a unit placement.
You can still check
<a href="{{ route('care.my-shifts') }}" class="font-medium text-indigo-600 hover:text-indigo-800">My shifts</a>.
</p>
</div>
@else
@if ($units->count() > 1)
<div class="flex flex-wrap gap-2">
@foreach ($units as $unit)
<a
href="{{ route('care.nursing.station', ['unit' => $unit->id]) }}"
@class([
'rounded-lg px-3 py-1.5 text-sm font-medium transition',
'bg-slate-900 text-white' => $selectedUnit && (int) $selectedUnit->id === (int) $unit->id,
'border border-slate-200 bg-white text-slate-700 hover:bg-slate-50' => ! $selectedUnit || (int) $selectedUnit->id !== (int) $unit->id,
])
>
{{ $unit->name }}
<span class="ml-1 tabular-nums opacity-70">{{ ($patientsByUnit[$unit->id] ?? collect())->count() }}</span>
</a>
@endforeach
</div>
@endif
@php
$unit = $selectedUnit;
$patients = $patientsByUnit[$unit->id] ?? collect();
$duty = $dutyByUnit[$unit->id] ?? collect();
$placement = $placementByUnit[$unit->id] ?? null;
@endphp
<div class="rounded-2xl border border-slate-200 bg-white p-5">
<div class="flex flex-wrap items-start justify-between gap-4">
<div>
<h2 class="text-lg font-semibold text-slate-900">{{ $unit->name }}</h2>
<p class="mt-0.5 text-sm text-slate-500">
{{ $unit->department?->name }}
@if ($unit->department?->branch)
· {{ $unit->department->branch->name }}
@endif
</p>
<div class="mt-2 flex flex-wrap gap-2 text-xs text-slate-600">
@foreach ($duty as $entry)
<span class="inline-flex items-center gap-1.5 rounded-md bg-teal-50 px-2 py-1 font-medium text-teal-800">
<span class="h-1.5 w-1.5 rounded-full" style="background: {{ $entry->shift?->color ?? '#14b8a6' }}"></span>
On duty · {{ $entry->shift?->label ?? 'Shift' }}
@if ($entry->shift)
({{ $entry->shift->timeLabel() }})
@endif
</span>
@endforeach
@if ($placement && $duty->isEmpty())
<span class="inline-flex rounded-md bg-slate-100 px-2 py-1 font-medium text-slate-700">
Unit placement · {{ str_replace('_', ' ', $placement->kind) }}
</span>
@elseif ($placement && $duty->isNotEmpty())
<span class="inline-flex rounded-md bg-slate-100 px-2 py-1 text-slate-600">
Also placed · {{ str_replace('_', ' ', $placement->kind) }}
</span>
@endif
</div>
</div>
<div class="flex flex-wrap gap-2">
@if ($canMar)
<a href="{{ route('care.care-units.mar', $unit) }}" class="btn-primary">MAR round</a>
@endif
@if ($canAssess || $canVitals)
<a href="{{ route('care.care-units.assessments', $unit) }}" class="btn-secondary">Vitals &amp; assess</a>
@endif
@if ($canNotes)
<a href="{{ route('care.care-units.notes', $unit) }}" class="btn-secondary">Notes</a>
@endif
@if ($canHandover)
<a href="{{ route('care.care-units.handovers', $unit) }}" class="btn-secondary">Handover</a>
@endif
</div>
</div>
<div class="mt-6 overflow-x-auto">
<table class="min-w-full text-sm">
<thead class="border-b border-slate-100 text-left text-xs uppercase tracking-wide text-slate-500">
<tr>
<th class="pb-2 pr-4">Patient</th>
<th class="pb-2 pr-4">Bed</th>
<th class="pb-2 pr-4">Placed</th>
<th class="pb-2 text-right">Chart</th>
</tr>
</thead>
<tbody class="divide-y divide-slate-50">
@forelse ($patients as $visit)
<tr>
<td class="py-3 pr-4">
<p class="font-medium text-slate-900">{{ $visit->patient?->fullName() ?? 'Patient' }}</p>
@if ($visit->patient?->patient_number)
<p class="text-xs text-slate-400">{{ $visit->patient->patient_number }}</p>
@endif
</td>
<td class="py-3 pr-4 text-slate-600">{{ $visit->bed?->label ?? '—' }}</td>
<td class="py-3 pr-4 whitespace-nowrap text-slate-500">
{{ $visit->placed_at?->format('d M H:i') ?? '—' }}
</td>
<td class="py-3 text-right whitespace-nowrap">
@if ($canMar)
<a href="{{ route('care.visits.mar', $visit) }}" class="mr-3 font-medium text-indigo-600 hover:text-indigo-800">MAR</a>
@endif
@if ($canAssess || $canVitals)
<a href="{{ route('care.care-units.assessments', $unit) }}#visit-{{ $visit->id }}" class="mr-3 text-slate-600 hover:text-slate-900">Vitals</a>
@endif
@if ($canNotes)
<a href="{{ route('care.care-units.notes', $unit) }}" class="text-slate-600 hover:text-slate-900">Notes</a>
@endif
</td>
</tr>
@empty
<tr>
<td colspan="4" class="py-10 text-center text-slate-500">
No patients placed on this unit yet.
@if ($canServicesHub)
Placements are managed from the care unit board.
@else
Ask a charge nurse to place patients on this unit.
@endif
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
@endif
</div>
</x-app-layout>
@@ -32,6 +32,16 @@
}
}
if ($permissions->can($member, 'nursing.station.view')) {
$nav[] = ['name' => 'My ward', 'route' => route('care.nursing.station'), 'active' => request()->routeIs('care.nursing.station')
|| (request()->routeIs('care.care-units.mar')
|| request()->routeIs('care.care-units.notes')
|| request()->routeIs('care.care-units.handovers*')
|| request()->routeIs('care.care-units.assessments')
|| request()->routeIs('care.visits.mar')),
'icon' => '<path stroke-linecap="round" stroke-linejoin="round" d="M2.25 21h19.5m-18-18v18m10.5-18v18m6-13.5V21M6.75 6.75h.75m-.75 3h.75m-.75 3h.75m3-6h.75m-.75 3h.75m-.75 3h.75M6.75 21v-3.375c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21M3 3h12m-.75 4.5H21m-3.75 3h.008v.008h-.008v-.008Zm0 3h.008v.008h-.008v-.008Zm0 3h.008v.008h-.008v-.008Z" />'];
}
if ($permissions->can($member, 'nursing.my_shifts.view')) {
$nav[] = ['name' => 'My shifts', 'route' => route('care.my-shifts'), 'active' => request()->routeIs('care.my-shifts'),
'icon' => '<path stroke-linecap="round" stroke-linejoin="round" d="M12 6v6h4.5m4.5 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />'];