diff --git a/app/Http/Controllers/Care/AppointmentController.php b/app/Http/Controllers/Care/AppointmentController.php index 46ed493..9e1da4b 100644 --- a/app/Http/Controllers/Care/AppointmentController.php +++ b/app/Http/Controllers/Care/AppointmentController.php @@ -38,11 +38,26 @@ class AppointmentController extends Controller $practitioners = $this->activePractitioners($request, $organization->id); + $owner = $this->ownerRef($request); + $appointmentQuery = Appointment::owned($owner) + ->where('organization_id', $organization->id) + ->when($branchScope, fn ($q) => $q->where('branch_id', $branchScope)); + + $heroStats = [ + 'today' => (clone $appointmentQuery)->whereDate('scheduled_at', today())->count(), + 'scheduled' => (clone $appointmentQuery)->where('status', Appointment::STATUS_SCHEDULED)->count(), + 'waiting' => (clone $appointmentQuery)->whereIn('status', [ + Appointment::STATUS_CHECKED_IN, + Appointment::STATUS_WAITING, + ])->count(), + ]; + return view('care.appointments.index', [ 'organization' => $organization, 'appointments' => $appointments, 'practitioners' => $practitioners, 'statuses' => config('care.appointment_statuses'), + 'heroStats' => $heroStats, ]); } diff --git a/app/Http/Controllers/Care/BranchController.php b/app/Http/Controllers/Care/BranchController.php index 5ee2708..ef255ef 100644 --- a/app/Http/Controllers/Care/BranchController.php +++ b/app/Http/Controllers/Care/BranchController.php @@ -26,7 +26,13 @@ class BranchController extends Controller ->orderBy('name') ->get(); - return view('care.admin.branches.index', compact('branches', 'organization')); + $heroStats = [ + 'total' => $branches->count(), + 'active' => $branches->where('is_active', true)->count(), + 'departments' => $branches->sum('departments_count'), + ]; + + return view('care.admin.branches.index', compact('branches', 'organization', 'heroStats')); } public function create(Request $request): View diff --git a/app/Http/Controllers/Care/DepartmentController.php b/app/Http/Controllers/Care/DepartmentController.php index e15cfbc..0409961 100644 --- a/app/Http/Controllers/Care/DepartmentController.php +++ b/app/Http/Controllers/Care/DepartmentController.php @@ -27,10 +27,17 @@ class DepartmentController extends Controller ->orderBy('name') ->get(); + $heroStats = [ + 'total' => $departments->count(), + 'active' => $departments->where('is_active', true)->count(), + 'branches' => $departments->pluck('branch_id')->unique()->count(), + ]; + return view('care.admin.departments.index', [ 'departments' => $departments, 'organization' => $organization, 'types' => config('care.department_types'), + 'heroStats' => $heroStats, ]); } diff --git a/app/Http/Controllers/Care/DrugController.php b/app/Http/Controllers/Care/DrugController.php index 4facb5c..1674415 100644 --- a/app/Http/Controllers/Care/DrugController.php +++ b/app/Http/Controllers/Care/DrugController.php @@ -35,6 +35,16 @@ class DrugController extends Controller $lowStock = $this->pharmacy->lowStock($this->ownerRef($request), $organization->id); $expired = $this->pharmacy->expiredBatches($this->ownerRef($request), $organization->id); + $drugQuery = Drug::owned($this->ownerRef($request)) + ->where('organization_id', $organization->id) + ->when($branchScope, fn ($q) => $q->where('branch_id', $branchScope)); + + $heroStats = [ + 'total' => (clone $drugQuery)->count(), + 'low_stock' => $lowStock->count(), + 'expired' => $expired->count(), + ]; + return view('care.pharmacy.drugs.index', [ 'organization' => $organization, 'drugs' => $drugs, @@ -42,6 +52,7 @@ class DrugController extends Controller 'expired' => $expired, 'canManage' => app(\App\Services\Care\CarePermissions::class) ->can($this->member($request), 'pharmacy.manage'), + 'heroStats' => $heroStats, ]); } diff --git a/app/Http/Controllers/Care/MemberController.php b/app/Http/Controllers/Care/MemberController.php index 066d54a..d59fecb 100644 --- a/app/Http/Controllers/Care/MemberController.php +++ b/app/Http/Controllers/Care/MemberController.php @@ -27,10 +27,19 @@ class MemberController extends Controller ->orderBy('created_at') ->get(); + $clinicalRoles = ['doctor', 'nurse', 'lab_technician', 'pharmacist']; + + $heroStats = [ + 'total' => $members->count(), + 'clinical' => $members->whereIn('role', $clinicalRoles)->count(), + 'branches' => $members->whereNotNull('branch_id')->pluck('branch_id')->unique()->count(), + ]; + return view('care.admin.members.index', [ 'members' => $members, 'organization' => $organization, 'roles' => config('care.roles'), + 'heroStats' => $heroStats, ]); } diff --git a/app/Http/Controllers/Care/PatientController.php b/app/Http/Controllers/Care/PatientController.php index e85ff40..4885ab3 100644 --- a/app/Http/Controllers/Care/PatientController.php +++ b/app/Http/Controllers/Care/PatientController.php @@ -33,7 +33,18 @@ class PatientController extends Controller $branchScope, ); - return view('care.patients.index', compact('patients', 'organization')); + $owner = $this->ownerRef($request); + $patientQuery = Patient::owned($owner) + ->where('organization_id', $organization->id) + ->when($branchScope, fn ($q) => $q->where('branch_id', $branchScope)); + + $heroStats = [ + 'total' => (clone $patientQuery)->count(), + 'new_this_month' => (clone $patientQuery)->where('created_at', '>=', now()->startOfMonth())->count(), + 'with_visits' => (clone $patientQuery)->whereHas('appointments')->count(), + ]; + + return view('care.patients.index', compact('patients', 'organization', 'heroStats')); } public function create(Request $request): View diff --git a/app/Http/Controllers/Care/QueueController.php b/app/Http/Controllers/Care/QueueController.php index 4f55bb6..957139f 100644 --- a/app/Http/Controllers/Care/QueueController.php +++ b/app/Http/Controllers/Care/QueueController.php @@ -57,6 +57,16 @@ class QueueController extends Controller $canConsult = app(\App\Services\Care\CarePermissions::class) ->can($this->member($request), 'consultations.manage'); + $heroStats = [ + 'waiting' => $queue->count(), + 'in_consultation' => $inConsultation->count(), + 'today' => Appointment::owned($this->ownerRef($request)) + ->where('organization_id', $organization->id) + ->when($branchId, fn ($q) => $q->where('branch_id', $branchId)) + ->whereDate('scheduled_at', today()) + ->count(), + ]; + return view('care.queue.index', [ 'organization' => $organization, 'branches' => $branches, @@ -71,6 +81,7 @@ class QueueController extends Controller 'inConsultation' => $inConsultation, 'canManageQueue' => $canManageQueue, 'canConsult' => $canConsult, + 'heroStats' => $heroStats, ]); } diff --git a/resources/views/care/admin/branches/index.blade.php b/resources/views/care/admin/branches/index.blade.php index d5edfb0..62439dd 100644 --- a/resources/views/care/admin/branches/index.blade.php +++ b/resources/views/care/admin/branches/index.blade.php @@ -1,22 +1,43 @@ - -
-

Branches

- @if (app(\App\Services\Care\CarePermissions::class)->can(auth()->user() ? app(\App\Services\Care\OrganizationResolver::class)->memberFor(auth()->user(), $organization) : null, 'admin.branches.manage')) - Add branch - @endif -
+@php + $canManageBranches = app(\App\Services\Care\CarePermissions::class)->can( + auth()->user() ? app(\App\Services\Care\OrganizationResolver::class)->memberFor(auth()->user(), $organization) : null, + 'admin.branches.manage' + ); +@endphp -
- @forelse ($branches as $branch) -
-
-

{{ $branch->name }}

-

{{ $branch->address ?? 'No address' }} · {{ $branch->departments_count }} department(s)

-
- Edit + +
+ + @if ($canManageBranches) + + Add branch + + @endif + + +
+
+

All branches

- @empty -

No branches yet.

- @endforelse + @forelse ($branches as $branch) +
+
+

{{ $branch->name }}

+

{{ $branch->address ?? 'No address' }} · {{ $branch->departments_count }} department(s)

+
+ Edit +
+ @empty +

No branches yet.

+ @endforelse +
diff --git a/resources/views/care/admin/departments/index.blade.php b/resources/views/care/admin/departments/index.blade.php index 97d7464..7020889 100644 --- a/resources/views/care/admin/departments/index.blade.php +++ b/resources/views/care/admin/departments/index.blade.php @@ -1,28 +1,39 @@ -
-

Departments

- Add department -
+
+ + + Add department + + -
- - - - - - @forelse ($departments as $department) - - - - - - - @empty - - @endforelse - -
NameTypeBranch
{{ $department->name }}{{ $types[$department->type] ?? $department->type }}{{ $department->branch?->name }} - Edit -
No departments yet.
+
+ + + + + + @forelse ($departments as $department) + + + + + + + @empty + + @endforelse + +
NameTypeBranch
{{ $department->name }}{{ $types[$department->type] ?? $department->type }}{{ $department->branch?->name }} + Edit +
No departments yet.
+
diff --git a/resources/views/care/admin/members/index.blade.php b/resources/views/care/admin/members/index.blade.php index 3943caf..a455f81 100644 --- a/resources/views/care/admin/members/index.blade.php +++ b/resources/views/care/admin/members/index.blade.php @@ -1,36 +1,47 @@ -
-

Team members

- Add member -
+
+ + + Add member + + -
- - - - - - @foreach ($members as $member) - @php - $display = str_contains($member->user_ref, '@') - ? $member->user_ref - : (\App\Models\User::where('public_id', $member->user_ref)->value('email') ?? $member->user_ref); - @endphp - - - - - - - @endforeach - -
MemberRoleBranch
{{ $display }}{{ $roles[$member->role] ?? $member->role }}{{ $member->branch?->name ?? 'All branches' }} - @if ($member->user_ref !== auth()->user()->public_id) -
- @csrf @method('DELETE') - -
- @endif -
+
+ + + + + + @foreach ($members as $member) + @php + $display = str_contains($member->user_ref, '@') + ? $member->user_ref + : (\App\Models\User::where('public_id', $member->user_ref)->value('email') ?? $member->user_ref); + @endphp + + + + + + + @endforeach + +
MemberRoleBranch
{{ $display }}{{ $roles[$member->role] ?? $member->role }}{{ $member->branch?->name ?? 'All branches' }} + @if ($member->user_ref !== auth()->user()->public_id) +
+ @csrf @method('DELETE') + +
+ @endif +
+
diff --git a/resources/views/care/appointments/index.blade.php b/resources/views/care/appointments/index.blade.php index 0d05190..36b100e 100644 --- a/resources/views/care/appointments/index.blade.php +++ b/resources/views/care/appointments/index.blade.php @@ -1,76 +1,89 @@ +@php + $canManageAppointments = app(\App\Services\Care\CarePermissions::class)->can( + auth()->user() ? app(\App\Services\Care\OrganizationResolver::class)->memberFor(auth()->user(), $organization) : null, + 'appointments.manage' + ); +@endphp + -
-
-

Appointments

-

Schedule and manage patient appointments

+
+ + @if ($canManageAppointments) + + Walk-in + Book appointment + + @endif + + +
+ + + + + @if (request()->hasAny(['date', 'status', 'practitioner_id'])) + Clear + @endif +
+ +
+ + + + + + + + + + + + + @forelse ($appointments as $appointment) + + + + + + + + + @empty + + + + @endforelse + +
PatientScheduledPractitionerTypeStatus
+

{{ $appointment->patient->fullName() }}

+

{{ $appointment->patient->patient_number }}

+
{{ $appointment->scheduled_at?->format('d M Y H:i') ?? '—' }}{{ $appointment->practitioner?->name ?? '—' }}{{ str_replace('_', ' ', $appointment->type) }} + + {{ $statuses[$appointment->status] ?? $appointment->status }} + + + View +
No appointments found.
- @if (app(\App\Services\Care\CarePermissions::class)->can(auth()->user() ? app(\App\Services\Care\OrganizationResolver::class)->memberFor(auth()->user(), $organization) : null, 'appointments.manage')) - - @endif + +
{{ $appointments->links() }}
- -
- - - - - @if (request()->hasAny(['date', 'status', 'practitioner_id'])) - Clear - @endif -
- -
- - - - - - - - - - - - - @forelse ($appointments as $appointment) - - - - - - - - - @empty - - - - @endforelse - -
PatientScheduledPractitionerTypeStatus
-

{{ $appointment->patient->fullName() }}

-

{{ $appointment->patient->patient_number }}

-
{{ $appointment->scheduled_at?->format('d M Y H:i') ?? '—' }}{{ $appointment->practitioner?->name ?? '—' }}{{ str_replace('_', ' ', $appointment->type) }} - - {{ $statuses[$appointment->status] ?? $appointment->status }} - - - View -
No appointments found.
-
- -
{{ $appointments->links() }}
diff --git a/resources/views/care/patients/index.blade.php b/resources/views/care/patients/index.blade.php index 02f94dd..19afd9c 100644 --- a/resources/views/care/patients/index.blade.php +++ b/resources/views/care/patients/index.blade.php @@ -1,62 +1,77 @@ +@php + $canManagePatients = app(\App\Services\Care\CarePermissions::class)->can( + auth()->user() ? app(\App\Services\Care\OrganizationResolver::class)->memberFor(auth()->user(), $organization) : null, + 'patients.manage' + ); +@endphp + -
-
-

Patients

-

Search and manage patient records

+
+ + @if ($canManagePatients) + + Register patient + + @endif + + +
+ + + + @if (request()->hasAny(['q', 'date_of_birth', 'phone', 'national_id', 'patient_number'])) + Clear + @endif +
+ +
+ + + + + + + + + + + + + @forelse ($patients as $patient) + + + + + + + + + @empty + + + + @endforelse + +
PatientPatient IDPhoneDOBBranch
+

{{ $patient->fullName() }}

+ @if ($patient->national_id) +

NID: {{ $patient->national_id }}

+ @endif +
{{ $patient->patient_number }}{{ $patient->phone ?? '—' }}{{ $patient->date_of_birth?->format('Y-m-d') ?? '—' }}{{ $patient->branch?->name ?? '—' }} + View +
No patients found.
- @if (app(\App\Services\Care\CarePermissions::class)->can(auth()->user() ? app(\App\Services\Care\OrganizationResolver::class)->memberFor(auth()->user(), $organization) : null, 'patients.manage')) - Register patient - @endif + +
{{ $patients->links() }}
- -
- - - - @if (request()->hasAny(['q', 'date_of_birth', 'phone', 'national_id', 'patient_number'])) - Clear - @endif -
- -
- - - - - - - - - - - - - @forelse ($patients as $patient) - - - - - - - - - @empty - - - - @endforelse - -
PatientPatient IDPhoneDOBBranch
-

{{ $patient->fullName() }}

- @if ($patient->national_id) -

NID: {{ $patient->national_id }}

- @endif -
{{ $patient->patient_number }}{{ $patient->phone ?? '—' }}{{ $patient->date_of_birth?->format('Y-m-d') ?? '—' }}{{ $patient->branch?->name ?? '—' }} - View -
No patients found.
-
- -
{{ $patients->links() }}
diff --git a/resources/views/care/pharmacy/drugs/index.blade.php b/resources/views/care/pharmacy/drugs/index.blade.php index 444aed1..37637e1 100644 --- a/resources/views/care/pharmacy/drugs/index.blade.php +++ b/resources/views/care/pharmacy/drugs/index.blade.php @@ -1,32 +1,52 @@ @php $money = fn ($minor) => config('care.billing.currency').' '.number_format($minor / 100, 2); @endphp + -
-

Drug inventory

- @if ($canManage)Add drug@endif +
+ + @if ($canManage) + + Add drug + + @endif + + + @if ($lowStock->isNotEmpty()) +
{{ $lowStock->count() }} drug(s) below reorder level.
+ @endif + @if ($expired->isNotEmpty()) +
{{ $expired->count() }} expired batch(es) with stock on hand.
+ @endif + +
+ +
+ +
+ + + + @forelse ($drugs as $drug) + + + + + + + @empty + + @endforelse + +
DrugStockUnit price

{{ $drug->name }}

@if($drug->generic_name)

{{ $drug->generic_name }}

@endif
{{ $drug->stockOnHand() }} {{ $drug->unit }}{{ $money($drug->unit_price_minor) }}View
No drugs in inventory.
+
+ +
{{ $drugs->links() }}
- @if ($lowStock->isNotEmpty()) -
{{ $lowStock->count() }} drug(s) below reorder level.
- @endif - @if ($expired->isNotEmpty()) -
{{ $expired->count() }} expired batch(es) with stock on hand.
- @endif -
-
- - - - @forelse ($drugs as $drug) - - - - - - - @empty - - @endforelse - -
DrugStockUnit price

{{ $drug->name }}

@if($drug->generic_name)

{{ $drug->generic_name }}

@endif
{{ $drug->stockOnHand() }} {{ $drug->unit }}{{ $money($drug->unit_price_minor) }}View
No drugs in inventory.
-
-
{{ $drugs->links() }}
diff --git a/resources/views/care/queue/index.blade.php b/resources/views/care/queue/index.blade.php index 762af99..5123ef8 100644 --- a/resources/views/care/queue/index.blade.php +++ b/resources/views/care/queue/index.blade.php @@ -1,74 +1,82 @@ -
-
-

Patient queue

-

Waiting patients and active consultations

+
+ + @if ($canManageQueue) + + Walk-in + + @endif + + +
+ + + +
+ +
+
+

Waiting ({{ $queue->count() }})

+
+ @forelse ($queue as $appointment) +
+
+

+ @if ($appointment->queue_position) + {{ $appointment->queue_position }} + @endif + {{ $appointment->patient->fullName() }} +

+

{{ $appointment->patient->patient_number }} · {{ $appointment->reason ?? '—' }}

+
+ @if ($canConsult) +
+ @csrf + +
+ @endif +
+ @empty +

No patients waiting.

+ @endforelse +
+
+ +
+

In consultation ({{ $inConsultation->count() }})

+
+ @forelse ($inConsultation as $appointment) +
+
+

{{ $appointment->patient->fullName() }}

+

{{ $appointment->practitioner?->name ?? 'Unassigned' }}

+
+ @if ($appointment->consultation) + Open + @endif +
+ @empty +

No active consultations.

+ @endforelse +
+
- @if ($canManageQueue) - Walk-in - @endif -
- -
- - - -
- -
-
-

Waiting ({{ $queue->count() }})

-
- @forelse ($queue as $appointment) -
-
-

- @if ($appointment->queue_position) - {{ $appointment->queue_position }} - @endif - {{ $appointment->patient->fullName() }} -

-

{{ $appointment->patient->patient_number }} · {{ $appointment->reason ?? '—' }}

-
- @if ($canConsult) -
- @csrf - -
- @endif -
- @empty -

No patients waiting.

- @endforelse -
-
- -
-

In consultation ({{ $inConsultation->count() }})

-
- @forelse ($inConsultation as $appointment) -
-
-

{{ $appointment->patient->fullName() }}

-

{{ $appointment->practitioner?->name ?? 'Unassigned' }}

-
- @if ($appointment->consultation) - Open - @endif -
- @empty -

No active consultations.

- @endforelse -
-
diff --git a/resources/views/components/care/page-hero.blade.php b/resources/views/components/care/page-hero.blade.php new file mode 100644 index 0000000..a8ea8f0 --- /dev/null +++ b/resources/views/components/care/page-hero.blade.php @@ -0,0 +1,39 @@ +@props([ + 'badge', + 'title', + 'description' => null, + 'stats' => [], +]) + +
merge(['class' => 'relative overflow-hidden rounded-2xl border border-slate-200 bg-white']) }}> +
+
+
+
+
+ {{ $badge }} +
+

{{ $title }}

+ @if ($description) +

{{ $description }}

+ @endif + @isset($actions) +
+ {{ $actions }} +
+ @endisset +
+ + @if (count($stats) > 0) +
+ @foreach ($stats as $stat) +
+

{{ $stat['value'] }}

+

{{ $stat['label'] }}

+
+ @endforeach +
+ @endif +
+
+