Gate ward admin nav to nursing roles and add My shifts for staff.
Deploy Ladill Care / deploy (push) Successful in 52s

Ambulance and other non-nursing vitals roles no longer see Care units / Nursing Services; staff get a personal duty roster view of assigned shifts.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
isaacclad
2026-07-20 11:26:52 +00:00
co-authored by Cursor
parent 76241ac2b5
commit 7c32b34715
8 changed files with 410 additions and 9 deletions
@@ -0,0 +1,98 @@
<x-app-layout title="My shifts">
<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 shifts</h1>
<p class="mt-1 text-sm text-slate-500">
Your duty roster for
{{ $weekStart->format('d M Y') }} {{ $weekStart->copy()->addDays(6)->format('d M Y') }}.
</p>
</div>
<div class="flex flex-wrap gap-2">
<a href="{{ route('care.my-shifts', ['week' => $prevWeek]) }}" class="btn-secondary">Previous</a>
<a href="{{ route('care.my-shifts', ['week' => now()->startOfWeek(\Carbon\Carbon::MONDAY)->toDateString()]) }}" class="btn-secondary">This week</a>
<a href="{{ route('care.my-shifts', ['week' => $nextWeek]) }}" class="btn-secondary">Next</a>
</div>
</div>
@if ($todayEntries->isNotEmpty())
<div class="rounded-2xl border border-teal-200 bg-teal-50 p-5">
<h2 class="text-sm font-semibold uppercase tracking-wide text-teal-800">Today</h2>
<ul class="mt-3 space-y-2">
@foreach ($todayEntries as $entry)
<li class="flex flex-wrap items-center justify-between gap-2 rounded-xl bg-white/80 px-4 py-3 text-sm">
<div>
<p class="font-semibold text-slate-900">{{ $entry->shift?->label ?? 'Shift' }}</p>
<p class="text-xs text-slate-500">
{{ $entry->shift?->timeLabel() }}
· {{ $entry->careUnit?->name ?? 'Unit' }}
@if ($entry->careUnit?->department)
({{ $entry->careUnit->department->name }})
@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>
</li>
@endforeach
</ul>
</div>
@else
<div class="rounded-2xl border border-slate-200 bg-white px-5 py-4 text-sm text-slate-500">
No duty assigned for today.
</div>
@endif
<div class="overflow-hidden rounded-2xl border border-slate-200 bg-white">
<table class="min-w-full text-sm">
<thead class="bg-slate-50 text-left text-xs uppercase text-slate-500">
<tr>
<th class="px-4 py-3">Day</th>
<th class="px-4 py-3">Shift</th>
<th class="px-4 py-3">Unit</th>
<th class="px-4 py-3">Status</th>
</tr>
</thead>
<tbody class="divide-y divide-slate-50">
@foreach ($days as $day)
@php $dayEntries = $entriesByDate[$day->toDateString()] ?? []; @endphp
@forelse ($dayEntries as $entry)
<tr class="{{ $day->isToday() ? 'bg-teal-50/40' : '' }}">
<td class="px-4 py-3 whitespace-nowrap">
<span class="font-medium text-slate-900">{{ $day->format('D') }}</span>
<span class="text-slate-400">{{ $day->format('d M') }}</span>
@if ($day->isToday())
<span class="ml-1 text-[10px] font-semibold uppercase text-teal-700">Today</span>
@endif
</td>
<td class="px-4 py-3">
<span class="mr-1 inline-block h-2 w-2 rounded-full" style="background: {{ $entry->shift?->color ?? '#94a3b8' }}"></span>
{{ $entry->shift?->label ?? '—' }}
<div class="text-xs text-slate-400">{{ $entry->shift?->timeLabel() }}</div>
</td>
<td class="px-4 py-3">
{{ $entry->careUnit?->name ?? '—' }}
@if ($entry->careUnit?->department)
<div class="text-xs text-slate-400">{{ $entry->careUnit->department->name }}</div>
@endif
</td>
<td class="px-4 py-3 capitalize text-slate-600">
{{ config('care.roster_entry_statuses.'.$entry->status, $entry->status) }}
</td>
</tr>
@empty
<tr class="{{ $day->isToday() ? 'bg-teal-50/40' : '' }}">
<td class="px-4 py-3 whitespace-nowrap text-slate-500">
<span class="font-medium text-slate-700">{{ $day->format('D') }}</span>
{{ $day->format('d M') }}
</td>
<td class="px-4 py-3 text-slate-400" colspan="3">Off / not rostered</td>
</tr>
@endforelse
@endforeach
</tbody>
</table>
</div>
</div>
</x-app-layout>
+9 -1
View File
@@ -32,6 +32,11 @@
}
}
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" />'];
}
if (! empty($hasPaidPlan) && $permissions->can($member, 'displays.view')) {
$nav[] = ['name' => 'Displays', 'route' => route('care.displays.index'), 'active' => request()->routeIs('care.displays.*'),
'icon' => '<path stroke-linecap="round" stroke-linejoin="round" d="M6 20.25h12m-7.5-4v4m3-4v4M6.75 4.5h10.5a2.25 2.25 0 0 1 2.25 2.25v6.75a2.25 2.25 0 0 1-2.25 2.25H6.75A2.25 2.25 0 0 1 4.5 13.5V6.75A2.25 2.25 0 0 1 6.75 4.5Z" />'];
@@ -124,7 +129,10 @@
$adminNav[] = ['name' => 'Departments', 'route' => route('care.departments.index'), 'active' => request()->routeIs('care.departments.*'),
'icon' => '<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6A2.25 2.25 0 0 1 6 3.75h2.25A2.25 2.25 0 0 1 10.5 6v2.25a2.25 2.25 0 0 1-2.25 2.25H6a2.25 2.25 0 0 1-2.25-2.25V6ZM3.75 15.75A2.25 2.25 0 0 1 6 13.5h2.25a2.25 2.25 0 0 1 2.25 2.25V18a2.25 2.25 0 0 1-2.25 2.25H6A2.25 2.25 0 0 1 3.75 18v-2.25ZM13.5 6a2.25 2.25 0 0 1 2.25-2.25H18A2.25 2.25 0 0 1 20.25 6v2.25A2.25 2.25 0 0 1 18 10.5h-2.25a2.25 2.25 0 0 1-2.25-2.25V6ZM13.5 15.75a2.25 2.25 0 0 1 2.25-2.25H18a2.25 2.25 0 0 1 2.25 2.25V18A2.25 2.25 0 0 1 18 20.25h-2.25A2.25 2.25 0 0 1 13.5 18v-2.25Z" />'];
}
if ($permissions->can($member, 'admin.departments.view') || $permissions->can($member, 'inpatient.placement.view')) {
if ($permissions->can($member, 'admin.departments.view')) {
$adminNav[] = ['name' => 'Care units', 'route' => route('care.care-units.index'), 'active' => request()->routeIs('care.care-units.*'),
'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" />'];
} elseif ($permissions->can($member, 'nursing.services.view') && $permissions->can($member, 'inpatient.placement.view')) {
$adminNav[] = ['name' => 'Care units', 'route' => route('care.care-units.index'), 'active' => request()->routeIs('care.care-units.*'),
'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" />'];
}