1277 lines
77 KiB
PHP
1277 lines
77 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Database\Seeders;
|
|
|
|
use App\Enums\CrowdListType;
|
|
use App\Enums\ShiftAssignmentStatus;
|
|
use App\Models\Company;
|
|
use App\Models\CrowdList;
|
|
use App\Models\CrowdType;
|
|
use App\Models\Event;
|
|
use App\Models\FestivalSection;
|
|
use App\Models\Location;
|
|
use App\Models\Organisation;
|
|
use App\Models\Person;
|
|
use App\Models\Shift;
|
|
use App\Models\ShiftAssignment;
|
|
use App\Models\TimeSlot;
|
|
use App\Models\User;
|
|
use App\Models\UserOrganisationTag;
|
|
use App\Models\VolunteerAvailability;
|
|
use Illuminate\Database\Seeder;
|
|
use Illuminate\Support\Collection;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Hash;
|
|
|
|
class DevSeeder extends Seeder
|
|
{
|
|
private Organisation $org;
|
|
|
|
/** @var array<string, User> */
|
|
private array $users = [];
|
|
|
|
/** @var array<string, CrowdType> */
|
|
private array $crowdTypes = [];
|
|
|
|
/** @var array<string, Company> */
|
|
private array $companies = [];
|
|
|
|
/** @var array<string, \App\Models\PersonTag> */
|
|
private array $personTags = [];
|
|
|
|
public function run(): void
|
|
{
|
|
$this->call(RoleSeeder::class);
|
|
|
|
$this->seedOrganisation();
|
|
$this->seedEchtFeesten();
|
|
$this->seedIJsbaan();
|
|
$this->seedKoningsdag();
|
|
$this->seedNachtVanDeKaap();
|
|
}
|
|
|
|
// =========================================================================
|
|
// Organisation: Stichting Feestfabriek
|
|
// =========================================================================
|
|
|
|
private function seedOrganisation(): void
|
|
{
|
|
DB::transaction(function (): void {
|
|
$this->command->info('Seeding organisation: Stichting Feestfabriek...');
|
|
|
|
$this->org = Organisation::create([
|
|
'name' => 'Stichting Feestfabriek',
|
|
'slug' => 'stichting-feestfabriek',
|
|
'billing_status' => 'active',
|
|
'settings' => [],
|
|
]);
|
|
|
|
// ── Users (8) ──
|
|
|
|
$usersData = [
|
|
['email' => 'admin@crewli.test', 'first_name' => 'Super', 'last_name' => 'Admin', 'app_role' => 'super_admin', 'org_role' => 'org_admin'],
|
|
['email' => 'bert@feestfabriek.nl', 'first_name' => 'Bert', 'last_name' => 'Hausmans', 'org_role' => 'org_admin'],
|
|
['email' => 'lisa@feestfabriek.nl', 'first_name' => 'Lisa', 'last_name' => 'van den Berg', 'org_role' => 'org_member'],
|
|
['email' => 'ahmed@feestfabriek.nl', 'first_name' => 'Ahmed', 'last_name' => 'Yilmaz', 'org_role' => 'org_member'],
|
|
['email' => 'sara@feestfabriek.nl', 'first_name' => 'Sara', 'last_name' => 'de Groot', 'org_role' => 'org_member'],
|
|
['email' => 'tom@feestfabriek.nl', 'first_name' => 'Tom', 'last_name' => 'Visser', 'org_role' => 'org_member'],
|
|
['email' => 'nina@feestfabriek.nl', 'first_name' => 'Nina', 'last_name' => 'Jansen', 'org_role' => 'org_member'],
|
|
['email' => 'mark@feestfabriek.nl', 'first_name' => 'Mark', 'last_name' => 'de Boer', 'org_role' => 'org_member'],
|
|
];
|
|
|
|
foreach ($usersData as $data) {
|
|
$user = User::create([
|
|
'first_name' => $data['first_name'],
|
|
'last_name' => $data['last_name'],
|
|
'email' => $data['email'],
|
|
'password' => Hash::make('password'),
|
|
]);
|
|
|
|
if (isset($data['app_role'])) {
|
|
$user->assignRole($data['app_role']);
|
|
}
|
|
|
|
$this->org->users()->attach($user, ['role' => $data['org_role']]);
|
|
$this->users[$data['email']] = $user;
|
|
}
|
|
|
|
// ── Companies (6) ──
|
|
|
|
$companiesData = [
|
|
['name' => 'Tap & Co Horeca', 'type' => 'supplier', 'contact_first_name' => 'Jan', 'contact_last_name' => 'Tapper', 'contact_email' => 'jan@tapco.nl', 'contact_phone' => '+31612340001'],
|
|
['name' => 'SecureEvent BV', 'type' => 'supplier', 'contact_first_name' => 'Klaas', 'contact_last_name' => 'Veilig', 'contact_email' => 'klaas@secureevent.nl', 'contact_phone' => '+31612340002'],
|
|
['name' => 'Podiumtechniek Rijnmond', 'type' => 'supplier', 'contact_first_name' => 'Pieter', 'contact_last_name' => 'Geluid', 'contact_email' => 'pieter@podiumtechniek.nl', 'contact_phone' => '+31612340003'],
|
|
['name' => 'Brouwerij De Schelde', 'type' => 'partner', 'contact_first_name' => 'Eva', 'contact_last_name' => 'Brouwer', 'contact_email' => 'eva@brouwerijdeschelde.nl', 'contact_phone' => '+31612340004'],
|
|
['name' => 'Van Dijk Catering', 'type' => 'supplier', 'contact_first_name' => 'Maria', 'contact_last_name' => 'van Dijk', 'contact_email' => 'maria@vandijkcatering.nl', 'contact_phone' => '+31612340005'],
|
|
['name' => 'Rotterdam Festivals', 'type' => 'agency', 'contact_first_name' => 'Femke', 'contact_last_name' => 'de Wit', 'contact_email' => 'femke@rotterdamfestivals.nl', 'contact_phone' => '+31612340006'],
|
|
];
|
|
|
|
foreach ($companiesData as $data) {
|
|
$company = Company::create(['organisation_id' => $this->org->id, ...$data]);
|
|
$this->companies[$data['name']] = $company;
|
|
}
|
|
|
|
// ── Crowd Types (7) ──
|
|
|
|
$crowdTypesData = [
|
|
['name' => 'Vrijwilliger', 'system_type' => 'VOLUNTEER', 'color' => '#4CAF50', 'icon' => 'tabler-heart-handshake'],
|
|
['name' => 'Medewerker', 'system_type' => 'CREW', 'color' => '#2196F3', 'icon' => 'tabler-id-badge-2'],
|
|
['name' => 'Artiest', 'system_type' => 'ARTIST', 'color' => '#9C27B0', 'icon' => 'tabler-microphone-2'],
|
|
['name' => 'Pers', 'system_type' => 'PRESS', 'color' => '#FF9800', 'icon' => 'tabler-camera'],
|
|
['name' => 'Gast', 'system_type' => 'GUEST', 'color' => '#607D8B', 'icon' => 'tabler-ticket'],
|
|
['name' => 'Leverancier', 'system_type' => 'SUPPLIER', 'color' => '#795548', 'icon' => 'tabler-truck-delivery'],
|
|
['name' => 'Partner', 'system_type' => 'PARTNER', 'color' => '#FFC107', 'icon' => 'tabler-handshake'],
|
|
];
|
|
|
|
foreach ($crowdTypesData as $data) {
|
|
$ct = CrowdType::create(['organisation_id' => $this->org->id, ...$data, 'is_active' => true]);
|
|
$this->crowdTypes[$data['system_type']] = $ct;
|
|
}
|
|
|
|
// ── Person Tags (10) ──
|
|
|
|
$personTagsData = [
|
|
['name' => 'Tapper', 'category' => 'Horeca', 'icon' => 'tabler-beer', 'color' => '#FF9800', 'is_active' => true, 'sort_order' => 1],
|
|
['name' => 'Barista', 'category' => 'Horeca', 'icon' => 'tabler-coffee', 'color' => '#795548', 'is_active' => true, 'sort_order' => 2],
|
|
['name' => 'EHBO', 'category' => 'Veiligheid', 'icon' => 'tabler-first-aid-kit', 'color' => '#F44336', 'is_active' => true, 'sort_order' => 3],
|
|
['name' => 'BHV', 'category' => 'Veiligheid', 'icon' => 'tabler-fire-extinguisher', 'color' => '#E91E63', 'is_active' => true, 'sort_order' => 4],
|
|
['name' => 'Rijbewijs B', 'category' => 'Logistiek', 'icon' => 'tabler-car', 'color' => '#607D8B', 'is_active' => true, 'sort_order' => 5],
|
|
['name' => 'Heftruck', 'category' => 'Logistiek', 'icon' => 'tabler-forklift', 'color' => '#9E9E9E', 'is_active' => true, 'sort_order' => 6],
|
|
['name' => 'Duits', 'category' => 'Taal', 'icon' => 'tabler-flag', 'color' => '#000000', 'is_active' => true, 'sort_order' => 7],
|
|
['name' => 'Engels', 'category' => 'Taal', 'icon' => 'tabler-flag', 'color' => '#1565C0', 'is_active' => true, 'sort_order' => 8],
|
|
['name' => 'Podiumervaring', 'category' => 'Techniek', 'icon' => 'tabler-speakerphone', 'color' => '#9C27B0', 'is_active' => true, 'sort_order' => 9],
|
|
['name' => 'Teamleider', 'category' => 'Rol', 'icon' => 'tabler-crown', 'color' => '#FFC107', 'is_active' => false, 'sort_order' => 10],
|
|
];
|
|
|
|
foreach ($personTagsData as $data) {
|
|
$tag = $this->org->personTags()->create($data);
|
|
$this->personTags[$data['name']] = $tag;
|
|
}
|
|
|
|
$this->command->info(' Organisation, 8 users, 6 companies, 7 crowd types, 10 person tags created');
|
|
});
|
|
}
|
|
|
|
// =========================================================================
|
|
// Event 1: Echt Feesten 2026 (festival, registration_open, 150 persons)
|
|
// =========================================================================
|
|
|
|
private function seedEchtFeesten(): void
|
|
{
|
|
DB::transaction(function (): void {
|
|
$this->command->info('Seeding Echt Feesten 2026...');
|
|
|
|
// ── Event hierarchy ──
|
|
|
|
$festival = Event::create([
|
|
'organisation_id' => $this->org->id,
|
|
'name' => 'Echt Feesten 2026',
|
|
'slug' => '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',
|
|
'registration_welcome_text' => 'Wij zoeken enthousiaste vrijwilligers voor Echt Feesten 2026! Word onderdeel van ons team en beleef het festival van de andere kant. Gratis toegang, maaltijden, en een onvergetelijke ervaring.',
|
|
]);
|
|
|
|
$vrijdag = Event::create([
|
|
'organisation_id' => $this->org->id,
|
|
'parent_event_id' => $festival->id,
|
|
'name' => 'Vrijdag',
|
|
'slug' => 'echt-feesten-2026-vrijdag',
|
|
'start_date' => '2026-07-10',
|
|
'end_date' => '2026-07-10',
|
|
'timezone' => 'Europe/Amsterdam',
|
|
'status' => 'registration_open',
|
|
'event_type' => 'event',
|
|
]);
|
|
|
|
$zaterdag = Event::create([
|
|
'organisation_id' => $this->org->id,
|
|
'parent_event_id' => $festival->id,
|
|
'name' => 'Zaterdag',
|
|
'slug' => 'echt-feesten-2026-zaterdag',
|
|
'start_date' => '2026-07-11',
|
|
'end_date' => '2026-07-11',
|
|
'timezone' => 'Europe/Amsterdam',
|
|
'status' => 'registration_open',
|
|
'event_type' => 'event',
|
|
]);
|
|
|
|
$zondag = Event::create([
|
|
'organisation_id' => $this->org->id,
|
|
'parent_event_id' => $festival->id,
|
|
'name' => 'Zondag',
|
|
'slug' => 'echt-feesten-2026-zondag',
|
|
'start_date' => '2026-07-12',
|
|
'end_date' => '2026-07-12',
|
|
'timezone' => 'Europe/Amsterdam',
|
|
'status' => 'published',
|
|
'event_type' => 'event',
|
|
]);
|
|
|
|
$subEvents = ['vrijdag' => $vrijdag, 'zaterdag' => $zaterdag, 'zondag' => $zondag];
|
|
|
|
// Attach event-level user roles
|
|
$eventRoles = [
|
|
'lisa@feestfabriek.nl' => 'event_manager',
|
|
'ahmed@feestfabriek.nl' => 'event_manager',
|
|
'sara@feestfabriek.nl' => 'volunteer_coordinator',
|
|
'tom@feestfabriek.nl' => 'staff_coordinator',
|
|
'nina@feestfabriek.nl' => 'artist_manager',
|
|
];
|
|
foreach ($eventRoles as $email => $role) {
|
|
$festival->users()->attach($this->users[$email], ['role' => $role]);
|
|
}
|
|
|
|
// ── Locations (5, on parent) ──
|
|
|
|
$locations = [];
|
|
$locationsData = [
|
|
'hoofdpodium' => ['name' => 'Hoofdpodium', 'address' => 'Parkweg 1, Echt', 'lat' => 51.1039, 'lng' => 5.8718],
|
|
'theatertent' => ['name' => 'Theatertent', 'address' => 'Parkweg 1A, Echt', 'lat' => 51.1041, 'lng' => 5.8725],
|
|
'ingang' => ['name' => 'Festivalterrein Ingang', 'address' => 'Parkweg 2, Echt', 'lat' => 51.1035, 'lng' => 5.8710],
|
|
'backstage' => ['name' => 'Backstage Area', 'address' => 'Parkweg 1B, Echt', 'lat' => 51.1043, 'lng' => 5.8720],
|
|
'camping' => ['name' => 'Camping', 'address' => 'Kampeerweg 5, Echt', 'lat' => 51.1050, 'lng' => 5.8740],
|
|
];
|
|
foreach ($locationsData as $key => $data) {
|
|
$locations[$key] = Location::create(['event_id' => $festival->id, ...$data]);
|
|
}
|
|
|
|
// ── Festival-level sections (4) ──
|
|
|
|
$ehbo = FestivalSection::create([
|
|
'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,
|
|
'show_in_registration' => false,
|
|
]);
|
|
|
|
$nachtsecurity = FestivalSection::create([
|
|
'event_id' => $festival->id, 'name' => 'Nachtsecurity', 'type' => 'standard',
|
|
'category' => 'Veiligheid', 'icon' => 'tabler-shield', 'sort_order' => 2,
|
|
'responder_self_checkin' => true, 'crew_auto_accepts' => false,
|
|
'show_in_registration' => false,
|
|
]);
|
|
|
|
$terreinploeg = FestivalSection::create([
|
|
'event_id' => $festival->id, 'name' => 'Terreinploeg', 'type' => 'standard',
|
|
'category' => 'Productie', 'icon' => 'tabler-shovel', 'sort_order' => 3,
|
|
'responder_self_checkin' => true, 'crew_auto_accepts' => true,
|
|
'show_in_registration' => false,
|
|
]);
|
|
|
|
$accreditatiebalie = FestivalSection::create([
|
|
'event_id' => $festival->id, 'name' => 'Accreditatiebalie', 'type' => 'cross_event',
|
|
'category' => 'Ontvangst', 'icon' => 'tabler-id-badge', 'sort_order' => 4,
|
|
'responder_self_checkin' => true, 'crew_auto_accepts' => true,
|
|
'show_in_registration' => false,
|
|
]);
|
|
|
|
// ── Sub-event sections (5 per sub-event) ──
|
|
|
|
$sectionDefs = [
|
|
'hoofdbar' => ['name' => 'Hoofdpodium Bar', 'category' => 'Bar', 'icon' => 'tabler-beer', 'crew_auto_accepts' => true, 'show_in_registration' => true, 'registration_description' => 'Tap bier en drankjes voor festivalgangers bij het hoofdpodium'],
|
|
'theaterbar' => ['name' => 'Theatertent Bar', 'category' => 'Bar', 'icon' => 'tabler-beer', 'crew_auto_accepts' => true, 'show_in_registration' => true, 'registration_description' => 'Bediening in de overdekte theatertent'],
|
|
'hospitality' => ['name' => 'Backstage Hospitality', 'category' => 'Hospitality', 'icon' => 'tabler-armchair', 'crew_auto_accepts' => false, 'show_in_registration' => true, 'registration_description' => 'Ontvang en begeleid artiesten en gasten backstage'],
|
|
'podiumtechniek' => ['name' => 'Podiumtechniek', 'category' => 'Techniek', 'icon' => 'tabler-speakerphone', 'crew_auto_accepts' => false, 'show_in_registration' => true, 'registration_description' => 'Help met geluid- en lichttechniek bij de podia'],
|
|
'ingang' => ['name' => 'Ingang & Tickets', 'category' => 'Ontvangst', 'icon' => 'tabler-ticket', 'crew_auto_accepts' => true, 'show_in_registration' => true, 'registration_description' => 'Verwelkom bezoekers en scan tickets bij de ingang'],
|
|
];
|
|
|
|
$sections = [];
|
|
foreach ($subEvents as $dayKey => $subEvent) {
|
|
$order = 1;
|
|
foreach ($sectionDefs as $key => $def) {
|
|
$sections["{$dayKey}_{$key}"] = FestivalSection::create([
|
|
'event_id' => $subEvent->id,
|
|
'name' => $def['name'],
|
|
'type' => 'standard',
|
|
'category' => $def['category'],
|
|
'icon' => $def['icon'],
|
|
'sort_order' => $order++,
|
|
'responder_self_checkin' => true,
|
|
'crew_auto_accepts' => $def['crew_auto_accepts'],
|
|
'show_in_registration' => $def['show_in_registration'] ?? false,
|
|
'registration_description' => $def['registration_description'] ?? null,
|
|
]);
|
|
}
|
|
}
|
|
|
|
// ── Festival-level time slots (5) ──
|
|
|
|
$fSlots = [];
|
|
$fSlots['opbouw1'] = TimeSlot::create(['event_id' => $festival->id, 'name' => 'Opbouw Dag 1', 'person_type' => 'CREW', 'date' => '2026-07-09', 'start_time' => '08:00', 'end_time' => '18:00', 'duration_hours' => 10.00]);
|
|
$fSlots['opbouw2'] = TimeSlot::create(['event_id' => $festival->id, 'name' => 'Opbouw Dag 2', 'person_type' => 'CREW', 'date' => '2026-07-10', 'start_time' => '08:00', 'end_time' => '14:00', 'duration_hours' => 6.00]);
|
|
$fSlots['nacht_vr'] = TimeSlot::create(['event_id' => $festival->id, 'name' => 'Nachtbewaking Vr→Za', 'person_type' => 'CREW', 'date' => '2026-07-10', 'start_time' => '02:00', 'end_time' => '08:00', 'duration_hours' => 6.00]);
|
|
$fSlots['nacht_za'] = TimeSlot::create(['event_id' => $festival->id, 'name' => 'Nachtbewaking Za→Zo', 'person_type' => 'CREW', 'date' => '2026-07-11', 'start_time' => '02:00', 'end_time' => '08:00', 'duration_hours' => 6.00]);
|
|
$fSlots['afbraak'] = TimeSlot::create(['event_id' => $festival->id, 'name' => 'Afbraak', 'person_type' => 'CREW', 'date' => '2026-07-13', 'start_time' => '08:00', 'end_time' => '18:00', 'duration_hours' => 10.00]);
|
|
|
|
// ── Sub-event time slots ──
|
|
|
|
$ts = [];
|
|
$ts['vr_early'] = TimeSlot::create(['event_id' => $vrijdag->id, 'name' => 'Vrijdag Early', 'person_type' => 'VOLUNTEER', 'date' => '2026-07-10', 'start_time' => '14:00', 'end_time' => '18:00', 'duration_hours' => 4.00]);
|
|
$ts['vr_avond'] = TimeSlot::create(['event_id' => $vrijdag->id, 'name' => 'Vrijdag Avond', 'person_type' => 'VOLUNTEER', 'date' => '2026-07-10', 'start_time' => '18:00', 'end_time' => '02:00', 'duration_hours' => 8.00]);
|
|
$ts['vr_pers'] = TimeSlot::create(['event_id' => $vrijdag->id, 'name' => 'Vrijdag Pers', 'person_type' => 'PRESS', 'date' => '2026-07-10', 'start_time' => '16:00', 'end_time' => '22:00', 'duration_hours' => 6.00]);
|
|
$ts['za_dag'] = TimeSlot::create(['event_id' => $zaterdag->id, 'name' => 'Zaterdag Dag', 'person_type' => 'VOLUNTEER', 'date' => '2026-07-11', 'start_time' => '10:00', 'end_time' => '18:00', 'duration_hours' => 8.00]);
|
|
$ts['za_avond'] = TimeSlot::create(['event_id' => $zaterdag->id, 'name' => 'Zaterdag Avond', 'person_type' => 'VOLUNTEER', 'date' => '2026-07-11', 'start_time' => '18:00', 'end_time' => '02:00', 'duration_hours' => 8.00]);
|
|
$ts['za_crew'] = TimeSlot::create(['event_id' => $zaterdag->id, 'name' => 'Zaterdag Crew', 'person_type' => 'CREW', 'date' => '2026-07-11', 'start_time' => '08:00', 'end_time' => '20:00', 'duration_hours' => 12.00]);
|
|
$ts['zo_dag'] = TimeSlot::create(['event_id' => $zondag->id, 'name' => 'Zondag Dag', 'person_type' => 'VOLUNTEER', 'date' => '2026-07-12', 'start_time' => '10:00', 'end_time' => '18:00', 'duration_hours' => 8.00]);
|
|
$ts['zo_avond'] = TimeSlot::create(['event_id' => $zondag->id, 'name' => 'Zondag Avond', 'person_type' => 'VOLUNTEER', 'date' => '2026-07-12', 'start_time' => '18:00', 'end_time' => '22:00', 'duration_hours' => 4.00]);
|
|
|
|
// ── Shifts (~55) ──
|
|
|
|
$allShifts = [];
|
|
$s = []; // named shifts for explicit assignments
|
|
|
|
// EHBO: 6 shifts across volunteer time slots
|
|
foreach (['vr_early', 'vr_avond', 'za_dag', 'za_avond', 'zo_dag', 'zo_avond'] as $key) {
|
|
$shift = Shift::create([
|
|
'festival_section_id' => $ehbo->id,
|
|
'time_slot_id' => $ts[$key]->id,
|
|
'title' => 'EHBO Post',
|
|
'slots_total' => $key === 'za_dag' ? 4 : 3,
|
|
'slots_open_for_claiming' => $key === 'za_dag' ? 0 : 2,
|
|
'status' => 'open',
|
|
]);
|
|
$allShifts[] = $shift;
|
|
$s["ehbo_{$key}"] = $shift;
|
|
}
|
|
|
|
// Nachtsecurity: 2 shifts
|
|
foreach (['nacht_vr', 'nacht_za'] as $key) {
|
|
$shift = Shift::create([
|
|
'festival_section_id' => $nachtsecurity->id,
|
|
'time_slot_id' => $fSlots[$key]->id,
|
|
'title' => 'Nachtbewaker',
|
|
'slots_total' => 6,
|
|
'slots_open_for_claiming' => 0,
|
|
'status' => 'open',
|
|
]);
|
|
$allShifts[] = $shift;
|
|
$s["nacht_{$key}"] = $shift;
|
|
}
|
|
|
|
// Terreinploeg: 3 shifts
|
|
foreach (['opbouw1', 'opbouw2', 'afbraak'] as $key) {
|
|
$shift = Shift::create([
|
|
'festival_section_id' => $terreinploeg->id,
|
|
'time_slot_id' => $fSlots[$key]->id,
|
|
'title' => 'Terreinmedewerker',
|
|
'slots_total' => $key === 'afbraak' ? 20 : 15,
|
|
'slots_open_for_claiming' => $key === 'afbraak' ? 16 : 12,
|
|
'status' => $key === 'afbraak' ? 'draft' : 'open',
|
|
]);
|
|
$allShifts[] = $shift;
|
|
$s["terrein_{$key}"] = $shift;
|
|
}
|
|
|
|
// Accreditatiebalie: 4 shifts
|
|
foreach (['vr_early', 'za_dag', 'zo_dag', 'za_avond'] as $key) {
|
|
$shift = Shift::create([
|
|
'festival_section_id' => $accreditatiebalie->id,
|
|
'time_slot_id' => $ts[$key]->id,
|
|
'title' => 'Accreditatiemedewerker',
|
|
'slots_total' => 3,
|
|
'slots_open_for_claiming' => 2,
|
|
'status' => 'open',
|
|
]);
|
|
$allShifts[] = $shift;
|
|
$s["accred_{$key}"] = $shift;
|
|
}
|
|
|
|
// Sub-event shifts
|
|
$slotMap = [
|
|
'vrijdag' => ['vr_early', 'vr_avond'],
|
|
'zaterdag' => ['za_dag', 'za_avond'],
|
|
'zondag' => ['zo_dag', 'zo_avond'],
|
|
];
|
|
|
|
$locationMap = [
|
|
'hoofdbar' => 'hoofdpodium',
|
|
'theaterbar' => 'theatertent',
|
|
'hospitality' => 'backstage',
|
|
'podiumtechniek' => 'hoofdpodium',
|
|
'ingang' => 'ingang',
|
|
];
|
|
|
|
$titleMap = [
|
|
'hoofdbar' => 'Tapper',
|
|
'theaterbar' => 'Tapper',
|
|
'hospitality' => 'Hospitality',
|
|
'podiumtechniek' => 'Stagehand',
|
|
'ingang' => 'Ticketcontrole',
|
|
];
|
|
|
|
foreach ($slotMap as $dayKey => $slotKeys) {
|
|
foreach (array_keys($sectionDefs) as $secKey) {
|
|
foreach ($slotKeys as $slotKey) {
|
|
$sectionModel = $sections["{$dayKey}_{$secKey}"];
|
|
|
|
$slotsTotal = match ($secKey) {
|
|
'hoofdbar', 'theaterbar' => rand(8, 12),
|
|
'ingang' => rand(6, 8),
|
|
default => rand(3, 4),
|
|
};
|
|
$slotsOpen = match ($secKey) {
|
|
'hoofdbar', 'theaterbar' => rand(6, 10),
|
|
'ingang' => rand(4, 6),
|
|
default => 0,
|
|
};
|
|
|
|
$isDraft = $dayKey === 'zondag' && $slotKey === 'zo_avond';
|
|
|
|
$shift = Shift::create([
|
|
'festival_section_id' => $sectionModel->id,
|
|
'time_slot_id' => $ts[$slotKey]->id,
|
|
'location_id' => $locations[$locationMap[$secKey]]->id,
|
|
'title' => $titleMap[$secKey],
|
|
'slots_total' => $slotsTotal,
|
|
'slots_open_for_claiming' => $slotsOpen,
|
|
'status' => $isDraft ? 'draft' : 'open',
|
|
'allow_overlap' => $secKey === 'podiumtechniek',
|
|
]);
|
|
|
|
$allShifts[] = $shift;
|
|
$s["{$secKey}_{$slotKey}"] = $shift;
|
|
}
|
|
}
|
|
}
|
|
|
|
$shiftCount = count($allShifts);
|
|
$this->command->info(" {$shiftCount} shifts created");
|
|
|
|
// ── Named persons (30) ──
|
|
|
|
$vol = $this->crowdTypes['VOLUNTEER']->id;
|
|
$crew = $this->crowdTypes['CREW']->id;
|
|
$press = $this->crowdTypes['PRESS']->id;
|
|
$guest = $this->crowdTypes['GUEST']->id;
|
|
$supplier = $this->crowdTypes['SUPPLIER']->id;
|
|
|
|
// Create user accounts for volunteers who need user_id links
|
|
$volunteerUsers = [];
|
|
foreach ([
|
|
['first_name' => 'Jan', 'last_name' => 'de Vries', 'email' => 'jan@gmail.com'],
|
|
['first_name' => 'Ahmed', 'last_name' => 'Hassan', 'email' => 'ahmed.h@gmail.com'],
|
|
['first_name' => 'Tom', 'last_name' => 'Visser', 'email' => 'tom.visser@gmail.com'],
|
|
['first_name' => 'Lotte', 'last_name' => 'de Jong', 'email' => 'lotte@gmail.com'],
|
|
['first_name' => 'Pieter', 'last_name' => 'Geluid', 'email' => 'pieter@podiumtechniek.nl'],
|
|
] as $u) {
|
|
$volunteerUsers[$u['email']] = User::create([
|
|
'first_name' => $u['first_name'],
|
|
'last_name' => $u['last_name'],
|
|
'email' => $u['email'],
|
|
'password' => Hash::make('password'),
|
|
]);
|
|
}
|
|
|
|
// 15 named volunteers
|
|
$jan = Person::create(['event_id' => $festival->id, 'crowd_type_id' => $vol, 'user_id' => $volunteerUsers['jan@gmail.com']->id, 'first_name' => 'Jan', 'last_name' => 'de Vries', 'email' => 'jan@gmail.com', 'phone' => '+31612345001', 'status' => 'approved', 'date_of_birth' => '1995-03-15']);
|
|
$lisaB = Person::create(['event_id' => $festival->id, 'crowd_type_id' => $vol, 'first_name' => 'Lisa', 'last_name' => 'Bakker', 'email' => 'lisa.bakker@hotmail.com', 'phone' => '+31612345002', 'status' => 'approved', 'date_of_birth' => '1998-07-22']);
|
|
$ahmedP = Person::create(['event_id' => $festival->id, 'crowd_type_id' => $vol, 'user_id' => $volunteerUsers['ahmed.h@gmail.com']->id, 'first_name' => 'Ahmed', 'last_name' => 'Hassan', 'email' => 'ahmed.h@gmail.com', 'phone' => '+31612345003', 'status' => 'approved', 'date_of_birth' => '1992-11-08']);
|
|
$saraJ = Person::create(['event_id' => $festival->id, 'crowd_type_id' => $vol, 'first_name' => 'Sara', 'last_name' => 'Jansen', 'email' => 'sara.j@outlook.com', 'phone' => '+31612345004', 'status' => 'approved', 'date_of_birth' => '2000-01-30']);
|
|
$tomV = Person::create(['event_id' => $festival->id, 'crowd_type_id' => $vol, 'user_id' => $volunteerUsers['tom.visser@gmail.com']->id, 'first_name' => 'Tom', 'last_name' => 'Visser', 'email' => 'tom.visser@gmail.com', 'phone' => '+31612345005', 'status' => 'approved', 'date_of_birth' => '1997-06-14']);
|
|
$fatima = Person::create(['event_id' => $festival->id, 'crowd_type_id' => $vol, 'first_name' => 'Fatima', 'last_name' => 'El Amrani', 'email' => 'fatima@gmail.com', 'phone' => '+31612345006', 'status' => 'approved', 'date_of_birth' => '1996-05-20']);
|
|
$daan = Person::create(['event_id' => $festival->id, 'crowd_type_id' => $vol, 'first_name' => 'Daan', 'last_name' => 'Smit', 'email' => 'daan.smit@gmail.com', 'phone' => '+31612345007', 'status' => 'pending', 'date_of_birth' => '1993-08-11']);
|
|
Person::create(['event_id' => $festival->id, 'crowd_type_id' => $vol, 'first_name' => 'Sophie', 'last_name' => 'Mulder', 'email' => 'sophie.m@hotmail.com', 'phone' => '+31612345008', 'status' => 'pending', 'date_of_birth' => '2001-02-28']);
|
|
Person::create(['event_id' => $festival->id, 'crowd_type_id' => $vol, 'first_name' => 'Jesse', 'last_name' => 'van Dijk', 'email' => 'jesse@gmail.com', 'phone' => '+31612345009', 'status' => 'applied', 'date_of_birth' => '1999-10-05']);
|
|
Person::create(['event_id' => $festival->id, 'crowd_type_id' => $vol, 'first_name' => 'Noa', 'last_name' => 'Hendriks', 'email' => 'noa.h@outlook.com', 'phone' => '+31612345010', 'status' => 'applied', 'date_of_birth' => '2000-07-19']);
|
|
Person::create(['event_id' => $festival->id, 'crowd_type_id' => $vol, 'first_name' => 'Kevin', 'last_name' => 'Bos', 'email' => 'kevin.bos@gmail.com', 'phone' => '+31612345011', 'status' => 'rejected', 'admin_notes' => 'Vorig jaar no-show', 'date_of_birth' => '1994-12-01']);
|
|
Person::create(['event_id' => $festival->id, 'crowd_type_id' => $vol, 'first_name' => 'Priya', 'last_name' => 'Sharma', 'email' => 'priya@gmail.com', 'phone' => '+31612345012', 'status' => 'invited', 'date_of_birth' => '1998-03-27']);
|
|
$lotte = Person::create(['event_id' => $festival->id, 'crowd_type_id' => $vol, 'user_id' => $volunteerUsers['lotte@gmail.com']->id, 'first_name' => 'Lotte', 'last_name' => 'de Jong', 'email' => 'lotte@gmail.com', 'phone' => '+31612345013', 'status' => 'approved', 'date_of_birth' => '1994-09-25']);
|
|
$robin = Person::create(['event_id' => $festival->id, 'crowd_type_id' => $vol, 'first_name' => 'Robin', 'last_name' => 'Peters', 'email' => 'robin.p@hotmail.com', 'phone' => '+31612345014', 'status' => 'approved', 'date_of_birth' => '1991-12-03']);
|
|
$emma = Person::create(['event_id' => $festival->id, 'crowd_type_id' => $vol, 'first_name' => 'Emma', 'last_name' => 'Willems', 'email' => 'emma.w@gmail.com', 'phone' => '+31612345015', 'status' => 'no_show', 'is_blacklisted' => true, 'date_of_birth' => '1999-04-17']);
|
|
|
|
// 6 named crew
|
|
$klaas = Person::create(['event_id' => $festival->id, 'crowd_type_id' => $crew, 'company_id' => $this->companies['SecureEvent BV']->id, 'first_name' => 'Klaas', 'last_name' => 'Veilig Jr.', 'email' => 'klaas.jr@secureevent.nl', 'phone' => '+31612345016', 'status' => 'approved']);
|
|
$dennis = Person::create(['event_id' => $festival->id, 'crowd_type_id' => $crew, 'company_id' => $this->companies['SecureEvent BV']->id, 'first_name' => 'Dennis', 'last_name' => 'Schild', 'email' => 'dennis@secureevent.nl', 'phone' => '+31612345017', 'status' => 'approved']);
|
|
$pieter = Person::create(['event_id' => $festival->id, 'crowd_type_id' => $crew, 'company_id' => $this->companies['Podiumtechniek Rijnmond']->id, 'user_id' => $volunteerUsers['pieter@podiumtechniek.nl']->id, 'first_name' => 'Pieter', 'last_name' => 'Geluid', 'email' => 'pieter@podiumtechniek.nl', 'phone' => '+31612345018', 'status' => 'approved']);
|
|
Person::create(['event_id' => $festival->id, 'crowd_type_id' => $crew, 'company_id' => $this->companies['Podiumtechniek Rijnmond']->id, 'first_name' => 'Marco', 'last_name' => 'Licht', 'email' => 'marco@podiumtechniek.nl', 'phone' => '+31612345019', 'status' => 'approved']);
|
|
$eva = Person::create(['event_id' => $festival->id, 'crowd_type_id' => $crew, 'company_id' => $this->companies['Brouwerij De Schelde']->id, 'first_name' => 'Eva', 'last_name' => 'Brouwer', 'email' => 'eva@brouwerijdeschelde.nl', 'phone' => '+31612345020', 'status' => 'approved']);
|
|
$maria = Person::create(['event_id' => $festival->id, 'crowd_type_id' => $crew, 'company_id' => $this->companies['Van Dijk Catering']->id, 'first_name' => 'Maria', 'last_name' => 'van Dijk', 'email' => 'maria@vandijkcatering.nl', 'phone' => '+31612345021', 'status' => 'approved']);
|
|
|
|
// 3 named press
|
|
Person::create(['event_id' => $festival->id, 'crowd_type_id' => $press, 'first_name' => 'Joris', 'last_name' => 'van Laar', 'email' => 'joris@pers.nl', 'phone' => '+31612345022', 'status' => 'approved']);
|
|
Person::create(['event_id' => $festival->id, 'crowd_type_id' => $press, 'first_name' => 'Tamara', 'last_name' => 'Smeets', 'email' => 'tamara@pers.nl', 'phone' => '+31612345023', 'status' => 'approved']);
|
|
Person::create(['event_id' => $festival->id, 'crowd_type_id' => $press, 'first_name' => 'Sven', 'last_name' => 'Pieterse', 'email' => 'sven@pers.nl', 'phone' => '+31612345024', 'status' => 'pending']);
|
|
|
|
// 4 named guests
|
|
Person::create(['event_id' => $festival->id, 'crowd_type_id' => $guest, 'first_name' => 'Jan', 'last_name' => 'Slagter', 'email' => 'burgemeester@echt.nl', 'phone' => '+31612345025', 'status' => 'approved']);
|
|
Person::create(['event_id' => $festival->id, 'crowd_type_id' => $guest, 'first_name' => 'Petra', 'last_name' => 'Kamps', 'email' => 'wethouder@echt.nl', 'phone' => '+31612345026', 'status' => 'approved']);
|
|
Person::create(['event_id' => $festival->id, 'crowd_type_id' => $guest, 'first_name' => 'Jan', 'last_name' => 'Sponsor', 'email' => 'sponsor@bedrijf.nl', 'phone' => '+31612345027', 'status' => 'approved']);
|
|
Person::create(['event_id' => $festival->id, 'crowd_type_id' => $guest, 'first_name' => 'Gast', 'last_name' => 'van Artiest', 'email' => 'artiestgast@gmail.com', 'phone' => '+31612345028', 'status' => 'invited']);
|
|
|
|
// 2 named suppliers
|
|
Person::create(['event_id' => $festival->id, 'crowd_type_id' => $supplier, 'company_id' => $this->companies['Tap & Co Horeca']->id, 'first_name' => 'Hans', 'last_name' => 'Tapper', 'email' => 'hans@tapco.nl', 'phone' => '+31612345029', 'status' => 'approved']);
|
|
Person::create(['event_id' => $festival->id, 'crowd_type_id' => $supplier, 'company_id' => $this->companies['Van Dijk Catering']->id, 'first_name' => 'Frank', 'last_name' => 'van Dijk', 'email' => 'frank@vandijkcatering.nl', 'phone' => '+31612345030', 'status' => 'pending']);
|
|
|
|
// ── Factory persons (120) ──
|
|
|
|
$companyIds = collect($this->companies)->pluck('id');
|
|
|
|
// 80 factory volunteers
|
|
$factoryVolunteers = collect();
|
|
$factoryVolunteers = $factoryVolunteers->merge(Person::factory()->count(55)->approved()->create(['event_id' => $festival->id, 'crowd_type_id' => $vol]));
|
|
$factoryVolunteers = $factoryVolunteers->merge(Person::factory()->count(10)->create(['event_id' => $festival->id, 'crowd_type_id' => $vol, 'status' => 'pending']));
|
|
$factoryVolunteers = $factoryVolunteers->merge(Person::factory()->count(8)->create(['event_id' => $festival->id, 'crowd_type_id' => $vol, 'status' => 'applied']));
|
|
$factoryVolunteers = $factoryVolunteers->merge(Person::factory()->count(4)->create(['event_id' => $festival->id, 'crowd_type_id' => $vol, 'status' => 'invited']));
|
|
$factoryVolunteers = $factoryVolunteers->merge(Person::factory()->count(2)->create(['event_id' => $festival->id, 'crowd_type_id' => $vol, 'status' => 'rejected']));
|
|
$factoryVolunteers = $factoryVolunteers->merge(Person::factory()->count(1)->create(['event_id' => $festival->id, 'crowd_type_id' => $vol, 'status' => 'no_show']));
|
|
|
|
// 19 factory crew
|
|
Person::factory()->count(12)->approved()
|
|
->sequence(fn () => ['company_id' => $companyIds->random()])
|
|
->create(['event_id' => $festival->id, 'crowd_type_id' => $crew]);
|
|
Person::factory()->count(5)->approved()->create(['event_id' => $festival->id, 'crowd_type_id' => $crew]);
|
|
Person::factory()->count(2)->create(['event_id' => $festival->id, 'crowd_type_id' => $crew, 'status' => 'pending']);
|
|
|
|
// 7 factory press
|
|
Person::factory()->count(5)->approved()->create(['event_id' => $festival->id, 'crowd_type_id' => $press]);
|
|
Person::factory()->count(2)->create(['event_id' => $festival->id, 'crowd_type_id' => $press, 'status' => 'pending']);
|
|
|
|
// 8 factory guests
|
|
Person::factory()->count(6)->approved()->create(['event_id' => $festival->id, 'crowd_type_id' => $guest]);
|
|
Person::factory()->count(2)->create(['event_id' => $festival->id, 'crowd_type_id' => $guest, 'status' => 'invited']);
|
|
|
|
// 6 factory suppliers
|
|
Person::factory()->count(4)->approved()->create(['event_id' => $festival->id, 'crowd_type_id' => $supplier]);
|
|
Person::factory()->count(2)->create(['event_id' => $festival->id, 'crowd_type_id' => $supplier, 'status' => 'pending']);
|
|
|
|
$personCount = Person::where('event_id', $festival->id)->count();
|
|
$this->command->info(" {$personCount} persons created");
|
|
|
|
// ── Named shift assignments (22) ──
|
|
|
|
$tom = $this->users['tom@feestfabriek.nl'];
|
|
|
|
$namedAssignments = [
|
|
// Jan de Vries: 3 bar shifts
|
|
['person' => $jan, 'shift' => 'hoofdbar_vr_early', 'status' => ShiftAssignmentStatus::APPROVED, 'auto' => true],
|
|
['person' => $jan, 'shift' => 'hoofdbar_vr_avond', 'status' => ShiftAssignmentStatus::APPROVED, 'auto' => false],
|
|
['person' => $jan, 'shift' => 'hoofdbar_za_dag', 'status' => ShiftAssignmentStatus::APPROVED],
|
|
// Lisa Bakker
|
|
['person' => $lisaB, 'shift' => 'hoofdbar_vr_early', 'status' => ShiftAssignmentStatus::APPROVED, 'auto' => true],
|
|
['person' => $lisaB, 'shift' => 'hoofdbar_za_dag', 'status' => ShiftAssignmentStatus::PENDING_APPROVAL],
|
|
['person' => $lisaB, 'shift' => 'theaterbar_zo_dag', 'status' => ShiftAssignmentStatus::APPROVED, 'auto' => true],
|
|
// Ahmed Hassan: 4 EHBO shifts
|
|
['person' => $ahmedP, 'shift' => 'ehbo_vr_early', 'status' => ShiftAssignmentStatus::APPROVED],
|
|
['person' => $ahmedP, 'shift' => 'ehbo_vr_avond', 'status' => ShiftAssignmentStatus::APPROVED],
|
|
['person' => $ahmedP, 'shift' => 'ehbo_za_dag', 'status' => ShiftAssignmentStatus::APPROVED],
|
|
['person' => $ahmedP, 'shift' => 'ehbo_za_avond', 'status' => ShiftAssignmentStatus::APPROVED],
|
|
// Sara Jansen
|
|
['person' => $saraJ, 'shift' => 'theaterbar_vr_avond', 'status' => ShiftAssignmentStatus::PENDING_APPROVAL],
|
|
// Tom Visser (volunteer)
|
|
['person' => $tomV, 'shift' => 'podiumtechniek_vr_avond', 'status' => ShiftAssignmentStatus::APPROVED],
|
|
// Fatima
|
|
['person' => $fatima, 'shift' => 'hoofdbar_za_avond', 'status' => ShiftAssignmentStatus::APPROVED],
|
|
// Lotte: terreinploeg
|
|
['person' => $lotte, 'shift' => 'terrein_opbouw1', 'status' => ShiftAssignmentStatus::APPROVED, 'auto' => true],
|
|
['person' => $lotte, 'shift' => 'terrein_afbraak', 'status' => ShiftAssignmentStatus::APPROVED, 'auto' => true],
|
|
// Robin
|
|
['person' => $robin, 'shift' => 'theaterbar_za_dag', 'status' => ShiftAssignmentStatus::APPROVED, 'auto' => true],
|
|
// Klaas: nachtsecurity, assigned by Tom
|
|
['person' => $klaas, 'shift' => 'nacht_nacht_vr', 'status' => ShiftAssignmentStatus::APPROVED, 'assigned_by' => $tom->id],
|
|
// Dennis: nachtsecurity
|
|
['person' => $dennis, 'shift' => 'nacht_nacht_za', 'status' => ShiftAssignmentStatus::APPROVED, 'assigned_by' => $tom->id],
|
|
// Daan: rejected
|
|
['person' => $daan, 'shift' => 'hoofdbar_vr_early', 'status' => ShiftAssignmentStatus::REJECTED, 'reason' => 'Nog niet goedgekeurd'],
|
|
// Emma: cancelled
|
|
['person' => $emma, 'shift' => 'ingang_vr_early', 'status' => ShiftAssignmentStatus::CANCELLED],
|
|
// Pieter: podiumtechniek with overlap
|
|
['person' => $pieter, 'shift' => 'podiumtechniek_vr_avond', 'status' => ShiftAssignmentStatus::APPROVED],
|
|
['person' => $pieter, 'shift' => 'podiumtechniek_za_avond', 'status' => ShiftAssignmentStatus::APPROVED],
|
|
];
|
|
|
|
$usedPersonSlots = []; // track person_id => [time_slot_ids] for factory assignments
|
|
|
|
foreach ($namedAssignments as $a) {
|
|
$shift = $s[$a['shift']];
|
|
$isApproved = $a['status'] === ShiftAssignmentStatus::APPROVED;
|
|
ShiftAssignment::create([
|
|
'shift_id' => $shift->id,
|
|
'person_id' => $a['person']->id,
|
|
'time_slot_id' => $shift->time_slot_id,
|
|
'status' => $a['status'],
|
|
'auto_approved' => $a['auto'] ?? false,
|
|
'assigned_by' => $a['assigned_by'] ?? null,
|
|
'assigned_at' => now(),
|
|
'approved_at' => $isApproved ? now() : null,
|
|
'rejection_reason' => $a['reason'] ?? null,
|
|
]);
|
|
$usedPersonSlots[$a['person']->id][] = $shift->time_slot_id;
|
|
}
|
|
|
|
// ── Intentional overbooking for UI testing ──
|
|
|
|
$overbookPersons = Person::where('event_id', $festival->id)
|
|
->where('status', 'approved')
|
|
->whereNotIn('id', array_keys($usedPersonSlots))
|
|
->limit(20)
|
|
->get();
|
|
|
|
// EHBO Vrijdag Early: slots_total=3, target 4 approved (1 over)
|
|
// Already has 1 named approved (Ahmed). Add 3 more = 4 total.
|
|
$ehboOverbookTarget = 3;
|
|
$ehboShift = $s['ehbo_vr_early'];
|
|
foreach ($overbookPersons->splice(0, $ehboOverbookTarget) as $person) {
|
|
ShiftAssignment::create([
|
|
'shift_id' => $ehboShift->id,
|
|
'person_id' => $person->id,
|
|
'time_slot_id' => $ehboShift->time_slot_id,
|
|
'status' => ShiftAssignmentStatus::APPROVED,
|
|
'auto_approved' => false,
|
|
'assigned_at' => now(),
|
|
'approved_at' => now(),
|
|
]);
|
|
$usedPersonSlots[$person->id][] = $ehboShift->time_slot_id;
|
|
}
|
|
|
|
// Terreinploeg Opbouw Dag 1: slots_total=15, target 17 approved (2 over)
|
|
// Already has 1 named approved (Lotte). Add 16 more = 17 total.
|
|
$terreinOverbookTarget = 16;
|
|
$terreinShift = $s['terrein_opbouw1'];
|
|
$terreinOverbookPersons = Person::where('event_id', $festival->id)
|
|
->where('status', 'approved')
|
|
->whereNotIn('id', collect($usedPersonSlots)->keys()->filter(
|
|
fn ($pid) => in_array($terreinShift->time_slot_id, $usedPersonSlots[$pid] ?? []),
|
|
)->values())
|
|
->limit($terreinOverbookTarget)
|
|
->get();
|
|
foreach ($terreinOverbookPersons as $person) {
|
|
ShiftAssignment::create([
|
|
'shift_id' => $terreinShift->id,
|
|
'person_id' => $person->id,
|
|
'time_slot_id' => $terreinShift->time_slot_id,
|
|
'status' => ShiftAssignmentStatus::APPROVED,
|
|
'auto_approved' => true,
|
|
'assigned_at' => now(),
|
|
'approved_at' => now(),
|
|
]);
|
|
$usedPersonSlots[$person->id][] = $terreinShift->time_slot_id;
|
|
}
|
|
|
|
// ── Factory shift assignments (~100) ──
|
|
|
|
// Track filled (approved/completed) counts per shift to respect slots_total
|
|
$shiftFilledCounts = [];
|
|
$existingAssignments = ShiftAssignment::whereIn('shift_id', collect($allShifts)->pluck('id'))
|
|
->whereIn('status', [ShiftAssignmentStatus::APPROVED, ShiftAssignmentStatus::COMPLETED])
|
|
->get()
|
|
->groupBy('shift_id');
|
|
foreach ($allShifts as $shift) {
|
|
$shiftFilledCounts[$shift->id] = ($existingAssignments[$shift->id] ?? collect())->count();
|
|
}
|
|
|
|
$approvedPersons = Person::where('event_id', $festival->id)
|
|
->where('status', 'approved')
|
|
->get();
|
|
|
|
$openShifts = collect($allShifts)->filter(fn (Shift $shift) => $shift->status === 'open' && $shift->slots_open_for_claiming > 0);
|
|
|
|
$statusPool = array_merge(
|
|
array_fill(0, 75, ShiftAssignmentStatus::APPROVED),
|
|
array_fill(0, 15, ShiftAssignmentStatus::PENDING_APPROVAL),
|
|
array_fill(0, 5, ShiftAssignmentStatus::REJECTED),
|
|
array_fill(0, 5, ShiftAssignmentStatus::CANCELLED),
|
|
);
|
|
shuffle($statusPool);
|
|
$statusIdx = 0;
|
|
|
|
foreach ($approvedPersons->shuffle() as $person) {
|
|
$existing = $usedPersonSlots[$person->id] ?? [];
|
|
$available = $openShifts->filter(fn (Shift $shift) => !in_array($shift->time_slot_id, $existing));
|
|
|
|
if ($available->isEmpty()) {
|
|
continue;
|
|
}
|
|
|
|
$numShifts = min(rand(2, 3), $available->count());
|
|
$picked = $available->shuffle()->take($numShifts);
|
|
|
|
foreach ($picked as $shift) {
|
|
$status = $statusPool[$statusIdx % count($statusPool)];
|
|
$statusIdx++;
|
|
$isApproved = in_array($status, [ShiftAssignmentStatus::APPROVED, ShiftAssignmentStatus::COMPLETED]);
|
|
|
|
// Skip approved assignments if shift is already at capacity
|
|
if ($isApproved && ($shiftFilledCounts[$shift->id] ?? 0) >= $shift->slots_total) {
|
|
continue;
|
|
}
|
|
|
|
ShiftAssignment::create([
|
|
'shift_id' => $shift->id,
|
|
'person_id' => $person->id,
|
|
'time_slot_id' => $shift->time_slot_id,
|
|
'status' => $status,
|
|
'auto_approved' => $isApproved && rand(0, 1) === 1,
|
|
'assigned_at' => now(),
|
|
'approved_at' => $isApproved ? now() : null,
|
|
'rejection_reason' => $status === ShiftAssignmentStatus::REJECTED ? 'Geen beschikbare plek' : null,
|
|
]);
|
|
|
|
if ($isApproved) {
|
|
$shiftFilledCounts[$shift->id] = ($shiftFilledCounts[$shift->id] ?? 0) + 1;
|
|
}
|
|
|
|
$existing[] = $shift->time_slot_id;
|
|
}
|
|
$usedPersonSlots[$person->id] = $existing;
|
|
}
|
|
|
|
$assignmentCount = ShiftAssignment::whereIn('shift_id', collect($allShifts)->pluck('id'))->count();
|
|
$this->command->info(" {$assignmentCount} shift assignments created");
|
|
|
|
// Auto-correct shift statuses based on actual fill
|
|
foreach ($allShifts as $shift) {
|
|
$filled = ShiftAssignment::where('shift_id', $shift->id)
|
|
->whereIn('status', [ShiftAssignmentStatus::APPROVED, ShiftAssignmentStatus::COMPLETED])
|
|
->count();
|
|
|
|
$correctStatus = $filled >= $shift->slots_total ? 'full' : 'open';
|
|
if ($shift->status !== $correctStatus) {
|
|
$shift->update(['status' => $correctStatus]);
|
|
}
|
|
}
|
|
|
|
// ── Volunteer availabilities (~70) ──
|
|
|
|
$volSlotKeys = ['vr_early', 'vr_avond', 'za_dag', 'za_avond', 'zo_dag', 'zo_avond'];
|
|
$volSlots = collect($volSlotKeys)->map(fn (string $k) => $ts[$k]);
|
|
|
|
// Named availabilities
|
|
$namedAvails = [
|
|
[$jan, $volSlots->random(4), fn () => rand(2, 5)],
|
|
[$lisaB, $volSlots->random(5), fn () => rand(1, 5)],
|
|
[$ahmedP, $volSlots, fn () => 5],
|
|
[$saraJ, collect([$ts['vr_avond'], $ts['za_avond']]), fn () => rand(1, 5)],
|
|
[$fatima, collect([$ts['za_dag'], $ts['za_avond'], $ts['zo_dag']]), fn () => rand(1, 5)],
|
|
];
|
|
|
|
$usedAvails = [];
|
|
foreach ($namedAvails as [$person, $slots, $prefFn]) {
|
|
foreach ($slots as $slot) {
|
|
$combo = "{$person->id}_{$slot->id}";
|
|
if (isset($usedAvails[$combo])) {
|
|
continue;
|
|
}
|
|
VolunteerAvailability::create([
|
|
'person_id' => $person->id,
|
|
'time_slot_id' => $slot->id,
|
|
'preference_level' => $prefFn(),
|
|
'submitted_at' => now(),
|
|
]);
|
|
$usedAvails[$combo] = true;
|
|
}
|
|
}
|
|
|
|
// Factory availabilities for approved volunteers
|
|
$approvedVolunteers = Person::where('event_id', $festival->id)
|
|
->where('status', 'approved')
|
|
->whereHas('crowdType', fn ($q) => $q->where('system_type', 'VOLUNTEER'))
|
|
->get();
|
|
|
|
foreach ($approvedVolunteers as $person) {
|
|
$numSlots = rand(2, 5);
|
|
foreach ($volSlots->shuffle()->take($numSlots) as $slot) {
|
|
$combo = "{$person->id}_{$slot->id}";
|
|
if (isset($usedAvails[$combo])) {
|
|
continue;
|
|
}
|
|
VolunteerAvailability::create([
|
|
'person_id' => $person->id,
|
|
'time_slot_id' => $slot->id,
|
|
'preference_level' => rand(1, 5),
|
|
'submitted_at' => now(),
|
|
]);
|
|
$usedAvails[$combo] = true;
|
|
}
|
|
}
|
|
|
|
// ── Crowd Lists (7) ──
|
|
|
|
$bert = $this->users['bert@feestfabriek.nl'];
|
|
$allApprovedVolunteers = Person::where('event_id', $festival->id)->where('status', 'approved')
|
|
->whereHas('crowdType', fn ($q) => $q->where('system_type', 'VOLUNTEER'))->get();
|
|
$allApprovedCrew = Person::where('event_id', $festival->id)->where('status', 'approved')
|
|
->whereHas('crowdType', fn ($q) => $q->where('system_type', 'CREW'))->get();
|
|
$allApprovedGuests = Person::where('event_id', $festival->id)->where('status', 'approved')
|
|
->whereHas('crowdType', fn ($q) => $q->where('system_type', 'GUEST'))->get();
|
|
|
|
$this->createCrowdList($festival, 'Vaste Bar Crew', CrowdListType::INTERNAL, $this->crowdTypes['VOLUNTEER'], null, false, null, $allApprovedVolunteers->take(20), $bert);
|
|
$this->createCrowdList($festival, 'EHBO Team', CrowdListType::INTERNAL, $this->crowdTypes['VOLUNTEER'], null, false, 10, $allApprovedVolunteers->shuffle()->take(5), $bert);
|
|
$this->createCrowdList($festival, 'Terreinploeg Pool', CrowdListType::INTERNAL, $this->crowdTypes['VOLUNTEER'], null, true, 25, $allApprovedVolunteers->shuffle()->take(15), $bert);
|
|
$this->createCrowdList($festival, 'SecureEvent Beveiliging', CrowdListType::EXTERNAL, $this->crowdTypes['CREW'], $this->companies['SecureEvent BV'], true, 15, $allApprovedCrew->shuffle()->take(8), $bert);
|
|
$this->createCrowdList($festival, 'Podiumtechniek Crew', CrowdListType::EXTERNAL, $this->crowdTypes['CREW'], $this->companies['Podiumtechniek Rijnmond'], false, 6, $allApprovedCrew->shuffle()->take(5), $bert);
|
|
$this->createCrowdList($festival, 'Catering Crew', CrowdListType::EXTERNAL, $this->crowdTypes['CREW'], $this->companies['Van Dijk Catering'], false, 8, $allApprovedCrew->shuffle()->take(4), $bert);
|
|
$this->createCrowdList($festival, 'VIP Gastenlijst', CrowdListType::INTERNAL, $this->crowdTypes['GUEST'], null, true, 30, $allApprovedGuests->take(12), $bert);
|
|
|
|
// ── Event person activations (crew on sub-events) ──
|
|
|
|
$activations = [
|
|
[$klaas, [$vrijdag, $zaterdag, $zondag]],
|
|
[$dennis, [$zaterdag, $zondag]],
|
|
[$eva, [$vrijdag]],
|
|
[$maria, [$vrijdag, $zaterdag, $zondag]],
|
|
];
|
|
|
|
foreach ($activations as [$person, $events]) {
|
|
foreach ($events as $event) {
|
|
DB::table('event_person_activations')->insert([
|
|
'event_id' => $event->id,
|
|
'person_id' => $person->id,
|
|
]);
|
|
}
|
|
}
|
|
|
|
// Activate remaining approved crew on 1-3 random sub-events
|
|
$subEventIds = [$vrijdag->id, $zaterdag->id, $zondag->id];
|
|
$activatedPersonIds = collect($activations)->pluck('0.id');
|
|
$remainingCrew = $allApprovedCrew->reject(fn (Person $p) => $activatedPersonIds->contains($p->id));
|
|
|
|
foreach ($remainingCrew as $person) {
|
|
$numEvents = rand(1, 3);
|
|
$selectedEvents = collect($subEventIds)->shuffle()->take($numEvents);
|
|
foreach ($selectedEvents as $eventId) {
|
|
DB::table('event_person_activations')->insert([
|
|
'event_id' => $eventId,
|
|
'person_id' => $person->id,
|
|
]);
|
|
}
|
|
}
|
|
|
|
// ── User Organisation Tags ──
|
|
|
|
$tagData = [
|
|
['user' => $volunteerUsers['jan@gmail.com'], 'tag' => 'Tapper', 'source' => 'self_reported', 'proficiency' => 'experienced'],
|
|
['user' => $volunteerUsers['jan@gmail.com'], 'tag' => 'EHBO', 'source' => 'organiser_assigned', 'proficiency' => 'beginner', 'assigned_by' => $bert->id],
|
|
['user' => $volunteerUsers['ahmed.h@gmail.com'], 'tag' => 'EHBO', 'source' => 'self_reported', 'proficiency' => 'expert'],
|
|
['user' => $volunteerUsers['ahmed.h@gmail.com'], 'tag' => 'BHV', 'source' => 'organiser_assigned', 'proficiency' => 'expert', 'assigned_by' => $bert->id],
|
|
['user' => $volunteerUsers['ahmed.h@gmail.com'], 'tag' => 'Rijbewijs B', 'source' => 'self_reported', 'proficiency' => null],
|
|
['user' => $volunteerUsers['tom.visser@gmail.com'], 'tag' => 'Podiumervaring', 'source' => 'self_reported', 'proficiency' => 'experienced'],
|
|
['user' => $volunteerUsers['tom.visser@gmail.com'], 'tag' => 'Engels', 'source' => 'self_reported', 'proficiency' => 'experienced'],
|
|
['user' => $volunteerUsers['tom.visser@gmail.com'], 'tag' => 'Duits', 'source' => 'self_reported', 'proficiency' => 'beginner'],
|
|
['user' => $volunteerUsers['lotte@gmail.com'], 'tag' => 'Rijbewijs B', 'source' => 'self_reported', 'proficiency' => null],
|
|
['user' => $volunteerUsers['lotte@gmail.com'], 'tag' => 'Heftruck', 'source' => 'organiser_assigned', 'proficiency' => 'experienced', 'assigned_by' => $bert->id],
|
|
];
|
|
|
|
foreach ($tagData as $td) {
|
|
UserOrganisationTag::create([
|
|
'user_id' => $td['user']->id,
|
|
'organisation_id' => $this->org->id,
|
|
'person_tag_id' => $this->personTags[$td['tag']]->id,
|
|
'source' => $td['source'],
|
|
'assigned_by_user_id' => $td['assigned_by'] ?? null,
|
|
'proficiency' => $td['proficiency'],
|
|
'assigned_at' => now(),
|
|
]);
|
|
}
|
|
|
|
$this->command->info(' Echt Feesten 2026 complete');
|
|
});
|
|
}
|
|
|
|
// =========================================================================
|
|
// Event 2: IJsbaan Winterpark (series, published, 60 persons)
|
|
// =========================================================================
|
|
|
|
private function seedIJsbaan(): void
|
|
{
|
|
DB::transaction(function (): void {
|
|
$this->command->info('Seeding IJsbaan Winterpark...');
|
|
|
|
// ── Event hierarchy ──
|
|
|
|
$ijsbaan = Event::create([
|
|
'organisation_id' => $this->org->id,
|
|
'name' => 'IJsbaan Winterpark',
|
|
'slug' => 'ijsbaan-winterpark',
|
|
'start_date' => '2026-12-05',
|
|
'end_date' => '2027-01-25',
|
|
'timezone' => 'Europe/Amsterdam',
|
|
'status' => 'published',
|
|
'event_type' => 'series',
|
|
'event_type_label' => 'Schaatsseizoen',
|
|
'sub_event_label' => 'Editie',
|
|
]);
|
|
|
|
$weeksData = [
|
|
['name' => 'Week 1 — Opening', 'start' => '2026-12-05', 'end' => '2026-12-07', 'status' => 'published', 'zaterdag' => '2026-12-05', 'zondag' => '2026-12-06'],
|
|
['name' => 'Week 2 — Kerst Special', 'start' => '2026-12-19', 'end' => '2026-12-21', 'status' => 'published', 'zaterdag' => '2026-12-19', 'zondag' => '2026-12-20'],
|
|
['name' => 'Week 3 — Nieuwjaar', 'start' => '2027-01-02', 'end' => '2027-01-04', 'status' => 'draft', 'zaterdag' => '2027-01-03', 'zondag' => '2027-01-04'],
|
|
['name' => 'Week 4 — Afsluiting', 'start' => '2027-01-23', 'end' => '2027-01-25', 'status' => 'draft', 'zaterdag' => '2027-01-24', 'zondag' => '2027-01-25'],
|
|
];
|
|
|
|
$weeks = [];
|
|
foreach ($weeksData as $i => $wd) {
|
|
$weeks[$i] = Event::create([
|
|
'organisation_id' => $this->org->id,
|
|
'parent_event_id' => $ijsbaan->id,
|
|
'name' => $wd['name'],
|
|
'slug' => 'ijsbaan-week-' . ($i + 1),
|
|
'start_date' => $wd['start'],
|
|
'end_date' => $wd['end'],
|
|
'timezone' => 'Europe/Amsterdam',
|
|
'status' => $wd['status'],
|
|
'event_type' => 'event',
|
|
]);
|
|
}
|
|
|
|
// ── Locations (2, on parent) ──
|
|
|
|
Location::create(['event_id' => $ijsbaan->id, 'name' => 'IJsbaan Kralingse Plas', 'address' => 'Kralingseweg 200, Rotterdam', 'lat' => 51.9300, 'lng' => 4.5100]);
|
|
Location::create(['event_id' => $ijsbaan->id, 'name' => 'Verwarmde tent', 'address' => 'Kralingseweg 200A, Rotterdam', 'lat' => 51.9302, 'lng' => 4.5105]);
|
|
|
|
// ── Sections, time slots, shifts per sub-event ──
|
|
|
|
$sectionDefs = [
|
|
['name' => 'Schaatsbaan Bar', 'category' => 'Bar', 'icon' => 'tabler-beer', 'show_in_registration' => true, 'registration_description' => 'Warme dranken en snacks serveren aan schaatsers'],
|
|
['name' => 'Schaatsverhuur', 'category' => 'Ontvangst', 'icon' => 'tabler-ticket', 'show_in_registration' => true, 'registration_description' => 'Schaatsen uitgeven en innemen bij de verhuurbalie'],
|
|
['name' => 'Terrein', 'category' => 'Productie', 'icon' => 'tabler-shovel', 'show_in_registration' => true, 'registration_description' => 'IJsbaan onderhoud en terreinbeheer'],
|
|
];
|
|
|
|
$allShifts = [];
|
|
|
|
foreach ($weeks as $i => $week) {
|
|
$wd = $weeksData[$i];
|
|
$isOpen = in_array($wd['status'], ['published', 'registration_open']);
|
|
|
|
// Sections
|
|
$weekSections = [];
|
|
foreach ($sectionDefs as $order => $sd) {
|
|
$weekSections[] = FestivalSection::create([
|
|
'event_id' => $week->id,
|
|
'name' => $sd['name'],
|
|
'type' => 'standard',
|
|
'category' => $sd['category'],
|
|
'icon' => $sd['icon'],
|
|
'sort_order' => $order + 1,
|
|
'responder_self_checkin' => true,
|
|
'crew_auto_accepts' => true,
|
|
'show_in_registration' => $sd['show_in_registration'] ?? false,
|
|
'registration_description' => $sd['registration_description'] ?? null,
|
|
]);
|
|
}
|
|
|
|
// Time slots
|
|
$zatSlot = TimeSlot::create(['event_id' => $week->id, 'name' => 'Zaterdag', 'person_type' => 'VOLUNTEER', 'date' => $wd['zaterdag'], 'start_time' => '10:00', 'end_time' => '18:00', 'duration_hours' => 8.00]);
|
|
$zonSlot = TimeSlot::create(['event_id' => $week->id, 'name' => 'Zondag', 'person_type' => 'VOLUNTEER', 'date' => $wd['zondag'], 'start_time' => '10:00', 'end_time' => '18:00', 'duration_hours' => 8.00]);
|
|
|
|
// Shifts
|
|
foreach ($weekSections as $section) {
|
|
foreach ([$zatSlot, $zonSlot] as $slot) {
|
|
$shift = Shift::create([
|
|
'festival_section_id' => $section->id,
|
|
'time_slot_id' => $slot->id,
|
|
'title' => $section->name,
|
|
'slots_total' => 6,
|
|
'slots_open_for_claiming' => 6,
|
|
'status' => $isOpen ? 'open' : 'draft',
|
|
]);
|
|
$allShifts[] = $shift;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ── Persons (60, all factory) ──
|
|
|
|
$vol = $this->crowdTypes['VOLUNTEER']->id;
|
|
$crewType = $this->crowdTypes['CREW']->id;
|
|
$guestType = $this->crowdTypes['GUEST']->id;
|
|
|
|
$approvedVol = Person::factory()->count(35)->approved()->create(['event_id' => $ijsbaan->id, 'crowd_type_id' => $vol]);
|
|
Person::factory()->count(5)->create(['event_id' => $ijsbaan->id, 'crowd_type_id' => $vol, 'status' => 'pending']);
|
|
Person::factory()->count(3)->create(['event_id' => $ijsbaan->id, 'crowd_type_id' => $vol, 'status' => 'applied']);
|
|
$approvedCrew = Person::factory()->count(10)->approved()->create(['event_id' => $ijsbaan->id, 'crowd_type_id' => $crewType]);
|
|
Person::factory()->count(2)->create(['event_id' => $ijsbaan->id, 'crowd_type_id' => $crewType, 'status' => 'pending']);
|
|
Person::factory()->count(5)->approved()->create(['event_id' => $ijsbaan->id, 'crowd_type_id' => $guestType]);
|
|
|
|
// ── Shift assignments (~80) ──
|
|
|
|
$openShifts = collect($allShifts)->filter(fn (Shift $shift) => $shift->status === 'open' && $shift->slots_open_for_claiming > 0);
|
|
$draftShifts = collect($allShifts)->filter(fn (Shift $shift) => $shift->status === 'draft');
|
|
$allApproved = $approvedVol->merge($approvedCrew);
|
|
|
|
// Week 1+2: up to slots_total per open shift
|
|
foreach ($openShifts as $shift) {
|
|
$count = min(rand(5, 6), $shift->slots_total);
|
|
$assigned = $allApproved->shuffle()->take($count);
|
|
foreach ($assigned as $person) {
|
|
ShiftAssignment::create([
|
|
'shift_id' => $shift->id,
|
|
'person_id' => $person->id,
|
|
'time_slot_id' => $shift->time_slot_id,
|
|
'status' => ShiftAssignmentStatus::APPROVED,
|
|
'auto_approved' => true,
|
|
'assigned_at' => now(),
|
|
'approved_at' => now(),
|
|
]);
|
|
}
|
|
}
|
|
|
|
// ~10 pending for week 3 draft shifts
|
|
$pendingCount = 0;
|
|
foreach ($draftShifts->take(4) as $shift) {
|
|
$assigned = $allApproved->shuffle()->take(rand(2, 3));
|
|
foreach ($assigned as $person) {
|
|
ShiftAssignment::create([
|
|
'shift_id' => $shift->id,
|
|
'person_id' => $person->id,
|
|
'time_slot_id' => $shift->time_slot_id,
|
|
'status' => ShiftAssignmentStatus::PENDING_APPROVAL,
|
|
'assigned_at' => now(),
|
|
]);
|
|
$pendingCount++;
|
|
if ($pendingCount >= 10) {
|
|
break 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ── Crowd Lists (2) ──
|
|
|
|
$bert = $this->users['bert@feestfabriek.nl'];
|
|
$this->createCrowdList($ijsbaan, 'IJsbaan Vrijwilligerspool', CrowdListType::INTERNAL, $this->crowdTypes['VOLUNTEER'], null, false, null, $approvedVol, $bert);
|
|
$this->createCrowdList($ijsbaan, 'IJsbaan Vaste Crew', CrowdListType::INTERNAL, $this->crowdTypes['CREW'], null, false, null, $approvedCrew, $bert);
|
|
|
|
$personCount = Person::where('event_id', $ijsbaan->id)->count();
|
|
$this->command->info(" {$personCount} persons, " . count($allShifts) . ' shifts created');
|
|
});
|
|
}
|
|
|
|
// =========================================================================
|
|
// Event 3: Koningsdag Rotterdam 2026 (flat event, closed, 100 persons)
|
|
// =========================================================================
|
|
|
|
private function seedKoningsdag(): void
|
|
{
|
|
DB::transaction(function (): void {
|
|
$this->command->info('Seeding Koningsdag Rotterdam 2026...');
|
|
|
|
// ── Event ──
|
|
|
|
$koningsdag = Event::create([
|
|
'organisation_id' => $this->org->id,
|
|
'name' => 'Koningsdag Rotterdam 2026',
|
|
'slug' => 'koningsdag-rotterdam-2026',
|
|
'start_date' => '2026-04-27',
|
|
'end_date' => '2026-04-27',
|
|
'timezone' => 'Europe/Amsterdam',
|
|
'status' => 'closed',
|
|
'event_type' => 'event',
|
|
]);
|
|
|
|
// ── Locations (3) ──
|
|
|
|
$locErasmus = Location::create(['event_id' => $koningsdag->id, 'name' => 'Erasmusbrug podium', 'address' => 'Erasmusbrug, Rotterdam', 'lat' => 51.9090, 'lng' => 4.4870]);
|
|
$locWillems = Location::create(['event_id' => $koningsdag->id, 'name' => 'Willemsplein', 'address' => 'Willemsplein, Rotterdam', 'lat' => 51.9070, 'lng' => 4.4860]);
|
|
$locOudeHaven = Location::create(['event_id' => $koningsdag->id, 'name' => 'Oude Haven', 'address' => 'Oude Haven, Rotterdam', 'lat' => 51.9200, 'lng' => 4.4950]);
|
|
|
|
// ── Sections (4) ──
|
|
|
|
$secPodium = FestivalSection::create(['event_id' => $koningsdag->id, 'name' => 'Podium Erasmusbrug', 'type' => 'standard', 'category' => 'Podium', 'icon' => 'tabler-microphone-2', 'sort_order' => 1, 'responder_self_checkin' => true, 'show_in_registration' => true, 'registration_description' => 'Podiummedewerker bij de Erasmusbrug']);
|
|
$secBar = FestivalSection::create(['event_id' => $koningsdag->id, 'name' => 'Bar Willemsplein', 'type' => 'standard', 'category' => 'Bar', 'icon' => 'tabler-beer', 'sort_order' => 2, 'responder_self_checkin' => true, 'crew_auto_accepts' => true, 'show_in_registration' => true, 'registration_description' => 'Tappen en serveren op het Willemsplein']);
|
|
$secKids = FestivalSection::create(['event_id' => $koningsdag->id, 'name' => 'Kinderactiviteiten', 'type' => 'standard', 'category' => 'Entertainment', 'icon' => 'tabler-balloon', 'sort_order' => 3, 'responder_self_checkin' => true, 'show_in_registration' => true, 'registration_description' => 'Begeleid kinderactiviteiten en spelletjes']);
|
|
$secBev = FestivalSection::create(['event_id' => $koningsdag->id, 'name' => 'Beveiliging', 'type' => 'standard', 'category' => 'Veiligheid', 'icon' => 'tabler-shield', 'sort_order' => 4, 'responder_self_checkin' => true, 'show_in_registration' => true, 'registration_description' => 'Beveiliging en crowd management']);
|
|
|
|
$kSections = [$secPodium, $secBar, $secKids, $secBev];
|
|
$kLocations = [$locErasmus, $locWillems, $locOudeHaven, $locOudeHaven];
|
|
|
|
// ── Time Slots (3) ──
|
|
|
|
$tsOchtend = TimeSlot::create(['event_id' => $koningsdag->id, 'name' => 'Ochtend', 'person_type' => 'VOLUNTEER', 'date' => '2026-04-27', 'start_time' => '09:00', 'end_time' => '13:00', 'duration_hours' => 4.00]);
|
|
$tsMiddag = TimeSlot::create(['event_id' => $koningsdag->id, 'name' => 'Middag', 'person_type' => 'VOLUNTEER', 'date' => '2026-04-27', 'start_time' => '13:00', 'end_time' => '18:00', 'duration_hours' => 5.00]);
|
|
$tsAvond = TimeSlot::create(['event_id' => $koningsdag->id, 'name' => 'Avond', 'person_type' => 'VOLUNTEER', 'date' => '2026-04-27', 'start_time' => '18:00', 'end_time' => '23:00', 'duration_hours' => 5.00]);
|
|
$kSlots = [$tsOchtend, $tsMiddag, $tsAvond];
|
|
|
|
// ── Shifts (12, all completed) ──
|
|
|
|
$kShifts = [];
|
|
foreach ($kSections as $i => $section) {
|
|
foreach ($kSlots as $slot) {
|
|
$slotsTotal = $section->name === 'Bar Willemsplein' ? 15 : rand(8, 12);
|
|
$kShifts[] = Shift::create([
|
|
'festival_section_id' => $section->id,
|
|
'time_slot_id' => $slot->id,
|
|
'location_id' => $kLocations[$i]->id,
|
|
'title' => match ($section->name) {
|
|
'Bar Willemsplein' => 'Tapper',
|
|
'Podium Erasmusbrug' => 'Podiummedewerker',
|
|
'Kinderactiviteiten' => 'Animator',
|
|
'Beveiliging' => 'Beveiliger',
|
|
},
|
|
'slots_total' => $slotsTotal,
|
|
'slots_open_for_claiming' => 0,
|
|
'status' => 'completed',
|
|
]);
|
|
}
|
|
}
|
|
|
|
// ── Persons (100, all factory) ──
|
|
|
|
$vol = $this->crowdTypes['VOLUNTEER']->id;
|
|
$crewType = $this->crowdTypes['CREW']->id;
|
|
$pressType = $this->crowdTypes['PRESS']->id;
|
|
$guestType = $this->crowdTypes['GUEST']->id;
|
|
$supplierType = $this->crowdTypes['SUPPLIER']->id;
|
|
|
|
// 75 volunteers (55 approved, 8 no_show, 5 rejected, 4 approved no-shift, 3 rejected)
|
|
$kApprovedVol = Person::factory()->count(59)->approved()->create(['event_id' => $koningsdag->id, 'crowd_type_id' => $vol]);
|
|
$kNoShowVol = Person::factory()->count(8)->create(['event_id' => $koningsdag->id, 'crowd_type_id' => $vol, 'status' => 'no_show']);
|
|
Person::factory()->count(8)->create(['event_id' => $koningsdag->id, 'crowd_type_id' => $vol, 'status' => 'rejected']);
|
|
|
|
// 12 crew (8 with SecureEvent BV)
|
|
$kCrew = Person::factory()->count(8)->approved()->create(['event_id' => $koningsdag->id, 'crowd_type_id' => $crewType, 'company_id' => $this->companies['SecureEvent BV']->id]);
|
|
$kCrew = $kCrew->merge(Person::factory()->count(4)->approved()->create(['event_id' => $koningsdag->id, 'crowd_type_id' => $crewType]));
|
|
|
|
// 6 press
|
|
Person::factory()->count(6)->approved()->create(['event_id' => $koningsdag->id, 'crowd_type_id' => $pressType]);
|
|
|
|
// 5 guests (with Rotterdam Festivals)
|
|
$kGuests = Person::factory()->count(5)->approved()->create(['event_id' => $koningsdag->id, 'crowd_type_id' => $guestType, 'company_id' => $this->companies['Rotterdam Festivals']->id]);
|
|
|
|
// 2 suppliers
|
|
Person::factory()->count(2)->approved()->create(['event_id' => $koningsdag->id, 'crowd_type_id' => $supplierType]);
|
|
|
|
// ── Shift assignments (~150) ──
|
|
|
|
// 120 completed + 12 cancelled + 8 no-show + 5 rejected + 5 pending
|
|
$statusPool = array_merge(
|
|
array_fill(0, 120, 'completed'),
|
|
array_fill(0, 12, 'cancelled'),
|
|
array_fill(0, 5, 'rejected'),
|
|
array_fill(0, 5, 'pending'),
|
|
);
|
|
shuffle($statusPool);
|
|
$statusIdx = 0;
|
|
|
|
// Assign approved volunteers 2-3 shifts each
|
|
foreach ($kApprovedVol as $person) {
|
|
$numShifts = rand(2, 3);
|
|
$picked = collect($kShifts)->shuffle()->take($numShifts);
|
|
foreach ($picked as $shift) {
|
|
$statusKey = $statusPool[$statusIdx % count($statusPool)] ?? 'completed';
|
|
$statusIdx++;
|
|
$status = match ($statusKey) {
|
|
'completed' => ShiftAssignmentStatus::COMPLETED,
|
|
'cancelled' => ShiftAssignmentStatus::CANCELLED,
|
|
'rejected' => ShiftAssignmentStatus::REJECTED,
|
|
'pending' => ShiftAssignmentStatus::PENDING_APPROVAL,
|
|
};
|
|
$isCompleted = $status === ShiftAssignmentStatus::COMPLETED;
|
|
ShiftAssignment::create([
|
|
'shift_id' => $shift->id,
|
|
'person_id' => $person->id,
|
|
'time_slot_id' => $shift->time_slot_id,
|
|
'status' => $status,
|
|
'auto_approved' => $isCompleted,
|
|
'assigned_at' => now(),
|
|
'approved_at' => $isCompleted ? now() : null,
|
|
'hours_completed' => $isCompleted ? round(rand(35, 80) / 10, 1) : null,
|
|
'checked_in_at' => $isCompleted ? now() : null,
|
|
'checked_out_at' => $isCompleted ? now()->addHours(rand(3, 8)) : null,
|
|
]);
|
|
}
|
|
}
|
|
|
|
// No-show persons: approved assignment but no check-in
|
|
foreach ($kNoShowVol as $person) {
|
|
$shift = collect($kShifts)->random();
|
|
ShiftAssignment::create([
|
|
'shift_id' => $shift->id,
|
|
'person_id' => $person->id,
|
|
'time_slot_id' => $shift->time_slot_id,
|
|
'status' => ShiftAssignmentStatus::APPROVED,
|
|
'auto_approved' => true,
|
|
'assigned_at' => now(),
|
|
'approved_at' => now(),
|
|
'checked_in_at' => null,
|
|
]);
|
|
}
|
|
|
|
// ── Crowd Lists (3) ──
|
|
|
|
$bert = $this->users['bert@feestfabriek.nl'];
|
|
$allKVolunteers = Person::where('event_id', $koningsdag->id)->where('status', 'approved')
|
|
->whereHas('crowdType', fn ($q) => $q->where('system_type', 'VOLUNTEER'))->get();
|
|
|
|
$this->createCrowdList($koningsdag, 'Koningsdag Crew 2026', CrowdListType::INTERNAL, $this->crowdTypes['VOLUNTEER'], null, false, null, $allKVolunteers->take(55), $bert);
|
|
$this->createCrowdList($koningsdag, 'Beveiliging Koningsdag', CrowdListType::EXTERNAL, $this->crowdTypes['CREW'], $this->companies['SecureEvent BV'], false, null, $kCrew->take(8), $bert);
|
|
$this->createCrowdList($koningsdag, 'Gemeente Gasten', CrowdListType::EXTERNAL, $this->crowdTypes['GUEST'], $this->companies['Rotterdam Festivals'], false, null, $kGuests, $bert);
|
|
|
|
$personCount = Person::where('event_id', $koningsdag->id)->count();
|
|
$assignCount = ShiftAssignment::whereIn('shift_id', collect($kShifts)->pluck('id'))->count();
|
|
$this->command->info(" {$personCount} persons, 12 shifts, {$assignCount} assignments");
|
|
});
|
|
}
|
|
|
|
// =========================================================================
|
|
// Event 4: Nacht van de Kaap 2026 (flat event, draft, empty)
|
|
// =========================================================================
|
|
|
|
private function seedNachtVanDeKaap(): void
|
|
{
|
|
DB::transaction(function (): void {
|
|
$this->command->info('Seeding Nacht van de Kaap 2026...');
|
|
|
|
$event = Event::create([
|
|
'organisation_id' => $this->org->id,
|
|
'name' => 'Nacht van de Kaap 2026',
|
|
'slug' => 'nacht-van-de-kaap-2026',
|
|
'start_date' => '2026-09-12',
|
|
'end_date' => '2026-09-12',
|
|
'timezone' => 'Europe/Amsterdam',
|
|
'status' => 'draft',
|
|
'event_type' => 'event',
|
|
]);
|
|
|
|
Location::create(['event_id' => $event->id, 'name' => 'Katendrecht', 'address' => 'Deliplein, Rotterdam', 'lat' => 51.8950, 'lng' => 4.4850]);
|
|
|
|
FestivalSection::create(['event_id' => $event->id, 'name' => 'Kaapse Bar', 'type' => 'standard', 'category' => 'Bar', 'icon' => 'tabler-beer', 'sort_order' => 1, 'responder_self_checkin' => true]);
|
|
FestivalSection::create(['event_id' => $event->id, 'name' => 'Podium Deliplein', 'type' => 'standard', 'category' => 'Podium', 'icon' => 'tabler-microphone-2', 'sort_order' => 2, 'responder_self_checkin' => true]);
|
|
|
|
TimeSlot::create(['event_id' => $event->id, 'name' => 'Nacht', 'person_type' => 'VOLUNTEER', 'date' => '2026-09-12', 'start_time' => '20:00', 'end_time' => '04:00', 'duration_hours' => 8.00]);
|
|
|
|
$this->command->info(' Empty draft event created');
|
|
});
|
|
}
|
|
|
|
// =========================================================================
|
|
// Helpers
|
|
// =========================================================================
|
|
|
|
private function createCrowdList(
|
|
Event $event,
|
|
string $name,
|
|
CrowdListType $type,
|
|
CrowdType $crowdType,
|
|
?Company $company,
|
|
bool $autoApprove,
|
|
?int $maxPersons,
|
|
Collection $persons,
|
|
User $addedBy,
|
|
): CrowdList {
|
|
$list = CrowdList::create([
|
|
'event_id' => $event->id,
|
|
'crowd_type_id' => $crowdType->id,
|
|
'name' => $name,
|
|
'type' => $type,
|
|
'recipient_company_id' => $company?->id,
|
|
'auto_approve' => $autoApprove,
|
|
'max_persons' => $maxPersons,
|
|
]);
|
|
|
|
$pivotData = $persons->mapWithKeys(fn (Person $p) => [
|
|
$p->id => ['added_at' => now(), 'added_by_user_id' => $addedBy->id],
|
|
])->all();
|
|
|
|
$list->persons()->attach($pivotData);
|
|
|
|
return $list;
|
|
}
|
|
}
|