From 666636e5bfbe8c3ab92f9a7c57310723233c4b13 Mon Sep 17 00:00:00 2001 From: "ext.jeremy.guillot@maxicoffee.domains" Date: Wed, 12 Feb 2025 16:12:01 +0100 Subject: [PATCH] =?UTF-8?q?feat:=20Ajout=20de=20React=20pour=20le=20front,?= =?UTF-8?q?=20d=C3=A9but=20de=20refonte=20du=20front?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Makefile | 5 +- assets/app.js | 3 + assets/controllers.json | 6 + assets/react/app/App.jsx | 20 + .../useCases/getMangaCollection.js | 9 + .../application/useCases/getMangaDetail.js | 9 + .../app/application/useCases/searchMangas.js | 9 + assets/react/app/assets/react.svg | 1 + assets/react/app/domain/chapter.js | 10 + assets/react/app/domain/manga.js | 75 + .../react/app/domain/ports/mangaRepository.js | 6 + assets/react/app/index.jsx | 11 + .../infrastructure/api/apiMangaRepository.js | 41 + .../infrastructure/api/mockMangaRepository.js | 109 ++ .../app/presentation/components/Header.jsx | 26 + .../presentation/components/Layout/Layout.jsx | 26 + .../app/presentation/components/MangaCard.jsx | 43 + .../app/presentation/components/MangaGrid.jsx | 16 + .../components/SearchBar/SearchBar.jsx | 125 ++ .../app/presentation/components/Sidebar.jsx | 164 ++ .../components/Toolbar/Toolbar.jsx | 29 + .../components/Toolbar/ToolbarButton.jsx | 20 + .../app/presentation/pages/AddMangaPage.jsx | 185 ++ .../react/app/presentation/pages/HomePage.jsx | 80 + .../presentation/pages/MangaDetailPage.jsx | 183 ++ composer.json | 1 + composer.lock | 78 +- config/bundles.php | 1 + config/routes.yaml | 6 + package-lock.json | 1690 +++++++++++++++-- package.json | 8 + symfony.lock | 12 + tailwind.config.js | 4 +- templates/react/index.html.twig | 13 + webpack.config.js | 3 + 35 files changed, 2863 insertions(+), 164 deletions(-) create mode 100644 assets/react/app/App.jsx create mode 100644 assets/react/app/application/useCases/getMangaCollection.js create mode 100644 assets/react/app/application/useCases/getMangaDetail.js create mode 100644 assets/react/app/application/useCases/searchMangas.js create mode 100644 assets/react/app/assets/react.svg create mode 100644 assets/react/app/domain/chapter.js create mode 100644 assets/react/app/domain/manga.js create mode 100644 assets/react/app/domain/ports/mangaRepository.js create mode 100644 assets/react/app/index.jsx create mode 100644 assets/react/app/infrastructure/api/apiMangaRepository.js create mode 100644 assets/react/app/infrastructure/api/mockMangaRepository.js create mode 100644 assets/react/app/presentation/components/Header.jsx create mode 100644 assets/react/app/presentation/components/Layout/Layout.jsx create mode 100644 assets/react/app/presentation/components/MangaCard.jsx create mode 100644 assets/react/app/presentation/components/MangaGrid.jsx create mode 100644 assets/react/app/presentation/components/SearchBar/SearchBar.jsx create mode 100644 assets/react/app/presentation/components/Sidebar.jsx create mode 100644 assets/react/app/presentation/components/Toolbar/Toolbar.jsx create mode 100644 assets/react/app/presentation/components/Toolbar/ToolbarButton.jsx create mode 100644 assets/react/app/presentation/pages/AddMangaPage.jsx create mode 100644 assets/react/app/presentation/pages/HomePage.jsx create mode 100644 assets/react/app/presentation/pages/MangaDetailPage.jsx create mode 100644 templates/react/index.html.twig diff --git a/Makefile b/Makefile index 5dca719..4662fb5 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,7 @@ SF_MEMORY = $(PHP) -d memory_limit=256M bin/console # Misc .DEFAULT_GOAL = help -.PHONY : help build start install down stop logs sh composer vendor sf cc test phpmd phpcs quality fix-permissions controller entity migration migration-diff migration-migrate form crud fixtures command auth subscriber state-processor state-provider npm-install npm-run npm-watch +.PHONY : help build start install down stop logs sh composer vendor sf cc test phpmd phpcs quality fix-permissions controller entity migration migration-diff migration-migrate form crud fixtures command auth subscriber state-processor state-provider npm-install npm-run npm-watch openapi ## —— 🎵 🐳 The Symfony Docker Makefile 🐳 🎵 —————————————————————————————————— help: ## Outputs this help screen @@ -147,6 +147,9 @@ consume-schedule: ## Consume schedule messages message: ## Create a new message and handler @$(SYMFONY) make:message +openapi: ## Exporter la documentation OpenAPI en JSON + @$(SYMFONY) api:openapi:export --output=public/api-docs.json + ## —— Webpack Encore ————————————————————————————————————————————————————————————— npm-install: ## Install npm dependencies @$(DOCKER_COMP) exec node npm install --force diff --git a/assets/app.js b/assets/app.js index e66af97..e671a9f 100644 --- a/assets/app.js +++ b/assets/app.js @@ -1,3 +1,4 @@ +import { registerReactControllerComponents } from '@symfony/ux-react'; import './bootstrap.js'; import '@fortawesome/fontawesome-free/js/all.js'; @@ -13,3 +14,5 @@ import './styles/app.scss'; // start the Stimulus application import './bootstrap'; + +//registerReactControllerComponents(require.context('./react/controllers', true, /\.(j|t)sx?$/)); diff --git a/assets/controllers.json b/assets/controllers.json index 71abe9d..3dfe062 100644 --- a/assets/controllers.json +++ b/assets/controllers.json @@ -9,6 +9,12 @@ } } }, + "@symfony/ux-react": { + "react": { + "enabled": true, + "fetch": "eager" + } + }, "@symfony/ux-turbo": { "turbo-core": { "enabled": true, diff --git a/assets/react/app/App.jsx b/assets/react/app/App.jsx new file mode 100644 index 0000000..aeda3ce --- /dev/null +++ b/assets/react/app/App.jsx @@ -0,0 +1,20 @@ +import React from 'react'; +import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom'; +import { HomePage } from './presentation/pages/HomePage.jsx'; +import { MangaDetailPage } from './presentation/pages/MangaDetailPage.jsx'; +import { AddMangaPage } from './presentation/pages/AddMangaPage.jsx'; + +function App() { + return ( + + + } /> + } /> + } /> + } /> + + + ); +} + +export default App; \ No newline at end of file diff --git a/assets/react/app/application/useCases/getMangaCollection.js b/assets/react/app/application/useCases/getMangaCollection.js new file mode 100644 index 0000000..4418605 --- /dev/null +++ b/assets/react/app/application/useCases/getMangaCollection.js @@ -0,0 +1,9 @@ +export class GetMangaCollection { + constructor(mangaRepository) { + this.mangaRepository = mangaRepository; + } + + async execute(page = 1) { + return await this.mangaRepository.getMangaCollection(page); + } +} \ No newline at end of file diff --git a/assets/react/app/application/useCases/getMangaDetail.js b/assets/react/app/application/useCases/getMangaDetail.js new file mode 100644 index 0000000..d9aed45 --- /dev/null +++ b/assets/react/app/application/useCases/getMangaDetail.js @@ -0,0 +1,9 @@ +export class GetMangaDetail { + constructor(mangaRepository) { + this.mangaRepository = mangaRepository; + } + + async execute(slug) { + return await this.mangaRepository.getMangaBySlug(slug); + } +} \ No newline at end of file diff --git a/assets/react/app/application/useCases/searchMangas.js b/assets/react/app/application/useCases/searchMangas.js new file mode 100644 index 0000000..dc63692 --- /dev/null +++ b/assets/react/app/application/useCases/searchMangas.js @@ -0,0 +1,9 @@ +export class SearchMangas { + constructor(mangaRepository) { + this.mangaRepository = mangaRepository; + } + + async execute(query) { + return await this.mangaRepository.searchMangas(query); + } +} \ No newline at end of file diff --git a/assets/react/app/assets/react.svg b/assets/react/app/assets/react.svg new file mode 100644 index 0000000..6c87de9 --- /dev/null +++ b/assets/react/app/assets/react.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/react/app/domain/chapter.js b/assets/react/app/domain/chapter.js new file mode 100644 index 0000000..fcd4aa2 --- /dev/null +++ b/assets/react/app/domain/chapter.js @@ -0,0 +1,10 @@ +export class Chapter { + constructor(id, number, title, volume, isVisible, createdAt) { + this.id = id; + this.number = number; + this.title = title; + this.volume = volume; + this.isVisible = isVisible; + this.createdAt = createdAt; + } +} \ No newline at end of file diff --git a/assets/react/app/domain/manga.js b/assets/react/app/domain/manga.js new file mode 100644 index 0000000..7b82ed5 --- /dev/null +++ b/assets/react/app/domain/manga.js @@ -0,0 +1,75 @@ +import { Chapter } from './chapter.js'; + +export class Manga { + constructor( + id, + title, + slug, + imageUrl, + author, + publicationYear, + genres, + status, + rating, + description = '' + ) { + this.id = id; + this.title = title; + this.slug = slug; + this.imageUrl = imageUrl; + this.author = author; + this.publicationYear = publicationYear; + this.genres = genres; + this.status = status; + this.rating = rating; + this.description = description; + } +} + +export class MangaCollection { + constructor(items, total, page, limit, hasNextPage, hasPreviousPage) { + this.items = items; + this.total = total; + this.page = page; + this.limit = limit; + this.hasNextPage = hasNextPage; + this.hasPreviousPage = hasPreviousPage; + } +} + +export class MangaDetail extends Manga { + constructor(manga, chapters = []) { + super( + manga.id, + manga.title, + manga.slug, + manga.imageUrl, + manga.author, + manga.publicationYear, + manga.genres, + manga.status, + manga.rating, + manga.description + ); + this.chapters = this.organizeChaptersByVolume(chapters); + } + + organizeChaptersByVolume(chapters) { + const volumeMap = new Map(); + + chapters.forEach(chapter => { + const volume = chapter.volume || 0; + if (!volumeMap.has(volume)) { + volumeMap.set(volume, []); + } + volumeMap.get(volume).push(chapter); + }); + + // Sort chapters within each volume + volumeMap.forEach(chapters => { + chapters.sort((a, b) => b.number - a.number); + }); + + return new Map([...volumeMap.entries()].sort((a, b) => b[0] - a[0])); + } +} \ No newline at end of file diff --git a/assets/react/app/domain/ports/mangaRepository.js b/assets/react/app/domain/ports/mangaRepository.js new file mode 100644 index 0000000..29ffa21 --- /dev/null +++ b/assets/react/app/domain/ports/mangaRepository.js @@ -0,0 +1,6 @@ +// Port (interface) for manga data access +export class MangaRepository { + async getMangaCollection(page = 1) { + throw new Error('Not implemented'); + } +} \ No newline at end of file diff --git a/assets/react/app/index.jsx b/assets/react/app/index.jsx new file mode 100644 index 0000000..cec8494 --- /dev/null +++ b/assets/react/app/index.jsx @@ -0,0 +1,11 @@ +import React from 'react' +import { StrictMode } from 'react' +import { createRoot } from 'react-dom/client' +import App from './App.jsx' +import '../../styles/app.scss' + +createRoot(document.getElementById('react-app')).render( + + + , +) diff --git a/assets/react/app/infrastructure/api/apiMangaRepository.js b/assets/react/app/infrastructure/api/apiMangaRepository.js new file mode 100644 index 0000000..3e6ca64 --- /dev/null +++ b/assets/react/app/infrastructure/api/apiMangaRepository.js @@ -0,0 +1,41 @@ +import axios from 'axios'; +import { Manga, MangaCollection } from '../../domain/manga'; + +export class ApiMangaRepository { + constructor() { + this.api = axios.create({ + baseURL: '/api' + }); + } + + async getMangaCollection(page = 1) { + try { + const response = await this.api.get(`/mangas?page=${page}`); + const data = response.data; + + const mangas = data.items.map(item => new Manga( + item.id, + item.title, + item.slug, + item.imageUrl, + item.author, + item.publicationYear, + item.genres, + item.status, + item.rating + )); + + return new MangaCollection( + mangas, + data.total, + data.page, + data.limit, + data.hasNextPage, + data.hasPreviousPage + ); + } catch (error) { + console.error('Error fetching manga collection:', error); + throw error; + } + } +} \ No newline at end of file diff --git a/assets/react/app/infrastructure/api/mockMangaRepository.js b/assets/react/app/infrastructure/api/mockMangaRepository.js new file mode 100644 index 0000000..6794d33 --- /dev/null +++ b/assets/react/app/infrastructure/api/mockMangaRepository.js @@ -0,0 +1,109 @@ +import { Manga, MangaCollection, MangaDetail } from '../../domain/manga.js'; +import { Chapter } from '../../domain/chapter.js'; + +export class MockMangaRepository { + constructor() { + this.mangas = { + 'one-piece': { + id: '1', + title: 'One Piece', + slug: 'one-piece', + imageUrl: 'https://images.unsplash.com/photo-1607604276583-eef5d076aa5f?auto=format&fit=crop&w=800&q=80', + author: 'Eiichiro Oda', + publicationYear: 1997, + genres: ['Action', 'Adventure', 'Comedy'], + status: 'ongoing', + rating: 4.9, + description: 'Monkey D. Luffy refuses to let anyone or anything stand in the way of his quest to become king of all pirates.' + }, + 'naruto': { + id: '2', + title: 'Naruto', + slug: 'naruto', + imageUrl: 'https://images.unsplash.com/photo-1618519764620-7403abdbdfe9?auto=format&fit=crop&w=800&q=80', + author: 'Masashi Kishimoto', + publicationYear: 1999, + genres: ['Action', 'Adventure', 'Fantasy'], + status: 'completed', + rating: 4.8, + description: 'Twelve years ago the Village Hidden in the Leaves was attacked by a fearsome threat.' + }, + 'berserk': { + id: '3', + title: 'Berserk', + slug: 'berserk', + imageUrl: 'https://images.unsplash.com/photo-1607604276583-eef5d076aa5f?auto=format&fit=crop&w=800&q=80', + author: 'Kentaro Miura', + publicationYear: 1989, + genres: ['Action', 'Dark Fantasy', 'Horror', 'Psychological'], + status: 'ongoing', + rating: 4.9, + description: 'Guts, known as the Black Swordsman, seeks sanctuary from the demonic forces.' + } + }; + } + + async searchMangas(query) { + if (!query) return []; + + const normalizedQuery = query.toLowerCase(); + return Object.values(this.mangas) + .filter(manga => + manga.title.toLowerCase().includes(normalizedQuery) || + manga.author.toLowerCase().includes(normalizedQuery) + ) + .map(manga => new Manga( + manga.id, + manga.title, + manga.slug, + manga.imageUrl, + manga.author, + manga.publicationYear, + manga.genres, + manga.status, + manga.rating + )); + } + + async getMangaCollection(page = 1) { + const mangas = Object.values(this.mangas).map(manga => new Manga( + manga.id, + manga.title, + manga.slug, + manga.imageUrl, + manga.author, + manga.publicationYear, + manga.genres, + manga.status, + manga.rating + )); + + return new MangaCollection( + mangas, + mangas.length, + page, + 10, + false, + false + ); + } + + async getMangaBySlug(slug) { + const manga = this.mangas[slug]; + + if (!manga) { + throw new Error(`Manga with slug "${slug}" not found`); + } + + const chapters = [ + new Chapter('1', 378, 'Un assassin invité', 42, true, '2024-02-15'), + new Chapter('2', 377, 'Snake In One\'s Bosom', 42, true, '2024-02-01'), + new Chapter('3', 376, 'La mer tremble, la guerre se profile', 42, true, '2024-01-15'), + new Chapter('4', 375, 'L\'aube suivant la nuit brumeuse', 41, true, '2024-01-01'), + new Chapter('5', 374, 'Le monstre noir va-t-il se laisser faire ?', 41, true, '2023-12-15'), + new Chapter('6', 373, 'Confrontation', 41, true, '2023-12-01'), + ]; + + return new MangaDetail(manga, chapters); + } +} \ No newline at end of file diff --git a/assets/react/app/presentation/components/Header.jsx b/assets/react/app/presentation/components/Header.jsx new file mode 100644 index 0000000..a2e280e --- /dev/null +++ b/assets/react/app/presentation/components/Header.jsx @@ -0,0 +1,26 @@ +import React from 'react'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faBars } from '@fortawesome/free-solid-svg-icons'; +import { SearchBar } from './SearchBar/SearchBar.jsx'; + +export function Header({ onMenuClick, onMangaClick, onAddMangaClick }) { + return ( +
+ +
+ + Mangarr + + +
+
+ ); +} \ No newline at end of file diff --git a/assets/react/app/presentation/components/Layout/Layout.jsx b/assets/react/app/presentation/components/Layout/Layout.jsx new file mode 100644 index 0000000..c599ea5 --- /dev/null +++ b/assets/react/app/presentation/components/Layout/Layout.jsx @@ -0,0 +1,26 @@ +import React, { useState } from 'react'; +import { Header } from '../Header'; +import { Sidebar } from '../Sidebar'; + +export function Layout({ children, onMangaClick, onAddMangaClick }) { + const [isSidebarOpen, setIsSidebarOpen] = useState(false); + + return ( +
+
setIsSidebarOpen(!isSidebarOpen)} + onMangaClick={onMangaClick} + onAddMangaClick={onAddMangaClick} + /> + setIsSidebarOpen(false)} + onAddMangaClick={onAddMangaClick} + /> + +
+ {children} +
+
+ ); +} \ No newline at end of file diff --git a/assets/react/app/presentation/components/MangaCard.jsx b/assets/react/app/presentation/components/MangaCard.jsx new file mode 100644 index 0000000..05384c4 --- /dev/null +++ b/assets/react/app/presentation/components/MangaCard.jsx @@ -0,0 +1,43 @@ +import React from 'react'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faStar } from '@fortawesome/free-solid-svg-icons'; + +export function MangaCard({ manga, onClick }) { + return ( +
onClick?.(manga.slug)} + > +
+ {manga.title} +
+
+

{manga.title}

+

By {manga.author}

+
+ {manga.publicationYear} + {manga.rating && ( + + + {manga.rating} + + )} +
+
+ {manga.genres.map((genre, index) => ( + + {genre} + + ))} +
+
+
+ ); +} \ No newline at end of file diff --git a/assets/react/app/presentation/components/MangaGrid.jsx b/assets/react/app/presentation/components/MangaGrid.jsx new file mode 100644 index 0000000..fa8eb86 --- /dev/null +++ b/assets/react/app/presentation/components/MangaGrid.jsx @@ -0,0 +1,16 @@ +import React from 'react'; +import { MangaCard } from './MangaCard.jsx'; + +export function MangaGrid({ mangas, onMangaClick }) { + return ( +
+ {mangas.map((manga) => ( + + ))} +
+ ); +} \ No newline at end of file diff --git a/assets/react/app/presentation/components/SearchBar/SearchBar.jsx b/assets/react/app/presentation/components/SearchBar/SearchBar.jsx new file mode 100644 index 0000000..44e60bb --- /dev/null +++ b/assets/react/app/presentation/components/SearchBar/SearchBar.jsx @@ -0,0 +1,125 @@ +import React, { useState, useRef, useEffect } from 'react'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faSearch, faPlus } from '@fortawesome/free-solid-svg-icons'; +import { MockMangaRepository } from '../../../infrastructure/api/mockMangaRepository.js'; +import { SearchMangas } from '../../../application/useCases/searchMangas.js'; + +const mangaRepository = new MockMangaRepository(); +const searchMangas = new SearchMangas(mangaRepository); + +export function SearchBar({ onMangaClick, onAddMangaClick }) { + const [query, setQuery] = useState(''); + const [results, setResults] = useState([]); + const [isOpen, setIsOpen] = useState(false); + const [loading, setLoading] = useState(false); + const [hasSearched, setHasSearched] = useState(false); + const searchRef = useRef(null); + + useEffect(() => { + const handleClickOutside = (event) => { + if (searchRef.current && !searchRef.current.contains(event.target)) { + setIsOpen(false); + } + }; + + document.addEventListener('mousedown', handleClickOutside); + return () => document.removeEventListener('mousedown', handleClickOutside); + }, []); + + useEffect(() => { + const searchManga = async () => { + if (!query.trim()) { + setResults([]); + setHasSearched(false); + return; + } + + setLoading(true); + try { + const searchResults = await searchMangas.execute(query); + setResults(searchResults); + setHasSearched(true); + } catch (error) { + console.error('Search error:', error); + } finally { + setLoading(false); + } + }; + + const timeoutId = setTimeout(searchManga, 300); + return () => clearTimeout(timeoutId); + }, [query]); + + return ( +
+
+ + { + setQuery(e.target.value); + setIsOpen(true); + }} + onFocus={() => setIsOpen(true)} + placeholder="Rechercher" + className="appearance-none outline-none ml-2 pl-0 bg-transparent border-b border-white w-full placeholder:text-white text-white py-1 px-2 leading-tight transition-all duration-500 ease-in-out focus:placeholder:text-opacity-0 focus:border-opacity-0" + /> +
+ + {isOpen && query.trim() && ( +
+ {loading ? ( +
Chargement...
+ ) : results.length > 0 ? ( +
+

+ Mangas existants +

+ {results.map((manga) => ( + + ))} +
+ ) : hasSearched && ( +
+ +
+ )} +
+ )} +
+ ); +} \ No newline at end of file diff --git a/assets/react/app/presentation/components/Sidebar.jsx b/assets/react/app/presentation/components/Sidebar.jsx new file mode 100644 index 0000000..bd96296 --- /dev/null +++ b/assets/react/app/presentation/components/Sidebar.jsx @@ -0,0 +1,164 @@ +import React, { useState } from 'react'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { + faBook, + faPlus, + faFileImport, + faCompass, + faExchangeAlt, + faCalendar, + faClockRotateLeft, + faCog, + faDesktop, + faChevronDown, + faChevronUp +} from '@fortawesome/free-solid-svg-icons'; + +export function Sidebar({ isOpen, onClose, onAddMangaClick }) { + const [expandedMenus, setExpandedMenus] = useState({ + mangas: true, // Par défaut, le menu Mangas est ouvert + settings: false, + system: false + }); + + const menuItems = [ + { + icon: faBook, + text: 'Mangas', + id: 'mangas', + subItems: [ + { icon: faPlus, text: 'Ajouter un nouveau', onClick: () => onAddMangaClick() }, + { icon: faFileImport, text: 'Import bibliothèque', href: '#' }, + { icon: faCompass, text: 'Découvrir', href: '#' }, + ] + }, + { + icon: faExchangeAlt, + text: 'Convertir CBR en CBZ', + href: '#' + }, + { + icon: faCalendar, + text: 'Calendrier', + href: '#' + }, + { + icon: faClockRotateLeft, + text: 'Activité', + href: '#', + badge: '3' + }, + { + icon: faCog, + text: 'Paramètres', + id: 'settings', + subItems: [ + { text: 'Général', href: '#' }, + { text: 'Dossiers', href: '#' }, + { text: 'Scrappers', href: '#' }, + { text: 'UI', href: '#' } + ] + }, + { + icon: faDesktop, + text: 'Système', + id: 'system', + subItems: [ + { text: 'Status', href: '#' }, + { text: 'Backup', href: '#' }, + { text: 'Logs', href: '#' }, + { text: 'Updates', href: '#' } + ] + }, + ]; + + const toggleMenu = (menuId) => { + setExpandedMenus(prev => ({ + ...prev, + [menuId]: !prev[menuId] + })); + }; + + const MenuItem = ({ item }) => { + const hasSubItems = item.subItems && item.subItems.length > 0; + const isExpanded = item.id && expandedMenus[item.id]; + const isActive = false; // À implémenter avec un vrai système de routing + + return ( +
+ + + {hasSubItems && isExpanded && ( +
+ {item.subItems.map((subItem, index) => ( + { + e.preventDefault(); + if (subItem.onClick) { + subItem.onClick(); + } else if (subItem.href !== '#') { + window.location.href = subItem.href; + } + }} + className="block py-2 text-gray-300 hover:text-green-600 transition-colors duration-150" + > + {subItem.icon && ( + + )} + {subItem.text} + + ))} +
+ )} +
+ ); + }; + + return ( + + ); +} \ No newline at end of file diff --git a/assets/react/app/presentation/components/Toolbar/Toolbar.jsx b/assets/react/app/presentation/components/Toolbar/Toolbar.jsx new file mode 100644 index 0000000..7bc33ce --- /dev/null +++ b/assets/react/app/presentation/components/Toolbar/Toolbar.jsx @@ -0,0 +1,29 @@ +import React from 'react'; +import { ToolbarButton } from './ToolbarButton'; + +export function Toolbar({ + leftSection = [], + centerSection = [], + rightSection = [], + className = '' +}) { + const renderSection = (items) => ( +
+ {items.map((item, index) => ( + + ))} +
+ ); + + return ( +
+
+
+ {renderSection(leftSection)} + {renderSection(centerSection)} + {renderSection(rightSection)} +
+
+
+ ); +} \ No newline at end of file diff --git a/assets/react/app/presentation/components/Toolbar/ToolbarButton.jsx b/assets/react/app/presentation/components/Toolbar/ToolbarButton.jsx new file mode 100644 index 0000000..6d6832c --- /dev/null +++ b/assets/react/app/presentation/components/Toolbar/ToolbarButton.jsx @@ -0,0 +1,20 @@ +import React from 'react'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; + +export function ToolbarButton({ icon, label, onClick, active = false }) { + return ( + + ); +} \ No newline at end of file diff --git a/assets/react/app/presentation/pages/AddMangaPage.jsx b/assets/react/app/presentation/pages/AddMangaPage.jsx new file mode 100644 index 0000000..efe65c0 --- /dev/null +++ b/assets/react/app/presentation/pages/AddMangaPage.jsx @@ -0,0 +1,185 @@ +import React, { useState, useEffect } from 'react'; +import { useNavigate, useSearchParams } from 'react-router-dom'; +import { Layout } from '../components/Layout/Layout'; +import { Toolbar } from '../components/Toolbar/Toolbar'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faArrowLeft, faSearch, faStar } from '@fortawesome/free-solid-svg-icons'; +import { MockMangaRepository } from '../../infrastructure/api/mockMangaRepository'; +import { SearchMangas } from '../../application/useCases/searchMangas'; + +const mangaRepository = new MockMangaRepository(); +const searchMangas = new SearchMangas(mangaRepository); + +export function AddMangaPage() { + const navigate = useNavigate(); + const [searchParams] = useSearchParams(); + const initialQuery = searchParams.get('q') || ''; + + const [query, setQuery] = useState(initialQuery); + const [results, setResults] = useState([]); + const [loading, setLoading] = useState(false); + const [selectedManga, setSelectedManga] = useState(null); + + useEffect(() => { + if (initialQuery) { + handleSearch(initialQuery); + } + }, [initialQuery]); + + const handleSearch = async (searchQuery) => { + if (!searchQuery.trim()) { + setResults([]); + return; + } + + setLoading(true); + try { + const searchResults = await searchMangas.execute(searchQuery); + setResults(searchResults); + } catch (error) { + console.error('Search error:', error); + } finally { + setLoading(false); + } + }; + + const handleMangaClick = (slug) => { + navigate(`/manga/${slug}`); + }; + + const handleAddMangaClick = (query = '') => { + navigate(`/add${query ? `?q=${encodeURIComponent(query)}` : ''}`); + }; + + const toolbarConfig = { + leftSection: [ + { icon: faArrowLeft, onClick: () => navigate(-1) } + ] + }; + + return ( + + + +
+

Ajouter un manga

+ +
+
+
+ { + setQuery(e.target.value); + handleSearch(e.target.value); + }} + placeholder="Rechercher un manga..." + className="w-full px-4 py-2 pl-10 border-b border-gray-300 focus:border-green-600 outline-none transition-colors" + /> + +
+ + {loading ? ( +
+
+
+ ) : results.length > 0 ? ( +
+ {results.map((manga) => ( +
setSelectedManga(manga)} + className="flex gap-4 p-4 bg-gray-50 rounded-lg hover:bg-gray-100 transition-colors cursor-pointer" + > + {manga.title} +
+
+
+

{manga.title}

+

{manga.author}

+
+
+ + {manga.rating} +
+
+
+ + {manga.publicationYear} • {manga.status} + +
+
+ {manga.genres.map((genre, index) => ( + + {genre} + + ))} +
+
+
+ ))} +
+ ) : query && !loading && ( +
+ Aucun résultat trouvé pour "{query}" +
+ )} +
+
+
+ + {/* Modal de confirmation */} + {selectedManga && ( +
+
+
+

Ajouter à la bibliothèque

+
+ {selectedManga.title} +
+

{selectedManga.title}

+

{selectedManga.author}

+

+ {selectedManga.genres.join(', ')} +

+
+
+
+ + +
+
+
+
+ )} +
+ ); +} \ No newline at end of file diff --git a/assets/react/app/presentation/pages/HomePage.jsx b/assets/react/app/presentation/pages/HomePage.jsx new file mode 100644 index 0000000..e88444f --- /dev/null +++ b/assets/react/app/presentation/pages/HomePage.jsx @@ -0,0 +1,80 @@ +import React, { useEffect, useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { MangaGrid } from '../components/MangaGrid.jsx'; +import { Layout } from '../components/Layout/Layout.jsx'; +import { Toolbar } from '../components/Toolbar/Toolbar.jsx'; +import { MockMangaRepository } from '../../infrastructure/api/mockMangaRepository.js'; +import { GetMangaCollection } from '../../application/useCases/getMangaCollection.js'; +import { + faRefresh, + faSearch, + faGear, + faEye, + faSort, + faFilter +} from '@fortawesome/free-solid-svg-icons'; + +const mangaRepository = new MockMangaRepository(); +const getMangaCollection = new GetMangaCollection(mangaRepository); + +export function HomePage() { + const navigate = useNavigate(); + const [mangaCollection, setMangaCollection] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + const handleRefresh = async () => { + setLoading(true); + try { + const collection = await getMangaCollection.execute(1); + setMangaCollection(collection); + } catch (err) { + setError('Failed to load mangas'); + console.error(err); + } finally { + setLoading(false); + } + }; + + useEffect(() => { + handleRefresh(); + }, []); + + const handleMangaClick = (slug) => { + navigate(`/manga/${slug}`); + }; + + const handleAddMangaClick = (query = '') => { + navigate(`/add${query ? `?q=${encodeURIComponent(query)}` : ''}`); + }; + + const toolbarConfig = { + leftSection: [ + { icon: faRefresh, label: 'Refresh', onClick: handleRefresh }, + { icon: faSearch, label: 'Search', onClick: () => {} } + ], + rightSection: [ + { icon: faGear, onClick: () => {} }, + { icon: faEye, onClick: () => {} }, + { icon: faSort, onClick: () => {} }, + { icon: faFilter, onClick: () => {} } + ] + }; + + if (loading) { + return
Loading...
; + } + + if (error) { + return
{error}
; + } + + return ( + + +
+ +
+
+ ); +} \ No newline at end of file diff --git a/assets/react/app/presentation/pages/MangaDetailPage.jsx b/assets/react/app/presentation/pages/MangaDetailPage.jsx new file mode 100644 index 0000000..5b39ff4 --- /dev/null +++ b/assets/react/app/presentation/pages/MangaDetailPage.jsx @@ -0,0 +1,183 @@ +import React, { useEffect, useState } from 'react'; +import { useParams, useNavigate } from 'react-router-dom'; +import { MockMangaRepository } from '../../infrastructure/api/mockMangaRepository.js'; +import { GetMangaDetail } from '../../application/useCases/getMangaDetail.js'; +import { Layout } from '../components/Layout/Layout.jsx'; +import { Toolbar } from '../components/Toolbar/Toolbar.jsx'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { + faStar, + faDownload, + faEye, + faArrowLeft, + faRefresh, + faBookmark, + faShare +} from '@fortawesome/free-solid-svg-icons'; + +const mangaRepository = new MockMangaRepository(); +const getMangaDetail = new GetMangaDetail(mangaRepository); + +export function MangaDetailPage() { + const { slug } = useParams(); + const navigate = useNavigate(); + const [manga, setManga] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + const handleRefresh = async () => { + setLoading(true); + try { + const mangaDetail = await getMangaDetail.execute(slug); + setManga(mangaDetail); + } catch (err) { + setError('Failed to load manga details'); + console.error(err); + } finally { + setLoading(false); + } + }; + + useEffect(() => { + handleRefresh(); + }, [slug]); + + const handleMangaClick = (mangaSlug) => { + navigate(`/manga/${mangaSlug}`); + }; + + const handleAddMangaClick = (query = '') => { + navigate(`/add${query ? `?q=${encodeURIComponent(query)}` : ''}`); + }; + + const toolbarConfig = { + leftSection: [ + { icon: faArrowLeft, onClick: () => navigate(-1) }, + { icon: faRefresh, onClick: handleRefresh } + ], + rightSection: [ + { icon: faBookmark, onClick: () => {} }, + { icon: faShare, onClick: () => {} }, + { icon: faDownload, onClick: () => {} } + ] + }; + + if (loading) { + return
Loading...
; + } + + if (error) { + return
{error}
; + } + + return ( + + + + {/* Hero section with manga info */} +
+
+ {manga.title} +
+
+ +
+
+
+

{manga.title}

+ + {manga.status} + +
+ +
+
+ + {manga.rating} +
+ {manga.publicationYear} + {manga.author} +
+ +
+ {manga.genres.map((genre, index) => ( + + {genre} + + ))} +
+ +

+ {manga.description} +

+
+
+
+ + {/* Chapters section */} +
+ {Array.from(manga.chapters.entries()).map(([volume, chapters]) => ( +
+

+ Volume {volume || 'Unknown'} + + ({chapters.length} chapters) + +

+ +
+ + + + + + + + + + + {chapters.map((chapter) => ( + + + + + + + ))} + +
+ # + + Title + + Added + + Actions +
+ {chapter.number} + + {chapter.title} + + {new Date(chapter.createdAt).toLocaleDateString()} + + + +
+
+
+ ))} +
+ + ); +} \ No newline at end of file diff --git a/composer.json b/composer.json index 3b04b95..074f48d 100644 --- a/composer.json +++ b/composer.json @@ -47,6 +47,7 @@ "symfony/stimulus-bundle": "^2.17", "symfony/twig-bundle": "7.0.*", "symfony/ux-live-component": "^2.17", + "symfony/ux-react": "^2.23", "symfony/ux-turbo": "^2.18", "symfony/validator": "7.0.*", "symfony/webpack-encore-bundle": "^2.1", diff --git a/composer.lock b/composer.lock index d131de6..b26ae21 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "e84863f24aa342f98beebd3cd364f698", + "content-hash": "e2253575e3f4a0344fe2ff59ac54aad0", "packages": [ { "name": "api-platform/core", @@ -8233,6 +8233,82 @@ ], "time": "2024-04-22T18:53:03+00:00" }, + { + "name": "symfony/ux-react", + "version": "v2.23.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/ux-react.git", + "reference": "9ca27a52c536994f48005ee70e2208d0769b2ae8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/ux-react/zipball/9ca27a52c536994f48005ee70e2208d0769b2ae8", + "reference": "9ca27a52c536994f48005ee70e2208d0769b2ae8", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/stimulus-bundle": "^2.9.1" + }, + "require-dev": { + "symfony/asset-mapper": "^6.3|^7.0", + "symfony/finder": "^5.4|^6.0|^7.0", + "symfony/framework-bundle": "^5.4|^6.0|^7.0", + "symfony/phpunit-bridge": "^5.4|^6.0|^7.0", + "symfony/twig-bundle": "^5.4|^6.0|^7.0", + "symfony/var-dumper": "^5.4|^6.0|^7.0" + }, + "type": "symfony-bundle", + "extra": { + "thanks": { + "url": "https://github.com/symfony/ux", + "name": "symfony/ux" + } + }, + "autoload": { + "psr-4": { + "Symfony\\UX\\React\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Titouan Galopin", + "email": "galopintitouan@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Integration of React in Symfony", + "homepage": "https://symfony.com", + "keywords": [ + "symfony-ux" + ], + "support": { + "source": "https://github.com/symfony/ux-react/tree/v2.23.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-12-05T14:25:02+00:00" + }, { "name": "symfony/ux-turbo", "version": "v2.18.0", diff --git a/config/bundles.php b/config/bundles.php index 24a8025..2aee5e1 100644 --- a/config/bundles.php +++ b/config/bundles.php @@ -21,4 +21,5 @@ return [ Symfony\Bundle\MercureBundle\MercureBundle::class => ['all' => true], Symfony\UX\Turbo\TurboBundle::class => ['all' => true], DAMA\DoctrineTestBundle\DAMADoctrineTestBundle::class => ['test' => true], + Symfony\UX\React\ReactBundle::class => ['all' => true], ]; diff --git a/config/routes.yaml b/config/routes.yaml index 41ef814..8790af5 100644 --- a/config/routes.yaml +++ b/config/routes.yaml @@ -3,3 +3,9 @@ controllers: path: ../src/Controller/ namespace: App\Controller type: attribute + +react_app: + path: /react + controller: Symfony\Bundle\FrameworkBundle\Controller\TemplateController + defaults: + template: 'react/index.html.twig' diff --git a/package-lock.json b/package-lock.json index e3d396a..0a352c0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,25 +7,33 @@ "license": "UNLICENSED", "dependencies": { "@fortawesome/fontawesome-free": "^6.5.2", + "@fortawesome/fontawesome-svg-core": "^6.7.2", + "@fortawesome/free-solid-svg-icons": "^6.7.2", + "@fortawesome/react-fontawesome": "^0.2.2", "alpinejs": "^3.13.3", "autoprefixer": "^10.4.14", "bootstrap": "^5.3.3", "postcss-loader": "^7.1.0", "puppeteer": "^22.10.0", + "react-router-dom": "^7.1.5", "sortablejs": "^1.15.2", "tailwindcss": "^3.2.7" }, "devDependencies": { "@babel/core": "^7.17.0", "@babel/preset-env": "^7.16.0", + "@babel/preset-react": "^7.26.3", "@hotwired/stimulus": "^3.0.0", "@hotwired/turbo": "^7.1.1 || ^8.0", "@symfony/stimulus-bridge": "^3.2.0", "@symfony/ux-live-component": "file:vendor/symfony/ux-live-component/assets", + "@symfony/ux-react": "file:vendor/symfony/ux-react/assets", "@symfony/ux-turbo": "file:vendor/symfony/ux-turbo/assets", "@symfony/webpack-encore": "^4.0.0", "core-js": "^3.23.0", "daisyui": "^4.4.2", + "react": "^18.0", + "react-dom": "^18.0", "regenerator-runtime": "^0.13.9", "sass": "^1.59.3", "sass-loader": "^13.2.0", @@ -60,11 +68,13 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.24.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.6.tgz", - "integrity": "sha512-ZJhac6FkEd1yhG2AHOmfcXG4ceoLltoCVJjN5XsWN9BifBQr+cHJbWi0h68HZuSORq+3WtJ2z0hwF2NG1b5kcA==", + "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/highlight": "^7.24.6", + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", "picocolors": "^1.0.0" }, "engines": { @@ -72,30 +82,33 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.24.6", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.6.tgz", - "integrity": "sha512-aC2DGhBq5eEdyXWqrDInSqQjO0k8xtPRf5YylULqx8MCd6jBtzqfta/3ETMRpuKIc5hyswfO80ObyA1MvkCcUQ==", + "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.24.6", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.6.tgz", - "integrity": "sha512-qAHSfAdVyFmIvl0VHELib8xar7ONuSHrE2hLnsaWkYNTI68dmi1x8GYDhJjMI/e7XWal9QBlZkwbOnkcw7Z8gQ==", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.8.tgz", + "integrity": "sha512-l+lkXCHS6tQEc5oUpK28xBOZ6+HwaH7YwoYQbLFiYb4nS2/l1tKnZEtEWkD0GuiYdvArf9qBS0XlQGXzPMsNqQ==", "dev": true, + "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.24.6", - "@babel/generator": "^7.24.6", - "@babel/helper-compilation-targets": "^7.24.6", - "@babel/helper-module-transforms": "^7.24.6", - "@babel/helpers": "^7.24.6", - "@babel/parser": "^7.24.6", - "@babel/template": "^7.24.6", - "@babel/traverse": "^7.24.6", - "@babel/types": "^7.24.6", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.8", + "@babel/helper-compilation-targets": "^7.26.5", + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helpers": "^7.26.7", + "@babel/parser": "^7.26.8", + "@babel/template": "^7.26.8", + "@babel/traverse": "^7.26.8", + "@babel/types": "^7.26.8", + "@types/gensync": "^1.0.0", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -111,27 +124,30 @@ } }, "node_modules/@babel/generator": { - "version": "7.24.6", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.6.tgz", - "integrity": "sha512-S7m4eNa6YAPJRHmKsLHIDJhNAGNKoWNiWefz1MBbpnt8g9lvMDl1hir4P9bo/57bQEmuwEhnRU/AMWsD0G/Fbg==", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.8.tgz", + "integrity": "sha512-ef383X5++iZHWAXX0SXQR6ZyQhw/0KtTkrTz61WXRhFM6dhpHulO/RJz79L8S6ugZHJkOOkUrUdxgdF2YiPFnA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/types": "^7.24.6", + "@babel/parser": "^7.26.8", + "@babel/types": "^7.26.8", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^2.5.1" + "jsesc": "^3.0.2" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.24.6", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.6.tgz", - "integrity": "sha512-DitEzDfOMnd13kZnDqns1ccmftwJTS9DMkyn9pYTxulS7bZxUxpMly3Nf23QQ6NwA4UB8lAqjbqWtyvElEMAkg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", + "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/types": "^7.24.6" + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -150,14 +166,15 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.24.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.6.tgz", - "integrity": "sha512-VZQ57UsDGlX/5fFA7GkVPplZhHsVc+vuErWgdOiysI9Ksnw0Pbbd6pnPiR/mmJyKHgyIW0c7KT32gmhiF+cirg==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz", + "integrity": "sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.24.6", - "@babel/helper-validator-option": "^7.24.6", - "browserslist": "^4.22.2", + "@babel/compat-data": "^7.26.5", + "@babel/helper-validator-option": "^7.25.9", + "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" }, @@ -268,28 +285,29 @@ } }, "node_modules/@babel/helper-module-imports": { - "version": "7.24.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.6.tgz", - "integrity": "sha512-a26dmxFJBF62rRO9mmpgrfTLsAuyHk4e1hKTUkD/fcMfynt8gvEKwQPQDVxWhca8dHoDck+55DFt42zV0QMw5g==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", + "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/types": "^7.24.6" + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.24.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.6.tgz", - "integrity": "sha512-Y/YMPm83mV2HJTbX1Qh2sjgjqcacvOlhbzdCCsSlblOKjSYmQqEbO6rUniWQyRo9ncyfjT8hnUjlG06RXDEmcA==", + "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-environment-visitor": "^7.24.6", - "@babel/helper-module-imports": "^7.24.6", - "@babel/helper-simple-access": "^7.24.6", - "@babel/helper-split-export-declaration": "^7.24.6", - "@babel/helper-validator-identifier": "^7.24.6" + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -311,10 +329,11 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.24.6", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.6.tgz", - "integrity": "sha512-MZG/JcWfxybKwsA9N9PmtF2lOSFSEMVCpIRrbxccZFLJPrJciJdG/UhSh5W96GEteJI2ARqm5UAHxISwRDLSNg==", + "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" } @@ -390,27 +409,30 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.24.6", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.6.tgz", - "integrity": "sha512-WdJjwMEkmBicq5T9fm/cHND3+UlFa2Yj8ALLgmoSQAJZysYbBjw+azChSGPN4DSPLXOcooGRvDwZWMcF/mLO2Q==", + "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==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.24.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.6.tgz", - "integrity": "sha512-4yA7s865JHaqUdRbnaxarZREuPTHrjpDT+pXoAZ1yhyo6uFnIEpS8VMu16siFOHDpZNKYv5BObhsB//ycbICyw==", + "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.24.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.6.tgz", - "integrity": "sha512-Jktc8KkF3zIkePb48QO+IapbXlSapOW9S+ogZZkcO6bABgYAxtZcjZ/O005111YLf+j4M84uEgwYoidDkXbCkQ==", + "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" } @@ -430,37 +452,28 @@ } }, "node_modules/@babel/helpers": { - "version": "7.24.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.6.tgz", - "integrity": "sha512-V2PI+NqnyFu1i0GyTd/O/cTpxzQCYioSkUIRmgo7gFEHKKCg5w46+r/A6WeUR1+P3TeQ49dspGPNd/E3n9AnnA==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.7.tgz", + "integrity": "sha512-8NHiL98vsi0mbPQmYAGWwfcFaOy4j2HY49fXJCfuDcdE7fMIsH9a7GdaeXpIBsbT7307WU8KCMp5pUVDNL4f9A==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/template": "^7.24.6", - "@babel/types": "^7.24.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.24.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.6.tgz", - "integrity": "sha512-2YnuOp4HAk2BsBrJJvYCbItHx0zWscI1C3zgWkz+wDyD9I7GIVrfnLyrR4Y1VR+7p+chAEcrgRQYZAGIKMV7vQ==", - "dependencies": { - "@babel/helper-validator-identifier": "^7.24.6", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" + "@babel/template": "^7.25.9", + "@babel/types": "^7.26.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.24.6", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.6.tgz", - "integrity": "sha512-eNZXdfU35nJC2h24RznROuOpO94h6x8sg9ju0tT9biNtLZ2vuP8SduLqqV+/8+cebSLV9SJEAN5Z3zQbJG/M+Q==", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.8.tgz", + "integrity": "sha512-TZIQ25pkSoaKEYYaHbbxkfL36GNsQ6iFiBbeuzAkLnXayKR1yP1zFe+NxuZWWsUyvt8icPU9CCq0sgWGXR1GEw==", "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.26.8" + }, "bin": { "parser": "bin/babel-parser.js" }, @@ -661,6 +674,22 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz", + "integrity": "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==", + "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-syntax-logical-assignment-operators": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", @@ -1380,6 +1409,107 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-react-display-name": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.25.9.tgz", + "integrity": "sha512-KJfMlYIUxQB1CJfO3e0+h0ZHWOTLCPP115Awhaz8U0Zpq36Gl/cXlpoyMRnUWlhNUBAzldnCiAZNvCDj7CrKxQ==", + "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": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.25.9.tgz", + "integrity": "sha512-s5XwpQYCqGerXl+Pu6VDL3x0j2d82eiV77UJ8a2mDHAW7j9SWRqQ2y1fNo1Z74CdcYipl5Z41zvjj4Nfzq36rw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/plugin-syntax-jsx": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-development": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.25.9.tgz", + "integrity": "sha512-9mj6rm7XVYs4mdLIpbZnHOYdpW42uoiBCTVowg7sP1thUOiANgMb4UtpRivR0pp5iL+ocvUv7X4mZgFRpJEzGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/plugin-transform-react-jsx": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.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/plugin-transform-react-pure-annotations": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.25.9.tgz", + "integrity": "sha512-KQ/Takk3T8Qzj5TppkS1be588lkbTp5uj7w6a0LeQaTMSckU/wK0oJ/pih+T690tkgI5jfmg2TqDJvd41Sj1Cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-transform-regenerator": { "version": "7.24.6", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.24.6.tgz", @@ -1659,6 +1789,27 @@ "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" } }, + "node_modules/@babel/preset-react": { + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.26.3.tgz", + "integrity": "sha512-Nl03d6T9ky516DGK2YMxrTqvnpUW63TnJMOMonj+Zae0JiPC5BC9xPMSL6L8fiSpA5vP88qfygavVQvnLp+6Cw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-validator-option": "^7.25.9", + "@babel/plugin-transform-react-display-name": "^7.25.9", + "@babel/plugin-transform-react-jsx": "^7.25.9", + "@babel/plugin-transform-react-jsx-development": "^7.25.9", + "@babel/plugin-transform-react-pure-annotations": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/regjsgen": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", @@ -1703,33 +1854,32 @@ "dev": true }, "node_modules/@babel/template": { - "version": "7.24.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.6.tgz", - "integrity": "sha512-3vgazJlLwNXi9jhrR1ef8qiB65L1RK90+lEQwv4OxveHnqC3BfmnHdgySwRLzf6akhlOYenT+b7AfWq+a//AHw==", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.8.tgz", + "integrity": "sha512-iNKaX3ZebKIsCvJ+0jd6embf+Aulaa3vNBqZ41kM7iTWjx5qzWKXGHiJUW3+nTpQ18SG11hdF8OAzKrpXkb96Q==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.24.6", - "@babel/parser": "^7.24.6", - "@babel/types": "^7.24.6" + "@babel/code-frame": "^7.26.2", + "@babel/parser": "^7.26.8", + "@babel/types": "^7.26.8" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.24.6", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.6.tgz", - "integrity": "sha512-OsNjaJwT9Zn8ozxcfoBc+RaHdj3gFmCmYoQLUII1o6ZrUwku0BMg80FoOTPx+Gi6XhcQxAYE4xyjPTo4SxEQqw==", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.8.tgz", + "integrity": "sha512-nic9tRkjYH0oB2dzr/JoGIm+4Q6SuYeLEiIiZDwBscRMYFJ+tMAz98fuel9ZnbXViA2I0HVSSRRK8DW5fjXStA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.24.6", - "@babel/generator": "^7.24.6", - "@babel/helper-environment-visitor": "^7.24.6", - "@babel/helper-function-name": "^7.24.6", - "@babel/helper-hoist-variables": "^7.24.6", - "@babel/helper-split-export-declaration": "^7.24.6", - "@babel/parser": "^7.24.6", - "@babel/types": "^7.24.6", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.8", + "@babel/parser": "^7.26.8", + "@babel/template": "^7.26.8", + "@babel/types": "^7.26.8", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -1738,14 +1888,14 @@ } }, "node_modules/@babel/types": { - "version": "7.24.6", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.6.tgz", - "integrity": "sha512-WaMsgi6Q8zMgMth93GvWPXkhAIEobfsIkLTacoVZoK1J0CevIPGYY2Vo5YvJGqyHqXM6P4ppOYGsIRU8MM9pFQ==", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.8.tgz", + "integrity": "sha512-eUuWapzEGWFEpHFxgEaBG8e3n6S8L3MSu0oda755rOfabWPnh0Our1AozNFVUxGFIhbKgd1ksprsoDGMinTOTA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.24.6", - "@babel/helper-validator-identifier": "^7.24.6", - "to-fast-properties": "^2.0.0" + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1760,6 +1910,465 @@ "node": ">=10.0.0" } }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz", + "integrity": "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.2.tgz", + "integrity": "sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.2.tgz", + "integrity": "sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.2.tgz", + "integrity": "sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.2.tgz", + "integrity": "sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.2.tgz", + "integrity": "sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.2.tgz", + "integrity": "sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.2.tgz", + "integrity": "sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.2.tgz", + "integrity": "sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.2.tgz", + "integrity": "sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.2.tgz", + "integrity": "sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.2.tgz", + "integrity": "sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.2.tgz", + "integrity": "sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.2.tgz", + "integrity": "sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.2.tgz", + "integrity": "sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.2.tgz", + "integrity": "sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.2.tgz", + "integrity": "sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.24.2.tgz", + "integrity": "sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.2.tgz", + "integrity": "sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.2.tgz", + "integrity": "sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.2.tgz", + "integrity": "sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.2.tgz", + "integrity": "sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.2.tgz", + "integrity": "sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.2.tgz", + "integrity": "sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.2.tgz", + "integrity": "sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@fortawesome/fontawesome-common-types": { + "version": "6.7.2", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.7.2.tgz", + "integrity": "sha512-Zs+YeHUC5fkt7Mg1l6XTniei3k4bwG/yo3iFUtZWd/pMx9g3fdvkSK9E0FOC+++phXOka78uJcYb8JaFkW52Xg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/@fortawesome/fontawesome-free": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.5.2.tgz", @@ -1769,6 +2378,43 @@ "node": ">=6" } }, + "node_modules/@fortawesome/fontawesome-svg-core": { + "version": "6.7.2", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.7.2.tgz", + "integrity": "sha512-yxtOBWDrdi5DD5o1pmVdq3WMCvnobT0LU6R8RyyVXPvFRd2o79/0NCuQoCjNTeZz9EzA9xS3JxNWfv54RIHFEA==", + "license": "MIT", + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.7.2" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/free-solid-svg-icons": { + "version": "6.7.2", + "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.7.2.tgz", + "integrity": "sha512-GsBrnOzU8uj0LECDfD5zomZJIjrPhIlWU82AHwa2s40FKH+kcxQaBvBo3Z4TxyZHIyX8XTDxsyA33/Vx9eFuQA==", + "license": "(CC-BY-4.0 AND MIT)", + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.7.2" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/react-fontawesome": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.2.2.tgz", + "integrity": "sha512-EnkrprPNqI6SXJl//m29hpaNzOp1bruISWaOiRtkMi/xSvHJlzc2j2JAYS7egxt/EbjSNV/k6Xy0AQI6vB2+1g==", + "license": "MIT", + "dependencies": { + "prop-types": "^15.8.1" + }, + "peerDependencies": { + "@fortawesome/fontawesome-svg-core": "~1 || ~6", + "react": ">=16.3" + } + }, "node_modules/@hotwired/stimulus": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/@hotwired/stimulus/-/stimulus-3.2.2.tgz", @@ -2154,6 +2800,291 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "license": "ISC" }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.6.tgz", + "integrity": "sha512-+GcCXtOQoWuC7hhX1P00LqjjIiS/iOouHXhMdiDSnq/1DGTox4SpUvO52Xm+div6+106r+TcvOeo/cxvyEyTgg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "peer": true + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.34.6.tgz", + "integrity": "sha512-E8+2qCIjciYUnCa1AiVF1BkRgqIGW9KzJeesQqVfyRITGQN+dFuoivO0hnro1DjT74wXLRZ7QF8MIbz+luGaJA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "peer": true + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.6.tgz", + "integrity": "sha512-z9Ib+OzqN3DZEjX7PDQMHEhtF+t6Mi2z/ueChQPLS/qUMKY7Ybn5A2ggFoKRNRh1q1T03YTQfBTQCJZiepESAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.6.tgz", + "integrity": "sha512-PShKVY4u0FDAR7jskyFIYVyHEPCPnIQY8s5OcXkdU8mz3Y7eXDJPdyM/ZWjkYdR2m0izD9HHWA8sGcXn+Qrsyg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.34.6.tgz", + "integrity": "sha512-YSwyOqlDAdKqs0iKuqvRHLN4SrD2TiswfoLfvYXseKbL47ht1grQpq46MSiQAx6rQEN8o8URtpXARCpqabqxGQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "peer": true + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.34.6.tgz", + "integrity": "sha512-HEP4CgPAY1RxXwwL5sPFv6BBM3tVeLnshF03HMhJYCNc6kvSqBgTMmsEjb72RkZBAWIqiPUyF1JpEBv5XT9wKQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "peer": true + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.34.6.tgz", + "integrity": "sha512-88fSzjC5xeH9S2Vg3rPgXJULkHcLYMkh8faix8DX4h4TIAL65ekwuQMA/g2CXq8W+NJC43V6fUpYZNjaX3+IIg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.34.6.tgz", + "integrity": "sha512-wM4ztnutBqYFyvNeR7Av+reWI/enK9tDOTKNF+6Kk2Q96k9bwhDDOlnCUNRPvromlVXo04riSliMBs/Z7RteEg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.6.tgz", + "integrity": "sha512-9RyprECbRa9zEjXLtvvshhw4CMrRa3K+0wcp3KME0zmBe1ILmvcVHnypZ/aIDXpRyfhSYSuN4EPdCCj5Du8FIA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.34.6.tgz", + "integrity": "sha512-qTmklhCTyaJSB05S+iSovfo++EwnIEZxHkzv5dep4qoszUMX5Ca4WM4zAVUMbfdviLgCSQOu5oU8YoGk1s6M9Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.34.6.tgz", + "integrity": "sha512-4Qmkaps9yqmpjY5pvpkfOerYgKNUGzQpFxV6rnS7c/JfYbDSU0y6WpbbredB5cCpLFGJEqYX40WUmxMkwhWCjw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.34.6.tgz", + "integrity": "sha512-Zsrtux3PuaxuBTX/zHdLaFmcofWGzaWW1scwLU3ZbW/X+hSsFbz9wDIp6XvnT7pzYRl9MezWqEqKy7ssmDEnuQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.34.6.tgz", + "integrity": "sha512-aK+Zp+CRM55iPrlyKiU3/zyhgzWBxLVrw2mwiQSYJRobCURb781+XstzvA8Gkjg/hbdQFuDw44aUOxVQFycrAg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.34.6.tgz", + "integrity": "sha512-WoKLVrY9ogmaYPXwTH326+ErlCIgMmsoRSx6bO+l68YgJnlOXhygDYSZe/qbUJCSiCiZAQ+tKm88NcWuUXqOzw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.6.tgz", + "integrity": "sha512-Sht4aFvmA4ToHd2vFzwMFaQCiYm2lDFho5rPcvPBT5pCdC+GwHG6CMch4GQfmWTQ1SwRKS0dhDYb54khSrjDWw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.34.6.tgz", + "integrity": "sha512-zmmpOQh8vXc2QITsnCiODCDGXFC8LMi64+/oPpPx5qz3pqv0s6x46ps4xoycfUiVZps5PFn1gksZzo4RGTKT+A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.34.6.tgz", + "integrity": "sha512-3/q1qUsO/tLqGBaD4uXsB6coVGB3usxw3qyeVb59aArCgedSF66MPdgRStUd7vbZOsko/CgVaY5fo2vkvPLWiA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.34.6.tgz", + "integrity": "sha512-oLHxuyywc6efdKVTxvc0135zPrRdtYVjtVD5GUm55I3ODxhU/PwkQFD97z16Xzxa1Fz0AEe4W/2hzRtd+IfpOA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.6.tgz", + "integrity": "sha512-0PVwmgzZ8+TZ9oGBmdZoQVXflbvuwzN/HRclujpl4N/q3i+y0lqLw8n1bXA8ru3sApDjlmONaNAuYr38y1Kr9w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true + }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -2183,6 +3114,10 @@ "resolved": "vendor/symfony/ux-live-component/assets", "link": true }, + "node_modules/@symfony/ux-react": { + "resolved": "vendor/symfony/ux-react/assets", + "link": true + }, "node_modules/@symfony/ux-turbo": { "resolved": "vendor/symfony/ux-turbo/assets", "link": true @@ -2557,6 +3492,51 @@ "integrity": "sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig==", "dev": true }, + "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.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", + "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.6", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", + "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.20.7" + } + }, "node_modules/@types/body-parser": { "version": "1.19.5", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", @@ -2595,6 +3575,12 @@ "@types/node": "*" } }, + "node_modules/@types/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", + "license": "MIT" + }, "node_modules/@types/eslint": { "version": "8.56.10", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.10.tgz", @@ -2614,9 +3600,10 @@ } }, "node_modules/@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "license": "MIT" }, "node_modules/@types/express": { "version": "4.17.21", @@ -2642,6 +3629,13 @@ "@types/send": "*" } }, + "node_modules/@types/gensync": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@types/gensync/-/gensync-1.0.4.tgz", + "integrity": "sha512-C3YYeRQWp2fmq9OryX+FoDy8nXS6scQ7dPptD8LnFDAUNcKWJjXQKDNJD3HVm+kOUsXhTOkpi69vI4EuAr95bA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", @@ -2735,6 +3729,13 @@ "@types/node": "*" } }, + "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==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/qs": { "version": "6.9.15", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.15.tgz", @@ -2747,6 +3748,27 @@ "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", "dev": true }, + "node_modules/@types/react": { + "version": "18.3.18", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.18.tgz", + "integrity": "sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.3.5", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.5.tgz", + "integrity": "sha512-P4t6saawp+b/dFrUr2cvkVsfvPguwsxtH6dNIYRllMsefqFzkZk5UIjzyDOv5g1dXIPdG4Sp1yCR4Z6RCUsG/Q==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^18.0.0" + } + }, "node_modules/@types/retry": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", @@ -2832,6 +3854,26 @@ "@types/node": "*" } }, + "node_modules/@vitejs/plugin-react": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.4.tgz", + "integrity": "sha512-SCCPBJtYLdE8PX/7ZQAs1QAZ8Jqwih+0VBLum1EGqmCCQal+MIUqLCzj3ZUy8ufbC0cAM4LRlSTm7IQJwWT4ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.26.0", + "@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.14.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0" + } + }, "node_modules/@vue/reactivity": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.1.5.tgz", @@ -3173,6 +4215,7 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, "dependencies": { "color-convert": "^1.9.0" }, @@ -3643,9 +4686,9 @@ } }, "node_modules/browserslist": { - "version": "4.23.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", - "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", "funding": [ { "type": "opencollective", @@ -3660,11 +4703,12 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001587", - "electron-to-chromium": "^1.4.668", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.13" + "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" @@ -3780,9 +4824,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001627", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001627.tgz", - "integrity": "sha512-4zgNiB8nTyV/tHhwZrFs88ryjls/lHiqFhrxCW4qSTeuRByBVnPYpDInchOIySWknznucaf31Z4KYqjfbrecVw==", + "version": "1.0.30001699", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001699.tgz", + "integrity": "sha512-b+uH5BakXZ9Do9iK+CkDmctUSEqZl+SP056vc5usa0PL+ev5OHw003rZXcnjNDv3L8P5j6rwT6C0BPKSikW08w==", "funding": [ { "type": "opencollective", @@ -3796,12 +4840,14 @@ "type": "github", "url": "https://github.com/sponsors/ai" } - ] + ], + "license": "CC-BY-4.0" }, "node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -3815,6 +4861,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, "engines": { "node": ">=0.8.0" } @@ -3976,6 +5023,7 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, "dependencies": { "color-name": "1.1.3" } @@ -3983,7 +5031,8 @@ "node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true }, "node_modules/colord": { "version": "2.9.3", @@ -4547,6 +5596,13 @@ "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", "dev": true }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "dev": true, + "license": "MIT" + }, "node_modules/culori": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/culori/-/culori-3.3.0.tgz", @@ -4814,9 +5870,10 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.4.788", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.788.tgz", - "integrity": "sha512-ubp5+Ev/VV8KuRoWnfP2QF2Bg+O2ZFdb49DiiNbz2VmgkIqrnyYaqIOqj8A6K/3p1xV0QcU5hBQ1+BmB6ot1OA==" + "version": "1.5.97", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.97.tgz", + "integrity": "sha512-HKLtaH02augM7ZOdYRuO19rWDeY+QSJ1VxnXFa/XDFLf07HvM90pALIJFgrO+UVaajI3+aJMMpojoUTLZyQ7JQ==", + "license": "ISC" }, "node_modules/emoji-regex": { "version": "8.0.0", @@ -4935,10 +5992,53 @@ "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.3.tgz", "integrity": "sha512-i1gCgmR9dCl6Vil6UKPI/trA69s08g/syhiDK9TG0Nf1RJjjFI+AzoWW7sPufzkgYAn861skuCwJa0pIIHYxvg==" }, + "node_modules/esbuild": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz", + "integrity": "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "peer": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.24.2", + "@esbuild/android-arm": "0.24.2", + "@esbuild/android-arm64": "0.24.2", + "@esbuild/android-x64": "0.24.2", + "@esbuild/darwin-arm64": "0.24.2", + "@esbuild/darwin-x64": "0.24.2", + "@esbuild/freebsd-arm64": "0.24.2", + "@esbuild/freebsd-x64": "0.24.2", + "@esbuild/linux-arm": "0.24.2", + "@esbuild/linux-arm64": "0.24.2", + "@esbuild/linux-ia32": "0.24.2", + "@esbuild/linux-loong64": "0.24.2", + "@esbuild/linux-mips64el": "0.24.2", + "@esbuild/linux-ppc64": "0.24.2", + "@esbuild/linux-riscv64": "0.24.2", + "@esbuild/linux-s390x": "0.24.2", + "@esbuild/linux-x64": "0.24.2", + "@esbuild/netbsd-arm64": "0.24.2", + "@esbuild/netbsd-x64": "0.24.2", + "@esbuild/openbsd-arm64": "0.24.2", + "@esbuild/openbsd-x64": "0.24.2", + "@esbuild/sunos-x64": "0.24.2", + "@esbuild/win32-arm64": "0.24.2", + "@esbuild/win32-ia32": "0.24.2", + "@esbuild/win32-x64": "0.24.2" + } + }, "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", "engines": { "node": ">=6" } @@ -5672,6 +6772,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, "engines": { "node": ">=4" } @@ -6454,7 +7555,8 @@ "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==" + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" }, "node_modules/js-yaml": { "version": "4.1.0", @@ -6474,15 +7576,16 @@ "license": "MIT" }, "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", "dev": true, + "license": "MIT", "bin": { "jsesc": "bin/jsesc" }, "engines": { - "node": ">=4" + "node": ">=6" } }, "node_modules/json-parse-even-better-errors": { @@ -6615,6 +7718,18 @@ "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", "dev": true }, + "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", @@ -6874,15 +7989,16 @@ } }, "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -6969,9 +8085,10 @@ } }, "node_modules/node-releases": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==" + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "license": "MIT" }, "node_modules/normalize-path": { "version": "3.0.0", @@ -7316,9 +8433,10 @@ "license": "MIT" }, "node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==" + "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", @@ -7458,9 +8576,9 @@ } }, "node_modules/postcss": { - "version": "8.4.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", - "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.2.tgz", + "integrity": "sha512-MjOadfU3Ys9KYoX0AdkBlFEF1Vx37uCCeN4ZHnmwm9FfpbsGWMZeBLMmmpY+6Ocqod7mkdZ0DT31OlbsFrLlkA==", "funding": [ { "type": "opencollective", @@ -7475,10 +8593,11 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "nanoid": "^3.3.7", - "picocolors": "^1.0.0", - "source-map-js": "^1.2.0" + "nanoid": "^3.3.8", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" @@ -8220,6 +9339,23 @@ "node": ">=0.4.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/prop-types/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/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -8453,12 +9589,96 @@ "node": ">= 0.8" } }, + "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-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-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "dev": true }, + "node_modules/react-refresh": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", + "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-router": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.1.5.tgz", + "integrity": "sha512-8BUF+hZEU4/z/JD201yK6S+UYhsf58bzYIDq2NS1iGpwxSXDu7F+DeGSkIXMFBuHZB21FSiCzEcUb18cQNdRkA==", + "license": "MIT", + "dependencies": { + "@types/cookie": "^0.6.0", + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0", + "turbo-stream": "2.4.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router-dom": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.1.5.tgz", + "integrity": "sha512-/4f9+up0Qv92D3bB8iN5P1s3oHAepSGa9h5k6tpTFlixTTskJZwKGhJ6vRJ277tLD1zuaZTt95hyGWV1Z37csQ==", + "license": "MIT", + "dependencies": { + "react-router": "7.1.5" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/react-router/node_modules/cookie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", + "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -8724,6 +9944,46 @@ "rimraf": "bin.js" } }, + "node_modules/rollup": { + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.34.6.tgz", + "integrity": "sha512-wc2cBWqJgkU3Iz5oztRkQbfVkbxoz5EhnCGOrnJvnLnQ7O0WhQUYyv18qQI79O8L7DdHrrlJNeCHd4VGpnaXKQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/estree": "1.0.6" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.34.6", + "@rollup/rollup-android-arm64": "4.34.6", + "@rollup/rollup-darwin-arm64": "4.34.6", + "@rollup/rollup-darwin-x64": "4.34.6", + "@rollup/rollup-freebsd-arm64": "4.34.6", + "@rollup/rollup-freebsd-x64": "4.34.6", + "@rollup/rollup-linux-arm-gnueabihf": "4.34.6", + "@rollup/rollup-linux-arm-musleabihf": "4.34.6", + "@rollup/rollup-linux-arm64-gnu": "4.34.6", + "@rollup/rollup-linux-arm64-musl": "4.34.6", + "@rollup/rollup-linux-loongarch64-gnu": "4.34.6", + "@rollup/rollup-linux-powerpc64le-gnu": "4.34.6", + "@rollup/rollup-linux-riscv64-gnu": "4.34.6", + "@rollup/rollup-linux-s390x-gnu": "4.34.6", + "@rollup/rollup-linux-x64-gnu": "4.34.6", + "@rollup/rollup-linux-x64-musl": "4.34.6", + "@rollup/rollup-win32-arm64-msvc": "4.34.6", + "@rollup/rollup-win32-ia32-msvc": "4.34.6", + "@rollup/rollup-win32-x64-msvc": "4.34.6", + "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", @@ -8825,6 +10085,15 @@ } } }, + "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/schema-utils": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", @@ -9016,6 +10285,12 @@ "node": ">= 0.8.0" } }, + "node_modules/set-cookie-parser": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", + "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", + "license": "MIT" + }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", @@ -9172,9 +10447,10 @@ } }, "node_modules/source-map-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "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" } @@ -9439,6 +10715,7 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, "dependencies": { "has-flag": "^3.0.0" }, @@ -9789,15 +11066,6 @@ "node": ">=14.14" } }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -9835,6 +11103,12 @@ "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", "license": "0BSD" }, + "node_modules/turbo-stream": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/turbo-stream/-/turbo-stream-2.4.0.tgz", + "integrity": "sha512-FHncC10WpBd2eOmGwpmQsWLDoK4cqsA/UT/GqNoaKOQnT8uzhtCbg3EoUDMvqpOSAI0S26mr0rkjzbOO6S3v1g==", + "license": "ISC" + }, "node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -9922,9 +11196,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz", - "integrity": "sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz", + "integrity": "sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==", "funding": [ { "type": "opencollective", @@ -9939,9 +11213,10 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "escalade": "^3.1.2", - "picocolors": "^1.0.1" + "escalade": "^3.2.0", + "picocolors": "^1.1.1" }, "bin": { "update-browserslist-db": "cli.js" @@ -10002,6 +11277,79 @@ "node": ">= 0.8" } }, + "node_modules/vite": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.1.0.tgz", + "integrity": "sha512-RjjMipCKVoR4hVfPY6GQTgveinjNuyLw+qruksLDvA5ktI1150VmcMBKmQaEWJhg/j6Uaf6dNCNA0AfdzUb/hQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "esbuild": "^0.24.2", + "postcss": "^8.5.1", + "rollup": "^4.30.1" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, "node_modules/watchpack": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.1.tgz", @@ -10666,6 +12014,26 @@ "@hotwired/stimulus": "^3.0.0" } }, + "vendor/symfony/ux-react/assets": { + "name": "@symfony/ux-react", + "version": "1.0.0", + "dev": true, + "license": "MIT", + "devDependencies": { + "@hotwired/stimulus": "^3.0.0", + "@types/react": "^18.0", + "@types/react-dom": "^18.0", + "@types/webpack-env": "^1.16", + "@vitejs/plugin-react": "^4.1.0", + "react": "^18.0", + "react-dom": "^18.0" + }, + "peerDependencies": { + "@hotwired/stimulus": "^3.0.0", + "react": "^18.0", + "react-dom": "^18.0" + } + }, "vendor/symfony/ux-turbo/assets": { "name": "@symfony/ux-turbo", "version": "0.1.0", diff --git a/package.json b/package.json index 3a90333..c9c1c6c 100644 --- a/package.json +++ b/package.json @@ -2,14 +2,18 @@ "devDependencies": { "@babel/core": "^7.17.0", "@babel/preset-env": "^7.16.0", + "@babel/preset-react": "^7.26.3", "@hotwired/stimulus": "^3.0.0", "@hotwired/turbo": "^7.1.1 || ^8.0", "@symfony/stimulus-bridge": "^3.2.0", "@symfony/ux-live-component": "file:vendor/symfony/ux-live-component/assets", + "@symfony/ux-react": "file:vendor/symfony/ux-react/assets", "@symfony/ux-turbo": "file:vendor/symfony/ux-turbo/assets", "@symfony/webpack-encore": "^4.0.0", "core-js": "^3.23.0", "daisyui": "^4.4.2", + "react": "^18.0", + "react-dom": "^18.0", "regenerator-runtime": "^0.13.9", "sass": "^1.59.3", "sass-loader": "^13.2.0", @@ -28,11 +32,15 @@ }, "dependencies": { "@fortawesome/fontawesome-free": "^6.5.2", + "@fortawesome/fontawesome-svg-core": "^6.7.2", + "@fortawesome/free-solid-svg-icons": "^6.7.2", + "@fortawesome/react-fontawesome": "^0.2.2", "alpinejs": "^3.13.3", "autoprefixer": "^10.4.14", "bootstrap": "^5.3.3", "postcss-loader": "^7.1.0", "puppeteer": "^22.10.0", + "react-router-dom": "^7.1.5", "sortablejs": "^1.15.2", "tailwindcss": "^3.2.7" } diff --git a/symfony.lock b/symfony.lock index 2a29c42..d0ae49a 100644 --- a/symfony.lock +++ b/symfony.lock @@ -279,6 +279,18 @@ "config/routes/ux_live_component.yaml" ] }, + "symfony/ux-react": { + "version": "2.23", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "2.9", + "ref": "e970076b31d602ae6e2106cf91a82c7e1f7ddff2" + }, + "files": [ + "assets/react/controllers/Hello.jsx" + ] + }, "symfony/ux-turbo": { "version": "v2.18.0" }, diff --git a/tailwind.config.js b/tailwind.config.js index ab82141..51b2456 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,7 +1,9 @@ /** @type {import('tailwindcss').Config} */ module.exports = { content: [ - './templates/**/*.html.twig' + './templates/**/*.html.twig', + './assets/react/**/*.{js,jsx}', + './assets/**/*.{js,jsx}' ], theme: { extend: {}, diff --git a/templates/react/index.html.twig b/templates/react/index.html.twig new file mode 100644 index 0000000..74bedc4 --- /dev/null +++ b/templates/react/index.html.twig @@ -0,0 +1,13 @@ + + + + + + React App + {{ encore_entry_link_tags('react-app') }} + + +
+ {{ encore_entry_script_tags('react-app') }} + + \ No newline at end of file diff --git a/webpack.config.js b/webpack.config.js index 5cac666..92480d3 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -21,6 +21,7 @@ Encore * and one CSS file (e.g. app.css) if your JavaScript imports CSS. */ .addEntry('app', './assets/app.js') + .addEntry('react-app', './assets/react/app/index.jsx') // .addEntry('alpine', 'alpinejs') // enables the Symfony UX Stimulus bridge (used in assets/bootstrap.js) @@ -29,6 +30,8 @@ Encore // When enabled, Webpack "splits" your files into smaller pieces for greater optimization. .splitEntryChunks() + .enableReactPreset() + // will require an extra script tag for runtime.js // but, you probably want this, unless you're building a single-page app .enableSingleRuntimeChunk()