diff --git a/app/Enums/ParticipateStatus.php b/app/Enums/ParticipateStatus.php new file mode 100644 index 0000000..950572d --- /dev/null +++ b/app/Enums/ParticipateStatus.php @@ -0,0 +1,9 @@ +find($id); if (!$hunt) { return response()->json(['message' => 'Hunt not found'], 404); } + $user = $request->user('sanctum'); + + if ($user) { + $participation = $user->participations()->where('hunt_id', $hunt->id)->first(); + + if ($participation) { + // User is a participant + $currentStepNumber = $participation->pivot->current_step_number; + + // Filter steps + $hunt->setRelation('steps', $hunt->steps->filter(function ($step) use ($currentStepNumber) { + if ($step->step_number < $currentStepNumber) { + return true; // Show past steps fully + } + if ($step->step_number == $currentStepNumber) { + // Hide hints for current step + $step->makeHidden(['hint_text', 'hint_media_url']); + return true; + } + return false; // Hide future steps + })->values()); + + $hunt->participation_status = $participation->pivot; + } elseif ($hunt->creator_id !== $user->id) { + // User is logged in but not participant and not creator + // Show only first step or nothing specific (keep as is for now, maybe hide all steps or just show basic info) + // For now, let's just hide hints of all steps if not participating? + // Or maybe just show nothing of steps if not participating? + // Requirement: "fetch the hunt in question it retrieves me the hunt steps already passed and the next one" implying context of participation. + // If not participating, maybe just return basic hunt info without steps? + $hunt->unsetRelation('steps'); + } + // If creator, show everything (default) + } else { + // Guest + $hunt->unsetRelation('steps'); + } + return response()->json($hunt); } + public function participate(Request $request, $id) + { + $hunt = Hunts::findOrFail($id); + $user = $request->user(); + + if ($user->participations()->where('hunt_id', $hunt->id)->exists()) { + return response()->json(['message' => 'Already participating'], 409); + } + + $hunt->participants()->attach($user->id, ['current_step_number' => 1, 'status' => 'started']); + + return response()->json(['message' => 'Participation started'], 201); + } + /** * Update the specified resource in storage. */ diff --git a/app/Models/Hunts.php b/app/Models/Hunts.php index 63c8734..0d54beb 100644 --- a/app/Models/Hunts.php +++ b/app/Models/Hunts.php @@ -43,4 +43,11 @@ class Hunts extends Model { return $this->hasMany(HuntSteps::class, 'hunt_id'); } + + public function participants() + { + return $this->belongsToMany(User::class, 'hunt_participants', 'hunt_id', 'user_id') + ->withPivot(['current_step_number', 'status']) + ->withTimestamps(); + } } diff --git a/app/Models/User.php b/app/Models/User.php index c5a0f61..4687306 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -54,4 +54,11 @@ class User extends Authenticatable 'role'=> UserRole::class ]; } + + public function participations() + { + return $this->belongsToMany(Hunts::class, 'hunt_participants', 'user_id', 'hunt_id') + ->withPivot(['current_step_number', 'status']) + ->withTimestamps(); + } } diff --git a/database/migrations/2025_12_19_113519_create_hunt_participants_table.php b/database/migrations/2025_12_19_113519_create_hunt_participants_table.php new file mode 100644 index 0000000..47d909b --- /dev/null +++ b/database/migrations/2025_12_19_113519_create_hunt_participants_table.php @@ -0,0 +1,32 @@ +id(); + $table->foreignId('user_id')->constrained()->cascadeOnDelete(); + $table->foreignId('hunt_id')->constrained('hunts')->cascadeOnDelete(); + $table->unsignedSmallInteger('current_step_number')->default(1); + $table->string('status')->default(ParticipateStatus::Started->value); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('hunt_participants'); + } +}; diff --git a/routes/api.php b/routes/api.php index c2d0a74..0e0c5a0 100644 --- a/routes/api.php +++ b/routes/api.php @@ -7,6 +7,7 @@ use Illuminate\Support\Facades\Route; use App\Http\Controllers\HuntsController; +use App\Http\Controllers\HuntStepsController; use App\Http\Middleware\EnsureUserIsOrga; Route::prefix('auth')->group(function (): void { @@ -17,36 +18,22 @@ Route::prefix('auth')->group(function (): void { // Route::get('{provider}/callback', [ProviderCallbackController::class, 'callback']); }); - +// List hunts Route::get('hunts', [HuntsController::class, 'index']); Route::get('hunts/{hunt}', [HuntsController::class, 'show']); +// Ensure is Orga Route::middleware(['auth:sanctum', EnsureUserIsOrga::class])->group(function () { Route::post('hunts', [HuntsController::class, 'store']); - Route::post('hunts/{hunt}/steps', [\App\Http\Controllers\HuntStepsController::class, 'store']); + Route::post('hunts/{hunt}/steps', [HuntStepsController::class, 'store']); Route::match(['put', 'patch'], 'hunts/{hunt}', [HuntsController::class, 'update']); Route::delete('hunts/{hunt}', [HuntsController::class, 'destroy']); }); +// Auth middleware Route::middleware('auth:sanctum')->group(function (): void { - // Route::get('/user', function (Request $request) { - // return $request->user(); - // }); - // User + Route::post('hunts/{hunt}/participate', [HuntsController::class, 'participate']); Route::get('auth/me', [AuthController::class, 'me']); Route::post('auth/logout', [AuthController::class, 'logout']); - - - - // Route::middleware([EnsureAdmin::class])->group(function (): void { - // Route::apiResource('users', UserController::class); - // Route::apiResource('main-categories', MainCategoryController::class)->except(['index', 'show']); - // Route::apiResource('categories', CategoryController::class)->except(['index', 'show']); - // Route::apiResource('products', ProductsController::class)->except(['index', 'show']); - // Route::apiResource('product-variants', ProductVariantController::class); - // Route::apiResource('product-variant-images', ProductVariantImagesController::class); - // Route::apiResource('pickup-points', PickupPointsController::class)->only(['destroy']); - // Route::apiResource('pickup-point-schedules', PickupPointsSchedulesController::class)->except(['index', 'show']); - // }); }); \ No newline at end of file