Streamlined collection config
This commit is contained in:
parent
0a45ebb134
commit
65286f0c66
|
@ -1,9 +1,8 @@
|
|||
import { CollectionConfig } from "payload/types";
|
||||
import { slugField } from "../../fields/slugField/slugField";
|
||||
import { CollectionGroup, KeysTypes } from "../../constants";
|
||||
import { CollectionGroup } from "../../constants";
|
||||
import { localizedFields } from "../../fields/translatedFields/translatedFields";
|
||||
import { collectionSlug } from "../../utils/string";
|
||||
import { Contents } from "../Contents/Contents";
|
||||
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
||||
|
||||
const fields = {
|
||||
slug: "slug",
|
||||
|
@ -13,53 +12,49 @@ const fields = {
|
|||
contents: "contents",
|
||||
} as const satisfies Record<string, string>;
|
||||
|
||||
const labels = {
|
||||
singular: "Content Folder",
|
||||
plural: "Content Folders",
|
||||
} as const satisfies { singular: string; plural: string };
|
||||
|
||||
const slug = collectionSlug(labels.plural);
|
||||
|
||||
export const ContentFolders: CollectionConfig = {
|
||||
slug,
|
||||
labels,
|
||||
typescript: { interface: labels.singular },
|
||||
defaultSort: fields.slug,
|
||||
admin: {
|
||||
useAsTitle: fields.slug,
|
||||
defaultColumns: [fields.slug, fields.translations],
|
||||
group: CollectionGroup.Collections,
|
||||
export const ContentFolders = buildCollectionConfig(
|
||||
{
|
||||
singular: "Content Folder",
|
||||
plural: "Content Folders",
|
||||
},
|
||||
timestamps: false,
|
||||
versions: false,
|
||||
fields: [
|
||||
slugField({ name: fields.slug }),
|
||||
localizedFields({
|
||||
name: fields.translations,
|
||||
interfaceName: "ContentFoldersTranslation",
|
||||
admin: {
|
||||
useAsTitle: fields.name,
|
||||
},
|
||||
fields: [{ name: fields.name, type: "text", required: true }],
|
||||
}),
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
{
|
||||
type: "relationship",
|
||||
name: fields.subfolders,
|
||||
relationTo: [slug],
|
||||
hasMany: true,
|
||||
admin: { width: "50%" },
|
||||
},
|
||||
{
|
||||
type: "relationship",
|
||||
name: fields.contents,
|
||||
relationTo: [Contents.slug],
|
||||
hasMany: true,
|
||||
admin: { width: "50%" },
|
||||
},
|
||||
],
|
||||
({ slug }) => ({
|
||||
defaultSort: fields.slug,
|
||||
admin: {
|
||||
useAsTitle: fields.slug,
|
||||
defaultColumns: [fields.slug, fields.translations],
|
||||
group: CollectionGroup.Collections,
|
||||
},
|
||||
],
|
||||
};
|
||||
timestamps: false,
|
||||
versions: false,
|
||||
fields: [
|
||||
slugField({ name: fields.slug }),
|
||||
localizedFields({
|
||||
name: fields.translations,
|
||||
interfaceName: "ContentFoldersTranslation",
|
||||
admin: {
|
||||
useAsTitle: fields.name,
|
||||
},
|
||||
fields: [{ name: fields.name, type: "text", required: true }],
|
||||
}),
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
{
|
||||
type: "relationship",
|
||||
name: fields.subfolders,
|
||||
relationTo: [slug],
|
||||
hasMany: true,
|
||||
admin: { width: "50%" },
|
||||
},
|
||||
{
|
||||
type: "relationship",
|
||||
name: fields.contents,
|
||||
relationTo: [Contents.slug],
|
||||
hasMany: true,
|
||||
admin: { width: "50%" },
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
})
|
||||
);
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { CollectionConfig } from "payload/types";
|
||||
import { CollectionGroup } from "../../constants";
|
||||
import { collectionSlug } from "../../utils/string";
|
||||
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
||||
|
||||
const fields = {
|
||||
filename: "filename",
|
||||
|
@ -8,45 +7,41 @@ const fields = {
|
|||
filesize: "filesize",
|
||||
} as const satisfies Record<string, string>;
|
||||
|
||||
const labels = {
|
||||
singular: "Content Thumbnail",
|
||||
plural: "Content Thumbnails",
|
||||
} as const satisfies { singular: string; plural: string };
|
||||
|
||||
export const ContentThumbnails: CollectionConfig = {
|
||||
slug: collectionSlug(labels.plural),
|
||||
labels,
|
||||
typescript: { interface: labels.singular },
|
||||
defaultSort: fields.filename,
|
||||
admin: {
|
||||
useAsTitle: fields.filename,
|
||||
group: CollectionGroup.Media,
|
||||
export const ContentThumbnails = buildCollectionConfig(
|
||||
{
|
||||
singular: "Content Thumbnail",
|
||||
plural: "Content Thumbnails",
|
||||
},
|
||||
|
||||
upload: {
|
||||
staticDir: `../uploads/${labels.plural}`,
|
||||
mimeTypes: ["image/*"],
|
||||
imageSizes: [
|
||||
{
|
||||
name: "og",
|
||||
height: 750,
|
||||
width: 1125,
|
||||
formatOptions: {
|
||||
format: "jpg",
|
||||
options: { progressive: true, mozjpeg: true, compressionLevel: 9, quality: 80 },
|
||||
({ labels }) => ({
|
||||
defaultSort: fields.filename,
|
||||
admin: {
|
||||
useAsTitle: fields.filename,
|
||||
group: CollectionGroup.Media,
|
||||
},
|
||||
upload: {
|
||||
staticDir: `../uploads/${labels.plural}`,
|
||||
mimeTypes: ["image/*"],
|
||||
imageSizes: [
|
||||
{
|
||||
name: "og",
|
||||
height: 750,
|
||||
width: 1125,
|
||||
formatOptions: {
|
||||
format: "jpg",
|
||||
options: { progressive: true, mozjpeg: true, compressionLevel: 9, quality: 80 },
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "medium",
|
||||
height: 1000,
|
||||
width: 1500,
|
||||
formatOptions: {
|
||||
format: "webp",
|
||||
options: { effort: 6, quality: 80, alphaQuality: 80 },
|
||||
{
|
||||
name: "medium",
|
||||
height: 1000,
|
||||
width: 1500,
|
||||
formatOptions: {
|
||||
format: "webp",
|
||||
options: { effort: 6, quality: 80, alphaQuality: 80 },
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
fields: [],
|
||||
};
|
||||
],
|
||||
},
|
||||
fields: [],
|
||||
})
|
||||
);
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import { CollectionConfig } from "payload/types";
|
||||
import { collectionSlug } from "../../utils/string";
|
||||
import { CollectionGroup, FileTypes, KeysTypes } from "../../constants";
|
||||
import { slugField } from "../../fields/slugField/slugField";
|
||||
import { imageField } from "../../fields/imageField/imageField";
|
||||
|
@ -10,6 +8,7 @@ import { isDefined } from "../../utils/asserts";
|
|||
import { fileField } from "../../fields/fileField/fileField";
|
||||
import { contentBlocks } from "./Blocks/blocks";
|
||||
import { ContentThumbnails } from "../ContentThumbnails/ContentThumbnails";
|
||||
import { buildVersionedCollectionConfig } from "../../utils/versionedCollectionConfig";
|
||||
|
||||
const fields = {
|
||||
slug: "slug",
|
||||
|
@ -31,187 +30,186 @@ const fields = {
|
|||
audio: "audio",
|
||||
audioNotes: "videoNotes",
|
||||
status: "status",
|
||||
updatedBy: "updatedBy",
|
||||
} as const satisfies Record<string, string>;
|
||||
|
||||
const labels = {
|
||||
singular: "Content",
|
||||
plural: "Contents",
|
||||
} as const satisfies { singular: string; plural: string };
|
||||
|
||||
export const Contents: CollectionConfig = {
|
||||
slug: collectionSlug(labels.plural),
|
||||
labels,
|
||||
typescript: { interface: labels.singular },
|
||||
defaultSort: fields.slug,
|
||||
admin: {
|
||||
useAsTitle: fields.slug,
|
||||
defaultColumns: [
|
||||
fields.slug,
|
||||
fields.thumbnail,
|
||||
fields.categories,
|
||||
fields.type,
|
||||
fields.translations,
|
||||
fields.status,
|
||||
],
|
||||
group: CollectionGroup.Collections,
|
||||
preview: (doc) => `https://accords-library.com/contents/${doc.slug}`,
|
||||
export const Contents = buildVersionedCollectionConfig(
|
||||
{
|
||||
singular: "Content",
|
||||
plural: "Contents",
|
||||
},
|
||||
timestamps: true,
|
||||
versions: { drafts: { autosave: true } },
|
||||
fields: [
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
slugField({ name: fields.slug, admin: { width: "50%" } }),
|
||||
imageField({
|
||||
name: fields.thumbnail,
|
||||
relationTo: ContentThumbnails.slug,
|
||||
admin: { width: "50%" },
|
||||
}),
|
||||
() => ({
|
||||
defaultSort: fields.slug,
|
||||
admin: {
|
||||
useAsTitle: fields.slug,
|
||||
description:
|
||||
"All the contents (textual, audio, and video) from the Library or other online sources.",
|
||||
defaultColumns: [
|
||||
fields.slug,
|
||||
fields.thumbnail,
|
||||
fields.categories,
|
||||
fields.type,
|
||||
fields.translations,
|
||||
fields.status,
|
||||
],
|
||||
group: CollectionGroup.Collections,
|
||||
preview: (doc) => `https://accords-library.com/contents/${doc.slug}`,
|
||||
},
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
{
|
||||
name: fields.categories,
|
||||
type: "relationship",
|
||||
relationTo: [Keys.slug],
|
||||
filterOptions: { type: { equals: KeysTypes.Categories } },
|
||||
hasMany: true,
|
||||
admin: { allowCreate: false, width: "50%" },
|
||||
},
|
||||
{
|
||||
name: fields.type,
|
||||
type: "relationship",
|
||||
relationTo: [Keys.slug],
|
||||
filterOptions: { type: { equals: KeysTypes.Contents } },
|
||||
admin: { allowCreate: false, width: "50%" },
|
||||
},
|
||||
],
|
||||
},
|
||||
localizedFields({
|
||||
name: fields.translations,
|
||||
admin: { useAsTitle: fields.title, hasSourceLanguage: true },
|
||||
required: true,
|
||||
minRows: 1,
|
||||
fields: [
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
{ name: fields.pretitle, type: "text" },
|
||||
{ name: fields.title, type: "text", required: true },
|
||||
{ name: fields.subtitle, type: "text" },
|
||||
],
|
||||
},
|
||||
{ name: fields.summary, type: "textarea" },
|
||||
{
|
||||
type: "tabs",
|
||||
admin: {
|
||||
condition: (_, siblingData) =>
|
||||
isDefined(siblingData.language) && isDefined(siblingData.sourceLanguage),
|
||||
fields: [
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
slugField({ name: fields.slug, admin: { width: "50%" } }),
|
||||
imageField({
|
||||
name: fields.thumbnail,
|
||||
relationTo: ContentThumbnails.slug,
|
||||
admin: { width: "50%" },
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
{
|
||||
name: fields.categories,
|
||||
type: "relationship",
|
||||
relationTo: [Keys.slug],
|
||||
filterOptions: { type: { equals: KeysTypes.Categories } },
|
||||
hasMany: true,
|
||||
admin: { allowCreate: false, width: "50%" },
|
||||
},
|
||||
tabs: [
|
||||
{
|
||||
label: "Text",
|
||||
fields: [
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
{
|
||||
name: fields.textTranscribers,
|
||||
label: "Transcribers",
|
||||
type: "relationship",
|
||||
relationTo: Recorders.slug,
|
||||
hasMany: true,
|
||||
admin: {
|
||||
condition: (_, siblingData) =>
|
||||
siblingData.language === siblingData.sourceLanguage,
|
||||
width: "50%",
|
||||
{
|
||||
name: fields.type,
|
||||
type: "relationship",
|
||||
relationTo: [Keys.slug],
|
||||
filterOptions: { type: { equals: KeysTypes.Contents } },
|
||||
admin: { allowCreate: false, width: "50%" },
|
||||
},
|
||||
],
|
||||
},
|
||||
localizedFields({
|
||||
name: fields.translations,
|
||||
admin: { useAsTitle: fields.title, hasSourceLanguage: true },
|
||||
required: true,
|
||||
minRows: 1,
|
||||
fields: [
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
{ name: fields.pretitle, type: "text" },
|
||||
{ name: fields.title, type: "text", required: true },
|
||||
{ name: fields.subtitle, type: "text" },
|
||||
],
|
||||
},
|
||||
{ name: fields.summary, type: "textarea" },
|
||||
{
|
||||
type: "tabs",
|
||||
admin: {
|
||||
condition: (_, siblingData) =>
|
||||
isDefined(siblingData.language) && isDefined(siblingData.sourceLanguage),
|
||||
},
|
||||
tabs: [
|
||||
{
|
||||
label: "Text",
|
||||
fields: [
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
{
|
||||
name: fields.textTranscribers,
|
||||
label: "Transcribers",
|
||||
type: "relationship",
|
||||
relationTo: Recorders.slug,
|
||||
hasMany: true,
|
||||
admin: {
|
||||
condition: (_, siblingData) =>
|
||||
siblingData.language === siblingData.sourceLanguage,
|
||||
width: "50%",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: fields.textTranslators,
|
||||
label: "Translators",
|
||||
type: "relationship",
|
||||
relationTo: Recorders.slug,
|
||||
hasMany: true,
|
||||
admin: {
|
||||
condition: (_, siblingData) =>
|
||||
siblingData.language !== siblingData.sourceLanguage,
|
||||
width: "50%",
|
||||
{
|
||||
name: fields.textTranslators,
|
||||
label: "Translators",
|
||||
type: "relationship",
|
||||
relationTo: Recorders.slug,
|
||||
hasMany: true,
|
||||
admin: {
|
||||
condition: (_, siblingData) =>
|
||||
siblingData.language !== siblingData.sourceLanguage,
|
||||
width: "50%",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: fields.textProofreaders,
|
||||
label: "Proofreaders",
|
||||
type: "relationship",
|
||||
relationTo: Recorders.slug,
|
||||
hasMany: true,
|
||||
admin: { width: "50%" },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: fields.textContent,
|
||||
label: "Content",
|
||||
labels: { singular: "Block", plural: "Blocks" },
|
||||
type: "blocks",
|
||||
admin: { initCollapsed: true },
|
||||
blocks: contentBlocks,
|
||||
},
|
||||
{
|
||||
name: fields.textNotes,
|
||||
label: "Notes",
|
||||
type: "textarea",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Video",
|
||||
fields: [
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
fileField({
|
||||
name: fields.video,
|
||||
filterOptions: { type: { equals: FileTypes.ContentVideo } },
|
||||
admin: { width: "50%" },
|
||||
}),
|
||||
{
|
||||
name: fields.videoNotes,
|
||||
label: "Notes",
|
||||
type: "textarea",
|
||||
admin: { width: "50%" },
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Audio",
|
||||
fields: [
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
fileField({
|
||||
name: fields.audio,
|
||||
filterOptions: { type: { equals: FileTypes.ContentAudio } },
|
||||
admin: { width: "50%" },
|
||||
}),
|
||||
{
|
||||
name: fields.audioNotes,
|
||||
label: "Notes",
|
||||
type: "textarea",
|
||||
admin: { width: "50%" },
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}),
|
||||
],
|
||||
};
|
||||
{
|
||||
name: fields.textProofreaders,
|
||||
label: "Proofreaders",
|
||||
type: "relationship",
|
||||
relationTo: Recorders.slug,
|
||||
hasMany: true,
|
||||
admin: { width: "50%" },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: fields.textContent,
|
||||
label: "Content",
|
||||
labels: { singular: "Block", plural: "Blocks" },
|
||||
type: "blocks",
|
||||
admin: { initCollapsed: true },
|
||||
blocks: contentBlocks,
|
||||
},
|
||||
{
|
||||
name: fields.textNotes,
|
||||
label: "Notes",
|
||||
type: "textarea",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Video",
|
||||
fields: [
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
fileField({
|
||||
name: fields.video,
|
||||
filterOptions: { type: { equals: FileTypes.ContentVideo } },
|
||||
admin: { width: "50%" },
|
||||
}),
|
||||
{
|
||||
name: fields.videoNotes,
|
||||
label: "Notes",
|
||||
type: "textarea",
|
||||
admin: { width: "50%" },
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Audio",
|
||||
fields: [
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
fileField({
|
||||
name: fields.audio,
|
||||
filterOptions: { type: { equals: FileTypes.ContentAudio } },
|
||||
admin: { width: "50%" },
|
||||
}),
|
||||
{
|
||||
name: fields.audioNotes,
|
||||
label: "Notes",
|
||||
type: "textarea",
|
||||
admin: { width: "50%" },
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}),
|
||||
],
|
||||
})
|
||||
);
|
||||
|
|
|
@ -1,38 +1,34 @@
|
|||
import { CollectionConfig } from "payload/types";
|
||||
import { CollectionGroup, FileTypes } from "../../constants";
|
||||
import { collectionSlug } from "../../utils/string";
|
||||
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
||||
|
||||
const fields = {
|
||||
filename: "filename",
|
||||
type: "type",
|
||||
} as const satisfies Record<string, string>;
|
||||
|
||||
const labels = {
|
||||
singular: "File",
|
||||
plural: "Files",
|
||||
} as const satisfies { singular: string; plural: string };
|
||||
|
||||
export const Files: CollectionConfig = {
|
||||
slug: collectionSlug(labels.plural),
|
||||
labels,
|
||||
typescript: { interface: labels.singular },
|
||||
defaultSort: fields.filename,
|
||||
admin: {
|
||||
useAsTitle: fields.filename,
|
||||
group: CollectionGroup.Media,
|
||||
export const Files = buildCollectionConfig(
|
||||
{
|
||||
singular: "File",
|
||||
plural: "Files",
|
||||
},
|
||||
|
||||
fields: [
|
||||
{
|
||||
name: fields.filename,
|
||||
required: true,
|
||||
type: "text",
|
||||
() => ({
|
||||
defaultSort: fields.filename,
|
||||
admin: {
|
||||
useAsTitle: fields.filename,
|
||||
group: CollectionGroup.Media,
|
||||
},
|
||||
{
|
||||
name: fields.type,
|
||||
type: "select",
|
||||
required: true,
|
||||
options: Object.entries(FileTypes).map(([value, label]) => ({ label, value })),
|
||||
},
|
||||
],
|
||||
};
|
||||
fields: [
|
||||
{
|
||||
name: fields.filename,
|
||||
required: true,
|
||||
type: "text",
|
||||
},
|
||||
{
|
||||
name: fields.type,
|
||||
type: "select",
|
||||
required: true,
|
||||
options: Object.entries(FileTypes).map(([value, label]) => ({ label, value })),
|
||||
},
|
||||
],
|
||||
})
|
||||
);
|
||||
|
|
|
@ -2,9 +2,9 @@ import { CollectionConfig } from "payload/types";
|
|||
import { slugField } from "../../fields/slugField/slugField";
|
||||
import { CollectionGroup, KeysTypes } from "../../constants";
|
||||
import { localizedFields } from "../../fields/translatedFields/translatedFields";
|
||||
import { collectionSlug } from "../../utils/string";
|
||||
import { Key } from "../../types/collections";
|
||||
import { isDefined } from "../../utils/asserts";
|
||||
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
||||
|
||||
const fields = {
|
||||
slug: "slug",
|
||||
|
@ -14,56 +14,54 @@ const fields = {
|
|||
short: "short",
|
||||
} as const satisfies Record<string, string>;
|
||||
|
||||
const labels = {
|
||||
singular: "Key",
|
||||
plural: "Keys",
|
||||
} as const satisfies { singular: string; plural: string };
|
||||
|
||||
const keysTypesWithShort: (keyof typeof KeysTypes)[] = ["Categories", "GamePlatforms"];
|
||||
|
||||
export const Keys: CollectionConfig = {
|
||||
slug: collectionSlug(labels.plural),
|
||||
labels,
|
||||
typescript: { interface: labels.singular },
|
||||
defaultSort: fields.slug,
|
||||
admin: {
|
||||
useAsTitle: fields.slug,
|
||||
defaultColumns: [fields.slug, fields.type, fields.translations],
|
||||
group: CollectionGroup.Meta,
|
||||
export const Keys: CollectionConfig = buildCollectionConfig(
|
||||
{
|
||||
singular: "Key",
|
||||
plural: "Keys",
|
||||
},
|
||||
timestamps: false,
|
||||
versions: false,
|
||||
fields: [
|
||||
slugField({ name: fields.slug }),
|
||||
{
|
||||
name: fields.type,
|
||||
type: "select",
|
||||
required: true,
|
||||
options: Object.entries(KeysTypes).map(([value, label]) => ({ label, value })),
|
||||
() => ({
|
||||
defaultSort: fields.slug,
|
||||
admin: {
|
||||
useAsTitle: fields.slug,
|
||||
defaultColumns: [fields.slug, fields.type, fields.translations],
|
||||
group: CollectionGroup.Meta,
|
||||
},
|
||||
localizedFields({
|
||||
name: fields.translations,
|
||||
interfaceName: "CategoryTranslations",
|
||||
admin: {
|
||||
useAsTitle: fields.name,
|
||||
timestamps: false,
|
||||
versions: false,
|
||||
fields: [
|
||||
slugField({ name: fields.slug }),
|
||||
{
|
||||
name: fields.type,
|
||||
type: "select",
|
||||
required: true,
|
||||
options: Object.entries(KeysTypes).map(([value, label]) => ({ label, value })),
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
{ name: fields.name, type: "text", required: true, admin: { width: "50%" } },
|
||||
{
|
||||
name: fields.short,
|
||||
type: "text",
|
||||
admin: {
|
||||
condition: (data: Partial<Key>) =>
|
||||
isDefined(data.type) && keysTypesWithShort.includes(data.type),
|
||||
width: "50%",
|
||||
},
|
||||
},
|
||||
],
|
||||
localizedFields({
|
||||
name: fields.translations,
|
||||
interfaceName: "CategoryTranslations",
|
||||
admin: {
|
||||
useAsTitle: fields.name,
|
||||
},
|
||||
],
|
||||
}),
|
||||
],
|
||||
};
|
||||
fields: [
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
{ name: fields.name, type: "text", required: true, admin: { width: "50%" } },
|
||||
{
|
||||
name: fields.short,
|
||||
type: "text",
|
||||
admin: {
|
||||
condition: (data: Partial<Key>) =>
|
||||
isDefined(data.type) && keysTypesWithShort.includes(data.type),
|
||||
width: "50%",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}),
|
||||
],
|
||||
})
|
||||
);
|
||||
|
|
|
@ -1,46 +1,43 @@
|
|||
import { CollectionConfig } from "payload/types";
|
||||
import { CollectionGroup } from "../constants";
|
||||
import { collectionSlug } from "../utils/string";
|
||||
import { buildCollectionConfig } from "../utils/collectionConfig";
|
||||
|
||||
const fields = {
|
||||
id: "id",
|
||||
name: "name",
|
||||
} as const satisfies Record<string, string>;
|
||||
|
||||
const labels = {
|
||||
singular: "Language",
|
||||
plural: "Languages",
|
||||
} as const satisfies { singular: string; plural: string };
|
||||
|
||||
export const Languages: CollectionConfig = {
|
||||
slug: collectionSlug(labels.plural),
|
||||
labels,
|
||||
typescript: { interface: labels.singular },
|
||||
defaultSort: fields.name,
|
||||
admin: {
|
||||
useAsTitle: fields.name,
|
||||
defaultColumns: [fields.name, fields.id],
|
||||
group: CollectionGroup.Meta,
|
||||
export const Languages = buildCollectionConfig(
|
||||
{
|
||||
singular: "Language",
|
||||
plural: "Languages",
|
||||
},
|
||||
timestamps: false,
|
||||
fields: [
|
||||
{
|
||||
name: fields.id,
|
||||
type: "text",
|
||||
unique: true,
|
||||
required: true,
|
||||
validate: (value) => {
|
||||
if (/^[a-z]{2}(-[a-z]{2})?$/g.test(value)) {
|
||||
return true;
|
||||
}
|
||||
return "The code must be a valid IETF language tag and lowercase (i.e: en, pt-pt, fr, zh-tw...)";
|
||||
() => ({
|
||||
defaultSort: fields.name,
|
||||
admin: {
|
||||
useAsTitle: fields.name,
|
||||
defaultColumns: [fields.name, fields.id],
|
||||
group: CollectionGroup.Meta,
|
||||
},
|
||||
timestamps: false,
|
||||
fields: [
|
||||
{
|
||||
name: fields.id,
|
||||
type: "text",
|
||||
unique: true,
|
||||
required: true,
|
||||
validate: (value) => {
|
||||
if (/^[a-z]{2}(-[a-z]{2})?$/g.test(value)) {
|
||||
return true;
|
||||
}
|
||||
return "The code must be a valid IETF language tag and lowercase (i.e: en, pt-pt, fr, zh-tw...)";
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: fields.name,
|
||||
type: "text",
|
||||
unique: true,
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
{
|
||||
name: fields.name,
|
||||
type: "text",
|
||||
unique: true,
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
})
|
||||
);
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { CollectionConfig } from "payload/types";
|
||||
import { CollectionGroup } from "../../constants";
|
||||
import { collectionSlug } from "../../utils/string";
|
||||
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
||||
|
||||
const fields = {
|
||||
filename: "filename",
|
||||
|
@ -8,57 +7,53 @@ const fields = {
|
|||
filesize: "filesize",
|
||||
} as const satisfies Record<string, string>;
|
||||
|
||||
const labels = {
|
||||
singular: "Library Item Thumbnail",
|
||||
plural: "Library Item Thumbnails",
|
||||
} as const satisfies { singular: string; plural: string };
|
||||
|
||||
export const LibraryItemThumbnails: CollectionConfig = {
|
||||
slug: collectionSlug(labels.plural),
|
||||
labels,
|
||||
typescript: { interface: labels.singular },
|
||||
defaultSort: fields.filename,
|
||||
admin: {
|
||||
useAsTitle: fields.filename,
|
||||
group: CollectionGroup.Media,
|
||||
export const LibraryItemThumbnails = buildCollectionConfig(
|
||||
{
|
||||
singular: "Library Item Thumbnail",
|
||||
plural: "Library Item Thumbnails",
|
||||
},
|
||||
|
||||
upload: {
|
||||
staticDir: `../uploads/${labels.plural}`,
|
||||
mimeTypes: ["image/*"],
|
||||
imageSizes: [
|
||||
{
|
||||
name: "og",
|
||||
height: 1024,
|
||||
width: 1024,
|
||||
fit: "contain",
|
||||
formatOptions: {
|
||||
format: "jpg",
|
||||
options: { progressive: true, mozjpeg: true, compressionLevel: 9, quality: 80 },
|
||||
({ labels }) => ({
|
||||
defaultSort: fields.filename,
|
||||
admin: {
|
||||
useAsTitle: fields.filename,
|
||||
group: CollectionGroup.Media,
|
||||
},
|
||||
upload: {
|
||||
staticDir: `../uploads/${labels.plural}`,
|
||||
mimeTypes: ["image/*"],
|
||||
imageSizes: [
|
||||
{
|
||||
name: "og",
|
||||
height: 1024,
|
||||
width: 1024,
|
||||
fit: "contain",
|
||||
formatOptions: {
|
||||
format: "jpg",
|
||||
options: { progressive: true, mozjpeg: true, compressionLevel: 9, quality: 80 },
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "medium",
|
||||
height: 1024,
|
||||
width: 1024,
|
||||
fit: "contain",
|
||||
formatOptions: {
|
||||
format: "webp",
|
||||
options: { effort: 6, quality: 80, alphaQuality: 80 },
|
||||
{
|
||||
name: "medium",
|
||||
height: 1024,
|
||||
width: 1024,
|
||||
fit: "contain",
|
||||
formatOptions: {
|
||||
format: "webp",
|
||||
options: { effort: 6, quality: 80, alphaQuality: 80 },
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "large",
|
||||
height: 2048,
|
||||
width: 2048,
|
||||
fit: "contain",
|
||||
formatOptions: {
|
||||
format: "webp",
|
||||
options: { effort: 6, quality: 80, alphaQuality: 80 },
|
||||
{
|
||||
name: "large",
|
||||
height: 2048,
|
||||
width: 2048,
|
||||
fit: "contain",
|
||||
formatOptions: {
|
||||
format: "webp",
|
||||
options: { effort: 6, quality: 80, alphaQuality: 80 },
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
fields: [],
|
||||
};
|
||||
],
|
||||
},
|
||||
fields: [],
|
||||
})
|
||||
);
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { CollectionConfig } from "payload/types";
|
||||
import {
|
||||
CollectionGroup,
|
||||
KeysTypes,
|
||||
|
@ -8,12 +7,12 @@ import {
|
|||
} from "../../constants";
|
||||
import { slugField } from "../../fields/slugField/slugField";
|
||||
import { imageField } from "../../fields/imageField/imageField";
|
||||
import { collectionSlug } from "../../utils/string";
|
||||
import { isDefined, isUndefined } from "../../utils/asserts";
|
||||
import { LibraryItemThumbnails } from "../LibraryItemThumbnails/LibraryItemThumbnails";
|
||||
import { LibraryItem } from "../../types/collections";
|
||||
import { Keys } from "../Keys/Keys";
|
||||
import { Languages } from "../Languages";
|
||||
import { buildVersionedCollectionConfig } from "../../utils/versionedCollectionConfig";
|
||||
|
||||
const fields = {
|
||||
status: "status",
|
||||
|
@ -42,11 +41,6 @@ const fields = {
|
|||
audioSubtype: "audioSubtype",
|
||||
} as const satisfies Record<string, string>;
|
||||
|
||||
const labels = {
|
||||
singular: "Library Item",
|
||||
plural: "Library Items",
|
||||
} as const satisfies { singular: string; plural: string };
|
||||
|
||||
const validateSizeValue = (value?: number) => {
|
||||
if (isDefined(value) && value <= 0) return "This value must be greater than 0";
|
||||
return true;
|
||||
|
@ -58,216 +52,220 @@ const validateRequiredSizeValue = (value?: number) => {
|
|||
return true;
|
||||
};
|
||||
|
||||
export const LibraryItems: CollectionConfig = {
|
||||
slug: collectionSlug(labels.plural),
|
||||
labels,
|
||||
typescript: { interface: labels.singular },
|
||||
defaultSort: fields.slug,
|
||||
admin: {
|
||||
useAsTitle: fields.slug,
|
||||
defaultColumns: [fields.slug, fields.thumbnail, fields.status],
|
||||
group: CollectionGroup.Collections,
|
||||
preview: (doc) => `https://accords-library.com/library/${doc.slug}`,
|
||||
export const LibraryItems = buildVersionedCollectionConfig(
|
||||
{
|
||||
singular: "Library Item",
|
||||
plural: "Library Items",
|
||||
},
|
||||
timestamps: true,
|
||||
versions: { drafts: { autosave: true } },
|
||||
fields: [
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
slugField({ name: fields.slug, admin: { width: "50%" } }),
|
||||
imageField({
|
||||
name: fields.thumbnail,
|
||||
relationTo: LibraryItemThumbnails.slug,
|
||||
admin: { width: "50%" },
|
||||
}),
|
||||
],
|
||||
() => ({
|
||||
defaultSort: fields.slug,
|
||||
admin: {
|
||||
useAsTitle: fields.slug,
|
||||
description:
|
||||
"A comprehensive list of all Yokoverse’s side materials (books, novellas, artbooks, \
|
||||
stage plays, manga, drama CDs, and comics).",
|
||||
defaultColumns: [fields.slug, fields.thumbnail, fields.status],
|
||||
group: CollectionGroup.Collections,
|
||||
preview: (doc) => `https://accords-library.com/library/${doc.slug}`,
|
||||
},
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
{ name: fields.pretitle, type: "text" },
|
||||
{ name: fields.title, type: "text", required: true },
|
||||
{ name: fields.subtitle, type: "text" },
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
{
|
||||
name: fields.rootItem,
|
||||
type: "checkbox",
|
||||
required: true,
|
||||
defaultValue: true,
|
||||
admin: {
|
||||
description: "Only items that can be sold separetely should be root items.",
|
||||
width: "25%",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: fields.primary,
|
||||
type: "checkbox",
|
||||
required: true,
|
||||
defaultValue: true,
|
||||
admin: {
|
||||
description:
|
||||
"A primary item is an official item that focuses primarly on one or more of our Categories.",
|
||||
width: "25%",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: fields.digital,
|
||||
type: "checkbox",
|
||||
required: true,
|
||||
defaultValue: false,
|
||||
admin: {
|
||||
description:
|
||||
"The item is the digital version of another item, or the item is sold only digitally.",
|
||||
width: "25%",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: fields.downloadable,
|
||||
type: "checkbox",
|
||||
required: true,
|
||||
defaultValue: false,
|
||||
admin: {
|
||||
description: "Are the scans available for download?",
|
||||
width: "25%",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "size",
|
||||
type: "group",
|
||||
admin: { condition: (data) => !data.digital },
|
||||
fields: [
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
{
|
||||
name: fields.width,
|
||||
type: "number",
|
||||
validate: validateRequiredSizeValue,
|
||||
admin: { step: 1, width: "33%", description: "in mm." },
|
||||
},
|
||||
{
|
||||
name: fields.height,
|
||||
type: "number",
|
||||
validate: validateRequiredSizeValue,
|
||||
admin: { step: 1, width: "33%", description: "in mm." },
|
||||
},
|
||||
{
|
||||
name: fields.thickness,
|
||||
type: "number",
|
||||
validate: validateSizeValue,
|
||||
admin: { step: 1, width: "33%", description: "in mm." },
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: fields.itemType,
|
||||
type: "radio",
|
||||
options: Object.entries(LibraryItemsTypes).map(([value, label]) => ({ label, value })),
|
||||
admin: {
|
||||
layout: "horizontal",
|
||||
fields: [
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
slugField({ name: fields.slug, admin: { width: "50%" } }),
|
||||
imageField({
|
||||
name: fields.thumbnail,
|
||||
relationTo: LibraryItemThumbnails.slug,
|
||||
admin: { width: "50%" },
|
||||
}),
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: fields.textual,
|
||||
type: "group",
|
||||
admin: {
|
||||
condition: (data: Partial<LibraryItem>) => data.itemType === LibraryItemsTypes.Textual,
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
{ name: fields.pretitle, type: "text" },
|
||||
{ name: fields.title, type: "text", required: true },
|
||||
{ name: fields.subtitle, type: "text" },
|
||||
],
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
{
|
||||
name: fields.textualSubtype,
|
||||
label: "Subtype",
|
||||
type: "relationship",
|
||||
relationTo: [Keys.slug],
|
||||
filterOptions: { type: { equals: KeysTypes.LibraryTextual } },
|
||||
hasMany: true,
|
||||
admin: { allowCreate: false, width: "50%" },
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
{
|
||||
name: fields.rootItem,
|
||||
type: "checkbox",
|
||||
required: true,
|
||||
defaultValue: true,
|
||||
admin: {
|
||||
description: "Only items that can be sold separetely should be root items.",
|
||||
width: "25%",
|
||||
},
|
||||
{
|
||||
name: fields.textualLanguages,
|
||||
type: "relationship",
|
||||
relationTo: [Languages.slug],
|
||||
hasMany: true,
|
||||
admin: { allowCreate: false, width: "50%" },
|
||||
},
|
||||
{
|
||||
name: fields.primary,
|
||||
type: "checkbox",
|
||||
required: true,
|
||||
defaultValue: true,
|
||||
admin: {
|
||||
description:
|
||||
"A primary item is an official item that focuses primarly on one or more of our Categories.",
|
||||
width: "25%",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
{ name: fields.textualPageCount, type: "number", min: 1, admin: { width: "33%" } },
|
||||
{
|
||||
name: fields.textualBindingType,
|
||||
label: "Binding Type",
|
||||
type: "radio",
|
||||
options: Object.entries(LibraryItemsTextualBindingTypes).map(([value, label]) => ({
|
||||
label,
|
||||
value,
|
||||
})),
|
||||
admin: {
|
||||
layout: "horizontal",
|
||||
width: "33%",
|
||||
},
|
||||
{
|
||||
name: fields.digital,
|
||||
type: "checkbox",
|
||||
required: true,
|
||||
defaultValue: false,
|
||||
admin: {
|
||||
description:
|
||||
"The item is the digital version of another item, or the item is sold only digitally.",
|
||||
width: "25%",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: fields.downloadable,
|
||||
type: "checkbox",
|
||||
required: true,
|
||||
defaultValue: false,
|
||||
admin: {
|
||||
description: "Are the scans available for download?",
|
||||
width: "25%",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "size",
|
||||
type: "group",
|
||||
admin: { condition: (data) => !data.digital },
|
||||
fields: [
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
{
|
||||
name: fields.width,
|
||||
type: "number",
|
||||
validate: validateRequiredSizeValue,
|
||||
admin: { step: 1, width: "33%", description: "in mm." },
|
||||
},
|
||||
},
|
||||
{
|
||||
name: fields.textualPageOrder,
|
||||
label: "Page Order",
|
||||
type: "radio",
|
||||
options: Object.entries(LibraryItemsTextualPageOrders).map(([value, label]) => ({
|
||||
label,
|
||||
value,
|
||||
})),
|
||||
admin: {
|
||||
layout: "horizontal",
|
||||
width: "33%",
|
||||
{
|
||||
name: fields.height,
|
||||
type: "number",
|
||||
validate: validateRequiredSizeValue,
|
||||
admin: { step: 1, width: "33%", description: "in mm." },
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: fields.audio,
|
||||
type: "group",
|
||||
admin: {
|
||||
condition: (data: Partial<LibraryItem>) => data.itemType === LibraryItemsTypes.Audio,
|
||||
{
|
||||
name: fields.thickness,
|
||||
type: "number",
|
||||
validate: validateSizeValue,
|
||||
admin: { step: 1, width: "33%", description: "in mm." },
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
{
|
||||
name: fields.audioSubtype,
|
||||
label: "Subtype",
|
||||
type: "relationship",
|
||||
relationTo: [Keys.slug],
|
||||
filterOptions: { type: { equals: KeysTypes.LibraryAudio } },
|
||||
hasMany: true,
|
||||
admin: { allowCreate: false, width: "50%" },
|
||||
},
|
||||
],
|
||||
{
|
||||
name: fields.itemType,
|
||||
type: "radio",
|
||||
options: Object.entries(LibraryItemsTypes).map(([value, label]) => ({ label, value })),
|
||||
admin: {
|
||||
layout: "horizontal",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: fields.releaseDate,
|
||||
type: "date",
|
||||
admin: {
|
||||
date: { pickerAppearance: "dayOnly", displayFormat: "yyyy-MM-dd" },
|
||||
position: "sidebar",
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
{
|
||||
name: fields.textual,
|
||||
type: "group",
|
||||
admin: {
|
||||
condition: (data: Partial<LibraryItem>) => data.itemType === LibraryItemsTypes.Textual,
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
{
|
||||
name: fields.textualSubtype,
|
||||
label: "Subtype",
|
||||
type: "relationship",
|
||||
relationTo: [Keys.slug],
|
||||
filterOptions: { type: { equals: KeysTypes.LibraryTextual } },
|
||||
hasMany: true,
|
||||
admin: { allowCreate: false, width: "50%" },
|
||||
},
|
||||
{
|
||||
name: fields.textualLanguages,
|
||||
type: "relationship",
|
||||
relationTo: [Languages.slug],
|
||||
hasMany: true,
|
||||
admin: { allowCreate: false, width: "50%" },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
{ name: fields.textualPageCount, type: "number", min: 1, admin: { width: "33%" } },
|
||||
{
|
||||
name: fields.textualBindingType,
|
||||
label: "Binding Type",
|
||||
type: "radio",
|
||||
options: Object.entries(LibraryItemsTextualBindingTypes).map(([value, label]) => ({
|
||||
label,
|
||||
value,
|
||||
})),
|
||||
admin: {
|
||||
layout: "horizontal",
|
||||
width: "33%",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: fields.textualPageOrder,
|
||||
label: "Page Order",
|
||||
type: "radio",
|
||||
options: Object.entries(LibraryItemsTextualPageOrders).map(([value, label]) => ({
|
||||
label,
|
||||
value,
|
||||
})),
|
||||
admin: {
|
||||
layout: "horizontal",
|
||||
width: "33%",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: fields.audio,
|
||||
type: "group",
|
||||
admin: {
|
||||
condition: (data: Partial<LibraryItem>) => data.itemType === LibraryItemsTypes.Audio,
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
{
|
||||
name: fields.audioSubtype,
|
||||
label: "Subtype",
|
||||
type: "relationship",
|
||||
relationTo: [Keys.slug],
|
||||
filterOptions: { type: { equals: KeysTypes.LibraryAudio } },
|
||||
hasMany: true,
|
||||
admin: { allowCreate: false, width: "50%" },
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: fields.releaseDate,
|
||||
type: "date",
|
||||
admin: {
|
||||
date: { pickerAppearance: "dayOnly", displayFormat: "yyyy-MM-dd" },
|
||||
position: "sidebar",
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
);
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { CollectionConfig } from "payload/types";
|
||||
import { CollectionGroup } from "../../constants";
|
||||
import { collectionSlug } from "../../utils/string";
|
||||
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
||||
|
||||
const fields = {
|
||||
filename: "filename",
|
||||
|
@ -8,45 +7,41 @@ const fields = {
|
|||
filesize: "filesize",
|
||||
} as const satisfies Record<string, string>;
|
||||
|
||||
const labels = {
|
||||
singular: "Post Thumbnail",
|
||||
plural: "Post Thumbnails",
|
||||
} as const satisfies { singular: string; plural: string };
|
||||
|
||||
export const PostThumbnails: CollectionConfig = {
|
||||
slug: collectionSlug(labels.plural),
|
||||
labels,
|
||||
typescript: { interface: labels.singular },
|
||||
defaultSort: fields.filename,
|
||||
admin: {
|
||||
useAsTitle: fields.filename,
|
||||
group: CollectionGroup.Media,
|
||||
export const PostThumbnails = buildCollectionConfig(
|
||||
{
|
||||
singular: "Post Thumbnail",
|
||||
plural: "Post Thumbnails",
|
||||
},
|
||||
|
||||
upload: {
|
||||
staticDir: `../uploads/${labels.plural}`,
|
||||
mimeTypes: ["image/*"],
|
||||
imageSizes: [
|
||||
{
|
||||
name: "og",
|
||||
height: 750,
|
||||
width: 1125,
|
||||
formatOptions: {
|
||||
format: "jpg",
|
||||
options: { progressive: true, mozjpeg: true, compressionLevel: 9, quality: 80 },
|
||||
({ labels }) => ({
|
||||
defaultSort: fields.filename,
|
||||
admin: {
|
||||
useAsTitle: fields.filename,
|
||||
group: CollectionGroup.Media,
|
||||
},
|
||||
upload: {
|
||||
staticDir: `../uploads/${labels.plural}`,
|
||||
mimeTypes: ["image/*"],
|
||||
imageSizes: [
|
||||
{
|
||||
name: "og",
|
||||
height: 750,
|
||||
width: 1125,
|
||||
formatOptions: {
|
||||
format: "jpg",
|
||||
options: { progressive: true, mozjpeg: true, compressionLevel: 9, quality: 80 },
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "medium",
|
||||
height: 1000,
|
||||
width: 1500,
|
||||
formatOptions: {
|
||||
format: "webp",
|
||||
options: { effort: 6, quality: 80, alphaQuality: 80 },
|
||||
{
|
||||
name: "medium",
|
||||
height: 1000,
|
||||
width: 1500,
|
||||
formatOptions: {
|
||||
format: "webp",
|
||||
options: { effort: 6, quality: 80, alphaQuality: 80 },
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
fields: [],
|
||||
};
|
||||
],
|
||||
},
|
||||
fields: [],
|
||||
})
|
||||
);
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { CollectionConfig } from "payload/types";
|
||||
import { slugField } from "../../fields/slugField/slugField";
|
||||
import { imageField } from "../../fields/imageField/imageField";
|
||||
import { CollectionGroup, KeysTypes } from "../../constants";
|
||||
|
@ -7,8 +6,8 @@ import { localizedFields } from "../../fields/translatedFields/translatedFields"
|
|||
import { isDefined, isUndefined } from "../../utils/asserts";
|
||||
import { removeTranslatorsForTranscripts } from "./hooks/beforeValidate";
|
||||
import { Keys } from "../Keys/Keys";
|
||||
import { collectionSlug } from "../../utils/string";
|
||||
import { PostThumbnails } from "../PostThumbnails/PostThumbnails";
|
||||
import { buildVersionedCollectionConfig } from "../../utils/versionedCollectionConfig";
|
||||
|
||||
const fields = {
|
||||
slug: "slug",
|
||||
|
@ -26,133 +25,135 @@ const fields = {
|
|||
proofreaders: "proofreaders",
|
||||
} as const satisfies Record<string, string>;
|
||||
|
||||
const labels = {
|
||||
singular: "Post",
|
||||
plural: "Posts",
|
||||
} as const satisfies { singular: string; plural: string };
|
||||
|
||||
export const Posts: CollectionConfig = {
|
||||
slug: collectionSlug(labels.plural),
|
||||
labels,
|
||||
typescript: { interface: labels.singular },
|
||||
defaultSort: fields.slug,
|
||||
admin: {
|
||||
useAsTitle: fields.slug,
|
||||
defaultColumns: [fields.slug, fields.thumbnail, fields.categories],
|
||||
group: CollectionGroup.Collections,
|
||||
preview: (doc) => `https://accords-library.com/news/${doc.slug}`,
|
||||
export const Posts = buildVersionedCollectionConfig(
|
||||
{
|
||||
singular: "Post",
|
||||
plural: "Posts",
|
||||
},
|
||||
hooks: {
|
||||
beforeValidate: [removeTranslatorsForTranscripts],
|
||||
},
|
||||
timestamps: true,
|
||||
versions: { drafts: { autosave: true } },
|
||||
fields: [
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
slugField({ name: fields.slug, admin: { width: "50%" } }),
|
||||
imageField({
|
||||
name: fields.thumbnail,
|
||||
relationTo: PostThumbnails.slug,
|
||||
admin: { width: "50%" },
|
||||
}),
|
||||
],
|
||||
() => ({
|
||||
defaultSort: fields.slug,
|
||||
admin: {
|
||||
useAsTitle: fields.slug,
|
||||
description:
|
||||
"News articles written by our Recorders! Here you will find announcements about \
|
||||
new merch/items releases, guides, theories, unboxings, showcases...",
|
||||
defaultColumns: [fields.slug, fields.thumbnail, fields.categories],
|
||||
group: CollectionGroup.Collections,
|
||||
preview: (doc) => `https://accords-library.com/news/${doc.slug}`,
|
||||
},
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
{
|
||||
name: fields.authors,
|
||||
type: "relationship",
|
||||
relationTo: [Recorders.slug],
|
||||
required: true,
|
||||
minRows: 1,
|
||||
hasMany: true,
|
||||
admin: { width: "35%" },
|
||||
},
|
||||
{
|
||||
name: fields.categories,
|
||||
type: "relationship",
|
||||
relationTo: [Keys.slug],
|
||||
filterOptions: { type: { equals: KeysTypes.Categories } },
|
||||
hasMany: true,
|
||||
admin: { allowCreate: false, width: "35%" },
|
||||
},
|
||||
],
|
||||
hooks: {
|
||||
beforeValidate: [removeTranslatorsForTranscripts],
|
||||
},
|
||||
localizedFields({
|
||||
name: fields.translations,
|
||||
admin: { useAsTitle: fields.title, hasSourceLanguage: true },
|
||||
required: true,
|
||||
minRows: 1,
|
||||
fields: [
|
||||
{ name: fields.title, type: "text", required: true },
|
||||
{ name: fields.summary, type: "textarea" },
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
{
|
||||
name: fields.translators,
|
||||
type: "relationship",
|
||||
relationTo: Recorders.slug,
|
||||
hasMany: true,
|
||||
admin: {
|
||||
condition: (_, siblingData) => {
|
||||
fields: [
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
slugField({ name: fields.slug, admin: { width: "50%" } }),
|
||||
imageField({
|
||||
name: fields.thumbnail,
|
||||
relationTo: PostThumbnails.slug,
|
||||
admin: { width: "50%" },
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
{
|
||||
name: fields.authors,
|
||||
type: "relationship",
|
||||
relationTo: [Recorders.slug],
|
||||
required: true,
|
||||
minRows: 1,
|
||||
hasMany: true,
|
||||
admin: { width: "35%" },
|
||||
},
|
||||
{
|
||||
name: fields.categories,
|
||||
type: "relationship",
|
||||
relationTo: [Keys.slug],
|
||||
filterOptions: { type: { equals: KeysTypes.Categories } },
|
||||
hasMany: true,
|
||||
admin: { allowCreate: false, width: "35%" },
|
||||
},
|
||||
],
|
||||
},
|
||||
localizedFields({
|
||||
name: fields.translations,
|
||||
admin: { useAsTitle: fields.title, hasSourceLanguage: true },
|
||||
required: true,
|
||||
minRows: 1,
|
||||
fields: [
|
||||
{ name: fields.title, type: "text", required: true },
|
||||
{ name: fields.summary, type: "textarea" },
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
{
|
||||
name: fields.translators,
|
||||
type: "relationship",
|
||||
relationTo: Recorders.slug,
|
||||
hasMany: true,
|
||||
admin: {
|
||||
condition: (_, siblingData) => {
|
||||
if (
|
||||
isUndefined(siblingData.language) ||
|
||||
isUndefined(siblingData.sourceLanguage)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return siblingData.language !== siblingData.sourceLanguage;
|
||||
},
|
||||
width: "50%",
|
||||
},
|
||||
validate: (translators, { siblingData }) => {
|
||||
if (
|
||||
isUndefined(siblingData.language) ||
|
||||
isUndefined(siblingData.sourceLanguage)
|
||||
) {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
return siblingData.language !== siblingData.sourceLanguage;
|
||||
if (siblingData.language === siblingData.sourceLanguage) {
|
||||
return true;
|
||||
}
|
||||
if (isDefined(translators) && translators.length > 0) {
|
||||
return true;
|
||||
}
|
||||
return "This field is required when the language is different from the source language.";
|
||||
},
|
||||
width: "50%",
|
||||
},
|
||||
validate: (translators, { siblingData }) => {
|
||||
if (isUndefined(siblingData.language) || isUndefined(siblingData.sourceLanguage)) {
|
||||
return true;
|
||||
}
|
||||
if (siblingData.language === siblingData.sourceLanguage) {
|
||||
return true;
|
||||
}
|
||||
if (isDefined(translators) && translators.length > 0) {
|
||||
return true;
|
||||
}
|
||||
return "This field is required when the language is different from the source language.";
|
||||
{
|
||||
name: fields.proofreaders,
|
||||
type: "relationship",
|
||||
relationTo: Recorders.slug,
|
||||
hasMany: true,
|
||||
admin: { width: "50%" },
|
||||
},
|
||||
},
|
||||
{
|
||||
name: fields.proofreaders,
|
||||
type: "relationship",
|
||||
relationTo: Recorders.slug,
|
||||
hasMany: true,
|
||||
admin: { width: "50%" },
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
{ name: fields.content, type: "richText", admin: { hideGutter: true } },
|
||||
],
|
||||
}),
|
||||
{
|
||||
name: fields.publishedDate,
|
||||
type: "date",
|
||||
defaultValue: new Date().toISOString(),
|
||||
admin: {
|
||||
date: { pickerAppearance: "dayOnly", displayFormat: "yyyy-MM-dd" },
|
||||
position: "sidebar",
|
||||
},
|
||||
{ name: fields.content, type: "richText", admin: { hideGutter: true } },
|
||||
],
|
||||
}),
|
||||
{
|
||||
name: fields.publishedDate,
|
||||
type: "date",
|
||||
defaultValue: new Date().toISOString(),
|
||||
admin: {
|
||||
date: { pickerAppearance: "dayOnly", displayFormat: "yyyy-MM-dd" },
|
||||
position: "sidebar",
|
||||
required: true,
|
||||
},
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: fields.hidden,
|
||||
type: "checkbox",
|
||||
required: false,
|
||||
defaultValue: false,
|
||||
admin: {
|
||||
description: "If enabled, the post won't appear in the 'News' section",
|
||||
position: "sidebar",
|
||||
{
|
||||
name: fields.hidden,
|
||||
type: "checkbox",
|
||||
required: false,
|
||||
defaultValue: false,
|
||||
admin: {
|
||||
description: "If enabled, the post won't appear in the 'News' section",
|
||||
position: "sidebar",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
],
|
||||
})
|
||||
);
|
||||
|
|
|
@ -5,7 +5,7 @@ export const removeTranslatorsForTranscripts: CollectionBeforeValidateHook<Post>
|
|||
data: { translations, ...data },
|
||||
}) => ({
|
||||
...data,
|
||||
translations: translations.map(({ translators, ...translation }) => {
|
||||
translations: translations?.map(({ translators, ...translation }) => {
|
||||
if (translation.language === translation.sourceLanguage) {
|
||||
return { ...translation, translators: [] };
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { CollectionConfig } from "payload/types";
|
||||
import { CollectionGroup } from "../../constants";
|
||||
import { collectionSlug } from "../../utils/string";
|
||||
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
||||
|
||||
const fields = {
|
||||
filename: "filename",
|
||||
|
@ -8,46 +7,42 @@ const fields = {
|
|||
filesize: "filesize",
|
||||
} as const satisfies Record<string, string>;
|
||||
|
||||
const labels = {
|
||||
singular: "Recorder Thumbnail",
|
||||
plural: "Recorder Thumbnails",
|
||||
} as const satisfies { singular: string; plural: string };
|
||||
|
||||
export const RecorderThumbnails: CollectionConfig = {
|
||||
slug: collectionSlug(labels.plural),
|
||||
labels,
|
||||
typescript: { interface: labels.singular },
|
||||
defaultSort: fields.filename,
|
||||
admin: {
|
||||
useAsTitle: fields.filename,
|
||||
group: CollectionGroup.Media,
|
||||
export const RecorderThumbnails = buildCollectionConfig(
|
||||
{
|
||||
singular: "Recorder Thumbnail",
|
||||
plural: "Recorder Thumbnails",
|
||||
},
|
||||
|
||||
upload: {
|
||||
staticDir: `../uploads/${labels.plural}`,
|
||||
adminThumbnail: "small",
|
||||
mimeTypes: ["image/*"],
|
||||
imageSizes: [
|
||||
{
|
||||
name: "og",
|
||||
height: 256,
|
||||
width: 256,
|
||||
formatOptions: {
|
||||
format: "jpg",
|
||||
options: { progressive: true, mozjpeg: true, compressionLevel: 9, quality: 80 },
|
||||
({ labels }) => ({
|
||||
defaultSort: fields.filename,
|
||||
admin: {
|
||||
useAsTitle: fields.filename,
|
||||
group: CollectionGroup.Media,
|
||||
},
|
||||
upload: {
|
||||
staticDir: `../uploads/${labels.plural}`,
|
||||
adminThumbnail: "small",
|
||||
mimeTypes: ["image/*"],
|
||||
imageSizes: [
|
||||
{
|
||||
name: "og",
|
||||
height: 256,
|
||||
width: 256,
|
||||
formatOptions: {
|
||||
format: "jpg",
|
||||
options: { progressive: true, mozjpeg: true, compressionLevel: 9, quality: 80 },
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "small",
|
||||
height: 128,
|
||||
width: 128,
|
||||
formatOptions: {
|
||||
format: "webp",
|
||||
options: { effort: 6, quality: 80, alphaQuality: 80 },
|
||||
{
|
||||
name: "small",
|
||||
height: 128,
|
||||
width: 128,
|
||||
formatOptions: {
|
||||
format: "webp",
|
||||
options: { effort: 6, quality: 80, alphaQuality: 80 },
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
fields: [],
|
||||
};
|
||||
],
|
||||
},
|
||||
fields: [],
|
||||
})
|
||||
);
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
import { CollectionConfig } from "payload/types";
|
||||
import { localizedFields } from "../../fields/translatedFields/translatedFields";
|
||||
import { Languages } from "../Languages";
|
||||
import { beforeDuplicate } from "./hooks/beforeDuplicate";
|
||||
import { CollectionGroup } from "../../constants";
|
||||
import { collectionSlug } from "../../utils/string";
|
||||
import { RecorderThumbnails } from "../RecorderThumbnails/RecorderThumbnails";
|
||||
import { imageField } from "../../fields/imageField/imageField";
|
||||
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
||||
|
||||
const fields = {
|
||||
username: "username",
|
||||
|
@ -16,79 +15,77 @@ const fields = {
|
|||
avatar: "avatar",
|
||||
} as const satisfies Record<string, string>;
|
||||
|
||||
const labels = {
|
||||
singular: "Recorder",
|
||||
plural: "Recorders",
|
||||
} as const satisfies { singular: string; plural: string };
|
||||
|
||||
export const Recorders: CollectionConfig = {
|
||||
slug: collectionSlug(labels.plural),
|
||||
labels,
|
||||
typescript: { interface: labels.singular },
|
||||
defaultSort: fields.username,
|
||||
admin: {
|
||||
useAsTitle: fields.username,
|
||||
hooks: { beforeDuplicate },
|
||||
description:
|
||||
"Recorders are contributors of the Accord's Library project. Create a Recorder here to be able to credit them in other collections",
|
||||
defaultColumns: [
|
||||
fields.username,
|
||||
fields.avatar,
|
||||
fields.anonymize,
|
||||
fields.biographies,
|
||||
fields.languages,
|
||||
],
|
||||
group: CollectionGroup.Meta,
|
||||
export const Recorders = buildCollectionConfig(
|
||||
{
|
||||
singular: "Recorder",
|
||||
plural: "Recorders",
|
||||
},
|
||||
timestamps: false,
|
||||
fields: [
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
{
|
||||
name: fields.username,
|
||||
type: "text",
|
||||
unique: true,
|
||||
required: true,
|
||||
admin: { description: "The username must be unique", width: "33%" },
|
||||
},
|
||||
imageField({
|
||||
name: fields.avatar,
|
||||
relationTo: RecorderThumbnails.slug,
|
||||
admin: { width: "66%" },
|
||||
}),
|
||||
() => ({
|
||||
defaultSort: fields.username,
|
||||
admin: {
|
||||
useAsTitle: fields.username,
|
||||
hooks: { beforeDuplicate },
|
||||
description:
|
||||
"Recorders are contributors of the Accord's Library project. Create a Recorder here to be able to credit them in other collections",
|
||||
defaultColumns: [
|
||||
fields.username,
|
||||
fields.avatar,
|
||||
fields.anonymize,
|
||||
fields.biographies,
|
||||
fields.languages,
|
||||
],
|
||||
group: CollectionGroup.Meta,
|
||||
},
|
||||
{
|
||||
name: fields.languages,
|
||||
type: "relationship",
|
||||
relationTo: Languages.slug,
|
||||
hasMany: true,
|
||||
admin: {
|
||||
allowCreate: false,
|
||||
description: "List of language(s) that this recorder is familiar with",
|
||||
timestamps: false,
|
||||
fields: [
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
{
|
||||
name: fields.username,
|
||||
type: "text",
|
||||
unique: true,
|
||||
required: true,
|
||||
admin: { description: "The username must be unique", width: "33%" },
|
||||
},
|
||||
imageField({
|
||||
name: fields.avatar,
|
||||
relationTo: RecorderThumbnails.slug,
|
||||
admin: { width: "66%" },
|
||||
}),
|
||||
],
|
||||
},
|
||||
},
|
||||
localizedFields({
|
||||
name: fields.biographies,
|
||||
interfaceName: "RecorderBiographies",
|
||||
admin: {
|
||||
useAsTitle: fields.biography,
|
||||
description:
|
||||
"A short personal description about you or your involvement with this project or the franchise",
|
||||
{
|
||||
name: fields.languages,
|
||||
type: "relationship",
|
||||
relationTo: Languages.slug,
|
||||
hasMany: true,
|
||||
admin: {
|
||||
allowCreate: false,
|
||||
description: "List of language(s) that this recorder is familiar with",
|
||||
},
|
||||
},
|
||||
fields: [{ name: fields.biography, type: "textarea" }],
|
||||
}),
|
||||
{
|
||||
name: fields.anonymize,
|
||||
type: "checkbox",
|
||||
required: true,
|
||||
defaultValue: false,
|
||||
admin: {
|
||||
description:
|
||||
"If enabled, this recorder's username will not be made public. Instead they will be referred to as 'Recorder#0000' where '0000' is a random four digit number",
|
||||
position: "sidebar",
|
||||
localizedFields({
|
||||
name: fields.biographies,
|
||||
interfaceName: "RecorderBiographies",
|
||||
admin: {
|
||||
useAsTitle: fields.biography,
|
||||
description:
|
||||
"A short personal description about you or your involvement with this project or the franchise",
|
||||
},
|
||||
fields: [{ name: fields.biography, type: "textarea" }],
|
||||
}),
|
||||
{
|
||||
name: fields.anonymize,
|
||||
type: "checkbox",
|
||||
required: true,
|
||||
defaultValue: false,
|
||||
admin: {
|
||||
description:
|
||||
"If enabled, this recorder's username will not be made public. Instead they will be referred to as 'Recorder#0000' where '0000' is a random four digit number",
|
||||
position: "sidebar",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
],
|
||||
})
|
||||
);
|
||||
|
|
|
@ -1,27 +1,60 @@
|
|||
import { CollectionConfig } from "payload/types";
|
||||
import { CollectionGroup } from "../constants";
|
||||
import { collectionSlug } from "../utils/string";
|
||||
import { CollectionGroup, UserRoles } from "../constants";
|
||||
import { Recorders } from "./Recorders/Recorders";
|
||||
import { buildCollectionConfig } from "../utils/collectionConfig";
|
||||
|
||||
const fields = {
|
||||
recorder: "recorder",
|
||||
name: "name",
|
||||
email: "email",
|
||||
role: "role",
|
||||
} as const satisfies Record<string, string>;
|
||||
|
||||
const labels = {
|
||||
singular: "User",
|
||||
plural: "Users",
|
||||
} as const satisfies { singular: string; plural: string };
|
||||
|
||||
export const Users: CollectionConfig = {
|
||||
slug: collectionSlug(labels.plural),
|
||||
auth: true,
|
||||
labels,
|
||||
typescript: { interface: labels.singular },
|
||||
defaultSort: fields.email,
|
||||
admin: {
|
||||
useAsTitle: fields.email,
|
||||
defaultColumns: [fields.email],
|
||||
group: CollectionGroup.Administration,
|
||||
export const Users = buildCollectionConfig(
|
||||
{
|
||||
singular: "User",
|
||||
plural: "Users",
|
||||
},
|
||||
timestamps: false,
|
||||
fields: [],
|
||||
};
|
||||
() => ({
|
||||
auth: true,
|
||||
defaultSort: fields.recorder,
|
||||
admin: {
|
||||
useAsTitle: fields.name,
|
||||
defaultColumns: [fields.recorder, fields.name, fields.email, fields.role],
|
||||
group: CollectionGroup.Administration,
|
||||
},
|
||||
timestamps: false,
|
||||
fields: [
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
{
|
||||
name: fields.recorder,
|
||||
type: "relationship",
|
||||
relationTo: Recorders.slug,
|
||||
required: true,
|
||||
admin: { width: "33%" },
|
||||
},
|
||||
{
|
||||
name: fields.name,
|
||||
type: "text",
|
||||
required: true,
|
||||
unique: true,
|
||||
admin: { width: "33%" },
|
||||
},
|
||||
{
|
||||
name: fields.role,
|
||||
required: true,
|
||||
defaultValue: [UserRoles.Recorder],
|
||||
type: "select",
|
||||
hasMany: true,
|
||||
options: Object.entries(UserRoles).map(([value, label]) => ({
|
||||
label,
|
||||
value,
|
||||
})),
|
||||
admin: { width: "33%" },
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
})
|
||||
);
|
||||
|
|
|
@ -42,3 +42,8 @@ export enum LibraryItemsTextualPageOrders {
|
|||
LeftToRight = "Left to right",
|
||||
RightToLeft = "Right to left",
|
||||
}
|
||||
|
||||
export enum UserRoles {
|
||||
Admin = "Admin",
|
||||
Recorder = "Recorder",
|
||||
}
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
import { Props } from "payload/components/views/Cell";
|
||||
import { useState, useEffect } from "react";
|
||||
import React from "react";
|
||||
import { isUndefined } from "../../utils/asserts";
|
||||
|
||||
export const Cell = ({ cellData, field }: Props): JSX.Element => {
|
||||
const [imageURL, setImageURL] = useState<string>();
|
||||
useEffect(() => {
|
||||
const fetchUrl = async () => {
|
||||
if (isUndefined(cellData)) return;
|
||||
if (typeof cellData !== "string") return;
|
||||
if (field.type !== "upload") return;
|
||||
const result = await (await fetch(`/api/${field.relationTo}/${cellData}`)).json();
|
||||
|
|
|
@ -44,10 +44,11 @@ export const localizedFields = ({
|
|||
components: {
|
||||
Cell: ({ cellData }) =>
|
||||
Cell({
|
||||
cellData: cellData.map((row) => ({
|
||||
language: row.language,
|
||||
title: isDefined(useAsTitle) ? row[useAsTitle] : undefined,
|
||||
})),
|
||||
cellData:
|
||||
cellData?.map((row) => ({
|
||||
language: row.language,
|
||||
title: isDefined(useAsTitle) ? row[useAsTitle] : undefined,
|
||||
})) ?? [],
|
||||
}),
|
||||
RowLabel: ({ data }) =>
|
||||
RowLabel({
|
||||
|
|
|
@ -93,6 +93,7 @@ export interface LibraryItem {
|
|||
}[];
|
||||
};
|
||||
releaseDate?: string;
|
||||
lastModifiedBy: string | User;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
_status?: "draft" | "published";
|
||||
|
@ -154,6 +155,57 @@ export interface Language {
|
|||
id: string;
|
||||
name: string;
|
||||
}
|
||||
export interface User {
|
||||
id: string;
|
||||
recorder: string | Recorder;
|
||||
name: string;
|
||||
role: ("Admin" | "Recorder")[];
|
||||
email: string;
|
||||
resetPasswordToken?: string;
|
||||
resetPasswordExpiration?: string;
|
||||
salt?: string;
|
||||
hash?: string;
|
||||
loginAttempts?: number;
|
||||
lockUntil?: string;
|
||||
password?: string;
|
||||
}
|
||||
export interface Recorder {
|
||||
id: string;
|
||||
username: string;
|
||||
avatar?: string | RecorderThumbnail;
|
||||
languages?: string[] | Language[];
|
||||
biographies?: RecorderBiographies;
|
||||
anonymize: boolean;
|
||||
}
|
||||
export interface RecorderThumbnail {
|
||||
id: string;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
url?: string;
|
||||
filename?: string;
|
||||
mimeType?: string;
|
||||
filesize?: number;
|
||||
width?: number;
|
||||
height?: number;
|
||||
sizes?: {
|
||||
og?: {
|
||||
url?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
mimeType?: string;
|
||||
filesize?: number;
|
||||
filename?: string;
|
||||
};
|
||||
small?: {
|
||||
url?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
mimeType?: string;
|
||||
filesize?: number;
|
||||
filename?: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
export interface Content {
|
||||
id: string;
|
||||
slug: string;
|
||||
|
@ -188,6 +240,7 @@ export interface Content {
|
|||
audio?: string | File;
|
||||
id?: string;
|
||||
}[];
|
||||
lastModifiedBy: string | User;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
_status?: "draft" | "published";
|
||||
|
@ -221,43 +274,6 @@ export interface ContentThumbnail {
|
|||
};
|
||||
};
|
||||
}
|
||||
export interface Recorder {
|
||||
id: string;
|
||||
username: string;
|
||||
avatar?: string | RecorderThumbnail;
|
||||
languages?: string[] | Language[];
|
||||
biographies?: RecorderBiographies;
|
||||
anonymize: boolean;
|
||||
}
|
||||
export interface RecorderThumbnail {
|
||||
id: string;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
url?: string;
|
||||
filename?: string;
|
||||
mimeType?: string;
|
||||
filesize?: number;
|
||||
width?: number;
|
||||
height?: number;
|
||||
sizes?: {
|
||||
og?: {
|
||||
url?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
mimeType?: string;
|
||||
filesize?: number;
|
||||
filename?: string;
|
||||
};
|
||||
small?: {
|
||||
url?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
mimeType?: string;
|
||||
filesize?: number;
|
||||
filename?: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
export interface TextBlock {
|
||||
content: {
|
||||
[k: string]: unknown;
|
||||
|
@ -511,6 +527,7 @@ export interface Post {
|
|||
}[];
|
||||
publishedDate: string;
|
||||
hidden?: boolean;
|
||||
lastModifiedBy: string | User;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
_status?: "draft" | "published";
|
||||
|
@ -544,14 +561,3 @@ export interface PostThumbnail {
|
|||
};
|
||||
};
|
||||
}
|
||||
export interface User {
|
||||
id: string;
|
||||
email: string;
|
||||
resetPasswordToken?: string;
|
||||
resetPasswordExpiration?: string;
|
||||
salt?: string;
|
||||
hash?: string;
|
||||
loginAttempts?: number;
|
||||
lockUntil?: string;
|
||||
password?: string;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
import { CollectionConfig } from "payload/types";
|
||||
import slugify from "slugify";
|
||||
|
||||
export type BuildCollectionConfig = Omit<CollectionConfig, "slug" | "typescript" | "labels">;
|
||||
|
||||
export type GenerationFunctionProps = {
|
||||
labels: { singular: string; plural: string };
|
||||
slug: string;
|
||||
};
|
||||
|
||||
export const buildCollectionConfig = (
|
||||
labels: { singular: string; plural: string },
|
||||
generationFunction: (props: GenerationFunctionProps) => BuildCollectionConfig
|
||||
): CollectionConfig => {
|
||||
const slug = slugify(labels.plural, { lower: true, strict: true, trim: true });
|
||||
const config = generationFunction({ labels, slug });
|
||||
return {
|
||||
...config,
|
||||
slug,
|
||||
typescript: { interface: labels.singular },
|
||||
};
|
||||
};
|
|
@ -1,11 +1,7 @@
|
|||
import ISO6391 from "iso-639-1";
|
||||
import slugify from "slugify";
|
||||
|
||||
export const shortenEllipsis = (text: string, length: number): string =>
|
||||
text.length - 3 > length ? `${text.substring(0, length)}...` : text;
|
||||
|
||||
export const formatLanguageCode = (code: string): string =>
|
||||
ISO6391.validate(code) ? ISO6391.getName(code) : code;
|
||||
|
||||
export const collectionSlug = (text: string): string =>
|
||||
slugify(text, { lower: true, strict: true, trim: true });
|
||||
ISO6391.validate(code) ? ISO6391.getName(code) : code;
|
|
@ -0,0 +1,52 @@
|
|||
import { CollectionBeforeChangeHook, CollectionConfig, RelationshipField } from "payload/types";
|
||||
import { Users } from "../collections/Users";
|
||||
import {
|
||||
BuildCollectionConfig,
|
||||
GenerationFunctionProps,
|
||||
buildCollectionConfig,
|
||||
} from "./collectionConfig";
|
||||
|
||||
const fields = { lastModifiedBy: "lastModifiedBy" };
|
||||
|
||||
const beforeChangeLastModifiedBy: CollectionBeforeChangeHook = async ({
|
||||
data: { updatedBy, ...data },
|
||||
req,
|
||||
}) => {
|
||||
console.log(data, req.user);
|
||||
return {
|
||||
...data,
|
||||
[fields.lastModifiedBy]: req.user.id,
|
||||
};
|
||||
};
|
||||
|
||||
const lastModifiedByField = (): RelationshipField => ({
|
||||
name: fields.lastModifiedBy,
|
||||
type: "relationship",
|
||||
required: true,
|
||||
relationTo: Users.slug,
|
||||
admin: { readOnly: true, position: "sidebar" },
|
||||
});
|
||||
|
||||
type BuildVersionedCollectionConfig = Omit<BuildCollectionConfig, "timestamps" | "versions">;
|
||||
|
||||
export const buildVersionedCollectionConfig = (
|
||||
labels: { singular: string; plural: string },
|
||||
generationFunction: (props: GenerationFunctionProps) => BuildVersionedCollectionConfig
|
||||
): CollectionConfig => {
|
||||
const {
|
||||
hooks: { beforeChange, ...otherHooks } = {},
|
||||
fields,
|
||||
...otherParams
|
||||
} = buildCollectionConfig(labels, generationFunction);
|
||||
|
||||
return {
|
||||
...otherParams,
|
||||
timestamps: true,
|
||||
versions: { drafts: { autosave: { interval: 2000 } } },
|
||||
hooks: {
|
||||
...otherHooks,
|
||||
beforeChange: [...(beforeChange ?? []), beforeChangeLastModifiedBy],
|
||||
},
|
||||
fields: [...fields, lastModifiedByField()],
|
||||
};
|
||||
};
|
Loading…
Reference in New Issue