input('month', Carbon::now()->format('Y-m')); $user = Auth::user(); $administrations = $this->userService->getDashboard($user, $selectedMonth); $isAdmin = $user->role == config('constants.ROLE_ADMIN'); if ($isAdmin) { $stats = $this->userService->getPersonalStats($user, $selectedMonth); $receivedRanking = $this->userService->getRankingList('received', $selectedMonth, $user); $sentRanking = $this->userService->getRankingList('sent', $selectedMonth, $user); $receivedCount = (int) $stats['receivedCount']; $sentCount = (int) $stats['sentCount']; $currentRank = (int) $stats['currentRank']; $receivedRankers = $receivedRanking['rankers']; $receivedCurrentUserRankItem = $receivedRanking['currentUserRankItem']; $sentRankers = $sentRanking['rankers']; $sentCurrentUserRankItem = $sentRanking['currentUserRankItem']; } else { // For member, do NOT fetch ranking lists or calculate rank to ensure security and improve DB performance $startOfMonth = Carbon::parse($selectedMonth)->startOfMonth(); $endOfMonth = Carbon::parse($selectedMonth)->endOfMonth(); $receivedCount = (int) \App\Models\Administration::where('msnv', $user->msnv) ->whereBetween('date', [$startOfMonth, $endOfMonth]) ->sum('received'); $sentCount = (int) \App\Models\Administration::where('msnv', $user->msnv) ->whereBetween('date', [$startOfMonth, $endOfMonth]) ->sum('sent'); $currentRank = 0; $receivedRankers = []; $receivedCurrentUserRankItem = null; $sentRankers = []; $sentCurrentUserRankItem = null; } $dashboardData = new DashboardDataDto( administrations: $administrations, selectedMonth: $selectedMonth, user: $user, receivedRankers: $receivedRankers, receivedCurrentUserRankItem: $receivedCurrentUserRankItem, sentRankers: $sentRankers, sentCurrentUserRankItem: $sentCurrentUserRankItem, receivedCount: $receivedCount, sentCount: $sentCount, currentRank: $currentRank ); return view('user.dashboard', $dashboardData->toArray()); } /** * Show the form to send thank cards. */ public function sendThankcards(): View { $users = $this->userService->getOtherActiveUsers(Auth::user()); return view('user.send', compact('users')); } /** * Get statistics for a specific recipient. */ public function getRecipientStats(Request $request): JsonResponse { $receiverMsnv = (string) $request->query('receiver'); if (empty($receiverMsnv)) { return response()->json(['success' => false, 'message' => 'Receiver is required'], 400); } $sender = Auth::user(); $sentCount = $this->userService->getSentCardsToUserThisMonth($sender, $receiverMsnv); return response()->json([ 'success' => true, 'sent_count' => $sentCount, ]); } public function storeThankcards(SendThankCardRequest $request): JsonResponse { try { $this->userService->sendThankcards( Auth::user(), (string) $request->input('receiver'), (int) $request->input('amount'), (int) $request->input('template_id', 1) ); } catch (ThankCardException $e) { return response()->json(['success' => false, 'message' => $e->getMessage()], 422); } $receiverUser = \App\Models\User::where('msnv', $request->input('receiver'))->first(); // Send ChatOps notification from backend to avoid CORS issues $chatOpsUrl = config('constants.CHATOPS_API_URL'); $chatOpsToken = config('constants.CHATOPS_API_TOKEN'); if ($chatOpsUrl && $chatOpsToken && $receiverUser) { try { \Illuminate\Support\Facades\Http::timeout(5)->post($chatOpsUrl, [ 'message' => "💌 Bạn nhận được một Thank Card mới!\nNhanh chân đến khu vực nhận thư để nhận ngay nhé!", 'userEmail' => $receiverUser->mail, 'token' => $chatOpsToken, ]); } catch (\Exception $e) { \Illuminate\Support\Facades\Log::error('ChatOps API failed: ' . $e->getMessage()); } } $sender = Auth::user(); $newRecipientSentCount = $this->userService->getSentCardsToUserThisMonth($sender, (string) $request->input('receiver')); return response()->json([ 'success' => true, 'message' => __('messages.thank_card_send_success'), 'receiver_email' => $receiverUser ? $receiverUser->mail : '', 'new_balance' => (int) $sender->card, 'new_recipient_sent_count' => $newRecipientSentCount, ]); } // New page: list of ThankCards received by the current user public function receivedCards(Request $request) { $selectedMonth = $request->input('month', Carbon::now()->format('Y-m')); $user = Auth::user(); if ($request->ajax() || $request->wantsJson()) { $offset = (int) $request->input('offset', 0); $limit = 12; $data = $this->userService->getReceivedPageData($user, $selectedMonth, $limit, $offset); $html = view('user.partials.received_cards_feed', ['cards' => $data['cards']])->render(); return response()->json([ 'success' => true, 'html' => $html, 'hasMore' => $data['hasMore'], 'totalCards' => $data['totalCards'], 'totalSenders' => $data['totalSenders'] ]); } $data = $this->userService->getReceivedPageData($user, $selectedMonth, 12, 0); $data['selectedMonth'] = $selectedMonth; return view('user.received', $data); } // New page: list of ThankCards sent by the current user public function sentCards(Request $request) { $selectedMonth = $request->input('month', Carbon::now()->format('Y-m')); $user = Auth::user(); if ($request->ajax() || $request->wantsJson()) { $offset = (int) $request->input('offset', 0); $limit = 12; $data = $this->userService->getSentPageData($user, $selectedMonth, $limit, $offset); $html = view('user.partials.sent_cards_feed', ['cards' => $data['cards']])->render(); return response()->json([ 'success' => true, 'html' => $html, 'hasMore' => $data['hasMore'], 'totalCards' => $data['totalCards'], 'totalReceivers' => $data['totalReceivers'] ]); } $data = $this->userService->getSentPageData($user, $selectedMonth, 12, 0); $data['selectedMonth'] = $selectedMonth; return view('user.sent', $data); } // New page: ranking (both received and sent) with tabs public function ranking(Request $request): \Illuminate\View\View { if (Auth::user()->role !== config('constants.ROLE_ADMIN')) { abort(404); } $selectedMonth = $request->input('month', Carbon::now()->format('Y-m')); $user = Auth::user(); $receivedRanking = $this->userService->getRankingList('received', $selectedMonth, $user); $sentRanking = $this->userService->getRankingList('sent', $selectedMonth, $user); return view('user.ranking', [ 'selectedMonth' => $selectedMonth, 'receivedRankers' => $receivedRanking['rankers'], 'receivedCurrentUserRankItem' => $receivedRanking['currentUserRankItem'], 'sentRankers' => $sentRanking['rankers'], 'sentCurrentUserRankItem' => $sentRanking['currentUserRankItem'], ]); } /** * Show the change password form. */ public function changePasswordForm() { try { return view('user.change_password'); } catch (\Throwable $e) { \Illuminate\Support\Facades\Log::error('Error rendering change_password view: ' . $e->getMessage()); return response()->view('errors.500', [ 'exception' => $e, 'allowLogout' => true ], 500); } } /** * Handle user password change. */ public function updatePassword(UpdatePasswordRequest $request): RedirectResponse { $user = Auth::user(); $this->userService->updatePassword($user, $request->input('password')); $route = $user->role == config('constants.ROLE_ADMIN') ? 'admin.dashboard' : 'user.dashboard'; return redirect()->route($route)->with('success', __('messages.password_change_success')); } /** * Show the edit profile page (avatar & password). */ public function editProfile(): View { return view('user.edit'); } /** * Handle avatar upload and optional password change. */ public function updateProfile(UpdateProfileRequest $request): RedirectResponse { $user = Auth::user(); $this->userService->updateProfile( $user, $request->validated(), $request->file('avatar') ); return redirect()->route('user.edit')->with('success', 'Cập nhật hồ sơ thành công'); } }