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")}
window.location.reload()}>
- {this.props.t('common:retry')}
+ {this.props.t("common:retry")}
{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 (
+
+
+
+ MyLinks
+
+
+ {t("common:privacy")}
+
+
+ {t("common:terms")}
+
+ {status === "authenticated" ? (
+
+ {t("common:logout")}
+
+ ) : (
+
+ {t("common:login")}
+
+ )}
+
+
+ );
+}
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 (
+
+
+
+ MyLinks
+
+
+ Privacy
+
+
+ Terms of use
+
+ {status === "authenticated" ? (
+
+ Logout
+
+ ) : (
+
+ Login
+
+ )}
+
+
+ );
+}
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 (
-
+
-
+
+
- ← 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) {