feat: festival/series model with sub-events, cross-event sections, tab navigation, SectionsShiftsPanel extraction

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-10 11:15:19 +02:00
parent 11b9f1d399
commit 10bd55b8ae
40 changed files with 3087 additions and 1080 deletions

View File

@@ -134,6 +134,96 @@ class FestivalSectionTest extends TestCase
$this->assertSoftDeleted('festival_sections', ['id' => $section->id]);
}
public function test_update_cross_org_returns_403(): void
{
$section = FestivalSection::factory()->create(['event_id' => $this->event->id]);
Sanctum::actingAs($this->outsider);
$response = $this->putJson("/api/v1/events/{$this->event->id}/sections/{$section->id}", [
'name' => 'Hacked',
]);
$response->assertForbidden();
}
public function test_destroy_cross_org_returns_403(): void
{
$section = FestivalSection::factory()->create(['event_id' => $this->event->id]);
Sanctum::actingAs($this->outsider);
$response = $this->deleteJson("/api/v1/events/{$this->event->id}/sections/{$section->id}");
$response->assertForbidden();
}
public function test_store_section_with_category_and_icon(): void
{
Sanctum::actingAs($this->orgAdmin);
$response = $this->postJson("/api/v1/events/{$this->event->id}/sections", [
'name' => 'Tapkraan',
'category' => 'Bar',
'icon' => 'tabler-beer',
'sort_order' => 0,
]);
$response->assertCreated()
->assertJson(['data' => [
'name' => 'Tapkraan',
'category' => 'Bar',
'icon' => 'tabler-beer',
]]);
$this->assertDatabaseHas('festival_sections', [
'event_id' => $this->event->id,
'name' => 'Tapkraan',
'category' => 'Bar',
'icon' => 'tabler-beer',
]);
}
public function test_section_categories_endpoint(): void
{
FestivalSection::factory()->create([
'event_id' => $this->event->id,
'category' => 'Bar',
]);
FestivalSection::factory()->create([
'event_id' => $this->event->id,
'category' => 'Podium',
]);
// Duplicate category should not appear twice
FestivalSection::factory()->create([
'event_id' => $this->event->id,
'category' => 'Bar',
]);
// Null category should not appear
FestivalSection::factory()->create([
'event_id' => $this->event->id,
'category' => null,
]);
Sanctum::actingAs($this->orgAdmin);
$response = $this->getJson("/api/v1/organisations/{$this->organisation->id}/section-categories");
$response->assertOk()
->assertJson(['data' => ['Bar', 'Podium']]);
$this->assertCount(2, $response->json('data'));
}
public function test_section_categories_cross_org_returns_403(): void
{
Sanctum::actingAs($this->outsider);
$response = $this->getJson("/api/v1/organisations/{$this->organisation->id}/section-categories");
$response->assertForbidden();
}
public function test_reorder_updates_sort_order(): void
{
$sectionA = FestivalSection::factory()->create([
@@ -148,20 +238,17 @@ class FestivalSectionTest extends TestCase
Sanctum::actingAs($this->orgAdmin);
$response = $this->postJson("/api/v1/events/{$this->event->id}/sections/reorder", [
'sections' => [
['id' => $sectionA->id, 'sort_order' => 2],
['id' => $sectionB->id, 'sort_order' => 1],
],
'sections' => [$sectionB->id, $sectionA->id],
]);
$response->assertOk();
$this->assertDatabaseHas('festival_sections', [
'id' => $sectionA->id,
'sort_order' => 2,
'id' => $sectionB->id,
'sort_order' => 0,
]);
$this->assertDatabaseHas('festival_sections', [
'id' => $sectionB->id,
'id' => $sectionA->id,
'sort_order' => 1,
]);
}