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>
This commit is contained in:
2026-04-10 16:35:01 +02:00
parent d704242279
commit 303280286f
4 changed files with 389 additions and 35 deletions

View File

@@ -6,7 +6,10 @@ 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;
@@ -114,14 +117,18 @@ class DevSeeder extends Seeder
],
);
// 6. Festival with sub-events
// =============================================
// 6. Festival: Echt Feesten 2026
// =============================================
$festival = Event::firstOrCreate(
['organisation_id' => $org->id, 'slug' => 'echt-zomer-feesten-2026'],
['organisation_id' => $org->id, 'slug' => 'echt-feesten-2026'],
[
'name' => 'Echt Zomer Feesten 2026',
'name' => 'Echt Feesten 2026',
'start_date' => '2026-07-10',
'end_date' => '2026-07-11',
'status' => 'draft',
'end_date' => '2026-07-12',
'timezone' => 'Europe/Amsterdam',
'status' => 'registration_open',
'event_type' => 'festival',
'event_type_label' => 'Festival',
'sub_event_label' => 'Programmaonderdeel',
@@ -129,30 +136,173 @@ class DevSeeder extends Seeder
],
);
// Sub-event 1: Dance Festival
Event::firstOrCreate(
['organisation_id' => $org->id, 'slug' => 'dance-festival-2026'],
// --- Sub-events ---
$vrijdag = Event::firstOrCreate(
['organisation_id' => $org->id, 'slug' => 'echt-feesten-2026-vrijdag'],
[
'name' => 'Dance Festival',
'name' => 'Vrijdag',
'start_date' => '2026-07-10',
'end_date' => '2026-07-10',
'status' => 'draft',
'timezone' => 'Europe/Amsterdam',
'status' => 'registration_open',
'event_type' => 'event',
'parent_event_id' => $festival->id,
],
);
// Sub-event 2: Zomerfestival
Event::firstOrCreate(
['organisation_id' => $org->id, 'slug' => 'zomerfestival-2026'],
['organisation_id' => $org->id, 'slug' => 'echt-feesten-2026-zaterdag'],
[
'name' => 'Zomerfestival',
'name' => 'Zaterdag',
'start_date' => '2026-07-11',
'end_date' => '2026-07-11',
'status' => 'draft',
'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,
],
);
}
}
}