Improve editor + added imports

This commit is contained in:
DrMint 2023-10-23 22:39:17 +02:00
parent 509cbdba9b
commit 4f807c410b
31 changed files with 5043 additions and 2971 deletions

View File

@ -4,6 +4,7 @@ MONGODB_PORT=27017
PAYLOAD_URI=https://payload.domain.com PAYLOAD_URI=https://payload.domain.com
PAYLOAD_SECRET=payloadsecreta5e6ea45ef4e66eaa151612bdcb599df PAYLOAD_SECRET=payloadsecreta5e6ea45ef4e66eaa151612bdcb599df
PAYLOAD_PORT=3000 PAYLOAD_PORT=3000
RECORDER_DEFAULT_PASSWORD=somepassword
STRAPI_URI=https://strapi.domain.com STRAPI_URI=https://strapi.domain.com
STRAPI_TOKEN=strapisecreta5e6ea45ef4e66eaa151612bdcb599df STRAPI_TOKEN=strapisecreta5e6ea45ef4e66eaa151612bdcb599df

BIN
bun.lockb

Binary file not shown.

7122
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -22,27 +22,28 @@
"start": "sudo docker compose up" "start": "sudo docker compose up"
}, },
"dependencies": { "dependencies": {
"@fontsource/vollkorn": "^5.0.14", "@fontsource/vollkorn": "^5.0.17",
"@payloadcms/bundler-webpack": "^1.0.3", "@payloadcms/bundler-webpack": "^1.0.4",
"@payloadcms/db-mongodb": "^1.0.3", "@payloadcms/db-mongodb": "^1.0.4",
"@payloadcms/richtext-lexical": "^0.1.8", "@payloadcms/richtext-lexical": "^0.1.15",
"clean-deep": "^3.4.0", "clean-deep": "^3.4.0",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"language-tags": "^1.0.9", "language-tags": "^1.0.9",
"luxon": "^3.4.3", "luxon": "^3.4.3",
"payload": "^2.0.5", "payload": "^2.0.12",
"styled-components": "^6.0.9" "styled-components": "^6.1.0"
}, },
"devDependencies": { "devDependencies": {
"@types/dotenv": "^8.2.0", "@types/dotenv": "^8.2.0",
"@types/express": "^4.17.18", "@types/express": "^4.17.20",
"@types/language-tags": "^1.0.2", "@types/language-tags": "^1.0.3",
"@types/luxon": "^3.3.2", "@types/luxon": "^3.3.3",
"@types/qs": "^6.9.8", "@types/qs": "^6.9.9",
"@types/react-router-dom": "^5.3.3", "@types/react-router-dom": "^5.3.3",
"@types/styled-components": "^5.1.28", "@types/styled-components": "^5.1.29",
"copyfiles": "^2.4.1", "copyfiles": "^2.4.1",
"nodemon": "^3.0.1", "nodemon": "^3.0.1",
"npm-check-updates": "^16.14.6",
"prettier": "^3.0.3", "prettier": "^3.0.3",
"ts-node": "^10.9.1", "ts-node": "^10.9.1",
"ts-unused-exports": "^10.0.1", "ts-unused-exports": "^10.0.1",

View File

@ -1,4 +1,5 @@
import { Block } from "payload/types"; import { Block } from "payload/types";
import { createEditor } from "../utils/editor";
export const cueBlock: Block = { export const cueBlock: Block = {
slug: "cueBlock", slug: "cueBlock",
@ -8,12 +9,12 @@ export const cueBlock: Block = {
{ {
name: "content", name: "content",
label: false, label: false,
type: "textarea", type: "richText",
required: true, required: true,
admin: { admin: {
description: className: "reduced-margins",
"Parenthesis will automatically be added around cues. You don't have to include them here.",
}, },
editor: createEditor({ inlines: true, lists: true, links: true }),
}, },
], ],
}; };

View File

@ -1,4 +1,5 @@
import { Block } from "payload/types"; import { Block } from "payload/types";
import { createEditor } from "../utils/editor";
export const lineBlock: Block = { export const lineBlock: Block = {
slug: "lineBlock", slug: "lineBlock",
@ -10,6 +11,10 @@ export const lineBlock: Block = {
label: false, label: false,
type: "richText", type: "richText",
required: true, required: true,
admin: {
className: "reduced-margins",
},
editor: createEditor({ inlines: true, lists: true, links: true }),
}, },
], ],
}; };

View File

@ -0,0 +1,31 @@
import { Block } from "payload/types";
import { createEditor } from "../utils/editor";
import { transcriptBlock } from "./transcriptBlock";
const generateRecursiveSectionBlock = (depth = 1, maxDepth = 5): Block => ({
slug: "sectionBlock",
interfaceName: "SectionBlock",
labels: { singular: "Section", plural: "Sections" },
fields: [
{
name: "lines",
type: "richText",
label: false,
required: true,
editor: createEditor({
images: true,
inlines: true,
lists: true,
links: true,
relations: true,
alignment: true,
blocks: [
transcriptBlock,
...(depth < maxDepth ? [generateRecursiveSectionBlock(depth + 1, maxDepth)] : []),
],
}),
},
],
});
export const sectionBlock: Block = generateRecursiveSectionBlock();

View File

@ -1,6 +1,6 @@
import { Block } from "payload/types"; import { Block } from "payload/types";
import { lineBlock } from "./lineBlock";
import { cueBlock } from "./cueBlock"; import { cueBlock } from "./cueBlock";
import { lineBlock } from "./lineBlock";
export const transcriptBlock: Block = { export const transcriptBlock: Block = {
slug: "transcriptBlock", slug: "transcriptBlock",
@ -12,7 +12,7 @@ export const transcriptBlock: Block = {
type: "blocks", type: "blocks",
required: true, required: true,
minRows: 1, minRows: 1,
admin: { initCollapsed: true }, admin: { initCollapsed: true, className: "no-label" },
blocks: [lineBlock, cueBlock], blocks: [lineBlock, cueBlock],
}, },
], ],

View File

@ -5,6 +5,7 @@ import { backPropagationField } from "../../fields/backPropagationField/backProp
import { slugField } from "../../fields/slugField/slugField"; import { slugField } from "../../fields/slugField/slugField";
import { translatedFields } from "../../fields/translatedFields/translatedFields"; import { translatedFields } from "../../fields/translatedFields/translatedFields";
import { buildCollectionConfig } from "../../utils/collectionConfig"; import { buildCollectionConfig } from "../../utils/collectionConfig";
import { createEditor } from "../../utils/editor";
import { getAllEndpoint } from "./endpoints/getAllEndpoint"; import { getAllEndpoint } from "./endpoints/getAllEndpoint";
import { importFromStrapi } from "./endpoints/importFromStrapi"; import { importFromStrapi } from "./endpoints/importFromStrapi";
import { beforeValidateEndingGreaterThanStarting } from "./hooks/beforeValidateEndingGreaterThanStarting"; import { beforeValidateEndingGreaterThanStarting } from "./hooks/beforeValidateEndingGreaterThanStarting";
@ -68,7 +69,8 @@ export const ChronologyEras: CollectionConfig = buildCollectionConfig({
{ name: fields.translationsTitle, type: "text", required: true }, { name: fields.translationsTitle, type: "text", required: true },
{ {
name: fields.translationsDescription, name: fields.translationsDescription,
type: "textarea", type: "richText",
editor: createEditor({ inlines: true, lists: true, links: true }),
}, },
], ],
}), }),

View File

@ -6,6 +6,7 @@ import {
} from "../../components/QuickFilters"; } from "../../components/QuickFilters";
import { CollectionGroups, Collections } from "../../constants"; import { CollectionGroups, Collections } from "../../constants";
import { translatedFields } from "../../fields/translatedFields/translatedFields"; import { translatedFields } from "../../fields/translatedFields/translatedFields";
import { createEditor } from "../../utils/editor";
import { buildVersionedCollectionConfig } from "../../utils/versionedCollectionConfig"; import { buildVersionedCollectionConfig } from "../../utils/versionedCollectionConfig";
import { importFromStrapi } from "./endpoints/importFromStrapi"; import { importFromStrapi } from "./endpoints/importFromStrapi";
import { beforeValidatePopulateNameField } from "./hooks/beforeValidatePopulateNameField"; import { beforeValidatePopulateNameField } from "./hooks/beforeValidatePopulateNameField";
@ -127,9 +128,14 @@ export const ChronologyItems: CollectionConfig = buildVersionedCollectionConfig(
{ {
name: fields.eventsTranslationsDescription, name: fields.eventsTranslationsDescription,
validate: validateEventsTranslationsDescription, validate: validateEventsTranslationsDescription,
type: "textarea", type: "richText",
editor: createEditor({ inlines: true, lists: true, links: true }),
},
{
name: fields.eventsTranslationsNotes,
type: "richText",
editor: createEditor({ inlines: true, lists: true, links: true }),
}, },
{ name: fields.eventsTranslationsNotes, type: "textarea" },
], ],
}), }),
], ],

View File

@ -1,3 +1,5 @@
import { sectionBlock } from "../../blocks/sectionBlock";
import { transcriptBlock } from "../../blocks/transcriptBlock";
import { CollectionGroups, Collections, FileTypes, KeysTypes } from "../../constants"; import { CollectionGroups, Collections, FileTypes, KeysTypes } from "../../constants";
import { fileField } from "../../fields/fileField/fileField"; import { fileField } from "../../fields/fileField/fileField";
import { imageField } from "../../fields/imageField/imageField"; import { imageField } from "../../fields/imageField/imageField";
@ -8,7 +10,9 @@ import { beforeDuplicateAddCopyTo } from "../../hooks/beforeDuplicateAddCopyTo";
import { beforeDuplicatePiping } from "../../hooks/beforeDuplicatePiping"; import { beforeDuplicatePiping } from "../../hooks/beforeDuplicatePiping";
import { beforeDuplicateUnpublish } from "../../hooks/beforeDuplicateUnpublish"; import { beforeDuplicateUnpublish } from "../../hooks/beforeDuplicateUnpublish";
import { isDefined } from "../../utils/asserts"; import { isDefined } from "../../utils/asserts";
import { createEditor } from "../../utils/editor";
import { buildVersionedCollectionConfig } from "../../utils/versionedCollectionConfig"; import { buildVersionedCollectionConfig } from "../../utils/versionedCollectionConfig";
import { importFromStrapi } from "./endpoints/importFromStrapi";
const fields = { const fields = {
slug: "slug", slug: "slug",
@ -61,6 +65,7 @@ export const Contents = buildVersionedCollectionConfig({
}, },
preview: (doc) => `https://accords-library.com/contents/${doc.slug}`, preview: (doc) => `https://accords-library.com/contents/${doc.slug}`,
}, },
endpoints: [importFromStrapi],
fields: [ fields: [
{ {
type: "row", type: "row",
@ -103,7 +108,11 @@ export const Contents = buildVersionedCollectionConfig({
{ name: fields.subtitle, type: "text" }, { name: fields.subtitle, type: "text" },
], ],
}, },
{ name: fields.summary, type: "textarea" }, {
name: fields.summary,
type: "richText",
editor: createEditor({ inlines: true, lists: true, links: true }),
},
{ {
type: "tabs", type: "tabs",
admin: { admin: {
@ -114,7 +123,20 @@ export const Contents = buildVersionedCollectionConfig({
{ {
label: "Text", label: "Text",
fields: [ fields: [
{ name: fields.textContent, type: "richText" }, {
name: fields.textContent,
type: "richText",
label: false,
editor: createEditor({
blocks: [sectionBlock, transcriptBlock],
images: true,
inlines: true,
lists: true,
links: true,
relations: true,
alignment: true,
}),
},
{ {
type: "row", type: "row",
fields: [ fields: [
@ -155,7 +177,8 @@ export const Contents = buildVersionedCollectionConfig({
{ {
name: fields.textNotes, name: fields.textNotes,
label: "Notes", label: "Notes",
type: "textarea", type: "richText",
editor: createEditor({ inlines: true, lists: true, links: true }),
}, },
], ],
}, },
@ -173,7 +196,8 @@ export const Contents = buildVersionedCollectionConfig({
{ {
name: fields.videoNotes, name: fields.videoNotes,
label: "Notes", label: "Notes",
type: "textarea", type: "richText",
editor: createEditor({ inlines: true, lists: true, links: true }),
admin: { width: "0%" }, admin: { width: "0%" },
}, },
], ],
@ -194,7 +218,8 @@ export const Contents = buildVersionedCollectionConfig({
{ {
name: fields.audioNotes, name: fields.audioNotes,
label: "Notes", label: "Notes",
type: "textarea", type: "richText",
editor: createEditor({ inlines: true, lists: true, links: true }),
admin: { width: "0%" }, admin: { width: "0%" },
}, },
], ],

View File

@ -0,0 +1,121 @@
import type { MarkOptional } from "ts-essentials";
import { Collections } from "../../../constants";
import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint";
import { Content } from "../../../types/collections";
import { StrapiImage, StrapiLanguage, StrapiRecorders } from "../../../types/strapi";
import { isNotEmpty, isUndefined } from "../../../utils/asserts";
import {
findCategory,
findContentType,
findRecorder,
uploadStrapiImage,
} from "../../../utils/localApi";
import { plainTextToLexical } from "../../../utils/string";
type StrapiContent = {
slug: string;
categories: { data?: { attributes: { slug: string } }[] };
type: { data?: { attributes: { slug: string } } };
thumbnail: StrapiImage;
translations: {
title: string;
subtitle?: string;
pre_title?: string;
description?: string;
language: StrapiLanguage;
text_set?: {
text: string;
notes?: string;
source_language: StrapiLanguage;
transcribers: StrapiRecorders;
translators: StrapiRecorders;
proofreaders: StrapiRecorders;
};
}[];
};
export const importFromStrapi = createStrapiImportEndpoint<StrapiContent>({
strapi: {
collection: "contents",
params: {
populate: [
"type",
"categories",
"thumbnail",
"translations",
"translations.language",
"translations.text_set",
"translations.text_set.source_language",
"translations.text_set.transcribers",
"translations.text_set.translators",
"translations.text_set.proofreaders",
],
},
},
payload: {
collection: Collections.Contents,
convert: async ({ slug, categories, type, thumbnail, translations }) => {
const thumbnailId = await uploadStrapiImage({
collection: Collections.ContentsThumbnails,
image: thumbnail,
});
const data: MarkOptional<Content, "createdAt" | "id" | "updatedAt" | "updatedBy"> = {
slug,
categories:
categories.data &&
(await Promise.all(
categories.data.map(async (category) => await findCategory(category.attributes.slug))
)),
type: type.data && (await findContentType(type.data?.attributes.slug)),
thumbnail: thumbnailId,
translations: await Promise.all(
translations.map(
async ({ language, title, description, pre_title, subtitle, text_set }) => {
if (isUndefined(language.data))
throw new Error("A language is required for a content translation");
if (isUndefined(text_set))
throw new Error("Only content with text_set are supported");
if (isUndefined(text_set.source_language.data))
throw new Error("A language is required for a content translation text_set");
return {
language: language.data.attributes.code,
sourceLanguage: text_set.source_language.data.attributes.code,
title,
pretitle: pre_title,
subtitle,
summary: isNotEmpty(description) ? plainTextToLexical(description) : undefined,
textContent: plainTextToLexical(text_set.text),
textNotes: isNotEmpty(text_set.notes)
? plainTextToLexical(text_set.notes)
: undefined,
textTranscribers:
text_set.transcribers.data &&
(await Promise.all(
text_set.transcribers.data?.map(async (recorder) =>
findRecorder(recorder.attributes.username)
)
)),
textTranslators:
text_set.translators.data &&
(await Promise.all(
text_set.translators.data?.map(async (recorder) =>
findRecorder(recorder.attributes.username)
)
)),
textProofreaders:
text_set.proofreaders.data &&
(await Promise.all(
text_set.proofreaders.data?.map(async (recorder) =>
findRecorder(recorder.attributes.username)
)
)),
};
}
)
),
};
return data;
},
},
});

View File

@ -2,6 +2,7 @@ import { CollectionGroups, Collections } from "../../constants";
import { slugField } from "../../fields/slugField/slugField"; import { slugField } from "../../fields/slugField/slugField";
import { translatedFields } from "../../fields/translatedFields/translatedFields"; import { translatedFields } from "../../fields/translatedFields/translatedFields";
import { buildCollectionConfig } from "../../utils/collectionConfig"; import { buildCollectionConfig } from "../../utils/collectionConfig";
import { importFromStrapi } from "./endpoints/importFromStrapi";
const fields = { const fields = {
slug: "slug", slug: "slug",
@ -24,13 +25,13 @@ export const ContentsFolders = buildCollectionConfig({
disableDuplicate: true, disableDuplicate: true,
group: CollectionGroups.Collections, group: CollectionGroups.Collections,
}, },
endpoints: [importFromStrapi],
timestamps: false, timestamps: false,
versions: false, versions: false,
fields: [ fields: [
slugField({ name: fields.slug }), slugField({ name: fields.slug }),
translatedFields({ translatedFields({
name: fields.translations, name: fields.translations,
interfaceName: "ContentFoldersTranslation",
admin: { admin: {
useAsTitle: fields.name, useAsTitle: fields.name,
}, },

View File

@ -0,0 +1,107 @@
import payload from "payload";
import QueryString from "qs";
import { Collections } from "../../../constants";
import { CollectionEndpoint } from "../../../types/payload";
import { StrapiLanguage } from "../../../types/strapi";
import { isUndefined } from "../../../utils/asserts";
type StrapiContentsFolder = {
id: string;
attributes: {
slug: string;
titles?: { title: string; language: StrapiLanguage }[];
subfolders: { data: StrapiContentsFolder[] };
contents: { data: { id: number }[] };
};
};
const getStrapiContentFolder = async (id: number): Promise<StrapiContentsFolder> => {
const paramsWithPagination = QueryString.stringify({
populate: [
"subfolders",
"subfolders.contents",
"subfolders.titles",
"subfolders.titles.language",
"subfolders.subfolders",
"subfolders.subfolders.contents",
"subfolders.subfolders.titles",
"subfolders.subfolders.titles.language",
"subfolders.subfolders.subfolders",
"subfolders.subfolders.subfolders.contents",
"subfolders.subfolders.subfolders.titles",
"subfolders.subfolders.subfolders.titles.language",
"subfolders.subfolders.subfolders.subfolders",
"subfolders.subfolders.subfolders.subfolders.contents",
"subfolders.subfolders.subfolders.subfolders.titles",
"subfolders.subfolders.subfolders.subfolders.titles.language",
"subfolders.subfolders.subfolders.subfolders.subfolders",
"subfolders.subfolders.subfolders.subfolders.subfolders.contents",
"subfolders.subfolders.subfolders.subfolders.subfolders.titles",
"subfolders.subfolders.subfolders.subfolders.subfolders.titles.language",
"subfolders.subfolders.subfolders.subfolders.subfolders.subfolders",
"subfolders.subfolders.subfolders.subfolders.subfolders.subfolders.contents",
"subfolders.subfolders.subfolders.subfolders.subfolders.subfolders.titles",
"subfolders.subfolders.subfolders.subfolders.subfolders.subfolders.titles.language",
"subfolders.subfolders.subfolders.subfolders.subfolders.subfolders.subfolders",
"subfolders.subfolders.subfolders.subfolders.subfolders.subfolders.subfolders.contents",
"subfolders.subfolders.subfolders.subfolders.subfolders.subfolders.subfolders.titles",
"subfolders.subfolders.subfolders.subfolders.subfolders.subfolders.subfolders.titles.language",
],
});
const uri = `${process.env.STRAPI_URI}/api/contents-folders/${id}?${paramsWithPagination}`;
const fetchResult = await fetch(uri, {
method: "get",
headers: { authorization: `Bearer ${process.env.STRAPI_TOKEN}` },
});
const { data } = await fetchResult.json();
return data;
};
export const importFromStrapi: CollectionEndpoint = {
method: "post",
path: "/strapi",
handler: async (req, res) => {
if (!req.user) {
return res.status(403).send({
errors: [
{
message: "You are not allowed to perform this action.",
},
],
});
}
let foldersCreated = 0;
const createContentFolder = async (data: StrapiContentsFolder): Promise<string> => {
const subfolders = await Promise.all(
data.attributes.subfolders.data.map(createContentFolder)
);
const { slug, titles } = data.attributes;
const result = await payload.create({
collection: Collections.ContentsFolders,
data: {
slug,
subfolders,
translations: titles?.map(({ title, language }) => {
if (isUndefined(language.data))
throw new Error("A language is required for a content folder translation");
return { language: language.data.attributes.code, name: title };
}),
},
user: req.user,
});
foldersCreated++;
return result.id;
};
const rootFolder = await getStrapiContentFolder(72);
try {
await createContentFolder(rootFolder);
} catch (e) {
res.status(500).json({ message: "Something went wrong", error: e });
}
res.status(200).json({ message: `${foldersCreated} entries have been added successfully.` });
},
};

View File

@ -0,0 +1,67 @@
import { CollectionGroups, Collections } from "../../constants";
import { slugField } from "../../fields/slugField/slugField";
import { translatedFields } from "../../fields/translatedFields/translatedFields";
import { buildCollectionConfig } from "../../utils/collectionConfig";
import { createEditor } from "../../utils/editor";
const fields = {
slug: "slug",
translations: "translations",
name: "name",
description: "description",
subfolders: "subfolders",
items: "items",
} as const satisfies Record<string, string>;
export const LibraryFolders = buildCollectionConfig({
slug: Collections.LibraryFolders,
labels: {
singular: "Library Folder",
plural: "Library Folders",
},
defaultSort: fields.slug,
admin: {
useAsTitle: fields.slug,
defaultColumns: [fields.slug, fields.translations],
disableDuplicate: true,
group: CollectionGroups.Collections,
},
timestamps: false,
versions: false,
fields: [
slugField({ name: fields.slug }),
translatedFields({
name: fields.translations,
admin: {
useAsTitle: fields.name,
},
fields: [
{ name: fields.name, type: "text", required: true },
{
name: fields.description,
type: "richText",
editor: createEditor({ inlines: true, lists: true, links: true }),
},
],
}),
{
type: "row",
fields: [
{
type: "relationship",
name: fields.subfolders,
relationTo: Collections.LibraryFolders,
hasMany: true,
admin: { width: "0%" },
},
{
type: "relationship",
name: fields.items,
relationTo: Collections.LibraryItems,
hasMany: true,
admin: { width: "0%" },
},
],
},
],
});

View File

@ -19,6 +19,7 @@ import { beforeDuplicatePiping } from "../../hooks/beforeDuplicatePiping";
import { beforeDuplicateUnpublish } from "../../hooks/beforeDuplicateUnpublish"; import { beforeDuplicateUnpublish } from "../../hooks/beforeDuplicateUnpublish";
import { LibraryItem } from "../../types/collections"; import { LibraryItem } from "../../types/collections";
import { isDefined } from "../../utils/asserts"; import { isDefined } from "../../utils/asserts";
import { createEditor } from "../../utils/editor";
import { buildVersionedCollectionConfig } from "../../utils/versionedCollectionConfig"; import { buildVersionedCollectionConfig } from "../../utils/versionedCollectionConfig";
import { RowLabel } from "./components/RowLabel"; import { RowLabel } from "./components/RowLabel";
@ -144,6 +145,9 @@ export const LibraryItems = buildVersionedCollectionConfig({
}, },
preview: (doc) => `https://accords-library.com/library/${doc.slug}`, preview: (doc) => `https://accords-library.com/library/${doc.slug}`,
}, },
fields: [
{
type: "row",
fields: [ fields: [
{ {
name: fields.itemType, name: fields.itemType,
@ -154,8 +158,22 @@ export const LibraryItems = buildVersionedCollectionConfig({
})), })),
admin: { admin: {
layout: "horizontal", layout: "horizontal",
width: "0%",
}, },
}, },
{
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: "0%",
},
},
],
},
{ {
type: "tabs", type: "tabs",
admin: { admin: {
@ -209,27 +227,6 @@ export const LibraryItems = buildVersionedCollectionConfig({
width: "0%", width: "0%",
}, },
}, },
{
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: "0%",
},
},
{
name: fields.downloadable,
type: "checkbox",
required: true,
defaultValue: false,
admin: {
description: "Are the scans available for download?",
width: "0%",
},
},
], ],
}, },
], ],
@ -505,6 +502,16 @@ export const LibraryItems = buildVersionedCollectionConfig({
}, },
], ],
}, },
{
name: fields.downloadable,
type: "checkbox",
required: true,
defaultValue: false,
admin: {
description: "Are the scans available for download?",
width: "0%",
},
},
], ],
}), }),
], ],
@ -559,6 +566,7 @@ export const LibraryItems = buildVersionedCollectionConfig({
}) })
), ),
admin: { admin: {
condition: (data: Partial<LibraryItem>) => !data.digital,
layout: "horizontal", layout: "horizontal",
width: "0%", width: "0%",
}, },
@ -654,11 +662,18 @@ export const LibraryItems = buildVersionedCollectionConfig({
name: fields.translations, name: fields.translations,
label: "Descriptions", label: "Descriptions",
admin: { initCollapsed: true, useAsTitle: fields.translationsDescription }, admin: { initCollapsed: true, useAsTitle: fields.translationsDescription },
fields: [{ name: fields.translationsDescription, type: "textarea", required: true }], fields: [
{
name: fields.translationsDescription,
required: true,
type: "richText",
editor: createEditor({ inlines: true, lists: true, links: true }),
},
],
}), }),
optionalGroupField({ optionalGroupField({
name: fields.size, name: fields.size,
admin: { condition: (data) => !data.digital }, admin: { condition: (data: Partial<LibraryItem>) => !data.digital },
fields: [ fields: [
{ {
type: "row", type: "row",
@ -763,7 +778,8 @@ export const LibraryItems = buildVersionedCollectionConfig({
}, },
{ {
name: fields.contentsNote, name: fields.contentsNote,
type: "textarea", type: "richText",
editor: createEditor({ inlines: true, lists: true, links: true }),
admin: { admin: {
condition: ({ itemType }) => condition: ({ itemType }) =>
itemType === LibraryItemsTypes.Game || itemType === LibraryItemsTypes.Other, itemType === LibraryItemsTypes.Game || itemType === LibraryItemsTypes.Other,

View File

@ -0,0 +1,20 @@
import { CollectionConfig } from "payload/types";
import { Collections } from "../../constants";
import { buildCollectionConfig } from "../../utils/collectionConfig";
import { createEditor } from "../../utils/editor";
export const Notes: CollectionConfig = buildCollectionConfig({
slug: Collections.Notes,
labels: { singular: "Note", plural: "Notes" },
admin: {
// TODO: Reenable when we can use rich text as titles useAsTitle: fields.biography,
},
fields: [
{
name: "note",
type: "richText",
required: true,
editor: createEditor({ inlines: true, lists: true, links: true }),
},
],
});

View File

@ -7,6 +7,7 @@ import { beforeDuplicateAddCopyTo } from "../../hooks/beforeDuplicateAddCopyTo";
import { beforeDuplicatePiping } from "../../hooks/beforeDuplicatePiping"; import { beforeDuplicatePiping } from "../../hooks/beforeDuplicatePiping";
import { beforeDuplicateUnpublish } from "../../hooks/beforeDuplicateUnpublish"; import { beforeDuplicateUnpublish } from "../../hooks/beforeDuplicateUnpublish";
import { isDefined, isUndefined } from "../../utils/asserts"; import { isDefined, isUndefined } from "../../utils/asserts";
import { createEditor } from "../../utils/editor";
import { buildVersionedCollectionConfig } from "../../utils/versionedCollectionConfig"; import { buildVersionedCollectionConfig } from "../../utils/versionedCollectionConfig";
const fields = { const fields = {
@ -97,7 +98,11 @@ export const Posts = buildVersionedCollectionConfig({
minRows: 1, minRows: 1,
fields: [ fields: [
{ name: fields.title, type: "text", required: true }, { name: fields.title, type: "text", required: true },
{ name: fields.summary, type: "textarea" }, {
name: fields.summary,
type: "richText",
editor: createEditor({ inlines: true, lists: true, links: true }),
},
{ {
type: "row", type: "row",
fields: [ fields: [

View File

@ -6,6 +6,7 @@ import { CollectionGroups, Collections, RecordersRoles } from "../../constants";
import { imageField } from "../../fields/imageField/imageField"; import { imageField } from "../../fields/imageField/imageField";
import { translatedFields } from "../../fields/translatedFields/translatedFields"; import { translatedFields } from "../../fields/translatedFields/translatedFields";
import { buildCollectionConfig } from "../../utils/collectionConfig"; import { buildCollectionConfig } from "../../utils/collectionConfig";
import { createEditor } from "../../utils/editor";
import { importFromStrapi } from "./endpoints/importFromStrapi"; import { importFromStrapi } from "./endpoints/importFromStrapi";
import { beforeLoginMustHaveAtLeastOneRole } from "./hooks/beforeLoginMustHaveAtLeastOneRole"; import { beforeLoginMustHaveAtLeastOneRole } from "./hooks/beforeLoginMustHaveAtLeastOneRole";
@ -107,11 +108,18 @@ export const Recorders = buildCollectionConfig({
name: fields.biographies, name: fields.biographies,
interfaceName: "RecorderBiographies", interfaceName: "RecorderBiographies",
admin: { admin: {
useAsTitle: fields.biography, // TODO: Reenable when we can use rich text as titles useAsTitle: fields.biography,
description: description:
"A short personal description about you or your involvement with this project or the franchise", "A short personal description about you or your involvement with this project or the franchise",
}, },
fields: [{ name: fields.biography, required: true, type: "textarea" }], fields: [
{
name: fields.biography,
required: true,
type: "richText",
editor: createEditor({ inlines: true, lists: true, links: true }),
},
],
}), }),
{ {
name: fields.role, name: fields.role,

View File

@ -5,6 +5,7 @@ import { Recorder } from "../../../types/collections";
import { StrapiImage, StrapiLanguage } from "../../../types/strapi"; import { StrapiImage, StrapiLanguage } from "../../../types/strapi";
import { isDefined, isUndefined } from "../../../utils/asserts"; import { isDefined, isUndefined } from "../../../utils/asserts";
import { uploadStrapiImage } from "../../../utils/localApi"; import { uploadStrapiImage } from "../../../utils/localApi";
import { plainTextToLexical } from "../../../utils/string";
type StrapiRecorder = { type StrapiRecorder = {
username: string; username: string;
@ -19,7 +20,7 @@ export const importFromStrapi = createStrapiImportEndpoint<StrapiRecorder>({
strapi: { strapi: {
collection: "recorders", collection: "recorders",
params: { params: {
populate: "bio.language,languages,avatar", populate: ["bio.language", "languages", "avatar"],
}, },
}, },
payload: { payload: {
@ -52,7 +53,7 @@ export const importFromStrapi = createStrapiImportEndpoint<StrapiRecorder>({
if (isUndefined(bio)) throw new Error("A bio is required for a Recorder biography"); if (isUndefined(bio)) throw new Error("A bio is required for a Recorder biography");
return { return {
language: language.data.attributes.code, language: language.data.attributes.code,
biography: bio, biography: plainTextToLexical(bio),
}; };
}), }),
}, },
@ -72,10 +73,9 @@ export const importFromStrapi = createStrapiImportEndpoint<StrapiRecorder>({
if (isUndefined(bio)) throw new Error("A bio is required for a Recorder biography"); if (isUndefined(bio)) throw new Error("A bio is required for a Recorder biography");
return { return {
language: language.data.attributes.code, language: language.data.attributes.code,
biography: bio, biography: plainTextToLexical(bio),
}; };
}), }),
email: `${anonymous_code}@accords-library.com`, email: `${anonymous_code}@accords-library.com`,
password: process.env.RECORDER_DEFAULT_PASSWORD, password: process.env.RECORDER_DEFAULT_PASSWORD,
}, },

View File

@ -4,6 +4,7 @@ import { imageField } from "../../fields/imageField/imageField";
import { keysField } from "../../fields/keysField/keysField"; import { keysField } from "../../fields/keysField/keysField";
import { slugField } from "../../fields/slugField/slugField"; import { slugField } from "../../fields/slugField/slugField";
import { translatedFields } from "../../fields/translatedFields/translatedFields"; import { translatedFields } from "../../fields/translatedFields/translatedFields";
import { createEditor } from "../../utils/editor";
import { buildVersionedCollectionConfig } from "../../utils/versionedCollectionConfig"; import { buildVersionedCollectionConfig } from "../../utils/versionedCollectionConfig";
import { AppearanceRowLabel } from "./components/AppearanceRowLabel"; import { AppearanceRowLabel } from "./components/AppearanceRowLabel";
import { getBySlugEndpoint } from "./endpoints/getBySlugEndpoint"; import { getBySlugEndpoint } from "./endpoints/getBySlugEndpoint";
@ -113,7 +114,8 @@ export const Weapons = buildVersionedCollectionConfig({
}, },
{ {
name: fields.appearancesTranslationsDescription, name: fields.appearancesTranslationsDescription,
type: "textarea", type: "richText",
editor: createEditor({ inlines: true }),
admin: { width: "0%" }, admin: { width: "0%" },
}, },
], ],
@ -124,13 +126,15 @@ export const Weapons = buildVersionedCollectionConfig({
{ {
name: fields.appearancesTranslationsLevel1, name: fields.appearancesTranslationsLevel1,
label: "Level 1", label: "Level 1",
type: "textarea", type: "richText",
editor: createEditor({ inlines: true }),
admin: { width: "0%" }, admin: { width: "0%" },
}, },
{ {
name: fields.appearancesTranslationsLevel2, name: fields.appearancesTranslationsLevel2,
label: "Level 2", label: "Level 2",
type: "textarea", type: "richText",
editor: createEditor({ inlines: true }),
admin: { width: "0%" }, admin: { width: "0%" },
}, },
], ],
@ -141,13 +145,15 @@ export const Weapons = buildVersionedCollectionConfig({
{ {
name: fields.appearancesTranslationsLevel3, name: fields.appearancesTranslationsLevel3,
label: "Level 3", label: "Level 3",
type: "textarea", type: "richText",
editor: createEditor({ inlines: true }),
admin: { width: "0%" }, admin: { width: "0%" },
}, },
{ {
name: fields.appearancesTranslationsLevel4, name: fields.appearancesTranslationsLevel4,
label: "Level 4", label: "Level 4",
type: "textarea", type: "richText",
editor: createEditor({ inlines: true }),
admin: { width: "0%" }, admin: { width: "0%" },
}, },
], ],

View File

@ -8,10 +8,12 @@ export enum Collections {
Files = "files", Files = "files",
Keys = "keys", Keys = "keys",
Languages = "languages", Languages = "languages",
LibraryFolders = "library-folders",
LibraryItems = "library-items", LibraryItems = "library-items",
LibraryItemsThumbnails = "library-items-thumbnails", LibraryItemsThumbnails = "library-items-thumbnails",
LibraryItemsScans = "library-items-scans", LibraryItemsScans = "library-items-scans",
LibraryItemsGallery = "library-items-gallery", LibraryItemsGallery = "library-items-gallery",
Notes = "Notes",
Posts = "posts", Posts = "posts",
PostsThumbnails = "posts-thumbnails", PostsThumbnails = "posts-thumbnails",
Recorders = "recorders", Recorders = "recorders",

View File

@ -43,7 +43,7 @@ type Params<S> = {
convert?: ( convert?: (
strapiObject: S, strapiObject: S,
user: any user: any
) => Parameters<BasePayload<GeneratedTypes>["create"]>[0]["data"]; ) => Promise<Parameters<BasePayload<GeneratedTypes>["create"]>[0]["data"]>;
}; };
}; };
@ -64,7 +64,7 @@ export const importStrapiEntries = async <S>({
} else if (isDefined(payloadParams.convert)) { } else if (isDefined(payloadParams.convert)) {
await payload.create({ await payload.create({
collection: payloadParams.collection, collection: payloadParams.collection,
data: payloadParams.convert(attributes, user), data: await payloadParams.convert(attributes, user),
user, user,
}); });
} else { } else {

View File

@ -28,7 +28,7 @@ const languageField: Field = {
type: "relationship", type: "relationship",
relationTo: Collections.Languages, relationTo: Collections.Languages,
required: true, required: true,
admin: { allowCreate: false, width: "0%" }, admin: { allowCreate: false },
}; };
const sourceLanguageField: Field = { const sourceLanguageField: Field = {
@ -36,7 +36,7 @@ const sourceLanguageField: Field = {
type: "relationship", type: "relationship",
relationTo: Collections.Languages, relationTo: Collections.Languages,
required: true, required: true,
admin: { allowCreate: false, width: "0%" }, admin: { allowCreate: false },
}; };
const creditFields: Field = { const creditFields: Field = {

View File

@ -1,11 +1,9 @@
import { webpackBundler } from "@payloadcms/bundler-webpack"; import { webpackBundler } from "@payloadcms/bundler-webpack";
import { mongooseAdapter } from "@payloadcms/db-mongodb"; import { mongooseAdapter } from "@payloadcms/db-mongodb";
import { BlocksFeature, lexicalEditor } from "@payloadcms/richtext-lexical";
import path from "path"; import path from "path";
import { buildConfig } from "payload/config"; import { buildConfig } from "payload/config";
import { ChronologyEras } from "./collections/ChronologyEras/ChronologyEras"; import { ChronologyEras } from "./collections/ChronologyEras/ChronologyEras";
import { ChronologyItems } from "./collections/ChronologyItems/ChronologyItems"; import { ChronologyItems } from "./collections/ChronologyItems/ChronologyItems";
import { transcriptBlock } from "./collections/Contents/Blocks/transcriptBlock";
import { Contents } from "./collections/Contents/Contents"; import { Contents } from "./collections/Contents/Contents";
import { ContentsFolders } from "./collections/ContentsFolders/ContentsFolders"; import { ContentsFolders } from "./collections/ContentsFolders/ContentsFolders";
import { ContentsThumbnails } from "./collections/ContentsThumbnails/ContentsThumbnails"; import { ContentsThumbnails } from "./collections/ContentsThumbnails/ContentsThumbnails";
@ -13,10 +11,12 @@ import { Currencies } from "./collections/Currencies/Currencies";
import { Files } from "./collections/Files/Files"; import { Files } from "./collections/Files/Files";
import { Keys } from "./collections/Keys/Keys"; import { Keys } from "./collections/Keys/Keys";
import { Languages } from "./collections/Languages/Languages"; import { Languages } from "./collections/Languages/Languages";
import { LibraryFolders } from "./collections/LibraryFolders/LibraryFolders";
import { LibraryItems } from "./collections/LibraryItems/LibraryItems"; import { LibraryItems } from "./collections/LibraryItems/LibraryItems";
import { LibraryItemsGallery } from "./collections/LibraryItemsGallery/LibraryItemsGallery"; import { LibraryItemsGallery } from "./collections/LibraryItemsGallery/LibraryItemsGallery";
import { LibraryItemsScans } from "./collections/LibraryItemsScans/LibraryItemsScans"; import { LibraryItemsScans } from "./collections/LibraryItemsScans/LibraryItemsScans";
import { LibraryItemsThumbnails } from "./collections/LibraryItemsThumbnails/LibraryItemsThumbnails"; import { LibraryItemsThumbnails } from "./collections/LibraryItemsThumbnails/LibraryItemsThumbnails";
import { Notes } from "./collections/Notes/Notes";
import { Posts } from "./collections/Posts/Posts"; import { Posts } from "./collections/Posts/Posts";
import { PostsThumbnails } from "./collections/PostsThumbnails/PostsThumbnails"; import { PostsThumbnails } from "./collections/PostsThumbnails/PostsThumbnails";
import { Recorders } from "./collections/Recorders/Recorders"; import { Recorders } from "./collections/Recorders/Recorders";
@ -29,6 +29,7 @@ import { WeaponsThumbnails } from "./collections/WeaponsThumbnails/WeaponsThumbn
import { Icon } from "./components/Icon"; import { Icon } from "./components/Icon";
import { Logo } from "./components/Logo"; import { Logo } from "./components/Logo";
import { Collections } from "./constants"; import { Collections } from "./constants";
import { createEditor } from "./utils/editor";
export default buildConfig({ export default buildConfig({
serverURL: process.env.PAYLOAD_URI, serverURL: process.env.PAYLOAD_URI,
@ -43,13 +44,9 @@ export default buildConfig({
css: path.resolve(__dirname, "styles.scss"), css: path.resolve(__dirname, "styles.scss"),
bundler: webpackBundler(), bundler: webpackBundler(),
}, },
editor: lexicalEditor({ editor: createEditor({}),
features: ({ defaultFeatures }) => [
...defaultFeatures,
BlocksFeature({ blocks: [transcriptBlock] }),
],
}),
collections: [ collections: [
LibraryFolders,
LibraryItems, LibraryItems,
Contents, Contents,
ContentsFolders, ContentsFolders,
@ -66,6 +63,7 @@ export default buildConfig({
RecordersThumbnails, RecordersThumbnails,
PostsThumbnails, PostsThumbnails,
Files, Files,
Notes,
Videos, Videos,
VideosChannels, VideosChannels,
Languages, Languages,

View File

@ -56,6 +56,23 @@ html[data-theme="light"] {
} }
} }
.field-type.no-label > header {
display: none;
}
.lexical-block__block-pill-transcriptBlock + .section-title {
display: none;
}
.blocks-field__block-pill-cueBlock + .section-title {
display: none;
}
.rich-text-lexical.field-type.reduced-margins {
margin-top: -0.75em;
margin-bottom: -2rem;
}
.field-type.array-field.group-array { .field-type.array-field.group-array {
> .array-field__header { > .array-field__header {
.array-field__header-actions { .array-field__header-actions {

View File

@ -14,17 +14,15 @@ export type CategoryTranslations = {
}[]; }[];
export type RecorderBiographies = { export type RecorderBiographies = {
language: string | Language; language: string | Language;
biography: string; biography: {
id?: string; [k: string]: unknown;
}[]; }[];
export type ContentFoldersTranslation = {
language: string | Language;
name: string;
id?: string; id?: string;
}[]; }[];
export interface Config { export interface Config {
collections: { collections: {
'library-folders': LibraryFolder;
'library-items': LibraryItem; 'library-items': LibraryItem;
contents: Content; contents: Content;
'contents-folders': ContentsFolder; 'contents-folders': ContentsFolder;
@ -41,6 +39,7 @@ export interface Config {
'recorders-thumbnails': RecordersThumbnail; 'recorders-thumbnails': RecordersThumbnail;
'posts-thumbnails': PostThumbnail; 'posts-thumbnails': PostThumbnail;
files: File; files: File;
Notes: Note;
videos: Video; videos: Video;
'videos-channels': VideosChannel; 'videos-channels': VideosChannel;
languages: Language; languages: Language;
@ -52,9 +51,28 @@ export interface Config {
}; };
globals: {}; globals: {};
} }
export interface LibraryFolder {
id: string;
slug: string;
translations?: {
language: string | Language;
name: string;
description?: {
[k: string]: unknown;
}[];
id?: string;
}[];
subfolders?: string[] | LibraryFolder[];
items?: string[] | LibraryItem[];
}
export interface Language {
id: string;
name: string;
}
export interface LibraryItem { export interface LibraryItem {
id: string; id: string;
itemType?: 'Textual' | 'Audio' | 'Video' | 'Game' | 'Other'; itemType?: 'Textual' | 'Audio' | 'Video' | 'Game' | 'Other';
digital: boolean;
slug: string; slug: string;
thumbnail?: string | LibraryItemThumbnail; thumbnail?: string | LibraryItemThumbnail;
pretitle?: string; pretitle?: string;
@ -62,8 +80,6 @@ export interface LibraryItem {
subtitle?: string; subtitle?: string;
rootItem: boolean; rootItem: boolean;
primary: boolean; primary: boolean;
digital: boolean;
downloadable: boolean;
gallery?: { gallery?: {
image?: string | LibraryItemGallery; image?: string | LibraryItemGallery;
id?: string; id?: string;
@ -111,6 +127,7 @@ export interface LibraryItem {
image: string | LibraryItemScans; image: string | LibraryItemScans;
id?: string; id?: string;
}[]; }[];
downloadable: boolean;
id?: string; id?: string;
}[]; }[];
textual?: { textual?: {
@ -132,7 +149,9 @@ export interface LibraryItem {
categories?: string[] | Key[]; categories?: string[] | Key[];
translations?: { translations?: {
language: string | Language; language: string | Language;
description: string; description: {
[k: string]: unknown;
}[];
id?: string; id?: string;
}[]; }[];
size?: { size?: {
@ -156,7 +175,9 @@ export interface LibraryItem {
pageEnd?: number; pageEnd?: number;
timeStart?: number; timeStart?: number;
timeEnd?: number; timeEnd?: number;
note?: string; note?: {
[k: string]: unknown;
}[];
id?: string; id?: string;
}[]; }[];
updatedBy: string | Recorder; updatedBy: string | Recorder;
@ -292,10 +313,6 @@ export interface Key {
| 'Wordings'; | 'Wordings';
translations?: CategoryTranslations; translations?: CategoryTranslations;
} }
export interface Language {
id: string;
name: string;
}
export interface File { export interface File {
id: string; id: string;
filename: string; filename: string;
@ -318,16 +335,22 @@ export interface Content {
pretitle?: string; pretitle?: string;
title: string; title: string;
subtitle?: string; subtitle?: string;
summary?: string; summary?: {
[k: string]: unknown;
}[];
textContent?: { textContent?: {
[k: string]: unknown; [k: string]: unknown;
}[]; }[];
textTranscribers?: string[] | Recorder[]; textTranscribers?: string[] | Recorder[];
textTranslators?: string[] | Recorder[]; textTranslators?: string[] | Recorder[];
textProofreaders?: string[] | Recorder[]; textProofreaders?: string[] | Recorder[];
textNotes?: string; textNotes?: {
[k: string]: unknown;
}[];
video?: string | File; video?: string | File;
videoNotes?: string; videoNotes?: {
[k: string]: unknown;
}[];
audio?: string | File; audio?: string | File;
id?: string; id?: string;
}[]; }[];
@ -424,7 +447,11 @@ export interface RecordersThumbnail {
export interface ContentsFolder { export interface ContentsFolder {
id: string; id: string;
slug: string; slug: string;
translations?: ContentFoldersTranslation; translations?: {
language: string | Language;
name: string;
id?: string;
}[];
subfolders?: string[] | ContentsFolder[]; subfolders?: string[] | ContentsFolder[];
contents?: string[] | Content[]; contents?: string[] | Content[];
} }
@ -454,7 +481,9 @@ export interface Post {
language: string | Language; language: string | Language;
sourceLanguage: string | Language; sourceLanguage: string | Language;
title: string; title: string;
summary?: string; summary?: {
[k: string]: unknown;
}[];
translators?: string[] | Recorder[]; translators?: string[] | Recorder[];
proofreaders?: string[] | Recorder[]; proofreaders?: string[] | Recorder[];
content?: { content?: {
@ -529,8 +558,12 @@ export interface ChronologyItem {
language: string | Language; language: string | Language;
sourceLanguage: string | Language; sourceLanguage: string | Language;
title?: string; title?: string;
description?: string; description?: {
notes?: string; [k: string]: unknown;
}[];
notes?: {
[k: string]: unknown;
}[];
transcribers?: string[] | Recorder[]; transcribers?: string[] | Recorder[];
translators?: string[] | Recorder[]; translators?: string[] | Recorder[];
proofreaders?: string[] | Recorder[]; proofreaders?: string[] | Recorder[];
@ -551,7 +584,9 @@ export interface ChronologyEra {
translations?: { translations?: {
language: string | Language; language: string | Language;
title: string; title: string;
description?: string; description?: {
[k: string]: unknown;
}[];
id?: string; id?: string;
}[]; }[];
events?: string[] | ChronologyItem[]; events?: string[] | ChronologyItem[];
@ -570,11 +605,21 @@ export interface Weapon {
language: string | Language; language: string | Language;
sourceLanguage: string | Language; sourceLanguage: string | Language;
name: string; name: string;
description?: string; description?: {
level1?: string; [k: string]: unknown;
level2?: string; }[];
level3?: string; level1?: {
level4?: string; [k: string]: unknown;
}[];
level2?: {
[k: string]: unknown;
}[];
level3?: {
[k: string]: unknown;
}[];
level4?: {
[k: string]: unknown;
}[];
transcribers?: string[] | Recorder[]; transcribers?: string[] | Recorder[];
translators?: string[] | Recorder[]; translators?: string[] | Recorder[];
proofreaders?: string[] | Recorder[]; proofreaders?: string[] | Recorder[];
@ -643,6 +688,14 @@ export interface WeaponsGroup {
}[]; }[];
weapons?: string[] | Weapon[]; weapons?: string[] | Weapon[];
} }
export interface Note {
id: string;
note: {
[k: string]: unknown;
}[];
updatedAt: string;
createdAt: string;
}
export interface Video { export interface Video {
id: string; id: string;
uid: string; uid: string;
@ -690,33 +743,5 @@ export interface PayloadMigration {
declare module 'payload' { declare module 'payload' {
export interface GeneratedTypes { export interface GeneratedTypes extends Config {}
collections: {
'library-items': LibraryItem
'contents': Content
'contents-folders': ContentsFolder
'posts': Post
'chronology-items': ChronologyItem
'chronology-eras': ChronologyEra
'weapons': Weapon
'weapons-groups': WeaponsGroup
'weapons-thumbnails': WeaponsThumbnail
'contents-thumbnails': ContentsThumbnail
'library-items-thumbnails': LibraryItemThumbnail
'library-items-scans': LibraryItemScans
'library-items-gallery': LibraryItemGallery
'recorders-thumbnails': RecordersThumbnail
'posts-thumbnails': PostThumbnail
'files': File
'videos': Video
'videos-channels': VideosChannel
'languages': Language
'currencies': Currency
'recorders': Recorder
'keys': Key
'payload-preferences': PayloadPreference
'payload-migrations': PayloadMigration
}
}
} }

View File

@ -2,6 +2,10 @@ export type StrapiLanguage = {
data?: { attributes: { code: string } }; data?: { attributes: { code: string } };
}; };
export type StrapiRecorders = {
data?: { attributes: { username: string } }[];
};
export type StrapiImage = { export type StrapiImage = {
data?: { data?: {
attributes: { attributes: {
@ -9,6 +13,8 @@ export type StrapiImage = {
mime: string; mime: string;
name: string; name: string;
size: number; size: number;
hash: string;
ext: string;
}; };
}; };
}; };

75
src/utils/editor.ts Normal file
View File

@ -0,0 +1,75 @@
import {
AdapterProps,
AlignFeature,
BlocksFeature,
BoldTextFeature,
CheckListFeature,
FeatureProvider,
// IndentFeature,
HeadingFeature,
InlineCodeTextFeature,
ItalicTextFeature,
LinkFeature,
// BlockQuoteFeature,
OrderedListFeature,
ParagraphFeature,
RelationshipFeature,
StrikethroughTextFeature,
SubscriptTextFeature,
SuperscriptTextFeature,
TreeviewFeature,
UnderlineTextFeature,
UnoderedListFeature,
UploadFeature,
lexicalEditor,
} from "@payloadcms/richtext-lexical";
import { Block, RichTextAdapter } from "payload/types";
interface EditorOptions {
debugs: boolean;
blocks: Block[];
headings: boolean;
lists: boolean;
inlines: boolean;
images: boolean;
relations: boolean;
links: boolean;
alignment: boolean;
}
export const createEditor = ({
debugs = false,
blocks = [],
headings = false,
images = false,
inlines = false,
lists = false,
links = false,
relations = false,
alignment = false,
}: Partial<EditorOptions>): RichTextAdapter<any, AdapterProps> => {
const enabledFeatures: FeatureProvider[] = [];
if (lists) enabledFeatures.push(OrderedListFeature(), UnoderedListFeature(), CheckListFeature());
if (blocks.length > 0) enabledFeatures.push(BlocksFeature({ blocks }));
if (headings) enabledFeatures.push(ParagraphFeature(), HeadingFeature({}));
if (debugs) enabledFeatures.push(TreeviewFeature());
if (images) enabledFeatures.push(UploadFeature({ collections: [] }));
if (links) enabledFeatures.push(LinkFeature({}));
if (relations) enabledFeatures.push(RelationshipFeature());
if (alignment) enabledFeatures.push(AlignFeature());
if (inlines)
enabledFeatures.push(
BoldTextFeature(),
ItalicTextFeature(),
UnderlineTextFeature(),
StrikethroughTextFeature(),
SubscriptTextFeature(),
SuperscriptTextFeature(),
InlineCodeTextFeature()
);
return lexicalEditor({
features: enabledFeatures,
});
};

View File

@ -21,6 +21,24 @@ export const findCategory = async (name: string): Promise<string> => {
return key.docs[0]?.id; return key.docs[0]?.id;
}; };
export const findRecorder = async (name: string): Promise<string> => {
const recorder = await payload.find({
collection: Collections.Recorders,
where: { username: { equals: name } },
});
if (!recorder.docs[0]) throw new Error(`Recorder ${name} wasn't found`);
return recorder.docs[0]?.id;
};
export const findContentType = async (name: string): Promise<string> => {
const key = await payload.find({
collection: Collections.Keys,
where: { name: { equals: name }, type: { equals: KeysTypes.Contents } },
});
if (!key.docs[0]) throw new Error(`Content type ${name} wasn't found`);
return key.docs[0]?.id;
};
type UploadStrapiImage = { type UploadStrapiImage = {
image: StrapiImage; image: StrapiImage;
collection: Collections; collection: Collections;
@ -31,6 +49,16 @@ export const uploadStrapiImage = async ({
image, image,
}: UploadStrapiImage): Promise<string | undefined> => { }: UploadStrapiImage): Promise<string | undefined> => {
if (isDefined(image.data)) { if (isDefined(image.data)) {
const filename = image.data.attributes.hash + image.data.attributes.ext;
const existingImage = await payload.find({
collection,
where: { filename: { equals: filename } },
});
if (existingImage.docs[0]) {
return existingImage.docs[0].id;
}
const url = `${process.env.STRAPI_URI}${image.data.attributes.url}`; const url = `${process.env.STRAPI_URI}${image.data.attributes.url}`;
const blob = await (await fetch(url)).blob(); const blob = await (await fetch(url)).blob();
@ -41,7 +69,7 @@ export const uploadStrapiImage = async ({
file: { file: {
data: buffer, data: buffer,
mimetype: image.data.attributes.mime, mimetype: image.data.attributes.mime,
name: image.data.attributes.name, name: filename,
size: image.data.attributes.size, size: image.data.attributes.size,
}, },
data: {}, data: {},

View File

@ -21,3 +21,37 @@ export const formatToCamelCase = (name: string): string =>
.join(""); .join("");
export const formatToPascalCase = (name: string): string => capitalize(formatToCamelCase(name)); export const formatToPascalCase = (name: string): string => capitalize(formatToCamelCase(name));
export const plainTextToLexical = (
text: string
): {
[k: string]: unknown;
}[] => ({
root: {
type: "root",
format: "",
indent: 0,
version: 1,
children: [
{
children: [
{
detail: 0,
format: 0,
mode: "normal",
style: "",
text,
type: "text",
version: 1,
},
],
direction: "ltr",
format: "",
indent: 0,
type: "paragraph",
version: 1,
},
],
direction: "ltr",
},
});