Deploy Ladill Care / deploy (push) Successful in 30s
Roster no longer writes temporary staff assignments; unit assignment UI drops shift fields and labels clarify the two workflows. Co-authored-by: Cursor <cursoragent@cursor.com>
125 lines
8.1 KiB
PHP
125 lines
8.1 KiB
PHP
<x-app-layout title="Roster · {{ $unit->name }}">
|
||
<div class="space-y-6">
|
||
<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>
|
||
<span class="text-slate-300">/</span>
|
||
Duty roster
|
||
</p>
|
||
<h1 class="mt-1 text-2xl font-semibold text-slate-900">Shift assignments</h1>
|
||
<p class="mt-1 text-sm text-slate-500">
|
||
{{ $weekStart->format('d M Y') }} – {{ $weekStart->copy()->addDays(6)->format('d M Y') }}
|
||
· {{ $unit->department?->name }}
|
||
</p>
|
||
</div>
|
||
<div class="flex flex-wrap items-center gap-2">
|
||
<a href="{{ route('care.care-units.roster', ['careUnit' => $unit, 'week' => $prevWeek]) }}" class="btn-secondary">Previous</a>
|
||
<a href="{{ route('care.care-units.roster', ['careUnit' => $unit, 'week' => now()->startOfWeek(\Carbon\Carbon::MONDAY)->toDateString()]) }}" class="btn-secondary">This week</a>
|
||
<a href="{{ route('care.care-units.roster', ['careUnit' => $unit, 'week' => $nextWeek]) }}" class="btn-secondary">Next</a>
|
||
@if ($canManage)
|
||
<a href="{{ route('care.shifts.index') }}" class="btn-secondary">Shift templates</a>
|
||
@endif
|
||
</div>
|
||
</div>
|
||
|
||
<div class="overflow-x-auto 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="sticky left-0 z-10 bg-slate-50 px-3 py-3">Shift</th>
|
||
@foreach ($days as $day)
|
||
<th class="min-w-[9rem] px-3 py-3 {{ $day->isToday() ? 'bg-indigo-50 text-indigo-700' : '' }}">
|
||
<div>{{ $day->format('D') }}</div>
|
||
<div class="font-normal normal-case text-slate-400">{{ $day->format('d M') }}</div>
|
||
</th>
|
||
@endforeach
|
||
</tr>
|
||
</thead>
|
||
<tbody class="divide-y divide-slate-100">
|
||
@foreach ($shifts as $shift)
|
||
<tr>
|
||
<td class="sticky left-0 z-10 bg-white px-3 py-3 align-top">
|
||
<div class="flex items-center gap-2 font-medium text-slate-900">
|
||
<span class="inline-block h-2.5 w-2.5 rounded-full" style="background: {{ $shift->color ?? '#64748b' }}"></span>
|
||
{{ $shift->label }}
|
||
</div>
|
||
<div class="mt-0.5 text-xs text-slate-400">{{ $shift->timeLabel() }}</div>
|
||
</td>
|
||
@foreach ($days as $day)
|
||
@php
|
||
$key = $day->toDateString().'|'.$shift->id;
|
||
$entries = $cells[$key] ?? [];
|
||
@endphp
|
||
<td class="px-2 py-2 align-top {{ $day->isToday() ? 'bg-indigo-50/40' : '' }}">
|
||
<div class="space-y-1.5">
|
||
@forelse ($entries as $entry)
|
||
<div class="rounded-lg border border-slate-100 bg-slate-50 px-2 py-1.5">
|
||
<div class="text-xs font-medium text-slate-800">
|
||
{{ $memberLabels[$entry->member_id] ?? ($entry->member?->user_ref ?? 'Staff') }}
|
||
</div>
|
||
@if ($canManage)
|
||
<form method="POST" action="{{ route('care.care-units.roster.destroy', [$unit, $entry]) }}" class="mt-1" onsubmit="return confirm('Remove from roster?')">
|
||
@csrf
|
||
@method('DELETE')
|
||
<button type="submit" class="text-[11px] text-red-600 hover:text-red-800">Remove</button>
|
||
</form>
|
||
@endif
|
||
</div>
|
||
@empty
|
||
<p class="px-1 text-[11px] text-slate-300">—</p>
|
||
@endforelse
|
||
</div>
|
||
</td>
|
||
@endforeach
|
||
</tr>
|
||
@endforeach
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
@if ($canManage)
|
||
<div class="rounded-2xl border border-slate-200 bg-white p-6">
|
||
<h2 class="text-base font-semibold text-slate-900">Add shift assignment</h2>
|
||
<p class="mt-1 text-sm text-slate-500">Schedules who works this unit on a specific date and shift. Does not change their unit placement.</p>
|
||
<form method="POST" action="{{ route('care.care-units.roster.store', $unit) }}" class="mt-4 grid gap-3 sm:grid-cols-2 lg:grid-cols-5">
|
||
@csrf
|
||
<div>
|
||
<label class="block text-xs font-medium uppercase text-slate-500">Date</label>
|
||
<input type="date" name="duty_date" value="{{ old('duty_date', now()->toDateString()) }}" required class="mt-1 w-full rounded-lg border-slate-300 text-sm">
|
||
@error('duty_date')<p class="mt-1 text-xs text-red-600">{{ $message }}</p>@enderror
|
||
</div>
|
||
<div>
|
||
<label class="block text-xs font-medium uppercase text-slate-500">Shift</label>
|
||
<select name="shift_id" required class="mt-1 w-full rounded-lg border-slate-300 text-sm">
|
||
@foreach ($shifts as $shift)
|
||
<option value="{{ $shift->id }}" @selected(old('shift_id') == $shift->id)>{{ $shift->label }} ({{ $shift->timeLabel() }})</option>
|
||
@endforeach
|
||
</select>
|
||
@error('shift_id')<p class="mt-1 text-xs text-red-600">{{ $message }}</p>@enderror
|
||
</div>
|
||
<div class="lg:col-span-2">
|
||
<label class="block text-xs font-medium uppercase text-slate-500">Staff</label>
|
||
<select name="member_id" required class="mt-1 w-full rounded-lg border-slate-300 text-sm">
|
||
<option value="">Select…</option>
|
||
@foreach ($members as $member)
|
||
<option value="{{ $member->id }}" @selected(old('member_id') == $member->id)>
|
||
{{ $memberLabels[$member->id] ?? $member->user_ref }}
|
||
</option>
|
||
@endforeach
|
||
</select>
|
||
@error('member_id')<p class="mt-1 text-xs text-red-600">{{ $message }}</p>@enderror
|
||
</div>
|
||
<div class="flex items-end">
|
||
<button type="submit" class="btn-primary w-full">Assign</button>
|
||
</div>
|
||
<div class="sm:col-span-2 lg:col-span-5">
|
||
<label class="block text-xs font-medium uppercase text-slate-500">Notes</label>
|
||
<input type="text" name="notes" value="{{ old('notes') }}" class="mt-1 w-full rounded-lg border-slate-300 text-sm" placeholder="Optional">
|
||
</div>
|
||
</form>
|
||
</div>
|
||
@endif
|
||
</div>
|
||
</x-app-layout>
|