feat(auth): add contexts + platform.is_super_admin to /auth/me, factory role-category states

Additive enrichment to MeResource — existing fields untouched, MeTest stays green.
New fields:
- contexts.available: list<'portal'|'organizer'> derived from Person + Organisation memberships
- contexts.default: precedence super_admin > organizer > portal > fallback portal
- platform.is_super_admin: bool promoted from app_roles
- organisations[].roles: 1-element array form alongside the legacy scalar role,
  forward-compatible for the multi-role pivot work tracked in TECH-PIVOT-ROLES-MULTI

UserFactory gains volunteer(), orgAdmin(), volunteerAndOrganizer(), superAdmin()
state methods — codified role categories for reuse across future workstreams.

Adds forbidden.vue placeholder (PublicLayout) for the context-failure landing in
the upcoming guard rewrite.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-05 21:15:10 +02:00
parent b5a2140517
commit a2760ffd64
6 changed files with 272 additions and 19 deletions

View File

@@ -4,6 +4,8 @@ declare(strict_types=1);
namespace Database\Factories;
use App\Models\Organisation;
use App\Models\Person;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Facades\Hash;
@@ -23,7 +25,7 @@ final class UserFactory extends Factory
'date_of_birth' => fake()->dateTimeBetween('-50 years', '-18 years')->format('Y-m-d'),
'email' => fake()->unique()->safeEmail(),
'email_verified_at' => now(),
'password' => static::$password ??= Hash::make('password'),
'password' => self::$password ??= Hash::make('password'),
'timezone' => 'Europe/Amsterdam',
'locale' => 'nl',
'remember_token' => Str::random(10),
@@ -34,4 +36,47 @@ final class UserFactory extends Factory
{
return $this->state(fn () => ['email_verified_at' => null]);
}
/**
* Volunteer-only user has a Person record (portal context),
* no Spatie role, no organisation membership.
*/
public function volunteer(): static
{
return $this->afterCreating(function (User $user): void {
Person::factory()->create(['user_id' => $user->id]);
});
}
/**
* Organizer-only user attached to a fresh Organisation as `org_admin`,
* no Person record, no Spatie role.
*/
public function orgAdmin(): static
{
return $this->afterCreating(function (User $user): void {
$organisation = Organisation::factory()->create();
$organisation->users()->attach($user, ['role' => 'org_admin']);
});
}
/**
* Multi-role user has both a Person record AND organisation membership.
*/
public function volunteerAndOrganizer(): static
{
return $this->volunteer()->orgAdmin();
}
/**
* Platform admin Spatie super_admin role. No org/person attachments
* by default (mirrors the production case where super_admins live above
* the org tree).
*/
public function superAdmin(): static
{
return $this->afterCreating(function (User $user): void {
$user->assignRole('super_admin');
});
}
}