'use client'; import { useRef } from 'react'; import { motion, useInView } from 'framer-motion'; import { useTranslations } from 'next-intl'; import { Shield, Eye, Lock, UserCheck, Database, Globe, Mail, Calendar, Building2, Sparkles, Share2, Scale, type LucideIcon, } from 'lucide-react'; import { LandingHeader, LandingFooter } from '@/components/layout'; import { CONTROLLER, SUBPROCESSORS, SUPERVISORY_AUTHORITY } from '@/lib/legal/processing'; /** * Politique de confidentialite. * * La version precedente citait le Privacy Shield comme garantie de transfert — * un mecanisme invalide par la CJUE en juillet 2020 — ne nommait aucun * sous-traitant, ne donnait aucune base legale et passait sous silence le * traitement par un modele de langage. Le texte vit dans les messages ; la * liste des destinataires vient de `lib/legal/processing`, la meme source que * la page de conformite de l'administration, pour que les deux ne puissent pas * diverger. */ const SECTION_KEYS = [ 'controller', 'data', 'purposes', 'ai', 'recipients', 'transfers', 'retention', 'rights', 'security', ] as const; type SectionKey = (typeof SECTION_KEYS)[number]; const ICONS: Record = { controller: Building2, data: Database, purposes: Eye, ai: Sparkles, recipients: Share2, transfers: Globe, retention: Calendar, rights: UserCheck, security: Lock, }; function renderInlineBold(content: string) { return content .split('**') .map((part, i) => (i % 2 === 1 ? {part} : part)); } export default function PrivacyPage() { const t = useTranslations('marketing.privacy'); const tCommon = useTranslations('marketing.common'); const heroRef = useRef(null); const contentRef = useRef(null); const isHeroInView = useInView(heroRef, { once: true }); const isContentInView = useInView(contentRef, { once: true }); const containerVariants = { hidden: { opacity: 0, y: 50 }, visible: { opacity: 1, y: 0, transition: { duration: 0.6, staggerChildren: 0.1 } }, }; const itemVariants = { hidden: { opacity: 0, y: 20 }, visible: { opacity: 1, y: 0, transition: { duration: 0.5 } }, }; // Seuls les champs renseignes sont publies : mieux vaut une mention // manquante, que la console d'administration signale, qu'une mention fausse. const identityRows = [ ['tradingName', CONTROLLER.tradingName], ['legalName', CONTROLLER.legalName], ['registrationNumber', CONTROLLER.registrationNumber], ['address', CONTROLLER.address], ['contact', CONTROLLER.privacyContact], ['dpo', CONTROLLER.dpo], ].filter(([, value]) => Boolean(value)) as [string, string][]; return (
{t('badge')}

{t('title1')}
{t('title2')}

{t('intro')}

{tCommon('lastUpdated')}

{/* Identite du responsable : la premiere chose que doit trouver quelqu'un qui veut savoir a qui s'adresser (art. 13.1.a). */} {identityRows.length > 0 && ( {identityRows.map(([key, value]) => (
{t(`identity.${key}`)}
{key === 'contact' ? ( {value} ) : ( value )}
))}
)} {SECTION_KEYS.map(key => { const IconComponent = ICONS[key]; return (

{t(`sections.${key}.title`)}

{renderInlineBold(t(`sections.${key}.content`))}
{key === 'recipients' && }
); })}
{/* Droit de reclamation (art. 13.2.d) : absent de la version precedente, alors qu'il doit etre porte a la connaissance. */}

{t('complaint.title')}

{t('complaint.body')}

{t('complaint.cta')}

{t('contact.title')}

{t('contact.body')}

{CONTROLLER.privacyContact}
); } /** * Registre des sous-traitants. * * Un tableau plutot qu'une liste a puces : la question qu'on se pose devant * cette section est « qui recoit quoi, et ou », et elle se lit en colonnes. */ function SubprocessorTable() { const t = useTranslations('marketing.privacy.recipientsTable'); const tProcessor = useTranslations('marketing.privacy.processors'); return (
{(['name', 'role', 'data', 'location', 'basis'] as const).map(column => ( ))} {SUBPROCESSORS.map(processor => ( ))}
{t('caption')}
{t(column)}
{processor.name} {tProcessor(`${processor.key}.role`)} {processor.conditional && ( {tProcessor(`${processor.key}.conditional`)} )} {tProcessor(`${processor.key}.data`)} {tProcessor(`${processor.key}.location`)} {t(`bases.${processor.transferBasis}`)}
); }