diff --git a/package-lock.json b/package-lock.json index 090127c..71f63d0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "@fontsource/vollkorn": "5.0.18", + "@fontsource/vollkorn": "5.0.19", "@payloadcms/bundler-webpack": "1.0.6", "@payloadcms/db-mongodb": "1.4.3", "@payloadcms/richtext-lexical": "0.7.0", @@ -1953,9 +1953,9 @@ "integrity": "sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A==" }, "node_modules/@fontsource/vollkorn": { - "version": "5.0.18", - "resolved": "https://registry.npmjs.org/@fontsource/vollkorn/-/vollkorn-5.0.18.tgz", - "integrity": "sha512-jdZL0LnKtkHHTDo5YKJ3yiS4XhCuE1EO9IrRJbf/5N07Y3lbDATuaXj6erg8DQltqLhqrb5TTfxYpno2MKpddQ==" + "version": "5.0.19", + "resolved": "https://registry.npmjs.org/@fontsource/vollkorn/-/vollkorn-5.0.19.tgz", + "integrity": "sha512-Wh72GEg5oqR5tiLDtgzs3RoFRHFdGiCWuTpf+p6cDw9P84CWSYucH59XJ2VCWtoSwtgl1FugnT1bkp8eVcSMYw==" }, "node_modules/@hapi/hoek": { "version": "9.3.0", diff --git a/package.json b/package.json index 8a0d125..829ffa5 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "start": "npm install && npm run generate:types && npm run dev" }, "dependencies": { - "@fontsource/vollkorn": "5.0.18", + "@fontsource/vollkorn": "5.0.19", "@payloadcms/bundler-webpack": "1.0.6", "@payloadcms/db-mongodb": "1.4.3", "@payloadcms/richtext-lexical": "0.7.0", diff --git a/src/collections/Collectibles/Collectibles.ts b/src/collections/Collectibles/Collectibles.ts index f52737b..f0d4ba9 100644 --- a/src/collections/Collectibles/Collectibles.ts +++ b/src/collections/Collectibles/Collectibles.ts @@ -27,6 +27,7 @@ const fields = { status: "_status", slug: "slug", thumbnail: "thumbnail", + backgroundImage: "backgroundImage", nature: "nature", tags: "tags", languages: "languages", @@ -198,6 +199,15 @@ export const Collectibles = buildVersionedCollectionConfig({ { label: "Images", fields: [ + imageField({ + name: fields.backgroundImage, + relationTo: Collections.Images, + admin: { + description: + "The image used as background from the webpage.\ + If missing, the thumbnail will be used instead.", + }, + }), { name: fields.gallery, type: "array", diff --git a/src/collections/Collectibles/endpoints/getBySlugEndpoint.ts b/src/collections/Collectibles/endpoints/getBySlugEndpoint.ts index 3d87cc8..2ab37b4 100644 --- a/src/collections/Collectibles/endpoints/getBySlugEndpoint.ts +++ b/src/collections/Collectibles/endpoints/getBySlugEndpoint.ts @@ -1,30 +1,138 @@ import { CollectibleNature, CollectionStatus, Collections } from "../../../constants"; import { createGetByEndpoint } from "../../../endpoints/createGetByEndpoint"; -import { EndpointCollectible, EndpointCollectiblePreview } from "../../../sdk"; +import { EndpointCollectible, EndpointCollectiblePreview, PayloadImage } from "../../../sdk"; import { Collectible } from "../../../types/collections"; -import { isPayloadArrayType, isPayloadType, isValidPayloadImage } from "../../../utils/asserts"; -import { convertTagsToGroups } from "../../../utils/endpoints"; +import { + isDefined, + isPayloadArrayType, + isPayloadType, + isValidPayloadImage, +} from "../../../utils/asserts"; +import { convertTagsToGroups, handleParentPages } from "../../../utils/endpoints"; +import { convertPageToPreview } from "../../Pages/endpoints/getBySlugEndpoint"; export const getBySlugEndpoint = createGetByEndpoint( Collections.Collectibles, "slug", (collectible: Collectible): EndpointCollectible => { - const { nature, urls, subitems, gallery, contents } = collectible; + const { + nature, + urls, + subitems, + gallery, + contents, + priceEnabled, + price, + size, + sizeEnabled, + weight, + weightEnabled, + pageInfo, + pageInfoEnabled, + parentItems, + folders, + backgroundImage, + } = collectible; + return { ...convertCollectibleToPreview(collectible), + ...(isValidPayloadImage(backgroundImage) ? { backgroundImage } : {}), contents: handleContents(contents), - gallery: - gallery - ?.map(({ image }) => image) - .flatMap((image) => (isValidPayloadImage(image) ? [image] : [])) ?? [], + gallery: handleGallery(gallery), + scans: handleScans(collectible.scans), nature: nature === "Physical" ? CollectibleNature.Physical : CollectibleNature.Digital, - parentPages: [], // TODO: todo + parentPages: handleParentPages({collectibles: parentItems, folders}), subitems: isPayloadArrayType(subitems) ? subitems.map(convertCollectibleToPreview) : [], urls: urls?.map(({ url }) => ({ url, label: getLabelFromUrl(url) })) ?? [], + ...(weightEnabled && weight ? { weight: weight.amount } : {}), + ...handleSize(size, sizeEnabled), + ...handlePageInfo(pageInfo, pageInfoEnabled), + ...handlePrice(price, priceEnabled), }; - } + }, + 3 ); +export const handlePrice = ( + price: Collectible["price"], + enabled: Collectible["priceEnabled"] +): { price: NonNullable } | {} => { + if (!price || !enabled || !isPayloadType(price.currency)) return {}; + return { + price: { amount: price.amount, currency: price.currency.id }, + }; +}; + +export const handleSize = ( + size: Collectible["size"], + enabled: Collectible["sizeEnabled"] +): { size: NonNullable } | {} => { + if (!size || !enabled) return {}; + return { + size: { + width: size.width, + height: size.height, + ...(isDefined(size.thickness) ? { thickness: size.thickness } : {}), + }, + }; +}; + +export const handlePageInfo = ( + pageInfo: Collectible["pageInfo"], + enabled: Collectible["pageInfoEnabled"] +): { pageInfo: NonNullable } | {} => { + if (!pageInfo || !enabled) return {}; + return { + pageInfo: { + pageCount: pageInfo.pageCount, + ...(isDefined(pageInfo.bindingType) ? { bindingType: pageInfo.bindingType } : {}), + ...(isDefined(pageInfo.pageOrder) ? { pageOrder: pageInfo.pageOrder } : {}), + }, + }; +}; + +export const handleGallery = (gallery: Collectible["gallery"]): EndpointCollectible["gallery"] => { + const result: PayloadImage[] = []; + if (!gallery) return result; + + gallery?.forEach(({ image }) => { + if (isValidPayloadImage(image)) { + result.push(image); + } + }); + + return result.slice(0, 10); +}; + +export const handleScans = (scans: Collectible["scans"]): EndpointCollectible["scans"] => { + const result: PayloadImage[] = []; + if (!scans) return result; + + scans.pages?.forEach(({ image }) => { + if (isValidPayloadImage(image)) { + result.push(image); + } + }); + + if (isValidPayloadImage(scans.cover?.front)) { + result.push(scans.cover.front); + } + + if (isValidPayloadImage(scans.cover?.back)) { + result.push(scans.cover.back); + } + + if (isValidPayloadImage(scans.dustjacket?.front)) { + result.push(scans.dustjacket.front); + } + + if (isValidPayloadImage(scans.dustjacket?.back)) { + result.push(scans.dustjacket.back); + } + + return result.slice(0, 10); +}; + export const handleContents = ( contents: Collectible["contents"] ): EndpointCollectible["contents"] => { @@ -65,12 +173,20 @@ export const handleContents = ( switch (content.relationTo) { case "generic-contents": return isPayloadType(content.value) - ? { relationTo: "generic-contents", value: content.value } + ? { + relationTo: "generic-contents", + value: { + translations: content.value.translations.map(({ language, name }) => ({ + language: isPayloadType(language) ? language.id : language, + name, + })), + }, + } : undefined; case "pages": return isPayloadType(content.value) - ? { relationTo: "pages", value: content.value } + ? { relationTo: "pages", value: convertPageToPreview(content.value) } : undefined; default: diff --git a/src/collections/Folders/endpoints/getBySlugEndpoint.ts b/src/collections/Folders/endpoints/getBySlugEndpoint.ts index d2746af..936cf61 100644 --- a/src/collections/Folders/endpoints/getBySlugEndpoint.ts +++ b/src/collections/Folders/endpoints/getBySlugEndpoint.ts @@ -3,6 +3,8 @@ import { createGetByEndpoint } from "../../../endpoints/createGetByEndpoint"; import { EndpointFolder, EndpointFolderPreview } from "../../../sdk"; import { Folder, Language } from "../../../types/collections"; import { isDefined, isPayloadType, isValidPayloadImage } from "../../../utils/asserts"; +import { convertCollectibleToPreview } from "../../Collectibles/endpoints/getBySlugEndpoint"; +import { convertPageToPreview } from "../../Pages/endpoints/getBySlugEndpoint"; export const getBySlugEndpoint = createGetByEndpoint( Collections.Folders, @@ -36,9 +38,9 @@ export const getBySlugEndpoint = createGetByEndpoint( } switch (relationTo) { case "collectibles": - return [{ relationTo, value }]; + return [{ relationTo, value: convertCollectibleToPreview(value) }]; case "pages": - return [{ relationTo, value }]; + return [{ relationTo, value: convertPageToPreview(value) }]; } }) ?? [], }; diff --git a/src/collections/Pages/Pages.ts b/src/collections/Pages/Pages.ts index f40525e..0369bd4 100644 --- a/src/collections/Pages/Pages.ts +++ b/src/collections/Pages/Pages.ts @@ -23,6 +23,7 @@ const fields = { type: "type", authors: "authors", thumbnail: "thumbnail", + backgroundImage: "backgroundImage", translations: "translations", tags: "tags", sourceLanguage: "sourceLanguage", @@ -71,22 +72,33 @@ export const Pages = buildVersionedCollectionConfig({ }, endpoints: [getBySlugEndpoint], fields: [ - { - name: fields.type, - type: "radio", - required: true, - defaultValue: PageType.Generic, - options: Object.entries(PageType).map(([_, value]) => ({ - label: value, - value: value, - })), - }, rowField([ slugField({ name: fields.slug }), + { + name: fields.type, + type: "radio", + required: true, + defaultValue: PageType.Generic, + options: Object.entries(PageType).map(([_, value]) => ({ + label: value, + value: value, + })), + }, + ]), + rowField([ imageField({ name: fields.thumbnail, relationTo: Collections.Images, }), + imageField({ + name: fields.backgroundImage, + relationTo: Collections.Images, + admin: { + description: + "The image used as background from the webpage.\ + If missing, the thumbnail will be used instead.", + }, + }), ]), rowField([ tagsField({ name: fields.tags }), diff --git a/src/collections/Pages/endpoints/getBySlugEndpoint.ts b/src/collections/Pages/endpoints/getBySlugEndpoint.ts index fdad64a..5e8c5ee 100644 --- a/src/collections/Pages/endpoints/getBySlugEndpoint.ts +++ b/src/collections/Pages/endpoints/getBySlugEndpoint.ts @@ -6,59 +6,49 @@ import { isNodeBlockNode, } from "../../../constants"; import { createGetByEndpoint } from "../../../endpoints/createGetByEndpoint"; -import { EndpointPage, ParentPage, TableOfContentEntry } from "../../../sdk"; +import { EndpointPage, EndpointPagePreview, TableOfContentEntry } from "../../../sdk"; import { Page } from "../../../types/collections"; import { isPayloadArrayType, isPayloadType, isValidPayloadImage } from "../../../utils/asserts"; -import { convertTagsToGroups } from "../../../utils/endpoints"; +import { convertTagsToGroups, handleParentPages } from "../../../utils/endpoints"; export const getBySlugEndpoint = createGetByEndpoint( Collections.Pages, "slug", - ({ - authors, - slug, - translations, - tags, - thumbnail, - _status, - collectibles, - folders, - type, - }: Page): EndpointPage => ({ - slug, - type: type as PageType, - ...(isValidPayloadImage(thumbnail) ? { thumbnail } : {}), - tagGroups: convertTagsToGroups(tags), - translations: translations.map( - ({ - content, - language, - sourceLanguage, - title, - pretitle, - subtitle, - proofreaders, - summary, - transcribers, - translators, - }) => ({ - language: isPayloadType(language) ? language.id : language, - sourceLanguage: isPayloadType(sourceLanguage) ? sourceLanguage.id : sourceLanguage, - ...(pretitle ? { pretitle } : {}), - title, - ...(subtitle ? { subtitle } : {}), - ...(summary ? { summary } : {}), - content: handleContent(content), - toc: handleToc(content), - translators: isPayloadArrayType(translators) ? translators.map(({ id }) => id) : [], - transcribers: isPayloadArrayType(transcribers) ? transcribers.map(({ id }) => id) : [], - proofreaders: isPayloadArrayType(proofreaders) ? proofreaders.map(({ id }) => id) : [], - }) - ), - authors: isPayloadArrayType(authors) ? authors.map(({ id }) => id) : [], - status: _status === "published" ? "published" : "draft", - parentPages: handleParentPages({ collectibles, folders }), - }) + (page: Page): EndpointPage => { + const { translations, collectibles, folders, backgroundImage } = page; + + return { + ...convertPageToPreview(page), + ...(isValidPayloadImage(backgroundImage) ? { backgroundImage } : {}), + translations: translations.map( + ({ + content, + language, + sourceLanguage, + title, + pretitle, + subtitle, + proofreaders, + summary, + transcribers, + translators, + }) => ({ + language: isPayloadType(language) ? language.id : language, + sourceLanguage: isPayloadType(sourceLanguage) ? sourceLanguage.id : sourceLanguage, + ...(pretitle ? { pretitle } : {}), + title, + ...(subtitle ? { subtitle } : {}), + ...(summary ? { summary } : {}), + content: handleContent(content), + toc: handleToc(content), + translators: isPayloadArrayType(translators) ? translators.map(({ id }) => id) : [], + transcribers: isPayloadArrayType(transcribers) ? transcribers.map(({ id }) => id) : [], + proofreaders: isPayloadArrayType(proofreaders) ? proofreaders.map(({ id }) => id) : [], + }) + ), + parentPages: handleParentPages({ collectibles, folders }), + }; + } ); const handleContent = ( @@ -98,40 +88,27 @@ const handleToc = (content: RichTextContent, parentPrefix = ""): TableOfContentE children: handleToc(fields.content, `${index + 1}.`), })); -const handleParentPages = ({ - collectibles, - folders, -}: Pick): ParentPage[] => { - const result: ParentPage[] = []; - if (collectibles && isPayloadArrayType(collectibles)) { - collectibles.forEach(({ slug, translations }) => { - result.push({ - collection: Collections.Collectibles, - slug, - translations: translations.map(({ language, title }) => ({ - language: isPayloadType(language) ? language.id : language, - name: title, // TODO: Use the entire pretitle + title + subtitle - })), - tag: "collectible", - }); - }); - } - if (folders && isPayloadArrayType(folders)) { - folders.forEach(({ slug, translations }) => { - result.push({ - collection: Collections.Folders, - slug, - translations: - translations?.map(({ language, name }) => ({ - language: isPayloadType(language) ? language.id : language, - name, - })) ?? [], - tag: "folders", - }); - }); - } - - return result; -}; +export const convertPageToPreview = ({ + authors, + slug, + translations, + tags, + thumbnail, + _status, + type, +}: Page): EndpointPagePreview => ({ + slug, + type: type as PageType, + ...(isValidPayloadImage(thumbnail) ? { thumbnail } : {}), + tagGroups: convertTagsToGroups(tags), + translations: translations.map(({ language, title, pretitle, subtitle }) => ({ + language: isPayloadType(language) ? language.id : language, + ...(pretitle ? { pretitle } : {}), + title, + ...(subtitle ? { subtitle } : {}), + })), + authors: isPayloadArrayType(authors) ? authors.map(({ id }) => id) : [], + status: _status === "published" ? "published" : "draft", +}); diff --git a/src/endpoints/createGetByEndpoint.ts b/src/endpoints/createGetByEndpoint.ts index 3299881..d2cc941 100644 --- a/src/endpoints/createGetByEndpoint.ts +++ b/src/endpoints/createGetByEndpoint.ts @@ -4,7 +4,8 @@ import { CollectionEndpoint } from "../types/payload"; export const createGetByEndpoint = ( collection: C, attribute: string, - handler: (doc: GeneratedTypes["collections"][C]) => Promise | R + handler: (doc: GeneratedTypes["collections"][C]) => Promise | R, + depth?: number, ): CollectionEndpoint => ({ path: `/${attribute}/:${attribute}`, method: "get", @@ -21,6 +22,7 @@ export const createGetByEndpoint = { @@ -26,3 +27,45 @@ export const convertTagsToGroups = (tags: (string | Tag)[] | null | undefined): return groups; }; + + +export const handleParentPages = ({ + collectibles, + folders, +}: { + collectibles?: (string | Collectible)[] | null | undefined; + folders?: (string | Folder)[] | null | undefined +}): ParentPage[] => { + const result: ParentPage[] = []; + + if (collectibles && isPayloadArrayType(collectibles)) { + collectibles.forEach(({ slug, translations }) => { + result.push({ + collection: Collections.Collectibles, + slug, + translations: translations.map(({ language, title }) => ({ + language: isPayloadType(language) ? language.id : language, + name: title, // TODO: Use the entire pretitle + title + subtitle + })), + tag: "collectible", + }); + }); + } + + if (folders && isPayloadArrayType(folders)) { + folders.forEach(({ slug, translations }) => { + result.push({ + collection: Collections.Folders, + slug, + translations: + translations?.map(({ language, name }) => ({ + language: isPayloadType(language) ? language.id : language, + name, + })) ?? [], + tag: "folders", + }); + }); + } + + return result; +}; \ No newline at end of file