diff --git a/api/app/Http/Controllers/UserController.php b/api/app/Http/Controllers/UserController.php index 4e6a608..29eec09 100644 --- a/api/app/Http/Controllers/UserController.php +++ b/api/app/Http/Controllers/UserController.php @@ -18,23 +18,23 @@ class UserController extends Controller */ public function index(Request $request) { - $this->authorize('viewAny', User::class); + $this->authorize("viewAny", User::class); $query = User::query(); - if ($request->has('type')) { - $query->where('type', $request->type); + if ($request->has("type")) { + $query->where("type", $request->type); } - if ($request->has('blocked')) { - $query->where('blocked', $request->type); + if ($request->has("blocked")) { + $query->where("blocked", $request->type); } - if ($request->has('search')) { + if ($request->has("search")) { $query->where(function ($q) use ($request) { - $q->where('name', 'like', '%'.$request->search.'%') - ->orWhere('email', 'like', '%'.$request->search.'%') - ->orWhere('nickname', 'like', '%'.$request->search.'%'); + $q->where("name", "like", "%" . $request->search . "%") + ->orWhere("email", "like", "%" . $request->search . "%") + ->orWhere("nickname", "like", "%" . $request->search . "%"); }); } @@ -47,16 +47,16 @@ class UserController extends Controller */ public function store(Request $request) { - $this->authorize('create', User::class); + $this->authorize("create", User::class); $validated = $request->validate([ - 'name' => 'required|string|max:255', - 'email' => 'required|email|unique:users,email', - 'password' => 'required|string|min:3', - 'type' => 'required|in:A,U', + "name" => "required|string|max:255", + "email" => "required|email|unique:users,email", + "password" => "required|string|min:3", + "type" => "required|in:A,U", ]); - $validated['password'] = Hash::make($validated['password']); + $validated["password"] = Hash::make($validated["password"]); $user = User::create($validated); @@ -82,15 +82,15 @@ class UserController extends Controller $currentUser = Auth::user(); // Admins and owners get full profile, others get limited view - if ($currentUser->type === 'A' || $currentUser->id === $user->id) { + if ($currentUser->type === "A" || $currentUser->id === $user->id) { return response()->json($user); } else { return response()->json([ - 'id' => $user->id, - 'name' => $user->name, - 'nickname' => $user->nickname, - 'photo_avatar_filename' => $user->photo_avatar_filename, - 'type' => $user->type, + "id" => $user->id, + "name" => $user->name, + "nickname" => $user->nickname, + "photo_avatar_filename" => $user->photo_avatar_filename, + "type" => $user->type, ]); } } @@ -101,22 +101,22 @@ class UserController extends Controller */ public function update(Request $request, User $user) { - $this->authorize('update', $user); - + $this->authorize("update", $user); + $validated = $request->validate([ - 'name' => 'sometimes|string|max:255', - 'nickname' => [ - 'sometimes', - 'string', - 'max:20', - Rule::unique('users')->ignore($user->id), + "name" => "sometimes|string|max:255", + "nickname" => [ + "sometimes", + "string", + "max:20", + Rule::unique("users")->ignore($user->id), ], - 'email' => [ - 'sometimes', - 'email', - Rule::unique('users')->ignore($user->id), + "email" => [ + "sometimes", + "email", + Rule::unique("users")->ignore($user->id), ], - 'password' => 'sometimes|string|min:3', + "password" => "sometimes|string|min:3", ]); // Lógica de Upload de Foto (Exemplo Básico) @@ -130,26 +130,28 @@ class UserController extends Controller return response()->json($user); } - public function updatePassword(Request $request, User $user) { - $this->authorize('update', $user); + $this->authorize("update", $user); $validated = $request->validate([ - 'current_password' => 'required|string', - 'new_password' => 'required|string|min:3|confirmed', + "current_password" => "required|string", + "new_password" => "required|string|min:3|confirmed", ]); // Verificar se a password atual está correta - if (!Hash::check($validated['current_password'], $user->password)) { - return response()->json(['message' => 'Current password is incorrect'], 403); + if (!Hash::check($validated["current_password"], $user->password)) { + return response()->json( + ["message" => "Current password is incorrect"], + 403, + ); } // Atualizar para a nova password - $user->password = Hash::make($validated['new_password']); + $user->password = Hash::make($validated["new_password"]); $user->save(); - return response()->json(['message' => 'Password updated successfully']); + return response()->json(["message" => "Password updated successfully"]); } /** @@ -158,24 +160,27 @@ class UserController extends Controller */ public function destroy(User $user) { - $this->authorize('delete', $user); + $this->authorize("delete", $user); // Check if user has transactions or games - if so, soft delete $hasTransactions = $user->transactions()->exists(); $hasGames = Game::where(function ($q) use ($user) { - $q->where('player1_user_id', $user->id) - ->orWhere('player2_user_id', $user->id); + $q->where("player1_user_id", $user->id)->orWhere( + "player2_user_id", + $user->id, + ); })->exists(); if ($hasTransactions || $hasGames) { $user->delete(); // Soft delete - $message = 'User account deactivated (soft-deleted due to transaction/game history)'; + $message = + "User account deactivated (soft-deleted due to transaction/game history)"; } else { $user->forceDelete(); // Hard delete - $message = 'User permanently deleted'; + $message = "User permanently deleted"; } - return response()->json(['message' => $message]); + return response()->json(["message" => $message]); } /** @@ -184,37 +189,61 @@ class UserController extends Controller */ public function getGames(Request $request, User $user) { - $this->authorize('view', $user); + $this->authorize("view", $user); // Define a query base para GAMES $query = \App\Models\Game::query() ->where(function ($q) use ($user) { - $q->where('player1_user_id', $user->id) - ->orWhere('player2_user_id', $user->id); + $q->where("player1_user_id", $user->id)->orWhere( + "player2_user_id", + $user->id, + ); }) - ->with(['winner', 'player1', 'player2']); + ->with(["winner", "player1", "player2"]); - if ($request->has('type')) { - $query->where('type', $request->type); + if ($request->has("type")) { + $query->where("type", $request->type); } - if ($request->has('status')) { - $query->where('status', $request->status); + if ($request->has("status")) { + $query->where("status", $request->status); } - $sortDirection = $request->get('sort_direction', 'desc'); - $query->orderBy('began_at', $sortDirection); + // Date filtering + if ($request->has("date_from")) { + $query->whereDate("began_at", ">=", $request->date_from); + } + + if ($request->has("date_to")) { + $query->whereDate("began_at", "<=", $request->date_to); + } + + // Flexible sorting + $sortColumn = $request->get("sort_by", "began_at"); + $sortDirection = $request->get("sort_direction", "desc"); + + // Whitelist sortable columns for security + $allowedColumns = [ + "began_at", + "ended_at", + "total_time", + "total_points", + ]; + if (in_array($sortColumn, $allowedColumns)) { + $query->orderBy($sortColumn, $sortDirection); + } else { + $query->orderBy("began_at", $sortDirection); + } - $games = $query->paginate(10); $games->getCollection()->transform(function ($game) use ($user) { if ($game->winner_user_id == $user->id) { - $game->outcome = 'win'; + $game->outcome = "win"; } elseif ($game->is_draw) { - $game->outcome = 'draw'; + $game->outcome = "draw"; } else { - $game->outcome = 'loss'; + $game->outcome = "loss"; } return $game; }); @@ -228,35 +257,58 @@ class UserController extends Controller */ public function getMatches(Request $request, User $user) { - $this->authorize('view', $user); + $this->authorize("view", $user); - $query = MatchGame::query() + $query = MatchGame::query() ->where(function ($q) use ($user) { - $q->where('player1_user_id', $user->id) - ->orWhere('player2_user_id', $user->id); + $q->where("player1_user_id", $user->id)->orWhere( + "player2_user_id", + $user->id, + ); }) - ->with(['winner', 'player1', 'player2']); + ->with(["winner", "player1", "player2"]); - if ($request->has('type')) { - $query->where('type', $request->type); - } - - if ($request->has('status')) { - $query->where('status', $request->status); + if ($request->has("type")) { + $query->where("type", $request->type); } - $sortDirection = $request->get('sort_direction', 'desc'); - $query->orderBy('began_at', $sortDirection); + if ($request->has("status")) { + $query->where("status", $request->status); + } + + // Date filtering + if ($request->has("date_from")) { + $query->whereDate("began_at", ">=", $request->date_from); + } + + if ($request->has("date_to")) { + $query->whereDate("began_at", "<=", $request->date_to); + } + + // Flexible sorting + $sortColumn = $request->get("sort_by", "began_at"); + $sortDirection = $request->get("sort_direction", "desc"); + + // Whitelist sortable columns for security + $allowedColumns = ["began_at", "ended_at", "stake", "total_time"]; + if (in_array($sortColumn, $allowedColumns)) { + $query->orderBy($sortColumn, $sortDirection); + } else { + $query->orderBy("began_at", $sortDirection); + } $matches = $query->paginate(10); $matches->getCollection()->transform(function ($match) use ($user) { if ($match->winner_user_id == $user->id) { - $match->outcome = 'win'; - } elseif ($match->winner_user_id && $match->winner_user_id != $user->id) { - $match->outcome = 'loss'; + $match->outcome = "win"; + } elseif ( + $match->winner_user_id && + $match->winner_user_id != $user->id + ) { + $match->outcome = "loss"; } else { - $match->outcome = 'interrupted'; // Matches geralmente não empatam (alguém chega a 4 marcas) + $match->outcome = "interrupted"; // Matches geralmente não empatam (alguém chega a 4 marcas) } return $match; }); @@ -266,7 +318,7 @@ class UserController extends Controller public function block(User $user) { - $this->authorize('block', $user); + $this->authorize("block", $user); $user->blocked = true; $user->save(); @@ -274,43 +326,51 @@ class UserController extends Controller // Revoke all active tokens for immediate effect $user->tokens()->delete(); - return response()->json(['message' => 'User blocked successfully']); + return response()->json(["message" => "User blocked successfully"]); } public function unblock(User $user) { - $this->authorize('unblock', $user); + $this->authorize("unblock", $user); $user->blocked = false; $user->save(); - return response()->json(['message' => 'User unblocked successfully']); + return response()->json(["message" => "User unblocked successfully"]); } public function getTransactions(Request $request, User $user) { - if ($request->user()->id !== $user->id && $request->user()->type !== 'A') { - return response()->json(['message' => 'Forbidden'], 403); + if ( + $request->user()->id !== $user->id && + $request->user()->type !== "A" + ) { + return response()->json(["message" => "Forbidden"], 403); } - $query = $user->transactions()->with('type'); + $query = $user->transactions()->with("type"); - if ($request->has('type')) { - $query->whereHas('type', function ($q) use ($request) { - $q->where('type', $request->type); + if ($request->has("type")) { + $query->whereHas("type", function ($q) use ($request) { + $q->where("type", $request->type); }); } - if ($request->has('date_from')) { - $query->whereDate('transaction_datetime', '>=', $request->date_from); + if ($request->has("date_from")) { + $query->whereDate( + "transaction_datetime", + ">=", + $request->date_from, + ); } - if ($request->has('date_to')) { - $query->whereDate('transaction_datetime', '<=', $request->date_to); + if ($request->has("date_to")) { + $query->whereDate("transaction_datetime", "<=", $request->date_to); } - $transactions = $query->orderBy('transaction_datetime', 'desc') - ->paginate(10); + $transactions = $query + ->orderBy("transaction_datetime", "desc") + ->paginate(10); return response()->json($transactions); } diff --git a/frontend/src/components/layout/NavBar.vue b/frontend/src/components/layout/NavBar.vue index 60230b9..c012324 100644 --- a/frontend/src/components/layout/NavBar.vue +++ b/frontend/src/components/layout/NavBar.vue @@ -1,64 +1,54 @@ \ No newline at end of file + }, + { immediate: true }, +) + diff --git a/frontend/src/pages/TestAllAnimations.vue b/frontend/src/pages/TestAllAnimations.vue deleted file mode 100644 index 2e83f08..0000000 --- a/frontend/src/pages/TestAllAnimations.vue +++ /dev/null @@ -1,342 +0,0 @@ - - - diff --git a/frontend/src/pages/TestAnimations.vue b/frontend/src/pages/TestAnimations.vue deleted file mode 100644 index 10b6006..0000000 --- a/frontend/src/pages/TestAnimations.vue +++ /dev/null @@ -1,252 +0,0 @@ - - - diff --git a/frontend/src/pages/TestDealing.vue b/frontend/src/pages/TestDealing.vue deleted file mode 100644 index 4eb77bd..0000000 --- a/frontend/src/pages/TestDealing.vue +++ /dev/null @@ -1,275 +0,0 @@ - - - diff --git a/frontend/src/pages/TestGameBoard.vue b/frontend/src/pages/TestGameBoard.vue deleted file mode 100644 index de3c89f..0000000 --- a/frontend/src/pages/TestGameBoard.vue +++ /dev/null @@ -1,444 +0,0 @@ - - - diff --git a/frontend/src/pages/admin/AdminPage.vue b/frontend/src/pages/admin/AdminPage.vue index 1245741..348dd48 100644 --- a/frontend/src/pages/admin/AdminPage.vue +++ b/frontend/src/pages/admin/AdminPage.vue @@ -603,65 +603,6 @@ v-else-if="activeTab === 'matches'" class="animate-in fade-in slide-in-from-bottom-2 duration-300" > -
-
- - -
-
- - -
-
- - -
-
- - -
- -
-
-
- - -
-
- @@ -666,21 +650,6 @@
-
- - -
-
- @@ -1083,7 +1051,6 @@ const pagination = ref({ const filters = ref({ type: 'all', variant: 'all', - outcome: 'all', startDate: '', endDate: '', sort: { @@ -1164,7 +1131,6 @@ const fetchData = async () => { const gameMatchParams = { page: state.page, type: filters.value.variant !== 'all' ? filters.value.variant : undefined, - outcome: filters.value.outcome !== 'all' ? filters.value.outcome : undefined, status: 'Ended', date_from: filters.value.startDate || undefined, date_to: filters.value.endDate || undefined, @@ -1324,7 +1290,6 @@ const clearFilters = () => { if (isGameOrMatch) { filters.value.variant = 'all' - filters.value.outcome = 'all' } else { filters.value.type = 'all' } diff --git a/frontend/src/router/index.js b/frontend/src/router/index.js index b2c0293..c7aca9e 100644 --- a/frontend/src/router/index.js +++ b/frontend/src/router/index.js @@ -1,13 +1,7 @@ import HomePage from '@/pages/home/HomePage.vue' import LoginPage from '@/pages/login/LoginPage.vue' -import LaravelPage from '@/pages/testing/LaravelPage.vue' -import WebsocketsPage from '@/pages/testing/WebsocketsPage.vue' import UserPage from '@/pages/user/UserPage.vue' import { createRouter, createWebHistory } from 'vue-router' -import TestAnimations from '@/pages/TestAnimations.vue' -import TestDealing from '@/pages/TestDealing.vue' -import TestAllAnimations from '@/pages/TestAllAnimations.vue' -import TestGameBoard from '@/pages/TestGameBoard.vue' import SinglePlayerGamePage from '@/pages/game/SinglePlayerGamePage.vue' import MultiplayerGamePage from '@/pages/game/MultiplayerGamePage.vue' import RegisterPage from '@/pages/register/RegisterPage.vue' @@ -22,7 +16,7 @@ const router = createRouter({ path: '/', name: 'home', component: HomePage, - meta: { requiresAdmin: false } + meta: { requiresAdmin: false }, }, { path: '/login', @@ -52,7 +46,7 @@ const router = createRouter({ path: '/game/multiplayer/:id', name: 'multiplayer-game', component: MultiplayerGamePage, - meta: { requiresAuth: true } + meta: { requiresAuth: true }, }, { path: '/user', @@ -70,35 +64,6 @@ const router = createRouter({ component: AdminPage, meta: { requiresAuth: true, requiresAdmin: true }, }, - { - path: '/testing', - children: [ - { - path: 'laravel', - component: LaravelPage, - }, - { - path: 'websockets', - component: WebsocketsPage, - }, - { - path: 'animations', - component: TestAnimations, - }, - { - path: 'dealing', - component: TestDealing, - }, - { - path: 'all-animations', - component: TestAllAnimations, - }, - { - path: 'gameboard', - component: TestGameBoard, - }, - ], - }, ], }) diff --git a/frontend/src/stores/api.js b/frontend/src/stores/api.js index ebfc4dd..7ef0d78 100644 --- a/frontend/src/stores/api.js +++ b/frontend/src/stores/api.js @@ -174,19 +174,16 @@ export const useAPIStore = defineStore('api', () => { ...(params.sort_direction == 'asc' && { sort_direction: 'asc' }), ...(params.type && { type: params.type }), }).toString() - + return axios.get(`${API_BASE_URL}/games?${queryParams}`) } const getAdminMatches = (params = {}) => { const queryParams = new URLSearchParams({ page: params.page || 1, + ...(params.sort_direction == 'asc' && { sort_direction: 'asc' }), ...(params.type && { type: params.type }), - ...(params.outcome && { outcome: params.outcome }), - ...(params.date_from && { date_from: params.date_from }), - ...(params.date_to && { date_to: params.date_to }), - sort_by: params.sort_by || 'began_at', - sort_direction: params.sort_direction || 'desc', + ...(params.pot && { pot: params.pot }), }).toString() return axios.get(`${API_BASE_URL}/matches?${queryParams}`)