Removed posts and contents
This commit is contained in:
		
							parent
							
								
									8a819de41e
								
							
						
					
					
						commit
						a5b636a858
					
				| @ -1,230 +0,0 @@ | ||||
| import { Where } from "payload/types"; | ||||
| import { sectionBlock } from "../../blocks/sectionBlock"; | ||||
| import { transcriptBlock } from "../../blocks/transcriptBlock"; | ||||
| import { CollectionGroups, Collections, FileTypes } from "../../constants"; | ||||
| import { backPropagationField } from "../../fields/backPropagationField/backPropagationField"; | ||||
| import { fileField } from "../../fields/fileField/fileField"; | ||||
| import { imageField } from "../../fields/imageField/imageField"; | ||||
| import { rowField } from "../../fields/rowField/rowField"; | ||||
| import { slugField } from "../../fields/slugField/slugField"; | ||||
| import { tagsField } from "../../fields/tagsField/tagsField"; | ||||
| import { translatedFields } from "../../fields/translatedFields/translatedFields"; | ||||
| import { beforeDuplicateAddCopyTo } from "../../hooks/beforeDuplicateAddCopyTo"; | ||||
| import { beforeDuplicatePiping } from "../../hooks/beforeDuplicatePiping"; | ||||
| import { beforeDuplicateUnpublish } from "../../hooks/beforeDuplicateUnpublish"; | ||||
| import { isDefined } from "../../utils/asserts"; | ||||
| import { createEditor } from "../../utils/editor"; | ||||
| import { buildVersionedCollectionConfig } from "../../utils/versionedCollectionConfig"; | ||||
| import { getBySlugEndpoint } from "./endpoints/getBySlugEndpoint"; | ||||
| import { importFromStrapi } from "./endpoints/importFromStrapi"; | ||||
| import { importRelationsFromStrapi } from "./endpoints/importRelationsFromStrapi"; | ||||
| 
 | ||||
| const fields = { | ||||
|   slug: "slug", | ||||
|   thumbnail: "thumbnail", | ||||
|   translations: "translations", | ||||
|   pretitle: "pretitle", | ||||
|   title: "title", | ||||
|   subtitle: "subtitle", | ||||
|   summary: "summary", | ||||
|   textContent: "textContent", | ||||
|   textTranscribers: "textTranscribers", | ||||
|   textTranslators: "textTranslators", | ||||
|   textProofreaders: "textProofreaders", | ||||
|   textNotes: "textNotes", | ||||
|   video: "video", | ||||
|   videoNotes: "videoNotes", | ||||
|   audio: "audio", | ||||
|   audioNotes: "audioNotes", | ||||
|   status: "status", | ||||
|   updatedBy: "updatedBy", | ||||
|   previousContents: "previousContents", | ||||
|   nextContents: "nextContents", | ||||
|   folders: "folders", | ||||
|   libraryItems: "libraryItems", | ||||
|   tags: "tags", | ||||
| } as const satisfies Record<string, string>; | ||||
| 
 | ||||
| export const Contents = buildVersionedCollectionConfig({ | ||||
|   slug: Collections.Contents, | ||||
|   labels: { | ||||
|     singular: "Content", | ||||
|     plural: "Contents", | ||||
|   }, | ||||
|   defaultSort: fields.slug, | ||||
|   admin: { | ||||
|     useAsTitle: fields.slug, | ||||
|     description: | ||||
|       "All the contents (textual, audio, and video) from the Library or other online sources.", | ||||
|     defaultColumns: [fields.thumbnail, fields.slug, fields.translations, fields.status], | ||||
|     group: CollectionGroups.Collections, | ||||
|     hooks: { | ||||
|       beforeDuplicate: beforeDuplicatePiping([ | ||||
|         beforeDuplicateUnpublish, | ||||
|         beforeDuplicateAddCopyTo(fields.slug), | ||||
|       ]), | ||||
|     }, | ||||
|   }, | ||||
|   endpoints: [importFromStrapi, importRelationsFromStrapi, getBySlugEndpoint], | ||||
|   fields: [ | ||||
|     rowField([ | ||||
|       slugField({ name: fields.slug }), | ||||
|       imageField({ | ||||
|         name: fields.thumbnail, | ||||
|         relationTo: Collections.ContentsThumbnails, | ||||
|       }), | ||||
|     ]), | ||||
|     tagsField({ name: fields.tags }), | ||||
|     translatedFields({ | ||||
|       name: fields.translations, | ||||
|       admin: { useAsTitle: fields.title, hasSourceLanguage: true }, | ||||
|       required: true, | ||||
|       minRows: 1, | ||||
|       fields: [ | ||||
|         rowField([ | ||||
|           { name: fields.pretitle, type: "text" }, | ||||
|           { name: fields.title, type: "text", required: true }, | ||||
|           { name: fields.subtitle, type: "text" }, | ||||
|         ]), | ||||
|         { | ||||
|           name: fields.summary, | ||||
|           type: "richText", | ||||
|           editor: createEditor({ inlines: true, lists: true, links: true }), | ||||
|         }, | ||||
|         { | ||||
|           type: "tabs", | ||||
|           admin: { | ||||
|             condition: (_, siblingData) => | ||||
|               isDefined(siblingData.language) && isDefined(siblingData.sourceLanguage), | ||||
|           }, | ||||
|           tabs: [ | ||||
|             { | ||||
|               label: "Text", | ||||
|               fields: [ | ||||
|                 { | ||||
|                   name: fields.textContent, | ||||
|                   type: "richText", | ||||
|                   label: false, | ||||
|                   editor: createEditor({ | ||||
|                     blocks: [sectionBlock, transcriptBlock], | ||||
|                     images: true, | ||||
|                     inlines: true, | ||||
|                     lists: true, | ||||
|                     links: true, | ||||
|                     relations: true, | ||||
|                     alignment: true, | ||||
|                   }), | ||||
|                 }, | ||||
|                 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, | ||||
|                     }, | ||||
|                   }, | ||||
|                   { | ||||
|                     name: fields.textProofreaders, | ||||
|                     label: "Proofreaders", | ||||
|                     type: "relationship", | ||||
|                     relationTo: Collections.Recorders, | ||||
|                     hasMany: true, | ||||
|                   }, | ||||
|                 ]), | ||||
|                 { | ||||
|                   name: fields.textNotes, | ||||
|                   label: "Notes", | ||||
|                   type: "richText", | ||||
|                   editor: createEditor({ inlines: true, lists: true, links: true }), | ||||
|                 }, | ||||
|               ], | ||||
|             }, | ||||
|             { | ||||
|               label: "Video", | ||||
|               fields: [ | ||||
|                 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: [ | ||||
|                 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, | ||||
|         relationTo: Collections.Folders, | ||||
|         hasMany: true, | ||||
|         where: ({ id }) => ({ | ||||
|           and: [ | ||||
|             { "files.value": { equals: id } }, | ||||
|             { "files.relationTo": { equals: Collections.Contents } }, | ||||
|           ] as Where[], | ||||
|         }), | ||||
|       }), | ||||
|       backPropagationField({ | ||||
|         name: fields.libraryItems, | ||||
|         hasMany: true, | ||||
|         relationTo: Collections.LibraryItems, | ||||
|         where: ({ id }) => ({ "contents.content": { equals: id } }), | ||||
|       }), | ||||
|     ]), | ||||
|     rowField([ | ||||
|       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, | ||||
|       }, | ||||
|     ]), | ||||
|   ], | ||||
| }); | ||||
| @ -1,50 +0,0 @@ | ||||
| import { Collections } from "../../../constants"; | ||||
| import { createGetByEndpoint } from "../../../endpoints/createGetByEndpoint"; | ||||
| import { EndpointContent } from "../../../sdk"; | ||||
| import { Content } from "../../../types/collections"; | ||||
| import { isPayloadArrayType, isPayloadType, isValidPayloadImage } from "../../../utils/asserts"; | ||||
| import { convertTagsToGroups } from "../../../utils/tags"; | ||||
| 
 | ||||
| export const getBySlugEndpoint = createGetByEndpoint( | ||||
|   Collections.Contents, | ||||
|   "slug", | ||||
|   ({ thumbnail, slug, translations, tags }: Content): EndpointContent => ({ | ||||
|     slug, | ||||
|     ...(isValidPayloadImage(thumbnail) ? { thumbnail } : {}), | ||||
|     tagGroups: convertTagsToGroups(tags), | ||||
|     translations: translations.map((translation) => { | ||||
|       const { language, sourceLanguage, title, subtitle, pretitle, summary } = translation; | ||||
|       const text = handleTextContent(translation); | ||||
| 
 | ||||
|       return { | ||||
|         language: isPayloadType(language) ? language.id : language, | ||||
|         sourceLanguage: isPayloadType(sourceLanguage) ? sourceLanguage.id : sourceLanguage, | ||||
|         ...(pretitle ? { pretitle } : {}), | ||||
|         title, | ||||
|         ...(subtitle ? { subtitle } : {}), | ||||
|         ...(summary ? { summary } : {}), | ||||
|         format: { ...(text ? { text } : {}) }, | ||||
|       }; | ||||
|     }), | ||||
|   }) | ||||
| ); | ||||
| 
 | ||||
| 
 | ||||
| const handleTextContent = ({ | ||||
|   textContent, | ||||
|   textNotes, | ||||
|   textProofreaders, | ||||
|   textTranscribers, | ||||
|   textTranslators, | ||||
| }: Content["translations"][number]): EndpointContent["translations"][number]["format"]["text"] => { | ||||
|   if (!textContent) return undefined; | ||||
| 
 | ||||
|   return { | ||||
|     content: textContent, | ||||
|     toc: [], | ||||
|     translators: isPayloadArrayType(textTranslators) ? textTranslators.map(({ id }) => id) : [], | ||||
|     transcribers: isPayloadArrayType(textTranscribers) ? textTranscribers.map(({ id }) => id) : [], | ||||
|     proofreaders: isPayloadArrayType(textProofreaders) ? textProofreaders.map(({ id }) => id) : [], | ||||
|     ...(textNotes ? { notes: textNotes } : {}), | ||||
|   }; | ||||
| }; | ||||
| @ -1,123 +0,0 @@ | ||||
| import type { MarkOptional } from "ts-essentials"; | ||||
| import { Collections } from "../../../constants"; | ||||
| import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint"; | ||||
| import { Content } from "../../../types/collections"; | ||||
| import { StrapiImage, StrapiLanguage, StrapiRecorders } from "../../../types/strapi"; | ||||
| import { isNotEmpty, isUndefined } from "../../../utils/asserts"; | ||||
| import { | ||||
|   findCategory, | ||||
|   findContentType, | ||||
|   findRecorder, | ||||
|   uploadStrapiImage, | ||||
| } from "../../../utils/localApi"; | ||||
| import { plainTextToLexical } from "../../../utils/string"; | ||||
| 
 | ||||
| type StrapiContent = { | ||||
|   slug: string; | ||||
|   categories: { data?: { attributes: { slug: string } }[] }; | ||||
|   type: { data?: { attributes: { slug: string } } }; | ||||
|   thumbnail: StrapiImage; | ||||
|   translations: { | ||||
|     title: string; | ||||
|     subtitle?: string; | ||||
|     pre_title?: string; | ||||
|     description?: string; | ||||
|     language: StrapiLanguage; | ||||
|     text_set?: { | ||||
|       text: string; | ||||
|       notes?: string; | ||||
|       source_language: StrapiLanguage; | ||||
|       transcribers: StrapiRecorders; | ||||
|       translators: StrapiRecorders; | ||||
|       proofreaders: StrapiRecorders; | ||||
|     }; | ||||
|   }[]; | ||||
| }; | ||||
| 
 | ||||
| export const importFromStrapi = createStrapiImportEndpoint<StrapiContent>({ | ||||
|   strapi: { | ||||
|     collection: "contents", | ||||
|     params: { | ||||
|       populate: [ | ||||
|         "type", | ||||
|         "categories", | ||||
|         "thumbnail", | ||||
|         "translations", | ||||
|         "translations.language", | ||||
|         "translations.text_set", | ||||
|         "translations.text_set.source_language", | ||||
|         "translations.text_set.transcribers", | ||||
|         "translations.text_set.translators", | ||||
|         "translations.text_set.proofreaders", | ||||
|       ], | ||||
|     }, | ||||
|   }, | ||||
|   payload: { | ||||
|     collection: Collections.Contents, | ||||
|     convert: async ({ slug, categories, type, thumbnail, translations }) => { | ||||
|       const thumbnailId = await uploadStrapiImage({ | ||||
|         collection: Collections.ContentsThumbnails, | ||||
|         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: | ||||
|           categories.data && | ||||
|           (await Promise.all( | ||||
|             categories.data.map(async (category) => await findCategory(category.attributes.slug)) | ||||
|           )), | ||||
|         type: type.data && (await findContentType(type.data?.attributes.slug)), | ||||
|         thumbnail: thumbnailId, | ||||
|         translations: await Promise.all(translations.map(handleTranslation)), | ||||
|       }; | ||||
|       return data; | ||||
|     }, | ||||
|   }, | ||||
| }); | ||||
| @ -1,38 +0,0 @@ | ||||
| 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,50 +0,0 @@ | ||||
| import { Collections } from "../../constants"; | ||||
| import { backPropagationField } from "../../fields/backPropagationField/backPropagationField"; | ||||
| import { buildImageCollectionConfig } from "../../utils/imageCollectionConfig"; | ||||
| 
 | ||||
| const fields = { | ||||
|   filename: "filename", | ||||
|   mimeType: "mimeType", | ||||
|   filesize: "filesize", | ||||
|   contents: "contents", | ||||
|   updatedAt: "updatedAt", | ||||
| } as const satisfies Record<string, string>; | ||||
| 
 | ||||
| export const ContentsThumbnails = buildImageCollectionConfig({ | ||||
|   slug: Collections.ContentsThumbnails, | ||||
|   labels: { | ||||
|     singular: "Contents Thumbnail", | ||||
|     plural: "Contents Thumbnails", | ||||
|   }, | ||||
|   admin: { defaultColumns: [fields.filename, fields.contents, fields.updatedAt] }, | ||||
|   upload: { | ||||
|     imageSizes: [ | ||||
|       { | ||||
|         name: "og", | ||||
|         height: 750, | ||||
|         width: 1125, | ||||
|         formatOptions: { | ||||
|           format: "jpg", | ||||
|           options: { progressive: true, mozjpeg: true, compressionLevel: 9, quality: 80 }, | ||||
|         }, | ||||
|       }, | ||||
|       { | ||||
|         name: "medium", | ||||
|         height: 1000, | ||||
|         width: 1500, | ||||
|         formatOptions: { | ||||
|           format: "webp", | ||||
|           options: { effort: 6, quality: 80, alphaQuality: 80 }, | ||||
|         }, | ||||
|       }, | ||||
|     ], | ||||
|   }, | ||||
|   fields: [ | ||||
|     backPropagationField({ | ||||
|       name: fields.contents, | ||||
|       hasMany: true, | ||||
|       relationTo: Collections.Contents, | ||||
|       where: ({ id }) => ({ thumbnail: { equals: id } }), | ||||
|     }), | ||||
|   ], | ||||
| }); | ||||
| @ -73,7 +73,7 @@ export const Folders = buildCollectionConfig({ | ||||
|           ], | ||||
|         }), | ||||
|         { | ||||
|           name: fields.sectionsSubfolders, | ||||
|           name: fields.sectionsSubfolders,   | ||||
|           type: "relationship", | ||||
|           relationTo: Collections.Folders, | ||||
|           hasMany: true, | ||||
| @ -83,7 +83,7 @@ export const Folders = buildCollectionConfig({ | ||||
|     { | ||||
|       type: "relationship", | ||||
|       name: fields.files, | ||||
|       relationTo: [Collections.LibraryItems, Collections.Contents, Collections.Pages], | ||||
|       relationTo: [Collections.LibraryItems, Collections.Pages], | ||||
|       hasMany: true, | ||||
|     }, | ||||
|   ], | ||||
|  | ||||
| @ -7,6 +7,7 @@ import { | ||||
|   LibraryItemsTextualBindingTypes, | ||||
|   LibraryItemsTextualPageOrders, | ||||
|   LibraryItemsTypes, | ||||
|   PageType, | ||||
| } from "../../constants"; | ||||
| import { backPropagationField } from "../../fields/backPropagationField/backPropagationField"; | ||||
| import { componentField } from "../../fields/componentField/componentField"; | ||||
| @ -734,8 +735,12 @@ export const LibraryItems = buildVersionedCollectionConfig({ | ||||
|                 { | ||||
|                   name: fields.contentsContent, | ||||
|                   type: "relationship", | ||||
|                   relationTo: Collections.Contents, | ||||
|                   relationTo: Collections.Pages, | ||||
|                   admin: { | ||||
|                     allowCreate: false, | ||||
|                   }, | ||||
|                   required: true, | ||||
|                   filterOptions: { type: { equals: PageType.Content } }, | ||||
|                 }, | ||||
|                 { | ||||
|                   type: "row", | ||||
|  | ||||
| @ -56,7 +56,7 @@ export const Pages = buildVersionedCollectionConfig({ | ||||
|       BeforeListTable: [ | ||||
|         () => | ||||
|           QuickFilters({ | ||||
|             slug: Collections.Posts, | ||||
|             slug: Collections.Pages, | ||||
|             filterGroups: [publishStatusFilters], | ||||
|           }), | ||||
|       ], | ||||
|  | ||||
| @ -1,173 +0,0 @@ | ||||
| import { sectionBlock } from "../../blocks/sectionBlock"; | ||||
| import { transcriptBlock } from "../../blocks/transcriptBlock"; | ||||
| 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"; | ||||
| import { beforeDuplicatePiping } from "../../hooks/beforeDuplicatePiping"; | ||||
| import { beforeDuplicateUnpublish } from "../../hooks/beforeDuplicateUnpublish"; | ||||
| import { isDefined, isUndefined } from "../../utils/asserts"; | ||||
| import { createEditor } from "../../utils/editor"; | ||||
| import { buildVersionedCollectionConfig } from "../../utils/versionedCollectionConfig"; | ||||
| import { importFromStrapi } from "./endpoints/importFromStrapi"; | ||||
| 
 | ||||
| const fields = { | ||||
|   slug: "slug", | ||||
|   hidden: "hidden", | ||||
|   thumbnail: "thumbnail", | ||||
|   categories: "categories", | ||||
|   authors: "authors", | ||||
|   publishedDate: "publishedDate", | ||||
|   translations: "translations", | ||||
|   sourceLanguage: "sourceLanguage", | ||||
|   title: "title", | ||||
|   summary: "summary", | ||||
|   content: "content", | ||||
|   translators: "translators", | ||||
|   proofreaders: "proofreaders", | ||||
| } as const satisfies Record<string, string>; | ||||
| 
 | ||||
| export const Posts = buildVersionedCollectionConfig({ | ||||
|   slug: Collections.Posts, | ||||
|   labels: { | ||||
|     singular: "Post", | ||||
|     plural: "Posts", | ||||
|   }, | ||||
|   defaultSort: fields.slug, | ||||
|   admin: { | ||||
|     useAsTitle: fields.slug, | ||||
|     description: | ||||
|       "News articles written by our Recorders! Here you will find announcements about \ | ||||
|          new merch/items releases, guides, theories, unboxings, showcases...", | ||||
|     defaultColumns: [fields.thumbnail, fields.slug, fields.categories], | ||||
|     group: CollectionGroups.Collections, | ||||
|     components: { | ||||
|       BeforeListTable: [ | ||||
|         () => | ||||
|           QuickFilters({ | ||||
|             slug: Collections.Posts, | ||||
|             filterGroups: [publishStatusFilters], | ||||
|           }), | ||||
|       ], | ||||
|     }, | ||||
|     hooks: { | ||||
|       beforeDuplicate: beforeDuplicatePiping([ | ||||
|         beforeDuplicateUnpublish, | ||||
|         beforeDuplicateAddCopyTo(fields.slug), | ||||
|       ]), | ||||
|     }, | ||||
|     preview: (doc) => `https://accords-library.com/news/${doc.slug}`, | ||||
|   }, | ||||
|   endpoints: [importFromStrapi], | ||||
|   fields: [ | ||||
|     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 }, | ||||
|       required: true, | ||||
|       minRows: 1, | ||||
|       fields: [ | ||||
|         { name: fields.title, type: "text", required: true }, | ||||
|         { | ||||
|           name: fields.summary, | ||||
|           type: "richText", | ||||
|           editor: createEditor({ inlines: true, lists: true, links: true }), | ||||
|         }, | ||||
|         { | ||||
|           name: fields.content, | ||||
|           type: "richText", | ||||
|           editor: createEditor({ | ||||
|             images: true, | ||||
|             inlines: true, | ||||
|             alignment: true, | ||||
|             blocks: [sectionBlock, transcriptBlock], | ||||
|             links: true, | ||||
|             lists: true, | ||||
|           }), | ||||
|         }, | ||||
|         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, | ||||
|           }, | ||||
|         ]), | ||||
|       ], | ||||
|     }), | ||||
|     { | ||||
|       name: fields.publishedDate, | ||||
|       type: "date", | ||||
|       defaultValue: new Date().toISOString(), | ||||
|       admin: { | ||||
|         date: { pickerAppearance: "dayOnly", displayFormat: "yyyy-MM-dd" }, | ||||
|       }, | ||||
|       required: true, | ||||
|     }, | ||||
|     { | ||||
|       name: fields.hidden, | ||||
|       type: "checkbox", | ||||
|       required: false, | ||||
|       defaultValue: false, | ||||
|       admin: { | ||||
|         description: "If enabled, the post won't appear in the 'News' section", | ||||
|       }, | ||||
|     }, | ||||
|   ], | ||||
| }); | ||||
| @ -1,115 +0,0 @@ | ||||
| import { DateTime } from "luxon"; | ||||
| import type { MarkOptional } from "ts-essentials"; | ||||
| import { Collections } from "../../../constants"; | ||||
| import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint"; | ||||
| import type { Post } from "../../../types/collections"; | ||||
| import { StrapiImage, StrapiLanguage, StrapiRecorders } from "../../../types/strapi"; | ||||
| import { isDefined, isUndefined } from "../../../utils/asserts"; | ||||
| import { findCategory, findRecorder, uploadStrapiImage } from "../../../utils/localApi"; | ||||
| import { plainTextToLexical } from "../../../utils/string"; | ||||
| 
 | ||||
| type StrapiPost = { | ||||
|   slug: string; | ||||
|   categories: { data: { attributes: { slug: string } }[] }; | ||||
|   authors: StrapiRecorders; | ||||
|   hidden: boolean; | ||||
|   thumbnail: StrapiImage; | ||||
|   translations: { | ||||
|     title: string; | ||||
|     excerpt?: string; | ||||
|     body: string; | ||||
|     translators: StrapiRecorders; | ||||
|     proofreaders: StrapiRecorders; | ||||
|     language: StrapiLanguage; | ||||
|     source_language: StrapiLanguage; | ||||
|   }[]; | ||||
|   date: { | ||||
|     day: number; | ||||
|     month: number; | ||||
|     year: number; | ||||
|   }; | ||||
| }; | ||||
| 
 | ||||
| export const importFromStrapi = createStrapiImportEndpoint<StrapiPost>({ | ||||
|   strapi: { | ||||
|     collection: "posts", | ||||
|     params: { | ||||
|       populate: [ | ||||
|         "date", | ||||
|         "authors", | ||||
|         "thumbnail", | ||||
|         "categories", | ||||
|         "translations", | ||||
|         "translations.language", | ||||
|         "translations.translators", | ||||
|         "translations.proofreaders", | ||||
|         "translations.source_language", | ||||
|       ], | ||||
|     }, | ||||
|   }, | ||||
|   payload: { | ||||
|     collection: Collections.Posts, | ||||
|     convert: async ({ | ||||
|       slug, | ||||
|       date: { day, month, year }, | ||||
|       hidden, | ||||
|       authors, | ||||
|       thumbnail, | ||||
|       categories, | ||||
|       translations, | ||||
|     }) => { | ||||
|       const thumbnailId = await uploadStrapiImage({ | ||||
|         collection: Collections.PostsThumbnails, | ||||
|         image: thumbnail, | ||||
|       }); | ||||
| 
 | ||||
|       const handleTranslation = async ({ | ||||
|         language, | ||||
|         title, | ||||
|         body, | ||||
|         excerpt, | ||||
|         proofreaders, | ||||
|         source_language, | ||||
|         translators, | ||||
|       }: StrapiPost["translations"][number]): Promise<Post["translations"][number]> => { | ||||
|         if (isUndefined(language.data)) | ||||
|           throw new Error("A language is required for a post translation"); | ||||
|         if (isUndefined(source_language.data)) | ||||
|           throw new Error("A source_language is required for a post translation"); | ||||
|         return { | ||||
|           language: language.data.attributes.code, | ||||
|           sourceLanguage: source_language.data.attributes.code, | ||||
|           title, | ||||
|           content: plainTextToLexical(body), | ||||
|           summary: isDefined(excerpt) ? plainTextToLexical(excerpt) : undefined, | ||||
|           translators: | ||||
|             translators.data && | ||||
|             (await Promise.all( | ||||
|               translators.data?.map(async (recorder) => findRecorder(recorder.attributes.username)) | ||||
|             )), | ||||
|           proofreaders: | ||||
|             proofreaders.data && | ||||
|             (await Promise.all( | ||||
|               proofreaders.data?.map(async (recorder) => findRecorder(recorder.attributes.username)) | ||||
|             )), | ||||
|         }; | ||||
|       }; | ||||
| 
 | ||||
|       const data: MarkOptional<Post, "createdAt" | "id" | "updatedAt" | "updatedBy"> = { | ||||
|         slug, | ||||
|         publishedDate: | ||||
|           DateTime.fromObject({ day, month, year }).toISO() ?? new Date().toISOString(), | ||||
|         categories: await Promise.all( | ||||
|           categories.data.map((category) => findCategory(category.attributes.slug)) | ||||
|         ), | ||||
|         translations: await Promise.all(translations.map(handleTranslation)), | ||||
|         authors: await Promise.all( | ||||
|           authors.data?.map((author) => findRecorder(author.attributes.username)) ?? [] | ||||
|         ), | ||||
|         thumbnail: thumbnailId, | ||||
|         hidden, | ||||
|       }; | ||||
|       return data; | ||||
|     }, | ||||
|   }, | ||||
| }); | ||||
| @ -1,50 +0,0 @@ | ||||
| import { Collections } from "../../constants"; | ||||
| import { backPropagationField } from "../../fields/backPropagationField/backPropagationField"; | ||||
| import { buildImageCollectionConfig } from "../../utils/imageCollectionConfig"; | ||||
| 
 | ||||
| const fields = { | ||||
|   filename: "filename", | ||||
|   mimeType: "mimeType", | ||||
|   filesize: "filesize", | ||||
|   posts: "posts", | ||||
|   updatedAt: "updatedAt", | ||||
| } as const satisfies Record<string, string>; | ||||
| 
 | ||||
| export const PostsThumbnails = buildImageCollectionConfig({ | ||||
|   slug: Collections.PostsThumbnails, | ||||
|   labels: { | ||||
|     singular: "Post Thumbnail", | ||||
|     plural: "Post Thumbnails", | ||||
|   }, | ||||
|   admin: { defaultColumns: [fields.filename, fields.posts, fields.updatedAt] }, | ||||
|   upload: { | ||||
|     imageSizes: [ | ||||
|       { | ||||
|         name: "og", | ||||
|         height: 750, | ||||
|         width: 1125, | ||||
|         formatOptions: { | ||||
|           format: "jpg", | ||||
|           options: { progressive: true, mozjpeg: true, compressionLevel: 9, quality: 80 }, | ||||
|         }, | ||||
|       }, | ||||
|       { | ||||
|         name: "medium", | ||||
|         height: 1000, | ||||
|         width: 1500, | ||||
|         formatOptions: { | ||||
|           format: "webp", | ||||
|           options: { effort: 6, quality: 80, alphaQuality: 80 }, | ||||
|         }, | ||||
|       }, | ||||
|     ], | ||||
|   }, | ||||
|   fields: [ | ||||
|     backPropagationField({ | ||||
|       name: fields.posts, | ||||
|       hasMany: true, | ||||
|       relationTo: Collections.Posts, | ||||
|       where: ({ id }) => ({ thumbnail: { equals: id } }), | ||||
|     }), | ||||
|   ], | ||||
| }); | ||||
| @ -5,8 +5,6 @@ import type { CueBlock, LineBlock, SectionBlock, TranscriptBlock } from "./types | ||||
| export enum Collections { | ||||
|   ChronologyEras = "chronology-eras", | ||||
|   ChronologyItems = "chronology-items", | ||||
|   Contents = "contents", | ||||
|   ContentsThumbnails = "contents-thumbnails", | ||||
|   Currencies = "currencies", | ||||
|   Files = "files", | ||||
|   Keys = "keys", | ||||
| @ -16,10 +14,8 @@ export enum Collections { | ||||
|   LibraryItemsScans = "library-items-scans", | ||||
|   LibraryItemsGallery = "library-items-gallery", | ||||
|   Notes = "notes", | ||||
|   Posts = "posts", | ||||
|   Pages = "pages", | ||||
|   PagesThumbnails = "pages-thumbnails", | ||||
|   PostsThumbnails = "posts-thumbnails", | ||||
|   Recorders = "recorders", | ||||
|   RecordersThumbnails = "recorders-thumbnails", | ||||
|   VideosChannels = "videos-channels", | ||||
|  | ||||
| @ -4,8 +4,6 @@ import path from "path"; | ||||
| import { buildConfig } from "payload/config"; | ||||
| import { ChronologyEras } from "./collections/ChronologyEras/ChronologyEras"; | ||||
| import { ChronologyItems } from "./collections/ChronologyItems/ChronologyItems"; | ||||
| import { Contents } from "./collections/Contents/Contents"; | ||||
| import { ContentsThumbnails } from "./collections/ContentsThumbnails/ContentsThumbnails"; | ||||
| import { Currencies } from "./collections/Currencies/Currencies"; | ||||
| import { Files } from "./collections/Files/Files"; | ||||
| import { Folders } from "./collections/Folders/Folders"; | ||||
| @ -19,8 +17,6 @@ import { LibraryItemsScans } from "./collections/LibraryItemsScans/LibraryItemsS | ||||
| import { LibraryItemsThumbnails } from "./collections/LibraryItemsThumbnails/LibraryItemsThumbnails"; | ||||
| import { Notes } from "./collections/Notes/Notes"; | ||||
| import { Pages } from "./collections/Pages/Pages"; | ||||
| 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 { Tags } from "./collections/Tags/Tags"; | ||||
| @ -53,20 +49,16 @@ export default buildConfig({ | ||||
|     Folders, | ||||
|     FoldersThumbnails, | ||||
|     LibraryItems, | ||||
|     Contents, | ||||
|     Posts, | ||||
|     Pages, | ||||
|     ChronologyItems, | ||||
|     ChronologyEras, | ||||
|     Weapons, | ||||
|     WeaponsGroups, | ||||
|     WeaponsThumbnails, | ||||
|     ContentsThumbnails, | ||||
|     LibraryItemsThumbnails, | ||||
|     LibraryItemsScans, | ||||
|     LibraryItemsGallery, | ||||
|     RecordersThumbnails, | ||||
|     PostsThumbnails, | ||||
|     Files, | ||||
|     Notes, | ||||
|     Videos, | ||||
|  | ||||
| @ -33,26 +33,6 @@ export const findRecorder = async (name: string): Promise<string> => { | ||||
|   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; | ||||
| }; | ||||
| 
 | ||||
| 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 = { | ||||
|   image: StrapiImage; | ||||
|   collection: Collections; | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 DrMint
						DrMint