Use attributes instead of group tags

This commit is contained in:
DrMint 2024-05-15 13:44:50 +02:00
parent c6165c251c
commit 46613c8500
24 changed files with 363 additions and 149 deletions

View File

@ -0,0 +1,25 @@
import { Block } from "payload/types";
import { AttributeTypes, Collections } from "../../constants";
import { rowField } from "../../fields/rowField/rowField";
export const numberBlock: Block = {
slug: "numberBlock",
interfaceName: "NumberBlock",
labels: { singular: "Number attribute", plural: "Number attributes" },
fields: [
rowField([
{
name: "name",
type: "relationship",
relationTo: Collections.Attributes,
filterOptions: () => ({ type: { equals: AttributeTypes.Number } }),
required: true,
},
{
name: "number",
type: "number",
required: true,
},
]),
],
};

View File

@ -0,0 +1,28 @@
import { Block } from "payload/types";
import { AttributeTypes, Collections } from "../../constants";
import { rowField } from "../../fields/rowField/rowField";
export const tagsBlock: Block = {
slug: "tagsBlock",
interfaceName: "TagsBlock",
labels: { singular: "Tags attribute", plural: "Tags attributes" },
fields: [
rowField([
{
name: "name",
type: "relationship",
relationTo: Collections.Attributes,
filterOptions: () => ({ type: { equals: AttributeTypes.Tags } }),
required: true,
},
{
name: "tags",
type: "relationship",
relationTo: Collections.Tags,
required: true,
hasMany: true,
minRows: 1,
},
]),
],
};

View File

@ -0,0 +1,25 @@
import { Block } from "payload/types";
import { AttributeTypes, Collections } from "../../constants";
import { rowField } from "../../fields/rowField/rowField";
export const textBlock: Block = {
slug: "textBlock",
interfaceName: "TextBlock",
labels: { singular: "Text attribute", plural: "Text attributes" },
fields: [
rowField([
{
name: "name",
type: "relationship",
relationTo: Collections.Attributes,
filterOptions: () => ({ type: { equals: AttributeTypes.Text } }),
required: true,
},
{
name: "text",
type: "textarea",
required: true,
},
]),
],
};

View File

@ -0,0 +1,51 @@
import { CollectionConfig } from "payload/types";
import { mustBeAdmin } from "../../accesses/fields/mustBeAdmin";
import { AttributeTypes, CollectionGroups, Collections } from "../../constants";
import { iconField } from "../../fields/iconField/iconField";
import { rowField } from "../../fields/rowField/rowField";
import { slugField } from "../../fields/slugField/slugField";
import { translatedFields } from "../../fields/translatedFields/translatedFields";
import { buildCollectionConfig } from "../../utils/collectionConfig";
const fields = {
slug: "slug",
translations: "translations",
translationsName: "name",
icon: "icon",
type: "type",
};
export const Attributes: CollectionConfig = buildCollectionConfig({
slug: Collections.Attributes,
labels: { singular: "Attribute", plural: "Attributes" },
admin: {
group: CollectionGroups.Meta,
useAsTitle: fields.slug,
defaultColumns: [fields.slug, fields.icon, fields.type, fields.translations],
},
fields: [
rowField([
slugField({ name: fields.slug }),
iconField({ name: fields.icon }),
{
name: fields.type,
type: "select",
access: {
update: mustBeAdmin,
},
required: true,
options: Object.values(AttributeTypes).map((value) => ({
label: value,
value: value,
})),
},
]),
translatedFields({
name: fields.translations,
admin: { useAsTitle: fields.translationsName },
required: true,
minRows: 1,
fields: [{ name: fields.translationsName, type: "text", required: true }],
}),
],
});

View File

@ -1,4 +1,5 @@
import { CollectionGroups, Collections } from "../../constants"; import { CollectionGroups, Collections } from "../../constants";
import { attributesField } from "../../fields/attributesField/attributesField";
import { creditsField } from "../../fields/creditsField/creditsField"; import { creditsField } from "../../fields/creditsField/creditsField";
import { imageField } from "../../fields/imageField/imageField"; import { imageField } from "../../fields/imageField/imageField";
import { rowField } from "../../fields/rowField/rowField"; import { rowField } from "../../fields/rowField/rowField";
@ -21,6 +22,7 @@ const fields = {
thumbnail: "thumbnail", thumbnail: "thumbnail",
duration: "duration", duration: "duration",
tags: "tags", tags: "tags",
attributes: "attributes",
credits: "credits", credits: "credits",
}; };
@ -72,6 +74,7 @@ export const Audios = buildCollectionConfig({
], ],
}), }),
tagsField({ name: fields.tags }), tagsField({ name: fields.tags }),
attributesField({ name: fields.attributes }),
creditsField({ name: fields.credits }), creditsField({ name: fields.credits }),
], ],
}); });

View File

@ -5,9 +5,9 @@ import { Audio } from "../../../types/collections";
import { CollectionEndpoint } from "../../../types/payload"; import { CollectionEndpoint } from "../../../types/payload";
import { isNotEmpty, isValidPayloadImage, isValidPayloadMedia } from "../../../utils/asserts"; import { isNotEmpty, isValidPayloadImage, isValidPayloadMedia } from "../../../utils/asserts";
import { import {
convertAttributesToEndpointAttributes,
convertCreditsToEndpointCredits, convertCreditsToEndpointCredits,
convertRTCToEndpointRTC, convertRTCToEndpointRTC,
convertTagsEndpointTagsGroups,
getLanguageId, getLanguageId,
} from "../../../utils/endpoints"; } from "../../../utils/endpoints";
@ -48,7 +48,7 @@ export const getByID: CollectionEndpoint = {
export const convertAudioToEndpointAudio = ({ export const convertAudioToEndpointAudio = ({
url, url,
tags, attributes,
translations, translations,
mimeType, mimeType,
createdAt, createdAt,
@ -61,7 +61,7 @@ export const convertAudioToEndpointAudio = ({
credits, credits,
}: Audio & PayloadMedia): EndpointAudio => ({ }: Audio & PayloadMedia): EndpointAudio => ({
url, url,
tagGroups: convertTagsEndpointTagsGroups(tags), attributes: convertAttributesToEndpointAttributes(attributes),
createdAt, createdAt,
filename, filename,
filesize, filesize,

View File

@ -7,6 +7,7 @@ import {
CollectionGroups, CollectionGroups,
Collections, Collections,
} from "../../constants"; } from "../../constants";
import { attributesField } from "../../fields/attributesField/attributesField";
import { backPropagationField } from "../../fields/backPropagationField/backPropagationField"; import { backPropagationField } from "../../fields/backPropagationField/backPropagationField";
import { componentField } from "../../fields/componentField/componentField"; import { componentField } from "../../fields/componentField/componentField";
import { creditsField } from "../../fields/creditsField/creditsField"; import { creditsField } from "../../fields/creditsField/creditsField";
@ -34,6 +35,7 @@ const fields = {
backgroundImage: "backgroundImage", backgroundImage: "backgroundImage",
nature: "nature", nature: "nature",
tags: "tags", tags: "tags",
attributes: "attributes",
languages: "languages", languages: "languages",
translations: "translations", translations: "translations",
@ -189,6 +191,7 @@ export const Collectibles = buildVersionedCollectionConfig({
]), ]),
tagsField({ name: fields.tags }), tagsField({ name: fields.tags }),
attributesField({ name: fields.attributes }),
translatedFields({ translatedFields({
name: fields.translations, name: fields.translations,

View File

@ -12,8 +12,8 @@ import {
isValidPayloadMedia, isValidPayloadMedia,
} from "../../../utils/asserts"; } from "../../../utils/asserts";
import { import {
convertAttributesToEndpointAttributes,
convertSourceToEndpointSource, convertSourceToEndpointSource,
convertTagsEndpointTagsGroups,
getDomainFromUrl, getDomainFromUrl,
} from "../../../utils/endpoints"; } from "../../../utils/endpoints";
import { convertAudioToEndpointAudio } from "../../Audios/endpoints/getByID"; import { convertAudioToEndpointAudio } from "../../Audios/endpoints/getByID";
@ -52,7 +52,7 @@ export const convertCollectibleToEndpointCollectible = ({
releaseDate, releaseDate,
languages, languages,
scans: rawScans, scans: rawScans,
tags, attributes,
createdAt, createdAt,
updatedAt, updatedAt,
scansEnabled, scansEnabled,
@ -72,7 +72,7 @@ export const convertCollectibleToEndpointCollectible = ({
...(isValidPayloadImage(backgroundImage) ...(isValidPayloadImage(backgroundImage)
? { backgroundImage: convertImageToEndpointImage(backgroundImage) } ? { backgroundImage: convertImageToEndpointImage(backgroundImage) }
: {}), : {}),
tagGroups: convertTagsEndpointTagsGroups(tags), attributes: convertAttributesToEndpointAttributes(attributes),
translations: translations:
translations?.map(({ language, title, description, pretitle, subtitle }) => ({ translations?.map(({ language, title, description, pretitle, subtitle }) => ({
language: isPayloadType(language) ? language.id : language, language: isPayloadType(language) ? language.id : language,

View File

@ -1,4 +1,5 @@
import { Collections } from "../../constants"; import { Collections } from "../../constants";
import { attributesField } from "../../fields/attributesField/attributesField";
import { creditsField } from "../../fields/creditsField/creditsField"; import { creditsField } from "../../fields/creditsField/creditsField";
import { rowField } from "../../fields/rowField/rowField"; import { rowField } from "../../fields/rowField/rowField";
import { tagsField } from "../../fields/tagsField/tagsField"; import { tagsField } from "../../fields/tagsField/tagsField";
@ -19,6 +20,7 @@ const fields = {
translationsSubtitle: "subtitle", translationsSubtitle: "subtitle",
translationsDescription: "description", translationsDescription: "description",
tags: "tags", tags: "tags",
attributes: "attributes",
credits: "credits", credits: "credits",
} as const satisfies Record<string, string>; } as const satisfies Record<string, string>;
@ -64,6 +66,7 @@ export const Images = buildImageCollectionConfig({
], ],
}), }),
tagsField({ name: fields.tags }), tagsField({ name: fields.tags }),
attributesField({ name: fields.attributes }),
creditsField({ name: fields.credits }), creditsField({ name: fields.credits }),
], ],
}); });

View File

@ -5,9 +5,9 @@ import { Image } from "../../../types/collections";
import { CollectionEndpoint } from "../../../types/payload"; import { CollectionEndpoint } from "../../../types/payload";
import { isNotEmpty, isValidPayloadImage } from "../../../utils/asserts"; import { isNotEmpty, isValidPayloadImage } from "../../../utils/asserts";
import { import {
convertAttributesToEndpointAttributes,
convertCreditsToEndpointCredits, convertCreditsToEndpointCredits,
convertRTCToEndpointRTC, convertRTCToEndpointRTC,
convertTagsEndpointTagsGroups,
getLanguageId, getLanguageId,
} from "../../../utils/endpoints"; } from "../../../utils/endpoints";
@ -50,7 +50,7 @@ export const convertImageToEndpointImage = ({
url, url,
width, width,
height, height,
tags, attributes,
translations, translations,
mimeType, mimeType,
createdAt, createdAt,
@ -63,7 +63,7 @@ export const convertImageToEndpointImage = ({
url, url,
width, width,
height, height,
tagGroups: convertTagsEndpointTagsGroups(tags), attributes: convertAttributesToEndpointAttributes(attributes),
createdAt, createdAt,
filename, filename,
filesize, filesize,

View File

@ -4,6 +4,7 @@ import { sectionBlock } from "../../blocks/sectionBlock";
import { transcriptBlock } from "../../blocks/transcriptBlock"; import { transcriptBlock } from "../../blocks/transcriptBlock";
import { QuickFilters, publishStatusFilters } from "../../components/QuickFilters"; import { QuickFilters, publishStatusFilters } from "../../components/QuickFilters";
import { CollectionGroups, Collections } from "../../constants"; import { CollectionGroups, Collections } from "../../constants";
import { attributesField } from "../../fields/attributesField/attributesField";
import { backPropagationField } from "../../fields/backPropagationField/backPropagationField"; import { backPropagationField } from "../../fields/backPropagationField/backPropagationField";
import { creditsField } from "../../fields/creditsField/creditsField"; import { creditsField } from "../../fields/creditsField/creditsField";
import { imageField } from "../../fields/imageField/imageField"; import { imageField } from "../../fields/imageField/imageField";
@ -24,6 +25,7 @@ const fields = {
backgroundImage: "backgroundImage", backgroundImage: "backgroundImage",
translations: "translations", translations: "translations",
tags: "tags", tags: "tags",
attributes: "attributes",
sourceLanguage: "sourceLanguage", sourceLanguage: "sourceLanguage",
pretitle: "pretitle", pretitle: "pretitle",
title: "title", title: "title",
@ -89,6 +91,7 @@ export const Pages = buildVersionedCollectionConfig({
}), }),
]), ]),
tagsField({ name: fields.tags }), tagsField({ name: fields.tags }),
attributesField({ name: fields.attributes }),
translatedFields({ translatedFields({
name: fields.translations, name: fields.translations,
admin: { useAsTitle: fields.title, hasSourceLanguage: true }, admin: { useAsTitle: fields.title, hasSourceLanguage: true },

View File

@ -11,10 +11,10 @@ import { EndpointPage, TableOfContentEntry } from "../../../sdk";
import { Page } from "../../../types/collections"; import { Page } from "../../../types/collections";
import { isNotEmpty, isPayloadType, isValidPayloadImage } from "../../../utils/asserts"; import { isNotEmpty, isPayloadType, isValidPayloadImage } from "../../../utils/asserts";
import { import {
convertAttributesToEndpointAttributes,
convertCreditsToEndpointCredits, convertCreditsToEndpointCredits,
convertRTCToEndpointRTC, convertRTCToEndpointRTC,
convertSourceToEndpointSource, convertSourceToEndpointSource,
convertTagsEndpointTagsGroups,
} from "../../../utils/endpoints"; } from "../../../utils/endpoints";
import { convertImageToEndpointImage } from "../../Images/endpoints/getByID"; import { convertImageToEndpointImage } from "../../Images/endpoints/getByID";
import { convertRecorderToEndpointRecorder } from "../../Recorders/endpoints/getByUsername"; import { convertRecorderToEndpointRecorder } from "../../Recorders/endpoints/getByUsername";
@ -31,7 +31,7 @@ export const convertPageToEndpointPage = ({
folders, folders,
backgroundImage, backgroundImage,
slug, slug,
tags, attributes,
thumbnail, thumbnail,
createdAt, createdAt,
updatedAt, updatedAt,
@ -42,7 +42,7 @@ export const convertPageToEndpointPage = ({
...(isValidPayloadImage(backgroundImage) ...(isValidPayloadImage(backgroundImage)
? { backgroundImage: convertImageToEndpointImage(backgroundImage) } ? { backgroundImage: convertImageToEndpointImage(backgroundImage) }
: {}), : {}),
tagGroups: convertTagsEndpointTagsGroups(tags), attributes: convertAttributesToEndpointAttributes(attributes),
translations: translations.map( translations: translations.map(
({ content, language, sourceLanguage, title, pretitle, subtitle, summary, credits }) => ({ ({ content, language, sourceLanguage, title, pretitle, subtitle, summary, credits }) => ({
language: isPayloadType(language) ? language.id : language, language: isPayloadType(language) ? language.id : language,

View File

@ -1,36 +1,16 @@
import payload from "payload"; import { CollectionConfig } from "payload/types";
import { CollectionBeforeChangeHook, CollectionConfig } from "payload/types";
import { CollectionGroups, Collections } from "../../constants"; import { CollectionGroups, Collections } from "../../constants";
import { rowField } from "../../fields/rowField/rowField";
import { slugField } from "../../fields/slugField/slugField"; import { slugField } from "../../fields/slugField/slugField";
import { translatedFields } from "../../fields/translatedFields/translatedFields"; import { translatedFields } from "../../fields/translatedFields/translatedFields";
import { beforeDuplicateAddCopyTo } from "../../hooks/beforeDuplicateAddCopyTo"; import { beforeDuplicateAddCopyTo } from "../../hooks/beforeDuplicateAddCopyTo";
import { buildCollectionConfig } from "../../utils/collectionConfig"; import { buildCollectionConfig } from "../../utils/collectionConfig";
const beforeChangeUpdateName: CollectionBeforeChangeHook = async ({ data }) => {
let name = data.slug;
const parentId = data[fields.group];
if (parentId) {
const parent = await payload.findByID({
collection: Collections.TagsGroups,
id: data[fields.group],
});
name = `${parent.slug} / ${data.slug}`;
}
return {
...data,
[fields.name]: name,
};
};
const fields = { const fields = {
slug: "slug", slug: "slug",
name: "name",
translations: "translations", translations: "translations",
translationsName: "name", translationsName: "name",
group: "group", page: "page",
}; };
export const Tags: CollectionConfig = buildCollectionConfig({ export const Tags: CollectionConfig = buildCollectionConfig({
@ -38,16 +18,27 @@ export const Tags: CollectionConfig = buildCollectionConfig({
labels: { singular: "Tag", plural: "Tags" }, labels: { singular: "Tag", plural: "Tags" },
admin: { admin: {
group: CollectionGroups.Meta, group: CollectionGroups.Meta,
useAsTitle: fields.name, useAsTitle: fields.slug,
defaultColumns: [fields.slug, fields.group, fields.translations], defaultColumns: [fields.slug, fields.translations],
hooks: { hooks: {
beforeDuplicate: beforeDuplicateAddCopyTo(fields.slug), beforeDuplicate: beforeDuplicateAddCopyTo(fields.slug),
}, },
}, },
hooks: { beforeChange: [beforeChangeUpdateName] },
fields: [ fields: [
{ name: fields.name, type: "text", admin: { readOnly: true, hidden: true } }, rowField([
slugField({ name: fields.slug }), slugField({ name: fields.slug }),
{
name: fields.page,
type: "relationship",
relationTo: Collections.Pages,
admin: {
description:
"You can declare a 'definition' page where more information of the tag will be presented.\
The selected page will then feature a new section\
where elements tagged with this tag will be listed.",
},
},
]),
translatedFields({ translatedFields({
name: fields.translations, name: fields.translations,
admin: { useAsTitle: fields.translationsName }, admin: { useAsTitle: fields.translationsName },
@ -55,11 +46,5 @@ export const Tags: CollectionConfig = buildCollectionConfig({
minRows: 1, minRows: 1,
fields: [{ name: fields.translationsName, type: "text", required: true }], fields: [{ name: fields.translationsName, type: "text", required: true }],
}), }),
{
name: fields.group,
type: "relationship",
required: true,
relationTo: Collections.TagsGroups,
},
], ],
}); });

View File

@ -1,34 +0,0 @@
import { CollectionConfig } from "payload/types";
import { CollectionGroups, Collections } from "../../constants";
import { iconField } from "../../fields/iconField/iconField";
import { slugField } from "../../fields/slugField/slugField";
import { translatedFields } from "../../fields/translatedFields/translatedFields";
import { buildCollectionConfig } from "../../utils/collectionConfig";
const fields = {
slug: "slug",
translations: "translations",
translationsName: "name",
icon: "icon",
};
export const TagsGroups: CollectionConfig = buildCollectionConfig({
slug: Collections.TagsGroups,
labels: { singular: "Tags Group", plural: "Tags Groups" },
admin: {
group: CollectionGroups.Meta,
useAsTitle: fields.slug,
defaultColumns: [fields.slug, fields.translations],
},
fields: [
slugField({ name: fields.slug }),
iconField({ name: fields.icon }),
translatedFields({
name: fields.translations,
admin: { useAsTitle: fields.translationsName },
required: true,
minRows: 1,
fields: [{ name: fields.translationsName, type: "text", required: true }],
}),
],
});

View File

@ -1,4 +1,5 @@
import { CollectionGroups, Collections } from "../../constants"; import { CollectionGroups, Collections } from "../../constants";
import { attributesField } from "../../fields/attributesField/attributesField";
import { componentField } from "../../fields/componentField/componentField"; import { componentField } from "../../fields/componentField/componentField";
import { creditsField } from "../../fields/creditsField/creditsField"; import { creditsField } from "../../fields/creditsField/creditsField";
import { imageField } from "../../fields/imageField/imageField"; import { imageField } from "../../fields/imageField/imageField";
@ -23,6 +24,7 @@ const fields = {
thumbnail: "thumbnail", thumbnail: "thumbnail",
duration: "duration", duration: "duration",
tags: "tags", tags: "tags",
attributes: "attributes",
platform: "platform", platform: "platform",
platformChannel: "channel", platformChannel: "channel",
platformViews: "views", platformViews: "views",
@ -87,6 +89,7 @@ export const Videos = buildCollectionConfig({
], ],
}), }),
tagsField({ name: fields.tags }), tagsField({ name: fields.tags }),
attributesField({ name: fields.attributes }),
creditsField({ name: fields.credits }), creditsField({ name: fields.credits }),
componentField({ componentField({
name: fields.platform, name: fields.platform,

View File

@ -13,9 +13,9 @@ import {
isValidPayloadMedia, isValidPayloadMedia,
} from "../../../utils/asserts"; } from "../../../utils/asserts";
import { import {
convertAttributesToEndpointAttributes,
convertCreditsToEndpointCredits, convertCreditsToEndpointCredits,
convertRTCToEndpointRTC, convertRTCToEndpointRTC,
convertTagsEndpointTagsGroups,
getLanguageId, getLanguageId,
} from "../../../utils/endpoints"; } from "../../../utils/endpoints";
@ -56,7 +56,7 @@ export const getByID: CollectionEndpoint = {
export const convertVideoToEndpointVideo = ({ export const convertVideoToEndpointVideo = ({
url, url,
tags, attributes,
translations, translations,
mimeType, mimeType,
createdAt, createdAt,
@ -71,7 +71,7 @@ export const convertVideoToEndpointVideo = ({
credits, credits,
}: Video & PayloadMedia): EndpointVideo => ({ }: Video & PayloadMedia): EndpointVideo => ({
url, url,
tagGroups: convertTagsEndpointTagsGroups(tags), attributes: convertAttributesToEndpointAttributes(attributes),
createdAt, createdAt,
filename, filename,
filesize, filesize,

View File

@ -4,26 +4,26 @@ import type { BreakBlock, SectionBlock, TranscriptBlock } from "./types/collecti
// END MOCKING SECTION // END MOCKING SECTION
export enum Collections { export enum Collections {
Attributes = "attributes",
Audios = "audios", Audios = "audios",
ChronologyEvents = "chronology-events", ChronologyEvents = "chronology-events",
Collectibles = "collectibles",
CreditsRole = "credits-roles",
Currencies = "currencies", Currencies = "currencies",
Folders = "folders",
GenericContents = "generic-contents",
Images = "images",
Languages = "languages", Languages = "languages",
MediaThumbnails = "media-thumbnails",
Pages = "pages", Pages = "pages",
Recorders = "recorders", Recorders = "recorders",
Folders = "folders",
Tags = "tags",
TagsGroups = "tags-groups",
Images = "images",
Wordings = "wordings",
Collectibles = "collectibles",
GenericContents = "generic-contents",
WebsiteConfig = "website-config",
Videos = "videos",
VideosSubtitles = "videos-subtitles",
VideosChannels = "videos-channels",
MediaThumbnails = "media-thumbnails",
Scans = "scans", Scans = "scans",
CreditsRole = "credits-roles", Tags = "tags",
Videos = "videos",
VideosChannels = "videos-channels",
VideosSubtitles = "videos-subtitles",
Wordings = "wordings",
WebsiteConfig = "website-config",
} }
export enum CollectionGroups { export enum CollectionGroups {
@ -80,6 +80,12 @@ export enum CollectionStatus {
Published = "published", Published = "published",
} }
export enum AttributeTypes {
Number = "Number",
Text = "Text",
Tags = "Tags",
}
/* RICH TEXT */ /* RICH TEXT */
export type RichTextContent = { export type RichTextContent = {

View File

@ -0,0 +1,12 @@
import { BlockField } from "payload/dist/fields/config/types";
import { numberBlock } from "../../blocks/attributeBlocks/numberBlock";
import { tagsBlock } from "../../blocks/attributeBlocks/tagsBlock";
import { textBlock } from "../../blocks/attributeBlocks/textBlock";
type AttributesFieldProps = Omit<BlockField, "type" | "blocks">;
export const attributesField = ({ ...props }: AttributesFieldProps): BlockField => ({
...props,
type: "blocks",
blocks: [tagsBlock, numberBlock, textBlock],
});

View File

@ -4,6 +4,7 @@ import { cloudStorage } from "@payloadcms/plugin-cloud-storage";
import path from "path"; import path from "path";
import { buildConfig } from "payload/config"; import { buildConfig } from "payload/config";
import { sftpAdapter } from "payloadcms-sftp-storage"; import { sftpAdapter } from "payloadcms-sftp-storage";
import { Attributes } from "./collections/Attributes/Attributes";
import { Audios } from "./collections/Audios/Audios"; import { Audios } from "./collections/Audios/Audios";
import { ChronologyEvents } from "./collections/ChronologyEvents/ChronologyEvents"; import { ChronologyEvents } from "./collections/ChronologyEvents/ChronologyEvents";
import { Collectibles } from "./collections/Collectibles/Collectibles"; import { Collectibles } from "./collections/Collectibles/Collectibles";
@ -13,15 +14,14 @@ import { Folders } from "./collections/Folders/Folders";
import { GenericContents } from "./collections/GenericContents/GenericContents"; import { GenericContents } from "./collections/GenericContents/GenericContents";
import { Images } from "./collections/Images/Images"; import { Images } from "./collections/Images/Images";
import { Languages } from "./collections/Languages/Languages"; import { Languages } from "./collections/Languages/Languages";
import { MediaThumbnails } from "./collections/MediaThumbnails/MediaThumbnails";
import { Pages } from "./collections/Pages/Pages"; import { Pages } from "./collections/Pages/Pages";
import { Recorders } from "./collections/Recorders/Recorders"; import { Recorders } from "./collections/Recorders/Recorders";
import { Scans } from "./collections/Scans/Scans"; import { Scans } from "./collections/Scans/Scans";
import { Tags } from "./collections/Tags/Tags"; import { Tags } from "./collections/Tags/Tags";
import { TagsGroups } from "./collections/TagsGroups/TagsGroups";
import { Videos } from "./collections/Videos/Videos"; import { Videos } from "./collections/Videos/Videos";
import { VideosChannels } from "./collections/VideosChannels/VideosChannels"; import { VideosChannels } from "./collections/VideosChannels/VideosChannels";
import { VideosSubtitles } from "./collections/VideosSubtitles/VideosSubtitles"; import { VideosSubtitles } from "./collections/VideosSubtitles/VideosSubtitles";
import { MediaThumbnails } from "./collections/VideosThumbnails/VideosThumbnails";
import { WebsiteConfig } from "./collections/WebsiteConfig/WebsiteConfig"; import { WebsiteConfig } from "./collections/WebsiteConfig/WebsiteConfig";
import { Wordings } from "./collections/Wordings/Wordings"; import { Wordings } from "./collections/Wordings/Wordings";
import { Icon } from "./components/Icon"; import { Icon } from "./components/Icon";
@ -68,7 +68,7 @@ export default buildConfig({
Scans, Scans,
Tags, Tags,
TagsGroups, Attributes,
CreditsRoles, CreditsRoles,
Recorders, Recorders,
Languages, Languages,

View File

@ -1,4 +1,5 @@
import { import {
AttributeTypes,
CollectibleBindingTypes, CollectibleBindingTypes,
CollectibleNature, CollectibleNature,
CollectiblePageOrders, CollectiblePageOrders,
@ -173,22 +174,42 @@ export type EndpointWording = {
export type EndpointTag = { export type EndpointTag = {
slug: string; slug: string;
page?: EndpointPage;
translations: { translations: {
language: string; language: string;
name: string; name: string;
}[]; }[];
}; };
export type EndpointTagsGroup = { export type EndpointGenericAttribute = {
slug: string; slug: string;
icon: string; icon: string;
translations: { translations: {
language: string; language: string;
name: string; name: string;
}[]; }[];
tags: EndpointTag[];
}; };
export type EndpointNumberAttribute = EndpointGenericAttribute & {
type: AttributeTypes.Number;
value: number;
};
export type EndpointTextAttribute = EndpointGenericAttribute & {
type: AttributeTypes.Text;
value: string;
};
export type EndpointTagsAttribute = EndpointGenericAttribute & {
type: AttributeTypes.Tags;
value: EndpointTag[];
};
export type EndpointAttribute =
| EndpointNumberAttribute
| EndpointTextAttribute
| EndpointTagsAttribute;
export type EndpointRole = { export type EndpointRole = {
icon: string; icon: string;
translations: { translations: {
@ -205,7 +226,7 @@ export type EndpointCredit = {
export type EndpointPage = { export type EndpointPage = {
slug: string; slug: string;
thumbnail?: EndpointImage; thumbnail?: EndpointImage;
tagGroups: EndpointTagsGroup[]; attributes: EndpointAttribute[];
backgroundImage?: EndpointImage; backgroundImage?: EndpointImage;
translations: { translations: {
language: string; language: string;
@ -234,7 +255,7 @@ export type EndpointCollectible = {
subtitle?: string; subtitle?: string;
description?: RichTextContent; description?: RichTextContent;
}[]; }[];
tagGroups: EndpointTagsGroup[]; attributes: EndpointAttribute[];
releaseDate?: string; releaseDate?: string;
languages: string[]; languages: string[];
backgroundImage?: EndpointImage; backgroundImage?: EndpointImage;
@ -458,7 +479,7 @@ export type EndpointMedia = {
filesize: number; filesize: number;
updatedAt: string; updatedAt: string;
createdAt: string; createdAt: string;
tagGroups: EndpointTagsGroup[]; attributes: EndpointAttribute[];
translations: { translations: {
language: string; language: string;
pretitle?: string; pretitle?: string;

View File

@ -75,6 +75,9 @@ html[data-theme="light"] {
} }
.blocks-field__block-pill-cueBlock + .section-title, .blocks-field__block-pill-cueBlock + .section-title,
.blocks-field__block-pill-tagsBlock + .section-title,
.blocks-field__block-pill-textBlock + .section-title,
.blocks-field__block-pill-numberBlock + .section-title,
.blocks-field__block-pill-pageRange + .section-title, .blocks-field__block-pill-pageRange + .section-title,
.blocks-field__block-pill-timeRange + .section-title, .blocks-field__block-pill-timeRange + .section-title,
.blocks-field__block-pill-urlBlock + .section-title, .blocks-field__block-pill-urlBlock + .section-title,

View File

@ -32,7 +32,7 @@ export interface Config {
"videos-channels": VideosChannel; "videos-channels": VideosChannel;
scans: Scan; scans: Scan;
tags: Tag; tags: Tag;
"tags-groups": TagsGroup; attributes: Attribute;
"credits-roles": CreditsRole; "credits-roles": CreditsRole;
recorders: Recorder; recorders: Recorder;
languages: Language; languages: Language;
@ -56,6 +56,7 @@ export interface Page {
thumbnail?: string | Image | null; thumbnail?: string | Image | null;
backgroundImage?: string | Image | null; backgroundImage?: string | Image | null;
tags?: (string | Tag)[] | null; tags?: (string | Tag)[] | null;
attributes?: (TagsBlock | NumberBlock | TextBlock)[] | null;
translations: { translations: {
language: string | Language; language: string | Language;
sourceLanguage: string | Language; sourceLanguage: string | Language;
@ -133,6 +134,7 @@ export interface Image {
}[] }[]
| null; | null;
tags?: (string | Tag)[] | null; tags?: (string | Tag)[] | null;
attributes?: (TagsBlock | NumberBlock | TextBlock)[] | null;
credits?: Credits; credits?: Credits;
updatedAt: string; updatedAt: string;
createdAt: string; createdAt: string;
@ -175,25 +177,36 @@ export interface Language {
*/ */
export interface Tag { export interface Tag {
id: string; id: string;
name?: string | null;
slug: string; slug: string;
page?: (string | null) | Page;
translations: { translations: {
language: string | Language; language: string | Language;
name: string; name: string;
id?: string | null; id?: string | null;
}[]; }[];
group: string | TagsGroup;
updatedAt: string; updatedAt: string;
createdAt: string; createdAt: string;
} }
/** /**
* This interface was referenced by `Config`'s JSON-Schema * This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "tags-groups". * via the `definition` "TagsBlock".
*/ */
export interface TagsGroup { export interface TagsBlock {
name: string | Attribute;
tags: (string | Tag)[];
id?: string | null;
blockName?: string | null;
blockType: "tagsBlock";
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "attributes".
*/
export interface Attribute {
id: string; id: string;
slug: string; slug: string;
icon?: string | null; icon?: string | null;
type: "Number" | "Text" | "Tags";
translations: { translations: {
language: string | Language; language: string | Language;
name: string; name: string;
@ -202,6 +215,28 @@ export interface TagsGroup {
updatedAt: string; updatedAt: string;
createdAt: string; createdAt: string;
} }
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "NumberBlock".
*/
export interface NumberBlock {
name: string | Attribute;
number: number;
id?: string | null;
blockName?: string | null;
blockType: "numberBlock";
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "TextBlock".
*/
export interface TextBlock {
name: string | Attribute;
text: string;
id?: string | null;
blockName?: string | null;
blockType: "textBlock";
}
/** /**
* This interface was referenced by `Config`'s JSON-Schema * This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "credits-roles". * via the `definition` "credits-roles".
@ -318,6 +353,7 @@ export interface Collectible {
nature: "Physical" | "Digital"; nature: "Physical" | "Digital";
languages?: (string | Language)[] | null; languages?: (string | Language)[] | null;
tags?: (string | Tag)[] | null; tags?: (string | Tag)[] | null;
attributes?: (TagsBlock | NumberBlock | TextBlock)[] | null;
translations: { translations: {
language: string | Language; language: string | Language;
pretitle?: string | null; pretitle?: string | null;
@ -582,6 +618,7 @@ export interface Audio {
id?: string | null; id?: string | null;
}[]; }[];
tags?: (string | Tag)[] | null; tags?: (string | Tag)[] | null;
attributes?: (TagsBlock | NumberBlock | TextBlock)[] | null;
credits?: Credits; credits?: Credits;
updatedAt: string; updatedAt: string;
createdAt: string; createdAt: string;
@ -657,6 +694,7 @@ export interface Video {
id?: string | null; id?: string | null;
}[]; }[];
tags?: (string | Tag)[] | null; tags?: (string | Tag)[] | null;
attributes?: (TagsBlock | NumberBlock | TextBlock)[] | null;
credits?: Credits; credits?: Credits;
platformEnabled?: boolean | null; platformEnabled?: boolean | null;
platform?: { platform?: {

View File

@ -2,9 +2,11 @@ import { convertAudioToEndpointAudio } from "../collections/Audios/endpoints/get
import { convertCollectibleToEndpointCollectible } from "../collections/Collectibles/endpoints/getBySlugEndpoint"; import { convertCollectibleToEndpointCollectible } from "../collections/Collectibles/endpoints/getBySlugEndpoint";
import { convertFolderToEndpointFolder } from "../collections/Folders/endpoints/getBySlugEndpoint"; import { convertFolderToEndpointFolder } from "../collections/Folders/endpoints/getBySlugEndpoint";
import { convertImageToEndpointImage } from "../collections/Images/endpoints/getByID"; import { convertImageToEndpointImage } from "../collections/Images/endpoints/getByID";
import { convertPageToEndpointPage } from "../collections/Pages/endpoints/getBySlugEndpoint";
import { convertRecorderToEndpointRecorder } from "../collections/Recorders/endpoints/getByUsername"; import { convertRecorderToEndpointRecorder } from "../collections/Recorders/endpoints/getByUsername";
import { convertVideoToEndpointVideo } from "../collections/Videos/endpoints/getByID"; import { convertVideoToEndpointVideo } from "../collections/Videos/endpoints/getByID";
import { import {
AttributeTypes,
RichTextBreakBlock, RichTextBreakBlock,
RichTextContent, RichTextContent,
RichTextSectionBlock, RichTextSectionBlock,
@ -18,11 +20,11 @@ import {
isUploadNodeVideoNode, isUploadNodeVideoNode,
} from "../constants"; } from "../constants";
import { import {
EndpointAttribute,
EndpointCredit, EndpointCredit,
EndpointRole, EndpointRole,
EndpointSource, EndpointSource,
EndpointTag, EndpointTag,
EndpointTagsGroup,
} from "../sdk"; } from "../sdk";
import { import {
Audio, Audio,
@ -32,10 +34,15 @@ import {
Folder, Folder,
Image, Image,
Language, Language,
NumberBlock,
Tag, Tag,
TagsBlock,
TextBlock,
Video, Video,
} from "../types/collections"; } from "../types/collections";
import { import {
isDefined,
isEmpty,
isPayloadArrayType, isPayloadArrayType,
isPayloadType, isPayloadType,
isPublished, isPublished,
@ -43,45 +50,14 @@ import {
isValidPayloadMedia, isValidPayloadMedia,
} from "./asserts"; } from "./asserts";
export const convertTagsEndpointTagsGroups = ( const convertTagToEndpointTag = ({ slug, page, translations }: Tag): EndpointTag => ({
tags: (string | Tag)[] | null | undefined
): EndpointTagsGroup[] => {
if (!isPayloadArrayType(tags)) {
return [];
}
const groups: EndpointTagsGroup[] = [];
tags.forEach(({ translations, slug, group }) => {
if (isPayloadType(group)) {
const existingGroup = groups.find((existingGroup) => existingGroup.slug === group.slug);
const endpointTag: EndpointTag = {
slug, slug,
...(page && isPayloadType(page) ? { page: convertPageToEndpointPage(page) } : {}),
translations: translations.map(({ language, name }) => ({ translations: translations.map(({ language, name }) => ({
language: isPayloadType(language) ? language.id : language, language: isPayloadType(language) ? language.id : language,
name, name,
})), })),
};
if (existingGroup) {
existingGroup.tags.push(endpointTag);
} else {
groups.push({
slug: group.slug,
icon: group.icon ?? "material-symbols:category-outline",
tags: [endpointTag],
translations: group.translations.map(({ language, name }) => ({
language: isPayloadType(language) ? language.id : language,
name,
})),
}); });
}
}
});
return groups;
};
export const convertRTCToEndpointRTC = ( export const convertRTCToEndpointRTC = (
{ root: { children, ...others } }: RichTextContent, { root: { children, ...others } }: RichTextContent,
@ -233,3 +209,66 @@ export const convertCreditsToEndpointCredits = (credits?: Credits | null): Endpo
}, },
]; ];
}) ?? []; }) ?? [];
export const convertAttributesToEndpointAttributes = (
attributes: (TagsBlock | NumberBlock | TextBlock)[] | null | undefined
): EndpointAttribute[] =>
attributes?.map(convertAttributeToEndpointAttribute).filter(isDefined) ?? [];
const convertAttributeToEndpointAttribute = (
attribute: TagsBlock | NumberBlock | TextBlock
): EndpointAttribute | undefined => {
switch (attribute.blockType) {
case "numberBlock": {
const { name, number } = attribute;
if (!isPayloadType(name)) return;
const { slug, icon, translations } = name;
return {
slug,
icon: icon ?? "material-symbols:category-outline",
translations: translations.map(({ language, name }) => ({
language: isPayloadType(language) ? language.id : language,
name,
})),
type: AttributeTypes.Number,
value: number,
};
}
case "textBlock": {
const { name, text } = attribute;
if (!isPayloadType(name)) return;
if (isEmpty(text)) return;
const { slug, icon, translations } = name;
return {
slug,
icon: icon ?? "material-symbols:category-outline",
translations: translations.map(({ language, name }) => ({
language: isPayloadType(language) ? language.id : language,
name,
})),
type: AttributeTypes.Text,
value: text,
};
}
case "tagsBlock": {
const { name, tags } = attribute;
if (!isPayloadType(name)) return;
if (!isPayloadArrayType(tags)) return;
if (tags.length === 0) return;
const { slug, icon, translations } = name;
return {
slug,
icon: icon ?? "material-symbols:category-outline",
translations: translations.map(({ language, name }) => ({
language: isPayloadType(language) ? language.id : language,
name,
})),
type: AttributeTypes.Tags,
value: tags.map(convertTagToEndpointTag),
};
}
}
};