Moved to 2.0
This commit is contained in:
parent
26b4798761
commit
76b7e4a8a2
|
@ -9,7 +9,7 @@ services:
|
|||
- .:/home/node/app
|
||||
- node_modules:/home/node/app/node_modules
|
||||
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:
|
||||
- mongo
|
||||
environment:
|
||||
|
|
File diff suppressed because it is too large
Load Diff
12
package.json
12
package.json
|
@ -13,21 +13,25 @@
|
|||
"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: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",
|
||||
"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",
|
||||
"clean": "sudo rm -r uploads mongo",
|
||||
"start": "sudo docker compose up"
|
||||
},
|
||||
"dependencies": {
|
||||
"@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",
|
||||
"cross-env": "^7.0.3",
|
||||
"language-tags": "^1.0.9",
|
||||
"luxon": "^3.4.3",
|
||||
"payload": "^1.15.7",
|
||||
"styled-components": "^6.0.8"
|
||||
"payload": "^2.0.5",
|
||||
"styled-components": "^6.0.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/dotenv": "^8.2.0",
|
||||
|
@ -36,10 +40,12 @@
|
|||
"@types/luxon": "^3.3.2",
|
||||
"@types/qs": "^6.9.8",
|
||||
"@types/react-router-dom": "^5.3.3",
|
||||
"@types/styled-components": "^5.1.28",
|
||||
"copyfiles": "^2.4.1",
|
||||
"nodemon": "^3.0.1",
|
||||
"prettier": "^3.0.3",
|
||||
"ts-node": "^10.9.1",
|
||||
"ts-unused-exports": "^10.0.1",
|
||||
"typescript": "^5.2.2"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
|
@ -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);
|
||||
};
|
|
@ -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;
|
||||
};
|
|
@ -50,14 +50,14 @@ export const ChronologyEras: CollectionConfig = buildCollectionConfig({
|
|||
type: "number",
|
||||
min: 0,
|
||||
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,
|
||||
type: "number",
|
||||
min: 0,
|
||||
required: true,
|
||||
admin: { width: "50%", description: "The year the era ended (year included)" },
|
||||
admin: { width: "0%", description: "The year the era ended (year included)" },
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { Collections } from "../../../constants";
|
||||
import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint";
|
||||
import { ChronologyEra } from "../../../types/collections";
|
||||
import { StrapiLanguage } from "../../../types/strapi";
|
||||
import { isUndefined } from "../../../utils/asserts";
|
||||
|
||||
|
@ -11,7 +10,7 @@ type StrapiChronologyEra = {
|
|||
title: { title: string; language: StrapiLanguage; description?: string }[];
|
||||
};
|
||||
|
||||
export const importFromStrapi = createStrapiImportEndpoint<ChronologyEra, StrapiChronologyEra>({
|
||||
export const importFromStrapi = createStrapiImportEndpoint<StrapiChronologyEra>({
|
||||
strapi: {
|
||||
collection: "chronology-eras",
|
||||
params: {
|
||||
|
|
|
@ -76,10 +76,22 @@ export const ChronologyItems: CollectionConfig = buildVersionedCollectionConfig(
|
|||
type: "number",
|
||||
required: true,
|
||||
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%" } },
|
||||
],
|
||||
},
|
||||
],
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { Collections } from "../../../constants";
|
||||
import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint";
|
||||
import { ChronologyItem } from "../../../types/collections";
|
||||
import { StrapiLanguage } from "../../../types/strapi";
|
||||
import { isUndefined } from "../../../utils/asserts";
|
||||
|
||||
|
@ -18,7 +17,7 @@ type StrapiChronologyItem = {
|
|||
}[];
|
||||
};
|
||||
|
||||
export const importFromStrapi = createStrapiImportEndpoint<ChronologyItem, StrapiChronologyItem>({
|
||||
export const importFromStrapi = createStrapiImportEndpoint<StrapiChronologyItem>({
|
||||
strapi: {
|
||||
collection: "chronology-items",
|
||||
params: {
|
||||
|
|
|
@ -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);
|
|
@ -10,11 +10,6 @@ export const lineBlock: Block = {
|
|||
label: false,
|
||||
type: "richText",
|
||||
required: true,
|
||||
admin: {
|
||||
hideGutter: true,
|
||||
elements: [],
|
||||
leaves: ["bold", "italic", "underline", "strikethrough", "code"],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
|
|
@ -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: [],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
|
@ -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"],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
|
@ -9,7 +9,6 @@ import { beforeDuplicatePiping } from "../../hooks/beforeDuplicatePiping";
|
|||
import { beforeDuplicateUnpublish } from "../../hooks/beforeDuplicateUnpublish";
|
||||
import { isDefined } from "../../utils/asserts";
|
||||
import { buildVersionedCollectionConfig } from "../../utils/versionedCollectionConfig";
|
||||
import { contentBlocks } from "./Blocks/blocks";
|
||||
|
||||
const fields = {
|
||||
slug: "slug",
|
||||
|
@ -66,11 +65,11 @@ export const Contents = buildVersionedCollectionConfig({
|
|||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
slugField({ name: fields.slug, admin: { width: "50%" } }),
|
||||
slugField({ name: fields.slug, admin: { width: "0%" } }),
|
||||
imageField({
|
||||
name: fields.thumbnail,
|
||||
relationTo: Collections.ContentsThumbnails,
|
||||
admin: { width: "50%" },
|
||||
admin: { width: "0%" },
|
||||
}),
|
||||
],
|
||||
},
|
||||
|
@ -81,12 +80,12 @@ export const Contents = buildVersionedCollectionConfig({
|
|||
name: fields.categories,
|
||||
relationTo: KeysTypes.Categories,
|
||||
hasMany: true,
|
||||
admin: { allowCreate: false, width: "50%" },
|
||||
admin: { allowCreate: false, width: "0%" },
|
||||
}),
|
||||
keysField({
|
||||
name: fields.type,
|
||||
relationTo: KeysTypes.Contents,
|
||||
admin: { allowCreate: false, width: "50%" },
|
||||
admin: { allowCreate: false, width: "0%" },
|
||||
}),
|
||||
],
|
||||
},
|
||||
|
@ -115,6 +114,7 @@ export const Contents = buildVersionedCollectionConfig({
|
|||
{
|
||||
label: "Text",
|
||||
fields: [
|
||||
{ name: fields.textContent, type: "richText" },
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
|
@ -127,7 +127,7 @@ export const Contents = buildVersionedCollectionConfig({
|
|||
admin: {
|
||||
condition: (_, siblingData) =>
|
||||
siblingData.language === siblingData.sourceLanguage,
|
||||
width: "50%",
|
||||
width: "0%",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -139,7 +139,7 @@ export const Contents = buildVersionedCollectionConfig({
|
|||
admin: {
|
||||
condition: (_, siblingData) =>
|
||||
siblingData.language !== siblingData.sourceLanguage,
|
||||
width: "50%",
|
||||
width: "0%",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -148,18 +148,10 @@ export const Contents = buildVersionedCollectionConfig({
|
|||
type: "relationship",
|
||||
relationTo: Collections.Recorders,
|
||||
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,
|
||||
label: "Notes",
|
||||
|
@ -176,13 +168,13 @@ export const Contents = buildVersionedCollectionConfig({
|
|||
fileField({
|
||||
name: fields.video,
|
||||
relationTo: FileTypes.ContentVideo,
|
||||
admin: { width: "50%" },
|
||||
admin: { width: "0%" },
|
||||
}),
|
||||
{
|
||||
name: fields.videoNotes,
|
||||
label: "Notes",
|
||||
type: "textarea",
|
||||
admin: { width: "50%" },
|
||||
admin: { width: "0%" },
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -197,13 +189,13 @@ export const Contents = buildVersionedCollectionConfig({
|
|||
fileField({
|
||||
name: fields.audio,
|
||||
relationTo: FileTypes.ContentAudio,
|
||||
admin: { width: "50%" },
|
||||
admin: { width: "0%" },
|
||||
}),
|
||||
{
|
||||
name: fields.audioNotes,
|
||||
label: "Notes",
|
||||
type: "textarea",
|
||||
admin: { width: "50%" },
|
||||
admin: { width: "0%" },
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
|
@ -44,14 +44,14 @@ export const ContentsFolders = buildCollectionConfig({
|
|||
name: fields.subfolders,
|
||||
relationTo: Collections.ContentsFolders,
|
||||
hasMany: true,
|
||||
admin: { width: "50%" },
|
||||
admin: { width: "0%" },
|
||||
},
|
||||
{
|
||||
type: "relationship",
|
||||
name: fields.contents,
|
||||
relationTo: Collections.Contents,
|
||||
hasMany: true,
|
||||
admin: { width: "50%" },
|
||||
admin: { width: "0%" },
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
import { Collections } from "../../../constants";
|
||||
import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint";
|
||||
import { Language } from "../../../types/collections";
|
||||
|
||||
type StrapiLanguage = {
|
||||
code: string;
|
||||
name: string;
|
||||
};
|
||||
|
||||
export const importFromStrapi = createStrapiImportEndpoint<Language, StrapiLanguage>({
|
||||
export const importFromStrapi = createStrapiImportEndpoint<StrapiLanguage>({
|
||||
strapi: {
|
||||
collection: "currencies",
|
||||
params: {},
|
||||
|
|
|
@ -102,7 +102,7 @@ export const Keys = buildCollectionConfig({
|
|||
name: fields.translationsName,
|
||||
type: "text",
|
||||
required: true,
|
||||
admin: { width: "50%" },
|
||||
admin: { width: "0%" },
|
||||
},
|
||||
{
|
||||
name: fields.translationsShort,
|
||||
|
@ -110,7 +110,7 @@ export const Keys = buildCollectionConfig({
|
|||
admin: {
|
||||
condition: (data: Partial<Key>) =>
|
||||
isDefined(data.type) && keysTypesWithShort.includes(data.type),
|
||||
width: "50%",
|
||||
width: "0%",
|
||||
},
|
||||
},
|
||||
],
|
||||
|
|
|
@ -5,21 +5,24 @@ import {
|
|||
importStrapiEntries,
|
||||
} from "../../../endpoints/createStrapiImportEndpoint";
|
||||
import { Key } from "../../../types/collections";
|
||||
import { CollectionEndpoint, PayloadCreateData } from "../../../types/payload";
|
||||
import { CollectionEndpoint } from "../../../types/payload";
|
||||
import { StrapiLanguage } from "../../../types/strapi";
|
||||
import { isDefined, isUndefined } from "../../../utils/asserts";
|
||||
import { formatToCamelCase } from "../../../utils/string";
|
||||
|
||||
const importStrapiWordings: typeof importStrapiEntries = async ({
|
||||
payload: payloadParams,
|
||||
strapi: strapiParams,
|
||||
user,
|
||||
}) => {
|
||||
const rawEntries = await getAllStrapiEntries<any>(strapiParams.collection, strapiParams.params);
|
||||
const importStrapiWordings: typeof importStrapiEntries = async ({ strapi: strapiParams, user }) => {
|
||||
const rawEntries = await getAllStrapiEntries(strapiParams.collection, strapiParams.params);
|
||||
|
||||
const { ui_language, createdAt, updatedAt, ...otherKeys } = rawEntries[0].attributes;
|
||||
|
||||
const entries: PayloadCreateData<Key>[] = Object.keys(otherKeys).map((key) => ({
|
||||
const errors: string[] = [];
|
||||
|
||||
await Promise.all(
|
||||
Object.keys(otherKeys).map(async (key) => {
|
||||
try {
|
||||
await payload.create({
|
||||
collection: Collections.Keys,
|
||||
data: {
|
||||
name: formatToCamelCase(key),
|
||||
type: "Wordings",
|
||||
translations: rawEntries
|
||||
|
@ -28,28 +31,19 @@ const importStrapiWordings: typeof importStrapiEntries = async ({
|
|||
name: entry.attributes[key],
|
||||
}))
|
||||
.filter(({ name }) => isDefined(name) && name !== ""),
|
||||
}));
|
||||
|
||||
const errors: string[] = [];
|
||||
|
||||
await Promise.all(
|
||||
entries.map(async (entry) => {
|
||||
try {
|
||||
await payload.create({
|
||||
collection: payloadParams.collection,
|
||||
data: entry,
|
||||
},
|
||||
user,
|
||||
});
|
||||
} catch (e) {
|
||||
console.warn(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 = {
|
||||
|
@ -71,10 +65,8 @@ export const importFromStrapi: CollectionEndpoint = {
|
|||
titles: { title?: string; short?: string; language: StrapiLanguage }[];
|
||||
};
|
||||
|
||||
const { count: categoriesCount, errors: categoriesErrors } = await importStrapiEntries<
|
||||
Key,
|
||||
StrapiCategories
|
||||
>({
|
||||
const { count: categoriesCount, errors: categoriesErrors } =
|
||||
await importStrapiEntries<StrapiCategories>({
|
||||
strapi: {
|
||||
collection: "categories",
|
||||
params: { populate: { titles: { populate: "language" } } },
|
||||
|
@ -105,10 +97,8 @@ export const importFromStrapi: CollectionEndpoint = {
|
|||
titles: { title: string; language: StrapiLanguage }[];
|
||||
};
|
||||
|
||||
const { count: contentTypesCount, errors: contentTypesErrors } = await importStrapiEntries<
|
||||
Key,
|
||||
StrapiContentType
|
||||
>({
|
||||
const { count: contentTypesCount, errors: contentTypesErrors } =
|
||||
await importStrapiEntries<StrapiContentType>({
|
||||
strapi: {
|
||||
collection: "content-types",
|
||||
params: { populate: { titles: { populate: "language" } } },
|
||||
|
@ -136,10 +126,8 @@ export const importFromStrapi: CollectionEndpoint = {
|
|||
titles: { title?: string; short?: string; language: StrapiLanguage }[];
|
||||
};
|
||||
|
||||
const { count: gamePlatformsCount, errors: gamePlatformsErrors } = await importStrapiEntries<
|
||||
Key,
|
||||
StrapiGamePlatform
|
||||
>({
|
||||
const { count: gamePlatformsCount, errors: gamePlatformsErrors } =
|
||||
await importStrapiEntries<StrapiGamePlatform>({
|
||||
strapi: {
|
||||
collection: "game-platforms",
|
||||
params: { populate: { titles: { populate: "language" } } },
|
||||
|
@ -170,10 +158,8 @@ export const importFromStrapi: CollectionEndpoint = {
|
|||
titles: { title: string; language: StrapiLanguage }[];
|
||||
};
|
||||
|
||||
const { count: libraryCount, errors: libraryErrors } = await importStrapiEntries<
|
||||
Key,
|
||||
StrapiMetadataTypes
|
||||
>({
|
||||
const { count: libraryCount, errors: libraryErrors } =
|
||||
await importStrapiEntries<StrapiMetadataTypes>({
|
||||
strapi: {
|
||||
collection: "metadata-types",
|
||||
params: { populate: { titles: { populate: "language" } } },
|
||||
|
@ -201,10 +187,8 @@ export const importFromStrapi: CollectionEndpoint = {
|
|||
titles: { title: string; language: StrapiLanguage }[];
|
||||
};
|
||||
|
||||
const { count: libraryAudioCount, errors: libraryAudioErrors } = await importStrapiEntries<
|
||||
Key,
|
||||
StrapiAudioSubtypes
|
||||
>({
|
||||
const { count: libraryAudioCount, errors: libraryAudioErrors } =
|
||||
await importStrapiEntries<StrapiAudioSubtypes>({
|
||||
strapi: {
|
||||
collection: "audio-subtypes",
|
||||
params: { populate: { titles: { populate: "language" } } },
|
||||
|
@ -232,10 +216,8 @@ export const importFromStrapi: CollectionEndpoint = {
|
|||
titles: { title: string; language: StrapiLanguage }[];
|
||||
};
|
||||
|
||||
const { count: libraryGroupCount, errors: libraryGroupErrors } = await importStrapiEntries<
|
||||
Key,
|
||||
StrapiGroupSubtypes
|
||||
>({
|
||||
const { count: libraryGroupCount, errors: libraryGroupErrors } =
|
||||
await importStrapiEntries<StrapiGroupSubtypes>({
|
||||
strapi: {
|
||||
collection: "group-subtypes",
|
||||
params: { populate: { titles: { populate: "language" } } },
|
||||
|
@ -263,10 +245,8 @@ export const importFromStrapi: CollectionEndpoint = {
|
|||
titles: { title: string; language: StrapiLanguage }[];
|
||||
};
|
||||
|
||||
const { count: libraryTextualCount, errors: libraryTextualErrors } = await importStrapiEntries<
|
||||
Key,
|
||||
StrapiTextualSubtypes
|
||||
>({
|
||||
const { count: libraryTextualCount, errors: libraryTextualErrors } =
|
||||
await importStrapiEntries<StrapiTextualSubtypes>({
|
||||
strapi: {
|
||||
collection: "textual-subtypes",
|
||||
params: { populate: { titles: { populate: "language" } } },
|
||||
|
@ -294,10 +274,8 @@ export const importFromStrapi: CollectionEndpoint = {
|
|||
titles: { title: string; language: StrapiLanguage }[];
|
||||
};
|
||||
|
||||
const { count: libraryVideoCount, errors: libraryVideoErrors } = await importStrapiEntries<
|
||||
Key,
|
||||
StrapiVideoSubtypes
|
||||
>({
|
||||
const { count: libraryVideoCount, errors: libraryVideoErrors } =
|
||||
await importStrapiEntries<StrapiVideoSubtypes>({
|
||||
strapi: {
|
||||
collection: "video-subtypes",
|
||||
params: { populate: { titles: { populate: "language" } } },
|
||||
|
@ -325,10 +303,8 @@ export const importFromStrapi: CollectionEndpoint = {
|
|||
translations: { name?: string; language: StrapiLanguage }[];
|
||||
};
|
||||
|
||||
const { count: weaponsCount, errors: weaponsErrors } = await importStrapiEntries<
|
||||
Key,
|
||||
StrapiWeaponTypes
|
||||
>({
|
||||
const { count: weaponsCount, errors: weaponsErrors } =
|
||||
await importStrapiEntries<StrapiWeaponTypes>({
|
||||
strapi: {
|
||||
collection: "weapon-story-types",
|
||||
params: { populate: { translations: { populate: "language" } } },
|
||||
|
@ -353,7 +329,7 @@ export const importFromStrapi: CollectionEndpoint = {
|
|||
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" } },
|
||||
payload: { collection: Collections.Keys, convert: (strapiObject) => strapiObject },
|
||||
user: req.user,
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
import { Collections } from "../../../constants";
|
||||
import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint";
|
||||
import { Language } from "../../../types/collections";
|
||||
|
||||
type StrapiLanguage = {
|
||||
name: string;
|
||||
code: string;
|
||||
};
|
||||
|
||||
export const importFromStrapi = createStrapiImportEndpoint<Language, StrapiLanguage>({
|
||||
export const importFromStrapi = createStrapiImportEndpoint<StrapiLanguage>({
|
||||
strapi: {
|
||||
collection: "languages",
|
||||
params: {},
|
||||
|
|
|
@ -170,12 +170,10 @@ export const LibraryItems = buildVersionedCollectionConfig({
|
|||
fields: [
|
||||
slugField({
|
||||
name: fields.slug,
|
||||
admin: { width: "50%" },
|
||||
}),
|
||||
imageField({
|
||||
name: fields.thumbnail,
|
||||
relationTo: Collections.LibraryItemsThumbnails,
|
||||
admin: { width: "50%" },
|
||||
}),
|
||||
],
|
||||
},
|
||||
|
@ -197,7 +195,7 @@ export const LibraryItems = buildVersionedCollectionConfig({
|
|||
defaultValue: true,
|
||||
admin: {
|
||||
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: {
|
||||
description:
|
||||
"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: {
|
||||
description:
|
||||
"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,
|
||||
admin: {
|
||||
description: "Are the scans available for download?",
|
||||
width: "25%",
|
||||
width: "0%",
|
||||
},
|
||||
},
|
||||
],
|
||||
|
@ -265,17 +263,17 @@ export const LibraryItems = buildVersionedCollectionConfig({
|
|||
imageField({
|
||||
name: fields.scansCoverFront,
|
||||
relationTo: Collections.LibraryItemsScans,
|
||||
admin: { width: "33%" },
|
||||
admin: { width: "0%" },
|
||||
}),
|
||||
imageField({
|
||||
name: fields.scansCoverSpine,
|
||||
relationTo: Collections.LibraryItemsScans,
|
||||
admin: { width: "33%" },
|
||||
admin: { width: "0%" },
|
||||
}),
|
||||
imageField({
|
||||
name: fields.scansCoverBack,
|
||||
relationTo: Collections.LibraryItemsScans,
|
||||
admin: { width: "33%" },
|
||||
admin: { width: "0%" },
|
||||
}),
|
||||
],
|
||||
},
|
||||
|
@ -285,12 +283,12 @@ export const LibraryItems = buildVersionedCollectionConfig({
|
|||
imageField({
|
||||
name: fields.scansCoverInsideFront,
|
||||
relationTo: Collections.LibraryItemsScans,
|
||||
admin: { width: "50%" },
|
||||
admin: { width: "0%" },
|
||||
}),
|
||||
imageField({
|
||||
name: fields.scansCoverBack,
|
||||
relationTo: Collections.LibraryItemsScans,
|
||||
admin: { width: "50%" },
|
||||
admin: { width: "0%" },
|
||||
}),
|
||||
],
|
||||
},
|
||||
|
@ -300,22 +298,22 @@ export const LibraryItems = buildVersionedCollectionConfig({
|
|||
imageField({
|
||||
name: fields.scansCoverFlapFront,
|
||||
relationTo: Collections.LibraryItemsScans,
|
||||
admin: { width: "25%" },
|
||||
admin: { width: "0%" },
|
||||
}),
|
||||
imageField({
|
||||
name: fields.scansCoverFlapBack,
|
||||
relationTo: Collections.LibraryItemsScans,
|
||||
admin: { width: "25%" },
|
||||
admin: { width: "0%" },
|
||||
}),
|
||||
imageField({
|
||||
name: fields.scansCoverInsideFlapFront,
|
||||
relationTo: Collections.LibraryItemsScans,
|
||||
admin: { width: "25%" },
|
||||
admin: { width: "0%" },
|
||||
}),
|
||||
imageField({
|
||||
name: fields.scansCoverInsideFlapBack,
|
||||
relationTo: Collections.LibraryItemsScans,
|
||||
admin: { width: "25%" },
|
||||
admin: { width: "0%" },
|
||||
}),
|
||||
],
|
||||
},
|
||||
|
@ -337,17 +335,17 @@ export const LibraryItems = buildVersionedCollectionConfig({
|
|||
imageField({
|
||||
name: fields.scansDustjacketFront,
|
||||
relationTo: Collections.LibraryItemsScans,
|
||||
admin: { width: "33%" },
|
||||
admin: { width: "0%" },
|
||||
}),
|
||||
imageField({
|
||||
name: fields.scansDustjacketSpine,
|
||||
relationTo: Collections.LibraryItemsScans,
|
||||
admin: { width: "33%" },
|
||||
admin: { width: "0%" },
|
||||
}),
|
||||
imageField({
|
||||
name: fields.scansDustjacketBack,
|
||||
relationTo: Collections.LibraryItemsScans,
|
||||
admin: { width: "33%" },
|
||||
admin: { width: "0%" },
|
||||
}),
|
||||
],
|
||||
},
|
||||
|
@ -357,17 +355,17 @@ export const LibraryItems = buildVersionedCollectionConfig({
|
|||
imageField({
|
||||
name: fields.scansDustjacketInsideFront,
|
||||
relationTo: Collections.LibraryItemsScans,
|
||||
admin: { width: "33%" },
|
||||
admin: { width: "0%" },
|
||||
}),
|
||||
imageField({
|
||||
name: fields.scansDustjacketInsideSpine,
|
||||
relationTo: Collections.LibraryItemsScans,
|
||||
admin: { width: "33%" },
|
||||
admin: { width: "0%" },
|
||||
}),
|
||||
imageField({
|
||||
name: fields.scansDustjacketInsideBack,
|
||||
relationTo: Collections.LibraryItemsScans,
|
||||
admin: { width: "33%" },
|
||||
admin: { width: "0%" },
|
||||
}),
|
||||
],
|
||||
},
|
||||
|
@ -377,22 +375,22 @@ export const LibraryItems = buildVersionedCollectionConfig({
|
|||
imageField({
|
||||
name: fields.scansDustjacketFlapFront,
|
||||
relationTo: Collections.LibraryItemsScans,
|
||||
admin: { width: "25%" },
|
||||
admin: { width: "0%" },
|
||||
}),
|
||||
imageField({
|
||||
name: fields.scansDustjacketFlapBack,
|
||||
relationTo: Collections.LibraryItemsScans,
|
||||
admin: { width: "25%" },
|
||||
admin: { width: "0%" },
|
||||
}),
|
||||
imageField({
|
||||
name: fields.scansDustjacketInsideFlapFront,
|
||||
relationTo: Collections.LibraryItemsScans,
|
||||
admin: { width: "25%" },
|
||||
admin: { width: "0%" },
|
||||
}),
|
||||
imageField({
|
||||
name: fields.scansDustjacketInsideFlapBack,
|
||||
relationTo: Collections.LibraryItemsScans,
|
||||
admin: { width: "25%" },
|
||||
admin: { width: "0%" },
|
||||
}),
|
||||
],
|
||||
},
|
||||
|
@ -414,17 +412,17 @@ export const LibraryItems = buildVersionedCollectionConfig({
|
|||
imageField({
|
||||
name: fields.scansObiFront,
|
||||
relationTo: Collections.LibraryItemsScans,
|
||||
admin: { width: "33%" },
|
||||
admin: { width: "0%" },
|
||||
}),
|
||||
imageField({
|
||||
name: fields.scansObiSpine,
|
||||
relationTo: Collections.LibraryItemsScans,
|
||||
admin: { width: "33%" },
|
||||
admin: { width: "0%" },
|
||||
}),
|
||||
imageField({
|
||||
name: fields.scansObiBack,
|
||||
relationTo: Collections.LibraryItemsScans,
|
||||
admin: { width: "33%" },
|
||||
admin: { width: "0%" },
|
||||
}),
|
||||
],
|
||||
},
|
||||
|
@ -434,17 +432,17 @@ export const LibraryItems = buildVersionedCollectionConfig({
|
|||
imageField({
|
||||
name: fields.scansObiInsideFront,
|
||||
relationTo: Collections.LibraryItemsScans,
|
||||
admin: { width: "33%" },
|
||||
admin: { width: "0%" },
|
||||
}),
|
||||
imageField({
|
||||
name: fields.scansObiInsideSpine,
|
||||
relationTo: Collections.LibraryItemsScans,
|
||||
admin: { width: "33%" },
|
||||
admin: { width: "0%" },
|
||||
}),
|
||||
imageField({
|
||||
name: fields.scansObiInsideBack,
|
||||
relationTo: Collections.LibraryItemsScans,
|
||||
admin: { width: "33%" },
|
||||
admin: { width: "0%" },
|
||||
}),
|
||||
],
|
||||
},
|
||||
|
@ -454,22 +452,22 @@ export const LibraryItems = buildVersionedCollectionConfig({
|
|||
imageField({
|
||||
name: fields.scansObiFlapFront,
|
||||
relationTo: Collections.LibraryItemsScans,
|
||||
admin: { width: "25%" },
|
||||
admin: { width: "0%" },
|
||||
}),
|
||||
imageField({
|
||||
name: fields.scansObiFlapBack,
|
||||
relationTo: Collections.LibraryItemsScans,
|
||||
admin: { width: "25%" },
|
||||
admin: { width: "0%" },
|
||||
}),
|
||||
imageField({
|
||||
name: fields.scansObiInsideFlapFront,
|
||||
relationTo: Collections.LibraryItemsScans,
|
||||
admin: { width: "25%" },
|
||||
admin: { width: "0%" },
|
||||
}),
|
||||
imageField({
|
||||
name: fields.scansObiInsideFlapBack,
|
||||
relationTo: Collections.LibraryItemsScans,
|
||||
admin: { width: "25%" },
|
||||
admin: { width: "0%" },
|
||||
}),
|
||||
],
|
||||
},
|
||||
|
@ -495,13 +493,13 @@ export const LibraryItems = buildVersionedCollectionConfig({
|
|||
name: fields.scansPagesPage,
|
||||
type: "number",
|
||||
required: true,
|
||||
admin: { width: "33%" },
|
||||
admin: { width: "0%" },
|
||||
},
|
||||
imageField({
|
||||
name: fields.scansPagesImage,
|
||||
relationTo: Collections.LibraryItemsScans,
|
||||
required: true,
|
||||
admin: { width: "66%" },
|
||||
admin: { width: "0%" },
|
||||
}),
|
||||
],
|
||||
},
|
||||
|
@ -518,6 +516,7 @@ export const LibraryItems = buildVersionedCollectionConfig({
|
|||
{
|
||||
name: fields.textual,
|
||||
type: "group",
|
||||
label: false,
|
||||
admin: {
|
||||
condition: (data: Partial<LibraryItem>) =>
|
||||
data.itemType === LibraryItemsTypes.Textual,
|
||||
|
@ -530,14 +529,14 @@ export const LibraryItems = buildVersionedCollectionConfig({
|
|||
name: fields.textualSubtype,
|
||||
relationTo: KeysTypes.LibraryTextual,
|
||||
hasMany: true,
|
||||
admin: { allowCreate: false, width: "50%" },
|
||||
admin: { allowCreate: false, width: "0%" },
|
||||
}),
|
||||
{
|
||||
name: fields.textualLanguages,
|
||||
type: "relationship",
|
||||
relationTo: Collections.Languages,
|
||||
hasMany: true,
|
||||
admin: { allowCreate: false, width: "50%" },
|
||||
admin: { allowCreate: false, width: "0%" },
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -548,7 +547,7 @@ export const LibraryItems = buildVersionedCollectionConfig({
|
|||
name: fields.textualPageCount,
|
||||
type: "number",
|
||||
min: 1,
|
||||
admin: { width: "33%" },
|
||||
admin: { width: "0%" },
|
||||
},
|
||||
{
|
||||
name: fields.textualBindingType,
|
||||
|
@ -561,7 +560,7 @@ export const LibraryItems = buildVersionedCollectionConfig({
|
|||
),
|
||||
admin: {
|
||||
layout: "horizontal",
|
||||
width: "33%",
|
||||
width: "0%",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -575,7 +574,7 @@ export const LibraryItems = buildVersionedCollectionConfig({
|
|||
),
|
||||
admin: {
|
||||
layout: "horizontal",
|
||||
width: "33%",
|
||||
width: "0%",
|
||||
},
|
||||
},
|
||||
],
|
||||
|
@ -585,6 +584,7 @@ export const LibraryItems = buildVersionedCollectionConfig({
|
|||
{
|
||||
name: fields.audio,
|
||||
type: "group",
|
||||
label: false,
|
||||
admin: {
|
||||
condition: (data: Partial<LibraryItem>) =>
|
||||
data.itemType === LibraryItemsTypes.Audio,
|
||||
|
@ -597,7 +597,7 @@ export const LibraryItems = buildVersionedCollectionConfig({
|
|||
name: fields.audioSubtype,
|
||||
relationTo: KeysTypes.LibraryAudio,
|
||||
hasMany: true,
|
||||
admin: { allowCreate: false, width: "50%" },
|
||||
admin: { allowCreate: false, width: "0%" },
|
||||
}),
|
||||
],
|
||||
},
|
||||
|
@ -612,13 +612,13 @@ export const LibraryItems = buildVersionedCollectionConfig({
|
|||
name: fields.audioTracksTitle,
|
||||
type: "text",
|
||||
required: true,
|
||||
admin: { width: "50%" },
|
||||
admin: { width: "0%" },
|
||||
},
|
||||
fileField({
|
||||
name: fields.audioTracksFile,
|
||||
relationTo: FileTypes.LibrarySoundtracks,
|
||||
required: true,
|
||||
admin: { width: "50%" },
|
||||
admin: { width: "0%" },
|
||||
}),
|
||||
],
|
||||
},
|
||||
|
@ -639,21 +639,21 @@ export const LibraryItems = buildVersionedCollectionConfig({
|
|||
type: "date",
|
||||
admin: {
|
||||
date: { pickerAppearance: "dayOnly", displayFormat: "yyyy-MM-dd" },
|
||||
width: "50%",
|
||||
width: "0%",
|
||||
},
|
||||
},
|
||||
keysField({
|
||||
name: fields.categories,
|
||||
relationTo: KeysTypes.Categories,
|
||||
hasMany: true,
|
||||
admin: { allowCreate: false, width: "50%" },
|
||||
admin: { allowCreate: false, width: "0%" },
|
||||
}),
|
||||
],
|
||||
},
|
||||
translatedFields({
|
||||
name: fields.translations,
|
||||
label: "Descriptions",
|
||||
admin: { initCollapsed: true },
|
||||
admin: { initCollapsed: true, useAsTitle: fields.translationsDescription },
|
||||
fields: [{ name: fields.translationsDescription, type: "textarea", required: true }],
|
||||
}),
|
||||
optionalGroupField({
|
||||
|
@ -667,18 +667,18 @@ export const LibraryItems = buildVersionedCollectionConfig({
|
|||
name: fields.width,
|
||||
type: "number",
|
||||
required: true,
|
||||
admin: { step: 1, width: "33%", description: "in mm." },
|
||||
admin: { step: 1, width: "0%", description: "in mm." },
|
||||
},
|
||||
{
|
||||
name: fields.height,
|
||||
type: "number",
|
||||
required: true,
|
||||
admin: { step: 1, width: "33%", description: "in mm." },
|
||||
admin: { step: 1, width: "0%", description: "in mm." },
|
||||
},
|
||||
{
|
||||
name: fields.thickness,
|
||||
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({
|
||||
name: fields.price,
|
||||
admin: { className: "group-array", width: "50%" },
|
||||
admin: { className: "group-array", width: "0%" },
|
||||
fields: [
|
||||
{
|
||||
type: "row",
|
||||
|
@ -696,14 +696,14 @@ export const LibraryItems = buildVersionedCollectionConfig({
|
|||
type: "number",
|
||||
required: true,
|
||||
min: 0,
|
||||
admin: { width: "50%" },
|
||||
admin: { width: "0%" },
|
||||
},
|
||||
{
|
||||
name: fields.priceCurrency,
|
||||
type: "relationship",
|
||||
relationTo: Collections.Currencies,
|
||||
required: true,
|
||||
admin: { allowCreate: false, width: "50%" },
|
||||
admin: { allowCreate: false, width: "0%" },
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -715,7 +715,7 @@ export const LibraryItems = buildVersionedCollectionConfig({
|
|||
type: "array",
|
||||
admin: {
|
||||
description: "Links to official websites where to get/buy the item.",
|
||||
width: "50%",
|
||||
width: "0%",
|
||||
},
|
||||
fields: [{ name: fields.urlsUrl, type: "text", required: true }],
|
||||
},
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from "react";
|
||||
import { styled } from "styled-components";
|
||||
import styled from "styled-components";
|
||||
import { isDefined } from "../../../utils/asserts";
|
||||
|
||||
interface Props {
|
||||
|
|
|
@ -60,11 +60,11 @@ export const Posts = buildVersionedCollectionConfig({
|
|||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
slugField({ name: fields.slug, admin: { width: "50%" } }),
|
||||
slugField({ name: fields.slug, admin: { width: "0%" } }),
|
||||
imageField({
|
||||
name: fields.thumbnail,
|
||||
relationTo: Collections.PostsThumbnails,
|
||||
admin: { width: "50%" },
|
||||
admin: { width: "0%" },
|
||||
}),
|
||||
],
|
||||
},
|
||||
|
@ -78,7 +78,7 @@ export const Posts = buildVersionedCollectionConfig({
|
|||
required: true,
|
||||
minRows: 1,
|
||||
hasMany: true,
|
||||
admin: { width: "35%" },
|
||||
admin: { width: "0%" },
|
||||
},
|
||||
{
|
||||
name: fields.categories,
|
||||
|
@ -86,7 +86,7 @@ export const Posts = buildVersionedCollectionConfig({
|
|||
relationTo: [Collections.Keys],
|
||||
filterOptions: { type: { equals: KeysTypes.Categories } },
|
||||
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;
|
||||
},
|
||||
width: "50%",
|
||||
width: "0%",
|
||||
},
|
||||
validate: (translators, { siblingData }) => {
|
||||
if (isUndefined(siblingData.language) || isUndefined(siblingData.sourceLanguage)) {
|
||||
|
@ -145,11 +145,11 @@ export const Posts = buildVersionedCollectionConfig({
|
|||
type: "relationship",
|
||||
relationTo: Collections.Recorders,
|
||||
hasMany: true,
|
||||
admin: { width: "50%" },
|
||||
admin: { width: "0%" },
|
||||
},
|
||||
],
|
||||
},
|
||||
{ name: fields.content, type: "richText", admin: { hideGutter: true } },
|
||||
{ name: fields.content, type: "richText" },
|
||||
],
|
||||
}),
|
||||
{
|
||||
|
|
|
@ -84,12 +84,12 @@ export const Recorders = buildCollectionConfig({
|
|||
type: "text",
|
||||
unique: true,
|
||||
required: true,
|
||||
admin: { description: "The username must be unique", width: "33%" },
|
||||
admin: { description: "The username must be unique", width: "0%" },
|
||||
},
|
||||
imageField({
|
||||
name: fields.avatar,
|
||||
relationTo: Collections.RecordersThumbnails,
|
||||
admin: { width: "66%" },
|
||||
admin: { width: "0%" },
|
||||
}),
|
||||
],
|
||||
},
|
||||
|
|
|
@ -2,7 +2,6 @@ import payload from "payload";
|
|||
import { Collections } from "../../../constants";
|
||||
import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint";
|
||||
import { Recorder } from "../../../types/collections";
|
||||
import { PayloadCreateData } from "../../../types/payload";
|
||||
import { StrapiImage, StrapiLanguage } from "../../../types/strapi";
|
||||
import { isDefined, isUndefined } from "../../../utils/asserts";
|
||||
import { uploadStrapiImage } from "../../../utils/localApi";
|
||||
|
@ -16,7 +15,7 @@ type StrapiRecorder = {
|
|||
bio: { language: StrapiLanguage; bio?: string }[];
|
||||
};
|
||||
|
||||
export const importFromStrapi = createStrapiImportEndpoint<Recorder, StrapiRecorder>({
|
||||
export const importFromStrapi = createStrapiImportEndpoint<StrapiRecorder>({
|
||||
strapi: {
|
||||
collection: "recorders",
|
||||
params: {
|
||||
|
@ -31,7 +30,18 @@ export const importFromStrapi = createStrapiImportEndpoint<Recorder, StrapiRecor
|
|||
image: avatar,
|
||||
});
|
||||
|
||||
const data: Omit<PayloadCreateData<Recorder>, "password" | "email"> = {
|
||||
const recorder = (
|
||||
await payload.find({
|
||||
collection: Collections.Recorders,
|
||||
where: { username: { equals: username } },
|
||||
})
|
||||
).docs[0] as Recorder | undefined;
|
||||
|
||||
if (isDefined(recorder)) {
|
||||
await payload.update({
|
||||
collection: Collections.Recorders,
|
||||
id: recorder.id,
|
||||
data: {
|
||||
username,
|
||||
anonymize,
|
||||
languages: languages.data?.map((language) => language.attributes.code),
|
||||
|
@ -45,22 +55,27 @@ export const importFromStrapi = createStrapiImportEndpoint<Recorder, StrapiRecor
|
|||
biography: bio,
|
||||
};
|
||||
}),
|
||||
};
|
||||
|
||||
const recorder = (
|
||||
await payload.find({
|
||||
collection: Collections.Recorders,
|
||||
where: { username: { equals: username } },
|
||||
})
|
||||
).docs[0] as Recorder | undefined;
|
||||
|
||||
if (isDefined(recorder)) {
|
||||
await payload.update({ collection: Collections.Recorders, id: recorder.id, data, user });
|
||||
},
|
||||
user,
|
||||
});
|
||||
} else {
|
||||
await payload.create({
|
||||
collection: Collections.Recorders,
|
||||
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`,
|
||||
password: process.env.RECORDER_DEFAULT_PASSWORD,
|
||||
},
|
||||
|
|
|
@ -50,7 +50,7 @@ export const Videos: CollectionConfig = buildCollectionConfig({
|
|||
{
|
||||
type: "row",
|
||||
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,
|
||||
type: "checkbox",
|
||||
|
@ -59,7 +59,7 @@ export const Videos: CollectionConfig = buildCollectionConfig({
|
|||
admin: {
|
||||
description:
|
||||
"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",
|
||||
required: true,
|
||||
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",
|
||||
fields: [
|
||||
{ name: fields.likes, type: "number", admin: { width: "50%" } },
|
||||
{ name: fields.views, type: "number", admin: { width: "50%" } },
|
||||
{ name: fields.likes, type: "number", admin: { width: "0%" } },
|
||||
{ name: fields.views, type: "number", admin: { width: "0%" } },
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
import payload from "payload";
|
||||
import { Collections, VideoSources } from "../../../constants";
|
||||
import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint";
|
||||
import { Video, VideosChannel } from "../../../types/collections";
|
||||
import { PayloadCreateData } from "../../../types/payload";
|
||||
import { isDefined, isUndefined } from "../../../utils/asserts";
|
||||
|
||||
type StapiVideo = {
|
||||
|
@ -21,7 +19,7 @@ type StapiVideo = {
|
|||
channel: { data?: { attributes: { uid: string; title: string; subscribers: number } } };
|
||||
};
|
||||
|
||||
export const importFromStrapi = createStrapiImportEndpoint<Video, StapiVideo>({
|
||||
export const importFromStrapi = createStrapiImportEndpoint<StapiVideo>({
|
||||
strapi: {
|
||||
collection: "videos",
|
||||
params: { populate: "published_date,channel" },
|
||||
|
@ -49,14 +47,13 @@ export const importFromStrapi = createStrapiImportEndpoint<Video, StapiVideo>({
|
|||
let videoChannelId;
|
||||
if (isDefined(channel.data)) {
|
||||
try {
|
||||
const videoChannel: PayloadCreateData<VideosChannel> = {
|
||||
await payload.create({
|
||||
collection: Collections.VideosChannels,
|
||||
data: {
|
||||
uid: channel.data.attributes.uid,
|
||||
title: channel.data.attributes.title,
|
||||
subscribers: channel.data.attributes.subscribers,
|
||||
};
|
||||
await payload.create({
|
||||
collection: Collections.VideosChannels,
|
||||
data: videoChannel,
|
||||
},
|
||||
user,
|
||||
});
|
||||
} catch (e) {}
|
||||
|
@ -66,12 +63,14 @@ export const importFromStrapi = createStrapiImportEndpoint<Video, StapiVideo>({
|
|||
where: { uid: { equals: channel.data.attributes.uid } },
|
||||
});
|
||||
|
||||
if (result.docs.length > 0) {
|
||||
if (result.docs[0]) {
|
||||
videoChannelId = result.docs[0].id;
|
||||
}
|
||||
}
|
||||
|
||||
const video: PayloadCreateData<Video> = {
|
||||
await payload.create({
|
||||
collection: Collections.Videos,
|
||||
data: {
|
||||
uid,
|
||||
title,
|
||||
description,
|
||||
|
@ -81,11 +80,7 @@ export const importFromStrapi = createStrapiImportEndpoint<Video, StapiVideo>({
|
|||
source,
|
||||
publishedDate: `${year}-${month}-${day}`,
|
||||
channel: videoChannelId,
|
||||
};
|
||||
|
||||
await payload.create({
|
||||
collection: Collections.Videos,
|
||||
data: video,
|
||||
},
|
||||
user,
|
||||
});
|
||||
},
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { RowLabelArgs } from "payload/dist/admin/components/forms/RowLabel/types";
|
||||
import { CollectionGroups, Collections, KeysTypes } from "../../constants";
|
||||
import { createGetSlugsEndpoint } from "../../endpoints/createGetSlugsEndpoint";
|
||||
import { imageField } from "../../fields/imageField/imageField";
|
||||
import { keysField } from "../../fields/keysField/keysField";
|
||||
import { slugField } from "../../fields/slugField/slugField";
|
||||
|
@ -43,20 +42,16 @@ export const Weapons = buildVersionedCollectionConfig({
|
|||
],
|
||||
group: CollectionGroups.Collections,
|
||||
},
|
||||
endpoints: [
|
||||
importFromStrapi,
|
||||
createGetSlugsEndpoint(Collections.Weapons),
|
||||
getBySlugEndpoint,
|
||||
],
|
||||
endpoints: [importFromStrapi, getBySlugEndpoint],
|
||||
fields: [
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
slugField({ name: fields.slug, admin: { width: "50%" } }),
|
||||
slugField({ name: fields.slug, admin: { width: "0%" } }),
|
||||
imageField({
|
||||
name: fields.thumbnail,
|
||||
relationTo: Collections.WeaponsThumbnails,
|
||||
admin: { width: "50%" },
|
||||
admin: { width: "0%" },
|
||||
}),
|
||||
],
|
||||
},
|
||||
|
@ -67,13 +62,13 @@ export const Weapons = buildVersionedCollectionConfig({
|
|||
name: fields.type,
|
||||
relationTo: KeysTypes.Weapons,
|
||||
required: true,
|
||||
admin: { allowCreate: false, width: "50%" },
|
||||
admin: { allowCreate: false, width: "0%" },
|
||||
}),
|
||||
{
|
||||
name: fields.group,
|
||||
type: "relationship",
|
||||
relationTo: Collections.WeaponsGroups,
|
||||
admin: { width: "50%" },
|
||||
admin: { width: "0%" },
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -114,12 +109,12 @@ export const Weapons = buildVersionedCollectionConfig({
|
|||
name: fields.appearancesTranslationsName,
|
||||
type: "text",
|
||||
required: true,
|
||||
admin: { width: "50%" },
|
||||
admin: { width: "0%" },
|
||||
},
|
||||
{
|
||||
name: fields.appearancesTranslationsDescription,
|
||||
type: "textarea",
|
||||
admin: { width: "50%" },
|
||||
admin: { width: "0%" },
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -130,13 +125,13 @@ export const Weapons = buildVersionedCollectionConfig({
|
|||
name: fields.appearancesTranslationsLevel1,
|
||||
label: "Level 1",
|
||||
type: "textarea",
|
||||
admin: { width: "50%" },
|
||||
admin: { width: "0%" },
|
||||
},
|
||||
{
|
||||
name: fields.appearancesTranslationsLevel2,
|
||||
label: "Level 2",
|
||||
type: "textarea",
|
||||
admin: { width: "50%" },
|
||||
admin: { width: "0%" },
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -147,13 +142,13 @@ export const Weapons = buildVersionedCollectionConfig({
|
|||
name: fields.appearancesTranslationsLevel3,
|
||||
label: "Level 3",
|
||||
type: "textarea",
|
||||
admin: { width: "50%" },
|
||||
admin: { width: "0%" },
|
||||
},
|
||||
{
|
||||
name: fields.appearancesTranslationsLevel4,
|
||||
label: "Level 4",
|
||||
type: "textarea",
|
||||
admin: { width: "50%" },
|
||||
admin: { width: "0%" },
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useEffect, useState } from "react";
|
||||
import { styled } from "styled-components";
|
||||
import styled from "styled-components";
|
||||
import { Collections } from "../../../constants";
|
||||
|
||||
interface Props {
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
import payload from "payload";
|
||||
import { Collections } from "../../../constants";
|
||||
import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint";
|
||||
import { Weapon, WeaponsGroup } from "../../../types/collections";
|
||||
import { PayloadCreateData } from "../../../types/payload";
|
||||
import { StrapiImage, StrapiLanguage } from "../../../types/strapi";
|
||||
import { isDefined, isUndefined } from "../../../utils/asserts";
|
||||
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: {
|
||||
collection: "weapon-stories",
|
||||
params: {
|
||||
|
@ -46,12 +44,11 @@ export const importFromStrapi = createStrapiImportEndpoint<Weapon, StrapiWeapon>
|
|||
let groupId: string | undefined;
|
||||
if (isDefined(weapon_group.data)) {
|
||||
try {
|
||||
const groupData: PayloadCreateData<WeaponsGroup> = {
|
||||
slug: weapon_group.data.attributes.slug,
|
||||
};
|
||||
await payload.create({
|
||||
collection: Collections.WeaponsGroups,
|
||||
data: groupData,
|
||||
data: {
|
||||
slug: weapon_group.data.attributes.slug,
|
||||
},
|
||||
user,
|
||||
});
|
||||
} catch (e) {}
|
||||
|
@ -61,7 +58,7 @@ export const importFromStrapi = createStrapiImportEndpoint<Weapon, StrapiWeapon>
|
|||
where: { slug: { equals: weapon_group.data.attributes.slug } },
|
||||
});
|
||||
|
||||
if (result.docs.length > 0) {
|
||||
if (result.docs[0]) {
|
||||
groupId = result.docs[0].id;
|
||||
}
|
||||
}
|
||||
|
@ -73,7 +70,10 @@ export const importFromStrapi = createStrapiImportEndpoint<Weapon, StrapiWeapon>
|
|||
|
||||
if (isUndefined(type.data)) throw new Error("A type is required to create a Weapon");
|
||||
|
||||
const data: PayloadCreateData<Weapon> = {
|
||||
await payload.create({
|
||||
collection: Collections.Weapons,
|
||||
data: {
|
||||
updatedBy: user.id,
|
||||
slug,
|
||||
type: await findWeaponType(type.data.attributes.slug),
|
||||
group: groupId,
|
||||
|
@ -107,9 +107,9 @@ export const importFromStrapi = createStrapiImportEndpoint<Weapon, StrapiWeapon>
|
|||
),
|
||||
}))
|
||||
),
|
||||
};
|
||||
|
||||
await payload.create({ collection: Collections.Weapons, data, user });
|
||||
},
|
||||
user,
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { styled } from "styled-components";
|
||||
import styled from "styled-components";
|
||||
|
||||
export const Icon = styled.div`
|
||||
width: 46px;
|
||||
height: 46px;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
mask: url("/public/accords.svg");
|
||||
background-color: var(--theme-elevation-1000);
|
||||
mask-size: contain;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import "@fontsource/vollkorn/700.css";
|
||||
import React from "react";
|
||||
import { styled } from "styled-components";
|
||||
import styled from "styled-components";
|
||||
|
||||
export const Logo = (): JSX.Element => (
|
||||
<Container>
|
||||
|
|
|
@ -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 QueryString from "qs";
|
||||
import React from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import styled from "styled-components";
|
||||
import { LanguageCodes } from "../constants";
|
||||
|
||||
type Props = {
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import payload from "payload";
|
||||
import payload, { GeneratedTypes } from "payload";
|
||||
import { Collections } from "../constants";
|
||||
import { CollectionEndpoint } from "../types/payload";
|
||||
|
||||
export const createGetByEndpoint = <T, R>(
|
||||
collection: string,
|
||||
export const createGetByEndpoint = <C extends Collections, R>(
|
||||
collection: C,
|
||||
attribute: string,
|
||||
handler: (doc: T) => Promise<R> | R = (doc) => doc as unknown as R
|
||||
handler: (doc: GeneratedTypes["collections"][C]) => Promise<R> | R
|
||||
): CollectionEndpoint => ({
|
||||
path: `/${attribute}/:${attribute}`,
|
||||
method: "get",
|
||||
|
@ -24,7 +25,7 @@ export const createGetByEndpoint = <T, R>(
|
|||
where: { [attribute]: { equals: req.params[attribute] } },
|
||||
});
|
||||
|
||||
if (result.docs.length === 0) {
|
||||
if (!result.docs[0]) {
|
||||
return res.sendStatus(404);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
},
|
||||
});
|
|
@ -6,7 +6,7 @@ import { isDefined } from "../utils/asserts";
|
|||
|
||||
type Image = {
|
||||
filename: string;
|
||||
id: string | number;
|
||||
id: string;
|
||||
};
|
||||
|
||||
export const createImageRegenerationEndpoint = (collection: Collections): CollectionEndpoint => ({
|
||||
|
@ -36,7 +36,7 @@ export const createImageRegenerationEndpoint = (collection: Collections): Collec
|
|||
});
|
||||
|
||||
await Promise.all(
|
||||
images.docs.map(async (image: Image) => {
|
||||
images.docs.filter(isImage).map(async (image: Image) => {
|
||||
try {
|
||||
await payload.update({
|
||||
collection,
|
||||
|
@ -64,3 +64,9 @@ export const createImageRegenerationEndpoint = (collection: Collections): Collec
|
|||
.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;
|
||||
};
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
import payload from "payload";
|
||||
import payload, { GeneratedTypes } from "payload";
|
||||
import { BasePayload } from "payload/dist/payload";
|
||||
import QueryString from "qs";
|
||||
import { Collections } from "../constants";
|
||||
import { Recorder } from "../types/collections";
|
||||
import { CollectionEndpoint, PayloadCreateData } from "../types/payload";
|
||||
import { CollectionEndpoint } from "../types/payload";
|
||||
import { isDefined } from "../utils/asserts";
|
||||
|
||||
export const getAllStrapiEntries = async <T>(
|
||||
export const getAllStrapiEntries = async (
|
||||
collectionSlug: string,
|
||||
params: Object
|
||||
): Promise<T[]> => {
|
||||
): Promise<any[]> => {
|
||||
let page = 1;
|
||||
let totalPage = 1;
|
||||
const result: T[] = [];
|
||||
const result: any[] = [];
|
||||
|
||||
while (page <= totalPage) {
|
||||
const paramsWithPagination = QueryString.stringify({
|
||||
|
@ -30,24 +32,27 @@ export const getAllStrapiEntries = async <T>(
|
|||
return result;
|
||||
};
|
||||
|
||||
type Params<T, S> = {
|
||||
type Params<S> = {
|
||||
strapi: {
|
||||
collection: string;
|
||||
params: any;
|
||||
};
|
||||
payload: {
|
||||
collection: string;
|
||||
collection: Collections;
|
||||
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,
|
||||
payload: payloadParams,
|
||||
user,
|
||||
}: Params<T, S> & { user: Recorder }) => {
|
||||
const entries = await getAllStrapiEntries<any>(strapiParams.collection, strapiParams.params);
|
||||
}: Params<S> & { user: Recorder }) => {
|
||||
const entries = await getAllStrapiEntries(strapiParams.collection, strapiParams.params);
|
||||
|
||||
const errors: string[] = [];
|
||||
|
||||
|
@ -77,7 +82,7 @@ export const importStrapiEntries = async <T, S>({
|
|||
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",
|
||||
path: "/strapi",
|
||||
handler: async (req, res) => {
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
import payload from "payload";
|
||||
import { FieldBase } from "payload/dist/fields/config/types";
|
||||
import { RelationshipField, Where } from "payload/types";
|
||||
import { Collections } from "../../constants";
|
||||
import { isNotEmpty } from "../../utils/asserts";
|
||||
|
||||
type BackPropagationField = FieldBase & {
|
||||
where: (data: any) => Where;
|
||||
relationTo: string;
|
||||
relationTo: Collections;
|
||||
hasMany?: boolean;
|
||||
};
|
||||
export const backPropagationField = ({
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Props } from "payload/components/views/Cell";
|
||||
import React, { useEffect, useMemo, useState } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { styled } from "styled-components";
|
||||
import styled from "styled-components";
|
||||
import { isUndefined } from "../../utils/asserts";
|
||||
|
||||
const Image = styled.img`
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import { Language } from "../../types/collections";
|
||||
import { isDefined } from "../../utils/asserts";
|
||||
import { formatLanguageCode, shortenEllipsis } from "../../utils/string";
|
||||
import { Language } from "../../types/collections";
|
||||
import { styled } from "styled-components";
|
||||
|
||||
interface Props {
|
||||
language?: Language | string;
|
||||
|
|
|
@ -28,7 +28,7 @@ const languageField: Field = {
|
|||
type: "relationship",
|
||||
relationTo: Collections.Languages,
|
||||
required: true,
|
||||
admin: { allowCreate: false, width: "50%" },
|
||||
admin: { allowCreate: false, width: "0%" },
|
||||
};
|
||||
|
||||
const sourceLanguageField: Field = {
|
||||
|
@ -36,7 +36,7 @@ const sourceLanguageField: Field = {
|
|||
type: "relationship",
|
||||
relationTo: Collections.Languages,
|
||||
required: true,
|
||||
admin: { allowCreate: false, width: "50%" },
|
||||
admin: { allowCreate: false, width: "0%" },
|
||||
};
|
||||
|
||||
const creditFields: Field = {
|
||||
|
@ -64,7 +64,7 @@ const creditFields: Field = {
|
|||
},
|
||||
admin: {
|
||||
condition: (_, siblingData) => siblingData.language === siblingData.sourceLanguage,
|
||||
width: "50%",
|
||||
width: "0%",
|
||||
},
|
||||
validate: (count, { siblingData }) => {
|
||||
if (siblingData[fieldsNames.language] !== siblingData[fieldsNames.sourceLanguage]) {
|
||||
|
@ -95,7 +95,7 @@ const creditFields: Field = {
|
|||
admin: {
|
||||
condition: (_, siblingData) =>
|
||||
siblingData[fieldsNames.language] !== siblingData[fieldsNames.sourceLanguage],
|
||||
width: "50%",
|
||||
width: "0%",
|
||||
},
|
||||
validate: (count, { siblingData }) => {
|
||||
if (siblingData[fieldsNames.language] === siblingData[fieldsNames.sourceLanguage]) {
|
||||
|
@ -114,7 +114,7 @@ const creditFields: Field = {
|
|||
type: "relationship",
|
||||
relationTo: "recorders",
|
||||
hasMany: true,
|
||||
admin: { width: "50%" },
|
||||
admin: { width: "0%" },
|
||||
},
|
||||
],
|
||||
};
|
||||
|
|
|
@ -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 { buildConfig } from "payload/config";
|
||||
import { ChronologyEras } from "./collections/ChronologyEras/ChronologyEras";
|
||||
import { ChronologyItems } from "./collections/ChronologyItems/ChronologyItems";
|
||||
import { transcriptBlock } from "./collections/Contents/Blocks/transcriptBlock";
|
||||
import { Contents } from "./collections/Contents/Contents";
|
||||
import { ContentsFolders } from "./collections/ContentsFolders/ContentsFolders";
|
||||
import { ContentsThumbnails } from "./collections/ContentsThumbnails/ContentsThumbnails";
|
||||
|
@ -25,7 +29,6 @@ import { WeaponsThumbnails } from "./collections/WeaponsThumbnails/WeaponsThumbn
|
|||
import { Icon } from "./components/Icon";
|
||||
import { Logo } from "./components/Logo";
|
||||
import { Collections } from "./constants";
|
||||
import { payloadGridView } from "./plugins/payload-grid-view";
|
||||
|
||||
export default buildConfig({
|
||||
serverURL: process.env.PAYLOAD_URI,
|
||||
|
@ -38,7 +41,14 @@ export default buildConfig({
|
|||
titleSuffix: "- Accord’s Library",
|
||||
},
|
||||
css: path.resolve(__dirname, "styles.scss"),
|
||||
bundler: webpackBundler(),
|
||||
},
|
||||
editor: lexicalEditor({
|
||||
features: ({ defaultFeatures }) => [
|
||||
...defaultFeatures,
|
||||
BlocksFeature({ blocks: [transcriptBlock] }),
|
||||
],
|
||||
}),
|
||||
collections: [
|
||||
LibraryItems,
|
||||
Contents,
|
||||
|
@ -63,6 +73,9 @@ export default buildConfig({
|
|||
Recorders,
|
||||
Keys,
|
||||
],
|
||||
db: mongooseAdapter({
|
||||
url: process.env.MONGODB_URI ?? "mongodb://mongo:27017/payload",
|
||||
}),
|
||||
globals: [],
|
||||
telemetry: false,
|
||||
typescript: {
|
||||
|
@ -71,5 +84,4 @@ export default buildConfig({
|
|||
graphQL: {
|
||||
disable: true,
|
||||
},
|
||||
plugins: [payloadGridView],
|
||||
});
|
||||
|
|
|
@ -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%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
|
@ -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>
|
||||
);
|
||||
};
|
|
@ -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;
|
|
@ -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,
|
||||
});
|
|
@ -167,8 +167,6 @@ export type PayloadImage = {
|
|||
};
|
||||
|
||||
export const payload = {
|
||||
getSlugsWeapons: async (): Promise<string[]> =>
|
||||
await (await request(payloadApiUrl(Collections.Weapons, "slugs"))).json(),
|
||||
getWeapon: async (slug: string): Promise<EndpointWeapon> =>
|
||||
await (await request(payloadApiUrl(Collections.Weapons, `slug/${slug}`))).json(),
|
||||
getEras: async (): Promise<EndpointEra[]> =>
|
||||
|
|
|
@ -4,8 +4,6 @@ import { readFileSync } from "fs";
|
|||
import path from "path";
|
||||
import payload from "payload";
|
||||
import { Collections, RecordersRoles } from "./constants";
|
||||
import { Recorder } from "./types/collections";
|
||||
import { PayloadCreateData } from "./types/payload";
|
||||
import { isDefined, isUndefined } from "./utils/asserts";
|
||||
|
||||
const app = express();
|
||||
|
@ -28,7 +26,6 @@ const start = async () => {
|
|||
|
||||
await payload.init({
|
||||
secret: process.env.PAYLOAD_SECRET,
|
||||
mongoURL: process.env.MONGODB_URI,
|
||||
express: app,
|
||||
onInit: async () => {
|
||||
payload.logger.info(`Payload Admin URL: ${payload.getAdminURL()}`);
|
||||
|
@ -43,16 +40,15 @@ const start = async () => {
|
|||
if (recorders.docs.length === 0) {
|
||||
payload.logger.info("Seeding some initial data");
|
||||
|
||||
const recorder: PayloadCreateData<Recorder> = {
|
||||
await payload.create({
|
||||
collection: Collections.Recorders,
|
||||
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,
|
||||
};
|
||||
await payload.create({
|
||||
collection: Collections.Recorders,
|
||||
data: recorder,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,6 +46,16 @@ html[data-theme="light"] {
|
|||
--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 {
|
||||
> .array-field__header {
|
||||
.array-field__header-actions {
|
||||
|
|
|
@ -25,34 +25,36 @@ export type ContentFoldersTranslation = {
|
|||
|
||||
export interface Config {
|
||||
collections: {
|
||||
"library-items": LibraryItem;
|
||||
'library-items': LibraryItem;
|
||||
contents: Content;
|
||||
"contents-folders": ContentsFolder;
|
||||
'contents-folders': ContentsFolder;
|
||||
posts: Post;
|
||||
"chronology-items": ChronologyItem;
|
||||
"chronology-eras": ChronologyEra;
|
||||
'chronology-items': ChronologyItem;
|
||||
'chronology-eras': ChronologyEra;
|
||||
weapons: Weapon;
|
||||
"weapons-groups": WeaponsGroup;
|
||||
"weapons-thumbnails": WeaponsThumbnail;
|
||||
"contents-thumbnails": ContentsThumbnail;
|
||||
"library-items-thumbnails": LibraryItemThumbnail;
|
||||
"library-items-scans": LibraryItemScans;
|
||||
"library-items-gallery": LibraryItemGallery;
|
||||
"recorders-thumbnails": RecordersThumbnail;
|
||||
"posts-thumbnails": PostThumbnail;
|
||||
'weapons-groups': WeaponsGroup;
|
||||
'weapons-thumbnails': WeaponsThumbnail;
|
||||
'contents-thumbnails': ContentsThumbnail;
|
||||
'library-items-thumbnails': LibraryItemThumbnail;
|
||||
'library-items-scans': LibraryItemScans;
|
||||
'library-items-gallery': LibraryItemGallery;
|
||||
'recorders-thumbnails': RecordersThumbnail;
|
||||
'posts-thumbnails': PostThumbnail;
|
||||
files: File;
|
||||
videos: Video;
|
||||
"videos-channels": VideosChannel;
|
||||
'videos-channels': VideosChannel;
|
||||
languages: Language;
|
||||
currencies: Currency;
|
||||
recorders: Recorder;
|
||||
keys: Key;
|
||||
'payload-preferences': PayloadPreference;
|
||||
'payload-migrations': PayloadMigration;
|
||||
};
|
||||
globals: {};
|
||||
}
|
||||
export interface LibraryItem {
|
||||
id: string;
|
||||
itemType?: "Textual" | "Audio" | "Video" | "Game" | "Other";
|
||||
itemType?: 'Textual' | 'Audio' | 'Video' | 'Game' | 'Other';
|
||||
slug: string;
|
||||
thumbnail?: string | LibraryItemThumbnail;
|
||||
pretitle?: string;
|
||||
|
@ -115,8 +117,8 @@ export interface LibraryItem {
|
|||
subtype?: string[] | Key[];
|
||||
languages?: string[] | Language[];
|
||||
pageCount?: number;
|
||||
bindingType?: "Paperback" | "Hardcover";
|
||||
pageOrder?: "LeftToRight" | "RightToLeft";
|
||||
bindingType?: 'Paperback' | 'Hardcover';
|
||||
pageOrder?: 'LeftToRight' | 'RightToLeft';
|
||||
};
|
||||
audio?: {
|
||||
audioSubtype?: string[] | Key[];
|
||||
|
@ -160,7 +162,7 @@ export interface LibraryItem {
|
|||
updatedBy: string | Recorder;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
_status?: "draft" | "published";
|
||||
_status?: 'draft' | 'published';
|
||||
}
|
||||
export interface LibraryItemThumbnail {
|
||||
id: string;
|
||||
|
@ -278,16 +280,16 @@ export interface Key {
|
|||
id: string;
|
||||
name: string;
|
||||
type:
|
||||
| "Contents"
|
||||
| "LibraryAudio"
|
||||
| "LibraryVideo"
|
||||
| "LibraryTextual"
|
||||
| "LibraryGroup"
|
||||
| "Library"
|
||||
| "Weapons"
|
||||
| "GamePlatforms"
|
||||
| "Categories"
|
||||
| "Wordings";
|
||||
| 'Contents'
|
||||
| 'LibraryAudio'
|
||||
| 'LibraryVideo'
|
||||
| 'LibraryTextual'
|
||||
| 'LibraryGroup'
|
||||
| 'Library'
|
||||
| 'Weapons'
|
||||
| 'GamePlatforms'
|
||||
| 'Categories'
|
||||
| 'Wordings';
|
||||
translations?: CategoryTranslations;
|
||||
}
|
||||
export interface Language {
|
||||
|
@ -297,7 +299,7 @@ export interface Language {
|
|||
export interface File {
|
||||
id: string;
|
||||
filename: string;
|
||||
type: "LibraryScans" | "LibrarySoundtracks" | "ContentVideo" | "ContentAudio";
|
||||
type: 'LibraryScans' | 'LibrarySoundtracks' | 'ContentVideo' | 'ContentAudio';
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
|
@ -317,10 +319,12 @@ export interface Content {
|
|||
title: string;
|
||||
subtitle?: string;
|
||||
summary?: string;
|
||||
textContent?: {
|
||||
[k: string]: unknown;
|
||||
}[];
|
||||
textTranscribers?: string[] | Recorder[];
|
||||
textTranslators?: string[] | Recorder[];
|
||||
textProofreaders?: string[] | Recorder[];
|
||||
textContent?: (TextBlock | Section | Tabs | TranscriptBlock | QuoteBlock)[];
|
||||
textNotes?: string;
|
||||
video?: string | File;
|
||||
videoNotes?: string;
|
||||
|
@ -330,7 +334,7 @@ export interface Content {
|
|||
updatedBy: string | Recorder;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
_status?: "draft" | "published";
|
||||
_status?: 'draft' | 'published';
|
||||
}
|
||||
export interface ContentsThumbnail {
|
||||
id: string;
|
||||
|
@ -376,7 +380,7 @@ export interface Recorder {
|
|||
avatar?: string | RecordersThumbnail;
|
||||
languages?: string[] | Language[];
|
||||
biographies?: RecorderBiographies;
|
||||
role?: ("Admin" | "Recorder" | "Api")[];
|
||||
role?: ('Admin' | 'Recorder' | 'Api')[];
|
||||
anonymize: boolean;
|
||||
email: 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 {
|
||||
id: string;
|
||||
slug: string;
|
||||
|
@ -617,21 +434,21 @@ export interface Post {
|
|||
thumbnail?: string | PostThumbnail;
|
||||
authors:
|
||||
| {
|
||||
relationTo: 'recorders';
|
||||
value: string;
|
||||
relationTo: "recorders";
|
||||
}[]
|
||||
| {
|
||||
relationTo: 'recorders';
|
||||
value: Recorder;
|
||||
relationTo: "recorders";
|
||||
}[];
|
||||
categories?:
|
||||
| {
|
||||
relationTo: 'keys';
|
||||
value: string;
|
||||
relationTo: "keys";
|
||||
}[]
|
||||
| {
|
||||
relationTo: 'keys';
|
||||
value: Key;
|
||||
relationTo: "keys";
|
||||
}[];
|
||||
translations: {
|
||||
language: string | Language;
|
||||
|
@ -650,7 +467,7 @@ export interface Post {
|
|||
updatedBy: string | Recorder;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
_status?: "draft" | "published";
|
||||
_status?: 'draft' | 'published';
|
||||
}
|
||||
export interface PostThumbnail {
|
||||
id: string;
|
||||
|
@ -701,12 +518,12 @@ export interface ChronologyItem {
|
|||
events: {
|
||||
source?:
|
||||
| {
|
||||
relationTo: 'contents';
|
||||
value: string | Content;
|
||||
relationTo: "contents";
|
||||
}
|
||||
| {
|
||||
relationTo: 'library-items';
|
||||
value: string | LibraryItem;
|
||||
relationTo: "library-items";
|
||||
};
|
||||
translations: {
|
||||
language: string | Language;
|
||||
|
@ -724,7 +541,7 @@ export interface ChronologyItem {
|
|||
updatedBy: string | Recorder;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
_status?: "draft" | "published";
|
||||
_status?: 'draft' | 'published';
|
||||
}
|
||||
export interface ChronologyEra {
|
||||
id: string;
|
||||
|
@ -768,7 +585,7 @@ export interface Weapon {
|
|||
updatedBy: string | Recorder;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
_status?: "draft" | "published";
|
||||
_status?: 'draft' | 'published';
|
||||
}
|
||||
export interface WeaponsThumbnail {
|
||||
id: string;
|
||||
|
@ -830,7 +647,7 @@ export interface Video {
|
|||
id: string;
|
||||
uid: string;
|
||||
gone: boolean;
|
||||
source: "YouTube" | "NicoNico" | "Tumblr";
|
||||
source: 'YouTube' | 'NicoNico' | 'Tumblr';
|
||||
title: string;
|
||||
description?: string;
|
||||
likes?: number;
|
||||
|
@ -844,3 +661,62 @@ export interface VideosChannel {
|
|||
title: string;
|
||||
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
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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<
|
||||
T,
|
||||
"id" | "updatedAt" | "createdAt" | "sizes" | "updatedBy"
|
||||
>;
|
||||
|
||||
export type CollectionEndpoint = NonNullable<CollectionConfig["endpoints"]>[number];
|
||||
export type CollectionEndpoint = Omit<Endpoint, "root">;
|
||||
|
||||
export type EndpointAccess<U> = (req: PayloadRequest<U>) => boolean;
|
||||
|
|
|
@ -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 =>
|
||||
!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 =>
|
||||
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;
|
||||
|
||||
type Span = [number, number];
|
||||
export const hasNoIntersection = (a: Span, b: Span): boolean => {
|
||||
const hasNoIntersection = (a: Span, b: Span): boolean => {
|
||||
const [aStart, aEnd] = a;
|
||||
const [bStart, bEnd] = b;
|
||||
return aEnd < bStart || aStart > bEnd;
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import { CollectionConfig } from "payload/types";
|
||||
import { Collections } from "../constants";
|
||||
import { CollectionConfigWithGridView } from "../plugins/payload-grid-view";
|
||||
|
||||
type CollectionConfigWithPlugins = CollectionConfig & CollectionConfigWithGridView;
|
||||
type CollectionConfigWithPlugins = CollectionConfig;
|
||||
|
||||
export type BuildCollectionConfig = Omit<
|
||||
CollectionConfigWithPlugins,
|
||||
|
|
|
@ -8,6 +8,7 @@ export const findWeaponType = async (name: string): Promise<string> => {
|
|||
collection: Collections.Keys,
|
||||
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;
|
||||
};
|
||||
|
||||
|
@ -16,7 +17,8 @@ export const findCategory = async (name: string): Promise<string> => {
|
|||
collection: Collections.Keys,
|
||||
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 = {
|
||||
|
|
|
@ -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);
|
||||
};
|
|
@ -7,7 +7,7 @@ export const shortenEllipsis = (text: string, length: number): string =>
|
|||
export const formatLanguageCode = (code: string): string =>
|
||||
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;
|
||||
if (isUndefined(firstLetter)) return "";
|
||||
return [firstLetter.toUpperCase(), ...otherLetters].join("");
|
||||
|
@ -19,6 +19,3 @@ export const formatToCamelCase = (name: string): string =>
|
|||
.split(/[ \_-]/g)
|
||||
.map((part, index) => (index > 0 ? capitalize(part) : part))
|
||||
.join("");
|
||||
|
||||
export const formatToKebabCase = (name: string): string =>
|
||||
name.toLowerCase().replaceAll(/[ \_]/g, "-");
|
||||
|
|
|
@ -14,7 +14,7 @@ const updatedByField = (): RelationshipField => ({
|
|||
type: "relationship",
|
||||
required: true,
|
||||
relationTo: Collections.Recorders,
|
||||
admin: { readOnly: true, position: "sidebar" },
|
||||
admin: { readOnly: true },
|
||||
});
|
||||
|
||||
type BuildVersionedCollectionConfig = Omit<BuildCollectionConfig, "timestamps" | "versions">;
|
||||
|
|
Loading…
Reference in New Issue