Added more collections
This commit is contained in:
parent
138d7ae571
commit
84f0b20999
|
@ -13,7 +13,8 @@
|
|||
"dotenv": "^8.2.0",
|
||||
"express": "^4.17.1",
|
||||
"iso-639-1": "^2.1.15",
|
||||
"payload": "^1.11.1"
|
||||
"payload": "^1.11.1",
|
||||
"slugify": "^1.6.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/express": "^4.17.9",
|
||||
|
@ -8504,6 +8505,14 @@
|
|||
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.0.6.tgz",
|
||||
"integrity": "sha512-FOyLWWVjG+aC0UqG76V53yAWdXfH8bO6FNmyZOuUrzDzK8DI3/JRY25UD7+g49JWM1LXwymsKERB+DzI0dTEQA=="
|
||||
},
|
||||
"node_modules/slugify": {
|
||||
"version": "1.6.6",
|
||||
"resolved": "https://registry.npmjs.org/slugify/-/slugify-1.6.6.tgz",
|
||||
"integrity": "sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw==",
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/smart-buffer": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
|
||||
|
|
|
@ -22,7 +22,8 @@
|
|||
"dotenv": "^8.2.0",
|
||||
"express": "^4.17.1",
|
||||
"iso-639-1": "^2.1.15",
|
||||
"payload": "^1.11.1"
|
||||
"payload": "^1.11.1",
|
||||
"slugify": "^1.6.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/express": "^4.17.9",
|
||||
|
|
|
@ -1,48 +0,0 @@
|
|||
import { CollectionConfig } from "payload/types";
|
||||
import { localizedFields } from "../../elements/translatedFields/translatedFields";
|
||||
|
||||
const fields = {
|
||||
id: "id",
|
||||
translations: "translations",
|
||||
name: "name",
|
||||
short: "short",
|
||||
} as const satisfies Record<string, string>;
|
||||
|
||||
const labels = {
|
||||
singular: "Category",
|
||||
plural: "Categories",
|
||||
} as const satisfies { singular: string; plural: string };
|
||||
|
||||
export const Categories: CollectionConfig = {
|
||||
slug: labels.plural,
|
||||
labels,
|
||||
typescript: { interface: labels.singular },
|
||||
defaultSort: fields.id,
|
||||
admin: {
|
||||
useAsTitle: fields.id,
|
||||
defaultColumns: [fields.id, fields.translations],
|
||||
},
|
||||
timestamps: false,
|
||||
fields: [
|
||||
{
|
||||
name: fields.id,
|
||||
type: "text",
|
||||
},
|
||||
localizedFields({
|
||||
name: fields.translations,
|
||||
interfaceName: "CategoryTranslations",
|
||||
admin: {
|
||||
useAsTitle: fields.name,
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
{ name: fields.name, type: "text", required: true },
|
||||
{ name: fields.short, type: "text" },
|
||||
],
|
||||
},
|
||||
],
|
||||
}),
|
||||
],
|
||||
};
|
|
@ -0,0 +1,169 @@
|
|||
import { Block, BlockField } from "payload/types";
|
||||
import { cueBlock } from "./cueBlock";
|
||||
import { textBlock } from "./textBlock";
|
||||
import { transcriptBlock } from "./transcriptBlock";
|
||||
import { lineBlock } from "./lineBlock";
|
||||
|
||||
const INITIAL_DEPTH = 1;
|
||||
const MAX_DEPTH = 4;
|
||||
|
||||
enum BlockName {
|
||||
Text = "Text",
|
||||
Section = "Section",
|
||||
Tabs = "Tabs",
|
||||
Tab = "Tab",
|
||||
Columns = "Columns",
|
||||
Column = "Column",
|
||||
Transcript = "Transcript",
|
||||
Collapsible = "Collapsible",
|
||||
Accordion = "Accordion",
|
||||
Line = "Line",
|
||||
Cue = "Cue",
|
||||
}
|
||||
|
||||
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, {
|
||||
slug: "section",
|
||||
interfaceName: "Section",
|
||||
labels: { singular: "Section", plural: "Sections" },
|
||||
fields: {
|
||||
name: "content",
|
||||
newDepth: (depth) => depth + 1,
|
||||
blocks: blocksChildren.Section,
|
||||
},
|
||||
}),
|
||||
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, {
|
||||
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",
|
||||
newDepth: (depth) => depth,
|
||||
blocks: blocksChildren.Columns,
|
||||
},
|
||||
}),
|
||||
Column: (currentDepth) =>
|
||||
implementationForRecursiveBlocks(currentDepth, {
|
||||
slug: "column",
|
||||
interfaceName: "Column",
|
||||
labels: { singular: "Column", plural: "Columns" },
|
||||
fields: { name: "content", newDepth: (depth) => depth + 1, blocks: blocksChildren.Column },
|
||||
}),
|
||||
};
|
||||
|
||||
export const rootBlocks: Block[] = rootBlocksNames
|
||||
.filter((block) => block in implementations)
|
||||
.map((block) => implementations[block](INITIAL_DEPTH));
|
|
@ -0,0 +1,19 @@
|
|||
import { Block } from "payload/types";
|
||||
|
||||
export const cueBlock: Block = {
|
||||
slug: "cueBlock",
|
||||
interfaceName: "CueBlock",
|
||||
labels: { singular: "Cue", plural: "Cues" },
|
||||
fields: [
|
||||
{
|
||||
name: "content",
|
||||
label: false,
|
||||
type: "textarea",
|
||||
required: true,
|
||||
admin: {
|
||||
description:
|
||||
"Parenthesis will automatically be added around cues. You don't have to include them here.",
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
|
@ -0,0 +1,20 @@
|
|||
import { Block } from "payload/types";
|
||||
|
||||
export const lineBlock: Block = {
|
||||
slug: "lineBlock",
|
||||
interfaceName: "LineBlock",
|
||||
labels: { singular: "Line", plural: "Lines" },
|
||||
fields: [
|
||||
{
|
||||
name: "content",
|
||||
label: false,
|
||||
type: "richText",
|
||||
required: true,
|
||||
admin: {
|
||||
hideGutter: true,
|
||||
elements: [],
|
||||
leaves: ["bold", "italic", "underline", "strikethrough", "code"],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
|
@ -0,0 +1,19 @@
|
|||
import { Block } from "payload/types";
|
||||
|
||||
export const textBlock: Block = {
|
||||
slug: "textBlock",
|
||||
interfaceName: "TextBlock",
|
||||
labels: { singular: "Text", plural: "Texts" },
|
||||
fields: [
|
||||
{
|
||||
name: "content",
|
||||
type: "richText",
|
||||
label: false,
|
||||
required: true,
|
||||
admin: {
|
||||
hideGutter: true,
|
||||
elements: ["ul", "ol", "indent", "link", "relationship", "upload", "blockquote"],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
|
@ -0,0 +1,19 @@
|
|||
import { Block } from "payload/types";
|
||||
import { lineBlock } from "./lineBlock";
|
||||
import { cueBlock } from "./cueBlock";
|
||||
|
||||
export const transcriptBlock: Block = {
|
||||
slug: "transcriptBlock",
|
||||
interfaceName: "TranscriptBlock",
|
||||
labels: { singular: "Transcript", plural: "Transcripts" },
|
||||
fields: [
|
||||
{
|
||||
name: "lines",
|
||||
type: "blocks",
|
||||
required: true,
|
||||
minRows: 1,
|
||||
admin: { initCollapsed: true },
|
||||
blocks: [lineBlock, cueBlock],
|
||||
},
|
||||
],
|
||||
};
|
|
@ -0,0 +1,208 @@
|
|||
import { CollectionConfig } from "payload/types";
|
||||
import { collectionSlug } from "../../utils/string";
|
||||
import { CollectionGroup, FileTypes, TagsTypes } from "../../constants";
|
||||
import { slugField } from "../../fields/slugField/slugField";
|
||||
import { imageField } from "../../fields/imageField/imageField";
|
||||
import { Tags } from "../Tags/Tags";
|
||||
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";
|
||||
|
||||
const fields = {
|
||||
slug: "slug",
|
||||
thumbnail: "thumbnail",
|
||||
categories: "categories",
|
||||
type: "type",
|
||||
translations: "translations",
|
||||
pretitle: "pretitle",
|
||||
title: "title",
|
||||
subtitle: "subtitle",
|
||||
summary: "summary",
|
||||
textContent: "textContent",
|
||||
textTranscribers: "textTranscribers",
|
||||
textTranslators: "textTranslators",
|
||||
textProofreaders: "textProofreaders",
|
||||
textNotes: "textNotes",
|
||||
video: "video",
|
||||
videoNotes: "videoNotes",
|
||||
audio: "audio",
|
||||
audioNotes: "videoNotes",
|
||||
} as const satisfies Record<string, string>;
|
||||
|
||||
const labels = {
|
||||
singular: "Content",
|
||||
plural: "Contents",
|
||||
} as const satisfies { singular: string; plural: string };
|
||||
|
||||
export const Contents: CollectionConfig = {
|
||||
slug: collectionSlug(labels.plural),
|
||||
labels,
|
||||
typescript: { interface: labels.singular },
|
||||
defaultSort: fields.slug,
|
||||
admin: {
|
||||
useAsTitle: fields.slug,
|
||||
defaultColumns: [fields.slug, fields.thumbnail, fields.categories],
|
||||
group: CollectionGroup.Collections,
|
||||
preview: (doc) => `https://accords-library.com/contents/${doc.slug}`,
|
||||
},
|
||||
timestamps: true,
|
||||
versions: { drafts: true },
|
||||
fields: [
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
slugField({ name: fields.slug, admin: { width: "50%" } }),
|
||||
imageField({ name: fields.thumbnail, 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 } },
|
||||
hasMany: true,
|
||||
admin: { allowCreate: false, width: "50%" },
|
||||
},
|
||||
{
|
||||
name: fields.type,
|
||||
type: "relationship",
|
||||
relationTo: [Tags.slug],
|
||||
filterOptions: { type: { equals: TagsTypes.Contents } },
|
||||
admin: { allowCreate: false, width: "50%" },
|
||||
},
|
||||
],
|
||||
},
|
||||
localizedFields({
|
||||
name: fields.translations,
|
||||
admin: { useAsTitle: fields.title, hasSourceLanguage: true },
|
||||
required: true,
|
||||
minRows: 1,
|
||||
fields: [
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
{ name: fields.pretitle, type: "text" },
|
||||
{ name: fields.title, type: "text", required: true },
|
||||
{ name: fields.subtitle, type: "text" },
|
||||
],
|
||||
},
|
||||
{ name: fields.summary, type: "textarea" },
|
||||
{
|
||||
type: "tabs",
|
||||
admin: {
|
||||
condition: (_, siblingData) =>
|
||||
isDefined(siblingData.language) && isDefined(siblingData.sourceLanguage),
|
||||
},
|
||||
tabs: [
|
||||
{
|
||||
label: "Text",
|
||||
fields: [
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
{
|
||||
name: fields.textTranscribers,
|
||||
label: "Transcribers",
|
||||
type: "relationship",
|
||||
relationTo: Recorders.slug,
|
||||
hasMany: true,
|
||||
admin: {
|
||||
condition: (_, siblingData) =>
|
||||
siblingData.language === siblingData.sourceLanguage,
|
||||
width: "50%",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: fields.textTranslators,
|
||||
label: "Translators",
|
||||
type: "relationship",
|
||||
relationTo: Recorders.slug,
|
||||
hasMany: true,
|
||||
admin: {
|
||||
condition: (_, siblingData) =>
|
||||
siblingData.language !== siblingData.sourceLanguage,
|
||||
width: "50%",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: fields.textProofreaders,
|
||||
label: "Proofreaders",
|
||||
type: "relationship",
|
||||
relationTo: Recorders.slug,
|
||||
hasMany: true,
|
||||
admin: { width: "50%" },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: fields.textContent,
|
||||
label: "Content",
|
||||
type: "richText",
|
||||
admin: { hideGutter: true },
|
||||
},
|
||||
{
|
||||
name: fields.textNotes,
|
||||
label: "Notes",
|
||||
type: "textarea",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Video",
|
||||
fields: [
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
fileField({
|
||||
name: fields.video,
|
||||
filterOptions: { type: { equals: FileTypes.ContentVideo } },
|
||||
admin: { width: "50%" },
|
||||
}),
|
||||
{
|
||||
name: fields.videoNotes,
|
||||
label: "Notes",
|
||||
type: "textarea",
|
||||
admin: { width: "50%" },
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Audio",
|
||||
fields: [
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
fileField({
|
||||
name: fields.audio,
|
||||
filterOptions: { type: { equals: FileTypes.ContentAudio } },
|
||||
admin: { width: "50%" },
|
||||
}),
|
||||
{
|
||||
name: fields.audioNotes,
|
||||
label: "Notes",
|
||||
type: "textarea",
|
||||
admin: { width: "50%" },
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}),
|
||||
],
|
||||
};
|
|
@ -0,0 +1,38 @@
|
|||
import { CollectionConfig } from "payload/types";
|
||||
import { CollectionGroup, FileTypes } from "../../constants";
|
||||
import { collectionSlug } from "../../utils/string";
|
||||
|
||||
const fields = {
|
||||
filename: "filename",
|
||||
type: "type",
|
||||
} as const satisfies Record<string, string>;
|
||||
|
||||
const labels = {
|
||||
singular: "File",
|
||||
plural: "Files",
|
||||
} as const satisfies { singular: string; plural: string };
|
||||
|
||||
export const Files: CollectionConfig = {
|
||||
slug: collectionSlug(labels.plural),
|
||||
labels,
|
||||
typescript: { interface: labels.singular },
|
||||
defaultSort: fields.filename,
|
||||
admin: {
|
||||
useAsTitle: fields.filename,
|
||||
group: CollectionGroup.Media,
|
||||
},
|
||||
|
||||
fields: [
|
||||
{
|
||||
name: fields.filename,
|
||||
required: true,
|
||||
type: "text",
|
||||
},
|
||||
{
|
||||
name: fields.type,
|
||||
type: "select",
|
||||
required: true,
|
||||
options: Object.entries(FileTypes).map(([value, label]) => ({ label, value })),
|
||||
},
|
||||
],
|
||||
};
|
|
@ -1,4 +1,6 @@
|
|||
import { CollectionConfig } from "payload/types";
|
||||
import { CollectionGroup } from "../../constants";
|
||||
import { collectionSlug } from "../../utils/string";
|
||||
|
||||
const fields = {
|
||||
filename: "filename",
|
||||
|
@ -13,13 +15,13 @@ const labels = {
|
|||
} as const satisfies { singular: string; plural: string };
|
||||
|
||||
export const Images: CollectionConfig = {
|
||||
slug: labels.plural,
|
||||
slug: collectionSlug(labels.plural),
|
||||
labels,
|
||||
typescript: { interface: labels.singular },
|
||||
defaultSort: fields.filename,
|
||||
admin: {
|
||||
useAsTitle: fields.filename,
|
||||
group: "Media",
|
||||
group: CollectionGroup.Media,
|
||||
},
|
||||
|
||||
upload: {
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import { CollectionConfig } from "payload/types";
|
||||
import { CollectionGroup } from "../constants";
|
||||
import { collectionSlug } from "../utils/string";
|
||||
|
||||
const fields = {
|
||||
id: "id",
|
||||
|
@ -11,13 +13,14 @@ const labels = {
|
|||
} as const satisfies { singular: string; plural: string };
|
||||
|
||||
export const Languages: CollectionConfig = {
|
||||
slug: labels.plural,
|
||||
slug: collectionSlug(labels.plural),
|
||||
labels,
|
||||
typescript: { interface: labels.singular },
|
||||
defaultSort: fields.name,
|
||||
admin: {
|
||||
useAsTitle: fields.name,
|
||||
defaultColumns: [fields.name, fields.id],
|
||||
group: CollectionGroup.Meta,
|
||||
},
|
||||
timestamps: false,
|
||||
fields: [
|
||||
|
|
|
@ -0,0 +1,148 @@
|
|||
import { CollectionConfig } from "payload/types";
|
||||
import { CollectionGroup } 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";
|
||||
|
||||
const fields = {
|
||||
status: "status",
|
||||
slug: "slug",
|
||||
thumbnail: "thumbnail",
|
||||
pretitle: "pretitle",
|
||||
title: "title",
|
||||
subtitle: "subtitle",
|
||||
rootItem: "rootItem",
|
||||
primary: "primary",
|
||||
digital: "digital",
|
||||
downloadable: "downloadable",
|
||||
size: "size",
|
||||
width: "width",
|
||||
height: "height",
|
||||
thickness: "thickness",
|
||||
} as const satisfies Record<string, string>;
|
||||
|
||||
const labels = {
|
||||
singular: "Library Item",
|
||||
plural: "Library Items",
|
||||
} as const satisfies { singular: string; plural: string };
|
||||
|
||||
const validateSizeValue = (value?: number) => {
|
||||
if (isDefined(value) && value <= 0) return "This value must be greater than 0";
|
||||
return true;
|
||||
};
|
||||
|
||||
const validateRequiredSizeValue = (value?: number) => {
|
||||
if (isUndefined(value)) return "This field is required.";
|
||||
if (value <= 0) return "This value must be greater than 0.";
|
||||
return true;
|
||||
};
|
||||
|
||||
export const LibraryItems: CollectionConfig = {
|
||||
slug: collectionSlug(labels.plural),
|
||||
labels,
|
||||
typescript: { interface: labels.singular },
|
||||
defaultSort: fields.slug,
|
||||
admin: {
|
||||
useAsTitle: fields.slug,
|
||||
defaultColumns: [fields.slug, fields.thumbnail, fields.status],
|
||||
group: CollectionGroup.Collections,
|
||||
preview: (doc) => `https://accords-library.com/library/${doc.slug}`,
|
||||
},
|
||||
timestamps: true,
|
||||
versions: { drafts: true },
|
||||
fields: [
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
slugField({ name: fields.slug, admin: { width: "50%" } }),
|
||||
imageField({ name: fields.thumbnail, admin: { width: "50%" } }),
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
{ name: fields.pretitle, type: "text" },
|
||||
{ name: fields.title, type: "text", required: true },
|
||||
{ name: fields.subtitle, type: "text" },
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
{
|
||||
name: fields.rootItem,
|
||||
type: "checkbox",
|
||||
required: true,
|
||||
defaultValue: true,
|
||||
admin: {
|
||||
description: "Only items that can be sold separetely should be root items.",
|
||||
width: "25%",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: fields.primary,
|
||||
type: "checkbox",
|
||||
required: true,
|
||||
defaultValue: true,
|
||||
admin: {
|
||||
description:
|
||||
"A primary item is an official item that focuses primarly on one or more of our Categories.",
|
||||
width: "25%",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: fields.digital,
|
||||
type: "checkbox",
|
||||
required: true,
|
||||
defaultValue: false,
|
||||
admin: {
|
||||
description:
|
||||
"The item is the digital version of another item, or the item is sold only digitally.",
|
||||
width: "25%",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: fields.downloadable,
|
||||
type: "checkbox",
|
||||
required: true,
|
||||
defaultValue: false,
|
||||
admin: {
|
||||
description: "Are the scans available for download?",
|
||||
width: "25%",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "size",
|
||||
type: "group",
|
||||
admin: { condition: (data) => !data.digital },
|
||||
fields: [
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
{
|
||||
name: fields.width,
|
||||
type: "number",
|
||||
validate: validateRequiredSizeValue,
|
||||
admin: { step: 1, width: "33%", description: "in mm." },
|
||||
},
|
||||
{
|
||||
name: fields.height,
|
||||
type: "number",
|
||||
validate: validateRequiredSizeValue,
|
||||
admin: { step: 1, width: "33%", description: "in mm." },
|
||||
},
|
||||
{
|
||||
name: fields.thickness,
|
||||
type: "number",
|
||||
validate: validateSizeValue,
|
||||
admin: { step: 1, width: "33%", description: "in mm." },
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
|
@ -0,0 +1,142 @@
|
|||
import { CollectionConfig } from "payload/types";
|
||||
import { slugField } from "../../fields/slugField/slugField";
|
||||
import { imageField } from "../../fields/imageField/imageField";
|
||||
import { CollectionGroup, TagsTypes } 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 { collectionSlug } from "../../utils/string";
|
||||
|
||||
const fields = {
|
||||
slug: "slug",
|
||||
thumbnail: "thumbnail",
|
||||
categories: "categories",
|
||||
authors: "authors",
|
||||
publishedDate: "publishedDate",
|
||||
translations: "translations",
|
||||
sourceLanguage: "sourceLanguage",
|
||||
title: "title",
|
||||
summary: "summary",
|
||||
content: "content",
|
||||
translators: "translators",
|
||||
proofreaders: "proofreaders",
|
||||
} as const satisfies Record<string, string>;
|
||||
|
||||
const labels = {
|
||||
singular: "Post",
|
||||
plural: "Posts",
|
||||
} as const satisfies { singular: string; plural: string };
|
||||
|
||||
export const Posts: CollectionConfig = {
|
||||
slug: collectionSlug(labels.plural),
|
||||
labels,
|
||||
typescript: { interface: labels.singular },
|
||||
defaultSort: fields.slug,
|
||||
admin: {
|
||||
useAsTitle: fields.slug,
|
||||
defaultColumns: [fields.slug, fields.thumbnail, fields.categories],
|
||||
group: CollectionGroup.Collections,
|
||||
preview: (doc) => `https://accords-library.com/news/${doc.slug}`,
|
||||
},
|
||||
hooks: {
|
||||
beforeValidate: [removeTranslatorsForTranscripts],
|
||||
},
|
||||
timestamps: true,
|
||||
versions: { drafts: true },
|
||||
fields: [
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
slugField({ name: fields.slug, admin: { width: "50%" } }),
|
||||
imageField({ name: fields.thumbnail, admin: { width: "50%" } }),
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
{
|
||||
name: fields.authors,
|
||||
type: "relationship",
|
||||
relationTo: [Recorders.slug],
|
||||
required: true,
|
||||
minRows: 1,
|
||||
hasMany: true,
|
||||
admin: { width: "50%" },
|
||||
},
|
||||
{
|
||||
name: fields.categories,
|
||||
type: "relationship",
|
||||
relationTo: [Tags.slug],
|
||||
filterOptions: { type: { equals: TagsTypes.Categories } },
|
||||
hasMany: true,
|
||||
admin: { allowCreate: false, width: "50%" },
|
||||
},
|
||||
],
|
||||
},
|
||||
localizedFields({
|
||||
name: fields.translations,
|
||||
admin: { useAsTitle: fields.title, hasSourceLanguage: true },
|
||||
required: true,
|
||||
minRows: 1,
|
||||
fields: [
|
||||
{ name: fields.title, type: "text", required: true },
|
||||
{ name: fields.summary, type: "textarea" },
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
{
|
||||
name: fields.translators,
|
||||
type: "relationship",
|
||||
relationTo: Recorders.slug,
|
||||
hasMany: true,
|
||||
admin: {
|
||||
condition: (_, siblingData) => {
|
||||
if (
|
||||
isUndefined(siblingData.language) ||
|
||||
isUndefined(siblingData.sourceLanguage)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return siblingData.language !== siblingData.sourceLanguage;
|
||||
},
|
||||
width: "50%",
|
||||
},
|
||||
validate: (translators, { siblingData }) => {
|
||||
if (isUndefined(siblingData.language) || isUndefined(siblingData.sourceLanguage)) {
|
||||
return true;
|
||||
}
|
||||
if (siblingData.language === siblingData.sourceLanguage) {
|
||||
return true;
|
||||
}
|
||||
if (isDefined(translators) && translators.length > 0) {
|
||||
return true;
|
||||
}
|
||||
return "This field is required when the language is different from the source language.";
|
||||
},
|
||||
},
|
||||
{
|
||||
name: fields.proofreaders,
|
||||
type: "relationship",
|
||||
relationTo: Recorders.slug,
|
||||
hasMany: true,
|
||||
admin: { width: "50%" },
|
||||
},
|
||||
],
|
||||
},
|
||||
{ name: fields.content, type: "richText", admin: { hideGutter: true } },
|
||||
],
|
||||
}),
|
||||
{
|
||||
name: fields.publishedDate,
|
||||
type: "date",
|
||||
defaultValue: new Date().toISOString(),
|
||||
admin: {
|
||||
date: { pickerAppearance: "dayOnly", displayFormat: "yyyy-MM-dd" },
|
||||
position: "sidebar",
|
||||
},
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
};
|
|
@ -0,0 +1,14 @@
|
|||
import { CollectionBeforeValidateHook } from "payload/types";
|
||||
import { Post } from "../../../types/collections";
|
||||
|
||||
export const removeTranslatorsForTranscripts: CollectionBeforeValidateHook<Post> = async ({
|
||||
data: { translations, ...data },
|
||||
}) => ({
|
||||
...data,
|
||||
translations: translations.map(({ translators, ...translation }) => {
|
||||
if (translation.language === translation.sourceLanguage) {
|
||||
return { ...translation, translators: [] };
|
||||
}
|
||||
return { ...translation, translators };
|
||||
}),
|
||||
});
|
|
@ -1,9 +1,12 @@
|
|||
import { CollectionConfig } from "payload/types";
|
||||
import { localizedFields } from "../../elements/translatedFields/translatedFields";
|
||||
import { localizedFields } from "../../fields/translatedFields/translatedFields";
|
||||
import { Languages } from "../Languages";
|
||||
import { Images } from "../Images/Images";
|
||||
import { ImageCell } from "../Images/components/ImageCell";
|
||||
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";
|
||||
|
||||
const fields = {
|
||||
username: "username",
|
||||
|
@ -20,7 +23,7 @@ const labels = {
|
|||
} as const satisfies { singular: string; plural: string };
|
||||
|
||||
export const Recorders: CollectionConfig = {
|
||||
slug: labels.plural,
|
||||
slug: collectionSlug(labels.plural),
|
||||
labels,
|
||||
typescript: { interface: labels.singular },
|
||||
defaultSort: fields.username,
|
||||
|
@ -30,22 +33,14 @@ export const Recorders: CollectionConfig = {
|
|||
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],
|
||||
group: CollectionGroup.Meta,
|
||||
},
|
||||
timestamps: false,
|
||||
fields: [
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
{
|
||||
name: fields.avatar,
|
||||
type: "upload",
|
||||
relationTo: Images.slug,
|
||||
admin: {
|
||||
components: {
|
||||
Cell: ImageCell,
|
||||
},
|
||||
},
|
||||
},
|
||||
imageField({ name: fields.avatar }),
|
||||
{
|
||||
name: fields.username,
|
||||
type: "text",
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
import { CollectionConfig } from "payload/types";
|
||||
import { slugField } from "../../fields/slugField/slugField";
|
||||
import { CollectionGroup, TagsTypes } from "../../constants";
|
||||
import { localizedFields } from "../../fields/translatedFields/translatedFields";
|
||||
import { collectionSlug } from "../../utils/string";
|
||||
|
||||
const fields = {
|
||||
slug: "slug",
|
||||
translations: "translations",
|
||||
type: "type",
|
||||
name: "name",
|
||||
} as const satisfies Record<string, string>;
|
||||
|
||||
const labels = {
|
||||
singular: "Tag",
|
||||
plural: "Tags",
|
||||
} as const satisfies { singular: string; plural: string };
|
||||
|
||||
export const Tags: CollectionConfig = {
|
||||
slug: collectionSlug(labels.plural),
|
||||
labels,
|
||||
typescript: { interface: labels.singular },
|
||||
defaultSort: fields.slug,
|
||||
admin: {
|
||||
useAsTitle: fields.slug,
|
||||
defaultColumns: [fields.slug, fields.type, fields.translations],
|
||||
group: CollectionGroup.Meta,
|
||||
},
|
||||
timestamps: false,
|
||||
versions: false,
|
||||
fields: [
|
||||
slugField({ name: fields.slug }),
|
||||
{
|
||||
name: fields.type,
|
||||
type: "select",
|
||||
required: true,
|
||||
options: Object.entries(TagsTypes).map(([value, label]) => ({ label, value })),
|
||||
},
|
||||
localizedFields({
|
||||
name: fields.translations,
|
||||
interfaceName: "CategoryTranslations",
|
||||
admin: {
|
||||
useAsTitle: fields.name,
|
||||
},
|
||||
fields: [{ name: fields.name, type: "text", required: true }],
|
||||
}),
|
||||
],
|
||||
};
|
|
@ -1,4 +1,6 @@
|
|||
import { CollectionConfig } from "payload/types";
|
||||
import { CollectionGroup } from "../constants";
|
||||
import { collectionSlug } from "../utils/string";
|
||||
|
||||
const fields = {
|
||||
email: "email",
|
||||
|
@ -10,7 +12,7 @@ const labels = {
|
|||
} as const satisfies { singular: string; plural: string };
|
||||
|
||||
export const Users: CollectionConfig = {
|
||||
slug: labels.plural,
|
||||
slug: collectionSlug(labels.plural),
|
||||
auth: true,
|
||||
labels,
|
||||
typescript: { interface: labels.singular },
|
||||
|
@ -18,7 +20,7 @@ export const Users: CollectionConfig = {
|
|||
admin: {
|
||||
useAsTitle: fields.email,
|
||||
defaultColumns: [fields.email],
|
||||
group: "Administration",
|
||||
group: CollectionGroup.Administration,
|
||||
},
|
||||
timestamps: false,
|
||||
fields: [],
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
export enum CollectionGroup {
|
||||
Collections = "Collections",
|
||||
Media = "Media",
|
||||
Meta = "Meta",
|
||||
Administration = "Administration",
|
||||
}
|
||||
|
||||
export enum TagsTypes {
|
||||
Contents = "Contents",
|
||||
LibraryAudio = "Library / Audio",
|
||||
LibraryVideo = "Library / Video",
|
||||
LibraryTextual = "Library / Textual",
|
||||
LibraryGroup = "Library / Group",
|
||||
Library = "Library",
|
||||
Weapons = "Weapons",
|
||||
GamePlatforms = "Game Platforms",
|
||||
Categories = "Categories",
|
||||
}
|
||||
|
||||
export enum FileTypes {
|
||||
LibraryScans = "Library / Scans",
|
||||
LibrarySoundtracks = "Library / Soundtracks",
|
||||
ContentVideo = "Content / Video",
|
||||
ContentAudio = "Content / Audio",
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
import { RelationshipField, UploadField } from "payload/types";
|
||||
import { Files } from "../../collections/Files/Files";
|
||||
|
||||
type Props = Omit<UploadField, "type" | "relationTo">;
|
||||
|
||||
export const fileField = (props: Props): RelationshipField => ({
|
||||
...props,
|
||||
type: "relationship",
|
||||
relationTo: Files.slug,
|
||||
});
|
|
@ -2,7 +2,7 @@ import { Props } from "payload/components/views/Cell";
|
|||
import { useState, useEffect } from "react";
|
||||
import React from "react";
|
||||
|
||||
export const ImageCell = ({ cellData, field }: Props): JSX.Element => {
|
||||
export const Cell = ({ cellData, field }: Props): JSX.Element => {
|
||||
const [imageURL, setImageURL] = useState<string>();
|
||||
useEffect(() => {
|
||||
const fetchUrl = async () => {
|
|
@ -0,0 +1,17 @@
|
|||
import { UploadField } from "payload/types";
|
||||
import { Images } from "../../collections/Images/Images";
|
||||
import { Cell } from "./Cell";
|
||||
|
||||
type Props = Omit<UploadField, "type" | "relationTo">;
|
||||
|
||||
export const imageField = ({ admin, ...otherProps }: Props): UploadField => ({
|
||||
...otherProps,
|
||||
type: "upload",
|
||||
relationTo: Images.slug,
|
||||
admin: {
|
||||
components: {
|
||||
Cell,
|
||||
},
|
||||
...admin,
|
||||
},
|
||||
});
|
|
@ -0,0 +1,23 @@
|
|||
import { TextField } from "payload/types";
|
||||
import { isUndefined } from "../../utils/asserts";
|
||||
|
||||
type Props = Omit<TextField, "type">;
|
||||
|
||||
const validateSlug = (value?: string) => {
|
||||
if (isUndefined(value) || value === "") return "This field is required.";
|
||||
if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(value)) return "This is not a valid slug.";
|
||||
return true;
|
||||
};
|
||||
|
||||
export const slugField = ({ admin, ...otherProps }: Props): TextField => ({
|
||||
...otherProps,
|
||||
type: "text",
|
||||
required: true,
|
||||
unique: true,
|
||||
validate: validateSlug,
|
||||
admin: {
|
||||
description:
|
||||
"A slug must only include lowercase letters and digits. Instead of spaces you can use dashes.",
|
||||
...admin,
|
||||
},
|
||||
});
|
|
@ -1,21 +1,40 @@
|
|||
import { ArrayField } from "payload/types";
|
||||
import { ArrayField, Field } from "payload/types";
|
||||
import { hasDuplicates } from "../../utils/validation";
|
||||
import { isDefined, isUndefined } from "../../utils/asserts";
|
||||
import { Languages } from "../../collections/Languages";
|
||||
import { RowLabel } from "./RowLabel";
|
||||
import { Cell } from "./Cell";
|
||||
|
||||
const LANGUAGE_FIELD_NAME = "language";
|
||||
const fieldsNames = {
|
||||
language: "language",
|
||||
sourceLanguage: "sourceLanguage",
|
||||
} as const satisfies Record<string, string>;
|
||||
|
||||
type LocalizedFieldsProps = Omit<ArrayField, "type" | "admin"> & {
|
||||
admin?: ArrayField["admin"] & { useAsTitle?: string };
|
||||
admin?: ArrayField["admin"] & { useAsTitle?: string; hasSourceLanguage?: boolean };
|
||||
};
|
||||
type ArrayData = { [fieldsNames.language]?: string }[] | number | undefined;
|
||||
|
||||
const languageField: Field = {
|
||||
name: fieldsNames.language,
|
||||
type: "relationship",
|
||||
relationTo: Languages.slug,
|
||||
required: true,
|
||||
admin: { allowCreate: false, width: "50%" },
|
||||
};
|
||||
|
||||
const sourceLanguageField: Field = {
|
||||
name: fieldsNames.sourceLanguage,
|
||||
type: "relationship",
|
||||
relationTo: Languages.slug,
|
||||
required: true,
|
||||
admin: { allowCreate: false, width: "50%" },
|
||||
};
|
||||
type ArrayData = { [LANGUAGE_FIELD_NAME]?: string }[] | number | undefined;
|
||||
|
||||
export const localizedFields = ({
|
||||
fields,
|
||||
validate,
|
||||
admin: { useAsTitle, ...admin },
|
||||
admin: { useAsTitle, hasSourceLanguage, ...admin },
|
||||
...otherProps
|
||||
}: LocalizedFieldsProps): ArrayField => ({
|
||||
...otherProps,
|
||||
|
@ -45,19 +64,15 @@ export const localizedFields = ({
|
|||
|
||||
const languages = data.map((biography) => biography.language);
|
||||
if (hasDuplicates(languages)) {
|
||||
return `There cannot be multiple ${otherProps.name} with the same ${LANGUAGE_FIELD_NAME}`;
|
||||
return `There cannot be multiple ${otherProps.name} with the same ${fieldsNames.language}`;
|
||||
}
|
||||
|
||||
return isDefined(validate) ? validate(value, options) : true;
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: LANGUAGE_FIELD_NAME,
|
||||
type: "relationship",
|
||||
relationTo: Languages.slug,
|
||||
required: true,
|
||||
admin: { allowCreate: false },
|
||||
},
|
||||
hasSourceLanguage
|
||||
? { type: "row", fields: [languageField, sourceLanguageField] }
|
||||
: languageField,
|
||||
...fields,
|
||||
],
|
||||
});
|
|
@ -4,14 +4,18 @@ import { Users } from "./collections/Users";
|
|||
import { Languages } from "./collections/Languages";
|
||||
import { Recorders } from "./collections/Recorders/Recorders";
|
||||
import { Images } from "./collections/Images/Images";
|
||||
import { Categories } from "./collections/Categories/Categories";
|
||||
import { Posts } from "./collections/Posts/Posts";
|
||||
import { Tags } from "./collections/Tags/Tags";
|
||||
import { LibraryItems } from "./collections/LibraryItems/LibraryItems";
|
||||
import { Contents } from "./collections/Contents/Contents";
|
||||
import { Files } from "./collections/Files/Files";
|
||||
|
||||
export default buildConfig({
|
||||
serverURL: "http://localhost:3000",
|
||||
admin: {
|
||||
user: Users.slug,
|
||||
},
|
||||
collections: [Users, Languages, Recorders, Images, Categories],
|
||||
collections: [LibraryItems, Contents, Posts, Images, Files, Languages, Recorders, Tags, Users],
|
||||
globals: [],
|
||||
telemetry: false,
|
||||
typescript: {
|
||||
|
|
|
@ -6,6 +6,11 @@
|
|||
* and re-run `payload generate:types` to regenerate this file.
|
||||
*/
|
||||
|
||||
export type CategoryTranslations = {
|
||||
language: string | Language;
|
||||
name: string;
|
||||
id?: string;
|
||||
}[];
|
||||
export type RecorderBiographies = {
|
||||
language: string | Language;
|
||||
biography?: string;
|
||||
|
@ -14,35 +19,37 @@ export type RecorderBiographies = {
|
|||
|
||||
export interface Config {
|
||||
collections: {
|
||||
Users: User;
|
||||
Languages: Language;
|
||||
Recorders: Recorder;
|
||||
Images: Image;
|
||||
'library-items': LibraryItem;
|
||||
contents: Content;
|
||||
posts: Post;
|
||||
images: Image;
|
||||
files: File;
|
||||
languages: Language;
|
||||
recorders: Recorder;
|
||||
tags: Tag;
|
||||
users: User;
|
||||
};
|
||||
globals: {};
|
||||
}
|
||||
export interface User {
|
||||
export interface LibraryItem {
|
||||
id: string;
|
||||
email: string;
|
||||
resetPasswordToken?: string;
|
||||
resetPasswordExpiration?: string;
|
||||
salt?: string;
|
||||
hash?: string;
|
||||
loginAttempts?: number;
|
||||
lockUntil?: string;
|
||||
password?: string;
|
||||
}
|
||||
export interface Language {
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
export interface Recorder {
|
||||
id: string;
|
||||
avatar?: string | Image;
|
||||
username: string;
|
||||
anonymize: boolean;
|
||||
languages?: string[] | Language[];
|
||||
biographies?: RecorderBiographies;
|
||||
slug: string;
|
||||
thumbnail?: string | Image;
|
||||
pretitle?: string;
|
||||
title: string;
|
||||
subtitle?: string;
|
||||
rootItem: boolean;
|
||||
primary: boolean;
|
||||
digital: boolean;
|
||||
downloadable: boolean;
|
||||
size?: {
|
||||
width?: number;
|
||||
height?: number;
|
||||
thickness?: number;
|
||||
};
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
_status?: 'draft' | 'published';
|
||||
}
|
||||
export interface Image {
|
||||
id: string;
|
||||
|
@ -56,3 +63,330 @@ export interface Image {
|
|||
width?: number;
|
||||
height?: number;
|
||||
}
|
||||
export interface Content {
|
||||
id: string;
|
||||
slug: string;
|
||||
thumbnail?: string | Image;
|
||||
testing?: (Section_1 | Collapsible_1 | Columns_1 | Tabs_1 | Accordion_1 | TextBlock | TranscriptBlock)[];
|
||||
categories?:
|
||||
| {
|
||||
value: string;
|
||||
relationTo: 'tags';
|
||||
}[]
|
||||
| {
|
||||
value: Tag;
|
||||
relationTo: 'tags';
|
||||
}[];
|
||||
type?: {
|
||||
value: string | Tag;
|
||||
relationTo: 'tags';
|
||||
};
|
||||
translations: {
|
||||
language: string | Language;
|
||||
sourceLanguage: string | Language;
|
||||
pretitle?: string;
|
||||
title: string;
|
||||
subtitle?: string;
|
||||
summary?: string;
|
||||
textTranscribers?: string[] | Recorder[];
|
||||
textTranslators?: string[] | Recorder[];
|
||||
textProofreaders?: string[] | Recorder[];
|
||||
textContent?: {
|
||||
[k: string]: unknown;
|
||||
}[];
|
||||
textNotes?: string;
|
||||
video?: string | File;
|
||||
videoNotes?: string;
|
||||
audio?: string | File;
|
||||
id?: string;
|
||||
}[];
|
||||
updatedAt: string;
|
||||
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 Section_2 {
|
||||
content?: (Section_3 | Collapsible_3 | Columns_3 | Tabs_3 | Accordion_3 | TextBlock | TranscriptBlock)[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'section_2';
|
||||
}
|
||||
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 TextBlock {
|
||||
content: {
|
||||
[k: string]: unknown;
|
||||
}[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'textBlock';
|
||||
}
|
||||
export interface TranscriptBlock {
|
||||
lines: (LineBlock | CueBlock)[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'transcriptBlock';
|
||||
}
|
||||
export interface LineBlock {
|
||||
content: {
|
||||
[k: string]: unknown;
|
||||
}[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'lineBlock';
|
||||
}
|
||||
export interface CueBlock {
|
||||
content: string;
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'cueBlock';
|
||||
}
|
||||
export interface Collapsible_4 {
|
||||
content?: (TextBlock | TranscriptBlock)[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'collapsible_4';
|
||||
}
|
||||
export interface Columns_4 {
|
||||
columns?: Column_4[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'columns_4';
|
||||
}
|
||||
export interface Column_4 {
|
||||
content?: (TextBlock | TranscriptBlock)[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'column_4';
|
||||
}
|
||||
export interface Tabs_4 {
|
||||
tabs?: Tab_4[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'tabs_4';
|
||||
}
|
||||
export interface Tab_4 {
|
||||
content?: (TextBlock | TranscriptBlock)[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'tab_4';
|
||||
}
|
||||
export interface Accordion_4 {
|
||||
content?: Collapsible_5[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'accordion_4';
|
||||
}
|
||||
export interface Collapsible_5 {
|
||||
content?: (TextBlock | TranscriptBlock)[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'collapsible_5';
|
||||
}
|
||||
export interface Collapsible_3 {
|
||||
content?: (Section_4 | Collapsible_4 | Columns_4 | Tabs_4 | Accordion_4 | TextBlock | TranscriptBlock)[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'collapsible_3';
|
||||
}
|
||||
export interface Columns_3 {
|
||||
columns?: Column_3[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'columns_3';
|
||||
}
|
||||
export interface Column_3 {
|
||||
content?: (Section_4 | Collapsible_4 | Columns_4 | Tabs_4 | Accordion_4 | TextBlock | TranscriptBlock)[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'column_3';
|
||||
}
|
||||
export interface Tabs_3 {
|
||||
tabs?: Tab_3[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'tabs_3';
|
||||
}
|
||||
export interface Tab_3 {
|
||||
content?: (Section_4 | Collapsible_4 | Columns_4 | Tabs_4 | Accordion_4 | TextBlock | TranscriptBlock)[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'tab_3';
|
||||
}
|
||||
export interface Accordion_3 {
|
||||
content?: Collapsible_4[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'accordion_3';
|
||||
}
|
||||
export interface Collapsible_2 {
|
||||
content?: (Section_3 | Collapsible_3 | Columns_3 | Tabs_3 | Accordion_3 | TextBlock | TranscriptBlock)[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'collapsible_2';
|
||||
}
|
||||
export interface Columns_2 {
|
||||
columns?: Column_2[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'columns_2';
|
||||
}
|
||||
export interface Column_2 {
|
||||
content?: (Section_3 | Collapsible_3 | Columns_3 | Tabs_3 | Accordion_3 | TextBlock | TranscriptBlock)[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'column_2';
|
||||
}
|
||||
export interface Tabs_2 {
|
||||
tabs?: Tab_2[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'tabs_2';
|
||||
}
|
||||
export interface Tab_2 {
|
||||
content?: (Section_3 | Collapsible_3 | Columns_3 | Tabs_3 | Accordion_3 | TextBlock | TranscriptBlock)[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'tab_2';
|
||||
}
|
||||
export interface Accordion_2 {
|
||||
content?: Collapsible_3[];
|
||||
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;
|
||||
}
|
||||
export interface File {
|
||||
id: string;
|
||||
filename: string;
|
||||
type: 'LibraryScans' | 'LibrarySoundtracks' | 'ContentVideo' | 'ContentAudio';
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
export interface Post {
|
||||
id: string;
|
||||
slug: string;
|
||||
thumbnail?: string | Image;
|
||||
authors:
|
||||
| {
|
||||
value: string;
|
||||
relationTo: 'recorders';
|
||||
}[]
|
||||
| {
|
||||
value: Recorder;
|
||||
relationTo: 'recorders';
|
||||
}[];
|
||||
categories?:
|
||||
| {
|
||||
value: string;
|
||||
relationTo: 'tags';
|
||||
}[]
|
||||
| {
|
||||
value: Tag;
|
||||
relationTo: 'tags';
|
||||
}[];
|
||||
translations: {
|
||||
language: string | Language;
|
||||
sourceLanguage: string | Language;
|
||||
title: string;
|
||||
summary?: string;
|
||||
translators?: string[] | Recorder[];
|
||||
proofreaders?: string[] | Recorder[];
|
||||
content?: {
|
||||
[k: string]: unknown;
|
||||
}[];
|
||||
id?: string;
|
||||
}[];
|
||||
publishedDate: string;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
_status?: 'draft' | 'published';
|
||||
}
|
||||
export interface User {
|
||||
id: string;
|
||||
email: string;
|
||||
resetPasswordToken?: string;
|
||||
resetPasswordExpiration?: string;
|
||||
salt?: string;
|
||||
hash?: string;
|
||||
loginAttempts?: number;
|
||||
lockUntil?: string;
|
||||
password?: string;
|
||||
}
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
export type AdminComponent<T extends Record<string, any>> = (props: {
|
||||
data: Partial<T>;
|
||||
index?: number;
|
||||
}) => string;
|
|
@ -1,7 +1,11 @@
|
|||
import ISO6391 from "iso-639-1";
|
||||
import slugify from "slugify";
|
||||
|
||||
export const shortenEllipsis = (text: string, length: number): string =>
|
||||
text.length - 3 > length ? `${text.substring(0, length)}...` : text;
|
||||
|
||||
export const formatLanguageCode = (code: string): string =>
|
||||
ISO6391.validate(code) ? ISO6391.getName(code) : code;
|
||||
|
||||
export const collectionSlug = (text: string): string =>
|
||||
slugify(text, { lower: true, strict: true, trim: true });
|
||||
|
|
Loading…
Reference in New Issue