diff --git a/app/Http/Controllers/Care/DashboardController.php b/app/Http/Controllers/Care/DashboardController.php index 4be4078..ad8dbd5 100644 --- a/app/Http/Controllers/Care/DashboardController.php +++ b/app/Http/Controllers/Care/DashboardController.php @@ -6,6 +6,7 @@ use App\Http\Controllers\Controller; use App\Http\Controllers\Care\Concerns\ScopesToAccount; use App\Models\Branch; use App\Models\Member; +use App\Services\Care\CarePermissions; use App\Services\Care\OrganizationResolver; use App\Services\Care\ReportService; use Illuminate\Http\Request; @@ -17,6 +18,7 @@ class DashboardController extends Controller public function __construct( protected ReportService $reports, + protected CarePermissions $permissions, ) {} public function index(Request $request): View @@ -24,27 +26,57 @@ class DashboardController extends Controller $this->authorizeAbility($request, 'dashboard.view'); $organization = $this->organization($request); $owner = $this->ownerRef($request); + $member = $this->member($request); + + $canBranches = $this->permissions->can($member, 'admin.branches.view'); + $canMembers = $this->permissions->can($member, 'admin.members.view'); + $canDepartments = $this->permissions->can($member, 'admin.departments.view'); + $canBills = $this->permissions->can($member, 'bills.view'); + $canFinance = $this->permissions->can($member, 'reports.finance.view'); $branchQuery = Branch::owned($owner)->where('organization_id', $organization->id); $this->scopeToBranch($request, $branchQuery); $stats = [ - 'branches' => (clone $branchQuery)->where('is_active', true)->count(), - 'team_members' => Member::owned($owner)->where('organization_id', $organization->id)->count(), - 'departments' => $organization->branches() - ->when(app(OrganizationResolver::class)->branchScope($this->member($request)), function ($q, $branchId) { - $q->where('id', $branchId); - }) - ->withCount('departments') - ->get() - ->sum('departments_count'), + 'branches' => $canBranches ? (clone $branchQuery)->where('is_active', true)->count() : 0, + 'team_members' => $canMembers + ? Member::owned($owner)->where('organization_id', $organization->id)->count() + : 0, + 'departments' => $canDepartments + ? $organization->branches() + ->when(app(OrganizationResolver::class)->branchScope($member), function ($q, $branchId) { + $q->where('id', $branchId); + }) + ->withCount('departments') + ->get() + ->sum('departments_count') + : 0, ]; - $branches = (clone $branchQuery)->withCount('departments')->orderBy('name')->get(); + $branches = $canBranches + ? (clone $branchQuery)->withCount('departments')->orderBy('name')->get() + : collect(); - $branchScope = app(OrganizationResolver::class)->branchScope($this->member($request)); - $operational = $this->reports->dashboardStats($owner, $organization->id, $branchScope); + $branchScope = app(OrganizationResolver::class)->branchScope($member); + $operational = $this->reports->dashboardStats( + $owner, + $organization->id, + $branchScope, + includeBilling: $canBills || $canFinance, + ); - return view('care.dashboard', compact('organization', 'stats', 'branches', 'operational')); + return view('care.dashboard', [ + 'organization' => $organization, + 'member' => $member, + 'permissions' => $this->permissions, + 'stats' => $stats, + 'branches' => $branches, + 'operational' => $operational, + 'canBranches' => $canBranches, + 'canMembers' => $canMembers, + 'canDepartments' => $canDepartments, + 'canBills' => $canBills, + 'canFinance' => $canFinance, + ]); } } diff --git a/app/Services/Care/ReportService.php b/app/Services/Care/ReportService.php index 2d8fdc8..9480d47 100644 --- a/app/Services/Care/ReportService.php +++ b/app/Services/Care/ReportService.php @@ -19,8 +19,12 @@ class ReportService /** * @return array */ - public function dashboardStats(string $ownerRef, int $organizationId, ?int $branchId = null): array - { + public function dashboardStats( + string $ownerRef, + int $organizationId, + ?int $branchId = null, + bool $includeBilling = true, + ): array { $today = now()->startOfDay(); $patientsToday = Patient::owned($ownerRef) @@ -35,21 +39,25 @@ class ReportService ->whereDate('scheduled_at', $today) ->count(); - $openBills = Bill::owned($ownerRef) - ->where('organization_id', $organizationId) - ->when($branchId, fn ($q) => $q->where('branch_id', $branchId)) - ->whereIn('status', [Bill::STATUS_OPEN, Bill::STATUS_PARTIAL]) - ->count(); + $openBills = 0; + $revenueToday = 0; + if ($includeBilling) { + $openBills = Bill::owned($ownerRef) + ->where('organization_id', $organizationId) + ->when($branchId, fn ($q) => $q->where('branch_id', $branchId)) + ->whereIn('status', [Bill::STATUS_OPEN, Bill::STATUS_PARTIAL]) + ->count(); - $revenueToday = Payment::owned($ownerRef) - ->whereHas('bill', function (Builder $q) use ($organizationId, $branchId) { - $q->where('organization_id', $organizationId); - if ($branchId) { - $q->where('branch_id', $branchId); - } - }) - ->whereDate('paid_at', $today) - ->sum('amount_minor'); + $revenueToday = Payment::owned($ownerRef) + ->whereHas('bill', function (Builder $q) use ($organizationId, $branchId) { + $q->where('organization_id', $organizationId); + if ($branchId) { + $q->where('branch_id', $branchId); + } + }) + ->whereDate('paid_at', $today) + ->sum('amount_minor'); + } $pendingLab = InvestigationRequest::owned($ownerRef) ->where('organization_id', $organizationId) diff --git a/resources/views/care/dashboard.blade.php b/resources/views/care/dashboard.blade.php index 48f1c04..cd5d9b9 100644 --- a/resources/views/care/dashboard.blade.php +++ b/resources/views/care/dashboard.blade.php @@ -1,60 +1,79 @@ @php $currency = config('care.billing.currency'); - $operationalCards = [ - [ + $operationalCards = []; + + if ($permissions->can($member, 'patients.view')) { + $operationalCards[] = [ 'label' => 'Patients today', 'value' => number_format($operational['patients_today']), 'href' => route('care.patients.index'), 'icon' => '', - ], - [ + ]; + } + + if ($permissions->can($member, 'appointments.view')) { + $operationalCards[] = [ 'label' => 'Appointments today', 'value' => number_format($operational['appointments_today']), 'href' => route('care.appointments.index'), 'icon' => '', - ], - [ + ]; + } + + if ($canBills) { + $operationalCards[] = [ 'label' => 'Open bills', 'value' => number_format($operational['open_bills']), 'href' => route('care.bills.index'), 'icon' => '', - ], - [ + ]; + } + + if ($canFinance) { + $operationalCards[] = [ 'label' => 'Revenue today', 'value' => $currency.' '.number_format($operational['revenue_today_minor'] / 100, 2), 'href' => route('care.bills.index'), 'icon' => '', 'valueClass' => 'text-emerald-700', - ], - [ + ]; + } + + if ($permissions->can($member, 'lab.view')) { + $operationalCards[] = [ 'label' => 'Pending lab', 'value' => number_format($operational['pending_lab']), 'href' => route('care.lab.queue.index'), 'icon' => '', - ], - ]; + ]; + } - $organizationCards = [ - [ + $organizationCards = []; + if ($canBranches) { + $organizationCards[] = [ 'label' => 'Active branches', 'value' => number_format($stats['branches']), 'href' => route('care.branches.index'), 'icon' => '', - ], - [ + ]; + } + if ($canMembers) { + $organizationCards[] = [ 'label' => 'Team members', 'value' => number_format($stats['team_members']), 'href' => route('care.members.index'), 'icon' => '', - ], - [ + ]; + } + if ($canDepartments) { + $organizationCards[] = [ 'label' => 'Departments', 'value' => number_format($stats['departments']), 'href' => route('care.departments.index'), 'icon' => '', - ], - ]; + ]; + } @endphp
@@ -63,73 +82,99 @@
- @include('partials.mobile-header-btn', [ - 'href' => route('care.patients.create'), - 'label' => 'Register patient', - 'desktopLabel' => 'Register patient', - 'variant' => 'outline', - 'showDesktopIcon' => false, - ]) - @include('partials.mobile-header-btn', [ - 'href' => route('care.appointments.create'), - 'label' => 'Book appointment', - 'desktopLabel' => 'Book appointment', - 'variant' => 'primary', - ]) + @if ($permissions->can($member, 'patients.manage')) + @include('partials.mobile-header-btn', [ + 'href' => route('care.patients.create'), + 'label' => 'Register patient', + 'desktopLabel' => 'Register patient', + 'variant' => 'outline', + 'showDesktopIcon' => false, + ]) + @endif + @if ($permissions->can($member, 'appointments.manage')) + @include('partials.mobile-header-btn', [ + 'href' => route('care.appointments.create'), + 'label' => 'Book appointment', + 'desktopLabel' => 'Book appointment', + 'variant' => 'primary', + ]) + @endif
@include('partials.upgrade-banner') -
- @foreach ($operationalCards as $card) - -
- - -
- @foreach ($organizationCards as $card) - -
-
- {!! $card['icon'] !!} -
-

{{ $card['label'] }}

-
-

{{ $card['value'] }}

-
- @endforeach -
- -
-
-

Branches

- @include('partials.mobile-icon-link', [ - 'href' => route('care.branches.index'), - 'label' => 'Manage branches', - 'icon' => 'arrow', - ]) +

{{ $card['value'] }}

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

{{ $branch->name }}

-

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

-
- @unless ($branch->is_active) - Inactive - @endunless + @endif + + @if (count($organizationCards) > 0) + @php + $orgCols = match (count($organizationCards)) { + 1 => 'lg:grid-cols-1', + 2 => 'lg:grid-cols-2', + default => 'lg:grid-cols-3', + }; + @endphp +
+ @foreach ($organizationCards as $card) + +
+
+ {!! $card['icon'] !!} +
+

{{ $card['label'] }}

+
+

{{ $card['value'] }}

+
+ @endforeach +
+ @endif + + @if ($canBranches) +
+
+

Branches

+ @include('partials.mobile-icon-link', [ + 'href' => route('care.branches.index'), + 'label' => 'Manage branches', + 'icon' => 'arrow', + ])
- @empty -

No branches configured yet.

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

{{ $branch->name }}

+

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

+
+ @unless ($branch->is_active) + Inactive + @endunless +
+ @empty +

No branches configured yet.

+ @endforelse +
+ @endif diff --git a/tests/Feature/CareWebTest.php b/tests/Feature/CareWebTest.php index 0f42050..c03c28d 100644 --- a/tests/Feature/CareWebTest.php +++ b/tests/Feature/CareWebTest.php @@ -81,7 +81,24 @@ class CareWebTest extends TestCase $this->actingAs($this->user) ->get(route('care.dashboard')) ->assertOk() - ->assertSee('Test Clinic'); + ->assertSee('Test Clinic') + ->assertSee('Revenue today') + ->assertSee('Open bills'); + } + + public function test_doctor_dashboard_hides_finance_metrics(): void + { + Member::where('user_ref', $this->user->public_id)->update(['role' => 'doctor']); + + $this->actingAs($this->user) + ->get(route('care.dashboard')) + ->assertOk() + ->assertSee('Patients today') + ->assertSee('Appointments today') + ->assertDontSee('Revenue today') + ->assertDontSee('Open bills') + ->assertDontSee('Team members') + ->assertDontSee('Active branches'); } public function test_onboarding_creates_organization(): void