diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..f42b52a --- /dev/null +++ b/.eslintignore @@ -0,0 +1,2 @@ +*.js +*.ts \ No newline at end of file diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..3285bef --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,214 @@ +module.exports = { + parser: "@typescript-eslint/parser", + parserOptions: { + project: `./tsconfig.json`, + }, + plugins: ["@typescript-eslint"], + extends: [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "next/core-web-vitals", + ], + rules: { + /* POSSIBLES PROBLEMS */ + + // "array-callback-return": "error", + "no-await-in-loop": "error", + "no-constructor-return": "error", + "no-promise-executor-return": "error", + "no-self-compare": "error", + "no-template-curly-in-string": "error", + "no-unmodified-loop-condition": "error", + "no-unreachable-loop": "error", + "no-unused-private-class-members": "error", + // "no-use-before-define": "error", + "require-atomic-updates": "error", + + /* SUGGESTIONS */ + + "accessor-pairs": "warn", + "arrow-body-style": "warn", + "block-scoped-var": "warn", + // camelcase: "warn", + // "capitalized-comments": "warn", + // "class-methods-use-this": "warn", + // complexity: "warn", + "consistent-return": "warn", + "consistent-this": "warn", + // curly: "warn", + "default-case": "warn", + "default-case-last": "warn", + eqeqeq: "error", + "func-name-matching": "warn", + "func-names": "warn", + "func-style": ["warn", "declaration"], + "grouped-accessor-pairs": "warn", + "guard-for-in": "warn", + "id-denylist": ["error", "data", "err", "e", "cb", "callback", "i"], + // "id-length": "warn", + "id-match": "warn", + "max-classes-per-file": ["error", 1], + // "max-depth": ["warn", 4], + // "max-lines": "warn", + // "max-lines-per-function": "warn", + // "max-nested-callbacks": "warn", + // "max-params": "warn", + // "max-statements": "warn", + "multiline-comment-style": "warn", + "new-cap": "warn", + "no-alert": "warn", + "no-bitwise": "warn", + "no-caller": "warn", + "no-confusing-arrow": "warn", + "no-continue": "warn", + "no-else-return": "warn", + "no-eq-null": "warn", + "no-eval": "warn", + "no-extend-native": "warn", + "no-extra-bind": "warn", + "no-floating-decimal": "warn", + "no-implicit-coercion": "warn", + "no-implicit-globals": "warn", + "no-inline-comments": "warn", + "no-iterator": "warn", + "no-label-var": "warn", + "no-labels": "warn", + "no-lone-blocks": "warn", + "no-lonely-if": "warn", + // "no-magic-numbers": "warn", + "no-mixed-operators": "warn", + "no-multi-assign": "warn", + "no-multi-str": "warn", + "no-negated-condition": "warn", + // "no-nested-ternary": "warn", + "no-new": "warn", + "no-new-func": "warn", + "no-new-object": "warn", + "no-new-wrappers": "warn", + "no-octal-escape": "warn", + "no-param-reassign": "warn", + "no-plusplus": "warn", + "no-proto": "warn", + "no-restricted-exports": "warn", + "no-restricted-globals": "warn", + "no-restricted-imports": "warn", + "no-restricted-properties": "warn", + "no-restricted-syntax": "warn", + "no-return-assign": "warn", + // "no-return-await": "warn", + "no-script-url": "warn", + "no-sequences": "warn", + // "no-ternary": "off", + "no-throw-literal": "warn", + "no-undef": "off", + "no-undef-init": "warn", + // "no-undefined": "warn", + // "no-underscore-dangle": "warn", + "no-unneeded-ternary": "warn", + "no-useless-call": "warn", + "no-useless-computed-key": "warn", + "no-useless-concat": "warn", + "no-useless-rename": "warn", + "no-useless-return": "warn", + "no-var": "warn", + "no-void": "warn", + "no-warning-comments": "warn", + // "object-shorthand": "warn", + "operator-assignment": "warn", + "prefer-arrow-callback": "warn", + "prefer-const": "warn", + "prefer-destructuring": ["warn", { array: false, object: true }], + "prefer-exponentiation-operator": "warn", + "prefer-named-capture-group": "warn", + "prefer-numeric-literals": "warn", + // "prefer-object-has-own": "warn", + "prefer-object-spread": "warn", + "prefer-promise-reject-errors": "warn", + "prefer-regex-literals": "warn", + "prefer-rest-params": "warn", + "prefer-spread": "warn", + "prefer-template": "warn", + // "quote-props": "warn", + radix: "warn", + "require-unicode-regexp": "warn", + // "sort-imports": "warn", + // "sort-keys": "warn", + "sort-vars": "warn", + "spaced-comment": "warn", + strict: "warn", + "symbol-description": "warn", + "vars-on-top": "warn", + yoda: "warn", + + /* TYPESCRIPT */ + + "@typescript-eslint/array-type": "warn", + "@typescript-eslint/ban-tslint-comment": "warn", + "@typescript-eslint/class-literal-property-style": "warn", + "@typescript-eslint/consistent-indexed-object-style": "warn", + "@typescript-eslint/consistent-type-assertions": [ + "warn", + { assertionStyle: "as" }, + ], + "@typescript-eslint/consistent-type-exports": "error", + "@typescript-eslint/explicit-module-boundary-types": "warn", + "@typescript-eslint/method-signature-style": ["error", "property"], + "@typescript-eslint/no-base-to-string": "warn", + "@typescript-eslint/no-confusing-non-null-assertion": "warn", + "@typescript-eslint/no-confusing-void-expression": [ + "error", + { ignoreArrowShorthand: true }, + ], + "@typescript-eslint/no-dynamic-delete": "error", + "@typescript-eslint/no-empty-interface": [ + "error", + { allowSingleExtends: true }, + ], + "@typescript-eslint/no-invalid-void-type": "error", + "@typescript-eslint/no-meaningless-void-operator": "error", + "@typescript-eslint/no-non-null-asserted-nullish-coalescing": "error", + "@typescript-eslint/no-parameter-properties": "error", + "@typescript-eslint/no-require-imports": "error", + // "@typescript-eslint/no-type-alias": "warn", + "@typescript-eslint/no-unnecessary-boolean-literal-compare": "warn", + // "@typescript-eslint/no-unnecessary-condition": "warn", + "@typescript-eslint/no-unnecessary-qualifier": "warn", + "@typescript-eslint/no-unnecessary-type-arguments": "warn", + "@typescript-eslint/prefer-enum-initializers": "error", + "@typescript-eslint/prefer-for-of": "error", + "@typescript-eslint/prefer-includes": "error", + "@typescript-eslint/prefer-literal-enum-member": "error", + "@typescript-eslint/prefer-nullish-coalescing": "warn", + "@typescript-eslint/prefer-optional-chain": "warn", + "@typescript-eslint/prefer-readonly": "warn", + // "@typescript-eslint/prefer-readonly-parameter-types": "warn", + "@typescript-eslint/prefer-reduce-type-parameter": "warn", + // "@typescript-eslint/prefer-regexp-exec": "warn", + "@typescript-eslint/prefer-return-this-type": "warn", + "@typescript-eslint/prefer-string-starts-ends-with": "error", + "@typescript-eslint/promise-function-async": "error", + "@typescript-eslint/require-array-sort-compare": "error", + "@typescript-eslint/sort-type-union-intersection-members": "warn", + // "@typescript-eslint/strict-boolean-expressions": "error", + "@typescript-eslint/switch-exhaustiveness-check": "error", + "@typescript-eslint/typedef": "error", + "@typescript-eslint/unified-signatures": "error", + + /* EXTENSION OF ESLINT */ + "@typescript-eslint/no-duplicate-imports": "error", + "@typescript-eslint/default-param-last": "warn", + "@typescript-eslint/dot-notation": "warn", + "@typescript-eslint/init-declarations": "warn", + "@typescript-eslint/no-array-constructor": "warn", + "@typescript-eslint/no-implied-eval": "warn", + "@typescript-eslint/no-invalid-this": "warn", + "@typescript-eslint/no-loop-func": "warn", + "@typescript-eslint/no-shadow": "warn", + "@typescript-eslint/no-unused-expressions": "warn", + "@typescript-eslint/no-useless-constructor": "warn", + "@typescript-eslint/require-await": "warn", + + /* NEXTJS */ + "@next/next/no-img-element": "off", + }, +}; diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index bffb357..0000000 --- a/.eslintrc.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "next/core-web-vitals" -} diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml new file mode 100644 index 0000000..a5242e0 --- /dev/null +++ b/.github/workflows/node.js.yml @@ -0,0 +1,37 @@ +# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node +# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions + +name: Node.js CI + +on: +# push: +# branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + build: + + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [16.x] + # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ + + steps: + - uses: actions/checkout@v2 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v2 + with: + node-version: ${{ matrix.node-version }} + cache: 'npm' + - run: npm ci + - run: npm run lint + - run: npm run build --if-present + env: + ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }} + NEXT_PUBLIC_URL_CMS: ${{ secrets.NEXT_PUBLIC_URL_CMS }} + NEXT_PUBLIC_URL_IMG: ${{ secrets.NEXT_PUBLIC_URL_IMG }} + NEXT_PUBLIC_URL_SELF: ${{ secrets.NEXT_PUBLIC_URL_SELF }} + URL_GRAPHQL: ${{ secrets.URL_GRAPHQL }} diff --git a/.gitignore b/.gitignore index 027613e..e1be44d 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ # production /build +/public/sitemap* # misc .DS_Store diff --git a/.hintrc b/.hintrc new file mode 100644 index 0000000..158ec71 --- /dev/null +++ b/.hintrc @@ -0,0 +1,10 @@ +{ + "extends": ["development"], + "hints": { + "no-inline-styles": "off", + "apple-touch-icons": "off", + "compat-api/html": "off", + "axe/text-alternatives": "off", + "axe/parsing": "off" + } +} diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..a680367 --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +.next diff --git a/README.md b/README.md index 6b245ca..7838a71 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,9 @@ # Accords-library.com +[![Node.js CI](https://github.com/Accords-Library/accords-library.com/actions/workflows/node.js.yml/badge.svg?branch=main)](https://github.com/Accords-Library/accords-library.com/actions/workflows/node.js.yml) +[![GitHub](https://img.shields.io/github/license/Accords-Library/accords-library.com?style=flat-square)](https://github.com/Accords-Library/accords-library.com/blob/main/LICENSE) +![Libraries.io dependency status for GitHub repo](https://img.shields.io/librariesio/github/Accords-Library/accords-library.com?style=flat-square) + ## Technologies #### [Back](https://github.com/Accords-Library/strapi.accords-library.com) @@ -9,6 +13,14 @@ - Multilanguage support - Markdown format for the rich text fields +#### [Image Processor](https://github.com/Accords-Library/img.accords-library.com) + +- Convert the images from the CMS to 4 formats + - Small: 512x512, quality 60, .webp + - Medium: 1024x1024, quality 75, .webp + - Large: 2048x2048, quality 80, .webp + - Og: 512x512, quality 60, .jpg + #### [Front](https://github.com/Accords-Library/accords-library.com) (this repository) - Language: [TypeScript](https://www.typescriptlang.org/) @@ -29,6 +41,12 @@ - State Management: [React Context](https://reactjs.org/docs/context.html) - Persistent app state using LocalStorage - Support for many screen sizes and resolutions +- SSG (Static Site Generation): + - The website is built before running in production + - Performances are great, and possibility to deploy the app using a CDN +- OpenGraph and Metadata + - Good defaults for the metadate and OpenGraph properties + - Each page can provide the thumbnail, title, description to be used - Data quality testing - Data from the CMS is subject to a battery of tests (about 20 warning types and 40 error types) at build time - Each warning/error comes with a front-end link to the incriminating element, as well as a link to the CMS to fix it. @@ -53,6 +71,9 @@ Enter the followind information: ```txt URL_GRAPHQL=https://url-to.strapi-accords-library.com/graphql ACCESS_TOKEN=genatedcode-by-strapi-api +SMTP_HOST=email.provider.com +SMTP_USER=email@example.com +SMTP_PASSWORD=mypassword123 NEXT_PUBLIC_URL_CMS=https://url-to.strapi-accords-library.com/ NEXT_PUBLIC_URL_IMG=https://url-to.img-accords-library.com/ NEXT_PUBLIC_URL_SELF=https://url-to-front-accords-library.com diff --git a/next-env.d.ts b/next-env.d.ts index 4f11a03..62b8a52 100644 --- a/next-env.d.ts +++ b/next-env.d.ts @@ -1,5 +1,5 @@ -/// -/// - -// NOTE: This file should not be edited -// see https://nextjs.org/docs/basic-features/typescript for more information. +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/next-sitemap.js b/next-sitemap.js new file mode 100644 index 0000000..5bd51b9 --- /dev/null +++ b/next-sitemap.js @@ -0,0 +1,28 @@ +/** @type {import('next-sitemap').IConfig} */ +module.exports = { + siteUrl: process.env.NEXT_PUBLIC_URL_SELF && "https://accords-library.com", + generateRobotsTxt: true, + alternateRefs: [ + { + href: `${process.env.NEXT_PUBLIC_URL_SELF}/en/`, + hreflang: "en", + }, + { + href: `${process.env.NEXT_PUBLIC_URL_SELF}/fr/`, + hreflang: "fr", + }, + { + href: `${process.env.NEXT_PUBLIC_URL_SELF}/ja/`, + hreflang: "ja", + }, + { + href: `${process.env.NEXT_PUBLIC_URL_SELF}/es/`, + hreflang: "es", + }, + { + href: `${process.env.NEXT_PUBLIC_URL_SELF}/pt-br/`, + hreflang: "pt-br", + }, + ], + exclude: ["/en/*", "/fr/*", "/ja/*", "/es/*", "/pt-br/*"], +}; diff --git a/next.config.js b/next.config.js index f6c43d3..5d7877c 100644 --- a/next.config.js +++ b/next.config.js @@ -3,7 +3,7 @@ module.exports = { swcMinify: true, reactStrictMode: true, i18n: { - locales: ["en", "fr", "ja", "es", "xx"], + locales: ["en", "fr", "ja", "es", "pt-br"], defaultLocale: "en", }, images: { diff --git a/package-lock.json b/package-lock.json index 5628ae9..f3fd0d0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,25 +8,33 @@ "dependencies": { "@fontsource/material-icons": "^4.5.2", "@fontsource/material-icons-rounded": "^4.5.2", + "@fontsource/opendyslexic": "^4.5.2", "@fontsource/vollkorn": "^4.5.4", "@fontsource/zen-maru-gothic": "^4.5.5", - "markdown-to-jsx": "^7.1.6", + "@tippyjs/react": "^4.2.6", + "markdown-to-jsx": "^7.1.7", "next": "^12.1.0", + "nodemailer": "^6.7.3", "react": "17.0.2", "react-dom": "17.0.2", + "react-image-lightbox": "^5.1.4", "react-swipeable": "^6.2.0", - "react-tooltip": "^4.2.21", "turndown": "^7.1.1" }, "devDependencies": { - "@tailwindcss/typography": "^0.5.2", - "@types/node": "17.0.18", - "@types/react": "17.0.39", - "@types/react-dom": "^17.0.11", - "eslint": "8.9.0", + "@types/node": "17.0.21", + "@types/nodemailer": "^6.4.4", + "@types/react": "17.0.40", + "@types/react-dom": "^17.0.13", + "@types/turndown": "^5.0.1", + "@typescript-eslint/eslint-plugin": "^5.16.0", + "@typescript-eslint/parser": "^5.16.0", + "eslint": "^8.10.0", "eslint-config-next": "12.1.0", + "next-sitemap": "^2.5.14", + "prettier-plugin-organize-imports": "^2.3.4", "tailwindcss": "^3.0.23", - "typescript": "4.5.5" + "typescript": "^4.6.2" } }, "node_modules/@babel/code-frame": { @@ -160,10 +168,16 @@ "node": ">=6.9.0" } }, + "node_modules/@corex/deepmerge": { + "version": "2.6.148", + "resolved": "https://registry.npmjs.org/@corex/deepmerge/-/deepmerge-2.6.148.tgz", + "integrity": "sha512-6QMz0/2h5C3ua51iAnXMPWFbb1QOU1UvSM4bKBw5mzdT+WtLgjbETBBIQZ+Sh9WvEcGwlAt/DEdRpIC3XlDBMA==", + "dev": true + }, "node_modules/@eslint/eslintrc": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.1.0.tgz", - "integrity": "sha512-C1DfL7XX4nPqGd6jcP01W9pVM1HYCuUkFk1432D7F0v3JSlUIeOYn9oCoi3eoLZ+iwBSb29BMFxxny0YrrEZqg==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.0.tgz", + "integrity": "sha512-igm9SjJHNEJRiUnecP/1R5T3wKLEJ7pL6e2P+GUSfCd0dGjPYYZve08uzw8L2J8foVHFz+NGu12JxRcU2gGo6w==", "dev": true, "dependencies": { "ajv": "^6.12.4", @@ -199,6 +213,11 @@ "resolved": "https://registry.npmjs.org/@fontsource/material-icons-rounded/-/material-icons-rounded-4.5.2.tgz", "integrity": "sha512-wk/vqodMF+4IBbxhI0cjaPBcouvRrnJdeQCslY0Zae8ojyZCUksJn4JTiQk89fbY9kvT3oG7AZIZ+poKdpS02w==" }, + "node_modules/@fontsource/opendyslexic": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/@fontsource/opendyslexic/-/opendyslexic-4.5.2.tgz", + "integrity": "sha512-vW+A3Bd1ZEG6nAZuix0OhbS0ygMlhvtc3RvLlDXrTAinrAZHQ0bOGUJRN2iaGbQ1kWNP8/7A+AKMFH5FM/pjKA==" + }, "node_modules/@fontsource/vollkorn": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/@fontsource/vollkorn/-/vollkorn-4.5.4.tgz", @@ -443,26 +462,40 @@ "node": ">= 8" } }, + "node_modules/@popperjs/core": { + "version": "2.11.3", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.3.tgz", + "integrity": "sha512-8U7hIl7+30XbIrJ0deQMXpXESM1L4yrt6BHok5hzcR0LivivuNkk+tHU1iRVScOwCmQcrOr6kvtIr29MNbQHqQ==", + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, "node_modules/@rushstack/eslint-patch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.1.0.tgz", "integrity": "sha512-JLo+Y592QzIE+q7Dl2pMUtt4q8SKYI5jDrZxrozEQxnGVOyYE+GWK9eLkwTaeN9DDctlaRAQ3TBmzZ1qdLE30A==", "dev": true }, - "node_modules/@tailwindcss/typography": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.2.tgz", - "integrity": "sha512-coq8DBABRPFcVhVIk6IbKyyHUt7YTEC/C992tatFB+yEx5WGBQrCgsSFjxHUr8AWXphWckadVJbominEduYBqw==", - "dev": true, + "node_modules/@tippyjs/react": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/@tippyjs/react/-/react-4.2.6.tgz", + "integrity": "sha512-91RicDR+H7oDSyPycI13q3b7o4O60wa2oRbjlz2fyRLmHImc4vyDwuUP8NtZaN0VARJY5hybvDYrFzhY9+Lbyw==", "dependencies": { - "lodash.castarray": "^4.4.0", - "lodash.isplainobject": "^4.0.6", - "lodash.merge": "^4.6.2" + "tippy.js": "^6.3.1" }, "peerDependencies": { - "tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1 || insiders" + "react": ">=16.8", + "react-dom": ">=16.8" } }, + "node_modules/@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true + }, "node_modules/@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", @@ -470,11 +503,20 @@ "dev": true }, "node_modules/@types/node": { - "version": "17.0.18", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.18.tgz", - "integrity": "sha512-eKj4f/BsN/qcculZiRSujogjvp5O/k4lOW5m35NopjZM/QwLOR075a8pJW5hD+Rtdm2DaCVPENS6KtSQnUD6BA==", + "version": "17.0.21", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz", + "integrity": "sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==", "dev": true }, + "node_modules/@types/nodemailer": { + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.4.tgz", + "integrity": "sha512-Ksw4t7iliXeYGvIQcSIgWQ5BLuC/mljIEbjf615svhZL10PE9t+ei8O9gDaD3FPCasUJn9KTLwz2JFJyiiyuqw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", @@ -488,9 +530,9 @@ "dev": true }, "node_modules/@types/react": { - "version": "17.0.39", - "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.39.tgz", - "integrity": "sha512-UVavlfAxDd/AgAacMa60Azl7ygyQNRwC/DsHZmKgNvPmRR5p70AJ5Q9EAmL2NWOJmeV+vVUI4IAP7GZrN8h8Ug==", + "version": "17.0.40", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.40.tgz", + "integrity": "sha512-UrXhD/JyLH+W70nNSufXqMZNuUD2cXHu6UjCllC6pmOQgBX4SGXOH8fjRka0O0Ee0HrFxapDD8Bwn81Kmiz6jQ==", "dev": true, "dependencies": { "@types/prop-types": "*", @@ -499,9 +541,9 @@ } }, "node_modules/@types/react-dom": { - "version": "17.0.11", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.11.tgz", - "integrity": "sha512-f96K3k+24RaLGVu/Y2Ng3e1EbZ8/cVJvypZWd7cy0ofCBaf2lcM46xNhycMZ2xGwbBjRql7hOlZ+e2WlJ5MH3Q==", + "version": "17.0.13", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.13.tgz", + "integrity": "sha512-wEP+B8hzvy6ORDv1QBhcQia4j6ea4SFIBttHYpXKPFZRviBvknq0FRh3VrIxeXUmsPkwuXVZrVGG7KUVONmXCQ==", "dev": true, "dependencies": { "@types/react": "*" @@ -513,15 +555,54 @@ "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==", "dev": true }, - "node_modules/@typescript-eslint/parser": { - "version": "5.12.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.12.0.tgz", - "integrity": "sha512-MfSwg9JMBojMUoGjUmX+D2stoQj1CBYTCP0qnnVtu9A+YQXVKNtLjasYh+jozOcrb/wau8TCfWOkQTiOAruBog==", + "node_modules/@types/turndown": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@types/turndown/-/turndown-5.0.1.tgz", + "integrity": "sha512-N8Ad4e3oJxh9n9BiZx9cbe/0M3kqDpOTm2wzj13wdDUxDPjfjloWIJaquZzWE1cYTAHpjOH3rcTnXQdpEfS/SQ==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.16.0.tgz", + "integrity": "sha512-SJoba1edXvQRMmNI505Uo4XmGbxCK9ARQpkvOd00anxzri9RNQk0DDCxD+LIl+jYhkzOJiOMMKYEHnHEODjdCw==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.12.0", - "@typescript-eslint/types": "5.12.0", - "@typescript-eslint/typescript-estree": "5.12.0", + "@typescript-eslint/scope-manager": "5.16.0", + "@typescript-eslint/type-utils": "5.16.0", + "@typescript-eslint/utils": "5.16.0", + "debug": "^4.3.2", + "functional-red-black-tree": "^1.0.1", + "ignore": "^5.1.8", + "regexpp": "^3.2.0", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.16.0.tgz", + "integrity": "sha512-fkDq86F0zl8FicnJtdXakFs4lnuebH6ZADDw6CYQv0UZeIjHvmEw87m9/29nk2Dv5Lmdp0zQ3zDQhiMWQf/GbA==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.16.0", + "@typescript-eslint/types": "5.16.0", + "@typescript-eslint/typescript-estree": "5.16.0", "debug": "^4.3.2" }, "engines": { @@ -541,13 +622,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.12.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.12.0.tgz", - "integrity": "sha512-GAMobtIJI8FGf1sLlUWNUm2IOkIjvn7laFWyRx7CLrv6nLBI7su+B7lbStqVlK5NdLvHRFiJo2HhiDF7Ki01WQ==", + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.16.0.tgz", + "integrity": "sha512-P+Yab2Hovg8NekLIR/mOElCDPyGgFZKhGoZA901Yax6WR6HVeGLbsqJkZ+Cvk5nts/dAlFKm8PfL43UZnWdpIQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.12.0", - "@typescript-eslint/visitor-keys": "5.12.0" + "@typescript-eslint/types": "5.16.0", + "@typescript-eslint/visitor-keys": "5.16.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -557,10 +638,36 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@typescript-eslint/type-utils": { + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.16.0.tgz", + "integrity": "sha512-SKygICv54CCRl1Vq5ewwQUJV/8padIWvPgCxlWPGO/OgQLCijY9G7lDu6H+mqfQtbzDNlVjzVWQmeqbLMBLEwQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/utils": "5.16.0", + "debug": "^4.3.2", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/@typescript-eslint/types": { - "version": "5.12.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.12.0.tgz", - "integrity": "sha512-JowqbwPf93nvf8fZn5XrPGFBdIK8+yx5UEGs2QFAYFI8IWYfrzz+6zqlurGr2ctShMaJxqwsqmra3WXWjH1nRQ==", + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.16.0.tgz", + "integrity": "sha512-oUorOwLj/3/3p/HFwrp6m/J2VfbLC8gjW5X3awpQJ/bSG+YRGFS4dpsvtQ8T2VNveV+LflQHjlLvB6v0R87z4g==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -571,13 +678,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.12.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.12.0.tgz", - "integrity": "sha512-Dd9gVeOqt38QHR0BEA8oRaT65WYqPYbIc5tRFQPkfLquVEFPD1HAtbZT98TLBkEcCkvwDYOAvuSvAD9DnQhMfQ==", + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.16.0.tgz", + "integrity": "sha512-SE4VfbLWUZl9MR+ngLSARptUv2E8brY0luCdgmUevU6arZRY/KxYoLI/3V/yxaURR8tLRN7bmZtJdgmzLHI6pQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.12.0", - "@typescript-eslint/visitor-keys": "5.12.0", + "@typescript-eslint/types": "5.16.0", + "@typescript-eslint/visitor-keys": "5.16.0", "debug": "^4.3.2", "globby": "^11.0.4", "is-glob": "^4.0.3", @@ -597,13 +704,59 @@ } } }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.12.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.12.0.tgz", - "integrity": "sha512-cFwTlgnMV6TgezQynx2c/4/tx9Tufbuo9LPzmWqyRC3QC4qTGkAG1C6pBr0/4I10PAI/FlYunI3vJjIcu+ZHMg==", + "node_modules/@typescript-eslint/utils": { + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.16.0.tgz", + "integrity": "sha512-iYej2ER6AwmejLWMWzJIHy3nPJeGDuCqf8Jnb+jAQVoPpmWzwQOfa9hWVB8GIQE5gsCv/rfN4T+AYb/V06WseQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.12.0", + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.16.0", + "@typescript-eslint/types": "5.16.0", + "@typescript-eslint/typescript-estree": "5.16.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.16.0.tgz", + "integrity": "sha512-jqxO8msp5vZDhikTwq9ubyMHqZ67UIvawohr4qF3KhlpL7gzSjOd+8471H3nh5LyABkaI85laEKKU8SnGUK5/g==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.16.0", "eslint-visitor-keys": "^3.0.0" }, "engines": { @@ -1296,12 +1449,12 @@ } }, "node_modules/eslint": { - "version": "8.9.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.9.0.tgz", - "integrity": "sha512-PB09IGwv4F4b0/atrbcMFboF/giawbBLVC7fyDamk5Wtey4Jh2K+rYaBhCAbUyEI4QzB1ly09Uglc9iCtFaG2Q==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.10.0.tgz", + "integrity": "sha512-tcI1D9lfVec+R4LE1mNDnzoJ/f71Kl/9Cv4nG47jOueCMBrCCKYXr4AUVS7go6mWYGFD4+EoN6+eXSrEbRzXVw==", "dev": true, "dependencies": { - "@eslint/eslintrc": "^1.1.0", + "@eslint/eslintrc": "^1.2.0", "@humanwhocodes/config-array": "^0.9.2", "ajv": "^6.10.0", "chalk": "^4.0.0", @@ -1694,6 +1847,11 @@ "node": ">=0.10.0" } }, + "node_modules/exenv": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/exenv/-/exenv-1.2.2.tgz", + "integrity": "sha1-KueOhdmJQVhnCwPUe+wfA72Ru50=" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -2399,18 +2557,6 @@ "node": ">=4" } }, - "node_modules/lodash.castarray": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz", - "integrity": "sha1-wCUTUV4wna3dTCTGDP3c9ZdtkRU=", - "dev": true - }, - "node_modules/lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=", - "dev": true - }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -2441,9 +2587,9 @@ } }, "node_modules/markdown-to-jsx": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/markdown-to-jsx/-/markdown-to-jsx-7.1.6.tgz", - "integrity": "sha512-1wrIGZYwIG2gR3yfRmbr4FlQmhaAKoKTpRo4wur4fp9p0njU1Hi7vR8fj0AUKKIcPduiJmPprzmCB5B/GvlC7g==", + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/markdown-to-jsx/-/markdown-to-jsx-7.1.7.tgz", + "integrity": "sha512-VI3TyyHlGkO8uFle0IOibzpO1c1iJDcXcS/zBrQrXQQvJ2tpdwVzVZ7XdKsyRz1NdRmre4dqQkMZzUHaKIG/1w==", "engines": { "node": ">= 10" }, @@ -2486,9 +2632,9 @@ } }, "node_modules/minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", "dev": true }, "node_modules/ms": { @@ -2563,6 +2709,25 @@ } } }, + "node_modules/next-sitemap": { + "version": "2.5.14", + "resolved": "https://registry.npmjs.org/next-sitemap/-/next-sitemap-2.5.14.tgz", + "integrity": "sha512-aJmxGmoE23NClCi1P6KpHov9DUieF/ZZbfGpTiruOYCq4nKu8Q4masOuswlOl3nNKZa0C3u4JG+TPubjslYH9A==", + "dev": true, + "dependencies": { + "@corex/deepmerge": "^2.6.148", + "minimist": "^1.2.6" + }, + "bin": { + "next-sitemap": "bin/next-sitemap" + }, + "engines": { + "node": ">=14.18" + }, + "peerDependencies": { + "next": "*" + } + }, "node_modules/next/node_modules/postcss": { "version": "8.4.5", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.5.tgz", @@ -2587,6 +2752,14 @@ "dev": true, "peer": true }, + "node_modules/nodemailer": { + "version": "6.7.3", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.7.3.tgz", + "integrity": "sha512-KUdDsspqx89sD4UUyUKzdlUOper3hRkDVkrKh/89G+d9WKsU5ox51NWS4tB1XR5dPUdR4SP0E3molyEfOvSa3g==", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -2977,6 +3150,32 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.6.1.tgz", + "integrity": "sha512-8UVbTBYGwN37Bs9LERmxCPjdvPxlEowx2urIL6urHzdb3SDq4B/Z6xLFCblrSnE4iKWcS6ziJ3aOYrc1kz/E2A==", + "dev": true, + "peer": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-plugin-organize-imports": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-2.3.4.tgz", + "integrity": "sha512-R8o23sf5iVL/U71h9SFUdhdOEPsi3nm42FD/oDYIZ2PQa4TNWWuWecxln6jlIQzpZTDMUeO1NicJP6lLn2TtRw==", + "dev": true, + "peerDependencies": { + "prettier": ">=2.0", + "typescript": ">=2.9" + } + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -3053,11 +3252,47 @@ "react": "17.0.2" } }, + "node_modules/react-image-lightbox": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/react-image-lightbox/-/react-image-lightbox-5.1.4.tgz", + "integrity": "sha512-kTiAODz091bgT7SlWNHab0LSMZAPJtlNWDGKv7pLlLY1krmf7FuG1zxE0wyPpeA8gPdwfr3cu6sPwZRqWsc3Eg==", + "dependencies": { + "prop-types": "^15.7.2", + "react-modal": "^3.11.1" + }, + "peerDependencies": { + "react": "16.x || 17.x", + "react-dom": "16.x || 17.x" + } + }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "node_modules/react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + }, + "node_modules/react-modal": { + "version": "3.14.4", + "resolved": "https://registry.npmjs.org/react-modal/-/react-modal-3.14.4.tgz", + "integrity": "sha512-8surmulejafYCH9wfUmFyj4UfbSJwjcgbS9gf3oOItu4Hwd6ivJyVBETI0yHRhpJKCLZMUtnhzk76wXTsNL6Qg==", + "dependencies": { + "exenv": "^1.2.0", + "prop-types": "^15.7.2", + "react-lifecycles-compat": "^3.0.0", + "warning": "^4.0.3" + }, + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "react": "^0.14.0 || ^15.0.0 || ^16 || ^17", + "react-dom": "^0.14.0 || ^15.0.0 || ^16 || ^17" + } + }, "node_modules/react-swipeable": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/react-swipeable/-/react-swipeable-6.2.0.tgz", @@ -3066,22 +3301,6 @@ "react": "^16.8.3 || ^17" } }, - "node_modules/react-tooltip": { - "version": "4.2.21", - "resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-4.2.21.tgz", - "integrity": "sha512-zSLprMymBDowknr0KVDiJ05IjZn9mQhhg4PRsqln0OZtURAJ1snt1xi5daZfagsh6vfsziZrc9pErPTDY1ACig==", - "dependencies": { - "prop-types": "^15.7.2", - "uuid": "^7.0.3" - }, - "engines": { - "npm": ">=6.13" - }, - "peerDependencies": { - "react": ">=16.0.0", - "react-dom": ">=16.0.0" - } - }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -3445,6 +3664,14 @@ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, + "node_modules/tippy.js": { + "version": "6.3.7", + "resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-6.3.7.tgz", + "integrity": "sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==", + "dependencies": { + "@popperjs/core": "^2.9.0" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -3523,9 +3750,9 @@ } }, "node_modules/typescript": { - "version": "4.5.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz", - "integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==", + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.2.tgz", + "integrity": "sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -3576,20 +3803,20 @@ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true }, - "node_modules/uuid": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz", - "integrity": "sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==", - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/v8-compile-cache": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", "dev": true }, + "node_modules/warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -3765,10 +3992,16 @@ "regenerator-runtime": "^0.13.4" } }, + "@corex/deepmerge": { + "version": "2.6.148", + "resolved": "https://registry.npmjs.org/@corex/deepmerge/-/deepmerge-2.6.148.tgz", + "integrity": "sha512-6QMz0/2h5C3ua51iAnXMPWFbb1QOU1UvSM4bKBw5mzdT+WtLgjbETBBIQZ+Sh9WvEcGwlAt/DEdRpIC3XlDBMA==", + "dev": true + }, "@eslint/eslintrc": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.1.0.tgz", - "integrity": "sha512-C1DfL7XX4nPqGd6jcP01W9pVM1HYCuUkFk1432D7F0v3JSlUIeOYn9oCoi3eoLZ+iwBSb29BMFxxny0YrrEZqg==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.0.tgz", + "integrity": "sha512-igm9SjJHNEJRiUnecP/1R5T3wKLEJ7pL6e2P+GUSfCd0dGjPYYZve08uzw8L2J8foVHFz+NGu12JxRcU2gGo6w==", "dev": true, "requires": { "ajv": "^6.12.4", @@ -3800,6 +4033,11 @@ "resolved": "https://registry.npmjs.org/@fontsource/material-icons-rounded/-/material-icons-rounded-4.5.2.tgz", "integrity": "sha512-wk/vqodMF+4IBbxhI0cjaPBcouvRrnJdeQCslY0Zae8ojyZCUksJn4JTiQk89fbY9kvT3oG7AZIZ+poKdpS02w==" }, + "@fontsource/opendyslexic": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/@fontsource/opendyslexic/-/opendyslexic-4.5.2.tgz", + "integrity": "sha512-vW+A3Bd1ZEG6nAZuix0OhbS0ygMlhvtc3RvLlDXrTAinrAZHQ0bOGUJRN2iaGbQ1kWNP8/7A+AKMFH5FM/pjKA==" + }, "@fontsource/vollkorn": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/@fontsource/vollkorn/-/vollkorn-4.5.4.tgz", @@ -3933,23 +4171,31 @@ "fastq": "^1.6.0" } }, + "@popperjs/core": { + "version": "2.11.3", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.3.tgz", + "integrity": "sha512-8U7hIl7+30XbIrJ0deQMXpXESM1L4yrt6BHok5hzcR0LivivuNkk+tHU1iRVScOwCmQcrOr6kvtIr29MNbQHqQ==" + }, "@rushstack/eslint-patch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.1.0.tgz", "integrity": "sha512-JLo+Y592QzIE+q7Dl2pMUtt4q8SKYI5jDrZxrozEQxnGVOyYE+GWK9eLkwTaeN9DDctlaRAQ3TBmzZ1qdLE30A==", "dev": true }, - "@tailwindcss/typography": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.2.tgz", - "integrity": "sha512-coq8DBABRPFcVhVIk6IbKyyHUt7YTEC/C992tatFB+yEx5WGBQrCgsSFjxHUr8AWXphWckadVJbominEduYBqw==", - "dev": true, + "@tippyjs/react": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/@tippyjs/react/-/react-4.2.6.tgz", + "integrity": "sha512-91RicDR+H7oDSyPycI13q3b7o4O60wa2oRbjlz2fyRLmHImc4vyDwuUP8NtZaN0VARJY5hybvDYrFzhY9+Lbyw==", "requires": { - "lodash.castarray": "^4.4.0", - "lodash.isplainobject": "^4.0.6", - "lodash.merge": "^4.6.2" + "tippy.js": "^6.3.1" } }, + "@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true + }, "@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", @@ -3957,11 +4203,20 @@ "dev": true }, "@types/node": { - "version": "17.0.18", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.18.tgz", - "integrity": "sha512-eKj4f/BsN/qcculZiRSujogjvp5O/k4lOW5m35NopjZM/QwLOR075a8pJW5hD+Rtdm2DaCVPENS6KtSQnUD6BA==", + "version": "17.0.21", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz", + "integrity": "sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==", "dev": true }, + "@types/nodemailer": { + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.4.tgz", + "integrity": "sha512-Ksw4t7iliXeYGvIQcSIgWQ5BLuC/mljIEbjf615svhZL10PE9t+ei8O9gDaD3FPCasUJn9KTLwz2JFJyiiyuqw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", @@ -3975,9 +4230,9 @@ "dev": true }, "@types/react": { - "version": "17.0.39", - "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.39.tgz", - "integrity": "sha512-UVavlfAxDd/AgAacMa60Azl7ygyQNRwC/DsHZmKgNvPmRR5p70AJ5Q9EAmL2NWOJmeV+vVUI4IAP7GZrN8h8Ug==", + "version": "17.0.40", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.40.tgz", + "integrity": "sha512-UrXhD/JyLH+W70nNSufXqMZNuUD2cXHu6UjCllC6pmOQgBX4SGXOH8fjRka0O0Ee0HrFxapDD8Bwn81Kmiz6jQ==", "dev": true, "requires": { "@types/prop-types": "*", @@ -3986,9 +4241,9 @@ } }, "@types/react-dom": { - "version": "17.0.11", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.11.tgz", - "integrity": "sha512-f96K3k+24RaLGVu/Y2Ng3e1EbZ8/cVJvypZWd7cy0ofCBaf2lcM46xNhycMZ2xGwbBjRql7hOlZ+e2WlJ5MH3Q==", + "version": "17.0.13", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.13.tgz", + "integrity": "sha512-wEP+B8hzvy6ORDv1QBhcQia4j6ea4SFIBttHYpXKPFZRviBvknq0FRh3VrIxeXUmsPkwuXVZrVGG7KUVONmXCQ==", "dev": true, "requires": { "@types/react": "*" @@ -4000,42 +4255,76 @@ "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==", "dev": true }, - "@typescript-eslint/parser": { - "version": "5.12.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.12.0.tgz", - "integrity": "sha512-MfSwg9JMBojMUoGjUmX+D2stoQj1CBYTCP0qnnVtu9A+YQXVKNtLjasYh+jozOcrb/wau8TCfWOkQTiOAruBog==", + "@types/turndown": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@types/turndown/-/turndown-5.0.1.tgz", + "integrity": "sha512-N8Ad4e3oJxh9n9BiZx9cbe/0M3kqDpOTm2wzj13wdDUxDPjfjloWIJaquZzWE1cYTAHpjOH3rcTnXQdpEfS/SQ==", + "dev": true + }, + "@typescript-eslint/eslint-plugin": { + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.16.0.tgz", + "integrity": "sha512-SJoba1edXvQRMmNI505Uo4XmGbxCK9ARQpkvOd00anxzri9RNQk0DDCxD+LIl+jYhkzOJiOMMKYEHnHEODjdCw==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.12.0", - "@typescript-eslint/types": "5.12.0", - "@typescript-eslint/typescript-estree": "5.12.0", + "@typescript-eslint/scope-manager": "5.16.0", + "@typescript-eslint/type-utils": "5.16.0", + "@typescript-eslint/utils": "5.16.0", + "debug": "^4.3.2", + "functional-red-black-tree": "^1.0.1", + "ignore": "^5.1.8", + "regexpp": "^3.2.0", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/parser": { + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.16.0.tgz", + "integrity": "sha512-fkDq86F0zl8FicnJtdXakFs4lnuebH6ZADDw6CYQv0UZeIjHvmEw87m9/29nk2Dv5Lmdp0zQ3zDQhiMWQf/GbA==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "5.16.0", + "@typescript-eslint/types": "5.16.0", + "@typescript-eslint/typescript-estree": "5.16.0", "debug": "^4.3.2" } }, "@typescript-eslint/scope-manager": { - "version": "5.12.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.12.0.tgz", - "integrity": "sha512-GAMobtIJI8FGf1sLlUWNUm2IOkIjvn7laFWyRx7CLrv6nLBI7su+B7lbStqVlK5NdLvHRFiJo2HhiDF7Ki01WQ==", + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.16.0.tgz", + "integrity": "sha512-P+Yab2Hovg8NekLIR/mOElCDPyGgFZKhGoZA901Yax6WR6HVeGLbsqJkZ+Cvk5nts/dAlFKm8PfL43UZnWdpIQ==", "dev": true, "requires": { - "@typescript-eslint/types": "5.12.0", - "@typescript-eslint/visitor-keys": "5.12.0" + "@typescript-eslint/types": "5.16.0", + "@typescript-eslint/visitor-keys": "5.16.0" + } + }, + "@typescript-eslint/type-utils": { + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.16.0.tgz", + "integrity": "sha512-SKygICv54CCRl1Vq5ewwQUJV/8padIWvPgCxlWPGO/OgQLCijY9G7lDu6H+mqfQtbzDNlVjzVWQmeqbLMBLEwQ==", + "dev": true, + "requires": { + "@typescript-eslint/utils": "5.16.0", + "debug": "^4.3.2", + "tsutils": "^3.21.0" } }, "@typescript-eslint/types": { - "version": "5.12.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.12.0.tgz", - "integrity": "sha512-JowqbwPf93nvf8fZn5XrPGFBdIK8+yx5UEGs2QFAYFI8IWYfrzz+6zqlurGr2ctShMaJxqwsqmra3WXWjH1nRQ==", + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.16.0.tgz", + "integrity": "sha512-oUorOwLj/3/3p/HFwrp6m/J2VfbLC8gjW5X3awpQJ/bSG+YRGFS4dpsvtQ8T2VNveV+LflQHjlLvB6v0R87z4g==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.12.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.12.0.tgz", - "integrity": "sha512-Dd9gVeOqt38QHR0BEA8oRaT65WYqPYbIc5tRFQPkfLquVEFPD1HAtbZT98TLBkEcCkvwDYOAvuSvAD9DnQhMfQ==", + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.16.0.tgz", + "integrity": "sha512-SE4VfbLWUZl9MR+ngLSARptUv2E8brY0luCdgmUevU6arZRY/KxYoLI/3V/yxaURR8tLRN7bmZtJdgmzLHI6pQ==", "dev": true, "requires": { - "@typescript-eslint/types": "5.12.0", - "@typescript-eslint/visitor-keys": "5.12.0", + "@typescript-eslint/types": "5.16.0", + "@typescript-eslint/visitor-keys": "5.16.0", "debug": "^4.3.2", "globby": "^11.0.4", "is-glob": "^4.0.3", @@ -4043,13 +4332,45 @@ "tsutils": "^3.21.0" } }, - "@typescript-eslint/visitor-keys": { - "version": "5.12.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.12.0.tgz", - "integrity": "sha512-cFwTlgnMV6TgezQynx2c/4/tx9Tufbuo9LPzmWqyRC3QC4qTGkAG1C6pBr0/4I10PAI/FlYunI3vJjIcu+ZHMg==", + "@typescript-eslint/utils": { + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.16.0.tgz", + "integrity": "sha512-iYej2ER6AwmejLWMWzJIHy3nPJeGDuCqf8Jnb+jAQVoPpmWzwQOfa9hWVB8GIQE5gsCv/rfN4T+AYb/V06WseQ==", "dev": true, "requires": { - "@typescript-eslint/types": "5.12.0", + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.16.0", + "@typescript-eslint/types": "5.16.0", + "@typescript-eslint/typescript-estree": "5.16.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + }, + "dependencies": { + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + } + } + }, + "@typescript-eslint/visitor-keys": { + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.16.0.tgz", + "integrity": "sha512-jqxO8msp5vZDhikTwq9ubyMHqZ67UIvawohr4qF3KhlpL7gzSjOd+8471H3nh5LyABkaI85laEKKU8SnGUK5/g==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.16.0", "eslint-visitor-keys": "^3.0.0" } }, @@ -4553,12 +4874,12 @@ "dev": true }, "eslint": { - "version": "8.9.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.9.0.tgz", - "integrity": "sha512-PB09IGwv4F4b0/atrbcMFboF/giawbBLVC7fyDamk5Wtey4Jh2K+rYaBhCAbUyEI4QzB1ly09Uglc9iCtFaG2Q==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.10.0.tgz", + "integrity": "sha512-tcI1D9lfVec+R4LE1mNDnzoJ/f71Kl/9Cv4nG47jOueCMBrCCKYXr4AUVS7go6mWYGFD4+EoN6+eXSrEbRzXVw==", "dev": true, "requires": { - "@eslint/eslintrc": "^1.1.0", + "@eslint/eslintrc": "^1.2.0", "@humanwhocodes/config-array": "^0.9.2", "ajv": "^6.10.0", "chalk": "^4.0.0", @@ -4864,6 +5185,11 @@ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, + "exenv": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/exenv/-/exenv-1.2.2.tgz", + "integrity": "sha1-KueOhdmJQVhnCwPUe+wfA72Ru50=" + }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -5383,18 +5709,6 @@ "path-exists": "^3.0.0" } }, - "lodash.castarray": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz", - "integrity": "sha1-wCUTUV4wna3dTCTGDP3c9ZdtkRU=", - "dev": true - }, - "lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=", - "dev": true - }, "lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -5419,9 +5733,9 @@ } }, "markdown-to-jsx": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/markdown-to-jsx/-/markdown-to-jsx-7.1.6.tgz", - "integrity": "sha512-1wrIGZYwIG2gR3yfRmbr4FlQmhaAKoKTpRo4wur4fp9p0njU1Hi7vR8fj0AUKKIcPduiJmPprzmCB5B/GvlC7g==", + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/markdown-to-jsx/-/markdown-to-jsx-7.1.7.tgz", + "integrity": "sha512-VI3TyyHlGkO8uFle0IOibzpO1c1iJDcXcS/zBrQrXQQvJ2tpdwVzVZ7XdKsyRz1NdRmre4dqQkMZzUHaKIG/1w==", "requires": {} }, "merge2": { @@ -5450,9 +5764,9 @@ } }, "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", "dev": true }, "ms": { @@ -5507,6 +5821,16 @@ } } }, + "next-sitemap": { + "version": "2.5.14", + "resolved": "https://registry.npmjs.org/next-sitemap/-/next-sitemap-2.5.14.tgz", + "integrity": "sha512-aJmxGmoE23NClCi1P6KpHov9DUieF/ZZbfGpTiruOYCq4nKu8Q4masOuswlOl3nNKZa0C3u4JG+TPubjslYH9A==", + "dev": true, + "requires": { + "@corex/deepmerge": "^2.6.148", + "minimist": "^1.2.6" + } + }, "node-releases": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.2.tgz", @@ -5514,6 +5838,11 @@ "dev": true, "peer": true }, + "nodemailer": { + "version": "6.7.3", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.7.3.tgz", + "integrity": "sha512-KUdDsspqx89sD4UUyUKzdlUOper3hRkDVkrKh/89G+d9WKsU5ox51NWS4tB1XR5dPUdR4SP0E3molyEfOvSa3g==" + }, "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -5775,6 +6104,20 @@ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true }, + "prettier": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.6.1.tgz", + "integrity": "sha512-8UVbTBYGwN37Bs9LERmxCPjdvPxlEowx2urIL6urHzdb3SDq4B/Z6xLFCblrSnE4iKWcS6ziJ3aOYrc1kz/E2A==", + "dev": true, + "peer": true + }, + "prettier-plugin-organize-imports": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-2.3.4.tgz", + "integrity": "sha512-R8o23sf5iVL/U71h9SFUdhdOEPsi3nm42FD/oDYIZ2PQa4TNWWuWecxln6jlIQzpZTDMUeO1NicJP6lLn2TtRw==", + "dev": true, + "requires": {} + }, "prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -5822,26 +6165,42 @@ "scheduler": "^0.20.2" } }, + "react-image-lightbox": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/react-image-lightbox/-/react-image-lightbox-5.1.4.tgz", + "integrity": "sha512-kTiAODz091bgT7SlWNHab0LSMZAPJtlNWDGKv7pLlLY1krmf7FuG1zxE0wyPpeA8gPdwfr3cu6sPwZRqWsc3Eg==", + "requires": { + "prop-types": "^15.7.2", + "react-modal": "^3.11.1" + } + }, "react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + }, + "react-modal": { + "version": "3.14.4", + "resolved": "https://registry.npmjs.org/react-modal/-/react-modal-3.14.4.tgz", + "integrity": "sha512-8surmulejafYCH9wfUmFyj4UfbSJwjcgbS9gf3oOItu4Hwd6ivJyVBETI0yHRhpJKCLZMUtnhzk76wXTsNL6Qg==", + "requires": { + "exenv": "^1.2.0", + "prop-types": "^15.7.2", + "react-lifecycles-compat": "^3.0.0", + "warning": "^4.0.3" + } + }, "react-swipeable": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/react-swipeable/-/react-swipeable-6.2.0.tgz", "integrity": "sha512-nWQ8dEM8e/uswZLSIkXUsAnQmnX4MTcryOHBQIQYRMJFDpgDBSiVbKsz/BZVCIScF4NtJh16oyxwaNOepR6xSw==", "requires": {} }, - "react-tooltip": { - "version": "4.2.21", - "resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-4.2.21.tgz", - "integrity": "sha512-zSLprMymBDowknr0KVDiJ05IjZn9mQhhg4PRsqln0OZtURAJ1snt1xi5daZfagsh6vfsziZrc9pErPTDY1ACig==", - "requires": { - "prop-types": "^15.7.2", - "uuid": "^7.0.3" - } - }, "readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -6082,6 +6441,14 @@ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, + "tippy.js": { + "version": "6.3.7", + "resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-6.3.7.tgz", + "integrity": "sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==", + "requires": { + "@popperjs/core": "^2.9.0" + } + }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -6142,9 +6509,9 @@ "dev": true }, "typescript": { - "version": "4.5.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz", - "integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==", + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.2.tgz", + "integrity": "sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg==", "dev": true }, "unbox-primitive": { @@ -6182,17 +6549,20 @@ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true }, - "uuid": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz", - "integrity": "sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==" - }, "v8-compile-cache": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", "dev": true }, + "warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "requires": { + "loose-envify": "^1.0.0" + } + }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index 5044c59..9e0092c 100644 --- a/package.json +++ b/package.json @@ -4,30 +4,39 @@ "scripts": { "dev": "next dev", "build": "next build", + "postbuild": "next-sitemap", "start": "next start", "lint": "next lint" }, "dependencies": { "@fontsource/material-icons": "^4.5.2", "@fontsource/material-icons-rounded": "^4.5.2", + "@fontsource/opendyslexic": "^4.5.2", "@fontsource/vollkorn": "^4.5.4", "@fontsource/zen-maru-gothic": "^4.5.5", - "markdown-to-jsx": "^7.1.6", + "@tippyjs/react": "^4.2.6", + "markdown-to-jsx": "^7.1.7", "next": "^12.1.0", + "nodemailer": "^6.7.3", "react": "17.0.2", "react-dom": "17.0.2", + "react-image-lightbox": "^5.1.4", "react-swipeable": "^6.2.0", - "react-tooltip": "^4.2.21", "turndown": "^7.1.1" }, "devDependencies": { - "@tailwindcss/typography": "^0.5.2", - "@types/node": "17.0.18", - "@types/react": "17.0.39", - "@types/react-dom": "^17.0.11", - "eslint": "8.9.0", + "@types/node": "17.0.21", + "@types/nodemailer": "^6.4.4", + "@types/react": "17.0.40", + "@types/react-dom": "^17.0.13", + "@types/turndown": "^5.0.1", + "@typescript-eslint/eslint-plugin": "^5.16.0", + "@typescript-eslint/parser": "^5.16.0", + "eslint": "^8.10.0", "eslint-config-next": "12.1.0", + "next-sitemap": "^2.5.14", + "prettier-plugin-organize-imports": "^2.3.4", "tailwindcss": "^3.0.23", - "typescript": "4.5.5" + "typescript": "^4.6.2" } } diff --git a/postcss.config.js b/postcss.config.js index c8965cb..a1b36d2 100644 --- a/postcss.config.js +++ b/postcss.config.js @@ -2,5 +2,5 @@ module.exports = { plugins: { tailwindcss: {}, autoprefixer: {}, - } -} \ No newline at end of file + }, +}; diff --git a/public/README.md b/public/README.md index f42f4a5..4995470 100644 --- a/public/README.md +++ b/public/README.md @@ -21,4 +21,4 @@ Insert the following code in the `head` section of your pages: -*Optional* - Check your favicon with the [favicon checker](https://realfavicongenerator.net/favicon_checker) \ No newline at end of file +_Optional_ - Check your favicon with the [favicon checker](https://realfavicongenerator.net/favicon_checker) diff --git a/public/html_code.html b/public/html_code.html index a2b5866..cfcf03b 100644 --- a/public/html_code.html +++ b/public/html_code.html @@ -1,10 +1,10 @@ - - - - - - - - - - \ No newline at end of file + + + + + + + + + + diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 0000000..6b316df --- /dev/null +++ b/public/robots.txt @@ -0,0 +1,9 @@ +# * +User-agent: * +Allow: / + +# Host +Host: https://accords-library.com + +# Sitemaps +Sitemap: https://accords-library.com/sitemap.xml diff --git a/run_accords_prettier.sh b/run_accords_prettier.sh new file mode 100755 index 0000000..b19f459 --- /dev/null +++ b/run_accords_prettier.sh @@ -0,0 +1 @@ +npx prettier --end-of-line auto --write . diff --git a/run_accords_testing.sh b/run_accords_testing.sh index 5b91b7b..16243bf 100755 --- a/run_accords_testing.sh +++ b/run_accords_testing.sh @@ -1,4 +1,2 @@ -NODE_ENV=test -# npx next build | tee ./testing_logs/$(date +"%Y-%m-%d---%H-%M-%S").log - -npx next build 2> >(tee ./testing_logs/$(date +"%Y-%m-%d---%H-%M-%S").stderr.tsv) 1> >(tee ./testing_logs/$(date +"%Y-%m-%d---%H-%M-%S").stdout.tsv) \ No newline at end of file +export ENABLE_TESTING_LOG=true +npx next build 2> >(tee ./testing_logs/$(date +"%Y-%m-%d---%H-%M-%S").stderr.tsv) 1> >(tee ./testing_logs/$(date +"%Y-%m-%d---%H-%M-%S").stdout.tsv) diff --git a/src/components/AppLayout.tsx b/src/components/AppLayout.tsx index 40f5890..a2a68c7 100644 --- a/src/components/AppLayout.tsx +++ b/src/components/AppLayout.tsx @@ -1,34 +1,32 @@ -import { - GetWebsiteInterfaceQuery, - StrapiImage, -} from "graphql/operations-types"; -import MainPanel from "./Panels/MainPanel"; -import Head from "next/head"; -import { useSwipeable } from "react-swipeable"; -import { useRouter } from "next/router"; import Button from "components/Button"; -import { getOgImage, OgImage, prettyLanguage } from "queries/helpers"; -import { useMediaCoarse, useMediaMobile } from "hooks/useMediaQuery"; -import ReactTooltip from "react-tooltip"; import { useAppLayout } from "contexts/AppLayoutContext"; +import { StrapiImage } from "graphql/operations-types"; +import { useMediaMobile } from "hooks/useMediaQuery"; +import Head from "next/head"; +import { useRouter } from "next/router"; +import { AppStaticProps } from "queries/getAppStaticProps"; +import { getOgImage, OgImage, prettyLanguage } from "queries/helpers"; +import { useEffect, useState } from "react"; +import { useSwipeable } from "react-swipeable"; import { ImageQuality } from "./Img"; +import MainPanel from "./Panels/MainPanel"; +import Popup from "./Popup"; +import Select from "./Select"; -type AppLayoutProps = { +interface AppLayoutProps extends AppStaticProps { subPanel?: React.ReactNode; subPanelIcon?: string; contentPanel?: React.ReactNode; - langui: GetWebsiteInterfaceQuery["websiteInterfaces"]["data"][number]["attributes"]; title?: string; navTitle: string; thumbnail?: StrapiImage; description?: string; - extra?: React.ReactNode; -}; +} export default function AppLayout(props: AppLayoutProps): JSX.Element { + const { langui, currencies, languages, subPanel, contentPanel } = props; const router = useRouter(); const isMobile = useMediaMobile(); - const isCoarse = useMediaCoarse(); const appLayout = useAppLayout(); const sensibilitySwipe = 1.1; @@ -38,7 +36,7 @@ export default function AppLayout(props: AppLayoutProps): JSX.Element { if (SwipeEventData.velocity < sensibilitySwipe) return; if (appLayout.mainPanelOpen) { appLayout.setMainPanelOpen(false); - } else if (props.subPanel && props.contentPanel) { + } else if (subPanel && contentPanel) { appLayout.setSubPanelOpen(true); } }, @@ -52,28 +50,7 @@ export default function AppLayout(props: AppLayoutProps): JSX.Element { }, }); - const mainPanelClass = `fixed desktop:left-0 desktop:top-0 desktop:bottom-0 ${ - appLayout.mainPanelReduced ? "desktop:w-[6rem]" : "desktop:w-[20rem]" - }`; - const subPanelClass = `fixed desktop:top-0 desktop:bottom-0 desktop:w-[20rem] ${ - appLayout.mainPanelReduced ? " desktop:left-[6rem]" : "desktop:left-[20rem]" - }`; - let contentPanelClass = ""; - if (props.subPanel) { - contentPanelClass = `fixed desktop:top-0 desktop:bottom-0 desktop:right-0 ${ - appLayout.mainPanelReduced - ? "desktop:left-[26rem]" - : "desktop:left-[40rem]" - }`; - } else if (props.contentPanel) { - contentPanelClass = `fixed desktop:top-0 desktop:bottom-0 desktop:right-0 ${ - appLayout.mainPanelReduced - ? "desktop:left-[6rem]" - : "desktop:left-[20rem]" - }`; - } - - const turnSubIntoContent = props.subPanel && !props.contentPanel; + const turnSubIntoContent = subPanel && !contentPanel; const titlePrefix = "Accord’s Library"; const metaImage: OgImage = props.thumbnail @@ -88,13 +65,58 @@ export default function AppLayout(props: AppLayoutProps): JSX.Element { const metaDescription = props.description ? props.description - : "Accord's Library aims at gathering and archiving all of Yoko Taro’s work. Yoko Taro is a Japanese video game director and scenario writer."; + : langui.default_description; + + useEffect(() => { + document.getElementsByTagName("html")[0].style.fontSize = `${ + (appLayout.fontSize ?? 1) * 100 + }%`; + }, [appLayout.fontSize]); + + const currencyOptions = currencies.map( + (currency) => currency.attributes.code + ); + const [currencySelect, setCurrencySelect] = useState(-1); + + useEffect(() => { + if (appLayout.currency) + setCurrencySelect(currencyOptions.indexOf(appLayout.currency)); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [appLayout.currency]); + + useEffect(() => { + if (currencySelect >= 0) + appLayout.setCurrency(currencyOptions[currencySelect]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [currencySelect]); + + let gridCol = ""; + if (props.subPanel) { + if (appLayout.mainPanelReduced) { + gridCol = "grid-cols-[6rem_20rem_1fr]"; + } else { + gridCol = "grid-cols-[20rem_20rem_1fr]"; + } + } else if (appLayout.mainPanelReduced) { + gridCol = "grid-cols-[6rem_0px_1fr]"; + } else { + gridCol = "grid-cols-[20rem_0px_1fr]"; + } return ( -
+
{`${titlePrefix} - ${ogTitle}`} @@ -104,12 +126,8 @@ export default function AppLayout(props: AppLayoutProps): JSX.Element { content={`${titlePrefix} - ${ogTitle}`} > - {props.description && ( - <> - - - - )} + + @@ -128,145 +146,247 @@ export default function AppLayout(props: AppLayoutProps): JSX.Element { + {/* Background when navbar is opened */} +
+
{ + appLayout.setMainPanelOpen(false); + appLayout.setSubPanelOpen(false); + }} + >
+
+ + {/* Content panel */} +
+ {contentPanel ? ( + contentPanel + ) : ( +
+
+

+

{langui.select_option_sidebar}

+
+
+ )} +
+ + {/* Sub panel */} + {subPanel && ( +
+ {subPanel} +
+ )} + + {/* Main panel */} +
+ +
+ {/* Navbar */} -
+
appLayout.setMainPanelOpen(true)} + onClick={() => { + appLayout.setMainPanelOpen(!appLayout.mainPanelOpen); + appLayout.setSubPanelOpen(false); + }} > - menu + {appLayout.mainPanelOpen ? "close" : "menu"}

{props.navTitle}

appLayout.setSubPanelOpen(true)} + onClick={() => { + appLayout.setSubPanelOpen(!appLayout.subPanelOpen); + appLayout.setMainPanelOpen(false); + }} > - {props.subPanel && !turnSubIntoContent - ? props.subPanelIcon + {subPanel && !turnSubIntoContent + ? appLayout.subPanelOpen + ? "close" + : props.subPanelIcon ? props.subPanelIcon : "tune" : ""}
- {/* Content panel */} -
- {props.contentPanel ? ( - props.contentPanel - ) : ( -
-
-

-

- Select one of the options in the sidebar -

+

{langui.select_language}

+
+ {router.locales?.map((locale) => ( + + ))} +
+ + + +

{langui.settings}

+ +
+
+

{langui.theme}

+
+ + +
- )} -
- {/* Background when navbar is opened */} -
{ - appLayout.setMainPanelOpen(false); - appLayout.setSubPanelOpen(false); - }} - >
+
+

{langui.currency}

+
+ + appLayout.setPlayerName( + (event.target as HTMLInputElement).value + ) + } + />
-
- - +
- - {props.extra}
); } diff --git a/src/components/Button.tsx b/src/components/Button.tsx index 1106928..8da4382 100644 --- a/src/components/Button.tsx +++ b/src/components/Button.tsx @@ -1,17 +1,19 @@ -import Link from "next/link"; +import { useRouter } from "next/router"; import { MouseEventHandler } from "react"; type ButtonProps = { id?: string; className?: string; href?: string; - children: React.ReactChild | React.ReactChild[]; + children: React.ReactNode; active?: boolean; locale?: string; onClick?: MouseEventHandler; }; export default function Button(props: ButtonProps): JSX.Element { + const router = useRouter(); + const button = (
{props.children}
); - const result = props.href ? ( - + return ( +
{ + if (props.href || props.locale) + router.push(props.href ?? router.asPath, props.href, { + locale: props.locale, + }); + }} + > {button} - - ) : ( - button +
); - return result; } diff --git a/src/components/Chip.tsx b/src/components/Chip.tsx index e490ddc..282f566 100644 --- a/src/components/Chip.tsx +++ b/src/components/Chip.tsx @@ -1,25 +1,12 @@ type ChipProps = { className?: string; - children: React.ReactChild | React.ReactChild[]; - "data-tip"?: string; - "data-for"?: string; - "data-html"?: boolean; - "data-multiline"?: boolean; + children: React.ReactNode; }; export default function Chip(props: ChipProps): JSX.Element { return (
{props.children}
diff --git a/src/components/Chronology/ChronologyItemComponent.tsx b/src/components/Chronology/ChronologyItemComponent.tsx index 090fb92..a2b1e0f 100644 --- a/src/components/Chronology/ChronologyItemComponent.tsx +++ b/src/components/Chronology/ChronologyItemComponent.tsx @@ -1,35 +1,40 @@ import Chip from "components/Chip"; +import ToolTip from "components/ToolTip"; import { Enum_Componenttranslationschronologyitem_Status, GetChronologyItemsQuery, + GetWebsiteInterfaceQuery, } from "graphql/operations-types"; +import { getStatusDescription } from "queries/helpers"; export type ChronologyItemComponentProps = { item: GetChronologyItemsQuery["chronologyItems"]["data"][number]; displayYear: boolean; + langui: GetWebsiteInterfaceQuery["websiteInterfaces"]["data"][number]["attributes"]; }; export default function ChronologyItemComponent( props: ChronologyItemComponentProps ): JSX.Element { + const { langui } = props; + function generateAnchor(year: number, month: number, day: number): string { - let result: string = ""; + let result = ""; result += year; - if (month) result += "-" + month.toString().padStart(2, "0"); - if (day) result += "-" + day.toString().padStart(2, "0"); + if (month) result += `- ${month.toString().padStart(2, "0")}`; + if (day) result += `- ${day.toString().padStart(2, "0")}`; return result; } function generateYear(displayed_date: string, year: number): string { if (displayed_date) { return displayed_date; - } else { - return year.toString(); } + return year.toString(); } function generateDate(month: number, day: number): string { - let lut = [ + const lut = [ "Jan", "Feb", "Mar", @@ -44,11 +49,11 @@ export default function ChronologyItemComponent( "Dec", ]; - let result: string = ""; + let result = ""; if (month) { result += lut[month - 1]; if (day) { - result += " " + day; + result += ` ${day}`; } } @@ -64,15 +69,13 @@ export default function ChronologyItemComponent( props.item.attributes.day )} > - {props.displayYear ? ( + {props.displayYear && (

{generateYear( props.item.attributes.displayed_date, props.item.attributes.year )}

- ) : ( - "" )}

@@ -87,28 +90,17 @@ export default function ChronologyItemComponent(

{translation.status !== Enum_Componenttranslationschronologyitem_Status.Done && ( - - {translation.status} - + {translation.status} + )} {translation.title ?

{translation.title}

: ""}
- {translation.description ? ( + {translation.description && (

1 @@ -118,11 +110,9 @@ export default function ChronologyItemComponent( > {translation.description}

- ) : ( - "" )} {translation.note ? ( - {"Notes: " + translation.note} + {`Notes: ${translation.note}`} ) : ( "" )} @@ -131,7 +121,7 @@ export default function ChronologyItemComponent(

{event.source.data ? ( - "(" + event.source.data.attributes.name + ")" + `(${event.source.data.attributes.name})` ) : ( <> warningNo diff --git a/src/components/Chronology/ChronologyYearComponent.tsx b/src/components/Chronology/ChronologyYearComponent.tsx index 8136ac0..d4cfbd0 100644 --- a/src/components/Chronology/ChronologyYearComponent.tsx +++ b/src/components/Chronology/ChronologyYearComponent.tsx @@ -1,14 +1,20 @@ import ChronologyItemComponent from "components/Chronology/ChronologyItemComponent"; -import { GetChronologyItemsQuery } from "graphql/operations-types"; +import { + GetChronologyItemsQuery, + GetWebsiteInterfaceQuery, +} from "graphql/operations-types"; type ChronologyYearComponentProps = { year: number; items: GetChronologyItemsQuery["chronologyItems"]["data"][number][]; + langui: GetWebsiteInterfaceQuery["websiteInterfaces"]["data"][number]["attributes"]; }; export default function ChronologyYearComponent( props: ChronologyYearComponentProps ): JSX.Element { + const { langui } = props; + return (

))}
diff --git a/src/components/Content/ThumbnailHeader.tsx b/src/components/Content/ThumbnailHeader.tsx index 13f1c3a..23bba89 100644 --- a/src/components/Content/ThumbnailHeader.tsx +++ b/src/components/Content/ThumbnailHeader.tsx @@ -1,85 +1,90 @@ +import Chip from "components/Chip"; +import Img, { ImageQuality } from "components/Img"; +import InsetBox from "components/InsetBox"; import { GetContentQuery, GetWebsiteInterfaceQuery, } from "graphql/operations-types"; -import { prettySlug } from "queries/helpers"; -import Button from "components/Button"; -import Img, { ImageQuality } from "components/Img"; -import InsetBox from "components/InsetBox"; +import { prettyinlineTitle, prettySlug, slugify } from "queries/helpers"; export type ThumbnailHeaderProps = { - content: { - slug: GetContentQuery["contents"]["data"][number]["attributes"]["slug"]; - thumbnail: GetContentQuery["contents"]["data"][number]["attributes"]["thumbnail"]; - titles: GetContentQuery["contents"]["data"][number]["attributes"]["titles"]; - type: GetContentQuery["contents"]["data"][number]["attributes"]["type"]; - categories: GetContentQuery["contents"]["data"][number]["attributes"]["categories"]; - }; + pre_title?: string; + title: string; + subtitle?: string; + description?: string; + type?: GetContentQuery["contents"]["data"][number]["attributes"]["type"]; + categories?: GetContentQuery["contents"]["data"][number]["attributes"]["categories"]; + thumbnail?: GetContentQuery["contents"]["data"][number]["attributes"]["thumbnail"]["data"]["attributes"]; langui: GetWebsiteInterfaceQuery["websiteInterfaces"]["data"][number]["attributes"]; }; export default function ThumbnailHeader( props: ThumbnailHeaderProps ): JSX.Element { - const content = props.content; - const langui = props.langui; + const { + langui, + pre_title, + title, + subtitle, + thumbnail, + type, + categories, + description, + } = props; return ( <>
- {content.thumbnail.data ? ( + {thumbnail ? ( ) : ( -
+
)}
-
- {content.titles.length > 0 ? ( - <> -

{content.titles[0].pre_title}

-

{content.titles[0].title}

-

{content.titles[0].subtitle}

- - ) : ( -

{prettySlug(content.slug)}

+
+

{pre_title}

+

{title}

+

{subtitle}

- {content.type ? ( -
-

{langui.global_type}

- + {type?.data && ( +
+

{langui.type}

+
+ + {type.data.attributes.titles.length > 0 + ? type.data.attributes.titles[0].title + : prettySlug(type.data.attributes.slug)} + +
- ) : ( - "" )} - {content.categories.data.length > 0 ? ( -
-

{langui.global_categories}

- {content.categories.data.map((category) => ( - - ))} + {categories && categories.data.length > 0 && ( +
+

{langui.categories}

+
+ {categories.data.map((category) => ( + {category.attributes.name} + ))} +
- ) : ( - "" )}
- {content.titles.length > 0 && content.titles[0].description && ( - {content.titles[0].description} - )} + {description && {description}} ); } diff --git a/src/components/Img.tsx b/src/components/Img.tsx index 8723bbb..c3bec91 100644 --- a/src/components/Img.tsx +++ b/src/components/Img.tsx @@ -1,6 +1,5 @@ import { StrapiImage } from "graphql/operations-types"; -import { ImageProps } from "next/image"; -import Image from "next/image"; +import Image, { ImageProps } from "next/image"; export enum ImageQuality { Small = "small", @@ -10,11 +9,12 @@ export enum ImageQuality { } export function getAssetURL(url: string, quality: ImageQuality): string { - url = url.replace(/^\/uploads/, "/" + quality); - url = url.replace(/.jpg$/, ".webp"); - url = url.replace(/.png$/, ".webp"); - if (quality === ImageQuality.Og) url = url.replace(/.webp$/, ".jpg"); - return process.env.NEXT_PUBLIC_URL_IMG + url; + let newUrl = url; + newUrl = newUrl.replace(/^\/uploads/u, `/${quality}`); + newUrl = newUrl.replace(/.jpg$/u, ".webp"); + newUrl = newUrl.replace(/.png$/u, ".webp"); + if (quality === ImageQuality.Og) newUrl = newUrl.replace(/.webp$/u, ".jpg"); + return process.env.NEXT_PUBLIC_URL_IMG + newUrl; } export function getImgSizesByMaxSize( @@ -25,10 +25,9 @@ export function getImgSizesByMaxSize( if (width > height) { if (width < maxSize) return { width: width, height: height }; return { width: maxSize, height: (height / width) * maxSize }; - } else { - if (height < maxSize) return { width: width, height: height }; - return { width: (width / height) * maxSize, height: maxSize }; } + if (height < maxSize) return { width: width, height: height }; + return { width: (width / height) * maxSize, height: maxSize }; } export function getImgSizesByQuality( @@ -45,12 +44,14 @@ export function getImgSizesByQuality( return getImgSizesByMaxSize(width, height, 1024); case ImageQuality.Large: return getImgSizesByMaxSize(width, height, 2048); + default: + return { width: 0, height: 0 }; } } type ImgProps = { className?: string; - image: StrapiImage; + image?: StrapiImage; quality?: ImageQuality; alt?: ImageProps["alt"]; layout?: ImageProps["layout"]; @@ -60,27 +61,28 @@ type ImgProps = { }; export default function Img(props: ImgProps): JSX.Element { - const imgSize = getImgSizesByQuality( - props.image.width, - props.image.height, - props.quality ? props.quality : ImageQuality.Small - ); - - if (props.rawImg) { - return ( - // eslint-disable-next-line @next/next/no-img-element - {props.alt + if (props.image) { + const imgSize = getImgSizesByQuality( + props.image.width, + props.image.height, + props.quality ? props.quality : ImageQuality.Small ); - } else { + + if (props.rawImg) { + return ( + // eslint-disable-next-line @next/next/no-img-element + {props.alt + ); + } return ( ); } + return <>; } diff --git a/src/components/InsetBox.tsx b/src/components/InsetBox.tsx index 83de892..c9f45ea 100644 --- a/src/components/InsetBox.tsx +++ b/src/components/InsetBox.tsx @@ -1,6 +1,6 @@ type InsetBoxProps = { className?: string; - children: React.ReactChild | React.ReactChild[]; + children: React.ReactNode; id?: string; }; diff --git a/src/components/LanguageSwitcher.tsx b/src/components/LanguageSwitcher.tsx new file mode 100644 index 0000000..af45511 --- /dev/null +++ b/src/components/LanguageSwitcher.tsx @@ -0,0 +1,42 @@ +import { + GetLanguagesQuery, + GetWebsiteInterfaceQuery, +} from "graphql/operations-types"; +import { useRouter } from "next/router"; +import { prettyLanguage } from "queries/helpers"; +import Button from "./Button"; + +type HorizontalLineProps = { + className?: string; + locales: string[]; + languages: GetLanguagesQuery["languages"]["data"]; + langui: GetWebsiteInterfaceQuery["websiteInterfaces"]["data"][number]["attributes"]; + href?: string; +}; + +export default function HorizontalLine( + props: HorizontalLineProps +): JSX.Element { + const { locales, langui, href } = props; + const router = useRouter(); + + return ( +
+
+

{langui.language_switch_message}

+
+ {locales.map((locale, index) => ( + + ))} +
+
+
+ ); +} diff --git a/src/components/Library/ContentTOCLine.tsx b/src/components/Library/ContentTOCLine.tsx new file mode 100644 index 0000000..06de4d4 --- /dev/null +++ b/src/components/Library/ContentTOCLine.tsx @@ -0,0 +1,101 @@ +import Button from "components/Button"; +import Chip from "components/Chip"; +import { + GetLibraryItemQuery, + GetWebsiteInterfaceQuery, +} from "graphql/operations-types"; +import { prettyinlineTitle, prettySlug } from "queries/helpers"; +import { useState } from "react"; + +type ContentTOCLineProps = { + content: GetLibraryItemQuery["libraryItems"]["data"][number]["attributes"]["contents"]["data"][number]; + parentSlug: string; + langui: GetWebsiteInterfaceQuery["websiteInterfaces"]["data"][number]["attributes"]; +}; + +export default function ContentTOCLine( + props: ContentTOCLineProps +): JSX.Element { + const { content, langui, parentSlug } = props; + + const [opened, setOpened] = useState(false); + + return ( +
+
+ +

setOpened(!opened)}> + {content.attributes.content.data && + content.attributes.content.data.attributes.titles.length > 0 + ? prettyinlineTitle( + content.attributes.content.data.attributes.titles[0] + .pre_title, + content.attributes.content.data.attributes.titles[0].title, + content.attributes.content.data.attributes.titles[0].subtitle + ) + : prettySlug(content.attributes.slug, props.parentSlug)} +

+
+
+ {content.attributes.content.data?.attributes.categories.data.map( + (category) => ( + {category.attributes.short} + ) + )} +
+

+

+ {content.attributes.range[0].__typename === "ComponentRangePageRange" + ? content.attributes.range[0].starting_page + : ""} +

+ {content.attributes.content.data && ( + + {content.attributes.content.data.attributes.type.data.attributes + .titles.length > 0 + ? content.attributes.content.data.attributes.type.data.attributes + .titles[0].title + : prettySlug( + content.attributes.content.data.attributes.type.data + .attributes.slug + )} + + )} +
+
+ + subdirectory_arrow_right + + + {content.attributes.scan_set.length > 0 && ( + + )} + + {content.attributes.content.data && ( + + )} + + {content.attributes.scan_set.length === 0 && + !content.attributes.content.data + ? "The content is not available" + : ""} +
+
+ ); +} diff --git a/src/components/Library/LibraryContentPreview.tsx b/src/components/Library/LibraryContentPreview.tsx index bcebbce..89c0c82 100644 --- a/src/components/Library/LibraryContentPreview.tsx +++ b/src/components/Library/LibraryContentPreview.tsx @@ -1,8 +1,8 @@ -import Link from "next/link"; -import { GetContentsQuery } from "graphql/operations-types"; -import { prettySlug } from "queries/helpers"; import Chip from "components/Chip"; import Img, { ImageQuality } from "components/Img"; +import { GetContentsQuery } from "graphql/operations-types"; +import Link from "next/link"; +import { prettySlug } from "queries/helpers"; export type LibraryContentPreviewProps = { item: { @@ -17,10 +17,10 @@ export type LibraryContentPreviewProps = { export default function LibraryContentPreview( props: LibraryContentPreviewProps ): JSX.Element { - const item = props.item; + const { item } = props; return ( - +
{item.thumbnail.data ? (
- {item.type.data ? ( + {item.type.data && ( {item.type.data.attributes.titles.length > 0 ? item.type.data.attributes.titles[0].title : prettySlug(item.type.data.attributes.slug)} - ) : ( - "" )}
diff --git a/src/components/Library/LibraryItemsPreview.tsx b/src/components/Library/LibraryItemsPreview.tsx index d4f97ef..f667268 100644 --- a/src/components/Library/LibraryItemsPreview.tsx +++ b/src/components/Library/LibraryItemsPreview.tsx @@ -1,8 +1,12 @@ -import Link from "next/link"; -import { GetLibraryItemsPreviewQuery } from "graphql/operations-types"; -import { prettyDate, prettyPrice, prettyItemSubType } from "queries/helpers"; import Chip from "components/Chip"; import Img, { ImageQuality } from "components/Img"; +import { useAppLayout } from "contexts/AppLayoutContext"; +import { + GetCurrenciesQuery, + GetLibraryItemsPreviewQuery, +} from "graphql/operations-types"; +import Link from "next/link"; +import { prettyDate, prettyItemSubType, prettyPrice } from "queries/helpers"; export type LibraryItemsPreviewProps = { className?: string; @@ -12,68 +16,76 @@ export type LibraryItemsPreviewProps = { title: GetLibraryItemsPreviewQuery["libraryItems"]["data"][number]["attributes"]["title"]; subtitle: GetLibraryItemsPreviewQuery["libraryItems"]["data"][number]["attributes"]["subtitle"]; price?: GetLibraryItemsPreviewQuery["libraryItems"]["data"][number]["attributes"]["price"]; + categories: GetLibraryItemsPreviewQuery["libraryItems"]["data"][number]["attributes"]["categories"]; release_date?: GetLibraryItemsPreviewQuery["libraryItems"]["data"][number]["attributes"]["release_date"]; metadata?: GetLibraryItemsPreviewQuery["libraryItems"]["data"][number]["attributes"]["metadata"]; }; + currencies?: GetCurrenciesQuery["currencies"]["data"]; }; export default function LibraryItemsPreview( props: LibraryItemsPreviewProps ): JSX.Element { - const item = props.item; + const { item } = props; + const appLayout = useAppLayout(); return ( - +
{item.thumbnail.data ? ( ) : (
)}
- {item.metadata && item.metadata.length > 0 ? ( + {item.metadata && item.metadata.length > 0 && (
{prettyItemSubType(item.metadata[0])}
- ) : ( - "" )}

{item.title}

{item.subtitle}

- {item.release_date || item.price ? ( + +
+ {item.categories.data.map((category) => ( + + {category.attributes.short} + + ))} +
+ + {(item.release_date || item.price) && (
- {item.release_date ? ( + {item.release_date && (

event {prettyDate(item.release_date)}

- ) : ( - "" )} - {item.price ? ( + {item.price && props.currencies && (

shopping_cart - {prettyPrice(item.price)} + {prettyPrice( + item.price, + props.currencies, + appLayout.currency + )}

- ) : ( - "" )}
- ) : ( - "" )}
diff --git a/src/components/LightBox.tsx b/src/components/LightBox.tsx new file mode 100644 index 0000000..41754bc --- /dev/null +++ b/src/components/LightBox.tsx @@ -0,0 +1,39 @@ +import { useMediaMobile } from "hooks/useMediaQuery"; +import { Dispatch, SetStateAction } from "react"; +import Lightbox from "react-image-lightbox"; + +export type LightBoxProps = { + setState: + | Dispatch> + | Dispatch>; + state: boolean; + images: string[]; + index: number; + setIndex: Dispatch>; +}; + +export default function LightBox(props: LightBoxProps): JSX.Element { + const { state, setState, images, index, setIndex } = props; + const mobile = useMediaMobile(); + + return ( + <> + {state && ( + document.getElementById("MyAppLayout"), + }} + mainSrc={images[index]} + prevSrc={index > 0 ? images[index - 1] : undefined} + nextSrc={index < images.length ? images[index + 1] : undefined} + onMovePrevRequest={() => setIndex(index - 1)} + onMoveNextRequest={() => setIndex(index + 1)} + imageCaption="" + imageTitle="" + onCloseRequest={() => setState(false)} + imagePadding={mobile ? 0 : 70} + /> + )} + + ); +} diff --git a/src/components/Markdown/Markdawn.tsx b/src/components/Markdown/Markdawn.tsx index 8212ad4..3e7d108 100644 --- a/src/components/Markdown/Markdawn.tsx +++ b/src/components/Markdown/Markdawn.tsx @@ -1,30 +1,388 @@ +import HorizontalLine from "components/HorizontalLine"; +import Img, { getAssetURL, ImageQuality } from "components/Img"; +import InsetBox from "components/InsetBox"; +import LightBox from "components/LightBox"; +import ToolTip from "components/ToolTip"; +import { useAppLayout } from "contexts/AppLayoutContext"; import Markdown from "markdown-to-jsx"; -import SceneBreak from "./SceneBreak"; +import { useRouter } from "next/router"; +import { slugify } from "queries/helpers"; +import React, { useState } from "react"; +import ReactDOMServer from "react-dom/server"; -type ScenBreakProps = { +type MarkdawnProps = { className?: string; text: string; }; -export default function Markdawn(props: ScenBreakProps): JSX.Element { - if (props.text) { +export default function Markdawn(props: MarkdawnProps): JSX.Element { + const appLayout = useAppLayout(); + const text = preprocessMarkDawn(props.text); + + const router = useRouter(); + + const [lightboxOpen, setLightboxOpen] = useState(false); + const [lightboxImages, setLightboxImages] = useState([""]); + const [lightboxIndex, setLightboxIndex] = useState(0); + + if (text) { return ( - + + { + if (compProps.href.startsWith("/")) { + return ( + router.push(compProps.href)}> + {compProps.children} + + ); + } + return {compProps.children}; + }, + }, + h1: { + component: (compProps: { + id: string; + style: React.CSSProperties; + children: React.ReactNode; + }) => ( +

+ {compProps.children} + +

+ ), + }, + h2: { + component: (compProps: { + id: string; + style: React.CSSProperties; + children: React.ReactNode; + }) => ( +

+ {compProps.children} + +

+ ), + }, + h3: { + component: (compProps: { + id: string; + style: React.CSSProperties; + children: React.ReactNode; + }) => ( +

+ {compProps.children} + +

+ ), + }, + h4: { + component: (compProps: { + id: string; + style: React.CSSProperties; + children: React.ReactNode; + }) => ( +

+ {compProps.children} + +

+ ), + }, + h5: { + component: (compProps: { + id: string; + style: React.CSSProperties; + children: React.ReactNode; + }) => ( +
+ {compProps.children} + +
+ ), + }, + h6: { + component: (compProps: { + id: string; + style: React.CSSProperties; + children: React.ReactNode; + }) => ( +
+ {compProps.children} + +
+ ), + }, + Sep: { + component: () =>
, + }, + SceneBreak: { + component: (compProps: { id: string }) => ( +
+ * * * +
+ ), + }, + IntraLink: { + component: (compProps: { + children: React.ReactNode; + target?: string; + page?: string; + }) => { + const slug = compProps.target + ? slugify(compProps.target) + : slugify(compProps.children?.toString()); + return ( + + router.replace( + `${compProps.page ? compProps.page : ""}#${slug}` + ) + } + > + {compProps.children} + + ); + }, + }, + player: { + component: () => ( + + {appLayout.playerName ? appLayout.playerName : ""} + + ), + }, + Transcript: { + component: (compProps) => ( +
+ {compProps.children} +
+ ), + }, + Line: { + component: (compProps) => ( + <> + + {compProps.name} + +

{compProps.children}

+ + ), + }, + InsetBox: { + component: (compProps) => ( + {compProps.children} + ), + }, + li: { + component: (compProps: { children: React.ReactNode }) => ( +
  • {compProps.children} + ).length > 100 + ? "my-4" + : "" + } + > + {compProps.children} +
  • + ), + }, + Highlight: { + component: (compProps: { children: React.ReactNode }) => ( + {compProps.children} + ), + }, + footer: { + component: (compProps: { children: React.ReactNode }) => ( + <> + +
    {compProps.children}
    + + ), + }, + blockquote: { + component: (compProps: { + children: React.ReactNode; + cite?: string; + }) => ( +
    + {compProps.cite ? ( + <> + “{compProps.children}” + — {compProps.cite} + + ) : ( + compProps.children + )} +
    + ), + }, + img: { + component: (compProps: { + alt: string; + src: string; + width?: number; + height?: number; + caption?: string; + name?: string; + }) => ( +
    { + setLightboxOpen(true); + setLightboxImages([ + compProps.src.startsWith("/uploads/") + ? getAssetURL(compProps.src, ImageQuality.Large) + : compProps.src, + ]); + setLightboxIndex(0); + }} + > + {compProps.src.startsWith("/uploads/") ? ( +
    + +
    + ) : ( +
    + {/* eslint-disable-next-line jsx-a11y/alt-text */} + +
    + )} +
    + ), + }, }, - player: { - component: () => {return {""}} - }, - }, - }} - > - {props.text} -
    + }} + > + {text} +
    + ); } return <>; -} \ No newline at end of file +} + +function HeaderToolTip(props: { id: string }) { + return ( + + + { + navigator.clipboard.writeText( + `${process.env.NEXT_PUBLIC_URL_SELF + window.location.pathname}#${ + props.id + }` + ); + }} + > + link + + + + ); +} + +export function preprocessMarkDawn(text: string): string { + if (!text) return ""; + + let scenebreakIndex = 0; + const visitedSlugs: string[] = []; + + const result = text.split("\n").map((line) => { + if (line === "* * *" || line === "---") { + scenebreakIndex += 1; + return ``; + } + + if (line.startsWith("# ")) { + return markdawnHeadersParser(headerLevels.h1, line, visitedSlugs); + } + + if (line.startsWith("## ")) { + return markdawnHeadersParser(headerLevels.h2, line, visitedSlugs); + } + + if (line.startsWith("### ")) { + return markdawnHeadersParser(headerLevels.h3, line, visitedSlugs); + } + + if (line.startsWith("#### ")) { + return markdawnHeadersParser(headerLevels.h4, line, visitedSlugs); + } + + if (line.startsWith("##### ")) { + return markdawnHeadersParser(headerLevels.h5, line, visitedSlugs); + } + + if (line.startsWith("###### ")) { + return markdawnHeadersParser(headerLevels.h6, line, visitedSlugs); + } + + return line; + }); + return result.join("\n"); +} + +enum headerLevels { + h1 = 1, + h2 = 2, + h3 = 3, + h4 = 4, + h5 = 5, + h6 = 6, +} + +function markdawnHeadersParser( + headerLevel: headerLevels, + line: string, + visitedSlugs: string[] +): string { + const lineText = line.slice(headerLevel + 1); + const slug = slugify(lineText); + let newSlug = slug; + let index = 2; + while (visitedSlugs.includes(newSlug)) { + newSlug = `${slug}-${index}`; + index += 1; + } + visitedSlugs.push(newSlug); + return `<${headerLevels[headerLevel]} id="${newSlug}">${lineText}`; +} diff --git a/src/components/Markdown/SceneBreak.tsx b/src/components/Markdown/SceneBreak.tsx deleted file mode 100644 index a636120..0000000 --- a/src/components/Markdown/SceneBreak.tsx +++ /dev/null @@ -1,15 +0,0 @@ -type ScenBreakProps = { - className?: string; -}; - -export default function SceneBreak(props: ScenBreakProps): JSX.Element { - return ( -
    - * * * -
    - ); -} diff --git a/src/components/Markdown/TOC.tsx b/src/components/Markdown/TOC.tsx new file mode 100644 index 0000000..430c358 --- /dev/null +++ b/src/components/Markdown/TOC.tsx @@ -0,0 +1,165 @@ +import { useRouter } from "next/router"; +import { slugify } from "queries/helpers"; +import { preprocessMarkDawn } from "./Markdawn"; + +type TOCProps = { + text: string; + title?: string; +}; + +export default function TOCComponent(props: TOCProps): JSX.Element { + const { text, title } = props; + const toc = getTocFromMarkdawn(preprocessMarkDawn(text), title); + const router = useRouter(); + + return ( + <> +

    Table of content

    + + + ); +} + +type TOCLevelProps = { + tocchildren: TOC[]; + parentNumbering: string; +}; + +function TOCLevel(props: TOCLevelProps): JSX.Element { + const router = useRouter(); + const { tocchildren, parentNumbering } = props; + return ( +
      + {tocchildren.map((child, childIndex) => ( + <> +
    1. + {`${parentNumbering}${ + childIndex + 1 + }.`}{" "} + router.replace(`#${child.slug}`)}> + {{child.title}} + +
    2. + + + ))} +
    + ); +} + +export type TOC = { + title: string; + slug: string; + children: TOC[]; +}; + +export function getTocFromMarkdawn(text: string, title?: string): TOC { + const toc: TOC = { + title: title ?? "Return to top", + slug: slugify(title) ?? "", + children: [], + }; + let h2 = -1; + let h3 = -1; + let h4 = -1; + let h5 = -1; + let scenebreak = 0; + let scenebreakIndex = 0; + + function getTitle(line: string): string { + return line.slice(line.indexOf(`">`) + 2, line.indexOf("`)); + } + + text.split("\n").map((line) => { + if (line.startsWith("

    +
    + {post.thumbnail.data ? ( + + ) : ( +
    + )} +
    +
    + {post.date && ( +

    + + event + + {prettyDate(post.date)} +

    + )} +
    +
    + {post.translations.length > 0 ? ( + <> +

    {post.translations[0].title}

    +

    {post.translations[0].excerpt}

    + + ) : ( +

    {prettySlug(post.slug)}

    + )} +
    +
    + {post.categories.data.map((category) => ( + + {category.attributes.short} + + ))} +
    +
    +
    + + ); +} diff --git a/src/components/PanelComponents/NavOption.tsx b/src/components/PanelComponents/NavOption.tsx index be793d4..c640aa9 100644 --- a/src/components/PanelComponents/NavOption.tsx +++ b/src/components/PanelComponents/NavOption.tsx @@ -1,14 +1,12 @@ +import ToolTip from "components/ToolTip"; import { useRouter } from "next/router"; -import Link from "next/link"; import { MouseEventHandler } from "react"; -import ReactDOMServer from "react-dom/server"; type NavOptionProps = { url: string; icon?: string; title: string; subtitle?: string; - tooltipId?: string; border?: boolean; reduced?: boolean; onClick?: MouseEventHandler; @@ -25,21 +23,29 @@ export default function NavOption(props: NavOptionProps): JSX.Element { } ${isActive ? divActive : ""}`; return ( - + +

    {props.title}

    + {props.subtitle &&

    {props.subtitle}

    } +

    + } + placement="right" + className="text-left" + disabled={!props.reduced} + >
    -

    {props.title}

    - {props.subtitle && ( -

    {props.subtitle}

    - )} -
    - )} - data-for={props.tooltipId} - className={`grid grid-flow-col grid-cols-[auto] auto-cols-fr justify-center ${ + onClick={(event) => { + if (props.onClick) props.onClick(event); + if (props.url) { + if (props.url.startsWith("#")) { + router.replace(props.url); + } else { + router.push(props.url); + } + } + }} + className={`relative grid grid-flow-col grid-cols-[auto] auto-cols-fr justify-center ${ props.icon ? "text-left" : "text-center" } ${divCommon}`} > @@ -54,6 +60,6 @@ export default function NavOption(props: NavOptionProps): JSX.Element {
    )}
    - + ); } diff --git a/src/components/PanelComponents/PanelHeader.tsx b/src/components/PanelComponents/PanelHeader.tsx index 94e32b3..925d46d 100644 --- a/src/components/PanelComponents/PanelHeader.tsx +++ b/src/components/PanelComponents/PanelHeader.tsx @@ -10,10 +10,8 @@ export default function PanelHeader(props: PanelHeaderProps): JSX.Element { return ( <>
    - {props.icon ? ( + {props.icon && ( {props.icon} - ) : ( - "" )}

    {props.title}

    {props.description ?

    {props.description}

    : ""} diff --git a/src/components/PanelComponents/ReturnButton.tsx b/src/components/PanelComponents/ReturnButton.tsx index 6f2517c..e930e71 100644 --- a/src/components/PanelComponents/ReturnButton.tsx +++ b/src/components/PanelComponents/ReturnButton.tsx @@ -1,4 +1,5 @@ import Button from "components/Button"; +import HorizontalLine from "components/HorizontalLine"; import { useAppLayout } from "contexts/AppLayoutContext"; import { GetWebsiteInterfaceQuery } from "graphql/operations-types"; @@ -6,14 +7,39 @@ type ReturnButtonProps = { href: string; title: string; langui: GetWebsiteInterfaceQuery["websiteInterfaces"]["data"][number]["attributes"]; + displayOn: ReturnButtonType; + horizontalLine?: boolean; + className?: string; }; +export enum ReturnButtonType { + mobile = "mobile", + desktop = "desktop", + both = "both", +} + export default function ReturnButton(props: ReturnButtonProps): JSX.Element { const appLayout = useAppLayout(); return ( - +
    + + {props.horizontalLine && } +
    ); } diff --git a/src/components/Panels/ContentPanel.tsx b/src/components/Panels/ContentPanel.tsx index 5b0dfa5..ad86d8a 100644 --- a/src/components/Panels/ContentPanel.tsx +++ b/src/components/Panels/ContentPanel.tsx @@ -5,19 +5,22 @@ type ContentPanelProps = { }; export enum ContentPanelWidthSizes { - default, - large, + default = "default", + large = "large", } export default function ContentPanel(props: ContentPanelProps): JSX.Element { const width = props.width ? props.width : ContentPanelWidthSizes.default; const widthCSS = - width === ContentPanelWidthSizes.default ? "max-w-[45rem]" : "w-full"; - const prose = props.autoformat ? "prose text-justify" : ""; + width === ContentPanelWidthSizes.default ? "max-w-2xl" : "w-full"; return (
    -
    +
    {props.children}
    diff --git a/src/components/Panels/MainPanel.tsx b/src/components/Panels/MainPanel.tsx index 1ac3b2f..f95ee31 100644 --- a/src/components/Panels/MainPanel.tsx +++ b/src/components/Panels/MainPanel.tsx @@ -1,20 +1,20 @@ -import Link from "next/link"; -import NavOption from "components/PanelComponents/NavOption"; -import SVG from "components/SVG"; -import { useRouter } from "next/router"; import Button from "components/Button"; import HorizontalLine from "components/HorizontalLine"; -import { GetWebsiteInterfaceQuery } from "graphql/operations-types"; -import Markdown from "markdown-to-jsx"; -import { useMediaDesktop } from "hooks/useMediaQuery"; +import NavOption from "components/PanelComponents/NavOption"; +import ToolTip from "components/ToolTip"; import { useAppLayout } from "contexts/AppLayoutContext"; +import { GetWebsiteInterfaceQuery } from "graphql/operations-types"; +import { useMediaDesktop } from "hooks/useMediaQuery"; +import Markdown from "markdown-to-jsx"; +import Link from "next/link"; +import { useRouter } from "next/router"; type MainPanelProps = { langui: GetWebsiteInterfaceQuery["websiteInterfaces"]["data"][number]["attributes"]; }; export default function MainPanel(props: MainPanelProps): JSX.Element { - const langui = props.langui; + const { langui } = props; const router = useRouter(); const isDesktop = useMediaDesktop(); const appLayout = useAppLayout(); @@ -25,6 +25,20 @@ export default function MainPanel(props: MainPanelProps): JSX.Element { appLayout.mainPanelReduced && isDesktop && "px-4" }`} > + {/* Reduce/expand main menu */} +
    + appLayout.setMainPanelReduced(!appLayout.mainPanelReduced) + } + > + +
    +
    @@ -49,32 +63,74 @@ export default function MainPanel(props: MainPanelProps): JSX.Element { : "flex-row" } flex-wrap gap-2`} > - - - {router.locale && ( + + + {router.locale && ( + {langui.change_language}} + placement="right" + className="text-left" + disabled={!appLayout.mainPanelReduced} + > + + + )} + + {/* {langui.open_search}} + placement="right" + className="text-left" + disabled={!appLayout.mainPanelReduced} + > + - )} + */}
    @@ -84,9 +140,8 @@ export default function MainPanel(props: MainPanelProps): JSX.Element { appLayout.setMainPanelOpen(false)} /> @@ -94,9 +149,8 @@ export default function MainPanel(props: MainPanelProps): JSX.Element { appLayout.setMainPanelOpen(false)} /> @@ -104,66 +158,73 @@ export default function MainPanel(props: MainPanelProps): JSX.Element { appLayout.setMainPanelOpen(false)} /> + {/* + appLayout.setMainPanelOpen(false)} /> + + */} appLayout.setMainPanelOpen(false)} /> - + {/* appLayout.setMainPanelOpen(false)} /> + + */} appLayout.setMainPanelOpen(false)} /> + {/* + appLayout.setMainPanelOpen(false)} /> + + */} + appLayout.setMainPanelOpen(false)} /> @@ -176,10 +237,8 @@ export default function MainPanel(props: MainPanelProps): JSX.Element { }`} >

    - {langui.main_licensing ? ( - {langui.main_licensing} - ) : ( - "" + {langui.licensing_notice && ( + {langui.licensing_notice} )}

    - {langui.main_copyright ? ( - {langui.main_copyright} - ) : ( - "" + {langui.copyright_notice && ( + {langui.copyright_notice} )}

    diff --git a/src/components/Panels/SubPanel.tsx b/src/components/Panels/SubPanel.tsx index 89f2584..be36a28 100644 --- a/src/components/Panels/SubPanel.tsx +++ b/src/components/Panels/SubPanel.tsx @@ -4,7 +4,7 @@ type SubPanelProps = { export default function SubPanel(props: SubPanelProps): JSX.Element { return ( -
    +
    {props.children}
    ); diff --git a/src/components/Popup.tsx b/src/components/Popup.tsx new file mode 100644 index 0000000..345ca26 --- /dev/null +++ b/src/components/Popup.tsx @@ -0,0 +1,54 @@ +import { Dispatch, SetStateAction } from "react"; +import Button from "./Button"; + +export type PopupProps = { + setState: + | Dispatch> + | Dispatch>; + state?: boolean; + children: React.ReactNode; + fillViewport?: boolean; + hideBackground?: boolean; +}; + +export default function Popup(props: PopupProps): JSX.Element { + return ( +
    { + if (event.key.match("Escape")) props.setState(false); + }} + tabIndex={0} + > +
    { + props.setState(false); + }} + /> +
    + + {props.children} +
    +
    + ); +} diff --git a/src/components/RecorderChip.tsx b/src/components/RecorderChip.tsx index af0c644..8a23bef 100644 --- a/src/components/RecorderChip.tsx +++ b/src/components/RecorderChip.tsx @@ -1,60 +1,69 @@ import Chip from "components/Chip"; -import { GetContentTextQuery } from "graphql/operations-types"; +import { + GetContentTextQuery, + GetWebsiteInterfaceQuery, +} from "graphql/operations-types"; +import Button from "./Button"; import Img, { ImageQuality } from "./Img"; -import ReactDOMServer from "react-dom/server"; +import ToolTip from "./ToolTip"; type RecorderChipProps = { className?: string; recorder: GetContentTextQuery["contents"]["data"][number]["attributes"]["text_set"][number]["transcribers"]["data"][number]; + langui: GetWebsiteInterfaceQuery["websiteInterfaces"]["data"][number]["attributes"]; }; export default function RecorderChip(props: RecorderChipProps): JSX.Element { - const recorder = props.recorder; + const { recorder, langui } = props; + return ( - -
    + +
    {recorder.attributes.avatar.data && ( )} -

    {recorder.attributes.username}

    +
    +

    {recorder.attributes.username}

    + {recorder.attributes.languages.data.length > 0 && ( +
    +

    {langui.languages}:

    + {recorder.attributes.languages.data.map((language) => ( + + {language.attributes.code.toUpperCase()} + + ))} +
    + )} + {recorder.attributes.pronouns && ( +
    +

    {langui.pronouns}:

    + {recorder.attributes.pronouns} +
    + )} +
    - {recorder.attributes.languages.data.length > 0 && ( -
    -

    Languages:

    - {recorder.attributes.languages.data.map((language) => ( - - {language.attributes.code.toUpperCase()} - - ))} -
    + + {recorder.attributes.bio.length > 0 && ( +

    {recorder.attributes.bio[0].bio}

    )} - {recorder.attributes.pronouns && ( -
    -

    Pronouns:

    - {recorder.attributes.pronouns} -
    - )} -

    - {recorder.attributes.bio.length > 0 && - recorder.attributes.bio[0].bio} -

    + +
    - )} - data-for={"RecordersTooltip"} - data-multiline - data-html + } + placement="top" > - {recorder.attributes.anonymize - ? `Recorder#${recorder.attributes.anonymous_code}` - : recorder.attributes.username} -
    + + {recorder.attributes.anonymize + ? `Recorder#${recorder.attributes.anonymous_code}` + : recorder.attributes.username} + + ); } diff --git a/src/components/Select.tsx b/src/components/Select.tsx new file mode 100644 index 0000000..74c270d --- /dev/null +++ b/src/components/Select.tsx @@ -0,0 +1,66 @@ +import { Dispatch, SetStateAction, useState } from "react"; + +export type SelectProps = { + setState: Dispatch>; + state: number; + options: string[]; + selected?: number; + allowEmpty?: boolean; + className?: string; +}; + +export default function Select(props: SelectProps): JSX.Element { + const [opened, setOpened] = useState(false); + + return ( +
    +
    +

    setOpened(!opened)} className="w-full"> + {props.state === -1 ? "—" : props.options[props.state]} +

    + {props.state >= 0 && props.allowEmpty && ( + props.setState(-1)} + className="material-icons !text-xs" + > + close + + )} + setOpened(!opened)} className="material-icons"> + {opened ? "arrow_drop_up" : "arrow_drop_down"} + +
    +
    + {props.options.map((option, index) => ( + <> + {index !== props.state && ( +
    { + setOpened(false); + props.setState(index); + }} + > + {option} +
    + )} + + ))} +
    +
    + ); +} diff --git a/src/components/Switch.tsx b/src/components/Switch.tsx new file mode 100644 index 0000000..72cb871 --- /dev/null +++ b/src/components/Switch.tsx @@ -0,0 +1,26 @@ +import { Dispatch, SetStateAction } from "react"; + +export type SwitchProps = { + setState: Dispatch>; + state: boolean; + className?: string; +}; + +export default function Switch(props: SwitchProps): JSX.Element { + return ( +
    { + props.setState(!props.state); + }} + > +
    +
    + ); +} diff --git a/src/components/ToolTip.tsx b/src/components/ToolTip.tsx new file mode 100644 index 0000000..de5b64d --- /dev/null +++ b/src/components/ToolTip.tsx @@ -0,0 +1,19 @@ +import Tippy, { TippyProps } from "@tippyjs/react"; +import "tippy.js/animations/scale-subtle.css"; + +interface ToolTipProps extends TippyProps {} + +export default function ToolTip(props: ToolTipProps): JSX.Element { + const newProps = { ...props }; + + // Set defaults + if (newProps.delay === undefined) newProps.delay = [150, 0]; + if (newProps.interactive === undefined) newProps.interactive = true; + if (newProps.animation === undefined) newProps.animation = "scale-subtle"; + + return ( + +
    {props.children}
    +
    + ); +} diff --git a/src/contexts/AppLayoutContext.tsx b/src/contexts/AppLayoutContext.tsx index eac6321..b931cdf 100644 --- a/src/contexts/AppLayoutContext.tsx +++ b/src/contexts/AppLayoutContext.tsx @@ -5,13 +5,20 @@ import React, { ReactNode, useContext } from "react"; export interface AppLayoutState { subPanelOpen: boolean | undefined; languagePanelOpen: boolean | undefined; + configPanelOpen: boolean | undefined; mainPanelReduced: boolean | undefined; mainPanelOpen: boolean | undefined; darkMode: boolean | undefined; + selectedThemeMode: boolean | undefined; + fontSize: number | undefined; + dyslexic: boolean | undefined; + currency: string | undefined; + playerName: string | undefined; setSubPanelOpen: React.Dispatch>; setLanguagePanelOpen: React.Dispatch< React.SetStateAction >; + setConfigPanelOpen: React.Dispatch>; setMainPanelReduced: React.Dispatch< React.SetStateAction >; @@ -20,27 +27,44 @@ export interface AppLayoutState { setSelectedThemeMode: React.Dispatch< React.SetStateAction >; + setFontSize: React.Dispatch>; + setDyslexic: React.Dispatch>; + setCurrency: React.Dispatch>; + setPlayerName: React.Dispatch>; } +/* eslint-disable @typescript-eslint/no-empty-function */ const initialState: AppLayoutState = { subPanelOpen: false, languagePanelOpen: false, + configPanelOpen: false, mainPanelReduced: false, mainPanelOpen: false, darkMode: false, + selectedThemeMode: false, + fontSize: 1, + dyslexic: false, + currency: "USD", + playerName: "", setSubPanelOpen: () => {}, setLanguagePanelOpen: () => {}, setMainPanelReduced: () => {}, setMainPanelOpen: () => {}, setDarkMode: () => {}, setSelectedThemeMode: () => {}, + setConfigPanelOpen: () => {}, + setFontSize: () => {}, + setDyslexic: () => {}, + setCurrency: () => {}, + setPlayerName: () => {}, }; +/* eslint-enable @typescript-eslint/no-empty-function */ const AppContext = React.createContext(initialState); export default AppContext; -export function useAppLayout() { +export function useAppLayout(): AppLayoutState { return useContext(AppContext); } @@ -48,7 +72,7 @@ type Props = { children: ReactNode; }; -export const AppContextProvider = (props: Props) => { +export function AppContextProvider(props: Props): JSX.Element { const [subPanelOpen, setSubPanelOpen] = useStateWithLocalStorage< boolean | undefined >("subPanelOpen", initialState.subPanelOpen); @@ -57,6 +81,10 @@ export const AppContextProvider = (props: Props) => { boolean | undefined >("languagePanelOpen", initialState.languagePanelOpen); + const [configPanelOpen, setConfigPanelOpen] = useStateWithLocalStorage< + boolean | undefined + >("configPanelOpen", initialState.configPanelOpen); + const [mainPanelReduced, setMainPanelReduced] = useStateWithLocalStorage< boolean | undefined >("mainPanelReduced", initialState.mainPanelReduced); @@ -65,28 +93,56 @@ export const AppContextProvider = (props: Props) => { boolean | undefined >("mainPanelOpen", initialState.mainPanelOpen); - const [darkMode, setDarkMode, setSelectedThemeMode] = useDarkMode( - "darkMode", - initialState.darkMode + const [darkMode, selectedThemeMode, setDarkMode, setSelectedThemeMode] = + useDarkMode("darkMode", initialState.darkMode); + + const [fontSize, setFontSize] = useStateWithLocalStorage( + "fontSize", + initialState.fontSize ); + const [dyslexic, setDyslexic] = useStateWithLocalStorage( + "dyslexic", + initialState.dyslexic + ); + + const [currency, setCurrency] = useStateWithLocalStorage( + "currency", + initialState.currency + ); + + const [playerName, setPlayerName] = useStateWithLocalStorage< + string | undefined + >("playerName", initialState.playerName); + return ( {props.children} ); -}; +} diff --git a/src/graphql/operation.graphql b/src/graphql/operation.graphql index 63d9963..bf38c35 100644 --- a/src/graphql/operation.graphql +++ b/src/graphql/operation.graphql @@ -1,72 +1,137 @@ query getWebsiteInterface($language_code: String) { - websiteInterfaces(filters: { language: { code: { eq: $language_code } } }) { + websiteInterfaces( + filters: { ui_language: { code: { eq: $language_code } } } + ) { data { attributes { - main_library - main_library_description - main_news - main_merch - main_gallery - main_archives - main_about_us - main_licensing - main_copyright + library + contents + wiki + chronicles + library_short_description + contents_short_description + wiki_short_description + chronicles_short_description + news + merch + gallery + archives + about_us + licensing_notice + copyright_notice + contents_description + type + category + categories + size + release_date + release_year + details + price + width + height + thickness + subitem + subitems + subitem_of + variant + variants + variant_of + summary + audio + video + textual + game + other + return_to + left_to_right + right_to_left + page + pages + page_order + binding + type_information + front_matter + back_matter + open_content + read_content + watch_content + listen_content + view_scans + paperback + hardcover + languages + select_language + language library_description - library_item_summary - library_item_gallery - library_item_details - library_item_subitems - library_item_variants - library_item_content - global_return_label - global_subitem_of - global_type - global_width - global_height - global_thickness - global_binding - global_language - global_languages - global_page - global_pages - global_page_order - global_release_date - global_price - library_item_physical_size - library_item_type_information - library_item_front_matter - library_item_back_matter - library_item_type_textual - library_item_type_audio - library_item_type_game - library_item_type_video - library_item_type_other - library_item_open_content - library_item_view_scans - content_read_content - content_watch_content - content_listen_content - global_category - global_categories - global_paperback - global_hardcover - global_left_to_right - global_right_to_left - main_wiki - main_wiki_description - main_chronicles - main_chronicles_description - library_items - library_items_description - library_content - library_content_description wiki_description - news_description chronicles_description + news_description + merch_description gallery_description archives_description about_us_description - merch_description + page_not_found + default_description + name + show_subitems + show_primary_items + show_secondary_items + no_type + no_year + order_by + group_by + select_option_sidebar + group + settings + theme + light + auto + dark + font_size + player_name + currency + font + calculated + status_incomplete + status_draft + status_review + status_done + incomplete + draft + review + done + status + transcribers + translators + proofreaders + transcript_notice + translation_notice + source_language + pronouns + no_category + item + items + content + result + results + language_switch_message + open_settings + change_language + open_search + chronology + accords_handbook + legality + members + sharing_policy + contact_us + email + email_gdpr_notice + message + send + response_invalid_code + response_invalid_email + response_email_success } } } @@ -125,17 +190,15 @@ query getChronologyItems($language_code: String) { } query getLibraryItemsPreview($language_code: String) { - libraryItems( - filters: { root_item: { eq: true } } - pagination: { limit: -1 } - sort: ["title:asc", "subtitle:asc"] - ) { + libraryItems(pagination: { limit: -1 }) { data { id attributes { title subtitle slug + root_item + primary thumbnail { data { attributes { @@ -160,10 +223,20 @@ query getLibraryItemsPreview($language_code: String) { attributes { symbol code + rate_to_usd } } } } + categories { + data { + id + attributes { + name + short + } + } + } metadata { __typename ... on ComponentMetadataBooks { @@ -218,7 +291,7 @@ query getLibraryItemsPreview($language_code: String) { } } } - ... on ComponentMetadataOther { + ... on ComponentMetadataGroup { subtype { data { attributes { @@ -231,6 +304,18 @@ query getLibraryItemsPreview($language_code: String) { } } } + subitems_type { + data { + attributes { + slug + titles( + filters: { language: { code: { eq: $language_code } } } + ) { + title + } + } + } + } } } } @@ -296,10 +381,20 @@ query getLibraryItem($slug: String, $language_code: String) { attributes { symbol code + rate_to_usd } } } } + categories { + data { + id + attributes { + name + short + } + } + } size { width height @@ -396,7 +491,7 @@ query getLibraryItem($slug: String, $language_code: String) { } } } - ... on ComponentMetadataOther { + ... on ComponentMetadataGroup { subtype { data { attributes { @@ -409,6 +504,18 @@ query getLibraryItem($slug: String, $language_code: String) { } } } + subitems_type { + data { + attributes { + slug + titles( + filters: { language: { code: { eq: $language_code } } } + ) { + title + } + } + } + } } } subitem_of { @@ -452,10 +559,20 @@ query getLibraryItem($slug: String, $language_code: String) { attributes { symbol code + rate_to_usd } } } } + categories { + data { + id + attributes { + name + short + } + } + } metadata { __typename ... on ComponentMetadataBooks { @@ -516,7 +633,7 @@ query getLibraryItem($slug: String, $language_code: String) { } } } - ... on ComponentMetadataOther { + ... on ComponentMetadataGroup { subtype { data { attributes { @@ -531,6 +648,20 @@ query getLibraryItem($slug: String, $language_code: String) { } } } + subitems_type { + data { + attributes { + slug + titles( + filters: { + language: { code: { eq: $language_code } } + } + ) { + title + } + } + } + } } } } @@ -585,6 +716,7 @@ query getLibraryItem($slug: String, $language_code: String) { data { id attributes { + name short } } @@ -655,6 +787,7 @@ query getContents($language_code: String) { data { id attributes { + name short } } @@ -878,6 +1011,15 @@ query getContentText($slug: String, $language_code: String) { } } } + text_set_languages: text_set { + language { + data { + attributes { + code + } + } + } + } text_set(filters: { language: { code: { eq: $language_code } } }) { status text @@ -1005,3 +1147,417 @@ query getContentText($slug: String, $language_code: String) { } } } + +query getCurrencies { + currencies { + data { + id + attributes { + code + symbol + rate_to_usd + display_decimals + } + } + } +} + +query getLanguages { + languages { + data { + id + attributes { + name + code + localized_name + } + } + } +} + +query getPost($slug: String, $language_code: String) { + posts(filters: { slug: { eq: $slug } }) { + data { + id + attributes { + slug + updatedAt + date { + year + month + day + } + authors { + data { + id + attributes { + username + anonymize + anonymous_code + pronouns + bio(filters: { language: { code: { eq: $language_code } } }) { + bio + } + languages { + data { + attributes { + code + } + } + } + avatar { + data { + attributes { + name + alternativeText + caption + width + height + url + } + } + } + } + } + } + categories { + data { + id + attributes { + name + short + } + } + } + hidden + thumbnail { + data { + attributes { + name + alternativeText + caption + width + height + url + } + } + } + translations_languages: translations { + language { + data { + attributes { + code + } + } + } + } + translations(filters: { language: { code: { eq: $language_code } } }) { + status + title + excerpt + thumbnail { + data { + attributes { + name + alternativeText + caption + width + height + url + } + } + } + body + } + } + } + } +} + +query getPostsSlugs { + posts(filters: { hidden: { eq: false } }) { + data { + id + attributes { + slug + } + } + } +} + +query getPostsPreview($language_code: String) { + posts(filters: { hidden: { eq: false } }) { + data { + id + attributes { + slug + date { + year + month + day + } + categories { + data { + id + attributes { + short + } + } + } + thumbnail { + data { + attributes { + name + alternativeText + caption + width + height + url + } + } + } + translations(filters: { language: { code: { eq: $language_code } } }) { + title + excerpt + thumbnail { + data { + attributes { + name + alternativeText + caption + width + height + url + } + } + } + } + } + } + } +} + +query getContentLanguages($slug: String) { + contents(filters: { slug: { eq: $slug } }) { + data { + attributes { + text_set { + language { + data { + attributes { + code + } + } + } + } + video_set { + language { + data { + attributes { + code + } + } + } + } + audio_set { + language { + data { + attributes { + code + } + } + } + } + } + } + } +} + +query getLibraryItemScans($slug: String, $language_code: String) { + libraryItems(filters: { slug: { eq: $slug } }) { + data { + id + attributes { + slug + title + subtitle + thumbnail { + data { + attributes { + name + alternativeText + caption + width + height + url + } + } + } + contents(pagination: { limit: -1 }) { + data { + id + attributes { + slug + range { + __typename + ... on ComponentRangePageRange { + starting_page + ending_page + } + ... on ComponentRangeTimeRange { + starting_time + ending_time + } + } + scan_set_languages: scan_set { + language { + data { + attributes { + code + } + } + } + } + scan_set( + filters: { + or: [ + { language: { code: { eq: "xx" } } } + { language: { code: { eq: $language_code } } } + ] + } + ) { + status + source_language { + data { + attributes { + code + } + } + } + scanners { + data { + id + attributes { + username + anonymize + anonymous_code + pronouns + bio( + filters: { language: { code: { eq: $language_code } } } + ) { + bio + } + languages { + data { + attributes { + code + } + } + } + avatar { + data { + attributes { + name + alternativeText + caption + width + height + url + } + } + } + } + } + } + cleaners { + data { + id + attributes { + username + anonymize + anonymous_code + pronouns + bio( + filters: { language: { code: { eq: $language_code } } } + ) { + bio + } + languages { + data { + attributes { + code + } + } + } + avatar { + data { + attributes { + name + alternativeText + caption + width + height + url + } + } + } + } + } + } + typesetters { + data { + id + attributes { + username + anonymize + anonymous_code + pronouns + bio( + filters: { language: { code: { eq: $language_code } } } + ) { + bio + } + languages { + data { + attributes { + code + } + } + } + avatar { + data { + attributes { + name + alternativeText + caption + width + height + url + } + } + } + } + } + } + notes + pages(pagination: { limit: -1 }) { + data { + id + attributes { + name + alternativeText + caption + width + height + url + } + } + } + } + } + } + } + } + } + } +} diff --git a/src/graphql/operations-types.ts b/src/graphql/operations-types.ts index 57b4615..a07b590 100644 --- a/src/graphql/operations-types.ts +++ b/src/graphql/operations-types.ts @@ -1,3 +1,7 @@ +/* eslint-disable @typescript-eslint/consistent-indexed-object-style */ +/* eslint-disable @typescript-eslint/array-type */ +/* eslint-disable no-shadow */ +/* eslint-disable id-denylist */ export type Exact = T; export type InputMaybe = T; export type Scalars = { @@ -6,30 +10,31 @@ export type Scalars = { Boolean: boolean; Int: number; Float: number; - JSON: any; - DateTime: any; - Time: any; - Upload: any; - LibraryContentRangeDynamicZoneInput: any; - LibraryItemMetadataDynamicZoneInput: any; - SourceSourceDynamicZoneInput: any; + JSON: unknown; + DateTime: unknown; + Time: unknown; + Upload: unknown; + LibraryContentRangeDynamicZoneInput: unknown; + LibraryItemMetadataDynamicZoneInput: unknown; + SourceSourceDynamicZoneInput: unknown; }; /* - The following is generated using https://www.graphql-code-generator.com/ - With the following codegen.yml: - - generates: - operations-types.ts: - plugins: - - typescript-operations - - And the schema.graphql and operation.graphql files from this folder. - - But to make the type easier to work with, it went through the following transformation: - - Removed ? - - Removed | null -*/ + * The following is generated using https://www.graphql-code-generator.com/ + * With the following codegen.yml: + * + * generates: + * operations-types.ts: + * plugins: + * - typescript-operations + * + * And the schema.graphql and operation.graphql files from this folder. + * + * But to make the type easier to work with, it went through the following transformation: + * - Removed ? + * - Removed + * - Replaced any by unknown + */ export enum Enum_Componentmetadatabooks_Binding_Type { Paperback = "Paperback", @@ -48,6 +53,13 @@ export enum Enum_Componentmetadatavideo_Resolution { QuadHd_2160p = "QuadHD_2160p", } +export enum Enum_Componenttranslationsposts_Status { + Incomplete = "Incomplete", + Draft = "Draft", + Review = "Review", + Done = "Done", +} + export enum Enum_Componenttranslationschronologyitem_Status { Incomplete = "Incomplete", Draft = "Draft", @@ -62,6 +74,13 @@ export enum Enum_Componentsetstextset_Status { Done = "Done", } +export enum Enum_Componentsetsscanset_Status { + Incomplete = "Incomplete", + Draft = "Draft", + Review = "Review", + Done = "Done", +} + export type StrapiImage = { __typename: "UploadFile"; name: string; @@ -86,71 +105,134 @@ export type GetWebsiteInterfaceQuery = { __typename: "WebsiteInterfaceEntity"; attributes: { __typename: "WebsiteInterface"; - main_library: string; - main_library_description: string; - main_news: string; - main_merch: string; - main_gallery: string; - main_archives: string; - main_about_us: string; - main_licensing: string; - main_copyright: string; + library: string; + contents: string; + wiki: string; + chronicles: string; + library_short_description: string; + contents_short_description: string; + wiki_short_description: string; + chronicles_short_description: string; + news: string; + merch: string; + gallery: string; + archives: string; + about_us: string; + licensing_notice: string; + copyright_notice: string; + contents_description: string; + type: string; + category: string; + categories: string; + size: string; + release_date: string; + release_year: string; + details: string; + price: string; + width: string; + height: string; + thickness: string; + subitem: string; + subitems: string; + subitem_of: string; + variant: string; + variants: string; + variant_of: string; + summary: string; + audio: string; + video: string; + textual: string; + game: string; + other: string; + return_to: string; + left_to_right: string; + right_to_left: string; + page: string; + pages: string; + page_order: string; + binding: string; + type_information: string; + front_matter: string; + back_matter: string; + open_content: string; + read_content: string; + watch_content: string; + listen_content: string; + view_scans: string; + paperback: string; + hardcover: string; + languages: string; + select_language: string; + language: string; library_description: string; - library_item_summary: string; - library_item_gallery: string; - library_item_details: string; - library_item_subitems: string; - library_item_variants: string; - library_item_content: string; - global_return_label: string; - global_subitem_of: string; - global_type: string; - global_width: string; - global_height: string; - global_thickness: string; - global_binding: string; - global_language: string; - global_languages: string; - global_page: string; - global_pages: string; - global_page_order: string; - global_release_date: string; - global_price: string; - library_item_physical_size: string; - library_item_type_information: string; - library_item_front_matter: string; - library_item_back_matter: string; - library_item_type_textual: string; - library_item_type_audio: string; - library_item_type_game: string; - library_item_type_video: string; - library_item_type_other: string; - library_item_open_content: string; - library_item_view_scans: string; - content_read_content: string; - content_watch_content: string; - content_listen_content: string; - global_category: string; - global_categories: string; - global_paperback: string; - global_hardcover: string; - global_left_to_right: string; - global_right_to_left: string; - main_wiki: string; - main_wiki_description: string; - main_chronicles: string; - main_chronicles_description: string; - library_items: string; - library_items_description: string; - library_content: string; - library_content_description: string; wiki_description: string; - news_description: string; chronicles_description: string; + news_description: string; + merch_description: string; gallery_description: string; archives_description: string; about_us_description: string; - merch_description: string; + page_not_found: string; + default_description: string; + name: string; + show_subitems: string; + show_primary_items: string; + show_secondary_items: string; + no_type: string; + no_year: string; + order_by: string; + group_by: string; + select_option_sidebar: string; + group: string; + settings: string; + theme: string; + light: string; + auto: string; + dark: string; + font_size: string; + player_name: string; + currency: string; + font: string; + calculated: string; + status_incomplete: string; + status_draft: string; + status_review: string; + status_done: string; + incomplete: string; + draft: string; + review: string; + done: string; + status: string; + transcribers: string; + translators: string; + proofreaders: string; + transcript_notice: string; + translation_notice: string; + source_language: string; + pronouns: string; + no_category: string; + item: string; + items: string; + content: string; + result: string; + results: string; + language_switch_message: string; + open_settings: string; + change_language: string; + open_search: string; + chronology: string; + accords_handbook: string; + legality: string; + members: string; + sharing_policy: string; + contact_us: string; + email: string; + email_gdpr_notice: string; + message: string; + send: string; + response_invalid_code: string; + response_invalid_email: string; + response_email_success: string; }; }>; }; @@ -238,6 +320,8 @@ export type GetLibraryItemsPreviewQuery = { title: string; subtitle: string; slug: string; + root_item: boolean; + primary: boolean; thumbnail: { __typename: "UploadFileEntityResponse"; data: { @@ -270,19 +354,32 @@ export type GetLibraryItemsPreviewQuery = { __typename: "Currency"; symbol: string; code: string; + rate_to_usd: number; }; }; }; }; + categories: { + __typename: "CategoryRelationResponseCollection"; + data: Array<{ + __typename: "CategoryEntity"; + id: string; + attributes: { + __typename: "Category"; + name: string; + short: string; + }; + }>; + }; metadata: Array< | { - __typename: "ComponentMetadataBooks"; + __typename: "ComponentMetadataAudio"; subtype: { - __typename: "TextualSubtypeEntityResponse"; + __typename: "AudioSubtypeEntityResponse"; data: { - __typename: "TextualSubtypeEntity"; + __typename: "AudioSubtypeEntity"; attributes: { - __typename: "TextualSubtype"; + __typename: "AudioSubtype"; slug: string; titles: Array<{ __typename: "ComponentTranslationsSimpleTitle"; @@ -293,13 +390,13 @@ export type GetLibraryItemsPreviewQuery = { }; } | { - __typename: "ComponentMetadataVideo"; + __typename: "ComponentMetadataBooks"; subtype: { - __typename: "VideoSubtypeEntityResponse"; + __typename: "TextualSubtypeEntityResponse"; data: { - __typename: "VideoSubtypeEntity"; + __typename: "TextualSubtypeEntity"; attributes: { - __typename: "VideoSubtype"; + __typename: "TextualSubtype"; slug: string; titles: Array<{ __typename: "ComponentTranslationsSimpleTitle"; @@ -324,13 +421,27 @@ export type GetLibraryItemsPreviewQuery = { }; } | { - __typename: "ComponentMetadataAudio"; + __typename: "ComponentMetadataGroup"; subtype: { - __typename: "AudioSubtypeEntityResponse"; + __typename: "GroupSubtypeEntityResponse"; data: { - __typename: "AudioSubtypeEntity"; + __typename: "GroupSubtypeEntity"; attributes: { - __typename: "AudioSubtype"; + __typename: "GroupSubtype"; + slug: string; + titles: Array<{ + __typename: "ComponentTranslationsSimpleTitle"; + title: string; + }>; + }; + }; + }; + subitems_type: { + __typename: "MetadataTypeEntityResponse"; + data: { + __typename: "MetadataTypeEntity"; + attributes: { + __typename: "MetadataType"; slug: string; titles: Array<{ __typename: "ComponentTranslationsSimpleTitle"; @@ -340,14 +451,15 @@ export type GetLibraryItemsPreviewQuery = { }; }; } + | { __typename: "ComponentMetadataOther" } | { - __typename: "ComponentMetadataOther"; + __typename: "ComponentMetadataVideo"; subtype: { - __typename: "OtherSubtypeEntityResponse"; + __typename: "VideoSubtypeEntityResponse"; data: { - __typename: "OtherSubtypeEntity"; + __typename: "VideoSubtypeEntity"; attributes: { - __typename: "OtherSubtype"; + __typename: "VideoSubtype"; slug: string; titles: Array<{ __typename: "ComponentTranslationsSimpleTitle"; @@ -447,10 +559,23 @@ export type GetLibraryItemQuery = { __typename: "Currency"; symbol: string; code: string; + rate_to_usd: number; }; }; }; }; + categories: { + __typename: "CategoryRelationResponseCollection"; + data: Array<{ + __typename: "CategoryEntity"; + id: string; + attributes: { + __typename: "Category"; + name: string; + short: string; + }; + }>; + }; size: { __typename: "ComponentBasicsSize"; width: number; @@ -462,6 +587,23 @@ export type GetLibraryItemQuery = { description: string; }>; metadata: Array< + | { + __typename: "ComponentMetadataAudio"; + subtype: { + __typename: "AudioSubtypeEntityResponse"; + data: { + __typename: "AudioSubtypeEntity"; + attributes: { + __typename: "AudioSubtype"; + slug: string; + titles: Array<{ + __typename: "ComponentTranslationsSimpleTitle"; + title: string; + }>; + }; + }; + }; + } | { __typename: "ComponentMetadataBooks"; binding_type: Enum_Componentmetadatabooks_Binding_Type; @@ -493,22 +635,6 @@ export type GetLibraryItemQuery = { }>; }; } - | { - __typename: "ComponentMetadataVideo"; - subtype: { - __typename: "VideoSubtypeEntityResponse"; - data: { - __typename: "VideoSubtypeEntity"; - attributes: { - __typename: "VideoSubtype"; - titles: Array<{ - __typename: "ComponentTranslationsSimpleTitle"; - title: string; - }>; - }; - }; - }; - } | { __typename: "ComponentMetadataGame"; platforms: { @@ -557,13 +683,27 @@ export type GetLibraryItemQuery = { }; } | { - __typename: "ComponentMetadataAudio"; + __typename: "ComponentMetadataGroup"; subtype: { - __typename: "AudioSubtypeEntityResponse"; + __typename: "GroupSubtypeEntityResponse"; data: { - __typename: "AudioSubtypeEntity"; + __typename: "GroupSubtypeEntity"; attributes: { - __typename: "AudioSubtype"; + __typename: "GroupSubtype"; + slug: string; + titles: Array<{ + __typename: "ComponentTranslationsSimpleTitle"; + title: string; + }>; + }; + }; + }; + subitems_type: { + __typename: "MetadataTypeEntityResponse"; + data: { + __typename: "MetadataTypeEntity"; + attributes: { + __typename: "MetadataType"; slug: string; titles: Array<{ __typename: "ComponentTranslationsSimpleTitle"; @@ -573,15 +713,15 @@ export type GetLibraryItemQuery = { }; }; } + | { __typename: "ComponentMetadataOther" } | { - __typename: "ComponentMetadataOther"; + __typename: "ComponentMetadataVideo"; subtype: { - __typename: "OtherSubtypeEntityResponse"; + __typename: "VideoSubtypeEntityResponse"; data: { - __typename: "OtherSubtypeEntity"; + __typename: "VideoSubtypeEntity"; attributes: { - __typename: "OtherSubtype"; - slug: string; + __typename: "VideoSubtype"; titles: Array<{ __typename: "ComponentTranslationsSimpleTitle"; title: string; @@ -647,19 +787,32 @@ export type GetLibraryItemQuery = { __typename: "Currency"; symbol: string; code: string; + rate_to_usd: number; }; }; }; }; + categories: { + __typename: "CategoryRelationResponseCollection"; + data: Array<{ + __typename: "CategoryEntity"; + id: string; + attributes: { + __typename: "Category"; + name: string; + short: string; + }; + }>; + }; metadata: Array< | { - __typename: "ComponentMetadataBooks"; + __typename: "ComponentMetadataAudio"; subtype: { - __typename: "TextualSubtypeEntityResponse"; + __typename: "AudioSubtypeEntityResponse"; data: { - __typename: "TextualSubtypeEntity"; + __typename: "AudioSubtypeEntity"; attributes: { - __typename: "TextualSubtype"; + __typename: "AudioSubtype"; slug: string; titles: Array<{ __typename: "ComponentTranslationsSimpleTitle"; @@ -670,13 +823,13 @@ export type GetLibraryItemQuery = { }; } | { - __typename: "ComponentMetadataVideo"; + __typename: "ComponentMetadataBooks"; subtype: { - __typename: "VideoSubtypeEntityResponse"; + __typename: "TextualSubtypeEntityResponse"; data: { - __typename: "VideoSubtypeEntity"; + __typename: "TextualSubtypeEntity"; attributes: { - __typename: "VideoSubtype"; + __typename: "TextualSubtype"; slug: string; titles: Array<{ __typename: "ComponentTranslationsSimpleTitle"; @@ -701,13 +854,27 @@ export type GetLibraryItemQuery = { }; } | { - __typename: "ComponentMetadataAudio"; + __typename: "ComponentMetadataGroup"; subtype: { - __typename: "AudioSubtypeEntityResponse"; + __typename: "GroupSubtypeEntityResponse"; data: { - __typename: "AudioSubtypeEntity"; + __typename: "GroupSubtypeEntity"; attributes: { - __typename: "AudioSubtype"; + __typename: "GroupSubtype"; + slug: string; + titles: Array<{ + __typename: "ComponentTranslationsSimpleTitle"; + title: string; + }>; + }; + }; + }; + subitems_type: { + __typename: "MetadataTypeEntityResponse"; + data: { + __typename: "MetadataTypeEntity"; + attributes: { + __typename: "MetadataType"; slug: string; titles: Array<{ __typename: "ComponentTranslationsSimpleTitle"; @@ -717,14 +884,15 @@ export type GetLibraryItemQuery = { }; }; } + | { __typename: "ComponentMetadataOther" } | { - __typename: "ComponentMetadataOther"; + __typename: "ComponentMetadataVideo"; subtype: { - __typename: "OtherSubtypeEntityResponse"; + __typename: "VideoSubtypeEntityResponse"; data: { - __typename: "OtherSubtypeEntity"; + __typename: "VideoSubtypeEntity"; attributes: { - __typename: "OtherSubtype"; + __typename: "VideoSubtype"; slug: string; titles: Array<{ __typename: "ComponentTranslationsSimpleTitle"; @@ -783,8 +951,8 @@ export type GetLibraryItemQuery = { } | { __typename: "ComponentRangeTimeRange"; - starting_time: any; - ending_time: any; + starting_time: unknown; + ending_time: unknown; } | { __typename: "ComponentRangeOther" } | { __typename: "Error" } @@ -807,6 +975,7 @@ export type GetLibraryItemQuery = { id: string; attributes: { __typename: "Category"; + name: string; short: string; }; }>; @@ -892,7 +1061,11 @@ export type GetContentsQuery = { data: Array<{ __typename: "CategoryEntity"; id: string; - attributes: { __typename: "Category"; short: string }; + attributes: { + __typename: "Category"; + name: string; + short: string; + }; }>; }; type: { @@ -1194,6 +1367,19 @@ export type GetContentTextQuery = { }; }>; }; + text_set_languages: Array<{ + __typename: "ComponentSetsTextSet"; + language: { + __typename: "LanguageEntityResponse"; + data: { + __typename: "LanguageEntity"; + attributes: { + __typename: "Language"; + code: string; + }; + }; + }; + }>; text_set: Array<{ __typename: "ComponentSetsTextSet"; status: Enum_Componentsetstextset_Status; @@ -1355,3 +1541,556 @@ export type GetContentTextQuery = { }>; }; }; + +export type GetCurrenciesQueryVariables = Exact<{ [key: string]: never }>; + +export type GetCurrenciesQuery = { + __typename: "Query"; + currencies: { + __typename: "CurrencyEntityResponseCollection"; + data: Array<{ + __typename: "CurrencyEntity"; + id: string; + attributes: { + __typename: "Currency"; + code: string; + symbol: string; + rate_to_usd: number; + display_decimals: boolean; + }; + }>; + }; +}; + +export type GetLanguagesQueryVariables = Exact<{ [key: string]: never }>; + +export type GetLanguagesQuery = { + __typename: "Query"; + languages: { + __typename: "LanguageEntityResponseCollection"; + data: Array<{ + __typename: "LanguageEntity"; + id: string; + attributes: { + __typename: "Language"; + name: string; + code: string; + localized_name: string; + }; + }>; + }; +}; + +export type GetPostQueryVariables = Exact<{ + slug: InputMaybe; + language_code: InputMaybe; +}>; + +export type GetPostQuery = { + __typename: "Query"; + posts: { + __typename: "PostEntityResponseCollection"; + data: Array<{ + __typename: "PostEntity"; + id: string; + attributes: { + __typename: "Post"; + slug: string; + updatedAt: any; + hidden: boolean; + date: { + __typename: "ComponentBasicsDatepicker"; + year: number; + month: number; + day: number; + }; + authors: { + __typename: "RecorderRelationResponseCollection"; + data: Array<{ + __typename: "RecorderEntity"; + id: string; + attributes: { + __typename: "Recorder"; + username: string; + anonymize: boolean; + anonymous_code: string; + pronouns: string; + bio: Array<{ + __typename: "ComponentTranslationsBio"; + bio: string; + }>; + languages: { + __typename: "LanguageRelationResponseCollection"; + data: Array<{ + __typename: "LanguageEntity"; + attributes: { __typename: "Language"; code: string }; + }>; + }; + avatar: { + __typename: "UploadFileEntityResponse"; + data: { + __typename: "UploadFileEntity"; + attributes: { + __typename: "UploadFile"; + name: string; + alternativeText: string; + caption: string; + width: number; + height: number; + url: string; + }; + }; + }; + }; + }>; + }; + categories: { + __typename: "CategoryRelationResponseCollection"; + data: Array<{ + __typename: "CategoryEntity"; + id: string; + attributes: { __typename: "Category"; name: string; short: string }; + }>; + }; + thumbnail: { + __typename: "UploadFileEntityResponse"; + data: { + __typename: "UploadFileEntity"; + attributes: { + __typename: "UploadFile"; + name: string; + alternativeText: string; + caption: string; + width: number; + height: number; + url: string; + }; + }; + }; + translations_languages: Array<{ + __typename: "ComponentTranslationsPosts"; + language: { + __typename: "LanguageEntityResponse"; + data: { + __typename: "LanguageEntity"; + attributes: { __typename: "Language"; code: string }; + }; + }; + }>; + translations: Array<{ + __typename: "ComponentTranslationsPosts"; + status: Enum_Componenttranslationsposts_Status; + title: string; + excerpt: string; + body: string; + thumbnail: { + __typename: "UploadFileEntityResponse"; + data: { + __typename: "UploadFileEntity"; + attributes: { + __typename: "UploadFile"; + name: string; + alternativeText: string; + caption: string; + width: number; + height: number; + url: string; + }; + }; + }; + }>; + }; + }>; + }; +}; + +export type GetPostsSlugsQueryVariables = Exact<{ [key: string]: never }>; + +export type GetPostsSlugsQuery = { + __typename: "Query"; + posts: { + __typename: "PostEntityResponseCollection"; + data: Array<{ + __typename: "PostEntity"; + id: string; + attributes: { __typename: "Post"; slug: string }; + }>; + }; +}; + +export type GetPostsPreviewQueryVariables = Exact<{ + language_code: InputMaybe; +}>; + +export type GetPostsPreviewQuery = { + __typename: "Query"; + posts: { + __typename: "PostEntityResponseCollection"; + data: Array<{ + __typename: "PostEntity"; + id: string; + attributes: { + __typename: "Post"; + slug: string; + date: { + __typename: "ComponentBasicsDatepicker"; + year: number; + month: number; + day: number; + }; + categories: { + __typename: "CategoryRelationResponseCollection"; + data: Array<{ + __typename: "CategoryEntity"; + id: string; + attributes: { __typename: "Category"; short: string }; + }>; + }; + thumbnail: { + __typename: "UploadFileEntityResponse"; + data: { + __typename: "UploadFileEntity"; + attributes: { + __typename: "UploadFile"; + name: string; + alternativeText: string; + caption: string; + width: number; + height: number; + url: string; + }; + }; + }; + translations: Array<{ + __typename: "ComponentTranslationsPosts"; + title: string; + excerpt: string; + thumbnail: { + __typename: "UploadFileEntityResponse"; + data: { + __typename: "UploadFileEntity"; + attributes: { + __typename: "UploadFile"; + name: string; + alternativeText: string; + caption: string; + width: number; + height: number; + url: string; + }; + }; + }; + }>; + }; + }>; + }; +}; + +export type GetPostLanguagesQueryVariables = Exact<{ + slug: InputMaybe; +}>; + +export type GetPostLanguagesQuery = { + __typename: "Query"; + posts: { + __typename: "PostEntityResponseCollection"; + data: Array<{ + __typename: "PostEntity"; + attributes: { + __typename: "Post"; + translations: Array<{ + __typename: "ComponentTranslationsPosts"; + language: { + __typename: "LanguageEntityResponse"; + data: { + __typename: "LanguageEntity"; + attributes: { __typename: "Language"; code: string }; + }; + }; + }>; + }; + }>; + }; +}; + +export type GetContentLanguagesQueryVariables = Exact<{ + slug: InputMaybe; +}>; + +export type GetContentLanguagesQuery = { + __typename: "Query"; + contents: { + __typename: "ContentEntityResponseCollection"; + data: Array<{ + __typename: "ContentEntity"; + attributes: { + __typename: "Content"; + text_set: Array<{ + __typename: "ComponentSetsTextSet"; + language: { + __typename: "LanguageEntityResponse"; + data: { + __typename: "LanguageEntity"; + attributes: { __typename: "Language"; code: string }; + }; + }; + }>; + video_set: Array<{ + __typename: "ComponentSetsVideoSet"; + language: { + __typename: "LanguageEntityResponse"; + data: { + __typename: "LanguageEntity"; + attributes: { __typename: "Language"; code: string }; + }; + }; + }>; + audio_set: Array<{ + __typename: "ComponentSetsAudioSet"; + language: { + __typename: "LanguageEntityResponse"; + data: { + __typename: "LanguageEntity"; + attributes: { __typename: "Language"; code: string }; + }; + }; + }>; + }; + }>; + }; +}; + +export type GetLibraryItemScansQueryVariables = Exact<{ + slug: InputMaybe; + language_code: InputMaybe; +}>; + +export type GetLibraryItemScansQuery = { + __typename: "Query"; + libraryItems: { + __typename: "LibraryItemEntityResponseCollection"; + data: Array<{ + __typename: "LibraryItemEntity"; + id: string; + attributes: { + __typename: "LibraryItem"; + slug: string; + title: string; + subtitle: string; + thumbnail: { + __typename: "UploadFileEntityResponse"; + data: { + __typename: "UploadFileEntity"; + attributes: { + __typename: "UploadFile"; + name: string; + alternativeText: string; + caption: string; + width: number; + height: number; + url: string; + }; + }; + }; + contents: { + __typename: "RangedContentRelationResponseCollection"; + data: Array<{ + __typename: "RangedContentEntity"; + id: string; + attributes: { + __typename: "RangedContent"; + slug: string; + range: Array< + | { + __typename: "ComponentRangePageRange"; + starting_page: number; + ending_page: number; + } + | { + __typename: "ComponentRangeTimeRange"; + starting_time: any; + ending_time: any; + } + | { __typename: "ComponentRangeOther" } + | { __typename: "Error" } + >; + scan_set_languages: Array<{ + __typename: "ComponentSetsScanSet"; + language: { + __typename: "LanguageEntityResponse"; + data: { + __typename: "LanguageEntity"; + attributes: { + __typename: "Language"; + code: string; + }; + }; + }; + }>; + scan_set: Array<{ + __typename: "ComponentSetsScanSet"; + status: Enum_Componentsetsscanset_Status; + notes: string; + source_language: { + __typename: "LanguageEntityResponse"; + data: { + __typename: "LanguageEntity"; + attributes: { + __typename: "Language"; + code: string; + }; + }; + }; + scanners: { + __typename: "RecorderRelationResponseCollection"; + data: Array<{ + __typename: "RecorderEntity"; + id: string; + attributes: { + __typename: "Recorder"; + username: string; + anonymize: boolean; + anonymous_code: string; + pronouns: string; + bio: Array<{ + __typename: "ComponentTranslationsBio"; + bio: string; + }>; + languages: { + __typename: "LanguageRelationResponseCollection"; + data: Array<{ + __typename: "LanguageEntity"; + attributes: { + __typename: "Language"; + code: string; + }; + }>; + }; + avatar: { + __typename: "UploadFileEntityResponse"; + data: { + __typename: "UploadFileEntity"; + attributes: { + __typename: "UploadFile"; + name: string; + alternativeText: string; + caption: string; + width: number; + height: number; + url: string; + }; + }; + }; + }; + }>; + }; + cleaners: { + __typename: "RecorderRelationResponseCollection"; + data: Array<{ + __typename: "RecorderEntity"; + id: string; + attributes: { + __typename: "Recorder"; + username: string; + anonymize: boolean; + anonymous_code: string; + pronouns: string; + bio: Array<{ + __typename: "ComponentTranslationsBio"; + bio: string; + }>; + languages: { + __typename: "LanguageRelationResponseCollection"; + data: Array<{ + __typename: "LanguageEntity"; + attributes: { + __typename: "Language"; + code: string; + }; + }>; + }; + avatar: { + __typename: "UploadFileEntityResponse"; + data: { + __typename: "UploadFileEntity"; + attributes: { + __typename: "UploadFile"; + name: string; + alternativeText: string; + caption: string; + width: number; + height: number; + url: string; + }; + }; + }; + }; + }>; + }; + typesetters: { + __typename: "RecorderRelationResponseCollection"; + data: Array<{ + __typename: "RecorderEntity"; + id: string; + attributes: { + __typename: "Recorder"; + username: string; + anonymize: boolean; + anonymous_code: string; + pronouns: string; + bio: Array<{ + __typename: "ComponentTranslationsBio"; + bio: string; + }>; + languages: { + __typename: "LanguageRelationResponseCollection"; + data: Array<{ + __typename: "LanguageEntity"; + attributes: { + __typename: "Language"; + code: string; + }; + }>; + }; + avatar: { + __typename: "UploadFileEntityResponse"; + data: { + __typename: "UploadFileEntity"; + attributes: { + __typename: "UploadFile"; + name: string; + alternativeText: string; + caption: string; + width: number; + height: number; + url: string; + }; + }; + }; + }; + }>; + }; + pages: { + __typename: "UploadFileRelationResponseCollection"; + data: Array<{ + __typename: "UploadFileEntity"; + id: string; + attributes: { + __typename: "UploadFile"; + name: string; + alternativeText: string; + caption: string; + width: number; + height: number; + url: string; + }; + }>; + }; + }>; + }; + }>; + }; + }; + }>; + }; +}; diff --git a/src/graphql/operations.ts b/src/graphql/operations.ts index 4245797..dab516f 100644 --- a/src/graphql/operations.ts +++ b/src/graphql/operations.ts @@ -1,5 +1,4 @@ import { readFileSync } from "fs"; - import { GetChronologyItemsQuery, GetChronologyItemsQueryVariables, @@ -11,19 +10,31 @@ import { GetContentsSlugsQueryVariables, GetContentTextQuery, GetContentTextQueryVariables, + GetCurrenciesQuery, + GetCurrenciesQueryVariables, GetErasQuery, GetErasQueryVariables, + GetLanguagesQuery, + GetLanguagesQueryVariables, GetLibraryItemQuery, GetLibraryItemQueryVariables, + GetLibraryItemScansQuery, + GetLibraryItemScansQueryVariables, GetLibraryItemsPreviewQuery, GetLibraryItemsPreviewQueryVariables, GetLibraryItemsSlugsQuery, GetLibraryItemsSlugsQueryVariables, + GetPostQuery, + GetPostQueryVariables, + GetPostsPreviewQuery, + GetPostsPreviewQueryVariables, + GetPostsSlugsQuery, + GetPostsSlugsQueryVariables, GetWebsiteInterfaceQuery, GetWebsiteInterfaceQueryVariables, } from "graphql/operations-types"; -const graphQL = async (query: string, variables?: string) => { +async function graphQL(query: string, variables?: string) { const res = await fetch(`${process.env.URL_GRAPHQL}`, { method: "POST", body: JSON.stringify({ @@ -32,11 +43,11 @@ const graphQL = async (query: string, variables?: string) => { }), headers: { "content-type": "application/json", - Authorization: "Bearer " + process.env.ACCESS_TOKEN, + Authorization: `Bearer ${process.env.ACCESS_TOKEN}`, }, }); return (await res.json()).data; -}; +} function getQueryFromOperations(queryName: string): string { const operations = readFileSync("./src/graphql/operation.graphql", "utf8"); @@ -103,7 +114,6 @@ export async function getContentsSlugs( return await graphQL(query, JSON.stringify(variables)); } - export async function getContents( variables: GetContentsQueryVariables ): Promise { @@ -124,3 +134,45 @@ export async function getContentText( const query = getQueryFromOperations("getContentText"); return await graphQL(query, JSON.stringify(variables)); } + +export async function getCurrencies( + variables: GetCurrenciesQueryVariables +): Promise { + const query = getQueryFromOperations("getCurrencies"); + return await graphQL(query, JSON.stringify(variables)); +} + +export async function getLanguages( + variables: GetLanguagesQueryVariables +): Promise { + const query = getQueryFromOperations("getLanguages"); + return await graphQL(query, JSON.stringify(variables)); +} + +export async function getPost( + variables: GetPostQueryVariables +): Promise { + const query = getQueryFromOperations("getPost"); + return await graphQL(query, JSON.stringify(variables)); +} + +export async function getPostsSlugs( + variables: GetPostsSlugsQueryVariables +): Promise { + const query = getQueryFromOperations("getPostsSlugs"); + return await graphQL(query, JSON.stringify(variables)); +} + +export async function getPostsPreview( + variables: GetPostsPreviewQueryVariables +): Promise { + const query = getQueryFromOperations("getPostsPreview"); + return await graphQL(query, JSON.stringify(variables)); +} + +export async function getLibraryItemScans( + variables: GetLibraryItemScansQueryVariables +): Promise { + const query = getQueryFromOperations("getLibraryItemScans"); + return await graphQL(query, JSON.stringify(variables)); +} diff --git a/src/graphql/schema.graphql b/src/graphql/schema.graphql index 41e523f..6a9d013 100644 --- a/src/graphql/schema.graphql +++ b/src/graphql/schema.graphql @@ -26,11 +26,6 @@ type ResponseCollectionMeta { pagination: Pagination! } -enum PublicationState { - LIVE - PREVIEW -} - input IDFilterInput { and: [ID] or: [ID] @@ -247,6 +242,24 @@ type ComponentBasicsDatepicker { day: Int } +enum ENUM_COMPONENTBASICSFILESIZE_UNIT { + kb + mb + gb +} + +input ComponentBasicsFileSizeInput { + id: ID + size: Float + unit: ENUM_COMPONENTBASICSFILESIZE_UNIT +} + +type ComponentBasicsFileSize { + id: ID! + size: Float! + unit: ENUM_COMPONENTBASICSFILESIZE_UNIT! +} + input ComponentBasicsPriceInput { id: ID amount: Float @@ -387,6 +400,8 @@ input ComponentCollectionsComponentLibraryObiBeltInput { back: ID full: ID inside_full: ID + flap_front: ID + flap_back: ID } type ComponentCollectionsComponentLibraryObiBelt { @@ -396,6 +411,8 @@ type ComponentCollectionsComponentLibraryObiBelt { back: UploadFileEntityResponse full: UploadFileEntityResponse inside_full: UploadFileEntityResponse + flap_front: UploadFileEntityResponse + flap_back: UploadFileEntityResponse } input ComponentCollectionsComponentTitlesFiltersInput { @@ -509,6 +526,12 @@ type ComponentMetadataGame { ): LanguageRelationResponseCollection } +type ComponentMetadataGroup { + id: ID! + subtype: GroupSubtypeEntityResponse + subitems_type: MetadataTypeEntityResponse +} + type ComponentMetadataMerch { id: ID! merch_item: MerchItemEntityResponse @@ -516,7 +539,6 @@ type ComponentMetadataMerch { type ComponentMetadataOther { id: ID! - subtype: OtherSubtypeEntityResponse } type ComponentMetadataVideo { @@ -1052,9 +1074,11 @@ enum ENUM_COMPONENTTRANSLATIONSPOSTS_STATUS { } input ComponentTranslationsPostsFiltersInput { - Status: StringFilterInput title: StringFilterInput excerpt: StringFilterInput + body: StringFilterInput + language: LanguageFiltersInput + status: StringFilterInput and: [ComponentTranslationsPostsFiltersInput] or: [ComponentTranslationsPostsFiltersInput] not: ComponentTranslationsPostsFiltersInput @@ -1062,18 +1086,22 @@ input ComponentTranslationsPostsFiltersInput { input ComponentTranslationsPostsInput { id: ID - Status: ENUM_COMPONENTTRANSLATIONSPOSTS_STATUS title: String excerpt: String thumbnail: ID + body: String + language: ID + status: ENUM_COMPONENTTRANSLATIONSPOSTS_STATUS } type ComponentTranslationsPosts { id: ID! - Status: ENUM_COMPONENTTRANSLATIONSPOSTS_STATUS! title: String! excerpt: String thumbnail: UploadFileEntityResponse + body: String + language: LanguageEntityResponse + status: ENUM_COMPONENTTRANSLATIONSPOSTS_STATUS! } enum ENUM_COMPONENTTRANSLATIONSSCANSET_STATUS { @@ -1258,6 +1286,29 @@ type ComponentTranslationsWeaponStory { language: LanguageEntityResponse } +input ComponentTranslationsWebArchivesFiltersInput { + language: LanguageFiltersInput + description: StringFilterInput + notes: StringFilterInput + and: [ComponentTranslationsWebArchivesFiltersInput] + or: [ComponentTranslationsWebArchivesFiltersInput] + not: ComponentTranslationsWebArchivesFiltersInput +} + +input ComponentTranslationsWebArchivesInput { + id: ID + language: ID + description: String + notes: String +} + +type ComponentTranslationsWebArchives { + id: ID! + language: LanguageEntityResponse + description: String + notes: String +} + input UploadFileFiltersInput { id: IDFilterInput name: StringFilterInput @@ -1642,6 +1693,8 @@ input CurrencyFiltersInput { id: IDFilterInput symbol: StringFilterInput code: StringFilterInput + rate_to_usd: FloatFilterInput + display_decimals: BooleanFilterInput createdAt: DateTimeFilterInput updatedAt: DateTimeFilterInput and: [CurrencyFiltersInput] @@ -1652,11 +1705,15 @@ input CurrencyFiltersInput { input CurrencyInput { symbol: String code: String + rate_to_usd: Float + display_decimals: Boolean } type Currency { symbol: String! code: String! + rate_to_usd: Float! + display_decimals: Boolean! createdAt: DateTime updatedAt: DateTime } @@ -1801,6 +1858,46 @@ type GlossaryItemTypeEntityResponseCollection { meta: ResponseCollectionMeta! } +input GroupSubtypeFiltersInput { + id: IDFilterInput + slug: StringFilterInput + createdAt: DateTimeFilterInput + updatedAt: DateTimeFilterInput + and: [GroupSubtypeFiltersInput] + or: [GroupSubtypeFiltersInput] + not: GroupSubtypeFiltersInput +} + +input GroupSubtypeInput { + slug: String + titles: [ComponentTranslationsSimpleTitleInput] +} + +type GroupSubtype { + slug: String! + titles( + filters: ComponentTranslationsSimpleTitleFiltersInput + pagination: PaginationArg = {} + sort: [String] = [] + ): [ComponentTranslationsSimpleTitle] + createdAt: DateTime + updatedAt: DateTime +} + +type GroupSubtypeEntity { + id: ID + attributes: GroupSubtype +} + +type GroupSubtypeEntityResponse { + data: GroupSubtypeEntity +} + +type GroupSubtypeEntityResponseCollection { + data: [GroupSubtypeEntity!]! + meta: ResponseCollectionMeta! +} + input LanguageFiltersInput { id: IDFilterInput name: StringFilterInput @@ -1846,11 +1943,12 @@ type LanguageRelationResponseCollection { } union LibraryItemMetadataDynamicZone = - ComponentMetadataBooks - | ComponentMetadataVideo + ComponentMetadataAudio + | ComponentMetadataBooks | ComponentMetadataGame - | ComponentMetadataAudio + | ComponentMetadataGroup | ComponentMetadataOther + | ComponentMetadataVideo | Error scalar LibraryItemMetadataDynamicZoneInput @@ -1867,6 +1965,7 @@ input LibraryItemFiltersInput { digital: BooleanFilterInput primary: BooleanFilterInput submerchs: MerchItemFiltersInput + categories: CategoryFiltersInput createdAt: DateTimeFilterInput updatedAt: DateTimeFilterInput and: [LibraryItemFiltersInput] @@ -1893,6 +1992,7 @@ input LibraryItemInput { digital: Boolean primary: Boolean submerchs: [ID] + categories: [ID] } type LibraryItem { @@ -1938,6 +2038,11 @@ type LibraryItem { pagination: PaginationArg = {} sort: [String] = [] ): MerchItemRelationResponseCollection + categories( + filters: CategoryFiltersInput + pagination: PaginationArg = {} + sort: [String] = [] + ): CategoryRelationResponseCollection createdAt: DateTime updatedAt: DateTime } @@ -2013,22 +2118,22 @@ type MerchItemRelationResponseCollection { data: [MerchItemEntity!]! } -input OtherSubtypeFiltersInput { +input MetadataTypeFiltersInput { id: IDFilterInput slug: StringFilterInput createdAt: DateTimeFilterInput updatedAt: DateTimeFilterInput - and: [OtherSubtypeFiltersInput] - or: [OtherSubtypeFiltersInput] - not: OtherSubtypeFiltersInput + and: [MetadataTypeFiltersInput] + or: [MetadataTypeFiltersInput] + not: MetadataTypeFiltersInput } -input OtherSubtypeInput { +input MetadataTypeInput { slug: String titles: [ComponentTranslationsSimpleTitleInput] } -type OtherSubtype { +type MetadataType { slug: String! titles( filters: ComponentTranslationsSimpleTitleFiltersInput @@ -2039,17 +2144,17 @@ type OtherSubtype { updatedAt: DateTime } -type OtherSubtypeEntity { +type MetadataTypeEntity { id: ID - attributes: OtherSubtype + attributes: MetadataType } -type OtherSubtypeEntityResponse { - data: OtherSubtypeEntity +type MetadataTypeEntityResponse { + data: MetadataTypeEntity } -type OtherSubtypeEntityResponseCollection { - data: [OtherSubtypeEntity!]! +type MetadataTypeEntityResponseCollection { + data: [MetadataTypeEntity!]! meta: ResponseCollectionMeta! } @@ -2058,9 +2163,9 @@ input PostFiltersInput { authors: RecorderFiltersInput slug: StringFilterInput categories: CategoryFiltersInput + hidden: BooleanFilterInput createdAt: DateTimeFilterInput updatedAt: DateTimeFilterInput - publishedAt: DateTimeFilterInput and: [PostFiltersInput] or: [PostFiltersInput] not: PostFiltersInput @@ -2071,7 +2176,9 @@ input PostInput { slug: String categories: [ID] translations: [ComponentTranslationsPostsInput] - publishedAt: DateTime + hidden: Boolean + thumbnail: ID + date: ComponentBasicsDatepickerInput } type Post { @@ -2091,9 +2198,11 @@ type Post { pagination: PaginationArg = {} sort: [String] = [] ): [ComponentTranslationsPosts] + hidden: Boolean! + thumbnail: UploadFileEntityResponse + date: ComponentBasicsDatepicker! createdAt: DateTime updatedAt: DateTime - publishedAt: DateTime } type PostEntity { @@ -2488,74 +2597,207 @@ type WeaponStoryTypeEntityResponseCollection { meta: ResponseCollectionMeta! } +enum ENUM_WEBARCHIVE_TYPE { + website + webpage + online_doc +} + +enum ENUM_WEBARCHIVE_FORMAT { + zip + wacz +} + +input WebArchiveFiltersInput { + id: IDFilterInput + type: StringFilterInput + source_url: StringFilterInput + format: StringFilterInput + num_pages: IntFilterInput + author: StringFilterInput + still_online: BooleanFilterInput + createdAt: DateTimeFilterInput + updatedAt: DateTimeFilterInput + and: [WebArchiveFiltersInput] + or: [WebArchiveFiltersInput] + not: WebArchiveFiltersInput +} + +input WebArchiveInput { + type: ENUM_WEBARCHIVE_TYPE + source_url: String + format: ENUM_WEBARCHIVE_FORMAT + date: ComponentBasicsDatepickerInput + num_pages: Int + author: String + still_online: Boolean + size: ComponentBasicsFileSizeInput + descriptions: [ComponentTranslationsWebArchivesInput] +} + +type WebArchive { + type: ENUM_WEBARCHIVE_TYPE! + source_url: String! + format: ENUM_WEBARCHIVE_FORMAT! + date: ComponentBasicsDatepicker! + num_pages: Int + author: String + still_online: Boolean! + size: ComponentBasicsFileSize! + descriptions( + filters: ComponentTranslationsWebArchivesFiltersInput + pagination: PaginationArg = {} + sort: [String] = [] + ): [ComponentTranslationsWebArchives] + createdAt: DateTime + updatedAt: DateTime +} + +type WebArchiveEntity { + id: ID + attributes: WebArchive +} + +type WebArchiveEntityResponse { + data: WebArchiveEntity +} + +type WebArchiveEntityResponseCollection { + data: [WebArchiveEntity!]! + meta: ResponseCollectionMeta! +} + input WebsiteInterfaceFiltersInput { id: IDFilterInput - language: LanguageFiltersInput - main_library: StringFilterInput - main_library_description: StringFilterInput - main_news: StringFilterInput - main_merch: StringFilterInput - main_gallery: StringFilterInput - main_archives: StringFilterInput - main_about_us: StringFilterInput - main_licensing: StringFilterInput - main_copyright: StringFilterInput + library: StringFilterInput + contents: StringFilterInput + wiki: StringFilterInput + chronicles: StringFilterInput + library_short_description: StringFilterInput + contents_short_description: StringFilterInput + wiki_short_description: StringFilterInput + chronicles_short_description: StringFilterInput + news: StringFilterInput + merch: StringFilterInput + gallery: StringFilterInput + archives: StringFilterInput + about_us: StringFilterInput + licensing_notice: StringFilterInput + copyright_notice: StringFilterInput + contents_description: StringFilterInput + type: StringFilterInput + category: StringFilterInput + categories: StringFilterInput + size: StringFilterInput + release_date: StringFilterInput + release_year: StringFilterInput + details: StringFilterInput + price: StringFilterInput + width: StringFilterInput + height: StringFilterInput + thickness: StringFilterInput + subitem: StringFilterInput + subitems: StringFilterInput + subitem_of: StringFilterInput + variant: StringFilterInput + variants: StringFilterInput + variant_of: StringFilterInput + summary: StringFilterInput + audio: StringFilterInput + video: StringFilterInput + textual: StringFilterInput + game: StringFilterInput + other: StringFilterInput + return_to: StringFilterInput + left_to_right: StringFilterInput + right_to_left: StringFilterInput + page: StringFilterInput + pages: StringFilterInput + page_order: StringFilterInput + binding: StringFilterInput + type_information: StringFilterInput + front_matter: StringFilterInput + back_matter: StringFilterInput + open_content: StringFilterInput + read_content: StringFilterInput + watch_content: StringFilterInput + listen_content: StringFilterInput + view_scans: StringFilterInput + paperback: StringFilterInput + hardcover: StringFilterInput + ui_language: LanguageFiltersInput + languages: StringFilterInput + select_language: StringFilterInput + language: StringFilterInput library_description: StringFilterInput - library_item_summary: StringFilterInput - library_item_gallery: StringFilterInput - library_item_details: StringFilterInput - library_item_subitems: StringFilterInput - library_item_variants: StringFilterInput - library_item_content: StringFilterInput - global_return_label: StringFilterInput - global_subitem_of: StringFilterInput - global_type: StringFilterInput - global_width: StringFilterInput - global_height: StringFilterInput - global_thickness: StringFilterInput - global_binding: StringFilterInput - global_language: StringFilterInput - global_languages: StringFilterInput - global_page: StringFilterInput - global_pages: StringFilterInput - global_page_order: StringFilterInput - global_release_date: StringFilterInput - global_price: StringFilterInput - library_item_physical_size: StringFilterInput - library_item_type_information: StringFilterInput - library_item_front_matter: StringFilterInput - library_item_back_matter: StringFilterInput - library_item_type_textual: StringFilterInput - library_item_type_audio: StringFilterInput - library_item_type_game: StringFilterInput - library_item_type_video: StringFilterInput - library_item_type_other: StringFilterInput - library_item_open_content: StringFilterInput - library_item_view_scans: StringFilterInput - content_read_content: StringFilterInput - content_watch_content: StringFilterInput - content_listen_content: StringFilterInput - global_category: StringFilterInput - global_categories: StringFilterInput - global_paperback: StringFilterInput - global_hardcover: StringFilterInput - global_left_to_right: StringFilterInput - global_right_to_left: StringFilterInput - main_wiki: StringFilterInput - main_wiki_description: StringFilterInput - main_chronicles: StringFilterInput - main_chronicles_description: StringFilterInput - library_items: StringFilterInput - library_items_description: StringFilterInput - library_content: StringFilterInput - library_content_description: StringFilterInput wiki_description: StringFilterInput - news_description: StringFilterInput chronicles_description: StringFilterInput + news_description: StringFilterInput + merch_description: StringFilterInput gallery_description: StringFilterInput archives_description: StringFilterInput about_us_description: StringFilterInput - merch_description: StringFilterInput + page_not_found: StringFilterInput + default_description: StringFilterInput + name: StringFilterInput + show_subitems: StringFilterInput + show_primary_items: StringFilterInput + show_secondary_items: StringFilterInput + no_type: StringFilterInput + no_year: StringFilterInput + order_by: StringFilterInput + group_by: StringFilterInput + select_option_sidebar: StringFilterInput + group: StringFilterInput + settings: StringFilterInput + theme: StringFilterInput + light: StringFilterInput + auto: StringFilterInput + dark: StringFilterInput + font_size: StringFilterInput + player_name: StringFilterInput + currency: StringFilterInput + font: StringFilterInput + calculated: StringFilterInput + status_incomplete: StringFilterInput + status_draft: StringFilterInput + status_review: StringFilterInput + status_done: StringFilterInput + incomplete: StringFilterInput + draft: StringFilterInput + review: StringFilterInput + done: StringFilterInput + status: StringFilterInput + transcribers: StringFilterInput + translators: StringFilterInput + proofreaders: StringFilterInput + transcript_notice: StringFilterInput + translation_notice: StringFilterInput + source_language: StringFilterInput + pronouns: StringFilterInput + no_category: StringFilterInput + item: StringFilterInput + items: StringFilterInput + content: StringFilterInput + result: StringFilterInput + results: StringFilterInput + language_switch_message: StringFilterInput + open_settings: StringFilterInput + change_language: StringFilterInput + open_search: StringFilterInput + chronology: StringFilterInput + accords_handbook: StringFilterInput + legality: StringFilterInput + members: StringFilterInput + sharing_policy: StringFilterInput + contact_us: StringFilterInput + email: StringFilterInput + email_gdpr_notice: StringFilterInput + message: StringFilterInput + send: StringFilterInput + response_invalid_code: StringFilterInput + response_invalid_email: StringFilterInput + response_email_success: StringFilterInput createdAt: DateTimeFilterInput updatedAt: DateTimeFilterInput and: [WebsiteInterfaceFiltersInput] @@ -2564,141 +2806,267 @@ input WebsiteInterfaceFiltersInput { } input WebsiteInterfaceInput { - language: ID - main_library: String - main_library_description: String - main_news: String - main_merch: String - main_gallery: String - main_archives: String - main_about_us: String - main_licensing: String - main_copyright: String + library: String + contents: String + wiki: String + chronicles: String + library_short_description: String + contents_short_description: String + wiki_short_description: String + chronicles_short_description: String + news: String + merch: String + gallery: String + archives: String + about_us: String + licensing_notice: String + copyright_notice: String + contents_description: String + type: String + category: String + categories: String + size: String + release_date: String + release_year: String + details: String + price: String + width: String + height: String + thickness: String + subitem: String + subitems: String + subitem_of: String + variant: String + variants: String + variant_of: String + summary: String + audio: String + video: String + textual: String + game: String + other: String + return_to: String + left_to_right: String + right_to_left: String + page: String + pages: String + page_order: String + binding: String + type_information: String + front_matter: String + back_matter: String + open_content: String + read_content: String + watch_content: String + listen_content: String + view_scans: String + paperback: String + hardcover: String + ui_language: ID + languages: String + select_language: String + language: String library_description: String - library_item_summary: String - library_item_gallery: String - library_item_details: String - library_item_subitems: String - library_item_variants: String - library_item_content: String - global_return_label: String - global_subitem_of: String - global_type: String - global_width: String - global_height: String - global_thickness: String - global_binding: String - global_language: String - global_languages: String - global_page: String - global_pages: String - global_page_order: String - global_release_date: String - global_price: String - library_item_physical_size: String - library_item_type_information: String - library_item_front_matter: String - library_item_back_matter: String - library_item_type_textual: String - library_item_type_audio: String - library_item_type_game: String - library_item_type_video: String - library_item_type_other: String - library_item_open_content: String - library_item_view_scans: String - content_read_content: String - content_watch_content: String - content_listen_content: String - global_category: String - global_categories: String - global_paperback: String - global_hardcover: String - global_left_to_right: String - global_right_to_left: String - main_wiki: String - main_wiki_description: String - main_chronicles: String - main_chronicles_description: String - library_items: String - library_items_description: String - library_content: String - library_content_description: String wiki_description: String - news_description: String chronicles_description: String + news_description: String + merch_description: String gallery_description: String archives_description: String about_us_description: String - merch_description: String + page_not_found: String + default_description: String + name: String + show_subitems: String + show_primary_items: String + show_secondary_items: String + no_type: String + no_year: String + order_by: String + group_by: String + select_option_sidebar: String + group: String + settings: String + theme: String + light: String + auto: String + dark: String + font_size: String + player_name: String + currency: String + font: String + calculated: String + status_incomplete: String + status_draft: String + status_review: String + status_done: String + incomplete: String + draft: String + review: String + done: String + status: String + transcribers: String + translators: String + proofreaders: String + transcript_notice: String + translation_notice: String + source_language: String + pronouns: String + no_category: String + item: String + items: String + content: String + result: String + results: String + language_switch_message: String + open_settings: String + change_language: String + open_search: String + chronology: String + accords_handbook: String + legality: String + members: String + sharing_policy: String + contact_us: String + email: String + email_gdpr_notice: String + message: String + send: String + response_invalid_code: String + response_invalid_email: String + response_email_success: String } type WebsiteInterface { - language: LanguageEntityResponse - main_library: String - main_library_description: String - main_news: String - main_merch: String - main_gallery: String - main_archives: String - main_about_us: String - main_licensing: String - main_copyright: String + library: String + contents: String + wiki: String + chronicles: String + library_short_description: String + contents_short_description: String + wiki_short_description: String + chronicles_short_description: String + news: String + merch: String + gallery: String + archives: String + about_us: String + licensing_notice: String + copyright_notice: String + contents_description: String + type: String + category: String + categories: String + size: String + release_date: String + release_year: String + details: String + price: String + width: String + height: String + thickness: String + subitem: String + subitems: String + subitem_of: String + variant: String + variants: String + variant_of: String + summary: String + audio: String + video: String + textual: String + game: String + other: String + return_to: String + left_to_right: String + right_to_left: String + page: String + pages: String + page_order: String + binding: String + type_information: String + front_matter: String + back_matter: String + open_content: String + read_content: String + watch_content: String + listen_content: String + view_scans: String + paperback: String + hardcover: String + ui_language: LanguageEntityResponse + languages: String + select_language: String + language: String library_description: String - library_item_summary: String - library_item_gallery: String - library_item_details: String - library_item_subitems: String - library_item_variants: String - library_item_content: String - global_return_label: String - global_subitem_of: String - global_type: String - global_width: String - global_height: String - global_thickness: String - global_binding: String - global_language: String - global_languages: String - global_page: String - global_pages: String - global_page_order: String - global_release_date: String - global_price: String - library_item_physical_size: String - library_item_type_information: String - library_item_front_matter: String - library_item_back_matter: String - library_item_type_textual: String - library_item_type_audio: String - library_item_type_game: String - library_item_type_video: String - library_item_type_other: String - library_item_open_content: String - library_item_view_scans: String - content_read_content: String - content_watch_content: String - content_listen_content: String - global_category: String - global_categories: String - global_paperback: String - global_hardcover: String - global_left_to_right: String - global_right_to_left: String - main_wiki: String - main_wiki_description: String - main_chronicles: String - main_chronicles_description: String - library_items: String - library_items_description: String - library_content: String - library_content_description: String wiki_description: String - news_description: String chronicles_description: String + news_description: String + merch_description: String gallery_description: String archives_description: String about_us_description: String - merch_description: String + page_not_found: String + default_description: String + name: String + show_subitems: String + show_primary_items: String + show_secondary_items: String + no_type: String + no_year: String + order_by: String + group_by: String + select_option_sidebar: String + group: String + settings: String + theme: String + light: String + auto: String + dark: String + font_size: String + player_name: String + currency: String + font: String + calculated: String + status_incomplete: String + status_draft: String + status_review: String + status_done: String + incomplete: String + draft: String + review: String + done: String + status: String + transcribers: String + translators: String + proofreaders: String + transcript_notice: String + translation_notice: String + source_language: String + pronouns: String + no_category: String + item: String + items: String + content: String + result: String + results: String + language_switch_message: String + open_settings: String + change_language: String + open_search: String + chronology: String + accords_handbook: String + legality: String + members: String + sharing_policy: String + contact_us: String + email: String + email_gdpr_notice: String + message: String + send: String + response_invalid_code: String + response_invalid_email: String + response_email_success: String createdAt: DateTime updatedAt: DateTime } @@ -2805,6 +3173,7 @@ type WikiPageTypeEntityResponseCollection { union GenericMorph = ComponentBasicsCredits | ComponentBasicsDatepicker + | ComponentBasicsFileSize | ComponentBasicsPrice | ComponentBasicsSize | ComponentCollectionsComponentEvent @@ -2819,6 +3188,7 @@ union GenericMorph = | ComponentMetadataAudio | ComponentMetadataBooks | ComponentMetadataGame + | ComponentMetadataGroup | ComponentMetadataMerch | ComponentMetadataOther | ComponentMetadataVideo @@ -2855,6 +3225,7 @@ union GenericMorph = | ComponentTranslationsWeaponStoryStory | ComponentTranslationsWeaponStoryType | ComponentTranslationsWeaponStory + | ComponentTranslationsWebArchives | UploadFile | AudioSubtype | Category @@ -2866,10 +3237,11 @@ union GenericMorph = | GamePlatform | GlossaryItem | GlossaryItemType + | GroupSubtype | Language | LibraryItem | MerchItem - | OtherSubtype + | MetadataType | Post | RangedContent | Recorder @@ -2879,6 +3251,7 @@ union GenericMorph = | WeaponStory | WeaponStoryGroup | WeaponStoryType + | WebArchive | WebsiteInterface | WikiPage | WikiPageType @@ -2963,6 +3336,12 @@ type Query { pagination: PaginationArg = {} sort: [String] = [] ): GlossaryItemTypeEntityResponseCollection + groupSubtype(id: ID): GroupSubtypeEntityResponse + groupSubtypes( + filters: GroupSubtypeFiltersInput + pagination: PaginationArg = {} + sort: [String] = [] + ): GroupSubtypeEntityResponseCollection language(id: ID): LanguageEntityResponse languages( filters: LanguageFiltersInput @@ -2981,18 +3360,17 @@ type Query { pagination: PaginationArg = {} sort: [String] = [] ): MerchItemEntityResponseCollection - otherSubtype(id: ID): OtherSubtypeEntityResponse - otherSubtypes( - filters: OtherSubtypeFiltersInput + metadataType(id: ID): MetadataTypeEntityResponse + metadataTypes( + filters: MetadataTypeFiltersInput pagination: PaginationArg = {} sort: [String] = [] - ): OtherSubtypeEntityResponseCollection + ): MetadataTypeEntityResponseCollection post(id: ID): PostEntityResponse posts( filters: PostFiltersInput pagination: PaginationArg = {} sort: [String] = [] - publicationState: PublicationState = LIVE ): PostEntityResponseCollection rangedContent(id: ID): RangedContentEntityResponse rangedContents( @@ -3042,6 +3420,12 @@ type Query { pagination: PaginationArg = {} sort: [String] = [] ): WeaponStoryTypeEntityResponseCollection + webArchive(id: ID): WebArchiveEntityResponse + webArchives( + filters: WebArchiveFiltersInput + pagination: PaginationArg = {} + sort: [String] = [] + ): WebArchiveEntityResponseCollection websiteInterface(id: ID): WebsiteInterfaceEntityResponse websiteInterfaces( filters: WebsiteInterfaceFiltersInput @@ -3116,6 +3500,12 @@ type Mutation { data: GlossaryItemTypeInput! ): GlossaryItemTypeEntityResponse deleteGlossaryItemType(id: ID!): GlossaryItemTypeEntityResponse + createGroupSubtype(data: GroupSubtypeInput!): GroupSubtypeEntityResponse + updateGroupSubtype( + id: ID! + data: GroupSubtypeInput! + ): GroupSubtypeEntityResponse + deleteGroupSubtype(id: ID!): GroupSubtypeEntityResponse createLanguage(data: LanguageInput!): LanguageEntityResponse updateLanguage(id: ID!, data: LanguageInput!): LanguageEntityResponse deleteLanguage(id: ID!): LanguageEntityResponse @@ -3125,12 +3515,12 @@ type Mutation { createMerchItem(data: MerchItemInput!): MerchItemEntityResponse updateMerchItem(id: ID!, data: MerchItemInput!): MerchItemEntityResponse deleteMerchItem(id: ID!): MerchItemEntityResponse - createOtherSubtype(data: OtherSubtypeInput!): OtherSubtypeEntityResponse - updateOtherSubtype( + createMetadataType(data: MetadataTypeInput!): MetadataTypeEntityResponse + updateMetadataType( id: ID! - data: OtherSubtypeInput! - ): OtherSubtypeEntityResponse - deleteOtherSubtype(id: ID!): OtherSubtypeEntityResponse + data: MetadataTypeInput! + ): MetadataTypeEntityResponse + deleteMetadataType(id: ID!): MetadataTypeEntityResponse createPost(data: PostInput!): PostEntityResponse updatePost(id: ID!, data: PostInput!): PostEntityResponse deletePost(id: ID!): PostEntityResponse @@ -3177,6 +3567,9 @@ type Mutation { data: WeaponStoryTypeInput! ): WeaponStoryTypeEntityResponse deleteWeaponStoryType(id: ID!): WeaponStoryTypeEntityResponse + createWebArchive(data: WebArchiveInput!): WebArchiveEntityResponse + updateWebArchive(id: ID!, data: WebArchiveInput!): WebArchiveEntityResponse + deleteWebArchive(id: ID!): WebArchiveEntityResponse createWebsiteInterface( data: WebsiteInterfaceInput! ): WebsiteInterfaceEntityResponse diff --git a/src/hooks/useDarkMode.ts b/src/hooks/useDarkMode.ts index 94a7c19..4705942 100644 --- a/src/hooks/useDarkMode.ts +++ b/src/hooks/useDarkMode.ts @@ -6,6 +6,7 @@ export default function useDarkMode( key: string, initialValue: boolean | undefined ): [ + boolean | undefined, boolean | undefined, React.Dispatch>, React.Dispatch> @@ -23,5 +24,5 @@ export default function useDarkMode( if (selectedThemeMode === false) setDarkMode(prefersDarkMode); }, [selectedThemeMode, prefersDarkMode, setDarkMode]); - return [darkMode, setDarkMode, setSelectedThemeMode]; + return [darkMode, selectedThemeMode, setDarkMode, setSelectedThemeMode]; } diff --git a/src/hooks/useMediaQuery.ts b/src/hooks/useMediaQuery.ts index bbd8cad..00c90c6 100644 --- a/src/hooks/useMediaQuery.ts +++ b/src/hooks/useMediaQuery.ts @@ -1,13 +1,13 @@ import { useEffect, useState } from "react"; export default function useMediaQuery(query: string): boolean { - const getMatches = (query: string): boolean => { + function getMatches(query: string): boolean { // Prevents SSR issues if (typeof window !== "undefined") { return window.matchMedia(query).matches; } return false; - }; + } const [matches, setMatches] = useState(getMatches(query)); diff --git a/src/pages/404.tsx b/src/pages/404.tsx index fd9217f..54f4e51 100644 --- a/src/pages/404.tsx +++ b/src/pages/404.tsx @@ -1,37 +1,36 @@ -import Link from "next/link"; -import ContentPanel from "components/Panels/ContentPanel"; -import { getWebsiteInterface } from "graphql/operations"; -import { GetStaticProps } from "next"; -import { GetWebsiteInterfaceQuery } from "graphql/operations-types"; import AppLayout from "components/AppLayout"; +import ReturnButton, { + ReturnButtonType, +} from "components/PanelComponents/ReturnButton"; +import ContentPanel from "components/Panels/ContentPanel"; +import { GetStaticPropsContext } from "next"; +import { AppStaticProps, getAppStaticProps } from "queries/getAppStaticProps"; -type FourOhFourProps = { - langui: GetWebsiteInterfaceQuery; -}; +interface FourOhFourProps extends AppStaticProps {} export default function FourOhFour(props: FourOhFourProps): JSX.Element { - const langui = props.langui.websiteInterfaces.data[0].attributes; + const { langui } = props; const contentPanel = ( -

    404 - Page Not Found

    - - Go back home - +

    404 - {langui.page_not_found}

    +
    ); - return ; + return ; } -export const getStaticProps: GetStaticProps = async (context) => { - if (context.locale) { - const props: FourOhFourProps = { - langui: await getWebsiteInterface({ - language_code: context.locale, - }), - }; - return { - props: props, - }; - } - return { props: {} }; -}; +export async function getStaticProps( + context: GetStaticPropsContext +): Promise<{ props: FourOhFourProps }> { + const props: FourOhFourProps = { + ...(await getAppStaticProps(context)), + }; + return { + props: props, + }; +} diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index ac12d3d..99bbb20 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -1,15 +1,17 @@ +import "@fontsource/material-icons"; +import "@fontsource/opendyslexic/400.css"; +import "@fontsource/opendyslexic/700.css"; +import "@fontsource/vollkorn/700.css"; +import "@fontsource/zen-maru-gothic/500.css"; +import "@fontsource/zen-maru-gothic/900.css"; +import { AppContextProvider } from "contexts/AppLayoutContext"; import type { AppProps } from "next/app"; import "tailwind.css"; -import "@fontsource/zen-maru-gothic/500.css"; -import "@fontsource/vollkorn/700.css"; -import "@fontsource/material-icons"; -import { AppContextProvider } from "contexts/AppLayoutContext"; - -export default function AccordsLibraryApp(appProps: AppProps) { +export default function AccordsLibraryApp(props: AppProps): JSX.Element { return ( - + ); } diff --git a/src/pages/_document.tsx b/src/pages/_document.tsx index 506d91b..4c21e31 100644 --- a/src/pages/_document.tsx +++ b/src/pages/_document.tsx @@ -1,18 +1,19 @@ import Document, { - Html, + DocumentContext, Head, + Html, Main, NextScript, - DocumentContext, } from "next/document"; class MyDocument extends Document { + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types static async getInitialProps(ctx: DocumentContext) { const initialProps = await Document.getInitialProps(ctx); return { ...initialProps }; } - render() { + render(): JSX.Element { return ( diff --git a/src/pages/about-us/accords-handbook.tsx b/src/pages/about-us/accords-handbook.tsx new file mode 100644 index 0000000..4c7ab4e --- /dev/null +++ b/src/pages/about-us/accords-handbook.tsx @@ -0,0 +1,97 @@ +import AppLayout from "components/AppLayout"; +import LanguageSwitcher from "components/LanguageSwitcher"; +import Markdawn from "components/Markdown/Markdawn"; +import TOC from "components/Markdown/TOC"; +import ReturnButton, { + ReturnButtonType, +} from "components/PanelComponents/ReturnButton"; +import ContentPanel from "components/Panels/ContentPanel"; +import SubPanel from "components/Panels/SubPanel"; +import { getPost } from "graphql/operations"; +import { GetPostQuery } from "graphql/operations-types"; +import { GetStaticPropsContext } from "next"; +import { useRouter } from "next/router"; +import { AppStaticProps, getAppStaticProps } from "queries/getAppStaticProps"; +import { getLocalesFromLanguages, prettySlug } from "queries/helpers"; + +interface AccordsHandbookProps extends AppStaticProps { + post: GetPostQuery["posts"]["data"][number]["attributes"]; +} + +export default function AccordsHandbook( + props: AccordsHandbookProps +): JSX.Element { + const { langui, post } = props; + const router = useRouter(); + const locales = getLocalesFromLanguages(post.translations_languages); + + const subPanel = ( + + + {post.translations.length > 0 && post.translations[0].body && ( + + )} + + ); + + const contentPanel = ( + + + {locales.includes(router.locale ?? "en") ? ( + + ) : ( + + )} + + ); + + return ( + 0 + ? post.translations[0].title + : prettySlug(post.slug) + } + subPanel={subPanel} + contentPanel={contentPanel} + {...props} + /> + ); +} + +export async function getStaticProps( + context: GetStaticPropsContext +): Promise<{ props: AccordsHandbookProps }> { + const slug = "accords-handbook"; + const props: AccordsHandbookProps = { + ...(await getAppStaticProps(context)), + post: ( + await getPost({ + slug: slug, + language_code: context.locale ?? "en", + }) + ).posts.data[0].attributes, + }; + return { + props: props, + }; +} diff --git a/src/pages/about-us/contact.tsx b/src/pages/about-us/contact.tsx new file mode 100644 index 0000000..a7a6d52 --- /dev/null +++ b/src/pages/about-us/contact.tsx @@ -0,0 +1,241 @@ +import AppLayout from "components/AppLayout"; +import InsetBox from "components/InsetBox"; +import LanguageSwitcher from "components/LanguageSwitcher"; +import Markdawn from "components/Markdown/Markdawn"; +import TOC from "components/Markdown/TOC"; +import ReturnButton, { + ReturnButtonType, +} from "components/PanelComponents/ReturnButton"; +import ContentPanel from "components/Panels/ContentPanel"; +import SubPanel from "components/Panels/SubPanel"; +import { getPost } from "graphql/operations"; +import { GetPostQuery } from "graphql/operations-types"; +import { GetStaticPropsContext } from "next"; +import { useRouter } from "next/router"; +import { RequestMailProps, ResponseMailProps } from "pages/api/mail"; +import { AppStaticProps, getAppStaticProps } from "queries/getAppStaticProps"; +import { getLocalesFromLanguages, randomInt } from "queries/helpers"; +import { useState } from "react"; + +interface ContactProps extends AppStaticProps { + post: GetPostQuery["posts"]["data"][number]["attributes"]; +} + +export default function AboutUs(props: ContactProps): JSX.Element { + const { langui, post } = props; + const router = useRouter(); + const [formResponse, setFormResponse] = useState(""); + const [formState, setFormState] = useState<"completed" | "ongoing" | "stale">( + "stale" + ); + const locales = getLocalesFromLanguages(post.translations_languages); + + const [randomNumber1, setRandomNumber1] = useState(randomInt(0, 10)); + const [randomNumber2, setRandomNumber2] = useState(randomInt(0, 10)); + + const subPanel = ( + + + {post.translations.length > 0 && post.translations[0].body && ( + + )} + + ); + + const contentPanel = ( + + + {locales.includes(router.locale ?? "en") ? ( + + ) : ( + + )} + +
    +
    { + event.preventDefault(); + + const fields = event.target as unknown as { + verif: HTMLInputElement; + name: HTMLInputElement; + email: HTMLInputElement; + message: HTMLInputElement; + }; + + setFormState("ongoing"); + + if ( + parseInt(fields.verif.value, 10) === + randomNumber1 + randomNumber2 && + formState !== "completed" + ) { + const content: RequestMailProps = { + name: fields.name.value, + email: fields.email.value, + message: fields.message.value, + formName: "Contact Form", + }; + fetch("/api/mail", { + method: "POST", + body: JSON.stringify(content), + headers: { + "Content-type": "application/json; charset=UTF-8", + }, + }) + .then(async (responseJson) => responseJson.json()) + .then((response: ResponseMailProps) => { + switch (response.code) { + case "OKAY": + setFormResponse(langui.response_email_success); + setFormState("completed"); + + break; + + case "EENVELOPE": + setFormResponse(langui.response_invalid_email); + setFormState("stale"); + break; + + default: + setFormResponse(response.message ?? ""); + setFormState("stale"); + break; + } + }); + } else { + setFormResponse(langui.response_invalid_code); + setFormState("stale"); + setRandomNumber1(randomInt(0, 10)); + setRandomNumber2(randomInt(0, 10)); + } + + router.replace("#send-response"); + fields.verif.value = ""; + }} + > +
    + + +
    + +
    + + +

    + {langui.email_gdpr_notice} +

    +
    + +
    + +