Stas 1 месяц назад
Сommit
83df4433c6
100 измененных файлов с 16064 добавлено и 0 удалено
  1. BIN
      .DS_Store
  2. 18 0
      .editorconfig
  3. 65 0
      .env
  4. 65 0
      .env.example
  5. 2 0
      .npmrc
  6. 123 0
      app/Console/Commands/DownloadBrandLogos.php
  7. 181 0
      app/Http/Controllers/Admin/BlockAdminController.php
  8. 177 0
      app/Http/Controllers/Admin/BlockLayoutWizardController.php
  9. 24 0
      app/Http/Controllers/Admin/CacheAdminController.php
  10. 196 0
      app/Http/Controllers/Admin/CarController.php
  11. 158 0
      app/Http/Controllers/Admin/DashboardController.php
  12. 50 0
      app/Http/Controllers/Admin/LeadController.php
  13. 78 0
      app/Http/Controllers/Admin/LoginController.php
  14. 215 0
      app/Http/Controllers/Admin/ManualController.php
  15. 68 0
      app/Http/Controllers/Admin/PageAdminController.php
  16. 121 0
      app/Http/Controllers/Admin/PageSectionController.php
  17. 116 0
      app/Http/Controllers/Admin/ReviewAdminController.php
  18. 103 0
      app/Http/Controllers/Admin/ServiceAdminController.php
  19. 99 0
      app/Http/Controllers/Admin/SettingAdminController.php
  20. 113 0
      app/Http/Controllers/Admin/UserAdminController.php
  21. 109 0
      app/Http/Controllers/Admin/WebFormController.php
  22. 148 0
      app/Http/Controllers/CatalogController.php
  23. 10 0
      app/Http/Controllers/Controller.php
  24. 50 0
      app/Http/Controllers/FavoritesController.php
  25. 87 0
      app/Http/Controllers/FormSubmitController.php
  26. 101 0
      app/Http/Controllers/PageController.php
  27. 22 0
      app/Http/Controllers/ServiceController.php
  28. 32 0
      app/Http/Middleware/EnsureUserIsAdmin.php
  29. 33 0
      app/Http/Middleware/SiteMaintenanceMiddleware.php
  30. 46 0
      app/Http/Middleware/TrackPageVisit.php
  31. 50 0
      app/Mail/LeadNotification.php
  32. 61 0
      app/Models/Block.php
  33. 52 0
      app/Models/Car.php
  34. 39 0
      app/Models/DictSection.php
  35. 41 0
      app/Models/DictValue.php
  36. 36 0
      app/Models/Lead.php
  37. 40 0
      app/Models/Page.php
  38. 45 0
      app/Models/PageSection.php
  39. 31 0
      app/Models/Review.php
  40. 50 0
      app/Models/Service.php
  41. 55 0
      app/Models/Setting.php
  42. 82 0
      app/Models/User.php
  43. 13 0
      app/Models/UserPermission.php
  44. 35 0
      app/Models/WebForm.php
  45. 48 0
      app/Providers/AppServiceProvider.php
  46. 75 0
      app/Services/ImageUploadService.php
  47. 123 0
      app/Services/PermissionService.php
  48. 69 0
      app/Support/BlockLayoutParser.php
  49. 195 0
      app/Support/BlockLayoutRegistry.php
  50. 96 0
      app/Support/Traits/BlockLayoutBuilderTrait.php
  51. 45 0
      app/Support/Traits/BlockLayoutDetectorTrait.php
  52. 163 0
      app/Support/Traits/BlockLayoutHelpersTrait.php
  53. 0 0
      app/Support/layouts/.gitkeep
  54. 18 0
      artisan
  55. 14 0
      boost.json
  56. 22 0
      bootstrap/app.php
  57. 2 0
      bootstrap/cache/.gitignore
  58. 7 0
      bootstrap/providers.php
  59. 93 0
      composer.json
  60. 8774 0
      composer.lock
  61. 585 0
      config/adminlte.php
  62. 126 0
      config/app.php
  63. 117 0
      config/auth.php
  64. 130 0
      config/cache.php
  65. 184 0
      config/database.php
  66. 80 0
      config/filesystems.php
  67. 132 0
      config/logging.php
  68. 118 0
      config/mail.php
  69. 129 0
      config/queue.php
  70. 38 0
      config/services.php
  71. 233 0
      config/session.php
  72. 45 0
      database/factories/UserFactory.php
  73. 49 0
      database/migrations/0001_01_01_000000_create_users_table.php
  74. 35 0
      database/migrations/0001_01_01_000001_create_cache_table.php
  75. 57 0
      database/migrations/0001_01_01_000002_create_jobs_table.php
  76. 30 0
      database/migrations/2026_05_06_145556_add_is_admin_to_users_table.php
  77. 70 0
      database/migrations/2026_05_06_151148_create_cars_table.php
  78. 36 0
      database/migrations/2026_05_06_151148_create_dict_sections_table.php
  79. 39 0
      database/migrations/2026_05_06_151148_create_dict_values_table.php
  80. 26 0
      database/migrations/2026_05_06_173123_create_blocks_table.php
  81. 28 0
      database/migrations/2026_05_06_173123_create_pages_table.php
  82. 24 0
      database/migrations/2026_05_06_202015_create_settings_table.php
  83. 26 0
      database/migrations/2026_05_06_212903_add_role_to_users_table.php
  84. 33 0
      database/migrations/2026_05_06_212903_create_user_permissions_table.php
  85. 37 0
      database/migrations/2026_05_07_091033_create_web_forms_table.php
  86. 35 0
      database/migrations/2026_05_07_091034_create_leads_table.php
  87. 27 0
      database/migrations/2026_05_07_100000_create_page_sections_table.php
  88. 41 0
      database/migrations/2026_05_07_100001_alter_page_sections_add_content_type.php
  89. 25 0
      database/migrations/2026_05_07_110000_alter_blocks_add_layout_data.php
  90. 27 0
      database/migrations/2026_05_07_135048_add_logo_to_dict_values_table.php
  91. 34 0
      database/migrations/2026_05_07_135356_create_reviews_table.php
  92. 35 0
      database/migrations/2026_05_07_191559_create_services_table.php
  93. 26 0
      database/migrations/2026_05_07_193451_add_flag_to_dict_values_table.php
  94. 29 0
      database/migrations/2026_05_07_194433_create_page_visits_table.php
  95. 23 0
      database/migrations/2026_05_07_195525_mark_service_tags_as_system.php
  96. 54 0
      database/migrations/2026_05_08_100000_add_platform_to_cars_and_create_platforms_dict.php
  97. 30 0
      database/seeders/AdminSeeder.php
  98. 120 0
      database/seeders/CarGallerySeeder.php
  99. 121 0
      database/seeders/CarPhotosSeeder.php
  100. 46 0
      database/seeders/CarsFromDromSeeder.php

+ 18 - 0
.editorconfig

@@ -0,0 +1,18 @@
+root = true
+
+[*]
+charset = utf-8
+end_of_line = lf
+indent_size = 4
+indent_style = space
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[*.md]
+trim_trailing_whitespace = false
+
+[*.{yml,yaml}]
+indent_size = 2
+
+[{compose,docker-compose}.{yml,yaml}]
+indent_size = 4

+ 65 - 0
.env

@@ -0,0 +1,65 @@
+APP_NAME=Laravel
+APP_ENV=local
+APP_KEY=base64:Wa7VERBSduEDNeO5MW3GApZ3JIWnoW4RYQoLBD5cvbY=
+APP_DEBUG=true
+APP_URL=http://localhost:8000
+
+APP_LOCALE=en
+APP_FALLBACK_LOCALE=en
+APP_FAKER_LOCALE=en_US
+
+APP_MAINTENANCE_DRIVER=file
+# APP_MAINTENANCE_STORE=database
+
+# PHP_CLI_SERVER_WORKERS=4
+
+BCRYPT_ROUNDS=12
+
+LOG_CHANNEL=stack
+LOG_STACK=single
+LOG_DEPRECATIONS_CHANNEL=null
+LOG_LEVEL=debug
+
+DB_CONNECTION=mysql
+DB_HOST=127.0.0.1
+DB_PORT=3306
+DB_DATABASE=tocha_app
+DB_USERNAME=root
+DB_PASSWORD=
+
+SESSION_DRIVER=database
+SESSION_LIFETIME=120
+SESSION_ENCRYPT=false
+SESSION_PATH=/
+SESSION_DOMAIN=null
+
+BROADCAST_CONNECTION=log
+FILESYSTEM_DISK=local
+QUEUE_CONNECTION=database
+
+CACHE_STORE=database
+# CACHE_PREFIX=
+
+MEMCACHED_HOST=127.0.0.1
+
+REDIS_CLIENT=phpredis
+REDIS_HOST=127.0.0.1
+REDIS_PASSWORD=null
+REDIS_PORT=6379
+
+MAIL_MAILER=log
+MAIL_SCHEME=null
+MAIL_HOST=127.0.0.1
+MAIL_PORT=2525
+MAIL_USERNAME=null
+MAIL_PASSWORD=null
+MAIL_FROM_ADDRESS="hello@example.com"
+MAIL_FROM_NAME="${APP_NAME}"
+
+AWS_ACCESS_KEY_ID=
+AWS_SECRET_ACCESS_KEY=
+AWS_DEFAULT_REGION=us-east-1
+AWS_BUCKET=
+AWS_USE_PATH_STYLE_ENDPOINT=false
+
+VITE_APP_NAME="${APP_NAME}"

+ 65 - 0
.env.example

@@ -0,0 +1,65 @@
+APP_NAME=Laravel
+APP_ENV=local
+APP_KEY=
+APP_DEBUG=true
+APP_URL=http://localhost
+
+APP_LOCALE=en
+APP_FALLBACK_LOCALE=en
+APP_FAKER_LOCALE=en_US
+
+APP_MAINTENANCE_DRIVER=file
+# APP_MAINTENANCE_STORE=database
+
+# PHP_CLI_SERVER_WORKERS=4
+
+BCRYPT_ROUNDS=12
+
+LOG_CHANNEL=stack
+LOG_STACK=single
+LOG_DEPRECATIONS_CHANNEL=null
+LOG_LEVEL=debug
+
+DB_CONNECTION=sqlite
+# DB_HOST=127.0.0.1
+# DB_PORT=3306
+# DB_DATABASE=laravel
+# DB_USERNAME=root
+# DB_PASSWORD=
+
+SESSION_DRIVER=database
+SESSION_LIFETIME=120
+SESSION_ENCRYPT=false
+SESSION_PATH=/
+SESSION_DOMAIN=null
+
+BROADCAST_CONNECTION=log
+FILESYSTEM_DISK=local
+QUEUE_CONNECTION=database
+
+CACHE_STORE=database
+# CACHE_PREFIX=
+
+MEMCACHED_HOST=127.0.0.1
+
+REDIS_CLIENT=phpredis
+REDIS_HOST=127.0.0.1
+REDIS_PASSWORD=null
+REDIS_PORT=6379
+
+MAIL_MAILER=log
+MAIL_SCHEME=null
+MAIL_HOST=127.0.0.1
+MAIL_PORT=2525
+MAIL_USERNAME=null
+MAIL_PASSWORD=null
+MAIL_FROM_ADDRESS="hello@example.com"
+MAIL_FROM_NAME="${APP_NAME}"
+
+AWS_ACCESS_KEY_ID=
+AWS_SECRET_ACCESS_KEY=
+AWS_DEFAULT_REGION=us-east-1
+AWS_BUCKET=
+AWS_USE_PATH_STYLE_ENDPOINT=false
+
+VITE_APP_NAME="${APP_NAME}"

+ 2 - 0
.npmrc

@@ -0,0 +1,2 @@
+ignore-scripts=true
+audit=true

+ 123 - 0
app/Console/Commands/DownloadBrandLogos.php

@@ -0,0 +1,123 @@
+<?php
+
+namespace App\Console\Commands;
+
+/*
+ * Artisan-команда: php artisan brands:download-logos
+ *
+ * Скачивает логотипы марок автомобилей из simple-icons CDN и Wikimedia Commons
+ * и сохраняет в storage/app/public/makes/logos/.
+ * Обновляет поле logo в dict_values для совпавших марок (раздел code=makes).
+ *
+ * Повторный запуск безопасен: уже скачанные файлы не перезаписываются.
+ * Добавьте --force чтобы принудительно перескачать все логотипы.
+ */
+
+use App\Models\DictSection;
+use App\Models\DictValue;
+use Illuminate\Console\Command;
+use Illuminate\Support\Facades\Http;
+use Illuminate\Support\Facades\Storage;
+use Illuminate\Support\Str;
+
+class DownloadBrandLogos extends Command
+{
+    protected $signature = 'brands:download-logos {--force : Перезаписать существующие файлы}';
+
+    protected $description = 'Скачивает логотипы марок автомобилей из simple-icons CDN и Wikimedia';
+
+    // Карта: название марки → прямой URL для скачивания SVG
+    // simple-icons CDN для большинства, Wikimedia для остальных
+    private array $logoUrls = [
+        'Toyota' => 'https://cdn.jsdelivr.net/npm/simple-icons@latest/icons/toyota.svg',
+        'Nissan' => 'https://cdn.jsdelivr.net/npm/simple-icons@latest/icons/nissan.svg',
+        'Mitsubishi' => 'https://cdn.jsdelivr.net/npm/simple-icons@latest/icons/mitsubishi.svg',
+        'Honda' => 'https://cdn.jsdelivr.net/npm/simple-icons@latest/icons/honda.svg',
+        'Subaru' => 'https://cdn.jsdelivr.net/npm/simple-icons@latest/icons/subaru.svg',
+        'Mazda' => 'https://cdn.jsdelivr.net/npm/simple-icons@latest/icons/mazda.svg',
+        'Suzuki' => 'https://cdn.jsdelivr.net/npm/simple-icons@latest/icons/suzuki.svg',
+        'Lexus' => 'https://upload.wikimedia.org/wikipedia/commons/7/75/Lexus.svg',
+        'Infiniti' => 'https://upload.wikimedia.org/wikipedia/commons/c/c3/Infiniti_logo.svg',
+        'BMW' => 'https://cdn.jsdelivr.net/npm/simple-icons@latest/icons/bmw.svg',
+        'Mercedes-Benz' => 'https://cdn.jsdelivr.net/npm/simple-icons@latest/icons/mercedes.svg',
+        'Audi' => 'https://cdn.jsdelivr.net/npm/simple-icons@latest/icons/audi.svg',
+        'Volkswagen' => 'https://cdn.jsdelivr.net/npm/simple-icons@latest/icons/volkswagen.svg',
+        'Porsche' => 'https://cdn.jsdelivr.net/npm/simple-icons@latest/icons/porsche.svg',
+        'Land Rover' => 'https://cdn.jsdelivr.net/npm/simple-icons@latest/icons/landrover.svg',
+        'Jeep' => 'https://cdn.jsdelivr.net/npm/simple-icons@latest/icons/jeep.svg',
+        'Ford' => 'https://cdn.jsdelivr.net/npm/simple-icons@latest/icons/ford.svg',
+        'Chevrolet' => 'https://cdn.jsdelivr.net/npm/simple-icons@latest/icons/chevrolet.svg',
+        'Kia' => 'https://cdn.jsdelivr.net/npm/simple-icons@latest/icons/kia.svg',
+        'Hyundai' => 'https://cdn.jsdelivr.net/npm/simple-icons@latest/icons/hyundai.svg',
+        'Volvo' => 'https://cdn.jsdelivr.net/npm/simple-icons@latest/icons/volvo.svg',
+        'Skoda' => 'https://cdn.jsdelivr.net/npm/simple-icons@latest/icons/skoda.svg',
+        'Renault' => 'https://cdn.jsdelivr.net/npm/simple-icons@latest/icons/renault.svg',
+        'Tesla' => 'https://cdn.jsdelivr.net/npm/simple-icons@latest/icons/tesla.svg',
+        'Chery' => 'https://upload.wikimedia.org/wikipedia/commons/b/b6/Chery_logo.svg',
+        'Geely' => 'https://upload.wikimedia.org/wikipedia/commons/d/d4/Geely_logo.svg',
+    ];
+
+    public function handle(): int
+    {
+        $section = DictSection::where('code', 'makes')->first();
+        if (! $section) {
+            $this->error('Раздел справочника "makes" не найден.');
+
+            return 1;
+        }
+
+        Storage::disk('public')->makeDirectory('makes/logos');
+
+        $makes = DictValue::where('section_id', $section->id)
+            ->whereNull('parent_id')
+            ->get(['id', 'value', 'logo']);
+
+        $this->info("Найдено {$makes->count()} марок. Начинаю загрузку...");
+        $downloaded = 0;
+        $skipped = 0;
+
+        foreach ($makes as $make) {
+            $url = $this->logoUrls[$make->value] ?? null;
+
+            if (! $url) {
+                $this->line("  ⚠  {$make->value}: нет источника в карте логотипов");
+
+                continue;
+            }
+
+            $filename = 'makes/logos/'.Str::slug($make->value).'.svg';
+
+            // Пропускаем если файл уже есть и не --force
+            if (! $this->option('force') && $make->logo && Storage::disk('public')->exists($make->logo)) {
+                $this->line("  ✓  {$make->value}: пропущено (уже есть)");
+                $skipped++;
+
+                continue;
+            }
+
+            try {
+                $response = Http::timeout(15)
+                    ->withHeaders(['User-Agent' => 'TochaApp/1.0 (logo-downloader; contact@tocha.ru)'])
+                    ->get($url);
+
+                if (! $response->successful()) {
+                    $this->warn("  ✗  {$make->value}: HTTP {$response->status()} → {$url}");
+
+                    continue;
+                }
+
+                Storage::disk('public')->put($filename, $response->body());
+                $make->update(['logo' => $filename]);
+                $this->info("  ↓  {$make->value}: сохранён → {$filename}");
+                $downloaded++;
+
+            } catch (\Throwable $e) {
+                $this->warn("  ✗  {$make->value}: {$e->getMessage()}");
+            }
+        }
+
+        $this->info("Готово: скачано {$downloaded}, пропущено {$skipped}.");
+
+        return 0;
+    }
+}

+ 181 - 0
app/Http/Controllers/Admin/BlockAdminController.php

@@ -0,0 +1,181 @@
+<?php
+
+namespace App\Http\Controllers\Admin;
+
+/*
+ * BlockAdminController — управление блоками контента.
+ *
+ * Каждый блок привязан к макету (BlockLayoutRegistry), пользователь заполняет
+ * структурированные поля — никакой вёрстки в руках редактора.
+ * Данные хранятся в JSON-поле data, рендеринг — через Blade-шаблон blocks/{layout}.
+ *
+ * Изображения: загружаются через ImageUploadService, хранятся в storage/public/blocks/,
+ * ресайзятся по размерам из определения поля макета, конвертируются в WebP.
+ */
+
+use App\Http\Controllers\Controller;
+use App\Models\Block;
+use App\Services\ImageUploadService;
+use App\Support\BlockLayoutRegistry;
+use Illuminate\Http\RedirectResponse;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Cache;
+use Illuminate\View\View;
+
+class BlockAdminController extends Controller
+{
+    public function __construct(private ImageUploadService $images) {}
+
+    public function index(): View
+    {
+        $blocks = Block::orderBy('title')->get();
+
+        return view('admin.blocks.index', compact('blocks'));
+    }
+
+    public function create(): View
+    {
+        return view('admin.blocks.form', [
+            'block' => new Block,
+            'layouts' => BlockLayoutRegistry::all(),
+            'layoutDef' => null,
+        ]);
+    }
+
+    public function store(Request $request): RedirectResponse
+    {
+        $validated = $request->validate([
+            'name' => 'required|string|max:100|regex:/^[a-z0-9_]+$/|unique:blocks,name',
+            'title' => 'required|string|max:255',
+            'layout' => 'required|string|in:'.implode(',', BlockLayoutRegistry::keys()),
+            'is_active' => 'nullable|boolean',
+        ]);
+
+        $validated['is_active'] = $request->boolean('is_active');
+        $validated['data'] = $this->buildData($request, $validated['layout'], null);
+
+        Block::create($validated);
+
+        return redirect()->route('admin.blocks.index')
+            ->with('success', 'Блок «'.$validated['title'].'» создан.');
+    }
+
+    public function edit(Block $block): View
+    {
+        return view('admin.blocks.form', [
+            'block' => $block,
+            'layouts' => BlockLayoutRegistry::all(),
+            'layoutDef' => BlockLayoutRegistry::get($block->layout ?? ''),
+        ]);
+    }
+
+    public function update(Request $request, Block $block): RedirectResponse
+    {
+        $validated = $request->validate([
+            'title' => 'required|string|max:255',
+            'is_active' => 'nullable|boolean',
+        ]);
+
+        $validated['is_active'] = $request->boolean('is_active');
+        $validated['data'] = $this->buildData($request, $block->layout, $block->data ?? []);
+
+        $block->update($validated);
+
+        Cache::forget('block.'.$block->name);
+        // Cache::tags() не поддерживается database-драйвером — сбрасываем секции всех страниц явно
+        foreach (['home', 'services', 'contacts', 'privacy', 'offer'] as $slug) {
+            Cache::forget('page_sections.'.$slug);
+            Cache::forget('page.'.$slug);
+        }
+
+        return redirect()->route('admin.blocks.index')
+            ->with('success', 'Блок «'.$block->title.'» обновлён.');
+    }
+
+    public function destroy(Block $block): RedirectResponse
+    {
+        Cache::forget('block.'.$block->name);
+        $title = $block->title;
+        $block->delete();
+
+        return redirect()->route('admin.blocks.index')
+            ->with('success', 'Блок «'.$title.'» удалён.');
+    }
+
+    // ── Сборка data: текстовые поля + загруженные изображения ────────────
+
+    private function buildData(Request $request, ?string $layoutKey, ?array $oldData): array
+    {
+        $layoutDef = $layoutKey ? BlockLayoutRegistry::get($layoutKey) : null;
+
+        if (! $layoutDef) {
+            return [];
+        }
+
+        $raw = $request->input('data', []);
+        $uploads = $request->file('uploads', []);
+        $result = [];
+
+        foreach ($layoutDef['fields'] as $field) {
+            $name = $field['name'];
+
+            if ($field['type'] === 'repeater') {
+                $rows = array_values($raw[$name] ?? []);
+                $oldRows = $oldData[$name] ?? [];
+
+                $result[$name] = array_map(function (array $row, int $i) use ($field, $uploads, $name, $oldRows, $layoutKey) {
+                    $clean = [];
+                    foreach ($field['sub_fields'] as $sub) {
+                        $subName = $sub['name'];
+                        $oldVal = $oldRows[$i][$subName] ?? '';
+                        $uploadFile = data_get($uploads, "{$name}.{$i}.{$subName}");
+
+                        if ($sub['type'] === 'image' && $uploadFile?->isValid()) {
+                            $this->images->delete($oldVal);
+                            $clean[$subName] = $this->images->store($uploadFile, $layoutKey, $sub);
+                        } else {
+                            $clean[$subName] = $sub['type'] === 'image'
+                                ? ($row[$subName] ?? $oldVal)     // hidden input сохраняет старый путь
+                                : trim($row[$subName] ?? '');
+                        }
+                    }
+
+                    return $clean;
+                }, $rows, array_keys($rows));
+
+            } elseif ($field['type'] === 'checkbox') {
+                // Булев флаг: hidden-поле даёт "0", чекбокс при отмеченном даёт "1"
+                $result[$name] = (bool) ($raw[$name] ?? false);
+
+            } elseif (in_array($field['type'], ['cars_picker', 'makes_picker'])) {
+                // Мультиселект — хранится как plain array значений
+                $result[$name] = array_values(array_filter((array) ($raw[$name] ?? [])));
+
+            } elseif ($field['type'] === 'reviews_picker') {
+                // Мультиселект отзывов — хранится как plain array целочисленных ID
+                $result[$name] = array_values(array_map('intval', array_filter((array) ($raw[$name] ?? []))));
+
+            } elseif ($field['type'] === 'services_picker') {
+                // Мультиселект услуг — plain array целочисленных ID
+                $result[$name] = array_values(array_map('intval', array_filter((array) ($raw[$name] ?? []))));
+
+            } elseif ($field['type'] === 'image') {
+                $oldVal = $oldData[$name] ?? '';
+                $uploadFile = data_get($uploads, $name);
+
+                if ($uploadFile?->isValid()) {
+                    $this->images->delete($oldVal);
+                    $result[$name] = $this->images->store($uploadFile, $layoutKey, $field);
+                } else {
+                    // Сохраняем путь из hidden-input (не трогаем старый файл)
+                    $result[$name] = $raw[$name] ?? $oldVal;
+                }
+
+            } else {
+                $result[$name] = trim($raw[$name] ?? '');
+            }
+        }
+
+        return $result;
+    }
+}

+ 177 - 0
app/Http/Controllers/Admin/BlockLayoutWizardController.php

@@ -0,0 +1,177 @@
+<?php
+
+namespace App\Http\Controllers\Admin;
+
+/*
+ * BlockLayoutWizardController — мастер генерации макетов блоков.
+ *
+ * Шаг 1: пользователь вставляет HTML-фрагмент секции.
+ * Шаг 2: парсер определяет поля, показывает черновик Blade и таблицу полей для правки.
+ * Шаг 3: сохраняем два файла и редиректим на создание блока.
+ *
+ * Генерируемые файлы:
+ *   app/Support/layouts/{name}.php              — определение полей для реестра
+ *   resources/views/blocks/{name}.blade.php     — Blade-шаблон для рендера
+ */
+
+use App\Http\Controllers\Controller;
+use App\Support\BlockLayoutParser;
+use App\Support\BlockLayoutRegistry;
+use Illuminate\Http\RedirectResponse;
+use Illuminate\Http\Request;
+use Illuminate\View\View;
+
+class BlockLayoutWizardController extends Controller
+{
+    // Шаг 1: форма ввода HTML
+    public function index(): View
+    {
+        return view('admin.blocks.wizard', ['parsed' => null]);
+    }
+
+    // Шаг 2: парсим HTML, показываем обнаруженные поля и черновик Blade
+    public function analyze(Request $request): View
+    {
+        $request->validate([
+            'layout_name'  => 'required|string|max:100|regex:/^[a-z0-9_]+$/',
+            'layout_title' => 'required|string|max:255',
+            'html'         => 'required|string',
+        ]);
+
+        $parsed = (new BlockLayoutParser)->parse($request->input('html'));
+
+        return view('admin.blocks.wizard', [
+            'parsed'        => $parsed,
+            'layout_name'   => $request->input('layout_name'),
+            'layout_title'  => $request->input('layout_title'),
+            'original_html' => $request->input('html'),
+        ]);
+    }
+
+    // Шаг 3: сохраняем файлы макета и Blade-шаблона
+    public function generate(Request $request): RedirectResponse
+    {
+        $request->validate([
+            'layout_name'  => 'required|string|max:100|regex:/^[a-z0-9_]+$/',
+            'layout_title' => 'required|string|max:255',
+            'blade'        => 'required|string',
+        ]);
+
+        $name  = $request->input('layout_name');
+        $title = $request->input('layout_title');
+
+        // Защита: не перезаписываем встроенные макеты
+        if (isset(BlockLayoutRegistry::all()[$name]) && !file_exists(app_path("Support/layouts/{$name}.php"))) {
+            return back()->withInput()->withErrors(['layout_name' => 'Такое имя уже занято встроенным макетом.']);
+        }
+
+        // Собираем определение полей из submitted-данных формы
+        $fields = $this->buildFieldsFromRequest($request);
+
+        // Сохраняем app/Support/layouts/{name}.php
+        $layoutPhp = $this->renderLayoutPhp($title, $fields);
+        file_put_contents(app_path("Support/layouts/{$name}.php"), $layoutPhp);
+
+        // Сохраняем resources/views/blocks/{name}.blade.php
+        $bladeContent = "{{-- Blade-шаблон блока «{$title}». Сгенерирован мастером макетов. --}}\n"
+            . trim($request->input('blade')) . "\n";
+        file_put_contents(resource_path("views/blocks/{$name}.blade.php"), $bladeContent);
+
+        // Сбрасываем кеш скомпилированных вьюх
+        array_map('unlink', glob(storage_path('framework/views/*.php')));
+
+        return redirect()->route('admin.blocks.create')
+            ->with('success', "Макет «{$title}» создан. Теперь создайте блок на его основе.");
+    }
+
+    // Собирает поля из multipart-данных формы шага 2
+    private function buildFieldsFromRequest(Request $request): array
+    {
+        $isRepeater    = (bool) $request->input('is_repeater', false);
+        $rawFlatFields = $request->input('flat_fields', []);
+
+        $flatFields = array_values(array_filter(
+            array_map(fn($f) => [
+                'name'  => trim($f['name']  ?? ''),
+                'label' => trim($f['label'] ?? ''),
+                'type'  => $f['type'] ?? 'text',
+            ], $rawFlatFields),
+            fn($f) => $f['name'] !== ''
+        ));
+
+        if (!$isRepeater) {
+            return $flatFields;
+        }
+
+        // Repeater: плоские поля перед/после + repeater-поле
+        $subRaw = $request->input('sub_fields', []);
+        $subFields = array_values(array_filter(
+            array_map(fn($f) => [
+                'name'  => trim($f['name']  ?? ''),
+                'label' => trim($f['label'] ?? ''),
+                'type'  => $f['type'] ?? 'text',
+            ], $subRaw),
+            fn($f) => $f['name'] !== ''
+        ));
+
+        $repeaterField = [
+            'name'       => trim($request->input('repeater_name', 'items')),
+            'label'      => trim($request->input('repeater_label', 'Элементы')),
+            'type'       => 'repeater',
+            'sub_fields' => $subFields,
+        ];
+
+        // Плоские поля (если есть) идут перед repeater
+        return array_merge($flatFields, [$repeaterField]);
+    }
+
+    // Рендерит PHP-файл определения макета
+    private function renderLayoutPhp(string $title, array $fields): string
+    {
+        $fieldsExport = $this->varExportPretty($fields, 1);
+
+        return <<<PHP
+<?php
+
+// Сгенерирован мастером макетов tocha_app
+return [
+    'title'  => {$this->exportStr($title)},
+    'fields' => {$fieldsExport},
+];
+PHP;
+    }
+
+    // Аккуратный var_export с отступами вместо стандартного
+    private function varExportPretty(mixed $value, int $depth = 0): string
+    {
+        $pad = str_repeat('    ', $depth);
+        $pad1 = str_repeat('    ', $depth + 1);
+
+        if (is_string($value)) {
+            return $this->exportStr($value);
+        }
+
+        if (!is_array($value)) {
+            return var_export($value, true);
+        }
+
+        if (empty($value)) {
+            return '[]';
+        }
+
+        $isList = array_keys($value) === range(0, count($value) - 1);
+        $lines  = [];
+
+        foreach ($value as $k => $v) {
+            $key     = $isList ? '' : $this->exportStr((string) $k) . ' => ';
+            $lines[] = $pad1 . $key . $this->varExportPretty($v, $depth + 1);
+        }
+
+        return "[\n" . implode(",\n", $lines) . ",\n{$pad}]";
+    }
+
+    private function exportStr(string $s): string
+    {
+        return "'" . addcslashes($s, "'\\") . "'";
+    }
+}

+ 24 - 0
app/Http/Controllers/Admin/CacheAdminController.php

@@ -0,0 +1,24 @@
+<?php
+
+namespace App\Http\Controllers\Admin;
+
+/*
+ * CacheAdminController — ручное управление кешем из панели администратора.
+ *
+ * Маршрут: POST /admin/cache/clear
+ * Сбрасывает всё приложение (Cache::flush()), включая кеш страниц и каталога.
+ */
+
+use App\Http\Controllers\Controller;
+use Illuminate\Http\RedirectResponse;
+use Illuminate\Support\Facades\Cache;
+
+class CacheAdminController extends Controller
+{
+    public function clear(): RedirectResponse
+    {
+        Cache::flush();
+
+        return back()->with('success', 'Кеш успешно очищен.');
+    }
+}

+ 196 - 0
app/Http/Controllers/Admin/CarController.php

@@ -0,0 +1,196 @@
+<?php
+
+/*
+ * CarController — CRUD-контроллер для управления автомобилями в административной панели.
+ *
+ * Создан: 2026-05-06
+ * Маршруты: resource /admin/cars (index, create, store, edit, update, destroy)
+ * Защита: middleware 'admin' → EnsureUserIsAdmin — только авторизованные администраторы
+ * Зависимости: модель Car (app/Models/Car.php), DictSection (справочники для форм)
+ */
+
+namespace App\Http\Controllers\Admin;
+
+use App\Http\Controllers\Controller;
+use App\Models\Car;
+use App\Models\DictSection;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Cache;
+use Illuminate\Support\Facades\Storage;
+
+class CarController extends Controller
+{
+    // Список с фильтрацией: текст (марка/модель/VIN/заголовок) + поля (статус, марка и др.)
+    // paginate(20)->withQueryString() — сохраняет фильтры в ссылках страниц
+    // Вьюха: resources/views/admin/cars/index.blade.php
+    public function index(Request $request)
+    {
+        $query = Car::query()->orderByDesc('id');
+
+        if ($request->filled('q')) {
+            $q = '%'.$request->q.'%';
+            $query->where(function ($qb) use ($q) {
+                $qb->where('make', 'like', $q)
+                    ->orWhere('model', 'like', $q)
+                    ->orWhere('title', 'like', $q)
+                    ->orWhere('vin', 'like', $q);
+            });
+        }
+
+        foreach (['status', 'condition', 'make', 'engine_type', 'transmission', 'drive', 'platform'] as $field) {
+            if ($request->filled($field)) {
+                $query->where($field, $request->input($field));
+            }
+        }
+
+        $cars = $query->paginate(20)->withQueryString();
+
+        $makes = Car::select('make')->distinct()->orderBy('make')->pluck('make');
+
+        // Площадки строго из справочника — чтобы отображались все, даже без привязанных авто
+        $platformSection = DictSection::where('code', 'platforms')->with('values')->first();
+        $platforms = $platformSection ? $platformSection->values->pluck('value') : collect();
+
+        return view('admin.cars.index', compact('cars', 'makes', 'platforms'));
+    }
+
+    // Форма создания нового автомобиля
+    // keyBy('code') — индексирует коллекцию по полю code для удобного доступа в шаблоне: $dictSections->get('makes')
+    // Вьюха: resources/views/admin/cars/form.blade.php (общая для create и edit)
+    public function create()
+    {
+        $dictSections = DictSection::with('values')->orderBy('sort_order')->get()->keyBy('code');
+
+        return view('admin.cars.form', compact('dictSections'));
+    }
+
+    // Сохранение нового автомобиля: validated() → Car::create() → handlePhotos() → редирект
+    public function store(Request $request)
+    {
+        $data = $this->validated($request);
+        $car = Car::create($data);
+
+        $this->handlePhotos($request, $car);
+        Cache::flush(); // каталог с фильтрами — сбрасываем весь кеш при изменении авто
+
+        return redirect()->route('admin.cars.index')->with('success', 'Автомобиль добавлен.');
+    }
+
+    // Форма редактирования: $car передаётся через route model binding по {car} в URL
+    // Та же вьюха form.blade.php — внутри проверяется isset($car)
+    public function edit(Car $car)
+    {
+        $dictSections = DictSection::with('values')->orderBy('sort_order')->get()->keyBy('code');
+
+        return view('admin.cars.form', compact('car', 'dictSections'));
+    }
+
+    // Обновление данных автомобиля: логика аналогична store()
+    public function update(Request $request, Car $car)
+    {
+        $data = $this->validated($request);
+        $car->update($data);
+
+        $this->handlePhotos($request, $car);
+        Cache::flush(); // каталог с фильтрами — сбрасываем весь кеш при изменении авто
+
+        return redirect()->route('admin.cars.index')->with('success', 'Автомобиль обновлён.');
+    }
+
+    // Удаление: сначала удаляет файлы с диска, затем запись из БД
+    // photo_main и photos_gallery (JSON-массив) удаляются из Storage::disk('public')
+    public function destroy(Car $car)
+    {
+        if ($car->photo_main) {
+            Storage::disk('public')->delete($car->photo_main);
+        }
+        if ($car->photos_gallery) {
+            foreach ($car->photos_gallery as $path) {
+                Storage::disk('public')->delete($path);
+            }
+        }
+
+        $car->delete();
+        Cache::flush();
+
+        return redirect()->route('admin.cars.index')->with('success', 'Автомобиль удалён.');
+    }
+
+    // Правила валидации для формы автомобиля — вынесены в приватный метод
+    // чтобы не дублировать между store() и update(); фото-поля здесь не включены
+    private function validated(Request $request): array
+    {
+        return $request->validate([
+            'status' => 'required|in:active,sold,draft',
+            'condition' => 'required|in:new,used',
+            'make' => 'required|string|max:64',
+            'model' => 'required|string|max:64',
+            'generation' => 'nullable|string|max:64',
+            'year' => 'required|integer|min:1900|max:2100',
+            'vin' => 'nullable|string|max:17',
+            'plate' => 'nullable|string|max:20',
+            'body_type' => 'nullable|string|max:32',
+            'doors' => 'nullable|integer|min:1|max:10',
+            'color_exterior' => 'nullable|string|max:32',
+            'color_interior' => 'nullable|string|max:32',
+            'engine_type' => 'nullable|in:petrol,diesel,hybrid,electric,gas,other',
+            'engine_volume' => 'nullable|numeric|min:0|max:99',
+            'engine_power_hp' => 'nullable|integer|min:0|max:5000',
+            'transmission' => 'nullable|in:manual,automatic,robot,variator,electric',
+            'drive' => 'nullable|in:FWD,RWD,AWD,4WD',
+            'mileage_km' => 'nullable|integer|min:0',
+            'steering' => 'required|in:left,right',
+            'owners_count' => 'nullable|integer|min:0',
+            'customs_cleared' => 'nullable|boolean',
+            'pts' => 'nullable|in:original,duplicate,electronic',
+            'accident_free' => 'nullable|boolean',
+            'price_usd' => 'nullable|integer|min:0',
+            'price_rub' => 'nullable|integer|min:0',
+            'price_vladivostok' => 'nullable|integer|min:0',
+            'price_moscow' => 'nullable|integer|min:0',
+            'price_negotiable' => 'nullable|boolean',
+            'country_origin' => 'nullable|string|max:64',
+            'city' => 'nullable|string|max:64',
+            'platform' => 'required|string|max:64',
+            'options' => 'nullable|array',
+            'title' => 'nullable|string|max:128',
+            'description' => 'nullable|string',
+        ]);
+    }
+
+    // Загрузка и обновление фото: вызывается из store() и update() после сохранения записи
+    // 1. photo_main — старый файл удаляется, новый → storage/app/public/cars/{id}/main/
+    // 2. photos_gallery — новые фото добавляются к JSON-массиву → cars/{id}/gallery/
+    // 3. delete_gallery — пути из формы → удаление файла + фильтрация массива + array_values()
+    // Доступ через web: /storage/cars/... (требует: php artisan storage:link)
+    private function handlePhotos(Request $request, Car $car): void
+    {
+        if ($request->hasFile('photo_main') && $request->file('photo_main')->isValid()) {
+            if ($car->photo_main) {
+                Storage::disk('public')->delete($car->photo_main);
+            }
+            $path = $request->file('photo_main')->store("cars/{$car->id}/main", 'public');
+            $car->update(['photo_main' => $path]);
+        }
+
+        if ($request->hasFile('photos_gallery')) {
+            $gallery = $car->photos_gallery ?? [];
+            foreach ($request->file('photos_gallery') as $file) {
+                if ($file->isValid()) {
+                    $gallery[] = $file->store("cars/{$car->id}/gallery", 'public');
+                }
+            }
+            $car->update(['photos_gallery' => $gallery]);
+        }
+
+        if ($request->filled('delete_gallery')) {
+            $toDelete = (array) $request->input('delete_gallery');
+            $gallery = $car->photos_gallery ?? [];
+            foreach ($toDelete as $path) {
+                Storage::disk('public')->delete($path);
+                $gallery = array_filter($gallery, fn ($p) => $p !== $path);
+            }
+            $car->update(['photos_gallery' => array_values($gallery)]);
+        }
+    }
+}

+ 158 - 0
app/Http/Controllers/Admin/DashboardController.php

@@ -0,0 +1,158 @@
+<?php
+
+namespace App\Http\Controllers\Admin;
+
+/*
+ * DashboardController — главная страница административной панели.
+ * Собирает метрики: заявки, посетители, состояние системы, последние события.
+ */
+
+use App\Http\Controllers\Controller;
+use App\Models\Car;
+use App\Models\Lead;
+use App\Models\Review;
+use App\Models\Service;
+use Illuminate\Support\Carbon;
+use Illuminate\Support\Facades\Cache;
+use Illuminate\Support\Facades\DB;
+use Illuminate\View\View;
+
+class DashboardController extends Controller
+{
+    public function index(): View
+    {
+        $today     = now()->toDateString();
+        $yesterday = now()->subDay()->toDateString();
+
+        // ── Заявки ────────────────────────────────────────────────────────
+        $leadsToday     = Lead::whereDate('created_at', $today)->count();
+        $leadsYesterday = Lead::whereDate('created_at', $yesterday)->count();
+        $leadsTotal     = Lead::count();
+        $leadsUnread    = Lead::whereNull('read_at')->count();
+
+        // График заявок за 14 дней
+        $leadsChart = $this->chartData(
+            Lead::class,
+            'created_at',
+            14,
+            'Заявки'
+        );
+
+        // Последние 6 заявок
+        $recentLeads = Lead::latest()->limit(6)->get();
+
+        // ── Посетители ────────────────────────────────────────────────────
+        $visitRow      = DB::table('page_visits')->where('date', $today)->first();
+        $visitYesterday = DB::table('page_visits')->where('date', $yesterday)->first();
+        $visitsToday   = $visitRow?->views ?? 0;
+        $uniqueToday   = $visitRow?->unique_ips ?? 0;
+
+        // График посетителей за 14 дней
+        $visitsChart = $this->visitsChartData(14);
+
+        // ── Контент ───────────────────────────────────────────────────────
+        $carsTotal     = Car::count();
+        $servicesTotal = Service::count();
+        $reviewsTotal  = Review::count();
+
+        // ── Скорость / система ────────────────────────────────────────────
+        $systemInfo = $this->systemInfo();
+
+        // ── Средняя скорость ответа (самозамер) ───────────────────────────
+        $responseTime = $this->measureSelfResponseTime();
+
+        return view('admin.dashboard', compact(
+            'leadsToday', 'leadsYesterday', 'leadsTotal', 'leadsUnread',
+            'leadsChart', 'recentLeads',
+            'visitsToday', 'uniqueToday', 'visitYesterday',
+            'visitsChart',
+            'carsTotal', 'servicesTotal', 'reviewsTotal',
+            'systemInfo', 'responseTime'
+        ));
+    }
+
+    // ── Приватные хелперы ─────────────────────────────────────────────────
+
+    private function chartData(string $model, string $column, int $days, string $label): array
+    {
+        $start = now()->subDays($days - 1)->startOfDay();
+
+        $rows = DB::table((new $model)->getTable())
+            ->selectRaw("DATE($column) as d, COUNT(*) as cnt")
+            ->where($column, '>=', $start)
+            ->groupBy('d')
+            ->pluck('cnt', 'd');
+
+        $labels = [];
+        $data   = [];
+
+        for ($i = $days - 1; $i >= 0; $i--) {
+            $date     = now()->subDays($i)->toDateString();
+            $labels[] = now()->subDays($i)->format('d.m');
+            $data[]   = (int) ($rows[$date] ?? 0);
+        }
+
+        return compact('labels', 'data', 'label');
+    }
+
+    private function visitsChartData(int $days): array
+    {
+        $start = now()->subDays($days - 1)->toDateString();
+
+        $rows = DB::table('page_visits')
+            ->where('date', '>=', $start)
+            ->orderBy('date')
+            ->pluck('views', 'date');
+
+        $labels = [];
+        $data   = [];
+
+        for ($i = $days - 1; $i >= 0; $i--) {
+            $date     = now()->subDays($i)->toDateString();
+            $labels[] = now()->subDays($i)->format('d.m');
+            $data[]   = (int) ($rows[$date] ?? 0);
+        }
+
+        return ['labels' => $labels, 'data' => $data, 'label' => 'Просмотры'];
+    }
+
+    private function systemInfo(): array
+    {
+        $cacheDriver = config('cache.default', 'file');
+        $opcache     = function_exists('opcache_get_status') ? opcache_get_status(false) : null;
+
+        return [
+            'php'           => PHP_VERSION,
+            'laravel'       => app()->version(),
+            'cache_driver'  => $cacheDriver,
+            'opcache'       => $opcache && ($opcache['opcache_enabled'] ?? false),
+            'opcache_hits'  => $opcache ? ($opcache['opcache_statistics']['hits'] ?? 0) : 0,
+            'opcache_misses'=> $opcache ? ($opcache['opcache_statistics']['misses'] ?? 0) : 0,
+            'disk_free'     => $this->humanBytes(disk_free_space(base_path())),
+            'disk_total'    => $this->humanBytes(disk_total_space(base_path())),
+            'disk_pct'      => round((1 - disk_free_space(base_path()) / disk_total_space(base_path())) * 100),
+            'memory_limit'  => ini_get('memory_limit'),
+            'max_upload'    => ini_get('upload_max_filesize'),
+        ];
+    }
+
+    private function measureSelfResponseTime(): ?int
+    {
+        // Берём время старта текущего запроса как proxy метрики
+        if (defined('LARAVEL_START')) {
+            return (int) ((microtime(true) - LARAVEL_START) * 1000);
+        }
+        return null;
+    }
+
+    private function humanBytes(float $bytes): string
+    {
+        foreach (['Б', 'КБ', 'МБ', 'ГБ', 'ТБ'] as $unit) {
+            if ($bytes < 1024) {
+                return round($bytes, 1) . ' ' . $unit;
+            }
+            $bytes /= 1024;
+        }
+        return round($bytes, 1) . ' ПБ';
+    }
+}

+ 50 - 0
app/Http/Controllers/Admin/LeadController.php

@@ -0,0 +1,50 @@
+<?php
+
+namespace App\Http\Controllers\Admin;
+
+/*
+ * LeadController — просмотр заявок с веб-форм в административной панели.
+ * Маршруты:
+ *   GET  admin/leads         — список всех заявок (с фильтром по форме)
+ *   GET  admin/leads/{lead}  — просмотр одной заявки (помечает как прочитанную)
+ *   DELETE admin/leads/{lead} — удалить заявку
+ */
+
+use App\Http\Controllers\Controller;
+use App\Models\Lead;
+use App\Models\WebForm;
+use Illuminate\Http\RedirectResponse;
+use Illuminate\Http\Request;
+use Illuminate\View\View;
+
+class LeadController extends Controller
+{
+    public function index(Request $request): View
+    {
+        $forms = WebForm::orderBy('title')->get();
+        $formId = $request->query('form_id');
+
+        $query = Lead::with('form')->latest();
+        if ($formId) {
+            $query->where('form_id', $formId);
+        }
+
+        $leads = $query->paginate(30)->withQueryString();
+
+        return view('admin.leads.index', compact('leads', 'forms', 'formId'));
+    }
+
+    public function show(Lead $lead): View
+    {
+        $lead->markAsRead();
+
+        return view('admin.leads.show', compact('lead'));
+    }
+
+    public function destroy(Lead $lead): RedirectResponse
+    {
+        $lead->delete();
+
+        return redirect()->route('admin.leads.index')->with('success', 'Заявка удалена.');
+    }
+}

+ 78 - 0
app/Http/Controllers/Admin/LoginController.php

@@ -0,0 +1,78 @@
+<?php
+
+/*
+ * LoginController — авторизация администраторов в панели управления.
+ *
+ * Создан: 2026-05-06
+ * Маршруты: GET /admin/login → showLoginForm()
+ *           POST /admin/login → login()
+ *           POST /admin/logout → logout()
+ *
+ * Особенность: вход по полю 'name' (логин), а не email.
+ * Laravel Auth по умолчанию ищет по email — здесь credentials передаются вручную.
+ * После успешной авторизации проверяется флаг is_admin: если false — сессия сбрасывается.
+ */
+
+namespace App\Http\Controllers\Admin;
+
+use App\Http\Controllers\Controller;
+use Illuminate\Http\RedirectResponse;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Auth;
+use Illuminate\View\View;
+
+class LoginController extends Controller
+{
+    // Форма входа; если уже авторизован как admin — редирект на дашборд
+    // Вьюха: resources/views/admin/login.blade.php
+    public function showLoginForm(): View|RedirectResponse
+    {
+        if (Auth::check() && Auth::user()->is_admin) {
+            return redirect()->route('admin.dashboard');
+        }
+
+        return view('admin.login');
+    }
+
+    // Обработка формы входа
+    // Auth::attempt(['name' => ...]) — нестандартно, по умолчанию Laravel ищет по email
+    // После входа проверяется is_admin: если false — немедленный Auth::logout()
+    // session()->regenerate() — защита от session fixation атаки
+    // redirect()->intended() — редирект на URL куда шёл до входа, иначе на дашборд
+    public function login(Request $request): RedirectResponse
+    {
+        $request->validate([
+            'login' => ['required', 'string'],
+            'password' => ['required', 'string'],
+        ]);
+
+        $credentials = [
+            'name' => $request->login,
+            'password' => $request->password,
+        ];
+
+        if (Auth::attempt($credentials, $request->boolean('remember'))) {
+            if (Auth::user()->is_admin) {
+                $request->session()->regenerate();
+
+                return redirect()->intended(route('admin.dashboard'));
+            }
+
+            Auth::logout();
+        }
+
+        return back()->withErrors([
+            'login' => 'Неверный логин или пароль.',
+        ])->onlyInput('login');
+    }
+
+    // Выход: invalidate() уничтожает сессию, regenerateToken() обновляет CSRF-токен
+    public function logout(Request $request): RedirectResponse
+    {
+        Auth::logout();
+        $request->session()->invalidate();
+        $request->session()->regenerateToken();
+
+        return redirect()->route('admin.login');
+    }
+}

+ 215 - 0
app/Http/Controllers/Admin/ManualController.php

@@ -0,0 +1,215 @@
+<?php
+
+/*
+ * ManualController — управление справочниками (разделы и значения).
+ *
+ * Создан: 2026-05-06
+ * Маршруты: /admin/manuals/... (не resource, ручные маршруты в routes/web.php)
+ * Защита: middleware 'admin' (EnsureUserIsAdmin)
+ *
+ * Справочники — двухуровневая структура:
+ *   DictSection (разделы): например "Марки и модели", "Типы кузова"
+ *   DictValue (значения): Toyota → Camry, Corolla и т.д.
+ * Поддерживает иерархические (is_hierarchical=true) и плоские (false) разделы.
+ */
+
+namespace App\Http\Controllers\Admin;
+
+use App\Http\Controllers\Controller;
+use App\Models\DictSection;
+use App\Models\DictValue;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Storage;
+
+class ManualController extends Controller
+{
+    // ── Разделы справочников ────────────────────────────────────
+
+    // Список всех разделов справочников, отсортированных по sort_order
+    // Вьюха: resources/views/admin/manuals/index.blade.php
+    public function index()
+    {
+        $sections = DictSection::orderBy('sort_order')->get();
+
+        return view('admin.manuals.index', compact('sections'));
+    }
+
+    // Форма создания нового раздела справочника
+    // Вьюха: resources/views/admin/manuals/section_form.blade.php
+    public function sectionCreate()
+    {
+        return view('admin.manuals.section_form');
+    }
+
+    // Сохранение нового раздела; is_system всегда false — системные разделы создаются только через сидер
+    public function sectionStore(Request $request)
+    {
+        $request->validate([
+            'code' => 'required|string|max:64|unique:dict_sections,code',
+            'label' => 'required|string|max:128',
+            'is_hierarchical' => 'nullable|boolean',
+            'sort_order' => 'nullable|integer',
+        ]);
+
+        DictSection::create([
+            'code' => $request->code,
+            'label' => $request->label,
+            'is_system' => false,
+            'is_hierarchical' => (bool) $request->input('is_hierarchical', false),
+            'sort_order' => (int) $request->input('sort_order', 0),
+        ]);
+
+        return redirect()->route('admin.manuals.index')->with('success', 'Раздел добавлен.');
+    }
+
+    // Форма редактирования раздела справочника (та же вьюха, что и sectionCreate)
+    public function sectionEdit(DictSection $section)
+    {
+        return view('admin.manuals.section_form', compact('section'));
+    }
+
+    // Обновление раздела; unique-правило исключает текущую запись: unique:dict_sections,code,{id}
+    public function sectionUpdate(Request $request, DictSection $section)
+    {
+        $request->validate([
+            'code' => 'required|string|max:64|unique:dict_sections,code,'.$section->id,
+            'label' => 'required|string|max:128',
+            'is_hierarchical' => 'nullable|boolean',
+            'sort_order' => 'nullable|integer',
+        ]);
+
+        $section->update([
+            'code' => $request->code,
+            'label' => $request->label,
+            'is_hierarchical' => (bool) $request->input('is_hierarchical', false),
+            'sort_order' => (int) $request->input('sort_order', 0),
+        ]);
+
+        return redirect()->route('admin.manuals.index')->with('success', 'Раздел обновлён.');
+    }
+
+    // Удаление раздела; системные (is_system=true) удалять нельзя — защита от разрушения структуры
+    // При удалении раздела cascade удаляет все его значения (настроено в миграции FK)
+    public function sectionDestroy(DictSection $section)
+    {
+        if ($section->is_system) {
+            return back()->with('error', 'Системный раздел нельзя удалить.');
+        }
+
+        $section->delete();
+
+        return redirect()->route('admin.manuals.index')->with('success', 'Раздел удалён.');
+    }
+
+    // ── Значения справочника ────────────────────────────────────
+
+    // Список значений раздела; иерархические — с children (eager load); плоские — простой список
+    // Вьюха: resources/views/admin/manuals/values.blade.php (показывает оба варианта через @if)
+    public function values(DictSection $section)
+    {
+        if ($section->is_hierarchical) {
+            $items = DictValue::where('section_id', $section->id)
+                ->whereNull('parent_id')
+                ->with('children')
+                ->orderBy('sort_order')
+                ->get();
+        } else {
+            $items = DictValue::where('section_id', $section->id)
+                ->orderBy('sort_order')
+                ->get();
+        }
+
+        return view('admin.manuals.values', compact('section', 'items'));
+    }
+
+    // Форма добавления значения; если передан parent_id в URL → предзаполняет родителя
+    // Вьюха: resources/views/admin/manuals/value_form.blade.php
+    public function valueCreate(DictSection $section, Request $request)
+    {
+        $parent = null;
+        if ($request->filled('parent_id')) {
+            $parent = DictValue::findOrFail($request->parent_id);
+        }
+
+        return view('admin.manuals.value_form', compact('section', 'parent'));
+    }
+
+    // Сохранение нового значения; parent_id = null для верхнего уровня иерархии
+    public function valueStore(DictSection $section, Request $request)
+    {
+        $request->validate([
+            'value' => 'required|string|max:255',
+            'flag' => 'nullable|string|max:10',
+            'parent_id' => 'nullable|integer|exists:dict_values,id',
+            'sort_order' => 'nullable|integer',
+            'logo' => 'nullable|image|max:2048',
+        ]);
+
+        $logoPath = null;
+        if ($request->hasFile('logo') && $request->file('logo')->isValid()) {
+            $logoPath = $request->file('logo')->store('makes/logos', 'public');
+        }
+
+        DictValue::create([
+            'section_id' => $section->id,
+            'parent_id' => $request->input('parent_id') ?: null,
+            'value' => $request->value,
+            'flag' => $request->input('flag', ''),
+            'logo' => $logoPath,
+            'sort_order' => (int) $request->input('sort_order', 0),
+        ]);
+
+        return redirect()->route('admin.manuals.values', $section)->with('success', 'Значение добавлено.');
+    }
+
+    // Форма редактирования значения; загружает текущего родителя для отображения в форме
+    public function valueEdit(DictSection $section, DictValue $value)
+    {
+        $parent = $value->parent_id ? DictValue::find($value->parent_id) : null;
+
+        return view('admin.manuals.value_form', compact('section', 'value', 'parent'));
+    }
+
+    // Обновление значения; parent_id обнуляется если пустой (ternary: '' → null)
+    public function valueUpdate(Request $request, DictSection $section, DictValue $value)
+    {
+        $request->validate([
+            'value' => 'required|string|max:255',
+            'flag' => 'nullable|string|max:10',
+            'parent_id' => 'nullable|integer|exists:dict_values,id',
+            'sort_order' => 'nullable|integer',
+            'logo' => 'nullable|image|max:2048',
+            'remove_logo' => 'nullable|boolean',
+        ]);
+
+        $data = [
+            'parent_id' => $request->input('parent_id') ?: null,
+            'value' => $request->value,
+            'flag' => $request->input('flag', ''),
+            'sort_order' => (int) $request->input('sort_order', 0),
+        ];
+
+        if ($request->hasFile('logo') && $request->file('logo')->isValid()) {
+            // Удаляем старый логотип перед заменой
+            if ($value->logo) {
+                Storage::disk('public')->delete($value->logo);
+            }
+            $data['logo'] = $request->file('logo')->store('makes/logos', 'public');
+        } elseif ($request->boolean('remove_logo') && $value->logo) {
+            Storage::disk('public')->delete($value->logo);
+            $data['logo'] = null;
+        }
+
+        $value->update($data);
+
+        return redirect()->route('admin.manuals.values', $section)->with('success', 'Значение обновлено.');
+    }
+
+    // Удаление значения; cascade из FK удалит и все дочерние значения (если иерархия)
+    public function valueDestroy(DictSection $section, DictValue $value)
+    {
+        $value->delete();
+
+        return redirect()->route('admin.manuals.values', $section)->with('success', 'Значение удалено.');
+    }
+}

+ 68 - 0
app/Http/Controllers/Admin/PageAdminController.php

@@ -0,0 +1,68 @@
+<?php
+
+namespace App\Http\Controllers\Admin;
+
+/*
+ * PageAdminController — управление статическими страницами сайта в админке.
+ *
+ * Маршруты: GET/PUT /admin/pages (список + редактирование)
+ * Страницы фиксированные (home, services, contacts) — создание/удаление не предусмотрено.
+ * После сохранения сбрасывается кеш соответствующей публичной страницы.
+ */
+
+use App\Http\Controllers\Controller;
+use App\Models\Block;
+use App\Models\Page;
+use App\Models\PageSection;
+use Illuminate\Http\RedirectResponse;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Cache;
+use Illuminate\View\View;
+
+class PageAdminController extends Controller
+{
+    public function index(): View
+    {
+        $pages = Page::orderBy('id')->get();
+
+        return view('admin.pages.index', compact('pages'));
+    }
+
+    public function edit(Page $page): View
+    {
+        // Текущие секции страницы с загруженным блоком
+        $sections = PageSection::where('page_id', $page->id)
+            ->orderBy('sort_order')
+            ->with('block')
+            ->get();
+
+        // Блоки, которые ещё не добавлены на страницу (type=block секции)
+        $usedBlockIds = $sections->where('type', 'block')->pluck('block_id')->filter()->all();
+        $availableBlocks = Block::where('is_active', true)
+            ->whereNotIn('id', $usedBlockIds)
+            ->orderBy('title')
+            ->get();
+
+        return view('admin.pages.edit', compact('page', 'sections', 'availableBlocks'));
+    }
+
+    public function update(Request $request, Page $page): RedirectResponse
+    {
+        $data = $request->validate([
+            'title' => 'required|string|max:255',
+            'content' => 'nullable|string',
+            'meta_title' => 'nullable|string|max:255',
+            'meta_description' => 'nullable|string|max:500',
+            'is_active' => 'nullable|boolean',
+        ]);
+
+        $data['is_active'] = $request->boolean('is_active');
+        $page->update($data);
+
+        // Сбрасываем кеш публичной страницы после сохранения
+        Cache::forget('page.'.$page->slug);
+
+        return redirect()->route('admin.pages.index')
+            ->with('success', 'Страница «'.$page->title.'» сохранена.');
+    }
+}

+ 121 - 0
app/Http/Controllers/Admin/PageSectionController.php

@@ -0,0 +1,121 @@
+<?php
+
+namespace App\Http\Controllers\Admin;
+
+/*
+ * PageSectionController — page-builder: управление секциями страницы.
+ *
+ * Секции двух типов:
+ *   type=block   — переиспользуемый блок из библиотеки
+ *   type=content — произвольный HTML (свой WYSIWYG на каждую секцию)
+ *
+ * Маршруты (все под middleware admin):
+ *   POST   admin/pages/{page}/sections/reorder          → reorder()       — сохранить порядок (JSON)
+ *   POST   admin/pages/{page}/sections/attach-block     → attachBlock()   — добавить блок
+ *   POST   admin/pages/{page}/sections/add-content      → addContent()    — добавить текстовую секцию
+ *   PUT    admin/pages/{page}/sections/{section}        → updateContent() — сохранить текст секции
+ *   DELETE admin/pages/{page}/sections/{section}        → destroy()       — удалить секцию
+ */
+
+use App\Http\Controllers\Controller;
+use App\Models\Block;
+use App\Models\Page;
+use App\Models\PageSection;
+use Illuminate\Http\JsonResponse;
+use Illuminate\Http\RedirectResponse;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Cache;
+
+class PageSectionController extends Controller
+{
+    // Сохраняет новый порядок. Принимает JSON: {"order": [id, id, ...]}
+    public function reorder(Request $request, Page $page): JsonResponse
+    {
+        $ids = $request->validate(['order' => 'required|array', 'order.*' => 'integer'])['order'];
+
+        foreach ($ids as $sortOrder => $sectionId) {
+            PageSection::where('page_id', $page->id)
+                ->where('id', $sectionId)
+                ->update(['sort_order' => $sortOrder]);
+        }
+
+        $this->flushCache($page);
+
+        return response()->json(['ok' => true]);
+    }
+
+    // Добавляет блок из библиотеки на страницу (тип block)
+    public function attachBlock(Request $request, Page $page): RedirectResponse
+    {
+        $data = $request->validate(['block_id' => 'required|exists:blocks,id']);
+
+        $maxOrder = PageSection::where('page_id', $page->id)->max('sort_order') ?? -1;
+
+        PageSection::create([
+            'page_id' => $page->id,
+            'type' => 'block',
+            'block_id' => $data['block_id'],
+            'sort_order' => $maxOrder + 1,
+        ]);
+
+        $this->flushCache($page);
+
+        return redirect()->route('admin.pages.edit', $page)
+            ->with('success', 'Блок добавлен на страницу.');
+    }
+
+    // Добавляет пустую текстовую секцию (тип content)
+    public function addContent(Page $page): RedirectResponse
+    {
+        $maxOrder = PageSection::where('page_id', $page->id)->max('sort_order') ?? -1;
+
+        PageSection::create([
+            'page_id' => $page->id,
+            'type' => 'content',
+            'content' => '',
+            'sort_order' => $maxOrder + 1,
+        ]);
+
+        $this->flushCache($page);
+
+        return redirect()->route('admin.pages.edit', $page)
+            ->with('success', 'Текстовая область добавлена.');
+    }
+
+    // Сохраняет HTML-контент текстовой секции
+    public function updateContent(Request $request, Page $page, PageSection $section): JsonResponse
+    {
+        abort_if($section->page_id !== $page->id || $section->type !== 'content', 403);
+
+        $section->update(['content' => $request->input('content', '')]);
+        $this->flushCache($page);
+
+        return response()->json(['ok' => true]);
+    }
+
+    // Удаляет секцию (любого типа) и перенумеровывает остальные
+    public function destroy(Page $page, PageSection $section): RedirectResponse
+    {
+        abort_if($section->page_id !== $page->id, 403);
+
+        $section->delete();
+
+        // Перенумеровываем порядок без дырок
+        $sections = PageSection::where('page_id', $page->id)->orderBy('sort_order')->get();
+        foreach ($sections as $i => $s) {
+            $s->update(['sort_order' => $i]);
+        }
+
+        $this->flushCache($page);
+
+        return redirect()->route('admin.pages.edit', $page)
+            ->with('success', 'Секция удалена.');
+    }
+
+    // Сброс кеша публичной страницы и её секций
+    private function flushCache(Page $page): void
+    {
+        Cache::forget('page.'.$page->slug);
+        Cache::forget('page_sections.'.$page->slug);
+    }
+}

+ 116 - 0
app/Http/Controllers/Admin/ReviewAdminController.php

@@ -0,0 +1,116 @@
+<?php
+
+namespace App\Http\Controllers\Admin;
+
+/*
+ * ReviewAdminController — CRUD отзывов клиентов.
+ *
+ * Создан: 2026-05-07
+ * Маршруты: resource /admin/reviews (index, create, store, edit, update, destroy)
+ *           POST /admin/reviews/{review}/toggle — переключение is_active
+ * Защита: middleware 'admin' (EnsureUserIsAdmin)
+ *
+ * После изменения отзывов сбрасывается кеш страниц, где они используются.
+ */
+
+use App\Http\Controllers\Controller;
+use App\Models\Review;
+use Illuminate\Http\RedirectResponse;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Cache;
+use Illuminate\View\View;
+
+class ReviewAdminController extends Controller
+{
+    // Список всех отзывов, упорядочен по sort_order, затем по дате создания
+    public function index(): View
+    {
+        $reviews = Review::orderBy('sort_order')->orderByDesc('created_at')->get();
+
+        return view('admin.reviews.index', compact('reviews'));
+    }
+
+    // Форма создания нового отзыва
+    public function create(): View
+    {
+        return view('admin.reviews.form', ['review' => new Review]);
+    }
+
+    // Сохранение нового отзыва
+    public function store(Request $request): RedirectResponse
+    {
+        $data = $this->validated($request);
+        Review::create($data);
+        $this->flushCache();
+
+        return redirect()->route('admin.reviews.index')->with('success', 'Отзыв добавлен.');
+    }
+
+    // Форма редактирования отзыва
+    public function edit(Review $review): View
+    {
+        return view('admin.reviews.form', compact('review'));
+    }
+
+    // Обновление отзыва
+    public function update(Request $request, Review $review): RedirectResponse
+    {
+        $review->update($this->validated($request));
+        $this->flushCache();
+
+        return redirect()->route('admin.reviews.index')->with('success', 'Отзыв обновлён.');
+    }
+
+    // Удаление отзыва
+    public function destroy(Review $review): RedirectResponse
+    {
+        $review->delete();
+        $this->flushCache();
+
+        return redirect()->route('admin.reviews.index')->with('success', 'Отзыв удалён.');
+    }
+
+    // Переключение видимости (активен / скрыт) без перехода на форму
+    public function toggle(Review $review): RedirectResponse
+    {
+        $review->update(['is_active' => ! $review->is_active]);
+        $this->flushCache();
+
+        return back()->with('success', $review->is_active ? 'Отзыв активирован.' : 'Отзыв скрыт.');
+    }
+
+    // ── Приватные вспомогательные ────────────────────────────────────────
+
+    // Валидация и подготовка данных из запроса
+    private function validated(Request $request): array
+    {
+        $request->validate([
+            'author' => 'required|string|max:128',
+            'car_name' => 'nullable|string|max:128',
+            'rating' => 'required|integer|min:1|max:5',
+            'body' => 'required|string|max:2000',
+            'review_date' => 'nullable|date',
+            'is_active' => 'nullable|boolean',
+            'sort_order' => 'nullable|integer|min:0',
+        ]);
+
+        return [
+            'author' => $request->input('author'),
+            'car_name' => $request->input('car_name') ?: null,
+            'rating' => (int) $request->input('rating', 5),
+            'body' => $request->input('body'),
+            'review_date' => $request->input('review_date') ?: null,
+            'is_active' => $request->boolean('is_active'),
+            'sort_order' => (int) $request->input('sort_order', 0),
+        ];
+    }
+
+    // Сбрасываем кеш страниц при изменении отзывов
+    private function flushCache(): void
+    {
+        foreach (['home', 'services', 'contacts', 'privacy', 'offer'] as $slug) {
+            Cache::forget('page_sections.'.$slug);
+            Cache::forget('page.'.$slug);
+        }
+    }
+}

+ 103 - 0
app/Http/Controllers/Admin/ServiceAdminController.php

@@ -0,0 +1,103 @@
+<?php
+
+namespace App\Http\Controllers\Admin;
+
+/*
+ * ServiceAdminController — CRUD услуг в административной панели.
+ * Маршруты: admin.services.*
+ * Slug генерируется автоматически из title при создании,
+ * при редактировании не меняется (стабильные URL на детальных страницах).
+ */
+
+use App\Http\Controllers\Controller;
+use App\Models\Service;
+use Illuminate\Http\RedirectResponse;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Cache;
+use Illuminate\View\View;
+
+class ServiceAdminController extends Controller
+{
+    public function index(): View
+    {
+        $services = Service::orderBy('sort_order')->orderBy('title')->get();
+
+        return view('admin.services.index', compact('services'));
+    }
+
+    public function create(): View
+    {
+        return view('admin.services.form', ['service' => new Service]);
+    }
+
+    public function store(Request $request): RedirectResponse
+    {
+        $data = $this->validated($request);
+        $data['slug'] = Service::generateSlug($data['title']);
+
+        Service::create($data);
+        $this->forgetCache();
+
+        return redirect()->route('admin.services.index')
+            ->with('success', 'Услуга «' . $data['title'] . '» создана.');
+    }
+
+    public function edit(Service $service): View
+    {
+        return view('admin.services.form', compact('service'));
+    }
+
+    public function update(Request $request, Service $service): RedirectResponse
+    {
+        $data = $this->validated($request, $service->id);
+
+        $service->update($data);
+        $this->forgetCache();
+
+        return redirect()->route('admin.services.index')
+            ->with('success', 'Услуга «' . $service->title . '» обновлена.');
+    }
+
+    public function destroy(Service $service): RedirectResponse
+    {
+        $title = $service->title;
+        $service->delete();
+        $this->forgetCache();
+
+        return redirect()->route('admin.services.index')
+            ->with('success', 'Услуга «' . $title . '» удалена.');
+    }
+
+    // ── Приватные хелперы ─────────────────────────────────────────────────
+
+    private function validated(Request $request, ?int $exceptId = null): array
+    {
+        $rules = [
+            'title'       => 'required|string|max:255',
+            'icon'        => 'nullable|string|max:10',
+            'excerpt'     => 'nullable|string|max:500',
+            'description' => 'nullable|string',
+            'tags'        => 'nullable|array',
+            'tags.*'      => 'integer',
+            'sort_order'  => 'nullable|integer|min:0',
+            'is_active'   => 'nullable|boolean',
+        ];
+
+        $raw = $request->validate($rules);
+
+        // tags — plain array integer ID из справочника dict_values
+        $raw['tags']       = array_values(array_map('intval', array_filter($raw['tags'] ?? [])));
+        $raw['sort_order'] = (int) ($raw['sort_order'] ?? 0);
+        $raw['is_active']  = $request->boolean('is_active');
+
+        return $raw;
+    }
+
+    private function forgetCache(): void
+    {
+        // Сбрасываем кеш секций страниц, где используются блоки services_grid
+        foreach (['home', 'services', 'contacts'] as $slug) {
+            Cache::forget('page_sections.' . $slug);
+        }
+    }
+}

+ 99 - 0
app/Http/Controllers/Admin/SettingAdminController.php

@@ -0,0 +1,99 @@
+<?php
+
+namespace App\Http\Controllers\Admin;
+
+/*
+ * SettingAdminController — настройки сайта в административной панели.
+ *
+ * Маршруты: GET /admin/settings (форма), POST /admin/settings (сохранение)
+ * Хранит: контакты, соцсети, копирайт, слоган, логотипы,
+ *         Яндекс.Метрика, Google Analytics, Open Graph.
+ * Логотипы/OG-изображение загружаются как файлы, путь сохраняется в settings.
+ * После сохранения сбрасывается кеш настроек.
+ */
+
+use App\Http\Controllers\Controller;
+use App\Models\Setting;
+use Illuminate\Http\RedirectResponse;
+use Illuminate\Http\Request;
+use Illuminate\View\View;
+
+class SettingAdminController extends Controller
+{
+    private const TEXT_KEYS = [
+        // Контакты
+        'phone', 'email', 'working_hours',
+        // Соцсети
+        'telegram', 'youtube', 'vk',
+        // Подвал
+        'footer_slogan', 'copyright',
+        // Аналитика
+        'yandex_metrika', 'google_analytics',
+        // Open Graph
+        'og_site_name', 'og_description', 'og_locale',
+        // Режим сайта (хранятся как '1'/'0')
+        'site_maintenance', 'site_noindex',
+    ];
+
+    private const LOGO_KEYS = ['logo_header', 'logo_footer'];
+
+    public function index(): View
+    {
+        $settings = Setting::all();
+
+        return view('admin.settings.index', compact('settings'));
+    }
+
+    public function update(Request $request): RedirectResponse
+    {
+        $request->validate([
+            'phone' => 'nullable|string|max:30',
+            'email' => 'nullable|email|max:100',
+            'telegram' => 'nullable|url|max:255',
+            'youtube' => 'nullable|url|max:255',
+            'vk' => 'nullable|url|max:255',
+            'footer_slogan' => 'nullable|string|max:255',
+            'copyright' => 'nullable|string|max:255',
+            'working_hours' => 'nullable|string|max:500',
+            // Аналитика
+            'yandex_metrika' => 'nullable|digits_between:6,12',
+            'google_analytics' => 'nullable|string|max:30',
+            // Open Graph
+            'og_site_name' => 'nullable|string|max:100',
+            'og_description' => 'nullable|string|max:300',
+            'og_locale' => 'nullable|string|max:10',
+            // Режим сайта
+            'site_maintenance' => 'nullable|in:0,1',
+            'site_noindex' => 'nullable|in:0,1',
+            // Файлы
+            'logo_header' => 'nullable|image|mimes:png,jpg,svg,webp|max:512',
+            'logo_footer' => 'nullable|image|mimes:png,jpg,svg,webp|max:512',
+            'og_image' => 'nullable|image|mimes:jpg,jpeg,png,webp|max:2048',
+        ]);
+
+        // Сохраняем текстовые поля
+        foreach (self::TEXT_KEYS as $key) {
+            Setting::set($key, $request->input($key));
+        }
+
+        // Загружаем логотипы — заменяем файл и сохраняем путь в settings
+        foreach (self::LOGO_KEYS as $key) {
+            if ($request->hasFile($key) && $request->file($key)->isValid()) {
+                $ext = $request->file($key)->getClientOriginalExtension();
+                $filename = $key.'.'.$ext;
+                $request->file($key)->move(public_path('images/logos'), $filename);
+                Setting::set($key, 'images/logos/'.$filename);
+            }
+        }
+
+        // OG-изображение: 1200×630, сохраняется в public/images/og/
+        if ($request->hasFile('og_image') && $request->file('og_image')->isValid()) {
+            $ext = $request->file('og_image')->getClientOriginalExtension();
+            $request->file('og_image')->move(public_path('images/og'), 'og_image.'.$ext);
+            Setting::set('og_image', 'images/og/og_image.'.$ext);
+        }
+
+        return redirect()->route('admin.settings.index')
+            ->with('success', 'Настройки сохранены.');
+    }
+}

+ 113 - 0
app/Http/Controllers/Admin/UserAdminController.php

@@ -0,0 +1,113 @@
+<?php
+
+namespace App\Http\Controllers\Admin;
+
+/*
+ * UserAdminController — управление пользователями и правами в административной панели.
+ *
+ * index()       — список всех пользователей (только для users.view)
+ * edit()        — форма редактирования роли и прав конкретного пользователя
+ * update()      — сохранение роли и индивидуальных override-прав
+ * destroy()     — удаление пользователя (superadmin удалить нельзя)
+ *
+ * Управлять можно только теми, кем текущий пользователь canManage() — защита иерархии.
+ */
+
+use App\Http\Controllers\Controller;
+use App\Models\User;
+use App\Models\UserPermission;
+use App\Services\PermissionService;
+use Illuminate\Http\RedirectResponse;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Auth;
+use Illuminate\View\View;
+
+class UserAdminController extends Controller
+{
+    public function index(): View
+    {
+        $this->authorize('users.view');
+
+        $users = User::with('userPermissions')->orderBy('id')->get();
+        $roleLabels = PermissionService::roleLabels();
+
+        return view('admin.users.index', compact('users', 'roleLabels'));
+    }
+
+    public function edit(User $user): View
+    {
+        $this->authorize('users.manage');
+        abort_unless(Auth::user()->canManage($user), 403, 'Недостаточно прав для управления этим пользователем.');
+
+        $user->load('userPermissions');
+        $permissions = PermissionService::all();
+        $roleLabels = PermissionService::roleLabels();
+        $roleDefaults = $user->role ? PermissionService::roleDefaults($user->role) : [];
+        $overrides = $user->userPermissions->keyBy('permission');
+
+        return view('admin.users.edit', compact('user', 'permissions', 'roleLabels', 'roleDefaults', 'overrides'));
+    }
+
+    public function update(Request $request, User $user): RedirectResponse
+    {
+        $this->authorize('users.manage');
+        abort_unless(Auth::user()->canManage($user), 403, 'Недостаточно прав для управления этим пользователем.');
+
+        $request->validate([
+            'role' => 'nullable|in:superadmin,admin,editor,viewer',
+            'permissions' => 'nullable|array',
+            'permissions.*' => 'in:allow,deny',
+        ]);
+
+        // superadmin не может снять роль superadmin сам у себя, если он единственный
+        if ($user->role === 'superadmin' && $request->input('role') !== 'superadmin') {
+            $superCount = User::where('role', 'superadmin')->count();
+            abort_if($superCount <= 1, 422, 'Нельзя снять роль у единственного суперадминистратора.');
+        }
+
+        // Только superadmin может назначать роль superadmin или admin
+        $newRole = $request->input('role');
+        if (in_array($newRole, ['superadmin', 'admin']) && Auth::user()->role !== 'superadmin') {
+            abort(403, 'Только суперадминистратор может назначать эту роль.');
+        }
+
+        $user->update([
+            'role' => $newRole ?: null,
+            'is_admin' => ! empty($newRole), // синхронизируем legacy-флаг
+        ]);
+
+        // Перезаписываем индивидуальные overrides
+        UserPermission::where('user_id', $user->id)->delete();
+
+        $allKeys = PermissionService::keys();
+        foreach ($request->input('permissions', []) as $key => $action) {
+            if (! in_array($key, $allKeys, true)) {
+                continue;
+            }
+            if (! in_array($action, ['allow', 'deny'], true)) {
+                continue;
+            }
+
+            UserPermission::create([
+                'user_id' => $user->id,
+                'permission' => $key,
+                'action' => $action,
+            ]);
+        }
+
+        return redirect()->route('admin.users.index')
+            ->with('success', 'Пользователь «'.$user->name.'» обновлён.');
+    }
+
+    public function destroy(User $user): RedirectResponse
+    {
+        $this->authorize('users.manage');
+        abort_unless(Auth::user()->canManage($user), 403);
+        abort_if($user->id === Auth::id(), 422, 'Нельзя удалить самого себя.');
+
+        $user->delete();
+
+        return redirect()->route('admin.users.index')
+            ->with('success', 'Пользователь удалён.');
+    }
+}

+ 109 - 0
app/Http/Controllers/Admin/WebFormController.php

@@ -0,0 +1,109 @@
+<?php
+
+namespace App\Http\Controllers\Admin;
+
+/*
+ * WebFormController — управление конструктором веб-форм в административной панели.
+ * Маршруты: resource admin/forms (index, create, store, edit, update, destroy)
+ * Поля формы хранятся в JSON-колонке fields модели WebForm.
+ * Поле fields приходит в POST как JSON-строка из конструктора на JS.
+ */
+
+use App\Http\Controllers\Controller;
+use App\Models\Service;
+use App\Models\WebForm;
+use Illuminate\Http\RedirectResponse;
+use Illuminate\Http\Request;
+use Illuminate\View\View;
+
+class WebFormController extends Controller
+{
+    public function index(): View
+    {
+        $forms = WebForm::withCount('leads')->latest()->get();
+
+        return view('admin.forms.index', compact('forms'));
+    }
+
+    public function create(): View
+    {
+        $form = new WebForm;
+        $services = $this->servicesList();
+
+        return view('admin.forms.edit', compact('form', 'services'));
+    }
+
+    public function store(Request $request): RedirectResponse
+    {
+        $data = $this->validated($request);
+        WebForm::create($data);
+
+        return redirect()->route('admin.forms.index')->with('success', 'Форма создана.');
+    }
+
+    public function edit(WebForm $form): View
+    {
+        $services = $this->servicesList();
+
+        return view('admin.forms.edit', compact('form', 'services'));
+    }
+
+    public function update(Request $request, WebForm $form): RedirectResponse
+    {
+        $data = $this->validated($request, $form);
+        $form->update($data);
+
+        return redirect()->route('admin.forms.index')->with('success', 'Форма сохранена.');
+    }
+
+    public function destroy(WebForm $form): RedirectResponse
+    {
+        $form->delete();
+
+        return redirect()->route('admin.forms.index')->with('success', 'Форма удалена.');
+    }
+
+    // Активные услуги в порядке sort_order для подстановки в select-поля форм
+    private function servicesList(): array
+    {
+        return Service::where('is_active', true)
+            ->orderBy('sort_order')
+            ->orderBy('title')
+            ->pluck('title')
+            ->toArray();
+    }
+
+    // Парсим и валидируем входящие данные формы
+    private function validated(Request $request, ?WebForm $form = null): array
+    {
+        $slugRule = 'required|string|max:80|regex:/^[a-z0-9\-_]+$/|unique:web_forms,slug';
+        if ($form && $form->exists) {
+            $slugRule .= ','.$form->id;
+        }
+
+        $request->validate([
+            'title' => 'required|string|max:150',
+            'slug' => $slugRule,
+            'notify_email' => 'nullable|email|max:150',
+            'is_active' => 'nullable|boolean',
+            'fields_json' => 'nullable|string',
+        ]);
+
+        // Декодируем поля из JSON-строки
+        $fields = [];
+        if ($request->filled('fields_json')) {
+            $decoded = json_decode($request->input('fields_json'), true);
+            if (is_array($decoded)) {
+                $fields = $decoded;
+            }
+        }
+
+        return [
+            'title' => $request->input('title'),
+            'slug' => $request->input('slug'),
+            'notify_email' => $request->input('notify_email'),
+            'is_active' => $request->boolean('is_active'),
+            'fields' => $fields,
+        ];
+    }
+}

+ 148 - 0
app/Http/Controllers/CatalogController.php

@@ -0,0 +1,148 @@
+<?php
+
+namespace App\Http\Controllers;
+
+/*
+ * CatalogController — публичный каталог и страница автомобиля.
+ *
+ * Кеш: plain array (не объекты) → без __PHP_Incomplete_Class при unserialize.
+ * Ключ кеша включает все параметры фильтра + страницу пагинации.
+ * filterOptions() кешируется отдельно — пересобирать при каждом запросе дорого.
+ */
+
+use App\Models\Car;
+use Illuminate\Http\Request;
+use Illuminate\Pagination\LengthAwarePaginator;
+use Illuminate\Support\Facades\Cache;
+use Illuminate\View\View;
+
+class CatalogController extends Controller
+{
+    private const CACHE_TTL = 86400; // 24 часа
+
+    private const PER_PAGE = 12;
+
+    public function index(Request $request): View
+    {
+        // ── Читаем фильтры из GET ──────────────────────────────────────
+        $conditions = array_filter((array) $request->input('condition', []));
+        $countries = array_filter((array) $request->input('country_origin', []));
+        $fuels = array_filter((array) $request->input('engine_type', []));
+        $make = trim((string) $request->input('make', ''));
+        $bodyType = trim((string) $request->input('body_type', ''));
+        $priceTo = $request->filled('price_to') ? (int) $request->input('price_to') : null;
+        $priceFrom = null;
+
+        // price_range из быстрого подбора: "1500000", "1500000-3000000", "5000000-"
+        $priceRange = $request->input('price_range', '');
+        if ($priceRange !== '') {
+            if (str_ends_with($priceRange, '-')) {
+                $priceFrom = (int) rtrim($priceRange, '-') ?: null;
+            } elseif (str_contains($priceRange, '-')) {
+                [$from, $to] = explode('-', $priceRange, 2);
+                $priceFrom = $from !== '' ? (int) $from : null;
+                $priceTo   = $to   !== '' ? (int) $to   : $priceTo;
+            } else {
+                $priceTo = (int) $priceRange ?: $priceTo;
+            }
+        }
+
+        $year = $request->filled('year') ? (int) $request->input('year') : null;
+        $sort = $request->input('sort', '');
+        $page = $request->integer('page', 1);
+
+        // ── Строим ключ кеша ──────────────────────────────────────────
+        $filterData = compact('conditions', 'countries', 'fuels', 'make', 'bodyType', 'priceFrom', 'priceTo', 'year', 'sort');
+        ksort($filterData);
+        $cacheKey = 'catalog.'.md5(serialize($filterData)).'.p'.$page;
+
+        $cached = Cache::remember($cacheKey, self::CACHE_TTL, function () use (
+            $conditions, $countries, $fuels, $make, $bodyType, $priceFrom, $priceTo, $year, $sort, $page
+        ) {
+            $query = Car::where('status', 'active');
+
+            if ($conditions) {
+                $query->whereIn('condition', $conditions);
+            }
+            if ($countries) {
+                $query->whereIn('country_origin', $countries);
+            }
+            if ($fuels) {
+                $query->whereIn('engine_type', $fuels);
+            }
+            if ($make) {
+                $query->where('make', $make);
+            }
+            if ($bodyType) {
+                $query->where('body_type', $bodyType);
+            }
+            if ($priceFrom) {
+                $query->where('price_rub', '>=', $priceFrom);
+            }
+            if ($priceTo) {
+                $query->where('price_rub', '<=', $priceTo);
+            }
+            if ($year) {
+                $query->where('year', $year);
+            }
+
+            match ($sort) {
+                'price_asc' => $query->orderBy('price_rub'),
+                'price_desc' => $query->orderByDesc('price_rub'),
+                'year_desc' => $query->orderByDesc('year'),
+                default => $query->orderByDesc('id'),
+            };
+
+            $paginator = $query->paginate(self::PER_PAGE, ['*'], 'page', $page);
+
+            return [
+                'total' => $paginator->total(),
+                'items' => $paginator->getCollection()->map->getAttributes()->all(),
+            ];
+        });
+
+        // Восстанавливаем Car из атрибутов (без unserialize объекта)
+        $items = collect($cached['items'])->map(fn ($a) => (new Car)->setRawAttributes($a, true));
+        $cars = new LengthAwarePaginator(
+            $items, $cached['total'], self::PER_PAGE, $page,
+            ['path' => $request->url(), 'query' => $request->query()]
+        );
+
+        $filterOptions = $this->filterOptions();
+        $activeFilters = compact('conditions', 'countries', 'fuels', 'make', 'bodyType', 'priceFrom', 'priceTo', 'year', 'sort');
+
+        return view('pages.catalog', compact('cars', 'filterOptions', 'activeFilters'));
+    }
+
+    public function show(int $id): View
+    {
+        // Детальная страница: без кеша — запросы редкие, зато данные всегда свежие
+        $car = Car::where('status', 'active')->findOrFail($id);
+
+        $similar = Car::where('status', 'active')
+            ->where('id', '!=', $car->id)
+            ->where(fn ($q) => $q->where('make', $car->make)->orWhere('body_type', $car->body_type))
+            ->orderByDesc('id')
+            ->limit(3)
+            ->get();
+
+        return view('pages.car', compact('car', 'similar'));
+    }
+
+    // Опции для фильтров — кешируются отдельно
+    private function filterOptions(): array
+    {
+        return Cache::remember('catalog_filter_opts', self::CACHE_TTL, function () {
+            $base = Car::where('status', 'active');
+
+            return [
+                'makes' => (clone $base)->select('make')->distinct()->orderBy('make')->pluck('make')->toArray(),
+                'body_types' => (clone $base)->whereNotNull('body_type')->where('body_type', '!=', '')->select('body_type')->distinct()->orderBy('body_type')->pluck('body_type')->toArray(),
+                'countries' => (clone $base)->whereNotNull('country_origin')->select('country_origin')->distinct()->orderBy('country_origin')->pluck('country_origin')->toArray(),
+                'years' => (clone $base)->select('year')->distinct()->orderByDesc('year')->pluck('year')->toArray(),
+                'price_min' => (int) ((clone $base)->whereNotNull('price_rub')->min('price_rub') ?? 0),
+                'price_max' => (int) ((clone $base)->whereNotNull('price_rub')->max('price_rub') ?? 50000000),
+            ];
+        });
+    }
+}

+ 10 - 0
app/Http/Controllers/Controller.php

@@ -0,0 +1,10 @@
+<?php
+
+namespace App\Http\Controllers;
+
+use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
+
+abstract class Controller
+{
+    use AuthorizesRequests;
+}

+ 50 - 0
app/Http/Controllers/FavoritesController.php

@@ -0,0 +1,50 @@
+<?php
+
+namespace App\Http\Controllers;
+
+/*
+ * FavoritesController — страница и JSON-endpoint избранного.
+ *
+ * Избранное хранится в localStorage браузера.
+ * index()  — отдаёт пустую страницу (JS читает localStorage и вызывает load())
+ * load()   — принимает POST с массивом IDs, возвращает HTML карточек
+ */
+
+use App\Models\Car;
+use Illuminate\Http\JsonResponse;
+use Illuminate\Http\Request;
+use Illuminate\View\View;
+
+class FavoritesController extends Controller
+{
+    public function index(): View
+    {
+        return view('pages.favorites');
+    }
+
+    // POST /favorites/load  {ids: [1,2,3]}  → HTML-фрагмент с карточками
+    public function load(Request $request): JsonResponse
+    {
+        $ids = array_filter((array) $request->input('ids', []), 'is_numeric');
+        $ids = array_map('intval', $ids);
+
+        if (empty($ids)) {
+            return response()->json(['html' => '', 'count' => 0]);
+        }
+
+        $cars = Car::whereIn('id', $ids)
+            ->where('status', 'active')
+            ->orderByRaw('FIELD(id, '.implode(',', $ids).')')
+            ->get();
+
+        $fuelLabels = ['petrol' => 'Бензин', 'diesel' => 'Дизель', 'hybrid' => 'Гибрид', 'electric' => 'Электро', 'gas' => 'Газ', 'other' => 'Другое'];
+        $countryFlags = ['Германия' => '🇩🇪', 'Япония' => '🇯🇵', 'Корея' => '🇰🇷', 'США' => '🇺🇸', 'Китай' => '🇨🇳', 'ОАЭ' => '🇦🇪', 'Грузия' => '🇬🇪', 'Великобритания' => '🇬🇧', 'Франция' => '🇫🇷', 'Италия' => '🇮🇹'];
+
+        $html = '';
+        foreach ($cars as $car) {
+            $html .= view('pages.partials._car_card', compact('car', 'fuelLabels', 'countryFlags'))->render();
+        }
+
+        return response()->json(['html' => $html, 'count' => $cars->count()]);
+    }
+}

+ 87 - 0
app/Http/Controllers/FormSubmitController.php

@@ -0,0 +1,87 @@
+<?php
+
+namespace App\Http\Controllers;
+
+/*
+ * FormSubmitController — обработка отправки веб-форм с публичного сайта.
+ * Маршрут: POST /forms/{slug}/submit
+ * Логика: валидация по конфигурации полей, сохранение Lead, отправка email, JSON-ответ.
+ */
+
+use App\Mail\LeadNotification;
+use App\Models\Lead;
+use App\Models\WebForm;
+use Illuminate\Http\JsonResponse;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Mail;
+use Illuminate\Validation\ValidationException;
+
+class FormSubmitController extends Controller
+{
+    public function submit(Request $request, string $slug): JsonResponse
+    {
+        $form = WebForm::where('slug', $slug)->where('is_active', true)->firstOrFail();
+
+        // Строим правила валидации из конфигурации полей формы
+        $rules = [];
+        foreach ($form->fields ?? [] as $field) {
+            if (empty($field['name'])) {
+                continue;
+            }
+            $fieldRules = [];
+            if (! empty($field['required'])) {
+                $fieldRules[] = 'required';
+            } else {
+                $fieldRules[] = 'nullable';
+            }
+            $fieldRules[] = match ($field['type'] ?? 'text') {
+                'email' => 'email|max:200',
+                'tel' => 'string|max:30',
+                'textarea' => 'string|max:3000',
+                'checkbox' => 'accepted',
+                default => 'string|max:500',
+            };
+            $rules[$field['name']] = implode('|', $fieldRules);
+        }
+
+        try {
+            $validated = $request->validate($rules);
+        } catch (ValidationException $e) {
+            return response()->json(['errors' => $e->errors()], 422);
+        }
+
+        // Убираем checkbox (согласие) из сохраняемых данных — только факт согласия нужен
+        $data = collect($validated)
+            ->filter(fn ($v, $k) => ! $this->isConsentField($form->fields ?? [], $k))
+            ->toArray();
+
+        $lead = Lead::create([
+            'form_id' => $form->id,
+            'data' => $data,
+            'ip' => $request->ip(),
+        ]);
+
+        // Отправляем email уведомление если указан notify_email
+        if ($form->notify_email) {
+            try {
+                Mail::to($form->notify_email)->send(new LeadNotification($lead, $form));
+            } catch (\Throwable) {
+                // Не прерываем работу при ошибке email
+            }
+        }
+
+        return response()->json(['ok' => true]);
+    }
+
+    // Является ли поле чекбоксом-согласием (не нужно хранить в заявке)
+    private function isConsentField(array $fields, string $name): bool
+    {
+        foreach ($fields as $field) {
+            if (($field['name'] ?? '') === $name && ($field['type'] ?? '') === 'checkbox') {
+                return true;
+            }
+        }
+
+        return false;
+    }
+}

+ 101 - 0
app/Http/Controllers/PageController.php

@@ -0,0 +1,101 @@
+<?php
+
+namespace App\Http\Controllers;
+
+/*
+ * PageController — публичные страницы сайта.
+ *
+ * Кеширование: сохраняем только массив атрибутов (не объект модели),
+ * чтобы избежать ошибки десериализации "incomplete object" при file-кеше.
+ * После чтения из кеша восстанавливаем модель через setRawAttributes().
+ */
+
+use App\Models\Page;
+use App\Models\PageSection;
+use App\Models\Setting;
+use Illuminate\Support\Facades\Cache;
+use Illuminate\View\View;
+
+class PageController extends Controller
+{
+    private const CACHE_TTL = 86400; // 24 часа
+
+    public function home(): View
+    {
+        $page = $this->getPage('home');
+        $sections = $this->getPageSections('home');
+
+        return view('pages.home', compact('page', 'sections'));
+    }
+
+    public function services(): View
+    {
+        $page = $this->getPage('services');
+        $sections = $this->getPageSections('services');
+
+        return view('pages.services', compact('page', 'sections'));
+    }
+
+    public function contacts(): View
+    {
+        $page = $this->getPage('contacts');
+        $siteSettings = Setting::all()->toArray();
+
+        return view('pages.contacts', compact('page', 'siteSettings'));
+    }
+
+    public function privacy(): View
+    {
+        $page = $this->getPage('privacy');
+
+        return view('pages.text', compact('page'));
+    }
+
+    public function offer(): View
+    {
+        $page = $this->getPage('offer');
+
+        return view('pages.text', compact('page'));
+    }
+
+    // Кешируем только массив атрибутов, восстанавливаем модель после — без проблем с unserialize
+    private function getPage(string $slug): Page
+    {
+        $attrs = Cache::remember('page.'.$slug, self::CACHE_TTL, function () use ($slug) {
+            return Page::findBySlug($slug)->getAttributes();
+        });
+
+        return (new Page)->setRawAttributes($attrs, true);
+    }
+
+    // Секции страницы кешируем как plain array [{title, content}]
+    // Оба типа: block (берём из блока) и content (свой HTML)
+    private function getPageSections(string $slug): array
+    {
+        return Cache::remember('page_sections.'.$slug, self::CACHE_TTL, function () use ($slug) {
+            $page = Page::where('slug', $slug)->first();
+            if (! $page) {
+                return [];
+            }
+
+            return PageSection::where('page_id', $page->id)
+                ->orderBy('sort_order')
+                ->with('block')
+                ->get()
+                ->map(function ($s) {
+                    $html = $s->getRenderedContent();
+                    if ($html === '') {
+                        return null; // пропускаем пустые и удалённые блоки
+                    }
+
+                    return [
+                        'title' => $s->type === 'block' ? ($s->block->title ?? '') : 'Текстовая область',
+                        'content' => $html,
+                    ];
+                })
+                ->filter()
+                ->values()
+                ->all();
+        });
+    }
+}

+ 22 - 0
app/Http/Controllers/ServiceController.php

@@ -0,0 +1,22 @@
+<?php
+
+namespace App\Http\Controllers;
+
+/*
+ * ServiceController — детальная страница услуги.
+ * GET /services/{slug}
+ * 404 если услуга не найдена или неактивна.
+ */
+
+use App\Models\Service;
+use Illuminate\View\View;
+
+class ServiceController extends Controller
+{
+    public function show(string $slug): View
+    {
+        $service = Service::active()->where('slug', $slug)->firstOrFail();
+
+        return view('pages.service', compact('service'));
+    }
+}

+ 32 - 0
app/Http/Middleware/EnsureUserIsAdmin.php

@@ -0,0 +1,32 @@
+<?php
+
+/*
+ * EnsureUserIsAdmin — middleware защиты маршрутов только для администраторов.
+ *
+ * Создан: 2026-05-06
+ * Регистрация: bootstrap/app.php → withMiddleware() → alias 'admin'
+ * Используется: в routes/web.php для группы Route::middleware('admin')->group(...)
+ *
+ * Проверяет: авторизован ли пользователь И имеет ли флаг is_admin = true.
+ * Если нет — редиректит на страницу входа /admin/login.
+ */
+
+namespace App\Http\Middleware;
+
+use Closure;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Auth;
+use Symfony\Component\HttpFoundation\Response;
+
+class EnsureUserIsAdmin
+{
+    // Проверяет авторизацию и наличие роли (или legacy is_admin); отказ → форма входа
+    public function handle(Request $request, Closure $next): Response
+    {
+        if (! Auth::check() || ! Auth::user()->isAdminUser()) {
+            return redirect()->route('admin.login');
+        }
+
+        return $next($request);
+    }
+}

+ 33 - 0
app/Http/Middleware/SiteMaintenanceMiddleware.php

@@ -0,0 +1,33 @@
+<?php
+
+namespace App\Http\Middleware;
+
+use App\Models\Setting;
+use Closure;
+use Illuminate\Http\Request;
+use Symfony\Component\HttpFoundation\Response;
+
+/*
+ * SiteMaintenanceMiddleware — показывает заглушку «Сайт на обслуживании»
+ * всем гостям и пользователям с ролью ниже admin/superadmin,
+ * если включена настройка site_maintenance = '1'.
+ * Администраторы и супер-администраторы видят сайт в обычном режиме.
+ */
+class SiteMaintenanceMiddleware
+{
+    public function handle(Request $request, Closure $next): Response
+    {
+        if (Setting::get('site_maintenance') !== '1') {
+            return $next($request);
+        }
+
+        $user = $request->user();
+
+        // Пропускаем администраторов и супер-администраторов
+        if ($user && in_array($user->role, ['admin', 'superadmin'], true)) {
+            return $next($request);
+        }
+
+        return response()->view('errors.maintenance', [], 503);
+    }
+}

+ 46 - 0
app/Http/Middleware/TrackPageVisit.php

@@ -0,0 +1,46 @@
+<?php
+
+namespace App\Http\Middleware;
+
+/*
+ * TrackPageVisit — считает публичные просмотры страниц.
+ * Обновляет/создаёт строку в page_visits за текущую дату.
+ * Уникальные IP считаются через Rate Limiter — один IP раз в сутки.
+ * Исключает запросы к /admin, /api, статику.
+ */
+
+use Closure;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Cache;
+use Illuminate\Support\Facades\DB;
+use Symfony\Component\HttpFoundation\Response;
+
+class TrackPageVisit
+{
+    public function handle(Request $request, Closure $next): Response
+    {
+        $response = $next($request);
+
+        // Только GET-запросы на публичные HTML-страницы
+        if (
+            $request->isMethod('GET')
+            && !$request->is('admin/*', 'admin')
+            && !$request->ajax()
+            && !$request->expectsJson()
+            && str_contains($response->headers->get('Content-Type', ''), 'text/html')
+        ) {
+            $today = now()->toDateString();
+            $ip    = $request->ip();
+            $isNew = Cache::add("visit_ip_{$today}_{$ip}", 1, now()->endOfDay());
+
+            DB::table('page_visits')
+                ->upsert(
+                    ['date' => $today, 'views' => 1, 'unique_ips' => $isNew ? 1 : 0],
+                    ['date'],
+                    ['views' => DB::raw('views + 1'), 'unique_ips' => DB::raw('unique_ips + ' . ($isNew ? 1 : 0))]
+                );
+        }
+
+        return $response;
+    }
+}

+ 50 - 0
app/Mail/LeadNotification.php

@@ -0,0 +1,50 @@
+<?php
+
+namespace App\Mail;
+
+/*
+ * LeadNotification — письмо-уведомление о новой заявке с сайта.
+ * Отправляется на notify_email формы сразу после сохранения Lead.
+ * Содержит: название формы, все поля заявки, дата и IP.
+ */
+
+use App\Models\Lead;
+use App\Models\WebForm;
+use Illuminate\Bus\Queueable;
+use Illuminate\Mail\Mailable;
+use Illuminate\Mail\Mailables\Content;
+use Illuminate\Mail\Mailables\Envelope;
+use Illuminate\Queue\SerializesModels;
+
+class LeadNotification extends Mailable
+{
+    use Queueable, SerializesModels;
+
+    public function __construct(
+        public readonly Lead $lead,
+        public readonly WebForm $form,
+    ) {}
+
+    public function envelope(): Envelope
+    {
+        return new Envelope(
+            subject: 'Новая заявка: '.$this->form->title,
+        );
+    }
+
+    public function content(): Content
+    {
+        return new Content(
+            markdown: 'mail.lead-notification',
+            with: [
+                'lead' => $this->lead,
+                'form' => $this->form,
+            ],
+        );
+    }
+
+    public function attachments(): array
+    {
+        return [];
+    }
+}

+ 61 - 0
app/Models/Block.php

@@ -0,0 +1,61 @@
+<?php
+
+namespace App\Models;
+
+/*
+ * Block — блок контента с привязанным макетом и структурированными полями.
+ *
+ * Таблица: blocks
+ * layout — ключ макета из BlockLayoutRegistry (why_us, hero_banner и т.д.)
+ * data   — значения полей в виде массива (хранится как JSON)
+ *
+ * getByName() — получить активный блок по системному имени
+ * render()    — рендерит блок через Blade-шаблон blocks/{layout}.blade.php
+ */
+
+use App\Support\BlockLayoutRegistry;
+use Illuminate\Database\Eloquent\Model;
+use Illuminate\Database\Eloquent\Relations\BelongsToMany;
+
+class Block extends Model
+{
+    protected $fillable = ['name', 'title', 'layout', 'content', 'data', 'is_active'];
+
+    protected $casts = [
+        'is_active' => 'boolean',
+        'data'      => 'array',
+    ];
+
+    public static function getByName(string $name): ?self
+    {
+        return static::where('name', $name)->where('is_active', true)->first();
+    }
+
+    // Рендерит блок через соответствующий Blade-шаблон, возвращает готовый HTML
+    public function render(): string
+    {
+        if (!$this->layout || !$this->data) {
+            return '';
+        }
+
+        $view = 'blocks.' . $this->layout;
+
+        if (!view()->exists($view)) {
+            return '';
+        }
+
+        return view($view, ['data' => $this->data])->render();
+    }
+
+    // Определение макета из реестра (null если макет не задан или не найден)
+    public function layoutDefinition(): ?array
+    {
+        return $this->layout ? BlockLayoutRegistry::get($this->layout) : null;
+    }
+
+    // Страницы, на которых размещён этот блок
+    public function pages(): BelongsToMany
+    {
+        return $this->belongsToMany(Page::class, 'page_sections')->withPivot('sort_order');
+    }
+}

+ 52 - 0
app/Models/Car.php

@@ -0,0 +1,52 @@
+<?php
+
+/*
+ * Car — модель автомобиля в каталоге.
+ *
+ * Создана: 2026-05-06 | Таблица: cars (миграция: 2026_05_06_151148_create_cars_table.php)
+ * $fillable — все поля, кроме id/timestamps (mass assignment через Car::create/update)
+ * $casts — автоматическое преобразование типов: boolean-поля, JSON-массивы (options, photos_gallery)
+ * Акцессоры getXxxAttribute() — виртуальные поля для отображения меток в шаблонах
+ */
+
+namespace App\Models;
+
+use Illuminate\Database\Eloquent\Model;
+
+class Car extends Model
+{
+    protected $fillable = [
+        'status', 'condition', 'make', 'model', 'generation', 'year',
+        'vin', 'plate', 'body_type', 'doors', 'color_exterior', 'color_interior',
+        'engine_type', 'engine_volume', 'engine_power_hp', 'transmission', 'drive',
+        'mileage_km', 'steering', 'owners_count', 'customs_cleared', 'pts', 'accident_free',
+        'price_usd', 'price_rub', 'price_vladivostok', 'price_moscow', 'price_negotiable',
+        'country_origin', 'city', 'platform', 'options', 'title', 'description',
+        'photo_main', 'photos_gallery',
+    ];
+
+    protected $casts = [
+        'customs_cleared' => 'boolean',
+        'accident_free' => 'boolean',
+        'price_negotiable' => 'boolean',
+        'photos_gallery' => 'array',
+        'options' => 'array',
+    ];
+
+    // Акцессор: возвращает читаемую русскую метку статуса для шаблонов ($car->status_label)
+    public function getStatusLabelAttribute(): string
+    {
+        return match ($this->status) {
+            'active' => 'Активен',
+            'sold' => 'Продан',
+            'draft' => 'Черновик',
+            default => $this->status,
+        };
+    }
+
+    // Акцессор: возвращает "Новый" или "Б/У" ($car->condition_label)
+    public function getConditionLabelAttribute(): string
+    {
+        return $this->condition === 'new' ? 'Новый' : 'Б/У';
+    }
+}

+ 39 - 0
app/Models/DictSection.php

@@ -0,0 +1,39 @@
+<?php
+
+/*
+ * DictSection — раздел справочника (например: "Марки и модели", "Типы кузова").
+ *
+ * Создана: 2026-05-06 | Таблица: dict_sections (миграция: 2026_05_06_151148_create_dict_sections_table.php)
+ * $timestamps = false — у раздела нет created_at/updated_at (статичные данные)
+ * is_system — системные разделы нельзя удалить (защита в ManualController::sectionDestroy)
+ * is_hierarchical — если true, значения двухуровневые (марка → модели); если false — плоский список
+ */
+
+namespace App\Models;
+
+use Illuminate\Database\Eloquent\Model;
+use Illuminate\Database\Eloquent\Relations\HasMany;
+
+class DictSection extends Model
+{
+    public $timestamps = false;
+
+    protected $fillable = ['code', 'label', 'is_system', 'is_hierarchical', 'sort_order'];
+
+    protected $casts = [
+        'is_system' => 'boolean',
+        'is_hierarchical' => 'boolean',
+    ];
+
+    // Только корневые значения (parent_id IS NULL), отсортированные — для плоских и иерархических разделов
+    public function values(): HasMany
+    {
+        return $this->hasMany(DictValue::class, 'section_id')->whereNull('parent_id')->orderBy('sort_order');
+    }
+
+    // Все значения раздела включая дочерние (для иерархических разделов типа 'makes')
+    public function allValues(): HasMany
+    {
+        return $this->hasMany(DictValue::class, 'section_id')->orderBy('sort_order');
+    }
+}

+ 41 - 0
app/Models/DictValue.php

@@ -0,0 +1,41 @@
+<?php
+
+/*
+ * DictValue — значение справочника (например: "Toyota", "Camry", "Седан").
+ *
+ * Создана: 2026-05-06 | Таблица: dict_values (миграция: 2026_05_06_151148_create_dict_values_table.php)
+ * $timestamps = false — значения не отслеживаются по времени
+ * parent_id — для иерархических разделов: NULL = корень (марка), не NULL = дочернее (модель)
+ * Отношения: section() → раздел, parent() → родитель, children() → дочерние значения
+ */
+
+namespace App\Models;
+
+use Illuminate\Database\Eloquent\Model;
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
+use Illuminate\Database\Eloquent\Relations\HasMany;
+
+class DictValue extends Model
+{
+    public $timestamps = false;
+
+    protected $fillable = ['section_id', 'parent_id', 'value', 'flag', 'logo', 'sort_order'];
+
+    // Раздел, которому принадлежит значение (например: 'makes', 'body_types')
+    public function section(): BelongsTo
+    {
+        return $this->belongsTo(DictSection::class, 'section_id');
+    }
+
+    // Родительское значение (для иерархий: модель → марка)
+    public function parent(): BelongsTo
+    {
+        return $this->belongsTo(DictValue::class, 'parent_id');
+    }
+
+    // Дочерние значения (для иерархий: марка → список моделей), отсортированные по sort_order
+    public function children(): HasMany
+    {
+        return $this->hasMany(DictValue::class, 'parent_id')->orderBy('sort_order');
+    }
+}

+ 36 - 0
app/Models/Lead.php

@@ -0,0 +1,36 @@
+<?php
+
+namespace App\Models;
+
+/*
+ * Lead — заявка, поступившая с веб-формы.
+ * Таблица: leads
+ * Поле data — JSON-объект: {field_name: value, ...}
+ * read_at — null = новая (непросмотренная) заявка
+ */
+
+use Illuminate\Database\Eloquent\Model;
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
+
+class Lead extends Model
+{
+    protected $fillable = ['form_id', 'data', 'ip'];
+
+    protected $casts = [
+        'data' => 'array',
+        'read_at' => 'datetime',
+    ];
+
+    public function form(): BelongsTo
+    {
+        return $this->belongsTo(WebForm::class, 'form_id');
+    }
+
+    // Пометить заявку как просмотренную
+    public function markAsRead(): void
+    {
+        if (! $this->read_at) {
+            $this->update(['read_at' => now()]);
+        }
+    }
+}

+ 40 - 0
app/Models/Page.php

@@ -0,0 +1,40 @@
+<?php
+
+namespace App\Models;
+
+/*
+ * Page — статическая страница сайта.
+ *
+ * Таблица: pages | slug — уникальный ключ ('home', 'services', 'contacts')
+ * Метод findBySlug() — удобный поиск с 404 при отсутствии
+ */
+
+use Illuminate\Database\Eloquent\Model;
+use Illuminate\Database\Eloquent\Relations\BelongsToMany;
+use Illuminate\Database\Eloquent\Relations\HasMany;
+
+class Page extends Model
+{
+    protected $fillable = ['slug', 'title', 'content', 'meta_title', 'meta_description', 'is_active'];
+
+    protected $casts = ['is_active' => 'boolean'];
+
+    public static function findBySlug(string $slug): self
+    {
+        return static::where('slug', $slug)->firstOrFail();
+    }
+
+    // Секции страницы (сводная таблица page_sections с порядком)
+    public function pageSections(): HasMany
+    {
+        return $this->hasMany(PageSection::class)->orderBy('sort_order');
+    }
+
+    // Блоки страницы через pivot — удобно для attach/detach/sync
+    public function blocks(): BelongsToMany
+    {
+        return $this->belongsToMany(Block::class, 'page_sections')
+            ->withPivot('sort_order')
+            ->orderByPivot('sort_order');
+    }
+}

+ 45 - 0
app/Models/PageSection.php

@@ -0,0 +1,45 @@
+<?php
+
+namespace App\Models;
+
+/*
+ * PageSection — одна секция страницы в page-builder.
+ *
+ * Два типа (поле type):
+ *   'block'   — ссылка на переиспользуемый блок (block_id → blocks)
+ *   'content' — произвольный HTML-контент (поле content)
+ *
+ * Порядок вывода на странице задаётся sort_order.
+ */
+
+use Illuminate\Database\Eloquent\Model;
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
+
+class PageSection extends Model
+{
+    protected $fillable = ['page_id', 'type', 'content', 'block_id', 'sort_order'];
+
+    public function page(): BelongsTo
+    {
+        return $this->belongsTo(Page::class);
+    }
+
+    public function block(): BelongsTo
+    {
+        return $this->belongsTo(Block::class);
+    }
+
+    // Возвращает HTML для рендера на публичной странице
+    public function getRenderedContent(): string
+    {
+        if ($this->type === 'content') {
+            return (string) $this->content;
+        }
+
+        if ($this->block && $this->block->is_active) {
+            return $this->block->render();
+        }
+
+        return '';
+    }
+}

+ 31 - 0
app/Models/Review.php

@@ -0,0 +1,31 @@
+<?php
+
+namespace App\Models;
+
+/*
+ * Review — отзыв клиента.
+ * Таблица: reviews (миграция 2026_05_07_135356_create_reviews_table.php)
+ * Управляется через Admin\ReviewAdminController.
+ * В блоке «Отзывы» (layout=reviews) выбираются по ID через reviews_picker.
+ */
+
+use Illuminate\Database\Eloquent\Model;
+
+class Review extends Model
+{
+    protected $fillable = [
+        'author',
+        'car_name',
+        'rating',
+        'body',
+        'review_date',
+        'is_active',
+        'sort_order',
+    ];
+
+    protected $casts = [
+        'is_active' => 'boolean',
+        'review_date' => 'date',
+        'rating' => 'integer',
+    ];
+}

+ 50 - 0
app/Models/Service.php

@@ -0,0 +1,50 @@
+<?php
+
+namespace App\Models;
+
+/*
+ * Service — услуга компании.
+ * Таблица: services
+ * Slug генерируется из title при создании, уникален.
+ * tags хранятся как JSON-массив строк.
+ */
+
+use Illuminate\Database\Eloquent\Builder;
+use Illuminate\Database\Eloquent\Model;
+use Illuminate\Support\Str;
+
+class Service extends Model
+{
+    protected $fillable = [
+        'title', 'slug', 'icon', 'excerpt', 'description', 'tags', 'sort_order', 'is_active',
+    ];
+
+    protected $casts = [
+        'tags'      => 'array',
+        'is_active' => 'boolean',
+    ];
+
+    // Генерирует уникальный slug из заголовка
+    public static function generateSlug(string $title, ?int $exceptId = null): string
+    {
+        $base = Str::slug($title);
+        $slug = $base;
+        $i    = 1;
+
+        while (
+            static::where('slug', $slug)
+                ->when($exceptId, fn (Builder $q) => $q->where('id', '!=', $exceptId))
+                ->exists()
+        ) {
+            $slug = $base . '-' . $i++;
+        }
+
+        return $slug;
+    }
+
+    // Scope: только активные услуги
+    public function scopeActive(Builder $query): Builder
+    {
+        return $query->where('is_active', true);
+    }
+}

+ 55 - 0
app/Models/Setting.php

@@ -0,0 +1,55 @@
+<?php
+
+namespace App\Models;
+
+/*
+ * Setting — key-value хранилище настроек сайта.
+ *
+ * Таблица: settings | Ключи: phone, email, telegram, youtube, vk
+ * get(key, default) — получить значение по ключу (через кеш 24ч)
+ * set(key, value)   — сохранить значение и сбросить кеш
+ * all()             — все настройки как ассоциативный массив (через кеш)
+ */
+
+use Illuminate\Database\Eloquent\Model;
+use Illuminate\Support\Collection;
+use Illuminate\Support\Facades\Cache;
+
+class Setting extends Model
+{
+    protected $fillable = ['key', 'value'];
+
+    private const CACHE_KEY = 'site_settings';
+
+    private const CACHE_TTL = 86400; // 24 часа
+
+    // Возвращает все настройки как ['key' => 'value', ...].
+    // Кешируем plain array, а не Collection — иначе file-кеш падает с __PHP_Incomplete_Class при unserialize.
+    public static function all($columns = ['*']): Collection
+    {
+        $data = Cache::remember(self::CACHE_KEY, self::CACHE_TTL, function () {
+            return parent::all(['key', 'value'])->pluck('value', 'key')->toArray();
+        });
+
+        return collect(is_array($data) ? $data : []);
+    }
+
+    // Получить одну настройку по ключу
+    public static function get(string $key, string $default = ''): string
+    {
+        return static::all()[$key] ?? $default;
+    }
+
+    // Сохранить настройку и сбросить кеш
+    public static function set(string $key, ?string $value): void
+    {
+        static::updateOrCreate(['key' => $key], ['value' => $value]);
+        Cache::forget(self::CACHE_KEY);
+    }
+
+    // Сбросить кеш настроек (вызывается после массового сохранения)
+    public static function clearCache(): void
+    {
+        Cache::forget(self::CACHE_KEY);
+    }
+}

+ 82 - 0
app/Models/User.php

@@ -0,0 +1,82 @@
+<?php
+
+namespace App\Models;
+
+/*
+ * User — модель пользователя.
+ *
+ * role: superadmin | admin | editor | viewer | null (нет доступа в админку)
+ * is_admin: legacy-флаг (оставлен, выставляется автоматически при установке роли)
+ * userPermissions(): индивидуальные override-права
+ * hasPermission(): быстрая проверка через PermissionService (без Gate)
+ */
+
+use App\Services\PermissionService;
+use Database\Factories\UserFactory;
+use Illuminate\Database\Eloquent\Factories\HasFactory;
+use Illuminate\Database\Eloquent\Relations\HasMany;
+use Illuminate\Foundation\Auth\User as Authenticatable;
+use Illuminate\Notifications\Notifiable;
+
+class User extends Authenticatable
+{
+    use HasFactory, Notifiable;
+
+    protected $fillable = ['name', 'email', 'password', 'is_admin', 'role'];
+
+    protected $hidden = ['password', 'remember_token'];
+
+    protected function casts(): array
+    {
+        return [
+            'email_verified_at' => 'datetime',
+            'password' => 'hashed',
+            'is_admin' => 'boolean',
+        ];
+    }
+
+    public function userPermissions(): HasMany
+    {
+        return $this->hasMany(UserPermission::class);
+    }
+
+    // Проверка права (eager-load relation один раз на запрос)
+    public function hasPermission(string $permission): bool
+    {
+        if (! $this->relationLoaded('userPermissions')) {
+            $this->load('userPermissions');
+        }
+
+        return PermissionService::userCan($this, $permission);
+    }
+
+    // Может ли пользователь входить в админку
+    public function isAdminUser(): bool
+    {
+        return (bool) $this->role || $this->is_admin;
+    }
+
+    // Может ли текущий пользователь управлять целевым
+    public function canManage(self $target): bool
+    {
+        if ($this->role === 'superadmin') {
+            return true;
+        }
+        if ($this->role === 'admin') {
+            return ! in_array($target->role, ['superadmin', 'admin'], true);
+        }
+
+        return false;
+    }
+
+    public function roleBadge(): array
+    {
+        return PermissionService::roleLabels()[$this->role]
+            ?? ['label' => 'Без роли', 'color' => 'secondary'];
+    }
+
+    protected static function newFactory(): UserFactory
+    {
+        return UserFactory::new();
+    }
+}

+ 13 - 0
app/Models/UserPermission.php

@@ -0,0 +1,13 @@
+<?php
+
+namespace App\Models;
+
+// UserPermission — индивидуальное разрешение/запрет для конкретного пользователя.
+// action: 'allow' перекрывает ограничения роли, 'deny' — запрещает даже то, что роль разрешает.
+
+use Illuminate\Database\Eloquent\Model;
+
+class UserPermission extends Model
+{
+    protected $fillable = ['user_id', 'permission', 'action'];
+}

+ 35 - 0
app/Models/WebForm.php

@@ -0,0 +1,35 @@
+<?php
+
+namespace App\Models;
+
+/*
+ * WebForm — модель веб-формы из конструктора форм.
+ * Таблица: web_forms
+ * Поле fields хранит JSON-массив объектов: [{label, name, type, width, required, options[]}, ...]
+ * Связи: hasMany Lead
+ */
+
+use Illuminate\Database\Eloquent\Model;
+use Illuminate\Database\Eloquent\Relations\HasMany;
+
+class WebForm extends Model
+{
+    protected $fillable = ['title', 'slug', 'notify_email', 'fields', 'is_active'];
+
+    protected $casts = [
+        'fields' => 'array',
+        'is_active' => 'boolean',
+    ];
+
+    // Заявки, поступившие через эту форму
+    public function leads(): HasMany
+    {
+        return $this->hasMany(Lead::class, 'form_id');
+    }
+
+    // Количество непросмотренных заявок
+    public function unreadLeadsCount(): int
+    {
+        return $this->leads()->whereNull('read_at')->count();
+    }
+}

+ 48 - 0
app/Providers/AppServiceProvider.php

@@ -0,0 +1,48 @@
+<?php
+
+namespace App\Providers;
+
+/*
+ * AppServiceProvider — View Composer для siteSettings + Gate-регистрация прав доступа.
+ *
+ * Gates регистрируются для всех ключей PermissionService::keys().
+ * В Blade: @can('cars.edit') ... @endcan
+ * В контроллерах: $this->authorize('cars.edit')
+ * В AdminLTE меню: 'can' => 'cars.view' (GateFilter включён по умолчанию)
+ */
+
+use App\Models\Setting;
+use App\Models\User;
+use App\Services\PermissionService;
+use Illuminate\Support\Facades\Gate;
+use Illuminate\Support\Facades\View;
+use Illuminate\Support\ServiceProvider;
+
+class AppServiceProvider extends ServiceProvider
+{
+    public function register(): void {}
+
+    public function boot(): void
+    {
+        // View Composer: настройки сайта в layout, шапке, подвале и страницах каталога
+        View::composer(
+            [
+                'layouts.app',
+                'layouts.partials._header',
+                'layouts.partials._footer',
+                'pages.catalog',
+                'pages.car',
+            ],
+            function ($view) {
+                $view->with('siteSettings', Setting::all());
+            }
+        );
+
+        // Gates: регистрируем gate для каждого ключа разрешения
+        foreach (PermissionService::keys() as $permission) {
+            Gate::define($permission, function (User $user) use ($permission) {
+                return $user->hasPermission($permission);
+            });
+        }
+    }
+}

+ 75 - 0
app/Services/ImageUploadService.php

@@ -0,0 +1,75 @@
+<?php
+
+namespace App\Services;
+
+/*
+ * ImageUploadService — загрузка, ресайз и хранение изображений блоков.
+ *
+ * Сохраняет в disk 'public' → storage/app/public/blocks/{folder}/uuid.webp
+ * Публичный URL:  asset('storage/blocks/{folder}/uuid.webp')
+ *
+ * Размеры из определения поля макета:
+ *   width + height → обрезать по центру (cover crop)
+ *   только width   → уменьшить до ширины, сохранить пропорции
+ *   ничего         → уменьшить до 1200px по ширине, сохранить пропорции
+ *
+ * Формат вывода всегда WebP (качество 85).
+ */
+
+use Illuminate\Http\UploadedFile;
+use Illuminate\Support\Facades\Storage;
+use Illuminate\Support\Str;
+use Intervention\Image\Drivers\Gd\Driver;
+use Intervention\Image\ImageManager;
+
+class ImageUploadService
+{
+    private ImageManager $manager;
+
+    public function __construct()
+    {
+        $this->manager = new ImageManager(new Driver());
+    }
+
+    /**
+     * Сохраняет загруженный файл, возвращает путь относительно диска 'public'.
+     * Например: 'blocks/steps_section/550e8400-e29b-41d4-a716-446655440000.webp'
+     *
+     * @param  array{width?:int, height?:int}  $fieldDef  — определение поля из BlockLayoutRegistry
+     */
+    public function store(UploadedFile $file, string $folder, array $fieldDef = []): string
+    {
+        $image = $this->manager->read($file->getRealPath());
+
+        $w = $fieldDef['width']  ?? null;
+        $h = $fieldDef['height'] ?? null;
+
+        if ($w && $h) {
+            // Cover-кроп: обрезаем по центру до точных размеров
+            $image->cover($w, $h);
+        } elseif ($w) {
+            // Масштабируем до ширины (не увеличиваем)
+            $image->scaleDown(width: $w);
+        } else {
+            // По умолчанию: не шире 1200px
+            $image->scaleDown(width: 1200);
+        }
+
+        $filename = Str::uuid() . '.webp';
+        $path     = "blocks/{$folder}/{$filename}";
+
+        Storage::disk('public')->put($path, $image->toWebp(85));
+
+        return $path;
+    }
+
+    /**
+     * Удаляет старый файл из диска 'public', если это внутренний файл (не внешний URL).
+     */
+    public function delete(string $oldPath): void
+    {
+        if ($oldPath && !str_starts_with($oldPath, 'http') && Storage::disk('public')->exists($oldPath)) {
+            Storage::disk('public')->delete($oldPath);
+        }
+    }
+}

+ 123 - 0
app/Services/PermissionService.php

@@ -0,0 +1,123 @@
+<?php
+
+namespace App\Services;
+
+/*
+ * PermissionService — единый реестр прав доступа.
+ *
+ * Роли (по убыванию силы): superadmin → admin → editor → viewer
+ * superadmin: все права автоматически
+ * admin: всё кроме users.manage (если не выдано явно)
+ * editor: управление контентом и автомобилями
+ * viewer: только просмотр
+ *
+ * Индивидуальные overrides (user_permissions) перекрывают роль:
+ *   action=allow → разрешает, даже если роль не даёт
+ *   action=deny  → запрещает, даже если роль разрешает
+ *
+ * superadmin НЕ может быть ограничен deny-записями.
+ */
+
+use App\Models\User;
+
+class PermissionService
+{
+    // Все доступные разрешения, сгруппированные по секции
+    public static function all(): array
+    {
+        return [
+            'Автомобили' => [
+                'cars.view' => 'Просмотр каталога авто',
+                'cars.edit' => 'Управление авто (создание / редактирование / удаление)',
+            ],
+            'Контент сайта' => [
+                'pages.view' => 'Просмотр страниц',
+                'pages.edit' => 'Редактирование страниц',
+                'blocks.view' => 'Просмотр блоков',
+                'blocks.edit' => 'Редактирование блоков',
+            ],
+            'Справочники' => [
+                'manuals.view' => 'Просмотр справочников',
+                'manuals.edit' => 'Управление справочниками',
+            ],
+            'Система' => [
+                'settings.view' => 'Просмотр настроек',
+                'settings.edit' => 'Изменение настроек',
+                'users.view' => 'Просмотр пользователей',
+                'users.manage' => 'Управление пользователями и правами',
+            ],
+        ];
+    }
+
+    // Плоский список ключей
+    public static function keys(): array
+    {
+        return array_merge(...array_values(array_map('array_keys', static::all())));
+    }
+
+    // Права роли по умолчанию
+    public static function roleDefaults(string $role): array
+    {
+        return match ($role) {
+            'superadmin' => static::keys(), // все
+            'admin' => [
+                'cars.view', 'cars.edit',
+                'pages.view', 'pages.edit',
+                'blocks.view', 'blocks.edit',
+                'manuals.view', 'manuals.edit',
+                'settings.view', 'settings.edit',
+                'users.view',
+                // users.manage — только если выдано явно
+            ],
+            'editor' => [
+                'cars.view', 'cars.edit',
+                'pages.view', 'pages.edit',
+                'blocks.view', 'blocks.edit',
+                'manuals.view',
+            ],
+            'viewer' => [
+                'cars.view',
+                'pages.view',
+                'blocks.view',
+                'manuals.view',
+            ],
+            default => [],
+        };
+    }
+
+    // Проверка: имеет ли пользователь конкретное право
+    public static function userCan(User $user, string $permission): bool
+    {
+        // superadmin всегда может
+        if ($user->role === 'superadmin') {
+            return true;
+        }
+
+        // Нет роли — нет доступа в админку
+        if (! $user->role) {
+            return false;
+        }
+
+        // Индивидуальный override (кешируется в сессии через preloaded relation)
+        $override = $user->userPermissions
+            ->firstWhere('permission', $permission);
+
+        if ($override) {
+            return $override->action === 'allow';
+        }
+
+        // Дефолты роли
+        return in_array($permission, static::roleDefaults($user->role), true);
+    }
+
+    // Метки ролей для UI
+    public static function roleLabels(): array
+    {
+        return [
+            'superadmin' => ['label' => 'Главный администратор', 'color' => 'danger'],
+            'admin' => ['label' => 'Администратор',         'color' => 'warning'],
+            'editor' => ['label' => 'Редактор',              'color' => 'info'],
+            'viewer' => ['label' => 'Наблюдатель',           'color' => 'secondary'],
+        ];
+    }
+}

+ 69 - 0
app/Support/BlockLayoutParser.php

@@ -0,0 +1,69 @@
+<?php
+
+namespace App\Support;
+
+use DOMDocument;
+use DOMElement;
+use App\Support\Traits\BlockLayoutDetectorTrait;
+use App\Support\Traits\BlockLayoutBuilderTrait;
+use App\Support\Traits\BlockLayoutHelpersTrait;
+
+/*
+ * BlockLayoutParser — разбирает произвольный HTML-фрагмент и автоматически:
+ *   1. Определяет поля блока (плоские или repeater-группу).
+ *   2. Генерирует черновик Blade-шаблона с {{ $data[...] }} / {{ $item[...] }}.
+ *
+ * Логика разбита по трейтам (app/Support/Traits/):
+ *   BlockLayoutDetectorTrait — detectRepeater, allSimilar
+ *   BlockLayoutBuilderTrait  — buildFlat, buildRepeater
+ *   BlockLayoutHelpersTrait  — extractFields, mkField, applyTokens, replaceTokens,
+ *                              serializeChildren, publicFields, elementChildren
+ */
+class BlockLayoutParser
+{
+    use BlockLayoutDetectorTrait;
+    use BlockLayoutBuilderTrait;
+    use BlockLayoutHelpersTrait;
+
+    private DOMDocument $dom;
+    private array $usedNames = [];
+    private array $tokens    = []; // token => bladeExpr
+
+    /**
+     * Разбирает HTML и возвращает:
+     *   fields       — определения полей для BlockLayoutRegistry
+     *   blade        — черновик Blade-шаблона
+     *   is_repeater  — обнаружена ли repeater-структура
+     */
+    public function parse(string $html): array
+    {
+        $this->usedNames = [];
+        $this->tokens    = [];
+
+        $this->dom = new DOMDocument('1.0', 'UTF-8');
+        libxml_use_internal_errors(true);
+
+        // charset-мета нужна чтобы DOMDocument корректно читал UTF-8 (emoji, кириллица)
+        $this->dom->loadHTML(
+            '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">'
+            . '<div id="BLPARSER">' . $html . '</div>'
+        );
+        libxml_clear_errors();
+
+        $wrapper = $this->dom->getElementById('BLPARSER');
+
+        if (!$wrapper) {
+            return ['fields' => [], 'blade' => $html, 'is_repeater' => false];
+        }
+
+        // Ищем repeater-группу
+        $repeater = $this->detectRepeater($wrapper);
+
+        if ($repeater) {
+            [$container, $items] = $repeater;
+            return $this->buildRepeater($wrapper, $container, $items);
+        }
+
+        return $this->buildFlat($wrapper);
+    }
+}

+ 195 - 0
app/Support/BlockLayoutRegistry.php

@@ -0,0 +1,195 @@
+<?php
+
+namespace App\Support;
+
+/*
+ * BlockLayoutRegistry — реестр макетов блоков.
+ *
+ * Каждый макет задаёт набор полей, которые пользователь заполняет в админке.
+ * Разработчик добавляет новый макет сюда и создаёт соответствующий Blade-шаблон
+ * resources/views/blocks/{key}.blade.php для рендеринга на фронтенде.
+ *
+ * Типы полей:
+ *   text     — однострочный текст
+ *   textarea — многострочный текст
+ *   url      — ссылка
+ *   image    — URL изображения
+ *   repeater — группа повторяющихся строк (sub_fields — список дочерних полей)
+ */
+class BlockLayoutRegistry
+{
+    public static function all(): array
+    {
+        // Встроенные макеты
+        $builtin = [
+
+            // ---------------------------------------------------------------
+            // Блок «Почему мы» — секция с карточками преимуществ
+            // ---------------------------------------------------------------
+            'why_us' => [
+                '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());
+    }
+}

+ 96 - 0
app/Support/Traits/BlockLayoutBuilderTrait.php

@@ -0,0 +1,96 @@
+<?php
+
+namespace App\Support\Traits;
+
+use DOMElement;
+
+/*
+ * Трейт: построение результирующего массива (fields + blade) для плоской
+ * структуры и для repeater-структуры.
+ * Используется классом BlockLayoutParser.
+ */
+trait BlockLayoutBuilderTrait
+{
+    // ── Плоская структура ─────────────────────────────────────────────────
+
+    private function buildFlat(DOMElement $wrapper): array
+    {
+        $raw = [];
+        foreach ($this->elementChildren($wrapper) as $child) {
+            $this->extractFields($child, $raw, '$data');
+        }
+
+        $this->applyTokens($raw);
+        $blade  = $this->serializeChildren($wrapper);
+        $blade  = $this->replaceTokens($blade);
+        $fields = $this->publicFields($raw);
+
+        return ['fields' => $fields, 'blade' => $blade, 'is_repeater' => false];
+    }
+
+    // ── Repeater ──────────────────────────────────────────────────────────
+
+    private function buildRepeater(DOMElement $wrapper, DOMElement $container, array $items): array
+    {
+        // Sub-fields извлекаем из первого элемента-образца
+        $rawSub = [];
+        $this->extractFields($items[0], $rawSub, '$item');
+        $this->applyTokens($rawSub);
+
+        // Blade для одного item
+        $itemBlade = $this->dom->saveHTML($items[0]);
+        $itemBlade = $this->replaceTokens($itemBlade);
+
+        // Блок @foreach
+        $foreachBlock = "@foreach(\$data['items'] as \$item)\n    "
+            . str_replace("\n", "\n    ", trim($itemBlade))
+            . "\n@endforeach";
+
+        if ($container === $wrapper) {
+            $blade = $foreachBlock;
+        } else {
+            // container — один из потомков wrapper;
+            // удаляем лишние item-дублёры, оставляем placeholder @foreach
+            foreach (array_slice($items, 1) as $item) {
+                $container->removeChild($item);
+            }
+            $foreachToken = 'BLADEFOREACHBLOCK';
+            $this->tokens[$foreachToken] = $foreachBlock;
+            $container->replaceChild(
+                $this->dom->createTextNode($foreachToken),
+                $items[0]
+            );
+
+            // Также ищем плоские поля в частях wrapper вне container
+            $outerRaw = [];
+            foreach ($this->elementChildren($wrapper) as $child) {
+                if ($child !== $container) {
+                    $this->extractFields($child, $outerRaw, '$data');
+                }
+            }
+            $this->applyTokens($outerRaw);
+
+            $blade  = $this->serializeChildren($wrapper);
+            $blade  = $this->replaceTokens($blade);
+
+            $outerFields = $this->publicFields($outerRaw);
+            $fields = array_merge($outerFields, [[
+                'name'       => 'items',
+                'label'      => 'Элементы',
+                'type'       => 'repeater',
+                'sub_fields' => $this->publicFields($rawSub),
+            ]]);
+
+            return ['fields' => $fields, 'blade' => $blade, 'is_repeater' => true];
+        }
+
+        $fields = [[
+            'name'       => 'items',
+            'label'      => 'Элементы',
+            'type'       => 'repeater',
+            'sub_fields' => $this->publicFields($rawSub),
+        ]];
+
+        return ['fields' => $fields, 'blade' => $blade, 'is_repeater' => true];
+    }
+}

+ 45 - 0
app/Support/Traits/BlockLayoutDetectorTrait.php

@@ -0,0 +1,45 @@
+<?php
+
+namespace App\Support\Traits;
+
+use DOMElement;
+
+/*
+ * Трейт: обнаружение repeater-структуры в DOM.
+ * Используется классом BlockLayoutParser.
+ */
+trait BlockLayoutDetectorTrait
+{
+    /** @return array{0:DOMElement,1:DOMElement[]}|null */
+    private function detectRepeater(DOMElement $wrapper): ?array
+    {
+        $children = $this->elementChildren($wrapper);
+
+        // Уровень 1: прямые дети обёртки одинаковы
+        if (count($children) >= 2 && $this->allSimilar($children)) {
+            return [$wrapper, $children];
+        }
+
+        // Уровень 2: единственный дочерний элемент содержит похожих детей
+        if (count($children) === 1) {
+            $grand = $this->elementChildren($children[0]);
+            if (count($grand) >= 2 && $this->allSimilar($grand)) {
+                return [$children[0], $grand];
+            }
+        }
+
+        return null;
+    }
+
+    /** Все элементы имеют одинаковый тег и класс (считаем «похожими»). */
+    private function allSimilar(array $els): bool
+    {
+        $tag   = strtolower($els[0]->tagName);
+        $class = $els[0]->getAttribute('class');
+        foreach (array_slice($els, 1) as $el) {
+            if (strtolower($el->tagName) !== $tag)     return false;
+            if ($el->getAttribute('class') !== $class) return false;
+        }
+        return true;
+    }
+}

+ 163 - 0
app/Support/Traits/BlockLayoutHelpersTrait.php

@@ -0,0 +1,163 @@
+<?php
+
+namespace App\Support\Traits;
+
+use DOMElement;
+
+/*
+ * Трейт: низкоуровневые хелперы BlockLayoutParser —
+ * извлечение полей, токенизация DOM, сериализация.
+ * Используется классом BlockLayoutParser.
+ */
+trait BlockLayoutHelpersTrait
+{
+    // ── Извлечение полей из DOM-элемента ──────────────────────────────────
+
+    /** Рекурсивно собирает поля из $el в массив $raw. */
+    private function extractFields(DOMElement $el, array &$raw, string $var): void
+    {
+        $tag = strtolower($el->tagName);
+
+        // Заголовки → text
+        if (in_array($tag, ['h1','h2','h3','h4','h5','h6'])) {
+            $text = trim($el->textContent);
+            if ($text !== '') {
+                $raw[] = $this->mkField('heading', 'Заголовок', 'text', $el, null, $var);
+            }
+            return;
+        }
+
+        // Абзац → textarea
+        if ($tag === 'p') {
+            $text = trim($el->textContent);
+            if ($text !== '') {
+                $raw[] = $this->mkField('text', 'Текст', 'textarea', $el, null, $var);
+            }
+            return;
+        }
+
+        // Картинка → image
+        if ($tag === 'img') {
+            if ($el->getAttribute('src') !== '') {
+                $raw[] = $this->mkField('image', 'Изображение', 'image', $el, 'src', $var);
+            }
+            return;
+        }
+
+        // Ссылка → url + text
+        if ($tag === 'a') {
+            $href = $el->getAttribute('href');
+            $text = trim($el->textContent);
+            if ($text !== '') {
+                $raw[] = $this->mkField('link_text', 'Текст ссылки', 'text', $el, null, $var);
+            }
+            if ($href !== '' && $href !== '#') {
+                $raw[] = $this->mkField('link_url', 'URL ссылки', 'url', $el, 'href', $var);
+            }
+            return;
+        }
+
+        // Листовой элемент с коротким текстом → text
+        $childEls = $this->elementChildren($el);
+        if (empty($childEls)) {
+            $text = trim($el->textContent);
+            if ($text !== '' && mb_strlen($text) <= 300) {
+                $raw[] = $this->mkField('label', 'Подпись', 'text', $el, null, $var);
+            }
+            return;
+        }
+
+        // Иначе рекурсируем
+        foreach ($childEls as $child) {
+            $this->extractFields($child, $raw, $var);
+        }
+    }
+
+    // ── Создание поля ─────────────────────────────────────────────────────
+
+    private function mkField(string $base, string $label, string $type, DOMElement $el, ?string $attr, string $var): array
+    {
+        $name = $base;
+        $i    = 2;
+        while (isset($this->usedNames[$name])) {
+            $name = $base . '_' . $i++;
+        }
+        $this->usedNames[$name] = true;
+
+        return [
+            'name'   => $name,
+            'label'  => $label,
+            'type'   => $type,
+            '_el'    => $el,
+            '_attr'  => $attr,
+            '_var'   => $var,
+        ];
+    }
+
+    // ── Токенизация DOM ───────────────────────────────────────────────────
+
+    /** Заменяет содержимое/атрибуты в DOM на уникальные токены. */
+    private function applyTokens(array $raw): void
+    {
+        foreach ($raw as $field) {
+            $token = 'BLTKN' . strtoupper($field['name']) . 'END';
+            $expr  = "{{ {$field['_var']}['{$field['name']}'] }}";
+            $this->tokens[$token] = $expr;
+
+            /** @var DOMElement $el */
+            $el = $field['_el'];
+
+            if ($field['_attr'] !== null) {
+                $el->setAttribute($field['_attr'], $token);
+            } else {
+                while ($el->firstChild) {
+                    $el->removeChild($el->firstChild);
+                }
+                $el->appendChild($this->dom->createTextNode($token));
+            }
+        }
+    }
+
+    private function replaceTokens(string $html): string
+    {
+        foreach ($this->tokens as $token => $expr) {
+            $html = str_replace($token, $expr, $html);
+        }
+        return $html;
+    }
+
+    // ── Сериализация ──────────────────────────────────────────────────────
+
+    private function serializeChildren(DOMElement $el): string
+    {
+        $html = '';
+        foreach ($el->childNodes as $child) {
+            $html .= $this->dom->saveHTML($child);
+        }
+        return $html;
+    }
+
+    // ── Хелперы ───────────────────────────────────────────────────────────
+
+    /** Возвращает только публичные поля (без служебных _el, _attr, _var). */
+    private function publicFields(array $raw): array
+    {
+        return array_values(array_map(fn($f) => array_filter(
+            $f,
+            fn($k) => !str_starts_with($k, '_'),
+            ARRAY_FILTER_USE_KEY
+        ), $raw));
+    }
+
+    /** Возвращает дочерние DOM-элементы (без текстовых узлов). */
+    private function elementChildren(DOMElement $el): array
+    {
+        $result = [];
+        foreach ($el->childNodes as $node) {
+            if ($node->nodeType === XML_ELEMENT_NODE) {
+                $result[] = $node;
+            }
+        }
+        return $result;
+    }
+}

+ 0 - 0
app/Support/layouts/.gitkeep


+ 18 - 0
artisan

@@ -0,0 +1,18 @@
+#!/usr/bin/env php
+<?php
+
+use Illuminate\Foundation\Application;
+use Symfony\Component\Console\Input\ArgvInput;
+
+define('LARAVEL_START', microtime(true));
+
+// Register the Composer autoloader...
+require __DIR__.'/vendor/autoload.php';
+
+// Bootstrap Laravel and handle the command...
+/** @var Application $app */
+$app = require_once __DIR__.'/bootstrap/app.php';
+
+$status = $app->handleCommand(new ArgvInput);
+
+exit($status);

+ 14 - 0
boost.json

@@ -0,0 +1,14 @@
+{
+    "agents": [
+        "cursor"
+    ],
+    "cloud": false,
+    "guidelines": true,
+    "mcp": true,
+    "nightwatch": false,
+    "sail": false,
+    "skills": [
+        "laravel-best-practices",
+        "tailwindcss-development"
+    ]
+}

+ 22 - 0
bootstrap/app.php

@@ -0,0 +1,22 @@
+<?php
+
+use Illuminate\Foundation\Application;
+use Illuminate\Foundation\Configuration\Exceptions;
+use Illuminate\Foundation\Configuration\Middleware;
+
+return Application::configure(basePath: dirname(__DIR__))
+    ->withRouting(
+        web: __DIR__.'/../routes/web.php',
+        commands: __DIR__.'/../routes/console.php',
+        health: '/up',
+    )
+    ->withMiddleware(function (Middleware $middleware): void {
+        $middleware->alias([
+            'admin'       => \App\Http\Middleware\EnsureUserIsAdmin::class,
+            'maintenance' => \App\Http\Middleware\SiteMaintenanceMiddleware::class,
+        ]);
+        $middleware->appendToGroup('web', \App\Http\Middleware\TrackPageVisit::class);
+    })
+    ->withExceptions(function (Exceptions $exceptions): void {
+        //
+    })->create();

+ 2 - 0
bootstrap/cache/.gitignore

@@ -0,0 +1,2 @@
+*
+!.gitignore

+ 7 - 0
bootstrap/providers.php

@@ -0,0 +1,7 @@
+<?php
+
+use App\Providers\AppServiceProvider;
+
+return [
+    AppServiceProvider::class,
+];

+ 93 - 0
composer.json

@@ -0,0 +1,93 @@
+{
+    "$schema": "https://getcomposer.org/schema.json",
+    "name": "laravel/laravel",
+    "type": "project",
+    "description": "The skeleton application for the Laravel framework.",
+    "keywords": [
+        "laravel",
+        "framework"
+    ],
+    "license": "MIT",
+    "require": {
+        "php": "^8.3",
+        "intervention/image": "^4.0",
+        "jeroennoten/laravel-adminlte": "*",
+        "laravel/framework": "^13.7",
+        "laravel/tinker": "^3.0"
+    },
+    "require-dev": {
+        "fakerphp/faker": "^1.23",
+        "laravel/boost": "^2.2",
+        "laravel/pail": "^1.2.5",
+        "laravel/pao": "^1.0.6",
+        "laravel/pint": "^1.27",
+        "mockery/mockery": "^1.6",
+        "nunomaduro/collision": "^8.6",
+        "phpunit/phpunit": "^12.5.12"
+    },
+    "autoload": {
+        "psr-4": {
+            "App\\": "app/",
+            "Database\\Factories\\": "database/factories/",
+            "Database\\Seeders\\": "database/seeders/"
+        }
+    },
+    "autoload-dev": {
+        "psr-4": {
+            "Tests\\": "tests/"
+        }
+    },
+    "scripts": {
+        "setup": [
+            "composer install",
+            "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"",
+            "@php artisan key:generate",
+            "@php artisan migrate --force",
+            "npm install --ignore-scripts",
+            "npm run build"
+        ],
+        "dev": [
+            "Composer\\Config::disableProcessTimeout",
+            "npx concurrently -c \"#93c5fd,#c4b5fd,#fb7185,#fdba74\" \"php artisan serve\" \"php artisan queue:listen --tries=1 --timeout=0\" \"php artisan pail --timeout=0\" \"npm run dev\" --names=server,queue,logs,vite --kill-others"
+        ],
+        "test": [
+            "@php artisan config:clear --ansi @no_additional_args",
+            "@php artisan test"
+        ],
+        "post-autoload-dump": [
+            "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
+            "@php artisan package:discover --ansi"
+        ],
+        "post-update-cmd": [
+            "@php artisan vendor:publish --tag=laravel-assets --ansi --force",
+            "@php artisan boost:update --ansi"
+        ],
+        "post-root-package-install": [
+            "@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
+        ],
+        "post-create-project-cmd": [
+            "@php artisan key:generate --ansi",
+            "@php -r \"file_exists('database/database.sqlite') || touch('database/database.sqlite');\"",
+            "@php artisan migrate --graceful --ansi"
+        ],
+        "pre-package-uninstall": [
+            "Illuminate\\Foundation\\ComposerScripts::prePackageUninstall"
+        ]
+    },
+    "extra": {
+        "laravel": {
+            "dont-discover": []
+        }
+    },
+    "config": {
+        "optimize-autoloader": true,
+        "preferred-install": "dist",
+        "sort-packages": true,
+        "allow-plugins": {
+            "pestphp/pest-plugin": true,
+            "php-http/discovery": true
+        }
+    },
+    "minimum-stability": "stable",
+    "prefer-stable": true
+}

+ 8774 - 0
composer.lock

@@ -0,0 +1,8774 @@
+{
+    "_readme": [
+        "This file locks the dependencies of your project to a known state",
+        "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
+        "This file is @generated automatically"
+    ],
+    "content-hash": "63b066585ebfa6883990bfd247dd0390",
+    "packages": [
+        {
+            "name": "almasaeed2010/adminlte",
+            "version": "v3.2.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/ColorlibHQ/AdminLTE.git",
+                "reference": "bd4d9c72931f1dd28601b6bfb387554a381ad540"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/ColorlibHQ/AdminLTE/zipball/bd4d9c72931f1dd28601b6bfb387554a381ad540",
+                "reference": "bd4d9c72931f1dd28601b6bfb387554a381ad540",
+                "shasum": ""
+            },
+            "type": "library",
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Colorlib"
+                }
+            ],
+            "description": "AdminLTE - admin control panel and dashboard that's based on Bootstrap 4",
+            "homepage": "https://adminlte.io/",
+            "keywords": [
+                "JS",
+                "admin",
+                "back-end",
+                "css",
+                "less",
+                "responsive",
+                "template",
+                "theme",
+                "web"
+            ],
+            "support": {
+                "issues": "https://github.com/ColorlibHQ/AdminLTE/issues",
+                "source": "https://github.com/ColorlibHQ/AdminLTE/tree/v3.2.0"
+            },
+            "time": "2022-02-07T20:33:09+00:00"
+        },
+        {
+            "name": "brick/math",
+            "version": "0.14.8",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/brick/math.git",
+                "reference": "63422359a44b7f06cae63c3b429b59e8efcc0629"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/brick/math/zipball/63422359a44b7f06cae63c3b429b59e8efcc0629",
+                "reference": "63422359a44b7f06cae63c3b429b59e8efcc0629",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^8.2"
+            },
+            "require-dev": {
+                "php-coveralls/php-coveralls": "^2.2",
+                "phpstan/phpstan": "2.1.22",
+                "phpunit/phpunit": "^11.5"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Brick\\Math\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "Arbitrary-precision arithmetic library",
+            "keywords": [
+                "Arbitrary-precision",
+                "BigInteger",
+                "BigRational",
+                "arithmetic",
+                "bigdecimal",
+                "bignum",
+                "bignumber",
+                "brick",
+                "decimal",
+                "integer",
+                "math",
+                "mathematics",
+                "rational"
+            ],
+            "support": {
+                "issues": "https://github.com/brick/math/issues",
+                "source": "https://github.com/brick/math/tree/0.14.8"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/BenMorel",
+                    "type": "github"
+                }
+            ],
+            "time": "2026-02-10T14:33:43+00:00"
+        },
+        {
+            "name": "carbonphp/carbon-doctrine-types",
+            "version": "3.2.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/CarbonPHP/carbon-doctrine-types.git",
+                "reference": "18ba5ddfec8976260ead6e866180bd5d2f71aa1d"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/CarbonPHP/carbon-doctrine-types/zipball/18ba5ddfec8976260ead6e866180bd5d2f71aa1d",
+                "reference": "18ba5ddfec8976260ead6e866180bd5d2f71aa1d",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^8.1"
+            },
+            "conflict": {
+                "doctrine/dbal": "<4.0.0 || >=5.0.0"
+            },
+            "require-dev": {
+                "doctrine/dbal": "^4.0.0",
+                "nesbot/carbon": "^2.71.0 || ^3.0.0",
+                "phpunit/phpunit": "^10.3"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Carbon\\Doctrine\\": "src/Carbon/Doctrine/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "KyleKatarn",
+                    "email": "kylekatarnls@gmail.com"
+                }
+            ],
+            "description": "Types to use Carbon in Doctrine",
+            "keywords": [
+                "carbon",
+                "date",
+                "datetime",
+                "doctrine",
+                "time"
+            ],
+            "support": {
+                "issues": "https://github.com/CarbonPHP/carbon-doctrine-types/issues",
+                "source": "https://github.com/CarbonPHP/carbon-doctrine-types/tree/3.2.0"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/kylekatarnls",
+                    "type": "github"
+                },
+                {
+                    "url": "https://opencollective.com/Carbon",
+                    "type": "open_collective"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/nesbot/carbon",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2024-02-09T16:56:22+00:00"
+        },
+        {
+            "name": "dflydev/dot-access-data",
+            "version": "v3.0.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/dflydev/dflydev-dot-access-data.git",
+                "reference": "a23a2bf4f31d3518f3ecb38660c95715dfead60f"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/dflydev/dflydev-dot-access-data/zipball/a23a2bf4f31d3518f3ecb38660c95715dfead60f",
+                "reference": "a23a2bf4f31d3518f3ecb38660c95715dfead60f",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.1 || ^8.0"
+            },
+            "require-dev": {
+                "phpstan/phpstan": "^0.12.42",
+                "phpunit/phpunit": "^7.5 || ^8.5 || ^9.3",
+                "scrutinizer/ocular": "1.6.0",
+                "squizlabs/php_codesniffer": "^3.5",
+                "vimeo/psalm": "^4.0.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "3.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Dflydev\\DotAccessData\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Dragonfly Development Inc.",
+                    "email": "info@dflydev.com",
+                    "homepage": "http://dflydev.com"
+                },
+                {
+                    "name": "Beau Simensen",
+                    "email": "beau@dflydev.com",
+                    "homepage": "http://beausimensen.com"
+                },
+                {
+                    "name": "Carlos Frutos",
+                    "email": "carlos@kiwing.it",
+                    "homepage": "https://github.com/cfrutos"
+                },
+                {
+                    "name": "Colin O'Dell",
+                    "email": "colinodell@gmail.com",
+                    "homepage": "https://www.colinodell.com"
+                }
+            ],
+            "description": "Given a deep data structure, access data by dot notation.",
+            "homepage": "https://github.com/dflydev/dflydev-dot-access-data",
+            "keywords": [
+                "access",
+                "data",
+                "dot",
+                "notation"
+            ],
+            "support": {
+                "issues": "https://github.com/dflydev/dflydev-dot-access-data/issues",
+                "source": "https://github.com/dflydev/dflydev-dot-access-data/tree/v3.0.3"
+            },
+            "time": "2024-07-08T12:26:09+00:00"
+        },
+        {
+            "name": "doctrine/inflector",
+            "version": "2.1.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/doctrine/inflector.git",
+                "reference": "6d6c96277ea252fc1304627204c3d5e6e15faa3b"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/doctrine/inflector/zipball/6d6c96277ea252fc1304627204c3d5e6e15faa3b",
+                "reference": "6d6c96277ea252fc1304627204c3d5e6e15faa3b",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.2 || ^8.0"
+            },
+            "require-dev": {
+                "doctrine/coding-standard": "^12.0 || ^13.0",
+                "phpstan/phpstan": "^1.12 || ^2.0",
+                "phpstan/phpstan-phpunit": "^1.4 || ^2.0",
+                "phpstan/phpstan-strict-rules": "^1.6 || ^2.0",
+                "phpunit/phpunit": "^8.5 || ^12.2"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Doctrine\\Inflector\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Guilherme Blanco",
+                    "email": "guilhermeblanco@gmail.com"
+                },
+                {
+                    "name": "Roman Borschel",
+                    "email": "roman@code-factory.org"
+                },
+                {
+                    "name": "Benjamin Eberlei",
+                    "email": "kontakt@beberlei.de"
+                },
+                {
+                    "name": "Jonathan Wage",
+                    "email": "jonwage@gmail.com"
+                },
+                {
+                    "name": "Johannes Schmitt",
+                    "email": "schmittjoh@gmail.com"
+                }
+            ],
+            "description": "PHP Doctrine Inflector is a small library that can perform string manipulations with regard to upper/lowercase and singular/plural forms of words.",
+            "homepage": "https://www.doctrine-project.org/projects/inflector.html",
+            "keywords": [
+                "inflection",
+                "inflector",
+                "lowercase",
+                "manipulation",
+                "php",
+                "plural",
+                "singular",
+                "strings",
+                "uppercase",
+                "words"
+            ],
+            "support": {
+                "issues": "https://github.com/doctrine/inflector/issues",
+                "source": "https://github.com/doctrine/inflector/tree/2.1.0"
+            },
+            "funding": [
+                {
+                    "url": "https://www.doctrine-project.org/sponsorship.html",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://www.patreon.com/phpdoctrine",
+                    "type": "patreon"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finflector",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2025-08-10T19:31:58+00:00"
+        },
+        {
+            "name": "doctrine/lexer",
+            "version": "3.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/doctrine/lexer.git",
+                "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/doctrine/lexer/zipball/31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd",
+                "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^8.1"
+            },
+            "require-dev": {
+                "doctrine/coding-standard": "^12",
+                "phpstan/phpstan": "^1.10",
+                "phpunit/phpunit": "^10.5",
+                "psalm/plugin-phpunit": "^0.18.3",
+                "vimeo/psalm": "^5.21"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Doctrine\\Common\\Lexer\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Guilherme Blanco",
+                    "email": "guilhermeblanco@gmail.com"
+                },
+                {
+                    "name": "Roman Borschel",
+                    "email": "roman@code-factory.org"
+                },
+                {
+                    "name": "Johannes Schmitt",
+                    "email": "schmittjoh@gmail.com"
+                }
+            ],
+            "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.",
+            "homepage": "https://www.doctrine-project.org/projects/lexer.html",
+            "keywords": [
+                "annotations",
+                "docblock",
+                "lexer",
+                "parser",
+                "php"
+            ],
+            "support": {
+                "issues": "https://github.com/doctrine/lexer/issues",
+                "source": "https://github.com/doctrine/lexer/tree/3.0.1"
+            },
+            "funding": [
+                {
+                    "url": "https://www.doctrine-project.org/sponsorship.html",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://www.patreon.com/phpdoctrine",
+                    "type": "patreon"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2024-02-05T11:56:58+00:00"
+        },
+        {
+            "name": "dragonmantank/cron-expression",
+            "version": "v3.6.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/dragonmantank/cron-expression.git",
+                "reference": "d61a8a9604ec1f8c3d150d09db6ce98b32675013"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/d61a8a9604ec1f8c3d150d09db6ce98b32675013",
+                "reference": "d61a8a9604ec1f8c3d150d09db6ce98b32675013",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^8.2|^8.3|^8.4|^8.5"
+            },
+            "replace": {
+                "mtdowling/cron-expression": "^1.0"
+            },
+            "require-dev": {
+                "phpstan/extension-installer": "^1.4.3",
+                "phpstan/phpstan": "^1.12.32|^2.1.31",
+                "phpunit/phpunit": "^8.5.48|^9.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Cron\\": "src/Cron/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Chris Tankersley",
+                    "email": "chris@ctankersley.com",
+                    "homepage": "https://github.com/dragonmantank"
+                }
+            ],
+            "description": "CRON for PHP: Calculate the next or previous run date and determine if a CRON expression is due",
+            "keywords": [
+                "cron",
+                "schedule"
+            ],
+            "support": {
+                "issues": "https://github.com/dragonmantank/cron-expression/issues",
+                "source": "https://github.com/dragonmantank/cron-expression/tree/v3.6.0"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/dragonmantank",
+                    "type": "github"
+                }
+            ],
+            "time": "2025-10-31T18:51:33+00:00"
+        },
+        {
+            "name": "egulias/email-validator",
+            "version": "4.0.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/egulias/EmailValidator.git",
+                "reference": "d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa",
+                "reference": "d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa",
+                "shasum": ""
+            },
+            "require": {
+                "doctrine/lexer": "^2.0 || ^3.0",
+                "php": ">=8.1",
+                "symfony/polyfill-intl-idn": "^1.26"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^10.2",
+                "vimeo/psalm": "^5.12"
+            },
+            "suggest": {
+                "ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "4.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Egulias\\EmailValidator\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Eduardo Gulias Davis"
+                }
+            ],
+            "description": "A library for validating emails against several RFCs",
+            "homepage": "https://github.com/egulias/EmailValidator",
+            "keywords": [
+                "email",
+                "emailvalidation",
+                "emailvalidator",
+                "validation",
+                "validator"
+            ],
+            "support": {
+                "issues": "https://github.com/egulias/EmailValidator/issues",
+                "source": "https://github.com/egulias/EmailValidator/tree/4.0.4"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/egulias",
+                    "type": "github"
+                }
+            ],
+            "time": "2025-03-06T22:45:56+00:00"
+        },
+        {
+            "name": "fruitcake/php-cors",
+            "version": "v1.4.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/fruitcake/php-cors.git",
+                "reference": "38aaa6c3fd4c157ffe2a4d10aa8b9b16ba8de379"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/fruitcake/php-cors/zipball/38aaa6c3fd4c157ffe2a4d10aa8b9b16ba8de379",
+                "reference": "38aaa6c3fd4c157ffe2a4d10aa8b9b16ba8de379",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^8.1",
+                "symfony/http-foundation": "^5.4|^6.4|^7.3|^8"
+            },
+            "require-dev": {
+                "phpstan/phpstan": "^2",
+                "phpunit/phpunit": "^9",
+                "squizlabs/php_codesniffer": "^4"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.3-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Fruitcake\\Cors\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fruitcake",
+                    "homepage": "https://fruitcake.nl"
+                },
+                {
+                    "name": "Barryvdh",
+                    "email": "barryvdh@gmail.com"
+                }
+            ],
+            "description": "Cross-origin resource sharing library for the Symfony HttpFoundation",
+            "homepage": "https://github.com/fruitcake/php-cors",
+            "keywords": [
+                "cors",
+                "laravel",
+                "symfony"
+            ],
+            "support": {
+                "issues": "https://github.com/fruitcake/php-cors/issues",
+                "source": "https://github.com/fruitcake/php-cors/tree/v1.4.0"
+            },
+            "funding": [
+                {
+                    "url": "https://fruitcake.nl",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/barryvdh",
+                    "type": "github"
+                }
+            ],
+            "time": "2025-12-03T09:33:47+00:00"
+        },
+        {
+            "name": "graham-campbell/result-type",
+            "version": "v1.1.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/GrahamCampbell/Result-Type.git",
+                "reference": "e01f4a821471308ba86aa202fed6698b6b695e3b"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/e01f4a821471308ba86aa202fed6698b6b695e3b",
+                "reference": "e01f4a821471308ba86aa202fed6698b6b695e3b",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.2.5 || ^8.0",
+                "phpoption/phpoption": "^1.9.5"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^8.5.41 || ^9.6.22 || ^10.5.45 || ^11.5.7"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "GrahamCampbell\\ResultType\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Graham Campbell",
+                    "email": "hello@gjcampbell.co.uk",
+                    "homepage": "https://github.com/GrahamCampbell"
+                }
+            ],
+            "description": "An Implementation Of The Result Type",
+            "keywords": [
+                "Graham Campbell",
+                "GrahamCampbell",
+                "Result Type",
+                "Result-Type",
+                "result"
+            ],
+            "support": {
+                "issues": "https://github.com/GrahamCampbell/Result-Type/issues",
+                "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.4"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/GrahamCampbell",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2025-12-27T19:43:20+00:00"
+        },
+        {
+            "name": "guzzlehttp/guzzle",
+            "version": "7.10.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/guzzle/guzzle.git",
+                "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b51ac707cfa420b7bfd4e4d5e510ba8008e822b4",
+                "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4",
+                "shasum": ""
+            },
+            "require": {
+                "ext-json": "*",
+                "guzzlehttp/promises": "^2.3",
+                "guzzlehttp/psr7": "^2.8",
+                "php": "^7.2.5 || ^8.0",
+                "psr/http-client": "^1.0",
+                "symfony/deprecation-contracts": "^2.2 || ^3.0"
+            },
+            "provide": {
+                "psr/http-client-implementation": "1.0"
+            },
+            "require-dev": {
+                "bamarni/composer-bin-plugin": "^1.8.2",
+                "ext-curl": "*",
+                "guzzle/client-integration-tests": "3.0.2",
+                "php-http/message-factory": "^1.1",
+                "phpunit/phpunit": "^8.5.39 || ^9.6.20",
+                "psr/log": "^1.1 || ^2.0 || ^3.0"
+            },
+            "suggest": {
+                "ext-curl": "Required for CURL handler support",
+                "ext-intl": "Required for Internationalized Domain Name (IDN) support",
+                "psr/log": "Required for using the Log middleware"
+            },
+            "type": "library",
+            "extra": {
+                "bamarni-bin": {
+                    "bin-links": true,
+                    "forward-command": false
+                }
+            },
+            "autoload": {
+                "files": [
+                    "src/functions_include.php"
+                ],
+                "psr-4": {
+                    "GuzzleHttp\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Graham Campbell",
+                    "email": "hello@gjcampbell.co.uk",
+                    "homepage": "https://github.com/GrahamCampbell"
+                },
+                {
+                    "name": "Michael Dowling",
+                    "email": "mtdowling@gmail.com",
+                    "homepage": "https://github.com/mtdowling"
+                },
+                {
+                    "name": "Jeremy Lindblom",
+                    "email": "jeremeamia@gmail.com",
+                    "homepage": "https://github.com/jeremeamia"
+                },
+                {
+                    "name": "George Mponos",
+                    "email": "gmponos@gmail.com",
+                    "homepage": "https://github.com/gmponos"
+                },
+                {
+                    "name": "Tobias Nyholm",
+                    "email": "tobias.nyholm@gmail.com",
+                    "homepage": "https://github.com/Nyholm"
+                },
+                {
+                    "name": "Márk Sági-Kazár",
+                    "email": "mark.sagikazar@gmail.com",
+                    "homepage": "https://github.com/sagikazarmark"
+                },
+                {
+                    "name": "Tobias Schultze",
+                    "email": "webmaster@tubo-world.de",
+                    "homepage": "https://github.com/Tobion"
+                }
+            ],
+            "description": "Guzzle is a PHP HTTP client library",
+            "keywords": [
+                "client",
+                "curl",
+                "framework",
+                "http",
+                "http client",
+                "psr-18",
+                "psr-7",
+                "rest",
+                "web service"
+            ],
+            "support": {
+                "issues": "https://github.com/guzzle/guzzle/issues",
+                "source": "https://github.com/guzzle/guzzle/tree/7.10.0"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/GrahamCampbell",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/Nyholm",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2025-08-23T22:36:01+00:00"
+        },
+        {
+            "name": "guzzlehttp/promises",
+            "version": "2.3.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/guzzle/promises.git",
+                "reference": "481557b130ef3790cf82b713667b43030dc9c957"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/guzzle/promises/zipball/481557b130ef3790cf82b713667b43030dc9c957",
+                "reference": "481557b130ef3790cf82b713667b43030dc9c957",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.2.5 || ^8.0"
+            },
+            "require-dev": {
+                "bamarni/composer-bin-plugin": "^1.8.2",
+                "phpunit/phpunit": "^8.5.44 || ^9.6.25"
+            },
+            "type": "library",
+            "extra": {
+                "bamarni-bin": {
+                    "bin-links": true,
+                    "forward-command": false
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "GuzzleHttp\\Promise\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Graham Campbell",
+                    "email": "hello@gjcampbell.co.uk",
+                    "homepage": "https://github.com/GrahamCampbell"
+                },
+                {
+                    "name": "Michael Dowling",
+                    "email": "mtdowling@gmail.com",
+                    "homepage": "https://github.com/mtdowling"
+                },
+                {
+                    "name": "Tobias Nyholm",
+                    "email": "tobias.nyholm@gmail.com",
+                    "homepage": "https://github.com/Nyholm"
+                },
+                {
+                    "name": "Tobias Schultze",
+                    "email": "webmaster@tubo-world.de",
+                    "homepage": "https://github.com/Tobion"
+                }
+            ],
+            "description": "Guzzle promises library",
+            "keywords": [
+                "promise"
+            ],
+            "support": {
+                "issues": "https://github.com/guzzle/promises/issues",
+                "source": "https://github.com/guzzle/promises/tree/2.3.0"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/GrahamCampbell",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/Nyholm",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2025-08-22T14:34:08+00:00"
+        },
+        {
+            "name": "guzzlehttp/psr7",
+            "version": "2.9.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/guzzle/psr7.git",
+                "reference": "7d0ed42f28e42d61352a7a79de682e5e67fec884"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/guzzle/psr7/zipball/7d0ed42f28e42d61352a7a79de682e5e67fec884",
+                "reference": "7d0ed42f28e42d61352a7a79de682e5e67fec884",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.2.5 || ^8.0",
+                "psr/http-factory": "^1.0",
+                "psr/http-message": "^1.1 || ^2.0",
+                "ralouphie/getallheaders": "^3.0"
+            },
+            "provide": {
+                "psr/http-factory-implementation": "1.0",
+                "psr/http-message-implementation": "1.0"
+            },
+            "require-dev": {
+                "bamarni/composer-bin-plugin": "^1.8.2",
+                "http-interop/http-factory-tests": "0.9.0",
+                "jshttp/mime-db": "1.54.0.1",
+                "phpunit/phpunit": "^8.5.44 || ^9.6.25"
+            },
+            "suggest": {
+                "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
+            },
+            "type": "library",
+            "extra": {
+                "bamarni-bin": {
+                    "bin-links": true,
+                    "forward-command": false
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "GuzzleHttp\\Psr7\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Graham Campbell",
+                    "email": "hello@gjcampbell.co.uk",
+                    "homepage": "https://github.com/GrahamCampbell"
+                },
+                {
+                    "name": "Michael Dowling",
+                    "email": "mtdowling@gmail.com",
+                    "homepage": "https://github.com/mtdowling"
+                },
+                {
+                    "name": "George Mponos",
+                    "email": "gmponos@gmail.com",
+                    "homepage": "https://github.com/gmponos"
+                },
+                {
+                    "name": "Tobias Nyholm",
+                    "email": "tobias.nyholm@gmail.com",
+                    "homepage": "https://github.com/Nyholm"
+                },
+                {
+                    "name": "Márk Sági-Kazár",
+                    "email": "mark.sagikazar@gmail.com",
+                    "homepage": "https://github.com/sagikazarmark"
+                },
+                {
+                    "name": "Tobias Schultze",
+                    "email": "webmaster@tubo-world.de",
+                    "homepage": "https://github.com/Tobion"
+                },
+                {
+                    "name": "Márk Sági-Kazár",
+                    "email": "mark.sagikazar@gmail.com",
+                    "homepage": "https://sagikazarmark.hu"
+                }
+            ],
+            "description": "PSR-7 message implementation that also provides common utility methods",
+            "keywords": [
+                "http",
+                "message",
+                "psr-7",
+                "request",
+                "response",
+                "stream",
+                "uri",
+                "url"
+            ],
+            "support": {
+                "issues": "https://github.com/guzzle/psr7/issues",
+                "source": "https://github.com/guzzle/psr7/tree/2.9.0"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/GrahamCampbell",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/Nyholm",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2026-03-10T16:41:02+00:00"
+        },
+        {
+            "name": "guzzlehttp/uri-template",
+            "version": "v1.0.5",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/guzzle/uri-template.git",
+                "reference": "4f4bbd4e7172148801e76e3decc1e559bdee34e1"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/guzzle/uri-template/zipball/4f4bbd4e7172148801e76e3decc1e559bdee34e1",
+                "reference": "4f4bbd4e7172148801e76e3decc1e559bdee34e1",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.2.5 || ^8.0",
+                "symfony/polyfill-php80": "^1.24"
+            },
+            "require-dev": {
+                "bamarni/composer-bin-plugin": "^1.8.2",
+                "phpunit/phpunit": "^8.5.44 || ^9.6.25",
+                "uri-template/tests": "1.0.0"
+            },
+            "type": "library",
+            "extra": {
+                "bamarni-bin": {
+                    "bin-links": true,
+                    "forward-command": false
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "GuzzleHttp\\UriTemplate\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Graham Campbell",
+                    "email": "hello@gjcampbell.co.uk",
+                    "homepage": "https://github.com/GrahamCampbell"
+                },
+                {
+                    "name": "Michael Dowling",
+                    "email": "mtdowling@gmail.com",
+                    "homepage": "https://github.com/mtdowling"
+                },
+                {
+                    "name": "George Mponos",
+                    "email": "gmponos@gmail.com",
+                    "homepage": "https://github.com/gmponos"
+                },
+                {
+                    "name": "Tobias Nyholm",
+                    "email": "tobias.nyholm@gmail.com",
+                    "homepage": "https://github.com/Nyholm"
+                }
+            ],
+            "description": "A polyfill class for uri_template of PHP",
+            "keywords": [
+                "guzzlehttp",
+                "uri-template"
+            ],
+            "support": {
+                "issues": "https://github.com/guzzle/uri-template/issues",
+                "source": "https://github.com/guzzle/uri-template/tree/v1.0.5"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/GrahamCampbell",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/Nyholm",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/uri-template",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2025-08-22T14:27:06+00:00"
+        },
+        {
+            "name": "intervention/gif",
+            "version": "5.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/Intervention/gif.git",
+                "reference": "d856f59205aec768059d837148d755c079cdb94a"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/Intervention/gif/zipball/d856f59205aec768059d837148d755c079cdb94a",
+                "reference": "d856f59205aec768059d837148d755c079cdb94a",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^8.3"
+            },
+            "require-dev": {
+                "phpstan/phpstan": "^2.1",
+                "phpunit/phpunit": "^12.0",
+                "slevomat/coding-standard": "~8.0",
+                "squizlabs/php_codesniffer": "^4"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Intervention\\Gif\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Oliver Vogel",
+                    "email": "oliver@intervention.io",
+                    "homepage": "https://intervention.io/"
+                }
+            ],
+            "description": "PHP GIF Encoder/Decoder",
+            "homepage": "https://github.com/intervention/gif",
+            "keywords": [
+                "animation",
+                "gd",
+                "gif",
+                "image"
+            ],
+            "support": {
+                "issues": "https://github.com/Intervention/gif/issues",
+                "source": "https://github.com/Intervention/gif/tree/5.0.0"
+            },
+            "funding": [
+                {
+                    "url": "https://paypal.me/interventionio",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/Intervention",
+                    "type": "github"
+                },
+                {
+                    "url": "https://ko-fi.com/interventionphp",
+                    "type": "ko_fi"
+                }
+            ],
+            "time": "2026-03-21T05:08:17+00:00"
+        },
+        {
+            "name": "intervention/image",
+            "version": "4.0.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/Intervention/image.git",
+                "reference": "76d4e5a48b78f7b48f84d90160e6973b1e800832"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/Intervention/image/zipball/76d4e5a48b78f7b48f84d90160e6973b1e800832",
+                "reference": "76d4e5a48b78f7b48f84d90160e6973b1e800832",
+                "shasum": ""
+            },
+            "require": {
+                "ext-mbstring": "*",
+                "intervention/gif": "^5",
+                "php": "^8.3"
+            },
+            "require-dev": {
+                "mockery/mockery": "^1.6",
+                "phpstan/phpstan": "^2.1",
+                "phpunit/phpunit": "^12.0",
+                "slevomat/coding-standard": "~8.0",
+                "squizlabs/php_codesniffer": "^4"
+            },
+            "suggest": {
+                "ext-exif": "Recommended to be able to read EXIF data properly."
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Intervention\\Image\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Oliver Vogel",
+                    "email": "oliver@intervention.io",
+                    "homepage": "https://intervention.io"
+                }
+            ],
+            "description": "PHP Image Processing",
+            "homepage": "https://image.intervention.io",
+            "keywords": [
+                "gd",
+                "image",
+                "imagick",
+                "resize",
+                "thumbnail",
+                "watermark"
+            ],
+            "support": {
+                "issues": "https://github.com/Intervention/image/issues",
+                "source": "https://github.com/Intervention/image/tree/4.0.3"
+            },
+            "funding": [
+                {
+                    "url": "https://paypal.me/interventionio",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/Intervention",
+                    "type": "github"
+                },
+                {
+                    "url": "https://ko-fi.com/interventionphp",
+                    "type": "ko_fi"
+                }
+            ],
+            "time": "2026-05-01T08:19:10+00:00"
+        },
+        {
+            "name": "jeroennoten/laravel-adminlte",
+            "version": "v3.16.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/jeroennoten/Laravel-AdminLTE.git",
+                "reference": "dc92e92a9c8ac7443a82c97310e347ad62d971e4"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/jeroennoten/Laravel-AdminLTE/zipball/dc92e92a9c8ac7443a82c97310e347ad62d971e4",
+                "reference": "dc92e92a9c8ac7443a82c97310e347ad62d971e4",
+                "shasum": ""
+            },
+            "require": {
+                "almasaeed2010/adminlte": "3.2.*",
+                "laravel/framework": ">=8.0",
+                "php": ">=7.3"
+            },
+            "require-dev": {
+                "orchestra/testbench": ">=6.0",
+                "phpunit/phpunit": ">=9.1"
+            },
+            "type": "library",
+            "extra": {
+                "laravel": {
+                    "providers": [
+                        "JeroenNoten\\LaravelAdminLte\\AdminLteServiceProvider"
+                    ]
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "JeroenNoten\\LaravelAdminLte\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Jeroen Noten",
+                    "email": "jeroennoten@me.com"
+                }
+            ],
+            "description": "Easy AdminLTE integration with Laravel",
+            "keywords": [
+                "AdminLTE",
+                "admin",
+                "administrator",
+                "laravel"
+            ],
+            "support": {
+                "issues": "https://github.com/jeroennoten/Laravel-AdminLTE/issues",
+                "source": "https://github.com/jeroennoten/Laravel-AdminLTE/tree/v3.16.0"
+            },
+            "time": "2026-04-30T07:28:25+00:00"
+        },
+        {
+            "name": "laravel/framework",
+            "version": "v13.8.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/laravel/framework.git",
+                "reference": "e7db333a025a1e93ebca7744953069d7719f4bcf"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/laravel/framework/zipball/e7db333a025a1e93ebca7744953069d7719f4bcf",
+                "reference": "e7db333a025a1e93ebca7744953069d7719f4bcf",
+                "shasum": ""
+            },
+            "require": {
+                "brick/math": "^0.14.2 || ^0.15 || ^0.16 || ^0.17",
+                "composer-runtime-api": "^2.2",
+                "doctrine/inflector": "^2.0.5",
+                "dragonmantank/cron-expression": "^3.4",
+                "egulias/email-validator": "^4.0",
+                "ext-ctype": "*",
+                "ext-filter": "*",
+                "ext-hash": "*",
+                "ext-mbstring": "*",
+                "ext-openssl": "*",
+                "ext-session": "*",
+                "ext-tokenizer": "*",
+                "fruitcake/php-cors": "^1.3",
+                "guzzlehttp/guzzle": "^7.8.2",
+                "guzzlehttp/promises": "^2.0.3",
+                "guzzlehttp/uri-template": "^1.0",
+                "laravel/prompts": "^0.3.0",
+                "laravel/serializable-closure": "^2.0.10",
+                "league/commonmark": "^2.8.1",
+                "league/flysystem": "^3.25.1",
+                "league/flysystem-local": "^3.25.1",
+                "league/uri": "^7.5.1",
+                "monolog/monolog": "^3.0",
+                "nesbot/carbon": "^3.8.4",
+                "nunomaduro/termwind": "^2.0",
+                "php": "^8.3",
+                "psr/container": "^1.1.1 || ^2.0.1",
+                "psr/log": "^1.0 || ^2.0 || ^3.0",
+                "psr/simple-cache": "^1.0 || ^2.0 || ^3.0",
+                "ramsey/uuid": "^4.7",
+                "symfony/console": "^7.4.0 || ^8.0.0",
+                "symfony/error-handler": "^7.4.0 || ^8.0.0",
+                "symfony/finder": "^7.4.0 || ^8.0.0",
+                "symfony/http-foundation": "^7.4.0 || ^8.0.0",
+                "symfony/http-kernel": "^7.4.0 || ^8.0.0",
+                "symfony/mailer": "^7.4.0 || ^8.0.0",
+                "symfony/mime": "^7.4.0 || ^8.0.0",
+                "symfony/polyfill-php84": "^1.36",
+                "symfony/polyfill-php85": "^1.36",
+                "symfony/polyfill-php86": "^1.36",
+                "symfony/process": "^7.4.5 || ^8.0.5",
+                "symfony/routing": "^7.4.0 || ^8.0.0",
+                "symfony/uid": "^7.4.0 || ^8.0.0",
+                "symfony/var-dumper": "^7.4.0 || ^8.0.0",
+                "tijsverkoyen/css-to-inline-styles": "^2.2.5",
+                "vlucas/phpdotenv": "^5.6.1",
+                "voku/portable-ascii": "^2.0.2"
+            },
+            "conflict": {
+                "tightenco/collect": "<5.5.33"
+            },
+            "provide": {
+                "psr/container-implementation": "1.1 || 2.0",
+                "psr/log-implementation": "1.0 || 2.0 || 3.0",
+                "psr/simple-cache-implementation": "1.0 || 2.0 || 3.0"
+            },
+            "replace": {
+                "illuminate/auth": "self.version",
+                "illuminate/broadcasting": "self.version",
+                "illuminate/bus": "self.version",
+                "illuminate/cache": "self.version",
+                "illuminate/collections": "self.version",
+                "illuminate/concurrency": "self.version",
+                "illuminate/conditionable": "self.version",
+                "illuminate/config": "self.version",
+                "illuminate/console": "self.version",
+                "illuminate/container": "self.version",
+                "illuminate/contracts": "self.version",
+                "illuminate/cookie": "self.version",
+                "illuminate/database": "self.version",
+                "illuminate/encryption": "self.version",
+                "illuminate/events": "self.version",
+                "illuminate/filesystem": "self.version",
+                "illuminate/hashing": "self.version",
+                "illuminate/http": "self.version",
+                "illuminate/json-schema": "self.version",
+                "illuminate/log": "self.version",
+                "illuminate/macroable": "self.version",
+                "illuminate/mail": "self.version",
+                "illuminate/notifications": "self.version",
+                "illuminate/pagination": "self.version",
+                "illuminate/pipeline": "self.version",
+                "illuminate/process": "self.version",
+                "illuminate/queue": "self.version",
+                "illuminate/redis": "self.version",
+                "illuminate/reflection": "self.version",
+                "illuminate/routing": "self.version",
+                "illuminate/session": "self.version",
+                "illuminate/support": "self.version",
+                "illuminate/testing": "self.version",
+                "illuminate/translation": "self.version",
+                "illuminate/validation": "self.version",
+                "illuminate/view": "self.version",
+                "spatie/once": "*"
+            },
+            "require-dev": {
+                "ably/ably-php": "^1.0",
+                "aws/aws-sdk-php": "^3.322.9",
+                "ext-gmp": "*",
+                "fakerphp/faker": "^1.24",
+                "guzzlehttp/psr7": "^2.4",
+                "laravel/pint": "^1.18",
+                "league/flysystem-aws-s3-v3": "^3.25.1",
+                "league/flysystem-ftp": "^3.25.1",
+                "league/flysystem-path-prefixing": "^3.25.1",
+                "league/flysystem-read-only": "^3.25.1",
+                "league/flysystem-sftp-v3": "^3.25.1",
+                "mockery/mockery": "^1.6.10",
+                "opis/json-schema": "^2.4.1",
+                "orchestra/testbench-core": "^11.0.0",
+                "pda/pheanstalk": "^7.0.0 || ^8.0.0",
+                "php-http/discovery": "^1.15",
+                "phpstan/phpstan": "^2.0",
+                "phpunit/phpunit": "^11.5.50 || ^12.5.8 || ^13.0.3",
+                "predis/predis": "^2.3 || ^3.0",
+                "rector/rector": "^2.3",
+                "resend/resend-php": "^1.0",
+                "symfony/cache": "^7.4.0 || ^8.0.0",
+                "symfony/http-client": "^7.4.0 || ^8.0.0",
+                "symfony/psr-http-message-bridge": "^7.4.0 || ^8.0.0",
+                "symfony/translation": "^7.4.0 || ^8.0.0"
+            },
+            "suggest": {
+                "ably/ably-php": "Required to use the Ably broadcast driver (^1.0).",
+                "aws/aws-sdk-php": "Required to use the SQS queue driver, DynamoDb failed job storage, and SES mail driver (^3.322.9).",
+                "brianium/paratest": "Required to run tests in parallel (^7.0 || ^8.0).",
+                "ext-apcu": "Required to use the APC cache driver.",
+                "ext-fileinfo": "Required to use the Filesystem class.",
+                "ext-ftp": "Required to use the Flysystem FTP driver.",
+                "ext-gd": "Required to use Illuminate\\Http\\Testing\\FileFactory::image().",
+                "ext-memcached": "Required to use the memcache cache driver.",
+                "ext-pcntl": "Required to use all features of the queue worker and console signal trapping.",
+                "ext-pdo": "Required to use all database features.",
+                "ext-posix": "Required to use all features of the queue worker.",
+                "ext-redis": "Required to use the Redis cache and queue drivers (^4.0 || ^5.0 || ^6.0).",
+                "fakerphp/faker": "Required to generate fake data using the fake() helper (^1.23).",
+                "filp/whoops": "Required for friendly error pages in development (^2.14.3).",
+                "laravel/tinker": "Required to use the tinker console command (^2.0).",
+                "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^3.25.1).",
+                "league/flysystem-ftp": "Required to use the Flysystem FTP driver (^3.25.1).",
+                "league/flysystem-path-prefixing": "Required to use the scoped driver (^3.25.1).",
+                "league/flysystem-read-only": "Required to use read-only disks (^3.25.1)",
+                "league/flysystem-sftp-v3": "Required to use the Flysystem SFTP driver (^3.25.1).",
+                "mockery/mockery": "Required to use mocking (^1.6).",
+                "pda/pheanstalk": "Required to use the beanstalk queue driver (^7.0 || ^8.0).",
+                "php-http/discovery": "Required to use PSR-7 bridging features (^1.15).",
+                "phpunit/phpunit": "Required to use assertions and run tests (^11.5.50 || ^12.5.8 || ^13.0.3).",
+                "predis/predis": "Required to use the predis connector (^2.3 || ^3.0).",
+                "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).",
+                "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^6.0 || ^7.0).",
+                "resend/resend-php": "Required to enable support for the Resend mail transport (^0.10.0 || ^1.0).",
+                "spatie/fork": "Required to use the 'fork' concurrency driver (^1.2).",
+                "symfony/cache": "Required to PSR-6 cache bridge (^7.4 || ^8.0).",
+                "symfony/filesystem": "Required to enable support for relative symbolic links (^7.4 || ^8.0).",
+                "symfony/http-client": "Required to enable support for the Symfony API mail transports (^7.4 || ^8.0).",
+                "symfony/mailgun-mailer": "Required to enable support for the Mailgun mail transport (^7.4 || ^8.0).",
+                "symfony/postmark-mailer": "Required to enable support for the Postmark mail transport (^7.4 || ^8.0).",
+                "symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^7.4 || ^8.0)."
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "13.0.x-dev"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "src/Illuminate/Collections/functions.php",
+                    "src/Illuminate/Collections/helpers.php",
+                    "src/Illuminate/Events/functions.php",
+                    "src/Illuminate/Filesystem/functions.php",
+                    "src/Illuminate/Foundation/helpers.php",
+                    "src/Illuminate/Log/functions.php",
+                    "src/Illuminate/Reflection/helpers.php",
+                    "src/Illuminate/Support/functions.php",
+                    "src/Illuminate/Support/helpers.php"
+                ],
+                "psr-4": {
+                    "Illuminate\\": "src/Illuminate/",
+                    "Illuminate\\Support\\": [
+                        "src/Illuminate/Macroable/",
+                        "src/Illuminate/Collections/",
+                        "src/Illuminate/Conditionable/",
+                        "src/Illuminate/Reflection/"
+                    ]
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Taylor Otwell",
+                    "email": "taylor@laravel.com"
+                }
+            ],
+            "description": "The Laravel Framework.",
+            "homepage": "https://laravel.com",
+            "keywords": [
+                "framework",
+                "laravel"
+            ],
+            "support": {
+                "issues": "https://github.com/laravel/framework/issues",
+                "source": "https://github.com/laravel/framework"
+            },
+            "time": "2026-05-05T21:01:14+00:00"
+        },
+        {
+            "name": "laravel/prompts",
+            "version": "v0.3.17",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/laravel/prompts.git",
+                "reference": "6a82ac19a28b916ae0885828795dbd4c59d9a818"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/laravel/prompts/zipball/6a82ac19a28b916ae0885828795dbd4c59d9a818",
+                "reference": "6a82ac19a28b916ae0885828795dbd4c59d9a818",
+                "shasum": ""
+            },
+            "require": {
+                "composer-runtime-api": "^2.2",
+                "ext-mbstring": "*",
+                "php": "^8.1",
+                "symfony/console": "^6.2|^7.0|^8.0"
+            },
+            "conflict": {
+                "illuminate/console": ">=10.17.0 <10.25.0",
+                "laravel/framework": ">=10.17.0 <10.25.0"
+            },
+            "require-dev": {
+                "illuminate/collections": "^10.0|^11.0|^12.0|^13.0",
+                "mockery/mockery": "^1.5",
+                "pestphp/pest": "^2.3|^3.4|^4.0",
+                "phpstan/phpstan": "^1.12.28",
+                "phpstan/phpstan-mockery": "^1.1.3"
+            },
+            "suggest": {
+                "ext-pcntl": "Required for the spinner to be animated."
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "0.3.x-dev"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "src/helpers.php"
+                ],
+                "psr-4": {
+                    "Laravel\\Prompts\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "Add beautiful and user-friendly forms to your command-line applications.",
+            "support": {
+                "issues": "https://github.com/laravel/prompts/issues",
+                "source": "https://github.com/laravel/prompts/tree/v0.3.17"
+            },
+            "time": "2026-04-20T16:07:33+00:00"
+        },
+        {
+            "name": "laravel/serializable-closure",
+            "version": "v2.0.13",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/laravel/serializable-closure.git",
+                "reference": "b566ee0dd251f3c4078bed003a7ce015f5ea6dce"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/b566ee0dd251f3c4078bed003a7ce015f5ea6dce",
+                "reference": "b566ee0dd251f3c4078bed003a7ce015f5ea6dce",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^8.1"
+            },
+            "require-dev": {
+                "illuminate/support": "^10.0|^11.0|^12.0|^13.0",
+                "nesbot/carbon": "^2.67|^3.0",
+                "pestphp/pest": "^2.36|^3.0|^4.0",
+                "phpstan/phpstan": "^2.0",
+                "symfony/var-dumper": "^6.2.0|^7.0.0|^8.0.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Laravel\\SerializableClosure\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Taylor Otwell",
+                    "email": "taylor@laravel.com"
+                },
+                {
+                    "name": "Nuno Maduro",
+                    "email": "nuno@laravel.com"
+                }
+            ],
+            "description": "Laravel Serializable Closure provides an easy and secure way to serialize closures in PHP.",
+            "keywords": [
+                "closure",
+                "laravel",
+                "serializable"
+            ],
+            "support": {
+                "issues": "https://github.com/laravel/serializable-closure/issues",
+                "source": "https://github.com/laravel/serializable-closure"
+            },
+            "time": "2026-04-16T14:03:50+00:00"
+        },
+        {
+            "name": "laravel/tinker",
+            "version": "v3.0.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/laravel/tinker.git",
+                "reference": "4faba77764bd33411735936acdf30446d058c78b"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/laravel/tinker/zipball/4faba77764bd33411735936acdf30446d058c78b",
+                "reference": "4faba77764bd33411735936acdf30446d058c78b",
+                "shasum": ""
+            },
+            "require": {
+                "illuminate/console": "^8.0|^9.0|^10.0|^11.0|^12.0|^13.0",
+                "illuminate/contracts": "^8.0|^9.0|^10.0|^11.0|^12.0|^13.0",
+                "illuminate/support": "^8.0|^9.0|^10.0|^11.0|^12.0|^13.0",
+                "php": "^8.1",
+                "psy/psysh": "^0.12.0",
+                "symfony/var-dumper": "^5.4|^6.0|^7.0|^8.0"
+            },
+            "require-dev": {
+                "mockery/mockery": "~1.3.3|^1.4.2",
+                "phpstan/phpstan": "^1.10",
+                "phpunit/phpunit": "^10.5|^11.5"
+            },
+            "suggest": {
+                "illuminate/database": "The Illuminate Database package (^8.0|^9.0|^10.0|^11.0|^12.0|^13.0)."
+            },
+            "type": "library",
+            "extra": {
+                "laravel": {
+                    "providers": [
+                        "Laravel\\Tinker\\TinkerServiceProvider"
+                    ]
+                },
+                "branch-alias": {
+                    "dev-master": "3.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Laravel\\Tinker\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Taylor Otwell",
+                    "email": "taylor@laravel.com"
+                }
+            ],
+            "description": "Powerful REPL for the Laravel framework.",
+            "keywords": [
+                "REPL",
+                "Tinker",
+                "laravel",
+                "psysh"
+            ],
+            "support": {
+                "issues": "https://github.com/laravel/tinker/issues",
+                "source": "https://github.com/laravel/tinker/tree/v3.0.2"
+            },
+            "time": "2026-03-17T14:54:13+00:00"
+        },
+        {
+            "name": "league/commonmark",
+            "version": "2.8.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/thephpleague/commonmark.git",
+                "reference": "59fb075d2101740c337c7216e3f32b36c204218b"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/59fb075d2101740c337c7216e3f32b36c204218b",
+                "reference": "59fb075d2101740c337c7216e3f32b36c204218b",
+                "shasum": ""
+            },
+            "require": {
+                "ext-mbstring": "*",
+                "league/config": "^1.1.1",
+                "php": "^7.4 || ^8.0",
+                "psr/event-dispatcher": "^1.0",
+                "symfony/deprecation-contracts": "^2.1 || ^3.0",
+                "symfony/polyfill-php80": "^1.16"
+            },
+            "require-dev": {
+                "cebe/markdown": "^1.0",
+                "commonmark/cmark": "0.31.1",
+                "commonmark/commonmark.js": "0.31.1",
+                "composer/package-versions-deprecated": "^1.8",
+                "embed/embed": "^4.4",
+                "erusev/parsedown": "^1.0",
+                "ext-json": "*",
+                "github/gfm": "0.29.0",
+                "michelf/php-markdown": "^1.4 || ^2.0",
+                "nyholm/psr7": "^1.5",
+                "phpstan/phpstan": "^1.8.2",
+                "phpunit/phpunit": "^9.5.21 || ^10.5.9 || ^11.0.0",
+                "scrutinizer/ocular": "^1.8.1",
+                "symfony/finder": "^5.3 | ^6.0 | ^7.0 || ^8.0",
+                "symfony/process": "^5.4 | ^6.0 | ^7.0 || ^8.0",
+                "symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0 | ^7.0 || ^8.0",
+                "unleashedtech/php-coding-standard": "^3.1.1",
+                "vimeo/psalm": "^4.24.0 || ^5.0.0 || ^6.0.0"
+            },
+            "suggest": {
+                "symfony/yaml": "v2.3+ required if using the Front Matter extension"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "2.9-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "League\\CommonMark\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Colin O'Dell",
+                    "email": "colinodell@gmail.com",
+                    "homepage": "https://www.colinodell.com",
+                    "role": "Lead Developer"
+                }
+            ],
+            "description": "Highly-extensible PHP Markdown parser which fully supports the CommonMark spec and GitHub-Flavored Markdown (GFM)",
+            "homepage": "https://commonmark.thephpleague.com",
+            "keywords": [
+                "commonmark",
+                "flavored",
+                "gfm",
+                "github",
+                "github-flavored",
+                "markdown",
+                "md",
+                "parser"
+            ],
+            "support": {
+                "docs": "https://commonmark.thephpleague.com/",
+                "forum": "https://github.com/thephpleague/commonmark/discussions",
+                "issues": "https://github.com/thephpleague/commonmark/issues",
+                "rss": "https://github.com/thephpleague/commonmark/releases.atom",
+                "source": "https://github.com/thephpleague/commonmark"
+            },
+            "funding": [
+                {
+                    "url": "https://www.colinodell.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://www.paypal.me/colinpodell/10.00",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/colinodell",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/league/commonmark",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2026-03-19T13:16:38+00:00"
+        },
+        {
+            "name": "league/config",
+            "version": "v1.2.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/thephpleague/config.git",
+                "reference": "754b3604fb2984c71f4af4a9cbe7b57f346ec1f3"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/thephpleague/config/zipball/754b3604fb2984c71f4af4a9cbe7b57f346ec1f3",
+                "reference": "754b3604fb2984c71f4af4a9cbe7b57f346ec1f3",
+                "shasum": ""
+            },
+            "require": {
+                "dflydev/dot-access-data": "^3.0.1",
+                "nette/schema": "^1.2",
+                "php": "^7.4 || ^8.0"
+            },
+            "require-dev": {
+                "phpstan/phpstan": "^1.8.2",
+                "phpunit/phpunit": "^9.5.5",
+                "scrutinizer/ocular": "^1.8.1",
+                "unleashedtech/php-coding-standard": "^3.1",
+                "vimeo/psalm": "^4.7.3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "1.2-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "League\\Config\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Colin O'Dell",
+                    "email": "colinodell@gmail.com",
+                    "homepage": "https://www.colinodell.com",
+                    "role": "Lead Developer"
+                }
+            ],
+            "description": "Define configuration arrays with strict schemas and access values with dot notation",
+            "homepage": "https://config.thephpleague.com",
+            "keywords": [
+                "array",
+                "config",
+                "configuration",
+                "dot",
+                "dot-access",
+                "nested",
+                "schema"
+            ],
+            "support": {
+                "docs": "https://config.thephpleague.com/",
+                "issues": "https://github.com/thephpleague/config/issues",
+                "rss": "https://github.com/thephpleague/config/releases.atom",
+                "source": "https://github.com/thephpleague/config"
+            },
+            "funding": [
+                {
+                    "url": "https://www.colinodell.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://www.paypal.me/colinpodell/10.00",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/colinodell",
+                    "type": "github"
+                }
+            ],
+            "time": "2022-12-11T20:36:23+00:00"
+        },
+        {
+            "name": "league/flysystem",
+            "version": "3.33.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/thephpleague/flysystem.git",
+                "reference": "570b8871e0ce693764434b29154c54b434905350"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/570b8871e0ce693764434b29154c54b434905350",
+                "reference": "570b8871e0ce693764434b29154c54b434905350",
+                "shasum": ""
+            },
+            "require": {
+                "league/flysystem-local": "^3.0.0",
+                "league/mime-type-detection": "^1.0.0",
+                "php": "^8.0.2"
+            },
+            "conflict": {
+                "async-aws/core": "<1.19.0",
+                "async-aws/s3": "<1.14.0",
+                "aws/aws-sdk-php": "3.209.31 || 3.210.0",
+                "guzzlehttp/guzzle": "<7.0",
+                "guzzlehttp/ringphp": "<1.1.1",
+                "phpseclib/phpseclib": "3.0.15",
+                "symfony/http-client": "<5.2"
+            },
+            "require-dev": {
+                "async-aws/s3": "^1.5 || ^2.0",
+                "async-aws/simple-s3": "^1.1 || ^2.0",
+                "aws/aws-sdk-php": "^3.295.10",
+                "composer/semver": "^3.0",
+                "ext-fileinfo": "*",
+                "ext-ftp": "*",
+                "ext-mongodb": "^1.3|^2",
+                "ext-zip": "*",
+                "friendsofphp/php-cs-fixer": "^3.5",
+                "google/cloud-storage": "^1.23",
+                "guzzlehttp/psr7": "^2.6",
+                "microsoft/azure-storage-blob": "^1.1",
+                "mongodb/mongodb": "^1.2|^2",
+                "phpseclib/phpseclib": "^3.0.36",
+                "phpstan/phpstan": "^1.10",
+                "phpunit/phpunit": "^9.5.11|^10.0",
+                "sabre/dav": "^4.6.0"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "League\\Flysystem\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Frank de Jonge",
+                    "email": "info@frankdejonge.nl"
+                }
+            ],
+            "description": "File storage abstraction for PHP",
+            "keywords": [
+                "WebDAV",
+                "aws",
+                "cloud",
+                "file",
+                "files",
+                "filesystem",
+                "filesystems",
+                "ftp",
+                "s3",
+                "sftp",
+                "storage"
+            ],
+            "support": {
+                "issues": "https://github.com/thephpleague/flysystem/issues",
+                "source": "https://github.com/thephpleague/flysystem/tree/3.33.0"
+            },
+            "time": "2026-03-25T07:59:30+00:00"
+        },
+        {
+            "name": "league/flysystem-local",
+            "version": "3.31.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/thephpleague/flysystem-local.git",
+                "reference": "2f669db18a4c20c755c2bb7d3a7b0b2340488079"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/2f669db18a4c20c755c2bb7d3a7b0b2340488079",
+                "reference": "2f669db18a4c20c755c2bb7d3a7b0b2340488079",
+                "shasum": ""
+            },
+            "require": {
+                "ext-fileinfo": "*",
+                "league/flysystem": "^3.0.0",
+                "league/mime-type-detection": "^1.0.0",
+                "php": "^8.0.2"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "League\\Flysystem\\Local\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Frank de Jonge",
+                    "email": "info@frankdejonge.nl"
+                }
+            ],
+            "description": "Local filesystem adapter for Flysystem.",
+            "keywords": [
+                "Flysystem",
+                "file",
+                "files",
+                "filesystem",
+                "local"
+            ],
+            "support": {
+                "source": "https://github.com/thephpleague/flysystem-local/tree/3.31.0"
+            },
+            "time": "2026-01-23T15:30:45+00:00"
+        },
+        {
+            "name": "league/mime-type-detection",
+            "version": "1.16.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/thephpleague/mime-type-detection.git",
+                "reference": "2d6702ff215bf922936ccc1ad31007edc76451b9"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/2d6702ff215bf922936ccc1ad31007edc76451b9",
+                "reference": "2d6702ff215bf922936ccc1ad31007edc76451b9",
+                "shasum": ""
+            },
+            "require": {
+                "ext-fileinfo": "*",
+                "php": "^7.4 || ^8.0"
+            },
+            "require-dev": {
+                "friendsofphp/php-cs-fixer": "^3.2",
+                "phpstan/phpstan": "^0.12.68",
+                "phpunit/phpunit": "^8.5.8 || ^9.3 || ^10.0"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "League\\MimeTypeDetection\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Frank de Jonge",
+                    "email": "info@frankdejonge.nl"
+                }
+            ],
+            "description": "Mime-type detection for Flysystem",
+            "support": {
+                "issues": "https://github.com/thephpleague/mime-type-detection/issues",
+                "source": "https://github.com/thephpleague/mime-type-detection/tree/1.16.0"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/frankdejonge",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/league/flysystem",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2024-09-21T08:32:55+00:00"
+        },
+        {
+            "name": "league/uri",
+            "version": "7.8.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/thephpleague/uri.git",
+                "reference": "08cf38e3924d4f56238125547b5720496fac8fd4"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/thephpleague/uri/zipball/08cf38e3924d4f56238125547b5720496fac8fd4",
+                "reference": "08cf38e3924d4f56238125547b5720496fac8fd4",
+                "shasum": ""
+            },
+            "require": {
+                "league/uri-interfaces": "^7.8.1",
+                "php": "^8.1",
+                "psr/http-factory": "^1"
+            },
+            "conflict": {
+                "league/uri-schemes": "^1.0"
+            },
+            "suggest": {
+                "ext-bcmath": "to improve IPV4 host parsing",
+                "ext-dom": "to convert the URI into an HTML anchor tag",
+                "ext-fileinfo": "to create Data URI from file contennts",
+                "ext-gmp": "to improve IPV4 host parsing",
+                "ext-intl": "to handle IDN host with the best performance",
+                "ext-uri": "to use the PHP native URI class",
+                "jeremykendall/php-domain-parser": "to further parse the URI host and resolve its Public Suffix and Top Level Domain",
+                "league/uri-components": "to provide additional tools to manipulate URI objects components",
+                "league/uri-polyfill": "to backport the PHP URI extension for older versions of PHP",
+                "php-64bit": "to improve IPV4 host parsing",
+                "rowbot/url": "to handle URLs using the WHATWG URL Living Standard specification",
+                "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "7.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "League\\Uri\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Ignace Nyamagana Butera",
+                    "email": "nyamsprod@gmail.com",
+                    "homepage": "https://nyamsprod.com"
+                }
+            ],
+            "description": "URI manipulation library",
+            "homepage": "https://uri.thephpleague.com",
+            "keywords": [
+                "URN",
+                "data-uri",
+                "file-uri",
+                "ftp",
+                "hostname",
+                "http",
+                "https",
+                "middleware",
+                "parse_str",
+                "parse_url",
+                "psr-7",
+                "query-string",
+                "querystring",
+                "rfc2141",
+                "rfc3986",
+                "rfc3987",
+                "rfc6570",
+                "rfc8141",
+                "uri",
+                "uri-template",
+                "url",
+                "ws"
+            ],
+            "support": {
+                "docs": "https://uri.thephpleague.com",
+                "forum": "https://thephpleague.slack.com",
+                "issues": "https://github.com/thephpleague/uri-src/issues",
+                "source": "https://github.com/thephpleague/uri/tree/7.8.1"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sponsors/nyamsprod",
+                    "type": "github"
+                }
+            ],
+            "time": "2026-03-15T20:22:25+00:00"
+        },
+        {
+            "name": "league/uri-interfaces",
+            "version": "7.8.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/thephpleague/uri-interfaces.git",
+                "reference": "85d5c77c5d6d3af6c54db4a78246364908f3c928"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/85d5c77c5d6d3af6c54db4a78246364908f3c928",
+                "reference": "85d5c77c5d6d3af6c54db4a78246364908f3c928",
+                "shasum": ""
+            },
+            "require": {
+                "ext-filter": "*",
+                "php": "^8.1",
+                "psr/http-message": "^1.1 || ^2.0"
+            },
+            "suggest": {
+                "ext-bcmath": "to improve IPV4 host parsing",
+                "ext-gmp": "to improve IPV4 host parsing",
+                "ext-intl": "to handle IDN host with the best performance",
+                "php-64bit": "to improve IPV4 host parsing",
+                "rowbot/url": "to handle URLs using the WHATWG URL Living Standard specification",
+                "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "7.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "League\\Uri\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Ignace Nyamagana Butera",
+                    "email": "nyamsprod@gmail.com",
+                    "homepage": "https://nyamsprod.com"
+                }
+            ],
+            "description": "Common tools for parsing and resolving RFC3987/RFC3986 URI",
+            "homepage": "https://uri.thephpleague.com",
+            "keywords": [
+                "data-uri",
+                "file-uri",
+                "ftp",
+                "hostname",
+                "http",
+                "https",
+                "parse_str",
+                "parse_url",
+                "psr-7",
+                "query-string",
+                "querystring",
+                "rfc3986",
+                "rfc3987",
+                "rfc6570",
+                "uri",
+                "url",
+                "ws"
+            ],
+            "support": {
+                "docs": "https://uri.thephpleague.com",
+                "forum": "https://thephpleague.slack.com",
+                "issues": "https://github.com/thephpleague/uri-src/issues",
+                "source": "https://github.com/thephpleague/uri-interfaces/tree/7.8.1"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sponsors/nyamsprod",
+                    "type": "github"
+                }
+            ],
+            "time": "2026-03-08T20:05:35+00:00"
+        },
+        {
+            "name": "monolog/monolog",
+            "version": "3.10.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/Seldaek/monolog.git",
+                "reference": "b321dd6749f0bf7189444158a3ce785cc16d69b0"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/Seldaek/monolog/zipball/b321dd6749f0bf7189444158a3ce785cc16d69b0",
+                "reference": "b321dd6749f0bf7189444158a3ce785cc16d69b0",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.1",
+                "psr/log": "^2.0 || ^3.0"
+            },
+            "provide": {
+                "psr/log-implementation": "3.0.0"
+            },
+            "require-dev": {
+                "aws/aws-sdk-php": "^3.0",
+                "doctrine/couchdb": "~1.0@dev",
+                "elasticsearch/elasticsearch": "^7 || ^8",
+                "ext-json": "*",
+                "graylog2/gelf-php": "^1.4.2 || ^2.0",
+                "guzzlehttp/guzzle": "^7.4.5",
+                "guzzlehttp/psr7": "^2.2",
+                "mongodb/mongodb": "^1.8 || ^2.0",
+                "php-amqplib/php-amqplib": "~2.4 || ^3",
+                "php-console/php-console": "^3.1.8",
+                "phpstan/phpstan": "^2",
+                "phpstan/phpstan-deprecation-rules": "^2",
+                "phpstan/phpstan-strict-rules": "^2",
+                "phpunit/phpunit": "^10.5.17 || ^11.0.7",
+                "predis/predis": "^1.1 || ^2",
+                "rollbar/rollbar": "^4.0",
+                "ruflin/elastica": "^7 || ^8",
+                "symfony/mailer": "^5.4 || ^6",
+                "symfony/mime": "^5.4 || ^6"
+            },
+            "suggest": {
+                "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB",
+                "doctrine/couchdb": "Allow sending log messages to a CouchDB server",
+                "elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client",
+                "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)",
+                "ext-curl": "Required to send log messages using the IFTTTHandler, the LogglyHandler, the SendGridHandler, the SlackWebhookHandler or the TelegramBotHandler",
+                "ext-mbstring": "Allow to work properly with unicode symbols",
+                "ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)",
+                "ext-openssl": "Required to send log messages using SSL",
+                "ext-sockets": "Allow sending log messages to a Syslog server (via UDP driver)",
+                "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server",
+                "mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)",
+                "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib",
+                "rollbar/rollbar": "Allow sending log messages to Rollbar",
+                "ruflin/elastica": "Allow sending log messages to an Elastic Search server"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "3.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Monolog\\": "src/Monolog"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Jordi Boggiano",
+                    "email": "j.boggiano@seld.be",
+                    "homepage": "https://seld.be"
+                }
+            ],
+            "description": "Sends your logs to files, sockets, inboxes, databases and various web services",
+            "homepage": "https://github.com/Seldaek/monolog",
+            "keywords": [
+                "log",
+                "logging",
+                "psr-3"
+            ],
+            "support": {
+                "issues": "https://github.com/Seldaek/monolog/issues",
+                "source": "https://github.com/Seldaek/monolog/tree/3.10.0"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/Seldaek",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/monolog/monolog",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2026-01-02T08:56:05+00:00"
+        },
+        {
+            "name": "nesbot/carbon",
+            "version": "3.11.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/CarbonPHP/carbon.git",
+                "reference": "e890471a3494740f7d9326d72ce6a8c559ffee60"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/e890471a3494740f7d9326d72ce6a8c559ffee60",
+                "reference": "e890471a3494740f7d9326d72ce6a8c559ffee60",
+                "shasum": ""
+            },
+            "require": {
+                "carbonphp/carbon-doctrine-types": "<100.0",
+                "ext-json": "*",
+                "php": "^8.1",
+                "psr/clock": "^1.0",
+                "symfony/clock": "^6.3.12 || ^7.0 || ^8.0",
+                "symfony/polyfill-mbstring": "^1.0",
+                "symfony/translation": "^4.4.18 || ^5.2.1 || ^6.0 || ^7.0 || ^8.0"
+            },
+            "provide": {
+                "psr/clock-implementation": "1.0"
+            },
+            "require-dev": {
+                "doctrine/dbal": "^3.6.3 || ^4.0",
+                "doctrine/orm": "^2.15.2 || ^3.0",
+                "friendsofphp/php-cs-fixer": "^v3.87.1",
+                "kylekatarnls/multi-tester": "^2.5.3",
+                "phpmd/phpmd": "^2.15.0",
+                "phpstan/extension-installer": "^1.4.3",
+                "phpstan/phpstan": "^2.1.22",
+                "phpunit/phpunit": "^10.5.53",
+                "squizlabs/php_codesniffer": "^3.13.4 || ^4.0.0"
+            },
+            "bin": [
+                "bin/carbon"
+            ],
+            "type": "library",
+            "extra": {
+                "laravel": {
+                    "providers": [
+                        "Carbon\\Laravel\\ServiceProvider"
+                    ]
+                },
+                "phpstan": {
+                    "includes": [
+                        "extension.neon"
+                    ]
+                },
+                "branch-alias": {
+                    "dev-2.x": "2.x-dev",
+                    "dev-master": "3.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Carbon\\": "src/Carbon/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Brian Nesbitt",
+                    "email": "brian@nesbot.com",
+                    "homepage": "https://markido.com"
+                },
+                {
+                    "name": "kylekatarnls",
+                    "homepage": "https://github.com/kylekatarnls"
+                }
+            ],
+            "description": "An API extension for DateTime that supports 281 different languages.",
+            "homepage": "https://carbonphp.github.io/carbon/",
+            "keywords": [
+                "date",
+                "datetime",
+                "time"
+            ],
+            "support": {
+                "docs": "https://carbonphp.github.io/carbon/guide/getting-started/introduction.html",
+                "issues": "https://github.com/CarbonPHP/carbon/issues",
+                "source": "https://github.com/CarbonPHP/carbon"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sponsors/kylekatarnls",
+                    "type": "github"
+                },
+                {
+                    "url": "https://opencollective.com/Carbon#sponsor",
+                    "type": "opencollective"
+                },
+                {
+                    "url": "https://tidelift.com/subscription/pkg/packagist-nesbot-carbon?utm_source=packagist-nesbot-carbon&utm_medium=referral&utm_campaign=readme",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2026-04-07T09:57:54+00:00"
+        },
+        {
+            "name": "nette/schema",
+            "version": "v1.3.5",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/nette/schema.git",
+                "reference": "f0ab1a3cda782dbc5da270d28545236aa80c4002"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/nette/schema/zipball/f0ab1a3cda782dbc5da270d28545236aa80c4002",
+                "reference": "f0ab1a3cda782dbc5da270d28545236aa80c4002",
+                "shasum": ""
+            },
+            "require": {
+                "nette/utils": "^4.0",
+                "php": "8.1 - 8.5"
+            },
+            "require-dev": {
+                "nette/phpstan-rules": "^1.0",
+                "nette/tester": "^2.6",
+                "phpstan/extension-installer": "^1.4@stable",
+                "phpstan/phpstan": "^2.1.39@stable",
+                "tracy/tracy": "^2.8"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.3-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Nette\\": "src"
+                },
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause",
+                "GPL-2.0-only",
+                "GPL-3.0-only"
+            ],
+            "authors": [
+                {
+                    "name": "David Grudl",
+                    "homepage": "https://davidgrudl.com"
+                },
+                {
+                    "name": "Nette Community",
+                    "homepage": "https://nette.org/contributors"
+                }
+            ],
+            "description": "📐 Nette Schema: validating data structures against a given Schema.",
+            "homepage": "https://nette.org",
+            "keywords": [
+                "config",
+                "nette"
+            ],
+            "support": {
+                "issues": "https://github.com/nette/schema/issues",
+                "source": "https://github.com/nette/schema/tree/v1.3.5"
+            },
+            "time": "2026-02-23T03:47:12+00:00"
+        },
+        {
+            "name": "nette/utils",
+            "version": "v4.1.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/nette/utils.git",
+                "reference": "bb3ea637e3d131d72acc033cfc2746ee893349fe"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/nette/utils/zipball/bb3ea637e3d131d72acc033cfc2746ee893349fe",
+                "reference": "bb3ea637e3d131d72acc033cfc2746ee893349fe",
+                "shasum": ""
+            },
+            "require": {
+                "php": "8.2 - 8.5"
+            },
+            "conflict": {
+                "nette/finder": "<3",
+                "nette/schema": "<1.2.2"
+            },
+            "require-dev": {
+                "jetbrains/phpstorm-attributes": "^1.2",
+                "nette/phpstan-rules": "^1.0",
+                "nette/tester": "^2.5",
+                "phpstan/extension-installer": "^1.4@stable",
+                "phpstan/phpstan": "^2.1@stable",
+                "tracy/tracy": "^2.9"
+            },
+            "suggest": {
+                "ext-gd": "to use Image",
+                "ext-iconv": "to use Strings::webalize(), toAscii(), chr() and reverse()",
+                "ext-intl": "to use Strings::webalize(), toAscii(), normalize() and compare()",
+                "ext-json": "to use Nette\\Utils\\Json",
+                "ext-mbstring": "to use Strings::lower() etc...",
+                "ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "4.1-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Nette\\": "src"
+                },
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause",
+                "GPL-2.0-only",
+                "GPL-3.0-only"
+            ],
+            "authors": [
+                {
+                    "name": "David Grudl",
+                    "homepage": "https://davidgrudl.com"
+                },
+                {
+                    "name": "Nette Community",
+                    "homepage": "https://nette.org/contributors"
+                }
+            ],
+            "description": "🛠  Nette Utils: lightweight utilities for string & array manipulation, image handling, safe JSON encoding/decoding, validation, slug or strong password generating etc.",
+            "homepage": "https://nette.org",
+            "keywords": [
+                "array",
+                "core",
+                "datetime",
+                "images",
+                "json",
+                "nette",
+                "paginator",
+                "password",
+                "slugify",
+                "string",
+                "unicode",
+                "utf-8",
+                "utility",
+                "validation"
+            ],
+            "support": {
+                "issues": "https://github.com/nette/utils/issues",
+                "source": "https://github.com/nette/utils/tree/v4.1.3"
+            },
+            "time": "2026-02-13T03:05:33+00:00"
+        },
+        {
+            "name": "nikic/php-parser",
+            "version": "v5.7.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/nikic/PHP-Parser.git",
+                "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/dca41cd15c2ac9d055ad70dbfd011130757d1f82",
+                "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82",
+                "shasum": ""
+            },
+            "require": {
+                "ext-ctype": "*",
+                "ext-json": "*",
+                "ext-tokenizer": "*",
+                "php": ">=7.4"
+            },
+            "require-dev": {
+                "ircmaxell/php-yacc": "^0.0.7",
+                "phpunit/phpunit": "^9.0"
+            },
+            "bin": [
+                "bin/php-parse"
+            ],
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "5.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "PhpParser\\": "lib/PhpParser"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Nikita Popov"
+                }
+            ],
+            "description": "A PHP parser written in PHP",
+            "keywords": [
+                "parser",
+                "php"
+            ],
+            "support": {
+                "issues": "https://github.com/nikic/PHP-Parser/issues",
+                "source": "https://github.com/nikic/PHP-Parser/tree/v5.7.0"
+            },
+            "time": "2025-12-06T11:56:16+00:00"
+        },
+        {
+            "name": "nunomaduro/termwind",
+            "version": "v2.4.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/nunomaduro/termwind.git",
+                "reference": "712a31b768f5daea284c2169a7d227031001b9a8"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/712a31b768f5daea284c2169a7d227031001b9a8",
+                "reference": "712a31b768f5daea284c2169a7d227031001b9a8",
+                "shasum": ""
+            },
+            "require": {
+                "ext-mbstring": "*",
+                "php": "^8.2",
+                "symfony/console": "^7.4.4 || ^8.0.4"
+            },
+            "require-dev": {
+                "illuminate/console": "^11.47.0",
+                "laravel/pint": "^1.27.1",
+                "mockery/mockery": "^1.6.12",
+                "pestphp/pest": "^2.36.0 || ^3.8.4 || ^4.3.2",
+                "phpstan/phpstan": "^1.12.32",
+                "phpstan/phpstan-strict-rules": "^1.6.2",
+                "symfony/var-dumper": "^7.3.5 || ^8.0.4",
+                "thecodingmachine/phpstan-strict-rules": "^1.0.0"
+            },
+            "type": "library",
+            "extra": {
+                "laravel": {
+                    "providers": [
+                        "Termwind\\Laravel\\TermwindServiceProvider"
+                    ]
+                },
+                "branch-alias": {
+                    "dev-2.x": "2.x-dev"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "src/Functions.php"
+                ],
+                "psr-4": {
+                    "Termwind\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nuno Maduro",
+                    "email": "enunomaduro@gmail.com"
+                }
+            ],
+            "description": "It's like Tailwind CSS, but for the console.",
+            "keywords": [
+                "cli",
+                "console",
+                "css",
+                "package",
+                "php",
+                "style"
+            ],
+            "support": {
+                "issues": "https://github.com/nunomaduro/termwind/issues",
+                "source": "https://github.com/nunomaduro/termwind/tree/v2.4.0"
+            },
+            "funding": [
+                {
+                    "url": "https://www.paypal.com/paypalme/enunomaduro",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/nunomaduro",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/xiCO2k",
+                    "type": "github"
+                }
+            ],
+            "time": "2026-02-16T23:10:27+00:00"
+        },
+        {
+            "name": "phpoption/phpoption",
+            "version": "1.9.5",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/schmittjoh/php-option.git",
+                "reference": "75365b91986c2405cf5e1e012c5595cd487a98be"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/75365b91986c2405cf5e1e012c5595cd487a98be",
+                "reference": "75365b91986c2405cf5e1e012c5595cd487a98be",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.2.5 || ^8.0"
+            },
+            "require-dev": {
+                "bamarni/composer-bin-plugin": "^1.8.2",
+                "phpunit/phpunit": "^8.5.44 || ^9.6.25 || ^10.5.53 || ^11.5.34"
+            },
+            "type": "library",
+            "extra": {
+                "bamarni-bin": {
+                    "bin-links": true,
+                    "forward-command": false
+                },
+                "branch-alias": {
+                    "dev-master": "1.9-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "PhpOption\\": "src/PhpOption/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "Apache-2.0"
+            ],
+            "authors": [
+                {
+                    "name": "Johannes M. Schmitt",
+                    "email": "schmittjoh@gmail.com",
+                    "homepage": "https://github.com/schmittjoh"
+                },
+                {
+                    "name": "Graham Campbell",
+                    "email": "hello@gjcampbell.co.uk",
+                    "homepage": "https://github.com/GrahamCampbell"
+                }
+            ],
+            "description": "Option Type for PHP",
+            "keywords": [
+                "language",
+                "option",
+                "php",
+                "type"
+            ],
+            "support": {
+                "issues": "https://github.com/schmittjoh/php-option/issues",
+                "source": "https://github.com/schmittjoh/php-option/tree/1.9.5"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/GrahamCampbell",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2025-12-27T19:41:33+00:00"
+        },
+        {
+            "name": "psr/clock",
+            "version": "1.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/clock.git",
+                "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d",
+                "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.0 || ^8.0"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Clock\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "https://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interface for reading the clock.",
+            "homepage": "https://github.com/php-fig/clock",
+            "keywords": [
+                "clock",
+                "now",
+                "psr",
+                "psr-20",
+                "time"
+            ],
+            "support": {
+                "issues": "https://github.com/php-fig/clock/issues",
+                "source": "https://github.com/php-fig/clock/tree/1.0.0"
+            },
+            "time": "2022-11-25T14:36:26+00:00"
+        },
+        {
+            "name": "psr/container",
+            "version": "2.0.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/container.git",
+                "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963",
+                "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.4.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Container\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "https://www.php-fig.org/"
+                }
+            ],
+            "description": "Common Container Interface (PHP FIG PSR-11)",
+            "homepage": "https://github.com/php-fig/container",
+            "keywords": [
+                "PSR-11",
+                "container",
+                "container-interface",
+                "container-interop",
+                "psr"
+            ],
+            "support": {
+                "issues": "https://github.com/php-fig/container/issues",
+                "source": "https://github.com/php-fig/container/tree/2.0.2"
+            },
+            "time": "2021-11-05T16:47:00+00:00"
+        },
+        {
+            "name": "psr/event-dispatcher",
+            "version": "1.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/event-dispatcher.git",
+                "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0",
+                "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.2.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\EventDispatcher\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "http://www.php-fig.org/"
+                }
+            ],
+            "description": "Standard interfaces for event handling.",
+            "keywords": [
+                "events",
+                "psr",
+                "psr-14"
+            ],
+            "support": {
+                "issues": "https://github.com/php-fig/event-dispatcher/issues",
+                "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0"
+            },
+            "time": "2019-01-08T18:20:26+00:00"
+        },
+        {
+            "name": "psr/http-client",
+            "version": "1.0.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/http-client.git",
+                "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90",
+                "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.0 || ^8.0",
+                "psr/http-message": "^1.0 || ^2.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Http\\Client\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "https://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interface for HTTP clients",
+            "homepage": "https://github.com/php-fig/http-client",
+            "keywords": [
+                "http",
+                "http-client",
+                "psr",
+                "psr-18"
+            ],
+            "support": {
+                "source": "https://github.com/php-fig/http-client"
+            },
+            "time": "2023-09-23T14:17:50+00:00"
+        },
+        {
+            "name": "psr/http-factory",
+            "version": "1.1.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/http-factory.git",
+                "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
+                "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.1",
+                "psr/http-message": "^1.0 || ^2.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Http\\Message\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "https://www.php-fig.org/"
+                }
+            ],
+            "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories",
+            "keywords": [
+                "factory",
+                "http",
+                "message",
+                "psr",
+                "psr-17",
+                "psr-7",
+                "request",
+                "response"
+            ],
+            "support": {
+                "source": "https://github.com/php-fig/http-factory"
+            },
+            "time": "2024-04-15T12:06:14+00:00"
+        },
+        {
+            "name": "psr/http-message",
+            "version": "2.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/http-message.git",
+                "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71",
+                "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.2 || ^8.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Http\\Message\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "https://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interface for HTTP messages",
+            "homepage": "https://github.com/php-fig/http-message",
+            "keywords": [
+                "http",
+                "http-message",
+                "psr",
+                "psr-7",
+                "request",
+                "response"
+            ],
+            "support": {
+                "source": "https://github.com/php-fig/http-message/tree/2.0"
+            },
+            "time": "2023-04-04T09:54:51+00:00"
+        },
+        {
+            "name": "psr/log",
+            "version": "3.0.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/log.git",
+                "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3",
+                "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.0.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Log\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "https://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interface for logging libraries",
+            "homepage": "https://github.com/php-fig/log",
+            "keywords": [
+                "log",
+                "psr",
+                "psr-3"
+            ],
+            "support": {
+                "source": "https://github.com/php-fig/log/tree/3.0.2"
+            },
+            "time": "2024-09-11T13:17:53+00:00"
+        },
+        {
+            "name": "psr/simple-cache",
+            "version": "3.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/simple-cache.git",
+                "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865",
+                "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.0.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\SimpleCache\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "https://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interfaces for simple caching",
+            "keywords": [
+                "cache",
+                "caching",
+                "psr",
+                "psr-16",
+                "simple-cache"
+            ],
+            "support": {
+                "source": "https://github.com/php-fig/simple-cache/tree/3.0.0"
+            },
+            "time": "2021-10-29T13:26:27+00:00"
+        },
+        {
+            "name": "psy/psysh",
+            "version": "v0.12.22",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/bobthecow/psysh.git",
+                "reference": "3be75d5b9244936dd4ac62ade2bfb004d13acf0f"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/bobthecow/psysh/zipball/3be75d5b9244936dd4ac62ade2bfb004d13acf0f",
+                "reference": "3be75d5b9244936dd4ac62ade2bfb004d13acf0f",
+                "shasum": ""
+            },
+            "require": {
+                "ext-json": "*",
+                "ext-tokenizer": "*",
+                "nikic/php-parser": "^5.0 || ^4.0",
+                "php": "^8.0 || ^7.4",
+                "symfony/console": "^8.0 || ^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4",
+                "symfony/var-dumper": "^8.0 || ^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4"
+            },
+            "conflict": {
+                "symfony/console": "4.4.37 || 5.3.14 || 5.3.15 || 5.4.3 || 5.4.4 || 6.0.3 || 6.0.4"
+            },
+            "require-dev": {
+                "bamarni/composer-bin-plugin": "^1.2",
+                "composer/class-map-generator": "^1.6"
+            },
+            "suggest": {
+                "composer/class-map-generator": "Improved tab completion performance with better class discovery.",
+                "ext-pcntl": "Enabling the PCNTL extension makes PsySH a lot happier :)",
+                "ext-posix": "If you have PCNTL, you'll want the POSIX extension as well."
+            },
+            "bin": [
+                "bin/psysh"
+            ],
+            "type": "library",
+            "extra": {
+                "bamarni-bin": {
+                    "bin-links": false,
+                    "forward-command": false
+                },
+                "branch-alias": {
+                    "dev-main": "0.12.x-dev"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "src/functions.php"
+                ],
+                "psr-4": {
+                    "Psy\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Justin Hileman",
+                    "email": "justin@justinhileman.info"
+                }
+            ],
+            "description": "An interactive shell for modern PHP.",
+            "homepage": "https://psysh.org",
+            "keywords": [
+                "REPL",
+                "console",
+                "interactive",
+                "shell"
+            ],
+            "support": {
+                "issues": "https://github.com/bobthecow/psysh/issues",
+                "source": "https://github.com/bobthecow/psysh/tree/v0.12.22"
+            },
+            "time": "2026-03-22T23:03:24+00:00"
+        },
+        {
+            "name": "ralouphie/getallheaders",
+            "version": "3.0.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/ralouphie/getallheaders.git",
+                "reference": "120b605dfeb996808c31b6477290a714d356e822"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822",
+                "reference": "120b605dfeb996808c31b6477290a714d356e822",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.6"
+            },
+            "require-dev": {
+                "php-coveralls/php-coveralls": "^2.1",
+                "phpunit/phpunit": "^5 || ^6.5"
+            },
+            "type": "library",
+            "autoload": {
+                "files": [
+                    "src/getallheaders.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Ralph Khattar",
+                    "email": "ralph.khattar@gmail.com"
+                }
+            ],
+            "description": "A polyfill for getallheaders.",
+            "support": {
+                "issues": "https://github.com/ralouphie/getallheaders/issues",
+                "source": "https://github.com/ralouphie/getallheaders/tree/develop"
+            },
+            "time": "2019-03-08T08:55:37+00:00"
+        },
+        {
+            "name": "ramsey/collection",
+            "version": "2.1.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/ramsey/collection.git",
+                "reference": "344572933ad0181accbf4ba763e85a0306a8c5e2"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/ramsey/collection/zipball/344572933ad0181accbf4ba763e85a0306a8c5e2",
+                "reference": "344572933ad0181accbf4ba763e85a0306a8c5e2",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^8.1"
+            },
+            "require-dev": {
+                "captainhook/plugin-composer": "^5.3",
+                "ergebnis/composer-normalize": "^2.45",
+                "fakerphp/faker": "^1.24",
+                "hamcrest/hamcrest-php": "^2.0",
+                "jangregor/phpstan-prophecy": "^2.1",
+                "mockery/mockery": "^1.6",
+                "php-parallel-lint/php-console-highlighter": "^1.0",
+                "php-parallel-lint/php-parallel-lint": "^1.4",
+                "phpspec/prophecy-phpunit": "^2.3",
+                "phpstan/extension-installer": "^1.4",
+                "phpstan/phpstan": "^2.1",
+                "phpstan/phpstan-mockery": "^2.0",
+                "phpstan/phpstan-phpunit": "^2.0",
+                "phpunit/phpunit": "^10.5",
+                "ramsey/coding-standard": "^2.3",
+                "ramsey/conventional-commits": "^1.6",
+                "roave/security-advisories": "dev-latest"
+            },
+            "type": "library",
+            "extra": {
+                "captainhook": {
+                    "force-install": true
+                },
+                "ramsey/conventional-commits": {
+                    "configFile": "conventional-commits.json"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Ramsey\\Collection\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Ben Ramsey",
+                    "email": "ben@benramsey.com",
+                    "homepage": "https://benramsey.com"
+                }
+            ],
+            "description": "A PHP library for representing and manipulating collections.",
+            "keywords": [
+                "array",
+                "collection",
+                "hash",
+                "map",
+                "queue",
+                "set"
+            ],
+            "support": {
+                "issues": "https://github.com/ramsey/collection/issues",
+                "source": "https://github.com/ramsey/collection/tree/2.1.1"
+            },
+            "time": "2025-03-22T05:38:12+00:00"
+        },
+        {
+            "name": "ramsey/uuid",
+            "version": "4.9.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/ramsey/uuid.git",
+                "reference": "8429c78ca35a09f27565311b98101e2826affde0"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/ramsey/uuid/zipball/8429c78ca35a09f27565311b98101e2826affde0",
+                "reference": "8429c78ca35a09f27565311b98101e2826affde0",
+                "shasum": ""
+            },
+            "require": {
+                "brick/math": "^0.8.16 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13 || ^0.14",
+                "php": "^8.0",
+                "ramsey/collection": "^1.2 || ^2.0"
+            },
+            "replace": {
+                "rhumsaa/uuid": "self.version"
+            },
+            "require-dev": {
+                "captainhook/captainhook": "^5.25",
+                "captainhook/plugin-composer": "^5.3",
+                "dealerdirect/phpcodesniffer-composer-installer": "^1.0",
+                "ergebnis/composer-normalize": "^2.47",
+                "mockery/mockery": "^1.6",
+                "paragonie/random-lib": "^2",
+                "php-mock/php-mock": "^2.6",
+                "php-mock/php-mock-mockery": "^1.5",
+                "php-parallel-lint/php-parallel-lint": "^1.4.0",
+                "phpbench/phpbench": "^1.2.14",
+                "phpstan/extension-installer": "^1.4",
+                "phpstan/phpstan": "^2.1",
+                "phpstan/phpstan-mockery": "^2.0",
+                "phpstan/phpstan-phpunit": "^2.0",
+                "phpunit/phpunit": "^9.6",
+                "slevomat/coding-standard": "^8.18",
+                "squizlabs/php_codesniffer": "^3.13"
+            },
+            "suggest": {
+                "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.",
+                "ext-gmp": "Enables faster math with arbitrary-precision integers using GMP.",
+                "ext-uuid": "Enables the use of PeclUuidTimeGenerator and PeclUuidRandomGenerator.",
+                "paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter",
+                "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type."
+            },
+            "type": "library",
+            "extra": {
+                "captainhook": {
+                    "force-install": true
+                }
+            },
+            "autoload": {
+                "files": [
+                    "src/functions.php"
+                ],
+                "psr-4": {
+                    "Ramsey\\Uuid\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "A PHP library for generating and working with universally unique identifiers (UUIDs).",
+            "keywords": [
+                "guid",
+                "identifier",
+                "uuid"
+            ],
+            "support": {
+                "issues": "https://github.com/ramsey/uuid/issues",
+                "source": "https://github.com/ramsey/uuid/tree/4.9.2"
+            },
+            "time": "2025-12-14T04:43:48+00:00"
+        },
+        {
+            "name": "symfony/clock",
+            "version": "v8.0.8",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/clock.git",
+                "reference": "b55a638b189a6faa875e0ccdb00908fb87af95b3"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/clock/zipball/b55a638b189a6faa875e0ccdb00908fb87af95b3",
+                "reference": "b55a638b189a6faa875e0ccdb00908fb87af95b3",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.4",
+                "psr/clock": "^1.0"
+            },
+            "provide": {
+                "psr/clock-implementation": "1.0"
+            },
+            "type": "library",
+            "autoload": {
+                "files": [
+                    "Resources/now.php"
+                ],
+                "psr-4": {
+                    "Symfony\\Component\\Clock\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Decouples applications from the system clock",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "clock",
+                "psr20",
+                "time"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/clock/tree/v8.0.8"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/nicolas-grekas",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2026-03-30T15:14:47+00:00"
+        },
+        {
+            "name": "symfony/console",
+            "version": "v8.0.9",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/console.git",
+                "reference": "7113778e2e91f4709cb3194a75dfa9c0d028d94d"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/console/zipball/7113778e2e91f4709cb3194a75dfa9c0d028d94d",
+                "reference": "7113778e2e91f4709cb3194a75dfa9c0d028d94d",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.4",
+                "symfony/polyfill-mbstring": "^1.0",
+                "symfony/service-contracts": "^2.5|^3",
+                "symfony/string": "^7.4|^8.0"
+            },
+            "provide": {
+                "psr/log-implementation": "1.0|2.0|3.0"
+            },
+            "require-dev": {
+                "psr/log": "^1|^2|^3",
+                "symfony/config": "^7.4|^8.0",
+                "symfony/dependency-injection": "^7.4|^8.0",
+                "symfony/event-dispatcher": "^7.4|^8.0",
+                "symfony/http-foundation": "^7.4|^8.0",
+                "symfony/http-kernel": "^7.4|^8.0",
+                "symfony/lock": "^7.4|^8.0",
+                "symfony/messenger": "^7.4|^8.0",
+                "symfony/process": "^7.4|^8.0",
+                "symfony/stopwatch": "^7.4|^8.0",
+                "symfony/var-dumper": "^7.4|^8.0"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\Console\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Eases the creation of beautiful and testable command line interfaces",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "cli",
+                "command-line",
+                "console",
+                "terminal"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/console/tree/v8.0.9"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/nicolas-grekas",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2026-04-29T15:02:55+00:00"
+        },
+        {
+            "name": "symfony/css-selector",
+            "version": "v8.0.9",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/css-selector.git",
+                "reference": "3665cfade90565430909b906394c73c8739e57d0"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/css-selector/zipball/3665cfade90565430909b906394c73c8739e57d0",
+                "reference": "3665cfade90565430909b906394c73c8739e57d0",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.4"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\CssSelector\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Jean-François Simon",
+                    "email": "jeanfrancois.simon@sensiolabs.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Converts CSS selectors to XPath expressions",
+            "homepage": "https://symfony.com",
+            "support": {
+                "source": "https://github.com/symfony/css-selector/tree/v8.0.9"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/nicolas-grekas",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2026-04-18T13:51:42+00:00"
+        },
+        {
+            "name": "symfony/deprecation-contracts",
+            "version": "v3.6.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/deprecation-contracts.git",
+                "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62",
+                "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.1"
+            },
+            "type": "library",
+            "extra": {
+                "thanks": {
+                    "url": "https://github.com/symfony/contracts",
+                    "name": "symfony/contracts"
+                },
+                "branch-alias": {
+                    "dev-main": "3.6-dev"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "function.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "A generic function and convention to trigger deprecation notices",
+            "homepage": "https://symfony.com",
+            "support": {
+                "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2024-09-25T14:21:43+00:00"
+        },
+        {
+            "name": "symfony/error-handler",
+            "version": "v8.0.8",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/error-handler.git",
+                "reference": "c1119fe8dcfc3825ec74ec061b96ef0c8f281517"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/error-handler/zipball/c1119fe8dcfc3825ec74ec061b96ef0c8f281517",
+                "reference": "c1119fe8dcfc3825ec74ec061b96ef0c8f281517",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.4",
+                "psr/log": "^1|^2|^3",
+                "symfony/polyfill-php85": "^1.32",
+                "symfony/var-dumper": "^7.4|^8.0"
+            },
+            "conflict": {
+                "symfony/deprecation-contracts": "<2.5"
+            },
+            "require-dev": {
+                "symfony/console": "^7.4|^8.0",
+                "symfony/deprecation-contracts": "^2.5|^3",
+                "symfony/http-kernel": "^7.4|^8.0",
+                "symfony/serializer": "^7.4|^8.0",
+                "symfony/webpack-encore-bundle": "^1.0|^2.0"
+            },
+            "bin": [
+                "Resources/bin/patch-type-declarations"
+            ],
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\ErrorHandler\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Provides tools to manage errors and ease debugging PHP code",
+            "homepage": "https://symfony.com",
+            "support": {
+                "source": "https://github.com/symfony/error-handler/tree/v8.0.8"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/nicolas-grekas",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2026-03-30T15:14:47+00:00"
+        },
+        {
+            "name": "symfony/event-dispatcher",
+            "version": "v8.0.9",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/event-dispatcher.git",
+                "reference": "0c3c1a17604c4dbbec4b93fe162c538482096e1f"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/0c3c1a17604c4dbbec4b93fe162c538482096e1f",
+                "reference": "0c3c1a17604c4dbbec4b93fe162c538482096e1f",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.4",
+                "symfony/event-dispatcher-contracts": "^2.5|^3"
+            },
+            "conflict": {
+                "symfony/security-http": "<7.4",
+                "symfony/service-contracts": "<2.5"
+            },
+            "provide": {
+                "psr/event-dispatcher-implementation": "1.0",
+                "symfony/event-dispatcher-implementation": "2.0|3.0"
+            },
+            "require-dev": {
+                "psr/log": "^1|^2|^3",
+                "symfony/config": "^7.4|^8.0",
+                "symfony/dependency-injection": "^7.4|^8.0",
+                "symfony/error-handler": "^7.4|^8.0",
+                "symfony/expression-language": "^7.4|^8.0",
+                "symfony/framework-bundle": "^7.4|^8.0",
+                "symfony/http-foundation": "^7.4|^8.0",
+                "symfony/service-contracts": "^2.5|^3",
+                "symfony/stopwatch": "^7.4|^8.0"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\EventDispatcher\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them",
+            "homepage": "https://symfony.com",
+            "support": {
+                "source": "https://github.com/symfony/event-dispatcher/tree/v8.0.9"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/nicolas-grekas",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2026-04-18T13:51:42+00:00"
+        },
+        {
+            "name": "symfony/event-dispatcher-contracts",
+            "version": "v3.6.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/event-dispatcher-contracts.git",
+                "reference": "59eb412e93815df44f05f342958efa9f46b1e586"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/59eb412e93815df44f05f342958efa9f46b1e586",
+                "reference": "59eb412e93815df44f05f342958efa9f46b1e586",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.1",
+                "psr/event-dispatcher": "^1"
+            },
+            "type": "library",
+            "extra": {
+                "thanks": {
+                    "url": "https://github.com/symfony/contracts",
+                    "name": "symfony/contracts"
+                },
+                "branch-alias": {
+                    "dev-main": "3.6-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Contracts\\EventDispatcher\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Generic abstractions related to dispatching event",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "abstractions",
+                "contracts",
+                "decoupling",
+                "interfaces",
+                "interoperability",
+                "standards"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.6.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2024-09-25T14:21:43+00:00"
+        },
+        {
+            "name": "symfony/finder",
+            "version": "v8.0.8",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/finder.git",
+                "reference": "8da41214757b87d97f181e3d14a4179286151007"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/finder/zipball/8da41214757b87d97f181e3d14a4179286151007",
+                "reference": "8da41214757b87d97f181e3d14a4179286151007",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.4"
+            },
+            "require-dev": {
+                "symfony/filesystem": "^7.4|^8.0"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\Finder\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Finds files and directories via an intuitive fluent interface",
+            "homepage": "https://symfony.com",
+            "support": {
+                "source": "https://github.com/symfony/finder/tree/v8.0.8"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/nicolas-grekas",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2026-03-30T15:14:47+00:00"
+        },
+        {
+            "name": "symfony/http-foundation",
+            "version": "v8.0.8",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/http-foundation.git",
+                "reference": "02656f7ebeae5c155d659e946f6b3a33df24051b"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/http-foundation/zipball/02656f7ebeae5c155d659e946f6b3a33df24051b",
+                "reference": "02656f7ebeae5c155d659e946f6b3a33df24051b",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.4",
+                "symfony/polyfill-mbstring": "^1.1"
+            },
+            "conflict": {
+                "doctrine/dbal": "<4.3"
+            },
+            "require-dev": {
+                "doctrine/dbal": "^4.3",
+                "predis/predis": "^1.1|^2.0",
+                "symfony/cache": "^7.4|^8.0",
+                "symfony/clock": "^7.4|^8.0",
+                "symfony/dependency-injection": "^7.4|^8.0",
+                "symfony/expression-language": "^7.4|^8.0",
+                "symfony/http-kernel": "^7.4|^8.0",
+                "symfony/mime": "^7.4|^8.0",
+                "symfony/rate-limiter": "^7.4|^8.0"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\HttpFoundation\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Defines an object-oriented layer for the HTTP specification",
+            "homepage": "https://symfony.com",
+            "support": {
+                "source": "https://github.com/symfony/http-foundation/tree/v8.0.8"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/nicolas-grekas",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2026-03-30T15:14:47+00:00"
+        },
+        {
+            "name": "symfony/http-kernel",
+            "version": "v8.0.10",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/http-kernel.git",
+                "reference": "fb3f65b3d4ca2dad31c80d323819a762ca31d6ac"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/http-kernel/zipball/fb3f65b3d4ca2dad31c80d323819a762ca31d6ac",
+                "reference": "fb3f65b3d4ca2dad31c80d323819a762ca31d6ac",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.4",
+                "psr/log": "^1|^2|^3",
+                "symfony/error-handler": "^7.4|^8.0",
+                "symfony/event-dispatcher": "^7.4|^8.0",
+                "symfony/http-foundation": "^7.4|^8.0",
+                "symfony/polyfill-ctype": "^1.8"
+            },
+            "conflict": {
+                "symfony/flex": "<2.10",
+                "symfony/http-client-contracts": "<2.5",
+                "symfony/translation-contracts": "<2.5",
+                "twig/twig": "<3.21"
+            },
+            "provide": {
+                "psr/log-implementation": "1.0|2.0|3.0"
+            },
+            "require-dev": {
+                "psr/cache": "^1.0|^2.0|^3.0",
+                "symfony/browser-kit": "^7.4|^8.0",
+                "symfony/clock": "^7.4|^8.0",
+                "symfony/config": "^7.4|^8.0",
+                "symfony/console": "^7.4|^8.0",
+                "symfony/css-selector": "^7.4|^8.0",
+                "symfony/dependency-injection": "^7.4|^8.0",
+                "symfony/dom-crawler": "^7.4|^8.0",
+                "symfony/expression-language": "^7.4|^8.0",
+                "symfony/finder": "^7.4|^8.0",
+                "symfony/http-client-contracts": "^2.5|^3",
+                "symfony/process": "^7.4|^8.0",
+                "symfony/property-access": "^7.4|^8.0",
+                "symfony/routing": "^7.4|^8.0",
+                "symfony/serializer": "^7.4|^8.0",
+                "symfony/stopwatch": "^7.4|^8.0",
+                "symfony/translation": "^7.4|^8.0",
+                "symfony/translation-contracts": "^2.5|^3",
+                "symfony/uid": "^7.4|^8.0",
+                "symfony/validator": "^7.4|^8.0",
+                "symfony/var-dumper": "^7.4|^8.0",
+                "symfony/var-exporter": "^7.4|^8.0",
+                "twig/twig": "^3.21"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\HttpKernel\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Provides a structured process for converting a Request into a Response",
+            "homepage": "https://symfony.com",
+            "support": {
+                "source": "https://github.com/symfony/http-kernel/tree/v8.0.10"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/nicolas-grekas",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2026-05-06T12:27:31+00:00"
+        },
+        {
+            "name": "symfony/mailer",
+            "version": "v8.0.8",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/mailer.git",
+                "reference": "ca5f6edaf8780ece814404b58a4482b22b509c56"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/mailer/zipball/ca5f6edaf8780ece814404b58a4482b22b509c56",
+                "reference": "ca5f6edaf8780ece814404b58a4482b22b509c56",
+                "shasum": ""
+            },
+            "require": {
+                "egulias/email-validator": "^2.1.10|^3|^4",
+                "php": ">=8.4",
+                "psr/event-dispatcher": "^1",
+                "psr/log": "^1|^2|^3",
+                "symfony/event-dispatcher": "^7.4|^8.0",
+                "symfony/mime": "^7.4|^8.0",
+                "symfony/service-contracts": "^2.5|^3"
+            },
+            "conflict": {
+                "symfony/http-client-contracts": "<2.5"
+            },
+            "require-dev": {
+                "symfony/console": "^7.4|^8.0",
+                "symfony/http-client": "^7.4|^8.0",
+                "symfony/messenger": "^7.4|^8.0",
+                "symfony/twig-bridge": "^7.4|^8.0"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\Mailer\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Helps sending emails",
+            "homepage": "https://symfony.com",
+            "support": {
+                "source": "https://github.com/symfony/mailer/tree/v8.0.8"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/nicolas-grekas",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2026-03-30T15:14:47+00:00"
+        },
+        {
+            "name": "symfony/mime",
+            "version": "v8.0.9",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/mime.git",
+                "reference": "a9fcb293650c054b62a5b406f4e92e7b711ea333"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/mime/zipball/a9fcb293650c054b62a5b406f4e92e7b711ea333",
+                "reference": "a9fcb293650c054b62a5b406f4e92e7b711ea333",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.4",
+                "symfony/polyfill-intl-idn": "^1.10",
+                "symfony/polyfill-mbstring": "^1.0"
+            },
+            "conflict": {
+                "egulias/email-validator": "~3.0.0",
+                "phpdocumentor/reflection-docblock": "<5.2|>=7",
+                "phpdocumentor/type-resolver": "<1.5.1"
+            },
+            "require-dev": {
+                "egulias/email-validator": "^2.1.10|^3.1|^4",
+                "league/html-to-markdown": "^5.0",
+                "phpdocumentor/reflection-docblock": "^5.2|^6.0",
+                "symfony/dependency-injection": "^7.4|^8.0",
+                "symfony/process": "^7.4|^8.0",
+                "symfony/property-access": "^7.4|^8.0",
+                "symfony/property-info": "^7.4|^8.0",
+                "symfony/serializer": "^7.4|^8.0"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\Mime\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Allows manipulating MIME messages",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "mime",
+                "mime-type"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/mime/tree/v8.0.9"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/nicolas-grekas",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2026-04-29T15:02:55+00:00"
+        },
+        {
+            "name": "symfony/polyfill-ctype",
+            "version": "v1.37.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-ctype.git",
+                "reference": "141046a8f9477948ff284fa65be2095baafb94f2"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/141046a8f9477948ff284fa65be2095baafb94f2",
+                "reference": "141046a8f9477948ff284fa65be2095baafb94f2",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.2"
+            },
+            "provide": {
+                "ext-ctype": "*"
+            },
+            "suggest": {
+                "ext-ctype": "For best performance"
+            },
+            "type": "library",
+            "extra": {
+                "thanks": {
+                    "url": "https://github.com/symfony/polyfill",
+                    "name": "symfony/polyfill"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "bootstrap.php"
+                ],
+                "psr-4": {
+                    "Symfony\\Polyfill\\Ctype\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Gert de Pagter",
+                    "email": "BackEndTea@gmail.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill for ctype functions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "ctype",
+                "polyfill",
+                "portable"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/polyfill-ctype/tree/v1.37.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/nicolas-grekas",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2026-04-10T16:19:22+00:00"
+        },
+        {
+            "name": "symfony/polyfill-intl-grapheme",
+            "version": "v1.37.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-intl-grapheme.git",
+                "reference": "4864388bfbd3001ce88e234fab652acd91fdc57e"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/4864388bfbd3001ce88e234fab652acd91fdc57e",
+                "reference": "4864388bfbd3001ce88e234fab652acd91fdc57e",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.2"
+            },
+            "suggest": {
+                "ext-intl": "For best performance"
+            },
+            "type": "library",
+            "extra": {
+                "thanks": {
+                    "url": "https://github.com/symfony/polyfill",
+                    "name": "symfony/polyfill"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "bootstrap.php"
+                ],
+                "psr-4": {
+                    "Symfony\\Polyfill\\Intl\\Grapheme\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill for intl's grapheme_* functions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "grapheme",
+                "intl",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.37.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/nicolas-grekas",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2026-04-26T13:13:48+00:00"
+        },
+        {
+            "name": "symfony/polyfill-intl-idn",
+            "version": "v1.37.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-intl-idn.git",
+                "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/9614ac4d8061dc257ecc64cba1b140873dce8ad3",
+                "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.2",
+                "symfony/polyfill-intl-normalizer": "^1.10"
+            },
+            "suggest": {
+                "ext-intl": "For best performance"
+            },
+            "type": "library",
+            "extra": {
+                "thanks": {
+                    "url": "https://github.com/symfony/polyfill",
+                    "name": "symfony/polyfill"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "bootstrap.php"
+                ],
+                "psr-4": {
+                    "Symfony\\Polyfill\\Intl\\Idn\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Laurent Bassin",
+                    "email": "laurent@bassin.info"
+                },
+                {
+                    "name": "Trevor Rowbotham",
+                    "email": "trevor.rowbotham@pm.me"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "idn",
+                "intl",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.37.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/nicolas-grekas",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2024-09-10T14:38:51+00:00"
+        },
+        {
+            "name": "symfony/polyfill-intl-normalizer",
+            "version": "v1.37.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-intl-normalizer.git",
+                "reference": "3833d7255cc303546435cb650316bff708a1c75c"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c",
+                "reference": "3833d7255cc303546435cb650316bff708a1c75c",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.2"
+            },
+            "suggest": {
+                "ext-intl": "For best performance"
+            },
+            "type": "library",
+            "extra": {
+                "thanks": {
+                    "url": "https://github.com/symfony/polyfill",
+                    "name": "symfony/polyfill"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "bootstrap.php"
+                ],
+                "psr-4": {
+                    "Symfony\\Polyfill\\Intl\\Normalizer\\": ""
+                },
+                "classmap": [
+                    "Resources/stubs"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill for intl's Normalizer class and related functions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "intl",
+                "normalizer",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.37.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/nicolas-grekas",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2024-09-09T11:45:10+00:00"
+        },
+        {
+            "name": "symfony/polyfill-mbstring",
+            "version": "v1.37.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-mbstring.git",
+                "reference": "6a21eb99c6973357967f6ce3708cd55a6bec6315"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6a21eb99c6973357967f6ce3708cd55a6bec6315",
+                "reference": "6a21eb99c6973357967f6ce3708cd55a6bec6315",
+                "shasum": ""
+            },
+            "require": {
+                "ext-iconv": "*",
+                "php": ">=7.2"
+            },
+            "provide": {
+                "ext-mbstring": "*"
+            },
+            "suggest": {
+                "ext-mbstring": "For best performance"
+            },
+            "type": "library",
+            "extra": {
+                "thanks": {
+                    "url": "https://github.com/symfony/polyfill",
+                    "name": "symfony/polyfill"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "bootstrap.php"
+                ],
+                "psr-4": {
+                    "Symfony\\Polyfill\\Mbstring\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill for the Mbstring extension",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "mbstring",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.37.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/nicolas-grekas",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2026-04-10T17:25:58+00:00"
+        },
+        {
+            "name": "symfony/polyfill-php80",
+            "version": "v1.37.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-php80.git",
+                "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/dfb55726c3a76ea3b6459fcfda1ec2d80a682411",
+                "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.2"
+            },
+            "type": "library",
+            "extra": {
+                "thanks": {
+                    "url": "https://github.com/symfony/polyfill",
+                    "name": "symfony/polyfill"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "bootstrap.php"
+                ],
+                "psr-4": {
+                    "Symfony\\Polyfill\\Php80\\": ""
+                },
+                "classmap": [
+                    "Resources/stubs"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Ion Bazan",
+                    "email": "ion.bazan@gmail.com"
+                },
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/polyfill-php80/tree/v1.37.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/nicolas-grekas",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2026-04-10T16:19:22+00:00"
+        },
+        {
+            "name": "symfony/polyfill-php84",
+            "version": "v1.37.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-php84.git",
+                "reference": "88486db2c389b290bf87ff1de7ebc1e13e42bb06"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/88486db2c389b290bf87ff1de7ebc1e13e42bb06",
+                "reference": "88486db2c389b290bf87ff1de7ebc1e13e42bb06",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.2"
+            },
+            "type": "library",
+            "extra": {
+                "thanks": {
+                    "url": "https://github.com/symfony/polyfill",
+                    "name": "symfony/polyfill"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "bootstrap.php"
+                ],
+                "psr-4": {
+                    "Symfony\\Polyfill\\Php84\\": ""
+                },
+                "classmap": [
+                    "Resources/stubs"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill backporting some PHP 8.4+ features to lower PHP versions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/polyfill-php84/tree/v1.37.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/nicolas-grekas",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2026-04-10T18:47:49+00:00"
+        },
+        {
+            "name": "symfony/polyfill-php85",
+            "version": "v1.37.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-php85.git",
+                "reference": "fcfa4973a9917cef23f2e38774da74a2b7d115ee"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/fcfa4973a9917cef23f2e38774da74a2b7d115ee",
+                "reference": "fcfa4973a9917cef23f2e38774da74a2b7d115ee",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.2"
+            },
+            "type": "library",
+            "extra": {
+                "thanks": {
+                    "url": "https://github.com/symfony/polyfill",
+                    "name": "symfony/polyfill"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "bootstrap.php"
+                ],
+                "psr-4": {
+                    "Symfony\\Polyfill\\Php85\\": ""
+                },
+                "classmap": [
+                    "Resources/stubs"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill backporting some PHP 8.5+ features to lower PHP versions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/polyfill-php85/tree/v1.37.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/nicolas-grekas",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2026-04-26T13:10:57+00:00"
+        },
+        {
+            "name": "symfony/polyfill-php86",
+            "version": "v1.37.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-php86.git",
+                "reference": "33d8fc5a705481e21fe3a81212b26f9b1f61749c"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-php86/zipball/33d8fc5a705481e21fe3a81212b26f9b1f61749c",
+                "reference": "33d8fc5a705481e21fe3a81212b26f9b1f61749c",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.2"
+            },
+            "type": "library",
+            "extra": {
+                "thanks": {
+                    "url": "https://github.com/symfony/polyfill",
+                    "name": "symfony/polyfill"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "bootstrap.php"
+                ],
+                "psr-4": {
+                    "Symfony\\Polyfill\\Php86\\": ""
+                },
+                "classmap": [
+                    "Resources/stubs"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill backporting some PHP 8.6+ features to lower PHP versions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/polyfill-php86/tree/v1.37.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/nicolas-grekas",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2026-04-26T13:13:48+00:00"
+        },
+        {
+            "name": "symfony/polyfill-uuid",
+            "version": "v1.37.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-uuid.git",
+                "reference": "26dfec253c4cf3e51b541b52ddf7e42cb0908e94"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/26dfec253c4cf3e51b541b52ddf7e42cb0908e94",
+                "reference": "26dfec253c4cf3e51b541b52ddf7e42cb0908e94",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.2"
+            },
+            "provide": {
+                "ext-uuid": "*"
+            },
+            "suggest": {
+                "ext-uuid": "For best performance"
+            },
+            "type": "library",
+            "extra": {
+                "thanks": {
+                    "url": "https://github.com/symfony/polyfill",
+                    "name": "symfony/polyfill"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "bootstrap.php"
+                ],
+                "psr-4": {
+                    "Symfony\\Polyfill\\Uuid\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Grégoire Pineau",
+                    "email": "lyrixx@lyrixx.info"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill for uuid functions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "polyfill",
+                "portable",
+                "uuid"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/polyfill-uuid/tree/v1.37.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/nicolas-grekas",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2026-04-10T16:19:22+00:00"
+        },
+        {
+            "name": "symfony/process",
+            "version": "v8.0.8",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/process.git",
+                "reference": "cb8939aff03470d1a9d1d1b66d08c6fa71b3bbdc"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/process/zipball/cb8939aff03470d1a9d1d1b66d08c6fa71b3bbdc",
+                "reference": "cb8939aff03470d1a9d1d1b66d08c6fa71b3bbdc",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.4"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\Process\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Executes commands in sub-processes",
+            "homepage": "https://symfony.com",
+            "support": {
+                "source": "https://github.com/symfony/process/tree/v8.0.8"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/nicolas-grekas",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2026-03-30T15:14:47+00:00"
+        },
+        {
+            "name": "symfony/routing",
+            "version": "v8.0.9",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/routing.git",
+                "reference": "75d1bd8e5da3424e4db2fc3ff0222cb4d0c73038"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/routing/zipball/75d1bd8e5da3424e4db2fc3ff0222cb4d0c73038",
+                "reference": "75d1bd8e5da3424e4db2fc3ff0222cb4d0c73038",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.4",
+                "symfony/deprecation-contracts": "^2.5|^3"
+            },
+            "require-dev": {
+                "psr/log": "^1|^2|^3",
+                "symfony/config": "^7.4|^8.0",
+                "symfony/dependency-injection": "^7.4|^8.0",
+                "symfony/expression-language": "^7.4|^8.0",
+                "symfony/http-foundation": "^7.4|^8.0",
+                "symfony/yaml": "^7.4|^8.0"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\Routing\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Maps an HTTP request to a set of configuration variables",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "router",
+                "routing",
+                "uri",
+                "url"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/routing/tree/v8.0.9"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/nicolas-grekas",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2026-04-29T15:02:55+00:00"
+        },
+        {
+            "name": "symfony/service-contracts",
+            "version": "v3.6.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/service-contracts.git",
+                "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/service-contracts/zipball/45112560a3ba2d715666a509a0bc9521d10b6c43",
+                "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.1",
+                "psr/container": "^1.1|^2.0",
+                "symfony/deprecation-contracts": "^2.5|^3"
+            },
+            "conflict": {
+                "ext-psr": "<1.1|>=2"
+            },
+            "type": "library",
+            "extra": {
+                "thanks": {
+                    "url": "https://github.com/symfony/contracts",
+                    "name": "symfony/contracts"
+                },
+                "branch-alias": {
+                    "dev-main": "3.6-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Contracts\\Service\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Test/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Generic abstractions related to writing services",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "abstractions",
+                "contracts",
+                "decoupling",
+                "interfaces",
+                "interoperability",
+                "standards"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/service-contracts/tree/v3.6.1"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/nicolas-grekas",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2025-07-15T11:30:57+00:00"
+        },
+        {
+            "name": "symfony/string",
+            "version": "v8.0.8",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/string.git",
+                "reference": "ae9488f874d7603f9d2dfbf120203882b645d963"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/string/zipball/ae9488f874d7603f9d2dfbf120203882b645d963",
+                "reference": "ae9488f874d7603f9d2dfbf120203882b645d963",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.4",
+                "symfony/polyfill-ctype": "^1.8",
+                "symfony/polyfill-intl-grapheme": "^1.33",
+                "symfony/polyfill-intl-normalizer": "^1.0",
+                "symfony/polyfill-mbstring": "^1.0"
+            },
+            "conflict": {
+                "symfony/translation-contracts": "<2.5"
+            },
+            "require-dev": {
+                "symfony/emoji": "^7.4|^8.0",
+                "symfony/http-client": "^7.4|^8.0",
+                "symfony/intl": "^7.4|^8.0",
+                "symfony/translation-contracts": "^2.5|^3.0",
+                "symfony/var-exporter": "^7.4|^8.0"
+            },
+            "type": "library",
+            "autoload": {
+                "files": [
+                    "Resources/functions.php"
+                ],
+                "psr-4": {
+                    "Symfony\\Component\\String\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "grapheme",
+                "i18n",
+                "string",
+                "unicode",
+                "utf-8",
+                "utf8"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/string/tree/v8.0.8"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/nicolas-grekas",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2026-03-30T15:14:47+00:00"
+        },
+        {
+            "name": "symfony/translation",
+            "version": "v8.0.10",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/translation.git",
+                "reference": "f63e9342e12646a57c91ef8a366a4f9d8e557b67"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/translation/zipball/f63e9342e12646a57c91ef8a366a4f9d8e557b67",
+                "reference": "f63e9342e12646a57c91ef8a366a4f9d8e557b67",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.4",
+                "symfony/polyfill-mbstring": "^1.0",
+                "symfony/translation-contracts": "^3.6.1"
+            },
+            "conflict": {
+                "nikic/php-parser": "<5.0",
+                "symfony/http-client-contracts": "<2.5",
+                "symfony/service-contracts": "<2.5"
+            },
+            "provide": {
+                "symfony/translation-implementation": "2.3|3.0"
+            },
+            "require-dev": {
+                "nikic/php-parser": "^5.0",
+                "psr/log": "^1|^2|^3",
+                "symfony/config": "^7.4|^8.0",
+                "symfony/console": "^7.4|^8.0",
+                "symfony/dependency-injection": "^7.4|^8.0",
+                "symfony/finder": "^7.4|^8.0",
+                "symfony/http-client-contracts": "^2.5|^3.0",
+                "symfony/http-kernel": "^7.4|^8.0",
+                "symfony/intl": "^7.4|^8.0",
+                "symfony/polyfill-intl-icu": "^1.21",
+                "symfony/routing": "^7.4|^8.0",
+                "symfony/service-contracts": "^2.5|^3",
+                "symfony/yaml": "^7.4|^8.0"
+            },
+            "type": "library",
+            "autoload": {
+                "files": [
+                    "Resources/functions.php"
+                ],
+                "psr-4": {
+                    "Symfony\\Component\\Translation\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Provides tools to internationalize your application",
+            "homepage": "https://symfony.com",
+            "support": {
+                "source": "https://github.com/symfony/translation/tree/v8.0.10"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/nicolas-grekas",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2026-05-06T11:30:54+00:00"
+        },
+        {
+            "name": "symfony/translation-contracts",
+            "version": "v3.6.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/translation-contracts.git",
+                "reference": "65a8bc82080447fae78373aa10f8d13b38338977"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/65a8bc82080447fae78373aa10f8d13b38338977",
+                "reference": "65a8bc82080447fae78373aa10f8d13b38338977",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.1"
+            },
+            "type": "library",
+            "extra": {
+                "thanks": {
+                    "url": "https://github.com/symfony/contracts",
+                    "name": "symfony/contracts"
+                },
+                "branch-alias": {
+                    "dev-main": "3.6-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Contracts\\Translation\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Test/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Generic abstractions related to translation",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "abstractions",
+                "contracts",
+                "decoupling",
+                "interfaces",
+                "interoperability",
+                "standards"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/translation-contracts/tree/v3.6.1"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/nicolas-grekas",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2025-07-15T13:41:35+00:00"
+        },
+        {
+            "name": "symfony/uid",
+            "version": "v8.0.9",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/uid.git",
+                "reference": "4d9d6510bbe88ebb4608b7200d18606cdf80825c"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/uid/zipball/4d9d6510bbe88ebb4608b7200d18606cdf80825c",
+                "reference": "4d9d6510bbe88ebb4608b7200d18606cdf80825c",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.4",
+                "symfony/polyfill-uuid": "^1.15"
+            },
+            "require-dev": {
+                "symfony/console": "^7.4|^8.0"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\Uid\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Grégoire Pineau",
+                    "email": "lyrixx@lyrixx.info"
+                },
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Provides an object-oriented API to generate and represent UIDs",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "UID",
+                "ulid",
+                "uuid"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/uid/tree/v8.0.9"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/nicolas-grekas",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2026-04-30T16:10:06+00:00"
+        },
+        {
+            "name": "symfony/var-dumper",
+            "version": "v8.0.8",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/var-dumper.git",
+                "reference": "cfb7badd53bf4177f6e9416cfbbccc13c0e773a1"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/var-dumper/zipball/cfb7badd53bf4177f6e9416cfbbccc13c0e773a1",
+                "reference": "cfb7badd53bf4177f6e9416cfbbccc13c0e773a1",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.4",
+                "symfony/polyfill-mbstring": "^1.0"
+            },
+            "conflict": {
+                "symfony/console": "<7.4",
+                "symfony/error-handler": "<7.4"
+            },
+            "require-dev": {
+                "symfony/console": "^7.4|^8.0",
+                "symfony/http-kernel": "^7.4|^8.0",
+                "symfony/process": "^7.4|^8.0",
+                "symfony/uid": "^7.4|^8.0",
+                "twig/twig": "^3.12"
+            },
+            "bin": [
+                "Resources/bin/var-dump-server"
+            ],
+            "type": "library",
+            "autoload": {
+                "files": [
+                    "Resources/functions/dump.php"
+                ],
+                "psr-4": {
+                    "Symfony\\Component\\VarDumper\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Provides mechanisms for walking through any arbitrary PHP variable",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "debug",
+                "dump"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/var-dumper/tree/v8.0.8"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/nicolas-grekas",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2026-03-31T07:15:36+00:00"
+        },
+        {
+            "name": "tijsverkoyen/css-to-inline-styles",
+            "version": "v2.4.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/tijsverkoyen/CssToInlineStyles.git",
+                "reference": "f0292ccf0ec75843d65027214426b6b163b48b41"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/f0292ccf0ec75843d65027214426b6b163b48b41",
+                "reference": "f0292ccf0ec75843d65027214426b6b163b48b41",
+                "shasum": ""
+            },
+            "require": {
+                "ext-dom": "*",
+                "ext-libxml": "*",
+                "php": "^7.4 || ^8.0",
+                "symfony/css-selector": "^5.4 || ^6.0 || ^7.0 || ^8.0"
+            },
+            "require-dev": {
+                "phpstan/phpstan": "^2.0",
+                "phpstan/phpstan-phpunit": "^2.0",
+                "phpunit/phpunit": "^8.5.21 || ^9.5.10"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "TijsVerkoyen\\CssToInlineStyles\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Tijs Verkoyen",
+                    "email": "css_to_inline_styles@verkoyen.eu",
+                    "role": "Developer"
+                }
+            ],
+            "description": "CssToInlineStyles is a class that enables you to convert HTML-pages/files into HTML-pages/files with inline styles. This is very useful when you're sending emails.",
+            "homepage": "https://github.com/tijsverkoyen/CssToInlineStyles",
+            "support": {
+                "issues": "https://github.com/tijsverkoyen/CssToInlineStyles/issues",
+                "source": "https://github.com/tijsverkoyen/CssToInlineStyles/tree/v2.4.0"
+            },
+            "time": "2025-12-02T11:56:42+00:00"
+        },
+        {
+            "name": "vlucas/phpdotenv",
+            "version": "v5.6.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/vlucas/phpdotenv.git",
+                "reference": "955e7815d677a3eaa7075231212f2110983adecc"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/955e7815d677a3eaa7075231212f2110983adecc",
+                "reference": "955e7815d677a3eaa7075231212f2110983adecc",
+                "shasum": ""
+            },
+            "require": {
+                "ext-pcre": "*",
+                "graham-campbell/result-type": "^1.1.4",
+                "php": "^7.2.5 || ^8.0",
+                "phpoption/phpoption": "^1.9.5",
+                "symfony/polyfill-ctype": "^1.26",
+                "symfony/polyfill-mbstring": "^1.26",
+                "symfony/polyfill-php80": "^1.26"
+            },
+            "require-dev": {
+                "bamarni/composer-bin-plugin": "^1.8.2",
+                "ext-filter": "*",
+                "phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2"
+            },
+            "suggest": {
+                "ext-filter": "Required to use the boolean validator."
+            },
+            "type": "library",
+            "extra": {
+                "bamarni-bin": {
+                    "bin-links": true,
+                    "forward-command": false
+                },
+                "branch-alias": {
+                    "dev-master": "5.6-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Dotenv\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Graham Campbell",
+                    "email": "hello@gjcampbell.co.uk",
+                    "homepage": "https://github.com/GrahamCampbell"
+                },
+                {
+                    "name": "Vance Lucas",
+                    "email": "vance@vancelucas.com",
+                    "homepage": "https://github.com/vlucas"
+                }
+            ],
+            "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.",
+            "keywords": [
+                "dotenv",
+                "env",
+                "environment"
+            ],
+            "support": {
+                "issues": "https://github.com/vlucas/phpdotenv/issues",
+                "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.3"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/GrahamCampbell",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2025-12-27T19:49:13+00:00"
+        },
+        {
+            "name": "voku/portable-ascii",
+            "version": "2.1.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/voku/portable-ascii.git",
+                "reference": "8e1051fe39379367aecf014f41744ce7539a856f"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/voku/portable-ascii/zipball/8e1051fe39379367aecf014f41744ce7539a856f",
+                "reference": "8e1051fe39379367aecf014f41744ce7539a856f",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.1.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "~8.5 || ~9.6 || ~10.5 || ~11.5"
+            },
+            "suggest": {
+                "ext-intl": "Use Intl for transliterator_transliterate() support"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "voku\\": "src/voku/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Lars Moelleken",
+                    "homepage": "https://www.moelleken.org/"
+                }
+            ],
+            "description": "Portable ASCII library - performance optimized (ascii) string functions for php.",
+            "homepage": "https://github.com/voku/portable-ascii",
+            "keywords": [
+                "ascii",
+                "clean",
+                "php"
+            ],
+            "support": {
+                "issues": "https://github.com/voku/portable-ascii/issues",
+                "source": "https://github.com/voku/portable-ascii/tree/2.1.1"
+            },
+            "funding": [
+                {
+                    "url": "https://www.paypal.me/moelleken",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/voku",
+                    "type": "github"
+                },
+                {
+                    "url": "https://opencollective.com/portable-ascii",
+                    "type": "open_collective"
+                },
+                {
+                    "url": "https://www.patreon.com/voku",
+                    "type": "patreon"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/voku/portable-ascii",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2026-04-26T05:33:54+00:00"
+        }
+    ],
+    "packages-dev": [
+        {
+            "name": "fakerphp/faker",
+            "version": "v1.24.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/FakerPHP/Faker.git",
+                "reference": "e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5",
+                "reference": "e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.4 || ^8.0",
+                "psr/container": "^1.0 || ^2.0",
+                "symfony/deprecation-contracts": "^2.2 || ^3.0"
+            },
+            "conflict": {
+                "fzaninotto/faker": "*"
+            },
+            "require-dev": {
+                "bamarni/composer-bin-plugin": "^1.4.1",
+                "doctrine/persistence": "^1.3 || ^2.0",
+                "ext-intl": "*",
+                "phpunit/phpunit": "^9.5.26",
+                "symfony/phpunit-bridge": "^5.4.16"
+            },
+            "suggest": {
+                "doctrine/orm": "Required to use Faker\\ORM\\Doctrine",
+                "ext-curl": "Required by Faker\\Provider\\Image to download images.",
+                "ext-dom": "Required by Faker\\Provider\\HtmlLorem for generating random HTML.",
+                "ext-iconv": "Required by Faker\\Provider\\ru_RU\\Text::realText() for generating real Russian text.",
+                "ext-mbstring": "Required for multibyte Unicode string functionality."
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Faker\\": "src/Faker/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "François Zaninotto"
+                }
+            ],
+            "description": "Faker is a PHP library that generates fake data for you.",
+            "keywords": [
+                "data",
+                "faker",
+                "fixtures"
+            ],
+            "support": {
+                "issues": "https://github.com/FakerPHP/Faker/issues",
+                "source": "https://github.com/FakerPHP/Faker/tree/v1.24.1"
+            },
+            "time": "2024-11-21T13:46:39+00:00"
+        },
+        {
+            "name": "filp/whoops",
+            "version": "2.18.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/filp/whoops.git",
+                "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/filp/whoops/zipball/d2102955e48b9fd9ab24280a7ad12ed552752c4d",
+                "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.1 || ^8.0",
+                "psr/log": "^1.0.1 || ^2.0 || ^3.0"
+            },
+            "require-dev": {
+                "mockery/mockery": "^1.0",
+                "phpunit/phpunit": "^7.5.20 || ^8.5.8 || ^9.3.3",
+                "symfony/var-dumper": "^4.0 || ^5.0"
+            },
+            "suggest": {
+                "symfony/var-dumper": "Pretty print complex values better with var-dumper available",
+                "whoops/soap": "Formats errors as SOAP responses"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.7-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Whoops\\": "src/Whoops/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Filipe Dobreira",
+                    "homepage": "https://github.com/filp",
+                    "role": "Developer"
+                }
+            ],
+            "description": "php error handling for cool kids",
+            "homepage": "https://filp.github.io/whoops/",
+            "keywords": [
+                "error",
+                "exception",
+                "handling",
+                "library",
+                "throwable",
+                "whoops"
+            ],
+            "support": {
+                "issues": "https://github.com/filp/whoops/issues",
+                "source": "https://github.com/filp/whoops/tree/2.18.4"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/denis-sokolov",
+                    "type": "github"
+                }
+            ],
+            "time": "2025-08-08T12:00:00+00:00"
+        },
+        {
+            "name": "hamcrest/hamcrest-php",
+            "version": "v2.1.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/hamcrest/hamcrest-php.git",
+                "reference": "f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487",
+                "reference": "f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.4|^8.0"
+            },
+            "replace": {
+                "cordoval/hamcrest-php": "*",
+                "davedevelopment/hamcrest-php": "*",
+                "kodova/hamcrest-php": "*"
+            },
+            "require-dev": {
+                "phpunit/php-file-iterator": "^1.4 || ^2.0 || ^3.0",
+                "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0 || ^8.0 || ^9.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.1-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "hamcrest"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "description": "This is the PHP port of Hamcrest Matchers",
+            "keywords": [
+                "test"
+            ],
+            "support": {
+                "issues": "https://github.com/hamcrest/hamcrest-php/issues",
+                "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.1.1"
+            },
+            "time": "2025-04-30T06:54:44+00:00"
+        },
+        {
+            "name": "laravel/agent-detector",
+            "version": "v2.0.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/laravel/agent-detector.git",
+                "reference": "90694b9256099591cf9e55d08c18ba7a00bf099f"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/laravel/agent-detector/zipball/90694b9256099591cf9e55d08c18ba7a00bf099f",
+                "reference": "90694b9256099591cf9e55d08c18ba7a00bf099f",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^8.2.0"
+            },
+            "require-dev": {
+                "laravel/pint": "^1.24.0",
+                "pestphp/pest": "^3.8.5|^4.1.0",
+                "pestphp/pest-plugin-type-coverage": "^3.0|^4.0.2",
+                "phpstan/phpstan": "^2.1.26",
+                "rector/rector": "^2.1.7",
+                "symfony/var-dumper": "^7.3.3"
+            },
+            "type": "library",
+            "autoload": {
+                "files": [
+                    "src/functions.php"
+                ],
+                "psr-4": {
+                    "Laravel\\AgentDetector\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Taylor Otwell",
+                    "email": "taylor@laravel.com"
+                }
+            ],
+            "description": "Detect if code is running in an AI agent or automated development environment",
+            "homepage": "https://github.com/laravel/agent-detector",
+            "keywords": [
+                "Agent",
+                "ai",
+                "automation",
+                "claude",
+                "cursor",
+                "detection",
+                "devin",
+                "php"
+            ],
+            "support": {
+                "issues": "https://github.com/laravel/agent-detector/issues",
+                "source": "https://github.com/laravel/agent-detector"
+            },
+            "time": "2026-04-29T18:32:34+00:00"
+        },
+        {
+            "name": "laravel/boost",
+            "version": "v2.4.6",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/laravel/boost.git",
+                "reference": "c9ea6368c66f7c0e6a9b26706b401de900cdb9ac"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/laravel/boost/zipball/c9ea6368c66f7c0e6a9b26706b401de900cdb9ac",
+                "reference": "c9ea6368c66f7c0e6a9b26706b401de900cdb9ac",
+                "shasum": ""
+            },
+            "require": {
+                "guzzlehttp/guzzle": "^7.9",
+                "illuminate/console": "^11.45.3|^12.41.1|^13.0",
+                "illuminate/contracts": "^11.45.3|^12.41.1|^13.0",
+                "illuminate/routing": "^11.45.3|^12.41.1|^13.0",
+                "illuminate/support": "^11.45.3|^12.41.1|^13.0",
+                "laravel/mcp": "^0.5.1|^0.6.0|^0.7.0",
+                "laravel/prompts": "^0.3.10",
+                "laravel/roster": "^0.5.0",
+                "php": "^8.2"
+            },
+            "require-dev": {
+                "laravel/pint": "^1.27.0",
+                "mockery/mockery": "^1.6.12",
+                "orchestra/testbench": "^9.15.0|^10.6|^11.0",
+                "pestphp/pest": "^2.36.0|^3.8.4|^4.1.5",
+                "phpstan/phpstan": "^2.1.27",
+                "rector/rector": "^2.1"
+            },
+            "type": "library",
+            "extra": {
+                "laravel": {
+                    "providers": [
+                        "Laravel\\Boost\\BoostServiceProvider"
+                    ]
+                },
+                "branch-alias": {
+                    "dev-master": "1.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Laravel\\Boost\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "Laravel Boost accelerates AI-assisted development by providing the essential context and structure that AI needs to generate high-quality, Laravel-specific code.",
+            "homepage": "https://github.com/laravel/boost",
+            "keywords": [
+                "ai",
+                "dev",
+                "laravel"
+            ],
+            "support": {
+                "issues": "https://github.com/laravel/boost/issues",
+                "source": "https://github.com/laravel/boost"
+            },
+            "time": "2026-04-28T11:52:01+00:00"
+        },
+        {
+            "name": "laravel/mcp",
+            "version": "v0.7.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/laravel/mcp.git",
+                "reference": "3513b4feca5f1678be4d2261dcfa8e456436d02a"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/laravel/mcp/zipball/3513b4feca5f1678be4d2261dcfa8e456436d02a",
+                "reference": "3513b4feca5f1678be4d2261dcfa8e456436d02a",
+                "shasum": ""
+            },
+            "require": {
+                "ext-json": "*",
+                "ext-mbstring": "*",
+                "illuminate/console": "^11.45.3|^12.41.1|^13.0",
+                "illuminate/container": "^11.45.3|^12.41.1|^13.0",
+                "illuminate/contracts": "^11.45.3|^12.41.1|^13.0",
+                "illuminate/http": "^11.45.3|^12.41.1|^13.0",
+                "illuminate/json-schema": "^12.41.1|^13.0",
+                "illuminate/routing": "^11.45.3|^12.41.1|^13.0",
+                "illuminate/support": "^11.45.3|^12.41.1|^13.0",
+                "illuminate/validation": "^11.45.3|^12.41.1|^13.0",
+                "php": "^8.2"
+            },
+            "require-dev": {
+                "laravel/pint": "^1.20",
+                "orchestra/testbench": "^9.15|^10.8|^11.0",
+                "pestphp/pest": "^3.8.5|^4.3.2",
+                "phpstan/phpstan": "^2.1.27",
+                "rector/rector": "^2.2.4"
+            },
+            "type": "library",
+            "extra": {
+                "laravel": {
+                    "aliases": {
+                        "Mcp": "Laravel\\Mcp\\Server\\Facades\\Mcp"
+                    },
+                    "providers": [
+                        "Laravel\\Mcp\\Server\\McpServiceProvider"
+                    ]
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Laravel\\Mcp\\": "src/",
+                    "Laravel\\Mcp\\Server\\": "src/Server/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Taylor Otwell",
+                    "email": "taylor@laravel.com"
+                }
+            ],
+            "description": "Rapidly build MCP servers for your Laravel applications.",
+            "homepage": "https://github.com/laravel/mcp",
+            "keywords": [
+                "laravel",
+                "mcp"
+            ],
+            "support": {
+                "issues": "https://github.com/laravel/mcp/issues",
+                "source": "https://github.com/laravel/mcp"
+            },
+            "time": "2026-04-21T10:23:03+00:00"
+        },
+        {
+            "name": "laravel/pail",
+            "version": "v1.2.6",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/laravel/pail.git",
+                "reference": "aa71a01c309e7f66bc2ec4fb1a59291b82eb4abf"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/laravel/pail/zipball/aa71a01c309e7f66bc2ec4fb1a59291b82eb4abf",
+                "reference": "aa71a01c309e7f66bc2ec4fb1a59291b82eb4abf",
+                "shasum": ""
+            },
+            "require": {
+                "ext-mbstring": "*",
+                "illuminate/console": "^10.24|^11.0|^12.0|^13.0",
+                "illuminate/contracts": "^10.24|^11.0|^12.0|^13.0",
+                "illuminate/log": "^10.24|^11.0|^12.0|^13.0",
+                "illuminate/process": "^10.24|^11.0|^12.0|^13.0",
+                "illuminate/support": "^10.24|^11.0|^12.0|^13.0",
+                "nunomaduro/termwind": "^1.15|^2.0",
+                "php": "^8.2",
+                "symfony/console": "^6.0|^7.0|^8.0"
+            },
+            "require-dev": {
+                "laravel/framework": "^10.24|^11.0|^12.0|^13.0",
+                "laravel/pint": "^1.13",
+                "orchestra/testbench-core": "^8.13|^9.17|^10.8|^11.0",
+                "pestphp/pest": "^2.20|^3.0|^4.0",
+                "pestphp/pest-plugin-type-coverage": "^2.3|^3.0|^4.0",
+                "phpstan/phpstan": "^1.12.27",
+                "symfony/var-dumper": "^6.3|^7.0|^8.0",
+                "symfony/yaml": "^6.3|^7.0|^8.0"
+            },
+            "type": "library",
+            "extra": {
+                "laravel": {
+                    "providers": [
+                        "Laravel\\Pail\\PailServiceProvider"
+                    ]
+                },
+                "branch-alias": {
+                    "dev-main": "1.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Laravel\\Pail\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Taylor Otwell",
+                    "email": "taylor@laravel.com"
+                },
+                {
+                    "name": "Nuno Maduro",
+                    "email": "enunomaduro@gmail.com"
+                }
+            ],
+            "description": "Easily delve into your Laravel application's log files directly from the command line.",
+            "homepage": "https://github.com/laravel/pail",
+            "keywords": [
+                "dev",
+                "laravel",
+                "logs",
+                "php",
+                "tail"
+            ],
+            "support": {
+                "issues": "https://github.com/laravel/pail/issues",
+                "source": "https://github.com/laravel/pail"
+            },
+            "time": "2026-02-09T13:44:54+00:00"
+        },
+        {
+            "name": "laravel/pao",
+            "version": "v1.0.6",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/laravel/pao.git",
+                "reference": "02f62a64c2b60af44a418ee490fee193590d8269"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/laravel/pao/zipball/02f62a64c2b60af44a418ee490fee193590d8269",
+                "reference": "02f62a64c2b60af44a418ee490fee193590d8269",
+                "shasum": ""
+            },
+            "require": {
+                "laravel/agent-detector": "^2.0.0",
+                "php": "^8.3"
+            },
+            "conflict": {
+                "laravel/framework": "<12.0.0",
+                "nunomaduro/collision": "<8.9.3",
+                "pestphp/pest": "<4.6.3 || >=6.0.0",
+                "phpunit/phpunit": "<12.5.23 || >=13.0.0 <13.1.7 || >=14.0.0"
+            },
+            "require-dev": {
+                "brianium/paratest": "^7.20.0",
+                "laravel/pint": "^1.29.1",
+                "orchestra/testbench": "^10.11.0 || ^11.1.0",
+                "pestphp/pest": "^4.6.3 || ^5.0.0",
+                "pestphp/pest-plugin-type-coverage": "^4.0.4 || ^5.0.0",
+                "phpstan/phpstan": "^2.1.51",
+                "rector/rector": "^2.4.2",
+                "symfony/process": "^7.4.8 || ^8.1.0",
+                "symfony/var-dumper": "^7.4.8 || ^8.0.8"
+            },
+            "type": "library",
+            "extra": {
+                "pest": {
+                    "plugins": [
+                        "Laravel\\Pao\\Drivers\\Pest\\Plugin"
+                    ]
+                },
+                "laravel": {
+                    "providers": [
+                        "Laravel\\Pao\\Laravel\\ServiceProvider"
+                    ]
+                }
+            },
+            "autoload": {
+                "files": [
+                    "src/Autoload.php"
+                ],
+                "psr-4": {
+                    "Laravel\\Pao\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Taylor Otwell",
+                    "email": "taylor@laravel.com"
+                }
+            ],
+            "description": "Agent-optimized output for PHP testing tools",
+            "keywords": [
+                "Agent",
+                "PHPStan",
+                "ai",
+                "dev",
+                "paratest",
+                "pest",
+                "php",
+                "phpunit",
+                "testing"
+            ],
+            "support": {
+                "issues": "https://github.com/laravel/pao/issues",
+                "source": "https://github.com/laravel/pao"
+            },
+            "time": "2026-04-27T22:37:26+00:00"
+        },
+        {
+            "name": "laravel/pint",
+            "version": "v1.29.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/laravel/pint.git",
+                "reference": "0770e9b7fafd50d4586881d456d6eb41c9247a80"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/laravel/pint/zipball/0770e9b7fafd50d4586881d456d6eb41c9247a80",
+                "reference": "0770e9b7fafd50d4586881d456d6eb41c9247a80",
+                "shasum": ""
+            },
+            "require": {
+                "ext-json": "*",
+                "ext-mbstring": "*",
+                "ext-tokenizer": "*",
+                "ext-xml": "*",
+                "php": "^8.2.0"
+            },
+            "require-dev": {
+                "friendsofphp/php-cs-fixer": "^3.95.1",
+                "illuminate/view": "^12.56.0",
+                "larastan/larastan": "^3.9.6",
+                "laravel-zero/framework": "^12.1.0",
+                "mockery/mockery": "^1.6.12",
+                "nunomaduro/termwind": "^2.4.0",
+                "pestphp/pest": "^3.8.6",
+                "shipfastlabs/agent-detector": "^1.1.3"
+            },
+            "bin": [
+                "builds/pint"
+            ],
+            "type": "project",
+            "autoload": {
+                "psr-4": {
+                    "App\\": "app/",
+                    "Database\\Seeders\\": "database/seeders/",
+                    "Database\\Factories\\": "database/factories/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nuno Maduro",
+                    "email": "enunomaduro@gmail.com"
+                }
+            ],
+            "description": "An opinionated code formatter for PHP.",
+            "homepage": "https://laravel.com",
+            "keywords": [
+                "dev",
+                "format",
+                "formatter",
+                "lint",
+                "linter",
+                "php"
+            ],
+            "support": {
+                "issues": "https://github.com/laravel/pint/issues",
+                "source": "https://github.com/laravel/pint"
+            },
+            "time": "2026-04-20T15:26:14+00:00"
+        },
+        {
+            "name": "laravel/roster",
+            "version": "v0.5.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/laravel/roster.git",
+                "reference": "5089de7615f72f78e831590ff9d0435fed0102bb"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/laravel/roster/zipball/5089de7615f72f78e831590ff9d0435fed0102bb",
+                "reference": "5089de7615f72f78e831590ff9d0435fed0102bb",
+                "shasum": ""
+            },
+            "require": {
+                "illuminate/console": "^11.0|^12.0|^13.0",
+                "illuminate/contracts": "^11.0|^12.0|^13.0",
+                "illuminate/routing": "^11.0|^12.0|^13.0",
+                "illuminate/support": "^11.0|^12.0|^13.0",
+                "php": "^8.2",
+                "symfony/yaml": "^7.2|^8.0"
+            },
+            "require-dev": {
+                "laravel/pint": "^1.14",
+                "mockery/mockery": "^1.6",
+                "orchestra/testbench": "^9.0|^10.0|^11.0",
+                "pestphp/pest": "^3.0|^4.1",
+                "phpstan/phpstan": "^2.0"
+            },
+            "type": "library",
+            "extra": {
+                "laravel": {
+                    "providers": [
+                        "Laravel\\Roster\\RosterServiceProvider"
+                    ]
+                },
+                "branch-alias": {
+                    "dev-master": "1.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Laravel\\Roster\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "Detect packages & approaches in use within a Laravel project",
+            "homepage": "https://github.com/laravel/roster",
+            "keywords": [
+                "dev",
+                "laravel"
+            ],
+            "support": {
+                "issues": "https://github.com/laravel/roster/issues",
+                "source": "https://github.com/laravel/roster"
+            },
+            "time": "2026-03-05T07:58:43+00:00"
+        },
+        {
+            "name": "mockery/mockery",
+            "version": "1.6.12",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/mockery/mockery.git",
+                "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/mockery/mockery/zipball/1f4efdd7d3beafe9807b08156dfcb176d18f1699",
+                "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699",
+                "shasum": ""
+            },
+            "require": {
+                "hamcrest/hamcrest-php": "^2.0.1",
+                "lib-pcre": ">=7.0",
+                "php": ">=7.3"
+            },
+            "conflict": {
+                "phpunit/phpunit": "<8.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^8.5 || ^9.6.17",
+                "symplify/easy-coding-standard": "^12.1.14"
+            },
+            "type": "library",
+            "autoload": {
+                "files": [
+                    "library/helpers.php",
+                    "library/Mockery.php"
+                ],
+                "psr-4": {
+                    "Mockery\\": "library/Mockery"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Pádraic Brady",
+                    "email": "padraic.brady@gmail.com",
+                    "homepage": "https://github.com/padraic",
+                    "role": "Author"
+                },
+                {
+                    "name": "Dave Marshall",
+                    "email": "dave.marshall@atstsolutions.co.uk",
+                    "homepage": "https://davedevelopment.co.uk",
+                    "role": "Developer"
+                },
+                {
+                    "name": "Nathanael Esayeas",
+                    "email": "nathanael.esayeas@protonmail.com",
+                    "homepage": "https://github.com/ghostwriter",
+                    "role": "Lead Developer"
+                }
+            ],
+            "description": "Mockery is a simple yet flexible PHP mock object framework",
+            "homepage": "https://github.com/mockery/mockery",
+            "keywords": [
+                "BDD",
+                "TDD",
+                "library",
+                "mock",
+                "mock objects",
+                "mockery",
+                "stub",
+                "test",
+                "test double",
+                "testing"
+            ],
+            "support": {
+                "docs": "https://docs.mockery.io/",
+                "issues": "https://github.com/mockery/mockery/issues",
+                "rss": "https://github.com/mockery/mockery/releases.atom",
+                "security": "https://github.com/mockery/mockery/security/advisories",
+                "source": "https://github.com/mockery/mockery"
+            },
+            "time": "2024-05-16T03:13:13+00:00"
+        },
+        {
+            "name": "myclabs/deep-copy",
+            "version": "1.13.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/myclabs/DeepCopy.git",
+                "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a",
+                "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.1 || ^8.0"
+            },
+            "conflict": {
+                "doctrine/collections": "<1.6.8",
+                "doctrine/common": "<2.13.3 || >=3 <3.2.2"
+            },
+            "require-dev": {
+                "doctrine/collections": "^1.6.8",
+                "doctrine/common": "^2.13.3 || ^3.2.2",
+                "phpspec/prophecy": "^1.10",
+                "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13"
+            },
+            "type": "library",
+            "autoload": {
+                "files": [
+                    "src/DeepCopy/deep_copy.php"
+                ],
+                "psr-4": {
+                    "DeepCopy\\": "src/DeepCopy/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "Create deep copies (clones) of your objects",
+            "keywords": [
+                "clone",
+                "copy",
+                "duplicate",
+                "object",
+                "object graph"
+            ],
+            "support": {
+                "issues": "https://github.com/myclabs/DeepCopy/issues",
+                "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4"
+            },
+            "funding": [
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2025-08-01T08:46:24+00:00"
+        },
+        {
+            "name": "nunomaduro/collision",
+            "version": "v8.9.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/nunomaduro/collision.git",
+                "reference": "716af8f95a470e9094cfca09ed897b023be191a5"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/nunomaduro/collision/zipball/716af8f95a470e9094cfca09ed897b023be191a5",
+                "reference": "716af8f95a470e9094cfca09ed897b023be191a5",
+                "shasum": ""
+            },
+            "require": {
+                "filp/whoops": "^2.18.4",
+                "nunomaduro/termwind": "^2.4.0",
+                "php": "^8.2.0",
+                "symfony/console": "^7.4.8 || ^8.0.8"
+            },
+            "conflict": {
+                "laravel/framework": "<11.48.0 || >=14.0.0",
+                "phpunit/phpunit": "<11.5.50 || >=14.0.0"
+            },
+            "require-dev": {
+                "brianium/paratest": "^7.8.5",
+                "larastan/larastan": "^3.9.6",
+                "laravel/framework": "^11.48.0 || ^12.56.0 || ^13.5.0",
+                "laravel/pint": "^1.29.1",
+                "orchestra/testbench-core": "^9.12.0 || ^10.12.1 || ^11.2.1",
+                "pestphp/pest": "^3.8.5 || ^4.4.3 || ^5.0.0",
+                "sebastian/environment": "^7.2.1 || ^8.0.4 || ^9.3.0"
+            },
+            "type": "library",
+            "extra": {
+                "laravel": {
+                    "providers": [
+                        "NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider"
+                    ]
+                },
+                "branch-alias": {
+                    "dev-8.x": "8.x-dev"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "./src/Adapters/Phpunit/Autoload.php"
+                ],
+                "psr-4": {
+                    "NunoMaduro\\Collision\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nuno Maduro",
+                    "email": "enunomaduro@gmail.com"
+                }
+            ],
+            "description": "Cli error handling for console/command-line PHP applications.",
+            "keywords": [
+                "artisan",
+                "cli",
+                "command-line",
+                "console",
+                "dev",
+                "error",
+                "handling",
+                "laravel",
+                "laravel-zero",
+                "php",
+                "symfony"
+            ],
+            "support": {
+                "issues": "https://github.com/nunomaduro/collision/issues",
+                "source": "https://github.com/nunomaduro/collision"
+            },
+            "funding": [
+                {
+                    "url": "https://www.paypal.com/paypalme/enunomaduro",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/nunomaduro",
+                    "type": "github"
+                },
+                {
+                    "url": "https://www.patreon.com/nunomaduro",
+                    "type": "patreon"
+                }
+            ],
+            "time": "2026-04-21T14:04:20+00:00"
+        },
+        {
+            "name": "phar-io/manifest",
+            "version": "2.0.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phar-io/manifest.git",
+                "reference": "54750ef60c58e43759730615a392c31c80e23176"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176",
+                "reference": "54750ef60c58e43759730615a392c31c80e23176",
+                "shasum": ""
+            },
+            "require": {
+                "ext-dom": "*",
+                "ext-libxml": "*",
+                "ext-phar": "*",
+                "ext-xmlwriter": "*",
+                "phar-io/version": "^3.0.1",
+                "php": "^7.2 || ^8.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.0.x-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Arne Blankerts",
+                    "email": "arne@blankerts.de",
+                    "role": "Developer"
+                },
+                {
+                    "name": "Sebastian Heuer",
+                    "email": "sebastian@phpeople.de",
+                    "role": "Developer"
+                },
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "Developer"
+                }
+            ],
+            "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)",
+            "support": {
+                "issues": "https://github.com/phar-io/manifest/issues",
+                "source": "https://github.com/phar-io/manifest/tree/2.0.4"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/theseer",
+                    "type": "github"
+                }
+            ],
+            "time": "2024-03-03T12:33:53+00:00"
+        },
+        {
+            "name": "phar-io/version",
+            "version": "3.2.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phar-io/version.git",
+                "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74",
+                "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.2 || ^8.0"
+            },
+            "type": "library",
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Arne Blankerts",
+                    "email": "arne@blankerts.de",
+                    "role": "Developer"
+                },
+                {
+                    "name": "Sebastian Heuer",
+                    "email": "sebastian@phpeople.de",
+                    "role": "Developer"
+                },
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "Developer"
+                }
+            ],
+            "description": "Library for handling version information and constraints",
+            "support": {
+                "issues": "https://github.com/phar-io/version/issues",
+                "source": "https://github.com/phar-io/version/tree/3.2.1"
+            },
+            "time": "2022-02-21T01:04:05+00:00"
+        },
+        {
+            "name": "phpunit/php-code-coverage",
+            "version": "12.5.6",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
+                "reference": "876099a072646c7745f673d7aeab5382c4439691"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/876099a072646c7745f673d7aeab5382c4439691",
+                "reference": "876099a072646c7745f673d7aeab5382c4439691",
+                "shasum": ""
+            },
+            "require": {
+                "ext-dom": "*",
+                "ext-libxml": "*",
+                "ext-xmlwriter": "*",
+                "nikic/php-parser": "^5.7.0",
+                "php": ">=8.3",
+                "phpunit/php-text-template": "^5.0",
+                "sebastian/complexity": "^5.0",
+                "sebastian/environment": "^8.0.3",
+                "sebastian/lines-of-code": "^4.0",
+                "sebastian/version": "^6.0",
+                "theseer/tokenizer": "^2.0.1"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^12.5.1"
+            },
+            "suggest": {
+                "ext-pcov": "PHP extension that provides line coverage",
+                "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "12.5.x-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
+            "homepage": "https://github.com/sebastianbergmann/php-code-coverage",
+            "keywords": [
+                "coverage",
+                "testing",
+                "xunit"
+            ],
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
+                "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
+                "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/12.5.6"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                },
+                {
+                    "url": "https://liberapay.com/sebastianbergmann",
+                    "type": "liberapay"
+                },
+                {
+                    "url": "https://thanks.dev/u/gh/sebastianbergmann",
+                    "type": "thanks_dev"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/phpunit/php-code-coverage",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2026-04-15T08:23:17+00:00"
+        },
+        {
+            "name": "phpunit/php-file-iterator",
+            "version": "6.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
+                "reference": "3d1cd096ef6bea4bf2762ba586e35dbd317cbfd5"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/3d1cd096ef6bea4bf2762ba586e35dbd317cbfd5",
+                "reference": "3d1cd096ef6bea4bf2762ba586e35dbd317cbfd5",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^12.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "6.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "FilterIterator implementation that filters files based on a list of suffixes.",
+            "homepage": "https://github.com/sebastianbergmann/php-file-iterator/",
+            "keywords": [
+                "filesystem",
+                "iterator"
+            ],
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues",
+                "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy",
+                "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/6.0.1"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                },
+                {
+                    "url": "https://liberapay.com/sebastianbergmann",
+                    "type": "liberapay"
+                },
+                {
+                    "url": "https://thanks.dev/u/gh/sebastianbergmann",
+                    "type": "thanks_dev"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/phpunit/php-file-iterator",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2026-02-02T14:04:18+00:00"
+        },
+        {
+            "name": "phpunit/php-invoker",
+            "version": "6.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/php-invoker.git",
+                "reference": "12b54e689b07a25a9b41e57736dfab6ec9ae5406"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/12b54e689b07a25a9b41e57736dfab6ec9ae5406",
+                "reference": "12b54e689b07a25a9b41e57736dfab6ec9ae5406",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.3"
+            },
+            "require-dev": {
+                "ext-pcntl": "*",
+                "phpunit/phpunit": "^12.0"
+            },
+            "suggest": {
+                "ext-pcntl": "*"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "6.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "Invoke callables with a timeout",
+            "homepage": "https://github.com/sebastianbergmann/php-invoker/",
+            "keywords": [
+                "process"
+            ],
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/php-invoker/issues",
+                "security": "https://github.com/sebastianbergmann/php-invoker/security/policy",
+                "source": "https://github.com/sebastianbergmann/php-invoker/tree/6.0.0"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2025-02-07T04:58:58+00:00"
+        },
+        {
+            "name": "phpunit/php-text-template",
+            "version": "5.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/php-text-template.git",
+                "reference": "e1367a453f0eda562eedb4f659e13aa900d66c53"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/e1367a453f0eda562eedb4f659e13aa900d66c53",
+                "reference": "e1367a453f0eda562eedb4f659e13aa900d66c53",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^12.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "5.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "Simple template engine.",
+            "homepage": "https://github.com/sebastianbergmann/php-text-template/",
+            "keywords": [
+                "template"
+            ],
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/php-text-template/issues",
+                "security": "https://github.com/sebastianbergmann/php-text-template/security/policy",
+                "source": "https://github.com/sebastianbergmann/php-text-template/tree/5.0.0"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2025-02-07T04:59:16+00:00"
+        },
+        {
+            "name": "phpunit/php-timer",
+            "version": "8.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/php-timer.git",
+                "reference": "f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc",
+                "reference": "f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^12.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "8.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "Utility class for timing",
+            "homepage": "https://github.com/sebastianbergmann/php-timer/",
+            "keywords": [
+                "timer"
+            ],
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/php-timer/issues",
+                "security": "https://github.com/sebastianbergmann/php-timer/security/policy",
+                "source": "https://github.com/sebastianbergmann/php-timer/tree/8.0.0"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2025-02-07T04:59:38+00:00"
+        },
+        {
+            "name": "phpunit/phpunit",
+            "version": "12.5.24",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/phpunit.git",
+                "reference": "d75dd30597caa80e72fad2ef7904601a30ef1046"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/d75dd30597caa80e72fad2ef7904601a30ef1046",
+                "reference": "d75dd30597caa80e72fad2ef7904601a30ef1046",
+                "shasum": ""
+            },
+            "require": {
+                "ext-dom": "*",
+                "ext-json": "*",
+                "ext-libxml": "*",
+                "ext-mbstring": "*",
+                "ext-xml": "*",
+                "ext-xmlwriter": "*",
+                "myclabs/deep-copy": "^1.13.4",
+                "phar-io/manifest": "^2.0.4",
+                "phar-io/version": "^3.2.1",
+                "php": ">=8.3",
+                "phpunit/php-code-coverage": "^12.5.6",
+                "phpunit/php-file-iterator": "^6.0.1",
+                "phpunit/php-invoker": "^6.0.0",
+                "phpunit/php-text-template": "^5.0.0",
+                "phpunit/php-timer": "^8.0.0",
+                "sebastian/cli-parser": "^4.2.0",
+                "sebastian/comparator": "^7.1.6",
+                "sebastian/diff": "^7.0.0",
+                "sebastian/environment": "^8.1.0",
+                "sebastian/exporter": "^7.0.2",
+                "sebastian/global-state": "^8.0.2",
+                "sebastian/object-enumerator": "^7.0.0",
+                "sebastian/recursion-context": "^7.0.1",
+                "sebastian/type": "^6.0.3",
+                "sebastian/version": "^6.0.0",
+                "staabm/side-effects-detector": "^1.0.5"
+            },
+            "bin": [
+                "phpunit"
+            ],
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "12.5-dev"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "src/Framework/Assert/Functions.php"
+                ],
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "The PHP Unit Testing framework.",
+            "homepage": "https://phpunit.de/",
+            "keywords": [
+                "phpunit",
+                "testing",
+                "xunit"
+            ],
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/phpunit/issues",
+                "security": "https://github.com/sebastianbergmann/phpunit/security/policy",
+                "source": "https://github.com/sebastianbergmann/phpunit/tree/12.5.24"
+            },
+            "funding": [
+                {
+                    "url": "https://phpunit.de/sponsoring.html",
+                    "type": "other"
+                }
+            ],
+            "time": "2026-05-01T04:21:04+00:00"
+        },
+        {
+            "name": "sebastian/cli-parser",
+            "version": "4.2.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/cli-parser.git",
+                "reference": "90f41072d220e5c40df6e8635f5dafba2d9d4d04"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/90f41072d220e5c40df6e8635f5dafba2d9d4d04",
+                "reference": "90f41072d220e5c40df6e8635f5dafba2d9d4d04",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^12.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "4.2-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "Library for parsing CLI options",
+            "homepage": "https://github.com/sebastianbergmann/cli-parser",
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/cli-parser/issues",
+                "security": "https://github.com/sebastianbergmann/cli-parser/security/policy",
+                "source": "https://github.com/sebastianbergmann/cli-parser/tree/4.2.0"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                },
+                {
+                    "url": "https://liberapay.com/sebastianbergmann",
+                    "type": "liberapay"
+                },
+                {
+                    "url": "https://thanks.dev/u/gh/sebastianbergmann",
+                    "type": "thanks_dev"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/sebastian/cli-parser",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2025-09-14T09:36:45+00:00"
+        },
+        {
+            "name": "sebastian/comparator",
+            "version": "7.1.6",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/comparator.git",
+                "reference": "c769009dee98f494e0edc3fd4f4087501688f11e"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/c769009dee98f494e0edc3fd4f4087501688f11e",
+                "reference": "c769009dee98f494e0edc3fd4f4087501688f11e",
+                "shasum": ""
+            },
+            "require": {
+                "ext-dom": "*",
+                "ext-mbstring": "*",
+                "php": ">=8.3",
+                "sebastian/diff": "^7.0",
+                "sebastian/exporter": "^7.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^12.2"
+            },
+            "suggest": {
+                "ext-bcmath": "For comparing BcMath\\Number objects"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "7.1-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                },
+                {
+                    "name": "Jeff Welch",
+                    "email": "whatthejeff@gmail.com"
+                },
+                {
+                    "name": "Volker Dusch",
+                    "email": "github@wallbash.com"
+                },
+                {
+                    "name": "Bernhard Schussek",
+                    "email": "bschussek@2bepublished.at"
+                }
+            ],
+            "description": "Provides the functionality to compare PHP values for equality",
+            "homepage": "https://github.com/sebastianbergmann/comparator",
+            "keywords": [
+                "comparator",
+                "compare",
+                "equality"
+            ],
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/comparator/issues",
+                "security": "https://github.com/sebastianbergmann/comparator/security/policy",
+                "source": "https://github.com/sebastianbergmann/comparator/tree/7.1.6"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                },
+                {
+                    "url": "https://liberapay.com/sebastianbergmann",
+                    "type": "liberapay"
+                },
+                {
+                    "url": "https://thanks.dev/u/gh/sebastianbergmann",
+                    "type": "thanks_dev"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2026-04-14T08:23:15+00:00"
+        },
+        {
+            "name": "sebastian/complexity",
+            "version": "5.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/complexity.git",
+                "reference": "bad4316aba5303d0221f43f8cee37eb58d384bbb"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/bad4316aba5303d0221f43f8cee37eb58d384bbb",
+                "reference": "bad4316aba5303d0221f43f8cee37eb58d384bbb",
+                "shasum": ""
+            },
+            "require": {
+                "nikic/php-parser": "^5.0",
+                "php": ">=8.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^12.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "5.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "Library for calculating the complexity of PHP code units",
+            "homepage": "https://github.com/sebastianbergmann/complexity",
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/complexity/issues",
+                "security": "https://github.com/sebastianbergmann/complexity/security/policy",
+                "source": "https://github.com/sebastianbergmann/complexity/tree/5.0.0"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2025-02-07T04:55:25+00:00"
+        },
+        {
+            "name": "sebastian/diff",
+            "version": "7.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/diff.git",
+                "reference": "7ab1ea946c012266ca32390913653d844ecd085f"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7ab1ea946c012266ca32390913653d844ecd085f",
+                "reference": "7ab1ea946c012266ca32390913653d844ecd085f",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^12.0",
+                "symfony/process": "^7.2"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "7.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                },
+                {
+                    "name": "Kore Nordmann",
+                    "email": "mail@kore-nordmann.de"
+                }
+            ],
+            "description": "Diff implementation",
+            "homepage": "https://github.com/sebastianbergmann/diff",
+            "keywords": [
+                "diff",
+                "udiff",
+                "unidiff",
+                "unified diff"
+            ],
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/diff/issues",
+                "security": "https://github.com/sebastianbergmann/diff/security/policy",
+                "source": "https://github.com/sebastianbergmann/diff/tree/7.0.0"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2025-02-07T04:55:46+00:00"
+        },
+        {
+            "name": "sebastian/environment",
+            "version": "8.1.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/environment.git",
+                "reference": "b121608b28a13f721e76ffbbd386d08eff58f3f6"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/b121608b28a13f721e76ffbbd386d08eff58f3f6",
+                "reference": "b121608b28a13f721e76ffbbd386d08eff58f3f6",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^12.0"
+            },
+            "suggest": {
+                "ext-posix": "*"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "8.1-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                }
+            ],
+            "description": "Provides functionality to handle HHVM/PHP environments",
+            "homepage": "https://github.com/sebastianbergmann/environment",
+            "keywords": [
+                "Xdebug",
+                "environment",
+                "hhvm"
+            ],
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/environment/issues",
+                "security": "https://github.com/sebastianbergmann/environment/security/policy",
+                "source": "https://github.com/sebastianbergmann/environment/tree/8.1.0"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                },
+                {
+                    "url": "https://liberapay.com/sebastianbergmann",
+                    "type": "liberapay"
+                },
+                {
+                    "url": "https://thanks.dev/u/gh/sebastianbergmann",
+                    "type": "thanks_dev"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/sebastian/environment",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2026-04-15T12:13:01+00:00"
+        },
+        {
+            "name": "sebastian/exporter",
+            "version": "7.0.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/exporter.git",
+                "reference": "016951ae10980765e4e7aee491eb288c64e505b7"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/016951ae10980765e4e7aee491eb288c64e505b7",
+                "reference": "016951ae10980765e4e7aee491eb288c64e505b7",
+                "shasum": ""
+            },
+            "require": {
+                "ext-mbstring": "*",
+                "php": ">=8.3",
+                "sebastian/recursion-context": "^7.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^12.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "7.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                },
+                {
+                    "name": "Jeff Welch",
+                    "email": "whatthejeff@gmail.com"
+                },
+                {
+                    "name": "Volker Dusch",
+                    "email": "github@wallbash.com"
+                },
+                {
+                    "name": "Adam Harvey",
+                    "email": "aharvey@php.net"
+                },
+                {
+                    "name": "Bernhard Schussek",
+                    "email": "bschussek@gmail.com"
+                }
+            ],
+            "description": "Provides the functionality to export PHP variables for visualization",
+            "homepage": "https://www.github.com/sebastianbergmann/exporter",
+            "keywords": [
+                "export",
+                "exporter"
+            ],
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/exporter/issues",
+                "security": "https://github.com/sebastianbergmann/exporter/security/policy",
+                "source": "https://github.com/sebastianbergmann/exporter/tree/7.0.2"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                },
+                {
+                    "url": "https://liberapay.com/sebastianbergmann",
+                    "type": "liberapay"
+                },
+                {
+                    "url": "https://thanks.dev/u/gh/sebastianbergmann",
+                    "type": "thanks_dev"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2025-09-24T06:16:11+00:00"
+        },
+        {
+            "name": "sebastian/global-state",
+            "version": "8.0.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/global-state.git",
+                "reference": "ef1377171613d09edd25b7816f05be8313f9115d"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/ef1377171613d09edd25b7816f05be8313f9115d",
+                "reference": "ef1377171613d09edd25b7816f05be8313f9115d",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.3",
+                "sebastian/object-reflector": "^5.0",
+                "sebastian/recursion-context": "^7.0"
+            },
+            "require-dev": {
+                "ext-dom": "*",
+                "phpunit/phpunit": "^12.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "8.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                }
+            ],
+            "description": "Snapshotting of global state",
+            "homepage": "https://www.github.com/sebastianbergmann/global-state",
+            "keywords": [
+                "global state"
+            ],
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/global-state/issues",
+                "security": "https://github.com/sebastianbergmann/global-state/security/policy",
+                "source": "https://github.com/sebastianbergmann/global-state/tree/8.0.2"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                },
+                {
+                    "url": "https://liberapay.com/sebastianbergmann",
+                    "type": "liberapay"
+                },
+                {
+                    "url": "https://thanks.dev/u/gh/sebastianbergmann",
+                    "type": "thanks_dev"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/sebastian/global-state",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2025-08-29T11:29:25+00:00"
+        },
+        {
+            "name": "sebastian/lines-of-code",
+            "version": "4.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/lines-of-code.git",
+                "reference": "97ffee3bcfb5805568d6af7f0f893678fc076d2f"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/97ffee3bcfb5805568d6af7f0f893678fc076d2f",
+                "reference": "97ffee3bcfb5805568d6af7f0f893678fc076d2f",
+                "shasum": ""
+            },
+            "require": {
+                "nikic/php-parser": "^5.0",
+                "php": ">=8.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^12.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "4.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "Library for counting the lines of code in PHP source code",
+            "homepage": "https://github.com/sebastianbergmann/lines-of-code",
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/lines-of-code/issues",
+                "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy",
+                "source": "https://github.com/sebastianbergmann/lines-of-code/tree/4.0.0"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2025-02-07T04:57:28+00:00"
+        },
+        {
+            "name": "sebastian/object-enumerator",
+            "version": "7.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/object-enumerator.git",
+                "reference": "1effe8e9b8e068e9ae228e542d5d11b5d16db894"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/1effe8e9b8e068e9ae228e542d5d11b5d16db894",
+                "reference": "1effe8e9b8e068e9ae228e542d5d11b5d16db894",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.3",
+                "sebastian/object-reflector": "^5.0",
+                "sebastian/recursion-context": "^7.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^12.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "7.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                }
+            ],
+            "description": "Traverses array structures and object graphs to enumerate all referenced objects",
+            "homepage": "https://github.com/sebastianbergmann/object-enumerator/",
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/object-enumerator/issues",
+                "security": "https://github.com/sebastianbergmann/object-enumerator/security/policy",
+                "source": "https://github.com/sebastianbergmann/object-enumerator/tree/7.0.0"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2025-02-07T04:57:48+00:00"
+        },
+        {
+            "name": "sebastian/object-reflector",
+            "version": "5.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/object-reflector.git",
+                "reference": "4bfa827c969c98be1e527abd576533293c634f6a"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/4bfa827c969c98be1e527abd576533293c634f6a",
+                "reference": "4bfa827c969c98be1e527abd576533293c634f6a",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^12.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "5.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                }
+            ],
+            "description": "Allows reflection of object attributes, including inherited and non-public ones",
+            "homepage": "https://github.com/sebastianbergmann/object-reflector/",
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/object-reflector/issues",
+                "security": "https://github.com/sebastianbergmann/object-reflector/security/policy",
+                "source": "https://github.com/sebastianbergmann/object-reflector/tree/5.0.0"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2025-02-07T04:58:17+00:00"
+        },
+        {
+            "name": "sebastian/recursion-context",
+            "version": "7.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/recursion-context.git",
+                "reference": "0b01998a7d5b1f122911a66bebcb8d46f0c82d8c"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/0b01998a7d5b1f122911a66bebcb8d46f0c82d8c",
+                "reference": "0b01998a7d5b1f122911a66bebcb8d46f0c82d8c",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^12.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "7.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                },
+                {
+                    "name": "Jeff Welch",
+                    "email": "whatthejeff@gmail.com"
+                },
+                {
+                    "name": "Adam Harvey",
+                    "email": "aharvey@php.net"
+                }
+            ],
+            "description": "Provides functionality to recursively process PHP variables",
+            "homepage": "https://github.com/sebastianbergmann/recursion-context",
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/recursion-context/issues",
+                "security": "https://github.com/sebastianbergmann/recursion-context/security/policy",
+                "source": "https://github.com/sebastianbergmann/recursion-context/tree/7.0.1"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                },
+                {
+                    "url": "https://liberapay.com/sebastianbergmann",
+                    "type": "liberapay"
+                },
+                {
+                    "url": "https://thanks.dev/u/gh/sebastianbergmann",
+                    "type": "thanks_dev"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2025-08-13T04:44:59+00:00"
+        },
+        {
+            "name": "sebastian/type",
+            "version": "6.0.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/type.git",
+                "reference": "e549163b9760b8f71f191651d22acf32d56d6d4d"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/e549163b9760b8f71f191651d22acf32d56d6d4d",
+                "reference": "e549163b9760b8f71f191651d22acf32d56d6d4d",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^12.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "6.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "Collection of value objects that represent the types of the PHP type system",
+            "homepage": "https://github.com/sebastianbergmann/type",
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/type/issues",
+                "security": "https://github.com/sebastianbergmann/type/security/policy",
+                "source": "https://github.com/sebastianbergmann/type/tree/6.0.3"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                },
+                {
+                    "url": "https://liberapay.com/sebastianbergmann",
+                    "type": "liberapay"
+                },
+                {
+                    "url": "https://thanks.dev/u/gh/sebastianbergmann",
+                    "type": "thanks_dev"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/sebastian/type",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2025-08-09T06:57:12+00:00"
+        },
+        {
+            "name": "sebastian/version",
+            "version": "6.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/version.git",
+                "reference": "3e6ccf7657d4f0a59200564b08cead899313b53c"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/3e6ccf7657d4f0a59200564b08cead899313b53c",
+                "reference": "3e6ccf7657d4f0a59200564b08cead899313b53c",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "6.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "Library that helps with managing the version number of Git-hosted PHP projects",
+            "homepage": "https://github.com/sebastianbergmann/version",
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/version/issues",
+                "security": "https://github.com/sebastianbergmann/version/security/policy",
+                "source": "https://github.com/sebastianbergmann/version/tree/6.0.0"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2025-02-07T05:00:38+00:00"
+        },
+        {
+            "name": "staabm/side-effects-detector",
+            "version": "1.0.5",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/staabm/side-effects-detector.git",
+                "reference": "d8334211a140ce329c13726d4a715adbddd0a163"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/staabm/side-effects-detector/zipball/d8334211a140ce329c13726d4a715adbddd0a163",
+                "reference": "d8334211a140ce329c13726d4a715adbddd0a163",
+                "shasum": ""
+            },
+            "require": {
+                "ext-tokenizer": "*",
+                "php": "^7.4 || ^8.0"
+            },
+            "require-dev": {
+                "phpstan/extension-installer": "^1.4.3",
+                "phpstan/phpstan": "^1.12.6",
+                "phpunit/phpunit": "^9.6.21",
+                "symfony/var-dumper": "^5.4.43",
+                "tomasvotruba/type-coverage": "1.0.0",
+                "tomasvotruba/unused-public": "1.0.0"
+            },
+            "type": "library",
+            "autoload": {
+                "classmap": [
+                    "lib/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "A static analysis tool to detect side effects in PHP code",
+            "keywords": [
+                "static analysis"
+            ],
+            "support": {
+                "issues": "https://github.com/staabm/side-effects-detector/issues",
+                "source": "https://github.com/staabm/side-effects-detector/tree/1.0.5"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/staabm",
+                    "type": "github"
+                }
+            ],
+            "time": "2024-10-20T05:08:20+00:00"
+        },
+        {
+            "name": "symfony/yaml",
+            "version": "v8.0.10",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/yaml.git",
+                "reference": "aa9ee60c41d9b20a2468c41ff0a32e2a7405ac05"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/yaml/zipball/aa9ee60c41d9b20a2468c41ff0a32e2a7405ac05",
+                "reference": "aa9ee60c41d9b20a2468c41ff0a32e2a7405ac05",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.4",
+                "symfony/polyfill-ctype": "^1.8"
+            },
+            "conflict": {
+                "symfony/console": "<7.4"
+            },
+            "require-dev": {
+                "symfony/console": "^7.4|^8.0"
+            },
+            "bin": [
+                "Resources/bin/yaml-lint"
+            ],
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\Yaml\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Loads and dumps YAML files",
+            "homepage": "https://symfony.com",
+            "support": {
+                "source": "https://github.com/symfony/yaml/tree/v8.0.10"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/nicolas-grekas",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2026-05-05T08:10:04+00:00"
+        },
+        {
+            "name": "theseer/tokenizer",
+            "version": "2.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/theseer/tokenizer.git",
+                "reference": "7989e43bf381af0eac72e4f0ca5bcbfa81658be4"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/theseer/tokenizer/zipball/7989e43bf381af0eac72e4f0ca5bcbfa81658be4",
+                "reference": "7989e43bf381af0eac72e4f0ca5bcbfa81658be4",
+                "shasum": ""
+            },
+            "require": {
+                "ext-dom": "*",
+                "ext-tokenizer": "*",
+                "ext-xmlwriter": "*",
+                "php": "^8.1"
+            },
+            "type": "library",
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Arne Blankerts",
+                    "email": "arne@blankerts.de",
+                    "role": "Developer"
+                }
+            ],
+            "description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
+            "support": {
+                "issues": "https://github.com/theseer/tokenizer/issues",
+                "source": "https://github.com/theseer/tokenizer/tree/2.0.1"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/theseer",
+                    "type": "github"
+                }
+            ],
+            "time": "2025-12-08T11:19:18+00:00"
+        }
+    ],
+    "aliases": [],
+    "minimum-stability": "stable",
+    "stability-flags": {},
+    "prefer-stable": true,
+    "prefer-lowest": false,
+    "platform": {
+        "php": "^8.3"
+    },
+    "platform-dev": {},
+    "plugin-api-version": "2.9.0"
+}

+ 585 - 0
config/adminlte.php

@@ -0,0 +1,585 @@
+<?php
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | Title
+    |--------------------------------------------------------------------------
+    |
+    | Here you can change the default title of your admin panel.
+    |
+    | For detailed instructions you can look the title section here:
+    | https://github.com/jeroennoten/Laravel-AdminLTE/wiki/Basic-Configuration
+    |
+    */
+
+    'title' => 'AdminLTE 3',
+    'title_prefix' => '',
+    'title_postfix' => '',
+
+    /*
+    |--------------------------------------------------------------------------
+    | Favicon
+    |--------------------------------------------------------------------------
+    |
+    | Here you can activate the favicon.
+    |
+    | For detailed instructions you can look the favicon section here:
+    | https://github.com/jeroennoten/Laravel-AdminLTE/wiki/Basic-Configuration
+    |
+    */
+
+    'use_ico_only' => false,
+    'use_full_favicon' => false,
+
+    /*
+    |--------------------------------------------------------------------------
+    | Google Fonts
+    |--------------------------------------------------------------------------
+    |
+    | Here you can allow or not the use of external google fonts. Disabling the
+    | google fonts may be useful if your admin panel internet access is
+    | restricted somehow.
+    |
+    | For detailed instructions you can look the google fonts section here:
+    | https://github.com/jeroennoten/Laravel-AdminLTE/wiki/Basic-Configuration
+    |
+    */
+
+    'google_fonts' => [
+        'allowed' => true,
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | Admin Panel Logo
+    |--------------------------------------------------------------------------
+    |
+    | Here you can change the logo of your admin panel.
+    |
+    | For detailed instructions you can look the logo section here:
+    | https://github.com/jeroennoten/Laravel-AdminLTE/wiki/Basic-Configuration
+    |
+    */
+
+    'logo' => '<b>Admin</b>LTE',
+    'logo_img' => 'vendor/adminlte/dist/img/AdminLTELogo.png',
+    'logo_img_class' => 'brand-image img-circle elevation-3',
+    'logo_img_xl' => null,
+    'logo_img_xl_class' => 'brand-image-xs',
+    'logo_img_alt' => 'Admin Logo',
+
+    /*
+    |--------------------------------------------------------------------------
+    | Authentication Logo
+    |--------------------------------------------------------------------------
+    |
+    | Here you can setup an alternative logo to use on your login and register
+    | screens. When disabled, the admin panel logo will be used instead.
+    |
+    | For detailed instructions you can look the auth logo section here:
+    | https://github.com/jeroennoten/Laravel-AdminLTE/wiki/Basic-Configuration
+    |
+    */
+
+    'auth_logo' => [
+        'enabled' => false,
+        'img' => [
+            'path' => 'vendor/adminlte/dist/img/AdminLTELogo.png',
+            'alt' => 'Auth Logo',
+            'class' => '',
+            'width' => 50,
+            'height' => 50,
+        ],
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | Preloader Animation
+    |--------------------------------------------------------------------------
+    |
+    | Here you can change the preloader animation configuration. Currently, two
+    | modes are supported: 'fullscreen' for a fullscreen preloader animation
+    | and 'cwrapper' to attach the preloader animation into the content-wrapper
+    | element and avoid overlapping it with the sidebars and the top navbar.
+    |
+    | For detailed instructions you can look the preloader section here:
+    | https://github.com/jeroennoten/Laravel-AdminLTE/wiki/Basic-Configuration
+    |
+    */
+
+    'preloader' => [
+        'enabled' => true,
+        'mode' => 'fullscreen',
+        'img' => [
+            'path' => 'vendor/adminlte/dist/img/AdminLTELogo.png',
+            'alt' => 'AdminLTE Preloader Image',
+            'effect' => 'animation__shake',
+            'width' => 60,
+            'height' => 60,
+        ],
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | User Menu
+    |--------------------------------------------------------------------------
+    |
+    | Here you can activate and change the user menu.
+    |
+    | For detailed instructions you can look the user menu section here:
+    | https://github.com/jeroennoten/Laravel-AdminLTE/wiki/Basic-Configuration
+    |
+    */
+
+    'usermenu_enabled' => true,
+    'usermenu_header' => false,
+    'usermenu_header_class' => 'bg-primary',
+    'usermenu_image' => false,
+    'usermenu_desc' => false,
+    'usermenu_profile_url' => false,
+
+    /*
+    |--------------------------------------------------------------------------
+    | Layout
+    |--------------------------------------------------------------------------
+    |
+    | Here we change the layout of your admin panel.
+    |
+    | For detailed instructions you can look the layout section here:
+    | https://github.com/jeroennoten/Laravel-AdminLTE/wiki/Layout-and-Styling-Configuration
+    |
+    */
+
+    'layout_topnav' => null,
+    'layout_boxed' => null,
+    'layout_fixed_sidebar' => null,
+    'layout_fixed_navbar' => null,
+    'layout_fixed_footer' => null,
+    'layout_dark_mode' => null,
+
+    /*
+    |--------------------------------------------------------------------------
+    | Authentication Views Classes
+    |--------------------------------------------------------------------------
+    |
+    | Here you can change the look and behavior of the authentication views.
+    |
+    | For detailed instructions you can look the auth classes section here:
+    | https://github.com/jeroennoten/Laravel-AdminLTE/wiki/Layout-and-Styling-Configuration
+    |
+    */
+
+    'classes_auth_card' => 'card-outline card-primary',
+    'classes_auth_header' => '',
+    'classes_auth_body' => '',
+    'classes_auth_footer' => '',
+    'classes_auth_icon' => '',
+    'classes_auth_btn' => 'btn-flat btn-primary',
+
+    /*
+    |--------------------------------------------------------------------------
+    | Admin Panel Classes
+    |--------------------------------------------------------------------------
+    |
+    | Here you can change the look and behavior of the admin panel.
+    |
+    | For detailed instructions you can look the admin panel classes here:
+    | https://github.com/jeroennoten/Laravel-AdminLTE/wiki/Layout-and-Styling-Configuration
+    |
+    */
+
+    'classes_body' => '',
+    'classes_brand' => '',
+    'classes_brand_text' => '',
+    'classes_content_wrapper' => '',
+    'classes_content_header' => '',
+    'classes_content' => '',
+    'classes_sidebar' => 'sidebar-dark-primary elevation-4',
+    'classes_sidebar_nav' => '',
+    'classes_topnav' => 'navbar-white navbar-light',
+    'classes_topnav_nav' => 'navbar-expand',
+    'classes_topnav_container' => 'container',
+
+    /*
+    |--------------------------------------------------------------------------
+    | Sidebar
+    |--------------------------------------------------------------------------
+    |
+    | Here we can modify the sidebar of the admin panel.
+    |
+    | For detailed instructions you can look the sidebar section here:
+    | https://github.com/jeroennoten/Laravel-AdminLTE/wiki/Layout-and-Styling-Configuration
+    |
+    */
+
+    'sidebar_mini' => 'lg',
+    'sidebar_collapse' => false,
+    'sidebar_collapse_auto_size' => false,
+    'sidebar_collapse_remember' => false,
+    'sidebar_collapse_remember_no_transition' => true,
+    'sidebar_scrollbar_theme' => 'os-theme-light',
+    'sidebar_scrollbar_auto_hide' => 'l',
+    'sidebar_nav_accordion' => true,
+    'sidebar_nav_animation_speed' => 300,
+
+    /*
+    |--------------------------------------------------------------------------
+    | Control Sidebar (Right Sidebar)
+    |--------------------------------------------------------------------------
+    |
+    | Here we can modify the right sidebar aka control sidebar of the admin panel.
+    |
+    | For detailed instructions you can look the right sidebar section here:
+    | https://github.com/jeroennoten/Laravel-AdminLTE/wiki/Layout-and-Styling-Configuration
+    |
+    */
+
+    'right_sidebar' => false,
+    'right_sidebar_icon' => 'fas fa-cogs',
+    'right_sidebar_theme' => 'dark',
+    'right_sidebar_slide' => true,
+    'right_sidebar_push' => true,
+    'right_sidebar_scrollbar_theme' => 'os-theme-light',
+    'right_sidebar_scrollbar_auto_hide' => 'l',
+
+    /*
+    |--------------------------------------------------------------------------
+    | URLs
+    |--------------------------------------------------------------------------
+    |
+    | Here we can modify the url settings of the admin panel.
+    |
+    | For detailed instructions you can look the urls section here:
+    | https://github.com/jeroennoten/Laravel-AdminLTE/wiki/Basic-Configuration
+    |
+    */
+
+    'use_route_url' => false,
+    'dashboard_url' => 'admin',
+    'logout_url' => 'admin/logout',
+    'login_url' => 'admin/login',
+    'register_url' => 'register',
+    'password_reset_url' => 'password/reset',
+    'password_email_url' => 'password/email',
+    'profile_url' => false,
+    'disable_darkmode_routes' => false,
+
+    /*
+    |--------------------------------------------------------------------------
+    | Laravel Asset Bundling
+    |--------------------------------------------------------------------------
+    |
+    | Here we can enable the Laravel Asset Bundling option for the admin panel.
+    | Currently, the next modes are supported: 'mix', 'vite' and 'vite_js_only'.
+    | When using 'vite_js_only', it's expected that your CSS is imported using
+    | JavaScript. Typically, in your application's 'resources/js/app.js' file.
+    | If you are not using any of these, leave it as 'false'.
+    |
+    | For detailed instructions you can look the asset bundling section here:
+    | https://github.com/jeroennoten/Laravel-AdminLTE/wiki/Other-Configuration
+    |
+    */
+
+    'laravel_asset_bundling' => false,
+    'laravel_css_path' => 'css/app.css',
+    'laravel_js_path' => 'js/app.js',
+
+    /*
+    |--------------------------------------------------------------------------
+    | Menu Items
+    |--------------------------------------------------------------------------
+    |
+    | Here we can modify the sidebar/top navigation of the admin panel.
+    |
+    | For detailed instructions you can look here:
+    | https://github.com/jeroennoten/Laravel-AdminLTE/wiki/Menu-Configuration
+    |
+    */
+
+    'menu' => [
+        // Navbar items (правый верхний угол):
+        [
+            'type'         => 'fullscreen-widget',
+            'topnav_right' => true,
+        ],
+        // Sidebar items:
+        [
+            'type' => 'sidebar-menu-search',
+            'text' => 'search',
+        ],
+
+        ['header' => 'УПРАВЛЕНИЕ'],
+        [
+            'text'        => 'Дашборд',
+            'url'         => 'admin',
+            'icon'        => 'fas fa-fw fa-tachometer-alt',
+            'active'      => ['admin'],
+        ],
+
+        ['header' => 'АВТОМОБИЛИ'],
+        [
+            'text'    => 'Автомобили',
+            'icon'    => 'fas fa-fw fa-car',
+            'can'     => 'cars.view',
+            'submenu' => [
+                [
+                    'text' => 'Все автомобили',
+                    'url'  => 'admin/cars',
+                    'icon' => 'fas fa-fw fa-list',
+                    'can'  => 'cars.view',
+                ],
+                [
+                    'text' => 'Добавить',
+                    'url'  => 'admin/cars/create',
+                    'icon' => 'fas fa-fw fa-plus',
+                    'can'  => 'cars.edit',
+                ],
+            ],
+        ],
+        [
+            'text' => 'Справочники',
+            'url'  => 'admin/manuals',
+            'icon' => 'fas fa-fw fa-book',
+            'can'  => 'manuals.view',
+        ],
+
+        ['header' => 'КОНТЕНТ САЙТА'],
+        [
+            'text' => 'Страницы',
+            'url'  => 'admin/pages',
+            'icon' => 'fas fa-fw fa-file-alt',
+            'can'  => 'pages.view',
+        ],
+        [
+            'text' => 'Блоки',
+            'url'  => 'admin/blocks',
+            'icon' => 'fas fa-fw fa-th-large',
+            'can'  => 'blocks.view',
+        ],
+        [
+            'text' => 'Услуги',
+            'url'  => 'admin/services',
+            'icon' => 'fas fa-fw fa-concierge-bell',
+            'can'  => 'blocks.view',
+        ],
+        [
+            'text' => 'Отзывы',
+            'url'  => 'admin/reviews',
+            'icon' => 'fas fa-fw fa-star',
+            'can'  => 'blocks.view',
+        ],
+
+        ['header' => 'ЗАЯВКИ'],
+        [
+            'text'    => 'Заявки и формы',
+            'icon'    => 'fas fa-fw fa-paper-plane',
+            'can'     => 'settings.view',
+            'submenu' => [
+                [
+                    'text' => 'Веб-формы',
+                    'url'  => 'admin/forms',
+                    'icon' => 'fas fa-fw fa-list-alt',
+                    'can'  => 'settings.view',
+                ],
+                [
+                    'text' => 'Заявки',
+                    'url'  => 'admin/leads',
+                    'icon' => 'fas fa-fw fa-inbox',
+                    'can'  => 'settings.view',
+                ],
+            ],
+        ],
+
+        ['header' => 'СИСТЕМА'],
+        [
+            'text' => 'Настройки',
+            'url'  => 'admin/settings',
+            'icon' => 'fas fa-fw fa-cog',
+            'can'  => 'settings.view',
+        ],
+        [
+            'text' => 'Пользователи',
+            'url'  => 'admin/users',
+            'icon' => 'fas fa-fw fa-users',
+            'can'  => 'users.view',
+        ],
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | Menu Filters
+    |--------------------------------------------------------------------------
+    |
+    | Here we can modify the menu filters of the admin panel.
+    |
+    | For detailed instructions you can look the menu filters section here:
+    | https://github.com/jeroennoten/Laravel-AdminLTE/wiki/Menu-Configuration
+    |
+    */
+
+    'filters' => [
+        JeroenNoten\LaravelAdminLte\Menu\Filters\GateFilter::class,
+        JeroenNoten\LaravelAdminLte\Menu\Filters\HrefFilter::class,
+        JeroenNoten\LaravelAdminLte\Menu\Filters\SearchFilter::class,
+        JeroenNoten\LaravelAdminLte\Menu\Filters\ActiveFilter::class,
+        JeroenNoten\LaravelAdminLte\Menu\Filters\ClassesFilter::class,
+        JeroenNoten\LaravelAdminLte\Menu\Filters\LangFilter::class,
+        JeroenNoten\LaravelAdminLte\Menu\Filters\DataFilter::class,
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | Plugins Initialization
+    |--------------------------------------------------------------------------
+    |
+    | Here we can modify the plugins used inside the admin panel.
+    |
+    | For detailed instructions you can look the plugins section here:
+    | https://github.com/jeroennoten/Laravel-AdminLTE/wiki/Plugins-Configuration
+    |
+    */
+
+    'plugins' => [
+        'Datatables' => [
+            'active' => false,
+            'files' => [
+                [
+                    'type' => 'js',
+                    'asset' => false,
+                    'location' => '//cdn.datatables.net/1.10.19/js/jquery.dataTables.min.js',
+                ],
+                [
+                    'type' => 'js',
+                    'asset' => false,
+                    'location' => '//cdn.datatables.net/1.10.19/js/dataTables.bootstrap4.min.js',
+                ],
+                [
+                    'type' => 'css',
+                    'asset' => false,
+                    'location' => '//cdn.datatables.net/1.10.19/css/dataTables.bootstrap4.min.css',
+                ],
+            ],
+        ],
+        'Select2' => [
+            'active' => false,
+            'files' => [
+                [
+                    'type' => 'js',
+                    'asset' => false,
+                    'location' => '//cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/js/select2.min.js',
+                ],
+                [
+                    'type' => 'css',
+                    'asset' => false,
+                    'location' => '//cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/css/select2.css',
+                ],
+            ],
+        ],
+        'Chartjs' => [
+            'active' => false,
+            'files' => [
+                [
+                    'type' => 'js',
+                    'asset' => false,
+                    'location' => '//cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.0/Chart.bundle.min.js',
+                ],
+            ],
+        ],
+        'Sweetalert2' => [
+            'active' => false,
+            'files' => [
+                [
+                    'type' => 'js',
+                    'asset' => false,
+                    'location' => '//cdn.jsdelivr.net/npm/sweetalert2@8',
+                ],
+            ],
+        ],
+        'Pace' => [
+            'active' => false,
+            'files' => [
+                [
+                    'type' => 'css',
+                    'asset' => false,
+                    'location' => '//cdnjs.cloudflare.com/ajax/libs/pace/1.0.2/themes/blue/pace-theme-center-radar.min.css',
+                ],
+                [
+                    'type' => 'js',
+                    'asset' => false,
+                    'location' => '//cdnjs.cloudflare.com/ajax/libs/pace/1.0.2/pace.min.js',
+                ],
+            ],
+        ],
+
+        // Summernote — WYSIWYG-редактор для Bootstrap 4 (с русской локализацией)
+        'Summernote' => [
+            'active' => false,
+            'files' => [
+                [
+                    'type' => 'css',
+                    'asset' => false,
+                    'location' => 'https://cdn.jsdelivr.net/npm/summernote@0.8.20/dist/summernote-bs4.min.css',
+                ],
+                [
+                    'type' => 'js',
+                    'asset' => false,
+                    'location' => 'https://cdn.jsdelivr.net/npm/summernote@0.8.20/dist/summernote-bs4.min.js',
+                ],
+                [
+                    'type' => 'js',
+                    'asset' => false,
+                    'location' => 'https://cdn.jsdelivr.net/npm/summernote@0.8.20/dist/lang/summernote-ru-RU.min.js',
+                ],
+            ],
+        ],
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | IFrame
+    |--------------------------------------------------------------------------
+    |
+    | Here we change the IFrame mode configuration. Note these changes will
+    | only apply to the view that extends and enable the IFrame mode.
+    |
+    | For detailed instructions you can look the iframe mode section here:
+    | https://github.com/jeroennoten/Laravel-AdminLTE/wiki/IFrame-Mode-Configuration
+    |
+    */
+
+    'iframe' => [
+        'default_tab' => [
+            'url' => null,
+            'title' => null,
+        ],
+        'buttons' => [
+            'close' => true,
+            'close_all' => true,
+            'close_all_other' => true,
+            'scroll_left' => true,
+            'scroll_right' => true,
+            'fullscreen' => true,
+        ],
+        'options' => [
+            'loading_screen' => 1000,
+            'auto_show_new_tab' => true,
+            'use_navbar_items' => true,
+        ],
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | Livewire
+    |--------------------------------------------------------------------------
+    |
+    | Here we can enable the Livewire support.
+    |
+    | For detailed instructions you can look the livewire here:
+    | https://github.com/jeroennoten/Laravel-AdminLTE/wiki/Other-Configuration
+    |
+    */
+
+    'livewire' => false,
+];

+ 126 - 0
config/app.php

@@ -0,0 +1,126 @@
+<?php
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | Application Name
+    |--------------------------------------------------------------------------
+    |
+    | This value is the name of your application, which will be used when the
+    | framework needs to place the application's name in a notification or
+    | other UI elements where an application name needs to be displayed.
+    |
+    */
+
+    'name' => env('APP_NAME', 'Laravel'),
+
+    /*
+    |--------------------------------------------------------------------------
+    | Application Environment
+    |--------------------------------------------------------------------------
+    |
+    | This value determines the "environment" your application is currently
+    | running in. This may determine how you prefer to configure various
+    | services the application utilizes. Set this in your ".env" file.
+    |
+    */
+
+    'env' => env('APP_ENV', 'production'),
+
+    /*
+    |--------------------------------------------------------------------------
+    | Application Debug Mode
+    |--------------------------------------------------------------------------
+    |
+    | When your application is in debug mode, detailed error messages with
+    | stack traces will be shown on every error that occurs within your
+    | application. If disabled, a simple generic error page is shown.
+    |
+    */
+
+    'debug' => (bool) env('APP_DEBUG', false),
+
+    /*
+    |--------------------------------------------------------------------------
+    | Application URL
+    |--------------------------------------------------------------------------
+    |
+    | This URL is used by the console to properly generate URLs when using
+    | the Artisan command line tool. You should set this to the root of
+    | the application so that it's available within Artisan commands.
+    |
+    */
+
+    'url' => env('APP_URL', 'http://localhost'),
+
+    /*
+    |--------------------------------------------------------------------------
+    | Application Timezone
+    |--------------------------------------------------------------------------
+    |
+    | Here you may specify the default timezone for your application, which
+    | will be used by the PHP date and date-time functions. The timezone
+    | is set to "UTC" by default as it is suitable for most use cases.
+    |
+    */
+
+    'timezone' => 'UTC',
+
+    /*
+    |--------------------------------------------------------------------------
+    | Application Locale Configuration
+    |--------------------------------------------------------------------------
+    |
+    | The application locale determines the default locale that will be used
+    | by Laravel's translation / localization methods. This option can be
+    | set to any locale for which you plan to have translation strings.
+    |
+    */
+
+    'locale' => env('APP_LOCALE', 'en'),
+
+    'fallback_locale' => env('APP_FALLBACK_LOCALE', 'en'),
+
+    'faker_locale' => env('APP_FAKER_LOCALE', 'en_US'),
+
+    /*
+    |--------------------------------------------------------------------------
+    | Encryption Key
+    |--------------------------------------------------------------------------
+    |
+    | This key is utilized by Laravel's encryption services and should be set
+    | to a random, 32 character string to ensure that all encrypted values
+    | are secure. You should do this prior to deploying the application.
+    |
+    */
+
+    'cipher' => 'AES-256-CBC',
+
+    'key' => env('APP_KEY'),
+
+    'previous_keys' => [
+        ...array_filter(
+            explode(',', (string) env('APP_PREVIOUS_KEYS', ''))
+        ),
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | Maintenance Mode Driver
+    |--------------------------------------------------------------------------
+    |
+    | These configuration options determine the driver used to determine and
+    | manage Laravel's "maintenance mode" status. The "cache" driver will
+    | allow maintenance mode to be controlled across multiple machines.
+    |
+    | Supported drivers: "file", "cache"
+    |
+    */
+
+    'maintenance' => [
+        'driver' => env('APP_MAINTENANCE_DRIVER', 'file'),
+        'store' => env('APP_MAINTENANCE_STORE', 'database'),
+    ],
+
+];

+ 117 - 0
config/auth.php

@@ -0,0 +1,117 @@
+<?php
+
+use App\Models\User;
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | Authentication Defaults
+    |--------------------------------------------------------------------------
+    |
+    | This option defines the default authentication "guard" and password
+    | reset "broker" for your application. You may change these values
+    | as required, but they're a perfect start for most applications.
+    |
+    */
+
+    'defaults' => [
+        'guard' => env('AUTH_GUARD', 'web'),
+        'passwords' => env('AUTH_PASSWORD_BROKER', 'users'),
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | Authentication Guards
+    |--------------------------------------------------------------------------
+    |
+    | Next, you may define every authentication guard for your application.
+    | Of course, a great default configuration has been defined for you
+    | which utilizes session storage plus the Eloquent user provider.
+    |
+    | All authentication guards have a user provider, which defines how the
+    | users are actually retrieved out of your database or other storage
+    | system used by the application. Typically, Eloquent is utilized.
+    |
+    | Supported: "session"
+    |
+    */
+
+    'guards' => [
+        'web' => [
+            'driver' => 'session',
+            'provider' => 'users',
+        ],
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | User Providers
+    |--------------------------------------------------------------------------
+    |
+    | All authentication guards have a user provider, which defines how the
+    | users are actually retrieved out of your database or other storage
+    | system used by the application. Typically, Eloquent is utilized.
+    |
+    | If you have multiple user tables or models you may configure multiple
+    | providers to represent the model / table. These providers may then
+    | be assigned to any extra authentication guards you have defined.
+    |
+    | Supported: "database", "eloquent"
+    |
+    */
+
+    'providers' => [
+        'users' => [
+            'driver' => 'eloquent',
+            'model' => env('AUTH_MODEL', User::class),
+        ],
+
+        // 'users' => [
+        //     'driver' => 'database',
+        //     'table' => 'users',
+        // ],
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | Resetting Passwords
+    |--------------------------------------------------------------------------
+    |
+    | These configuration options specify the behavior of Laravel's password
+    | reset functionality, including the table utilized for token storage
+    | and the user provider that is invoked to actually retrieve users.
+    |
+    | The expiry time is the number of minutes that each reset token will be
+    | considered valid. This security feature keeps tokens short-lived so
+    | they have less time to be guessed. You may change this as needed.
+    |
+    | The throttle setting is the number of seconds a user must wait before
+    | generating more password reset tokens. This prevents the user from
+    | quickly generating a very large amount of password reset tokens.
+    |
+    */
+
+    'passwords' => [
+        'users' => [
+            'provider' => 'users',
+            'table' => env('AUTH_PASSWORD_RESET_TOKEN_TABLE', 'password_reset_tokens'),
+            'expire' => 60,
+            'throttle' => 60,
+        ],
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | Password Confirmation Timeout
+    |--------------------------------------------------------------------------
+    |
+    | Here you may define the number of seconds before a password confirmation
+    | window expires and users are asked to re-enter their password via the
+    | confirmation screen. By default, the timeout lasts for three hours.
+    |
+    */
+
+    'password_timeout' => env('AUTH_PASSWORD_TIMEOUT', 10800),
+
+];

+ 130 - 0
config/cache.php

@@ -0,0 +1,130 @@
+<?php
+
+use Illuminate\Support\Str;
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | Default Cache Store
+    |--------------------------------------------------------------------------
+    |
+    | This option controls the default cache store that will be used by the
+    | framework. This connection is utilized if another isn't explicitly
+    | specified when running a cache operation inside the application.
+    |
+    */
+
+    'default' => env('CACHE_STORE', 'database'),
+
+    /*
+    |--------------------------------------------------------------------------
+    | Cache Stores
+    |--------------------------------------------------------------------------
+    |
+    | Here you may define all of the cache "stores" for your application as
+    | well as their drivers. You may even define multiple stores for the
+    | same cache driver to group types of items stored in your caches.
+    |
+    | Supported drivers: "array", "database", "file", "memcached",
+    |                    "redis", "dynamodb", "octane",
+    |                    "failover", "null"
+    |
+    */
+
+    'stores' => [
+
+        'array' => [
+            'driver' => 'array',
+            'serialize' => false,
+        ],
+
+        'database' => [
+            'driver' => 'database',
+            'connection' => env('DB_CACHE_CONNECTION'),
+            'table' => env('DB_CACHE_TABLE', 'cache'),
+            'lock_connection' => env('DB_CACHE_LOCK_CONNECTION'),
+            'lock_table' => env('DB_CACHE_LOCK_TABLE'),
+        ],
+
+        'file' => [
+            'driver' => 'file',
+            'path' => storage_path('framework/cache/data'),
+            'lock_path' => storage_path('framework/cache/data'),
+        ],
+
+        'memcached' => [
+            'driver' => 'memcached',
+            'persistent_id' => env('MEMCACHED_PERSISTENT_ID'),
+            'sasl' => [
+                env('MEMCACHED_USERNAME'),
+                env('MEMCACHED_PASSWORD'),
+            ],
+            'options' => [
+                // Memcached::OPT_CONNECT_TIMEOUT => 2000,
+            ],
+            'servers' => [
+                [
+                    'host' => env('MEMCACHED_HOST', '127.0.0.1'),
+                    'port' => env('MEMCACHED_PORT', 11211),
+                    'weight' => 100,
+                ],
+            ],
+        ],
+
+        'redis' => [
+            'driver' => 'redis',
+            'connection' => env('REDIS_CACHE_CONNECTION', 'cache'),
+            'lock_connection' => env('REDIS_CACHE_LOCK_CONNECTION', 'default'),
+        ],
+
+        'dynamodb' => [
+            'driver' => 'dynamodb',
+            'key' => env('AWS_ACCESS_KEY_ID'),
+            'secret' => env('AWS_SECRET_ACCESS_KEY'),
+            'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
+            'table' => env('DYNAMODB_CACHE_TABLE', 'cache'),
+            'endpoint' => env('DYNAMODB_ENDPOINT'),
+        ],
+
+        'octane' => [
+            'driver' => 'octane',
+        ],
+
+        'failover' => [
+            'driver' => 'failover',
+            'stores' => [
+                'database',
+                'array',
+            ],
+        ],
+
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | Cache Key Prefix
+    |--------------------------------------------------------------------------
+    |
+    | When utilizing the APC, database, memcached, Redis, and DynamoDB cache
+    | stores, there might be other applications using the same cache. For
+    | that reason, you may prefix every cache key to avoid collisions.
+    |
+    */
+
+    'prefix' => env('CACHE_PREFIX', Str::slug((string) env('APP_NAME', 'laravel')).'-cache-'),
+
+    /*
+    |--------------------------------------------------------------------------
+    | Serializable Classes
+    |--------------------------------------------------------------------------
+    |
+    | This value determines the classes that can be unserialized from cache
+    | storage. By default, no PHP classes will be unserialized from your
+    | cache to prevent gadget chain attacks if your APP_KEY is leaked.
+    |
+    */
+
+    'serializable_classes' => false,
+
+];

+ 184 - 0
config/database.php

@@ -0,0 +1,184 @@
+<?php
+
+use Illuminate\Support\Str;
+use Pdo\Mysql;
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | Default Database Connection Name
+    |--------------------------------------------------------------------------
+    |
+    | Here you may specify which of the database connections below you wish
+    | to use as your default connection for database operations. This is
+    | the connection which will be utilized unless another connection
+    | is explicitly specified when you execute a query / statement.
+    |
+    */
+
+    'default' => env('DB_CONNECTION', 'sqlite'),
+
+    /*
+    |--------------------------------------------------------------------------
+    | Database Connections
+    |--------------------------------------------------------------------------
+    |
+    | Below are all of the database connections defined for your application.
+    | An example configuration is provided for each database system which
+    | is supported by Laravel. You're free to add / remove connections.
+    |
+    */
+
+    'connections' => [
+
+        'sqlite' => [
+            'driver' => 'sqlite',
+            'url' => env('DB_URL'),
+            'database' => env('DB_DATABASE', database_path('database.sqlite')),
+            'prefix' => '',
+            'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true),
+            'busy_timeout' => null,
+            'journal_mode' => null,
+            'synchronous' => null,
+            'transaction_mode' => 'DEFERRED',
+        ],
+
+        'mysql' => [
+            'driver' => 'mysql',
+            'url' => env('DB_URL'),
+            'host' => env('DB_HOST', '127.0.0.1'),
+            'port' => env('DB_PORT', '3306'),
+            'database' => env('DB_DATABASE', 'laravel'),
+            'username' => env('DB_USERNAME', 'root'),
+            'password' => env('DB_PASSWORD', ''),
+            'unix_socket' => env('DB_SOCKET', ''),
+            'charset' => env('DB_CHARSET', 'utf8mb4'),
+            'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'),
+            'prefix' => '',
+            'prefix_indexes' => true,
+            'strict' => true,
+            'engine' => null,
+            'options' => extension_loaded('pdo_mysql') ? array_filter([
+                (PHP_VERSION_ID >= 80500 ? Mysql::ATTR_SSL_CA : PDO::MYSQL_ATTR_SSL_CA) => env('MYSQL_ATTR_SSL_CA'),
+            ]) : [],
+        ],
+
+        'mariadb' => [
+            'driver' => 'mariadb',
+            'url' => env('DB_URL'),
+            'host' => env('DB_HOST', '127.0.0.1'),
+            'port' => env('DB_PORT', '3306'),
+            'database' => env('DB_DATABASE', 'laravel'),
+            'username' => env('DB_USERNAME', 'root'),
+            'password' => env('DB_PASSWORD', ''),
+            'unix_socket' => env('DB_SOCKET', ''),
+            'charset' => env('DB_CHARSET', 'utf8mb4'),
+            'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'),
+            'prefix' => '',
+            'prefix_indexes' => true,
+            'strict' => true,
+            'engine' => null,
+            'options' => extension_loaded('pdo_mysql') ? array_filter([
+                (PHP_VERSION_ID >= 80500 ? Mysql::ATTR_SSL_CA : PDO::MYSQL_ATTR_SSL_CA) => env('MYSQL_ATTR_SSL_CA'),
+            ]) : [],
+        ],
+
+        'pgsql' => [
+            'driver' => 'pgsql',
+            'url' => env('DB_URL'),
+            'host' => env('DB_HOST', '127.0.0.1'),
+            'port' => env('DB_PORT', '5432'),
+            'database' => env('DB_DATABASE', 'laravel'),
+            'username' => env('DB_USERNAME', 'root'),
+            'password' => env('DB_PASSWORD', ''),
+            'charset' => env('DB_CHARSET', 'utf8'),
+            'prefix' => '',
+            'prefix_indexes' => true,
+            'search_path' => 'public',
+            'sslmode' => env('DB_SSLMODE', 'prefer'),
+        ],
+
+        'sqlsrv' => [
+            'driver' => 'sqlsrv',
+            'url' => env('DB_URL'),
+            'host' => env('DB_HOST', 'localhost'),
+            'port' => env('DB_PORT', '1433'),
+            'database' => env('DB_DATABASE', 'laravel'),
+            'username' => env('DB_USERNAME', 'root'),
+            'password' => env('DB_PASSWORD', ''),
+            'charset' => env('DB_CHARSET', 'utf8'),
+            'prefix' => '',
+            'prefix_indexes' => true,
+            // 'encrypt' => env('DB_ENCRYPT', 'yes'),
+            // 'trust_server_certificate' => env('DB_TRUST_SERVER_CERTIFICATE', 'false'),
+        ],
+
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | Migration Repository Table
+    |--------------------------------------------------------------------------
+    |
+    | This table keeps track of all the migrations that have already run for
+    | your application. Using this information, we can determine which of
+    | the migrations on disk haven't actually been run on the database.
+    |
+    */
+
+    'migrations' => [
+        'table' => 'migrations',
+        'update_date_on_publish' => true,
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | Redis Databases
+    |--------------------------------------------------------------------------
+    |
+    | Redis is an open source, fast, and advanced key-value store that also
+    | provides a richer body of commands than a typical key-value system
+    | such as Memcached. You may define your connection settings here.
+    |
+    */
+
+    'redis' => [
+
+        'client' => env('REDIS_CLIENT', 'phpredis'),
+
+        'options' => [
+            'cluster' => env('REDIS_CLUSTER', 'redis'),
+            'prefix' => env('REDIS_PREFIX', Str::slug((string) env('APP_NAME', 'laravel')).'-database-'),
+            'persistent' => env('REDIS_PERSISTENT', false),
+        ],
+
+        'default' => [
+            'url' => env('REDIS_URL'),
+            'host' => env('REDIS_HOST', '127.0.0.1'),
+            'username' => env('REDIS_USERNAME'),
+            'password' => env('REDIS_PASSWORD'),
+            'port' => env('REDIS_PORT', '6379'),
+            'database' => env('REDIS_DB', '0'),
+            'max_retries' => env('REDIS_MAX_RETRIES', 3),
+            'backoff_algorithm' => env('REDIS_BACKOFF_ALGORITHM', 'decorrelated_jitter'),
+            'backoff_base' => env('REDIS_BACKOFF_BASE', 100),
+            'backoff_cap' => env('REDIS_BACKOFF_CAP', 1000),
+        ],
+
+        'cache' => [
+            'url' => env('REDIS_URL'),
+            'host' => env('REDIS_HOST', '127.0.0.1'),
+            'username' => env('REDIS_USERNAME'),
+            'password' => env('REDIS_PASSWORD'),
+            'port' => env('REDIS_PORT', '6379'),
+            'database' => env('REDIS_CACHE_DB', '1'),
+            'max_retries' => env('REDIS_MAX_RETRIES', 3),
+            'backoff_algorithm' => env('REDIS_BACKOFF_ALGORITHM', 'decorrelated_jitter'),
+            'backoff_base' => env('REDIS_BACKOFF_BASE', 100),
+            'backoff_cap' => env('REDIS_BACKOFF_CAP', 1000),
+        ],
+
+    ],
+
+];

+ 80 - 0
config/filesystems.php

@@ -0,0 +1,80 @@
+<?php
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | Default Filesystem Disk
+    |--------------------------------------------------------------------------
+    |
+    | Here you may specify the default filesystem disk that should be used
+    | by the framework. The "local" disk, as well as a variety of cloud
+    | based disks are available to your application for file storage.
+    |
+    */
+
+    'default' => env('FILESYSTEM_DISK', 'local'),
+
+    /*
+    |--------------------------------------------------------------------------
+    | Filesystem Disks
+    |--------------------------------------------------------------------------
+    |
+    | Below you may configure as many filesystem disks as necessary, and you
+    | may even configure multiple disks for the same driver. Examples for
+    | most supported storage drivers are configured here for reference.
+    |
+    | Supported drivers: "local", "ftp", "sftp", "s3"
+    |
+    */
+
+    'disks' => [
+
+        'local' => [
+            'driver' => 'local',
+            'root' => storage_path('app/private'),
+            'serve' => true,
+            'throw' => false,
+            'report' => false,
+        ],
+
+        'public' => [
+            'driver' => 'local',
+            'root' => storage_path('app/public'),
+            'url' => rtrim(env('APP_URL', 'http://localhost'), '/').'/storage',
+            'visibility' => 'public',
+            'throw' => false,
+            'report' => false,
+        ],
+
+        's3' => [
+            'driver' => 's3',
+            'key' => env('AWS_ACCESS_KEY_ID'),
+            'secret' => env('AWS_SECRET_ACCESS_KEY'),
+            'region' => env('AWS_DEFAULT_REGION'),
+            'bucket' => env('AWS_BUCKET'),
+            'url' => env('AWS_URL'),
+            'endpoint' => env('AWS_ENDPOINT'),
+            'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false),
+            'throw' => false,
+            'report' => false,
+        ],
+
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | Symbolic Links
+    |--------------------------------------------------------------------------
+    |
+    | Here you may configure the symbolic links that will be created when the
+    | `storage:link` Artisan command is executed. The array keys should be
+    | the locations of the links and the values should be their targets.
+    |
+    */
+
+    'links' => [
+        public_path('storage') => storage_path('app/public'),
+    ],
+
+];

+ 132 - 0
config/logging.php

@@ -0,0 +1,132 @@
+<?php
+
+use Monolog\Handler\NullHandler;
+use Monolog\Handler\StreamHandler;
+use Monolog\Handler\SyslogUdpHandler;
+use Monolog\Processor\PsrLogMessageProcessor;
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | Default Log Channel
+    |--------------------------------------------------------------------------
+    |
+    | This option defines the default log channel that is utilized to write
+    | messages to your logs. The value provided here should match one of
+    | the channels present in the list of "channels" configured below.
+    |
+    */
+
+    'default' => env('LOG_CHANNEL', 'stack'),
+
+    /*
+    |--------------------------------------------------------------------------
+    | Deprecations Log Channel
+    |--------------------------------------------------------------------------
+    |
+    | This option controls the log channel that should be used to log warnings
+    | regarding deprecated PHP and library features. This allows you to get
+    | your application ready for upcoming major versions of dependencies.
+    |
+    */
+
+    'deprecations' => [
+        'channel' => env('LOG_DEPRECATIONS_CHANNEL', 'null'),
+        'trace' => env('LOG_DEPRECATIONS_TRACE', false),
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | Log Channels
+    |--------------------------------------------------------------------------
+    |
+    | Here you may configure the log channels for your application. Laravel
+    | utilizes the Monolog PHP logging library, which includes a variety
+    | of powerful log handlers and formatters that you're free to use.
+    |
+    | Available drivers: "single", "daily", "slack", "syslog",
+    |                    "errorlog", "monolog", "custom", "stack"
+    |
+    */
+
+    'channels' => [
+
+        'stack' => [
+            'driver' => 'stack',
+            'channels' => explode(',', (string) env('LOG_STACK', 'single')),
+            'ignore_exceptions' => false,
+        ],
+
+        'single' => [
+            'driver' => 'single',
+            'path' => storage_path('logs/laravel.log'),
+            'level' => env('LOG_LEVEL', 'debug'),
+            'replace_placeholders' => true,
+        ],
+
+        'daily' => [
+            'driver' => 'daily',
+            'path' => storage_path('logs/laravel.log'),
+            'level' => env('LOG_LEVEL', 'debug'),
+            'days' => env('LOG_DAILY_DAYS', 14),
+            'replace_placeholders' => true,
+        ],
+
+        'slack' => [
+            'driver' => 'slack',
+            'url' => env('LOG_SLACK_WEBHOOK_URL'),
+            'username' => env('LOG_SLACK_USERNAME', env('APP_NAME', 'Laravel')),
+            'emoji' => env('LOG_SLACK_EMOJI', ':boom:'),
+            'level' => env('LOG_LEVEL', 'critical'),
+            'replace_placeholders' => true,
+        ],
+
+        'papertrail' => [
+            'driver' => 'monolog',
+            'level' => env('LOG_LEVEL', 'debug'),
+            'handler' => env('LOG_PAPERTRAIL_HANDLER', SyslogUdpHandler::class),
+            'handler_with' => [
+                'host' => env('PAPERTRAIL_URL'),
+                'port' => env('PAPERTRAIL_PORT'),
+                'connectionString' => 'tls://'.env('PAPERTRAIL_URL').':'.env('PAPERTRAIL_PORT'),
+            ],
+            'processors' => [PsrLogMessageProcessor::class],
+        ],
+
+        'stderr' => [
+            'driver' => 'monolog',
+            'level' => env('LOG_LEVEL', 'debug'),
+            'handler' => StreamHandler::class,
+            'handler_with' => [
+                'stream' => 'php://stderr',
+            ],
+            'formatter' => env('LOG_STDERR_FORMATTER'),
+            'processors' => [PsrLogMessageProcessor::class],
+        ],
+
+        'syslog' => [
+            'driver' => 'syslog',
+            'level' => env('LOG_LEVEL', 'debug'),
+            'facility' => env('LOG_SYSLOG_FACILITY', LOG_USER),
+            'replace_placeholders' => true,
+        ],
+
+        'errorlog' => [
+            'driver' => 'errorlog',
+            'level' => env('LOG_LEVEL', 'debug'),
+            'replace_placeholders' => true,
+        ],
+
+        'null' => [
+            'driver' => 'monolog',
+            'handler' => NullHandler::class,
+        ],
+
+        'emergency' => [
+            'path' => storage_path('logs/laravel.log'),
+        ],
+
+    ],
+
+];

+ 118 - 0
config/mail.php

@@ -0,0 +1,118 @@
+<?php
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | Default Mailer
+    |--------------------------------------------------------------------------
+    |
+    | This option controls the default mailer that is used to send all email
+    | messages unless another mailer is explicitly specified when sending
+    | the message. All additional mailers can be configured within the
+    | "mailers" array. Examples of each type of mailer are provided.
+    |
+    */
+
+    'default' => env('MAIL_MAILER', 'log'),
+
+    /*
+    |--------------------------------------------------------------------------
+    | Mailer Configurations
+    |--------------------------------------------------------------------------
+    |
+    | Here you may configure all of the mailers used by your application plus
+    | their respective settings. Several examples have been configured for
+    | you and you are free to add your own as your application requires.
+    |
+    | Laravel supports a variety of mail "transport" drivers that can be used
+    | when delivering an email. You may specify which one you're using for
+    | your mailers below. You may also add additional mailers if needed.
+    |
+    | Supported: "smtp", "sendmail", "mailgun", "ses", "ses-v2",
+    |            "postmark", "resend", "log", "array",
+    |            "failover", "roundrobin"
+    |
+    */
+
+    'mailers' => [
+
+        'smtp' => [
+            'transport' => 'smtp',
+            'scheme' => env('MAIL_SCHEME'),
+            'url' => env('MAIL_URL'),
+            'host' => env('MAIL_HOST', '127.0.0.1'),
+            'port' => env('MAIL_PORT', 2525),
+            'username' => env('MAIL_USERNAME'),
+            'password' => env('MAIL_PASSWORD'),
+            'timeout' => null,
+            'local_domain' => env('MAIL_EHLO_DOMAIN', parse_url((string) env('APP_URL', 'http://localhost'), PHP_URL_HOST)),
+        ],
+
+        'ses' => [
+            'transport' => 'ses',
+        ],
+
+        'postmark' => [
+            'transport' => 'postmark',
+            // 'message_stream_id' => env('POSTMARK_MESSAGE_STREAM_ID'),
+            // 'client' => [
+            //     'timeout' => 5,
+            // ],
+        ],
+
+        'resend' => [
+            'transport' => 'resend',
+        ],
+
+        'sendmail' => [
+            'transport' => 'sendmail',
+            'path' => env('MAIL_SENDMAIL_PATH', '/usr/sbin/sendmail -bs -i'),
+        ],
+
+        'log' => [
+            'transport' => 'log',
+            'channel' => env('MAIL_LOG_CHANNEL'),
+        ],
+
+        'array' => [
+            'transport' => 'array',
+        ],
+
+        'failover' => [
+            'transport' => 'failover',
+            'mailers' => [
+                'smtp',
+                'log',
+            ],
+            'retry_after' => 60,
+        ],
+
+        'roundrobin' => [
+            'transport' => 'roundrobin',
+            'mailers' => [
+                'ses',
+                'postmark',
+            ],
+            'retry_after' => 60,
+        ],
+
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | Global "From" Address
+    |--------------------------------------------------------------------------
+    |
+    | You may wish for all emails sent by your application to be sent from
+    | the same address. Here you may specify a name and address that is
+    | used globally for all emails that are sent by your application.
+    |
+    */
+
+    'from' => [
+        'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'),
+        'name' => env('MAIL_FROM_NAME', env('APP_NAME', 'Laravel')),
+    ],
+
+];

+ 129 - 0
config/queue.php

@@ -0,0 +1,129 @@
+<?php
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | Default Queue Connection Name
+    |--------------------------------------------------------------------------
+    |
+    | Laravel's queue supports a variety of backends via a single, unified
+    | API, giving you convenient access to each backend using identical
+    | syntax for each. The default queue connection is defined below.
+    |
+    */
+
+    'default' => env('QUEUE_CONNECTION', 'database'),
+
+    /*
+    |--------------------------------------------------------------------------
+    | Queue Connections
+    |--------------------------------------------------------------------------
+    |
+    | Here you may configure the connection options for every queue backend
+    | used by your application. An example configuration is provided for
+    | each backend supported by Laravel. You're also free to add more.
+    |
+    | Drivers: "sync", "database", "beanstalkd", "sqs", "redis",
+    |          "deferred", "background", "failover", "null"
+    |
+    */
+
+    'connections' => [
+
+        'sync' => [
+            'driver' => 'sync',
+        ],
+
+        'database' => [
+            'driver' => 'database',
+            'connection' => env('DB_QUEUE_CONNECTION'),
+            'table' => env('DB_QUEUE_TABLE', 'jobs'),
+            'queue' => env('DB_QUEUE', 'default'),
+            'retry_after' => (int) env('DB_QUEUE_RETRY_AFTER', 90),
+            'after_commit' => false,
+        ],
+
+        'beanstalkd' => [
+            'driver' => 'beanstalkd',
+            'host' => env('BEANSTALKD_QUEUE_HOST', 'localhost'),
+            'queue' => env('BEANSTALKD_QUEUE', 'default'),
+            'retry_after' => (int) env('BEANSTALKD_QUEUE_RETRY_AFTER', 90),
+            'block_for' => 0,
+            'after_commit' => false,
+        ],
+
+        'sqs' => [
+            'driver' => 'sqs',
+            'key' => env('AWS_ACCESS_KEY_ID'),
+            'secret' => env('AWS_SECRET_ACCESS_KEY'),
+            'prefix' => env('SQS_PREFIX', 'https://sqs.us-east-1.amazonaws.com/your-account-id'),
+            'queue' => env('SQS_QUEUE', 'default'),
+            'suffix' => env('SQS_SUFFIX'),
+            'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
+            'after_commit' => false,
+        ],
+
+        'redis' => [
+            'driver' => 'redis',
+            'connection' => env('REDIS_QUEUE_CONNECTION', 'default'),
+            'queue' => env('REDIS_QUEUE', 'default'),
+            'retry_after' => (int) env('REDIS_QUEUE_RETRY_AFTER', 90),
+            'block_for' => null,
+            'after_commit' => false,
+        ],
+
+        'deferred' => [
+            'driver' => 'deferred',
+        ],
+
+        'background' => [
+            'driver' => 'background',
+        ],
+
+        'failover' => [
+            'driver' => 'failover',
+            'connections' => [
+                'database',
+                'deferred',
+            ],
+        ],
+
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | Job Batching
+    |--------------------------------------------------------------------------
+    |
+    | The following options configure the database and table that store job
+    | batching information. These options can be updated to any database
+    | connection and table which has been defined by your application.
+    |
+    */
+
+    'batching' => [
+        'database' => env('DB_CONNECTION', 'sqlite'),
+        'table' => 'job_batches',
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | Failed Queue Jobs
+    |--------------------------------------------------------------------------
+    |
+    | These options configure the behavior of failed queue job logging so you
+    | can control how and where failed jobs are stored. Laravel ships with
+    | support for storing failed jobs in a simple file or in a database.
+    |
+    | Supported drivers: "database-uuids", "dynamodb", "file", "null"
+    |
+    */
+
+    'failed' => [
+        'driver' => env('QUEUE_FAILED_DRIVER', 'database-uuids'),
+        'database' => env('DB_CONNECTION', 'sqlite'),
+        'table' => 'failed_jobs',
+    ],
+
+];

+ 38 - 0
config/services.php

@@ -0,0 +1,38 @@
+<?php
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | Third Party Services
+    |--------------------------------------------------------------------------
+    |
+    | This file is for storing the credentials for third party services such
+    | as Mailgun, Postmark, AWS and more. This file provides the de facto
+    | location for this type of information, allowing packages to have
+    | a conventional file to locate the various service credentials.
+    |
+    */
+
+    'postmark' => [
+        'key' => env('POSTMARK_API_KEY'),
+    ],
+
+    'resend' => [
+        'key' => env('RESEND_API_KEY'),
+    ],
+
+    'ses' => [
+        'key' => env('AWS_ACCESS_KEY_ID'),
+        'secret' => env('AWS_SECRET_ACCESS_KEY'),
+        'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
+    ],
+
+    'slack' => [
+        'notifications' => [
+            'bot_user_oauth_token' => env('SLACK_BOT_USER_OAUTH_TOKEN'),
+            'channel' => env('SLACK_BOT_USER_DEFAULT_CHANNEL'),
+        ],
+    ],
+
+];

+ 233 - 0
config/session.php

@@ -0,0 +1,233 @@
+<?php
+
+use Illuminate\Support\Str;
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | Default Session Driver
+    |--------------------------------------------------------------------------
+    |
+    | This option determines the default session driver that is utilized for
+    | incoming requests. Laravel supports a variety of storage options to
+    | persist session data. Database storage is a great default choice.
+    |
+    | Supported: "file", "cookie", "database", "memcached",
+    |            "redis", "dynamodb", "array"
+    |
+    */
+
+    'driver' => env('SESSION_DRIVER', 'database'),
+
+    /*
+    |--------------------------------------------------------------------------
+    | Session Lifetime
+    |--------------------------------------------------------------------------
+    |
+    | Here you may specify the number of minutes that you wish the session
+    | to be allowed to remain idle before it expires. If you want them
+    | to expire immediately when the browser is closed then you may
+    | indicate that via the expire_on_close configuration option.
+    |
+    */
+
+    'lifetime' => (int) env('SESSION_LIFETIME', 120),
+
+    'expire_on_close' => env('SESSION_EXPIRE_ON_CLOSE', false),
+
+    /*
+    |--------------------------------------------------------------------------
+    | Session Encryption
+    |--------------------------------------------------------------------------
+    |
+    | This option allows you to easily specify that all of your session data
+    | should be encrypted before it's stored. All encryption is performed
+    | automatically by Laravel and you may use the session like normal.
+    |
+    */
+
+    'encrypt' => env('SESSION_ENCRYPT', false),
+
+    /*
+    |--------------------------------------------------------------------------
+    | Session File Location
+    |--------------------------------------------------------------------------
+    |
+    | When utilizing the "file" session driver, the session files are placed
+    | on disk. The default storage location is defined here; however, you
+    | are free to provide another location where they should be stored.
+    |
+    */
+
+    'files' => storage_path('framework/sessions'),
+
+    /*
+    |--------------------------------------------------------------------------
+    | Session Database Connection
+    |--------------------------------------------------------------------------
+    |
+    | When using the "database" or "redis" session drivers, you may specify a
+    | connection that should be used to manage these sessions. This should
+    | correspond to a connection in your database configuration options.
+    |
+    */
+
+    'connection' => env('SESSION_CONNECTION'),
+
+    /*
+    |--------------------------------------------------------------------------
+    | Session Database Table
+    |--------------------------------------------------------------------------
+    |
+    | When using the "database" session driver, you may specify the table to
+    | be used to store sessions. Of course, a sensible default is defined
+    | for you; however, you're welcome to change this to another table.
+    |
+    */
+
+    'table' => env('SESSION_TABLE', 'sessions'),
+
+    /*
+    |--------------------------------------------------------------------------
+    | Session Cache Store
+    |--------------------------------------------------------------------------
+    |
+    | When using one of the framework's cache driven session backends, you may
+    | define the cache store which should be used to store the session data
+    | between requests. This must match one of your defined cache stores.
+    |
+    | Affects: "dynamodb", "memcached", "redis"
+    |
+    */
+
+    'store' => env('SESSION_STORE'),
+
+    /*
+    |--------------------------------------------------------------------------
+    | Session Sweeping Lottery
+    |--------------------------------------------------------------------------
+    |
+    | Some session drivers must manually sweep their storage location to get
+    | rid of old sessions from storage. Here are the chances that it will
+    | happen on a given request. By default, the odds are 2 out of 100.
+    |
+    */
+
+    'lottery' => [2, 100],
+
+    /*
+    |--------------------------------------------------------------------------
+    | Session Cookie Name
+    |--------------------------------------------------------------------------
+    |
+    | Here you may change the name of the session cookie that is created by
+    | the framework. Typically, you should not need to change this value
+    | since doing so does not grant a meaningful security improvement.
+    |
+    */
+
+    'cookie' => env(
+        'SESSION_COOKIE',
+        Str::slug((string) env('APP_NAME', 'laravel')).'-session'
+    ),
+
+    /*
+    |--------------------------------------------------------------------------
+    | Session Cookie Path
+    |--------------------------------------------------------------------------
+    |
+    | The session cookie path determines the path for which the cookie will
+    | be regarded as available. Typically, this will be the root path of
+    | your application, but you're free to change this when necessary.
+    |
+    */
+
+    'path' => env('SESSION_PATH', '/'),
+
+    /*
+    |--------------------------------------------------------------------------
+    | Session Cookie Domain
+    |--------------------------------------------------------------------------
+    |
+    | This value determines the domain and subdomains the session cookie is
+    | available to. By default, the cookie will be available to the root
+    | domain without subdomains. Typically, this shouldn't be changed.
+    |
+    */
+
+    'domain' => env('SESSION_DOMAIN'),
+
+    /*
+    |--------------------------------------------------------------------------
+    | HTTPS Only Cookies
+    |--------------------------------------------------------------------------
+    |
+    | By setting this option to true, session cookies will only be sent back
+    | to the server if the browser has a HTTPS connection. This will keep
+    | the cookie from being sent to you when it can't be done securely.
+    |
+    */
+
+    'secure' => env('SESSION_SECURE_COOKIE'),
+
+    /*
+    |--------------------------------------------------------------------------
+    | HTTP Access Only
+    |--------------------------------------------------------------------------
+    |
+    | Setting this value to true will prevent JavaScript from accessing the
+    | value of the cookie and the cookie will only be accessible through
+    | the HTTP protocol. It's unlikely you should disable this option.
+    |
+    */
+
+    'http_only' => env('SESSION_HTTP_ONLY', true),
+
+    /*
+    |--------------------------------------------------------------------------
+    | Same-Site Cookies
+    |--------------------------------------------------------------------------
+    |
+    | This option determines how your cookies behave when cross-site requests
+    | take place, and can be used to mitigate CSRF attacks. By default, we
+    | will set this value to "lax" to permit secure cross-site requests.
+    |
+    | See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#samesitesamesite-value
+    |
+    | Supported: "lax", "strict", "none", null
+    |
+    */
+
+    'same_site' => env('SESSION_SAME_SITE', 'lax'),
+
+    /*
+    |--------------------------------------------------------------------------
+    | Partitioned Cookies
+    |--------------------------------------------------------------------------
+    |
+    | Setting this value to true will tie the cookie to the top-level site for
+    | a cross-site context. Partitioned cookies are accepted by the browser
+    | when flagged "secure" and the Same-Site attribute is set to "none".
+    |
+    */
+
+    'partitioned' => env('SESSION_PARTITIONED_COOKIE', false),
+
+    /*
+    |--------------------------------------------------------------------------
+    | Session Serialization
+    |--------------------------------------------------------------------------
+    |
+    | This value controls the serialization strategy for session data, which
+    | is JSON by default. Setting this to "php" allows the storage of PHP
+    | objects in the session but can make an application vulnerable to
+    | "gadget chain" serialization attacks if the APP_KEY is leaked.
+    |
+    | Supported: "json", "php"
+    |
+    */
+
+    'serialization' => 'json',
+
+];

+ 45 - 0
database/factories/UserFactory.php

@@ -0,0 +1,45 @@
+<?php
+
+namespace Database\Factories;
+
+use App\Models\User;
+use Illuminate\Database\Eloquent\Factories\Factory;
+use Illuminate\Support\Facades\Hash;
+use Illuminate\Support\Str;
+
+/**
+ * @extends Factory<User>
+ */
+class UserFactory extends Factory
+{
+    /**
+     * The current password being used by the factory.
+     */
+    protected static ?string $password;
+
+    /**
+     * Define the model's default state.
+     *
+     * @return array<string, mixed>
+     */
+    public function definition(): array
+    {
+        return [
+            'name' => fake()->name(),
+            'email' => fake()->unique()->safeEmail(),
+            'email_verified_at' => now(),
+            'password' => static::$password ??= Hash::make('password'),
+            'remember_token' => Str::random(10),
+        ];
+    }
+
+    /**
+     * Indicate that the model's email address should be unverified.
+     */
+    public function unverified(): static
+    {
+        return $this->state(fn (array $attributes) => [
+            'email_verified_at' => null,
+        ]);
+    }
+}

+ 49 - 0
database/migrations/0001_01_01_000000_create_users_table.php

@@ -0,0 +1,49 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+    /**
+     * Run the migrations.
+     */
+    public function up(): void
+    {
+        Schema::create('users', function (Blueprint $table) {
+            $table->id();
+            $table->string('name');
+            $table->string('email')->unique();
+            $table->timestamp('email_verified_at')->nullable();
+            $table->string('password');
+            $table->rememberToken();
+            $table->timestamps();
+        });
+
+        Schema::create('password_reset_tokens', function (Blueprint $table) {
+            $table->string('email')->primary();
+            $table->string('token');
+            $table->timestamp('created_at')->nullable();
+        });
+
+        Schema::create('sessions', function (Blueprint $table) {
+            $table->string('id')->primary();
+            $table->foreignId('user_id')->nullable()->index();
+            $table->string('ip_address', 45)->nullable();
+            $table->text('user_agent')->nullable();
+            $table->longText('payload');
+            $table->integer('last_activity')->index();
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     */
+    public function down(): void
+    {
+        Schema::dropIfExists('users');
+        Schema::dropIfExists('password_reset_tokens');
+        Schema::dropIfExists('sessions');
+    }
+};

+ 35 - 0
database/migrations/0001_01_01_000001_create_cache_table.php

@@ -0,0 +1,35 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+    /**
+     * Run the migrations.
+     */
+    public function up(): void
+    {
+        Schema::create('cache', function (Blueprint $table) {
+            $table->string('key')->primary();
+            $table->mediumText('value');
+            $table->bigInteger('expiration')->index();
+        });
+
+        Schema::create('cache_locks', function (Blueprint $table) {
+            $table->string('key')->primary();
+            $table->string('owner');
+            $table->bigInteger('expiration')->index();
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     */
+    public function down(): void
+    {
+        Schema::dropIfExists('cache');
+        Schema::dropIfExists('cache_locks');
+    }
+};

+ 57 - 0
database/migrations/0001_01_01_000002_create_jobs_table.php

@@ -0,0 +1,57 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+    /**
+     * Run the migrations.
+     */
+    public function up(): void
+    {
+        Schema::create('jobs', function (Blueprint $table) {
+            $table->id();
+            $table->string('queue')->index();
+            $table->longText('payload');
+            $table->unsignedSmallInteger('attempts');
+            $table->unsignedInteger('reserved_at')->nullable();
+            $table->unsignedInteger('available_at');
+            $table->unsignedInteger('created_at');
+        });
+
+        Schema::create('job_batches', function (Blueprint $table) {
+            $table->string('id')->primary();
+            $table->string('name');
+            $table->integer('total_jobs');
+            $table->integer('pending_jobs');
+            $table->integer('failed_jobs');
+            $table->longText('failed_job_ids');
+            $table->mediumText('options')->nullable();
+            $table->integer('cancelled_at')->nullable();
+            $table->integer('created_at');
+            $table->integer('finished_at')->nullable();
+        });
+
+        Schema::create('failed_jobs', function (Blueprint $table) {
+            $table->id();
+            $table->string('uuid')->unique();
+            $table->text('connection');
+            $table->text('queue');
+            $table->longText('payload');
+            $table->longText('exception');
+            $table->timestamp('failed_at')->useCurrent();
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     */
+    public function down(): void
+    {
+        Schema::dropIfExists('jobs');
+        Schema::dropIfExists('job_batches');
+        Schema::dropIfExists('failed_jobs');
+    }
+};

+ 30 - 0
database/migrations/2026_05_06_145556_add_is_admin_to_users_table.php

@@ -0,0 +1,30 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+/*
+ * Миграция: добавляет флаг is_admin в таблицу users.
+ * Создана: 2026-05-06 | Причина: разграничение прав — только admin видит /admin/* маршруты
+ * Используется в: EnsureUserIsAdmin middleware, LoginController, User модели
+ */
+return new class extends Migration
+{
+    public function up(): void
+    {
+        Schema::table('users', function (Blueprint $table) {
+            $table->boolean('is_admin')->default(false)->after('email');
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     */
+    public function down(): void
+    {
+        Schema::table('users', function (Blueprint $table) {
+            $table->dropColumn('is_admin');
+        });
+    }
+};

+ 70 - 0
database/migrations/2026_05_06_151148_create_cars_table.php

@@ -0,0 +1,70 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+/*
+ * Миграция: создаёт таблицу cars — основная таблица каталога автомобилей.
+ * Создана: 2026-05-06
+ * Индексы: idx_make_model (поиск по марке/модели), idx_year, idx_status (фильтрация в списке)
+ * photos_gallery — TEXT/JSON-массив путей к файлам (кастуется в array моделью Car)
+ * options — TEXT/JSON-массив выбранных опций комплектации
+ */
+return new class extends Migration
+{
+    public function up(): void
+    {
+        Schema::create('cars', function (Blueprint $table) {
+            $table->unsignedBigInteger('id')->autoIncrement();
+            $table->enum('status', ['active', 'sold', 'draft'])->default('active');
+            $table->enum('condition', ['new', 'used'])->default('used');
+            $table->string('make', 64);
+            $table->string('model', 64);
+            $table->string('generation', 64)->nullable();
+            $table->smallInteger('year')->unsigned();
+            $table->string('vin', 17)->nullable();
+            $table->string('plate', 20)->nullable();
+            $table->string('body_type', 32)->nullable();
+            $table->tinyInteger('doors')->unsigned()->nullable();
+            $table->string('color_exterior', 32)->nullable();
+            $table->string('color_interior', 32)->nullable();
+            $table->enum('engine_type', ['petrol', 'diesel', 'hybrid', 'electric', 'gas', 'other'])->nullable();
+            $table->decimal('engine_volume', 3, 1)->nullable();
+            $table->smallInteger('engine_power_hp')->unsigned()->nullable();
+            $table->enum('transmission', ['manual', 'automatic', 'robot', 'variator', 'electric'])->nullable();
+            $table->enum('drive', ['FWD', 'RWD', 'AWD', '4WD'])->nullable();
+            $table->unsignedInteger('mileage_km')->nullable();
+            $table->enum('steering', ['left', 'right'])->default('left');
+            $table->tinyInteger('owners_count')->unsigned()->nullable();
+            $table->boolean('customs_cleared')->default(true);
+            $table->enum('pts', ['original', 'duplicate', 'electronic'])->nullable();
+            $table->boolean('accident_free')->default(false);
+            $table->unsignedInteger('price_usd')->nullable();
+            $table->unsignedBigInteger('price_rub')->nullable();
+            $table->unsignedBigInteger('price_vladivostok')->nullable();
+            $table->unsignedBigInteger('price_moscow')->nullable();
+            $table->boolean('price_negotiable')->default(false);
+            $table->string('country_origin', 64)->nullable();
+            $table->string('city', 64)->nullable();
+            $table->text('options')->nullable();
+            $table->string('title', 128)->nullable();
+            $table->text('description')->nullable();
+            $table->string('photo_main', 512)->nullable();
+            $table->text('photos_gallery')->nullable()->comment('JSON array of paths');
+            $table->timestamps();
+
+            $table->index(['make', 'model'], 'idx_make_model');
+            $table->index('year', 'idx_year');
+            $table->index('status', 'idx_status');
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     */
+    public function down(): void
+    {
+        Schema::dropIfExists('cars');
+    }
+};

+ 36 - 0
database/migrations/2026_05_06_151148_create_dict_sections_table.php

@@ -0,0 +1,36 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+/*
+ * Миграция: создаёт таблицу dict_sections — разделы справочников.
+ * Создана: 2026-05-06
+ * code — уникальный строковый идентификатор ('makes', 'body_types', 'colors_ext' и т.д.)
+ * is_system — защита от удаления через UI (системные разделы заполняются DictionarySeeder)
+ * is_hierarchical — если true: значения двухуровневые (Марка → Модели), иначе плоский список
+ * Нет timestamps — статичные справочные данные не требуют отслеживания времени
+ */
+return new class extends Migration
+{
+    public function up(): void
+    {
+        Schema::create('dict_sections', function (Blueprint $table) {
+            $table->unsignedBigInteger('id')->autoIncrement();
+            $table->string('code', 64)->unique();
+            $table->string('label', 128);
+            $table->boolean('is_system')->default(false);
+            $table->boolean('is_hierarchical')->default(false);
+            $table->smallInteger('sort_order')->default(0);
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     */
+    public function down(): void
+    {
+        Schema::dropIfExists('dict_sections');
+    }
+};

+ 39 - 0
database/migrations/2026_05_06_151148_create_dict_values_table.php

@@ -0,0 +1,39 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+/*
+ * Миграция: создаёт таблицу dict_values — значения справочников.
+ * Создана: 2026-05-06
+ * section_id → dict_sections (onDelete cascade: удаление раздела удаляет все его значения)
+ * parent_id → dict_values (самоссылающийся FK; NULL = корневое, не NULL = дочернее значение)
+ * Нет timestamps — значения справочников не требуют отслеживания времени
+ */
+return new class extends Migration
+{
+    public function up(): void
+    {
+        Schema::create('dict_values', function (Blueprint $table) {
+            $table->unsignedBigInteger('id')->autoIncrement();
+            $table->unsignedBigInteger('section_id');
+            $table->unsignedBigInteger('parent_id')->nullable();
+            $table->string('value', 255);
+            $table->smallInteger('sort_order')->default(0);
+
+            $table->index('section_id', 'idx_section');
+            $table->index('parent_id', 'idx_parent');
+            $table->foreign('section_id')->references('id')->on('dict_sections')->onDelete('cascade');
+            $table->foreign('parent_id')->references('id')->on('dict_values')->onDelete('cascade');
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     */
+    public function down(): void
+    {
+        Schema::dropIfExists('dict_values');
+    }
+};

+ 26 - 0
database/migrations/2026_05_06_173123_create_blocks_table.php

@@ -0,0 +1,26 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+// Таблица блоков контента — переиспользуемые HTML-фрагменты, подключаемые на страницах
+return new class extends Migration
+{
+    public function up(): void
+    {
+        Schema::create('blocks', function (Blueprint $table) {
+            $table->id();
+            $table->string('name')->unique();        // системный идентификатор блока (латиница): hero, footer_contacts
+            $table->string('title');                 // читаемое название для отображения в админке
+            $table->longText('content')->nullable(); // HTML-контент блока
+            $table->boolean('is_active')->default(true);
+            $table->timestamps();
+        });
+    }
+
+    public function down(): void
+    {
+        Schema::dropIfExists('blocks');
+    }
+};

+ 28 - 0
database/migrations/2026_05_06_173123_create_pages_table.php

@@ -0,0 +1,28 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+// Таблица статических страниц сайта (главная, услуги, контакты)
+return new class extends Migration
+{
+    public function up(): void
+    {
+        Schema::create('pages', function (Blueprint $table) {
+            $table->id();
+            $table->string('slug')->unique();         // ключ страницы: home, services, contacts
+            $table->string('title');                  // заголовок страницы
+            $table->longText('content')->nullable();  // HTML-контент (основное тело)
+            $table->string('meta_title')->nullable();
+            $table->text('meta_description')->nullable();
+            $table->boolean('is_active')->default(true);
+            $table->timestamps();
+        });
+    }
+
+    public function down(): void
+    {
+        Schema::dropIfExists('pages');
+    }
+};

+ 24 - 0
database/migrations/2026_05_06_202015_create_settings_table.php

@@ -0,0 +1,24 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+// Таблица настроек сайта: key-value хранилище (телефон, соцсети, почта и т.д.)
+return new class extends Migration
+{
+    public function up(): void
+    {
+        Schema::create('settings', function (Blueprint $table) {
+            $table->id();
+            $table->string('key')->unique();   // системный ключ: phone, telegram, email…
+            $table->text('value')->nullable(); // значение настройки
+            $table->timestamps();
+        });
+    }
+
+    public function down(): void
+    {
+        Schema::dropIfExists('settings');
+    }
+};

+ 26 - 0
database/migrations/2026_05_06_212903_add_role_to_users_table.php

@@ -0,0 +1,26 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+    /**
+     * Run the migrations.
+     */
+    // Роль пользователя: superadmin > admin > editor > viewer (null = нет доступа в админку)
+    public function up(): void
+    {
+        Schema::table('users', function (Blueprint $table) {
+            $table->enum('role', ['superadmin', 'admin', 'editor', 'viewer'])->nullable()->after('is_admin');
+        });
+    }
+
+    public function down(): void
+    {
+        Schema::table('users', function (Blueprint $table) {
+            $table->dropColumn('role');
+        });
+    }
+};

+ 33 - 0
database/migrations/2026_05_06_212903_create_user_permissions_table.php

@@ -0,0 +1,33 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+    /**
+     * Run the migrations.
+     */
+    // Индивидуальные разрешения/запреты на конкретные действия для пользователя.
+    // action = 'allow' перекрывает ограничения роли; 'deny' — перекрывает разрешения роли.
+    public function up(): void
+    {
+        Schema::create('user_permissions', function (Blueprint $table) {
+            $table->id();
+            $table->foreignId('user_id')->constrained()->cascadeOnDelete();
+            $table->string('permission');           // ключ: 'cars.edit', 'pages.view' и т.д.
+            $table->enum('action', ['allow', 'deny'])->default('allow');
+            $table->timestamps();
+            $table->unique(['user_id', 'permission']);
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     */
+    public function down(): void
+    {
+        Schema::dropIfExists('user_permissions');
+    }
+};

+ 37 - 0
database/migrations/2026_05_07_091033_create_web_forms_table.php

@@ -0,0 +1,37 @@
+<?php
+
+/*
+ * Миграция: таблица web_forms — конструктор веб-форм.
+ * Создана: 2026-05-07
+ * Поля:
+ *   title        — название формы (для отображения в админке)
+ *   slug         — уникальный идентификатор для компонента <x-web-form slug="...">
+ *   notify_email — email для отправки уведомлений о заявках
+ *   fields       — JSON-массив полей [{label, name, type, width, required, options}, ...]
+ *   is_active    — форма включена/выключена
+ */
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+    public function up(): void
+    {
+        Schema::create('web_forms', function (Blueprint $table) {
+            $table->id();
+            $table->string('title');
+            $table->string('slug')->unique();
+            $table->string('notify_email')->nullable();
+            $table->json('fields')->nullable();
+            $table->boolean('is_active')->default(true);
+            $table->timestamps();
+        });
+    }
+
+    public function down(): void
+    {
+        Schema::dropIfExists('web_forms');
+    }
+};

+ 35 - 0
database/migrations/2026_05_07_091034_create_leads_table.php

@@ -0,0 +1,35 @@
+<?php
+
+/*
+ * Миграция: таблица leads — заявки с форм сайта.
+ * Создана: 2026-05-07
+ * Поля:
+ *   form_id — ID формы (web_forms), FK с каскадным удалением
+ *   data    — JSON-объект с данными заявки {field_name: value, ...}
+ *   ip      — IP-адрес отправителя
+ *   read_at — когда менеджер просмотрел заявку (null = новая)
+ */
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+    public function up(): void
+    {
+        Schema::create('leads', function (Blueprint $table) {
+            $table->id();
+            $table->foreignId('form_id')->constrained('web_forms')->cascadeOnDelete();
+            $table->json('data');
+            $table->string('ip', 45)->nullable();
+            $table->timestamp('read_at')->nullable();
+            $table->timestamps();
+        });
+    }
+
+    public function down(): void
+    {
+        Schema::dropIfExists('leads');
+    }
+};

+ 27 - 0
database/migrations/2026_05_07_100000_create_page_sections_table.php

@@ -0,0 +1,27 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+// Привязка блоков к страницам с произвольным порядком отображения.
+// Один блок может быть добавлен на несколько страниц — уникальность (page_id, block_id).
+return new class extends Migration
+{
+    public function up(): void
+    {
+        Schema::create('page_sections', function (Blueprint $table) {
+            $table->id();
+            $table->foreignId('page_id')->constrained()->cascadeOnDelete();
+            $table->foreignId('block_id')->constrained()->cascadeOnDelete();
+            $table->unsignedInteger('sort_order')->default(0); // позиция блока на странице
+            $table->unique(['page_id', 'block_id']);
+            $table->timestamps();
+        });
+    }
+
+    public function down(): void
+    {
+        Schema::dropIfExists('page_sections');
+    }
+};

+ 41 - 0
database/migrations/2026_05_07_100001_alter_page_sections_add_content_type.php

@@ -0,0 +1,41 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+// Расширяем page_sections под page-builder:
+// - добавляем тип секции (block | content) и поле content для текстовых вставок
+// - block_id теперь nullable (текстовая секция не привязана к блоку)
+// - убираем unique(page_id, block_id) — страница может иметь несколько текстовых секций
+// Пересоздаём таблицу т.к. данных ещё нет и нужно снять NOT NULL с block_id
+
+return new class extends Migration
+{
+    public function up(): void
+    {
+        Schema::dropIfExists('page_sections');
+
+        Schema::create('page_sections', function (Blueprint $table) {
+            $table->id();
+            $table->foreignId('page_id')->constrained()->cascadeOnDelete();
+
+            // 'block' — ссылка на блок из библиотеки; 'content' — произвольный HTML
+            $table->string('type')->default('block');
+
+            // Для type=content: HTML-контент этой секции
+            $table->longText('content')->nullable();
+
+            // Для type=block: ID блока из таблицы blocks
+            $table->foreignId('block_id')->nullable()->constrained()->nullOnDelete();
+
+            $table->unsignedInteger('sort_order')->default(0);
+            $table->timestamps();
+        });
+    }
+
+    public function down(): void
+    {
+        Schema::dropIfExists('page_sections');
+    }
+};

+ 25 - 0
database/migrations/2026_05_07_110000_alter_blocks_add_layout_data.php

@@ -0,0 +1,25 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+// Расширяем таблицу blocks: добавляем макет (layout) и структурированные данные полей (data)
+// content оставляем nullable на случай старых записей, но новая система использует только data
+return new class extends Migration
+{
+    public function up(): void
+    {
+        Schema::table('blocks', function (Blueprint $table) {
+            $table->string('layout')->nullable()->after('name');  // ключ макета: why_us, hero_banner и т.д.
+            $table->json('data')->nullable()->after('content');   // значения полей в виде JSON
+        });
+    }
+
+    public function down(): void
+    {
+        Schema::table('blocks', function (Blueprint $table) {
+            $table->dropColumn(['layout', 'data']);
+        });
+    }
+};

+ 27 - 0
database/migrations/2026_05_07_135048_add_logo_to_dict_values_table.php

@@ -0,0 +1,27 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+/*
+ * Миграция: добавляет поле logo в таблицу dict_values.
+ * Используется для хранения пути к логотипу марки (относительно storage/public).
+ * nullable — большинство значений (модели, типы кузова и т.д.) логотипов не имеют.
+ */
+return new class extends Migration
+{
+    public function up(): void
+    {
+        Schema::table('dict_values', function (Blueprint $table) {
+            $table->string('logo', 500)->nullable()->after('value');
+        });
+    }
+
+    public function down(): void
+    {
+        Schema::table('dict_values', function (Blueprint $table) {
+            $table->dropColumn('logo');
+        });
+    }
+};

+ 34 - 0
database/migrations/2026_05_07_135356_create_reviews_table.php

@@ -0,0 +1,34 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+/*
+ * Миграция: создаёт таблицу reviews — отзывы клиентов.
+ * Создана: 2026-05-07
+ * Управляется через ReviewAdminController (отдельный раздел «Отзывы» в AdminLTE).
+ * В блоке «Отзывы» выбираются конкретные записи через reviews_picker.
+ */
+return new class extends Migration
+{
+    public function up(): void
+    {
+        Schema::create('reviews', function (Blueprint $table) {
+            $table->id();
+            $table->string('author', 128);
+            $table->string('car_name', 128)->nullable();
+            $table->tinyInteger('rating')->default(5); // 1–5 звёзд
+            $table->text('body');                      // текст отзыва
+            $table->date('review_date')->nullable();   // дата (отображаемая)
+            $table->boolean('is_active')->default(true);
+            $table->unsignedSmallInteger('sort_order')->default(0);
+            $table->timestamps();
+        });
+    }
+
+    public function down(): void
+    {
+        Schema::dropIfExists('reviews');
+    }
+};

+ 35 - 0
database/migrations/2026_05_07_191559_create_services_table.php

@@ -0,0 +1,35 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+    /**
+     * Run the migrations.
+     */
+    public function up(): void
+    {
+        Schema::create('services', function (Blueprint $table) {
+            $table->id();
+            $table->string('title');
+            $table->string('slug')->unique();
+            $table->string('icon', 10)->default('');
+            $table->text('excerpt')->default('');
+            $table->longText('description')->nullable();
+            $table->json('tags')->nullable();
+            $table->unsignedSmallInteger('sort_order')->default(0);
+            $table->boolean('is_active')->default(true);
+            $table->timestamps();
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     */
+    public function down(): void
+    {
+        Schema::dropIfExists('services');
+    }
+};

+ 26 - 0
database/migrations/2026_05_07_193451_add_flag_to_dict_values_table.php

@@ -0,0 +1,26 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+    /**
+     * Run the migrations.
+     */
+    public function up(): void
+    {
+        Schema::table('dict_values', function (Blueprint $table) {
+            // Emoji-флаг для стран (🇩🇪), используется рядом с названием в пикерах и на фронте
+            $table->string('flag', 10)->default('')->after('value');
+        });
+    }
+
+    public function down(): void
+    {
+        Schema::table('dict_values', function (Blueprint $table) {
+            $table->dropColumn('flag');
+        });
+    }
+};

+ 29 - 0
database/migrations/2026_05_07_194433_create_page_visits_table.php

@@ -0,0 +1,29 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+    /**
+     * Run the migrations.
+     */
+    public function up(): void
+    {
+        Schema::create('page_visits', function (Blueprint $table) {
+            // Агрегированный счётчик посещений по дням: один row = одна дата
+            $table->date('date')->primary();
+            $table->unsignedInteger('views')->default(0);
+            $table->unsignedInteger('unique_ips')->default(0);
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     */
+    public function down(): void
+    {
+        Schema::dropIfExists('page_visits');
+    }
+};

+ 23 - 0
database/migrations/2026_05_07_195525_mark_service_tags_as_system.php

@@ -0,0 +1,23 @@
+<?php
+
+// Помечаем раздел service_tags как системный — чтобы его нельзя было случайно удалить в AdminLTE
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Support\Facades\DB;
+
+return new class extends Migration
+{
+    public function up(): void
+    {
+        DB::table('dict_sections')
+            ->where('code', 'service_tags')
+            ->update(['is_system' => true]);
+    }
+
+    public function down(): void
+    {
+        DB::table('dict_sections')
+            ->where('code', 'service_tags')
+            ->update(['is_system' => false]);
+    }
+};

+ 54 - 0
database/migrations/2026_05_08_100000_add_platform_to_cars_and_create_platforms_dict.php

@@ -0,0 +1,54 @@
+<?php
+
+use App\Models\DictSection;
+use App\Models\DictValue;
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+/*
+ * Миграция: добавляет поле platform в таблицу cars
+ * и создаёт раздел справочника «Площадки» с тремя начальными значениями.
+ * platform nullable — для совместимости с уже существующими авто.
+ * В форме поле будет обязательным (required в validation).
+ */
+return new class extends Migration
+{
+    public function up(): void
+    {
+        Schema::table('cars', function (Blueprint $table) {
+            // После поля city, перед title — логически рядом с местоположением
+            $table->string('platform', 64)->nullable()->after('city');
+        });
+
+        // Создаём раздел «Площадки» только если его ещё нет
+        if (! DictSection::where('code', 'platforms')->exists()) {
+            $section = DictSection::create([
+                'code' => 'platforms',
+                'label' => 'Площадки',
+                'is_system' => false,
+                'is_hierarchical' => false,
+                'sort_order' => 90,
+            ]);
+
+            $platforms = ['Площадка 1', 'Площадка 2', 'Мои машины'];
+            foreach ($platforms as $i => $value) {
+                DictValue::create([
+                    'section_id' => $section->id,
+                    'parent_id' => null,
+                    'value' => $value,
+                    'sort_order' => ($i + 1) * 10,
+                ]);
+            }
+        }
+    }
+
+    public function down(): void
+    {
+        Schema::table('cars', function (Blueprint $table) {
+            $table->dropColumn('platform');
+        });
+
+        DictSection::where('code', 'platforms')->delete();
+    }
+};

+ 30 - 0
database/seeders/AdminSeeder.php

@@ -0,0 +1,30 @@
+<?php
+
+/*
+ * AdminSeeder — создаёт учётную запись администратора для первоначального входа.
+ *
+ * Создан: 2026-05-06
+ * Запуск: php artisan db:seed (через DatabaseSeeder)
+ * firstOrCreate() — идемпотентен: повторный запуск не создаёт дубликаты
+ * Логин: admin / Пароль: 1234 (хешируется через cast 'hashed' в User модели)
+ */
+
+namespace Database\Seeders;
+
+use App\Models\User;
+use Illuminate\Database\Seeder;
+
+class AdminSeeder extends Seeder
+{
+    public function run(): void
+    {
+        User::firstOrCreate(
+            ['name' => 'admin'],
+            [
+                'email' => 'admin@admin.local',
+                'password' => '1234',
+                'is_admin' => true,
+            ]
+        );
+    }
+}

+ 120 - 0
database/seeders/CarGallerySeeder.php

@@ -0,0 +1,120 @@
+<?php
+
+namespace Database\Seeders;
+
+/*
+ * CarGallerySeeder — скачивает 3 дополнительных фото на тип кузова
+ * и заполняет поле photos_gallery для всех автомобилей.
+ */
+
+use App\Models\Car;
+use Illuminate\Database\Seeder;
+use Illuminate\Support\Facades\Http;
+use Illuminate\Support\Facades\Storage;
+
+class CarGallerySeeder extends Seeder
+{
+    // 3 фото на тип кузова для галереи
+    private const GALLERY = [
+        'suv' => [
+            'https://images.unsplash.com/photo-1503376780353-7e6692767b70?w=800&q=75',
+            'https://images.unsplash.com/photo-1549317661-bd32c8ce0db2?w=800&q=75',
+            'https://images.unsplash.com/photo-1544636331-e26879cd4d9b?w=800&q=75',
+        ],
+        'sedan' => [
+            'https://images.unsplash.com/photo-1494976388531-d1058494cdd8?w=800&q=75',
+            'https://images.unsplash.com/photo-1492144534655-ae79c964c9d7?w=800&q=75',
+            'https://images.unsplash.com/photo-1549399542-7e3f8b79c341?w=800&q=75',
+        ],
+        'hatchback' => [
+            'https://images.unsplash.com/photo-1541899481282-d53bffe3c35d?w=800&q=75',
+            'https://images.unsplash.com/photo-1549317661-bd32c8ce0db2?w=800&q=75',
+            'https://images.unsplash.com/photo-1503376780353-7e6692767b70?w=800&q=75',
+        ],
+        'wagon' => [
+            'https://images.unsplash.com/photo-1544636331-e26879cd4d9b?w=800&q=75',
+            'https://images.unsplash.com/photo-1503376780353-7e6692767b70?w=800&q=75',
+            'https://images.unsplash.com/photo-1492144534655-ae79c964c9d7?w=800&q=75',
+        ],
+        'pickup' => [
+            'https://images.unsplash.com/photo-1558618666-fcd25c85cd64?w=800&q=75',
+            'https://images.unsplash.com/photo-1503376780353-7e6692767b70?w=800&q=75',
+            'https://images.unsplash.com/photo-1494976388531-d1058494cdd8?w=800&q=75',
+        ],
+        'van' => [
+            'https://images.unsplash.com/photo-1519641471654-76ce0107ad1b?w=800&q=75',
+            'https://images.unsplash.com/photo-1503376780353-7e6692767b70?w=800&q=75',
+            'https://images.unsplash.com/photo-1544636331-e26879cd4d9b?w=800&q=75',
+        ],
+        'minivan' => [
+            'https://images.unsplash.com/photo-1449965408869-eaa3f722e40d?w=800&q=75',
+            'https://images.unsplash.com/photo-1544636331-e26879cd4d9b?w=800&q=75',
+            'https://images.unsplash.com/photo-1503376780353-7e6692767b70?w=800&q=75',
+        ],
+        'default' => [
+            'https://images.unsplash.com/photo-1494976388531-d1058494cdd8?w=800&q=75',
+            'https://images.unsplash.com/photo-1503376780353-7e6692767b70?w=800&q=75',
+            'https://images.unsplash.com/photo-1549317661-bd32c8ce0db2?w=800&q=75',
+        ],
+    ];
+
+    public function run(): void
+    {
+        $dir = 'cars/demo';
+        Storage::disk('public')->makeDirectory($dir);
+
+        // Собираем все уникальные URL
+        $allUrls = [];
+        foreach (self::GALLERY as $pool) {
+            foreach ($pool as $url) {
+                $allUrls[$url] = true;
+            }
+        }
+        $allUrls = array_keys($allUrls);
+
+        // Скачиваем
+        $downloaded = [];
+        foreach ($allUrls as $url) {
+            $hash = md5($url);
+            $file = $dir . '/' . $hash . '.jpg';
+
+            if (Storage::disk('public')->exists($file)) {
+                $downloaded[$url] = $file;
+                $this->command->line("  [skip] {$hash}.jpg");
+                continue;
+            }
+
+            $this->command->line("  [download] {$hash}.jpg...");
+            try {
+                $response = Http::timeout(25)->get($url);
+                if ($response->successful()) {
+                    Storage::disk('public')->put($file, $response->body());
+                    $downloaded[$url] = $file;
+                    $this->command->line('    ✓ ' . round(strlen($response->body()) / 1024) . ' KB');
+                } else {
+                    $this->command->warn('    ✗ HTTP ' . $response->status());
+                }
+            } catch (\Exception $e) {
+                $this->command->warn('    ✗ ' . $e->getMessage());
+            }
+        }
+
+        // Присваиваем галереи
+        $updated = 0;
+        foreach (Car::all() as $car) {
+            $pool = self::GALLERY[$car->body_type] ?? self::GALLERY['default'];
+            $gallery = [];
+            foreach ($pool as $url) {
+                if (isset($downloaded[$url])) {
+                    $gallery[] = $downloaded[$url];
+                }
+            }
+            if (!empty($gallery)) {
+                $car->update(['photos_gallery' => $gallery]);
+                $updated++;
+            }
+        }
+
+        $this->command->info("Галерея обновлена: {$updated} авто.");
+    }
+}

+ 121 - 0
database/seeders/CarPhotosSeeder.php

@@ -0,0 +1,121 @@
+<?php
+
+namespace Database\Seeders;
+
+/*
+ * CarPhotosSeeder — скачивает демо-фотографии с Unsplash и присваивает их автомобилям.
+ * Фото подбираются по типу кузова. Удалить можно через php artisan db:seed --class=CarPhotosSeeder reset.
+ */
+
+use App\Models\Car;
+use Illuminate\Database\Seeder;
+use Illuminate\Support\Facades\Http;
+use Illuminate\Support\Facades\Storage;
+
+class CarPhotosSeeder extends Seeder
+{
+    // Unsplash-фото по типу кузова — несколько штук для ротации
+    private const PHOTOS = [
+        'suv' => [
+            'https://images.unsplash.com/photo-1617469767269-f80a3e3f13dc?w=900&q=80',
+            'https://images.unsplash.com/photo-1555215695-3004980ad54e?w=900&q=80',
+            'https://images.unsplash.com/photo-1568605117036-5fe5e7bab0b7?w=900&q=80',
+            'https://images.unsplash.com/photo-1617654112329-a50e7f47e065?w=900&q=80',
+            'https://images.unsplash.com/photo-1533473359331-0135ef1b58bf?w=900&q=80',
+        ],
+        'sedan' => [
+            'https://images.unsplash.com/photo-1621007947382-bb3c3994e3fb?w=900&q=80',
+            'https://images.unsplash.com/photo-1618843479313-40f8afb4b4d8?w=900&q=80',
+            'https://images.unsplash.com/photo-1609752740145-6ac4be2c3947?w=900&q=80',
+            'https://images.unsplash.com/photo-1492144534655-ae79c964c9d7?w=900&q=80',
+        ],
+        'hatchback' => [
+            'https://images.unsplash.com/photo-1541899481282-d53bffe3c35d?w=900&q=80',
+            'https://images.unsplash.com/photo-1606664515524-ed2f786a0bd6?w=900&q=80',
+        ],
+        'wagon' => [
+            'https://images.unsplash.com/photo-1544636331-e26879cd4d9b?w=900&q=80',
+            'https://images.unsplash.com/photo-1549317661-bd32c8ce0db2?w=900&q=80',
+        ],
+        'van' => [
+            'https://images.unsplash.com/photo-1519641471654-76ce0107ad1b?w=900&q=80',
+        ],
+        'pickup' => [
+            'https://images.unsplash.com/photo-1558618666-fcd25c85cd64?w=900&q=80',
+        ],
+        'minivan' => [
+            'https://images.unsplash.com/photo-1449965408869-eaa3f722e40d?w=900&q=80',
+        ],
+        'default' => [
+            'https://images.unsplash.com/photo-1494976388531-d1058494cdd8?w=900&q=80',
+            'https://images.unsplash.com/photo-1533473359331-0135ef1b58bf?w=900&q=80',
+        ],
+    ];
+
+    public function run(): void
+    {
+        $dir = 'cars/demo';
+        Storage::disk('public')->makeDirectory($dir);
+
+        // Скачиваем все уникальные URL один раз
+        $urlToFile = $this->downloadAll($dir);
+
+        // Счётчик ротации по каждому типу кузова
+        $counters = [];
+
+        $cars = Car::all();
+        $updated = 0;
+
+        foreach ($cars as $car) {
+            $type   = $car->body_type ?? 'default';
+            $pool   = self::PHOTOS[$type] ?? self::PHOTOS['default'];
+            $idx    = ($counters[$type] ?? 0) % count($pool);
+            $url    = $pool[$idx];
+            $counters[$type] = $idx + 1;
+
+            if (!isset($urlToFile[$url])) continue;
+
+            $car->update(['photo_main' => $urlToFile[$url]]);
+            $updated++;
+        }
+
+        $this->command->info("Фото присвоены {$updated} автомобилям.");
+    }
+
+    private function downloadAll(string $dir): array
+    {
+        $urlToFile = [];
+
+        // Собираем все уникальные URL
+        $allUrls = array_unique(array_merge(...array_values(self::PHOTOS)));
+
+        foreach ($allUrls as $url) {
+            $hash     = md5($url);
+            $filename = $dir . '/' . $hash . '.jpg';
+
+            // Не качаем повторно если файл уже есть
+            if (Storage::disk('public')->exists($filename)) {
+                $this->command->line("  [skip] {$hash}.jpg");
+                $urlToFile[$url] = $filename;
+                continue;
+            }
+
+            $this->command->line("  [download] {$hash}.jpg ...");
+
+            try {
+                $response = Http::timeout(30)->get($url);
+                if ($response->successful()) {
+                    Storage::disk('public')->put($filename, $response->body());
+                    $urlToFile[$url] = $filename;
+                    $this->command->line("    ✓ OK (" . round(strlen($response->body()) / 1024) . " KB)");
+                } else {
+                    $this->command->warn("    ✗ HTTP " . $response->status());
+                }
+            } catch (\Exception $e) {
+                $this->command->warn("    ✗ " . $e->getMessage());
+            }
+        }
+
+        return $urlToFile;
+    }
+}

+ 46 - 0
database/seeders/CarsFromDromSeeder.php

@@ -0,0 +1,46 @@
+<?php
+
+namespace Database\Seeders;
+
+/*
+ * CarsFromDromSeeder — 30 автомобилей с реальными данными с auto.drom.ru
+ * Данные вынесены в database/seeders/data/cars_drom.php (файл возвращает массив).
+ * steering: Москва → left; японские авто из Владивостока → right.
+ */
+
+use App\Models\Car;
+use Illuminate\Database\Seeder;
+
+class CarsFromDromSeeder extends Seeder
+{
+    public function run(): void
+    {
+        $cars = require __DIR__ . '/data/cars_drom.php';
+
+        foreach ($cars as $data) {
+            Car::create(array_merge($data, [
+                'status'      => 'active',
+                'doors'       => null,
+                'vin'         => null,
+                'plate'       => null,
+                'color_exterior' => null,
+                'color_interior' => null,
+                'generation'  => null,
+                'owners_count' => null,
+                'customs_cleared' => false,
+                'accident_free'   => false,
+                'pts'          => null,
+                'price_usd'    => null,
+                'price_vladivostok' => null,
+                'price_moscow'      => null,
+                'price_negotiable'  => false,
+                'options'      => null,
+                'description'  => null,
+                'photo_main'   => null,
+                'photos_gallery' => null,
+            ]));
+        }
+
+        $this->command->info('Залито ' . count($cars) . ' автомобилей с drom.ru');
+    }
+}

Некоторые файлы не были показаны из-за большого количества измененных файлов