Files
crewli/api/database/seeders/DevSeeder.php
bert.hausmans 303280286f feat: festival helper scopes and DevSeeder with full festival structure (TECH-02, TECH-03)
Fix scopeWithChildren to accept an event ID and add scopeForFestival
scope for resolving any event to its full festival context. Extend
DevSeeder with sections, time slots, and persons on the festival.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 16:35:01 +02:00

309 lines
10 KiB
PHP

<?php
declare(strict_types=1);
namespace Database\Seeders;
use App\Models\CrowdType;
use App\Models\Event;
use App\Models\FestivalSection;
use App\Models\Organisation;
use App\Models\Person;
use App\Models\TimeSlot;
use App\Models\User;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\Hash;
class DevSeeder extends Seeder
{
public function run(): void
{
$this->call(RoleSeeder::class);
// 1. Super Admin
$admin = User::firstOrCreate(
['email' => 'admin@crewli.test'],
[
'name' => 'Dev Admin',
'password' => Hash::make('password'),
],
);
$admin->assignRole('super_admin');
// Test organisation
$org = Organisation::firstOrCreate(
['slug' => 'test-festival-bv'],
[
'name' => 'Test Festival BV',
'billing_status' => 'active',
'settings' => [],
],
);
// 2. Org Admin
$orgAdmin = User::firstOrCreate(
['email' => 'orgadmin@crewli.test'],
[
'name' => 'Org Admin',
'password' => Hash::make('password'),
],
);
if (!$org->users()->where('user_id', $orgAdmin->id)->exists()) {
$org->users()->attach($orgAdmin, ['role' => 'org_admin']);
}
// Second test organisation (for testing the organisation switcher)
$org2 = Organisation::firstOrCreate(
['slug' => 'zomerfest-nederland'],
[
'name' => 'Zomerfest Nederland',
'billing_status' => 'trial',
'settings' => [],
],
);
// Attach admin and orgAdmin to second org too
if (!$org2->users()->where('user_id', $admin->id)->exists()) {
$org2->users()->attach($admin, ['role' => 'org_admin']);
}
if (!$org2->users()->where('user_id', $orgAdmin->id)->exists()) {
$org2->users()->attach($orgAdmin, ['role' => 'org_member']);
}
// 3. Org Member
$member = User::firstOrCreate(
['email' => 'crew@crewli.test'],
[
'name' => 'Crew Member',
'password' => Hash::make('password'),
],
);
if (!$org->users()->where('user_id', $member->id)->exists()) {
$org->users()->attach($member, ['role' => 'org_member']);
}
// 4. Default Crowd Types for Test Festival BV
$crowdTypes = [
['name' => 'Crew', 'system_type' => 'CREW', 'color' => '#3b82f6'],
['name' => 'Vrijwilliger', 'system_type' => 'VOLUNTEER', 'color' => '#10b981'],
['name' => 'Artiest', 'system_type' => 'ARTIST', 'color' => '#8b5cf6'],
['name' => 'Gast', 'system_type' => 'GUEST', 'color' => '#f59e0b'],
['name' => 'Pers', 'system_type' => 'PRESS', 'color' => '#6366f1'],
];
foreach ($crowdTypes as $ct) {
CrowdType::firstOrCreate(
[
'organisation_id' => $org->id,
'system_type' => $ct['system_type'],
],
[
'name' => $ct['name'],
'color' => $ct['color'],
],
);
}
// 5. Flat event (backward compatible single event)
Event::firstOrCreate(
['organisation_id' => $org->id, 'slug' => 'test-event-01'],
[
'name' => 'Test Event 01',
'start_date' => '2026-08-15',
'end_date' => '2026-08-15',
'status' => 'draft',
'event_type' => 'event',
'parent_event_id' => null,
],
);
// =============================================
// 6. Festival: Echt Feesten 2026
// =============================================
$festival = Event::firstOrCreate(
['organisation_id' => $org->id, 'slug' => 'echt-feesten-2026'],
[
'name' => 'Echt Feesten 2026',
'start_date' => '2026-07-10',
'end_date' => '2026-07-12',
'timezone' => 'Europe/Amsterdam',
'status' => 'registration_open',
'event_type' => 'festival',
'event_type_label' => 'Festival',
'sub_event_label' => 'Programmaonderdeel',
'parent_event_id' => null,
],
);
// --- Sub-events ---
$vrijdag = Event::firstOrCreate(
['organisation_id' => $org->id, 'slug' => 'echt-feesten-2026-vrijdag'],
[
'name' => 'Vrijdag',
'start_date' => '2026-07-10',
'end_date' => '2026-07-10',
'timezone' => 'Europe/Amsterdam',
'status' => 'registration_open',
'event_type' => 'event',
'parent_event_id' => $festival->id,
],
);
Event::firstOrCreate(
['organisation_id' => $org->id, 'slug' => 'echt-feesten-2026-zaterdag'],
[
'name' => 'Zaterdag',
'start_date' => '2026-07-11',
'end_date' => '2026-07-11',
'timezone' => 'Europe/Amsterdam',
'status' => 'registration_open',
'event_type' => 'event',
'parent_event_id' => $festival->id,
],
);
Event::firstOrCreate(
['organisation_id' => $org->id, 'slug' => 'echt-feesten-2026-zondag'],
[
'name' => 'Zondag',
'start_date' => '2026-07-12',
'end_date' => '2026-07-12',
'timezone' => 'Europe/Amsterdam',
'status' => 'registration_open',
'event_type' => 'event',
'parent_event_id' => $festival->id,
],
);
// --- Festival-level sections (on the parent) ---
FestivalSection::firstOrCreate(
['event_id' => $festival->id, 'name' => 'EHBO'],
[
'type' => 'cross_event',
'category' => 'Veiligheid',
'icon' => 'tabler-first-aid-kit',
'sort_order' => 1,
'responder_self_checkin' => true,
'crew_auto_accepts' => false,
],
);
FestivalSection::firstOrCreate(
['event_id' => $festival->id, 'name' => 'Nachtsecurity'],
[
'type' => 'standard',
'category' => 'Veiligheid',
'icon' => 'tabler-shield',
'sort_order' => 2,
'responder_self_checkin' => true,
'crew_auto_accepts' => false,
],
);
// --- Sub-event sections (on Vrijdag) ---
FestivalSection::firstOrCreate(
['event_id' => $vrijdag->id, 'name' => 'Hoofdpodium Bar'],
[
'type' => 'standard',
'category' => 'Bar',
'icon' => 'tabler-beer',
'sort_order' => 1,
'responder_self_checkin' => true,
'crew_auto_accepts' => false,
],
);
FestivalSection::firstOrCreate(
['event_id' => $vrijdag->id, 'name' => 'Backstage'],
[
'type' => 'standard',
'category' => 'Hospitality',
'icon' => 'tabler-armchair',
'sort_order' => 2,
'responder_self_checkin' => true,
'crew_auto_accepts' => false,
],
);
// --- Festival-level time slots (on the parent) ---
TimeSlot::firstOrCreate(
['event_id' => $festival->id, 'name' => 'Opbouw'],
[
'date' => '2026-07-09',
'start_time' => '08:00:00',
'end_time' => '18:00:00',
'duration_hours' => 10.00,
'person_type' => 'CREW',
],
);
TimeSlot::firstOrCreate(
['event_id' => $festival->id, 'name' => 'Afbraak'],
[
'date' => '2026-07-13',
'start_time' => '08:00:00',
'end_time' => '18:00:00',
'duration_hours' => 10.00,
'person_type' => 'CREW',
],
);
// --- Sub-event time slots (on Vrijdag) ---
TimeSlot::firstOrCreate(
['event_id' => $vrijdag->id, 'name' => 'Vrijdag Avond'],
[
'date' => '2026-07-10',
'start_time' => '18:00:00',
'end_time' => '02:00:00',
'duration_hours' => 8.00,
'person_type' => 'VOLUNTEER',
],
);
TimeSlot::firstOrCreate(
['event_id' => $vrijdag->id, 'name' => 'Vrijdag Nacht'],
[
'date' => '2026-07-10',
'start_time' => '22:00:00',
'end_time' => '04:00:00',
'duration_hours' => 6.00,
'person_type' => 'CREW',
],
);
// --- Festival-level persons (5 volunteers on the parent) ---
$volunteerType = CrowdType::where('organisation_id', $org->id)
->where('system_type', 'VOLUNTEER')
->first();
$festivalPersons = [
['name' => 'Sanne de Vries', 'email' => 'sanne.devries@example.nl', 'phone' => '+31612345001'],
['name' => 'Pieter Jansen', 'email' => 'pieter.jansen@example.nl', 'phone' => '+31612345002'],
['name' => 'Marieke van den Berg', 'email' => 'marieke.vandenberg@example.nl', 'phone' => '+31612345003'],
['name' => 'Thijs Bakker', 'email' => 'thijs.bakker@example.nl', 'phone' => '+31612345004'],
// Uses the seeded orgAdmin email to test identity matching later
['name' => 'Org Admin Volunteer', 'email' => 'orgadmin@crewli.test', 'phone' => '+31612345005'],
];
foreach ($festivalPersons as $personData) {
Person::firstOrCreate(
['event_id' => $festival->id, 'email' => $personData['email']],
[
'crowd_type_id' => $volunteerType->id,
'name' => $personData['name'],
'phone' => $personData['phone'],
'status' => 'approved',
'is_blacklisted' => false,
],
);
}
}
}