diff --git a/src/app/backend/promotions/page.tsx b/src/app/backend/promotions/page.tsx index 902d140..a30a2f7 100644 --- a/src/app/backend/promotions/page.tsx +++ b/src/app/backend/promotions/page.tsx @@ -57,7 +57,7 @@ export default async function PromotionsPage() { )} -
+
{ordered.map((p) => { const isPending = p.status === "pending"; return ( diff --git a/src/app/confidentialite/page.tsx b/src/app/confidentialite/page.tsx new file mode 100644 index 0000000..2260e56 --- /dev/null +++ b/src/app/confidentialite/page.tsx @@ -0,0 +1,58 @@ +import type { Metadata } from "next"; +import { LegalPage } from "@/components/LegalPage"; + +export const metadata: Metadata = { + title: "Confidentialité — Plein R", + description: "Politique de confidentialité et traitement des données personnelles par Plein R.", +}; + +export default function ConfidentialitePage() { + return ( + +

Données recueillies

+

Selon votre utilisation du site, Plein R peut recevoir :

+
    +
  • vos nom, e-mail, téléphone et message lors d’une prise de contact ou demande d’adhésion ;
  • +
  • les informations professionnelles publiées dans l’annuaire et les promotions ;
  • +
  • les données nécessaires à la connexion et à la sécurisation des espaces privés.
  • +
+ +

Finalités

+

+ Ces données servent à répondre aux demandes, gérer les adhésions, administrer l’annuaire, + modérer les promotions et sécuriser l’accès aux comptes. Elles ne sont pas vendues à des tiers. +

+ +

Responsable, bases légales et destinataires

+

+ Plein R est responsable des traitements réalisés par ce site. Les traitements reposent, selon le cas, + sur les démarches précontractuelles liées à l’adhésion, l’exécution de la relation avec les adhérents, + l’intérêt légitime de l’association à répondre aux demandes et administrer son réseau, ou une obligation légale. +

+

+ Seules les personnes habilitées au sein de Plein R et les prestataires techniques nécessaires au fonctionnement + du site peuvent accéder aux données, dans la limite de leurs missions. +

+ +

Durée et sécurité

+

+ Les données sont conservées pendant la durée nécessaire au traitement demandé, à la gestion de la relation + avec l’association et au respect de ses obligations. Des mesures adaptées limitent accès, perte ou divulgation non autorisée. +

+ +

Cookies

+

+ Le site utilise uniquement les éléments techniques nécessaires à son fonctionnement, notamment pour maintenir + une session sécurisée dans l’espace adhérent ou l’administration. +

+ +

Vos droits

+

+ Vous pouvez demander accès, rectification, effacement, limitation, opposition ou portabilité concernant vos données, + selon les conditions prévues par la réglementation. + Envoyez votre demande via le formulaire « Nous contacter » en précisant votre identité et l’objet de votre demande. +

+

Vous pouvez également saisir la CNIL si vous estimez que vos droits ne sont pas respectés.

+
+ ); +} diff --git a/src/app/globals.css b/src/app/globals.css index bcdba34..ee9d017 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -237,6 +237,69 @@ button { align-items: start; } +/* ---- Public footer & legal pages ---- */ +.footer-grid { + display: grid; + grid-template-columns: minmax(280px, 1.8fr) 1fr 1fr; + gap: 48px; + align-items: start; +} +.footer-title { + font-family: "Bricolage Grotesque", "Public Sans", system-ui, sans-serif; + color: var(--ink-strong); + font-size: 13px; + font-weight: 800; + margin: 3px 0 11px; +} +.footer-links { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 8px; + color: var(--muted); + font-size: 13px; +} +.footer-links a, +.footer-links button { + text-decoration: none; + cursor: pointer; +} +.footer-links a:hover, +.footer-links button:hover { + color: var(--brown) !important; + text-decoration: underline; +} +.footer-bottom { + display: flex; + justify-content: space-between; + gap: 16px; + flex-wrap: wrap; + border-top: 1px solid #ded2b9; + margin-top: 26px; + padding-top: 18px; + color: var(--muted-2); + font-size: 12.5px; +} +.legal-content h2 { + font-family: "Bricolage Grotesque", "Public Sans", system-ui, sans-serif; + color: var(--ink-strong); + font-size: 20px; + margin: 28px 0 8px; +} +.legal-content p, +.legal-content li { + color: var(--muted); + font-size: 14.5px; + line-height: 1.75; +} +.legal-content p { + margin: 0 0 12px; +} +.legal-content ul { + margin: 8px 0 14px; + padding-left: 20px; +} + @media (max-width: 1024px) { .grid-6 { grid-template-columns: repeat(3, 1fr); @@ -263,6 +326,10 @@ button { .grid-2 { grid-template-columns: 1fr; } + .footer-grid { + grid-template-columns: 1.5fr 1fr 1fr; + gap: 28px; + } } @media (max-width: 640px) { .grid-6, @@ -273,6 +340,12 @@ button { .container { padding: 0 16px; } + .footer-grid { + grid-template-columns: 1fr 1fr; + } + .footer-grid > :first-child { + grid-column: 1 / -1; + } } /* Évite tout débordement horizontal sur mobile. */ diff --git a/src/app/mentions-legales/page.tsx b/src/app/mentions-legales/page.tsx new file mode 100644 index 0000000..15ddada --- /dev/null +++ b/src/app/mentions-legales/page.tsx @@ -0,0 +1,43 @@ +import type { Metadata } from "next"; +import { LegalPage } from "@/components/LegalPage"; + +export const metadata: Metadata = { + title: "Mentions légales — Plein R", + description: "Mentions légales du site de l'association Plein R.", +}; + +export default function MentionsLegalesPage() { + return ( + +

Éditeur du site

+

+ Ce site est édité par Plein R, association des commerçants et entreprises du Bassin de Pompey. +

+
    +
  • Siège social : à compléter par l’association
  • +
  • Numéro RNA et, le cas échéant, SIREN : à compléter par l’association
  • +
  • Direction de la publication : à compléter par l’association
  • +
+

Pour contacter Plein R, utilisez le formulaire « Nous contacter » disponible en pied de page.

+ +

Hébergement

+

+ Nom, raison sociale, adresse et numéro de téléphone de l’hébergeur : à compléter par l’association + dès que l’hébergement de production est choisi. +

+ +

Propriété intellectuelle

+

+ Structure, textes, éléments graphiques et identité visuelle du site sont protégés. Toute reproduction, + adaptation ou diffusion sans autorisation préalable de Plein R est interdite, hors exceptions prévues par la loi. +

+

Logos, images et contenus publiés par les adhérents restent la propriété de leurs titulaires respectifs.

+ +

Responsabilité

+

+ Plein R veille à la qualité des informations publiées, sans pouvoir garantir leur exhaustivité permanente. + Les offres, coordonnées et contenus des adhérents relèvent de la responsabilité de leurs auteurs. +

+
+ ); +} diff --git a/src/components/ContactModalButton.tsx b/src/components/ContactModalButton.tsx index f0081f7..d8da527 100644 --- a/src/components/ContactModalButton.tsx +++ b/src/components/ContactModalButton.tsx @@ -1,6 +1,7 @@ "use client"; import { useState } from "react"; +import Link from "next/link"; import { Modal } from "./Modal"; import { submitContactMessage } from "@/app/actions/public"; @@ -74,6 +75,13 @@ export function ContactModalButton({ {error &&
{error}
} +

+ Vos informations servent uniquement à répondre à votre message. Consultez notre{" "} + + politique de confidentialité + . +

+ diff --git a/src/components/LegalPage.tsx b/src/components/LegalPage.tsx new file mode 100644 index 0000000..5e0e767 --- /dev/null +++ b/src/components/LegalPage.tsx @@ -0,0 +1,31 @@ +import type { ReactNode } from "react"; +import Link from "next/link"; +import { SiteFooter } from "./SiteFooter"; +import { SiteHeader } from "./SiteHeader"; + +export function LegalPage({ title, intro, children }: { title: string; intro: string; children: ReactNode }) { + return ( +
+ +
+ + ← Retour à l'accueil + +
+
+ Informations du site +
+

+ {title} +

+

{intro}

+ {children} +
+
+ +
+ ); +} diff --git a/src/components/MembershipModalButton.tsx b/src/components/MembershipModalButton.tsx index 1db49a0..0888d65 100644 --- a/src/components/MembershipModalButton.tsx +++ b/src/components/MembershipModalButton.tsx @@ -1,6 +1,7 @@ "use client"; import { useState } from "react"; +import Link from "next/link"; import { Modal } from "./Modal"; import { submitMembershipRequest } from "@/app/actions/public"; @@ -75,6 +76,13 @@ export function MembershipModalButton({ {error &&
{error}
} +

+ Vos informations servent uniquement à traiter votre demande. Consultez notre{" "} + + politique de confidentialité + . +

+ diff --git a/src/components/Modal.tsx b/src/components/Modal.tsx index 9f0b500..26e2e4a 100644 --- a/src/components/Modal.tsx +++ b/src/components/Modal.tsx @@ -1,6 +1,7 @@ "use client"; import { useEffect } from "react"; +import { createPortal } from "react-dom"; export function Modal({ open, @@ -31,7 +32,7 @@ export function Modal({ if (!open) return null; - return ( + return createPortal(
{children}
-
+
, + document.body, ); } diff --git a/src/components/SiteFooter.tsx b/src/components/SiteFooter.tsx index 60befff..705ec49 100644 --- a/src/components/SiteFooter.tsx +++ b/src/components/SiteFooter.tsx @@ -1,28 +1,49 @@ +import Link from "next/link"; import { ContactModalButton } from "./ContactModalButton"; export function SiteFooter() { + const year = new Date().getFullYear(); + return ( -