From e163d2f3491c496e15d88a993cdba7f958d6d4d7 Mon Sep 17 00:00:00 2001 From: antv Date: Mon, 6 Jul 2026 16:44:03 +0700 Subject: [PATCH] feature: main page - ranking list --- app/Services/User/UserService.php | 30 ++++- .../user/partials/ranking_list.blade.php | 2 +- tests/Feature/UserDashboardLayoutTest.php | 117 ++++++++++++++++++ 3 files changed, 142 insertions(+), 7 deletions(-) diff --git a/app/Services/User/UserService.php b/app/Services/User/UserService.php index b9eebc9..465f4af 100644 --- a/app/Services/User/UserService.php +++ b/app/Services/User/UserService.php @@ -115,8 +115,13 @@ class UserService implements UserServiceInterface ->whereColumn('msnv', 'user.msnv') ->whereBetween('date', [$startOfMonth, $endOfMonth]) ]) - ->orderByDesc('score') - ->get(); + ->get() + ->filter(fn($u) => $u->score > 0) + ->sortBy([ + ['score', 'desc'], + ['msnv', 'asc'] + ]) + ->values(); $currentRank = 0; $prevScore = null; @@ -157,8 +162,13 @@ class UserService implements UserServiceInterface ->whereColumn('msnv', 'user.msnv') ->whereBetween('date', [$startOfMonth, $endOfMonth]) ]) - ->orderByDesc('score') - ->get(); + ->get() + ->filter(fn($u) => $u->score > 0) + ->sortBy([ + ['score', 'desc'], + ['msnv', 'asc'] + ]) + ->values(); $prevScore = null; $rank = 1; @@ -181,15 +191,23 @@ class UserService implements UserServiceInterface $currentUserRankItem = $userItem; } - if ($index < 5) { + if ($index < 4) { $rankers[] = $userItem; } $rank++; } + $showCurrentUserAtBottom = false; + if ($currentUserRankItem) { + $currentUserIndex = $users->search(fn($u) => $u->msnv == $currentUser->msnv); + if ($currentUserIndex !== false && $currentUserIndex >= 4) { + $showCurrentUserAtBottom = true; + } + } + return [ 'rankers' => $rankers, - 'currentUserRankItem' => $currentUserRankItem + 'currentUserRankItem' => $showCurrentUserAtBottom ? $currentUserRankItem : null ]; } } \ No newline at end of file diff --git a/resources/views/user/partials/ranking_list.blade.php b/resources/views/user/partials/ranking_list.blade.php index dc27b1c..f1a63a7 100644 --- a/resources/views/user/partials/ranking_list.blade.php +++ b/resources/views/user/partials/ranking_list.blade.php @@ -18,7 +18,7 @@ /> @endforeach - @if($currentUserRankItem && $currentUserRankItem->rank > 5) + @if($currentUserRankItem)
assertSee('User A', false); $response->assertSee('User B', false); } + + public function test_dashboard_ranking_empty_state_when_no_data(): void + { + $user = \App\Models\User::create([ + 'msnv' => 5001, + 'name' => 'User A', + 'mail' => 'usera@runsystem.net', + 'pass' => md5('password'), + 'departments' => 1, + 'role' => \App\Models\User::ROLE_MEMBER, + 'status' => \App\Models\User::STATUS_ACTIVE, + 'card' => 10, + 'flag_send' => \App\Models\User::FLAG_SEND_ENABLED, + 'first_login' => \App\Models\User::FIRST_LOGIN_FALSE, + ]); + + $this->actingAs($user); + + $response = $this->get('/my-page'); + $response->assertOk(); + $response->assertSee('Chưa có xếp hạng cho tháng này', false); + } + + public function test_dashboard_ranking_dense_rank_and_deterministic_order(): void + { + $users = []; + for ($i = 1; $i <= 5; $i++) { + $char = chr(64 + $i); + $users[$i] = \App\Models\User::create([ + 'msnv' => 5000 + $i, + 'name' => "User {$char}", + 'mail' => "user{$char}@runsystem.net", + 'pass' => md5('password'), + 'departments' => 1, + 'role' => \App\Models\User::ROLE_MEMBER, + 'status' => \App\Models\User::STATUS_ACTIVE, + 'card' => 10, + 'flag_send' => \App\Models\User::FLAG_SEND_ENABLED, + 'first_login' => \App\Models\User::FIRST_LOGIN_FALSE, + ]); + + \App\Models\Administration::create([ + 'msnv' => 5000 + $i, + 'received' => 9, + 'sender' => 9999, + 'sent' => 0, + 'receiver' => null, + 'date' => \Carbon\Carbon::now()->format('Y-m-d') + ]); + } + + $this->actingAs($users[5]); + + $response = $this->get('/my-page'); + $response->assertOk(); + + $response->assertSee('User A', false); + $response->assertSee('User B', false); + $response->assertSee('User C', false); + $response->assertSee('User D', false); + $response->assertSee('User E', false); + } + + public function test_dashboard_ranking_inactive_users_excluded(): void + { + $activeUser = \App\Models\User::create([ + 'msnv' => 5001, + 'name' => 'Active User', + 'mail' => 'active@runsystem.net', + 'pass' => md5('password'), + 'departments' => 1, + 'role' => \App\Models\User::ROLE_MEMBER, + 'status' => \App\Models\User::STATUS_ACTIVE, + 'card' => 10, + 'flag_send' => \App\Models\User::FLAG_SEND_ENABLED, + 'first_login' => \App\Models\User::FIRST_LOGIN_FALSE, + ]); + + $inactiveUser = \App\Models\User::create([ + 'msnv' => 5002, + 'name' => 'Inactive User', + 'mail' => 'inactive@runsystem.net', + 'pass' => md5('password'), + 'departments' => 1, + 'role' => \App\Models\User::ROLE_MEMBER, + 'status' => \App\Models\User::STATUS_INACTIVE, + 'card' => 10, + 'flag_send' => \App\Models\User::FLAG_SEND_ENABLED, + 'first_login' => \App\Models\User::FIRST_LOGIN_FALSE, + ]); + + \App\Models\Administration::create([ + 'msnv' => 5001, + 'received' => 10, + 'sender' => 9999, + 'sent' => 0, + 'receiver' => null, + 'date' => \Carbon\Carbon::now()->format('Y-m-d') + ]); + + \App\Models\Administration::create([ + 'msnv' => 5002, + 'received' => 20, + 'sender' => 9999, + 'sent' => 0, + 'receiver' => null, + 'date' => \Carbon\Carbon::now()->format('Y-m-d') + ]); + + $this->actingAs($activeUser); + + $response = $this->get('/my-page'); + $response->assertOk(); + + $response->assertSee('Active User', false); + $response->assertDontSee('Inactive User', false); + } }