>, updated_at: string|null} */ public function fromPlaylist(DisplayPlaylist $playlist): array { $playlist->loadMissing('modules.items'); return $this->fromModules($playlist->modules, $playlist->updated_at); } /** * @param Collection $modules * @return array{playlist: array>, updated_at: string|null} */ public function fromModules(Collection $modules, mixed $fallbackUpdatedAt = null): array { $playlist = $modules ->loadMissing('items') ->map(fn (DisplayVersion $module) => $this->moduleData($module)) ->filter() ->values() ->all(); return [ 'playlist' => $playlist, 'updated_at' => $this->updatedAt($modules, $fallbackUpdatedAt), ]; } /** * @return array|null */ private function moduleData(DisplayVersion $module): ?array { $items = $module->items ->where('is_active', true) ->sortBy('sort_order') ->values(); return match ($module->type->value) { 'video-display' => $this->videoDisplayData($module, $items), 'b2in' => $this->b2inData($module, $items), 'offers' => $this->offersData($module, $items), default => null, }; } /** * @param Collection $modules */ private function updatedAt(Collection $modules, mixed $fallbackUpdatedAt = null): ?string { $timestamps = collect([$fallbackUpdatedAt]) ->merge($modules->pluck('updated_at')) ->merge($modules->flatMap(fn (DisplayVersion $module) => $module->items->pluck('updated_at'))) ->filter(); return $timestamps->max()?->toIso8601String(); } /** * @param Collection $items * @return array */ private function videoDisplayData(DisplayVersion $module, Collection $items): array { $videos = $items->where('item_type', 'video')->values()->map(fn ($item) => [ 'src' => $this->mediaSource($item->content['filename'] ?? ''), 'position' => $item->content['position'] ?? 25, ]); $footerContent = $items->where('item_type', 'footer')->values()->map(function ($item) { $data = [ 'headline' => $item->content['headline'] ?? '', 'subline' => $item->content['subline'] ?? '', ]; if (! empty($item->content['url'])) { $data['url'] = $item->content['url']; } return $data; }); return [ 'type' => 'video-display', 'version_name' => $module->name, 'settings' => DisplayModuleSettings::merge($module->type, $module->settings), 'videoPlaylist' => $videos, 'footerContent' => $footerContent, ]; } private function mediaSource(string $source): string { if ($source === '' || str_starts_with($source, 'http') || str_starts_with($source, '/') || str_starts_with($source, '../')) { return $source; } return 'assets/'.$source; } /** * @param Collection $items * @return array */ private function b2inData(DisplayVersion $module, Collection $items): array { $mediaItems = $items->where('item_type', 'media')->values()->map(fn ($item) => [ 'id' => $item->id, 'category' => $item->content['category'] ?? 'immobilien', 'media_type' => $item->content['media_type'] ?? 'image', 'media_url' => $item->content['media_url'] ?? '', 'headline' => $item->content['headline'] ?? '', 'subline' => $item->content['subline'] ?? '', 'duration_seconds' => $item->content['duration_seconds'] ?? 10, 'sort_order' => $item->sort_order, 'is_active' => true, ]); return [ 'type' => 'b2in', 'version_name' => $module->name, 'settings' => DisplayModuleSettings::merge($module->type, $module->settings), 'items' => $mediaItems, ]; } /** * @param Collection $items * @return array */ private function offersData(DisplayVersion $module, Collection $items): array { $slides = $items->where('item_type', 'slide')->values()->map(function ($item) { $content = $item->content; return [ 'type' => $content['type'] ?? 'detail', 'show_logo' => $content['show_logo'] ?? true, 'logo_url' => $content['logo_url'] ?? '', 'brand_text' => $content['brand_text'] ?? '', 'duration' => $content['duration'] ?? 8000, 'image_url' => $content['image_url'] ?? '', 'badge_text' => $content['badge_text'] ?? '', 'show_badge' => $content['show_badge'] ?? ($content['badge_text'] ?? '') !== '', 'eyebrow' => $content['eyebrow'] ?? '', 'show_eyebrow' => $content['show_eyebrow'] ?? ($content['eyebrow'] ?? '') !== '', 'title' => $content['title'] ?? '', 'subline' => $content['subline'] ?? '', 'show_subline' => $content['show_subline'] ?? ($content['subline'] ?? '') !== '', 'price' => $content['price'] ?? '', 'original_price' => $content['original_price'] ?? '', 'strike_original_price' => $content['strike_original_price'] ?? false, 'tag_text' => $content['tag_text'] ?? '', 'show_price' => $content['show_price'] ?? ($content['price'] ?? '') !== '', 'bullets' => $content['bullets'] ?? [], 'show_bullets' => $content['show_bullets'] ?? ! empty($content['bullets']), 'disclaimer' => $content['disclaimer'] ?? '', 'show_disclaimer' => $content['show_disclaimer'] ?? ($content['disclaimer'] ?? '') !== '', 'qr_url' => $content['qr_url'] ?? '', 'qr_title' => $content['qr_title'] ?? '', 'show_qr' => $content['show_qr'] ?? ($content['qr_url'] ?? '') !== '', 'contact' => $content['contact'] ?? '', 'show_contact' => $content['show_contact'] ?? ($content['contact'] ?? '') !== '', 'brand_tagline' => $content['brand_tagline'] ?? '', ]; }); return [ 'type' => 'offers', 'version_name' => $module->name, 'settings' => DisplayModuleSettings::merge($module->type, $module->settings), 'slides' => $slides, ]; } }