From 09a1cb8c8e45f3bf79079b8094eda39dcce3d272 Mon Sep 17 00:00:00 2001 From: Hector Villarreal Date: Sat, 28 Mar 2026 12:36:12 -0600 Subject: [PATCH] Add fixtures and picks --- app/Http/Controllers/Api/AuthController.php | 34 +++++++----- .../Controllers/Api/FixturesController.php | 14 +++++ app/Http/Controllers/Api/PicksController.php | 54 +++++++++++++++++++ app/Http/Resources/FixtureResource.php | 35 ++++++++++++ app/Models/Pick.php | 7 +++ app/Models/User.php | 2 +- config/cors.php | 2 +- ..._21_052231_add_username_to_users_table.php | 28 ++++++++++ docker-compose.yml | 2 + routes/api.php | 9 ++++ 10 files changed, 172 insertions(+), 15 deletions(-) create mode 100644 app/Http/Controllers/Api/FixturesController.php create mode 100644 app/Http/Controllers/Api/PicksController.php create mode 100644 app/Http/Resources/FixtureResource.php create mode 100644 database/migrations/2026_03_21_052231_add_username_to_users_table.php diff --git a/app/Http/Controllers/Api/AuthController.php b/app/Http/Controllers/Api/AuthController.php index 69d451a..15a6757 100644 --- a/app/Http/Controllers/Api/AuthController.php +++ b/app/Http/Controllers/Api/AuthController.php @@ -6,6 +6,7 @@ use App\Http\Controllers\Controller; use App\Models\User; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; +use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Hash; use Illuminate\Validation\ValidationException; @@ -18,23 +19,25 @@ class AuthController extends Controller 'last_name' => ['required', 'string', 'max:255'], 'email' => ['required', 'string', 'email', 'max:255', 'unique:users,email'], 'phone' => ['required', 'string', 'max:25'], - 'password' => ['required', 'string', 'min:8'], - 'device_name' => ['nullable', 'string', 'max:255'], + 'password' => ['required', 'string', 'min:8', 'confirmed'], + 'username' => ['required', 'string', 'max:32', 'unique:users,username'] ]); $user = User::create([ + 'username' => $validated['username'], 'first_name' => $validated['first_name'], 'last_name' => $validated['last_name'], 'email' => strtolower($validated['email']), 'phone' => $validated['phone'], - 'password' => $validated['password'], + 'password' => Hash::make($validated['password']), ]); + Auth::login($user); + $request->session()->regenerate(); + return response()->json([ 'message' => 'User registered successfully.', - 'token' => $user->createToken($validated['device_name'] ?? 'react-app')->plainTextToken, - 'token_type' => 'Bearer', - 'user' => $user, + 'user' => $request->user(), ], 201); } @@ -43,28 +46,33 @@ class AuthController extends Controller $validated = $request->validate([ 'email' => ['required', 'string', 'email'], 'password' => ['required', 'string'], - 'device_name' => ['nullable', 'string', 'max:255'], ]); - $user = User::query()->where('email', strtolower($validated['email']))->first(); + $credentials = [ + 'email' => strtolower($validated['email']), + 'password' => $validated['password'], + ]; - if ($user === null || ! Hash::check($validated['password'], $user->password)) { + if (! Auth::attempt($credentials, $request->boolean('remember'))) { throw ValidationException::withMessages([ 'email' => ['Los datos no coinciden con nuestros registros.'], ]); } + $request->session()->regenerate(); + return response()->json([ 'message' => 'Login successful.', - 'token' => $user->createToken($validated['device_name'] ?? 'react-app')->plainTextToken, - 'token_type' => 'Bearer', - 'user' => $user, + 'user' => $request->user(), ]); } public function logout(Request $request): JsonResponse { - $request->user()->currentAccessToken()?->delete(); + Auth::guard('web')->logout(); + + $request->session()->invalidate(); + $request->session()->regenerateToken(); return response()->json([ 'message' => 'Logout successful.', diff --git a/app/Http/Controllers/Api/FixturesController.php b/app/Http/Controllers/Api/FixturesController.php new file mode 100644 index 0000000..37741bc --- /dev/null +++ b/app/Http/Controllers/Api/FixturesController.php @@ -0,0 +1,14 @@ +get(); + return response()->json(['fixtures' => FixtureResource::collection($fixtures)]); + } +} diff --git a/app/Http/Controllers/Api/PicksController.php b/app/Http/Controllers/Api/PicksController.php new file mode 100644 index 0000000..649fe23 --- /dev/null +++ b/app/Http/Controllers/Api/PicksController.php @@ -0,0 +1,54 @@ +where('user_id', $request->user()->id) + ->get([ + 'id', + 'match_id', + 'selection', + 'points_awarded', + 'submitted_at', + 'graded_at', + ]); + + return response()->json([ + 'picks' => $picks, + ]); + } + + public function store(Request $request): JsonResponse + { + $validated = $request->validate([ + 'match_id' => ['required', 'integer', 'exists:matches,id'], + 'selection' => ['required', Rule::in(['home', 'draw', 'away'])], + ]); + + $pick = Pick::updateOrCreate( + [ + 'user_id' => $request->user()->id, + 'match_id' => $validated['match_id'], + ], + [ + 'selection' => $validated['selection'], + 'submitted_at' => now(), + ] + ); + + return response()->json( + ['pick' => $pick], + $pick->wasRecentlyCreated ? 201 : 200 + ); + } +} diff --git a/app/Http/Resources/FixtureResource.php b/app/Http/Resources/FixtureResource.php new file mode 100644 index 0000000..8d39ecd --- /dev/null +++ b/app/Http/Resources/FixtureResource.php @@ -0,0 +1,35 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this->id, + 'home_team' => $this->homeTeam->name, + 'away_team' => $this->awayTeam->name, + 'starts_at' => $this->starts_at->timestamp, + 'venue' => $this->venue, + 'status' => $this->status, + 'stage' => $this->stage, + 'home_short_name' => $this->homeTeam->short_name, + 'away_short_name' => $this->awayTeam->short_name, + ]; + } +} diff --git a/app/Models/Pick.php b/app/Models/Pick.php index c486926..cc2c40e 100644 --- a/app/Models/Pick.php +++ b/app/Models/Pick.php @@ -20,6 +20,13 @@ class Pick extends Model /** * @return array */ + protected $fillable = [ + 'user_id', + 'match_id', + 'selection', + 'submitted_at', + ]; + protected function casts(): array { return [ diff --git a/app/Models/User.php b/app/Models/User.php index 37d18f5..8834a2e 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -25,7 +25,7 @@ class User extends Authenticatable implements FilamentUser protected $fillable = [ 'first_name', 'last_name', - + 'username', 'email', 'phone', 'is_admin', diff --git a/config/cors.php b/config/cors.php index 94f07be..5ff2a03 100644 --- a/config/cors.php +++ b/config/cors.php @@ -19,7 +19,7 @@ return [ 'allowed_methods' => ['*'], - 'allowed_origins' => ['*'], + 'allowed_origins' => ['http://localhost:3001'], 'allowed_origins_patterns' => [], diff --git a/database/migrations/2026_03_21_052231_add_username_to_users_table.php b/database/migrations/2026_03_21_052231_add_username_to_users_table.php new file mode 100644 index 0000000..1af8b9e --- /dev/null +++ b/database/migrations/2026_03_21_052231_add_username_to_users_table.php @@ -0,0 +1,28 @@ +string('username', 32)->nullable()->after('id'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn('username'); + }); + } +}; diff --git a/docker-compose.yml b/docker-compose.yml index aed22fe..f519579 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -20,6 +20,8 @@ services: REDIS_CLIENT: phpredis REDIS_HOST: redis REDIS_PORT: 6379 + SANCTUM_STATEFUL_DOMAINS: "localhost:3001,127.0.0.1:3001,localhost:8080,127.0.0.1:8080" + SESSION_DOMAIN: localhost volumes: - ./:/var/www/html - ./docker/php/conf.d/local.ini:/usr/local/etc/php/conf.d/local.ini:ro diff --git a/routes/api.php b/routes/api.php index 94b7bf2..bb8f8ba 100644 --- a/routes/api.php +++ b/routes/api.php @@ -1,6 +1,8 @@ group(function (): void { }); }); }); + +Route::middleware('auth:sanctum')->group(function(): void { + Route::get('/fixtures', [FixturesController::class, 'index']); + + Route::get('/picks', [PicksController::class, 'index']); + Route::post('/picks', [PicksController::class, 'store']); +});