diff --git a/apps/web/package.json b/apps/web/package.json index a8377ba..f94db83 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -25,6 +25,7 @@ "@fullcalendar/react": "^6.1.17", "@fullcalendar/timegrid": "^6.1.17", "@hookform/resolvers": "^3.9.0", + "@radix-ui/react-accordion": "^1.2.12", "@radix-ui/react-avatar": "^1.1.3", "@radix-ui/react-checkbox": "^1.1.3", "@radix-ui/react-dialog": "^1.1.7", diff --git a/apps/web/src/features/profile/athlete-profile-section.tsx b/apps/web/src/features/profile/athlete-profile-section.tsx index 8888b51..4b986d1 100644 --- a/apps/web/src/features/profile/athlete-profile-section.tsx +++ b/apps/web/src/features/profile/athlete-profile-section.tsx @@ -225,7 +225,7 @@ export function AthleteProfileSection() { if (isLoading) { return ( - + @@ -241,7 +241,7 @@ export function AthleteProfileSection() { // No athlete profile if (!athlete && !isCreating) { return ( - + {t('profile:athlete.title')} @@ -264,7 +264,7 @@ export function AthleteProfileSection() { return ( <> - + {t('profile:athlete.title')} {!isEditing && !isCreating && ( diff --git a/apps/web/src/routes/__home.athletes.$athleteId.tsx b/apps/web/src/routes/__home.athletes.$athleteId.tsx index b249c9a..6340723 100644 --- a/apps/web/src/routes/__home.athletes.$athleteId.tsx +++ b/apps/web/src/routes/__home.athletes.$athleteId.tsx @@ -16,6 +16,7 @@ import { useQuery, useQueryClient } from '@tanstack/react-query'; import { createFileRoute } from '@tanstack/react-router'; import { useEffect, useState } from 'react'; import { useForm } from 'react-hook-form'; +import { ScrollArea } from '@/shared/components/ui/scroll-area'; export const Route = createFileRoute('/__home/athletes/$athleteId')({ component: AthleteDetailPage, @@ -255,21 +256,23 @@ function AthleteDetailPage() { } return ( -
- -
+ +
+ +
+
); } diff --git a/apps/web/src/routes/__home.help.tsx b/apps/web/src/routes/__home.help.tsx index 98cd534..0d83591 100644 --- a/apps/web/src/routes/__home.help.tsx +++ b/apps/web/src/routes/__home.help.tsx @@ -1,19 +1,336 @@ -import { createFileRoute } from '@tanstack/react-router' -import { useTranslation } from '@dropit/i18n' -import { usePageMeta } from '../shared/hooks/use-page-meta' -import { useEffect } from 'react' +import { createFileRoute } from '@tanstack/react-router'; +import { useTranslation } from '@dropit/i18n'; +import { usePageMeta } from '../shared/hooks/use-page-meta'; +import { useEffect, useState } from 'react'; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/shared/components/ui/card'; +import { Button } from '@/shared/components/ui/button'; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, +} from '@/shared/components/ui/dialog'; +import { ScrollArea } from '@/shared/components/ui/scroll-area'; +import { Mail, FileText, Shield, HelpCircle } from 'lucide-react'; +import { + Accordion, + AccordionContent, + AccordionItem, + AccordionTrigger, +} from '@/shared/components/ui/accordion'; export const Route = createFileRoute('/__home/help')({ component: RouteComponent, -}) +}); function RouteComponent() { - const { t } = useTranslation() - const { setPageMeta } = usePageMeta() + const { t } = useTranslation(['common', 'privacy', 'auth']); + const { setPageMeta } = usePageMeta(); + const [privacyDialogOpen, setPrivacyDialogOpen] = useState(false); + const [termsDialogOpen, setTermsDialogOpen] = useState(false); useEffect(() => { - setPageMeta({ title: t('sidebar.menu.help') }) - }, [setPageMeta, t]) + setPageMeta({ title: t('help.title') }); + }, [setPageMeta, t]); - return
Hello "/__home/help"!
+ return ( + +
+
+ {/* Header */} +
+

{t('help.title')}

+

{t('help.description')}

+
+ +
+ {/* Contact Section */} + + +
+ + {t('help.contact.title')} +
+ {t('help.contact.description')} +
+ +
+

+ {t('help.contact.email.label')} +

+

{t('help.contact.email.value')}

+ +
+

+ {t('help.contact.response_time')} +

+
+
+ + {/* Legal Documents Section */} + + +
+ + {t('help.legal.title')} +
+ {t('help.legal.description')} +
+ +
+
+
+ +
+

{t('help.legal.privacy.title')}

+

+ {t('help.legal.privacy.description')} +

+
+
+ +
+ +
+
+ +
+

{t('help.legal.terms.title')}

+

+ {t('help.legal.terms.description')} +

+
+
+ +
+
+
+
+
+ + {/* FAQ Section */} + + +
+ + {t('help.faq.title')} +
+
+ + + + {t('help.faq.items.account.question')} + + {t('help.faq.items.account.answer')} + + + + + {t('help.faq.items.athlete.question')} + + {t('help.faq.items.athlete.answer')} + + + + + {t('help.faq.items.data.question')} + + {t('help.faq.items.data.answer')} + + + + + {t('help.faq.items.support.question')} + + {t('help.faq.items.support.answer')} + + + + +
+ + {/* Privacy Policy Dialog */} + + + + {t('help.dialogs.privacy.title')} + + + + + + {/* Terms Dialog */} + + + + {t('help.dialogs.terms.title')} + + + + +
+
+
+ ); +} + +// Privacy Policy Content Component +function PrivacyContent() { + const { t } = useTranslation(['privacy']); + + type ProcessingPurpose = { + purpose: string; + data: string; + reason: string; + }; + + type GdprRight = { + name: string; + description: string; + howTo: string; + }; + + return ( +
+

{t('lastUpdated')}

+

{t('introduction')}

+ + {/* Data Controller */} +
+

{t('sections.responsable.title')}

+

{t('sections.responsable.content')}

+
+

{t('sections.responsable.name')}

+

{t('sections.responsable.email')}

+
+
+ + {/* Personal Data Collected */} +
+

{t('sections.donneesCollectees.title')}

+

{t('sections.donneesCollectees.intro')}

+
+
+

+ {t('sections.donneesCollectees.categories.compte.title')} +

+
    + {( + t('sections.donneesCollectees.categories.compte.items', { + returnObjects: true, + }) as string[] + ).map((item: string) => ( +
  • {item}
  • + ))} +
+
+
+
+ + {/* Processing Purposes */} +
+

{t('sections.finalites.title')}

+
+ {( + t('sections.finalites.items', { returnObjects: true }) as ProcessingPurpose[] + ).map((item) => ( +
+

{item.purpose}

+

Données: {item.data}

+
+ ))} +
+
+ + {/* GDPR Rights */} +
+

{t('sections.droits.title')}

+

{t('sections.droits.intro')}

+
+ {(t('sections.droits.rights', { returnObjects: true }) as GdprRight[]).map((right) => ( +
+

{right.name}

+

{right.description}

+
+ ))} +
+
+ + {/* Contact */} +
+

{t('contact.title')}

+

{t('contact.description')}

+

{t('contact.email')}

+
+
+ ); +} + +// Terms Content Component +function TermsContent() { + const { t } = useTranslation(['auth']); + + return ( +
+

{t('terms.lastUpdated')}

+ +
+
+

+ {t('terms.sections.introduction.title')} +

+

{t('terms.sections.introduction.description')}

+
+ +
+

+ {t('terms.sections.usingOurService.title')} +

+

{t('terms.sections.usingOurService.description')}

+
+ +
+

{t('terms.sections.yourAccount.title')}

+

{t('terms.sections.yourAccount.description')}

+
+ +
+

+ {t('terms.sections.privacyAndCopyright.title')} +

+

{t('terms.sections.privacyAndCopyright.description')}

+
+ +
+

+ {t('terms.sections.modifyingAndTerminating.title')} +

+

{t('terms.sections.modifyingAndTerminating.description')}

+
+ +
+

+ {t('terms.sections.warrantiesAndDisclaimers.title')} +

+

{t('terms.sections.warrantiesAndDisclaimers.description')}

+
+
+
+ ); } diff --git a/apps/web/src/routes/__home.profile.tsx b/apps/web/src/routes/__home.profile.tsx index 84d7a88..61103e1 100644 --- a/apps/web/src/routes/__home.profile.tsx +++ b/apps/web/src/routes/__home.profile.tsx @@ -20,11 +20,11 @@ function RouteComponent() { }, [setPageMeta, t]); return ( -
-
+
+
{/* Left Column: User Profile + Danger Zone */} -
- +
+
@@ -35,7 +35,7 @@ function RouteComponent() {
{/* Right Column: Athlete Profile */} -
+
diff --git a/apps/web/src/shared/components/ui/accordion.tsx b/apps/web/src/shared/components/ui/accordion.tsx new file mode 100644 index 0000000..e1797c9 --- /dev/null +++ b/apps/web/src/shared/components/ui/accordion.tsx @@ -0,0 +1,55 @@ +import * as React from "react" +import * as AccordionPrimitive from "@radix-ui/react-accordion" +import { ChevronDown } from "lucide-react" + +import { cn } from "@/lib/utils" + +const Accordion = AccordionPrimitive.Root + +const AccordionItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AccordionItem.displayName = "AccordionItem" + +const AccordionTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + svg]:rotate-180", + className + )} + {...props} + > + {children} + + + +)) +AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName + +const AccordionContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + +
{children}
+
+)) +AccordionContent.displayName = AccordionPrimitive.Content.displayName + +export { Accordion, AccordionItem, AccordionTrigger, AccordionContent } diff --git a/apps/web/tailwind.config.js b/apps/web/tailwind.config.js index 1ac2848..a9c9628 100644 --- a/apps/web/tailwind.config.js +++ b/apps/web/tailwind.config.js @@ -3,83 +3,105 @@ export default { darkMode: ['class'], content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'], theme: { - extend: { - borderRadius: { - lg: 'var(--radius)', - md: 'calc(var(--radius) - 2px)', - sm: 'calc(var(--radius) - 4px)', - }, - colors: { - background: 'hsl(var(--background))', - foreground: 'hsl(var(--foreground))', - card: { - DEFAULT: 'hsl(var(--card))', - foreground: 'hsl(var(--card-foreground))', - }, - popover: { - DEFAULT: 'hsl(var(--popover))', - foreground: 'hsl(var(--popover-foreground))', - }, - primary: { - DEFAULT: 'hsl(var(--primary))', - foreground: 'hsl(var(--primary-foreground))', - }, - secondary: { - DEFAULT: 'hsl(var(--secondary))', - foreground: 'hsl(var(--secondary-foreground))', - }, - tertiary: { - DEFAULT: 'hsl(var(--tertiary))', - foreground: 'hsl(var(--tertiary-foreground))', - }, - muted: { - DEFAULT: 'hsl(var(--muted))', - foreground: 'hsl(var(--muted-foreground))', - }, - accent: { - DEFAULT: 'hsl(var(--accent))', - foreground: 'hsl(var(--accent-foreground))', - }, - destructive: { - DEFAULT: 'hsl(var(--destructive))', - foreground: 'hsl(var(--destructive-foreground))', - }, - border: 'hsl(var(--border))', - input: 'hsl(var(--input))', - ring: 'hsl(var(--ring))', - outlet: 'hsl(var(--outlet))', - chart: { - 1: 'hsl(var(--chart-1))', - 2: 'hsl(var(--chart-2))', - 3: 'hsl(var(--chart-3))', - 4: 'hsl(var(--chart-4))', - 5: 'hsl(var(--chart-5))', - }, - sidebar: { - DEFAULT: 'hsl(var(--sidebar-background))', - foreground: 'hsl(var(--sidebar-foreground))', - primary: 'hsl(var(--sidebar-primary))', - 'primary-foreground': 'hsl(var(--sidebar-primary-foreground))', - accent: 'hsl(var(--sidebar-accent))', - 'accent-foreground': 'hsl(var(--sidebar-accent-foreground))', - border: 'hsl(var(--sidebar-border))', - ring: 'hsl(var(--sidebar-ring))', - }, - purple: { - 50: 'hsl(256, 100%, 97%)', - 100: 'hsl(256, 100%, 94%)', - 200: 'hsl(256, 100%, 91%)', - 300: 'hsl(256, 100%, 88%)', - 400: 'hsl(256, 100%, 86%)', - 500: 'hsl(256, 100%, 85%)', - 600: 'hsl(256, 100%, 75%)', - 700: 'hsl(256, 100%, 65%)', - 800: 'hsl(256, 95%, 55%)', - 900: 'hsl(256, 90%, 45%)', - 950: 'hsl(256, 85%, 30%)', - }, - }, - }, + extend: { + borderRadius: { + lg: 'var(--radius)', + md: 'calc(var(--radius) - 2px)', + sm: 'calc(var(--radius) - 4px)' + }, + colors: { + background: 'hsl(var(--background))', + foreground: 'hsl(var(--foreground))', + card: { + DEFAULT: 'hsl(var(--card))', + foreground: 'hsl(var(--card-foreground))' + }, + popover: { + DEFAULT: 'hsl(var(--popover))', + foreground: 'hsl(var(--popover-foreground))' + }, + primary: { + DEFAULT: 'hsl(var(--primary))', + foreground: 'hsl(var(--primary-foreground))' + }, + secondary: { + DEFAULT: 'hsl(var(--secondary))', + foreground: 'hsl(var(--secondary-foreground))' + }, + tertiary: { + DEFAULT: 'hsl(var(--tertiary))', + foreground: 'hsl(var(--tertiary-foreground))' + }, + muted: { + DEFAULT: 'hsl(var(--muted))', + foreground: 'hsl(var(--muted-foreground))' + }, + accent: { + DEFAULT: 'hsl(var(--accent))', + foreground: 'hsl(var(--accent-foreground))' + }, + destructive: { + DEFAULT: 'hsl(var(--destructive))', + foreground: 'hsl(var(--destructive-foreground))' + }, + border: 'hsl(var(--border))', + input: 'hsl(var(--input))', + ring: 'hsl(var(--ring))', + outlet: 'hsl(var(--outlet))', + chart: { + '1': 'hsl(var(--chart-1))', + '2': 'hsl(var(--chart-2))', + '3': 'hsl(var(--chart-3))', + '4': 'hsl(var(--chart-4))', + '5': 'hsl(var(--chart-5))' + }, + sidebar: { + DEFAULT: 'hsl(var(--sidebar-background))', + foreground: 'hsl(var(--sidebar-foreground))', + primary: 'hsl(var(--sidebar-primary))', + 'primary-foreground': 'hsl(var(--sidebar-primary-foreground))', + accent: 'hsl(var(--sidebar-accent))', + 'accent-foreground': 'hsl(var(--sidebar-accent-foreground))', + border: 'hsl(var(--sidebar-border))', + ring: 'hsl(var(--sidebar-ring))' + }, + purple: { + '50': 'hsl(256, 100%, 97%)', + '100': 'hsl(256, 100%, 94%)', + '200': 'hsl(256, 100%, 91%)', + '300': 'hsl(256, 100%, 88%)', + '400': 'hsl(256, 100%, 86%)', + '500': 'hsl(256, 100%, 85%)', + '600': 'hsl(256, 100%, 75%)', + '700': 'hsl(256, 100%, 65%)', + '800': 'hsl(256, 95%, 55%)', + '900': 'hsl(256, 90%, 45%)', + '950': 'hsl(256, 85%, 30%)' + } + }, + keyframes: { + 'accordion-down': { + from: { + height: '0' + }, + to: { + height: 'var(--radix-accordion-content-height)' + } + }, + 'accordion-up': { + from: { + height: 'var(--radix-accordion-content-height)' + }, + to: { + height: '0' + } + } + }, + animation: { + 'accordion-down': 'accordion-down 0.2s ease-out', + 'accordion-up': 'accordion-up 0.2s ease-out' + } + } }, plugins: [require('tailwindcss-animate')], }; diff --git a/packages/i18n/src/locales/en/common.json b/packages/i18n/src/locales/en/common.json index 630e8bd..22d602f 100644 --- a/packages/i18n/src/locales/en/common.json +++ b/packages/i18n/src/locales/en/common.json @@ -162,5 +162,64 @@ "description": "Description", "description_placeholder": "Enter organization description" } + }, + "help": { + "title": "Help Center", + "description": "Need assistance? Check our resources or contact us", + "contact": { + "title": "Contact Us", + "description": "Our team is here to help", + "email": { + "label": "Support Email", + "value": "levasseur.sten@gmail.com", + "action": "Send an email" + }, + "response_time": "We typically respond within 24-48 hours" + }, + "legal": { + "title": "Legal Documents", + "description": "Review our policies and terms of use", + "privacy": { + "title": "Privacy Policy", + "description": "Learn how we protect your personal data in compliance with GDPR", + "action": "View policy" + }, + "terms": { + "title": "Terms of Use", + "description": "Review DropIt's general terms and conditions", + "action": "View terms" + } + }, + "faq": { + "title": "Frequently Asked Questions", + "items": { + "account": { + "question": "How can I update my personal information?", + "answer": "Go to your profile via the user menu in the top right, then click on 'Profile'. You can update your name and email there." + }, + "athlete": { + "question": "How do I create an athlete profile?", + "answer": "In your profile, you'll find an 'Athlete Profile' section. If you don't have one yet, click on 'Create my athlete profile' and fill in the required information." + }, + "data": { + "question": "How can I delete my data?", + "answer": "In your profile, in the 'Danger Zone' section, you can delete your athlete profile or your complete account. Warning: these actions are irreversible." + }, + "support": { + "question": "How can I get additional help?", + "answer": "Feel free to contact us by email at levasseur.sten@gmail.com. Our team will respond as soon as possible." + } + } + }, + "dialogs": { + "privacy": { + "title": "Privacy Policy", + "close": "Close" + }, + "terms": { + "title": "Terms of Use", + "close": "Close" + } + } } } diff --git a/packages/i18n/src/locales/fr/common.json b/packages/i18n/src/locales/fr/common.json index d58e2d1..b637393 100644 --- a/packages/i18n/src/locales/fr/common.json +++ b/packages/i18n/src/locales/fr/common.json @@ -132,5 +132,64 @@ "greeting": "Bonjour {{name}} !", "description": "Bienvenue sur votre tableau de bord. Gérez vos athlètes, programmez vos séances et suivez leurs performances." } + }, + "help": { + "title": "Centre d'aide", + "description": "Besoin d'assistance ? Consultez nos ressources ou contactez-nous", + "contact": { + "title": "Nous contacter", + "description": "Notre équipe est là pour vous aider", + "email": { + "label": "Email de support", + "value": "levasseur.sten@gmail.com", + "action": "Envoyer un email" + }, + "response_time": "Nous répondons généralement sous 24-48h" + }, + "legal": { + "title": "Documents légaux", + "description": "Consultez nos politiques et conditions d'utilisation", + "privacy": { + "title": "Politique de confidentialité", + "description": "Découvrez comment nous protégeons vos données personnelles conformément au RGPD", + "action": "Voir la politique" + }, + "terms": { + "title": "Conditions d'utilisation", + "description": "Consultez les conditions générales d'utilisation de DropIt", + "action": "Voir les conditions" + } + }, + "faq": { + "title": "Questions fréquentes", + "items": { + "account": { + "question": "Comment puis-je modifier mes informations personnelles ?", + "answer": "Rendez-vous dans votre profil via le menu utilisateur en haut à droite, puis cliquez sur 'Profil'. Vous pourrez y modifier votre nom et votre email." + }, + "athlete": { + "question": "Comment créer un profil athlète ?", + "answer": "Dans votre profil, vous trouverez une section 'Profil Athlète'. Si vous n'en avez pas encore, cliquez sur 'Créer mon profil athlète' et remplissez les informations demandées." + }, + "data": { + "question": "Comment supprimer mes données ?", + "answer": "Dans votre profil, dans la section 'Zone de danger', vous pouvez supprimer votre profil athlète ou votre compte complet. Attention, ces actions sont irréversibles." + }, + "support": { + "question": "Comment obtenir de l'aide supplémentaire ?", + "answer": "N'hésitez pas à nous contacter par email à levasseur.sten@gmail.com. Notre équipe vous répondra dans les plus brefs délais." + } + } + }, + "dialogs": { + "privacy": { + "title": "Politique de confidentialité", + "close": "Fermer" + }, + "terms": { + "title": "Conditions d'utilisation", + "close": "Fermer" + } + } } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cbbaa7e..b0c3317 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -265,6 +265,9 @@ importers: '@hookform/resolvers': specifier: ^3.9.0 version: 3.10.0(react-hook-form@7.63.0(react@19.0.0)) + '@radix-ui/react-accordion': + specifier: ^1.2.12 + version: 1.2.12(@types/react-dom@19.2.0(@types/react@19.2.0))(@types/react@19.2.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) '@radix-ui/react-avatar': specifier: ^1.1.3 version: 1.1.10(@types/react-dom@19.2.0(@types/react@19.2.0))(@types/react@19.2.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) @@ -2090,6 +2093,19 @@ packages: '@radix-ui/primitive@1.1.3': resolution: {integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==} + '@radix-ui/react-accordion@1.2.12': + resolution: {integrity: sha512-T4nygeh9YE9dLRPhAHSeOZi7HBXo+0kYIPJXayZfvWOWA0+n3dESrZbjfDPUABkUNym6Hd+f2IR113To8D2GPA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-arrow@1.1.7': resolution: {integrity: sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==} peerDependencies: @@ -2129,6 +2145,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-collapsible@1.1.12': + resolution: {integrity: sha512-Uu+mSh4agx2ib1uIGPP4/CKNULyajb3p92LsVXmH2EHVMTfZWpll88XJ0j4W0z3f8NK1eYl1+Mf/szHPmcHzyA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-collection@1.1.7': resolution: {integrity: sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==} peerDependencies: @@ -9420,6 +9449,23 @@ snapshots: '@radix-ui/primitive@1.1.3': {} + '@radix-ui/react-accordion@1.2.12(@types/react-dom@19.2.0(@types/react@19.2.0))(@types/react@19.2.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collapsible': 1.1.12(@types/react-dom@19.2.0(@types/react@19.2.0))(@types/react@19.2.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.0(@types/react@19.2.0))(@types/react@19.2.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.0)(react@19.0.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.0)(react@19.0.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.0)(react@19.0.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.0)(react@19.0.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.0(@types/react@19.2.0))(@types/react@19.2.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.0)(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.2.0 + '@types/react-dom': 19.2.0(@types/react@19.2.0) + '@radix-ui/react-arrow@1.1.7(@types/react-dom@19.2.0(@types/react@19.2.0))(@types/react@19.2.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.0(@types/react@19.2.0))(@types/react@19.2.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) @@ -9458,6 +9504,22 @@ snapshots: '@types/react': 19.2.0 '@types/react-dom': 19.2.0(@types/react@19.2.0) + '@radix-ui/react-collapsible@1.1.12(@types/react-dom@19.2.0(@types/react@19.2.0))(@types/react@19.2.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.0)(react@19.0.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.0)(react@19.0.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.0)(react@19.0.0) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.0(@types/react@19.2.0))(@types/react@19.2.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.0(@types/react@19.2.0))(@types/react@19.2.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.0)(react@19.0.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.0)(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.2.0 + '@types/react-dom': 19.2.0(@types/react@19.2.0) + '@radix-ui/react-collection@1.1.7(@types/react-dom@19.2.0(@types/react@19.2.0))(@types/react@19.2.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.0)(react@19.0.0)