feat: add search toggle for game setup and improve RGPD compliance
This commit is contained in:
@@ -68,6 +68,14 @@ export function AuthModal({ onClose, onSuccess }: { onClose: () => void; onSucce
|
|||||||
<button className="w-full min-h-11 px-5 rounded-xl text-sm font-semibold bg-[#7c3aed] text-white hover:bg-[#6d28d9] disabled:opacity-50 cursor-pointer mt-1" type="submit" disabled={loading}>
|
<button className="w-full min-h-11 px-5 rounded-xl text-sm font-semibold bg-[#7c3aed] text-white hover:bg-[#6d28d9] disabled:opacity-50 cursor-pointer mt-1" type="submit" disabled={loading}>
|
||||||
{loading ? "Chargement..." : mode === "login" ? "Se connecter" : "Créer un compte"}
|
{loading ? "Chargement..." : mode === "login" ? "Se connecter" : "Créer un compte"}
|
||||||
</button>
|
</button>
|
||||||
|
{mode === "register" && (
|
||||||
|
<p className="text-xs text-[#555] text-center leading-relaxed">
|
||||||
|
En créant un compte, vous acceptez notre{" "}
|
||||||
|
<a href="/privacy" className="text-[#888] hover:text-[#f0f0f0] underline underline-offset-2 transition-colors">
|
||||||
|
politique de confidentialité
|
||||||
|
</a>.
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
import { useBlitzGame } from "../../lib/useBlitzGame";
|
import { useBlitzGame } from "../../lib/useBlitzGame";
|
||||||
import { ArticleView } from "./ArticleView";
|
import { ArticleView } from "./ArticleView";
|
||||||
import { ShareBar } from "./ShareBar";
|
import { ShareBar } from "./ShareBar";
|
||||||
|
import { useSearchAllowed } from "../../lib/useSearchAllowed";
|
||||||
|
|
||||||
function fmtLeft(s: number): string {
|
function fmtLeft(s: number): string {
|
||||||
const m = Math.floor(s / 60);
|
const m = Math.floor(s / 60);
|
||||||
@@ -12,6 +13,8 @@ function fmtLeft(s: number): string {
|
|||||||
|
|
||||||
export function BlitzScreen({ onBack }: { onBack: () => void }) {
|
export function BlitzScreen({ onBack }: { onBack: () => void }) {
|
||||||
const game = useBlitzGame();
|
const game = useBlitzGame();
|
||||||
|
const { allowed: searchAllowed, toggle: toggleSearch } = useSearchAllowed();
|
||||||
|
|
||||||
|
|
||||||
const btnPrimary = "w-full min-h-11 rounded-xl text-sm font-semibold bg-[#7c3aed] text-white hover:bg-[#6d28d9] disabled:opacity-50 cursor-pointer transition-colors";
|
const btnPrimary = "w-full min-h-11 rounded-xl text-sm font-semibold bg-[#7c3aed] text-white hover:bg-[#6d28d9] disabled:opacity-50 cursor-pointer transition-colors";
|
||||||
const btnGhost = "w-full min-h-11 rounded-xl text-sm font-semibold bg-[#242424] border border-[#2e2e2e] text-[#f0f0f0] hover:bg-[#1a1a1a] cursor-pointer transition-colors";
|
const btnGhost = "w-full min-h-11 rounded-xl text-sm font-semibold bg-[#242424] border border-[#2e2e2e] text-[#f0f0f0] hover:bg-[#1a1a1a] cursor-pointer transition-colors";
|
||||||
@@ -28,6 +31,15 @@ export function BlitzScreen({ onBack }: { onBack: () => void }) {
|
|||||||
<p className="text-[#888] text-sm sm:text-base leading-relaxed">
|
<p className="text-[#888] text-sm sm:text-base leading-relaxed">
|
||||||
Tu as <span className="text-[#f0f0f0] font-bold">2 minutes</span>{" "}pour naviguer de l'article de départ jusqu'à l'article cible en cliquant sur les liens Wikipedia. Plus tu es rapide, mieux c'est !
|
Tu as <span className="text-[#f0f0f0] font-bold">2 minutes</span>{" "}pour naviguer de l'article de départ jusqu'à l'article cible en cliquant sur les liens Wikipedia. Plus tu es rapide, mieux c'est !
|
||||||
</p>
|
</p>
|
||||||
|
<button
|
||||||
|
onClick={toggleSearch}
|
||||||
|
className={`w-full min-h-11 rounded-xl text-sm font-semibold border transition-colors cursor-pointer flex items-center justify-between px-4 ${searchAllowed ? "bg-[#7c3aed]/10 border-[#7c3aed] text-[#a78bfa]" : "bg-[#1a1a1a] border-[#2e2e2e] text-[#888]"}`}
|
||||||
|
>
|
||||||
|
<span>🔍 Recherche Ctrl+F</span>
|
||||||
|
<span className={`text-xs font-bold px-2 py-0.5 rounded-full ${searchAllowed ? "bg-[#7c3aed]/30 text-[#a78bfa]" : "bg-[#242424] text-[#555]"}`}>
|
||||||
|
{searchAllowed ? "Autorisée" : "Bloquée"}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
<button className={btnPrimary} onClick={game.start} disabled={game.loading}>
|
<button className={btnPrimary} onClick={game.start} disabled={game.loading}>
|
||||||
{game.loading ? "Préparation..." : "Lancer le chrono !"}
|
{game.loading ? "Préparation..." : "Lancer le chrono !"}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ export function GameScreen({
|
|||||||
</div>
|
</div>
|
||||||
<Breadcrumbs history={history} endRef={breadcrumbEndRef} />
|
<Breadcrumbs history={history} endRef={breadcrumbEndRef} />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-2 sm:gap-3 shrink-0 text-[10px] sm:text-xs font-bold text-[#888] tabular-nums">
|
<div className="flex items-center gap-2 sm:gap-3 shrink-0 text-[10px] sm:text-xs font-bold text-[#888] tabular-nums">
|
||||||
<span>{elapsed}</span>
|
<span>{elapsed}</span>
|
||||||
<span>{clicks} clics</span>
|
<span>{clicks} clics</span>
|
||||||
<span className="hidden sm:inline">{myPlayer?.score ?? 0} pts</span>
|
<span className="hidden sm:inline">{myPlayer?.score ?? 0} pts</span>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
import { useSearchAllowed } from "../../lib/useSearchAllowed";
|
||||||
import type { Room } from "../api/rooms/route";
|
import type { Room } from "../api/rooms/route";
|
||||||
|
|
||||||
type LobbyScreenProps = {
|
type LobbyScreenProps = {
|
||||||
@@ -19,6 +20,7 @@ export function LobbyScreen({
|
|||||||
}: LobbyScreenProps) {
|
}: LobbyScreenProps) {
|
||||||
const isHost = room.players.find((p) => p.id === playerId)?.isHost ?? false;
|
const isHost = room.players.find((p) => p.id === playerId)?.isHost ?? false;
|
||||||
const [copied, setCopied] = useState(false);
|
const [copied, setCopied] = useState(false);
|
||||||
|
const { allowed: searchAllowed, toggle: toggleSearch } = useSearchAllowed();
|
||||||
|
|
||||||
function copyCode() {
|
function copyCode() {
|
||||||
navigator.clipboard.writeText(room.code).then(() => {
|
navigator.clipboard.writeText(room.code).then(() => {
|
||||||
@@ -74,6 +76,16 @@ export function LobbyScreen({
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
onClick={toggleSearch}
|
||||||
|
className={`w-full min-h-11 rounded-xl text-sm font-semibold border transition-colors cursor-pointer flex items-center justify-between px-4 ${searchAllowed ? "bg-[#7c3aed]/10 border-[#7c3aed] text-[#a78bfa]" : "bg-[#1a1a1a] border-[#2e2e2e] text-[#888]"}`}
|
||||||
|
>
|
||||||
|
<span>🔍 Recherche Ctrl+F</span>
|
||||||
|
<span className={`text-xs font-bold px-2 py-0.5 rounded-full ${searchAllowed ? "bg-[#7c3aed]/30 text-[#a78bfa]" : "bg-[#242424] text-[#555]"}`}>
|
||||||
|
{searchAllowed ? "Autorisée" : "Bloquée"}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
{room.round > 0 && <p className="text-xs text-[#888] text-center">Manche {room.round} terminée</p>}
|
{room.round > 0 && <p className="text-xs text-[#888] text-center">Manche {room.round} terminée</p>}
|
||||||
|
|
||||||
{isHost ? (
|
{isHost ? (
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { useEffect, useRef } from "react";
|
|||||||
import { ArticleView } from "./ArticleView";
|
import { ArticleView } from "./ArticleView";
|
||||||
import { Breadcrumbs } from "./Breadcrumbs";
|
import { Breadcrumbs } from "./Breadcrumbs";
|
||||||
import { ShareBar } from "./ShareBar";
|
import { ShareBar } from "./ShareBar";
|
||||||
|
import { useSearchAllowed } from "../../lib/useSearchAllowed";
|
||||||
import type { Puzzle } from "../../lib/types";
|
import type { Puzzle } from "../../lib/types";
|
||||||
|
|
||||||
type SoloPhase = "setup" | "playing" | "won";
|
type SoloPhase = "setup" | "playing" | "won";
|
||||||
@@ -32,8 +33,10 @@ export function SoloScreen({
|
|||||||
elapsedDisplay, canGoBack, onStart, onNavigate, onBack, onQuit, onNewGame, onRetry,
|
elapsedDisplay, canGoBack, onStart, onNavigate, onBack, onQuit, onNewGame, onRetry,
|
||||||
}: SoloScreenProps) {
|
}: SoloScreenProps) {
|
||||||
const breadcrumbEndRef = useRef<HTMLDivElement>(null);
|
const breadcrumbEndRef = useRef<HTMLDivElement>(null);
|
||||||
|
const { allowed: searchAllowed, toggle: toggleSearch } = useSearchAllowed();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
||||||
breadcrumbEndRef.current?.scrollIntoView({ behavior: "smooth", block: "nearest", inline: "end" });
|
breadcrumbEndRef.current?.scrollIntoView({ behavior: "smooth", block: "nearest", inline: "end" });
|
||||||
}, [history]);
|
}, [history]);
|
||||||
|
|
||||||
@@ -49,6 +52,15 @@ export function SoloScreen({
|
|||||||
<p className="text-[#888] text-sm sm:text-base leading-relaxed">
|
<p className="text-[#888] text-sm sm:text-base leading-relaxed">
|
||||||
Deux articles aléatoires seront choisis. Atteins l'article cible en cliquant uniquement sur les liens !
|
Deux articles aléatoires seront choisis. Atteins l'article cible en cliquant uniquement sur les liens !
|
||||||
</p>
|
</p>
|
||||||
|
<button
|
||||||
|
onClick={toggleSearch}
|
||||||
|
className={`w-full min-h-11 rounded-xl text-sm font-semibold border transition-colors cursor-pointer flex items-center justify-between px-4 ${searchAllowed ? "bg-[#7c3aed]/10 border-[#7c3aed] text-[#a78bfa]" : "bg-[#1a1a1a] border-[#2e2e2e] text-[#888]"}`}
|
||||||
|
>
|
||||||
|
<span>🔍 Recherche Ctrl+F</span>
|
||||||
|
<span className={`text-xs font-bold px-2 py-0.5 rounded-full ${searchAllowed ? "bg-[#7c3aed]/30 text-[#a78bfa]" : "bg-[#242424] text-[#555]"}`}>
|
||||||
|
{searchAllowed ? "Autorisée" : "Bloquée"}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
<button className={btnPrimary} onClick={onStart} disabled={loading}>
|
<button className={btnPrimary} onClick={onStart} disabled={loading}>
|
||||||
{loading ? "Préparation..." : "Lancer une partie"}
|
{loading ? "Préparation..." : "Lancer une partie"}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
+206
-49
@@ -1,3 +1,5 @@
|
|||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
export const metadata = {
|
export const metadata = {
|
||||||
title: "Politique de confidentialité - WikiRush",
|
title: "Politique de confidentialité - WikiRush",
|
||||||
};
|
};
|
||||||
@@ -6,34 +8,71 @@ export default function PrivacyPage() {
|
|||||||
return (
|
return (
|
||||||
<div className="min-h-dvh bg-[#0f0f0f] text-[#f0f0f0]">
|
<div className="min-h-dvh bg-[#0f0f0f] text-[#f0f0f0]">
|
||||||
<div className="max-w-2xl mx-auto px-4 py-12 sm:py-16">
|
<div className="max-w-2xl mx-auto px-4 py-12 sm:py-16">
|
||||||
<a href="/" className="inline-flex items-center gap-2 text-sm text-[#888] hover:text-[#f0f0f0] mb-8 transition-colors">
|
<Link
|
||||||
|
href="/"
|
||||||
|
className="inline-flex items-center gap-2 text-sm text-[#888] hover:text-[#f0f0f0] mb-8 transition-colors"
|
||||||
|
>
|
||||||
← Retour
|
← Retour
|
||||||
</a>
|
</Link>
|
||||||
|
|
||||||
<h1 className="text-3xl sm:text-4xl font-black mb-2">Politique de confidentialité</h1>
|
<h1 className="text-3xl sm:text-4xl font-black mb-2">
|
||||||
<p className="text-sm text-[#888] mb-10">Dernière mise à jour : avril 2025</p>
|
Politique de confidentialité
|
||||||
|
</h1>
|
||||||
|
<p className="text-sm text-[#888] mb-10">
|
||||||
|
Dernière mise à jour : avril 2026
|
||||||
|
</p>
|
||||||
|
|
||||||
<div className="flex flex-col gap-8 text-sm sm:text-base leading-relaxed text-[#ccc]">
|
<div className="flex flex-col gap-8 text-sm sm:text-base leading-relaxed text-[#ccc]">
|
||||||
|
|
||||||
<section className="flex flex-col gap-3">
|
<section className="flex flex-col gap-3">
|
||||||
<h2 className="text-lg sm:text-xl font-bold text-[#f0f0f0]">1. Présentation</h2>
|
<h2 className="text-lg sm:text-xl font-bold text-[#f0f0f0]">
|
||||||
|
1. Présentation
|
||||||
|
</h2>
|
||||||
<p>
|
<p>
|
||||||
WikiRush est un jeu de navigation Wikipedia jouable en solo et en multijoueur.
|
WikiRush est un jeu de navigation Wikipedia jouable en solo et en
|
||||||
Cette politique explique quelles données sont collectées, pourquoi, et comment elles sont protégées.
|
multijoueur. Cette politique explique quelles données sont
|
||||||
|
collectées, pourquoi, et comment elles sont protégées.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Responsable du traitement :{" "}
|
||||||
|
<span className="text-[#f0f0f0] font-semibold">DAVID Jessy</span>{" "}
|
||||||
|
-{" "}
|
||||||
|
<a
|
||||||
|
href="mailto:contact@jessy-david.dev"
|
||||||
|
className="text-[#7c3aed] hover:underline"
|
||||||
|
>
|
||||||
|
contact@jessy-david.dev
|
||||||
|
</a>
|
||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section className="flex flex-col gap-3">
|
<section className="flex flex-col gap-3">
|
||||||
<h2 className="text-lg sm:text-xl font-bold text-[#f0f0f0]">2. Données collectées</h2>
|
<h2 className="text-lg sm:text-xl font-bold text-[#f0f0f0]">
|
||||||
|
2. Données collectées
|
||||||
|
</h2>
|
||||||
<p>Lors de la création d'un compte, nous collectons :</p>
|
<p>Lors de la création d'un compte, nous collectons :</p>
|
||||||
<ul className="list-disc pl-5 flex flex-col gap-1.5 text-[#ccc]">
|
<ul className="list-disc pl-5 flex flex-col gap-1.5 text-[#ccc]">
|
||||||
<li><span className="text-[#f0f0f0] font-semibold">Pseudo</span> - affiché dans les parties et le classement.</li>
|
<li>
|
||||||
<li><span className="text-[#f0f0f0] font-semibold">Adresse e-mail</span> - utilisée uniquement pour l'authentification. Elle n'est jamais affichée publiquement.</li>
|
<span className="text-[#f0f0f0] font-semibold">Pseudo</span> -
|
||||||
<li><span className="text-[#f0f0f0] font-semibold">Mot de passe</span> - stocké sous forme hachée (bcrypt). Nous n'avons jamais accès à votre mot de passe en clair.</li>
|
affiché dans les parties et le classement.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<span className="text-[#f0f0f0] font-semibold">
|
||||||
|
Adresse e-mail
|
||||||
|
</span>{" "}
|
||||||
|
- utilisée uniquement pour l'authentification. Elle
|
||||||
|
n'est jamais affichée publiquement.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<span className="text-[#f0f0f0] font-semibold">
|
||||||
|
Mot de passe
|
||||||
|
</span>{" "}
|
||||||
|
- stocké sous forme hachée (bcrypt). Nous n'avons jamais
|
||||||
|
accès à votre mot de passe en clair.
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>Pour chaque partie jouée, nous enregistrons :</p>
|
<p>Pour chaque partie jouée, nous enregistrons :</p>
|
||||||
<ul className="list-disc pl-5 flex flex-col gap-1.5 text-[#ccc]">
|
<ul className="list-disc pl-5 flex flex-col gap-1.5 text-[#ccc]">
|
||||||
<li>Mode de jeu (solo / multijoueur)</li>
|
<li>Mode de jeu (solo / multijoueur / blitz / défi du jour)</li>
|
||||||
<li>Articles de départ et cible</li>
|
<li>Articles de départ et cible</li>
|
||||||
<li>Chemin parcouru, nombre de clics, temps</li>
|
<li>Chemin parcouru, nombre de clics, temps</li>
|
||||||
<li>Résultat (victoire / abandon)</li>
|
<li>Résultat (victoire / abandon)</li>
|
||||||
@@ -42,53 +81,171 @@ export default function PrivacyPage() {
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section className="flex flex-col gap-3">
|
<section className="flex flex-col gap-3">
|
||||||
<h2 className="text-lg sm:text-xl font-bold text-[#f0f0f0]">3. Utilisation des données</h2>
|
<h2 className="text-lg sm:text-xl font-bold text-[#f0f0f0]">
|
||||||
<p>Les données collectées servent exclusivement à :</p>
|
3. Finalités et bases légales
|
||||||
|
</h2>
|
||||||
<ul className="list-disc pl-5 flex flex-col gap-1.5">
|
<ul className="list-disc pl-5 flex flex-col gap-1.5">
|
||||||
<li>Afficher votre historique de parties dans votre profil.</li>
|
|
||||||
<li>Alimenter le classement global (pseudo + statistiques uniquement).</li>
|
|
||||||
<li>Maintenir votre session de connexion.</li>
|
|
||||||
</ul>
|
|
||||||
<p>Aucune donnée n'est vendue, partagée ou utilisée à des fins commerciales ou publicitaires.</p>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section className="flex flex-col gap-3">
|
|
||||||
<h2 className="text-lg sm:text-xl font-bold text-[#f0f0f0]">4. Stockage et sécurité</h2>
|
|
||||||
<p>
|
|
||||||
Les données sont stockées dans une base SQLite hébergée localement sur le serveur de l'application.
|
|
||||||
Les mots de passe sont hachés avec bcrypt avant tout stockage.
|
|
||||||
Les sessions sont gérées via JWT (JSON Web Token) et ne contiennent pas d'informations sensibles.
|
|
||||||
</p>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section className="flex flex-col gap-3">
|
|
||||||
<h2 className="text-lg sm:text-xl font-bold text-[#f0f0f0]">5. Données de navigation</h2>
|
|
||||||
<p>
|
|
||||||
WikiRush n'utilise pas de cookies tiers, de trackers analytiques (Google Analytics, etc.) ni de réseaux publicitaires.
|
|
||||||
La session de connexion est stockée dans un cookie HTTP sécurisé et httpOnly, nécessaire au fonctionnement de l'authentification.
|
|
||||||
</p>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section className="flex flex-col gap-3">
|
|
||||||
<h2 className="text-lg sm:text-xl font-bold text-[#f0f0f0]">6. Vos droits</h2>
|
|
||||||
<p>Vous pouvez à tout moment :</p>
|
|
||||||
<ul className="list-disc pl-5 flex flex-col gap-1.5">
|
|
||||||
<li><span className="text-[#f0f0f0] font-semibold">Consulter</span> vos données via votre page de profil.</li>
|
|
||||||
<li>
|
<li>
|
||||||
<span className="text-[#f0f0f0] font-semibold">Supprimer</span> votre compte directement depuis votre profil,
|
<span className="text-[#f0f0f0] font-semibold">
|
||||||
rubrique <span className="italic">Zone dangereuse</span>. Cette action supprime immédiatement et définitivement
|
Gestion du compte
|
||||||
votre compte ainsi que l'intégralité de vos parties enregistrées.
|
</span>{" "}
|
||||||
|
- base légale : exécution du contrat (Art. 6.1.b RGPD).
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<span className="text-[#f0f0f0] font-semibold">
|
||||||
|
Historique de parties et classement
|
||||||
|
</span>{" "}
|
||||||
|
- base légale : exécution du contrat (Art. 6.1.b RGPD).
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<span className="text-[#f0f0f0] font-semibold">
|
||||||
|
Session de connexion
|
||||||
|
</span>{" "}
|
||||||
|
- base légale : intérêt légitime (Art. 6.1.f RGPD) - nécessaire
|
||||||
|
au fonctionnement du service.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<p>
|
||||||
|
Aucune donnée n'est vendue, partagée ou utilisée à des fins
|
||||||
|
commerciales ou publicitaires.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="flex flex-col gap-3">
|
||||||
|
<h2 className="text-lg sm:text-xl font-bold text-[#f0f0f0]">
|
||||||
|
4. Durées de conservation
|
||||||
|
</h2>
|
||||||
|
<ul className="list-disc pl-5 flex flex-col gap-1.5">
|
||||||
|
<li>
|
||||||
|
Les données de compte (pseudo, email) sont conservées
|
||||||
|
jusqu'à la suppression du compte.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Les données de parties sont liées au compte et supprimées avec
|
||||||
|
lui.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Aucune archive n'est conservée après suppression du compte.
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section className="flex flex-col gap-3">
|
<section className="flex flex-col gap-3">
|
||||||
<h2 className="text-lg sm:text-xl font-bold text-[#f0f0f0]">7. Contact</h2>
|
<h2 className="text-lg sm:text-xl font-bold text-[#f0f0f0]">
|
||||||
|
5. Stockage et sécurité
|
||||||
|
</h2>
|
||||||
<p>
|
<p>
|
||||||
Pour toute question relative à vos données personnelles, contactez-nous via la page GitHub du projet.
|
Les données sont stockées dans une base SQLite hébergée sur le
|
||||||
|
serveur de l'application. Les mots de passe sont hachés avec
|
||||||
|
bcrypt avant tout stockage. Les sessions sont gérées via JWT (JSON
|
||||||
|
Web Token) et ne contiennent pas d'informations sensibles.
|
||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<section className="flex flex-col gap-3">
|
||||||
|
<h2 className="text-lg sm:text-xl font-bold text-[#f0f0f0]">
|
||||||
|
6. Hébergement et transferts
|
||||||
|
</h2>
|
||||||
|
<p>
|
||||||
|
WikiRush est hébergé par{" "}
|
||||||
|
<span className="text-[#f0f0f0] font-semibold">
|
||||||
|
QuantumCraft Studios
|
||||||
|
</span>{" "}
|
||||||
|
(58 Rue de Monceau, 75008 Paris). L'infrastructure de
|
||||||
|
déploiement peut faire appel à des prestataires établis hors UE.
|
||||||
|
Dans ce cas, les transferts sont encadrés par des clauses
|
||||||
|
contractuelles types (SCC) conformes au RGPD.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="flex flex-col gap-3">
|
||||||
|
<h2 className="text-lg sm:text-xl font-bold text-[#f0f0f0]">
|
||||||
|
7. Données de navigation
|
||||||
|
</h2>
|
||||||
|
<p>
|
||||||
|
WikiRush n'utilise pas de cookies tiers, de trackers
|
||||||
|
analytiques (Google Analytics, etc.) ni de réseaux publicitaires.
|
||||||
|
La session de connexion est stockée dans un cookie HTTP sécurisé
|
||||||
|
et httpOnly, nécessaire au fonctionnement de
|
||||||
|
l'authentification.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="flex flex-col gap-3">
|
||||||
|
<h2 className="text-lg sm:text-xl font-bold text-[#f0f0f0]">
|
||||||
|
8. Vos droits
|
||||||
|
</h2>
|
||||||
|
<p>Conformément au RGPD, vous disposez des droits suivants :</p>
|
||||||
|
<ul className="list-disc pl-5 flex flex-col gap-1.5">
|
||||||
|
<li>
|
||||||
|
<span className="text-[#f0f0f0] font-semibold">Accès</span> -
|
||||||
|
consulter vos données via votre page de profil.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<span className="text-[#f0f0f0] font-semibold">
|
||||||
|
Portabilité
|
||||||
|
</span>{" "}
|
||||||
|
- télécharger l'intégralité de vos données au format JSON
|
||||||
|
depuis votre profil (bouton « Télécharger mes
|
||||||
|
données »).
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<span className="text-[#f0f0f0] font-semibold">
|
||||||
|
Rectification
|
||||||
|
</span>{" "}
|
||||||
|
- corriger des données inexactes en nous contactant.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<span className="text-[#f0f0f0] font-semibold">
|
||||||
|
Suppression
|
||||||
|
</span>{" "}
|
||||||
|
- supprimer votre compte depuis votre profil, rubrique{" "}
|
||||||
|
<span className="italic">Zone dangereuse</span>. Cette action
|
||||||
|
supprime immédiatement et définitivement votre compte ainsi que
|
||||||
|
l'intégralité de vos données.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<span className="text-[#f0f0f0] font-semibold">Opposition</span>{" "}
|
||||||
|
- vous opposer à un traitement en nous contactant.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<p>
|
||||||
|
Pour exercer ces droits :{" "}
|
||||||
|
<a
|
||||||
|
href="mailto:contact@jessy-david.dev"
|
||||||
|
className="text-[#7c3aed] hover:underline"
|
||||||
|
>
|
||||||
|
contact@jessy-david.dev
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Vous pouvez également introduire une réclamation auprès de la{" "}
|
||||||
|
<a
|
||||||
|
href="https://www.cnil.fr/fr/plaintes"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="text-[#7c3aed] hover:underline"
|
||||||
|
>
|
||||||
|
CNIL
|
||||||
|
</a>
|
||||||
|
.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="flex flex-col gap-3">
|
||||||
|
<h2 className="text-lg sm:text-xl font-bold text-[#f0f0f0]">
|
||||||
|
9. Contact
|
||||||
|
</h2>
|
||||||
|
<p>
|
||||||
|
Pour toute question relative à vos données personnelles :{" "}
|
||||||
|
<a
|
||||||
|
href="mailto:contact@jessy-david.dev"
|
||||||
|
className="text-[#7c3aed] hover:underline"
|
||||||
|
>
|
||||||
|
contact@jessy-david.dev
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
const KEY = "wikirush_search_allowed";
|
||||||
|
|
||||||
|
export function useSearchAllowed() {
|
||||||
|
const [allowed, setAllowed] = useState<boolean>(() => {
|
||||||
|
if (typeof window === "undefined") return false;
|
||||||
|
return localStorage.getItem(KEY) === "true";
|
||||||
|
});
|
||||||
|
|
||||||
|
function toggle() {
|
||||||
|
setAllowed((prev) => {
|
||||||
|
const next = !prev;
|
||||||
|
localStorage.setItem(KEY, String(next));
|
||||||
|
return next;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
function handleKeyDown(e: KeyboardEvent) {
|
||||||
|
if ((e.ctrlKey || e.metaKey) && e.key === "f") {
|
||||||
|
if (!allowed) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
window.addEventListener("keydown", handleKeyDown, { capture: true });
|
||||||
|
return () => window.removeEventListener("keydown", handleKeyDown, { capture: true });
|
||||||
|
}, [allowed]);
|
||||||
|
|
||||||
|
return { allowed, toggle };
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user