Files
la-voix-du-peuple/artifacts/voix-du-peuple/src/pages/transparence.tsx
T
pironantoine a3d06f6c53 Update website colors and add disclaimers about expression
Modify CSS variables for a politically neutral color palette and inject disclaimer text across multiple pages to clarify that the platform captures citizen expression rather than established truth.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 923ae0e3-a363-4db8-b04a-e8baca2a1330
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: a6455e51-215a-43d1-a452-a445436b0317
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/8af7d2ec-2cc3-4ece-8af3-9f071488d072/923ae0e3-a363-4db8-b04a-e8baca2a1330/UL3T8eF
Replit-Helium-Checkpoint-Created: true
2026-04-04 11:32:08 +00:00

305 lines
17 KiB
TypeScript

import React from "react";
import { Link } from "wouter";
import { ArrowLeft, Bot, Filter, FileText, Database, AlertTriangle, CheckCircle, XCircle, ArrowRight } from "lucide-react";
import { Button } from "@/components/ui/button";
export default function Transparence() {
return (
<div className="min-h-screen bg-background">
<div className="container mx-auto max-w-3xl px-4 md:px-8 py-12">
<Link href="/">
<Button variant="ghost" className="mb-8 -ml-3 text-muted-foreground">
<ArrowLeft className="mr-2 h-4 w-4" /> Retour
</Button>
</Link>
<header className="mb-12 space-y-4">
<h1 className="text-4xl md:text-5xl font-serif font-bold text-primary">
Comment ça fonctionne
</h1>
<p className="text-xl text-muted-foreground leading-relaxed">
Cette page décrit précisément le rôle de l'intelligence artificielle dans cette plateforme, les données collectées et les limites du système.
</p>
</header>
<div className="space-y-14">
{/* Flux général */}
<section className="space-y-6">
<div className="flex items-center gap-3 text-xl font-bold font-serif border-b border-border/50 pb-2">
<ArrowRight className="h-5 w-5 text-primary" />
<h2>Le parcours d'une contribution</h2>
</div>
<p className="text-foreground/90 leading-relaxed">
Quand vous soumettez une proposition, elle suit quatre étapes automatiques avant d'être visible et intégrée à la synthèse.
</p>
<ol className="space-y-4">
{[
{
num: "1",
titre: "Réception",
desc: "Votre texte est enregistré en base de données avec la date et l'heure. Le pseudonyme est facultatif. Aucune donnée d'identification (adresse IP, compte utilisateur) n'est associée à la contribution.",
},
{
num: "2",
titre: "Filtre de modération (IA)",
desc: "Un modèle de langage (GPT) analyse le texte pour vérifier sa conformité avec les droits fondamentaux reconnus par le droit international. Voir la section dédiée ci-dessous.",
},
{
num: "3",
titre: "Mise à jour de la synthèse (IA)",
desc: "Si la contribution est acceptée, un second modèle relit l'ensemble des contributions retenues et produit un résumé structuré, destiné à être transmis à des représentants politiques.",
},
{
num: "4",
titre: "Affichage",
desc: "La contribution apparaît dans le fil des contributions récentes. La synthèse mise à jour s'affiche immédiatement dans la colonne de droite.",
},
].map((step) => (
<li key={step.num} className="flex gap-4">
<span className="flex-shrink-0 w-7 h-7 rounded-full bg-primary text-primary-foreground flex items-center justify-center text-sm font-bold font-mono">
{step.num}
</span>
<div className="space-y-1">
<p className="font-semibold">{step.titre}</p>
<p className="text-foreground/80 leading-relaxed text-sm">{step.desc}</p>
</div>
</li>
))}
</ol>
</section>
{/* Filtre IA */}
<section className="space-y-6">
<div className="flex items-center gap-3 text-xl font-bold font-serif border-b border-border/50 pb-2">
<Filter className="h-5 w-5 text-primary" />
<h2>L'IA de modération</h2>
</div>
<div className="space-y-4">
<div className="bg-muted/40 border border-border/50 p-5 space-y-2">
<p className="font-mono text-xs font-semibold uppercase tracking-widest text-primary">Modèle utilisé</p>
<p className="text-foreground/90 text-sm leading-relaxed">
Mistral Small par défaut (configurable via la variable d'environnement <code className="bg-muted px-1 py-0.5 rounded text-xs">FILTER_MODEL</code>). Le modèle est interrogé via l'API Mistral AI — aucun fine-tuning ni entraînement n'est effectué sur les contributions des utilisateurs.
</p>
</div>
<div>
<p className="font-semibold mb-2">Ce que le filtre vérifie</p>
<p className="text-foreground/80 text-sm leading-relaxed mb-3">
Le modèle reçoit le texte de la contribution et une liste de critères fondés sur les textes internationaux suivants :
</p>
<ul className="text-sm space-y-1.5 text-foreground/80">
{[
"DUDH (ONU, 1948) Art. 1, 2, 7, 19, 20",
"PIDCP (ONU, 1966) Art. 20, 25, 26",
"CEDH (Conseil de l'Europe, 1950) — Art. 10, 14, 17",
"Charte des droits fondamentaux de l'UE (2000)",
"Convention internationale sur l'élimination de la discrimination raciale — CERD (ONU, 1965)",
"Statut de Rome (CPI, 1998) — incitation au génocide",
"Convention contre la torture (ONU, 1984)",
].map((t) => (
<li key={t} className="flex items-start gap-2">
<span className="text-primary mt-0.5">•</span>
<span>{t}</span>
</li>
))}
</ul>
</div>
<div>
<p className="font-semibold mb-2">Ce qui est refusé</p>
<ul className="text-sm space-y-1.5 text-foreground/80">
{[
"Appels à la haine ou à la discrimination (raciale, religieuse, sexuelle, etc.)",
"Incitation à la violence ou à la persécution d'un groupe",
"Apologie du génocide, de crimes contre l'humanité ou de crimes de guerre",
"Désinformation délibérée visant à nuire à un groupe protégé",
"Contenus portant atteinte à la dignité humaine au sens de l'Art. 1 de la DUDH",
].map((r) => (
<li key={r} className="flex items-start gap-2">
<XCircle className="h-3.5 w-3.5 text-red-500 flex-shrink-0 mt-0.5" />
<span>{r}</span>
</li>
))}
</ul>
</div>
<div>
<p className="font-semibold mb-2">Ce qui est accepté</p>
<p className="text-foreground/80 text-sm leading-relaxed">
Toute proposition qui exprime une opinion politique, sociale, économique ou environnementale — même controversée — dans le respect des droits fondamentaux. Le désaccord, la critique des institutions, la revendication et la protestation sont acceptés et bienvenus.
</p>
</div>
<div className="bg-muted/40 border border-border/50 p-5 space-y-2">
<p className="font-mono text-xs font-semibold uppercase tracking-widest text-primary">En cas de refus</p>
<p className="text-foreground/90 text-sm leading-relaxed">
Le motif du refus vous est communiqué avec la référence légale précise. La contribution n'est jamais rendue publique. Elle est conservée en base de données avec son statut (refusée) pour des raisons de traçabilité interne.
</p>
</div>
</div>
</section>
{/* Synthèse IA */}
<section className="space-y-6">
<div className="flex items-center gap-3 text-xl font-bold font-serif border-b border-border/50 pb-2">
<Bot className="h-5 w-5 text-primary" />
<h2>L'IA de synthèse</h2>
</div>
<div className="space-y-4">
<div className="bg-muted/40 border border-border/50 p-5 space-y-2">
<p className="font-mono text-xs font-semibold uppercase tracking-widest text-primary">Modèle utilisé</p>
<p className="text-foreground/90 text-sm leading-relaxed">
Mistral Large par défaut (configurable via <code className="bg-muted px-1 py-0.5 rounded text-xs">SYNTHESIS_MODEL</code>). Un modèle plus puissant est utilisé ici pour produire un résumé de meilleure qualité.
</p>
</div>
<div>
<p className="font-semibold mb-2">Ce que l'IA produit</p>
<p className="text-foreground/80 text-sm leading-relaxed">
Le modèle reçoit l'intégralité des contributions acceptées et produit un résumé structuré par thèmes. L'objectif est de rendre le texte directement lisible et utilisable par un élu ou un service public : les idées sont regroupées, les priorités dégagées, les contradictions éventuelles mentionnées.
</p>
</div>
<div>
<p className="font-semibold mb-2">Ce que l'IA ne fait pas</p>
<ul className="text-sm space-y-1.5 text-foreground/80">
{[
"Elle n'invente pas d'idées qui ne sont pas dans les contributions",
"Elle n'écarte pas une contribution selon ses propres préférences",
"Elle ne hiérarchise pas les contributions par leur auteur",
"Elle ne produit pas de recommandations politiques de son propre chef",
].map((r) => (
<li key={r} className="flex items-start gap-2">
<CheckCircle className="h-3.5 w-3.5 text-green-600 flex-shrink-0 mt-0.5" />
<span>{r}</span>
</li>
))}
</ul>
</div>
</div>
</section>
{/* Données */}
<section className="space-y-6">
<div className="flex items-center gap-3 text-xl font-bold font-serif border-b border-border/50 pb-2">
<Database className="h-5 w-5 text-primary" />
<h2>Données collectées</h2>
</div>
<div className="overflow-x-auto">
<table className="w-full text-sm border-collapse">
<thead>
<tr className="border-b border-border">
<th className="text-left py-2 pr-6 font-mono text-xs uppercase tracking-widest text-muted-foreground">Donnée</th>
<th className="text-left py-2 pr-6 font-mono text-xs uppercase tracking-widest text-muted-foreground">Collectée ?</th>
<th className="text-left py-2 font-mono text-xs uppercase tracking-widest text-muted-foreground">Précisions</th>
</tr>
</thead>
<tbody className="divide-y divide-border/50">
{[
{ donnee: "Texte de la contribution", oui: true, detail: "Conservé en base de données" },
{ donnee: "Pseudonyme", oui: true, detail: "Facultatif, librement choisi par l'utilisateur" },
{ donnee: "Date et heure", oui: true, detail: "Horodatage de la soumission" },
{ donnee: "Résultat du filtre", oui: true, detail: "Acceptée / refusée + motif" },
{ donnee: "Adresse IP", oui: false, detail: "Non conservée" },
{ donnee: "Compte utilisateur", oui: false, detail: "Aucune inscription requise" },
{ donnee: "Cookie de suivi", oui: false, detail: "Aucun tracker, aucune publicité" },
{ donnee: "Données de navigation", oui: false, detail: "Non collectées" },
].map((row) => (
<tr key={row.donnee}>
<td className="py-2.5 pr-6 font-medium">{row.donnee}</td>
<td className="py-2.5 pr-6">
{row.oui
? <span className="text-amber-700 font-semibold">Oui</span>
: <span className="text-green-700 font-semibold">Non</span>
}
</td>
<td className="py-2.5 text-foreground/70">{row.detail}</td>
</tr>
))}
</tbody>
</table>
</div>
</section>
{/* Limites */}
<section className="space-y-6">
<div className="flex items-center gap-3 text-xl font-bold font-serif border-b border-border/50 pb-2">
<AlertTriangle className="h-5 w-5 text-primary" />
<h2>Limites du système</h2>
</div>
<div className="bg-muted/30 border-l-4 border-primary/40 p-5 space-y-2">
<p className="font-semibold text-sm">Ce que cette plateforme n'est pas</p>
<p className="text-sm leading-relaxed text-foreground/80">
La synthèse n'est pas un document factuel. Elle reflète fidèlement ce qui a été exprimé — ni plus ni moins. Les propositions ne sont pas vérifiées, expertisées ni validées politiquement. Cette plateforme capture des expressions citoyennes, pas des vérités établies.
</p>
<p className="text-sm leading-relaxed text-foreground/80">
L'auteur de la démarche reste personnellement attaché à l'expertise et au dialogue fondé sur les preuves. Ce projet n'a pas vocation à substituer la parole collective au jugement éclairé, mais à rendre cette parole lisible et transmissible.
</p>
</div>
<div className="space-y-4">
{[
{
titre: "L'IA peut se tromper",
desc: "Le filtre automatique n'est pas infaillible. Une contribution légitime peut être refusée par erreur ; une contribution limite peut passer. Aucun système automatique ne remplace un jugement humain. Si vous estimez qu'un refus est injustifié, vous pouvez reformuler votre contribution.",
},
{
titre: "La synthèse est une interprétation",
desc: "Le résumé produit par l'IA reflète les contributions telles qu'elle les a comprises. Des nuances peuvent être perdues dans l'agrégation. Le texte synthétisé ne doit pas être présenté comme une décision collective formelle, mais comme un aperçu des préoccupations exprimées.",
},
{
titre: "Aucun recours automatique",
desc: "Il n'existe pas encore de mécanisme de contestation d'un refus. Si cette plateforme est déployée dans un cadre institutionnel, il est recommandé d'ajouter un contact humain de recours.",
},
{
titre: "Dépendance à l'API IA",
desc: "Le filtrage et la synthèse dépendent de l'API Mistral (ou OpenAI en alternative). En cas d'indisponibilité, les soumissions sont refusées par précaution et le service affiche un message d'erreur.",
},
].map((item) => (
<div key={item.titre} className="border-l-4 border-amber-400/60 pl-4 space-y-1">
<p className="font-semibold">{item.titre}</p>
<p className="text-foreground/80 text-sm leading-relaxed">{item.desc}</p>
</div>
))}
</div>
</section>
{/* Modèles */}
<section className="space-y-6">
<div className="flex items-center gap-3 text-xl font-bold font-serif border-b border-border/50 pb-2">
<FileText className="h-5 w-5 text-primary" />
<h2>Récapitulatif technique</h2>
</div>
<div className="bg-muted/40 border border-border/50 p-5">
<table className="w-full text-sm">
<tbody className="divide-y divide-border/50">
{[
{ label: "Backend", val: "Python / Flask" },
{ label: "Base de données", val: "PostgreSQL" },
{ label: "Frontend", val: "React + Vite" },
{ label: "IA de modération", val: "Mistral small (défaut) configurable via FILTER_MODEL" },
{ label: "IA de synthèse", val: "Mistral large (défaut) configurable via SYNTHESIS_MODEL" },
{ label: "Données personnelles", val: "Aucune (pseudonyme facultatif uniquement)" },
{ label: "Trackers / publicité", val: "Aucun" },
{ label: "Inscription requise", val: "Non" },
].map((row) => (
<tr key={row.label}>
<td className="py-2 pr-6 font-mono text-xs font-semibold text-muted-foreground uppercase tracking-wider">{row.label}</td>
<td className="py-2 text-foreground/90">{row.val}</td>
</tr>
))}
</tbody>
</table>
</div>
</section>
</div>
</div>
</div>
);
}