[ 'title' => 'Почему мы', 'fields' => [ ['name' => 'label', 'label' => 'Надпись над заголовком', 'type' => 'text'], ['name' => 'heading', 'label' => 'Заголовок секции', 'type' => 'text'], ['name' => 'items', 'label' => 'Карточки преимуществ', 'type' => 'repeater', 'sub_fields' => [ ['name' => 'icon', 'label' => 'Иконка (emoji)', 'type' => 'text'], ['name' => 'title', 'label' => 'Заголовок карточки', 'type' => 'text'], ['name' => 'text', 'label' => 'Описание', 'type' => 'textarea'], ]], ], ], // --------------------------------------------------------------- // Блок «Этапы работ» — интерактивный JS-степпер // Шапка секции + repeater шагов (title, desc, image_url, image_alt) // Blade-шаблон сам генерирует nav-кнопки и STEPS-массив для JS // --------------------------------------------------------------- 'steps_section' => [ 'title' => 'Этапы работ (степпер)', 'fields' => [ ['name' => 'label', 'label' => 'Надпись над заголовком', 'type' => 'text'], ['name' => 'heading', 'label' => 'Заголовок секции', 'type' => 'text'], ['name' => 'subtext', 'label' => 'Описание секции', 'type' => 'textarea'], ['name' => 'steps', 'label' => 'Шаги', 'type' => 'repeater', 'sub_fields' => [ ['name' => 'title', 'label' => 'Название шага', 'type' => 'text'], ['name' => 'desc', 'label' => 'Описание шага', 'type' => 'textarea'], // aspect-ratio 4/3 из CSS .step-right → cover-кроп 800×600 ['name' => 'image_url', 'label' => 'Изображение', 'type' => 'image', 'width' => 800, 'height' => 600], ['name' => 'image_alt', 'label' => 'Alt изображения', 'type' => 'text'], ]], ], ], // --------------------------------------------------------------- // Блок «Главный баннер» — hero-секция с фото, заголовком, кнопками и статами // --------------------------------------------------------------- 'hero_banner' => [ 'title' => 'Главный баннер (Hero)', 'fields' => [ ['name' => 'eyebrow', 'label' => 'Надпись сверху (маленьким шрифтом)', 'type' => 'text'], ['name' => 'line1', 'label' => 'Заголовок — строка 1', 'type' => 'text'], ['name' => 'line2', 'label' => 'Заголовок — строка 2 (красная)', 'type' => 'text'], ['name' => 'line3', 'label' => 'Заголовок — строка 3', 'type' => 'text'], ['name' => 'subtext', 'label' => 'Подзаголовок', 'type' => 'textarea'], ['name' => 'btn1_text', 'label' => 'Кнопка 1 — текст', 'type' => 'text'], ['name' => 'btn1_url', 'label' => 'Кнопка 1 — ссылка', 'type' => 'url'], ['name' => 'btn2_text', 'label' => 'Кнопка 2 — текст', 'type' => 'text'], ['name' => 'btn2_url', 'label' => 'Кнопка 2 — ссылка', 'type' => 'url'], // aspect-ratio 16/9 → 1920×1080 ['name' => 'image', 'label' => 'Фоновое изображение', 'type' => 'image', 'width' => 1920, 'height' => 1080], ['name' => 'show_quick_search', 'label' => 'Показать форму быстрого подбора', 'type' => 'checkbox'], ['name' => 'stats', 'label' => 'Статистика внизу баннера', 'type' => 'repeater', 'sub_fields' => [ ['name' => 'value', 'label' => 'Число (напр. 500)', 'type' => 'text'], ['name' => 'suffix', 'label' => 'Суффикс красным (+ / к / лет)', 'type' => 'text'], ['name' => 'label', 'label' => 'Подпись', 'type' => 'text'], ]], ], ], // --------------------------------------------------------------- // Блок «Витрина автомобилей» — выбор конкретных авто из каталога // --------------------------------------------------------------- 'featured_cars' => [ 'title' => 'Витрина автомобилей', 'fields' => [ ['name' => 'label', 'label' => 'Надпись над заголовком', 'type' => 'text'], ['name' => 'heading', 'label' => 'Заголовок секции', 'type' => 'text'], // cars_picker — мультиселект авто из таблицы cars ['name' => 'car_ids', 'label' => 'Автомобили (выбрать из каталога)', 'type' => 'cars_picker'], ], ], // --------------------------------------------------------------- // Блок «Марки автомобилей» — сетка марок с выбором из БД // --------------------------------------------------------------- 'brands_grid' => [ 'title' => 'Сетка марок', 'fields' => [ ['name' => 'label', 'label' => 'Надпись над заголовком', 'type' => 'text'], ['name' => 'heading', 'label' => 'Заголовок секции', 'type' => 'text'], // makes_picker — чекбоксы марок из таблицы cars ['name' => 'makes', 'label' => 'Марки для отображения', 'type' => 'makes_picker'], ], ], // --------------------------------------------------------------- // Блок «CTA-полоса» — тёмный баннер с призывом к действию // --------------------------------------------------------------- 'cta_banner' => [ 'title' => 'CTA-полоса (призыв к действию)', 'fields' => [ ['name' => 'title', 'label' => 'Заголовок', 'type' => 'text'], ['name' => 'subtext', 'label' => 'Подзаголовок', 'type' => 'textarea'], ['name' => 'btn1_text', 'label' => 'Кнопка 1 — текст', 'type' => 'text'], ['name' => 'btn1_url', 'label' => 'Кнопка 1 — ссылка', 'type' => 'url'], ['name' => 'btn2_text', 'label' => 'Кнопка 2 — текст', 'type' => 'text'], ['name' => 'btn2_url', 'label' => 'Кнопка 2 — ссылка', 'type' => 'url'], ], ], // --------------------------------------------------------------- // Блок «Сетка услуг» — список услуг компании с ссылками на детальные страницы. // Режимы: выбрать конкретные услуги (services_picker) или показать последние N. // --------------------------------------------------------------- 'services_grid' => [ 'title' => 'Сетка услуг', 'fields' => [ ['name' => 'label', 'label' => 'Надпись над заголовком', 'type' => 'text'], ['name' => 'heading', 'label' => 'Заголовок секции', 'type' => 'text'], ['name' => 'subtext', 'label' => 'Описание секции', 'type' => 'textarea'], // services_picker — чекбоксы услуг; если пусто — берутся последние по limit ['name' => 'service_ids', 'label' => 'Конкретные услуги (пусто = последние N)', 'type' => 'services_picker'], ['name' => 'limit', 'label' => 'Максимум (если не выбраны конкретные)', 'type' => 'text'], ], ], // --------------------------------------------------------------- // Блок «Отзывы клиентов» — сетка карточек отзывов из таблицы reviews // Отзывы управляются через раздел «Отзывы» в AdminLTE (ReviewAdminController). // Здесь выбираются конкретные записи для вывода в блоке. // --------------------------------------------------------------- 'reviews' => [ 'title' => 'Отзывы клиентов', 'fields' => [ ['name' => 'label', 'label' => 'Надпись над заголовком', 'type' => 'text'], ['name' => 'heading', 'label' => 'Заголовок секции', 'type' => 'text'], // reviews_picker — выбор активных отзывов из таблицы reviews ['name' => 'review_ids', 'label' => 'Отзывы для отображения в блоке', 'type' => 'reviews_picker'], ], ], ]; // Сгенерированные макеты — файлы из app/Support/layouts/*.php $custom = []; $dir = app_path('Support/layouts'); if (is_dir($dir)) { foreach (glob($dir.'/*.php') as $file) { $key = pathinfo($file, PATHINFO_FILENAME); if (! isset($builtin[$key])) { $custom[$key] = require $file; } } } return array_merge($builtin, $custom); } // Получить определение макета по ключу (null если не найден) public static function get(string $key): ?array { return static::all()[$key] ?? null; } // Проверить, существует ли макет public static function exists(string $key): bool { return isset(static::all()[$key]); } // Список ключей для валидации (in:key1,key2,...) public static function keys(): array { return array_keys(static::all()); } }