From 8893076c4dc1f01ca570a294f23cfab9f2608e4c Mon Sep 17 00:00:00 2001 From: Timothy Armes Date: Sat, 28 Sep 2024 16:31:25 +0200 Subject: [PATCH] Added privacy policy --- .vscode/settings.json | 2 + package.json | 1 + src/app/[locale]/(main)/components/Footer.tsx | 8 ++ src/app/[locale]/(main)/privacy/page.tsx | 53 +++++++++++ src/app/[locale]/(main)/tournaments/page.tsx | 1 - src/app/[locale]/layout.tsx | 2 +- src/i18n.ts | 19 +++- src/messages/en.json | 21 ++++- src/messages/fr.json | 21 ++++- src/middleware.ts | 10 +-- src/server/models/userModel.ts | 1 + src/server/mongodb.ts | 2 +- src/utils/navigation.ts | 88 +++++++++++-------- tailwind.config.js | 6 +- yarn.lock | 28 ++++++ 15 files changed, 207 insertions(+), 56 deletions(-) create mode 100644 src/app/[locale]/(main)/privacy/page.tsx diff --git a/.vscode/settings.json b/.vscode/settings.json index db917fc..dc86bb2 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,6 +7,7 @@ "cellspacing", "colour", "colours", + "confidentialite", "contactez", "creer", "datepicker", @@ -23,6 +24,7 @@ "localisation", "markercluster", "moveend", + "politique", "randomisation", "Rapide", "Rees", diff --git a/package.json b/package.json index 50a3e2a..38b05fe 100644 --- a/package.json +++ b/package.json @@ -64,6 +64,7 @@ "devDependencies": { "@swc-jotai/react-refresh": "^0.2.0", "@tailwindcss/forms": "^0.5.4", + "@tailwindcss/typography": "^0.5.15", "@testing-library/jest-dom": "^6.1.2", "@types/leaflet": "^1.9.12", "@types/leaflet.markercluster": "^1.5.4", diff --git a/src/app/[locale]/(main)/components/Footer.tsx b/src/app/[locale]/(main)/components/Footer.tsx index d20692d..b3abf1b 100644 --- a/src/app/[locale]/(main)/components/Footer.tsx +++ b/src/app/[locale]/(main)/components/Footer.tsx @@ -4,6 +4,7 @@ import { useSetAtom } from "jotai"; import { useTranslations } from "next-intl"; import { useParams } from "next/navigation"; import { FaGithub, FaRegEnvelope } from "react-icons/fa"; +import { MdOutlinePrivacyTip } from "react-icons/md"; import { burgerMenuIsOpenAtom } from "@/atoms"; import { Link, usePathname, useRouter } from "@/utils/navigation"; @@ -41,6 +42,13 @@ export default function Footer() { + + +
| diff --git a/src/app/[locale]/(main)/privacy/page.tsx b/src/app/[locale]/(main)/privacy/page.tsx new file mode 100644 index 0000000..aff1308 --- /dev/null +++ b/src/app/[locale]/(main)/privacy/page.tsx @@ -0,0 +1,53 @@ +import { useTranslations } from "next-intl"; + +export default function Contact() { + const t = useTranslations("Privacy"); + + return ( +
+
+

+ {t("title")} +

+ +
+

1. {t("introTitle")}

+

{t("introInfo")}

+ +

2. {t("informationTitle")}

+

{t("informationInfo")}

+ +

3. {t("useTitle")}

+

{t("useInfo")}

+ +

4. {t("deleteTitle")}

+

{t("deleteInfo")}

+ +

5. {t("shareTitle")}

+

{t("shareInfo")}

+ +

6. {t("cookiesTitle")}

+

+ {t.rich("cookiesInfo", { + link: (str) => ( + + {str} + + ), + })} +

+ +

7. {t("changesTitle")}

+

{t("changesInfo")}

+
+
+
+ ); +} diff --git a/src/app/[locale]/(main)/tournaments/page.tsx b/src/app/[locale]/(main)/tournaments/page.tsx index 6f87cbb..63bd9de 100644 --- a/src/app/[locale]/(main)/tournaments/page.tsx +++ b/src/app/[locale]/(main)/tournaments/page.tsx @@ -1,6 +1,5 @@ import { differenceInDays, - formatISO, isSameDay, parse, setDefaultOptions, diff --git a/src/app/[locale]/layout.tsx b/src/app/[locale]/layout.tsx index df4caba..4e55903 100644 --- a/src/app/[locale]/layout.tsx +++ b/src/app/[locale]/layout.tsx @@ -26,7 +26,7 @@ export async function generateMetadata({ // While the `locale` is required, the namespace is optional and // identical to the parameter that `useTranslations` accepts. const t = await getTranslations({ - locale: locale ?? "en", + locale: locale ?? "fr", namespace: "Metadata", }); diff --git a/src/i18n.ts b/src/i18n.ts index b1b57de..aa556aa 100644 --- a/src/i18n.ts +++ b/src/i18n.ts @@ -1,5 +1,18 @@ import { getRequestConfig } from "next-intl/server"; +import { notFound } from "next/navigation"; -export default getRequestConfig(async ({ locale }) => ({ - messages: (await import(`./messages/${locale}.json`)).default, -})); +import { routing } from "./utils/navigation"; + +export default getRequestConfig(async ({ locale }) => { + // Validate that the incoming `locale` parameter is valid + if (!routing.locales.includes(locale as any)) notFound(); + + return { + messages: ( + await (locale === "en" + ? // When using Turbopack, this will enable HMR for `en` + import("./messages/en.json") + : import(`./messages/${locale}.json`)) + ).default, + }; +}); diff --git a/src/messages/en.json b/src/messages/en.json index b778788..da8f24f 100644 --- a/src/messages/en.json +++ b/src/messages/en.json @@ -42,7 +42,8 @@ "Footer": { "githubAria": "Link to GitHub repository", - "contactAria": "Contact Us" + "contactAria": "Contact Us", + "privacyPolicyAria": "Privacy Policy" }, "FormValidation": { @@ -267,5 +268,23 @@ "deleteButton": "Delete Account", "success": "Your account has been deleted.", "failure": "Oops, something went wrong. Please try again later." + }, + + "Privacy": { + "title": "Privacy Policy", + "introTitle": "Introduction", + "introInfo": "Welcome to EchecsFrance.com (\"we,\" \"our,\" or \"us\"). We are an open-source project run by enthusiasts with no commercial intentions. This privacy policy explains how we collect, use, and protect your email address when you interact with our website https://echecsfrance.com/ or use our services. By using our website and services, you agree to the practices described in this policy.", + "informationTitle": "Information We Collect", + "informationInfo": "The only personal information we collect, if you choose to create an account, is your email address. No other personal data is collected.", + "useTitle": "How We Use Your Information", + "useInfo": "We use your email address solely for the purpose of sending you chess tournament notifications that you have specifically requested. Your email address is not used for any other purpose.", + "deleteTitle": "Account Deletion", + "deleteInfo": "You can delete your account at any time, and your email address will be permanently removed from our database.", + "shareTitle": "Data Sharing", + "shareInfo": "We do not share, sell, or provide access to your email address to any third parties. Your information is kept strictly confidential and is only used within our project.", + "cookiesTitle": "Cookies and Tracking", + "cookiesInfo": "We do not track users in an intrusive manner. However, we use TinyAnalytics.io, an anonymous, GDPR-friendly analytics tool, to gather anonymized usage statistics. No personally identifiable information is collected by this tool, and its usage is strictly limited to improving user experience.", + "changesTitle": "Changes to This Privacy Policy", + "changesInfo": "We may update this privacy policy from time to time. Any changes will be posted on this page, and we encourage you to review this policy periodically." } } diff --git a/src/messages/fr.json b/src/messages/fr.json index ff8166d..d663737 100644 --- a/src/messages/fr.json +++ b/src/messages/fr.json @@ -42,7 +42,8 @@ "Footer": { "githubAria": "Lien vers le dépôt GitHub", - "contactAria": "Contactez-nous" + "contactAria": "Contactez-nous", + "privacyPolicyAria": "Politique de confidentialité" }, "FormValidation": { @@ -267,5 +268,23 @@ "deleteButton": "Supprimer le compte", "success": "Votre compte a été supprimé avec succès.", "failure": "Oups, quelque chose s'est mal passé. Veuillez réessayer." + }, + + "Privacy": { + "title": "Politique de Confidentialité", + "introTitle": "Introduction", + "introInfo": "Bienvenue sur EchecsFrance.com (« nous », « notre » ou « nos »). Nous sommes un projet open-source géré par des passionnés, sans intention commerciale. Cette politique de confidentialité explique comment nous collectons, utilisons et protégeons votre adresse email lorsque vous interagissez avec notre site web https://echecsfrance.com/ ou utilisez nos services. En utilisant notre site et nos services, vous acceptez les pratiques décrites dans cette politique.", + "informationTitle": "Informations que Nous Collectons", + "informationInfo": "La seule information personnelle que nous collectons, si vous choisissez de créer un compte, est votre adresse email. Aucune autre donnée personnelle n'est collectée.", + "useTitle": "Comment Nous Utilisons Votre Information", + "useInfo": "Nous utilisons votre adresse email uniquement dans le but de vous envoyer des notifications de tournois d'échecs que vous avez spécifiquement demandées. Votre adresse email n'est utilisée à aucune autre fin.", + "deleteTitle": "Suppression de Compte", + "deleteInfo": "Vous pouvez supprimer votre compte à tout moment, et votre adresse email sera définitivement supprimée de notre base de données.", + "shareTitle": "Partage de Données", + "shareInfo": "Nous ne partageons, ne vendons ni ne fournissons l'accès à votre adresse email à des tiers. Vos informations sont strictement confidentielles et sont utilisées uniquement dans le cadre de notre projet.", + "cookiesTitle": "Cookies et Suivi", + "cookiesInfo": "Nous ne suivons pas les utilisateurs de manière intrusive. Toutefois, nous utilisons TinyAnalytics.io, un outil d'analyse anonyme et conforme au RGPD (Règlement Général sur la Protection des Données), pour obtenir des statistiques d'utilisation anonymisées. Aucune donnée personnelle identifiable n'est collectée par cet outil, et son utilisation est strictement limitée à des fins d'amélioration de l'expérience utilisateur.", + "changesTitle": "Modifications de Cette Politique de Confidentialité", + "changesInfo": "Nous pouvons mettre à jour cette politique de confidentialité de temps à autre. Toutes les modifications seront publiées sur cette page, et nous vous encourageons à consulter régulièrement cette politique." } } diff --git a/src/middleware.ts b/src/middleware.ts index b7bfb33..0b1ab88 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -1,14 +1,8 @@ import createMiddleware from "next-intl/middleware"; -import { locales, pathnames } from "@/utils/navigation"; +import { routing } from "@/utils/navigation"; -export default createMiddleware({ - defaultLocale: "fr", - locales, - pathnames, - - localePrefix: "as-needed", -}); +export default createMiddleware(routing); export const config = { // Skip all paths that should not be internationalized. This example skips certain folders diff --git a/src/server/models/userModel.ts b/src/server/models/userModel.ts index 8bd75f1..c37d781 100644 --- a/src/server/models/userModel.ts +++ b/src/server/models/userModel.ts @@ -3,6 +3,7 @@ import { ObjectId } from "mongodb"; export type UserModel = { email: string; emailVerified?: Date; + locale: string; }; export type VerificationModel = { diff --git a/src/server/mongodb.ts b/src/server/mongodb.ts index 514ae22..7b6547e 100644 --- a/src/server/mongodb.ts +++ b/src/server/mongodb.ts @@ -52,7 +52,7 @@ export async function dbConnect() { const p = await clientPromise; const userData: mongoDB.Db = p.db("userData"); - collections.users = userData.collection("userData"); + collections.users = userData.collection("users"); collections.userVerificationTokens = userData.collection( "userVerificationTokens", ); diff --git a/src/utils/navigation.ts b/src/utils/navigation.ts index 181187a..a038cf7 100644 --- a/src/utils/navigation.ts +++ b/src/utils/navigation.ts @@ -1,52 +1,62 @@ -import { - Pathnames, - createLocalizedPathnamesNavigation, -} from "next-intl/navigation"; +import { createLocalizedPathnamesNavigation } from "next-intl/navigation"; +import { defineRouting } from "next-intl/routing"; export const locales = ["fr", "en"] as const; -// The `pathnames` object holds pairs of internal -// and external paths, separated by locale. -export const pathnames = { - "/": "/", - "/clubs": "/clubs", - "/elo": "/elo", +export const routing = defineRouting({ + locales, + defaultLocale: "fr", + localePrefix: "as-needed", - "/tournaments": { - fr: "/tournois", - en: "/tournaments", - }, + pathnames: { + "/": "/", + "/clubs": "/clubs", + "/elo": "/elo", - "/zones": { - fr: "/regions", - en: "/zones", - }, + "/tournaments": { + fr: "/tournois", + en: "/tournaments", + }, - "/zones/create": { - fr: "/regions/creer", - en: "/zones/create", - }, + "/zones": { + fr: "/regions", + en: "/zones", + }, - "/zones/edit/[id]": { - fr: "/regions/modifier/[id]", - en: "/zones/edit/[id]", - }, + "/zones/create": { + fr: "/regions/creer", + en: "/zones/create", + }, - "/add-tournament": { - fr: "/ajouter-un-tournoi", - en: "/add-tournament", - }, + "/zones/edit/[id]": { + fr: "/regions/modifier/[id]", + en: "/zones/edit/[id]", + }, - "/contact-us": { - fr: "/contactez-nous", - en: "/contact-us", - }, + "/add-tournament": { + fr: "/ajouter-un-tournoi", + en: "/add-tournament", + }, - "/delete-account": { - fr: "/supprimer-compte", - en: "/delete-account", + "/contact-us": { + fr: "/contactez-nous", + en: "/contact-us", + }, + + "/delete-account": { + fr: "/supprimer-compte", + en: "/delete-account", + }, + + "/privacy": { + fr: "/politique-de-confidentialite", + en: "/privacy", + }, }, -} satisfies Pathnames; +}); + +export type Pathnames = keyof typeof routing.pathnames; +export type Locale = (typeof routing.locales)[number]; export const { Link, redirect, usePathname, useRouter, getPathname } = - createLocalizedPathnamesNavigation({ locales, pathnames }); + createLocalizedPathnamesNavigation(routing); diff --git a/tailwind.config.js b/tailwind.config.js index 739cf80..66e7d0f 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -53,5 +53,9 @@ module.exports = { }, }, }, - plugins: [require("@headlessui/tailwindcss"), require("@tailwindcss/forms")], + plugins: [ + require("@headlessui/tailwindcss"), + require("@tailwindcss/forms"), + require("@tailwindcss/typography"), + ], }; diff --git a/yarn.lock b/yarn.lock index 38bb961..a9d63f6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1827,6 +1827,16 @@ dependencies: mini-svg-data-uri "^1.2.3" +"@tailwindcss/typography@^0.5.15": + version "0.5.15" + resolved "https://registry.yarnpkg.com/@tailwindcss/typography/-/typography-0.5.15.tgz#007ab9870c86082a1c76e5b3feda9392c7c8d648" + integrity sha512-AqhlCXl+8grUz8uqExv5OTtgpjuVIwFTSXTrh8y9/pw6q2ek7fJ+Y8ZEVw7EB2DCcuCOtEjf9w3+J3rzts01uA== + dependencies: + lodash.castarray "^4.4.0" + lodash.isplainobject "^4.0.6" + lodash.merge "^4.6.2" + postcss-selector-parser "6.0.10" + "@tanstack/query-core@5.56.2": version "5.56.2" resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-5.56.2.tgz#2def2fb0290cd2836bbb08afb0c175595bb8109b" @@ -4339,11 +4349,21 @@ lodash-es@^4.17.15: resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee" integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw== +lodash.castarray@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.castarray/-/lodash.castarray-4.4.0.tgz#c02513515e309daddd4c24c60cfddcf5976d9115" + integrity sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q== + lodash.debounce@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== +lodash.isplainobject@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" + integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA== + lodash.merge@^4.6.2: version "4.6.2" resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" @@ -4880,6 +4900,14 @@ postcss-nested@^6.0.1: dependencies: postcss-selector-parser "^6.1.1" +postcss-selector-parser@6.0.10: + version "6.0.10" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz#79b61e2c0d1bfc2602d549e11d0876256f8df88d" + integrity sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w== + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" + postcss-selector-parser@^6.0.11, postcss-selector-parser@^6.1.1: version "6.1.2" resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz#27ecb41fb0e3b6ba7a1ec84fff347f734c7929de"