*/ public function toArray(Request $request): array { return [ 'id' => $this->id, 'engagement_id' => $this->engagement_id, 'event_id' => $this->event_id, 'stage_id' => $this->stage_id, 'lane' => (int) $this->lane, 'lane_resolved' => $this->resolveLane(), 'start_at' => optional($this->start_at)->toIso8601String(), 'end_at' => optional($this->end_at)->toIso8601String(), 'version' => (int) $this->version, 'notes' => $this->notes, 'warnings' => $this->computeWarnings(), 'engagement' => ArtistEngagementResource::make($this->whenLoaded('engagement')), 'stage' => StageResource::make($this->whenLoaded('stage')), 'created_at' => optional($this->created_at)->toIso8601String(), 'updated_at' => optional($this->updated_at)->toIso8601String(), 'deleted_at' => optional($this->deleted_at)->toIso8601String(), ]; } /** * Computed via LaneResolver over the (stage, sub-event) cohort. * For parked performances (stage_id = null) the persisted lane is * surfaced as-is — the wachtrij is a flat list, not a lane grid. */ private function resolveLane(): int { if ($this->stage_id === null) { return (int) $this->lane; } $cohort = Performance::query() ->where('stage_id', $this->stage_id) ->where('event_id', $this->event_id) ->get(); $resolved = app(LaneResolver::class)->resolve($cohort); return $resolved[(string) $this->id] ?? (int) $this->lane; } /** * RFC v0.2 D5 / D6 / D25 — overlap, B2B, capacity warnings. * Naive implementation for Session 2; refined as the timetable * frontend lands in Session 4. * * @return array */ private function computeWarnings(): array { $warnings = []; if ($this->stage_id === null) { return $warnings; } $start = CarbonImmutable::instance($this->start_at); $end = CarbonImmutable::instance($this->end_at); $peers = Performance::query() ->where('stage_id', $this->stage_id) ->where('event_id', $this->event_id) ->where('id', '!=', $this->id) ->get(); foreach ($peers as $other) { $oStart = CarbonImmutable::instance($other->start_at); $oEnd = CarbonImmutable::instance($other->end_at); if ($start < $oEnd && $oStart < $end && (int) $other->lane === (int) $this->lane) { $warnings[] = 'overlap'; break; } } foreach ($peers as $other) { $oEnd = CarbonImmutable::instance($other->end_at); $oStart = CarbonImmutable::instance($other->start_at); if ($oEnd->equalTo($start) || $oStart->equalTo($end)) { $warnings[] = 'b2b'; break; } } return array_values(array_unique($warnings)); } }