import { useEffect, useRef, useState } from 'react'; export function useWebSocket(url: string) { const [lastMessage, setLastMessage] = useState(null); const ws = useRef(null); useEffect(() => { ws.current = new WebSocket(url); ws.current.onmessage = (event) => setLastMessage(JSON.parse(event.data)); return () => ws.current?.close(); }, [url]); const send = (payload: any) => ws.current?.send(JSON.stringify(payload)); return { lastMessage, send }; } import React from 'react'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Switch } from '@/components/ui/switch'; import { Label } from '@/components/ui/label'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; export default function RGPDPage() { const deleteAccount = async () => { if (confirm('Droits à l’oubli : êtes-vous sûr ? Cette action est irréversible.')) { await fetch('/api/delete-account', { method: 'DELETE' }); window.location.href = '/'; } }; return (
Préférences linguistiques Notifications push Droit à l’oubli

Vous pouvez demander la suppression définitive de toutes vos données.

); } import React, { useRef, useEffect } from 'react'; import * as d3 from 'd3'; import { Card } from '@/components/ui/card'; const data = { nodes: [ { id: 'user:1', label: 'Alice' }, { id: 'contrib:42', label: 'Contribution #42' }, { id: 'tag:ai', label: 'IA' } ], links: [ { source: 'user:1', target: 'contrib:42' }, { source: 'contrib:42', target: 'tag:ai' } ] }; export default function GraphViewerPage() { const ref = useRef(null); useEffect(() => { if (!ref.current) return; const svg = d3.select(ref.current); svg.selectAll('*').remove(); const width = 600; const height = 400; const simulation = d3.forceSimulation(data.nodes as any) .force('link', d3.forceLink(data.links).id((d: any) => d.id)) .force('charge', d3.forceManyBody().strength(-200)) .force('center', d3.forceCenter(width / 2, height / 2)); const link = svg.append('g') .selectAll('line') .data(data.links) .enter() .append('line') .attr('stroke', '#999'); const node = svg.append('g') .selectAll('circle') .data(data.nodes) .enter() .append('circle') .attr('r', 10) .attr('fill', '#69b3a2') .call(d3.drag() .on('start', (event, d: any) => { if (!event.active) simulation.alphaTarget(0.3).restart(); d.fx = d.x; d.fy = d.y; }) .on('drag', (event, d: any) => { d.fx = event.x; d.fy = event.y; }) .on('end', (event, d: any) => { if (!event.active) simulation.alphaTarget(0); d.fx = null; d.fy = null; })); const label = svg.append('g') .selectAll('text') .data(data.nodes) .enter() .append('text') .text((d: any) => d.label) .attr('font-size', 12) .attr('dx', 12) .attr('dy', 4); simulation.on('tick', () => { link .attr('x1', (d: any) => d.source.x) .attr('y1', (d: any) => d.source.y) .attr('x2', (d: any) => d.target.x) .attr('y2', (d: any) => d.target.y); node .attr('cx', (d: any) => d.x) .attr('cy', (d: any) => d.y); label .attr('x', (d: any) => d.x) .attr('y', (d: any) => d.y); }); }, []); return (
); } import React, { useEffect, useState } from 'react'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { useWebSocket } from '@/components/useWebSocket'; export default function DashboardPage() { const [score, setScore] = useState(0); const { lastMessage } = useWebSocket('wss://api.example.com/notifs'); useEffect(() => { fetch('/api/score').then(r => r.json()).then(d => setScore(d.score)); }, []); useEffect(() => { if (lastMessage?.type === 'SCORE_UPDATE') setScore(lastMessage.payload); }, [lastMessage]); return (
Score d'impact
{score}
{score > 50 ? 'Excellent' : 'À améliorer'}
Notifications {lastMessage ? (

{lastMessage.type}: {JSON.stringify(lastMessage.payload)}

) : (

Aucune nouvelle notification.

)}
); } import React, { useState } from 'react'; import { Button } from '@/components/ui/button'; import { Progress } from '@/components/ui/progress'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Input } from '@/components/ui/input'; import { Textarea } from '@/components/ui/textarea'; import { Label } from '@/components/ui/label'; import { useWebSocket } from '@/components/useWebSocket'; const steps = ['Type de contribution', 'Contenu', 'Résumé & envoi']; export default function HomePage() { const [step, setStep] = useState(0); const [data, setData] = useState({ type: 'text', content: '', metadata: '' }); const { send } = useWebSocket('wss://api.example.com/notifs'); const next = () => setStep(s => s + 1); const prev = () => setStep(s => s - 1); const submit = () => { fetch('/api/submit-data', { method: 'POST', body: JSON.stringify(data), headers: { 'Content-Type': 'application/json' } }).then(() => send({ type: 'NEW_CONTRIBUTION' })); }; return (
{steps[step]} {step === 0 && ( <> )} {step === 1 && ( <>