feat: implement Story 2.3 — Worker-Scoped Dashboard
Scope stat cards and urgent declarations table to the authenticated worker's own assignments. Add empty state when no declarations are assigned, hide the "Assigné à" column for worker role, and expose isWorker flag through DashboardController and dashboard types. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
316
tests/Feature/Dashboard/WorkerDashboardTest.php
Normal file
316
tests/Feature/Dashboard/WorkerDashboardTest.php
Normal file
@@ -0,0 +1,316 @@
|
||||
<?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 setupWorkerWorkspace(): array
|
||||
{
|
||||
$worker = User::factory()->create();
|
||||
$workspace = Workspace::factory()->create();
|
||||
$workspace->users()->attach($worker->id, ['role' => 'worker']);
|
||||
$client = Client::factory()->create(['workspace_id' => $workspace->id]);
|
||||
session(['current_workspace_id' => $workspace->id]);
|
||||
|
||||
return [$worker, $workspace, $client];
|
||||
}
|
||||
|
||||
test('worker sees only their assigned declarations in kpi counts', function () {
|
||||
[$worker, $workspace, $client] = setupWorkerWorkspace();
|
||||
|
||||
$otherUser = User::factory()->create();
|
||||
$workspace->users()->attach($otherUser->id, ['role' => 'worker']);
|
||||
|
||||
// Assigned to this worker — overdue
|
||||
Declaration::factory()->create([
|
||||
'workspace_id' => $workspace->id,
|
||||
'client_id' => $client->id,
|
||||
'assigned_to' => $worker->id,
|
||||
'status' => DeclarationStatus::EnCours,
|
||||
'due_date' => now()->subDays(3),
|
||||
]);
|
||||
|
||||
// Assigned to this worker — due this week
|
||||
Declaration::factory()->create([
|
||||
'workspace_id' => $workspace->id,
|
||||
'client_id' => $client->id,
|
||||
'assigned_to' => $worker->id,
|
||||
'status' => DeclarationStatus::EnCours,
|
||||
'due_date' => now()->addDays(2),
|
||||
]);
|
||||
|
||||
// Assigned to this worker — en attente client
|
||||
Declaration::factory()->create([
|
||||
'workspace_id' => $workspace->id,
|
||||
'client_id' => $client->id,
|
||||
'assigned_to' => $worker->id,
|
||||
'status' => DeclarationStatus::EnAttenteClient,
|
||||
'due_date' => now()->addDays(10),
|
||||
]);
|
||||
|
||||
// Assigned to OTHER worker (should NOT count)
|
||||
Declaration::factory()->create([
|
||||
'workspace_id' => $workspace->id,
|
||||
'client_id' => $client->id,
|
||||
'assigned_to' => $otherUser->id,
|
||||
'status' => DeclarationStatus::EnCours,
|
||||
'due_date' => now()->subDays(1),
|
||||
]);
|
||||
|
||||
$this->actingAs($worker);
|
||||
$response = $this->get(route('dashboard'));
|
||||
|
||||
$response->assertOk();
|
||||
$response->assertInertia(fn ($page) => $page
|
||||
->component('Dashboard')
|
||||
->where('stats.overdue', 1)
|
||||
->where('stats.dueThisWeek', 1)
|
||||
->where('stats.enAttenteClient', 1)
|
||||
->where('stats.enCours', 2)
|
||||
);
|
||||
});
|
||||
|
||||
test('worker does not see declarations assigned to other team members in kpi counts', function () {
|
||||
[$worker, $workspace, $client] = setupWorkerWorkspace();
|
||||
|
||||
$otherWorker = User::factory()->create();
|
||||
$workspace->users()->attach($otherWorker->id, ['role' => 'worker']);
|
||||
|
||||
// Only assigned to other worker
|
||||
Declaration::factory()->create([
|
||||
'workspace_id' => $workspace->id,
|
||||
'client_id' => $client->id,
|
||||
'assigned_to' => $otherWorker->id,
|
||||
'status' => DeclarationStatus::EnCours,
|
||||
'due_date' => now()->subDays(5),
|
||||
]);
|
||||
|
||||
Declaration::factory()->create([
|
||||
'workspace_id' => $workspace->id,
|
||||
'client_id' => $client->id,
|
||||
'assigned_to' => $otherWorker->id,
|
||||
'status' => DeclarationStatus::EnAttenteClient,
|
||||
'due_date' => now()->addDays(2),
|
||||
]);
|
||||
|
||||
$this->actingAs($worker);
|
||||
$response = $this->get(route('dashboard'));
|
||||
|
||||
$response->assertOk();
|
||||
$response->assertInertia(fn ($page) => $page
|
||||
->component('Dashboard')
|
||||
->where('stats.overdue', 0)
|
||||
->where('stats.dueThisWeek', 0)
|
||||
->where('stats.enAttenteClient', 0)
|
||||
->where('stats.enCours', 0)
|
||||
);
|
||||
});
|
||||
|
||||
test('worker sees only their assigned declarations in the urgent declarations table', function () {
|
||||
[$worker, $workspace, $client] = setupWorkerWorkspace();
|
||||
|
||||
$otherWorker = User::factory()->create();
|
||||
$workspace->users()->attach($otherWorker->id, ['role' => 'worker']);
|
||||
|
||||
// Assigned to this worker
|
||||
$myDeclaration = Declaration::factory()->create([
|
||||
'workspace_id' => $workspace->id,
|
||||
'client_id' => $client->id,
|
||||
'assigned_to' => $worker->id,
|
||||
'status' => DeclarationStatus::EnCours,
|
||||
'due_date' => now()->addDays(2),
|
||||
]);
|
||||
|
||||
// Assigned to other worker
|
||||
Declaration::factory()->create([
|
||||
'workspace_id' => $workspace->id,
|
||||
'client_id' => $client->id,
|
||||
'assigned_to' => $otherWorker->id,
|
||||
'status' => DeclarationStatus::EnCours,
|
||||
'due_date' => now()->addDays(1),
|
||||
]);
|
||||
|
||||
$this->actingAs($worker);
|
||||
$response = $this->get(route('dashboard'));
|
||||
|
||||
$response->assertOk();
|
||||
$response->assertInertia(fn ($page) => $page
|
||||
->component('Dashboard')
|
||||
->has('declarations', 1)
|
||||
->where('declarations.0.id', $myDeclaration->id)
|
||||
);
|
||||
});
|
||||
|
||||
test('worker sees only their assigned declarations in priority alerts', function () {
|
||||
[$worker, $workspace, $client] = setupWorkerWorkspace();
|
||||
|
||||
$otherWorker = User::factory()->create();
|
||||
$workspace->users()->attach($otherWorker->id, ['role' => 'worker']);
|
||||
|
||||
// Overdue declaration assigned to this worker (generates critical alert)
|
||||
Declaration::factory()->create([
|
||||
'workspace_id' => $workspace->id,
|
||||
'client_id' => $client->id,
|
||||
'assigned_to' => $worker->id,
|
||||
'status' => DeclarationStatus::EnCours,
|
||||
'due_date' => now()->subDays(5),
|
||||
]);
|
||||
|
||||
// Overdue declaration assigned to other worker (should NOT appear)
|
||||
Declaration::factory()->create([
|
||||
'workspace_id' => $workspace->id,
|
||||
'client_id' => $client->id,
|
||||
'assigned_to' => $otherWorker->id,
|
||||
'status' => DeclarationStatus::EnCours,
|
||||
'due_date' => now()->subDays(3),
|
||||
]);
|
||||
|
||||
$this->actingAs($worker);
|
||||
$response = $this->get(route('dashboard'));
|
||||
|
||||
$response->assertOk();
|
||||
$response->assertInertia(fn ($page) => $page
|
||||
->component('Dashboard')
|
||||
->has('alerts', 1)
|
||||
->where('alerts.0.severity', 'critical')
|
||||
);
|
||||
});
|
||||
|
||||
test('worker dashboard returns isWorker true in inertia props', function () {
|
||||
[$worker, $workspace, $client] = setupWorkerWorkspace();
|
||||
|
||||
$this->actingAs($worker);
|
||||
$response = $this->get(route('dashboard'));
|
||||
|
||||
$response->assertOk();
|
||||
$response->assertInertia(fn ($page) => $page
|
||||
->component('Dashboard')
|
||||
->where('isWorker', true)
|
||||
);
|
||||
});
|
||||
|
||||
test('worker with no assigned declarations gets zero counts and empty arrays', function () {
|
||||
[$worker, $workspace, $client] = setupWorkerWorkspace();
|
||||
|
||||
// Declarations exist but assigned to someone else
|
||||
$otherUser = User::factory()->create();
|
||||
$workspace->users()->attach($otherUser->id, ['role' => 'worker']);
|
||||
|
||||
Declaration::factory()->create([
|
||||
'workspace_id' => $workspace->id,
|
||||
'client_id' => $client->id,
|
||||
'assigned_to' => $otherUser->id,
|
||||
'status' => DeclarationStatus::EnCours,
|
||||
'due_date' => now()->subDays(1),
|
||||
]);
|
||||
|
||||
$this->actingAs($worker);
|
||||
$response = $this->get(route('dashboard'));
|
||||
|
||||
$response->assertOk();
|
||||
$response->assertInertia(fn ($page) => $page
|
||||
->component('Dashboard')
|
||||
->where('stats.overdue', 0)
|
||||
->where('stats.dueThisWeek', 0)
|
||||
->where('stats.enAttenteClient', 0)
|
||||
->where('stats.enCours', 0)
|
||||
->where('declarations', [])
|
||||
->where('alerts', [])
|
||||
->where('isWorker', true)
|
||||
);
|
||||
});
|
||||
|
||||
test('worker stat card hrefs include assignee scoping param', function () {
|
||||
[$worker, $workspace, $client] = setupWorkerWorkspace();
|
||||
|
||||
$this->actingAs($worker);
|
||||
$response = $this->get(route('dashboard'));
|
||||
|
||||
$response->assertOk();
|
||||
$response->assertInertia(fn ($page) => $page
|
||||
->component('Dashboard')
|
||||
->has('statCards', 4)
|
||||
->where('statCards.0.href', fn ($href) => str_contains($href, 'assignee='.$worker->id))
|
||||
->where('statCards.1.href', fn ($href) => str_contains($href, 'assignee='.$worker->id))
|
||||
->where('statCards.2.href', fn ($href) => str_contains($href, 'assignee='.$worker->id))
|
||||
->where('statCards.3.href', fn ($href) => str_contains($href, 'assignee='.$worker->id))
|
||||
);
|
||||
});
|
||||
|
||||
test('owner and manager dashboard returns isWorker false', function () {
|
||||
$ownerUser = User::factory()->create();
|
||||
$managerUser = User::factory()->create();
|
||||
$workspace = Workspace::factory()->create();
|
||||
$workspace->users()->attach($ownerUser->id, ['role' => 'owner']);
|
||||
$workspace->users()->attach($managerUser->id, ['role' => 'manager']);
|
||||
session(['current_workspace_id' => $workspace->id]);
|
||||
|
||||
// Owner
|
||||
$this->actingAs($ownerUser);
|
||||
$response = $this->get(route('dashboard'));
|
||||
$response->assertOk();
|
||||
$response->assertInertia(fn ($page) => $page
|
||||
->component('Dashboard')
|
||||
->where('isWorker', false)
|
||||
);
|
||||
|
||||
// Manager
|
||||
$this->actingAs($managerUser);
|
||||
$response = $this->get(route('dashboard'));
|
||||
$response->assertOk();
|
||||
$response->assertInertia(fn ($page) => $page
|
||||
->component('Dashboard')
|
||||
->where('isWorker', false)
|
||||
);
|
||||
});
|
||||
|
||||
test('cached data is scoped per user with worker cache key including user id', function () {
|
||||
[$worker, $workspace, $client] = setupWorkerWorkspace();
|
||||
|
||||
$otherWorker = User::factory()->create();
|
||||
$workspace->users()->attach($otherWorker->id, ['role' => 'worker']);
|
||||
|
||||
// Declaration assigned to first worker
|
||||
Declaration::factory()->create([
|
||||
'workspace_id' => $workspace->id,
|
||||
'client_id' => $client->id,
|
||||
'assigned_to' => $worker->id,
|
||||
'status' => DeclarationStatus::EnCours,
|
||||
'due_date' => now()->subDays(1),
|
||||
]);
|
||||
|
||||
// Declaration assigned to second worker
|
||||
Declaration::factory()->create([
|
||||
'workspace_id' => $workspace->id,
|
||||
'client_id' => $client->id,
|
||||
'assigned_to' => $otherWorker->id,
|
||||
'status' => DeclarationStatus::EnCours,
|
||||
'due_date' => now()->subDays(2),
|
||||
]);
|
||||
|
||||
// First worker request
|
||||
$this->actingAs($worker);
|
||||
$this->get(route('dashboard'))->assertOk();
|
||||
|
||||
$workerCacheKey = "dashboard:{$workspace->id}:{$worker->id}";
|
||||
$otherCacheKey = "dashboard:{$workspace->id}:{$otherWorker->id}";
|
||||
|
||||
expect(Cache::has($workerCacheKey))->toBeTrue();
|
||||
expect(Cache::has($otherCacheKey))->toBeFalse();
|
||||
|
||||
$workerCached = Cache::get($workerCacheKey);
|
||||
expect($workerCached['overdue'])->toBe(1)
|
||||
->and($workerCached['enCours'])->toBe(1);
|
||||
|
||||
// Second worker request
|
||||
$this->actingAs($otherWorker);
|
||||
$this->get(route('dashboard'))->assertOk();
|
||||
|
||||
expect(Cache::has($otherCacheKey))->toBeTrue();
|
||||
$otherCached = Cache::get($otherCacheKey);
|
||||
expect($otherCached['overdue'])->toBe(1)
|
||||
->and($otherCached['enCours'])->toBe(1);
|
||||
});
|
||||
Reference in New Issue
Block a user