1+ import { useState } from 'react' ;
2+ import { Card , CardContent , CardDescription , CardHeader , CardTitle } from '@/components/ui/card' ;
3+ import { Button } from '@/components/ui/button' ;
4+ import { Switch } from '@/components/ui/switch' ;
5+ import { Badge } from '@/components/ui/badge' ;
6+ import { Separator } from '@/components/ui/separator' ;
7+ import { Dialog , DialogContent , DialogDescription , DialogHeader , DialogTitle , DialogTrigger } from '@/components/ui/dialog' ;
8+ import { Alert , AlertDescription } from '@/components/ui/alert' ;
9+ import { useCookieConsent , CookiePreferences , CookieOptions } from '@/hooks/useCookieConsent' ;
10+ import { Shield , Cookie , Database , Clock , AlertTriangle , Download , Trash2 , Eye , Settings } from 'lucide-react' ;
11+ import { format } from 'date-fns' ;
12+ import { fr } from 'date-fns/locale' ;
13+
14+ export const PrivacyDashboard = ( ) => {
15+ const {
16+ preferences,
17+ options,
18+ updatePreferences,
19+ resetConsent,
20+ getConsentTimestamp,
21+ getExpiryDate
22+ } = useCookieConsent ( ) ;
23+
24+ const [ localPreferences , setLocalPreferences ] = useState < CookiePreferences > ( preferences ) ;
25+ const [ localOptions , setLocalOptions ] = useState < CookieOptions > ( options ) ;
26+ const [ showDataExport , setShowDataExport ] = useState ( false ) ;
27+ const [ showDataDeletion , setShowDataDeletion ] = useState ( false ) ;
28+
29+ const consentDate = getConsentTimestamp ( ) ;
30+ const expiryDate = getExpiryDate ( ) ;
31+
32+ const handleSavePreferences = ( ) => {
33+ updatePreferences ( localPreferences , localOptions ) ;
34+ } ;
35+
36+ const handleExportData = ( ) => {
37+ const data = {
38+ consentTimestamp : consentDate ?. toISOString ( ) ,
39+ preferences : preferences ,
40+ options : options ,
41+ exportDate : new Date ( ) . toISOString ( ) ,
42+ } ;
43+
44+ const blob = new Blob ( [ JSON . stringify ( data , null , 2 ) ] , { type : 'application/json' } ) ;
45+ const url = URL . createObjectURL ( blob ) ;
46+ const a = document . createElement ( 'a' ) ;
47+ a . href = url ;
48+ a . download = `refspring-privacy-data-${ format ( new Date ( ) , 'yyyy-MM-dd' ) } .json` ;
49+ document . body . appendChild ( a ) ;
50+ a . click ( ) ;
51+ document . body . removeChild ( a ) ;
52+ URL . revokeObjectURL ( url ) ;
53+ } ;
54+
55+ const cookieCategories = [
56+ {
57+ key : 'necessary' as keyof CookiePreferences ,
58+ title : 'Cookies Nécessaires' ,
59+ description : 'Essentiels au fonctionnement du site' ,
60+ icon : Shield ,
61+ required : true ,
62+ examples : [ 'Authentification' , 'Sécurité' , 'Navigation' ] ,
63+ } ,
64+ {
65+ key : 'analytics' as keyof CookiePreferences ,
66+ title : 'Cookies Analytiques' ,
67+ description : 'Nous aident à comprendre l\'usage du site' ,
68+ icon : Database ,
69+ required : false ,
70+ examples : [ 'Google Analytics' , 'Hotjar' , 'Statistiques' ] ,
71+ } ,
72+ {
73+ key : 'marketing' as keyof CookiePreferences ,
74+ title : 'Cookies Marketing' ,
75+ description : 'Personnalisent la publicité' ,
76+ icon : Eye ,
77+ required : false ,
78+ examples : [ 'Facebook Pixel' , 'Google Ads' , 'Retargeting' ] ,
79+ } ,
80+ {
81+ key : 'personalization' as keyof CookiePreferences ,
82+ title : 'Cookies Personnalisation' ,
83+ description : 'Améliorent votre expérience' ,
84+ icon : Settings ,
85+ required : false ,
86+ examples : [ 'Préférences UI' , 'Recommandations' , 'Langue' ] ,
87+ } ,
88+ ] ;
89+
90+ return (
91+ < div className = "space-y-6" >
92+ { /* En-tête du tableau de bord */ }
93+ < Card >
94+ < CardHeader >
95+ < div className = "flex items-center gap-3" >
96+ < Shield className = "h-6 w-6 text-primary" />
97+ < div >
98+ < CardTitle > Tableau de Bord Confidentialité</ CardTitle >
99+ < CardDescription >
100+ Gérez vos données personnelles et préférences de confidentialité
101+ </ CardDescription >
102+ </ div >
103+ </ div >
104+ </ CardHeader >
105+ < CardContent className = "space-y-4" >
106+ < div className = "grid grid-cols-1 md:grid-cols-3 gap-4" >
107+ < div className = "text-center p-4 bg-muted/50 rounded-lg" >
108+ < Clock className = "h-8 w-8 mx-auto text-muted-foreground mb-2" />
109+ < p className = "text-sm font-medium" > Consentement donné</ p >
110+ < p className = "text-xs text-muted-foreground" >
111+ { consentDate ? format ( consentDate , 'dd MMMM yyyy' , { locale : fr } ) : 'Jamais' }
112+ </ p >
113+ </ div >
114+ < div className = "text-center p-4 bg-muted/50 rounded-lg" >
115+ < AlertTriangle className = "h-8 w-8 mx-auto text-muted-foreground mb-2" />
116+ < p className = "text-sm font-medium" > Expire le</ p >
117+ < p className = "text-xs text-muted-foreground" >
118+ { expiryDate ? format ( expiryDate , 'dd MMMM yyyy' , { locale : fr } ) : 'Jamais' }
119+ </ p >
120+ </ div >
121+ < div className = "text-center p-4 bg-muted/50 rounded-lg" >
122+ < Cookie className = "h-8 w-8 mx-auto text-muted-foreground mb-2" />
123+ < p className = "text-sm font-medium" > Cookies actifs</ p >
124+ < p className = "text-xs text-muted-foreground" >
125+ { Object . values ( preferences ) . filter ( Boolean ) . length } / { Object . keys ( preferences ) . length }
126+ </ p >
127+ </ div >
128+ </ div >
129+ </ CardContent >
130+ </ Card >
131+
132+ { /* Gestion des cookies par catégorie */ }
133+ < Card >
134+ < CardHeader >
135+ < CardTitle > Préférences des Cookies</ CardTitle >
136+ < CardDescription >
137+ Contrôlez quels types de cookies vous souhaitez autoriser
138+ </ CardDescription >
139+ </ CardHeader >
140+ < CardContent className = "space-y-6" >
141+ { cookieCategories . map ( ( category , index ) => (
142+ < div key = { category . key } >
143+ < div className = "flex items-center justify-between" >
144+ < div className = "flex items-start gap-3 flex-1" >
145+ < category . icon className = "h-5 w-5 mt-1 text-muted-foreground" />
146+ < div className = "space-y-1 flex-1" >
147+ < div className = "flex items-center gap-2" >
148+ < h3 className = "font-medium" > { category . title } </ h3 >
149+ { category . required && (
150+ < Badge variant = "secondary" className = "text-xs" >
151+ Requis
152+ </ Badge >
153+ ) }
154+ </ div >
155+ < p className = "text-sm text-muted-foreground" >
156+ { category . description }
157+ </ p >
158+ < div className = "flex flex-wrap gap-1 mt-2" >
159+ { category . examples . map ( ( example ) => (
160+ < Badge key = { example } variant = "outline" className = "text-xs" >
161+ { example }
162+ </ Badge >
163+ ) ) }
164+ </ div >
165+ </ div >
166+ </ div >
167+ < Switch
168+ checked = { localPreferences [ category . key ] }
169+ onCheckedChange = { ( checked ) =>
170+ setLocalPreferences ( prev => ( {
171+ ...prev ,
172+ [ category . key ] : checked
173+ } ) )
174+ }
175+ disabled = { category . required }
176+ />
177+ </ div >
178+ { index < cookieCategories . length - 1 && < Separator className = "mt-6" /> }
179+ </ div >
180+ ) ) }
181+
182+ < div className = "pt-4 space-y-4" >
183+ < Separator />
184+ < div className = "space-y-4" >
185+ < h3 className = "font-medium" > Options Avancées</ h3 >
186+
187+ < div className = "flex items-center justify-between" >
188+ < div >
189+ < p className = "text-sm font-medium" > Mode Strict</ p >
190+ < p className = "text-xs text-muted-foreground" >
191+ Bloque automatiquement les nouveaux cookies
192+ </ p >
193+ </ div >
194+ < Switch
195+ checked = { localOptions . strictMode }
196+ onCheckedChange = { ( checked ) =>
197+ setLocalOptions ( prev => ( { ...prev , strictMode : checked } ) )
198+ }
199+ />
200+ </ div >
201+
202+ < div className = "flex items-center justify-between" >
203+ < div >
204+ < p className = "text-sm font-medium" > Expiration Automatique</ p >
205+ < p className = "text-xs text-muted-foreground" >
206+ Redemander le consentement après 1 an
207+ </ p >
208+ </ div >
209+ < Switch
210+ checked = { localOptions . autoExpiry }
211+ onCheckedChange = { ( checked ) =>
212+ setLocalOptions ( prev => ( { ...prev , autoExpiry : checked } ) )
213+ }
214+ />
215+ </ div >
216+
217+ < div className = "flex items-center justify-between" >
218+ < div >
219+ < p className = "text-sm font-medium" > Opt-out du Tracking</ p >
220+ < p className = "text-xs text-muted-foreground" >
221+ Envoie le signal "Do Not Track"
222+ </ p >
223+ </ div >
224+ < Switch
225+ checked = { localOptions . trackingOptOut }
226+ onCheckedChange = { ( checked ) =>
227+ setLocalOptions ( prev => ( { ...prev , trackingOptOut : checked } ) )
228+ }
229+ />
230+ </ div >
231+ </ div >
232+
233+ < Button onClick = { handleSavePreferences } className = "w-full" >
234+ Sauvegarder les Préférences
235+ </ Button >
236+ </ div >
237+ </ CardContent >
238+ </ Card >
239+
240+ { /* Actions sur les données */ }
241+ < Card >
242+ < CardHeader >
243+ < CardTitle > Vos Droits sur les Données</ CardTitle >
244+ < CardDescription >
245+ Exercez vos droits RGPD concernant vos données personnelles
246+ </ CardDescription >
247+ </ CardHeader >
248+ < CardContent className = "space-y-4" >
249+ < div className = "grid grid-cols-1 md:grid-cols-3 gap-4" >
250+ < Dialog open = { showDataExport } onOpenChange = { setShowDataExport } >
251+ < DialogTrigger asChild >
252+ < Button variant = "outline" className = "h-auto p-4 flex flex-col items-center gap-2" >
253+ < Download className = "h-6 w-6" />
254+ < div className = "text-center" >
255+ < p className = "font-medium" > Exporter mes données</ p >
256+ < p className = "text-xs text-muted-foreground" > Télécharger vos données</ p >
257+ </ div >
258+ </ Button >
259+ </ DialogTrigger >
260+ < DialogContent >
261+ < DialogHeader >
262+ < DialogTitle > Exporter vos données</ DialogTitle >
263+ < DialogDescription >
264+ Téléchargez toutes les données que nous avons collectées à votre sujet.
265+ </ DialogDescription >
266+ </ DialogHeader >
267+ < div className = "space-y-4" >
268+ < Alert >
269+ < AlertTriangle className = "h-4 w-4" />
270+ < AlertDescription >
271+ Le fichier contiendra vos préférences de confidentialité et métadonnées de consentement.
272+ </ AlertDescription >
273+ </ Alert >
274+ < Button onClick = { handleExportData } className = "w-full" >
275+ < Download className = "h-4 w-4 mr-2" />
276+ Télécharger mes données
277+ </ Button >
278+ </ div >
279+ </ DialogContent >
280+ </ Dialog >
281+
282+ < Dialog open = { showDataDeletion } onOpenChange = { setShowDataDeletion } >
283+ < DialogTrigger asChild >
284+ < Button variant = "outline" className = "h-auto p-4 flex flex-col items-center gap-2" >
285+ < Trash2 className = "h-6 w-6" />
286+ < div className = "text-center" >
287+ < p className = "font-medium" > Supprimer mes données</ p >
288+ < p className = "text-xs text-muted-foreground" > Droit à l'effacement</ p >
289+ </ div >
290+ </ Button >
291+ </ DialogTrigger >
292+ < DialogContent >
293+ < DialogHeader >
294+ < DialogTitle > Supprimer vos données</ DialogTitle >
295+ < DialogDescription >
296+ Cette action supprimera toutes vos préférences de confidentialité.
297+ </ DialogDescription >
298+ </ DialogHeader >
299+ < div className = "space-y-4" >
300+ < Alert >
301+ < AlertTriangle className = "h-4 w-4" />
302+ < AlertDescription >
303+ Attention : Cette action est irréversible. Vos préférences seront perdues.
304+ </ AlertDescription >
305+ </ Alert >
306+ < Button
307+ variant = "destructive"
308+ onClick = { ( ) => {
309+ resetConsent ( ) ;
310+ setShowDataDeletion ( false ) ;
311+ } }
312+ className = "w-full"
313+ >
314+ < Trash2 className = "h-4 w-4 mr-2" />
315+ Confirmer la suppression
316+ </ Button >
317+ </ div >
318+ </ DialogContent >
319+ </ Dialog >
320+
321+ < Button variant = "outline" className = "h-auto p-4 flex flex-col items-center gap-2" >
322+ < Shield className = "h-6 w-6" />
323+ < div className = "text-center" >
324+ < p className = "font-medium" > Nous contacter</ p >
325+ < p className = "text-xs text-muted-foreground" > Questions sur vos données</ p >
326+ </ div >
327+ </ Button >
328+ </ div >
329+ </ CardContent >
330+ </ Card >
331+ </ div >
332+ ) ;
333+ } ;
0 commit comments