From 6d5afee4f46e1f1ecbc32e40e6ed2e06e1852b35 Mon Sep 17 00:00:00 2001 From: Sonny Date: Sun, 19 Nov 2023 18:58:49 +0100 Subject: [PATCH] feat: privacy page with translation refactor: apply prettier conf to all files feat: terms of use with translation --- .idea/.gitignore | 8 ++ .idea/codeStyles/Project.xml | 58 +++++++++ .idea/codeStyles/codeStyleConfig.xml | 5 + .idea/misc.xml | 5 + .idea/modules.xml | 8 ++ .idea/my-links.iml | 9 ++ .idea/prettier.xml | 6 + .idea/vcs.xml | 6 + .prettierrc | 2 +- .vscode/settings.json | 4 +- config.ts | 2 +- middleware.ts | 4 +- package-lock.json | 100 ++++++++++++++++ package.json | 2 + public/locales/en/common.json | 4 +- public/locales/en/privacy.json | 45 +++++++ public/locales/en/terms.json | 0 public/locales/fr/common.json | 4 +- public/locales/fr/privacy.json | 51 ++++++++ public/locales/fr/terms.json | 0 .../ErrorBoundary/ErrorBoundary.jsx | 18 +-- src/components/Links/Links.tsx | 3 +- src/components/Modal/Modal.tsx | 2 +- src/components/Navbar/Navbar.tsx | 35 ++++++ src/components/Navbar/NavbarUntranslated.tsx | 33 ++++++ src/components/Navbar/navbar.module.scss | 10 ++ src/components/SearchModal/SearchList.tsx | 10 +- src/components/SearchModal/SearchModal.tsx | 12 +- .../SideMenu/Categories/Categories.tsx | 6 +- src/components/SideMenu/SideMenu.tsx | 8 +- src/constants/paths.ts | 2 + src/i18n/index.ts | 9 +- src/i18n/resources.ts | 4 + src/lib/category/getUserCategoryByName.ts | 2 +- src/lib/link/getLinkFromCategoryByName.ts | 2 +- src/pages/404.tsx | 11 +- src/pages/500.tsx | 11 +- src/pages/api/auth/[...nextauth].ts | 2 +- src/pages/api/favicon.ts | 2 +- src/pages/api/link/[lid].ts | 2 +- src/pages/api/link/index.ts | 2 +- src/pages/category/create.tsx | 4 +- src/pages/category/edit/[cid].tsx | 4 +- src/pages/category/remove/[cid].tsx | 8 +- src/pages/index.tsx | 28 ++--- src/pages/link/create.tsx | 6 +- src/pages/link/edit/[lid].tsx | 4 +- src/pages/link/remove/[lid].tsx | 4 +- src/pages/login.tsx | 5 +- src/pages/privacy.tsx | 76 ++++++++++++ src/pages/terms.tsx | 111 ++++++++++++++++++ src/styles/error-page.module.scss | 44 +++---- src/styles/globals.scss | 14 +-- src/styles/legal-pages.module.scss | 24 ++++ src/utils/session.ts | 2 +- 55 files changed, 725 insertions(+), 118 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/codeStyles/Project.xml create mode 100644 .idea/codeStyles/codeStyleConfig.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/my-links.iml create mode 100644 .idea/prettier.xml create mode 100644 .idea/vcs.xml create mode 100644 public/locales/en/privacy.json create mode 100644 public/locales/en/terms.json create mode 100644 public/locales/fr/privacy.json create mode 100644 public/locales/fr/terms.json create mode 100644 src/components/Navbar/Navbar.tsx create mode 100644 src/components/Navbar/NavbarUntranslated.tsx create mode 100644 src/components/Navbar/navbar.module.scss create mode 100644 src/pages/privacy.tsx create mode 100644 src/pages/terms.tsx create mode 100644 src/styles/legal-pages.module.scss diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000..f986f2f --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000..79ee123 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..6e86672 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..d19e58a --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/my-links.iml b/.idea/my-links.iml new file mode 100644 index 0000000..d6ebd48 --- /dev/null +++ b/.idea/my-links.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/prettier.xml b/.idea/prettier.xml new file mode 100644 index 0000000..b0c1c68 --- /dev/null +++ b/.idea/prettier.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.prettierrc b/.prettierrc index 7a28161..222861c 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,4 +1,4 @@ { "tabWidth": 2, "useTabs": false -} \ No newline at end of file +} diff --git a/.vscode/settings.json b/.vscode/settings.json index 54f6c0d..8034358 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,3 @@ { - "discord.enabled": false -} \ No newline at end of file + "discord.enabled": false +} diff --git a/config.ts b/config.ts index 2c144be..a9d2c56 100644 --- a/config.ts +++ b/config.ts @@ -1,3 +1,3 @@ export const config = { - siteName: "My Links", + siteName: "My Links", }; diff --git a/middleware.ts b/middleware.ts index c692534..a37fa56 100644 --- a/middleware.ts +++ b/middleware.ts @@ -29,14 +29,14 @@ export function middleware(req) { !req.nextUrl.pathname.startsWith("/_next") ) { return NextResponse.redirect( - new URL(`/${lng}${req.nextUrl.pathname}`, req.url) + new URL(`/${lng}${req.nextUrl.pathname}`, req.url), ); } if (req.headers.has("referer")) { const refererUrl = new URL(req.headers.get("referer")); const lngInReferer = i18n.locales.find((l) => - refererUrl.pathname.startsWith(`/${l}`) + refererUrl.pathname.startsWith(`/${l}`), ); const response = NextResponse.next(); if (lngInReferer) response.cookies.set(cookieName, lngInReferer); diff --git a/package-lock.json b/package-lock.json index 56d3290..0d175ee 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,8 @@ "@prisma/client": "^5.5.2", "@svgr/webpack": "^8.1.0", "accept-language": "^3.0.18", + "axios": "^1.6.1", + "clsx": "^2.0.0", "framer-motion": "^10.16.4", "i18next": "^23.7.3", "next": "^14.0.2", @@ -3124,6 +3126,11 @@ "has-symbols": "^1.0.3" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, "node_modules/available-typed-arrays": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", @@ -3145,6 +3152,16 @@ "node": ">=4" } }, + "node_modules/axios": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz", + "integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==", + "dependencies": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/axobject-query": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", @@ -3459,6 +3476,14 @@ "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" }, + "node_modules/clsx": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz", + "integrity": "sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==", + "engines": { + "node": ">=6" + } + }, "node_modules/color": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", @@ -3509,6 +3534,17 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/commander": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", @@ -3761,6 +3797,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/dequal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", @@ -4720,6 +4764,25 @@ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", "dev": true }, + "node_modules/follow-redirects": { + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", + "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -4729,6 +4792,19 @@ "is-callable": "^1.1.3" } }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/framer-motion": { "version": "10.16.4", "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-10.16.4.tgz", @@ -5789,6 +5865,25 @@ "node": ">=8.6" } }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/mimic-response": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", @@ -6474,6 +6569,11 @@ "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.5.tgz", "integrity": "sha512-IJUkICM5dP5znhCckHSv30Q4b5/JA5enCtkRHYaOVOAocnH/1BQEYTC5NMfT3AVl/iXKdr3aqQbQn9DxyWknwA==" }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", diff --git a/package.json b/package.json index 9c23a37..ce00d28 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,8 @@ "@prisma/client": "^5.5.2", "@svgr/webpack": "^8.1.0", "accept-language": "^3.0.18", + "axios": "^1.6.1", + "clsx": "^2.0.0", "framer-motion": "^10.16.4", "i18next": "^23.7.3", "next": "^14.0.2", diff --git a/public/locales/en/common.json b/public/locales/en/common.json index c4e6fc3..f91a150 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -29,5 +29,7 @@ "avatar": "{{name}}'s avatar", "generic-error": "Something went wrong", "generic-error-description": "An error has occurred, if this happens again please create an issue with as much detail as possible.", - "retry": "Retry" + "retry": "Retry", + "privacy": "Privacy", + "terms": "Terms of use" } diff --git a/public/locales/en/privacy.json b/public/locales/en/privacy.json new file mode 100644 index 0000000..0e281bb --- /dev/null +++ b/public/locales/en/privacy.json @@ -0,0 +1,45 @@ +{ + "title": "Privacy Policy of MyLinks", + "edited_at": "Last updated: {{date}}", + "welcome": "Welcome to MyLinks, a free and open-source bookmark manager focused on privacy and self-hosting. This privacy policy aims to inform you about how we collect, use, and protect your data.", + "collect": { + "title": "1. Data Collection", + "cookie": { + "title": "1.1 Cookies", + "description": "Cookies used on MyLinks are essential to ensure the proper functioning of the site. By continuing to use our service, you consent to the use of these cookies." + }, + "user": { + "title": "1.2 User Data", + "description": "To create personalized categories and links and associate them with their author, we collect the following information:", + "fields": ["Google ID", "Lastname", "Firstname", "Email", "Avatar"] + } + }, + "data_use": { + "title": "2. Data Use", + "description": "The collected data is neither resold nor used for purposes other than initially intended, namely the management of categories and links created by the user." + }, + "data_storage": { + "title": "3. Data Storage", + "description": "Data is stored securely to protect your privacy.", + "data_retention": { + "title": "3.1 Data Retention Period", + "description": "Functional data is retained until the user requests deletion. Once this request is made, the data will be permanently deleted." + } + }, + "user_rights": { + "title": "4. User Rights", + "description": "The user has the right to retrieve all their data at any time and/or request the complete deletion of their data." + }, + "rgpd": { + "title": "5. GDPR Compliance", + "description": "MyLinks complies with the General Data Protection Regulation (GDPR) of the European Union." + }, + "contact": { + "title": "6. Contact", + "description": "If you have any questions or concerns about our privacy policy, feel free to contact us at the following address:" + }, + "footer": { + "changes": "We reserve the right to update this privacy policy. We encourage you to regularly check this page to stay informed of any changes.", + "thanks": "Thank you for using MyLinks!" + } +} diff --git a/public/locales/en/terms.json b/public/locales/en/terms.json new file mode 100644 index 0000000..e69de29 diff --git a/public/locales/fr/common.json b/public/locales/fr/common.json index cbd50f7..8f7c37d 100644 --- a/public/locales/fr/common.json +++ b/public/locales/fr/common.json @@ -29,5 +29,7 @@ "avatar": "Avatar de {{name}}", "generic-error": "Une erreur est survenue", "generic-error-description": "Une erreur est survenue, si cela se reproduit merci de créer une issue avec le maximum de détails.", - "retry": "Recommencer" + "retry": "Recommencer", + "privacy": "Confidentialité", + "terms": "CGU" } diff --git a/public/locales/fr/privacy.json b/public/locales/fr/privacy.json new file mode 100644 index 0000000..39a7ede --- /dev/null +++ b/public/locales/fr/privacy.json @@ -0,0 +1,51 @@ +{ + "title": "Politique de confidentialité de MyLinks", + "edited_at": "Dernière mise à jour : {{date}}", + "welcome": "Bienvenue sur MyLinks, un gestionnaire de favoris gratuit et open source axé sur la privacy et le self hosting. Cette politique de confidentialité vise à vous informer sur la manière dont nous collectons, utilisons et protégeons vos données.", + "collect": { + "title": "1. Collecte de données", + "cookie": { + "title": "1.1 Cookies", + "description": "Les cookies utilisés sur MyLinks sont indispensables pour assurer le bon fonctionnement du site. En continuant à utiliser notre service, vous consentez à l'utilisation de ces cookies." + }, + "user": { + "title": "1.2 Données utilisateur", + "description": "Pour créer des catégories et liens personnalisés et les associer à leur auteur, nous collectons les informations suivantes :", + "fields": [ + "Identifiant Google", + "Nom", + "Prénom", + "Adresse e-mail", + "Avatar" + ] + } + }, + "data_use": { + "title": "2. Utilisation des données", + "description": "Les données collectées ne sont ni revendues ni utilisées à d'autres fins que celles prévues initialement, à savoir la gestion des catégories et des liens créés par l'utilisateur." + }, + "data_storage": { + "title": "3. Stockage des données", + "description": "Les données sont stockées de manière sécurisée afin de protéger votre confidentialité.", + "data_retention": { + "title": "3.1 Durée de conservation", + "description": "Les données fonctionnelles sont conservées jusqu'à ce que l'utilisateur fasse une demande de suppression. Une fois cette demande effectuée, les données seront définitivement supprimées." + } + }, + "user_rights": { + "title": "4. Droits de l'utilisateur", + "description": "L'utilisateur a le droit de récupérer l'ensemble de ses données à tout moment et/ou de demander la suppression complète de ses données." + }, + "rgpd": { + "title": "5. Conformité au RGPD", + "description": "MyLinks est conforme au Règlement Général sur la Protection des Données (RGPD) de l'Union européenne." + }, + "contact": { + "title": "6. Contact", + "description": "Si vous avez des questions ou des préoccupations concernant notre politique de confidentialité, n'hésitez pas à nous contacter à l'adresse suivante :" + }, + "footer": { + "changes": "Nous nous réservons le droit de mettre à jour cette politique de confidentialité. Nous vous encourageons à consulter régulièrement cette page pour rester informé des changements éventuels.", + "thanks": "Merci d'utiliser MyLinks !" + } +} diff --git a/public/locales/fr/terms.json b/public/locales/fr/terms.json new file mode 100644 index 0000000..e69de29 diff --git a/src/components/ErrorBoundary/ErrorBoundary.jsx b/src/components/ErrorBoundary/ErrorBoundary.jsx index b30474c..086ee9c 100644 --- a/src/components/ErrorBoundary/ErrorBoundary.jsx +++ b/src/components/ErrorBoundary/ErrorBoundary.jsx @@ -1,7 +1,7 @@ -import { withTranslation } from 'next-i18next'; -import React from 'react'; -import LangSelector from '../LangSelector'; -import styles from './error-boundary.module.scss'; +import { withTranslation } from "next-i18next"; +import React from "react"; +import LangSelector from "../LangSelector"; +import styles from "./error-boundary.module.scss"; class ErrorBoundary extends React.Component { constructor(props) { @@ -13,7 +13,7 @@ class ErrorBoundary extends React.Component { // Catch errors in any components below and re-render with error message this.setState({ error: error, - errorInfo: errorInfo + errorInfo: errorInfo, }); } @@ -23,20 +23,20 @@ class ErrorBoundary extends React.Component { return (
-

{this.props.t('common:generic-error')}

+

{this.props.t("common:generic-error")}

{this.state.error && this.state.error.toString()} {this.state.errorInfo.componentStack}
-
+
diff --git a/src/components/Links/Links.tsx b/src/components/Links/Links.tsx index d3cab3e..44729e7 100644 --- a/src/components/Links/Links.tsx +++ b/src/components/Links/Links.tsx @@ -11,6 +11,7 @@ import { Category, Link } from "types"; import { TFunctionParam } from "types/i18next"; import LinkItem from "./LinkItem"; import styles from "./links.module.scss"; +import clsx from "clsx"; export default function Links({ category, @@ -64,7 +65,7 @@ export default function Links({ {links.length !== 0 ? ( -
    +
      {links.map((link, index) => ( {children}
, - document.body + document.body, ); } diff --git a/src/components/Navbar/Navbar.tsx b/src/components/Navbar/Navbar.tsx new file mode 100644 index 0000000..1a960eb --- /dev/null +++ b/src/components/Navbar/Navbar.tsx @@ -0,0 +1,35 @@ +import LinkTag from "next/link"; +import { useSession } from "next-auth/react"; +import PATHS from "constants/paths"; +import styles from "./navbar.module.scss"; +import { useTranslation } from "next-i18next"; + +export default function Navbar() { + const { status } = useSession(); + const { t } = useTranslation(); + + return ( + + ); +} diff --git a/src/components/Navbar/NavbarUntranslated.tsx b/src/components/Navbar/NavbarUntranslated.tsx new file mode 100644 index 0000000..63d955a --- /dev/null +++ b/src/components/Navbar/NavbarUntranslated.tsx @@ -0,0 +1,33 @@ +import LinkTag from "next/link"; +import { useSession } from "next-auth/react"; +import PATHS from "constants/paths"; +import styles from "./navbar.module.scss"; + +export default function NavbarUntranslated() { + const { status } = useSession(); + + return ( + + ); +} diff --git a/src/components/Navbar/navbar.module.scss b/src/components/Navbar/navbar.module.scss new file mode 100644 index 0000000..87bdaa2 --- /dev/null +++ b/src/components/Navbar/navbar.module.scss @@ -0,0 +1,10 @@ +.navbar { + width: 100%; + padding: 0.75em; +} + +.navbar ul { + display: flex; + gap: 1.5em; + justify-content: center; +} diff --git a/src/components/SearchModal/SearchList.tsx b/src/components/SearchModal/SearchList.tsx index cc9cc0c..d15b61e 100644 --- a/src/components/SearchModal/SearchList.tsx +++ b/src/components/SearchModal/SearchList.tsx @@ -24,16 +24,16 @@ export default function SearchList({ }) { const searchItemsGrouped = useMemo( () => groupItemBy(items, "category.name"), - [items] + [items], ); const groupedItems = useMemo( () => Object.entries(searchItemsGrouped), - [searchItemsGrouped] + [searchItemsGrouped], ); const selectedItemIndex = useMemo( () => items.findIndex((item) => isActiveItem(item, selectedItem)), - [items, selectedItem] + [items, selectedItem], ); useHotkeys( @@ -43,7 +43,7 @@ export default function SearchList({ enableOnFormTags: ["INPUT"], enabled: items.length > 1 && selectedItemIndex !== 0, preventDefault: true, - } + }, ); useHotkeys( Keys.ARROW_DOWN, @@ -52,7 +52,7 @@ export default function SearchList({ enableOnFormTags: ["INPUT"], enabled: items.length > 1 && selectedItemIndex !== items.length - 1, preventDefault: true, - } + }, ); useEffect(() => { diff --git a/src/components/SearchModal/SearchModal.tsx b/src/components/SearchModal/SearchModal.tsx index 473f885..a05e23b 100644 --- a/src/components/SearchModal/SearchModal.tsx +++ b/src/components/SearchModal/SearchModal.tsx @@ -29,11 +29,11 @@ export default function SearchModal({ const [canSearchLink, setCanSearchLink] = useLocalStorage( "search-link", - true + true, ); const [canSearchCategory, setCanSearchCategory] = useLocalStorage( "search-category", - false + false, ); const [search, setSearch] = useState(""); @@ -52,9 +52,9 @@ export default function SearchModal({ (item.type === "link" && canSearchLink)) && item.name .toLocaleLowerCase() - .includes(search.toLocaleLowerCase().trim()) + .includes(search.toLocaleLowerCase().trim()), ), - [canSearchCategory, canSearchLink, items, search] + [canSearchCategory, canSearchLink, items, search], ); const resetForm = useCallback(() => { @@ -64,7 +64,7 @@ export default function SearchModal({ const handleSearchInputChange = useCallback( (value: string) => setSearch(value), - [] + [], ); const handleCanSearchLink = (checked: boolean) => setCanSearchLink(checked); @@ -94,7 +94,7 @@ export default function SearchModal({ resetForm, search, selectedItem, - ] + ], ); return ( diff --git a/src/components/SideMenu/Categories/Categories.tsx b/src/components/SideMenu/Categories/Categories.tsx index adf0ad4..e43decc 100644 --- a/src/components/SideMenu/Categories/Categories.tsx +++ b/src/components/SideMenu/Categories/Categories.tsx @@ -3,12 +3,14 @@ import { useMemo } from "react"; import { Category } from "types"; import CategoryItem from "./CategoryItem"; import styles from "./categories.module.scss"; +import clsx from "clsx"; interface CategoriesProps { categories: Category[]; categoryActive: Category; handleSelectCategory: (category: Category) => void; } + export default function Categories({ categories, categoryActive, @@ -17,7 +19,7 @@ export default function Categories({ const { t } = useTranslation(); const linksCount = useMemo( () => categories.reduce((acc, current) => (acc += current.links.length), 0), - [categories] + [categories], ); return ( @@ -25,7 +27,7 @@ export default function Categories({

{t("common:category.categories")} • {linksCount}

-
    +
      {categories.map((category, index) => ( { const currentCategoryIndex = categories.findIndex( - ({ id }) => id === categoryActive.id + ({ id }) => id === categoryActive.id, ); if (currentCategoryIndex === -1 || currentCategoryIndex === 0) return; handleSelectCategory(categories[currentCategoryIndex - 1]); }, - { enabled: !isModalShowing } + { enabled: !isModalShowing }, ); useHotkeys( Keys.ARROW_DOWN, () => { const currentCategoryIndex = categories.findIndex( - ({ id }) => id === categoryActive.id + ({ id }) => id === categoryActive.id, ); if ( currentCategoryIndex === -1 || @@ -52,7 +52,7 @@ export default function SideMenu({ handleSelectCategory(categories[currentCategoryIndex + 1]); }, - { enabled: !isModalShowing } + { enabled: !isModalShowing }, ); return ( diff --git a/src/constants/paths.ts b/src/constants/paths.ts index 3bb554a..59f3a3b 100644 --- a/src/constants/paths.ts +++ b/src/constants/paths.ts @@ -2,6 +2,8 @@ const PATHS = { LOGIN: "/login", LOGOUT: "/logout", HOME: "/", + PRIVACY: "/privacy", + TERMS: "/terms", CATEGORY: { CREATE: "/category/create", EDIT: "/category/edit", diff --git a/src/i18n/index.ts b/src/i18n/index.ts index f833e04..5b31d66 100644 --- a/src/i18n/index.ts +++ b/src/i18n/index.ts @@ -1,11 +1,14 @@ import { serverSideTranslations } from "next-i18next/serverSideTranslations"; import nextI18NextConfig from "../../next-i18next.config"; -async function getServerSideTranslation(locale: string = "en") { +async function getServerSideTranslation( + locale: string = "en", + requiredNs: string[] = [], +) { return await serverSideTranslations( locale, - ["common", "login", "home"], - nextI18NextConfig + ["common", ...requiredNs], + nextI18NextConfig, ); } diff --git a/src/i18n/resources.ts b/src/i18n/resources.ts index 21ae67f..a3cc50c 100644 --- a/src/i18n/resources.ts +++ b/src/i18n/resources.ts @@ -1,11 +1,15 @@ import common from "../../public/locales/en/common.json"; import home from "../../public/locales/en/home.json"; import login from "../../public/locales/en/login.json"; +import privacy from "../../public/locales/en/privacy.json"; +import terms from "../../public/locales/en/terms.json"; const resources = { common, login, home, + privacy, + terms, } as const; export default resources; diff --git a/src/lib/category/getUserCategoryByName.ts b/src/lib/category/getUserCategoryByName.ts index 07b26de..d6fa6f8 100644 --- a/src/lib/category/getUserCategoryByName.ts +++ b/src/lib/category/getUserCategoryByName.ts @@ -3,7 +3,7 @@ import prisma from "utils/prisma"; export default async function getUserCategoryByName( user: User, - name: Category["name"] + name: Category["name"], ) { return await prisma.category.findFirst({ where: { name, authorId: user.id }, diff --git a/src/lib/link/getLinkFromCategoryByName.ts b/src/lib/link/getLinkFromCategoryByName.ts index 4bdaa3f..a554952 100644 --- a/src/lib/link/getLinkFromCategoryByName.ts +++ b/src/lib/link/getLinkFromCategoryByName.ts @@ -4,7 +4,7 @@ import prisma from "utils/prisma"; export default async function getLinkFromCategoryByName( user: User, name: Link["name"], - categoryId: Category["id"] + categoryId: Category["id"], ) { return await prisma.link.findFirst({ where: { diff --git a/src/pages/404.tsx b/src/pages/404.tsx index 881d1d1..ecec484 100644 --- a/src/pages/404.tsx +++ b/src/pages/404.tsx @@ -1,18 +1,17 @@ import PageTransition from "components/PageTransition"; -import PATHS from "constants/paths"; import { NextSeo } from "next-seo"; -import Link from "next/link"; import styles from "styles/error-page.module.scss"; +import NavbarUntranslated from "../components/Navbar/NavbarUntranslated"; export default function Custom404() { return ( - + -
      + +

      404

      Page not found

      -
      - ← Back to home page +
      ); } diff --git a/src/pages/500.tsx b/src/pages/500.tsx index 358f5af..cc819b4 100644 --- a/src/pages/500.tsx +++ b/src/pages/500.tsx @@ -1,18 +1,17 @@ import PageTransition from "components/PageTransition"; -import PATHS from "constants/paths"; import { NextSeo } from "next-seo"; -import Link from "next/link"; import styles from "styles/error-page.module.scss"; +import NavbarUntranslated from "../components/Navbar/NavbarUntranslated"; export default function Custom500() { return ( - + -
      + +

      500

      An internal server error has occurred

      -
      - ← Back to home page +
      ); } diff --git a/src/pages/api/auth/[...nextauth].ts b/src/pages/api/auth/[...nextauth].ts index 8a0c560..fea5223 100644 --- a/src/pages/api/auth/[...nextauth].ts +++ b/src/pages/api/auth/[...nextauth].ts @@ -70,4 +70,4 @@ export const authOptions = { signOut: PATHS.LOGOUT } } as NextAuthOptions; -export default NextAuth(authOptions); \ No newline at end of file +export default NextAuth(authOptions); diff --git a/src/pages/api/favicon.ts b/src/pages/api/favicon.ts index 21eb7a2..eb7a7ab 100644 --- a/src/pages/api/favicon.ts +++ b/src/pages/api/favicon.ts @@ -15,7 +15,7 @@ interface Favicon { export default async function handler( req: NextApiRequest, - res: NextApiResponse + res: NextApiResponse, ) { const urlParam = (req.query?.url as string) || ""; if (!urlParam) { diff --git a/src/pages/api/link/[lid].ts b/src/pages/api/link/[lid].ts index bf553e2..40719f5 100644 --- a/src/pages/api/link/[lid].ts +++ b/src/pages/api/link/[lid].ts @@ -12,7 +12,7 @@ export default apiHandler({ async function editLink({ req, res, user }) { const { lid } = await LinkQuerySchema.validate(req.query); const { name, url, favorite, categoryId } = await LinkBodySchema.validate( - req.body + req.body, ); const link = await getUserLink(user, lid); diff --git a/src/pages/api/link/index.ts b/src/pages/api/link/index.ts index f8746b6..8ddfb7f 100644 --- a/src/pages/api/link/index.ts +++ b/src/pages/api/link/index.ts @@ -11,7 +11,7 @@ export default apiHandler({ async function createLink({ req, res, user }) { const { name, url, favorite, categoryId } = await LinkBodySchema.validate( - req.body + req.body, ); const link = await getUserLinkByName(user, name, categoryId); diff --git a/src/pages/category/create.tsx b/src/pages/category/create.tsx index dc15231..791ba45 100644 --- a/src/pages/category/create.tsx +++ b/src/pages/category/create.tsx @@ -29,7 +29,7 @@ export default function PageCreateCategory({ const canSubmit = useMemo( () => name.length !== 0 && !submitted, - [name.length, submitted] + [name.length, submitted], ); const handleSubmit = async (event: FormEvent) => { @@ -82,5 +82,5 @@ export const getServerSideProps = withAuthentication( ...(await getServerSideTranslation(locale)) } }; - } + }, ); diff --git a/src/pages/category/edit/[cid].tsx b/src/pages/category/edit/[cid].tsx index 4146268..bb71f4f 100644 --- a/src/pages/category/edit/[cid].tsx +++ b/src/pages/category/edit/[cid].tsx @@ -25,7 +25,7 @@ export default function PageEditCategory({ category }: { category: Category }) { const canSubmit = useMemo( () => name !== category.name && name !== "" && !submitted, - [category.name, name, submitted] + [category.name, name, submitted], ); const handleSubmit = async (event: FormEvent) => { @@ -85,5 +85,5 @@ export const getServerSideProps = withAuthentication( ...(await getServerSideTranslation(locale)) } }; - } + }, ); diff --git a/src/pages/category/remove/[cid].tsx b/src/pages/category/remove/[cid].tsx index 1298d50..ba3bd39 100644 --- a/src/pages/category/remove/[cid].tsx +++ b/src/pages/category/remove/[cid].tsx @@ -27,7 +27,7 @@ export default function PageRemoveCategory({ const canSubmit = useMemo( () => category.links.length === 0 && confirmDelete && !submitted, - [category.links.length, confirmDelete, submitted] + [category.links.length, confirmDelete, submitted], ); const handleSubmit = async (event: FormEvent) => { @@ -46,7 +46,9 @@ export default function PageRemoveCategory({ useEffect(() => { setError( - category.links.length > 0 ? t("common:category.remove-description") : null + category.links.length > 0 + ? t("common:category.remove-description") + : null, ); }, [category.links.length, i18n.language, t]); @@ -100,5 +102,5 @@ export const getServerSideProps = withAuthentication( ...(await getServerSideTranslation(locale)) } }; - } + }, ); diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 72fae7b..efac005 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -37,23 +37,23 @@ export default function HomePage(props: HomePageProps) { const [categories, setCategories] = useState(props.categories); const [categoryActive, setCategoryActive] = useState( - props.currentCategory || categories?.[0] + props.currentCategory || categories?.[0], ); const favorites = useMemo( () => categories.reduce((acc, category) => { category.links.forEach((link) => - link.favorite ? acc.push(link) : null + link.favorite ? acc.push(link) : null, ); return acc; }, [] as Link[]), - [categories] + [categories], ); const searchItemBuilder = ( item: Category | Link, - type: SearchItem["type"] + type: SearchItem["type"], ): SearchItem => ({ id: item.id, name: item.name, @@ -66,15 +66,13 @@ export default function HomePage(props: HomePageProps) { }); const itemsSearch = useMemo(() => { - const items = categories.reduce((acc, category) => { + return categories.reduce((acc, category) => { const categoryItem = searchItemBuilder(category, "category"); const items: SearchItem[] = category.links.map((link) => - searchItemBuilder(link, "link") + searchItemBuilder(link, "link"), ); return [...acc, ...items, categoryItem]; }, [] as SearchItem[]); - - return items; }, [categories]); // TODO: refacto @@ -101,7 +99,7 @@ export default function HomePage(props: HomePageProps) { setCategoryActive(categories[categoryIndex]); } }, - [categories, categoryActive.id] + [categories, categoryActive.id], ); const handleSelectCategory = (category: Category) => { @@ -117,7 +115,7 @@ export default function HomePage(props: HomePageProps) { event.preventDefault(); searchModal.open(); }, - areHokeysEnabled + areHokeysEnabled, ); useHotkeys(Keys.CLOSE_SEARCH_KEY, searchModal.close, { enabled: searchModal.isShowing, @@ -129,14 +127,14 @@ export default function HomePage(props: HomePageProps) { () => { router.push(`${PATHS.LINK.CREATE}?categoryId=${categoryActive.id}`); }, - areHokeysEnabled + areHokeysEnabled, ); useHotkeys( Keys.OPEN_CREATE_CATEGORY_KEY, () => { router.push("/category/create"); }, - areHokeysEnabled + areHokeysEnabled, ); return ( @@ -216,7 +214,7 @@ export const getServerSideProps = withAuthentication( } const currentCategory = categories.find( - ({ id }) => id === Number(queryCategoryId) + ({ id }) => id === Number(queryCategoryId), ); return { props: { @@ -225,8 +223,8 @@ export const getServerSideProps = withAuthentication( currentCategory: currentCategory ? JSON.parse(JSON.stringify(currentCategory)) : null, - ...(await getServerSideTranslation(locale)), + ...(await getServerSideTranslation(locale, ["home"])), }, }; - } + }, ); diff --git a/src/pages/link/create.tsx b/src/pages/link/create.tsx index dc991c3..2daf64e 100644 --- a/src/pages/link/create.tsx +++ b/src/pages/link/create.tsx @@ -30,7 +30,7 @@ export default function PageCreateLink({ const [url, setUrl] = useState(""); const [favorite, setFavorite] = useState(false); const [categoryId, setCategoryId] = useState( - Number(categoryIdQuery) || categories?.[0].id || null + Number(categoryIdQuery) || categories?.[0].id || null, ); const [error, setError] = useState(null); @@ -43,7 +43,7 @@ export default function PageCreateLink({ favorite !== null && categoryId !== null && !submitted, - [name, url, favorite, categoryId, submitted] + [name, url, favorite, categoryId, submitted], ); const handleSubmit = (event: FormEvent) => { @@ -126,5 +126,5 @@ export const getServerSideProps = withAuthentication( ...(await getServerSideTranslation(locale)) } }; - } + }, ); diff --git a/src/pages/link/edit/[lid].tsx b/src/pages/link/edit/[lid].tsx index 8df5182..9c6d95d 100644 --- a/src/pages/link/edit/[lid].tsx +++ b/src/pages/link/edit/[lid].tsx @@ -32,7 +32,7 @@ export default function PageEditLink({ const [url, setUrl] = useState(link.url); const [favorite, setFavorite] = useState(link.favorite); const [categoryId, setCategoryId] = useState( - link.category?.id || null + link.category?.id || null, ); const [error, setError] = useState(null); @@ -145,5 +145,5 @@ export const getServerSideProps = withAuthentication( ...(await getServerSideTranslation(locale)) } }; - } + }, ); diff --git a/src/pages/link/remove/[lid].tsx b/src/pages/link/remove/[lid].tsx index b34d1b2..c6a25e3 100644 --- a/src/pages/link/remove/[lid].tsx +++ b/src/pages/link/remove/[lid].tsx @@ -23,7 +23,7 @@ export default function PageRemoveLink({ link }: { link: Link }) { const canSubmit = useMemo( () => confirmDelete && !submitted, - [confirmDelete, submitted] + [confirmDelete, submitted], ); const handleSubmit = async (event: FormEvent) => { @@ -109,5 +109,5 @@ export const getServerSideProps = withAuthentication( ...(await getServerSideTranslation(locale)) } }; - } + }, ); diff --git a/src/pages/login.tsx b/src/pages/login.tsx index a158e4d..2d6bf4c 100644 --- a/src/pages/login.tsx +++ b/src/pages/login.tsx @@ -3,7 +3,7 @@ import LangSelector from "components/LangSelector"; import MessageManager from "components/MessageManager/MessageManager"; import PageTransition from "components/PageTransition"; import PATHS from "constants/paths"; -import { getServerSideTranslation } from "i18n/index"; +import { getServerSideTranslation } from "../i18n"; import getUser from "lib/user/getUser"; import { Provider } from "next-auth/providers"; import { getProviders, signIn } from "next-auth/react"; @@ -17,6 +17,7 @@ import { getSession } from "utils/session"; interface SignInProps { providers: Provider[]; } + export default function SignIn({ providers }: SignInProps) { const { t } = useTranslation("login"); @@ -70,7 +71,7 @@ export async function getServerSideProps({ req, res, locale }) { props: { session, providers, - ...(await getServerSideTranslation(locale)), + ...(await getServerSideTranslation(locale, ["login"])), }, }; } diff --git a/src/pages/privacy.tsx b/src/pages/privacy.tsx new file mode 100644 index 0000000..016d13e --- /dev/null +++ b/src/pages/privacy.tsx @@ -0,0 +1,76 @@ +import LinkTag from "next/link"; +import PageTransition from "components/PageTransition"; +import styles from "styles/legal-pages.module.scss"; +import clsx from "clsx"; +import Navbar from "../components/Navbar/Navbar"; +import { getServerSideTranslation } from "../i18n"; +import { useTranslation } from "next-i18next"; +import { TFunctionParam } from "../types/i18next"; + +export default function Privacy() { + const { t } = useTranslation("privacy"); + + return ( + + +
      +

      {t("privacy:title")}

      +

      + {t("privacy:edited_at", { date: "19/11/2023" } as TFunctionParam)} +

      +

      {t("privacy:welcome")}

      + +

      {t("privacy:collect.title")}

      +

      {t("privacy:collect.cookie.title")}

      +

      {t("privacy:collect.cookie.description")}

      + +

      {t("privacy:collect.user.title")}

      +

      {t("privacy:collect.user.description")}

      +
        + {( + t("privacy:collect.user.fields", { + returnObjects: true, + } as TFunctionParam) as Array + ).map((field) => ( +
      • {field}
      • + ))} +
      + +

      {t("privacy:data_use.title")}

      +

      {t("privacy:data_use.description")}

      + +

      {t("privacy:data_storage.title")}

      +

      {t("privacy:data_storage.description")}

      + +

      {t("privacy:data_storage.data_retention.title")}

      +

      {t("privacy:data_storage.data_retention.description")}

      + +

      {t("privacy:user_rights.title")}

      +

      {t("privacy:user_rights.description")}

      + +

      {t("privacy:rgpd.title")}

      +

      {t("privacy:rgpd.description")}

      + +

      {t("privacy:contact.title")}

      +

      + {t("privacy:contact.description")}{" "} + + sonnyasdev[at]gmail.com + + . +

      + +

      {t("privacy:footer.changes")}

      +

      {t("privacy:footer.thanks")}

      +
      +
      + ); +} + +export async function getServerSideProps({ locale }) { + return { + props: { + ...(await getServerSideTranslation(locale, ["privacy"])), + }, + }; +} diff --git a/src/pages/terms.tsx b/src/pages/terms.tsx new file mode 100644 index 0000000..22738ae --- /dev/null +++ b/src/pages/terms.tsx @@ -0,0 +1,111 @@ +import PageTransition from "components/PageTransition"; +import styles from "styles/legal-pages.module.scss"; +import clsx from "clsx"; +import LinkTag from "next/link"; +import Navbar from "components/Navbar/Navbar"; +import { getServerSideTranslation } from "../i18n"; + +export default function Terms() { + return ( + + +
      +

      Conditions Générales d'Utilisation de MyLinks

      +

      Dernière mise à jour : 19/11/2023

      +

      + Bienvenue sur MyLinks, un gestionnaire de favoris gratuit et open + source axé sur la privacy et le self hosting. En utilisant ce service, + vous acceptez les conditions générales d'utilisation énoncées + ci-dessous. Veuillez les lire attentivement. +

      + +

      1. Acceptation des Conditions

      +

      + En accédant à MyLinks et en utilisant nos services, vous acceptez de + vous conformer à ces Conditions Générales d'Utilisation. +

      + +

      2. Utilisation du Service

      +

      2.1 Compte Utilisateur

      +

      + Pour accéder à certaines fonctionnalités de MyLinks, vous devrez créer + un compte utilisateur. Vous êtes responsable de la confidentialité de + votre compte et de vos informations d'identification. +

      + +

      2.2 Utilisation Autorisée

      +

      + Vous vous engagez à utiliser MyLinks conformément aux lois en vigueur + et à ne pas violer les droits de tiers. +

      + +

      2.3 Contenu Utilisateur

      +

      + En publiant du contenu sur MyLinks, vous accordez à MyLinks une + licence mondiale, non exclusive, transférable et gratuite pour + utiliser, reproduire, distribuer et afficher ce contenu. +

      + +

      3. Données Personnelles

      +

      3.1 Collecte et Utilisation

      +

      + Les données personnelles collectées sont utilisées conformément à + notre Politique de Confidentialité. + En utilisant MyLinks, vous consentez à cette collecte et utilisation. +

      + +

      3.2 Suppression de Compte

      +

      + Vous pouvez demander la suppression de votre compte à tout moment + conformément à notre Politique de Confidentialité. +

      + +

      4. Responsabilités et Garanties

      +

      4.1 Responsabilité

      +

      + MyLinks ne peut être tenu responsable des dommages directs ou + indirects découlant de l'utilisation de nos services. +

      + +

      4.2 Garanties

      +

      + MyLinks ne garantit pas que le service sera exempt d'erreurs ou de + interruptions. +

      + +

      5. Modifications des Conditions

      +

      + MyLinks se réserve le droit de modifier ces Conditions Générales + d'Utilisation à tout moment. Les utilisateurs seront informés des + changements par le biais d'une notification sur le site. +

      + +

      6. Résiliation

      +

      + MyLinks se réserve le droit de résilier ou de suspendre votre accès au + service, avec ou sans préavis, en cas de violation de ces Conditions + Générales d'Utilisation. +

      + +

      7. Contact

      +

      + Pour toute question ou préoccupation concernant ces Conditions + Générales d'Utilisation, veuillez nous contacter à l'adresse suivante + :{" "} + + sonnyasdev[at]gmail.com + + . +

      +
      +
      + ); +} + +export async function getServerSideProps({ locale }) { + return { + props: { + ...(await getServerSideTranslation(locale)), + }, + }; +} diff --git a/src/styles/error-page.module.scss b/src/styles/error-page.module.scss index 0588418..dcd33b6 100644 --- a/src/styles/error-page.module.scss +++ b/src/styles/error-page.module.scss @@ -1,28 +1,30 @@ @import "keyframes.scss"; .App { - margin-top: 10em; - margin-bottom: 3em; - display: flex; - align-items: center; - justify-content: center; - animation: fadein 250ms both; + margin-top: 3em; - & h1 { - font-size: 1.75em; - font-weight: 500; - margin: 0; - margin-right: 1em; - border-right: 1px solid rgba(0, 0, 0, 0.3); - padding: 10px 23px 10px 0; - display: inline-block; - } + & header { + margin-top: 3em; + display: flex; + align-items: center; + justify-content: center; - & h2 { - font-size: 1em; - font-weight: normal; - line-height: inherit; - margin: 0; - padding: 0; + & h1 { + font-size: 1.75em; + font-weight: 500; + margin: 0; + margin-right: 1em; + border-right: 1px solid rgba(0, 0, 0, 0.3); + padding: 10px 23px 10px 0; + display: inline-block; + } + + & h2 { + font-size: 1em; + font-weight: normal; + line-height: inherit; + margin: 0; + padding: 0; + } } } diff --git a/src/styles/globals.scss b/src/styles/globals.scss index a978df6..11b82a9 100644 --- a/src/styles/globals.scss +++ b/src/styles/globals.scss @@ -1,7 +1,7 @@ @import "keyframes.scss"; @import "colors.scss"; -* { +*:not(ul) { box-sizing: border-box; outline: 0; margin: 0; @@ -33,7 +33,6 @@ body { width: 1280px; padding: 10px; display: flex; - justify-content: center; } a { @@ -59,11 +58,6 @@ h6 { color: $blue; } -ul, -li { - list-style: none; -} - /* width */ ::-webkit-scrollbar { height: 0.45em; @@ -106,6 +100,7 @@ button:not(.reset) { color: $white; } } + button.red-btn { cursor: pointer; width: 100%; @@ -180,6 +175,7 @@ select:not(.nostyle) { padding: 0; margin: 0; border: 0; + list-style: none; } kbd { @@ -189,7 +185,9 @@ kbd { padding: 0.25em 0.5em; border-radius: 3px; border: 1px solid rgb(204, 204, 204); - box-shadow: 0 1px 0px rgba(0, 0, 0, 0.2), 0 0 0 2px #ffffff inset; + box-shadow: + 0 1px 0 rgba(0, 0, 0, 0.2), + 0 0 0 2px #ffffff inset; display: inline-block; } diff --git a/src/styles/legal-pages.module.scss b/src/styles/legal-pages.module.scss new file mode 100644 index 0000000..3eab0de --- /dev/null +++ b/src/styles/legal-pages.module.scss @@ -0,0 +1,24 @@ +.privacy { + height: 100%; + flex-direction: column; +} + +.privacy main { + overflow: auto; +} + +.privacy h1 { + margin-top: 0.5em; +} + +.privacy h2 { + margin-top: 1.5em; +} + +.privacy h3 { + margin-top: 1em; +} + +.privacy p { + margin-top: 0.5em; +} diff --git a/src/utils/session.ts b/src/utils/session.ts index 3e88821..50bb356 100644 --- a/src/utils/session.ts +++ b/src/utils/session.ts @@ -19,7 +19,7 @@ export function withAuthentication(serverSidePropsFunc) { const session = await getSession( req as NextApiRequest, - res as NextApiResponse + res as NextApiResponse, ); const user = await getUser(session); if (!session || !user) {