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:
290
tests/Feature/Dashboard/PriorityAlertsPanelTest.php
Normal file
290
tests/Feature/Dashboard/PriorityAlertsPanelTest.php
Normal file
@@ -0,0 +1,290 @@
|
||||
<?php
|
||||
|
||||
use App\Enums\DeclarationStatus;
|
||||
use App\Models\Client;
|
||||
use App\Models\Declaration;
|
||||
use App\Models\User;
|
||||
use App\Models\Workspace;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
function setupAlertWorkspace(string $role = 'owner'): array
|
||||
{
|
||||
$user = User::factory()->create();
|
||||
$workspace = Workspace::factory()->create();
|
||||
$workspace->users()->attach($user->id, ['role' => $role]);
|
||||
$client = Client::factory()->create(['workspace_id' => $workspace->id]);
|
||||
session(['current_workspace_id' => $workspace->id]);
|
||||
|
||||
return [$user, $workspace, $client];
|
||||
}
|
||||
|
||||
test('overdue declarations appear as critical alerts with correct daysOverdue', function () {
|
||||
[$user, $workspace, $client] = setupAlertWorkspace();
|
||||
|
||||
$declaration = Declaration::factory()->create([
|
||||
'workspace_id' => $workspace->id,
|
||||
'client_id' => $client->id,
|
||||
'status' => DeclarationStatus::EnCours,
|
||||
'due_date' => now()->subDays(5),
|
||||
]);
|
||||
|
||||
$this->actingAs($user);
|
||||
$response = $this->get(route('dashboard'));
|
||||
|
||||
$response->assertOk();
|
||||
$response->assertInertia(fn ($page) => $page
|
||||
->component('Dashboard')
|
||||
->has('alerts', 1)
|
||||
->where('alerts.0.severity', 'critical')
|
||||
->where('alerts.0.clientName', $client->company_name)
|
||||
->where('alerts.0.daysValue', 5)
|
||||
->where('alerts.0.daysLabel', 'jours en retard')
|
||||
->has('alerts.0.showUrl')
|
||||
);
|
||||
});
|
||||
|
||||
test('approaching deadline declarations appear as warning alerts', function () {
|
||||
[$user, $workspace, $client] = setupAlertWorkspace();
|
||||
|
||||
Declaration::factory()->create([
|
||||
'workspace_id' => $workspace->id,
|
||||
'client_id' => $client->id,
|
||||
'status' => DeclarationStatus::EnCours,
|
||||
'due_date' => now()->addDays(2),
|
||||
]);
|
||||
|
||||
$this->actingAs($user);
|
||||
$response = $this->get(route('dashboard'));
|
||||
|
||||
$response->assertOk();
|
||||
$response->assertInertia(fn ($page) => $page
|
||||
->component('Dashboard')
|
||||
->has('alerts', 1)
|
||||
->where('alerts.0.severity', 'warning')
|
||||
->where('alerts.0.daysLabel', 'jours restants')
|
||||
);
|
||||
});
|
||||
|
||||
test('en_attente_client over 3 days appears as info alert', function () {
|
||||
[$user, $workspace, $client] = setupAlertWorkspace();
|
||||
|
||||
Declaration::factory()->create([
|
||||
'workspace_id' => $workspace->id,
|
||||
'client_id' => $client->id,
|
||||
'status' => DeclarationStatus::EnAttenteClient,
|
||||
'due_date' => now()->addDays(10),
|
||||
'updated_at' => now()->subDays(5),
|
||||
]);
|
||||
|
||||
$this->actingAs($user);
|
||||
$response = $this->get(route('dashboard'));
|
||||
|
||||
$response->assertOk();
|
||||
$response->assertInertia(fn ($page) => $page
|
||||
->component('Dashboard')
|
||||
->where('alerts.0.severity', 'info')
|
||||
->where('alerts.0.daysLabel', 'jours en attente')
|
||||
);
|
||||
});
|
||||
|
||||
test('en_attente_client 3 days or less does not appear as info alert', function () {
|
||||
[$user, $workspace, $client] = setupAlertWorkspace();
|
||||
|
||||
Declaration::factory()->create([
|
||||
'workspace_id' => $workspace->id,
|
||||
'client_id' => $client->id,
|
||||
'status' => DeclarationStatus::EnAttenteClient,
|
||||
'due_date' => now()->addDays(10),
|
||||
'updated_at' => now()->subDays(2),
|
||||
]);
|
||||
|
||||
$this->actingAs($user);
|
||||
$response = $this->get(route('dashboard'));
|
||||
|
||||
$response->assertOk();
|
||||
$response->assertInertia(fn ($page) => $page
|
||||
->component('Dashboard')
|
||||
->has('alerts', 0)
|
||||
);
|
||||
});
|
||||
|
||||
test('alerts are capped at 20 items', function () {
|
||||
[$user, $workspace, $client] = setupAlertWorkspace();
|
||||
|
||||
// Create 25 overdue declarations
|
||||
for ($i = 1; $i <= 25; $i++) {
|
||||
Declaration::factory()->create([
|
||||
'workspace_id' => $workspace->id,
|
||||
'client_id' => $client->id,
|
||||
'status' => DeclarationStatus::EnCours,
|
||||
'due_date' => now()->subDays($i),
|
||||
]);
|
||||
}
|
||||
|
||||
$this->actingAs($user);
|
||||
$response = $this->get(route('dashboard'));
|
||||
|
||||
$response->assertOk();
|
||||
$response->assertInertia(fn ($page) => $page
|
||||
->component('Dashboard')
|
||||
->has('alerts', 20)
|
||||
);
|
||||
});
|
||||
|
||||
test('alerts are sorted by severity: critical first, then warning, then info', function () {
|
||||
[$user, $workspace, $client] = setupAlertWorkspace();
|
||||
|
||||
// Info alert (en_attente_client >3 days)
|
||||
Declaration::factory()->create([
|
||||
'workspace_id' => $workspace->id,
|
||||
'client_id' => $client->id,
|
||||
'status' => DeclarationStatus::EnAttenteClient,
|
||||
'due_date' => now()->addDays(10),
|
||||
'updated_at' => now()->subDays(5),
|
||||
]);
|
||||
|
||||
// Warning alert (due in 2 days)
|
||||
Declaration::factory()->create([
|
||||
'workspace_id' => $workspace->id,
|
||||
'client_id' => $client->id,
|
||||
'status' => DeclarationStatus::EnCours,
|
||||
'due_date' => now()->addDays(2),
|
||||
]);
|
||||
|
||||
// Critical alert (overdue)
|
||||
Declaration::factory()->create([
|
||||
'workspace_id' => $workspace->id,
|
||||
'client_id' => $client->id,
|
||||
'status' => DeclarationStatus::EnCours,
|
||||
'due_date' => now()->subDays(3),
|
||||
]);
|
||||
|
||||
$this->actingAs($user);
|
||||
$response = $this->get(route('dashboard'));
|
||||
|
||||
$response->assertOk();
|
||||
$response->assertInertia(fn ($page) => $page
|
||||
->component('Dashboard')
|
||||
->has('alerts', 3)
|
||||
->where('alerts.0.severity', 'critical')
|
||||
->where('alerts.1.severity', 'warning')
|
||||
->where('alerts.2.severity', 'info')
|
||||
);
|
||||
});
|
||||
|
||||
test('worker sees only alerts for assigned declarations', function () {
|
||||
[$user, $workspace, $client] = setupAlertWorkspace('worker');
|
||||
|
||||
$otherUser = User::factory()->create();
|
||||
$workspace->users()->attach($otherUser->id, ['role' => 'worker']);
|
||||
|
||||
// Overdue assigned to this worker (should appear)
|
||||
Declaration::factory()->create([
|
||||
'workspace_id' => $workspace->id,
|
||||
'client_id' => $client->id,
|
||||
'assigned_to' => $user->id,
|
||||
'status' => DeclarationStatus::EnCours,
|
||||
'due_date' => now()->subDays(2),
|
||||
]);
|
||||
|
||||
// Overdue assigned to other worker (should NOT appear)
|
||||
Declaration::factory()->create([
|
||||
'workspace_id' => $workspace->id,
|
||||
'client_id' => $client->id,
|
||||
'assigned_to' => $otherUser->id,
|
||||
'status' => DeclarationStatus::EnCours,
|
||||
'due_date' => now()->subDays(3),
|
||||
]);
|
||||
|
||||
$this->actingAs($user);
|
||||
$response = $this->get(route('dashboard'));
|
||||
|
||||
$response->assertOk();
|
||||
$response->assertInertia(fn ($page) => $page
|
||||
->component('Dashboard')
|
||||
->has('alerts', 1)
|
||||
->where('alerts.0.severity', 'critical')
|
||||
);
|
||||
});
|
||||
|
||||
test('empty alerts array when no urgent items exist', function () {
|
||||
[$user, $workspace, $client] = setupAlertWorkspace();
|
||||
|
||||
// Declaration far in the future (no alert)
|
||||
Declaration::factory()->create([
|
||||
'workspace_id' => $workspace->id,
|
||||
'client_id' => $client->id,
|
||||
'status' => DeclarationStatus::EnCours,
|
||||
'due_date' => now()->addDays(30),
|
||||
]);
|
||||
|
||||
$this->actingAs($user);
|
||||
$response = $this->get(route('dashboard'));
|
||||
|
||||
$response->assertOk();
|
||||
$response->assertInertia(fn ($page) => $page
|
||||
->component('Dashboard')
|
||||
->has('alerts', 0)
|
||||
);
|
||||
});
|
||||
|
||||
test('alerts are included in cached dashboard payload', function () {
|
||||
[$user, $workspace, $client] = setupAlertWorkspace();
|
||||
|
||||
Declaration::factory()->create([
|
||||
'workspace_id' => $workspace->id,
|
||||
'client_id' => $client->id,
|
||||
'status' => DeclarationStatus::EnCours,
|
||||
'due_date' => now()->subDays(2),
|
||||
]);
|
||||
|
||||
$this->actingAs($user);
|
||||
$this->get(route('dashboard'))->assertOk();
|
||||
|
||||
$cacheKey = "dashboard:{$workspace->id}:{$user->id}";
|
||||
expect(Cache::has($cacheKey))->toBeTrue();
|
||||
|
||||
$cached = Cache::get($cacheKey);
|
||||
expect($cached)->toBeArray()
|
||||
->and($cached)->toHaveKey('alerts')
|
||||
->and($cached['alerts'])->toBeArray()
|
||||
->and($cached['alerts'])->toHaveCount(1)
|
||||
->and($cached['alerts'][0]['severity'])->toBe('critical');
|
||||
});
|
||||
|
||||
test('excluded statuses do not generate alerts', function () {
|
||||
[$user, $workspace, $client] = setupAlertWorkspace();
|
||||
|
||||
// Termine (excluded) - overdue
|
||||
Declaration::factory()->create([
|
||||
'workspace_id' => $workspace->id,
|
||||
'client_id' => $client->id,
|
||||
'status' => DeclarationStatus::Termine,
|
||||
'due_date' => now()->subDays(5),
|
||||
]);
|
||||
|
||||
// MiseEnDemeure (excluded) - overdue
|
||||
Declaration::factory()->create([
|
||||
'workspace_id' => $workspace->id,
|
||||
'client_id' => $client->id,
|
||||
'status' => DeclarationStatus::MiseEnDemeure,
|
||||
'due_date' => now()->subDays(3),
|
||||
]);
|
||||
|
||||
// Ferme (excluded) - overdue
|
||||
Declaration::factory()->create([
|
||||
'workspace_id' => $workspace->id,
|
||||
'client_id' => $client->id,
|
||||
'status' => DeclarationStatus::Ferme,
|
||||
'due_date' => now()->subDays(1),
|
||||
]);
|
||||
|
||||
$this->actingAs($user);
|
||||
$response = $this->get(route('dashboard'));
|
||||
|
||||
$response->assertOk();
|
||||
$response->assertInertia(fn ($page) => $page
|
||||
->component('Dashboard')
|
||||
->has('alerts', 0)
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user