Access control
This commit is contained in:
parent
1cda674782
commit
e98fabfd0e
|
@ -21,6 +21,7 @@
|
|||
"devDependencies": {
|
||||
"@types/dotenv": "^8.2.0",
|
||||
"@types/express": "^4.17.9",
|
||||
"@types/react-router-dom": "^5.3.3",
|
||||
"copyfiles": "^2.4.1",
|
||||
"nodemon": "^2.0.6",
|
||||
"prettier": "^3.0.0",
|
||||
|
@ -2998,6 +2999,12 @@
|
|||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/history": {
|
||||
"version": "4.7.11",
|
||||
"resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.11.tgz",
|
||||
"integrity": "sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/html-minifier-terser": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz",
|
||||
|
@ -3098,6 +3105,27 @@
|
|||
"csstype": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/react-router": {
|
||||
"version": "5.1.20",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.20.tgz",
|
||||
"integrity": "sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/history": "^4.7.11",
|
||||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/react-router-dom": {
|
||||
"version": "5.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.3.3.tgz",
|
||||
"integrity": "sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/history": "^4.7.11",
|
||||
"@types/react": "*",
|
||||
"@types/react-router": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/react-transition-group": {
|
||||
"version": "4.4.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.6.tgz",
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
"devDependencies": {
|
||||
"@types/dotenv": "^8.2.0",
|
||||
"@types/express": "^4.17.9",
|
||||
"@types/react-router-dom": "^5.3.3",
|
||||
"copyfiles": "^2.4.1",
|
||||
"nodemon": "^2.0.6",
|
||||
"prettier": "^3.0.0",
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
import { Access } from "payload/config";
|
||||
import { Recorder } from "../../types/collections";
|
||||
import { RecordersRoles } from "../../constants";
|
||||
import { isUndefined } from "../../utils/asserts";
|
||||
|
||||
export const mustBeAdminOrSelf: Access = ({ req }) => {
|
||||
const user = req.user as Recorder | undefined;
|
||||
if (isUndefined(user)) return false;
|
||||
if (user.role.includes(RecordersRoles.Admin)) return true;
|
||||
return { id: { equals: user.id } };
|
||||
};
|
|
@ -0,0 +1,8 @@
|
|||
import { Recorder } from "../../types/collections";
|
||||
import { isUndefined } from "../../utils/asserts";
|
||||
|
||||
export const mustHaveAtLeastOneRole = ({ req }): boolean => {
|
||||
const user = req.user as Recorder | undefined;
|
||||
if (isUndefined(user)) return false;
|
||||
return user.role.length > 0;
|
||||
};
|
|
@ -0,0 +1,9 @@
|
|||
import { Recorder } from "../types/collections";
|
||||
import { RecordersRoles } from "../constants";
|
||||
import { isUndefined } from "../utils/asserts";
|
||||
|
||||
export const mustBeAdmin = ({ req }): boolean => {
|
||||
const user = req.user as Recorder | undefined;
|
||||
if (isUndefined(user)) return false;
|
||||
return user.role.includes(RecordersRoles.Admin);
|
||||
};
|
|
@ -22,6 +22,7 @@ export const ContentFolders = buildCollectionConfig(
|
|||
admin: {
|
||||
useAsTitle: fields.slug,
|
||||
defaultColumns: [fields.slug, fields.translations],
|
||||
disableDuplicate: true,
|
||||
group: CollectionGroup.Collections,
|
||||
},
|
||||
timestamps: false,
|
||||
|
|
|
@ -16,6 +16,7 @@ export const ContentThumbnails = buildCollectionConfig(
|
|||
defaultSort: fields.filename,
|
||||
admin: {
|
||||
useAsTitle: fields.filename,
|
||||
disableDuplicate: true,
|
||||
group: CollectionGroup.Media,
|
||||
},
|
||||
upload: {
|
||||
|
|
|
@ -9,6 +9,9 @@ import { fileField } from "../../fields/fileField/fileField";
|
|||
import { contentBlocks } from "./Blocks/blocks";
|
||||
import { ContentThumbnails } from "../ContentThumbnails/ContentThumbnails";
|
||||
import { buildVersionedCollectionConfig } from "../../utils/versionedCollectionConfig";
|
||||
import { beforeDuplicatePiping } from "../../hooks/beforeDuplicatePiping";
|
||||
import { beforeDuplicateUnpublish } from "../../hooks/beforeDuplicateUnpublish";
|
||||
import { beforeDuplicateAddCopyTo } from "../../hooks/beforeDuplicateAddCopyTo";
|
||||
|
||||
const fields = {
|
||||
slug: "slug",
|
||||
|
@ -53,6 +56,12 @@ export const Contents = buildVersionedCollectionConfig(
|
|||
fields.status,
|
||||
],
|
||||
group: CollectionGroup.Collections,
|
||||
hooks: {
|
||||
beforeDuplicate: beforeDuplicatePiping([
|
||||
beforeDuplicateUnpublish,
|
||||
beforeDuplicateAddCopyTo(fields.slug),
|
||||
]),
|
||||
},
|
||||
preview: (doc) => `https://accords-library.com/contents/${doc.slug}`,
|
||||
},
|
||||
fields: [
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
import { mustBeAdmin } from "../../accesses/mustBeAdmin";
|
||||
import { CollectionGroup } from "../../constants";
|
||||
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
||||
|
||||
const fields = {
|
||||
id: "id",
|
||||
} as const satisfies Record<string, string>;
|
||||
|
||||
export const Currencies = buildCollectionConfig(
|
||||
{
|
||||
singular: "Currency",
|
||||
plural: "Currencies",
|
||||
},
|
||||
() => ({
|
||||
defaultSort: fields.id,
|
||||
admin: {
|
||||
useAsTitle: fields.id,
|
||||
defaultColumns: [fields.id],
|
||||
disableDuplicate: true,
|
||||
group: CollectionGroup.Meta,
|
||||
},
|
||||
access: { create: mustBeAdmin, update: mustBeAdmin },
|
||||
timestamps: false,
|
||||
fields: [
|
||||
{
|
||||
name: fields.id,
|
||||
type: "text",
|
||||
unique: true,
|
||||
required: true,
|
||||
validate: (value) => {
|
||||
if (/^[A-Z]{3}$/g.test(value)) {
|
||||
return true;
|
||||
}
|
||||
return "The code must be a valid ISO 4217 currency code (e.g: EUR, CAD...)";
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
);
|
|
@ -15,6 +15,7 @@ export const Files = buildCollectionConfig(
|
|||
defaultSort: fields.filename,
|
||||
admin: {
|
||||
useAsTitle: fields.filename,
|
||||
disableDuplicate: true,
|
||||
group: CollectionGroup.Media,
|
||||
},
|
||||
fields: [
|
||||
|
|
|
@ -5,6 +5,9 @@ import { localizedFields } from "../../fields/translatedFields/translatedFields"
|
|||
import { Key } from "../../types/collections";
|
||||
import { isDefined } from "../../utils/asserts";
|
||||
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
||||
import { mustBeAdmin } from "../../accesses/mustBeAdmin";
|
||||
import { beforeDuplicateAddCopyTo } from "../../hooks/beforeDuplicateAddCopyTo";
|
||||
import { QuickFilters } from "../../components/QuickFilters";
|
||||
|
||||
const fields = {
|
||||
slug: "slug",
|
||||
|
@ -27,6 +30,26 @@ export const Keys: CollectionConfig = buildCollectionConfig(
|
|||
useAsTitle: fields.slug,
|
||||
defaultColumns: [fields.slug, fields.type, fields.translations],
|
||||
group: CollectionGroup.Meta,
|
||||
components: {
|
||||
BeforeListTable: [
|
||||
() =>
|
||||
QuickFilters({
|
||||
route: "/admin/collections/keys",
|
||||
filters: [
|
||||
{ label: "Wordings", filter: "where[type][equals]=Wordings" },
|
||||
{ label: "∅ English", filter: "where[translations.language][not_equals]=en" },
|
||||
{ label: "∅ French", filter: "where[translations.language][not_equals]=fr" },
|
||||
],
|
||||
}),
|
||||
],
|
||||
},
|
||||
hooks: {
|
||||
beforeDuplicate: beforeDuplicateAddCopyTo(fields.slug),
|
||||
},
|
||||
},
|
||||
access: {
|
||||
create: mustBeAdmin,
|
||||
delete: mustBeAdmin,
|
||||
},
|
||||
timestamps: false,
|
||||
versions: false,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { CollectionGroup } from "../constants";
|
||||
import { buildCollectionConfig } from "../utils/collectionConfig";
|
||||
import { mustBeAdmin } from "../../accesses/mustBeAdmin";
|
||||
import { CollectionGroup } from "../../constants";
|
||||
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
||||
|
||||
const fields = {
|
||||
id: "id",
|
||||
|
@ -16,8 +17,10 @@ export const Languages = buildCollectionConfig(
|
|||
admin: {
|
||||
useAsTitle: fields.name,
|
||||
defaultColumns: [fields.name, fields.id],
|
||||
disableDuplicate: true,
|
||||
group: CollectionGroup.Meta,
|
||||
},
|
||||
access: { create: mustBeAdmin, update: mustBeAdmin },
|
||||
timestamps: false,
|
||||
fields: [
|
||||
{
|
|
@ -16,6 +16,7 @@ export const LibraryItemThumbnails = buildCollectionConfig(
|
|||
defaultSort: fields.filename,
|
||||
admin: {
|
||||
useAsTitle: fields.filename,
|
||||
disableDuplicate: true,
|
||||
group: CollectionGroup.Media,
|
||||
},
|
||||
upload: {
|
||||
|
|
|
@ -11,8 +11,13 @@ 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 { Languages } from "../Languages/Languages";
|
||||
import { buildVersionedCollectionConfig } from "../../utils/versionedCollectionConfig";
|
||||
import { beforeDuplicateAddCopyTo } from "../../hooks/beforeDuplicateAddCopyTo";
|
||||
import { beforeDuplicateUnpublish } from "../../hooks/beforeDuplicateUnpublish";
|
||||
import { beforeDuplicatePiping } from "../../hooks/beforeDuplicatePiping";
|
||||
import { Currencies } from "../Currencies/Currencies";
|
||||
import { optionalGroupField } from "../../fields/optionalGroupField/optionalGroupField";
|
||||
|
||||
const fields = {
|
||||
status: "status",
|
||||
|
@ -29,6 +34,9 @@ const fields = {
|
|||
width: "width",
|
||||
height: "height",
|
||||
thickness: "thickness",
|
||||
price: "price",
|
||||
priceAmount: "amount",
|
||||
priceCurrency: "currency",
|
||||
releaseDate: "releaseDate",
|
||||
itemType: "itemType",
|
||||
textual: "textual",
|
||||
|
@ -39,19 +47,24 @@ const fields = {
|
|||
textualLanguages: "languages",
|
||||
audio: "audio",
|
||||
audioSubtype: "audioSubtype",
|
||||
scans: "scans",
|
||||
scansCover: "cover",
|
||||
scansCoverFront: "front",
|
||||
scansCoverSpine: "spine",
|
||||
scansCoverBack: "back",
|
||||
scansDustjacket: "dustjacket",
|
||||
scansDustjacketFront: "front",
|
||||
scansDustjacketSpine: "spine",
|
||||
scansDustjacketBack: "back",
|
||||
scansObibelt: "obibelt",
|
||||
scansObibeltFront: "front",
|
||||
scansObibeltSpine: "spine",
|
||||
scansObibeltBack: "back",
|
||||
scansPages: "pages",
|
||||
scansPagesPage: "page",
|
||||
scansPagesImage: "image",
|
||||
} as const satisfies Record<string, string>;
|
||||
|
||||
const validateSizeValue = (value?: number) => {
|
||||
if (isDefined(value) && value <= 0) return "This value must be greater than 0";
|
||||
return true;
|
||||
};
|
||||
|
||||
const validateRequiredSizeValue = (value?: number) => {
|
||||
if (isUndefined(value)) return "This field is required.";
|
||||
if (value <= 0) return "This value must be greater than 0.";
|
||||
return true;
|
||||
};
|
||||
|
||||
export const LibraryItems = buildVersionedCollectionConfig(
|
||||
{
|
||||
singular: "Library Item",
|
||||
|
@ -63,9 +76,15 @@ export const LibraryItems = buildVersionedCollectionConfig(
|
|||
useAsTitle: fields.slug,
|
||||
description:
|
||||
"A comprehensive list of all Yokoverse’s side materials (books, novellas, artbooks, \
|
||||
stage plays, manga, drama CDs, and comics).",
|
||||
stage plays, manga, drama CDs, and comics).",
|
||||
defaultColumns: [fields.slug, fields.thumbnail, fields.status],
|
||||
group: CollectionGroup.Collections,
|
||||
hooks: {
|
||||
beforeDuplicate: beforeDuplicatePiping([
|
||||
beforeDuplicateUnpublish,
|
||||
beforeDuplicateAddCopyTo(fields.slug),
|
||||
]),
|
||||
},
|
||||
preview: (doc) => `https://accords-library.com/library/${doc.slug}`,
|
||||
},
|
||||
fields: [
|
||||
|
@ -135,9 +154,115 @@ stage plays, manga, drama CDs, and comics).",
|
|||
},
|
||||
],
|
||||
},
|
||||
optionalGroupField({
|
||||
name: fields.scans,
|
||||
fields: [
|
||||
optionalGroupField({
|
||||
name: fields.scansCover,
|
||||
fields: [
|
||||
{
|
||||
name: "size",
|
||||
type: "group",
|
||||
type: "row",
|
||||
fields: [
|
||||
imageField({
|
||||
name: fields.scansCoverFront,
|
||||
relationTo: LibraryItemThumbnails.slug,
|
||||
admin: { width: "33%" },
|
||||
}),
|
||||
imageField({
|
||||
name: fields.scansCoverSpine,
|
||||
relationTo: LibraryItemThumbnails.slug,
|
||||
admin: { width: "33%" },
|
||||
}),
|
||||
imageField({
|
||||
name: fields.scansCoverBack,
|
||||
relationTo: LibraryItemThumbnails.slug,
|
||||
admin: { width: "33%" },
|
||||
}),
|
||||
],
|
||||
},
|
||||
],
|
||||
}),
|
||||
optionalGroupField({
|
||||
name: fields.scansDustjacket,
|
||||
label: "Dust Jacket",
|
||||
labels: { singular: "Dust Jacket", plural: "Dust Jackets" },
|
||||
fields: [
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
imageField({
|
||||
name: fields.scansDustjacketFront,
|
||||
relationTo: LibraryItemThumbnails.slug,
|
||||
admin: { width: "33%" },
|
||||
}),
|
||||
imageField({
|
||||
name: fields.scansDustjacketSpine,
|
||||
relationTo: LibraryItemThumbnails.slug,
|
||||
admin: { width: "33%" },
|
||||
}),
|
||||
imageField({
|
||||
name: fields.scansDustjacketBack,
|
||||
relationTo: LibraryItemThumbnails.slug,
|
||||
admin: { width: "33%" },
|
||||
}),
|
||||
],
|
||||
},
|
||||
],
|
||||
}),
|
||||
optionalGroupField({
|
||||
name: fields.scansObibelt,
|
||||
label: "Obi Belt",
|
||||
labels: { singular: "Obi Belt", plural: "Obi Belts" },
|
||||
fields: [
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
imageField({
|
||||
name: fields.scansObibeltFront,
|
||||
relationTo: LibraryItemThumbnails.slug,
|
||||
admin: { width: "33%" },
|
||||
}),
|
||||
imageField({
|
||||
name: fields.scansObibeltSpine,
|
||||
relationTo: LibraryItemThumbnails.slug,
|
||||
admin: { width: "33%" },
|
||||
}),
|
||||
imageField({
|
||||
name: fields.scansObibeltBack,
|
||||
relationTo: LibraryItemThumbnails.slug,
|
||||
admin: { width: "33%" },
|
||||
}),
|
||||
],
|
||||
},
|
||||
],
|
||||
}),
|
||||
{
|
||||
name: fields.scansPages,
|
||||
type: "array",
|
||||
fields: [
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
{
|
||||
name: fields.scansPagesPage,
|
||||
type: "number",
|
||||
required: true,
|
||||
admin: { width: "33%" },
|
||||
},
|
||||
imageField({
|
||||
name: fields.scansPagesImage,
|
||||
relationTo: LibraryItemThumbnails.slug,
|
||||
required: true,
|
||||
admin: { width: "66%" },
|
||||
}),
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}),
|
||||
optionalGroupField({
|
||||
name: fields.size,
|
||||
admin: { condition: (data) => !data.digital },
|
||||
fields: [
|
||||
{
|
||||
|
@ -146,25 +271,49 @@ stage plays, manga, drama CDs, and comics).",
|
|||
{
|
||||
name: fields.width,
|
||||
type: "number",
|
||||
validate: validateRequiredSizeValue,
|
||||
required: true,
|
||||
admin: { step: 1, width: "33%", description: "in mm." },
|
||||
},
|
||||
{
|
||||
name: fields.height,
|
||||
type: "number",
|
||||
validate: validateRequiredSizeValue,
|
||||
required: true,
|
||||
admin: { step: 1, width: "33%", description: "in mm." },
|
||||
},
|
||||
{
|
||||
name: fields.thickness,
|
||||
type: "number",
|
||||
validate: validateSizeValue,
|
||||
admin: { step: 1, width: "33%", description: "in mm." },
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}),
|
||||
optionalGroupField({
|
||||
name: fields.price,
|
||||
admin: { className: "group-array" },
|
||||
fields: [
|
||||
{
|
||||
type: "row",
|
||||
fields: [
|
||||
{
|
||||
name: fields.priceAmount,
|
||||
type: "number",
|
||||
required: true,
|
||||
min: 0,
|
||||
admin: { width: "50%" },
|
||||
},
|
||||
{
|
||||
name: fields.priceCurrency,
|
||||
type: "relationship",
|
||||
relationTo: Currencies.slug,
|
||||
required: true,
|
||||
admin: { allowCreate: false, width: "50%" },
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}),
|
||||
{
|
||||
name: fields.itemType,
|
||||
type: "radio",
|
||||
|
|
|
@ -16,6 +16,7 @@ export const PostThumbnails = buildCollectionConfig(
|
|||
defaultSort: fields.filename,
|
||||
admin: {
|
||||
useAsTitle: fields.filename,
|
||||
disableDuplicate: true,
|
||||
group: CollectionGroup.Media,
|
||||
},
|
||||
upload: {
|
||||
|
|
|
@ -8,6 +8,9 @@ import { removeTranslatorsForTranscripts } from "./hooks/beforeValidate";
|
|||
import { Keys } from "../Keys/Keys";
|
||||
import { PostThumbnails } from "../PostThumbnails/PostThumbnails";
|
||||
import { buildVersionedCollectionConfig } from "../../utils/versionedCollectionConfig";
|
||||
import { beforeDuplicatePiping } from "../../hooks/beforeDuplicatePiping";
|
||||
import { beforeDuplicateUnpublish } from "../../hooks/beforeDuplicateUnpublish";
|
||||
import { beforeDuplicateAddCopyTo } from "../../hooks/beforeDuplicateAddCopyTo";
|
||||
|
||||
const fields = {
|
||||
slug: "slug",
|
||||
|
@ -36,9 +39,15 @@ export const Posts = buildVersionedCollectionConfig(
|
|||
useAsTitle: fields.slug,
|
||||
description:
|
||||
"News articles written by our Recorders! Here you will find announcements about \
|
||||
new merch/items releases, guides, theories, unboxings, showcases...",
|
||||
new merch/items releases, guides, theories, unboxings, showcases...",
|
||||
defaultColumns: [fields.slug, fields.thumbnail, fields.categories],
|
||||
group: CollectionGroup.Collections,
|
||||
hooks: {
|
||||
beforeDuplicate: beforeDuplicatePiping([
|
||||
beforeDuplicateUnpublish,
|
||||
beforeDuplicateAddCopyTo(fields.slug),
|
||||
]),
|
||||
},
|
||||
preview: (doc) => `https://accords-library.com/news/${doc.slug}`,
|
||||
},
|
||||
hooks: {
|
||||
|
|
|
@ -16,6 +16,7 @@ export const RecorderThumbnails = buildCollectionConfig(
|
|||
defaultSort: fields.filename,
|
||||
admin: {
|
||||
useAsTitle: fields.filename,
|
||||
disableDuplicate: true,
|
||||
group: CollectionGroup.Media,
|
||||
},
|
||||
upload: {
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
import { localizedFields } from "../../fields/translatedFields/translatedFields";
|
||||
import { Languages } from "../Languages";
|
||||
import { beforeDuplicate } from "./hooks/beforeDuplicate";
|
||||
import { CollectionGroup } from "../../constants";
|
||||
import { Languages } from "../Languages/Languages";
|
||||
import { CollectionGroup, RecordersRoles } from "../../constants";
|
||||
import { RecorderThumbnails } from "../RecorderThumbnails/RecorderThumbnails";
|
||||
import { imageField } from "../../fields/imageField/imageField";
|
||||
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
||||
import { mustBeAdmin } from "../../accesses/mustBeAdmin";
|
||||
import { mustBeAdminOrSelf } from "../../accesses/collections/mustBeAdminOrSelf";
|
||||
import { beforeLoginMustHaveAtLeastOneRole } from "./hooks/beforeLoginMustHaveAtLeastOneRole";
|
||||
import { QuickFilters } from "../../components/QuickFilters";
|
||||
|
||||
const fields = {
|
||||
username: "username",
|
||||
|
@ -13,6 +16,7 @@ const fields = {
|
|||
biographies: "biographies",
|
||||
biography: "biography",
|
||||
avatar: "avatar",
|
||||
role: "role",
|
||||
} as const satisfies Record<string, string>;
|
||||
|
||||
export const Recorders = buildCollectionConfig(
|
||||
|
@ -24,17 +28,43 @@ export const Recorders = buildCollectionConfig(
|
|||
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",
|
||||
"Recorders are contributors of the Accord's Library project. Ask an admin to create a \
|
||||
Recorder here to be able to credit them in other collections.",
|
||||
defaultColumns: [
|
||||
fields.username,
|
||||
fields.avatar,
|
||||
fields.anonymize,
|
||||
fields.biographies,
|
||||
fields.languages,
|
||||
fields.role,
|
||||
],
|
||||
disableDuplicate: true,
|
||||
group: CollectionGroup.Meta,
|
||||
components: {
|
||||
BeforeListTable: [
|
||||
() =>
|
||||
QuickFilters({
|
||||
route: "/admin/collections/recorders",
|
||||
filters: [
|
||||
{ label: "Admins", filter: "where[role][equals]=Admin" },
|
||||
{ label: "Recorders", filter: "where[role][equals]=Recorder" },
|
||||
{ label: "∅ Role", filter: "where[role][not_in]=Admin,Recorder" },
|
||||
{ label: "Anonymized", filter: "where[anonymize][equals]=true" },
|
||||
],
|
||||
}),
|
||||
],
|
||||
},
|
||||
},
|
||||
auth: true,
|
||||
access: {
|
||||
unlock: mustBeAdmin,
|
||||
update: mustBeAdminOrSelf,
|
||||
delete: mustBeAdmin,
|
||||
create: mustBeAdmin,
|
||||
},
|
||||
hooks: {
|
||||
beforeLogin: [beforeLoginMustHaveAtLeastOneRole],
|
||||
},
|
||||
timestamps: false,
|
||||
fields: [
|
||||
|
@ -75,6 +105,20 @@ export const Recorders = buildCollectionConfig(
|
|||
},
|
||||
fields: [{ name: fields.biography, type: "textarea" }],
|
||||
}),
|
||||
{
|
||||
name: fields.role,
|
||||
type: "select",
|
||||
access: {
|
||||
update: mustBeAdmin,
|
||||
create: mustBeAdmin,
|
||||
},
|
||||
hasMany: true,
|
||||
options: Object.entries(RecordersRoles).map(([value, label]) => ({
|
||||
label,
|
||||
value,
|
||||
})),
|
||||
admin: { position: "sidebar" },
|
||||
},
|
||||
{
|
||||
name: fields.anonymize,
|
||||
type: "checkbox",
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
import { BeforeDuplicate } from "payload/types";
|
||||
import { Recorder } from "../../../types/collections";
|
||||
|
||||
export const beforeDuplicate: BeforeDuplicate<Recorder> = ({ data }) => {
|
||||
return {
|
||||
...data,
|
||||
id: `${data.id}-copy`,
|
||||
};
|
||||
};
|
|
@ -0,0 +1,7 @@
|
|||
import { BeforeLoginHook } from "payload/dist/collections/config/types";
|
||||
|
||||
export const beforeLoginMustHaveAtLeastOneRole: BeforeLoginHook = ({ user }) => {
|
||||
if (user.role.length === 0) {
|
||||
throw new Error("User is not authorized to log-in.");
|
||||
}
|
||||
};
|
|
@ -1,59 +0,0 @@
|
|||
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>;
|
||||
|
||||
export const Users = buildCollectionConfig(
|
||||
{
|
||||
singular: "User",
|
||||
plural: "Users",
|
||||
},
|
||||
() => ({
|
||||
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,
|
||||
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%" },
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
})
|
||||
);
|
|
@ -0,0 +1,47 @@
|
|||
import React from "react";
|
||||
import { styled } from "styled-components";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
type Props = {
|
||||
route: string;
|
||||
filters: { label: string; filter: string }[];
|
||||
};
|
||||
|
||||
export const QuickFilters = ({ route, filters }: Props) => {
|
||||
return (
|
||||
<Container>
|
||||
<div>Quick Filters:</div>
|
||||
<FilterContainer>
|
||||
<FilterCell label="None" to={route} />
|
||||
{filters.map(({ label, filter }, index) => (
|
||||
<FilterCell key={index} label={label} to={`${route}?${filter}`} />
|
||||
))}
|
||||
</FilterContainer>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
type FilterProps = {
|
||||
label: string;
|
||||
to: string;
|
||||
};
|
||||
|
||||
const FilterCell = ({ label, to }: FilterProps) => (
|
||||
<Link className="pill pill--has-action" to={to}>
|
||||
{label}
|
||||
</Link>
|
||||
);
|
||||
|
||||
const Container = styled.div`
|
||||
display: flex;
|
||||
place-items: center;
|
||||
gap: 1rem;
|
||||
margin-top: -1rem;
|
||||
margin-bottom: 2rem;
|
||||
`;
|
||||
|
||||
const FilterContainer = styled.div`
|
||||
display: flex;
|
||||
place-items: center;
|
||||
gap: 0.5rem;
|
||||
`;
|
|
@ -2,7 +2,6 @@ export enum CollectionGroup {
|
|||
Collections = "Collections",
|
||||
Media = "Media",
|
||||
Meta = "Meta",
|
||||
Administration = "Administration",
|
||||
}
|
||||
|
||||
export enum KeysTypes {
|
||||
|
@ -43,7 +42,12 @@ export enum LibraryItemsTextualPageOrders {
|
|||
RightToLeft = "Right to left",
|
||||
}
|
||||
|
||||
export enum UserRoles {
|
||||
export enum RecordersRoles {
|
||||
Admin = "Admin",
|
||||
Recorder = "Recorder",
|
||||
}
|
||||
|
||||
export enum CollectionStatus {
|
||||
Draft = "draft",
|
||||
Published = "published",
|
||||
}
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
import { ArrayField } from "payload/types";
|
||||
|
||||
type Props = Omit<ArrayField, "type" | "maxRows" | "minRows">;
|
||||
|
||||
export const optionalGroupField = ({
|
||||
admin: { className = "", ...otherAdmin } = {},
|
||||
...otherProps
|
||||
}: Props): ArrayField => ({
|
||||
...otherProps,
|
||||
type: "array",
|
||||
minRows: 0,
|
||||
maxRows: 1,
|
||||
admin: { ...otherAdmin, className: `${className} group-array` },
|
||||
});
|
|
@ -1,7 +1,7 @@
|
|||
import { ArrayField, Field } from "payload/types";
|
||||
import { hasDuplicates } from "../../utils/validation";
|
||||
import { isDefined, isUndefined } from "../../utils/asserts";
|
||||
import { Languages } from "../../collections/Languages";
|
||||
import { Languages } from "../../collections/Languages/Languages";
|
||||
import { RowLabel } from "./RowLabel";
|
||||
import { Cell } from "./Cell";
|
||||
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
import { BeforeDuplicate } from "payload/types";
|
||||
|
||||
export const beforeDuplicateAddCopyTo =
|
||||
(fieldName: string): BeforeDuplicate =>
|
||||
({ data }) => ({ ...data, [fieldName]: `${data[fieldName]}-copy` });
|
|
@ -0,0 +1,5 @@
|
|||
import { BeforeDuplicate } from "payload/types";
|
||||
|
||||
export const beforeDuplicatePiping = (hooks: BeforeDuplicate[]): BeforeDuplicate => {
|
||||
return ({ data: initialData }) => hooks.reduce((data, hook) => hook({ data }), initialData);
|
||||
};
|
|
@ -0,0 +1,7 @@
|
|||
import { BeforeDuplicate } from "payload/types";
|
||||
import { CollectionStatus } from "../constants";
|
||||
|
||||
export const beforeDuplicateUnpublish: BeforeDuplicate = ({ data }) => ({
|
||||
...data,
|
||||
_status: CollectionStatus.Draft,
|
||||
});
|
|
@ -1,7 +1,6 @@
|
|||
import { buildConfig } from "payload/config";
|
||||
import path from "path";
|
||||
import { Users } from "./collections/Users";
|
||||
import { Languages } from "./collections/Languages";
|
||||
import { Languages } from "./collections/Languages/Languages";
|
||||
import { Recorders } from "./collections/Recorders/Recorders";
|
||||
import { Posts } from "./collections/Posts/Posts";
|
||||
import { Keys } from "./collections/Keys/Keys";
|
||||
|
@ -15,11 +14,12 @@ import { ContentThumbnails } from "./collections/ContentThumbnails/ContentThumbn
|
|||
import { ContentFolders } from "./collections/ContentFolders/ContentFolders";
|
||||
import { Logo } from "./components/Logo";
|
||||
import { Icon } from "./components/Icon";
|
||||
import { Currencies } from "./collections/Currencies/Currencies";
|
||||
|
||||
export default buildConfig({
|
||||
serverURL: "https://dashboard.accords-library.com",
|
||||
admin: {
|
||||
user: Users.slug,
|
||||
user: Recorders.slug,
|
||||
components: { graphics: { Logo, Icon } },
|
||||
meta: {
|
||||
favicon: "/public/favicon.ico",
|
||||
|
@ -39,9 +39,9 @@ export default buildConfig({
|
|||
PostThumbnails,
|
||||
Files,
|
||||
Languages,
|
||||
Currencies,
|
||||
Recorders,
|
||||
Keys,
|
||||
Users,
|
||||
],
|
||||
globals: [],
|
||||
telemetry: false,
|
||||
|
|
|
@ -45,3 +45,18 @@ html[data-theme="light"] {
|
|||
--color-base-950: #11100b;
|
||||
--color-base-1000: #000000;
|
||||
}
|
||||
|
||||
.field-type.array-field.group-array {
|
||||
.array-field__header-actions,
|
||||
.collapsible__drag,
|
||||
.collapsible__toggle,
|
||||
.collapsible__header-wrap,
|
||||
.collapsible__indicator,
|
||||
.array-actions__add,
|
||||
.array-actions__duplicate {
|
||||
display: none;
|
||||
}
|
||||
.collapsible__actions-wrap {
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,19 +25,18 @@ export type ContentFoldersTranslation = {
|
|||
|
||||
export interface Config {
|
||||
collections: {
|
||||
"library-items": LibraryItem;
|
||||
'library-items': LibraryItem;
|
||||
contents: Content;
|
||||
"content-folders": ContentFolder;
|
||||
'content-folders': ContentFolder;
|
||||
posts: Post;
|
||||
"content-thumbnails": ContentThumbnail;
|
||||
"library-item-thumbnails": LibraryItemThumbnail;
|
||||
"recorder-thumbnails": RecorderThumbnail;
|
||||
"post-thumbnails": PostThumbnail;
|
||||
'content-thumbnails': ContentThumbnail;
|
||||
'library-item-thumbnails': LibraryItemThumbnail;
|
||||
'recorder-thumbnails': RecorderThumbnail;
|
||||
'post-thumbnails': PostThumbnail;
|
||||
files: File;
|
||||
languages: Language;
|
||||
recorders: Recorder;
|
||||
keys: Key;
|
||||
users: User;
|
||||
};
|
||||
globals: {};
|
||||
}
|
||||
|
@ -53,50 +52,55 @@ export interface LibraryItem {
|
|||
digital: boolean;
|
||||
downloadable: boolean;
|
||||
size?: {
|
||||
width?: number;
|
||||
height?: number;
|
||||
width: number;
|
||||
height: number;
|
||||
thickness?: number;
|
||||
};
|
||||
itemType?: "Textual" | "Audio" | "Video" | "Game" | "Other";
|
||||
id?: string;
|
||||
}[];
|
||||
price?: {
|
||||
priceAmount: number;
|
||||
id?: string;
|
||||
}[];
|
||||
itemType?: 'Textual' | 'Audio' | 'Video' | 'Game' | 'Other';
|
||||
textual?: {
|
||||
subtype?:
|
||||
| {
|
||||
value: string;
|
||||
relationTo: "keys";
|
||||
relationTo: 'keys';
|
||||
}[]
|
||||
| {
|
||||
value: Key;
|
||||
relationTo: "keys";
|
||||
relationTo: 'keys';
|
||||
}[];
|
||||
languages?:
|
||||
| {
|
||||
value: string;
|
||||
relationTo: "languages";
|
||||
relationTo: 'languages';
|
||||
}[]
|
||||
| {
|
||||
value: Language;
|
||||
relationTo: "languages";
|
||||
relationTo: 'languages';
|
||||
}[];
|
||||
pageCount?: number;
|
||||
bindingType?: "Paperback" | "Hardcover";
|
||||
pageOrder?: "LeftToRight" | "RightToLeft";
|
||||
bindingType?: 'Paperback' | 'Hardcover';
|
||||
pageOrder?: 'LeftToRight' | 'RightToLeft';
|
||||
};
|
||||
audio?: {
|
||||
audioSubtype?:
|
||||
| {
|
||||
value: string;
|
||||
relationTo: "keys";
|
||||
relationTo: 'keys';
|
||||
}[]
|
||||
| {
|
||||
value: Key;
|
||||
relationTo: "keys";
|
||||
relationTo: 'keys';
|
||||
}[];
|
||||
};
|
||||
releaseDate?: string;
|
||||
lastModifiedBy: string | User;
|
||||
lastModifiedBy: string | Recorder;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
_status?: "draft" | "published";
|
||||
_status?: 'draft' | 'published';
|
||||
}
|
||||
export interface LibraryItemThumbnail {
|
||||
id: string;
|
||||
|
@ -139,27 +143,30 @@ export interface Key {
|
|||
id: string;
|
||||
slug: 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 {
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
export interface User {
|
||||
export interface Recorder {
|
||||
id: string;
|
||||
recorder: string | Recorder;
|
||||
name: string;
|
||||
role: ("Admin" | "Recorder")[];
|
||||
username: string;
|
||||
avatar?: string | RecorderThumbnail;
|
||||
languages?: string[] | Language[];
|
||||
biographies?: RecorderBiographies;
|
||||
role?: ('Admin' | 'Recorder')[];
|
||||
anonymize: boolean;
|
||||
email: string;
|
||||
resetPasswordToken?: string;
|
||||
resetPasswordExpiration?: string;
|
||||
|
@ -169,14 +176,6 @@ export interface User {
|
|||
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;
|
||||
|
@ -213,15 +212,15 @@ export interface Content {
|
|||
categories?:
|
||||
| {
|
||||
value: string;
|
||||
relationTo: "keys";
|
||||
relationTo: 'keys';
|
||||
}[]
|
||||
| {
|
||||
value: Key;
|
||||
relationTo: "keys";
|
||||
relationTo: 'keys';
|
||||
}[];
|
||||
type?: {
|
||||
value: string | Key;
|
||||
relationTo: "keys";
|
||||
relationTo: 'keys';
|
||||
};
|
||||
translations: {
|
||||
language: string | Language;
|
||||
|
@ -240,10 +239,10 @@ export interface Content {
|
|||
audio?: string | File;
|
||||
id?: string;
|
||||
}[];
|
||||
lastModifiedBy: string | User;
|
||||
lastModifiedBy: string | Recorder;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
_status?: "draft" | "published";
|
||||
_status?: 'draft' | 'published';
|
||||
}
|
||||
export interface ContentThumbnail {
|
||||
id: string;
|
||||
|
@ -280,25 +279,19 @@ export interface TextBlock {
|
|||
}[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: "textBlock";
|
||||
blockType: 'textBlock';
|
||||
}
|
||||
export interface Section {
|
||||
content?: (Section_Section | Section_Tabs | TranscriptBlock | QuoteBlock | TextBlock)[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: "section";
|
||||
blockType: 'section';
|
||||
}
|
||||
export interface Section_Section {
|
||||
content?: (
|
||||
| Section_Section_Section
|
||||
| Section_Section_Tabs
|
||||
| TranscriptBlock
|
||||
| QuoteBlock
|
||||
| TextBlock
|
||||
)[];
|
||||
content?: (Section_Section_Section | Section_Section_Tabs | TranscriptBlock | QuoteBlock | TextBlock)[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: "section";
|
||||
blockType: 'section';
|
||||
}
|
||||
export interface Section_Section_Section {
|
||||
content?: (
|
||||
|
@ -310,25 +303,25 @@ export interface Section_Section_Section {
|
|||
)[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: "section";
|
||||
blockType: 'section';
|
||||
}
|
||||
export interface Section_Section_Section_Section {
|
||||
content?: (Section_Section_Section_Section_Section | TranscriptBlock | QuoteBlock | TextBlock)[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: "section";
|
||||
blockType: 'section';
|
||||
}
|
||||
export interface Section_Section_Section_Section_Section {
|
||||
content?: (TranscriptBlock | QuoteBlock | TextBlock)[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: "section";
|
||||
blockType: 'section';
|
||||
}
|
||||
export interface TranscriptBlock {
|
||||
lines: (LineBlock | CueBlock)[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: "transcriptBlock";
|
||||
blockType: 'transcriptBlock';
|
||||
}
|
||||
export interface LineBlock {
|
||||
content: {
|
||||
|
@ -336,13 +329,13 @@ export interface LineBlock {
|
|||
}[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: "lineBlock";
|
||||
blockType: 'lineBlock';
|
||||
}
|
||||
export interface CueBlock {
|
||||
content: string;
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: "cueBlock";
|
||||
blockType: 'cueBlock';
|
||||
}
|
||||
export interface QuoteBlock {
|
||||
from: string;
|
||||
|
@ -351,120 +344,120 @@ export interface QuoteBlock {
|
|||
}[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: "quoteBlock";
|
||||
blockType: 'quoteBlock';
|
||||
}
|
||||
export interface Section_Section_Section_Tabs {
|
||||
tabs?: Section_Section_Section_Tabs_Tab[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: "tabs";
|
||||
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";
|
||||
blockType: 'tab';
|
||||
}
|
||||
export interface Section_Section_Section_Tabs_Tab_Section {
|
||||
content?: (TranscriptBlock | QuoteBlock | TextBlock)[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: "section";
|
||||
blockType: 'section';
|
||||
}
|
||||
export interface Section_Section_Tabs {
|
||||
tabs?: Section_Section_Tabs_Tab[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: "tabs";
|
||||
blockType: 'tabs';
|
||||
}
|
||||
export interface Section_Section_Tabs_Tab {
|
||||
content?: (Section_Section_Tabs_Tab_Section | TranscriptBlock | QuoteBlock | TextBlock)[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: "tab";
|
||||
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";
|
||||
blockType: 'section';
|
||||
}
|
||||
export interface Section_Section_Tabs_Tab_Section_Section {
|
||||
content?: (TranscriptBlock | QuoteBlock | TextBlock)[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: "section";
|
||||
blockType: 'section';
|
||||
}
|
||||
export interface Section_Tabs {
|
||||
tabs?: Section_Tabs_Tab[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: "tabs";
|
||||
blockType: 'tabs';
|
||||
}
|
||||
export interface Section_Tabs_Tab {
|
||||
content?: (Section_Tabs_Tab_Section | TranscriptBlock | QuoteBlock | TextBlock)[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: "tab";
|
||||
blockType: 'tab';
|
||||
}
|
||||
export interface Section_Tabs_Tab_Section {
|
||||
content?: (Section_Tabs_Tab_Section_Section | TranscriptBlock | QuoteBlock | TextBlock)[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: "section";
|
||||
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";
|
||||
blockType: 'section';
|
||||
}
|
||||
export interface Section_Tabs_Tab_Section_Section_Section {
|
||||
content?: (TranscriptBlock | QuoteBlock | TextBlock)[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: "section";
|
||||
blockType: 'section';
|
||||
}
|
||||
export interface Tabs {
|
||||
tabs?: Tabs_Tab[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: "tabs";
|
||||
blockType: 'tabs';
|
||||
}
|
||||
export interface Tabs_Tab {
|
||||
content?: (Tabs_Tab_Section | TranscriptBlock | QuoteBlock | TextBlock)[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: "tab";
|
||||
blockType: 'tab';
|
||||
}
|
||||
export interface Tabs_Tab_Section {
|
||||
content?: (Tabs_Tab_Section_Section | TranscriptBlock | QuoteBlock | TextBlock)[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: "section";
|
||||
blockType: 'section';
|
||||
}
|
||||
export interface Tabs_Tab_Section_Section {
|
||||
content?: (Tabs_Tab_Section_Section_Section | TranscriptBlock | QuoteBlock | TextBlock)[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: "section";
|
||||
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";
|
||||
blockType: 'section';
|
||||
}
|
||||
export interface Tabs_Tab_Section_Section_Section_Section {
|
||||
content?: (TranscriptBlock | QuoteBlock | TextBlock)[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: "section";
|
||||
blockType: 'section';
|
||||
}
|
||||
export interface File {
|
||||
id: string;
|
||||
filename: string;
|
||||
type: "LibraryScans" | "LibrarySoundtracks" | "ContentVideo" | "ContentAudio";
|
||||
type: 'LibraryScans' | 'LibrarySoundtracks' | 'ContentVideo' | 'ContentAudio';
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
|
@ -475,20 +468,20 @@ export interface ContentFolder {
|
|||
subfolders?:
|
||||
| {
|
||||
value: string;
|
||||
relationTo: "content-folders";
|
||||
relationTo: 'content-folders';
|
||||
}[]
|
||||
| {
|
||||
value: ContentFolder;
|
||||
relationTo: "content-folders";
|
||||
relationTo: 'content-folders';
|
||||
}[];
|
||||
contents?:
|
||||
| {
|
||||
value: string;
|
||||
relationTo: "contents";
|
||||
relationTo: 'contents';
|
||||
}[]
|
||||
| {
|
||||
value: Content;
|
||||
relationTo: "contents";
|
||||
relationTo: 'contents';
|
||||
}[];
|
||||
}
|
||||
export interface Post {
|
||||
|
@ -498,20 +491,20 @@ export interface Post {
|
|||
authors:
|
||||
| {
|
||||
value: string;
|
||||
relationTo: "recorders";
|
||||
relationTo: 'recorders';
|
||||
}[]
|
||||
| {
|
||||
value: Recorder;
|
||||
relationTo: "recorders";
|
||||
relationTo: 'recorders';
|
||||
}[];
|
||||
categories?:
|
||||
| {
|
||||
value: string;
|
||||
relationTo: "keys";
|
||||
relationTo: 'keys';
|
||||
}[]
|
||||
| {
|
||||
value: Key;
|
||||
relationTo: "keys";
|
||||
relationTo: 'keys';
|
||||
}[];
|
||||
translations: {
|
||||
language: string | Language;
|
||||
|
@ -527,10 +520,10 @@ export interface Post {
|
|||
}[];
|
||||
publishedDate: string;
|
||||
hidden?: boolean;
|
||||
lastModifiedBy: string | User;
|
||||
lastModifiedBy: string | Recorder;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
_status?: "draft" | "published";
|
||||
_status?: 'draft' | 'published';
|
||||
}
|
||||
export interface PostThumbnail {
|
||||
id: string;
|
||||
|
|
|
@ -1,29 +1,26 @@
|
|||
import { CollectionBeforeChangeHook, CollectionConfig, RelationshipField } from "payload/types";
|
||||
import { Users } from "../collections/Users";
|
||||
import {
|
||||
BuildCollectionConfig,
|
||||
GenerationFunctionProps,
|
||||
buildCollectionConfig,
|
||||
} from "./collectionConfig";
|
||||
import { Recorders } from "../collections/Recorders/Recorders";
|
||||
|
||||
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,
|
||||
relationTo: Recorders.slug,
|
||||
admin: { readOnly: true, position: "sidebar" },
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in New Issue