From b82b4d5b592e09cf393beffbfcbe1e646949f736 Mon Sep 17 00:00:00 2001 From: AfonsoCMSousa Date: Tue, 23 Dec 2025 18:21:04 +0000 Subject: [PATCH] SyntaxSquad/DADProject#9 feat: Added profile change and name/password changes. --- api/routes/api.php | 61 +++ frontend/src/main.js | 5 +- frontend/src/pages/user/UserPage.vue | 590 +++++++++++++++++++++++---- frontend/src/stores/auth.js | 2 + 4 files changed, 572 insertions(+), 86 deletions(-) diff --git a/api/routes/api.php b/api/routes/api.php index 6aeaa71..e3a0fe3 100644 --- a/api/routes/api.php +++ b/api/routes/api.php @@ -18,6 +18,67 @@ Route::middleware('auth:sanctum')->group(function () { Route::get('/me', function (Request $request) { return $request->user(); }); + + // Update user profile (name and nickname) + Route::put('/me', function (Request $request) { + $request->validate([ + 'name' => 'required|string|max:255', + 'nickname' => 'required|string|max:255|unique:users,nickname,' . $request->user()->id, + ]); + + $user = $request->user(); + $user->name = $request->name; + $user->nickname = $request->nickname; + $user->save(); + + return response()->json($user); + }); + + // Change password + Route::put('/me/password', function (Request $request) { + $request->validate([ + 'current_password' => 'required', + 'new_password' => 'required|min:8|confirmed', + ]); + + $user = $request->user(); + + if (!\Hash::check($request->current_password, $user->password)) { + return response()->json([ + 'message' => 'Current password is incorrect' + ], 422); + } + + $user->password = \Hash::make($request->new_password); + $user->save(); + + return response()->json([ + 'message' => 'Password changed successfully' + ]); + }); + + // Upload avatar + Route::post('/me/avatar', function (Request $request) { + $request->validate([ + 'avatar' => 'required|image|mimes:jpeg,png,jpg,gif|max:2048', + ]); + + $user = $request->user(); + + if ($user->photo_avatar_filename) { + \Storage::disk('public')->delete('photos_avatars/' . $user->photo_avatar_filename); + } + + $file = $request->file('avatar'); + $filename = str_pad($user->id, 5, '0', STR_PAD_LEFT) . '_' . \Str::random(10) . '.' . $file->extension(); + $file->storeAs('photos_avatars', $filename, 'public'); + + $user->photo_avatar_filename = $filename; + $user->save(); + + return response()->json($user); + }); + Route::get('/{user}/matches', [UserController::class, 'getMatches']); }); diff --git a/frontend/src/main.js b/frontend/src/main.js index a14e65a..2d5c398 100644 --- a/frontend/src/main.js +++ b/frontend/src/main.js @@ -1,7 +1,6 @@ import { createApp } from 'vue' import { createPinia } from 'pinia' import { io } from 'socket.io-client' -import { useAuthStore } from '@/stores/auth' import App from './App.vue' import router from './router' @@ -21,7 +20,5 @@ app.provide('apiBaseURL', `http://${apiDomain}/api`) app.use(createPinia()) app.use(router) -const authStore = useAuthStore() -authStore.initAuth() - app.mount('#app') + diff --git a/frontend/src/pages/user/UserPage.vue b/frontend/src/pages/user/UserPage.vue index 2af39e8..147a64d 100644 --- a/frontend/src/pages/user/UserPage.vue +++ b/frontend/src/pages/user/UserPage.vue @@ -30,13 +30,26 @@
{{ authStore.currentUser.name.charAt(0).toUpperCase() }}
+ +

{{ authStore.currentUser.name }}

@@ -52,91 +65,209 @@
-
-
-
- - - - -
-
- -

{{ authStore.currentUser.email }}

-
-
+ +
+ + + +
-
-
- - - - + +
+
+
+
+ + + + +
+
+ +

{{ authStore.currentUser.email }}

+
-
- -

{{ authStore.currentUser.type === 'P' ? 'Player' : 'Other' }}

-
-
-
-
- - - - - - +
+
+ + + + +
+
+ +

{{ authStore.currentUser.type === 'P' ? 'Player' : 'Other' }}

+
-
- -

{{ formatDate(authStore.currentUser.created_at) }}

-
-
-
-
- - - - +
+
+ + + + + + +
+
+ +

{{ formatDate(authStore.currentUser.created_at) }}

+
-
- -

- - {{ authStore.currentUser.blocked ? 'Blocked' : 'Active' }} - -

-
-
-
-
- - - - +
+
+ + + + +
+
+ +

+ + {{ authStore.currentUser.blocked ? 'Blocked' : 'Active' }} + +

+
-
- -

{{ formatDate(authStore.currentUser.email_verified_at) }}

-
-
-
-
- - - - +
+
+ + + + +
+
+ +

{{ formatDate(authStore.currentUser.email_verified_at) }}

+
-
- -

{{ formatDate(authStore.currentUser.updated_at) }}

+ +
+
+ + + + +
+
+ +

{{ formatDate(authStore.currentUser.updated_at) }}

+
+ + +
+
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ {{ profileMessage }} +
+
+
+ + +
+
+
+ + +
+ +
+ + + Password must be at least 8 characters long +
+ +
+ + +
+ +
+ + +
+ +
+ {{ passwordMessage }} +
+
+
@@ -149,17 +280,40 @@ @@ -210,14 +465,12 @@ onMounted(async () => { diff --git a/frontend/src/stores/auth.js b/frontend/src/stores/auth.js index 43c8434..14f60ea 100644 --- a/frontend/src/stores/auth.js +++ b/frontend/src/stores/auth.js @@ -5,6 +5,8 @@ import { useAPIStore } from './api' export const useAuthStore = defineStore('auth', () => { const apiStore = useAPIStore() + + const currentUser = ref(undefined) const token = ref(localStorage.getItem('token') || null)