Add RSVP functionality for Meetup Events

- 🏷️ Introduce `RsvpStatus` enum for managing attendance states (`attending`, `maybe`, `none`).
- ✏️ Add `MeetupEventController` methods for RSVP actions (`rsvpStatus`, `rsvp`) and payload handling.
-  Implement RSVP helpers in `MeetupEvent` model for user-specific attendance management.
- 🌐 Register RSVP routes for showing and updating attendance in the API.
- 🧪 Add feature tests for RSVP actions, covering validation, idempotency, and correct list handling.
This commit is contained in:
HolgerHatGarKeineNode
2026-06-15 22:10:10 +02:00
parent e55967e9ac
commit 0a1d177fc4
6 changed files with 249 additions and 0 deletions
@@ -3,11 +3,14 @@
namespace App\Http\Controllers\Api;
use App\Actions\MeetupEvents\CreateMeetupEventSeries;
use App\Enums\RsvpStatus;
use App\Http\Controllers\Controller;
use App\Http\Requests\Api\RsvpMeetupEventRequest;
use App\Http\Requests\Api\StoreMeetupEventRequest;
use App\Http\Requests\Api\UpdateMeetupEventRequest;
use App\Http\Resources\MeetupEventResource;
use App\Models\MeetupEvent;
use App\Models\User;
use Carbon\Carbon;
use Carbon\Exceptions\InvalidFormatException;
use Dedoc\Scramble\Attributes\Group;
@@ -158,4 +161,49 @@ class MeetupEventController extends Controller
return MeetupEventResource::make($meetupEvent);
}
/**
* RSVP-Status eines Termins anzeigen
*
* Liefert den eigenen Teilnahme-Status des authentifizierten Nutzers für
* diesen Termin sowie die aktuellen Zähler der Zu- und Vielleicht-Sagen.
*/
public function rsvpStatus(Request $request, MeetupEvent $meetupEvent): JsonResponse
{
return response()->json($this->rsvpPayload($meetupEvent, $request->user()));
}
/**
* Für einen Termin zu- oder absagen
*
* Trägt den authentifizierten Nutzer als Teilnehmer („attending"),
* Vielleicht-Teilnehmer („maybe") oder gar nicht („none", = absagen) ein.
* Der Anzeigename wird automatisch aus dem Profil übernommen. Idempotent:
* derselbe Status mehrfach gesetzt verändert nichts.
*/
#[ResponseAttribute(status: 401, description: 'Nicht authentifiziert.')]
#[ResponseAttribute(status: 422, description: 'Validierungsfehler (unbekannter Status).')]
public function rsvp(RsvpMeetupEventRequest $request, MeetupEvent $meetupEvent): JsonResponse
{
$user = $request->user();
$status = RsvpStatus::from($request->validated('status'));
$meetupEvent->setRsvpFor($user, $status, (string) $user->name);
return response()->json($this->rsvpPayload($meetupEvent->fresh(), $user));
}
/**
* Einheitliche RSVP-Antwort: eigener Status + aktuelle Zähler.
*
* @return array{status: string, attendees: int, might_attendees: int}
*/
private function rsvpPayload(MeetupEvent $meetupEvent, User $user): array
{
return [
'status' => $meetupEvent->rsvpStatusFor($user)->value,
'attendees' => count($meetupEvent->attendees ?? []),
'might_attendees' => count($meetupEvent->might_attendees ?? []),
];
}
}
@@ -0,0 +1,26 @@
<?php
namespace App\Http\Requests\Api;
use App\Enums\RsvpStatus;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
class RsvpMeetupEventRequest extends FormRequest
{
public function authorize(): bool
{
// Jeder authentifizierte Nutzer darf für einen Termin zu-/absagen.
return $this->user() !== null;
}
/**
* @return array<string, array<int, mixed>>
*/
public function rules(): array
{
return [
'status' => ['required', Rule::enum(RsvpStatus::class)],
];
}
}