Moved to 2.0

This commit is contained in:
DrMint 2023-10-14 11:27:32 +02:00
parent 26b4798761
commit 76b7e4a8a2
59 changed files with 2113 additions and 2405 deletions

BIN
bun.lockb

Binary file not shown.

View File

@ -9,7 +9,7 @@ services:
- .:/home/node/app - .:/home/node/app
- node_modules:/home/node/app/node_modules - node_modules:/home/node/app/node_modules
working_dir: /home/node/app/ working_dir: /home/node/app/
command: sh -c "npm install && npm run dev" command: sh -c "npm install && npm run generate:types && npm run dev"
depends_on: depends_on:
- mongo - mongo
environment: environment:

2174
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -13,21 +13,25 @@
"copyfiles": "copyfiles -u 1 \"src/**/*.{html,css,scss,ttf,woff,woff2,eot,svg,jpg,png}\" dist/", "copyfiles": "copyfiles -u 1 \"src/**/*.{html,css,scss,ttf,woff,woff2,eot,svg,jpg,png}\" dist/",
"generate:types": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts payload generate:types", "generate:types": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts payload generate:types",
"generate:graphQLSchema": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts payload generate:graphQLSchema", "generate:graphQLSchema": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts payload generate:graphQLSchema",
"unused-exports": "ts-unused-exports ./tsconfig.json --excludePathsFromReport='src/payload.config.ts;src/sdk.ts;src/types/collections.ts'",
"prettier": "prettier --list-different --end-of-line auto --write src", "prettier": "prettier --list-different --end-of-line auto --write src",
"tsc": "tsc --noEmit", "tsc": "tsc --noEmit",
"precommit": "npm run generate:types && npm run prettier && npm run tsc", "precommit": "npm run generate:types && npm run prettier && npm run unused-exports && npm run tsc",
"upgrade": "ncu", "upgrade": "ncu",
"clean": "sudo rm -r uploads mongo", "clean": "sudo rm -r uploads mongo",
"start": "sudo docker compose up" "start": "sudo docker compose up"
}, },
"dependencies": { "dependencies": {
"@fontsource/vollkorn": "^5.0.14", "@fontsource/vollkorn": "^5.0.14",
"@payloadcms/bundler-webpack": "^1.0.3",
"@payloadcms/db-mongodb": "^1.0.3",
"@payloadcms/richtext-lexical": "^0.1.8",
"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": "^1.15.7", "payload": "^2.0.5",
"styled-components": "^6.0.8" "styled-components": "^6.0.9"
}, },
"devDependencies": { "devDependencies": {
"@types/dotenv": "^8.2.0", "@types/dotenv": "^8.2.0",
@ -36,10 +40,12 @@
"@types/luxon": "^3.3.2", "@types/luxon": "^3.3.2",
"@types/qs": "^6.9.8", "@types/qs": "^6.9.8",
"@types/react-router-dom": "^5.3.3", "@types/react-router-dom": "^5.3.3",
"@types/styled-components": "^5.1.28",
"copyfiles": "^2.4.1", "copyfiles": "^2.4.1",
"nodemon": "^3.0.1", "nodemon": "^3.0.1",
"prettier": "^3.0.3", "prettier": "^3.0.3",
"ts-node": "^10.9.1", "ts-node": "^10.9.1",
"ts-unused-exports": "^10.0.1",
"typescript": "^5.2.2" "typescript": "^5.2.2"
} }
} }

View File

@ -1,8 +0,0 @@
import { Access } from "payload/config";
import { Recorder } from "../../types/collections";
import { isDefined, isUndefined } from "../../utils/asserts";
export const mustHaveAtLeastOneRole: Access<unknown, Recorder> = ({ req: { user } }): boolean => {
if (isUndefined(user)) return false;
return isDefined(user.role) && user.role.length > 0;
};

View File

@ -1,9 +0,0 @@
import { RecordersRoles } from "../../constants";
import { Recorder } from "../../types/collections";
import { EndpointAccess } from "../../types/payload";
import { isDefined, isUndefined } from "../../utils/asserts";
export const mustBeApi: EndpointAccess<Recorder> = ({ user }) => {
if (isUndefined(user)) return false;
return isDefined(user.role) && user.role.includes(RecordersRoles.Api);
};

View File

@ -1,8 +0,0 @@
import { Recorder } from "../../types/collections";
import { EndpointAccess } from "../../types/payload";
import { isDefined, isUndefined } from "../../utils/asserts";
export const mustHaveAtLeastOneRole: EndpointAccess<Recorder> = ({ user }) => {
if (isUndefined(user)) return false;
return isDefined(user.role) && user.role.length > 0;
};

View File

@ -50,14 +50,14 @@ export const ChronologyEras: CollectionConfig = buildCollectionConfig({
type: "number", type: "number",
min: 0, min: 0,
required: true, required: true,
admin: { width: "50%", description: "The year the era started (year included)" }, admin: { width: "0%", description: "The year the era started (year included)" },
}, },
{ {
name: fields.endingYear, name: fields.endingYear,
type: "number", type: "number",
min: 0, min: 0,
required: true, required: true,
admin: { width: "50%", description: "The year the era ended (year included)" }, admin: { width: "0%", description: "The year the era ended (year included)" },
}, },
], ],
}, },

View File

@ -1,6 +1,5 @@
import { Collections } from "../../../constants"; import { Collections } from "../../../constants";
import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint"; import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint";
import { ChronologyEra } from "../../../types/collections";
import { StrapiLanguage } from "../../../types/strapi"; import { StrapiLanguage } from "../../../types/strapi";
import { isUndefined } from "../../../utils/asserts"; import { isUndefined } from "../../../utils/asserts";
@ -11,7 +10,7 @@ type StrapiChronologyEra = {
title: { title: string; language: StrapiLanguage; description?: string }[]; title: { title: string; language: StrapiLanguage; description?: string }[];
}; };
export const importFromStrapi = createStrapiImportEndpoint<ChronologyEra, StrapiChronologyEra>({ export const importFromStrapi = createStrapiImportEndpoint<StrapiChronologyEra>({
strapi: { strapi: {
collection: "chronology-eras", collection: "chronology-eras",
params: { params: {

View File

@ -76,10 +76,22 @@ export const ChronologyItems: CollectionConfig = buildVersionedCollectionConfig(
type: "number", type: "number",
required: true, required: true,
min: 0, min: 0,
admin: { width: "33%" }, admin: { width: "0%" },
},
{
name: fields.dateMonth,
type: "number",
min: 1,
max: 12,
admin: { width: "0%" },
},
{
name: fields.dateDay,
type: "number",
min: 1,
max: 31,
admin: { width: "0%" },
}, },
{ name: fields.dateMonth, type: "number", min: 1, max: 12, admin: { width: "33%" } },
{ name: fields.dateDay, type: "number", min: 1, max: 31, admin: { width: "33%" } },
], ],
}, },
], ],

View File

@ -1,6 +1,5 @@
import { Collections } from "../../../constants"; import { Collections } from "../../../constants";
import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint"; import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint";
import { ChronologyItem } from "../../../types/collections";
import { StrapiLanguage } from "../../../types/strapi"; import { StrapiLanguage } from "../../../types/strapi";
import { isUndefined } from "../../../utils/asserts"; import { isUndefined } from "../../../utils/asserts";
@ -18,7 +17,7 @@ type StrapiChronologyItem = {
}[]; }[];
}; };
export const importFromStrapi = createStrapiImportEndpoint<ChronologyItem, StrapiChronologyItem>({ export const importFromStrapi = createStrapiImportEndpoint<StrapiChronologyItem>({
strapi: { strapi: {
collection: "chronology-items", collection: "chronology-items",
params: { params: {

View File

@ -1,81 +0,0 @@
import { BlocksConfig, generateBlocks } from "../../../utils/recursiveBlocks";
import { quoteBlock } from "./quoteBlock";
import { textBlock } from "./textBlock";
import { transcriptBlock } from "./transcriptBlock";
const enum BlockName {
Text = "Text",
Section = "Section",
Tabs = "Tabs",
Tab = "Tab",
Transcript = "Transcript",
Quote = "Quote",
}
const blocksConfig: BlocksConfig<BlockName> = {
Text: {
root: true,
block: textBlock,
},
Section: {
root: true,
block: {
slug: "section",
labels: { singular: "Section", plural: "Sections" },
recursion: {
name: "content",
condition: (depth) => depth < 5,
newDepth: (depth) => depth + 1,
blocks: [
BlockName.Section,
BlockName.Tabs,
BlockName.Transcript,
BlockName.Quote,
BlockName.Text,
],
},
},
},
Tabs: {
root: true,
block: {
slug: "tabs",
labels: { singular: "Tabs", plural: "Tabs" },
recursion: {
name: "tabs",
newDepth: (depth) => depth,
condition: (depth, parents) => !parents.includes(BlockName.Tabs) && depth < 5,
blocks: [BlockName.Tab],
},
},
},
Tab: {
root: false,
block: {
slug: "tab",
labels: { singular: "Tab", plural: "Tabs" },
recursion: {
name: "content",
condition: (depth) => depth < 5,
newDepth: (depth) => depth + 1,
blocks: [
BlockName.Section,
BlockName.Tabs,
BlockName.Transcript,
BlockName.Quote,
BlockName.Text,
],
},
},
},
Transcript: {
root: true,
block: transcriptBlock,
},
Quote: {
root: true,
block: quoteBlock,
},
};
export const contentBlocks = generateBlocks(blocksConfig);

View File

@ -10,11 +10,6 @@ export const lineBlock: Block = {
label: false, label: false,
type: "richText", type: "richText",
required: true, required: true,
admin: {
hideGutter: true,
elements: [],
leaves: ["bold", "italic", "underline", "strikethrough", "code"],
},
}, },
], ],
}; };

View File

@ -1,24 +0,0 @@
import { Block } from "payload/types";
export const quoteBlock: Block = {
slug: "quoteBlock",
interfaceName: "QuoteBlock",
labels: { singular: "Quote", plural: "Quotes" },
fields: [
{
name: "from",
type: "text",
required: true,
},
{
name: "content",
type: "richText",
label: false,
required: true,
admin: {
hideGutter: true,
elements: [],
},
},
],
};

View File

@ -1,19 +0,0 @@
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"],
},
},
],
};

View File

@ -9,7 +9,6 @@ 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 { buildVersionedCollectionConfig } from "../../utils/versionedCollectionConfig"; import { buildVersionedCollectionConfig } from "../../utils/versionedCollectionConfig";
import { contentBlocks } from "./Blocks/blocks";
const fields = { const fields = {
slug: "slug", slug: "slug",
@ -66,11 +65,11 @@ export const Contents = buildVersionedCollectionConfig({
{ {
type: "row", type: "row",
fields: [ fields: [
slugField({ name: fields.slug, admin: { width: "50%" } }), slugField({ name: fields.slug, admin: { width: "0%" } }),
imageField({ imageField({
name: fields.thumbnail, name: fields.thumbnail,
relationTo: Collections.ContentsThumbnails, relationTo: Collections.ContentsThumbnails,
admin: { width: "50%" }, admin: { width: "0%" },
}), }),
], ],
}, },
@ -81,12 +80,12 @@ export const Contents = buildVersionedCollectionConfig({
name: fields.categories, name: fields.categories,
relationTo: KeysTypes.Categories, relationTo: KeysTypes.Categories,
hasMany: true, hasMany: true,
admin: { allowCreate: false, width: "50%" }, admin: { allowCreate: false, width: "0%" },
}), }),
keysField({ keysField({
name: fields.type, name: fields.type,
relationTo: KeysTypes.Contents, relationTo: KeysTypes.Contents,
admin: { allowCreate: false, width: "50%" }, admin: { allowCreate: false, width: "0%" },
}), }),
], ],
}, },
@ -115,6 +114,7 @@ export const Contents = buildVersionedCollectionConfig({
{ {
label: "Text", label: "Text",
fields: [ fields: [
{ name: fields.textContent, type: "richText" },
{ {
type: "row", type: "row",
fields: [ fields: [
@ -127,7 +127,7 @@ export const Contents = buildVersionedCollectionConfig({
admin: { admin: {
condition: (_, siblingData) => condition: (_, siblingData) =>
siblingData.language === siblingData.sourceLanguage, siblingData.language === siblingData.sourceLanguage,
width: "50%", width: "0%",
}, },
}, },
{ {
@ -139,7 +139,7 @@ export const Contents = buildVersionedCollectionConfig({
admin: { admin: {
condition: (_, siblingData) => condition: (_, siblingData) =>
siblingData.language !== siblingData.sourceLanguage, siblingData.language !== siblingData.sourceLanguage,
width: "50%", width: "0%",
}, },
}, },
{ {
@ -148,18 +148,10 @@ export const Contents = buildVersionedCollectionConfig({
type: "relationship", type: "relationship",
relationTo: Collections.Recorders, relationTo: Collections.Recorders,
hasMany: true, hasMany: true,
admin: { width: "50%" }, admin: { width: "0%" },
}, },
], ],
}, },
{
name: fields.textContent,
label: "Content",
labels: { singular: "Block", plural: "Blocks" },
type: "blocks",
admin: { initCollapsed: true },
blocks: contentBlocks,
},
{ {
name: fields.textNotes, name: fields.textNotes,
label: "Notes", label: "Notes",
@ -176,13 +168,13 @@ export const Contents = buildVersionedCollectionConfig({
fileField({ fileField({
name: fields.video, name: fields.video,
relationTo: FileTypes.ContentVideo, relationTo: FileTypes.ContentVideo,
admin: { width: "50%" }, admin: { width: "0%" },
}), }),
{ {
name: fields.videoNotes, name: fields.videoNotes,
label: "Notes", label: "Notes",
type: "textarea", type: "textarea",
admin: { width: "50%" }, admin: { width: "0%" },
}, },
], ],
}, },
@ -197,13 +189,13 @@ export const Contents = buildVersionedCollectionConfig({
fileField({ fileField({
name: fields.audio, name: fields.audio,
relationTo: FileTypes.ContentAudio, relationTo: FileTypes.ContentAudio,
admin: { width: "50%" }, admin: { width: "0%" },
}), }),
{ {
name: fields.audioNotes, name: fields.audioNotes,
label: "Notes", label: "Notes",
type: "textarea", type: "textarea",
admin: { width: "50%" }, admin: { width: "0%" },
}, },
], ],
}, },

View File

@ -44,14 +44,14 @@ export const ContentsFolders = buildCollectionConfig({
name: fields.subfolders, name: fields.subfolders,
relationTo: Collections.ContentsFolders, relationTo: Collections.ContentsFolders,
hasMany: true, hasMany: true,
admin: { width: "50%" }, admin: { width: "0%" },
}, },
{ {
type: "relationship", type: "relationship",
name: fields.contents, name: fields.contents,
relationTo: Collections.Contents, relationTo: Collections.Contents,
hasMany: true, hasMany: true,
admin: { width: "50%" }, admin: { width: "0%" },
}, },
], ],
}, },

View File

@ -1,13 +1,12 @@
import { Collections } from "../../../constants"; import { Collections } from "../../../constants";
import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint"; import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint";
import { Language } from "../../../types/collections";
type StrapiLanguage = { type StrapiLanguage = {
code: string; code: string;
name: string; name: string;
}; };
export const importFromStrapi = createStrapiImportEndpoint<Language, StrapiLanguage>({ export const importFromStrapi = createStrapiImportEndpoint<StrapiLanguage>({
strapi: { strapi: {
collection: "currencies", collection: "currencies",
params: {}, params: {},

View File

@ -102,7 +102,7 @@ export const Keys = buildCollectionConfig({
name: fields.translationsName, name: fields.translationsName,
type: "text", type: "text",
required: true, required: true,
admin: { width: "50%" }, admin: { width: "0%" },
}, },
{ {
name: fields.translationsShort, name: fields.translationsShort,
@ -110,7 +110,7 @@ export const Keys = buildCollectionConfig({
admin: { admin: {
condition: (data: Partial<Key>) => condition: (data: Partial<Key>) =>
isDefined(data.type) && keysTypesWithShort.includes(data.type), isDefined(data.type) && keysTypesWithShort.includes(data.type),
width: "50%", width: "0%",
}, },
}, },
], ],

View File

@ -5,51 +5,45 @@ import {
importStrapiEntries, importStrapiEntries,
} from "../../../endpoints/createStrapiImportEndpoint"; } from "../../../endpoints/createStrapiImportEndpoint";
import { Key } from "../../../types/collections"; import { Key } from "../../../types/collections";
import { CollectionEndpoint, PayloadCreateData } from "../../../types/payload"; import { CollectionEndpoint } from "../../../types/payload";
import { StrapiLanguage } from "../../../types/strapi"; import { StrapiLanguage } from "../../../types/strapi";
import { isDefined, isUndefined } from "../../../utils/asserts"; import { isDefined, isUndefined } from "../../../utils/asserts";
import { formatToCamelCase } from "../../../utils/string"; import { formatToCamelCase } from "../../../utils/string";
const importStrapiWordings: typeof importStrapiEntries = async ({ const importStrapiWordings: typeof importStrapiEntries = async ({ strapi: strapiParams, user }) => {
payload: payloadParams, const rawEntries = await getAllStrapiEntries(strapiParams.collection, strapiParams.params);
strapi: strapiParams,
user,
}) => {
const rawEntries = await getAllStrapiEntries<any>(strapiParams.collection, strapiParams.params);
const { ui_language, createdAt, updatedAt, ...otherKeys } = rawEntries[0].attributes; const { ui_language, createdAt, updatedAt, ...otherKeys } = rawEntries[0].attributes;
const entries: PayloadCreateData<Key>[] = Object.keys(otherKeys).map((key) => ({
name: formatToCamelCase(key),
type: "Wordings",
translations: rawEntries
.map((entry) => ({
language: entry.attributes.ui_language.data.attributes.code,
name: entry.attributes[key],
}))
.filter(({ name }) => isDefined(name) && name !== ""),
}));
const errors: string[] = []; const errors: string[] = [];
await Promise.all( await Promise.all(
entries.map(async (entry) => { Object.keys(otherKeys).map(async (key) => {
try { try {
await payload.create({ await payload.create({
collection: payloadParams.collection, collection: Collections.Keys,
data: entry, data: {
name: formatToCamelCase(key),
type: "Wordings",
translations: rawEntries
.map((entry) => ({
language: entry.attributes.ui_language.data.attributes.code,
name: entry.attributes[key],
}))
.filter(({ name }) => isDefined(name) && name !== ""),
},
user, user,
}); });
} catch (e) { } catch (e) {
console.warn(e); console.warn(e);
if (typeof e === "object" && isDefined(e) && "name" in e) { if (typeof e === "object" && isDefined(e) && "name" in e) {
errors.push(`${e.name} with ${entry.name}`); errors.push(`${e.name} with ${key}`);
} }
} }
}) })
); );
return { count: entries.length, errors }; return { count: Object.keys(otherKeys).length, errors };
}; };
export const importFromStrapi: CollectionEndpoint = { export const importFromStrapi: CollectionEndpoint = {
@ -71,289 +65,271 @@ export const importFromStrapi: CollectionEndpoint = {
titles: { title?: string; short?: string; language: StrapiLanguage }[]; titles: { title?: string; short?: string; language: StrapiLanguage }[];
}; };
const { count: categoriesCount, errors: categoriesErrors } = await importStrapiEntries< const { count: categoriesCount, errors: categoriesErrors } =
Key, await importStrapiEntries<StrapiCategories>({
StrapiCategories strapi: {
>({ collection: "categories",
strapi: { params: { populate: { titles: { populate: "language" } } },
collection: "categories", },
params: { populate: { titles: { populate: "language" } } }, payload: {
}, collection: Collections.Keys,
payload: { convert: ({ slug, titles }) => ({
collection: Collections.Keys, name: slug,
convert: ({ slug, titles }) => ({ type: "Categories",
name: slug, translations: titles.map(({ title, short, language }) => {
type: "Categories", if (isUndefined(language.data))
translations: titles.map(({ title, short, language }) => { throw new Error("A language is required for a Keys title translation");
if (isUndefined(language.data)) if (isUndefined(title))
throw new Error("A language is required for a Keys title translation"); throw new Error("A title is required for a Keys title translation");
if (isUndefined(title)) return {
throw new Error("A title is required for a Keys title translation"); name: title,
return { short,
name: title, language: language.data.attributes.code,
short, };
language: language.data.attributes.code, }),
};
}), }),
}), },
}, user: req.user,
user: req.user, });
});
type StrapiContentType = { type StrapiContentType = {
slug: string; slug: string;
titles: { title: string; language: StrapiLanguage }[]; titles: { title: string; language: StrapiLanguage }[];
}; };
const { count: contentTypesCount, errors: contentTypesErrors } = await importStrapiEntries< const { count: contentTypesCount, errors: contentTypesErrors } =
Key, await importStrapiEntries<StrapiContentType>({
StrapiContentType strapi: {
>({ collection: "content-types",
strapi: { params: { populate: { titles: { populate: "language" } } },
collection: "content-types", },
params: { populate: { titles: { populate: "language" } } }, payload: {
}, collection: Collections.Keys,
payload: { convert: ({ slug, titles }) => ({
collection: Collections.Keys, name: slug,
convert: ({ slug, titles }) => ({ type: "Contents",
name: slug, translations: titles.map(({ title, language }) => {
type: "Contents", if (isUndefined(language.data))
translations: titles.map(({ title, language }) => { throw new Error("A language is required for a Keys title translation");
if (isUndefined(language.data)) return {
throw new Error("A language is required for a Keys title translation"); name: title,
return { language: language.data.attributes.code,
name: title, };
language: language.data.attributes.code, }),
};
}), }),
}), },
}, user: req.user,
user: req.user, });
});
type StrapiGamePlatform = { type StrapiGamePlatform = {
slug: string; slug: string;
titles: { title?: string; short?: string; language: StrapiLanguage }[]; titles: { title?: string; short?: string; language: StrapiLanguage }[];
}; };
const { count: gamePlatformsCount, errors: gamePlatformsErrors } = await importStrapiEntries< const { count: gamePlatformsCount, errors: gamePlatformsErrors } =
Key, await importStrapiEntries<StrapiGamePlatform>({
StrapiGamePlatform strapi: {
>({ collection: "game-platforms",
strapi: { params: { populate: { titles: { populate: "language" } } },
collection: "game-platforms", },
params: { populate: { titles: { populate: "language" } } }, payload: {
}, collection: Collections.Keys,
payload: { convert: ({ slug, titles }) => ({
collection: Collections.Keys, name: slug,
convert: ({ slug, titles }) => ({ type: "GamePlatforms",
name: slug, translations: titles.map(({ title, short, language }) => {
type: "GamePlatforms", if (isUndefined(language.data))
translations: titles.map(({ title, short, language }) => { throw new Error("A language is required for a Keys title translation");
if (isUndefined(language.data)) if (isUndefined(title))
throw new Error("A language is required for a Keys title translation"); throw new Error("A title is required for a Keys title translation");
if (isUndefined(title)) return {
throw new Error("A title is required for a Keys title translation"); name: title,
return { short,
name: title, language: language.data.attributes.code,
short, };
language: language.data.attributes.code, }),
};
}), }),
}), },
}, user: req.user,
user: req.user, });
});
type StrapiMetadataTypes = { type StrapiMetadataTypes = {
slug: string; slug: string;
titles: { title: string; language: StrapiLanguage }[]; titles: { title: string; language: StrapiLanguage }[];
}; };
const { count: libraryCount, errors: libraryErrors } = await importStrapiEntries< const { count: libraryCount, errors: libraryErrors } =
Key, await importStrapiEntries<StrapiMetadataTypes>({
StrapiMetadataTypes strapi: {
>({ collection: "metadata-types",
strapi: { params: { populate: { titles: { populate: "language" } } },
collection: "metadata-types", },
params: { populate: { titles: { populate: "language" } } }, payload: {
}, collection: Collections.Keys,
payload: { convert: ({ slug, titles }) => ({
collection: Collections.Keys, name: slug,
convert: ({ slug, titles }) => ({ type: "Library",
name: slug, translations: titles.map(({ title, language }) => {
type: "Library", if (isUndefined(language.data))
translations: titles.map(({ title, language }) => { throw new Error("A language is required for a Keys title translation");
if (isUndefined(language.data)) return {
throw new Error("A language is required for a Keys title translation"); name: title,
return { language: language.data.attributes.code,
name: title, };
language: language.data.attributes.code, }),
};
}), }),
}), },
}, user: req.user,
user: req.user, });
});
type StrapiAudioSubtypes = { type StrapiAudioSubtypes = {
slug: string; slug: string;
titles: { title: string; language: StrapiLanguage }[]; titles: { title: string; language: StrapiLanguage }[];
}; };
const { count: libraryAudioCount, errors: libraryAudioErrors } = await importStrapiEntries< const { count: libraryAudioCount, errors: libraryAudioErrors } =
Key, await importStrapiEntries<StrapiAudioSubtypes>({
StrapiAudioSubtypes strapi: {
>({ collection: "audio-subtypes",
strapi: { params: { populate: { titles: { populate: "language" } } },
collection: "audio-subtypes", },
params: { populate: { titles: { populate: "language" } } }, payload: {
}, collection: Collections.Keys,
payload: { convert: ({ slug, titles }) => ({
collection: Collections.Keys, name: slug,
convert: ({ slug, titles }) => ({ type: "LibraryAudio",
name: slug, translations: titles.map(({ title, language }) => {
type: "LibraryAudio", if (isUndefined(language.data))
translations: titles.map(({ title, language }) => { throw new Error("A language is required for a Keys title translation");
if (isUndefined(language.data)) return {
throw new Error("A language is required for a Keys title translation"); name: title,
return { language: language.data.attributes.code,
name: title, };
language: language.data.attributes.code, }),
};
}), }),
}), },
}, user: req.user,
user: req.user, });
});
type StrapiGroupSubtypes = { type StrapiGroupSubtypes = {
slug: string; slug: string;
titles: { title: string; language: StrapiLanguage }[]; titles: { title: string; language: StrapiLanguage }[];
}; };
const { count: libraryGroupCount, errors: libraryGroupErrors } = await importStrapiEntries< const { count: libraryGroupCount, errors: libraryGroupErrors } =
Key, await importStrapiEntries<StrapiGroupSubtypes>({
StrapiGroupSubtypes strapi: {
>({ collection: "group-subtypes",
strapi: { params: { populate: { titles: { populate: "language" } } },
collection: "group-subtypes", },
params: { populate: { titles: { populate: "language" } } }, payload: {
}, collection: Collections.Keys,
payload: { convert: ({ slug, titles }) => ({
collection: Collections.Keys, name: slug,
convert: ({ slug, titles }) => ({ type: "LibraryGroup",
name: slug, translations: titles.map(({ title, language }) => {
type: "LibraryGroup", if (isUndefined(language.data))
translations: titles.map(({ title, language }) => { throw new Error("A language is required for a Keys title translation");
if (isUndefined(language.data)) return {
throw new Error("A language is required for a Keys title translation"); name: title,
return { language: language.data.attributes.code,
name: title, };
language: language.data.attributes.code, }),
};
}), }),
}), },
}, user: req.user,
user: req.user, });
});
type StrapiTextualSubtypes = { type StrapiTextualSubtypes = {
slug: string; slug: string;
titles: { title: string; language: StrapiLanguage }[]; titles: { title: string; language: StrapiLanguage }[];
}; };
const { count: libraryTextualCount, errors: libraryTextualErrors } = await importStrapiEntries< const { count: libraryTextualCount, errors: libraryTextualErrors } =
Key, await importStrapiEntries<StrapiTextualSubtypes>({
StrapiTextualSubtypes strapi: {
>({ collection: "textual-subtypes",
strapi: { params: { populate: { titles: { populate: "language" } } },
collection: "textual-subtypes", },
params: { populate: { titles: { populate: "language" } } }, payload: {
}, collection: Collections.Keys,
payload: { convert: ({ slug, titles }) => ({
collection: Collections.Keys, name: slug,
convert: ({ slug, titles }) => ({ type: "LibraryTextual",
name: slug, translations: titles.map(({ title, language }) => {
type: "LibraryTextual", if (isUndefined(language.data))
translations: titles.map(({ title, language }) => { throw new Error("A language is required for a Keys title translation");
if (isUndefined(language.data)) return {
throw new Error("A language is required for a Keys title translation"); name: title,
return { language: language.data.attributes.code,
name: title, };
language: language.data.attributes.code, }),
};
}), }),
}), },
}, user: req.user,
user: req.user, });
});
type StrapiVideoSubtypes = { type StrapiVideoSubtypes = {
slug: string; slug: string;
titles: { title: string; language: StrapiLanguage }[]; titles: { title: string; language: StrapiLanguage }[];
}; };
const { count: libraryVideoCount, errors: libraryVideoErrors } = await importStrapiEntries< const { count: libraryVideoCount, errors: libraryVideoErrors } =
Key, await importStrapiEntries<StrapiVideoSubtypes>({
StrapiVideoSubtypes strapi: {
>({ collection: "video-subtypes",
strapi: { params: { populate: { titles: { populate: "language" } } },
collection: "video-subtypes", },
params: { populate: { titles: { populate: "language" } } }, payload: {
}, collection: Collections.Keys,
payload: { convert: ({ slug, titles }) => ({
collection: Collections.Keys, name: slug,
convert: ({ slug, titles }) => ({ type: "LibraryVideo",
name: slug, translations: titles.map(({ title, language }) => {
type: "LibraryVideo", if (isUndefined(language.data))
translations: titles.map(({ title, language }) => { throw new Error("A language is required for a Keys title translation");
if (isUndefined(language.data)) return {
throw new Error("A language is required for a Keys title translation"); name: title,
return { language: language.data.attributes.code,
name: title, };
language: language.data.attributes.code, }),
};
}), }),
}), },
}, user: req.user,
user: req.user, });
});
type StrapiWeaponTypes = { type StrapiWeaponTypes = {
slug: string; slug: string;
translations: { name?: string; language: StrapiLanguage }[]; translations: { name?: string; language: StrapiLanguage }[];
}; };
const { count: weaponsCount, errors: weaponsErrors } = await importStrapiEntries< const { count: weaponsCount, errors: weaponsErrors } =
Key, await importStrapiEntries<StrapiWeaponTypes>({
StrapiWeaponTypes strapi: {
>({ collection: "weapon-story-types",
strapi: { params: { populate: { translations: { populate: "language" } } },
collection: "weapon-story-types", },
params: { populate: { translations: { populate: "language" } } }, payload: {
}, collection: Collections.Keys,
payload: { convert: ({ slug, translations }) => ({
collection: Collections.Keys, name: slug,
convert: ({ slug, translations }) => ({ type: "Weapons",
name: slug, translations: translations.map(({ name, language }) => {
type: "Weapons", if (isUndefined(language.data))
translations: translations.map(({ name, language }) => { throw new Error("A language is required for a Keys title translation");
if (isUndefined(language.data)) if (isUndefined(name))
throw new Error("A language is required for a Keys title translation"); throw new Error("A name is required for a Keys title translation");
if (isUndefined(name)) return {
throw new Error("A name is required for a Keys title translation"); name,
return { language: language.data.attributes.code,
name, };
language: language.data.attributes.code, }),
};
}), }),
}), },
}, user: req.user,
user: req.user, });
});
const { count: wordingsCount, errors: wordingsErrors } = await importStrapiWordings<Key, Key>({ const { count: wordingsCount, errors: wordingsErrors } = await importStrapiWordings<Key>({
strapi: { collection: "website-interfaces", params: { populate: "ui_language" } }, strapi: { collection: "website-interfaces", params: { populate: "ui_language" } },
payload: { collection: Collections.Keys, convert: (strapiObject) => strapiObject }, payload: { collection: Collections.Keys, convert: (strapiObject) => strapiObject },
user: req.user, user: req.user,

View File

@ -1,13 +1,12 @@
import { Collections } from "../../../constants"; import { Collections } from "../../../constants";
import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint"; import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint";
import { Language } from "../../../types/collections";
type StrapiLanguage = { type StrapiLanguage = {
name: string; name: string;
code: string; code: string;
}; };
export const importFromStrapi = createStrapiImportEndpoint<Language, StrapiLanguage>({ export const importFromStrapi = createStrapiImportEndpoint<StrapiLanguage>({
strapi: { strapi: {
collection: "languages", collection: "languages",
params: {}, params: {},

View File

@ -170,12 +170,10 @@ export const LibraryItems = buildVersionedCollectionConfig({
fields: [ fields: [
slugField({ slugField({
name: fields.slug, name: fields.slug,
admin: { width: "50%" },
}), }),
imageField({ imageField({
name: fields.thumbnail, name: fields.thumbnail,
relationTo: Collections.LibraryItemsThumbnails, relationTo: Collections.LibraryItemsThumbnails,
admin: { width: "50%" },
}), }),
], ],
}, },
@ -197,7 +195,7 @@ export const LibraryItems = buildVersionedCollectionConfig({
defaultValue: true, defaultValue: true,
admin: { admin: {
description: "Only items that can be sold separetely should be root items.", description: "Only items that can be sold separetely should be root items.",
width: "25%", width: "0%",
}, },
}, },
{ {
@ -208,7 +206,7 @@ export const LibraryItems = buildVersionedCollectionConfig({
admin: { admin: {
description: description:
"A primary item is an official item that focuses primarly on one or more of our Categories.", "A primary item is an official item that focuses primarly on one or more of our Categories.",
width: "25%", width: "0%",
}, },
}, },
{ {
@ -219,7 +217,7 @@ export const LibraryItems = buildVersionedCollectionConfig({
admin: { admin: {
description: description:
"The item is the digital version of another item, or the item is sold only digitally.", "The item is the digital version of another item, or the item is sold only digitally.",
width: "25%", width: "0%",
}, },
}, },
{ {
@ -229,7 +227,7 @@ export const LibraryItems = buildVersionedCollectionConfig({
defaultValue: false, defaultValue: false,
admin: { admin: {
description: "Are the scans available for download?", description: "Are the scans available for download?",
width: "25%", width: "0%",
}, },
}, },
], ],
@ -265,17 +263,17 @@ export const LibraryItems = buildVersionedCollectionConfig({
imageField({ imageField({
name: fields.scansCoverFront, name: fields.scansCoverFront,
relationTo: Collections.LibraryItemsScans, relationTo: Collections.LibraryItemsScans,
admin: { width: "33%" }, admin: { width: "0%" },
}), }),
imageField({ imageField({
name: fields.scansCoverSpine, name: fields.scansCoverSpine,
relationTo: Collections.LibraryItemsScans, relationTo: Collections.LibraryItemsScans,
admin: { width: "33%" }, admin: { width: "0%" },
}), }),
imageField({ imageField({
name: fields.scansCoverBack, name: fields.scansCoverBack,
relationTo: Collections.LibraryItemsScans, relationTo: Collections.LibraryItemsScans,
admin: { width: "33%" }, admin: { width: "0%" },
}), }),
], ],
}, },
@ -285,12 +283,12 @@ export const LibraryItems = buildVersionedCollectionConfig({
imageField({ imageField({
name: fields.scansCoverInsideFront, name: fields.scansCoverInsideFront,
relationTo: Collections.LibraryItemsScans, relationTo: Collections.LibraryItemsScans,
admin: { width: "50%" }, admin: { width: "0%" },
}), }),
imageField({ imageField({
name: fields.scansCoverBack, name: fields.scansCoverBack,
relationTo: Collections.LibraryItemsScans, relationTo: Collections.LibraryItemsScans,
admin: { width: "50%" }, admin: { width: "0%" },
}), }),
], ],
}, },
@ -300,22 +298,22 @@ export const LibraryItems = buildVersionedCollectionConfig({
imageField({ imageField({
name: fields.scansCoverFlapFront, name: fields.scansCoverFlapFront,
relationTo: Collections.LibraryItemsScans, relationTo: Collections.LibraryItemsScans,
admin: { width: "25%" }, admin: { width: "0%" },
}), }),
imageField({ imageField({
name: fields.scansCoverFlapBack, name: fields.scansCoverFlapBack,
relationTo: Collections.LibraryItemsScans, relationTo: Collections.LibraryItemsScans,
admin: { width: "25%" }, admin: { width: "0%" },
}), }),
imageField({ imageField({
name: fields.scansCoverInsideFlapFront, name: fields.scansCoverInsideFlapFront,
relationTo: Collections.LibraryItemsScans, relationTo: Collections.LibraryItemsScans,
admin: { width: "25%" }, admin: { width: "0%" },
}), }),
imageField({ imageField({
name: fields.scansCoverInsideFlapBack, name: fields.scansCoverInsideFlapBack,
relationTo: Collections.LibraryItemsScans, relationTo: Collections.LibraryItemsScans,
admin: { width: "25%" }, admin: { width: "0%" },
}), }),
], ],
}, },
@ -337,17 +335,17 @@ export const LibraryItems = buildVersionedCollectionConfig({
imageField({ imageField({
name: fields.scansDustjacketFront, name: fields.scansDustjacketFront,
relationTo: Collections.LibraryItemsScans, relationTo: Collections.LibraryItemsScans,
admin: { width: "33%" }, admin: { width: "0%" },
}), }),
imageField({ imageField({
name: fields.scansDustjacketSpine, name: fields.scansDustjacketSpine,
relationTo: Collections.LibraryItemsScans, relationTo: Collections.LibraryItemsScans,
admin: { width: "33%" }, admin: { width: "0%" },
}), }),
imageField({ imageField({
name: fields.scansDustjacketBack, name: fields.scansDustjacketBack,
relationTo: Collections.LibraryItemsScans, relationTo: Collections.LibraryItemsScans,
admin: { width: "33%" }, admin: { width: "0%" },
}), }),
], ],
}, },
@ -357,17 +355,17 @@ export const LibraryItems = buildVersionedCollectionConfig({
imageField({ imageField({
name: fields.scansDustjacketInsideFront, name: fields.scansDustjacketInsideFront,
relationTo: Collections.LibraryItemsScans, relationTo: Collections.LibraryItemsScans,
admin: { width: "33%" }, admin: { width: "0%" },
}), }),
imageField({ imageField({
name: fields.scansDustjacketInsideSpine, name: fields.scansDustjacketInsideSpine,
relationTo: Collections.LibraryItemsScans, relationTo: Collections.LibraryItemsScans,
admin: { width: "33%" }, admin: { width: "0%" },
}), }),
imageField({ imageField({
name: fields.scansDustjacketInsideBack, name: fields.scansDustjacketInsideBack,
relationTo: Collections.LibraryItemsScans, relationTo: Collections.LibraryItemsScans,
admin: { width: "33%" }, admin: { width: "0%" },
}), }),
], ],
}, },
@ -377,22 +375,22 @@ export const LibraryItems = buildVersionedCollectionConfig({
imageField({ imageField({
name: fields.scansDustjacketFlapFront, name: fields.scansDustjacketFlapFront,
relationTo: Collections.LibraryItemsScans, relationTo: Collections.LibraryItemsScans,
admin: { width: "25%" }, admin: { width: "0%" },
}), }),
imageField({ imageField({
name: fields.scansDustjacketFlapBack, name: fields.scansDustjacketFlapBack,
relationTo: Collections.LibraryItemsScans, relationTo: Collections.LibraryItemsScans,
admin: { width: "25%" }, admin: { width: "0%" },
}), }),
imageField({ imageField({
name: fields.scansDustjacketInsideFlapFront, name: fields.scansDustjacketInsideFlapFront,
relationTo: Collections.LibraryItemsScans, relationTo: Collections.LibraryItemsScans,
admin: { width: "25%" }, admin: { width: "0%" },
}), }),
imageField({ imageField({
name: fields.scansDustjacketInsideFlapBack, name: fields.scansDustjacketInsideFlapBack,
relationTo: Collections.LibraryItemsScans, relationTo: Collections.LibraryItemsScans,
admin: { width: "25%" }, admin: { width: "0%" },
}), }),
], ],
}, },
@ -414,17 +412,17 @@ export const LibraryItems = buildVersionedCollectionConfig({
imageField({ imageField({
name: fields.scansObiFront, name: fields.scansObiFront,
relationTo: Collections.LibraryItemsScans, relationTo: Collections.LibraryItemsScans,
admin: { width: "33%" }, admin: { width: "0%" },
}), }),
imageField({ imageField({
name: fields.scansObiSpine, name: fields.scansObiSpine,
relationTo: Collections.LibraryItemsScans, relationTo: Collections.LibraryItemsScans,
admin: { width: "33%" }, admin: { width: "0%" },
}), }),
imageField({ imageField({
name: fields.scansObiBack, name: fields.scansObiBack,
relationTo: Collections.LibraryItemsScans, relationTo: Collections.LibraryItemsScans,
admin: { width: "33%" }, admin: { width: "0%" },
}), }),
], ],
}, },
@ -434,17 +432,17 @@ export const LibraryItems = buildVersionedCollectionConfig({
imageField({ imageField({
name: fields.scansObiInsideFront, name: fields.scansObiInsideFront,
relationTo: Collections.LibraryItemsScans, relationTo: Collections.LibraryItemsScans,
admin: { width: "33%" }, admin: { width: "0%" },
}), }),
imageField({ imageField({
name: fields.scansObiInsideSpine, name: fields.scansObiInsideSpine,
relationTo: Collections.LibraryItemsScans, relationTo: Collections.LibraryItemsScans,
admin: { width: "33%" }, admin: { width: "0%" },
}), }),
imageField({ imageField({
name: fields.scansObiInsideBack, name: fields.scansObiInsideBack,
relationTo: Collections.LibraryItemsScans, relationTo: Collections.LibraryItemsScans,
admin: { width: "33%" }, admin: { width: "0%" },
}), }),
], ],
}, },
@ -454,22 +452,22 @@ export const LibraryItems = buildVersionedCollectionConfig({
imageField({ imageField({
name: fields.scansObiFlapFront, name: fields.scansObiFlapFront,
relationTo: Collections.LibraryItemsScans, relationTo: Collections.LibraryItemsScans,
admin: { width: "25%" }, admin: { width: "0%" },
}), }),
imageField({ imageField({
name: fields.scansObiFlapBack, name: fields.scansObiFlapBack,
relationTo: Collections.LibraryItemsScans, relationTo: Collections.LibraryItemsScans,
admin: { width: "25%" }, admin: { width: "0%" },
}), }),
imageField({ imageField({
name: fields.scansObiInsideFlapFront, name: fields.scansObiInsideFlapFront,
relationTo: Collections.LibraryItemsScans, relationTo: Collections.LibraryItemsScans,
admin: { width: "25%" }, admin: { width: "0%" },
}), }),
imageField({ imageField({
name: fields.scansObiInsideFlapBack, name: fields.scansObiInsideFlapBack,
relationTo: Collections.LibraryItemsScans, relationTo: Collections.LibraryItemsScans,
admin: { width: "25%" }, admin: { width: "0%" },
}), }),
], ],
}, },
@ -495,13 +493,13 @@ export const LibraryItems = buildVersionedCollectionConfig({
name: fields.scansPagesPage, name: fields.scansPagesPage,
type: "number", type: "number",
required: true, required: true,
admin: { width: "33%" }, admin: { width: "0%" },
}, },
imageField({ imageField({
name: fields.scansPagesImage, name: fields.scansPagesImage,
relationTo: Collections.LibraryItemsScans, relationTo: Collections.LibraryItemsScans,
required: true, required: true,
admin: { width: "66%" }, admin: { width: "0%" },
}), }),
], ],
}, },
@ -518,6 +516,7 @@ export const LibraryItems = buildVersionedCollectionConfig({
{ {
name: fields.textual, name: fields.textual,
type: "group", type: "group",
label: false,
admin: { admin: {
condition: (data: Partial<LibraryItem>) => condition: (data: Partial<LibraryItem>) =>
data.itemType === LibraryItemsTypes.Textual, data.itemType === LibraryItemsTypes.Textual,
@ -530,14 +529,14 @@ export const LibraryItems = buildVersionedCollectionConfig({
name: fields.textualSubtype, name: fields.textualSubtype,
relationTo: KeysTypes.LibraryTextual, relationTo: KeysTypes.LibraryTextual,
hasMany: true, hasMany: true,
admin: { allowCreate: false, width: "50%" }, admin: { allowCreate: false, width: "0%" },
}), }),
{ {
name: fields.textualLanguages, name: fields.textualLanguages,
type: "relationship", type: "relationship",
relationTo: Collections.Languages, relationTo: Collections.Languages,
hasMany: true, hasMany: true,
admin: { allowCreate: false, width: "50%" }, admin: { allowCreate: false, width: "0%" },
}, },
], ],
}, },
@ -548,7 +547,7 @@ export const LibraryItems = buildVersionedCollectionConfig({
name: fields.textualPageCount, name: fields.textualPageCount,
type: "number", type: "number",
min: 1, min: 1,
admin: { width: "33%" }, admin: { width: "0%" },
}, },
{ {
name: fields.textualBindingType, name: fields.textualBindingType,
@ -561,7 +560,7 @@ export const LibraryItems = buildVersionedCollectionConfig({
), ),
admin: { admin: {
layout: "horizontal", layout: "horizontal",
width: "33%", width: "0%",
}, },
}, },
{ {
@ -575,7 +574,7 @@ export const LibraryItems = buildVersionedCollectionConfig({
), ),
admin: { admin: {
layout: "horizontal", layout: "horizontal",
width: "33%", width: "0%",
}, },
}, },
], ],
@ -585,6 +584,7 @@ export const LibraryItems = buildVersionedCollectionConfig({
{ {
name: fields.audio, name: fields.audio,
type: "group", type: "group",
label: false,
admin: { admin: {
condition: (data: Partial<LibraryItem>) => condition: (data: Partial<LibraryItem>) =>
data.itemType === LibraryItemsTypes.Audio, data.itemType === LibraryItemsTypes.Audio,
@ -597,7 +597,7 @@ export const LibraryItems = buildVersionedCollectionConfig({
name: fields.audioSubtype, name: fields.audioSubtype,
relationTo: KeysTypes.LibraryAudio, relationTo: KeysTypes.LibraryAudio,
hasMany: true, hasMany: true,
admin: { allowCreate: false, width: "50%" }, admin: { allowCreate: false, width: "0%" },
}), }),
], ],
}, },
@ -612,13 +612,13 @@ export const LibraryItems = buildVersionedCollectionConfig({
name: fields.audioTracksTitle, name: fields.audioTracksTitle,
type: "text", type: "text",
required: true, required: true,
admin: { width: "50%" }, admin: { width: "0%" },
}, },
fileField({ fileField({
name: fields.audioTracksFile, name: fields.audioTracksFile,
relationTo: FileTypes.LibrarySoundtracks, relationTo: FileTypes.LibrarySoundtracks,
required: true, required: true,
admin: { width: "50%" }, admin: { width: "0%" },
}), }),
], ],
}, },
@ -639,21 +639,21 @@ export const LibraryItems = buildVersionedCollectionConfig({
type: "date", type: "date",
admin: { admin: {
date: { pickerAppearance: "dayOnly", displayFormat: "yyyy-MM-dd" }, date: { pickerAppearance: "dayOnly", displayFormat: "yyyy-MM-dd" },
width: "50%", width: "0%",
}, },
}, },
keysField({ keysField({
name: fields.categories, name: fields.categories,
relationTo: KeysTypes.Categories, relationTo: KeysTypes.Categories,
hasMany: true, hasMany: true,
admin: { allowCreate: false, width: "50%" }, admin: { allowCreate: false, width: "0%" },
}), }),
], ],
}, },
translatedFields({ translatedFields({
name: fields.translations, name: fields.translations,
label: "Descriptions", label: "Descriptions",
admin: { initCollapsed: true }, admin: { initCollapsed: true, useAsTitle: fields.translationsDescription },
fields: [{ name: fields.translationsDescription, type: "textarea", required: true }], fields: [{ name: fields.translationsDescription, type: "textarea", required: true }],
}), }),
optionalGroupField({ optionalGroupField({
@ -667,18 +667,18 @@ export const LibraryItems = buildVersionedCollectionConfig({
name: fields.width, name: fields.width,
type: "number", type: "number",
required: true, required: true,
admin: { step: 1, width: "33%", description: "in mm." }, admin: { step: 1, width: "0%", description: "in mm." },
}, },
{ {
name: fields.height, name: fields.height,
type: "number", type: "number",
required: true, required: true,
admin: { step: 1, width: "33%", description: "in mm." }, admin: { step: 1, width: "0%", description: "in mm." },
}, },
{ {
name: fields.thickness, name: fields.thickness,
type: "number", type: "number",
admin: { step: 1, width: "33%", description: "in mm." }, admin: { step: 1, width: "0%", description: "in mm." },
}, },
], ],
}, },
@ -686,7 +686,7 @@ export const LibraryItems = buildVersionedCollectionConfig({
}), }),
optionalGroupField({ optionalGroupField({
name: fields.price, name: fields.price,
admin: { className: "group-array", width: "50%" }, admin: { className: "group-array", width: "0%" },
fields: [ fields: [
{ {
type: "row", type: "row",
@ -696,14 +696,14 @@ export const LibraryItems = buildVersionedCollectionConfig({
type: "number", type: "number",
required: true, required: true,
min: 0, min: 0,
admin: { width: "50%" }, admin: { width: "0%" },
}, },
{ {
name: fields.priceCurrency, name: fields.priceCurrency,
type: "relationship", type: "relationship",
relationTo: Collections.Currencies, relationTo: Collections.Currencies,
required: true, required: true,
admin: { allowCreate: false, width: "50%" }, admin: { allowCreate: false, width: "0%" },
}, },
], ],
}, },
@ -715,7 +715,7 @@ export const LibraryItems = buildVersionedCollectionConfig({
type: "array", type: "array",
admin: { admin: {
description: "Links to official websites where to get/buy the item.", description: "Links to official websites where to get/buy the item.",
width: "50%", width: "0%",
}, },
fields: [{ name: fields.urlsUrl, type: "text", required: true }], fields: [{ name: fields.urlsUrl, type: "text", required: true }],
}, },

View File

@ -1,5 +1,5 @@
import React from "react"; import React from "react";
import { styled } from "styled-components"; import styled from "styled-components";
import { isDefined } from "../../../utils/asserts"; import { isDefined } from "../../../utils/asserts";
interface Props { interface Props {

View File

@ -60,11 +60,11 @@ export const Posts = buildVersionedCollectionConfig({
{ {
type: "row", type: "row",
fields: [ fields: [
slugField({ name: fields.slug, admin: { width: "50%" } }), slugField({ name: fields.slug, admin: { width: "0%" } }),
imageField({ imageField({
name: fields.thumbnail, name: fields.thumbnail,
relationTo: Collections.PostsThumbnails, relationTo: Collections.PostsThumbnails,
admin: { width: "50%" }, admin: { width: "0%" },
}), }),
], ],
}, },
@ -78,7 +78,7 @@ export const Posts = buildVersionedCollectionConfig({
required: true, required: true,
minRows: 1, minRows: 1,
hasMany: true, hasMany: true,
admin: { width: "35%" }, admin: { width: "0%" },
}, },
{ {
name: fields.categories, name: fields.categories,
@ -86,7 +86,7 @@ export const Posts = buildVersionedCollectionConfig({
relationTo: [Collections.Keys], relationTo: [Collections.Keys],
filterOptions: { type: { equals: KeysTypes.Categories } }, filterOptions: { type: { equals: KeysTypes.Categories } },
hasMany: true, hasMany: true,
admin: { allowCreate: false, width: "35%" }, admin: { allowCreate: false, width: "0%" },
}, },
], ],
}, },
@ -125,7 +125,7 @@ export const Posts = buildVersionedCollectionConfig({
} }
return siblingData.language !== siblingData.sourceLanguage; return siblingData.language !== siblingData.sourceLanguage;
}, },
width: "50%", width: "0%",
}, },
validate: (translators, { siblingData }) => { validate: (translators, { siblingData }) => {
if (isUndefined(siblingData.language) || isUndefined(siblingData.sourceLanguage)) { if (isUndefined(siblingData.language) || isUndefined(siblingData.sourceLanguage)) {
@ -145,11 +145,11 @@ export const Posts = buildVersionedCollectionConfig({
type: "relationship", type: "relationship",
relationTo: Collections.Recorders, relationTo: Collections.Recorders,
hasMany: true, hasMany: true,
admin: { width: "50%" }, admin: { width: "0%" },
}, },
], ],
}, },
{ name: fields.content, type: "richText", admin: { hideGutter: true } }, { name: fields.content, type: "richText" },
], ],
}), }),
{ {

View File

@ -84,12 +84,12 @@ export const Recorders = buildCollectionConfig({
type: "text", type: "text",
unique: true, unique: true,
required: true, required: true,
admin: { description: "The username must be unique", width: "33%" }, admin: { description: "The username must be unique", width: "0%" },
}, },
imageField({ imageField({
name: fields.avatar, name: fields.avatar,
relationTo: Collections.RecordersThumbnails, relationTo: Collections.RecordersThumbnails,
admin: { width: "66%" }, admin: { width: "0%" },
}), }),
], ],
}, },

View File

@ -2,7 +2,6 @@ import payload from "payload";
import { Collections } from "../../../constants"; import { Collections } from "../../../constants";
import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint"; import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint";
import { Recorder } from "../../../types/collections"; import { Recorder } from "../../../types/collections";
import { PayloadCreateData } from "../../../types/payload";
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";
@ -16,7 +15,7 @@ type StrapiRecorder = {
bio: { language: StrapiLanguage; bio?: string }[]; bio: { language: StrapiLanguage; bio?: string }[];
}; };
export const importFromStrapi = createStrapiImportEndpoint<Recorder, StrapiRecorder>({ export const importFromStrapi = createStrapiImportEndpoint<StrapiRecorder>({
strapi: { strapi: {
collection: "recorders", collection: "recorders",
params: { params: {
@ -31,22 +30,6 @@ export const importFromStrapi = createStrapiImportEndpoint<Recorder, StrapiRecor
image: avatar, image: avatar,
}); });
const data: Omit<PayloadCreateData<Recorder>, "password" | "email"> = {
username,
anonymize,
languages: languages.data?.map((language) => language.attributes.code),
avatar: avatarId,
biographies: bios?.map(({ language, bio }) => {
if (isUndefined(language.data))
throw new Error("A language is required for a Recorder biography");
if (isUndefined(bio)) throw new Error("A bio is required for a Recorder biography");
return {
language: language.data.attributes.code,
biography: bio,
};
}),
};
const recorder = ( const recorder = (
await payload.find({ await payload.find({
collection: Collections.Recorders, collection: Collections.Recorders,
@ -55,12 +38,44 @@ export const importFromStrapi = createStrapiImportEndpoint<Recorder, StrapiRecor
).docs[0] as Recorder | undefined; ).docs[0] as Recorder | undefined;
if (isDefined(recorder)) { if (isDefined(recorder)) {
await payload.update({ collection: Collections.Recorders, id: recorder.id, data, user }); await payload.update({
collection: Collections.Recorders,
id: recorder.id,
data: {
username,
anonymize,
languages: languages.data?.map((language) => language.attributes.code),
avatar: avatarId,
biographies: bios?.map(({ language, bio }) => {
if (isUndefined(language.data))
throw new Error("A language is required for a Recorder biography");
if (isUndefined(bio)) throw new Error("A bio is required for a Recorder biography");
return {
language: language.data.attributes.code,
biography: bio,
};
}),
},
user,
});
} else { } else {
await payload.create({ await payload.create({
collection: Collections.Recorders, collection: Collections.Recorders,
data: { data: {
...data, username,
anonymize,
languages: languages.data?.map((language) => language.attributes.code),
avatar: avatarId,
biographies: bios?.map(({ language, bio }) => {
if (isUndefined(language.data))
throw new Error("A language is required for a Recorder biography");
if (isUndefined(bio)) throw new Error("A bio is required for a Recorder biography");
return {
language: language.data.attributes.code,
biography: 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

@ -50,7 +50,7 @@ export const Videos: CollectionConfig = buildCollectionConfig({
{ {
type: "row", type: "row",
fields: [ fields: [
{ name: fields.uid, type: "text", required: true, unique: true, admin: { width: "33%" } }, { name: fields.uid, type: "text", required: true, unique: true, admin: { width: "0%" } },
{ {
name: fields.gone, name: fields.gone,
type: "checkbox", type: "checkbox",
@ -59,7 +59,7 @@ export const Videos: CollectionConfig = buildCollectionConfig({
admin: { admin: {
description: description:
"Is the video no longer available (deleted, privatized, unlisted, blocked...)", "Is the video no longer available (deleted, privatized, unlisted, blocked...)",
width: "33%", width: "0%",
}, },
}, },
{ {
@ -67,7 +67,7 @@ export const Videos: CollectionConfig = buildCollectionConfig({
type: "select", type: "select",
required: true, required: true,
options: Object.entries(VideoSources).map(([value, label]) => ({ label, value })), options: Object.entries(VideoSources).map(([value, label]) => ({ label, value })),
admin: { width: "33%" }, admin: { width: "0%" },
}, },
], ],
}, },
@ -77,8 +77,8 @@ export const Videos: CollectionConfig = buildCollectionConfig({
{ {
type: "row", type: "row",
fields: [ fields: [
{ name: fields.likes, type: "number", admin: { width: "50%" } }, { name: fields.likes, type: "number", admin: { width: "0%" } },
{ name: fields.views, type: "number", admin: { width: "50%" } }, { name: fields.views, type: "number", admin: { width: "0%" } },
], ],
}, },
{ {

View File

@ -1,8 +1,6 @@
import payload from "payload"; import payload from "payload";
import { Collections, VideoSources } from "../../../constants"; import { Collections, VideoSources } from "../../../constants";
import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint"; import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint";
import { Video, VideosChannel } from "../../../types/collections";
import { PayloadCreateData } from "../../../types/payload";
import { isDefined, isUndefined } from "../../../utils/asserts"; import { isDefined, isUndefined } from "../../../utils/asserts";
type StapiVideo = { type StapiVideo = {
@ -21,7 +19,7 @@ type StapiVideo = {
channel: { data?: { attributes: { uid: string; title: string; subscribers: number } } }; channel: { data?: { attributes: { uid: string; title: string; subscribers: number } } };
}; };
export const importFromStrapi = createStrapiImportEndpoint<Video, StapiVideo>({ export const importFromStrapi = createStrapiImportEndpoint<StapiVideo>({
strapi: { strapi: {
collection: "videos", collection: "videos",
params: { populate: "published_date,channel" }, params: { populate: "published_date,channel" },
@ -49,14 +47,13 @@ export const importFromStrapi = createStrapiImportEndpoint<Video, StapiVideo>({
let videoChannelId; let videoChannelId;
if (isDefined(channel.data)) { if (isDefined(channel.data)) {
try { try {
const videoChannel: PayloadCreateData<VideosChannel> = {
uid: channel.data.attributes.uid,
title: channel.data.attributes.title,
subscribers: channel.data.attributes.subscribers,
};
await payload.create({ await payload.create({
collection: Collections.VideosChannels, collection: Collections.VideosChannels,
data: videoChannel, data: {
uid: channel.data.attributes.uid,
title: channel.data.attributes.title,
subscribers: channel.data.attributes.subscribers,
},
user, user,
}); });
} catch (e) {} } catch (e) {}
@ -66,26 +63,24 @@ export const importFromStrapi = createStrapiImportEndpoint<Video, StapiVideo>({
where: { uid: { equals: channel.data.attributes.uid } }, where: { uid: { equals: channel.data.attributes.uid } },
}); });
if (result.docs.length > 0) { if (result.docs[0]) {
videoChannelId = result.docs[0].id; videoChannelId = result.docs[0].id;
} }
} }
const video: PayloadCreateData<Video> = {
uid,
title,
description,
views,
likes,
gone,
source,
publishedDate: `${year}-${month}-${day}`,
channel: videoChannelId,
};
await payload.create({ await payload.create({
collection: Collections.Videos, collection: Collections.Videos,
data: video, data: {
uid,
title,
description,
views,
likes,
gone,
source,
publishedDate: `${year}-${month}-${day}`,
channel: videoChannelId,
},
user, user,
}); });
}, },

View File

@ -1,6 +1,5 @@
import { RowLabelArgs } from "payload/dist/admin/components/forms/RowLabel/types"; import { RowLabelArgs } from "payload/dist/admin/components/forms/RowLabel/types";
import { CollectionGroups, Collections, KeysTypes } from "../../constants"; import { CollectionGroups, Collections, KeysTypes } from "../../constants";
import { createGetSlugsEndpoint } from "../../endpoints/createGetSlugsEndpoint";
import { imageField } from "../../fields/imageField/imageField"; 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";
@ -43,20 +42,16 @@ export const Weapons = buildVersionedCollectionConfig({
], ],
group: CollectionGroups.Collections, group: CollectionGroups.Collections,
}, },
endpoints: [ endpoints: [importFromStrapi, getBySlugEndpoint],
importFromStrapi,
createGetSlugsEndpoint(Collections.Weapons),
getBySlugEndpoint,
],
fields: [ fields: [
{ {
type: "row", type: "row",
fields: [ fields: [
slugField({ name: fields.slug, admin: { width: "50%" } }), slugField({ name: fields.slug, admin: { width: "0%" } }),
imageField({ imageField({
name: fields.thumbnail, name: fields.thumbnail,
relationTo: Collections.WeaponsThumbnails, relationTo: Collections.WeaponsThumbnails,
admin: { width: "50%" }, admin: { width: "0%" },
}), }),
], ],
}, },
@ -67,13 +62,13 @@ export const Weapons = buildVersionedCollectionConfig({
name: fields.type, name: fields.type,
relationTo: KeysTypes.Weapons, relationTo: KeysTypes.Weapons,
required: true, required: true,
admin: { allowCreate: false, width: "50%" }, admin: { allowCreate: false, width: "0%" },
}), }),
{ {
name: fields.group, name: fields.group,
type: "relationship", type: "relationship",
relationTo: Collections.WeaponsGroups, relationTo: Collections.WeaponsGroups,
admin: { width: "50%" }, admin: { width: "0%" },
}, },
], ],
}, },
@ -114,12 +109,12 @@ export const Weapons = buildVersionedCollectionConfig({
name: fields.appearancesTranslationsName, name: fields.appearancesTranslationsName,
type: "text", type: "text",
required: true, required: true,
admin: { width: "50%" }, admin: { width: "0%" },
}, },
{ {
name: fields.appearancesTranslationsDescription, name: fields.appearancesTranslationsDescription,
type: "textarea", type: "textarea",
admin: { width: "50%" }, admin: { width: "0%" },
}, },
], ],
}, },
@ -130,13 +125,13 @@ export const Weapons = buildVersionedCollectionConfig({
name: fields.appearancesTranslationsLevel1, name: fields.appearancesTranslationsLevel1,
label: "Level 1", label: "Level 1",
type: "textarea", type: "textarea",
admin: { width: "50%" }, admin: { width: "0%" },
}, },
{ {
name: fields.appearancesTranslationsLevel2, name: fields.appearancesTranslationsLevel2,
label: "Level 2", label: "Level 2",
type: "textarea", type: "textarea",
admin: { width: "50%" }, admin: { width: "0%" },
}, },
], ],
}, },
@ -147,13 +142,13 @@ export const Weapons = buildVersionedCollectionConfig({
name: fields.appearancesTranslationsLevel3, name: fields.appearancesTranslationsLevel3,
label: "Level 3", label: "Level 3",
type: "textarea", type: "textarea",
admin: { width: "50%" }, admin: { width: "0%" },
}, },
{ {
name: fields.appearancesTranslationsLevel4, name: fields.appearancesTranslationsLevel4,
label: "Level 4", label: "Level 4",
type: "textarea", type: "textarea",
admin: { width: "50%" }, admin: { width: "0%" },
}, },
], ],
}, },

View File

@ -1,5 +1,5 @@
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { styled } from "styled-components"; import styled from "styled-components";
import { Collections } from "../../../constants"; import { Collections } from "../../../constants";
interface Props { interface Props {

View File

@ -1,8 +1,6 @@
import payload from "payload"; import payload from "payload";
import { Collections } from "../../../constants"; import { Collections } from "../../../constants";
import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint"; import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint";
import { Weapon, WeaponsGroup } from "../../../types/collections";
import { PayloadCreateData } from "../../../types/payload";
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 { findCategory, findWeaponType, uploadStrapiImage } from "../../../utils/localApi"; import { findCategory, findWeaponType, uploadStrapiImage } from "../../../utils/localApi";
@ -26,7 +24,7 @@ type StrapiWeapon = {
}[]; }[];
}; };
export const importFromStrapi = createStrapiImportEndpoint<Weapon, StrapiWeapon>({ export const importFromStrapi = createStrapiImportEndpoint<StrapiWeapon>({
strapi: { strapi: {
collection: "weapon-stories", collection: "weapon-stories",
params: { params: {
@ -46,12 +44,11 @@ export const importFromStrapi = createStrapiImportEndpoint<Weapon, StrapiWeapon>
let groupId: string | undefined; let groupId: string | undefined;
if (isDefined(weapon_group.data)) { if (isDefined(weapon_group.data)) {
try { try {
const groupData: PayloadCreateData<WeaponsGroup> = {
slug: weapon_group.data.attributes.slug,
};
await payload.create({ await payload.create({
collection: Collections.WeaponsGroups, collection: Collections.WeaponsGroups,
data: groupData, data: {
slug: weapon_group.data.attributes.slug,
},
user, user,
}); });
} catch (e) {} } catch (e) {}
@ -61,7 +58,7 @@ export const importFromStrapi = createStrapiImportEndpoint<Weapon, StrapiWeapon>
where: { slug: { equals: weapon_group.data.attributes.slug } }, where: { slug: { equals: weapon_group.data.attributes.slug } },
}); });
if (result.docs.length > 0) { if (result.docs[0]) {
groupId = result.docs[0].id; groupId = result.docs[0].id;
} }
} }
@ -73,43 +70,46 @@ export const importFromStrapi = createStrapiImportEndpoint<Weapon, StrapiWeapon>
if (isUndefined(type.data)) throw new Error("A type is required to create a Weapon"); if (isUndefined(type.data)) throw new Error("A type is required to create a Weapon");
const data: PayloadCreateData<Weapon> = { await payload.create({
slug, collection: Collections.Weapons,
type: await findWeaponType(type.data.attributes.slug), data: {
group: groupId, updatedBy: user.id,
thumbnail: thumbnailId, slug,
appearances: await Promise.all( type: await findWeaponType(type.data.attributes.slug),
stories.map(async ({ categories, translations }) => ({ group: groupId,
categories: await Promise.all( thumbnail: thumbnailId,
categories.data.map(({ attributes }) => findCategory(attributes.slug)) appearances: await Promise.all(
), stories.map(async ({ categories, translations }) => ({
translations: translations.map( categories: await Promise.all(
({ language, description, level_1, level_2, level_3, level_4 }) => { categories.data.map(({ attributes }) => findCategory(attributes.slug))
if (isUndefined(language.data)) ),
throw new Error("A language is required to create a Weapon translation"); translations: translations.map(
const name = names.find( ({ language, description, level_1, level_2, level_3, level_4 }) => {
(name) => name.language.data?.attributes.code === language.data?.attributes.code if (isUndefined(language.data))
)?.name; throw new Error("A language is required to create a Weapon translation");
if (isUndefined(name)) const name = names.find(
throw new Error("A name is required to create a Weapon translation"); (name) => name.language.data?.attributes.code === language.data?.attributes.code
return { )?.name;
language: language.data?.attributes.code, if (isUndefined(name))
sourceLanguage: language.data?.attributes.code, throw new Error("A name is required to create a Weapon translation");
name, return {
description, language: language.data?.attributes.code,
level1: level_1, sourceLanguage: language.data?.attributes.code,
level2: level_2, name,
level3: level_3, description,
level4: level_4, level1: level_1,
transcribers: [user.id], level2: level_2,
}; level3: level_3,
} level4: level_4,
), transcribers: [user.id],
})) };
), }
}; ),
}))
await payload.create({ collection: Collections.Weapons, data, user }); ),
},
user,
});
}, },
}, },
}); });

View File

@ -1,8 +1,8 @@
import { styled } from "styled-components"; import styled from "styled-components";
export const Icon = styled.div` export const Icon = styled.div`
width: 46px; width: 18px;
height: 46px; height: 18px;
mask: url("/public/accords.svg"); mask: url("/public/accords.svg");
background-color: var(--theme-elevation-1000); background-color: var(--theme-elevation-1000);
mask-size: contain; mask-size: contain;

View File

@ -1,6 +1,6 @@
import "@fontsource/vollkorn/700.css"; import "@fontsource/vollkorn/700.css";
import React from "react"; import React from "react";
import { styled } from "styled-components"; import styled from "styled-components";
export const Logo = (): JSX.Element => ( export const Logo = (): JSX.Element => (
<Container> <Container>

View File

@ -1,8 +1,8 @@
import React from "react";
import { styled } from "styled-components";
import { Link } from "react-router-dom";
import QueryString from "qs";
import { Options } from "payload/dist/collections/operations/local/find"; import { Options } from "payload/dist/collections/operations/local/find";
import QueryString from "qs";
import React from "react";
import { Link } from "react-router-dom";
import styled from "styled-components";
import { LanguageCodes } from "../constants"; import { LanguageCodes } from "../constants";
type Props = { type Props = {

View File

@ -1,10 +1,11 @@
import payload from "payload"; import payload, { GeneratedTypes } from "payload";
import { Collections } from "../constants";
import { CollectionEndpoint } from "../types/payload"; import { CollectionEndpoint } from "../types/payload";
export const createGetByEndpoint = <T, R>( export const createGetByEndpoint = <C extends Collections, R>(
collection: string, collection: C,
attribute: string, attribute: string,
handler: (doc: T) => Promise<R> | R = (doc) => doc as unknown as R handler: (doc: GeneratedTypes["collections"][C]) => Promise<R> | R
): CollectionEndpoint => ({ ): CollectionEndpoint => ({
path: `/${attribute}/:${attribute}`, path: `/${attribute}/:${attribute}`,
method: "get", method: "get",
@ -24,7 +25,7 @@ export const createGetByEndpoint = <T, R>(
where: { [attribute]: { equals: req.params[attribute] } }, where: { [attribute]: { equals: req.params[attribute] } },
}); });
if (result.docs.length === 0) { if (!result.docs[0]) {
return res.sendStatus(404); return res.sendStatus(404);
} }

View File

@ -1,39 +0,0 @@
import payload from "payload";
import { mustBeApi } from "../accesses/endpoints/mustBeApi";
import { Collections } from "../constants";
import { CollectionEndpoint } from "../types/payload";
export const createGetSlugsEndpoint = (collection: Collections): CollectionEndpoint => ({
path: "/slugs",
method: "get",
handler: async (req, res) => {
if (!mustBeApi(req)) {
return res.status(403).send({
errors: [
{
message: "You are not allowed to perform this action.",
},
],
});
}
let page = 1;
let totalPage = 1;
const slugs: string[] = [];
while (page <= totalPage) {
const entries = await payload.find({
collection,
page,
user: req.user,
});
entries.docs.forEach(({ slug }: { slug: string }) => slugs.push(slug));
totalPage = entries.totalPages;
page++;
}
res.status(200).json(slugs);
},
});

View File

@ -6,7 +6,7 @@ import { isDefined } from "../utils/asserts";
type Image = { type Image = {
filename: string; filename: string;
id: string | number; id: string;
}; };
export const createImageRegenerationEndpoint = (collection: Collections): CollectionEndpoint => ({ export const createImageRegenerationEndpoint = (collection: Collections): CollectionEndpoint => ({
@ -36,7 +36,7 @@ export const createImageRegenerationEndpoint = (collection: Collections): Collec
}); });
await Promise.all( await Promise.all(
images.docs.map(async (image: Image) => { images.docs.filter(isImage).map(async (image: Image) => {
try { try {
await payload.update({ await payload.update({
collection, collection,
@ -64,3 +64,9 @@ export const createImageRegenerationEndpoint = (collection: Collections): Collec
.json({ message: `${count} entries have been regenerated successfully.`, errors }); .json({ message: `${count} entries have been regenerated successfully.`, errors });
}, },
}); });
const isImage = (item: Object): item is Image => {
if (!("id" in item) || typeof item.id !== "string") return false;
if (!("filename" in item) || typeof item.filename !== "string") return false;
return true;
};

View File

@ -1,16 +1,18 @@
import payload from "payload"; import payload, { GeneratedTypes } from "payload";
import { BasePayload } from "payload/dist/payload";
import QueryString from "qs"; import QueryString from "qs";
import { Collections } from "../constants";
import { Recorder } from "../types/collections"; import { Recorder } from "../types/collections";
import { CollectionEndpoint, PayloadCreateData } from "../types/payload"; import { CollectionEndpoint } from "../types/payload";
import { isDefined } from "../utils/asserts"; import { isDefined } from "../utils/asserts";
export const getAllStrapiEntries = async <T>( export const getAllStrapiEntries = async (
collectionSlug: string, collectionSlug: string,
params: Object params: Object
): Promise<T[]> => { ): Promise<any[]> => {
let page = 1; let page = 1;
let totalPage = 1; let totalPage = 1;
const result: T[] = []; const result: any[] = [];
while (page <= totalPage) { while (page <= totalPage) {
const paramsWithPagination = QueryString.stringify({ const paramsWithPagination = QueryString.stringify({
@ -30,24 +32,27 @@ export const getAllStrapiEntries = async <T>(
return result; return result;
}; };
type Params<T, S> = { type Params<S> = {
strapi: { strapi: {
collection: string; collection: string;
params: any; params: any;
}; };
payload: { payload: {
collection: string; collection: Collections;
import?: (strapiObject: S, user: any) => Promise<void>; import?: (strapiObject: S, user: any) => Promise<void>;
convert?: (strapiObject: S, user: any) => PayloadCreateData<T>; convert?: (
strapiObject: S,
user: any
) => Parameters<BasePayload<GeneratedTypes>["create"]>[0]["data"];
}; };
}; };
export const importStrapiEntries = async <T, S>({ export const importStrapiEntries = async <S>({
strapi: strapiParams, strapi: strapiParams,
payload: payloadParams, payload: payloadParams,
user, user,
}: Params<T, S> & { user: Recorder }) => { }: Params<S> & { user: Recorder }) => {
const entries = await getAllStrapiEntries<any>(strapiParams.collection, strapiParams.params); const entries = await getAllStrapiEntries(strapiParams.collection, strapiParams.params);
const errors: string[] = []; const errors: string[] = [];
@ -77,7 +82,7 @@ export const importStrapiEntries = async <T, S>({
return { count: entries.length, errors }; return { count: entries.length, errors };
}; };
export const createStrapiImportEndpoint = <T, S>(params: Params<T, S>): CollectionEndpoint => ({ export const createStrapiImportEndpoint = <S>(params: Params<S>): CollectionEndpoint => ({
method: "post", method: "post",
path: "/strapi", path: "/strapi",
handler: async (req, res) => { handler: async (req, res) => {

View File

@ -1,11 +1,12 @@
import payload from "payload"; import payload from "payload";
import { FieldBase } from "payload/dist/fields/config/types"; import { FieldBase } from "payload/dist/fields/config/types";
import { RelationshipField, Where } from "payload/types"; import { RelationshipField, Where } from "payload/types";
import { Collections } from "../../constants";
import { isNotEmpty } from "../../utils/asserts"; import { isNotEmpty } from "../../utils/asserts";
type BackPropagationField = FieldBase & { type BackPropagationField = FieldBase & {
where: (data: any) => Where; where: (data: any) => Where;
relationTo: string; relationTo: Collections;
hasMany?: boolean; hasMany?: boolean;
}; };
export const backPropagationField = ({ export const backPropagationField = ({

View File

@ -1,7 +1,7 @@
import { Props } from "payload/components/views/Cell"; import { Props } from "payload/components/views/Cell";
import React, { useEffect, useMemo, useState } from "react"; import React, { useEffect, useMemo, useState } from "react";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { styled } from "styled-components"; import styled from "styled-components";
import { isUndefined } from "../../utils/asserts"; import { isUndefined } from "../../utils/asserts";
const Image = styled.img` const Image = styled.img`

View File

@ -1,8 +1,8 @@
import React from "react"; import React from "react";
import styled from "styled-components";
import { Language } from "../../types/collections";
import { isDefined } from "../../utils/asserts"; import { isDefined } from "../../utils/asserts";
import { formatLanguageCode, shortenEllipsis } from "../../utils/string"; import { formatLanguageCode, shortenEllipsis } from "../../utils/string";
import { Language } from "../../types/collections";
import { styled } from "styled-components";
interface Props { interface Props {
language?: Language | string; language?: Language | string;

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: "50%" }, admin: { allowCreate: false, width: "0%" },
}; };
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: "50%" }, admin: { allowCreate: false, width: "0%" },
}; };
const creditFields: Field = { const creditFields: Field = {
@ -64,7 +64,7 @@ const creditFields: Field = {
}, },
admin: { admin: {
condition: (_, siblingData) => siblingData.language === siblingData.sourceLanguage, condition: (_, siblingData) => siblingData.language === siblingData.sourceLanguage,
width: "50%", width: "0%",
}, },
validate: (count, { siblingData }) => { validate: (count, { siblingData }) => {
if (siblingData[fieldsNames.language] !== siblingData[fieldsNames.sourceLanguage]) { if (siblingData[fieldsNames.language] !== siblingData[fieldsNames.sourceLanguage]) {
@ -95,7 +95,7 @@ const creditFields: Field = {
admin: { admin: {
condition: (_, siblingData) => condition: (_, siblingData) =>
siblingData[fieldsNames.language] !== siblingData[fieldsNames.sourceLanguage], siblingData[fieldsNames.language] !== siblingData[fieldsNames.sourceLanguage],
width: "50%", width: "0%",
}, },
validate: (count, { siblingData }) => { validate: (count, { siblingData }) => {
if (siblingData[fieldsNames.language] === siblingData[fieldsNames.sourceLanguage]) { if (siblingData[fieldsNames.language] === siblingData[fieldsNames.sourceLanguage]) {
@ -114,7 +114,7 @@ const creditFields: Field = {
type: "relationship", type: "relationship",
relationTo: "recorders", relationTo: "recorders",
hasMany: true, hasMany: true,
admin: { width: "50%" }, admin: { width: "0%" },
}, },
], ],
}; };

View File

@ -1,7 +1,11 @@
import { webpackBundler } from "@payloadcms/bundler-webpack";
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";
@ -25,7 +29,6 @@ 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 { payloadGridView } from "./plugins/payload-grid-view";
export default buildConfig({ export default buildConfig({
serverURL: process.env.PAYLOAD_URI, serverURL: process.env.PAYLOAD_URI,
@ -38,7 +41,14 @@ export default buildConfig({
titleSuffix: "- Accords Library", titleSuffix: "- Accords Library",
}, },
css: path.resolve(__dirname, "styles.scss"), css: path.resolve(__dirname, "styles.scss"),
bundler: webpackBundler(),
}, },
editor: lexicalEditor({
features: ({ defaultFeatures }) => [
...defaultFeatures,
BlocksFeature({ blocks: [transcriptBlock] }),
],
}),
collections: [ collections: [
LibraryItems, LibraryItems,
Contents, Contents,
@ -63,6 +73,9 @@ export default buildConfig({
Recorders, Recorders,
Keys, Keys,
], ],
db: mongooseAdapter({
url: process.env.MONGODB_URI ?? "mongodb://mongo:27017/payload",
}),
globals: [], globals: [],
telemetry: false, telemetry: false,
typescript: { typescript: {
@ -71,5 +84,4 @@ export default buildConfig({
graphQL: { graphQL: {
disable: true, disable: true,
}, },
plugins: [payloadGridView],
}); });

View File

@ -1,69 +0,0 @@
.grid {
margin-bottom: 25px;
> .grid__header {
margin-bottom: 15px;
display: flex;
gap: 1.5rem;
}
> .grid__cells {
display: grid;
grid-gap: 1rem;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
> .grid__cells__cell {
display: grid;
grid-template-rows: 1fr;
background-color: var(--theme-elevation-50);
border: 1px solid var(--theme-elevation-100);
position: relative;
> .grid__cells__cell__filename {
position: relative;
aspect-ratio: 1;
.thumbnail {
> img {
position: absolute;
inset: 0;
object-fit: contain;
background-size: 9% 9%;
background-position: center;
background-image: radial-gradient(
circle,
var(--theme-elevation-100) 1px,
var(--theme-elevation-0) 1px
);
}
}
}
> .grid__cells__cell__selector {
position: absolute;
left: 0.75rem;
top: 0.75rem;
}
> .grid__cells__cell__info {
display: grid;
line-height: 1.5;
padding: 1rem;
gap: 0.5rem;
overflow-x: hidden;
> .grid__cells__cell__title {
font-weight: 600;
}
> .grid__cells__cell__others {
display: grid;
line-height: 1.5;
color: var(--theme-elevation-600);
width: 100%;
font-size: 90%;
}
}
}
}
}

View File

@ -1,100 +0,0 @@
import { useTableColumns } from "payload/dist/admin/components/elements/TableColumns";
import React, { Fragment } from "react";
import { Link } from "react-router-dom";
import "payload/dist/admin/components/elements/Table/index.scss";
import { Column } from "payload/dist/admin/components/elements/Table/types";
import Thumbnail from "payload/dist/admin/components/elements/Thumbnail";
import { SanitizedCollectionConfig } from "payload/types";
import "./index.scss";
const baseClass = "grid";
type Props = {
data: any[];
collection: SanitizedCollectionConfig;
};
const fieldNames = {
filename: "filename",
select: "_select",
};
export const Grid: React.FC<Props> = ({ data, collection }) => {
const { columns: columnsFromContext } = useTableColumns();
const fields = columnsFromContext;
const otherFields = fields?.filter(
(col) => col.active && ![fieldNames.filename, fieldNames.select].includes(col.accessor)
);
const filenameField = fields.find((col) => col.accessor === fieldNames.filename);
const selectorField = fields.find((col) => col.accessor === fieldNames.select);
const headerColumns = fields
.sort((a, b) => {
const sortingValue = (value: Column) => {
switch (value.accessor) {
case fieldNames.select:
return 2;
case fieldNames.filename:
return 1;
default:
return 0;
}
};
return sortingValue(b) - sortingValue(a);
})
.filter(({ active, accessor }) => active || accessor === fieldNames.filename);
return (
<div className={baseClass}>
<div className={`${baseClass}__header`}>
{headerColumns.map((col, index) => (
<div key={index} id={`heading-${col.accessor}`}>
{col.components.Heading}
</div>
))}
</div>
<div className={`${baseClass}__cells`}>
{data &&
data.map((gridCell, cellIndex) => (
<div key={cellIndex} className={`${baseClass}__cells__cell`}>
{filenameField && (
<Link
className={`${baseClass}__cells__cell__filename`}
to={`${collection.slug}/${gridCell.id}`}>
<Thumbnail collection={collection} doc={gridCell} />
</Link>
)}
{selectorField && (
<div className={`${baseClass}__cells__cell__selector`}>
{selectorField.components.renderCell(gridCell, gridCell[selectorField.accessor])}
</div>
)}
<div className={`${baseClass}__cells__cell__info`}>
{filenameField && (
<Link
className={`${baseClass}__cells__cell__title`}
to={`${collection.slug}/${gridCell.id}`}>
{String(gridCell[filenameField.accessor])}
</Link>
)}
{otherFields.length > 0 && (
<div className={`${baseClass}__cells__cell__others`}>
{otherFields.map((col, colIndex) => (
<Fragment key={colIndex}>
{col.components.renderCell(gridCell, gridCell[col.accessor])}
</Fragment>
))}
</div>
)}
</div>
</div>
))}
</div>
</div>
);
};
export default Grid;

View File

@ -1,202 +0,0 @@
import { useWindowInfo } from "@faceless-ui/window-info";
import Button from "payload/dist/admin/components/elements/Button";
import DeleteMany from "payload/dist/admin/components/elements/DeleteMany";
import EditMany from "payload/dist/admin/components/elements/EditMany";
import Eyebrow from "payload/dist/admin/components/elements/Eyebrow";
import { Gutter } from "payload/dist/admin/components/elements/Gutter";
import ListSelection from "payload/dist/admin/components/elements/ListSelection";
import Paginator from "payload/dist/admin/components/elements/Paginator";
import PerPage from "payload/dist/admin/components/elements/PerPage";
import Pill from "payload/dist/admin/components/elements/Pill";
import PublishMany from "payload/dist/admin/components/elements/PublishMany";
import { StaggeredShimmers } from "payload/dist/admin/components/elements/ShimmerEffect";
import { Table } from "payload/dist/admin/components/elements/Table";
import UnpublishMany from "payload/dist/admin/components/elements/UnpublishMany";
import ViewDescription from "payload/dist/admin/components/elements/ViewDescription";
import Meta from "payload/dist/admin/components/utilities/Meta";
import { RelationshipProvider } from "payload/dist/admin/components/views/collections/List/RelationshipProvider";
import { SelectionProvider } from "payload/dist/admin/components/views/collections/List/SelectionProvider";
import { Props } from "payload/dist/admin/components/views/collections/List/types";
import formatFilesize from "payload/dist/uploads/formatFilesize";
import { getTranslation } from "payload/dist/utilities/getTranslation";
import React, { Fragment, useState } from "react";
import { useTranslation } from "react-i18next";
import Grid from "../Grid";
import ListControls, { ViewMode } from "../ListControls";
const baseClass = "collection-list";
export type UploadsGridViewOptions = {
list?: boolean;
grid?: boolean;
};
export const DefaultList =
(options: UploadsGridViewOptions) =>
(props: Props): JSX.Element => {
const {
collection,
collection: {
labels: { singular: singularLabel, plural: pluralLabel },
admin: {
description,
components: { BeforeList, BeforeListTable, AfterListTable, AfterList } = {},
} = {},
},
data,
newDocumentURL,
limit,
hasCreatePermission,
disableEyebrow,
modifySearchParams,
handleSortChange,
handleWhereChange,
handlePageChange,
handlePerPageChange,
customHeader,
resetParams,
} = props;
const [viewMode, setViewMode] = useState<ViewMode>(options.grid === true ? "grid" : "list");
const {
breakpoints: { s: smallBreak },
} = useWindowInfo();
const { t, i18n } = useTranslation("general");
let formattedDocs = data.docs || [];
if (collection.upload) {
formattedDocs = formattedDocs?.map((doc) => {
return {
...doc,
filesize: formatFilesize(doc.filesize),
};
});
}
return (
<div className={baseClass}>
{Array.isArray(BeforeList) &&
BeforeList.map((Component, i) => <Component key={i} {...props} />)}
<Meta title={getTranslation(collection.labels.plural, i18n)} />
<SelectionProvider docs={data.docs} totalDocs={data.totalDocs}>
{!disableEyebrow && <Eyebrow />}
<Gutter className={`${baseClass}__wrap`}>
<header className={`${baseClass}__header`}>
{customHeader && customHeader}
{!customHeader && (
<Fragment>
<h1>{getTranslation(pluralLabel, i18n)}</h1>
{hasCreatePermission && (
<Pill
to={newDocumentURL}
aria-label={t("createNewLabel", {
label: getTranslation(singularLabel, i18n),
})}>
{t("createNew")}
</Pill>
)}
{!smallBreak && (
<ListSelection label={getTranslation(collection.labels.plural, i18n)} />
)}
{description && (
<div className={`${baseClass}__sub-header`}>
<ViewDescription description={description} />
</div>
)}
</Fragment>
)}
</header>
<ListControls
collection={collection}
modifySearchQuery={modifySearchParams}
handleSortChange={handleSortChange}
handleWhereChange={handleWhereChange}
handleViewModeChange={(newViewMode) => setViewMode(newViewMode)}
resetParams={resetParams}
viewMode={viewMode}
showViewModeToggle={options.list === true && options.grid === true}
/>
{Array.isArray(BeforeListTable) &&
BeforeListTable.map((Component, i) => <Component key={i} {...props} />)}
{!data.docs && (
<StaggeredShimmers
className={[`${baseClass}__shimmer`, `${baseClass}__shimmer--rows`].join(" ")}
count={6}
/>
)}
{data.docs && data.docs.length > 0 && (
<RelationshipProvider>
{viewMode === "grid" ? (
<Grid data={formattedDocs} collection={collection} />
) : (
<Table data={formattedDocs} />
)}
</RelationshipProvider>
)}
{data.docs && data.docs.length === 0 && (
<div className={`${baseClass}__no-results`}>
<p>{t("noResults", { label: getTranslation(pluralLabel, i18n) })}</p>
{hasCreatePermission && newDocumentURL && (
<Button el="link" to={newDocumentURL}>
{t("createNewLabel", { label: getTranslation(singularLabel, i18n) })}
</Button>
)}
</div>
)}
{Array.isArray(AfterListTable) &&
AfterListTable.map((Component, i) => <Component key={i} {...props} />)}
<div className={`${baseClass}__page-controls`}>
<Paginator
limit={data.limit}
totalPages={data.totalPages}
page={data.page}
hasPrevPage={data.hasPrevPage}
hasNextPage={data.hasNextPage}
prevPage={data.prevPage ?? undefined}
nextPage={data.nextPage ?? undefined}
numberOfNeighbors={1}
disableHistoryChange={modifySearchParams === false}
onChange={handlePageChange}
/>
{data?.totalDocs > 0 && (
<Fragment>
<div className={`${baseClass}__page-info`}>
{data.page ?? 1 * data.limit - (data.limit - 1)}-
{data.totalPages > 1 && data.totalPages !== data.page
? data.limit * (data.page ?? 1)
: data.totalDocs}{" "}
{t("of")} {data.totalDocs}
</div>
<PerPage
limits={collection?.admin?.pagination?.limits}
limit={limit}
modifySearchParams={modifySearchParams}
handleChange={handlePerPageChange}
resetPage={data.totalDocs <= data.pagingCounter}
/>
<div className={`${baseClass}__list-selection`}>
{smallBreak && (
<Fragment>
<ListSelection label={getTranslation(collection.labels.plural, i18n)} />
<div className={`${baseClass}__list-selection-actions`}>
<EditMany collection={collection} resetParams={resetParams} />
<PublishMany collection={collection} resetParams={resetParams} />
<UnpublishMany collection={collection} resetParams={resetParams} />
<DeleteMany collection={collection} resetParams={resetParams} />
</div>
</Fragment>
)}
</div>
</Fragment>
)}
</div>
</Gutter>
</SelectionProvider>
{Array.isArray(AfterList) &&
AfterList.map((Component, i) => <Component key={i} {...props} />)}
</div>
);
};

View File

@ -1,228 +0,0 @@
import { useWindowInfo } from "@faceless-ui/window-info";
import Button from "payload/dist/admin/components/elements/Button";
import ColumnSelector from "payload/dist/admin/components/elements/ColumnSelector";
import DeleteMany from "payload/dist/admin/components/elements/DeleteMany";
import EditMany from "payload/dist/admin/components/elements/EditMany";
import { getTextFieldsToBeSearched } from "payload/dist/admin/components/elements/ListControls/getTextFieldsToBeSearched";
import { Props } from "payload/dist/admin/components/elements/ListControls/types";
import Pill from "payload/dist/admin/components/elements/Pill";
import PublishMany from "payload/dist/admin/components/elements/PublishMany";
import SearchFilter from "payload/dist/admin/components/elements/SearchFilter";
import SortComplex from "payload/dist/admin/components/elements/SortComplex";
import UnpublishMany from "payload/dist/admin/components/elements/UnpublishMany";
import WhereBuilder from "payload/dist/admin/components/elements/WhereBuilder";
import validateWhereQuery from "payload/dist/admin/components/elements/WhereBuilder/validateWhereQuery";
import Chevron from "payload/dist/admin/components/icons/Chevron";
import { useSearchParams } from "payload/dist/admin/components/utilities/SearchParams";
import { SanitizedCollectionConfig } from "payload/dist/collections/config/types";
import { fieldAffectsData } from "payload/dist/fields/config/types";
import flattenFields from "payload/dist/utilities/flattenTopLevelFields";
import { getTranslation } from "payload/dist/utilities/getTranslation";
import React, { useEffect, useState } from "react";
import AnimateHeight from "react-animate-height";
import { useTranslation } from "react-i18next";
const baseClass = "list-controls";
export type ViewMode = "grid" | "list";
const getUseAsTitle = (collection: SanitizedCollectionConfig) => {
const {
admin: { useAsTitle },
fields,
} = collection;
const topLevelFields = flattenFields(fields);
return topLevelFields.find((field) => fieldAffectsData(field) && field.name === useAsTitle);
};
/**
* The ListControls component is used to render the controls (search, filter, where)
* for a collection's list view. You can find those directly above the table which lists
* the collection's documents.
*/
const ListControls: React.FC<
Props & {
viewMode: ViewMode;
handleViewModeChange: (newMode: ViewMode) => void;
showViewModeToggle: boolean;
}
> = (props) => {
const {
collection,
enableColumns = true,
enableSort = false,
handleSortChange,
handleWhereChange,
modifySearchQuery = true,
resetParams = () => undefined,
viewMode,
handleViewModeChange,
collection: {
fields,
admin: { listSearchableFields },
},
showViewModeToggle,
} = props;
const params = useSearchParams();
const shouldInitializeWhereOpened = validateWhereQuery(params?.where);
const [titleField, setTitleField] = useState(getUseAsTitle(collection));
useEffect(() => {
setTitleField(getUseAsTitle(collection));
}, [collection]);
const [textFieldsToBeSearched] = useState(
getTextFieldsToBeSearched(listSearchableFields, fields)
);
const [visibleDrawer, setVisibleDrawer] = useState<"where" | "sort" | "columns" | undefined>(
shouldInitializeWhereOpened ? "where" : undefined
);
const { t, i18n } = useTranslation("general");
const {
breakpoints: { s: smallBreak },
} = useWindowInfo();
return (
<div className={baseClass}>
<div className={`${baseClass}__wrap`}>
<SearchFilter
fieldName={titleField && fieldAffectsData(titleField) ? titleField.name : undefined}
handleChange={handleWhereChange}
modifySearchQuery={modifySearchQuery}
fieldLabel={
titleField && fieldAffectsData(titleField)
? getTranslation(String(titleField.label ?? titleField.name), i18n)
: undefined
}
listSearchableFields={textFieldsToBeSearched}
/>
<div className={`${baseClass}__buttons`}>
<div className={`${baseClass}__buttons-wrap`}>
{!smallBreak && (
<React.Fragment>
<EditMany collection={collection} resetParams={resetParams} />
<PublishMany collection={collection} resetParams={resetParams} />
<UnpublishMany collection={collection} resetParams={resetParams} />
<DeleteMany collection={collection} resetParams={resetParams} />
</React.Fragment>
)}
{enableColumns && (
<Pill
pillStyle="light"
className={`${baseClass}__toggle-columns ${
visibleDrawer === "columns" ? `${baseClass}__buttons-active` : ""
}`}
onClick={() =>
setVisibleDrawer(visibleDrawer !== "columns" ? "columns" : undefined)
}
aria-expanded={visibleDrawer === "columns"}
aria-controls={`${baseClass}-columns`}
icon={<Chevron />}>
{t("columns")}
</Pill>
)}
<Pill
pillStyle="light"
className={`${baseClass}__toggle-where ${
visibleDrawer === "where" ? `${baseClass}__buttons-active` : ""
}`}
onClick={() => setVisibleDrawer(visibleDrawer !== "where" ? "where" : undefined)}
aria-expanded={visibleDrawer === "where"}
aria-controls={`${baseClass}-where`}
icon={<Chevron />}>
{t("filters")}
</Pill>
{enableSort && (
<Button
className={`${baseClass}__toggle-sort`}
buttonStyle={visibleDrawer === "sort" ? undefined : "secondary"}
onClick={() => setVisibleDrawer(visibleDrawer !== "sort" ? "sort" : undefined)}
aria-expanded={visibleDrawer === "sort"}
aria-controls={`${baseClass}-sort`}
icon="chevron"
iconStyle="none">
{t("sort")}
</Button>
)}
{showViewModeToggle && (
<div style={{ marginLeft: 10 }}>
<svg
onClick={() => handleViewModeChange("list")}
style={{
cursor: "pointer",
color:
viewMode === "list"
? "var(--theme-elevation-1000)"
: "var(--theme-elevation-500)",
}}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 -960 960 960"
preserveAspectRatio="none"
height="32"
width="28">
<path
fill="currentColor"
d="M333-242h432.5q12 0 22-10t10-22v-100.5H333V-242ZM162.5-586h145v-132h-113q-12 0-22 10t-10 22v100Zm0 187h145v-161.5h-145V-399Zm32 157h113v-132.5h-145V-274q0 12 10 22t22 10ZM333-399h464.5v-161.5H333V-399Zm0-187h464.5v-100q0-12-10-22t-22-10H333v132ZM194.28-216.5q-24.218 0-40.749-16.531Q137-249.562 137-273.802v-412.396q0-24.24 16.531-40.771Q170.062-743.5 194.28-743.5h571.44q24.218 0 40.749 16.531Q823-710.438 823-686.198v412.396q0 24.24-16.531 40.771Q789.938-216.5 765.72-216.5H194.28Z"
/>
</svg>
<svg
onClick={() => handleViewModeChange("grid")}
style={{
cursor: "pointer",
color:
viewMode === "grid"
? "var(--theme-elevation-1000)"
: "var(--theme-elevation-500)",
}}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 -960 960 960"
height="28"
width="28">
<path
fill="currentColor"
d="M176.5-519v-264.5h265V-519h-265Zm0 342.5v-265h265v265h-265ZM519-519v-264.5h264.5V-519H519Zm0 342.5v-265h264.5v265H519Zm-317-368h214V-758H202v213.5Zm342.5 0H758V-758H544.5v213.5Zm0 342.5H758v-214H544.5v214ZM202-202h214v-214H202v214Zm342.5-342.5Zm0 128.5ZM416-416Zm0-128.5Z"
/>
</svg>
</div>
)}
</div>
</div>
</div>
{enableColumns && (
<AnimateHeight
className={`${baseClass}__columns`}
height={visibleDrawer === "columns" ? "auto" : 0}
id={`${baseClass}-columns`}>
<ColumnSelector collection={collection} />
</AnimateHeight>
)}
<AnimateHeight
className={`${baseClass}__where`}
height={visibleDrawer === "where" ? "auto" : 0}
id={`${baseClass}-where`}>
<WhereBuilder
collection={collection}
modifySearchQuery={modifySearchQuery}
handleChange={handleWhereChange}
/>
</AnimateHeight>
{enableSort && (
<AnimateHeight
className={`${baseClass}__sort`}
height={visibleDrawer === "sort" ? "auto" : 0}
id={`${baseClass}-sort`}>
<SortComplex
modifySearchQuery={modifySearchQuery}
collection={collection}
handleChange={handleSortChange}
/>
</AnimateHeight>
)}
</div>
);
};
export default ListControls;

View File

@ -1,53 +0,0 @@
import { Plugin } from "payload/config";
import { CollectionAdminOptions } from "payload/dist/collections/config/types";
import { CollectionConfig } from "payload/types";
import { DefaultList, UploadsGridViewOptions } from "./components/List";
type Components = Required<CollectionAdminOptions>["components"];
type ViewsComponents = Required<Required<CollectionAdminOptions>["components"]>["views"];
type Options = {
isUploadEnabled: boolean;
gridView: UploadsGridViewOptions;
};
export type CollectionConfigWithGridView = CollectionConfig & {
custom?: { gridView?: UploadsGridViewOptions };
};
export const payloadGridView: Plugin = ({ collections, ...others }) => ({
collections: collections?.map(handleCollection),
...others,
});
const handleCollection = ({
admin,
...others
}: CollectionConfigWithGridView): CollectionConfig => ({
...others,
admin: handleAdmin(admin, {
isUploadEnabled: others.upload !== undefined,
gridView: others.custom?.gridView ?? { grid: true, list: true },
}),
});
const handleAdmin = (
{ components, ...others }: CollectionAdminOptions = {},
options: Options
): CollectionAdminOptions => ({
...others,
components: handleComponents(components, options),
});
const handleComponents = ({ views, ...others }: Components = {}, options: Options): Components => ({
...others,
views: handleViewsComponents(views, options),
});
const handleViewsComponents = (
{ List, ...others }: ViewsComponents = {},
{ isUploadEnabled, gridView }: Options
): ViewsComponents => ({
...others,
List: isUploadEnabled ? DefaultList(gridView) : List,
});

View File

@ -167,8 +167,6 @@ export type PayloadImage = {
}; };
export const payload = { export const payload = {
getSlugsWeapons: async (): Promise<string[]> =>
await (await request(payloadApiUrl(Collections.Weapons, "slugs"))).json(),
getWeapon: async (slug: string): Promise<EndpointWeapon> => getWeapon: async (slug: string): Promise<EndpointWeapon> =>
await (await request(payloadApiUrl(Collections.Weapons, `slug/${slug}`))).json(), await (await request(payloadApiUrl(Collections.Weapons, `slug/${slug}`))).json(),
getEras: async (): Promise<EndpointEra[]> => getEras: async (): Promise<EndpointEra[]> =>

View File

@ -4,8 +4,6 @@ import { readFileSync } from "fs";
import path from "path"; import path from "path";
import payload from "payload"; import payload from "payload";
import { Collections, RecordersRoles } from "./constants"; import { Collections, RecordersRoles } from "./constants";
import { Recorder } from "./types/collections";
import { PayloadCreateData } from "./types/payload";
import { isDefined, isUndefined } from "./utils/asserts"; import { isDefined, isUndefined } from "./utils/asserts";
const app = express(); const app = express();
@ -28,7 +26,6 @@ const start = async () => {
await payload.init({ await payload.init({
secret: process.env.PAYLOAD_SECRET, secret: process.env.PAYLOAD_SECRET,
mongoURL: process.env.MONGODB_URI,
express: app, express: app,
onInit: async () => { onInit: async () => {
payload.logger.info(`Payload Admin URL: ${payload.getAdminURL()}`); payload.logger.info(`Payload Admin URL: ${payload.getAdminURL()}`);
@ -43,16 +40,15 @@ const start = async () => {
if (recorders.docs.length === 0) { if (recorders.docs.length === 0) {
payload.logger.info("Seeding some initial data"); payload.logger.info("Seeding some initial data");
const recorder: PayloadCreateData<Recorder> = {
email: process.env.SEEDING_ADMIN_EMAIL,
password: process.env.SEEDING_ADMIN_PASSWORD,
username: process.env.SEEDING_ADMIN_USERNAME,
role: [RecordersRoles.Admin, RecordersRoles.Api],
anonymize: false,
};
await payload.create({ await payload.create({
collection: Collections.Recorders, collection: Collections.Recorders,
data: recorder, data: {
email: process.env.SEEDING_ADMIN_EMAIL,
password: process.env.SEEDING_ADMIN_PASSWORD,
username: process.env.SEEDING_ADMIN_USERNAME,
role: [RecordersRoles.Admin, RecordersRoles.Api],
anonymize: false,
},
}); });
} }
} }

View File

@ -46,6 +46,16 @@ html[data-theme="light"] {
--color-base-1000: #000000; --color-base-1000: #000000;
} }
.field-type.row {
padding: 1rem 0;
}
.field-type.radio-group {
> ul {
padding: 0.75rem 0;
}
}
.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

@ -25,34 +25,36 @@ export type ContentFoldersTranslation = {
export interface Config { export interface Config {
collections: { collections: {
"library-items": LibraryItem; 'library-items': LibraryItem;
contents: Content; contents: Content;
"contents-folders": ContentsFolder; 'contents-folders': ContentsFolder;
posts: Post; posts: Post;
"chronology-items": ChronologyItem; 'chronology-items': ChronologyItem;
"chronology-eras": ChronologyEra; 'chronology-eras': ChronologyEra;
weapons: Weapon; weapons: Weapon;
"weapons-groups": WeaponsGroup; 'weapons-groups': WeaponsGroup;
"weapons-thumbnails": WeaponsThumbnail; 'weapons-thumbnails': WeaponsThumbnail;
"contents-thumbnails": ContentsThumbnail; 'contents-thumbnails': ContentsThumbnail;
"library-items-thumbnails": LibraryItemThumbnail; 'library-items-thumbnails': LibraryItemThumbnail;
"library-items-scans": LibraryItemScans; 'library-items-scans': LibraryItemScans;
"library-items-gallery": LibraryItemGallery; 'library-items-gallery': LibraryItemGallery;
"recorders-thumbnails": RecordersThumbnail; 'recorders-thumbnails': RecordersThumbnail;
"posts-thumbnails": PostThumbnail; 'posts-thumbnails': PostThumbnail;
files: File; files: File;
videos: Video; videos: Video;
"videos-channels": VideosChannel; 'videos-channels': VideosChannel;
languages: Language; languages: Language;
currencies: Currency; currencies: Currency;
recorders: Recorder; recorders: Recorder;
keys: Key; keys: Key;
'payload-preferences': PayloadPreference;
'payload-migrations': PayloadMigration;
}; };
globals: {}; globals: {};
} }
export interface LibraryItem { export interface LibraryItem {
id: string; id: string;
itemType?: "Textual" | "Audio" | "Video" | "Game" | "Other"; itemType?: 'Textual' | 'Audio' | 'Video' | 'Game' | 'Other';
slug: string; slug: string;
thumbnail?: string | LibraryItemThumbnail; thumbnail?: string | LibraryItemThumbnail;
pretitle?: string; pretitle?: string;
@ -115,8 +117,8 @@ export interface LibraryItem {
subtype?: string[] | Key[]; subtype?: string[] | Key[];
languages?: string[] | Language[]; languages?: string[] | Language[];
pageCount?: number; pageCount?: number;
bindingType?: "Paperback" | "Hardcover"; bindingType?: 'Paperback' | 'Hardcover';
pageOrder?: "LeftToRight" | "RightToLeft"; pageOrder?: 'LeftToRight' | 'RightToLeft';
}; };
audio?: { audio?: {
audioSubtype?: string[] | Key[]; audioSubtype?: string[] | Key[];
@ -160,7 +162,7 @@ export interface LibraryItem {
updatedBy: string | Recorder; updatedBy: string | Recorder;
updatedAt: string; updatedAt: string;
createdAt: string; createdAt: string;
_status?: "draft" | "published"; _status?: 'draft' | 'published';
} }
export interface LibraryItemThumbnail { export interface LibraryItemThumbnail {
id: string; id: string;
@ -278,16 +280,16 @@ export interface Key {
id: string; id: string;
name: string; name: string;
type: type:
| "Contents" | 'Contents'
| "LibraryAudio" | 'LibraryAudio'
| "LibraryVideo" | 'LibraryVideo'
| "LibraryTextual" | 'LibraryTextual'
| "LibraryGroup" | 'LibraryGroup'
| "Library" | 'Library'
| "Weapons" | 'Weapons'
| "GamePlatforms" | 'GamePlatforms'
| "Categories" | 'Categories'
| "Wordings"; | 'Wordings';
translations?: CategoryTranslations; translations?: CategoryTranslations;
} }
export interface Language { export interface Language {
@ -297,7 +299,7 @@ export interface Language {
export interface File { export interface File {
id: string; id: string;
filename: string; filename: string;
type: "LibraryScans" | "LibrarySoundtracks" | "ContentVideo" | "ContentAudio"; type: 'LibraryScans' | 'LibrarySoundtracks' | 'ContentVideo' | 'ContentAudio';
updatedAt: string; updatedAt: string;
createdAt: string; createdAt: string;
} }
@ -317,10 +319,12 @@ export interface Content {
title: string; title: string;
subtitle?: string; subtitle?: string;
summary?: string; summary?: string;
textContent?: {
[k: string]: unknown;
}[];
textTranscribers?: string[] | Recorder[]; textTranscribers?: string[] | Recorder[];
textTranslators?: string[] | Recorder[]; textTranslators?: string[] | Recorder[];
textProofreaders?: string[] | Recorder[]; textProofreaders?: string[] | Recorder[];
textContent?: (TextBlock | Section | Tabs | TranscriptBlock | QuoteBlock)[];
textNotes?: string; textNotes?: string;
video?: string | File; video?: string | File;
videoNotes?: string; videoNotes?: string;
@ -330,7 +334,7 @@ export interface Content {
updatedBy: string | Recorder; updatedBy: string | Recorder;
updatedAt: string; updatedAt: string;
createdAt: string; createdAt: string;
_status?: "draft" | "published"; _status?: 'draft' | 'published';
} }
export interface ContentsThumbnail { export interface ContentsThumbnail {
id: string; id: string;
@ -376,7 +380,7 @@ export interface Recorder {
avatar?: string | RecordersThumbnail; avatar?: string | RecordersThumbnail;
languages?: string[] | Language[]; languages?: string[] | Language[];
biographies?: RecorderBiographies; biographies?: RecorderBiographies;
role?: ("Admin" | "Recorder" | "Api")[]; role?: ('Admin' | 'Recorder' | 'Api')[];
anonymize: boolean; anonymize: boolean;
email: string; email: string;
resetPasswordToken?: string; resetPasswordToken?: string;
@ -417,193 +421,6 @@ export interface RecordersThumbnail {
}; };
}; };
} }
export interface TextBlock {
content: {
[k: string]: unknown;
}[];
id?: string;
blockName?: string;
blockType: "textBlock";
}
export interface Section {
content?: (Section_Section | Section_Tabs | TranscriptBlock | QuoteBlock | TextBlock)[];
id?: string;
blockName?: string;
blockType: "section";
}
export interface Section_Section {
content?: (
| Section_Section_Section
| Section_Section_Tabs
| TranscriptBlock
| QuoteBlock
| TextBlock
)[];
id?: string;
blockName?: string;
blockType: "section";
}
export interface Section_Section_Section {
content?: (
| Section_Section_Section_Section
| Section_Section_Section_Tabs
| TranscriptBlock
| QuoteBlock
| TextBlock
)[];
id?: string;
blockName?: string;
blockType: "section";
}
export interface Section_Section_Section_Section {
content?: (Section_Section_Section_Section_Section | TranscriptBlock | QuoteBlock | TextBlock)[];
id?: string;
blockName?: string;
blockType: "section";
}
export interface Section_Section_Section_Section_Section {
content?: (TranscriptBlock | QuoteBlock | TextBlock)[];
id?: string;
blockName?: string;
blockType: "section";
}
export interface TranscriptBlock {
lines: (LineBlock | CueBlock)[];
id?: string;
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 QuoteBlock {
from: string;
content: {
[k: string]: unknown;
}[];
id?: string;
blockName?: string;
blockType: "quoteBlock";
}
export interface Section_Section_Section_Tabs {
tabs?: Section_Section_Section_Tabs_Tab[];
id?: string;
blockName?: string;
blockType: "tabs";
}
export interface Section_Section_Section_Tabs_Tab {
content?: (Section_Section_Section_Tabs_Tab_Section | TranscriptBlock | QuoteBlock | TextBlock)[];
id?: string;
blockName?: string;
blockType: "tab";
}
export interface Section_Section_Section_Tabs_Tab_Section {
content?: (TranscriptBlock | QuoteBlock | TextBlock)[];
id?: string;
blockName?: string;
blockType: "section";
}
export interface Section_Section_Tabs {
tabs?: Section_Section_Tabs_Tab[];
id?: string;
blockName?: string;
blockType: "tabs";
}
export interface Section_Section_Tabs_Tab {
content?: (Section_Section_Tabs_Tab_Section | TranscriptBlock | QuoteBlock | TextBlock)[];
id?: string;
blockName?: string;
blockType: "tab";
}
export interface Section_Section_Tabs_Tab_Section {
content?: (Section_Section_Tabs_Tab_Section_Section | TranscriptBlock | QuoteBlock | TextBlock)[];
id?: string;
blockName?: string;
blockType: "section";
}
export interface Section_Section_Tabs_Tab_Section_Section {
content?: (TranscriptBlock | QuoteBlock | TextBlock)[];
id?: string;
blockName?: string;
blockType: "section";
}
export interface Section_Tabs {
tabs?: Section_Tabs_Tab[];
id?: string;
blockName?: string;
blockType: "tabs";
}
export interface Section_Tabs_Tab {
content?: (Section_Tabs_Tab_Section | TranscriptBlock | QuoteBlock | TextBlock)[];
id?: string;
blockName?: string;
blockType: "tab";
}
export interface Section_Tabs_Tab_Section {
content?: (Section_Tabs_Tab_Section_Section | TranscriptBlock | QuoteBlock | TextBlock)[];
id?: string;
blockName?: string;
blockType: "section";
}
export interface Section_Tabs_Tab_Section_Section {
content?: (Section_Tabs_Tab_Section_Section_Section | TranscriptBlock | QuoteBlock | TextBlock)[];
id?: string;
blockName?: string;
blockType: "section";
}
export interface Section_Tabs_Tab_Section_Section_Section {
content?: (TranscriptBlock | QuoteBlock | TextBlock)[];
id?: string;
blockName?: string;
blockType: "section";
}
export interface Tabs {
tabs?: Tabs_Tab[];
id?: string;
blockName?: string;
blockType: "tabs";
}
export interface Tabs_Tab {
content?: (Tabs_Tab_Section | TranscriptBlock | QuoteBlock | TextBlock)[];
id?: string;
blockName?: string;
blockType: "tab";
}
export interface Tabs_Tab_Section {
content?: (Tabs_Tab_Section_Section | TranscriptBlock | QuoteBlock | TextBlock)[];
id?: string;
blockName?: string;
blockType: "section";
}
export interface Tabs_Tab_Section_Section {
content?: (Tabs_Tab_Section_Section_Section | TranscriptBlock | QuoteBlock | TextBlock)[];
id?: string;
blockName?: string;
blockType: "section";
}
export interface Tabs_Tab_Section_Section_Section {
content?: (Tabs_Tab_Section_Section_Section_Section | TranscriptBlock | QuoteBlock | TextBlock)[];
id?: string;
blockName?: string;
blockType: "section";
}
export interface Tabs_Tab_Section_Section_Section_Section {
content?: (TranscriptBlock | QuoteBlock | TextBlock)[];
id?: string;
blockName?: string;
blockType: "section";
}
export interface ContentsFolder { export interface ContentsFolder {
id: string; id: string;
slug: string; slug: string;
@ -617,21 +434,21 @@ export interface Post {
thumbnail?: string | PostThumbnail; thumbnail?: string | PostThumbnail;
authors: authors:
| { | {
relationTo: 'recorders';
value: string; value: string;
relationTo: "recorders";
}[] }[]
| { | {
relationTo: 'recorders';
value: Recorder; value: Recorder;
relationTo: "recorders";
}[]; }[];
categories?: categories?:
| { | {
relationTo: 'keys';
value: string; value: string;
relationTo: "keys";
}[] }[]
| { | {
relationTo: 'keys';
value: Key; value: Key;
relationTo: "keys";
}[]; }[];
translations: { translations: {
language: string | Language; language: string | Language;
@ -650,7 +467,7 @@ export interface Post {
updatedBy: string | Recorder; updatedBy: string | Recorder;
updatedAt: string; updatedAt: string;
createdAt: string; createdAt: string;
_status?: "draft" | "published"; _status?: 'draft' | 'published';
} }
export interface PostThumbnail { export interface PostThumbnail {
id: string; id: string;
@ -701,12 +518,12 @@ export interface ChronologyItem {
events: { events: {
source?: source?:
| { | {
relationTo: 'contents';
value: string | Content; value: string | Content;
relationTo: "contents";
} }
| { | {
relationTo: 'library-items';
value: string | LibraryItem; value: string | LibraryItem;
relationTo: "library-items";
}; };
translations: { translations: {
language: string | Language; language: string | Language;
@ -724,7 +541,7 @@ export interface ChronologyItem {
updatedBy: string | Recorder; updatedBy: string | Recorder;
updatedAt: string; updatedAt: string;
createdAt: string; createdAt: string;
_status?: "draft" | "published"; _status?: 'draft' | 'published';
} }
export interface ChronologyEra { export interface ChronologyEra {
id: string; id: string;
@ -768,7 +585,7 @@ export interface Weapon {
updatedBy: string | Recorder; updatedBy: string | Recorder;
updatedAt: string; updatedAt: string;
createdAt: string; createdAt: string;
_status?: "draft" | "published"; _status?: 'draft' | 'published';
} }
export interface WeaponsThumbnail { export interface WeaponsThumbnail {
id: string; id: string;
@ -830,7 +647,7 @@ export interface Video {
id: string; id: string;
uid: string; uid: string;
gone: boolean; gone: boolean;
source: "YouTube" | "NicoNico" | "Tumblr"; source: 'YouTube' | 'NicoNico' | 'Tumblr';
title: string; title: string;
description?: string; description?: string;
likes?: number; likes?: number;
@ -844,3 +661,62 @@ export interface VideosChannel {
title: string; title: string;
subscribers?: number; subscribers?: number;
} }
export interface PayloadPreference {
id: string;
user: {
relationTo: 'recorders';
value: string | Recorder;
};
key?: string;
value?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
updatedAt: string;
createdAt: string;
}
export interface PayloadMigration {
id: string;
name?: string;
batch?: number;
updatedAt: string;
createdAt: string;
}
declare module 'payload' {
export interface GeneratedTypes {
collections: {
'library-items': LibraryItem
'contents': Content
'contents-folders': Contents Folder
'posts': Post
'chronology-items': ChronologyItem
'chronology-eras': Chronology Era
'weapons': Weapon
'weapons-groups': Weapons Group
'weapons-thumbnails': Weapons Thumbnail
'contents-thumbnails': Contents Thumbnail
'library-items-thumbnails': Library Item Thumbnail
'library-items-scans': Library Item Scans
'library-items-gallery': Library Item Gallery
'recorders-thumbnails': Recorders Thumbnail
'posts-thumbnails': Post Thumbnail
'files': File
'videos': Video
'videos-channels': Videos Channel
'languages': Language
'currencies': Currency
'recorders': Recorder
'keys': Key
'payload-preferences': PayloadPreference
'payload-migrations': PayloadMigration
}
}
}

View File

@ -1,10 +1,6 @@
import { CollectionConfig, PayloadRequest } from "payload/types"; import { Endpoint } from "payload/config";
import { PayloadRequest } from "payload/types";
export type PayloadCreateData<T> = Omit< export type CollectionEndpoint = Omit<Endpoint, "root">;
T,
"id" | "updatedAt" | "createdAt" | "sizes" | "updatedBy"
>;
export type CollectionEndpoint = NonNullable<CollectionConfig["endpoints"]>[number];
export type EndpointAccess<U> = (req: PayloadRequest<U>) => boolean; export type EndpointAccess<U> = (req: PayloadRequest<U>) => boolean;

View File

@ -6,10 +6,6 @@ export const isDefined = <T>(value: T | null | undefined): value is T =>
export const isUndefined = <T>(value: T | null | undefined): value is null | undefined => export const isUndefined = <T>(value: T | null | undefined): value is null | undefined =>
!isDefined(value); !isDefined(value);
export const filterDefined = <T>(array: (T | null | undefined)[]): T[] => array.filter(isDefined);
export const isValidDate = (date: Date): boolean => date instanceof Date && !isNaN(date.getDate());
export const isNotEmpty = (value: string | null | undefined): value is string => export const isNotEmpty = (value: string | null | undefined): value is string =>
isDefined(value) && value.trim().length > 0; isDefined(value) && value.trim().length > 0;
@ -17,7 +13,7 @@ export const isEmpty = (value: string | null | undefined): value is string =>
isUndefined(value) || value.trim().length === 0; isUndefined(value) || value.trim().length === 0;
type Span = [number, number]; type Span = [number, number];
export const hasNoIntersection = (a: Span, b: Span): boolean => { const hasNoIntersection = (a: Span, b: Span): boolean => {
const [aStart, aEnd] = a; const [aStart, aEnd] = a;
const [bStart, bEnd] = b; const [bStart, bEnd] = b;
return aEnd < bStart || aStart > bEnd; return aEnd < bStart || aStart > bEnd;

View File

@ -1,8 +1,7 @@
import { CollectionConfig } from "payload/types"; import { CollectionConfig } from "payload/types";
import { Collections } from "../constants"; import { Collections } from "../constants";
import { CollectionConfigWithGridView } from "../plugins/payload-grid-view";
type CollectionConfigWithPlugins = CollectionConfig & CollectionConfigWithGridView; type CollectionConfigWithPlugins = CollectionConfig;
export type BuildCollectionConfig = Omit< export type BuildCollectionConfig = Omit<
CollectionConfigWithPlugins, CollectionConfigWithPlugins,

View File

@ -8,6 +8,7 @@ export const findWeaponType = async (name: string): Promise<string> => {
collection: Collections.Keys, collection: Collections.Keys,
where: { name: { equals: name }, type: { equals: KeysTypes.Weapons } }, where: { name: { equals: name }, type: { equals: KeysTypes.Weapons } },
}); });
if (!key.docs[0]) throw new Error(`Weapon type ${name} wasn't found`);
return key.docs[0].id; return key.docs[0].id;
}; };
@ -16,7 +17,8 @@ export const findCategory = async (name: string): Promise<string> => {
collection: Collections.Keys, collection: Collections.Keys,
where: { name: { equals: name }, type: { equals: KeysTypes.Categories } }, where: { name: { equals: name }, type: { equals: KeysTypes.Categories } },
}); });
return key.docs[0].id; if (!key.docs[0]) throw new Error(`Category ${name} wasn't found`);
return key.docs[0]?.id;
}; };
type UploadStrapiImage = { type UploadStrapiImage = {

View File

@ -1,91 +0,0 @@
import { Block, BlockField } from "payload/types";
import { capitalize } from "./string";
const isDefined = <T>(value: T | null | undefined): value is T =>
value !== null && value !== undefined;
const recursionFieldName = "recursion" as const;
type BlockConfig<T extends string> = {
root: boolean;
block: RecursiveBlock<T> | Block;
};
type RecursiveBlock<T extends string> = Omit<Block, "fields" | "interfaceName"> & {
[recursionFieldName]: Omit<BlockField, "blocks" | "type"> & {
newDepth: (currentDepth: number) => number;
condition: (currentDepth: number, parents: T[]) => boolean;
blocks: T[];
};
fields?: Block["fields"];
};
export type BlocksConfig<T extends string> = Record<T, BlockConfig<T>>;
export const generateBlocks = <T extends string>(blocksConfig: BlocksConfig<T>): Block[] => {
const isRecursiveBlock = (block: RecursiveBlock<T> | Block): block is RecursiveBlock<T> =>
recursionFieldName in block;
const getInterfaceName = (parents: T[], currentBlockName: T): string => {
return [...parents, currentBlockName]
.map((blockName) => blocksConfig[blockName].block.slug)
.map(capitalize)
.join("_");
};
const getCurrentDepth = (parents: T[]): number =>
parents.reduce((acc, blockName) => {
const block = blocksConfig[blockName].block;
if (!isRecursiveBlock(block)) return acc;
return block[recursionFieldName].newDepth(acc);
}, 1);
const generateRecursiveBlocks = (parents: T[], blockName: T): Block | undefined => {
const currentDepth = getCurrentDepth(parents);
const block = blocksConfig[blockName].block;
if (!isRecursiveBlock(block)) return block;
const {
slug,
labels,
fields = [],
recursion: { newDepth, blocks, condition, ...fieldsProps },
} = block;
const generatedBlocks = blocks
.filter((blockName) => {
const block = blocksConfig[blockName].block;
if (!isRecursiveBlock(block)) return true;
return block[recursionFieldName].condition(currentDepth, parents);
})
.map((nextBlockName) => generateRecursiveBlocks([...parents, blockName], nextBlockName))
.filter(isDefined);
// Cut dead branches (branches without leafs)
if (generatedBlocks.length === 0) {
return undefined;
}
return {
slug,
interfaceName: getInterfaceName(parents, blockName),
labels,
fields: [
...fields,
{
...fieldsProps,
type: "blocks",
blocks: generatedBlocks,
},
],
};
};
const rootBlockNames = Object.entries<BlockConfig<T>>(blocksConfig)
.filter(([_, blockConfig]) => blockConfig.root)
.map(([blockName]) => blockName as T);
return rootBlockNames
.map((blockName) => generateRecursiveBlocks([], blockName))
.filter(isDefined);
};

View File

@ -7,7 +7,7 @@ export const shortenEllipsis = (text: string, length: number): string =>
export const formatLanguageCode = (code: string): string => export const formatLanguageCode = (code: string): string =>
tags(code).valid() ? tags(code).language()?.descriptions()[0] ?? code : code; tags(code).valid() ? tags(code).language()?.descriptions()[0] ?? code : code;
export const capitalize = (string: string): string => { const capitalize = (string: string): string => {
const [firstLetter, ...otherLetters] = string; const [firstLetter, ...otherLetters] = string;
if (isUndefined(firstLetter)) return ""; if (isUndefined(firstLetter)) return "";
return [firstLetter.toUpperCase(), ...otherLetters].join(""); return [firstLetter.toUpperCase(), ...otherLetters].join("");
@ -19,6 +19,3 @@ export const formatToCamelCase = (name: string): string =>
.split(/[ \_-]/g) .split(/[ \_-]/g)
.map((part, index) => (index > 0 ? capitalize(part) : part)) .map((part, index) => (index > 0 ? capitalize(part) : part))
.join(""); .join("");
export const formatToKebabCase = (name: string): string =>
name.toLowerCase().replaceAll(/[ \_]/g, "-");

View File

@ -14,7 +14,7 @@ const updatedByField = (): RelationshipField => ({
type: "relationship", type: "relationship",
required: true, required: true,
relationTo: Collections.Recorders, relationTo: Collections.Recorders,
admin: { readOnly: true, position: "sidebar" }, admin: { readOnly: true },
}); });
type BuildVersionedCollectionConfig = Omit<BuildCollectionConfig, "timestamps" | "versions">; type BuildVersionedCollectionConfig = Omit<BuildCollectionConfig, "timestamps" | "versions">;