From 618dcef37f2096826ea7813d34393c94ff6b0693 Mon Sep 17 00:00:00 2001 From: antv Date: Mon, 6 Jul 2026 16:07:35 +0700 Subject: [PATCH] feature: main page - code be --- app/Http/Controllers/User/UserController.php | 14 ++- app/Models/Administration.php | 10 ++ app/Models/User.php | 10 ++ .../User/Contracts/UserServiceInterface.php | 4 + app/Services/User/UserService.php | 98 +++++++++++++++++++ resources/views/user/dashboard.blade.php | 38 +++---- .../user/partials/ranking_list.blade.php | 45 +++++++++ routes/web.php | 5 +- tests/Feature/UserDashboardLayoutTest.php | 55 +++++++++++ 9 files changed, 249 insertions(+), 30 deletions(-) create mode 100644 resources/views/user/partials/ranking_list.blade.php diff --git a/app/Http/Controllers/User/UserController.php b/app/Http/Controllers/User/UserController.php index ba8909c..b8cd1eb 100644 --- a/app/Http/Controllers/User/UserController.php +++ b/app/Http/Controllers/User/UserController.php @@ -21,8 +21,20 @@ class UserController extends Controller $user = Auth::user(); $administrations = $this->userService->getDashboard($user, $selectedMonth); + $stats = $this->userService->getPersonalStats($user, $selectedMonth); - return view('user.dashboard', compact('administrations', 'selectedMonth', 'user')); + $receivedRanking = $this->userService->getRankingList('received', $selectedMonth, $user); + $sentRanking = $this->userService->getRankingList('sent', $selectedMonth, $user); + + return view('user.dashboard', array_merge([ + 'administrations' => $administrations, + 'selectedMonth' => $selectedMonth, + 'user' => $user, + 'receivedRankers' => $receivedRanking['rankers'], + 'receivedCurrentUserRankItem' => $receivedRanking['currentUserRankItem'], + 'sentRankers' => $sentRanking['rankers'], + 'sentCurrentUserRankItem' => $sentRanking['currentUserRankItem'] + ], $stats)); } public function sendThankcards() diff --git a/app/Models/Administration.php b/app/Models/Administration.php index 5555e33..361407d 100644 --- a/app/Models/Administration.php +++ b/app/Models/Administration.php @@ -23,4 +23,14 @@ class Administration extends Model 'receiver', 'date', ]; + + public function senderUser() + { + return $this->belongsTo(User::class, 'sender', 'msnv'); + } + + public function receiverUser() + { + return $this->belongsTo(User::class, 'receiver', 'msnv'); + } } diff --git a/app/Models/User.php b/app/Models/User.php index e2259ad..15f1895 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -69,4 +69,14 @@ class User extends Authenticatable { return ''; } + + public function transactions() + { + return $this->hasMany(Administration::class, 'msnv', 'msnv'); + } + + public function cardPurchases() + { + return $this->hasMany(AddCard::class, 'buyer', 'msnv'); + } } diff --git a/app/Services/User/Contracts/UserServiceInterface.php b/app/Services/User/Contracts/UserServiceInterface.php index 6daecdf..ac57fca 100644 --- a/app/Services/User/Contracts/UserServiceInterface.php +++ b/app/Services/User/Contracts/UserServiceInterface.php @@ -14,4 +14,8 @@ interface UserServiceInterface public function sendThankcards(User $sender, string $receiverMsnv, int $amount): void; public function updatePassword(User $user, string $newPassword): void; + + public function getPersonalStats(User $user, string $selectedMonth): array; + + public function getRankingList(string $type, string $selectedMonth, User $currentUser): array; } \ No newline at end of file diff --git a/app/Services/User/UserService.php b/app/Services/User/UserService.php index 47eacd2..b9eebc9 100644 --- a/app/Services/User/UserService.php +++ b/app/Services/User/UserService.php @@ -94,4 +94,102 @@ class UserService implements UserServiceInterface $user->first_login = User::FIRST_LOGIN_FALSE; $user->save(); } + + public function getPersonalStats(User $user, string $selectedMonth): array + { + $startOfMonth = Carbon::parse($selectedMonth)->startOfMonth(); + $endOfMonth = Carbon::parse($selectedMonth)->endOfMonth(); + + $receivedCount = (int) Administration::where('msnv', $user->msnv) + ->whereBetween('date', [$startOfMonth, $endOfMonth]) + ->sum('received'); + + $sentCount = (int) Administration::where('msnv', $user->msnv) + ->whereBetween('date', [$startOfMonth, $endOfMonth]) + ->sum('sent'); + + // Calculate current rank (based on received) + $rankings = User::where('status', User::STATUS_ACTIVE) + ->addSelect([ + 'score' => Administration::selectRaw('COALESCE(SUM(received), 0)') + ->whereColumn('msnv', 'user.msnv') + ->whereBetween('date', [$startOfMonth, $endOfMonth]) + ]) + ->orderByDesc('score') + ->get(); + + $currentRank = 0; + $prevScore = null; + $rank = 1; + $denseRank = 0; + + foreach ($rankings as $item) { + $score = (int) $item->score; + if ($score !== $prevScore) { + $denseRank = $rank; + $prevScore = $score; + } + + if ($item->msnv == $user->msnv) { + $currentRank = $denseRank; + break; + } + $rank++; + } + + return [ + 'receivedCount' => $receivedCount, + 'sentCount' => $sentCount, + 'currentRank' => $currentRank + ]; + } + + public function getRankingList(string $type, string $selectedMonth, User $currentUser): array + { + $startOfMonth = Carbon::parse($selectedMonth)->startOfMonth(); + $endOfMonth = Carbon::parse($selectedMonth)->endOfMonth(); + + $column = $type === 'sent' ? 'sent' : 'received'; + + $users = User::where('status', User::STATUS_ACTIVE) + ->addSelect([ + 'score' => Administration::selectRaw('COALESCE(SUM(' . $column . '), 0)') + ->whereColumn('msnv', 'user.msnv') + ->whereBetween('date', [$startOfMonth, $endOfMonth]) + ]) + ->orderByDesc('score') + ->get(); + + $prevScore = null; + $rank = 1; + $denseRank = 0; + + $rankers = []; + $currentUserRankItem = null; + + foreach ($users as $index => $userItem) { + $score = (int) $userItem->score; + if ($score !== $prevScore) { + $denseRank = $rank; + $prevScore = $score; + } + + $userItem->rank = $denseRank; + $userItem->score = $score; + + if ($userItem->msnv == $currentUser->msnv) { + $currentUserRankItem = $userItem; + } + + if ($index < 5) { + $rankers[] = $userItem; + } + $rank++; + } + + return [ + 'rankers' => $rankers, + 'currentUserRankItem' => $currentUserRankItem + ]; + } } \ No newline at end of file diff --git a/resources/views/user/dashboard.blade.php b/resources/views/user/dashboard.blade.php index 2027692..73f1a01 100644 --- a/resources/views/user/dashboard.blade.php +++ b/resources/views/user/dashboard.blade.php @@ -19,7 +19,7 @@
@@ -29,7 +29,7 @@ @@ -40,7 +40,7 @@ @@ -119,26 +119,14 @@
-
- - - - - -
- - -
- - @@ -151,8 +139,8 @@ function switchRankingTab(tab) { const receivedTab = document.getElementById('tab-received'); const sentTab = document.getElementById('tab-sent'); - const receivedContent = document.getElementById('ranking-received'); - const sentContent = document.getElementById('ranking-sent'); + const receivedContent = document.getElementById('ranking-received-content'); + const sentContent = document.getElementById('ranking-sent-content'); const activeClass = 'flex items-center justify-center gap-2 w-[120px] py-2 rounded-[10px] transition-all bg-white shadow-[0_2px_4px_rgba(0,0,0,0.04)] text-[#3462f7] font-bold text-[14px]'; const inactiveClass = 'flex items-center justify-center gap-2 w-[120px] py-2 rounded-[10px] transition-all text-[#64748b] hover:text-gray-800 font-bold text-[14px]'; diff --git a/resources/views/user/partials/ranking_list.blade.php b/resources/views/user/partials/ranking_list.blade.php new file mode 100644 index 0000000..dc27b1c --- /dev/null +++ b/resources/views/user/partials/ranking_list.blade.php @@ -0,0 +1,45 @@ +@if(count($rankers) > 0) + @foreach($rankers as $item) + + @endforeach + + @if($currentUserRankItem && $currentUserRankItem->rank > 5) +
+ + @endif +@else +
+ 🏆 +

Chưa có xếp hạng cho tháng này

+
+@endif diff --git a/routes/web.php b/routes/web.php index 85bda36..87925da 100644 --- a/routes/web.php +++ b/routes/web.php @@ -38,10 +38,7 @@ Route::middleware('auth')->group(function () { Route::post('/logout', [AuthController::class, 'logout'])->name('logout'); Route::middleware('force_change_password')->group(function () { - - - - Route::middleware('member')->group(function () { + Route::middleware('member')->group(function () { Route::get('/my-page', [UserController::class, 'index'])->name('user.dashboard'); // Coming soon routes for new tabs diff --git a/tests/Feature/UserDashboardLayoutTest.php b/tests/Feature/UserDashboardLayoutTest.php index c7f769d..bba38b3 100644 --- a/tests/Feature/UserDashboardLayoutTest.php +++ b/tests/Feature/UserDashboardLayoutTest.php @@ -37,4 +37,59 @@ class UserDashboardLayoutTest extends TestCase $response->assertSee('lg:min-w-[340px]', false); $response->assertSee('lg:max-w-[400px]', false); } + + public function test_dashboard_renders_ranking_list_from_database(): void + { + $user1 = \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, + ]); + + $user2 = \App\Models\User::create([ + 'msnv' => 5002, + 'name' => 'User B', + 'mail' => 'userb@runsystem.net', + 'pass' => md5('password'), + 'departments' => 2, + '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' => 5002, + 'received' => 0, + 'sender' => null, + 'sent' => 5, + 'receiver' => 5001, + 'date' => \Carbon\Carbon::now()->format('Y-m-d') + ]); + + \App\Models\Administration::create([ + 'msnv' => 5001, + 'received' => 5, + 'sender' => 5002, + 'sent' => 0, + 'receiver' => null, + 'date' => \Carbon\Carbon::now()->format('Y-m-d') + ]); + + $this->actingAs($user1); + + $response = $this->get('/my-page'); + + $response->assertOk(); + $response->assertSee('User A', false); + $response->assertSee('User B', false); + } }