From b06918b346569285dbe1f0a27fccd50a2bc1c4da Mon Sep 17 00:00:00 2001 From: DrMint <29893320+DrMint@users.noreply.github.com> Date: Mon, 4 Mar 2024 10:51:47 +0100 Subject: [PATCH] Removed library and keys collections --- src/blocks/spacerBlock.ts | 6 +- src/collections/Collectibles/Collectibles.ts | 84 +- .../endpoints/getBySlugEndpoint.ts | 124 +++ src/collections/Files/Files.ts | 46 - .../hooks/beforeValidateCheckFileExists.ts | 45 - .../Folders/endpoints/getBySlugEndpoint.ts | 2 +- .../GenericContents/GenericContents.ts | 12 +- src/collections/Keys/Keys.ts | 118 --- .../Keys/endpoints/getAllEndpoint.ts | 42 - .../Keys/endpoints/importFromStrapi.ts | 365 -------- src/collections/LibraryItems/LibraryItems.ts | 790 ------------------ .../LibraryItems/components/RowLabel.tsx | 26 - .../LibraryItemsGallery.ts | 33 - .../LibraryItemsScans/LibraryItemsScans.ts | 53 -- .../LibraryItemsThumbnails.ts | 53 -- src/collections/Pages/Pages.ts | 21 +- .../Pages/endpoints/getBySlugEndpoint.ts | 11 +- src/collections/Recorders/Recorders.ts | 10 +- src/collections/Tags/Tags.ts | 11 +- src/collections/Videos/Videos.ts | 5 +- src/collections/Weapons/Weapons.ts | 14 +- src/collections/Wordings/Wordings.ts | 11 +- src/constants.ts | 55 +- .../backPropagationField.ts | 11 +- src/fields/fileField/fileField.ts | 23 - src/fields/keysField/keysField.ts | 28 - src/fields/slugField/slugField.ts | 2 +- src/payload.config.ts | 12 - src/sdk.ts | 99 ++- src/types/collections.ts | 510 +++++------ src/utils/endpoints.ts | 28 + src/utils/localApi.ts | 22 +- src/utils/tags.ts | 29 - 33 files changed, 537 insertions(+), 2164 deletions(-) create mode 100644 src/collections/Collectibles/endpoints/getBySlugEndpoint.ts delete mode 100644 src/collections/Files/Files.ts delete mode 100644 src/collections/Files/hooks/beforeValidateCheckFileExists.ts delete mode 100644 src/collections/Keys/Keys.ts delete mode 100644 src/collections/Keys/endpoints/getAllEndpoint.ts delete mode 100644 src/collections/Keys/endpoints/importFromStrapi.ts delete mode 100644 src/collections/LibraryItems/LibraryItems.ts delete mode 100644 src/collections/LibraryItems/components/RowLabel.tsx delete mode 100644 src/collections/LibraryItemsGallery/LibraryItemsGallery.ts delete mode 100644 src/collections/LibraryItemsScans/LibraryItemsScans.ts delete mode 100644 src/collections/LibraryItemsThumbnails/LibraryItemsThumbnails.ts delete mode 100644 src/fields/fileField/fileField.ts delete mode 100644 src/fields/keysField/keysField.ts create mode 100644 src/utils/endpoints.ts delete mode 100644 src/utils/tags.ts diff --git a/src/blocks/spacerBlock.ts b/src/blocks/spacerBlock.ts index 7df26b0..601e98a 100644 --- a/src/blocks/spacerBlock.ts +++ b/src/blocks/spacerBlock.ts @@ -11,9 +11,9 @@ export const spacerBlock: Block = { type: "radio", defaultValue: "medium", required: true, - options: Object.entries(SpacerSizes).map(([value, label]) => ({ - label, - value, + options: Object.entries(SpacerSizes).map(([_, value]) => ({ + label: value, + value: value, })), }, ], diff --git a/src/collections/Collectibles/Collectibles.ts b/src/collections/Collectibles/Collectibles.ts index 5074fdb..f52737b 100644 --- a/src/collections/Collectibles/Collectibles.ts +++ b/src/collections/Collectibles/Collectibles.ts @@ -21,6 +21,7 @@ import { beforeDuplicateUnpublish } from "../../hooks/beforeDuplicateUnpublish"; import { createEditor } from "../../utils/editor"; import { buildVersionedCollectionConfig } from "../../utils/versionedCollectionConfig"; import { RowLabel } from "./components/RowLabel"; +import { getBySlugEndpoint } from "./endpoints/getBySlugEndpoint"; const fields = { status: "_status", @@ -131,6 +132,7 @@ export const Collectibles = buildVersionedCollectionConfig({ ]), }, }, + endpoints: [getBySlugEndpoint], fields: [ { type: "tabs", @@ -152,9 +154,9 @@ export const Collectibles = buildVersionedCollectionConfig({ type: "radio", required: true, defaultValue: CollectibleNature.Physical, - options: Object.entries(CollectibleNature).map(([value, label]) => ({ - label, - value, + options: Object.entries(CollectibleNature).map(([_, value]) => ({ + label: value, + value: value, })), }, { @@ -207,7 +209,8 @@ export const Collectibles = buildVersionedCollectionConfig({ fields: [ imageField({ name: fields.galleryImage, - relationTo: Collections.LibraryItemsGallery, + required: true, + relationTo: Collections.Images, }), ], }, @@ -242,43 +245,43 @@ export const Collectibles = buildVersionedCollectionConfig({ rowField([ imageField({ name: fields.scansCoverFront, - relationTo: Collections.LibraryItemsScans, + relationTo: Collections.Images, }), imageField({ name: fields.scansCoverSpine, - relationTo: Collections.LibraryItemsScans, + relationTo: Collections.Images, }), imageField({ name: fields.scansCoverBack, - relationTo: Collections.LibraryItemsScans, + relationTo: Collections.Images, }), ]), rowField([ imageField({ name: fields.scansCoverInsideFront, - relationTo: Collections.LibraryItemsScans, + relationTo: Collections.Images, }), imageField({ name: fields.scansCoverInsideBack, - relationTo: Collections.LibraryItemsScans, + relationTo: Collections.Images, }), ]), rowField([ imageField({ name: fields.scansCoverFlapFront, - relationTo: Collections.LibraryItemsScans, + relationTo: Collections.Images, }), imageField({ name: fields.scansCoverFlapBack, - relationTo: Collections.LibraryItemsScans, + relationTo: Collections.Images, }), imageField({ name: fields.scansCoverInsideFlapFront, - relationTo: Collections.LibraryItemsScans, + relationTo: Collections.Images, }), imageField({ name: fields.scansCoverInsideFlapBack, - relationTo: Collections.LibraryItemsScans, + relationTo: Collections.Images, }), ]), ], @@ -295,47 +298,47 @@ export const Collectibles = buildVersionedCollectionConfig({ rowField([ imageField({ name: fields.scansDustjacketFront, - relationTo: Collections.LibraryItemsScans, + relationTo: Collections.Images, }), imageField({ name: fields.scansDustjacketSpine, - relationTo: Collections.LibraryItemsScans, + relationTo: Collections.Images, }), imageField({ name: fields.scansDustjacketBack, - relationTo: Collections.LibraryItemsScans, + relationTo: Collections.Images, }), ]), rowField([ imageField({ name: fields.scansDustjacketInsideFront, - relationTo: Collections.LibraryItemsScans, + relationTo: Collections.Images, }), imageField({ name: fields.scansDustjacketInsideSpine, - relationTo: Collections.LibraryItemsScans, + relationTo: Collections.Images, }), imageField({ name: fields.scansDustjacketInsideBack, - relationTo: Collections.LibraryItemsScans, + relationTo: Collections.Images, }), ]), rowField([ imageField({ name: fields.scansDustjacketFlapFront, - relationTo: Collections.LibraryItemsScans, + relationTo: Collections.Images, }), imageField({ name: fields.scansDustjacketFlapBack, - relationTo: Collections.LibraryItemsScans, + relationTo: Collections.Images, }), imageField({ name: fields.scansDustjacketInsideFlapFront, - relationTo: Collections.LibraryItemsScans, + relationTo: Collections.Images, }), imageField({ name: fields.scansDustjacketInsideFlapBack, - relationTo: Collections.LibraryItemsScans, + relationTo: Collections.Images, }), ]), ], @@ -352,47 +355,47 @@ export const Collectibles = buildVersionedCollectionConfig({ rowField([ imageField({ name: fields.scansObiFront, - relationTo: Collections.LibraryItemsScans, + relationTo: Collections.Images, }), imageField({ name: fields.scansObiSpine, - relationTo: Collections.LibraryItemsScans, + relationTo: Collections.Images, }), imageField({ name: fields.scansObiBack, - relationTo: Collections.LibraryItemsScans, + relationTo: Collections.Images, }), ]), rowField([ imageField({ name: fields.scansObiInsideFront, - relationTo: Collections.LibraryItemsScans, + relationTo: Collections.Images, }), imageField({ name: fields.scansObiInsideSpine, - relationTo: Collections.LibraryItemsScans, + relationTo: Collections.Images, }), imageField({ name: fields.scansObiInsideBack, - relationTo: Collections.LibraryItemsScans, + relationTo: Collections.Images, }), ]), rowField([ imageField({ name: fields.scansObiFlapFront, - relationTo: Collections.LibraryItemsScans, + relationTo: Collections.Images, }), imageField({ name: fields.scansObiFlapBack, - relationTo: Collections.LibraryItemsScans, + relationTo: Collections.Images, }), imageField({ name: fields.scansObiInsideFlapFront, - relationTo: Collections.LibraryItemsScans, + relationTo: Collections.Images, }), imageField({ name: fields.scansObiInsideFlapBack, - relationTo: Collections.LibraryItemsScans, + relationTo: Collections.Images, }), ]), ], @@ -418,7 +421,7 @@ export const Collectibles = buildVersionedCollectionConfig({ }, imageField({ name: fields.scansPagesImage, - relationTo: Collections.LibraryItemsScans, + relationTo: Collections.Images, required: true, }), ]), @@ -534,9 +537,9 @@ export const Collectibles = buildVersionedCollectionConfig({ { name: fields.pageInfoBindingType, type: "radio", - options: Object.entries(CollectibleBindingTypes).map(([value, label]) => ({ - label, - value, + options: Object.entries(CollectibleBindingTypes).map(([_, value]) => ({ + label: value, + value: value, })), admin: { condition: ({ nature }) => nature === CollectibleNature.Physical, @@ -545,9 +548,9 @@ export const Collectibles = buildVersionedCollectionConfig({ { name: fields.pageInfoPageOrder, type: "radio", - options: Object.entries(CollectiblePageOrders).map(([value, label]) => ({ - label, - value, + options: Object.entries(CollectiblePageOrders).map(([_, value]) => ({ + label: value, + value: value, })), }, ]), @@ -683,6 +686,7 @@ export const Collectibles = buildVersionedCollectionConfig({ { name: "note", type: "richText", + required: true, editor: createEditor({ inlines: true, lists: true, links: true }), }, ], diff --git a/src/collections/Collectibles/endpoints/getBySlugEndpoint.ts b/src/collections/Collectibles/endpoints/getBySlugEndpoint.ts new file mode 100644 index 0000000..3d87cc8 --- /dev/null +++ b/src/collections/Collectibles/endpoints/getBySlugEndpoint.ts @@ -0,0 +1,124 @@ +import { CollectibleNature, CollectionStatus, Collections } from "../../../constants"; +import { createGetByEndpoint } from "../../../endpoints/createGetByEndpoint"; +import { EndpointCollectible, EndpointCollectiblePreview } from "../../../sdk"; +import { Collectible } from "../../../types/collections"; +import { isPayloadArrayType, isPayloadType, isValidPayloadImage } from "../../../utils/asserts"; +import { convertTagsToGroups } from "../../../utils/endpoints"; + +export const getBySlugEndpoint = createGetByEndpoint( + Collections.Collectibles, + "slug", + (collectible: Collectible): EndpointCollectible => { + const { nature, urls, subitems, gallery, contents } = collectible; + return { + ...convertCollectibleToPreview(collectible), + contents: handleContents(contents), + gallery: + gallery + ?.map(({ image }) => image) + .flatMap((image) => (isValidPayloadImage(image) ? [image] : [])) ?? [], + nature: nature === "Physical" ? CollectibleNature.Physical : CollectibleNature.Digital, + parentPages: [], // TODO: todo + subitems: isPayloadArrayType(subitems) ? subitems.map(convertCollectibleToPreview) : [], + urls: urls?.map(({ url }) => ({ url, label: getLabelFromUrl(url) })) ?? [], + }; + } +); + +export const handleContents = ( + contents: Collectible["contents"] +): EndpointCollectible["contents"] => { + if (!contents) return []; + + return contents.flatMap(({ content, range: rangeArray }) => { + const handleRange = (): EndpointCollectible["contents"][number]["range"] => { + const range = rangeArray?.[0]; + switch (range?.blockType) { + case "other": { + const { translations, blockType } = range; + return { + type: blockType, + translations: + translations?.map(({ language, note }) => ({ + language: isPayloadType(language) ? language.id : language, + note, + })) ?? [], + }; + } + + case "pageRange": { + const { blockType, end, start } = range; + return { type: blockType, end, start }; + } + + case "timeRange": { + const { blockType, end, start } = range; + return { type: blockType, end, start }; + } + + default: + return undefined; + } + }; + + const handleContent = (): EndpointCollectible["contents"][number]["content"] | undefined => { + switch (content.relationTo) { + case "generic-contents": + return isPayloadType(content.value) + ? { relationTo: "generic-contents", value: content.value } + : undefined; + + case "pages": + return isPayloadType(content.value) + ? { relationTo: "pages", value: content.value } + : undefined; + + default: + return undefined; + } + }; + + const newContent = handleContent(); + const range = handleRange(); + + if (!newContent) return []; + return [{ content: newContent, range }]; + }); +}; + +export const convertCollectibleToPreview = ({ + slug, + _status, + thumbnail, + translations, + releaseDate, + languages, + tags, +}: Collectible): EndpointCollectiblePreview => { + return { + slug, + languages: + languages?.map((language) => (isPayloadType(language) ? language.id : language)) ?? [], + status: _status === "draft" ? CollectionStatus.Draft : CollectionStatus.Published, + ...(releaseDate ? { releaseDate } : {}), + ...(isValidPayloadImage(thumbnail) ? { thumbnail } : {}), + tagGroups: convertTagsToGroups(tags), + translations: + translations?.map(({ language, title, description, pretitle, subtitle }) => ({ + language: isPayloadType(language) ? language.id : language, + title, + ...(pretitle ? { pretitle } : {}), + ...(subtitle ? { subtitle } : {}), + ...(description ? { description } : {}), + })) ?? [], + }; +}; + +const getLabelFromUrl = (url: string): string => { + const urlObject = new URL(url); + let domain = urlObject.hostname; + if (domain.startsWith("www.")) { + domain = domain.substring("www.".length); + } + return domain; +}; diff --git a/src/collections/Files/Files.ts b/src/collections/Files/Files.ts deleted file mode 100644 index 8404b00..0000000 --- a/src/collections/Files/Files.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { CollectionGroups, Collections, FileTypes } from "../../constants"; -import { File } from "../../types/collections"; -import { buildCollectionConfig } from "../../utils/collectionConfig"; -import { - beforeValidateCheckFileExists, - generatePathForFile, -} from "./hooks/beforeValidateCheckFileExists"; - -const fields = { - filename: "filename", - type: "type", -} as const satisfies Record; - -export const Files = buildCollectionConfig({ - slug: Collections.Files, - labels: { - singular: "File", - plural: "Files", - }, - defaultSort: fields.filename, - admin: { - useAsTitle: fields.filename, - disableDuplicate: true, - group: CollectionGroups.Media, - preview: (doc) => { - const { filename, type } = doc as unknown as File; - return generatePathForFile(type, filename); - }, - }, - hooks: { - beforeValidate: [beforeValidateCheckFileExists], - }, - fields: [ - { - name: fields.filename, - required: true, - type: "text", - }, - { - name: fields.type, - type: "select", - required: true, - options: Object.entries(FileTypes).map(([value, label]) => ({ label, value })), - }, - ], -}); diff --git a/src/collections/Files/hooks/beforeValidateCheckFileExists.ts b/src/collections/Files/hooks/beforeValidateCheckFileExists.ts deleted file mode 100644 index ff1ba47..0000000 --- a/src/collections/Files/hooks/beforeValidateCheckFileExists.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { CollectionBeforeValidateHook } from "payload/types"; -import { FileTypes } from "../../../constants"; -import { File } from "../../../types/collections"; -import { isUndefined } from "../../../utils/asserts"; - -const reshareSubFolderFromType: Record = { - ContentAudio: "/contents/audios", - ContentVideo: "/contents/videos", - LibraryScans: "/library/scans", - LibrarySoundtracks: "/library/tracks", -}; - -const expectedMimeFromType = { - ContentAudio: "audio/", - ContentVideo: "video/", - LibraryScans: "application/zip", - LibrarySoundtracks: "audio/", -}; - -export const generatePathForFile = (type: keyof typeof FileTypes, filename: string) => - `https://resha.re/accords${reshareSubFolderFromType[type]}/${filename}`; - -export const beforeValidateCheckFileExists: CollectionBeforeValidateHook = async ({ - data, -}) => { - if (isUndefined(data)) throw new Error("The data is undefined"); - const { type, filename } = data; - if (isUndefined(filename)) throw new Error("Filename is undefined"); - if (isUndefined(type)) throw new Error("Filename is undefined"); - - const url = generatePathForFile(type, filename); - - const result = await fetch(url, { method: "head" }); - - if (result.status !== 200) { - throw new Error(`Unable to locate the file at the following address: ${url}`); - } - - const contentType = result.headers.get("content-type"); - if (isUndefined(contentType) || !contentType.startsWith(expectedMimeFromType[type])) { - throw new Error( - `Wrong MIME type found: ${contentType}. The expected MIME type was ${expectedMimeFromType[type]}` - ); - } -}; diff --git a/src/collections/Folders/endpoints/getBySlugEndpoint.ts b/src/collections/Folders/endpoints/getBySlugEndpoint.ts index 7af4eb8..d2746af 100644 --- a/src/collections/Folders/endpoints/getBySlugEndpoint.ts +++ b/src/collections/Folders/endpoints/getBySlugEndpoint.ts @@ -35,7 +35,7 @@ export const getBySlugEndpoint = createGetByEndpoint( return []; } switch (relationTo) { - case "library-items": + case "collectibles": return [{ relationTo, value }]; case "pages": return [{ relationTo, value }]; diff --git a/src/collections/GenericContents/GenericContents.ts b/src/collections/GenericContents/GenericContents.ts index af0442c..de25d5c 100644 --- a/src/collections/GenericContents/GenericContents.ts +++ b/src/collections/GenericContents/GenericContents.ts @@ -1,6 +1,6 @@ import { CollectionConfig } from "payload/types"; -import { QuickFilters } from "../../components/QuickFilters"; -import { CollectionGroups, Collections, LanguageCodes } from "../../constants"; +import { QuickFilters, languageBasedFilters } from "../../components/QuickFilters"; +import { CollectionGroups, Collections } from "../../constants"; import { rowField } from "../../fields/rowField/rowField"; import { translatedFields } from "../../fields/translatedFields/translatedFields"; import { beforeDuplicateAddCopyTo } from "../../hooks/beforeDuplicateAddCopyTo"; @@ -31,12 +31,7 @@ export const GenericContents: CollectionConfig = buildCollectionConfig({ () => QuickFilters({ slug: Collections.GenericContents, - filterGroups: [ - Object.entries(LanguageCodes).map(([key, value]) => ({ - label: `∅ ${value}`, - filter: { where: { "translations.language": { not_equals: key } } }, - })), - ], + filterGroups: [languageBasedFilters("translations.language")], }), ], }, @@ -52,7 +47,6 @@ export const GenericContents: CollectionConfig = buildCollectionConfig({ name: fields.translations, minRows: 1, required: true, - interfaceName: "CategoryTranslations", admin: { useAsTitle: fields.translationsName, }, diff --git a/src/collections/Keys/Keys.ts b/src/collections/Keys/Keys.ts deleted file mode 100644 index 1b9ae63..0000000 --- a/src/collections/Keys/Keys.ts +++ /dev/null @@ -1,118 +0,0 @@ -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"; -import { isDefined, isUndefined } from "../../utils/asserts"; -import { buildCollectionConfig } from "../../utils/collectionConfig"; -import { getAllEndpoint } from "./endpoints/getAllEndpoint"; -import { importFromStrapi } from "./endpoints/importFromStrapi"; - -const fields = { - name: "name", - type: "type", - translations: "translations", - translationsName: "name", - translationsShort: "short", -} as const satisfies Record; - -const keysTypesWithShort: (keyof typeof KeysTypes)[] = ["Categories", "GamePlatforms"]; - -// TODO: Maybe make sure there is at least one English translation as a fallback - -export const Keys = buildCollectionConfig({ - slug: Collections.Keys, - labels: { - singular: "Key", - plural: "Keys", - }, - defaultSort: fields.name, - admin: { - useAsTitle: fields.name, - defaultColumns: [fields.name, fields.type, fields.translations], - group: CollectionGroups.Meta, - components: { - BeforeListTable: [ - () => - QuickFilters({ - slug: Collections.Keys, - filterGroups: [ - Object.entries(KeysTypes).map(([key, value]) => ({ - label: value, - filter: { where: { type: { equals: key } } }, - })), - Object.entries(LanguageCodes).map(([key, value]) => ({ - label: `∅ ${value}`, - filter: { where: { "translations.language": { not_equals: key } } }, - })), - ], - }), - ], - }, - hooks: { - beforeDuplicate: beforeDuplicateAddCopyTo(fields.name), - }, - }, - access: { - create: mustBeAdmin, - delete: mustBeAdmin, - }, - hooks: { - beforeValidate: [ - async ({ data }) => { - if (isUndefined(data)) return; - const { name, type } = data; - const result = await payload.find({ - collection: Collections.Keys, - where: { name: { equals: name }, type: { equals: type } }, - }); - if (result.docs.length > 0) { - throw new Error(`A Key of type "${type}" already exists with the name "${name}"`); - } - }, - ], - }, - endpoints: [importFromStrapi, getAllEndpoint], - timestamps: false, - versions: false, - fields: [ - { - name: fields.name, - type: "text", - required: true, - }, - { - name: fields.type, - type: "select", - required: true, - options: Object.entries(KeysTypes).map(([value, label]) => ({ label, value })), - }, - translatedFields({ - name: fields.translations, - interfaceName: "CategoryTranslations", - admin: { - useAsTitle: fields.translationsName, - }, - fields: [ - rowField([ - { - name: fields.translationsName, - type: "text", - required: true, - }, - { - name: fields.translationsShort, - type: "text", - admin: { - condition: (data: Partial) => - isDefined(data.type) && keysTypesWithShort.includes(data.type), - }, - }, - ]), - ], - }), - ], -}); diff --git a/src/collections/Keys/endpoints/getAllEndpoint.ts b/src/collections/Keys/endpoints/getAllEndpoint.ts deleted file mode 100644 index 39bd7e4..0000000 --- a/src/collections/Keys/endpoints/getAllEndpoint.ts +++ /dev/null @@ -1,42 +0,0 @@ -import payload from "payload"; -import { Collections } from "../../../constants"; -import { EndpointKey } from "../../../sdk"; -import { Key } from "../../../types/collections"; -import { CollectionEndpoint } from "../../../types/payload"; -import { isPayloadType } from "../../../utils/asserts"; - -export const getAllEndpoint: CollectionEndpoint = { - method: "get", - path: "/all", - handler: async (req, res) => { - if (!req.user) { - return res.status(403).send({ - errors: [ - { - message: "You are not allowed to perform this action.", - }, - ], - }); - } - - const keys: Key[] = ( - await payload.find({ - collection: Collections.Keys, - sort: "id", - pagination: false, - }) - ).docs; - - const result: EndpointKey[] = keys.map(({ translations, ...others }) => ({ - ...others, - translations: - translations?.map(({ language, name, short }) => ({ - language: isPayloadType(language) ? language.id : language, - name, - short: short ?? name, - })) ?? [], - })); - - res.status(200).json(result); - }, -}; diff --git a/src/collections/Keys/endpoints/importFromStrapi.ts b/src/collections/Keys/endpoints/importFromStrapi.ts deleted file mode 100644 index b636c17..0000000 --- a/src/collections/Keys/endpoints/importFromStrapi.ts +++ /dev/null @@ -1,365 +0,0 @@ -import payload from "payload"; -import { Collections } from "../../../constants"; -import { - getAllStrapiEntries, - importStrapiEntries, -} from "../../../endpoints/createStrapiImportEndpoint"; -import { Key } from "../../../types/collections"; -import { CollectionEndpoint } from "../../../types/payload"; -import { StrapiLanguage } from "../../../types/strapi"; -import { isDefined, isUndefined } from "../../../utils/asserts"; -import { formatToCamelCase } from "../../../utils/string"; - -const importStrapiWordings: typeof importStrapiEntries = async ({ strapi: strapiParams, user }) => { - const rawEntries = await getAllStrapiEntries(strapiParams.collection, strapiParams.params); - - const { ui_language, createdAt, updatedAt, ...otherKeys } = rawEntries[0].attributes; - - const errors: string[] = []; - - await Promise.all( - Object.keys(otherKeys).map(async (key) => { - try { - await payload.create({ - collection: Collections.Keys, - data: { - name: formatToCamelCase(key), - type: "Wordings", - translations: rawEntries - .map((entry) => ({ - language: entry.attributes.ui_language.data.attributes.code, - name: entry.attributes[key], - })) - .filter(({ name }) => isDefined(name) && name !== ""), - }, - user, - }); - } catch (e) { - console.warn(e); - if (typeof e === "object" && isDefined(e) && "name" in e) { - errors.push(`${e.name} with ${key}`); - } - } - }) - ); - - return { count: Object.keys(otherKeys).length, errors }; -}; - -export const importFromStrapi: CollectionEndpoint = { - method: "post", - path: "/strapi", - handler: async (req, res) => { - if (!req.user) { - return res.status(403).send({ - errors: [ - { - message: "You are not allowed to perform this action.", - }, - ], - }); - } - - type StrapiCategories = { - slug: string; - titles: { title?: string; short?: string; language: StrapiLanguage }[]; - }; - - const { count: categoriesCount, errors: categoriesErrors } = - await importStrapiEntries({ - strapi: { - collection: "categories", - params: { populate: { titles: { populate: "language" } } }, - }, - payload: { - collection: Collections.Keys, - convert: ({ slug, titles }) => ({ - name: slug, - type: "Categories", - translations: titles.map(({ title, short, language }) => { - if (isUndefined(language.data)) - throw new Error("A language is required for a Keys title translation"); - if (isUndefined(title)) - throw new Error("A title is required for a Keys title translation"); - return { - name: title, - short, - language: language.data.attributes.code, - }; - }), - }), - }, - user: req.user, - }); - - type StrapiContentType = { - slug: string; - titles: { title: string; language: StrapiLanguage }[]; - }; - - const { count: contentTypesCount, errors: contentTypesErrors } = - await importStrapiEntries({ - strapi: { - collection: "content-types", - params: { populate: { titles: { populate: "language" } } }, - }, - payload: { - collection: Collections.Keys, - convert: ({ slug, titles }) => ({ - name: slug, - type: "Contents", - translations: titles.map(({ title, language }) => { - if (isUndefined(language.data)) - throw new Error("A language is required for a Keys title translation"); - return { - name: title, - language: language.data.attributes.code, - }; - }), - }), - }, - user: req.user, - }); - - type StrapiGamePlatform = { - slug: string; - titles: { title?: string; short?: string; language: StrapiLanguage }[]; - }; - - const { count: gamePlatformsCount, errors: gamePlatformsErrors } = - await importStrapiEntries({ - strapi: { - collection: "game-platforms", - params: { populate: { titles: { populate: "language" } } }, - }, - payload: { - collection: Collections.Keys, - convert: ({ slug, titles }) => ({ - name: slug, - type: "GamePlatforms", - translations: titles.map(({ title, short, language }) => { - if (isUndefined(language.data)) - throw new Error("A language is required for a Keys title translation"); - if (isUndefined(title)) - throw new Error("A title is required for a Keys title translation"); - return { - name: title, - short, - language: language.data.attributes.code, - }; - }), - }), - }, - user: req.user, - }); - - type StrapiMetadataTypes = { - slug: string; - titles: { title: string; language: StrapiLanguage }[]; - }; - - const { count: libraryCount, errors: libraryErrors } = - await importStrapiEntries({ - strapi: { - collection: "metadata-types", - params: { populate: { titles: { populate: "language" } } }, - }, - payload: { - collection: Collections.Keys, - convert: ({ slug, titles }) => ({ - name: slug, - type: "Library", - translations: titles.map(({ title, language }) => { - if (isUndefined(language.data)) - throw new Error("A language is required for a Keys title translation"); - return { - name: title, - language: language.data.attributes.code, - }; - }), - }), - }, - user: req.user, - }); - - type StrapiAudioSubtypes = { - slug: string; - titles: { title: string; language: StrapiLanguage }[]; - }; - - const { count: libraryAudioCount, errors: libraryAudioErrors } = - await importStrapiEntries({ - strapi: { - collection: "audio-subtypes", - params: { populate: { titles: { populate: "language" } } }, - }, - payload: { - collection: Collections.Keys, - convert: ({ slug, titles }) => ({ - name: slug, - type: "LibraryAudio", - translations: titles.map(({ title, language }) => { - if (isUndefined(language.data)) - throw new Error("A language is required for a Keys title translation"); - return { - name: title, - language: language.data.attributes.code, - }; - }), - }), - }, - user: req.user, - }); - - type StrapiGroupSubtypes = { - slug: string; - titles: { title: string; language: StrapiLanguage }[]; - }; - - const { count: libraryGroupCount, errors: libraryGroupErrors } = - await importStrapiEntries({ - strapi: { - collection: "group-subtypes", - params: { populate: { titles: { populate: "language" } } }, - }, - payload: { - collection: Collections.Keys, - convert: ({ slug, titles }) => ({ - name: slug, - type: "LibraryGroup", - translations: titles.map(({ title, language }) => { - if (isUndefined(language.data)) - throw new Error("A language is required for a Keys title translation"); - return { - name: title, - language: language.data.attributes.code, - }; - }), - }), - }, - user: req.user, - }); - - type StrapiTextualSubtypes = { - slug: string; - titles: { title: string; language: StrapiLanguage }[]; - }; - - const { count: libraryTextualCount, errors: libraryTextualErrors } = - await importStrapiEntries({ - strapi: { - collection: "textual-subtypes", - params: { populate: { titles: { populate: "language" } } }, - }, - payload: { - collection: Collections.Keys, - convert: ({ slug, titles }) => ({ - name: slug, - type: "LibraryTextual", - translations: titles.map(({ title, language }) => { - if (isUndefined(language.data)) - throw new Error("A language is required for a Keys title translation"); - return { - name: title, - language: language.data.attributes.code, - }; - }), - }), - }, - user: req.user, - }); - - type StrapiVideoSubtypes = { - slug: string; - titles: { title: string; language: StrapiLanguage }[]; - }; - - const { count: libraryVideoCount, errors: libraryVideoErrors } = - await importStrapiEntries({ - strapi: { - collection: "video-subtypes", - params: { populate: { titles: { populate: "language" } } }, - }, - payload: { - collection: Collections.Keys, - convert: ({ slug, titles }) => ({ - name: slug, - type: "LibraryVideo", - translations: titles.map(({ title, language }) => { - if (isUndefined(language.data)) - throw new Error("A language is required for a Keys title translation"); - return { - name: title, - language: language.data.attributes.code, - }; - }), - }), - }, - user: req.user, - }); - - type StrapiWeaponTypes = { - slug: string; - translations: { name?: string; language: StrapiLanguage }[]; - }; - - const { count: weaponsCount, errors: weaponsErrors } = - await importStrapiEntries({ - strapi: { - collection: "weapon-story-types", - params: { populate: { translations: { populate: "language" } } }, - }, - payload: { - collection: Collections.Keys, - convert: ({ slug, translations }) => ({ - name: slug, - type: "Weapons", - translations: translations.map(({ name, language }) => { - if (isUndefined(language.data)) - throw new Error("A language is required for a Keys title translation"); - if (isUndefined(name)) - throw new Error("A name is required for a Keys title translation"); - return { - name, - language: language.data.attributes.code, - }; - }), - }), - }, - user: req.user, - }); - - const { count: wordingsCount, errors: wordingsErrors } = await importStrapiWordings({ - strapi: { collection: "website-interfaces", params: { populate: "ui_language" } }, - payload: { collection: Collections.Keys, convert: (strapiObject) => strapiObject }, - user: req.user, - }); - - res.status(200).json({ - message: `${ - categoriesCount + - contentTypesCount + - gamePlatformsCount + - libraryCount + - libraryAudioCount + - libraryGroupCount + - libraryTextualCount + - libraryVideoCount + - weaponsCount + - wordingsCount - } entries have been added successfully.`, - errors: { - categoriesErrors, - contentTypesErrors, - gamePlatformsErrors, - libraryErrors, - libraryAudioErrors, - libraryGroupErrors, - libraryTextualErrors, - libraryVideoErrors, - weaponsErrors, - wordingsErrors, - }, - }); - }, -}; diff --git a/src/collections/LibraryItems/LibraryItems.ts b/src/collections/LibraryItems/LibraryItems.ts deleted file mode 100644 index c014314..0000000 --- a/src/collections/LibraryItems/LibraryItems.ts +++ /dev/null @@ -1,790 +0,0 @@ -import { RowLabelArgs } from "payload/dist/admin/components/forms/RowLabel/types"; -import { - CollectibleBindingTypes, - CollectiblePageOrders, - CollectionGroups, - Collections, - FileTypes, - KeysTypes, - LibraryItemsTypes, - PageType, -} from "../../constants"; -import { backPropagationField } from "../../fields/backPropagationField/backPropagationField"; -import { componentField } from "../../fields/componentField/componentField"; -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"; -import { beforeDuplicatePiping } from "../../hooks/beforeDuplicatePiping"; -import { beforeDuplicateUnpublish } from "../../hooks/beforeDuplicateUnpublish"; -import { LibraryItem } from "../../types/collections"; -import { isDefined } from "../../utils/asserts"; -import { createEditor } from "../../utils/editor"; -import { buildVersionedCollectionConfig } from "../../utils/versionedCollectionConfig"; -import { RowLabel } from "./components/RowLabel"; - -const fields = { - status: "_status", - itemType: "itemType", - language: "language", - - slug: "slug", - thumbnail: "thumbnail", - - pretitle: "pretitle", - title: "title", - subtitle: "subtitle", - - translations: "translations", - translationsDescription: "description", - - digital: "digital", - - size: "size", - width: "width", - height: "height", - thickness: "thickness", - - price: "price", - priceAmount: "amount", - priceCurrency: "currency", - releaseDate: "releaseDate", - - gallery: "gallery", - galleryImage: "image", - - urls: "urls", - urlsUrl: "url", - - categories: "categories", - - textual: "textual", - textualSubtype: "subtype", - textualBindingType: "bindingType", - textualPageCount: "pageCount", - textualPageOrder: "pageOrder", - - audio: "audio", - audioSubtype: "audioSubtype", - audioTracks: "tracks", - audioTracksFile: "file", - audioTracksTitle: "title", - - video: "video", - videoSubtype: "subtype", - - game: "game", - gameDemo: "demo", - gamePlatform: "platform", - gameAudioLanguages: "audioLanguages", - gameSubtitleLanguages: "subtitleLanguages", - gameInterfacesLanguages: "interfacesLanguages", - - scans: "scans", - scansScanners: "scanners", - scansCleaners: "cleaners", - scansTypesetters: "typesetters", - - scansCover: "cover", - scansCoverFlapFront: "flapFront", - scansCoverFront: "front", - scansCoverSpine: "spine", - scansCoverBack: "back", - scansCoverFlapBack: "flapBack", - scansCoverInsideFlapFront: "insideFlapFront", - scansCoverInsideFront: "insideFront", - scansCoverInsideBack: "insideBack", - scansCoverInsideFlapBack: "insideFlapBack", - - scansDustjacket: "dustjacket", - scansDustjacketFlapFront: "flapFront", - scansDustjacketFront: "front", - scansDustjacketSpine: "spine", - scansDustjacketBack: "back", - scansDustjacketFlapBack: "flapBack", - scansDustjacketInsideFlapFront: "insideFlapFront", - scansDustjacketInsideFront: "insideFront", - scansDustjacketInsideSpine: "insideSpine", - scansDustjacketInsideBack: "insideBack", - scansDustjacketInsideFlapBack: "insideFlapBack", - - scansObi: "obi", - scansObiFlapFront: "flapFront", - scansObiFront: "front", - scansObiSpine: "spine", - scansObiBack: "back", - scansObiFlapBack: "flapBack", - scansObiInsideFlapFront: "insideFlapFront", - scansObiInsideFront: "insideFront", - scansObiInsideSpine: "insideSpine", - scansObiInsideBack: "insideBack", - scansObiInsideFlapBack: "insideFlapBack", - - scansPages: "pages", - scansPagesPage: "page", - scansPagesImage: "image", - - scanArchiveFile: "archiveFile", - - contents: "contents", - contentsContent: "content", - contentsPageStart: "pageStart", - contentsPageEnd: "pageEnd", - contentsTimeStart: "timeStart", - contentsTimeEnd: "timeEnd", - contentsNote: "note", - - parentFolders: "parentFolders", - parentItems: "parentItems", - subitems: "subitems", -} as const satisfies Record; - -export const LibraryItems = buildVersionedCollectionConfig({ - slug: Collections.LibraryItems, - labels: { - singular: "Library Item", - plural: "Library Items", - }, - defaultSort: fields.slug, - admin: { - useAsTitle: fields.slug, - description: - "A comprehensive list of all Yokoverse’s side materials (books, novellas, artbooks, \ - stage plays, manga, drama CDs, and comics).", - defaultColumns: [fields.thumbnail, fields.slug, fields.status], - group: CollectionGroups.Collections, - hooks: { - beforeDuplicate: beforeDuplicatePiping([ - beforeDuplicateUnpublish, - beforeDuplicateAddCopyTo(fields.slug), - ]), - }, - }, - fields: [ - rowField([ - { - name: fields.itemType, - type: "radio", - options: Object.entries(LibraryItemsTypes).map(([value, label]) => ({ - label, - value, - })), - admin: { - layout: "horizontal", - }, - }, - { - name: fields.language, - type: "relationship", - relationTo: Collections.Languages, - required: true, - admin: { - allowCreate: false, - description: - "This item sole or primary language (most notably, the language used on the cover)", - }, - }, - ]), - { - type: "tabs", - admin: { - condition: ({ itemType }) => isDefined(itemType), - }, - tabs: [ - { - label: "Overview", - fields: [ - rowField([ - slugField({ - name: fields.slug, - }), - imageField({ - name: fields.thumbnail, - relationTo: Collections.LibraryItemsThumbnails, - }), - ]), - rowField([ - { name: fields.pretitle, type: "text" }, - { name: fields.title, type: "text", required: true }, - { name: fields.subtitle, type: "text" }, - ]), - { - name: fields.digital, - type: "checkbox", - required: true, - defaultValue: false, - admin: { - description: - "The item is the digital version of another item, or the item is sold only digitally.", - }, - }, - ], - }, - { - label: "Images", - fields: [ - { - name: fields.gallery, - type: "array", - admin: { - description: - "Additional images of the item (unboxing, on shelf, promotional images...)", - }, - labels: { singular: "Image", plural: "Images" }, - fields: [ - imageField({ - name: fields.galleryImage, - relationTo: Collections.LibraryItemsGallery, - }), - ], - }, - componentField({ - name: fields.scans, - fields: [ - rowField([ - { - name: fields.scansScanners, - type: "relationship", - relationTo: Collections.Recorders, - hasMany: true, - required: true, - }, - { - name: fields.scansCleaners, - type: "relationship", - relationTo: Collections.Recorders, - hasMany: true, - required: true, - }, - { - name: fields.scansTypesetters, - type: "relationship", - relationTo: Collections.Recorders, - hasMany: true, - }, - ]), - componentField({ - name: fields.scansCover, - fields: [ - rowField([ - imageField({ - name: fields.scansCoverFront, - relationTo: Collections.LibraryItemsScans, - }), - imageField({ - name: fields.scansCoverSpine, - relationTo: Collections.LibraryItemsScans, - }), - imageField({ - name: fields.scansCoverBack, - relationTo: Collections.LibraryItemsScans, - }), - ]), - rowField([ - imageField({ - name: fields.scansCoverInsideFront, - relationTo: Collections.LibraryItemsScans, - }), - imageField({ - name: fields.scansCoverInsideBack, - relationTo: Collections.LibraryItemsScans, - }), - ]), - rowField([ - imageField({ - name: fields.scansCoverFlapFront, - relationTo: Collections.LibraryItemsScans, - }), - imageField({ - name: fields.scansCoverFlapBack, - relationTo: Collections.LibraryItemsScans, - }), - imageField({ - name: fields.scansCoverInsideFlapFront, - relationTo: Collections.LibraryItemsScans, - }), - imageField({ - name: fields.scansCoverInsideFlapBack, - relationTo: Collections.LibraryItemsScans, - }), - ]), - ], - }), - componentField({ - name: fields.scansDustjacket, - label: "Dust Jacket", - admin: { - description: - "The dust jacket of a book is the detachable outer cover with folded \ - flaps that hold it to the front and back book covers", - }, - fields: [ - rowField([ - imageField({ - name: fields.scansDustjacketFront, - relationTo: Collections.LibraryItemsScans, - }), - imageField({ - name: fields.scansDustjacketSpine, - relationTo: Collections.LibraryItemsScans, - }), - imageField({ - name: fields.scansDustjacketBack, - relationTo: Collections.LibraryItemsScans, - }), - ]), - rowField([ - imageField({ - name: fields.scansDustjacketInsideFront, - relationTo: Collections.LibraryItemsScans, - }), - imageField({ - name: fields.scansDustjacketInsideSpine, - relationTo: Collections.LibraryItemsScans, - }), - imageField({ - name: fields.scansDustjacketInsideBack, - relationTo: Collections.LibraryItemsScans, - }), - ]), - rowField([ - imageField({ - name: fields.scansDustjacketFlapFront, - relationTo: Collections.LibraryItemsScans, - }), - imageField({ - name: fields.scansDustjacketFlapBack, - relationTo: Collections.LibraryItemsScans, - }), - imageField({ - name: fields.scansDustjacketInsideFlapFront, - relationTo: Collections.LibraryItemsScans, - }), - imageField({ - name: fields.scansDustjacketInsideFlapBack, - relationTo: Collections.LibraryItemsScans, - }), - ]), - ], - }), - componentField({ - name: fields.scansObi, - label: "Obi", - admin: { - description: - "An obi is a strip of paper looped around a book or other product. \ - it typically add marketing claims, or other relevant information about the product.", - }, - fields: [ - rowField([ - imageField({ - name: fields.scansObiFront, - relationTo: Collections.LibraryItemsScans, - }), - imageField({ - name: fields.scansObiSpine, - relationTo: Collections.LibraryItemsScans, - }), - imageField({ - name: fields.scansObiBack, - relationTo: Collections.LibraryItemsScans, - }), - ]), - rowField([ - imageField({ - name: fields.scansObiInsideFront, - relationTo: Collections.LibraryItemsScans, - }), - imageField({ - name: fields.scansObiInsideSpine, - relationTo: Collections.LibraryItemsScans, - }), - imageField({ - name: fields.scansObiInsideBack, - relationTo: Collections.LibraryItemsScans, - }), - ]), - rowField([ - imageField({ - name: fields.scansObiFlapFront, - relationTo: Collections.LibraryItemsScans, - }), - imageField({ - name: fields.scansObiFlapBack, - relationTo: Collections.LibraryItemsScans, - }), - imageField({ - name: fields.scansObiInsideFlapFront, - relationTo: Collections.LibraryItemsScans, - }), - imageField({ - name: fields.scansObiInsideFlapBack, - relationTo: Collections.LibraryItemsScans, - }), - ]), - ], - }), - { - name: fields.scansPages, - type: "array", - admin: { - initCollapsed: true, - description: - "Make sure the page number corresponds to the page number written on \ - the scan. You can use negative page numbers if necessary.", - components: { - RowLabel: ({ data }: RowLabelArgs) => RowLabel(data), - }, - }, - fields: [ - rowField([ - { - name: fields.scansPagesPage, - type: "number", - required: true, - }, - imageField({ - name: fields.scansPagesImage, - relationTo: Collections.LibraryItemsScans, - required: true, - }), - ]), - ], - }, - fileField({ - name: fields.scanArchiveFile, - relationTo: FileTypes.LibraryScans, - }), - ], - }), - ], - }, - { - label: "Type", - admin: { condition: () => false }, - fields: [ - { - name: fields.textual, - type: "group", - label: false, - admin: { - condition: (data: Partial) => - data.itemType === LibraryItemsTypes.Textual, - }, - fields: [ - { - type: "row", - fields: [ - keysField({ - name: fields.textualSubtype, - relationTo: KeysTypes.LibraryTextual, - }), - { - name: fields.textualPageCount, - type: "number", - min: 1, - }, - ], - }, - rowField([ - { - name: fields.textualBindingType, - type: "radio", - options: Object.entries(CollectibleBindingTypes).map( - ([value, label]) => ({ - label, - value, - }) - ), - admin: { - condition: (data: Partial) => !data.digital, - layout: "horizontal", - }, - }, - { - name: fields.textualPageOrder, - type: "radio", - options: Object.entries(CollectiblePageOrders).map( - ([value, label]) => ({ - label, - value, - }) - ), - admin: { - layout: "horizontal", - }, - }, - ]), - ], - }, - { - name: fields.audio, - type: "group", - label: false, - admin: { - condition: (data: Partial) => - data.itemType === LibraryItemsTypes.Audio, - }, - fields: [ - keysField({ - name: fields.audioSubtype, - relationTo: KeysTypes.LibraryAudio, - }), - { - name: fields.audioTracks, - type: "array", - fields: [ - rowField([ - { - name: fields.audioTracksTitle, - type: "text", - required: true, - }, - fileField({ - name: fields.audioTracksFile, - relationTo: FileTypes.LibrarySoundtracks, - required: true, - }), - ]), - ], - }, - ], - }, - { - name: fields.video, - type: "group", - label: false, - admin: { - condition: (data: Partial) => - data.itemType === LibraryItemsTypes.Video, - }, - fields: [ - keysField({ - name: fields.videoSubtype, - relationTo: KeysTypes.LibraryVideo, - }), - ], - }, - { - name: fields.game, - type: "group", - label: false, - admin: { - condition: (data: Partial) => data.itemType === LibraryItemsTypes.Game, - }, - fields: [ - rowField([ - { - name: fields.gameDemo, - type: "checkbox", - admin: { description: "Is this item a demo for the game" }, - }, - keysField({ - name: fields.gamePlatform, - relationTo: KeysTypes.GamePlatforms, - }), - ]), - rowField([ - { - name: fields.gameAudioLanguages, - type: "relationship", - relationTo: Collections.Languages, - hasMany: true, - }, - { - name: fields.gameSubtitleLanguages, - type: "relationship", - relationTo: Collections.Languages, - hasMany: true, - }, - { - name: fields.gameInterfacesLanguages, - type: "relationship", - relationTo: Collections.Languages, - hasMany: true, - }, - ]), - ], - }, - ], - }, - { - label: "Details", - fields: [ - rowField([ - { - name: fields.releaseDate, - type: "date", - admin: { - date: { pickerAppearance: "dayOnly", displayFormat: "yyyy-MM-dd" }, - }, - }, - keysField({ - name: fields.categories, - relationTo: KeysTypes.Categories, - hasMany: true, - }), - ]), - componentField({ - name: fields.size, - admin: { - condition: (data: Partial) => !data.digital, - description: "Add physical size information about the item", - }, - fields: [ - rowField([ - { - name: fields.width, - type: "number", - required: true, - admin: { step: 1, description: "in mm." }, - }, - { - name: fields.height, - type: "number", - required: true, - admin: { step: 1, description: "in mm." }, - }, - { - name: fields.thickness, - type: "number", - admin: { step: 1, description: "in mm." }, - }, - ]), - ], - }), - componentField({ - name: fields.price, - admin: { description: "Add pricing information about the item" }, - fields: [ - rowField([ - { - name: fields.priceAmount, - type: "number", - required: true, - min: 0, - }, - { - name: fields.priceCurrency, - type: "relationship", - relationTo: Collections.Currencies, - required: true, - admin: { allowCreate: false }, - }, - ]), - ], - }), - translatedFields({ - name: fields.translations, - label: "Descriptions", - admin: { initCollapsed: true, useAsTitle: fields.translationsDescription }, - fields: [ - { - name: fields.translationsDescription, - required: true, - type: "richText", - editor: createEditor({ inlines: true, lists: true, links: true }), - }, - ], - }), - { - name: fields.urls, - label: "URLs", - type: "array", - admin: { - description: "Links to official websites where to get/buy the item.", - }, - fields: [{ name: fields.urlsUrl, type: "text", required: true }], - }, - ], - }, - { - label: "Contents", - fields: [ - rowField([ - // TODO: Uncomment when the Folders are ready - // backPropagationField({ - // name: fields.parentFolders, - // relationTo: Collections.Folders, - // hasMany: true, - // where: ({ id }) => ({ files: { equals: id } }), - // admin: { - // description: `You can set the folders from the "Folders" collection`, - // }, - // }), - backPropagationField({ - name: fields.parentItems, - relationTo: Collections.LibraryItems, - hasMany: true, - where: ({ id }) => ({ [fields.subitems]: { equals: id } }), - }), - { - name: fields.subitems, - type: "relationship", - hasMany: true, - relationTo: Collections.LibraryItems, - }, - ]), - { - name: fields.contents, - type: "array", - fields: [ - { - name: fields.contentsContent, - type: "relationship", - relationTo: Collections.Pages, - admin: { - allowCreate: false, - }, - required: true, - filterOptions: { type: { equals: PageType.Content } }, - }, - { - type: "row", - admin: { - // TODO: Check why those condition doesn't work - condition: ({ itemType }: Partial) => - itemType === LibraryItemsTypes.Textual, - }, - fields: [ - { - name: fields.contentsPageStart, - type: "number", - }, - { name: fields.contentsPageEnd, type: "number" }, - ], - }, - { - type: "row", - admin: { - condition: ({ itemType }: Partial) => - itemType === LibraryItemsTypes.Audio || itemType === LibraryItemsTypes.Video, - }, - fields: [ - { - name: fields.contentsTimeStart, - type: "number", - }, - { name: fields.contentsTimeEnd, type: "number" }, - ], - }, - { - name: fields.contentsNote, - type: "richText", - editor: createEditor({ inlines: true, lists: true, links: true }), - admin: { - condition: ({ itemType }: Partial) => - itemType === LibraryItemsTypes.Game || itemType === LibraryItemsTypes.Other, - }, - }, - ], - }, - ], - }, - ], - }, - ], -}); diff --git a/src/collections/LibraryItems/components/RowLabel.tsx b/src/collections/LibraryItems/components/RowLabel.tsx deleted file mode 100644 index 47e97ee..0000000 --- a/src/collections/LibraryItems/components/RowLabel.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import React from "react"; -import styled from "styled-components"; -import { isDefined } from "../../../utils/asserts"; - -interface Props { - page?: number; - image?: string; -} - -const Container = styled.div` - display: flex; - place-items: center; - gap: 10px; -`; - -const Title = styled.div` - font-weight: 600; - font-size: 1.2rem; -`; - -export const RowLabel = ({ page, image }: Props): JSX.Element => ( - - {isDefined(page) &&
{`Page ${page}`}
} - {isDefined(image) && {image}} -
-); diff --git a/src/collections/LibraryItemsGallery/LibraryItemsGallery.ts b/src/collections/LibraryItemsGallery/LibraryItemsGallery.ts deleted file mode 100644 index b12560e..0000000 --- a/src/collections/LibraryItemsGallery/LibraryItemsGallery.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Collections } from "../../constants"; -import { buildImageCollectionConfig } from "../../utils/imageCollectionConfig"; - -const fields = { - filename: "filename", - mimeType: "mimeType", - filesize: "filesize", - updatedAt: "updatedAt", -} as const satisfies Record; - -export const LibraryItemsGallery = buildImageCollectionConfig({ - slug: Collections.LibraryItemsGallery, - labels: { - singular: "Library Item Gallery", - plural: "Library Item Gallery", - }, - admin: { defaultColumns: [fields.filename, fields.updatedAt] }, - upload: { - imageSizes: [ - { - name: "small", - height: 512, - width: 512, - fit: "cover", - formatOptions: { - format: "webp", - options: { effort: 6, quality: 60, alphaQuality: 60 }, - }, - }, - ], - }, - fields: [], -}); diff --git a/src/collections/LibraryItemsScans/LibraryItemsScans.ts b/src/collections/LibraryItemsScans/LibraryItemsScans.ts deleted file mode 100644 index 99d3c84..0000000 --- a/src/collections/LibraryItemsScans/LibraryItemsScans.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { Collections } from "../../constants"; -import { buildImageCollectionConfig } from "../../utils/imageCollectionConfig"; - -const fields = { - filename: "filename", - mimeType: "mimeType", - filesize: "filesize", - updatedAt: "updatedAt", -} as const satisfies Record; - -export const LibraryItemsScans = buildImageCollectionConfig({ - slug: Collections.LibraryItemsScans, - labels: { - singular: "Library Item Scans", - plural: "Library Item Scans", - }, - admin: { defaultColumns: [fields.filename, fields.updatedAt] }, - upload: { - imageSizes: [ - { - name: "og", - height: 1024, - width: 1024, - fit: "contain", - formatOptions: { - format: "jpg", - options: { progressive: true, mozjpeg: true, compressionLevel: 9, quality: 80 }, - }, - }, - { - name: "medium", - height: 1024, - width: 1024, - fit: "contain", - formatOptions: { - format: "webp", - options: { effort: 6, quality: 80, alphaQuality: 80 }, - }, - }, - { - name: "large", - height: 2048, - width: 2048, - fit: "contain", - formatOptions: { - format: "webp", - options: { effort: 6, quality: 80, alphaQuality: 80 }, - }, - }, - ], - }, - fields: [], -}); diff --git a/src/collections/LibraryItemsThumbnails/LibraryItemsThumbnails.ts b/src/collections/LibraryItemsThumbnails/LibraryItemsThumbnails.ts deleted file mode 100644 index c8eaaee..0000000 --- a/src/collections/LibraryItemsThumbnails/LibraryItemsThumbnails.ts +++ /dev/null @@ -1,53 +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", - libraryItem: "libraryItem", - updatedAt: "updatedAt", -} as const satisfies Record; - -export const LibraryItemsThumbnails = buildImageCollectionConfig({ - slug: Collections.LibraryItemsThumbnails, - labels: { - singular: "Library Item Thumbnail", - plural: "Library Item Thumbnails", - }, - admin: { defaultColumns: [fields.filename, fields.libraryItem, fields.updatedAt] }, - upload: { - imageSizes: [ - { - name: "og", - height: 1024, - width: 1024, - fit: "inside", - formatOptions: { - format: "jpg", - options: { progressive: true, mozjpeg: true, compressionLevel: 9, quality: 80 }, - }, - }, - { - name: "square", - height: 1024, - width: 1024, - fit: "contain", - background: { r: 0, g: 0, b: 0, alpha: 0 }, - formatOptions: { - format: "webp", - options: { effort: 6, quality: 80, alphaQuality: 80 }, - }, - }, - ], - }, - fields: [ - backPropagationField({ - name: fields.libraryItem, - hasMany: true, - relationTo: Collections.LibraryItems, - where: ({ id }) => ({ thumbnail: { equals: id } }), - }), - ], -}); diff --git a/src/collections/Pages/Pages.ts b/src/collections/Pages/Pages.ts index d29d560..f40525e 100644 --- a/src/collections/Pages/Pages.ts +++ b/src/collections/Pages/Pages.ts @@ -38,7 +38,7 @@ const fields = { folders: "folders", } as const satisfies Record; -const pageTypesWithAuthor = [PageType.Article]; +const pageTypesWithAuthor = [PageType.Post]; const pageTypesWithCollectibles = [PageType.Content]; const pageTypesWithTranscribers = [PageType.Content]; @@ -76,7 +76,10 @@ export const Pages = buildVersionedCollectionConfig({ type: "radio", required: true, defaultValue: PageType.Generic, - options: Object.entries(PageType).map(([value, label]) => ({ label, value })), + options: Object.entries(PageType).map(([_, value]) => ({ + label: value, + value: value, + })), }, rowField([ slugField({ name: fields.slug }), @@ -119,7 +122,10 @@ export const Pages = buildVersionedCollectionConfig({ name: fields.content, type: "richText", required: true, - admin: {description: "Looking for help? Read the Rich Text Editor guide here: https://accords-library.com/dev/rich-text"}, + admin: { + description: + "Looking for help? Read the Rich Text Editor guide here: https://accords-library.com/dev/rich-text", + }, editor: createEditor({ images: true, inlines: true, @@ -206,12 +212,17 @@ export const Pages = buildVersionedCollectionConfig({ backPropagationField({ name: fields.collectibles, hasMany: true, - relationTo: Collections.LibraryItems, + relationTo: Collections.Collectibles, admin: { condition: (_, siblingData) => pageTypesWithCollectibles.includes(siblingData[fields.type]), }, - where: ({ id }) => ({ "contents.content": { equals: id } }), + where: ({ id }) => ({ + and: [ + { "contents.content.value": { equals: id } }, + { "contents.content.relationTo": { equals: Collections.Pages } }, + ] as Where[], + }), }), ]), ], diff --git a/src/collections/Pages/endpoints/getBySlugEndpoint.ts b/src/collections/Pages/endpoints/getBySlugEndpoint.ts index 5fc5088..fdad64a 100644 --- a/src/collections/Pages/endpoints/getBySlugEndpoint.ts +++ b/src/collections/Pages/endpoints/getBySlugEndpoint.ts @@ -9,7 +9,7 @@ import { createGetByEndpoint } from "../../../endpoints/createGetByEndpoint"; import { EndpointPage, ParentPage, TableOfContentEntry } from "../../../sdk"; import { Page } from "../../../types/collections"; import { isPayloadArrayType, isPayloadType, isValidPayloadImage } from "../../../utils/asserts"; -import { convertTagsToGroups } from "../../../utils/tags"; +import { convertTagsToGroups } from "../../../utils/endpoints"; export const getBySlugEndpoint = createGetByEndpoint( Collections.Pages, @@ -105,11 +105,14 @@ const handleParentPages = ({ const result: ParentPage[] = []; if (collectibles && isPayloadArrayType(collectibles)) { - collectibles.forEach(({ slug, title }) => { + collectibles.forEach(({ slug, translations }) => { result.push({ - collection: Collections.LibraryItems, + collection: Collections.Collectibles, slug, - translations: [{ language: "en", name: title }], + translations: translations.map(({ language, title }) => ({ + language: isPayloadType(language) ? language.id : language, + name: title, // TODO: Use the entire pretitle + title + subtitle + })), tag: "collectible", }); }); diff --git a/src/collections/Recorders/Recorders.ts b/src/collections/Recorders/Recorders.ts index 37f66ec..0b4f6d9 100644 --- a/src/collections/Recorders/Recorders.ts +++ b/src/collections/Recorders/Recorders.ts @@ -51,9 +51,9 @@ export const Recorders = buildCollectionConfig({ slug: Collections.Recorders, filterGroups: [ [ - ...Object.entries(RecordersRoles).map(([key, value]) => ({ + ...Object.entries(RecordersRoles).map(([_, value]) => ({ label: value, - filter: { where: { role: { equals: key } } }, + filter: { where: { role: { equals: value } } }, })), { label: "∅ Role", @@ -127,9 +127,9 @@ export const Recorders = buildCollectionConfig({ create: mustBeAdminForFields, }, hasMany: true, - options: Object.entries(RecordersRoles).map(([value, label]) => ({ - label, - value, + options: Object.entries(RecordersRoles).map(([_, value]) => ({ + label: value, + value: value, })), }, { diff --git a/src/collections/Tags/Tags.ts b/src/collections/Tags/Tags.ts index c3a287f..b6f48c6 100644 --- a/src/collections/Tags/Tags.ts +++ b/src/collections/Tags/Tags.ts @@ -3,6 +3,7 @@ import { CollectionBeforeChangeHook, CollectionConfig } from "payload/types"; import { CollectionGroups, Collections } from "../../constants"; import { slugField } from "../../fields/slugField/slugField"; import { translatedFields } from "../../fields/translatedFields/translatedFields"; +import { beforeDuplicateAddCopyTo } from "../../hooks/beforeDuplicateAddCopyTo"; import { buildCollectionConfig } from "../../utils/collectionConfig"; import { getAllEndpoint } from "./endpoints/getAllEndpoint"; @@ -40,6 +41,9 @@ export const Tags: CollectionConfig = buildCollectionConfig({ group: CollectionGroups.Meta, useAsTitle: fields.name, defaultColumns: [fields.slug, fields.group, fields.translations], + hooks: { + beforeDuplicate: beforeDuplicateAddCopyTo(fields.slug), + }, }, endpoints: [getAllEndpoint], hooks: { beforeChange: [beforeChangeUpdateName] }, @@ -53,6 +57,11 @@ export const Tags: CollectionConfig = buildCollectionConfig({ minRows: 1, fields: [{ name: fields.translationsName, type: "text", required: true }], }), - { name: fields.group, type: "relationship", required: true, relationTo: Collections.TagsGroups }, + { + name: fields.group, + type: "relationship", + required: true, + relationTo: Collections.TagsGroups, + }, ], }); diff --git a/src/collections/Videos/Videos.ts b/src/collections/Videos/Videos.ts index b4f1d27..f189132 100644 --- a/src/collections/Videos/Videos.ts +++ b/src/collections/Videos/Videos.ts @@ -64,7 +64,10 @@ export const Videos: CollectionConfig = buildCollectionConfig({ name: fields.source, type: "select", required: true, - options: Object.entries(VideoSources).map(([value, label]) => ({ label, value })), + options: Object.entries(VideoSources).map(([_, value]) => ({ + label: value, + value: value, + })), }, ]), { name: fields.title, type: "text", required: true }, diff --git a/src/collections/Weapons/Weapons.ts b/src/collections/Weapons/Weapons.ts index d8ca8cf..1a61b29 100644 --- a/src/collections/Weapons/Weapons.ts +++ b/src/collections/Weapons/Weapons.ts @@ -1,7 +1,6 @@ import { RowLabelArgs } from "payload/dist/admin/components/forms/RowLabel/types"; -import { CollectionGroups, Collections, KeysTypes } from "../../constants"; +import { CollectionGroups, Collections } 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"; @@ -54,11 +53,6 @@ export const Weapons = buildVersionedCollectionConfig({ }), ]), rowField([ - keysField({ - name: fields.type, - relationTo: KeysTypes.Weapons, - required: true, - }), { name: fields.group, type: "relationship", @@ -78,12 +72,6 @@ export const Weapons = buildVersionedCollectionConfig({ }, }, fields: [ - keysField({ - name: fields.appearancesCategories, - required: true, - hasMany: true, - relationTo: KeysTypes.Categories, - }), translatedFields({ name: fields.appearancesTranslations, required: true, diff --git a/src/collections/Wordings/Wordings.ts b/src/collections/Wordings/Wordings.ts index 28e1d6f..2aa6359 100644 --- a/src/collections/Wordings/Wordings.ts +++ b/src/collections/Wordings/Wordings.ts @@ -1,7 +1,7 @@ import { CollectionConfig } from "payload/types"; import { mustBeAdmin } from "../../accesses/collections/mustBeAdmin"; -import { QuickFilters } from "../../components/QuickFilters"; -import { CollectionGroups, Collections, LanguageCodes } from "../../constants"; +import { QuickFilters, languageBasedFilters } from "../../components/QuickFilters"; +import { CollectionGroups, Collections } from "../../constants"; import { rowField } from "../../fields/rowField/rowField"; import { translatedFields } from "../../fields/translatedFields/translatedFields"; import { afterOperationWebhook } from "../../hooks/afterOperationWebhook"; @@ -34,12 +34,7 @@ export const Wordings: CollectionConfig = buildCollectionConfig({ () => QuickFilters({ slug: Collections.Wordings, - filterGroups: [ - Object.entries(LanguageCodes).map(([key, value]) => ({ - label: `∅ ${value}`, - filter: { where: { "translations.language": { not_equals: key } } }, - })), - ], + filterGroups: [languageBasedFilters("translations.language")], }), ], }, diff --git a/src/constants.ts b/src/constants.ts index 1db4784..e5a3b23 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -13,12 +13,7 @@ export enum Collections { ChronologyItems = "chronology-items", Currencies = "currencies", Files = "files", - Keys = "keys", Languages = "languages", - LibraryItems = "library-items", - LibraryItemsThumbnails = "library-items-thumbnails", - LibraryItemsScans = "library-items-scans", - LibraryItemsGallery = "library-items-gallery", Notes = "notes", Pages = "pages", PagesThumbnails = "pages-thumbnails", @@ -45,19 +40,6 @@ export enum CollectionGroups { Meta = "Meta", } -export enum KeysTypes { - Contents = "Contents", - LibraryAudio = "Library / Audio", - LibraryVideo = "Library / Video", - LibraryTextual = "Library / Textual", - LibraryGroup = "Library / Group", - Library = "Library", - Weapons = "Weapons", - GamePlatforms = "Game Platforms", - Categories = "Categories", - Wordings = "Wordings", -} - export enum LanguageCodes { en = "English", fr = "French", @@ -67,21 +49,6 @@ export enum LanguageCodes { "zh" = "Chinese", } -export enum FileTypes { - LibraryScans = "Library / Scans", - LibrarySoundtracks = "Library / Soundtracks", - ContentVideo = "Content / Video", - ContentAudio = "Content / Audio", -} - -export enum LibraryItemsTypes { - Textual = "Textual", - Audio = "Audio", - Video = "Video", - Game = "Game", - Other = "Other", -} - export enum CollectibleBindingTypes { Paperback = "Paperback", Hardcover = "Hardcover", @@ -92,6 +59,17 @@ export enum CollectiblePageOrders { RightToLeft = "Right to left", } +export enum CollectibleNature { + Physical = "Physical", + Digital = "Digital", +} + +export enum CollectibleContentType { + None = "None", + Indexes = "Index-based", + Pages = "Page-based", +} + export enum RecordersRoles { Admin = "Admin", Recorder = "Recorder", @@ -122,17 +100,6 @@ export enum SpacerSizes { XLarge = "Extra Large", } -export enum CollectibleNature { - Physical = "Physical", - Digital = "Digital", -} - -export enum CollectibleContentType { - None = "None", - Indexes = "Index-based", - Pages = "Page-based", -} - /* RICH TEXT */ export type RichTextContent = { diff --git a/src/fields/backPropagationField/backPropagationField.ts b/src/fields/backPropagationField/backPropagationField.ts index 3cf7a01..08e190f 100644 --- a/src/fields/backPropagationField/backPropagationField.ts +++ b/src/fields/backPropagationField/backPropagationField.ts @@ -1,12 +1,11 @@ -import payload from "payload"; -import { FieldBase } from "payload/dist/fields/config/types"; -import { RelationshipField, Where } from "payload/types"; -import { Collections } from "../../constants"; +import payload, { GeneratedTypes } from "payload"; +import { FieldBase, SingleRelationshipField } from "payload/dist/fields/config/types"; +import { Where } from "payload/types"; import { isEmpty } from "../../utils/asserts"; type BackPropagationField = FieldBase & { where: (data: any) => Where; - relationTo: Collections; + relationTo: keyof GeneratedTypes["collections"]; hasMany?: boolean; }; export const backPropagationField = ({ @@ -15,7 +14,7 @@ export const backPropagationField = ({ where, hasMany = false, ...params -}: BackPropagationField): RelationshipField => ({ +}: BackPropagationField): SingleRelationshipField => ({ ...params, type: "relationship", hasMany: hasMany, diff --git a/src/fields/fileField/fileField.ts b/src/fields/fileField/fileField.ts deleted file mode 100644 index 0038cca..0000000 --- a/src/fields/fileField/fileField.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { FieldBase, RelationshipField } from "payload/dist/fields/config/types"; -import { Collections, FileTypes } from "../../constants"; - -type FileField = FieldBase & { - relationTo: FileTypes; - hasMany?: boolean; - admin?: RelationshipField["admin"]; -}; - -export const fileField = ({ - relationTo, - hasMany = false, - ...props -}: FileField): RelationshipField => ({ - ...props, - type: "relationship", - hasMany: hasMany, - relationTo: Collections.Files, - filterOptions: { type: { equals: getFileTypesKey(relationTo) } }, -}); - -const getFileTypesKey = (fileType: FileTypes): string | undefined => - Object.entries(FileTypes).find(([, value]) => value === fileType)?.[0]; diff --git a/src/fields/keysField/keysField.ts b/src/fields/keysField/keysField.ts deleted file mode 100644 index 5c780ad..0000000 --- a/src/fields/keysField/keysField.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { FieldBase, SingleRelationshipField } from "payload/dist/fields/config/types"; -import { Collections, KeysTypes } from "../../constants"; - -type KeysField = FieldBase & { - relationTo: KeysTypes; - hasMany?: boolean; - admin?: SingleRelationshipField["admin"]; -}; - -export const keysField = ({ - relationTo, - hasMany = false, - admin, - ...props -}: KeysField): SingleRelationshipField => ({ - ...props, - admin: { - allowCreate: false, - ...admin, - }, - type: "relationship", - hasMany: hasMany, - relationTo: Collections.Keys, - filterOptions: { type: { equals: getKeysTypesKey(relationTo) } }, -}); - -const getKeysTypesKey = (keyType: KeysTypes): string | undefined => - Object.entries(KeysTypes).find(([, value]) => value === keyType)?.[0]; diff --git a/src/fields/slugField/slugField.ts b/src/fields/slugField/slugField.ts index 4157c11..be7f7b6 100644 --- a/src/fields/slugField/slugField.ts +++ b/src/fields/slugField/slugField.ts @@ -1,7 +1,7 @@ import { TextField } from "payload/types"; import { isUndefined } from "../../utils/asserts"; -type Props = Omit; +type Props = Omit; const validateSlug = (value?: string) => { if (isUndefined(value) || value === "") return "This field is required."; diff --git a/src/payload.config.ts b/src/payload.config.ts index d1fdbc0..514c0b0 100644 --- a/src/payload.config.ts +++ b/src/payload.config.ts @@ -6,17 +6,11 @@ import { ChronologyEras } from "./collections/ChronologyEras/ChronologyEras"; import { ChronologyItems } from "./collections/ChronologyItems/ChronologyItems"; import { Collectibles } from "./collections/Collectibles/Collectibles"; import { Currencies } from "./collections/Currencies/Currencies"; -import { Files } from "./collections/Files/Files"; import { Folders } from "./collections/Folders/Folders"; import { FoldersThumbnails } from "./collections/FoldersThumbnails/FoldersThumbnails"; import { GenericContents } from "./collections/GenericContents/GenericContents"; import { Images } from "./collections/Images/Images"; -import { Keys } from "./collections/Keys/Keys"; import { Languages } from "./collections/Languages/Languages"; -import { LibraryItems } from "./collections/LibraryItems/LibraryItems"; -import { LibraryItemsGallery } from "./collections/LibraryItemsGallery/LibraryItemsGallery"; -import { LibraryItemsScans } from "./collections/LibraryItemsScans/LibraryItemsScans"; -import { LibraryItemsThumbnails } from "./collections/LibraryItemsThumbnails/LibraryItemsThumbnails"; import { Notes } from "./collections/Notes/Notes"; import { Pages } from "./collections/Pages/Pages"; import { Recorders } from "./collections/Recorders/Recorders"; @@ -51,25 +45,19 @@ export default buildConfig({ collections: [ Folders, FoldersThumbnails, - LibraryItems, Pages, ChronologyItems, ChronologyEras, Weapons, WeaponsGroups, WeaponsThumbnails, - LibraryItemsThumbnails, - LibraryItemsScans, - LibraryItemsGallery, RecordersThumbnails, - Files, Notes, Videos, VideosChannels, Languages, Currencies, Recorders, - Keys, Tags, TagsGroups, Images, diff --git a/src/sdk.ts b/src/sdk.ts index ab58c08..bb6eb97 100644 --- a/src/sdk.ts +++ b/src/sdk.ts @@ -1,5 +1,12 @@ -import { Collections, PageType, RichTextContent } from "./constants"; -import { Currency, Key, Language, LibraryItem, Page } from "./types/collections"; +import { + CollectibleBindingTypes, + CollectibleNature, + CollectiblePageOrders, + Collections, + PageType, + RichTextContent, +} from "./constants"; +import { Collectible, Currency, GenericContent, Language, Page } from "./types/collections"; class NodeCache { constructor(_params: any) {} @@ -171,8 +178,8 @@ export type EndpointFolder = EndpointFolderPreview & { }; files: ( | { - relationTo: "library-items"; - value: LibraryItem; + relationTo: "collectibles"; + value: Collectible; } | { relationTo: "pages"; @@ -204,17 +211,6 @@ export type EndpointRecorder = { }[]; }; -export type EndpointKey = { - id: string; - name: string; - type: Key["type"]; - translations: { - language: string; - name: string; - short: string; - }[]; -}; - export type EndpointWording = { name: string; translations: { @@ -272,6 +268,77 @@ export type ParentPage = { tag: string; }; +export type EndpointCollectiblePreview = { + slug: string; + thumbnail?: PayloadImage; + translations: { + language: string; + pretitle?: string; + title: string; + subtitle?: string; + description?: RichTextContent; + }[]; + tagGroups: TagGroup[]; + status: "draft" | "published"; + releaseDate?: string; + languages: string[]; +}; + +export type EndpointCollectible = EndpointCollectiblePreview & { + nature: CollectibleNature; + gallery: PayloadImage[]; + urls: { url: string; label: string }[]; + price?: { + amount: number; + currency: string; + }; + size?: { + width: number; + height: number; + thickness?: number; + }; + weight?: { + amount: number; + }; + pageInfo?: { + pageCount: number; + bindingType?: CollectibleBindingTypes; + pageOrder?: CollectiblePageOrders; + }; + subitems: EndpointCollectiblePreview[]; + contents: { + content: + | { + relationTo: "pages"; + value: Page; + } + | { + relationTo: "generic-contents"; + value: GenericContent; + }; + + range?: + | { + type: "pageRange"; + start: number; + end: number; + } + | { + type: "timeRange"; + start: string; + end: string; + } + | { + type: "other"; + translations: { + language: string; + note: RichTextContent; + }[]; + }; + }[]; + parentPages: ParentPage[]; +}; + export type TagGroup = { slug: string; icon: string; values: string[] }; export type TableOfContentEntry = { @@ -311,4 +378,6 @@ export const payload = { await (await request(payloadApiUrl(Collections.TagsGroups, `all`))).json(), getPage: async (slug: string): Promise => await (await request(payloadApiUrl(Collections.Pages, `slug/${slug}`))).json(), + getCollectible: async (slug: string): Promise => + await (await request(payloadApiUrl(Collections.Collectibles, `slug/${slug}`))).json(), }; diff --git a/src/types/collections.ts b/src/types/collections.ts index dd73b89..081a53f 100644 --- a/src/types/collections.ts +++ b/src/types/collections.ts @@ -35,41 +35,35 @@ export type RecorderBiographies = * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "CategoryTranslations". */ -export type CategoryTranslations = - | { - language: string | Language; - name: string; - id?: string | null; - }[] - | null; +export type CategoryTranslations = { + language: string | Language; + name: string; + id?: string | null; +}[]; export interface Config { collections: { folders: Folder; 'folders-thumbnails': FoldersThumbnail; - 'library-items': LibraryItem; pages: Page; 'chronology-items': ChronologyItem; 'chronology-eras': ChronologyEra; weapons: Weapon; 'weapons-groups': WeaponsGroup; 'weapons-thumbnails': WeaponsThumbnail; - 'library-items-thumbnails': LibraryItemThumbnail; - 'library-items-scans': LibraryItemScans; - 'library-items-gallery': LibraryItemGallery; 'recorders-thumbnails': RecordersThumbnail; - files: File; notes: Note; videos: Video; 'videos-channels': VideosChannel; languages: Language; currencies: Currency; recorders: Recorder; - keys: Key; tags: Tag; 'tags-groups': TagsGroup; images: Image; wordings: Wording; + collectibles: Collectible; + 'generic-contents': GenericContent; 'payload-preferences': PayloadPreference; 'payload-migrations': PayloadMigration; }; @@ -123,8 +117,8 @@ export interface Folder { files?: | ( | { - relationTo: 'library-items'; - value: string | LibraryItem; + relationTo: 'collectibles'; + value: string | Collectible; } | { relationTo: 'pages'; @@ -170,21 +164,40 @@ export interface Language { } /** * This interface was referenced by `Config`'s JSON-Schema - * via the `definition` "library-items". + * via the `definition` "collectibles". */ -export interface LibraryItem { +export interface Collectible { id: string; - itemType?: ('Textual' | 'Audio' | 'Video' | 'Game' | 'Other') | null; - language: string | Language; slug: string; - thumbnail?: string | LibraryItemThumbnail | null; - pretitle?: string | null; - title: string; - subtitle?: string | null; - digital: boolean; + thumbnail?: string | Image | null; + nature: 'Physical' | 'Digital'; + languages?: (string | Language)[] | null; + tags?: (string | Tag)[] | null; + translations: { + language: string | Language; + pretitle?: string | null; + title: string; + subtitle?: string | null; + description?: { + root: { + children: { + type: string; + version: number; + [k: string]: unknown; + }[]; + direction: ('ltr' | 'rtl') | null; + format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; + indent: number; + type: string; + version: number; + }; + [k: string]: unknown; + } | null; + id?: string | null; + }[]; gallery?: | { - image?: string | LibraryItemGallery | null; + image: string | Image; id?: string | null; }[] | null; @@ -195,141 +208,136 @@ export interface LibraryItem { typesetters?: (string | Recorder)[] | null; coverEnabled?: boolean | null; cover?: { - front?: string | LibraryItemScans | null; - spine?: string | LibraryItemScans | null; - back?: string | LibraryItemScans | null; - insideFront?: string | LibraryItemScans | null; - insideBack?: string | LibraryItemScans | null; - flapFront?: string | LibraryItemScans | null; - flapBack?: string | LibraryItemScans | null; - insideFlapFront?: string | LibraryItemScans | null; - insideFlapBack?: string | LibraryItemScans | null; + front?: string | Image | null; + spine?: string | Image | null; + back?: string | Image | null; + insideFront?: string | Image | null; + insideBack?: string | Image | null; + flapFront?: string | Image | null; + flapBack?: string | Image | null; + insideFlapFront?: string | Image | null; + insideFlapBack?: string | Image | null; }; dustjacketEnabled?: boolean | null; dustjacket?: { - front?: string | LibraryItemScans | null; - spine?: string | LibraryItemScans | null; - back?: string | LibraryItemScans | null; - insideFront?: string | LibraryItemScans | null; - insideSpine?: string | LibraryItemScans | null; - insideBack?: string | LibraryItemScans | null; - flapFront?: string | LibraryItemScans | null; - flapBack?: string | LibraryItemScans | null; - insideFlapFront?: string | LibraryItemScans | null; - insideFlapBack?: string | LibraryItemScans | null; + front?: string | Image | null; + spine?: string | Image | null; + back?: string | Image | null; + insideFront?: string | Image | null; + insideSpine?: string | Image | null; + insideBack?: string | Image | null; + flapFront?: string | Image | null; + flapBack?: string | Image | null; + insideFlapFront?: string | Image | null; + insideFlapBack?: string | Image | null; }; obiEnabled?: boolean | null; obi?: { - front?: string | LibraryItemScans | null; - spine?: string | LibraryItemScans | null; - back?: string | LibraryItemScans | null; - insideFront?: string | LibraryItemScans | null; - insideSpine?: string | LibraryItemScans | null; - insideBack?: string | LibraryItemScans | null; - flapFront?: string | LibraryItemScans | null; - flapBack?: string | LibraryItemScans | null; - insideFlapFront?: string | LibraryItemScans | null; - insideFlapBack?: string | LibraryItemScans | null; + front?: string | Image | null; + spine?: string | Image | null; + back?: string | Image | null; + insideFront?: string | Image | null; + insideSpine?: string | Image | null; + insideBack?: string | Image | null; + flapFront?: string | Image | null; + flapBack?: string | Image | null; + insideFlapFront?: string | Image | null; + insideFlapBack?: string | Image | null; }; pages?: | { page: number; - image: string | LibraryItemScans; - id?: string | null; - }[] - | null; - archiveFile?: (string | null) | File; - }; - textual?: { - subtype?: (string | null) | Key; - pageCount?: number | null; - bindingType?: ('Paperback' | 'Hardcover') | null; - pageOrder?: ('LeftToRight' | 'RightToLeft') | null; - }; - audio?: { - audioSubtype?: (string | null) | Key; - tracks?: - | { - title: string; - file: string | File; + image: string | Image; id?: string | null; }[] | null; }; - video?: { - subtype?: (string | null) | Key; - }; - game?: { - demo?: boolean | null; - platform?: (string | null) | Key; - audioLanguages?: (string | Language)[] | null; - subtitleLanguages?: (string | Language)[] | null; - interfacesLanguages?: (string | Language)[] | null; - }; - releaseDate?: string | null; - categories?: (string | Key)[] | null; - sizeEnabled?: boolean | null; - size?: { - width: number; - height: number; - thickness?: number | null; - }; - priceEnabled?: boolean | null; - price?: { - amount: number; - currency: string | Currency; - }; - translations?: - | { - language: string | Language; - description: { - root: { - children: { - type: string; - version: number; - [k: string]: unknown; - }[]; - direction: ('ltr' | 'rtl') | null; - format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; - indent: number; - type: string; - version: number; - }; - [k: string]: unknown; - }; - id?: string | null; - }[] - | null; urls?: | { url: string; id?: string | null; }[] | null; - parentItems?: (string | LibraryItem)[] | null; - subitems?: (string | LibraryItem)[] | null; + releaseDate?: string | null; + priceEnabled?: boolean | null; + price?: { + amount: number; + currency: string | Currency; + }; + sizeEnabled?: boolean | null; + size?: { + width: number; + height: number; + thickness?: number | null; + }; + weightEnabled?: boolean | null; + weight?: { + amount: number; + }; + pageInfoEnabled?: boolean | null; + pageInfo?: { + pageCount: number; + bindingType?: ('Paperback' | 'Hardcover') | null; + pageOrder?: ('Left to right' | 'Right to left') | null; + }; + folders?: (string | Folder)[] | null; + parentItems?: (string | Collectible)[] | null; + subitems?: (string | Collectible)[] | null; contents?: | { - content: string | Page; - pageStart?: number | null; - pageEnd?: number | null; - timeStart?: number | null; - timeEnd?: number | null; - note?: { - root: { - children: { - type: string; - version: number; - [k: string]: unknown; - }[]; - direction: ('ltr' | 'rtl') | null; - format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; - indent: number; - type: string; - version: number; - }; - [k: string]: unknown; - } | null; + content: + | { + relationTo: 'pages'; + value: string | Page; + } + | { + relationTo: 'generic-contents'; + value: string | GenericContent; + }; + range?: + | ( + | { + start: number; + end: number; + id?: string | null; + blockName?: string | null; + blockType: 'pageRange'; + } + | { + start: string; + end: string; + id?: string | null; + blockName?: string | null; + blockType: 'timeRange'; + } + | { + translations?: + | { + language: string | Language; + note: { + root: { + children: { + type: string; + version: number; + [k: string]: unknown; + }[]; + direction: ('ltr' | 'rtl') | null; + format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; + indent: number; + type: string; + version: number; + }; + [k: string]: unknown; + }; + id?: string | null; + }[] + | null; + id?: string | null; + blockName?: string | null; + blockType: 'other'; + } + )[] + | null; id?: string | null; }[] | null; @@ -340,11 +348,10 @@ export interface LibraryItem { } /** * This interface was referenced by `Config`'s JSON-Schema - * via the `definition` "library-items-thumbnails". + * via the `definition` "images". */ -export interface LibraryItemThumbnail { +export interface Image { id: string; - libraryItem?: (string | LibraryItem)[] | null; updatedAt: string; createdAt: string; url?: string | null; @@ -370,48 +377,40 @@ export interface LibraryItemThumbnail { filesize?: number | null; filename?: string | null; }; - square?: { - url?: string | null; - width?: number | null; - height?: number | null; - mimeType?: string | null; - filesize?: number | null; - filename?: string | null; - }; }; } /** * This interface was referenced by `Config`'s JSON-Schema - * via the `definition` "library-items-gallery". + * via the `definition` "tags". */ -export interface LibraryItemGallery { +export interface Tag { id: string; + name?: string | null; + slug: string; + translations: { + language: string | Language; + name: string; + id?: string | null; + }[]; + group: string | TagsGroup; + updatedAt: string; + createdAt: string; +} +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "tags-groups". + */ +export interface TagsGroup { + id: string; + slug: string; + icon?: string | null; + translations: { + language: string | Language; + name: string; + id?: string | null; + }[]; updatedAt: string; createdAt: string; - url?: string | null; - filename?: string | null; - mimeType?: string | null; - filesize?: number | null; - width?: number | null; - height?: number | null; - sizes?: { - thumb?: { - url?: string | null; - width?: number | null; - height?: number | null; - mimeType?: string | null; - filesize?: number | null; - filename?: string | null; - }; - small?: { - url?: string | null; - width?: number | null; - height?: number | null; - mimeType?: string | null; - filesize?: number | null; - filename?: string | null; - }; - }; } /** * This interface was referenced by `Config`'s JSON-Schema @@ -468,86 +467,6 @@ export interface RecordersThumbnail { }; }; } -/** - * This interface was referenced by `Config`'s JSON-Schema - * via the `definition` "library-items-scans". - */ -export interface LibraryItemScans { - id: string; - updatedAt: string; - createdAt: string; - url?: string | null; - filename?: string | null; - mimeType?: string | null; - filesize?: number | null; - width?: number | null; - height?: number | null; - sizes?: { - thumb?: { - url?: string | null; - width?: number | null; - height?: number | null; - mimeType?: string | null; - filesize?: number | null; - filename?: string | null; - }; - og?: { - url?: string | null; - width?: number | null; - height?: number | null; - mimeType?: string | null; - filesize?: number | null; - filename?: string | null; - }; - medium?: { - url?: string | null; - width?: number | null; - height?: number | null; - mimeType?: string | null; - filesize?: number | null; - filename?: string | null; - }; - large?: { - url?: string | null; - width?: number | null; - height?: number | null; - mimeType?: string | null; - filesize?: number | null; - filename?: string | null; - }; - }; -} -/** - * This interface was referenced by `Config`'s JSON-Schema - * via the `definition` "files". - */ -export interface File { - id: string; - filename: string; - type: 'LibraryScans' | 'LibrarySoundtracks' | 'ContentVideo' | 'ContentAudio'; - updatedAt: string; - createdAt: string; -} -/** - * This interface was referenced by `Config`'s JSON-Schema - * via the `definition` "keys". - */ -export interface Key { - id: string; - name: string; - type: - | 'Contents' - | 'LibraryAudio' - | 'LibraryVideo' - | 'LibraryTextual' - | 'LibraryGroup' - | 'Library' - | 'Weapons' - | 'GamePlatforms' - | 'Categories' - | 'Wordings'; - translations?: CategoryTranslations; -} /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "currencies". @@ -561,7 +480,7 @@ export interface Currency { */ export interface Page { id: string; - type: 'Content' | 'Article' | 'Generic'; + type: 'Content' | 'Post' | 'Generic'; slug: string; thumbnail?: string | Image | null; tags?: (string | Tag)[] | null; @@ -608,7 +527,7 @@ export interface Page { id?: string | null; }[]; folders?: (string | Folder)[] | null; - collectibles?: (string | LibraryItem)[] | null; + collectibles?: (string | Collectible)[] | null; updatedBy: string | Recorder; updatedAt: string; createdAt: string; @@ -616,71 +535,16 @@ export interface Page { } /** * This interface was referenced by `Config`'s JSON-Schema - * via the `definition` "images". + * via the `definition` "generic-contents". */ -export interface Image { +export interface GenericContent { id: string; - updatedAt: string; - createdAt: string; - url?: string | null; - filename?: string | null; - mimeType?: string | null; - filesize?: number | null; - width?: number | null; - height?: number | null; - sizes?: { - thumb?: { - url?: string | null; - width?: number | null; - height?: number | null; - mimeType?: string | null; - filesize?: number | null; - filename?: string | null; - }; - og?: { - url?: string | null; - width?: number | null; - height?: number | null; - mimeType?: string | null; - filesize?: number | null; - filename?: string | null; - }; - }; -} -/** - * This interface was referenced by `Config`'s JSON-Schema - * via the `definition` "tags". - */ -export interface Tag { - id: string; - name?: string | null; - slug: string; - translations?: - | { - language: string | Language; - name: string; - id?: string | null; - }[] - | null; - group: string | TagsGroup; - updatedAt: string; - createdAt: string; -} -/** - * This interface was referenced by `Config`'s JSON-Schema - * via the `definition` "tags-groups". - */ -export interface TagsGroup { - id: string; - slug: string; - icon?: string | null; - translations?: - | { - language: string | Language; - name: string; - id?: string | null; - }[] - | null; + name: string; + translations: { + language: string | Language; + name: string; + id?: string | null; + }[]; updatedAt: string; createdAt: string; } @@ -786,10 +650,8 @@ export interface Weapon { id: string; slug: string; thumbnail?: string | WeaponsThumbnail | null; - type: string | Key; group?: (string | null) | WeaponsGroup; appearances: { - categories: (string | Key)[]; translations: { language: string | Language; sourceLanguage: string | Language; @@ -1004,7 +866,7 @@ export interface VideosChannel { export interface Wording { id: string; name: string; - translations?: CategoryTranslations; + translations: CategoryTranslations; updatedAt: string; createdAt: string; } @@ -1047,7 +909,7 @@ export interface PayloadMigration { * via the `definition` "SpacerBlock". */ export interface SpacerBlock { - size: 'Small' | 'Medium' | 'Large' | 'XLarge'; + size: 'Small' | 'Medium' | 'Large' | 'Extra Large'; blockType: 'spacerBlock'; } /** diff --git a/src/utils/endpoints.ts b/src/utils/endpoints.ts new file mode 100644 index 0000000..32ab26d --- /dev/null +++ b/src/utils/endpoints.ts @@ -0,0 +1,28 @@ +import { TagGroup } from "../sdk"; +import { Tag } from "../types/collections"; +import { isPayloadArrayType, isPayloadType } from "./asserts"; + +export const convertTagsToGroups = (tags: (string | Tag)[] | null | undefined): TagGroup[] => { + if (!isPayloadArrayType(tags)) { + return []; + } + + const groups: TagGroup[] = []; + + tags.forEach(({ group, slug }) => { + if (isPayloadType(group)) { + const existingGroup = groups.find((existingGroup) => existingGroup.slug === group.slug); + if (existingGroup) { + existingGroup.values.push(slug); + } else { + groups.push({ + slug: group.slug, + icon: group.icon ?? "material-symbols:category-outline", + values: [slug], + }); + } + } + }); + + return groups; +}; diff --git a/src/utils/localApi.ts b/src/utils/localApi.ts index a00bdfe..7b9293f 100644 --- a/src/utils/localApi.ts +++ b/src/utils/localApi.ts @@ -1,28 +1,8 @@ import payload, { GeneratedTypes } from "payload"; -import { Collections, KeysTypes } from "../constants"; +import { Collections } from "../constants"; import { StrapiImage } from "../types/strapi"; import { isDefined } from "./asserts"; -export const findWeaponType = async (name: string): Promise => { - 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; -}; - -export const findCategory = async (name: string): Promise => { - 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; -}; - export const findRecorder = async (name: string): Promise => { const recorder = await payload.find({ collection: Collections.Recorders, diff --git a/src/utils/tags.ts b/src/utils/tags.ts deleted file mode 100644 index a3f9fe9..0000000 --- a/src/utils/tags.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { TagGroup } from "../sdk"; -import { Tag } from "../types/collections"; -import { isPayloadArrayType, isPayloadType } from "./asserts"; - -export const convertTagsToGroups = (tags: (string | Tag)[] | null | undefined): TagGroup[] => { - if (!isPayloadArrayType(tags)) { - return []; - } - - const groups: TagGroup[] = []; - - tags.forEach(({ group, slug }) => { - if (isPayloadType(group)) { - const existingGroup = groups.find((existingGroup) => existingGroup.slug === group.slug); - if (existingGroup) { - existingGroup.values.push(slug); - } else { - groups.push({ - slug: group.slug, - icon: group.icon ?? "material-symbols:category-outline", - values: [slug], - }); - } - } - }); - - return groups; - }; - \ No newline at end of file