More updates
This commit is contained in:
parent
8d68ff50ce
commit
b436967b3e
|
@ -0,0 +1,65 @@
|
|||
import { CollectionConfig } from "payload/types";
|
||||
import { slugField } from "../../fields/slugField/slugField";
|
||||
import { CollectionGroup, KeysTypes } from "../../constants";
|
||||
import { localizedFields } from "../../fields/translatedFields/translatedFields";
|
||||
import { collectionSlug } from "../../utils/string";
|
||||
import { Contents } from "../Contents/Contents";
|
||||
|
||||
const fields = {
|
||||
slug: "slug",
|
||||
translations: "translations",
|
||||
name: "name",
|
||||
subfolders: "subfolders",
|
||||
contents: "contents",
|
||||
} as const satisfies Record<string, string>;
|
||||
|
||||
const labels = {
|
||||
singular: "Content Folder",
|
||||
plural: "Content Folders",
|
||||
} as const satisfies { singular: string; plural: string };
|
||||
|
||||
const slug = collectionSlug(labels.plural);
|
||||
|
||||
export const ContentFolders: CollectionConfig = {
|
||||
slug,
|
||||
labels,
|
||||
typescript: { interface: labels.singular },
|
||||
defaultSort: fields.slug,
|
||||
admin: {
|
||||
useAsTitle: fields.slug,
|
||||
defaultColumns: [fields.slug, fields.translations],
|
||||
group: CollectionGroup.Collections,
|
||||
},
|
||||
timestamps: false,
|
||||
versions: false,
|
||||
fields: [
|
||||
slugField({ name: fields.slug }),
|
||||
localizedFields({
|
||||
name: fields.translations,
|
||||
interfaceName: "ContentFoldersTranslation",
|
||||
admin: {
|
||||
useAsTitle: fields.name,
|
||||
},
|
||||
fields: [{ name: fields.name, type: "text", required: true }],
|
||||
}),
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
{
|
||||
type: "relationship",
|
||||
name: fields.subfolders,
|
||||
relationTo: [slug],
|
||||
hasMany: true,
|
||||
admin: { width: "50%" },
|
||||
},
|
||||
{
|
||||
type: "relationship",
|
||||
name: fields.contents,
|
||||
relationTo: [Contents.slug],
|
||||
hasMany: true,
|
||||
admin: { width: "50%" },
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
|
@ -0,0 +1,52 @@
|
|||
import { CollectionConfig } from "payload/types";
|
||||
import { CollectionGroup } from "../../constants";
|
||||
import { collectionSlug } from "../../utils/string";
|
||||
|
||||
const fields = {
|
||||
filename: "filename",
|
||||
mimeType: "mimeType",
|
||||
filesize: "filesize",
|
||||
} as const satisfies Record<string, string>;
|
||||
|
||||
const labels = {
|
||||
singular: "Content Thumbnail",
|
||||
plural: "Content Thumbnails",
|
||||
} as const satisfies { singular: string; plural: string };
|
||||
|
||||
export const ContentThumbnails: CollectionConfig = {
|
||||
slug: collectionSlug(labels.plural),
|
||||
labels,
|
||||
typescript: { interface: labels.singular },
|
||||
defaultSort: fields.filename,
|
||||
admin: {
|
||||
useAsTitle: fields.filename,
|
||||
group: CollectionGroup.Media,
|
||||
},
|
||||
|
||||
upload: {
|
||||
staticDir: `../uploads/${labels.plural}`,
|
||||
mimeTypes: ["image/*"],
|
||||
imageSizes: [
|
||||
{
|
||||
name: "og",
|
||||
height: 750,
|
||||
width: 1125,
|
||||
formatOptions: {
|
||||
format: "jpg",
|
||||
options: { progressive: true, mozjpeg: true, compressionLevel: 9, quality: 80 },
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "medium",
|
||||
height: 1000,
|
||||
width: 1500,
|
||||
formatOptions: {
|
||||
format: "webp",
|
||||
options: { effort: 6, quality: 80, alphaQuality: 80 },
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
fields: [],
|
||||
};
|
|
@ -1,169 +1,93 @@
|
|||
import { Block, BlockField } from "payload/types";
|
||||
import { cueBlock } from "./cueBlock";
|
||||
import { textBlock } from "./textBlock";
|
||||
import { transcriptBlock } from "./transcriptBlock";
|
||||
import { lineBlock } from "./lineBlock";
|
||||
import { quoteBlock } from "./quoteBlock";
|
||||
import { BlocksConfig, generateBlocks } from "../../../utils/recursiveBlocks";
|
||||
|
||||
const INITIAL_DEPTH = 1;
|
||||
const MAX_DEPTH = 4;
|
||||
|
||||
enum BlockName {
|
||||
const enum BlockName {
|
||||
Text = "Text",
|
||||
Section = "Section",
|
||||
Tabs = "Tabs",
|
||||
Tab = "Tab",
|
||||
Columns = "Columns",
|
||||
Column = "Column",
|
||||
Transcript = "Transcript",
|
||||
Collapsible = "Collapsible",
|
||||
Accordion = "Accordion",
|
||||
Line = "Line",
|
||||
Cue = "Cue",
|
||||
Quote = "Quote",
|
||||
}
|
||||
|
||||
const rootBlocksNames: BlockName[] = [
|
||||
BlockName.Section,
|
||||
BlockName.Collapsible,
|
||||
BlockName.Columns,
|
||||
BlockName.Tabs,
|
||||
BlockName.Accordion,
|
||||
BlockName.Text,
|
||||
BlockName.Transcript,
|
||||
];
|
||||
|
||||
|
||||
const recursiveBlocks: BlockName[] = [
|
||||
BlockName.Section,
|
||||
BlockName.Collapsible,
|
||||
BlockName.Accordion,
|
||||
BlockName.Tabs,
|
||||
BlockName.Tab,
|
||||
BlockName.Column,
|
||||
BlockName.Columns,
|
||||
];
|
||||
|
||||
const blocksChildren: Record<BlockName, BlockName[]> = {
|
||||
Tabs: [BlockName.Tab],
|
||||
Columns: [BlockName.Column],
|
||||
Section: rootBlocksNames,
|
||||
Collapsible: rootBlocksNames,
|
||||
Tab: rootBlocksNames,
|
||||
Column: rootBlocksNames,
|
||||
Accordion: [BlockName.Collapsible],
|
||||
Text: [],
|
||||
Transcript: [BlockName.Line, BlockName.Cue],
|
||||
Cue: [],
|
||||
Line: [],
|
||||
};
|
||||
|
||||
export type RecursiveBlock = Omit<Block, "fields"> & {
|
||||
fields: Omit<BlockField, "blocks" | "type"> & {
|
||||
newDepth: (currentDepth: number) => number;
|
||||
blocks: BlockName[];
|
||||
};
|
||||
};
|
||||
|
||||
// TODO: Check for loops in the block graph instead of manually defining recursive blocks
|
||||
const isNotRecursiveBlock = (name: BlockName): boolean => !recursiveBlocks.includes(name);
|
||||
|
||||
const implementationForRecursiveBlocks = (
|
||||
currentDepth: number,
|
||||
{ slug, interfaceName, labels, fields: { newDepth, blocks, ...fieldsProps } }: RecursiveBlock
|
||||
): Block => ({
|
||||
slug: [slug, currentDepth].join("_"),
|
||||
interfaceName: [interfaceName, currentDepth].join("_"),
|
||||
labels,
|
||||
fields: [
|
||||
{
|
||||
...fieldsProps,
|
||||
type: "blocks",
|
||||
blocks: blocks
|
||||
.filter((block) => {
|
||||
if (currentDepth < MAX_DEPTH) return true;
|
||||
if (blocks.filter(isNotRecursiveBlock).length === 0) return true;
|
||||
return isNotRecursiveBlock(block);
|
||||
})
|
||||
.map((block) => implementations[block](newDepth(currentDepth))),
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const implementations: Record<BlockName, (currentDepth: number) => Block> = {
|
||||
Cue: () => cueBlock,
|
||||
Text: () => textBlock,
|
||||
Transcript: () => transcriptBlock,
|
||||
Line: () => lineBlock,
|
||||
Section: (currentDepth) =>
|
||||
implementationForRecursiveBlocks(currentDepth, {
|
||||
const blocksConfig: BlocksConfig<BlockName> = {
|
||||
Text: {
|
||||
root: true,
|
||||
block: textBlock,
|
||||
},
|
||||
Section: {
|
||||
root: true,
|
||||
block: {
|
||||
slug: "section",
|
||||
interfaceName: "Section",
|
||||
labels: { singular: "Section", plural: "Sections" },
|
||||
fields: {
|
||||
recursion: {
|
||||
name: "content",
|
||||
condition: (depth) => depth < 5,
|
||||
newDepth: (depth) => depth + 1,
|
||||
blocks: blocksChildren.Section,
|
||||
blocks: [
|
||||
BlockName.Section,
|
||||
BlockName.Tabs,
|
||||
BlockName.Transcript,
|
||||
BlockName.Quote,
|
||||
BlockName.Text,
|
||||
],
|
||||
},
|
||||
}),
|
||||
Accordion: (currentDepth) =>
|
||||
implementationForRecursiveBlocks(currentDepth, {
|
||||
slug: "accordion",
|
||||
interfaceName: "Accordion",
|
||||
labels: { singular: "Accordion", plural: "Accordions" },
|
||||
fields: {
|
||||
name: "content",
|
||||
newDepth: (depth) => depth + 1,
|
||||
blocks: blocksChildren.Accordion,
|
||||
},
|
||||
}),
|
||||
Collapsible: (currentDepth) =>
|
||||
implementationForRecursiveBlocks(currentDepth, {
|
||||
slug: "collapsible",
|
||||
interfaceName: "Collapsible",
|
||||
labels: { singular: "Collapsible", plural: "Collapsibles" },
|
||||
fields: {
|
||||
name: "content",
|
||||
newDepth: (depth) => depth + 1,
|
||||
blocks: blocksChildren.Collapsible,
|
||||
},
|
||||
}),
|
||||
Tabs: (currentDepth) =>
|
||||
implementationForRecursiveBlocks(currentDepth, {
|
||||
},
|
||||
},
|
||||
Tabs: {
|
||||
root: true,
|
||||
block: {
|
||||
slug: "tabs",
|
||||
interfaceName: "Tabs",
|
||||
labels: { singular: "Tabs", plural: "Tabs" },
|
||||
fields: { name: "tabs", newDepth: (depth) => depth, blocks: blocksChildren.Tabs },
|
||||
}),
|
||||
Tab: (currentDepth) =>
|
||||
implementationForRecursiveBlocks(currentDepth, {
|
||||
slug: "tab",
|
||||
interfaceName: "Tab",
|
||||
labels: { singular: "Tab", plural: "Tabs" },
|
||||
fields: {
|
||||
name: "content",
|
||||
newDepth: (depth) => depth + 1,
|
||||
blocks: blocksChildren.Tab,
|
||||
},
|
||||
}),
|
||||
Columns: (currentDepth) =>
|
||||
implementationForRecursiveBlocks(currentDepth, {
|
||||
slug: "columns",
|
||||
interfaceName: "Columns",
|
||||
labels: { singular: "Columns", plural: "Columns" },
|
||||
fields: {
|
||||
name: "columns",
|
||||
recursion: {
|
||||
name: "tabs",
|
||||
newDepth: (depth) => depth,
|
||||
blocks: blocksChildren.Columns,
|
||||
condition: (depth, parents) => !parents.includes(BlockName.Tabs) && depth < 5,
|
||||
blocks: [BlockName.Tab],
|
||||
},
|
||||
}),
|
||||
Column: (currentDepth) =>
|
||||
implementationForRecursiveBlocks(currentDepth, {
|
||||
slug: "column",
|
||||
interfaceName: "Column",
|
||||
labels: { singular: "Column", plural: "Columns" },
|
||||
fields: { name: "content", newDepth: (depth) => depth + 1, blocks: blocksChildren.Column },
|
||||
}),
|
||||
},
|
||||
},
|
||||
Tab: {
|
||||
root: false,
|
||||
block: {
|
||||
slug: "tab",
|
||||
labels: { singular: "Tab", plural: "Tabs" },
|
||||
recursion: {
|
||||
name: "content",
|
||||
condition: (depth) => depth < 5,
|
||||
newDepth: (depth) => depth + 1,
|
||||
blocks: [
|
||||
BlockName.Section,
|
||||
BlockName.Tabs,
|
||||
BlockName.Transcript,
|
||||
BlockName.Quote,
|
||||
BlockName.Text,
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
Transcript: {
|
||||
root: true,
|
||||
block: transcriptBlock,
|
||||
},
|
||||
Cue: {
|
||||
root: false,
|
||||
block: cueBlock,
|
||||
},
|
||||
Line: {
|
||||
root: false,
|
||||
block: lineBlock,
|
||||
},
|
||||
Quote: {
|
||||
root: true,
|
||||
block: quoteBlock,
|
||||
},
|
||||
};
|
||||
|
||||
export const rootBlocks: Block[] = rootBlocksNames
|
||||
.filter((block) => block in implementations)
|
||||
.map((block) => implementations[block](INITIAL_DEPTH));
|
||||
export const contentBlocks = generateBlocks(blocksConfig);
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
import { Block } from "payload/types";
|
||||
|
||||
export const quoteBlock: Block = {
|
||||
slug: "quoteBlock",
|
||||
interfaceName: "QuoteBlock",
|
||||
labels: { singular: "Quote", plural: "Quotes" },
|
||||
fields: [
|
||||
{
|
||||
name: "from",
|
||||
type: "text",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: "content",
|
||||
type: "richText",
|
||||
label: false,
|
||||
required: true,
|
||||
admin: {
|
||||
hideGutter: true,
|
||||
elements: [],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
|
@ -1,14 +1,15 @@
|
|||
import { CollectionConfig } from "payload/types";
|
||||
import { collectionSlug } from "../../utils/string";
|
||||
import { CollectionGroup, FileTypes, TagsTypes } from "../../constants";
|
||||
import { CollectionGroup, FileTypes, KeysTypes } from "../../constants";
|
||||
import { slugField } from "../../fields/slugField/slugField";
|
||||
import { imageField } from "../../fields/imageField/imageField";
|
||||
import { Tags } from "../Tags/Tags";
|
||||
import { Keys } from "../Keys/Keys";
|
||||
import { localizedFields } from "../../fields/translatedFields/translatedFields";
|
||||
import { Recorders } from "../Recorders/Recorders";
|
||||
import { isDefined } from "../../utils/asserts";
|
||||
import { fileField } from "../../fields/fileField/fileField";
|
||||
import { rootBlocks } from "./Blocks/blocks";
|
||||
import { contentBlocks } from "./Blocks/blocks";
|
||||
import { ContentThumbnails } from "../ContentThumbnails/ContentThumbnails";
|
||||
|
||||
const fields = {
|
||||
slug: "slug",
|
||||
|
@ -29,6 +30,7 @@ const fields = {
|
|||
videoNotes: "videoNotes",
|
||||
audio: "audio",
|
||||
audioNotes: "videoNotes",
|
||||
status: "status",
|
||||
} as const satisfies Record<string, string>;
|
||||
|
||||
const labels = {
|
||||
|
@ -43,42 +45,47 @@ export const Contents: CollectionConfig = {
|
|||
defaultSort: fields.slug,
|
||||
admin: {
|
||||
useAsTitle: fields.slug,
|
||||
defaultColumns: [fields.slug, fields.thumbnail, fields.categories],
|
||||
defaultColumns: [
|
||||
fields.slug,
|
||||
fields.thumbnail,
|
||||
fields.categories,
|
||||
fields.type,
|
||||
fields.translations,
|
||||
fields.status,
|
||||
],
|
||||
group: CollectionGroup.Collections,
|
||||
preview: (doc) => `https://accords-library.com/contents/${doc.slug}`,
|
||||
},
|
||||
timestamps: true,
|
||||
versions: { drafts: true },
|
||||
versions: { drafts: { autosave: true } },
|
||||
fields: [
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
slugField({ name: fields.slug, admin: { width: "50%" } }),
|
||||
imageField({ name: fields.thumbnail, admin: { width: "50%" } }),
|
||||
imageField({
|
||||
name: fields.thumbnail,
|
||||
relationTo: ContentThumbnails.slug,
|
||||
admin: { width: "50%" },
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "testing",
|
||||
type: "blocks",
|
||||
admin: { initCollapsed: true },
|
||||
blocks: rootBlocks,
|
||||
},
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
{
|
||||
name: fields.categories,
|
||||
type: "relationship",
|
||||
relationTo: [Tags.slug],
|
||||
filterOptions: { type: { equals: TagsTypes.Categories } },
|
||||
relationTo: [Keys.slug],
|
||||
filterOptions: { type: { equals: KeysTypes.Categories } },
|
||||
hasMany: true,
|
||||
admin: { allowCreate: false, width: "50%" },
|
||||
},
|
||||
{
|
||||
name: fields.type,
|
||||
type: "relationship",
|
||||
relationTo: [Tags.slug],
|
||||
filterOptions: { type: { equals: TagsTypes.Contents } },
|
||||
relationTo: [Keys.slug],
|
||||
filterOptions: { type: { equals: KeysTypes.Contents } },
|
||||
admin: { allowCreate: false, width: "50%" },
|
||||
},
|
||||
],
|
||||
|
@ -148,8 +155,10 @@ export const Contents: CollectionConfig = {
|
|||
{
|
||||
name: fields.textContent,
|
||||
label: "Content",
|
||||
type: "richText",
|
||||
admin: { hideGutter: true },
|
||||
labels: { singular: "Block", plural: "Blocks" },
|
||||
type: "blocks",
|
||||
admin: { initCollapsed: true },
|
||||
blocks: contentBlocks,
|
||||
},
|
||||
{
|
||||
name: fields.textNotes,
|
||||
|
|
|
@ -1,22 +1,27 @@
|
|||
import { CollectionConfig } from "payload/types";
|
||||
import { slugField } from "../../fields/slugField/slugField";
|
||||
import { CollectionGroup, TagsTypes } from "../../constants";
|
||||
import { CollectionGroup, KeysTypes } from "../../constants";
|
||||
import { localizedFields } from "../../fields/translatedFields/translatedFields";
|
||||
import { collectionSlug } from "../../utils/string";
|
||||
import { Key } from "../../types/collections";
|
||||
import { isDefined } from "../../utils/asserts";
|
||||
|
||||
const fields = {
|
||||
slug: "slug",
|
||||
translations: "translations",
|
||||
type: "type",
|
||||
name: "name",
|
||||
short: "short",
|
||||
} as const satisfies Record<string, string>;
|
||||
|
||||
const labels = {
|
||||
singular: "Tag",
|
||||
plural: "Tags",
|
||||
singular: "Key",
|
||||
plural: "Keys",
|
||||
} as const satisfies { singular: string; plural: string };
|
||||
|
||||
export const Tags: CollectionConfig = {
|
||||
const keysTypesWithShort: (keyof typeof KeysTypes)[] = ["Categories", "GamePlatforms"];
|
||||
|
||||
export const Keys: CollectionConfig = {
|
||||
slug: collectionSlug(labels.plural),
|
||||
labels,
|
||||
typescript: { interface: labels.singular },
|
||||
|
@ -34,7 +39,7 @@ export const Tags: CollectionConfig = {
|
|||
name: fields.type,
|
||||
type: "select",
|
||||
required: true,
|
||||
options: Object.entries(TagsTypes).map(([value, label]) => ({ label, value })),
|
||||
options: Object.entries(KeysTypes).map(([value, label]) => ({ label, value })),
|
||||
},
|
||||
localizedFields({
|
||||
name: fields.translations,
|
||||
|
@ -42,7 +47,23 @@ export const Tags: CollectionConfig = {
|
|||
admin: {
|
||||
useAsTitle: fields.name,
|
||||
},
|
||||
fields: [{ name: fields.name, type: "text", required: true }],
|
||||
fields: [
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
{ name: fields.name, type: "text", required: true, admin: { width: "50%" } },
|
||||
{
|
||||
name: fields.short,
|
||||
type: "text",
|
||||
admin: {
|
||||
condition: (data: Partial<Key>) =>
|
||||
isDefined(data.type) && keysTypesWithShort.includes(data.type),
|
||||
width: "50%",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}),
|
||||
],
|
||||
};
|
|
@ -0,0 +1,64 @@
|
|||
import { CollectionConfig } from "payload/types";
|
||||
import { CollectionGroup } from "../../constants";
|
||||
import { collectionSlug } from "../../utils/string";
|
||||
|
||||
const fields = {
|
||||
filename: "filename",
|
||||
mimeType: "mimeType",
|
||||
filesize: "filesize",
|
||||
} as const satisfies Record<string, string>;
|
||||
|
||||
const labels = {
|
||||
singular: "Library Item Thumbnail",
|
||||
plural: "Library Item Thumbnails",
|
||||
} as const satisfies { singular: string; plural: string };
|
||||
|
||||
export const LibraryItemThumbnails: CollectionConfig = {
|
||||
slug: collectionSlug(labels.plural),
|
||||
labels,
|
||||
typescript: { interface: labels.singular },
|
||||
defaultSort: fields.filename,
|
||||
admin: {
|
||||
useAsTitle: fields.filename,
|
||||
group: CollectionGroup.Media,
|
||||
},
|
||||
|
||||
upload: {
|
||||
staticDir: `../uploads/${labels.plural}`,
|
||||
mimeTypes: ["image/*"],
|
||||
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: [],
|
||||
};
|
|
@ -1,9 +1,19 @@
|
|||
import { CollectionConfig } from "payload/types";
|
||||
import { CollectionGroup } from "../../constants";
|
||||
import {
|
||||
CollectionGroup,
|
||||
KeysTypes,
|
||||
LibraryItemsTextualBindingTypes,
|
||||
LibraryItemsTextualPageOrders,
|
||||
LibraryItemsTypes,
|
||||
} from "../../constants";
|
||||
import { slugField } from "../../fields/slugField/slugField";
|
||||
import { imageField } from "../../fields/imageField/imageField";
|
||||
import { collectionSlug } from "../../utils/string";
|
||||
import { isDefined, isUndefined } from "../../utils/asserts";
|
||||
import { LibraryItemThumbnails } from "../LibraryItemThumbnails/LibraryItemThumbnails";
|
||||
import { LibraryItem } from "../../types/collections";
|
||||
import { Keys } from "../Keys/Keys";
|
||||
import { Languages } from "../Languages";
|
||||
|
||||
const fields = {
|
||||
status: "status",
|
||||
|
@ -20,6 +30,16 @@ const fields = {
|
|||
width: "width",
|
||||
height: "height",
|
||||
thickness: "thickness",
|
||||
releaseDate: "releaseDate",
|
||||
itemType: "itemType",
|
||||
textual: "textual",
|
||||
textualSubtype: "subtype",
|
||||
textualBindingType: "bindingType",
|
||||
textualPageCount: "pageCount",
|
||||
textualPageOrder: "pageOrder",
|
||||
textualLanguages: "languages",
|
||||
audio: "audio",
|
||||
audioSubtype: "audioSubtype",
|
||||
} as const satisfies Record<string, string>;
|
||||
|
||||
const labels = {
|
||||
|
@ -50,13 +70,17 @@ export const LibraryItems: CollectionConfig = {
|
|||
preview: (doc) => `https://accords-library.com/library/${doc.slug}`,
|
||||
},
|
||||
timestamps: true,
|
||||
versions: { drafts: true },
|
||||
versions: { drafts: { autosave: true } },
|
||||
fields: [
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
slugField({ name: fields.slug, admin: { width: "50%" } }),
|
||||
imageField({ name: fields.thumbnail, admin: { width: "50%" } }),
|
||||
imageField({
|
||||
name: fields.thumbnail,
|
||||
relationTo: LibraryItemThumbnails.slug,
|
||||
admin: { width: "50%" },
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
|
@ -144,5 +168,106 @@ export const LibraryItems: CollectionConfig = {
|
|||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: fields.itemType,
|
||||
type: "radio",
|
||||
options: Object.entries(LibraryItemsTypes).map(([value, label]) => ({ label, value })),
|
||||
admin: {
|
||||
layout: "horizontal",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: fields.textual,
|
||||
type: "group",
|
||||
admin: {
|
||||
condition: (data: Partial<LibraryItem>) => data.itemType === LibraryItemsTypes.Textual,
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
{
|
||||
name: fields.textualSubtype,
|
||||
label: "Subtype",
|
||||
type: "relationship",
|
||||
relationTo: [Keys.slug],
|
||||
filterOptions: { type: { equals: KeysTypes.LibraryTextual } },
|
||||
hasMany: true,
|
||||
admin: { allowCreate: false, width: "50%" },
|
||||
},
|
||||
{
|
||||
name: fields.textualLanguages,
|
||||
type: "relationship",
|
||||
relationTo: [Languages.slug],
|
||||
hasMany: true,
|
||||
admin: { allowCreate: false, width: "50%" },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
{ name: fields.textualPageCount, type: "number", min: 1, admin: { width: "33%" } },
|
||||
{
|
||||
name: fields.textualBindingType,
|
||||
label: "Binding Type",
|
||||
type: "radio",
|
||||
options: Object.entries(LibraryItemsTextualBindingTypes).map(([value, label]) => ({
|
||||
label,
|
||||
value,
|
||||
})),
|
||||
admin: {
|
||||
layout: "horizontal",
|
||||
width: "33%",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: fields.textualPageOrder,
|
||||
label: "Page Order",
|
||||
type: "radio",
|
||||
options: Object.entries(LibraryItemsTextualPageOrders).map(([value, label]) => ({
|
||||
label,
|
||||
value,
|
||||
})),
|
||||
admin: {
|
||||
layout: "horizontal",
|
||||
width: "33%",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: fields.audio,
|
||||
type: "group",
|
||||
admin: {
|
||||
condition: (data: Partial<LibraryItem>) => data.itemType === LibraryItemsTypes.Audio,
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
{
|
||||
name: fields.audioSubtype,
|
||||
label: "Subtype",
|
||||
type: "relationship",
|
||||
relationTo: [Keys.slug],
|
||||
filterOptions: { type: { equals: KeysTypes.LibraryAudio } },
|
||||
hasMany: true,
|
||||
admin: { allowCreate: false, width: "50%" },
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: fields.releaseDate,
|
||||
type: "date",
|
||||
admin: {
|
||||
date: { pickerAppearance: "dayOnly", displayFormat: "yyyy-MM-dd" },
|
||||
position: "sidebar",
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
|
|
@ -6,15 +6,14 @@ const fields = {
|
|||
filename: "filename",
|
||||
mimeType: "mimeType",
|
||||
filesize: "filesize",
|
||||
alt: "alt",
|
||||
} as const satisfies Record<string, string>;
|
||||
|
||||
const labels = {
|
||||
singular: "Image",
|
||||
plural: "Images",
|
||||
singular: "Post Thumbnail",
|
||||
plural: "Post Thumbnails",
|
||||
} as const satisfies { singular: string; plural: string };
|
||||
|
||||
export const Images: CollectionConfig = {
|
||||
export const PostThumbnails: CollectionConfig = {
|
||||
slug: collectionSlug(labels.plural),
|
||||
labels,
|
||||
typescript: { interface: labels.singular },
|
||||
|
@ -27,13 +26,27 @@ export const Images: CollectionConfig = {
|
|||
upload: {
|
||||
staticDir: `../uploads/${labels.plural}`,
|
||||
mimeTypes: ["image/*"],
|
||||
imageSizes: [
|
||||
{
|
||||
name: "og",
|
||||
height: 750,
|
||||
width: 1125,
|
||||
formatOptions: {
|
||||
format: "jpg",
|
||||
options: { progressive: true, mozjpeg: true, compressionLevel: 9, quality: 80 },
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "medium",
|
||||
height: 1000,
|
||||
width: 1500,
|
||||
formatOptions: {
|
||||
format: "webp",
|
||||
options: { effort: 6, quality: 80, alphaQuality: 80 },
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
fields: [
|
||||
{
|
||||
name: fields.alt,
|
||||
label: "Alt Text",
|
||||
type: "text",
|
||||
},
|
||||
],
|
||||
fields: [],
|
||||
};
|
|
@ -1,16 +1,18 @@
|
|||
import { CollectionConfig } from "payload/types";
|
||||
import { slugField } from "../../fields/slugField/slugField";
|
||||
import { imageField } from "../../fields/imageField/imageField";
|
||||
import { CollectionGroup, TagsTypes } from "../../constants";
|
||||
import { CollectionGroup, KeysTypes } from "../../constants";
|
||||
import { Recorders } from "../Recorders/Recorders";
|
||||
import { localizedFields } from "../../fields/translatedFields/translatedFields";
|
||||
import { isDefined, isUndefined } from "../../utils/asserts";
|
||||
import { removeTranslatorsForTranscripts } from "./hooks/beforeValidate";
|
||||
import { Tags } from "../Tags/Tags";
|
||||
import { Keys } from "../Keys/Keys";
|
||||
import { collectionSlug } from "../../utils/string";
|
||||
import { PostThumbnails } from "../PostThumbnails/PostThumbnails";
|
||||
|
||||
const fields = {
|
||||
slug: "slug",
|
||||
hidden: "hidden",
|
||||
thumbnail: "thumbnail",
|
||||
categories: "categories",
|
||||
authors: "authors",
|
||||
|
@ -44,13 +46,17 @@ export const Posts: CollectionConfig = {
|
|||
beforeValidate: [removeTranslatorsForTranscripts],
|
||||
},
|
||||
timestamps: true,
|
||||
versions: { drafts: true },
|
||||
versions: { drafts: { autosave: true } },
|
||||
fields: [
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
slugField({ name: fields.slug, admin: { width: "50%" } }),
|
||||
imageField({ name: fields.thumbnail, admin: { width: "50%" } }),
|
||||
imageField({
|
||||
name: fields.thumbnail,
|
||||
relationTo: PostThumbnails.slug,
|
||||
admin: { width: "50%" },
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
|
@ -63,15 +69,15 @@ export const Posts: CollectionConfig = {
|
|||
required: true,
|
||||
minRows: 1,
|
||||
hasMany: true,
|
||||
admin: { width: "50%" },
|
||||
admin: { width: "35%" },
|
||||
},
|
||||
{
|
||||
name: fields.categories,
|
||||
type: "relationship",
|
||||
relationTo: [Tags.slug],
|
||||
filterOptions: { type: { equals: TagsTypes.Categories } },
|
||||
relationTo: [Keys.slug],
|
||||
filterOptions: { type: { equals: KeysTypes.Categories } },
|
||||
hasMany: true,
|
||||
admin: { allowCreate: false, width: "50%" },
|
||||
admin: { allowCreate: false, width: "35%" },
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -138,5 +144,15 @@ export const Posts: CollectionConfig = {
|
|||
},
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: fields.hidden,
|
||||
type: "checkbox",
|
||||
required: false,
|
||||
defaultValue: false,
|
||||
admin: {
|
||||
description: "If enabled, the post won't appear in the 'News' section",
|
||||
position: "sidebar",
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
import { CollectionConfig } from "payload/types";
|
||||
import { CollectionGroup } from "../../constants";
|
||||
import { collectionSlug } from "../../utils/string";
|
||||
|
||||
const fields = {
|
||||
filename: "filename",
|
||||
mimeType: "mimeType",
|
||||
filesize: "filesize",
|
||||
} as const satisfies Record<string, string>;
|
||||
|
||||
const labels = {
|
||||
singular: "Recorder Thumbnail",
|
||||
plural: "Recorder Thumbnails",
|
||||
} as const satisfies { singular: string; plural: string };
|
||||
|
||||
export const RecorderThumbnails: CollectionConfig = {
|
||||
slug: collectionSlug(labels.plural),
|
||||
labels,
|
||||
typescript: { interface: labels.singular },
|
||||
defaultSort: fields.filename,
|
||||
admin: {
|
||||
useAsTitle: fields.filename,
|
||||
group: CollectionGroup.Media,
|
||||
},
|
||||
|
||||
upload: {
|
||||
staticDir: `../uploads/${labels.plural}`,
|
||||
adminThumbnail: "small",
|
||||
mimeTypes: ["image/*"],
|
||||
imageSizes: [
|
||||
{
|
||||
name: "og",
|
||||
height: 256,
|
||||
width: 256,
|
||||
formatOptions: {
|
||||
format: "jpg",
|
||||
options: { progressive: true, mozjpeg: true, compressionLevel: 9, quality: 80 },
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "small",
|
||||
height: 128,
|
||||
width: 128,
|
||||
formatOptions: {
|
||||
format: "webp",
|
||||
options: { effort: 6, quality: 80, alphaQuality: 80 },
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
fields: [],
|
||||
};
|
|
@ -1,12 +1,11 @@
|
|||
import { CollectionConfig } from "payload/types";
|
||||
import { localizedFields } from "../../fields/translatedFields/translatedFields";
|
||||
import { Languages } from "../Languages";
|
||||
import { Images } from "../Images/Images";
|
||||
import { Cell } from "../../fields/imageField/Cell";
|
||||
import { beforeDuplicate } from "./hooks/beforeDuplicate";
|
||||
import { imageField } from "../../fields/imageField/imageField";
|
||||
import { CollectionGroup } from "../../constants";
|
||||
import { collectionSlug } from "../../utils/string";
|
||||
import { RecorderThumbnails } from "../RecorderThumbnails/RecorderThumbnails";
|
||||
import { imageField } from "../../fields/imageField/imageField";
|
||||
|
||||
const fields = {
|
||||
username: "username",
|
||||
|
@ -32,7 +31,13 @@ export const Recorders: CollectionConfig = {
|
|||
hooks: { beforeDuplicate },
|
||||
description:
|
||||
"Recorders are contributors of the Accord's Library project. Create a Recorder here to be able to credit them in other collections",
|
||||
defaultColumns: [fields.username, fields.anonymize, fields.biographies, fields.languages],
|
||||
defaultColumns: [
|
||||
fields.username,
|
||||
fields.avatar,
|
||||
fields.anonymize,
|
||||
fields.biographies,
|
||||
fields.languages,
|
||||
],
|
||||
group: CollectionGroup.Meta,
|
||||
},
|
||||
timestamps: false,
|
||||
|
@ -40,25 +45,18 @@ export const Recorders: CollectionConfig = {
|
|||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
imageField({ name: fields.avatar }),
|
||||
{
|
||||
name: fields.username,
|
||||
type: "text",
|
||||
unique: true,
|
||||
required: true,
|
||||
admin: { description: "The username must be unique" },
|
||||
},
|
||||
{
|
||||
name: fields.anonymize,
|
||||
type: "checkbox",
|
||||
required: true,
|
||||
defaultValue: false,
|
||||
admin: {
|
||||
width: "50%",
|
||||
description:
|
||||
"If enabled, this recorder's username will not be made public. Instead they will be referred to as 'Recorder#0000' where '0000' is a random four digit number",
|
||||
},
|
||||
admin: { description: "The username must be unique", width: "33%" },
|
||||
},
|
||||
imageField({
|
||||
name: fields.avatar,
|
||||
relationTo: RecorderThumbnails.slug,
|
||||
admin: { width: "66%" },
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
|
@ -81,5 +79,16 @@ export const Recorders: CollectionConfig = {
|
|||
},
|
||||
fields: [{ name: fields.biography, type: "textarea" }],
|
||||
}),
|
||||
{
|
||||
name: fields.anonymize,
|
||||
type: "checkbox",
|
||||
required: true,
|
||||
defaultValue: false,
|
||||
admin: {
|
||||
description:
|
||||
"If enabled, this recorder's username will not be made public. Instead they will be referred to as 'Recorder#0000' where '0000' is a random four digit number",
|
||||
position: "sidebar",
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
|
|
@ -5,7 +5,7 @@ export enum CollectionGroup {
|
|||
Administration = "Administration",
|
||||
}
|
||||
|
||||
export enum TagsTypes {
|
||||
export enum KeysTypes {
|
||||
Contents = "Contents",
|
||||
LibraryAudio = "Library / Audio",
|
||||
LibraryVideo = "Library / Video",
|
||||
|
@ -15,6 +15,7 @@ export enum TagsTypes {
|
|||
Weapons = "Weapons",
|
||||
GamePlatforms = "Game Platforms",
|
||||
Categories = "Categories",
|
||||
Wordings = "Wordings",
|
||||
}
|
||||
|
||||
export enum FileTypes {
|
||||
|
@ -23,3 +24,21 @@ export enum FileTypes {
|
|||
ContentVideo = "Content / Video",
|
||||
ContentAudio = "Content / Audio",
|
||||
}
|
||||
|
||||
export enum LibraryItemsTypes {
|
||||
Textual = "Textual",
|
||||
Audio = "Audio",
|
||||
Video = "Video",
|
||||
Game = "Game",
|
||||
Other = "Other",
|
||||
}
|
||||
|
||||
export enum LibraryItemsTextualBindingTypes {
|
||||
Paperback = "Paperback",
|
||||
Hardcover = "Hardcover",
|
||||
}
|
||||
|
||||
export enum LibraryItemsTextualPageOrders {
|
||||
LeftToRight = "Left to right",
|
||||
RightToLeft = "Right to left",
|
||||
}
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
import { UploadField } from "payload/types";
|
||||
import { Images } from "../../collections/Images/Images";
|
||||
import { Cell } from "./Cell";
|
||||
|
||||
type Props = Omit<UploadField, "type" | "relationTo">;
|
||||
type Props = Omit<UploadField, "type">;
|
||||
|
||||
export const imageField = ({ admin, ...otherProps }: Props): UploadField => ({
|
||||
...otherProps,
|
||||
type: "upload",
|
||||
relationTo: Images.slug,
|
||||
admin: {
|
||||
components: {
|
||||
Cell,
|
||||
|
|
|
@ -3,12 +3,16 @@ import path from "path";
|
|||
import { Users } from "./collections/Users";
|
||||
import { Languages } from "./collections/Languages";
|
||||
import { Recorders } from "./collections/Recorders/Recorders";
|
||||
import { Images } from "./collections/Images/Images";
|
||||
import { Posts } from "./collections/Posts/Posts";
|
||||
import { Tags } from "./collections/Tags/Tags";
|
||||
import { Keys } from "./collections/Keys/Keys";
|
||||
import { LibraryItems } from "./collections/LibraryItems/LibraryItems";
|
||||
import { Contents } from "./collections/Contents/Contents";
|
||||
import { Files } from "./collections/Files/Files";
|
||||
import { RecorderThumbnails } from "./collections/RecorderThumbnails/RecorderThumbnails";
|
||||
import { PostThumbnails } from "./collections/PostThumbnails/PostThumbnails";
|
||||
import { LibraryItemThumbnails } from "./collections/LibraryItemThumbnails/LibraryItemThumbnails";
|
||||
import { ContentThumbnails } from "./collections/ContentThumbnails/ContentThumbnails";
|
||||
import { ContentFolders } from "./collections/ContentFolders/ContentFolders";
|
||||
import { Logo } from "./components/Logo";
|
||||
import { Icon } from "./components/Icon";
|
||||
|
||||
|
@ -21,15 +25,30 @@ export default buildConfig({
|
|||
favicon: "/public/favicon.ico",
|
||||
ogImage: "og.jpg",
|
||||
titleSuffix: "- Accord’s Library",
|
||||
},
|
||||
},
|
||||
css: path.resolve(__dirname, "styles.scss"),
|
||||
},
|
||||
collections: [
|
||||
LibraryItems,
|
||||
Contents,
|
||||
ContentFolders,
|
||||
Posts,
|
||||
ContentThumbnails,
|
||||
LibraryItemThumbnails,
|
||||
RecorderThumbnails,
|
||||
PostThumbnails,
|
||||
Files,
|
||||
Languages,
|
||||
Recorders,
|
||||
Keys,
|
||||
Users,
|
||||
],
|
||||
globals: [],
|
||||
telemetry: false,
|
||||
typescript: {
|
||||
outputFile: path.resolve(__dirname, "types/collections.ts"),
|
||||
},
|
||||
graphQL: {
|
||||
schemaOutputFile: path.resolve(__dirname, "generated-schema.graphql"),
|
||||
disable: true,
|
||||
},
|
||||
});
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
export type CategoryTranslations = {
|
||||
language: string | Language;
|
||||
name: string;
|
||||
short?: string;
|
||||
id?: string;
|
||||
}[];
|
||||
export type RecorderBiographies = {
|
||||
|
@ -16,25 +17,39 @@ export type RecorderBiographies = {
|
|||
biography?: string;
|
||||
id?: string;
|
||||
}[];
|
||||
export type ContentFoldersTranslation = {
|
||||
language: string | Language;
|
||||
name: string;
|
||||
id?: string;
|
||||
}[];
|
||||
|
||||
export interface Config {
|
||||
collections: {
|
||||
'library-items': LibraryItem;
|
||||
contents: Content;
|
||||
'content-folders': ContentFolder;
|
||||
posts: Post;
|
||||
images: Image;
|
||||
'content-thumbnails': ContentThumbnail;
|
||||
'library-item-thumbnails': LibraryItemThumbnail;
|
||||
'recorder-thumbnails': RecorderThumbnail;
|
||||
'post-thumbnails': PostThumbnail;
|
||||
files: File;
|
||||
languages: Language;
|
||||
recorders: Recorder;
|
||||
tags: Tag;
|
||||
keys: Key;
|
||||
users: User;
|
||||
};
|
||||
globals: {};
|
||||
}
|
||||
export interface LibraryItem {
|
||||
id: string;
|
||||
/**
|
||||
* @minItems 2
|
||||
* @maxItems 2
|
||||
*/
|
||||
test?: [number, number];
|
||||
slug: string;
|
||||
thumbnail?: string | Image;
|
||||
thumbnail?: string | LibraryItemThumbnail;
|
||||
pretitle?: string;
|
||||
title: string;
|
||||
subtitle?: string;
|
||||
|
@ -47,13 +62,37 @@ export interface LibraryItem {
|
|||
height?: number;
|
||||
thickness?: number;
|
||||
};
|
||||
itemType?: 'Textual' | 'Audio' | 'Video' | 'Game' | 'Other';
|
||||
textual?: {
|
||||
subtype?:
|
||||
| {
|
||||
value: string;
|
||||
relationTo: 'keys';
|
||||
}[]
|
||||
| {
|
||||
value: Key;
|
||||
relationTo: 'keys';
|
||||
}[];
|
||||
languages?:
|
||||
| {
|
||||
value: string;
|
||||
relationTo: 'languages';
|
||||
}[]
|
||||
| {
|
||||
value: Language;
|
||||
relationTo: 'languages';
|
||||
}[];
|
||||
pageCount?: number;
|
||||
bindingType?: 'Paperback' | 'Hardcover';
|
||||
pageOrder?: 'LeftToRight' | 'RightToLeft';
|
||||
};
|
||||
releaseDate?: string;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
_status?: 'draft' | 'published';
|
||||
}
|
||||
export interface Image {
|
||||
export interface LibraryItemThumbnail {
|
||||
id: string;
|
||||
alt?: string;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
url?: string;
|
||||
|
@ -62,24 +101,69 @@ export interface Image {
|
|||
filesize?: number;
|
||||
width?: number;
|
||||
height?: number;
|
||||
sizes?: {
|
||||
og?: {
|
||||
url?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
mimeType?: string;
|
||||
filesize?: number;
|
||||
filename?: string;
|
||||
};
|
||||
medium?: {
|
||||
url?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
mimeType?: string;
|
||||
filesize?: number;
|
||||
filename?: string;
|
||||
};
|
||||
large?: {
|
||||
url?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
mimeType?: string;
|
||||
filesize?: number;
|
||||
filename?: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
export interface Key {
|
||||
id: string;
|
||||
slug: string;
|
||||
type:
|
||||
| 'Contents'
|
||||
| 'LibraryAudio'
|
||||
| 'LibraryVideo'
|
||||
| 'LibraryTextual'
|
||||
| 'LibraryGroup'
|
||||
| 'Library'
|
||||
| 'Weapons'
|
||||
| 'GamePlatforms'
|
||||
| 'Categories'
|
||||
| 'Wordings';
|
||||
translations?: CategoryTranslations;
|
||||
}
|
||||
export interface Language {
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
export interface Content {
|
||||
id: string;
|
||||
slug: string;
|
||||
thumbnail?: string | Image;
|
||||
testing?: (Section_1 | Collapsible_1 | Columns_1 | Tabs_1 | Accordion_1 | TextBlock | TranscriptBlock)[];
|
||||
thumbnail?: string | ContentThumbnail;
|
||||
categories?:
|
||||
| {
|
||||
value: string;
|
||||
relationTo: 'tags';
|
||||
relationTo: 'keys';
|
||||
}[]
|
||||
| {
|
||||
value: Tag;
|
||||
relationTo: 'tags';
|
||||
value: Key;
|
||||
relationTo: 'keys';
|
||||
}[];
|
||||
type?: {
|
||||
value: string | Tag;
|
||||
relationTo: 'tags';
|
||||
value: string | Key;
|
||||
relationTo: 'keys';
|
||||
};
|
||||
translations: {
|
||||
language: string | Language;
|
||||
|
@ -91,9 +175,7 @@ export interface Content {
|
|||
textTranscribers?: string[] | Recorder[];
|
||||
textTranslators?: string[] | Recorder[];
|
||||
textProofreaders?: string[] | Recorder[];
|
||||
textContent?: {
|
||||
[k: string]: unknown;
|
||||
}[];
|
||||
textContent?: (TextBlock | Section | Tabs | TranscriptBlock | QuoteBlock)[];
|
||||
textNotes?: string;
|
||||
video?: string | File;
|
||||
videoNotes?: string;
|
||||
|
@ -104,29 +186,71 @@ export interface Content {
|
|||
createdAt: string;
|
||||
_status?: 'draft' | 'published';
|
||||
}
|
||||
export interface Section_1 {
|
||||
content?: (Section_2 | Collapsible_2 | Columns_2 | Tabs_2 | Accordion_2 | TextBlock | TranscriptBlock)[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'section_1';
|
||||
export interface ContentThumbnail {
|
||||
id: string;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
url?: string;
|
||||
filename?: string;
|
||||
mimeType?: string;
|
||||
filesize?: number;
|
||||
width?: number;
|
||||
height?: number;
|
||||
sizes?: {
|
||||
og?: {
|
||||
url?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
mimeType?: string;
|
||||
filesize?: number;
|
||||
filename?: string;
|
||||
};
|
||||
medium?: {
|
||||
url?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
mimeType?: string;
|
||||
filesize?: number;
|
||||
filename?: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
export interface Section_2 {
|
||||
content?: (Section_3 | Collapsible_3 | Columns_3 | Tabs_3 | Accordion_3 | TextBlock | TranscriptBlock)[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'section_2';
|
||||
export interface Recorder {
|
||||
id: string;
|
||||
username: string;
|
||||
avatar?: string | RecorderThumbnail;
|
||||
languages?: string[] | Language[];
|
||||
biographies?: RecorderBiographies;
|
||||
anonymize: boolean;
|
||||
}
|
||||
export interface Section_3 {
|
||||
content?: (Section_4 | Collapsible_4 | Columns_4 | Tabs_4 | Accordion_4 | TextBlock | TranscriptBlock)[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'section_3';
|
||||
}
|
||||
export interface Section_4 {
|
||||
content?: (TextBlock | TranscriptBlock)[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'section_4';
|
||||
export interface RecorderThumbnail {
|
||||
id: string;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
url?: string;
|
||||
filename?: string;
|
||||
mimeType?: string;
|
||||
filesize?: number;
|
||||
width?: number;
|
||||
height?: number;
|
||||
sizes?: {
|
||||
og?: {
|
||||
url?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
mimeType?: string;
|
||||
filesize?: number;
|
||||
filename?: string;
|
||||
};
|
||||
small?: {
|
||||
url?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
mimeType?: string;
|
||||
filesize?: number;
|
||||
filename?: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
export interface TextBlock {
|
||||
content: {
|
||||
|
@ -136,6 +260,42 @@ export interface TextBlock {
|
|||
blockName?: string;
|
||||
blockType: 'textBlock';
|
||||
}
|
||||
export interface Section {
|
||||
content?: (Section_Section | Section_Tabs | TranscriptBlock | QuoteBlock | TextBlock)[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'section';
|
||||
}
|
||||
export interface Section_Section {
|
||||
content?: (Section_Section_Section | Section_Section_Tabs | TranscriptBlock | QuoteBlock | TextBlock)[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'section';
|
||||
}
|
||||
export interface Section_Section_Section {
|
||||
content?: (
|
||||
| Section_Section_Section_Section
|
||||
| Section_Section_Section_Tabs
|
||||
| TranscriptBlock
|
||||
| QuoteBlock
|
||||
| TextBlock
|
||||
)[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'section';
|
||||
}
|
||||
export interface Section_Section_Section_Section {
|
||||
content?: (Section_Section_Section_Section_Section | TranscriptBlock | QuoteBlock | TextBlock)[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'section';
|
||||
}
|
||||
export interface Section_Section_Section_Section_Section {
|
||||
content?: (TranscriptBlock | QuoteBlock | TextBlock)[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'section';
|
||||
}
|
||||
export interface TranscriptBlock {
|
||||
lines: (LineBlock | CueBlock)[];
|
||||
id?: string;
|
||||
|
@ -156,182 +316,122 @@ export interface CueBlock {
|
|||
blockName?: string;
|
||||
blockType: 'cueBlock';
|
||||
}
|
||||
export interface Collapsible_4 {
|
||||
content?: (TextBlock | TranscriptBlock)[];
|
||||
export interface QuoteBlock {
|
||||
from: string;
|
||||
content: {
|
||||
[k: string]: unknown;
|
||||
}[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'collapsible_4';
|
||||
blockType: 'quoteBlock';
|
||||
}
|
||||
export interface Columns_4 {
|
||||
columns?: Column_4[];
|
||||
export interface Section_Section_Section_Tabs {
|
||||
tabs?: Section_Section_Section_Tabs_Tab[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'columns_4';
|
||||
blockType: 'tabs';
|
||||
}
|
||||
export interface Column_4 {
|
||||
content?: (TextBlock | TranscriptBlock)[];
|
||||
export interface Section_Section_Section_Tabs_Tab {
|
||||
content?: (Section_Section_Section_Tabs_Tab_Section | TranscriptBlock | QuoteBlock | TextBlock)[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'column_4';
|
||||
blockType: 'tab';
|
||||
}
|
||||
export interface Tabs_4 {
|
||||
tabs?: Tab_4[];
|
||||
export interface Section_Section_Section_Tabs_Tab_Section {
|
||||
content?: (TranscriptBlock | QuoteBlock | TextBlock)[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'tabs_4';
|
||||
blockType: 'section';
|
||||
}
|
||||
export interface Tab_4 {
|
||||
content?: (TextBlock | TranscriptBlock)[];
|
||||
export interface Section_Section_Tabs {
|
||||
tabs?: Section_Section_Tabs_Tab[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'tab_4';
|
||||
blockType: 'tabs';
|
||||
}
|
||||
export interface Accordion_4 {
|
||||
content?: Collapsible_5[];
|
||||
export interface Section_Section_Tabs_Tab {
|
||||
content?: (Section_Section_Tabs_Tab_Section | TranscriptBlock | QuoteBlock | TextBlock)[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'accordion_4';
|
||||
blockType: 'tab';
|
||||
}
|
||||
export interface Collapsible_5 {
|
||||
content?: (TextBlock | TranscriptBlock)[];
|
||||
export interface Section_Section_Tabs_Tab_Section {
|
||||
content?: (Section_Section_Tabs_Tab_Section_Section | TranscriptBlock | QuoteBlock | TextBlock)[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'collapsible_5';
|
||||
blockType: 'section';
|
||||
}
|
||||
export interface Collapsible_3 {
|
||||
content?: (Section_4 | Collapsible_4 | Columns_4 | Tabs_4 | Accordion_4 | TextBlock | TranscriptBlock)[];
|
||||
export interface Section_Section_Tabs_Tab_Section_Section {
|
||||
content?: (TranscriptBlock | QuoteBlock | TextBlock)[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'collapsible_3';
|
||||
blockType: 'section';
|
||||
}
|
||||
export interface Columns_3 {
|
||||
columns?: Column_3[];
|
||||
export interface Section_Tabs {
|
||||
tabs?: Section_Tabs_Tab[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'columns_3';
|
||||
blockType: 'tabs';
|
||||
}
|
||||
export interface Column_3 {
|
||||
content?: (Section_4 | Collapsible_4 | Columns_4 | Tabs_4 | Accordion_4 | TextBlock | TranscriptBlock)[];
|
||||
export interface Section_Tabs_Tab {
|
||||
content?: (Section_Tabs_Tab_Section | TranscriptBlock | QuoteBlock | TextBlock)[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'column_3';
|
||||
blockType: 'tab';
|
||||
}
|
||||
export interface Tabs_3 {
|
||||
tabs?: Tab_3[];
|
||||
export interface Section_Tabs_Tab_Section {
|
||||
content?: (Section_Tabs_Tab_Section_Section | TranscriptBlock | QuoteBlock | TextBlock)[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'tabs_3';
|
||||
blockType: 'section';
|
||||
}
|
||||
export interface Tab_3 {
|
||||
content?: (Section_4 | Collapsible_4 | Columns_4 | Tabs_4 | Accordion_4 | TextBlock | TranscriptBlock)[];
|
||||
export interface Section_Tabs_Tab_Section_Section {
|
||||
content?: (Section_Tabs_Tab_Section_Section_Section | TranscriptBlock | QuoteBlock | TextBlock)[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'tab_3';
|
||||
blockType: 'section';
|
||||
}
|
||||
export interface Accordion_3 {
|
||||
content?: Collapsible_4[];
|
||||
export interface Section_Tabs_Tab_Section_Section_Section {
|
||||
content?: (TranscriptBlock | QuoteBlock | TextBlock)[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'accordion_3';
|
||||
blockType: 'section';
|
||||
}
|
||||
export interface Collapsible_2 {
|
||||
content?: (Section_3 | Collapsible_3 | Columns_3 | Tabs_3 | Accordion_3 | TextBlock | TranscriptBlock)[];
|
||||
export interface Tabs {
|
||||
tabs?: Tabs_Tab[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'collapsible_2';
|
||||
blockType: 'tabs';
|
||||
}
|
||||
export interface Columns_2 {
|
||||
columns?: Column_2[];
|
||||
export interface Tabs_Tab {
|
||||
content?: (Tabs_Tab_Section | TranscriptBlock | QuoteBlock | TextBlock)[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'columns_2';
|
||||
blockType: 'tab';
|
||||
}
|
||||
export interface Column_2 {
|
||||
content?: (Section_3 | Collapsible_3 | Columns_3 | Tabs_3 | Accordion_3 | TextBlock | TranscriptBlock)[];
|
||||
export interface Tabs_Tab_Section {
|
||||
content?: (Tabs_Tab_Section_Section | TranscriptBlock | QuoteBlock | TextBlock)[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'column_2';
|
||||
blockType: 'section';
|
||||
}
|
||||
export interface Tabs_2 {
|
||||
tabs?: Tab_2[];
|
||||
export interface Tabs_Tab_Section_Section {
|
||||
content?: (Tabs_Tab_Section_Section_Section | TranscriptBlock | QuoteBlock | TextBlock)[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'tabs_2';
|
||||
blockType: 'section';
|
||||
}
|
||||
export interface Tab_2 {
|
||||
content?: (Section_3 | Collapsible_3 | Columns_3 | Tabs_3 | Accordion_3 | TextBlock | TranscriptBlock)[];
|
||||
export interface Tabs_Tab_Section_Section_Section {
|
||||
content?: (Tabs_Tab_Section_Section_Section_Section | TranscriptBlock | QuoteBlock | TextBlock)[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'tab_2';
|
||||
blockType: 'section';
|
||||
}
|
||||
export interface Accordion_2 {
|
||||
content?: Collapsible_3[];
|
||||
export interface Tabs_Tab_Section_Section_Section_Section {
|
||||
content?: (TranscriptBlock | QuoteBlock | TextBlock)[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'accordion_2';
|
||||
}
|
||||
export interface Collapsible_1 {
|
||||
content?: (Section_2 | Collapsible_2 | Columns_2 | Tabs_2 | Accordion_2 | TextBlock | TranscriptBlock)[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'collapsible_1';
|
||||
}
|
||||
export interface Columns_1 {
|
||||
columns?: Column_1[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'columns_1';
|
||||
}
|
||||
export interface Column_1 {
|
||||
content?: (Section_2 | Collapsible_2 | Columns_2 | Tabs_2 | Accordion_2 | TextBlock | TranscriptBlock)[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'column_1';
|
||||
}
|
||||
export interface Tabs_1 {
|
||||
tabs?: Tab_1[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'tabs_1';
|
||||
}
|
||||
export interface Tab_1 {
|
||||
content?: (Section_2 | Collapsible_2 | Columns_2 | Tabs_2 | Accordion_2 | TextBlock | TranscriptBlock)[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'tab_1';
|
||||
}
|
||||
export interface Accordion_1 {
|
||||
content?: Collapsible_2[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'accordion_1';
|
||||
}
|
||||
export interface Tag {
|
||||
id: string;
|
||||
slug: string;
|
||||
type:
|
||||
| 'Contents'
|
||||
| 'LibraryAudio'
|
||||
| 'LibraryVideo'
|
||||
| 'LibraryTextual'
|
||||
| 'LibraryGroup'
|
||||
| 'Library'
|
||||
| 'Weapons'
|
||||
| 'GamePlatforms'
|
||||
| 'Categories';
|
||||
translations?: CategoryTranslations;
|
||||
}
|
||||
export interface Language {
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
export interface Recorder {
|
||||
id: string;
|
||||
avatar?: string | Image;
|
||||
username: string;
|
||||
anonymize: boolean;
|
||||
languages?: string[] | Language[];
|
||||
biographies?: RecorderBiographies;
|
||||
blockType: 'section';
|
||||
}
|
||||
export interface File {
|
||||
id: string;
|
||||
|
@ -340,10 +440,33 @@ export interface File {
|
|||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
export interface ContentFolder {
|
||||
id: string;
|
||||
slug: string;
|
||||
translations?: ContentFoldersTranslation;
|
||||
subfolders?:
|
||||
| {
|
||||
value: string;
|
||||
relationTo: 'content-folders';
|
||||
}[]
|
||||
| {
|
||||
value: ContentFolder;
|
||||
relationTo: 'content-folders';
|
||||
}[];
|
||||
contents?:
|
||||
| {
|
||||
value: string;
|
||||
relationTo: 'contents';
|
||||
}[]
|
||||
| {
|
||||
value: Content;
|
||||
relationTo: 'contents';
|
||||
}[];
|
||||
}
|
||||
export interface Post {
|
||||
id: string;
|
||||
slug: string;
|
||||
thumbnail?: string | Image;
|
||||
thumbnail?: string | PostThumbnail;
|
||||
authors:
|
||||
| {
|
||||
value: string;
|
||||
|
@ -356,11 +479,11 @@ export interface Post {
|
|||
categories?:
|
||||
| {
|
||||
value: string;
|
||||
relationTo: 'tags';
|
||||
relationTo: 'keys';
|
||||
}[]
|
||||
| {
|
||||
value: Tag;
|
||||
relationTo: 'tags';
|
||||
value: Key;
|
||||
relationTo: 'keys';
|
||||
}[];
|
||||
translations: {
|
||||
language: string | Language;
|
||||
|
@ -375,10 +498,40 @@ export interface Post {
|
|||
id?: string;
|
||||
}[];
|
||||
publishedDate: string;
|
||||
hidden?: boolean;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
_status?: 'draft' | 'published';
|
||||
}
|
||||
export interface PostThumbnail {
|
||||
id: string;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
url?: string;
|
||||
filename?: string;
|
||||
mimeType?: string;
|
||||
filesize?: number;
|
||||
width?: number;
|
||||
height?: number;
|
||||
sizes?: {
|
||||
og?: {
|
||||
url?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
mimeType?: string;
|
||||
filesize?: number;
|
||||
filename?: string;
|
||||
};
|
||||
medium?: {
|
||||
url?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
mimeType?: string;
|
||||
filesize?: number;
|
||||
filename?: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
export interface User {
|
||||
id: string;
|
||||
email: string;
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
import { Block, BlockField } from "payload/types";
|
||||
|
||||
const isDefined = <T>(value: T | null | undefined): value is T =>
|
||||
value !== null && value !== undefined;
|
||||
|
||||
const recursionFieldName = "recursion" as const;
|
||||
|
||||
type BlockConfig<T extends string> = {
|
||||
root: boolean;
|
||||
block: RecursiveBlock<T> | Block;
|
||||
};
|
||||
|
||||
type RecursiveBlock<T extends string> = Omit<Block, "fields" | "interfaceName"> & {
|
||||
[recursionFieldName]: Omit<BlockField, "blocks" | "type"> & {
|
||||
newDepth: (currentDepth: number) => number;
|
||||
condition: (currentDepth: number, parents: T[]) => boolean;
|
||||
blocks: T[];
|
||||
};
|
||||
fields?: Block["fields"];
|
||||
};
|
||||
|
||||
export type BlocksConfig<T extends string> = Record<T, BlockConfig<T>>;
|
||||
|
||||
export const generateBlocks = <T extends string>(blocksConfig: BlocksConfig<T>): Block[] => {
|
||||
const isRecursiveBlock = (block: RecursiveBlock<T> | Block): block is RecursiveBlock<T> =>
|
||||
recursionFieldName in block;
|
||||
|
||||
const getInterfaceName = (parents: T[], currentBlockName: T): string => {
|
||||
const capitalize = (text: string): string => {
|
||||
if (text.length === 0) return text;
|
||||
const [firstLetter, ...rest] = text;
|
||||
return [firstLetter.toUpperCase(), ...rest].join("");
|
||||
};
|
||||
return [...parents, currentBlockName]
|
||||
.map((blockName) => blocksConfig[blockName].block.slug)
|
||||
.map(capitalize)
|
||||
.join("_");
|
||||
};
|
||||
|
||||
const getCurrentDepth = (parents: T[]): number =>
|
||||
parents.reduce((acc, blockName) => {
|
||||
const block = blocksConfig[blockName].block;
|
||||
if (!isRecursiveBlock(block)) return acc;
|
||||
return block[recursionFieldName].newDepth(acc);
|
||||
}, 1);
|
||||
|
||||
const generateRecursiveBlocks = (parents: T[], blockName: T): Block | undefined => {
|
||||
const currentDepth = getCurrentDepth(parents);
|
||||
const block = blocksConfig[blockName].block;
|
||||
if (!isRecursiveBlock(block)) return block;
|
||||
|
||||
const {
|
||||
slug,
|
||||
labels,
|
||||
fields = [],
|
||||
recursion: { newDepth, blocks, condition, ...fieldsProps },
|
||||
} = block;
|
||||
|
||||
const generatedBlocks = blocks
|
||||
.filter((blockName) => {
|
||||
const block = blocksConfig[blockName].block;
|
||||
if (!isRecursiveBlock(block)) return true;
|
||||
return block[recursionFieldName].condition(currentDepth, parents);
|
||||
})
|
||||
.map((nextBlockName) => generateRecursiveBlocks([...parents, blockName], nextBlockName))
|
||||
.filter(isDefined);
|
||||
|
||||
// Cut dead branches (branches without leafs)
|
||||
if (generatedBlocks.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
slug,
|
||||
interfaceName: getInterfaceName(parents, blockName),
|
||||
labels,
|
||||
fields: [
|
||||
...fields,
|
||||
{
|
||||
...fieldsProps,
|
||||
type: "blocks",
|
||||
blocks: generatedBlocks,
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
const rootBlockNames = Object.entries<BlockConfig<T>>(blocksConfig)
|
||||
.filter(([_, blockConfig]) => blockConfig.root)
|
||||
.map(([blockName]) => blockName as T);
|
||||
|
||||
return rootBlockNames
|
||||
.map((blockName) => generateRecursiveBlocks([], blockName))
|
||||
.filter(isDefined);
|
||||
};
|
|
@ -1,11 +1,8 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"target": "ES2022",
|
||||
"moduleResolution": "NodeNext",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"strict": false,
|
||||
"esModuleInterop": true,
|
||||
|
@ -14,21 +11,13 @@
|
|||
"rootDir": "./src",
|
||||
"jsx": "react",
|
||||
"paths": {
|
||||
"payload/generated-types": [
|
||||
"./src/payload-types.ts",
|
||||
],
|
||||
"payload/generated-types": ["./src/payload-types.ts"]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"dist",
|
||||
"build",
|
||||
],
|
||||
"include": ["src"],
|
||||
"exclude": ["node_modules", "dist", "build"],
|
||||
"ts-node": {
|
||||
"transpileOnly": true,
|
||||
"swc": true,
|
||||
"swc": true
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue