feat: implement Story 2.2 — Priority Alerts Panel with UI fixes
Add PriorityAlertsPanel component to the dashboard, update DashboardController with alert logic, and apply misc UI fixes across sidebar, forms, and pages. Includes epic-1 retrospective and sprint status update. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -28,10 +28,12 @@ class DashboardController extends Controller
|
||||
'stats' => null,
|
||||
'statCards' => [],
|
||||
'declarations' => [],
|
||||
'alerts' => [],
|
||||
'workspaceName' => null,
|
||||
'roleLabel' => null,
|
||||
'declarationsUrl' => null,
|
||||
'clientsUrl' => null,
|
||||
'viewAllAlertsUrl' => null,
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -69,11 +71,14 @@ class DashboardController extends Controller
|
||||
->where('status', DeclarationStatus::EnCours)
|
||||
->count();
|
||||
|
||||
$alerts = $this->buildAlerts($baseQuery);
|
||||
|
||||
return [
|
||||
'overdue' => $overdue,
|
||||
'dueThisWeek' => $dueThisWeek,
|
||||
'enAttenteClient' => $enAttenteClient,
|
||||
'enCours' => $enCours,
|
||||
'alerts' => $alerts,
|
||||
];
|
||||
});
|
||||
|
||||
@@ -132,13 +137,87 @@ class DashboardController extends Controller
|
||||
'stats' => $dashboardData,
|
||||
'statCards' => $statCards,
|
||||
'declarations' => $urgentDeclarations,
|
||||
'alerts' => $dashboardData['alerts'],
|
||||
'workspaceName' => $workspace->name,
|
||||
'roleLabel' => $roleLabel,
|
||||
'declarationsUrl' => route('declarations.index'),
|
||||
'clientsUrl' => route('clients.index'),
|
||||
'viewAllAlertsUrl' => route('declarations.index', ['filter' => 'alerts']),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build priority alerts from declarations, sorted by severity and urgency.
|
||||
*
|
||||
* @param \Closure $baseQuery A closure that returns a fresh query builder with workspace/user scoping applied
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
private function buildAlerts(\Closure $baseQuery): array
|
||||
{
|
||||
$typeLabels = $this->typeLabels();
|
||||
$today = now()->startOfDay();
|
||||
|
||||
// Critical: overdue declarations (past deadline), excluding en_attente_client (handled by info)
|
||||
$critical = $baseQuery()
|
||||
->whereNotNull('due_date')
|
||||
->where('due_date', '<', $today)
|
||||
->where('status', '!=', DeclarationStatus::EnAttenteClient)
|
||||
->with('client:id,company_name')
|
||||
->orderBy('due_date', 'asc')
|
||||
->limit(20)
|
||||
->get()
|
||||
->map(fn (Declaration $d) => [
|
||||
'id' => $d->id,
|
||||
'severity' => 'critical',
|
||||
'clientName' => $d->client?->company_name ?? 'Client supprimé',
|
||||
'declarationType' => $d->type->value,
|
||||
'typeLabel' => $typeLabels[$d->type->value] ?? $d->type->value,
|
||||
'daysValue' => (int) abs($today->diffInDays($d->due_date)),
|
||||
'daysLabel' => abs($today->diffInDays($d->due_date)) <= 1 ? 'jour en retard' : 'jours en retard',
|
||||
'showUrl' => route('declarations.show', $d),
|
||||
]);
|
||||
|
||||
// Warning: approaching deadlines (due within 3 days)
|
||||
$warning = $baseQuery()
|
||||
->whereNotNull('due_date')
|
||||
->whereBetween('due_date', [$today, now()->addDays(3)->endOfDay()])
|
||||
->with('client:id,company_name')
|
||||
->orderBy('due_date', 'asc')
|
||||
->limit(20)
|
||||
->get()
|
||||
->map(fn (Declaration $d) => [
|
||||
'id' => $d->id,
|
||||
'severity' => 'warning',
|
||||
'clientName' => $d->client?->company_name ?? 'Client supprimé',
|
||||
'declarationType' => $d->type->value,
|
||||
'typeLabel' => $typeLabels[$d->type->value] ?? $d->type->value,
|
||||
'daysValue' => (int) $today->diffInDays($d->due_date),
|
||||
'daysLabel' => $today->diffInDays($d->due_date) <= 1 ? 'jour restant' : 'jours restants',
|
||||
'showUrl' => route('declarations.show', $d),
|
||||
]);
|
||||
|
||||
// Info: waiting on client for >3 days
|
||||
$info = $baseQuery()
|
||||
->where('status', DeclarationStatus::EnAttenteClient)
|
||||
->where('updated_at', '<', now()->subDays(3))
|
||||
->with('client:id,company_name')
|
||||
->orderBy('updated_at', 'asc')
|
||||
->limit(20)
|
||||
->get()
|
||||
->map(fn (Declaration $d) => [
|
||||
'id' => $d->id,
|
||||
'severity' => 'info',
|
||||
'clientName' => $d->client?->company_name ?? 'Client supprimé',
|
||||
'declarationType' => $d->type->value,
|
||||
'typeLabel' => $typeLabels[$d->type->value] ?? $d->type->value,
|
||||
'daysValue' => (int) abs($today->diffInDays($d->updated_at)),
|
||||
'daysLabel' => abs($today->diffInDays($d->updated_at)) <= 1 ? 'jour en attente' : 'jours en attente',
|
||||
'showUrl' => route('declarations.show', $d),
|
||||
]);
|
||||
|
||||
return $critical->concat($warning)->concat($info)->take(20)->values()->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get declaration type labels.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user