diff --git a/app/Http/Controllers/Care/PatientController.php b/app/Http/Controllers/Care/PatientController.php index 06463ce..cca75e6 100644 --- a/app/Http/Controllers/Care/PatientController.php +++ b/app/Http/Controllers/Care/PatientController.php @@ -193,33 +193,83 @@ class PatientController extends Controller if ($assessmentsEnabled) { $careFeatures->ensureAssessmentCatalog(); } + + $isNurseStation = $permissions->can($member, 'nursing.station.view'); + $canConsult = $permissions->can($member, 'consultations.manage'); + $canViewLab = $permissions->can($member, 'lab.view') || $permissions->can($member, 'lab.results.view'); + $canViewRx = $permissions->can($member, 'prescriptions.view') || $permissions->can($member, 'prescriptions.manage'); + $canViewBills = $permissions->can($member, 'bills.view'); + $canViewMar = $permissions->can($member, 'mar.view') || $permissions->can($member, 'mar.manage'); + $canViewAssessments = $assessmentsEnabled && $permissions->can($member, 'assessments.view'); - $canCaptureAssessment = $assessmentsEnabled && ( + // GP instruments / pathways CTAs stay consult-gated; nurses capture via unit boards. + $canCaptureAssessment = $assessmentsEnabled && $canConsult && ( $permissions->can($member, 'assessments.capture') || $permissions->can($member, 'assessments.manage') ); + $canViewPathways = $canConsult && $permissions->can($member, 'pathways.manage'); + $recentAssessments = collect(); $latestUniversalIntake = null; + $nursingAssessments = collect(); + $activePlacedVisit = null; + $latestVitals = null; + if ($canViewAssessments) { $branchScope = app(OrganizationResolver::class)->branchScope($member); - $recentAssessments = $patient->assessments() + $assessmentQuery = $patient->assessments() ->with('template') ->when($branchScope, fn ($q) => $q->where('branch_id', $branchScope)) - ->orderByDesc('created_at') - ->limit(10) - ->get(); + ->orderByDesc('created_at'); - $latestUniversalIntake = $patient->assessments() - ->with(['template', 'answers.question']) - ->when($branchScope, fn ($q) => $q->where('branch_id', $branchScope)) - ->whereHas('template', fn ($q) => $q->where('code', 'universal_intake')) - ->whereIn('status', [\App\Models\Assessment::STATUS_DRAFT, \App\Models\Assessment::STATUS_COMPLETED]) - ->orderByDesc('created_at') + if ($isNurseStation && ! $canConsult) { + $nursingAssessments = (clone $assessmentQuery) + ->whereHas('template', fn ($q) => $q->whereIn( + 'code', + \App\Services\Care\NursingAssessmentService::NURSING_PACK_CODES, + )) + ->limit(10) + ->get(); + } else { + $recentAssessments = (clone $assessmentQuery)->limit(10)->get(); + $latestUniversalIntake = $patient->assessments() + ->with(['template', 'answers.question']) + ->when($branchScope, fn ($q) => $q->where('branch_id', $branchScope)) + ->whereHas('template', fn ($q) => $q->where('code', 'universal_intake')) + ->whereIn('status', [\App\Models\Assessment::STATUS_DRAFT, \App\Models\Assessment::STATUS_COMPLETED]) + ->orderByDesc('created_at') + ->first(); + } + } + + if ($isNurseStation) { + $activePlacedVisit = $patient->visits() + ->whereIn('status', [Visit::STATUS_OPEN, Visit::STATUS_IN_PROGRESS]) + ->whereNotNull('care_unit_id') + ->with(['careUnit', 'bed']) + ->orderByDesc('placed_at') ->first(); + + if ($activePlacedVisit) { + $latestVitals = \App\Models\VisitVital::owned($this->ownerRef($request)) + ->where('visit_id', $activePlacedVisit->id) + ->orderByDesc('recorded_at') + ->first(); + } } return view('care.patients.show', array_merge($dashboard, [ 'canManage' => $canManage, + 'isNurseStation' => $isNurseStation, + 'canConsult' => $canConsult, + 'canViewLab' => $canViewLab, + 'canViewRx' => $canViewRx, + 'canViewBills' => $canViewBills, + 'canViewMar' => $canViewMar, + 'canViewPathways' => $canViewPathways, + 'activePlacedVisit' => $activePlacedVisit, + 'latestVitals' => $latestVitals, + 'nursingAssessments' => $nursingAssessments, 'messagingSmsReady' => $suiteSmsReady || $credential->hasValidSms(), 'messagingEmailReady' => $suiteEmailReady || $credential->hasValidBird(), 'suiteMessagingReady' => $suiteEmailReady || $suiteSmsReady, diff --git a/resources/views/care/patients/show.blade.php b/resources/views/care/patients/show.blade.php index 24400d5..c4eac86 100644 --- a/resources/views/care/patients/show.blade.php +++ b/resources/views/care/patients/show.blade.php @@ -84,7 +84,9 @@
-

Visit & clinical history

+

+ {{ ($isNurseStation ?? false) && ! ($canConsult ?? false) ? 'Visit & nursing care' : 'Visit & clinical history' }} +

Recent visits

@@ -101,76 +103,52 @@

No visits yet

@endforelse
-
-

Consultations

- @forelse ($consultations as $consultation) -

- - {{ $consultation->started_at?->format('d M Y') ?? '—' }} - @if ($consultation->practitioner) · {{ $consultation->practitioner->name }} @endif - - @if ($consultation->diagnoses->isNotEmpty()) - — {{ $consultation->diagnoses->first()->description }} + + @if (($isNurseStation ?? false) && ! ($canConsult ?? false)) + @if ($activePlacedVisit ?? null) +

+

On unit now

+

+ {{ $activePlacedVisit->careUnit?->name ?? 'Care unit' }} + @if ($activePlacedVisit->bed) + · Bed {{ $activePlacedVisit->bed->label }} + @endif +

+ @if ($latestVitals ?? null) +

+ Latest vitals + {{ $latestVitals->recorded_at?->format('d M H:i') }}: + BP {{ $latestVitals->bp_systolic }}/{{ $latestVitals->bp_diastolic }} + · P {{ $latestVitals->pulse }} + · SpO₂ {{ $latestVitals->spo2 }} +

+ @else +

No vitals recorded on this placement yet.

@endif -

- @empty -

No consultations yet

- @endforelse -
-
-

Laboratory

- @forelse ($laboratory as $lab) -

- - {{ $lab->investigationType->name }} - - · {{ $lab->completed_at?->format('d M Y') ?? '—' }} - @if ($lab->result?->is_abnormal) - abnormal - @endif -

- @empty -

No lab results yet

- @endforelse -
-
-

Prescriptions

- @forelse ($prescriptions as $rx) -

- - {{ $rx->created_at->format('d M Y') }} - - · {{ config('care.prescription_statuses')[$rx->status] ?? $rx->status }} - · {{ $rx->items->pluck('name')->take(2)->join(', ') }} -

- @empty -

No prescriptions yet

- @endforelse -
- @if ($canViewAssessments ?? false) +

+ Open care unit + @if ($canViewMar ?? false) + MAR chart + @endif + Vitals & assess + Nursing notes +

+
+ @else +
+ Not placed on a care unit right now. + My care unit +
+ @endif +
-

Assessments

- View all +

Nursing assessments

+ @if ($canViewAssessments ?? false) + View all + @endif
- @if ($latestUniversalIntake ?? null) - @php - $ccAnswer = $latestUniversalIntake->answers->first(fn ($a) => $a->question?->code === 'chief_complaint'); - @endphp -
-

Patient history form

-

- - {{ $assessmentStatuses[$latestUniversalIntake->status] ?? $latestUniversalIntake->status }} - - · {{ $latestUniversalIntake->created_at?->format('d M Y') ?? '—' }} -

- @if ($ccAnswer) -

{{ \Illuminate\Support\Str::limit((string) $ccAnswer->authoritativeValue(), 120) }}

- @endif -
- @endif - @forelse ($recentAssessments as $assessment) + @forelse ($nursingAssessments ?? [] as $assessment)

{{ $assessment->template->name }} @@ -179,18 +157,133 @@ · {{ $assessment->created_at?->format('d M Y') ?? '—' }}

@empty -

No assessments yet

+

No nursing assessments yet — start NEWS2, Braden, Morse, or pain from the unit board.

@endforelse - @if ($canViewAssessments ?? false) +
+ +
+

Medications

+ @forelse ($prescriptions as $rx) +

+ {{ $rx->created_at->format('d M Y') }} + · {{ config('care.prescription_statuses')[$rx->status] ?? $rx->status }} + · {{ $rx->items->pluck('name')->take(2)->join(', ') }} +

+ @empty +

No prescriptions on record

+ @endforelse +
+ +
+

Laboratory

+ @forelse ($laboratory as $lab) +

+ {{ $lab->investigationType->name }} + · {{ $lab->completed_at?->format('d M Y') ?? '—' }} + @if ($lab->result?->is_abnormal) + abnormal + @endif +

+ @empty +

No lab results yet

+ @endforelse +
+ @else +
+

Consultations

+ @forelse ($consultations as $consultation) +

+ + {{ $consultation->started_at?->format('d M Y') ?? '—' }} + @if ($consultation->practitioner) · {{ $consultation->practitioner->name }} @endif + + @if ($consultation->diagnoses->isNotEmpty()) + — {{ $consultation->diagnoses->first()->description }} + @endif +

+ @empty +

No consultations yet

+ @endforelse +
+ @if ($canViewLab ?? true) +
+

Laboratory

+ @forelse ($laboratory as $lab) +

+ + {{ $lab->investigationType->name }} + + · {{ $lab->completed_at?->format('d M Y') ?? '—' }} + @if ($lab->result?->is_abnormal) + abnormal + @endif +

+ @empty +

No lab results yet

+ @endforelse +
+ @endif + @if ($canViewRx ?? true) +
+

Prescriptions

+ @forelse ($prescriptions as $rx) +

+ + {{ $rx->created_at->format('d M Y') }} + + · {{ config('care.prescription_statuses')[$rx->status] ?? $rx->status }} + · {{ $rx->items->pluck('name')->take(2)->join(', ') }} +

+ @empty +

No prescriptions yet

+ @endforelse +
+ @endif + @if ($canViewAssessments ?? false) +
+
+

Assessments

+ View all +
+ @if ($latestUniversalIntake ?? null) + @php + $ccAnswer = $latestUniversalIntake->answers->first(fn ($a) => $a->question?->code === 'chief_complaint'); + @endphp +
+

Patient history form

+

+ + {{ $assessmentStatuses[$latestUniversalIntake->status] ?? $latestUniversalIntake->status }} + + · {{ $latestUniversalIntake->created_at?->format('d M Y') ?? '—' }} +

+ @if ($ccAnswer) +

{{ \Illuminate\Support\Str::limit((string) $ccAnswer->authoritativeValue(), 120) }}

+ @endif +
+ @endif + @forelse ($recentAssessments as $assessment) +

+ + {{ $assessment->template->name }} + + · {{ ($assessmentStatuses[$assessment->status] ?? $assessment->status) }} + · {{ $assessment->created_at?->format('d M Y') ?? '—' }} +

+ @empty +

No assessments yet

+ @endforelse

@if ($canCaptureAssessment ?? false) Start assessment @endif - Condition pathways - Follow-up scores + @if ($canViewPathways ?? false) + Condition pathways + Follow-up scores + @endif

- @endif -
+
+ @endif @endif
@@ -269,6 +362,7 @@ @endforelse + @if (($canViewBills ?? false) || ($canConsult ?? false) || ($canManage ?? false)) @php $money = fn ($minor) => config('care.billing.currency').' '.number_format($minor / 100, 2); @endphp

Billing

@@ -283,6 +377,7 @@ @endforelse
+ @endif

Documents

diff --git a/tests/Feature/CareMyShiftsAndNavPermissionsTest.php b/tests/Feature/CareMyShiftsAndNavPermissionsTest.php index 895b89a..84e5f8d 100644 --- a/tests/Feature/CareMyShiftsAndNavPermissionsTest.php +++ b/tests/Feature/CareMyShiftsAndNavPermissionsTest.php @@ -308,4 +308,53 @@ class CareMyShiftsAndNavPermissionsTest extends TestCase ->assertOk() ->assertSee('Open care unit'); } + + public function test_nurse_patient_chart_shows_nursing_care_not_gp_clinical_toolkit(): void + { + $user = User::create([ + 'public_id' => 'nurse-chart', + 'name' => 'Nurse Chart', + 'email' => 'nurse-chart@example.com', + ]); + 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, + ]); + + $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-1', + 'first_name' => 'Efua', + 'last_name' => 'Owusu', + ]); + + \App\Models\Visit::create([ + 'owner_ref' => $this->owner->public_id, + 'organization_id' => $this->organization->id, + 'branch_id' => $this->branch->id, + 'patient_id' => $patient->id, + 'care_unit_id' => $this->unit->id, + 'status' => \App\Models\Visit::STATUS_OPEN, + 'checked_in_at' => now(), + 'placed_at' => now(), + ]); + + $this->actingAs($user) + ->get(route('care.patients.show', $patient)) + ->assertOk() + ->assertSee('Visit & nursing care', false) + ->assertSee('On unit now') + ->assertSee('Nursing assessments') + ->assertSee('My care unit') + ->assertDontSee('Visit & clinical history', false) + ->assertDontSee('Condition pathways') + ->assertDontSee('Follow-up scores') + ->assertDontSee('Start assessment'); + } }