Spent the weekend improving stuff
This commit is contained in:
		
							parent
							
								
									4f807c410b
								
							
						
					
					
						commit
						cbd0251ad5
					
				
							
								
								
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -167,4 +167,6 @@ dist | ||||
| 
 | ||||
| # Ignore Data | ||||
| mongo/ | ||||
| uploads/ | ||||
| uploads/ | ||||
| build | ||||
| core | ||||
| @ -10,6 +10,7 @@ services: | ||||
|       - node_modules:/home/node/app/node_modules | ||||
|     working_dir: /home/node/app/ | ||||
|     command: sh -c "npm install && npm run generate:types && npm run dev" | ||||
|     # command: sh -c "npm install && npm run generate:types && npm run build:payload && npm run serve" | ||||
|     depends_on: | ||||
|       - mongo | ||||
|     environment: | ||||
|  | ||||
							
								
								
									
										37
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										37
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @ -13,11 +13,10 @@ | ||||
|         "@payloadcms/bundler-webpack": "^1.0.4", | ||||
|         "@payloadcms/db-mongodb": "^1.0.4", | ||||
|         "@payloadcms/richtext-lexical": "^0.1.15", | ||||
|         "clean-deep": "^3.4.0", | ||||
|         "cross-env": "^7.0.3", | ||||
|         "language-tags": "^1.0.9", | ||||
|         "luxon": "^3.4.3", | ||||
|         "payload": "^2.0.12", | ||||
|         "payload": "^2.0.13", | ||||
|         "styled-components": "^6.1.0" | ||||
|       }, | ||||
|       "devDependencies": { | ||||
| @ -5413,19 +5412,6 @@ | ||||
|         "node": ">= 10.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/clean-deep": { | ||||
|       "version": "3.4.0", | ||||
|       "resolved": "https://registry.npmjs.org/clean-deep/-/clean-deep-3.4.0.tgz", | ||||
|       "integrity": "sha512-Lo78NV5ItJL/jl+B5w0BycAisaieJGXK1qYi/9m4SjR8zbqmrUtO7Yhro40wEShGmmxs/aJLI/A+jNhdkXK8mw==", | ||||
|       "dependencies": { | ||||
|         "lodash.isempty": "^4.4.0", | ||||
|         "lodash.isplainobject": "^4.0.6", | ||||
|         "lodash.transform": "^4.6.0" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">=4" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/clean-stack": { | ||||
|       "version": "2.2.0", | ||||
|       "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", | ||||
| @ -9321,26 +9307,11 @@ | ||||
|       "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", | ||||
|       "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==" | ||||
|     }, | ||||
|     "node_modules/lodash.isempty": { | ||||
|       "version": "4.4.0", | ||||
|       "resolved": "https://registry.npmjs.org/lodash.isempty/-/lodash.isempty-4.4.0.tgz", | ||||
|       "integrity": "sha512-oKMuF3xEeqDltrGMfDxAPGIVMSSRv8tbRSODbrs4KGsRRLEhrW8N8Rd4DRgB2+621hY8A8XwwrTVhXWpxFvMzg==" | ||||
|     }, | ||||
|     "node_modules/lodash.isplainobject": { | ||||
|       "version": "4.0.6", | ||||
|       "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", | ||||
|       "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" | ||||
|     }, | ||||
|     "node_modules/lodash.memoize": { | ||||
|       "version": "4.1.2", | ||||
|       "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", | ||||
|       "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==" | ||||
|     }, | ||||
|     "node_modules/lodash.transform": { | ||||
|       "version": "4.6.0", | ||||
|       "resolved": "https://registry.npmjs.org/lodash.transform/-/lodash.transform-4.6.0.tgz", | ||||
|       "integrity": "sha512-LO37ZnhmBVx0GvOU/caQuipEh4GN82TcWv3yHlebGDgOxbxiwwzW5Pcx2AcvpIv2WmvmSMoC492yQFNhy/l/UQ==" | ||||
|     }, | ||||
|     "node_modules/lodash.uniq": { | ||||
|       "version": "4.5.0", | ||||
|       "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", | ||||
| @ -11140,9 +11111,9 @@ | ||||
|       "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==" | ||||
|     }, | ||||
|     "node_modules/payload": { | ||||
|       "version": "2.0.12", | ||||
|       "resolved": "https://registry.npmjs.org/payload/-/payload-2.0.12.tgz", | ||||
|       "integrity": "sha512-M3x9Y53ukiflZC4STri/34Gx4VjMk+UjuLc38dQRiCLPFGXEuqPfFFdt+c1Lh4ZeTQmMlWzzJcRMQ00tK+kWYg==", | ||||
|       "version": "2.0.13", | ||||
|       "resolved": "https://registry.npmjs.org/payload/-/payload-2.0.13.tgz", | ||||
|       "integrity": "sha512-rD9ncVH8ClP7SphDymnrtVv0GAwHeyBXt9b1wSQBF15Dx/svU5rD1OEDtDPgEUTQApnySBVsB4NDGM1xO32YjA==", | ||||
|       "dependencies": { | ||||
|         "@date-io/date-fns": "2.16.0", | ||||
|         "@dnd-kit/core": "6.0.8", | ||||
|  | ||||
| @ -19,18 +19,18 @@ | ||||
|     "precommit": "npm run generate:types && npm run prettier && npm run unused-exports && npm run tsc", | ||||
|     "upgrade": "ncu", | ||||
|     "clean": "sudo rm -r uploads mongo", | ||||
|     "start": "sudo docker compose up" | ||||
|     "start": "sudo docker compose up", | ||||
|     "stop": "sudo docker compose down" | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "@fontsource/vollkorn": "^5.0.17", | ||||
|     "@payloadcms/bundler-webpack": "^1.0.4", | ||||
|     "@payloadcms/db-mongodb": "^1.0.4", | ||||
|     "@payloadcms/richtext-lexical": "^0.1.15", | ||||
|     "clean-deep": "^3.4.0", | ||||
|     "cross-env": "^7.0.3", | ||||
|     "language-tags": "^1.0.9", | ||||
|     "luxon": "^3.4.3", | ||||
|     "payload": "^2.0.12", | ||||
|     "payload": "^2.0.13", | ||||
|     "styled-components": "^6.1.0" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|  | ||||
| @ -2,6 +2,7 @@ import { CollectionConfig } from "payload/types"; | ||||
| import { mustBeAdmin } from "../../accesses/collections/mustBeAdmin"; | ||||
| import { CollectionGroups, Collections } from "../../constants"; | ||||
| import { backPropagationField } from "../../fields/backPropagationField/backPropagationField"; | ||||
| import { rowField } from "../../fields/rowField/rowField"; | ||||
| import { slugField } from "../../fields/slugField/slugField"; | ||||
| import { translatedFields } from "../../fields/translatedFields/translatedFields"; | ||||
| import { buildCollectionConfig } from "../../utils/collectionConfig"; | ||||
| @ -43,25 +44,22 @@ export const ChronologyEras: CollectionConfig = buildCollectionConfig({ | ||||
|   endpoints: [importFromStrapi, getAllEndpoint], | ||||
|   fields: [ | ||||
|     slugField({ name: fields.slug }), | ||||
|     { | ||||
|       type: "row", | ||||
|       fields: [ | ||||
|         { | ||||
|           name: fields.startingYear, | ||||
|           type: "number", | ||||
|           min: 0, | ||||
|           required: true, | ||||
|           admin: { width: "0%", description: "The year the era started (year included)" }, | ||||
|         }, | ||||
|         { | ||||
|           name: fields.endingYear, | ||||
|           type: "number", | ||||
|           min: 0, | ||||
|           required: true, | ||||
|           admin: { width: "0%", description: "The year the era ended (year included)" }, | ||||
|         }, | ||||
|       ], | ||||
|     }, | ||||
|     rowField([ | ||||
|       { | ||||
|         name: fields.startingYear, | ||||
|         type: "number", | ||||
|         min: 0, | ||||
|         required: true, | ||||
|         admin: { description: "The year the era started (year included)" }, | ||||
|       }, | ||||
|       { | ||||
|         name: fields.endingYear, | ||||
|         type: "number", | ||||
|         min: 0, | ||||
|         required: true, | ||||
|         admin: { description: "The year the era ended (year included)" }, | ||||
|       }, | ||||
|     ]), | ||||
|     translatedFields({ | ||||
|       name: fields.translations, | ||||
|       admin: { useAsTitle: fields.translationsTitle }, | ||||
|  | ||||
| @ -5,6 +5,7 @@ import { | ||||
|   publishStatusFilters, | ||||
| } from "../../components/QuickFilters"; | ||||
| import { CollectionGroups, Collections } from "../../constants"; | ||||
| import { rowField } from "../../fields/rowField/rowField"; | ||||
| import { translatedFields } from "../../fields/translatedFields/translatedFields"; | ||||
| import { createEditor } from "../../utils/editor"; | ||||
| import { buildVersionedCollectionConfig } from "../../utils/versionedCollectionConfig"; | ||||
| @ -69,32 +70,26 @@ export const ChronologyItems: CollectionConfig = buildVersionedCollectionConfig( | ||||
|       name: fields.date, | ||||
|       validate: validateDate, | ||||
|       fields: [ | ||||
|         { | ||||
|           type: "row", | ||||
|           fields: [ | ||||
|             { | ||||
|               name: fields.dateYear, | ||||
|               type: "number", | ||||
|               required: true, | ||||
|               min: 0, | ||||
|               admin: { width: "0%" }, | ||||
|             }, | ||||
|             { | ||||
|               name: fields.dateMonth, | ||||
|               type: "number", | ||||
|               min: 1, | ||||
|               max: 12, | ||||
|               admin: { width: "0%" }, | ||||
|             }, | ||||
|             { | ||||
|               name: fields.dateDay, | ||||
|               type: "number", | ||||
|               min: 1, | ||||
|               max: 31, | ||||
|               admin: { width: "0%" }, | ||||
|             }, | ||||
|           ], | ||||
|         }, | ||||
|         rowField([ | ||||
|           { | ||||
|             name: fields.dateYear, | ||||
|             type: "number", | ||||
|             required: true, | ||||
|             min: 0, | ||||
|           }, | ||||
|           { | ||||
|             name: fields.dateMonth, | ||||
|             type: "number", | ||||
|             min: 1, | ||||
|             max: 12, | ||||
|           }, | ||||
|           { | ||||
|             name: fields.dateDay, | ||||
|             type: "number", | ||||
|             min: 1, | ||||
|             max: 31, | ||||
|           }, | ||||
|         ]), | ||||
|       ], | ||||
|     }, | ||||
|     { | ||||
|  | ||||
| @ -1,9 +1,11 @@ | ||||
| import { sectionBlock } from "../../blocks/sectionBlock"; | ||||
| import { transcriptBlock } from "../../blocks/transcriptBlock"; | ||||
| import { CollectionGroups, Collections, FileTypes, KeysTypes } from "../../constants"; | ||||
| import { backPropagationField } from "../../fields/backPropagationField/backPropagationField"; | ||||
| import { fileField } from "../../fields/fileField/fileField"; | ||||
| import { imageField } from "../../fields/imageField/imageField"; | ||||
| import { keysField } from "../../fields/keysField/keysField"; | ||||
| import { rowField } from "../../fields/rowField/rowField"; | ||||
| import { slugField } from "../../fields/slugField/slugField"; | ||||
| import { translatedFields } from "../../fields/translatedFields/translatedFields"; | ||||
| import { beforeDuplicateAddCopyTo } from "../../hooks/beforeDuplicateAddCopyTo"; | ||||
| @ -13,6 +15,7 @@ import { isDefined } from "../../utils/asserts"; | ||||
| import { createEditor } from "../../utils/editor"; | ||||
| import { buildVersionedCollectionConfig } from "../../utils/versionedCollectionConfig"; | ||||
| import { importFromStrapi } from "./endpoints/importFromStrapi"; | ||||
| import { importRelationsFromStrapi } from "./endpoints/importRelationsFromStrapi"; | ||||
| 
 | ||||
| const fields = { | ||||
|   slug: "slug", | ||||
| @ -35,6 +38,10 @@ const fields = { | ||||
|   audioNotes: "videoNotes", | ||||
|   status: "status", | ||||
|   updatedBy: "updatedBy", | ||||
|   previousContents: "previousContents", | ||||
|   nextContents: "nextContents", | ||||
|   folders: "folders", | ||||
|   libraryItems: "libraryItems", | ||||
| } as const satisfies Record<string, string>; | ||||
| 
 | ||||
| export const Contents = buildVersionedCollectionConfig({ | ||||
| @ -63,51 +70,44 @@ export const Contents = buildVersionedCollectionConfig({ | ||||
|         beforeDuplicateAddCopyTo(fields.slug), | ||||
|       ]), | ||||
|     }, | ||||
|     preview: (doc) => `https://accords-library.com/contents/${doc.slug}`, | ||||
|   }, | ||||
|   endpoints: [importFromStrapi], | ||||
|   endpoints: [importFromStrapi, importRelationsFromStrapi], | ||||
|   fields: [ | ||||
|     { | ||||
|       type: "row", | ||||
|       fields: [ | ||||
|         slugField({ name: fields.slug, admin: { width: "0%" } }), | ||||
|         imageField({ | ||||
|           name: fields.thumbnail, | ||||
|           relationTo: Collections.ContentsThumbnails, | ||||
|           admin: { width: "0%" }, | ||||
|         }), | ||||
|       ], | ||||
|     }, | ||||
|     { | ||||
|       type: "row", | ||||
|       fields: [ | ||||
|         keysField({ | ||||
|           name: fields.categories, | ||||
|           relationTo: KeysTypes.Categories, | ||||
|           hasMany: true, | ||||
|           admin: { allowCreate: false, width: "0%" }, | ||||
|         }), | ||||
|         keysField({ | ||||
|           name: fields.type, | ||||
|           relationTo: KeysTypes.Contents, | ||||
|           admin: { allowCreate: false, width: "0%" }, | ||||
|         }), | ||||
|       ], | ||||
|     }, | ||||
|     rowField([ | ||||
|       slugField({ name: fields.slug }), | ||||
|       imageField({ | ||||
|         name: fields.thumbnail, | ||||
|         relationTo: Collections.ContentsThumbnails, | ||||
|       }), | ||||
|     ]), | ||||
|     rowField([ | ||||
|       keysField({ | ||||
|         name: fields.categories, | ||||
|         relationTo: KeysTypes.Categories, | ||||
|         hasMany: true, | ||||
|       }), | ||||
|       keysField({ | ||||
|         name: fields.type, | ||||
|         relationTo: KeysTypes.Contents, | ||||
|       }), | ||||
|       backPropagationField({ | ||||
|         name: fields.libraryItems, | ||||
|         hasMany: true, | ||||
|         relationTo: Collections.LibraryItems, | ||||
|         where: ({ id }) => ({ "contents.content": { equals: id } }), | ||||
|       }), | ||||
|     ]), | ||||
|     translatedFields({ | ||||
|       name: fields.translations, | ||||
|       admin: { useAsTitle: fields.title, hasSourceLanguage: true }, | ||||
|       required: true, | ||||
|       minRows: 1, | ||||
|       fields: [ | ||||
|         { | ||||
|           type: "row", | ||||
|           fields: [ | ||||
|             { name: fields.pretitle, type: "text" }, | ||||
|             { name: fields.title, type: "text", required: true }, | ||||
|             { name: fields.subtitle, type: "text" }, | ||||
|           ], | ||||
|         }, | ||||
|         rowField([ | ||||
|           { name: fields.pretitle, type: "text" }, | ||||
|           { name: fields.title, type: "text", required: true }, | ||||
|           { name: fields.subtitle, type: "text" }, | ||||
|         ]), | ||||
|         { | ||||
|           name: fields.summary, | ||||
|           type: "richText", | ||||
| @ -137,43 +137,37 @@ export const Contents = buildVersionedCollectionConfig({ | ||||
|                     alignment: true, | ||||
|                   }), | ||||
|                 }, | ||||
|                 { | ||||
|                   type: "row", | ||||
|                   fields: [ | ||||
|                     { | ||||
|                       name: fields.textTranscribers, | ||||
|                       label: "Transcribers", | ||||
|                       type: "relationship", | ||||
|                       relationTo: Collections.Recorders, | ||||
|                       hasMany: true, | ||||
|                       admin: { | ||||
|                         condition: (_, siblingData) => | ||||
|                           siblingData.language === siblingData.sourceLanguage, | ||||
|                         width: "0%", | ||||
|                       }, | ||||
|                 rowField([ | ||||
|                   { | ||||
|                     name: fields.textTranscribers, | ||||
|                     label: "Transcribers", | ||||
|                     type: "relationship", | ||||
|                     relationTo: Collections.Recorders, | ||||
|                     hasMany: true, | ||||
|                     admin: { | ||||
|                       condition: (_, siblingData) => | ||||
|                         siblingData.language === siblingData.sourceLanguage, | ||||
|                     }, | ||||
|                     { | ||||
|                       name: fields.textTranslators, | ||||
|                       label: "Translators", | ||||
|                       type: "relationship", | ||||
|                       relationTo: Collections.Recorders, | ||||
|                       hasMany: true, | ||||
|                       admin: { | ||||
|                         condition: (_, siblingData) => | ||||
|                           siblingData.language !== siblingData.sourceLanguage, | ||||
|                         width: "0%", | ||||
|                       }, | ||||
|                   }, | ||||
|                   { | ||||
|                     name: fields.textTranslators, | ||||
|                     label: "Translators", | ||||
|                     type: "relationship", | ||||
|                     relationTo: Collections.Recorders, | ||||
|                     hasMany: true, | ||||
|                     admin: { | ||||
|                       condition: (_, siblingData) => | ||||
|                         siblingData.language !== siblingData.sourceLanguage, | ||||
|                     }, | ||||
|                     { | ||||
|                       name: fields.textProofreaders, | ||||
|                       label: "Proofreaders", | ||||
|                       type: "relationship", | ||||
|                       relationTo: Collections.Recorders, | ||||
|                       hasMany: true, | ||||
|                       admin: { width: "0%" }, | ||||
|                     }, | ||||
|                   ], | ||||
|                 }, | ||||
|                   }, | ||||
|                   { | ||||
|                     name: fields.textProofreaders, | ||||
|                     label: "Proofreaders", | ||||
|                     type: "relationship", | ||||
|                     relationTo: Collections.Recorders, | ||||
|                     hasMany: true, | ||||
|                   }, | ||||
|                 ]), | ||||
|                 { | ||||
|                   name: fields.textNotes, | ||||
|                   label: "Notes", | ||||
| @ -185,50 +179,63 @@ export const Contents = buildVersionedCollectionConfig({ | ||||
|             { | ||||
|               label: "Video", | ||||
|               fields: [ | ||||
|                 { | ||||
|                   type: "row", | ||||
|                   fields: [ | ||||
|                     fileField({ | ||||
|                       name: fields.video, | ||||
|                       relationTo: FileTypes.ContentVideo, | ||||
|                       admin: { width: "0%" }, | ||||
|                     }), | ||||
|                     { | ||||
|                       name: fields.videoNotes, | ||||
|                       label: "Notes", | ||||
|                       type: "richText", | ||||
|                       editor: createEditor({ inlines: true, lists: true, links: true }), | ||||
|                       admin: { width: "0%" }, | ||||
|                     }, | ||||
|                   ], | ||||
|                 }, | ||||
|                 rowField([ | ||||
|                   fileField({ | ||||
|                     name: fields.video, | ||||
|                     relationTo: FileTypes.ContentVideo, | ||||
|                   }), | ||||
|                   { | ||||
|                     name: fields.videoNotes, | ||||
|                     label: "Notes", | ||||
|                     type: "richText", | ||||
|                     editor: createEditor({ inlines: true, lists: true, links: true }), | ||||
|                   }, | ||||
|                 ]), | ||||
|               ], | ||||
|             }, | ||||
|             { | ||||
|               label: "Audio", | ||||
|               fields: [ | ||||
|                 { | ||||
|                   type: "row", | ||||
|                   fields: [ | ||||
|                     fileField({ | ||||
|                       name: fields.audio, | ||||
|                       relationTo: FileTypes.ContentAudio, | ||||
|                       admin: { width: "0%" }, | ||||
|                     }), | ||||
|                     { | ||||
|                       name: fields.audioNotes, | ||||
|                       label: "Notes", | ||||
|                       type: "richText", | ||||
|                       editor: createEditor({ inlines: true, lists: true, links: true }), | ||||
|                       admin: { width: "0%" }, | ||||
|                     }, | ||||
|                   ], | ||||
|                 }, | ||||
|                 rowField([ | ||||
|                   fileField({ | ||||
|                     name: fields.audio, | ||||
|                     relationTo: FileTypes.ContentAudio, | ||||
|                   }), | ||||
|                   { | ||||
|                     name: fields.audioNotes, | ||||
|                     label: "Notes", | ||||
|                     type: "richText", | ||||
|                     editor: createEditor({ inlines: true, lists: true, links: true }), | ||||
|                   }, | ||||
|                 ]), | ||||
|               ], | ||||
|             }, | ||||
|           ], | ||||
|         }, | ||||
|       ], | ||||
|     }), | ||||
|     rowField([ | ||||
|       backPropagationField({ | ||||
|         name: fields.folders, | ||||
|         hasMany: true, | ||||
|         relationTo: Collections.ContentsFolders, | ||||
|         where: ({ id }) => ({ contents: { equals: id } }), | ||||
|         admin: { | ||||
|           description: `You can set the folder(s) from the "Contents Folders" collection`, | ||||
|         }, | ||||
|       }), | ||||
|       backPropagationField({ | ||||
|         name: fields.previousContents, | ||||
|         relationTo: Collections.Contents, | ||||
|         hasMany: true, | ||||
|         where: ({ id }) => ({ [fields.nextContents]: { equals: id } }), | ||||
|       }), | ||||
|       { | ||||
|         name: fields.nextContents, | ||||
|         type: "relationship", | ||||
|         hasMany: true, | ||||
|         relationTo: Collections.Contents, | ||||
|       }, | ||||
|     ]), | ||||
|   ], | ||||
| }); | ||||
|  | ||||
| @ -60,6 +60,52 @@ export const importFromStrapi = createStrapiImportEndpoint<StrapiContent>({ | ||||
|         image: thumbnail, | ||||
|       }); | ||||
| 
 | ||||
|       const handleTranslation = async ({ | ||||
|         language, | ||||
|         title, | ||||
|         description, | ||||
|         pre_title, | ||||
|         subtitle, | ||||
|         text_set, | ||||
|       }: StrapiContent["translations"][number]) => { | ||||
|         if (isUndefined(language.data)) | ||||
|           throw new Error("A language is required for a content translation"); | ||||
|         if (isUndefined(text_set)) throw new Error("Only content with text_set are supported"); | ||||
|         if (isUndefined(text_set.source_language.data)) | ||||
|           throw new Error("A language is required for a content translation text_set"); | ||||
|         return { | ||||
|           language: language.data.attributes.code, | ||||
|           sourceLanguage: text_set.source_language.data.attributes.code, | ||||
|           title, | ||||
|           pretitle: pre_title, | ||||
|           subtitle, | ||||
|           summary: isNotEmpty(description) ? plainTextToLexical(description) : undefined, | ||||
|           textContent: plainTextToLexical(text_set.text), | ||||
|           textNotes: isNotEmpty(text_set.notes) ? plainTextToLexical(text_set.notes) : undefined, | ||||
|           textTranscribers: | ||||
|             text_set.transcribers.data && | ||||
|             (await Promise.all( | ||||
|               text_set.transcribers.data?.map(async (recorder) => | ||||
|                 findRecorder(recorder.attributes.username) | ||||
|               ) | ||||
|             )), | ||||
|           textTranslators: | ||||
|             text_set.translators.data && | ||||
|             (await Promise.all( | ||||
|               text_set.translators.data?.map(async (recorder) => | ||||
|                 findRecorder(recorder.attributes.username) | ||||
|               ) | ||||
|             )), | ||||
|           textProofreaders: | ||||
|             text_set.proofreaders.data && | ||||
|             (await Promise.all( | ||||
|               text_set.proofreaders.data?.map(async (recorder) => | ||||
|                 findRecorder(recorder.attributes.username) | ||||
|               ) | ||||
|             )), | ||||
|         }; | ||||
|       }; | ||||
| 
 | ||||
|       const data: MarkOptional<Content, "createdAt" | "id" | "updatedAt" | "updatedBy"> = { | ||||
|         slug, | ||||
|         categories: | ||||
| @ -69,51 +115,7 @@ export const importFromStrapi = createStrapiImportEndpoint<StrapiContent>({ | ||||
|           )), | ||||
|         type: type.data && (await findContentType(type.data?.attributes.slug)), | ||||
|         thumbnail: thumbnailId, | ||||
|         translations: await Promise.all( | ||||
|           translations.map( | ||||
|             async ({ language, title, description, pre_title, subtitle, text_set }) => { | ||||
|               if (isUndefined(language.data)) | ||||
|                 throw new Error("A language is required for a content translation"); | ||||
|               if (isUndefined(text_set)) | ||||
|                 throw new Error("Only content with text_set are supported"); | ||||
|               if (isUndefined(text_set.source_language.data)) | ||||
|                 throw new Error("A language is required for a content translation text_set"); | ||||
|               return { | ||||
|                 language: language.data.attributes.code, | ||||
|                 sourceLanguage: text_set.source_language.data.attributes.code, | ||||
|                 title, | ||||
|                 pretitle: pre_title, | ||||
|                 subtitle, | ||||
|                 summary: isNotEmpty(description) ? plainTextToLexical(description) : undefined, | ||||
|                 textContent: plainTextToLexical(text_set.text), | ||||
|                 textNotes: isNotEmpty(text_set.notes) | ||||
|                   ? plainTextToLexical(text_set.notes) | ||||
|                   : undefined, | ||||
|                 textTranscribers: | ||||
|                   text_set.transcribers.data && | ||||
|                   (await Promise.all( | ||||
|                     text_set.transcribers.data?.map(async (recorder) => | ||||
|                       findRecorder(recorder.attributes.username) | ||||
|                     ) | ||||
|                   )), | ||||
|                 textTranslators: | ||||
|                   text_set.translators.data && | ||||
|                   (await Promise.all( | ||||
|                     text_set.translators.data?.map(async (recorder) => | ||||
|                       findRecorder(recorder.attributes.username) | ||||
|                     ) | ||||
|                   )), | ||||
|                 textProofreaders: | ||||
|                   text_set.proofreaders.data && | ||||
|                   (await Promise.all( | ||||
|                     text_set.proofreaders.data?.map(async (recorder) => | ||||
|                       findRecorder(recorder.attributes.username) | ||||
|                     ) | ||||
|                   )), | ||||
|               }; | ||||
|             } | ||||
|           ) | ||||
|         ), | ||||
|         translations: await Promise.all(translations.map(handleTranslation)), | ||||
|       }; | ||||
|       return data; | ||||
|     }, | ||||
|  | ||||
| @ -0,0 +1,38 @@ | ||||
| import payload from "payload"; | ||||
| import { Collections } from "../../../constants"; | ||||
| import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint"; | ||||
| import { findContent } from "../../../utils/localApi"; | ||||
| 
 | ||||
| type StrapiContent = { | ||||
|   slug: string; | ||||
|   next_contents: { data: { attributes: { slug: string } }[] }; | ||||
| }; | ||||
| 
 | ||||
| export const importRelationsFromStrapi = createStrapiImportEndpoint<StrapiContent>({ | ||||
|   strapi: { | ||||
|     collection: "contents", | ||||
|     params: { | ||||
|       populate: ["next_contents"], | ||||
|     }, | ||||
|   }, | ||||
|   payload: { | ||||
|     path: "/strapi/related-content", | ||||
|     collection: Collections.Contents, | ||||
|     import: async ({ slug, next_contents }, user) => { | ||||
|       if (next_contents.data.length === 0) return; | ||||
|       const currentContent = await findContent(slug); | ||||
|       const nextContents: string[] = []; | ||||
|       for (const nextContent of next_contents.data) { | ||||
|         const result = await findContent(nextContent.attributes.slug); | ||||
|         nextContents.push(result); | ||||
|       } | ||||
| 
 | ||||
|       payload.update({ | ||||
|         collection: Collections.Contents, | ||||
|         id: currentContent, | ||||
|         data: { nextContents }, | ||||
|         user, | ||||
|       }); | ||||
|     }, | ||||
|   }, | ||||
| }); | ||||
| @ -1,4 +1,5 @@ | ||||
| import { CollectionGroups, Collections } from "../../constants"; | ||||
| import { rowField } from "../../fields/rowField/rowField"; | ||||
| import { slugField } from "../../fields/slugField/slugField"; | ||||
| import { translatedFields } from "../../fields/translatedFields/translatedFields"; | ||||
| import { buildCollectionConfig } from "../../utils/collectionConfig"; | ||||
| @ -37,24 +38,19 @@ export const ContentsFolders = buildCollectionConfig({ | ||||
|       }, | ||||
|       fields: [{ name: fields.name, type: "text", required: true }], | ||||
|     }), | ||||
|     { | ||||
|       type: "row", | ||||
|       fields: [ | ||||
|         { | ||||
|           type: "relationship", | ||||
|           name: fields.subfolders, | ||||
|           relationTo: Collections.ContentsFolders, | ||||
|           hasMany: true, | ||||
|           admin: { width: "0%" }, | ||||
|         }, | ||||
|         { | ||||
|           type: "relationship", | ||||
|           name: fields.contents, | ||||
|           relationTo: Collections.Contents, | ||||
|           hasMany: true, | ||||
|           admin: { width: "0%" }, | ||||
|         }, | ||||
|       ], | ||||
|     }, | ||||
|     rowField([ | ||||
|       { | ||||
|         type: "relationship", | ||||
|         name: fields.subfolders, | ||||
|         relationTo: Collections.ContentsFolders, | ||||
|         hasMany: true, | ||||
|       }, | ||||
|       { | ||||
|         type: "relationship", | ||||
|         name: fields.contents, | ||||
|         relationTo: Collections.Contents, | ||||
|         hasMany: true, | ||||
|       }, | ||||
|     ]), | ||||
|   ], | ||||
| }); | ||||
|  | ||||
| @ -4,6 +4,7 @@ import { Collections } from "../../../constants"; | ||||
| import { CollectionEndpoint } from "../../../types/payload"; | ||||
| import { StrapiLanguage } from "../../../types/strapi"; | ||||
| import { isUndefined } from "../../../utils/asserts"; | ||||
| import { findContent } from "../../../utils/localApi"; | ||||
| 
 | ||||
| type StrapiContentsFolder = { | ||||
|   id: string; | ||||
| @ -11,13 +12,14 @@ type StrapiContentsFolder = { | ||||
|     slug: string; | ||||
|     titles?: { title: string; language: StrapiLanguage }[]; | ||||
|     subfolders: { data: StrapiContentsFolder[] }; | ||||
|     contents: { data: { id: number }[] }; | ||||
|     contents: { data: { attributes: { slug: string } }[] }; | ||||
|   }; | ||||
| }; | ||||
| 
 | ||||
| const getStrapiContentFolder = async (id: number): Promise<StrapiContentsFolder> => { | ||||
|   const paramsWithPagination = QueryString.stringify({ | ||||
|     populate: [ | ||||
|       "contents", | ||||
|       "subfolders", | ||||
|       "subfolders.contents", | ||||
|       "subfolders.titles", | ||||
| @ -72,17 +74,31 @@ export const importFromStrapi: CollectionEndpoint = { | ||||
|     } | ||||
| 
 | ||||
|     let foldersCreated = 0; | ||||
|     const errors: string[] = []; | ||||
| 
 | ||||
|     const createContentFolder = async (data: StrapiContentsFolder): Promise<string> => { | ||||
|       const { slug, titles } = data.attributes; | ||||
| 
 | ||||
|       const subfolders = await Promise.all( | ||||
|         data.attributes.subfolders.data.map(createContentFolder) | ||||
|       ); | ||||
|       const { slug, titles } = data.attributes; | ||||
| 
 | ||||
|       const contents: string[] = []; | ||||
|       for (const content of data.attributes.contents.data) { | ||||
|         try { | ||||
|           const result = await findContent(content.attributes.slug); | ||||
|           contents.push(result); | ||||
|         } catch (e) { | ||||
|           errors.push(`Couldn't add ${content.attributes.slug} to folder ${slug}`); | ||||
|         } | ||||
|       } | ||||
| 
 | ||||
|       const result = await payload.create({ | ||||
|         collection: Collections.ContentsFolders, | ||||
|         data: { | ||||
|           slug, | ||||
|           subfolders, | ||||
|           contents, | ||||
|           translations: titles?.map(({ title, language }) => { | ||||
|             if (isUndefined(language.data)) | ||||
|               throw new Error("A language is required for a content folder translation"); | ||||
| @ -102,6 +118,8 @@ export const importFromStrapi: CollectionEndpoint = { | ||||
|       res.status(500).json({ message: "Something went wrong", error: e }); | ||||
|     } | ||||
| 
 | ||||
|     res.status(200).json({ message: `${foldersCreated} entries have been added successfully.` }); | ||||
|     res | ||||
|       .status(200) | ||||
|       .json({ message: `${foldersCreated} entries have been added successfully.`, errors }); | ||||
|   }, | ||||
| }; | ||||
|  | ||||
| @ -2,6 +2,7 @@ import payload from "payload"; | ||||
| import { mustBeAdmin } from "../../accesses/collections/mustBeAdmin"; | ||||
| import { QuickFilters } from "../../components/QuickFilters"; | ||||
| import { CollectionGroups, Collections, KeysTypes, LanguageCodes } from "../../constants"; | ||||
| import { rowField } from "../../fields/rowField/rowField"; | ||||
| import { translatedFields } from "../../fields/translatedFields/translatedFields"; | ||||
| import { beforeDuplicateAddCopyTo } from "../../hooks/beforeDuplicateAddCopyTo"; | ||||
| import { Key } from "../../types/collections"; | ||||
| @ -95,26 +96,21 @@ export const Keys = buildCollectionConfig({ | ||||
|         useAsTitle: fields.translationsName, | ||||
|       }, | ||||
|       fields: [ | ||||
|         { | ||||
|           type: "row", | ||||
|           fields: [ | ||||
|             { | ||||
|               name: fields.translationsName, | ||||
|               type: "text", | ||||
|               required: true, | ||||
|               admin: { width: "0%" }, | ||||
|         rowField([ | ||||
|           { | ||||
|             name: fields.translationsName, | ||||
|             type: "text", | ||||
|             required: true, | ||||
|           }, | ||||
|           { | ||||
|             name: fields.translationsShort, | ||||
|             type: "text", | ||||
|             admin: { | ||||
|               condition: (data: Partial<Key>) => | ||||
|                 isDefined(data.type) && keysTypesWithShort.includes(data.type), | ||||
|             }, | ||||
|             { | ||||
|               name: fields.translationsShort, | ||||
|               type: "text", | ||||
|               admin: { | ||||
|                 condition: (data: Partial<Key>) => | ||||
|                   isDefined(data.type) && keysTypesWithShort.includes(data.type), | ||||
|                 width: "0%", | ||||
|               }, | ||||
|             }, | ||||
|           ], | ||||
|         }, | ||||
|           }, | ||||
|         ]), | ||||
|       ], | ||||
|     }), | ||||
|   ], | ||||
|  | ||||
| @ -1,4 +1,6 @@ | ||||
| import { CollectionGroups, Collections } from "../../constants"; | ||||
| import { backPropagationField } from "../../fields/backPropagationField/backPropagationField"; | ||||
| import { rowField } from "../../fields/rowField/rowField"; | ||||
| import { slugField } from "../../fields/slugField/slugField"; | ||||
| import { translatedFields } from "../../fields/translatedFields/translatedFields"; | ||||
| import { buildCollectionConfig } from "../../utils/collectionConfig"; | ||||
| @ -11,6 +13,7 @@ const fields = { | ||||
|   description: "description", | ||||
|   subfolders: "subfolders", | ||||
|   items: "items", | ||||
|   parentFolders: "parentFolders", | ||||
| } as const satisfies Record<string, string>; | ||||
| 
 | ||||
| export const LibraryFolders = buildCollectionConfig({ | ||||
| @ -44,24 +47,25 @@ export const LibraryFolders = buildCollectionConfig({ | ||||
|         }, | ||||
|       ], | ||||
|     }), | ||||
|     { | ||||
|       type: "row", | ||||
|       fields: [ | ||||
|         { | ||||
|           type: "relationship", | ||||
|           name: fields.subfolders, | ||||
|           relationTo: Collections.LibraryFolders, | ||||
|           hasMany: true, | ||||
|           admin: { width: "0%" }, | ||||
|         }, | ||||
|         { | ||||
|           type: "relationship", | ||||
|           name: fields.items, | ||||
|           relationTo: Collections.LibraryItems, | ||||
|           hasMany: true, | ||||
|           admin: { width: "0%" }, | ||||
|         }, | ||||
|       ], | ||||
|     }, | ||||
|     rowField([ | ||||
|       backPropagationField({ | ||||
|         name: fields.parentFolders, | ||||
|         relationTo: Collections.LibraryFolders, | ||||
|         hasMany: true, | ||||
|         where: ({ id }) => ({ [fields.subfolders]: { equals: id } }), | ||||
|       }), | ||||
|       { | ||||
|         type: "relationship", | ||||
|         name: fields.subfolders, | ||||
|         relationTo: Collections.LibraryFolders, | ||||
|         hasMany: true, | ||||
|       }, | ||||
|       { | ||||
|         type: "relationship", | ||||
|         name: fields.items, | ||||
|         relationTo: Collections.LibraryItems, | ||||
|         hasMany: true, | ||||
|       }, | ||||
|     ]), | ||||
|   ], | ||||
| }); | ||||
|  | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -1,6 +1,9 @@ | ||||
| import { sectionBlock } from "../../blocks/sectionBlock"; | ||||
| import { QuickFilters, publishStatusFilters } from "../../components/QuickFilters"; | ||||
| import { CollectionGroups, Collections, KeysTypes } from "../../constants"; | ||||
| import { imageField } from "../../fields/imageField/imageField"; | ||||
| import { keysField } from "../../fields/keysField/keysField"; | ||||
| import { rowField } from "../../fields/rowField/rowField"; | ||||
| import { slugField } from "../../fields/slugField/slugField"; | ||||
| import { translatedFields } from "../../fields/translatedFields/translatedFields"; | ||||
| import { beforeDuplicateAddCopyTo } from "../../hooks/beforeDuplicateAddCopyTo"; | ||||
| @ -58,39 +61,24 @@ export const Posts = buildVersionedCollectionConfig({ | ||||
|     preview: (doc) => `https://accords-library.com/news/${doc.slug}`, | ||||
|   }, | ||||
|   fields: [ | ||||
|     { | ||||
|       type: "row", | ||||
|       fields: [ | ||||
|         slugField({ name: fields.slug, admin: { width: "0%" } }), | ||||
|         imageField({ | ||||
|           name: fields.thumbnail, | ||||
|           relationTo: Collections.PostsThumbnails, | ||||
|           admin: { width: "0%" }, | ||||
|         }), | ||||
|       ], | ||||
|     }, | ||||
|     { | ||||
|       type: "row", | ||||
|       fields: [ | ||||
|         { | ||||
|           name: fields.authors, | ||||
|           type: "relationship", | ||||
|           relationTo: [Collections.Recorders], | ||||
|           required: true, | ||||
|           minRows: 1, | ||||
|           hasMany: true, | ||||
|           admin: { width: "0%" }, | ||||
|         }, | ||||
|         { | ||||
|           name: fields.categories, | ||||
|           type: "relationship", | ||||
|           relationTo: [Collections.Keys], | ||||
|           filterOptions: { type: { equals: KeysTypes.Categories } }, | ||||
|           hasMany: true, | ||||
|           admin: { allowCreate: false, width: "0%" }, | ||||
|         }, | ||||
|       ], | ||||
|     }, | ||||
|     rowField([ | ||||
|       slugField({ name: fields.slug }), | ||||
|       imageField({ | ||||
|         name: fields.thumbnail, | ||||
|         relationTo: Collections.PostsThumbnails, | ||||
|       }), | ||||
|     ]), | ||||
|     rowField([ | ||||
|       { | ||||
|         name: fields.authors, | ||||
|         type: "relationship", | ||||
|         relationTo: [Collections.Recorders], | ||||
|         required: true, | ||||
|         minRows: 1, | ||||
|         hasMany: true, | ||||
|       }, | ||||
|       keysField({ name: fields.categories, relationTo: KeysTypes.Categories, hasMany: true }), | ||||
|     ]), | ||||
|     translatedFields({ | ||||
|       name: fields.translations, | ||||
|       admin: { useAsTitle: fields.title, hasSourceLanguage: true }, | ||||
| @ -104,57 +92,60 @@ export const Posts = buildVersionedCollectionConfig({ | ||||
|           editor: createEditor({ inlines: true, lists: true, links: true }), | ||||
|         }, | ||||
|         { | ||||
|           type: "row", | ||||
|           fields: [ | ||||
|             { | ||||
|               name: fields.translators, | ||||
|               type: "relationship", | ||||
|               relationTo: Collections.Recorders, | ||||
|               hasMany: true, | ||||
|               hooks: { | ||||
|                 beforeChange: [ | ||||
|                   ({ siblingData }) => { | ||||
|                     if (siblingData.language === siblingData.sourceLanguage) { | ||||
|                       delete siblingData.translators; | ||||
|                     } | ||||
|                   }, | ||||
|                 ], | ||||
|               }, | ||||
|               admin: { | ||||
|                 condition: (_, siblingData) => { | ||||
|                   if ( | ||||
|                     isUndefined(siblingData.language) || | ||||
|                     isUndefined(siblingData.sourceLanguage) | ||||
|                   ) { | ||||
|                     return false; | ||||
|                   } | ||||
|                   return siblingData.language !== siblingData.sourceLanguage; | ||||
|                 }, | ||||
|                 width: "0%", | ||||
|               }, | ||||
|               validate: (translators, { siblingData }) => { | ||||
|                 if (isUndefined(siblingData.language) || isUndefined(siblingData.sourceLanguage)) { | ||||
|                   return true; | ||||
|                 } | ||||
|                 if (siblingData.language === siblingData.sourceLanguage) { | ||||
|                   return true; | ||||
|                 } | ||||
|                 if (isDefined(translators) && translators.length > 0) { | ||||
|                   return true; | ||||
|                 } | ||||
|                 return "This field is required when the language is different from the source language."; | ||||
|               }, | ||||
|             }, | ||||
|             { | ||||
|               name: fields.proofreaders, | ||||
|               type: "relationship", | ||||
|               relationTo: Collections.Recorders, | ||||
|               hasMany: true, | ||||
|               admin: { width: "0%" }, | ||||
|             }, | ||||
|           ], | ||||
|           name: fields.content, | ||||
|           type: "richText", | ||||
|           editor: createEditor({ | ||||
|             images: true, | ||||
|             inlines: true, | ||||
|             alignment: true, | ||||
|             blocks: [sectionBlock], | ||||
|             links: true, | ||||
|             lists: true, | ||||
|           }), | ||||
|         }, | ||||
|         { name: fields.content, type: "richText" }, | ||||
|         rowField([ | ||||
|           { | ||||
|             name: fields.translators, | ||||
|             type: "relationship", | ||||
|             relationTo: Collections.Recorders, | ||||
|             hasMany: true, | ||||
|             hooks: { | ||||
|               beforeChange: [ | ||||
|                 ({ siblingData }) => { | ||||
|                   if (siblingData.language === siblingData.sourceLanguage) { | ||||
|                     delete siblingData.translators; | ||||
|                   } | ||||
|                 }, | ||||
|               ], | ||||
|             }, | ||||
|             admin: { | ||||
|               condition: (_, siblingData) => { | ||||
|                 if (isUndefined(siblingData.language) || isUndefined(siblingData.sourceLanguage)) { | ||||
|                   return false; | ||||
|                 } | ||||
|                 return siblingData.language !== siblingData.sourceLanguage; | ||||
|               }, | ||||
|             }, | ||||
|             validate: (translators, { siblingData }) => { | ||||
|               if (isUndefined(siblingData.language) || isUndefined(siblingData.sourceLanguage)) { | ||||
|                 return true; | ||||
|               } | ||||
|               if (siblingData.language === siblingData.sourceLanguage) { | ||||
|                 return true; | ||||
|               } | ||||
|               if (isDefined(translators) && translators.length > 0) { | ||||
|                 return true; | ||||
|               } | ||||
|               return "This field is required when the language is different from the source language."; | ||||
|             }, | ||||
|           }, | ||||
|           { | ||||
|             name: fields.proofreaders, | ||||
|             type: "relationship", | ||||
|             relationTo: Collections.Recorders, | ||||
|             hasMany: true, | ||||
|           }, | ||||
|         ]), | ||||
|       ], | ||||
|     }), | ||||
|     { | ||||
|  | ||||
| @ -4,6 +4,7 @@ import { mustBeAdmin as mustBeAdminForFields } from "../../accesses/fields/mustB | ||||
| import { QuickFilters } from "../../components/QuickFilters"; | ||||
| import { CollectionGroups, Collections, RecordersRoles } from "../../constants"; | ||||
| import { imageField } from "../../fields/imageField/imageField"; | ||||
| import { rowField } from "../../fields/rowField/rowField"; | ||||
| import { translatedFields } from "../../fields/translatedFields/translatedFields"; | ||||
| import { buildCollectionConfig } from "../../utils/collectionConfig"; | ||||
| import { createEditor } from "../../utils/editor"; | ||||
| @ -77,23 +78,19 @@ export const Recorders = buildCollectionConfig({ | ||||
|   endpoints: [importFromStrapi], | ||||
|   timestamps: false, | ||||
|   fields: [ | ||||
|     { | ||||
|       type: "row", | ||||
|       fields: [ | ||||
|         { | ||||
|           name: fields.username, | ||||
|           type: "text", | ||||
|           unique: true, | ||||
|           required: true, | ||||
|           admin: { description: "The username must be unique", width: "0%" }, | ||||
|         }, | ||||
|         imageField({ | ||||
|           name: fields.avatar, | ||||
|           relationTo: Collections.RecordersThumbnails, | ||||
|           admin: { width: "0%" }, | ||||
|         }), | ||||
|       ], | ||||
|     }, | ||||
|     rowField([ | ||||
|       { | ||||
|         name: fields.username, | ||||
|         type: "text", | ||||
|         unique: true, | ||||
|         required: true, | ||||
|         admin: { description: "The username must be unique" }, | ||||
|       }, | ||||
|       imageField({ | ||||
|         name: fields.avatar, | ||||
|         relationTo: Collections.RecordersThumbnails, | ||||
|       }), | ||||
|     ]), | ||||
|     { | ||||
|       name: fields.languages, | ||||
|       type: "relationship", | ||||
|  | ||||
| @ -51,7 +51,7 @@ export const importFromStrapi = createStrapiImportEndpoint<StrapiRecorder>({ | ||||
|               if (isUndefined(language.data)) | ||||
|                 throw new Error("A language is required for a Recorder biography"); | ||||
|               if (isUndefined(bio)) throw new Error("A bio is required for a Recorder biography"); | ||||
|               return {     | ||||
|               return { | ||||
|                 language: language.data.attributes.code, | ||||
|                 biography: plainTextToLexical(bio), | ||||
|               }; | ||||
|  | ||||
| @ -1,6 +1,7 @@ | ||||
| import { CollectionConfig } from "payload/types"; | ||||
| import { mustBeAdmin } from "../../accesses/collections/mustBeAdmin"; | ||||
| import { CollectionGroups, Collections, VideoSources } from "../../constants"; | ||||
| import { rowField } from "../../fields/rowField/rowField"; | ||||
| import { buildCollectionConfig } from "../../utils/collectionConfig"; | ||||
| import { importFromStrapi } from "./endpoints/importFromStrapi"; | ||||
| 
 | ||||
| @ -47,40 +48,31 @@ export const Videos: CollectionConfig = buildCollectionConfig({ | ||||
|   endpoints: [importFromStrapi], | ||||
|   timestamps: false, | ||||
|   fields: [ | ||||
|     { | ||||
|       type: "row", | ||||
|       fields: [ | ||||
|         { name: fields.uid, type: "text", required: true, unique: true, admin: { width: "0%" } }, | ||||
|         { | ||||
|           name: fields.gone, | ||||
|           type: "checkbox", | ||||
|           defaultValue: false, | ||||
|           required: true, | ||||
|           admin: { | ||||
|             description: | ||||
|               "Is the video no longer available (deleted, privatized, unlisted, blocked...)", | ||||
|             width: "0%", | ||||
|           }, | ||||
|     rowField([ | ||||
|       { name: fields.uid, type: "text", required: true, unique: true }, | ||||
|       { | ||||
|         name: fields.gone, | ||||
|         type: "checkbox", | ||||
|         defaultValue: false, | ||||
|         required: true, | ||||
|         admin: { | ||||
|           description: | ||||
|             "Is the video no longer available (deleted, privatized, unlisted, blocked...)", | ||||
|         }, | ||||
|         { | ||||
|           name: fields.source, | ||||
|           type: "select", | ||||
|           required: true, | ||||
|           options: Object.entries(VideoSources).map(([value, label]) => ({ label, value })), | ||||
|           admin: { width: "0%" }, | ||||
|         }, | ||||
|       ], | ||||
|     }, | ||||
| 
 | ||||
|       }, | ||||
|       { | ||||
|         name: fields.source, | ||||
|         type: "select", | ||||
|         required: true, | ||||
|         options: Object.entries(VideoSources).map(([value, label]) => ({ label, value })), | ||||
|       }, | ||||
|     ]), | ||||
|     { name: fields.title, type: "text", required: true }, | ||||
|     { name: fields.description, type: "textarea" }, | ||||
|     { | ||||
|       type: "row", | ||||
|       fields: [ | ||||
|         { name: fields.likes, type: "number", admin: { width: "0%" } }, | ||||
|         { name: fields.views, type: "number", admin: { width: "0%" } }, | ||||
|       ], | ||||
|     }, | ||||
|     rowField([ | ||||
|       { name: fields.likes, type: "number" }, | ||||
|       { name: fields.views, type: "number" }, | ||||
|     ]), | ||||
|     { | ||||
|       name: fields.publishedDate, | ||||
|       type: "date", | ||||
|  | ||||
| @ -1,6 +1,7 @@ | ||||
| import { CollectionConfig } from "payload/types"; | ||||
| import { mustBeAdmin } from "../../accesses/collections/mustBeAdmin"; | ||||
| import { CollectionGroups, Collections } from "../../constants"; | ||||
| import { rowField } from "../../fields/rowField/rowField"; | ||||
| import { buildCollectionConfig } from "../../utils/collectionConfig"; | ||||
| 
 | ||||
| const fields = { | ||||
| @ -30,12 +31,9 @@ export const VideosChannels: CollectionConfig = buildCollectionConfig({ | ||||
|   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" }, | ||||
|       ], | ||||
|     }, | ||||
|     rowField([ | ||||
|       { name: fields.title, type: "text", required: true }, | ||||
|       { name: fields.subscribers, type: "number" }, | ||||
|     ]), | ||||
|   ], | ||||
| }); | ||||
|  | ||||
| @ -2,6 +2,7 @@ import { RowLabelArgs } from "payload/dist/admin/components/forms/RowLabel/types | ||||
| import { CollectionGroups, Collections, KeysTypes } from "../../constants"; | ||||
| import { imageField } from "../../fields/imageField/imageField"; | ||||
| import { keysField } from "../../fields/keysField/keysField"; | ||||
| import { rowField } from "../../fields/rowField/rowField"; | ||||
| import { slugField } from "../../fields/slugField/slugField"; | ||||
| import { translatedFields } from "../../fields/translatedFields/translatedFields"; | ||||
| import { createEditor } from "../../utils/editor"; | ||||
| @ -45,34 +46,25 @@ export const Weapons = buildVersionedCollectionConfig({ | ||||
|   }, | ||||
|   endpoints: [importFromStrapi, getBySlugEndpoint], | ||||
|   fields: [ | ||||
|     { | ||||
|       type: "row", | ||||
|       fields: [ | ||||
|         slugField({ name: fields.slug, admin: { width: "0%" } }), | ||||
|         imageField({ | ||||
|           name: fields.thumbnail, | ||||
|           relationTo: Collections.WeaponsThumbnails, | ||||
|           admin: { width: "0%" }, | ||||
|         }), | ||||
|       ], | ||||
|     }, | ||||
|     { | ||||
|       type: "row", | ||||
|       fields: [ | ||||
|         keysField({ | ||||
|           name: fields.type, | ||||
|           relationTo: KeysTypes.Weapons, | ||||
|           required: true, | ||||
|           admin: { allowCreate: false, width: "0%" }, | ||||
|         }), | ||||
|         { | ||||
|           name: fields.group, | ||||
|           type: "relationship", | ||||
|           relationTo: Collections.WeaponsGroups, | ||||
|           admin: { width: "0%" }, | ||||
|         }, | ||||
|       ], | ||||
|     }, | ||||
|     rowField([ | ||||
|       slugField({ name: fields.slug }), | ||||
|       imageField({ | ||||
|         name: fields.thumbnail, | ||||
|         relationTo: Collections.WeaponsThumbnails, | ||||
|       }), | ||||
|     ]), | ||||
|     rowField([ | ||||
|       keysField({ | ||||
|         name: fields.type, | ||||
|         relationTo: KeysTypes.Weapons, | ||||
|         required: true, | ||||
|       }), | ||||
|       { | ||||
|         name: fields.group, | ||||
|         type: "relationship", | ||||
|         relationTo: Collections.WeaponsGroups, | ||||
|       }, | ||||
|     ]), | ||||
|     { | ||||
|       name: fields.appearances, | ||||
|       type: "array", | ||||
| @ -91,7 +83,6 @@ export const Weapons = buildVersionedCollectionConfig({ | ||||
|           required: true, | ||||
|           hasMany: true, | ||||
|           relationTo: KeysTypes.Categories, | ||||
|           admin: { allowCreate: false }, | ||||
|         }), | ||||
|         translatedFields({ | ||||
|           name: fields.appearancesTranslations, | ||||
| @ -103,61 +94,46 @@ export const Weapons = buildVersionedCollectionConfig({ | ||||
|             hasCredits: true, | ||||
|           }, | ||||
|           fields: [ | ||||
|             { | ||||
|               type: "row", | ||||
|               fields: [ | ||||
|                 { | ||||
|                   name: fields.appearancesTranslationsName, | ||||
|                   type: "text", | ||||
|                   required: true, | ||||
|                   admin: { width: "0%" }, | ||||
|                 }, | ||||
|                 { | ||||
|                   name: fields.appearancesTranslationsDescription, | ||||
|                   type: "richText", | ||||
|                   editor: createEditor({ inlines: true }), | ||||
|                   admin: { width: "0%" }, | ||||
|                 }, | ||||
|               ], | ||||
|             }, | ||||
|             { | ||||
|               type: "row", | ||||
|               fields: [ | ||||
|                 { | ||||
|                   name: fields.appearancesTranslationsLevel1, | ||||
|                   label: "Level 1", | ||||
|                   type: "richText", | ||||
|                   editor: createEditor({ inlines: true }), | ||||
|                   admin: { width: "0%" }, | ||||
|                 }, | ||||
|                 { | ||||
|                   name: fields.appearancesTranslationsLevel2, | ||||
|                   label: "Level 2", | ||||
|                   type: "richText", | ||||
|                   editor: createEditor({ inlines: true }), | ||||
|                   admin: { width: "0%" }, | ||||
|                 }, | ||||
|               ], | ||||
|             }, | ||||
|             { | ||||
|               type: "row", | ||||
|               fields: [ | ||||
|                 { | ||||
|                   name: fields.appearancesTranslationsLevel3, | ||||
|                   label: "Level 3", | ||||
|                   type: "richText", | ||||
|                   editor: createEditor({ inlines: true }), | ||||
|                   admin: { width: "0%" }, | ||||
|                 }, | ||||
|                 { | ||||
|                   name: fields.appearancesTranslationsLevel4, | ||||
|                   label: "Level 4", | ||||
|                   type: "richText", | ||||
|                   editor: createEditor({ inlines: true }), | ||||
|                   admin: { width: "0%" }, | ||||
|                 }, | ||||
|               ], | ||||
|             }, | ||||
|             rowField([ | ||||
|               { | ||||
|                 name: fields.appearancesTranslationsName, | ||||
|                 type: "text", | ||||
|                 required: true, | ||||
|               }, | ||||
|               { | ||||
|                 name: fields.appearancesTranslationsDescription, | ||||
|                 type: "richText", | ||||
|                 editor: createEditor({ inlines: true }), | ||||
|               }, | ||||
|             ]), | ||||
|             rowField([ | ||||
|               { | ||||
|                 name: fields.appearancesTranslationsLevel1, | ||||
|                 label: "Level 1", | ||||
|                 type: "richText", | ||||
|                 editor: createEditor({ inlines: true }), | ||||
|               }, | ||||
|               { | ||||
|                 name: fields.appearancesTranslationsLevel2, | ||||
|                 label: "Level 2", | ||||
|                 type: "richText", | ||||
|                 editor: createEditor({ inlines: true }), | ||||
|               }, | ||||
|             ]), | ||||
|             rowField([ | ||||
|               { | ||||
|                 name: fields.appearancesTranslationsLevel3, | ||||
|                 label: "Level 3", | ||||
|                 type: "richText", | ||||
|                 editor: createEditor({ inlines: true }), | ||||
|               }, | ||||
|               { | ||||
|                 name: fields.appearancesTranslationsLevel4, | ||||
|                 label: "Level 4", | ||||
|                 type: "richText", | ||||
|                 editor: createEditor({ inlines: true }), | ||||
|               }, | ||||
|             ]), | ||||
|           ], | ||||
|         }), | ||||
|       ], | ||||
|  | ||||
| @ -38,6 +38,7 @@ type Params<S> = { | ||||
|     params: any; | ||||
|   }; | ||||
|   payload: { | ||||
|     path?: string; | ||||
|     collection: Collections; | ||||
|     import?: (strapiObject: S, user: any) => Promise<void>; | ||||
|     convert?: ( | ||||
| @ -55,36 +56,38 @@ export const importStrapiEntries = async <S>({ | ||||
|   const entries = await getAllStrapiEntries(strapiParams.collection, strapiParams.params); | ||||
| 
 | ||||
|   const errors: string[] = []; | ||||
|   let currentCount = 1; | ||||
| 
 | ||||
|   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: await payloadParams.convert(attributes, user), | ||||
|             user, | ||||
|           }); | ||||
|         } else { | ||||
|           throw new Error("No function was provided to handle importing the Strapi data"); | ||||
|         } | ||||
|       } catch (e) { | ||||
|         console.warn(e); | ||||
|         if (typeof e === "object" && isDefined(e) && "name" in e) { | ||||
|           errors.push(`${e.name} with ${id}`); | ||||
|         } | ||||
|   for (const { attributes, id } of entries) { | ||||
|     console.debug(`Handling entry ${currentCount}/${entries.length} (id: ${id})`); | ||||
|     currentCount++; | ||||
|     try { | ||||
|       if (isDefined(payloadParams.import)) { | ||||
|         await payloadParams.import(attributes, user); | ||||
|       } else if (isDefined(payloadParams.convert)) { | ||||
|         await payload.create({ | ||||
|           collection: payloadParams.collection, | ||||
|           data: await payloadParams.convert(attributes, user), | ||||
|           user, | ||||
|         }); | ||||
|       } else { | ||||
|         throw new Error("No function was provided to handle importing the Strapi data"); | ||||
|       } | ||||
|     }) | ||||
|   ); | ||||
|     } catch (e) { | ||||
|       console.warn(e); | ||||
|       if (typeof e === "object" && isDefined(e) && "name" in e) { | ||||
|         const message = "message" in e ? ` (${e.message})` : ""; | ||||
|         errors.push(`${e.name}${message} with ${id}`); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   return { count: entries.length, errors }; | ||||
| }; | ||||
| 
 | ||||
| export const createStrapiImportEndpoint = <S>(params: Params<S>): CollectionEndpoint => ({ | ||||
|   method: "post", | ||||
|   path: "/strapi", | ||||
|   path: params.payload.path ?? "/strapi", | ||||
|   handler: async (req, res) => { | ||||
|     if (!req.user) { | ||||
|       return res.status(403).send({ | ||||
|  | ||||
| @ -2,7 +2,7 @@ import payload from "payload"; | ||||
| import { FieldBase } from "payload/dist/fields/config/types"; | ||||
| import { RelationshipField, Where } from "payload/types"; | ||||
| import { Collections } from "../../constants"; | ||||
| import { isNotEmpty } from "../../utils/asserts"; | ||||
| import { isEmpty } from "../../utils/asserts"; | ||||
| 
 | ||||
| type BackPropagationField = FieldBase & { | ||||
|   where: (data: any) => Where; | ||||
| @ -30,21 +30,22 @@ export const backPropagationField = ({ | ||||
|     ], | ||||
|     afterRead: [ | ||||
|       ...afterRead, | ||||
|       async ({ data }) => { | ||||
|         if (isNotEmpty(data?.id)) { | ||||
|           const result = await payload.find({ | ||||
|             collection: params.relationTo, | ||||
|             where: where(data), | ||||
|             limit: 100, | ||||
|             depth: 0, | ||||
|           }); | ||||
|           if (hasMany) { | ||||
|             return result.docs.map((doc) => doc.id); | ||||
|           } else { | ||||
|             return result.docs[0]?.id; | ||||
|           } | ||||
|       async ({ data, context }) => { | ||||
|         if (isEmpty(data?.id) || context.stopPropagation) { | ||||
|           return hasMany ? [] : undefined; | ||||
|         } | ||||
|         const result = await payload.find({ | ||||
|           collection: params.relationTo, | ||||
|           where: where(data), | ||||
|           limit: 100, | ||||
|           depth: 0, | ||||
|           context: { stopPropagation: true }, | ||||
|         }); | ||||
|         if (hasMany) { | ||||
|           return result.docs.map((doc) => doc.id); | ||||
|         } else { | ||||
|           return result.docs[0]?.id; | ||||
|         } | ||||
|         return hasMany ? [] : undefined; | ||||
|       }, | ||||
|     ], | ||||
|   }, | ||||
|  | ||||
							
								
								
									
										55
									
								
								src/fields/componentField/componentField.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								src/fields/componentField/componentField.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,55 @@ | ||||
| import { CollapsibleField, Condition, Field } from "payload/types"; | ||||
| import { capitalize } from "../../utils/string"; | ||||
| 
 | ||||
| type Props = { | ||||
|   name: string; | ||||
|   label?: string; | ||||
|   admin?: { | ||||
|     description?: string; | ||||
|     condition?: Condition; | ||||
|   }; | ||||
|   fields: Field[]; | ||||
| }; | ||||
| 
 | ||||
| export const componentField = ({ | ||||
|   name, | ||||
|   fields, | ||||
|   label = capitalize(name), | ||||
|   admin, | ||||
| }: Props): CollapsibleField => { | ||||
|   return { | ||||
|     type: "collapsible", | ||||
|     label: name, | ||||
|     admin: { className: "component-field", condition: admin?.condition }, | ||||
|     fields: [ | ||||
|       { | ||||
|         name: `${name}Enabled`, | ||||
|         label, | ||||
|         type: "checkbox", | ||||
|         admin: { | ||||
|           className: "component-field-checkbox", | ||||
|           description: admin?.description, | ||||
|         }, | ||||
|       }, | ||||
|       { | ||||
|         name, | ||||
|         type: "group", | ||||
|         hooks: { | ||||
|           beforeChange: [ | ||||
|             ({ siblingData }) => { | ||||
|               if (!siblingData[`${name}Enabled`]) { | ||||
|                 delete siblingData[name]; | ||||
|               } | ||||
|             }, | ||||
|           ], | ||||
|         }, | ||||
|         admin: { | ||||
|           className: "component-field-group", | ||||
|           condition: (_, siblingData) => siblingData[`${name}Enabled`], | ||||
|           hideGutter: true, | ||||
|         }, | ||||
|         fields, | ||||
|       }, | ||||
|     ], | ||||
|   }; | ||||
| }; | ||||
| @ -10,9 +10,14 @@ type KeysField = FieldBase & { | ||||
| export const keysField = ({ | ||||
|   relationTo, | ||||
|   hasMany = false, | ||||
|   admin, | ||||
|   ...props | ||||
| }: KeysField): RelationshipField => ({ | ||||
|   ...props, | ||||
|   admin: { | ||||
|     allowCreate: false, | ||||
|     ...admin, | ||||
|   }, | ||||
|   type: "relationship", | ||||
|   hasMany: hasMany, | ||||
|   relationTo: Collections.Keys, | ||||
|  | ||||
| @ -1,14 +0,0 @@ | ||||
| import { ArrayField } from "payload/types"; | ||||
| 
 | ||||
| type Props = Omit<ArrayField, "type" | "maxRows" | "minRows">; | ||||
| 
 | ||||
| export const optionalGroupField = ({ | ||||
|   admin: { className = "", ...otherAdmin } = {}, | ||||
|   ...otherProps | ||||
| }: Props): ArrayField => ({ | ||||
|   ...otherProps, | ||||
|   type: "array", | ||||
|   minRows: 0, | ||||
|   maxRows: 1, | ||||
|   admin: { ...otherAdmin, className: `${className} group-array` }, | ||||
| }); | ||||
							
								
								
									
										9
									
								
								src/fields/rowField/rowField.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/fields/rowField/rowField.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | ||||
| import { Field, RowField } from "payload/types"; | ||||
| 
 | ||||
| export const rowField = (fields: Field[]): RowField => ({ | ||||
|   type: "row", | ||||
|   fields: fields.map(({ admin, ...otherConfig }) => ({ | ||||
|     ...otherConfig, | ||||
|     admin: { width: "0%", ...admin }, | ||||
|   })), | ||||
| }); | ||||
| @ -3,6 +3,7 @@ import { array } from "payload/dist/fields/validations"; | ||||
| import { ArrayField, Field } from "payload/types"; | ||||
| import { Collections } from "../../constants"; | ||||
| import { hasDuplicates, isDefined, isUndefined } from "../../utils/asserts"; | ||||
| import { rowField } from "../rowField/rowField"; | ||||
| import { Cell } from "./Cell"; | ||||
| import { RowLabel } from "./RowLabel"; | ||||
| 
 | ||||
| @ -28,7 +29,7 @@ const languageField: Field = { | ||||
|   type: "relationship", | ||||
|   relationTo: Collections.Languages, | ||||
|   required: true, | ||||
|   admin: { allowCreate: false  }, | ||||
|   admin: { allowCreate: false }, | ||||
| }; | ||||
| 
 | ||||
| const sourceLanguageField: Field = { | ||||
| @ -169,9 +170,7 @@ export const translatedFields = ({ | ||||
|     return true; | ||||
|   }, | ||||
|   fields: [ | ||||
|     hasSourceLanguage | ||||
|       ? { type: "row", fields: [languageField, sourceLanguageField] } | ||||
|       : languageField, | ||||
|     rowField(hasSourceLanguage ? [languageField, sourceLanguageField] : [languageField]), | ||||
|     ...fields, | ||||
|     ...(hasCredits ? [creditFields] : []), | ||||
|   ], | ||||
|  | ||||
| @ -1,3 +1,5 @@ | ||||
| // Theme override | ||||
| 
 | ||||
| html[data-theme="dark"] { | ||||
|   --color-base-0: #ffffff; | ||||
|   --color-base-50: #ebeae7; | ||||
| @ -46,8 +48,14 @@ html[data-theme="light"] { | ||||
|   --color-base-1000: #000000; | ||||
| } | ||||
| 
 | ||||
| // Fix height disparities between certain fields | ||||
| 
 | ||||
| .field-type.row { | ||||
|   padding: 1rem 0; | ||||
| 
 | ||||
|   > div > .field-type.checkbox { | ||||
|     padding-top: 2.4rem; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .field-type.radio-group { | ||||
| @ -56,6 +64,8 @@ html[data-theme="light"] { | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| // Hide header on blocks with classname "no-label" | ||||
| 
 | ||||
| .field-type.no-label > header { | ||||
|   display: none; | ||||
| } | ||||
| @ -68,37 +78,49 @@ html[data-theme="light"] { | ||||
|   display: none; | ||||
| } | ||||
| 
 | ||||
| // Reduce margin on Lexical blocks with the classname "reduced-margins" | ||||
| 
 | ||||
| .rich-text-lexical.field-type.reduced-margins { | ||||
|   margin-top: -0.75em; | ||||
|   margin-bottom: -2rem; | ||||
| } | ||||
| 
 | ||||
| .field-type.array-field.group-array { | ||||
|   > .array-field__header { | ||||
|     .array-field__header-actions { | ||||
| // CSS for componentField | ||||
| 
 | ||||
| .collapsible-field.component-field { | ||||
|   > .collapsible > .collapsible__toggle-wrap { | ||||
|     display: none; | ||||
|   } | ||||
| 
 | ||||
|   .component-field-checkbox { | ||||
|     margin-bottom: 0; | ||||
| 
 | ||||
|     .checkbox-input { | ||||
|       flex-direction: row-reverse; | ||||
|       justify-content: space-between; | ||||
|       display: flex; | ||||
|       gap: 16px; | ||||
|       pointer-events: none; | ||||
| 
 | ||||
|       > .checkbox-input__input { | ||||
|         pointer-events: auto; | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     .field-label { | ||||
|       font-weight: 500; | ||||
|       font-family: "Suisse Intl"; | ||||
|       font-size: 1.7788461538rem; | ||||
|       padding: 0; | ||||
|       transform: translateY(2px); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   .component-field-group { | ||||
|     margin-top: 2rem; | ||||
| 
 | ||||
|     > .group-field__wrap > .group-field__header { | ||||
|       display: none; | ||||
|     } | ||||
|   } | ||||
|   > div { | ||||
|     > div { | ||||
|       > div { | ||||
|         > .collapsible__toggle-wrap { | ||||
|           .array-actions__duplicate { | ||||
|             display: none; | ||||
|           } | ||||
| 
 | ||||
|           .collapsible__drag, | ||||
|           .collapsible__toggle, | ||||
|           .collapsible__header-wrap, | ||||
|           .collapsible__indicator { | ||||
|             display: none; | ||||
|           } | ||||
| 
 | ||||
|           .collapsible__actions-wrap { | ||||
|             z-index: 1; | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -22,32 +22,32 @@ export type RecorderBiographies = { | ||||
| 
 | ||||
| export interface Config { | ||||
|   collections: { | ||||
|     'library-folders': LibraryFolder; | ||||
|     'library-items': LibraryItem; | ||||
|     "library-folders": LibraryFolder; | ||||
|     "library-items": LibraryItem; | ||||
|     contents: Content; | ||||
|     'contents-folders': ContentsFolder; | ||||
|     "contents-folders": ContentsFolder; | ||||
|     posts: Post; | ||||
|     'chronology-items': ChronologyItem; | ||||
|     'chronology-eras': ChronologyEra; | ||||
|     "chronology-items": ChronologyItem; | ||||
|     "chronology-eras": ChronologyEra; | ||||
|     weapons: Weapon; | ||||
|     'weapons-groups': WeaponsGroup; | ||||
|     'weapons-thumbnails': WeaponsThumbnail; | ||||
|     'contents-thumbnails': ContentsThumbnail; | ||||
|     'library-items-thumbnails': LibraryItemThumbnail; | ||||
|     'library-items-scans': LibraryItemScans; | ||||
|     'library-items-gallery': LibraryItemGallery; | ||||
|     'recorders-thumbnails': RecordersThumbnail; | ||||
|     'posts-thumbnails': PostThumbnail; | ||||
|     "weapons-groups": WeaponsGroup; | ||||
|     "weapons-thumbnails": WeaponsThumbnail; | ||||
|     "contents-thumbnails": ContentsThumbnail; | ||||
|     "library-items-thumbnails": LibraryItemThumbnail; | ||||
|     "library-items-scans": LibraryItemScans; | ||||
|     "library-items-gallery": LibraryItemGallery; | ||||
|     "recorders-thumbnails": RecordersThumbnail; | ||||
|     "posts-thumbnails": PostThumbnail; | ||||
|     files: File; | ||||
|     Notes: Note; | ||||
|     videos: Video; | ||||
|     'videos-channels': VideosChannel; | ||||
|     "videos-channels": VideosChannel; | ||||
|     languages: Language; | ||||
|     currencies: Currency; | ||||
|     recorders: Recorder; | ||||
|     keys: Key; | ||||
|     'payload-preferences': PayloadPreference; | ||||
|     'payload-migrations': PayloadMigration; | ||||
|     "payload-preferences": PayloadPreference; | ||||
|     "payload-migrations": PayloadMigration; | ||||
|   }; | ||||
|   globals: {}; | ||||
| } | ||||
| @ -62,6 +62,7 @@ export interface LibraryFolder { | ||||
|     }[]; | ||||
|     id?: string; | ||||
|   }[]; | ||||
|   parentFolders?: string[] | LibraryFolder[]; | ||||
|   subfolders?: string[] | LibraryFolder[]; | ||||
|   items?: string[] | LibraryItem[]; | ||||
| } | ||||
| @ -71,20 +72,20 @@ export interface Language { | ||||
| } | ||||
| export interface LibraryItem { | ||||
|   id: string; | ||||
|   itemType?: 'Textual' | 'Audio' | 'Video' | 'Game' | 'Other'; | ||||
|   digital: boolean; | ||||
|   itemType?: "Textual" | "Audio" | "Video" | "Game" | "Other"; | ||||
|   slug: string; | ||||
|   thumbnail?: string | LibraryItemThumbnail; | ||||
|   pretitle?: string; | ||||
|   title: string; | ||||
|   subtitle?: string; | ||||
|   rootItem: boolean; | ||||
|   primary: boolean; | ||||
|   digital: boolean; | ||||
|   gallery?: { | ||||
|     image?: string | LibraryItemGallery; | ||||
|     id?: string; | ||||
|   }[]; | ||||
|   scansEnabled?: boolean; | ||||
|   scans?: { | ||||
|     coverEnabled?: boolean; | ||||
|     cover?: { | ||||
|       front?: string | LibraryItemScans; | ||||
|       spine?: string | LibraryItemScans; | ||||
| @ -94,8 +95,8 @@ export interface LibraryItem { | ||||
|       flapBack?: string | LibraryItemScans; | ||||
|       insideFlapFront?: string | LibraryItemScans; | ||||
|       insideFlapBack?: string | LibraryItemScans; | ||||
|       id?: string; | ||||
|     }[]; | ||||
|     }; | ||||
|     dustjacketEnabled?: boolean; | ||||
|     dustjacket?: { | ||||
|       front?: string | LibraryItemScans; | ||||
|       spine?: string | LibraryItemScans; | ||||
| @ -107,8 +108,8 @@ export interface LibraryItem { | ||||
|       flapBack?: string | LibraryItemScans; | ||||
|       insideFlapFront?: string | LibraryItemScans; | ||||
|       insideFlapBack?: string | LibraryItemScans; | ||||
|       id?: string; | ||||
|     }[]; | ||||
|     }; | ||||
|     obiEnabled?: boolean; | ||||
|     obi?: { | ||||
|       front?: string | LibraryItemScans; | ||||
|       spine?: string | LibraryItemScans; | ||||
| @ -120,22 +121,19 @@ export interface LibraryItem { | ||||
|       flapBack?: string | LibraryItemScans; | ||||
|       insideFlapFront?: string | LibraryItemScans; | ||||
|       insideFlapBack?: string | LibraryItemScans; | ||||
|       id?: string; | ||||
|     }[]; | ||||
|     }; | ||||
|     pages?: { | ||||
|       page: number; | ||||
|       image: string | LibraryItemScans; | ||||
|       id?: string; | ||||
|     }[]; | ||||
|     downloadable: boolean; | ||||
|     id?: string; | ||||
|   }[]; | ||||
|   }; | ||||
|   textual?: { | ||||
|     subtype?: string[] | Key[]; | ||||
|     languages?: string[] | Language[]; | ||||
|     pageCount?: number; | ||||
|     bindingType?: 'Paperback' | 'Hardcover'; | ||||
|     pageOrder?: 'LeftToRight' | 'RightToLeft'; | ||||
|     bindingType?: "Paperback" | "Hardcover"; | ||||
|     pageOrder?: "LeftToRight" | "RightToLeft"; | ||||
|   }; | ||||
|   audio?: { | ||||
|     audioSubtype?: string[] | Key[]; | ||||
| @ -147,6 +145,17 @@ export interface LibraryItem { | ||||
|   }; | ||||
|   releaseDate?: string; | ||||
|   categories?: string[] | Key[]; | ||||
|   sizeEnabled?: boolean; | ||||
|   size?: { | ||||
|     width: number; | ||||
|     height: number; | ||||
|     thickness?: number; | ||||
|   }; | ||||
|   priceEnabled?: boolean; | ||||
|   price?: { | ||||
|     amount: number; | ||||
|     currency: string | Currency; | ||||
|   }; | ||||
|   translations?: { | ||||
|     language: string | Language; | ||||
|     description: { | ||||
| @ -154,21 +163,13 @@ export interface LibraryItem { | ||||
|     }[]; | ||||
|     id?: string; | ||||
|   }[]; | ||||
|   size?: { | ||||
|     width: number; | ||||
|     height: number; | ||||
|     thickness?: number; | ||||
|     id?: string; | ||||
|   }[]; | ||||
|   price?: { | ||||
|     amount: number; | ||||
|     currency: string | Currency; | ||||
|     id?: string; | ||||
|   }[]; | ||||
|   urls?: { | ||||
|     url: string; | ||||
|     id?: string; | ||||
|   }[]; | ||||
|   parentFolders?: string[] | LibraryFolder[]; | ||||
|   parentItems?: string[] | LibraryItem[]; | ||||
|   subitems?: string[] | LibraryItem[]; | ||||
|   contents?: { | ||||
|     content: string | Content; | ||||
|     pageStart?: number; | ||||
| @ -183,7 +184,7 @@ export interface LibraryItem { | ||||
|   updatedBy: string | Recorder; | ||||
|   updatedAt: string; | ||||
|   createdAt: string; | ||||
|   _status?: 'draft' | 'published'; | ||||
|   _status?: "draft" | "published"; | ||||
| } | ||||
| export interface LibraryItemThumbnail { | ||||
|   id: string; | ||||
| @ -301,22 +302,22 @@ export interface Key { | ||||
|   id: string; | ||||
|   name: string; | ||||
|   type: | ||||
|     | 'Contents' | ||||
|     | 'LibraryAudio' | ||||
|     | 'LibraryVideo' | ||||
|     | 'LibraryTextual' | ||||
|     | 'LibraryGroup' | ||||
|     | 'Library' | ||||
|     | 'Weapons' | ||||
|     | 'GamePlatforms' | ||||
|     | 'Categories' | ||||
|     | 'Wordings'; | ||||
|     | "Contents" | ||||
|     | "LibraryAudio" | ||||
|     | "LibraryVideo" | ||||
|     | "LibraryTextual" | ||||
|     | "LibraryGroup" | ||||
|     | "Library" | ||||
|     | "Weapons" | ||||
|     | "GamePlatforms" | ||||
|     | "Categories" | ||||
|     | "Wordings"; | ||||
|   translations?: CategoryTranslations; | ||||
| } | ||||
| export interface File { | ||||
|   id: string; | ||||
|   filename: string; | ||||
|   type: 'LibraryScans' | 'LibrarySoundtracks' | 'ContentVideo' | 'ContentAudio'; | ||||
|   type: "LibraryScans" | "LibrarySoundtracks" | "ContentVideo" | "ContentAudio"; | ||||
|   updatedAt: string; | ||||
|   createdAt: string; | ||||
| } | ||||
| @ -329,6 +330,7 @@ export interface Content { | ||||
|   thumbnail?: string | ContentsThumbnail; | ||||
|   categories?: string[] | Key[]; | ||||
|   type?: string | Key; | ||||
|   libraryItems?: string[] | LibraryItem[]; | ||||
|   translations: { | ||||
|     language: string | Language; | ||||
|     sourceLanguage: string | Language; | ||||
| @ -354,10 +356,13 @@ export interface Content { | ||||
|     audio?: string | File; | ||||
|     id?: string; | ||||
|   }[]; | ||||
|   folders?: string[] | ContentsFolder[]; | ||||
|   previousContents?: string[] | Content[]; | ||||
|   nextContents?: string[] | Content[]; | ||||
|   updatedBy: string | Recorder; | ||||
|   updatedAt: string; | ||||
|   createdAt: string; | ||||
|   _status?: 'draft' | 'published'; | ||||
|   _status?: "draft" | "published"; | ||||
| } | ||||
| export interface ContentsThumbnail { | ||||
|   id: string; | ||||
| @ -403,7 +408,7 @@ export interface Recorder { | ||||
|   avatar?: string | RecordersThumbnail; | ||||
|   languages?: string[] | Language[]; | ||||
|   biographies?: RecorderBiographies; | ||||
|   role?: ('Admin' | 'Recorder' | 'Api')[]; | ||||
|   role?: ("Admin" | "Recorder" | "Api")[]; | ||||
|   anonymize: boolean; | ||||
|   email: string; | ||||
|   resetPasswordToken?: string; | ||||
| @ -461,20 +466,20 @@ export interface Post { | ||||
|   thumbnail?: string | PostThumbnail; | ||||
|   authors: | ||||
|     | { | ||||
|         relationTo: 'recorders'; | ||||
|         relationTo: "recorders"; | ||||
|         value: string; | ||||
|       }[] | ||||
|     | { | ||||
|         relationTo: 'recorders'; | ||||
|         relationTo: "recorders"; | ||||
|         value: Recorder; | ||||
|       }[]; | ||||
|   categories?: | ||||
|     | { | ||||
|         relationTo: 'keys'; | ||||
|         relationTo: "keys"; | ||||
|         value: string; | ||||
|       }[] | ||||
|     | { | ||||
|         relationTo: 'keys'; | ||||
|         relationTo: "keys"; | ||||
|         value: Key; | ||||
|       }[]; | ||||
|   translations: { | ||||
| @ -484,11 +489,11 @@ export interface Post { | ||||
|     summary?: { | ||||
|       [k: string]: unknown; | ||||
|     }[]; | ||||
|     translators?: string[] | Recorder[]; | ||||
|     proofreaders?: string[] | Recorder[]; | ||||
|     content?: { | ||||
|       [k: string]: unknown; | ||||
|     }[]; | ||||
|     translators?: string[] | Recorder[]; | ||||
|     proofreaders?: string[] | Recorder[]; | ||||
|     id?: string; | ||||
|   }[]; | ||||
|   publishedDate: string; | ||||
| @ -496,7 +501,7 @@ export interface Post { | ||||
|   updatedBy: string | Recorder; | ||||
|   updatedAt: string; | ||||
|   createdAt: string; | ||||
|   _status?: 'draft' | 'published'; | ||||
|   _status?: "draft" | "published"; | ||||
| } | ||||
| export interface PostThumbnail { | ||||
|   id: string; | ||||
| @ -547,11 +552,11 @@ export interface ChronologyItem { | ||||
|   events: { | ||||
|     source?: | ||||
|       | { | ||||
|           relationTo: 'contents'; | ||||
|           relationTo: "contents"; | ||||
|           value: string | Content; | ||||
|         } | ||||
|       | { | ||||
|           relationTo: 'library-items'; | ||||
|           relationTo: "library-items"; | ||||
|           value: string | LibraryItem; | ||||
|         }; | ||||
|     translations: { | ||||
| @ -574,7 +579,7 @@ export interface ChronologyItem { | ||||
|   updatedBy: string | Recorder; | ||||
|   updatedAt: string; | ||||
|   createdAt: string; | ||||
|   _status?: 'draft' | 'published'; | ||||
|   _status?: "draft" | "published"; | ||||
| } | ||||
| export interface ChronologyEra { | ||||
|   id: string; | ||||
| @ -630,7 +635,7 @@ export interface Weapon { | ||||
|   updatedBy: string | Recorder; | ||||
|   updatedAt: string; | ||||
|   createdAt: string; | ||||
|   _status?: 'draft' | 'published'; | ||||
|   _status?: "draft" | "published"; | ||||
| } | ||||
| export interface WeaponsThumbnail { | ||||
|   id: string; | ||||
| @ -700,7 +705,7 @@ export interface Video { | ||||
|   id: string; | ||||
|   uid: string; | ||||
|   gone: boolean; | ||||
|   source: 'YouTube' | 'NicoNico' | 'Tumblr'; | ||||
|   source: "YouTube" | "NicoNico" | "Tumblr"; | ||||
|   title: string; | ||||
|   description?: string; | ||||
|   likes?: number; | ||||
| @ -717,7 +722,7 @@ export interface VideosChannel { | ||||
| export interface PayloadPreference { | ||||
|   id: string; | ||||
|   user: { | ||||
|     relationTo: 'recorders'; | ||||
|     relationTo: "recorders"; | ||||
|     value: string | Recorder; | ||||
|   }; | ||||
|   key?: string; | ||||
| @ -741,7 +746,6 @@ export interface PayloadMigration { | ||||
|   createdAt: string; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| declare module 'payload' { | ||||
| declare module "payload" { | ||||
|   export interface GeneratedTypes extends Config {} | ||||
| } | ||||
| } | ||||
|  | ||||
| @ -7,6 +7,7 @@ export const findWeaponType = async (name: string): Promise<string> => { | ||||
|   const key = await payload.find({ | ||||
|     collection: Collections.Keys, | ||||
|     where: { name: { equals: name }, type: { equals: KeysTypes.Weapons } }, | ||||
|     depth: 0, | ||||
|   }); | ||||
|   if (!key.docs[0]) throw new Error(`Weapon type ${name} wasn't found`); | ||||
|   return key.docs[0].id; | ||||
| @ -16,27 +17,40 @@ export const findCategory = async (name: string): Promise<string> => { | ||||
|   const key = await payload.find({ | ||||
|     collection: Collections.Keys, | ||||
|     where: { name: { equals: name }, type: { equals: KeysTypes.Categories } }, | ||||
|     depth: 0, | ||||
|   }); | ||||
|   if (!key.docs[0]) throw new Error(`Category ${name} wasn't found`); | ||||
|   return key.docs[0]?.id; | ||||
|   return key.docs[0].id; | ||||
| }; | ||||
| 
 | ||||
| export const findRecorder = async (name: string): Promise<string> => { | ||||
|   const recorder = await payload.find({ | ||||
|     collection: Collections.Recorders, | ||||
|     where: { username: { equals: name } }, | ||||
|     depth: 0, | ||||
|   }); | ||||
|   if (!recorder.docs[0]) throw new Error(`Recorder ${name} wasn't found`); | ||||
|   return recorder.docs[0]?.id; | ||||
|   return recorder.docs[0].id; | ||||
| }; | ||||
| 
 | ||||
| export const findContentType = async (name: string): Promise<string> => { | ||||
|   const key = await payload.find({ | ||||
|     collection: Collections.Keys, | ||||
|     where: { name: { equals: name }, type: { equals: KeysTypes.Contents } }, | ||||
|     depth: 0, | ||||
|   }); | ||||
|   if (!key.docs[0]) throw new Error(`Content type ${name} wasn't found`); | ||||
|   return key.docs[0]?.id; | ||||
|   return key.docs[0].id; | ||||
| }; | ||||
| 
 | ||||
| export const findContent = async (slug: string): Promise<string> => { | ||||
|   const content = await payload.find({ | ||||
|     collection: Collections.Contents, | ||||
|     where: { slug: { equals: slug } }, | ||||
|     depth: 0, | ||||
|   }); | ||||
|   if (!content.docs[0]) throw new Error(`Content ${slug} wasn't found`); | ||||
|   return content.docs[0].id; | ||||
| }; | ||||
| 
 | ||||
| type UploadStrapiImage = { | ||||
| @ -58,7 +72,7 @@ export const uploadStrapiImage = async ({ | ||||
|     if (existingImage.docs[0]) { | ||||
|       return existingImage.docs[0].id; | ||||
|     } | ||||
|   | ||||
| 
 | ||||
|     const url = `${process.env.STRAPI_URI}${image.data.attributes.url}`; | ||||
| 
 | ||||
|     const blob = await (await fetch(url)).blob(); | ||||
|  | ||||
| @ -7,7 +7,7 @@ export const shortenEllipsis = (text: string, length: number): string => | ||||
| export const formatLanguageCode = (code: string): string => | ||||
|   tags(code).valid() ? tags(code).language()?.descriptions()[0] ?? code : code; | ||||
| 
 | ||||
| const capitalize = (string: string): string => { | ||||
| export const capitalize = (string: string): string => { | ||||
|   const [firstLetter, ...otherLetters] = string; | ||||
|   if (isUndefined(firstLetter)) return ""; | ||||
|   return [firstLetter.toUpperCase(), ...otherLetters].join(""); | ||||
|  | ||||
| @ -14,7 +14,7 @@ const updatedByField = (): RelationshipField => ({ | ||||
|   type: "relationship", | ||||
|   required: true, | ||||
|   relationTo: Collections.Recorders, | ||||
|   admin: { readOnly: true }, | ||||
|   admin: { readOnly: true, hidden: true }, | ||||
| }); | ||||
| 
 | ||||
| type BuildVersionedCollectionConfig = Omit<BuildCollectionConfig, "timestamps" | "versions">; | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 DrMint
						DrMint