commit d775fbb9ba092671c38ca216a05ec3ed989aba8b Author: jze9 Date: Mon Apr 6 18:44:02 2026 +0500 tsett diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..a282f28 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,18 @@ +.git +.gitignore +node_modules +vendor +.env +.env.* +!.env.example +storage/logs/* +storage/framework/cache/* +storage/framework/sessions/* +storage/framework/views/* +public/build +*.md +tests/ +phpunit.xml +docker-compose*.yml +.editorconfig +.styleci.yml diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..8f0de65 --- /dev/null +++ b/.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 + +[docker-compose.yml] +indent_size = 4 diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..ea0665b --- /dev/null +++ b/.env.example @@ -0,0 +1,59 @@ +APP_NAME=Laravel +APP_ENV=local +APP_KEY= +APP_DEBUG=true +APP_URL=http://localhost + +LOG_CHANNEL=stack +LOG_DEPRECATIONS_CHANNEL=null +LOG_LEVEL=debug + +DB_CONNECTION=mysql +DB_HOST=127.0.0.1 +DB_PORT=3306 +DB_DATABASE=laravel +DB_USERNAME=root +DB_PASSWORD= + +BROADCAST_DRIVER=log +CACHE_DRIVER=file +FILESYSTEM_DISK=local +QUEUE_CONNECTION=sync +SESSION_DRIVER=file +SESSION_LIFETIME=120 + +MEMCACHED_HOST=127.0.0.1 + +REDIS_HOST=127.0.0.1 +REDIS_PASSWORD=null +REDIS_PORT=6379 + +MAIL_MAILER=smtp +MAIL_HOST=mailpit +MAIL_PORT=1025 +MAIL_USERNAME=null +MAIL_PASSWORD=null +MAIL_ENCRYPTION=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 + +PUSHER_APP_ID= +PUSHER_APP_KEY= +PUSHER_APP_SECRET= +PUSHER_HOST= +PUSHER_PORT=443 +PUSHER_SCHEME=https +PUSHER_APP_CLUSTER=mt1 + +VITE_APP_NAME="${APP_NAME}" +VITE_PUSHER_APP_KEY="${PUSHER_APP_KEY}" +VITE_PUSHER_HOST="${PUSHER_HOST}" +VITE_PUSHER_PORT="${PUSHER_PORT}" +VITE_PUSHER_SCHEME="${PUSHER_SCHEME}" +VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}" diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..fcb21d3 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,11 @@ +* text=auto eol=lf + +*.blade.php diff=html +*.css diff=css +*.html diff=html +*.md diff=markdown +*.php diff=php + +/.github export-ignore +CHANGELOG.md export-ignore +.styleci.yml export-ignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7fe978f --- /dev/null +++ b/.gitignore @@ -0,0 +1,19 @@ +/.phpunit.cache +/node_modules +/public/build +/public/hot +/public/storage +/storage/*.key +/vendor +.env +.env.backup +.env.production +.phpunit.result.cache +Homestead.json +Homestead.yaml +auth.json +npm-debug.log +yarn-error.log +/.fleet +/.idea +/.vscode diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..ad15390 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,76 @@ +# ───────────────────────────────────────────── +# Stage 1: Build frontend assets +# ───────────────────────────────────────────── +FROM node:20-alpine AS node-builder + +WORKDIR /app + +COPY package.json package-lock.json* ./ +RUN npm ci + +COPY vite.config.js tailwind.config.js postcss.config.js ./ +COPY resources/ resources/ +COPY public/ public/ + +RUN npm run build + +# ───────────────────────────────────────────── +# Stage 2: PHP application +# ───────────────────────────────────────────── +FROM php:8.2-fpm-alpine AS app + +# System dependencies +RUN apk add --no-cache \ + nginx \ + supervisor \ + curl \ + libpng-dev \ + libjpeg-turbo-dev \ + libwebp-dev \ + freetype-dev \ + libzip-dev \ + oniguruma-dev \ + icu-dev \ + && docker-php-ext-configure gd --with-freetype --with-jpeg --with-webp \ + && docker-php-ext-install \ + pdo_mysql \ + mbstring \ + gd \ + zip \ + bcmath \ + intl \ + opcache + +# Install Composer +COPY --from=composer:2 /usr/bin/composer /usr/bin/composer + +WORKDIR /var/www/html + +# Copy composer files first for layer caching +COPY composer.json composer.lock ./ +RUN composer install --no-dev --no-scripts --optimize-autoloader --no-interaction + +# Copy application source +COPY . . + +# Copy built frontend assets from stage 1 +COPY --from=node-builder /app/public/build public/build + +# Laravel setup +RUN cp .env.example .env \ + && php artisan key:generate --force \ + && php artisan config:clear \ + && php artisan storage:link || true + +# Permissions +RUN chown -R www-data:www-data /var/www/html/storage /var/www/html/bootstrap/cache \ + && chmod -R 775 /var/www/html/storage /var/www/html/bootstrap/cache + +# Copy config files +COPY docker/nginx/default.conf /etc/nginx/http.d/default.conf +COPY docker/php/php.ini $PHP_INI_DIR/conf.d/app.ini +COPY docker/supervisord.conf /etc/supervisor/conf.d/supervisord.conf + +EXPOSE 80 + +CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..1a4c26b --- /dev/null +++ b/README.md @@ -0,0 +1,66 @@ +

Laravel Logo

+ +

+Build Status +Total Downloads +Latest Stable Version +License +

+ +## About Laravel + +Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experience to be truly fulfilling. Laravel takes the pain out of development by easing common tasks used in many web projects, such as: + +- [Simple, fast routing engine](https://laravel.com/docs/routing). +- [Powerful dependency injection container](https://laravel.com/docs/container). +- Multiple back-ends for [session](https://laravel.com/docs/session) and [cache](https://laravel.com/docs/cache) storage. +- Expressive, intuitive [database ORM](https://laravel.com/docs/eloquent). +- Database agnostic [schema migrations](https://laravel.com/docs/migrations). +- [Robust background job processing](https://laravel.com/docs/queues). +- [Real-time event broadcasting](https://laravel.com/docs/broadcasting). + +Laravel is accessible, powerful, and provides tools required for large, robust applications. + +## Learning Laravel + +Laravel has the most extensive and thorough [documentation](https://laravel.com/docs) and video tutorial library of all modern web application frameworks, making it a breeze to get started with the framework. + +You may also try the [Laravel Bootcamp](https://bootcamp.laravel.com), where you will be guided through building a modern Laravel application from scratch. + +If you don't feel like reading, [Laracasts](https://laracasts.com) can help. Laracasts contains thousands of video tutorials on a range of topics including Laravel, modern PHP, unit testing, and JavaScript. Boost your skills by digging into our comprehensive video library. + +## Laravel Sponsors + +We would like to extend our thanks to the following sponsors for funding Laravel development. If you are interested in becoming a sponsor, please visit the [Laravel Partners program](https://partners.laravel.com). + +### Premium Partners + +- **[Vehikl](https://vehikl.com/)** +- **[Tighten Co.](https://tighten.co)** +- **[WebReinvent](https://webreinvent.com/)** +- **[Kirschbaum Development Group](https://kirschbaumdevelopment.com)** +- **[64 Robots](https://64robots.com)** +- **[Curotec](https://www.curotec.com/services/technologies/laravel/)** +- **[Cyber-Duck](https://cyber-duck.co.uk)** +- **[DevSquad](https://devsquad.com/hire-laravel-developers)** +- **[Jump24](https://jump24.co.uk)** +- **[Redberry](https://redberry.international/laravel/)** +- **[Active Logic](https://activelogic.com)** +- **[byte5](https://byte5.de)** +- **[OP.GG](https://op.gg)** + +## Contributing + +Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions). + +## Code of Conduct + +In order to ensure that the Laravel community is welcoming to all, please review and abide by the [Code of Conduct](https://laravel.com/docs/contributions#code-of-conduct). + +## Security Vulnerabilities + +If you discover a security vulnerability within Laravel, please send an e-mail to Taylor Otwell via [taylor@laravel.com](mailto:taylor@laravel.com). All security vulnerabilities will be promptly addressed. + +## License + +The Laravel framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT). diff --git a/TailAndPaws.sql b/TailAndPaws.sql new file mode 100644 index 0000000..abd95d3 --- /dev/null +++ b/TailAndPaws.sql @@ -0,0 +1,1462 @@ +-- phpMyAdmin SQL Dump +-- version 5.2.0 +-- https://www.phpmyadmin.net/ +-- +-- Servidor: 127.0.0.1:3306 +-- Tiempo de generación: 06-04-2026 a las 16:37:55 +-- Versión del servidor: 10.4.26-MariaDB-log +-- Versión de PHP: 8.1.9 + +SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO"; +START TRANSACTION; +SET time_zone = "+00:00"; + + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8mb4 */; + +-- +-- Base de datos: `TailAndPaws` +-- + +-- -------------------------------------------------------- + +-- +-- Estructura de tabla para la tabla `brands` +-- + +CREATE TABLE `brands` ( + `id` bigint(20) UNSIGNED NOT NULL, + `name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, + `slug` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, + `description` text COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `logo` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `website` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `country` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `is_active` tinyint(1) NOT NULL DEFAULT 1 +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- +-- Volcado de datos para la tabla `brands` +-- + +INSERT INTO `brands` (`id`, `name`, `slug`, `description`, `logo`, `website`, `country`, `is_active`) VALUES +(1, 'Royal Canin', 'royal-canin', 'Премиальные корма для кошек и собак', 'Royal-Canin-Logo.png', 'https://www.royalcanin.com', 'Франция', 1), +(2, 'ABBA', 'abba', 'Качественные корма для домашних животных', 'abba.png', 'https://abba.ru', 'Россия', 1), +(3, 'RURRI', 'rurri', 'Одежда и амуниция для собак', 'rurri.png', NULL, 'Россия', 1), +(4, 'Ownat', 'ownat', 'Натуральные корма премиум-класса', 'ownat.png', 'https://www.ownat.com', 'Испания', 1), +(5, 'Grandin', 'grandin', 'Корма и лакомства для собак', 'grandin.png', 'https://www.grandin-pet.ru/', 'Россия', 1), +(6, 'Triol', 'triol', 'Российский производитель качественных кормов и лакомств для собак и кошек.', 'triol.png', 'https://triol.pet/', 'Россия', 1), +(10, 'Rungo', 'rungo', 'RUNGO – продукция предназначенная для качественного ухода за собаками.\r\n\r\nОдежда и аксессуары RUNGO соответствуют современным потребностям домашних животных и хозяев. Эти изделия отличаются высоким качеством, сохраняют свои технические характеристики и внешний вид в хорошем состоянии в течение длительного срока эксплуатации. В ассортимент RUNGO входят следующие группы товаров: светящиеся игрушки, маячки, ошейники, попоны и дождевики.\r\n\r\nС применением изделий RUNGO многие практические задачи будут решены быстро, точно и с комфортом.', 'rungo.jpeg', NULL, 'Россия', 1), +(11, 'Klicker', 'klicker', 'Турецкий бренд сухих и влажных кормов супер-премиум класса для кошек и собак, производимый компанией Hermos Pet Food с 2017 года. Корма производятся в Турции, являются беззерновыми, монопротеиновыми и экспортируются во многие страны.', 'klicker.png', NULL, 'Турция', 1), +(12, 'AlphaPet', 'alphapet', 'Российский бренд высококачественных кормов для собак и кошек классов суперпремиум и холистик. Продукция производится в России на современном оборудовании с высоким содержанием свежего мяса (до 95% во влажных рационах), разработанная с учетом физиологических потребностей животных и принципов здорового питания.', 'alphapet.png', 'https://alphapet.ru/', 'Россия', 1), +(13, 'Tetra', 'tetra', 'Мировой лидер в производстве товаров для аквариумистики, основанный в Германии, который первым разработал сухой корм для рыб в виде хлопьев', 'tetra.jpg', 'https://www.tetra.net/ru-ru', 'Германия', 1), +(14, 'Little One', 'little-one', 'Бренд Little One (корма и лакомства для грызунов и птиц) производится в России. Торговая марка принадлежит компании Mealberry, которая специализируется на выпуске товаров для мелких домашних животных. Продукция, включая корма, лакомства и витамины, широко представлена на российском рынке.', 'little-one.jpg', NULL, 'Россия', 1), +(15, 'GRANDORF', 'grandorf', 'Это бельгийский бренд высококачественных кормов класса холистик, предназначенных для кошек и собак. Продукция славится высоким содержанием мяса (до 70%), гипоаллергенными составами без кукурузы, пшеницы и субпродуктов, а также наличием живых пробиотиков для пищеварения. Подходит для животных с чувствительным ЖКТ.', 'grandorf.jpg', 'https://grandorf.ru/', 'Бельгия', 1), +(16, 'Rogz', 'rogz', 'Известный южноафриканский бренд товаров для домашних животных, основанный в 1995 году в Кейптауне Полом Фуллером и Ирен Раубенхаймер. Марка производит безопасные, функциональные и стильные аксессуары (ошейники, поводки, шлейки, игрушки) для собак и кошек, отличающиеся ярким дизайном и высоким качеством материалов.', 'rogz.png', 'https://rogz.com/', 'Южно-Африканская Республика', 1); + +-- -------------------------------------------------------- + +-- +-- Estructura de tabla para la tabla `cart_items` +-- + +CREATE TABLE `cart_items` ( + `id` bigint(20) UNSIGNED NOT NULL, + `user_id` bigint(20) UNSIGNED DEFAULT NULL, + `session_id` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `variation_id` bigint(20) UNSIGNED NOT NULL, + `quantity` int(11) NOT NULL DEFAULT 1, + `price` decimal(10,2) NOT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- +-- Volcado de datos para la tabla `cart_items` +-- + +INSERT INTO `cart_items` (`id`, `user_id`, `session_id`, `variation_id`, `quantity`, `price`, `created_at`, `updated_at`) VALUES +(20, 1, 'FsRQjMKDSOThcwwPUvflAKIrh9FN4myyQTqA0M3B', 46, 1, '1999.00', '2026-04-06 06:51:52', '2026-04-06 06:51:52'); + +-- -------------------------------------------------------- + +-- +-- Estructura de tabla para la tabla `categories` +-- + +CREATE TABLE `categories` ( + `id` bigint(20) UNSIGNED NOT NULL, + `name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, + `slug` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, + `description` text COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `icon` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `image` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `parent_id` bigint(20) UNSIGNED DEFAULT NULL, + `sort_order` int(11) NOT NULL DEFAULT 0, + `is_active` tinyint(1) NOT NULL DEFAULT 1 +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- +-- Volcado de datos para la tabla `categories` +-- + +INSERT INTO `categories` (`id`, `name`, `slug`, `description`, `icon`, `image`, `parent_id`, `sort_order`, `is_active`) VALUES +(1, 'Для собак', 'dlya-sobak', 'Всё необходимое для вашего четвероногого друга: от качественного питания до удобной амуниции и развивающих игрушек. Позаботьтесь о здоровье и счастье вашей собаки вместе с TailAndPaws!', 'dlya-sobak.svg', 'dlia-sobak_image.jpg', NULL, 10, 1), +(2, 'Корма', 'korma-dlya-sobak', 'Правильное питание — основа здоровья вашего питомца. У нас представлены корма премиум-класса для собак всех возрастов, пород и особенностей здоровья.', NULL, 'korma_image.jpg', 1, 10, 1), +(3, 'Сухие корма', 'suhie-korma', 'Сбалансированные сухие корма с высоким содержанием белка, витаминов и минералов. Удобный формат хранения и кормления для активных собак.', NULL, 'suxie-korma_image.jpg', 2, 10, 1), +(4, 'Влажные корма', 'vlazhnye-korma', 'Ароматные паштеты, кусочки в соусе и консервы, которые ваша собака полюбит с первого раза. Идеально для привередливых питомцев.', NULL, 'vlaznye-korma_image.jpg', 2, 20, 1), +(5, 'Диетическое питание', 'dieticheskoe-pitanie', 'Специализированные корма для собак с чувствительным пищеварением, аллергиями или особыми потребностями. Только одобренные ветеринарами формулы.', NULL, NULL, 2, 30, 1), +(6, 'Амуниция', 'amunitsiya', 'Качественная амуниция для комфортных прогулок и безопасного содержания. Ошейники, поводки, шлейки и аксессуары на любой вкус.', NULL, NULL, 1, 20, 1), +(7, 'Ошейники и поводки', 'osheyniki-i-povodki', 'Надёжные и стильные ошейники и поводки из качественных материалов. Регулируемые размеры, светоотражающие элементы и удобные застёжки.', NULL, 'oseiniki-i-povodki_image.jpg', 6, 10, 1), +(8, 'Намордники', 'namordniki', 'Безопасные и комфортные намордники для прогулок и посещения ветеринара. Различные размеры и материалы для любой породы.', NULL, NULL, 6, 20, 1), +(10, 'Игрушки', 'igrushki', 'Развивающие, интерактивные и просто весёлые игрушки для активных собак. Помогают направить энергию в мирное русло и укрепить связь с хозяином.', NULL, NULL, 1, 30, 1), +(11, 'Мячики и фрисби', 'myachiki-i-frisbi', 'Идеальный выбор для активных игр на свежем воздухе. Прочные мячики, летающие тарелки и снаряды для апортировки, которые выдержат любую нагрузку.', NULL, NULL, 10, 10, 1), +(12, 'Канаты и кости', 'kanaty-i-kosti', 'Прочные канатные игрушки и безопасные кости для жевания. Помогают чистить зубы, массировать дёсны и надолго увлечь питомца.', NULL, NULL, 10, 20, 1), +(13, 'Лежанки и домики', 'lezhanki-i-domiki', 'Уютные и тёплые места для отдыха вашего питомца. Выбирайте из широкого ассортимента лежанок и домиков для собак любых размеров.', 'lezhanki-i-domiki.svg', 'lezhanki-i-domiki.jpg', 1, 40, 1), +(14, 'Лежанки', 'lezhanki', 'Мягкие лежанки с ортопедическими свойствами для комфортного сна и отдыха. Съёмные чехлы, водоотталкивающие материалы — уют в каждой детали.', 'lezhanki.svg', 'lezhanki.jpg', 13, 10, 1), +(15, 'Домики', 'domiki', 'Уютные домики и будки, где ваш питомец будет чувствовать себя в полной безопасности. Для квартиры, дома или уличного содержания.', 'domiki.svg', 'domiki.jpg', 13, 20, 1), +(16, 'Миски и кормушки', 'miski-i-kormushki', 'Практичные миски и автоматические кормушки для правильного режима питания. Нержавеющая сталь, керамика, пластик — выбор за вами.', NULL, NULL, 1, 50, 1), +(17, 'Миски', 'miski', 'Удобные миски для воды и корма на любой вкус. С нескользящими ножками, подставками и регулируемой высотой для правильной осанки питомца.', 'miski.svg', 'miski.jpg', 16, 10, 1), +(18, 'Автокормушки', 'avtokormushki', 'Современные автоматические кормушки с программируемым временем подачи. Идеально для занятых хозяев и питомцев, которым нужен режим.', 'avtokormushki.svg', 'avtokormushki.jpg', 16, 20, 1), +(19, 'Для кошек', 'dlya-koshek', 'Всё, что нужно для счастливой и здоровой жизни вашей кошки. Корма премиум-класса, удобные наполнители, когтеточки и уютные домики.', 'dlya-koshek.svg', 'dlia-kosek_image.jpg', NULL, 20, 1), +(20, 'Корма', 'korma-dlya-koshek', 'Качественные корма для кошек всех возрастов. Сухие и влажные рационы, лакомства и специализированное питание для здоровья и долголетия.', 'korma-dlya-koshek.svg', 'korma-dlya-koshek.jpg', 19, 10, 1), +(21, 'Сухие корма', 'suhie-korma-dlya-koshek', 'Сбалансированные сухие корма с высоким содержанием таурина, витаминов и минералов. Для поддержания здоровья шерсти, зубов и пищеварения.', 'suhie-korma-dlya-koshek.svg', 'suxie-korma_image.jpg', 20, 10, 1), +(22, 'Паучи и консервы', 'pauchi-i-konservy', 'Аппетитные паучи и консервы в удобной порционной упаковке. Натуральный состав, высокое содержание мяса — идеальный выбор для гурманов.', 'pauchi-i-konservy.svg', 'pauci-i-konservy_image.jpg', 20, 20, 1), +(23, 'Лакомства', 'lakomstva-dlya-koshek', 'Вкусные и полезные лакомства для поощрения и разнообразия рациона. Палочки, подушечки, крема — ваша кошка будет в восторге!', 'lakomstva-dlya-koshek.svg', 'lakomstva_image.jpg', 20, 30, 1), +(24, 'Наполнители', 'napolniteli', 'Качественные наполнители для кошачьего туалета с отличной впитываемостью и контролем запаха. Выбирайте оптимальный вариант для вашего питомца.', 'napolniteli.svg', 'napolniteli.jpg', 19, 20, 1), +(25, 'Древесные', 'drevesnye', 'Экологичные древесные наполнители из натуральных материалов. Отличная абсорбция, приятный аромат и бережное отношение к природе.', 'drevesnye.svg', 'drevesnye.jpg', 24, 10, 1), +(26, 'Силикагелевые', 'silikagelevye', 'Современные силикагелевые наполнители с максимальной впитываемостью. Долго сохраняют сухость и нейтрализуют неприятные запахи.', 'silikagelevye.svg', 'silikagelevye.jpg', 24, 20, 1), +(27, 'Комкующиеся', 'komkuyuschiesya', 'Удобные комкующиеся наполнители на основе бентонитовой глины. Легко убирать, экономичный расход и отличный контроль запаха.', 'komkuyuschiesya.svg', 'komkuyuschiesya.jpg', 24, 30, 1), +(28, 'Когтеточки и домики', 'kogtetochki-i-domiki', 'Всё для активных и спокойных кошек. Когтеточки для стачивания когтей, уютные домики и развлекательные комплексы.', 'kogtetochki-i-domiki.svg', 'kogtetochki-i-domiki.jpg', 19, 30, 1), +(29, 'Когтеточки', 'kogtetochki', 'Качественные когтеточки разных форм и размеров. Спасите вашу мебель и подарите кошке любимое место для точения когтей.', 'kogtetochki.svg', 'kogtetochki.jpg', 28, 10, 1), +(30, 'Лежанки для кошек', 'lezhanki-dlya-koshek', 'Мягкие и тёплые лежанки для любимых кошек. Уютные места для сна и отдыха, которые так любят наши пушистые друзья.', 'lezhanki-dlya-koshek.svg', 'lezhanki-dlya-koshek.jpg', 28, 20, 1), +(31, 'Игровые комплексы', 'igrovye-kompleksy', 'Многоуровневые игровые комплексы с когтеточками, домиками и подвесными игрушками. Идеальное решение для активных кошек.', 'igrovye-kompleksy.svg', 'igrovye-kompleksy.jpg', 28, 30, 1), +(32, 'Для грызунов', 'dlya-gryzunov', 'Всё для комфортной жизни маленьких питомцев: хомяков, морских свинок, крыс и шиншилл. Качественные корма, уютные клетки и развивающие аксессуары.', 'dlya-gryzunov.svg', 'dlia-gryzunov_image.jpg', NULL, 30, 1), +(33, 'Корма', 'korma-dlya-gryzunov', 'Сбалансированные корма для грызунов с учётом их видовых потребностей. Зерновые смеси, сено, травяные гранулы и полезные лакомства.', 'korma-dlya-gryzunov.svg', 'korma-dlya-gryzunov.jpg', 32, 10, 1), +(34, 'Зерновые смеси', 'zernovye-smesi', 'Питательные зерновые смеси с добавлением овощей, фруктов и витаминов. Обеспечивают организм грызунов всеми необходимыми веществами.', 'zernovye-smesi.svg', 'zernovye-smesi.jpg', 33, 10, 1), +(35, 'Сено и травы', 'seno-i-travy', 'Ароматное сено и полезные травы для правильного пищеварения и стачивания зубов грызунов. Натуральные и экологически чистые продукты.', 'seno-i-travy.svg', 'seno-i-travy.jpg', 33, 20, 1), +(36, 'Лакомства', 'lakomstva-dlya-gryzunov', 'Вкусные и полезные лакомства для поощрения и разнообразия рациона. Палочки, сухофрукты, злаковые батончики — здоровое угощение для вашего питомца.', 'lakomstva-dlya-gryzunov.svg', 'lakomstva-dlya-gryzunov.jpg', 33, 30, 1), +(37, 'Клетки и аксессуары', 'kletki-i-aksessuary', 'Просторные клетки и всё необходимое для обустройства дома грызуна. Миски, поилки, домики, колеса для бега и другие аксессуары.', 'kletki-i-aksessuary.svg', 'kletki-i-aksessuary.jpg', 32, 20, 1), +(38, 'Клетки', 'kletki', 'Качественные клетки разных размеров с глубокими поддонами, удобными дверцами и аксессуарами в комплекте. Безопасные и комфортные.', 'kletki.svg', 'kletki.jpg', 37, 10, 1), +(39, 'Поилки и миски', 'poilki-i-miski', 'Удобные поилки и миски для грызунов. Автоматические поилки с шариковым механизмом, керамические миски, которые сложно перевернуть.', 'poilki-i-miski.svg', 'poilki-i-miski.jpg', 37, 20, 1), +(40, 'Наполнители', 'napolniteli-dlya-gryzunov', 'Безопасные наполнители для клеток грызунов. Древесные гранулы, кукурузный наполнитель, бумажные пеллеты — отличная абсорбция и контроль запаха.', 'napolniteli-dlya-gryzunov.svg', 'napolniteli-dlya-gryzunov.jpg', 37, 30, 1), +(41, 'Для птиц', 'dlya-ptic', 'Всё для пернатых друзей: от качественных кормов до просторных клеток и развивающих игрушек. Подарите своим птицам здоровую и счастливую жизнь.', 'dlya-ptic.svg', 'dlia-ptic_image.jpg', NULL, 40, 1), +(42, 'Корма', 'korma-dlya-ptic', 'Сбалансированные кормовые смеси для разных видов птиц. Попугаи, канарейки, амадины — подбирайте питание с учётом потребностей вашего питомца.', 'korma-dlya-ptic.svg', 'korma-dlya-ptic.jpg', 41, 10, 1), +(43, 'Корма для попугаев', 'korma-dlya-popugaev', 'Питательные смеси для попугаев всех видов. Злаки, орехи, семена, сухофрукты — всё для энергии, яркого оперения и долголетия.', 'korma-dlya-popugaev.svg', 'korma-dlia-popugaev_image.jpg', 42, 10, 1), +(44, 'Корма для канареек', 'korma-dlya-kanareek', 'Специализированные корма для канареек с высоким содержанием семян, витаминов и минералов. Для здоровья, красивого пения и яркого оперения.', 'korma-dlya-kanareek.svg', 'korma-dlya-kanareek.jpg', 42, 20, 1), +(45, 'Минеральные камни', 'mineralnye-kamni', 'Минеральные камни и сепии для стачивания клюва и восполнения запаса кальция. Необходимый элемент в клетке каждой птицы.', 'mineralnye-kamni.svg', 'mineralnye-kamni.jpg', 42, 30, 1), +(46, 'Аксессуары', 'aksessuary-dlya-ptic', 'Всё для обустройства комфортной жизни птиц. Удобные клетки, жердочки, купалки, игрушки и другие необходимые аксессуары.', 'aksessuary-dlya-ptic.svg', 'aksessuary-dlya-ptic.jpg', 41, 20, 1), +(47, 'Клетки и жердочки', 'kletki-i-zherdochki', 'Просторные клетки и удобные жердочки из натурального дерева. Обеспечивают комфортное пространство для жизни и возможность для полётов.', 'kletki-i-zherdochki.svg', 'kletki-i-zerdocki_image.jpg', 46, 10, 1), +(48, 'Игрушки для птиц', 'igrushki-dlya-ptic', 'Развивающие игрушки для птиц: качели, колокольчики, лесенки, зеркала. Стимулируют умственную активность и предотвращают скуку.', 'igrushki-dlya-ptic.svg', 'igrushki-dlya-ptic.jpg', 46, 20, 1), +(49, 'Для рыб', 'dlya-ryb', 'Всё для красивого и здорового аквариума. Качественные корма, современное оборудование, декорации и грунт — создайте подводный мир мечты.', 'dlya-ryb.svg', 'dlia-ryb_image.jpg', NULL, 50, 1), +(50, 'Корма для рыб', 'korma-dlya-ryb', 'Сбалансированные корма для аквариумных рыб. Хлопья, гранулы, таблетки для донных рыб — подбирайте питание по типу и размеру обитателей.', 'korma-dlya-ryb.svg', 'korma-dlya-ryb.jpg', 49, 10, 1), +(51, 'Аквариумы', 'akvariumy', 'Аквариумы разных форм и объёмов от проверенных производителей. Полный комплект с подсветкой, фильтром и крышкой для быстрого старта.', 'akvariumy.svg', 'akvariumy.jpg', 49, 20, 1), +(52, 'Фильтрация и помпы', 'filtratsiya-i-pompy', 'Надёжные фильтры, помпы и системы очистки воды. Обеспечивают биологическое, механическое и химическое очищение для здоровой экосистемы.', 'filtratsiya-i-pompy.svg', 'filtratsiya-i-pompy.jpg', 49, 30, 1), +(53, 'Освещение', 'osveschenie', 'Качественное освещение для аквариумов. Светодиодные лампы, люминесцентные светильники, которые подчёркивают красоту рыб и стимулируют рост растений.', 'osveschenie.svg', 'osveschenie.jpg', 49, 40, 1), +(54, 'Грунт и декор', 'grunt-i-dekor', 'Декоративный грунт, коряги, камни, искусственные и живые растения. Создайте уникальный дизайн вашего аквариума.', 'grunt-i-dekor.svg', 'grunt-i-dekor.jpg', 49, 50, 1), +(55, 'Здоровье и уход', 'zdorovie-i-uhod', 'Всё для здоровья и ухода за домашними питомцами. Шампуни, витамины, средства от паразитов и ветеринарные препараты для поддержания отличного самочувствия.', 'zdorovie-i-uhod.svg', 'zdorove-i-uxod_image.jpg', NULL, 60, 1), +(56, 'Шампуни и косметика', 'shampuni-i-kosmetika', 'Качественные шампуни, кондиционеры и косметические средства для ухода за шерстью, кожей, когтями и зубами ваших питомцев.', 'shampuni-i-kosmetika.svg', 'shampuni-i-kosmetika.jpg', 55, 10, 1), +(57, 'Витамины и добавки', 'vitaminy-i-dobavki', 'Витаминно-минеральные комплексы и биологически активные добавки для поддержания здоровья питомцев. Укрепление иммунитета, здоровье суставов, красивая шерсть.', 'vitaminy-i-dobavki.svg', 'vitaminy-i-dobavki_image.jpg', 55, 20, 1), +(58, 'Средства от паразитов', 'sredstva-ot-parazitov', 'Надёжная защита от блох, клещей, гельминтов и других паразитов. Капли, спреи, ошейники, таблетки — выбирайте удобный формат.', 'sredstva-ot-parazitov.svg', 'sredstva-ot-parazitov.jpg', 55, 30, 1), +(59, 'Аптечка', 'aptechka', 'Всё для домашней ветеринарной аптечки: перевязочные материалы, антисептики, средства для обработки ран, глаз, ушей. Будьте готовы к любым ситуациям.', 'aptechka.svg', 'aptecka_image.jpg', 55, 40, 1), +(60, 'Груминг', 'gruming', 'Инструменты для профессионального и домашнего груминга. Расчёски, щётки, пуходёрки, когтерезы, триммеры и машинки для стрижки.', 'gruming.svg', 'gruming.jpg', 55, 50, 1), +(65, 'Одежда', 'odezda', 'Стильная и функциональная одежда для собак всех пород. Дождевики, попоны, комбинезоны, свитера — защита от непогоды и яркий образ для вашего питомца.', NULL, NULL, 6, 40, 1); + +-- -------------------------------------------------------- + +-- +-- Estructura de tabla para la tabla `contacts` +-- + +CREATE TABLE `contacts` ( + `id` bigint(20) UNSIGNED NOT NULL, + `name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'Хвостики и лапки', + `description` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `logo` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `favicon` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `phone` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `email` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `address` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `work_hours` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `telegram` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `whatsapp` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `vkontakte` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `meta_title` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `meta_description` text COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `meta_keywords` text COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- +-- Volcado de datos para la tabla `contacts` +-- + +INSERT INTO `contacts` (`id`, `name`, `description`, `logo`, `favicon`, `phone`, `email`, `address`, `work_hours`, `telegram`, `whatsapp`, `vkontakte`, `meta_title`, `meta_description`, `meta_keywords`, `created_at`, `updated_at`) VALUES +(1, 'Хвостики и лапки', NULL, 'logo.svg', 'favicon.svg', '+7(985)-070-56-33', 'tailandpaws_info@gmail.com', 'г. Челябинск, ул. Дружбы, д. 15', '10:00 - 18:00', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + +-- -------------------------------------------------------- + +-- +-- Estructura de tabla para la tabla `failed_jobs` +-- + +CREATE TABLE `failed_jobs` ( + `id` bigint(20) UNSIGNED NOT NULL, + `uuid` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, + `connection` text COLLATE utf8mb4_unicode_ci NOT NULL, + `queue` text COLLATE utf8mb4_unicode_ci NOT NULL, + `payload` longtext COLLATE utf8mb4_unicode_ci NOT NULL, + `exception` longtext COLLATE utf8mb4_unicode_ci NOT NULL, + `failed_at` timestamp NOT NULL DEFAULT current_timestamp() +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- -------------------------------------------------------- + +-- +-- Estructura de tabla para la tabla `migrations` +-- + +CREATE TABLE `migrations` ( + `id` int(10) UNSIGNED NOT NULL, + `migration` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, + `batch` int(11) NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- +-- Volcado de datos para la tabla `migrations` +-- + +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES +(14, '2014_10_12_000000_create_users_table', 1), +(15, '2014_10_12_100000_create_password_reset_tokens_table', 1), +(16, '2019_08_19_000000_create_failed_jobs_table', 1), +(17, '2019_12_14_000001_create_personal_access_tokens_table', 1), +(18, '2026_03_12_171109_create_roles_table', 1), +(19, '2026_03_12_171110_create_permissions_table', 1), +(20, '2026_03_12_171110_create_role_user_table', 1), +(21, '2026_03_12_171111_create_permission_role_table', 1), +(22, '2026_03_14_102454_create_categories_table', 1), +(23, '2026_03_14_124100_create_brands_table', 1), +(24, '2026_03_14_124103_create_products_table', 1), +(25, '2026_03_14_135714_create_product_variations_table', 1), +(26, '2026_03_14_201029_create_reviews_table', 1), +(27, '2026_03_14_213006_create_contacts_table', 2), +(28, '2026_03_14_213427_add_meta_fields_to_products_table', 2), +(29, '2026_03_16_191828_add_phone_to_users_table', 3), +(30, '2026_03_16_192010_add_email_index_to_users_table', 3), +(31, '2026_03_18_191033_add_unique_constraint_to_role_user_table', 4), +(32, '2026_03_18_192641_add_role_id_to_users_table', 5), +(33, '2026_03_28_191947_fix_products_and_variations_structure', 6), +(34, '2026_04_01_142406_create_cart_items_table', 7), +(35, '2026_04_01_142423_create_orders_table', 7), +(36, '2026_04_01_142428_create_order_items_table', 7); + +-- -------------------------------------------------------- + +-- +-- Estructura de tabla para la tabla `orders` +-- + +CREATE TABLE `orders` ( + `id` bigint(20) UNSIGNED NOT NULL, + `order_number` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, + `user_id` bigint(20) UNSIGNED DEFAULT NULL, + `customer_name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, + `customer_email` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, + `customer_phone` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, + `shipping_address` text COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `subtotal` decimal(10,2) NOT NULL, + `shipping_cost` decimal(10,2) NOT NULL DEFAULT 0.00, + `discount` decimal(10,2) NOT NULL DEFAULT 0.00, + `total` decimal(10,2) NOT NULL, + `payment_method` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'cash', + `payment_status` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'pending', + `delivery_method` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'courier', + `delivery_status` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'pending', + `comment` text COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- +-- Volcado de datos para la tabla `orders` +-- + +INSERT INTO `orders` (`id`, `order_number`, `user_id`, `customer_name`, `customer_email`, `customer_phone`, `shipping_address`, `subtotal`, `shipping_cost`, `discount`, `total`, `payment_method`, `payment_status`, `delivery_method`, `delivery_status`, `comment`, `created_at`, `updated_at`) VALUES +(1, 'ORD-20260402-69CDC5BBD6FEB', 1, 'duckinahatt', 'margaritaborodovskih@gmail.com', '+7(909)-090-86-13', NULL, '5403.00', '0.00', '0.00', '5403.00', 'cash', 'paid', 'pickup', 'delivered', NULL, '2026-04-01 22:26:19', '2026-04-06 06:04:06'), +(2, 'ORD-20260406-69D3808EA5C09', 1, 'duckinahat', 'margaritaborodovskih@gmail.com', '+7(909)-090-86-13', NULL, '5208.00', '0.00', '0.00', '5208.00', 'cash', 'pending', 'pickup', 'pending', NULL, '2026-04-06 06:44:46', '2026-04-06 06:44:46'), +(3, 'ORD-20260406-69D380D6CE7B4', 1, 'duckinahat', 'margaritaborodovskih@gmail.com', '+7(909)-090-86-13', NULL, '1435.00', '0.00', '0.00', '1435.00', 'online', 'pending', 'pickup', 'pending', NULL, '2026-04-06 06:45:58', '2026-04-06 06:45:58'); + +-- -------------------------------------------------------- + +-- +-- Estructura de tabla para la tabla `order_items` +-- + +CREATE TABLE `order_items` ( + `id` bigint(20) UNSIGNED NOT NULL, + `order_id` bigint(20) UNSIGNED NOT NULL, + `variation_id` bigint(20) UNSIGNED NOT NULL, + `product_name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, + `variation_name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `sku` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `quantity` int(11) NOT NULL, + `price` decimal(10,2) NOT NULL, + `total` decimal(10,2) NOT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- +-- Volcado de datos para la tabla `order_items` +-- + +INSERT INTO `order_items` (`id`, `order_id`, `variation_id`, `product_name`, `variation_name`, `sku`, `quantity`, `price`, `total`, `created_at`, `updated_at`) VALUES +(1, 1, 60, 'AlphaPet Сухой корм для стерилизованных кошек', 'AlphaPet Сухой корм для стерилизованных кошек, с ягненком и индейкой, 1,5 кг', '1053741', 1, '1969.00', '1969.00', '2026-04-01 22:26:19', '2026-04-01 22:26:19'), +(2, 1, 53, 'Klicker Adult Sensitive Digestion Сухой корм для кошек с чувствительным пищеварением', 'Klicker Adult Sensitive Digestion Сухой корм для кошек с чувствительным пищеварением, с ягненком, 1 кг', '1065459', 1, '1435.00', '1435.00', '2026-04-01 22:26:19', '2026-04-01 22:26:19'), +(3, 1, 46, 'Royal Canin Mini Adult Сухой корм для взрослых собак мелких размеров в возрасте от 10 месяцев до 8 лет', 'Royal Canin Mini Adult Сухой корм для взрослых собак мелких размеров в возрасте от 10 месяцев до 8 лет, 2 кг', '1002774', 1, '1999.00', '1999.00', '2026-04-01 22:26:19', '2026-04-01 22:26:19'), +(4, 2, 57, 'Royal Canin Sterilised 37 Regular Сухой корм для стерилизованных кошек с 1 до 7 лет', 'Royal Canin Sterilised 37 Regular Сухой корм для стерилизованных кошек с 1 до 7 лет, 400 гр.', '1000724', 2, '635.00', '1270.00', '2026-04-06 06:44:46', '2026-04-06 06:44:46'), +(5, 2, 60, 'AlphaPet Сухой корм для стерилизованных кошек', 'AlphaPet Сухой корм для стерилизованных кошек, с ягненком и индейкой, 1,5 кг', '1053741', 2, '1969.00', '3938.00', '2026-04-06 06:44:46', '2026-04-06 06:44:46'), +(6, 3, 53, 'Klicker Adult Sensitive Digestion Сухой корм для кошек с чувствительным пищеварением', 'Klicker Adult Sensitive Digestion Сухой корм для кошек с чувствительным пищеварением, с ягненком, 1 кг', '1065459', 1, '1435.00', '1435.00', '2026-04-06 06:45:58', '2026-04-06 06:45:58'); + +-- -------------------------------------------------------- + +-- +-- Estructura de tabla para la tabla `password_reset_tokens` +-- + +CREATE TABLE `password_reset_tokens` ( + `email` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, + `token` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, + `created_at` timestamp NULL DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- -------------------------------------------------------- + +-- +-- Estructura de tabla para la tabla `permissions` +-- + +CREATE TABLE `permissions` ( + `id` bigint(20) UNSIGNED NOT NULL, + `name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, + `slug` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, + `group` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `description` text COLLATE utf8mb4_unicode_ci DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- +-- Volcado de datos para la tabla `permissions` +-- + +INSERT INTO `permissions` (`id`, `name`, `slug`, `group`, `description`) VALUES +(1, 'Доступ в личный кабинет', 'cabinet_access', 'cabinet', 'Возможность входить в личный кабинет и просматривать свои данные'), +(2, 'Просматривать заказы', 'view_orders', 'orders', 'Просмотр списка заказов'), +(3, 'Редактировать заказы', 'edit_orders', 'orders', 'Редактирование заказов (статус, удаление, подтверждение)'), +(4, 'Оформлять заказы', 'checkout', 'orders', 'Оформление заказов на сайте'), +(5, 'Управлять корзиной', 'manage_cart', 'cart', 'Добавление/удаление товаров из корзины'), +(6, 'Добавлять товары', 'create_products', 'products', 'Создание новых товаров'), +(7, 'Редактировать товары', 'edit_products', 'products', 'Редактирование товаров'), +(8, 'Управлять количеством товара', 'manage_stock', 'products', 'Изменение остатков товаров на складе'), +(9, 'Просматривать пользователей', 'view_users', 'users', 'Просмотр списка пользователей'), +(10, 'Создавать пользователей', 'create_users', 'users', 'Создание новых пользователей'), +(11, 'Управлять ролями', 'manage_roles', 'rbac', 'Создание/редактирование ролей'), +(12, 'Управлять правами', 'manage_permissions', 'rbac', 'Создание/редактирование прав доступа'), +(13, 'Редактировать данные магазина', 'edit_shop_settings', 'shop', 'Изменение настроек магазина'), +(14, 'Доступ в админ-панель', 'admin_access', 'admin', 'Возможность входить в административную панель'); + +-- -------------------------------------------------------- + +-- +-- Estructura de tabla para la tabla `permission_role` +-- + +CREATE TABLE `permission_role` ( + `id` bigint(20) UNSIGNED NOT NULL, + `permission_id` bigint(20) UNSIGNED NOT NULL, + `role_id` bigint(20) UNSIGNED NOT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- +-- Volcado de datos para la tabla `permission_role` +-- + +INSERT INTO `permission_role` (`id`, `permission_id`, `role_id`, `created_at`, `updated_at`) VALUES +(1, 14, 2, '2026-03-14 18:11:29', '2026-03-14 18:11:29'), +(2, 1, 2, '2026-03-14 18:11:29', '2026-03-14 18:11:29'), +(3, 4, 2, '2026-03-14 18:11:29', '2026-03-14 18:11:29'), +(4, 6, 2, '2026-03-14 18:11:29', '2026-03-14 18:11:29'), +(5, 10, 2, '2026-03-14 18:11:29', '2026-03-14 18:11:29'), +(6, 3, 2, '2026-03-14 18:11:29', '2026-03-14 18:11:29'), +(7, 7, 2, '2026-03-14 18:11:29', '2026-03-14 18:11:29'), +(8, 13, 2, '2026-03-14 18:11:29', '2026-03-14 18:11:29'), +(9, 5, 2, '2026-03-14 18:11:29', '2026-03-14 18:11:29'), +(10, 12, 2, '2026-03-14 18:11:29', '2026-03-14 18:11:29'), +(11, 11, 2, '2026-03-14 18:11:29', '2026-03-14 18:11:29'), +(12, 8, 2, '2026-03-14 18:11:29', '2026-03-14 18:11:29'), +(13, 2, 2, '2026-03-14 18:11:29', '2026-03-14 18:11:29'), +(14, 9, 2, '2026-03-14 18:11:29', '2026-03-14 18:11:29'), +(15, 14, 3, '2026-03-14 18:11:29', '2026-03-14 18:11:29'), +(16, 1, 3, '2026-03-14 18:11:29', '2026-03-14 18:11:29'), +(17, 6, 3, '2026-03-14 18:11:29', '2026-03-14 18:11:29'), +(18, 7, 3, '2026-03-14 18:11:29', '2026-03-14 18:11:29'), +(19, 2, 3, '2026-03-14 18:11:29', '2026-03-14 18:11:29'), +(20, 14, 4, '2026-03-14 18:11:29', '2026-03-14 18:11:29'), +(21, 1, 4, '2026-03-14 18:11:29', '2026-03-14 18:11:29'), +(22, 4, 4, '2026-03-14 18:11:29', '2026-03-14 18:11:29'), +(23, 3, 4, '2026-03-14 18:11:29', '2026-03-14 18:11:29'), +(24, 2, 4, '2026-03-14 18:11:29', '2026-03-14 18:11:29'), +(25, 9, 4, '2026-03-14 18:11:29', '2026-03-14 18:11:29'), +(26, 14, 5, '2026-03-14 18:11:29', '2026-03-14 18:11:29'), +(27, 1, 5, '2026-03-14 18:11:29', '2026-03-14 18:11:29'), +(28, 8, 5, '2026-03-14 18:11:29', '2026-03-14 18:11:29'), +(29, 2, 5, '2026-03-14 18:11:29', '2026-03-14 18:11:29'), +(30, 1, 6, '2026-03-14 18:11:29', '2026-03-14 18:11:29'), +(31, 4, 6, '2026-03-14 18:11:29', '2026-03-14 18:11:29'), +(32, 5, 6, '2026-03-14 18:11:29', '2026-03-14 18:11:29'); + +-- -------------------------------------------------------- + +-- +-- Estructura de tabla para la tabla `personal_access_tokens` +-- + +CREATE TABLE `personal_access_tokens` ( + `id` bigint(20) UNSIGNED NOT NULL, + `tokenable_type` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, + `tokenable_id` bigint(20) UNSIGNED NOT NULL, + `name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, + `token` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL, + `abilities` text COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `last_used_at` timestamp NULL DEFAULT NULL, + `expires_at` timestamp NULL DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- -------------------------------------------------------- + +-- +-- Estructura de tabla para la tabla `products` +-- + +CREATE TABLE `products` ( + `id` bigint(20) UNSIGNED NOT NULL, + `name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, + `slug` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, + `description` text COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `brand_id` bigint(20) UNSIGNED DEFAULT NULL, + `category_id` bigint(20) UNSIGNED NOT NULL, + `is_active` tinyint(1) NOT NULL DEFAULT 1, + `meta_title` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `meta_description` text COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `meta_keywords` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- +-- Volcado de datos para la tabla `products` +-- + +INSERT INTO `products` (`id`, `name`, `slug`, `description`, `brand_id`, `category_id`, `is_active`, `meta_title`, `meta_description`, `meta_keywords`, `created_at`, `updated_at`) VALUES +(12, 'Rungo Ошейник нейлоновый с ручкой для собак K-9, L', 'rungo-oseinik-neilonovyi-s-ruckoi-dlia-sobak-k-9-l', 'Ошейник K-9 от бренда RUNGO – надежный аксессуар для дополнительного контроля собаки на прогулках, в поездках, при дрессировке.\r\nРазмер: обхват шеи 42-65 см, ширина 3,8 см', 10, 7, 1, 'Вот вариант для этого товара: Meta Title: Rungo ошейник нейлоновый с ручкой K-9 для собак, L', 'Прочный нейлоновый ошейник Rungo K-9 с удобной ручкой для контроля собаки. Размер L, надежная посадка и комфорт в ежедневном использовании.', 'rungo ошейник, ошейник для собак, нейлоновый ошейник, ошейник с ручкой, rungo k-9, ошейник l, ошейник для крупных собак, амуниция для собак', '2026-03-28 18:14:26', '2026-03-28 18:14:26'), +(21, 'Grandin Hypoallergenic ягненок', 'grandin-hypoallergenic-iagnenok', 'Гипоаллергенный сухой корм для собак всех пород с ягненком. Беззерновой суперпремиум класс без курицы и говядины.', 5, 3, 1, 'Grandin Hypoallergenic сухой корм ягненок', 'Гипоаллергенный корм Grandin с ягненком для собак всех пород без злаков суперпремиум класс', 'grandin hypoallergenic,сухой корм собак,ягненок,гипоаллергенный корм собак', '2026-03-29 12:20:24', '2026-03-29 13:18:29'), +(22, 'Klicker Adult', 'klicker-adult', 'Сухой корм для собак мелких пород с лососем. Высокая усвояемость и поддержка пищеварения.', 11, 3, 1, 'Klicker Adult сухой корм лосось мелкие породы', 'Премиум корм Klicker для мелких пород с лососем поддержка пищеварения', 'klicker корм,сухой корм мелких собак,лосось собак', '2026-03-29 12:20:24', '2026-03-29 13:41:32'), +(23, 'Royal Canin Mini Adult Сухой корм для взрослых собак мелких размеров в возрасте от 10 месяцев до 8 лет', 'royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let', 'Сухой корм для взрослых собак мелких пород. Поддержка кожи и шерсти, высокая калорийность.', 1, 3, 1, 'Royal Canin Mini Adult Сухой корм для взрослых собак мелких размеров в возрасте от 10 месяцев до 8 лет', 'Корм Royal Canin для мелких взрослых собак поддержка кожи шерсти', 'royal canin mini adult,сухой корм собак мелких пород', '2026-03-29 12:20:24', '2026-03-29 14:07:02'), +(24, 'АВВА Adult Сухой корм на основе свежего мяса для взрослых собак мелких пород, с ягненком и индейкой', 'avva-adult-suxoi-korm-na-osnove-svezego-miasa-dlia-vzroslyx-sobak-melkix-porod-s-iagnenkom-i-indeikoi', 'Сухой корм суперпремиум с ягненком для всех пород. Естественные ингредиенты без ГМО.', 2, 3, 1, 'АВВА Adult Сухой корм на основе свежего мяса для взрослых собак мелких пород, с ягненком и индейкой', 'ABBA суперпремиум корм с ягненком для собак всех пород натуральные ингредиенты', 'abba premium,ягненок сухой корм,суперпремиум собак', '2026-03-29 12:20:24', '2026-03-29 17:16:40'), +(25, 'AlphaPet Adult Monoprotein Сухой корм для собак средних и крупных пород, белая рыба', 'alphapet-adult-monoprotein-suxoi-korm-dlia-sobak-srednix-i-krupnyx-porod-belaia-ryba', 'Здоровый и активный питомец – главная задача каждого хозяина. Команда AlphaPet® создает и производит полезные корма высшей категории качества Holistiс, давая возможность каждому владельцу быть уверенным в правильном выборе кормления для своего животного. AlphaPet® MONOPROTEIN на основе белой рыбы - благодаря единственному источнику животного белка, подойдет животным с пищевой непереносимостью.\r\n\r\nБелая рыба - отличный источник легкоусвояемого белка, микроэлементов: железа, селена, цинка, йода и витаминов B12 и D. Также рыба ценный источник ненасыщенных жирных кислот Омега 3, благодаря которым кожа и шерсть животных будут в прекрасном состоянии. Диетическое мясо белой рыбы хорошо сбалансированно и несет в себе максимум пользы для питомцев.\r\n\r\nПриготовленно по инновационной технологии из свежей белой рыбы и с содержанием большого процента животного белка!', 12, 3, 1, 'AlphaPet Adult Monoprotein Сухой корм для собак средних и крупных пород, белая рыба', 'Здоровый и активный питомец – главная задача каждого хозяина. Команда AlphaPet® создает и производит полезные корма высшей категории качества Holistiс, давая возможность каждому владельцу быть уверенным в правильном выборе кормления для своего животного. AlphaPet® MONOPROTEIN на основе белой рыбы - благодаря единственному источнику животного белка, подойдет животным с пищевой непереносимостью.\r\n\r\nБелая рыба - отличный источник легкоусвояемого белка, микроэлементов: железа, селена, цинка, йода и витаминов B12 и D. Также рыба ценный источник ненасыщенных жирных кислот Омега 3, благодаря которым кожа и шерсть животных будут в прекрасном состоянии. Диетическое мясо белой рыбы хорошо сбалансированно и несет в себе максимум пользы для питомцев.\r\n\r\nПриготовленно по инновационной технологии из свежей белой рыбы и с содержанием большого процента животного белка!', 'alphapet monoprotein, сухой корм собак, белая рыба корм, корм средних пород, корм крупных собак, монобелковый корм, корм при аллергии, холистик корм собак, alphapet adult', '2026-03-29 12:20:24', '2026-03-29 18:45:42'), +(26, 'Ownat Grain Free Just Сухой корм беззерновой для собак', 'ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak', 'В основе корма лежит максимальное количество натуральных ингредиентов: свежее мясо, свежие овощи и фрукты, а также водоросли, что позволяет донести до питомца пользу продуктов в том виде, в каком задумала природа.', 4, 3, 1, 'Ownat Grain Free Just Сухой корм беззерновой для собак, с лососем и морепродуктами', 'Ownat Grain Free Just беззерновой сухой корм для собак. Доступны вкусы: лосось и морепродукты, курица, утка, ягненок. 70% мяса, 20% свежего, холистик класс беззлаковый.', 'ownat grain free just, беззерновой корм собак, корм лосось морепродукты, ownat курица, ownat утка, ownat ягненок, сухой корм беззлаковый, холистик корм собак, ownat grain free вкусы', '2026-03-29 12:20:24', '2026-03-29 19:03:16'), +(27, 'Klicker Adult Sensitive Digestion Сухой корм для кошек с чувствительным пищеварением', 'klicker-adult-sensitive-digestion-suxoi-korm-dlia-kosek-s-cuvstvitelnym-pishhevareniem', 'Сухой корм для кошек с чувствительным пищеварением. Легкоусвояемые ингредиенты.', 11, 21, 1, 'Klicker Adult Sensitive Digestion Сухой корм для кошек с чувствительным пищеварением, с ягненком, 1 кг', 'Корм для кошек с чувствительным пищеварением Klicker легкоусвояемые ингредиенты', 'klicker sensitive,сухой корм кошек,пищеварение кошки', '2026-03-29 12:20:24', '2026-03-29 19:06:53'), +(28, 'Grandin Holistic Влажный корм (консервы) для взрослых кошек', 'grandin-holistic-vlaznyi-korm-konservy-dlia-vzroslyx-kosek', 'GRANDIN Holistic Health Privilege в желе - высококачественный консервированный корм для взрослых кошек с повышенным содержанием рыбы.', 5, 22, 1, 'Grandin Holistic влажный корм для взрослых кошек, консервы без злаков', 'Grandin Holistic влажный корм (консервы) для взрослых кошек. Высокое содержание мяса, без злаков и ГМО, нежное желе/соус, подходит для привередливых и чувствительных кошек.', 'grandin holistic, влажный корм для кошек, консервы для кошек, корм для взрослых кошек, беззлаковый влажный корм, grandin консервы, тунец в желе, курица в желе, индейка в соусе', '2026-03-29 12:20:24', '2026-03-29 19:12:47'), +(29, 'Royal Canin Sterilised 37 Regular Сухой корм для стерилизованных кошек с 1 до 7 лет', 'royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let', 'Поддержание оптимального веса. Помогает поддерживать оптимальный вес благодаря ограниченному содержанию жиров, оптимальному уровню белков и входящему в состав корма L-карнитину.\r\nПоддержание мышечного тонуса. Помогает поддерживать мышечный тонус благодаря повышенному содержанию белка и L-карнитину.\r\nПоддержание здоровья мочевыделительной системы. Способствует профилактике заболеваний мочевыделительной системы за счет сбалансированного содержания минеральных веществ и поддержания рН мочи.', 1, 21, 1, 'Royal Canin Sterilised сухой корм кошки', 'Корм Royal Canin для стерилизованных кошек поддержка мочевыводящей системы', 'royal canin sterilised,корм стерилизованных кошек', '2026-03-29 12:20:24', '2026-03-29 19:22:45'), +(30, 'AlphaPet Сухой корм для стерилизованных кошек', 'alphapet-suxoi-korm-dlia-sterilizovannyx-kosek', '80% мясного белка;\r\n30% свежего мяса;\r\nНизкое содержание жира и оптимальный уровень клетчатки помогают ограничивать набор избыточного веса;\r\nБаланс магния, кальция и фосфора для здоровья мочевыделительной системы;\r\nУникальный комплекс натуральных добавок AlphaPetBIO®;\r\nНатуральные ингредиенты;\r\nНе содержит ГМО, искусственных красителей и ароматизаторов;\r\nИнгредиенты, пригодные в пищу человеку, уровня Human Grade;\r\nБережная технология, сохраняющая вкусовые и питательные свойства свежего мяса.', 12, 21, 1, 'AlphaPet Сухой корм для стерилизованных кошек, с ягненком и индейкой', 'AlphaPet Superpremium Sterilised сухой корм для стерилизованных кошек с ягненком и индейкой. 80% мясного белка, 30% свежего мяса, контроль веса, профилактика МКБ.', 'alphapet sterilised, корм стерилизованных кошек, ягненок индейка корм, alphapet суперпремиум, сухой корм кошки стерилизованные, корм мкб кошки, alphapet ягненок', '2026-03-29 12:20:24', '2026-03-29 19:44:22'), +(31, 'Tetra Min Holiday корм желе', 'tetra-min-holiday-korm-zele', 'Сухой корм для кошек с курицей. Полнорационное питание.', 13, 50, 1, 'Tetra Min Holiday корм желе аквариумные рыбы 14 дней 30г', 'Tetra Min Holiday гелевый корм-желе для аквариумных рыб на 14 дней. 100% съедобный блок с дафнией, витаминами, минералами. Не мутит воду, немецкое качество.', 'tetra min holiday, корм желе рыбы, корм на отпуск 14 дней, tetra holiday желе, аквариумный корм желе, корм дафния рыбы, tetra 30г желе', '2026-03-29 12:20:24', '2026-03-29 19:58:14'), +(32, 'Little One Корм для морских свинок', 'little-one-korm-dlia-morskix-svinok', 'Полнорационный корм с добавлением витаминов и минеральных веществ. Разработан с учетом специфических потребностей морских свинок и содержит повышенное количество витамина С, жизненно необходимого для их здоровья.\r\n\r\nРазнообразный состав корма включает в себя травяные гранулы, воздушные ингредиенты и хлопья, семена, овощи и редкие плоды.', 14, 34, 1, 'Little One корм для морских свинок полнорационный витамин С', 'Little One корм для морских свинок с повышенным содержанием витамина С. Полнорационный состав: травяные гранулы, семена, овощи, воздушные хлопья. 35-50г/сутки на зверька.', 'little one морские свинки, корм морских свинок, корм с витамином с, little one грызуны, полнорационный корм свинки, корм травяные гранулы свинок', '2026-03-29 12:20:24', '2026-03-29 20:00:36'), +(34, 'Little One Корм для морских свинок Зелёная долина', 'little-one-korm-dlia-morskix-svinok-zelenaia-dolina', 'Little One «Зеленая долина» — это полнорационный беззерновой корм для морских свинок, в состав которого входит 60 разновидностей трав.\r\nДополнительно его состав обогащен шиповником и лепестками розы, которые богаты витамином С, а также добавлены другие полезные и любимые морскими свинками ингредиенты – тыква, пастернак, яблоко и др.\r\nБлагодаря использованию специальной технологии холодного прессования, травяные гранулы сохранили все витамины и минералы, входящие в состав растений.\r\nКорм богат длинными волокнами клетчатки, а также обогащен фруктоолигосахаридами для поддержания роста полезной микрофлоры в кишечнике и жирными кислотами ω-3 и ω-6 для здоровья кожи и блестящей шерсти.\r\nКорм отлично подходит для диетического питания.', 14, 34, 1, 'Little One Зелёная долина корм морских свинок разнотравье 750г', 'Little One \"Зелёная долина\" корм для морских свинок из разнотравья 750г. 60 трав холодного прессования, 24% клетчатки, без зерна, витамин С, диетический для пищеварения.', 'little one зеленая долина, корм морских свинок разнотравье, корм без зерна свинки, little one 750г, корм 60 трав свинок, диетический корм свинок', '2026-03-29 12:20:24', '2026-03-29 20:05:17'), +(35, 'Rungo Комбинезон теплый для собак породы мопс', 'rungo-kombinezon-teplyi-dlia-sobak-porody-mops', 'Теплый комбинезон для собак от бренд RUNGO – простой и эффективный способ сделать прогулку с собакой комфортной в любую непогоду. Комбинезон защитит Вашего питомца от сырости, загрязнений и переохлаждения.', 10, 65, 1, 'Ferplast парашютный ошейник собака', 'Легкий парашютный ошейник Ferplast для собак', 'ferplast парашютный,ошейник легкий собака', '2026-03-29 12:20:24', '2026-03-29 20:12:19'), +(36, 'Rogz ошейник Utility', 'rogz-oseinik-utility', 'Усиленный нейлон с мягкой подкладкой. 5 точек фиксации.', 16, 7, 1, 'Rogz Utility ошейник собака', 'Прочный ошейник Rogz Utility усиленный нейлон', 'rogz utility,ошейник собака усиленный', '2026-03-29 12:20:24', '2026-03-30 09:47:30'), +(37, 'Hunter кожаный плетеный', 'hunter-kozhanii-pletenyi', 'Ручная плетка из натуральной кожи. Эксклюзивный дизайн.', NULL, 7, 1, 'Hunter плетеный кожаный ошейник', 'Эксклюзивный плетеный кожаный ошейник Hunter', 'hunter плетеный,кожаный ошейник эксклюзив', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(38, 'Petzl ошейник альпинистский', 'petzl-alpinistskii-osheynik', 'Сверхпрочный для рабочих собак. Используется кинологами.', NULL, 7, 1, 'Petzl альпинистский ошейник собака', 'Сверхпрочный ошейник Petzl для рабочих собак', 'petzl альпинистский,ошейник рабочие собаки', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(39, 'Ever Clean Extra Strength', 'ever-clean-extra-strength', 'Древесный наполнитель для кошек. Сильная комкуемость отличная нейтрализация запаха.', NULL, 25, 1, 'Ever Clean Extra Strength древесный наполнитель', 'Премиум древесный наполнитель Ever Clean сильная комкуемость', 'ever clean,древесный наполнитель кошки,премиум наполнитель', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(40, 'Cat\'s Best Original', 'cats-best-original', 'Натуральный гранулированный наполнитель из волокон целлюлозы. 100% биоразлагаемый.', NULL, 25, 1, 'Cat\'s Best Original древесный наполнитель', 'Натуральный древесный наполнитель Cat\'s Best из целлюлозы', 'cats best,древесный натуральный,наполнитель кошки', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(41, 'Barsik Premium древесный', 'barsik-premium-drevesnyi', 'Древесный наполнитель из опилок хвойных пород. Высокая впитываемость.', NULL, 25, 1, 'Barsik Premium древесный наполнитель кошки', 'Древесный наполнитель Barsik Premium из хвойных пород', 'barsik древесный,наполнитель кошки хвойный', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(42, 'Perfect Fit древесный', 'perfect-fit-drevesnyi', 'Комкующийся древесный наполнитель. Натуральный состав без химии.', NULL, 25, 1, 'Perfect Fit древесный наполнитель', 'Комкующийся древесный наполнитель Perfect Fit натуральный', 'perfect fit древесный,комкующийся наполнитель', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(43, 'Сибирский лес премиум', 'sibirskii-les-premium', 'Премиум древесный наполнитель. Сильный контроль запаха.', NULL, 25, 1, 'Сибирский лес премиум древесный наполнитель', 'Премиум древесный наполнитель Сибирский лес контроль запаха', 'сибирский лес,древесный премиум наполнитель', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(44, 'Травка-Сено древесный', 'travka-seno-drevesnyi', 'Натуральный древесный наполнитель с экстрактами трав.', NULL, 25, 1, 'Травка-Сено древесный наполнитель кошки', 'Древесный наполнитель Травка-Сено с экстрактами трав', 'травка сено,древесный с травами наполнитель', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(45, 'Kong Classic красный', 'kong-classic-krasnyi', 'Классическая резиновая игрушка для жевания и апортировки. Неотъемлемый элемент дрессировки.', NULL, 11, 1, 'Kong Classic красный мячик собака', 'Резиновая игрушка Kong Classic для собак жевание апортировка', 'kong classic,мячик собака резиновый,игрушка для жевания', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(46, 'Trixie теннисный мячик', 'trixie-tennisnyi-myachik', 'Теннисный мячик для собак. Прочная резина с войлоком.', NULL, 11, 1, 'Trixie теннисный мячик собака', 'Теннисный мячик Trixie для апортировки собак', 'trixie теннисный,мячик собака апортировка', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(47, 'Ferplast фрисби Flyer', 'ferplast-frisbi-flyer', 'Пластиковый фрисби для активных игр на улице. Легкий и прочный.', NULL, 11, 1, 'Ferplast Flyer фрисби собака', 'Фрисби Ferplast Flyer для собак активные игры', 'ferplast фрисби,фрисби собака уличные игры', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(48, 'K9 Granit мячик', 'k9-granit-myachik', 'Прочный резиновый мячик для сильных челюстей. Выдерживает давление.', NULL, 11, 1, 'K9 Granit мячик собака', 'Резиновый мячик K9 Granit для мощных собак', 'k9 granit,мячик собака прочный челюсти', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(49, 'Chuckit! Ultra мячик', 'chuckit-ultra-myachik', 'Яркий мячик для метателя Chuckit. Увеличенная дальность броска.', NULL, 11, 1, 'Chuckit Ultra мячик собака метатель', 'Мячик Chuckit Ultra для метателя собак', 'chuckit ultra,мячик метатель собака', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(50, 'Rogz Grinz мячик зубастый', 'rogz-grinz-myachik-zubastyi', 'Резиновый мячик с зубастой мордочкой. Массаж десен.', NULL, 11, 1, 'Rogz Grinz зубастый мячик собака', 'Мячик Rogz Grinz массаж десен собак', 'rogz grinz,мячик зубастый собака десны', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(51, 'Trixie Ортопедическая лежанка', 'trixie-ortopedicheskaya-lezhanka', 'Лежанка с ортопедической пеной memory foam. Для собак с проблемами суставов.', NULL, 14, 1, 'Trixie ортопедическая лежанка собака', 'Ортопедическая лежанка Trixie memory foam для собак суставы', 'trixie ортопедическая,лежанка memory foam,собаки суставы', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(52, 'Good Boy плед лежанка', 'good-boy-pled-lezhanka', 'Мягкая лежанка-плед из флиса. Съемный чехол машинная стирка.', NULL, 14, 1, 'Good Boy плед лежанка собака', 'Лежанка-плед Good Boy флис съемный чехол', 'good boy плед,лежанка флис собака', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(53, 'Ferplast лежанка Carlotta', 'ferplast-lezhanka-carlotta', 'Круглая лежанка с бортиками. Машинная стирка 30°C.', NULL, 14, 1, 'Ferplast Carlotta лежанка собака', 'Круглая лежанка Ferplast Carlotta с бортиками', 'ferplast carlotta,лежанка круглая бортики', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(54, 'Hunter лежанка Bavaria', 'hunter-lezhanka-bavaria', 'Немецкое качество. Прочная ткань водоотталкивающая подкладка.', NULL, 14, 1, 'Hunter Bavaria лежанка собака', 'Лежанка Hunter Bavaria немецкое качество водоотталкивающая', 'hunter bavaria,лежанка немецкая качество', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(55, 'PetFusion ортопедическая', 'petfusion-ortopedicheskaya', 'Лежанка с ортопедической пеной. Экологичные материалы гипоаллергенная.', NULL, 14, 1, 'PetFusion ортопедическая лежанка собака', 'Ортопедическая лежанка PetFusion экологичные гипоаллергенная', 'petfusion ортопедическая,лежанка экологичная', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(56, 'Laifugy лежанка домик', 'laifugy-lezhanka-domik', 'Лежанка-домик с крышей. Для маленьких собак и щенков.', NULL, 14, 1, 'Laifugy лежанка домик собака', 'Лежанка-домик Laifugy для маленьких собак щенков', 'laifugy домик,лежанка маленькие собаки', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(57, 'GRANDORF Holistic Adult Sterilised Сухой корм для взрослых стерилизованных кошек', 'grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek', 'GRANDORF Holistic Adult Sterilised — суперпремиальный беззерновой сухой корм для взрослых стерилизованных кошек.', 15, 21, 1, 'GRANDORF Holistic Adult Sterilised сухой корм стерилизованные кошки', 'GRANDORF Holistic Adult Sterilised сухой корм для взрослых стерилизованных кошек. 70% мяса (индейка+ягненок), беззерновой, таурин, L-карнитин, контроль веса, здоровье мочевыводящей системы.', 'grandorf sterilised, корм стерилизованных кошек, grandorf holistic adult, беззерновой корм кошки, корм с таурином кошки, grandorf индейка ягненок, holistic корм стерилизованные', '2026-03-30 06:08:32', '2026-03-30 06:08:32'), +(58, 'Ownat Adult Sterilized Grain Free Prime Сухой корм для стерилизованных кошек', 'ownat-adult-sterilized-grain-free-prime-suxoi-korm-dlia-sterilizovannyx-kosek', 'OWNAT GRAIN FREE PRIME STERILIZES – это полнорационный сбалансированный корм для стерилизованных кошек и кастрированных котов.\r\nПовышенное содержание белков: мясные и рыбные ингредиенты в составе обеспечивают чувство сытости и наполняют организм незаменимыми аминокислотами.\r\nКорм содержит полный комплекс необходимых микро- и макроэлементов.\r\nНе содержит злаки, поэтому снижает риск возникновения пищевой аллергии.', 4, 21, 1, 'Ownat Adult Sterilized Grain Free Prime корм стерилизованные кошки', 'Ownat Grain Free Prime Adult Sterilized сухой корм для стерилизованных кошек. Беззерновой, 70% мяса, таурин, L-карнитин, контроль веса, профилактика МКБ, суперпремиум.', 'ownat grain free, корм стерилизованных кошек, ownat sterilized prime, беззерновой корм кошки, ownat кролик тунец, корм с таурином кошки, super premium sterilized', '2026-03-30 06:56:38', '2026-03-30 06:56:38'), +(59, 'AlphaPet WOW Сухой корм для стерилизованных кошек', 'alphapet-wow-suxoi-korm-dlia-sterilizovannyx-kosek', '80% мясного белка;\r\nСвежее мясо в составе;\r\nНизкое содержание жира и оптимальный уровень клетчатки помогают ограничивать набор избыточного веса;\r\nБаланс магния, кальций и фосфора для здоровья мочевыделительной системы;\r\nУникальный комплекс натуральных добавок AlphaPetBIO®;\r\nНатуральные ингредиенты;\r\nНе содержит ГМО, искусственных красителей и ароматизаторов;\r\nИнгредиенты, пригодные в пищу человеку, уровня Human Grade;\r\nБережная технология, сохраняющая вкусовые и питательные свойства свежего мяса.', 12, 21, 1, 'AlphaPet WOW Сухой корм стерилизованных кошек с индейкой', 'AlphaPet WOW Grain Free сухой корм для стерилизованных кошек с индейкой. 65% мяса, беззерновой, L-карнитин, таурин 0.2%, контроль веса, здоровье мочевыводящей системы, суперпремиум класс.', 'alphapet wow, корм стерилизованных кошек индейка, alphapet grain free, сухой корм кошки стерилизованные, alphapet l-карнитин, корм с таурином кошки, суперпремиум стерилизованные', '2026-03-30 07:01:27', '2026-03-30 07:05:45'); + +-- -------------------------------------------------------- + +-- +-- Estructura de tabla para la tabla `product_attributes` +-- + +CREATE TABLE `product_attributes` ( + `id` bigint(20) UNSIGNED NOT NULL, + `product_id` bigint(20) UNSIGNED NOT NULL, + `key` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, + `value` text COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- +-- Volcado de datos para la tabla `product_attributes` +-- + +INSERT INTO `product_attributes` (`id`, `product_id`, `key`, `value`, `created_at`, `updated_at`) VALUES +(19, 12, 'Размер питомца', 'Все размеры', '2026-03-28 18:14:26', '2026-03-28 18:14:26'), +(20, 12, 'Материал изготовления', 'Нейлон', '2026-03-28 18:14:26', '2026-03-28 18:14:26'), +(71, 37, 'Материал', 'Натуральная кожа (плетеный)', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(72, 38, 'Материал', 'Альпинистский нейлон', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(73, 39, 'Тип наполнителя', 'Древесный, комкующийся', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(74, 40, 'Тип наполнителя', 'Древесный, целлюлозный', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(75, 41, 'Тип наполнителя', 'Древесный (хвойные породы)', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(76, 42, 'Тип наполнителя', 'Древесный, комкующийся', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(77, 43, 'Тип наполнителя', 'Древесный премиум', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(78, 44, 'Тип наполнителя', 'Древесный с травами', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(79, 45, 'Материал', 'Натуральная резина', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(80, 45, 'Тип игрушки', 'Для жевания, апортировки', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(81, 46, 'Материал', 'Резина с войлоком', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(82, 47, 'Материал', 'Пластик', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(83, 47, 'Тип игрушки', 'Фрисби', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(84, 48, 'Материал', 'Прочная резина', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(85, 49, 'Материал', 'Резина', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(86, 49, 'Особенности', 'Для метателя Chuckit', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(87, 50, 'Материал', 'Резина', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(88, 50, 'Особенности', 'Массаж десен', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(89, 51, 'Материал', 'Memory foam', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(90, 51, 'Особенности', 'Ортопедическая', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(91, 52, 'Материал', 'Флис', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(92, 52, 'Особенности', 'Съемный чехол', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(93, 53, 'Форма', 'Круглая с бортиками', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(94, 54, 'Особенности', 'Водоотталкивающая', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(95, 55, 'Материал', 'Ортопедическая пена', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(96, 55, 'Особенности', 'Гипоаллергенная', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(97, 56, 'Тип', 'Лежанка-домик', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(101, 21, 'Класс корма', 'Суперпремиум', '2026-03-29 13:18:29', '2026-03-29 13:18:29'), +(102, 21, 'Особенности', 'Беззерновой, гипоаллергенный', '2026-03-29 13:18:29', '2026-03-29 13:18:29'), +(103, 21, 'Размер питомца', 'Все размеры', '2026-03-29 13:18:29', '2026-03-29 13:18:29'), +(104, 21, 'Тип корма', 'Сухой', '2026-03-29 13:18:29', '2026-03-29 13:18:29'), +(105, 22, 'Класс корма', 'Премиум', '2026-03-29 13:41:32', '2026-03-29 13:41:32'), +(106, 22, 'Размер питомца', 'Мелкие породы', '2026-03-29 13:41:32', '2026-03-29 13:41:32'), +(107, 22, 'Тип корма', 'Сухой', '2026-03-29 13:41:32', '2026-03-29 13:41:32'), +(132, 25, 'Возраст питомца', 'Для взрослых 1-6 лет', '2026-03-29 18:45:42', '2026-03-29 18:45:42'), +(133, 25, 'Размер питомца', 'Средний и крупный', '2026-03-29 18:45:42', '2026-03-29 18:45:42'), +(134, 25, 'Тип корма', 'Сухой', '2026-03-29 18:45:42', '2026-03-29 18:45:42'), +(135, 26, 'Класс корма', 'Холистик', '2026-03-29 19:03:16', '2026-03-29 19:03:16'), +(136, 26, 'Размер питомца', 'Все размеры', '2026-03-29 19:03:16', '2026-03-29 19:03:16'), +(137, 26, 'Тип корма', 'Сухой', '2026-03-29 19:03:16', '2026-03-29 19:03:16'), +(138, 27, 'Специальные показания', 'Для чувствительного пищеварения', '2026-03-29 19:06:53', '2026-03-29 19:06:53'), +(139, 27, 'Тип корма', 'Сухой', '2026-03-29 19:06:53', '2026-03-29 19:06:53'), +(140, 27, 'Возраст питомца', 'Все возрасты', '2026-03-29 19:06:53', '2026-03-29 19:06:53'), +(141, 27, 'Размер питомца', 'Все размеры', '2026-03-29 19:06:53', '2026-03-29 19:06:53'), +(142, 27, 'Особенности ингредиентов', 'Беззерновой корм', '2026-03-29 19:06:53', '2026-03-29 19:06:53'), +(143, 28, 'Возраст питомца', 'Для взрослых 1-6 лет', '2026-03-29 19:12:47', '2026-03-29 19:12:47'), +(144, 28, 'Особенности ингредиентов', 'Холистик', '2026-03-29 19:12:47', '2026-03-29 19:12:47'), +(145, 28, 'Тип корма', 'Влажный', '2026-03-29 19:12:47', '2026-03-29 19:12:47'), +(150, 23, 'Возраст', 'Взрослые (1-8 лет)', '2026-03-29 19:24:15', '2026-03-29 19:24:15'), +(151, 23, 'Размер питомца', 'Мелкие породы', '2026-03-29 19:24:15', '2026-03-29 19:24:15'), +(152, 23, 'Тип корма', 'Сухой', '2026-03-29 19:24:15', '2026-03-29 19:24:15'), +(153, 24, 'Класс корма', 'Суперпремиум', '2026-03-29 19:24:26', '2026-03-29 19:24:26'), +(154, 24, 'Размер питомца', 'Все размеры', '2026-03-29 19:24:26', '2026-03-29 19:24:26'), +(155, 24, 'Тип корма', 'Сухой', '2026-03-29 19:24:26', '2026-03-29 19:24:26'), +(156, 29, 'Возраст питомца', 'Для взрослых 1-7 лет', '2026-03-29 19:24:35', '2026-03-29 19:24:35'), +(157, 29, 'Размер питомца', 'Все размеры', '2026-03-29 19:24:35', '2026-03-29 19:24:35'), +(158, 29, 'Специальные показания', 'Кастраты и стерилизованные', '2026-03-29 19:24:35', '2026-03-29 19:24:35'), +(159, 29, 'Тип корма', 'Сухой', '2026-03-29 19:24:35', '2026-03-29 19:24:35'), +(180, 30, 'Возраст питомца', 'Для взрослых 1-6 лет', '2026-03-29 19:46:02', '2026-03-29 19:46:02'), +(181, 30, 'Особенности ингредиентов', 'Свежее мясо', '2026-03-29 19:46:02', '2026-03-29 19:46:02'), +(182, 30, 'Размер питомца', 'Все размеры', '2026-03-29 19:46:02', '2026-03-29 19:46:02'), +(183, 30, 'Тип корма', 'Сухой', '2026-03-29 19:46:02', '2026-03-29 19:46:02'), +(186, 31, 'Тип упаковки', 'Пакет, саше или коробка', '2026-03-29 19:58:14', '2026-03-29 19:58:14'), +(187, 32, 'Возраст питомца', 'Все возрасты', '2026-03-29 20:00:36', '2026-03-29 20:00:36'), +(188, 34, 'Возраст питомца', 'Все возрасты', '2026-03-29 20:05:17', '2026-03-29 20:05:17'), +(191, 35, 'Материал изготовления', 'Мембрана', '2026-03-29 20:12:37', '2026-03-29 20:12:37'), +(192, 35, 'Пол животного', 'Девочка', '2026-03-29 20:12:37', '2026-03-29 20:12:37'), +(196, 58, 'Возраст питомца', 'Для взрослых 1-6 лет', '2026-03-30 06:56:38', '2026-03-30 06:56:38'), +(197, 58, 'Особенности ингредиентов', 'Беззерновой корм', '2026-03-30 06:56:38', '2026-03-30 06:56:38'), +(198, 58, 'Специальные показания', 'Кастраты и стерилизованные', '2026-03-30 06:56:38', '2026-03-30 06:56:38'), +(201, 59, 'Возраст питомца', 'Для взрослых 1-6 лет', '2026-03-30 07:05:45', '2026-03-30 07:05:45'), +(202, 59, 'Размер питомца', 'Все размеры', '2026-03-30 07:05:45', '2026-03-30 07:05:45'), +(203, 36, 'Материал', 'Усиленный нейлон', '2026-03-30 09:47:30', '2026-03-30 09:47:30'), +(204, 57, 'Возраст питомца', 'Для взрослых 1-6 лет', '2026-03-31 16:14:19', '2026-03-31 16:14:19'), +(205, 57, 'Размер питомца', 'Все размеры', '2026-03-31 16:14:19', '2026-03-31 16:14:19'), +(206, 57, 'Специальные показания', 'Кастраты и стерилизованные', '2026-03-31 16:14:19', '2026-03-31 16:14:19'); + +-- -------------------------------------------------------- + +-- +-- Estructura de tabla para la tabla `product_variations` +-- + +CREATE TABLE `product_variations` ( + `id` bigint(20) UNSIGNED NOT NULL, + `product_id` bigint(20) UNSIGNED NOT NULL, + `name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, + `sku` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, + `price` decimal(10,2) NOT NULL, + `old_price` decimal(10,2) DEFAULT NULL, + `stock` int(11) NOT NULL DEFAULT 0, + `is_default` tinyint(1) NOT NULL DEFAULT 0, + `is_active` tinyint(1) NOT NULL DEFAULT 1, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- +-- Volcado de datos para la tabla `product_variations` +-- + +INSERT INTO `product_variations` (`id`, `product_id`, `name`, `sku`, `price`, `old_price`, `stock`, `is_default`, `is_active`, `created_at`, `updated_at`) VALUES +(22, 12, 'Rungo Ошейник нейлоновый с ручкой для собак K-9, L, обхват шеи 42-65 см, ширина 3,8 см, черный', '1064128', '1749.00', NULL, 25, 1, 1, '2026-03-28 18:14:26', '2026-03-28 18:14:26'), +(23, 12, 'Rungo Ошейник нейлоновый с ручкой для собак K-9, M, обхват шеи 38-48 см, ширина 3,2 см, красный', '1064127', '1499.00', '1599.00', 10, 0, 1, '2026-03-28 18:14:26', '2026-03-28 18:14:26'), +(41, 21, 'Grandin Hypoallergenic ягненок, 2.7 кг', '1049894', '4499.00', '4200.00', 50, 1, 1, '2026-03-29 12:20:24', '2026-03-29 13:18:29'), +(42, 21, 'Grandin Hypoallergenic ягненок, 11.2 кг', '1049893', '10999.00', NULL, 25, 0, 1, '2026-03-29 12:20:24', '2026-03-29 13:18:29'), +(43, 22, 'Klicker Adult лосось, 0.5 кг', '1060933', '779.00', NULL, 100, 1, 1, '2026-03-29 12:20:24', '2026-03-29 13:41:32'), +(45, 23, 'Royal Canin Mini Adult Сухой корм для взрослых собак мелких размеров в возрасте от 10 месяцев до 8 лет, 800 гр.', '1005371', '755.00', '855.00', 75, 0, 1, '2026-03-29 12:20:24', '2026-03-29 19:24:15'), +(46, 23, 'Royal Canin Mini Adult Сухой корм для взрослых собак мелких размеров в возрасте от 10 месяцев до 8 лет, 2 кг', '1002774', '1999.00', '2200.00', 39, 1, 1, '2026-03-29 12:20:24', '2026-04-01 22:26:19'), +(47, 24, 'АВВА Adult Сухой корм на основе свежего мяса для взрослых собак мелких пород, с ягненком и индейкой, 400 гр.', '1065978', '489.00', NULL, 60, 0, 1, '2026-03-29 12:20:24', '2026-03-29 19:24:26'), +(48, 24, 'АВВА Adult Сухой корм на основе свежего мяса для взрослых собак мелких пород, с ягненком и индейкой, 1,5 кг', 'ABBA-YAG-12KG', '1559.00', NULL, 30, 1, 1, '2026-03-29 12:20:24', '2026-03-29 19:24:26'), +(49, 25, 'AlphaPet Adult Monoprotein Сухой корм для собак средних и крупных пород, белая рыба, 2 кг', '1060824', '2019.00', '2219.00', 80, 1, 1, '2026-03-29 12:20:24', '2026-03-29 18:45:42'), +(50, 25, 'AlphaPet Adult Monoprotein Сухой корм для собак средних и крупных пород, белая рыба, 12 кг', '1060825', '10529.00', NULL, 30, 0, 1, '2026-03-29 12:20:24', '2026-03-29 18:45:42'), +(51, 26, 'Ownat Grain Free Just Сухой корм беззерновой для собак, с лососем и морепродуктами, 3 кг', '1042255', '3179.00', NULL, 45, 1, 1, '2026-03-29 12:20:24', '2026-03-29 19:03:16'), +(52, 26, 'Ownat Grain Free Just Сухой корм беззерновой для собак, с лососем и морепродуктами, 14 кг', '1061428', '9999.00', '12000.00', 15, 0, 1, '2026-03-29 12:20:24', '2026-03-29 19:03:16'), +(53, 27, 'Klicker Adult Sensitive Digestion Сухой корм для кошек с чувствительным пищеварением, с ягненком, 1 кг', '1065459', '1435.00', '1793.00', 78, 1, 1, '2026-03-29 12:20:24', '2026-04-06 06:45:58'), +(55, 28, 'Grandin Holistic Влажный корм (консервы) для взрослых кошек, тунец в желе, 80 гр.', '1064817', '199.00', NULL, 50, 1, 1, '2026-03-29 12:20:24', '2026-03-29 19:12:47'), +(56, 28, 'Grandin Holistic Влажный корм (консервы) для взрослых кошек, тунец с топпингом из лосося, 80 гр.', '1064818', '199.00', NULL, 25, 0, 1, '2026-03-29 12:20:24', '2026-03-29 19:12:47'), +(57, 29, 'Royal Canin Sterilised 37 Regular Сухой корм для стерилизованных кошек с 1 до 7 лет, 400 гр.', '1000724', '635.00', NULL, 48, 0, 1, '2026-03-29 12:20:24', '2026-04-06 06:44:46'), +(58, 29, 'Royal Canin Sterilised 37 Regular Сухой корм для стерилизованных кошек с 1 до 7 лет, 200 гр.', '1051781', '339.00', NULL, 30, 0, 1, '2026-03-29 12:20:24', '2026-03-29 19:22:45'), +(59, 30, 'AlphaPet Сухой корм для стерилизованных кошек, с ягненком и индейкой, 400 гр.', '1053776', '639.00', NULL, 55, 0, 1, '2026-03-29 12:20:24', '2026-03-29 19:44:22'), +(60, 30, 'AlphaPet Сухой корм для стерилизованных кошек, с ягненком и индейкой, 1,5 кг', '1053741', '1969.00', NULL, 17, 1, 1, '2026-03-29 12:20:24', '2026-04-06 06:44:46'), +(61, 31, 'Tetra Min Holiday корм желе на 14 дней, 30 г', '1006487', '499.00', NULL, 65, 1, 1, '2026-03-29 12:20:24', '2026-03-29 19:49:44'), +(63, 32, 'Little One Корм для морских свинок, 400 гр.', '1007511', '255.00', NULL, 75, 1, 1, '2026-03-29 12:20:24', '2026-03-29 19:55:44'), +(64, 32, 'Little One Корм для морских свинок, 900 гр.', '1015934', '537.00', NULL, 15, 0, 1, '2026-03-29 12:20:24', '2026-03-29 19:55:44'), +(67, 34, 'Little One Корм для морских свинок Зелёная долина, 750 гр.', '1015722', '615.00', NULL, 40, 1, 1, '2026-03-29 12:20:24', '2026-03-29 20:05:17'), +(69, 35, 'Rungo Комбинезон теплый для собак породы мопс, обхват шеи 34 см, обхват груди 57 см, длина спины 34 см, шоколад (девочка)', '1063488', '2599.00', NULL, 5, 1, 1, '2026-03-29 12:20:24', '2026-03-29 20:12:19'), +(70, 35, 'Rungo Комбинезон с флисовой подкладкой для мопса, 57х34х34 см, пудра-бордо (девочка)', '1059683', '2599.00', NULL, 10, 0, 1, '2026-03-29 12:20:24', '2026-03-29 20:12:19'), +(71, 36, 'Rogz Utility ошейник, M', 'ROGZ-UTILITY-M', '2999.00', NULL, 30, 1, 1, '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(72, 36, 'Rogz Utility ошейник, L', 'ROGZ-UTILITY-L', '3499.00', NULL, 15, 0, 1, '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(73, 37, 'Hunter кожаный плетеный, M', 'HUNTER-PLET-M', '4999.00', NULL, 10, 1, 1, '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(74, 37, 'Hunter кожаный плетеный, L', 'HUNTER-PLET-L', '5999.00', NULL, 5, 0, 1, '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(75, 38, 'Petzl ошейник альпинистский, L/XL', 'PETZL-ALP-LXL', '7999.00', NULL, 8, 1, 1, '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(76, 39, 'Ever Clean Extra Strength, 10 л', 'EVERCLEAN-10L', '1499.00', NULL, 40, 1, 1, '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(77, 39, 'Ever Clean Extra Strength, 20 л', 'EVERCLEAN-20L', '2599.00', NULL, 25, 0, 1, '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(78, 40, 'Cat\'s Best Original, 6 кг', 'CATS-BEST-6KG', '1299.00', NULL, 60, 1, 1, '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(79, 40, 'Cat\'s Best Original, 17 кг', 'CATS-BEST-17KG', '2999.00', NULL, 30, 0, 1, '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(80, 41, 'Barsik Premium древесный, 5 л', 'BARSIK-5L', '899.00', NULL, 80, 1, 1, '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(81, 41, 'Barsik Premium древесный, 10 л', 'BARSIK-10L', '1499.00', NULL, 50, 0, 1, '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(82, 42, 'Perfect Fit древесный, 8 л', 'PERFECTFIT-8L', '1099.00', NULL, 70, 1, 1, '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(83, 42, 'Perfect Fit древесный, 15 л', 'PERFECTFIT-15L', '1999.00', NULL, 40, 0, 1, '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(84, 43, 'Сибирский лес премиум, 10 л', 'SIBLES-10L', '1399.00', NULL, 45, 1, 1, '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(85, 43, 'Сибирский лес премиум, 20 л', 'SIBLES-20L', '2499.00', NULL, 20, 0, 1, '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(86, 44, 'Травка-Сено древесный, 7 л', 'TRAVKA-7L', '1199.00', NULL, 55, 1, 1, '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(87, 44, 'Травка-Сено древесный, 14 л', 'TRAVKA-14L', '2099.00', NULL, 30, 0, 1, '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(88, 45, 'Kong Classic красный, S', 'KONG-CLASSIC-S', '1999.00', NULL, 35, 1, 1, '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(89, 45, 'Kong Classic красный, M', 'KONG-CLASSIC-M', '2499.00', NULL, 25, 0, 1, '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(90, 46, 'Trixie теннисный мячик, стандарт', 'TRIXIE-TENNIS-STD', '399.00', NULL, 100, 1, 1, '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(91, 46, 'Trixie теннисный мячик, большой', 'TRIXIE-TENNIS-L', '599.00', NULL, 70, 0, 1, '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(92, 47, 'Ferplast фрисби Flyer, средний', 'FERPLAST-FLYER-M', '799.00', NULL, 60, 1, 1, '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(93, 47, 'Ferplast фрисби Flyer, большой', 'FERPLAST-FLYER-L', '999.00', NULL, 40, 0, 1, '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(94, 48, 'K9 Granit мячик, M', 'K9-GRANIT-M', '1499.00', NULL, 30, 1, 1, '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(95, 48, 'K9 Granit мячик, L', 'K9-GRANIT-L', '1999.00', NULL, 20, 0, 1, '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(96, 49, 'Chuckit! Ultra мячик, средний', 'CHUCKIT-ULTRA-M', '1299.00', NULL, 50, 1, 1, '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(97, 49, 'Chuckit! Ultra мячик, большой', 'CHUCKIT-ULTRA-L', '1699.00', NULL, 35, 0, 1, '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(98, 50, 'Rogz Grinz мячик зубастый, M', 'ROGZ-GRINZ-M', '1199.00', NULL, 45, 1, 1, '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(99, 50, 'Rogz Grinz мячик зубастый, L', 'ROGZ-GRINZ-L', '1499.00', NULL, 25, 0, 1, '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(100, 51, 'Trixie Ортопедическая лежанка, S (50x40 см)', 'TRIXIE-ORTHO-S', '7999.00', NULL, 15, 1, 1, '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(101, 51, 'Trixie Ортопедическая лежанка, M (70x50 см)', 'TRIXIE-ORTHO-M', '10999.00', NULL, 10, 0, 1, '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(102, 52, 'Good Boy плед лежанка, S (60x45 см)', 'GOODBOY-PLED-S', '2999.00', NULL, 40, 1, 1, '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(103, 52, 'Good Boy плед лежанка, L (90x65 см)', 'GOODBOY-PLED-L', '5999.00', NULL, 25, 0, 1, '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(104, 53, 'Ferplast лежанка Carlotta, S (50 см диаметр)', 'FERPLAST-CARL-S', '3499.00', NULL, 35, 1, 1, '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(105, 53, 'Ferplast лежанка Carlotta, M (70 см диаметр)', 'FERPLAST-CARL-M', '5999.00', NULL, 20, 0, 1, '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(106, 54, 'Hunter лежанка Bavaria, M (75x55 см)', 'HUNTER-BAV-M', '6999.00', NULL, 20, 1, 1, '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(107, 54, 'Hunter лежанка Bavaria, L (95x70 см)', 'HUNTER-BAV-L', '9999.00', NULL, 12, 0, 1, '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(108, 55, 'PetFusion ортопедическая, M (71x53 см)', 'PETFUSION-M', '8999.00', NULL, 18, 1, 1, '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(109, 55, 'PetFusion ортопедическая, L (91x66 см)', 'PETFUSION-L', '12999.00', NULL, 8, 0, 1, '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(110, 56, 'Laifugy лежанка домик, S (60x45x30 см)', 'LAIFUGY-S', '4499.00', NULL, 30, 1, 1, '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(111, 56, 'Laifugy лежанка домик, M (80x60x35 см)', 'LAIFUGY-M', '6999.00', NULL, 20, 0, 1, '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(112, 23, 'Royal Canin Mini Adult Сухой корм для взрослых собак мелких размеров в возрасте от 10 месяцев до 8 лет, 4 кг', '1027343', '3739.00', NULL, 25, 0, 1, '2026-03-29 14:07:03', '2026-03-29 14:07:03'), +(115, 23, 'Royal Canin Mini Adult Сухой корм для взрослых собак мелких размеров в возрасте от 10 месяцев до 8 лет, 8 кг', '1002775', '6719.00', NULL, 15, 0, 1, '2026-03-29 14:30:52', '2026-03-29 14:30:52'), +(116, 26, 'Ownat Adult Grain Free Сухой корм беззерновой для взрослых собак, с ягненком, 3 кг', '1042254', '3179.00', NULL, 5, 0, 1, '2026-03-29 19:03:16', '2026-03-29 19:03:16'), +(117, 26, 'Ownat Adult Grain Free Сухой корм беззерновой для взрослых собак, с ягненком, 14 кг', '1044809', '9999.00', NULL, 10, 0, 1, '2026-03-29 19:03:16', '2026-03-29 19:03:16'), +(118, 26, 'Ownat Adult Grain Free Сухой корм для взрослых собак, с уткой, 14 кг', '1044810', '8999.00', '9999.00', 0, 0, 1, '2026-03-29 19:03:16', '2026-03-29 19:03:16'), +(119, 26, 'Ownat Adult Grain Free Сухой корм для взрослых собак, с уткой, 3 кг', '1044811', '3179.00', NULL, 15, 0, 1, '2026-03-29 19:03:16', '2026-03-29 19:03:16'), +(120, 26, 'Ownat Adult Grain Free Сухой корм для взрослых собак, с курицей, 3 кг', '1040783', '2507.00', '2949.00', 20, 0, 1, '2026-03-29 19:03:16', '2026-03-29 19:03:16'), +(121, 29, 'Royal Canin Sterilised 37 Regular Сухой корм для стерилизованных кошек с 1 до 7 лет, 1,2 кг', '1051779', '1839.00', NULL, 0, 1, 1, '2026-03-29 19:22:45', '2026-03-29 19:24:35'), +(122, 29, 'Royal Canin Sterilised 37 Regular Сухой корм для стерилизованных кошек с 1 до 7 лет, 2 кг', '1000715', '2945.00', NULL, 10, 0, 1, '2026-03-29 19:22:45', '2026-03-29 19:22:45'), +(123, 29, 'Royal Canin Sterilised 37 Regular Сухой корм для стерилизованных кошек с 1 до 7 лет, 4 кг', '1003418', '5669.00', NULL, 0, 0, 1, '2026-03-29 19:22:45', '2026-03-29 19:22:45'), +(124, 29, 'Royal Canin Sterilised 37 Regular Сухой корм для стерилизованных кошек с 1 до 7 лет, 10 кг', '1002563', '12815.00', NULL, 25, 0, 1, '2026-03-29 19:22:45', '2026-03-29 19:22:45'), +(127, 30, 'AlphaPet Сухой корм для стерилизованных кошек, с ягненком и индейкой, 7 кг', '1066740', '7499.00', NULL, 0, 0, 1, '2026-03-29 19:45:46', '2026-03-29 19:45:46'), +(128, 35, 'Rungo Комбинезон теплый для собак породы мопс, длина спины 30 см, обхват шеи 43 см, обхват груди 60 см, красный (девочка)', '1054814', '2399.00', '2599.00', 3, 0, 1, '2026-03-29 20:12:19', '2026-03-29 20:12:19'), +(129, 57, 'GRANDORF Holistic Adult Sterilised Сухой корм для взрослых стерилизованных кошек, с кроликом и индейкой, 2 кг', '1040169', '3735.00', NULL, 5, 1, 1, '2026-03-30 06:08:32', '2026-03-30 06:08:32'), +(130, 57, 'GRANDORF Holistic Adult Sterilised Сухой корм для взрослых стерилизованных кошек, с кроликом и индейкой, 400 гр.', '1040168', '1125.00', NULL, 15, 0, 1, '2026-03-30 06:08:32', '2026-03-30 06:08:32'), +(131, 57, 'GRANDORF Holistic Adult Sterilised Сухой корм для взрослых стерилизованных кошек, с кроликом и индейкой, 8 кг', '1063077', '11255.00', NULL, 5, 0, 1, '2026-03-30 06:08:32', '2026-03-30 06:08:32'), +(132, 57, 'GRANDORF Holistic Adult Sterilised Сухой корм для стерилизованных кошек, с индейкой, 2 кг', '1063079', '3435.00', '3735.00', 5, 0, 1, '2026-03-30 06:08:32', '2026-03-30 06:08:32'), +(133, 57, 'GRANDORF Holistic Adult Sterilised Сухой корм для стерилизованных кошек, с индейкой, 400 гр.', '1063078', '900.00', '1125.00', 0, 0, 1, '2026-03-30 06:08:32', '2026-03-30 06:08:32'), +(134, 57, 'GRANDORF Holistic Adult Sterilised Сухой корм для взрослых стерилизованных кошек, четыре вида мяса, 400 гр.', '1040174', '1185.00', NULL, 8, 0, 1, '2026-03-30 06:08:32', '2026-03-30 06:08:32'), +(135, 57, 'GRANDORF Holistic Adult Sterilised Сухой корм для взрослых стерилизованных кошек, четыре вида мяса, 2 кг', '1040175', '3915.00', NULL, 8, 0, 1, '2026-03-30 06:08:32', '2026-03-30 06:08:32'), +(136, 57, 'GRANDORF Holistic Adult Sterilised Сухой корм для взрослых стерилизованных кошек, четыре вида мяса, 8 кг', '1065210', '11669.00', NULL, 15, 0, 1, '2026-03-30 06:08:32', '2026-03-30 06:08:32'), +(137, 58, 'Ownat Adult Sterilized Grain Free Prime Сухой корм для стерилизованных кошек, с курицей и индейкой, 1 кг', '1040784', '1929.00', NULL, 10, 1, 1, '2026-03-30 06:56:38', '2026-03-30 06:56:38'), +(138, 58, 'Ownat Adult Sterilized Grain Free Prime Сухой корм для стерилизованных кошек, с рыбой, 1 кг', '1061427', '2169.00', NULL, 5, 0, 1, '2026-03-30 06:56:38', '2026-03-30 06:56:38'), +(139, 59, 'AlphaPet WOW Сухой корм для стерилизованных кошек с индейкой, 350 г', '1053735', '339.00', NULL, 10, 1, 1, '2026-03-30 07:01:27', '2026-03-30 07:01:27'), +(140, 59, 'AlphaPet WOW Сухой корм для стерилизованных кошек с индейкой, 1,5 кг', '1053736', '1129.00', NULL, 10, 0, 1, '2026-03-30 07:01:27', '2026-03-30 07:01:27'), +(141, 59, 'AlphaPet WOW Сухой корм для стерилизованных кошек с цыпленком, 350 г', '1053779', '339.00', NULL, 15, 0, 1, '2026-03-30 07:01:27', '2026-03-30 07:01:27'); + +-- -------------------------------------------------------- + +-- +-- Estructura de tabla para la tabla `reviews` +-- + +CREATE TABLE `reviews` ( + `id` bigint(20) UNSIGNED NOT NULL, + `user_id` bigint(20) UNSIGNED NOT NULL, + `product_id` bigint(20) UNSIGNED NOT NULL, + `rating` tinyint(4) NOT NULL, + `title` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `comment` text COLLATE utf8mb4_unicode_ci NOT NULL, + `advantages` text COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `disadvantages` text COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `helpful_count` int(11) NOT NULL DEFAULT 0, + `unhelpful_count` int(11) NOT NULL DEFAULT 0, + `is_approved` tinyint(1) NOT NULL DEFAULT 0, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- -------------------------------------------------------- + +-- +-- Estructura de tabla para la tabla `roles` +-- + +CREATE TABLE `roles` ( + `id` bigint(20) UNSIGNED NOT NULL, + `name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, + `slug` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, + `description` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- +-- Volcado de datos para la tabla `roles` +-- + +INSERT INTO `roles` (`id`, `name`, `slug`, `description`) VALUES +(1, 'Супер администратор', 'super_admin', 'Полный доступ ко всем функциям системы (обходит все проверки прав)'), +(2, 'Администратор магазина', 'shop_admin', 'Полное управление магазином, товарами и заказами'), +(3, 'Менеджер по товарам', 'product_manager', 'Управление каталогом товаров'), +(4, 'Менеджер по заказам', 'order_manager', 'Обработка заказов'), +(5, 'Кладовщик', 'warehouse_manager', 'Управление остатками товаров'), +(6, 'Зарегистрированный пользователь', 'registered_user', 'Обычный пользователь сайта'); + +-- -------------------------------------------------------- + +-- +-- Estructura de tabla para la tabla `users` +-- + +CREATE TABLE `users` ( + `id` bigint(20) UNSIGNED NOT NULL, + `name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, + `email` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, + `phone` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `email_verified_at` timestamp NULL DEFAULT NULL, + `password` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, + `remember_token` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + `role_id` bigint(20) UNSIGNED DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- +-- Volcado de datos para la tabla `users` +-- + +INSERT INTO `users` (`id`, `name`, `email`, `phone`, `email_verified_at`, `password`, `remember_token`, `created_at`, `updated_at`, `role_id`) VALUES +(1, 'duckinahat', 'margaritaborodovskih@gmail.com', '+7(909)-090-86-13', NULL, '$2y$12$DA50T5Pr3l1EYQyf4/LYd.PmtAVm9bIXcF5lpqV5qiLV3hrCrOP2e', 'QncGrCIXhpPDpCigKEchTXoSyE2dFs9G0eCKTfnKBqKMFgfk1ltjx5mHBAmM', '2026-03-18 15:31:55', '2026-04-06 04:51:11', 1), +(2, 'user11', 'user1@mail.com', '+7(888)-888-88-88', NULL, '$2y$12$9Z5wZFaeK7UZ00o9MNvjDurBEc6.D.iWqYd3k3LauTmtgh3MNYw.S', NULL, '2026-04-01 14:45:38', '2026-04-04 12:16:40', 6); + +-- -------------------------------------------------------- + +-- +-- Estructura de tabla para la tabla `variation_attributes` +-- + +CREATE TABLE `variation_attributes` ( + `id` bigint(20) UNSIGNED NOT NULL, + `variation_id` bigint(20) UNSIGNED NOT NULL, + `key` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, + `value` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- +-- Volcado de datos para la tabla `variation_attributes` +-- + +INSERT INTO `variation_attributes` (`id`, `variation_id`, `key`, `value`, `created_at`, `updated_at`) VALUES +(1, 22, 'color', 'Черный', '2026-03-28 18:14:26', '2026-03-28 18:14:26'), +(2, 22, 'size', 'L', '2026-03-28 18:14:26', '2026-03-28 18:14:26'), +(3, 23, 'color', 'Красный', '2026-03-28 18:14:26', '2026-03-28 18:14:26'), +(4, 23, 'size', 'M', '2026-03-28 18:14:26', '2026-03-28 18:14:26'), +(49, 76, 'объем', '10 л', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(50, 77, 'объем', '20 л', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(51, 78, 'вес', '6 кг', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(52, 79, 'вес', '17 кг', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(53, 80, 'объем', '5 л', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(54, 81, 'объем', '10 л', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(55, 88, 'размер', 'S', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(56, 89, 'размер', 'M', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(57, 90, 'размер', 'стандарт', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(58, 91, 'размер', 'большой', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(59, 100, 'размер', 'S (50x40 см)', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(60, 101, 'размер', 'M (70x50 см)', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(61, 102, 'размер', 'S (60x45 см)', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(62, 103, 'размер', 'L (90x65 см)', '2026-03-29 12:20:24', '2026-03-29 12:20:24'), +(63, 41, 'flavor', 'Ягненок', '2026-03-29 13:18:29', '2026-03-29 13:18:29'), +(64, 41, 'weight', '2.7', '2026-03-29 13:18:29', '2026-03-29 13:18:29'), +(65, 42, 'flavor', 'Ягненок', '2026-03-29 13:18:29', '2026-03-29 13:18:29'), +(66, 43, 'flavor', 'Лосось', '2026-03-29 13:41:32', '2026-03-29 13:41:32'), +(67, 43, 'weight', '0.5', '2026-03-29 13:41:32', '2026-03-29 13:41:32'), +(118, 49, 'flavor', 'Белая рыба', '2026-03-29 18:45:42', '2026-03-29 18:45:42'), +(119, 49, 'weight', '2', '2026-03-29 18:45:42', '2026-03-29 18:45:42'), +(120, 50, 'flavor', 'Белая рыба', '2026-03-29 18:45:42', '2026-03-29 18:45:42'), +(121, 50, 'weight', '12', '2026-03-29 18:45:42', '2026-03-29 18:45:42'), +(122, 51, 'flavor', 'Лосось', '2026-03-29 19:03:16', '2026-03-29 19:03:16'), +(123, 51, 'weight', '3', '2026-03-29 19:03:16', '2026-03-29 19:03:16'), +(124, 52, 'flavor', 'Лосось', '2026-03-29 19:03:16', '2026-03-29 19:03:16'), +(125, 52, 'weight', '14', '2026-03-29 19:03:16', '2026-03-29 19:03:16'), +(126, 116, 'flavor', 'Ягненок', '2026-03-29 19:03:16', '2026-03-29 19:03:16'), +(127, 116, 'weight', '3', '2026-03-29 19:03:16', '2026-03-29 19:03:16'), +(128, 117, 'flavor', 'Ягненок', '2026-03-29 19:03:16', '2026-03-29 19:03:16'), +(129, 117, 'weight', '14', '2026-03-29 19:03:16', '2026-03-29 19:03:16'), +(130, 118, 'flavor', 'Утка', '2026-03-29 19:03:16', '2026-03-29 19:03:16'), +(131, 118, 'weight', '14', '2026-03-29 19:03:16', '2026-03-29 19:03:16'), +(132, 119, 'flavor', 'Утка', '2026-03-29 19:03:16', '2026-03-29 19:03:16'), +(133, 119, 'weight', '3', '2026-03-29 19:03:16', '2026-03-29 19:03:16'), +(134, 120, 'flavor', 'Курица', '2026-03-29 19:03:16', '2026-03-29 19:03:16'), +(135, 120, 'weight', '3', '2026-03-29 19:03:16', '2026-03-29 19:03:16'), +(136, 53, 'flavor', 'Ягненок', '2026-03-29 19:06:53', '2026-03-29 19:06:53'), +(137, 53, 'weight', '1', '2026-03-29 19:06:53', '2026-03-29 19:06:53'), +(138, 55, 'flavor', 'Тунец', '2026-03-29 19:12:47', '2026-03-29 19:12:47'), +(139, 55, 'weight', '0.08', '2026-03-29 19:12:47', '2026-03-29 19:12:47'), +(140, 56, 'flavor', 'Тунец с топпингом из лосося', '2026-03-29 19:12:47', '2026-03-29 19:12:47'), +(153, 45, 'flavor', 'Кукуруза', '2026-03-29 19:24:15', '2026-03-29 19:24:15'), +(154, 45, 'weight', '0.8', '2026-03-29 19:24:15', '2026-03-29 19:24:15'), +(155, 46, 'flavor', 'Кукуруза', '2026-03-29 19:24:15', '2026-03-29 19:24:15'), +(156, 46, 'weight', '2', '2026-03-29 19:24:15', '2026-03-29 19:24:15'), +(157, 112, 'flavor', 'Кукуруза', '2026-03-29 19:24:15', '2026-03-29 19:24:15'), +(158, 112, 'weight', '4', '2026-03-29 19:24:15', '2026-03-29 19:24:15'), +(159, 115, 'flavor', 'Кукуруза', '2026-03-29 19:24:15', '2026-03-29 19:24:15'), +(160, 115, 'weight', '8', '2026-03-29 19:24:15', '2026-03-29 19:24:15'), +(161, 47, 'flavor', 'Ягненок, индейка', '2026-03-29 19:24:26', '2026-03-29 19:24:26'), +(162, 47, 'weight', '0.4', '2026-03-29 19:24:26', '2026-03-29 19:24:26'), +(163, 48, 'flavor', 'Ягненок, индейка', '2026-03-29 19:24:26', '2026-03-29 19:24:26'), +(164, 48, 'weight', '1.5', '2026-03-29 19:24:26', '2026-03-29 19:24:26'), +(165, 57, 'flavor', 'Птица', '2026-03-29 19:24:35', '2026-03-29 19:24:35'), +(166, 57, 'weight', '0.4', '2026-03-29 19:24:35', '2026-03-29 19:24:35'), +(167, 58, 'flavor', 'Птица', '2026-03-29 19:24:35', '2026-03-29 19:24:35'), +(168, 58, 'weight', '0.2', '2026-03-29 19:24:35', '2026-03-29 19:24:35'), +(169, 121, 'flavor', 'Птица', '2026-03-29 19:24:35', '2026-03-29 19:24:35'), +(170, 121, 'weight', '1.2', '2026-03-29 19:24:35', '2026-03-29 19:24:35'), +(171, 122, 'flavor', 'Птица', '2026-03-29 19:24:35', '2026-03-29 19:24:35'), +(172, 122, 'weight', '2', '2026-03-29 19:24:35', '2026-03-29 19:24:35'), +(173, 123, 'flavor', 'Птица', '2026-03-29 19:24:35', '2026-03-29 19:24:35'), +(174, 123, 'weight', '4', '2026-03-29 19:24:35', '2026-03-29 19:24:35'), +(175, 124, 'flavor', 'Птица', '2026-03-29 19:24:35', '2026-03-29 19:24:35'), +(176, 124, 'weight', '10', '2026-03-29 19:24:35', '2026-03-29 19:24:35'), +(200, 59, 'flavor', 'Ягненок, индейка', '2026-03-29 19:46:02', '2026-03-29 19:46:02'), +(201, 59, 'weight', '0.4', '2026-03-29 19:46:02', '2026-03-29 19:46:02'), +(202, 60, 'flavor', 'Ягненок, индейка', '2026-03-29 19:46:02', '2026-03-29 19:46:02'), +(203, 60, 'weight', '1.5', '2026-03-29 19:46:02', '2026-03-29 19:46:02'), +(204, 127, 'flavor', 'Ягненок, индейка', '2026-03-29 19:46:02', '2026-03-29 19:46:02'), +(208, 61, 'weight', '0.03', '2026-03-29 19:58:14', '2026-03-29 19:58:14'), +(209, 63, 'weight', '0.4', '2026-03-29 20:00:36', '2026-03-29 20:00:36'), +(210, 64, 'weight', '0.9', '2026-03-29 20:00:36', '2026-03-29 20:00:36'), +(211, 67, 'weight', '0.75', '2026-03-29 20:05:17', '2026-03-29 20:05:17'), +(217, 69, 'color', 'Шоколад', '2026-03-29 20:12:37', '2026-03-29 20:12:37'), +(218, 69, 'size', 'XL', '2026-03-29 20:12:37', '2026-03-29 20:12:37'), +(219, 70, 'color', 'Пудровый', '2026-03-29 20:12:37', '2026-03-29 20:12:37'), +(220, 70, 'size', 'XL', '2026-03-29 20:12:37', '2026-03-29 20:12:37'), +(221, 128, 'color', 'Красный', '2026-03-29 20:12:37', '2026-03-29 20:12:37'), +(222, 128, 'size', 'L', '2026-03-29 20:12:37', '2026-03-29 20:12:37'), +(239, 137, 'flavor', 'Курица, индейка', '2026-03-30 06:56:38', '2026-03-30 06:56:38'), +(240, 137, 'weight', '1', '2026-03-30 06:56:38', '2026-03-30 06:56:38'), +(241, 138, 'flavor', 'Рыба', '2026-03-30 06:56:38', '2026-03-30 06:56:38'), +(242, 138, 'weight', '1', '2026-03-30 06:56:38', '2026-03-30 06:56:38'), +(249, 139, 'flavor', 'Индейка', '2026-03-30 07:05:45', '2026-03-30 07:05:45'), +(250, 139, 'weight', '0.35', '2026-03-30 07:05:45', '2026-03-30 07:05:45'), +(251, 140, 'flavor', 'Индейка', '2026-03-30 07:05:45', '2026-03-30 07:05:45'), +(252, 140, 'weight', '1.5', '2026-03-30 07:05:45', '2026-03-30 07:05:45'), +(253, 141, 'flavor', 'Цыпленок', '2026-03-30 07:05:45', '2026-03-30 07:05:45'), +(254, 141, 'weight', '0.35', '2026-03-30 07:05:45', '2026-03-30 07:05:45'), +(255, 129, 'flavor', 'Кролик, индейка', '2026-03-31 16:14:19', '2026-03-31 16:14:19'), +(256, 129, 'weight', '2', '2026-03-31 16:14:19', '2026-03-31 16:14:19'), +(257, 130, 'flavor', 'Кролик, индейка', '2026-03-31 16:14:19', '2026-03-31 16:14:19'), +(258, 130, 'weight', '0.4', '2026-03-31 16:14:19', '2026-03-31 16:14:19'), +(259, 131, 'flavor', 'Кролик, индейка', '2026-03-31 16:14:19', '2026-03-31 16:14:19'), +(260, 131, 'weight', '8', '2026-03-31 16:14:19', '2026-03-31 16:14:19'), +(261, 132, 'flavor', 'Индейка', '2026-03-31 16:14:19', '2026-03-31 16:14:19'), +(262, 132, 'weight', '2', '2026-03-31 16:14:19', '2026-03-31 16:14:19'), +(263, 133, 'flavor', 'Индейка', '2026-03-31 16:14:19', '2026-03-31 16:14:19'), +(264, 133, 'weight', '0.4', '2026-03-31 16:14:19', '2026-03-31 16:14:19'), +(265, 134, 'flavor', 'Мясо', '2026-03-31 16:14:19', '2026-03-31 16:14:19'), +(266, 134, 'weight', '0.4', '2026-03-31 16:14:19', '2026-03-31 16:14:19'), +(267, 135, 'flavor', 'Мясо', '2026-03-31 16:14:19', '2026-03-31 16:14:19'), +(268, 135, 'weight', '2', '2026-03-31 16:14:19', '2026-03-31 16:14:19'), +(269, 136, 'flavor', 'Мясо', '2026-03-31 16:14:19', '2026-03-31 16:14:19'), +(270, 136, 'weight', '8', '2026-03-31 16:14:19', '2026-03-31 16:14:19'); + +-- -------------------------------------------------------- + +-- +-- Estructura de tabla para la tabla `variation_images` +-- + +CREATE TABLE `variation_images` ( + `id` bigint(20) UNSIGNED NOT NULL, + `variation_id` bigint(20) UNSIGNED NOT NULL, + `path` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, + `sort_order` int(11) NOT NULL DEFAULT 0, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- +-- Volcado de datos para la tabla `variation_images` +-- + +INSERT INTO `variation_images` (`id`, `variation_id`, `path`, `sort_order`, `created_at`, `updated_at`) VALUES +(25, 22, 'assets/images/products/rungo-oseinik-neilonovyi-s-ruckoi-dlia-sobak-k-9-l/variation_22/0.webp', 0, '2026-03-28 18:14:26', '2026-03-28 18:14:26'), +(26, 22, 'assets/images/products/rungo-oseinik-neilonovyi-s-ruckoi-dlia-sobak-k-9-l/variation_22/1.webp', 1, '2026-03-28 18:14:26', '2026-03-28 18:14:26'), +(27, 23, 'assets/images/products/rungo-oseinik-neilonovyi-s-ruckoi-dlia-sobak-k-9-l/variation_23/0.webp', 0, '2026-03-28 18:14:26', '2026-03-28 18:14:26'), +(28, 23, 'assets/images/products/rungo-oseinik-neilonovyi-s-ruckoi-dlia-sobak-k-9-l/variation_23/1.webp', 1, '2026-03-28 18:14:26', '2026-03-28 18:14:26'), +(29, 41, 'assets/images/products/grandin-hypoallergenic-iagnenok/variation_41/0.webp', 0, '2026-03-29 13:18:29', '2026-03-29 13:18:29'), +(30, 41, 'assets/images/products/grandin-hypoallergenic-iagnenok/variation_41/1.webp', 1, '2026-03-29 13:18:29', '2026-03-29 13:18:29'), +(31, 41, 'assets/images/products/grandin-hypoallergenic-iagnenok/variation_41/2.webp', 2, '2026-03-29 13:18:29', '2026-03-29 13:18:29'), +(32, 42, 'assets/images/products/grandin-hypoallergenic-iagnenok/variation_42/0.jpeg', 0, '2026-03-29 13:18:29', '2026-03-29 13:18:29'), +(33, 42, 'assets/images/products/grandin-hypoallergenic-iagnenok/variation_42/1.webp', 1, '2026-03-29 13:18:29', '2026-03-29 13:18:29'), +(34, 42, 'assets/images/products/grandin-hypoallergenic-iagnenok/variation_42/2.webp', 2, '2026-03-29 13:18:29', '2026-03-29 13:18:29'), +(35, 43, 'assets/images/products/klicker-adult/variation_43/0.webp', 0, '2026-03-29 13:41:32', '2026-03-29 13:41:32'), +(36, 43, 'assets/images/products/klicker-adult/variation_43/1.webp', 1, '2026-03-29 13:41:32', '2026-03-29 13:41:32'), +(37, 43, 'assets/images/products/klicker-adult/variation_43/2.webp', 2, '2026-03-29 13:41:32', '2026-03-29 13:41:32'), +(38, 45, 'assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_45/0.webp', 0, '2026-03-29 14:07:03', '2026-03-29 14:07:03'), +(39, 45, 'assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_45/1.webp', 1, '2026-03-29 14:07:03', '2026-03-29 14:07:03'), +(40, 45, 'assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_45/2.webp', 2, '2026-03-29 14:07:03', '2026-03-29 14:07:03'), +(41, 46, 'assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_46/0.webp', 0, '2026-03-29 14:07:03', '2026-03-29 14:07:03'), +(42, 46, 'assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_46/1.webp', 1, '2026-03-29 14:07:03', '2026-03-29 14:07:03'), +(43, 46, 'assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_46/2.webp', 2, '2026-03-29 14:07:03', '2026-03-29 14:07:03'), +(44, 112, 'assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_112/0.webp', 0, '2026-03-29 14:07:03', '2026-03-29 14:07:03'), +(45, 112, 'assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_112/1.webp', 1, '2026-03-29 14:07:03', '2026-03-29 14:07:03'), +(46, 112, 'assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_112/2.webp', 2, '2026-03-29 14:07:03', '2026-03-29 14:07:03'), +(56, 115, 'assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_115/0.webp', 0, '2026-03-29 14:30:52', '2026-03-29 14:30:52'), +(57, 115, 'assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_115/1.webp', 1, '2026-03-29 14:30:52', '2026-03-29 14:30:52'), +(58, 115, 'assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_115/2.webp', 2, '2026-03-29 14:30:52', '2026-03-29 14:30:52'), +(59, 47, 'assets/images/products/avva-adult-suxoi-korm-na-osnove-svezego-miasa-dlia-vzroslyx-sobak-melkix-porod-s-iagnenkom-i-indeikoi/variation_47/0.webp', 0, '2026-03-29 17:16:40', '2026-03-29 17:16:40'), +(60, 47, 'assets/images/products/avva-adult-suxoi-korm-na-osnove-svezego-miasa-dlia-vzroslyx-sobak-melkix-porod-s-iagnenkom-i-indeikoi/variation_47/1.webp', 1, '2026-03-29 17:16:40', '2026-03-29 17:16:40'), +(61, 47, 'assets/images/products/avva-adult-suxoi-korm-na-osnove-svezego-miasa-dlia-vzroslyx-sobak-melkix-porod-s-iagnenkom-i-indeikoi/variation_47/2.webp', 2, '2026-03-29 17:16:40', '2026-03-29 17:16:40'), +(62, 48, 'assets/images/products/avva-adult-suxoi-korm-na-osnove-svezego-miasa-dlia-vzroslyx-sobak-melkix-porod-s-iagnenkom-i-indeikoi/variation_48/0.webp', 0, '2026-03-29 17:16:40', '2026-03-29 17:16:40'), +(63, 48, 'assets/images/products/avva-adult-suxoi-korm-na-osnove-svezego-miasa-dlia-vzroslyx-sobak-melkix-porod-s-iagnenkom-i-indeikoi/variation_48/1.webp', 1, '2026-03-29 17:16:40', '2026-03-29 17:16:40'), +(64, 48, 'assets/images/products/avva-adult-suxoi-korm-na-osnove-svezego-miasa-dlia-vzroslyx-sobak-melkix-porod-s-iagnenkom-i-indeikoi/variation_48/2.webp', 2, '2026-03-29 17:16:40', '2026-03-29 17:16:40'), +(65, 49, 'assets/images/products/alphapet-adult-monoprotein-suxoi-korm-dlia-sobak-srednix-i-krupnyx-porod-belaia-ryba/variation_49/0.webp', 0, '2026-03-29 18:45:42', '2026-03-29 18:45:42'), +(66, 49, 'assets/images/products/alphapet-adult-monoprotein-suxoi-korm-dlia-sobak-srednix-i-krupnyx-porod-belaia-ryba/variation_49/1.webp', 1, '2026-03-29 18:45:42', '2026-03-29 18:45:42'), +(67, 50, 'assets/images/products/alphapet-adult-monoprotein-suxoi-korm-dlia-sobak-srednix-i-krupnyx-porod-belaia-ryba/variation_50/0.webp', 0, '2026-03-29 18:45:42', '2026-03-29 18:45:42'), +(68, 50, 'assets/images/products/alphapet-adult-monoprotein-suxoi-korm-dlia-sobak-srednix-i-krupnyx-porod-belaia-ryba/variation_50/1.webp', 1, '2026-03-29 18:45:42', '2026-03-29 18:45:42'), +(69, 51, 'assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_51/0.webp', 0, '2026-03-29 19:03:16', '2026-03-29 19:03:16'), +(70, 51, 'assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_51/1.webp', 1, '2026-03-29 19:03:16', '2026-03-29 19:03:16'), +(71, 51, 'assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_51/2.webp', 2, '2026-03-29 19:03:16', '2026-03-29 19:03:16'), +(72, 52, 'assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_52/0.webp', 0, '2026-03-29 19:03:16', '2026-03-29 19:03:16'), +(73, 52, 'assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_52/1.webp', 1, '2026-03-29 19:03:16', '2026-03-29 19:03:16'), +(74, 52, 'assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_52/2.webp', 2, '2026-03-29 19:03:16', '2026-03-29 19:03:16'), +(75, 116, 'assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_116/0.webp', 0, '2026-03-29 19:03:16', '2026-03-29 19:03:16'), +(76, 116, 'assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_116/1.webp', 1, '2026-03-29 19:03:16', '2026-03-29 19:03:16'), +(77, 116, 'assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_116/2.webp', 2, '2026-03-29 19:03:16', '2026-03-29 19:03:16'), +(78, 117, 'assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_117/0.webp', 0, '2026-03-29 19:03:16', '2026-03-29 19:03:16'), +(79, 117, 'assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_117/1.webp', 1, '2026-03-29 19:03:16', '2026-03-29 19:03:16'), +(80, 117, 'assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_117/2.webp', 2, '2026-03-29 19:03:16', '2026-03-29 19:03:16'), +(81, 118, 'assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_118/0.webp', 0, '2026-03-29 19:03:16', '2026-03-29 19:03:16'), +(82, 118, 'assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_118/1.webp', 1, '2026-03-29 19:03:16', '2026-03-29 19:03:16'), +(83, 118, 'assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_118/2.webp', 2, '2026-03-29 19:03:16', '2026-03-29 19:03:16'), +(84, 119, 'assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_119/0.webp', 0, '2026-03-29 19:03:16', '2026-03-29 19:03:16'), +(85, 119, 'assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_119/1.webp', 1, '2026-03-29 19:03:16', '2026-03-29 19:03:16'), +(86, 119, 'assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_119/2.webp', 2, '2026-03-29 19:03:16', '2026-03-29 19:03:16'), +(87, 120, 'assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_120/0.webp', 0, '2026-03-29 19:03:16', '2026-03-29 19:03:16'), +(88, 120, 'assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_120/1.webp', 1, '2026-03-29 19:03:16', '2026-03-29 19:03:16'), +(89, 53, 'assets/images/products/klicker-adult-sensitive-digestion-suxoi-korm-dlia-kosek-s-cuvstvitelnym-pishhevareniem/variation_53/0.webp', 0, '2026-03-29 19:06:53', '2026-03-29 19:06:53'), +(90, 53, 'assets/images/products/klicker-adult-sensitive-digestion-suxoi-korm-dlia-kosek-s-cuvstvitelnym-pishhevareniem/variation_53/1.webp', 1, '2026-03-29 19:06:53', '2026-03-29 19:06:53'), +(91, 53, 'assets/images/products/klicker-adult-sensitive-digestion-suxoi-korm-dlia-kosek-s-cuvstvitelnym-pishhevareniem/variation_53/2.webp', 2, '2026-03-29 19:06:53', '2026-03-29 19:06:53'), +(92, 55, 'assets/images/products/grandin-holistic-vlaznyi-korm-konservy-dlia-vzroslyx-kosek/variation_55/0.webp', 0, '2026-03-29 19:12:47', '2026-03-29 19:12:47'), +(93, 55, 'assets/images/products/grandin-holistic-vlaznyi-korm-konservy-dlia-vzroslyx-kosek/variation_55/1.webp', 1, '2026-03-29 19:12:47', '2026-03-29 19:12:47'), +(94, 55, 'assets/images/products/grandin-holistic-vlaznyi-korm-konservy-dlia-vzroslyx-kosek/variation_55/2.webp', 2, '2026-03-29 19:12:47', '2026-03-29 19:12:47'), +(95, 56, 'assets/images/products/grandin-holistic-vlaznyi-korm-konservy-dlia-vzroslyx-kosek/variation_56/0.webp', 0, '2026-03-29 19:12:47', '2026-03-29 19:12:47'), +(96, 56, 'assets/images/products/grandin-holistic-vlaznyi-korm-konservy-dlia-vzroslyx-kosek/variation_56/1.webp', 1, '2026-03-29 19:12:47', '2026-03-29 19:12:47'), +(97, 56, 'assets/images/products/grandin-holistic-vlaznyi-korm-konservy-dlia-vzroslyx-kosek/variation_56/2.webp', 2, '2026-03-29 19:12:47', '2026-03-29 19:12:47'), +(98, 57, 'assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_57/0.webp', 0, '2026-03-29 19:22:45', '2026-03-29 19:22:45'), +(99, 57, 'assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_57/1.webp', 1, '2026-03-29 19:22:45', '2026-03-29 19:22:45'), +(100, 57, 'assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_57/2.webp', 2, '2026-03-29 19:22:45', '2026-03-29 19:22:45'), +(101, 57, 'assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_57/3.webp', 3, '2026-03-29 19:22:45', '2026-03-29 19:22:45'), +(102, 58, 'assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_58/0.webp', 0, '2026-03-29 19:22:45', '2026-03-29 19:22:45'), +(103, 58, 'assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_58/1.webp', 1, '2026-03-29 19:22:45', '2026-03-29 19:22:45'), +(104, 58, 'assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_58/2.webp', 2, '2026-03-29 19:22:45', '2026-03-29 19:22:45'), +(105, 58, 'assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_58/3.webp', 3, '2026-03-29 19:22:45', '2026-03-29 19:22:45'), +(106, 121, 'assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_121/0.webp', 0, '2026-03-29 19:22:45', '2026-03-29 19:22:45'), +(107, 121, 'assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_121/1.webp', 1, '2026-03-29 19:22:45', '2026-03-29 19:22:45'), +(108, 121, 'assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_121/2.webp', 2, '2026-03-29 19:22:45', '2026-03-29 19:22:45'), +(109, 121, 'assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_121/3.webp', 3, '2026-03-29 19:22:45', '2026-03-29 19:22:45'), +(110, 122, 'assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_122/0.webp', 0, '2026-03-29 19:22:45', '2026-03-29 19:22:45'), +(111, 122, 'assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_122/1.webp', 1, '2026-03-29 19:22:45', '2026-03-29 19:22:45'), +(112, 122, 'assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_122/2.webp', 2, '2026-03-29 19:22:45', '2026-03-29 19:22:45'), +(113, 122, 'assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_122/3.webp', 3, '2026-03-29 19:22:45', '2026-03-29 19:22:45'), +(114, 123, 'assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_123/0.webp', 0, '2026-03-29 19:22:45', '2026-03-29 19:22:45'), +(115, 123, 'assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_123/1.webp', 1, '2026-03-29 19:22:45', '2026-03-29 19:22:45'), +(116, 123, 'assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_123/2.webp', 2, '2026-03-29 19:22:45', '2026-03-29 19:22:45'), +(117, 123, 'assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_123/3.webp', 3, '2026-03-29 19:22:45', '2026-03-29 19:22:45'), +(118, 59, 'assets/images/products/alphapet-suxoi-korm-dlia-sterilizovannyx-kosek/variation_59/0.webp', 0, '2026-03-29 19:44:22', '2026-03-29 19:44:22'), +(119, 59, 'assets/images/products/alphapet-suxoi-korm-dlia-sterilizovannyx-kosek/variation_59/1.webp', 1, '2026-03-29 19:44:22', '2026-03-29 19:44:22'), +(120, 59, 'assets/images/products/alphapet-suxoi-korm-dlia-sterilizovannyx-kosek/variation_59/2.webp', 2, '2026-03-29 19:44:22', '2026-03-29 19:44:22'), +(121, 60, 'assets/images/products/alphapet-suxoi-korm-dlia-sterilizovannyx-kosek/variation_60/0.webp', 0, '2026-03-29 19:44:22', '2026-03-29 19:44:22'), +(122, 60, 'assets/images/products/alphapet-suxoi-korm-dlia-sterilizovannyx-kosek/variation_60/1.webp', 1, '2026-03-29 19:44:22', '2026-03-29 19:44:22'), +(123, 60, 'assets/images/products/alphapet-suxoi-korm-dlia-sterilizovannyx-kosek/variation_60/2.webp', 2, '2026-03-29 19:44:22', '2026-03-29 19:44:22'), +(133, 127, 'assets/images/products/alphapet-suxoi-korm-dlia-sterilizovannyx-kosek/variation_127/0.webp', 0, '2026-03-29 19:45:46', '2026-03-29 19:45:46'), +(134, 127, 'assets/images/products/alphapet-suxoi-korm-dlia-sterilizovannyx-kosek/variation_127/1.webp', 1, '2026-03-29 19:45:46', '2026-03-29 19:45:46'), +(135, 127, 'assets/images/products/alphapet-suxoi-korm-dlia-sterilizovannyx-kosek/variation_127/2.webp', 2, '2026-03-29 19:45:46', '2026-03-29 19:45:46'), +(136, 61, 'assets/images/products/tetra-min-holiday-korm-zele/variation_61/0.webp', 0, '2026-03-29 19:49:44', '2026-03-29 19:49:44'), +(137, 63, 'assets/images/products/little-one-korm-dlia-morskix-svinok/variation_63/0.webp', 0, '2026-03-29 19:55:44', '2026-03-29 19:55:44'), +(138, 63, 'assets/images/products/little-one-korm-dlia-morskix-svinok/variation_63/1.webp', 1, '2026-03-29 19:55:44', '2026-03-29 19:55:44'), +(139, 63, 'assets/images/products/little-one-korm-dlia-morskix-svinok/variation_63/2.webp', 2, '2026-03-29 19:55:44', '2026-03-29 19:55:44'), +(140, 64, 'assets/images/products/little-one-korm-dlia-morskix-svinok/variation_64/0.webp', 0, '2026-03-29 19:55:44', '2026-03-29 19:55:44'), +(141, 64, 'assets/images/products/little-one-korm-dlia-morskix-svinok/variation_64/1.webp', 1, '2026-03-29 19:55:44', '2026-03-29 19:55:44'), +(142, 64, 'assets/images/products/little-one-korm-dlia-morskix-svinok/variation_64/2.webp', 2, '2026-03-29 19:55:44', '2026-03-29 19:55:44'), +(143, 67, 'assets/images/products/little-one-korm-dlia-morskix-svinok-zelenaia-dolina/variation_67/0.webp', 0, '2026-03-29 20:05:17', '2026-03-29 20:05:17'), +(144, 67, 'assets/images/products/little-one-korm-dlia-morskix-svinok-zelenaia-dolina/variation_67/1.webp', 1, '2026-03-29 20:05:17', '2026-03-29 20:05:17'), +(145, 67, 'assets/images/products/little-one-korm-dlia-morskix-svinok-zelenaia-dolina/variation_67/2.webp', 2, '2026-03-29 20:05:17', '2026-03-29 20:05:17'), +(146, 69, 'assets/images/products/rungo-kombinezon-teplyi-dlia-sobak-porody-mops/variation_69/0.webp', 0, '2026-03-29 20:12:19', '2026-03-29 20:12:19'), +(147, 70, 'assets/images/products/rungo-kombinezon-teplyi-dlia-sobak-porody-mops/variation_70/0.webp', 0, '2026-03-29 20:12:19', '2026-03-29 20:12:19'), +(148, 128, 'assets/images/products/rungo-kombinezon-teplyi-dlia-sobak-porody-mops/variation_128/0.webp', 0, '2026-03-29 20:12:19', '2026-03-29 20:12:19'), +(149, 129, 'assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_129/0.webp', 0, '2026-03-30 06:08:32', '2026-03-30 06:08:32'), +(150, 129, 'assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_129/1.webp', 1, '2026-03-30 06:08:32', '2026-03-30 06:08:32'), +(151, 129, 'assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_129/2.webp', 2, '2026-03-30 06:08:32', '2026-03-30 06:08:32'), +(152, 130, 'assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_130/0.webp', 0, '2026-03-30 06:08:32', '2026-03-30 06:08:32'), +(153, 130, 'assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_130/1.webp', 1, '2026-03-30 06:08:32', '2026-03-30 06:08:32'), +(154, 130, 'assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_130/2.webp', 2, '2026-03-30 06:08:32', '2026-03-30 06:08:32'), +(155, 131, 'assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_131/0.webp', 0, '2026-03-30 06:08:32', '2026-03-30 06:08:32'), +(156, 131, 'assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_131/1.webp', 1, '2026-03-30 06:08:32', '2026-03-30 06:08:32'), +(157, 131, 'assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_131/2.webp', 2, '2026-03-30 06:08:32', '2026-03-30 06:08:32'), +(158, 132, 'assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_132/0.webp', 0, '2026-03-30 06:08:32', '2026-03-30 06:08:32'), +(159, 132, 'assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_132/1.webp', 1, '2026-03-30 06:08:32', '2026-03-30 06:08:32'), +(160, 132, 'assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_132/2.webp', 2, '2026-03-30 06:08:32', '2026-03-30 06:08:32'), +(161, 133, 'assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_133/0.webp', 0, '2026-03-30 06:08:32', '2026-03-30 06:08:32'), +(162, 133, 'assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_133/1.webp', 1, '2026-03-30 06:08:32', '2026-03-30 06:08:32'), +(163, 133, 'assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_133/2.webp', 2, '2026-03-30 06:08:32', '2026-03-30 06:08:32'), +(164, 134, 'assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_134/0.webp', 0, '2026-03-30 06:08:32', '2026-03-30 06:08:32'), +(165, 134, 'assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_134/1.webp', 1, '2026-03-30 06:08:32', '2026-03-30 06:08:32'), +(166, 134, 'assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_134/2.webp', 2, '2026-03-30 06:08:32', '2026-03-30 06:08:32'), +(167, 135, 'assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_135/0.webp', 0, '2026-03-30 06:08:32', '2026-03-30 06:08:32'), +(168, 135, 'assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_135/1.webp', 1, '2026-03-30 06:08:32', '2026-03-30 06:08:32'), +(169, 137, 'assets/images/products/ownat-adult-sterilized-grain-free-prime-suxoi-korm-dlia-sterilizovannyx-kosek/variation_137/0.webp', 0, '2026-03-30 06:56:38', '2026-03-30 06:56:38'), +(170, 137, 'assets/images/products/ownat-adult-sterilized-grain-free-prime-suxoi-korm-dlia-sterilizovannyx-kosek/variation_137/1.webp', 1, '2026-03-30 06:56:38', '2026-03-30 06:56:38'), +(171, 137, 'assets/images/products/ownat-adult-sterilized-grain-free-prime-suxoi-korm-dlia-sterilizovannyx-kosek/variation_137/2.webp', 2, '2026-03-30 06:56:38', '2026-03-30 06:56:38'), +(172, 138, 'assets/images/products/ownat-adult-sterilized-grain-free-prime-suxoi-korm-dlia-sterilizovannyx-kosek/variation_138/0.webp', 0, '2026-03-30 06:56:38', '2026-03-30 06:56:38'), +(173, 138, 'assets/images/products/ownat-adult-sterilized-grain-free-prime-suxoi-korm-dlia-sterilizovannyx-kosek/variation_138/1.webp', 1, '2026-03-30 06:56:38', '2026-03-30 06:56:38'), +(174, 138, 'assets/images/products/ownat-adult-sterilized-grain-free-prime-suxoi-korm-dlia-sterilizovannyx-kosek/variation_138/2.webp', 2, '2026-03-30 06:56:38', '2026-03-30 06:56:38'), +(175, 139, 'assets/images/products/alphapet-wow-suxoi-korm-dlia-sterilizovannyx-kosek/variation_139/0.webp', 0, '2026-03-30 07:01:27', '2026-03-30 07:01:27'), +(176, 139, 'assets/images/products/alphapet-wow-suxoi-korm-dlia-sterilizovannyx-kosek/variation_139/1.webp', 1, '2026-03-30 07:01:27', '2026-03-30 07:01:27'), +(177, 140, 'assets/images/products/alphapet-wow-suxoi-korm-dlia-sterilizovannyx-kosek/variation_140/0.webp', 0, '2026-03-30 07:01:27', '2026-03-30 07:01:27'), +(178, 140, 'assets/images/products/alphapet-wow-suxoi-korm-dlia-sterilizovannyx-kosek/variation_140/1.webp', 1, '2026-03-30 07:01:27', '2026-03-30 07:01:27'), +(179, 141, 'assets/images/products/alphapet-wow-suxoi-korm-dlia-sterilizovannyx-kosek/variation_141/0.webp', 0, '2026-03-30 07:01:27', '2026-03-30 07:01:27'), +(180, 141, 'assets/images/products/alphapet-wow-suxoi-korm-dlia-sterilizovannyx-kosek/variation_141/1.webp', 1, '2026-03-30 07:01:27', '2026-03-30 07:01:27'), +(181, 135, 'assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_135/2.webp', 2, NULL, NULL), +(182, 136, 'assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_136/0.webp', 0, NULL, NULL), +(183, 136, 'assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_136/1.webp', 1, NULL, NULL), +(184, 136, 'assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_136/2.webp', 2, NULL, NULL); + +-- +-- Índices para tablas volcadas +-- + +-- +-- Indices de la tabla `brands` +-- +ALTER TABLE `brands` + ADD PRIMARY KEY (`id`), + ADD UNIQUE KEY `brands_slug_unique` (`slug`); + +-- +-- Indices de la tabla `cart_items` +-- +ALTER TABLE `cart_items` + ADD PRIMARY KEY (`id`), + ADD KEY `cart_items_variation_id_foreign` (`variation_id`), + ADD KEY `cart_items_user_id_session_id_index` (`user_id`,`session_id`), + ADD KEY `cart_items_session_id_index` (`session_id`); + +-- +-- Indices de la tabla `categories` +-- +ALTER TABLE `categories` + ADD PRIMARY KEY (`id`), + ADD UNIQUE KEY `categories_slug_unique` (`slug`), + ADD UNIQUE KEY `categories_name_parent_id_unique` (`name`,`parent_id`), + ADD KEY `categories_parent_id_index` (`parent_id`), + ADD KEY `categories_sort_order_index` (`sort_order`); + +-- +-- Indices de la tabla `contacts` +-- +ALTER TABLE `contacts` + ADD PRIMARY KEY (`id`); + +-- +-- Indices de la tabla `failed_jobs` +-- +ALTER TABLE `failed_jobs` + ADD PRIMARY KEY (`id`), + ADD UNIQUE KEY `failed_jobs_uuid_unique` (`uuid`); + +-- +-- Indices de la tabla `migrations` +-- +ALTER TABLE `migrations` + ADD PRIMARY KEY (`id`); + +-- +-- Indices de la tabla `orders` +-- +ALTER TABLE `orders` + ADD PRIMARY KEY (`id`), + ADD UNIQUE KEY `orders_order_number_unique` (`order_number`), + ADD KEY `orders_user_id_foreign` (`user_id`); + +-- +-- Indices de la tabla `order_items` +-- +ALTER TABLE `order_items` + ADD PRIMARY KEY (`id`), + ADD KEY `order_items_order_id_foreign` (`order_id`), + ADD KEY `order_items_variation_id_foreign` (`variation_id`); + +-- +-- Indices de la tabla `password_reset_tokens` +-- +ALTER TABLE `password_reset_tokens` + ADD PRIMARY KEY (`email`); + +-- +-- Indices de la tabla `permissions` +-- +ALTER TABLE `permissions` + ADD PRIMARY KEY (`id`), + ADD UNIQUE KEY `permissions_name_unique` (`name`), + ADD UNIQUE KEY `permissions_slug_unique` (`slug`); + +-- +-- Indices de la tabla `permission_role` +-- +ALTER TABLE `permission_role` + ADD PRIMARY KEY (`id`), + ADD UNIQUE KEY `permission_role_permission_id_role_id_unique` (`permission_id`,`role_id`), + ADD KEY `permission_role_role_id_foreign` (`role_id`); + +-- +-- Indices de la tabla `personal_access_tokens` +-- +ALTER TABLE `personal_access_tokens` + ADD PRIMARY KEY (`id`), + ADD UNIQUE KEY `personal_access_tokens_token_unique` (`token`), + ADD KEY `personal_access_tokens_tokenable_type_tokenable_id_index` (`tokenable_type`,`tokenable_id`); + +-- +-- Indices de la tabla `products` +-- +ALTER TABLE `products` + ADD PRIMARY KEY (`id`), + ADD UNIQUE KEY `products_slug_unique` (`slug`), + ADD KEY `products_brand_id_index` (`brand_id`), + ADD KEY `products_category_id_index` (`category_id`), + ADD KEY `products_is_active_index` (`is_active`); + +-- +-- Indices de la tabla `product_attributes` +-- +ALTER TABLE `product_attributes` + ADD PRIMARY KEY (`id`), + ADD KEY `product_attributes_product_id_key_index` (`product_id`,`key`); + +-- +-- Indices de la tabla `product_variations` +-- +ALTER TABLE `product_variations` + ADD PRIMARY KEY (`id`), + ADD UNIQUE KEY `product_variations_sku_unique` (`sku`), + ADD KEY `product_variations_sku_index` (`sku`), + ADD KEY `product_variations_product_id_index` (`product_id`); + +-- +-- Indices de la tabla `reviews` +-- +ALTER TABLE `reviews` + ADD PRIMARY KEY (`id`), + ADD KEY `reviews_user_id_foreign` (`user_id`), + ADD KEY `reviews_product_id_foreign` (`product_id`), + ADD KEY `reviews_rating_index` (`rating`), + ADD KEY `reviews_is_approved_index` (`is_approved`), + ADD KEY `reviews_created_at_index` (`created_at`); + +-- +-- Indices de la tabla `roles` +-- +ALTER TABLE `roles` + ADD PRIMARY KEY (`id`), + ADD UNIQUE KEY `roles_name_unique` (`name`), + ADD UNIQUE KEY `roles_slug_unique` (`slug`); + +-- +-- Indices de la tabla `users` +-- +ALTER TABLE `users` + ADD PRIMARY KEY (`id`), + ADD UNIQUE KEY `users_email_unique` (`email`), + ADD KEY `users_phone_index` (`phone`), + ADD KEY `users_email_index` (`email`), + ADD KEY `users_role_id_foreign` (`role_id`); + +-- +-- Indices de la tabla `variation_attributes` +-- +ALTER TABLE `variation_attributes` + ADD PRIMARY KEY (`id`), + ADD KEY `variation_attributes_variation_id_key_index` (`variation_id`,`key`); + +-- +-- Indices de la tabla `variation_images` +-- +ALTER TABLE `variation_images` + ADD PRIMARY KEY (`id`), + ADD KEY `variation_images_variation_id_sort_order_index` (`variation_id`,`sort_order`); + +-- +-- AUTO_INCREMENT de las tablas volcadas +-- + +-- +-- AUTO_INCREMENT de la tabla `brands` +-- +ALTER TABLE `brands` + MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=17; + +-- +-- AUTO_INCREMENT de la tabla `cart_items` +-- +ALTER TABLE `cart_items` + MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=21; + +-- +-- AUTO_INCREMENT de la tabla `categories` +-- +ALTER TABLE `categories` + MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=66; + +-- +-- AUTO_INCREMENT de la tabla `contacts` +-- +ALTER TABLE `contacts` + MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=2; + +-- +-- AUTO_INCREMENT de la tabla `failed_jobs` +-- +ALTER TABLE `failed_jobs` + MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT; + +-- +-- AUTO_INCREMENT de la tabla `migrations` +-- +ALTER TABLE `migrations` + MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=37; + +-- +-- AUTO_INCREMENT de la tabla `orders` +-- +ALTER TABLE `orders` + MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=4; + +-- +-- AUTO_INCREMENT de la tabla `order_items` +-- +ALTER TABLE `order_items` + MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=7; + +-- +-- AUTO_INCREMENT de la tabla `permissions` +-- +ALTER TABLE `permissions` + MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=16; + +-- +-- AUTO_INCREMENT de la tabla `permission_role` +-- +ALTER TABLE `permission_role` + MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=38; + +-- +-- AUTO_INCREMENT de la tabla `personal_access_tokens` +-- +ALTER TABLE `personal_access_tokens` + MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT; + +-- +-- AUTO_INCREMENT de la tabla `products` +-- +ALTER TABLE `products` + MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=60; + +-- +-- AUTO_INCREMENT de la tabla `product_attributes` +-- +ALTER TABLE `product_attributes` + MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=207; + +-- +-- AUTO_INCREMENT de la tabla `product_variations` +-- +ALTER TABLE `product_variations` + MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=142; + +-- +-- AUTO_INCREMENT de la tabla `reviews` +-- +ALTER TABLE `reviews` + MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT; + +-- +-- AUTO_INCREMENT de la tabla `roles` +-- +ALTER TABLE `roles` + MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=8; + +-- +-- AUTO_INCREMENT de la tabla `users` +-- +ALTER TABLE `users` + MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=3; + +-- +-- AUTO_INCREMENT de la tabla `variation_attributes` +-- +ALTER TABLE `variation_attributes` + MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=271; + +-- +-- AUTO_INCREMENT de la tabla `variation_images` +-- +ALTER TABLE `variation_images` + MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=185; + +-- +-- Restricciones para tablas volcadas +-- + +-- +-- Filtros para la tabla `cart_items` +-- +ALTER TABLE `cart_items` + ADD CONSTRAINT `cart_items_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE, + ADD CONSTRAINT `cart_items_variation_id_foreign` FOREIGN KEY (`variation_id`) REFERENCES `product_variations` (`id`) ON DELETE CASCADE; + +-- +-- Filtros para la tabla `categories` +-- +ALTER TABLE `categories` + ADD CONSTRAINT `categories_parent_id_foreign` FOREIGN KEY (`parent_id`) REFERENCES `categories` (`id`) ON DELETE CASCADE; + +-- +-- Filtros para la tabla `orders` +-- +ALTER TABLE `orders` + ADD CONSTRAINT `orders_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE SET NULL; + +-- +-- Filtros para la tabla `order_items` +-- +ALTER TABLE `order_items` + ADD CONSTRAINT `order_items_order_id_foreign` FOREIGN KEY (`order_id`) REFERENCES `orders` (`id`) ON DELETE CASCADE, + ADD CONSTRAINT `order_items_variation_id_foreign` FOREIGN KEY (`variation_id`) REFERENCES `product_variations` (`id`) ON DELETE CASCADE; + +-- +-- Filtros para la tabla `permission_role` +-- +ALTER TABLE `permission_role` + ADD CONSTRAINT `permission_role_permission_id_foreign` FOREIGN KEY (`permission_id`) REFERENCES `permissions` (`id`) ON DELETE CASCADE, + ADD CONSTRAINT `permission_role_role_id_foreign` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`) ON DELETE CASCADE; + +-- +-- Filtros para la tabla `products` +-- +ALTER TABLE `products` + ADD CONSTRAINT `products_brand_id_foreign` FOREIGN KEY (`brand_id`) REFERENCES `brands` (`id`) ON DELETE SET NULL, + ADD CONSTRAINT `products_category_id_foreign` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`) ON DELETE CASCADE; + +-- +-- Filtros para la tabla `product_attributes` +-- +ALTER TABLE `product_attributes` + ADD CONSTRAINT `product_attributes_product_id_foreign` FOREIGN KEY (`product_id`) REFERENCES `products` (`id`) ON DELETE CASCADE; + +-- +-- Filtros para la tabla `product_variations` +-- +ALTER TABLE `product_variations` + ADD CONSTRAINT `product_variations_product_id_foreign` FOREIGN KEY (`product_id`) REFERENCES `products` (`id`) ON DELETE CASCADE; + +-- +-- Filtros para la tabla `reviews` +-- +ALTER TABLE `reviews` + ADD CONSTRAINT `reviews_product_id_foreign` FOREIGN KEY (`product_id`) REFERENCES `products` (`id`) ON DELETE CASCADE, + ADD CONSTRAINT `reviews_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE; + +-- +-- Filtros para la tabla `users` +-- +ALTER TABLE `users` + ADD CONSTRAINT `users_role_id_foreign` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`) ON DELETE SET NULL; + +-- +-- Filtros para la tabla `variation_attributes` +-- +ALTER TABLE `variation_attributes` + ADD CONSTRAINT `variation_attributes_variation_id_foreign` FOREIGN KEY (`variation_id`) REFERENCES `product_variations` (`id`) ON DELETE CASCADE; + +-- +-- Filtros para la tabla `variation_images` +-- +ALTER TABLE `variation_images` + ADD CONSTRAINT `variation_images_variation_id_foreign` FOREIGN KEY (`variation_id`) REFERENCES `product_variations` (`id`) ON DELETE CASCADE; +COMMIT; + +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php new file mode 100644 index 0000000..e6b9960 --- /dev/null +++ b/app/Console/Kernel.php @@ -0,0 +1,27 @@ +command('inspire')->hourly(); + } + + /** + * Register the commands for the application. + */ + protected function commands(): void + { + $this->load(__DIR__.'/Commands'); + + require base_path('routes/console.php'); + } +} diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php new file mode 100644 index 0000000..56af264 --- /dev/null +++ b/app/Exceptions/Handler.php @@ -0,0 +1,30 @@ + + */ + protected $dontFlash = [ + 'current_password', + 'password', + 'password_confirmation', + ]; + + /** + * Register the exception handling callbacks for the application. + */ + public function register(): void + { + $this->reportable(function (Throwable $e) { + // + }); + } +} diff --git a/app/Http/Controllers/Admin/BrandsController.php b/app/Http/Controllers/Admin/BrandsController.php new file mode 100644 index 0000000..48e4301 --- /dev/null +++ b/app/Http/Controllers/Admin/BrandsController.php @@ -0,0 +1,122 @@ +paginate(5); + return view('admin.brands.brands', compact('brands')); + } + + public function store(Request $request) + { + $validated = $request->validate([ + 'name' => 'required|string|max:255|unique:brands', + 'description' => 'nullable|string', + 'logo' => 'nullable|image|mimes:jpg,jpeg,png,svg|max:2048', + 'website' => 'nullable|url|max:255', + 'country' => 'nullable|string|max:100', + 'is_active' => 'nullable|boolean', + ], [ + 'name.required' => 'Название бренда обязательно', + 'name.unique' => 'Бренд с таким названием уже существует', + 'name.max' => 'Название не может быть длиннее 255 символов', + 'logo.image' => 'Загрузите изображение (jpg, png, svg)', + 'logo.max' => 'Изображение не должно превышать 2 МБ', + 'website.url' => 'Введите корректный URL сайта', + 'website.max' => 'URL сайта не может быть длиннее 255 символов', + 'country.max' => 'Название страны не может быть длиннее 100 символов', + ]); + + if ($request->hasFile('logo')) { + $logo = $request->file('logo'); + $logoName = Str::slug($request->name) . '.' . $logo->getClientOriginalExtension(); + $logo->move(public_path('assets/images/brands'), $logoName); + $validated['logo'] = $logoName; + } + + $validated['slug'] = Str::slug($request->name); + + $validated['is_active'] = $request->has('is_active'); + + Brand::create($validated); + + return redirect()->route('admin.brands') + ->with('success', 'Бренд "' . $request->name . '" успешно создан'); + } + + public function edit($id) + { + $brand = Brand::findOrFail($id); + return view('admin.brands.edit', compact('brand')); + } + + public function update(Request $request, $id) + { + $brand = Brand::findOrFail($id); + + $validated = $request->validate([ + 'name' => 'required|string|max:255', + 'description' => 'nullable|string', + 'logo' => 'nullable|image|mimes:jpg,jpeg,png,svg|max:2048', + 'website' => 'nullable|url|max:255', + 'country' => 'nullable|string|max:100', + 'is_active' => 'nullable|boolean', + ], [ + 'name.required' => 'Название бренда обязательно', + 'name.max' => 'Название не может быть длиннее 255 символов', + 'logo.image' => 'Загрузите изображение (jpg, png, svg)', + 'logo.mimes' => 'Изображение должно быть в форматах: jpg, jpeg, png, svg', + 'logo.max' => 'Изображение не должно превышать 2 МБ', + 'website.url' => 'Введите корректный URL сайта', + 'website.max' => 'URL сайта не может быть длиннее 255 символов', + 'country.max' => 'Название страны не может быть длиннее 100 символов', + ]); + + $brand->name = $validated['name']; + $brand->description = $validated['description']; + $brand->website = $validated['website']; + $brand->country = $validated['country']; + $brand->is_active = $request->has('is_active'); + + if ($request->hasFile('logo')) { + if ($brand->logo && file_exists(public_path('assets/images/brands/' . $brand->logo))) { + unlink(public_path('assets/images/brands/' . $brand->logo)); + } + + $logo = $request->file('logo'); + $logoName = Str::slug($request->name) . '.' . $logo->getClientOriginalExtension(); + $logo->move(public_path('assets/images/brands'), $logoName); + $brand->logo = $logoName; + } + + $brand->save(); + + return redirect()->route('admin.brands') + ->with('success', 'Бренд "' . $brand->name . '" успешно обновлен'); + } + + public function destroy($id) + { + $brand = Brand::findOrFail($id); + + $brandName = $brand->name; + + if ($brand->logo && file_exists(public_path('assets/images/brands/' . $brand->logo))) { + unlink(public_path('assets/images/brands/' . $brand->logo)); + } + + $brand->delete(); + + return redirect()->route('admin.brands')->with('success', 'Бренд "' . $brandName . '" успешно удален'); + } +} diff --git a/app/Http/Controllers/Admin/CategoriesController.php b/app/Http/Controllers/Admin/CategoriesController.php new file mode 100644 index 0000000..b60c249 --- /dev/null +++ b/app/Http/Controllers/Admin/CategoriesController.php @@ -0,0 +1,217 @@ +parent_id) + ->where('sort_order', '<', $category->sort_order) + ->orderBy('sort_order', 'desc') + ->first(); + + if ($previous) { + $temp = $category->sort_order; + $category->sort_order = $previous->sort_order; + $previous->sort_order = $temp; + + $category->save(); + $previous->save(); + } + + return redirect()->route('admin.categories')->with('success', 'Порядок категорий обновлен'); + } + + public function moveDown($id) + { + $category = Category::findOrFail($id); + + $next = Category::where('parent_id', $category->parent_id) + ->where('sort_order', '>', $category->sort_order) + ->orderBy('sort_order', 'asc') + ->first(); + + if ($next) { + $temp = $category->sort_order; + $category->sort_order = $next->sort_order; + $next->sort_order = $temp; + + $category->save(); + $next->save(); + } + + return redirect()->route('admin.categories')->with('success', 'Порядок категорий обновлен'); + } + + public function store(Request $request) + { + $validated = $request->validate([ + 'parent_id' => 'nullable|exists:categories,id', + 'name' => 'required|string|max:255', + 'description' => 'nullable|string', + 'icon' => 'nullable|image|mimes:jpg,jpeg,png,svg|max:2048', + 'image' => 'nullable|image|mimes:jpg,jpeg,png,svg|max:2048', + 'is_active' => 'nullable|boolean', + ], [ + 'name.required' => 'Название категории обязательно', + 'name.max' => 'Название не может быть длиннее 255 символов', + 'icon.image' => 'Загрузите изображение (jpg, png, svg)', + 'icon.max' => 'Изображение не должно превышать 2 МБ', + 'image.image' => 'Загрузите изображение (jpg, png, svg)', + 'image.max' => 'Изображение не должно превышать 2 МБ', + 'parent_id.exists' => 'Выбранная родительская категория не существует', + ]); + + if ($request->hasFile('icon')) { + $icon = $request->file('icon'); + $iconName = Str::slug($request->name) . '_icon.' . $icon->getClientOriginalExtension(); + $icon->move(public_path('assets/images/categories/icons'), $iconName); + $validated['icon'] = $iconName; + } + + if ($request->hasFile('image')) { + $image = $request->file('image'); + $imageName = Str::slug($request->name) . '_image.' . $image->getClientOriginalExtension(); + $image->move(public_path('assets/images/categories/images'), $imageName); + $validated['image'] = $imageName; + } + + $validated['slug'] = Str::slug($request->name); + + if (Category::where('slug', $validated['slug'])->exists()) { + $validated['slug'] = $validated['slug'] . '-' . uniqid(); + } + + $validated['is_active'] = $request->has('is_active'); + $validated['sort_order'] = Category::where('parent_id', $request->parent_id)->max('sort_order') + 10; + + Category::create($validated); + + return redirect()->route('admin.categories') + ->with('success', 'Категория "' . $request->name . '" успешно создана'); + } + + public function edit($id) + { + $category = Category::findOrFail($id); + + $allCategories = Category::with('parent') + ->get() + ->map(function ($cat) use ($category) { + $cat->level = $cat->calculateLevel(); + $cat->disabled = $cat->id == $category->id || $cat->isDescendantOf($category->id); + return $cat; + }) + ->filter(function ($cat) { + return $cat->level < 2; + }); + + return view('admin.categories.edit', compact('category', 'allCategories')); + } + + public function update(Request $request, $id) + { + $category = Category::findOrFail($id); + + $validated = $request->validate([ + 'parent_id' => 'nullable|exists:categories,id', + 'name' => 'required|string|max:255', + 'description' => 'nullable|string', + 'icon' => 'nullable|image|mimes:jpg,jpeg,png,svg|max:2048', + 'image' => 'nullable|image|mimes:jpg,jpeg,png,svg|max:2048', + 'is_active' => 'nullable|boolean', + 'remove_icon' => 'nullable|boolean', + 'remove_image' => 'nullable|boolean', + ], [ + 'name.required' => 'Название категории обязательно', + 'name.max' => 'Название не может быть длиннее 255 символов', + 'icon.image' => 'Загрузите изображение (jpg, png, svg)', + 'icon.max' => 'Изображение не должно превышать 2 МБ', + 'image.image' => 'Загрузите изображение (jpg, png, svg)', + 'image.max' => 'Изображение не должно превышать 2 МБ', + 'parent_id.exists' => 'Выбранная родительская категория не существует', + ]); + + $category->name = $validated['name']; + $category->description = $validated['description']; + $category->parent_id = $validated['parent_id']; + $category->is_active = $request->has('is_active'); + + if ($request->hasFile('icon')) { + if ($category->icon && file_exists(public_path('assets/images/categories/icons/' . $category->icon))) { + unlink(public_path('assets/images/categories/icons/' . $category->icon)); + } + + $icon = $request->file('icon'); + $iconName = Str::slug($request->name) . '_icon.' . $icon->getClientOriginalExtension(); + $icon->move(public_path('assets/images/categories/icons'), $iconName); + $category->icon = $iconName; + } + + if ($request->has('remove_icon') && $request->remove_icon == 1) { + if ($category->icon && file_exists(public_path('assets/images/categories/icons/' . $category->icon))) { + unlink(public_path('assets/images/categories/icons/' . $category->icon)); + } + $category->icon = null; + } + + if ($request->hasFile('image')) { + if ($category->image && file_exists(public_path('assets/images/categories/images/' . $category->image))) { + unlink(public_path('assets/images/categories/images/' . $category->image)); + } + + $image = $request->file('image'); + $imageName = Str::slug($request->name) . '_image.' . $image->getClientOriginalExtension(); + $image->move(public_path('assets/images/categories/images'), $imageName); + $category->image = $imageName; + } + + if ($request->has('remove_image') && $request->remove_image == 1) { + if ($category->image && file_exists(public_path('assets/images/categories/images/' . $category->image))) { + unlink(public_path('assets/images/categories/images/' . $category->image)); + } + $category->image = null; + } + + $category->save(); + + return redirect()->route('admin.categories') + ->with('success', 'Категория "' . $category->name . '" успешно обновлена'); + } + + public function destroy($id) + { + $category = Category::findOrFail($id); + $categoryName = $category->name; + + if ($category->icon && file_exists(public_path('assets/images/categories/icons/' . $category->icon))) { + unlink(public_path('assets/images/categories/icons/' . $category->icon)); + } + + if ($category->image && file_exists(public_path('assets/images/categories/images/' . $category->image))) { + unlink(public_path('assets/images/categories/images/' . $category->image)); + } + + $category->delete(); + + return redirect()->route('admin.categories') + ->with('success', 'Категория "' . $categoryName . '" успешно удалена'); + } +} diff --git a/app/Http/Controllers/Admin/ContactController.php b/app/Http/Controllers/Admin/ContactController.php new file mode 100644 index 0000000..dfae30e --- /dev/null +++ b/app/Http/Controllers/Admin/ContactController.php @@ -0,0 +1,90 @@ + 'Хвостики и лапки' + ]); + } + + return view('admin.contacts.edit', compact('contact')); + } + + public function update(Request $request) + { + $contact = Contact::first(); + + if (!$contact) { + $contact = Contact::create(); + } + + $request->validate([ + 'name' => 'required|string|max:255', + 'description' => 'nullable|string|max:255', + 'logo' => 'nullable|image|mimes:jpeg,png,jpg,gif,svg,webp|max:2048', + 'favicon' => 'nullable|image|mimes:ico,png,svg|max:1024', + 'phone' => 'nullable|string|max:255', + 'email' => 'nullable|email|max:255', + 'address' => 'nullable|string|max:255', + 'work_hours' => 'nullable|string|max:255', + 'telegram' => 'nullable|string|max:255', + 'whatsapp' => 'nullable|string|max:255', + 'vkontakte' => 'nullable|string|max:255', + 'meta_title' => 'nullable|string|max:255', + 'meta_description' => 'nullable|string', + 'meta_keywords' => 'nullable|string' + ]); + + if ($request->hasFile('logo')) { + if ($contact->logo && file_exists(public_path('assets/images/logo/' . $contact->logo))) { + unlink(public_path('assets/images/logo/' . $contact->logo)); + } + + $logoFile = $request->file('logo'); + $logoName = time() . '_logo.' . $logoFile->getClientOriginalExtension(); + $logoFile->move(public_path('assets/images/logo'), $logoName); + $contact->logo = $logoName; + } + + if ($request->hasFile('favicon')) { + if ($contact->favicon && file_exists(public_path('assets/images/logo/' . $contact->favicon))) { + unlink(public_path('assets/images/logo/' . $contact->favicon)); + } + + $faviconFile = $request->file('favicon'); + $faviconName = time() . '_favicon.' . $faviconFile->getClientOriginalExtension(); + $faviconFile->move(public_path('assets/images/logo'), $faviconName); + $contact->favicon = $faviconName; + } + + $contact->update([ + 'name' => $request->name, + 'description' => $request->description, + 'phone' => $request->phone, + 'email' => $request->email, + 'address' => $request->address, + 'work_hours' => $request->work_hours, + 'telegram' => $request->telegram, + 'whatsapp' => $request->whatsapp, + 'vkontakte' => $request->vkontakte, + 'meta_title' => $request->meta_title, + 'meta_description' => $request->meta_description, + 'meta_keywords' => $request->meta_keywords + ]); + + return redirect()->route('admin.contacts.edit') + ->with('success', 'Данные сайта успешно обновлены'); + } +} diff --git a/app/Http/Controllers/Admin/DashboardController.php b/app/Http/Controllers/Admin/DashboardController.php new file mode 100644 index 0000000..7f6ee26 --- /dev/null +++ b/app/Http/Controllers/Admin/DashboardController.php @@ -0,0 +1,31 @@ + User::count(), + 'totalProducts' => ProductVariation::count(), + 'totalOrders' => Order::count(), + 'recentOrders' => Order::with('user') + ->orderBy('created_at', 'desc') + ->limit(5) + ->get(), + 'popularProducts' => OrderItem::popular(10)->get(), + 'monthlyRevenue' => Order::getMonthlyRevenue(), + + ]; + return view('admin.dashboard', $data); + } +} diff --git a/app/Http/Controllers/Admin/OrdersController.php b/app/Http/Controllers/Admin/OrdersController.php new file mode 100644 index 0000000..7b249d8 --- /dev/null +++ b/app/Http/Controllers/Admin/OrdersController.php @@ -0,0 +1,61 @@ +filled('search')) { + $query->where('order_number', 'like', "%{$request->search}%"); + } + + // Фильтр по статусу + if ($request->filled('status')) { + $query->where('delivery_status', $request->status); + } + + $orders = $query->orderBy('created_at', 'desc')->paginate(20); + + return view('admin.orders.orders', compact('orders')); + } + + /** + * Детали заказа + */ + public function show($id) + { + $order = Order::with(['user', 'items.variation.product'])->findOrFail($id); + return view('admin.orders.show', compact('order')); + } + + /** + * Обновление статуса заказа + */ + public function updateStatus(Request $request, $id) + { + $request->validate([ + 'delivery_status' => 'required|in:pending,processing,shipped,delivered,cancelled', + 'payment_status' => 'required|in:pending,paid,failed' + ]); + + $order = Order::findOrFail($id); + $order->update([ + 'delivery_status' => $request->delivery_status, + 'payment_status' => $request->payment_status + ]); + + return redirect()->route('admin.orders.show', $order->id) + ->with('success', 'Статус заказа обновлен'); + } +} diff --git a/app/Http/Controllers/Admin/PermissionsController.php b/app/Http/Controllers/Admin/PermissionsController.php new file mode 100644 index 0000000..aef51aa --- /dev/null +++ b/app/Http/Controllers/Admin/PermissionsController.php @@ -0,0 +1,107 @@ +orderBy('name')->paginate(10); + return view('admin.permissions.permissions', compact('permissions')); + } + + /** + * Сохранить новое право + */ + public function store(Request $request) + { + $request->validate([ + 'name' => 'required|string|max:255|unique:permissions', + 'slug' => 'required|string|max:255|unique:permissions', + 'group' => 'nullable|string|max:255', + 'description' => 'nullable|string' + ], [ + 'name.required' => 'Обязательно для заполнения', + 'name.unique' => 'Право с таким названием уже существует', + 'slug.required' => 'Обязательно для заполнения', + 'slug.unique' => 'Право с таким slug уже существует' + ]); + + Permission::create([ + 'name' => $request->name, + 'slug' => $request->slug, + 'group' => $request->group, + 'description' => $request->description + ]); + + return redirect()->route('admin.permissions') + ->with('success', 'Право "' . $request->name . '" успешно создано'); + } + + /** + * Форма редактирования + */ + public function edit($id) + { + $permission = Permission::findOrFail($id); + return view('admin.permissions.edit', compact('permission')); + } + + /** + * Обновить право + */ + public function update(Request $request, $id) + { + $permission = Permission::findOrFail($id); + + $request->validate([ + 'name' => 'required|string|max:255|unique:permissions,name,' . $id, + 'slug' => 'required|string|max:255|unique:permissions,slug,' . $id, + 'group' => 'nullable|string|max:255', + 'description' => 'nullable|string' + ], [ + 'name.required' => 'Обязательно для заполнения', + 'name.unique' => 'Право с таким названием уже существует', + 'slug.required' => 'Обязательно для заполнения', + 'slug.unique' => 'Право с таким slug уже существует' + ]); + + $permission->update([ + 'name' => $request->name, + 'slug' => $request->slug, + 'group' => $request->group, + 'description' => $request->description + ]); + + return redirect()->route('admin.permissions') + ->with('success', 'Право "' . $permission->name . '" успешно обновлено'); + } + + /** + * Удалить право + */ + public function destroy($id) + { + $permission = Permission::findOrFail($id); + + $rolesCount = $permission->roles()->count(); + + $permissionName = $permission->name; + $permission->delete(); + + $message = 'Право "' . $permissionName . '" успешно удалено'; + if ($rolesCount > 0) { + $message .= ' Право было удалено у ' . $rolesCount . ' ролей.'; + } + + return redirect()->route('admin.permissions') + ->with('success', $message); + } +} diff --git a/app/Http/Controllers/Admin/ProductController.php b/app/Http/Controllers/Admin/ProductController.php new file mode 100644 index 0000000..6b2e6ee --- /dev/null +++ b/app/Http/Controllers/Admin/ProductController.php @@ -0,0 +1,722 @@ +orderBy('created_at', 'desc') + ->paginate(15); + + return view('admin.products.products', compact('products')); + } + + public function create() + { + $categories = Category::getFlatTree(); + $brands = Brand::active()->orderBy('name')->get(); + + return view('admin.products.create', compact('categories', 'brands')); + } + + public function store(Request $request) + { + $validated = $request->validate([ + 'name' => 'required|string|max:255', + 'category_id' => 'required|exists:categories,id', + 'brand_id' => 'nullable|exists:brands,id', + 'description' => 'nullable|string', + 'is_active' => 'nullable|boolean', + + 'meta_title' => 'nullable|string|max:255', + 'meta_description' => 'nullable|string', + 'meta_keywords' => 'nullable|string', + + 'attributes' => 'nullable|array', + 'attributes.*.key' => 'nullable|string', + 'attributes.*.value' => 'nullable|string', + + 'variations' => 'required|array|min:1', + 'variations.*.name' => 'required|string|max:255', + 'variations.*.sku' => 'required|string|max:100|unique:product_variations,sku', + 'variations.*.price' => 'required|numeric|min:0', + 'variations.*.old_price' => 'nullable|numeric|min:0', + 'variations.*.stock' => 'required|integer|min:0', + 'variations.*.is_default' => 'nullable|boolean', + 'variations.*.attributes' => 'nullable|array', + 'variations.*.attributes.flavor' => 'nullable|string|max:100', + 'variations.*.attributes.color' => 'nullable|string|max:100', + 'variations.*.attributes.size' => 'nullable|string|max:50', + 'variations.*.attributes.weight' => 'nullable|numeric|min:0', + + 'variations.*.images' => 'nullable|array', + 'variations.*.images.*' => 'nullable|image|mimes:jpg,jpeg,png,webp|max:2048', + ], [ + 'name.required' => 'Название товара обязательно', + 'category_id.required' => 'Выберите категорию', + 'variations.required' => 'Добавьте хотя бы одну вариацию', + 'variations.*.name.required' => 'Название вариации обязательно', + 'variations.*.sku.required' => 'Артикул обязателен', + 'variations.*.sku.unique' => 'Артикул "{value}" уже существует', + 'variations.*.price.required' => 'Цена обязательна', + 'variations.*.stock.required' => 'Количество обязательно', + 'variations.*.images.*.image' => 'Файл должен быть изображением', + 'variations.*.images.*.mimes' => 'Допустимые форматы: jpg, jpeg, png, webp', + 'variations.*.images.*.max' => 'Размер изображения не должен превышать 2 МБ', + ]); + + DB::beginTransaction(); + + try { + $slug = Str::slug($request->name); + $baseSlug = $slug; + $counter = 1; + while (Product::where('slug', $slug)->exists()) { + $slug = $baseSlug . '-' . $counter; + $counter++; + } + + $product = Product::create([ + 'name' => $validated['name'], + 'slug' => $slug, + 'category_id' => $validated['category_id'], + 'brand_id' => $validated['brand_id'] ?? null, + 'description' => $validated['description'] ?? null, + 'is_active' => $request->boolean('is_active'), + 'meta_title' => $validated['meta_title'] ?? null, + 'meta_description' => $validated['meta_description'] ?? null, + 'meta_keywords' => $validated['meta_keywords'] ?? null, + ]); + + // Сохраняем атрибуты товара + if (!empty($validated['attributes'])) { + foreach ($validated['attributes'] as $attr) { + if (!empty($attr['key']) && !empty($attr['value'])) { + ProductAttribute::create([ + 'product_id' => $product->id, + 'key' => $attr['key'], + 'value' => $attr['value'], + ]); + } + } + } + + // Создаем вариации + foreach ($validated['variations'] as $index => $variationData) { + $variation = ProductVariation::create([ + 'product_id' => $product->id, + 'name' => $variationData['name'], + 'sku' => $variationData['sku'], + 'price' => $variationData['price'], + 'old_price' => $variationData['old_price'] ?? null, + 'stock' => $variationData['stock'], + 'is_default' => $variationData['is_default'] ?? ($index == 0), + 'is_active' => true, + ]); + + // Сохраняем атрибуты вариации + if (!empty($variationData['attributes'])) { + foreach ($variationData['attributes'] as $key => $value) { + if (!empty($value) || $value === 0 || $value === '0') { + VariationAttribute::create([ + 'variation_id' => $variation->id, + 'key' => $key, + 'value' => $value, + ]); + } + } + } + + // Сохраняем изображения + if ($request->hasFile("variations.{$index}.images")) { + $files = $request->file("variations.{$index}.images"); + $validFiles = array_filter($files, function ($file) { + return $file !== null && $file->isValid(); + }); + + if (!empty($validFiles)) { + $this->saveVariationImages($validFiles, $product->slug, $variation->id); + } + } + } + + DB::commit(); + + return redirect()->route('admin.products.create') + ->with('success', 'Товар "' . $product->name . '" успешно создан'); + } catch (\Exception $e) { + DB::rollBack(); + Log::error('Product create error: ' . $e->getMessage()); + return redirect()->back() + ->withInput() + ->with('error', 'Ошибка при создании товара: ' . $e->getMessage()); + } + } + + public function edit($id) + { + $product = Product::with(['variations.attributes', 'variations.images', 'attributes', 'brand', 'category'])->findOrFail($id); + + $categories = Category::getFlatTree(); + + $brands = Brand::active()->orderBy('name')->get(); + + return view('admin.products.edit', compact('product', 'categories', 'brands')); + } + + public function update(Request $request, $id) + { + $product = Product::findOrFail($id); + + $validated = $request->validate([ + 'name' => 'required|string|max:255', + 'category_id' => 'required|exists:categories,id', + 'brand_id' => 'nullable|exists:brands,id', + 'description' => 'nullable|string', + 'is_active' => 'nullable|boolean', + + 'meta_title' => 'nullable|string|max:255', + 'meta_description' => 'nullable|string', + 'meta_keywords' => 'nullable|string', + + 'attributes' => 'nullable|array', + 'attributes.*.key' => 'nullable|string', + 'attributes.*.value' => 'nullable|string', + + 'variations' => 'required|array|min:1', + 'variations.*.id' => 'nullable|exists:product_variations,id', + 'variations.*.name' => 'required|string|max:255', + 'variations.*.sku' => 'required|string|max:100', + 'variations.*.price' => 'required|numeric|min:0', + 'variations.*.old_price' => 'nullable|numeric|min:0', + 'variations.*.stock' => 'required|integer|min:0', + 'variations.*.is_default' => 'nullable|boolean', + 'variations.*.attributes' => 'nullable|array', + 'variations.*.attributes.flavor' => 'nullable|string|max:100', + 'variations.*.attributes.color' => 'nullable|string|max:100', + 'variations.*.attributes.size' => 'nullable|string|max:50', + 'variations.*.attributes.weight' => 'nullable|numeric|min:0', + + 'variations.*.images' => 'nullable|array', + 'variations.*.images.*' => 'nullable|image|mimes:jpg,jpeg,png,webp|max:2048', + 'variations.*.removed_images' => 'nullable|array', + 'variations.*.removed_images.*' => 'nullable|string', + 'variations.*.existing_images' => 'nullable|array', + 'variations.*.existing_images.*' => 'nullable|string', + ], [ + // Основные поля + 'name.required' => 'Название товара обязательно', + 'name.string' => 'Название товара должно быть строкой', + 'name.max' => 'Название товара не может превышать 255 символов', + + 'category_id.required' => 'Выберите категорию', + 'category_id.exists' => 'Выбранная категория не существует', + + 'brand_id.exists' => 'Выбранный бренд не существует', + + 'meta_title.max' => 'Meta Title не может превышать 255 символов', + + // Вариации + 'variations.required' => 'Добавьте хотя бы одну вариацию', + 'variations.array' => 'Некорректный формат вариаций', + 'variations.min' => 'Добавьте хотя бы одну вариацию', + + 'variations.*.id.exists' => 'Вариация с ID :input не найдена', + + 'variations.*.name.required' => 'Название вариации обязательно', + 'variations.*.name.string' => 'Название вариации должно быть строкой', + 'variations.*.name.max' => 'Название вариации не может превышать 255 символов', + + 'variations.*.sku.required' => 'Артикул (SKU) обязателен', + 'variations.*.sku.string' => 'Артикул должен быть строкой', + 'variations.*.sku.max' => 'Артикул не может превышать 100 символов', + + 'variations.*.price.required' => 'Цена обязательна', + 'variations.*.price.numeric' => 'Цена должна быть числом', + 'variations.*.price.min' => 'Цена не может быть отрицательной', + + 'variations.*.old_price.numeric' => 'Старая цена должна быть числом', + 'variations.*.old_price.min' => 'Старая цена не может быть отрицательной', + + 'variations.*.stock.required' => 'Количество обязательно', + 'variations.*.stock.integer' => 'Количество должно быть целым числом', + 'variations.*.stock.min' => 'Количество не может быть отрицательным', + + 'variations.*.is_default.boolean' => 'Поле "Основная вариация" должно быть true или false', + + 'variations.*.attributes.array' => 'Некорректный формат атрибутов вариации', + + 'variations.*.attributes.flavor.string' => 'Вкус должен быть строкой', + 'variations.*.attributes.flavor.max' => 'Вкус не может превышать 100 символов', + + 'variations.*.attributes.color.string' => 'Цвет должен быть строкой', + 'variations.*.attributes.color.max' => 'Цвет не может превышать 100 символов', + + 'variations.*.attributes.size.string' => 'Размер должен быть строкой', + 'variations.*.attributes.size.max' => 'Размер не может превышать 50 символов', + + 'variations.*.attributes.weight.numeric' => 'Вес должен быть числом', + 'variations.*.attributes.weight.min' => 'Вес не может быть отрицательным', + + // Изображения + 'variations.*.images.array' => 'Некорректный формат изображений', + + 'variations.*.images.*.image' => 'Файл должен быть изображением', + 'variations.*.images.*.mimes' => 'Допустимые форматы изображений: jpg, jpeg, png, webp', + 'variations.*.images.*.max' => 'Размер изображения не должен превышать 2 МБ', + + 'variations.*.removed_images.array' => 'Некорректный формат списка удаляемых изображений', + 'variations.*.removed_images.*.string' => 'Путь к удаляемому изображению должен быть строкой', + + 'variations.*.existing_images.array' => 'Некорректный формат списка существующих изображений', + 'variations.*.existing_images.*.string' => 'Путь к существующему изображению должен быть строкой', + + // Атрибуты товара + 'attributes.array' => 'Некорректный формат характеристик', + 'attributes.*.key.string' => 'Название характеристики должно быть строкой', + 'attributes.*.value.string' => 'Значение характеристики должно быть строкой', + ]); + + DB::beginTransaction(); + + try { + // Обновляем slug если изменилось название + $newSlug = Str::slug($validated['name']); + $oldSlug = $product->slug; + + if ($oldSlug != $newSlug) { + $slug = $newSlug; + $counter = 1; + while (Product::where('slug', $slug)->where('id', '!=', $id)->exists()) { + $slug = $newSlug . '-' . $counter; + $counter++; + } + $product->slug = $slug; + $this->renameProductFolder($oldSlug, $slug, $product); + } + + // Обновляем товар + $product->update([ + 'name' => $validated['name'], + 'description' => $validated['description'], + 'brand_id' => $validated['brand_id'], + 'category_id' => $validated['category_id'], + 'is_active' => $request->boolean('is_active'), + 'meta_title' => $validated['meta_title'], + 'meta_description' => $validated['meta_description'], + 'meta_keywords' => $validated['meta_keywords'], + ]); + + // Обновляем атрибуты товара + ProductAttribute::where('product_id', $product->id)->delete(); + if (!empty($validated['attributes'])) { + foreach ($validated['attributes'] as $attr) { + if (!empty($attr['key']) && !empty($attr['value'])) { + ProductAttribute::create([ + 'product_id' => $product->id, + 'key' => $attr['key'], + 'value' => $attr['value'], + ]); + } + } + } + + $existingVariationIds = []; + + foreach ($validated['variations'] as $index => $variationData) { + if (isset($variationData['id']) && $variationData['id']) { + $variation = ProductVariation::find($variationData['id']); + + if ($variation && $variation->product_id == $product->id) { + $variation->update([ + 'name' => $variationData['name'], + 'sku' => $variationData['sku'], + 'price' => $variationData['price'], + 'old_price' => $variationData['old_price'] ?? null, + 'stock' => $variationData['stock'], + 'is_default' => $variationData['is_default'] ?? false, + ]); + + // Обновляем атрибуты вариации + VariationAttribute::where('variation_id', $variation->id)->delete(); + if (!empty($variationData['attributes'])) { + foreach ($variationData['attributes'] as $key => $value) { + if (!empty($value) || $value === 0 || $value === '0') { + VariationAttribute::create([ + 'variation_id' => $variation->id, + 'key' => $key, + 'value' => $value, + ]); + } + } + } + + // ========== ОБРАБОТКА ИЗОБРАЖЕНИЙ ========== + // 1. Удаляем отмеченные изображения + if (!empty($variationData['removed_images']) && is_array($variationData['removed_images'])) { + foreach ($variationData['removed_images'] as $imagePath) { + $image = VariationImage::where('variation_id', $variation->id) + ->where('path', $imagePath) + ->first(); + if ($image) { + $fullPath = public_path($imagePath); + if (file_exists($fullPath) && is_file($fullPath)) { + @unlink($fullPath); + } + $image->delete(); + } + } + } + + // 2. Обновляем порядок существующих изображений + if (!empty($variationData['existing_images']) && is_array($variationData['existing_images'])) { + foreach ($variationData['existing_images'] as $sortOrder => $imagePath) { + $image = VariationImage::where('variation_id', $variation->id) + ->where('path', $imagePath) + ->first(); + if ($image) { + $image->sort_order = $sortOrder; + $image->save(); + } + } + } + + // 3. Сохраняем новые изображения + if ($request->hasFile("variations.{$index}.images")) { + $files = $request->file("variations.{$index}.images"); + $validFiles = array_filter($files, function ($file) { + return $file !== null && $file->isValid(); + }); + + if (!empty($validFiles)) { + $this->saveVariationImages($validFiles, $product->slug, $variation->id); + } + } + + $existingVariationIds[] = $variation->id; + } + } else { + // Создаем новую вариацию + $variation = ProductVariation::create([ + 'product_id' => $product->id, + 'name' => $variationData['name'], + 'sku' => $variationData['sku'], + 'price' => $variationData['price'], + 'old_price' => $variationData['old_price'] ?? null, + 'stock' => $variationData['stock'], + 'is_default' => $variationData['is_default'] ?? false, + 'is_active' => true, + ]); + + // Сохраняем атрибуты + if (!empty($variationData['attributes'])) { + foreach ($variationData['attributes'] as $key => $value) { + if (!empty($value) || $value === 0 || $value === '0') { + VariationAttribute::create([ + 'variation_id' => $variation->id, + 'key' => $key, + 'value' => $value, + ]); + } + } + } + + // Сохраняем изображения + if ($request->hasFile("variations.{$index}.images")) { + $files = $request->file("variations.{$index}.images"); + $validFiles = array_filter($files, function ($file) { + return $file !== null && $file->isValid(); + }); + + if (!empty($validFiles)) { + $this->saveVariationImages($validFiles, $product->slug, $variation->id); + } + } + + $existingVariationIds[] = $variation->id; + } + } + + // Удаляем вариации, которых нет в форме + $variationsToDelete = ProductVariation::where('product_id', $product->id) + ->whereNotIn('id', $existingVariationIds) + ->get(); + + foreach ($variationsToDelete as $variation) { + // Удаляем изображения из БД и файловой системы + $images = VariationImage::where('variation_id', $variation->id)->get(); + foreach ($images as $image) { + $fullPath = public_path($image->path); + if (file_exists($fullPath) && is_file($fullPath)) { + @unlink($fullPath); + } + $image->delete(); + } + + // Удаляем атрибуты + VariationAttribute::where('variation_id', $variation->id)->delete(); + + // Удаляем папку + $folderPath = public_path('assets/images/products/' . $product->slug . '/variation_' . $variation->id); + if (file_exists($folderPath) && is_dir($folderPath)) { + $this->deleteDirectory($folderPath); + } + + $variation->delete(); + } + + DB::commit(); + + return redirect()->route('admin.products') + ->with('success', 'Товар "' . $product->name . '" успешно обновлен'); + } catch (\Exception $e) { + DB::rollBack(); + Log::error('Product update error: ' . $e->getMessage(), ['trace' => $e->getTraceAsString()]); + return redirect()->back() + ->withInput() + ->with('error', 'Ошибка при обновлении товара: ' . $e->getMessage()); + } + } + + private function saveVariationImages($images, $productSlug, $variationId) + { + $folder = public_path('assets/images/products/' . $productSlug . '/variation_' . $variationId); + + if (!file_exists($folder)) { + mkdir($folder, 0755, true); + } + + // Получаем текущий максимальный sort_order + $maxSortOrder = VariationImage::where('variation_id', $variationId)->max('sort_order'); + $nextIndex = ($maxSortOrder !== null) ? $maxSortOrder + 1 : 0; + + foreach ($images as $index => $image) { + if ($image && $image->isValid()) { + $extension = $image->getClientOriginalExtension(); + $filename = ($nextIndex + $index) . '.' . $extension; + $destinationPath = $folder . '/' . $filename; + + $image->move($folder, $filename); + + if (file_exists($destinationPath)) { + VariationImage::create([ + 'variation_id' => $variationId, + 'path' => 'assets/images/products/' . $productSlug . '/variation_' . $variationId . '/' . $filename, + 'sort_order' => $nextIndex + $index, + ]); + } + } + } + } + + private function renameProductFolder($oldSlug, $newSlug, $product) + { + $oldFolder = public_path('assets/images/products/' . $oldSlug); + $newFolder = public_path('assets/images/products/' . $newSlug); + + if (file_exists($oldFolder) && is_dir($oldFolder)) { + if (file_exists($newFolder)) { + $this->mergeDirectories($oldFolder, $newFolder); + $this->deleteDirectory($oldFolder); + } else { + rename($oldFolder, $newFolder); + } + + // Обновляем пути в БД + $variations = ProductVariation::where('product_id', $product->id)->get(); + foreach ($variations as $variation) { + $images = VariationImage::where('variation_id', $variation->id)->get(); + foreach ($images as $image) { + $image->path = str_replace('assets/images/products/' . $oldSlug, 'assets/images/products/' . $newSlug, $image->path); + $image->save(); + } + } + } + } + + private function mergeDirectories($source, $destination) + { + if (!is_dir($destination)) { + mkdir($destination, 0777, true); + } + + $files = scandir($source); + foreach ($files as $file) { + if ($file == '.' || $file == '..') continue; + + $sourcePath = $source . '/' . $file; + $destPath = $destination . '/' . $file; + + if (is_dir($sourcePath)) { + $this->mergeDirectories($sourcePath, $destPath); + rmdir($sourcePath); + } else { + copy($sourcePath, $destPath); + unlink($sourcePath); + } + } + } + + private function deleteDirectory($dir) + { + if (!file_exists($dir)) { + return true; + } + + if (!is_dir($dir)) { + return unlink($dir); + } + + foreach (scandir($dir) as $item) { + if ($item == '.' || $item == '..') { + continue; + } + + if (!$this->deleteDirectory($dir . DIRECTORY_SEPARATOR . $item)) { + return false; + } + } + + return rmdir($dir); + } + + public function checkSku(Request $request) + { + $request->validate([ + 'sku' => 'required|string', + 'variation_id' => 'nullable|exists:product_variations,id', + 'product_id' => 'nullable|exists:products,id' + ]); + + $sku = $request->sku; + $variationId = $request->variation_id; + $productId = $request->product_id; + + $isUnique = ProductVariation::isSkuUnique($sku, $variationId, $productId); + + return response()->json([ + 'unique' => $isUnique, + 'message' => $isUnique ? null : 'Артикул "' . $sku . '" уже используется' + ]); + } + + public function destroy($id) + { + $product = Product::findOrFail($id); + $productName = $product->name; + + foreach ($product->variations as $variation) { + $images = VariationImage::where('variation_id', $variation->id)->get(); + foreach ($images as $image) { + $fullPath = public_path($image->path); + if (file_exists($fullPath) && is_file($fullPath)) { + @unlink($fullPath); + } + } + $folderPath = public_path('assets/images/products/' . $product->slug . '/variation_' . $variation->id); + if (file_exists($folderPath) && is_dir($folderPath)) { + $this->deleteDirectory($folderPath); + } + } + + $productFolder = public_path('assets/images/products/' . $product->slug); + if (file_exists($productFolder) && is_dir($productFolder)) { + $this->deleteDirectory($productFolder); + } + + $product->delete(); + + return redirect()->route('admin.products') + ->with('success', 'Товар "' . $productName . '" успешно удален'); + } + + public function duplicate($id) + { + DB::beginTransaction(); + + try { + $original = Product::with(['variations.attributes', 'variations.images'])->findOrFail($id); + + $newProduct = $original->replicate(); + $newProduct->name = $original->name . ' (копия)'; + $newProduct->slug = Str::slug($newProduct->name) . '-' . uniqid(); + $newProduct->created_at = now(); + $newProduct->updated_at = now(); + $newProduct->save(); + + // Копируем атрибуты товара + foreach ($original->attributes as $attr) { + ProductAttribute::create([ + 'product_id' => $newProduct->id, + 'key' => $attr->key, + 'value' => $attr->value, + ]); + } + + // Копируем вариации + foreach ($original->variations as $variation) { + $newVariation = $variation->replicate(); + $newVariation->product_id = $newProduct->id; + $newVariation->sku = $variation->sku . '-copy-' . uniqid(); + $newVariation->created_at = now(); + $newVariation->updated_at = now(); + $newVariation->save(); + + // Копируем атрибуты вариации + foreach ($variation->attributes as $attr) { + VariationAttribute::create([ + 'variation_id' => $newVariation->id, + 'key' => $attr->key, + 'value' => $attr->value, + ]); + } + + // Копируем изображения + $newFolder = public_path('assets/images/products/' . $newProduct->slug . '/variation_' . $newVariation->id); + if (!file_exists($newFolder)) { + mkdir($newFolder, 0755, true); + } + + foreach ($variation->images as $image) { + $oldPath = public_path($image->path); + $filename = basename($image->path); + $newPath = $newFolder . '/' . $filename; + + if (file_exists($oldPath)) { + copy($oldPath, $newPath); + VariationImage::create([ + 'variation_id' => $newVariation->id, + 'path' => 'assets/images/products/' . $newProduct->slug . '/variation_' . $newVariation->id . '/' . $filename, + 'sort_order' => $image->sort_order, + ]); + } + } + } + + DB::commit(); + + return redirect()->route('admin.products') + ->with('success', 'Товар "' . $original->name . '" успешно скопирован. Новая версия: "' . $newProduct->name . '"'); + } catch (\Exception $e) { + DB::rollBack(); + Log::error('Product duplicate error: ' . $e->getMessage()); + return redirect()->back() + ->with('error', 'Ошибка при копировании товара: ' . $e->getMessage()); + } + } +} diff --git a/app/Http/Controllers/Admin/RolesController.php b/app/Http/Controllers/Admin/RolesController.php new file mode 100644 index 0000000..fe7cc4f --- /dev/null +++ b/app/Http/Controllers/Admin/RolesController.php @@ -0,0 +1,109 @@ +paginate(15); + + return view('admin.roles.roles', compact('roles')); + } + + public function create() + { + $permissions = Permission::all(); + + return view('admin.roles.create', compact('permissions')); + } + + public function store(Request $request) + { + $request->validate([ + 'name' => 'required|string|max:255', + 'slug' => 'required|string|max:255|unique:roles', + 'description' => 'nullable|string', + 'permissions' => 'nullable|array', + 'permissions.*' => 'exists:permissions,id' + ], [ + 'name.required' => 'Обязательно для заполнения', + 'name.max' => 'Не более 255 символов', + 'slug.required' => 'Обязательно для заполнения', + 'slug.max' => 'Не более 255 символов', + 'slug.unique' => 'Такой slug уже существует', + 'permissions.*.exists' => 'Право не найдено' + ]); + + $role = Role::create([ + 'name' => $request->name, + 'slug' => $request->slug, + 'description' => $request->description + ]); + + if ($request->has('permissions')) { + $role->permissions()->attach($request->permissions); + } + + return redirect()->route('admin.roles') + ->with('success', 'Роль "' . $role->name . '" успешно создана'); + } + + public function edit($id) + { + $role = Role::with('permissions')->findOrFail($id); + $permissions = Permission::all(); + + return view('admin.roles.edit', compact('role', 'permissions')); + } + + public function update(Request $request, $id) + { + $role = Role::findOrFail($id); + + $request->validate([ + 'name' => 'required|string|max:255', + 'slug' => 'required|string|max:255|unique:roles,slug,' . $id, + 'description' => 'nullable|string', + 'permissions' => 'nullable|array', + 'permissions.*' => 'exists:permissions,id' + ]); + + $role->update([ + 'name' => $request->name, + 'slug' => $request->slug, + 'description' => $request->description + ]); + + $role->permissions()->sync($request->permissions ?? []); + + return redirect()->route('admin.roles') + ->with('success', 'Роль "' . $role->name . '" успешно обновлена'); + } + + public function destroy($id) + { + $role = Role::findOrFail($id); + + $usersCount = User::where('role_id', $role->id)->count(); + + if ($usersCount > 0) { + return redirect()->route('admin.roles') + ->with('error', 'Нельзя удалить роль, у которой есть пользователи'); + } + + $roleName = $role->name; + $role->permissions()->detach(); + $role->delete(); + + return redirect()->route('admin.roles') + ->with('success', 'Роль "' . $roleName . '" успешно удалена'); + } +} diff --git a/app/Http/Controllers/Admin/UsersController.php b/app/Http/Controllers/Admin/UsersController.php new file mode 100644 index 0000000..1dee19d --- /dev/null +++ b/app/Http/Controllers/Admin/UsersController.php @@ -0,0 +1,133 @@ +filled('search')) { + $search = $request->search; + $query->where(function ($q) use ($search) { + $q->where('name', 'like', "%{$search}%") + ->orWhere('email', 'like', "%{$search}%"); + }); + } + + $users = $query->orderBy('created_at', 'desc')->paginate(15); + $users->appends($request->all()); + + return view('admin.users.users', compact('users')); + } + + public function create() + { + $roles = Role::all(); + return view('admin.users.create', compact('roles')); + } + + public function store(Request $request) + { + $request->validate([ + 'name' => 'required|string|max:255', + 'email' => 'required|email|max:255|unique:users', + 'phone' => 'nullable|string|max:20', + 'password' => 'required|string|min:8|confirmed', + 'role_id' => 'required|exists:roles,id' + ], [ + 'name.required' => 'Обязательно для заполнения', + 'name.max' => 'Не более 255 символов', + 'email.required' => 'Обязательно для заполнения', + 'email.email' => 'Введите корректный email', + 'email.max' => 'Не более 255 символов', + 'email.unique' => 'Этот email уже занят', + 'phone.max' => 'Не более 20 символов', + 'password.required' => 'Обязательно для заполнения', + 'password.min' => 'Минимум 8 символов', + 'password.confirmed' => 'Пароли не совпадают', + 'role_id.required' => 'Обязательно для заполнения' + ]); + + $user = User::create([ + 'name' => $request->name, + 'email' => $request->email, + 'phone' => $request->phone, + 'password' => Hash::make($request->password), + 'role_id' => $request->role_id + ]); + + return redirect()->route('admin.users') + ->with('success', 'Пользователь "' . $user->name . '" успешно создан'); + } + + public function edit($id) + { + $user = User::findOrFail($id); + $roles = Role::all(); + return view('admin.users.edit', compact('user', 'roles')); + } + + public function update(Request $request, $id) + { + $user = User::findOrFail($id); + + $request->validate([ + 'name' => 'required|string|max:255', + 'email' => 'required|email|max:255|unique:users,email,' . $id, + 'phone' => 'nullable|string|max:20', + 'password' => 'nullable|string|min:8|confirmed', + 'role_id' => 'required|exists:roles,id' + ], [ + 'name.required' => 'Обязательно для заполнения', + 'name.max' => 'Не более 255 символов', + 'email.required' => 'Обязательно для заполнения', + 'email.email' => 'Введите корректный email', + 'email.max' => 'Не более 255 символов', + 'email.unique' => 'Этот email уже занят', + 'phone.max' => 'Не более 20 символов', + 'password.min' => 'Минимум 8 символов', + 'password.confirmed' => 'Пароли не совпадают', + 'role_id.required' => 'Обязательно для заполнения' + ]); + + $data = [ + 'name' => $request->name, + 'email' => $request->email, + 'phone' => $request->phone, + 'role_id' => $request->role_id + ]; + + if ($request->filled('password')) { + $data['password'] = Hash::make($request->password); + } + + $user->update($data); + + return redirect()->route('admin.users') + ->with('success', 'Пользователь "' . $user->name . '" успешно обновлен'); + } + + public function destroy($id) + { + $user = User::findOrFail($id); + + if ($user->id === auth()->id()) { + return redirect()->route('admin.users') + ->with('error', 'Нельзя удалить самого себя'); + } + + $userName = $user->name; + $user->delete(); + + return redirect()->route('admin.users') + ->with('success', 'Пользователь "' . $userName . '" успешно удален'); + } +} diff --git a/app/Http/Controllers/Auth/LoginController.php b/app/Http/Controllers/Auth/LoginController.php new file mode 100644 index 0000000..62ffd2a --- /dev/null +++ b/app/Http/Controllers/Auth/LoginController.php @@ -0,0 +1,50 @@ +validate([ + 'email' => 'required|email', + 'password' => 'required|string', + ], [ + 'email.required' => 'Введите email', + 'email.email' => 'Введите корректный email', + 'password.required' => 'Введите пароль', + ]); + + $remember = $request->has('save_checkbox'); + + if (Auth::attempt($credentials, $remember)) { + $request->session()->regenerate(); + + return redirect()->intended('/'); + } + + return back()->withErrors([ + 'email' => 'Неверный email или пароль', + ])->onlyInput('email'); + } + + public function logout(Request $request) + { + Auth::logout(); + + $request->session()->invalidate(); + $request->session()->regenerateToken(); + + return redirect('/'); + } +} diff --git a/app/Http/Controllers/Auth/RegisterController.php b/app/Http/Controllers/Auth/RegisterController.php new file mode 100644 index 0000000..24ebf55 --- /dev/null +++ b/app/Http/Controllers/Auth/RegisterController.php @@ -0,0 +1,63 @@ +all(), [ + 'name' => 'required|string|max:255', + 'email' => 'required|string|email|max:255|unique:users', + 'phone' => 'required|string|max:20', + 'password' => 'required|string|min:8|confirmed', + 'save_checkbox' => 'accepted' + ], [ + 'name.required' => 'Обязательно введите Ваше имя', + 'email.required' => 'Обязательно введите адрес электронной почты', + 'phone.required' => 'Обязательно введите Ваш номер телефона', + 'email.email' => 'Введите корректный адрес электронной почты', + 'email.unique' => 'Пользователь с таким адресом электронной почты уже существует', + 'password.required' => 'Поле пароль обязательно для заполнения', + 'password.min' => 'Пароль должен содержать минимум 8 символов', + 'password.confirmed' => 'Пароли не совпадают', + 'save_checkbox.accepted' => 'Необходимо согласие на обработку персональных данных' + ]); + + if ($validator->fails()) { + return redirect()->back() + ->withErrors($validator) + ->withInput(); + } + + $user = User::create([ + 'name' => $request->name, + 'email' => $request->email, + 'phone' => $request->phone, + 'password' => Hash::make($request->password), + ]); + + $defaultRole = Role::where('slug', 'registered_user')->first(); + if ($defaultRole) { + $user->role()->attach($defaultRole->id); + } + + auth()->login($user); + + return redirect()->intended('/') + ->with('success', 'Регистрация прошла успешно!'); + } +} diff --git a/app/Http/Controllers/BrandController.php b/app/Http/Controllers/BrandController.php new file mode 100644 index 0000000..113683a --- /dev/null +++ b/app/Http/Controllers/BrandController.php @@ -0,0 +1,113 @@ +withCount('products') + ->orderBy('name') + ->paginate(20); + + return view('brands.brands', compact('brands')); + } + + /** + * Display products by brand with filters. + */ + public function show(Request $request, $slug) + { + $brand = Brand::where('slug', $slug) + ->active() + ->firstOrFail(); + + // Получаем ID категорий, в которых есть товары этого бренда + $categoryIds = $brand->products() + ->active() + ->pluck('category_id') + ->unique() + ->filter() + ->values() + ->toArray(); + + // Загружаем категории с родителями для построения полного пути + $categories = Category::with(['parent', 'parent.parent']) + ->whereIn('id', $categoryIds) + ->active() + ->get(); + + // Формируем полный путь для каждой категории + $categoriesWithPath = $categories->map(function ($category) { + $path = []; + $current = $category; + + while ($current) { + $path[] = $current->name; + $current = $current->parent; + } + + $category->full_path = implode(' / ', array_reverse($path)); + return $category; + })->sortBy('full_path'); + + // Диапазон цен + $priceRange = DB::table('products') + ->join('product_variations', 'products.id', '=', 'product_variations.product_id') + ->where('products.brand_id', $brand->id) + ->where('products.is_active', true) + ->selectRaw('MIN(product_variations.price) as min_price, MAX(product_variations.price) as max_price') + ->first(); + + $minPrice = $priceRange->min_price ?? 0; + $maxPrice = $priceRange->max_price ?? 10000; + + // Запрос на продукты с фильтрацией + $productsQuery = Product::with(['variations', 'category']) + ->where('brand_id', $brand->id) + ->active(); + + // Фильтрация по категориям + if ($request->has('categories') && !empty($request->categories)) { + $productsQuery->whereIn('category_id', $request->categories); + } + + // Фильтрация по цене + if ($request->has('price-min') && $request->has('price-max')) { + $min = (float) $request->get('price-min'); + $max = (float) $request->get('price-max'); + + $productsQuery->whereHas('variations', function ($q) use ($min, $max) { + $q->whereBetween('price', [$min, $max]); + }); + } + + // Сортировка + $sort = $request->get('sort', 'created_at'); + $order = $request->get('order', 'desc'); + + if ($sort === 'price') { + $productsQuery->withMin('variations', 'price') + ->orderBy('variations_min_price', $order); + } elseif ($sort === 'name') { + $productsQuery->orderBy('name', $order); + } else { + $productsQuery->orderBy($sort, $order); + } + + $products = $productsQuery->paginate(20); + $products->appends($request->all()); + + return view('brands.show', compact('brand', 'products', 'categoriesWithPath', 'minPrice', 'maxPrice')); + } +} diff --git a/app/Http/Controllers/CartController.php b/app/Http/Controllers/CartController.php new file mode 100644 index 0000000..cba7487 --- /dev/null +++ b/app/Http/Controllers/CartController.php @@ -0,0 +1,179 @@ +get(); + $total = $cartItems->sum('total'); + + return view('cart.show', compact('cartItems', 'total')); + } + + public function add(Request $request) + { + $request->validate([ + 'variation_id' => 'required|exists:product_variations,id', + 'quantity' => 'required|integer|min:1' + ]); + + $variation = ProductVariation::with('product')->find($request->variation_id); + + if (!$variation) { + if ($request->ajax()) { + return response()->json(['success' => false, 'message' => 'Товар не найден'], 404); + } + return back()->with('error', 'Товар не найден'); + } + + if ($variation->stock < $request->quantity) { + if ($request->ajax()) { + return response()->json(['success' => false, 'message' => 'Недостаточно товара на складе'], 422); + } + return back()->with('error', 'Недостаточно товара на складе'); + } + + $cartItem = CartItem::findByVariation($request->variation_id); + + if ($cartItem) { + $newQuantity = $cartItem->quantity + $request->quantity; + if ($variation->stock < $newQuantity) { + if ($request->ajax()) { + return response()->json(['success' => false, 'message' => 'Недостаточно товара на складе'], 422); + } + return back()->with('error', 'Недостаточно товара на складе'); + } + $cartItem->update(['quantity' => $newQuantity]); + } else { + CartItem::create([ + 'user_id' => Auth::id(), + 'session_id' => session()->getId(), + 'variation_id' => $request->variation_id, + 'quantity' => $request->quantity, + 'price' => $variation->price + ]); + } + + $cartCount = CartItem::getCount(); + + if ($request->ajax()) { + return response()->json([ + 'success' => true, + 'message' => 'Товар добавлен в корзину', + 'cart_count' => $cartCount + ]); + } + + return back()->with('success', 'Товар добавлен в корзину'); + } + + + public function update(Request $request, $id) + { + $request->validate(['quantity' => 'required|integer|min:1']); + + $cartItem = CartItem::find($id); + + if (!$cartItem) { + return response()->json(['error' => 'Товар не найден'], 404); + } + + $variation = $cartItem->variation; + + if ($variation->stock < $request->quantity) { + return response()->json(['error' => 'Недостаточно товара на складе'], 422); + } + + $cartItem->update(['quantity' => $request->quantity]); + + $cartItems = CartItem::getCartQuery()->get(); + $cartTotal = $cartItems->sum('total'); + $totalItems = $cartItems->sum('quantity'); + + return response()->json([ + 'success' => true, + 'total' => $cartItem->total, + 'cart_total' => $cartTotal, + 'total_items' => $totalItems, + 'max_stock' => $variation->stock + ]); + } + + public function remove($id) + { + $cartItem = CartItem::findCartItem($id); + + if ($cartItem) { + $cartItem->delete(); + return redirect()->route('cart.index')->with('success', 'Товар удален из корзины'); + } + + return redirect()->route('cart.index')->with('error', 'Товар не найден'); + } + + public function clear() + { + CartItem::clear(); + return redirect()->route('cart.index')->with('success', 'Корзина очищена'); + } + + public function checkout() + { + $cartItems = CartItem::getCartQuery()->get(); + + if ($cartItems->isEmpty()) { + return redirect()->route('cart.index')->with('error', 'Корзина пуста'); + } + + $total = $cartItems->sum('total'); + + return view('cart.checkout', compact('cartItems', 'total')); + } + + public function processOrder(Request $request) + { + $request->validate([ + 'customer_name' => 'required|string|max:255', + 'customer_email' => 'required|email|max:255', + 'customer_phone' => 'required|string|max:20', + 'shipping_address' => 'required_if:delivery_method,courier,express|max:1000', + 'delivery_method' => 'required|in:courier,pickup,express', + 'payment_method' => 'required|in:cash,card,online' + ], [ + 'customer_name.required' => 'Поле обязательно для заполнения', + 'customer_email.required' => 'Поле обязательно для заполнения', + 'customer_email.email' => 'Введите корректный адрес электронной почты', + 'customer_phone.required' => 'Поле обязательно для заполнения', + 'shipping_address.required_if' => 'Адрес доставки обязателен для выбранного способа доставки', + 'delivery_method.required' => 'Выберите способ доставки', + 'payment_method.required' => 'Выберите способ оплаты' + ]); + + + $cartItems = CartItem::getCartQuery()->get(); + + if ($cartItems->isEmpty()) { + return back()->with('error', 'Корзина пуста'); + } + + $order = Order::createFromCart($request->all(), $cartItems); + + if ($order['success']) { + return redirect()->route('orders.show', $order['order']) + ->with('success', 'Заказ оформлен! Номер заказа: ' . $order['order']->order_number); + } + + return back()->with('error', $order['message']); + } +} diff --git a/app/Http/Controllers/CategoryController.php b/app/Http/Controllers/CategoryController.php new file mode 100644 index 0000000..fe93c77 --- /dev/null +++ b/app/Http/Controllers/CategoryController.php @@ -0,0 +1,67 @@ +getAllCategoryIds(); + + $brands = Brand::whereHas('products', function ($query) use ($categoryIds) { + $query->whereIn('category_id', $categoryIds) + ->active(); + }) + ->active() + ->orderBy('name') + ->get(); + + $priceRange = DB::table('products') + ->join('product_variations', 'products.id', '=', 'product_variations.product_id') + ->whereIn('products.category_id', $categoryIds) + ->where('products.is_active', true) + ->where('product_variations.is_active', true) + ->selectRaw('MIN(product_variations.price) as min_price, MAX(product_variations.price) as max_price') + ->first(); + + $minPrice = $priceRange->min_price ?? 0; + $maxPrice = $priceRange->max_price ?? 10000; + + $productsQuery = $category->productsWithChildren() + ->with(['variations', 'brand']) + ->active(); + + if ($request->has('brands') && !empty($request->brands)) { + $productsQuery->whereIn('brand_id', $request->brands); + } + + if ($request->has('price-min') && $request->has('price-max')) { + $min = (float) $request->get('price-min'); + $max = (float) $request->get('price-max'); + + $productsQuery->whereHas('variations', function ($q) use ($min, $max) { + $q->whereBetween('price', [$min, $max]); + }); + } + + $products = $productsQuery->orderBy('created_at', 'desc')->paginate(20); + + $products->appends($request->all()); + + $children = $category->children()->active()->get(); + + return view('categories.show', compact('category', 'products', 'children', 'brands', 'minPrice', 'maxPrice')); + } +} diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php new file mode 100644 index 0000000..77ec359 --- /dev/null +++ b/app/Http/Controllers/Controller.php @@ -0,0 +1,12 @@ +discounted() + ->inStock() + ->active() + ->orderByRaw('(old_price - price) / old_price * 100 DESC') + ->limit(8) + ->get(); + + $categories = Category::active()->root()->orderBy('sort_order')->get(); + + $dryCatFood = Product::getForCarousel(21, 12); + + $dryDogFood = Product::getForCarousel(3, 12); + + return view('index', compact('discountedProducts', 'categories', 'dryCatFood', 'dryDogFood')); + } + + public function about() + { + + $topBrands = Brand::active()->popular()->limit(5)->get(); + + return view('about', compact('topBrands')); + } + + public function contacts() + { + return view('contacts'); + } + + public function privacyPolicy() + { + return view('privacy-policy'); + } +} diff --git a/app/Http/Controllers/MenuController.php b/app/Http/Controllers/MenuController.php new file mode 100644 index 0000000..14b1f90 --- /dev/null +++ b/app/Http/Controllers/MenuController.php @@ -0,0 +1,90 @@ +active() + ->orderBy('sort_order') + ->get(); + + $contacts = Contact::getData(); + + $popularBrands = Brand::active()->popular()->limit(8)->get(); + + return [ + 'categories' => $categories, + 'contacts' => $contacts, + 'popularBrands' => $popularBrands, + 'footerCategories' => $footerCategories, + ]; + } + + public function search(Request $request) + { + $query = trim($request->get('q', '')); + + // Проверка на минимальную длину + if (mb_strlen($query) < 3) { + return redirect()->back()->with('error', 'Для поиска введите минимум 3 символа'); + } + + // Поиск товаров + $products = Product::with(['variations', 'brand', 'category']) + ->where('name', 'like', "%{$query}%") + ->orWhere('description', 'like', "%{$query}%") + ->orWhere('meta_keywords', 'like', "%{$query}%") + ->active() + ->paginate(20); + + // Поиск брендов + $brands = Brand::where('name', 'like', "%{$query}%") + ->orWhere('description', 'like', "%{$query}%") + ->active() + ->limit(10) + ->get(); + + return view('search.results', compact('products', 'brands', 'query')); + } + + /** + * AJAX поиск для подсказок + */ + public function searchAjax(Request $request) + { + $query = trim($request->get('q', '')); + + if (mb_strlen($query) < 2) { + return response()->json([]); + } + + // Поиск товаров + $products = Product::with('variations') + ->where('name', 'like', "%{$query}%") + ->active() + ->limit(5) + ->get(['id', 'name', 'slug']); + + // Поиск брендов + $brands = Brand::where('name', 'like', "%{$query}%") + ->active() + ->limit(3) + ->get(['id', 'name', 'slug']); + + return response()->json([ + 'products' => $products, + 'brands' => $brands + ]); + } +} diff --git a/app/Http/Controllers/OrderController.php b/app/Http/Controllers/OrderController.php new file mode 100644 index 0000000..17c01bb --- /dev/null +++ b/app/Http/Controllers/OrderController.php @@ -0,0 +1,35 @@ +orderBy('created_at', 'desc') + ->paginate(20); + + return view('orders.orders', compact('orders')); + } + + /** + * Display order details. + */ + public function show(Order $order) + { + $user = Auth::user(); + if ($order->user_id !== Auth::id()) { + abort(403); + } + + return view('profile.order-show', compact('order', 'user')); + } +} diff --git a/app/Http/Controllers/ProductsController.php b/app/Http/Controllers/ProductsController.php new file mode 100644 index 0000000..9f97740 --- /dev/null +++ b/app/Http/Controllers/ProductsController.php @@ -0,0 +1,39 @@ +where('slug', $slug) + ->where('is_active', true) + ->firstOrFail(); + + $defaultVariation = $product->default_variation; + + return view('products.show', compact('product', 'defaultVariation')); + } + + public function getVariationImages($variationId) + { + $images = ProductVariation::getImagesDataById($variationId); + + return response()->json([ + 'success' => true, + 'images' => $images + ]); + } +} diff --git a/app/Http/Controllers/ProfileController.php b/app/Http/Controllers/ProfileController.php new file mode 100644 index 0000000..f4ccb7a --- /dev/null +++ b/app/Http/Controllers/ProfileController.php @@ -0,0 +1,97 @@ +id) + ->whereNotIn('delivery_status', ['delivered', 'cancelled']) + ->orderBy('created_at', 'desc') + ->get(); + + $archiveOrders = Order::where('user_id', $user->id) + ->whereIn('delivery_status', ['delivered', 'cancelled']) + ->orderBy('created_at', 'desc') + ->get(); + + return view('profile.profile', compact('user', 'activeOrders', 'archiveOrders')); + } + + /** + * Редактирование профиля + */ + public function edit() + { + $user = Auth::user(); + return view('profile.edit', compact('user')); + } + + /** + * Обновление профиля + */ + public function update(Request $request) + { + $user = Auth::user(); + + $request->validate([ + 'name' => 'required|string|max:255', + 'email' => [ + 'required', + 'email', + 'max:255', + Rule::unique('users')->ignore($user->id), + ], + 'phone' => 'nullable|string|max:20', + 'current_password' => 'nullable|required_with:password|current_password', + 'password' => 'nullable|string|min:8|confirmed', + ], [ + 'name.required' => 'Обязательно для заполнения', + 'email.required' => 'Обязательно для заполнения', + 'email.email' => 'Введите корректный email', + 'email.unique' => 'Этот email уже занят', + 'current_password.required_with' => 'Введите текущий пароль', + 'current_password.current_password' => 'Неверный текущий пароль', + 'password.min' => 'Пароль должен содержать минимум 8 символов', + 'password.confirmed' => 'Пароли не совпадают', + ]); + + $user->name = $request->name; + $user->email = $request->email; + $user->phone = $request->phone; + + if ($request->filled('password')) { + $user->password = Hash::make($request->password); + } + + $user->save(); + + return redirect()->route('profile.index') + ->with('success', 'Профиль успешно обновлен'); + } + + /** + * Детали заказа + */ + public function orderShow($id) + { + $order = Order::where('user_id', Auth::id()) + ->with('items.variation.product') + ->findOrFail($id); + $user = Auth::user(); + + return view('profile.order-show', compact('order', 'user')); + } +} diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php new file mode 100644 index 0000000..74b91eb --- /dev/null +++ b/app/Http/Kernel.php @@ -0,0 +1,69 @@ + + */ + protected $middleware = [ + // \App\Http\Middleware\TrustHosts::class, + \App\Http\Middleware\TrustProxies::class, + \Illuminate\Http\Middleware\HandleCors::class, + \App\Http\Middleware\PreventRequestsDuringMaintenance::class, + \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class, + \App\Http\Middleware\TrimStrings::class, + \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class, + ]; + + /** + * The application's route middleware groups. + * + * @var array> + */ + protected $middlewareGroups = [ + 'web' => [ + \App\Http\Middleware\EncryptCookies::class, + \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, + \Illuminate\Session\Middleware\StartSession::class, + \Illuminate\View\Middleware\ShareErrorsFromSession::class, + \App\Http\Middleware\VerifyCsrfToken::class, + \Illuminate\Routing\Middleware\SubstituteBindings::class, + ], + + 'api' => [ + // \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class, + \Illuminate\Routing\Middleware\ThrottleRequests::class.':api', + \Illuminate\Routing\Middleware\SubstituteBindings::class, + ], + ]; + + /** + * The application's middleware aliases. + * + * Aliases may be used instead of class names to conveniently assign middleware to routes and groups. + * + * @var array + */ + protected $middlewareAliases = [ + 'auth' => \App\Http\Middleware\Authenticate::class, + 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, + 'auth.session' => \Illuminate\Session\Middleware\AuthenticateSession::class, + 'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class, + 'can' => \Illuminate\Auth\Middleware\Authorize::class, + 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class, + 'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class, + 'precognitive' => \Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests::class, + 'signed' => \App\Http\Middleware\ValidateSignature::class, + 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, + 'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class, + 'permission' => \App\Http\Middleware\CheckPermission::class, + ]; +} diff --git a/app/Http/Middleware/Authenticate.php b/app/Http/Middleware/Authenticate.php new file mode 100644 index 0000000..d4ef644 --- /dev/null +++ b/app/Http/Middleware/Authenticate.php @@ -0,0 +1,17 @@ +expectsJson() ? null : route('login'); + } +} diff --git a/app/Http/Middleware/CheckPermission.php b/app/Http/Middleware/CheckPermission.php new file mode 100644 index 0000000..08f29b7 --- /dev/null +++ b/app/Http/Middleware/CheckPermission.php @@ -0,0 +1,32 @@ +hasPermission($permission)) { + abort(403, 'У вас нет прав для доступа к этой странице'); + } + + return $next($request); + } +} diff --git a/app/Http/Middleware/CheckRole.php b/app/Http/Middleware/CheckRole.php new file mode 100644 index 0000000..ab71f03 --- /dev/null +++ b/app/Http/Middleware/CheckRole.php @@ -0,0 +1,20 @@ + + */ + protected $except = [ + // + ]; +} diff --git a/app/Http/Middleware/PreventRequestsDuringMaintenance.php b/app/Http/Middleware/PreventRequestsDuringMaintenance.php new file mode 100644 index 0000000..74cbd9a --- /dev/null +++ b/app/Http/Middleware/PreventRequestsDuringMaintenance.php @@ -0,0 +1,17 @@ + + */ + protected $except = [ + // + ]; +} diff --git a/app/Http/Middleware/RedirectIfAuthenticated.php b/app/Http/Middleware/RedirectIfAuthenticated.php new file mode 100644 index 0000000..afc78c4 --- /dev/null +++ b/app/Http/Middleware/RedirectIfAuthenticated.php @@ -0,0 +1,30 @@ +check()) { + return redirect(RouteServiceProvider::HOME); + } + } + + return $next($request); + } +} diff --git a/app/Http/Middleware/TrimStrings.php b/app/Http/Middleware/TrimStrings.php new file mode 100644 index 0000000..88cadca --- /dev/null +++ b/app/Http/Middleware/TrimStrings.php @@ -0,0 +1,19 @@ + + */ + protected $except = [ + 'current_password', + 'password', + 'password_confirmation', + ]; +} diff --git a/app/Http/Middleware/TrustHosts.php b/app/Http/Middleware/TrustHosts.php new file mode 100644 index 0000000..c9c58bd --- /dev/null +++ b/app/Http/Middleware/TrustHosts.php @@ -0,0 +1,20 @@ + + */ + public function hosts(): array + { + return [ + $this->allSubdomainsOfApplicationUrl(), + ]; + } +} diff --git a/app/Http/Middleware/TrustProxies.php b/app/Http/Middleware/TrustProxies.php new file mode 100644 index 0000000..3391630 --- /dev/null +++ b/app/Http/Middleware/TrustProxies.php @@ -0,0 +1,28 @@ +|string|null + */ + protected $proxies; + + /** + * The headers that should be used to detect proxies. + * + * @var int + */ + protected $headers = + Request::HEADER_X_FORWARDED_FOR | + Request::HEADER_X_FORWARDED_HOST | + Request::HEADER_X_FORWARDED_PORT | + Request::HEADER_X_FORWARDED_PROTO | + Request::HEADER_X_FORWARDED_AWS_ELB; +} diff --git a/app/Http/Middleware/ValidateSignature.php b/app/Http/Middleware/ValidateSignature.php new file mode 100644 index 0000000..093bf64 --- /dev/null +++ b/app/Http/Middleware/ValidateSignature.php @@ -0,0 +1,22 @@ + + */ + protected $except = [ + // 'fbclid', + // 'utm_campaign', + // 'utm_content', + // 'utm_medium', + // 'utm_source', + // 'utm_term', + ]; +} diff --git a/app/Http/Middleware/VerifyCsrfToken.php b/app/Http/Middleware/VerifyCsrfToken.php new file mode 100644 index 0000000..9e86521 --- /dev/null +++ b/app/Http/Middleware/VerifyCsrfToken.php @@ -0,0 +1,17 @@ + + */ + protected $except = [ + // + ]; +} diff --git a/app/Models/Brand.php b/app/Models/Brand.php new file mode 100644 index 0000000..50cf957 --- /dev/null +++ b/app/Models/Brand.php @@ -0,0 +1,62 @@ + 'boolean', + ]; + + public function products(): HasMany + { + return $this->hasMany(Product::class); + } + + public function activeProducts(): HasMany + { + return $this->hasMany(Product::class)->where('is_active', true); + } + + public function getProductsCountAttribute(): int + { + return $this->activeProducts()->count(); + } + + public function scopeActive($query) + { + return $query->where('is_active', true); + } + + public function scopePopular($query) + { + return $query->withCount('products')->orderBy('products_count', 'desc'); + } + + public function getLogoUrlAttribute(): string + { + if ($this->logo) { + $logoPath = 'assets/images/brands/' . $this->logo; + if (file_exists(public_path($logoPath))) { + return asset($logoPath); + } + } + + return asset('assets/images/brands/default.svg'); + } +} diff --git a/app/Models/CartItem.php b/app/Models/CartItem.php new file mode 100644 index 0000000..58a6e29 --- /dev/null +++ b/app/Models/CartItem.php @@ -0,0 +1,97 @@ + 'integer', + 'price' => 'decimal:2' + ]; + + public function variation(): BelongsTo + { + return $this->belongsTo(ProductVariation::class); + } + + public function user(): BelongsTo + { + return $this->belongsTo(User::class); + } + + public function getTotalAttribute(): float + { + return $this->quantity * $this->price; + } + + /** + * Получить корзину для текущего пользователя/сессии + */ + public static function getCartQuery() + { + return self::with('variation.product') + ->where(function ($query) { + if (Auth::check()) { + $query->where('user_id', Auth::id()); + } else { + $query->where('session_id', session()->getId()); + } + }); + } + + /** + * Получить количество товаров в корзине + */ + public static function getCount() + { + return self::getCartQuery()->sum('quantity'); + } + + /** + * Получить общую сумму корзины + */ + public static function getTotal() + { + return self::getCartQuery()->get()->sum('total'); + } + + /** + * Получить товар в корзине по вариации + */ + public static function findByVariation($variationId) + { + return self::getCartQuery() + ->where('variation_id', $variationId) + ->first(); + } + + /** + * Получить товар в корзине по ID + */ + public static function findCartItem($id) + { + return self::getCartQuery() + ->where('id', $id) + ->first(); + } + + /** + * Очистить корзину + */ + public static function clear() + { + return self::getCartQuery()->delete(); + } +} diff --git a/app/Models/Category.php b/app/Models/Category.php new file mode 100644 index 0000000..61c16ad --- /dev/null +++ b/app/Models/Category.php @@ -0,0 +1,243 @@ + 'boolean', + ]; + + public function parent(): BelongsTo + { + return $this->belongsTo(Category::class, 'parent_id'); + } + + public function children(): HasMany + { + return $this->hasMany(Category::class, 'parent_id')->orderBy('sort_order'); + } + + public function descendants() + { + return $this->children()->with('descendants'); + } + + public function products(): HasMany + { + return $this->hasMany(Product::class, 'category_id'); + } + + public function getFullSlugAttribute(): string + { + $slugs = []; + $current = $this; + + while ($current) { + $slugs[] = $current->slug; + $current = $current->parent; + } + + return implode('/', array_reverse($slugs)); + } + + public function getUrlAttribute(): string + { + return route('category.show', $this->full_slug); + } + + public function getBreadcrumbsAttribute() + { + $breadcrumbs = []; + $current = $this; + + while ($current) { + array_unshift($breadcrumbs, [ + 'name' => $current->name, + 'slug' => $current->slug, + 'url' => $current->url + ]); + $current = $current->parent; + } + + return $breadcrumbs; + } + + public function getPathAttribute() + { + $path = []; + $current = $this; + + while ($current) { + array_unshift($path, $current->slug); + $current = $current->parent; + } + + return implode('/', $path); + } + + public function scopeActive($query) + { + return $query->where('is_active', true); + } + + public function scopeRoot($query) + { + return $query->whereNull('parent_id'); + } + + public static function getTreeMenu() + { + return self::with('descendants')->root()->active()->orderBy('sort_order')->get(); + } + + public static function findByPath($path) + { + $slugs = explode('/', trim($path, '/')); + $category = null; + + foreach ($slugs as $slug) { + if (!$category) { + $category = self::where('slug', $slug) + ->whereNull('parent_id') + ->active() + ->first(); + } else { + $category = $category->children() + ->where('slug', $slug) + ->active() + ->first(); + } + + if (!$category) { + return null; + } + } + + return $category; + } + + public static function getSelectTree($maxLevel = 1) + { + $categories = self::with('parent')->get(); + + return $categories->map(function ($cat) { + $cat->level = $cat->calculateLevel(); + return $cat; + })->filter(function ($cat) use ($maxLevel) { + return $cat->level < $maxLevel; + }); + } + + public static function getFlatTree() + { + $categories = self::with('parent') + ->orderBy('parent_id') + ->orderBy('sort_order') + ->get(); + + return self::buildFlatTree($categories); + } + + private static function buildFlatTree($categories, $parentId = null, $level = 0) + { + $result = collect(); + + foreach ($categories as $category) { + if ($category->parent_id == $parentId) { + $category->level = $level; + $result->push($category); + + $children = self::buildFlatTree($categories, $category->id, $level + 1); + $result = $result->concat($children); + } + } + + return $result; + } + + public function getLevelAttribute() + { + return $this->calculateLevel(); + } + + public function calculateLevel($level = 0) + { + if ($this->parent) { + return $this->parent->calculateLevel($level + 1); + } + return $level; + } + + public function isDescendantOf($ancestorId): bool + { + $current = $this; + while ($current) { + if ($current->id == $ancestorId) { + return true; + } + $current = $current->parent; + } + return false; + } + + public function getIconUrlAttribute(): string + { + if ($this->icon !== NULL) { + $iconPath = 'assets/images/categories/icons/' . $this->icon; + if (file_exists(public_path($iconPath))) { + return asset($iconPath); + } + } + + return asset('assets/images/categories/icons/default.svg'); + } + + public function getImageUrlAttribute(): string + { + if ($this->image !== NULL) { + $imagePath = 'assets/images/categories/images/' . $this->image; + if (file_exists(public_path($imagePath))) { + return asset($imagePath); + } + } + + return asset('assets/images/categories/images/default.svg'); + } + + public function getAllCategoryIds(): array + { + $ids = [$this->id]; + + foreach ($this->children as $child) { + $ids = array_merge($ids, $child->getAllCategoryIds()); + } + + return $ids; + } + + // В модели Category + public function productsWithChildren() + { + $categoryIds = $this->getAllCategoryIds(); + + return Product::whereIn('category_id', $categoryIds); + } +} diff --git a/app/Models/Contact.php b/app/Models/Contact.php new file mode 100644 index 0000000..d717a3f --- /dev/null +++ b/app/Models/Contact.php @@ -0,0 +1,31 @@ + 'decimal:2', + 'shipping_cost' => 'decimal:2', + 'discount' => 'decimal:2', + 'total' => 'decimal:2' + ]; + + const DELIVERY_STATUSES = [ + 'pending' => 'Ожидает обработки', + 'processing' => 'В обработке', + 'shipped' => 'Отправлен', + 'delivered' => 'Доставлен', + 'cancelled' => 'Отменён' + ]; + + const PAYMENT_STATUSES = [ + 'pending' => 'Ожидает оплаты', + 'paid' => 'Оплачен', + 'failed' => 'Ошибка оплаты' + ]; + + public function user(): BelongsTo + { + return $this->belongsTo(User::class); + } + + public function items(): HasMany + { + return $this->hasMany(OrderItem::class); + } + + public static function generateOrderNumber(): string + { + return 'ORD-' . date('Ymd') . '-' . strtoupper(uniqid()); + } + + /** + * Создать заказ из корзины + */ + public static function createFromCart($data, $cartItems) + { + DB::beginTransaction(); + + try { + $subtotal = $cartItems->sum('total'); + $shippingCost = self::calculateShipping($data['delivery_method']); + $total = $subtotal + $shippingCost; + + $shippingAddress = null; + if (in_array($data['delivery_method'], ['courier', 'express'])) { + $shippingAddress = $data['shipping_address']; + } + + $order = self::create([ + 'order_number' => self::generateOrderNumber(), + 'user_id' => auth()->id(), + 'customer_name' => $data['customer_name'], + 'customer_email' => $data['customer_email'], + 'customer_phone' => $data['customer_phone'], + 'shipping_address' => $shippingAddress, + 'subtotal' => $subtotal, + 'shipping_cost' => $shippingCost, + 'total' => $total, + 'payment_method' => $data['payment_method'], + 'delivery_method' => $data['delivery_method'], + 'comment' => $data['comment'] ?? null + ]); + + foreach ($cartItems as $item) { + OrderItem::create([ + 'order_id' => $order->id, + 'variation_id' => $item->variation_id, + 'product_name' => $item->variation->product->name, + 'variation_name' => $item->variation->name, + 'sku' => $item->variation->sku, + 'quantity' => $item->quantity, + 'price' => $item->price, + 'total' => $item->total + ]); + + $item->variation->decrement('stock', $item->quantity); + } + + CartItem::clear(); + + DB::commit(); + + return ['success' => true, 'order' => $order]; + } catch (\Exception $e) { + DB::rollBack(); + return ['success' => false, 'message' => $e->getMessage()]; + } + } + + /** + * Рассчитать стоимость доставки + */ + private static function calculateShipping($method) + { + return match ($method) { + 'express' => 300, + 'courier' => 0, + 'pickup' => 0, + default => 0, + }; + } + + public function getDeliveryStatusNameAttribute(): string + { + return self::DELIVERY_STATUSES[$this->delivery_status] ?? $this->delivery_status; + } + + public function getPaymentStatusNameAttribute(): string + { + return self::PAYMENT_STATUSES[$this->payment_status] ?? $this->payment_status; + } + + public function getDeliveryStatusColorAttribute(): string + { + return match ($this->delivery_status) { + 'pending' => 'warning', + 'processing' => 'info', + 'shipped' => 'primary', + 'delivered' => 'success', + 'cancelled' => 'danger', + default => 'secondary' + }; + } + + public function getDeliveryStatusIconAttribute(): string + { + return match ($this->delivery_status) { + 'pending' => 'bi-clock', + 'processing' => 'bi-arrow-repeat', + 'shipped' => 'bi-truck', + 'delivered' => 'bi-check-circle', + 'cancelled' => 'bi-x-circle', + default => 'bi-question-circle' + }; + } + + /** + * Получить все возможные статусы доставки для выпадающего списка + */ + public static function getDeliveryStatuses(): array + { + return self::DELIVERY_STATUSES; + } + + /** + * Получить все возможные статусы оплаты для выпадающего списка + */ + public static function getPaymentStatuses(): array + { + return self::PAYMENT_STATUSES; + } + + public static function getRevenue($startDate, $endDate, $paidOnly = true) + { + $query = self::query(); + + if ($paidOnly) { + $query->where('payment_status', 'paid'); + } + + return $query->whereBetween('created_at', [$startDate, $endDate]) + ->sum('total'); + } + + public static function getMonthlyRevenue() + { + return self::getRevenue(now()->startOfMonth(), now()->endOfMonth()); + } +} diff --git a/app/Models/OrderItem.php b/app/Models/OrderItem.php new file mode 100644 index 0000000..aa9af30 --- /dev/null +++ b/app/Models/OrderItem.php @@ -0,0 +1,65 @@ + 'integer', + 'price' => 'decimal:2', + 'total' => 'decimal:2' + ]; + + public function order(): BelongsTo + { + return $this->belongsTo(Order::class); + } + + public function variation(): BelongsTo + { + return $this->belongsTo(ProductVariation::class); + } + + public static function createFromCartItem($orderId, $cartItem) + { + return self::create([ + 'order_id' => $orderId, + 'variation_id' => $cartItem->variation_id, + 'product_name' => $cartItem->variation->product->name, + 'variation_name' => $cartItem->variation->name, + 'sku' => $cartItem->variation->sku, + 'quantity' => $cartItem->quantity, + 'price' => $cartItem->price, + 'total' => $cartItem->total + ]); + } + + public function scopePopular($query, $limit = 5) + { + return $query->select( + 'product_name', + 'variation_name', + 'variation_id', + DB::raw('SUM(quantity) as total_quantity'), + DB::raw('SUM(total) as total_revenue') + ) + ->groupBy('product_name', 'variation_name', 'variation_id') + ->orderBy('total_quantity', 'desc') + ->limit($limit); + } +} diff --git a/app/Models/Permission.php b/app/Models/Permission.php new file mode 100644 index 0000000..e6ba5a5 --- /dev/null +++ b/app/Models/Permission.php @@ -0,0 +1,28 @@ +belongsToMany(Role::class)->withTimestamps(); + } + + public function scopeInGroup($query, string $group) + { + $query->where('group', $group); + } +} diff --git a/app/Models/Product.php b/app/Models/Product.php new file mode 100644 index 0000000..d9e2c85 --- /dev/null +++ b/app/Models/Product.php @@ -0,0 +1,183 @@ + 'boolean', + ]; + + public function brand(): BelongsTo + { + return $this->belongsTo(Brand::class); + } + + public function category(): BelongsTo + { + return $this->belongsTo(Category::class); + } + + public function variations(): HasMany + { + return $this->hasMany(ProductVariation::class); + } + + public function attributes(): HasMany + { + return $this->hasMany(ProductAttribute::class); + } + + public function getDefaultVariationAttribute() + { + $defaultInStock = $this->variations->firstWhere(function ($variation) { + return $variation->is_default && $variation->stock > 0; + }); + + if ($defaultInStock) { + return $defaultInStock; + } + + $anyInStock = $this->variations->firstWhere('stock', '>', 0); + + if ($anyInStock) { + return $anyInStock; + } + + return $this->variations->firstWhere('is_default', true) ?? $this->variations->first(); + } + + /** + * Получить URL главного изображения товара (из дефолтной вариации) + */ + public function getMainImageAttribute(): string + { + $defaultVariation = $this->default_variation; + + if ($defaultVariation) { + return $defaultVariation->main_image_url; + } + + return asset('assets/images/products/default.svg'); + } + + /** + * Получить изображения дефолтной вариации + */ + public function getDefaultVariationImages() + { + $defaultVariation = $this->default_variation; + + if ($defaultVariation) { + return $defaultVariation->images; + } + + return collect(); + } + + public static function getForCarousel($categoryId = null, $limit = 9, $itemsPerSlide = 3) + { + $query = self::with(['category', 'brand', 'variations']) + ->active(); + + if ($categoryId) { + $query->byCategory($categoryId); + } + + $products = $query->limit($limit)->get(); + + $products = $products->filter(function ($product) { + return $product->variations->isNotEmpty(); + }); + + $products = $products->sortByDesc(function ($product) { + $defaultVariation = $product->default_variation; + if ($defaultVariation && $defaultVariation->stock > 0) { + return 3; + } elseif ($defaultVariation) { + return 2; + } elseif ($product->variations->firstWhere('stock', '>', 0)) { + return 1; + } + return 0; + }); + + return $products->chunk($itemsPerSlide); + } + + public function getRatingAttribute(): array + { + return [ + 'score' => 4.9, + 'count' => 1000 + ]; + } + + public function getMinPriceAttribute(): float + { + return $this->variations->min('price') ?? 0; + } + + public function getMaxPriceAttribute(): float + { + return $this->variations->max('price') ?? 0; + } + + public function getPriceRangeAttribute(): string + { + $min = $this->min_price; + $max = $this->max_price; + + if ($min == $max) { + return number_format($min, 0, '.', ' ') . ' ₽'; + } + + return 'от ' . number_format($min, 0, '.', ' ') . ' ₽ до ' . number_format($max, 0, '.', ' ') . ' ₽'; + } + + public function getTotalStockAttribute(): int + { + return $this->variations->sum('stock'); + } + + public function getHasStockAttribute(): bool + { + return $this->total_stock > 0; + } + + public function getFullNameAttribute(): string + { + return $this->brand ? "[{$this->brand->name}] {$this->name}" : $this->name; + } + + public function scopeActive($query) + { + return $query->where('is_active', true); + } + + public function scopeByBrand($query, $brandId) + { + return $query->where('brand_id', $brandId); + } + + public function scopeByCategory($query, $categoryId) + { + return $query->where('category_id', $categoryId); + } +} \ No newline at end of file diff --git a/app/Models/ProductAttribute.php b/app/Models/ProductAttribute.php new file mode 100644 index 0000000..57af154 --- /dev/null +++ b/app/Models/ProductAttribute.php @@ -0,0 +1,16 @@ +belongsTo(Product::class); + } +} diff --git a/app/Models/ProductVariation.php b/app/Models/ProductVariation.php new file mode 100644 index 0000000..77ff3ac --- /dev/null +++ b/app/Models/ProductVariation.php @@ -0,0 +1,160 @@ + 'boolean', + 'is_active' => 'boolean', + 'price' => 'float', + 'old_price' => 'float', + ]; + + public function product(): BelongsTo + { + return $this->belongsTo(Product::class); + } + + public function attributes(): HasMany + { + return $this->hasMany(VariationAttribute::class, 'variation_id'); + } + + public function images(): HasMany + { + return $this->hasMany(VariationImage::class, 'variation_id') + ->ordered(); + } + + /** + * Получить URL главного изображения вариации + */ + public function getMainImageUrlAttribute(): string + { + $image = $this->images()->first(); + + if ($image) { + return $image->url; + } + + return asset('assets/images/products/default.svg'); + } + + /** + * Получить все изображения вариации + */ + public function getAllImages() + { + return $this->images; + } + + public function getHasDiscountAttribute(): bool + { + return $this->old_price && $this->old_price > $this->price; + } + + public function getDiscountPercentAttribute(): int + { + if (!$this->has_discount) return 0; + return round(($this->old_price - $this->price) / $this->old_price * 100); + } + + public function getInStockAttribute(): bool + { + return $this->stock > 0; + } + + public function getFormattedPriceAttribute(): string + { + return number_format($this->price, 0, '.', ' ') . ' ₽'; + } + + public function getFormattedOldPriceAttribute(): string + { + if (!$this->old_price) return ''; + return number_format($this->old_price, 0, '.', ' ') . ' ₽'; + } + + public static function isSkuUnique($sku, $excludeId = null, $productId = null) + { + $query = self::where('sku', $sku); + + if ($excludeId) { + $query->where('id', '!=', $excludeId); + } + + if ($productId) { + $query->where('product_id', $productId); + } + + return !$query->exists(); + } + + public function scopeActive($query) + { + return $query->where('is_active', true); + } + + public function scopeInStock($query) + { + return $query->where('stock', '>', 0); + } + + public function scopeDiscounted($query) + { + return $query->whereNotNull('old_price') + ->whereColumn('old_price', '>', 'price'); + } + + public function scopeDefault($query) + { + return $query->where('is_default', true); + } + + // ========== МЕТОДЫ ДЛЯ AJAX ========== + + public static function getImagesByVariationId($variationId) + { + $variation = self::with(['images' => function ($query) { + $query->active()->ordered(); + }])->find($variationId); + + if (!$variation) { + return collect(); + } + + return $variation->images; + } + + public static function getImagesDataById($variationId) + { + $images = self::getImagesByVariationId($variationId); + + return $images->map(function ($image) { + return [ + 'id' => $image->id, + 'url' => $image->url, + 'sort_order' => $image->sort_order, + 'variation_id' => $image->variation_id + ]; + }); + } +} diff --git a/app/Models/Review.php b/app/Models/Review.php new file mode 100644 index 0000000..59107ed --- /dev/null +++ b/app/Models/Review.php @@ -0,0 +1,74 @@ + 'boolean', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + ]; + + public function user(): BelongsTo + { + return $this->belongsTo(User::class); + } + + public function product(): BelongsTo + { + return $this->belongsTo(Product::class); + } + + public function getDateAttribute(): string + { + return $this->created_at->format('d.m.Y'); + } + + public function getIsFreshAttribute(): bool + { + return $this->created_at->gt(now()->subWeek()); + } + + public function scopeApproved($query) + { + return $query->where('is_approved', true); + } + + public function scopeByRating($query, $rating) + { + return $query->where('rating', $rating); + } + + public function scopeHelpful($query) + { + return $query->orderBy('helpful_count', 'desc'); + } + + public function scopeLatest($query) + { + return $query->orderBy('created_at', 'desc'); + } + + public function scopeUserProduct($query, $userId, $productId) + { + return $query->where('user_id', $userId) + ->where('product_id', $productId); + } +} diff --git a/app/Models/Role.php b/app/Models/Role.php new file mode 100644 index 0000000..d2ff7ef --- /dev/null +++ b/app/Models/Role.php @@ -0,0 +1,32 @@ +belongsToMany(User::class)->withTimestamps(); + } + + public function permissions(): BelongsToMany + { + return $this->belongsToMany(Permission::class)->withTimestamps(); + } + + public function hasPermission(string $permissionSlug): bool + { + return $this->permissions()->where('slug', $permissionSlug)->exists(); + } +} diff --git a/app/Models/User.php b/app/Models/User.php new file mode 100644 index 0000000..8270725 --- /dev/null +++ b/app/Models/User.php @@ -0,0 +1,110 @@ + + */ + protected $fillable = [ + 'name', + 'email', + 'password', + 'phone', + 'role_id', + ]; + + /** + * The attributes that should be hidden for serialization. + * + * @var array + */ + protected $hidden = [ + 'password', + 'remember_token', + ]; + + /** + * The attributes that should be cast. + * + * @var array + */ + protected $casts = [ + 'email_verified_at' => 'datetime', + 'password' => 'hashed', + ]; + + public function role(): BelongsTo + { + return $this->belongsTo(Role::class); + } + + public function permissions(): BelongsToMany + { + return $this->belongsToMany(Permission::class, 'role_user', 'user_id', 'role_id') + ->join('permission_role', 'role_user.role_id', '=', 'permission_role.role_id') + ->join('permissions', 'permission_role.permission_id', '=', 'permissions.id') + ->select('permissions.*') + ->distinct() + ->withTimestamps(); + } + + public function hasRole(string $roleSlug): bool + { + return $this->role()->where('slug', $roleSlug)->exists(); + } + + public function hasPermission(string $permissionSlug): bool + { + if ($this->hasRole('super_admin')) { + return true; + } + + $role = $this->role; + + return $role && $role->hasPermission($permissionSlug); + } + + public function hasAnyPermission(array $permissionSlugs): bool + { + if ($this->is_super_admin) { + return true; + } + + foreach ($permissionSlugs as $permissionSlug) { + if ($this->hasPermission($permissionSlug)) { + return true; + } + } + + return false; + } + + public function hasAllPermissions(array $permissionSlugs): bool + { + if ($this->is_super_admin) { + return true; + } + + foreach ($permissionSlugs as $permissionSlug) { + if (!$this->hasPermission($permissionSlug)) { + return false; + } + } + + return true; + } +} diff --git a/app/Models/VariationAttribute.php b/app/Models/VariationAttribute.php new file mode 100644 index 0000000..c31b15e --- /dev/null +++ b/app/Models/VariationAttribute.php @@ -0,0 +1,16 @@ +belongsTo(ProductVariation::class, 'variation_id'); + } +} diff --git a/app/Models/VariationImage.php b/app/Models/VariationImage.php new file mode 100644 index 0000000..1cef1c6 --- /dev/null +++ b/app/Models/VariationImage.php @@ -0,0 +1,47 @@ + 'integer', + 'is_active' => 'boolean' + ]; + + public function variation(): BelongsTo + { + return $this->belongsTo(ProductVariation::class, 'variation_id'); + } + + public function getUrlAttribute(): string + { + if ($this->path && file_exists(public_path($this->path))) { + return asset($this->path); + } + + return asset('assets/images/products/default.svg'); + } + + public function scopeOrdered($query) + { + return $query->orderBy('sort_order', 'asc'); + } + + public function scopeActive($query) + { + return $query->where('is_active', true); + } +} diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php new file mode 100644 index 0000000..7acca87 --- /dev/null +++ b/app/Providers/AppServiceProvider.php @@ -0,0 +1,25 @@ + + */ + protected $policies = [ + // + ]; + + /** + * Register any authentication / authorization services. + */ + public function boot(): void + { + Gate::before(function ($user, $ability) { + return $user->hasPermission($ability); + }); + } +} diff --git a/app/Providers/BroadcastServiceProvider.php b/app/Providers/BroadcastServiceProvider.php new file mode 100644 index 0000000..2be04f5 --- /dev/null +++ b/app/Providers/BroadcastServiceProvider.php @@ -0,0 +1,19 @@ +> + */ + protected $listen = [ + Registered::class => [ + SendEmailVerificationNotification::class, + ], + ]; + + /** + * Register any events for your application. + */ + public function boot(): void + { + // + } + + /** + * Determine if events and listeners should be automatically discovered. + */ + public function shouldDiscoverEvents(): bool + { + return false; + } +} diff --git a/app/Providers/MenuServiceProvider.php b/app/Providers/MenuServiceProvider.php new file mode 100644 index 0000000..3c9821c --- /dev/null +++ b/app/Providers/MenuServiceProvider.php @@ -0,0 +1,34 @@ +getMenuData(); + + $view->with('menuCategories', $menuData['categories']); + $view->with('menuContacts', $menuData['contacts']); + $view->with('menuBrands', $menuData['popularBrands']); + $view->with('footerCategories', $menuData['footerCategories']); + }); + } +} diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php new file mode 100644 index 0000000..1cf5f15 --- /dev/null +++ b/app/Providers/RouteServiceProvider.php @@ -0,0 +1,40 @@ +by($request->user()?->id ?: $request->ip()); + }); + + $this->routes(function () { + Route::middleware('api') + ->prefix('api') + ->group(base_path('routes/api.php')); + + Route::middleware('web') + ->group(base_path('routes/web.php')); + }); + } +} diff --git a/artisan b/artisan new file mode 100644 index 0000000..67a3329 --- /dev/null +++ b/artisan @@ -0,0 +1,53 @@ +#!/usr/bin/env php +make(Illuminate\Contracts\Console\Kernel::class); + +$status = $kernel->handle( + $input = new Symfony\Component\Console\Input\ArgvInput, + new Symfony\Component\Console\Output\ConsoleOutput +); + +/* +|-------------------------------------------------------------------------- +| Shutdown The Application +|-------------------------------------------------------------------------- +| +| Once Artisan has finished running, we will fire off the shutdown events +| so that any final work may be done by the application before we shut +| down the process. This is the last thing to happen to the request. +| +*/ + +$kernel->terminate($input, $status); + +exit($status); diff --git a/bootstrap/app.php b/bootstrap/app.php new file mode 100644 index 0000000..037e17d --- /dev/null +++ b/bootstrap/app.php @@ -0,0 +1,55 @@ +singleton( + Illuminate\Contracts\Http\Kernel::class, + App\Http\Kernel::class +); + +$app->singleton( + Illuminate\Contracts\Console\Kernel::class, + App\Console\Kernel::class +); + +$app->singleton( + Illuminate\Contracts\Debug\ExceptionHandler::class, + App\Exceptions\Handler::class +); + +/* +|-------------------------------------------------------------------------- +| Return The Application +|-------------------------------------------------------------------------- +| +| This script returns the application instance. The instance is given to +| the calling script so we can separate the building of the instances +| from the actual running of the application and sending responses. +| +*/ + +return $app; diff --git a/bootstrap/cache/.gitignore b/bootstrap/cache/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/bootstrap/cache/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..8a3d72d --- /dev/null +++ b/composer.json @@ -0,0 +1,66 @@ +{ + "name": "laravel/laravel", + "type": "project", + "description": "The skeleton application for the Laravel framework.", + "keywords": ["laravel", "framework"], + "license": "MIT", + "require": { + "php": "^8.1", + "guzzlehttp/guzzle": "^7.2", + "laravel/framework": "^10.10", + "laravel/sanctum": "^3.3", + "laravel/tinker": "^2.8" + }, + "require-dev": { + "fakerphp/faker": "^1.9.1", + "laravel/pint": "^1.0", + "laravel/sail": "^1.18", + "mockery/mockery": "^1.4.4", + "nunomaduro/collision": "^7.0", + "phpunit/phpunit": "^10.1", + "spatie/laravel-ignition": "^2.0" + }, + "autoload": { + "psr-4": { + "App\\": "app/", + "Database\\Factories\\": "database/factories/", + "Database\\Seeders\\": "database/seeders/" + } + }, + "autoload-dev": { + "psr-4": { + "Tests\\": "tests/" + } + }, + "scripts": { + "post-autoload-dump": [ + "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump", + "@php artisan package:discover --ansi" + ], + "post-update-cmd": [ + "@php artisan vendor:publish --tag=laravel-assets --ansi --force" + ], + "post-root-package-install": [ + "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" + ], + "post-create-project-cmd": [ + "@php artisan key:generate --ansi" + ] + }, + "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 +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..27d0f68 --- /dev/null +++ b/composer.lock @@ -0,0 +1,8146 @@ +{ + "_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": "9c491b8531eec05ba41a11d9276a5749", + "packages": [ + { + "name": "brick/math", + "version": "0.12.1", + "source": { + "type": "git", + "url": "https://github.com/brick/math.git", + "reference": "f510c0a40911935b77b86859eb5223d58d660df1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/brick/math/zipball/f510c0a40911935b77b86859eb5223d58d660df1", + "reference": "f510c0a40911935b77b86859eb5223d58d660df1", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.2", + "phpunit/phpunit": "^10.1", + "vimeo/psalm": "5.16.0" + }, + "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.12.1" + }, + "funding": [ + { + "url": "https://github.com/BenMorel", + "type": "github" + } + ], + "time": "2023-11-29T23:19:16+00:00" + }, + { + "name": "carbonphp/carbon-doctrine-types", + "version": "2.1.0", + "source": { + "type": "git", + "url": "https://github.com/CarbonPHP/carbon-doctrine-types.git", + "reference": "99f76ffa36cce3b70a4a6abce41dba15ca2e84cb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/CarbonPHP/carbon-doctrine-types/zipball/99f76ffa36cce3b70a4a6abce41dba15ca2e84cb", + "reference": "99f76ffa36cce3b70a4a6abce41dba15ca2e84cb", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "conflict": { + "doctrine/dbal": "<3.7.0 || >=4.0.0" + }, + "require-dev": { + "doctrine/dbal": "^3.7.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/2.1.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": "2023-12-11T17:09:12+00:00" + }, + { + "name": "dflydev/dot-access-data", + "version": "v3.0.2", + "source": { + "type": "git", + "url": "https://github.com/dflydev/dflydev-dot-access-data.git", + "reference": "f41715465d65213d644d3141a6a93081be5d3549" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dflydev/dflydev-dot-access-data/zipball/f41715465d65213d644d3141a6a93081be5d3549", + "reference": "f41715465d65213d644d3141a6a93081be5d3549", + "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.2" + }, + "time": "2022-10-27T11:44:00+00:00" + }, + { + "name": "doctrine/inflector", + "version": "2.0.10", + "source": { + "type": "git", + "url": "https://github.com/doctrine/inflector.git", + "reference": "5817d0659c5b50c9b950feb9af7b9668e2c436bc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/5817d0659c5b50c9b950feb9af7b9668e2c436bc", + "reference": "5817d0659c5b50c9b950feb9af7b9668e2c436bc", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^11.0", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.1", + "phpstan/phpstan-strict-rules": "^1.3", + "phpunit/phpunit": "^8.5 || ^9.5", + "vimeo/psalm": "^4.25 || ^5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Inflector\\": "lib/Doctrine/Inflector" + } + }, + "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.0.10" + }, + "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": "2024-02-18T20:23:39+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.3.3", + "source": { + "type": "git", + "url": "https://github.com/dragonmantank/cron-expression.git", + "reference": "adfb1f505deb6384dc8b39804c5065dd3c8c8c0a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/adfb1f505deb6384dc8b39804c5065dd3c8c8c0a", + "reference": "adfb1f505deb6384dc8b39804c5065dd3c8c8c0a", + "shasum": "" + }, + "require": { + "php": "^7.2|^8.0", + "webmozart/assert": "^1.0" + }, + "replace": { + "mtdowling/cron-expression": "^1.0" + }, + "require-dev": { + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-webmozart-assert": "^1.0", + "phpunit/phpunit": "^7.0|^8.0|^9.0" + }, + "type": "library", + "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.3.3" + }, + "funding": [ + { + "url": "https://github.com/dragonmantank", + "type": "github" + } + ], + "time": "2023-08-10T19:36:49+00:00" + }, + { + "name": "egulias/email-validator", + "version": "4.0.2", + "source": { + "type": "git", + "url": "https://github.com/egulias/EmailValidator.git", + "reference": "ebaaf5be6c0286928352e054f2d5125608e5405e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/ebaaf5be6c0286928352e054f2d5125608e5405e", + "reference": "ebaaf5be6c0286928352e054f2d5125608e5405e", + "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.2" + }, + "funding": [ + { + "url": "https://github.com/egulias", + "type": "github" + } + ], + "time": "2023-10-06T06:47:41+00:00" + }, + { + "name": "fruitcake/php-cors", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/fruitcake/php-cors.git", + "reference": "3d158f36e7875e2f040f37bc0573956240a5a38b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fruitcake/php-cors/zipball/3d158f36e7875e2f040f37bc0573956240a5a38b", + "reference": "3d158f36e7875e2f040f37bc0573956240a5a38b", + "shasum": "" + }, + "require": { + "php": "^7.4|^8.0", + "symfony/http-foundation": "^4.4|^5.4|^6|^7" + }, + "require-dev": { + "phpstan/phpstan": "^1.4", + "phpunit/phpunit": "^9", + "squizlabs/php_codesniffer": "^3.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2-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.3.0" + }, + "funding": [ + { + "url": "https://fruitcake.nl", + "type": "custom" + }, + { + "url": "https://github.com/barryvdh", + "type": "github" + } + ], + "time": "2023-10-12T05:21:21+00:00" + }, + { + "name": "graham-campbell/result-type", + "version": "v1.1.2", + "source": { + "type": "git", + "url": "https://github.com/GrahamCampbell/Result-Type.git", + "reference": "fbd48bce38f73f8a4ec8583362e732e4095e5862" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/fbd48bce38f73f8a4ec8583362e732e4095e5862", + "reference": "fbd48bce38f73f8a4ec8583362e732e4095e5862", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "phpoption/phpoption": "^1.9.2" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2" + }, + "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.2" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type", + "type": "tidelift" + } + ], + "time": "2023-11-12T22:16:48+00:00" + }, + { + "name": "guzzlehttp/guzzle", + "version": "7.8.1", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "41042bc7ab002487b876a0683fc8dce04ddce104" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/41042bc7ab002487b876a0683fc8dce04ddce104", + "reference": "41042bc7ab002487b876a0683fc8dce04ddce104", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/promises": "^1.5.3 || ^2.0.1", + "guzzlehttp/psr7": "^1.9.1 || ^2.5.1", + "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": "*", + "php-http/client-integration-tests": "dev-master#2c025848417c1135031fdf9c728ee53d0a7ceaee as 3.0.999", + "php-http/message-factory": "^1.1", + "phpunit/phpunit": "^8.5.36 || ^9.6.15", + "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.8.1" + }, + "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": "2023-12-03T20:35:24+00:00" + }, + { + "name": "guzzlehttp/promises", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "bbff78d96034045e58e13dedd6ad91b5d1253223" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/bbff78d96034045e58e13dedd6ad91b5d1253223", + "reference": "bbff78d96034045e58e13dedd6ad91b5d1253223", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.36 || ^9.6.15" + }, + "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.0.2" + }, + "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": "2023-12-03T20:19:20+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "2.6.2", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "45b30f99ac27b5ca93cb4831afe16285f57b8221" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/45b30f99ac27b5ca93cb4831afe16285f57b8221", + "reference": "45b30f99ac27b5ca93cb4831afe16285f57b8221", + "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", + "phpunit/phpunit": "^8.5.36 || ^9.6.15" + }, + "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.6.2" + }, + "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": "2023-12-03T20:05:35+00:00" + }, + { + "name": "guzzlehttp/uri-template", + "version": "v1.0.3", + "source": { + "type": "git", + "url": "https://github.com/guzzle/uri-template.git", + "reference": "ecea8feef63bd4fef1f037ecb288386999ecc11c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/uri-template/zipball/ecea8feef63bd4fef1f037ecb288386999ecc11c", + "reference": "ecea8feef63bd4fef1f037ecb288386999ecc11c", + "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.36 || ^9.6.15", + "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.3" + }, + "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": "2023-12-03T19:50:20+00:00" + }, + { + "name": "laravel/framework", + "version": "v10.48.10", + "source": { + "type": "git", + "url": "https://github.com/laravel/framework.git", + "reference": "91e2b9e218afa4e5c377510faa11957042831ba3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/framework/zipball/91e2b9e218afa4e5c377510faa11957042831ba3", + "reference": "91e2b9e218afa4e5c377510faa11957042831ba3", + "shasum": "" + }, + "require": { + "brick/math": "^0.9.3|^0.10.2|^0.11|^0.12", + "composer-runtime-api": "^2.2", + "doctrine/inflector": "^2.0.5", + "dragonmantank/cron-expression": "^3.3.2", + "egulias/email-validator": "^3.2.1|^4.0", + "ext-ctype": "*", + "ext-filter": "*", + "ext-hash": "*", + "ext-mbstring": "*", + "ext-openssl": "*", + "ext-session": "*", + "ext-tokenizer": "*", + "fruitcake/php-cors": "^1.2", + "guzzlehttp/uri-template": "^1.0", + "laravel/prompts": "^0.1.9", + "laravel/serializable-closure": "^1.3", + "league/commonmark": "^2.2.1", + "league/flysystem": "^3.8.0", + "monolog/monolog": "^3.0", + "nesbot/carbon": "^2.67", + "nunomaduro/termwind": "^1.13", + "php": "^8.1", + "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": "^6.2", + "symfony/error-handler": "^6.2", + "symfony/finder": "^6.2", + "symfony/http-foundation": "^6.4", + "symfony/http-kernel": "^6.2", + "symfony/mailer": "^6.2", + "symfony/mime": "^6.2", + "symfony/process": "^6.2", + "symfony/routing": "^6.2", + "symfony/uid": "^6.2", + "symfony/var-dumper": "^6.2", + "tijsverkoyen/css-to-inline-styles": "^2.2.5", + "vlucas/phpdotenv": "^5.4.1", + "voku/portable-ascii": "^2.0" + }, + "conflict": { + "carbonphp/carbon-doctrine-types": ">=3.0", + "doctrine/dbal": ">=4.0", + "mockery/mockery": "1.6.8", + "phpunit/phpunit": ">=11.0.0", + "tightenco/collect": "<5.5.33" + }, + "provide": { + "psr/container-implementation": "1.1|2.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/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/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/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" + }, + "require-dev": { + "ably/ably-php": "^1.0", + "aws/aws-sdk-php": "^3.235.5", + "doctrine/dbal": "^3.5.1", + "ext-gmp": "*", + "fakerphp/faker": "^1.21", + "guzzlehttp/guzzle": "^7.5", + "league/flysystem-aws-s3-v3": "^3.0", + "league/flysystem-ftp": "^3.0", + "league/flysystem-path-prefixing": "^3.3", + "league/flysystem-read-only": "^3.3", + "league/flysystem-sftp-v3": "^3.0", + "mockery/mockery": "^1.5.1", + "nyholm/psr7": "^1.2", + "orchestra/testbench-core": "^8.23.4", + "pda/pheanstalk": "^4.0", + "phpstan/phpstan": "^1.4.7", + "phpunit/phpunit": "^10.0.7", + "predis/predis": "^2.0.2", + "symfony/cache": "^6.2", + "symfony/http-client": "^6.2.4", + "symfony/psr-http-message-bridge": "^2.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.235.5).", + "brianium/paratest": "Required to run tests in parallel (^6.0).", + "doctrine/dbal": "Required to rename columns and drop SQLite columns (^3.5.1).", + "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).", + "fakerphp/faker": "Required to use the eloquent factory builder (^1.9.1).", + "filp/whoops": "Required for friendly error pages in development (^2.14.3).", + "guzzlehttp/guzzle": "Required to use the HTTP Client and the ping methods on schedules (^7.5).", + "laravel/tinker": "Required to use the tinker console command (^2.0).", + "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^3.0).", + "league/flysystem-ftp": "Required to use the Flysystem FTP driver (^3.0).", + "league/flysystem-path-prefixing": "Required to use the scoped driver (^3.3).", + "league/flysystem-read-only": "Required to use read-only disks (^3.3)", + "league/flysystem-sftp-v3": "Required to use the Flysystem SFTP driver (^3.0).", + "mockery/mockery": "Required to use mocking (^1.5.1).", + "nyholm/psr7": "Required to use PSR-7 bridging features (^1.2).", + "pda/pheanstalk": "Required to use the beanstalk queue driver (^4.0).", + "phpunit/phpunit": "Required to use assertions and run tests (^9.5.8|^10.0.7).", + "predis/predis": "Required to use the predis connector (^2.0.2).", + "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).", + "symfony/cache": "Required to PSR-6 cache bridge (^6.2).", + "symfony/filesystem": "Required to enable support for relative symbolic links (^6.2).", + "symfony/http-client": "Required to enable support for the Symfony API mail transports (^6.2).", + "symfony/mailgun-mailer": "Required to enable support for the Mailgun mail transport (^6.2).", + "symfony/postmark-mailer": "Required to enable support for the Postmark mail transport (^6.2).", + "symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^2.0)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "10.x-dev" + } + }, + "autoload": { + "files": [ + "src/Illuminate/Collections/helpers.php", + "src/Illuminate/Events/functions.php", + "src/Illuminate/Filesystem/functions.php", + "src/Illuminate/Foundation/helpers.php", + "src/Illuminate/Support/helpers.php" + ], + "psr-4": { + "Illuminate\\": "src/Illuminate/", + "Illuminate\\Support\\": [ + "src/Illuminate/Macroable/", + "src/Illuminate/Collections/", + "src/Illuminate/Conditionable/" + ] + } + }, + "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": "2024-04-30T12:52:59+00:00" + }, + { + "name": "laravel/prompts", + "version": "v0.1.21", + "source": { + "type": "git", + "url": "https://github.com/laravel/prompts.git", + "reference": "23ea808e8a145653e0ab29e30d4385e49f40a920" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/prompts/zipball/23ea808e8a145653e0ab29e30d4385e49f40a920", + "reference": "23ea808e8a145653e0ab29e30d4385e49f40a920", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "illuminate/collections": "^10.0|^11.0", + "php": "^8.1", + "symfony/console": "^6.2|^7.0" + }, + "conflict": { + "illuminate/console": ">=10.17.0 <10.25.0", + "laravel/framework": ">=10.17.0 <10.25.0" + }, + "require-dev": { + "mockery/mockery": "^1.5", + "pestphp/pest": "^2.3", + "phpstan/phpstan": "^1.11", + "phpstan/phpstan-mockery": "^1.1" + }, + "suggest": { + "ext-pcntl": "Required for the spinner to be animated." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "0.1.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.1.21" + }, + "time": "2024-04-30T12:46:16+00:00" + }, + { + "name": "laravel/sanctum", + "version": "v3.3.3", + "source": { + "type": "git", + "url": "https://github.com/laravel/sanctum.git", + "reference": "8c104366459739f3ada0e994bcd3e6fd681ce3d5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/sanctum/zipball/8c104366459739f3ada0e994bcd3e6fd681ce3d5", + "reference": "8c104366459739f3ada0e994bcd3e6fd681ce3d5", + "shasum": "" + }, + "require": { + "ext-json": "*", + "illuminate/console": "^9.21|^10.0", + "illuminate/contracts": "^9.21|^10.0", + "illuminate/database": "^9.21|^10.0", + "illuminate/support": "^9.21|^10.0", + "php": "^8.0.2" + }, + "require-dev": { + "mockery/mockery": "^1.0", + "orchestra/testbench": "^7.28.2|^8.8.3", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + }, + "laravel": { + "providers": [ + "Laravel\\Sanctum\\SanctumServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Sanctum\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Laravel Sanctum provides a featherweight authentication system for SPAs and simple APIs.", + "keywords": [ + "auth", + "laravel", + "sanctum" + ], + "support": { + "issues": "https://github.com/laravel/sanctum/issues", + "source": "https://github.com/laravel/sanctum" + }, + "time": "2023-12-19T18:44:48+00:00" + }, + { + "name": "laravel/serializable-closure", + "version": "v1.3.3", + "source": { + "type": "git", + "url": "https://github.com/laravel/serializable-closure.git", + "reference": "3dbf8a8e914634c48d389c1234552666b3d43754" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/3dbf8a8e914634c48d389c1234552666b3d43754", + "reference": "3dbf8a8e914634c48d389c1234552666b3d43754", + "shasum": "" + }, + "require": { + "php": "^7.3|^8.0" + }, + "require-dev": { + "nesbot/carbon": "^2.61", + "pestphp/pest": "^1.21.3", + "phpstan/phpstan": "^1.8.2", + "symfony/var-dumper": "^5.4.11" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.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": "2023-11-08T14:08:06+00:00" + }, + { + "name": "laravel/tinker", + "version": "v2.9.0", + "source": { + "type": "git", + "url": "https://github.com/laravel/tinker.git", + "reference": "502e0fe3f0415d06d5db1f83a472f0f3b754bafe" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/tinker/zipball/502e0fe3f0415d06d5db1f83a472f0f3b754bafe", + "reference": "502e0fe3f0415d06d5db1f83a472f0f3b754bafe", + "shasum": "" + }, + "require": { + "illuminate/console": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0", + "illuminate/contracts": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0", + "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0", + "php": "^7.2.5|^8.0", + "psy/psysh": "^0.11.1|^0.12.0", + "symfony/var-dumper": "^4.3.4|^5.0|^6.0|^7.0" + }, + "require-dev": { + "mockery/mockery": "~1.3.3|^1.4.2", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^8.5.8|^9.3.3" + }, + "suggest": { + "illuminate/database": "The Illuminate Database package (^6.0|^7.0|^8.0|^9.0|^10.0|^11.0)." + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Laravel\\Tinker\\TinkerServiceProvider" + ] + } + }, + "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/v2.9.0" + }, + "time": "2024-01-04T16:10:04+00:00" + }, + { + "name": "league/commonmark", + "version": "2.4.2", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/commonmark.git", + "reference": "91c24291965bd6d7c46c46a12ba7492f83b1cadf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/91c24291965bd6d7c46c46a12ba7492f83b1cadf", + "reference": "91c24291965bd6d7c46c46a12ba7492f83b1cadf", + "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.30.3", + "commonmark/commonmark.js": "0.30.0", + "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", + "symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0 || ^7.0", + "unleashedtech/php-coding-standard": "^3.1.1", + "vimeo/psalm": "^4.24.0 || ^5.0.0" + }, + "suggest": { + "symfony/yaml": "v2.3+ required if using the Front Matter extension" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.5-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": "2024-02-02T11:59:32+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.27.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem.git", + "reference": "4729745b1ab737908c7d055148c9a6b3e959832f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/4729745b1ab737908c7d055148c9a6b3e959832f", + "reference": "4729745b1ab737908c7d055148c9a6b3e959832f", + "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-zip": "*", + "friendsofphp/php-cs-fixer": "^3.5", + "google/cloud-storage": "^1.23", + "microsoft/azure-storage-blob": "^1.1", + "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.27.0" + }, + "funding": [ + { + "url": "https://ecologi.com/frankdejonge", + "type": "custom" + }, + { + "url": "https://github.com/frankdejonge", + "type": "github" + } + ], + "time": "2024-04-07T19:17:50+00:00" + }, + { + "name": "league/flysystem-local", + "version": "3.25.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem-local.git", + "reference": "61a6a90d6e999e4ddd9ce5adb356de0939060b92" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/61a6a90d6e999e4ddd9ce5adb356de0939060b92", + "reference": "61a6a90d6e999e4ddd9ce5adb356de0939060b92", + "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.25.1" + }, + "funding": [ + { + "url": "https://ecologi.com/frankdejonge", + "type": "custom" + }, + { + "url": "https://github.com/frankdejonge", + "type": "github" + } + ], + "time": "2024-03-15T19:58:44+00:00" + }, + { + "name": "league/mime-type-detection", + "version": "1.15.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/mime-type-detection.git", + "reference": "ce0f4d1e8a6f4eb0ddff33f57c69c50fd09f4301" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/ce0f4d1e8a6f4eb0ddff33f57c69c50fd09f4301", + "reference": "ce0f4d1e8a6f4eb0ddff33f57c69c50fd09f4301", + "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.15.0" + }, + "funding": [ + { + "url": "https://github.com/frankdejonge", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/league/flysystem", + "type": "tidelift" + } + ], + "time": "2024-01-28T23:22:08+00:00" + }, + { + "name": "monolog/monolog", + "version": "3.6.0", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/monolog.git", + "reference": "4b18b21a5527a3d5ffdac2fd35d3ab25a9597654" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/4b18b21a5527a3d5ffdac2fd35d3ab25a9597654", + "reference": "4b18b21a5527a3d5ffdac2fd35d3ab25a9597654", + "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", + "php-amqplib/php-amqplib": "~2.4 || ^3", + "phpstan/phpstan": "^1.9", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpstan/phpstan-strict-rules": "^1.4", + "phpunit/phpunit": "^10.5.17", + "predis/predis": "^1.1 || ^2", + "ruflin/elastica": "^7", + "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.6.0" + }, + "funding": [ + { + "url": "https://github.com/Seldaek", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/monolog/monolog", + "type": "tidelift" + } + ], + "time": "2024-04-12T21:02:21+00:00" + }, + { + "name": "nesbot/carbon", + "version": "2.72.3", + "source": { + "type": "git", + "url": "https://github.com/briannesbitt/Carbon.git", + "reference": "0c6fd108360c562f6e4fd1dedb8233b423e91c83" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/0c6fd108360c562f6e4fd1dedb8233b423e91c83", + "reference": "0c6fd108360c562f6e4fd1dedb8233b423e91c83", + "shasum": "" + }, + "require": { + "carbonphp/carbon-doctrine-types": "*", + "ext-json": "*", + "php": "^7.1.8 || ^8.0", + "psr/clock": "^1.0", + "symfony/polyfill-mbstring": "^1.0", + "symfony/polyfill-php80": "^1.16", + "symfony/translation": "^3.4 || ^4.0 || ^5.0 || ^6.0" + }, + "provide": { + "psr/clock-implementation": "1.0" + }, + "require-dev": { + "doctrine/dbal": "^2.0 || ^3.1.4 || ^4.0", + "doctrine/orm": "^2.7 || ^3.0", + "friendsofphp/php-cs-fixer": "^3.0", + "kylekatarnls/multi-tester": "^2.0", + "ondrejmirtes/better-reflection": "*", + "phpmd/phpmd": "^2.9", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^0.12.99 || ^1.7.14", + "phpunit/php-file-iterator": "^2.0.5 || ^3.0.6", + "phpunit/phpunit": "^7.5.20 || ^8.5.26 || ^9.5.20", + "squizlabs/php_codesniffer": "^3.4" + }, + "bin": [ + "bin/carbon" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-3.x": "3.x-dev", + "dev-master": "2.x-dev" + }, + "laravel": { + "providers": [ + "Carbon\\Laravel\\ServiceProvider" + ] + }, + "phpstan": { + "includes": [ + "extension.neon" + ] + } + }, + "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://carbon.nesbot.com", + "keywords": [ + "date", + "datetime", + "time" + ], + "support": { + "docs": "https://carbon.nesbot.com/docs", + "issues": "https://github.com/briannesbitt/Carbon/issues", + "source": "https://github.com/briannesbitt/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": "2024-01-25T10:35:09+00:00" + }, + { + "name": "nette/schema", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/nette/schema.git", + "reference": "a6d3a6d1f545f01ef38e60f375d1cf1f4de98188" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/schema/zipball/a6d3a6d1f545f01ef38e60f375d1cf1f4de98188", + "reference": "a6d3a6d1f545f01ef38e60f375d1cf1f4de98188", + "shasum": "" + }, + "require": { + "nette/utils": "^4.0", + "php": "8.1 - 8.3" + }, + "require-dev": { + "nette/tester": "^2.4", + "phpstan/phpstan-nette": "^1.0", + "tracy/tracy": "^2.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "autoload": { + "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.0" + }, + "time": "2023-12-11T11:54:22+00:00" + }, + { + "name": "nette/utils", + "version": "v4.0.4", + "source": { + "type": "git", + "url": "https://github.com/nette/utils.git", + "reference": "d3ad0aa3b9f934602cb3e3902ebccf10be34d218" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/utils/zipball/d3ad0aa3b9f934602cb3e3902ebccf10be34d218", + "reference": "d3ad0aa3b9f934602cb3e3902ebccf10be34d218", + "shasum": "" + }, + "require": { + "php": ">=8.0 <8.4" + }, + "conflict": { + "nette/finder": "<3", + "nette/schema": "<1.2.2" + }, + "require-dev": { + "jetbrains/phpstorm-attributes": "dev-master", + "nette/tester": "^2.5", + "phpstan/phpstan": "^1.0", + "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.0-dev" + } + }, + "autoload": { + "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.0.4" + }, + "time": "2024-01-17T16:50:36+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v5.0.2", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "139676794dc1e9231bf7bcd123cfc0c99182cb13" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/139676794dc1e9231bf7bcd123cfc0c99182cb13", + "reference": "139676794dc1e9231bf7bcd123cfc0c99182cb13", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-json": "*", + "ext-tokenizer": "*", + "php": ">=7.4" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-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.0.2" + }, + "time": "2024-03-05T20:51:40+00:00" + }, + { + "name": "nunomaduro/termwind", + "version": "v1.15.1", + "source": { + "type": "git", + "url": "https://github.com/nunomaduro/termwind.git", + "reference": "8ab0b32c8caa4a2e09700ea32925441385e4a5dc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/8ab0b32c8caa4a2e09700ea32925441385e4a5dc", + "reference": "8ab0b32c8caa4a2e09700ea32925441385e4a5dc", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": "^8.0", + "symfony/console": "^5.3.0|^6.0.0" + }, + "require-dev": { + "ergebnis/phpstan-rules": "^1.0.", + "illuminate/console": "^8.0|^9.0", + "illuminate/support": "^8.0|^9.0", + "laravel/pint": "^1.0.0", + "pestphp/pest": "^1.21.0", + "pestphp/pest-plugin-mock": "^1.0", + "phpstan/phpstan": "^1.4.6", + "phpstan/phpstan-strict-rules": "^1.1.0", + "symfony/var-dumper": "^5.2.7|^6.0.0", + "thecodingmachine/phpstan-strict-rules": "^1.0.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Termwind\\Laravel\\TermwindServiceProvider" + ] + } + }, + "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": "Its 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/v1.15.1" + }, + "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": "2023-02-08T01:06:31+00:00" + }, + { + "name": "phpoption/phpoption", + "version": "1.9.2", + "source": { + "type": "git", + "url": "https://github.com/schmittjoh/php-option.git", + "reference": "80735db690fe4fc5c76dfa7f9b770634285fa820" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/80735db690fe4fc5c76dfa7f9b770634285fa820", + "reference": "80735db690fe4fc5c76dfa7f9b770634285fa820", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": true + }, + "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.2" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption", + "type": "tidelift" + } + ], + "time": "2023-11-12T21:59:55+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.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", + "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.0" + }, + "time": "2021-07-14T16:46:02+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.3", + "source": { + "type": "git", + "url": "https://github.com/bobthecow/psysh.git", + "reference": "b6b6cce7d3ee8fbf31843edce5e8f5a72eff4a73" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/b6b6cce7d3ee8fbf31843edce5e8f5a72eff4a73", + "reference": "b6b6cce7d3ee8fbf31843edce5e8f5a72eff4a73", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-tokenizer": "*", + "nikic/php-parser": "^5.0 || ^4.0", + "php": "^8.0 || ^7.4", + "symfony/console": "^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4", + "symfony/var-dumper": "^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" + }, + "suggest": { + "ext-pcntl": "Enabling the PCNTL extension makes PsySH a lot happier :)", + "ext-pdo-sqlite": "The doc command requires SQLite to work.", + "ext-posix": "If you have PCNTL, you'll want the POSIX extension as well." + }, + "bin": [ + "bin/psysh" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "0.12.x-dev" + }, + "bamarni-bin": { + "bin-links": false, + "forward-command": false + } + }, + "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", + "homepage": "http://justinhileman.com" + } + ], + "description": "An interactive shell for modern PHP.", + "homepage": "http://psysh.org", + "keywords": [ + "REPL", + "console", + "interactive", + "shell" + ], + "support": { + "issues": "https://github.com/bobthecow/psysh/issues", + "source": "https://github.com/bobthecow/psysh/tree/v0.12.3" + }, + "time": "2024-04-02T15:57:53+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.0.0", + "source": { + "type": "git", + "url": "https://github.com/ramsey/collection.git", + "reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ramsey/collection/zipball/a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5", + "reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "captainhook/plugin-composer": "^5.3", + "ergebnis/composer-normalize": "^2.28.3", + "fakerphp/faker": "^1.21", + "hamcrest/hamcrest-php": "^2.0", + "jangregor/phpstan-prophecy": "^1.0", + "mockery/mockery": "^1.5", + "php-parallel-lint/php-console-highlighter": "^1.0", + "php-parallel-lint/php-parallel-lint": "^1.3", + "phpcsstandards/phpcsutils": "^1.0.0-rc1", + "phpspec/prophecy-phpunit": "^2.0", + "phpstan/extension-installer": "^1.2", + "phpstan/phpstan": "^1.9", + "phpstan/phpstan-mockery": "^1.1", + "phpstan/phpstan-phpunit": "^1.3", + "phpunit/phpunit": "^9.5", + "psalm/plugin-mockery": "^1.1", + "psalm/plugin-phpunit": "^0.18.4", + "ramsey/coding-standard": "^2.0.3", + "ramsey/conventional-commits": "^1.3", + "vimeo/psalm": "^5.4" + }, + "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.0.0" + }, + "funding": [ + { + "url": "https://github.com/ramsey", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/ramsey/collection", + "type": "tidelift" + } + ], + "time": "2022-12-31T21:50:55+00:00" + }, + { + "name": "ramsey/uuid", + "version": "4.7.6", + "source": { + "type": "git", + "url": "https://github.com/ramsey/uuid.git", + "reference": "91039bc1faa45ba123c4328958e620d382ec7088" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/91039bc1faa45ba123c4328958e620d382ec7088", + "reference": "91039bc1faa45ba123c4328958e620d382ec7088", + "shasum": "" + }, + "require": { + "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12", + "ext-json": "*", + "php": "^8.0", + "ramsey/collection": "^1.2 || ^2.0" + }, + "replace": { + "rhumsaa/uuid": "self.version" + }, + "require-dev": { + "captainhook/captainhook": "^5.10", + "captainhook/plugin-composer": "^5.3", + "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", + "doctrine/annotations": "^1.8", + "ergebnis/composer-normalize": "^2.15", + "mockery/mockery": "^1.3", + "paragonie/random-lib": "^2", + "php-mock/php-mock": "^2.2", + "php-mock/php-mock-mockery": "^1.3", + "php-parallel-lint/php-parallel-lint": "^1.1", + "phpbench/phpbench": "^1.0", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-mockery": "^1.1", + "phpstan/phpstan-phpunit": "^1.1", + "phpunit/phpunit": "^8.5 || ^9", + "ramsey/composer-repl": "^1.4", + "slevomat/coding-standard": "^8.4", + "squizlabs/php_codesniffer": "^3.5", + "vimeo/psalm": "^4.9" + }, + "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.7.6" + }, + "funding": [ + { + "url": "https://github.com/ramsey", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/ramsey/uuid", + "type": "tidelift" + } + ], + "time": "2024-04-27T21:32:50+00:00" + }, + { + "name": "symfony/console", + "version": "v6.4.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "a170e64ae10d00ba89e2acbb590dc2e54da8ad8f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/a170e64ae10d00ba89e2acbb590dc2e54da8ad8f", + "reference": "a170e64ae10d00ba89e2acbb590dc2e54da8ad8f", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^5.4|^6.0|^7.0" + }, + "conflict": { + "symfony/dependency-injection": "<5.4", + "symfony/dotenv": "<5.4", + "symfony/event-dispatcher": "<5.4", + "symfony/lock": "<5.4", + "symfony/process": "<5.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/lock": "^5.4|^6.0|^7.0", + "symfony/messenger": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/stopwatch": "^5.4|^6.0|^7.0", + "symfony/var-dumper": "^5.4|^6.0|^7.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/v6.4.7" + }, + "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-04-18T09:22:46+00:00" + }, + { + "name": "symfony/css-selector", + "version": "v6.4.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/css-selector.git", + "reference": "1c5d5c2103c3762aff27a27e1e2409e30a79083b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/1c5d5c2103c3762aff27a27e1e2409e30a79083b", + "reference": "1c5d5c2103c3762aff27a27e1e2409e30a79083b", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "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/v6.4.7" + }, + "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-04-18T09:22:46+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", + "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "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.5.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-04-18T09:32:20+00:00" + }, + { + "name": "symfony/error-handler", + "version": "v6.4.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/error-handler.git", + "reference": "667a072466c6a53827ed7b119af93806b884cbb3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/667a072466c6a53827ed7b119af93806b884cbb3", + "reference": "667a072466c6a53827ed7b119af93806b884cbb3", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/log": "^1|^2|^3", + "symfony/var-dumper": "^5.4|^6.0|^7.0" + }, + "conflict": { + "symfony/deprecation-contracts": "<2.5", + "symfony/http-kernel": "<6.4" + }, + "require-dev": { + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/serializer": "^5.4|^6.0|^7.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/v6.4.7" + }, + "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-04-18T09:22:46+00:00" + }, + { + "name": "symfony/event-dispatcher", + "version": "v6.4.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "d84384f3f67de3cb650db64d685d70395dacfc3f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/d84384f3f67de3cb650db64d685d70395dacfc3f", + "reference": "d84384f3f67de3cb650db64d685d70395dacfc3f", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/event-dispatcher-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/dependency-injection": "<5.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": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/error-handler": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^5.4|^6.0|^7.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/stopwatch": "^5.4|^6.0|^7.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/v6.4.7" + }, + "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-04-18T09:22:46+00:00" + }, + { + "name": "symfony/event-dispatcher-contracts", + "version": "v3.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher-contracts.git", + "reference": "8f93aec25d41b72493c6ddff14e916177c9efc50" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/8f93aec25d41b72493c6ddff14e916177c9efc50", + "reference": "8f93aec25d41b72493c6ddff14e916177c9efc50", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/event-dispatcher": "^1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "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.5.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-04-18T09:32:20+00:00" + }, + { + "name": "symfony/finder", + "version": "v6.4.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "511c48990be17358c23bf45c5d71ab85d40fb764" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/511c48990be17358c23bf45c5d71ab85d40fb764", + "reference": "511c48990be17358c23bf45c5d71ab85d40fb764", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "symfony/filesystem": "^6.0|^7.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/v6.4.7" + }, + "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-04-23T10:36:43+00:00" + }, + { + "name": "symfony/http-foundation", + "version": "v6.4.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-foundation.git", + "reference": "b4db6b833035477cb70e18d0ae33cb7c2b521759" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/b4db6b833035477cb70e18d0ae33cb7c2b521759", + "reference": "b4db6b833035477cb70e18d0ae33cb7c2b521759", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.1", + "symfony/polyfill-php83": "^1.27" + }, + "conflict": { + "symfony/cache": "<6.3" + }, + "require-dev": { + "doctrine/dbal": "^2.13.1|^3|^4", + "predis/predis": "^1.1|^2.0", + "symfony/cache": "^6.3|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/http-kernel": "^5.4.12|^6.0.12|^6.1.4|^7.0", + "symfony/mime": "^5.4|^6.0|^7.0", + "symfony/rate-limiter": "^5.4|^6.0|^7.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/v6.4.7" + }, + "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-04-18T09:22:46+00:00" + }, + { + "name": "symfony/http-kernel", + "version": "v6.4.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-kernel.git", + "reference": "b7b5e6cdef670a0c82d015a966ffc7e855861a98" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/b7b5e6cdef670a0c82d015a966ffc7e855861a98", + "reference": "b7b5e6cdef670a0c82d015a966ffc7e855861a98", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/log": "^1|^2|^3", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/error-handler": "^6.4|^7.0", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "symfony/browser-kit": "<5.4", + "symfony/cache": "<5.4", + "symfony/config": "<6.1", + "symfony/console": "<5.4", + "symfony/dependency-injection": "<6.4", + "symfony/doctrine-bridge": "<5.4", + "symfony/form": "<5.4", + "symfony/http-client": "<5.4", + "symfony/http-client-contracts": "<2.5", + "symfony/mailer": "<5.4", + "symfony/messenger": "<5.4", + "symfony/translation": "<5.4", + "symfony/translation-contracts": "<2.5", + "symfony/twig-bridge": "<5.4", + "symfony/validator": "<6.4", + "symfony/var-dumper": "<6.3", + "twig/twig": "<2.13" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/cache": "^1.0|^2.0|^3.0", + "symfony/browser-kit": "^5.4|^6.0|^7.0", + "symfony/clock": "^6.2|^7.0", + "symfony/config": "^6.1|^7.0", + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/css-selector": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/dom-crawler": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/finder": "^5.4|^6.0|^7.0", + "symfony/http-client-contracts": "^2.5|^3", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/property-access": "^5.4.5|^6.0.5|^7.0", + "symfony/routing": "^5.4|^6.0|^7.0", + "symfony/serializer": "^6.4.4|^7.0.4", + "symfony/stopwatch": "^5.4|^6.0|^7.0", + "symfony/translation": "^5.4|^6.0|^7.0", + "symfony/translation-contracts": "^2.5|^3", + "symfony/uid": "^5.4|^6.0|^7.0", + "symfony/validator": "^6.4|^7.0", + "symfony/var-dumper": "^5.4|^6.4|^7.0", + "symfony/var-exporter": "^6.2|^7.0", + "twig/twig": "^2.13|^3.0.4" + }, + "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/v6.4.7" + }, + "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-04-29T11:24:44+00:00" + }, + { + "name": "symfony/mailer", + "version": "v6.4.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/mailer.git", + "reference": "2c446d4e446995bed983c0b5bb9ff837e8de7dbd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/mailer/zipball/2c446d4e446995bed983c0b5bb9ff837e8de7dbd", + "reference": "2c446d4e446995bed983c0b5bb9ff837e8de7dbd", + "shasum": "" + }, + "require": { + "egulias/email-validator": "^2.1.10|^3|^4", + "php": ">=8.1", + "psr/event-dispatcher": "^1", + "psr/log": "^1|^2|^3", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/mime": "^6.2|^7.0", + "symfony/service-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/http-client-contracts": "<2.5", + "symfony/http-kernel": "<5.4", + "symfony/messenger": "<6.2", + "symfony/mime": "<6.2", + "symfony/twig-bridge": "<6.2.1" + }, + "require-dev": { + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/http-client": "^5.4|^6.0|^7.0", + "symfony/messenger": "^6.2|^7.0", + "symfony/twig-bridge": "^6.2|^7.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/v6.4.7" + }, + "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-04-18T09:22:46+00:00" + }, + { + "name": "symfony/mime", + "version": "v6.4.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/mime.git", + "reference": "decadcf3865918ecfcbfa90968553994ce935a5e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/mime/zipball/decadcf3865918ecfcbfa90968553994ce935a5e", + "reference": "decadcf3865918ecfcbfa90968553994ce935a5e", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-intl-idn": "^1.10", + "symfony/polyfill-mbstring": "^1.0" + }, + "conflict": { + "egulias/email-validator": "~3.0.0", + "phpdocumentor/reflection-docblock": "<3.2.2", + "phpdocumentor/type-resolver": "<1.4.0", + "symfony/mailer": "<5.4", + "symfony/serializer": "<6.3.2" + }, + "require-dev": { + "egulias/email-validator": "^2.1.10|^3.1|^4", + "league/html-to-markdown": "^5.0", + "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.4|^7.0", + "symfony/property-access": "^5.4|^6.0|^7.0", + "symfony/property-info": "^5.4|^6.0|^7.0", + "symfony/serializer": "^6.3.2|^7.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/v6.4.7" + }, + "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-04-18T09:22:46+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ef4d7e442ca910c4764bce785146269b30cb5fc4", + "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/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.29.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-01-29T20:11:03+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/32a9da87d7b3245e09ac426c83d334ae9f06f80f", + "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/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.29.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-01-29T20:11:03+00:00" + }, + { + "name": "symfony/polyfill-intl-idn", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-idn.git", + "reference": "a287ed7475f85bf6f61890146edbc932c0fff919" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/a287ed7475f85bf6f61890146edbc932c0fff919", + "reference": "a287ed7475f85bf6f61890146edbc932c0fff919", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "symfony/polyfill-intl-normalizer": "^1.10", + "symfony/polyfill-php72": "^1.10" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/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.29.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-01-29T20:11:03+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "bc45c394692b948b4d383a08d7753968bed9a83d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/bc45c394692b948b4d383a08d7753968bed9a83d", + "reference": "bc45c394692b948b4d383a08d7753968bed9a83d", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/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.29.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-01-29T20:11:03+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9773676c8a1bb1f8d4340a62efe641cf76eda7ec", + "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/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.29.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-01-29T20:11:03+00:00" + }, + { + "name": "symfony/polyfill-php72", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php72.git", + "reference": "861391a8da9a04cbad2d232ddd9e4893220d6e25" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/861391a8da9a04cbad2d232ddd9e4893220d6e25", + "reference": "861391a8da9a04cbad2d232ddd9e4893220d6e25", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php72\\": "" + } + }, + "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 7.2+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php72/tree/v1.29.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-01-29T20:11:03+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/87b68208d5c1188808dd7839ee1e6c8ec3b02f1b", + "reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/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.29.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-01-29T20:11:03+00:00" + }, + { + "name": "symfony/polyfill-php83", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php83.git", + "reference": "86fcae159633351e5fd145d1c47de6c528f8caff" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/86fcae159633351e5fd145d1c47de6c528f8caff", + "reference": "86fcae159633351e5fd145d1c47de6c528f8caff", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "symfony/polyfill-php80": "^1.14" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php83\\": "" + }, + "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.3+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php83/tree/v1.29.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-01-29T20:11:03+00:00" + }, + { + "name": "symfony/polyfill-uuid", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-uuid.git", + "reference": "3abdd21b0ceaa3000ee950097bc3cf9efc137853" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/3abdd21b0ceaa3000ee950097bc3cf9efc137853", + "reference": "3abdd21b0ceaa3000ee950097bc3cf9efc137853", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-uuid": "*" + }, + "suggest": { + "ext-uuid": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/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.29.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-01-29T20:11:03+00:00" + }, + { + "name": "symfony/process", + "version": "v6.4.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "cdb1c81c145fd5aa9b0038bab694035020943381" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/cdb1c81c145fd5aa9b0038bab694035020943381", + "reference": "cdb1c81c145fd5aa9b0038bab694035020943381", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "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/v6.4.7" + }, + "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-04-18T09:22:46+00:00" + }, + { + "name": "symfony/routing", + "version": "v6.4.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/routing.git", + "reference": "276e06398f71fa2a973264d94f28150f93cfb907" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/routing/zipball/276e06398f71fa2a973264d94f28150f93cfb907", + "reference": "276e06398f71fa2a973264d94f28150f93cfb907", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "conflict": { + "doctrine/annotations": "<1.12", + "symfony/config": "<6.2", + "symfony/dependency-injection": "<5.4", + "symfony/yaml": "<5.4" + }, + "require-dev": { + "doctrine/annotations": "^1.12|^2", + "psr/log": "^1|^2|^3", + "symfony/config": "^6.2|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^5.4|^6.0|^7.0", + "symfony/yaml": "^5.4|^6.0|^7.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/v6.4.7" + }, + "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-04-18T09:22:46+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v3.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", + "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", + "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": { + "branch-alias": { + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "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.5.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-04-18T09:32:20+00:00" + }, + { + "name": "symfony/string", + "version": "v6.4.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "ffeb9591c61f65a68d47f77d12b83fa530227a69" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/ffeb9591c61f65a68d47f77d12b83fa530227a69", + "reference": "ffeb9591c61f65a68d47f77d12b83fa530227a69", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/translation-contracts": "<2.5" + }, + "require-dev": { + "symfony/error-handler": "^5.4|^6.0|^7.0", + "symfony/http-client": "^5.4|^6.0|^7.0", + "symfony/intl": "^6.2|^7.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^5.4|^6.0|^7.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/v6.4.7" + }, + "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-04-18T09:22:46+00:00" + }, + { + "name": "symfony/translation", + "version": "v6.4.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation.git", + "reference": "7495687c58bfd88b7883823747b0656d90679123" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation/zipball/7495687c58bfd88b7883823747b0656d90679123", + "reference": "7495687c58bfd88b7883823747b0656d90679123", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/translation-contracts": "^2.5|^3.0" + }, + "conflict": { + "symfony/config": "<5.4", + "symfony/console": "<5.4", + "symfony/dependency-injection": "<5.4", + "symfony/http-client-contracts": "<2.5", + "symfony/http-kernel": "<5.4", + "symfony/service-contracts": "<2.5", + "symfony/twig-bundle": "<5.4", + "symfony/yaml": "<5.4" + }, + "provide": { + "symfony/translation-implementation": "2.3|3.0" + }, + "require-dev": { + "nikic/php-parser": "^4.18|^5.0", + "psr/log": "^1|^2|^3", + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/finder": "^5.4|^6.0|^7.0", + "symfony/http-client-contracts": "^2.5|^3.0", + "symfony/http-kernel": "^5.4|^6.0|^7.0", + "symfony/intl": "^5.4|^6.0|^7.0", + "symfony/polyfill-intl-icu": "^1.21", + "symfony/routing": "^5.4|^6.0|^7.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/yaml": "^5.4|^6.0|^7.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/v6.4.7" + }, + "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-04-18T09:22:46+00:00" + }, + { + "name": "symfony/translation-contracts", + "version": "v3.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation-contracts.git", + "reference": "b9d2189887bb6b2e0367a9fc7136c5239ab9b05a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/b9d2189887bb6b2e0367a9fc7136c5239ab9b05a", + "reference": "b9d2189887bb6b2e0367a9fc7136c5239ab9b05a", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "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.5.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-04-18T09:32:20+00:00" + }, + { + "name": "symfony/uid", + "version": "v6.4.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/uid.git", + "reference": "a66efcb71d8bc3a207d9d78e0bd67f3321510355" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/uid/zipball/a66efcb71d8bc3a207d9d78e0bd67f3321510355", + "reference": "a66efcb71d8bc3a207d9d78e0bd67f3321510355", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/polyfill-uuid": "^1.15" + }, + "require-dev": { + "symfony/console": "^5.4|^6.0|^7.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/v6.4.7" + }, + "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-04-18T09:22:46+00:00" + }, + { + "name": "symfony/var-dumper", + "version": "v6.4.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-dumper.git", + "reference": "7a9cd977cd1c5fed3694bee52990866432af07d7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/7a9cd977cd1c5fed3694bee52990866432af07d7", + "reference": "7a9cd977cd1c5fed3694bee52990866432af07d7", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/console": "<5.4" + }, + "require-dev": { + "ext-iconv": "*", + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/error-handler": "^6.3|^7.0", + "symfony/http-kernel": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/uid": "^5.4|^6.0|^7.0", + "twig/twig": "^2.13|^3.0.4" + }, + "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/v6.4.7" + }, + "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-04-18T09:22:46+00:00" + }, + { + "name": "tijsverkoyen/css-to-inline-styles", + "version": "v2.2.7", + "source": { + "type": "git", + "url": "https://github.com/tijsverkoyen/CssToInlineStyles.git", + "reference": "83ee6f38df0a63106a9e4536e3060458b74ccedb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/83ee6f38df0a63106a9e4536e3060458b74ccedb", + "reference": "83ee6f38df0a63106a9e4536e3060458b74ccedb", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "php": "^5.5 || ^7.0 || ^8.0", + "symfony/css-selector": "^2.7 || ^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0 || ^7.5 || ^8.5.21 || ^9.5.10" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.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.2.7" + }, + "time": "2023-12-08T13:03:43+00:00" + }, + { + "name": "vlucas/phpdotenv", + "version": "v5.6.0", + "source": { + "type": "git", + "url": "https://github.com/vlucas/phpdotenv.git", + "reference": "2cf9fb6054c2bb1d59d1f3817706ecdb9d2934c4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/2cf9fb6054c2bb1d59d1f3817706ecdb9d2934c4", + "reference": "2cf9fb6054c2bb1d59d1f3817706ecdb9d2934c4", + "shasum": "" + }, + "require": { + "ext-pcre": "*", + "graham-campbell/result-type": "^1.1.2", + "php": "^7.2.5 || ^8.0", + "phpoption/phpoption": "^1.9.2", + "symfony/polyfill-ctype": "^1.24", + "symfony/polyfill-mbstring": "^1.24", + "symfony/polyfill-php80": "^1.24" + }, + "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": true + }, + "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.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv", + "type": "tidelift" + } + ], + "time": "2023-11-12T22:43:29+00:00" + }, + { + "name": "voku/portable-ascii", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/voku/portable-ascii.git", + "reference": "b56450eed252f6801410d810c8e1727224ae0743" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/voku/portable-ascii/zipball/b56450eed252f6801410d810c8e1727224ae0743", + "reference": "b56450eed252f6801410d810c8e1727224ae0743", + "shasum": "" + }, + "require": { + "php": ">=7.0.0" + }, + "require-dev": { + "phpunit/phpunit": "~6.0 || ~7.0 || ~9.0" + }, + "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": "http://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.0.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": "2022-03-08T17:03:00+00:00" + }, + { + "name": "webmozart/assert", + "version": "1.11.0", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/assert.git", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "php": "^7.2 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<0.12.20", + "vimeo/psalm": "<4.6.1 || 4.6.2" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.13" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.11.0" + }, + "time": "2022-06-03T18:03:27+00:00" + } + ], + "packages-dev": [ + { + "name": "fakerphp/faker", + "version": "v1.23.1", + "source": { + "type": "git", + "url": "https://github.com/FakerPHP/Faker.git", + "reference": "bfb4fe148adbf78eff521199619b93a52ae3554b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/bfb4fe148adbf78eff521199619b93a52ae3554b", + "reference": "bfb4fe148adbf78eff521199619b93a52ae3554b", + "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.23.1" + }, + "time": "2024-01-02T13:46:09+00:00" + }, + { + "name": "filp/whoops", + "version": "2.15.4", + "source": { + "type": "git", + "url": "https://github.com/filp/whoops.git", + "reference": "a139776fa3f5985a50b509f2a02ff0f709d2a546" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/filp/whoops/zipball/a139776fa3f5985a50b509f2a02ff0f709d2a546", + "reference": "a139776fa3f5985a50b509f2a02ff0f709d2a546", + "shasum": "" + }, + "require": { + "php": "^5.5.9 || ^7.0 || ^8.0", + "psr/log": "^1.0.1 || ^2.0 || ^3.0" + }, + "require-dev": { + "mockery/mockery": "^0.9 || ^1.0", + "phpunit/phpunit": "^4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.3", + "symfony/var-dumper": "^2.6 || ^3.0 || ^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.15.4" + }, + "funding": [ + { + "url": "https://github.com/denis-sokolov", + "type": "github" + } + ], + "time": "2023-11-03T12:00:00+00:00" + }, + { + "name": "hamcrest/hamcrest-php", + "version": "v2.0.1", + "source": { + "type": "git", + "url": "https://github.com/hamcrest/hamcrest-php.git", + "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/8c3d0a3f6af734494ad8f6fbbee0ba92422859f3", + "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3", + "shasum": "" + }, + "require": { + "php": "^5.3|^7.0|^8.0" + }, + "replace": { + "cordoval/hamcrest-php": "*", + "davedevelopment/hamcrest-php": "*", + "kodova/hamcrest-php": "*" + }, + "require-dev": { + "phpunit/php-file-iterator": "^1.4 || ^2.0", + "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.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.0.1" + }, + "time": "2020-07-09T08:09:16+00:00" + }, + { + "name": "laravel/pint", + "version": "v1.15.3", + "source": { + "type": "git", + "url": "https://github.com/laravel/pint.git", + "reference": "3600b5d17aff52f6100ea4921849deacbbeb8656" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/pint/zipball/3600b5d17aff52f6100ea4921849deacbbeb8656", + "reference": "3600b5d17aff52f6100ea4921849deacbbeb8656", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-mbstring": "*", + "ext-tokenizer": "*", + "ext-xml": "*", + "php": "^8.1.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.54.0", + "illuminate/view": "^10.48.8", + "larastan/larastan": "^2.9.5", + "laravel-zero/framework": "^10.3.0", + "mockery/mockery": "^1.6.11", + "nunomaduro/termwind": "^1.15.1", + "pestphp/pest": "^2.34.7" + }, + "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": [ + "format", + "formatter", + "lint", + "linter", + "php" + ], + "support": { + "issues": "https://github.com/laravel/pint/issues", + "source": "https://github.com/laravel/pint" + }, + "time": "2024-04-30T15:02:26+00:00" + }, + { + "name": "laravel/sail", + "version": "v1.29.1", + "source": { + "type": "git", + "url": "https://github.com/laravel/sail.git", + "reference": "8be4a31150eab3b46af11a2e7b2c4632eefaad7e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/sail/zipball/8be4a31150eab3b46af11a2e7b2c4632eefaad7e", + "reference": "8be4a31150eab3b46af11a2e7b2c4632eefaad7e", + "shasum": "" + }, + "require": { + "illuminate/console": "^9.52.16|^10.0|^11.0", + "illuminate/contracts": "^9.52.16|^10.0|^11.0", + "illuminate/support": "^9.52.16|^10.0|^11.0", + "php": "^8.0", + "symfony/console": "^6.0|^7.0", + "symfony/yaml": "^6.0|^7.0" + }, + "require-dev": { + "orchestra/testbench": "^7.0|^8.0|^9.0", + "phpstan/phpstan": "^1.10" + }, + "bin": [ + "bin/sail" + ], + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Laravel\\Sail\\SailServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Sail\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Docker files for running a basic Laravel application.", + "keywords": [ + "docker", + "laravel" + ], + "support": { + "issues": "https://github.com/laravel/sail/issues", + "source": "https://github.com/laravel/sail" + }, + "time": "2024-03-20T20:09:31+00:00" + }, + { + "name": "mockery/mockery", + "version": "1.6.11", + "source": { + "type": "git", + "url": "https://github.com/mockery/mockery.git", + "reference": "81a161d0b135df89951abd52296adf97deb0723d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mockery/mockery/zipball/81a161d0b135df89951abd52296adf97deb0723d", + "reference": "81a161d0b135df89951abd52296adf97deb0723d", + "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-03-21T18:34:15+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.11.1", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "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", + "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.11.1" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2023-03-08T13:26:56+00:00" + }, + { + "name": "nunomaduro/collision", + "version": "v7.10.0", + "source": { + "type": "git", + "url": "https://github.com/nunomaduro/collision.git", + "reference": "49ec67fa7b002712da8526678abd651c09f375b2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nunomaduro/collision/zipball/49ec67fa7b002712da8526678abd651c09f375b2", + "reference": "49ec67fa7b002712da8526678abd651c09f375b2", + "shasum": "" + }, + "require": { + "filp/whoops": "^2.15.3", + "nunomaduro/termwind": "^1.15.1", + "php": "^8.1.0", + "symfony/console": "^6.3.4" + }, + "conflict": { + "laravel/framework": ">=11.0.0" + }, + "require-dev": { + "brianium/paratest": "^7.3.0", + "laravel/framework": "^10.28.0", + "laravel/pint": "^1.13.3", + "laravel/sail": "^1.25.0", + "laravel/sanctum": "^3.3.1", + "laravel/tinker": "^2.8.2", + "nunomaduro/larastan": "^2.6.4", + "orchestra/testbench-core": "^8.13.0", + "pestphp/pest": "^2.23.2", + "phpunit/phpunit": "^10.4.1", + "sebastian/environment": "^6.0.1", + "spatie/laravel-ignition": "^2.3.1" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider" + ] + } + }, + "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", + "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": "2023-10-11T15:45:01+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": "10.1.14", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "e3f51450ebffe8e0efdf7346ae966a656f7d5e5b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/e3f51450ebffe8e0efdf7346ae966a656f7d5e5b", + "reference": "e3f51450ebffe8e0efdf7346ae966a656f7d5e5b", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=8.1", + "phpunit/php-file-iterator": "^4.0", + "phpunit/php-text-template": "^3.0", + "sebastian/code-unit-reverse-lookup": "^3.0", + "sebastian/complexity": "^3.0", + "sebastian/environment": "^6.0", + "sebastian/lines-of-code": "^2.0", + "sebastian/version": "^4.0", + "theseer/tokenizer": "^1.2.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.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": "10.1-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/10.1.14" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-12T15:33:41+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "4.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/a95037b6d9e608ba092da1b23931e537cadc3c3c", + "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.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": "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/4.1.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-08-31T06:24:48+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "4.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", + "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^10.0" + }, + "suggest": { + "ext-pcntl": "*" + }, + "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": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/4.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:56:09+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/0c7b06ff49e3d5072f057eb1fa59258bf287a748", + "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.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/3.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-08-31T14:07:24+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "6.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/e2a2d67966e740530f4a3343fe2e030ffdc1161d", + "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.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": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/6.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:57:52+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "10.5.20", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "547d314dc24ec1e177720d45c6263fb226cc2ae3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/547d314dc24ec1e177720d45c6263fb226cc2ae3", + "reference": "547d314dc24ec1e177720d45c6263fb226cc2ae3", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.10.1", + "phar-io/manifest": "^2.0.3", + "phar-io/version": "^3.0.2", + "php": ">=8.1", + "phpunit/php-code-coverage": "^10.1.5", + "phpunit/php-file-iterator": "^4.0", + "phpunit/php-invoker": "^4.0", + "phpunit/php-text-template": "^3.0", + "phpunit/php-timer": "^6.0", + "sebastian/cli-parser": "^2.0", + "sebastian/code-unit": "^2.0", + "sebastian/comparator": "^5.0", + "sebastian/diff": "^5.0", + "sebastian/environment": "^6.0", + "sebastian/exporter": "^5.1", + "sebastian/global-state": "^6.0.1", + "sebastian/object-enumerator": "^5.0", + "sebastian/recursion-context": "^5.0", + "sebastian/type": "^4.0", + "sebastian/version": "^4.0" + }, + "suggest": { + "ext-soap": "To be able to generate mocks based on WSDL files" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "10.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/10.5.20" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsors.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "type": "tidelift" + } + ], + "time": "2024-04-24T06:32:35+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/c34583b87e7b7a8055bf6c450c2c77ce32a24084", + "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.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 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/2.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T07:12:49+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "a81fee9eef0b7a76af11d121767abc44c104e503" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/a81fee9eef0b7a76af11d121767abc44c104e503", + "reference": "a81fee9eef0b7a76af11d121767abc44c104e503", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.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 PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/2.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:58:43+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", + "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/3.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:59:15+00:00" + }, + { + "name": "sebastian/comparator", + "version": "5.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "2db5010a484d53ebf536087a70b4a5423c102372" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2db5010a484d53ebf536087a70b4a5423c102372", + "reference": "2db5010a484d53ebf536087a70b4a5423c102372", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-mbstring": "*", + "php": ">=8.1", + "sebastian/diff": "^5.0", + "sebastian/exporter": "^5.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.3" + }, + "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" + }, + { + "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/5.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-08-14T13:18:12+00:00" + }, + { + "name": "sebastian/complexity", + "version": "3.2.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "68ff824baeae169ec9f2137158ee529584553799" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68ff824baeae169ec9f2137158ee529584553799", + "reference": "68ff824baeae169ec9f2137158ee529584553799", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.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 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/3.2.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-12-21T08:37:17+00:00" + }, + { + "name": "sebastian/diff", + "version": "5.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/c41e007b4b62af48218231d6c2275e4c9b975b2e", + "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0", + "symfony/process": "^6.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.1-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/5.1.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T07:15:17+00:00" + }, + { + "name": "sebastian/environment", + "version": "6.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "8074dbcd93529b357029f5cc5058fd3e43666984" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/8074dbcd93529b357029f5cc5058fd3e43666984", + "reference": "8074dbcd93529b357029f5cc5058fd3e43666984", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.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/6.1.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-23T08:47:14+00:00" + }, + { + "name": "sebastian/exporter", + "version": "5.1.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "955288482d97c19a372d3f31006ab3f37da47adf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/955288482d97c19a372d3f31006ab3f37da47adf", + "reference": "955288482d97c19a372d3f31006ab3f37da47adf", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=8.1", + "sebastian/recursion-context": "^5.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.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": "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/5.1.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T07:17:12+00:00" + }, + { + "name": "sebastian/global-state", + "version": "6.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", + "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "sebastian/object-reflector": "^3.0", + "sebastian/recursion-context": "^5.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^10.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" + } + ], + "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/6.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T07:19:19+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/856e7f6a75a84e339195d48c556f23be2ebf75d0", + "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.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/2.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-12-21T08:38:20+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/202d0e344a580d7f7d04b3fafce6933e59dae906", + "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "sebastian/object-reflector": "^3.0", + "sebastian/recursion-context": "^5.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.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": "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", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:08:32+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "24ed13d98130f0e7122df55d06c5c4942a577957" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/24ed13d98130f0e7122df55d06c5c4942a577957", + "reference": "24ed13d98130f0e7122df55d06c5c4942a577957", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.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", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/3.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:06:18+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "05909fb5bc7df4c52992396d0116aed689f93712" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/05909fb5bc7df4c52992396d0116aed689f93712", + "reference": "05909fb5bc7df4c52992396d0116aed689f93712", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.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" + }, + { + "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", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:05:40+00:00" + }, + { + "name": "sebastian/type", + "version": "4.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "462699a16464c3944eefc02ebdd77882bd3925bf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/462699a16464c3944eefc02ebdd77882bd3925bf", + "reference": "462699a16464c3944eefc02ebdd77882bd3925bf", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.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": "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", + "source": "https://github.com/sebastianbergmann/type/tree/4.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:10:45+00:00" + }, + { + "name": "sebastian/version", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c51fa83a5d8f43f1402e3f32a005e6262244ef17", + "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "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 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", + "source": "https://github.com/sebastianbergmann/version/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-07T11:34:05+00:00" + }, + { + "name": "spatie/backtrace", + "version": "1.6.1", + "source": { + "type": "git", + "url": "https://github.com/spatie/backtrace.git", + "reference": "8373b9d51638292e3bfd736a9c19a654111b4a23" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/backtrace/zipball/8373b9d51638292e3bfd736a9c19a654111b4a23", + "reference": "8373b9d51638292e3bfd736a9c19a654111b4a23", + "shasum": "" + }, + "require": { + "php": "^7.3|^8.0" + }, + "require-dev": { + "ext-json": "*", + "laravel/serializable-closure": "^1.3", + "phpunit/phpunit": "^9.3", + "spatie/phpunit-snapshot-assertions": "^4.2", + "symfony/var-dumper": "^5.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Spatie\\Backtrace\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van de Herten", + "email": "freek@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" + } + ], + "description": "A better backtrace", + "homepage": "https://github.com/spatie/backtrace", + "keywords": [ + "Backtrace", + "spatie" + ], + "support": { + "source": "https://github.com/spatie/backtrace/tree/1.6.1" + }, + "funding": [ + { + "url": "https://github.com/sponsors/spatie", + "type": "github" + }, + { + "url": "https://spatie.be/open-source/support-us", + "type": "other" + } + ], + "time": "2024-04-24T13:22:11+00:00" + }, + { + "name": "spatie/flare-client-php", + "version": "1.5.1", + "source": { + "type": "git", + "url": "https://github.com/spatie/flare-client-php.git", + "reference": "e27977d534eefe04c154c6fd8460217024054c05" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/flare-client-php/zipball/e27977d534eefe04c154c6fd8460217024054c05", + "reference": "e27977d534eefe04c154c6fd8460217024054c05", + "shasum": "" + }, + "require": { + "illuminate/pipeline": "^8.0|^9.0|^10.0|^11.0", + "php": "^8.0", + "spatie/backtrace": "^1.5.2", + "symfony/http-foundation": "^5.2|^6.0|^7.0", + "symfony/mime": "^5.2|^6.0|^7.0", + "symfony/process": "^5.2|^6.0|^7.0", + "symfony/var-dumper": "^5.2|^6.0|^7.0" + }, + "require-dev": { + "dms/phpunit-arraysubset-asserts": "^0.5.0", + "pestphp/pest": "^1.20|^2.0", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpstan/phpstan-phpunit": "^1.0", + "spatie/phpunit-snapshot-assertions": "^4.0|^5.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.3.x-dev" + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Spatie\\FlareClient\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Send PHP errors to Flare", + "homepage": "https://github.com/spatie/flare-client-php", + "keywords": [ + "exception", + "flare", + "reporting", + "spatie" + ], + "support": { + "issues": "https://github.com/spatie/flare-client-php/issues", + "source": "https://github.com/spatie/flare-client-php/tree/1.5.1" + }, + "funding": [ + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2024-05-03T15:43:14+00:00" + }, + { + "name": "spatie/ignition", + "version": "1.14.1", + "source": { + "type": "git", + "url": "https://github.com/spatie/ignition.git", + "reference": "c23cc018c5f423d2f413b99f84655fceb6549811" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/ignition/zipball/c23cc018c5f423d2f413b99f84655fceb6549811", + "reference": "c23cc018c5f423d2f413b99f84655fceb6549811", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-mbstring": "*", + "php": "^8.0", + "spatie/backtrace": "^1.5.3", + "spatie/flare-client-php": "^1.4.0", + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/var-dumper": "^5.4|^6.0|^7.0" + }, + "require-dev": { + "illuminate/cache": "^9.52|^10.0|^11.0", + "mockery/mockery": "^1.4", + "pestphp/pest": "^1.20|^2.0", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpstan/phpstan-phpunit": "^1.0", + "psr/simple-cache-implementation": "*", + "symfony/cache": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", + "vlucas/phpdotenv": "^5.5" + }, + "suggest": { + "openai-php/client": "Require get solutions from OpenAI", + "simple-cache-implementation": "To cache solutions from OpenAI" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.5.x-dev" + } + }, + "autoload": { + "psr-4": { + "Spatie\\Ignition\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Spatie", + "email": "info@spatie.be", + "role": "Developer" + } + ], + "description": "A beautiful error page for PHP applications.", + "homepage": "https://flareapp.io/ignition", + "keywords": [ + "error", + "flare", + "laravel", + "page" + ], + "support": { + "docs": "https://flareapp.io/docs/ignition-for-laravel/introduction", + "forum": "https://twitter.com/flareappio", + "issues": "https://github.com/spatie/ignition/issues", + "source": "https://github.com/spatie/ignition" + }, + "funding": [ + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2024-05-03T15:56:16+00:00" + }, + { + "name": "spatie/laravel-ignition", + "version": "2.7.0", + "source": { + "type": "git", + "url": "https://github.com/spatie/laravel-ignition.git", + "reference": "f52124d50122611e8a40f628cef5c19ff6cc5b57" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/laravel-ignition/zipball/f52124d50122611e8a40f628cef5c19ff6cc5b57", + "reference": "f52124d50122611e8a40f628cef5c19ff6cc5b57", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-json": "*", + "ext-mbstring": "*", + "illuminate/support": "^10.0|^11.0", + "php": "^8.1", + "spatie/flare-client-php": "^1.5", + "spatie/ignition": "^1.14", + "symfony/console": "^6.2.3|^7.0", + "symfony/var-dumper": "^6.2.3|^7.0" + }, + "require-dev": { + "livewire/livewire": "^2.11|^3.3.5", + "mockery/mockery": "^1.5.1", + "openai-php/client": "^0.8.1", + "orchestra/testbench": "8.22.3|^9.0", + "pestphp/pest": "^2.34", + "phpstan/extension-installer": "^1.3.1", + "phpstan/phpstan-deprecation-rules": "^1.1.1", + "phpstan/phpstan-phpunit": "^1.3.16", + "vlucas/phpdotenv": "^5.5" + }, + "suggest": { + "openai-php/client": "Require get solutions from OpenAI", + "psr/simple-cache-implementation": "Needed to cache solutions from OpenAI" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Spatie\\LaravelIgnition\\IgnitionServiceProvider" + ], + "aliases": { + "Flare": "Spatie\\LaravelIgnition\\Facades\\Flare" + } + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Spatie\\LaravelIgnition\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Spatie", + "email": "info@spatie.be", + "role": "Developer" + } + ], + "description": "A beautiful error page for Laravel applications.", + "homepage": "https://flareapp.io/ignition", + "keywords": [ + "error", + "flare", + "laravel", + "page" + ], + "support": { + "docs": "https://flareapp.io/docs/ignition-for-laravel/introduction", + "forum": "https://twitter.com/flareappio", + "issues": "https://github.com/spatie/laravel-ignition/issues", + "source": "https://github.com/spatie/laravel-ignition" + }, + "funding": [ + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2024-05-02T13:42:49+00:00" + }, + { + "name": "symfony/yaml", + "version": "v6.4.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "53e8b1ef30a65f78eac60fddc5ee7ebbbdb1dee0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/53e8b1ef30a65f78eac60fddc5ee7ebbbdb1dee0", + "reference": "53e8b1ef30a65f78eac60fddc5ee7ebbbdb1dee0", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "symfony/console": "<5.4" + }, + "require-dev": { + "symfony/console": "^5.4|^6.0|^7.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/v6.4.7" + }, + "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-04-28T10:28:08+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.2.3", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "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" + } + ], + "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/1.2.3" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:36:25+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": true, + "prefer-lowest": false, + "platform": { + "php": "^8.1" + }, + "platform-dev": [], + "plugin-api-version": "2.6.0" +} diff --git a/config/app.php b/config/app.php new file mode 100644 index 0000000..74b5766 --- /dev/null +++ b/config/app.php @@ -0,0 +1,189 @@ + 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 + | your application so that it is used when running Artisan tasks. + | + */ + + 'url' => env('APP_URL', 'http://localhost'), + + 'asset_url' => env('ASSET_URL'), + + /* + |-------------------------------------------------------------------------- + | Application Timezone + |-------------------------------------------------------------------------- + | + | Here you may specify the default timezone for your application, which + | will be used by the PHP date and date-time functions. We have gone + | ahead and set this to a sensible default for you out of the box. + | + */ + + 'timezone' => 'UTC', + + /* + |-------------------------------------------------------------------------- + | Application Locale Configuration + |-------------------------------------------------------------------------- + | + | The application locale determines the default locale that will be used + | by the translation service provider. You are free to set this value + | to any of the locales which will be supported by the application. + | + */ + + 'locale' => 'en', + + /* + |-------------------------------------------------------------------------- + | Application Fallback Locale + |-------------------------------------------------------------------------- + | + | The fallback locale determines the locale to use when the current one + | is not available. You may change the value to correspond to any of + | the language folders that are provided through your application. + | + */ + + 'fallback_locale' => 'en', + + /* + |-------------------------------------------------------------------------- + | Faker Locale + |-------------------------------------------------------------------------- + | + | This locale will be used by the Faker PHP library when generating fake + | data for your database seeds. For example, this will be used to get + | localized telephone numbers, street address information and more. + | + */ + + 'faker_locale' => 'en_US', + + /* + |-------------------------------------------------------------------------- + | Encryption Key + |-------------------------------------------------------------------------- + | + | This key is used by the Illuminate encrypter service and should be set + | to a random, 32 character string, otherwise these encrypted strings + | will not be safe. Please do this before deploying an application! + | + */ + + 'key' => env('APP_KEY'), + + 'cipher' => 'AES-256-CBC', + + /* + |-------------------------------------------------------------------------- + | 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' => 'file', + // 'store' => 'redis', + ], + + /* + |-------------------------------------------------------------------------- + | Autoloaded Service Providers + |-------------------------------------------------------------------------- + | + | The service providers listed here will be automatically loaded on the + | request to your application. Feel free to add your own services to + | this array to grant expanded functionality to your applications. + | + */ + + 'providers' => ServiceProvider::defaultProviders()->merge([ + /* + * Package Service Providers... + */ + + /* + * Application Service Providers... + */ + App\Providers\AppServiceProvider::class, + App\Providers\AuthServiceProvider::class, + // App\Providers\BroadcastServiceProvider::class, + App\Providers\EventServiceProvider::class, + App\Providers\RouteServiceProvider::class, + App\Providers\MenuServiceProvider::class, + ])->toArray(), + + /* + |-------------------------------------------------------------------------- + | Class Aliases + |-------------------------------------------------------------------------- + | + | This array of class aliases will be registered when this application + | is started. However, feel free to register as many as you wish as + | the aliases are "lazy" loaded so they don't hinder performance. + | + */ + + 'aliases' => Facade::defaultAliases()->merge([ + // 'Example' => App\Facades\Example::class, + ])->toArray(), + +]; diff --git a/config/auth.php b/config/auth.php new file mode 100644 index 0000000..9548c15 --- /dev/null +++ b/config/auth.php @@ -0,0 +1,115 @@ + [ + 'guard' => 'web', + 'passwords' => 'users', + ], + + /* + |-------------------------------------------------------------------------- + | Authentication Guards + |-------------------------------------------------------------------------- + | + | Next, you may define every authentication guard for your application. + | Of course, a great default configuration has been defined for you + | here which uses session storage and the Eloquent user provider. + | + | All authentication drivers have a user provider. This defines how the + | users are actually retrieved out of your database or other storage + | mechanisms used by this application to persist your user's data. + | + | Supported: "session" + | + */ + + 'guards' => [ + 'web' => [ + 'driver' => 'session', + 'provider' => 'users', + ], + ], + + /* + |-------------------------------------------------------------------------- + | User Providers + |-------------------------------------------------------------------------- + | + | All authentication drivers have a user provider. This defines how the + | users are actually retrieved out of your database or other storage + | mechanisms used by this application to persist your user's data. + | + | If you have multiple user tables or models you may configure multiple + | sources which represent each model / table. These sources may then + | be assigned to any extra authentication guards you have defined. + | + | Supported: "database", "eloquent" + | + */ + + 'providers' => [ + 'users' => [ + 'driver' => 'eloquent', + 'model' => App\Models\User::class, + ], + + // 'users' => [ + // 'driver' => 'database', + // 'table' => 'users', + // ], + ], + + /* + |-------------------------------------------------------------------------- + | Resetting Passwords + |-------------------------------------------------------------------------- + | + | You may specify multiple password reset configurations if you have more + | than one user table or model in the application and you want to have + | separate password reset settings based on the specific user types. + | + | 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' => 'password_reset_tokens', + 'expire' => 60, + 'throttle' => 60, + ], + ], + + /* + |-------------------------------------------------------------------------- + | Password Confirmation Timeout + |-------------------------------------------------------------------------- + | + | Here you may define the amount of seconds before a password confirmation + | times out and the user is prompted to re-enter their password via the + | confirmation screen. By default, the timeout lasts for three hours. + | + */ + + 'password_timeout' => 10800, + +]; diff --git a/config/broadcasting.php b/config/broadcasting.php new file mode 100644 index 0000000..2410485 --- /dev/null +++ b/config/broadcasting.php @@ -0,0 +1,71 @@ + env('BROADCAST_DRIVER', 'null'), + + /* + |-------------------------------------------------------------------------- + | Broadcast Connections + |-------------------------------------------------------------------------- + | + | Here you may define all of the broadcast connections that will be used + | to broadcast events to other systems or over websockets. Samples of + | each available type of connection are provided inside this array. + | + */ + + 'connections' => [ + + 'pusher' => [ + 'driver' => 'pusher', + 'key' => env('PUSHER_APP_KEY'), + 'secret' => env('PUSHER_APP_SECRET'), + 'app_id' => env('PUSHER_APP_ID'), + 'options' => [ + 'cluster' => env('PUSHER_APP_CLUSTER'), + 'host' => env('PUSHER_HOST') ?: 'api-'.env('PUSHER_APP_CLUSTER', 'mt1').'.pusher.com', + 'port' => env('PUSHER_PORT', 443), + 'scheme' => env('PUSHER_SCHEME', 'https'), + 'encrypted' => true, + 'useTLS' => env('PUSHER_SCHEME', 'https') === 'https', + ], + 'client_options' => [ + // Guzzle client options: https://docs.guzzlephp.org/en/stable/request-options.html + ], + ], + + 'ably' => [ + 'driver' => 'ably', + 'key' => env('ABLY_KEY'), + ], + + 'redis' => [ + 'driver' => 'redis', + 'connection' => 'default', + ], + + 'log' => [ + 'driver' => 'log', + ], + + 'null' => [ + 'driver' => 'null', + ], + + ], + +]; diff --git a/config/cache.php b/config/cache.php new file mode 100644 index 0000000..d4171e2 --- /dev/null +++ b/config/cache.php @@ -0,0 +1,111 @@ + env('CACHE_DRIVER', 'file'), + + /* + |-------------------------------------------------------------------------- + | 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: "apc", "array", "database", "file", + | "memcached", "redis", "dynamodb", "octane", "null" + | + */ + + 'stores' => [ + + 'apc' => [ + 'driver' => 'apc', + ], + + 'array' => [ + 'driver' => 'array', + 'serialize' => false, + ], + + 'database' => [ + 'driver' => 'database', + 'table' => 'cache', + 'connection' => null, + 'lock_connection' => null, + ], + + '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' => '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', + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Cache Key Prefix + |-------------------------------------------------------------------------- + | + | When utilizing the APC, database, memcached, Redis, or 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(env('APP_NAME', 'laravel'), '_').'_cache_'), + +]; diff --git a/config/cors.php b/config/cors.php new file mode 100644 index 0000000..8a39e6d --- /dev/null +++ b/config/cors.php @@ -0,0 +1,34 @@ + ['api/*', 'sanctum/csrf-cookie'], + + 'allowed_methods' => ['*'], + + 'allowed_origins' => ['*'], + + 'allowed_origins_patterns' => [], + + 'allowed_headers' => ['*'], + + 'exposed_headers' => [], + + 'max_age' => 0, + + 'supports_credentials' => false, + +]; diff --git a/config/database.php b/config/database.php new file mode 100644 index 0000000..137ad18 --- /dev/null +++ b/config/database.php @@ -0,0 +1,151 @@ + env('DB_CONNECTION', 'mysql'), + + /* + |-------------------------------------------------------------------------- + | Database Connections + |-------------------------------------------------------------------------- + | + | Here are each of the database connections setup for your application. + | Of course, examples of configuring each database platform that is + | supported by Laravel is shown below to make development simple. + | + | + | All database work in Laravel is done through the PHP PDO facilities + | so make sure you have the driver for your particular database of + | choice installed on your machine before you begin development. + | + */ + + 'connections' => [ + + 'sqlite' => [ + 'driver' => 'sqlite', + 'url' => env('DATABASE_URL'), + 'database' => env('DB_DATABASE', database_path('database.sqlite')), + 'prefix' => '', + 'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true), + ], + + 'mysql' => [ + 'driver' => 'mysql', + 'url' => env('DATABASE_URL'), + 'host' => env('DB_HOST', '127.0.0.1'), + 'port' => env('DB_PORT', '3306'), + 'database' => env('DB_DATABASE', 'forge'), + 'username' => env('DB_USERNAME', 'forge'), + 'password' => env('DB_PASSWORD', ''), + 'unix_socket' => env('DB_SOCKET', ''), + 'charset' => 'utf8mb4', + 'collation' => 'utf8mb4_unicode_ci', + 'prefix' => '', + 'prefix_indexes' => true, + 'strict' => true, + 'engine' => null, + 'options' => extension_loaded('pdo_mysql') ? array_filter([ + PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'), + ]) : [], + ], + + 'pgsql' => [ + 'driver' => 'pgsql', + 'url' => env('DATABASE_URL'), + 'host' => env('DB_HOST', '127.0.0.1'), + 'port' => env('DB_PORT', '5432'), + 'database' => env('DB_DATABASE', 'forge'), + 'username' => env('DB_USERNAME', 'forge'), + 'password' => env('DB_PASSWORD', ''), + 'charset' => 'utf8', + 'prefix' => '', + 'prefix_indexes' => true, + 'search_path' => 'public', + 'sslmode' => 'prefer', + ], + + 'sqlsrv' => [ + 'driver' => 'sqlsrv', + 'url' => env('DATABASE_URL'), + 'host' => env('DB_HOST', 'localhost'), + 'port' => env('DB_PORT', '1433'), + 'database' => env('DB_DATABASE', 'forge'), + 'username' => env('DB_USERNAME', 'forge'), + 'password' => env('DB_PASSWORD', ''), + '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 in the database. + | + */ + + 'migrations' => 'migrations', + + /* + |-------------------------------------------------------------------------- + | 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 APC or Memcached. Laravel makes it easy to dig right in. + | + */ + + 'redis' => [ + + 'client' => env('REDIS_CLIENT', 'phpredis'), + + 'options' => [ + 'cluster' => env('REDIS_CLUSTER', 'redis'), + 'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'), + ], + + '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'), + ], + + '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'), + ], + + ], + +]; diff --git a/config/filesystems.php b/config/filesystems.php new file mode 100644 index 0000000..e9d9dbd --- /dev/null +++ b/config/filesystems.php @@ -0,0 +1,76 @@ + env('FILESYSTEM_DISK', 'local'), + + /* + |-------------------------------------------------------------------------- + | Filesystem Disks + |-------------------------------------------------------------------------- + | + | Here you may configure as many filesystem "disks" as you wish, and you + | may even configure multiple disks of the same driver. Defaults have + | been set up for each driver as an example of the required values. + | + | Supported Drivers: "local", "ftp", "sftp", "s3" + | + */ + + 'disks' => [ + + 'local' => [ + 'driver' => 'local', + 'root' => storage_path('app'), + 'throw' => false, + ], + + 'public' => [ + 'driver' => 'local', + 'root' => storage_path('app/public'), + 'url' => env('APP_URL').'/storage', + 'visibility' => 'public', + 'throw' => 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, + ], + + ], + + /* + |-------------------------------------------------------------------------- + | 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'), + ], + +]; diff --git a/config/hashing.php b/config/hashing.php new file mode 100644 index 0000000..0e8a0bb --- /dev/null +++ b/config/hashing.php @@ -0,0 +1,54 @@ + 'bcrypt', + + /* + |-------------------------------------------------------------------------- + | Bcrypt Options + |-------------------------------------------------------------------------- + | + | Here you may specify the configuration options that should be used when + | passwords are hashed using the Bcrypt algorithm. This will allow you + | to control the amount of time it takes to hash the given password. + | + */ + + 'bcrypt' => [ + 'rounds' => env('BCRYPT_ROUNDS', 12), + 'verify' => true, + ], + + /* + |-------------------------------------------------------------------------- + | Argon Options + |-------------------------------------------------------------------------- + | + | Here you may specify the configuration options that should be used when + | passwords are hashed using the Argon algorithm. These will allow you + | to control the amount of time it takes to hash the given password. + | + */ + + 'argon' => [ + 'memory' => 65536, + 'threads' => 1, + 'time' => 4, + 'verify' => true, + ], + +]; diff --git a/config/logging.php b/config/logging.php new file mode 100644 index 0000000..c44d276 --- /dev/null +++ b/config/logging.php @@ -0,0 +1,131 @@ + 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' => false, + ], + + /* + |-------------------------------------------------------------------------- + | Log Channels + |-------------------------------------------------------------------------- + | + | Here you may configure the log channels for your application. Out of + | the box, Laravel uses the Monolog PHP logging library. This gives + | you a variety of powerful log handlers / formatters to utilize. + | + | Available Drivers: "single", "daily", "slack", "syslog", + | "errorlog", "monolog", + | "custom", "stack" + | + */ + + 'channels' => [ + 'stack' => [ + 'driver' => 'stack', + 'channels' => ['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' => 14, + 'replace_placeholders' => true, + ], + + 'slack' => [ + 'driver' => 'slack', + 'url' => env('LOG_SLACK_WEBHOOK_URL'), + 'username' => 'Laravel Log', + '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, + 'formatter' => env('LOG_STDERR_FORMATTER'), + 'with' => [ + 'stream' => 'php://stderr', + ], + 'processors' => [PsrLogMessageProcessor::class], + ], + + 'syslog' => [ + 'driver' => 'syslog', + 'level' => env('LOG_LEVEL', 'debug'), + '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'), + ], + ], + +]; diff --git a/config/mail.php b/config/mail.php new file mode 100644 index 0000000..e894b2e --- /dev/null +++ b/config/mail.php @@ -0,0 +1,134 @@ + env('MAIL_MAILER', 'smtp'), + + /* + |-------------------------------------------------------------------------- + | 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 to be used while + | sending an e-mail. You will specify which one you are using for your + | mailers below. You are free to add additional mailers as required. + | + | Supported: "smtp", "sendmail", "mailgun", "ses", "ses-v2", + | "postmark", "log", "array", "failover", "roundrobin" + | + */ + + 'mailers' => [ + 'smtp' => [ + 'transport' => 'smtp', + 'url' => env('MAIL_URL'), + 'host' => env('MAIL_HOST', 'smtp.mailgun.org'), + 'port' => env('MAIL_PORT', 587), + 'encryption' => env('MAIL_ENCRYPTION', 'tls'), + 'username' => env('MAIL_USERNAME'), + 'password' => env('MAIL_PASSWORD'), + 'timeout' => null, + 'local_domain' => env('MAIL_EHLO_DOMAIN'), + ], + + 'ses' => [ + 'transport' => 'ses', + ], + + 'postmark' => [ + 'transport' => 'postmark', + // 'message_stream_id' => null, + // 'client' => [ + // 'timeout' => 5, + // ], + ], + + 'mailgun' => [ + 'transport' => 'mailgun', + // 'client' => [ + // 'timeout' => 5, + // ], + ], + + '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', + ], + ], + + 'roundrobin' => [ + 'transport' => 'roundrobin', + 'mailers' => [ + 'ses', + 'postmark', + ], + ], + ], + + /* + |-------------------------------------------------------------------------- + | Global "From" Address + |-------------------------------------------------------------------------- + | + | You may wish for all e-mails 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 e-mails that are sent by your application. + | + */ + + 'from' => [ + 'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'), + 'name' => env('MAIL_FROM_NAME', 'Example'), + ], + + /* + |-------------------------------------------------------------------------- + | Markdown Mail Settings + |-------------------------------------------------------------------------- + | + | If you are using Markdown based email rendering, you may configure your + | theme and component paths here, allowing you to customize the design + | of the emails. Or, you may simply stick with the Laravel defaults! + | + */ + + 'markdown' => [ + 'theme' => 'default', + + 'paths' => [ + resource_path('views/vendor/mail'), + ], + ], + +]; diff --git a/config/queue.php b/config/queue.php new file mode 100644 index 0000000..01c6b05 --- /dev/null +++ b/config/queue.php @@ -0,0 +1,109 @@ + env('QUEUE_CONNECTION', 'sync'), + + /* + |-------------------------------------------------------------------------- + | Queue Connections + |-------------------------------------------------------------------------- + | + | Here you may configure the connection information for each server that + | is used by your application. A default configuration has been added + | for each back-end shipped with Laravel. You are free to add more. + | + | Drivers: "sync", "database", "beanstalkd", "sqs", "redis", "null" + | + */ + + 'connections' => [ + + 'sync' => [ + 'driver' => 'sync', + ], + + 'database' => [ + 'driver' => 'database', + 'table' => 'jobs', + 'queue' => 'default', + 'retry_after' => 90, + 'after_commit' => false, + ], + + 'beanstalkd' => [ + 'driver' => 'beanstalkd', + 'host' => 'localhost', + 'queue' => 'default', + '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' => 'default', + 'queue' => env('REDIS_QUEUE', 'default'), + 'retry_after' => 90, + 'block_for' => null, + 'after_commit' => false, + ], + + ], + + /* + |-------------------------------------------------------------------------- + | 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', 'mysql'), + 'table' => 'job_batches', + ], + + /* + |-------------------------------------------------------------------------- + | Failed Queue Jobs + |-------------------------------------------------------------------------- + | + | These options configure the behavior of failed queue job logging so you + | can control which database and table are used to store the jobs that + | have failed. You may change them to any database / table you wish. + | + */ + + 'failed' => [ + 'driver' => env('QUEUE_FAILED_DRIVER', 'database-uuids'), + 'database' => env('DB_CONNECTION', 'mysql'), + 'table' => 'failed_jobs', + ], + +]; diff --git a/config/sanctum.php b/config/sanctum.php new file mode 100644 index 0000000..35d75b3 --- /dev/null +++ b/config/sanctum.php @@ -0,0 +1,83 @@ + explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf( + '%s%s', + 'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1', + Sanctum::currentApplicationUrlWithPort() + ))), + + /* + |-------------------------------------------------------------------------- + | Sanctum Guards + |-------------------------------------------------------------------------- + | + | This array contains the authentication guards that will be checked when + | Sanctum is trying to authenticate a request. If none of these guards + | are able to authenticate the request, Sanctum will use the bearer + | token that's present on an incoming request for authentication. + | + */ + + 'guard' => ['web'], + + /* + |-------------------------------------------------------------------------- + | Expiration Minutes + |-------------------------------------------------------------------------- + | + | This value controls the number of minutes until an issued token will be + | considered expired. This will override any values set in the token's + | "expires_at" attribute, but first-party sessions are not affected. + | + */ + + 'expiration' => null, + + /* + |-------------------------------------------------------------------------- + | Token Prefix + |-------------------------------------------------------------------------- + | + | Sanctum can prefix new tokens in order to take advantage of numerous + | security scanning initiatives maintained by open source platforms + | that notify developers if they commit tokens into repositories. + | + | See: https://docs.github.com/en/code-security/secret-scanning/about-secret-scanning + | + */ + + 'token_prefix' => env('SANCTUM_TOKEN_PREFIX', ''), + + /* + |-------------------------------------------------------------------------- + | Sanctum Middleware + |-------------------------------------------------------------------------- + | + | When authenticating your first-party SPA with Sanctum you may need to + | customize some of the middleware Sanctum uses while processing the + | request. You may change the middleware listed below as required. + | + */ + + 'middleware' => [ + 'authenticate_session' => Laravel\Sanctum\Http\Middleware\AuthenticateSession::class, + 'encrypt_cookies' => App\Http\Middleware\EncryptCookies::class, + 'verify_csrf_token' => App\Http\Middleware\VerifyCsrfToken::class, + ], + +]; diff --git a/config/services.php b/config/services.php new file mode 100644 index 0000000..0ace530 --- /dev/null +++ b/config/services.php @@ -0,0 +1,34 @@ + [ + 'domain' => env('MAILGUN_DOMAIN'), + 'secret' => env('MAILGUN_SECRET'), + 'endpoint' => env('MAILGUN_ENDPOINT', 'api.mailgun.net'), + 'scheme' => 'https', + ], + + 'postmark' => [ + 'token' => env('POSTMARK_TOKEN'), + ], + + 'ses' => [ + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), + ], + +]; diff --git a/config/session.php b/config/session.php new file mode 100644 index 0000000..e738cb3 --- /dev/null +++ b/config/session.php @@ -0,0 +1,214 @@ + env('SESSION_DRIVER', 'file'), + + /* + |-------------------------------------------------------------------------- + | 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 immediately expire on the browser closing, set that option. + | + */ + + 'lifetime' => env('SESSION_LIFETIME', 120), + + 'expire_on_close' => false, + + /* + |-------------------------------------------------------------------------- + | Session Encryption + |-------------------------------------------------------------------------- + | + | This option allows you to easily specify that all of your session data + | should be encrypted before it is stored. All encryption will be run + | automatically by Laravel and you can use the Session like normal. + | + */ + + 'encrypt' => false, + + /* + |-------------------------------------------------------------------------- + | Session File Location + |-------------------------------------------------------------------------- + | + | When using the native session driver, we need a location where session + | files may be stored. A default has been set for you but a different + | location may be specified. This is only needed for file sessions. + | + */ + + '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 we + | should use to manage the sessions. Of course, a sensible default is + | provided for you; however, you are free to change this as needed. + | + */ + + 'table' => 'sessions', + + /* + |-------------------------------------------------------------------------- + | Session Cache Store + |-------------------------------------------------------------------------- + | + | While using one of the framework's cache driven session backends you may + | list a cache store that should be used for these sessions. This value + | must match with one of the application's configured cache "stores". + | + | Affects: "apc", "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 cookie used to identify a session + | instance by ID. The name specified here will get used every time a + | new session cookie is created by the framework for every driver. + | + */ + + 'cookie' => env( + 'SESSION_COOKIE', + Str::slug(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 are free to change this when necessary. + | + */ + + 'path' => '/', + + /* + |-------------------------------------------------------------------------- + | Session Cookie Domain + |-------------------------------------------------------------------------- + | + | Here you may change the domain of the cookie used to identify a session + | in your application. This will determine which domains the cookie is + | available to in your application. A sensible default has been set. + | + */ + + '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. You are free to modify this option if needed. + | + */ + + '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" since this is a secure default value. + | + | Supported: "lax", "strict", "none", null + | + */ + + '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' => false, + +]; diff --git a/config/view.php b/config/view.php new file mode 100644 index 0000000..22b8a18 --- /dev/null +++ b/config/view.php @@ -0,0 +1,36 @@ + [ + resource_path('views'), + ], + + /* + |-------------------------------------------------------------------------- + | Compiled View Path + |-------------------------------------------------------------------------- + | + | This option determines where all the compiled Blade templates will be + | stored for your application. Typically, this is within the storage + | directory. However, as usual, you are free to change this value. + | + */ + + 'compiled' => env( + 'VIEW_COMPILED_PATH', + realpath(storage_path('framework/views')) + ), + +]; diff --git a/database/.gitignore b/database/.gitignore new file mode 100644 index 0000000..9b19b93 --- /dev/null +++ b/database/.gitignore @@ -0,0 +1 @@ +*.sqlite* diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php new file mode 100644 index 0000000..584104c --- /dev/null +++ b/database/factories/UserFactory.php @@ -0,0 +1,44 @@ + + */ +class UserFactory extends Factory +{ + /** + * The current password being used by the factory. + */ + protected static ?string $password; + + /** + * Define the model's default state. + * + * @return array + */ + 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, + ]); + } +} diff --git a/database/migrations/2014_10_12_000000_create_users_table.php b/database/migrations/2014_10_12_000000_create_users_table.php new file mode 100644 index 0000000..444fafb --- /dev/null +++ b/database/migrations/2014_10_12_000000_create_users_table.php @@ -0,0 +1,32 @@ +id(); + $table->string('name'); + $table->string('email')->unique(); + $table->timestamp('email_verified_at')->nullable(); + $table->string('password'); + $table->rememberToken(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('users'); + } +}; diff --git a/database/migrations/2014_10_12_100000_create_password_reset_tokens_table.php b/database/migrations/2014_10_12_100000_create_password_reset_tokens_table.php new file mode 100644 index 0000000..81a7229 --- /dev/null +++ b/database/migrations/2014_10_12_100000_create_password_reset_tokens_table.php @@ -0,0 +1,28 @@ +string('email')->primary(); + $table->string('token'); + $table->timestamp('created_at')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('password_reset_tokens'); + } +}; diff --git a/database/migrations/2019_08_19_000000_create_failed_jobs_table.php b/database/migrations/2019_08_19_000000_create_failed_jobs_table.php new file mode 100644 index 0000000..249da81 --- /dev/null +++ b/database/migrations/2019_08_19_000000_create_failed_jobs_table.php @@ -0,0 +1,32 @@ +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('failed_jobs'); + } +}; diff --git a/database/migrations/2019_12_14_000001_create_personal_access_tokens_table.php b/database/migrations/2019_12_14_000001_create_personal_access_tokens_table.php new file mode 100644 index 0000000..e828ad8 --- /dev/null +++ b/database/migrations/2019_12_14_000001_create_personal_access_tokens_table.php @@ -0,0 +1,33 @@ +id(); + $table->morphs('tokenable'); + $table->string('name'); + $table->string('token', 64)->unique(); + $table->text('abilities')->nullable(); + $table->timestamp('last_used_at')->nullable(); + $table->timestamp('expires_at')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('personal_access_tokens'); + } +}; diff --git a/database/migrations/2026_03_12_171109_create_roles_table.php b/database/migrations/2026_03_12_171109_create_roles_table.php new file mode 100644 index 0000000..35c6474 --- /dev/null +++ b/database/migrations/2026_03_12_171109_create_roles_table.php @@ -0,0 +1,29 @@ +id(); + $table->string('name')->unique(); + $table->string('slug')->unique(); + $table->string('description')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('roles'); + } +}; diff --git a/database/migrations/2026_03_12_171110_create_permissions_table.php b/database/migrations/2026_03_12_171110_create_permissions_table.php new file mode 100644 index 0000000..26c4338 --- /dev/null +++ b/database/migrations/2026_03_12_171110_create_permissions_table.php @@ -0,0 +1,30 @@ +id(); + $table->string('name')->unique(); + $table->string('slug')->unique(); + $table->string('group')->nullable(); + $table->text('description')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('permissions'); + } +}; diff --git a/database/migrations/2026_03_12_171111_create_permission_role_table.php b/database/migrations/2026_03_12_171111_create_permission_role_table.php new file mode 100644 index 0000000..b501c05 --- /dev/null +++ b/database/migrations/2026_03_12_171111_create_permission_role_table.php @@ -0,0 +1,31 @@ +id(); + $table->foreignId('permission_id')->constrained()->onDelete('cascade'); + $table->foreignId('role_id')->constrained()->onDelete('cascade'); + $table->timestamps(); + + $table->unique(['permission_id', 'role_id']); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('permission_role'); + } +}; diff --git a/database/migrations/2026_03_14_102454_create_categories_table.php b/database/migrations/2026_03_14_102454_create_categories_table.php new file mode 100644 index 0000000..76904fe --- /dev/null +++ b/database/migrations/2026_03_14_102454_create_categories_table.php @@ -0,0 +1,45 @@ +id(); + $table->string('name'); + $table->string('slug')->unique(); + $table->text('description')->nullable(); + $table->string('icon')->nullable(); + $table->string('image')->nullable(); + + $table->unsignedBigInteger('parent_id')->nullable(); + + $table->integer('sort_order')->default(0)->index(); + $table->boolean('is_active')->default(true); + + $table->index('parent_id'); + + $table->foreign('parent_id') + ->references('id') + ->on('categories') + ->onDelete('cascade'); + + $table->unique(['name', 'parent_id']); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('categories'); + } +}; diff --git a/database/migrations/2026_03_14_124100_create_brands_table.php b/database/migrations/2026_03_14_124100_create_brands_table.php new file mode 100644 index 0000000..41550bb --- /dev/null +++ b/database/migrations/2026_03_14_124100_create_brands_table.php @@ -0,0 +1,33 @@ +id(); + $table->string('name'); + $table->string('slug')->unique(); + $table->text('description')->nullable(); + $table->string('logo')->nullable(); + $table->string('website')->nullable(); + $table->string('country')->nullable(); + $table->boolean('is_active')->default(true); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('brands'); + } +}; diff --git a/database/migrations/2026_03_14_124103_create_products_table.php b/database/migrations/2026_03_14_124103_create_products_table.php new file mode 100644 index 0000000..3a45fc4 --- /dev/null +++ b/database/migrations/2026_03_14_124103_create_products_table.php @@ -0,0 +1,40 @@ +id(); + $table->string('name'); + $table->string('slug')->unique(); + $table->text('description'); + + $table->foreignId('brand_id')->nullable()->constrained()->onDelete('set null'); + $table->foreignId('category_id')->constrained()->onDelete('cascade'); + + $table->json('attributes')->nullable(); + $table->boolean('is_active')->default(true); + $table->timestamps(); + + $table->index('brand_id'); + $table->index('category_id'); + $table->index('is_active'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('products'); + } +}; diff --git a/database/migrations/2026_03_14_135714_create_product_variations_table.php b/database/migrations/2026_03_14_135714_create_product_variations_table.php new file mode 100644 index 0000000..242c627 --- /dev/null +++ b/database/migrations/2026_03_14_135714_create_product_variations_table.php @@ -0,0 +1,44 @@ +id(); + $table->foreignId('product_id')->constrained()->onDelete('cascade'); + + $table->string('name'); + $table->string('sku')->unique(); + $table->decimal('price', 10, 2); + $table->decimal('old_price', 10, 2)->nullable(); + $table->json('attributes')->nullable(); + + $table->json('images')->nullable(); + $table->integer('stock')->default(0); + + $table->boolean('is_default')->default(false); + $table->boolean('is_active')->default(true); + + $table->timestamps(); + + $table->index('sku'); + $table->index('product_id'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('product_variations'); + } +}; diff --git a/database/migrations/2026_03_14_201029_create_reviews_table.php b/database/migrations/2026_03_14_201029_create_reviews_table.php new file mode 100644 index 0000000..70a74e7 --- /dev/null +++ b/database/migrations/2026_03_14_201029_create_reviews_table.php @@ -0,0 +1,43 @@ +id(); + $table->foreignId('user_id')->constrained()->onDelete('cascade'); + $table->foreignId('product_id')->constrained()->onDelete('cascade'); + + $table->tinyInteger('rating'); + $table->string('title')->nullable(); + $table->text('comment'); + $table->text('advantages')->nullable(); + $table->text('disadvantages')->nullable(); + + $table->integer('helpful_count')->default(0); + $table->integer('unhelpful_count')->default(0); + $table->boolean('is_approved')->default(false); + $table->timestamps(); + + $table->index('rating'); + $table->index('is_approved'); + $table->index('created_at'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('reviews'); + } +}; diff --git a/database/migrations/2026_03_14_213006_create_contacts_table.php b/database/migrations/2026_03_14_213006_create_contacts_table.php new file mode 100644 index 0000000..04680b2 --- /dev/null +++ b/database/migrations/2026_03_14_213006_create_contacts_table.php @@ -0,0 +1,45 @@ +id(); + $table->string('name')->default('Хвостики и лапки'); + $table->string('description')->nullable(); + $table->string('logo')->nullable(); + $table->string('favicon')->nullable(); + + $table->string('phone')->nullable(); + $table->string('email')->nullable(); + $table->string('address')->nullable(); + $table->string('work_hours')->nullable(); + + $table->string('telegram')->nullable(); + $table->string('whatsapp')->nullable(); + $table->string('vkontakte')->nullable(); + + $table->string('meta_title')->nullable(); + $table->text('meta_description')->nullable(); + $table->text('meta_keywords')->nullable(); + + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('contacts'); + } +}; diff --git a/database/migrations/2026_03_14_213427_add_meta_fields_to_products_table.php b/database/migrations/2026_03_14_213427_add_meta_fields_to_products_table.php new file mode 100644 index 0000000..aaaa903 --- /dev/null +++ b/database/migrations/2026_03_14_213427_add_meta_fields_to_products_table.php @@ -0,0 +1,30 @@ +string('meta_title')->nullable()->after('is_active'); + $table->text('meta_description')->nullable()->after('meta_title'); + $table->string('meta_keywords')->nullable()->after('meta_description'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('products', function (Blueprint $table) { + $table->dropColumn(['meta_title', 'meta_description', 'meta_keywords']); + }); + } +}; diff --git a/database/migrations/2026_03_16_191828_add_phone_to_users_table.php b/database/migrations/2026_03_16_191828_add_phone_to_users_table.php new file mode 100644 index 0000000..abfef36 --- /dev/null +++ b/database/migrations/2026_03_16_191828_add_phone_to_users_table.php @@ -0,0 +1,30 @@ +string('phone', 20)->nullable()->after('email'); + + $table->index('phone'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn('phone'); + }); + } +}; diff --git a/database/migrations/2026_03_16_192010_add_email_index_to_users_table.php b/database/migrations/2026_03_16_192010_add_email_index_to_users_table.php new file mode 100644 index 0000000..89b0904 --- /dev/null +++ b/database/migrations/2026_03_16_192010_add_email_index_to_users_table.php @@ -0,0 +1,25 @@ +index('email'); + }); + } + + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $table->dropIndex('email'); + }); + } +}; diff --git a/database/migrations/2026_03_18_192641_add_role_id_to_users_table.php b/database/migrations/2026_03_18_192641_add_role_id_to_users_table.php new file mode 100644 index 0000000..3ff3cd7 --- /dev/null +++ b/database/migrations/2026_03_18_192641_add_role_id_to_users_table.php @@ -0,0 +1,29 @@ +foreignId('role_id')->nullable()->constrained('roles')->nullOnDelete()->after('phone'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $table->dropForeign(['role_id']); + $table->dropColumn('role_id'); + }); + } +}; diff --git a/database/migrations/2026_03_28_191947_fix_products_and_variations_structure.php b/database/migrations/2026_03_28_191947_fix_products_and_variations_structure.php new file mode 100644 index 0000000..77982f4 --- /dev/null +++ b/database/migrations/2026_03_28_191947_fix_products_and_variations_structure.php @@ -0,0 +1,73 @@ +dropColumn('attributes'); + }); + + // Удаляем JSON поля из таблицы product_variations + Schema::table('product_variations', function (Blueprint $table) { + $table->dropColumn('attributes'); + $table->dropColumn('images'); + }); + + // Создаем таблицу для изображений вариаций + Schema::create('variation_images', function (Blueprint $table) { + $table->id(); + $table->foreignId('variation_id')->constrained('product_variations')->cascadeOnDelete(); + $table->string('path'); + $table->integer('sort_order')->default(0); + $table->timestamps(); + + $table->index(['variation_id', 'sort_order']); + }); + + // Создаем таблицу для атрибутов вариаций + Schema::create('variation_attributes', function (Blueprint $table) { + $table->id(); + $table->foreignId('variation_id')->constrained('product_variations')->cascadeOnDelete(); + $table->string('key'); + $table->string('value')->nullable(); + $table->timestamps(); + + $table->index(['variation_id', 'key']); + }); + + // Создаем таблицу для атрибутов товара + Schema::create('product_attributes', function (Blueprint $table) { + $table->id(); + $table->foreignId('product_id')->constrained()->cascadeOnDelete(); + $table->string('key'); + $table->text('value')->nullable(); + $table->timestamps(); + + $table->index(['product_id', 'key']); + }); + } + + public function down() + { + // Возвращаем JSON поля + Schema::table('products', function (Blueprint $table) { + $table->json('attributes')->nullable(); + }); + + Schema::table('product_variations', function (Blueprint $table) { + $table->json('attributes')->nullable(); + $table->json('images')->nullable(); + }); + + // Удаляем новые таблицы + Schema::dropIfExists('variation_images'); + Schema::dropIfExists('variation_attributes'); + Schema::dropIfExists('product_attributes'); + } +}; diff --git a/database/migrations/2026_04_01_142406_create_cart_items_table.php b/database/migrations/2026_04_01_142406_create_cart_items_table.php new file mode 100644 index 0000000..c57e84e --- /dev/null +++ b/database/migrations/2026_04_01_142406_create_cart_items_table.php @@ -0,0 +1,28 @@ +id(); + $table->foreignId('user_id')->nullable()->constrained()->onDelete('cascade'); + $table->string('session_id')->nullable()->index(); + $table->foreignId('variation_id')->constrained('product_variations')->onDelete('cascade'); + $table->integer('quantity')->default(1); + $table->decimal('price', 10, 2); + $table->timestamps(); + + $table->index(['user_id', 'session_id']); + }); + } + + public function down() + { + Schema::dropIfExists('cart_items'); + } +} diff --git a/database/migrations/2026_04_01_142423_create_orders_table.php b/database/migrations/2026_04_01_142423_create_orders_table.php new file mode 100644 index 0000000..a59b38e --- /dev/null +++ b/database/migrations/2026_04_01_142423_create_orders_table.php @@ -0,0 +1,36 @@ +id(); + $table->string('order_number')->unique(); + $table->foreignId('user_id')->nullable()->constrained()->nullOnDelete(); + $table->string('customer_name'); + $table->string('customer_email'); + $table->string('customer_phone'); + $table->text('shipping_address')->nullable(); + $table->decimal('subtotal', 10, 2); + $table->decimal('shipping_cost', 10, 2)->default(0); + $table->decimal('discount', 10, 2)->default(0); + $table->decimal('total', 10, 2); + $table->string('payment_method')->default('cash'); + $table->string('payment_status')->default('pending'); + $table->string('delivery_method')->default('courier'); + $table->string('delivery_status')->default('pending'); + $table->text('comment')->nullable(); + $table->timestamps(); + }); + } + + public function down() + { + Schema::dropIfExists('orders'); + } +} diff --git a/database/migrations/2026_04_01_142428_create_order_items_table.php b/database/migrations/2026_04_01_142428_create_order_items_table.php new file mode 100644 index 0000000..78ab621 --- /dev/null +++ b/database/migrations/2026_04_01_142428_create_order_items_table.php @@ -0,0 +1,29 @@ +id(); + $table->foreignId('order_id')->constrained()->onDelete('cascade'); + $table->foreignId('variation_id')->constrained('product_variations')->onDelete('cascade'); + $table->string('product_name'); + $table->string('variation_name')->nullable(); + $table->string('sku')->nullable(); + $table->integer('quantity'); + $table->decimal('price', 10, 2); + $table->decimal('total', 10, 2); + $table->timestamps(); + }); + } + + public function down() + { + Schema::dropIfExists('order_items'); + } +} diff --git a/database/seeders/BrandSeeder.php b/database/seeders/BrandSeeder.php new file mode 100644 index 0000000..cb1fe62 --- /dev/null +++ b/database/seeders/BrandSeeder.php @@ -0,0 +1,67 @@ +cleanTables(); + + $brands = [ + [ + 'name' => 'Royal Canin', + 'slug' => 'royal-canin', + 'description' => 'Премиальные корма для кошек и собак', + 'country' => 'Франция', + 'website' => 'https://www.royalcanin.com', + ], + [ + 'name' => 'ABBA', + 'slug' => 'abba', + 'description' => 'Качественные корма для домашних животных', + 'country' => 'Россия', + 'website' => 'https://abba.ru', + ], + [ + 'name' => 'RURRI', + 'slug' => 'rurri', + 'description' => 'Одежда и амуниция для собак', + 'country' => 'Россия', + ], + [ + 'name' => 'Ownat', + 'slug' => 'ownat', + 'description' => 'Натуральные корма премиум-класса', + 'country' => 'Испания', + 'website' => 'https://www.ownat.com', + ], + [ + 'name' => 'Grandin', + 'slug' => 'grandin', + 'description' => 'Корма и лакомства для собак', + 'country' => 'Россия', + ], + ]; + + foreach ($brands as $brandData) { + Brand::create($brandData); + } + + $this->command->info('✅ Бренды созданы: ' . Brand::count()); + } + + private function cleanTables() + { + DB::statement('SET FOREIGN_KEY_CHECKS=0'); + DB::table('products')->truncate(); + DB::table('brands')->truncate(); + DB::statement('SET FOREIGN_KEY_CHECKS=1'); + } +} diff --git a/database/seeders/CategorySeeder.php b/database/seeders/CategorySeeder.php new file mode 100644 index 0000000..2fcc787 --- /dev/null +++ b/database/seeders/CategorySeeder.php @@ -0,0 +1,421 @@ +cleanTables(); + + $categories = [ + // ============================================ + // ДЛЯ СОБАК + // ============================================ + [ + 'name' => 'Для собак', + 'slug' => 'dlya-sobak', + 'sort_order' => 10, + 'children' => [ + // Корма для собак + [ + 'name' => 'Корма', + 'slug' => 'korma-dlya-sobak', + 'sort_order' => 10, + 'children' => [ + [ + 'name' => 'Сухие корма', + 'slug' => 'suhie-korma', + 'sort_order' => 10 + ], + [ + 'name' => 'Влажные корма', + 'slug' => 'vlazhnye-korma', + 'sort_order' => 20 + ], + [ + 'name' => 'Диетическое питание', + 'slug' => 'dieticheskoe-pitanie', + 'sort_order' => 30 + ], + ] + ], + // Амуниция для собак + [ + 'name' => 'Амуниция', + 'slug' => 'amunitsiya', + 'sort_order' => 20, + 'children' => [ + [ + 'name' => 'Ошейники и поводки', + 'slug' => 'osheyniki-i-povodki', + 'sort_order' => 10 + ], + [ + 'name' => 'Намордники', + 'slug' => 'namordniki', + 'sort_order' => 20 + ], + [ + 'name' => 'Шлейки', + 'slug' => 'shleyki', + 'sort_order' => 30 + ], + ] + ], + // Игрушки для собак + [ + 'name' => 'Игрушки', + 'slug' => 'igrushki', + 'sort_order' => 30, + 'children' => [ + [ + 'name' => 'Мячики и фрисби', + 'slug' => 'myachiki-i-frisbi', + 'sort_order' => 10 + ], + [ + 'name' => 'Канаты и кости', + 'slug' => 'kanaty-i-kosti', + 'sort_order' => 20 + ], + ] + ], + // Лежанки и домики + [ + 'name' => 'Лежанки и домики', + 'slug' => 'lezhanki-i-domiki', + 'sort_order' => 40, + 'children' => [ + [ + 'name' => 'Лежанки', + 'slug' => 'lezhanki', + 'sort_order' => 10 + ], + [ + 'name' => 'Домики', + 'slug' => 'domiki', + 'sort_order' => 20 + ], + ] + ], + // Миски и кормушки + [ + 'name' => 'Миски и кормушки', + 'slug' => 'miski-i-kormushki', + 'sort_order' => 50, + 'children' => [ + [ + 'name' => 'Миски', + 'slug' => 'miski', + 'sort_order' => 10 + ], + [ + 'name' => 'Автокормушки', + 'slug' => 'avtokormushki', + 'sort_order' => 20 + ], + ] + ], + ] + ], + + // ============================================ + // ДЛЯ КОШЕК + // ============================================ + [ + 'name' => 'Для кошек', + 'slug' => 'dlya-koshek', + 'sort_order' => 20, + 'children' => [ + // Корма для кошек + [ + 'name' => 'Корма', + 'slug' => 'korma-dlya-koshek', + 'sort_order' => 10, + 'children' => [ + [ + 'name' => 'Сухие корма', + 'slug' => 'suhie-korma-dlya-koshek', + 'sort_order' => 10 + ], + [ + 'name' => 'Паучи и консервы', + 'slug' => 'pauchi-i-konservy', + 'sort_order' => 20 + ], + [ + 'name' => 'Лакомства', + 'slug' => 'lakomstva-dlya-koshek', + 'sort_order' => 30 + ], + ] + ], + // Наполнители + [ + 'name' => 'Наполнители', + 'slug' => 'napolniteli', + 'sort_order' => 20, + 'children' => [ + [ + 'name' => 'Древесные', + 'slug' => 'drevesnye', + 'sort_order' => 10 + ], + [ + 'name' => 'Силикагелевые', + 'slug' => 'silikagelevye', + 'sort_order' => 20 + ], + [ + 'name' => 'Комкующиеся', + 'slug' => 'komkuyuschiesya', + 'sort_order' => 30 + ], + ] + ], + // Когтеточки и домики + [ + 'name' => 'Когтеточки и домики', + 'slug' => 'kogtetochki-i-domiki', + 'sort_order' => 30, + 'children' => [ + [ + 'name' => 'Когтеточки', + 'slug' => 'kogtetochki', + 'sort_order' => 10 + ], + [ + 'name' => 'Лежанки для кошек', + 'slug' => 'lezhanki-dlya-koshek', + 'sort_order' => 20 + ], + [ + 'name' => 'Игровые комплексы', + 'slug' => 'igrovye-kompleksy', + 'sort_order' => 30 + ], + ] + ], + ] + ], + + // ============================================ + // ДЛЯ ГРЫЗУНОВ + // ============================================ + [ + 'name' => 'Для грызунов', + 'slug' => 'dlya-gryzunov', + 'sort_order' => 30, + 'children' => [ + [ + 'name' => 'Корма', + 'slug' => 'korma-dlya-gryzunov', + 'sort_order' => 10, + 'children' => [ + [ + 'name' => 'Зерновые смеси', + 'slug' => 'zernovye-smesi', + 'sort_order' => 10 + ], + [ + 'name' => 'Сено и травы', + 'slug' => 'seno-i-travy', + 'sort_order' => 20 + ], + [ + 'name' => 'Лакомства', + 'slug' => 'lakomstva-dlya-gryzunov', + 'sort_order' => 30 + ], + ] + ], + [ + 'name' => 'Клетки и аксессуары', + 'slug' => 'kletki-i-aksessuary', + 'sort_order' => 20, + 'children' => [ + [ + 'name' => 'Клетки', + 'slug' => 'kletki', + 'sort_order' => 10 + ], + [ + 'name' => 'Поилки и миски', + 'slug' => 'poilki-i-miski', + 'sort_order' => 20 + ], + [ + 'name' => 'Наполнители', + 'slug' => 'napolniteli-dlya-gryzunov', + 'sort_order' => 30 + ], + ] + ], + ] + ], + + // ============================================ + // ДЛЯ ПТИЦ + // ============================================ + [ + 'name' => 'Для птиц', + 'slug' => 'dlya-ptic', + 'sort_order' => 40, + 'children' => [ + [ + 'name' => 'Корма', + 'slug' => 'korma-dlya-ptic', + 'sort_order' => 10, + 'children' => [ + [ + 'name' => 'Корма для попугаев', + 'slug' => 'korma-dlya-popugaev', + 'sort_order' => 10 + ], + [ + 'name' => 'Корма для канареек', + 'slug' => 'korma-dlya-kanareek', + 'sort_order' => 20 + ], + [ + 'name' => 'Минеральные камни', + 'slug' => 'mineralnye-kamni', + 'sort_order' => 30 + ], + ] + ], + [ + 'name' => 'Аксессуары', + 'slug' => 'aksessuary-dlya-ptic', + 'sort_order' => 20, + 'children' => [ + [ + 'name' => 'Клетки и жердочки', + 'slug' => 'kletki-i-zherdochki', + 'sort_order' => 10 + ], + [ + 'name' => 'Игрушки для птиц', + 'slug' => 'igrushki-dlya-ptic', + 'sort_order' => 20 + ], + ] + ], + ] + ], + + // ============================================ + // ДЛЯ РЫБ + // ============================================ + [ + 'name' => 'Для рыб', + 'slug' => 'dlya-ryb', + 'sort_order' => 50, + 'children' => [ + [ + 'name' => 'Корма для рыб', + 'slug' => 'korma-dlya-ryb', + 'sort_order' => 10 + ], + [ + 'name' => 'Аквариумы', + 'slug' => 'akvariumy', + 'sort_order' => 20 + ], + [ + 'name' => 'Фильтрация и помпы', + 'slug' => 'filtratsiya-i-pompy', + 'sort_order' => 30 + ], + [ + 'name' => 'Освещение', + 'slug' => 'osveschenie', + 'sort_order' => 40 + ], + [ + 'name' => 'Грунт и декор', + 'slug' => 'grunt-i-dekor', + 'sort_order' => 50 + ], + ] + ], + + // ============================================ + // ЗДОРОВЬЕ И УХОД + // ============================================ + [ + 'name' => 'Здоровье и уход', + 'slug' => 'zdorovie-i-uhod', + 'sort_order' => 60, + 'children' => [ + [ + 'name' => 'Шампуни и косметика', + 'slug' => 'shampuni-i-kosmetika', + 'sort_order' => 10 + ], + [ + 'name' => 'Витамины и добавки', + 'slug' => 'vitaminy-i-dobavki', + 'sort_order' => 20 + ], + [ + 'name' => 'Средства от паразитов', + 'slug' => 'sredstva-ot-parazitov', + 'sort_order' => 30 + ], + [ + 'name' => 'Аптечка', + 'slug' => 'aptechka', + 'sort_order' => 40 + ], + [ + 'name' => 'Груминг', + 'slug' => 'gruming', + 'sort_order' => 50 + ], + ] + ], + ]; + + foreach ($categories as $categoryData) { + $this->createCategoryWithChildren($categoryData); + } + + $this->command->info('✅ Категории успешно созданы!'); + $this->command->info('📊 Всего категорий: ' . Category::count()); + } + + private function cleanTables() + { + DB::statement('SET FOREIGN_KEY_CHECKS=0'); + DB::table('products')->truncate(); + DB::table('categories')->truncate(); + DB::statement('SET FOREIGN_KEY_CHECKS=1'); + } + + private function createCategoryWithChildren($data, $parentId = null) + { + $children = $data['children'] ?? []; + unset($data['children']); + + $data['parent_id'] = $parentId; + $data['is_active'] = true; + + $data['icon'] = $data['slug'] . '.svg'; + + $data['image'] = $data['slug'] . '.jpg'; + + $category = Category::create($data); + + foreach ($children as $childData) { + $this->createCategoryWithChildren($childData, $category->id); + } + } +} diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php new file mode 100644 index 0000000..4500a48 --- /dev/null +++ b/database/seeders/DatabaseSeeder.php @@ -0,0 +1,21 @@ +call([ + RolesAndPermissionsSeeder::class, + CategorySeeder::class, + BrandSeeder::class, + ]); + } +} diff --git a/database/seeders/ProductCatalogSeeder.php b/database/seeders/ProductCatalogSeeder.php new file mode 100644 index 0000000..9ad6798 --- /dev/null +++ b/database/seeders/ProductCatalogSeeder.php @@ -0,0 +1,1651 @@ + КОРМА -> СУХИЕ КОРМА (category_id=3) + // ====================================================== + DB::table('products')->insert([ + [ + 'id' => 21, + 'name' => 'Grandin Hypoallergenic ягненок', + 'slug' => 'grandin-hypoallergenic-yagnenok', + 'description' => 'Гипоаллергенный сухой корм для собак всех пород с ягненком. Беззерновой суперпремиум класс без курицы и говядины.', + 'brand_id' => 5, + 'category_id' => 3, + 'is_active' => 1, + 'meta_title' => 'Grandin Hypoallergenic сухой корм ягненок', + 'meta_description' => 'Гипоаллергенный корм Grandin с ягненком для собак всех пород без злаков суперпремиум класс', + 'meta_keywords' => 'grandin hypoallergenic,сухой корм собак,ягненок,гипоаллергенный корм собак', + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 22, + 'name' => 'Klicker Adult лосось', + 'slug' => 'klicker-adult-los-os', + 'description' => 'Сухой корм для собак мелких пород с лососем. Высокая усвояемость и поддержка пищеварения.', + 'brand_id' => null, + 'category_id' => 3, + 'is_active' => 1, + 'meta_title' => 'Klicker Adult сухой корм лосось мелкие породы', + 'meta_description' => 'Премиум корм Klicker для мелких пород с лососем поддержка пищеварения', + 'meta_keywords' => 'klicker корм,сухой корм мелких собак,лосось собак', + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 23, + 'name' => 'Royal Canin Mini Adult', + 'slug' => 'royal-canin-mini-adult', + 'description' => 'Сухой корм для взрослых собак мелких пород. Поддержка кожи и шерсти, высокая калорийность.', + 'brand_id' => 1, + 'category_id' => 3, + 'is_active' => 1, + 'meta_title' => 'Royal Canin Mini Adult сухой корм мелкие породы', + 'meta_description' => 'Корм Royal Canin для мелких взрослых собак поддержка кожи шерсти', + 'meta_keywords' => 'royal canin mini adult,сухой корм собак мелких пород', + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 24, + 'name' => 'ABBA Premium ягненок', + 'slug' => 'abba-premium-yagnenok', + 'description' => 'Сухой корм суперпремиум с ягненком для всех пород. Естественные ингредиенты без ГМО.', + 'brand_id' => 2, + 'category_id' => 3, + 'is_active' => 1, + 'meta_title' => 'ABBA Premium сухой корм ягненок', + 'meta_description' => 'ABBA суперпремиум корм с ягненком для собак всех пород натуральные ингредиенты', + 'meta_keywords' => 'abba premium,ягненок сухой корм,суперпремиум собак', + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 25, + 'name' => 'RURRI Classic говядина', + 'slug' => 'rurri-classic-govyadina', + 'description' => 'Сухой корм с говядиной для собак всех пород. Балансированное питание эконом класс.', + 'brand_id' => 3, + 'category_id' => 3, + 'is_active' => 1, + 'meta_title' => 'RURRI Classic говядина сухой корм', + 'meta_description' => 'Качественный корм RURRI Classic с говядиной для собак всех пород', + 'meta_keywords' => 'rurri classic,говядина корм собак,сухой корм эконом', + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 26, + 'name' => 'Ownat Grain Free курица', + 'slug' => 'ownat-grain-free-kurica', + 'description' => 'Беззерновой сухой корм с курицей и рисом для собак всех пород. Холистик класс.', + 'brand_id' => 4, + 'category_id' => 3, + 'is_active' => 1, + 'meta_title' => 'Ownat Grain Free сухой корм курица', + 'meta_description' => 'Беззерновой корм Ownat Grain Free с курицей холистик класс для собак', + 'meta_keywords' => 'ownat grain free,курица беззерновой,холистик корм', + 'created_at' => $now, + 'updated_at' => $now, + ], + ]); + + DB::table('product_variations')->insert([ + [ + 'id' => 41, + 'product_id' => 21, + 'name' => 'Grandin Hypoallergenic ягненок, 2.7 кг', + 'sku' => 'GRANDIN-1049894', + 'price' => 2499.00, + 'old_price' => null, + 'stock' => 50, + 'is_default' => 1, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 42, + 'product_id' => 21, + 'name' => 'Grandin Hypoallergenic ягненок, 11.2 кг', + 'sku' => 'GRANDIN-1049893', + 'price' => 7899.00, + 'old_price' => null, + 'stock' => 25, + 'is_default' => 0, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 43, + 'product_id' => 22, + 'name' => 'Klicker Adult лосось, 0.5 кг', + 'sku' => 'KLICKER-1060933', + 'price' => 1299.00, + 'old_price' => null, + 'stock' => 100, + 'is_default' => 1, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 44, + 'product_id' => 22, + 'name' => 'Klicker Adult лосось, 2 кг', + 'sku' => 'KLICKER-LOSOS-2KG', + 'price' => 3999.00, + 'old_price' => null, + 'stock' => 60, + 'is_default' => 0, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 45, + 'product_id' => 23, + 'name' => 'Royal Canin Mini Adult, 2 кг', + 'sku' => 'RC-MINI-ADULT-2KG', + 'price' => 3599.00, + 'old_price' => null, + 'stock' => 75, + 'is_default' => 1, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 46, + 'product_id' => 23, + 'name' => 'Royal Canin Mini Adult, 4 кг', + 'sku' => 'RC-MINI-ADULT-4KG', + 'price' => 5999.00, + 'old_price' => null, + 'stock' => 40, + 'is_default' => 0, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 47, + 'product_id' => 24, + 'name' => 'ABBA Premium ягненок, 3 кг', + 'sku' => 'ABBA-YAG-3KG', + 'price' => 2199.00, + 'old_price' => null, + 'stock' => 60, + 'is_default' => 1, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 48, + 'product_id' => 24, + 'name' => 'ABBA Premium ягненок, 12 кг', + 'sku' => 'ABBA-YAG-12KG', + 'price' => 6999.00, + 'old_price' => null, + 'stock' => 30, + 'is_default' => 0, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 49, + 'product_id' => 25, + 'name' => 'RURRI Classic говядина, 3 кг', + 'sku' => 'RURRI-GOV-3KG', + 'price' => 1599.00, + 'old_price' => 1799.00, + 'stock' => 80, + 'is_default' => 1, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 50, + 'product_id' => 25, + 'name' => 'RURRI Classic говядина, 15 кг', + 'sku' => 'RURRI-GOV-15KG', + 'price' => 5999.00, + 'old_price' => null, + 'stock' => 20, + 'is_default' => 0, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 51, + 'product_id' => 26, + 'name' => 'Ownat Grain Free курица, 2 кг', + 'sku' => 'OWNAT-KUR-2KG', + 'price' => 2299.00, + 'old_price' => null, + 'stock' => 45, + 'is_default' => 1, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 52, + 'product_id' => 26, + 'name' => 'Ownat Grain Free курица, 12 кг', + 'sku' => 'OWNAT-KUR-12KG', + 'price' => 7999.00, + 'old_price' => null, + 'stock' => 15, + 'is_default' => 0, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + ]); + + // ====================================================== + // 2. КОШКИ -> КОРМА -> СУХИЕ КОРМА (category_id=21) + // ====================================================== + DB::table('products')->insert([ + [ + 'id' => 27, + 'name' => 'Klicker Sensitive Digestion', + 'slug' => 'klicker-sensitive-digestion', + 'description' => 'Сухой корм для кошек с чувствительным пищеварением. Легкоусвояемые ингредиенты.', + 'brand_id' => null, + 'category_id' => 21, + 'is_active' => 1, + 'meta_title' => 'Klicker Sensitive сухой корм кошки пищеварение', + 'meta_description' => 'Корм для кошек с чувствительным пищеварением Klicker легкоусвояемые ингредиенты', + 'meta_keywords' => 'klicker sensitive,сухой корм кошек,пищеварение кошки', + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 28, + 'name' => 'Bozita Funktion Indoor', + 'slug' => 'bozita-funktion-indoor', + 'description' => 'Сухой корм для кошек живущих в доме. Контроль веса и шерсти.', + 'brand_id' => null, + 'category_id' => 21, + 'is_active' => 1, + 'meta_title' => 'Bozita Funktion Indoor сухой корм кошки', + 'meta_description' => 'Корм для домашних кошек Bozita Funktion контроль веса шерсти', + 'meta_keywords' => 'bozita indoor,корм кошек дом,функциональный корм', + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 29, + 'name' => 'Royal Canin Sterilised', + 'slug' => 'royal-canin-sterilised', + 'description' => 'Сухой корм для стерилизованных кошек. Поддержка мочевыводящей системы.', + 'brand_id' => 1, + 'category_id' => 21, + 'is_active' => 1, + 'meta_title' => 'Royal Canin Sterilised сухой корм кошки', + 'meta_description' => 'Корм Royal Canin для стерилизованных кошек поддержка мочевыводящей системы', + 'meta_keywords' => 'royal canin sterilised,корм стерилизованных кошек', + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 30, + 'name' => 'Grandin Hypo Cat', + 'slug' => 'grandin-hypo-cat', + 'description' => 'Гипоаллергенный сухой корм для кошек всех пород.', + 'brand_id' => 5, + 'category_id' => 21, + 'is_active' => 1, + 'meta_title' => 'Grandin Hypoallergenic сухой корм кошки', + 'meta_description' => 'Гипоаллергенный корм Grandin для кошек всех пород', + 'meta_keywords' => 'grandin hypo кошка,гипоаллергенный корм кошек', + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 31, + 'name' => 'Ownat Classic курица', + 'slug' => 'ownat-classic-kurica-koshka', + 'description' => 'Сухой корм для кошек с курицей. Полнорационное питание.', + 'brand_id' => 4, + 'category_id' => 21, + 'is_active' => 1, + 'meta_title' => 'Ownat Classic сухой корм кошки курица', + 'meta_description' => 'Полнорационный корм Ownat Classic с курицей для кошек', + 'meta_keywords' => 'ownat classic кошка,курица корм кошек', + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 32, + 'name' => 'RURRI Indoor', + 'slug' => 'rurri-indoor-koshka', + 'description' => 'Сухой корм для взрослых кошек живущих в помещении.', + 'brand_id' => 3, + 'category_id' => 21, + 'is_active' => 1, + 'meta_title' => 'RURRI Indoor сухой корм кошки', + 'meta_description' => 'Корм RURRI для взрослых кошек живущих в помещении', + 'meta_keywords' => 'rurri indoor,корм кошек помещение', + 'created_at' => $now, + 'updated_at' => $now, + ], + ]); + + DB::table('product_variations')->insert([ + [ + 'id' => 53, + 'product_id' => 27, + 'name' => 'Klicker Sensitive Digestion, 1 кг', + 'sku' => 'KLICKER-SENS-1KG', + 'price' => 1399.00, + 'old_price' => null, + 'stock' => 80, + 'is_default' => 1, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 54, + 'product_id' => 27, + 'name' => 'Klicker Sensitive Digestion, 4 кг', + 'sku' => 'KLICKER-SENS-4KG', + 'price' => 4499.00, + 'old_price' => null, + 'stock' => 35, + 'is_default' => 0, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 55, + 'product_id' => 28, + 'name' => 'Bozita Funktion Indoor, 1.5 кг', + 'sku' => 'BOZITA-INDOOR-1.5', + 'price' => 1899.00, + 'old_price' => null, + 'stock' => 70, + 'is_default' => 1, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 56, + 'product_id' => 28, + 'name' => 'Bozita Funktion Indoor, 4 кг', + 'sku' => 'BOZITA-INDOOR-4KG', + 'price' => 4899.00, + 'old_price' => null, + 'stock' => 25, + 'is_default' => 0, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 57, + 'product_id' => 29, + 'name' => 'Royal Canin Sterilised, 2 кг', + 'sku' => 'RC-STER-2KG', + 'price' => 3499.00, + 'old_price' => null, + 'stock' => 60, + 'is_default' => 1, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 58, + 'product_id' => 29, + 'name' => 'Royal Canin Sterilised, 4 кг', + 'sku' => 'RC-STER-4KG', + 'price' => 5899.00, + 'old_price' => null, + 'stock' => 30, + 'is_default' => 0, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 59, + 'product_id' => 30, + 'name' => 'Grandin Hypo Cat, 2 кг', + 'sku' => 'GRANDIN-HYPOCAT-2', + 'price' => 2399.00, + 'old_price' => null, + 'stock' => 55, + 'is_default' => 1, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 60, + 'product_id' => 30, + 'name' => 'Grandin Hypo Cat, 10 кг', + 'sku' => 'GRANDIN-HYPOCAT-10', + 'price' => 7499.00, + 'old_price' => null, + 'stock' => 20, + 'is_default' => 0, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 61, + 'product_id' => 31, + 'name' => 'Ownat Classic курица, 1.5 кг', + 'sku' => 'OWNAT-KUR-KOSH-1.5', + 'price' => 1699.00, + 'old_price' => null, + 'stock' => 65, + 'is_default' => 1, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 62, + 'product_id' => 31, + 'name' => 'Ownat Classic курица, 7 кг', + 'sku' => 'OWNAT-KUR-KOSH-7', + 'price' => 4999.00, + 'old_price' => null, + 'stock' => 25, + 'is_default' => 0, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 63, + 'product_id' => 32, + 'name' => 'RURRI Indoor, 2 кг', + 'sku' => 'RURRI-INDOOR-2', + 'price' => 1799.00, + 'old_price' => null, + 'stock' => 75, + 'is_default' => 1, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 64, + 'product_id' => 32, + 'name' => 'RURRI Indoor, 10 кг', + 'sku' => 'RURRI-INDOOR-10', + 'price' => 5999.00, + 'old_price' => null, + 'stock' => 15, + 'is_default' => 0, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + ]); + + // ====================================================== + // 3. СОБАКИ -> АМУНИЦИЯ -> ОШЕЙНИКИ И ПОВОДКИ (category_id=7) + // ====================================================== + DB::table('products')->insert([ + [ + 'id' => 33, + 'name' => 'Rungo K-9 ошейник', + 'slug' => 'rungo-k9-osheynik', + 'description' => 'Нейлоновый ошейник с ручкой для контроля собаки. Прочный и удобный.', + 'brand_id' => 10, + 'category_id' => 7, + 'is_active' => 1, + 'meta_title' => 'Rungo K-9 ошейник нейлоновый с ручкой', + 'meta_description' => 'Прочный ошейник Rungo K-9 с ручкой для собак всех размеров', + 'meta_keywords' => 'rungo k9,ошейник собак с ручкой,нейлоновый ошейник', + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 34, + 'name' => 'Trixie кожаный ошейник', + 'slug' => 'trixie-kozhanii-osheynik', + 'description' => 'Натуральная кожа с металлическими заклепками. Классический дизайн.', + 'brand_id' => null, + 'category_id' => 7, + 'is_active' => 1, + 'meta_title' => 'Trixie кожаный ошейник собака', + 'meta_description' => 'Кожаный ошейник Trixie натуральная кожа для собак', + 'meta_keywords' => 'trixie кожаный,ошейник собака кожа', + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 35, + 'name' => 'Ferplast парашютный ошейник', + 'slug' => 'ferplast-parashyutnyi-osheynik', + 'description' => 'Парашютная ткань легкая и прочная. Регулируемый размер.', + 'brand_id' => null, + 'category_id' => 7, + 'is_active' => 1, + 'meta_title' => 'Ferplast парашютный ошейник собака', + 'meta_description' => 'Легкий парашютный ошейник Ferplast для собак', + 'meta_keywords' => 'ferplast парашютный,ошейник легкий собака', + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 36, + 'name' => 'Rogz ошейник Utility', + 'slug' => 'rogz-utility-osheynik', + 'description' => 'Усиленный нейлон с мягкой подкладкой. 5 точек фиксации.', + 'brand_id' => null, + 'category_id' => 7, + 'is_active' => 1, + 'meta_title' => 'Rogz Utility ошейник собака', + 'meta_description' => 'Прочный ошейник Rogz Utility усиленный нейлон', + 'meta_keywords' => 'rogz utility,ошейник собака усиленный', + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 37, + 'name' => 'Hunter кожаный плетеный', + 'slug' => 'hunter-kozhanii-pletenyi', + 'description' => 'Ручная плетка из натуральной кожи. Эксклюзивный дизайн.', + 'brand_id' => null, + 'category_id' => 7, + 'is_active' => 1, + 'meta_title' => 'Hunter плетеный кожаный ошейник', + 'meta_description' => 'Эксклюзивный плетеный кожаный ошейник Hunter', + 'meta_keywords' => 'hunter плетеный,кожаный ошейник эксклюзив', + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 38, + 'name' => 'Petzl ошейник альпинистский', + 'slug' => 'petzl-alpinistskii-osheynik', + 'description' => 'Сверхпрочный для рабочих собак. Используется кинологами.', + 'brand_id' => null, + 'category_id' => 7, + 'is_active' => 1, + 'meta_title' => 'Petzl альпинистский ошейник собака', + 'meta_description' => 'Сверхпрочный ошейник Petzl для рабочих собак', + 'meta_keywords' => 'petzl альпинистский,ошейник рабочие собаки', + 'created_at' => $now, + 'updated_at' => $now, + ], + ]); + + DB::table('product_variations')->insert([ + [ + 'id' => 65, + 'product_id' => 33, + 'name' => 'Rungo K-9 ошейник, L (42-65 см)', + 'sku' => 'RUNGO-K9-L-1064128', + 'price' => 1749.00, + 'old_price' => null, + 'stock' => 25, + 'is_default' => 1, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 66, + 'product_id' => 33, + 'name' => 'Rungo K-9 ошейник, M (38-48 см)', + 'sku' => 'RUNGO-K9-M-1064127', + 'price' => 1499.00, + 'old_price' => 1599.00, + 'stock' => 10, + 'is_default' => 0, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 67, + 'product_id' => 34, + 'name' => 'Trixie кожаный ошейник, M (30-45 см)', + 'sku' => 'TRIXIE-LEATHER-M', + 'price' => 2199.00, + 'old_price' => null, + 'stock' => 40, + 'is_default' => 1, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 68, + 'product_id' => 34, + 'name' => 'Trixie кожаный ошейник, L (40-60 см)', + 'sku' => 'TRIXIE-LEATHER-L', + 'price' => 2799.00, + 'old_price' => null, + 'stock' => 20, + 'is_default' => 0, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 69, + 'product_id' => 35, + 'name' => 'Ferplast парашютный ошейник, S (25-40 см)', + 'sku' => 'FERPLAST-S', + 'price' => 1299.00, + 'old_price' => null, + 'stock' => 60, + 'is_default' => 1, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 70, + 'product_id' => 35, + 'name' => 'Ferplast парашютный ошейник, M (35-55 см)', + 'sku' => 'FERPLAST-M', + 'price' => 1599.00, + 'old_price' => null, + 'stock' => 35, + 'is_default' => 0, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 71, + 'product_id' => 36, + 'name' => 'Rogz Utility ошейник, M', + 'sku' => 'ROGZ-UTILITY-M', + 'price' => 2999.00, + 'old_price' => null, + 'stock' => 30, + 'is_default' => 1, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 72, + 'product_id' => 36, + 'name' => 'Rogz Utility ошейник, L', + 'sku' => 'ROGZ-UTILITY-L', + 'price' => 3499.00, + 'old_price' => null, + 'stock' => 15, + 'is_default' => 0, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 73, + 'product_id' => 37, + 'name' => 'Hunter кожаный плетеный, M', + 'sku' => 'HUNTER-PLET-M', + 'price' => 4999.00, + 'old_price' => null, + 'stock' => 10, + 'is_default' => 1, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 74, + 'product_id' => 37, + 'name' => 'Hunter кожаный плетеный, L', + 'sku' => 'HUNTER-PLET-L', + 'price' => 5999.00, + 'old_price' => null, + 'stock' => 5, + 'is_default' => 0, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 75, + 'product_id' => 38, + 'name' => 'Petzl ошейник альпинистский, L/XL', + 'sku' => 'PETZL-ALP-LXL', + 'price' => 7999.00, + 'old_price' => null, + 'stock' => 8, + 'is_default' => 1, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + ]); + + // ====================================================== + // 4. КОШКИ -> НАПОЛНИТЕЛИ -> ДРЕВЕСНЫЕ (category_id=25) + // ====================================================== + DB::table('products')->insert([ + [ + 'id' => 39, + 'name' => 'Ever Clean Extra Strength', + 'slug' => 'ever-clean-extra-strength', + 'description' => 'Древесный наполнитель для кошек. Сильная комкуемость отличная нейтрализация запаха.', + 'brand_id' => null, + 'category_id' => 25, + 'is_active' => 1, + 'meta_title' => 'Ever Clean Extra Strength древесный наполнитель', + 'meta_description' => 'Премиум древесный наполнитель Ever Clean сильная комкуемость', + 'meta_keywords' => 'ever clean,древесный наполнитель кошки,премиум наполнитель', + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 40, + 'name' => "Cat's Best Original", + 'slug' => 'cats-best-original', + 'description' => 'Натуральный гранулированный наполнитель из волокон целлюлозы. 100% биоразлагаемый.', + 'brand_id' => null, + 'category_id' => 25, + 'is_active' => 1, + 'meta_title' => "Cat's Best Original древесный наполнитель", + 'meta_description' => 'Натуральный древесный наполнитель Cat\'s Best из целлюлозы', + 'meta_keywords' => 'cats best,древесный натуральный,наполнитель кошки', + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 41, + 'name' => 'Barsik Premium древесный', + 'slug' => 'barsik-premium-drevesnyi', + 'description' => 'Древесный наполнитель из опилок хвойных пород. Высокая впитываемость.', + 'brand_id' => null, + 'category_id' => 25, + 'is_active' => 1, + 'meta_title' => 'Barsik Premium древесный наполнитель кошки', + 'meta_description' => 'Древесный наполнитель Barsik Premium из хвойных пород', + 'meta_keywords' => 'barsik древесный,наполнитель кошки хвойный', + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 42, + 'name' => 'Perfect Fit древесный', + 'slug' => 'perfect-fit-drevesnyi', + 'description' => 'Комкующийся древесный наполнитель. Натуральный состав без химии.', + 'brand_id' => null, + 'category_id' => 25, + 'is_active' => 1, + 'meta_title' => 'Perfect Fit древесный наполнитель', + 'meta_description' => 'Комкующийся древесный наполнитель Perfect Fit натуральный', + 'meta_keywords' => 'perfect fit древесный,комкующийся наполнитель', + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 43, + 'name' => 'Сибирский лес премиум', + 'slug' => 'sibirskii-les-premium', + 'description' => 'Премиум древесный наполнитель. Сильный контроль запаха.', + 'brand_id' => null, + 'category_id' => 25, + 'is_active' => 1, + 'meta_title' => 'Сибирский лес премиум древесный наполнитель', + 'meta_description' => 'Премиум древесный наполнитель Сибирский лес контроль запаха', + 'meta_keywords' => 'сибирский лес,древесный премиум наполнитель', + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 44, + 'name' => 'Травка-Сено древесный', + 'slug' => 'travka-seno-drevesnyi', + 'description' => 'Натуральный древесный наполнитель с экстрактами трав.', + 'brand_id' => null, + 'category_id' => 25, + 'is_active' => 1, + 'meta_title' => 'Травка-Сено древесный наполнитель кошки', + 'meta_description' => 'Древесный наполнитель Травка-Сено с экстрактами трав', + 'meta_keywords' => 'травка сено,древесный с травами наполнитель', + 'created_at' => $now, + 'updated_at' => $now, + ], + ]); + + DB::table('product_variations')->insert([ + [ + 'id' => 76, + 'product_id' => 39, + 'name' => 'Ever Clean Extra Strength, 10 л', + 'sku' => 'EVERCLEAN-10L', + 'price' => 1499.00, + 'old_price' => null, + 'stock' => 40, + 'is_default' => 1, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 77, + 'product_id' => 39, + 'name' => 'Ever Clean Extra Strength, 20 л', + 'sku' => 'EVERCLEAN-20L', + 'price' => 2599.00, + 'old_price' => null, + 'stock' => 25, + 'is_default' => 0, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 78, + 'product_id' => 40, + 'name' => "Cat's Best Original, 6 кг", + 'sku' => 'CATS-BEST-6KG', + 'price' => 1299.00, + 'old_price' => null, + 'stock' => 60, + 'is_default' => 1, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 79, + 'product_id' => 40, + 'name' => "Cat's Best Original, 17 кг", + 'sku' => 'CATS-BEST-17KG', + 'price' => 2999.00, + 'old_price' => null, + 'stock' => 30, + 'is_default' => 0, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 80, + 'product_id' => 41, + 'name' => 'Barsik Premium древесный, 5 л', + 'sku' => 'BARSIK-5L', + 'price' => 899.00, + 'old_price' => null, + 'stock' => 80, + 'is_default' => 1, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 81, + 'product_id' => 41, + 'name' => 'Barsik Premium древесный, 10 л', + 'sku' => 'BARSIK-10L', + 'price' => 1499.00, + 'old_price' => null, + 'stock' => 50, + 'is_default' => 0, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 82, + 'product_id' => 42, + 'name' => 'Perfect Fit древесный, 8 л', + 'sku' => 'PERFECTFIT-8L', + 'price' => 1099.00, + 'old_price' => null, + 'stock' => 70, + 'is_default' => 1, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 83, + 'product_id' => 42, + 'name' => 'Perfect Fit древесный, 15 л', + 'sku' => 'PERFECTFIT-15L', + 'price' => 1999.00, + 'old_price' => null, + 'stock' => 40, + 'is_default' => 0, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 84, + 'product_id' => 43, + 'name' => 'Сибирский лес премиум, 10 л', + 'sku' => 'SIBLES-10L', + 'price' => 1399.00, + 'old_price' => null, + 'stock' => 45, + 'is_default' => 1, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 85, + 'product_id' => 43, + 'name' => 'Сибирский лес премиум, 20 л', + 'sku' => 'SIBLES-20L', + 'price' => 2499.00, + 'old_price' => null, + 'stock' => 20, + 'is_default' => 0, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 86, + 'product_id' => 44, + 'name' => 'Травка-Сено древесный, 7 л', + 'sku' => 'TRAVKA-7L', + 'price' => 1199.00, + 'old_price' => null, + 'stock' => 55, + 'is_default' => 1, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 87, + 'product_id' => 44, + 'name' => 'Травка-Сено древесный, 14 л', + 'sku' => 'TRAVKA-14L', + 'price' => 2099.00, + 'old_price' => null, + 'stock' => 30, + 'is_default' => 0, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + ]); + + // ====================================================== + // 5. СОБАКИ -> ИГРУШКИ -> МЯЧИКИ И ФРИСБИ (category_id=11) + // ====================================================== + DB::table('products')->insert([ + [ + 'id' => 45, + 'name' => 'Kong Classic красный', + 'slug' => 'kong-classic-krasnyi', + 'description' => 'Классическая резиновая игрушка для жевания и апортировки. Неотъемлемый элемент дрессировки.', + 'brand_id' => null, + 'category_id' => 11, + 'is_active' => 1, + 'meta_title' => 'Kong Classic красный мячик собака', + 'meta_description' => 'Резиновая игрушка Kong Classic для собак жевание апортировка', + 'meta_keywords' => 'kong classic,мячик собака резиновый,игрушка для жевания', + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 46, + 'name' => 'Trixie теннисный мячик', + 'slug' => 'trixie-tennisnyi-myachik', + 'description' => 'Теннисный мячик для собак. Прочная резина с войлоком.', + 'brand_id' => null, + 'category_id' => 11, + 'is_active' => 1, + 'meta_title' => 'Trixie теннисный мячик собака', + 'meta_description' => 'Теннисный мячик Trixie для апортировки собак', + 'meta_keywords' => 'trixie теннисный,мячик собака апортировка', + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 47, + 'name' => 'Ferplast фрисби Flyer', + 'slug' => 'ferplast-frisbi-flyer', + 'description' => 'Пластиковый фрисби для активных игр на улице. Легкий и прочный.', + 'brand_id' => null, + 'category_id' => 11, + 'is_active' => 1, + 'meta_title' => 'Ferplast Flyer фрисби собака', + 'meta_description' => 'Фрисби Ferplast Flyer для собак активные игры', + 'meta_keywords' => 'ferplast фрисби,фрисби собака уличные игры', + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 48, + 'name' => 'K9 Granit мячик', + 'slug' => 'k9-granit-myachik', + 'description' => 'Прочный резиновый мячик для сильных челюстей. Выдерживает давление.', + 'brand_id' => null, + 'category_id' => 11, + 'is_active' => 1, + 'meta_title' => 'K9 Granit мячик собака', + 'meta_description' => 'Резиновый мячик K9 Granit для мощных собак', + 'meta_keywords' => 'k9 granit,мячик собака прочный челюсти', + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 49, + 'name' => 'Chuckit! Ultra мячик', + 'slug' => 'chuckit-ultra-myachik', + 'description' => 'Яркий мячик для метателя Chuckit. Увеличенная дальность броска.', + 'brand_id' => null, + 'category_id' => 11, + 'is_active' => 1, + 'meta_title' => 'Chuckit Ultra мячик собака метатель', + 'meta_description' => 'Мячик Chuckit Ultra для метателя собак', + 'meta_keywords' => 'chuckit ultra,мячик метатель собака', + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 50, + 'name' => 'Rogz Grinz мячик зубастый', + 'slug' => 'rogz-grinz-myachik-zubastyi', + 'description' => 'Резиновый мячик с зубастой мордочкой. Массаж десен.', + 'brand_id' => null, + 'category_id' => 11, + 'is_active' => 1, + 'meta_title' => 'Rogz Grinz зубастый мячик собака', + 'meta_description' => 'Мячик Rogz Grinz массаж десен собак', + 'meta_keywords' => 'rogz grinz,мячик зубастый собака десны', + 'created_at' => $now, + 'updated_at' => $now, + ], + ]); + + DB::table('product_variations')->insert([ + [ + 'id' => 88, + 'product_id' => 45, + 'name' => 'Kong Classic красный, S', + 'sku' => 'KONG-CLASSIC-S', + 'price' => 1999.00, + 'old_price' => null, + 'stock' => 35, + 'is_default' => 1, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 89, + 'product_id' => 45, + 'name' => 'Kong Classic красный, M', + 'sku' => 'KONG-CLASSIC-M', + 'price' => 2499.00, + 'old_price' => null, + 'stock' => 25, + 'is_default' => 0, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 90, + 'product_id' => 46, + 'name' => 'Trixie теннисный мячик, стандарт', + 'sku' => 'TRIXIE-TENNIS-STD', + 'price' => 399.00, + 'old_price' => null, + 'stock' => 100, + 'is_default' => 1, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 91, + 'product_id' => 46, + 'name' => 'Trixie теннисный мячик, большой', + 'sku' => 'TRIXIE-TENNIS-L', + 'price' => 599.00, + 'old_price' => null, + 'stock' => 70, + 'is_default' => 0, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 92, + 'product_id' => 47, + 'name' => 'Ferplast фрисби Flyer, средний', + 'sku' => 'FERPLAST-FLYER-M', + 'price' => 799.00, + 'old_price' => null, + 'stock' => 60, + 'is_default' => 1, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 93, + 'product_id' => 47, + 'name' => 'Ferplast фрисби Flyer, большой', + 'sku' => 'FERPLAST-FLYER-L', + 'price' => 999.00, + 'old_price' => null, + 'stock' => 40, + 'is_default' => 0, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 94, + 'product_id' => 48, + 'name' => 'K9 Granit мячик, M', + 'sku' => 'K9-GRANIT-M', + 'price' => 1499.00, + 'old_price' => null, + 'stock' => 30, + 'is_default' => 1, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 95, + 'product_id' => 48, + 'name' => 'K9 Granit мячик, L', + 'sku' => 'K9-GRANIT-L', + 'price' => 1999.00, + 'old_price' => null, + 'stock' => 20, + 'is_default' => 0, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 96, + 'product_id' => 49, + 'name' => 'Chuckit! Ultra мячик, средний', + 'sku' => 'CHUCKIT-ULTRA-M', + 'price' => 1299.00, + 'old_price' => null, + 'stock' => 50, + 'is_default' => 1, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 97, + 'product_id' => 49, + 'name' => 'Chuckit! Ultra мячик, большой', + 'sku' => 'CHUCKIT-ULTRA-L', + 'price' => 1699.00, + 'old_price' => null, + 'stock' => 35, + 'is_default' => 0, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 98, + 'product_id' => 50, + 'name' => 'Rogz Grinz мячик зубастый, M', + 'sku' => 'ROGZ-GRINZ-M', + 'price' => 1199.00, + 'old_price' => null, + 'stock' => 45, + 'is_default' => 1, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 99, + 'product_id' => 50, + 'name' => 'Rogz Grinz мячик зубастый, L', + 'sku' => 'ROGZ-GRINZ-L', + 'price' => 1499.00, + 'old_price' => null, + 'stock' => 25, + 'is_default' => 0, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + ]); + + // ====================================================== + // 6. СОБАКИ -> ЛЕЖАНКИ И ДОМИКИ -> ЛЕЖАНКИ (category_id=14) + // ====================================================== + DB::table('products')->insert([ + [ + 'id' => 51, + 'name' => 'Trixie Ортопедическая лежанка', + 'slug' => 'trixie-ortopedicheskaya-lezhanka', + 'description' => 'Лежанка с ортопедической пеной memory foam. Для собак с проблемами суставов.', + 'brand_id' => null, + 'category_id' => 14, + 'is_active' => 1, + 'meta_title' => 'Trixie ортопедическая лежанка собака', + 'meta_description' => 'Ортопедическая лежанка Trixie memory foam для собак суставы', + 'meta_keywords' => 'trixie ортопедическая,лежанка memory foam,собаки суставы', + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 52, + 'name' => 'Good Boy плед лежанка', + 'slug' => 'good-boy-pled-lezhanka', + 'description' => 'Мягкая лежанка-плед из флиса. Съемный чехол машинная стирка.', + 'brand_id' => null, + 'category_id' => 14, + 'is_active' => 1, + 'meta_title' => 'Good Boy плед лежанка собака', + 'meta_description' => 'Лежанка-плед Good Boy флис съемный чехол', + 'meta_keywords' => 'good boy плед,лежанка флис собака', + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 53, + 'name' => 'Ferplast лежанка Carlotta', + 'slug' => 'ferplast-lezhanka-carlotta', + 'description' => 'Круглая лежанка с бортиками. Машинная стирка 30°C.', + 'brand_id' => null, + 'category_id' => 14, + 'is_active' => 1, + 'meta_title' => 'Ferplast Carlotta лежанка собака', + 'meta_description' => 'Круглая лежанка Ferplast Carlotta с бортиками', + 'meta_keywords' => 'ferplast carlotta,лежанка круглая бортики', + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 54, + 'name' => 'Hunter лежанка Bavaria', + 'slug' => 'hunter-lezhanka-bavaria', + 'description' => 'Немецкое качество. Прочная ткань водоотталкивающая подкладка.', + 'brand_id' => null, + 'category_id' => 14, + 'is_active' => 1, + 'meta_title' => 'Hunter Bavaria лежанка собака', + 'meta_description' => 'Лежанка Hunter Bavaria немецкое качество водоотталкивающая', + 'meta_keywords' => 'hunter bavaria,лежанка немецкая качество', + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 55, + 'name' => 'PetFusion ортопедическая', + 'slug' => 'petfusion-ortopedicheskaya', + 'description' => 'Лежанка с ортопедической пеной. Экологичные материалы гипоаллергенная.', + 'brand_id' => null, + 'category_id' => 14, + 'is_active' => 1, + 'meta_title' => 'PetFusion ортопедическая лежанка собака', + 'meta_description' => 'Ортопедическая лежанка PetFusion экологичные гипоаллергенная', + 'meta_keywords' => 'petfusion ортопедическая,лежанка экологичная', + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 56, + 'name' => 'Laifugy лежанка домик', + 'slug' => 'laifugy-lezhanka-domik', + 'description' => 'Лежанка-домик с крышей. Для маленьких собак и щенков.', + 'brand_id' => null, + 'category_id' => 14, + 'is_active' => 1, + 'meta_title' => 'Laifugy лежанка домик собака', + 'meta_description' => 'Лежанка-домик Laifugy для маленьких собак щенков', + 'meta_keywords' => 'laifugy домик,лежанка маленькие собаки', + 'created_at' => $now, + 'updated_at' => $now, + ], + ]); + + DB::table('product_variations')->insert([ + [ + 'id' => 100, + 'product_id' => 51, + 'name' => 'Trixie Ортопедическая лежанка, S (50x40 см)', + 'sku' => 'TRIXIE-ORTHO-S', + 'price' => 7999.00, + 'old_price' => null, + 'stock' => 15, + 'is_default' => 1, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 101, + 'product_id' => 51, + 'name' => 'Trixie Ортопедическая лежанка, M (70x50 см)', + 'sku' => 'TRIXIE-ORTHO-M', + 'price' => 10999.00, + 'old_price' => null, + 'stock' => 10, + 'is_default' => 0, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 102, + 'product_id' => 52, + 'name' => 'Good Boy плед лежанка, S (60x45 см)', + 'sku' => 'GOODBOY-PLED-S', + 'price' => 2999.00, + 'old_price' => null, + 'stock' => 40, + 'is_default' => 1, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 103, + 'product_id' => 52, + 'name' => 'Good Boy плед лежанка, L (90x65 см)', + 'sku' => 'GOODBOY-PLED-L', + 'price' => 5999.00, + 'old_price' => null, + 'stock' => 25, + 'is_default' => 0, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 104, + 'product_id' => 53, + 'name' => 'Ferplast лежанка Carlotta, S (50 см диаметр)', + 'sku' => 'FERPLAST-CARL-S', + 'price' => 3499.00, + 'old_price' => null, + 'stock' => 35, + 'is_default' => 1, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 105, + 'product_id' => 53, + 'name' => 'Ferplast лежанка Carlotta, M (70 см диаметр)', + 'sku' => 'FERPLAST-CARL-M', + 'price' => 5999.00, + 'old_price' => null, + 'stock' => 20, + 'is_default' => 0, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 106, + 'product_id' => 54, + 'name' => 'Hunter лежанка Bavaria, M (75x55 см)', + 'sku' => 'HUNTER-BAV-M', + 'price' => 6999.00, + 'old_price' => null, + 'stock' => 20, + 'is_default' => 1, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 107, + 'product_id' => 54, + 'name' => 'Hunter лежанка Bavaria, L (95x70 см)', + 'sku' => 'HUNTER-BAV-L', + 'price' => 9999.00, + 'old_price' => null, + 'stock' => 12, + 'is_default' => 0, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 108, + 'product_id' => 55, + 'name' => 'PetFusion ортопедическая, M (71x53 см)', + 'sku' => 'PETFUSION-M', + 'price' => 8999.00, + 'old_price' => null, + 'stock' => 18, + 'is_default' => 1, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 109, + 'product_id' => 55, + 'name' => 'PetFusion ортопедическая, L (91x66 см)', + 'sku' => 'PETFUSION-L', + 'price' => 12999.00, + 'old_price' => null, + 'stock' => 8, + 'is_default' => 0, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 110, + 'product_id' => 56, + 'name' => 'Laifugy лежанка домик, S (60x45x30 см)', + 'sku' => 'LAIFUGY-S', + 'price' => 4499.00, + 'old_price' => null, + 'stock' => 30, + 'is_default' => 1, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + [ + 'id' => 111, + 'product_id' => 56, + 'name' => 'Laifugy лежанка домик, M (80x60x35 см)', + 'sku' => 'LAIFUGY-M', + 'price' => 6999.00, + 'old_price' => null, + 'stock' => 20, + 'is_default' => 0, + 'is_active' => 1, + 'created_at' => $now, + 'updated_at' => $now, + ], + ]); + + // ====================================================== + // АТРИБУТЫ ДЛЯ ПРОДУКТОВ (product_attributes) + // ====================================================== + DB::table('product_attributes')->insert([ + // Для Grandin Hypoallergenic (id=21) + ['id' => 35, 'product_id' => 21, 'key' => 'Размер питомца', 'value' => 'Все размеры', 'created_at' => $now, 'updated_at' => $now], + ['id' => 36, 'product_id' => 21, 'key' => 'Тип корма', 'value' => 'Сухой', 'created_at' => $now, 'updated_at' => $now], + ['id' => 37, 'product_id' => 21, 'key' => 'Класс корма', 'value' => 'Суперпремиум', 'created_at' => $now, 'updated_at' => $now], + ['id' => 38, 'product_id' => 21, 'key' => 'Особенности', 'value' => 'Беззерновой, гипоаллергенный', 'created_at' => $now, 'updated_at' => $now], + + // Для Klicker Adult (id=22) + ['id' => 39, 'product_id' => 22, 'key' => 'Размер питомца', 'value' => 'Мелкие породы', 'created_at' => $now, 'updated_at' => $now], + ['id' => 40, 'product_id' => 22, 'key' => 'Тип корма', 'value' => 'Сухой', 'created_at' => $now, 'updated_at' => $now], + ['id' => 41, 'product_id' => 22, 'key' => 'Класс корма', 'value' => 'Премиум', 'created_at' => $now, 'updated_at' => $now], + + // Для Royal Canin Mini Adult (id=23) + ['id' => 42, 'product_id' => 23, 'key' => 'Размер питомца', 'value' => 'Мелкие породы', 'created_at' => $now, 'updated_at' => $now], + ['id' => 43, 'product_id' => 23, 'key' => 'Возраст', 'value' => 'Взрослые (1-7 лет)', 'created_at' => $now, 'updated_at' => $now], + ['id' => 44, 'product_id' => 23, 'key' => 'Тип корма', 'value' => 'Сухой', 'created_at' => $now, 'updated_at' => $now], + + // Для ABBA Premium (id=24) + ['id' => 45, 'product_id' => 24, 'key' => 'Размер питомца', 'value' => 'Все размеры', 'created_at' => $now, 'updated_at' => $now], + ['id' => 46, 'product_id' => 24, 'key' => 'Тип корма', 'value' => 'Сухой', 'created_at' => $now, 'updated_at' => $now], + ['id' => 47, 'product_id' => 24, 'key' => 'Класс корма', 'value' => 'Суперпремиум', 'created_at' => $now, 'updated_at' => $now], + + // Для RURRI Classic (id=25) + ['id' => 48, 'product_id' => 25, 'key' => 'Размер питомца', 'value' => 'Все размеры', 'created_at' => $now, 'updated_at' => $now], + ['id' => 49, 'product_id' => 25, 'key' => 'Тип корма', 'value' => 'Сухой', 'created_at' => $now, 'updated_at' => $now], + ['id' => 50, 'product_id' => 25, 'key' => 'Класс корма', 'value' => 'Эконом', 'created_at' => $now, 'updated_at' => $now], + + // Для Ownat Grain Free (id=26) + ['id' => 51, 'product_id' => 26, 'key' => 'Размер питомца', 'value' => 'Все размеры', 'created_at' => $now, 'updated_at' => $now], + ['id' => 52, 'product_id' => 26, 'key' => 'Тип корма', 'value' => 'Сухой', 'created_at' => $now, 'updated_at' => $now], + ['id' => 53, 'product_id' => 26, 'key' => 'Класс корма', 'value' => 'Холистик', 'created_at' => $now, 'updated_at' => $now], + + // Для кошачьих кормов (id=27-32) + ['id' => 54, 'product_id' => 27, 'key' => 'Тип корма', 'value' => 'Сухой', 'created_at' => $now, 'updated_at' => $now], + ['id' => 55, 'product_id' => 27, 'key' => 'Особенности', 'value' => 'Для чувствительного пищеварения', 'created_at' => $now, 'updated_at' => $now], + ['id' => 56, 'product_id' => 28, 'key' => 'Тип корма', 'value' => 'Сухой', 'created_at' => $now, 'updated_at' => $now], + ['id' => 57, 'product_id' => 28, 'key' => 'Особенности', 'value' => 'Для домашних кошек, контроль веса', 'created_at' => $now, 'updated_at' => $now], + ['id' => 58, 'product_id' => 29, 'key' => 'Тип корма', 'value' => 'Сухой', 'created_at' => $now, 'updated_at' => $now], + ['id' => 59, 'product_id' => 29, 'key' => 'Особенности', 'value' => 'Для стерилизованных кошек', 'created_at' => $now, 'updated_at' => $now], + ['id' => 60, 'product_id' => 30, 'key' => 'Тип корма', 'value' => 'Сухой', 'created_at' => $now, 'updated_at' => $now], + ['id' => 61, 'product_id' => 30, 'key' => 'Особенности', 'value' => 'Гипоаллергенный', 'created_at' => $now, 'updated_at' => $now], + ['id' => 62, 'product_id' => 31, 'key' => 'Тип корма', 'value' => 'Сухой', 'created_at' => $now, 'updated_at' => $now], + ['id' => 63, 'product_id' => 31, 'key' => 'Вкус', 'value' => 'Курица', 'created_at' => $now, 'updated_at' => $now], + ['id' => 64, 'product_id' => 32, 'key' => 'Тип корма', 'value' => 'Сухой', 'created_at' => $now, 'updated_at' => $now], + ['id' => 65, 'product_id' => 32, 'key' => 'Особенности', 'value' => 'Для домашних кошек', 'created_at' => $now, 'updated_at' => $now], + + // Для ошейников (id=33-38) + ['id' => 66, 'product_id' => 33, 'key' => 'Материал', 'value' => 'Нейлон', 'created_at' => $now, 'updated_at' => $now], + ['id' => 67, 'product_id' => 33, 'key' => 'Особенности', 'value' => 'С ручкой для контроля', 'created_at' => $now, 'updated_at' => $now], + ['id' => 68, 'product_id' => 34, 'key' => 'Материал', 'value' => 'Натуральная кожа', 'created_at' => $now, 'updated_at' => $now], + ['id' => 69, 'product_id' => 35, 'key' => 'Материал', 'value' => 'Парашютная ткань', 'created_at' => $now, 'updated_at' => $now], + ['id' => 70, 'product_id' => 36, 'key' => 'Материал', 'value' => 'Усиленный нейлон', 'created_at' => $now, 'updated_at' => $now], + ['id' => 71, 'product_id' => 37, 'key' => 'Материал', 'value' => 'Натуральная кожа (плетеный)', 'created_at' => $now, 'updated_at' => $now], + ['id' => 72, 'product_id' => 38, 'key' => 'Материал', 'value' => 'Альпинистский нейлон', 'created_at' => $now, 'updated_at' => $now], + + // Для наполнителей (id=39-44) + ['id' => 73, 'product_id' => 39, 'key' => 'Тип наполнителя', 'value' => 'Древесный, комкующийся', 'created_at' => $now, 'updated_at' => $now], + ['id' => 74, 'product_id' => 40, 'key' => 'Тип наполнителя', 'value' => 'Древесный, целлюлозный', 'created_at' => $now, 'updated_at' => $now], + ['id' => 75, 'product_id' => 41, 'key' => 'Тип наполнителя', 'value' => 'Древесный (хвойные породы)', 'created_at' => $now, 'updated_at' => $now], + ['id' => 76, 'product_id' => 42, 'key' => 'Тип наполнителя', 'value' => 'Древесный, комкующийся', 'created_at' => $now, 'updated_at' => $now], + ['id' => 77, 'product_id' => 43, 'key' => 'Тип наполнителя', 'value' => 'Древесный премиум', 'created_at' => $now, 'updated_at' => $now], + ['id' => 78, 'product_id' => 44, 'key' => 'Тип наполнителя', 'value' => 'Древесный с травами', 'created_at' => $now, 'updated_at' => $now], + + // Для игрушек (id=45-50) + ['id' => 79, 'product_id' => 45, 'key' => 'Материал', 'value' => 'Натуральная резина', 'created_at' => $now, 'updated_at' => $now], + ['id' => 80, 'product_id' => 45, 'key' => 'Тип игрушки', 'value' => 'Для жевания, апортировки', 'created_at' => $now, 'updated_at' => $now], + ['id' => 81, 'product_id' => 46, 'key' => 'Материал', 'value' => 'Резина с войлоком', 'created_at' => $now, 'updated_at' => $now], + ['id' => 82, 'product_id' => 47, 'key' => 'Материал', 'value' => 'Пластик', 'created_at' => $now, 'updated_at' => $now], + ['id' => 83, 'product_id' => 47, 'key' => 'Тип игрушки', 'value' => 'Фрисби', 'created_at' => $now, 'updated_at' => $now], + ['id' => 84, 'product_id' => 48, 'key' => 'Материал', 'value' => 'Прочная резина', 'created_at' => $now, 'updated_at' => $now], + ['id' => 85, 'product_id' => 49, 'key' => 'Материал', 'value' => 'Резина', 'created_at' => $now, 'updated_at' => $now], + ['id' => 86, 'product_id' => 49, 'key' => 'Особенности', 'value' => 'Для метателя Chuckit', 'created_at' => $now, 'updated_at' => $now], + ['id' => 87, 'product_id' => 50, 'key' => 'Материал', 'value' => 'Резина', 'created_at' => $now, 'updated_at' => $now], + ['id' => 88, 'product_id' => 50, 'key' => 'Особенности', 'value' => 'Массаж десен', 'created_at' => $now, 'updated_at' => $now], + + // Для лежанок (id=51-56) + ['id' => 89, 'product_id' => 51, 'key' => 'Материал', 'value' => 'Memory foam', 'created_at' => $now, 'updated_at' => $now], + ['id' => 90, 'product_id' => 51, 'key' => 'Особенности', 'value' => 'Ортопедическая', 'created_at' => $now, 'updated_at' => $now], + ['id' => 91, 'product_id' => 52, 'key' => 'Материал', 'value' => 'Флис', 'created_at' => $now, 'updated_at' => $now], + ['id' => 92, 'product_id' => 52, 'key' => 'Особенности', 'value' => 'Съемный чехол', 'created_at' => $now, 'updated_at' => $now], + ['id' => 93, 'product_id' => 53, 'key' => 'Форма', 'value' => 'Круглая с бортиками', 'created_at' => $now, 'updated_at' => $now], + ['id' => 94, 'product_id' => 54, 'key' => 'Особенности', 'value' => 'Водоотталкивающая', 'created_at' => $now, 'updated_at' => $now], + ['id' => 95, 'product_id' => 55, 'key' => 'Материал', 'value' => 'Ортопедическая пена', 'created_at' => $now, 'updated_at' => $now], + ['id' => 96, 'product_id' => 55, 'key' => 'Особенности', 'value' => 'Гипоаллергенная', 'created_at' => $now, 'updated_at' => $now], + ['id' => 97, 'product_id' => 56, 'key' => 'Тип', 'value' => 'Лежанка-домик', 'created_at' => $now, 'updated_at' => $now], + ]); + + // ====================================================== + // АТРИБУТЫ ДЛЯ ВАРИАЦИЙ (variation_attributes) + // ====================================================== + DB::table('variation_attributes')->insert([ + // Вариации кормов собак + ['id' => 15, 'variation_id' => 41, 'key' => 'вес', 'value' => '2.7 кг', 'created_at' => $now, 'updated_at' => $now], + ['id' => 16, 'variation_id' => 41, 'key' => 'вкус', 'value' => 'ягненок', 'created_at' => $now, 'updated_at' => $now], + ['id' => 17, 'variation_id' => 42, 'key' => 'вес', 'value' => '11.2 кг', 'created_at' => $now, 'updated_at' => $now], + ['id' => 18, 'variation_id' => 42, 'key' => 'вкус', 'value' => 'ягненок', 'created_at' => $now, 'updated_at' => $now], + ['id' => 19, 'variation_id' => 43, 'key' => 'вес', 'value' => '0.5 кг', 'created_at' => $now, 'updated_at' => $now], + ['id' => 20, 'variation_id' => 44, 'key' => 'вес', 'value' => '2 кг', 'created_at' => $now, 'updated_at' => $now], + ['id' => 21, 'variation_id' => 45, 'key' => 'вес', 'value' => '2 кг', 'created_at' => $now, 'updated_at' => $now], + ['id' => 22, 'variation_id' => 46, 'key' => 'вес', 'value' => '4 кг', 'created_at' => $now, 'updated_at' => $now], + ['id' => 23, 'variation_id' => 47, 'key' => 'вес', 'value' => '3 кг', 'created_at' => $now, 'updated_at' => $now], + ['id' => 24, 'variation_id' => 48, 'key' => 'вес', 'value' => '12 кг', 'created_at' => $now, 'updated_at' => $now], + ['id' => 25, 'variation_id' => 49, 'key' => 'вес', 'value' => '3 кг', 'created_at' => $now, 'updated_at' => $now], + ['id' => 26, 'variation_id' => 50, 'key' => 'вес', 'value' => '15 кг', 'created_at' => $now, 'updated_at' => $now], + ['id' => 27, 'variation_id' => 51, 'key' => 'вес', 'value' => '2 кг', 'created_at' => $now, 'updated_at' => $now], + ['id' => 28, 'variation_id' => 52, 'key' => 'вес', 'value' => '12 кг', 'created_at' => $now, 'updated_at' => $now], + + // Вариации кормов кошек + ['id' => 29, 'variation_id' => 53, 'key' => 'вес', 'value' => '1 кг', 'created_at' => $now, 'updated_at' => $now], + ['id' => 30, 'variation_id' => 54, 'key' => 'вес', 'value' => '4 кг', 'created_at' => $now, 'updated_at' => $now], + ['id' => 31, 'variation_id' => 55, 'key' => 'вес', 'value' => '1.5 кг', 'created_at' => $now, 'updated_at' => $now], + ['id' => 32, 'variation_id' => 56, 'key' => 'вес', 'value' => '4 кг', 'created_at' => $now, 'updated_at' => $now], + ['id' => 33, 'variation_id' => 57, 'key' => 'вес', 'value' => '2 кг', 'created_at' => $now, 'updated_at' => $now], + ['id' => 34, 'variation_id' => 58, 'key' => 'вес', 'value' => '4 кг', 'created_at' => $now, 'updated_at' => $now], + ['id' => 35, 'variation_id' => 59, 'key' => 'вес', 'value' => '2 кг', 'created_at' => $now, 'updated_at' => $now], + ['id' => 36, 'variation_id' => 60, 'key' => 'вес', 'value' => '10 кг', 'created_at' => $now, 'updated_at' => $now], + ['id' => 37, 'variation_id' => 61, 'key' => 'вес', 'value' => '1.5 кг', 'created_at' => $now, 'updated_at' => $now], + ['id' => 38, 'variation_id' => 62, 'key' => 'вес', 'value' => '7 кг', 'created_at' => $now, 'updated_at' => $now], + ['id' => 39, 'variation_id' => 63, 'key' => 'вес', 'value' => '2 кг', 'created_at' => $now, 'updated_at' => $now], + ['id' => 40, 'variation_id' => 64, 'key' => 'вес', 'value' => '10 кг', 'created_at' => $now, 'updated_at' => $now], + + // Вариации ошейников + ['id' => 41, 'variation_id' => 65, 'key' => 'размер', 'value' => 'L (42-65 см)', 'created_at' => $now, 'updated_at' => $now], + ['id' => 42, 'variation_id' => 65, 'key' => 'цвет', 'value' => 'черный', 'created_at' => $now, 'updated_at' => $now], + ['id' => 43, 'variation_id' => 66, 'key' => 'размер', 'value' => 'M (38-48 см)', 'created_at' => $now, 'updated_at' => $now], + ['id' => 44, 'variation_id' => 66, 'key' => 'цвет', 'value' => 'черный', 'created_at' => $now, 'updated_at' => $now], + ['id' => 45, 'variation_id' => 67, 'key' => 'размер', 'value' => 'M (30-45 см)', 'created_at' => $now, 'updated_at' => $now], + ['id' => 46, 'variation_id' => 68, 'key' => 'размер', 'value' => 'L (40-60 см)', 'created_at' => $now, 'updated_at' => $now], + ['id' => 47, 'variation_id' => 69, 'key' => 'размер', 'value' => 'S (25-40 см)', 'created_at' => $now, 'updated_at' => $now], + ['id' => 48, 'variation_id' => 70, 'key' => 'размер', 'value' => 'M (35-55 см)', 'created_at' => $now, 'updated_at' => $now], + + // Вариации наполнителей + ['id' => 49, 'variation_id' => 76, 'key' => 'объем', 'value' => '10 л', 'created_at' => $now, 'updated_at' => $now], + ['id' => 50, 'variation_id' => 77, 'key' => 'объем', 'value' => '20 л', 'created_at' => $now, 'updated_at' => $now], + ['id' => 51, 'variation_id' => 78, 'key' => 'вес', 'value' => '6 кг', 'created_at' => $now, 'updated_at' => $now], + ['id' => 52, 'variation_id' => 79, 'key' => 'вес', 'value' => '17 кг', 'created_at' => $now, 'updated_at' => $now], + ['id' => 53, 'variation_id' => 80, 'key' => 'объем', 'value' => '5 л', 'created_at' => $now, 'updated_at' => $now], + ['id' => 54, 'variation_id' => 81, 'key' => 'объем', 'value' => '10 л', 'created_at' => $now, 'updated_at' => $now], + + // Вариации игрушек + ['id' => 55, 'variation_id' => 88, 'key' => 'размер', 'value' => 'S', 'created_at' => $now, 'updated_at' => $now], + ['id' => 56, 'variation_id' => 89, 'key' => 'размер', 'value' => 'M', 'created_at' => $now, 'updated_at' => $now], + ['id' => 57, 'variation_id' => 90, 'key' => 'размер', 'value' => 'стандарт', 'created_at' => $now, 'updated_at' => $now], + ['id' => 58, 'variation_id' => 91, 'key' => 'размер', 'value' => 'большой', 'created_at' => $now, 'updated_at' => $now], + + // Вариации лежанок + ['id' => 59, 'variation_id' => 100, 'key' => 'размер', 'value' => 'S (50x40 см)', 'created_at' => $now, 'updated_at' => $now], + ['id' => 60, 'variation_id' => 101, 'key' => 'размер', 'value' => 'M (70x50 см)', 'created_at' => $now, 'updated_at' => $now], + ['id' => 61, 'variation_id' => 102, 'key' => 'размер', 'value' => 'S (60x45 см)', 'created_at' => $now, 'updated_at' => $now], + ['id' => 62, 'variation_id' => 103, 'key' => 'размер', 'value' => 'L (90x65 см)', 'created_at' => $now, 'updated_at' => $now], + ]); + } +} diff --git a/database/seeders/RolesAndPermissionsSeeder.php b/database/seeders/RolesAndPermissionsSeeder.php new file mode 100644 index 0000000..a9046c0 --- /dev/null +++ b/database/seeders/RolesAndPermissionsSeeder.php @@ -0,0 +1,279 @@ +cleanTables(); + $this->createPermissions(); + $this->createRoles(); + $this->assignPermissionsToRoles(); + } + + private function cleanTables() + { + DB::statement('SET FOREIGN_KEY_CHECKS=0'); + DB::table('permission_role')->truncate(); + DB::table('role_user')->truncate(); + DB::table('permissions')->truncate(); + DB::table('roles')->truncate(); + DB::statement('SET FOREIGN_KEY_CHECKS=1'); + } + + private function createPermissions() + { + $permissions = [ + + // ========== ЛИЧНЫЙ КАБИНЕТ ========== + [ + 'name' => 'Доступ в личный кабинет', + 'slug' => 'cabinet_access', + 'group' => 'cabinet', + 'description' => 'Возможность входить в личный кабинет и просматривать свои данные' + ], + + // ========== ЗАКАЗЫ ========== + [ + 'name' => 'Просматривать заказы', + 'slug' => 'view_orders', + 'group' => 'orders', + 'description' => 'Просмотр списка заказов' + ], + [ + 'name' => 'Редактировать заказы', + 'slug' => 'edit_orders', + 'group' => 'orders', + 'description' => 'Редактирование заказов (статус, удаление, подтверждение)' + ], + [ + 'name' => 'Оформлять заказы', + 'slug' => 'checkout', + 'group' => 'orders', + 'description' => 'Оформление заказов на сайте' + ], + + // ========== КОРЗИНА ========== + [ + 'name' => 'Управлять корзиной', + 'slug' => 'manage_cart', + 'group' => 'cart', + 'description' => 'Добавление/удаление товаров из корзины' + ], + + // ========== ТОВАРЫ ========== + [ + 'name' => 'Добавлять товары', + 'slug' => 'create_products', + 'group' => 'products', + 'description' => 'Создание новых товаров' + ], + [ + 'name' => 'Редактировать товары', + 'slug' => 'edit_products', + 'group' => 'products', + 'description' => 'Редактирование товаров' + ], + [ + 'name' => 'Управлять количеством товара', + 'slug' => 'manage_stock', + 'group' => 'products', + 'description' => 'Изменение остатков товаров на складе' + ], + + // ========== ПОЛЬЗОВАТЕЛИ ========== + [ + 'name' => 'Просматривать пользователей', + 'slug' => 'view_users', + 'group' => 'users', + 'description' => 'Просмотр списка пользователей' + ], + [ + 'name' => 'Создавать пользователей', + 'slug' => 'create_users', + 'group' => 'users', + 'description' => 'Создание новых пользователей' + ], + + // ========== РОЛИ И ПРАВА ========== + [ + 'name' => 'Управлять ролями', + 'slug' => 'manage_roles', + 'group' => 'rbac', + 'description' => 'Создание/редактирование ролей' + ], + [ + 'name' => 'Управлять правами', + 'slug' => 'manage_permissions', + 'group' => 'rbac', + 'description' => 'Создание/редактирование прав доступа' + ], + + // ========== МАГАЗИН ========== + [ + 'name' => 'Редактировать данные магазина', + 'slug' => 'edit_shop_settings', + 'group' => 'shop', + 'description' => 'Изменение настроек магазина' + ], + + // ========== ДОСТУП В АДМИНКУ ========== + [ + 'name' => 'Доступ в админ-панель', + 'slug' => 'admin_access', + 'group' => 'admin', + 'description' => 'Возможность входить в административную панель' + ], + ]; + + foreach ($permissions as $permission) { + Permission::firstOrCreate( + ['slug' => $permission['slug']], + $permission + ); + $this->command->info("✓ Создано право: {$permission['name']}"); + } + } + + private function createRoles() + { + $roles = [ + [ + 'name' => 'Супер администратор', + 'slug' => 'super_admin', + 'description' => 'Полный доступ ко всем функциям системы (обходит все проверки прав)' + ], + [ + 'name' => 'Администратор магазина', + 'slug' => 'shop_admin', + 'description' => 'Полное управление магазином, товарами и заказами' + ], + [ + 'name' => 'Менеджер по товарам', + 'slug' => 'product_manager', + 'description' => 'Управление каталогом товаров' + ], + [ + 'name' => 'Менеджер по заказам', + 'slug' => 'order_manager', + 'description' => 'Обработка заказов' + ], + [ + 'name' => 'Кладовщик', + 'slug' => 'warehouse_manager', + 'description' => 'Управление остатками товаров' + ], + [ + 'name' => 'Зарегистрированный пользователь', + 'slug' => 'registered_user', + 'description' => 'Обычный пользователь сайта' + ], + ]; + + foreach ($roles as $roleData) { + Role::firstOrCreate( + ['slug' => $roleData['slug']], + $roleData + ); + $this->command->info("✓ Создана роль: {$roleData['name']}"); + } + } + + private function assignPermissionsToRoles() + { + // ============================================ + // АДМИНИСТРАТОР МАГАЗИНА + // ============================================ + + $shopAdmin = Role::where('slug', 'shop_admin')->first(); + $shopAdmin->permissions()->sync( + Permission::whereIn('slug', [ + 'admin_access', + 'cabinet_access', + 'view_orders', + 'edit_orders', + 'checkout', + 'manage_cart', + 'create_products', + 'edit_products', + 'manage_stock', + 'view_users', + 'create_users', + 'manage_roles', + 'manage_permissions', + 'edit_shop_settings', + ])->pluck('id') + ); + $this->command->info('✓ Назначены права для Администратора магазина'); + + // ============================================ + // МЕНЕДЖЕР ПО ТОВАРАМ + // ============================================ + + $productManager = Role::where('slug', 'product_manager')->first(); + $productManager->permissions()->sync( + Permission::whereIn('slug', [ + 'admin_access', + 'cabinet_access', + 'create_products', + 'edit_products', + 'view_orders', + ])->pluck('id') + ); + $this->command->info('✓ Назначены права для Менеджера по товарам'); + + // ============================================ + // МЕНЕДЖЕР ПО ЗАКАЗАМ + // ============================================ + $orderManager = Role::where('slug', 'order_manager')->first(); + $orderManager->permissions()->sync( + Permission::whereIn('slug', [ + 'admin_access', + 'cabinet_access', + 'view_orders', + 'edit_orders', + 'checkout', + 'view_users', + ])->pluck('id') + ); + $this->command->info('✓ Назначены права для Менеджера по заказам'); + + // ============================================ + // КЛАДОВЩИК + // ============================================ + $warehouseManager = Role::where('slug', 'warehouse_manager')->first(); + $warehouseManager->permissions()->sync( + Permission::whereIn('slug', [ + 'admin_access', + 'cabinet_access', + 'manage_stock', + 'view_orders', + ])->pluck('id') + ); + $this->command->info('✓ Назначены права для Кладовщика'); + + // ============================================ + // ЗАРЕГИСТРИРОВАННЫЙ ПОЛЬЗОВАТЕЛЬ + // ============================================ + $registeredUser = Role::where('slug', 'registered_user')->first(); + $registeredUser->permissions()->sync( + Permission::whereIn('slug', [ + 'cabinet_access', + 'manage_cart', + 'checkout', + ])->pluck('id') + ); + $this->command->info('✓ Назначены права для Зарегистрированного пользователя'); + } +} diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..fcd310b --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,60 @@ +services: + app: + build: + context: . + dockerfile: Dockerfile + container_name: tailandpaws_app + restart: unless-stopped + environment: + APP_ENV: production + APP_DEBUG: "false" + APP_URL: http://localhost + DB_CONNECTION: mysql + DB_HOST: db + DB_PORT: 3306 + DB_DATABASE: ${DB_DATABASE:-tailandpaws} + DB_USERNAME: ${DB_USERNAME:-laravel} + DB_PASSWORD: ${DB_PASSWORD:-secret} + CACHE_DRIVER: file + SESSION_DRIVER: file + QUEUE_CONNECTION: sync + volumes: + - storage_data:/var/www/html/storage/app + - logs_data:/var/www/html/storage/logs + ports: + - "8080:80" + depends_on: + db: + condition: service_healthy + networks: + - tailandpaws + + db: + image: mysql:8.0 + container_name: tailandpaws_db + restart: unless-stopped + environment: + MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:-rootsecret} + MYSQL_DATABASE: ${DB_DATABASE:-tailandpaws} + MYSQL_USER: ${DB_USERNAME:-laravel} + MYSQL_PASSWORD: ${DB_PASSWORD:-secret} + volumes: + - db_data:/var/lib/mysql + ports: + - "3306:3306" + healthcheck: + test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p${DB_ROOT_PASSWORD:-rootsecret}"] + interval: 10s + timeout: 5s + retries: 5 + networks: + - tailandpaws + +volumes: + db_data: + storage_data: + logs_data: + +networks: + tailandpaws: + driver: bridge diff --git a/docker/nginx/default.conf b/docker/nginx/default.conf new file mode 100644 index 0000000..455f143 --- /dev/null +++ b/docker/nginx/default.conf @@ -0,0 +1,29 @@ +server { + listen 80; + server_name _; + root /var/www/html/public; + index index.php; + + charset utf-8; + client_max_body_size 64M; + + location / { + try_files $uri $uri/ /index.php?$query_string; + } + + location = /favicon.ico { access_log off; log_not_found off; } + location = /robots.txt { access_log off; log_not_found off; } + + error_page 404 /index.php; + + location ~ \.php$ { + fastcgi_pass 127.0.0.1:9000; + fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; + include fastcgi_params; + fastcgi_hide_header X-Powered-By; + } + + location ~ /\.(?!well-known).* { + deny all; + } +} diff --git a/docker/php/php.ini b/docker/php/php.ini new file mode 100644 index 0000000..2ca8e70 --- /dev/null +++ b/docker/php/php.ini @@ -0,0 +1,12 @@ +upload_max_filesize = 64M +post_max_size = 64M +memory_limit = 256M +max_execution_time = 60 +expose_php = Off + +opcache.enable = 1 +opcache.memory_consumption = 128 +opcache.interned_strings_buffer = 8 +opcache.max_accelerated_files = 10000 +opcache.revalidate_freq = 60 +opcache.validate_timestamps = 0 diff --git a/docker/supervisord.conf b/docker/supervisord.conf new file mode 100644 index 0000000..5547ec0 --- /dev/null +++ b/docker/supervisord.conf @@ -0,0 +1,23 @@ +[supervisord] +nodaemon=true +user=root +logfile=/var/log/supervisord.log +pidfile=/var/run/supervisord.pid + +[program:php-fpm] +command=php-fpm +autostart=true +autorestart=true +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 + +[program:nginx] +command=nginx -g "daemon off;" +autostart=true +autorestart=true +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 diff --git a/isUniqueConstraintError($e)) b/isUniqueConstraintError($e)) new file mode 100644 index 0000000..e69de29 diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..2bc0240 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2552 @@ +{ + "name": "TailAndPaws", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "@popperjs/core": "^2.11.8", + "bootstrap": "^5.3.8", + "bootstrap-icons": "^1.13.1", + "sortablejs": "^1.15.7" + }, + "devDependencies": { + "autoprefixer": "^10.4.20", + "axios": "^1.7.9", + "laravel-vite-plugin": "^1.2.0", + "postcss": "^8.5.3", + "tailwindcss": "^3.4.17", + "vite": "^6.3.2" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz", + "integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz", + "integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz", + "integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz", + "integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz", + "integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz", + "integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz", + "integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz", + "integrity": "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz", + "integrity": "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz", + "integrity": "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz", + "integrity": "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz", + "integrity": "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz", + "integrity": "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz", + "integrity": "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz", + "integrity": "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz", + "integrity": "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz", + "integrity": "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz", + "integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz", + "integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz", + "integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz", + "integrity": "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz", + "integrity": "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz", + "integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz", + "integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz", + "integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true, + "license": "MIT" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/autoprefixer": { + "version": "10.4.27", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.27.tgz", + "integrity": "sha512-NP9APE+tO+LuJGn7/9+cohklunJsXWiaWEfV3si4Gi/XHDwVNgkwr1J3RQYFIvPy76GmJ9/bW8vyoU1LcxwKHA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.1", + "caniuse-lite": "^1.0.30001774", + "fraction.js": "^5.3.4", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/axios": { + "version": "1.13.6", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.6.tgz", + "integrity": "sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.11", + "form-data": "^4.0.5", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.8", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.8.tgz", + "integrity": "sha512-PCLz/LXGBsNTErbtB6i5u4eLpHeMfi93aUv5duMmj6caNu6IphS4q6UevDnL36sZQv9lrP11dbPKGMaXPwMKfQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bootstrap": { + "version": "5.3.8", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.8.tgz", + "integrity": "sha512-HP1SZDqaLDPwsNiqRqi5NcP0SSXciX2s9E+RyqJIIqGo+vJeN5AJVM98CXmW/Wux0nQ5L7jeWUdplCEf0Ee+tg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/twbs" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + } + ], + "license": "MIT", + "peerDependencies": { + "@popperjs/core": "^2.11.8" + } + }, + "node_modules/bootstrap-icons": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.13.1.tgz", + "integrity": "sha512-ijombt4v6bv5CLeXvRWKy7CuM3TRTuPEuGaGKvTV5cz65rQSY8RQ2JcHt6b90cBBAC7s8fsf2EkQDldzCoXUjw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/twbs" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + } + ], + "license": "MIT" + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001779", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001779.tgz", + "integrity": "sha512-U5og2PN7V4DMgF50YPNtnZJGWVLFjjsN3zb6uMT5VGYIewieDj1upwfuVNXf4Kor+89c3iCRJnSzMD5LmTvsfA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true, + "license": "MIT" + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.313", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.313.tgz", + "integrity": "sha512-QBMrTWEf00GXZmJyx2lbYD45jpI3TUFnNIzJ5BBc8piGUDwMPa1GV6HJWTZVvY/eiN3fSopl7NRbgGp9sZ9LTA==", + "dev": true, + "license": "ISC" + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fraction.js": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", + "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/laravel-vite-plugin": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/laravel-vite-plugin/-/laravel-vite-plugin-1.3.0.tgz", + "integrity": "sha512-P5qyG56YbYxM8OuYmK2OkhcKe0AksNVJUjq9LUZ5tOekU9fBn9LujYyctI4t9XoLjuMvHJXXpCoPntY1oKltuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picocolors": "^1.0.0", + "vite-plugin-full-reload": "^1.1.0" + }, + "bin": { + "clean-orphaned-assets": "bin/clean.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0" + } + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-releases": { + "version": "2.0.36", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.36.tgz", + "integrity": "sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss": { + "version": "8.5.8", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", + "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz", + "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", + "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.1.1" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "jiti": ">=1.21.0", + "postcss": ">=8.0.9", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true, + "license": "MIT" + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz", + "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.59.0", + "@rollup/rollup-android-arm64": "4.59.0", + "@rollup/rollup-darwin-arm64": "4.59.0", + "@rollup/rollup-darwin-x64": "4.59.0", + "@rollup/rollup-freebsd-arm64": "4.59.0", + "@rollup/rollup-freebsd-x64": "4.59.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.59.0", + "@rollup/rollup-linux-arm-musleabihf": "4.59.0", + "@rollup/rollup-linux-arm64-gnu": "4.59.0", + "@rollup/rollup-linux-arm64-musl": "4.59.0", + "@rollup/rollup-linux-loong64-gnu": "4.59.0", + "@rollup/rollup-linux-loong64-musl": "4.59.0", + "@rollup/rollup-linux-ppc64-gnu": "4.59.0", + "@rollup/rollup-linux-ppc64-musl": "4.59.0", + "@rollup/rollup-linux-riscv64-gnu": "4.59.0", + "@rollup/rollup-linux-riscv64-musl": "4.59.0", + "@rollup/rollup-linux-s390x-gnu": "4.59.0", + "@rollup/rollup-linux-x64-gnu": "4.59.0", + "@rollup/rollup-linux-x64-musl": "4.59.0", + "@rollup/rollup-openbsd-x64": "4.59.0", + "@rollup/rollup-openharmony-arm64": "4.59.0", + "@rollup/rollup-win32-arm64-msvc": "4.59.0", + "@rollup/rollup-win32-ia32-msvc": "4.59.0", + "@rollup/rollup-win32-x64-gnu": "4.59.0", + "@rollup/rollup-win32-x64-msvc": "4.59.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/sortablejs": { + "version": "1.15.7", + "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.15.7.tgz", + "integrity": "sha512-Kk8wLQPlS+yi1ZEf48a4+fzHa4yxjC30M/Sr2AnQu+f/MPwvvX9XjZ6OWejiz8crBsLwSq8GHqaxaET7u6ux0A==", + "license": "MIT" + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sucrase": { + "version": "3.35.1", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", + "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "tinyglobby": "^0.2.11", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tailwindcss": { + "version": "3.4.19", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz", + "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.6.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.7", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", + "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite-plugin-full-reload": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/vite-plugin-full-reload/-/vite-plugin-full-reload-1.2.0.tgz", + "integrity": "sha512-kz18NW79x0IHbxRSHm0jttP4zoO9P9gXh+n6UTwlNKnviTTEpOlum6oS9SmecrTtSr+muHEn5TUuC75UovQzcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picocolors": "^1.0.0", + "picomatch": "^2.3.1" + } + }, + "node_modules/vite/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..6b3b1a8 --- /dev/null +++ b/package.json @@ -0,0 +1,22 @@ +{ + "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build" + }, + "devDependencies": { + "autoprefixer": "^10.4.20", + "axios": "^1.7.9", + "laravel-vite-plugin": "^1.2.0", + "postcss": "^8.5.3", + "tailwindcss": "^3.4.17", + "vite": "^6.3.2" + }, + "dependencies": { + "@popperjs/core": "^2.11.8", + "bootstrap": "^5.3.8", + "bootstrap-icons": "^1.13.1", + "sortablejs": "^1.15.7" + } +} diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..bc86714 --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,32 @@ + + + + + tests/Unit + + + tests/Feature + + + + + app + + + + + + + + + + + + + + + diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 0000000..2e7af2b --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/prepareBindings($bindings) b/prepareBindings($bindings) new file mode 100644 index 0000000..e69de29 diff --git a/public/.htaccess b/public/.htaccess new file mode 100644 index 0000000..3aec5e2 --- /dev/null +++ b/public/.htaccess @@ -0,0 +1,21 @@ + + + Options -MultiViews -Indexes + + + RewriteEngine On + + # Handle Authorization Header + RewriteCond %{HTTP:Authorization} . + RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] + + # Redirect Trailing Slashes If Not A Folder... + RewriteCond %{REQUEST_FILENAME} !-d + RewriteCond %{REQUEST_URI} (.+)/$ + RewriteRule ^ %1 [L,R=301] + + # Send Requests To Front Controller... + RewriteCond %{REQUEST_FILENAME} !-d + RewriteCond %{REQUEST_FILENAME} !-f + RewriteRule ^ index.php [L] + diff --git a/public/assets/css/brand.css b/public/assets/css/brand.css new file mode 100644 index 0000000..be10714 --- /dev/null +++ b/public/assets/css/brand.css @@ -0,0 +1,8 @@ +.hover-shadow { + transition: transform 0.2s ease, box-shadow 0.2s ease; +} + +.hover-shadow:hover { + transform: translateY(-5px); + box-shadow: 0 8px 20px rgba(0, 0, 0, 0.1) !important; +} \ No newline at end of file diff --git a/public/assets/css/cart.css b/public/assets/css/cart.css new file mode 100644 index 0000000..8cd38c6 --- /dev/null +++ b/public/assets/css/cart.css @@ -0,0 +1,92 @@ +.cart-item { + transition: background-color 0.2s ease; +} + +.cart-item:hover { + background-color: #f8f9fa; +} + +.quantity-control { + background: #fff; + overflow: hidden; +} + +.quantity-control button { + background: none; + border: none; + font-size: 18px; + font-weight: 500; + cursor: pointer; + transition: background-color 0.2s ease; +} + +.quantity-control input { + text-align: center; + padding: 0; +} + +.quantity-control input:focus { + outline: none; + box-shadow: none; +} + +.quantity-control input::-webkit-inner-spin-button, +.quantity-control input::-webkit-outer-spin-button { + -webkit-appearance: none; + margin: 0; +} + +.remove-item { + opacity: 0.5; + transition: opacity 0.2s ease; +} + +.remove-item:hover { + opacity: 1; +} + +.btn-dark-green:disabled { + background-color: #6c757d; + cursor: not-allowed; +} + +.card { + border-radius: 12px; +} + +.item-total { + font-size: 16px; +} + +.btn-link { + font-size: 16pt; + text-decoration: none; + color: var(--dark-green); +} + +.btn-link:hover { + color: var(--dark-green); +} + +@media (max-width: 768px) { + .quantity-control { + width: 100px !important; + } + + .quantity-control button { + width: 28px !important; + } + + .quantity-control input { + width: 40px !important; + } + + .item-total { + font-size: 14px; + } + + .text-end { + width: 120px; + margin: 0 !important; + } +} \ No newline at end of file diff --git a/public/assets/css/category.css b/public/assets/css/category.css new file mode 100644 index 0000000..35db7b7 --- /dev/null +++ b/public/assets/css/category.css @@ -0,0 +1,103 @@ +.ui-slider { + height: 6px; + margin: 16px 16px 0; + border-radius: 3px; + background: #e9ecef; +} + +.ui-slider .noUi-background { + background: linear-gradient(90deg, #e9ecef 0%, #dee2e6 100%); + border-radius: 3px; +} + +.ui-slider .noUi-handle { + width: 22px; + height: 22px; + top: -8px; + right: -11px; + background: #fff; + border: 3px solid var(--dark-green); + border-radius: 50%; + box-shadow: + 0 4px 12px #3233293b, + 0 2px 4px rgba(0, 0, 0, 0.1), + inset 0 1px 0 rgba(255, 255, 255, 0.8); + cursor: pointer; +} + +.ui-slider .noUi-handle.noUi-active { + transform: scale(1.1) !important; + border-width: 4px; + border-color: var(--dark-green); + box-shadow: 0 0 0 4px #3233293b; +} + +.ui-slider .noUi-handle:before, +.ui-slider .noUi-handle:after { + display: none; +} + +.ui-slider.disabled { + opacity: 0.5; + pointer-events: none; +} + +.ui-slider.disabled .noUi-handle { + cursor: not-allowed; +} + +input:disabled { + background-color: #e9ecef; + cursor: not-allowed; +} + +.noUi-connect { + background: #fff; +} + +.filters .form-control:focus { + box-shadow: none; + border-color: var(---dark-green); +} + +.close-btn { + position: absolute; + top: 10px; + right: 20px; + background: none; + border: none; + color: white; + font-size: 2rem; + cursor: pointer; +} + +.form-check-input:checked { + background-color: var(--dark-green); + border-color: var(--dark-green); +} + +.form-check-input:focus, +.login-form .form-control:focus { + border-color: var(--dark-green); + outline: 0; + box-shadow: none; +} + + +@media (max-width: 767.98px) { + .filters.mobile-open { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.678); + z-index: 1050; + overflow-y: auto; + padding: 1rem; + } + + body.filters-open { + overflow: hidden; + } +} \ No newline at end of file diff --git a/public/assets/css/checkout.css b/public/assets/css/checkout.css new file mode 100644 index 0000000..71d0334 --- /dev/null +++ b/public/assets/css/checkout.css @@ -0,0 +1,28 @@ +.btn-dark-green:disabled { + background-color: #6c757d; + cursor: not-allowed; +} + +.btn-outline-mint { + background-color: transparent; + border: 1px solid #2c5e2e; + color: #2c5e2e; + border-radius: 8px; + transition: all 0.2s ease; +} + +.btn-outline-mint:hover { + background-color: #2c5e2e; + color: white; +} + +.form-control:focus, .form-check-input:focus { + border-color: var(--dark-green); + outline: 0; + box-shadow: 0 0 0 .25rem #2a2a2254; +} + +.form-check-input:checked { + background-color: var(--dark-green); + border-color: var(--dark-green); +} \ No newline at end of file diff --git a/public/assets/css/index.css b/public/assets/css/index.css new file mode 100644 index 0000000..92a8823 --- /dev/null +++ b/public/assets/css/index.css @@ -0,0 +1,153 @@ +/* Карусель с акциями */ +#carouselDiscount .carousel-inner { + height: 500px; + background-color: var(--orange); +} + +.carousel-discount-img { + background-color: var(--light-gray); + border-radius: 15px; + padding: 15px; + height: 100%; +} + +#carouselDiscount .carousel-item { + color: var(--light-gray); + height: 100% !important; + flex-shrink: 0; + width: 100%; + overflow: hidden; +} + +#carouselDiscount .carousel-item img { + width: 100%; + height: 100%; + object-fit: contain; +} + +.discount-badge { + width: 60px; + height: 60px; + display: flex; + align-items: center; + justify-content: center; + background-color: var(--dark-green); + border-radius: 100%; +} + +.carousel-price { + background-color: var(--light-gray); + color: var(--orange); + border-radius: 15px; + padding: 10px; +} + +.original-price { + text-decoration: line-through; + text-decoration-color: var(--dark-green); + text-decoration-thickness: 2px; + opacity: 0.8; + align-self: flex-end; +} + +.discounted-price { + align-self: center; +} + +/* Блок с товарами */ +.product-card { + height: 500px; +} + +/* Слайдер с продуктами */ +.carousel-indicators { + position: relative; +} + +/* Блок с брендами */ +.brand-circle { + display: flex; + justify-content: center; + align-items: center; + width: 100%; + padding: 10px; + background-color: var(--light-gray); + border-radius: 15px; +} + +.brand-circle img { + width: 80%; + height: 100%; + object-fit: contain; +} + + +@media (max-width: 991px) { + + /* Карусель на мобильных устройствах */ + #carouselDiscount .carousel-inner { + height: 500px; + } + + .carousel-discount-img { + height: 50%; + } + + #carouselDiscount .carousel-item h1 { + font-size: medium !important; + } + + #carouselDiscount .btn-dark-green { + padding: 5px !important; + font-size: 0.8rem !important; + } + + .discount-badge { + width: 40px; + height: 40px; + font-size: 0.7rem; + } + + .carousel-price { + padding: 0; + } + + .carousel-price div { + width: 70% !important; + padding: 5px; + } + + .original-price { + font-size: 0.8rem !important; + } + + .discounted-price { + font-size: 1rem !important; + } + + /* Карточки */ + .product-card { + height: 300px; + } + + .product-card .btn-dark-green { + padding: 5px !important; + font-size: 0.8rem !important; + } + + .card-text { + font-size: 0.7rem !important; + } + + .card-title h5 { + font-size: 0.8rem !important; + } +} + +@media (max-width: 576px) { + + /* Карточки */ + .product-card { + height: 250px; + } +} \ No newline at end of file diff --git a/public/assets/css/login.css b/public/assets/css/login.css new file mode 100644 index 0000000..533d690 --- /dev/null +++ b/public/assets/css/login.css @@ -0,0 +1,59 @@ +main { + flex: 1 0 auto; + display: flex; + flex-direction: column; + gap: 50px; + justify-content: center; + align-items: center; +} + +.form-check-input { + width: 15px !important; + height: 15px !important; + cursor: pointer; + margin: 0; +} + +.login-form .form-control, +input[type="submit"] { + border-radius: 5px; + height: 50px; + padding: 10px; +} + +.login-form { + min-height: 45vh; +} + +.form-check-input:checked { + background-color: var(--dark-green); + border-color: var(--dark-green); +} + +.form-check-input:focus, +.login-form .form-control:focus { + border-color: var(--dark-green); + outline: 0; + box-shadow: 0 0 0 .25rem #2a2a2254; +} + +@media (max-width: 991px) { + .login-form { + width: 100% !important; + min-height: 30vh; + } + + .login-form h3 { + font-size: 1.2rem !important; + } + + .login-form .form-control { + height: 40px; + padding: 5px; + } + + .form-check-input { + width: 18px !important; + height: 18px !important; + } +} \ No newline at end of file diff --git a/public/assets/css/menu.css b/public/assets/css/menu.css new file mode 100644 index 0000000..8b8b6cb --- /dev/null +++ b/public/assets/css/menu.css @@ -0,0 +1,719 @@ +* { + box-sizing: border-box; + font-family: 'Comic Relief', Arial, Helvetica, sans-serif; +} + +:root { + --dark-green: #323329; + --dark-green-hover: #2a2a22; + --light-gray: #E6E2DF; + --orange: #C47C4C; + --dark-orange: #b36b3c; + --action-btn-size: 44px; +} + +@font-face { + font-family: 'Comic Relief'; + src: url('../fonts/ComicRelief-Regular.ttf') format('truetype'); + font-weight: 400; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: 'Comic Relief'; + src: url('../fonts/ComicRelief-Bold.ttf') format('truetype'); + font-weight: 700; + font-style: bold; + font-display: swap; +} + +/* Основа */ + +body, +html { + height: 100%; + margin: 0; + display: flex; + flex-direction: column; +} + +body { + min-height: 130vh; +} + +main { + flex: 1 0 auto; + display: flex; + flex-direction: column; + gap: 50px; +} + +.alert-dark-green { + background-color: var(--dark-green); + color: var(--light-gray); +} + +/* Хэдер */ + +.navbar-brand, +.nav-link { + transition: transform 0.6s ease; + color: var(--light-gray); +} + +.nav-link { + font-size: 1.2rem; + position: relative; + text-align: center; +} + +.navbar-brand:hover, +.nav-link:hover { + transform: scale(1.05); +} + +.nav-link:hover, +.nav-link:focus { + color: rgb(199, 198, 197); +} + +.cart-count { + transform: translate(-50%, -50%); + min-width: 18px; + height: 18px; + line-height: 18px; + padding: 0 5px; + font-size: 10px; + font-weight: 600; + top: 20% !important; + left: 80% !important; +} + +@keyframes cartBump { + 0% { + transform: translate(-50%, -50%) scale(1); + } + + 50% { + transform: translate(-50%, -50%) scale(1.2); + } + + 100% { + transform: translate(-50%, -50%) scale(1); + } +} + +.cart-bump { + animation: cartBump 0.3s ease; +} + +.action-btn { + width: var(--action-btn-size); + height: var(--action-btn-size); + min-width: var(--action-btn-size); + min-height: var(--action-btn-size); + display: inline-flex; + align-items: center; + justify-content: center; + border-radius: 6px; + transition: all 0.3s ease; +} + +.auth-dropdown-menu .action-btn { + border: 1px solid var(--light-gray); +} + +.action-btn:hover { + transform: scale(1.05); + background-color: var(--dark-orange); +} + +.header-icon { + width: 24px !important; + height: 24px !important; + object-fit: contain; + display: inline-block; + flex-shrink: 0; +} + +#input_search:focus { + border-color: var(--orange); + box-shadow: 0 0 5px var(--orange); +} + +/* Мега-меню */ +.dropdown-megamenu { + position: static !important; +} + +.dropdown-icon { + display: inline-flex; + align-items: center; + margin-left: 3px; +} + +.dropdown-icon img { + width: 20px; + height: 20px; + transition: transform 0.5s ease; +} + +.dropdown-megamenu:hover .dropdown-icon img { + transform: rotate(180deg); +} + +.dropdown-megamenu>.nav-link::after { + content: ''; + position: absolute; + bottom: -20px; + left: 0; + width: 100%; + height: 20px; + background: transparent; +} + +.dropdown-megamenu .megamenu { + position: absolute; + top: 95%; + left: 0; + right: 0; + background: #ffffff; + border-radius: 0 0 12px 12px; + box-shadow: 0 15px 30px rgba(0, 0, 0, 0.2); + padding: 24px; + display: none; + z-index: 1050; + width: 100%; + border-top: 3px solid var(--orange); +} + +/* Добавляем невидимый мостик сверху меню */ +.dropdown-megamenu .megamenu::before { + content: ''; + position: absolute; + top: -20px; + left: 0; + width: 100%; + height: 20px; + background: transparent; +} + +/* Показываем меню при наведении */ +.dropdown-megamenu:hover .megamenu, +.dropdown-megamenu .megamenu:hover { + display: block; +} + +/* Анимация появления */ +.dropdown-megamenu .megamenu { + display: none; + animation: fadeIn 0.3s ease; +} + +@keyframes fadeIn { + from { + opacity: 0; + transform: translateY(-10px); + } + + to { + opacity: 1; + transform: translateY(0); + } +} + +/* Стили для контента мега-меню */ +.megamenu-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 24px; +} + +.category-column h5 { + color: var(--dark-green); + font-weight: 600; + margin-bottom: 12px; + font-size: 16px; +} + +.category-column ul { + list-style: none; + padding: 0; + margin: 0; +} + +.category-column li { + margin-bottom: 6px; +} + +.category-column a { + color: var(--dark-green); + text-decoration: none; + font-size: 14px; + padding: 4px 0; + display: block; + transition: all 0.2s; +} + +.category-column a:hover { + color: var(--orange); + padding-left: 8px; +} + +.category-image { + grid-column: 1 / -1; + text-align: center; + margin-bottom: 16px; +} + +.category-image img { + max-width: 100%; + height: 200px; + object-fit: cover; + border-radius: 8px; +} + +.text-dark-green { + color: var(--dark-green); +} + +/* Мегаменю пользователя */ +.header-main-menu { + flex: 1 1 auto; + min-width: 0; +} + +.header-right { + flex: 0 0 auto; + min-width: 280px; +} + +.header-search { + width: 100%; + max-width: 20vw; + min-width: 180px; + display: flex; + gap: 8px; +} + +.header-search input { + flex: 1 1 auto; + min-width: 0; + height: var(--action-btn-size); +} + +.header-search .action-btn { + flex-shrink: 0; +} + +.auth-dropdown { + position: relative; +} + +.auth-dropdown-menu { + position: absolute; + top: 100%; + right: 0; + display: none; + flex-direction: column; + gap: 8px; + padding-top: 10px; + z-index: 1050; +} + +.auth-dropdown:hover .auth-dropdown-menu { + display: flex; +} + +.auth-dropdown::before { + content: ""; + position: absolute; + top: 100%; + right: 0; + width: 100%; + height: 12px; + background: transparent; +} + +.user-mobile-menu { + display: flex; + gap: 8px; + align-items: center; +} + +/* Футер */ + +.footer-links { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(100px, 200px)); + justify-content: center; + align-items: center; +} + +.footer-link { + color: var(--light-gray); + text-decoration: none; + font-size: 14px; + transition: all 0.3s ease; + padding: 4px 0; + display: block; + width: 100%; + max-width: 300px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.footer-link:hover { + color: var(--orange); + transform: translateX(5px); +} + +.footer-section h3 { + color: var(--light-gray); + border-bottom: 2px solid var(--orange) !important; + font-size: 1.2rem; + padding-bottom: 0.5rem; + margin-bottom: 1rem; +} + +.card { + border-radius: 0px; + border: 1px solid var(--orange); + display: flex; + flex-direction: column; + justify-content: space-between; + gap: 5px; + padding: 10px; +} + +.card-body { + display: flex; + flex-direction: column; + justify-content: flex-start; +} + +.product-link { + display: block; + color: inherit; + text-decoration: none; + height: calc(100% - 60px); +} + +.product-link .card-body { + display: flex; + flex-direction: column; + justify-content: flex-start; + padding: 10px; +} + +.card-img { + width: 100%; + height: 50%; + object-fit: cover; +} + +.card-title { + height: 20%; + margin: 10px 0; +} + +.card-text { + flex-grow: 1; + margin: 0; +} + +.active>.page-link, +.page-link.active { + color: var(--light-gray); + background-color: var(--dark-green); + border-color: var(--dark-green); +} + +.page-link { + color: var(--dark-green); +} + +.page-link:hover { + color: var(--orange); +} + +.page-link:focus { + border-color: var(--dark-green); + outline: 0; + box-shadow: 0 0 0 .25rem #2a2a2254; + color: var(--dark-green); + background-color: #f8f9fa; +} + +.btn-close:focus { + border: none; + outline: 0; + box-shadow: none; +} + +/* Блок с категориями */ +.category-card { + background-size: cover; + background-position: center; + background-repeat: no-repeat; + height: 200px; + padding: 0; + border: none; +} + +.category-card .card-body { + display: flex; + justify-content: center; + align-items: center; + background-color: rgba(0, 0, 0, 0.5); + color: var(--light-gray); + transition: background-color 0.3s ease; +} + +.category-card:hover .card-body { + background-color: rgba(0, 0, 0, 0.7); +} + + +@media (max-width: 1399.98px) { + .header-search { + max-width: 25vw; + } + + .footer-section p, + .footer-section h3 { + text-align: center; + } + + .social-media { + justify-content: center !important; + } +} + +@media (max-width: 1199.98px) { + .header-search { + max-width: 30vw; + } +} + +@media (max-width: 991.98px) { + main { + gap: 30px; + } + + .dropdown-megamenu { + position: relative !important; + } + + .dropdown-megamenu .megamenu { + position: static; + box-shadow: none; + padding: 15px; + background: rgba(255, 255, 255, 0.1); + margin-top: 10px; + } + + .megamenu-grid { + grid-template-columns: 1fr; + } + + .category-column a { + color: var(--light-gray); + } + + .category-column a:hover { + color: var(--orange); + } + + .category-column h5 { + color: var(--light-gray); + } + + .navbar-brand img { + width: 60px; + height: 60px; + } + + .nav-link, + .footer-link { + font-size: 1rem !important; + } + + .header-right { + flex: 1; + min-width: 0; + width: 100%; + } + + .header-search { + max-width: none; + flex: 1; + min-width: 0; + } + + .header-search input { + width: 100% !important; + min-width: 0; + } +} + +@media (max-width: 767.98px) { + .header-right { + flex-wrap: wrap; + gap: 8px; + } + + .header-search { + flex: 1; + min-width: 0; + order: 1; + } + + .user-mobile-menu { + order: 2; + } + + .header-right:has(a:only-child) .header-search { + flex: 1; + } +} + +@media (max-width: 575.98px) { + .navbar-brand h3 { + font-size: 1rem !important; + } + + .navbar-brand img { + width: 50px; + height: 50px; + } + + .action-btn { + --action-btn-size: 40px; + } + + .header-search .action-btn { + flex-shrink: 0; + } + + .user-mobile-menu { + gap: 6px; + } + + .header-right { + gap: 6px; + } +} + +@media (max-width: 480px) { + + .navbar-brand h3 { + font-size: 0.9rem !important; + } + + .nav-link, + .footer-link { + font-size: 0.9rem !important; + } + + .footer-link { + text-align: center; + } + + .action-btn { + --action-btn-size: 38px; + } + + .header-icon { + width: 20px !important; + height: 20px !important; + } + + .dropdown-icon img { + width: 16px; + height: 16px; + } + + .header-search { + gap: 6px; + } +} + +@media (max-width: 375px) { + .action-btn { + --action-btn-size: 36px; + } + + .navbar-brand { + gap: 6px !important; + } + + .navbar-brand h3 { + font-size: 0.8rem !important; + } + + .user-mobile-menu { + gap: 4px; + } + + .header-right { + gap: 4px; + } +} + +@media (hover: none) and (pointer: coarse) { + .action-btn:hover { + transform: none; + } + + .dropdown-megamenu .megamenu { + display: none; + } + + .dropdown-megamenu.active .megamenu { + display: block; + } +} + +.action-btn { + position: relative; + overflow: hidden; +} + +.action-btn::after { + content: ''; + position: absolute; + top: 50%; + left: 50%; + width: 0; + height: 0; + border-radius: 50%; + background: rgba(255, 255, 255, 0.3); + transform: translate(-50%, -50%); + transition: width 0.3s ease, height 0.3s ease; +} + +.action-btn:active::after { + width: 100%; + height: 100%; +} + +::-webkit-scrollbar { + width: 8px; + height: 8px; +} + +::-webkit-scrollbar-track { + background: var(--light-gray); +} + +::-webkit-scrollbar-thumb { + background: var(--orange); + border-radius: 4px; +} + +::-webkit-scrollbar-thumb:hover { + background: var(--dark-orange); +} \ No newline at end of file diff --git a/public/assets/css/nouislider.min.css b/public/assets/css/nouislider.min.css new file mode 100644 index 0000000..60f217c --- /dev/null +++ b/public/assets/css/nouislider.min.css @@ -0,0 +1 @@ +.noUi-target,.noUi-target *{-webkit-touch-callout:none;-webkit-tap-highlight-color:transparent;-webkit-user-select:none;-ms-touch-action:none;touch-action:none;-ms-user-select:none;-moz-user-select:none;user-select:none;-moz-box-sizing:border-box;box-sizing:border-box}.noUi-target{position:relative}.noUi-base,.noUi-connects{width:100%;height:100%;position:relative;z-index:1}.noUi-connects{overflow:hidden;z-index:0}.noUi-connect,.noUi-origin{will-change:transform;position:absolute;z-index:1;top:0;right:0;height:100%;width:100%;-ms-transform-origin:0 0;-webkit-transform-origin:0 0;-webkit-transform-style:preserve-3d;transform-origin:0 0;transform-style:flat}.noUi-txt-dir-rtl.noUi-horizontal .noUi-origin{left:0;right:auto}.noUi-vertical .noUi-origin{top:-100%;width:0}.noUi-horizontal .noUi-origin{height:0}.noUi-handle{-webkit-backface-visibility:hidden;backface-visibility:hidden;position:absolute}.noUi-touch-area{height:100%;width:100%}.noUi-state-tap .noUi-connect,.noUi-state-tap .noUi-origin{-webkit-transition:transform .3s;transition:transform .3s}.noUi-state-drag *{cursor:inherit!important}.noUi-horizontal{height:18px}.noUi-horizontal .noUi-handle{width:34px;height:28px;right:-17px;top:-6px}.noUi-vertical{width:18px}.noUi-vertical .noUi-handle{width:28px;height:34px;right:-6px;bottom:-17px}.noUi-txt-dir-rtl.noUi-horizontal .noUi-handle{left:-17px;right:auto}.noUi-target{background:#FAFAFA;border-radius:4px;border:1px solid #D3D3D3;box-shadow:inset 0 1px 1px #F0F0F0,0 3px 6px -5px #BBB}.noUi-connects{border-radius:3px}.noUi-connect{background:#3FB8AF}.noUi-draggable{cursor:ew-resize}.noUi-vertical .noUi-draggable{cursor:ns-resize}.noUi-handle{border:1px solid #D9D9D9;border-radius:3px;background:#FFF;cursor:default;box-shadow:inset 0 0 1px #FFF,inset 0 1px 7px #EBEBEB,0 3px 6px -3px #BBB}.noUi-active{box-shadow:inset 0 0 1px #FFF,inset 0 1px 7px #DDD,0 3px 6px -3px #BBB}.noUi-handle:after,.noUi-handle:before{content:"";display:block;position:absolute;height:14px;width:1px;background:#E8E7E6;left:14px;top:6px}.noUi-handle:after{left:17px}.noUi-vertical .noUi-handle:after,.noUi-vertical .noUi-handle:before{width:14px;height:1px;left:6px;top:14px}.noUi-vertical .noUi-handle:after{top:17px}[disabled] .noUi-connect{background:#B8B8B8}[disabled] .noUi-handle,[disabled].noUi-handle,[disabled].noUi-target{cursor:not-allowed}.noUi-pips,.noUi-pips *{-moz-box-sizing:border-box;box-sizing:border-box}.noUi-pips{position:absolute;color:#999}.noUi-value{position:absolute;white-space:nowrap;text-align:center}.noUi-value-sub{color:#ccc;font-size:10px}.noUi-marker{position:absolute;background:#CCC}.noUi-marker-sub{background:#AAA}.noUi-marker-large{background:#AAA}.noUi-pips-horizontal{padding:10px 0;height:80px;top:100%;left:0;width:100%}.noUi-value-horizontal{-webkit-transform:translate(-50%,50%);transform:translate(-50%,50%)}.noUi-rtl .noUi-value-horizontal{-webkit-transform:translate(50%,50%);transform:translate(50%,50%)}.noUi-marker-horizontal.noUi-marker{margin-left:-1px;width:2px;height:5px}.noUi-marker-horizontal.noUi-marker-sub{height:10px}.noUi-marker-horizontal.noUi-marker-large{height:15px}.noUi-pips-vertical{padding:0 10px;height:100%;top:0;left:100%}.noUi-value-vertical{-webkit-transform:translate(0,-50%);transform:translate(0,-50%);padding-left:25px}.noUi-rtl .noUi-value-vertical{-webkit-transform:translate(0,50%);transform:translate(0,50%)}.noUi-marker-vertical.noUi-marker{width:5px;height:2px;margin-top:-1px}.noUi-marker-vertical.noUi-marker-sub{width:10px}.noUi-marker-vertical.noUi-marker-large{width:15px}.noUi-tooltip{display:block;position:absolute;border:1px solid #D9D9D9;border-radius:3px;background:#fff;color:#000;padding:5px;text-align:center;white-space:nowrap}.noUi-horizontal .noUi-tooltip{-webkit-transform:translate(-50%,0);transform:translate(-50%,0);left:50%;bottom:120%}.noUi-vertical .noUi-tooltip{-webkit-transform:translate(0,-50%);transform:translate(0,-50%);top:50%;right:120%}.noUi-horizontal .noUi-origin>.noUi-tooltip{-webkit-transform:translate(50%,0);transform:translate(50%,0);left:auto;bottom:10px}.noUi-vertical .noUi-origin>.noUi-tooltip{-webkit-transform:translate(0,-18px);transform:translate(0,-18px);top:auto;right:28px} \ No newline at end of file diff --git a/public/assets/css/product.css b/public/assets/css/product.css new file mode 100644 index 0000000..52d3944 --- /dev/null +++ b/public/assets/css/product.css @@ -0,0 +1,61 @@ +.product-gallery .main-image { + background-color: #f8f9fa; + border-radius: 8px; + overflow: hidden; +} + +.thumbnail-item { + border: 2px solid transparent; + border-radius: 8px; + transition: all 0.2s ease; +} + +.thumbnail-item.active { + border-color: var(--orange); +} + +.thumbnail-item:hover { + border-color: var(--orange); +} + +.btn-dark-green:disabled { + background-color: #6c757d; + cursor: not-allowed; +} + +.delivery-method { + background-color: #f8f9fa; + transition: all 0.2s ease; + cursor: pointer; +} + +.delivery-method:hover { + background-color: #e9ecef; +} + +.variation-btn { + width: 100%; + padding: 15px; +} + +.variation-btn.active { + background-color: var(--orange); + border-color: var(--orange); + color: white; +} + +.btn-link { + font-size: 16pt; + text-decoration: none; + color: var(--dark-green); +} + +.btn-link:hover { + color: var(--dark-green); +} + +.quantity-selector input::-webkit-inner-spin-button, +.quantity-selector input::-webkit-outer-spin-button { + -webkit-appearance: none; + margin: 0; +} \ No newline at end of file diff --git a/public/assets/css/profile.css b/public/assets/css/profile.css new file mode 100644 index 0000000..6c3fa97 --- /dev/null +++ b/public/assets/css/profile.css @@ -0,0 +1,76 @@ +/* Сайдбар */ + +aside { + min-height: 100%; +} + +.nav-link { + text-align: left; +} + +/* Таблицы */ + +td { + vertical-align: middle; +} + +.form-check-input:checked { + background-color: var(--dark-green); + border-color: var(--dark-green); +} + +.form-check-input:focus, +.table .form-control:focus, +.edit-brand .form-control:focus, +.card-body .form-control:focus, +.card-body .form-select:focus { + border-color: var(--dark-green) !important; + outline: 0 !important; + box-shadow: 0 0 0 .25rem #2a2a2254 !important; +} + +#search:focus { + border-color: var(--dark-green) !important; + outline: 0 !important; + box-shadow: 0 0 0 .25rem #2a2a2254 !important; +} + +/* Кнопки */ + +.btn-dark-green:hover { + color: var(--light-gray); +} + +.nav-item[role="presentation"] .nav-link.active { + background-color: var(--dark-green); + color: var(--light-gray); +} + +.nav-item[role="presentation"] .nav-link { + transform: scale(1); +} + +/* В ваш основной CSS файл */ +.category-name { + font-size: 14px; + line-height: 1.4; +} + +.table tbody tr:hover { + background-color: rgba(0, 0, 0, 0.02); +} + +.category-name .text-muted { + font-family: monospace; + font-size: 12px; + color: #adb5bd !important; +} + +@media (max-width: 991px) { + + /* Сайдбар */ + + aside { + min-height: auto; + } +} \ No newline at end of file diff --git a/public/assets/fonts/ComicRelief-Bold.ttf b/public/assets/fonts/ComicRelief-Bold.ttf new file mode 100644 index 0000000..7b86246 Binary files /dev/null and b/public/assets/fonts/ComicRelief-Bold.ttf differ diff --git a/public/assets/fonts/ComicRelief-Regular.ttf b/public/assets/fonts/ComicRelief-Regular.ttf new file mode 100644 index 0000000..d49aabc Binary files /dev/null and b/public/assets/fonts/ComicRelief-Regular.ttf differ diff --git a/public/assets/images/brands/Royal-Canin-Logo.png b/public/assets/images/brands/Royal-Canin-Logo.png new file mode 100644 index 0000000..c74c2f5 Binary files /dev/null and b/public/assets/images/brands/Royal-Canin-Logo.png differ diff --git a/public/assets/images/brands/abba.png b/public/assets/images/brands/abba.png new file mode 100644 index 0000000..d8aeb58 Binary files /dev/null and b/public/assets/images/brands/abba.png differ diff --git a/public/assets/images/brands/alphapet.png b/public/assets/images/brands/alphapet.png new file mode 100644 index 0000000..22a8d95 Binary files /dev/null and b/public/assets/images/brands/alphapet.png differ diff --git a/public/assets/images/brands/grandin.png b/public/assets/images/brands/grandin.png new file mode 100644 index 0000000..d212f00 Binary files /dev/null and b/public/assets/images/brands/grandin.png differ diff --git a/public/assets/images/brands/grandorf.jpg b/public/assets/images/brands/grandorf.jpg new file mode 100644 index 0000000..eb28893 Binary files /dev/null and b/public/assets/images/brands/grandorf.jpg differ diff --git a/public/assets/images/brands/klicker.png b/public/assets/images/brands/klicker.png new file mode 100644 index 0000000..567224d Binary files /dev/null and b/public/assets/images/brands/klicker.png differ diff --git a/public/assets/images/brands/little-one.jpg b/public/assets/images/brands/little-one.jpg new file mode 100644 index 0000000..e398764 Binary files /dev/null and b/public/assets/images/brands/little-one.jpg differ diff --git a/public/assets/images/brands/ownat.png b/public/assets/images/brands/ownat.png new file mode 100644 index 0000000..5b295e9 Binary files /dev/null and b/public/assets/images/brands/ownat.png differ diff --git a/public/assets/images/brands/rogz.png b/public/assets/images/brands/rogz.png new file mode 100644 index 0000000..3f68e03 Binary files /dev/null and b/public/assets/images/brands/rogz.png differ diff --git a/public/assets/images/brands/rungo.jpeg b/public/assets/images/brands/rungo.jpeg new file mode 100644 index 0000000..426c5e5 Binary files /dev/null and b/public/assets/images/brands/rungo.jpeg differ diff --git a/public/assets/images/brands/rurri.png b/public/assets/images/brands/rurri.png new file mode 100644 index 0000000..60c8c64 Binary files /dev/null and b/public/assets/images/brands/rurri.png differ diff --git a/public/assets/images/brands/tetra.jpg b/public/assets/images/brands/tetra.jpg new file mode 100644 index 0000000..607409a Binary files /dev/null and b/public/assets/images/brands/tetra.jpg differ diff --git a/public/assets/images/brands/triol.png b/public/assets/images/brands/triol.png new file mode 100644 index 0000000..8ef9abd Binary files /dev/null and b/public/assets/images/brands/triol.png differ diff --git a/public/assets/images/categories/icons/default.svg b/public/assets/images/categories/icons/default.svg new file mode 100644 index 0000000..3335599 --- /dev/null +++ b/public/assets/images/categories/icons/default.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/assets/images/categories/icons/dlya-gryzunov.svg b/public/assets/images/categories/icons/dlya-gryzunov.svg new file mode 100644 index 0000000..b61337f --- /dev/null +++ b/public/assets/images/categories/icons/dlya-gryzunov.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/public/assets/images/categories/icons/dlya-koshek.svg b/public/assets/images/categories/icons/dlya-koshek.svg new file mode 100644 index 0000000..06f15b5 --- /dev/null +++ b/public/assets/images/categories/icons/dlya-koshek.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/public/assets/images/categories/icons/dlya-ptic.svg b/public/assets/images/categories/icons/dlya-ptic.svg new file mode 100644 index 0000000..b2dbd96 --- /dev/null +++ b/public/assets/images/categories/icons/dlya-ptic.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/public/assets/images/categories/icons/dlya-ryb.svg b/public/assets/images/categories/icons/dlya-ryb.svg new file mode 100644 index 0000000..384ba78 --- /dev/null +++ b/public/assets/images/categories/icons/dlya-ryb.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/public/assets/images/categories/icons/dlya-sobak.svg b/public/assets/images/categories/icons/dlya-sobak.svg new file mode 100644 index 0000000..5705e81 --- /dev/null +++ b/public/assets/images/categories/icons/dlya-sobak.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/assets/images/categories/icons/yvavyavy.png b/public/assets/images/categories/icons/yvavyavy.png new file mode 100644 index 0000000..60c8c64 Binary files /dev/null and b/public/assets/images/categories/icons/yvavyavy.png differ diff --git a/public/assets/images/categories/icons/zdorovie-i-uhod.svg b/public/assets/images/categories/icons/zdorovie-i-uhod.svg new file mode 100644 index 0000000..ef6c6b7 --- /dev/null +++ b/public/assets/images/categories/icons/zdorovie-i-uhod.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/public/assets/images/categories/images/aptecka_image.jpg b/public/assets/images/categories/images/aptecka_image.jpg new file mode 100644 index 0000000..0302443 Binary files /dev/null and b/public/assets/images/categories/images/aptecka_image.jpg differ diff --git a/public/assets/images/categories/images/default.svg b/public/assets/images/categories/images/default.svg new file mode 100644 index 0000000..d7d26d0 --- /dev/null +++ b/public/assets/images/categories/images/default.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/public/assets/images/categories/images/dlia-gryzunov_image.jpg b/public/assets/images/categories/images/dlia-gryzunov_image.jpg new file mode 100644 index 0000000..b2494e7 Binary files /dev/null and b/public/assets/images/categories/images/dlia-gryzunov_image.jpg differ diff --git a/public/assets/images/categories/images/dlia-kosek_image.jpg b/public/assets/images/categories/images/dlia-kosek_image.jpg new file mode 100644 index 0000000..cca9f05 Binary files /dev/null and b/public/assets/images/categories/images/dlia-kosek_image.jpg differ diff --git a/public/assets/images/categories/images/dlia-ptic_image.jpg b/public/assets/images/categories/images/dlia-ptic_image.jpg new file mode 100644 index 0000000..c12e885 Binary files /dev/null and b/public/assets/images/categories/images/dlia-ptic_image.jpg differ diff --git a/public/assets/images/categories/images/dlia-ryb_image.jpg b/public/assets/images/categories/images/dlia-ryb_image.jpg new file mode 100644 index 0000000..266512a Binary files /dev/null and b/public/assets/images/categories/images/dlia-ryb_image.jpg differ diff --git a/public/assets/images/categories/images/dlia-sobak_image.jpg b/public/assets/images/categories/images/dlia-sobak_image.jpg new file mode 100644 index 0000000..30582bd Binary files /dev/null and b/public/assets/images/categories/images/dlia-sobak_image.jpg differ diff --git a/public/assets/images/categories/images/kletki-i-zerdocki_image.jpg b/public/assets/images/categories/images/kletki-i-zerdocki_image.jpg new file mode 100644 index 0000000..0f14888 Binary files /dev/null and b/public/assets/images/categories/images/kletki-i-zerdocki_image.jpg differ diff --git a/public/assets/images/categories/images/korma-dlia-popugaev_image.jpg b/public/assets/images/categories/images/korma-dlia-popugaev_image.jpg new file mode 100644 index 0000000..0d3dc98 Binary files /dev/null and b/public/assets/images/categories/images/korma-dlia-popugaev_image.jpg differ diff --git a/public/assets/images/categories/images/korma_image.jpg b/public/assets/images/categories/images/korma_image.jpg new file mode 100644 index 0000000..4528597 Binary files /dev/null and b/public/assets/images/categories/images/korma_image.jpg differ diff --git a/public/assets/images/categories/images/lakomstva_image.jpg b/public/assets/images/categories/images/lakomstva_image.jpg new file mode 100644 index 0000000..2a58983 Binary files /dev/null and b/public/assets/images/categories/images/lakomstva_image.jpg differ diff --git a/public/assets/images/categories/images/oseiniki-i-povodki_image.jpg b/public/assets/images/categories/images/oseiniki-i-povodki_image.jpg new file mode 100644 index 0000000..75b3e48 Binary files /dev/null and b/public/assets/images/categories/images/oseiniki-i-povodki_image.jpg differ diff --git a/public/assets/images/categories/images/pauci-i-konservy_image.jpg b/public/assets/images/categories/images/pauci-i-konservy_image.jpg new file mode 100644 index 0000000..a72d83e Binary files /dev/null and b/public/assets/images/categories/images/pauci-i-konservy_image.jpg differ diff --git a/public/assets/images/categories/images/suxie-korma_image.jpg b/public/assets/images/categories/images/suxie-korma_image.jpg new file mode 100644 index 0000000..673cbf5 Binary files /dev/null and b/public/assets/images/categories/images/suxie-korma_image.jpg differ diff --git a/public/assets/images/categories/images/vitaminy-i-dobavki_image.jpg b/public/assets/images/categories/images/vitaminy-i-dobavki_image.jpg new file mode 100644 index 0000000..672b629 Binary files /dev/null and b/public/assets/images/categories/images/vitaminy-i-dobavki_image.jpg differ diff --git a/public/assets/images/categories/images/vlaznye-korma_image.jpg b/public/assets/images/categories/images/vlaznye-korma_image.jpg new file mode 100644 index 0000000..9ab9714 Binary files /dev/null and b/public/assets/images/categories/images/vlaznye-korma_image.jpg differ diff --git a/public/assets/images/categories/images/zdorove-i-uxod_image.jpg b/public/assets/images/categories/images/zdorove-i-uxod_image.jpg new file mode 100644 index 0000000..18e7ecb Binary files /dev/null and b/public/assets/images/categories/images/zdorove-i-uxod_image.jpg differ diff --git a/public/assets/images/icons/add-folder-svgrepo-com.svg b/public/assets/images/icons/add-folder-svgrepo-com.svg new file mode 100644 index 0000000..581d448 --- /dev/null +++ b/public/assets/images/icons/add-folder-svgrepo-com.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/public/assets/images/icons/cart-svgrepo-com.svg b/public/assets/images/icons/cart-svgrepo-com.svg new file mode 100644 index 0000000..44d33a3 --- /dev/null +++ b/public/assets/images/icons/cart-svgrepo-com.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/public/assets/images/icons/copy-svgrepo-com.svg b/public/assets/images/icons/copy-svgrepo-com.svg new file mode 100644 index 0000000..4e21593 --- /dev/null +++ b/public/assets/images/icons/copy-svgrepo-com.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/public/assets/images/icons/delete-svgrepo-com.svg b/public/assets/images/icons/delete-svgrepo-com.svg new file mode 100644 index 0000000..aef0f2f --- /dev/null +++ b/public/assets/images/icons/delete-svgrepo-com.svg @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/public/assets/images/icons/dropdown-arrow-svgrepo-com.svg b/public/assets/images/icons/dropdown-arrow-svgrepo-com.svg new file mode 100644 index 0000000..e34c6c9 --- /dev/null +++ b/public/assets/images/icons/dropdown-arrow-svgrepo-com.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/public/assets/images/icons/edit-svgrepo-com.svg b/public/assets/images/icons/edit-svgrepo-com.svg new file mode 100644 index 0000000..6348748 --- /dev/null +++ b/public/assets/images/icons/edit-svgrepo-com.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/public/assets/images/icons/enter-svgrepo-com.svg b/public/assets/images/icons/enter-svgrepo-com.svg new file mode 100644 index 0000000..19dca1b --- /dev/null +++ b/public/assets/images/icons/enter-svgrepo-com.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/public/assets/images/icons/logout-svgrepo-com.svg b/public/assets/images/icons/logout-svgrepo-com.svg new file mode 100644 index 0000000..50ae40f --- /dev/null +++ b/public/assets/images/icons/logout-svgrepo-com.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/public/assets/images/icons/menu-svgrepo-com.svg b/public/assets/images/icons/menu-svgrepo-com.svg new file mode 100644 index 0000000..4f87961 --- /dev/null +++ b/public/assets/images/icons/menu-svgrepo-com.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/public/assets/images/icons/save-svgrepo-com.svg b/public/assets/images/icons/save-svgrepo-com.svg new file mode 100644 index 0000000..10cbd5a --- /dev/null +++ b/public/assets/images/icons/save-svgrepo-com.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/public/assets/images/icons/search-svgrepo-com.svg b/public/assets/images/icons/search-svgrepo-com.svg new file mode 100644 index 0000000..b8747ac --- /dev/null +++ b/public/assets/images/icons/search-svgrepo-com.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/public/assets/images/icons/settings-svgrepo-com.svg b/public/assets/images/icons/settings-svgrepo-com.svg new file mode 100644 index 0000000..6e24f0f --- /dev/null +++ b/public/assets/images/icons/settings-svgrepo-com.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/public/assets/images/icons/social_media/telegram-svgrepo-com.svg b/public/assets/images/icons/social_media/telegram-svgrepo-com.svg new file mode 100644 index 0000000..87145e8 --- /dev/null +++ b/public/assets/images/icons/social_media/telegram-svgrepo-com.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/public/assets/images/icons/social_media/vk-svgrepo-com.svg b/public/assets/images/icons/social_media/vk-svgrepo-com.svg new file mode 100644 index 0000000..1a75a1a --- /dev/null +++ b/public/assets/images/icons/social_media/vk-svgrepo-com.svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/public/assets/images/icons/social_media/whatsapp-svgrepo-com.svg b/public/assets/images/icons/social_media/whatsapp-svgrepo-com.svg new file mode 100644 index 0000000..eb824ba --- /dev/null +++ b/public/assets/images/icons/social_media/whatsapp-svgrepo-com.svg @@ -0,0 +1,7 @@ + + + + + + whatsapp [#E6E2DF] Created with Sketch. + \ No newline at end of file diff --git a/public/assets/images/icons/star-alt-4-svgrepo-com.svg b/public/assets/images/icons/star-alt-4-svgrepo-com.svg new file mode 100644 index 0000000..05db69a --- /dev/null +++ b/public/assets/images/icons/star-alt-4-svgrepo-com.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/public/assets/images/icons/user-block-alt-svgrepo-com.svg b/public/assets/images/icons/user-block-alt-svgrepo-com.svg new file mode 100644 index 0000000..4400852 --- /dev/null +++ b/public/assets/images/icons/user-block-alt-svgrepo-com.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/public/assets/images/icons/user-pen-alt-svgrepo-com.svg b/public/assets/images/icons/user-pen-alt-svgrepo-com.svg new file mode 100644 index 0000000..543ae3e --- /dev/null +++ b/public/assets/images/icons/user-pen-alt-svgrepo-com.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/public/assets/images/icons/user-svgrepo-com.svg b/public/assets/images/icons/user-svgrepo-com.svg new file mode 100644 index 0000000..0177add --- /dev/null +++ b/public/assets/images/icons/user-svgrepo-com.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/public/assets/images/logo/favicon.svg b/public/assets/images/logo/favicon.svg new file mode 100644 index 0000000..7e98584 --- /dev/null +++ b/public/assets/images/logo/favicon.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/public/assets/images/logo/logo.svg b/public/assets/images/logo/logo.svg new file mode 100644 index 0000000..7e98584 --- /dev/null +++ b/public/assets/images/logo/logo.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/public/assets/images/products/alphapet-adult-monoprotein-suxoi-korm-dlia-sobak-srednix-i-krupnyx-porod-belaia-ryba/variation_49/0.webp b/public/assets/images/products/alphapet-adult-monoprotein-suxoi-korm-dlia-sobak-srednix-i-krupnyx-porod-belaia-ryba/variation_49/0.webp new file mode 100644 index 0000000..edbc39c Binary files /dev/null and b/public/assets/images/products/alphapet-adult-monoprotein-suxoi-korm-dlia-sobak-srednix-i-krupnyx-porod-belaia-ryba/variation_49/0.webp differ diff --git a/public/assets/images/products/alphapet-adult-monoprotein-suxoi-korm-dlia-sobak-srednix-i-krupnyx-porod-belaia-ryba/variation_49/1.webp b/public/assets/images/products/alphapet-adult-monoprotein-suxoi-korm-dlia-sobak-srednix-i-krupnyx-porod-belaia-ryba/variation_49/1.webp new file mode 100644 index 0000000..a900dda Binary files /dev/null and b/public/assets/images/products/alphapet-adult-monoprotein-suxoi-korm-dlia-sobak-srednix-i-krupnyx-porod-belaia-ryba/variation_49/1.webp differ diff --git a/public/assets/images/products/alphapet-adult-monoprotein-suxoi-korm-dlia-sobak-srednix-i-krupnyx-porod-belaia-ryba/variation_50/0.webp b/public/assets/images/products/alphapet-adult-monoprotein-suxoi-korm-dlia-sobak-srednix-i-krupnyx-porod-belaia-ryba/variation_50/0.webp new file mode 100644 index 0000000..188a2f7 Binary files /dev/null and b/public/assets/images/products/alphapet-adult-monoprotein-suxoi-korm-dlia-sobak-srednix-i-krupnyx-porod-belaia-ryba/variation_50/0.webp differ diff --git a/public/assets/images/products/alphapet-adult-monoprotein-suxoi-korm-dlia-sobak-srednix-i-krupnyx-porod-belaia-ryba/variation_50/1.webp b/public/assets/images/products/alphapet-adult-monoprotein-suxoi-korm-dlia-sobak-srednix-i-krupnyx-porod-belaia-ryba/variation_50/1.webp new file mode 100644 index 0000000..3b81f4e Binary files /dev/null and b/public/assets/images/products/alphapet-adult-monoprotein-suxoi-korm-dlia-sobak-srednix-i-krupnyx-porod-belaia-ryba/variation_50/1.webp differ diff --git a/public/assets/images/products/alphapet-suxoi-korm-dlia-sterilizovannyx-kosek/variation_127/0.webp b/public/assets/images/products/alphapet-suxoi-korm-dlia-sterilizovannyx-kosek/variation_127/0.webp new file mode 100644 index 0000000..4a9d5b9 Binary files /dev/null and b/public/assets/images/products/alphapet-suxoi-korm-dlia-sterilizovannyx-kosek/variation_127/0.webp differ diff --git a/public/assets/images/products/alphapet-suxoi-korm-dlia-sterilizovannyx-kosek/variation_127/1.webp b/public/assets/images/products/alphapet-suxoi-korm-dlia-sterilizovannyx-kosek/variation_127/1.webp new file mode 100644 index 0000000..8834dd9 Binary files /dev/null and b/public/assets/images/products/alphapet-suxoi-korm-dlia-sterilizovannyx-kosek/variation_127/1.webp differ diff --git a/public/assets/images/products/alphapet-suxoi-korm-dlia-sterilizovannyx-kosek/variation_127/2.webp b/public/assets/images/products/alphapet-suxoi-korm-dlia-sterilizovannyx-kosek/variation_127/2.webp new file mode 100644 index 0000000..9c19831 Binary files /dev/null and b/public/assets/images/products/alphapet-suxoi-korm-dlia-sterilizovannyx-kosek/variation_127/2.webp differ diff --git a/public/assets/images/products/alphapet-suxoi-korm-dlia-sterilizovannyx-kosek/variation_59/0.webp b/public/assets/images/products/alphapet-suxoi-korm-dlia-sterilizovannyx-kosek/variation_59/0.webp new file mode 100644 index 0000000..4a9d5b9 Binary files /dev/null and b/public/assets/images/products/alphapet-suxoi-korm-dlia-sterilizovannyx-kosek/variation_59/0.webp differ diff --git a/public/assets/images/products/alphapet-suxoi-korm-dlia-sterilizovannyx-kosek/variation_59/1.webp b/public/assets/images/products/alphapet-suxoi-korm-dlia-sterilizovannyx-kosek/variation_59/1.webp new file mode 100644 index 0000000..8834dd9 Binary files /dev/null and b/public/assets/images/products/alphapet-suxoi-korm-dlia-sterilizovannyx-kosek/variation_59/1.webp differ diff --git a/public/assets/images/products/alphapet-suxoi-korm-dlia-sterilizovannyx-kosek/variation_59/2.webp b/public/assets/images/products/alphapet-suxoi-korm-dlia-sterilizovannyx-kosek/variation_59/2.webp new file mode 100644 index 0000000..9c19831 Binary files /dev/null and b/public/assets/images/products/alphapet-suxoi-korm-dlia-sterilizovannyx-kosek/variation_59/2.webp differ diff --git a/public/assets/images/products/alphapet-suxoi-korm-dlia-sterilizovannyx-kosek/variation_59/3.webp b/public/assets/images/products/alphapet-suxoi-korm-dlia-sterilizovannyx-kosek/variation_59/3.webp new file mode 100644 index 0000000..4a9d5b9 Binary files /dev/null and b/public/assets/images/products/alphapet-suxoi-korm-dlia-sterilizovannyx-kosek/variation_59/3.webp differ diff --git a/public/assets/images/products/alphapet-suxoi-korm-dlia-sterilizovannyx-kosek/variation_59/4.webp b/public/assets/images/products/alphapet-suxoi-korm-dlia-sterilizovannyx-kosek/variation_59/4.webp new file mode 100644 index 0000000..8834dd9 Binary files /dev/null and b/public/assets/images/products/alphapet-suxoi-korm-dlia-sterilizovannyx-kosek/variation_59/4.webp differ diff --git a/public/assets/images/products/alphapet-suxoi-korm-dlia-sterilizovannyx-kosek/variation_59/5.webp b/public/assets/images/products/alphapet-suxoi-korm-dlia-sterilizovannyx-kosek/variation_59/5.webp new file mode 100644 index 0000000..9c19831 Binary files /dev/null and b/public/assets/images/products/alphapet-suxoi-korm-dlia-sterilizovannyx-kosek/variation_59/5.webp differ diff --git a/public/assets/images/products/alphapet-suxoi-korm-dlia-sterilizovannyx-kosek/variation_60/0.webp b/public/assets/images/products/alphapet-suxoi-korm-dlia-sterilizovannyx-kosek/variation_60/0.webp new file mode 100644 index 0000000..4a9d5b9 Binary files /dev/null and b/public/assets/images/products/alphapet-suxoi-korm-dlia-sterilizovannyx-kosek/variation_60/0.webp differ diff --git a/public/assets/images/products/alphapet-suxoi-korm-dlia-sterilizovannyx-kosek/variation_60/1.webp b/public/assets/images/products/alphapet-suxoi-korm-dlia-sterilizovannyx-kosek/variation_60/1.webp new file mode 100644 index 0000000..8834dd9 Binary files /dev/null and b/public/assets/images/products/alphapet-suxoi-korm-dlia-sterilizovannyx-kosek/variation_60/1.webp differ diff --git a/public/assets/images/products/alphapet-suxoi-korm-dlia-sterilizovannyx-kosek/variation_60/2.webp b/public/assets/images/products/alphapet-suxoi-korm-dlia-sterilizovannyx-kosek/variation_60/2.webp new file mode 100644 index 0000000..9c19831 Binary files /dev/null and b/public/assets/images/products/alphapet-suxoi-korm-dlia-sterilizovannyx-kosek/variation_60/2.webp differ diff --git a/public/assets/images/products/alphapet-suxoi-korm-dlia-sterilizovannyx-kosek/variation_60/3.webp b/public/assets/images/products/alphapet-suxoi-korm-dlia-sterilizovannyx-kosek/variation_60/3.webp new file mode 100644 index 0000000..4a9d5b9 Binary files /dev/null and b/public/assets/images/products/alphapet-suxoi-korm-dlia-sterilizovannyx-kosek/variation_60/3.webp differ diff --git a/public/assets/images/products/alphapet-suxoi-korm-dlia-sterilizovannyx-kosek/variation_60/4.webp b/public/assets/images/products/alphapet-suxoi-korm-dlia-sterilizovannyx-kosek/variation_60/4.webp new file mode 100644 index 0000000..8834dd9 Binary files /dev/null and b/public/assets/images/products/alphapet-suxoi-korm-dlia-sterilizovannyx-kosek/variation_60/4.webp differ diff --git a/public/assets/images/products/alphapet-suxoi-korm-dlia-sterilizovannyx-kosek/variation_60/5.webp b/public/assets/images/products/alphapet-suxoi-korm-dlia-sterilizovannyx-kosek/variation_60/5.webp new file mode 100644 index 0000000..9c19831 Binary files /dev/null and b/public/assets/images/products/alphapet-suxoi-korm-dlia-sterilizovannyx-kosek/variation_60/5.webp differ diff --git a/public/assets/images/products/alphapet-wow-suxoi-korm-dlia-sterilizovannyx-kosek/variation_139/0.webp b/public/assets/images/products/alphapet-wow-suxoi-korm-dlia-sterilizovannyx-kosek/variation_139/0.webp new file mode 100644 index 0000000..b41f6b7 Binary files /dev/null and b/public/assets/images/products/alphapet-wow-suxoi-korm-dlia-sterilizovannyx-kosek/variation_139/0.webp differ diff --git a/public/assets/images/products/alphapet-wow-suxoi-korm-dlia-sterilizovannyx-kosek/variation_139/1.webp b/public/assets/images/products/alphapet-wow-suxoi-korm-dlia-sterilizovannyx-kosek/variation_139/1.webp new file mode 100644 index 0000000..a9db87b Binary files /dev/null and b/public/assets/images/products/alphapet-wow-suxoi-korm-dlia-sterilizovannyx-kosek/variation_139/1.webp differ diff --git a/public/assets/images/products/alphapet-wow-suxoi-korm-dlia-sterilizovannyx-kosek/variation_140/0.webp b/public/assets/images/products/alphapet-wow-suxoi-korm-dlia-sterilizovannyx-kosek/variation_140/0.webp new file mode 100644 index 0000000..b41f6b7 Binary files /dev/null and b/public/assets/images/products/alphapet-wow-suxoi-korm-dlia-sterilizovannyx-kosek/variation_140/0.webp differ diff --git a/public/assets/images/products/alphapet-wow-suxoi-korm-dlia-sterilizovannyx-kosek/variation_140/1.webp b/public/assets/images/products/alphapet-wow-suxoi-korm-dlia-sterilizovannyx-kosek/variation_140/1.webp new file mode 100644 index 0000000..a9db87b Binary files /dev/null and b/public/assets/images/products/alphapet-wow-suxoi-korm-dlia-sterilizovannyx-kosek/variation_140/1.webp differ diff --git a/public/assets/images/products/alphapet-wow-suxoi-korm-dlia-sterilizovannyx-kosek/variation_141/0.webp b/public/assets/images/products/alphapet-wow-suxoi-korm-dlia-sterilizovannyx-kosek/variation_141/0.webp new file mode 100644 index 0000000..3ca6166 Binary files /dev/null and b/public/assets/images/products/alphapet-wow-suxoi-korm-dlia-sterilizovannyx-kosek/variation_141/0.webp differ diff --git a/public/assets/images/products/alphapet-wow-suxoi-korm-dlia-sterilizovannyx-kosek/variation_141/1.webp b/public/assets/images/products/alphapet-wow-suxoi-korm-dlia-sterilizovannyx-kosek/variation_141/1.webp new file mode 100644 index 0000000..4b7ba7c Binary files /dev/null and b/public/assets/images/products/alphapet-wow-suxoi-korm-dlia-sterilizovannyx-kosek/variation_141/1.webp differ diff --git a/public/assets/images/products/avva-adult-suxoi-korm-na-osnove-svezego-miasa-dlia-vzroslyx-sobak-melkix-porod-s-iagnenkom-i-indeikoi/variation_47/0.webp b/public/assets/images/products/avva-adult-suxoi-korm-na-osnove-svezego-miasa-dlia-vzroslyx-sobak-melkix-porod-s-iagnenkom-i-indeikoi/variation_47/0.webp new file mode 100644 index 0000000..b6637c2 Binary files /dev/null and b/public/assets/images/products/avva-adult-suxoi-korm-na-osnove-svezego-miasa-dlia-vzroslyx-sobak-melkix-porod-s-iagnenkom-i-indeikoi/variation_47/0.webp differ diff --git a/public/assets/images/products/avva-adult-suxoi-korm-na-osnove-svezego-miasa-dlia-vzroslyx-sobak-melkix-porod-s-iagnenkom-i-indeikoi/variation_47/1.webp b/public/assets/images/products/avva-adult-suxoi-korm-na-osnove-svezego-miasa-dlia-vzroslyx-sobak-melkix-porod-s-iagnenkom-i-indeikoi/variation_47/1.webp new file mode 100644 index 0000000..7c91c4d Binary files /dev/null and b/public/assets/images/products/avva-adult-suxoi-korm-na-osnove-svezego-miasa-dlia-vzroslyx-sobak-melkix-porod-s-iagnenkom-i-indeikoi/variation_47/1.webp differ diff --git a/public/assets/images/products/avva-adult-suxoi-korm-na-osnove-svezego-miasa-dlia-vzroslyx-sobak-melkix-porod-s-iagnenkom-i-indeikoi/variation_47/2.webp b/public/assets/images/products/avva-adult-suxoi-korm-na-osnove-svezego-miasa-dlia-vzroslyx-sobak-melkix-porod-s-iagnenkom-i-indeikoi/variation_47/2.webp new file mode 100644 index 0000000..71e9796 Binary files /dev/null and b/public/assets/images/products/avva-adult-suxoi-korm-na-osnove-svezego-miasa-dlia-vzroslyx-sobak-melkix-porod-s-iagnenkom-i-indeikoi/variation_47/2.webp differ diff --git a/public/assets/images/products/avva-adult-suxoi-korm-na-osnove-svezego-miasa-dlia-vzroslyx-sobak-melkix-porod-s-iagnenkom-i-indeikoi/variation_48/0.webp b/public/assets/images/products/avva-adult-suxoi-korm-na-osnove-svezego-miasa-dlia-vzroslyx-sobak-melkix-porod-s-iagnenkom-i-indeikoi/variation_48/0.webp new file mode 100644 index 0000000..b6637c2 Binary files /dev/null and b/public/assets/images/products/avva-adult-suxoi-korm-na-osnove-svezego-miasa-dlia-vzroslyx-sobak-melkix-porod-s-iagnenkom-i-indeikoi/variation_48/0.webp differ diff --git a/public/assets/images/products/avva-adult-suxoi-korm-na-osnove-svezego-miasa-dlia-vzroslyx-sobak-melkix-porod-s-iagnenkom-i-indeikoi/variation_48/1.webp b/public/assets/images/products/avva-adult-suxoi-korm-na-osnove-svezego-miasa-dlia-vzroslyx-sobak-melkix-porod-s-iagnenkom-i-indeikoi/variation_48/1.webp new file mode 100644 index 0000000..7c91c4d Binary files /dev/null and b/public/assets/images/products/avva-adult-suxoi-korm-na-osnove-svezego-miasa-dlia-vzroslyx-sobak-melkix-porod-s-iagnenkom-i-indeikoi/variation_48/1.webp differ diff --git a/public/assets/images/products/avva-adult-suxoi-korm-na-osnove-svezego-miasa-dlia-vzroslyx-sobak-melkix-porod-s-iagnenkom-i-indeikoi/variation_48/2.webp b/public/assets/images/products/avva-adult-suxoi-korm-na-osnove-svezego-miasa-dlia-vzroslyx-sobak-melkix-porod-s-iagnenkom-i-indeikoi/variation_48/2.webp new file mode 100644 index 0000000..71e9796 Binary files /dev/null and b/public/assets/images/products/avva-adult-suxoi-korm-na-osnove-svezego-miasa-dlia-vzroslyx-sobak-melkix-porod-s-iagnenkom-i-indeikoi/variation_48/2.webp differ diff --git a/public/assets/images/products/default.svg b/public/assets/images/products/default.svg new file mode 100644 index 0000000..27a8559 --- /dev/null +++ b/public/assets/images/products/default.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/public/assets/images/products/grandin-holistic-vlaznyi-korm-konservy-dlia-vzroslyx-kosek/variation_55/0.webp b/public/assets/images/products/grandin-holistic-vlaznyi-korm-konservy-dlia-vzroslyx-kosek/variation_55/0.webp new file mode 100644 index 0000000..69f5eab Binary files /dev/null and b/public/assets/images/products/grandin-holistic-vlaznyi-korm-konservy-dlia-vzroslyx-kosek/variation_55/0.webp differ diff --git a/public/assets/images/products/grandin-holistic-vlaznyi-korm-konservy-dlia-vzroslyx-kosek/variation_55/1.webp b/public/assets/images/products/grandin-holistic-vlaznyi-korm-konservy-dlia-vzroslyx-kosek/variation_55/1.webp new file mode 100644 index 0000000..714cc96 Binary files /dev/null and b/public/assets/images/products/grandin-holistic-vlaznyi-korm-konservy-dlia-vzroslyx-kosek/variation_55/1.webp differ diff --git a/public/assets/images/products/grandin-holistic-vlaznyi-korm-konservy-dlia-vzroslyx-kosek/variation_55/2.webp b/public/assets/images/products/grandin-holistic-vlaznyi-korm-konservy-dlia-vzroslyx-kosek/variation_55/2.webp new file mode 100644 index 0000000..bccf878 Binary files /dev/null and b/public/assets/images/products/grandin-holistic-vlaznyi-korm-konservy-dlia-vzroslyx-kosek/variation_55/2.webp differ diff --git a/public/assets/images/products/grandin-holistic-vlaznyi-korm-konservy-dlia-vzroslyx-kosek/variation_56/0.webp b/public/assets/images/products/grandin-holistic-vlaznyi-korm-konservy-dlia-vzroslyx-kosek/variation_56/0.webp new file mode 100644 index 0000000..5add3f9 Binary files /dev/null and b/public/assets/images/products/grandin-holistic-vlaznyi-korm-konservy-dlia-vzroslyx-kosek/variation_56/0.webp differ diff --git a/public/assets/images/products/grandin-holistic-vlaznyi-korm-konservy-dlia-vzroslyx-kosek/variation_56/1.webp b/public/assets/images/products/grandin-holistic-vlaznyi-korm-konservy-dlia-vzroslyx-kosek/variation_56/1.webp new file mode 100644 index 0000000..3ca51f8 Binary files /dev/null and b/public/assets/images/products/grandin-holistic-vlaznyi-korm-konservy-dlia-vzroslyx-kosek/variation_56/1.webp differ diff --git a/public/assets/images/products/grandin-holistic-vlaznyi-korm-konservy-dlia-vzroslyx-kosek/variation_56/2.webp b/public/assets/images/products/grandin-holistic-vlaznyi-korm-konservy-dlia-vzroslyx-kosek/variation_56/2.webp new file mode 100644 index 0000000..8947ce3 Binary files /dev/null and b/public/assets/images/products/grandin-holistic-vlaznyi-korm-konservy-dlia-vzroslyx-kosek/variation_56/2.webp differ diff --git a/public/assets/images/products/grandin-hypoallergenic-iagnenok/variation_41/0.webp b/public/assets/images/products/grandin-hypoallergenic-iagnenok/variation_41/0.webp new file mode 100644 index 0000000..140f839 Binary files /dev/null and b/public/assets/images/products/grandin-hypoallergenic-iagnenok/variation_41/0.webp differ diff --git a/public/assets/images/products/grandin-hypoallergenic-iagnenok/variation_41/1.webp b/public/assets/images/products/grandin-hypoallergenic-iagnenok/variation_41/1.webp new file mode 100644 index 0000000..fcba451 Binary files /dev/null and b/public/assets/images/products/grandin-hypoallergenic-iagnenok/variation_41/1.webp differ diff --git a/public/assets/images/products/grandin-hypoallergenic-iagnenok/variation_41/2.webp b/public/assets/images/products/grandin-hypoallergenic-iagnenok/variation_41/2.webp new file mode 100644 index 0000000..7a403d4 Binary files /dev/null and b/public/assets/images/products/grandin-hypoallergenic-iagnenok/variation_41/2.webp differ diff --git a/public/assets/images/products/grandin-hypoallergenic-iagnenok/variation_42/0.jpeg b/public/assets/images/products/grandin-hypoallergenic-iagnenok/variation_42/0.jpeg new file mode 100644 index 0000000..3dc14bd Binary files /dev/null and b/public/assets/images/products/grandin-hypoallergenic-iagnenok/variation_42/0.jpeg differ diff --git a/public/assets/images/products/grandin-hypoallergenic-iagnenok/variation_42/1.webp b/public/assets/images/products/grandin-hypoallergenic-iagnenok/variation_42/1.webp new file mode 100644 index 0000000..fcba451 Binary files /dev/null and b/public/assets/images/products/grandin-hypoallergenic-iagnenok/variation_42/1.webp differ diff --git a/public/assets/images/products/grandin-hypoallergenic-iagnenok/variation_42/2.webp b/public/assets/images/products/grandin-hypoallergenic-iagnenok/variation_42/2.webp new file mode 100644 index 0000000..7a403d4 Binary files /dev/null and b/public/assets/images/products/grandin-hypoallergenic-iagnenok/variation_42/2.webp differ diff --git a/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_129/0.webp b/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_129/0.webp new file mode 100644 index 0000000..f1fe144 Binary files /dev/null and b/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_129/0.webp differ diff --git a/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_129/1.webp b/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_129/1.webp new file mode 100644 index 0000000..e281167 Binary files /dev/null and b/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_129/1.webp differ diff --git a/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_129/2.webp b/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_129/2.webp new file mode 100644 index 0000000..fc9defc Binary files /dev/null and b/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_129/2.webp differ diff --git a/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_130/0.webp b/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_130/0.webp new file mode 100644 index 0000000..f1fe144 Binary files /dev/null and b/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_130/0.webp differ diff --git a/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_130/1.webp b/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_130/1.webp new file mode 100644 index 0000000..e281167 Binary files /dev/null and b/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_130/1.webp differ diff --git a/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_130/2.webp b/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_130/2.webp new file mode 100644 index 0000000..fc9defc Binary files /dev/null and b/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_130/2.webp differ diff --git a/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_131/0.webp b/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_131/0.webp new file mode 100644 index 0000000..f1fe144 Binary files /dev/null and b/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_131/0.webp differ diff --git a/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_131/1.webp b/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_131/1.webp new file mode 100644 index 0000000..e281167 Binary files /dev/null and b/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_131/1.webp differ diff --git a/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_131/2.webp b/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_131/2.webp new file mode 100644 index 0000000..fc9defc Binary files /dev/null and b/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_131/2.webp differ diff --git a/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_132/0.webp b/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_132/0.webp new file mode 100644 index 0000000..65e58e8 Binary files /dev/null and b/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_132/0.webp differ diff --git a/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_132/1.webp b/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_132/1.webp new file mode 100644 index 0000000..4d067e8 Binary files /dev/null and b/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_132/1.webp differ diff --git a/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_132/2.webp b/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_132/2.webp new file mode 100644 index 0000000..5b4bd37 Binary files /dev/null and b/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_132/2.webp differ diff --git a/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_133/0.webp b/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_133/0.webp new file mode 100644 index 0000000..65e58e8 Binary files /dev/null and b/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_133/0.webp differ diff --git a/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_133/1.webp b/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_133/1.webp new file mode 100644 index 0000000..4d067e8 Binary files /dev/null and b/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_133/1.webp differ diff --git a/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_133/2.webp b/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_133/2.webp new file mode 100644 index 0000000..5b4bd37 Binary files /dev/null and b/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_133/2.webp differ diff --git a/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_134/0.webp b/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_134/0.webp new file mode 100644 index 0000000..e556bd9 Binary files /dev/null and b/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_134/0.webp differ diff --git a/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_134/1.webp b/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_134/1.webp new file mode 100644 index 0000000..620cbff Binary files /dev/null and b/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_134/1.webp differ diff --git a/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_134/2.webp b/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_134/2.webp new file mode 100644 index 0000000..bbf1ad0 Binary files /dev/null and b/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_134/2.webp differ diff --git a/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_135/0.webp b/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_135/0.webp new file mode 100644 index 0000000..e556bd9 Binary files /dev/null and b/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_135/0.webp differ diff --git a/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_135/1.webp b/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_135/1.webp new file mode 100644 index 0000000..620cbff Binary files /dev/null and b/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_135/1.webp differ diff --git a/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_135/2.webp b/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_135/2.webp new file mode 100644 index 0000000..bbf1ad0 Binary files /dev/null and b/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_135/2.webp differ diff --git a/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_136/0.webp b/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_136/0.webp new file mode 100644 index 0000000..e556bd9 Binary files /dev/null and b/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_136/0.webp differ diff --git a/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_136/1.webp b/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_136/1.webp new file mode 100644 index 0000000..620cbff Binary files /dev/null and b/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_136/1.webp differ diff --git a/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_136/2.webp b/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_136/2.webp new file mode 100644 index 0000000..bbf1ad0 Binary files /dev/null and b/public/assets/images/products/grandorf-holistic-adult-sterilised-suxoi-korm-dlia-vzroslyx-sterilizovannyx-kosek/variation_136/2.webp differ diff --git a/public/assets/images/products/klicker-adult-sensitive-digestion-suxoi-korm-dlia-kosek-s-cuvstvitelnym-pishhevareniem/variation_53/0.webp b/public/assets/images/products/klicker-adult-sensitive-digestion-suxoi-korm-dlia-kosek-s-cuvstvitelnym-pishhevareniem/variation_53/0.webp new file mode 100644 index 0000000..33bad5c Binary files /dev/null and b/public/assets/images/products/klicker-adult-sensitive-digestion-suxoi-korm-dlia-kosek-s-cuvstvitelnym-pishhevareniem/variation_53/0.webp differ diff --git a/public/assets/images/products/klicker-adult-sensitive-digestion-suxoi-korm-dlia-kosek-s-cuvstvitelnym-pishhevareniem/variation_53/1.webp b/public/assets/images/products/klicker-adult-sensitive-digestion-suxoi-korm-dlia-kosek-s-cuvstvitelnym-pishhevareniem/variation_53/1.webp new file mode 100644 index 0000000..5c6ac6a Binary files /dev/null and b/public/assets/images/products/klicker-adult-sensitive-digestion-suxoi-korm-dlia-kosek-s-cuvstvitelnym-pishhevareniem/variation_53/1.webp differ diff --git a/public/assets/images/products/klicker-adult-sensitive-digestion-suxoi-korm-dlia-kosek-s-cuvstvitelnym-pishhevareniem/variation_53/2.webp b/public/assets/images/products/klicker-adult-sensitive-digestion-suxoi-korm-dlia-kosek-s-cuvstvitelnym-pishhevareniem/variation_53/2.webp new file mode 100644 index 0000000..bbf7892 Binary files /dev/null and b/public/assets/images/products/klicker-adult-sensitive-digestion-suxoi-korm-dlia-kosek-s-cuvstvitelnym-pishhevareniem/variation_53/2.webp differ diff --git a/public/assets/images/products/klicker-adult/variation_43/0.webp b/public/assets/images/products/klicker-adult/variation_43/0.webp new file mode 100644 index 0000000..125de04 Binary files /dev/null and b/public/assets/images/products/klicker-adult/variation_43/0.webp differ diff --git a/public/assets/images/products/klicker-adult/variation_43/1.webp b/public/assets/images/products/klicker-adult/variation_43/1.webp new file mode 100644 index 0000000..61fef3a Binary files /dev/null and b/public/assets/images/products/klicker-adult/variation_43/1.webp differ diff --git a/public/assets/images/products/klicker-adult/variation_43/2.webp b/public/assets/images/products/klicker-adult/variation_43/2.webp new file mode 100644 index 0000000..c10373d Binary files /dev/null and b/public/assets/images/products/klicker-adult/variation_43/2.webp differ diff --git a/public/assets/images/products/little-one-korm-dlia-morskix-svinok-zelenaia-dolina/variation_67/0.webp b/public/assets/images/products/little-one-korm-dlia-morskix-svinok-zelenaia-dolina/variation_67/0.webp new file mode 100644 index 0000000..34a62d3 Binary files /dev/null and b/public/assets/images/products/little-one-korm-dlia-morskix-svinok-zelenaia-dolina/variation_67/0.webp differ diff --git a/public/assets/images/products/little-one-korm-dlia-morskix-svinok-zelenaia-dolina/variation_67/1.webp b/public/assets/images/products/little-one-korm-dlia-morskix-svinok-zelenaia-dolina/variation_67/1.webp new file mode 100644 index 0000000..f0adff6 Binary files /dev/null and b/public/assets/images/products/little-one-korm-dlia-morskix-svinok-zelenaia-dolina/variation_67/1.webp differ diff --git a/public/assets/images/products/little-one-korm-dlia-morskix-svinok-zelenaia-dolina/variation_67/2.webp b/public/assets/images/products/little-one-korm-dlia-morskix-svinok-zelenaia-dolina/variation_67/2.webp new file mode 100644 index 0000000..db068c4 Binary files /dev/null and b/public/assets/images/products/little-one-korm-dlia-morskix-svinok-zelenaia-dolina/variation_67/2.webp differ diff --git a/public/assets/images/products/little-one-korm-dlia-morskix-svinok/variation_63/0.webp b/public/assets/images/products/little-one-korm-dlia-morskix-svinok/variation_63/0.webp new file mode 100644 index 0000000..dda4b46 Binary files /dev/null and b/public/assets/images/products/little-one-korm-dlia-morskix-svinok/variation_63/0.webp differ diff --git a/public/assets/images/products/little-one-korm-dlia-morskix-svinok/variation_63/1.webp b/public/assets/images/products/little-one-korm-dlia-morskix-svinok/variation_63/1.webp new file mode 100644 index 0000000..8375f89 Binary files /dev/null and b/public/assets/images/products/little-one-korm-dlia-morskix-svinok/variation_63/1.webp differ diff --git a/public/assets/images/products/little-one-korm-dlia-morskix-svinok/variation_63/2.webp b/public/assets/images/products/little-one-korm-dlia-morskix-svinok/variation_63/2.webp new file mode 100644 index 0000000..e79d6e4 Binary files /dev/null and b/public/assets/images/products/little-one-korm-dlia-morskix-svinok/variation_63/2.webp differ diff --git a/public/assets/images/products/little-one-korm-dlia-morskix-svinok/variation_64/0.webp b/public/assets/images/products/little-one-korm-dlia-morskix-svinok/variation_64/0.webp new file mode 100644 index 0000000..dda4b46 Binary files /dev/null and b/public/assets/images/products/little-one-korm-dlia-morskix-svinok/variation_64/0.webp differ diff --git a/public/assets/images/products/little-one-korm-dlia-morskix-svinok/variation_64/1.webp b/public/assets/images/products/little-one-korm-dlia-morskix-svinok/variation_64/1.webp new file mode 100644 index 0000000..8375f89 Binary files /dev/null and b/public/assets/images/products/little-one-korm-dlia-morskix-svinok/variation_64/1.webp differ diff --git a/public/assets/images/products/little-one-korm-dlia-morskix-svinok/variation_64/2.webp b/public/assets/images/products/little-one-korm-dlia-morskix-svinok/variation_64/2.webp new file mode 100644 index 0000000..e79d6e4 Binary files /dev/null and b/public/assets/images/products/little-one-korm-dlia-morskix-svinok/variation_64/2.webp differ diff --git a/public/assets/images/products/ownat-adult-sterilized-grain-free-prime-suxoi-korm-dlia-sterilizovannyx-kosek/variation_137/0.webp b/public/assets/images/products/ownat-adult-sterilized-grain-free-prime-suxoi-korm-dlia-sterilizovannyx-kosek/variation_137/0.webp new file mode 100644 index 0000000..bb81af6 Binary files /dev/null and b/public/assets/images/products/ownat-adult-sterilized-grain-free-prime-suxoi-korm-dlia-sterilizovannyx-kosek/variation_137/0.webp differ diff --git a/public/assets/images/products/ownat-adult-sterilized-grain-free-prime-suxoi-korm-dlia-sterilizovannyx-kosek/variation_137/1.webp b/public/assets/images/products/ownat-adult-sterilized-grain-free-prime-suxoi-korm-dlia-sterilizovannyx-kosek/variation_137/1.webp new file mode 100644 index 0000000..ed885d3 Binary files /dev/null and b/public/assets/images/products/ownat-adult-sterilized-grain-free-prime-suxoi-korm-dlia-sterilizovannyx-kosek/variation_137/1.webp differ diff --git a/public/assets/images/products/ownat-adult-sterilized-grain-free-prime-suxoi-korm-dlia-sterilizovannyx-kosek/variation_137/2.webp b/public/assets/images/products/ownat-adult-sterilized-grain-free-prime-suxoi-korm-dlia-sterilizovannyx-kosek/variation_137/2.webp new file mode 100644 index 0000000..9cfc22a Binary files /dev/null and b/public/assets/images/products/ownat-adult-sterilized-grain-free-prime-suxoi-korm-dlia-sterilizovannyx-kosek/variation_137/2.webp differ diff --git a/public/assets/images/products/ownat-adult-sterilized-grain-free-prime-suxoi-korm-dlia-sterilizovannyx-kosek/variation_138/0.webp b/public/assets/images/products/ownat-adult-sterilized-grain-free-prime-suxoi-korm-dlia-sterilizovannyx-kosek/variation_138/0.webp new file mode 100644 index 0000000..0b56287 Binary files /dev/null and b/public/assets/images/products/ownat-adult-sterilized-grain-free-prime-suxoi-korm-dlia-sterilizovannyx-kosek/variation_138/0.webp differ diff --git a/public/assets/images/products/ownat-adult-sterilized-grain-free-prime-suxoi-korm-dlia-sterilizovannyx-kosek/variation_138/1.webp b/public/assets/images/products/ownat-adult-sterilized-grain-free-prime-suxoi-korm-dlia-sterilizovannyx-kosek/variation_138/1.webp new file mode 100644 index 0000000..fdf323e Binary files /dev/null and b/public/assets/images/products/ownat-adult-sterilized-grain-free-prime-suxoi-korm-dlia-sterilizovannyx-kosek/variation_138/1.webp differ diff --git a/public/assets/images/products/ownat-adult-sterilized-grain-free-prime-suxoi-korm-dlia-sterilizovannyx-kosek/variation_138/2.webp b/public/assets/images/products/ownat-adult-sterilized-grain-free-prime-suxoi-korm-dlia-sterilizovannyx-kosek/variation_138/2.webp new file mode 100644 index 0000000..8589ecd Binary files /dev/null and b/public/assets/images/products/ownat-adult-sterilized-grain-free-prime-suxoi-korm-dlia-sterilizovannyx-kosek/variation_138/2.webp differ diff --git a/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_116/0.webp b/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_116/0.webp new file mode 100644 index 0000000..e1be232 Binary files /dev/null and b/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_116/0.webp differ diff --git a/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_116/1.webp b/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_116/1.webp new file mode 100644 index 0000000..175c255 Binary files /dev/null and b/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_116/1.webp differ diff --git a/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_116/2.webp b/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_116/2.webp new file mode 100644 index 0000000..a6735e6 Binary files /dev/null and b/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_116/2.webp differ diff --git a/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_117/0.webp b/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_117/0.webp new file mode 100644 index 0000000..e1be232 Binary files /dev/null and b/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_117/0.webp differ diff --git a/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_117/1.webp b/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_117/1.webp new file mode 100644 index 0000000..175c255 Binary files /dev/null and b/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_117/1.webp differ diff --git a/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_117/2.webp b/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_117/2.webp new file mode 100644 index 0000000..a6735e6 Binary files /dev/null and b/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_117/2.webp differ diff --git a/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_118/0.webp b/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_118/0.webp new file mode 100644 index 0000000..e0709d0 Binary files /dev/null and b/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_118/0.webp differ diff --git a/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_118/1.webp b/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_118/1.webp new file mode 100644 index 0000000..92b0017 Binary files /dev/null and b/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_118/1.webp differ diff --git a/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_118/2.webp b/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_118/2.webp new file mode 100644 index 0000000..b1ca740 Binary files /dev/null and b/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_118/2.webp differ diff --git a/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_119/0.webp b/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_119/0.webp new file mode 100644 index 0000000..e0709d0 Binary files /dev/null and b/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_119/0.webp differ diff --git a/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_119/1.webp b/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_119/1.webp new file mode 100644 index 0000000..92b0017 Binary files /dev/null and b/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_119/1.webp differ diff --git a/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_119/2.webp b/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_119/2.webp new file mode 100644 index 0000000..b1ca740 Binary files /dev/null and b/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_119/2.webp differ diff --git a/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_120/0.webp b/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_120/0.webp new file mode 100644 index 0000000..5b5ccfa Binary files /dev/null and b/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_120/0.webp differ diff --git a/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_120/1.webp b/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_120/1.webp new file mode 100644 index 0000000..d9cbfcc Binary files /dev/null and b/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_120/1.webp differ diff --git a/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_51/0.webp b/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_51/0.webp new file mode 100644 index 0000000..4d55f36 Binary files /dev/null and b/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_51/0.webp differ diff --git a/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_51/1.webp b/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_51/1.webp new file mode 100644 index 0000000..5e17be3 Binary files /dev/null and b/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_51/1.webp differ diff --git a/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_51/2.webp b/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_51/2.webp new file mode 100644 index 0000000..5e49d6d Binary files /dev/null and b/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_51/2.webp differ diff --git a/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_52/0.webp b/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_52/0.webp new file mode 100644 index 0000000..4d55f36 Binary files /dev/null and b/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_52/0.webp differ diff --git a/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_52/1.webp b/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_52/1.webp new file mode 100644 index 0000000..5e17be3 Binary files /dev/null and b/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_52/1.webp differ diff --git a/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_52/2.webp b/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_52/2.webp new file mode 100644 index 0000000..5e49d6d Binary files /dev/null and b/public/assets/images/products/ownat-grain-free-just-suxoi-korm-bezzernovoi-dlia-sobak/variation_52/2.webp differ diff --git a/public/assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_112/0.webp b/public/assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_112/0.webp new file mode 100644 index 0000000..9c1f541 Binary files /dev/null and b/public/assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_112/0.webp differ diff --git a/public/assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_112/1.webp b/public/assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_112/1.webp new file mode 100644 index 0000000..0a0d83a Binary files /dev/null and b/public/assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_112/1.webp differ diff --git a/public/assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_112/2.webp b/public/assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_112/2.webp new file mode 100644 index 0000000..a848cc2 Binary files /dev/null and b/public/assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_112/2.webp differ diff --git a/public/assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_115/0.webp b/public/assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_115/0.webp new file mode 100644 index 0000000..9c1f541 Binary files /dev/null and b/public/assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_115/0.webp differ diff --git a/public/assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_115/1.webp b/public/assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_115/1.webp new file mode 100644 index 0000000..0a0d83a Binary files /dev/null and b/public/assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_115/1.webp differ diff --git a/public/assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_115/2.webp b/public/assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_115/2.webp new file mode 100644 index 0000000..a848cc2 Binary files /dev/null and b/public/assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_115/2.webp differ diff --git a/public/assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_45/0.webp b/public/assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_45/0.webp new file mode 100644 index 0000000..9c1f541 Binary files /dev/null and b/public/assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_45/0.webp differ diff --git a/public/assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_45/1.webp b/public/assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_45/1.webp new file mode 100644 index 0000000..0a0d83a Binary files /dev/null and b/public/assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_45/1.webp differ diff --git a/public/assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_45/2.webp b/public/assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_45/2.webp new file mode 100644 index 0000000..a848cc2 Binary files /dev/null and b/public/assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_45/2.webp differ diff --git a/public/assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_45/3.webp b/public/assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_45/3.webp new file mode 100644 index 0000000..9c1f541 Binary files /dev/null and b/public/assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_45/3.webp differ diff --git a/public/assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_45/4.webp b/public/assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_45/4.webp new file mode 100644 index 0000000..0a0d83a Binary files /dev/null and b/public/assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_45/4.webp differ diff --git a/public/assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_45/5.webp b/public/assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_45/5.webp new file mode 100644 index 0000000..a848cc2 Binary files /dev/null and b/public/assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_45/5.webp differ diff --git a/public/assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_46/0.webp b/public/assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_46/0.webp new file mode 100644 index 0000000..9c1f541 Binary files /dev/null and b/public/assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_46/0.webp differ diff --git a/public/assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_46/1.webp b/public/assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_46/1.webp new file mode 100644 index 0000000..0a0d83a Binary files /dev/null and b/public/assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_46/1.webp differ diff --git a/public/assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_46/2.webp b/public/assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_46/2.webp new file mode 100644 index 0000000..a848cc2 Binary files /dev/null and b/public/assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_46/2.webp differ diff --git a/public/assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_46/3.webp b/public/assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_46/3.webp new file mode 100644 index 0000000..9c1f541 Binary files /dev/null and b/public/assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_46/3.webp differ diff --git a/public/assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_46/4.webp b/public/assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_46/4.webp new file mode 100644 index 0000000..0a0d83a Binary files /dev/null and b/public/assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_46/4.webp differ diff --git a/public/assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_46/5.webp b/public/assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_46/5.webp new file mode 100644 index 0000000..a848cc2 Binary files /dev/null and b/public/assets/images/products/royal-canin-mini-adult-suxoi-korm-dlia-vzroslyx-sobak-melkix-razmerov-v-vozraste-ot-10-mesiacev-do-8-let/variation_46/5.webp differ diff --git a/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_121/0.webp b/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_121/0.webp new file mode 100644 index 0000000..b991184 Binary files /dev/null and b/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_121/0.webp differ diff --git a/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_121/1.webp b/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_121/1.webp new file mode 100644 index 0000000..65c4535 Binary files /dev/null and b/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_121/1.webp differ diff --git a/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_121/2.webp b/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_121/2.webp new file mode 100644 index 0000000..d3e1f3e Binary files /dev/null and b/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_121/2.webp differ diff --git a/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_121/3.webp b/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_121/3.webp new file mode 100644 index 0000000..5c4899e Binary files /dev/null and b/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_121/3.webp differ diff --git a/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_122/0.webp b/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_122/0.webp new file mode 100644 index 0000000..b991184 Binary files /dev/null and b/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_122/0.webp differ diff --git a/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_122/1.webp b/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_122/1.webp new file mode 100644 index 0000000..d3e1f3e Binary files /dev/null and b/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_122/1.webp differ diff --git a/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_122/2.webp b/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_122/2.webp new file mode 100644 index 0000000..65c4535 Binary files /dev/null and b/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_122/2.webp differ diff --git a/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_122/3.webp b/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_122/3.webp new file mode 100644 index 0000000..5c4899e Binary files /dev/null and b/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_122/3.webp differ diff --git a/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_123/0.webp b/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_123/0.webp new file mode 100644 index 0000000..b991184 Binary files /dev/null and b/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_123/0.webp differ diff --git a/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_123/1.webp b/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_123/1.webp new file mode 100644 index 0000000..65c4535 Binary files /dev/null and b/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_123/1.webp differ diff --git a/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_123/2.webp b/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_123/2.webp new file mode 100644 index 0000000..d3e1f3e Binary files /dev/null and b/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_123/2.webp differ diff --git a/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_123/3.webp b/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_123/3.webp new file mode 100644 index 0000000..5c4899e Binary files /dev/null and b/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_123/3.webp differ diff --git a/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_57/0.webp b/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_57/0.webp new file mode 100644 index 0000000..b991184 Binary files /dev/null and b/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_57/0.webp differ diff --git a/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_57/1.webp b/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_57/1.webp new file mode 100644 index 0000000..d3e1f3e Binary files /dev/null and b/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_57/1.webp differ diff --git a/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_57/2.webp b/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_57/2.webp new file mode 100644 index 0000000..65c4535 Binary files /dev/null and b/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_57/2.webp differ diff --git a/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_57/3.webp b/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_57/3.webp new file mode 100644 index 0000000..5c4899e Binary files /dev/null and b/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_57/3.webp differ diff --git a/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_58/0.webp b/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_58/0.webp new file mode 100644 index 0000000..b991184 Binary files /dev/null and b/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_58/0.webp differ diff --git a/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_58/1.webp b/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_58/1.webp new file mode 100644 index 0000000..d3e1f3e Binary files /dev/null and b/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_58/1.webp differ diff --git a/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_58/2.webp b/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_58/2.webp new file mode 100644 index 0000000..65c4535 Binary files /dev/null and b/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_58/2.webp differ diff --git a/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_58/3.webp b/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_58/3.webp new file mode 100644 index 0000000..5c4899e Binary files /dev/null and b/public/assets/images/products/royal-canin-sterilised-37-regular-suxoi-korm-dlia-sterilizovannyx-kosek-s-1-do-7-let/variation_58/3.webp differ diff --git a/public/assets/images/products/rungo-kombinezon-teplyi-dlia-sobak-porody-mops/variation_128/0.webp b/public/assets/images/products/rungo-kombinezon-teplyi-dlia-sobak-porody-mops/variation_128/0.webp new file mode 100644 index 0000000..bc3ae01 Binary files /dev/null and b/public/assets/images/products/rungo-kombinezon-teplyi-dlia-sobak-porody-mops/variation_128/0.webp differ diff --git a/public/assets/images/products/rungo-kombinezon-teplyi-dlia-sobak-porody-mops/variation_69/0.webp b/public/assets/images/products/rungo-kombinezon-teplyi-dlia-sobak-porody-mops/variation_69/0.webp new file mode 100644 index 0000000..586f6d7 Binary files /dev/null and b/public/assets/images/products/rungo-kombinezon-teplyi-dlia-sobak-porody-mops/variation_69/0.webp differ diff --git a/public/assets/images/products/rungo-kombinezon-teplyi-dlia-sobak-porody-mops/variation_70/0.webp b/public/assets/images/products/rungo-kombinezon-teplyi-dlia-sobak-porody-mops/variation_70/0.webp new file mode 100644 index 0000000..1691a9a Binary files /dev/null and b/public/assets/images/products/rungo-kombinezon-teplyi-dlia-sobak-porody-mops/variation_70/0.webp differ diff --git a/public/assets/images/products/rungo-oseinik-neilonovyi-s-ruckoi-dlia-sobak-k-9-l/variation_22/0.webp b/public/assets/images/products/rungo-oseinik-neilonovyi-s-ruckoi-dlia-sobak-k-9-l/variation_22/0.webp new file mode 100644 index 0000000..ebaf452 Binary files /dev/null and b/public/assets/images/products/rungo-oseinik-neilonovyi-s-ruckoi-dlia-sobak-k-9-l/variation_22/0.webp differ diff --git a/public/assets/images/products/rungo-oseinik-neilonovyi-s-ruckoi-dlia-sobak-k-9-l/variation_22/1.webp b/public/assets/images/products/rungo-oseinik-neilonovyi-s-ruckoi-dlia-sobak-k-9-l/variation_22/1.webp new file mode 100644 index 0000000..ee884ad Binary files /dev/null and b/public/assets/images/products/rungo-oseinik-neilonovyi-s-ruckoi-dlia-sobak-k-9-l/variation_22/1.webp differ diff --git a/public/assets/images/products/rungo-oseinik-neilonovyi-s-ruckoi-dlia-sobak-k-9-l/variation_23/0.webp b/public/assets/images/products/rungo-oseinik-neilonovyi-s-ruckoi-dlia-sobak-k-9-l/variation_23/0.webp new file mode 100644 index 0000000..9679200 Binary files /dev/null and b/public/assets/images/products/rungo-oseinik-neilonovyi-s-ruckoi-dlia-sobak-k-9-l/variation_23/0.webp differ diff --git a/public/assets/images/products/rungo-oseinik-neilonovyi-s-ruckoi-dlia-sobak-k-9-l/variation_23/1.webp b/public/assets/images/products/rungo-oseinik-neilonovyi-s-ruckoi-dlia-sobak-k-9-l/variation_23/1.webp new file mode 100644 index 0000000..95f959e Binary files /dev/null and b/public/assets/images/products/rungo-oseinik-neilonovyi-s-ruckoi-dlia-sobak-k-9-l/variation_23/1.webp differ diff --git a/public/assets/images/products/tetra-min-holiday-korm-zele/variation_61/0.webp b/public/assets/images/products/tetra-min-holiday-korm-zele/variation_61/0.webp new file mode 100644 index 0000000..d8829e9 Binary files /dev/null and b/public/assets/images/products/tetra-min-holiday-korm-zele/variation_61/0.webp differ diff --git a/public/assets/js/ajax-cart.js b/public/assets/js/ajax-cart.js new file mode 100644 index 0000000..0b8c15a --- /dev/null +++ b/public/assets/js/ajax-cart.js @@ -0,0 +1,126 @@ +// Универсальный AJAX для добавления в корзину +document.addEventListener('DOMContentLoaded', function () { + const cartForms = document.querySelectorAll('form[action*="cart/add"]'); + + cartForms.forEach(form => { + form.addEventListener('submit', async function (e) { + e.preventDefault(); + + const submitBtn = this.querySelector('button[type="submit"]'); + if (!submitBtn) return; + + const originalText = submitBtn.textContent; + submitBtn.disabled = true; + submitBtn.textContent = 'Добавление...'; + + // Получаем данные формы + const variationId = this.querySelector('input[name="variation_id"]')?.value; + const quantity = this.querySelector('input[name="quantity"]')?.value || 1; + + try { + const response = await fetch(this.action, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content, + 'X-Requested-With': 'XMLHttpRequest' + }, + body: JSON.stringify({ + variation_id: variationId, + quantity: quantity + }) + }); + + // Проверяем, что ответ JSON + const contentType = response.headers.get('content-type'); + if (!contentType || !contentType.includes('application/json')) { + throw new Error('Сервер вернул HTML вместо JSON'); + } + + const data = await response.json(); + + if (data.success) { + // Обновляем счетчик корзины + const cartCountElements = document.querySelectorAll('.cart-count'); + cartCountElements.forEach(el => { + el.textContent = data.cart_count; + el.classList.add('cart-bump'); + setTimeout(() => el.classList.remove('cart-bump'), 300); + }); + + submitBtn.textContent = '✓ Добавлено'; + showNotification(data.message, 'success'); + + setTimeout(() => { + submitBtn.textContent = originalText; + submitBtn.disabled = false; + }, 1500); + } else { + showNotification(data.message || 'Ошибка', 'error'); + submitBtn.textContent = originalText; + submitBtn.disabled = false; + } + } catch (error) { + console.error('Error:', error); + showNotification('Ошибка при добавлении в корзину', 'error'); + submitBtn.textContent = originalText; + submitBtn.disabled = false; + } + }); + }); + + function showNotification(message, type = 'success') { + const oldNotification = document.querySelector('.notification'); + if (oldNotification) oldNotification.remove(); + + const notification = document.createElement('div'); + notification.className = `notification notification-${type}`; + notification.textContent = message; + notification.style.cssText = ` + position: fixed; + top: 20px; + right: 20px; + background: ${type === 'success' ? '#2c5e2e' : '#dc3545'}; + color: white; + padding: 12px 24px; + border-radius: 8px; + z-index: 9999; + animation: slideIn 0.3s ease; + box-shadow: 0 2px 10px rgba(0,0,0,0.1); + `; + + document.body.appendChild(notification); + + setTimeout(() => { + notification.style.animation = 'slideOut 0.3s ease'; + setTimeout(() => notification.remove(), 300); + }, 3000); + } + + // Добавляем CSS анимации + if (!document.querySelector('#cart-animations')) { + const style = document.createElement('style'); + style.id = 'cart-animations'; + style.textContent = ` + @keyframes slideIn { + from { transform: translateX(100%); opacity: 0; } + to { transform: translateX(0); opacity: 1; } + } + @keyframes slideOut { + from { transform: translateX(0); opacity: 1; } + to { transform: translateX(100%); opacity: 0; } + } + @keyframes cartBump { + 0% { transform: scale(1); } + 50% { transform: scale(1.2); } + 100% { transform: scale(1); } + } + .cart-bump { + animation: cartBump 0.3s ease; + display: inline-block; + } + `; + document.head.appendChild(style); + } +}); \ No newline at end of file diff --git a/public/assets/js/ajax-search.js b/public/assets/js/ajax-search.js new file mode 100644 index 0000000..7e6cc8c --- /dev/null +++ b/public/assets/js/ajax-search.js @@ -0,0 +1,78 @@ +document.addEventListener('DOMContentLoaded', function () { + const searchInput = document.getElementById('input_search'); + let searchTimeout; + + if (searchInput) { + let suggestionsContainer = document.createElement('div'); + suggestionsContainer.className = 'search-suggestions'; + suggestionsContainer.style.cssText = ` + position: absolute; + top: 100%; + left: 0; + right: 0; + background: white; + border: 1px solid #ddd; + border-radius: 8px; + max-height: 400px; + overflow-y: auto; + z-index: 1000; + display: none; + box-shadow: 0 4px 12px rgba(0,0,0,0.1); + `; + searchInput.parentNode.style.position = 'relative'; + searchInput.parentNode.appendChild(suggestionsContainer); + + searchInput.addEventListener('input', function () { + clearTimeout(searchTimeout); + const query = this.value.trim(); + + if (query.length < 2) { + suggestionsContainer.style.display = 'none'; + return; + } + + searchTimeout = setTimeout(() => { + fetch(`/search/ajax?q=${encodeURIComponent(query)}`) + .then(response => response.json()) + .then(data => { + if (data.products.length > 0 || data.brands.length > 0) { + let html = ''; + + if (data.brands.length > 0) { + html += '
Бренды
'; + data.brands.forEach(brand => { + html += ` + + ${brand.name} + + `; + }); + } + + if (data.products.length > 0) { + html += '
Товары
'; + data.products.forEach(product => { + html += ` + + ${product.name} + + `; + }); + } + + suggestionsContainer.innerHTML = html; + suggestionsContainer.style.display = 'block'; + } else { + suggestionsContainer.style.display = 'none'; + } + }); + }, 300); + }); + + document.addEventListener('click', function (e) { + if (!searchInput.parentNode.contains(e.target)) { + suggestionsContainer.style.display = 'none'; + } + }); + } +}); diff --git a/public/assets/js/current-year.js b/public/assets/js/current-year.js new file mode 100644 index 0000000..6cd2d4d --- /dev/null +++ b/public/assets/js/current-year.js @@ -0,0 +1,9 @@ +function insertCurrentYear() { + const elements = document.querySelectorAll('.current-year'); + const currentYear = new Date().getFullYear(); + elements.forEach(element => { + element.textContent = currentYear; + }); +} + +insertCurrentYear(); \ No newline at end of file diff --git a/public/assets/js/jquery-3.7.1.min.js b/public/assets/js/jquery-3.7.1.min.js new file mode 100644 index 0000000..7f37b5d --- /dev/null +++ b/public/assets/js/jquery-3.7.1.min.js @@ -0,0 +1,2 @@ +/*! jQuery v3.7.1 | (c) OpenJS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(ie,e){"use strict";var oe=[],r=Object.getPrototypeOf,ae=oe.slice,g=oe.flat?function(e){return oe.flat.call(e)}:function(e){return oe.concat.apply([],e)},s=oe.push,se=oe.indexOf,n={},i=n.toString,ue=n.hasOwnProperty,o=ue.toString,a=o.call(Object),le={},v=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},y=function(e){return null!=e&&e===e.window},C=ie.document,u={type:!0,src:!0,nonce:!0,noModule:!0};function m(e,t,n){var r,i,o=(n=n||C).createElement("script");if(o.text=e,t)for(r in u)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function x(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[i.call(e)]||"object":typeof e}var t="3.7.1",l=/HTML$/i,ce=function(e,t){return new ce.fn.init(e,t)};function c(e){var t=!!e&&"length"in e&&e.length,n=x(e);return!v(e)&&!y(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+ge+")"+ge+"*"),x=new RegExp(ge+"|>"),j=new RegExp(g),A=new RegExp("^"+t+"$"),D={ID:new RegExp("^#("+t+")"),CLASS:new RegExp("^\\.("+t+")"),TAG:new RegExp("^("+t+"|[*])"),ATTR:new RegExp("^"+p),PSEUDO:new RegExp("^"+g),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+ge+"*(even|odd|(([+-]|)(\\d*)n|)"+ge+"*(?:([+-]|)"+ge+"*(\\d+)|))"+ge+"*\\)|)","i"),bool:new RegExp("^(?:"+f+")$","i"),needsContext:new RegExp("^"+ge+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+ge+"*((?:-\\d)?\\d*)"+ge+"*\\)|)(?=[^-]|$)","i")},N=/^(?:input|select|textarea|button)$/i,q=/^h\d$/i,L=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,H=/[+~]/,O=new RegExp("\\\\[\\da-fA-F]{1,6}"+ge+"?|\\\\([^\\r\\n\\f])","g"),P=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},M=function(){V()},R=J(function(e){return!0===e.disabled&&fe(e,"fieldset")},{dir:"parentNode",next:"legend"});try{k.apply(oe=ae.call(ye.childNodes),ye.childNodes),oe[ye.childNodes.length].nodeType}catch(e){k={apply:function(e,t){me.apply(e,ae.call(t))},call:function(e){me.apply(e,ae.call(arguments,1))}}}function I(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(V(e),e=e||T,C)){if(11!==p&&(u=L.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return k.call(n,a),n}else if(f&&(a=f.getElementById(i))&&I.contains(e,a)&&a.id===i)return k.call(n,a),n}else{if(u[2])return k.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&e.getElementsByClassName)return k.apply(n,e.getElementsByClassName(i)),n}if(!(h[t+" "]||d&&d.test(t))){if(c=t,f=e,1===p&&(x.test(t)||m.test(t))){(f=H.test(t)&&U(e.parentNode)||e)==e&&le.scope||((s=e.getAttribute("id"))?s=ce.escapeSelector(s):e.setAttribute("id",s=S)),o=(l=Y(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+Q(l[o]);c=l.join(",")}try{return k.apply(n,f.querySelectorAll(c)),n}catch(e){h(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return re(t.replace(ve,"$1"),e,n,r)}function W(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function F(e){return e[S]=!0,e}function $(e){var t=T.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function B(t){return function(e){return fe(e,"input")&&e.type===t}}function _(t){return function(e){return(fe(e,"input")||fe(e,"button"))&&e.type===t}}function z(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&R(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function X(a){return F(function(o){return o=+o,F(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function U(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}function V(e){var t,n=e?e.ownerDocument||e:ye;return n!=T&&9===n.nodeType&&n.documentElement&&(r=(T=n).documentElement,C=!ce.isXMLDoc(T),i=r.matches||r.webkitMatchesSelector||r.msMatchesSelector,r.msMatchesSelector&&ye!=T&&(t=T.defaultView)&&t.top!==t&&t.addEventListener("unload",M),le.getById=$(function(e){return r.appendChild(e).id=ce.expando,!T.getElementsByName||!T.getElementsByName(ce.expando).length}),le.disconnectedMatch=$(function(e){return i.call(e,"*")}),le.scope=$(function(){return T.querySelectorAll(":scope")}),le.cssHas=$(function(){try{return T.querySelector(":has(*,:jqfake)"),!1}catch(e){return!0}}),le.getById?(b.filter.ID=function(e){var t=e.replace(O,P);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&C){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(O,P);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&C){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):t.querySelectorAll(e)},b.find.CLASS=function(e,t){if("undefined"!=typeof t.getElementsByClassName&&C)return t.getElementsByClassName(e)},d=[],$(function(e){var t;r.appendChild(e).innerHTML="",e.querySelectorAll("[selected]").length||d.push("\\["+ge+"*(?:value|"+f+")"),e.querySelectorAll("[id~="+S+"-]").length||d.push("~="),e.querySelectorAll("a#"+S+"+*").length||d.push(".#.+[+~]"),e.querySelectorAll(":checked").length||d.push(":checked"),(t=T.createElement("input")).setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),r.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&d.push(":enabled",":disabled"),(t=T.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||d.push("\\["+ge+"*name"+ge+"*="+ge+"*(?:''|\"\")")}),le.cssHas||d.push(":has"),d=d.length&&new RegExp(d.join("|")),l=function(e,t){if(e===t)return a=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!le.sortDetached&&t.compareDocumentPosition(e)===n?e===T||e.ownerDocument==ye&&I.contains(ye,e)?-1:t===T||t.ownerDocument==ye&&I.contains(ye,t)?1:o?se.call(o,e)-se.call(o,t):0:4&n?-1:1)}),T}for(e in I.matches=function(e,t){return I(e,null,null,t)},I.matchesSelector=function(e,t){if(V(e),C&&!h[t+" "]&&(!d||!d.test(t)))try{var n=i.call(e,t);if(n||le.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){h(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(O,P),e[3]=(e[3]||e[4]||e[5]||"").replace(O,P),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||I.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&I.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return D.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&j.test(n)&&(t=Y(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(O,P).toLowerCase();return"*"===e?function(){return!0}:function(e){return fe(e,t)}},CLASS:function(e){var t=s[e+" "];return t||(t=new RegExp("(^|"+ge+")"+e+"("+ge+"|$)"))&&s(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=I.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function T(e,n,r){return v(n)?ce.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?ce.grep(e,function(e){return e===n!==r}):"string"!=typeof n?ce.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(ce.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||k,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:S.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof ce?t[0]:t,ce.merge(this,ce.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:C,!0)),w.test(r[1])&&ce.isPlainObject(t))for(r in t)v(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=C.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):v(e)?void 0!==n.ready?n.ready(e):e(ce):ce.makeArray(e,this)}).prototype=ce.fn,k=ce(C);var E=/^(?:parents|prev(?:Until|All))/,j={children:!0,contents:!0,next:!0,prev:!0};function A(e,t){while((e=e[t])&&1!==e.nodeType);return e}ce.fn.extend({has:function(e){var t=ce(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,Ce=/^$|^module$|\/(?:java|ecma)script/i;xe=C.createDocumentFragment().appendChild(C.createElement("div")),(be=C.createElement("input")).setAttribute("type","radio"),be.setAttribute("checked","checked"),be.setAttribute("name","t"),xe.appendChild(be),le.checkClone=xe.cloneNode(!0).cloneNode(!0).lastChild.checked,xe.innerHTML="",le.noCloneChecked=!!xe.cloneNode(!0).lastChild.defaultValue,xe.innerHTML="",le.option=!!xe.lastChild;var ke={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function Se(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&fe(e,t)?ce.merge([e],n):n}function Ee(e,t){for(var n=0,r=e.length;n",""]);var je=/<|&#?\w+;/;function Ae(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function Re(e,t){return fe(e,"table")&&fe(11!==t.nodeType?t:t.firstChild,"tr")&&ce(e).children("tbody")[0]||e}function Ie(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function We(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Fe(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(_.hasData(e)&&(s=_.get(e).events))for(i in _.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),C.head.appendChild(r[0])},abort:function(){i&&i()}}});var Jt,Kt=[],Zt=/(=)\?(?=&|$)|\?\?/;ce.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Kt.pop()||ce.expando+"_"+jt.guid++;return this[e]=!0,e}}),ce.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Zt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Zt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=v(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Zt,"$1"+r):!1!==e.jsonp&&(e.url+=(At.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||ce.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=ie[r],ie[r]=function(){o=arguments},n.always(function(){void 0===i?ce(ie).removeProp(r):ie[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Kt.push(r)),o&&v(i)&&i(o[0]),o=i=void 0}),"script"}),le.createHTMLDocument=((Jt=C.implementation.createHTMLDocument("").body).innerHTML="
",2===Jt.childNodes.length),ce.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(le.createHTMLDocument?((r=(t=C.implementation.createHTMLDocument("")).createElement("base")).href=C.location.href,t.head.appendChild(r)):t=C),o=!n&&[],(i=w.exec(e))?[t.createElement(i[1])]:(i=Ae([e],t,o),o&&o.length&&ce(o).remove(),ce.merge([],i.childNodes)));var r,i,o},ce.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(ce.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},ce.expr.pseudos.animated=function(t){return ce.grep(ce.timers,function(e){return t===e.elem}).length},ce.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=ce.css(e,"position"),c=ce(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=ce.css(e,"top"),u=ce.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),v(t)&&(t=t.call(e,n,ce.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},ce.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){ce.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===ce.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===ce.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=ce(e).offset()).top+=ce.css(e,"borderTopWidth",!0),i.left+=ce.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-ce.css(r,"marginTop",!0),left:t.left-i.left-ce.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===ce.css(e,"position"))e=e.offsetParent;return e||J})}}),ce.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;ce.fn[t]=function(e){return M(this,function(e,t,n){var r;if(y(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),ce.each(["top","left"],function(e,n){ce.cssHooks[n]=Ye(le.pixelPosition,function(e,t){if(t)return t=Ge(e,n),_e.test(t)?ce(e).position()[n]+"px":t})}),ce.each({Height:"height",Width:"width"},function(a,s){ce.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){ce.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return M(this,function(e,t,n){var r;return y(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?ce.css(e,t,i):ce.style(e,t,n,i)},s,n?e:void 0,n)}})}),ce.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){ce.fn[t]=function(e){return this.on(t,e)}}),ce.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.on("mouseenter",e).on("mouseleave",t||e)}}),ce.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){ce.fn[n]=function(e,t){return 00){h=a(this[0]);var p=h.data(a.mask.dataName);return p?p():void 0}return g=a.extend({autoclear:a.mask.autoclear,placeholder:a.mask.placeholder,completed:null},g),i=a.mask.definitions,j=[],k=n=c.length,l=null,a.each(c.split(""),function(a,b){"?"==b?(n--,k=a):i[b]?(j.push(new RegExp(i[b])),null===l&&(l=j.length-1),k>a&&(m=j.length-1)):j.push(null)}),this.trigger("unmask").each(function(){function h(){if(g.completed){for(var a=l;m>=a;a++)if(j[a]&&C[a]===p(a))return;g.completed.call(B)}}function p(a){return g.placeholder.charAt(a=0&&!j[a];);return a}function s(a,b){var c,d;if(!(0>a)){for(c=a,d=q(b);n>c;c++)if(j[c]){if(!(n>d&&j[c].test(C[d])))break;C[c]=C[d],C[d]=p(d),d=q(d)}z(),B.caret(Math.max(l,a))}}function t(a){var b,c,d,e;for(b=a,c=p(a);n>b;b++)if(j[b]){if(d=q(b),e=C[b],C[b]=c,!(n>d&&j[d].test(e)))break;c=e}}function u(){var a=B.val(),b=B.caret();if(o&&o.length&&o.length>a.length){for(A(!0);b.begin>0&&!j[b.begin-1];)b.begin--;if(0===b.begin)for(;b.beging)&&g&&13!==g){if(i.end-i.begin!==0&&(y(i.begin,i.end),s(i.begin,i.end-1)),c=q(i.begin-1),n>c&&(d=String.fromCharCode(g),j[c].test(d))){if(t(c),C[c]=d,z(),e=q(c),f){var k=function(){a.proxy(a.fn.caret,B,e)()};setTimeout(k,0)}else B.caret(e);i.begin<=m&&h()}b.preventDefault()}}}function y(a,b){var c;for(c=a;b>c&&n>c;c++)j[c]&&(C[c]=p(c))}function z(){B.val(C.join(""))}function A(a){var b,c,d,e=B.val(),f=-1;for(b=0,d=0;n>b;b++)if(j[b]){for(C[b]=p(b);d++e.length){y(b+1,n);break}}else C[b]===e.charAt(d)&&d++,k>b&&(f=b);return a?z():k>f+1?g.autoclear||C.join("")===D?(B.val()&&B.val(""),y(0,n)):z():(z(),B.val(B.val().substring(0,f+1))),k?b:l}var B=a(this),C=a.map(c.split(""),function(a,b){return"?"!=a?i[a]?p(b):a:void 0}),D=C.join(""),E=B.val();B.data(a.mask.dataName,function(){return a.map(C,function(a,b){return j[b]&&a!=p(b)?a:null}).join("")}),B.one("unmask",function(){B.off(".mask").removeData(a.mask.dataName)}).on("focus.mask",function(){if(!B.prop("readonly")){clearTimeout(b);var a;E=B.val(),a=A(),b=setTimeout(function(){B.get(0)===document.activeElement&&(z(),a==c.replace("?","").length?B.caret(0,a):B.caret(a))},10)}}).on("blur.mask",v).on("keydown.mask",w).on("keypress.mask",x).on("input.mask paste.mask",function(){B.prop("readonly")||setTimeout(function(){var a=A(!0);B.caret(a),h()},0)}),e&&f&&B.off("input.mask").on("input.mask",u),A()})}})}); \ No newline at end of file diff --git a/public/assets/js/nouislider.min.js b/public/assets/js/nouislider.min.js new file mode 100644 index 0000000..978a4a3 --- /dev/null +++ b/public/assets/js/nouislider.min.js @@ -0,0 +1 @@ +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).noUiSlider={})}(this,function(ut){"use strict";function n(t){return"object"==typeof t&&"function"==typeof t.to}function ct(t){t.parentElement.removeChild(t)}function pt(t){return null!=t}function ft(t){t.preventDefault()}function i(t){return"number"==typeof t&&!isNaN(t)&&isFinite(t)}function dt(t,e,r){0=e[r];)r+=1;return r}function r(t,e,r){if(r>=t.slice(-1)[0])return 100;var n=l(r,t),i=t[n-1],o=t[n],t=e[n-1],n=e[n];return t+(r=r,a(o=[i,o],o[0]<0?r+Math.abs(o[0]):r-o[0],0)/s(t,n))}function o(t,e,r,n){if(100===n)return n;var i=l(n,t),o=t[i-1],s=t[i];return r?(s-o)/2this.xPct[n+1];)n++;else t===this.xPct[this.xPct.length-1]&&(n=this.xPct.length-2);r||t!==this.xPct[n+1]||n++;for(var i,o=1,s=(e=null===e?[]:e)[n],a=0,l=0,u=0,c=r?(t-this.xPct[n])/(this.xPct[n+1]-this.xPct[n]):(this.xPct[n+1]-t)/(this.xPct[n+1]-this.xPct[n]);0= 2) required for mode 'count'.");for(var e=t.values-1,r=100/e,n=[];e--;)n[e]=e*r;return n.push(100),U(n,t.stepped)}(d),m={},t=S.xVal[0],e=S.xVal[S.xVal.length-1],g=!1,v=!1,b=0;return(h=h.slice().sort(function(t,e){return t-e}).filter(function(t){return!this[t]&&(this[t]=!0)},{}))[0]!==t&&(h.unshift(t),g=!0),h[h.length-1]!==e&&(h.push(e),v=!0),h.forEach(function(t,e){var r,n,i,o,s,a,l,u,t=t,c=h[e+1],p=d.mode===ut.PipsMode.Steps,f=(f=p?S.xNumSteps[e]:f)||c-t;for(void 0===c&&(c=t),f=Math.max(f,1e-7),r=t;r<=c;r=Number((r+f).toFixed(7))){for(a=(o=(i=S.toStepping(r))-b)/(d.density||1),u=o/(l=Math.round(a)),n=1;n<=l;n+=1)m[(s=b+n*u).toFixed(5)]=[S.fromStepping(s),0];a=-1ut.PipsType.NoValue&&((t=P(a,!1)).className=p(n,f.cssClasses.value),t.setAttribute("data-value",String(r)),t.style[f.style]=e+"%",t.innerHTML=String(s.to(r))))}),a}function L(){s&&(ct(s),s=null)}function T(t){L();var e=D(t),r=t.filter,t=t.format||{to:function(t){return String(Math.round(t))}};return s=d.appendChild(O(e,r,t))}function j(){var t=i.getBoundingClientRect(),e="offset"+["Width","Height"][f.ort];return 0===f.ort?t.width||i[e]:t.height||i[e]}function z(n,i,o,s){function e(t){var e,r=function(e,t,r){var n=0===e.type.indexOf("touch"),i=0===e.type.indexOf("mouse"),o=0===e.type.indexOf("pointer"),s=0,a=0;0===e.type.indexOf("MSPointer")&&(o=!0);if("mousedown"===e.type&&!e.buttons&&!e.touches)return!1;if(n){var l=function(t){t=t.target;return t===r||r.contains(t)||e.composed&&e.composedPath().shift()===r};if("touchstart"===e.type){n=Array.prototype.filter.call(e.touches,l);if(1=r[e-1]});if(x!==!e)return x=!x,xt(f,f.connect.map(function(t){return!t})),void at()}rt(t),rt(t+1),x&&(rt(t-1),rt(t+2))}function tt(){g.forEach(function(t){var e=50r.stepAfter.startValue&&(i=r.stepAfter.startValue-n),t=n>r.thisStep.startValue?r.thisStep.step:!1!==r.stepBefore.step&&n-r.stepBefore.highestStep,100===e?i=null:0===e&&(t=null);e=S.countStepDecimals();return null!==i&&!1!==i&&(i=Number(i.toFixed(e))),[t=null!==t&&!1!==t?Number(t.toFixed(e)):t,i]}function at(){for(;n.firstChild;)n.removeChild(n.firstChild);for(var t=0;t<=f.handles;t++)u[t]=N(n,f.connect[t]),rt(t);Y({drag:f.events.drag,fixed:!0})}gt(t=d,f.cssClasses.target),0===f.dir?gt(t,f.cssClasses.ltr):gt(t,f.cssClasses.rtl),0===f.ort?gt(t,f.cssClasses.horizontal):gt(t,f.cssClasses.vertical),gt(t,"rtl"===getComputedStyle(t).direction?f.cssClasses.textDirectionRtl:f.cssClasses.textDirectionLtr),i=P(t,f.cssClasses.base),function(t,e){n=P(e,f.cssClasses.connects),l=[],(u=[]).push(N(n,t[0]));for(var r=0;r { + el.textContent = count; + el.classList.add('cart-bump'); + setTimeout(() => el.classList.remove('cart-bump'), 300); + }); +} \ No newline at end of file diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000..e69de29 diff --git a/public/index.php b/public/index.php new file mode 100644 index 0000000..1d69f3a --- /dev/null +++ b/public/index.php @@ -0,0 +1,55 @@ +make(Kernel::class); + +$response = $kernel->handle( + $request = Request::capture() +)->send(); + +$kernel->terminate($request, $response); diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 0000000..eb05362 --- /dev/null +++ b/public/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: diff --git a/resources/css/app.css b/resources/css/app.css new file mode 100644 index 0000000..e0b9d48 --- /dev/null +++ b/resources/css/app.css @@ -0,0 +1,16 @@ +@import "../../node_modules/bootstrap/dist/css/bootstrap.min.css"; +@import "bootstrap-icons/font/bootstrap-icons.css"; + +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer components { + .btn-orange { + @apply tw-bg-orange tw-text-light-gray tw-px-4 tw-py-2 tw-rounded-lg hover:tw-bg-dark-orange hover:tw-text-light-gray focus:tw-bg-dark-orange focus:tw-text-light-gray active:tw-bg-dark-orange active:tw-text-light-gray active:tw-scale-95 tw-transition-all; + } + + .btn-dark-green { + @apply tw-bg-dark-green tw-text-light-gray tw-px-4 tw-py-2 tw-rounded-lg hover:tw-bg-dark-green-hover focus:tw-bg-dark-green-hover active:tw-bg-dark-green-hover active:tw-scale-95 tw-transition-all; + } +} \ No newline at end of file diff --git a/resources/js/app.js b/resources/js/app.js new file mode 100644 index 0000000..b1a0038 --- /dev/null +++ b/resources/js/app.js @@ -0,0 +1,7 @@ +import '../css/app.css'; +import * as bootstrap from 'bootstrap'; + +import Sortable from 'sortablejs'; +window.Sortable = Sortable; + +window.bootstrap = bootstrap; \ No newline at end of file diff --git a/resources/js/bootstrap.js b/resources/js/bootstrap.js new file mode 100644 index 0000000..846d350 --- /dev/null +++ b/resources/js/bootstrap.js @@ -0,0 +1,32 @@ +/** + * We'll load the axios HTTP library which allows us to easily issue requests + * to our Laravel back-end. This library automatically handles sending the + * CSRF token as a header based on the value of the "XSRF" token cookie. + */ + +import axios from 'axios'; +window.axios = axios; + +window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; + +/** + * Echo exposes an expressive API for subscribing to channels and listening + * for events that are broadcast by Laravel. Echo and event broadcasting + * allows your team to easily build robust real-time web applications. + */ + +// import Echo from 'laravel-echo'; + +// import Pusher from 'pusher-js'; +// window.Pusher = Pusher; + +// window.Echo = new Echo({ +// broadcaster: 'pusher', +// key: import.meta.env.VITE_PUSHER_APP_KEY, +// cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER ?? 'mt1', +// wsHost: import.meta.env.VITE_PUSHER_HOST ? import.meta.env.VITE_PUSHER_HOST : `ws-${import.meta.env.VITE_PUSHER_APP_CLUSTER}.pusher.com`, +// wsPort: import.meta.env.VITE_PUSHER_PORT ?? 80, +// wssPort: import.meta.env.VITE_PUSHER_PORT ?? 443, +// forceTLS: (import.meta.env.VITE_PUSHER_SCHEME ?? 'https') === 'https', +// enabledTransports: ['ws', 'wss'], +// }); diff --git a/resources/views/about.blade.php b/resources/views/about.blade.php new file mode 100644 index 0000000..fda2bdf --- /dev/null +++ b/resources/views/about.blade.php @@ -0,0 +1,167 @@ +@extends('layouts.app') + +@section('title', 'О нас') + +@section('content') +
+ + + {{-- Основной блок --}} +
+
+

+ TailAndPaws — это команда профессионалов, объединенных любовью к животным. +

+
+
+ + {{-- Наши преимущества --}} +
+
+
+ +

Быстрая доставка

+

Доставляем заказы по всей России

+
+
+
+
+ +

Гарантия качества

+

Только сертифицированные товары

+
+
+
+
+ +

Поддержка 24/7

+

Всегда готовы помочь

+
+
+
+ + {{-- История --}} +
+
+
+
+
+ +

Миссия компании — предоставлять владельцам домашних животных доступ к качественным, безопасным и проверенным товарам по доступным ценам.

+ +

Что мы предлагаем:

+
    +
  • --- Широкий ассортимент кормов, амуниции, игрушек и аксессуаров
  • +
  • --- Продукцию только от проверенных производителей
  • +
  • --- Регулярное обновление ассортимента
  • +
  • --- Доставку по всей России
  • +
  • --- Консультации специалистов по выбору товаров
  • +
+ +

Наши приоритеты:

+
    +
  • --- Качество каждого товара на полке
  • +
  • --- Честные цены без скрытых наценок
  • +
  • --- Быстрая обработка и отправка заказов
  • +
  • --- Открытость и прозрачность перед клиентами
  • +
+ +

TailAndPaws — забота, которой доверяют.

+
+
+
+
+
+ + {{-- Наши партнеры (топ бренды) --}} + @if($topBrands->count()) +
+
+

Наши партнеры

+

Мы сотрудничаем с лучшими производителями товаров для животных

+
+ @foreach($topBrands as $brand) +
+
+ @if($brand->logo) + {{ $brand->name }} + @else +
+ +
+ @endif +

{{ $brand->name }}

+
+
+ @endforeach +
+
+
+ @endif + +
+
+

Где нас найти

+
+
+
+
+

Наши контакты

+ +
+
+
+
+
+
+
+ +
+
+
+
+
+
+@endsection \ No newline at end of file diff --git a/resources/views/admin/brands/brands.blade.php b/resources/views/admin/brands/brands.blade.php new file mode 100644 index 0000000..b4032d7 --- /dev/null +++ b/resources/views/admin/brands/brands.blade.php @@ -0,0 +1,160 @@ +@extends('layouts.admin') + +@section('content') +
+ @if(session('success')) + + @endif + + @if(session('error')) + + @endif + +
+
+
+
Бренды
+
+
+ + + + + + + + + + + + + + @csrf + + + + + + + + + + + @foreach ($brands as $brand) + + + + + + + + + + @endforeach + +
НазваниеОписаниеЛогоСайтСтранаАктивен?Действия
+ + @error('name') +
{{ $message }}
+ @enderror +
+ + @error('description') +
{{ $message }}
+ @enderror +
+ + @error('logo') +
{{ $message }}
+ @enderror +
+ + @error('website') +
{{ $message }}
+ @enderror +
+ + @error('country') +
{{ $message }}
+ @enderror +
+ + + +
{{ $brand->name }} + @if ($brand->description) + {{ Str::limit($brand->description, 50) }} + @else + + @endif + + @if ($brand->logo) + {{ $brand->name }} + @else + + @endif + + @if ($brand->website) + {{ Str::limit($brand->website, 30) }} + @else + + @endif + + @if ($brand->country) + {{ $brand->country }} + @else + + @endif + + @if ($brand->is_active) + + @else + + @endif + +
+ + Редактировать + +
+ @csrf + @method('DELETE') + +
+
+
+
+
+
+
+ +
+ @if(method_exists($brands, 'hasPages') && $brands->hasPages()) + {{ $brands->links() }} + @endif +
+@endsection \ No newline at end of file diff --git a/resources/views/admin/brands/edit.blade.php b/resources/views/admin/brands/edit.blade.php new file mode 100644 index 0000000..22b006a --- /dev/null +++ b/resources/views/admin/brands/edit.blade.php @@ -0,0 +1,101 @@ +@extends('layouts.admin') + +@section('content') +
+
+
+
Редактирование бренда: {{ $brand->name }}
+
+
+
+ @csrf + @method('PUT') + +
+
+ + + @error('name') +
{{ $message }}
+ @enderror +
+ +
+ + + @error('country') +
{{ $message }}
+ @enderror +
+
+ +
+ + + @error('description') +
{{ $message }}
+ @enderror +
+ +
+
+ + + @error('website') +
{{ $message }}
+ @enderror +
+ +
+ + @if($brand->logo) +
+ {{ $brand->name }} +
+ @endif + + Оставьте пустым, чтобы не менять + @error('logo') +
{{ $message }}
+ @enderror +
+
+ +
+
+ is_active) ? 'checked' : '' }}> + +
+
+ + +
+
+
+
+@endsection \ No newline at end of file diff --git a/resources/views/admin/categories/categories.blade.php b/resources/views/admin/categories/categories.blade.php new file mode 100644 index 0000000..c0a2b10 --- /dev/null +++ b/resources/views/admin/categories/categories.blade.php @@ -0,0 +1,245 @@ +@extends('layouts.admin') + +@section('content') +
+ @if(session('success')) + + @endif + + @if(session('error')) + + @endif + +
+
+
+
Категории
+
+
+ + + + + + + + + + + + + + @csrf + + + + + + + + + + + @foreach ($categories as $index => $category) + + + + + + + + + + + + + + + + @endforeach + +
ПорядокНазваниеОписаниеИконкаИзображениеАктивна?Действия
+ + @error('parent_id') +
{{ $message }}
+ @enderror +
+ + @error('name') +
{{ $message }}
+ @enderror +
+ + @error('description') +
{{ $message }}
+ @enderror +
+ + @error('icon') +
{{ $message }}
+ @enderror +
+ + @error('image') +
{{ $message }}
+ @enderror +
+ + @error('is_active') +
{{ $message }}
+ @enderror +
+ +
+
+ @php + $canMoveUp = false; + for ($i = 0; $i < $index; $i++) { + if ($categories[$i]->parent_id == $category->parent_id) { + $canMoveUp = true; + break; + } + } + @endphp + @if($canMoveUp) +
+ @csrf + @method('PATCH') + +
+ @else + + @endif + + @php + $canMoveDown = false; + for ($i = $index + 1; $i < count($categories); $i++) { + if ($categories[$i]->parent_id == $category->parent_id) { + $canMoveDown = true; + break; + } + } + @endphp + @if($canMoveDown) +
+ @csrf + @method('PATCH') + +
+ @else + + @endif +
+
+
+ @for($i = 0; $i < ($category->level ?? 0); $i++) + + @endfor + + @if(($category->level ?? 0) > 0) + @php + $hasNext = false; + if (isset($categories[$index + 1])) { + $nextLevel = $categories[$index + 1]->level ?? 0; + $currentLevel = $category->level ?? 0; + if ($nextLevel >= $currentLevel) { + $hasNext = true; + } + } + @endphp + @if($hasNext) + ├─ + @else + └─ + @endif + @endif + + @if($category->children->count() > 0) + + @else + + @endif + + {{ $category->name }} + @if($category->children->count()) + {{ $category->children->count() }} + @endif +
+
+ @if($category->description) + {{ Str::limit($category->description, 50) }} + @else + + @endif + + @if ($category->icon) + {{ $category->name }} + @else + + @endif + + @if ($category->image) + {{ $category->name }} + @else + + @endif + + @if ($category->is_active) + + @else + + @endif + +
+ + Редактировать + +
+ @csrf + @method('DELETE') + +
+
+
+
+
+
+
+ +@if(method_exists($categories, 'hasPages') && $categories->hasPages()) +
+ {{ $categories->links() }} +
+@endif +@endsection \ No newline at end of file diff --git a/resources/views/admin/categories/edit.blade.php b/resources/views/admin/categories/edit.blade.php new file mode 100644 index 0000000..96869bf --- /dev/null +++ b/resources/views/admin/categories/edit.blade.php @@ -0,0 +1,132 @@ +@extends('layouts.admin') + +@section('content') +
+
+
+
Редактирование категории: {{ $category->name }}
+
+
+
+ @csrf + @method('PUT') + +
+
+
+ + + @error('name') +
{{ $message }}
+ @enderror +
+
+ + @error('parent_id') +
{{ $message }}
+ @enderror +
+
+ +
+ + + @error('description') +
{{ $message }}
+ @enderror +
+
+ +
+
+ +
+ + +
+ @if($category->icon) +
+ {{ $category->name }} +
+ @endif + + Оставьте пустым, чтобы не менять + @error('icon') +
{{ $message }}
+ @enderror +
+ +
+ +
+ + +
+ @if($category->image) +
+ {{ $category->name }} +
+ @endif + + Оставьте пустым, чтобы не менять + @error('image') +
{{ $message }}
+ @enderror +
+
+ +
+
+ is_active) ? 'checked' : '' }}> + +
+
+ + +
+
+
+
+@endsection \ No newline at end of file diff --git a/resources/views/admin/contacts/edit.blade.php b/resources/views/admin/contacts/edit.blade.php new file mode 100644 index 0000000..b7f280f --- /dev/null +++ b/resources/views/admin/contacts/edit.blade.php @@ -0,0 +1,347 @@ +@extends('layouts.admin') + +@section('content') +
+ @if(session('success')) + + @endif + + @if(session('error')) + + @endif + +
+
+
Редактирование данных сайта
+
+
+
+ @csrf + @method('PUT') + + + +
+ {{-- Основные данные --}} +
+
+ + + @error('name') +
{{ $message }}
+ @enderror +
+ +
+ + + @error('description') +
{{ $message }}
+ @enderror +
+ +
+ + + @error('work_hours') +
{{ $message }}
+ @enderror +
+
+ + {{-- Логотип и фавикон --}} +
+
+ +
+
+ + @error('logo') +
{{ $message }}
+ @enderror + Рекомендуемый размер: 200x200px. Максимум 2MB +
+
+ @if($contact->logo) +
+

Текущий логотип:

+ Логотип +
+ @else +
+

Логотип не загружен

+ +
+ @endif +
+
+
+ +
+ +
+
+ + @error('favicon') +
{{ $message }}
+ @enderror + Рекомендуемый размер: 16x16px или 32x32px. Максимум 1MB +
+
+ @if($contact->favicon) +
+

Текущий фавикон:

+ Фавикон +
+ @else +
+

Фавикон не загружен

+ +
+ @endif +
+
+
+
+ + {{-- Контакты --}} +
+
+ + + @error('phone') +
{{ $message }}
+ @enderror +
+ +
+ + + @error('email') +
{{ $message }}
+ @enderror +
+ +
+ + + @error('address') +
{{ $message }}
+ @enderror +
+
+ + {{-- Социальные сети --}} +
+
+ +
+ t.me/ + +
+ @error('telegram') +
{{ $message }}
+ @enderror +
+ +
+ +
+ wa.me/ + +
+ @error('whatsapp') +
{{ $message }}
+ @enderror +
+ +
+ +
+ vk.com/ + +
+ @error('vkontakte') +
{{ $message }}
+ @enderror +
+
+ + {{-- SEO --}} +
+
+ + + Рекомендуемая длина: 50-60 символов + @error('meta_title') +
{{ $message }}
+ @enderror +
+ +
+ + + Рекомендуемая длина: 150-160 символов + @error('meta_description') +
{{ $message }}
+ @enderror +
+ +
+ + + Ключевые слова через запятую + @error('meta_keywords') +
{{ $message }}
+ @enderror +
+
+
+ +
+ +
+
+
+
+
+@endsection + +@section('script') + +@endsection \ No newline at end of file diff --git a/resources/views/admin/dashboard.blade.php b/resources/views/admin/dashboard.blade.php new file mode 100644 index 0000000..c784851 --- /dev/null +++ b/resources/views/admin/dashboard.blade.php @@ -0,0 +1,113 @@ +@extends('layouts.admin') + +@section('content') +
+
+

Панель управления

+ +
+
+
+
+
Пользователи
+

{{ $totalUsers }}

+ Управление → +
+
+
+ +
+
+
+
Товары
+

{{ $totalProducts }}

+ Управление → +
+
+
+ +
+
+
+
Заказы
+

{{ $totalOrders }}

+ Управление → +
+
+
+ +
+
+
+
Выручка
+

{{ $monthlyRevenue }} ₽

+ За текущий месяц +
+
+
+
+
+ +
+
+
+
+
Последние заказы
+
+
+ + + + + + + + + + + @foreach ($recentOrders as $ord) + + + + + + + @endforeach + +
КлиентСуммаСтатус
{{ $ord->order_number }}{{ $ord->customer_name }}{{ $ord->total }} + + {{ $ord->delivery_status_name }} + +
+
+
+
+ +
+
+
+
Популярные товары
+
+
+ + + + + + + + + @foreach ($popularProducts as $product) + + + + + @endforeach + +
ТоварКол-во продаж
{{ $product->variation_name }}{{ $product->total_quantity }}
+
+
+
+
+
+@endsection \ No newline at end of file diff --git a/resources/views/admin/orders/orders.blade.php b/resources/views/admin/orders/orders.blade.php new file mode 100644 index 0000000..84b5db2 --- /dev/null +++ b/resources/views/admin/orders/orders.blade.php @@ -0,0 +1,82 @@ +@extends('layouts.admin') + +@section('title', 'Заказы') + +@section('content') +
+ @if(session('success')) + + @endif + +
+
+
Заказы
+
+ + + + @if(request('search') || request('status')) + Сбросить + @endif +
+
+
+
+ + + + + + + + + + + + + + @forelse($orders as $order) + + + + + + + + + + @empty + + + + @endforelse + +
№ заказаКлиентСуммаСтатус доставкиСтатус оплатыДатаДействия
{{ $order->order_number }}{{ $order->customer_name }}
{{ $order->customer_email }}
{{ number_format($order->total, 0, '.', ' ') }} ₽ + + {{ $order->delivery_status_name }} + + + + {{ $order->payment_status_name }} + + {{ $order->created_at->format('d.m.Y H:i') }} + + + +
Заказов не найдено
+
+ {{ $orders->links() }} +
+
+
+@endsection \ No newline at end of file diff --git a/resources/views/admin/orders/show.blade.php b/resources/views/admin/orders/show.blade.php new file mode 100644 index 0000000..e55d874 --- /dev/null +++ b/resources/views/admin/orders/show.blade.php @@ -0,0 +1,109 @@ +@extends('layouts.admin') + +@section('title', 'Заказ ' . $order->order_number) + +@section('content') +
+
+

Заказ #{{ $order->order_number }}

+ ← Назад к списку +
+ +
+
+
+
+
Информация о заказе
+
+
+

Дата: {{ $order->created_at->format('d.m.Y H:i') }}

+

Клиент: {{ $order->customer_name }}

+

Email: {{ $order->customer_email }}

+

Телефон: {{ $order->customer_phone }}

+ @if($order->shipping_address) +

Адрес доставки: {{ $order->shipping_address }}

+ @endif + @if($order->comment) +

Комментарий: {{ $order->comment }}

+ @endif +
+
+
+ +
+
+
+
Обновить статус
+
+
+
+ @csrf + @method('PUT') +
+ + +
+
+ + +
+ +
+
+
+
+
+ +
+
+
Товары в заказе
+
+
+
+ + + + + + + + + + + + + @foreach($order->items as $item) + + + + + + + + + @endforeach + + + + + + + +
ТоварВариацияSKUКол-воЦенаСумма
{{ $item->product_name }}{{ $item->variation_name ?? '—' }}{{ $item->sku ?? '—' }}{{ $item->quantity }}{{ number_format($item->price, 0, '.', ' ') }} ₽{{ number_format($item->total, 0, '.', ' ') }} ₽
Итого:{{ number_format($order->total, 0, '.', ' ') }} ₽
+
+
+
+
+@endsection \ No newline at end of file diff --git a/resources/views/admin/partials/header.blade.php b/resources/views/admin/partials/header.blade.php new file mode 100644 index 0000000..1a3d3cf --- /dev/null +++ b/resources/views/admin/partials/header.blade.php @@ -0,0 +1,5 @@ +
+
+

{{ auth()->user()->name }} | {{ auth()->user()->role->name }}

+
+
\ No newline at end of file diff --git a/resources/views/admin/partials/mobile-nav.blade.php b/resources/views/admin/partials/mobile-nav.blade.php new file mode 100644 index 0000000..beee3f0 --- /dev/null +++ b/resources/views/admin/partials/mobile-nav.blade.php @@ -0,0 +1,97 @@ + + +
+
+ +
+
\ No newline at end of file diff --git a/resources/views/admin/partials/sidebar.blade.php b/resources/views/admin/partials/sidebar.blade.php new file mode 100644 index 0000000..b18a749 --- /dev/null +++ b/resources/views/admin/partials/sidebar.blade.php @@ -0,0 +1,105 @@ + \ No newline at end of file diff --git a/resources/views/admin/permissions/edit.blade.php b/resources/views/admin/permissions/edit.blade.php new file mode 100644 index 0000000..c4e0a16 --- /dev/null +++ b/resources/views/admin/permissions/edit.blade.php @@ -0,0 +1,74 @@ +@extends('layouts.admin') + +@section('content') +
+
+
+
Редактирование права: {{ $permission->name }}
+
+
+
+ @csrf + @method('PUT') + +
+ + + @error('name') +
{{ $message }}
+ @enderror +
+ +
+ + + Уникальный идентификатор (например: manage_cart, admin_access) + @error('slug') +
{{ $message }}
+ @enderror +
+ +
+ + + Для группировки прав в интерфейсе + @error('group') +
{{ $message }}
+ @enderror +
+ +
+ + + @error('description') +
{{ $message }}
+ @enderror +
+ +
+ + + Отмена + Отмена + +
+
+
+
+
+@endsection \ No newline at end of file diff --git a/resources/views/admin/permissions/permissions.blade.php b/resources/views/admin/permissions/permissions.blade.php new file mode 100644 index 0000000..ffb62cf --- /dev/null +++ b/resources/views/admin/permissions/permissions.blade.php @@ -0,0 +1,118 @@ +@extends('layouts.admin') + +@section('content') +
+ @if(session('success')) + + @endif + + @if(session('error')) + + @endif + +
+
+
+
Права доступа
+
+
+ + + + + + + + + + + + + + @csrf + + + + + + + + + @foreach ($permissions as $permission) + + + + + + + + @endforeach + +
НазваниеSlugГруппаОписаниеДействия
+ + @error('name') +
{{ $message }}
+ @enderror +
+ + @error('slug') +
{{ $message }}
+ @enderror +
+ + @error('group') +
{{ $message }}
+ @enderror +
+ + @error('description') +
{{ $message }}
+ @enderror +
+ +
{{ $permission->name }}{{ $permission->slug }} + @if ($permission->group) + {{ $permission->group }} + @else + + @endif + {{ $permission->description ?? '—' }} +
+ + Редактировать + +
+ @csrf + @method('DELETE') + +
+
+
+
+
+
+
+ +
+ {{ $permissions->links() }} +
+@endsection \ No newline at end of file diff --git a/resources/views/admin/products/create.blade.php b/resources/views/admin/products/create.blade.php new file mode 100644 index 0000000..648a126 --- /dev/null +++ b/resources/views/admin/products/create.blade.php @@ -0,0 +1,650 @@ +@extends('layouts.admin') + +@section('title', 'Добавить товар') + +@section('content') +
+ + @if(session('success')) + + @endif + + @if(session('error')) + + @endif + +
+ @csrf + +
+
+
+
+
Основная информация
+
+
+
+
+ + + @error('name') +
{{ $message }}
+ @enderror +
+
+ +
+
+ + +
+
+ + + @error('category_id') +
{{ $message }}
+ @enderror +
+
+ +
+ + +
+ +
+
+
+ + +
+
+
+
+
+ +
+
+
Характеристики товара
+ +
+
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+
+ +
+
+
Вариации (размеры, вес, цвет)
+ +
+
+
+
+
+
+
+ +
+
+
+
SEO
+
+
+
+ + + До 60 символов +
+
+ + + До 160 символов +
+
+ + + Ключевые слова через запятую +
+
+
+
+
+ +
+ + + + + Отмена + Отмена + +
+
+
+@endsection + +@section('script') + +@endsection \ No newline at end of file diff --git a/resources/views/admin/products/edit.blade.php b/resources/views/admin/products/edit.blade.php new file mode 100644 index 0000000..fdfdff1 --- /dev/null +++ b/resources/views/admin/products/edit.blade.php @@ -0,0 +1,832 @@ +@extends('layouts.admin') + +@section('title', 'Редактировать товар') + +@section('content') +
+ @if(session('success')) + + @endif + + @if(session('error')) + + @endif + +
+ @csrf + @method('PUT') + + @php + $variationCount = old('variations') !== null ? count(old('variations')) : $product->variations->count(); + $specCount = old('attributes') !== null ? count(old('attributes')) : $product->attributes->count(); + @endphp + + + + +
+
+
+
+
Основная информация
+
+
+
+
+ + + @error('name') +
{{ $message }}
+ @enderror +
+
+ +
+
+ + +
+
+ + + @error('category_id') +
{{ $message }}
+ @enderror +
+
+ +
+ + +
+ +
+
+
+ is_active) ? 'checked' : '' }}> + +
+
+
+
+
+ +
+
+
Характеристики товара
+ +
+
+
+ @php + $productAttributes = old('attributes', $product->attributes->toArray()); + $attrIndex = 0; + @endphp + @foreach($productAttributes as $key => $value) +
+
+ +
+
+ +
+
+ +
+
+ @php $attrIndex++; @endphp + @endforeach +
+
+
+ +
+
+
Вариации (размеры, вес, цвет)
+ +
+
+
+ @foreach(old('variations', $product->variations) as $index => $variation) + @include('admin.products.partials.variation-form', [ + 'index' => $index, + 'variation' => $variation, + 'product' => $product + ]) + @endforeach +
+
+
+
+ +
+
+
+
SEO
+
+
+
+ + + До 60 символов +
+
+ + + До 160 символов +
+
+ + + Ключевые слова через запятую +
+
+
+
+
+ +
+ + + Отмена + +
+
+
+@endsection + +@section('script') + +@endsection \ No newline at end of file diff --git a/resources/views/admin/products/partials/variation-form.blade.php b/resources/views/admin/products/partials/variation-form.blade.php new file mode 100644 index 0000000..3c22da6 --- /dev/null +++ b/resources/views/admin/products/partials/variation-form.blade.php @@ -0,0 +1,180 @@ +
+ + +
+
+ + name ?? '')) }}"> + @error("variations.$index.name") +
{{ $message }}
+ @enderror +
+
+ + sku ?? '')) }}"> + @error("variations.$index.sku") +
{{ $message }}
+ @enderror +
+
+ + price ?? '')) }}"> + @error("variations.$index.price") +
{{ $message }}
+ @enderror +
+
+ +
+
+ + old_price ?? '')) }}"> +
+
+ + stock ?? 0)) }}"> + @error("variations.$index.stock") +
{{ $message }}
+ @enderror +
+
+ + @php + $weightValue = ''; + if (is_array($variation)) { + $weightValue = $variation['attributes']['weight'] ?? ''; + } else { + $weightValue = $variation->attributes->where('key', 'weight')->first()?->value ?? ''; + } + @endphp + +
+
+ +
+
+ + +
+ @php + $imagesArray = []; + + if (!is_array($variation)) { + $imagesArray = $variation->images->pluck('path')->toArray(); + } else { + $variationId = $variation['id'] ?? null; + if ($variationId && isset($product) && $product->variations) { + $originalVariation = $product->variations->where('id', $variationId)->first(); + if ($originalVariation) { + $imagesArray = $originalVariation->images->pluck('path')->toArray(); + } + } + } + @endphp + + @foreach($imagesArray as $idx => $imagePath) +
+ + @if($idx === 0) + Главное + @endif + +
+ @endforeach +
+ +
+ + + + @error("variations.{$index}.images.*") +
{{ $message }}
+ @enderror + Можно выбрать несколько фото. Первое будет главным. Перетаскивайте для сортировки. +
+
+ +
+
+ + @php + $flavorValue = ''; + if (is_array($variation)) { + $flavorValue = $variation['attributes']['flavor'] ?? ''; + } else { + $flavorValue = $variation->attributes->where('key', 'flavor')->first()?->value ?? ''; + } + @endphp + +
+
+ + @php + $colorValue = ''; + if (is_array($variation)) { + $colorValue = $variation['attributes']['color'] ?? ''; + } else { + $colorValue = $variation->attributes->where('key', 'color')->first()?->value ?? ''; + } + @endphp + +
+
+ + @php + $sizeValue = ''; + if (is_array($variation)) { + $sizeValue = $variation['attributes']['size'] ?? ''; + } else { + $sizeValue = $variation->attributes->where('key', 'size')->first()?->value ?? ''; + } + @endphp + +
+
+ +
+
+ is_default ?? false)) ? 'checked' : '' }}> + +
+
+ +
+ +
+
\ No newline at end of file diff --git a/resources/views/admin/products/products.blade.php b/resources/views/admin/products/products.blade.php new file mode 100644 index 0000000..77514cc --- /dev/null +++ b/resources/views/admin/products/products.blade.php @@ -0,0 +1,164 @@ +@extends('layouts.admin') + +@section('title', 'Товары') + +@section('content') +
+ @if(session('success')) + + @endif + + @if(session('error')) + + @endif + +
+
+ +
+
+ + + + + + + + + + + + + + + + @forelse($products as $product) + + + + + + + + + + + + @empty + + + + @endforelse + +
ФотоНазваниеКатегорияБрендВариацииЦенаОстатокСтатусДействия
+ {{ $product->name }} + + {{ $product->name }} + {{ $product->category->name ?? '-' }}{{ $product->brand->name ?? '-' }} +
+ @foreach($product->variations as $index => $variation) +
+ + {{ Str::limit($variation->name, 15) }} + + + {{ number_format($variation->price) }} ₽ + ×{{ $variation->stock }} + +
+ @endforeach + + @if($product->variations->count() > 3) + + Показать ещё {{ $product->variations->count() - 3 }} + + @endif +
+
{{ $product->price_range }} + @if($product->total_stock > 0) + {{ $product->total_stock }} шт. + @else + Нет + @endif + + @if($product->is_active) + Активен + @else + Неактивен + @endif + +
+ + Редактировать + +
+ @csrf + @method('DELETE') + +
+
+ @csrf + +
+
+
+ + Нет товаров. + Добавить первый товар +
+
+ +
+ @if(method_exists($products, 'hasPages') && $products->hasPages()) + {{ $products->links() }} + @endif +
+
+
+
+
+@endsection + +@section('script') + +@endsection \ No newline at end of file diff --git a/resources/views/admin/roles/create.blade.php b/resources/views/admin/roles/create.blade.php new file mode 100644 index 0000000..e8d1d1d --- /dev/null +++ b/resources/views/admin/roles/create.blade.php @@ -0,0 +1,79 @@ +@extends('layouts.admin') + +@section('content') +
+
+
+
Создание роли
+
+
+
+ @csrf + +
+ + + @error('name') +
{{ $message }}
+ @enderror +
+ +
+ + + Уникальный идентификатор роли (например: admin, manager) + @error('slug') +
{{ $message }}
+ @enderror +
+ +
+ + + @error('description') +
{{ $message }}
+ @enderror +
+ +
+ +
+ @foreach($permissions as $permission) +
+
+ + +
+
+ @endforeach +
+
+ + +
+
+
+
+@endsection \ No newline at end of file diff --git a/resources/views/admin/roles/edit.blade.php b/resources/views/admin/roles/edit.blade.php new file mode 100644 index 0000000..6e22853 --- /dev/null +++ b/resources/views/admin/roles/edit.blade.php @@ -0,0 +1,91 @@ +@extends('layouts.admin') + +@section('content') +
+
+
+
Редактирование роли: {{ $role->name }}
+
+
+
+ @csrf + @method('PUT') + +
+ + + @error('name') +
{{ $message }}
+ @enderror +
+ +
+ + + Уникальный идентификатор роли (например: admin, manager) + @error('slug') +
{{ $message }}
+ @enderror +
+ +
+ + + @error('description') +
{{ $message }}
+ @enderror +
+ +
+ +
+ @foreach($permissions as $permission) + @php + $checked = false; + if (old('permissions')) { + $checked = in_array($permission->id, old('permissions')); + } + else { + $checked = $role->permissions->contains($permission->id); + } + @endphp +
+
+ + +
+
+ @endforeach +
+
+ +
+ + + Отмена + Отмена + +
+
+
+
+
+@endsection \ No newline at end of file diff --git a/resources/views/admin/roles/roles.blade.php b/resources/views/admin/roles/roles.blade.php new file mode 100644 index 0000000..4eccf4b --- /dev/null +++ b/resources/views/admin/roles/roles.blade.php @@ -0,0 +1,90 @@ +@extends('layouts.admin') + +@section('content') +
+ @if(session('success')) + + @endif + + @if(session('error')) + + @endif + +
+
+
+
Роли пользователей
+ + Добавить роль + +
+
+ + + + + + + + + + + + @foreach ($roles as $role) + + + + + + + + @endforeach + +
НазваниеSlugОписаниеПраваДействия
{{ $role->name }} + {{ $role->slug }} + + @if ($role->description) + {{ Str::limit($role->description, 100) }} + @else + + @endif + + @if($role->permissions->count()) + @foreach ($role->permissions as $permission) + {{ $permission->name }} + @endforeach + @elseif($role->slug === 'super_admin') + Все права + @else + Нет прав + @endif + +
+ + Редактировать + +
+ @csrf + @method('DELETE') + +
+
+
+
+
+
+
+ +
+ {{ $roles->links() }} +
+@endsection \ No newline at end of file diff --git a/resources/views/admin/users/create.blade.php b/resources/views/admin/users/create.blade.php new file mode 100644 index 0000000..456c0cb --- /dev/null +++ b/resources/views/admin/users/create.blade.php @@ -0,0 +1,100 @@ +@extends('layouts.admin') + +@section('content') +
+
+
+
Создание пользователя
+
+
+
+ @csrf + +
+ + + @error('name') +
{{ $message }}
+ @enderror +
+ +
+ + + @error('email') +
{{ $message }}
+ @enderror +
+ +
+ + + @error('phone') +
{{ $message }}
+ @enderror +
+ +
+ + + Минимум 8 символов + @error('password') +
{{ $message }}
+ @enderror +
+ +
+ + + @error('password_confirmation') +
{{ $message }}
+ @enderror +
+ +
+ + + @error('role_id') +
{{ $message }}
+ @enderror +
+ +
+ + + Отмена + Отмена + +
+
+
+
+
+@endsection + +@section('script') + +@endsection \ No newline at end of file diff --git a/resources/views/admin/users/edit.blade.php b/resources/views/admin/users/edit.blade.php new file mode 100644 index 0000000..3ec7c11 --- /dev/null +++ b/resources/views/admin/users/edit.blade.php @@ -0,0 +1,98 @@ +@extends('layouts.admin') + +@section('content') +
+
+
+
Редактирование пользователя: {{ $user->name }}
+
+
+
+ @csrf + @method('PUT') + +
+ + + @error('name') +
{{ $message }}
+ @enderror +
+ +
+ + + @error('email') +
{{ $message }}
+ @enderror +
+ +
+ + + @error('phone') +
{{ $message }}
+ @enderror +
+ +
+ + + Оставьте пустым, если не хотите менять пароль. Минимум 8 символов + @error('password') +
{{ $message }}
+ @enderror +
+ +
+ + +
+ +
+ + + @error('role_id') +
{{ $message }}
+ @enderror +
+ +
+ + + Отмена + Отмена + +
+
+
+
+
+@endsection + +@section('script') + +@endsection \ No newline at end of file diff --git a/resources/views/admin/users/users.blade.php b/resources/views/admin/users/users.blade.php new file mode 100644 index 0000000..5ddcc62 --- /dev/null +++ b/resources/views/admin/users/users.blade.php @@ -0,0 +1,104 @@ +@extends('layouts.admin') + +@section('content') +
+ @if(session('success')) + + @endif + + @if(session('error')) + + @endif + +
+
+
+
Пользователи
+
+
+ + + @if(request('search')) + + Сбросить + + @endif +
+ + Добавить пользователя + +
+
+
+
+ + + + + + + + + + + + + @forelse ($users as $user) + + + + + + + + + @empty + + + + @endforelse + +
ИмяEmailТелефонРольДата регистрацииДействия
{{ $user->name }}{{ $user->email }}{{ $user->phone ?? '—' }} + + {{ $user->role?->name ?? 'Нет роли' }} + + {{ $user->created_at->format('d.m.Y H:i') }} +
+ + Редактировать + + @if($user->id !== auth()->id()) +
+ @csrf + @method('DELETE') + +
+ @endif +
+
Пользователи не найдены
+
+
+
+
+
+ +
+ {{ $users->links() }} +
+@endsection \ No newline at end of file diff --git a/resources/views/auth/login.blade.php b/resources/views/auth/login.blade.php new file mode 100644 index 0000000..e1bf820 --- /dev/null +++ b/resources/views/auth/login.blade.php @@ -0,0 +1,51 @@ +@extends('layouts.app') + +@php $style = 'login'; @endphp + +@section('content') +
+ +
+@endsection \ No newline at end of file diff --git a/resources/views/auth/register.blade.php b/resources/views/auth/register.blade.php new file mode 100644 index 0000000..fa6c0f4 --- /dev/null +++ b/resources/views/auth/register.blade.php @@ -0,0 +1,56 @@ +@extends('layouts.app') + +@php $style = 'login'; @endphp + +@section('content') +
+ +
+@endsection + +@section('script') + +@endsection \ No newline at end of file diff --git a/resources/views/brands/brands.blade.php b/resources/views/brands/brands.blade.php new file mode 100644 index 0000000..21685f1 --- /dev/null +++ b/resources/views/brands/brands.blade.php @@ -0,0 +1,34 @@ +@extends('layouts.app') + +@section('title', 'Бренды') + +@php $style = 'brand'; @endphp + +@section('content') +
+

Бренды

+ + + + {{ $brands->links() }} +
+@endsection \ No newline at end of file diff --git a/resources/views/brands/show.blade.php b/resources/views/brands/show.blade.php new file mode 100644 index 0000000..40129f7 --- /dev/null +++ b/resources/views/brands/show.blade.php @@ -0,0 +1,180 @@ +@extends('layouts.app') + +@section('title', $brand->name) + +@php $style = 'category'; @endphp + +@section('content') +
+ {{-- Хлебные крошки --}} + + + {{-- Шапка бренда --}} +
+ @if($brand->logo) + {{ $brand->name }} + @endif +

{{ $brand->name }}

+ + @if($brand->description) + {{ $brand->description }} + @endif +
+ + {{-- Продукты --}} +
+
+
+ +
+
+ + +
+ + {{-- Категории с полным путем --}} + @if($categoriesWithPath->count()) +
+
+

Категории

+
+ @foreach($categoriesWithPath as $category) +
+ id, request('categories', [])) ? 'checked' : '' }}> + +
+ @endforeach +
+
+
+ @endif + + {{-- Цена --}} +
+
+

Цена

+ @include('categories.partials.ui-slider', [ + 'field' => 'price', + 'max' => $maxPrice, + 'min' => $minPrice, + 'step' => 25, + 'currentMin' => request('price-min', $minPrice), + 'currentMax' => request('price-max', $maxPrice) + ]) +
+
+ + {{-- Кнопки --}} +
+ + Сбросить +
+
+
+
+ + {{-- БЛОК С КАРТОЧКАМИ --}} +
+ @if($products->count()) +
+ @foreach($products as $product) + @include('products.partials.product-card', compact('product')) + @endforeach +
+ + {{ $products->links() }} + @else +
+ Товары этого бренда пока отсутствуют. +
+ @endif +
+
+
+@endsection + +@section('script') + +@endsection \ No newline at end of file diff --git a/resources/views/cart/checkout.blade.php b/resources/views/cart/checkout.blade.php new file mode 100644 index 0000000..10f93ed --- /dev/null +++ b/resources/views/cart/checkout.blade.php @@ -0,0 +1,270 @@ +@extends('layouts.app') + +@section('title', 'Оформление заказа') + +@php $style = 'checkout'; @endphp + +@section('content') +
+
+
+

Оформление заказа

+
+
+ +
+
+
+
+
+ @csrf + + {{-- Контактные данные --}} +
Контактные данные
+
+
+ + + @error('customer_name') +
{{ $message }}
+ @enderror +
+
+ + + @error('customer_email') +
{{ $message }}
+ @enderror +
+
+ + + @error('customer_phone') +
{{ $message }}
+ @enderror +
+
+ + {{-- Способ доставки --}} +
Способ доставки
+
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ @error('delivery_method') +
{{ $message }}
+ @enderror +
+
+ + {{-- Адрес доставки (показывается только для курьера и экспресса) --}} + + + {{-- Способ оплаты --}} +
Способ оплаты
+
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ @error('payment_method') +
{{ $message }}
+ @enderror +
+
+ + {{-- Комментарий --}} +
Комментарий к заказу
+
+
+ +
+
+ +
+ + + Назад + +
+
+
+
+
+ + {{-- Информация о заказе --}} +
+
+
+
Ваш заказ
+ +
+ @foreach($cartItems as $item) +
+
+ {{ $item->variation->product->name }} + @if($item->variation->name) +
{{ $item->variation->name }} + @endif +
{{ $item->quantity }} шт. +
+
+ {{ number_format($item->total, 0, '.', ' ') }} ₽ +
+
+ @endforeach +
+ +
+
+ Товары ({{ $cartItems->sum('quantity') }} шт.) + {{ number_format($total, 0, '.', ' ') }} ₽ +
+
+ Доставка + 200 ₽ +
+
+
+ Итого к оплате: + {{ number_format($total + 200, 0, '.', ' ') }} ₽ +
+
+
+
+
+
+
+@endsection + +@section('script') + +@endsection \ No newline at end of file diff --git a/resources/views/cart/show.blade.php b/resources/views/cart/show.blade.php new file mode 100644 index 0000000..6243611 --- /dev/null +++ b/resources/views/cart/show.blade.php @@ -0,0 +1,299 @@ +@extends('layouts.app') + +@section('title', 'Корзина') + +@php $style = 'cart'; @endphp + +@section('content') +
+
+
+

Корзина

+
+
+ + @if($cartItems->count()) +
+ {{-- Список товаров --}} +
+
+
+ @foreach($cartItems as $item) +
+
+ {{-- Изображение --}} +
+ + {{ $item->variation->product->name }} + +
+ + {{-- Информация о товаре --}} +
+ +
{{ $item->variation->product->name }}
+
+ @if($item->variation->name) + {{ $item->variation->name }} + @endif + Артикул: {{ $item->variation->sku }} +
+ + {{-- Количество и цена --}} +
+
+ {{-- Количество (как на странице продукта) --}} +
+ + + +
+ + {{-- Цена --}} +
+ {{ number_format($item->total, 0, '.', ' ') }} ₽ +
{{ number_format($item->price, 0, '.', ' ') }} ₽ / шт
+
+ + {{-- Удалить --}} + +
+
+
+
+ @endforeach +
+
+
+ + {{-- Итого --}} +
+
+
+
Итого
+ +
+ Товары ({{ $cartItems->sum('quantity') }} шт.) + {{ number_format($total, 0, '.', ' ') }} ₽ +
+ +
+ Скидка + 0 ₽ +
+ +
+ +
+ Итого к оплате: + {{ number_format($total, 0, '.', ' ') }} ₽ +
+ + + Перейти к оформлению + + + +
+
+
+
+ @else + {{-- Пустая корзина --}} +
+
+

Корзина пуста

+

Добавьте товары в корзину, чтобы оформить заказ

+ + Перейти к покупкам + +
+
+ @endif +
+@endsection + +@section('script') + +@endsection \ No newline at end of file diff --git a/resources/views/categories/partials/categories-block.blade.php b/resources/views/categories/partials/categories-block.blade.php new file mode 100644 index 0000000..28ffc58 --- /dev/null +++ b/resources/views/categories/partials/categories-block.blade.php @@ -0,0 +1,14 @@ +
+ @foreach ($categories as $category) + + @endforeach +
\ No newline at end of file diff --git a/resources/views/categories/partials/ui-slider.blade.php b/resources/views/categories/partials/ui-slider.blade.php new file mode 100644 index 0000000..3005ea3 --- /dev/null +++ b/resources/views/categories/partials/ui-slider.blade.php @@ -0,0 +1,94 @@ +
+
+
+ от + +
+
+
+
+ до + +
+
+
+
+ + \ No newline at end of file diff --git a/resources/views/categories/show.blade.php b/resources/views/categories/show.blade.php new file mode 100644 index 0000000..f290ee8 --- /dev/null +++ b/resources/views/categories/show.blade.php @@ -0,0 +1,191 @@ +@extends('layouts.app') + +@section('title', $category->name) + +@php $style = 'category'; @endphp + +@section('content') +
+ + +
+

{{ $category->name }}

+ + @if($category->description) + {{ $category->description }} + @endif +
+ + {{-- Подкатегории --}} + @if($children->count()) +
+
+

Подкатегории

+ @include('categories.partials.categories-block', ['categories' => $children]) +
+
+ @endif + + {{-- Продукты --}} +
+
+
+ +
+
+ + + {{-- ФОРМА ФИЛЬТРОВ --}} +
+ + {{-- Бренды --}} + @if($brands->count()) +
+
+

Бренды

+
+ @foreach($brands as $brand) +
+ id, request('brands', [])) ? 'checked' : '' }}> + +
+ @endforeach +
+
+
+ @endif + + {{-- Цена --}} +
+
+

Цена

+ @include('categories.partials.ui-slider', [ + 'field' => 'price', + 'max' => $maxPrice, + 'min' => $minPrice, + 'step' => 25, + 'currentMin' => request('price-min', $minPrice), + 'currentMax' => request('price-max', $maxPrice) + ]) +
+
+ + {{-- Кнопки --}} +
+ + Сбросить +
+
+
+
+ + {{-- БЛОК С КАРТОЧКАМИ --}} +
+ @if($products->count()) +
+ @foreach($products as $product) + @include('products.partials.product-card', compact('product')) + @endforeach +
+ + {{ $products->links() }} + @else +
+ В этой категории пока нет товаров. +
+ @endif +
+
+
+@endsection + +@section('script') + +@endsection \ No newline at end of file diff --git a/resources/views/contacts.blade.php b/resources/views/contacts.blade.php new file mode 100644 index 0000000..2411e47 --- /dev/null +++ b/resources/views/contacts.blade.php @@ -0,0 +1,102 @@ +@extends('layouts.app') + +@section('title', 'Контакты') + +@section('content') +
+ + +
+
+
+
+

Контактная информация

+ +
+
+ +
+ Адрес
+ {{ $menuContacts->address ?? 'г. Челябинск, ул. Дружбы, д. 15' }} +
+
+ + + + + +
+ +
+ Режим работы
+ {{ $menuContacts->work_hours ?? 'Пн-Пт: 10:00 - 18:00, Сб-Вс: выходной' }} +
+
+
+ +
+ +
+

Социальные сети:

+ +
+
+
+
+ +
+
+
+ +
+
+
+
+ + +
+@endsection \ No newline at end of file diff --git a/resources/views/index.blade.php b/resources/views/index.blade.php new file mode 100644 index 0000000..f9b9db6 --- /dev/null +++ b/resources/views/index.blade.php @@ -0,0 +1,18 @@ +@extends('layouts.app') + +@php $style = 'index'; @endphp + +@section('content') +
+ @include('partials.index.carousel-discount', [ 'carousel_id' => 'carouselDiscount', 'items' => $discountedProducts, 'interval' => 7000 ]) +
+
+ @include('categories.partials.categories-block', compact('categories')) +
+
+ @include('partials.index.product-carousel', [ 'title' => 'Сухие корма для кошек', 'carousel_id' => 'carouselDryCatFood', 'items' => $dryCatFood, 'interval' => 7000 ]) +
+
+ @include('partials.index.product-carousel', [ 'title' => 'Сухие корма для собак', 'carousel_id' => 'carouselDryDogFood', 'items' => $dryDogFood, 'interval' => 7000 ]) +
+@endsection \ No newline at end of file diff --git a/resources/views/layouts/admin.blade.php b/resources/views/layouts/admin.blade.php new file mode 100644 index 0000000..1cab0e9 --- /dev/null +++ b/resources/views/layouts/admin.blade.php @@ -0,0 +1,44 @@ + + + + + + + + @if(isset($product)) + + @endif + @yield('title', 'Админ-панель') + + + @vite(['resources/css/app.css', 'resources/js/app.js']) + + + + + +
+
+
+ @include('admin.partials.sidebar') +
+ +
+
+ @include('admin.partials.mobile-nav') +
+ +
+ @include('admin.partials.header') + @yield('content') +
+
+
+
+ + + + @yield('script') + + + \ No newline at end of file diff --git a/resources/views/layouts/app.blade.php b/resources/views/layouts/app.blade.php new file mode 100644 index 0000000..39b97bd --- /dev/null +++ b/resources/views/layouts/app.blade.php @@ -0,0 +1,125 @@ + + + + + + + + @yield('title', 'Хвостики и лапки') + + @vite(['resources/css/app.css', 'resources/js/app.js']) + + + + @if (isset($style)) + + @endif + + + + @include('partials.menu.menu') + +
+ @yield('content') +
+ +
+
+
+ {{-- О компании --}} + + + {{-- Категории (0 уровень) --}} + + + {{-- Контакты --}} + +
+
+

© {{ $menuContacts->name }}. Все права защищены.

+ Политика обработки персональных данных +
+
+
+ + + + + + + + @yield('script') + + + + \ No newline at end of file diff --git a/resources/views/maket.blade.php b/resources/views/maket.blade.php new file mode 100644 index 0000000..74df8be --- /dev/null +++ b/resources/views/maket.blade.php @@ -0,0 +1 @@ +@extends('layouts.app') \ No newline at end of file diff --git a/resources/views/partials/index/carousel-discount.blade.php b/resources/views/partials/index/carousel-discount.blade.php new file mode 100644 index 0000000..7076aed --- /dev/null +++ b/resources/views/partials/index/carousel-discount.blade.php @@ -0,0 +1,47 @@ + \ No newline at end of file diff --git a/resources/views/partials/index/product-carousel.blade.php b/resources/views/partials/index/product-carousel.blade.php new file mode 100644 index 0000000..9af8544 --- /dev/null +++ b/resources/views/partials/index/product-carousel.blade.php @@ -0,0 +1,58 @@ +@php +$title = $title ?? 'Товары'; +$carousel_id = $carousel_id ?? 'productCarousel'; +$interval = $interval ?? 5000; +@endphp + +@if($items->count()) +
+

+ + {{ $title }} + +

+ + +
+@else +
+

Товары не найдены

+
+@endif \ No newline at end of file diff --git a/resources/views/partials/menu/catalog-megamenu.blade.php b/resources/views/partials/menu/catalog-megamenu.blade.php new file mode 100644 index 0000000..cd4229b --- /dev/null +++ b/resources/views/partials/menu/catalog-megamenu.blade.php @@ -0,0 +1,58 @@ +
+
+ @foreach($menuCategories as $category) +
+
+ {{ $category->name }} + + {{ $category->name }} + +
+
    + {{-- Подкатегории 1 уровня --}} + @foreach($category->children as $child) + @if($child->children->count() > 0) + {{-- Категория с подкатегориями --}} +
  • + + {{ $child->name }} + +
  • + @foreach($child->children as $subChild) +
  • + + — {{ $subChild->name }} + +
  • + @endforeach + @else + {{-- Обычная категория --}} +
  • + + {{ $child->name }} + +
  • + @endif + @endforeach +
+
+ @endforeach +
+ + {{-- Популярные бренды --}} + @if(isset($menuBrands) && $menuBrands->count() > 0) +
+
+

Популярные бренды:

+
+ @foreach($menuBrands as $brand) + + {{ $brand->name }} + + @endforeach +
+
+
+ @endif +
\ No newline at end of file diff --git a/resources/views/partials/menu/menu.blade.php b/resources/views/partials/menu/menu.blade.php new file mode 100644 index 0000000..9772b8d --- /dev/null +++ b/resources/views/partials/menu/menu.blade.php @@ -0,0 +1,125 @@ +
+
+ +
+
\ No newline at end of file diff --git a/resources/views/privacy-policy.blade.php b/resources/views/privacy-policy.blade.php new file mode 100644 index 0000000..2270783 --- /dev/null +++ b/resources/views/privacy-policy.blade.php @@ -0,0 +1,207 @@ +@extends('layouts.app') + +@section('title', 'Политика обработки персональных данных') + +@section('content') +
+ + +
+
+

Политика обработки персональных данных

+ +
+ Актуальная версия: 1.0 от 06.04.2026 +
+ + {{-- 1. Общие положения --}} +
+

1. Общие положения

+

Настоящая политика обработки персональных данных составлена в соответствии с требованиями Федерального закона от 27.07.2006. № 152-ФЗ «О персональных данных» (далее — Закон о персональных данных) и определяет порядок обработки персональных данных и меры по обеспечению безопасности персональных данных, предпринимаемые «Хвостики и Лапки» (далее — Оператор).

+

Оператор ставит своей важнейшей целью и условием осуществления своей деятельности соблюдение прав и свобод человека и гражданина при обработке его персональных данных, в том числе защиты прав на неприкосновенность частной жизни, личную и семейную тайну.

+

Настоящая политика Оператора в отношении обработки персональных данных (далее — Политика) применяется ко всей информации, которую Оператор может получить о посетителях веб-сайта http://tailandpuws.ru/.

+
+ + {{-- 2. Основные понятия --}} +
+

2. Основные понятия

+
+
+
+ Автоматизированная обработка +

Обработка персональных данных с помощью средств вычислительной техники.

+
+
+
+
+ Блокирование персональных данных +

Временное прекращение обработки персональных данных.

+
+
+
+
+ Веб-сайт +

Совокупность графических и информационных материалов по адресу http://tailandpuws.ru/.

+
+
+
+
+ Персональные данные +

Любая информация, относящаяся прямо или косвенно к Пользователю.

+
+
+
+
+ + {{-- 3. Права и обязанности Оператора --}} +
+

3. Права и обязанности Оператора

+
+
+
+
Оператор имеет право:
+
    +
  • Получать достоверные информацию и/или документы, содержащие персональные данные
  • +
  • Продолжить обработку персональных данных без согласия субъекта при наличии оснований
  • +
  • Самостоятельно определять состав мер для обеспечения безопасности персональных данных
  • +
+
+
+
+
+
Оператор обязан:
+
    +
  • Предоставлять информацию о обработке персональных данных по запросу
  • +
  • Организовывать обработку в соответствии с законодательством РФ
  • +
  • Публиковать Политику в свободном доступе
  • +
  • Принимать меры для защиты персональных данных
  • +
+
+
+
+
+ + {{-- 4. Права субъектов персональных данных --}} +
+

4. Права субъектов персональных данных

+
+

Вы имеете право:

+
    +
  • Получать информацию, касающуюся обработки ваших персональных данных
  • +
  • Требовать уточнения, блокирования или уничтожения ваших персональных данных
  • +
  • Отозвать согласие на обработку персональных данных
  • +
  • Обжаловать неправомерные действия Оператора в уполномоченный орган или суд
  • +
+
+
+ + {{-- 5. Принципы обработки --}} +
+

5. Принципы обработки персональных данных

+
+
+
+ +

Законность и справедливость

+
+
+
+
+ +

Ограничение целями обработки

+
+
+
+
+ +

Достоверность и достаточность

+
+
+
+
+ + {{-- 6. Цели обработки --}} +
+

6. Цели обработки персональных данных

+ + + + + + + + + + + + + + + +
Цель обработкиПерсональные данныеПравовые основания
Предоставление доступа к сервисам, информации и материалам сайта +
    +
  • Фамилия, имя, отчество
  • +
  • Электронный адрес
  • +
  • Номера телефонов
  • +
+
Федеральный закон № 149-ФЗ «Об информации, информационных технологиях и о защите информации»
+
+ + {{-- 7. Условия обработки --}} +
+

7. Условия обработки персональных данных

+
+ + Обработка персональных данных осуществляется с согласия субъекта персональных данных. +
+
+ + {{-- 8. Порядок сбора, хранения, передачи --}} +
+

8. Порядок сбора, хранения, передачи

+

Оператор обеспечивает сохранность персональных данных и принимает все возможные меры, исключающие доступ к персональным данным неуполномоченных лиц.

+ +
+ + Важно: Персональные данные Пользователя никогда, ни при каких условиях не будут переданы третьим лицам, за исключением случаев, предусмотренных законодательством. +
+ +

Для актуализации или отзыва согласия на обработку персональных данных направьте уведомление на электронную почту: tailandpaws_info@gmail.com.

+
+ + {{-- 9. Конфиденциальность --}} +
+

9. Конфиденциальность персональных данных

+

Оператор и иные лица, получившие доступ к персональным данным, обязаны не раскрывать третьим лицам и не распространять персональные данные без согласия субъекта персональных данных, если иное не предусмотрено федеральным законом.

+
+ + {{-- 10. Контактная информация --}} +
+

10. Контактная информация

+
+

По всем вопросам, касающимся обработки персональных данных, вы можете обратиться:

+
    +
  • По электронной почте: tailandpaws_info@gmail.com
  • +
  • По телефону: {{ $contacts->phone ?? '+7(985)-070-56-33' }}
  • +
  • По адресу: {{ $contacts->address ?? 'г. Челябинск, ул. Дружбы, д. 15' }}
  • +
+
+
+ +
+ +
+

Данная Политика действует бессрочно до замены ее новой версией.

+

Актуальная версия Политики в свободном доступе расположена по адресу: {{ route('privacy-policy') }}

+
+
+
+
+@endsection \ No newline at end of file diff --git a/resources/views/products/partials/product-card.blade.php b/resources/views/products/partials/product-card.blade.php new file mode 100644 index 0000000..95da55d --- /dev/null +++ b/resources/views/products/partials/product-card.blade.php @@ -0,0 +1,35 @@ +@php +$defaultVariation = $product->default_variation; +$price = $defaultVariation ? $defaultVariation->price : $product->min_price; +$hasStock = $defaultVariation ? $defaultVariation->stock > 0 : false; +$rating = $product->rating; +@endphp + + \ No newline at end of file diff --git a/resources/views/products/show.blade.php b/resources/views/products/show.blade.php new file mode 100644 index 0000000..b9c27a0 --- /dev/null +++ b/resources/views/products/show.blade.php @@ -0,0 +1,461 @@ +@extends('layouts.app') + +@section('title', $product->name) + +@php $style = 'product'; @endphp + +@section('content') +
+ + +
+
+ +
+ +
+
+
+ @if($defaultVariation && $defaultVariation->sku) + Артикул: {{ $defaultVariation->sku }} + @endif + @if($product->brand) + + {{ $product->brand->name }} + + @endif +
+ +

{{ $product->name }}

+ +
+
+ rating + {{ $product->rating['score'] ?? '4.9' }} +
+ + {{ $product->rating['count'] ?? '0' }} отзывов + +
+ + @php + $defaultVariation = $product->default_variation; + $price = $defaultVariation ? $defaultVariation->price : $product->min_price; + $oldPrice = $defaultVariation ? $defaultVariation->old_price : null; + $hasStock = $defaultVariation ? $defaultVariation->stock > 0 : false; + @endphp + +
+ @if($oldPrice && $oldPrice > $price) + + {{ number_format($oldPrice, 0, '.', ' ') }} ₽ + + + {{ number_format($price, 0, '.', ' ') }} ₽ + + -{{ $defaultVariation->discount_percent }}% + @else + + {{ number_format($price, 0, '.', ' ') }} ₽ + + @endif +
+ +
+ @if($hasStock) + + В наличии + + @if($defaultVariation && $defaultVariation->stock < 10) + (осталось {{ $defaultVariation->stock }} шт.) + @endif + @else + + Нет в наличии + + @endif +
+ + @if($product->variations->count() > 1) +
+ +
+ @foreach($product->variations as $variation) + @php + $imageUrls = $variation->images->map(function($image) { + return $image->url; + })->values()->toArray(); + @endphp + + @endforeach +
+
+ @endif + +
+
+ @csrf + +
+ + + +
+ +
+
+ +
+
+
+
+ + Экспресс + Платно, за 1 час +
+
+
+
+ + Доставка + Бесплатно +
+
+
+
+ + Самовывоз + Бесплатно +
+
+
+
+
+
+
+ + {{-- Описание и характеристики товара --}} +
+
+ @php + $hasAttributes = $product->attributes->count() > 0; + $hasDescription = !empty($product->description); + @endphp + + @if($hasDescription || $hasAttributes) + +
+ @if($hasDescription) +
+

{{ $product->description }}

+
+ @endif + + @if($hasAttributes) +
+ + @foreach($product->attributes as $attribute) + + + + + @endforeach +
{{ $attribute->key }}{{ $attribute->value }}
+
+ @endif + +
+

Отзывы будут здесь

+
+
+ @else +
+

Отзывы ({{ $product->rating['count'] ?? '0' }})

+

Отзывы будут здесь

+
+ @endif +
+
+
+@endsection + +@section('script') + +@endsection \ No newline at end of file diff --git a/resources/views/profile/edit.blade.php b/resources/views/profile/edit.blade.php new file mode 100644 index 0000000..57d9bfe --- /dev/null +++ b/resources/views/profile/edit.blade.php @@ -0,0 +1,86 @@ +@extends('layouts.app') + +@section('title', 'Настройки профиля') + +@php $style = 'profile'; @endphp + +@section('content') +
+ + @include('profile.partials.profile-sidebar', compact('user')) + +
+
+
+

Настройки профиля

+ + @if(session('success')) +
+ {{ session('success') }} + +
+ @endif + +
+ @csrf + @method('PUT') + +
+ + + @error('name') +
{{ $message }}
+ @enderror +
+ +
+ + + @error('email') +
{{ $message }}
+ @enderror +
+ +
+ + + @error('phone') +
{{ $message }}
+ @enderror +
+ +
+ +
Смена пароля
+ +
+ + + @error('current_password') +
{{ $message }}
+ @enderror +
+ +
+ + + @error('password') +
{{ $message }}
+ @enderror +
+ +
+ + +
+ +
+ +
+
+
+
+
+
+ +@endsection \ No newline at end of file diff --git a/resources/views/profile/order-show.blade.php b/resources/views/profile/order-show.blade.php new file mode 100644 index 0000000..8ef3a22 --- /dev/null +++ b/resources/views/profile/order-show.blade.php @@ -0,0 +1,97 @@ +@extends('layouts.app') + +@section('title', 'Заказ ' . $order->order_number) + +@php $style = 'profile'; @endphp + +@section('content') +
+ @include('profile.partials.profile-sidebar', compact('user')) +
+
+
+
+

Заказ #{{ $order->order_number }}

+ + ← Назад + +
+ +
+
+

Дата заказа: {{ $order->created_at->format('d.m.Y H:i') }}

+

Способ доставки: + @switch($order->delivery_method) + @case('courier') Курьером @break + @case('pickup') Самовывоз @break + @case('express') Экспресс @break + @endswitch +

+

Статус доставки: + + {{ $order->delivery_status_name }} + +

+
+
+

Способ оплаты: + @switch($order->payment_method) + @case('cash') Наличными при получении @break + @case('card') Картой при получении @break + @case('online') Онлайн-оплата @break + @endswitch +

+

Статус оплаты: + + {{ $order->payment_status_name }} + +

+ @if($order->shipping_address) +

Адрес доставки: {{ $order->shipping_address }}

+ @endif +
+
+ +
+ + + + + + + + + + + + @foreach($order->items as $item) + + + + + + + + @endforeach + + + + + + + +
ТоварВариацияКол-воЦенаСумма
{{ $item->product_name }}{{ $item->variation_name ?? '—' }}{{ $item->quantity }}{{ number_format($item->price, 0, '.', ' ') }} ₽{{ number_format($item->total, 0, '.', ' ') }} ₽
Итого:{{ number_format($order->total, 0, '.', ' ') }} ₽
+
+ + @if($order->comment) +
+

Комментарий к заказу:

+

{{ $order->comment }}

+
+ @endif +
+
+
+
+ +@endsection \ No newline at end of file diff --git a/resources/views/profile/partials/profile-sidebar.blade.php b/resources/views/profile/partials/profile-sidebar.blade.php new file mode 100644 index 0000000..26dc69e --- /dev/null +++ b/resources/views/profile/partials/profile-sidebar.blade.php @@ -0,0 +1,28 @@ +
+
+
+
+
+ +
+
{{ $user->name }}
+

{{ $user->email }}

+
+ +
+
+
\ No newline at end of file diff --git a/resources/views/profile/profile.blade.php b/resources/views/profile/profile.blade.php new file mode 100644 index 0000000..16d7411 --- /dev/null +++ b/resources/views/profile/profile.blade.php @@ -0,0 +1,101 @@ +@extends('layouts.app') + +@section('title', 'Личный кабинет') + +@php $style = 'profile'; @endphp + +@section('content') +
+ + @include('profile.partials.profile-sidebar', compact('user')) + +
+
+
+

Мои заказы

+ + @if($activeOrders->count()) +
Активные заказы
+
+ + + + + + + + + + + + @foreach($activeOrders as $order) + + + + + + + + @endforeach + +
№ заказаДатаСуммаСтатус
{{ $order->order_number }}{{ $order->created_at->format('d.m.Y H:i') }}{{ number_format($order->total, 0, '.', ' ') }} ₽ + + {{ $order->delivery_status_name }} + + + + Детали + +
+
+ @endif + + @if($archiveOrders->count()) +
Архив заказов
+
+ + + + + + + + + + + + @foreach($archiveOrders as $order) + + + + + + + + @endforeach + +
№ заказаДатаСуммаСтатус
{{ $order->order_number }}{{ $order->created_at->format('d.m.Y H:i') }}{{ number_format($order->total, 0, '.', ' ') }} ₽ + + {{ $order->delivery_status_name }} + + + + Детали + +
+
+ @endif + + @if($activeOrders->isEmpty() && $archiveOrders->isEmpty()) +
+ +

У вас пока нет заказов

+ Перейти к покупкам +
+ @endif +
+
+
+
+
+@endsection \ No newline at end of file diff --git a/resources/views/search/results.blade.php b/resources/views/search/results.blade.php new file mode 100644 index 0000000..05651e9 --- /dev/null +++ b/resources/views/search/results.blade.php @@ -0,0 +1,81 @@ +@extends('layouts.app') + +@section('title', 'Поиск: ' . $query) + +@section('content') +
+ {{-- Хлебные крошки --}} + + +

Результаты поиска: "{{ $query }}"

+ + {{-- Бренды --}} + @if($brands->count()) +
+
+

Бренды

+
+
+
+ @foreach($brands as $brand) + + @endforeach +
+
+
+ @endif + + {{-- Товары --}} + @if($products->count()) +
+
+

Товары ({{ $products->total() }})

+
+
+
+ @foreach($products as $product) +
+ @include('products.partials.product-card', ['product' => $product]) +
+ @endforeach +
+ +
+ {{ $products->withQueryString()->links() }} +
+
+
+ @endif + + {{-- Ничего не найдено --}} + @if($products->isEmpty() && $brands->isEmpty()) +
+ +

Ничего не найдено

+

Попробуйте изменить поисковый запрос или проверьте орфографию

+ Вернуться на главную +
+ @endif +
+@endsection \ No newline at end of file diff --git a/routes/api.php b/routes/api.php new file mode 100644 index 0000000..889937e --- /dev/null +++ b/routes/api.php @@ -0,0 +1,19 @@ +get('/user', function (Request $request) { + return $request->user(); +}); diff --git a/routes/channels.php b/routes/channels.php new file mode 100644 index 0000000..5d451e1 --- /dev/null +++ b/routes/channels.php @@ -0,0 +1,18 @@ +id === (int) $id; +}); diff --git a/routes/console.php b/routes/console.php new file mode 100644 index 0000000..e05f4c9 --- /dev/null +++ b/routes/console.php @@ -0,0 +1,19 @@ +comment(Inspiring::quote()); +})->purpose('Display an inspiring quote'); diff --git a/routes/web.php b/routes/web.php new file mode 100644 index 0000000..b4318cb --- /dev/null +++ b/routes/web.php @@ -0,0 +1,193 @@ +name('index'); + +//Макет +Route::get('/maket', function () { + return view('maket'); +})->name('maket'); + +// Страница "О нас" +Route::get('/about', [HomeController::class, 'about'])->name('about'); + +// Страница "Контакты" +Route::get('/contacts', [HomeController::class, 'contacts'])->name('contacts'); + +// Поиск +Route::get('/search', [MenuController::class, 'search'])->name('search'); +Route::get('/search/ajax', [MenuController::class, 'searchAjax'])->name('search.ajax'); + +// Аутентификация +Route::get('/login', [LoginController::class, 'showLoginForm'])->name('login'); +Route::post('/login', [LoginController::class, 'login']); +Route::get('/register', [RegisterController::class, 'showRegisterForm'])->name('register'); +Route::post('/register', [RegisterController::class, 'register']); +Route::post('/logout', [LoginController::class, 'logout'])->name('logout'); + +//Политика конфиденциальности +Route::get('/privacy-policy', [HomeController::class, 'privacyPolicy'])->name('privacy-policy'); +// Категории +Route::get('/catalog/{path}', [CategoryController::class, 'showByPath']) + ->where('path', '.*') + ->name('category.show'); + +// Продукты +Route::get('/product/{slug}', [ProductsController::class, 'show'])->name('product.show'); +Route::get('/api/variation/{variationId}/images', [ProductsController::class, 'getVariationImages']); + +// Бренды (с префиксом /brands) +Route::prefix('brands')->name('brands.')->group(function () { + Route::get('/', [BrandController::class, 'index'])->name('index'); // brands.index + Route::get('/{slug}', [BrandController::class, 'show'])->name('show'); // brands.show +}); + +Route::middleware(['auth', 'permission: checkout'])->prefix('orders')->name('orders.')->group(function () { + Route::get('/', [OrderController::class, 'index'])->name('index'); + Route::get('/{order}', [OrderController::class, 'show'])->name('show'); +}); + +Route::middleware(['auth', 'permission:checkout']) + ->prefix('checkout') + ->name('checkout.') + ->group(function () { + Route::get('/', [CartController::class, 'checkout'])->name('index'); + Route::post('/process', [CartController::class, 'processOrder'])->name('process'); + }); + +Route::middleware(['auth', 'permission:manage_cart']) + ->prefix('cart') + ->name('cart.') + ->group(function () { + Route::get('/', [CartController::class, 'index'])->name('index'); + Route::post('/add', [CartController::class, 'add'])->name('add'); + Route::put('/update/{id}', [CartController::class, 'update'])->name('update'); + Route::delete('/remove/{id}', [CartController::class, 'remove'])->name('remove'); + Route::delete('/clear', [CartController::class, 'clear'])->name('clear'); + }); + + +// Админка +Route::middleware(['auth', 'permission:admin_access'])->prefix('admin')->name('admin.')->group(function () { + + // Дашборд + Route::get('/dashboard', [DashboardController::class, 'index'])->name('dashboard'); + + // ===== БРЕНДЫ (требуют manage_brands) ===== + Route::middleware(['permission:manage_brands'])->group(function () { + Route::get('/brands', [BrandsController::class, 'index'])->name('brands'); + Route::post('/brands', [BrandsController::class, 'store'])->name('brands.store'); + Route::get('/brands/{id}/edit', [BrandsController::class, 'edit'])->name('brands.edit'); + Route::put('/brands/{id}', [BrandsController::class, 'update'])->name('brands.update'); + Route::delete('/brands/{id}', [BrandsController::class, 'destroy'])->name('brands.destroy'); + }); + + // ===== КАТЕГОРИИ (требуют manage_categories) ===== + Route::middleware(['permission:manage_categories'])->group(function () { + Route::get('/categories', [CategoriesController::class, 'index'])->name('categories'); + Route::post('/categories', [CategoriesController::class, 'store'])->name('categories.store'); + Route::get('/categories/{id}/edit', [CategoriesController::class, 'edit'])->name('categories.edit'); + Route::put('/categories/{id}', [CategoriesController::class, 'update'])->name('categories.update'); + Route::delete('/categories/{id}', [CategoriesController::class, 'destroy'])->name('categories.destroy'); + Route::patch('/categories/{id}/move-up', [CategoriesController::class, 'moveUp'])->name('categories.move-up'); + Route::patch('/categories/{id}/move-down', [CategoriesController::class, 'moveDown'])->name('categories.move-down'); + }); + + // ===== ПРОДУКТЫ (требуют manage_products) ===== + Route::middleware(['permission:manage_products'])->group(function () { + Route::get('/products', [ProductController::class, 'index'])->name('products'); + Route::get('/products/create', [ProductController::class, 'create'])->name('products.create'); + Route::post('/products/create', [ProductController::class, 'store'])->name('products.store'); + Route::get('/products/{id}/edit', [ProductController::class, 'edit'])->name('products.edit'); + Route::put('/products/{id}', [ProductController::class, 'update'])->name('products.update'); + Route::delete('/products/{id}', [ProductController::class, 'destroy'])->name('products.destroy'); + Route::post('/products/{id}/duplicate', [ProductController::class, 'duplicate'])->name('products.duplicate'); + Route::post('/products/check-sku', [ProductController::class, 'checkSku'])->name('products.check-sku'); + }); + + // ===== ЗАКАЗЫ В АДМИНКЕ (требуют view_orders и edit_orders) ===== + Route::get('/orders', [OrdersController::class, 'index']) + ->middleware('permission:view_orders') + ->name('orders'); + + Route::get('/orders/{order}', [OrdersController::class, 'show']) + ->middleware('permission:view_orders') + ->name('orders.show'); + + Route::put('/orders/{order}/status', [OrdersController::class, 'updateStatus']) + ->middleware('permission:edit_orders') + ->name('orders.update-status'); + + // ===== РОЛИ (требуют manage_roles) ===== + Route::middleware(['permission:manage_roles'])->group(function () { + Route::get('/roles', [RolesController::class, 'index'])->name('roles'); + Route::get('/roles/create', [RolesController::class, 'create'])->name('roles.create'); + Route::post('/roles', [RolesController::class, 'store'])->name('roles.store'); + Route::get('/roles/{id}/edit', [RolesController::class, 'edit'])->name('roles.edit'); + Route::put('/roles/{id}', [RolesController::class, 'update'])->name('roles.update'); + Route::delete('/roles/{id}', [RolesController::class, 'destroy'])->name('roles.destroy'); + }); + + // ===== ПРАВА ДОСТУПА (требуют manage_permissions) ===== + Route::middleware(['permission:manage_permissions'])->group(function () { + Route::get('/permissions', [PermissionsController::class, 'index'])->name('permissions'); + Route::post('/permissions', [PermissionsController::class, 'store'])->name('permissions.store'); + Route::get('/permissions/{id}/edit', [PermissionsController::class, 'edit'])->name('permissions.edit'); + Route::put('/permissions/{id}', [PermissionsController::class, 'update'])->name('permissions.update'); + Route::delete('/permissions/{id}', [PermissionsController::class, 'destroy'])->name('permissions.destroy'); + }); + + // ===== ПОЛЬЗОВАТЕЛИ (требуют manage_users) ===== + Route::middleware(['permission:manage_users'])->group(function () { + Route::get('/users', [UsersController::class, 'index'])->name('users'); + Route::get('/users/create', [UsersController::class, 'create'])->name('users.create'); + Route::post('/users', [UsersController::class, 'store'])->name('users.store'); + Route::get('/users/{id}/edit', [UsersController::class, 'edit'])->name('users.edit'); + Route::put('/users/{id}', [UsersController::class, 'update'])->name('users.update'); + Route::delete('/users/{id}', [UsersController::class, 'destroy'])->name('users.destroy'); + }); + + // ===== КОНТАКТЫ / НАСТРОЙКИ (требуют edit_shop_settings) ===== + Route::middleware(['permission:edit_shop_settings'])->group(function () { + Route::get('/contacts', [ContactController::class, 'edit'])->name('contacts.edit'); + Route::put('/contacts', [ContactController::class, 'update'])->name('contacts.update'); + }); +}); + +// Личный кабинет +Route::middleware(['auth', 'permission:cabinet_access']) + ->prefix('profile') + ->name('profile.') + ->group(function () { + Route::get('/', [ProfileController::class, 'index'])->name('index'); + Route::get('/edit', [ProfileController::class, 'edit'])->name('edit'); + Route::put('/update', [ProfileController::class, 'update'])->name('update'); + Route::get('/orders/{id}', [ProfileController::class, 'orderShow'])->name('order.show'); + }); diff --git a/storage/app/.gitignore b/storage/app/.gitignore new file mode 100644 index 0000000..8f4803c --- /dev/null +++ b/storage/app/.gitignore @@ -0,0 +1,3 @@ +* +!public/ +!.gitignore diff --git a/storage/app/public/.gitignore b/storage/app/public/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/storage/app/public/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/storage/framework/.gitignore b/storage/framework/.gitignore new file mode 100644 index 0000000..05c4471 --- /dev/null +++ b/storage/framework/.gitignore @@ -0,0 +1,9 @@ +compiled.php +config.php +down +events.scanned.php +maintenance.php +routes.php +routes.scanned.php +schedule-* +services.json diff --git a/storage/framework/cache/.gitignore b/storage/framework/cache/.gitignore new file mode 100644 index 0000000..01e4a6c --- /dev/null +++ b/storage/framework/cache/.gitignore @@ -0,0 +1,3 @@ +* +!data/ +!.gitignore diff --git a/storage/framework/cache/data/.gitignore b/storage/framework/cache/data/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/storage/framework/cache/data/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/storage/framework/sessions/.gitignore b/storage/framework/sessions/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/storage/framework/sessions/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/storage/framework/testing/.gitignore b/storage/framework/testing/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/storage/framework/testing/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/storage/framework/views/.gitignore b/storage/framework/views/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/storage/framework/views/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/storage/logs/.gitignore b/storage/logs/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/storage/logs/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/tailwind.config.js b/tailwind.config.js new file mode 100644 index 0000000..602bf6b --- /dev/null +++ b/tailwind.config.js @@ -0,0 +1,17 @@ +/** @type {import('tailwindcss').Config} */ +export default { + prefix: 'tw-', + content: ["./resources/**/*.blade.php"], + theme: { + extend: { + colors: { + 'dark-green': '#323329', + 'dark-green-hover': '#2a2a22', + 'light-gray': '#E6E2DF', + 'orange': '#C47C4C', + 'dark-orange': '#b36b3c' + }, + }, + }, + plugins: [], +} \ No newline at end of file diff --git a/tests/CreatesApplication.php b/tests/CreatesApplication.php new file mode 100644 index 0000000..cc68301 --- /dev/null +++ b/tests/CreatesApplication.php @@ -0,0 +1,21 @@ +make(Kernel::class)->bootstrap(); + + return $app; + } +} diff --git a/tests/Feature/ExampleTest.php b/tests/Feature/ExampleTest.php new file mode 100644 index 0000000..8364a84 --- /dev/null +++ b/tests/Feature/ExampleTest.php @@ -0,0 +1,19 @@ +get('/'); + + $response->assertStatus(200); + } +} diff --git a/tests/TestCase.php b/tests/TestCase.php new file mode 100644 index 0000000..2932d4a --- /dev/null +++ b/tests/TestCase.php @@ -0,0 +1,10 @@ +assertTrue(true); + } +} diff --git a/vite.config.js b/vite.config.js new file mode 100644 index 0000000..8eedfb5 --- /dev/null +++ b/vite.config.js @@ -0,0 +1,22 @@ +import { defineConfig } from 'vite'; +import laravel from 'laravel-vite-plugin'; +import path from 'path'; + +export default defineConfig({ + plugins: [ + laravel({ + input: ['resources/css/app.css', 'resources/js/app.js'], + refresh: true, + }), + ], + server: { + watch: { + usePolling: true, + }, + }, + resolve: { + alias: { + '~bootstrap': path.resolve(__dirname, 'node_modules/bootstrap'), + }, + }, +});