Commit e11ab21d authored by Mahmoud Aglan's avatar Mahmoud Aglan

Fix dashboard security: redirect roles + hide financial data

- Trainers/head trainers redirect to /trainer dashboard on login
- Receptionists redirect to /receptionist dashboard
- Guardians (parents) redirect to /guardian dashboard
- Financial data (revenue, invoices, payments) only queried if user
  has 'invoices.list' permission — returns null/empty otherwise
- Blade sections wrapped in @can('invoices.list') so even direct
  URL access shows no sensitive financial info to unauthorized roles
Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
parent 38768a29
...@@ -25,6 +25,25 @@ class Dashboard extends Component ...@@ -25,6 +25,25 @@ class Dashboard extends Component
{ {
use UsesBranchScope; use UsesBranchScope;
public function mount(): void
{
$user = auth()->user();
$roleSlug = $user->primaryRole?->slug;
// Redirect non-management roles to their dedicated dashboards
$redirectMap = [
'trainer' => 'trainer.dashboard',
'head_trainer' => 'trainer.dashboard',
'receptionist' => 'receptionist.dashboard',
'parent' => 'guardian.dashboard',
];
if (isset($redirectMap[$roleSlug])) {
$this->redirect(route($redirectMap[$roleSlug]));
return;
}
}
public function render() public function render()
{ {
$today = now()->toDateString(); $today = now()->toDateString();
...@@ -42,7 +61,9 @@ public function render() ...@@ -42,7 +61,9 @@ public function render()
->when($branchId, fn ($q) => $q->whereHas('group', fn ($g) => $g->where('branch_id', $branchId)))->count(), ->when($branchId, fn ($q) => $q->whereHas('group', fn ($g) => $g->where('branch_id', $branchId)))->count(),
]; ];
$financial = [ $canViewFinancials = auth()->user()->hasPermission('invoices.list');
$financial = $canViewFinancials ? [
'revenue_this_month' => Payment::where('status', 'confirmed') 'revenue_this_month' => Payment::where('status', 'confirmed')
->whereDate('created_at', '>=', $thisMonth) ->whereDate('created_at', '>=', $thisMonth)
->when($branchId, fn ($q) => $q->where('branch_id', $branchId))->sum('amount'), ->when($branchId, fn ($q) => $q->where('branch_id', $branchId))->sum('amount'),
...@@ -53,7 +74,7 @@ public function render() ...@@ -53,7 +74,7 @@ public function render()
->when($branchId, fn ($q) => $q->where('branch_id', $branchId))->sum('total_amount'), ->when($branchId, fn ($q) => $q->where('branch_id', $branchId))->sum('total_amount'),
'pos_today_count' => POSTransaction::whereDate('processed_at', $today) 'pos_today_count' => POSTransaction::whereDate('processed_at', $today)
->when($branchId, fn ($q) => $q->where('branch_id', $branchId))->count(), ->when($branchId, fn ($q) => $q->where('branch_id', $branchId))->count(),
]; ] : null;
$todaySessions = TrainingSession::where('session_date', $today) $todaySessions = TrainingSession::where('session_date', $today)
->when($branchId, fn ($q) => $q->whereHas('group', fn ($g) => $g->where('branch_id', $branchId))) ->when($branchId, fn ($q) => $q->whereHas('group', fn ($g) => $g->where('branch_id', $branchId)))
...@@ -89,12 +110,12 @@ public function render() ...@@ -89,12 +110,12 @@ public function render()
->when($branchId, fn ($q) => $q->whereHas('group', fn ($g) => $g->where('branch_id', $branchId))) ->when($branchId, fn ($q) => $q->whereHas('group', fn ($g) => $g->where('branch_id', $branchId)))
->count(); ->count();
$overdueInvoices = Invoice::where('status', InvoiceStatus::Overdue) $overdueInvoices = $canViewFinancials ? Invoice::where('status', InvoiceStatus::Overdue)
->when($branchId, fn ($q) => $q->whereHasMorph('billable', [Participant::class], fn ($p) => $p->where('branch_id', $branchId))) ->when($branchId, fn ($q) => $q->whereHasMorph('billable', [Participant::class], fn ($p) => $p->where('branch_id', $branchId)))
->with('billable') ->with('billable')
->orderBy('due_date') ->orderBy('due_date')
->limit(5) ->limit(5)
->get(); ->get() : collect();
$todaySchedule = TrainingSession::where('session_date', $today) $todaySchedule = TrainingSession::where('session_date', $today)
->when($branchId, fn ($q) => $q->whereHas('group', fn ($g) => $g->where('branch_id', $branchId))) ->when($branchId, fn ($q) => $q->whereHas('group', fn ($g) => $g->where('branch_id', $branchId)))
...@@ -116,12 +137,12 @@ public function render() ...@@ -116,12 +137,12 @@ public function render()
->limit(5) ->limit(5)
->get(); ->get();
$recentPayments = Payment::where('status', 'confirmed') $recentPayments = $canViewFinancials ? Payment::where('status', 'confirmed')
->when($branchId, fn ($q) => $q->where('branch_id', $branchId)) ->when($branchId, fn ($q) => $q->where('branch_id', $branchId))
->with('creator') ->with('creator')
->orderByDesc('created_at') ->orderByDesc('created_at')
->limit(5) ->limit(5)
->get(); ->get() : collect();
return view('livewire.dashboard', [ return view('livewire.dashboard', [
'stats' => $stats, 'stats' => $stats,
......
...@@ -67,7 +67,8 @@ ...@@ -67,7 +67,8 @@
</div> </div>
</div> </div>
<!-- Row 2: Financial Summary --> <!-- Row 2: Financial Summary (owner/admin only) -->
@can('invoices.list')
<div class="grid grid-cols-1 sm:grid-cols-2 gap-3 sm:gap-4 mb-4 sm:mb-6"> <div class="grid grid-cols-1 sm:grid-cols-2 gap-3 sm:gap-4 mb-4 sm:mb-6">
<!-- Revenue This Month --> <!-- Revenue This Month -->
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-4 sm:p-6"> <div class="bg-white rounded-xl shadow-sm border border-gray-200 p-4 sm:p-6">
...@@ -101,6 +102,7 @@ ...@@ -101,6 +102,7 @@
<p class="text-lg sm:text-2xl font-bold {{ $financial['outstanding_invoices'] > 0 ? 'text-red-600' : 'text-gray-800' }}" dir="ltr">{{ number_format($financial['outstanding_invoices'] / 100, 2) }} {{ __('ج.م') }}</p> <p class="text-lg sm:text-2xl font-bold {{ $financial['outstanding_invoices'] > 0 ? 'text-red-600' : 'text-gray-800' }}" dir="ltr">{{ number_format($financial['outstanding_invoices'] / 100, 2) }} {{ __('ج.م') }}</p>
</div> </div>
</div> </div>
@endcan
<!-- Row 3: Attendance Summary --> <!-- Row 3: Attendance Summary -->
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-4 sm:p-6 mb-4 sm:mb-6"> <div class="bg-white rounded-xl shadow-sm border border-gray-200 p-4 sm:p-6 mb-4 sm:mb-6">
...@@ -185,6 +187,7 @@ ...@@ -185,6 +187,7 @@
</div> </div>
<!-- Overdue Invoices --> <!-- Overdue Invoices -->
@can('invoices.list')
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-4 sm:p-6"> <div class="bg-white rounded-xl shadow-sm border border-gray-200 p-4 sm:p-6">
<h3 class="text-base sm:text-lg font-semibold text-gray-800 mb-3 sm:mb-4">{{ __('فواتير متأخرة') }}</h3> <h3 class="text-base sm:text-lg font-semibold text-gray-800 mb-3 sm:mb-4">{{ __('فواتير متأخرة') }}</h3>
@if($overdueInvoices->isEmpty()) @if($overdueInvoices->isEmpty())
...@@ -203,11 +206,13 @@ ...@@ -203,11 +206,13 @@
</div> </div>
@endif @endif
</div> </div>
@endcan
</div> </div>
<!-- Row 6: Recent Payments + Upcoming Birthdays --> <!-- Row 6: Recent Payments + Upcoming Birthdays -->
<div class="grid grid-cols-1 lg:grid-cols-2 gap-3 sm:gap-4"> <div class="grid grid-cols-1 lg:grid-cols-2 gap-3 sm:gap-4">
<!-- Recent Payments --> <!-- Recent Payments -->
@can('invoices.list')
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-4 sm:p-6"> <div class="bg-white rounded-xl shadow-sm border border-gray-200 p-4 sm:p-6">
<h3 class="text-base sm:text-lg font-semibold text-gray-800 mb-3 sm:mb-4">{{ __('آخر المدفوعات') }}</h3> <h3 class="text-base sm:text-lg font-semibold text-gray-800 mb-3 sm:mb-4">{{ __('آخر المدفوعات') }}</h3>
@if($recentPayments->isEmpty()) @if($recentPayments->isEmpty())
...@@ -226,6 +231,7 @@ ...@@ -226,6 +231,7 @@
</div> </div>
@endif @endif
</div> </div>
@endcan
<!-- Upcoming Birthdays --> <!-- Upcoming Birthdays -->
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-4 sm:p-6"> <div class="bg-white rounded-xl shadow-sm border border-gray-200 p-4 sm:p-6">
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment