Conformité RGPD (P3) + transparence éditoriale (P4)

P3 — RGPD :
- Table `consents` + `POST /api/consent` (art. 7.1 — preuve du consentement)
- Dialogue de consentement explicite avant la première contribution (art. 9.2.a)
- Pages `/mentions-legales` et `/politique-confidentialite`
- `docs/RGPD.md` — registre des traitements, bases légales, sous-traitants
- `getVisitorId()` exporté depuis l'API client React

P4 — Transparence éditoriale :
- Page `/contributions-brutes` avec pagination et export JSON/CSV
- `GET /api/contributions`, `GET /api/contributions/export/{json,csv}`
- `GET /api/stats/public` — stats publiques sans données de rejet
- Label de transparence IA sur la colonne de synthèse
- Compteurs (acceptées / soumises) dans le bandeau d'intro
- `docs/PROMPTS_IA.md` — prompts intégraux publiés + analyse des biais
- Pied de page avec liens légaux et transparence

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-23 22:30:30 +02:00
parent 45edc1fa77
commit a7b7684e87
12 changed files with 1354 additions and 50 deletions
+34 -1
View File
@@ -1,6 +1,6 @@
// Copyright (C) 2026 billisdead — Licence EUPL-1.2
import React from "react";
import { Switch, Route, Router as WouterRouter, Link } from "wouter";
import { Switch, Route, Router as WouterRouter, Link, useLocation } from "wouter";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { Toaster } from "@/components/ui/toaster";
import { TooltipProvider } from "@/components/ui/tooltip";
@@ -10,6 +10,9 @@ import About from "@/pages/about";
import Transparence from "@/pages/transparence";
import Flyer from "@/pages/flyer";
import Admin from "@/pages/admin";
import LegalNotice from "@/pages/legal-notice";
import PrivacyPolicy from "@/pages/privacy-policy";
import ContributionsBrutes from "@/pages/contributions-brutes";
import { AccessibilityProvider } from "@/hooks/use-accessibility";
import { AccessibilityPanel } from "@/components/accessibility-panel";
import { setVisitorId } from "@workspace/api-client-react";
@@ -55,7 +58,32 @@ function Navbar() {
);
}
function Footer() {
return (
<footer className="border-t border-border/40 bg-background py-5">
<div className="container mx-auto max-w-7xl px-4 md:px-8">
<div className="flex flex-col sm:flex-row gap-3 sm:gap-6 items-start sm:items-center justify-between text-xs text-muted-foreground font-mono">
<span className="font-serif font-semibold text-foreground/70 text-sm">La Voix du Peuple</span>
<nav className="flex flex-wrap gap-4" aria-label="Liens légaux et transparence">
<Link href="/mentions-legales" className="hover:text-foreground transition-colors">
Mentions légales
</Link>
<Link href="/politique-confidentialite" className="hover:text-foreground transition-colors">
Confidentialité
</Link>
<Link href="/contributions-brutes" className="hover:text-foreground transition-colors">
Contributions brutes
</Link>
</nav>
<span className="opacity-50">EUPL-1.2 · billisdead</span>
</div>
</div>
</footer>
);
}
function Router() {
const [location] = useLocation();
return (
<div className="min-h-screen flex flex-col font-sans">
{/* Lien d'évitement pour lecteurs d'écran */}
@@ -73,9 +101,14 @@ function Router() {
<Route path="/transparence" component={Transparence} />
<Route path="/flyer" component={Flyer} />
<Route path="/admin" component={Admin} />
<Route path="/mentions-legales" component={LegalNotice} />
<Route path="/politique-confidentialite" component={PrivacyPolicy} />
<Route path="/contributions-brutes" component={ContributionsBrutes} />
<Route component={NotFound} />
</Switch>
</main>
{/* Pied de page masqué sur la page d'accueil (layout plein-écran) */}
{location !== "/" && <Footer />}
</div>
);
}