commit 7c111dd711361a484bed87d67b47039353422fd9 Author: Degradin Date: Tue Apr 15 10:00:00 2025 +0300 Initial commit diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..dfe0770 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3d38482 --- /dev/null +++ b/.gitignore @@ -0,0 +1,49 @@ +# Environment variables +.env +.env.local +.env.*.local + +# Dependencies +/node_modules +/.pnp +.pnp.js + +# Production +/build +/dist + +# Misc +.DS_Store +*.pem +.vscode +.idea + +# Debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Local env files +.env*.local + +# Logs +logs +*.log +pnpm-debug.log* +lerna-debug.log* + +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? +/supabase/.temp diff --git a/README.md b/README.md new file mode 100644 index 0000000..0f12014 --- /dev/null +++ b/README.md @@ -0,0 +1,187 @@ +# CampFire Critics + +CampFire Critics — это веб-приложение, созданное с использованием React, Vite и Tailwind CSS, предназначенное для каталогизации медиаконтента (фильмов, сериалов, игр, аниме) и управления пользовательскими отзывами. Приложение включает в себя систему аутентификации, подробные страницы медиа, профили пользователей, систему достижений, систему поддержки и административную панель для модерации контента и управления данными. В качестве бэкенда используется PocketBase. + +## Функции + +* **Аутентификация пользователей**: Полная система регистрации, входа, выхода и сброса пароля с использованием PocketBase Auth. +* **Профили пользователей**: Просмотр профилей пользователей, отображение их статистики (количество рецензий, средний рейтинг, XP, уровень), списка достижений и витрины избранных рецензий. Возможность редактирования собственного профиля (описание, аватар, баннер, витрина). +* **Каталог медиа**: Просмотр списка медиа с фильтрацией по типу (фильмы, сериалы, игры, аниме) и поиском. +* **Страницы обзора медиа**: Подробные страницы для каждого медиа, включающие информацию о нем, список сезонов (для сериалов/аниме), а также раздел с пользовательскими рецензиями и рейтингами. +* **Система рецензий**: Пользователи могут оставлять рецензии на медиа или отдельные сезоны, выставлять оценки по нескольким характеристикам, указывать прогресс просмотра/прохождения и отмечать спойлеры. Рецензии отображаются на страницах медиа/сезонов и в профилях пользователей. +* **Система лайков рецензий**: Пользователи могут ставить лайки рецензиям других пользователей. +* **Система XP и уровней**: Пользователи получают XP за создание рецензий и получение достижений, что повышает их уровень. Уровень и XP отображаются в профиле. +* **Система достижений**: Пользователи могут получать достижения за различные действия. Список достижений отображается в профиле. +* **Система поддержки**: Пользователи могут создавать тикеты поддержки с выбором категории и описанием проблемы. +* **Административная панель**: Раздел для администраторов с возможностью управления медиа, пользователями, сезонами, достижениями и тикетами поддержки. Включает дашборд с общей статистикой. +* **Адаптивный дизайн**: Приложение адаптировано для корректного отображения на различных устройствах с использованием Tailwind CSS. + +## Технологии + +* **Фронтенд**: + * React: Библиотека для построения пользовательских интерфейсов. + * Vite: Быстрый сборщик фронтенда. + * Tailwind CSS: Утилитарный CSS-фреймворк для быстрой стилизации. + * React Router DOM: Для маршрутизации в приложении. + * `react-icons`: Набор популярных иконок. + * `chart.js` и `react-chartjs-2`: Для построения графиков рейтингов. + * `react-datepicker`: Для выбора дат. + * `react-fast-marquee`: Для бегущей строки (если используется). + * `react-quill`: WYSIWYG редактор для контента рецензий/описаний. + * `react-select`: Кастомизируемый компонент выбора. + * `react-toastify`: Для уведомлений (тостов). + * `dompurify`: Для очистки HTML контента. + * `uuid`: Для генерации уникальных ID. +* **Бэкенд**: + * PocketBase: Опенсорсный бэкенд в одном файле (Go) с базой данных SQLite, системой аутентификации, файловым хранилищем, realtime подписками и хуками. +* **Внешние API**: + * TMDB (The Movie Database): Используется для получения информации о фильмах, сериалах и, возможно, другом медиаконтенте (подразумевается, исходя из предыдущего README). + +## Настройка проекта + +1. **Клонирование репозитория** (стандартный шаг, не применимо в WebContainer): + + ```bash + git clone https://github.com/your-username/campfire-critics.git + cd campfire-critics + ``` + +2. **Установка зависимостей**: + + ```bash + npm install + ``` + +3. **Настройка PocketBase**: + * Скачайте и запустите PocketBase с [официального сайта](https://pocketbase.io/docs/getting-started/). + * Перейдите в Admin UI (обычно `http://127.0.0.1:8090/_/`). + * Создайте следующие коллекции (убедитесь, что включены необходимые поля и связи): + * `users` (тип `auth`): Стандартная коллекция пользователей PocketBase. Добавьте поля: `role` (select, options: `user`, `admin`), `is_critic` (bool), `description` (text), `profile_picture` (file), `banner_picture` (file), `review_count` (number, default 0), `average_rating` (number, default 0), `xp` (number, default 0), `level` (number, default 1), `showcase` (relation to `reviews`, multiple). + * `media`: Поля: `title` (text), `type` (select, options: `movie`, `tv`, `game`, `anime`), `path` (text, unique), `overview` (text), `release_date` (date), `poster` (file), `backdrop` (file), `trailer_url` (url), `characteristics` (json), `average_rating` (number, default 0), `review_count` (number, default 0), `is_published` (bool, default false), `is_popular` (bool, default false), `progress_type` (select, options: `percentage`, `episodes`, `chapters`, `none`), `created_by` (relation to `users`), `created` (datetime), `updated` (datetime). + * `seasons`: Поля: `media_id` (relation to `media`, required), `season_number` (number, required), `title` (text), `overview` (text), `release_date` (date), `poster` (file), `average_rating` (number, default 0), `review_count` (number, default 0), `is_published` (bool, default false), `created_by` (relation to `users`), `created` (datetime), `updated` (datetime). + * `reviews`: Поля: `user_id` (relation to `users`, required), `media_id` (relation to `media`, required), `season_id` (relation to `seasons`, optional), `content` (text), `ratings` (json), `overall_rating` (number, default 0), `has_spoilers` (bool, default false), `progress` (number), `created` (datetime), `updated` (datetime). + * `achievements`: Поля: `name` (text), `description` (text), `icon` (file), `xp_reward` (number, default 0), `created` (datetime), `updated` (datetime). + * `user_achievements`: Поля: `user_id` (relation to `users`, required), `achievement_id` (relation to `achievements`, required), `awarded_at` (datetime), `awarded_by` (relation to `users`, optional), `created` (datetime), `updated` (datetime). + * `review_likes`: Поля: `review_id` (relation to `reviews`, required), `user_id` (relation to `users`, required), `created` (datetime). + * `support_tickets`: Поля: `user_id` (relation to `users`, required), `category` (select, options: `bug`, `feature_request`, `question`, `other`), `subject` (text), `message` (text), `status` (select, options: `open`, `in_progress`, `closed`, default `open`), `admin_notes` (text), `created` (datetime), `updated` (datetime). + * **Настройте RLS (Row Level Security)** для каждой коллекции, чтобы определить, кто может просматривать, создавать, обновлять и удалять записи. + * **Настройте Hooks/Triggers** в PocketBase для автоматического обновления полей `average_rating` и `review_count` в коллекциях `media`, `seasons` и `users` при создании, обновлении или удалении записей в коллекциях `reviews` и `review_likes`. Также настройте хуки для обновления `xp` и `level` пользователя при создании рецензии или получении достижения. *Это критически важный шаг для корректной работы статистики.* + +4. **Настройте переменные окружения**: + + * Создайте файл `.env` в корне проекта, если его нет. + * Добавьте URL вашего PocketBase инстанса: + ``` + VITE_POCKETBASE_URL=http://127.0.0.1:8090 + ``` + Замените URL на адрес вашего запущенного PocketBase. + +5. **Запустите сервер разработки**: + + ```bash + npm run dev + ``` + +Приложение будет доступно по адресу, указанному Vite (обычно `http://localhost:5173`). + +## Структура проекта и описание модулей + +Проект организован следующим образом: + +``` +src/ +├── App.jsx # Основной компонент приложения, определяет маршруты. +├── main.jsx # Точка входа в приложение, инициализирует React и оборачивает App в контекст-провайдеры и роутер. +├── index.css # Основной файл стилей, импортирует Tailwind CSS. +├── pages/ # Компоненты, представляющие целые страницы приложения. +│ ├── HomePage.jsx # Главная страница с обзорами и статистикой. +│ ├── CatalogPage.jsx # Страница каталога медиа. +│ ├── MediaOverviewPage.jsx# Страница с подробным обзором конкретного медиа. +│ ├── ProfilePage.jsx # Страница профиля пользователя. +│ ├── RatingPage.jsx # Страница с рейтингами пользователей (по рецензиям, по уровню). +│ ├── SupportPage.jsx # Страница для создания тикетов поддержки. +│ ├── NotFoundPage.jsx # Страница 404. +│ ├── LoginPage.jsx # Страница входа. +│ ├── RegisterPage.jsx # Страница регистрации. +│ ├── ForgotPasswordPage.jsx# Страница сброса пароля. +│ └── admin/ # Страницы административной панели. +│ ├── AdminDashboard.jsx +│ ├── AdminMediaPage.jsx +│ ├── AdminUsersPage.jsx +│ ├── AdminSeasonsPage.jsx +│ ├── AdminAchievementsPage.jsx +│ ├── AdminTicketsPage.jsx +│ └── AdminTicketDetailPage.jsx +├── components/ # Переиспользуемые UI-компоненты. +│ ├── auth/ # Компоненты и логика, связанные с аутентификацией (AuthRoute, GuestRoute). +│ ├── layout/ # Компоненты макета (Header, Footer, Layout). +│ ├── admin/ # Компоненты для административной панели (AdminLayout). +│ ├── MediaCard.jsx # Карточка медиа для каталога/списков. +│ ├── ReviewCard.jsx # Карточка рецензии. +│ ├── RatingChart.jsx # Компонент графика рейтинга. +│ ├── SearchBar.jsx # Компонент поиска. +│ ├── UserRankCard.jsx # Карточка пользователя для рейтингов. +│ ├── AchievementCard.jsx # Карточка достижения. +│ ├── SupportTicketCard.jsx# Карточка тикета поддержки. +│ └── ... другие компоненты +├── contexts/ # React Context провайдеры для глобального состояния. +│ ├── AuthContext.jsx # Управляет состоянием аутентификации пользователя. +│ └── ProfileActionsContext.jsx # Управляет действиями, связанными с профилем (например, витрина). +├── services/ # Утилиты и сервисы для взаимодействия с API/бэкендом. +│ └── pocketbaseService.js # **Основной модуль для взаимодействия с PocketBase API.** +├── assets/ # Статические файлы (изображения, шрифты и т.д.). +└── ... другие файлы конфигурации (tailwind.config.js, postcss.config.js, vite.config.js) +``` + +### Описание ключевых модулей: + +* **`src/services/pocketbaseService.js`**: + Этот файл является центральным слоем для всех операций взаимодействия с бэкендом PocketBase. Он инициализирует PocketBase SDK и экспортирует функции для: + * **Аутентификации**: `signUp`, `signIn`, `signOut`, `getCurrentUser`, `requestPasswordReset`. + * **Пользователей**: `getUserProfile`, `getUserProfileByUsername`, `updateUserProfile`, `updateUserShowcase`, `listUsersRankedByReviews`, `listUsersRankedByLevel`. + * **Медиа**: `createMedia`, `getMediaByPath`, `getMediaById`, `listMedia`, `listPopularMedia`, `updateMedia`, `deleteMedia`, `searchMedia`. + * **Рецензий**: `getLatestReviews`, `getReviewsByMediaId`, `getReviewsByUserId`, `getUserReviewForMedia`, `createReview`, `updateReview`, `deleteReview`. + * **Сезонов**: `getSeasonsByMediaId`, `getSeasonById`, `createSeason`, `updateSeason`, `deleteSeason`. + * **Достижений**: `getAllAchievements`, `getUserAchievements`, `awardAchievement`. + * **Лайков рецензий**: `likeReview`, `unlikeReview`, `getReviewLikeCount`, `hasUserLikedReview`. + * **Тикетов поддержки**: `createSupportTicket`, `getUserTickets`, `getAllTickets`, `getTicketById`, `updateSupportTicket`. + * **Файлового хранилища**: `uploadFile`, `deleteFile`, `getFileUrl`. + * **Утилит**: `calculateLevel`, `getXpForNextLevel`, `getXpForCurrentLevel`, `validateMediaData`, `formatMediaData`, `updateRatingStats` (функция для пересчета средних рейтингов и количества рецензий, часто вызывается после операций с рецензиями). + Этот модуль абстрагирует логику работы с API, делая компоненты более чистыми. + +* **`src/contexts/AuthContext.jsx`**: + Предоставляет глобальный доступ к состоянию аутентификации текущего пользователя. Использует `pocketbaseService` для выполнения операций входа/выхода/регистрации и хранит информацию о пользователе в состоянии React Context. Компоненты могут использовать хук `useAuth` для доступа к данным пользователя и функциям аутентификации. + +* **`src/contexts/ProfileActionsContext.jsx`**: + Предоставляет контекст для управления действиями, специфичными для страницы профиля, такими как обновление витрины рецензий. + +* **`src/App.jsx`**: + Определяет структуру маршрутизации приложения с использованием `react-router-dom`. Здесь настраиваются публичные маршруты, маршруты только для гостей (`GuestRoute`) и защищенные маршруты (`AuthRoute`), включая маршруты административной панели. + +* **`src/components/auth/AuthRoute.jsx` и `src/components/auth/GuestRoute.jsx`**: + Вспомогательные компоненты маршрутизации, которые используют `AuthContext` для проверки статуса аутентификации пользователя и его роли, перенаправляя пользователя на другие страницы при необходимости (например, перенаправление неаутентифицированных пользователей с защищенных маршрутов или аутентифицированных пользователей со страниц входа/регистрации). + +* **`src/pages/` и `src/components/`**: + Эти директории содержат компоненты пользовательского интерфейса. `pages` содержат компоненты верхнего уровня, представляющие целые страницы, а `components` содержат более мелкие, переиспользуемые блоки UI. Стилизация в основном выполняется с помощью классов Tailwind CSS. + +* **Административная панель (`src/pages/admin/`, `src/components/admin/AdminLayout.jsx`)**: + Набор страниц и компонентов, доступных только пользователям с ролью 'admin'. `AdminLayout` предоставляет общую структуру макета для всех страниц админки (навигация, заголовок). Страницы внутри `pages/admin/` используют функции из `pocketbaseService` для управления данными (создание, чтение, обновление, удаление) медиа, пользователей, сезонов, достижений и тикетов поддержки. + +## Вклад в проект + +1. Откройте issue для обсуждения новой функции или исправления. +2. Сделайте fork репозитория. +3. Создайте новую ветку (`git checkout -b feature-name`). +4. Зафиксируйте изменения. +5. Запушьте ветку (`git push origin feature-name`). +6. Создайте Pull Request. + +## Требования + +* Node.js 18+ +* npm 8+ +* Запущенный инстанс PocketBase с настроенными коллекциями, RLS и хуками. + +## Лицензия + +MIT License. Подробнее см. [LICENSE](LICENSE). diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..8dc4095 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,41 @@ +import js from '@eslint/js' +import globals from 'globals' +import react from 'eslint-plugin-react' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' + +export default [ + { ignores: ['dist'] }, + { + files: ['**/*.{js,jsx,ts,tsx}'], + languageOptions: { + ecmaVersion: 2020, + globals: { + ...globals.browser, + process: 'readonly' + }, + parserOptions: { + ecmaVersion: 'latest', + ecmaFeatures: { jsx: true }, + sourceType: 'module', + }, + }, + settings: { react: { version: '18.3' } }, + plugins: { + react, + 'react-hooks': reactHooks, + 'react-refresh': reactRefresh, + }, + rules: { + ...js.configs.recommended.rules, + ...react.configs.recommended.rules, + ...react.configs['jsx-runtime'].rules, + ...reactHooks.configs.recommended.rules, + 'react/jsx-no-target-blank': 'off', + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + }, + }, +] diff --git a/index.html b/index.html new file mode 100644 index 0000000..8b2c936 --- /dev/null +++ b/index.html @@ -0,0 +1,14 @@ + + + + + + + + CampFire мнеие + + +
+ + + diff --git a/jsrepo.json b/jsrepo.json new file mode 100644 index 0000000..c8f47b9 --- /dev/null +++ b/jsrepo.json @@ -0,0 +1,16 @@ +{ + "$schema": "https://unpkg.com/jsrepo@2.0.3/schemas/project-config.json", + "repos": [ + "https://reactbits.dev/tailwind" + ], + "includeTests": false, + "watermark": true, + "configFiles": {}, + "paths": { + "*": "./src/components/reactbits", + "Animations": "./src/components/reactbits/Animations", + "TextAnimations": "./src/components/reactbits/TextAnimations", + "Backgrounds": "./src/components/reactbits/Backgrounds", + "Components": "./src/components/reactbits/Components" + } +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..19314de --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6590 @@ +{ + "name": "campfirecritics", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "campfirecritics", + "version": "0.0.0", + "dependencies": { + "@tailwindcss/forms": "^0.5.10", + "@tailwindcss/typography": "^0.5.16", + "chart.js": "^4.4.3", + "dompurify": "^3.2.5", + "framer-motion": "^11.18.2", + "gsap": "^3.13.0", + "pocketbase": "^0.21.3", + "react": "^18.2.0", + "react-chartjs-2": "^5.2.0", + "react-datepicker": "^8.3.0", + "react-dom": "^18.2.0", + "react-fast-marquee": "^1.6.4", + "react-hot-toast": "^2.5.2", + "react-icons": "^5.5.0", + "react-quill": "^2.0.0", + "react-router-dom": "^6.23.1", + "react-select": "^5.10.1", + "react-toastify": "^10.0.5", + "uuid": "^11.1.0" + }, + "devDependencies": { + "@types/react": "^18.2.66", + "@types/react-dom": "^18.2.22", + "@vitejs/plugin-react": "^4.2.1", + "autoprefixer": "^10.4.19", + "eslint": "^8.57.0", + "eslint-plugin-react": "^7.34.1", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.4.6", + "postcss": "^8.4.38", + "tailwindcss": "^3.4.3", + "vite": "^5.2.0" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.8.tgz", + "integrity": "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.10.tgz", + "integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.10", + "@babel/helper-compilation-targets": "^7.26.5", + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helpers": "^7.26.10", + "@babel/parser": "^7.26.10", + "@babel/template": "^7.26.9", + "@babel/traverse": "^7.26.10", + "@babel/types": "^7.26.10", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.0.tgz", + "integrity": "sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.27.0", + "@babel/types": "^7.27.0", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.0.tgz", + "integrity": "sha512-LVk7fbXml0H2xH34dFzKQ7TDZ2G4/rVTOrq9V+icbbadjbVxxeFeDsNHv2SrZeWoA+6ZiTyWYWtScEIW07EAcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.26.8", + "@babel/helper-validator-option": "^7.25.9", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", + "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", + "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz", + "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", + "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.0.tgz", + "integrity": "sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.0", + "@babel/types": "^7.27.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz", + "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.25.9.tgz", + "integrity": "sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.25.9.tgz", + "integrity": "sha512-+iqjT8xmXhhYv4/uiYd8FNQsraMFZIfxVSqxxVSZP0WbbSAWvBXAul0m/zu+7Vv4O/3WtApy9pmaTMiumEZgfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.1.tgz", + "integrity": "sha512-1x3D2xEk2fRo3PAhwQwu5UubzgiVWSXTBfWpVd2Mx2AzRqJuDJCsgaDVZ7HB5iGzDW1Hl1sWN2mFyKjmR9uAog==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz", + "integrity": "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "@babel/parser": "^7.27.0", + "@babel/types": "^7.27.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.0.tgz", + "integrity": "sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.27.0", + "@babel/parser": "^7.27.0", + "@babel/template": "^7.27.0", + "@babel/types": "^7.27.0", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/types": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz", + "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@emotion/babel-plugin": { + "version": "11.13.5", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz", + "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/serialize": "^1.3.3", + "babel-plugin-macros": "^3.1.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/babel-plugin/node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "license": "MIT" + }, + "node_modules/@emotion/cache": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz", + "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.9.0", + "@emotion/sheet": "^1.4.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/hash": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", + "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==", + "license": "MIT" + }, + "node_modules/@emotion/memoize": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==", + "license": "MIT" + }, + "node_modules/@emotion/react": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", + "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.13.5", + "@emotion/cache": "^11.14.0", + "@emotion/serialize": "^1.3.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "hoist-non-react-statics": "^3.3.1" + }, + "peerDependencies": { + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/serialize": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz", + "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==", + "license": "MIT", + "dependencies": { + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/unitless": "^0.10.0", + "@emotion/utils": "^1.4.2", + "csstype": "^3.0.2" + } + }, + "node_modules/@emotion/sheet": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", + "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==", + "license": "MIT" + }, + "node_modules/@emotion/unitless": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", + "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==", + "license": "MIT" + }, + "node_modules/@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz", + "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@emotion/utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz", + "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==", + "license": "MIT" + }, + "node_modules/@emotion/weak-memoize": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz", + "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==", + "license": "MIT" + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.6.1.tgz", + "integrity": "sha512-KTsJMmobmbrFLe3LDh0PC2FXpcSYJt/MLjlkh/9LEnmKYLSYmT/0EW9JWANjeoemiuZrmogti0tW5Ch+qNUYDw==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@floating-ui/core": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.0.tgz", + "integrity": "sha512-FRdBLykrPPA6P76GGGqlex/e7fbe0F1ykgxHYNXQsH/iTEtjMj/f9bpY5oQqbjt5VgZvgz/uKXbGuROijh3VLA==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.9" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.0.tgz", + "integrity": "sha512-lGTor4VlXcesUMh1cupTUTDoCxMb0V6bm3CnxHzQcw8Eaf1jQbgQX4i02fYgT0vJ82tb5MZ4CZk1LRGkktJCzg==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.7.0", + "@floating-ui/utils": "^0.2.9" + } + }, + "node_modules/@floating-ui/react": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.27.8.tgz", + "integrity": "sha512-EQJ4Th328y2wyHR3KzOUOoTW2UKjFk53fmyahfwExnFQ8vnsMYqKc+fFPOkeYtj5tcp1DUMiNJ7BFhed7e9ONw==", + "license": "MIT", + "dependencies": { + "@floating-ui/react-dom": "^2.1.2", + "@floating-ui/utils": "^0.2.9", + "tabbable": "^6.0.0" + }, + "peerDependencies": { + "react": ">=17.0.0", + "react-dom": ">=17.0.0" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.2.tgz", + "integrity": "sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.9.tgz", + "integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==", + "license": "MIT" + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@kurkle/color": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz", + "integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==", + "license": "MIT" + }, + "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==", + "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==", + "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==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@remix-run/router": { + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.0.tgz", + "integrity": "sha512-O3rHJzAQKamUz1fvE0Qaw0xSFqsA/yafi2iqeE0pvdFtCO1viYx8QL6f3Ln/aCCTLxs68SLf0KPM9eSeM8yBnA==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.0.tgz", + "integrity": "sha512-+Fbls/diZ0RDerhE8kyC6hjADCXA1K4yVNlH0EYfd2XjyH0UGgzaQ8MlT0pCXAThfxv3QUAczHaL+qSv1E4/Cg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.40.0.tgz", + "integrity": "sha512-PPA6aEEsTPRz+/4xxAmaoWDqh67N7wFbgFUJGMnanCFs0TV99M0M8QhhaSCks+n6EbQoFvLQgYOGXxlMGQe/6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.40.0.tgz", + "integrity": "sha512-GwYOcOakYHdfnjjKwqpTGgn5a6cUX7+Ra2HeNj/GdXvO2VJOOXCiYYlRFU4CubFM67EhbmzLOmACKEfvp3J1kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.40.0.tgz", + "integrity": "sha512-CoLEGJ+2eheqD9KBSxmma6ld01czS52Iw0e2qMZNpPDlf7Z9mj8xmMemxEucinev4LgHalDPczMyxzbq+Q+EtA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.40.0.tgz", + "integrity": "sha512-r7yGiS4HN/kibvESzmrOB/PxKMhPTlz+FcGvoUIKYoTyGd5toHp48g1uZy1o1xQvybwwpqpe010JrcGG2s5nkg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.40.0.tgz", + "integrity": "sha512-mVDxzlf0oLzV3oZOr0SMJ0lSDd3xC4CmnWJ8Val8isp9jRGl5Dq//LLDSPFrasS7pSm6m5xAcKaw3sHXhBjoRw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.40.0.tgz", + "integrity": "sha512-y/qUMOpJxBMy8xCXD++jeu8t7kzjlOCkoxxajL58G62PJGBZVl/Gwpm7JK9+YvlB701rcQTzjUZ1JgUoPTnoQA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.40.0.tgz", + "integrity": "sha512-GoCsPibtVdJFPv/BOIvBKO/XmwZLwaNWdyD8TKlXuqp0veo2sHE+A/vpMQ5iSArRUz/uaoj4h5S6Pn0+PdhRjg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.40.0.tgz", + "integrity": "sha512-L5ZLphTjjAD9leJzSLI7rr8fNqJMlGDKlazW2tX4IUF9P7R5TMQPElpH82Q7eNIDQnQlAyiNVfRPfP2vM5Avvg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.40.0.tgz", + "integrity": "sha512-ATZvCRGCDtv1Y4gpDIXsS+wfFeFuLwVxyUBSLawjgXK2tRE6fnsQEkE4csQQYWlBlsFztRzCnBvWVfcae/1qxQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.40.0.tgz", + "integrity": "sha512-wG9e2XtIhd++QugU5MD9i7OnpaVb08ji3P1y/hNbxrQ3sYEelKJOq1UJ5dXczeo6Hj2rfDEL5GdtkMSVLa/AOg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.40.0.tgz", + "integrity": "sha512-vgXfWmj0f3jAUvC7TZSU/m/cOE558ILWDzS7jBhiCAFpY2WEBn5jqgbqvmzlMjtp8KlLcBlXVD2mkTSEQE6Ixw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.40.0.tgz", + "integrity": "sha512-uJkYTugqtPZBS3Z136arevt/FsKTF/J9dEMTX/cwR7lsAW4bShzI2R0pJVw+hcBTWF4dxVckYh72Hk3/hWNKvA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.40.0.tgz", + "integrity": "sha512-rKmSj6EXQRnhSkE22+WvrqOqRtk733x3p5sWpZilhmjnkHkpeCgWsFFo0dGnUGeA+OZjRl3+VYq+HyCOEuwcxQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.40.0.tgz", + "integrity": "sha512-SpnYlAfKPOoVsQqmTFJ0usx0z84bzGOS9anAC0AZ3rdSo3snecihbhFTlJZ8XMwzqAcodjFU4+/SM311dqE5Sw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.40.0.tgz", + "integrity": "sha512-RcDGMtqF9EFN8i2RYN2W+64CdHruJ5rPqrlYw+cgM3uOVPSsnAQps7cpjXe9be/yDp8UC7VLoCoKC8J3Kn2FkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.40.0.tgz", + "integrity": "sha512-HZvjpiUmSNx5zFgwtQAV1GaGazT2RWvqeDi0hV+AtC8unqqDSsaFjPxfsO6qPtKRRg25SisACWnJ37Yio8ttaw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.40.0.tgz", + "integrity": "sha512-UtZQQI5k/b8d7d3i9AZmA/t+Q4tk3hOC0tMOMSq2GlMYOfxbesxG4mJSeDp0EHs30N9bsfwUvs3zF4v/RzOeTQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.40.0.tgz", + "integrity": "sha512-+m03kvI2f5syIqHXCZLPVYplP8pQch9JHyXKZ3AGMKlg8dCyr2PKHjwRLiW53LTrN/Nc3EqHOKxUxzoSPdKddA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.40.0.tgz", + "integrity": "sha512-lpPE1cLfP5oPzVjKMx10pgBmKELQnFJXHgvtHCtuJWOv8MxqdEIMNtgHgBFf7Ea2/7EuVwa9fodWUfXAlXZLZQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@tailwindcss/forms": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.10.tgz", + "integrity": "sha512-utI1ONF6uf/pPNO68kmN1b8rEwNXv3czukalo8VtJH8ksIkZXr3Q3VYudZLkCsDd4Wku120uF02hYK25XGPorw==", + "license": "MIT", + "dependencies": { + "mini-svg-data-uri": "^1.2.3" + }, + "peerDependencies": { + "tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1 || >= 4.0.0-alpha.20 || >= 4.0.0-beta.1" + } + }, + "node_modules/@tailwindcss/typography": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.16.tgz", + "integrity": "sha512-0wDLwCVF5V3x3b1SGXPCDcdsbDHMBe+lkFzBRaHeLvNi+nrrnZ1lA18u+OTWO8iSWU2GxUOCvlXtDuqftc1oiA==", + "license": "MIT", + "dependencies": { + "lodash.castarray": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.merge": "^4.6.2", + "postcss-selector-parser": "6.0.10" + }, + "peerDependencies": { + "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1" + } + }, + "node_modules/@tailwindcss/typography/node_modules/postcss-selector-parser": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.7.tgz", + "integrity": "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/estree": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", + "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.15.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.2.tgz", + "integrity": "sha512-uKXqKN9beGoMdBfcaTY1ecwz6ctxuJAcUlwE55938g0ZJ8lRxwAZqRz2AJ4pzpt5dHdTPMB863UZ0ESiFUcP7A==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/parse-json": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", + "license": "MIT" + }, + "node_modules/@types/prop-types": { + "version": "15.7.14", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", + "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==", + "license": "MIT" + }, + "node_modules/@types/quill": { + "version": "1.3.10", + "resolved": "https://registry.npmjs.org/@types/quill/-/quill-1.3.10.tgz", + "integrity": "sha512-IhW3fPW+bkt9MLNlycw8u8fWb7oO7W5URC9MfZYHBlA24rex9rs23D5DETChu1zvgVdc5ka64ICjJOgQMr6Shw==", + "license": "MIT", + "dependencies": { + "parchment": "^1.1.2" + } + }, + "node_modules/@types/react": { + "version": "18.3.20", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.20.tgz", + "integrity": "sha512-IPaCZN7PShZK/3t6Q87pfTkRm6oLTd4vztyoj+cbHUF1g3FfVb2tFIL79uCRKEfv16AhqDMBywP2VW3KIZUvcg==", + "license": "MIT", + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.3.6", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.6.tgz", + "integrity": "sha512-nf22//wEbKXusP6E9pfOCDwFdHAX4u172eaJI4YkDRQEZiorm6KfYnSC2SWLDMVWUOWPERmJnN0ujeAfTBLvrw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^18.0.0" + } + }, + "node_modules/@types/react-transition-group": { + "version": "4.4.12", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz", + "integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT", + "optional": true + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "dev": true, + "license": "ISC" + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.4.1.tgz", + "integrity": "sha512-IpEm5ZmeXAP/osiBXVVP5KjFMzbWOonMs0NaQQl+xYnUAcq4oHUBsF2+p4MgKWG4YMmFYJU8A6sxRPuowllm6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.26.10", + "@babel/plugin-transform-react-jsx-self": "^7.25.9", + "@babel/plugin-transform-react-jsx-source": "^7.25.9", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.17.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0" + } + }, + "node_modules/acorn": { + "version": "8.14.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", + "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "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==", + "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==", + "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==", + "license": "MIT" + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-includes": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", + "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.4", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlast": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", + "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", + "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.tosorted": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", + "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/autoprefixer": { + "version": "10.4.21", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz", + "integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==", + "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.24.4", + "caniuse-lite": "^1.0.30001702", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "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/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + } + }, + "node_modules/babel-plugin-macros/node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "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/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "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": { + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.1" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001715", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001715.tgz", + "integrity": "sha512-7ptkFGMm2OAOgvZpwgA4yjQ5SQbrNVGdRjzH0pBdy1Fasvcr+KAeECmbCAECzTuDuoX0FCY8KzUxjf9+9kfZEw==", + "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/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chart.js": { + "version": "4.4.9", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.9.tgz", + "integrity": "sha512-EyZ9wWKgpAU0fLJ43YAEIF8sr5F2W3LqbS40ZJyHIner2lY14ufqv2VMp69MAiZ2rpwxEUxEhIH/0U3xyRynxg==", + "license": "MIT", + "dependencies": { + "@kurkle/color": "^0.3.0" + }, + "engines": { + "pnpm": ">=8" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "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==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "license": "MIT", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cosmiconfig/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "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==", + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/date-fns": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz", + "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, + "node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-equal": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.2.tgz", + "integrity": "sha512-5tdhKF6DbU7iIzrIOa1AOUt39ZRm13cmL1cGEh//aqR8x9+tNfbywRf0n5FD/18OKMdo7DNEtrX2t22ZAkI+eg==", + "license": "MIT", + "dependencies": { + "is-arguments": "^1.1.1", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", + "object-is": "^1.1.5", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.5.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "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==", + "license": "MIT" + }, + "node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, + "node_modules/dompurify": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.5.tgz", + "integrity": "sha512-mLPd29uoRe9HpvwP2TxClGQBzGXeEC/we/q+bFlmPPmj2p2Ugl3r6ATu/UU1v77DXNcehiBg9zsr1dREyA/dJQ==", + "license": "(MPL-2.0 OR Apache-2.0)", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, + "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==", + "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/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.143", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.143.tgz", + "integrity": "sha512-QqklJMOFBMqe46k8iIOwA9l2hz57V2OKMmP5eSWcUvwx+mASAsbU+wkF1pHjn9ZVSBPrsYWr4/W/95y5SwYg2g==", + "dev": true, + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-abstract": { + "version": "1.23.9", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz", + "integrity": "sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.0", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-regex": "^1.2.1", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.0", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.3", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.3", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.18" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "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==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-iterator-helpers": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz", + "integrity": "sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.6", + "es-errors": "^1.3.0", + "es-set-tostringtag": "^2.0.3", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.6", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "iterator.prototype": "^1.1.4", + "safe-array-concat": "^1.1.3" + }, + "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==", + "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/es-shim-unscopables": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "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/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-react": { + "version": "7.37.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", + "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.8", + "array.prototype.findlast": "^1.2.5", + "array.prototype.flatmap": "^1.3.3", + "array.prototype.tosorted": "^1.1.4", + "doctrine": "^2.1.0", + "es-iterator-helpers": "^1.2.1", + "estraverse": "^5.3.0", + "hasown": "^2.0.2", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.9", + "object.fromentries": "^2.0.8", + "object.values": "^1.2.1", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.5", + "semver": "^6.3.1", + "string.prototype.matchall": "^4.0.12", + "string.prototype.repeat": "^1.0.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz", + "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.4.20", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.20.tgz", + "integrity": "sha512-XpbHQ2q5gUF8BGOX4dHe+71qoirYMhApEPZ7sfhF/dNnOF1UXnCMGZf79SFTBO7Bz5YEIT4TMieSlJBWhP9WBA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": ">=8.40" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/eslint/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eventemitter3": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-2.0.3.tgz", + "integrity": "sha512-jLN68Dx5kyFHaePoXWPsCGW5qdyZQtLYHkxkg02/Mz6g0kYpDx4FyP6XfArhQdlOC4b8Mv+EMxPo/8La7Tzghg==", + "license": "MIT" + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-diff": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.1.2.tgz", + "integrity": "sha512-KaJUt+M9t1qaIteSvjc6P3RbMdXsNhK61GRftR6SNxqmhthcd9MGIi4T+o0jD8LUSpSnSKXE20nLtJ3fOHxQig==", + "license": "Apache-2.0" + }, + "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==", + "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==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "license": "MIT" + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/framer-motion": { + "version": "11.18.2", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.18.2.tgz", + "integrity": "sha512-5F5Och7wrvtLVElIpclDT0CBzMVg3dL22B64aZwHtsIY8RB4mXICLrkajK4G9R+ieSAGcgrLeae2SeUTg2pr6w==", + "license": "MIT", + "dependencies": { + "motion-dom": "^11.18.1", + "motion-utils": "^11.18.1", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "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==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "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==", + "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==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/goober": { + "version": "2.1.16", + "resolved": "https://registry.npmjs.org/goober/-/goober-2.1.16.tgz", + "integrity": "sha512-erjk19y1U33+XAMe1VTvIONHYoSqE4iS7BYUZfHaqeohLmnC0FdxEh7rQU+6MZ4OajItzjZFSRtVANrQwNq6/g==", + "license": "MIT", + "peerDependencies": { + "csstype": "^3.0.10" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/gsap": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/gsap/-/gsap-3.13.0.tgz", + "integrity": "sha512-QL7MJ2WMjm1PHWsoFrAQH/J8wUeqZvMtHO58qdekHpCfhvhSL4gSiz6vJf5EeMP0LOn3ZCprL2ki/gjED8ghVw==", + "license": "Standard 'no charge' license: https://gsap.com/standard-license." + }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.0" + }, + "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==", + "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==", + "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==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "license": "BSD-3-Clause", + "dependencies": { + "react-is": "^16.7.0" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-arguments": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", + "integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "license": "MIT" + }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.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==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-function": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", + "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-proto": "^1.0.0", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/iterator.prototype": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", + "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "get-proto": "^1.0.0", + "has-symbols": "^1.1.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.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==", + "license": "MIT", + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsx-ast-utils": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "object.assign": "^4.1.4", + "object.values": "^1.1.6" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.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==", + "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==", + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, + "node_modules/lodash.castarray": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz", + "integrity": "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "license": "MIT" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/memoize-one": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", + "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==", + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "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==", + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mini-svg-data-uri": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz", + "integrity": "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==", + "license": "MIT", + "bin": { + "mini-svg-data-uri": "cli.js" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/motion-dom": { + "version": "11.18.1", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-11.18.1.tgz", + "integrity": "sha512-g76KvA001z+atjfxczdRtw/RXOM3OMSdd1f4DL77qCTF/+avrRJiawSG4yDibEQ215sr9kpinSlX2pCTJ9zbhw==", + "license": "MIT", + "dependencies": { + "motion-utils": "^11.18.1" + } + }, + "node_modules/motion-utils": { + "version": "11.18.1", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-11.18.1.tgz", + "integrity": "sha512-49Kt+HKjtbJKLtgO/LKj9Ld+6vw9BjH5d9sc40R/kVyH8GLAXgT42M2NnuPcJNuA3s9ZfZBUcwIgpmZWGEE+hA==", + "license": "MIT" + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "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==", + "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/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "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==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "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==", + "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==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-is": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", + "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.entries": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", + "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.values": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" + }, + "node_modules/parchment": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/parchment/-/parchment-1.1.4.tgz", + "integrity": "sha512-J5FBQt/pM2inLzg4hEWmzQx/8h8D0CiDxaG3vyp9rKrQRSDgBlhjdP5jQGgosEajXPSQouXGHOmVdgo7QmJuOg==", + "license": "BSD-3-Clause" + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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==", + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "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==", + "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==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pocketbase": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/pocketbase/-/pocketbase-0.21.5.tgz", + "integrity": "sha512-bnI/uinnQps+ElSlzxkc4yvwuSFfKcoszDtXH/4QT2FhGq2mJVUvDlxn+rjRXVntUjPfmMG5LEPZ1eGqV6ssog==", + "license": "MIT" + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postcss": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", + "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", + "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.8", + "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==", + "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-import/node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "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/postcss-js": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", + "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.0.0", + "yaml": "^2.3.4" + }, + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "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==", + "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==", + "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==", + "license": "MIT" + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "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==", + "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/quill": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/quill/-/quill-1.3.7.tgz", + "integrity": "sha512-hG/DVzh/TiknWtE6QmWAF/pxoZKYxfe3J/d/+ShUWkDvvkZQVTPeVmUJVu1uE6DDooC4fWTiCLh84ul89oNz5g==", + "license": "BSD-3-Clause", + "dependencies": { + "clone": "^2.1.1", + "deep-equal": "^1.0.1", + "eventemitter3": "^2.0.3", + "extend": "^3.0.2", + "parchment": "^1.1.4", + "quill-delta": "^3.6.2" + } + }, + "node_modules/quill-delta": { + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-3.6.3.tgz", + "integrity": "sha512-wdIGBlcX13tCHOXGMVnnTVFtGRLoP0imqxM696fIPwIf5ODIYUHIvHbZcyvGlZFiFhK5XzDC2lpjbxRhnM05Tg==", + "license": "MIT", + "dependencies": { + "deep-equal": "^1.0.1", + "extend": "^3.0.2", + "fast-diff": "1.1.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-chartjs-2": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-5.3.0.tgz", + "integrity": "sha512-UfZZFnDsERI3c3CZGxzvNJd02SHjaSJ8kgW1djn65H1KK8rehwTjyrRKOG3VTMG8wtHZ5rgAO5oTHtHi9GCCmw==", + "license": "MIT", + "peerDependencies": { + "chart.js": "^4.1.1", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/react-datepicker": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-8.3.0.tgz", + "integrity": "sha512-DhfrIJnTPJTUVRtXU7c7zooug40rD6q+Fc8UTCt19dYEotLpDQgTN98MfocY6Rc4S99oOFFEoxyanOM/TKauuw==", + "license": "MIT", + "dependencies": { + "@floating-ui/react": "^0.27.3", + "clsx": "^2.1.1", + "date-fns": "^4.1.0" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17 || ^18 || ^19 || ^19.0.0-rc", + "react-dom": "^16.9.0 || ^17 || ^18 || ^19 || ^19.0.0-rc" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-fast-marquee": { + "version": "1.6.5", + "resolved": "https://registry.npmjs.org/react-fast-marquee/-/react-fast-marquee-1.6.5.tgz", + "integrity": "sha512-swDnPqrT2XISAih0o74zQVE2wQJFMvkx+9VZXYYNSLb/CUcAzU9pNj637Ar2+hyRw6b4tP6xh4GQZip2ZCpQpg==", + "license": "MIT", + "peerDependencies": { + "react": ">= 16.8.0 || ^18.0.0", + "react-dom": ">= 16.8.0 || ^18.0.0" + } + }, + "node_modules/react-hot-toast": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.5.2.tgz", + "integrity": "sha512-Tun3BbCxzmXXM7C+NI4qiv6lT0uwGh4oAfeJyNOjYUejTsm35mK9iCaYLGv8cBz9L5YxZLx/2ii7zsIwPtPUdw==", + "license": "MIT", + "dependencies": { + "csstype": "^3.1.3", + "goober": "^2.1.16" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": ">=16", + "react-dom": ">=16" + } + }, + "node_modules/react-icons": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.5.0.tgz", + "integrity": "sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw==", + "license": "MIT", + "peerDependencies": { + "react": "*" + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/react-quill": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/react-quill/-/react-quill-2.0.0.tgz", + "integrity": "sha512-4qQtv1FtCfLgoD3PXAur5RyxuUbPXQGOHgTlFie3jtxp43mXDtzCKaOgQ3mLyZfi1PUlyjycfivKelFhy13QUg==", + "license": "MIT", + "dependencies": { + "@types/quill": "^1.3.10", + "lodash": "^4.17.4", + "quill": "^1.3.7" + }, + "peerDependencies": { + "react": "^16 || ^17 || ^18", + "react-dom": "^16 || ^17 || ^18" + } + }, + "node_modules/react-refresh": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-router": { + "version": "6.30.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.0.tgz", + "integrity": "sha512-D3X8FyH9nBcTSHGdEKurK7r8OYE1kKFn3d/CF+CoxbSHkxU7o37+Uh7eAHRXr6k2tSExXYO++07PeXJtA/dEhQ==", + "license": "MIT", + "dependencies": { + "@remix-run/router": "1.23.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.30.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.0.tgz", + "integrity": "sha512-x30B78HV5tFk8ex0ITwzC9TTZMua4jGyA9IUlH1JLQYQTFyxr/ZxwOJq7evg1JX1qGVUcvhsmQSKdPncQrjTgA==", + "license": "MIT", + "dependencies": { + "@remix-run/router": "1.23.0", + "react-router": "6.30.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, + "node_modules/react-select": { + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/react-select/-/react-select-5.10.1.tgz", + "integrity": "sha512-roPEZUL4aRZDx6DcsD+ZNreVl+fM8VsKn0Wtex1v4IazH60ILp5xhdlp464IsEAlJdXeD+BhDAFsBVMfvLQueA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.0", + "@emotion/cache": "^11.4.0", + "@emotion/react": "^11.8.1", + "@floating-ui/dom": "^1.0.1", + "@types/react-transition-group": "^4.4.0", + "memoize-one": "^6.0.0", + "prop-types": "^15.6.0", + "react-transition-group": "^4.3.0", + "use-isomorphic-layout-effect": "^1.2.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/react-toastify": { + "version": "10.0.6", + "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-10.0.6.tgz", + "integrity": "sha512-yYjp+omCDf9lhZcrZHKbSq7YMuK0zcYkDFTzfRFgTXkTFHZ1ToxwAonzA4JI5CxA91JpjFLmwEsZEgfYfOqI1A==", + "license": "MIT", + "dependencies": { + "clsx": "^2.1.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "license": "BSD-3-Clause", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, + "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==", + "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==", + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve": { + "version": "2.0.0-next.5", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", + "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.40.0.tgz", + "integrity": "sha512-Noe455xmA96nnqH5piFtLobsGbCij7Tu+tb3c1vYjNbTkfzGqXqQXG3wJaYXkRZuQ0vEYN4bhwg7QnIrqB5B+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.7" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.40.0", + "@rollup/rollup-android-arm64": "4.40.0", + "@rollup/rollup-darwin-arm64": "4.40.0", + "@rollup/rollup-darwin-x64": "4.40.0", + "@rollup/rollup-freebsd-arm64": "4.40.0", + "@rollup/rollup-freebsd-x64": "4.40.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.40.0", + "@rollup/rollup-linux-arm-musleabihf": "4.40.0", + "@rollup/rollup-linux-arm64-gnu": "4.40.0", + "@rollup/rollup-linux-arm64-musl": "4.40.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.40.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.40.0", + "@rollup/rollup-linux-riscv64-gnu": "4.40.0", + "@rollup/rollup-linux-riscv64-musl": "4.40.0", + "@rollup/rollup-linux-s390x-gnu": "4.40.0", + "@rollup/rollup-linux-x64-gnu": "4.40.0", + "@rollup/rollup-linux-x64-musl": "4.40.0", + "@rollup/rollup-win32-arm64-msvc": "4.40.0", + "@rollup/rollup-win32-ia32-msvc": "4.40.0", + "@rollup/rollup-win32-x64-msvc": "4.40.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==", + "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/safe-array-concat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "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==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string.prototype.matchall": { + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", + "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.6", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "regexp.prototype.flags": "^1.5.3", + "set-function-name": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.repeat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", + "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", + "license": "MIT" + }, + "node_modules/sucrase": { + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", + "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "^10.3.10", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tabbable": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", + "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==", + "license": "MIT" + }, + "node_modules/tailwindcss": { + "version": "3.4.17", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", + "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", + "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.6", + "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", + "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/tailwindcss/node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "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/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true, + "license": "MIT" + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "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==", + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "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==", + "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==", + "license": "Apache-2.0" + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "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/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/use-isomorphic-layout-effect": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.2.1.tgz", + "integrity": "sha512-tpZZ+EX0gaghDAiFR37hj5MgY6ZN55kLiPkJsKxBMZ6GZdOSPJXiOzPM984oPYZ5AnehYx5WQp1+ME8I/P/pRA==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "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==", + "license": "MIT" + }, + "node_modules/uuid": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, + "node_modules/vite": { + "version": "5.4.18", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.18.tgz", + "integrity": "sha512-1oDcnEp3lVyHCuQ2YFelM4Alm2o91xNoMncRm1U7S+JdYfYOvbiGZ3/CxGttrOu2M/KcGz7cRC2DoNUA6urmMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.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", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yaml": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz", + "integrity": "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..31deacc --- /dev/null +++ b/package.json @@ -0,0 +1,46 @@ +{ + "name": "campfirecritics", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", + "preview": "vite preview" + }, + "dependencies": { + "@tailwindcss/forms": "^0.5.10", + "@tailwindcss/typography": "^0.5.16", + "chart.js": "^4.4.3", + "dompurify": "^3.2.5", + "framer-motion": "^11.18.2", + "gsap": "^3.13.0", + "pocketbase": "^0.21.3", + "react": "^18.2.0", + "react-chartjs-2": "^5.2.0", + "react-datepicker": "^8.3.0", + "react-dom": "^18.2.0", + "react-fast-marquee": "^1.6.4", + "react-hot-toast": "^2.5.2", + "react-icons": "^5.5.0", + "react-quill": "^2.0.0", + "react-router-dom": "^6.23.1", + "react-select": "^5.10.1", + "react-toastify": "^10.0.5", + "uuid": "^11.1.0" + }, + "devDependencies": { + "@types/react": "^18.2.66", + "@types/react-dom": "^18.2.22", + "@vitejs/plugin-react": "^4.2.1", + "autoprefixer": "^10.4.19", + "eslint": "^8.57.0", + "eslint-plugin-react": "^7.34.1", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.4.6", + "postcss": "^8.4.38", + "tailwindcss": "^3.4.3", + "vite": "^5.2.0" + } +} diff --git a/pb_schema.txt b/pb_schema.txt new file mode 100644 index 0000000..32cec7b --- /dev/null +++ b/pb_schema.txt @@ -0,0 +1,186 @@ +users + +id +Nonempty +Hidden +password +Nonempty +Hidden +tokenKey +Nonempty +email +emailVisibility +verified +username +profile_picture +Single +role +user, moderator, admin +Single +showcase +reviews +Multiple +is_critic +description +banner_picture +Single +review_count +average_rating +balance +level +xp +created +Create +updated +Create/Update + + +achievements +id +title +description +xp_reward +icon +Single +created +Create +updated +Create/Update + + +media +id +path +title +type +movie, tv, game, anime +Single +overview +release_date +poster +Single +backdrop +Single +is_published +created_by +users +Single +average_rating +review_count +is_popular +characteristics +progress_type +created +Create +updated +Create/Update + + +review_likes +Nonempty +id +review_id +reviews +Single +user_id +users +Single +created +Create +updated +Create/Update + + +reviews +Nonempty +id +user_id +users +Single +media_id +media +Single +season_id +seasons +Single +media_type +movie, tv, game, anime +Single +content +ratings +overall_rating +has_spoilers +status +progress +like_count +created +Create +updated +Create/Update + + + +seasons +Nonempty +id +media_id +media +Single +Nonzero +season_number +title +overview +release_date +poster +Single +average_rating +review_count +is_published +created_by +users +Single +created +Create +updated +Create/Update + + + +support_tickets +Nonempty +id +user_id +users +Single +category +subject +message +status +open, in_progress, closed +Single +admin_notes +created +Create +updated +Create/Update + + + +user_achievements +Nonempty +id +user_id +users +Single +achievement_id +achievements +Single +awarded_by +users +Single +Nonempty +awarded_at +created +Create +updated +Create/Update 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/public/icon.png b/public/icon.png new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/public/icon.png @@ -0,0 +1 @@ + diff --git a/public/logo.png b/public/logo.png new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/public/logo.png @@ -0,0 +1 @@ + diff --git a/public/placeholder-poster.jpg b/public/placeholder-poster.jpg new file mode 100644 index 0000000..4e689ab --- /dev/null +++ b/public/placeholder-poster.jpg @@ -0,0 +1 @@ +https://images.pexels.com/photos/7991579/pexels-photo-7991579.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260 diff --git a/public/vite.svg b/public/vite.svg new file mode 100644 index 0000000..ee9fada --- /dev/null +++ b/public/vite.svg @@ -0,0 +1 @@ + diff --git a/src/App.css b/src/App.css new file mode 100644 index 0000000..b9d355d --- /dev/null +++ b/src/App.css @@ -0,0 +1,42 @@ +#root { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; + transition: filter 300ms; +} +.logo:hover { + filter: drop-shadow(0 0 2em #646cffaa); +} +.logo.react:hover { + filter: drop-shadow(0 0 2em #61dafbaa); +} + +@keyframes logo-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +@media (prefers-reduced-motion: no-preference) { + a:nth-of-type(2) .logo { + animation: logo-spin infinite 20s linear; + } +} + +.card { + padding: 2em; +} + +.read-the-docs { + color: #888; +} diff --git a/src/App.jsx b/src/App.jsx new file mode 100644 index 0000000..da0aa8a --- /dev/null +++ b/src/App.jsx @@ -0,0 +1,120 @@ +import React, { useEffect } from 'react'; +import { Routes, Route } from 'react-router-dom'; +import { AuthProvider } from './contexts/AuthContext'; +import { ProfileActionsProvider } from './contexts/ProfileActionsContext'; +import { ClickSparkProvider, useClickSpark } from './contexts/ClickSparkContext'; +import ClickSpark from './components/reactbits/Animations/ClickSpark/ClickSpark'; +import AuthRoute from './components/auth/AuthRoute'; +import GuestRoute from './components/auth/GuestRoute'; +import Header from './components/layout/Header'; +import Footer from './components/layout/Footer'; +import HomePage from './pages/HomePage'; +import CatalogPage from './pages/CatalogPage'; +import MediaOverviewPage from './pages/MediaOverviewPage'; +import ProfilePage from './pages/ProfilePage'; +import RatingPage from './pages/RatingPage'; +import AdminLayout from './components/admin/AdminLayout'; +import AdminDashboard from './pages/admin/AdminDashboard'; +import AdminMediaPage from './pages/admin/AdminMediaPage'; +import AdminUsersPage from './pages/admin/AdminUsersPage'; +import AdminSeasonsPage from './pages/admin/AdminSeasonsPage'; +import AdminAchievementsPage from './pages/admin/AdminAchievementsPage'; +import AdminSupportPage from './pages/admin/AdminSupportPage'; +import NotFoundPage from './pages/NotFoundPage'; +import LoginPage from './pages/LoginPage'; +import RegisterPage from './pages/RegisterPage'; +import ForgotPasswordPage from './pages/ForgotPasswordPage'; +import SupportTicketForm from './components/support/SupportTicketForm'; +import PrivacyPolicyPage from './pages/legal/PrivacyPolicyPage'; +import TermsOfServicePage from './pages/legal/TermsOfServicePage'; +import UserAgreementPage from './pages/legal/UserAgreementPage'; +import ProfileSettingsPage from './pages/ProfileSettingsPage'; +import AdminSuggestionsPage from './pages/admin/AdminSuggestionsPage'; +import AdminRoute from './components/auth/AdminRoute'; +import SupportPage from './pages/SupportPage'; +import './index.css'; + +const AppContent = () => { + const { addSpark } = useClickSpark(); + + useEffect(() => { + const handleClick = (e) => { + console.log('[ClickSpark] Добавление искры:', e.clientX, e.clientY); + addSpark(e.clientX, e.clientY); + }; + + document.addEventListener('click', handleClick); + return () => document.removeEventListener('click', handleClick); + }, [addSpark]); + + return ( +
+ +
+
+ + } /> + } /> + } /> + } /> + } /> + + } /> + } /> + } /> + + }> + } /> + } /> + + + }> + }> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + + + + }> + } /> + + + } /> + } /> + } /> + + } /> + +
+
+
+ ); +}; + +function App() { + return ( + + + + + + + + ); +} + +export default App; diff --git a/src/assets/404.webp b/src/assets/404.webp new file mode 100644 index 0000000..ead42ef Binary files /dev/null and b/src/assets/404.webp differ diff --git a/src/assets/react.svg b/src/assets/react.svg new file mode 100644 index 0000000..8e0e0f1 --- /dev/null +++ b/src/assets/react.svg @@ -0,0 +1 @@ + diff --git a/src/components/LatestReviewsMarquee.jsx b/src/components/LatestReviewsMarquee.jsx new file mode 100644 index 0000000..92d01e9 --- /dev/null +++ b/src/components/LatestReviewsMarquee.jsx @@ -0,0 +1,139 @@ +import React, { useEffect, useState } from 'react'; +import Marquee from 'react-fast-marquee'; +import { getLatestReviews, getFileUrl } from '../services/pocketbaseService'; // Assuming getLatestReviews exists +import { FaFire, FaUserCircle } from 'react-icons/fa'; // Import FaUserCircle +import { Link } from 'react-router-dom'; // Import Link + +// Mapping for status values +const statusLabels = { + not_started: 'Не начато', + in_progress: 'В процессе', + completed: 'Завершено', +}; + +const LatestReviewsMarquee = () => { + const [reviews, setReviews] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + const fetchReviews = async () => { + try { + setLoading(true); + setError(null); + // Ensure expand=users,media is included to get related data + const latestReviews = await getLatestReviews({ expand: 'users,media', sort: '-created', limit: 20 }); // Fetch latest 20 reviews, sorted by creation date descending + setReviews(latestReviews); + console.log('LatestReviewsMarquee: Fetched reviews with expand:', latestReviews); + } catch (err) { + console.error('Error fetching latest reviews:', err); + setError('Не удалось загрузить последние рецензии.'); + } finally { + setLoading(false); + } + }; + + fetchReviews(); + }, []); // Empty dependency array means this runs once on mount + + if (loading) { + return
Загрузка последних рецензий...
; + } + + if (error) { + return
{error}
; + } + + if (!reviews || reviews.length === 0) { + return
Пока нет последних рецензий.
; + } + + return ( +
+ + {reviews.map(review => { + // Add robust checks for expanded data + const user = review.expand?.users; + const media = review.expand?.media; + + // Skip review if essential expanded data is missing + if (!user || !media || typeof media.characteristics !== 'object') { + console.warn("Skipping review", review.id, "in marquee due to missing user, media, or required fields."); + return null; // Skip rendering this review + } + + const username = user.username || 'Анонимный пользователь'; + const avatarUrl = user.profile_picture ? getFileUrl(user, 'profile_picture', { thumb: '40x40' }) : null; + const userProfileLink = user.username ? `/profile/${user.username}` : '#'; + + const mediaTitle = media.title || 'Неизвестное произведение'; + const mediaLink = media.path ? `/media/${media.path}` : '#'; + + // Calculate average rating for this specific review based on media characteristics + const characteristics = media.characteristics; + const ratings = review.ratings; // Expecting { [key]: number } + + const applicableRatings = Object.entries(characteristics).reduce((acc, [key, label]) => { + // Check if the rating exists for this characteristic key and is a valid number + if (ratings && typeof ratings[key] === 'number' && ratings[key] >= 1 && ratings[key] <= 10) { + acc.push(ratings[key]); + } + return acc; + }, []); + + const totalApplicableRating = applicableRatings.reduce((sum, value) => sum + value, 0); + const averageReviewRating = applicableRatings.length > 0 + ? (totalApplicableRating / applicableRatings.length).toFixed(1) + : 'N/A'; // Show N/A if no applicable ratings + + + return ( +
+ {/* User Avatar */} + {avatarUrl ? ( + {`${username}'s + ) : ( + + )} + + {/* Review Info */} +
+
+ {/* Link to user profile */} + + {username} + + рецензирует + {/* Link to media page */} + + {mediaTitle} + +
+ {/* Display overall average for this review */} +
+ Оценка: + + {averageReviewRating} / 10 + + {averageReviewRating !== 'N/A' && } +
+ {/* Status */} + {review.status && statusLabels[review.status] && ( +
+ Статус: {statusLabels[review.status]} +
+ )} +
+
+ ); + })} +
+
+ ); +}; + +export default LatestReviewsMarquee; diff --git a/src/components/admin/AchievementForm.jsx b/src/components/admin/AchievementForm.jsx new file mode 100644 index 0000000..93ae688 --- /dev/null +++ b/src/components/admin/AchievementForm.jsx @@ -0,0 +1,151 @@ +import React, { useState, useEffect } from 'react'; +import { pb } from '../../services/pocketbaseService'; // Import pb for create/update + +const AchievementForm = ({ onSuccess, onCancel, achievementToEdit }) => { + const [formData, setFormData] = useState({ + title: '', + description: '', + xp_reward: 0, + // Add other fields like icon if you have them + }); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + + useEffect(() => { + if (achievementToEdit) { + setFormData({ + title: achievementToEdit.title, + description: achievementToEdit.description, + xp_reward: achievementToEdit.xp_reward, + // Load other fields here + }); + } else { + // Reset form if creating a new achievement + setFormData({ + title: '', + description: '', + xp_reward: 0, + }); + } + }, [achievementToEdit]); + + const handleChange = (e) => { + const { name, value, type } = e.target; + setFormData({ + ...formData, + [name]: type === 'number' ? parseInt(value, 10) || 0 : value, // Parse number input + }); + }; + + const handleSubmit = async (e) => { + e.preventDefault(); + setLoading(true); + setError(null); + + try { + let record; + if (achievementToEdit) { + // Update existing achievement + console.log('PocketBase: Updating achievement:', achievementToEdit.id, formData); + record = await pb.collection('achievements').update(achievementToEdit.id, formData); + console.log('PocketBase: Achievement updated:', record); + } else { + // Create new achievement + console.log('PocketBase: Creating achievement:', formData); + record = await pb.collection('achievements').create(formData); + console.log('PocketBase: Achievement created:', record); + } + onSuccess(); // Call success callback + } catch (err) { + console.error('Error saving achievement:', err); + setError(err.message); + } finally { + setLoading(false); + } + }; + + return ( +
+ {error && ( +
+ {error} +
+ )} + +
+ + +
+ +
+ + +
+ +
+ + +
+ + {/* Add file input for icon if needed */} + {/* +
+ + +
+ */} + + +
+ + +
+
+ ); +}; + +export default AchievementForm; diff --git a/src/components/admin/AdminLayout.jsx b/src/components/admin/AdminLayout.jsx new file mode 100644 index 0000000..41a6c31 --- /dev/null +++ b/src/components/admin/AdminLayout.jsx @@ -0,0 +1,46 @@ +import React, { useEffect } from 'react'; +import { Outlet, useLocation, Navigate } from 'react-router-dom'; +import AdminSidebar from './AdminSidebar'; +import { useAuth } from '../../contexts/AuthContext'; +import { canManageSystem } from '../../utils/permissions'; + +const AdminLayout = () => { + const { user, isInitialized } = useAuth(); + const location = useLocation(); + + useEffect(() => { + console.log('AdminLayout - Location changed:', location.pathname); + }, [location]); + + console.log('AdminLayout - Current location:', location.pathname); + console.log('AdminLayout - User:', user); + + if (!isInitialized) { + return ( +
+
+
+ ); + } + + if (!canManageSystem(user)) { + console.log('AdminLayout - User does not have system management rights, redirecting to home'); + return ; + } + + console.log('AdminLayout - Rendering layout with Outlet'); + return ( +
+
+ +
+
+ +
+
+
+
+ ); +}; + +export default AdminLayout; diff --git a/src/components/admin/AdminSidebar.jsx b/src/components/admin/AdminSidebar.jsx new file mode 100644 index 0000000..e590fc8 --- /dev/null +++ b/src/components/admin/AdminSidebar.jsx @@ -0,0 +1,108 @@ +import React from 'react'; +import { NavLink, useLocation } from 'react-router-dom'; +import { FaUsers, FaFilm, FaTrophy, FaHeadset, FaLightbulb, FaHome } from 'react-icons/fa'; +import { useAuth } from '../../contexts/AuthContext'; +import { + canManageMedia, + canManageUsers, + canManageSeasons, + canManageAchievements, + canManageSuggestions, + canManageSupport +} from '../../utils/permissions'; + +const AdminSidebar = () => { + const location = useLocation(); + const { user } = useAuth(); + + console.log('AdminSidebar - Current location:', location.pathname); + + const menuItems = [ + { + title: 'Главная', + path: '/admin', + icon: , + show: true // Всегда показываем главную + }, + { + title: 'Медиа', + path: '/admin/media', + icon: , + show: canManageMedia(user) + }, + { + title: 'Пользователи', + path: '/admin/users', + icon: , + show: canManageUsers(user) + }, + { + title: 'Сезоны', + path: '/admin/seasons', + icon: , + show: canManageSeasons(user) + }, + { + title: 'Достижения', + path: '/admin/achievements', + icon: , + show: canManageAchievements(user) + }, + { + title: 'Поддержка', + path: '/admin/support', + icon: , + show: canManageSupport(user) + }, + { + title: 'Предложения', + path: '/admin/suggestions', + icon: , + show: canManageSuggestions(user) + } + ]; + + return ( +
+ +
+ ); +}; + +export default AdminSidebar; \ No newline at end of file diff --git a/src/components/admin/MediaForm.jsx b/src/components/admin/MediaForm.jsx new file mode 100644 index 0000000..0dcbee2 --- /dev/null +++ b/src/components/admin/MediaForm.jsx @@ -0,0 +1,713 @@ +import React, { useState, useEffect, useMemo } from 'react'; // Import useMemo +import { createMedia, updateMedia, validateMediaData, formatMediaData, getFileUrl, mediaTypes } from '../../services/pocketbaseService'; +import { useAuth } from '../../contexts/AuthContext'; +import { FaUpload, FaTimesCircle } from 'react-icons/fa'; // Import icons +import { v4 as uuidv4 } from 'uuid'; // Import uuid for unique IDs + +// Define characteristic presets +const characteristicPresets = { + movie: [ + { key: 'story', label: 'Сюжет' }, + { key: 'acting', label: 'Актерская игра' }, + { key: 'visuals', label: 'Визуал' }, + { key: 'sound', label: 'Звук' }, + { key: 'atmosphere', label: 'Атмосфера' }, + { key: 'pacing', label: 'Темп' }, + ], + tv: [ + { key: 'story', label: 'Сюжет' }, + { key: 'acting', label: 'Актерская игра' }, + { key: 'visuals', label: 'Визуал' }, + { key: 'sound', label: 'Звук' }, + { key: 'characters', label: 'Персонажи' }, + { key: 'pacing', label: 'Темп' }, + ], + game: [ + { key: 'updates', label: 'Сопровождение' }, + { key: 'story', label: 'Сюжет' }, + { key: 'gameplay', label: 'Геймплей' }, + { key: 'visual', label: 'Визуал' }, + { key: 'community', label: 'Сообщество' }, + { key: 'atmosphere', label: 'Атмосфера' }, + { key: 'soundtrack', label: 'Саундтрек' }, + { key: 'openworld', label: 'Открытый мир' }, + ], + anime: [ + { key: 'story', label: 'Сюжет' }, + { key: 'characters', label: 'Персонажи' }, + { key: 'visuals', label: 'Визуал' }, + { key: 'sound', label: 'Звук' }, + { key: 'atmosphere', label: 'Атмосфера' }, + { key: 'pacing', label: 'Темп' }, + ], + // Add more types as needed +}; + +// Define progress type options +const progressTypeOptions = [ + { value: 'hours', label: 'Часы (для игр)' }, + { value: 'watched', label: 'Просмотрено (для фильмов/сериалов)' }, + { value: 'completed', label: 'Пройдено (для сюжетных игр)' }, +]; + + +function MediaForm({ media, onSuccess }) { + const [title, setTitle] = useState(''); + const [path, setPath] = useState(''); + const [type, setType] = useState(''); + const [overview, setOverview] = useState(''); + const [poster, setPoster] = useState(null); + const [backdrop, setBackdrop] = useState(null); + // Change characteristics state to an array of objects { id, key, label } + const [characteristics, setCharacteristics] = useState([]); + const [isPublished, setIsPublished] = useState(false); + const [isPopular, setIsPopular] = useState(false); // State for is_popular + const [progressType, setProgressType] = useState('completed'); // State for progress_type + const [releaseDate, setReleaseDate] = useState(''); // State for release_date + + const [posterPreview, setPosterPreview] = useState(null); + const [backdropPreview, setBackdropPreview] = useState(null); + + const [loading, setLoading] = useState(false); + const [errors, setErrors] = useState([]); + + const { user } = useAuth(); // Get current user + + const isEditing = !!media; // Determine if we are editing or creating + + useEffect(() => { + if (media) { + // Populate form for editing + setTitle(media.title || ''); + setPath(media.path || ''); + setType(media.type || ''); + setOverview(media.overview || media.description || ''); // Use overview or description + + // Convert characteristics object from PocketBase to array of { id, key, label } + let initialCharacteristicsArray = []; + try { + // Check if characteristics is already an object or needs parsing + const rawCharacteristics = typeof media.characteristics === 'object' && media.characteristics !== null + ? media.characteristics // It's already an object + : (typeof media.characteristics === 'string' && media.characteristics ? JSON.parse(media.characteristics) : {}); // Parse if it's a string + + // Convert the object { key: label } to an array of { id, key, label } + initialCharacteristicsArray = Object.entries(rawCharacteristics).map(([key, label]) => ({ + id: uuidv4(), // Generate a unique ID for React key + key: key, + label: label + })); + + } catch (e) { + console.error("Failed to parse characteristics JSON:", e); + initialCharacteristicsArray = []; // Default to empty array on error + } + setCharacteristics(initialCharacteristicsArray); + + setIsPublished(media.is_published ?? false); // Use ?? for null/undefined check + setIsPopular(media.is_popular ?? false); // Populate is_popular + setProgressType(media.progress_type || 'completed'); // Populate progress_type, default to 'completed' if null/empty + // Populate release_date, format to YYYY-MM-DD for input type="date" + setReleaseDate(media.release_date ? new Date(media.release_date).toISOString().split('T')[0] : ''); + + + // Set initial previews for existing files + setPosterPreview(getFileUrl(media, 'poster')); + setBackdropPreview(getFileUrl(media, 'backdrop')); + + } else { + // Reset form for creation + setTitle(''); + setPath(''); + setType(''); + setOverview(''); + setPoster(null); + setBackdrop(null); + setCharacteristics([]); // Reset to empty array + setIsPublished(false); + setIsPopular(false); // Reset is_popular + setProgressType('completed'); // Default progressType to 'completed' for new media + setReleaseDate(''); // Reset release_date + setPosterPreview(null); + setBackdropPreview(null); + } + setErrors([]); // Clear errors on media change + }, [media]); + + // Update path automatically when title changes, if creating + useEffect(() => { + if (!isEditing && title) { + // Simple slugification: lowercase, replace spaces with hyphens, remove non-alphanumeric + const slug = title + .toLowerCase() + .replace(/\s+/g, '-') // Replace spaces with hyphens + .replace(/[^a-z0-9-]/g, '') // Remove non-alphanumeric except hyphens + .replace(/--+/g, '-') // Replace multiple hyphens with single + .replace(/^-+|-+$/g, ''); // Remove leading/trailing hyphens + setPath(slug); + } else if (!isEditing && !title) { + setPath(''); // Clear path if title is empty when creating + } + }, [title, isEditing]); + + + // Handle change for characteristic label (now takes item id and new label) + const handleCharacteristicLabelChange = (id, newLabel) => { + setCharacteristics(prev => + prev.map(char => + char.id === id ? { ...char, label: newLabel } : char + ) + ); + }; + + // Handle change for characteristic key (now takes item id and new key) + const handleCharacteristicKeyChange = (id, newKey) => { + // Basic validation: key cannot be empty + if (!newKey.trim()) { + setErrors(prev => [...prev, 'Ключ характеристики не может быть пустым.']); + return; + } + + // Check for duplicate keys (excluding the item being edited) + const isDuplicate = characteristics.some(char => + char.id !== id && char.key === newKey.trim() + ); + + if (isDuplicate) { + setErrors(prev => [...prev, `Ключ характеристики "${newKey.trim()}" уже существует.`]); + return; + } + + setCharacteristics(prev => + prev.map(char => + char.id === id ? { ...char, key: newKey.trim() } : char + ) + ); + setErrors(prev => prev.filter(err => !err.startsWith('Ключ характеристики'))); // Clear key-related errors on successful change + }; + + + const handleAddCharacteristic = () => { + // Check if max characteristics reached (8) + if (characteristics.length >= 8) { + setErrors(prev => [...prev, 'Максимальное количество характеристик - 8.']); + return; + } + // Add a new characteristic object with a unique ID and default values + const newChar = { + id: uuidv4(), + key: `new_char_${characteristics.length + 1}`, // Default key + label: '' // Empty label + }; + setCharacteristics(prev => [...prev, newChar]); + setErrors(prev => prev.filter(err => !err.startsWith('Максимальное количество характеристик'))); // Clear max count error + }; + + const handleRemoveCharacteristic = (idToRemove) => { + // Check if minimum characteristics reached (6) + if (characteristics.length <= 6) { + setErrors(prev => [...prev, 'Минимальное количество характеристик - 6.']); + return; + } + setCharacteristics(prev => prev.filter(char => char.id !== idToRemove)); + setErrors(prev => prev.filter(err => !err.startsWith('Минимальное количество характеристик'))); // Clear min count error + }; + + + // Determine if the current type has a preset + const hasPreset = type && characteristicPresets[type]; + + // Get the preset characteristics for the current type (memoized) + const presetChars = useMemo(() => { + if (hasPreset) { + // Convert preset array to array of { id, key, label } + return characteristicPresets[type].map(char => ({ + id: uuidv4(), // Generate new IDs for preset application + key: char.key, + label: char.label + })); + } + return []; + }, [type, hasPreset]); // Recompute if type or hasPreset changes + + + // Function to apply a preset + const applyPreset = () => { + if (hasPreset) { + setCharacteristics(presetChars); // Set the state with the memoized preset array + setErrors(prev => prev.filter(err => !err.includes('характеристик'))); // Clear characteristic count errors + } + }; + + + const handleFileChange = (e, field) => { + const file = e.target.files[0]; + if (file) { + if (field === 'poster') { + setPoster(file); + setPosterPreview(URL.createObjectURL(file)); + } else if (field === 'backdrop') { + setBackdrop(file); + setBackdropPreview(URL.createObjectURL(file)); + } + } + }; + + const handleRemoveFile = (field) => { + if (field === 'poster') { + setPoster(null); + setPosterPreview(null); + } else if (field === 'backdrop') { + setBackdrop(null); + setBackdropPreview(null); + } + }; + + + const handleSubmit = async (e) => { + e.preventDefault(); + setLoading(true); + setErrors([]); + + const formData = new FormData(); + formData.append('title', title); + formData.append('path', path); + formData.append('type', type); + formData.append('overview', overview); // Use overview field + formData.append('is_published', isPublished); + formData.append('is_popular', isPopular); // Include is_popular + formData.append('progress_type', progressType); // Include progress_type + // Append release_date if it exists + if (releaseDate) { + formData.append('release_date', releaseDate); + } + + + // Append files if they are new File objects + if (poster instanceof File) { + formData.append('poster', poster); + } + if (backdrop instanceof File) { + formData.append('backdrop', backdrop); + } + + // Handle file deletion by setting the field to null in FormData + // This is necessary if the user removed a previously existing file + if (isEditing) { + // If editing and posterPreview is null but media.poster existed, set poster to null + if (!posterPreview && media.poster) { + formData.append('poster', ''); // PocketBase expects empty string or null for deletion + } + // If editing and backdropPreview is null but media.backdrop existed, set backdrop to null + if (!backdropPreview && media.backdrop) { + formData.append('backdrop', ''); // PocketBase expects empty string or null for deletion + } + } + + // Convert characteristics array [{ id, key, label }] back to object { key: label } for PocketBase + const characteristicsObject = characteristics.reduce((acc, char) => { + // Only include if key is not empty + if (char.key.trim()) { + acc[char.key.trim()] = char.label; + } + return acc; + }, {}); + + // Append characteristics as a JSON string + formData.append('characteristics', JSON.stringify(characteristicsObject)); + + // Add created_by user ID if creating + if (!isEditing && user) { + formData.append('created_by', user.id); + } + + + // Validate data before submitting + // Pass the characteristics object for validation + const validationErrors = validateMediaData(formData, characteristicsObject); + if (validationErrors.length > 0) { + setErrors(validationErrors); + setLoading(false); + return; + } + + // Format data (less critical with FormData, but can be used for final checks) + const dataToSubmit = formatMediaData(formData); + + + try { + if (isEditing) { + await updateMedia(media.id, dataToSubmit); + console.log('Media updated successfully'); + } else { + await createMedia(dataToSubmit); + console.log('Media created successfully'); + } + onSuccess(); // Close modal and refresh list + } catch (err) { + console.error('Error submitting media form:', err); + if (err.response && err.response.data) { + console.error('PocketBase Response Data:', err.response.data); + // Attempt to extract specific error messages from PocketBase response + const apiErrors = []; + for (const field in err.response.data) { + if (err.response.data[field].message) { + apiErrors.push(`${field}: ${err.response.data[field].message}`); + } + } + if (apiErrors.length > 0) { + setErrors(apiErrors); + } else { + setErrors(['Произошла ошибка при сохранении контента.']); + } + } else { + setErrors(['Произошла ошибка при сохранении контента.']); + } + } finally { + setLoading(false); + } + }; + + + return ( +
+

+ {isEditing ? 'Редактировать контент' : 'Добавить новый контент'} +

+ + {errors.length > 0 && ( +
+
    + {errors.map((err, index) => ( +
  • {err}
  • + ))} +
+
+ )} + +
+ {/* Basic Info */} +
+ + setTitle(e.target.value)} + className="input w-full px-3 py-2 bg-campfire-dark text-campfire-light border border-campfire-ash/30 rounded-md focus:outline-none focus:ring-campfire-amber focus:border-campfire-amber transition-colors" + required + /> +
+ +
+ + setPath(e.target.value)} + className="input w-full px-3 py-2 bg-campfire-dark text-campfire-light border border-campfire-ash/30 rounded-md focus:outline-none focus:ring-campfire-amber focus:border-campfire-amber transition-colors" + required + /> +

+ Используется в URL (например, `/media/ваш-путь`). Только латинские буквы, цифры и дефисы. +

+
+ +
+ + +
+ + {/* Progress Type Selection */} +
+ + +

+ Определяет, как пользователи будут отмечать прогресс (часы, просмотрено/нет, пройдено/нет). +

+
+ + {/* Release Date */} +
+ + setReleaseDate(e.target.value)} + className="input w-full px-3 py-2 bg-campfire-dark text-campfire-light border border-campfire-ash/30 rounded-md focus:outline-none focus:ring-campfire-amber focus:border-campfire-amber transition-colors" + /> +
+ + +
+ + +
+ + {/* File Uploads */} +
+ {/* Poster Upload */} +
+ +
+ {posterPreview ? ( + <> + Poster Preview + + + ) : ( +
+ +
+ +

или перетащите сюда

+
+

PNG, JPG, GIF до 10MB

+
+ )} +
+
+ + {/* Backdrop Upload */} +
+ +
+ {backdropPreview ? ( + <> + Backdrop Preview + + + ) : ( +
+ +
+ +

или перетащите сюда

+
+

PNG, JPG, GIF до 10MB

+
+ )} +
+
+
+ + + {/* Characteristics */} +
+
+

+ Характеристики * +

+ {hasPreset && ( + + )} +
+ +

+ Определите характеристики, по которым пользователи будут оценивать контент (минимум 6, максимум 8). +

+ +
{/* Use grid for better layout */} + {/* Map over the characteristics array */} + {characteristics.map((char) => ( +
{/* Styled container */} +
{/* Inner grid for key/label */} + {/* Key Input */} +
+ + handleCharacteristicKeyChange(char.id, e.target.value)} + className="input w-full px-2 py-1 bg-campfire-charcoal text-campfire-light border border-campfire-ash/20 rounded-md text-sm focus:outline-none focus:ring-campfire-amber focus:border-campfire-amber transition-colors" + placeholder="e.g., story" + required + /> +
+ {/* Label Input */} +
+ + handleCharacteristicLabelChange(char.id, e.target.value)} + className="input w-full px-2 py-1 bg-campfire-charcoal text-campfire-light border border-campfire-ash/20 rounded-md text-sm focus:outline-none focus:ring-campfire-amber focus:border-campfire-amber transition-colors" + placeholder="e.g., Сюжет" + required + /> +
+
+ {characteristics.length > 6 && ( // Only show remove if more than 6 + + )} +
+ ))} +
+ + {characteristics.length < 8 && ( // Only show add if less than 8 + + )} +
+ + + {/* Status Toggles */} +
+

Статус

+
+ {/* Is Published */} +
+ setIsPublished(e.target.checked)} + className="w-4 h-4 text-campfire-amber bg-campfire-dark border-campfire-ash rounded focus:ring-campfire-amber focus:ring-2 cursor-pointer" + /> + +
+ {/* Is Popular */} +
+ setIsPopular(e.target.checked)} + className="w-4 h-4 text-campfire-amber bg-campfire-dark border-campfire-ash rounded focus:ring-campfire-amber focus:ring-2 cursor-pointer" + /> + +
+
+
+ + + {/* Submit Button */} +
+ +
+
+
+ ); +} + +export default MediaForm; diff --git a/src/components/auth/AdminRoute.jsx b/src/components/auth/AdminRoute.jsx new file mode 100644 index 0000000..a122673 --- /dev/null +++ b/src/components/auth/AdminRoute.jsx @@ -0,0 +1,29 @@ +import { Navigate, Outlet, useLocation } from 'react-router-dom'; +import { useAuth } from '../../contexts/AuthContext'; +import { canManageSystem } from '../../utils/permissions'; + +const AdminRoute = () => { + const { user, isInitialized } = useAuth(); + const location = useLocation(); + + console.log('AdminRoute - Current location:', location.pathname); + console.log('AdminRoute - User:', user); + + if (!isInitialized) { + return ( +
+
+
+ ); + } + + if (!canManageSystem(user)) { + console.log('AdminRoute - User does not have system management rights, redirecting to home'); + return ; + } + + console.log('AdminRoute - User has system management rights, rendering child routes'); + return ; +}; + +export default AdminRoute; diff --git a/src/components/auth/AuthRoute.jsx b/src/components/auth/AuthRoute.jsx new file mode 100644 index 0000000..6aafda9 --- /dev/null +++ b/src/components/auth/AuthRoute.jsx @@ -0,0 +1,26 @@ +import { Navigate, Outlet } from 'react-router-dom'; +import { useAuth } from '../../contexts/AuthContext'; // Adjust path if necessary + +const AuthRoute = () => { // Renamed component from ProtectedRoute to AuthRoute + const { user, isInitialized } = useAuth(); + + // Wait for auth state to be initialized + if (!isInitialized) { + // You might want a loading spinner here + return ( +
+
+
+ ); + } + + // If user is not authenticated, redirect to login page + if (!user) { + return ; + } + + // If user is authenticated, render the child routes + return ; +}; + +export default AuthRoute; // Exporting AuthRoute diff --git a/src/components/auth/GuestRoute.jsx b/src/components/auth/GuestRoute.jsx new file mode 100644 index 0000000..0b5ed22 --- /dev/null +++ b/src/components/auth/GuestRoute.jsx @@ -0,0 +1,31 @@ +import { Navigate, Outlet } from 'react-router-dom'; +import { useAuth } from '../../contexts/AuthContext'; // Adjust path if necessary + +const GuestRoute = () => { + const { user, isInitialized } = useAuth(); + + // Wait for auth state to be initialized + if (!isInitialized) { + // You might want a loading spinner here + return ( +
+
+
+ ); + } + + // If user is authenticated, redirect them away from guest-only routes + // Redirect to profile page or home page + if (user) { + // Assuming userProfile contains username, otherwise redirect to a default authenticated page like '/' + // If userProfile is not immediately available, you might redirect to a loading page or '/' + // For now, let's redirect to the home page or profile if username is available + // A simple redirect to '/' is safer if userProfile isn't guaranteed here + return ; + } + + // If user is not authenticated, render the child routes + return ; +}; + +export default GuestRoute; diff --git a/src/components/common/Modal.jsx b/src/components/common/Modal.jsx new file mode 100644 index 0000000..d1ee0ed --- /dev/null +++ b/src/components/common/Modal.jsx @@ -0,0 +1,49 @@ +import React from 'react'; +import { createPortal } from 'react-dom'; +import { FaTimes } from 'react-icons/fa'; // Assuming you have react-icons installed + +// Added size prop to control modal width +const Modal = ({ isOpen, onClose, title, children, size = 'lg' }) => { + if (!isOpen) return null; + + // Determine max-width class based on size prop + let maxWidthClass = 'max-w-lg'; // Default size + if (size === 'md') { + maxWidthClass = 'max-w-md'; + } else if (size === 'xl') { + maxWidthClass = 'max-w-xl'; + } else if (size === '2xl') { + maxWidthClass = 'max-w-2xl'; + } else if (size === 'full') { + maxWidthClass = 'max-w-full'; // Use with caution, might need padding adjustments + } + + + // Use createPortal to render the modal outside the main app div + return createPortal( +
+ {/* Use maxWidthClass here */} +
+ {/* Modal Header */} +
+

{title}

+ +
+ + {/* Modal Body */} +
+ {children} +
+
+
, + document.body // Render into the body + ); +}; + +export default Modal; diff --git a/src/components/home/FeaturedMedia.jsx b/src/components/home/FeaturedMedia.jsx new file mode 100644 index 0000000..49289c1 --- /dev/null +++ b/src/components/home/FeaturedMedia.jsx @@ -0,0 +1,53 @@ +import React from 'react'; +import { Link } from 'react-router-dom'; +import TiltedCard from '../reactbits/Components/TiltedCard/TiltedCard'; +import { getFileUrl } from '../../services/pocketbaseService'; + +const FeaturedMedia = ({ media }) => { + if (!media || media.length === 0) { + return null; + } + + return ( +
+
+

Популярное

+
+ {media.map((item) => ( + + +

{item.title}

+
+ {item.rating} + / 10 +
+
+ } + /> + + ))} +
+ +
+ ); +}; + +export default FeaturedMedia; \ No newline at end of file diff --git a/src/components/home/GridSection.jsx b/src/components/home/GridSection.jsx new file mode 100644 index 0000000..1434266 --- /dev/null +++ b/src/components/home/GridSection.jsx @@ -0,0 +1,44 @@ +import React from 'react'; +import GridMotion from '../reactbits/Backgrounds/GridMotion/GridMotion'; +import StatsSection from './StatsSection'; + +const GridSection = ({ posters = [], stats = {} }) => { + // Функция для перемешивания массива + const shuffleArray = (array) => { + const shuffled = [...array]; + for (let i = shuffled.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]; + } + return shuffled; + }; + + // Перемешиваем постеры + const shuffledPosters = shuffleArray(posters); + + return ( +
+ {/* Stats Section with Background */} +
+
+
+ +
+
+
+ + {/* Grid Motion Background */} +
+ {shuffledPosters.length > 0 && ( + + )} +
+
+ ); +}; + +export default GridSection; \ No newline at end of file diff --git a/src/components/home/HeroSection.jsx b/src/components/home/HeroSection.jsx new file mode 100644 index 0000000..71db4cf --- /dev/null +++ b/src/components/home/HeroSection.jsx @@ -0,0 +1,20 @@ +import React from 'react'; + +const HeroSection = () => { + return ( +
+
+
+

+ Campfire мнеие +

+
+
+ + {/* Декоративный градиент */} +
+
+ ); +}; + +export default HeroSection; \ No newline at end of file diff --git a/src/components/home/HomeContent.jsx b/src/components/home/HomeContent.jsx new file mode 100644 index 0000000..f248fdf --- /dev/null +++ b/src/components/home/HomeContent.jsx @@ -0,0 +1,40 @@ +import TiltedCard from '../components/reactbits/Components/TiltedCard/TiltedCard'; + +const HomeContent = () => { + return ( +
+ {/* Hero Section с наложением на Grid */} +
+
+
+

+ Составляй рецензии на +

+
+ +
+
+
+
+ + {/* Grid начинается с верха страницы */} +
+
+ {mediaItems.map((item) => ( +
+ +
+ ))} +
+
+
+ ); +}; + +export default HomeContent; \ No newline at end of file diff --git a/src/components/home/StatsSection.jsx b/src/components/home/StatsSection.jsx new file mode 100644 index 0000000..75fca42 --- /dev/null +++ b/src/components/home/StatsSection.jsx @@ -0,0 +1,87 @@ +import React, { useState, useEffect } from 'react'; +import CountUp from '../reactbits/TextAnimations/CountUp/CountUp'; +import RotatingText from '../reactbits/TextAnimations/RotatingText/RotatingText'; +import { getMediaCount, getReviewsCount } from '../../services/pocketbaseService'; + +const StatsSection = () => { + const [stats, setStats] = useState({ + mediaCount: 0, + reviewsCount: 0 + }); + + useEffect(() => { + const fetchStats = async () => { + try { + const [mediaCount, reviewsCount] = await Promise.all([ + getMediaCount(), + getReviewsCount() + ]); + + const mediaCountNum = parseInt(mediaCount) || 0; + const reviewsCountNum = parseInt(reviewsCount) || 0; + + setStats({ + mediaCount: mediaCountNum, + reviewsCount: reviewsCountNum + }); + } catch (error) { + console.error('Error fetching stats:', error); + setStats({ mediaCount: 0, reviewsCount: 0 }); + } + }; + + fetchStats(); + }, []); + + return ( +
+
+
+

+
+ Составляй рецензии на +
+ +
+
+

+
+ +
+
+

Медиа в каталоге

+ +
+ +
+

Рецензий написано

+ +
+
+
+
+ ); +}; + +export default StatsSection; \ No newline at end of file diff --git a/src/components/layout/AdminSidebar.jsx b/src/components/layout/AdminSidebar.jsx new file mode 100644 index 0000000..9eb1524 --- /dev/null +++ b/src/components/layout/AdminSidebar.jsx @@ -0,0 +1,94 @@ +import React from 'react'; +import { NavLink, useLocation } from 'react-router-dom'; +import { FaTachometerAlt, FaFilm, FaUsers, FaHeadset, FaTrophy, FaLightbulb } from 'react-icons/fa'; + +const AdminSidebar = () => { + const location = useLocation(); + + const isActive = (path) => { + if (path === '/admin') { + return location.pathname === '/admin'; + } + return location.pathname.startsWith(path); + }; + + return ( +
+ +
+ ); +}; + +export default AdminSidebar; \ No newline at end of file diff --git a/src/components/layout/Footer.jsx b/src/components/layout/Footer.jsx new file mode 100644 index 0000000..061bc94 --- /dev/null +++ b/src/components/layout/Footer.jsx @@ -0,0 +1,198 @@ +import { useState, useEffect } from 'react'; +import { FaDiscord, FaTelegramPlane, FaFire } from "react-icons/fa"; +import { + RiOpenaiFill, + RiGeminiLine, + RiStackOverflowLine, +} from "react-icons/ri"; +import { Link } from "react-router-dom"; + +function Footer() { + const currentYear = new Date().getFullYear(); + + // State for managing logo source + const [logoSrc, setLogoSrc] = useState('/logo.png'); + const [showTextLogo, setShowTextLogo] = useState(false); + + // Effect to check local logo availability on mount + useEffect(() => { + const img = new Image(); + img.onload = () => setLogoSrc('/logo.png'); + img.onerror = () => setLogoSrc('https://campfiregg.ru/logo.png'); + img.src = '/logo.png'; + }, []); + + const handleExternalLogoError = () => { + setShowTextLogo(true); + }; + + return ( +
+
+
+ {/* Brand */} +
+ + {showTextLogo ? ( + CampFire мнеие + ) : ( + CampFire мнеие + )} + +

+ Делаем хорошо, но на отъебись. +

+ +
+ + {/* Navigation */} +
+

Атлас

+
    +
  • + + Главная + +
  • +
  • + + Фильмы + +
  • +
  • + + Сериалы + +
  • +
  • + + Игры + +
  • +
  • + + Аниме + +
  • +
+
+ + {/* Legal */} +
+

Правовая информация

+
    +
  • + + Политика конфиденциальности + +
  • +
  • + + Условия использования + +
  • +
  • + + Пользовательское соглашение + +
  • +
+
+ + {/* Contact */} +
+

Контакты

+
    +
  • + general@campfiregg.ru +
  • +
  • + + Связаться с нами + +
  • +
  • + + FAQ + +
  • +
+
+
+ +
+

+ © {currentYear} CampFire мнеие. Никакие права не защищены. +

+

+ + VibeCoded with + + + + +

+
+
+
+ ); +} + +export default Footer; diff --git a/src/components/layout/Header.jsx b/src/components/layout/Header.jsx new file mode 100644 index 0000000..b2142f8 --- /dev/null +++ b/src/components/layout/Header.jsx @@ -0,0 +1,390 @@ +import React, { useState, useRef, useEffect } from 'react'; +import { Link, NavLink, useNavigate, useLocation } from 'react-router-dom'; +import { useAuth } from '../../contexts/AuthContext'; +import { useProfileActions } from '../../contexts/ProfileActionsContext'; +import { getFileUrl } from '../../services/pocketbaseService'; +import { FaUserCircle, FaSignOutAlt, FaEdit, FaBars, FaTimes, FaTachometerAlt, FaBook, FaTrophy, FaCog, FaHeadset, FaHome, FaUser } from 'react-icons/fa'; // Import FaBook, FaTrophy, FaCog, and FaHeadset +import { FiSearch } from 'react-icons/fi'; +import SearchBar from '../ui/SearchBar'; +import AlphaBadge from '../ui/AlphaBadge'; +import localLogo from '/logo.png'; +import { canManageSystem } from '../../utils/permissions'; +import Dock from '../reactbits/Components/Dock/Dock'; +import '../reactbits/Components/Dock/Dock.css'; +import { AnimatePresence, motion } from 'framer-motion'; + +const Header = () => { + const { user, userProfile, signOut } = useAuth(); + const profileActions = useProfileActions(); + const navigate = useNavigate(); + const location = useLocation(); + const [isProfileMenuOpen, setIsProfileMenuOpen] = useState(false); + const [isMobileMenuOpen, setIsMobileMenu] = useState(false); + const [isSearchOpen, setIsSearchOpen] = useState(false); + const [logoSrc, setLogoSrc] = useState(localLogo); + const profileMenuRef = useRef(null); + const mobileMenuRef = useRef(null); + const searchContainerRef = useRef(null); // Ref for the search dropdown container + + // Эффект для проверки доступности локального логотипа при монтировании + useEffect(() => { + const img = new Image(); + img.onload = () => setLogoSrc(localLogo); // Если локальный загрузился, используем его + img.onerror = () => setLogoSrc('https://campfiregg.ru/logo.png'); // Если локальный не загрузился, пробуем внешний + img.src = localLogo; + }, []); // Запускаем только один раз при монтировании + + const handleExternalLogoError = () => { + console.log("External logo failed to load."); + }; + + const toggleProfileMenu = () => { + setIsProfileMenuOpen(!isProfileMenuOpen); + setIsMobileMenu(false); // Close mobile menu if profile opens + setIsSearchOpen(false); // Close search if profile opens + }; + + const toggleMobileMenu = () => { + setIsMobileMenu(!isMobileMenuOpen); + setIsProfileMenuOpen(false); // Close profile menu if mobile opens + setIsSearchOpen(false); // Close search if mobile opens + }; + + const toggleSearch = () => { + setIsSearchOpen(!isSearchOpen); + setIsProfileMenuOpen(false); // Close other menus + setIsMobileMenu(false); // Close other menus + }; + + const closeMenus = () => { + setIsProfileMenuOpen(false); + setIsMobileMenu(false); + // Keep search open if it was opened via its own button, close only if clicking outside + }; + + const closeSearch = () => { + setIsSearchOpen(false); + }; + + // Close menus/search when clicking outside + useEffect(() => { + const handleClickOutside = (event) => { + if (profileMenuRef.current && !profileMenuRef.current.contains(event.target)) { + setIsProfileMenuOpen(false); + } + if (mobileMenuRef.current && !mobileMenuRef.current.contains(event.target)) { + setIsMobileMenu(false); + } + // Close search only if clicking outside the search container itself + if (searchContainerRef.current && !searchContainerRef.current.contains(event.target)) { + // Check if the click was NOT on the search toggle button + const searchButton = document.getElementById('search-toggle-button'); + if (searchButton && searchButton !== event.target && !searchButton.contains(event.target)) { + setIsSearchOpen(false); + } + } + }; + + document.addEventListener('mousedown', handleClickOutside); + return () => { + document.removeEventListener('mousedown', handleClickOutside); + }; + }, []); + + // Handle Edit Profile click from Header + const handleEditProfileClick = () => { + if (userProfile) { + console.log('Header: Edit Profile clicked. Navigating to profile and triggering modal.'); + navigate(`/profile/${userProfile.username}`); + if (profileActions?.triggerEditModal) { + profileActions.triggerEditModal(); + } + closeMenus(); + } else { + console.log('Header: Edit Profile clicked, but userProfile is not available.'); + } + }; + + const canAccessAdmin = canManageSystem(user); + + const mainItems = [ + { + icon: , + label: 'Главная', + onClick: () => navigate('/'), + className: location.pathname === '/' ? 'bg-campfire-amber/20' : '' + }, + { + icon: , + label: 'Каталог', + onClick: () => navigate('/catalog'), + className: location.pathname === '/catalog' ? 'bg-campfire-amber/20' : '' + }, + { + icon: , + label: 'Рейтинги', + onClick: () => navigate('/rating'), + className: location.pathname === '/rating' ? 'bg-campfire-amber/20' : '' + }, + { + icon: , + label: 'Поиск', + onClick: toggleSearch, + className: isSearchOpen ? 'bg-campfire-amber/20' : '' + } + ]; + + const profileItems = [ + { + icon: ( +
+ {userProfile?.username + {userProfile?.username || 'Профиль'} +
+ ), + onClick: () => navigate(userProfile ? `/profile/${userProfile.username}` : '/auth/login'), + className: location.pathname.startsWith('/profile') ? 'bg-campfire-amber/20' : '', + showLabel: false, + isRectangular: true + }, + { + icon: , + label: 'Меню', + onClick: toggleProfileMenu, + className: isProfileMenuOpen ? 'bg-campfire-amber/20' : '' + } + ]; + + return ( + <> +
+
+
+ {/* Logo */} + + CampFire мнение + + + {/* Main Dock Navigation */} +
+
+ + + {/* Search Dock */} + + {isSearchOpen && ( + +
+ +
+
+ )} +
+
+
+ + {/* Profile Dock */} +
+ + + {/* Profile Dropdown Menu */} + + {isProfileMenuOpen && ( + + + + Мой профиль + + {canAccessAdmin && ( + + + Админ панель + + )} + {userProfile && ( + + )} + {user && ( + <> + + + Поддержка + + + + )} + + )} + +
+ + {/* Mobile Menu Button */} +
+ +
+
+
+ + {/* Search Results Dropdown */} + + {isSearchOpen && ( + +
+ {/* Здесь будет компонент с результатами поиска */} +
+
+ )} +
+ + {/* Mobile Menu */} + + {isMobileMenuOpen && ( + +
+ + `block px-4 py-2 hover:bg-campfire-ash/20 transition-colors ${isActive ? 'text-campfire-amber' : 'text-campfire-light'} flex items-center space-x-2` + } + > + + Каталог + + + `block px-4 py-2 hover:bg-campfire-ash/20 transition-colors ${isActive ? 'text-campfire-amber' : 'text-campfire-light'} flex items-center space-x-2` + } + > + + Рейтинги + + {user && ( + + )} +
+
+ )} +
+
+ + {/* Мобильный Dock */} + +
+ +
+
+ + {/* Alpha Badge */} + + + ); +}; + +export default Header; \ No newline at end of file diff --git a/src/components/layout/Layout.jsx b/src/components/layout/Layout.jsx new file mode 100644 index 0000000..337d557 --- /dev/null +++ b/src/components/layout/Layout.jsx @@ -0,0 +1,34 @@ +import { Outlet } from 'react-router-dom'; +import Header from './Header'; +import Footer from './Footer'; +import { useAuth } from '../../contexts/AuthContext'; // Import useAuth + +console.log('Layout.jsx: Rendering Layout component'); // Add logging + +function Layout() { + const { isInitialized } = useAuth(); // Use isInitialized from auth context + + // Optionally render a loading state if auth is not initialized + if (!isInitialized) { + console.log('Layout.jsx: Auth not initialized, rendering loading state.'); // Add logging + return ( +
+
+
+ ); + } + + console.log('Layout.jsx: Auth initialized, rendering Header, Outlet, and Footer.'); // Add logging + return ( +
+
+ {/* Removed container-custom and mx-auto to allow content to stretch */} +
+ {/* Renders the matched route's component */} +
+
+
+ ); +} + +export default Layout; diff --git a/src/components/media/CustomMediaCarousel.jsx b/src/components/media/CustomMediaCarousel.jsx new file mode 100644 index 0000000..91ba297 --- /dev/null +++ b/src/components/media/CustomMediaCarousel.jsx @@ -0,0 +1,79 @@ +import React, { useRef, useEffect } from 'react'; +import TiltedCard from '../components/reactbits/Components/TiltedCard/TiltedCard'; + +// Custom Media Carousel component with mouse scroll and auto-scroll +const CustomMediaCarousel = ({ media, userProfile }) => { + const carouselRef = useRef(null); + const scrollIntervalRef = useRef(null); + + // Auto-scroll logic + useEffect(() => { + const carousel = carouselRef.current; + if (!carousel) return; + + // Start auto-scroll + scrollIntervalRef.current = setInterval(() => { + // Scroll by the width of one card (approx) + const cardWidth = carousel.querySelector('.flex-shrink-0')?.offsetWidth || 256; // Default width if no card found + const currentScroll = carousel.scrollLeft; + const maxScroll = carousel.scrollWidth - carousel.clientWidth; + + if (currentScroll >= maxScroll) { + // If at the end, jump back to the beginning smoothly + carousel.scrollTo({ left: 0, behavior: 'smooth' }); + } else { + // Scroll forward + carousel.scrollBy({ left: cardWidth + 16, behavior: 'smooth' }); // Add gap (16px = space-x-4) + } + }, 3000); // Scroll every 3 seconds + + // Clear interval on user interaction (mouse wheel, drag) + const handleUserInteraction = () => { + clearInterval(scrollIntervalRef.current); + }; + + carousel.addEventListener('wheel', handleUserInteraction); + // Add more event listeners for drag/touch if needed, but wheel covers mouse scroll + + // Clean up interval and event listeners on component unmount + return () => { + clearInterval(scrollIntervalRef.current); + carousel.removeEventListener('wheel', handleUserInteraction); + }; + }, [media]); // Re-run effect if media changes + + if (!media || media.length === 0) { + return
Нет контента для отображения.
; + } + + return ( + // Use flexbox for horizontal layout and overflow-x-auto for scrolling + // Add padding-bottom to prevent scrollbar from covering content + // Added custom scrollbar styles via Tailwind config (if configured) or direct CSS + // Added ref for scrolling +
+ {media.map((mediaItem) => ( + // Wrap MediaCard in a div with flex-shrink-0 to prevent shrinking + // Set a fixed width for each card in the carousel +
+ +
+ ))} +
+ ); +}; + +export default CustomMediaCarousel; diff --git a/src/components/media/MediaCard.jsx b/src/components/media/MediaCard.jsx new file mode 100644 index 0000000..4a7adb2 --- /dev/null +++ b/src/components/media/MediaCard.jsx @@ -0,0 +1,89 @@ +import { Link } from 'react-router-dom'; +import { FaFire } from 'react-icons/fa'; // Changed FaStar to FaFire +import { getFileUrl } from '../../services/pocketbaseService'; // Corrected import path +import { useEffect } from 'react'; // Import useEffect for logging +import { useAuth } from '../../contexts/AuthContext'; // Import useAuth + +// MediaCard component displays a card for a media item. +// It now accepts averageRating and reviewCount props from the parent. +// Added userProfile prop to check admin/critic role +function MediaCard({ media, userProfile }) { + // Destructure media properties, including the new ones from Supabase + // Use 'path' for the link instead of 'id' + // Используем 'poster' вместо 'poster_url' для имени файла + const { id, title, poster, average_rating, review_count, release_date, type, path, is_published } = media; // Added is_published + + // Используем getFileUrl для получения URL постера + const posterUrl = getFileUrl(media, 'poster'); // Используем имя поля 'poster' + const releaseDate = release_date; + const averageRating = average_rating; // This is the 10-point average from Supabase + const reviewCount = review_count; + + // Use the 'path' field for the link + const mediaLink = path ? `/media/${path}` : `/media/${id}`; // Fallback to id if path is missing (for old data) + + // Check if the current user is admin or critic + const isAdminOrCritic = userProfile && (userProfile.role === 'admin' || userProfile.is_critic === true); + + + // Add log to check average_rating and review_count here when component mounts or media prop changes + useEffect(() => { + console.log(`MediaCard for "${title}" rendered with media prop:`, media); + console.log(`MediaCard for "${title}" stats:`, { + average_rating: averageRating, + review_count: reviewCount, + is_published: is_published + }); + }, [media]); + + + return ( + +
{/* Added relative for absolute positioning */} +
+ {/* Используем posterUrl, полученный через getFileUrl */} + {title} + {/* Display average rating and review count */} + {/* Проверяем, что average_rating не null и не undefined перед отображением */} + {/* Ensure average_rating is treated as a number */} + {average_rating !== null && average_rating !== undefined && !isNaN(parseFloat(average_rating)) && ( +
+ {/* Changed FaStar to FaFire */} + + {/* Форматируем average_rating до одной десятичной */} + {parseFloat(average_rating).toFixed(1)} / 10 + + {/* Optionally display review count */} + {/* {review_count !== null && review_count !== undefined && ( + ({review_count}) + )} */} +
+ )} + + {/* Admin/Critic mark for unpublished media */} + {isAdminOrCritic && !is_published && ( +
+ Не опубликовано +
+ )} + +
+
+

+ {title} +

+
+ {releaseDate ? new Date(releaseDate).getFullYear() : 'N/A'} +
+
+
+ + ); +} + +export default MediaCard; diff --git a/src/components/media/MediaCarousel.jsx b/src/components/media/MediaCarousel.jsx new file mode 100644 index 0000000..657c387 --- /dev/null +++ b/src/components/media/MediaCarousel.jsx @@ -0,0 +1,30 @@ +import React from 'react'; +import TiltedCard from '../components/reactbits/Components/TiltedCard/TiltedCard'; + +// Simple Media Carousel component +const MediaCarousel = ({ media, userProfile }) => { + if (!media || media.length === 0) { + return
Нет контента для отображения.
; + } + + return ( + // Use flexbox for horizontal layout and overflow-x-auto for scrolling + // Add padding-bottom to prevent scrollbar from covering content +
+ {media.map((mediaItem) => ( + // Wrap MediaCard in a div with flex-shrink-0 to prevent shrinking + // Set a fixed width for each card in the carousel +
{/* Adjust width as needed */} + +
+ ))} +
+ ); +}; + +export default MediaCarousel; diff --git a/src/components/navigation/ProfileMenu.jsx b/src/components/navigation/ProfileMenu.jsx new file mode 100644 index 0000000..4b1430c --- /dev/null +++ b/src/components/navigation/ProfileMenu.jsx @@ -0,0 +1,72 @@ +import React, { useEffect, useRef } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { getFileUrl } from '../../utils/fileUtils'; + +const ProfileMenu = ({ isOpen, onClose, user, onSignOut }) => { + const navigate = useNavigate(); + const menuRef = useRef(null); + + useEffect(() => { + const handleClickOutside = (event) => { + if (menuRef.current && !menuRef.current.contains(event.target)) { + onClose(); + } + }; + + if (isOpen) { + document.addEventListener('mousedown', handleClickOutside); + } + + return () => { + document.removeEventListener('mousedown', handleClickOutside); + }; + }, [isOpen, onClose]); + + if (!isOpen) return null; + + return ( +
+
+
+
+
+ {user.username} +
+

{user.username}

+

Уровень {user.level}

+
+
+
+
+ + +
+
+
+ ); +}; + +export default ProfileMenu; \ No newline at end of file diff --git a/src/components/profile/ProfileHeader.jsx b/src/components/profile/ProfileHeader.jsx new file mode 100644 index 0000000..424dcfa --- /dev/null +++ b/src/components/profile/ProfileHeader.jsx @@ -0,0 +1,37 @@ +import React from 'react'; +import { FaEdit } from 'react-icons/fa'; +import { useNavigate } from 'react-router-dom'; + +const ProfileHeader = ({ user, isOwnProfile }) => { + const navigate = useNavigate(); + + const handleEditClick = () => { + navigate('/profile/settings'); + }; + + return ( +
+
+ {user?.username} + {isOwnProfile && ( +
+ +
+ )} +
+

{user?.username}

+ {user?.bio && ( +

{user.bio}

+ )} +
+ ); +}; + +export default ProfileHeader; \ No newline at end of file diff --git a/src/components/reactbits/Animations/ClickSpark/ClickSpark.jsx b/src/components/reactbits/Animations/ClickSpark/ClickSpark.jsx new file mode 100644 index 0000000..6e6f279 --- /dev/null +++ b/src/components/reactbits/Animations/ClickSpark/ClickSpark.jsx @@ -0,0 +1,159 @@ +/* + Installed from https://reactbits.dev/tailwind/ +*/ + +import { useRef, useEffect, useCallback, forwardRef, useImperativeHandle } from "react"; + +const ClickSpark = forwardRef(({ + sparkColor = "#fff", + sparkSize = 10, + sparkRadius = 15, + sparkCount = 8, + duration = 400, + easing = "ease-out", + extraScale = 1.0, + children +}, ref) => { + const canvasRef = useRef(null); + const sparksRef = useRef([]); + const startTimeRef = useRef(null); + + useImperativeHandle(ref, () => ({ + addSpark: (x, y) => { + const canvas = canvasRef.current; + if (!canvas) return; + const rect = canvas.getBoundingClientRect(); + const sparkX = x - rect.left; + const sparkY = y - rect.top; + + const now = performance.now(); + const newSparks = Array.from({ length: sparkCount }, (_, i) => ({ + x: sparkX, + y: sparkY, + angle: (2 * Math.PI * i) / sparkCount, + startTime: now, + })); + + sparksRef.current.push(...newSparks); + } + })); + + useEffect(() => { + const canvas = canvasRef.current; + if (!canvas) return; + + const parent = canvas.parentElement; + if (!parent) return; + + let resizeTimeout; + + const resizeCanvas = () => { + const { width, height } = parent.getBoundingClientRect(); + if (canvas.width !== width || canvas.height !== height) { + canvas.width = width; + canvas.height = height; + } + }; + + const handleResize = () => { + clearTimeout(resizeTimeout); + resizeTimeout = setTimeout(resizeCanvas, 100); + }; + + const ro = new ResizeObserver(handleResize); + ro.observe(parent); + + resizeCanvas(); + + return () => { + ro.disconnect(); + clearTimeout(resizeTimeout); + }; + }, []); + + const easeFunc = useCallback( + (t) => { + switch (easing) { + case "linear": + return t; + case "ease-in": + return t * t; + case "ease-in-out": + return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t; + default: + return t * (2 - t); + } + }, + [easing] + ); + + useEffect(() => { + const canvas = canvasRef.current; + if (!canvas) return; + const ctx = canvas.getContext("2d"); + + let animationId; + + const draw = (timestamp) => { + if (!startTimeRef.current) { + startTimeRef.current = timestamp; + } + ctx.clearRect(0, 0, canvas.width, canvas.height); + + sparksRef.current = sparksRef.current.filter((spark) => { + const elapsed = timestamp - spark.startTime; + if (elapsed >= duration) { + return false; + } + + const progress = elapsed / duration; + const eased = easeFunc(progress); + + const distance = eased * sparkRadius * extraScale; + const lineLength = sparkSize * (1 - eased); + + const x1 = spark.x + distance * Math.cos(spark.angle); + const y1 = spark.y + distance * Math.sin(spark.angle); + const x2 = spark.x + (distance + lineLength) * Math.cos(spark.angle); + const y2 = spark.y + (distance + lineLength) * Math.sin(spark.angle); + + ctx.strokeStyle = sparkColor; + ctx.lineWidth = 2; + ctx.beginPath(); + ctx.moveTo(x1, y1); + ctx.lineTo(x2, y2); + ctx.stroke(); + + return true; + }); + + animationId = requestAnimationFrame(draw); + }; + + animationId = requestAnimationFrame(draw); + + return () => { + cancelAnimationFrame(animationId); + }; + }, [ + sparkColor, + sparkSize, + sparkRadius, + sparkCount, + duration, + easeFunc, + extraScale, + ]); + + return ( +
+ + {children} +
+ ); +}); + +export default ClickSpark; \ No newline at end of file diff --git a/src/components/reactbits/Backgrounds/GridMotion/GridMotion.jsx b/src/components/reactbits/Backgrounds/GridMotion/GridMotion.jsx new file mode 100644 index 0000000..f6a9ed6 --- /dev/null +++ b/src/components/reactbits/Backgrounds/GridMotion/GridMotion.jsx @@ -0,0 +1,105 @@ +/* + Installed from https://reactbits.dev/tailwind/ +*/ + +import { useEffect, useRef } from 'react'; +import { gsap } from 'gsap'; + +const GridMotion = ({ items = [], gradientColor = 'black' }) => { + const gridRef = useRef(null); + const rowRefs = useRef([]); // Array of refs for each row + const mouseXRef = useRef(window.innerWidth / 2); + + // Ensure the grid has 28 items (4 rows x 7 columns) by default + const totalItems = 28; + const defaultItems = Array.from({ length: totalItems }, (_, index) => `Item ${index + 1}`); + const combinedItems = items.length > 0 ? items.slice(0, totalItems) : defaultItems; + + useEffect(() => { + gsap.ticker.lagSmoothing(0); + + const handleMouseMove = (e) => { + mouseXRef.current = e.clientX; + }; + + const updateMotion = () => { + const maxMoveAmount = 300; + const baseDuration = 0.8; // Base duration for inertia + const inertiaFactors = [0.6, 0.4, 0.3, 0.2]; // Different inertia for each row, outer rows slower + + rowRefs.current.forEach((row, index) => { + if (row) { + const direction = index % 2 === 0 ? 1 : -1; + const moveAmount = ((mouseXRef.current / window.innerWidth) * maxMoveAmount - maxMoveAmount / 2) * direction; + + // Apply inertia and staggered stop + gsap.to(row, { + x: moveAmount, + duration: baseDuration + inertiaFactors[index % inertiaFactors.length], + ease: 'power3.out', + overwrite: 'auto', + }); + } + }); + }; + + const removeAnimationLoop = gsap.ticker.add(updateMotion); + window.addEventListener('mousemove', handleMouseMove); + + return () => { + window.removeEventListener('mousemove', handleMouseMove); + removeAnimationLoop(); + }; + }, []); + + return ( +
+
+ {/* Noise overlay */} +
+
+ {[...Array(4)].map((_, rowIndex) => ( +
(rowRefs.current[rowIndex] = el)} + > + {[...Array(7)].map((_, itemIndex) => { + const content = combinedItems[rowIndex * 7 + itemIndex]; + return ( +
+
+ {typeof content === 'string' && content.startsWith('http') ? ( +
+ ) : ( +
{content}
+ )} +
+
+ ); + })} +
+ ))} +
+
+
+
+ ); +}; + +export default GridMotion; diff --git a/src/components/reactbits/Components/Dock/Dock.css b/src/components/reactbits/Components/Dock/Dock.css new file mode 100644 index 0000000..57c41e3 --- /dev/null +++ b/src/components/reactbits/Components/Dock/Dock.css @@ -0,0 +1,44 @@ +.dock-item { + aspect-ratio: 1; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + border-radius: 0.25rem; + transition: all 0.2s ease-in-out; + background-color: rgba(23, 23, 23, 0.5); + backdrop-filter: blur(8px); +} + +.dock-item:hover { + transform: scale(1.1); + background-color: rgba(251, 146, 60, 0.1); +} + +.dock-item.active { + background-color: rgba(251, 146, 60, 0.2); +} + +.dock-item-icon { + width: 1.5rem; + height: 1.5rem; + display: flex; + align-items: center; + justify-content: center; +} + +.dock-item-label { + font-size: 0.7rem; + margin-top: 0.25rem; + text-align: center; + color: #e5e5e5; + opacity: 0.8; +} + +.dock-container { + background: rgba(23, 23, 23, 0.3); + backdrop-filter: blur(8px); + border-radius: 0.5rem; + padding: 0.5rem; + box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); +} \ No newline at end of file diff --git a/src/components/reactbits/Components/Dock/Dock.jsx b/src/components/reactbits/Components/Dock/Dock.jsx new file mode 100644 index 0000000..c595a6b --- /dev/null +++ b/src/components/reactbits/Components/Dock/Dock.jsx @@ -0,0 +1,185 @@ +/* + Installed from https://reactbits.dev/tailwind/ +*/ + +"use client"; + +import { + motion, + useMotionValue, + useSpring, + useTransform, + AnimatePresence, +} from "framer-motion"; +import { + Children, + cloneElement, + useEffect, + useMemo, + useRef, + useState, +} from "react"; + +function DockItem({ + children, + className = "", + onClick, + mouseX, + spring, + distance, + magnification, + baseItemSize, + showLabel = true, + isRectangular = false, +}) { + const ref = useRef(null); + const isHovered = useMotionValue(0); + + const mouseDistance = useTransform(mouseX, (val) => { + const rect = ref.current?.getBoundingClientRect() ?? { + x: 0, + width: baseItemSize, + }; + return val - rect.x - baseItemSize / 2; + }); + + const targetSize = useTransform( + mouseDistance, + [-distance, 0, distance], + [baseItemSize, magnification, baseItemSize] + ); + const size = useSpring(targetSize, spring); + + return ( +
+ isHovered.set(1)} + onHoverEnd={() => isHovered.set(0)} + onFocus={() => isHovered.set(1)} + onBlur={() => isHovered.set(0)} + onClick={onClick} + className={`relative inline-flex items-center justify-center rounded-md bg-[#060606] border-neutral-700 border-2 shadow-md ${className}`} + tabIndex={0} + role="button" + aria-haspopup="true" + > + {Children.map(children, (child) => + cloneElement(child, { isHovered, showLabel }) + )} + +
+ ); +} + +function DockLabel({ children, className = "", showLabel = true, ...rest }) { + const { isHovered } = rest; + const [isVisible, setIsVisible] = useState(false); + + useEffect(() => { + const unsubscribe = isHovered.on("change", (latest) => { + setIsVisible(latest === 1); + }); + return () => unsubscribe(); + }, [isHovered]); + + if (!showLabel) { + return ( + + {children} + + ); + } + + return ( + + {isVisible && ( + + {children} + + )} + + ); +} + +function DockIcon({ children, className = "" }) { + return ( +
+ {children} +
+ ); +} + +export default function Dock({ + items, + className = "", + spring = { mass: 0.1, stiffness: 150, damping: 12 }, + magnification = 70, + distance = 200, + panelHeight = 64, + dockHeight = 256, + baseItemSize = 50, +}) { + const mouseX = useMotionValue(Infinity); + const isHovered = useMotionValue(0); + + const maxHeight = useMemo( + () => Math.max(dockHeight, magnification + magnification / 2 + 4), + [magnification, dockHeight] + ); + const heightRow = useTransform(isHovered, [0, 1], [panelHeight, maxHeight]); + const height = useSpring(heightRow, spring); + + return ( + + { + isHovered.set(1); + mouseX.set(pageX); + }} + onMouseLeave={() => { + isHovered.set(0); + mouseX.set(Infinity); + }} + className={`${className} flex items-end w-fit gap-4 rounded-2xl border-neutral-700 border-2 pb-2 px-4`} + style={{ height: panelHeight }} + role="toolbar" + aria-label="Application dock" + > + {items.map((item, index) => ( + + {item.icon} + {item.label} + + ))} + + + ); +} diff --git a/src/components/reactbits/Components/Stepper/Stepper.jsx b/src/components/reactbits/Components/Stepper/Stepper.jsx new file mode 100644 index 0000000..71d8786 --- /dev/null +++ b/src/components/reactbits/Components/Stepper/Stepper.jsx @@ -0,0 +1,280 @@ +/* + Installed from https://reactbits.dev/tailwind/ +*/ + +import React, { useState, Children, useRef, useLayoutEffect } from "react"; +import { motion, AnimatePresence } from "framer-motion"; + +export default function Stepper({ + children, + initialStep = 1, + onStepChange = () => { }, + onFinalStepCompleted = () => { }, + stepCircleContainerClassName = "", + stepContainerClassName = "", + contentClassName = "", + footerClassName = "", + backButtonProps = {}, + nextButtonProps = {}, + backButtonText = "Back", + nextButtonText = "Continue", + disableStepIndicators = false, + renderStepIndicator, + ...rest +}) { + const [currentStep, setCurrentStep] = useState(initialStep); + const [direction, setDirection] = useState(0); + const stepsArray = Children.toArray(children); + const totalSteps = stepsArray.length; + const isCompleted = currentStep > totalSteps; + const isLastStep = currentStep === totalSteps; + + const updateStep = (newStep) => { + setCurrentStep(newStep); + if (newStep > totalSteps) onFinalStepCompleted(); + else onStepChange(newStep); + }; + + const handleBack = () => { + if (currentStep > 1) { + setDirection(-1); + updateStep(currentStep - 1); + } + }; + + const handleNext = () => { + if (!isLastStep) { + setDirection(1); + updateStep(currentStep + 1); + } + }; + + const handleComplete = () => { + setDirection(1); + updateStep(totalSteps + 1); + }; + + return ( +
+
+
+ {stepsArray.map((_, index) => { + const stepNumber = index + 1; + const isNotLastStep = index < totalSteps - 1; + return ( + + {renderStepIndicator ? ( + renderStepIndicator({ + step: stepNumber, + currentStep, + onStepClick: (clicked) => { + setDirection(clicked > currentStep ? 1 : -1); + updateStep(clicked); + }, + }) + ) : ( + { + setDirection(clicked > currentStep ? 1 : -1); + updateStep(clicked); + }} + /> + )} + {isNotLastStep && ( + stepNumber} /> + )} + + ); + })} +
+ + {stepsArray[currentStep - 1]} + + {!isCompleted && ( +
+
+ {currentStep !== 1 && ( + + )} + +
+
+ )} +
+
+ ); +} + +function StepContentWrapper({ isCompleted, currentStep, direction, children, className }) { + const [parentHeight, setParentHeight] = useState(0); + + return ( + + + {!isCompleted && ( + setParentHeight(h)} + > + {children} + + )} + + + ); +} + +function SlideTransition({ children, direction, onHeightReady }) { + const containerRef = useRef(null); + + useLayoutEffect(() => { + if (containerRef.current) onHeightReady(containerRef.current.offsetHeight); + }, [children, onHeightReady]); + + return ( + + {children} + + ); +} + +const stepVariants = { + enter: (dir) => ({ + x: dir >= 0 ? "-100%" : "100%", + opacity: 0, + }), + center: { + x: "0%", + opacity: 1, + }, + exit: (dir) => ({ + x: dir >= 0 ? "50%" : "-50%", + opacity: 0, + }), +}; + +export function Step({ children }) { + return
{children}
; +} + +function StepIndicator({ step, currentStep, onClickStep, disableStepIndicators }) { + const status = currentStep === step ? "active" : currentStep < step ? "inactive" : "complete"; + + const handleClick = () => { + if (step !== currentStep && !disableStepIndicators) onClickStep(step); + }; + + return ( + + + {status === "complete" ? ( + + ) : status === "active" ? ( +
+ ) : ( + {step} + )} + + + ); +} + +function StepConnector({ isComplete }) { + const lineVariants = { + incomplete: { width: 0, backgroundColor: "transparent" }, + complete: { width: "100%", backgroundColor: "#00d8ff" }, + }; + + return ( +
+ +
+ ); +} + +function CheckIcon(props) { + return ( + + + + ); +} diff --git a/src/components/reactbits/Components/TiltedCard/TiltedCard.jsx b/src/components/reactbits/Components/TiltedCard/TiltedCard.jsx new file mode 100644 index 0000000..4b66324 --- /dev/null +++ b/src/components/reactbits/Components/TiltedCard/TiltedCard.jsx @@ -0,0 +1,232 @@ +/* + Installed from https://reactbits.dev/tailwind/ +*/ + +import { useRef, useState, useEffect } from "react"; +import { motion, useMotionValue, useSpring } from "framer-motion"; +import { FaFire } from "react-icons/fa"; + +const springValues = { + damping: 30, + stiffness: 100, + mass: 2, +}; + +export default function TiltedCard({ + imageSrc, + altText = "Tilted card image", + captionText = "", + containerHeight = "300px", + containerWidth = "100%", + imageHeight = "300px", + imageWidth = "300px", + scaleOnHover = 1.1, + rotateAmplitude = 14, + showMobileWarning = true, + showTooltip = true, + overlayContent = null, + displayOverlayContent = false, + rating = null, + releaseDate = null +}) { + const ref = useRef(null); + const [isDark, setIsDark] = useState(true); + const x = useMotionValue(0); + const y = useMotionValue(0); + const rotateX = useSpring(useMotionValue(0), springValues); + const rotateY = useSpring(useMotionValue(0), springValues); + const scale = useSpring(1, springValues); + const opacity = useSpring(0); + const rotateFigcaption = useSpring(0, { + stiffness: 350, + damping: 30, + mass: 1, + }); + + const [lastY, setLastY] = useState(0); + + useEffect(() => { + const img = new Image(); + img.crossOrigin = "anonymous"; + img.src = imageSrc; + + img.onload = () => { + // Создаем временный canvas для анализа цвета + const canvas = document.createElement('canvas'); + const ctx = canvas.getContext('2d'); + + // Устанавливаем размер canvas равным размеру изображения + canvas.width = img.width; + canvas.height = img.height; + + // Рисуем изображение + ctx.drawImage(img, 0, 0); + + try { + // Получаем данные только из нижней трети изображения + const bottomThird = Math.floor(img.height * 0.66); + const imageData = ctx.getImageData(0, bottomThird, img.width, img.height - bottomThird); + const data = imageData.data; + + let r = 0, g = 0, b = 0; + let count = 0; + + // Анализируем каждый 4-й пиксель для производительности + for (let i = 0; i < data.length; i += 16) { + r += data[i]; + g += data[i + 1]; + b += data[i + 2]; + count++; + } + + // Вычисляем средний цвет + r = Math.floor(r / count); + g = Math.floor(g / count); + b = Math.floor(b / count); + + // Вычисляем яркость по формуле + const brightness = (r * 0.299 + g * 0.587 + b * 0.114); + + // Если яркость больше 128, считаем фон светлым + setIsDark(brightness < 128); + } catch (error) { + // В случае ошибки CORS, используем темный фон по умолчанию + console.warn('Не удалось определить яркость изображения:', error); + setIsDark(true); + } + }; + + img.onerror = () => { + // В случае ошибки загрузки изображения, используем темный фон + setIsDark(true); + }; + }, [imageSrc]); + + function handleMouse(e) { + if (!ref.current) return; + + const rect = ref.current.getBoundingClientRect(); + const offsetX = e.clientX - rect.left - rect.width / 2; + const offsetY = e.clientY - rect.top - rect.height / 2; + + const rotationX = (offsetY / (rect.height / 2)) * -rotateAmplitude; + const rotationY = (offsetX / (rect.width / 2)) * rotateAmplitude; + + rotateX.set(rotationX); + rotateY.set(rotationY); + + x.set(e.clientX - rect.left); + y.set(e.clientY - rect.top); + + const velocityY = offsetY - lastY; + rotateFigcaption.set(-velocityY * 0.6); + setLastY(offsetY); + } + + function handleMouseEnter() { + scale.set(scaleOnHover); + opacity.set(1); + } + + function handleMouseLeave() { + opacity.set(0); + scale.set(1); + rotateX.set(0); + rotateY.set(0); + rotateFigcaption.set(0); + } + + const formatYear = (dateString) => { + if (!dateString) return ''; + return new Date(dateString).getFullYear(); + }; + + return ( +
+ {showMobileWarning && ( +
+ This effect is not optimized for mobile. Check on desktop. +
+ )} + + + + + {displayOverlayContent && overlayContent && ( + + {overlayContent} + + )} + + {rating !== null && rating !== undefined && ( + +
+ + {rating} +
+
+ )} + + +
+

+ {captionText} +

+ {releaseDate && ( +

+ {formatYear(releaseDate)} +

+ )} +
+
+
+ + {showTooltip && ( + + {captionText} + + )} +
+ ); +} diff --git a/src/components/reactbits/TextAnimations/CountUp/CountUp.jsx b/src/components/reactbits/TextAnimations/CountUp/CountUp.jsx new file mode 100644 index 0000000..2fa0f86 --- /dev/null +++ b/src/components/reactbits/TextAnimations/CountUp/CountUp.jsx @@ -0,0 +1,103 @@ +/* + Installed from https://reactbits.dev/tailwind/ +*/ + +import { useEffect, useRef } from "react"; +import { useInView, useMotionValue, useSpring } from "framer-motion"; + +export default function CountUp({ + to, + from = 0, + direction = "up", + delay = 0, + duration = 2, // Duration of the animation in seconds + className = "", + startWhen = true, + separator = "", + onStart, + onEnd, +}) { + const ref = useRef(null); + const motionValue = useMotionValue(direction === "down" ? to : from); + + // Calculate damping and stiffness based on duration + const damping = 20 + 40 * (1 / duration); // Adjust this formula for finer control + const stiffness = 100 * (1 / duration); // Adjust this formula for finer control + + const springValue = useSpring(motionValue, { + damping, + stiffness, + }); + + const isInView = useInView(ref, { once: true, margin: "0px" }); + + // Set initial text content to the initial value based on direction + useEffect(() => { + if (ref.current) { + ref.current.textContent = String(direction === "down" ? to : from); + } + }, [from, to, direction]); + + // Start the animation when in view and startWhen is true + useEffect(() => { + if (isInView && startWhen) { + if (typeof onStart === "function") { + onStart(); + } + + const timeoutId = setTimeout(() => { + motionValue.set(direction === "down" ? from : to); + }, delay * 1000); + + const durationTimeoutId = setTimeout( + () => { + if (typeof onEnd === "function") { + onEnd(); + } + }, + delay * 1000 + duration * 1000 + ); + + return () => { + clearTimeout(timeoutId); + clearTimeout(durationTimeoutId); + }; + } + }, [ + isInView, + startWhen, + motionValue, + direction, + from, + to, + delay, + onStart, + onEnd, + duration, + ]); + + // Update text content with formatted number on spring value change + useEffect(() => { + const unsubscribe = springValue.on("change", (latest) => { + if (ref.current) { + const options = { + useGrouping: !!separator, + minimumFractionDigits: 0, + maximumFractionDigits: 0, + }; + + const formattedNumber = Intl.NumberFormat("en-US", options).format( + latest.toFixed(0) + ); + + ref.current.textContent = separator + ? formattedNumber.replace(/,/g, separator) + : formattedNumber; + } + }); + + return () => unsubscribe(); + }, [springValue, separator]); + + return ; +} \ No newline at end of file diff --git a/src/components/reactbits/TextAnimations/FuzzyText/FuzzyText.jsx b/src/components/reactbits/TextAnimations/FuzzyText/FuzzyText.jsx new file mode 100644 index 0000000..bf9968a --- /dev/null +++ b/src/components/reactbits/TextAnimations/FuzzyText/FuzzyText.jsx @@ -0,0 +1,205 @@ +/* + Installed from https://reactbits.dev/tailwind/ +*/ + +import React, { useEffect, useRef } from "react"; + +const FuzzyText = ({ + children, + fontSize = "clamp(2rem, 10vw, 10rem)", + fontWeight = 900, + fontFamily = "inherit", + color = "#fff", + enableHover = true, + baseIntensity = 0.18, + hoverIntensity = 0.5, +}) => { + const canvasRef = useRef(null); + + useEffect(() => { + let animationFrameId; + let isCancelled = false; + const canvas = canvasRef.current; + if (!canvas) return; + + const init = async () => { + if (document.fonts?.ready) { + await document.fonts.ready; + } + if (isCancelled) return; + + const ctx = canvas.getContext("2d"); + if (!ctx) return; + + const computedFontFamily = + fontFamily === "inherit" + ? window.getComputedStyle(canvas).fontFamily || "sans-serif" + : fontFamily; + + const fontSizeStr = + typeof fontSize === "number" ? `${fontSize}px` : fontSize; + let numericFontSize; + if (typeof fontSize === "number") { + numericFontSize = fontSize; + } else { + const temp = document.createElement("span"); + temp.style.fontSize = fontSize; + document.body.appendChild(temp); + const computedSize = window.getComputedStyle(temp).fontSize; + numericFontSize = parseFloat(computedSize); + document.body.removeChild(temp); + } + + const text = React.Children.toArray(children).join(""); + + // Create offscreen canvas + const offscreen = document.createElement("canvas"); + const offCtx = offscreen.getContext("2d"); + if (!offCtx) return; + + offCtx.font = `${fontWeight} ${fontSizeStr} ${computedFontFamily}`; + offCtx.textBaseline = "alphabetic"; + const metrics = offCtx.measureText(text); + + const actualLeft = metrics.actualBoundingBoxLeft ?? 0; + const actualRight = metrics.actualBoundingBoxRight ?? metrics.width; + const actualAscent = metrics.actualBoundingBoxAscent ?? numericFontSize; + const actualDescent = + metrics.actualBoundingBoxDescent ?? numericFontSize * 0.2; + + const textBoundingWidth = Math.ceil(actualLeft + actualRight); + const tightHeight = Math.ceil(actualAscent + actualDescent); + + const extraWidthBuffer = 10; + const offscreenWidth = textBoundingWidth + extraWidthBuffer; + + offscreen.width = offscreenWidth; + offscreen.height = tightHeight; + + const xOffset = extraWidthBuffer / 2; + offCtx.font = `${fontWeight} ${fontSizeStr} ${computedFontFamily}`; + offCtx.textBaseline = "alphabetic"; + offCtx.fillStyle = color; + offCtx.fillText(text, xOffset - actualLeft, actualAscent); + + const horizontalMargin = 50; + const verticalMargin = 0; + canvas.width = offscreenWidth + horizontalMargin * 2; + canvas.height = tightHeight + verticalMargin * 2; + ctx.translate(horizontalMargin, verticalMargin); + + const interactiveLeft = horizontalMargin + xOffset; + const interactiveTop = verticalMargin; + const interactiveRight = interactiveLeft + textBoundingWidth; + const interactiveBottom = interactiveTop + tightHeight; + + let isHovering = false; + const fuzzRange = 30; + + const run = () => { + if (isCancelled) return; + ctx.clearRect( + -fuzzRange, + -fuzzRange, + offscreenWidth + 2 * fuzzRange, + tightHeight + 2 * fuzzRange + ); + const intensity = isHovering ? hoverIntensity : baseIntensity; + for (let j = 0; j < tightHeight; j++) { + const dx = Math.floor(intensity * (Math.random() - 0.5) * fuzzRange); + ctx.drawImage( + offscreen, + 0, + j, + offscreenWidth, + 1, + dx, + j, + offscreenWidth, + 1 + ); + } + animationFrameId = window.requestAnimationFrame(run); + }; + + run(); + + const isInsideTextArea = (x, y) => { + return ( + x >= interactiveLeft && + x <= interactiveRight && + y >= interactiveTop && + y <= interactiveBottom + ); + }; + + const handleMouseMove = (e) => { + if (!enableHover) return; + const rect = canvas.getBoundingClientRect(); + const x = e.clientX - rect.left; + const y = e.clientY - rect.top; + isHovering = isInsideTextArea(x, y); + }; + + const handleMouseLeave = () => { + isHovering = false; + }; + + const handleTouchMove = (e) => { + if (!enableHover) return; + e.preventDefault(); + const rect = canvas.getBoundingClientRect(); + const touch = e.touches[0]; + const x = touch.clientX - rect.left; + const y = touch.clientY - rect.top; + isHovering = isInsideTextArea(x, y); + }; + + const handleTouchEnd = () => { + isHovering = false; + }; + + if (enableHover) { + canvas.addEventListener("mousemove", handleMouseMove); + canvas.addEventListener("mouseleave", handleMouseLeave); + canvas.addEventListener("touchmove", handleTouchMove, { passive: false }); + canvas.addEventListener("touchend", handleTouchEnd); + } + + const cleanup = () => { + window.cancelAnimationFrame(animationFrameId); + if (enableHover) { + canvas.removeEventListener("mousemove", handleMouseMove); + canvas.removeEventListener("mouseleave", handleMouseLeave); + canvas.removeEventListener("touchmove", handleTouchMove); + canvas.removeEventListener("touchend", handleTouchEnd); + } + }; + + canvas.cleanupFuzzyText = cleanup; + }; + + init(); + + return () => { + isCancelled = true; + window.cancelAnimationFrame(animationFrameId); + if (canvas && canvas.cleanupFuzzyText) { + canvas.cleanupFuzzyText(); + } + }; + }, [ + children, + fontSize, + fontWeight, + fontFamily, + color, + enableHover, + baseIntensity, + hoverIntensity, + ]); + + return ; +}; + +export default FuzzyText; diff --git a/src/components/reactbits/TextAnimations/RotatingText/RotatingText.jsx b/src/components/reactbits/TextAnimations/RotatingText/RotatingText.jsx new file mode 100644 index 0000000..684a1ad --- /dev/null +++ b/src/components/reactbits/TextAnimations/RotatingText/RotatingText.jsx @@ -0,0 +1,222 @@ +/* + Installed from https://reactbits.dev/tailwind/ +*/ + +"use client"; + +import { + forwardRef, + useCallback, + useEffect, + useImperativeHandle, + useMemo, + useState, +} from "react"; +import { motion, AnimatePresence } from "framer-motion"; + +function cn(...classes) { + return classes.filter(Boolean).join(" "); +} + +const RotatingText = forwardRef((props, ref) => { + const { + texts, + transition = { type: "spring", damping: 25, stiffness: 300 }, + initial = { y: "100%", opacity: 0 }, + animate = { y: 0, opacity: 1 }, + exit = { y: "-120%", opacity: 0 }, + animatePresenceMode = "wait", + animatePresenceInitial = false, + rotationInterval = 2000, + staggerDuration = 0, + staggerFrom = "first", + loop = true, + auto = true, + splitBy = "characters", + onNext, + mainClassName, + splitLevelClassName, + elementLevelClassName, + ...rest + } = props; + + const [currentTextIndex, setCurrentTextIndex] = useState(0); + + const splitIntoCharacters = (text) => { + if (typeof Intl !== "undefined" && Intl.Segmenter) { + const segmenter = new Intl.Segmenter("en", { granularity: "grapheme" }); + return Array.from(segmenter.segment(text), (segment) => segment.segment); + } + return Array.from(text); + }; + + const elements = useMemo(() => { + const currentText = texts[currentTextIndex]; + if (splitBy === "characters") { + const words = currentText.split(" "); + return words.map((word, i) => ({ + characters: splitIntoCharacters(word), + needsSpace: i !== words.length - 1, + })); + } + if (splitBy === "words") { + return currentText.split(" ").map((word, i, arr) => ({ + characters: [word], + needsSpace: i !== arr.length - 1, + })); + } + if (splitBy === "lines") { + return currentText.split("\n").map((line, i, arr) => ({ + characters: [line], + needsSpace: i !== arr.length - 1, + })); + } + // For a custom separator + return currentText.split(splitBy).map((part, i, arr) => ({ + characters: [part], + needsSpace: i !== arr.length - 1, + })); + }, [texts, currentTextIndex, splitBy]); + + const getStaggerDelay = useCallback( + (index, totalChars) => { + const total = totalChars; + if (staggerFrom === "first") return index * staggerDuration; + if (staggerFrom === "last") return (total - 1 - index) * staggerDuration; + if (staggerFrom === "center") { + const center = Math.floor(total / 2); + return Math.abs(center - index) * staggerDuration; + } + if (staggerFrom === "random") { + const randomIndex = Math.floor(Math.random() * total); + return Math.abs(randomIndex - index) * staggerDuration; + } + return Math.abs(staggerFrom - index) * staggerDuration; + }, + [staggerFrom, staggerDuration] + ); + + const handleIndexChange = useCallback( + (newIndex) => { + setCurrentTextIndex(newIndex); + if (onNext) onNext(newIndex); + }, + [onNext] + ); + + const next = useCallback(() => { + const nextIndex = + currentTextIndex === texts.length - 1 + ? loop + ? 0 + : currentTextIndex + : currentTextIndex + 1; + if (nextIndex !== currentTextIndex) { + handleIndexChange(nextIndex); + } + }, [currentTextIndex, texts.length, loop, handleIndexChange]); + + const previous = useCallback(() => { + const prevIndex = + currentTextIndex === 0 + ? loop + ? texts.length - 1 + : currentTextIndex + : currentTextIndex - 1; + if (prevIndex !== currentTextIndex) { + handleIndexChange(prevIndex); + } + }, [currentTextIndex, texts.length, loop, handleIndexChange]); + + const jumpTo = useCallback( + (index) => { + const validIndex = Math.max(0, Math.min(index, texts.length - 1)); + if (validIndex !== currentTextIndex) { + handleIndexChange(validIndex); + } + }, + [texts.length, currentTextIndex, handleIndexChange] + ); + + const reset = useCallback(() => { + if (currentTextIndex !== 0) { + handleIndexChange(0); + } + }, [currentTextIndex, handleIndexChange]); + + useImperativeHandle( + ref, + () => ({ + next, + previous, + jumpTo, + reset, + }), + [next, previous, jumpTo, reset] + ); + + useEffect(() => { + if (!auto) return; + const intervalId = setInterval(next, rotationInterval); + return () => clearInterval(intervalId); + }, [next, rotationInterval, auto]); + + return ( + + {/* Screen-reader only text */} + {texts[currentTextIndex]} + + + + + ); +}); + +RotatingText.displayName = "RotatingText"; +export default RotatingText; diff --git a/src/components/reactbits/TextAnimations/ShinyText/ShinyText.jsx b/src/components/reactbits/TextAnimations/ShinyText/ShinyText.jsx new file mode 100644 index 0000000..226a56f --- /dev/null +++ b/src/components/reactbits/TextAnimations/ShinyText/ShinyText.jsx @@ -0,0 +1,41 @@ +/* + Installed from https://reactbits.dev/tailwind/ +*/ + +const ShinyText = ({ text, disabled = false, speed = 5, className = '' }) => { + const animationDuration = `${speed}s`; + + return ( +
+ {text} +
+ ); +}; + +export default ShinyText; + +// tailwind.config.js +// module.exports = { +// theme: { +// extend: { +// keyframes: { +// shine: { +// '0%': { 'background-position': '100%' }, +// '100%': { 'background-position': '-100%' }, +// }, +// }, +// animation: { +// shine: 'shine 5s linear infinite', +// }, +// }, +// }, +// plugins: [], +// }; \ No newline at end of file diff --git a/src/components/reactbits/TextAnimations/TrueFocus/TrueFocus.jsx b/src/components/reactbits/TextAnimations/TrueFocus/TrueFocus.jsx new file mode 100644 index 0000000..c189cb7 --- /dev/null +++ b/src/components/reactbits/TextAnimations/TrueFocus/TrueFocus.jsx @@ -0,0 +1,144 @@ +/* + Installed from https://reactbits.dev/tailwind/ +*/ + +import { useEffect, useRef, useState } from "react"; +import { motion } from "framer-motion"; + +const TrueFocus = ({ + sentence = "True Focus", + manualMode = false, + blurAmount = 5, + borderColor = "green", + glowColor = "rgba(0, 255, 0, 0.6)", + animationDuration = 0.5, + pauseBetweenAnimations = 1, +}) => { + const words = sentence.split(" "); + const [currentIndex, setCurrentIndex] = useState(0); + const [lastActiveIndex, setLastActiveIndex] = useState(null); + const containerRef = useRef(null); + const wordRefs = useRef([]); + const [focusRect, setFocusRect] = useState({ x: 0, y: 0, width: 0, height: 0 }); + + useEffect(() => { + if (!manualMode) { + const interval = setInterval(() => { + setCurrentIndex((prev) => (prev + 1) % words.length); + }, (animationDuration + pauseBetweenAnimations) * 1000); + + return () => clearInterval(interval); + } + }, [manualMode, animationDuration, pauseBetweenAnimations, words.length]); + + useEffect(() => { + if (currentIndex === null || currentIndex === -1) return; + if (!wordRefs.current[currentIndex] || !containerRef.current) return; + + const parentRect = containerRef.current.getBoundingClientRect(); + const activeRect = wordRefs.current[currentIndex].getBoundingClientRect(); + + setFocusRect({ + x: activeRect.left - parentRect.left, + y: activeRect.top - parentRect.top, + width: activeRect.width, + height: activeRect.height, + }); + }, [currentIndex, words.length]); + + const handleMouseEnter = (index) => { + if (manualMode) { + setLastActiveIndex(index); + setCurrentIndex(index); + } + }; + + const handleMouseLeave = () => { + if (manualMode) { + setCurrentIndex(lastActiveIndex); + } + }; + + return ( +
+ {words.map((word, index) => { + const isActive = index === currentIndex; + return ( + (wordRefs.current[index] = el)} + className="relative text-[3rem] font-black cursor-pointer" + style={{ + filter: manualMode + ? isActive + ? `blur(0px)` + : `blur(${blurAmount}px)` + : isActive + ? `blur(0px)` + : `blur(${blurAmount}px)`, + "--border-color": borderColor, + "--glow-color": glowColor, + transition: `filter ${animationDuration}s ease`, + }} + onMouseEnter={() => handleMouseEnter(index)} + onMouseLeave={handleMouseLeave} + > + {word} + + ); + })} + + = 0 ? 1 : 0, + }} + transition={{ + duration: animationDuration, + }} + style={{ + "--border-color": borderColor, + "--glow-color": glowColor, + }} + > + + + + + +
+ ); +}; + +export default TrueFocus; diff --git a/src/components/reviews/FlameRatingInput.jsx b/src/components/reviews/FlameRatingInput.jsx new file mode 100644 index 0000000..4f51698 --- /dev/null +++ b/src/components/reviews/FlameRatingInput.jsx @@ -0,0 +1,38 @@ +import React, { useState } from 'react'; +import { FaFire } from 'react-icons/fa'; + +function FlameRatingInput({ value, onChange }) { + const [hoverValue, setHoverValue] = useState(0); + + const handleMouseEnter = (rating) => { + setHoverValue(rating); + }; + + const handleMouseLeave = () => { + setHoverValue(0); + }; + + const handleClick = (rating) => { + onChange(rating); + }; + + return ( +
+ {[...Array(10)].map((_, index) => { + const rating = index + 1; + const isFilled = rating <= (hoverValue || value); // Use hoverValue if > 0, otherwise use actual value + + return ( + handleMouseEnter(rating)} + onClick={() => handleClick(rating)} + /> + ); + })} +
+ ); +} + +export default FlameRatingInput; diff --git a/src/components/reviews/LatestReviewsMarquee.jsx b/src/components/reviews/LatestReviewsMarquee.jsx new file mode 100644 index 0000000..b117be8 --- /dev/null +++ b/src/components/reviews/LatestReviewsMarquee.jsx @@ -0,0 +1,54 @@ +import React from 'react'; +import Marquee from 'react-fast-marquee'; +import { Link } from 'react-router-dom'; +import { FaFire } from 'react-icons/fa'; +import DOMPurify from 'dompurify'; // Import DOMPurify + +function LatestReviewsMarquee({ reviews }) { + if (!reviews || reviews.length === 0) { + return null; + } + + return ( +
+ + {reviews.map(review => { + const user = review.expand?.user_id; + const media = review.expand?.media_id; + + if (!user || !media || !user.username || !media.path || !media.title) { + console.warn(`Skipping review ${review.id} in marquee due to missing expanded user, media, or required fields.`); + return null; + } + + // Sanitize and strip HTML from the review content for the marquee + const cleanContent = DOMPurify.sanitize(review.content, { ALLOWED_TAGS: [] }); // Allow no HTML tags + + return ( + + + {user.username}: + + + "{cleanContent.substring(0, 50)}{cleanContent.length > 50 ? '...' : ''}" {/* Use cleanContent */} + + + + {review.overall_rating !== undefined && review.overall_rating !== null ? parseFloat(review.overall_rating).toFixed(1) : 'N/A'} / 10 + + + на "{media.title}" + + + ); + })} + +
+ ); +} + +export default LatestReviewsMarquee; diff --git a/src/components/reviews/LikeButton.jsx b/src/components/reviews/LikeButton.jsx new file mode 100644 index 0000000..67aa174 --- /dev/null +++ b/src/components/reviews/LikeButton.jsx @@ -0,0 +1,127 @@ +import React, { useState, useEffect } from 'react'; +import { FaHeart, FaRegHeart } from 'react-icons/fa'; +import { useAuth } from '../../contexts/AuthContext'; +import { pb } from '../../services/pocketbaseService'; +import { toast } from 'react-toastify'; + +const LikeButton = ({ reviewId, initialLikes = 0, onLikeChange, reviewOwnerId }) => { + const { user } = useAuth(); + const [isLiked, setIsLiked] = useState(false); + const [likesCount, setLikesCount] = useState(initialLikes); + const [isLoading, setIsLoading] = useState(false); + const [likedUsers, setLikedUsers] = useState([]); + const [showTooltip, setShowTooltip] = useState(false); + + useEffect(() => { + const checkLikeStatus = async () => { + if (!user) return; + + try { + // Получаем рецензию и проверяем, есть ли пользователь в списке лайков + const review = await pb.collection('reviews').getOne(reviewId); + const likes = review.likes || []; + setIsLiked(likes.includes(user.id)); + setLikesCount(likes.length); + + // Если пользователь - владелец рецензии и есть лайки, получаем информацию о пользователях + if (user.id === reviewOwnerId && likes.length > 0) { + // Получаем информацию о пользователях, которые поставили лайки + const users = await Promise.all( + likes.map(userId => + pb.collection('users').getOne(userId) + .catch(() => null) // Игнорируем ошибки для отдельных пользователей + ) + ); + // Фильтруем null значения и обновляем состояние + setLikedUsers(users.filter(Boolean)); + } + } catch (err) { + console.error('Ошибка при проверке статуса лайка:', err); + setIsLiked(false); + } + }; + + checkLikeStatus(); + }, [user, reviewId, reviewOwnerId]); + + const handleLike = async () => { + if (!user) { + toast.error('Необходимо войти в систему'); + return; + } + + setIsLoading(true); + try { + // Получаем текущую рецензию + const review = await pb.collection('reviews').getOne(reviewId); + const currentLikes = review.likes || []; + + let newLikes; + if (isLiked) { + // Удаляем лайк + newLikes = currentLikes.filter(id => id !== user.id); + } else { + // Добавляем лайк + newLikes = [...currentLikes, user.id]; + } + + // Обновляем рецензию с новым списком лайков + await pb.collection('reviews').update(reviewId, { + likes: newLikes + }); + + // Если пользователь - владелец рецензии, обновляем список лайкнувших + if (user.id === reviewOwnerId) { + const users = await Promise.all( + newLikes.map(userId => + pb.collection('users').getOne(userId) + .catch(() => null) + ) + ); + setLikedUsers(users.filter(Boolean)); + } + + setIsLiked(!isLiked); + setLikesCount(newLikes.length); + + if (onLikeChange) { + onLikeChange(!isLiked); + } + } catch (err) { + console.error('Ошибка при обработке лайка:', err); + toast.error('Не удалось обработать лайк'); + } finally { + setIsLoading(false); + } + }; + + return ( +
+ + + {/* Показываем список лайкнувших только владельцу рецензии при наведении */} + {user?.id === reviewOwnerId && likesCount > 0 && ( +
+
Лайкнули:
+ {likedUsers.map(user => ( +
+ {user.username} +
+ ))} +
+ )} +
+ ); +}; + +export default LikeButton; \ No newline at end of file diff --git a/src/components/reviews/RatingChart.jsx b/src/components/reviews/RatingChart.jsx new file mode 100644 index 0000000..0a2b2a7 --- /dev/null +++ b/src/components/reviews/RatingChart.jsx @@ -0,0 +1,141 @@ +import React from 'react'; +import { + Chart as ChartJS, + RadialLinearScale, + PointElement, + LineElement, + Filler, + Tooltip, + Legend, +} from 'chart.js'; +import { Radar } from 'react-chartjs-2'; + +ChartJS.register( + RadialLinearScale, + PointElement, + LineElement, + Filler, + Tooltip, + Legend +); + +// Define colors based on your theme +const chartColors = { + amber: '#FF9D00', // Campfire Amber + dark: '#1A202C', // Campfire Dark + ash: '#A0AEC0', // Campfire Ash + light: '#F7FAFC', // Campfire Light + charcoal: '#2D3748', // Campfire Charcoal +}; + + +function RatingChart({ ratings, labels, size = 'medium' }) { + // Ensure ratings and labels are valid objects + if (!ratings || typeof ratings !== 'object' || !labels || typeof labels !== 'object') { + return
Нет данных для графика.
; + } + + // Filter out ratings that are not numbers or are outside the 1-10 range + const validRatings = Object.entries(ratings) + .filter(([key, value]) => typeof value === 'number' && value >= 1 && value <= 10); + + // If no valid ratings, don't render the chart + if (validRatings.length === 0) { + return
Нет данных для графика.
; + } + + // Prepare data for the chart + const chartLabels = validRatings.map(([key]) => labels[key] || key); // Use label if available, otherwise key + const chartDataValues = validRatings.map(([key, value]) => value); + + const data = { + labels: chartLabels, + datasets: [ + { + label: 'Оценка', // Or 'Средняя оценка' depending on context + data: chartDataValues, + backgroundColor: `${chartColors.amber}40`, // Amber with transparency + borderColor: chartColors.amber, + pointBackgroundColor: chartColors.amber, + pointBorderColor: chartColors.light, + pointHoverBackgroundColor: chartColors.light, + pointHoverBorderColor: chartColors.amber, + borderWidth: 2, + fill: true, + }, + ], + }; + + const options = { + responsive: true, + maintainAspectRatio: size === 'small' ? false : true, // Allow small charts to not maintain aspect ratio + aspectRatio: size === 'small' ? 1 : (size === 'medium' ? 1.5 : 2), // Adjust aspect ratio based on size + scales: { + r: { + angleLines: { + display: true, + color: chartColors.ash + '40', // Ash with transparency + }, + grid: { + color: chartColors.ash + '40', // Ash with transparency + }, + pointLabels: { + color: chartColors.light, // Light text for labels + font: { + size: size === 'small' ? 8 : (size === 'medium' ? 10 : 12), // Adjust font size + } + }, + suggestedMin: 0, // Start from 0 + suggestedMax: 10, // Max rating is 10 + ticks: { + stepSize: 2, // Steps of 2 + color: chartColors.ash, // Ash color for ticks + backdropColor: chartColors.charcoal, // Background color for tick labels + font: { + size: size === 'small' ? 8 : (size === 'medium' ? 10 : 12), // Adjust font size + } + }, + }, + }, + plugins: { + legend: { + display: false, // Hide legend + }, + tooltip: { + callbacks: { + label: function(context) { + let label = context.dataset.label || ''; + if (label) { + label += ': '; + } + if (context.raw !== null) { + label += context.raw.toFixed(1); // Show rating with one decimal place + } + return label; + } + } + } + }, + elements: { + line: { + tension: 0.1 // Add some tension for smoother lines + } + } + }; + + // Adjust container size based on the 'size' prop + const containerClasses = size === 'small' + ? 'w-32 h-32' // Smaller size for individual review items + : size === 'medium' + ? 'w-48 h-48 md:w-64 md:h-64' // Medium size for form preview + : 'w-64 h-64 md:w-80 md:h-80 lg:w-96 lg:h-96'; // Large size for media page aggregate chart + + + return ( +
+ +
+ ); +} + +export default RatingChart; diff --git a/src/components/reviews/ReviewCard.jsx b/src/components/reviews/ReviewCard.jsx new file mode 100644 index 0000000..2be79e4 --- /dev/null +++ b/src/components/reviews/ReviewCard.jsx @@ -0,0 +1,137 @@ +import { useState } from 'react'; +import { Link } from 'react-router-dom'; +import { FaThumbsUp, FaComment, FaShare } from 'react-icons/fa'; +import RatingChart from './RatingChart'; + +// Accept characteristics prop +function ReviewCard({ review, isDetailed = false, characteristics }) { + const [isExpanded, setIsExpanded] = useState(false); + + const { + id, + users, // Changed from user to users + content, + ratings, + likes, + comments, + created_at, + spoiler + } = review; + + // Use users object and rename for clarity + const userProfile = users; + + // Format date + const formattedDate = new Date(created_at).toLocaleDateString('ru-RU', { + year: 'numeric', + month: 'short', + day: 'numeric' + }); + + // Calculate overall rating + const overallRating = Object.values(ratings).reduce((sum, rating) => sum + rating, 0) / Object.keys(ratings).length; + + // Handle content length + const isLongContent = content.length > 300; + const displayContent = isLongContent && !isExpanded ? `${content.substring(0, 280)}...` : content; + + return ( +
+ {/* Review Header */} +
+
+ {/* Use userProfile?.id */} + {userProfile?.username} + +
+ {/* Use userProfile?.id */} + {userProfile?.username} {/* Use userProfile?.username */} + + {userProfile?.is_critic && ( // Use userProfile?.is_critic + + Critic + + )} +
{formattedDate}
+
+
+
+ + {overallRating.toFixed(1)} + +
+
+ + {/* Review Content */} + {/* Ensure grid is applied on md screens when detailed */} + {/* Changed grid-cols-3 to grid-cols-2 for 2 columns */} +
+ {/* Text Content */} + {/* Changed col-span-2 to col-span-1 for 2 columns */} + {/* Removed overflow-hidden, kept break-words */} +
+ {/* Spoiler Warning */} + {spoiler && ( +
+ Spoiler Warning: This review contains spoilers. +
+ )} + + {/* Review Text */} +

+ {displayContent} +

+ + {/* Expand/Collapse for long content */} + {isLongContent && ( + + )} +
+ + {/* Rating Chart */} + {/* Ensure chart takes 1 column on md screens when detailed */} + {/* Added flex, justify-center, items-center to center the chart */} +
+ +
+
+ + {/* Review Footer */} +
+
+ + +
+ +
+
+ ); +} + +export default ReviewCard; diff --git a/src/components/reviews/ReviewForm.jsx b/src/components/reviews/ReviewForm.jsx new file mode 100644 index 0000000..881dfd1 --- /dev/null +++ b/src/components/reviews/ReviewForm.jsx @@ -0,0 +1,522 @@ +import { useState, useEffect } from 'react'; +import { FaFire, FaEdit, FaTrashAlt } from 'react-icons/fa'; // Changed FaStar to FaFire +import RatingChart from './RatingChart'; +import FlameRatingInput from './FlameRatingInput'; // Import the new component +import ReactQuill from 'react-quill'; // Import ReactQuill +import 'react-quill/dist/quill.snow.css'; // Import Quill styles (snow theme) + + +// Default characteristics if none are provided (fallback) +const defaultCharacteristics = { + overall: 'Общая оценка' // Ensure a default 'overall' characteristic +}; + +// Mapping for watched/completed status values +const watchedStatusLabels = { + not_watched: 'Не просмотрено', + watched: 'Просмотрено', +}; + +const completedStatusLabels = { + not_completed: 'Не пройдено', + completed: 'Пройдено', +}; + + +// ReviewForm now expects characteristics prop to be the media's characteristics { [key]: label } +// Also expects mediaType, progressType, seasons, and selectedSeasonId +function ReviewForm({ mediaId, seasonId, mediaType, progressType, onSubmit, onEdit, onDelete, characteristics = defaultCharacteristics, existingReview, seasons = [], selectedSeasonId }) { + // Initial state for ratings, now storing just the number { [key]: number } + // Initialize with a default value (e.g., 5) for each characteristic provided + const initialRatings = Object.keys(characteristics).reduce((acc, key) => { + acc[key] = 5; // Default rating of 5 + return acc; + }, {}); + + const [ratings, setRatings] = useState(initialRatings); + // Use empty string for Quill content state initially + const [content, setContent] = useState(''); + const [hasSpoilers, setHasSpoilers] = useState(false); + // Use 'progress' state instead of 'status' + const [progress, setProgress] = useState(''); // State for progress (text field) + const [isSubmitting, setIsSubmitting] = useState(false); + const [isEditing, setIsEditing] = useState(false); + + // New state for selected season in the form (only relevant if media supports seasons) + // Initialize with the selectedSeasonId passed from the parent + const [formSeasonId, setFormSeasonId] = useState(selectedSeasonId); + + + // Determine the correct progress options/label based *only* on progressType + const getProgressOptions = () => { + if (progressType === 'watched') { + return watchedStatusLabels; + } else if (progressType === 'completed') { + return completedStatusLabels; + } + // If progressType is 'hours' or unknown, use text input + return null; + }; + + const progressOptions = getProgressOptions(); + const isProgressSelect = progressOptions !== null; // Determine if we should show a select/segmented control + + // Determine if the media type supports seasons + const supportsSeasons = mediaType === 'tv' || mediaType === 'anime'; + + + // Reset ratings, content, progress, and formSeasonId when characteristics, existingReview, progressType, or selectedSeasonId change + useEffect(() => { + console.log('ReviewForm useEffect: existingReview changed', existingReview); // LOG + console.log('ReviewForm useEffect: selectedSeasonId changed', selectedSeasonId); // LOG + // Ensure characteristics is an object before processing + const validCharacteristics = characteristics && typeof characteristics === 'object' ? characteristics : defaultCharacteristics; + + if (existingReview) { + // If editing, pre-fill form with existing review data + // Expect existingReview.ratings to be { [key]: number } + const populatedRatings = Object.keys(validCharacteristics).reduce((acc, key) => { + // Use existing rating if it's a number, otherwise default to 5 + acc[key] = typeof existingReview.ratings?.[key] === 'number' ? existingReview.ratings[key] : 5; + return acc; + }, {}); + + setRatings(populatedRatings); + // Set content from existing review (assuming it's HTML) + setContent(existingReview.content || ''); + setHasSpoilers(existingReview.has_spoilers ?? false); + // Initialize progress from existing review + setProgress(existingReview.progress || ''); + // Initialize formSeasonId from existing review's season_id + setFormSeasonId(existingReview.season_id || null); // Use null for overall review + console.log('ReviewForm useEffect: Setting isEditing to false (existing review)'); // LOG + setIsEditing(false); // Start in view mode + } else { + // If creating, reset form + const newInitialRatings = Object.keys(validCharacteristics).reduce((acc, key) => { + acc[key] = 5; // Default rating of 5 + return acc; + }, {}); + setRatings(newInitialRatings); + setContent(''); // Reset content to empty string for Quill + setHasSpoilers(false); + // Reset progress based on the type of input expected + if (isProgressSelect) { + // Set default for select based on options (e.g., 'completed' or 'not_watched') + // Default to the first option key, which should be the 'not_' status + setProgress(Object.keys(progressOptions)[0] || ''); + } else { + setProgress(''); // Default to empty string for text input (hours) + } + // Reset formSeasonId to the currently selected season on the page + setFormSeasonId(selectedSeasonId); + console.log('ReviewForm useEffect: Resetting form, setting isEditing to false (no existing review)'); // LOG + setIsEditing(false); + } + }, [characteristics, existingReview, progressType, isProgressSelect, selectedSeasonId, seasons]); // Depend on selectedSeasonId and seasons too + + + // Add a log to see when isEditing state changes + useEffect(() => { + console.log('ReviewForm: isEditing state changed to', isEditing); // LOG + }, [isEditing]); + + + const handleRatingChange = (category, value) => { + setRatings(prev => ({ + ...prev, + [category]: value // Store the number directly from FlameRatingInput + })); + }; + + const handleProgressChange = (value) => { + setProgress(value); + }; + + const handleFormSeasonChange = (e) => { + // Convert the value to null if it's the "Общее" option + const value = e.target.value === '' ? null : e.target.value; + setFormSeasonId(value); + }; + + + const handleSubmit = async (e) => { + e.preventDefault(); + setIsSubmitting(true); + + try { + // Validate that all characteristics have a rating (since N/A is removed) + const allRatingsValid = Object.keys(characteristics).every(key => + typeof ratings[key] === 'number' && ratings[key] >= 1 && ratings[key] <= 10 + ); + + // Validate that progress is filled/selected + const isProgressValid = isProgressSelect ? Object.keys(progressOptions).includes(progress) : progress.trim() !== ''; + + // Validate that a season is selected if media supports seasons and it's not the overall review + // This validation is now relaxed: if supportsSeasons, formSeasonId can be null OR a valid season ID + const isSeasonSelectedValid = supportsSeasons ? (formSeasonId === null || seasons.some(s => s.id === formSeasonId)) : true; + + // Validate that content is not empty (check Quill's internal state or HTML string) + // Quill's empty state is typically '


' or just '' + const isContentValid = content.trim() !== '' && content !== '


'; + + + if (!allRatingsValid || !isProgressValid || !isSeasonSelectedValid || !isContentValid) { + console.error("Validation failed: Not all characteristics have a valid rating, progress is invalid/empty, season selection is invalid, or content is empty."); + // Optionally set an error message here if needed, though button is disabled + setIsSubmitting(false); + return; + } + + + const reviewData = { + media_id: mediaId, + season_id: formSeasonId, // Use the season selected in the form (can be null) + media_type: mediaType, + content, // Use content from Quill editor state (HTML string) + ratings, // Store the ratings object { [key]: number } + has_spoilers: hasSpoilers, + progress, // Include progress (text field) + }; + + if (existingReview && isEditing) { + // If editing an existing review + await onEdit(existingReview.id, reviewData); + console.log('ReviewForm: Edit submitted, setting isEditing to false'); // LOG + setIsEditing(false); // Exit editing mode after submit + } else { + // If creating a new review + await onSubmit(reviewData); + // Form reset is handled by the useEffect when existingReview becomes null or changes + } + + } catch (error) { + console.error('Error submitting review:', error); + // Optionally set an error state to display to the user + } finally { + setIsSubmitting(false); + } + }; + + const handleDelete = async () => { + if (window.confirm('Вы уверены, что хотите удалить эту рецензию?')) { + setIsSubmitting(true); + try { + await onDelete(existingReview.id, mediaId); // deleteReview only needs reviewId, mediaId is optional + // Form reset is handled by the useEffect when existingReview becomes null + } catch (error) { + console.error('Error deleting review:', error); + // Optionally set an error state + } finally { + setIsSubmitting(false); + } + } + }; + + // Determine if the form is valid for submission + // Content must not be empty, ALL characteristics must have a valid rating (1-10), and progress must be filled/selected + const isFormValid = content.trim() !== '' && content !== '


' && // Check content validity + Object.keys(characteristics).length > 0 && // Ensure characteristics are loaded + Object.keys(characteristics).every(key => + typeof ratings[key] === 'number' && ratings[key] >= 1 && ratings[key] <= 10 + ) && + (isProgressSelect ? Object.keys(progressOptions).includes(progress) : progress.trim() !== '') && // Check progress validity based on input type + (supportsSeasons ? (formSeasonId === null || seasons.some(s => s.id === formSeasonId)) : true); // Check season selection validity + + + // If an existing review is present and not in editing mode, show review details instead of the form + if (existingReview && !isEditing) { + return ( +
+

Вы уже написали рецензию на это произведение.

+
+ + {/* Fixed delete button text color */} + +
+
+ ); + } + + // Quill modules - define toolbar options + const modules = { + toolbar: [ + [{ 'header': [1, 2, false] }], + ['bold', 'italic', 'underline', 'strike', 'blockquote'], + [{ 'list': 'ordered' }, { 'list': 'bullet' }], + ['link'], + ['clean'] + ], + }; + + const formats = [ + 'header', + 'bold', 'italic', 'underline', 'strike', 'blockquote', + 'list', 'bullet', + 'link' + ]; + + + return ( +
+

+ {existingReview ? 'Редактировать рецензию' : 'Написать рецензию'} +

+ + {/* Add a log to see if this form section is being rendered */} + {existingReview && isEditing && console.log('ReviewForm: Rendering Edit Form')} + {!existingReview && console.log('ReviewForm: Rendering Create Form')} + + +
+
+
+ + {/* Season Selection (if media supports seasons) */} + {supportsSeasons && seasons.length > 0 && ( +
+ + +
+ )} + + + {/* Progress Input - Conditional based on progressType */} +
+ + {isProgressSelect ? ( + // Segmented control for watched/completed status +
+ {Object.entries(progressOptions).map(([key, label]) => ( + + ))} +
+ ) : ( + // Text input for hours (for games with progressType 'hours') or fallback + handleProgressChange(e.target.value)} + className="w-full p-3 bg-campfire-dark border border-campfire-ash/30 rounded-md text-campfire-light focus:ring-2 focus:ring-campfire-amber focus:border-transparent transition-colors" + required + /> + )} + {/* Add a hidden required input to satisfy HTML5 validation */} + {/* This hidden input is a fallback; actual validation is in isFormValid */} + {/* Removed the hidden input as isFormValid handles validation */} +
+ + + {/* Rating Inputs - Using FlameRatingInput */} +
{/* Increased spacing */} + {/* Iterate over characteristics provided by the media */} + {Object.entries(characteristics).map(([key, label]) => ( +
+
+ + {/* Display value - Changed FaStar to FaFire */} + {/* Made value larger/bolder */} + {/* Adjusted icon size */} + {ratings[key] !== undefined ? ratings[key] : 5} + +
+ {/* Flame Rating Input Component */} + handleRatingChange(key, value)} + /> +
+ ))} +
+ + {/* Review Text - Using ReactQuill */} +
+ + + {/* Add custom styles for Quill */} + +
+ + {/* Spoiler Checkbox */} +
+ setHasSpoilers(e.target.checked)} + className="w-4 h-4 text-campfire-amber bg-campfire-dark border-campfire-ash rounded focus:ring-campfire-amber focus:ring-2 cursor-pointer" + /> + +
+
+ + {/* Rating Chart Preview */} +
+

Предварительный просмотр вашей оценки

+ { + if (characteristics.hasOwnProperty(key) && typeof value === 'number' && value >= 1 && value <= 10) { + acc[key] = value; + } + return acc; + }, {})} + labels={characteristics} // Pass the characteristics labels + size="medium" + /> +
+
+ + {/* Submit Button */} +
{/* Added mt-6 for spacing */} + +
+ {/* Corrected error message condition */} + {!isFormValid && (content.trim() === '' || content === '


' || !(isProgressSelect ? Object.keys(progressOptions).includes(progress) : progress.trim() !== '') || Object.keys(characteristics).some(key => typeof ratings[key] !== 'number' || ratings[key] < 1 || ratings[key] > 10) || (supportsSeasons && formSeasonId === undefined)) && ( +

+ Пожалуйста, заполните все обязательные поля (рецензия, прогресс, все оценки от 1 до 10{supportsSeasons ? ', сезон' : ''}). +

+ )} + {Object.keys(characteristics).length === 0 && ( +

+ Характеристики для этого произведения не загружены. +

+ )} +
+
+ ); +} + +export default ReviewForm; diff --git a/src/components/reviews/ReviewItem.jsx b/src/components/reviews/ReviewItem.jsx new file mode 100644 index 0000000..dfb4068 --- /dev/null +++ b/src/components/reviews/ReviewItem.jsx @@ -0,0 +1,256 @@ +import React, { useState } from 'react'; +import { Link } from 'react-router-dom'; +import { FaFire, FaRegCommentDots, FaEye, FaClock, FaCheckCircle, FaGamepad } from 'react-icons/fa'; +import { getFileUrl } from '../../services/pocketbaseService'; +import DOMPurify from 'dompurify'; +import LikeButton from './LikeButton'; + +// Mapping for watched/completed status values +const watchedStatusLabels = { + not_watched: 'Не просмотрено', + watched: 'Просмотрено', +}; + +const completedStatusLabels = { + not_completed: 'Не пройдено', + completed: 'Пройдено', +}; + + +// ReviewItem now expects review, media, season, reviewCharacteristics, isProfilePage, and isSmallCard props +function ReviewItem({ review, media, season, reviewCharacteristics = {}, isProfilePage = false, isSmallCard = false }) { + const [showFullReview, setShowFullReview] = useState(false); + const [showSpoilers, setShowSpoilers] = useState(false); + + const reviewMedia = media || review.expand?.media_id; + const reviewSeason = season || review.expand?.season_id; + const reviewUser = review.expand?.user_id; + + const characteristics = reviewMedia?.characteristics && typeof reviewMedia.characteristics === 'object' + ? reviewMedia.characteristics + : reviewCharacteristics; + + const reviewTitle = reviewMedia + ? `${reviewMedia.title}${reviewSeason ? ` - Сезон ${reviewSeason.season_number}${reviewSeason.title ? `: ${reviewSeason.title}` : ''}` : ''}` + : 'Неизвестное произведение'; + + const reviewLink = reviewMedia ? `/media/${reviewMedia.path}` : '#'; + + + // Sanitize HTML content from the rich text editor + const sanitizedContent = DOMPurify.sanitize(review.content); + + + // Determine progress display based on media's progress_type + const renderProgress = () => { + if (!reviewMedia || !reviewMedia.progress_type || review.progress === undefined || review.progress === null) { + return null; + } + + const progressType = reviewMedia.progress_type; + const progressValue = review.progress; + + if (progressType === 'hours') { + return ( + + {progressValue} часов + + ); + } else if (progressType === 'watched') { + const label = watchedStatusLabels[progressValue] || progressValue; + return ( + + {label} + + ); + } else if (progressType === 'completed') { + const label = completedStatusLabels[progressValue] || progressValue; + return ( + + {label} + + ); + } + return ( + + Прогресс: {progressValue} + + ); + }; + + // Render as a small card for the "All Reviews" section on the profile page + if (isSmallCard) { + return ( + {/* Added relative and overflow-hidden */} + {/* Small Poster in corner - Only show on profile page small cards */} + {isProfilePage && reviewMedia?.poster && ( +
{/* Adjusted size and positioning */} + {`Постер +
+ )} + + {/* User Avatar and Title */} +
{/* Added right padding conditionally */} + {reviewUser && ( + {reviewUser.username} + )} +

+ {reviewTitle} +

+
+ + {/* Overall Rating */} +
+ + {review.overall_rating !== null && review.overall_rating !== undefined ? parseFloat(review.overall_rating).toFixed(1) : 'N/A'} / 10 +
+ + {/* Progress Display */} + {renderProgress() && ( +
+ {renderProgress()} +
+ )} + + {/* Snippet of Review Content */} +
+ {/* Strip HTML for small card snippet */} + {sanitizedContent.replace(/<[^>]*>?/gm, '').substring(0, 150)}{sanitizedContent.replace(/<[^>]*>?/gm, '').length > 150 ? '...' : ''} +
+ + {/* Date */} +
+ {new Date(review.created).toLocaleDateString()} +
+ + ); + } + + + // Default rendering for larger cards (Showcase, Media Page) + return ( +
+ {/* Media Poster - Only show on profile page large cards (Showcase) */} + {isProfilePage && reviewMedia?.poster && ( + + {`Постер + + )} + + {/* Header: User, Media Title, Overall Rating */} +
+
+ {/* User Avatar */} + {reviewUser && ( + + {reviewUser.username} + + )} +
+ {/* User Link */} + {reviewUser && ( + + {reviewUser.username} + + )} + {/* Media Title Link */} +

+ + {reviewTitle} + +

+
+
+ {/* Overall Rating */} +
+ + {review.overall_rating !== null && review.overall_rating !== undefined ? parseFloat(review.overall_rating).toFixed(1) : 'N/A'} / 10 +
+
+ + {/* Characteristics Ratings */} + {Object.keys(characteristics).length > 0 && ( +
+

Оценки по характеристикам:

+
+ {Object.entries(characteristics).map(([key, label]) => { + const ratingValue = review.ratings?.[key]; + if (typeof ratingValue === 'number' && ratingValue >= 1 && ratingValue <= 10) { + return ( +
+ {label}: + + {ratingValue} + +
+ ); + } + return null; + })} +
+
+ )} + + {/* Review Content and Footer (Flex container) */} +
+ {/* Review Content */} +
+ {review.has_spoilers && !showSpoilers ? ( +
+

Внимание: Эта рецензия содержит спойлеры!

+ +
+ ) : ( +
+ )} +
+ + {/* Read More Button */} + {review.content && review.content.length > 300 && ( + + )} + + {/* Footer: Date, Comment Count (Placeholder) and Progress */} +
+
+ {new Date(review.created).toLocaleDateString()} + {renderProgress()} +
+ +
+
+
+ ); +} + +export default ReviewItem; diff --git a/src/components/reviews/ReviewList.jsx b/src/components/reviews/ReviewList.jsx new file mode 100644 index 0000000..185ed4e --- /dev/null +++ b/src/components/reviews/ReviewList.jsx @@ -0,0 +1,31 @@ +import React from 'react'; +import ReviewItem from './ReviewItem'; + +// ReviewList now expects reviews and optionally reviewCharacteristics and isProfilePage +function ReviewList({ reviews, reviewCharacteristics = {}, isProfilePage = false }) { + return ( +
+ {reviews.length === 0 ? ( +
+ Рецензий пока нет. Будьте первым! +
+ ) : ( + reviews.map(review => ( + + )) + )} +
+ ); +} + +export default ReviewList; diff --git a/src/components/suggestions/SuggestCardWidget.jsx b/src/components/suggestions/SuggestCardWidget.jsx new file mode 100644 index 0000000..6d05381 --- /dev/null +++ b/src/components/suggestions/SuggestCardWidget.jsx @@ -0,0 +1,119 @@ +import React, { useState } from 'react'; +import { toast } from 'react-toastify'; +import { createSuggestion } from '../../services/pocketbaseService'; +import { useAuth } from '../../contexts/AuthContext'; + +const SuggestCardWidget = ({ onSuccess }) => { + const [formData, setFormData] = useState({ + title: '', + description: '', + category: 'movie', + link: '' + }); + const { user } = useAuth(); + + const handleSubmit = async (e) => { + e.preventDefault(); + if (!user) { + toast.error('Необходимо войти в систему'); + return; + } + + try { + await createSuggestion({ + ...formData, + status: 'pending', + user: user.id + }); + + toast.success('Предложение успешно отправлено!'); + setFormData({ + title: '', + description: '', + category: 'movie', + link: '' + }); + + if (onSuccess) { + onSuccess(); + } + } catch (error) { + toast.error('Ошибка при отправке предложения'); + console.error('Error submitting suggestion:', error); + } + }; + + const handleChange = (e) => { + const { name, value } = e.target; + setFormData(prev => ({ + ...prev, + [name]: value + })); + }; + + return ( +
+
+ + +
+ +
+ + +
+ + {/* Stats (Placeholder) */} +
+

Статистика

+
+
+

Всего обзоров:

+

{userProfile.review_count || 0}

+
+
+

Средняя оценка:

+

{userProfile.average_rating ? userProfile.average_rating.toFixed(1) : 'N/A'}

+
+ {/* Add more stats here if available in userProfile */} +
+
+ + + {/* Save Button */} +
+ +
+
+ )} +
+
+
+ ); +}; + +export default ProfileSettingsPage; diff --git a/src/pages/RatingPage.jsx b/src/pages/RatingPage.jsx new file mode 100644 index 0000000..5b423f0 --- /dev/null +++ b/src/pages/RatingPage.jsx @@ -0,0 +1,210 @@ +import React, { useEffect, useState, useCallback } from 'react'; +import { Link } from 'react-router-dom'; +import { listMedia, listUsersRankedByReviews, listUsersRankedByLevel, mediaTypes, getFileUrl, getReviewsByLikes } from '../services/pocketbaseService'; +import { useAuth } from '../contexts/AuthContext'; +import { useProfileActions } from '../contexts/ProfileActionsContext'; +import TiltedCard from '../components/reactbits/Components/TiltedCard/TiltedCard'; +import { FaFire, FaStar, FaUsers, FaLevelUpAlt, FaMedal, FaHeart } from 'react-icons/fa'; + +const RatingPage = () => { + const { userProfile } = useAuth(); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const [activeTab, setActiveTab] = useState('media'); + + const [mediaByRating, setMediaByRating] = useState([]); + const [mediaByReviews, setMediaByReviews] = useState([]); + const [usersByReviews, setUsersByReviews] = useState([]); + const [usersByLevel, setUsersByLevel] = useState([]); + const [reviewsByLikes, setReviewsByLikes] = useState([]); + + useEffect(() => { + window.scrollTo(0, 0); + }, []); + + const fetchData = useCallback(async () => { + try { + setLoading(true); + setError(null); + + const [ + mediaRatingData, + mediaReviewsData, + usersReviewsData, + usersLevelData, + reviewsLikesData + ] = await Promise.all([ + listMedia(null, 1, 20, userProfile, false, true, '-average_rating'), + listMedia(null, 1, 20, userProfile, false, true, '-review_count'), + listUsersRankedByReviews(20), + listUsersRankedByLevel(20), + getReviewsByLikes(20) + ]); + + setMediaByRating(mediaRatingData.data || []); + setMediaByReviews(mediaReviewsData.data || []); + setUsersByReviews(usersReviewsData || []); + setUsersByLevel(usersLevelData || []); + setReviewsByLikes(reviewsLikesData || []); + + } catch (err) { + console.error('Error fetching rating data:', err); + setError('Не удалось загрузить данные рейтинга.'); + } finally { + setLoading(false); + } + }, [userProfile]); + + useEffect(() => { + fetchData(); + }, [fetchData]); + + const renderMediaList = (mediaItems) => ( +
+ {mediaItems.map((mediaItem) => ( + + + + ))} +
+ ); + + const renderUserList = (userItems, rankType) => ( +
+ {userItems.map((user, index) => ( + + {index + 1}. + {user.username} +
+

{user.username}

+ {rankType === 'reviews' && ( +

Рецензий: {user.review_count || 0}

+ )} + {rankType === 'level' && ( +

Уровень: {user.level || 1}

+ )} +
+ + ))} +
+ ); + + const renderReviewList = (reviews) => ( +
+ {reviews.map((review, index) => ( + +
+
+ {index + 1}. +
+

{review.expand.media_id.title}

+

Автор: {review.expand.user_id.username}

+
+
+ + {review.likes?.length || 0} +
+
+
+
+ + ))} +
+ ); + + if (loading) { + return ( +
+
+
+ ); + } + + if (error) { + return ( +
+
+ {error} +
+
+ ); + } + + return ( +
+

Рейтинги

+ +
+ + + + + +
+ +
+ {activeTab === 'media-rating' && ( + mediaByRating.length > 0 ? renderMediaList(mediaByRating) :

Нет данных для этого рейтинга.

+ )} + {activeTab === 'media-reviews' && ( + mediaByReviews.length > 0 ? renderMediaList(mediaByReviews) :

Нет данных для этого рейтинга.

+ )} + {activeTab === 'reviews-likes' && ( + reviewsByLikes.length > 0 ? renderReviewList(reviewsByLikes) :

Нет данных для этого рейтинга.

+ )} + {activeTab === 'users-reviews' && ( + usersByReviews.length > 0 ? renderUserList(usersByReviews, 'reviews') :

Нет данных для этого рейтинга.

+ )} + {activeTab === 'users-level' && ( + usersByLevel.length > 0 ? renderUserList(usersByLevel, 'level') :

Нет данных для этого рейтинга.

+ )} +
+
+ ); +}; + +export default RatingPage; \ No newline at end of file diff --git a/src/pages/RegisterPage.jsx b/src/pages/RegisterPage.jsx new file mode 100644 index 0000000..fd699aa --- /dev/null +++ b/src/pages/RegisterPage.jsx @@ -0,0 +1,249 @@ +import { useState, useEffect } from "react"; +import { Link, useNavigate } from "react-router-dom"; +import { useAuth } from "../contexts/AuthContext"; + +function RegisterPage() { + const [login, setLogin] = useState(""); + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + const [confirmPassword, setConfirmPassword] = useState(""); + const [error, setError] = useState(""); + const [loading, setLoading] = useState(false); + + const { user, userProfile, loading: authLoading, signUp, isInitialized } = useAuth(); + const navigate = useNavigate(); + + console.log('RegisterPage: Rendering. State:', { user: !!user, userProfile: !!userProfile, authLoading, isInitialized, componentLoading: loading, componentError: error }); // Add component render log + + + useEffect(() => { + console.log('RegisterPage useEffect: Running. State:', { user: !!user, userProfile: !!userProfile, authLoading, isInitialized }); // Add useEffect log + if (!authLoading && isInitialized) { // Wait for auth to be initialized and not loading + if (user && userProfile) { + console.log('RegisterPage useEffect: User and profile loaded, redirecting to home.'); + navigate("/"); // Redirect after successful registration or if already logged in + } else { + console.log('RegisterPage useEffect: No user and auth initialized, staying on register.'); + } + } else if (authLoading) { + console.log('RegisterPage useEffect: Auth is loading...'); + } else if (!isInitialized) { + console.log('RegisterPage useEffect: Auth is not initialized...'); + } + }, [user, userProfile, authLoading, isInitialized, navigate]); // Add isInitialized to dependencies + + + const handleSubmit = async (e) => { + e.preventDefault(); + + if (!login || !password || !confirmPassword) { + setError("Заполни обязательные поля"); + return; + } + + if (password !== confirmPassword) { + setError("Хмм, пароли не совпадают..."); + return; + } + + if (password.length < 6) { + setError("Коротковато..."); + return; + } + + // Проверка на допустимые символы в логине + if (!/^[a-zA-Z0-9_]+$/.test(login)) { + setError("Логин может содержать только латинские буквы, цифры и знак подчеркивания"); + return; + } + + // Проверка длины логина + if (login.length < 3 || login.length > 20) { + setError("Логин должен быть от 3 до 20 символов"); + return; + } + + try { + setError(""); + setLoading(true); + console.log('RegisterPage handleSubmit: Attempting sign up...'); + await signUp(login, password, email || null); + console.log('RegisterPage handleSubmit: Sign up process initiated. Redirect handled by useEffect.'); + // Redirect is now handled by the useEffect based on auth state change + + } catch (err) { + console.error("Registration error:", err); + // More specific error handling for PocketBase unique constraints + if (err.response && err.response.data) { + const errorData = err.response.data; + if (errorData.login && errorData.login.code === 'validation_not_unique') { + setError("Этот логин уже занят"); + } else if (errorData.email && errorData.email.code === 'validation_not_unique') { + setError("Пользователь с таким email уже существует"); + } else { + setError(err.message || "Произошла ошибка при регистрации"); + } + } else { + setError(err.message || "Произошла ошибка при регистрации"); + } + } finally { + setLoading(false); + } + }; + + // Render loading state based on AuthContext loading + // Removed the check here to allow the form to render even if auth is loading, + // relying on the AuthProvider's own loading state handling. + // This might help diagnose if the component itself is not rendering. + // if (authLoading || !isInitialized) { + // console.log('RegisterPage: Rendering initial auth loading state.'); + // return ( + //
+ // Загрузка авторизации... + //
+ // ); + // } + + console.log('RegisterPage: Rendering registration form.'); // Log before rendering form + + return ( +
+
+
+
+

+ Присоединиться к CampFire мнеие +

+

+ Создай свою учетную запись CampFire, чтобы оценивать и рецензировать + все что движется. +

+
+ + {error && ( +
+ {error} +
+ )} + +
+
+ + setLogin(e.target.value)} + className="input w-full px-3 py-2 bg-campfire-dark text-campfire-light border border-campfire-ash/30 rounded-md focus:outline-none focus:ring-campfire-amber focus:border-campfire-amber transition-colors" + placeholder="Введите логин" + required + autoComplete="username" + /> +

+ От 3 до 20 символов, только латинские буквы, цифры и _ +

+
+ +
+ + setEmail(e.target.value)} + className="input w-full px-3 py-2 bg-campfire-dark text-campfire-light border border-campfire-ash/30 rounded-md focus:outline-none focus:ring-campfire-amber focus:border-campfire-amber transition-colors" + placeholder="your@campfiregg.ru" + autoComplete="email" + /> +
+ +
+ + setPassword(e.target.value)} + className="input w-full px-3 py-2 bg-campfire-dark text-campfire-light border border-campfire-ash/30 rounded-md focus:outline-none focus:ring-campfire-amber focus:border-campfire-amber transition-colors" + placeholder="••••••••" + required + autoComplete="new-password" + /> +

+ Не меньше 6 знаков +

+
+ +
+ + setConfirmPassword(e.target.value)} + className="input w-full px-3 py-2 bg-campfire-dark text-campfire-light border border-campfire-ash/30 rounded-md focus:outline-none focus:ring-campfire-amber focus:border-campfire-amber transition-colors" + placeholder="••••••••" + required + autoComplete="new-password" + /> +
+ + +
+ +
+ Уже в строю?{" "} + + Войти + +
+
+
+
+ ); +} + +export default RegisterPage; diff --git a/src/pages/SupportPage.jsx b/src/pages/SupportPage.jsx new file mode 100644 index 0000000..7fee345 --- /dev/null +++ b/src/pages/SupportPage.jsx @@ -0,0 +1,410 @@ +import React, { useState, useEffect } from 'react'; +import { useAuth } from '../contexts/AuthContext'; +import { pb } from '../services/pocketbaseService'; +import { toast } from 'react-hot-toast'; +import { FaSpinner, FaCheck, FaTimes, FaClock, FaPlus } from 'react-icons/fa'; +import SupportTicketForm from '../components/support/SupportTicketForm'; +import SuggestCardWidget from '../components/suggestions/SuggestCardWidget'; +import { useNavigate } from 'react-router-dom'; + +const SupportPage = () => { + const { user } = useAuth(); + const [tickets, setTickets] = useState([]); + const [suggestions, setSuggestions] = useState([]); + const [loading, setLoading] = useState(true); + const [showTicketForm, setShowTicketForm] = useState(false); + const [showSuggestionForm, setShowSuggestionForm] = useState(false); + const [activeTab, setActiveTab] = useState('tickets'); // 'tickets' или 'suggestions' + const [replyText, setReplyText] = useState(''); + const [replyingTo, setReplyingTo] = useState(null); + const navigate = useNavigate(); + + useEffect(() => { + if (user) { + loadUserData(); + } + }, [user]); + + const loadUserData = async () => { + try { + setLoading(true); + + // Загружаем тикеты пользователя + const ticketsData = await pb.collection('support_tickets').getList(1, 50, { + filter: `user_id = "${user.id}"`, + sort: '-created' + }); + + // Загружаем предложения пользователя + const suggestionsData = await pb.collection('suggestions').getList(1, 50, { + filter: `user = "${user.id}"`, + sort: '-created' + }); + + setTickets(ticketsData.items); + setSuggestions(suggestionsData.items); + } catch (error) { + console.error('Ошибка при загрузке данных:', error); + toast.error('Не удалось загрузить данные'); + } finally { + setLoading(false); + } + }; + + const getStatusIcon = (status) => { + switch (status) { + case 'open': + return ; + case 'in_progress': + return ; + case 'closed': + return ; + default: + return null; + } + }; + + const getStatusLabel = (status) => { + switch (status) { + case 'open': + return 'Открыт'; + case 'in_progress': + return 'В работе'; + case 'closed': + return 'Закрыт'; + default: + return status; + } + }; + + const getSuggestionStatusLabel = (status) => { + switch (status) { + case 'pending': + return 'На рассмотрении'; + case 'approved': + return 'Одобрено'; + case 'rejected': + return 'Отклонено'; + default: + return status; + } + }; + + const getSuggestionStatusColor = (status) => { + switch (status) { + case 'pending': + return 'text-status-warning'; + case 'approved': + return 'text-status-success'; + case 'rejected': + return 'text-status-error'; + default: + return 'text-campfire-ash'; + } + }; + + const handleTicketCreated = () => { + setShowTicketForm(false); + loadUserData(); + toast.success('Обращение успешно создано'); + }; + + const formatMessages = (message) => { + // Разбиваем сообщение на части по датам + const parts = message.split(/\n\n/); + return parts.map(part => { + const [date, ...content] = part.split('\n'); + return { + date: date.trim(), + content: content.join('\n').trim() + }; + }).sort((a, b) => new Date(a.date) - new Date(b.date)); + }; + + const handleReply = async (ticketId) => { + if (!replyText.trim()) return; + + try { + setLoading(true); + const ticket = await pb.collection('support_tickets').getOne(ticketId); + const timestamp = new Date().toLocaleString(); + const updatedMessage = ticket.message + ? `${ticket.message}\n\n${timestamp}\n${replyText}` + : `${timestamp}\n${replyText}`; + + await pb.collection('support_tickets').update(ticketId, { + message: updatedMessage, + status: 'open' + }); + + // Обновляем локальное состояние + setTickets(prevTickets => + prevTickets.map(ticket => + ticket.id === ticketId + ? { ...ticket, message: updatedMessage, status: 'open' } + : ticket + ) + ); + + setReplyText(''); + setReplyingTo(null); + toast.success('Сообщение отправлено'); + } catch (error) { + console.error('Error sending reply:', error); + toast.error('Не удалось отправить сообщение'); + } finally { + setLoading(false); + } + }; + + if (!user) { + return ( +
+
+

Для доступа к поддержке необходимо войти в систему

+ +
+
+ ); + } + + return ( +
+
+
+

Поддержка

+
+ + +
+
+ + {/* Tabs */} +
+ + +
+ + {loading ? ( +
+ +
+ ) : ( + <> + {activeTab === 'tickets' ? ( +
+ {tickets.length === 0 ? ( +
+ У вас пока нет обращений в поддержку +
+ ) : ( + tickets.map(ticket => ( +
+
+
+

+ {ticket.subject} +

+

+ {new Date(ticket.created).toLocaleString()} +

+
+
+ {getStatusIcon(ticket.status)} + + {getStatusLabel(ticket.status)} + +
+
+ +
+

Сообщения:

+
+ {formatMessages(ticket.message).map((msg, index) => ( +
+
{msg.date}
+

{msg.content}

+
+ ))} +
+
+ + {ticket.admin_notes && ( +
+

Ответ поддержки:

+

{ticket.admin_notes}

+
+ )} + + {ticket.status !== 'closed' && ( +
+ {replyingTo === ticket.id ? ( +
+