From 3e3636dc5303b16fb8361a267406e18ff1bec0ab Mon Sep 17 00:00:00 2001 From: "bert.hausmans" Date: Fri, 8 May 2026 18:08:16 +0200 Subject: [PATCH] feat(timetable): factories + ArtistTimetableDevSeeder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Eight factories with named states (Genre, Artist, ArtistContact, Stage, ArtistEngagement, Performance, AdvanceSection, AdvanceSubmission). ArtistTimetableDevSeeder hooked into DevSeeder::seedEchtFeesten after the form-builder showcase. Produces: - 4 stages (Mainstage, Havana, Stairway, Socialite) with prototype-style hex colours - 4 stages × 3 sub-events = 12 stage_days rows - 4 genres (Hardstyle, Techno, Indie, Live band) - 6 master artists, each with one tour-manager ArtistContact - 12 engagements with status mix (1 Draft, 2 Requested, 3 Option, 2 Confirmed, 3 Contracted, 1 Cancelled). Two artists have two engagements each (different sub-events) — exercises D17 multi- engagement-per-artist. - 13 performances, including one parked (stage_id=null = wachtrij) and one B2B pair within 3 minutes on Mainstage Saturday to seed the Session 4 frontend B2B detector. Also fix LogOptions method name across 8 models: dontSubmitEmptyLogs() → dontLogEmptyChanges() (Spatie's actual API; surfaced when DevSeeder ran). Co-Authored-By: Claude Opus 4.7 (1M context) --- api/app/Models/AdvanceSection.php | 2 +- api/app/Models/AdvanceSubmission.php | 2 +- api/app/Models/Artist.php | 2 +- api/app/Models/ArtistContact.php | 2 +- api/app/Models/ArtistEngagement.php | 2 +- api/app/Models/Genre.php | 2 +- api/app/Models/Performance.php | 2 +- api/app/Models/Stage.php | 2 +- .../factories/AdvanceSectionFactory.php | 46 ++++ .../factories/AdvanceSubmissionFactory.php | 27 +++ .../factories/ArtistContactFactory.php | 42 ++++ .../factories/ArtistEngagementFactory.php | 84 +++++++ api/database/factories/ArtistFactory.php | 57 +++++ api/database/factories/GenreFactory.php | 25 +++ api/database/factories/PerformanceFactory.php | 48 ++++ api/database/factories/StageFactory.php | 30 +++ .../seeders/ArtistTimetableDevSeeder.php | 209 ++++++++++++++++++ api/database/seeders/DevSeeder.php | 15 +- 18 files changed, 588 insertions(+), 11 deletions(-) create mode 100644 api/database/factories/AdvanceSectionFactory.php create mode 100644 api/database/factories/AdvanceSubmissionFactory.php create mode 100644 api/database/factories/ArtistContactFactory.php create mode 100644 api/database/factories/ArtistEngagementFactory.php create mode 100644 api/database/factories/ArtistFactory.php create mode 100644 api/database/factories/GenreFactory.php create mode 100644 api/database/factories/PerformanceFactory.php create mode 100644 api/database/factories/StageFactory.php create mode 100644 api/database/seeders/ArtistTimetableDevSeeder.php diff --git a/api/app/Models/AdvanceSection.php b/api/app/Models/AdvanceSection.php index a41b1bc1..e2961a2d 100644 --- a/api/app/Models/AdvanceSection.php +++ b/api/app/Models/AdvanceSection.php @@ -64,7 +64,7 @@ final class AdvanceSection extends Model { return LogOptions::defaults() ->logFillable() - ->dontSubmitEmptyLogs(); + ->dontLogEmptyChanges(); } public function engagement(): BelongsTo diff --git a/api/app/Models/AdvanceSubmission.php b/api/app/Models/AdvanceSubmission.php index cefe1a9b..0afdbbef 100644 --- a/api/app/Models/AdvanceSubmission.php +++ b/api/app/Models/AdvanceSubmission.php @@ -55,7 +55,7 @@ final class AdvanceSubmission extends Model { return LogOptions::defaults() ->logFillable() - ->dontSubmitEmptyLogs(); + ->dontLogEmptyChanges(); } public function section(): BelongsTo diff --git a/api/app/Models/Artist.php b/api/app/Models/Artist.php index 783cc5b1..f16e31a2 100644 --- a/api/app/Models/Artist.php +++ b/api/app/Models/Artist.php @@ -57,7 +57,7 @@ final class Artist extends Model { return LogOptions::defaults() ->logFillable() - ->dontSubmitEmptyLogs(); + ->dontLogEmptyChanges(); } private function generateUniqueSlug(string $name): string diff --git a/api/app/Models/ArtistContact.php b/api/app/Models/ArtistContact.php index 8a4a0382..284ac4c9 100644 --- a/api/app/Models/ArtistContact.php +++ b/api/app/Models/ArtistContact.php @@ -54,7 +54,7 @@ final class ArtistContact extends Model { return LogOptions::defaults() ->logFillable() - ->dontSubmitEmptyLogs(); + ->dontLogEmptyChanges(); } public function artist(): BelongsTo diff --git a/api/app/Models/ArtistEngagement.php b/api/app/Models/ArtistEngagement.php index dbb58428..7849de8e 100644 --- a/api/app/Models/ArtistEngagement.php +++ b/api/app/Models/ArtistEngagement.php @@ -88,7 +88,7 @@ final class ArtistEngagement extends Model { return LogOptions::defaults() ->logFillable() - ->dontSubmitEmptyLogs(); + ->dontLogEmptyChanges(); } public function organisation(): BelongsTo diff --git a/api/app/Models/Genre.php b/api/app/Models/Genre.php index 070f8e09..076b27db 100644 --- a/api/app/Models/Genre.php +++ b/api/app/Models/Genre.php @@ -44,7 +44,7 @@ final class Genre extends Model { return LogOptions::defaults() ->logFillable() - ->dontSubmitEmptyLogs(); + ->dontLogEmptyChanges(); } public function organisation(): BelongsTo diff --git a/api/app/Models/Performance.php b/api/app/Models/Performance.php index 639775e2..5b16927e 100644 --- a/api/app/Models/Performance.php +++ b/api/app/Models/Performance.php @@ -56,7 +56,7 @@ final class Performance extends Model { return LogOptions::defaults() ->logFillable() - ->dontSubmitEmptyLogs(); + ->dontLogEmptyChanges(); } public function engagement(): BelongsTo diff --git a/api/app/Models/Stage.php b/api/app/Models/Stage.php index 7bd60a68..539421e8 100644 --- a/api/app/Models/Stage.php +++ b/api/app/Models/Stage.php @@ -51,7 +51,7 @@ final class Stage extends Model { return LogOptions::defaults() ->logFillable() - ->dontSubmitEmptyLogs(); + ->dontLogEmptyChanges(); } public function event(): BelongsTo diff --git a/api/database/factories/AdvanceSectionFactory.php b/api/database/factories/AdvanceSectionFactory.php new file mode 100644 index 00000000..6db86f31 --- /dev/null +++ b/api/database/factories/AdvanceSectionFactory.php @@ -0,0 +1,46 @@ + */ +final class AdvanceSectionFactory extends Factory +{ + /** @return array */ + public function definition(): array + { + return [ + 'engagement_id' => ArtistEngagement::factory(), + 'name' => fake()->randomElement(['Gastenlijst', 'Contacts', 'Productie', 'Catering']), + 'type' => fake()->randomElement(AdvanceSectionType::cases()), + 'is_open' => false, + 'sort_order' => 0, + 'submission_status' => AdvanceSectionSubmissionStatus::Open, + ]; + } + + public function open(): static + { + return $this->state(fn () => [ + 'is_open' => true, + 'open_from' => now()->subDays(7), + 'open_to' => now()->addDays(14), + 'submission_status' => AdvanceSectionSubmissionStatus::Open, + ]); + } + + public function approved(): static + { + return $this->state(fn () => [ + 'submission_status' => AdvanceSectionSubmissionStatus::Approved, + 'last_submitted_at' => now()->subDay(), + ]); + } +} diff --git a/api/database/factories/AdvanceSubmissionFactory.php b/api/database/factories/AdvanceSubmissionFactory.php new file mode 100644 index 00000000..de72f0b9 --- /dev/null +++ b/api/database/factories/AdvanceSubmissionFactory.php @@ -0,0 +1,27 @@ + */ +final class AdvanceSubmissionFactory extends Factory +{ + /** @return array */ + public function definition(): array + { + return [ + 'advance_section_id' => AdvanceSection::factory(), + 'submitted_by_name' => fake()->name(), + 'submitted_by_email' => fake()->safeEmail(), + 'submitted_at' => now()->subHours(fake()->numberBetween(1, 72)), + 'status' => AdvanceSubmissionStatus::Pending, + 'data' => ['payload' => fake()->sentence()], + ]; + } +} diff --git a/api/database/factories/ArtistContactFactory.php b/api/database/factories/ArtistContactFactory.php new file mode 100644 index 00000000..9334dda4 --- /dev/null +++ b/api/database/factories/ArtistContactFactory.php @@ -0,0 +1,42 @@ + */ +final class ArtistContactFactory extends Factory +{ + /** @return array */ + public function definition(): array + { + return [ + 'artist_id' => Artist::factory(), + 'name' => fake()->name(), + 'email' => fake()->safeEmail(), + 'phone' => fake()->phoneNumber(), + 'role' => fake()->randomElement(['tour_manager', 'agent', 'manager', 'production_manager']), + 'is_primary' => false, + 'receives_briefing' => false, + 'receives_infosheet' => false, + ]; + } + + public function primary(): static + { + return $this->state(fn () => [ + 'is_primary' => true, + 'receives_briefing' => true, + 'receives_infosheet' => true, + ]); + } + + public function tourManager(): static + { + return $this->state(fn () => ['role' => 'tour_manager']); + } +} diff --git a/api/database/factories/ArtistEngagementFactory.php b/api/database/factories/ArtistEngagementFactory.php new file mode 100644 index 00000000..14150a1c --- /dev/null +++ b/api/database/factories/ArtistEngagementFactory.php @@ -0,0 +1,84 @@ + */ +final class ArtistEngagementFactory extends Factory +{ + /** @return array */ + public function definition(): array + { + return [ + // organisation_id is set by the observer from the artist; + // factory leaves it null and lets the observer denormalise. + 'artist_id' => Artist::factory(), + 'event_id' => Event::factory(), + 'booking_status' => ArtistEngagementStatus::Draft, + 'fee_currency' => 'EUR', + 'buma_applicable' => true, + 'buma_percentage' => 7.00, + 'buma_handled_by' => 'organisation', + 'vat_applicable' => true, + 'vat_percentage' => 21.00, + 'payment_status' => 'none', + 'crew_count' => 0, + 'guests_count' => 0, + 'advancing_completed_count' => 0, + 'advancing_total_count' => 0, + ]; + } + + public function draft(): static + { + return $this->state(fn () => ['booking_status' => ArtistEngagementStatus::Draft]); + } + + public function requested(): static + { + return $this->state(fn () => [ + 'booking_status' => ArtistEngagementStatus::Requested, + 'requested_at' => now(), + ]); + } + + public function option(): static + { + return $this->state(fn () => [ + 'booking_status' => ArtistEngagementStatus::Option, + 'option_expires_at' => now()->addDays(14), + ]); + } + + public function offered(): static + { + return $this->state(fn () => ['booking_status' => ArtistEngagementStatus::Offered]); + } + + public function confirmed(): static + { + return $this->state(fn () => ['booking_status' => ArtistEngagementStatus::Confirmed]); + } + + public function contracted(): static + { + return $this->state(fn () => [ + 'booking_status' => ArtistEngagementStatus::Contracted, + 'fee_amount' => fake()->randomFloat(2, 500, 25000), + 'portal_token' => (string) Str::ulid(), + ]); + } + + public function cancelled(): static + { + return $this->state(fn () => ['booking_status' => ArtistEngagementStatus::Cancelled]); + } +} diff --git a/api/database/factories/ArtistFactory.php b/api/database/factories/ArtistFactory.php new file mode 100644 index 00000000..6306f508 --- /dev/null +++ b/api/database/factories/ArtistFactory.php @@ -0,0 +1,57 @@ + */ +final class ArtistFactory extends Factory +{ + /** @return array */ + public function definition(): array + { + $name = fake()->unique()->company().' '.fake()->randomElement(['Live', 'Sound', 'Project', 'Collective', 'DJ Set']); + + return [ + 'organisation_id' => Organisation::factory(), + 'name' => $name, + 'slug' => Str::slug($name).'-'.Str::lower(Str::random(4)), + 'default_genre_id' => null, + 'default_draw' => fake()->numberBetween(50, 5000), + 'star_rating' => fake()->numberBetween(1, 5), + 'home_base_country' => fake()->randomElement(['NL', 'BE', 'DE', 'FR', 'UK']), + 'agent_company_id' => null, + 'notes' => null, + ]; + } + + public function withGenre(?Genre $genre = null): static + { + return $this->state(function (array $attrs) use ($genre): array { + $resolved = $genre ?? Genre::factory()->create([ + 'organisation_id' => $attrs['organisation_id'], + ]); + + return ['default_genre_id' => $resolved->id]; + }); + } + + public function withAgent(?Company $company = null): static + { + return $this->state(function (array $attrs) use ($company): array { + $resolved = $company ?? Company::factory()->create([ + 'organisation_id' => $attrs['organisation_id'], + 'type' => 'agency', + ]); + + return ['agent_company_id' => $resolved->id]; + }); + } +} diff --git a/api/database/factories/GenreFactory.php b/api/database/factories/GenreFactory.php new file mode 100644 index 00000000..11ad5b40 --- /dev/null +++ b/api/database/factories/GenreFactory.php @@ -0,0 +1,25 @@ + */ +final class GenreFactory extends Factory +{ + /** @return array */ + public function definition(): array + { + return [ + 'organisation_id' => Organisation::factory(), + 'name' => fake()->unique()->words(2, true), + 'color' => fake()->hexColor(), + 'sort_order' => 0, + 'is_active' => true, + ]; + } +} diff --git a/api/database/factories/PerformanceFactory.php b/api/database/factories/PerformanceFactory.php new file mode 100644 index 00000000..bdaffa5d --- /dev/null +++ b/api/database/factories/PerformanceFactory.php @@ -0,0 +1,48 @@ + */ +final class PerformanceFactory extends Factory +{ + /** @return array */ + public function definition(): array + { + $start = CarbonImmutable::now()->addDays(7)->setTime(20, 0); + + return [ + 'engagement_id' => ArtistEngagement::factory(), + 'event_id' => Event::factory(), + 'stage_id' => Stage::factory(), + 'lane' => 0, + 'start_at' => $start, + 'end_at' => $start->addMinutes(60), + 'version' => 0, + 'notes' => null, + ]; + } + + public function parked(): static + { + return $this->state(fn () => ['stage_id' => null]); + } + + public function scheduled(Stage $stage, CarbonImmutable $start, int $minutes): static + { + return $this->state(fn () => [ + 'stage_id' => $stage->id, + 'event_id' => $stage->event_id, + 'start_at' => $start, + 'end_at' => $start->addMinutes($minutes), + ]); + } +} diff --git a/api/database/factories/StageFactory.php b/api/database/factories/StageFactory.php new file mode 100644 index 00000000..091b6e4e --- /dev/null +++ b/api/database/factories/StageFactory.php @@ -0,0 +1,30 @@ + */ +final class StageFactory extends Factory +{ + /** @return array */ + public function definition(): array + { + return [ + 'event_id' => Event::factory(), + 'name' => fake()->unique()->randomElement(['Mainstage', 'Havana', 'Stairway', 'Socialite', 'Tent', 'Open Air', 'Greenhouse']), + 'color' => fake()->hexColor(), + 'capacity' => fake()->numberBetween(200, 5000), + 'sort_order' => 0, + ]; + } + + public function withCapacity(int $capacity): static + { + return $this->state(fn () => ['capacity' => $capacity]); + } +} diff --git a/api/database/seeders/ArtistTimetableDevSeeder.php b/api/database/seeders/ArtistTimetableDevSeeder.php new file mode 100644 index 00000000..0529a3a9 --- /dev/null +++ b/api/database/seeders/ArtistTimetableDevSeeder.php @@ -0,0 +1,209 @@ + $subEvents Sub-events keyed 0..n in date order */ + public static function seedForFestival(Organisation $org, Event $festival, array $subEvents): void + { + if (count($subEvents) < 3) { + return; + } + + // Genres + $genres = collect([ + ['name' => 'Hardstyle', 'color' => '#e85d75'], + ['name' => 'Techno', 'color' => '#1f6feb'], + ['name' => 'Indie', 'color' => '#7c3aed'], + ['name' => 'Live band', 'color' => '#0ea5e9'], + ])->mapWithKeys(fn (array $g) => [ + $g['name'] => Genre::create([ + 'organisation_id' => $org->id, + 'name' => $g['name'], + 'color' => $g['color'], + 'sort_order' => 0, + 'is_active' => true, + ]), + ]); + + // Stages on the festival + $stages = collect([ + ['name' => 'Mainstage', 'color' => '#e85d75', 'capacity' => 4500, 'sort_order' => 1], + ['name' => 'Havana', 'color' => '#0ea5e9', 'capacity' => 1200, 'sort_order' => 2], + ['name' => 'Stairway', 'color' => '#7c3aed', 'capacity' => 800, 'sort_order' => 3], + ['name' => 'Socialite', 'color' => '#22c55e', 'capacity' => 600, 'sort_order' => 4], + ])->mapWithKeys(fn (array $s) => [ + $s['name'] => Stage::create(['event_id' => $festival->id, ...$s]), + ]); + + // stage_days — every stage active every sub-event day + foreach ($stages as $stage) { + foreach ($subEvents as $subEvent) { + StageDay::create([ + 'stage_id' => $stage->id, + 'event_id' => $subEvent->id, + ]); + } + } + + // Master artists + $artistsData = [ + ['name' => 'Donker & Licht', 'genre' => 'Hardstyle', 'draw' => 3500, 'star' => 5], + ['name' => 'Voltage Collective', 'genre' => 'Techno', 'draw' => 1800, 'star' => 4], + ['name' => 'Roos & de Wolf', 'genre' => 'Indie', 'draw' => 700, 'star' => 3], + ['name' => 'Rotterdam Brass', 'genre' => 'Live band', 'draw' => 900, 'star' => 4], + ['name' => 'Nachtwacht DJs', 'genre' => 'Techno', 'draw' => 1100, 'star' => 3], + ['name' => 'De Lichtbrigade', 'genre' => 'Live band', 'draw' => 500, 'star' => 3], + ]; + + $artists = []; + foreach ($artistsData as $data) { + /** @var Artist $artist */ + $artist = Artist::create([ + 'organisation_id' => $org->id, + 'name' => $data['name'], + 'default_genre_id' => $genres[$data['genre']]->id, + 'default_draw' => $data['draw'], + 'star_rating' => $data['star'], + 'home_base_country' => 'NL', + ]); + $artists[$data['name']] = $artist; + + ArtistContact::create([ + 'artist_id' => $artist->id, + 'name' => 'Tour Manager '.$artist->name, + 'email' => 'tm-'.$artist->slug.'@example.test', + 'phone' => '+31612340000', + 'role' => 'tour_manager', + 'is_primary' => true, + 'receives_briefing' => true, + 'receives_infosheet' => true, + ]); + } + + // Engagements (12) — status mix per RFC §5.3 Session 1 prompt. + // Two artists get two engagements (different days) to exercise D17. + $statusPlan = [ + ['artist' => 'Donker & Licht', 'sub' => 0, 'status' => ArtistEngagementStatus::Contracted], + ['artist' => 'Donker & Licht', 'sub' => 1, 'status' => ArtistEngagementStatus::Contracted], + ['artist' => 'Voltage Collective', 'sub' => 0, 'status' => ArtistEngagementStatus::Confirmed], + ['artist' => 'Voltage Collective', 'sub' => 1, 'status' => ArtistEngagementStatus::Option], + ['artist' => 'Roos & de Wolf', 'sub' => 1, 'status' => ArtistEngagementStatus::Contracted], + ['artist' => 'Rotterdam Brass', 'sub' => 0, 'status' => ArtistEngagementStatus::Confirmed], + ['artist' => 'Rotterdam Brass', 'sub' => 2, 'status' => ArtistEngagementStatus::Requested], + ['artist' => 'Nachtwacht DJs', 'sub' => 1, 'status' => ArtistEngagementStatus::Option], + ['artist' => 'Nachtwacht DJs', 'sub' => 2, 'status' => ArtistEngagementStatus::Cancelled], + ['artist' => 'De Lichtbrigade', 'sub' => 0, 'status' => ArtistEngagementStatus::Draft], + ['artist' => 'De Lichtbrigade', 'sub' => 1, 'status' => ArtistEngagementStatus::Requested], + ['artist' => 'De Lichtbrigade', 'sub' => 2, 'status' => ArtistEngagementStatus::Option], + ]; + + $engagements = []; + foreach ($statusPlan as $idx => $plan) { + /** @var Event $subEvent */ + $subEvent = $subEvents[$plan['sub']]; + $artist = $artists[$plan['artist']]; + + $attrs = [ + 'artist_id' => $artist->id, + 'event_id' => $subEvent->id, + 'booking_status' => $plan['status'], + 'fee_currency' => 'EUR', + 'buma_applicable' => true, + 'buma_percentage' => 7.00, + 'buma_handled_by' => 'organisation', + 'vat_applicable' => true, + 'vat_percentage' => 21.00, + 'payment_status' => 'none', + 'crew_count' => 2, + 'guests_count' => 4, + 'advancing_completed_count' => 0, + 'advancing_total_count' => 0, + ]; + + if ($plan['status'] === ArtistEngagementStatus::Option) { + $attrs['option_expires_at'] = CarbonImmutable::now()->addDays(14); + } + if ($plan['status'] === ArtistEngagementStatus::Requested) { + $attrs['requested_at'] = CarbonImmutable::now()->subDays(3); + } + if ($plan['status'] === ArtistEngagementStatus::Contracted) { + $attrs['fee_amount'] = 7500.00; + } + + $engagements[$idx] = ArtistEngagement::create($attrs); + } + + // Performances (13) — most engagements get 1 perf, a couple + // get 2 (D17). One parked. One B2B pair on Mainstage Saturday. + $perfPlan = [ + // [engagementIdx, stageName|null, subIdx, hour, minute, duration] + [0, 'Mainstage', 0, 22, 0, 75], // Donker & Licht — Vrijdag mainstage + [1, 'Mainstage', 1, 23, 30, 60], // Donker & Licht — Zaterdag mainstage (B2B partner-A) + [1, 'Mainstage', 1, 21, 0, 60], // …extra perf same engagement (D17 multi-perf) + [2, 'Havana', 0, 21, 0, 60], + [3, 'Havana', 1, 22, 30, 60], + [4, 'Stairway', 1, 21, 0, 75], + [5, 'Socialite', 0, 19, 0, 60], + [6, 'Socialite', 2, 17, 0, 45], + [7, 'Mainstage', 1, 23, 33, 60], // B2B partner-B (3-min offset → seeds B2B detector) + [9, null, 0, 0, 0, 60], // De Lichtbrigade Vrijdag = parked / wachtrij + [10, 'Stairway', 1, 19, 0, 60], + [11, 'Havana', 2, 20, 30, 60], + [4, 'Stairway', 1, 22, 30, 60], // multi-perf on same engagement (D17) + ]; + + foreach ($perfPlan as $row) { + [$engagementIdx, $stageName, $subIdx, $hour, $minute, $minutes] = $row; + $engagement = $engagements[$engagementIdx]; + /** @var Event $subEvent */ + $subEvent = $subEvents[$subIdx]; + + $start = CarbonImmutable::parse($subEvent->start_date)->setTime($hour, $minute); + + Performance::create([ + 'engagement_id' => $engagement->id, + 'event_id' => $subEvent->id, + 'stage_id' => $stageName === null ? null : $stages[$stageName]->id, + 'lane' => 0, + 'start_at' => $start, + 'end_at' => $start->addMinutes($minutes), + 'version' => 0, + ]); + } + } +} diff --git a/api/database/seeders/DevSeeder.php b/api/database/seeders/DevSeeder.php index 7f32f070..f3e4f331 100644 --- a/api/database/seeders/DevSeeder.php +++ b/api/database/seeders/DevSeeder.php @@ -756,7 +756,7 @@ class DevSeeder extends Seeder foreach ($approvedPersons->shuffle() as $person) { $existing = $usedPersonSlots[$person->id] ?? []; - $available = $openShifts->filter(fn (Shift $shift) => !in_array($shift->time_slot_id, $existing)); + $available = $openShifts->filter(fn (Shift $shift) => ! in_array($shift->time_slot_id, $existing)); if ($available->isEmpty()) { continue; @@ -956,6 +956,15 @@ class DevSeeder extends Seeder FormBuilderDevSeeder::seedEventRegistrationShowcase($this->org, $festival, $this->command); } + // RFC-TIMETABLE v0.2 — artist + timetable fixture for the + // festival (4 stages, 6 artists, 12 engagements, 13 perfs). + ArtistTimetableDevSeeder::seedForFestival( + $this->org, + $festival, + [$vrijdag, $zaterdag, $zondag], + ); + $this->command->info(' Artist timetable: 4 stages, 12 stage_days, 6 artists, 12 engagements, 13 performances'); + $this->command->info(' Echt Feesten 2026 complete'); }); } @@ -1063,7 +1072,7 @@ class DevSeeder extends Seeder 'organisation_id' => $this->org->id, 'parent_event_id' => $ijsbaan->id, 'name' => $wd['name'], - 'slug' => 'ijsbaan-week-' . ($i + 1), + 'slug' => 'ijsbaan-week-'.($i + 1), 'start_date' => $wd['start'], 'end_date' => $wd['end'], 'timezone' => 'Europe/Amsterdam', @@ -1192,7 +1201,7 @@ class DevSeeder extends Seeder $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'); + $this->command->info(" {$personCount} persons, ".count($allShifts).' shifts created'); $formSchema = FormBuilderDevSeeder::seedEventSchema($ijsbaan); $submissions = FormBuilderDevSeeder::seedSubmissionsForEvent($ijsbaan, $formSchema);