>, 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' => array_replace([ 'qr_label' => 'Website', ], $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' => array_replace_recursive([ 'theme' => 'dark', 'header_logo_url' => '../assets/b2in-logo-positive.svg', 'header_claim' => 'Connecting Design & Property', 'footer_url' => 'B2in.eu', 'footer_name' => '', 'footer_prefix' => 'by', 'qr_url' => '', 'transition' => [ 'type' => 'crossfade', 'duration_ms' => 800, ], 'default_image_duration' => 10, ], $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(fn ($item) => [ 'type' => $item->content['type'] ?? 'product-hero', 'duration' => $item->content['duration'] ?? 8000, 'image_url' => $item->content['image_url'] ?? '', 'badge_text' => $item->content['badge_text'] ?? '', 'eyebrow' => $item->content['eyebrow'] ?? '', 'title' => $item->content['title'] ?? '', 'subline' => $item->content['subline'] ?? '', 'price' => $item->content['price'] ?? '', 'original_price' => $item->content['original_price'] ?? '', 'tag_text' => $item->content['tag_text'] ?? '', 'bullets' => $item->content['bullets'] ?? [], 'disclaimer' => $item->content['disclaimer'] ?? '', 'qr_url' => $item->content['qr_url'] ?? '', 'qr_title' => $item->content['qr_title'] ?? '', 'contact' => $item->content['contact'] ?? '', 'show_brand_text' => $item->content['show_brand_text'] ?? false, 'brand_tagline' => $item->content['brand_tagline'] ?? '', ]); return [ 'type' => 'offers', 'version_name' => $module->name, 'settings' => array_replace_recursive([ 'loop' => true, 'logo_url' => '../logo-cabinet-300.png', 'brand_text' => 'Bielefeld', 'footer_claim' => '', 'footer_url' => '', 'qr_default_title' => 'Kontakt', 'qr_subtitle' => 'QR scannen', 'transition' => [ 'type' => 'fade', 'duration' => 600, ], ], $module->settings ?? []), 'slides' => $slides, ]; } }