Much changes
This commit is contained in:
parent
9b6e68113c
commit
d9f84f59a8
|
@ -1,4 +1,13 @@
|
||||||
MONGODB_URI=mongodb://mongo:27017/payload
|
MONGODB_URI=mongodb://mongo:27017/payload
|
||||||
MONGODB_PORT=27017
|
MONGODB_PORT=27017
|
||||||
|
|
||||||
|
PAYLOAD_URI=https://payload.domain.com
|
||||||
PAYLOAD_SECRET=payloadsecreta5e6ea45ef4e66eaa151612bdcb599df
|
PAYLOAD_SECRET=payloadsecreta5e6ea45ef4e66eaa151612bdcb599df
|
||||||
PAYLOAD_PORT=3000
|
PAYLOAD_PORT=3000
|
||||||
|
|
||||||
|
STRAPI_URI=https://strapi.domain.com
|
||||||
|
STRAPI_TOKEN=strapisecreta5e6ea45ef4e66eaa151612bdcb599df
|
||||||
|
|
||||||
|
SEEDING_ADMIN_USERNAME=admin_name
|
||||||
|
SEEDING_ADMIN_EMAIL=email@domain.com
|
||||||
|
SEEDING_ADMIN_PASSWORD=somepassword
|
|
@ -2,4 +2,8 @@
|
||||||
"css.lint.unknownAtRules": "ignore",
|
"css.lint.unknownAtRules": "ignore",
|
||||||
"editor.rulers": [100],
|
"editor.rulers": [100],
|
||||||
"typescript.preferences.importModuleSpecifier": "non-relative",
|
"typescript.preferences.importModuleSpecifier": "non-relative",
|
||||||
|
"editor.codeActionsOnSave": {
|
||||||
|
"source.fixAll": true,
|
||||||
|
"source.organizeImports": true
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -9,24 +9,29 @@
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fontsource/vollkorn": "^5.0.5",
|
"@fontsource/vollkorn": "^5.0.8",
|
||||||
"clean-deep": "^3.4.0",
|
"clean-deep": "^3.4.0",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"dotenv": "^16.3.1",
|
"dotenv": "^16.3.1",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"iso-639-1": "^2.1.15",
|
"language-tags": "^1.0.8",
|
||||||
"payload": "^1.11.7",
|
"luxon": "^3.4.0",
|
||||||
|
"payload": "^1.13.3",
|
||||||
|
"qs": "^6.11.2",
|
||||||
"slugify": "^1.6.6",
|
"slugify": "^1.6.6",
|
||||||
"styled-components": "^6.0.5",
|
"styled-components": "^6.0.7",
|
||||||
"unset-value": "^2.0.1"
|
"unset-value": "^2.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/dotenv": "^8.2.0",
|
"@types/dotenv": "^8.2.0",
|
||||||
"@types/express": "^4.17.17",
|
"@types/express": "^4.17.17",
|
||||||
|
"@types/language-tags": "^1.0.1",
|
||||||
|
"@types/luxon": "^3.3.1",
|
||||||
|
"@types/qs": "^6.9.7",
|
||||||
"@types/react-router-dom": "^5.3.3",
|
"@types/react-router-dom": "^5.3.3",
|
||||||
"copyfiles": "^2.4.1",
|
"copyfiles": "^2.4.1",
|
||||||
"nodemon": "^3.0.1",
|
"nodemon": "^3.0.1",
|
||||||
"prettier": "^3.0.0",
|
"prettier": "^3.0.1",
|
||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
"typescript": "^5.1.6"
|
"typescript": "^5.1.6"
|
||||||
}
|
}
|
||||||
|
@ -3687,9 +3692,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@fontsource/vollkorn": {
|
"node_modules/@fontsource/vollkorn": {
|
||||||
"version": "5.0.5",
|
"version": "5.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/@fontsource/vollkorn/-/vollkorn-5.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/@fontsource/vollkorn/-/vollkorn-5.0.8.tgz",
|
||||||
"integrity": "sha512-8YQT9VLc6z0rxuyrDRLrcQnYFJ8eZOOKs4Oat7HUMHYAwRe20XK1khUkiTLb5jGXD4XDfAwFW9sV21SJoyUhHQ=="
|
"integrity": "sha512-QSPfmwObfsqSNMJlDWeixrycNiOtTh8VrCP1khT1u3wUhESHAgj+FVcyB9IdWj9Z1jTWc1fZ9atPunSovze0YA=="
|
||||||
},
|
},
|
||||||
"node_modules/@hapi/hoek": {
|
"node_modules/@hapi/hoek": {
|
||||||
"version": "9.3.0",
|
"version": "9.3.0",
|
||||||
|
@ -4375,9 +4380,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@swc/core": {
|
"node_modules/@swc/core": {
|
||||||
"version": "1.3.68",
|
"version": "1.3.75",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.68.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.75.tgz",
|
||||||
"integrity": "sha512-njGQuJO+Wy06dEayt70cf0c/KI3HGjm4iW9LLViVLBuYNzJ4SSdNfzejludzufu6im+dsDJ0i3QjgWhAIcVHMQ==",
|
"integrity": "sha512-YLqd5oZVnaOq/OzkjRSsJUQqAfKYiD0fzUyVUPVlNNCoQEfVfSMcXH80hLmYe9aDH0T/a7qEMjWyIr/0kWqy1A==",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
|
@ -4387,16 +4392,16 @@
|
||||||
"url": "https://opencollective.com/swc"
|
"url": "https://opencollective.com/swc"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@swc/core-darwin-arm64": "1.3.68",
|
"@swc/core-darwin-arm64": "1.3.75",
|
||||||
"@swc/core-darwin-x64": "1.3.68",
|
"@swc/core-darwin-x64": "1.3.75",
|
||||||
"@swc/core-linux-arm-gnueabihf": "1.3.68",
|
"@swc/core-linux-arm-gnueabihf": "1.3.75",
|
||||||
"@swc/core-linux-arm64-gnu": "1.3.68",
|
"@swc/core-linux-arm64-gnu": "1.3.75",
|
||||||
"@swc/core-linux-arm64-musl": "1.3.68",
|
"@swc/core-linux-arm64-musl": "1.3.75",
|
||||||
"@swc/core-linux-x64-gnu": "1.3.68",
|
"@swc/core-linux-x64-gnu": "1.3.75",
|
||||||
"@swc/core-linux-x64-musl": "1.3.68",
|
"@swc/core-linux-x64-musl": "1.3.75",
|
||||||
"@swc/core-win32-arm64-msvc": "1.3.68",
|
"@swc/core-win32-arm64-msvc": "1.3.75",
|
||||||
"@swc/core-win32-ia32-msvc": "1.3.68",
|
"@swc/core-win32-ia32-msvc": "1.3.75",
|
||||||
"@swc/core-win32-x64-msvc": "1.3.68"
|
"@swc/core-win32-x64-msvc": "1.3.75"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@swc/helpers": "^0.5.0"
|
"@swc/helpers": "^0.5.0"
|
||||||
|
@ -4408,9 +4413,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@swc/core-darwin-arm64": {
|
"node_modules/@swc/core-darwin-arm64": {
|
||||||
"version": "1.3.68",
|
"version": "1.3.75",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.68.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.75.tgz",
|
||||||
"integrity": "sha512-Z5pNxeuP2NxpOHTzDQkJs0wAPLnTlglZnR3WjObijwvdwT/kw1Y5EPDKM/BVSIeG40SPMkDLBbI0aj0qyXzrBA==",
|
"integrity": "sha512-anDnx9L465lGbjB2mvcV54NGHW6illr0IDvVV7JmkabYUVneaRdQvTr0tbHv3xjHnjrK1wuwVOHKV0LcQF2tnQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
|
@ -4423,9 +4428,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@swc/core-darwin-x64": {
|
"node_modules/@swc/core-darwin-x64": {
|
||||||
"version": "1.3.68",
|
"version": "1.3.75",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.68.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.75.tgz",
|
||||||
"integrity": "sha512-ZHl42g6yXhfX4PzAQ0BNvBXpt/OcbAHfubWRN6eXELK3fiNnxL7QBW1if7iizlq6iA+Mj1pwHyyUit1pz0+fgA==",
|
"integrity": "sha512-dIHDfrLmeZfr2xwi1whO7AmzdI3HdamgvxthaL+S8L1x8TeczAZEvsmZTjy3s8p3Va4rbGXcb3+uBhmfkqCbfw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
|
@ -4438,9 +4443,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@swc/core-linux-arm-gnueabihf": {
|
"node_modules/@swc/core-linux-arm-gnueabihf": {
|
||||||
"version": "1.3.68",
|
"version": "1.3.75",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.68.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.75.tgz",
|
||||||
"integrity": "sha512-Mk8f6KCOQ2CNAR4PtWajIjS6XKSSR7ZYDOCf1GXRxhS3qEyQH7V8elWvqWYqHcT4foO60NUmxA/NOM/dQrdO1A==",
|
"integrity": "sha512-qeJmvMGrjC6xt+G0R4kVqqxvlhxJx7tTzhcEoWgLJnfvGZiF6SJdsef4OSM7HuReXrlBoEtJbfGPrLJtbV+C0w==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
|
@ -4453,9 +4458,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@swc/core-linux-arm64-gnu": {
|
"node_modules/@swc/core-linux-arm64-gnu": {
|
||||||
"version": "1.3.68",
|
"version": "1.3.75",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.68.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.75.tgz",
|
||||||
"integrity": "sha512-RhBllggh9t9sIxaRgRcGrVaS7fDk6KsIqR6b9+dwU5OyDr4ZyHWw1ZaH/1/HAebuXYhNBjoNUiRtca6lKRIPgQ==",
|
"integrity": "sha512-sqA9JqHEJBF4AdNuwo5zRqq0HC3l31SPsG9zpRa4nRzG5daBBJ80H7fi6PZQud1rfNNq+Q08gjYrdrxwHstvjw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
|
@ -4468,9 +4473,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@swc/core-linux-arm64-musl": {
|
"node_modules/@swc/core-linux-arm64-musl": {
|
||||||
"version": "1.3.68",
|
"version": "1.3.75",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.68.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.75.tgz",
|
||||||
"integrity": "sha512-8K3zjU+tFgn6yGDEeD343gkKaHU9dhz77NiVkI1VzwRaT/Ag5pwl5eMQ1yStm8koNFzn3zq6rGjHfI5g2yI5Wg==",
|
"integrity": "sha512-95rQT5xTAL3eKhMJbJbLsZHHP9EUlh1rcrFoLf0gUApoVF8g94QjZ9hYZiI72mMP5WPjgTEXQVnVB9O2GxeaLw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
|
@ -4483,9 +4488,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@swc/core-linux-x64-gnu": {
|
"node_modules/@swc/core-linux-x64-gnu": {
|
||||||
"version": "1.3.68",
|
"version": "1.3.75",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.68.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.75.tgz",
|
||||||
"integrity": "sha512-4xAnvsBOyeTL0AB8GWlRKDM/hsysJ5jr5qvdKKI3rZfJgnnxl/xSX6TJKPsJ8gygfUJ3BmfCbmUmEyeDZ3YPvA==",
|
"integrity": "sha512-If7UpAhnPduMmtC+TSgPpZ1UXZfp2hIpjUFxpeCmHHYLS6Fn/2GZC5hpEiu+wvFJF0hzPh93eNAHa9gUxGUG+w==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
|
@ -4498,9 +4503,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@swc/core-linux-x64-musl": {
|
"node_modules/@swc/core-linux-x64-musl": {
|
||||||
"version": "1.3.68",
|
"version": "1.3.75",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.68.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.75.tgz",
|
||||||
"integrity": "sha512-RCpaBo1fcpy1EFdjF+I7N4lfzOaHXVV0iMw/ABM+0PD6tp3V/9pxsguaZyeAHyEiUlDA6PZ4TfXv5zfnXEgW4Q==",
|
"integrity": "sha512-HOhxX0YNHTElCZqIviquka3CGYTN8rSQ6BdFfSk/K0O+ZEHx3qGte0qr+gGLPF/237GxreUkp3OMaWKuURtuCg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
|
@ -4513,9 +4518,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@swc/core-win32-arm64-msvc": {
|
"node_modules/@swc/core-win32-arm64-msvc": {
|
||||||
"version": "1.3.68",
|
"version": "1.3.75",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.68.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.75.tgz",
|
||||||
"integrity": "sha512-v2WZvXrSslYEpY1nqpItyamL4DyaJinmOkXvM8Bc1LLKU5rGuvmBdjUYg/5Y+o0AUynuiWubpgHNOkBWiCvfqw==",
|
"integrity": "sha512-7QPI+mvBXAerVfWahrgBNe+g7fK8PuetxFnZSEmXUcDXvWcdJXAndD7GjAJzbDyjQpLKHbsDKMiHYvfNxZoN/A==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
|
@ -4528,9 +4533,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@swc/core-win32-ia32-msvc": {
|
"node_modules/@swc/core-win32-ia32-msvc": {
|
||||||
"version": "1.3.68",
|
"version": "1.3.75",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.68.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.75.tgz",
|
||||||
"integrity": "sha512-HH5NJrIdzkJs+1xxprie0qSCMBeL9yeEhcC1yZTzYv8bwmabOUSdtKIqS55iYP/2hLWn9CTbvKPmLOIhCopW3Q==",
|
"integrity": "sha512-EfABCy4Wlq7O5ShWsm32FgDkSjyeyj/SQ4wnUIvWpkXhgfT1iNXky7KRU1HtX+SmnVk/k/NnabVZpIklYbjtZA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ia32"
|
"ia32"
|
||||||
],
|
],
|
||||||
|
@ -4543,9 +4548,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@swc/core-win32-x64-msvc": {
|
"node_modules/@swc/core-win32-x64-msvc": {
|
||||||
"version": "1.3.68",
|
"version": "1.3.75",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.68.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.75.tgz",
|
||||||
"integrity": "sha512-9HZVtLQUgK8r/yXQdwe0VBexbIcrY6+fBROhs7AAPWdewpaUeLkwQEJk6TbYr9CQuHw26FFGg6SjwAiqXF+kgQ==",
|
"integrity": "sha512-cTvP0pOD9C3pSp1cwtt85ZsrUkQz8RZfSPhM+jCGxKxmoowDCnInoOQ4Ica/ehyuUnQ4/IstSdYtYpO5yzPDJg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
|
@ -4743,11 +4748,23 @@
|
||||||
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz",
|
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz",
|
||||||
"integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA=="
|
"integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA=="
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/language-tags": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/language-tags/-/language-tags-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-rTtRNIewaBrkMUfsCe7ES3xsTRQcEVgic2yoDY9hM3D/nwmABcG2du4l4+dTbWvfO8pUYwL4/2TbWFJa/AGc2g==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/@types/lodash": {
|
"node_modules/@types/lodash": {
|
||||||
"version": "4.14.195",
|
"version": "4.14.195",
|
||||||
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.195.tgz",
|
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.195.tgz",
|
||||||
"integrity": "sha512-Hwx9EUgdwf2GLarOjQp5ZH8ZmblzcbTBC2wtQWNKARBSxM9ezRIAUpeDTgoQRAFB0+8CNWXVA9+MaSOzOF3nPg=="
|
"integrity": "sha512-Hwx9EUgdwf2GLarOjQp5ZH8ZmblzcbTBC2wtQWNKARBSxM9ezRIAUpeDTgoQRAFB0+8CNWXVA9+MaSOzOF3nPg=="
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/luxon": {
|
||||||
|
"version": "3.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.3.1.tgz",
|
||||||
|
"integrity": "sha512-XOS5nBcgEeP2PpcqJHjCWhUCAzGfXIU8ILOSLpx2FhxqMW9KdxgCGXNOEKGVBfveKtIpztHzKK5vSRVLyW/NqA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/@types/mime": {
|
"node_modules/@types/mime": {
|
||||||
"version": "1.3.2",
|
"version": "1.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
|
||||||
|
@ -5474,6 +5491,20 @@
|
||||||
"npm": "1.2.8000 || >= 1.4.16"
|
"npm": "1.2.8000 || >= 1.4.16"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/body-parser/node_modules/qs": {
|
||||||
|
"version": "6.11.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
|
||||||
|
"integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
|
||||||
|
"dependencies": {
|
||||||
|
"side-channel": "^1.0.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.6"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/body-scroll-lock": {
|
"node_modules/body-scroll-lock": {
|
||||||
"version": "3.1.5",
|
"version": "3.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/body-scroll-lock/-/body-scroll-lock-3.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/body-scroll-lock/-/body-scroll-lock-3.1.5.tgz",
|
||||||
|
@ -6034,6 +6065,14 @@
|
||||||
"node": ">=0.8"
|
"node": ">=0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/console-table-printer": {
|
||||||
|
"version": "2.11.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/console-table-printer/-/console-table-printer-2.11.2.tgz",
|
||||||
|
"integrity": "sha512-uuUHie0sfPP542TKGzPFal0W1wo1beuKAqIZdaavcONx8OoqdnJRKjkinbRTOta4FaCa1RcIL+7mMJWX3pQGVg==",
|
||||||
|
"dependencies": {
|
||||||
|
"simple-wcswidth": "^1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/content-disposition": {
|
"node_modules/content-disposition": {
|
||||||
"version": "0.5.4",
|
"version": "0.5.4",
|
||||||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
|
||||||
|
@ -7192,6 +7231,20 @@
|
||||||
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-5.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-5.5.1.tgz",
|
||||||
"integrity": "sha512-MTjE2eIbHv5DyfuFz4zLYWxpqVhEhkTiwFGuB74Q9CSou2WHO52nlE5y3Zlg6SIsiYUIPj6ifFxnkPz6O3sIUg=="
|
"integrity": "sha512-MTjE2eIbHv5DyfuFz4zLYWxpqVhEhkTiwFGuB74Q9CSou2WHO52nlE5y3Zlg6SIsiYUIPj6ifFxnkPz6O3sIUg=="
|
||||||
},
|
},
|
||||||
|
"node_modules/express/node_modules/qs": {
|
||||||
|
"version": "6.11.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
|
||||||
|
"integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
|
||||||
|
"dependencies": {
|
||||||
|
"side-channel": "^1.0.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.6"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ext": {
|
"node_modules/ext": {
|
||||||
"version": "1.7.0",
|
"version": "1.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz",
|
||||||
|
@ -8476,14 +8529,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
||||||
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
|
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
|
||||||
},
|
},
|
||||||
"node_modules/iso-639-1": {
|
|
||||||
"version": "2.1.15",
|
|
||||||
"resolved": "https://registry.npmjs.org/iso-639-1/-/iso-639-1-2.1.15.tgz",
|
|
||||||
"integrity": "sha512-7c7mBznZu2ktfvyT582E2msM+Udc1EjOyhVRE/0ZsjD9LBtWSm23h3PtiRh2a35XoUsTQQjJXaJzuLjXsOdFDg==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/isobject": {
|
"node_modules/isobject": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
|
||||||
|
@ -8760,6 +8805,19 @@
|
||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/language-subtag-registry": {
|
||||||
|
"version": "0.3.22",
|
||||||
|
"resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz",
|
||||||
|
"integrity": "sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w=="
|
||||||
|
},
|
||||||
|
"node_modules/language-tags": {
|
||||||
|
"version": "1.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.8.tgz",
|
||||||
|
"integrity": "sha512-aWAZwgPLS8hJ20lNPm9HNVs4inexz6S2sQa3wx/+ycuutMNE5/IfYxiWYBbi+9UWCQVaXYCOPUl6gFrPR7+jGg==",
|
||||||
|
"dependencies": {
|
||||||
|
"language-subtag-registry": "^0.3.20"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/lilconfig": {
|
"node_modules/lilconfig": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz",
|
||||||
|
@ -8888,6 +8946,14 @@
|
||||||
"es5-ext": "~0.10.2"
|
"es5-ext": "~0.10.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/luxon": {
|
||||||
|
"version": "3.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/luxon/-/luxon-3.4.0.tgz",
|
||||||
|
"integrity": "sha512-7eDo4Pt7aGhoCheGFIuq4Xa2fJm4ZpmldpGhjTYBNUYNCN6TIEP6v7chwwwt3KRp7YR+rghbfvjyo3V5y9hgBw==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/make-dir": {
|
"node_modules/make-dir": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
|
||||||
|
@ -9251,23 +9317,6 @@
|
||||||
"thenify-all": "^1.0.0"
|
"thenify-all": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/nanoid": {
|
|
||||||
"version": "3.3.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
|
|
||||||
"integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==",
|
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/ai"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"bin": {
|
|
||||||
"nanoid": "bin/nanoid.cjs"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/napi-build-utils": {
|
"node_modules/napi-build-utils": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz",
|
||||||
|
@ -9848,9 +9897,9 @@
|
||||||
"integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg=="
|
"integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg=="
|
||||||
},
|
},
|
||||||
"node_modules/payload": {
|
"node_modules/payload": {
|
||||||
"version": "1.11.7",
|
"version": "1.13.4",
|
||||||
"resolved": "https://registry.npmjs.org/payload/-/payload-1.11.7.tgz",
|
"resolved": "https://registry.npmjs.org/payload/-/payload-1.13.4.tgz",
|
||||||
"integrity": "sha512-pVImP0b8MA9VfcXDITqq8KlIVaFB81FMtt24TPrBfNqkG53cRFxnYiGRJS40p/CNVknCpSRGVBkdpKmVNQ07YQ==",
|
"integrity": "sha512-toVqxxxq4SJwBJtmCLaLOReza0JSidCrH4jTBxcy4h/qOYyeCAS4wApUFYhDI7JhB3KCd2+jbpVbhHOU4ALBKA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@date-io/date-fns": "^2.16.0",
|
"@date-io/date-fns": "^2.16.0",
|
||||||
"@dnd-kit/core": "^6.0.7",
|
"@dnd-kit/core": "^6.0.7",
|
||||||
|
@ -9859,7 +9908,7 @@
|
||||||
"@faceless-ui/scroll-info": "^1.3.0",
|
"@faceless-ui/scroll-info": "^1.3.0",
|
||||||
"@faceless-ui/window-info": "^2.1.1",
|
"@faceless-ui/window-info": "^2.1.1",
|
||||||
"@monaco-editor/react": "^4.5.1",
|
"@monaco-editor/react": "^4.5.1",
|
||||||
"@swc/core": "^1.3.26",
|
"@swc/core": "1.3.75",
|
||||||
"@swc/register": "^0.1.10",
|
"@swc/register": "^0.1.10",
|
||||||
"@types/sharp": "^0.31.1",
|
"@types/sharp": "^0.31.1",
|
||||||
"body-parser": "^1.20.1",
|
"body-parser": "^1.20.1",
|
||||||
|
@ -9867,6 +9916,7 @@
|
||||||
"compression": "^1.7.4",
|
"compression": "^1.7.4",
|
||||||
"conf": "^10.2.0",
|
"conf": "^10.2.0",
|
||||||
"connect-history-api-fallback": "^1.6.0",
|
"connect-history-api-fallback": "^1.6.0",
|
||||||
|
"console-table-printer": "^2.11.2",
|
||||||
"css-loader": "^5.2.7",
|
"css-loader": "^5.2.7",
|
||||||
"css-minimizer-webpack-plugin": "^5.0.0",
|
"css-minimizer-webpack-plugin": "^5.0.0",
|
||||||
"dataloader": "^2.1.0",
|
"dataloader": "^2.1.0",
|
||||||
|
@ -11386,6 +11436,23 @@
|
||||||
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
|
||||||
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
|
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
|
||||||
},
|
},
|
||||||
|
"node_modules/postcss/node_modules/nanoid": {
|
||||||
|
"version": "3.3.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
|
||||||
|
"integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/ai"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"bin": {
|
||||||
|
"nanoid": "bin/nanoid.cjs"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/prebuild-install": {
|
"node_modules/prebuild-install": {
|
||||||
"version": "7.1.1",
|
"version": "7.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz",
|
||||||
|
@ -11412,9 +11479,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/prettier": {
|
"node_modules/prettier": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.1.tgz",
|
||||||
"integrity": "sha512-zBf5eHpwHOGPC47h0zrPyNn+eAEIdEzfywMoYn2XPi0P44Zp0tSq64rq0xAREh4auw2cJZHo9QUob+NqCQky4g==",
|
"integrity": "sha512-fcOWSnnpCrovBsmFZIGIy9UqK2FaI7Hqax+DIO0A9UxeVoY4iweyaFjS5TavZN97Hfehph0nhsZnjlVKzEQSrQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"prettier": "bin/prettier.cjs"
|
"prettier": "bin/prettier.cjs"
|
||||||
|
@ -11510,9 +11577,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/qs": {
|
"node_modules/qs": {
|
||||||
"version": "6.11.0",
|
"version": "6.11.2",
|
||||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz",
|
||||||
"integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
|
"integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"side-channel": "^1.0.4"
|
"side-channel": "^1.0.4"
|
||||||
},
|
},
|
||||||
|
@ -12488,6 +12555,11 @@
|
||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/simple-wcswidth": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/simple-wcswidth/-/simple-wcswidth-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-xMO/8eNREtaROt7tJvWJqHBDTMFN4eiQ5I4JRMuilwfnFcV5W9u7RUkueNkdw0jPqGMX36iCywelS5yilTuOxg=="
|
||||||
|
},
|
||||||
"node_modules/sirv": {
|
"node_modules/sirv": {
|
||||||
"version": "1.0.19",
|
"version": "1.0.19",
|
||||||
"resolved": "https://registry.npmjs.org/sirv/-/sirv-1.0.19.tgz",
|
"resolved": "https://registry.npmjs.org/sirv/-/sirv-1.0.19.tgz",
|
||||||
|
@ -12782,9 +12854,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/styled-components": {
|
"node_modules/styled-components": {
|
||||||
"version": "6.0.5",
|
"version": "6.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.0.7.tgz",
|
||||||
"integrity": "sha512-308zi5o7LrA9cVaP4nPD0TaUpOjGPePkAUFb/OGB0xRI3I9ozpW5UyASvRVi9wJcYASG+Y3mLDLDUZC7nqzimw==",
|
"integrity": "sha512-xIwWuiRMYR43mskVsW9MGTRjSo7ol4bcVjT595fGUp3OLBJOlOgaiKaxsHdC4a2HqWKqKnh0CmcRbk5ogyDjTg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/cli": "^7.21.0",
|
"@babel/cli": "^7.21.0",
|
||||||
"@babel/core": "^7.21.0",
|
"@babel/core": "^7.21.0",
|
||||||
|
|
19
package.json
19
package.json
|
@ -16,27 +16,34 @@
|
||||||
"prettier": "prettier --list-different --end-of-line auto --write src",
|
"prettier": "prettier --list-different --end-of-line auto --write src",
|
||||||
"tsc": "tsc --noEmit",
|
"tsc": "tsc --noEmit",
|
||||||
"precommit": "npm run generate:types && npm run prettier && npm run tsc",
|
"precommit": "npm run generate:types && npm run prettier && npm run tsc",
|
||||||
"upgrade": "ncu"
|
"upgrade": "ncu",
|
||||||
|
"clean": "sudo rm -r uploads mongo",
|
||||||
|
"start": "sudo docker compose up"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fontsource/vollkorn": "^5.0.5",
|
"@fontsource/vollkorn": "^5.0.8",
|
||||||
"clean-deep": "^3.4.0",
|
"clean-deep": "^3.4.0",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"dotenv": "^16.3.1",
|
"dotenv": "^16.3.1",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"iso-639-1": "^2.1.15",
|
"language-tags": "^1.0.8",
|
||||||
"payload": "^1.11.7",
|
"luxon": "^3.4.0",
|
||||||
|
"payload": "^1.13.3",
|
||||||
|
"qs": "^6.11.2",
|
||||||
"slugify": "^1.6.6",
|
"slugify": "^1.6.6",
|
||||||
"styled-components": "^6.0.5",
|
"styled-components": "^6.0.7",
|
||||||
"unset-value": "^2.0.1"
|
"unset-value": "^2.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/dotenv": "^8.2.0",
|
"@types/dotenv": "^8.2.0",
|
||||||
"@types/express": "^4.17.17",
|
"@types/express": "^4.17.17",
|
||||||
|
"@types/language-tags": "^1.0.1",
|
||||||
|
"@types/luxon": "^3.3.1",
|
||||||
|
"@types/qs": "^6.9.7",
|
||||||
"@types/react-router-dom": "^5.3.3",
|
"@types/react-router-dom": "^5.3.3",
|
||||||
"copyfiles": "^2.4.1",
|
"copyfiles": "^2.4.1",
|
||||||
"nodemon": "^3.0.1",
|
"nodemon": "^3.0.1",
|
||||||
"prettier": "^3.0.0",
|
"prettier": "^3.0.1",
|
||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
"typescript": "^5.1.6"
|
"typescript": "^5.1.6"
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
export const publicAccess = () => true;
|
|
@ -0,0 +1,70 @@
|
||||||
|
import { CollectionConfig } from "payload/types";
|
||||||
|
import { mustBeAdmin } from "../../accesses/mustBeAdmin";
|
||||||
|
import { CollectionGroups, Collections } from "../../constants";
|
||||||
|
import { slugField } from "../../fields/slugField/slugField";
|
||||||
|
import { localizedFields } from "../../fields/translatedFields/translatedFields";
|
||||||
|
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
||||||
|
import { importFromStrapi } from "./endpoints/importFromStrapi";
|
||||||
|
|
||||||
|
const fields = {
|
||||||
|
slug: "slug",
|
||||||
|
startingYear: "startingYear",
|
||||||
|
endingYear: "endingYear",
|
||||||
|
translations: "translations",
|
||||||
|
translationsTitle: "title",
|
||||||
|
translationsDescription: "description",
|
||||||
|
} as const satisfies Record<string, string>;
|
||||||
|
|
||||||
|
export const ChronologyEras: CollectionConfig = buildCollectionConfig(
|
||||||
|
Collections.ChronologyEras,
|
||||||
|
{
|
||||||
|
singular: "Chronology Era",
|
||||||
|
plural: "Chronology Eras",
|
||||||
|
},
|
||||||
|
() => ({
|
||||||
|
defaultSort: fields.startingYear,
|
||||||
|
admin: {
|
||||||
|
group: CollectionGroups.Collections,
|
||||||
|
defaultColumns: [fields.slug, fields.startingYear, fields.endingYear, fields.translations],
|
||||||
|
useAsTitle: fields.slug,
|
||||||
|
},
|
||||||
|
access: {
|
||||||
|
create: mustBeAdmin,
|
||||||
|
delete: mustBeAdmin,
|
||||||
|
},
|
||||||
|
endpoints: [importFromStrapi],
|
||||||
|
fields: [
|
||||||
|
slugField({ name: fields.slug }),
|
||||||
|
{
|
||||||
|
type: "row",
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: fields.startingYear,
|
||||||
|
type: "number",
|
||||||
|
min: 0,
|
||||||
|
required: true,
|
||||||
|
admin: { width: "50%", description: "The year the era started (year included)" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: fields.endingYear,
|
||||||
|
type: "number",
|
||||||
|
min: 0,
|
||||||
|
required: true,
|
||||||
|
admin: { width: "50%", description: "The year the era ended (year included)" },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
localizedFields({
|
||||||
|
name: fields.translations,
|
||||||
|
admin: { useAsTitle: fields.translationsTitle },
|
||||||
|
fields: [
|
||||||
|
{ name: fields.translationsTitle, type: "text", required: true },
|
||||||
|
{
|
||||||
|
name: fields.translationsDescription,
|
||||||
|
type: "textarea",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
);
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { Collections } from "../../../constants";
|
||||||
|
import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint";
|
||||||
|
import { ChronologyEra } from "../../../types/collections";
|
||||||
|
|
||||||
|
export const importFromStrapi = createStrapiImportEndpoint<ChronologyEra>({
|
||||||
|
strapi: {
|
||||||
|
collection: "chronology-eras",
|
||||||
|
params: {
|
||||||
|
populate: { title: { populate: "language" } },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
payload: {
|
||||||
|
collection: Collections.ChronologyEras,
|
||||||
|
convert: ({ slug, starting_year, ending_year, title: titles }) => ({
|
||||||
|
slug,
|
||||||
|
startingYear: starting_year,
|
||||||
|
endingYear: ending_year,
|
||||||
|
translations: titles.map(({ language, title, description }) => ({
|
||||||
|
language: language.data.attributes.code,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
})),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
});
|
|
@ -0,0 +1,120 @@
|
||||||
|
import { DateTime } from "luxon";
|
||||||
|
import { CollectionConfig } from "payload/types";
|
||||||
|
import {
|
||||||
|
QuickFilters,
|
||||||
|
languageBasedFilters,
|
||||||
|
publishStatusFilters,
|
||||||
|
} from "../../components/QuickFilters";
|
||||||
|
import { CollectionGroups, Collections } from "../../constants";
|
||||||
|
import { localizedFields } from "../../fields/translatedFields/translatedFields";
|
||||||
|
import { isDefined, isUndefined } from "../../utils/asserts";
|
||||||
|
import { buildVersionedCollectionConfig } from "../../utils/versionedCollectionConfig";
|
||||||
|
import { importFromStrapi } from "./endpoints/importFromStrapi";
|
||||||
|
|
||||||
|
const fields = {
|
||||||
|
name: "name",
|
||||||
|
events: "events",
|
||||||
|
eventsTranslations: "translations",
|
||||||
|
eventsTranslationsTitle: "title",
|
||||||
|
eventsTranslationsDescription: "description",
|
||||||
|
eventsTranslationsNotes: "notes",
|
||||||
|
date: "date",
|
||||||
|
year: "year",
|
||||||
|
month: "month",
|
||||||
|
day: "day",
|
||||||
|
status: "_status",
|
||||||
|
} as const satisfies Record<string, string>;
|
||||||
|
|
||||||
|
export const ChronologyItems: CollectionConfig = buildVersionedCollectionConfig(
|
||||||
|
Collections.ChronologyItems,
|
||||||
|
{
|
||||||
|
singular: "Chronology Item",
|
||||||
|
plural: "Chronology Items",
|
||||||
|
},
|
||||||
|
() => ({
|
||||||
|
defaultSort: fields.name,
|
||||||
|
admin: {
|
||||||
|
group: CollectionGroups.Collections,
|
||||||
|
defaultColumns: [fields.name, fields.events, fields.status],
|
||||||
|
useAsTitle: fields.name,
|
||||||
|
components: {
|
||||||
|
BeforeListTable: [
|
||||||
|
() =>
|
||||||
|
QuickFilters({
|
||||||
|
slug: Collections.ChronologyItems,
|
||||||
|
filterGroups: [
|
||||||
|
languageBasedFilters("events.translations.language"),
|
||||||
|
publishStatusFilters,
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
endpoints: [importFromStrapi],
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: fields.name,
|
||||||
|
type: "text",
|
||||||
|
admin: { hidden: true },
|
||||||
|
hooks: {
|
||||||
|
beforeValidate: [
|
||||||
|
({
|
||||||
|
data: {
|
||||||
|
date: { year, month, day },
|
||||||
|
},
|
||||||
|
}) =>
|
||||||
|
[
|
||||||
|
String(year ?? "?????").padStart(5, "0"),
|
||||||
|
String(month ?? "??").padStart(2, "0"),
|
||||||
|
String(day ?? "??").padStart(2, "0"),
|
||||||
|
].join("-"),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "group",
|
||||||
|
name: fields.date,
|
||||||
|
validate: ({ year, month, day } = {}) => {
|
||||||
|
if (isDefined(day)) {
|
||||||
|
if (isUndefined(month)) return "A month is required if a day is set";
|
||||||
|
const stringDate = `${year}/${month}/${day}`;
|
||||||
|
if (!DateTime.fromObject({ year, month, day }).isValid)
|
||||||
|
return `The given date (${stringDate}) is not a valid date.`;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
type: "row",
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: fields.year,
|
||||||
|
type: "number",
|
||||||
|
required: true,
|
||||||
|
min: 0,
|
||||||
|
admin: { width: "33%" },
|
||||||
|
},
|
||||||
|
{ name: fields.month, type: "number", min: 1, max: 12, admin: { width: "33%" } },
|
||||||
|
{ name: fields.day, type: "number", min: 1, max: 31, admin: { width: "33%" } },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: fields.events,
|
||||||
|
type: "array",
|
||||||
|
fields: [
|
||||||
|
localizedFields({
|
||||||
|
name: fields.eventsTranslations,
|
||||||
|
admin: { useAsTitle: fields.eventsTranslationsTitle },
|
||||||
|
fields: [
|
||||||
|
{ name: fields.eventsTranslationsTitle, type: "text" },
|
||||||
|
{ name: fields.eventsTranslationsDescription, type: "textarea" },
|
||||||
|
{ name: fields.eventsTranslationsNotes, type: "textarea" },
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
);
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { Collections } from "../../../constants";
|
||||||
|
import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint";
|
||||||
|
import { ChronologyItem } from "../../../types/collections";
|
||||||
|
|
||||||
|
export const importFromStrapi = createStrapiImportEndpoint<ChronologyItem>({
|
||||||
|
strapi: {
|
||||||
|
collection: "chronology-items",
|
||||||
|
params: {
|
||||||
|
populate: { events: { populate: { translations: { populate: "language" } } } },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
payload: {
|
||||||
|
collection: Collections.ChronologyItems,
|
||||||
|
convert: ({ year, month, day, events }) => ({
|
||||||
|
date: { year, month, day },
|
||||||
|
events: events.map((event) => ({
|
||||||
|
translations: event.translations.map(({ title, description, note, language }) => ({
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
note,
|
||||||
|
language: language.data.attributes.code,
|
||||||
|
})),
|
||||||
|
})),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
});
|
|
@ -1,17 +1,14 @@
|
||||||
import { CollectionGroup, FileTypes, KeysTypes } from "../../constants";
|
import { CollectionGroups, Collections, FileTypes, KeysTypes } from "../../constants";
|
||||||
import { slugField } from "../../fields/slugField/slugField";
|
|
||||||
import { imageField } from "../../fields/imageField/imageField";
|
|
||||||
import { Keys } from "../Keys/Keys";
|
|
||||||
import { localizedFields } from "../../fields/translatedFields/translatedFields";
|
|
||||||
import { Recorders } from "../Recorders/Recorders";
|
|
||||||
import { isDefined } from "../../utils/asserts";
|
|
||||||
import { fileField } from "../../fields/fileField/fileField";
|
import { fileField } from "../../fields/fileField/fileField";
|
||||||
import { contentBlocks } from "./Blocks/blocks";
|
import { imageField } from "../../fields/imageField/imageField";
|
||||||
import { ContentThumbnails } from "../ContentThumbnails/ContentThumbnails";
|
import { slugField } from "../../fields/slugField/slugField";
|
||||||
import { buildVersionedCollectionConfig } from "../../utils/versionedCollectionConfig";
|
import { localizedFields } from "../../fields/translatedFields/translatedFields";
|
||||||
|
import { beforeDuplicateAddCopyTo } from "../../hooks/beforeDuplicateAddCopyTo";
|
||||||
import { beforeDuplicatePiping } from "../../hooks/beforeDuplicatePiping";
|
import { beforeDuplicatePiping } from "../../hooks/beforeDuplicatePiping";
|
||||||
import { beforeDuplicateUnpublish } from "../../hooks/beforeDuplicateUnpublish";
|
import { beforeDuplicateUnpublish } from "../../hooks/beforeDuplicateUnpublish";
|
||||||
import { beforeDuplicateAddCopyTo } from "../../hooks/beforeDuplicateAddCopyTo";
|
import { isDefined } from "../../utils/asserts";
|
||||||
|
import { buildVersionedCollectionConfig } from "../../utils/versionedCollectionConfig";
|
||||||
|
import { contentBlocks } from "./Blocks/blocks";
|
||||||
|
|
||||||
const fields = {
|
const fields = {
|
||||||
slug: "slug",
|
slug: "slug",
|
||||||
|
@ -37,6 +34,7 @@ const fields = {
|
||||||
} as const satisfies Record<string, string>;
|
} as const satisfies Record<string, string>;
|
||||||
|
|
||||||
export const Contents = buildVersionedCollectionConfig(
|
export const Contents = buildVersionedCollectionConfig(
|
||||||
|
Collections.Contents,
|
||||||
{
|
{
|
||||||
singular: "Content",
|
singular: "Content",
|
||||||
plural: "Contents",
|
plural: "Contents",
|
||||||
|
@ -55,7 +53,7 @@ export const Contents = buildVersionedCollectionConfig(
|
||||||
fields.translations,
|
fields.translations,
|
||||||
fields.status,
|
fields.status,
|
||||||
],
|
],
|
||||||
group: CollectionGroup.Collections,
|
group: CollectionGroups.Collections,
|
||||||
hooks: {
|
hooks: {
|
||||||
beforeDuplicate: beforeDuplicatePiping([
|
beforeDuplicate: beforeDuplicatePiping([
|
||||||
beforeDuplicateUnpublish,
|
beforeDuplicateUnpublish,
|
||||||
|
@ -71,7 +69,7 @@ export const Contents = buildVersionedCollectionConfig(
|
||||||
slugField({ name: fields.slug, admin: { width: "50%" } }),
|
slugField({ name: fields.slug, admin: { width: "50%" } }),
|
||||||
imageField({
|
imageField({
|
||||||
name: fields.thumbnail,
|
name: fields.thumbnail,
|
||||||
relationTo: ContentThumbnails.slug,
|
relationTo: Collections.ContentsThumbnails,
|
||||||
admin: { width: "50%" },
|
admin: { width: "50%" },
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
@ -82,7 +80,7 @@ export const Contents = buildVersionedCollectionConfig(
|
||||||
{
|
{
|
||||||
name: fields.categories,
|
name: fields.categories,
|
||||||
type: "relationship",
|
type: "relationship",
|
||||||
relationTo: [Keys.slug],
|
relationTo: [Collections.Keys],
|
||||||
filterOptions: { type: { equals: KeysTypes.Categories } },
|
filterOptions: { type: { equals: KeysTypes.Categories } },
|
||||||
hasMany: true,
|
hasMany: true,
|
||||||
admin: { allowCreate: false, width: "50%" },
|
admin: { allowCreate: false, width: "50%" },
|
||||||
|
@ -90,7 +88,7 @@ export const Contents = buildVersionedCollectionConfig(
|
||||||
{
|
{
|
||||||
name: fields.type,
|
name: fields.type,
|
||||||
type: "relationship",
|
type: "relationship",
|
||||||
relationTo: [Keys.slug],
|
relationTo: [Collections.Keys],
|
||||||
filterOptions: { type: { equals: KeysTypes.Contents } },
|
filterOptions: { type: { equals: KeysTypes.Contents } },
|
||||||
admin: { allowCreate: false, width: "50%" },
|
admin: { allowCreate: false, width: "50%" },
|
||||||
},
|
},
|
||||||
|
@ -128,7 +126,7 @@ export const Contents = buildVersionedCollectionConfig(
|
||||||
name: fields.textTranscribers,
|
name: fields.textTranscribers,
|
||||||
label: "Transcribers",
|
label: "Transcribers",
|
||||||
type: "relationship",
|
type: "relationship",
|
||||||
relationTo: Recorders.slug,
|
relationTo: Collections.Recorders,
|
||||||
hasMany: true,
|
hasMany: true,
|
||||||
admin: {
|
admin: {
|
||||||
condition: (_, siblingData) =>
|
condition: (_, siblingData) =>
|
||||||
|
@ -140,7 +138,7 @@ export const Contents = buildVersionedCollectionConfig(
|
||||||
name: fields.textTranslators,
|
name: fields.textTranslators,
|
||||||
label: "Translators",
|
label: "Translators",
|
||||||
type: "relationship",
|
type: "relationship",
|
||||||
relationTo: Recorders.slug,
|
relationTo: Collections.Recorders,
|
||||||
hasMany: true,
|
hasMany: true,
|
||||||
admin: {
|
admin: {
|
||||||
condition: (_, siblingData) =>
|
condition: (_, siblingData) =>
|
||||||
|
@ -152,7 +150,7 @@ export const Contents = buildVersionedCollectionConfig(
|
||||||
name: fields.textProofreaders,
|
name: fields.textProofreaders,
|
||||||
label: "Proofreaders",
|
label: "Proofreaders",
|
||||||
type: "relationship",
|
type: "relationship",
|
||||||
relationTo: Recorders.slug,
|
relationTo: Collections.Recorders,
|
||||||
hasMany: true,
|
hasMany: true,
|
||||||
admin: { width: "50%" },
|
admin: { width: "50%" },
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
|
import { CollectionGroups, Collections } from "../../constants";
|
||||||
import { slugField } from "../../fields/slugField/slugField";
|
import { slugField } from "../../fields/slugField/slugField";
|
||||||
import { CollectionGroup } from "../../constants";
|
|
||||||
import { localizedFields } from "../../fields/translatedFields/translatedFields";
|
import { localizedFields } from "../../fields/translatedFields/translatedFields";
|
||||||
import { Contents } from "../Contents/Contents";
|
|
||||||
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
||||||
|
|
||||||
const fields = {
|
const fields = {
|
||||||
|
@ -12,18 +11,19 @@ const fields = {
|
||||||
contents: "contents",
|
contents: "contents",
|
||||||
} as const satisfies Record<string, string>;
|
} as const satisfies Record<string, string>;
|
||||||
|
|
||||||
export const ContentFolders = buildCollectionConfig(
|
export const ContentsFolders = buildCollectionConfig(
|
||||||
|
Collections.ContentsFolders,
|
||||||
{
|
{
|
||||||
singular: "Content Folder",
|
singular: "Contents Folder",
|
||||||
plural: "Content Folders",
|
plural: "Contents Folders",
|
||||||
},
|
},
|
||||||
({ slug }) => ({
|
() => ({
|
||||||
defaultSort: fields.slug,
|
defaultSort: fields.slug,
|
||||||
admin: {
|
admin: {
|
||||||
useAsTitle: fields.slug,
|
useAsTitle: fields.slug,
|
||||||
defaultColumns: [fields.slug, fields.translations],
|
defaultColumns: [fields.slug, fields.translations],
|
||||||
disableDuplicate: true,
|
disableDuplicate: true,
|
||||||
group: CollectionGroup.Collections,
|
group: CollectionGroups.Collections,
|
||||||
},
|
},
|
||||||
timestamps: false,
|
timestamps: false,
|
||||||
versions: false,
|
versions: false,
|
||||||
|
@ -43,14 +43,14 @@ export const ContentFolders = buildCollectionConfig(
|
||||||
{
|
{
|
||||||
type: "relationship",
|
type: "relationship",
|
||||||
name: fields.subfolders,
|
name: fields.subfolders,
|
||||||
relationTo: [slug],
|
relationTo: Collections.ContentsFolders,
|
||||||
hasMany: true,
|
hasMany: true,
|
||||||
admin: { width: "50%" },
|
admin: { width: "50%" },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "relationship",
|
type: "relationship",
|
||||||
name: fields.contents,
|
name: fields.contents,
|
||||||
relationTo: [Contents.slug],
|
relationTo: Collections.Contents,
|
||||||
hasMany: true,
|
hasMany: true,
|
||||||
admin: { width: "50%" },
|
admin: { width: "50%" },
|
||||||
},
|
},
|
|
@ -1,4 +1,4 @@
|
||||||
import { CollectionGroup } from "../../constants";
|
import { CollectionGroups, Collections } from "../../constants";
|
||||||
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
||||||
|
|
||||||
const fields = {
|
const fields = {
|
||||||
|
@ -7,17 +7,18 @@ const fields = {
|
||||||
filesize: "filesize",
|
filesize: "filesize",
|
||||||
} as const satisfies Record<string, string>;
|
} as const satisfies Record<string, string>;
|
||||||
|
|
||||||
export const ContentThumbnails = buildCollectionConfig(
|
export const ContentsThumbnails = buildCollectionConfig(
|
||||||
|
Collections.ContentsThumbnails,
|
||||||
{
|
{
|
||||||
singular: "Content Thumbnail",
|
singular: "Contents Thumbnail",
|
||||||
plural: "Content Thumbnails",
|
plural: "Contents Thumbnails",
|
||||||
},
|
},
|
||||||
({ uploadDir }) => ({
|
({ uploadDir }) => ({
|
||||||
defaultSort: fields.filename,
|
defaultSort: fields.filename,
|
||||||
admin: {
|
admin: {
|
||||||
useAsTitle: fields.filename,
|
useAsTitle: fields.filename,
|
||||||
disableDuplicate: true,
|
disableDuplicate: true,
|
||||||
group: CollectionGroup.Media,
|
group: CollectionGroups.Media,
|
||||||
},
|
},
|
||||||
upload: {
|
upload: {
|
||||||
staticDir: uploadDir,
|
staticDir: uploadDir,
|
|
@ -1,12 +1,15 @@
|
||||||
|
import { text } from "payload/dist/fields/validations";
|
||||||
import { mustBeAdmin } from "../../accesses/mustBeAdmin";
|
import { mustBeAdmin } from "../../accesses/mustBeAdmin";
|
||||||
import { CollectionGroup } from "../../constants";
|
import { CollectionGroups, Collections } from "../../constants";
|
||||||
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
||||||
|
import { importFromStrapi } from "./endpoints/importFromStrapi";
|
||||||
|
|
||||||
const fields = {
|
const fields = {
|
||||||
id: "id",
|
id: "id",
|
||||||
} as const satisfies Record<string, string>;
|
} as const satisfies Record<string, string>;
|
||||||
|
|
||||||
export const Currencies = buildCollectionConfig(
|
export const Currencies = buildCollectionConfig(
|
||||||
|
Collections.Currencies,
|
||||||
{
|
{
|
||||||
singular: "Currency",
|
singular: "Currency",
|
||||||
plural: "Currencies",
|
plural: "Currencies",
|
||||||
|
@ -14,12 +17,14 @@ export const Currencies = buildCollectionConfig(
|
||||||
() => ({
|
() => ({
|
||||||
defaultSort: fields.id,
|
defaultSort: fields.id,
|
||||||
admin: {
|
admin: {
|
||||||
|
pagination: { defaultLimit: 100 },
|
||||||
useAsTitle: fields.id,
|
useAsTitle: fields.id,
|
||||||
defaultColumns: [fields.id],
|
defaultColumns: [fields.id],
|
||||||
disableDuplicate: true,
|
disableDuplicate: true,
|
||||||
group: CollectionGroup.Meta,
|
group: CollectionGroups.Meta,
|
||||||
},
|
},
|
||||||
access: { create: mustBeAdmin, update: mustBeAdmin },
|
access: { create: mustBeAdmin, update: mustBeAdmin },
|
||||||
|
endpoints: [importFromStrapi],
|
||||||
timestamps: false,
|
timestamps: false,
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
|
@ -27,11 +32,11 @@ export const Currencies = buildCollectionConfig(
|
||||||
type: "text",
|
type: "text",
|
||||||
unique: true,
|
unique: true,
|
||||||
required: true,
|
required: true,
|
||||||
validate: (value) => {
|
validate: (value, options) => {
|
||||||
if (/^[A-Z]{3}$/g.test(value)) {
|
if (!/^[A-Z]{3}$/g.test(value)) {
|
||||||
return true;
|
return "The code must be a valid ISO 4217 currency code (e.g: EUR, CAD...)";
|
||||||
}
|
}
|
||||||
return "The code must be a valid ISO 4217 currency code (e.g: EUR, CAD...)";
|
return text(value, options);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
import { Collections } from "../../../constants";
|
||||||
|
import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint";
|
||||||
|
import { Language } from "../../../types/collections";
|
||||||
|
|
||||||
|
export const importFromStrapi = createStrapiImportEndpoint<Language>({
|
||||||
|
strapi: {
|
||||||
|
collection: "currencies",
|
||||||
|
params: {},
|
||||||
|
},
|
||||||
|
payload: {
|
||||||
|
collection: Collections.Currencies,
|
||||||
|
convert: ({ code, name }) => ({ id: code, name }),
|
||||||
|
},
|
||||||
|
});
|
|
@ -1,4 +1,4 @@
|
||||||
import { CollectionGroup, FileTypes } from "../../constants";
|
import { CollectionGroups, Collections, FileTypes } from "../../constants";
|
||||||
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
||||||
|
|
||||||
const fields = {
|
const fields = {
|
||||||
|
@ -7,6 +7,7 @@ const fields = {
|
||||||
} as const satisfies Record<string, string>;
|
} as const satisfies Record<string, string>;
|
||||||
|
|
||||||
export const Files = buildCollectionConfig(
|
export const Files = buildCollectionConfig(
|
||||||
|
Collections.Files,
|
||||||
{
|
{
|
||||||
singular: "File",
|
singular: "File",
|
||||||
plural: "Files",
|
plural: "Files",
|
||||||
|
@ -16,7 +17,7 @@ export const Files = buildCollectionConfig(
|
||||||
admin: {
|
admin: {
|
||||||
useAsTitle: fields.filename,
|
useAsTitle: fields.filename,
|
||||||
disableDuplicate: true,
|
disableDuplicate: true,
|
||||||
group: CollectionGroup.Media,
|
group: CollectionGroups.Media,
|
||||||
},
|
},
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,60 +1,86 @@
|
||||||
import { CollectionConfig } from "payload/types";
|
import payload from "payload";
|
||||||
import { slugField } from "../../fields/slugField/slugField";
|
import { mustBeAdmin } from "../../accesses/mustBeAdmin";
|
||||||
import { CollectionGroup, KeysTypes } from "../../constants";
|
import { QuickFilters } from "../../components/QuickFilters";
|
||||||
|
import { CollectionGroups, Collections, KeysTypes, LanguageCodes } from "../../constants";
|
||||||
import { localizedFields } from "../../fields/translatedFields/translatedFields";
|
import { localizedFields } from "../../fields/translatedFields/translatedFields";
|
||||||
|
import { beforeDuplicateAddCopyTo } from "../../hooks/beforeDuplicateAddCopyTo";
|
||||||
import { Key } from "../../types/collections";
|
import { Key } from "../../types/collections";
|
||||||
import { isDefined } from "../../utils/asserts";
|
import { isDefined } from "../../utils/asserts";
|
||||||
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
||||||
import { mustBeAdmin } from "../../accesses/mustBeAdmin";
|
import { importFromStrapi } from "./endpoints/importFromStrapi";
|
||||||
import { beforeDuplicateAddCopyTo } from "../../hooks/beforeDuplicateAddCopyTo";
|
|
||||||
import { QuickFilters } from "../../components/QuickFilters";
|
|
||||||
|
|
||||||
const fields = {
|
const fields = {
|
||||||
slug: "slug",
|
|
||||||
translations: "translations",
|
|
||||||
type: "type",
|
|
||||||
name: "name",
|
name: "name",
|
||||||
short: "short",
|
type: "type",
|
||||||
|
translations: "translations",
|
||||||
|
translationsName: "name",
|
||||||
|
translationsShort: "short",
|
||||||
} as const satisfies Record<string, string>;
|
} as const satisfies Record<string, string>;
|
||||||
|
|
||||||
const keysTypesWithShort: (keyof typeof KeysTypes)[] = ["Categories", "GamePlatforms"];
|
const keysTypesWithShort: (keyof typeof KeysTypes)[] = ["Categories", "GamePlatforms"];
|
||||||
|
|
||||||
export const Keys: CollectionConfig = buildCollectionConfig(
|
export const Keys = buildCollectionConfig(
|
||||||
|
Collections.Keys,
|
||||||
{
|
{
|
||||||
singular: "Key",
|
singular: "Key",
|
||||||
plural: "Keys",
|
plural: "Keys",
|
||||||
},
|
},
|
||||||
() => ({
|
() => ({
|
||||||
defaultSort: fields.slug,
|
defaultSort: fields.name,
|
||||||
admin: {
|
admin: {
|
||||||
useAsTitle: fields.slug,
|
useAsTitle: fields.name,
|
||||||
defaultColumns: [fields.slug, fields.type, fields.translations],
|
defaultColumns: [fields.name, fields.type, fields.translations],
|
||||||
group: CollectionGroup.Meta,
|
group: CollectionGroups.Meta,
|
||||||
components: {
|
components: {
|
||||||
BeforeListTable: [
|
BeforeListTable: [
|
||||||
() =>
|
() =>
|
||||||
QuickFilters({
|
QuickFilters({
|
||||||
route: "/admin/collections/keys",
|
slug: Collections.Keys,
|
||||||
filters: [
|
filterGroups: [
|
||||||
{ label: "Wordings", filter: "where[type][equals]=Wordings" },
|
Object.entries(KeysTypes).map(([key, value]) => ({
|
||||||
{ label: "∅ English", filter: "where[translations.language][not_equals]=en" },
|
label: value,
|
||||||
{ label: "∅ French", filter: "where[translations.language][not_equals]=fr" },
|
filter: { where: { type: { equals: key } } },
|
||||||
|
})),
|
||||||
|
Object.entries(LanguageCodes).map(([key, value]) => ({
|
||||||
|
label: `∅ ${value}`,
|
||||||
|
filter: { where: { "translations.language": { not_equals: key } } },
|
||||||
|
})),
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
hooks: {
|
hooks: {
|
||||||
beforeDuplicate: beforeDuplicateAddCopyTo(fields.slug),
|
beforeDuplicate: beforeDuplicateAddCopyTo(fields.name),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
access: {
|
access: {
|
||||||
create: mustBeAdmin,
|
create: mustBeAdmin,
|
||||||
delete: mustBeAdmin,
|
delete: mustBeAdmin,
|
||||||
},
|
},
|
||||||
|
hooks: {
|
||||||
|
beforeValidate: [
|
||||||
|
async ({ data: { name, type } }) => {
|
||||||
|
const result = await payload.find({
|
||||||
|
collection: Collections.Keys,
|
||||||
|
where: { name: { equals: name }, type: { equals: type } },
|
||||||
|
});
|
||||||
|
if (result.docs.length > 0) {
|
||||||
|
throw new Error(
|
||||||
|
`A Key of type "${KeysTypes[type]}" already exists with the name "${name}"`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
endpoints: [importFromStrapi],
|
||||||
timestamps: false,
|
timestamps: false,
|
||||||
versions: false,
|
versions: false,
|
||||||
fields: [
|
fields: [
|
||||||
slugField({ name: fields.slug }),
|
{
|
||||||
|
name: fields.name,
|
||||||
|
type: "text",
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: fields.type,
|
name: fields.type,
|
||||||
type: "select",
|
type: "select",
|
||||||
|
@ -65,15 +91,20 @@ export const Keys: CollectionConfig = buildCollectionConfig(
|
||||||
name: fields.translations,
|
name: fields.translations,
|
||||||
interfaceName: "CategoryTranslations",
|
interfaceName: "CategoryTranslations",
|
||||||
admin: {
|
admin: {
|
||||||
useAsTitle: fields.name,
|
useAsTitle: fields.translationsName,
|
||||||
},
|
},
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
type: "row",
|
type: "row",
|
||||||
fields: [
|
fields: [
|
||||||
{ name: fields.name, type: "text", required: true, admin: { width: "50%" } },
|
|
||||||
{
|
{
|
||||||
name: fields.short,
|
name: fields.translationsName,
|
||||||
|
type: "text",
|
||||||
|
required: true,
|
||||||
|
admin: { width: "50%" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: fields.translationsShort,
|
||||||
type: "text",
|
type: "text",
|
||||||
admin: {
|
admin: {
|
||||||
condition: (data: Partial<Key>) =>
|
condition: (data: Partial<Key>) =>
|
||||||
|
|
|
@ -0,0 +1,283 @@
|
||||||
|
import payload from "payload";
|
||||||
|
import { CollectionConfig } from "payload/types";
|
||||||
|
import { Collections } from "../../../constants";
|
||||||
|
import {
|
||||||
|
getAllStrapiEntries,
|
||||||
|
importStrapiEntries,
|
||||||
|
} from "../../../endpoints/createStrapiImportEndpoint";
|
||||||
|
import { Key } from "../../../types/collections";
|
||||||
|
import { isDefined } from "../../../utils/asserts";
|
||||||
|
import { formatToCamelCase } from "../../../utils/string";
|
||||||
|
import { PayloadCreateData } from "../../../utils/types";
|
||||||
|
|
||||||
|
const importStrapiWordings: typeof importStrapiEntries = async ({
|
||||||
|
payload: payloadParams,
|
||||||
|
strapi: strapiParams,
|
||||||
|
user,
|
||||||
|
}) => {
|
||||||
|
const rawEntries = await getAllStrapiEntries<any>(strapiParams.collection, strapiParams.params);
|
||||||
|
|
||||||
|
const { ui_language, createdAt, updatedAt, ...otherKeys } = rawEntries[0].attributes;
|
||||||
|
|
||||||
|
const entries: PayloadCreateData<Key>[] = Object.keys(otherKeys).map((key) => ({
|
||||||
|
name: formatToCamelCase(key),
|
||||||
|
type: "Wordings",
|
||||||
|
translations: rawEntries
|
||||||
|
.map((entry) => ({
|
||||||
|
language: entry.attributes.ui_language.data.attributes.code,
|
||||||
|
name: entry.attributes[key],
|
||||||
|
}))
|
||||||
|
.filter(({ name }) => isDefined(name) && name !== ""),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const errors = [];
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
entries.map(async (entry) => {
|
||||||
|
try {
|
||||||
|
await payload.create({
|
||||||
|
collection: payloadParams.collection,
|
||||||
|
data: entry,
|
||||||
|
user,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.warn(e);
|
||||||
|
errors.push(`${e.name} with ${entry.name}`);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
return { count: entries.length, errors };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const importFromStrapi: CollectionConfig["endpoints"][number] = {
|
||||||
|
method: "get",
|
||||||
|
path: "/strapi",
|
||||||
|
handler: async (req, res) => {
|
||||||
|
if (!req.user) {
|
||||||
|
return res.status(403).send({
|
||||||
|
errors: [
|
||||||
|
{
|
||||||
|
message: "You are not allowed to perform this action.",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const { count: categoriesCount, errors: categoriesErrors } = await importStrapiEntries<Key>({
|
||||||
|
strapi: {
|
||||||
|
collection: "categories",
|
||||||
|
params: { populate: { titles: { populate: "language" } } },
|
||||||
|
},
|
||||||
|
payload: {
|
||||||
|
collection: Collections.Keys,
|
||||||
|
convert: ({ slug, titles }) => ({
|
||||||
|
name: slug,
|
||||||
|
type: "Categories",
|
||||||
|
translations: titles.map(({ title, short, language }) => ({
|
||||||
|
name: title,
|
||||||
|
short,
|
||||||
|
language: language.data.attributes.code,
|
||||||
|
})),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
user: req.user,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { count: contentTypesCount, errors: contentTypesErrors } = await importStrapiEntries<Key>(
|
||||||
|
{
|
||||||
|
strapi: {
|
||||||
|
collection: "content-types",
|
||||||
|
params: { populate: { titles: { populate: "language" } } },
|
||||||
|
},
|
||||||
|
payload: {
|
||||||
|
collection: Collections.Keys,
|
||||||
|
convert: ({ slug, titles }) => ({
|
||||||
|
name: slug,
|
||||||
|
type: "Contents",
|
||||||
|
translations: titles.map(({ title, language }) => ({
|
||||||
|
name: title,
|
||||||
|
language: language.data.attributes.code,
|
||||||
|
})),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
user: req.user,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const { count: gamePlatformsCount, errors: gamePlatformsErrors } =
|
||||||
|
await importStrapiEntries<Key>({
|
||||||
|
strapi: {
|
||||||
|
collection: "game-platforms",
|
||||||
|
params: { populate: { titles: { populate: "language" } } },
|
||||||
|
},
|
||||||
|
payload: {
|
||||||
|
collection: Collections.Keys,
|
||||||
|
convert: ({ slug, titles }) => ({
|
||||||
|
name: slug,
|
||||||
|
type: "GamePlatforms",
|
||||||
|
translations: titles.map(({ title, short, language }) => ({
|
||||||
|
name: title,
|
||||||
|
short,
|
||||||
|
language: language.data.attributes.code,
|
||||||
|
})),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
user: req.user,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { count: libraryCount, errors: libraryErrors } = await importStrapiEntries<Key>({
|
||||||
|
strapi: {
|
||||||
|
collection: "metadata-types",
|
||||||
|
params: { populate: { titles: { populate: "language" } } },
|
||||||
|
},
|
||||||
|
payload: {
|
||||||
|
collection: Collections.Keys,
|
||||||
|
convert: ({ slug, titles }) => ({
|
||||||
|
name: slug,
|
||||||
|
type: "Library",
|
||||||
|
translations: titles.map(({ title, language }) => ({
|
||||||
|
name: title,
|
||||||
|
language: language.data.attributes.code,
|
||||||
|
})),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
user: req.user,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { count: libraryAudioCount, errors: libraryAudioErrors } = await importStrapiEntries<Key>(
|
||||||
|
{
|
||||||
|
strapi: {
|
||||||
|
collection: "audio-subtypes",
|
||||||
|
params: { populate: { titles: { populate: "language" } } },
|
||||||
|
},
|
||||||
|
payload: {
|
||||||
|
collection: Collections.Keys,
|
||||||
|
convert: ({ slug, titles }) => ({
|
||||||
|
name: slug,
|
||||||
|
type: "LibraryAudio",
|
||||||
|
translations: titles.map(({ title, language }) => ({
|
||||||
|
name: title,
|
||||||
|
language: language.data.attributes.code,
|
||||||
|
})),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
user: req.user,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const { count: libraryGroupCount, errors: libraryGroupErrors } = await importStrapiEntries<Key>(
|
||||||
|
{
|
||||||
|
strapi: {
|
||||||
|
collection: "group-subtypes",
|
||||||
|
params: { populate: { titles: { populate: "language" } } },
|
||||||
|
},
|
||||||
|
payload: {
|
||||||
|
collection: Collections.Keys,
|
||||||
|
convert: ({ slug, titles }) => ({
|
||||||
|
name: slug,
|
||||||
|
type: "LibraryGroup",
|
||||||
|
translations: titles.map(({ title, language }) => ({
|
||||||
|
name: title,
|
||||||
|
language: language.data.attributes.code,
|
||||||
|
})),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
user: req.user,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const { count: libraryTextualCount, errors: libraryTextualErrors } =
|
||||||
|
await importStrapiEntries<Key>({
|
||||||
|
strapi: {
|
||||||
|
collection: "textual-subtypes",
|
||||||
|
params: { populate: { titles: { populate: "language" } } },
|
||||||
|
},
|
||||||
|
payload: {
|
||||||
|
collection: Collections.Keys,
|
||||||
|
convert: ({ slug, titles }) => ({
|
||||||
|
name: slug,
|
||||||
|
type: "LibraryTextual",
|
||||||
|
translations: titles.map(({ title, language }) => ({
|
||||||
|
name: title,
|
||||||
|
language: language.data.attributes.code,
|
||||||
|
})),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
user: req.user,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { count: libraryVideoCount, errors: libraryVideoErrors } = await importStrapiEntries<Key>(
|
||||||
|
{
|
||||||
|
strapi: {
|
||||||
|
collection: "video-subtypes",
|
||||||
|
params: { populate: { titles: { populate: "language" } } },
|
||||||
|
},
|
||||||
|
payload: {
|
||||||
|
collection: Collections.Keys,
|
||||||
|
convert: ({ slug, titles }) => ({
|
||||||
|
name: slug,
|
||||||
|
type: "LibraryVideo",
|
||||||
|
translations: titles.map(({ title, language }) => ({
|
||||||
|
name: title,
|
||||||
|
language: language.data.attributes.code,
|
||||||
|
})),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
user: req.user,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const { count: weaponsCount, errors: weaponsErrors } = await importStrapiEntries<Key>({
|
||||||
|
strapi: {
|
||||||
|
collection: "weapon-story-types",
|
||||||
|
params: { populate: { translations: { populate: "language" } } },
|
||||||
|
},
|
||||||
|
payload: {
|
||||||
|
collection: Collections.Keys,
|
||||||
|
convert: ({ slug, translations }) => ({
|
||||||
|
name: slug,
|
||||||
|
type: "Weapons",
|
||||||
|
translations: translations.map(({ name, language }) => ({
|
||||||
|
name,
|
||||||
|
language: language.data.attributes.code,
|
||||||
|
})),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
user: req.user,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { count: wordingsCount, errors: wordingsErrors } = await importStrapiWordings({
|
||||||
|
strapi: { collection: "website-interfaces", params: { populate: "ui_language" } },
|
||||||
|
payload: { collection: Collections.Keys, convert: (strapiObject) => strapiObject },
|
||||||
|
user: req.user,
|
||||||
|
});
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
message: `${
|
||||||
|
categoriesCount +
|
||||||
|
contentTypesCount +
|
||||||
|
gamePlatformsCount +
|
||||||
|
libraryCount +
|
||||||
|
libraryAudioCount +
|
||||||
|
libraryGroupCount +
|
||||||
|
libraryTextualCount +
|
||||||
|
libraryVideoCount +
|
||||||
|
weaponsCount +
|
||||||
|
wordingsCount
|
||||||
|
} entries have been added successfully.`,
|
||||||
|
errors: {
|
||||||
|
categoriesErrors,
|
||||||
|
contentTypesErrors,
|
||||||
|
gamePlatformsErrors,
|
||||||
|
libraryErrors,
|
||||||
|
libraryAudioErrors,
|
||||||
|
libraryGroupErrors,
|
||||||
|
libraryTextualErrors,
|
||||||
|
libraryVideoErrors,
|
||||||
|
weaponsErrors,
|
||||||
|
wordingsErrors,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
|
@ -1,6 +1,9 @@
|
||||||
|
import { text } from "payload/dist/fields/validations";
|
||||||
import { mustBeAdmin } from "../../accesses/mustBeAdmin";
|
import { mustBeAdmin } from "../../accesses/mustBeAdmin";
|
||||||
import { CollectionGroup } from "../../constants";
|
import { publicAccess } from "../../accesses/publicAccess";
|
||||||
|
import { CollectionGroups, Collections } from "../../constants";
|
||||||
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
||||||
|
import { importFromStrapi } from "./endpoints/importFromStrapi";
|
||||||
|
|
||||||
const fields = {
|
const fields = {
|
||||||
id: "id",
|
id: "id",
|
||||||
|
@ -8,6 +11,7 @@ const fields = {
|
||||||
} as const satisfies Record<string, string>;
|
} as const satisfies Record<string, string>;
|
||||||
|
|
||||||
export const Languages = buildCollectionConfig(
|
export const Languages = buildCollectionConfig(
|
||||||
|
Collections.Languages,
|
||||||
{
|
{
|
||||||
singular: "Language",
|
singular: "Language",
|
||||||
plural: "Languages",
|
plural: "Languages",
|
||||||
|
@ -18,21 +22,23 @@ export const Languages = buildCollectionConfig(
|
||||||
useAsTitle: fields.name,
|
useAsTitle: fields.name,
|
||||||
defaultColumns: [fields.name, fields.id],
|
defaultColumns: [fields.name, fields.id],
|
||||||
disableDuplicate: true,
|
disableDuplicate: true,
|
||||||
group: CollectionGroup.Meta,
|
group: CollectionGroups.Meta,
|
||||||
|
pagination: { defaultLimit: 100 },
|
||||||
},
|
},
|
||||||
access: { create: mustBeAdmin, update: mustBeAdmin },
|
access: { create: mustBeAdmin, update: mustBeAdmin, read: publicAccess },
|
||||||
timestamps: false,
|
timestamps: false,
|
||||||
|
endpoints: [importFromStrapi],
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
name: fields.id,
|
name: fields.id,
|
||||||
type: "text",
|
type: "text",
|
||||||
unique: true,
|
unique: true,
|
||||||
required: true,
|
required: true,
|
||||||
validate: (value) => {
|
validate: (value, options) => {
|
||||||
if (/^[a-z]{2}(-[a-z]{2})?$/g.test(value)) {
|
if (!/^[a-z]{2}(-[a-z]{2})?$/g.test(value)) {
|
||||||
return true;
|
return "The code must be a valid BCP 47 language tag and lowercase (i.e: en, pt-pt, fr, zh-tw...)";
|
||||||
}
|
}
|
||||||
return "The code must be a valid IETF language tag and lowercase (i.e: en, pt-pt, fr, zh-tw...)";
|
return text(value, options);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
import { Collections } from "../../../constants";
|
||||||
|
import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint";
|
||||||
|
import { Language } from "../../../types/collections";
|
||||||
|
|
||||||
|
export const importFromStrapi = createStrapiImportEndpoint<Language>({
|
||||||
|
strapi: {
|
||||||
|
collection: "languages",
|
||||||
|
params: {},
|
||||||
|
},
|
||||||
|
payload: {
|
||||||
|
collection: Collections.Languages,
|
||||||
|
convert: ({ code, name }) => ({ id: code, name }),
|
||||||
|
},
|
||||||
|
});
|
|
@ -1,24 +1,21 @@
|
||||||
import {
|
import {
|
||||||
CollectionGroup,
|
CollectionGroups,
|
||||||
|
Collections,
|
||||||
KeysTypes,
|
KeysTypes,
|
||||||
LibraryItemsTextualBindingTypes,
|
LibraryItemsTextualBindingTypes,
|
||||||
LibraryItemsTextualPageOrders,
|
LibraryItemsTextualPageOrders,
|
||||||
LibraryItemsTypes,
|
LibraryItemsTypes,
|
||||||
} from "../../constants";
|
} from "../../constants";
|
||||||
import { slugField } from "../../fields/slugField/slugField";
|
|
||||||
import { imageField } from "../../fields/imageField/imageField";
|
import { imageField } from "../../fields/imageField/imageField";
|
||||||
import { LibraryItemThumbnails } from "../LibraryItemThumbnails/LibraryItemThumbnails";
|
|
||||||
import { LibraryItem } from "../../types/collections";
|
|
||||||
import { Keys } from "../Keys/Keys";
|
|
||||||
import { Languages } from "../Languages/Languages";
|
|
||||||
import { buildVersionedCollectionConfig } from "../../utils/versionedCollectionConfig";
|
|
||||||
import { beforeDuplicateAddCopyTo } from "../../hooks/beforeDuplicateAddCopyTo";
|
|
||||||
import { beforeDuplicateUnpublish } from "../../hooks/beforeDuplicateUnpublish";
|
|
||||||
import { beforeDuplicatePiping } from "../../hooks/beforeDuplicatePiping";
|
|
||||||
import { Currencies } from "../Currencies/Currencies";
|
|
||||||
import { optionalGroupField } from "../../fields/optionalGroupField/optionalGroupField";
|
import { optionalGroupField } from "../../fields/optionalGroupField/optionalGroupField";
|
||||||
|
import { slugField } from "../../fields/slugField/slugField";
|
||||||
|
import { beforeDuplicateAddCopyTo } from "../../hooks/beforeDuplicateAddCopyTo";
|
||||||
|
import { beforeDuplicatePiping } from "../../hooks/beforeDuplicatePiping";
|
||||||
|
import { beforeDuplicateUnpublish } from "../../hooks/beforeDuplicateUnpublish";
|
||||||
|
import { LibraryItem } from "../../types/collections";
|
||||||
|
import { buildVersionedCollectionConfig } from "../../utils/versionedCollectionConfig";
|
||||||
import { RowLabel } from "./components/RowLabel";
|
import { RowLabel } from "./components/RowLabel";
|
||||||
import { getSlug } from "./endpoints/getSlug";
|
import { getBySlug } from "./endpoints/getBySlug";
|
||||||
|
|
||||||
const fields = {
|
const fields = {
|
||||||
status: "status",
|
status: "status",
|
||||||
|
@ -57,21 +54,29 @@ const fields = {
|
||||||
scansDustjacketFront: "front",
|
scansDustjacketFront: "front",
|
||||||
scansDustjacketSpine: "spine",
|
scansDustjacketSpine: "spine",
|
||||||
scansDustjacketBack: "back",
|
scansDustjacketBack: "back",
|
||||||
scansObibelt: "obibelt",
|
scansObi: "obi",
|
||||||
scansObibeltFront: "front",
|
scansObiFront: "front",
|
||||||
scansObibeltSpine: "spine",
|
scansObiSpine: "spine",
|
||||||
scansObibeltBack: "back",
|
scansObiBack: "back",
|
||||||
scansPages: "pages",
|
scansPages: "pages",
|
||||||
scansPagesPage: "page",
|
scansPagesPage: "page",
|
||||||
scansPagesImage: "image",
|
scansPagesImage: "image",
|
||||||
|
contents: "contents",
|
||||||
|
contentsContent: "content",
|
||||||
|
contentsPageStart: "pageStart",
|
||||||
|
contentsPageEnd: "pageEnd",
|
||||||
|
contentsTimeStart: "timeStart",
|
||||||
|
contentsTimeEnd: "timeEnd",
|
||||||
|
contentsNote: "note",
|
||||||
} as const satisfies Record<string, string>;
|
} as const satisfies Record<string, string>;
|
||||||
|
|
||||||
export const LibraryItems = buildVersionedCollectionConfig(
|
export const LibraryItems = buildVersionedCollectionConfig(
|
||||||
|
Collections.LibraryItems,
|
||||||
{
|
{
|
||||||
singular: "Library Item",
|
singular: "Library Item",
|
||||||
plural: "Library Items",
|
plural: "Library Items",
|
||||||
},
|
},
|
||||||
({ slug }) => ({
|
() => ({
|
||||||
defaultSort: fields.slug,
|
defaultSort: fields.slug,
|
||||||
admin: {
|
admin: {
|
||||||
useAsTitle: fields.slug,
|
useAsTitle: fields.slug,
|
||||||
|
@ -79,7 +84,7 @@ export const LibraryItems = buildVersionedCollectionConfig(
|
||||||
"A comprehensive list of all Yokoverse’s side materials (books, novellas, artbooks, \
|
"A comprehensive list of all Yokoverse’s side materials (books, novellas, artbooks, \
|
||||||
stage plays, manga, drama CDs, and comics).",
|
stage plays, manga, drama CDs, and comics).",
|
||||||
defaultColumns: [fields.slug, fields.thumbnail, fields.status],
|
defaultColumns: [fields.slug, fields.thumbnail, fields.status],
|
||||||
group: CollectionGroup.Collections,
|
group: CollectionGroups.Collections,
|
||||||
hooks: {
|
hooks: {
|
||||||
beforeDuplicate: beforeDuplicatePiping([
|
beforeDuplicate: beforeDuplicatePiping([
|
||||||
beforeDuplicateUnpublish,
|
beforeDuplicateUnpublish,
|
||||||
|
@ -88,7 +93,7 @@ export const LibraryItems = buildVersionedCollectionConfig(
|
||||||
},
|
},
|
||||||
preview: (doc) => `https://accords-library.com/library/${doc.slug}`,
|
preview: (doc) => `https://accords-library.com/library/${doc.slug}`,
|
||||||
},
|
},
|
||||||
endpoints: [getSlug(slug)],
|
endpoints: [getBySlug],
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
type: "row",
|
type: "row",
|
||||||
|
@ -96,7 +101,7 @@ export const LibraryItems = buildVersionedCollectionConfig(
|
||||||
slugField({ name: fields.slug, admin: { width: "50%" } }),
|
slugField({ name: fields.slug, admin: { width: "50%" } }),
|
||||||
imageField({
|
imageField({
|
||||||
name: fields.thumbnail,
|
name: fields.thumbnail,
|
||||||
relationTo: LibraryItemThumbnails.slug,
|
relationTo: Collections.LibraryItemsThumbnails,
|
||||||
admin: { width: "50%" },
|
admin: { width: "50%" },
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
@ -167,17 +172,17 @@ export const LibraryItems = buildVersionedCollectionConfig(
|
||||||
fields: [
|
fields: [
|
||||||
imageField({
|
imageField({
|
||||||
name: fields.scansCoverFront,
|
name: fields.scansCoverFront,
|
||||||
relationTo: LibraryItemThumbnails.slug,
|
relationTo: Collections.LibraryItemsThumbnails,
|
||||||
admin: { width: "33%" },
|
admin: { width: "33%" },
|
||||||
}),
|
}),
|
||||||
imageField({
|
imageField({
|
||||||
name: fields.scansCoverSpine,
|
name: fields.scansCoverSpine,
|
||||||
relationTo: LibraryItemThumbnails.slug,
|
relationTo: Collections.LibraryItemsThumbnails,
|
||||||
admin: { width: "33%" },
|
admin: { width: "33%" },
|
||||||
}),
|
}),
|
||||||
imageField({
|
imageField({
|
||||||
name: fields.scansCoverBack,
|
name: fields.scansCoverBack,
|
||||||
relationTo: LibraryItemThumbnails.slug,
|
relationTo: Collections.LibraryItemsThumbnails,
|
||||||
admin: { width: "33%" },
|
admin: { width: "33%" },
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
@ -188,23 +193,28 @@ export const LibraryItems = buildVersionedCollectionConfig(
|
||||||
name: fields.scansDustjacket,
|
name: fields.scansDustjacket,
|
||||||
label: "Dust Jacket",
|
label: "Dust Jacket",
|
||||||
labels: { singular: "Dust Jacket", plural: "Dust Jackets" },
|
labels: { singular: "Dust Jacket", plural: "Dust Jackets" },
|
||||||
|
admin: {
|
||||||
|
description:
|
||||||
|
"The dust jacket of a book is the detachable outer cover with folded \
|
||||||
|
flaps that hold it to the front and back book covers",
|
||||||
|
},
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
type: "row",
|
type: "row",
|
||||||
fields: [
|
fields: [
|
||||||
imageField({
|
imageField({
|
||||||
name: fields.scansDustjacketFront,
|
name: fields.scansDustjacketFront,
|
||||||
relationTo: LibraryItemThumbnails.slug,
|
relationTo: Collections.LibraryItemsThumbnails,
|
||||||
admin: { width: "33%" },
|
admin: { width: "33%" },
|
||||||
}),
|
}),
|
||||||
imageField({
|
imageField({
|
||||||
name: fields.scansDustjacketSpine,
|
name: fields.scansDustjacketSpine,
|
||||||
relationTo: LibraryItemThumbnails.slug,
|
relationTo: Collections.LibraryItemsThumbnails,
|
||||||
admin: { width: "33%" },
|
admin: { width: "33%" },
|
||||||
}),
|
}),
|
||||||
imageField({
|
imageField({
|
||||||
name: fields.scansDustjacketBack,
|
name: fields.scansDustjacketBack,
|
||||||
relationTo: LibraryItemThumbnails.slug,
|
relationTo: Collections.LibraryItemsThumbnails,
|
||||||
admin: { width: "33%" },
|
admin: { width: "33%" },
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
@ -212,26 +222,31 @@ export const LibraryItems = buildVersionedCollectionConfig(
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
optionalGroupField({
|
optionalGroupField({
|
||||||
name: fields.scansObibelt,
|
name: fields.scansObi,
|
||||||
label: "Obi Belt",
|
label: "Obi",
|
||||||
labels: { singular: "Obi Belt", plural: "Obi Belts" },
|
labels: { singular: "Obi Belt", plural: "Obi Belts" },
|
||||||
|
admin: {
|
||||||
|
description:
|
||||||
|
"An obi is a strip of paper looped around a book or other product. \
|
||||||
|
it typically add marketing claims, or other relevant information about the product.",
|
||||||
|
},
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
type: "row",
|
type: "row",
|
||||||
fields: [
|
fields: [
|
||||||
imageField({
|
imageField({
|
||||||
name: fields.scansObibeltFront,
|
name: fields.scansObiFront,
|
||||||
relationTo: LibraryItemThumbnails.slug,
|
relationTo: Collections.LibraryItemsThumbnails,
|
||||||
admin: { width: "33%" },
|
admin: { width: "33%" },
|
||||||
}),
|
}),
|
||||||
imageField({
|
imageField({
|
||||||
name: fields.scansObibeltSpine,
|
name: fields.scansObiSpine,
|
||||||
relationTo: LibraryItemThumbnails.slug,
|
relationTo: Collections.LibraryItemsThumbnails,
|
||||||
admin: { width: "33%" },
|
admin: { width: "33%" },
|
||||||
}),
|
}),
|
||||||
imageField({
|
imageField({
|
||||||
name: fields.scansObibeltBack,
|
name: fields.scansObiBack,
|
||||||
relationTo: LibraryItemThumbnails.slug,
|
relationTo: Collections.LibraryItemsThumbnails,
|
||||||
admin: { width: "33%" },
|
admin: { width: "33%" },
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
@ -243,6 +258,9 @@ export const LibraryItems = buildVersionedCollectionConfig(
|
||||||
type: "array",
|
type: "array",
|
||||||
admin: {
|
admin: {
|
||||||
initCollapsed: true,
|
initCollapsed: true,
|
||||||
|
description:
|
||||||
|
"Make sure the page number corresponds to the page number written on \
|
||||||
|
the scan. You can use negative page numbers if necessary.",
|
||||||
components: {
|
components: {
|
||||||
RowLabel: ({ data }) => RowLabel(data),
|
RowLabel: ({ data }) => RowLabel(data),
|
||||||
},
|
},
|
||||||
|
@ -259,7 +277,7 @@ export const LibraryItems = buildVersionedCollectionConfig(
|
||||||
},
|
},
|
||||||
imageField({
|
imageField({
|
||||||
name: fields.scansPagesImage,
|
name: fields.scansPagesImage,
|
||||||
relationTo: LibraryItemThumbnails.slug,
|
relationTo: Collections.LibraryItemsThumbnails,
|
||||||
required: true,
|
required: true,
|
||||||
admin: { width: "66%" },
|
admin: { width: "66%" },
|
||||||
}),
|
}),
|
||||||
|
@ -314,7 +332,7 @@ export const LibraryItems = buildVersionedCollectionConfig(
|
||||||
{
|
{
|
||||||
name: fields.priceCurrency,
|
name: fields.priceCurrency,
|
||||||
type: "relationship",
|
type: "relationship",
|
||||||
relationTo: Currencies.slug,
|
relationTo: Collections.Currencies,
|
||||||
required: true,
|
required: true,
|
||||||
admin: { allowCreate: false, width: "50%" },
|
admin: { allowCreate: false, width: "50%" },
|
||||||
},
|
},
|
||||||
|
@ -344,7 +362,7 @@ export const LibraryItems = buildVersionedCollectionConfig(
|
||||||
name: fields.textualSubtype,
|
name: fields.textualSubtype,
|
||||||
label: "Subtype",
|
label: "Subtype",
|
||||||
type: "relationship",
|
type: "relationship",
|
||||||
relationTo: [Keys.slug],
|
relationTo: [Collections.Keys],
|
||||||
filterOptions: { type: { equals: KeysTypes.LibraryTextual } },
|
filterOptions: { type: { equals: KeysTypes.LibraryTextual } },
|
||||||
hasMany: true,
|
hasMany: true,
|
||||||
admin: { allowCreate: false, width: "50%" },
|
admin: { allowCreate: false, width: "50%" },
|
||||||
|
@ -352,7 +370,7 @@ export const LibraryItems = buildVersionedCollectionConfig(
|
||||||
{
|
{
|
||||||
name: fields.textualLanguages,
|
name: fields.textualLanguages,
|
||||||
type: "relationship",
|
type: "relationship",
|
||||||
relationTo: [Languages.slug],
|
relationTo: [Collections.Languages],
|
||||||
hasMany: true,
|
hasMany: true,
|
||||||
admin: { allowCreate: false, width: "50%" },
|
admin: { allowCreate: false, width: "50%" },
|
||||||
},
|
},
|
||||||
|
@ -406,7 +424,7 @@ export const LibraryItems = buildVersionedCollectionConfig(
|
||||||
name: fields.audioSubtype,
|
name: fields.audioSubtype,
|
||||||
label: "Subtype",
|
label: "Subtype",
|
||||||
type: "relationship",
|
type: "relationship",
|
||||||
relationTo: [Keys.slug],
|
relationTo: [Collections.Keys],
|
||||||
filterOptions: { type: { equals: KeysTypes.LibraryAudio } },
|
filterOptions: { type: { equals: KeysTypes.LibraryAudio } },
|
||||||
hasMany: true,
|
hasMany: true,
|
||||||
admin: { allowCreate: false, width: "50%" },
|
admin: { allowCreate: false, width: "50%" },
|
||||||
|
@ -415,6 +433,57 @@ export const LibraryItems = buildVersionedCollectionConfig(
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: fields.contents,
|
||||||
|
type: "array",
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: fields.contentsContent,
|
||||||
|
type: "relationship",
|
||||||
|
relationTo: Collections.Contents,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "row",
|
||||||
|
admin: {
|
||||||
|
condition: ({ itemType }) => {
|
||||||
|
return itemType === LibraryItemsTypes.Textual;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: fields.contentsPageStart,
|
||||||
|
type: "number",
|
||||||
|
},
|
||||||
|
{ name: fields.contentsPageEnd, type: "number" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "row",
|
||||||
|
admin: {
|
||||||
|
condition: ({ itemType }) => {
|
||||||
|
return itemType === LibraryItemsTypes.Audio || itemType === LibraryItemsTypes.Video;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: fields.contentsTimeStart,
|
||||||
|
type: "number",
|
||||||
|
},
|
||||||
|
{ name: fields.contentsTimeEnd, type: "number" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: fields.contentsNote,
|
||||||
|
type: "textarea",
|
||||||
|
admin: {
|
||||||
|
condition: ({ itemType }) => {
|
||||||
|
return itemType === LibraryItemsTypes.Game || itemType === LibraryItemsTypes.Other;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: fields.releaseDate,
|
name: fields.releaseDate,
|
||||||
type: "date",
|
type: "date",
|
||||||
|
|
|
@ -1,20 +1,22 @@
|
||||||
import { LibraryItems } from "../LibraryItems";
|
|
||||||
import { LibraryItem } from "../../../types/collections";
|
|
||||||
import cleanDeep from "clean-deep";
|
import cleanDeep from "clean-deep";
|
||||||
import { createBySlugEndpoint } from "../../../endpoints/createBySlugEndpoint";
|
import { Collections } from "../../../constants";
|
||||||
|
import { createGetByEndpoint } from "../../../endpoints/createByEndpoint";
|
||||||
|
import { LibraryItem } from "../../../types/collections";
|
||||||
|
|
||||||
type ProcessedLibraryItem = Omit<LibraryItem, "size" | "price" | "scans" | "id"> & {
|
type ProcessedLibraryItem = Omit<LibraryItem, "size" | "price" | "scans" | "id"> & {
|
||||||
size?: Omit<LibraryItem["size"][number], "id">;
|
size?: Omit<LibraryItem["size"][number], "id">;
|
||||||
price?: Omit<LibraryItem["price"][number], "id" | "currency"> & { currency: string };
|
price?: Omit<LibraryItem["price"][number], "id" | "currency"> & { currency: string };
|
||||||
scans?: Omit<LibraryItem["scans"][number], "id" | "obibelt" | "cover" | "dustjacket"> & {
|
scans?: Omit<LibraryItem["scans"][number], "id" | "obi" | "cover" | "dustjacket"> & {
|
||||||
obibelt: Omit<LibraryItem["scans"][number]["obibelt"][number], "id">;
|
obi: Omit<LibraryItem["scans"][number]["obi"][number], "id">;
|
||||||
cover: Omit<LibraryItem["scans"][number]["obibelt"][number], "id">;
|
cover: Omit<LibraryItem["scans"][number]["obi"][number], "id">;
|
||||||
dustjacket: Omit<LibraryItem["scans"][number]["obibelt"][number], "id">;
|
dustjacket: Omit<LibraryItem["scans"][number]["obi"][number], "id">;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getSlug = (collectionSlug: string) =>
|
export const getBySlug = createGetByEndpoint<LibraryItem, Partial<ProcessedLibraryItem>>(
|
||||||
createBySlugEndpoint<LibraryItem>(collectionSlug, ({ id, size, price, scans, ...otherProps }) => {
|
Collections.LibraryItems,
|
||||||
|
"slug",
|
||||||
|
async ({ id, size, price, scans, ...otherProps }) => {
|
||||||
const processedLibraryItem: ProcessedLibraryItem = {
|
const processedLibraryItem: ProcessedLibraryItem = {
|
||||||
size: processOptionalGroup(size),
|
size: processOptionalGroup(size),
|
||||||
price: processPrice(price),
|
price: processPrice(price),
|
||||||
|
@ -30,15 +32,16 @@ export const getSlug = (collectionSlug: string) =>
|
||||||
undefinedValues: true,
|
undefinedValues: true,
|
||||||
NaNValues: false,
|
NaNValues: false,
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
const processScans = (scans: LibraryItem["scans"]): ProcessedLibraryItem["scans"] => {
|
const processScans = (scans: LibraryItem["scans"]): ProcessedLibraryItem["scans"] => {
|
||||||
if (!scans || scans.length === 0) return undefined;
|
if (!scans || scans.length === 0) return undefined;
|
||||||
const { cover, dustjacket, id, obibelt, ...otherProps } = scans[0];
|
const { cover, dustjacket, id, obi, ...otherProps } = scans[0];
|
||||||
return {
|
return {
|
||||||
cover: processOptionalGroup(cover),
|
cover: processOptionalGroup(cover),
|
||||||
dustjacket: processOptionalGroup(dustjacket),
|
dustjacket: processOptionalGroup(dustjacket),
|
||||||
obibelt: processOptionalGroup(obibelt),
|
obi: processOptionalGroup(obi),
|
||||||
...otherProps,
|
...otherProps,
|
||||||
};
|
};
|
||||||
};
|
};
|
|
@ -1,4 +1,4 @@
|
||||||
import { CollectionGroup } from "../../constants";
|
import { CollectionGroups, Collections } from "../../constants";
|
||||||
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
||||||
|
|
||||||
const fields = {
|
const fields = {
|
||||||
|
@ -7,7 +7,8 @@ const fields = {
|
||||||
filesize: "filesize",
|
filesize: "filesize",
|
||||||
} as const satisfies Record<string, string>;
|
} as const satisfies Record<string, string>;
|
||||||
|
|
||||||
export const LibraryItemThumbnails = buildCollectionConfig(
|
export const LibraryItemsThumbnails = buildCollectionConfig(
|
||||||
|
Collections.LibraryItemsThumbnails,
|
||||||
{
|
{
|
||||||
singular: "Library Item Thumbnail",
|
singular: "Library Item Thumbnail",
|
||||||
plural: "Library Item Thumbnails",
|
plural: "Library Item Thumbnails",
|
||||||
|
@ -17,7 +18,7 @@ export const LibraryItemThumbnails = buildCollectionConfig(
|
||||||
admin: {
|
admin: {
|
||||||
useAsTitle: fields.filename,
|
useAsTitle: fields.filename,
|
||||||
disableDuplicate: true,
|
disableDuplicate: true,
|
||||||
group: CollectionGroup.Media,
|
group: CollectionGroups.Media,
|
||||||
},
|
},
|
||||||
upload: {
|
upload: {
|
||||||
staticDir: uploadDir,
|
staticDir: uploadDir,
|
|
@ -1,16 +1,14 @@
|
||||||
import { slugField } from "../../fields/slugField/slugField";
|
import { QuickFilters, publishStatusFilters } from "../../components/QuickFilters";
|
||||||
|
import { CollectionGroups, Collections, KeysTypes } from "../../constants";
|
||||||
import { imageField } from "../../fields/imageField/imageField";
|
import { imageField } from "../../fields/imageField/imageField";
|
||||||
import { CollectionGroup, KeysTypes } from "../../constants";
|
import { slugField } from "../../fields/slugField/slugField";
|
||||||
import { Recorders } from "../Recorders/Recorders";
|
|
||||||
import { localizedFields } from "../../fields/translatedFields/translatedFields";
|
import { localizedFields } from "../../fields/translatedFields/translatedFields";
|
||||||
import { isDefined, isUndefined } from "../../utils/asserts";
|
import { beforeDuplicateAddCopyTo } from "../../hooks/beforeDuplicateAddCopyTo";
|
||||||
import { removeTranslatorsForTranscripts } from "./hooks/beforeValidate";
|
|
||||||
import { Keys } from "../Keys/Keys";
|
|
||||||
import { PostThumbnails } from "../PostThumbnails/PostThumbnails";
|
|
||||||
import { buildVersionedCollectionConfig } from "../../utils/versionedCollectionConfig";
|
|
||||||
import { beforeDuplicatePiping } from "../../hooks/beforeDuplicatePiping";
|
import { beforeDuplicatePiping } from "../../hooks/beforeDuplicatePiping";
|
||||||
import { beforeDuplicateUnpublish } from "../../hooks/beforeDuplicateUnpublish";
|
import { beforeDuplicateUnpublish } from "../../hooks/beforeDuplicateUnpublish";
|
||||||
import { beforeDuplicateAddCopyTo } from "../../hooks/beforeDuplicateAddCopyTo";
|
import { isDefined, isUndefined } from "../../utils/asserts";
|
||||||
|
import { buildVersionedCollectionConfig } from "../../utils/versionedCollectionConfig";
|
||||||
|
import { removeTranslatorsForTranscripts } from "./hooks/beforeValidate";
|
||||||
|
|
||||||
const fields = {
|
const fields = {
|
||||||
slug: "slug",
|
slug: "slug",
|
||||||
|
@ -29,6 +27,7 @@ const fields = {
|
||||||
} as const satisfies Record<string, string>;
|
} as const satisfies Record<string, string>;
|
||||||
|
|
||||||
export const Posts = buildVersionedCollectionConfig(
|
export const Posts = buildVersionedCollectionConfig(
|
||||||
|
Collections.Posts,
|
||||||
{
|
{
|
||||||
singular: "Post",
|
singular: "Post",
|
||||||
plural: "Posts",
|
plural: "Posts",
|
||||||
|
@ -41,7 +40,16 @@ export const Posts = buildVersionedCollectionConfig(
|
||||||
"News articles written by our Recorders! Here you will find announcements about \
|
"News articles written by our Recorders! Here you will find announcements about \
|
||||||
new merch/items releases, guides, theories, unboxings, showcases...",
|
new merch/items releases, guides, theories, unboxings, showcases...",
|
||||||
defaultColumns: [fields.slug, fields.thumbnail, fields.categories],
|
defaultColumns: [fields.slug, fields.thumbnail, fields.categories],
|
||||||
group: CollectionGroup.Collections,
|
group: CollectionGroups.Collections,
|
||||||
|
components: {
|
||||||
|
BeforeListTable: [
|
||||||
|
() =>
|
||||||
|
QuickFilters({
|
||||||
|
slug: Collections.Posts,
|
||||||
|
filterGroups: [publishStatusFilters],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
hooks: {
|
hooks: {
|
||||||
beforeDuplicate: beforeDuplicatePiping([
|
beforeDuplicate: beforeDuplicatePiping([
|
||||||
beforeDuplicateUnpublish,
|
beforeDuplicateUnpublish,
|
||||||
|
@ -60,7 +68,7 @@ export const Posts = buildVersionedCollectionConfig(
|
||||||
slugField({ name: fields.slug, admin: { width: "50%" } }),
|
slugField({ name: fields.slug, admin: { width: "50%" } }),
|
||||||
imageField({
|
imageField({
|
||||||
name: fields.thumbnail,
|
name: fields.thumbnail,
|
||||||
relationTo: PostThumbnails.slug,
|
relationTo: Collections.PostsThumbnails,
|
||||||
admin: { width: "50%" },
|
admin: { width: "50%" },
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
@ -71,7 +79,7 @@ export const Posts = buildVersionedCollectionConfig(
|
||||||
{
|
{
|
||||||
name: fields.authors,
|
name: fields.authors,
|
||||||
type: "relationship",
|
type: "relationship",
|
||||||
relationTo: [Recorders.slug],
|
relationTo: [Collections.Recorders],
|
||||||
required: true,
|
required: true,
|
||||||
minRows: 1,
|
minRows: 1,
|
||||||
hasMany: true,
|
hasMany: true,
|
||||||
|
@ -80,7 +88,7 @@ export const Posts = buildVersionedCollectionConfig(
|
||||||
{
|
{
|
||||||
name: fields.categories,
|
name: fields.categories,
|
||||||
type: "relationship",
|
type: "relationship",
|
||||||
relationTo: [Keys.slug],
|
relationTo: [Collections.Keys],
|
||||||
filterOptions: { type: { equals: KeysTypes.Categories } },
|
filterOptions: { type: { equals: KeysTypes.Categories } },
|
||||||
hasMany: true,
|
hasMany: true,
|
||||||
admin: { allowCreate: false, width: "35%" },
|
admin: { allowCreate: false, width: "35%" },
|
||||||
|
@ -101,7 +109,7 @@ export const Posts = buildVersionedCollectionConfig(
|
||||||
{
|
{
|
||||||
name: fields.translators,
|
name: fields.translators,
|
||||||
type: "relationship",
|
type: "relationship",
|
||||||
relationTo: Recorders.slug,
|
relationTo: Collections.Recorders,
|
||||||
hasMany: true,
|
hasMany: true,
|
||||||
admin: {
|
admin: {
|
||||||
condition: (_, siblingData) => {
|
condition: (_, siblingData) => {
|
||||||
|
@ -134,7 +142,7 @@ export const Posts = buildVersionedCollectionConfig(
|
||||||
{
|
{
|
||||||
name: fields.proofreaders,
|
name: fields.proofreaders,
|
||||||
type: "relationship",
|
type: "relationship",
|
||||||
relationTo: Recorders.slug,
|
relationTo: Collections.Recorders,
|
||||||
hasMany: true,
|
hasMany: true,
|
||||||
admin: { width: "50%" },
|
admin: { width: "50%" },
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { CollectionGroup } from "../../constants";
|
import { CollectionGroups, Collections } from "../../constants";
|
||||||
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
||||||
|
|
||||||
const fields = {
|
const fields = {
|
||||||
|
@ -7,7 +7,8 @@ const fields = {
|
||||||
filesize: "filesize",
|
filesize: "filesize",
|
||||||
} as const satisfies Record<string, string>;
|
} as const satisfies Record<string, string>;
|
||||||
|
|
||||||
export const PostThumbnails = buildCollectionConfig(
|
export const PostsThumbnails = buildCollectionConfig(
|
||||||
|
Collections.PostsThumbnails,
|
||||||
{
|
{
|
||||||
singular: "Post Thumbnail",
|
singular: "Post Thumbnail",
|
||||||
plural: "Post Thumbnails",
|
plural: "Post Thumbnails",
|
||||||
|
@ -17,7 +18,7 @@ export const PostThumbnails = buildCollectionConfig(
|
||||||
admin: {
|
admin: {
|
||||||
useAsTitle: fields.filename,
|
useAsTitle: fields.filename,
|
||||||
disableDuplicate: true,
|
disableDuplicate: true,
|
||||||
group: CollectionGroup.Media,
|
group: CollectionGroups.Media,
|
||||||
},
|
},
|
||||||
upload: {
|
upload: {
|
||||||
staticDir: uploadDir,
|
staticDir: uploadDir,
|
|
@ -1,13 +1,12 @@
|
||||||
import { localizedFields } from "../../fields/translatedFields/translatedFields";
|
|
||||||
import { Languages } from "../Languages/Languages";
|
|
||||||
import { CollectionGroup, RecordersRoles } from "../../constants";
|
|
||||||
import { RecorderThumbnails } from "../RecorderThumbnails/RecorderThumbnails";
|
|
||||||
import { imageField } from "../../fields/imageField/imageField";
|
|
||||||
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
|
||||||
import { mustBeAdmin } from "../../accesses/mustBeAdmin";
|
|
||||||
import { mustBeAdminOrSelf } from "../../accesses/collections/mustBeAdminOrSelf";
|
import { mustBeAdminOrSelf } from "../../accesses/collections/mustBeAdminOrSelf";
|
||||||
import { beforeLoginMustHaveAtLeastOneRole } from "./hooks/beforeLoginMustHaveAtLeastOneRole";
|
import { mustBeAdmin } from "../../accesses/mustBeAdmin";
|
||||||
import { QuickFilters } from "../../components/QuickFilters";
|
import { QuickFilters } from "../../components/QuickFilters";
|
||||||
|
import { CollectionGroups, Collections, RecordersRoles } from "../../constants";
|
||||||
|
import { imageField } from "../../fields/imageField/imageField";
|
||||||
|
import { localizedFields } from "../../fields/translatedFields/translatedFields";
|
||||||
|
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
||||||
|
import { importFromStrapi } from "./endpoints/importFromStrapi";
|
||||||
|
import { beforeLoginMustHaveAtLeastOneRole } from "./hooks/beforeLoginMustHaveAtLeastOneRole";
|
||||||
|
|
||||||
const fields = {
|
const fields = {
|
||||||
username: "username",
|
username: "username",
|
||||||
|
@ -20,6 +19,7 @@ const fields = {
|
||||||
} as const satisfies Record<string, string>;
|
} as const satisfies Record<string, string>;
|
||||||
|
|
||||||
export const Recorders = buildCollectionConfig(
|
export const Recorders = buildCollectionConfig(
|
||||||
|
Collections.Recorders,
|
||||||
{
|
{
|
||||||
singular: "Recorder",
|
singular: "Recorder",
|
||||||
plural: "Recorders",
|
plural: "Recorders",
|
||||||
|
@ -40,17 +40,25 @@ export const Recorders = buildCollectionConfig(
|
||||||
fields.role,
|
fields.role,
|
||||||
],
|
],
|
||||||
disableDuplicate: true,
|
disableDuplicate: true,
|
||||||
group: CollectionGroup.Meta,
|
group: CollectionGroups.Meta,
|
||||||
components: {
|
components: {
|
||||||
BeforeListTable: [
|
BeforeListTable: [
|
||||||
() =>
|
() =>
|
||||||
QuickFilters({
|
QuickFilters({
|
||||||
route: "/admin/collections/recorders",
|
slug: Collections.Recorders,
|
||||||
filters: [
|
filterGroups: [
|
||||||
{ label: "Admins", filter: "where[role][equals]=Admin" },
|
[
|
||||||
{ label: "Recorders", filter: "where[role][equals]=Recorder" },
|
...Object.entries(RecordersRoles).map(([key, value]) => ({
|
||||||
{ label: "∅ Role", filter: "where[role][not_in]=Admin,Recorder" },
|
label: value,
|
||||||
{ label: "Anonymized", filter: "where[anonymize][equals]=true" },
|
filter: { where: { role: { equals: key } } },
|
||||||
|
})),
|
||||||
|
{
|
||||||
|
label: "∅ Role",
|
||||||
|
filter: { where: { role: { not_in: Object.keys(RecordersRoles).join(",") } } },
|
||||||
|
},
|
||||||
|
,
|
||||||
|
],
|
||||||
|
[{ label: "Anonymized", filter: { where: { anonymize: { equals: true } } } }],
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
@ -66,6 +74,7 @@ export const Recorders = buildCollectionConfig(
|
||||||
hooks: {
|
hooks: {
|
||||||
beforeLogin: [beforeLoginMustHaveAtLeastOneRole],
|
beforeLogin: [beforeLoginMustHaveAtLeastOneRole],
|
||||||
},
|
},
|
||||||
|
endpoints: [importFromStrapi],
|
||||||
timestamps: false,
|
timestamps: false,
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
|
@ -80,7 +89,7 @@ export const Recorders = buildCollectionConfig(
|
||||||
},
|
},
|
||||||
imageField({
|
imageField({
|
||||||
name: fields.avatar,
|
name: fields.avatar,
|
||||||
relationTo: RecorderThumbnails.slug,
|
relationTo: Collections.RecordersThumbnails,
|
||||||
admin: { width: "66%" },
|
admin: { width: "66%" },
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
@ -88,7 +97,7 @@ export const Recorders = buildCollectionConfig(
|
||||||
{
|
{
|
||||||
name: fields.languages,
|
name: fields.languages,
|
||||||
type: "relationship",
|
type: "relationship",
|
||||||
relationTo: Languages.slug,
|
relationTo: Collections.Languages,
|
||||||
hasMany: true,
|
hasMany: true,
|
||||||
admin: {
|
admin: {
|
||||||
allowCreate: false,
|
allowCreate: false,
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
import payload from "payload";
|
||||||
|
import { Collections } from "../../../constants";
|
||||||
|
import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint";
|
||||||
|
import { Recorder } from "../../../types/collections";
|
||||||
|
import { uploadStrapiImage } from "../../../utils/localApi";
|
||||||
|
import { PayloadCreateData } from "../../../utils/types";
|
||||||
|
|
||||||
|
export const importFromStrapi = createStrapiImportEndpoint<Recorder>({
|
||||||
|
strapi: {
|
||||||
|
collection: "recorders",
|
||||||
|
params: {
|
||||||
|
populate: "bio.language,languages,avatar",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
payload: {
|
||||||
|
collection: Collections.Recorders,
|
||||||
|
import: async ({ username, anonymize, anonymous_code, languages, avatar, bio: bios }, user) => {
|
||||||
|
const avatarId = await uploadStrapiImage({
|
||||||
|
collection: Collections.RecordersThumbnails,
|
||||||
|
image: avatar,
|
||||||
|
});
|
||||||
|
|
||||||
|
const data: PayloadCreateData<Recorder> = {
|
||||||
|
email: `${anonymous_code}@accords-library.com`,
|
||||||
|
password: process.env.RECORDER_DEFAULT_PASSWORD,
|
||||||
|
username,
|
||||||
|
anonymize,
|
||||||
|
languages: languages.data?.map((language) => language.attributes.code),
|
||||||
|
avatar: avatarId,
|
||||||
|
biographies: bios?.map(({ language, bio }) => ({
|
||||||
|
language: language.data.attributes.code,
|
||||||
|
biography: bio,
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
|
||||||
|
await payload.create({ collection: Collections.Recorders, data, user });
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
|
@ -0,0 +1,59 @@
|
||||||
|
import { CollectionGroups, Collections } from "../../constants";
|
||||||
|
import { backPropagationField } from "../../fields/backPropagationField/backPropagationField";
|
||||||
|
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
||||||
|
|
||||||
|
const fields = {
|
||||||
|
filename: "filename",
|
||||||
|
mimeType: "mimeType",
|
||||||
|
filesize: "filesize",
|
||||||
|
recorder: "recorder",
|
||||||
|
} as const satisfies Record<string, string>;
|
||||||
|
|
||||||
|
export const RecordersThumbnails = buildCollectionConfig(
|
||||||
|
Collections.RecordersThumbnails,
|
||||||
|
{
|
||||||
|
singular: "Recorders Thumbnail",
|
||||||
|
plural: "Recorders Thumbnails",
|
||||||
|
},
|
||||||
|
({ uploadDir }) => ({
|
||||||
|
defaultSort: fields.filename,
|
||||||
|
admin: {
|
||||||
|
useAsTitle: fields.filename,
|
||||||
|
disableDuplicate: true,
|
||||||
|
group: CollectionGroups.Media,
|
||||||
|
},
|
||||||
|
upload: {
|
||||||
|
staticDir: uploadDir,
|
||||||
|
adminThumbnail: "small",
|
||||||
|
mimeTypes: ["image/*"],
|
||||||
|
imageSizes: [
|
||||||
|
{
|
||||||
|
name: "og",
|
||||||
|
height: 256,
|
||||||
|
width: 256,
|
||||||
|
formatOptions: {
|
||||||
|
format: "jpg",
|
||||||
|
options: { progressive: true, mozjpeg: true, compressionLevel: 9, quality: 80 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "small",
|
||||||
|
height: 128,
|
||||||
|
width: 128,
|
||||||
|
formatOptions: {
|
||||||
|
format: "webp",
|
||||||
|
options: { effort: 6, quality: 80, alphaQuality: 80 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
fields: [
|
||||||
|
backPropagationField({
|
||||||
|
name: fields.recorder,
|
||||||
|
hasMany: false,
|
||||||
|
relationTo: Collections.Recorders,
|
||||||
|
where: (id) => ({ avatar: { equals: id } }),
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
);
|
|
@ -0,0 +1,103 @@
|
||||||
|
import { CollectionConfig } from "payload/types";
|
||||||
|
import { mustBeAdmin } from "../../accesses/mustBeAdmin";
|
||||||
|
import { CollectionGroups, Collections, VideoSources } from "../../constants";
|
||||||
|
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
||||||
|
import { importFromStrapi } from "./endpoints/importFromStrapi";
|
||||||
|
|
||||||
|
const fields = {
|
||||||
|
uid: "uid",
|
||||||
|
gone: "gone",
|
||||||
|
source: "source",
|
||||||
|
liveChat: "liveChat",
|
||||||
|
title: "title",
|
||||||
|
description: "description",
|
||||||
|
publishedDate: "publishedDate",
|
||||||
|
views: "views",
|
||||||
|
likes: "likes",
|
||||||
|
channel: "channel",
|
||||||
|
} as const satisfies Record<string, string>;
|
||||||
|
|
||||||
|
export const Videos: CollectionConfig = buildCollectionConfig(
|
||||||
|
Collections.Videos,
|
||||||
|
{
|
||||||
|
singular: "Video",
|
||||||
|
plural: "Videos",
|
||||||
|
},
|
||||||
|
() => ({
|
||||||
|
defaultSort: fields.uid,
|
||||||
|
admin: {
|
||||||
|
useAsTitle: fields.title,
|
||||||
|
defaultColumns: [
|
||||||
|
fields.uid,
|
||||||
|
fields.title,
|
||||||
|
fields.source,
|
||||||
|
fields.gone,
|
||||||
|
fields.liveChat,
|
||||||
|
fields.publishedDate,
|
||||||
|
fields.views,
|
||||||
|
fields.likes,
|
||||||
|
fields.channel,
|
||||||
|
],
|
||||||
|
group: CollectionGroups.Media,
|
||||||
|
disableDuplicate: true,
|
||||||
|
},
|
||||||
|
access: {
|
||||||
|
create: mustBeAdmin,
|
||||||
|
delete: mustBeAdmin,
|
||||||
|
},
|
||||||
|
endpoints: [importFromStrapi],
|
||||||
|
timestamps: false,
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
type: "row",
|
||||||
|
fields: [
|
||||||
|
{ name: fields.uid, type: "text", required: true, unique: true, admin: { width: "33%" } },
|
||||||
|
{
|
||||||
|
name: fields.gone,
|
||||||
|
type: "checkbox",
|
||||||
|
defaultValue: false,
|
||||||
|
required: true,
|
||||||
|
admin: {
|
||||||
|
description:
|
||||||
|
"Is the video no longer available (deleted, privatized, unlisted, blocked...)",
|
||||||
|
width: "33%",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: fields.source,
|
||||||
|
type: "select",
|
||||||
|
required: true,
|
||||||
|
options: Object.entries(VideoSources).map(([value, label]) => ({ label, value })),
|
||||||
|
admin: { width: "33%" },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
{ name: fields.title, type: "text", required: true },
|
||||||
|
{ name: fields.description, type: "textarea" },
|
||||||
|
{
|
||||||
|
type: "row",
|
||||||
|
fields: [
|
||||||
|
{ name: fields.likes, type: "number", admin: { width: "50%" } },
|
||||||
|
{ name: fields.views, type: "number", admin: { width: "50%" } },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: fields.publishedDate,
|
||||||
|
type: "date",
|
||||||
|
admin: {
|
||||||
|
date: { pickerAppearance: "dayOnly", displayFormat: "yyyy-MM-dd" },
|
||||||
|
position: "sidebar",
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: fields.channel,
|
||||||
|
type: "relationship",
|
||||||
|
relationTo: Collections.VideosChannels,
|
||||||
|
required: true,
|
||||||
|
admin: { position: "sidebar" },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
);
|
|
@ -0,0 +1,70 @@
|
||||||
|
import payload from "payload";
|
||||||
|
import { Collections } from "../../../constants";
|
||||||
|
import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint";
|
||||||
|
import { Video, VideosChannel } from "../../../types/collections";
|
||||||
|
import { PayloadCreateData } from "../../../utils/types";
|
||||||
|
|
||||||
|
export const importFromStrapi = createStrapiImportEndpoint<Video>({
|
||||||
|
strapi: {
|
||||||
|
collection: "videos",
|
||||||
|
params: { populate: "published_date,channel" },
|
||||||
|
},
|
||||||
|
payload: {
|
||||||
|
collection: Collections.Videos,
|
||||||
|
import: async (
|
||||||
|
{
|
||||||
|
uid,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
views,
|
||||||
|
likes,
|
||||||
|
gone,
|
||||||
|
source,
|
||||||
|
published_date: { year, month, day },
|
||||||
|
channel,
|
||||||
|
},
|
||||||
|
user
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
const videoChannel: PayloadCreateData<VideosChannel> = {
|
||||||
|
uid: channel.data.attributes.uid,
|
||||||
|
title: channel.data.attributes.title,
|
||||||
|
subscribers: channel.data.attributes.subscribers,
|
||||||
|
};
|
||||||
|
await payload.create({
|
||||||
|
collection: Collections.VideosChannels,
|
||||||
|
data: videoChannel,
|
||||||
|
user,
|
||||||
|
});
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
const result = await payload.find({
|
||||||
|
collection: Collections.VideosChannels,
|
||||||
|
where: { uid: { equals: channel.data.attributes.uid } },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result.docs.length === 0) {
|
||||||
|
throw new Error("A video channel is required to create a video");
|
||||||
|
}
|
||||||
|
|
||||||
|
const videoChannel = result.docs[0] as VideosChannel;
|
||||||
|
const video: PayloadCreateData<Video> = {
|
||||||
|
uid,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
views,
|
||||||
|
likes,
|
||||||
|
gone,
|
||||||
|
source,
|
||||||
|
publishedDate: `${year}-${month}-${day}`,
|
||||||
|
channel: videoChannel.id,
|
||||||
|
};
|
||||||
|
|
||||||
|
await payload.create({
|
||||||
|
collection: Collections.Videos,
|
||||||
|
data: video,
|
||||||
|
user,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
|
@ -0,0 +1,45 @@
|
||||||
|
import { CollectionConfig } from "payload/types";
|
||||||
|
import { mustBeAdmin } from "../../accesses/mustBeAdmin";
|
||||||
|
import { CollectionGroups, Collections } from "../../constants";
|
||||||
|
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
||||||
|
import { importFromStrapi } from "./endpoints/importFromStrapi";
|
||||||
|
|
||||||
|
const fields = {
|
||||||
|
uid: "uid",
|
||||||
|
title: "title",
|
||||||
|
subscribers: "subscribers",
|
||||||
|
videos: "videos",
|
||||||
|
} as const satisfies Record<string, string>;
|
||||||
|
|
||||||
|
export const VideosChannels: CollectionConfig = buildCollectionConfig(
|
||||||
|
Collections.VideosChannels,
|
||||||
|
{
|
||||||
|
singular: "Videos Channel",
|
||||||
|
plural: "Videos Channels",
|
||||||
|
},
|
||||||
|
() => ({
|
||||||
|
defaultSort: fields.title,
|
||||||
|
admin: {
|
||||||
|
useAsTitle: fields.title,
|
||||||
|
defaultColumns: [fields.uid, fields.title, fields.subscribers, fields.videos],
|
||||||
|
group: CollectionGroups.Media,
|
||||||
|
disableDuplicate: true,
|
||||||
|
},
|
||||||
|
access: {
|
||||||
|
create: mustBeAdmin,
|
||||||
|
delete: mustBeAdmin,
|
||||||
|
},
|
||||||
|
endpoints: [importFromStrapi],
|
||||||
|
timestamps: false,
|
||||||
|
fields: [
|
||||||
|
{ name: fields.uid, type: "text", required: true, unique: true },
|
||||||
|
{
|
||||||
|
type: "row",
|
||||||
|
fields: [
|
||||||
|
{ name: fields.title, type: "text", required: true },
|
||||||
|
{ name: fields.subscribers, type: "number" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
);
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { Collections } from "../../../constants";
|
||||||
|
import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint";
|
||||||
|
import { VideosChannel } from "../../../types/collections";
|
||||||
|
|
||||||
|
export const importFromStrapi = createStrapiImportEndpoint<VideosChannel>({
|
||||||
|
strapi: {
|
||||||
|
collection: "video-channels",
|
||||||
|
params: {},
|
||||||
|
},
|
||||||
|
payload: {
|
||||||
|
collection: Collections.VideosChannels,
|
||||||
|
convert: ({ uid, title, subscribers }) => ({
|
||||||
|
uid,
|
||||||
|
title,
|
||||||
|
subscribers,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
});
|
|
@ -0,0 +1,163 @@
|
||||||
|
import { CollectionGroups, Collections, KeysTypes } from "../../constants";
|
||||||
|
import { imageField } from "../../fields/imageField/imageField";
|
||||||
|
import { slugField } from "../../fields/slugField/slugField";
|
||||||
|
import { localizedFields } from "../../fields/translatedFields/translatedFields";
|
||||||
|
import { buildVersionedCollectionConfig } from "../../utils/versionedCollectionConfig";
|
||||||
|
import { AppearanceRowLabel } from "./components/AppearanceRowLabel";
|
||||||
|
import { importFromStrapi } from "./endpoints/importFromStrapi";
|
||||||
|
|
||||||
|
const fields = {
|
||||||
|
slug: "slug",
|
||||||
|
thumbnail: "thumbnail",
|
||||||
|
type: "type",
|
||||||
|
group: "group",
|
||||||
|
appearances: "appearances",
|
||||||
|
appearancesCategories: "categories",
|
||||||
|
appearancesTranslations: "translations",
|
||||||
|
appearancesTranslationsName: "name",
|
||||||
|
appearancesTranslationsDescription: "description",
|
||||||
|
appearancesTranslationsLevel1: "level1",
|
||||||
|
appearancesTranslationsLevel2: "level2",
|
||||||
|
appearancesTranslationsLevel3: "level3",
|
||||||
|
appearancesTranslationsLevel4: "level4",
|
||||||
|
status: "_status",
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Weapons = buildVersionedCollectionConfig(
|
||||||
|
Collections.Weapons,
|
||||||
|
{ singular: "Weapon", plural: "Weapons" },
|
||||||
|
() => ({
|
||||||
|
defaultSort: fields.slug,
|
||||||
|
admin: {
|
||||||
|
useAsTitle: fields.slug,
|
||||||
|
defaultColumns: [
|
||||||
|
fields.slug,
|
||||||
|
fields.thumbnail,
|
||||||
|
fields.group,
|
||||||
|
fields.type,
|
||||||
|
fields.appearances,
|
||||||
|
fields.status,
|
||||||
|
],
|
||||||
|
group: CollectionGroups.Collections,
|
||||||
|
},
|
||||||
|
endpoints: [importFromStrapi],
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
type: "row",
|
||||||
|
fields: [
|
||||||
|
slugField({ name: fields.slug, admin: { width: "50%" } }),
|
||||||
|
imageField({
|
||||||
|
name: fields.thumbnail,
|
||||||
|
relationTo: Collections.WeaponsThumbnails,
|
||||||
|
admin: { width: "50%" },
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "row",
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: fields.type,
|
||||||
|
type: "relationship",
|
||||||
|
relationTo: Collections.Keys,
|
||||||
|
required: true,
|
||||||
|
filterOptions: { type: { equals: KeysTypes.Weapons } },
|
||||||
|
admin: { allowCreate: false, width: "50%" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: fields.group,
|
||||||
|
type: "relationship",
|
||||||
|
relationTo: Collections.WeaponsGroups,
|
||||||
|
admin: { width: "50%" },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: fields.appearances,
|
||||||
|
type: "array",
|
||||||
|
required: true,
|
||||||
|
minRows: 1,
|
||||||
|
admin: {
|
||||||
|
initCollapsed: true,
|
||||||
|
components: {
|
||||||
|
RowLabel: ({ data }) =>
|
||||||
|
AppearanceRowLabel({ keyIds: data[fields.appearancesCategories] ?? [] }),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: fields.appearancesCategories,
|
||||||
|
type: "relationship",
|
||||||
|
required: true,
|
||||||
|
hasMany: true,
|
||||||
|
relationTo: Collections.Keys,
|
||||||
|
filterOptions: { type: { equals: KeysTypes.Categories } },
|
||||||
|
admin: { allowCreate: false },
|
||||||
|
},
|
||||||
|
localizedFields({
|
||||||
|
name: fields.appearancesTranslations,
|
||||||
|
required: true,
|
||||||
|
minRows: 1,
|
||||||
|
admin: {
|
||||||
|
useAsTitle: fields.appearancesTranslationsName,
|
||||||
|
hasSourceLanguage: true,
|
||||||
|
hasCredits: true,
|
||||||
|
},
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
type: "row",
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: fields.appearancesTranslationsName,
|
||||||
|
type: "text",
|
||||||
|
required: true,
|
||||||
|
admin: { width: "50%" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: fields.appearancesTranslationsDescription,
|
||||||
|
type: "textarea",
|
||||||
|
admin: { width: "50%" },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "row",
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: fields.appearancesTranslationsLevel1,
|
||||||
|
label: "Level 1",
|
||||||
|
type: "textarea",
|
||||||
|
admin: { width: "50%" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: fields.appearancesTranslationsLevel2,
|
||||||
|
label: "Level 2",
|
||||||
|
type: "textarea",
|
||||||
|
admin: { width: "50%" },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "row",
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: fields.appearancesTranslationsLevel3,
|
||||||
|
label: "Level 3",
|
||||||
|
type: "textarea",
|
||||||
|
admin: { width: "50%" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: fields.appearancesTranslationsLevel4,
|
||||||
|
label: "Level 4",
|
||||||
|
type: "textarea",
|
||||||
|
admin: { width: "50%" },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
);
|
|
@ -0,0 +1,36 @@
|
||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import { styled } from "styled-components";
|
||||||
|
import { Collections } from "../../../constants";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
keyIds: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const Container = styled.div`
|
||||||
|
display: flex;
|
||||||
|
place-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const AppearanceRowLabel = ({ keyIds }: Props): JSX.Element => {
|
||||||
|
const [keySlugs, setKeySlugs] = useState<string[]>([]);
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchUrl = async () => {
|
||||||
|
const results = await Promise.all(
|
||||||
|
keyIds.map(async (keyId) => await (await fetch(`/api/${Collections.Keys}/${keyId}`)).json())
|
||||||
|
);
|
||||||
|
setKeySlugs(results.map((result) => result.name));
|
||||||
|
};
|
||||||
|
fetchUrl();
|
||||||
|
}, [keyIds]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container>
|
||||||
|
{keySlugs.map((keySlug) => (
|
||||||
|
<div id={keySlug} className="pill pill--style-white">
|
||||||
|
{keySlug}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
};
|
|
@ -0,0 +1,86 @@
|
||||||
|
import payload from "payload";
|
||||||
|
import { Collections } from "../../../constants";
|
||||||
|
import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint";
|
||||||
|
import { Weapon, WeaponsGroup } from "../../../types/collections";
|
||||||
|
import { isDefined } from "../../../utils/asserts";
|
||||||
|
import { findCategory, findWeaponType, uploadStrapiImage } from "../../../utils/localApi";
|
||||||
|
import { PayloadCreateData } from "../../../utils/types";
|
||||||
|
|
||||||
|
export const importFromStrapi = createStrapiImportEndpoint<Weapon>({
|
||||||
|
strapi: {
|
||||||
|
collection: "weapon-stories",
|
||||||
|
params: {
|
||||||
|
populate: [
|
||||||
|
"name.language",
|
||||||
|
"type",
|
||||||
|
"weapon_group",
|
||||||
|
"stories.categories",
|
||||||
|
"stories.translations.language",
|
||||||
|
"thumbnail",
|
||||||
|
].join(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
payload: {
|
||||||
|
collection: Collections.Weapons,
|
||||||
|
import: async ({ slug, type, stories, name: names, weapon_group, thumbnail }, user) => {
|
||||||
|
let groupId: string;
|
||||||
|
if (isDefined(weapon_group.data)) {
|
||||||
|
try {
|
||||||
|
const groupData: PayloadCreateData<WeaponsGroup> = {
|
||||||
|
slug: weapon_group.data.attributes.slug,
|
||||||
|
};
|
||||||
|
await payload.create({
|
||||||
|
collection: Collections.WeaponsGroups,
|
||||||
|
data: groupData,
|
||||||
|
user,
|
||||||
|
});
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
const result = await payload.find({
|
||||||
|
collection: Collections.WeaponsGroups,
|
||||||
|
where: { slug: { equals: weapon_group.data.attributes.slug } },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result.docs.length > 0) {
|
||||||
|
groupId = result.docs[0].id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const thumbnailId = await uploadStrapiImage({
|
||||||
|
collection: Collections.WeaponsThumbnails,
|
||||||
|
image: thumbnail,
|
||||||
|
});
|
||||||
|
|
||||||
|
const data: PayloadCreateData<Weapon> = {
|
||||||
|
slug,
|
||||||
|
type: await findWeaponType(type.data.attributes.slug),
|
||||||
|
group: groupId,
|
||||||
|
thumbnail: thumbnailId,
|
||||||
|
appearances: await Promise.all(
|
||||||
|
stories.map(async ({ categories, translations }) => ({
|
||||||
|
categories: await Promise.all(
|
||||||
|
categories.data.map(({ attributes }) => findCategory(attributes.slug))
|
||||||
|
),
|
||||||
|
translations: translations.map(
|
||||||
|
({ language, description, level_1, level_2, level_3, level_4 }) => ({
|
||||||
|
language: language.data?.attributes.code,
|
||||||
|
sourceLanguage: language.data?.attributes.code,
|
||||||
|
name: names.find(
|
||||||
|
(name) => name.language.data?.attributes.code === language.data?.attributes.code
|
||||||
|
)?.name,
|
||||||
|
description,
|
||||||
|
level1: level_1,
|
||||||
|
level2: level_2,
|
||||||
|
level3: level_3,
|
||||||
|
level4: level_4,
|
||||||
|
transcribers: [user.id],
|
||||||
|
})
|
||||||
|
),
|
||||||
|
}))
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
await payload.create({ collection: Collections.Weapons, data, user });
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
|
@ -0,0 +1,41 @@
|
||||||
|
import { CollectionGroups, Collections } from "../../constants";
|
||||||
|
import { backPropagationField } from "../../fields/backPropagationField/backPropagationField";
|
||||||
|
import { slugField } from "../../fields/slugField/slugField";
|
||||||
|
import { localizedFields } from "../../fields/translatedFields/translatedFields";
|
||||||
|
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
||||||
|
|
||||||
|
const fields = {
|
||||||
|
slug: "slug",
|
||||||
|
translations: "translations",
|
||||||
|
translationsName: "name",
|
||||||
|
subgroupOf: "subgroupOf",
|
||||||
|
weapons: "weapons",
|
||||||
|
};
|
||||||
|
|
||||||
|
export const WeaponsGroups = buildCollectionConfig(
|
||||||
|
Collections.WeaponsGroups,
|
||||||
|
{ singular: "Weapons Group", plural: "Weapon Groups" },
|
||||||
|
() => ({
|
||||||
|
defaultSort: fields.slug,
|
||||||
|
admin: {
|
||||||
|
useAsTitle: fields.slug,
|
||||||
|
defaultColumns: [fields.slug, fields.translations, fields.weapons, fields.subgroupOf],
|
||||||
|
group: CollectionGroups.Collections,
|
||||||
|
},
|
||||||
|
timestamps: false,
|
||||||
|
fields: [
|
||||||
|
slugField({ name: fields.slug }),
|
||||||
|
localizedFields({
|
||||||
|
name: fields.translations,
|
||||||
|
admin: { useAsTitle: fields.translationsName },
|
||||||
|
fields: [{ name: fields.translationsName, type: "text", required: true }],
|
||||||
|
}),
|
||||||
|
backPropagationField({
|
||||||
|
name: fields.weapons,
|
||||||
|
relationTo: Collections.Weapons,
|
||||||
|
hasMany: true,
|
||||||
|
where: (id) => ({ group: { equals: id } }),
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
);
|
|
@ -1,4 +1,4 @@
|
||||||
import { CollectionGroup } from "../../constants";
|
import { CollectionGroups, Collections } from "../../constants";
|
||||||
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
||||||
|
|
||||||
const fields = {
|
const fields = {
|
||||||
|
@ -7,17 +7,18 @@ const fields = {
|
||||||
filesize: "filesize",
|
filesize: "filesize",
|
||||||
} as const satisfies Record<string, string>;
|
} as const satisfies Record<string, string>;
|
||||||
|
|
||||||
export const RecorderThumbnails = buildCollectionConfig(
|
export const WeaponsThumbnails = buildCollectionConfig(
|
||||||
|
Collections.WeaponsThumbnails,
|
||||||
{
|
{
|
||||||
singular: "Recorder Thumbnail",
|
singular: "Weapons Thumbnail",
|
||||||
plural: "Recorder Thumbnails",
|
plural: "Weapons Thumbnails",
|
||||||
},
|
},
|
||||||
({ uploadDir }) => ({
|
({ uploadDir }) => ({
|
||||||
defaultSort: fields.filename,
|
defaultSort: fields.filename,
|
||||||
admin: {
|
admin: {
|
||||||
useAsTitle: fields.filename,
|
useAsTitle: fields.filename,
|
||||||
disableDuplicate: true,
|
disableDuplicate: true,
|
||||||
group: CollectionGroup.Media,
|
group: CollectionGroups.Media,
|
||||||
},
|
},
|
||||||
upload: {
|
upload: {
|
||||||
staticDir: uploadDir,
|
staticDir: uploadDir,
|
|
@ -1,22 +1,34 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { styled } from "styled-components";
|
import { styled } from "styled-components";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
import QueryString from "qs";
|
||||||
|
import { Options } from "payload/dist/collections/operations/local/find";
|
||||||
|
import { LanguageCodes } from "../constants";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
route: string;
|
slug: string;
|
||||||
filters: { label: string; filter: string }[];
|
filterGroups: { label: string; filter: Omit<Options<any>, "collection"> }[][];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const QuickFilters = ({ route, filters }: Props) => {
|
export const QuickFilters = ({ slug, filterGroups }: Props) => {
|
||||||
|
const route = `/admin/collections/${slug}`;
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<div>Quick Filters:</div>
|
<div>Quick Filters:</div>
|
||||||
<FilterContainer>
|
<GroupContainer>
|
||||||
<FilterCell label="None" to={route} />
|
{filterGroups.map((filtersGroup, groupIndex) => (
|
||||||
{filters.map(({ label, filter }, index) => (
|
<FilterContainer key={groupIndex}>
|
||||||
<FilterCell key={index} label={label} to={`${route}?${filter}`} />
|
<FilterCell label="None" to={route} />
|
||||||
|
{filtersGroup.map(({ label, filter }, index) => (
|
||||||
|
<FilterCell
|
||||||
|
key={index}
|
||||||
|
label={label}
|
||||||
|
to={`${route}?${QueryString.stringify(filter)}`}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</FilterContainer>
|
||||||
))}
|
))}
|
||||||
</FilterContainer>
|
</GroupContainer>
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -32,6 +44,11 @@ const FilterCell = ({ label, to }: FilterProps) => (
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const GroupContainer = styled.div`
|
||||||
|
display: grid;
|
||||||
|
gap: 4px;
|
||||||
|
`;
|
||||||
|
|
||||||
const Container = styled.div`
|
const Container = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
place-items: center;
|
place-items: center;
|
||||||
|
@ -42,6 +59,18 @@ const Container = styled.div`
|
||||||
|
|
||||||
const FilterContainer = styled.div`
|
const FilterContainer = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
place-items: center;
|
place-items: center;
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const languageBasedFilters = (field: string): Props["filterGroups"][number] =>
|
||||||
|
Object.entries(LanguageCodes).map(([key, value]) => ({
|
||||||
|
label: `∅ ${value}`,
|
||||||
|
filter: { where: { [field]: { not_equals: key } } },
|
||||||
|
}));
|
||||||
|
|
||||||
|
export const publishStatusFilters: Props["filterGroups"][number] = [
|
||||||
|
{ label: "Draft", filter: { where: { _status: { equals: "draft" } } } },
|
||||||
|
{ label: "Published", filter: { where: { _status: { equals: "published" } } } },
|
||||||
|
];
|
||||||
|
|
|
@ -1,4 +1,27 @@
|
||||||
export enum CollectionGroup {
|
export enum Collections {
|
||||||
|
ChronologyEras = "chronology-eras",
|
||||||
|
ChronologyItems = "chronology-items",
|
||||||
|
ContentsFolders = "contents-folders",
|
||||||
|
Contents = "contents",
|
||||||
|
ContentsThumbnails = "contents-thumbnails",
|
||||||
|
Currencies = "currencies",
|
||||||
|
Files = "files",
|
||||||
|
Keys = "keys",
|
||||||
|
Languages = "languages",
|
||||||
|
LibraryItems = "library-items",
|
||||||
|
LibraryItemsThumbnails = "library-items-thumbnails",
|
||||||
|
Posts = "posts",
|
||||||
|
PostsThumbnails = "posts-thumbnails",
|
||||||
|
Recorders = "recorders",
|
||||||
|
RecordersThumbnails = "recorders-thumbnails",
|
||||||
|
VideosChannels = "videos-channels",
|
||||||
|
Videos = "videos",
|
||||||
|
Weapons = "weapons",
|
||||||
|
WeaponsGroups = "weapons-groups",
|
||||||
|
WeaponsThumbnails = "weapons-thumbnails",
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum CollectionGroups {
|
||||||
Collections = "Collections",
|
Collections = "Collections",
|
||||||
Media = "Media",
|
Media = "Media",
|
||||||
Meta = "Meta",
|
Meta = "Meta",
|
||||||
|
@ -17,6 +40,15 @@ export enum KeysTypes {
|
||||||
Wordings = "Wordings",
|
Wordings = "Wordings",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum LanguageCodes {
|
||||||
|
en = "English",
|
||||||
|
fr = "French",
|
||||||
|
ja = "Japan",
|
||||||
|
es = "Spanish",
|
||||||
|
"pt-br" = "Portuguese",
|
||||||
|
"zh" = "Chinese",
|
||||||
|
}
|
||||||
|
|
||||||
export enum FileTypes {
|
export enum FileTypes {
|
||||||
LibraryScans = "Library / Scans",
|
LibraryScans = "Library / Scans",
|
||||||
LibrarySoundtracks = "Library / Soundtracks",
|
LibrarySoundtracks = "Library / Soundtracks",
|
||||||
|
@ -51,3 +83,8 @@ export enum CollectionStatus {
|
||||||
Draft = "draft",
|
Draft = "draft",
|
||||||
Published = "published",
|
Published = "published",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum VideoSources {
|
||||||
|
YouTube = "YouTube",
|
||||||
|
NicoNico = "NicoNico",
|
||||||
|
}
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
import payload from "payload";
|
import payload from "payload";
|
||||||
import { CollectionConfig } from "payload/types";
|
import { CollectionConfig } from "payload/types";
|
||||||
|
|
||||||
export const createBySlugEndpoint = <T>(
|
export const createGetByEndpoint = <T, R>(
|
||||||
collection: string,
|
collection: string,
|
||||||
handler: (doc: T) => unknown
|
attribute: string,
|
||||||
|
handler: (doc: T) => Promise<R>
|
||||||
): CollectionConfig["endpoints"][number] => ({
|
): CollectionConfig["endpoints"][number] => ({
|
||||||
path: "/slug/:slug",
|
path: `/${attribute}/:${attribute}`,
|
||||||
method: "get",
|
method: "get",
|
||||||
handler: async (req, res) => {
|
handler: async (req, res) => {
|
||||||
if (!req.user) {
|
if (!req.user) {
|
||||||
|
@ -20,13 +21,13 @@ export const createBySlugEndpoint = <T>(
|
||||||
|
|
||||||
const result = await payload.find({
|
const result = await payload.find({
|
||||||
collection,
|
collection,
|
||||||
where: { slug: { equals: req.params.slug } },
|
where: { [attribute]: { equals: req.params[attribute] } },
|
||||||
});
|
});
|
||||||
|
|
||||||
if (result.docs.length === 0) {
|
if (result.docs.length === 0) {
|
||||||
return res.sendStatus(404);
|
return res.sendStatus(404);
|
||||||
}
|
}
|
||||||
|
|
||||||
res.status(200).send(handler(result.docs[0]));
|
res.status(200).send(await handler(result.docs[0]));
|
||||||
},
|
},
|
||||||
});
|
});
|
|
@ -0,0 +1,99 @@
|
||||||
|
import payload from "payload";
|
||||||
|
import { CollectionConfig } from "payload/types";
|
||||||
|
import QueryString from "qs";
|
||||||
|
import { Recorder } from "../types/collections";
|
||||||
|
import { isDefined } from "../utils/asserts";
|
||||||
|
import { PayloadCreateData } from "../utils/types";
|
||||||
|
|
||||||
|
export const getAllStrapiEntries = async <T>(
|
||||||
|
collectionSlug: string,
|
||||||
|
params: Object
|
||||||
|
): Promise<T[]> => {
|
||||||
|
let page = 1;
|
||||||
|
let totalPage = 1;
|
||||||
|
const result: T[] = [];
|
||||||
|
|
||||||
|
while (page <= totalPage) {
|
||||||
|
const paramsWithPagination = QueryString.stringify({
|
||||||
|
...params,
|
||||||
|
pagination: { pageSize: 100, page },
|
||||||
|
});
|
||||||
|
const uri = `${process.env.STRAPI_URI}/api/${collectionSlug}?${paramsWithPagination}`;
|
||||||
|
const fetchResult = await fetch(uri, {
|
||||||
|
method: "GET",
|
||||||
|
headers: { authorization: `Bearer ${process.env.STRAPI_TOKEN}` },
|
||||||
|
});
|
||||||
|
const { data, meta } = await fetchResult.json();
|
||||||
|
result.push(...data);
|
||||||
|
totalPage = meta.pagination.pageCount;
|
||||||
|
page++;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Params<T> = {
|
||||||
|
strapi: {
|
||||||
|
collection: string;
|
||||||
|
params: any;
|
||||||
|
};
|
||||||
|
payload: {
|
||||||
|
collection: string;
|
||||||
|
import?: (strapiObject: any, user: any) => Promise<void>;
|
||||||
|
convert?: (strapiObject: any) => PayloadCreateData<T>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const importStrapiEntries = async <T>({
|
||||||
|
strapi: strapiParams,
|
||||||
|
payload: payloadParams,
|
||||||
|
user,
|
||||||
|
}: Params<T> & { user: Recorder }) => {
|
||||||
|
const entries = await getAllStrapiEntries<any>(strapiParams.collection, strapiParams.params);
|
||||||
|
|
||||||
|
const errors = [];
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
entries.map(async ({ attributes, id }) => {
|
||||||
|
try {
|
||||||
|
if (isDefined(payloadParams.import)) {
|
||||||
|
await payloadParams.import(attributes, user);
|
||||||
|
} else if (isDefined(payloadParams.convert)) {
|
||||||
|
await payload.create({
|
||||||
|
collection: payloadParams.collection,
|
||||||
|
data: payloadParams.convert(attributes),
|
||||||
|
user,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
throw new Error("No function was provided to handle importing the Strapi data");
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn(e);
|
||||||
|
errors.push(`${e.name} with ${id}`);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
return { count: entries.length, errors };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createStrapiImportEndpoint = <T>(
|
||||||
|
params: Params<T>
|
||||||
|
): CollectionConfig["endpoints"][number] => ({
|
||||||
|
method: "get",
|
||||||
|
path: "/strapi",
|
||||||
|
handler: async (req, res) => {
|
||||||
|
if (!req.user) {
|
||||||
|
return res.status(403).send({
|
||||||
|
errors: [
|
||||||
|
{
|
||||||
|
message: "You are not allowed to perform this action.",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const { count, errors } = await importStrapiEntries({ ...params, user: req.user });
|
||||||
|
|
||||||
|
res.status(200).json({ message: `${count} entries have been added successfully.`, errors });
|
||||||
|
},
|
||||||
|
});
|
|
@ -0,0 +1,48 @@
|
||||||
|
import payload from "payload";
|
||||||
|
import { FieldBase } from "payload/dist/fields/config/types";
|
||||||
|
import { RelationshipField, Where } from "payload/types";
|
||||||
|
import { isNotEmpty } from "../../utils/asserts";
|
||||||
|
|
||||||
|
type BackPropagationField = FieldBase & {
|
||||||
|
where: (id: string) => Where;
|
||||||
|
relationTo: string;
|
||||||
|
hasMany: boolean;
|
||||||
|
};
|
||||||
|
export const backPropagationField = ({
|
||||||
|
admin,
|
||||||
|
hooks: { beforeChange = [], afterRead = [], ...otherHooks } = {},
|
||||||
|
where,
|
||||||
|
...params
|
||||||
|
}: BackPropagationField): RelationshipField => ({
|
||||||
|
...params,
|
||||||
|
type: "relationship",
|
||||||
|
admin: { ...admin, readOnly: true },
|
||||||
|
hooks: {
|
||||||
|
...otherHooks,
|
||||||
|
beforeChange: [
|
||||||
|
...beforeChange,
|
||||||
|
({ siblingData }) => {
|
||||||
|
delete siblingData[params.name];
|
||||||
|
},
|
||||||
|
],
|
||||||
|
afterRead: [
|
||||||
|
...afterRead,
|
||||||
|
async ({ data }) => {
|
||||||
|
if (isNotEmpty(data.id)) {
|
||||||
|
const result = await payload.find({
|
||||||
|
collection: params.relationTo,
|
||||||
|
where: where(data.id),
|
||||||
|
limit: 100,
|
||||||
|
depth: 0,
|
||||||
|
});
|
||||||
|
if (params.hasMany) {
|
||||||
|
return result.docs.map((doc) => doc.id);
|
||||||
|
} else {
|
||||||
|
return result.docs[0].id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return params.hasMany ? [] : undefined;
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
|
@ -1,10 +1,10 @@
|
||||||
import { RelationshipField, UploadField } from "payload/types";
|
import { RelationshipField, UploadField } from "payload/types";
|
||||||
import { Files } from "../../collections/Files/Files";
|
import { Collections } from "../../constants";
|
||||||
|
|
||||||
type Props = Omit<UploadField, "type" | "relationTo">;
|
type Props = Omit<UploadField, "type" | "relationTo">;
|
||||||
|
|
||||||
export const fileField = (props: Props): RelationshipField => ({
|
export const fileField = (props: Props): RelationshipField => ({
|
||||||
...props,
|
...props,
|
||||||
type: "relationship",
|
type: "relationship",
|
||||||
relationTo: Files.slug,
|
relationTo: Collections.Files,
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,24 +1,32 @@
|
||||||
|
import { array } from "payload/dist/fields/validations";
|
||||||
import { ArrayField, Field } from "payload/types";
|
import { ArrayField, Field } from "payload/types";
|
||||||
import { hasDuplicates } from "../../utils/validation";
|
import { Collections } from "../../constants";
|
||||||
import { isDefined, isUndefined } from "../../utils/asserts";
|
import { isDefined, isUndefined } from "../../utils/asserts";
|
||||||
import { Languages } from "../../collections/Languages/Languages";
|
import { hasDuplicates } from "../../utils/validation";
|
||||||
import { RowLabel } from "./RowLabel";
|
|
||||||
import { Cell } from "./Cell";
|
import { Cell } from "./Cell";
|
||||||
|
import { RowLabel } from "./RowLabel";
|
||||||
|
|
||||||
const fieldsNames = {
|
const fieldsNames = {
|
||||||
language: "language",
|
language: "language",
|
||||||
sourceLanguage: "sourceLanguage",
|
sourceLanguage: "sourceLanguage",
|
||||||
|
transcribers: "transcribers",
|
||||||
|
translators: "translators",
|
||||||
|
proofreaders: "proofreaders",
|
||||||
} as const satisfies Record<string, string>;
|
} as const satisfies Record<string, string>;
|
||||||
|
|
||||||
type LocalizedFieldsProps = Omit<ArrayField, "type" | "admin"> & {
|
type LocalizedFieldsProps = Omit<ArrayField, "type" | "admin" | "validate"> & {
|
||||||
admin?: ArrayField["admin"] & { useAsTitle?: string; hasSourceLanguage?: boolean };
|
admin?: ArrayField["admin"] & {
|
||||||
|
useAsTitle?: string;
|
||||||
|
hasSourceLanguage?: boolean;
|
||||||
|
hasCredits?: boolean;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
type ArrayData = { [fieldsNames.language]?: string }[] | number | undefined;
|
type ArrayData = { [fieldsNames.language]?: string }[] | number | undefined;
|
||||||
|
|
||||||
const languageField: Field = {
|
const languageField: Field = {
|
||||||
name: fieldsNames.language,
|
name: fieldsNames.language,
|
||||||
type: "relationship",
|
type: "relationship",
|
||||||
relationTo: Languages.slug,
|
relationTo: Collections.Languages,
|
||||||
required: true,
|
required: true,
|
||||||
admin: { allowCreate: false, width: "50%" },
|
admin: { allowCreate: false, width: "50%" },
|
||||||
};
|
};
|
||||||
|
@ -26,15 +34,72 @@ const languageField: Field = {
|
||||||
const sourceLanguageField: Field = {
|
const sourceLanguageField: Field = {
|
||||||
name: fieldsNames.sourceLanguage,
|
name: fieldsNames.sourceLanguage,
|
||||||
type: "relationship",
|
type: "relationship",
|
||||||
relationTo: Languages.slug,
|
relationTo: Collections.Languages,
|
||||||
required: true,
|
required: true,
|
||||||
admin: { allowCreate: false, width: "50%" },
|
admin: { allowCreate: false, width: "50%" },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const creditFields: Field = {
|
||||||
|
type: "row",
|
||||||
|
admin: {
|
||||||
|
condition: (_, siblingData) =>
|
||||||
|
isDefined(siblingData.language) && isDefined(siblingData.sourceLanguage),
|
||||||
|
},
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: fieldsNames.transcribers,
|
||||||
|
label: "Transcribers",
|
||||||
|
type: "relationship",
|
||||||
|
relationTo: "recorders",
|
||||||
|
hasMany: true,
|
||||||
|
admin: {
|
||||||
|
condition: (_, siblingData) => siblingData.language === siblingData.sourceLanguage,
|
||||||
|
width: "50%",
|
||||||
|
},
|
||||||
|
validate: (count, { siblingData }) => {
|
||||||
|
if (siblingData.language !== siblingData.sourceLanguage) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (isDefined(count) && count.length > 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return "This field is required when the language is the same as the source language.";
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: fieldsNames.translators,
|
||||||
|
label: "Translators",
|
||||||
|
type: "relationship",
|
||||||
|
relationTo: "recorders",
|
||||||
|
hasMany: true,
|
||||||
|
admin: {
|
||||||
|
condition: (_, siblingData) => siblingData.language !== siblingData.sourceLanguage,
|
||||||
|
width: "50%",
|
||||||
|
},
|
||||||
|
validate: (count, { siblingData }) => {
|
||||||
|
if (siblingData.language === siblingData.sourceLanguage) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (isDefined(count) && count.length > 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return "This field is required when the language is different from the source language.";
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: fieldsNames.proofreaders,
|
||||||
|
label: "Proofreaders",
|
||||||
|
type: "relationship",
|
||||||
|
relationTo: "recorders",
|
||||||
|
hasMany: true,
|
||||||
|
admin: { width: "50%" },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
export const localizedFields = ({
|
export const localizedFields = ({
|
||||||
fields,
|
fields,
|
||||||
validate,
|
admin: { useAsTitle, hasSourceLanguage, hasCredits, ...admin } = {},
|
||||||
admin: { useAsTitle, hasSourceLanguage, ...admin },
|
|
||||||
...otherProps
|
...otherProps
|
||||||
}: LocalizedFieldsProps): ArrayField => ({
|
}: LocalizedFieldsProps): ArrayField => ({
|
||||||
...otherProps,
|
...otherProps,
|
||||||
|
@ -59,6 +124,9 @@ export const localizedFields = ({
|
||||||
...admin,
|
...admin,
|
||||||
},
|
},
|
||||||
validate: (value, options) => {
|
validate: (value, options) => {
|
||||||
|
const defaultValidation = array(value, options);
|
||||||
|
if (defaultValidation !== true) return defaultValidation;
|
||||||
|
|
||||||
const data = options.data[otherProps.name] as ArrayData;
|
const data = options.data[otherProps.name] as ArrayData;
|
||||||
if (isUndefined(data)) return true;
|
if (isUndefined(data)) return true;
|
||||||
if (typeof data === "number") return true;
|
if (typeof data === "number") return true;
|
||||||
|
@ -67,13 +135,12 @@ export const localizedFields = ({
|
||||||
if (hasDuplicates(languages)) {
|
if (hasDuplicates(languages)) {
|
||||||
return `There cannot be multiple ${otherProps.name} with the same ${fieldsNames.language}`;
|
return `There cannot be multiple ${otherProps.name} with the same ${fieldsNames.language}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return isDefined(validate) ? validate(value, options) : true;
|
|
||||||
},
|
},
|
||||||
fields: [
|
fields: [
|
||||||
hasSourceLanguage
|
hasSourceLanguage
|
||||||
? { type: "row", fields: [languageField, sourceLanguageField] }
|
? { type: "row", fields: [languageField, sourceLanguageField] }
|
||||||
: languageField,
|
: languageField,
|
||||||
...fields,
|
...fields,
|
||||||
|
...(hasCredits ? [creditFields] : []),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,25 +1,33 @@
|
||||||
import { buildConfig } from "payload/config";
|
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import { Languages } from "./collections/Languages/Languages";
|
import { buildConfig } from "payload/config";
|
||||||
import { Recorders } from "./collections/Recorders/Recorders";
|
import { ChronologyEras } from "./collections/ChronologyEras/ChronologyEras";
|
||||||
import { Posts } from "./collections/Posts/Posts";
|
import { ChronologyItems } from "./collections/ChronologyItems/ChronologyItems";
|
||||||
import { Keys } from "./collections/Keys/Keys";
|
|
||||||
import { LibraryItems } from "./collections/LibraryItems/LibraryItems";
|
|
||||||
import { Contents } from "./collections/Contents/Contents";
|
import { Contents } from "./collections/Contents/Contents";
|
||||||
import { Files } from "./collections/Files/Files";
|
import { ContentsFolders } from "./collections/ContentsFolders/ContentsFolders";
|
||||||
import { RecorderThumbnails } from "./collections/RecorderThumbnails/RecorderThumbnails";
|
import { ContentsThumbnails } from "./collections/ContentsThumbnails/ContentsThumbnails";
|
||||||
import { PostThumbnails } from "./collections/PostThumbnails/PostThumbnails";
|
|
||||||
import { LibraryItemThumbnails } from "./collections/LibraryItemThumbnails/LibraryItemThumbnails";
|
|
||||||
import { ContentThumbnails } from "./collections/ContentThumbnails/ContentThumbnails";
|
|
||||||
import { ContentFolders } from "./collections/ContentFolders/ContentFolders";
|
|
||||||
import { Logo } from "./components/Logo";
|
|
||||||
import { Icon } from "./components/Icon";
|
|
||||||
import { Currencies } from "./collections/Currencies/Currencies";
|
import { Currencies } from "./collections/Currencies/Currencies";
|
||||||
|
import { Files } from "./collections/Files/Files";
|
||||||
|
import { Keys } from "./collections/Keys/Keys";
|
||||||
|
import { Languages } from "./collections/Languages/Languages";
|
||||||
|
import { LibraryItems } from "./collections/LibraryItems/LibraryItems";
|
||||||
|
import { LibraryItemsThumbnails } from "./collections/LibraryItemsThumbnails/LibraryItemsThumbnails";
|
||||||
|
import { Posts } from "./collections/Posts/Posts";
|
||||||
|
import { PostsThumbnails } from "./collections/PostsThumbnails/PostsThumbnails";
|
||||||
|
import { Recorders } from "./collections/Recorders/Recorders";
|
||||||
|
import { RecordersThumbnails } from "./collections/RecordersThumbnails/RecordersThumbnails";
|
||||||
|
import { Videos } from "./collections/Videos/Videos";
|
||||||
|
import { VideosChannels } from "./collections/VideosChannels/VideosChannels";
|
||||||
|
import { Weapons } from "./collections/Weapons/Weapons";
|
||||||
|
import { WeaponsGroups } from "./collections/WeaponsGroups/WeaponsGroups";
|
||||||
|
import { WeaponsThumbnails } from "./collections/WeaponsThumbnails/WeaponsThumbnails";
|
||||||
|
import { Icon } from "./components/Icon";
|
||||||
|
import { Logo } from "./components/Logo";
|
||||||
|
import { Collections } from "./constants";
|
||||||
|
|
||||||
export default buildConfig({
|
export default buildConfig({
|
||||||
serverURL: "https://dashboard.accords-library.com",
|
serverURL: process.env.PAYLOAD_URI,
|
||||||
admin: {
|
admin: {
|
||||||
user: Recorders.slug,
|
user: Collections.Recorders,
|
||||||
components: { graphics: { Logo, Icon } },
|
components: { graphics: { Logo, Icon } },
|
||||||
meta: {
|
meta: {
|
||||||
favicon: "/public/favicon.ico",
|
favicon: "/public/favicon.ico",
|
||||||
|
@ -31,13 +39,20 @@ export default buildConfig({
|
||||||
collections: [
|
collections: [
|
||||||
LibraryItems,
|
LibraryItems,
|
||||||
Contents,
|
Contents,
|
||||||
ContentFolders,
|
ContentsFolders,
|
||||||
Posts,
|
Posts,
|
||||||
ContentThumbnails,
|
ChronologyItems,
|
||||||
LibraryItemThumbnails,
|
ChronologyEras,
|
||||||
RecorderThumbnails,
|
Weapons,
|
||||||
PostThumbnails,
|
WeaponsGroups,
|
||||||
|
WeaponsThumbnails,
|
||||||
|
ContentsThumbnails,
|
||||||
|
LibraryItemsThumbnails,
|
||||||
|
RecordersThumbnails,
|
||||||
|
PostsThumbnails,
|
||||||
Files,
|
Files,
|
||||||
|
Videos,
|
||||||
|
VideosChannels,
|
||||||
Languages,
|
Languages,
|
||||||
Currencies,
|
Currencies,
|
||||||
Recorders,
|
Recorders,
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
import express from "express";
|
|
||||||
import payload from "payload";
|
|
||||||
import "dotenv/config";
|
import "dotenv/config";
|
||||||
|
import express from "express";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
|
import payload from "payload";
|
||||||
|
import { Collections, RecordersRoles } from "./constants";
|
||||||
|
import { Recorder } from "./types/collections";
|
||||||
|
import { PayloadCreateData } from "./utils/types";
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
|
|
||||||
|
@ -18,12 +21,34 @@ const start = async () => {
|
||||||
express: app,
|
express: app,
|
||||||
onInit: async () => {
|
onInit: async () => {
|
||||||
payload.logger.info(`Payload Admin URL: ${payload.getAdminURL()}`);
|
payload.logger.info(`Payload Admin URL: ${payload.getAdminURL()}`);
|
||||||
|
const recorders = await payload.find({ collection: Collections.Recorders });
|
||||||
|
|
||||||
|
// If no recorders, we seed some initial data
|
||||||
|
if (recorders.docs.length === 0) {
|
||||||
|
payload.logger.info("Seeding some initial data");
|
||||||
|
const recorder: PayloadCreateData<Recorder> = {
|
||||||
|
email: process.env.SEEDING_ADMIN_EMAIL,
|
||||||
|
password: process.env.SEEDING_ADMIN_PASSWORD,
|
||||||
|
username: process.env.SEEDING_ADMIN_USERNAME,
|
||||||
|
role: [RecordersRoles.Admin],
|
||||||
|
anonymize: false,
|
||||||
|
};
|
||||||
|
await payload.create({
|
||||||
|
collection: Collections.Recorders,
|
||||||
|
data: recorder,
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add your own express routes here
|
// Add your own express routes here
|
||||||
app.use("/public", express.static(path.join(__dirname, "../public")));
|
app.use("/public", express.static(path.join(__dirname, "../public")));
|
||||||
|
|
||||||
|
app.get("/robots.txt", (_, res) => {
|
||||||
|
res.type("text/plain");
|
||||||
|
res.send("User-agent: *\nDisallow: /");
|
||||||
|
});
|
||||||
|
|
||||||
app.listen(process.env.PAYLOAD_PORT);
|
app.listen(process.env.PAYLOAD_PORT);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -27,13 +27,20 @@ export interface Config {
|
||||||
collections: {
|
collections: {
|
||||||
"library-items": LibraryItem;
|
"library-items": LibraryItem;
|
||||||
contents: Content;
|
contents: Content;
|
||||||
"content-folders": ContentFolder;
|
"contents-folders": ContentsFolder;
|
||||||
posts: Post;
|
posts: Post;
|
||||||
"content-thumbnails": ContentThumbnail;
|
"chronology-items": ChronologyItem;
|
||||||
"library-item-thumbnails": LibraryItemThumbnail;
|
"chronology-eras": ChronologyEra;
|
||||||
"recorder-thumbnails": RecorderThumbnail;
|
weapons: Weapon;
|
||||||
"post-thumbnails": PostThumbnail;
|
"weapons-groups": WeaponsGroup;
|
||||||
|
"weapons-thumbnails": WeaponsThumbnail;
|
||||||
|
"contents-thumbnails": ContentsThumbnail;
|
||||||
|
"library-items-thumbnails": LibraryItemThumbnail;
|
||||||
|
"recorders-thumbnails": RecordersThumbnail;
|
||||||
|
"posts-thumbnails": PostThumbnail;
|
||||||
files: File;
|
files: File;
|
||||||
|
videos: Video;
|
||||||
|
"videos-channels": VideosChannel;
|
||||||
languages: Language;
|
languages: Language;
|
||||||
currencies: Currency;
|
currencies: Currency;
|
||||||
recorders: Recorder;
|
recorders: Recorder;
|
||||||
|
@ -65,7 +72,7 @@ export interface LibraryItem {
|
||||||
back?: string | LibraryItemThumbnail;
|
back?: string | LibraryItemThumbnail;
|
||||||
id?: string;
|
id?: string;
|
||||||
}[];
|
}[];
|
||||||
obibelt?: {
|
obi?: {
|
||||||
front?: string | LibraryItemThumbnail;
|
front?: string | LibraryItemThumbnail;
|
||||||
spine?: string | LibraryItemThumbnail;
|
spine?: string | LibraryItemThumbnail;
|
||||||
back?: string | LibraryItemThumbnail;
|
back?: string | LibraryItemThumbnail;
|
||||||
|
@ -124,6 +131,15 @@ export interface LibraryItem {
|
||||||
relationTo: "keys";
|
relationTo: "keys";
|
||||||
}[];
|
}[];
|
||||||
};
|
};
|
||||||
|
contents?: {
|
||||||
|
content: string | Content;
|
||||||
|
pageStart?: number;
|
||||||
|
pageEnd?: number;
|
||||||
|
timeStart?: number;
|
||||||
|
timeEnd?: number;
|
||||||
|
note?: string;
|
||||||
|
id?: string;
|
||||||
|
}[];
|
||||||
releaseDate?: string;
|
releaseDate?: string;
|
||||||
updatedBy: string | Recorder;
|
updatedBy: string | Recorder;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
|
@ -172,7 +188,7 @@ export interface Currency {
|
||||||
}
|
}
|
||||||
export interface Key {
|
export interface Key {
|
||||||
id: string;
|
id: string;
|
||||||
slug: string;
|
name: string;
|
||||||
type:
|
type:
|
||||||
| "Contents"
|
| "Contents"
|
||||||
| "LibraryAudio"
|
| "LibraryAudio"
|
||||||
|
@ -190,56 +206,10 @@ export interface Language {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
export interface Recorder {
|
|
||||||
id: string;
|
|
||||||
username: string;
|
|
||||||
avatar?: string | RecorderThumbnail;
|
|
||||||
languages?: string[] | Language[];
|
|
||||||
biographies?: RecorderBiographies;
|
|
||||||
role?: ("Admin" | "Recorder")[];
|
|
||||||
anonymize: boolean;
|
|
||||||
email: string;
|
|
||||||
resetPasswordToken?: string;
|
|
||||||
resetPasswordExpiration?: string;
|
|
||||||
salt?: string;
|
|
||||||
hash?: string;
|
|
||||||
loginAttempts?: number;
|
|
||||||
lockUntil?: string;
|
|
||||||
password?: string;
|
|
||||||
}
|
|
||||||
export interface RecorderThumbnail {
|
|
||||||
id: string;
|
|
||||||
updatedAt: string;
|
|
||||||
createdAt: string;
|
|
||||||
url?: string;
|
|
||||||
filename?: string;
|
|
||||||
mimeType?: string;
|
|
||||||
filesize?: number;
|
|
||||||
width?: number;
|
|
||||||
height?: number;
|
|
||||||
sizes?: {
|
|
||||||
og?: {
|
|
||||||
url?: string;
|
|
||||||
width?: number;
|
|
||||||
height?: number;
|
|
||||||
mimeType?: string;
|
|
||||||
filesize?: number;
|
|
||||||
filename?: string;
|
|
||||||
};
|
|
||||||
small?: {
|
|
||||||
url?: string;
|
|
||||||
width?: number;
|
|
||||||
height?: number;
|
|
||||||
mimeType?: string;
|
|
||||||
filesize?: number;
|
|
||||||
filename?: string;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
export interface Content {
|
export interface Content {
|
||||||
id: string;
|
id: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
thumbnail?: string | ContentThumbnail;
|
thumbnail?: string | ContentsThumbnail;
|
||||||
categories?:
|
categories?:
|
||||||
| {
|
| {
|
||||||
value: string;
|
value: string;
|
||||||
|
@ -275,7 +245,7 @@ export interface Content {
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
_status?: "draft" | "published";
|
_status?: "draft" | "published";
|
||||||
}
|
}
|
||||||
export interface ContentThumbnail {
|
export interface ContentsThumbnail {
|
||||||
id: string;
|
id: string;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
|
@ -304,6 +274,53 @@ export interface ContentThumbnail {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
export interface Recorder {
|
||||||
|
id: string;
|
||||||
|
username: string;
|
||||||
|
avatar?: string | RecordersThumbnail;
|
||||||
|
languages?: string[] | Language[];
|
||||||
|
biographies?: RecorderBiographies;
|
||||||
|
role?: ("Admin" | "Recorder")[];
|
||||||
|
anonymize: boolean;
|
||||||
|
email: string;
|
||||||
|
resetPasswordToken?: string;
|
||||||
|
resetPasswordExpiration?: string;
|
||||||
|
salt?: string;
|
||||||
|
hash?: string;
|
||||||
|
loginAttempts?: number;
|
||||||
|
lockUntil?: string;
|
||||||
|
password?: string;
|
||||||
|
}
|
||||||
|
export interface RecordersThumbnail {
|
||||||
|
id: string;
|
||||||
|
recorder?: string | Recorder;
|
||||||
|
updatedAt: string;
|
||||||
|
createdAt: string;
|
||||||
|
url?: string;
|
||||||
|
filename?: string;
|
||||||
|
mimeType?: string;
|
||||||
|
filesize?: number;
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
|
sizes?: {
|
||||||
|
og?: {
|
||||||
|
url?: string;
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
|
mimeType?: string;
|
||||||
|
filesize?: number;
|
||||||
|
filename?: string;
|
||||||
|
};
|
||||||
|
small?: {
|
||||||
|
url?: string;
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
|
mimeType?: string;
|
||||||
|
filesize?: number;
|
||||||
|
filename?: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
export interface TextBlock {
|
export interface TextBlock {
|
||||||
content: {
|
content: {
|
||||||
[k: string]: unknown;
|
[k: string]: unknown;
|
||||||
|
@ -498,28 +515,12 @@ export interface File {
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
}
|
}
|
||||||
export interface ContentFolder {
|
export interface ContentsFolder {
|
||||||
id: string;
|
id: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
translations?: ContentFoldersTranslation;
|
translations?: ContentFoldersTranslation;
|
||||||
subfolders?:
|
subfolders?: string[] | ContentsFolder[];
|
||||||
| {
|
contents?: string[] | Content[];
|
||||||
value: string;
|
|
||||||
relationTo: "content-folders";
|
|
||||||
}[]
|
|
||||||
| {
|
|
||||||
value: ContentFolder;
|
|
||||||
relationTo: "content-folders";
|
|
||||||
}[];
|
|
||||||
contents?:
|
|
||||||
| {
|
|
||||||
value: string;
|
|
||||||
relationTo: "contents";
|
|
||||||
}[]
|
|
||||||
| {
|
|
||||||
value: Content;
|
|
||||||
relationTo: "contents";
|
|
||||||
}[];
|
|
||||||
}
|
}
|
||||||
export interface Post {
|
export interface Post {
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -591,3 +592,126 @@ export interface PostThumbnail {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
export interface ChronologyItem {
|
||||||
|
id: string;
|
||||||
|
name?: string;
|
||||||
|
date: {
|
||||||
|
year: number;
|
||||||
|
month?: number;
|
||||||
|
day?: number;
|
||||||
|
};
|
||||||
|
events?: {
|
||||||
|
translations?: {
|
||||||
|
language: string | Language;
|
||||||
|
title?: string;
|
||||||
|
description?: string;
|
||||||
|
notes?: string;
|
||||||
|
id?: string;
|
||||||
|
}[];
|
||||||
|
id?: string;
|
||||||
|
}[];
|
||||||
|
updatedBy: string | Recorder;
|
||||||
|
updatedAt: string;
|
||||||
|
createdAt: string;
|
||||||
|
_status?: "draft" | "published";
|
||||||
|
}
|
||||||
|
export interface ChronologyEra {
|
||||||
|
id: string;
|
||||||
|
slug: string;
|
||||||
|
startingYear: number;
|
||||||
|
endingYear: number;
|
||||||
|
translations?: {
|
||||||
|
language: string | Language;
|
||||||
|
title: string;
|
||||||
|
description?: string;
|
||||||
|
id?: string;
|
||||||
|
}[];
|
||||||
|
updatedAt: string;
|
||||||
|
createdAt: string;
|
||||||
|
}
|
||||||
|
export interface Weapon {
|
||||||
|
id: string;
|
||||||
|
slug: string;
|
||||||
|
thumbnail?: string | WeaponsThumbnail;
|
||||||
|
type: string | Key;
|
||||||
|
group?: string | WeaponsGroup;
|
||||||
|
appearances: {
|
||||||
|
categories: string[] | Key[];
|
||||||
|
translations: {
|
||||||
|
language: string | Language;
|
||||||
|
sourceLanguage: string | Language;
|
||||||
|
name: string;
|
||||||
|
description?: string;
|
||||||
|
level1?: string;
|
||||||
|
level2?: string;
|
||||||
|
level3?: string;
|
||||||
|
level4?: string;
|
||||||
|
transcribers?: string[] | Recorder[];
|
||||||
|
translators?: string[] | Recorder[];
|
||||||
|
proofreaders?: string[] | Recorder[];
|
||||||
|
id?: string;
|
||||||
|
}[];
|
||||||
|
id?: string;
|
||||||
|
}[];
|
||||||
|
updatedBy: string | Recorder;
|
||||||
|
updatedAt: string;
|
||||||
|
createdAt: string;
|
||||||
|
_status?: "draft" | "published";
|
||||||
|
}
|
||||||
|
export interface WeaponsThumbnail {
|
||||||
|
id: string;
|
||||||
|
updatedAt: string;
|
||||||
|
createdAt: string;
|
||||||
|
url?: string;
|
||||||
|
filename?: string;
|
||||||
|
mimeType?: string;
|
||||||
|
filesize?: number;
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
|
sizes?: {
|
||||||
|
og?: {
|
||||||
|
url?: string;
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
|
mimeType?: string;
|
||||||
|
filesize?: number;
|
||||||
|
filename?: string;
|
||||||
|
};
|
||||||
|
small?: {
|
||||||
|
url?: string;
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
|
mimeType?: string;
|
||||||
|
filesize?: number;
|
||||||
|
filename?: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export interface WeaponsGroup {
|
||||||
|
id: string;
|
||||||
|
slug: string;
|
||||||
|
translations?: {
|
||||||
|
language: string | Language;
|
||||||
|
name: string;
|
||||||
|
id?: string;
|
||||||
|
}[];
|
||||||
|
weapons?: string[] | Weapon[];
|
||||||
|
}
|
||||||
|
export interface Video {
|
||||||
|
id: string;
|
||||||
|
uid: string;
|
||||||
|
gone: boolean;
|
||||||
|
source: "YouTube" | "NicoNico";
|
||||||
|
title: string;
|
||||||
|
description?: string;
|
||||||
|
likes?: number;
|
||||||
|
views?: number;
|
||||||
|
publishedDate: string;
|
||||||
|
channel: string | VideosChannel;
|
||||||
|
}
|
||||||
|
export interface VideosChannel {
|
||||||
|
id: string;
|
||||||
|
uid: string;
|
||||||
|
title: string;
|
||||||
|
subscribers?: number;
|
||||||
|
}
|
||||||
|
|
|
@ -5,3 +5,8 @@ export const isUndefined = <T>(value: T | null | undefined): value is null | und
|
||||||
!isDefined(value);
|
!isDefined(value);
|
||||||
|
|
||||||
export const filterDefined = <T>(array: (T | null | undefined)[]): T[] => array.filter(isDefined);
|
export const filterDefined = <T>(array: (T | null | undefined)[]): T[] => array.filter(isDefined);
|
||||||
|
|
||||||
|
export const isValidDate = (date: Date): boolean => date instanceof Date && !isNaN(date.getDate());
|
||||||
|
|
||||||
|
export const isNotEmpty = (value: string | null | undefined): value is string =>
|
||||||
|
isDefined(value) && value.trim().length > 0;
|
||||||
|
|
|
@ -1,20 +1,19 @@
|
||||||
import { CollectionConfig } from "payload/types";
|
import { CollectionConfig } from "payload/types";
|
||||||
import slugify from "slugify";
|
import { Collections } from "../constants";
|
||||||
|
|
||||||
export type BuildCollectionConfig = Omit<CollectionConfig, "slug" | "typescript" | "labels">;
|
export type BuildCollectionConfig = Omit<CollectionConfig, "slug" | "typescript" | "labels">;
|
||||||
|
|
||||||
export type GenerationFunctionProps = {
|
export type GenerationFunctionProps = {
|
||||||
slug: string;
|
|
||||||
uploadDir: string;
|
uploadDir: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const buildCollectionConfig = (
|
export const buildCollectionConfig = (
|
||||||
|
slug: Collections,
|
||||||
labels: { singular: string; plural: string },
|
labels: { singular: string; plural: string },
|
||||||
generationFunction: (props: GenerationFunctionProps) => BuildCollectionConfig
|
generationFunction: (props: GenerationFunctionProps) => BuildCollectionConfig
|
||||||
): CollectionConfig => {
|
): CollectionConfig => {
|
||||||
const slug = slugify(labels.plural, { lower: true, strict: true, trim: true });
|
|
||||||
const uploadDir = `../uploads/${slug}`;
|
const uploadDir = `../uploads/${slug}`;
|
||||||
const config = generationFunction({ slug, uploadDir });
|
const config = generationFunction({ uploadDir });
|
||||||
return {
|
return {
|
||||||
...config,
|
...config,
|
||||||
slug,
|
slug,
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
import payload from "payload";
|
||||||
|
import { Collections, KeysTypes } from "../constants";
|
||||||
|
import { isDefined } from "./asserts";
|
||||||
|
|
||||||
|
export const findWeaponType = async (name: string): Promise<string> => {
|
||||||
|
const key = await payload.find({
|
||||||
|
collection: Collections.Keys,
|
||||||
|
where: { name: { equals: name }, type: { equals: KeysTypes.Weapons } },
|
||||||
|
});
|
||||||
|
return key.docs[0].id;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const findCategory = async (name: string): Promise<string> => {
|
||||||
|
const key = await payload.find({
|
||||||
|
collection: Collections.Keys,
|
||||||
|
where: { name: { equals: name }, type: { equals: KeysTypes.Categories } },
|
||||||
|
});
|
||||||
|
return key.docs[0].id;
|
||||||
|
};
|
||||||
|
|
||||||
|
type UploadStrapiImage = {
|
||||||
|
image: StrapiImage;
|
||||||
|
collection: Collections;
|
||||||
|
};
|
||||||
|
|
||||||
|
type StrapiImage = {
|
||||||
|
data?: {
|
||||||
|
attributes: {
|
||||||
|
url: string;
|
||||||
|
mime: string;
|
||||||
|
name: string;
|
||||||
|
size: number;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
export const uploadStrapiImage = async ({
|
||||||
|
collection,
|
||||||
|
image,
|
||||||
|
}: UploadStrapiImage): Promise<string> => {
|
||||||
|
if (isDefined(image.data)) {
|
||||||
|
const url = `${process.env.STRAPI_URI}${image.data.attributes.url}`;
|
||||||
|
|
||||||
|
const blob = await (await fetch(url)).blob();
|
||||||
|
const buffer = Buffer.from(await blob.arrayBuffer());
|
||||||
|
|
||||||
|
const result = await payload.create({
|
||||||
|
collection,
|
||||||
|
file: {
|
||||||
|
data: buffer,
|
||||||
|
mimetype: image.data.attributes.mime,
|
||||||
|
name: image.data.attributes.name,
|
||||||
|
size: image.data.attributes.size,
|
||||||
|
},
|
||||||
|
data: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
return result.id;
|
||||||
|
}
|
||||||
|
};
|
|
@ -1,7 +1,22 @@
|
||||||
import ISO6391 from "iso-639-1";
|
import tags from "language-tags";
|
||||||
|
|
||||||
export const shortenEllipsis = (text: string, length: number): string =>
|
export const shortenEllipsis = (text: string, length: number): string =>
|
||||||
text.length - 3 > length ? `${text.substring(0, length)}...` : text;
|
text.length - 3 > length ? `${text.substring(0, length)}...` : text;
|
||||||
|
|
||||||
export const formatLanguageCode = (code: string): string =>
|
export const formatLanguageCode = (code: string): string =>
|
||||||
ISO6391.validate(code) ? ISO6391.getName(code) : code;
|
tags(code).valid() ? tags(code).language().descriptions()[0] : code;
|
||||||
|
|
||||||
|
export const capitalize = (string: string): string => {
|
||||||
|
const [firstLetter, ...otherLetters] = string;
|
||||||
|
return [firstLetter.toUpperCase(), ...otherLetters].join("");
|
||||||
|
};
|
||||||
|
|
||||||
|
export const formatToCamelCase = (name: string): string =>
|
||||||
|
name
|
||||||
|
.toLowerCase()
|
||||||
|
.split(/[ \_-]/g)
|
||||||
|
.map((part, index) => (index > 0 ? capitalize(part) : part))
|
||||||
|
.join("");
|
||||||
|
|
||||||
|
export const formatToKebabCase = (name: string): string =>
|
||||||
|
name.toLowerCase().replaceAll(/[ \_]/g, "-");
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
export type PayloadCreateData<T> = Omit<
|
||||||
|
T,
|
||||||
|
"id" | "updatedAt" | "createdAt" | "sizes" | "updatedBy"
|
||||||
|
>;
|
|
@ -1,10 +1,10 @@
|
||||||
import { CollectionBeforeChangeHook, CollectionConfig, RelationshipField } from "payload/types";
|
import { CollectionBeforeChangeHook, CollectionConfig, RelationshipField } from "payload/types";
|
||||||
|
import { Collections } from "../constants";
|
||||||
import {
|
import {
|
||||||
BuildCollectionConfig,
|
BuildCollectionConfig,
|
||||||
GenerationFunctionProps,
|
GenerationFunctionProps,
|
||||||
buildCollectionConfig,
|
buildCollectionConfig,
|
||||||
} from "./collectionConfig";
|
} from "./collectionConfig";
|
||||||
import { Recorders } from "../collections/Recorders/Recorders";
|
|
||||||
|
|
||||||
const fields = { updatedBy: "updatedBy" };
|
const fields = { updatedBy: "updatedBy" };
|
||||||
|
|
||||||
|
@ -17,13 +17,14 @@ const updatedByField = (): RelationshipField => ({
|
||||||
name: fields.updatedBy,
|
name: fields.updatedBy,
|
||||||
type: "relationship",
|
type: "relationship",
|
||||||
required: true,
|
required: true,
|
||||||
relationTo: Recorders.slug,
|
relationTo: Collections.Recorders,
|
||||||
admin: { readOnly: true, position: "sidebar" },
|
admin: { readOnly: true, position: "sidebar" },
|
||||||
});
|
});
|
||||||
|
|
||||||
type BuildVersionedCollectionConfig = Omit<BuildCollectionConfig, "timestamps" | "versions">;
|
type BuildVersionedCollectionConfig = Omit<BuildCollectionConfig, "timestamps" | "versions">;
|
||||||
|
|
||||||
export const buildVersionedCollectionConfig = (
|
export const buildVersionedCollectionConfig = (
|
||||||
|
slug: Collections,
|
||||||
labels: { singular: string; plural: string },
|
labels: { singular: string; plural: string },
|
||||||
generationFunction: (props: GenerationFunctionProps) => BuildVersionedCollectionConfig
|
generationFunction: (props: GenerationFunctionProps) => BuildVersionedCollectionConfig
|
||||||
): CollectionConfig => {
|
): CollectionConfig => {
|
||||||
|
@ -31,7 +32,7 @@ export const buildVersionedCollectionConfig = (
|
||||||
hooks: { beforeChange, ...otherHooks } = {},
|
hooks: { beforeChange, ...otherHooks } = {},
|
||||||
fields,
|
fields,
|
||||||
...otherParams
|
...otherParams
|
||||||
} = buildCollectionConfig(labels, generationFunction);
|
} = buildCollectionConfig(slug, labels, generationFunction);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...otherParams,
|
...otherParams,
|
||||||
|
|
Loading…
Reference in New Issue