Clarify patient vs nurse unit placement on nursing charts.
Deploy Ladill Care / deploy (push) Successful in 55s
Deploy Ladill Care / deploy (push) Successful in 55s
The empty state referred to the patient but read like the nurse was unassigned; show duty units and patient-specific copy instead. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -214,6 +214,7 @@ class PatientController extends Controller
|
||||
$nursingAssessments = collect();
|
||||
$activePlacedVisit = null;
|
||||
$latestVitals = null;
|
||||
$nurseDutyUnits = collect();
|
||||
|
||||
if ($canViewAssessments) {
|
||||
$branchScope = app(OrganizationResolver::class)->branchScope($member);
|
||||
@@ -243,10 +244,13 @@ class PatientController extends Controller
|
||||
}
|
||||
|
||||
if ($isNurseStation) {
|
||||
$nurseDutyUnits = app(\App\Services\Care\NurseStationService::class)
|
||||
->dutyUnits($organization, $member, $this->ownerRef($request));
|
||||
|
||||
$activePlacedVisit = $patient->visits()
|
||||
->whereIn('status', [Visit::STATUS_OPEN, Visit::STATUS_IN_PROGRESS])
|
||||
->whereNotNull('care_unit_id')
|
||||
->with(['careUnit', 'bed'])
|
||||
->with(['careUnit.department.branch', 'bed'])
|
||||
->orderByDesc('placed_at')
|
||||
->first();
|
||||
|
||||
@@ -269,6 +273,7 @@ class PatientController extends Controller
|
||||
'canViewPathways' => $canViewPathways,
|
||||
'activePlacedVisit' => $activePlacedVisit,
|
||||
'latestVitals' => $latestVitals,
|
||||
'nurseDutyUnits' => $nurseDutyUnits,
|
||||
'nursingAssessments' => $nursingAssessments,
|
||||
'messagingSmsReady' => $suiteSmsReady || $credential->hasValidSms(),
|
||||
'messagingEmailReady' => $suiteEmailReady || $credential->hasValidBird(),
|
||||
|
||||
@@ -118,4 +118,65 @@ class NurseStationService
|
||||
'selected_unit' => $selected,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Units the nurse is on duty for today (roster) or has an active home placement.
|
||||
*
|
||||
* @return Collection<int, CareUnit>
|
||||
*/
|
||||
public function dutyUnits(Organization $organization, Member $member, string $ownerRef): Collection
|
||||
{
|
||||
$dutyToday = RosterEntry::owned($ownerRef)
|
||||
->where('organization_id', $organization->id)
|
||||
->where('member_id', $member->id)
|
||||
->whereDate('duty_date', now()->toDateString())
|
||||
->where('status', '!=', RosterEntry::STATUS_CANCELLED)
|
||||
->orderBy('shift_id')
|
||||
->get(['care_unit_id']);
|
||||
|
||||
$placements = StaffAssignment::owned($ownerRef)
|
||||
->where('organization_id', $organization->id)
|
||||
->where('member_id', $member->id)
|
||||
->where('status', 'active')
|
||||
->unitPlacements()
|
||||
->effectiveOn(now())
|
||||
->whereNotNull('care_unit_id')
|
||||
->get(['care_unit_id']);
|
||||
|
||||
$unitIds = $dutyToday->pluck('care_unit_id')
|
||||
->merge($placements->pluck('care_unit_id'))
|
||||
->filter()
|
||||
->map(fn ($id) => (int) $id)
|
||||
->unique()
|
||||
->values();
|
||||
|
||||
if ($unitIds->isEmpty()) {
|
||||
return collect();
|
||||
}
|
||||
|
||||
$units = CareUnit::owned($ownerRef)
|
||||
->where('organization_id', $organization->id)
|
||||
->whereIn('id', $unitIds->all())
|
||||
->where('is_active', true)
|
||||
->with('department.branch')
|
||||
->get()
|
||||
->keyBy('id');
|
||||
|
||||
// Preserve roster order, then placement-only units.
|
||||
$ordered = collect();
|
||||
foreach ($dutyToday as $entry) {
|
||||
$id = (int) $entry->care_unit_id;
|
||||
if ($units->has($id) && ! $ordered->has($id)) {
|
||||
$ordered->put($id, $units->get($id));
|
||||
}
|
||||
}
|
||||
foreach ($placements as $assignment) {
|
||||
$id = (int) $assignment->care_unit_id;
|
||||
if ($units->has($id) && ! $ordered->has($id)) {
|
||||
$ordered->put($id, $units->get($id));
|
||||
}
|
||||
}
|
||||
|
||||
return $ordered->values();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,9 +107,12 @@
|
||||
@if (($isNurseStation ?? false) && ! ($canConsult ?? false))
|
||||
@if ($activePlacedVisit ?? null)
|
||||
<div class="rounded-xl border border-teal-100 bg-teal-50/60 px-3 py-3">
|
||||
<p class="text-xs font-semibold uppercase tracking-wide text-teal-800">On unit now</p>
|
||||
<p class="text-xs font-semibold uppercase tracking-wide text-teal-800">Patient on unit</p>
|
||||
<p class="mt-1 font-medium text-slate-900">
|
||||
{{ $activePlacedVisit->careUnit?->name ?? 'Care unit' }}
|
||||
@if ($activePlacedVisit->careUnit?->department?->branch)
|
||||
· {{ $activePlacedVisit->careUnit->department->branch->name }}
|
||||
@endif
|
||||
@if ($activePlacedVisit->bed)
|
||||
· Bed {{ $activePlacedVisit->bed->label }}
|
||||
@endif
|
||||
@@ -130,14 +133,34 @@
|
||||
@if ($canViewMar ?? false)
|
||||
<a href="{{ route('care.visits.mar', $activePlacedVisit) }}" class="text-sm font-medium text-teal-700 hover:text-teal-900">MAR chart</a>
|
||||
@endif
|
||||
@if ($activePlacedVisit->careUnit)
|
||||
<a href="{{ route('care.care-units.assessments', $activePlacedVisit->careUnit) }}" class="text-sm font-medium text-teal-700 hover:text-teal-900">Vitals & assess</a>
|
||||
<a href="{{ route('care.care-units.notes', $activePlacedVisit->careUnit) }}" class="text-sm font-medium text-teal-700 hover:text-teal-900">Nursing notes</a>
|
||||
@endif
|
||||
</p>
|
||||
</div>
|
||||
@else
|
||||
<div class="rounded-xl border border-dashed border-slate-200 px-3 py-3 text-slate-500">
|
||||
Not placed on a care unit right now.
|
||||
<a href="{{ route('care.nursing.station') }}" class="ml-1 font-medium text-indigo-600 hover:text-indigo-800">My care unit</a>
|
||||
<div class="rounded-xl border border-dashed border-slate-200 px-3 py-3 text-slate-600">
|
||||
<p class="font-medium text-slate-800">This patient is not on a care unit right now.</p>
|
||||
@if (($nurseDutyUnits ?? collect())->isNotEmpty())
|
||||
<p class="mt-1 text-sm text-slate-500">
|
||||
You are on duty at
|
||||
{{ $nurseDutyUnits->pluck('name')->join(', ') }} —
|
||||
this chart is about the patient’s placement, not yours.
|
||||
</p>
|
||||
<p class="mt-2 flex flex-wrap gap-3">
|
||||
@foreach ($nurseDutyUnits as $dutyUnit)
|
||||
<a href="{{ route('care.nursing.station', ['unit' => $dutyUnit->id]) }}" class="text-sm font-medium text-indigo-600 hover:text-indigo-800">
|
||||
Open {{ $dutyUnit->name }}
|
||||
</a>
|
||||
@endforeach
|
||||
</p>
|
||||
@else
|
||||
<p class="mt-1 text-sm text-slate-500">
|
||||
Ask a charge nurse to place them, or open
|
||||
<a href="{{ route('care.nursing.station') }}" class="font-medium text-indigo-600 hover:text-indigo-800">My care unit</a>.
|
||||
</p>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
|
||||
|
||||
@@ -349,12 +349,68 @@ class CareMyShiftsAndNavPermissionsTest extends TestCase
|
||||
->get(route('care.patients.show', $patient))
|
||||
->assertOk()
|
||||
->assertSee('Visit & nursing care', false)
|
||||
->assertSee('On unit now')
|
||||
->assertSee('Patient on unit')
|
||||
->assertSee('Medical Ward')
|
||||
->assertSee('Nursing assessments')
|
||||
->assertSee('My care unit')
|
||||
->assertDontSee('Visit & clinical history', false)
|
||||
->assertDontSee('Condition pathways')
|
||||
->assertDontSee('Follow-up scores')
|
||||
->assertDontSee('Start assessment');
|
||||
->assertDontSee('Start assessment')
|
||||
->assertDontSee('This patient is not on a care unit');
|
||||
}
|
||||
|
||||
public function test_nurse_patient_chart_clarifies_unplaced_patient_vs_nurse_duty(): void
|
||||
{
|
||||
$user = User::create([
|
||||
'public_id' => 'nurse-chart-unplaced',
|
||||
'name' => 'Nurse Duty',
|
||||
'email' => 'nurse-chart-unplaced@example.com',
|
||||
]);
|
||||
$member = Member::create([
|
||||
'owner_ref' => $this->owner->public_id,
|
||||
'organization_id' => $this->organization->id,
|
||||
'user_ref' => $user->public_id,
|
||||
'role' => 'nurse',
|
||||
'branch_id' => $this->branch->id,
|
||||
]);
|
||||
|
||||
$roster = app(RosterService::class);
|
||||
$day = $roster->ensureDefaultShifts($this->organization, $this->owner->public_id)->firstWhere('code', 'day');
|
||||
$roster->assign(
|
||||
$this->unit,
|
||||
$day,
|
||||
$member,
|
||||
now()->toDateString(),
|
||||
$this->owner->public_id,
|
||||
$this->owner->public_id,
|
||||
);
|
||||
|
||||
$patient = \App\Models\Patient::create([
|
||||
'uuid' => (string) \Illuminate\Support\Str::uuid(),
|
||||
'owner_ref' => $this->owner->public_id,
|
||||
'organization_id' => $this->organization->id,
|
||||
'branch_id' => $this->branch->id,
|
||||
'patient_number' => 'P-CHART-2',
|
||||
'first_name' => 'Kofi',
|
||||
'last_name' => 'Mensah',
|
||||
]);
|
||||
|
||||
\App\Models\Visit::create([
|
||||
'owner_ref' => $this->owner->public_id,
|
||||
'organization_id' => $this->organization->id,
|
||||
'branch_id' => $this->branch->id,
|
||||
'patient_id' => $patient->id,
|
||||
'status' => \App\Models\Visit::STATUS_IN_PROGRESS,
|
||||
'checked_in_at' => now(),
|
||||
]);
|
||||
|
||||
$this->actingAs($user)
|
||||
->get(route('care.patients.show', $patient))
|
||||
->assertOk()
|
||||
->assertSee('This patient is not on a care unit right now.')
|
||||
->assertSee('You are on duty at')
|
||||
->assertSee('Medical Ward')
|
||||
->assertSee('Open Medical Ward')
|
||||
->assertDontSee('Not placed on a care unit right now.');
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user