> */ public function linkableEvents(User $owner): array { return QrCode::query() ->where('user_id', $owner->id) ->where('type', QrCode::TYPE_EVENT) ->orderByDesc('updated_at') ->get() ->filter(function (QrCode $event) { $format = (string) ($event->content()['format'] ?? 'in_person'); return in_array($format, ['virtual', 'hybrid'], true); }) ->map(fn (QrCode $event) => $this->summarizeEvent($event)) ->values() ->all(); } /** * @param array $payload * @return array */ public function linkRoom(User $owner, QrCode $event, array $payload): array { abort_unless($event->user_id === $owner->id, 403); abort_unless($event->type === QrCode::TYPE_EVENT, 422); $format = (string) ($event->content()['format'] ?? 'in_person'); abort_unless(in_array($format, ['virtual', 'hybrid'], true), 422, 'Only virtual or hybrid events can be linked.'); $roomUuid = trim((string) ($payload['meet_room_uuid'] ?? '')); $roomType = in_array($payload['meet_room_type'] ?? '', ['town_hall', 'webinar'], true) ? (string) $payload['meet_room_type'] : 'town_hall'; abort_unless($roomUuid !== '', 422, 'meet_room_uuid is required.'); $joinUrl = trim((string) ($payload['join_url'] ?? '')); $title = trim((string) ($payload['title'] ?? $event->label ?? 'Virtual session')); $sessionId = trim((string) ($payload['virtual_session_id'] ?? '')); $content = $event->content(); $sessions = array_values(array_filter( (array) ($content['virtual_sessions'] ?? []), fn ($session) => is_array($session), )); $matchedIndex = null; if ($sessionId !== '') { foreach ($sessions as $index => $session) { if ((string) ($session['id'] ?? '') === $sessionId) { $matchedIndex = $index; break; } } } if ($matchedIndex === null) { foreach ($sessions as $index => $session) { $existingUuid = trim((string) ($session['meet_room_uuid'] ?? '')); $existingType = (string) ($session['meet_room_type'] ?? 'town_hall'); if ($existingUuid === '' && $existingType === $roomType) { $matchedIndex = $index; break; } } } if ($matchedIndex === null) { $sessionId = $sessionId !== '' ? $sessionId : 'vs-'.Str::lower(Str::random(12)); $sessions[] = [ 'id' => $sessionId, 'title' => $title, 'meet_room_type' => $roomType, 'scheduled_at' => $payload['scheduled_at'] ?? $content['starts_at'] ?? null, 'duration_minutes' => max(15, (int) ($payload['duration_minutes'] ?? 60)), ]; $matchedIndex = count($sessions) - 1; } else { $sessionId = (string) ($sessions[$matchedIndex]['id'] ?? $sessionId); } $sessions[$matchedIndex] = array_merge($sessions[$matchedIndex], [ 'id' => $sessionId, 'title' => $title !== '' ? $title : (string) ($sessions[$matchedIndex]['title'] ?? 'Virtual session'), 'meet_room_type' => $roomType, 'meet_room_uuid' => $roomUuid, 'join_url' => $joinUrl !== '' ? $joinUrl : ($sessions[$matchedIndex]['join_url'] ?? null), 'scheduled_at' => $payload['scheduled_at'] ?? $sessions[$matchedIndex]['scheduled_at'] ?? null, 'duration_minutes' => max(15, (int) ($payload['duration_minutes'] ?? ($sessions[$matchedIndex]['duration_minutes'] ?? 60))), ]); $payloadData = (array) ($event->payload ?? []); $payloadData['content'] = array_merge($content, ['virtual_sessions' => $sessions]); $event->payload = $payloadData; $event->save(); $event = $this->sync->sync($event->fresh(), $owner); return array_merge($this->summarizeEvent($event), [ 'entity_id' => $sessionId, 'meet_room_uuid' => $roomUuid, 'join_url' => $joinUrl, ]); } /** @return array */ private function summarizeEvent(QrCode $event): array { $content = $event->content(); $sessions = collect((array) ($content['virtual_sessions'] ?? [])) ->filter(fn ($session) => is_array($session)) ->map(fn (array $session) => [ 'id' => (string) ($session['id'] ?? ''), 'title' => (string) ($session['title'] ?? ''), 'meet_room_type' => (string) ($session['meet_room_type'] ?? 'town_hall'), 'meet_room_uuid' => ($uuid = trim((string) ($session['meet_room_uuid'] ?? ''))) !== '' ? $uuid : null, 'scheduled_at' => $session['scheduled_at'] ?? null, 'duration_minutes' => (int) ($session['duration_minutes'] ?? 60), ]) ->values() ->all(); return [ 'id' => $event->id, 'title' => (string) ($content['title'] ?? $event->label ?? 'Event'), 'format' => (string) ($content['format'] ?? 'in_person'), 'short_code' => $event->short_code, 'registration_url' => $event->publicUrl(), 'admin_url' => url('/events/'.$event->id.'/attendees'), 'edit_url' => url('/events/'.$event->id), 'virtual_sessions' => $sessions, ]; } }