Lots of things, again
This commit is contained in:
parent
d9f84f59a8
commit
f56ba4675f
|
@ -12,15 +12,10 @@
|
||||||
"@fontsource/vollkorn": "^5.0.8",
|
"@fontsource/vollkorn": "^5.0.8",
|
||||||
"clean-deep": "^3.4.0",
|
"clean-deep": "^3.4.0",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"dotenv": "^16.3.1",
|
|
||||||
"express": "^4.18.2",
|
|
||||||
"language-tags": "^1.0.8",
|
"language-tags": "^1.0.8",
|
||||||
"luxon": "^3.4.0",
|
"luxon": "^3.4.0",
|
||||||
"payload": "^1.13.3",
|
"payload": "^1.13.3",
|
||||||
"qs": "^6.11.2",
|
"styled-components": "^6.0.7"
|
||||||
"slugify": "^1.6.6",
|
|
||||||
"styled-components": "^6.0.7",
|
|
||||||
"unset-value": "^2.0.1"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/dotenv": "^8.2.0",
|
"@types/dotenv": "^8.2.0",
|
||||||
|
@ -6887,6 +6882,7 @@
|
||||||
"version": "16.3.1",
|
"version": "16.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz",
|
||||||
"integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==",
|
"integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==",
|
||||||
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
|
@ -7565,17 +7561,6 @@
|
||||||
"url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
|
"url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/get-value": {
|
|
||||||
"version": "3.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/get-value/-/get-value-3.0.1.tgz",
|
|
||||||
"integrity": "sha512-mKZj9JLQrwMBtj5wxi6MH8Z5eSKaERpAwjg43dPtlGI1ZVEgH/qC7T8/6R2OBSUA+zzHBZgICsVJaEIV2tKTDA==",
|
|
||||||
"dependencies": {
|
|
||||||
"isobject": "^3.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/github-from-package": {
|
"node_modules/github-from-package": {
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
|
||||||
|
@ -7817,29 +7802,6 @@
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/has-value": {
|
|
||||||
"version": "2.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/has-value/-/has-value-2.0.2.tgz",
|
|
||||||
"integrity": "sha512-ybKOlcRsK2MqrM3Hmz/lQxXHZ6ejzSPzpNabKB45jb5qDgJvKPa3SdapTsTLwEb9WltgWpOmNax7i+DzNOk4TA==",
|
|
||||||
"dependencies": {
|
|
||||||
"get-value": "^3.0.0",
|
|
||||||
"has-values": "^2.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/has-values": {
|
|
||||||
"version": "2.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/has-values/-/has-values-2.0.1.tgz",
|
|
||||||
"integrity": "sha512-+QdH3jOmq9P8GfdjFg0eJudqx1FqU62NQJ4P16rOEHeRdl7ckgwn6uqQjzYE0ZoHVV/e5E2esuJ5Gl5+HUW19w==",
|
|
||||||
"dependencies": {
|
|
||||||
"kind-of": "^6.0.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/he": {
|
"node_modules/he": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
|
||||||
|
@ -12644,14 +12606,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.0.6.tgz",
|
||||||
"integrity": "sha512-FOyLWWVjG+aC0UqG76V53yAWdXfH8bO6FNmyZOuUrzDzK8DI3/JRY25UD7+g49JWM1LXwymsKERB+DzI0dTEQA=="
|
"integrity": "sha512-FOyLWWVjG+aC0UqG76V53yAWdXfH8bO6FNmyZOuUrzDzK8DI3/JRY25UD7+g49JWM1LXwymsKERB+DzI0dTEQA=="
|
||||||
},
|
},
|
||||||
"node_modules/slugify": {
|
|
||||||
"version": "1.6.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/slugify/-/slugify-1.6.6.tgz",
|
|
||||||
"integrity": "sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/smart-buffer": {
|
"node_modules/smart-buffer": {
|
||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
|
||||||
|
@ -13540,26 +13494,6 @@
|
||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/unset-value": {
|
|
||||||
"version": "2.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/unset-value/-/unset-value-2.0.1.tgz",
|
|
||||||
"integrity": "sha512-2hvrBfjUE00PkqN+q0XP6yRAOGrR06uSiUoIQGZkc7GxvQ9H7v8quUPNtZjMg4uux69i8HWpIjLPUKwCuRGyNg==",
|
|
||||||
"dependencies": {
|
|
||||||
"has-value": "^2.0.2",
|
|
||||||
"isobject": "^4.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/unset-value/node_modules/isobject": {
|
|
||||||
"version": "4.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/isobject/-/isobject-4.0.0.tgz",
|
|
||||||
"integrity": "sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/untildify": {
|
"node_modules/untildify": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz",
|
||||||
|
|
|
@ -24,15 +24,10 @@
|
||||||
"@fontsource/vollkorn": "^5.0.8",
|
"@fontsource/vollkorn": "^5.0.8",
|
||||||
"clean-deep": "^3.4.0",
|
"clean-deep": "^3.4.0",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"dotenv": "^16.3.1",
|
|
||||||
"express": "^4.18.2",
|
|
||||||
"language-tags": "^1.0.8",
|
"language-tags": "^1.0.8",
|
||||||
"luxon": "^3.4.0",
|
"luxon": "^3.4.0",
|
||||||
"payload": "^1.13.3",
|
"payload": "^1.13.3",
|
||||||
"qs": "^6.11.2",
|
"styled-components": "^6.0.7"
|
||||||
"slugify": "^1.6.6",
|
|
||||||
"styled-components": "^6.0.7",
|
|
||||||
"unset-value": "^2.0.1"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/dotenv": "^8.2.0",
|
"@types/dotenv": "^8.2.0",
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
import { CollectionConfig } from "payload/types";
|
import { CollectionConfig } from "payload/types";
|
||||||
import { mustBeAdmin } from "../../accesses/mustBeAdmin";
|
import { mustBeAdmin } from "../../accesses/mustBeAdmin";
|
||||||
import { CollectionGroups, Collections } from "../../constants";
|
import { CollectionGroups, Collections } from "../../constants";
|
||||||
|
import { backPropagationField } from "../../fields/backPropagationField/backPropagationField";
|
||||||
import { slugField } from "../../fields/slugField/slugField";
|
import { slugField } from "../../fields/slugField/slugField";
|
||||||
import { localizedFields } from "../../fields/translatedFields/translatedFields";
|
import { translatedFields } from "../../fields/translatedFields/translatedFields";
|
||||||
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
||||||
import { importFromStrapi } from "./endpoints/importFromStrapi";
|
import { importFromStrapi } from "./endpoints/importFromStrapi";
|
||||||
|
import { beforeValidateEndingGreaterThanStarting } from "./hooks/beforeValidateEndingGreaterThanStarting";
|
||||||
|
import { beforeValidateNoIntersection } from "./hooks/beforeValidateNoIntersection";
|
||||||
|
|
||||||
const fields = {
|
const fields = {
|
||||||
slug: "slug",
|
slug: "slug",
|
||||||
|
@ -13,58 +16,71 @@ const fields = {
|
||||||
translations: "translations",
|
translations: "translations",
|
||||||
translationsTitle: "title",
|
translationsTitle: "title",
|
||||||
translationsDescription: "description",
|
translationsDescription: "description",
|
||||||
|
events: "events",
|
||||||
} as const satisfies Record<string, string>;
|
} as const satisfies Record<string, string>;
|
||||||
|
|
||||||
export const ChronologyEras: CollectionConfig = buildCollectionConfig(
|
export const ChronologyEras: CollectionConfig = buildCollectionConfig({
|
||||||
Collections.ChronologyEras,
|
slug: Collections.ChronologyEras,
|
||||||
{
|
labels: {
|
||||||
singular: "Chronology Era",
|
singular: "Chronology Era",
|
||||||
plural: "Chronology Eras",
|
plural: "Chronology Eras",
|
||||||
},
|
},
|
||||||
() => ({
|
defaultSort: fields.startingYear,
|
||||||
defaultSort: fields.startingYear,
|
admin: {
|
||||||
admin: {
|
group: CollectionGroups.Collections,
|
||||||
group: CollectionGroups.Collections,
|
defaultColumns: [fields.slug, fields.startingYear, fields.endingYear, fields.translations],
|
||||||
defaultColumns: [fields.slug, fields.startingYear, fields.endingYear, fields.translations],
|
useAsTitle: fields.slug,
|
||||||
useAsTitle: fields.slug,
|
},
|
||||||
|
access: {
|
||||||
|
create: mustBeAdmin,
|
||||||
|
delete: mustBeAdmin,
|
||||||
|
},
|
||||||
|
hooks: {
|
||||||
|
beforeValidate: [beforeValidateEndingGreaterThanStarting, beforeValidateNoIntersection],
|
||||||
|
},
|
||||||
|
endpoints: [importFromStrapi],
|
||||||
|
fields: [
|
||||||
|
slugField({ name: fields.slug }),
|
||||||
|
{
|
||||||
|
type: "row",
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: fields.startingYear,
|
||||||
|
type: "number",
|
||||||
|
min: 0,
|
||||||
|
required: true,
|
||||||
|
admin: { width: "50%", description: "The year the era started (year included)" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: fields.endingYear,
|
||||||
|
type: "number",
|
||||||
|
min: 0,
|
||||||
|
required: true,
|
||||||
|
admin: { width: "50%", description: "The year the era ended (year included)" },
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
access: {
|
translatedFields({
|
||||||
create: mustBeAdmin,
|
name: fields.translations,
|
||||||
delete: mustBeAdmin,
|
admin: { useAsTitle: fields.translationsTitle },
|
||||||
},
|
fields: [
|
||||||
endpoints: [importFromStrapi],
|
{ name: fields.translationsTitle, type: "text", required: true },
|
||||||
fields: [
|
{
|
||||||
slugField({ name: fields.slug }),
|
name: fields.translationsDescription,
|
||||||
{
|
type: "textarea",
|
||||||
type: "row",
|
},
|
||||||
fields: [
|
],
|
||||||
{
|
}),
|
||||||
name: fields.startingYear,
|
backPropagationField({
|
||||||
type: "number",
|
name: fields.events,
|
||||||
min: 0,
|
hasMany: true,
|
||||||
required: true,
|
relationTo: Collections.ChronologyItems,
|
||||||
admin: { width: "50%", description: "The year the era started (year included)" },
|
where: ({ startingYear, endingYear }) => ({
|
||||||
},
|
and: [
|
||||||
{
|
{ "date.year": { greater_than_equal: startingYear } },
|
||||||
name: fields.endingYear,
|
{ "date.year": { less_than_equal: endingYear } },
|
||||||
type: "number",
|
|
||||||
min: 0,
|
|
||||||
required: true,
|
|
||||||
admin: { width: "50%", description: "The year the era ended (year included)" },
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
localizedFields({
|
|
||||||
name: fields.translations,
|
|
||||||
admin: { useAsTitle: fields.translationsTitle },
|
|
||||||
fields: [
|
|
||||||
{ name: fields.translationsTitle, type: "text", required: true },
|
|
||||||
{
|
|
||||||
name: fields.translationsDescription,
|
|
||||||
type: "textarea",
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
],
|
}),
|
||||||
})
|
],
|
||||||
);
|
});
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
import { CollectionBeforeValidateHook } from "payload/types";
|
||||||
|
import { ChronologyEra } from "../../../types/collections";
|
||||||
|
|
||||||
|
export const beforeValidateEndingGreaterThanStarting: CollectionBeforeValidateHook<
|
||||||
|
ChronologyEra
|
||||||
|
> = async ({ data: { startingYear, endingYear } }) => {
|
||||||
|
if (endingYear < startingYear) {
|
||||||
|
throw new Error("The ending year cannot be before the starting year.");
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,23 @@
|
||||||
|
import payload from "payload";
|
||||||
|
import { CollectionBeforeValidateHook } from "payload/types";
|
||||||
|
import { Collections } from "../../../constants";
|
||||||
|
import { ChronologyEra } from "../../../types/collections";
|
||||||
|
import { hasIntersection } from "../../../utils/asserts";
|
||||||
|
|
||||||
|
export const beforeValidateNoIntersection: CollectionBeforeValidateHook<ChronologyEra> = async ({
|
||||||
|
data: { startingYear, endingYear },
|
||||||
|
}) => {
|
||||||
|
const otherEras = await payload.find({
|
||||||
|
collection: Collections.ChronologyEras,
|
||||||
|
limit: 100,
|
||||||
|
});
|
||||||
|
|
||||||
|
otherEras.docs.forEach((otherEra: ChronologyEra) => {
|
||||||
|
if (hasIntersection([startingYear, endingYear], [otherEra.startingYear, otherEra.endingYear])) {
|
||||||
|
throw new Error(
|
||||||
|
`This era (${startingYear} -> ${endingYear}) is intersecting with the era\
|
||||||
|
"${otherEra.slug}" (${otherEra.startingYear} -> ${otherEra.endingYear})`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
|
@ -6,115 +6,137 @@ import {
|
||||||
publishStatusFilters,
|
publishStatusFilters,
|
||||||
} from "../../components/QuickFilters";
|
} from "../../components/QuickFilters";
|
||||||
import { CollectionGroups, Collections } from "../../constants";
|
import { CollectionGroups, Collections } from "../../constants";
|
||||||
import { localizedFields } from "../../fields/translatedFields/translatedFields";
|
import { translatedFields } from "../../fields/translatedFields/translatedFields";
|
||||||
import { isDefined, isUndefined } from "../../utils/asserts";
|
import { isEmpty, isUndefined } from "../../utils/asserts";
|
||||||
import { buildVersionedCollectionConfig } from "../../utils/versionedCollectionConfig";
|
import { buildVersionedCollectionConfig } from "../../utils/versionedCollectionConfig";
|
||||||
import { importFromStrapi } from "./endpoints/importFromStrapi";
|
import { importFromStrapi } from "./endpoints/importFromStrapi";
|
||||||
|
import { beforeValidatePopulateNameField } from "./hooks/beforeValidatePopulateNameField";
|
||||||
|
|
||||||
const fields = {
|
const fields = {
|
||||||
name: "name",
|
name: "name",
|
||||||
events: "events",
|
events: "events",
|
||||||
|
eventsSource: "source",
|
||||||
eventsTranslations: "translations",
|
eventsTranslations: "translations",
|
||||||
eventsTranslationsTitle: "title",
|
eventsTranslationsTitle: "title",
|
||||||
eventsTranslationsDescription: "description",
|
eventsTranslationsDescription: "description",
|
||||||
eventsTranslationsNotes: "notes",
|
eventsTranslationsNotes: "notes",
|
||||||
date: "date",
|
date: "date",
|
||||||
year: "year",
|
dateYear: "year",
|
||||||
month: "month",
|
dateMonth: "month",
|
||||||
day: "day",
|
dateDay: "day",
|
||||||
|
era: "era",
|
||||||
status: "_status",
|
status: "_status",
|
||||||
} as const satisfies Record<string, string>;
|
} as const satisfies Record<string, string>;
|
||||||
|
|
||||||
export const ChronologyItems: CollectionConfig = buildVersionedCollectionConfig(
|
export const ChronologyItems: CollectionConfig = buildVersionedCollectionConfig({
|
||||||
Collections.ChronologyItems,
|
slug: Collections.ChronologyItems,
|
||||||
{
|
labels: {
|
||||||
singular: "Chronology Item",
|
singular: "Chronology Item",
|
||||||
plural: "Chronology Items",
|
plural: "Chronology Items",
|
||||||
},
|
},
|
||||||
() => ({
|
defaultSort: fields.name,
|
||||||
defaultSort: fields.name,
|
admin: {
|
||||||
admin: {
|
group: CollectionGroups.Collections,
|
||||||
group: CollectionGroups.Collections,
|
defaultColumns: [fields.name, fields.events, fields.status],
|
||||||
defaultColumns: [fields.name, fields.events, fields.status],
|
useAsTitle: fields.name,
|
||||||
useAsTitle: fields.name,
|
components: {
|
||||||
components: {
|
BeforeListTable: [
|
||||||
BeforeListTable: [
|
() =>
|
||||||
() =>
|
QuickFilters({
|
||||||
QuickFilters({
|
slug: Collections.ChronologyItems,
|
||||||
slug: Collections.ChronologyItems,
|
filterGroups: [
|
||||||
filterGroups: [
|
languageBasedFilters("events.translations.language"),
|
||||||
languageBasedFilters("events.translations.language"),
|
publishStatusFilters,
|
||||||
publishStatusFilters,
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
endpoints: [importFromStrapi],
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
name: fields.name,
|
|
||||||
type: "text",
|
|
||||||
admin: { hidden: true },
|
|
||||||
hooks: {
|
|
||||||
beforeValidate: [
|
|
||||||
({
|
|
||||||
data: {
|
|
||||||
date: { year, month, day },
|
|
||||||
},
|
|
||||||
}) =>
|
|
||||||
[
|
|
||||||
String(year ?? "?????").padStart(5, "0"),
|
|
||||||
String(month ?? "??").padStart(2, "0"),
|
|
||||||
String(day ?? "??").padStart(2, "0"),
|
|
||||||
].join("-"),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "group",
|
|
||||||
name: fields.date,
|
|
||||||
validate: ({ year, month, day } = {}) => {
|
|
||||||
if (isDefined(day)) {
|
|
||||||
if (isUndefined(month)) return "A month is required if a day is set";
|
|
||||||
const stringDate = `${year}/${month}/${day}`;
|
|
||||||
if (!DateTime.fromObject({ year, month, day }).isValid)
|
|
||||||
return `The given date (${stringDate}) is not a valid date.`;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
type: "row",
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
name: fields.year,
|
|
||||||
type: "number",
|
|
||||||
required: true,
|
|
||||||
min: 0,
|
|
||||||
admin: { width: "33%" },
|
|
||||||
},
|
|
||||||
{ name: fields.month, type: "number", min: 1, max: 12, admin: { width: "33%" } },
|
|
||||||
{ name: fields.day, type: "number", min: 1, max: 31, admin: { width: "33%" } },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: fields.events,
|
|
||||||
type: "array",
|
|
||||||
fields: [
|
|
||||||
localizedFields({
|
|
||||||
name: fields.eventsTranslations,
|
|
||||||
admin: { useAsTitle: fields.eventsTranslationsTitle },
|
|
||||||
fields: [
|
|
||||||
{ name: fields.eventsTranslationsTitle, type: "text" },
|
|
||||||
{ name: fields.eventsTranslationsDescription, type: "textarea" },
|
|
||||||
{ name: fields.eventsTranslationsNotes, type: "textarea" },
|
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
endpoints: [importFromStrapi],
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: fields.name,
|
||||||
|
type: "text",
|
||||||
|
admin: { hidden: true },
|
||||||
|
hooks: {
|
||||||
|
beforeValidate: [beforeValidatePopulateNameField],
|
||||||
},
|
},
|
||||||
],
|
},
|
||||||
})
|
{
|
||||||
);
|
type: "group",
|
||||||
|
name: fields.date,
|
||||||
|
validate: ({ year, month, day } = {}) => {
|
||||||
|
if (isUndefined(day)) return true;
|
||||||
|
if (isUndefined(month)) return "A month is required if a day is set";
|
||||||
|
const stringDate = `${year}/${month}/${day}`;
|
||||||
|
if (!DateTime.fromObject({ year, month, day }).isValid) {
|
||||||
|
return `The given date (${stringDate}) is not a valid date.`;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
type: "row",
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: fields.dateYear,
|
||||||
|
type: "number",
|
||||||
|
required: true,
|
||||||
|
min: 0,
|
||||||
|
admin: { width: "33%" },
|
||||||
|
},
|
||||||
|
{ name: fields.dateMonth, type: "number", min: 1, max: 12, admin: { width: "33%" } },
|
||||||
|
{ name: fields.dateDay, type: "number", min: 1, max: 31, admin: { width: "33%" } },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: fields.events,
|
||||||
|
type: "array",
|
||||||
|
required: true,
|
||||||
|
minRows: 1,
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: fields.eventsSource,
|
||||||
|
type: "relationship",
|
||||||
|
relationTo: [Collections.Contents, Collections.LibraryItems],
|
||||||
|
// required: true,
|
||||||
|
admin: { allowCreate: false },
|
||||||
|
},
|
||||||
|
translatedFields({
|
||||||
|
name: fields.eventsTranslations,
|
||||||
|
required: true,
|
||||||
|
minRows: 1,
|
||||||
|
admin: {
|
||||||
|
useAsTitle: fields.eventsTranslationsTitle,
|
||||||
|
hasSourceLanguage: true,
|
||||||
|
hasCredits: true,
|
||||||
|
},
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: fields.eventsTranslationsTitle,
|
||||||
|
validate: (_, { siblingData: { description, title } }) => {
|
||||||
|
if (isEmpty(description) && isEmpty(title)) {
|
||||||
|
return "This field is required if no description is set.";
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
type: "text",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: fields.eventsTranslationsDescription,
|
||||||
|
validate: (_, { siblingData: { description, title } }) => {
|
||||||
|
if (isEmpty(description) && isEmpty(title)) {
|
||||||
|
return "This field is required if no title is set.";
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
type: "textarea",
|
||||||
|
},
|
||||||
|
{ name: fields.eventsTranslationsNotes, type: "textarea" },
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
|
@ -11,7 +11,7 @@ export const importFromStrapi = createStrapiImportEndpoint<ChronologyItem>({
|
||||||
},
|
},
|
||||||
payload: {
|
payload: {
|
||||||
collection: Collections.ChronologyItems,
|
collection: Collections.ChronologyItems,
|
||||||
convert: ({ year, month, day, events }) => ({
|
convert: ({ year, month, day, events }, user) => ({
|
||||||
date: { year, month, day },
|
date: { year, month, day },
|
||||||
events: events.map((event) => ({
|
events: events.map((event) => ({
|
||||||
translations: event.translations.map(({ title, description, note, language }) => ({
|
translations: event.translations.map(({ title, description, note, language }) => ({
|
||||||
|
@ -19,6 +19,10 @@ export const importFromStrapi = createStrapiImportEndpoint<ChronologyItem>({
|
||||||
description,
|
description,
|
||||||
note,
|
note,
|
||||||
language: language.data.attributes.code,
|
language: language.data.attributes.code,
|
||||||
|
sourceLanguage: "en",
|
||||||
|
...(language.data.attributes.code === "en"
|
||||||
|
? { transcribers: [user.id] }
|
||||||
|
: { translators: [user.id] }),
|
||||||
})),
|
})),
|
||||||
})),
|
})),
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
import { FieldHook } from "payload/dist/fields/config/types";
|
||||||
|
import { ChronologyItem } from "../../../types/collections";
|
||||||
|
import { isDefined, isUndefined } from "../../../utils/asserts";
|
||||||
|
|
||||||
|
export const beforeValidatePopulateNameField: FieldHook<
|
||||||
|
ChronologyItem,
|
||||||
|
ChronologyItem["name"],
|
||||||
|
ChronologyItem
|
||||||
|
> = ({ data: { date } }) => {
|
||||||
|
if (isUndefined(date?.year)) return "????-??-??";
|
||||||
|
const { year, month, day } = date;
|
||||||
|
let result = String(year).padStart(5, " ");
|
||||||
|
if (isDefined(month)) {
|
||||||
|
result += `-${String(date.month).padStart(2, "0")}`;
|
||||||
|
if (isDefined(day)) {
|
||||||
|
result += `-${String(date.day).padStart(2, "0")}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
|
@ -1,8 +1,9 @@
|
||||||
import { CollectionGroups, Collections, FileTypes, KeysTypes } from "../../constants";
|
import { CollectionGroups, Collections, FileTypes, KeysTypes } from "../../constants";
|
||||||
import { fileField } from "../../fields/fileField/fileField";
|
import { fileField } from "../../fields/fileField/fileField";
|
||||||
import { imageField } from "../../fields/imageField/imageField";
|
import { imageField } from "../../fields/imageField/imageField";
|
||||||
|
import { keysField } from "../../fields/keysField/keysField";
|
||||||
import { slugField } from "../../fields/slugField/slugField";
|
import { slugField } from "../../fields/slugField/slugField";
|
||||||
import { localizedFields } from "../../fields/translatedFields/translatedFields";
|
import { translatedFields } from "../../fields/translatedFields/translatedFields";
|
||||||
import { beforeDuplicateAddCopyTo } from "../../hooks/beforeDuplicateAddCopyTo";
|
import { beforeDuplicateAddCopyTo } from "../../hooks/beforeDuplicateAddCopyTo";
|
||||||
import { beforeDuplicatePiping } from "../../hooks/beforeDuplicatePiping";
|
import { beforeDuplicatePiping } from "../../hooks/beforeDuplicatePiping";
|
||||||
import { beforeDuplicateUnpublish } from "../../hooks/beforeDuplicateUnpublish";
|
import { beforeDuplicateUnpublish } from "../../hooks/beforeDuplicateUnpublish";
|
||||||
|
@ -33,190 +34,184 @@ const fields = {
|
||||||
updatedBy: "updatedBy",
|
updatedBy: "updatedBy",
|
||||||
} as const satisfies Record<string, string>;
|
} as const satisfies Record<string, string>;
|
||||||
|
|
||||||
export const Contents = buildVersionedCollectionConfig(
|
export const Contents = buildVersionedCollectionConfig({
|
||||||
Collections.Contents,
|
slug: Collections.Contents,
|
||||||
{
|
labels: {
|
||||||
singular: "Content",
|
singular: "Content",
|
||||||
plural: "Contents",
|
plural: "Contents",
|
||||||
},
|
},
|
||||||
() => ({
|
defaultSort: fields.slug,
|
||||||
defaultSort: fields.slug,
|
admin: {
|
||||||
admin: {
|
useAsTitle: fields.slug,
|
||||||
useAsTitle: fields.slug,
|
description:
|
||||||
description:
|
"All the contents (textual, audio, and video) from the Library or other online sources.",
|
||||||
"All the contents (textual, audio, and video) from the Library or other online sources.",
|
defaultColumns: [
|
||||||
defaultColumns: [
|
fields.slug,
|
||||||
fields.slug,
|
fields.thumbnail,
|
||||||
fields.thumbnail,
|
fields.categories,
|
||||||
fields.categories,
|
fields.type,
|
||||||
fields.type,
|
fields.translations,
|
||||||
fields.translations,
|
fields.status,
|
||||||
fields.status,
|
|
||||||
],
|
|
||||||
group: CollectionGroups.Collections,
|
|
||||||
hooks: {
|
|
||||||
beforeDuplicate: beforeDuplicatePiping([
|
|
||||||
beforeDuplicateUnpublish,
|
|
||||||
beforeDuplicateAddCopyTo(fields.slug),
|
|
||||||
]),
|
|
||||||
},
|
|
||||||
preview: (doc) => `https://accords-library.com/contents/${doc.slug}`,
|
|
||||||
},
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
type: "row",
|
|
||||||
fields: [
|
|
||||||
slugField({ name: fields.slug, admin: { width: "50%" } }),
|
|
||||||
imageField({
|
|
||||||
name: fields.thumbnail,
|
|
||||||
relationTo: Collections.ContentsThumbnails,
|
|
||||||
admin: { width: "50%" },
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "row",
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
name: fields.categories,
|
|
||||||
type: "relationship",
|
|
||||||
relationTo: [Collections.Keys],
|
|
||||||
filterOptions: { type: { equals: KeysTypes.Categories } },
|
|
||||||
hasMany: true,
|
|
||||||
admin: { allowCreate: false, width: "50%" },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: fields.type,
|
|
||||||
type: "relationship",
|
|
||||||
relationTo: [Collections.Keys],
|
|
||||||
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: Collections.Recorders,
|
|
||||||
hasMany: true,
|
|
||||||
admin: {
|
|
||||||
condition: (_, siblingData) =>
|
|
||||||
siblingData.language === siblingData.sourceLanguage,
|
|
||||||
width: "50%",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: fields.textTranslators,
|
|
||||||
label: "Translators",
|
|
||||||
type: "relationship",
|
|
||||||
relationTo: Collections.Recorders,
|
|
||||||
hasMany: true,
|
|
||||||
admin: {
|
|
||||||
condition: (_, siblingData) =>
|
|
||||||
siblingData.language !== siblingData.sourceLanguage,
|
|
||||||
width: "50%",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: fields.textProofreaders,
|
|
||||||
label: "Proofreaders",
|
|
||||||
type: "relationship",
|
|
||||||
relationTo: Collections.Recorders,
|
|
||||||
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%" },
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
],
|
],
|
||||||
})
|
group: CollectionGroups.Collections,
|
||||||
);
|
hooks: {
|
||||||
|
beforeDuplicate: beforeDuplicatePiping([
|
||||||
|
beforeDuplicateUnpublish,
|
||||||
|
beforeDuplicateAddCopyTo(fields.slug),
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
preview: (doc) => `https://accords-library.com/contents/${doc.slug}`,
|
||||||
|
},
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
type: "row",
|
||||||
|
fields: [
|
||||||
|
slugField({ name: fields.slug, admin: { width: "50%" } }),
|
||||||
|
imageField({
|
||||||
|
name: fields.thumbnail,
|
||||||
|
relationTo: Collections.ContentsThumbnails,
|
||||||
|
admin: { width: "50%" },
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "row",
|
||||||
|
fields: [
|
||||||
|
keysField({
|
||||||
|
name: fields.categories,
|
||||||
|
relationTo: KeysTypes.Categories,
|
||||||
|
hasMany: true,
|
||||||
|
admin: { allowCreate: false, width: "50%" },
|
||||||
|
}),
|
||||||
|
keysField({
|
||||||
|
name: fields.type,
|
||||||
|
relationTo: KeysTypes.Contents,
|
||||||
|
admin: { allowCreate: false, width: "50%" },
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
translatedFields({
|
||||||
|
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: Collections.Recorders,
|
||||||
|
hasMany: true,
|
||||||
|
admin: {
|
||||||
|
condition: (_, siblingData) =>
|
||||||
|
siblingData.language === siblingData.sourceLanguage,
|
||||||
|
width: "50%",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: fields.textTranslators,
|
||||||
|
label: "Translators",
|
||||||
|
type: "relationship",
|
||||||
|
relationTo: Collections.Recorders,
|
||||||
|
hasMany: true,
|
||||||
|
admin: {
|
||||||
|
condition: (_, siblingData) =>
|
||||||
|
siblingData.language !== siblingData.sourceLanguage,
|
||||||
|
width: "50%",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: fields.textProofreaders,
|
||||||
|
label: "Proofreaders",
|
||||||
|
type: "relationship",
|
||||||
|
relationTo: Collections.Recorders,
|
||||||
|
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,6 +1,6 @@
|
||||||
import { CollectionGroups, Collections } from "../../constants";
|
import { CollectionGroups, Collections } from "../../constants";
|
||||||
import { slugField } from "../../fields/slugField/slugField";
|
import { slugField } from "../../fields/slugField/slugField";
|
||||||
import { localizedFields } from "../../fields/translatedFields/translatedFields";
|
import { translatedFields } from "../../fields/translatedFields/translatedFields";
|
||||||
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
||||||
|
|
||||||
const fields = {
|
const fields = {
|
||||||
|
@ -11,51 +11,49 @@ const fields = {
|
||||||
contents: "contents",
|
contents: "contents",
|
||||||
} as const satisfies Record<string, string>;
|
} as const satisfies Record<string, string>;
|
||||||
|
|
||||||
export const ContentsFolders = buildCollectionConfig(
|
export const ContentsFolders = buildCollectionConfig({
|
||||||
Collections.ContentsFolders,
|
slug: Collections.ContentsFolders,
|
||||||
{
|
labels: {
|
||||||
singular: "Contents Folder",
|
singular: "Contents Folder",
|
||||||
plural: "Contents Folders",
|
plural: "Contents Folders",
|
||||||
},
|
},
|
||||||
() => ({
|
defaultSort: fields.slug,
|
||||||
defaultSort: fields.slug,
|
admin: {
|
||||||
admin: {
|
useAsTitle: fields.slug,
|
||||||
useAsTitle: fields.slug,
|
defaultColumns: [fields.slug, fields.translations],
|
||||||
defaultColumns: [fields.slug, fields.translations],
|
disableDuplicate: true,
|
||||||
disableDuplicate: true,
|
group: CollectionGroups.Collections,
|
||||||
group: CollectionGroups.Collections,
|
},
|
||||||
},
|
timestamps: false,
|
||||||
timestamps: false,
|
versions: false,
|
||||||
versions: false,
|
fields: [
|
||||||
fields: [
|
slugField({ name: fields.slug }),
|
||||||
slugField({ name: fields.slug }),
|
translatedFields({
|
||||||
localizedFields({
|
name: fields.translations,
|
||||||
name: fields.translations,
|
interfaceName: "ContentFoldersTranslation",
|
||||||
interfaceName: "ContentFoldersTranslation",
|
admin: {
|
||||||
admin: {
|
useAsTitle: fields.name,
|
||||||
useAsTitle: fields.name,
|
|
||||||
},
|
|
||||||
fields: [{ name: fields.name, type: "text", required: true }],
|
|
||||||
}),
|
|
||||||
{
|
|
||||||
type: "row",
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
type: "relationship",
|
|
||||||
name: fields.subfolders,
|
|
||||||
relationTo: Collections.ContentsFolders,
|
|
||||||
hasMany: true,
|
|
||||||
admin: { width: "50%" },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "relationship",
|
|
||||||
name: fields.contents,
|
|
||||||
relationTo: Collections.Contents,
|
|
||||||
hasMany: true,
|
|
||||||
admin: { width: "50%" },
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
],
|
fields: [{ name: fields.name, type: "text", required: true }],
|
||||||
})
|
}),
|
||||||
);
|
{
|
||||||
|
type: "row",
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
type: "relationship",
|
||||||
|
name: fields.subfolders,
|
||||||
|
relationTo: Collections.ContentsFolders,
|
||||||
|
hasMany: true,
|
||||||
|
admin: { width: "50%" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "relationship",
|
||||||
|
name: fields.contents,
|
||||||
|
relationTo: Collections.Contents,
|
||||||
|
hasMany: true,
|
||||||
|
admin: { width: "50%" },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { CollectionGroups, Collections } from "../../constants";
|
import { CollectionGroups, Collections } from "../../constants";
|
||||||
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
import { buildImageCollectionConfig } from "../../utils/imageCollectionConfig";
|
||||||
|
|
||||||
const fields = {
|
const fields = {
|
||||||
filename: "filename",
|
filename: "filename",
|
||||||
|
@ -7,43 +7,39 @@ const fields = {
|
||||||
filesize: "filesize",
|
filesize: "filesize",
|
||||||
} as const satisfies Record<string, string>;
|
} as const satisfies Record<string, string>;
|
||||||
|
|
||||||
export const ContentsThumbnails = buildCollectionConfig(
|
export const ContentsThumbnails = buildImageCollectionConfig({
|
||||||
Collections.ContentsThumbnails,
|
slug: Collections.ContentsThumbnails,
|
||||||
{
|
labels: {
|
||||||
singular: "Contents Thumbnail",
|
singular: "Contents Thumbnail",
|
||||||
plural: "Contents Thumbnails",
|
plural: "Contents Thumbnails",
|
||||||
},
|
},
|
||||||
({ uploadDir }) => ({
|
defaultSort: fields.filename,
|
||||||
defaultSort: fields.filename,
|
admin: {
|
||||||
admin: {
|
useAsTitle: fields.filename,
|
||||||
useAsTitle: fields.filename,
|
disableDuplicate: true,
|
||||||
disableDuplicate: true,
|
group: CollectionGroups.Media,
|
||||||
group: CollectionGroups.Media,
|
},
|
||||||
},
|
upload: {
|
||||||
upload: {
|
imageSizes: [
|
||||||
staticDir: uploadDir,
|
{
|
||||||
mimeTypes: ["image/*"],
|
name: "og",
|
||||||
imageSizes: [
|
height: 750,
|
||||||
{
|
width: 1125,
|
||||||
name: "og",
|
formatOptions: {
|
||||||
height: 750,
|
format: "jpg",
|
||||||
width: 1125,
|
options: { progressive: true, mozjpeg: true, compressionLevel: 9, quality: 80 },
|
||||||
formatOptions: {
|
|
||||||
format: "jpg",
|
|
||||||
options: { progressive: true, mozjpeg: true, compressionLevel: 9, quality: 80 },
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
},
|
||||||
name: "medium",
|
{
|
||||||
height: 1000,
|
name: "medium",
|
||||||
width: 1500,
|
height: 1000,
|
||||||
formatOptions: {
|
width: 1500,
|
||||||
format: "webp",
|
formatOptions: {
|
||||||
options: { effort: 6, quality: 80, alphaQuality: 80 },
|
format: "webp",
|
||||||
},
|
options: { effort: 6, quality: 80, alphaQuality: 80 },
|
||||||
},
|
},
|
||||||
],
|
},
|
||||||
},
|
],
|
||||||
fields: [],
|
},
|
||||||
})
|
fields: [],
|
||||||
);
|
});
|
||||||
|
|
|
@ -8,37 +8,35 @@ const fields = {
|
||||||
id: "id",
|
id: "id",
|
||||||
} as const satisfies Record<string, string>;
|
} as const satisfies Record<string, string>;
|
||||||
|
|
||||||
export const Currencies = buildCollectionConfig(
|
export const Currencies = buildCollectionConfig({
|
||||||
Collections.Currencies,
|
slug: Collections.Currencies,
|
||||||
{
|
labels: {
|
||||||
singular: "Currency",
|
singular: "Currency",
|
||||||
plural: "Currencies",
|
plural: "Currencies",
|
||||||
},
|
},
|
||||||
() => ({
|
defaultSort: fields.id,
|
||||||
defaultSort: fields.id,
|
admin: {
|
||||||
admin: {
|
pagination: { defaultLimit: 100 },
|
||||||
pagination: { defaultLimit: 100 },
|
useAsTitle: fields.id,
|
||||||
useAsTitle: fields.id,
|
defaultColumns: [fields.id],
|
||||||
defaultColumns: [fields.id],
|
disableDuplicate: true,
|
||||||
disableDuplicate: true,
|
group: CollectionGroups.Meta,
|
||||||
group: CollectionGroups.Meta,
|
},
|
||||||
},
|
access: { create: mustBeAdmin, update: mustBeAdmin },
|
||||||
access: { create: mustBeAdmin, update: mustBeAdmin },
|
endpoints: [importFromStrapi],
|
||||||
endpoints: [importFromStrapi],
|
timestamps: false,
|
||||||
timestamps: false,
|
fields: [
|
||||||
fields: [
|
{
|
||||||
{
|
name: fields.id,
|
||||||
name: fields.id,
|
type: "text",
|
||||||
type: "text",
|
unique: true,
|
||||||
unique: true,
|
required: true,
|
||||||
required: true,
|
validate: (value, options) => {
|
||||||
validate: (value, options) => {
|
if (!/^[A-Z]{3}$/g.test(value)) {
|
||||||
if (!/^[A-Z]{3}$/g.test(value)) {
|
return "The code must be a valid ISO 4217 currency code (e.g: EUR, CAD...)";
|
||||||
return "The code must be a valid ISO 4217 currency code (e.g: EUR, CAD...)";
|
}
|
||||||
}
|
return text(value, options);
|
||||||
return text(value, options);
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
],
|
},
|
||||||
})
|
],
|
||||||
);
|
});
|
||||||
|
|
|
@ -6,31 +6,29 @@ const fields = {
|
||||||
type: "type",
|
type: "type",
|
||||||
} as const satisfies Record<string, string>;
|
} as const satisfies Record<string, string>;
|
||||||
|
|
||||||
export const Files = buildCollectionConfig(
|
export const Files = buildCollectionConfig({
|
||||||
Collections.Files,
|
slug: Collections.Files,
|
||||||
{
|
labels: {
|
||||||
singular: "File",
|
singular: "File",
|
||||||
plural: "Files",
|
plural: "Files",
|
||||||
},
|
},
|
||||||
() => ({
|
defaultSort: fields.filename,
|
||||||
defaultSort: fields.filename,
|
admin: {
|
||||||
admin: {
|
useAsTitle: fields.filename,
|
||||||
useAsTitle: fields.filename,
|
disableDuplicate: true,
|
||||||
disableDuplicate: true,
|
group: CollectionGroups.Media,
|
||||||
group: CollectionGroups.Media,
|
},
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: fields.filename,
|
||||||
|
required: true,
|
||||||
|
type: "text",
|
||||||
},
|
},
|
||||||
fields: [
|
{
|
||||||
{
|
name: fields.type,
|
||||||
name: fields.filename,
|
type: "select",
|
||||||
required: true,
|
required: true,
|
||||||
type: "text",
|
options: Object.entries(FileTypes).map(([value, label]) => ({ label, value })),
|
||||||
},
|
},
|
||||||
{
|
],
|
||||||
name: fields.type,
|
});
|
||||||
type: "select",
|
|
||||||
required: true,
|
|
||||||
options: Object.entries(FileTypes).map(([value, label]) => ({ label, value })),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ import payload from "payload";
|
||||||
import { mustBeAdmin } from "../../accesses/mustBeAdmin";
|
import { mustBeAdmin } from "../../accesses/mustBeAdmin";
|
||||||
import { QuickFilters } from "../../components/QuickFilters";
|
import { QuickFilters } from "../../components/QuickFilters";
|
||||||
import { CollectionGroups, Collections, KeysTypes, LanguageCodes } from "../../constants";
|
import { CollectionGroups, Collections, KeysTypes, LanguageCodes } from "../../constants";
|
||||||
import { localizedFields } from "../../fields/translatedFields/translatedFields";
|
import { translatedFields } from "../../fields/translatedFields/translatedFields";
|
||||||
import { beforeDuplicateAddCopyTo } from "../../hooks/beforeDuplicateAddCopyTo";
|
import { beforeDuplicateAddCopyTo } from "../../hooks/beforeDuplicateAddCopyTo";
|
||||||
import { Key } from "../../types/collections";
|
import { Key } from "../../types/collections";
|
||||||
import { isDefined } from "../../utils/asserts";
|
import { isDefined } from "../../utils/asserts";
|
||||||
|
@ -19,103 +19,101 @@ const fields = {
|
||||||
|
|
||||||
const keysTypesWithShort: (keyof typeof KeysTypes)[] = ["Categories", "GamePlatforms"];
|
const keysTypesWithShort: (keyof typeof KeysTypes)[] = ["Categories", "GamePlatforms"];
|
||||||
|
|
||||||
export const Keys = buildCollectionConfig(
|
export const Keys = buildCollectionConfig({
|
||||||
Collections.Keys,
|
slug: Collections.Keys,
|
||||||
{
|
labels: {
|
||||||
singular: "Key",
|
singular: "Key",
|
||||||
plural: "Keys",
|
plural: "Keys",
|
||||||
},
|
},
|
||||||
() => ({
|
defaultSort: fields.name,
|
||||||
defaultSort: fields.name,
|
admin: {
|
||||||
admin: {
|
useAsTitle: fields.name,
|
||||||
useAsTitle: fields.name,
|
defaultColumns: [fields.name, fields.type, fields.translations],
|
||||||
defaultColumns: [fields.name, fields.type, fields.translations],
|
group: CollectionGroups.Meta,
|
||||||
group: CollectionGroups.Meta,
|
components: {
|
||||||
components: {
|
BeforeListTable: [
|
||||||
BeforeListTable: [
|
() =>
|
||||||
() =>
|
QuickFilters({
|
||||||
QuickFilters({
|
slug: Collections.Keys,
|
||||||
slug: Collections.Keys,
|
filterGroups: [
|
||||||
filterGroups: [
|
Object.entries(KeysTypes).map(([key, value]) => ({
|
||||||
Object.entries(KeysTypes).map(([key, value]) => ({
|
label: value,
|
||||||
label: value,
|
filter: { where: { type: { equals: key } } },
|
||||||
filter: { where: { type: { equals: key } } },
|
})),
|
||||||
})),
|
Object.entries(LanguageCodes).map(([key, value]) => ({
|
||||||
Object.entries(LanguageCodes).map(([key, value]) => ({
|
label: `∅ ${value}`,
|
||||||
label: `∅ ${value}`,
|
filter: { where: { "translations.language": { not_equals: key } } },
|
||||||
filter: { where: { "translations.language": { not_equals: key } } },
|
})),
|
||||||
})),
|
],
|
||||||
],
|
}),
|
||||||
}),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
hooks: {
|
|
||||||
beforeDuplicate: beforeDuplicateAddCopyTo(fields.name),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
access: {
|
|
||||||
create: mustBeAdmin,
|
|
||||||
delete: mustBeAdmin,
|
|
||||||
},
|
|
||||||
hooks: {
|
|
||||||
beforeValidate: [
|
|
||||||
async ({ data: { name, type } }) => {
|
|
||||||
const result = await payload.find({
|
|
||||||
collection: Collections.Keys,
|
|
||||||
where: { name: { equals: name }, type: { equals: type } },
|
|
||||||
});
|
|
||||||
if (result.docs.length > 0) {
|
|
||||||
throw new Error(
|
|
||||||
`A Key of type "${KeysTypes[type]}" already exists with the name "${name}"`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
endpoints: [importFromStrapi],
|
hooks: {
|
||||||
timestamps: false,
|
beforeDuplicate: beforeDuplicateAddCopyTo(fields.name),
|
||||||
versions: false,
|
},
|
||||||
fields: [
|
},
|
||||||
{
|
access: {
|
||||||
name: fields.name,
|
create: mustBeAdmin,
|
||||||
type: "text",
|
delete: mustBeAdmin,
|
||||||
required: true,
|
},
|
||||||
|
hooks: {
|
||||||
|
beforeValidate: [
|
||||||
|
async ({ data: { name, type } }) => {
|
||||||
|
const result = await payload.find({
|
||||||
|
collection: Collections.Keys,
|
||||||
|
where: { name: { equals: name }, type: { equals: type } },
|
||||||
|
});
|
||||||
|
if (result.docs.length > 0) {
|
||||||
|
throw new Error(
|
||||||
|
`A Key of type "${KeysTypes[type]}" already exists with the name "${name}"`
|
||||||
|
);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: fields.type,
|
|
||||||
type: "select",
|
|
||||||
required: true,
|
|
||||||
options: Object.entries(KeysTypes).map(([value, label]) => ({ label, value })),
|
|
||||||
},
|
|
||||||
localizedFields({
|
|
||||||
name: fields.translations,
|
|
||||||
interfaceName: "CategoryTranslations",
|
|
||||||
admin: {
|
|
||||||
useAsTitle: fields.translationsName,
|
|
||||||
},
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
type: "row",
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
name: fields.translationsName,
|
|
||||||
type: "text",
|
|
||||||
required: true,
|
|
||||||
admin: { width: "50%" },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: fields.translationsShort,
|
|
||||||
type: "text",
|
|
||||||
admin: {
|
|
||||||
condition: (data: Partial<Key>) =>
|
|
||||||
isDefined(data.type) && keysTypesWithShort.includes(data.type),
|
|
||||||
width: "50%",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
],
|
],
|
||||||
})
|
},
|
||||||
);
|
endpoints: [importFromStrapi],
|
||||||
|
timestamps: false,
|
||||||
|
versions: false,
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: fields.name,
|
||||||
|
type: "text",
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: fields.type,
|
||||||
|
type: "select",
|
||||||
|
required: true,
|
||||||
|
options: Object.entries(KeysTypes).map(([value, label]) => ({ label, value })),
|
||||||
|
},
|
||||||
|
translatedFields({
|
||||||
|
name: fields.translations,
|
||||||
|
interfaceName: "CategoryTranslations",
|
||||||
|
admin: {
|
||||||
|
useAsTitle: fields.translationsName,
|
||||||
|
},
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
type: "row",
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: fields.translationsName,
|
||||||
|
type: "text",
|
||||||
|
required: true,
|
||||||
|
admin: { width: "50%" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: fields.translationsShort,
|
||||||
|
type: "text",
|
||||||
|
admin: {
|
||||||
|
condition: (data: Partial<Key>) =>
|
||||||
|
isDefined(data.type) && keysTypesWithShort.includes(data.type),
|
||||||
|
width: "50%",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
|
@ -10,43 +10,42 @@ const fields = {
|
||||||
name: "name",
|
name: "name",
|
||||||
} as const satisfies Record<string, string>;
|
} as const satisfies Record<string, string>;
|
||||||
|
|
||||||
export const Languages = buildCollectionConfig(
|
export const Languages = buildCollectionConfig({
|
||||||
Collections.Languages,
|
slug: Collections.Languages,
|
||||||
{
|
labels: {
|
||||||
singular: "Language",
|
singular: "Language",
|
||||||
plural: "Languages",
|
plural: "Languages",
|
||||||
},
|
},
|
||||||
() => ({
|
defaultSort: fields.name,
|
||||||
defaultSort: fields.name,
|
admin: {
|
||||||
admin: {
|
useAsTitle: fields.name,
|
||||||
useAsTitle: fields.name,
|
defaultColumns: [fields.name, fields.id],
|
||||||
defaultColumns: [fields.name, fields.id],
|
disableDuplicate: true,
|
||||||
disableDuplicate: true,
|
group: CollectionGroups.Meta,
|
||||||
group: CollectionGroups.Meta,
|
pagination: { defaultLimit: 100 },
|
||||||
pagination: { defaultLimit: 100 },
|
},
|
||||||
|
access: { create: mustBeAdmin, update: mustBeAdmin, read: publicAccess },
|
||||||
|
timestamps: false,
|
||||||
|
endpoints: [importFromStrapi],
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: fields.id,
|
||||||
|
type: "text",
|
||||||
|
unique: true,
|
||||||
|
required: true,
|
||||||
|
validate: (value, options) => {
|
||||||
|
if (!/^[a-z]{2}(-[a-z]{2})?$/g.test(value)) {
|
||||||
|
return "The code must be a valid BCP 47 language \
|
||||||
|
tag and lowercase (i.e: en, pt-pt, fr, zh-tw...)";
|
||||||
|
}
|
||||||
|
return text(value, options);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
access: { create: mustBeAdmin, update: mustBeAdmin, read: publicAccess },
|
{
|
||||||
timestamps: false,
|
name: fields.name,
|
||||||
endpoints: [importFromStrapi],
|
type: "text",
|
||||||
fields: [
|
unique: true,
|
||||||
{
|
required: true,
|
||||||
name: fields.id,
|
},
|
||||||
type: "text",
|
],
|
||||||
unique: true,
|
});
|
||||||
required: true,
|
|
||||||
validate: (value, options) => {
|
|
||||||
if (!/^[a-z]{2}(-[a-z]{2})?$/g.test(value)) {
|
|
||||||
return "The code must be a valid BCP 47 language tag and lowercase (i.e: en, pt-pt, fr, zh-tw...)";
|
|
||||||
}
|
|
||||||
return text(value, options);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: fields.name,
|
|
||||||
type: "text",
|
|
||||||
unique: true,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,44 @@
|
||||||
|
import { CollectionGroups, Collections } from "../../constants";
|
||||||
|
import { buildImageCollectionConfig } from "../../utils/imageCollectionConfig";
|
||||||
|
|
||||||
|
const fields = {
|
||||||
|
filename: "filename",
|
||||||
|
mimeType: "mimeType",
|
||||||
|
filesize: "filesize",
|
||||||
|
} as const satisfies Record<string, string>;
|
||||||
|
|
||||||
|
export const LibraryItemsGallery = buildImageCollectionConfig({
|
||||||
|
slug: Collections.LibraryItemsGallery,
|
||||||
|
labels: {
|
||||||
|
singular: "Library Item Gallery",
|
||||||
|
plural: "Library Item Gallery",
|
||||||
|
},
|
||||||
|
defaultSort: fields.filename,
|
||||||
|
admin: {
|
||||||
|
useAsTitle: fields.filename,
|
||||||
|
disableDuplicate: true,
|
||||||
|
group: CollectionGroups.Media,
|
||||||
|
},
|
||||||
|
upload: {
|
||||||
|
imageSizes: [
|
||||||
|
{
|
||||||
|
name: "small",
|
||||||
|
height: 512,
|
||||||
|
width: 512,
|
||||||
|
fit: "cover",
|
||||||
|
formatOptions: {
|
||||||
|
format: "webp",
|
||||||
|
options: { effort: 6, quality: 60, alphaQuality: 60 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "max",
|
||||||
|
formatOptions: {
|
||||||
|
format: "webp",
|
||||||
|
options: { effort: 6, quality: 80, alphaQuality: 80 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
fields: [],
|
||||||
|
});
|
|
@ -0,0 +1,57 @@
|
||||||
|
import { CollectionGroups, Collections } from "../../constants";
|
||||||
|
import { buildImageCollectionConfig } from "../../utils/imageCollectionConfig";
|
||||||
|
|
||||||
|
const fields = {
|
||||||
|
filename: "filename",
|
||||||
|
mimeType: "mimeType",
|
||||||
|
filesize: "filesize",
|
||||||
|
} as const satisfies Record<string, string>;
|
||||||
|
|
||||||
|
export const LibraryItemsScans = buildImageCollectionConfig({
|
||||||
|
slug: Collections.LibraryItemsScans,
|
||||||
|
labels: {
|
||||||
|
singular: "Library Item Scans",
|
||||||
|
plural: "Library Item Scans",
|
||||||
|
},
|
||||||
|
defaultSort: fields.filename,
|
||||||
|
admin: {
|
||||||
|
useAsTitle: fields.filename,
|
||||||
|
disableDuplicate: true,
|
||||||
|
group: CollectionGroups.Media,
|
||||||
|
},
|
||||||
|
upload: {
|
||||||
|
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: "large",
|
||||||
|
height: 2048,
|
||||||
|
width: 2048,
|
||||||
|
fit: "contain",
|
||||||
|
formatOptions: {
|
||||||
|
format: "webp",
|
||||||
|
options: { effort: 6, quality: 80, alphaQuality: 80 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
fields: [],
|
||||||
|
});
|
|
@ -1,5 +1,5 @@
|
||||||
import { CollectionGroups, Collections } from "../../constants";
|
import { CollectionGroups, Collections } from "../../constants";
|
||||||
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
import { buildImageCollectionConfig } from "../../utils/imageCollectionConfig";
|
||||||
|
|
||||||
const fields = {
|
const fields = {
|
||||||
filename: "filename",
|
filename: "filename",
|
||||||
|
@ -7,55 +7,49 @@ const fields = {
|
||||||
filesize: "filesize",
|
filesize: "filesize",
|
||||||
} as const satisfies Record<string, string>;
|
} as const satisfies Record<string, string>;
|
||||||
|
|
||||||
export const LibraryItemsThumbnails = buildCollectionConfig(
|
export const LibraryItemsThumbnails = buildImageCollectionConfig({
|
||||||
Collections.LibraryItemsThumbnails,
|
slug: Collections.LibraryItemsThumbnails,
|
||||||
{
|
labels: {
|
||||||
singular: "Library Item Thumbnail",
|
singular: "Library Item Thumbnail",
|
||||||
plural: "Library Item Thumbnails",
|
plural: "Library Item Thumbnails",
|
||||||
},
|
},
|
||||||
({ uploadDir }) => ({
|
defaultSort: fields.filename,
|
||||||
defaultSort: fields.filename,
|
admin: {
|
||||||
admin: {
|
useAsTitle: fields.filename,
|
||||||
useAsTitle: fields.filename,
|
disableDuplicate: true,
|
||||||
disableDuplicate: true,
|
group: CollectionGroups.Media,
|
||||||
group: CollectionGroups.Media,
|
},
|
||||||
},
|
upload: {
|
||||||
upload: {
|
imageSizes: [
|
||||||
staticDir: uploadDir,
|
{
|
||||||
mimeTypes: ["image/*"],
|
name: "og",
|
||||||
imageSizes: [
|
height: 1024,
|
||||||
{
|
width: 1024,
|
||||||
name: "og",
|
fit: "inside",
|
||||||
height: 1024,
|
formatOptions: {
|
||||||
width: 1024,
|
format: "jpg",
|
||||||
fit: "contain",
|
options: { progressive: true, mozjpeg: true, compressionLevel: 9, quality: 80 },
|
||||||
formatOptions: {
|
|
||||||
format: "jpg",
|
|
||||||
options: { progressive: true, mozjpeg: true, compressionLevel: 9, quality: 80 },
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
},
|
||||||
name: "medium",
|
{
|
||||||
height: 1024,
|
name: "square",
|
||||||
width: 1024,
|
height: 1024,
|
||||||
fit: "contain",
|
width: 1024,
|
||||||
formatOptions: {
|
fit: "contain",
|
||||||
format: "webp",
|
background: { r: 0, g: 0, b: 0, alpha: 0 },
|
||||||
options: { effort: 6, quality: 80, alphaQuality: 80 },
|
formatOptions: {
|
||||||
},
|
format: "webp",
|
||||||
|
options: { effort: 6, quality: 80, alphaQuality: 80 },
|
||||||
},
|
},
|
||||||
{
|
},
|
||||||
name: "large",
|
{
|
||||||
height: 2048,
|
name: "max",
|
||||||
width: 2048,
|
formatOptions: {
|
||||||
fit: "contain",
|
format: "webp",
|
||||||
formatOptions: {
|
options: { effort: 6, quality: 80, alphaQuality: 80 },
|
||||||
format: "webp",
|
|
||||||
options: { effort: 6, quality: 80, alphaQuality: 80 },
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
],
|
},
|
||||||
},
|
],
|
||||||
fields: [],
|
},
|
||||||
})
|
fields: [],
|
||||||
);
|
});
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { QuickFilters, publishStatusFilters } from "../../components/QuickFilter
|
||||||
import { CollectionGroups, Collections, KeysTypes } from "../../constants";
|
import { CollectionGroups, Collections, KeysTypes } from "../../constants";
|
||||||
import { imageField } from "../../fields/imageField/imageField";
|
import { imageField } from "../../fields/imageField/imageField";
|
||||||
import { slugField } from "../../fields/slugField/slugField";
|
import { slugField } from "../../fields/slugField/slugField";
|
||||||
import { localizedFields } from "../../fields/translatedFields/translatedFields";
|
import { translatedFields } from "../../fields/translatedFields/translatedFields";
|
||||||
import { beforeDuplicateAddCopyTo } from "../../hooks/beforeDuplicateAddCopyTo";
|
import { beforeDuplicateAddCopyTo } from "../../hooks/beforeDuplicateAddCopyTo";
|
||||||
import { beforeDuplicatePiping } from "../../hooks/beforeDuplicatePiping";
|
import { beforeDuplicatePiping } from "../../hooks/beforeDuplicatePiping";
|
||||||
import { beforeDuplicateUnpublish } from "../../hooks/beforeDuplicateUnpublish";
|
import { beforeDuplicateUnpublish } from "../../hooks/beforeDuplicateUnpublish";
|
||||||
|
@ -26,151 +26,146 @@ const fields = {
|
||||||
proofreaders: "proofreaders",
|
proofreaders: "proofreaders",
|
||||||
} as const satisfies Record<string, string>;
|
} as const satisfies Record<string, string>;
|
||||||
|
|
||||||
export const Posts = buildVersionedCollectionConfig(
|
export const Posts = buildVersionedCollectionConfig({
|
||||||
Collections.Posts,
|
slug: Collections.Posts,
|
||||||
{
|
labels: {
|
||||||
singular: "Post",
|
singular: "Post",
|
||||||
plural: "Posts",
|
plural: "Posts",
|
||||||
},
|
},
|
||||||
() => ({
|
defaultSort: fields.slug,
|
||||||
defaultSort: fields.slug,
|
admin: {
|
||||||
admin: {
|
useAsTitle: fields.slug,
|
||||||
useAsTitle: fields.slug,
|
description:
|
||||||
description:
|
"News articles written by our Recorders! Here you will find announcements about \
|
||||||
"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],
|
defaultColumns: [fields.slug, fields.thumbnail, fields.categories],
|
||||||
group: CollectionGroups.Collections,
|
group: CollectionGroups.Collections,
|
||||||
components: {
|
components: {
|
||||||
BeforeListTable: [
|
BeforeListTable: [
|
||||||
() =>
|
() =>
|
||||||
QuickFilters({
|
QuickFilters({
|
||||||
slug: Collections.Posts,
|
slug: Collections.Posts,
|
||||||
filterGroups: [publishStatusFilters],
|
filterGroups: [publishStatusFilters],
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
},
|
|
||||||
hooks: {
|
|
||||||
beforeDuplicate: beforeDuplicatePiping([
|
|
||||||
beforeDuplicateUnpublish,
|
|
||||||
beforeDuplicateAddCopyTo(fields.slug),
|
|
||||||
]),
|
|
||||||
},
|
|
||||||
preview: (doc) => `https://accords-library.com/news/${doc.slug}`,
|
|
||||||
},
|
},
|
||||||
hooks: {
|
hooks: {
|
||||||
beforeValidate: [removeTranslatorsForTranscripts],
|
beforeDuplicate: beforeDuplicatePiping([
|
||||||
|
beforeDuplicateUnpublish,
|
||||||
|
beforeDuplicateAddCopyTo(fields.slug),
|
||||||
|
]),
|
||||||
},
|
},
|
||||||
fields: [
|
preview: (doc) => `https://accords-library.com/news/${doc.slug}`,
|
||||||
{
|
},
|
||||||
type: "row",
|
hooks: {
|
||||||
fields: [
|
beforeValidate: [removeTranslatorsForTranscripts],
|
||||||
slugField({ name: fields.slug, admin: { width: "50%" } }),
|
},
|
||||||
imageField({
|
fields: [
|
||||||
name: fields.thumbnail,
|
{
|
||||||
relationTo: Collections.PostsThumbnails,
|
type: "row",
|
||||||
admin: { width: "50%" },
|
fields: [
|
||||||
}),
|
slugField({ name: fields.slug, admin: { width: "50%" } }),
|
||||||
],
|
imageField({
|
||||||
},
|
name: fields.thumbnail,
|
||||||
{
|
relationTo: Collections.PostsThumbnails,
|
||||||
type: "row",
|
admin: { width: "50%" },
|
||||||
fields: [
|
}),
|
||||||
{
|
],
|
||||||
name: fields.authors,
|
},
|
||||||
type: "relationship",
|
{
|
||||||
relationTo: [Collections.Recorders],
|
type: "row",
|
||||||
required: true,
|
fields: [
|
||||||
minRows: 1,
|
{
|
||||||
hasMany: true,
|
name: fields.authors,
|
||||||
admin: { width: "35%" },
|
type: "relationship",
|
||||||
},
|
relationTo: [Collections.Recorders],
|
||||||
{
|
required: true,
|
||||||
name: fields.categories,
|
minRows: 1,
|
||||||
type: "relationship",
|
hasMany: true,
|
||||||
relationTo: [Collections.Keys],
|
admin: { width: "35%" },
|
||||||
filterOptions: { type: { equals: KeysTypes.Categories } },
|
},
|
||||||
hasMany: true,
|
{
|
||||||
admin: { allowCreate: false, width: "35%" },
|
name: fields.categories,
|
||||||
},
|
type: "relationship",
|
||||||
],
|
relationTo: [Collections.Keys],
|
||||||
},
|
filterOptions: { type: { equals: KeysTypes.Categories } },
|
||||||
localizedFields({
|
hasMany: true,
|
||||||
name: fields.translations,
|
admin: { allowCreate: false, width: "35%" },
|
||||||
admin: { useAsTitle: fields.title, hasSourceLanguage: true },
|
},
|
||||||
required: true,
|
],
|
||||||
minRows: 1,
|
},
|
||||||
fields: [
|
translatedFields({
|
||||||
{ name: fields.title, type: "text", required: true },
|
name: fields.translations,
|
||||||
{ name: fields.summary, type: "textarea" },
|
admin: { useAsTitle: fields.title, hasSourceLanguage: true },
|
||||||
{
|
required: true,
|
||||||
type: "row",
|
minRows: 1,
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{ name: fields.title, type: "text", required: true },
|
||||||
name: fields.translators,
|
{ name: fields.summary, type: "textarea" },
|
||||||
type: "relationship",
|
{
|
||||||
relationTo: Collections.Recorders,
|
type: "row",
|
||||||
hasMany: true,
|
fields: [
|
||||||
admin: {
|
{
|
||||||
condition: (_, siblingData) => {
|
name: fields.translators,
|
||||||
if (
|
type: "relationship",
|
||||||
isUndefined(siblingData.language) ||
|
relationTo: Collections.Recorders,
|
||||||
isUndefined(siblingData.sourceLanguage)
|
hasMany: true,
|
||||||
) {
|
admin: {
|
||||||
return false;
|
condition: (_, siblingData) => {
|
||||||
}
|
|
||||||
return siblingData.language !== siblingData.sourceLanguage;
|
|
||||||
},
|
|
||||||
width: "50%",
|
|
||||||
},
|
|
||||||
validate: (translators, { siblingData }) => {
|
|
||||||
if (
|
if (
|
||||||
isUndefined(siblingData.language) ||
|
isUndefined(siblingData.language) ||
|
||||||
isUndefined(siblingData.sourceLanguage)
|
isUndefined(siblingData.sourceLanguage)
|
||||||
) {
|
) {
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
if (siblingData.language === siblingData.sourceLanguage) {
|
return 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 }) => {
|
||||||
name: fields.proofreaders,
|
if (isUndefined(siblingData.language) || isUndefined(siblingData.sourceLanguage)) {
|
||||||
type: "relationship",
|
return true;
|
||||||
relationTo: Collections.Recorders,
|
}
|
||||||
hasMany: true,
|
if (siblingData.language === siblingData.sourceLanguage) {
|
||||||
admin: { width: "50%" },
|
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.content, type: "richText", admin: { hideGutter: true } },
|
name: fields.proofreaders,
|
||||||
],
|
type: "relationship",
|
||||||
}),
|
relationTo: Collections.Recorders,
|
||||||
{
|
hasMany: true,
|
||||||
name: fields.publishedDate,
|
admin: { width: "50%" },
|
||||||
type: "date",
|
},
|
||||||
defaultValue: new Date().toISOString(),
|
],
|
||||||
admin: {
|
|
||||||
date: { pickerAppearance: "dayOnly", displayFormat: "yyyy-MM-dd" },
|
|
||||||
position: "sidebar",
|
|
||||||
},
|
},
|
||||||
required: true,
|
{ 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,
|
||||||
name: fields.hidden,
|
},
|
||||||
type: "checkbox",
|
{
|
||||||
required: false,
|
name: fields.hidden,
|
||||||
defaultValue: false,
|
type: "checkbox",
|
||||||
admin: {
|
required: false,
|
||||||
description: "If enabled, the post won't appear in the 'News' section",
|
defaultValue: false,
|
||||||
position: "sidebar",
|
admin: {
|
||||||
},
|
description: "If enabled, the post won't appear in the 'News' section",
|
||||||
|
position: "sidebar",
|
||||||
},
|
},
|
||||||
],
|
},
|
||||||
})
|
],
|
||||||
);
|
});
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { CollectionGroups, Collections } from "../../constants";
|
import { CollectionGroups, Collections } from "../../constants";
|
||||||
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
import { buildImageCollectionConfig } from "../../utils/imageCollectionConfig";
|
||||||
|
|
||||||
const fields = {
|
const fields = {
|
||||||
filename: "filename",
|
filename: "filename",
|
||||||
|
@ -7,43 +7,39 @@ const fields = {
|
||||||
filesize: "filesize",
|
filesize: "filesize",
|
||||||
} as const satisfies Record<string, string>;
|
} as const satisfies Record<string, string>;
|
||||||
|
|
||||||
export const PostsThumbnails = buildCollectionConfig(
|
export const PostsThumbnails = buildImageCollectionConfig({
|
||||||
Collections.PostsThumbnails,
|
slug: Collections.PostsThumbnails,
|
||||||
{
|
labels: {
|
||||||
singular: "Post Thumbnail",
|
singular: "Post Thumbnail",
|
||||||
plural: "Post Thumbnails",
|
plural: "Post Thumbnails",
|
||||||
},
|
},
|
||||||
({ uploadDir }) => ({
|
defaultSort: fields.filename,
|
||||||
defaultSort: fields.filename,
|
admin: {
|
||||||
admin: {
|
useAsTitle: fields.filename,
|
||||||
useAsTitle: fields.filename,
|
disableDuplicate: true,
|
||||||
disableDuplicate: true,
|
group: CollectionGroups.Media,
|
||||||
group: CollectionGroups.Media,
|
},
|
||||||
},
|
upload: {
|
||||||
upload: {
|
imageSizes: [
|
||||||
staticDir: uploadDir,
|
{
|
||||||
mimeTypes: ["image/*"],
|
name: "og",
|
||||||
imageSizes: [
|
height: 750,
|
||||||
{
|
width: 1125,
|
||||||
name: "og",
|
formatOptions: {
|
||||||
height: 750,
|
format: "jpg",
|
||||||
width: 1125,
|
options: { progressive: true, mozjpeg: true, compressionLevel: 9, quality: 80 },
|
||||||
formatOptions: {
|
|
||||||
format: "jpg",
|
|
||||||
options: { progressive: true, mozjpeg: true, compressionLevel: 9, quality: 80 },
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
},
|
||||||
name: "medium",
|
{
|
||||||
height: 1000,
|
name: "medium",
|
||||||
width: 1500,
|
height: 1000,
|
||||||
formatOptions: {
|
width: 1500,
|
||||||
format: "webp",
|
formatOptions: {
|
||||||
options: { effort: 6, quality: 80, alphaQuality: 80 },
|
format: "webp",
|
||||||
},
|
options: { effort: 6, quality: 80, alphaQuality: 80 },
|
||||||
},
|
},
|
||||||
],
|
},
|
||||||
},
|
],
|
||||||
fields: [],
|
},
|
||||||
})
|
fields: [],
|
||||||
);
|
});
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { mustBeAdmin } from "../../accesses/mustBeAdmin";
|
||||||
import { QuickFilters } from "../../components/QuickFilters";
|
import { QuickFilters } from "../../components/QuickFilters";
|
||||||
import { CollectionGroups, Collections, RecordersRoles } from "../../constants";
|
import { CollectionGroups, Collections, RecordersRoles } from "../../constants";
|
||||||
import { imageField } from "../../fields/imageField/imageField";
|
import { imageField } from "../../fields/imageField/imageField";
|
||||||
import { localizedFields } from "../../fields/translatedFields/translatedFields";
|
import { translatedFields } from "../../fields/translatedFields/translatedFields";
|
||||||
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
||||||
import { importFromStrapi } from "./endpoints/importFromStrapi";
|
import { importFromStrapi } from "./endpoints/importFromStrapi";
|
||||||
import { beforeLoginMustHaveAtLeastOneRole } from "./hooks/beforeLoginMustHaveAtLeastOneRole";
|
import { beforeLoginMustHaveAtLeastOneRole } from "./hooks/beforeLoginMustHaveAtLeastOneRole";
|
||||||
|
@ -18,127 +18,125 @@ const fields = {
|
||||||
role: "role",
|
role: "role",
|
||||||
} as const satisfies Record<string, string>;
|
} as const satisfies Record<string, string>;
|
||||||
|
|
||||||
export const Recorders = buildCollectionConfig(
|
export const Recorders = buildCollectionConfig({
|
||||||
Collections.Recorders,
|
slug: Collections.Recorders,
|
||||||
{
|
labels: {
|
||||||
singular: "Recorder",
|
singular: "Recorder",
|
||||||
plural: "Recorders",
|
plural: "Recorders",
|
||||||
},
|
},
|
||||||
() => ({
|
defaultSort: fields.username,
|
||||||
defaultSort: fields.username,
|
admin: {
|
||||||
admin: {
|
useAsTitle: fields.username,
|
||||||
useAsTitle: fields.username,
|
description:
|
||||||
description:
|
"Recorders are contributors of the Accord's Library project. Ask an admin to create a \
|
||||||
"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.",
|
Recorder here to be able to credit them in other collections.",
|
||||||
defaultColumns: [
|
defaultColumns: [
|
||||||
fields.username,
|
fields.username,
|
||||||
fields.avatar,
|
fields.avatar,
|
||||||
fields.anonymize,
|
fields.anonymize,
|
||||||
fields.biographies,
|
fields.biographies,
|
||||||
fields.languages,
|
fields.languages,
|
||||||
fields.role,
|
fields.role,
|
||||||
],
|
|
||||||
disableDuplicate: true,
|
|
||||||
group: CollectionGroups.Meta,
|
|
||||||
components: {
|
|
||||||
BeforeListTable: [
|
|
||||||
() =>
|
|
||||||
QuickFilters({
|
|
||||||
slug: Collections.Recorders,
|
|
||||||
filterGroups: [
|
|
||||||
[
|
|
||||||
...Object.entries(RecordersRoles).map(([key, value]) => ({
|
|
||||||
label: value,
|
|
||||||
filter: { where: { role: { equals: key } } },
|
|
||||||
})),
|
|
||||||
{
|
|
||||||
label: "∅ Role",
|
|
||||||
filter: { where: { role: { not_in: Object.keys(RecordersRoles).join(",") } } },
|
|
||||||
},
|
|
||||||
,
|
|
||||||
],
|
|
||||||
[{ label: "Anonymized", filter: { where: { anonymize: { equals: true } } } }],
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
auth: true,
|
|
||||||
access: {
|
|
||||||
unlock: mustBeAdmin,
|
|
||||||
update: mustBeAdminOrSelf,
|
|
||||||
delete: mustBeAdmin,
|
|
||||||
create: mustBeAdmin,
|
|
||||||
},
|
|
||||||
hooks: {
|
|
||||||
beforeLogin: [beforeLoginMustHaveAtLeastOneRole],
|
|
||||||
},
|
|
||||||
endpoints: [importFromStrapi],
|
|
||||||
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: Collections.RecordersThumbnails,
|
|
||||||
admin: { width: "66%" },
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: fields.languages,
|
|
||||||
type: "relationship",
|
|
||||||
relationTo: Collections.Languages,
|
|
||||||
hasMany: true,
|
|
||||||
admin: {
|
|
||||||
allowCreate: false,
|
|
||||||
description: "List of language(s) that this recorder is familiar with",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
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.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",
|
|
||||||
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",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
})
|
disableDuplicate: true,
|
||||||
);
|
group: CollectionGroups.Meta,
|
||||||
|
components: {
|
||||||
|
BeforeListTable: [
|
||||||
|
() =>
|
||||||
|
QuickFilters({
|
||||||
|
slug: Collections.Recorders,
|
||||||
|
filterGroups: [
|
||||||
|
[
|
||||||
|
...Object.entries(RecordersRoles).map(([key, value]) => ({
|
||||||
|
label: value,
|
||||||
|
filter: { where: { role: { equals: key } } },
|
||||||
|
})),
|
||||||
|
{
|
||||||
|
label: "∅ Role",
|
||||||
|
filter: { where: { role: { not_in: Object.keys(RecordersRoles).join(",") } } },
|
||||||
|
},
|
||||||
|
,
|
||||||
|
],
|
||||||
|
[{ label: "Anonymized", filter: { where: { anonymize: { equals: true } } } }],
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
auth: true,
|
||||||
|
access: {
|
||||||
|
unlock: mustBeAdmin,
|
||||||
|
update: mustBeAdminOrSelf,
|
||||||
|
delete: mustBeAdmin,
|
||||||
|
create: mustBeAdmin,
|
||||||
|
},
|
||||||
|
hooks: {
|
||||||
|
beforeLogin: [beforeLoginMustHaveAtLeastOneRole],
|
||||||
|
},
|
||||||
|
endpoints: [importFromStrapi],
|
||||||
|
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: Collections.RecordersThumbnails,
|
||||||
|
admin: { width: "66%" },
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: fields.languages,
|
||||||
|
type: "relationship",
|
||||||
|
relationTo: Collections.Languages,
|
||||||
|
hasMany: true,
|
||||||
|
admin: {
|
||||||
|
allowCreate: false,
|
||||||
|
description: "List of language(s) that this recorder is familiar with",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
translatedFields({
|
||||||
|
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.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",
|
||||||
|
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,6 +1,6 @@
|
||||||
import { CollectionGroups, Collections } from "../../constants";
|
import { CollectionGroups, Collections } from "../../constants";
|
||||||
import { backPropagationField } from "../../fields/backPropagationField/backPropagationField";
|
import { backPropagationField } from "../../fields/backPropagationField/backPropagationField";
|
||||||
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
import { buildImageCollectionConfig } from "../../utils/imageCollectionConfig";
|
||||||
|
|
||||||
const fields = {
|
const fields = {
|
||||||
filename: "filename",
|
filename: "filename",
|
||||||
|
@ -9,51 +9,46 @@ const fields = {
|
||||||
recorder: "recorder",
|
recorder: "recorder",
|
||||||
} as const satisfies Record<string, string>;
|
} as const satisfies Record<string, string>;
|
||||||
|
|
||||||
export const RecordersThumbnails = buildCollectionConfig(
|
export const RecordersThumbnails = buildImageCollectionConfig({
|
||||||
Collections.RecordersThumbnails,
|
slug: Collections.RecordersThumbnails,
|
||||||
{
|
labels: {
|
||||||
singular: "Recorders Thumbnail",
|
singular: "Recorders Thumbnail",
|
||||||
plural: "Recorders Thumbnails",
|
plural: "Recorders Thumbnails",
|
||||||
},
|
},
|
||||||
({ uploadDir }) => ({
|
defaultSort: fields.filename,
|
||||||
defaultSort: fields.filename,
|
admin: {
|
||||||
admin: {
|
useAsTitle: fields.filename,
|
||||||
useAsTitle: fields.filename,
|
disableDuplicate: true,
|
||||||
disableDuplicate: true,
|
group: CollectionGroups.Media,
|
||||||
group: CollectionGroups.Media,
|
},
|
||||||
},
|
upload: {
|
||||||
upload: {
|
imageSizes: [
|
||||||
staticDir: uploadDir,
|
{
|
||||||
adminThumbnail: "small",
|
name: "og",
|
||||||
mimeTypes: ["image/*"],
|
height: 256,
|
||||||
imageSizes: [
|
width: 256,
|
||||||
{
|
formatOptions: {
|
||||||
name: "og",
|
format: "jpg",
|
||||||
height: 256,
|
options: { progressive: true, mozjpeg: true, compressionLevel: 9, quality: 80 },
|
||||||
width: 256,
|
|
||||||
formatOptions: {
|
|
||||||
format: "jpg",
|
|
||||||
options: { progressive: true, mozjpeg: true, compressionLevel: 9, quality: 80 },
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
},
|
||||||
name: "small",
|
{
|
||||||
height: 128,
|
name: "small",
|
||||||
width: 128,
|
height: 128,
|
||||||
formatOptions: {
|
width: 128,
|
||||||
format: "webp",
|
formatOptions: {
|
||||||
options: { effort: 6, quality: 80, alphaQuality: 80 },
|
format: "webp",
|
||||||
},
|
options: { effort: 6, quality: 80, alphaQuality: 80 },
|
||||||
},
|
},
|
||||||
],
|
},
|
||||||
},
|
|
||||||
fields: [
|
|
||||||
backPropagationField({
|
|
||||||
name: fields.recorder,
|
|
||||||
hasMany: false,
|
|
||||||
relationTo: Collections.Recorders,
|
|
||||||
where: (id) => ({ avatar: { equals: id } }),
|
|
||||||
}),
|
|
||||||
],
|
],
|
||||||
})
|
},
|
||||||
);
|
fields: [
|
||||||
|
backPropagationField({
|
||||||
|
name: fields.recorder,
|
||||||
|
hasMany: false,
|
||||||
|
relationTo: Collections.Recorders,
|
||||||
|
where: ({ id }) => ({ avatar: { equals: id } }),
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
|
@ -17,87 +17,85 @@ const fields = {
|
||||||
channel: "channel",
|
channel: "channel",
|
||||||
} as const satisfies Record<string, string>;
|
} as const satisfies Record<string, string>;
|
||||||
|
|
||||||
export const Videos: CollectionConfig = buildCollectionConfig(
|
export const Videos: CollectionConfig = buildCollectionConfig({
|
||||||
Collections.Videos,
|
slug: Collections.Videos,
|
||||||
{
|
labels: {
|
||||||
singular: "Video",
|
singular: "Video",
|
||||||
plural: "Videos",
|
plural: "Videos",
|
||||||
},
|
},
|
||||||
() => ({
|
defaultSort: fields.uid,
|
||||||
defaultSort: fields.uid,
|
admin: {
|
||||||
admin: {
|
useAsTitle: fields.title,
|
||||||
useAsTitle: fields.title,
|
defaultColumns: [
|
||||||
defaultColumns: [
|
fields.uid,
|
||||||
fields.uid,
|
fields.title,
|
||||||
fields.title,
|
fields.source,
|
||||||
fields.source,
|
fields.gone,
|
||||||
fields.gone,
|
fields.liveChat,
|
||||||
fields.liveChat,
|
fields.publishedDate,
|
||||||
fields.publishedDate,
|
fields.views,
|
||||||
fields.views,
|
fields.likes,
|
||||||
fields.likes,
|
fields.channel,
|
||||||
fields.channel,
|
|
||||||
],
|
|
||||||
group: CollectionGroups.Media,
|
|
||||||
disableDuplicate: true,
|
|
||||||
},
|
|
||||||
access: {
|
|
||||||
create: mustBeAdmin,
|
|
||||||
delete: mustBeAdmin,
|
|
||||||
},
|
|
||||||
endpoints: [importFromStrapi],
|
|
||||||
timestamps: false,
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
type: "row",
|
|
||||||
fields: [
|
|
||||||
{ name: fields.uid, type: "text", required: true, unique: true, admin: { width: "33%" } },
|
|
||||||
{
|
|
||||||
name: fields.gone,
|
|
||||||
type: "checkbox",
|
|
||||||
defaultValue: false,
|
|
||||||
required: true,
|
|
||||||
admin: {
|
|
||||||
description:
|
|
||||||
"Is the video no longer available (deleted, privatized, unlisted, blocked...)",
|
|
||||||
width: "33%",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: fields.source,
|
|
||||||
type: "select",
|
|
||||||
required: true,
|
|
||||||
options: Object.entries(VideoSources).map(([value, label]) => ({ label, value })),
|
|
||||||
admin: { width: "33%" },
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
|
|
||||||
{ name: fields.title, type: "text", required: true },
|
|
||||||
{ name: fields.description, type: "textarea" },
|
|
||||||
{
|
|
||||||
type: "row",
|
|
||||||
fields: [
|
|
||||||
{ name: fields.likes, type: "number", admin: { width: "50%" } },
|
|
||||||
{ name: fields.views, type: "number", admin: { width: "50%" } },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: fields.publishedDate,
|
|
||||||
type: "date",
|
|
||||||
admin: {
|
|
||||||
date: { pickerAppearance: "dayOnly", displayFormat: "yyyy-MM-dd" },
|
|
||||||
position: "sidebar",
|
|
||||||
},
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: fields.channel,
|
|
||||||
type: "relationship",
|
|
||||||
relationTo: Collections.VideosChannels,
|
|
||||||
required: true,
|
|
||||||
admin: { position: "sidebar" },
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
})
|
group: CollectionGroups.Media,
|
||||||
);
|
disableDuplicate: true,
|
||||||
|
},
|
||||||
|
access: {
|
||||||
|
create: mustBeAdmin,
|
||||||
|
delete: mustBeAdmin,
|
||||||
|
},
|
||||||
|
endpoints: [importFromStrapi],
|
||||||
|
timestamps: false,
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
type: "row",
|
||||||
|
fields: [
|
||||||
|
{ name: fields.uid, type: "text", required: true, unique: true, admin: { width: "33%" } },
|
||||||
|
{
|
||||||
|
name: fields.gone,
|
||||||
|
type: "checkbox",
|
||||||
|
defaultValue: false,
|
||||||
|
required: true,
|
||||||
|
admin: {
|
||||||
|
description:
|
||||||
|
"Is the video no longer available (deleted, privatized, unlisted, blocked...)",
|
||||||
|
width: "33%",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: fields.source,
|
||||||
|
type: "select",
|
||||||
|
required: true,
|
||||||
|
options: Object.entries(VideoSources).map(([value, label]) => ({ label, value })),
|
||||||
|
admin: { width: "33%" },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
{ name: fields.title, type: "text", required: true },
|
||||||
|
{ name: fields.description, type: "textarea" },
|
||||||
|
{
|
||||||
|
type: "row",
|
||||||
|
fields: [
|
||||||
|
{ name: fields.likes, type: "number", admin: { width: "50%" } },
|
||||||
|
{ name: fields.views, type: "number", admin: { width: "50%" } },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: fields.publishedDate,
|
||||||
|
type: "date",
|
||||||
|
admin: {
|
||||||
|
date: { pickerAppearance: "dayOnly", displayFormat: "yyyy-MM-dd" },
|
||||||
|
position: "sidebar",
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: fields.channel,
|
||||||
|
type: "relationship",
|
||||||
|
relationTo: Collections.VideosChannels,
|
||||||
|
required: true,
|
||||||
|
admin: { position: "sidebar" },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
|
@ -11,35 +11,33 @@ const fields = {
|
||||||
videos: "videos",
|
videos: "videos",
|
||||||
} as const satisfies Record<string, string>;
|
} as const satisfies Record<string, string>;
|
||||||
|
|
||||||
export const VideosChannels: CollectionConfig = buildCollectionConfig(
|
export const VideosChannels: CollectionConfig = buildCollectionConfig({
|
||||||
Collections.VideosChannels,
|
slug: Collections.VideosChannels,
|
||||||
{
|
labels: {
|
||||||
singular: "Videos Channel",
|
singular: "Videos Channel",
|
||||||
plural: "Videos Channels",
|
plural: "Videos Channels",
|
||||||
},
|
},
|
||||||
() => ({
|
defaultSort: fields.title,
|
||||||
defaultSort: fields.title,
|
admin: {
|
||||||
admin: {
|
useAsTitle: fields.title,
|
||||||
useAsTitle: fields.title,
|
defaultColumns: [fields.uid, fields.title, fields.subscribers, fields.videos],
|
||||||
defaultColumns: [fields.uid, fields.title, fields.subscribers, fields.videos],
|
group: CollectionGroups.Media,
|
||||||
group: CollectionGroups.Media,
|
disableDuplicate: true,
|
||||||
disableDuplicate: true,
|
},
|
||||||
|
access: {
|
||||||
|
create: mustBeAdmin,
|
||||||
|
delete: mustBeAdmin,
|
||||||
|
},
|
||||||
|
endpoints: [importFromStrapi],
|
||||||
|
timestamps: false,
|
||||||
|
fields: [
|
||||||
|
{ name: fields.uid, type: "text", required: true, unique: true },
|
||||||
|
{
|
||||||
|
type: "row",
|
||||||
|
fields: [
|
||||||
|
{ name: fields.title, type: "text", required: true },
|
||||||
|
{ name: fields.subscribers, type: "number" },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
access: {
|
],
|
||||||
create: mustBeAdmin,
|
});
|
||||||
delete: mustBeAdmin,
|
|
||||||
},
|
|
||||||
endpoints: [importFromStrapi],
|
|
||||||
timestamps: false,
|
|
||||||
fields: [
|
|
||||||
{ name: fields.uid, type: "text", required: true, unique: true },
|
|
||||||
{
|
|
||||||
type: "row",
|
|
||||||
fields: [
|
|
||||||
{ name: fields.title, type: "text", required: true },
|
|
||||||
{ name: fields.subscribers, type: "number" },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import { CollectionGroups, Collections, KeysTypes } from "../../constants";
|
import { CollectionGroups, Collections, KeysTypes } from "../../constants";
|
||||||
import { imageField } from "../../fields/imageField/imageField";
|
import { imageField } from "../../fields/imageField/imageField";
|
||||||
|
import { keysField } from "../../fields/keysField/keysField";
|
||||||
import { slugField } from "../../fields/slugField/slugField";
|
import { slugField } from "../../fields/slugField/slugField";
|
||||||
import { localizedFields } from "../../fields/translatedFields/translatedFields";
|
import { translatedFields } from "../../fields/translatedFields/translatedFields";
|
||||||
import { buildVersionedCollectionConfig } from "../../utils/versionedCollectionConfig";
|
import { buildVersionedCollectionConfig } from "../../utils/versionedCollectionConfig";
|
||||||
import { AppearanceRowLabel } from "./components/AppearanceRowLabel";
|
import { AppearanceRowLabel } from "./components/AppearanceRowLabel";
|
||||||
import { importFromStrapi } from "./endpoints/importFromStrapi";
|
import { importFromStrapi } from "./endpoints/importFromStrapi";
|
||||||
|
@ -23,141 +24,135 @@ const fields = {
|
||||||
status: "_status",
|
status: "_status",
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Weapons = buildVersionedCollectionConfig(
|
export const Weapons = buildVersionedCollectionConfig({
|
||||||
Collections.Weapons,
|
slug: Collections.Weapons,
|
||||||
{ singular: "Weapon", plural: "Weapons" },
|
labels: { singular: "Weapon", plural: "Weapons" },
|
||||||
() => ({
|
defaultSort: fields.slug,
|
||||||
defaultSort: fields.slug,
|
admin: {
|
||||||
admin: {
|
useAsTitle: fields.slug,
|
||||||
useAsTitle: fields.slug,
|
defaultColumns: [
|
||||||
defaultColumns: [
|
fields.slug,
|
||||||
fields.slug,
|
fields.thumbnail,
|
||||||
fields.thumbnail,
|
fields.group,
|
||||||
fields.group,
|
fields.type,
|
||||||
fields.type,
|
fields.appearances,
|
||||||
fields.appearances,
|
fields.status,
|
||||||
fields.status,
|
|
||||||
],
|
|
||||||
group: CollectionGroups.Collections,
|
|
||||||
},
|
|
||||||
endpoints: [importFromStrapi],
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
type: "row",
|
|
||||||
fields: [
|
|
||||||
slugField({ name: fields.slug, admin: { width: "50%" } }),
|
|
||||||
imageField({
|
|
||||||
name: fields.thumbnail,
|
|
||||||
relationTo: Collections.WeaponsThumbnails,
|
|
||||||
admin: { width: "50%" },
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "row",
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
name: fields.type,
|
|
||||||
type: "relationship",
|
|
||||||
relationTo: Collections.Keys,
|
|
||||||
required: true,
|
|
||||||
filterOptions: { type: { equals: KeysTypes.Weapons } },
|
|
||||||
admin: { allowCreate: false, width: "50%" },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: fields.group,
|
|
||||||
type: "relationship",
|
|
||||||
relationTo: Collections.WeaponsGroups,
|
|
||||||
admin: { width: "50%" },
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: fields.appearances,
|
|
||||||
type: "array",
|
|
||||||
required: true,
|
|
||||||
minRows: 1,
|
|
||||||
admin: {
|
|
||||||
initCollapsed: true,
|
|
||||||
components: {
|
|
||||||
RowLabel: ({ data }) =>
|
|
||||||
AppearanceRowLabel({ keyIds: data[fields.appearancesCategories] ?? [] }),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
name: fields.appearancesCategories,
|
|
||||||
type: "relationship",
|
|
||||||
required: true,
|
|
||||||
hasMany: true,
|
|
||||||
relationTo: Collections.Keys,
|
|
||||||
filterOptions: { type: { equals: KeysTypes.Categories } },
|
|
||||||
admin: { allowCreate: false },
|
|
||||||
},
|
|
||||||
localizedFields({
|
|
||||||
name: fields.appearancesTranslations,
|
|
||||||
required: true,
|
|
||||||
minRows: 1,
|
|
||||||
admin: {
|
|
||||||
useAsTitle: fields.appearancesTranslationsName,
|
|
||||||
hasSourceLanguage: true,
|
|
||||||
hasCredits: true,
|
|
||||||
},
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
type: "row",
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
name: fields.appearancesTranslationsName,
|
|
||||||
type: "text",
|
|
||||||
required: true,
|
|
||||||
admin: { width: "50%" },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: fields.appearancesTranslationsDescription,
|
|
||||||
type: "textarea",
|
|
||||||
admin: { width: "50%" },
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "row",
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
name: fields.appearancesTranslationsLevel1,
|
|
||||||
label: "Level 1",
|
|
||||||
type: "textarea",
|
|
||||||
admin: { width: "50%" },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: fields.appearancesTranslationsLevel2,
|
|
||||||
label: "Level 2",
|
|
||||||
type: "textarea",
|
|
||||||
admin: { width: "50%" },
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "row",
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
name: fields.appearancesTranslationsLevel3,
|
|
||||||
label: "Level 3",
|
|
||||||
type: "textarea",
|
|
||||||
admin: { width: "50%" },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: fields.appearancesTranslationsLevel4,
|
|
||||||
label: "Level 4",
|
|
||||||
type: "textarea",
|
|
||||||
admin: { width: "50%" },
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
})
|
group: CollectionGroups.Collections,
|
||||||
);
|
},
|
||||||
|
endpoints: [importFromStrapi],
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
type: "row",
|
||||||
|
fields: [
|
||||||
|
slugField({ name: fields.slug, admin: { width: "50%" } }),
|
||||||
|
imageField({
|
||||||
|
name: fields.thumbnail,
|
||||||
|
relationTo: Collections.WeaponsThumbnails,
|
||||||
|
admin: { width: "50%" },
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "row",
|
||||||
|
fields: [
|
||||||
|
keysField({
|
||||||
|
name: fields.type,
|
||||||
|
relationTo: KeysTypes.Weapons,
|
||||||
|
required: true,
|
||||||
|
admin: { allowCreate: false, width: "50%" },
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
name: fields.group,
|
||||||
|
type: "relationship",
|
||||||
|
relationTo: Collections.WeaponsGroups,
|
||||||
|
admin: { width: "50%" },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: fields.appearances,
|
||||||
|
type: "array",
|
||||||
|
required: true,
|
||||||
|
minRows: 1,
|
||||||
|
admin: {
|
||||||
|
initCollapsed: true,
|
||||||
|
components: {
|
||||||
|
RowLabel: ({ data }) =>
|
||||||
|
AppearanceRowLabel({ keyIds: data[fields.appearancesCategories] ?? [] }),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
fields: [
|
||||||
|
keysField({
|
||||||
|
name: fields.appearancesCategories,
|
||||||
|
required: true,
|
||||||
|
hasMany: true,
|
||||||
|
relationTo: KeysTypes.Categories,
|
||||||
|
admin: { allowCreate: false },
|
||||||
|
}),
|
||||||
|
translatedFields({
|
||||||
|
name: fields.appearancesTranslations,
|
||||||
|
required: true,
|
||||||
|
minRows: 1,
|
||||||
|
admin: {
|
||||||
|
useAsTitle: fields.appearancesTranslationsName,
|
||||||
|
hasSourceLanguage: true,
|
||||||
|
hasCredits: true,
|
||||||
|
},
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
type: "row",
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: fields.appearancesTranslationsName,
|
||||||
|
type: "text",
|
||||||
|
required: true,
|
||||||
|
admin: { width: "50%" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: fields.appearancesTranslationsDescription,
|
||||||
|
type: "textarea",
|
||||||
|
admin: { width: "50%" },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "row",
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: fields.appearancesTranslationsLevel1,
|
||||||
|
label: "Level 1",
|
||||||
|
type: "textarea",
|
||||||
|
admin: { width: "50%" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: fields.appearancesTranslationsLevel2,
|
||||||
|
label: "Level 2",
|
||||||
|
type: "textarea",
|
||||||
|
admin: { width: "50%" },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "row",
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: fields.appearancesTranslationsLevel3,
|
||||||
|
label: "Level 3",
|
||||||
|
type: "textarea",
|
||||||
|
admin: { width: "50%" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: fields.appearancesTranslationsLevel4,
|
||||||
|
label: "Level 4",
|
||||||
|
type: "textarea",
|
||||||
|
admin: { width: "50%" },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { CollectionGroups, Collections } from "../../constants";
|
import { CollectionGroups, Collections } from "../../constants";
|
||||||
import { backPropagationField } from "../../fields/backPropagationField/backPropagationField";
|
import { backPropagationField } from "../../fields/backPropagationField/backPropagationField";
|
||||||
import { slugField } from "../../fields/slugField/slugField";
|
import { slugField } from "../../fields/slugField/slugField";
|
||||||
import { localizedFields } from "../../fields/translatedFields/translatedFields";
|
import { translatedFields } from "../../fields/translatedFields/translatedFields";
|
||||||
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
||||||
|
|
||||||
const fields = {
|
const fields = {
|
||||||
|
@ -12,30 +12,28 @@ const fields = {
|
||||||
weapons: "weapons",
|
weapons: "weapons",
|
||||||
};
|
};
|
||||||
|
|
||||||
export const WeaponsGroups = buildCollectionConfig(
|
export const WeaponsGroups = buildCollectionConfig({
|
||||||
Collections.WeaponsGroups,
|
slug: Collections.WeaponsGroups,
|
||||||
{ singular: "Weapons Group", plural: "Weapon Groups" },
|
labels: { singular: "Weapons Group", plural: "Weapon Groups" },
|
||||||
() => ({
|
defaultSort: fields.slug,
|
||||||
defaultSort: fields.slug,
|
admin: {
|
||||||
admin: {
|
useAsTitle: fields.slug,
|
||||||
useAsTitle: fields.slug,
|
defaultColumns: [fields.slug, fields.translations, fields.weapons, fields.subgroupOf],
|
||||||
defaultColumns: [fields.slug, fields.translations, fields.weapons, fields.subgroupOf],
|
group: CollectionGroups.Collections,
|
||||||
group: CollectionGroups.Collections,
|
},
|
||||||
},
|
timestamps: false,
|
||||||
timestamps: false,
|
fields: [
|
||||||
fields: [
|
slugField({ name: fields.slug }),
|
||||||
slugField({ name: fields.slug }),
|
translatedFields({
|
||||||
localizedFields({
|
name: fields.translations,
|
||||||
name: fields.translations,
|
admin: { useAsTitle: fields.translationsName },
|
||||||
admin: { useAsTitle: fields.translationsName },
|
fields: [{ name: fields.translationsName, type: "text", required: true }],
|
||||||
fields: [{ name: fields.translationsName, type: "text", required: true }],
|
}),
|
||||||
}),
|
backPropagationField({
|
||||||
backPropagationField({
|
name: fields.weapons,
|
||||||
name: fields.weapons,
|
relationTo: Collections.Weapons,
|
||||||
relationTo: Collections.Weapons,
|
hasMany: true,
|
||||||
hasMany: true,
|
where: ({ id }) => ({ group: { equals: id } }),
|
||||||
where: (id) => ({ group: { equals: id } }),
|
}),
|
||||||
}),
|
],
|
||||||
],
|
});
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { CollectionGroups, Collections } from "../../constants";
|
import { CollectionGroups, Collections } from "../../constants";
|
||||||
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
import { buildImageCollectionConfig } from "../../utils/imageCollectionConfig";
|
||||||
|
|
||||||
const fields = {
|
const fields = {
|
||||||
filename: "filename",
|
filename: "filename",
|
||||||
|
@ -7,44 +7,60 @@ const fields = {
|
||||||
filesize: "filesize",
|
filesize: "filesize",
|
||||||
} as const satisfies Record<string, string>;
|
} as const satisfies Record<string, string>;
|
||||||
|
|
||||||
export const WeaponsThumbnails = buildCollectionConfig(
|
export const WeaponsThumbnails = buildImageCollectionConfig({
|
||||||
Collections.WeaponsThumbnails,
|
slug: Collections.WeaponsThumbnails,
|
||||||
{
|
labels: {
|
||||||
singular: "Weapons Thumbnail",
|
singular: "Weapons Thumbnail",
|
||||||
plural: "Weapons Thumbnails",
|
plural: "Weapons Thumbnails",
|
||||||
},
|
},
|
||||||
({ uploadDir }) => ({
|
defaultSort: fields.filename,
|
||||||
defaultSort: fields.filename,
|
admin: {
|
||||||
admin: {
|
useAsTitle: fields.filename,
|
||||||
useAsTitle: fields.filename,
|
disableDuplicate: true,
|
||||||
disableDuplicate: true,
|
group: CollectionGroups.Media,
|
||||||
group: CollectionGroups.Media,
|
},
|
||||||
},
|
upload: {
|
||||||
upload: {
|
imageSizes: [
|
||||||
staticDir: uploadDir,
|
{
|
||||||
adminThumbnail: "small",
|
name: "og",
|
||||||
mimeTypes: ["image/*"],
|
height: 512,
|
||||||
imageSizes: [
|
width: 512,
|
||||||
{
|
fit: "inside",
|
||||||
name: "og",
|
formatOptions: {
|
||||||
height: 256,
|
format: "jpg",
|
||||||
width: 256,
|
options: { progressive: true, mozjpeg: true, compressionLevel: 9, quality: 80 },
|
||||||
formatOptions: {
|
|
||||||
format: "jpg",
|
|
||||||
options: { progressive: true, mozjpeg: true, compressionLevel: 9, quality: 80 },
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
},
|
||||||
name: "small",
|
{
|
||||||
height: 128,
|
name: "small",
|
||||||
width: 128,
|
height: 256,
|
||||||
formatOptions: {
|
width: 256,
|
||||||
format: "webp",
|
fit: "contain",
|
||||||
options: { effort: 6, quality: 80, alphaQuality: 80 },
|
background: { r: 0, g: 0, b: 0, alpha: 0 },
|
||||||
},
|
formatOptions: {
|
||||||
|
format: "webp",
|
||||||
|
options: { effort: 6, quality: 70, alphaQuality: 70 },
|
||||||
},
|
},
|
||||||
],
|
},
|
||||||
},
|
{
|
||||||
fields: [],
|
name: "medium",
|
||||||
})
|
height: 1024,
|
||||||
);
|
width: 1024,
|
||||||
|
fit: "contain",
|
||||||
|
background: { r: 0, g: 0, b: 0, alpha: 0 },
|
||||||
|
formatOptions: {
|
||||||
|
format: "webp",
|
||||||
|
options: { effort: 6, quality: 80, alphaQuality: 80 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "max",
|
||||||
|
formatOptions: {
|
||||||
|
format: "webp",
|
||||||
|
options: { effort: 6, quality: 80, alphaQuality: 80 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
fields: [],
|
||||||
|
});
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import React from "react";
|
|
||||||
import { styled } from "styled-components";
|
import { styled } from "styled-components";
|
||||||
|
|
||||||
export const Icon = styled.div`
|
export const Icon = styled.div`
|
||||||
|
@ -9,4 +8,8 @@ export const Icon = styled.div`
|
||||||
mask-size: contain;
|
mask-size: contain;
|
||||||
mask-repeat: no-repeat;
|
mask-repeat: no-repeat;
|
||||||
mask-position: center;
|
mask-position: center;
|
||||||
|
-webkit-mask: url("/public/accords.svg");
|
||||||
|
-webkit-mask-size: contain;
|
||||||
|
-webkit-mask-repeat: no-repeat;
|
||||||
|
-webkit-mask-position: center;
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React from "react";
|
|
||||||
import "@fontsource/vollkorn/700.css";
|
import "@fontsource/vollkorn/700.css";
|
||||||
|
import React from "react";
|
||||||
import { styled } from "styled-components";
|
import { styled } from "styled-components";
|
||||||
|
|
||||||
export const Logo = (): JSX.Element => (
|
export const Logo = (): JSX.Element => (
|
||||||
|
@ -34,4 +34,8 @@ const Icon = styled.div`
|
||||||
mask-size: contain;
|
mask-size: contain;
|
||||||
mask-repeat: no-repeat;
|
mask-repeat: no-repeat;
|
||||||
mask-position: center;
|
mask-position: center;
|
||||||
|
-webkit-mask: url("/public/accords.svg");
|
||||||
|
-webkit-mask-size: contain;
|
||||||
|
-webkit-mask-repeat: no-repeat;
|
||||||
|
-webkit-mask-position: center;
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -10,6 +10,8 @@ export enum Collections {
|
||||||
Languages = "languages",
|
Languages = "languages",
|
||||||
LibraryItems = "library-items",
|
LibraryItems = "library-items",
|
||||||
LibraryItemsThumbnails = "library-items-thumbnails",
|
LibraryItemsThumbnails = "library-items-thumbnails",
|
||||||
|
LibraryItemsScans = "library-items-scans",
|
||||||
|
LibraryItemsGallery = "library-items-gallery",
|
||||||
Posts = "posts",
|
Posts = "posts",
|
||||||
PostsThumbnails = "posts-thumbnails",
|
PostsThumbnails = "posts-thumbnails",
|
||||||
Recorders = "recorders",
|
Recorders = "recorders",
|
||||||
|
@ -87,4 +89,5 @@ export enum CollectionStatus {
|
||||||
export enum VideoSources {
|
export enum VideoSources {
|
||||||
YouTube = "YouTube",
|
YouTube = "YouTube",
|
||||||
NicoNico = "NicoNico",
|
NicoNico = "NicoNico",
|
||||||
|
Tumblr = "Tumblr",
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,7 +39,7 @@ type Params<T> = {
|
||||||
payload: {
|
payload: {
|
||||||
collection: string;
|
collection: string;
|
||||||
import?: (strapiObject: any, user: any) => Promise<void>;
|
import?: (strapiObject: any, user: any) => Promise<void>;
|
||||||
convert?: (strapiObject: any) => PayloadCreateData<T>;
|
convert?: (strapiObject: any, user: any) => PayloadCreateData<T>;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ export const importStrapiEntries = async <T>({
|
||||||
} else if (isDefined(payloadParams.convert)) {
|
} else if (isDefined(payloadParams.convert)) {
|
||||||
await payload.create({
|
await payload.create({
|
||||||
collection: payloadParams.collection,
|
collection: payloadParams.collection,
|
||||||
data: payloadParams.convert(attributes),
|
data: payloadParams.convert(attributes, user),
|
||||||
user,
|
user,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -4,18 +4,20 @@ import { RelationshipField, Where } from "payload/types";
|
||||||
import { isNotEmpty } from "../../utils/asserts";
|
import { isNotEmpty } from "../../utils/asserts";
|
||||||
|
|
||||||
type BackPropagationField = FieldBase & {
|
type BackPropagationField = FieldBase & {
|
||||||
where: (id: string) => Where;
|
where: (data: any) => Where;
|
||||||
relationTo: string;
|
relationTo: string;
|
||||||
hasMany: boolean;
|
hasMany?: boolean;
|
||||||
};
|
};
|
||||||
export const backPropagationField = ({
|
export const backPropagationField = ({
|
||||||
admin,
|
admin,
|
||||||
hooks: { beforeChange = [], afterRead = [], ...otherHooks } = {},
|
hooks: { beforeChange = [], afterRead = [], ...otherHooks } = {},
|
||||||
where,
|
where,
|
||||||
|
hasMany = false,
|
||||||
...params
|
...params
|
||||||
}: BackPropagationField): RelationshipField => ({
|
}: BackPropagationField): RelationshipField => ({
|
||||||
...params,
|
...params,
|
||||||
type: "relationship",
|
type: "relationship",
|
||||||
|
hasMany: hasMany,
|
||||||
admin: { ...admin, readOnly: true },
|
admin: { ...admin, readOnly: true },
|
||||||
hooks: {
|
hooks: {
|
||||||
...otherHooks,
|
...otherHooks,
|
||||||
|
@ -31,17 +33,17 @@ export const backPropagationField = ({
|
||||||
if (isNotEmpty(data.id)) {
|
if (isNotEmpty(data.id)) {
|
||||||
const result = await payload.find({
|
const result = await payload.find({
|
||||||
collection: params.relationTo,
|
collection: params.relationTo,
|
||||||
where: where(data.id),
|
where: where(data),
|
||||||
limit: 100,
|
limit: 100,
|
||||||
depth: 0,
|
depth: 0,
|
||||||
});
|
});
|
||||||
if (params.hasMany) {
|
if (hasMany) {
|
||||||
return result.docs.map((doc) => doc.id);
|
return result.docs.map((doc) => doc.id);
|
||||||
} else {
|
} else {
|
||||||
return result.docs[0].id;
|
return result.docs[0].id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return params.hasMany ? [] : undefined;
|
return hasMany ? [] : undefined;
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,9 +1,23 @@
|
||||||
import { Props } from "payload/components/views/Cell";
|
import { Props } from "payload/components/views/Cell";
|
||||||
import { useState, useEffect } from "react";
|
import React, { useEffect, useMemo, useState } from "react";
|
||||||
import React from "react";
|
import { Link } from "react-router-dom";
|
||||||
|
import { styled } from "styled-components";
|
||||||
import { isUndefined } from "../../utils/asserts";
|
import { isUndefined } from "../../utils/asserts";
|
||||||
|
|
||||||
export const Cell = ({ cellData, field }: Props): JSX.Element => {
|
const Image = styled.img`
|
||||||
|
height: 3rem;
|
||||||
|
width: 3rem;
|
||||||
|
object-fit: contain;
|
||||||
|
transition: 0.2s transform;
|
||||||
|
transition-timing-function: cubic-bezier(0.075, 0.82, 0.165, 1);
|
||||||
|
position: absolute;
|
||||||
|
transform: translateY(-50%) scale(1);
|
||||||
|
&:hover {
|
||||||
|
transform: translateY(-50%) scale(3);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const Cell = ({ cellData, field, rowData, collection }: Props): JSX.Element => {
|
||||||
const [imageURL, setImageURL] = useState<string>();
|
const [imageURL, setImageURL] = useState<string>();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchUrl = async () => {
|
const fetchUrl = async () => {
|
||||||
|
@ -11,18 +25,14 @@ export const Cell = ({ cellData, field }: Props): JSX.Element => {
|
||||||
if (typeof cellData !== "string") return;
|
if (typeof cellData !== "string") return;
|
||||||
if (field.type !== "upload") return;
|
if (field.type !== "upload") return;
|
||||||
const result = await (await fetch(`/api/${field.relationTo}/${cellData}`)).json();
|
const result = await (await fetch(`/api/${field.relationTo}/${cellData}`)).json();
|
||||||
setImageURL(result.url);
|
setImageURL(result.sizes.thumb.url);
|
||||||
};
|
};
|
||||||
fetchUrl();
|
fetchUrl();
|
||||||
}, [cellData]);
|
}, [cellData]);
|
||||||
|
const link = useMemo(
|
||||||
return (
|
() => `/admin/collections/${collection.slug}/${rowData.id}`,
|
||||||
<>
|
[collection.slug, rowData.id]
|
||||||
{imageURL ? (
|
|
||||||
<img style={{ height: "3rem", borderRadius: "100%", aspectRatio: "1/1" }} src={imageURL} />
|
|
||||||
) : (
|
|
||||||
"<No Image>"
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return <Link to={link}>{imageURL ? <Image src={imageURL} /> : "<No Image>"}</Link>;
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
import { FieldBase, RelationshipField } from "payload/dist/fields/config/types";
|
||||||
|
import { Collections, KeysTypes } from "../../constants";
|
||||||
|
|
||||||
|
type KeysField = FieldBase & {
|
||||||
|
relationTo: KeysTypes;
|
||||||
|
hasMany?: boolean;
|
||||||
|
admin: RelationshipField["admin"];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const keysField = ({
|
||||||
|
relationTo,
|
||||||
|
hasMany = false,
|
||||||
|
...props
|
||||||
|
}: KeysField): RelationshipField => ({
|
||||||
|
...props,
|
||||||
|
type: "relationship",
|
||||||
|
hasMany: hasMany,
|
||||||
|
relationTo: Collections.Keys,
|
||||||
|
filterOptions: { type: { equals: getKeysTypesKey(relationTo) } },
|
||||||
|
});
|
||||||
|
|
||||||
|
const getKeysTypesKey = (keyType: KeysTypes): string =>
|
||||||
|
Object.entries(KeysTypes).find(([, value]) => value === keyType)[0];
|
|
@ -1,8 +1,7 @@
|
||||||
import { array } from "payload/dist/fields/validations";
|
import { array } from "payload/dist/fields/validations";
|
||||||
import { ArrayField, Field } from "payload/types";
|
import { ArrayField, Field } from "payload/types";
|
||||||
import { Collections } from "../../constants";
|
import { Collections } from "../../constants";
|
||||||
import { isDefined, isUndefined } from "../../utils/asserts";
|
import { hasDuplicates, isDefined, isUndefined } from "../../utils/asserts";
|
||||||
import { hasDuplicates } from "../../utils/validation";
|
|
||||||
import { Cell } from "./Cell";
|
import { Cell } from "./Cell";
|
||||||
import { RowLabel } from "./RowLabel";
|
import { RowLabel } from "./RowLabel";
|
||||||
|
|
||||||
|
@ -14,7 +13,7 @@ const fieldsNames = {
|
||||||
proofreaders: "proofreaders",
|
proofreaders: "proofreaders",
|
||||||
} as const satisfies Record<string, string>;
|
} as const satisfies Record<string, string>;
|
||||||
|
|
||||||
type LocalizedFieldsProps = Omit<ArrayField, "type" | "admin" | "validate"> & {
|
type LocalizedFieldsProps = Omit<ArrayField, "type" | "admin"> & {
|
||||||
admin?: ArrayField["admin"] & {
|
admin?: ArrayField["admin"] & {
|
||||||
useAsTitle?: string;
|
useAsTitle?: string;
|
||||||
hasSourceLanguage?: boolean;
|
hasSourceLanguage?: boolean;
|
||||||
|
@ -43,7 +42,8 @@ const creditFields: Field = {
|
||||||
type: "row",
|
type: "row",
|
||||||
admin: {
|
admin: {
|
||||||
condition: (_, siblingData) =>
|
condition: (_, siblingData) =>
|
||||||
isDefined(siblingData.language) && isDefined(siblingData.sourceLanguage),
|
isDefined(siblingData[fieldsNames.language]) &&
|
||||||
|
isDefined(siblingData[fieldsNames.sourceLanguage]),
|
||||||
},
|
},
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
|
@ -52,18 +52,28 @@ const creditFields: Field = {
|
||||||
type: "relationship",
|
type: "relationship",
|
||||||
relationTo: "recorders",
|
relationTo: "recorders",
|
||||||
hasMany: true,
|
hasMany: true,
|
||||||
|
hooks: {
|
||||||
|
beforeChange: [
|
||||||
|
({ siblingData }) => {
|
||||||
|
if (siblingData[fieldsNames.language] !== siblingData[fieldsNames.sourceLanguage]) {
|
||||||
|
delete siblingData[fieldsNames.transcribers];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
admin: {
|
admin: {
|
||||||
condition: (_, siblingData) => siblingData.language === siblingData.sourceLanguage,
|
condition: (_, siblingData) => siblingData.language === siblingData.sourceLanguage,
|
||||||
width: "50%",
|
width: "50%",
|
||||||
},
|
},
|
||||||
validate: (count, { siblingData }) => {
|
validate: (count, { siblingData }) => {
|
||||||
if (siblingData.language !== siblingData.sourceLanguage) {
|
if (siblingData[fieldsNames.language] !== siblingData[fieldsNames.sourceLanguage]) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (isDefined(count) && count.length > 0) {
|
if (isDefined(count) && count.length > 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return "This field is required when the language is the same as the source language.";
|
return `This field is required when the ${fieldsNames.language} \
|
||||||
|
is the same as the ${fieldsNames.sourceLanguage}.`;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -72,18 +82,29 @@ const creditFields: Field = {
|
||||||
type: "relationship",
|
type: "relationship",
|
||||||
relationTo: "recorders",
|
relationTo: "recorders",
|
||||||
hasMany: true,
|
hasMany: true,
|
||||||
|
hooks: {
|
||||||
|
beforeChange: [
|
||||||
|
({ siblingData }) => {
|
||||||
|
if (siblingData[fieldsNames.language] === siblingData[fieldsNames.sourceLanguage]) {
|
||||||
|
delete siblingData[fieldsNames.translators];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
admin: {
|
admin: {
|
||||||
condition: (_, siblingData) => siblingData.language !== siblingData.sourceLanguage,
|
condition: (_, siblingData) =>
|
||||||
|
siblingData[fieldsNames.language] !== siblingData[fieldsNames.sourceLanguage],
|
||||||
width: "50%",
|
width: "50%",
|
||||||
},
|
},
|
||||||
validate: (count, { siblingData }) => {
|
validate: (count, { siblingData }) => {
|
||||||
if (siblingData.language === siblingData.sourceLanguage) {
|
if (siblingData[fieldsNames.language] === siblingData[fieldsNames.sourceLanguage]) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (isDefined(count) && count.length > 0) {
|
if (isDefined(count) && count.length > 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return "This field is required when the language is different from the source language.";
|
return `This field is required when the ${fieldsNames.language} \
|
||||||
|
is different from the ${fieldsNames.sourceLanguage}.`;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -97,8 +118,9 @@ const creditFields: Field = {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
export const localizedFields = ({
|
export const translatedFields = ({
|
||||||
fields,
|
fields,
|
||||||
|
validate,
|
||||||
admin: { useAsTitle, hasSourceLanguage, hasCredits, ...admin } = {},
|
admin: { useAsTitle, hasSourceLanguage, hasCredits, ...admin } = {},
|
||||||
...otherProps
|
...otherProps
|
||||||
}: LocalizedFieldsProps): ArrayField => ({
|
}: LocalizedFieldsProps): ArrayField => ({
|
||||||
|
@ -111,13 +133,13 @@ export const localizedFields = ({
|
||||||
Cell({
|
Cell({
|
||||||
cellData:
|
cellData:
|
||||||
cellData?.map((row) => ({
|
cellData?.map((row) => ({
|
||||||
language: row.language,
|
language: row[fieldsNames.language],
|
||||||
title: isDefined(useAsTitle) ? row[useAsTitle] : undefined,
|
title: isDefined(useAsTitle) ? row[useAsTitle] : undefined,
|
||||||
})) ?? [],
|
})) ?? [],
|
||||||
}),
|
}),
|
||||||
RowLabel: ({ data }) =>
|
RowLabel: ({ data }) =>
|
||||||
RowLabel({
|
RowLabel({
|
||||||
language: data.language,
|
language: data[fieldsNames.language],
|
||||||
title: isDefined(useAsTitle) ? data[useAsTitle] : undefined,
|
title: isDefined(useAsTitle) ? data[useAsTitle] : undefined,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
@ -127,11 +149,16 @@ export const localizedFields = ({
|
||||||
const defaultValidation = array(value, options);
|
const defaultValidation = array(value, options);
|
||||||
if (defaultValidation !== true) return defaultValidation;
|
if (defaultValidation !== true) return defaultValidation;
|
||||||
|
|
||||||
|
if (isDefined(validate)) {
|
||||||
|
const propsValidation = validate(value, options);
|
||||||
|
if (propsValidation !== true) return propsValidation;
|
||||||
|
}
|
||||||
|
|
||||||
const data = options.data[otherProps.name] as ArrayData;
|
const data = options.data[otherProps.name] as ArrayData;
|
||||||
if (isUndefined(data)) return true;
|
if (isUndefined(data)) return true;
|
||||||
if (typeof data === "number") return true;
|
if (typeof data === "number") return true;
|
||||||
|
|
||||||
const languages = data.map((biography) => biography.language);
|
const languages = data.map((rows) => rows[fieldsNames.language]);
|
||||||
if (hasDuplicates(languages)) {
|
if (hasDuplicates(languages)) {
|
||||||
return `There cannot be multiple ${otherProps.name} with the same ${fieldsNames.language}`;
|
return `There cannot be multiple ${otherProps.name} with the same ${fieldsNames.language}`;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,8 @@ import { Files } from "./collections/Files/Files";
|
||||||
import { Keys } from "./collections/Keys/Keys";
|
import { Keys } from "./collections/Keys/Keys";
|
||||||
import { Languages } from "./collections/Languages/Languages";
|
import { Languages } from "./collections/Languages/Languages";
|
||||||
import { LibraryItems } from "./collections/LibraryItems/LibraryItems";
|
import { LibraryItems } from "./collections/LibraryItems/LibraryItems";
|
||||||
|
import { LibraryItemsGallery } from "./collections/LibraryItemsGallery/LibraryItemsGallery";
|
||||||
|
import { LibraryItemsScans } from "./collections/LibraryItemsScans/LibraryItemsScans";
|
||||||
import { LibraryItemsThumbnails } from "./collections/LibraryItemsThumbnails/LibraryItemsThumbnails";
|
import { LibraryItemsThumbnails } from "./collections/LibraryItemsThumbnails/LibraryItemsThumbnails";
|
||||||
import { Posts } from "./collections/Posts/Posts";
|
import { Posts } from "./collections/Posts/Posts";
|
||||||
import { PostsThumbnails } from "./collections/PostsThumbnails/PostsThumbnails";
|
import { PostsThumbnails } from "./collections/PostsThumbnails/PostsThumbnails";
|
||||||
|
@ -48,6 +50,8 @@ export default buildConfig({
|
||||||
WeaponsThumbnails,
|
WeaponsThumbnails,
|
||||||
ContentsThumbnails,
|
ContentsThumbnails,
|
||||||
LibraryItemsThumbnails,
|
LibraryItemsThumbnails,
|
||||||
|
LibraryItemsScans,
|
||||||
|
LibraryItemsGallery,
|
||||||
RecordersThumbnails,
|
RecordersThumbnails,
|
||||||
PostsThumbnails,
|
PostsThumbnails,
|
||||||
Files,
|
Files,
|
||||||
|
|
|
@ -36,6 +36,8 @@ export interface Config {
|
||||||
"weapons-thumbnails": WeaponsThumbnail;
|
"weapons-thumbnails": WeaponsThumbnail;
|
||||||
"contents-thumbnails": ContentsThumbnail;
|
"contents-thumbnails": ContentsThumbnail;
|
||||||
"library-items-thumbnails": LibraryItemThumbnail;
|
"library-items-thumbnails": LibraryItemThumbnail;
|
||||||
|
"library-items-scans": LibraryItemScans;
|
||||||
|
"library-items-gallery": LibraryItemGallery;
|
||||||
"recorders-thumbnails": RecordersThumbnail;
|
"recorders-thumbnails": RecordersThumbnail;
|
||||||
"posts-thumbnails": PostThumbnail;
|
"posts-thumbnails": PostThumbnail;
|
||||||
files: File;
|
files: File;
|
||||||
|
@ -50,6 +52,7 @@ export interface Config {
|
||||||
}
|
}
|
||||||
export interface LibraryItem {
|
export interface LibraryItem {
|
||||||
id: string;
|
id: string;
|
||||||
|
itemType?: "Textual" | "Audio" | "Video" | "Game" | "Other";
|
||||||
slug: string;
|
slug: string;
|
||||||
thumbnail?: string | LibraryItemThumbnail;
|
thumbnail?: string | LibraryItemThumbnail;
|
||||||
pretitle?: string;
|
pretitle?: string;
|
||||||
|
@ -59,32 +62,72 @@ export interface LibraryItem {
|
||||||
primary: boolean;
|
primary: boolean;
|
||||||
digital: boolean;
|
digital: boolean;
|
||||||
downloadable: boolean;
|
downloadable: boolean;
|
||||||
|
gallery?: {
|
||||||
|
image?: string | LibraryItemGallery;
|
||||||
|
id?: string;
|
||||||
|
}[];
|
||||||
scans?: {
|
scans?: {
|
||||||
cover?: {
|
cover?: {
|
||||||
front?: string | LibraryItemThumbnail;
|
front?: string | LibraryItemScans;
|
||||||
spine?: string | LibraryItemThumbnail;
|
spine?: string | LibraryItemScans;
|
||||||
back?: string | LibraryItemThumbnail;
|
back?: string | LibraryItemScans;
|
||||||
|
insideFront?: string | LibraryItemScans;
|
||||||
|
flapFront?: string | LibraryItemScans;
|
||||||
|
flapBack?: string | LibraryItemScans;
|
||||||
|
insideFlapFront?: string | LibraryItemScans;
|
||||||
|
insideFlapBack?: string | LibraryItemScans;
|
||||||
id?: string;
|
id?: string;
|
||||||
}[];
|
}[];
|
||||||
dustjacket?: {
|
dustjacket?: {
|
||||||
front?: string | LibraryItemThumbnail;
|
front?: string | LibraryItemScans;
|
||||||
spine?: string | LibraryItemThumbnail;
|
spine?: string | LibraryItemScans;
|
||||||
back?: string | LibraryItemThumbnail;
|
back?: string | LibraryItemScans;
|
||||||
|
insideFront?: string | LibraryItemScans;
|
||||||
|
insideSpine?: string | LibraryItemScans;
|
||||||
|
insideBack?: string | LibraryItemScans;
|
||||||
|
flapFront?: string | LibraryItemScans;
|
||||||
|
flapBack?: string | LibraryItemScans;
|
||||||
|
insideFlapFront?: string | LibraryItemScans;
|
||||||
|
insideFlapBack?: string | LibraryItemScans;
|
||||||
id?: string;
|
id?: string;
|
||||||
}[];
|
}[];
|
||||||
obi?: {
|
obi?: {
|
||||||
front?: string | LibraryItemThumbnail;
|
front?: string | LibraryItemScans;
|
||||||
spine?: string | LibraryItemThumbnail;
|
spine?: string | LibraryItemScans;
|
||||||
back?: string | LibraryItemThumbnail;
|
back?: string | LibraryItemScans;
|
||||||
|
insideFront?: string | LibraryItemScans;
|
||||||
|
insideSpine?: string | LibraryItemScans;
|
||||||
|
insideBack?: string | LibraryItemScans;
|
||||||
|
flapFront?: string | LibraryItemScans;
|
||||||
|
flapBack?: string | LibraryItemScans;
|
||||||
|
insideFlapFront?: string | LibraryItemScans;
|
||||||
|
insideFlapBack?: string | LibraryItemScans;
|
||||||
id?: string;
|
id?: string;
|
||||||
}[];
|
}[];
|
||||||
pages?: {
|
pages?: {
|
||||||
page: number;
|
page: number;
|
||||||
image: string | LibraryItemThumbnail;
|
image: string | LibraryItemScans;
|
||||||
id?: string;
|
id?: string;
|
||||||
}[];
|
}[];
|
||||||
id?: string;
|
id?: string;
|
||||||
}[];
|
}[];
|
||||||
|
textual?: {
|
||||||
|
subtype?: string[] | Key[];
|
||||||
|
languages?: string[] | Language[];
|
||||||
|
pageCount?: number;
|
||||||
|
bindingType?: "Paperback" | "Hardcover";
|
||||||
|
pageOrder?: "LeftToRight" | "RightToLeft";
|
||||||
|
};
|
||||||
|
audio?: {
|
||||||
|
audioSubtype?: string[] | Key[];
|
||||||
|
};
|
||||||
|
releaseDate?: string;
|
||||||
|
categories?: string[] | Key[];
|
||||||
|
translations?: {
|
||||||
|
language: string | Language;
|
||||||
|
description: string;
|
||||||
|
id?: string;
|
||||||
|
}[];
|
||||||
size?: {
|
size?: {
|
||||||
width: number;
|
width: number;
|
||||||
height: number;
|
height: number;
|
||||||
|
@ -96,41 +139,10 @@ export interface LibraryItem {
|
||||||
currency: string | Currency;
|
currency: string | Currency;
|
||||||
id?: string;
|
id?: string;
|
||||||
}[];
|
}[];
|
||||||
itemType?: "Textual" | "Audio" | "Video" | "Game" | "Other";
|
urls?: {
|
||||||
textual?: {
|
url: string;
|
||||||
subtype?:
|
id?: string;
|
||||||
| {
|
}[];
|
||||||
value: string;
|
|
||||||
relationTo: "keys";
|
|
||||||
}[]
|
|
||||||
| {
|
|
||||||
value: Key;
|
|
||||||
relationTo: "keys";
|
|
||||||
}[];
|
|
||||||
languages?:
|
|
||||||
| {
|
|
||||||
value: string;
|
|
||||||
relationTo: "languages";
|
|
||||||
}[]
|
|
||||||
| {
|
|
||||||
value: Language;
|
|
||||||
relationTo: "languages";
|
|
||||||
}[];
|
|
||||||
pageCount?: number;
|
|
||||||
bindingType?: "Paperback" | "Hardcover";
|
|
||||||
pageOrder?: "LeftToRight" | "RightToLeft";
|
|
||||||
};
|
|
||||||
audio?: {
|
|
||||||
audioSubtype?:
|
|
||||||
| {
|
|
||||||
value: string;
|
|
||||||
relationTo: "keys";
|
|
||||||
}[]
|
|
||||||
| {
|
|
||||||
value: Key;
|
|
||||||
relationTo: "keys";
|
|
||||||
}[];
|
|
||||||
};
|
|
||||||
contents?: {
|
contents?: {
|
||||||
content: string | Content;
|
content: string | Content;
|
||||||
pageStart?: number;
|
pageStart?: number;
|
||||||
|
@ -140,7 +152,6 @@ export interface LibraryItem {
|
||||||
note?: string;
|
note?: string;
|
||||||
id?: string;
|
id?: string;
|
||||||
}[];
|
}[];
|
||||||
releaseDate?: string;
|
|
||||||
updatedBy: string | Recorder;
|
updatedBy: string | Recorder;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
|
@ -157,6 +168,96 @@ export interface LibraryItemThumbnail {
|
||||||
width?: number;
|
width?: number;
|
||||||
height?: number;
|
height?: number;
|
||||||
sizes?: {
|
sizes?: {
|
||||||
|
thumb?: {
|
||||||
|
url?: string;
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
|
mimeType?: string;
|
||||||
|
filesize?: number;
|
||||||
|
filename?: string;
|
||||||
|
};
|
||||||
|
og?: {
|
||||||
|
url?: string;
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
|
mimeType?: string;
|
||||||
|
filesize?: number;
|
||||||
|
filename?: string;
|
||||||
|
};
|
||||||
|
square?: {
|
||||||
|
url?: string;
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
|
mimeType?: string;
|
||||||
|
filesize?: number;
|
||||||
|
filename?: string;
|
||||||
|
};
|
||||||
|
max?: {
|
||||||
|
url?: string;
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
|
mimeType?: string;
|
||||||
|
filesize?: number;
|
||||||
|
filename?: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export interface LibraryItemGallery {
|
||||||
|
id: string;
|
||||||
|
updatedAt: string;
|
||||||
|
createdAt: string;
|
||||||
|
url?: string;
|
||||||
|
filename?: string;
|
||||||
|
mimeType?: string;
|
||||||
|
filesize?: number;
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
|
sizes?: {
|
||||||
|
thumb?: {
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
max?: {
|
||||||
|
url?: string;
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
|
mimeType?: string;
|
||||||
|
filesize?: number;
|
||||||
|
filename?: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export interface LibraryItemScans {
|
||||||
|
id: string;
|
||||||
|
updatedAt: string;
|
||||||
|
createdAt: string;
|
||||||
|
url?: string;
|
||||||
|
filename?: string;
|
||||||
|
mimeType?: string;
|
||||||
|
filesize?: number;
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
|
sizes?: {
|
||||||
|
thumb?: {
|
||||||
|
url?: string;
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
|
mimeType?: string;
|
||||||
|
filesize?: number;
|
||||||
|
filename?: string;
|
||||||
|
};
|
||||||
og?: {
|
og?: {
|
||||||
url?: string;
|
url?: string;
|
||||||
width?: number;
|
width?: number;
|
||||||
|
@ -183,9 +284,6 @@ export interface LibraryItemThumbnail {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
export interface Currency {
|
|
||||||
id: string;
|
|
||||||
}
|
|
||||||
export interface Key {
|
export interface Key {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -206,23 +304,15 @@ export interface Language {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
|
export interface Currency {
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
export interface Content {
|
export interface Content {
|
||||||
id: string;
|
id: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
thumbnail?: string | ContentsThumbnail;
|
thumbnail?: string | ContentsThumbnail;
|
||||||
categories?:
|
categories?: string[] | Key[];
|
||||||
| {
|
type?: string | Key;
|
||||||
value: string;
|
|
||||||
relationTo: "keys";
|
|
||||||
}[]
|
|
||||||
| {
|
|
||||||
value: Key;
|
|
||||||
relationTo: "keys";
|
|
||||||
}[];
|
|
||||||
type?: {
|
|
||||||
value: string | Key;
|
|
||||||
relationTo: "keys";
|
|
||||||
};
|
|
||||||
translations: {
|
translations: {
|
||||||
language: string | Language;
|
language: string | Language;
|
||||||
sourceLanguage: string | Language;
|
sourceLanguage: string | Language;
|
||||||
|
@ -256,6 +346,14 @@ export interface ContentsThumbnail {
|
||||||
width?: number;
|
width?: number;
|
||||||
height?: number;
|
height?: number;
|
||||||
sizes?: {
|
sizes?: {
|
||||||
|
thumb?: {
|
||||||
|
url?: string;
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
|
mimeType?: string;
|
||||||
|
filesize?: number;
|
||||||
|
filename?: string;
|
||||||
|
};
|
||||||
og?: {
|
og?: {
|
||||||
url?: string;
|
url?: string;
|
||||||
width?: number;
|
width?: number;
|
||||||
|
@ -303,6 +401,14 @@ export interface RecordersThumbnail {
|
||||||
width?: number;
|
width?: number;
|
||||||
height?: number;
|
height?: number;
|
||||||
sizes?: {
|
sizes?: {
|
||||||
|
thumb?: {
|
||||||
|
url?: string;
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
|
mimeType?: string;
|
||||||
|
filesize?: number;
|
||||||
|
filename?: string;
|
||||||
|
};
|
||||||
og?: {
|
og?: {
|
||||||
url?: string;
|
url?: string;
|
||||||
width?: number;
|
width?: number;
|
||||||
|
@ -574,6 +680,14 @@ export interface PostThumbnail {
|
||||||
width?: number;
|
width?: number;
|
||||||
height?: number;
|
height?: number;
|
||||||
sizes?: {
|
sizes?: {
|
||||||
|
thumb?: {
|
||||||
|
url?: string;
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
|
mimeType?: string;
|
||||||
|
filesize?: number;
|
||||||
|
filename?: string;
|
||||||
|
};
|
||||||
og?: {
|
og?: {
|
||||||
url?: string;
|
url?: string;
|
||||||
width?: number;
|
width?: number;
|
||||||
|
@ -600,12 +714,25 @@ export interface ChronologyItem {
|
||||||
month?: number;
|
month?: number;
|
||||||
day?: number;
|
day?: number;
|
||||||
};
|
};
|
||||||
events?: {
|
events: {
|
||||||
translations?: {
|
source?:
|
||||||
|
| {
|
||||||
|
value: string | Content;
|
||||||
|
relationTo: "contents";
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
value: string | LibraryItem;
|
||||||
|
relationTo: "library-items";
|
||||||
|
};
|
||||||
|
translations: {
|
||||||
language: string | Language;
|
language: string | Language;
|
||||||
|
sourceLanguage: string | Language;
|
||||||
title?: string;
|
title?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
notes?: string;
|
notes?: string;
|
||||||
|
transcribers?: string[] | Recorder[];
|
||||||
|
translators?: string[] | Recorder[];
|
||||||
|
proofreaders?: string[] | Recorder[];
|
||||||
id?: string;
|
id?: string;
|
||||||
}[];
|
}[];
|
||||||
id?: string;
|
id?: string;
|
||||||
|
@ -626,6 +753,7 @@ export interface ChronologyEra {
|
||||||
description?: string;
|
description?: string;
|
||||||
id?: string;
|
id?: string;
|
||||||
}[];
|
}[];
|
||||||
|
events?: string[] | ChronologyItem[];
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
}
|
}
|
||||||
|
@ -669,6 +797,14 @@ export interface WeaponsThumbnail {
|
||||||
width?: number;
|
width?: number;
|
||||||
height?: number;
|
height?: number;
|
||||||
sizes?: {
|
sizes?: {
|
||||||
|
thumb?: {
|
||||||
|
url?: string;
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
|
mimeType?: string;
|
||||||
|
filesize?: number;
|
||||||
|
filename?: string;
|
||||||
|
};
|
||||||
og?: {
|
og?: {
|
||||||
url?: string;
|
url?: string;
|
||||||
width?: number;
|
width?: number;
|
||||||
|
@ -685,6 +821,22 @@ export interface WeaponsThumbnail {
|
||||||
filesize?: number;
|
filesize?: number;
|
||||||
filename?: string;
|
filename?: string;
|
||||||
};
|
};
|
||||||
|
medium?: {
|
||||||
|
url?: string;
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
|
mimeType?: string;
|
||||||
|
filesize?: number;
|
||||||
|
filename?: string;
|
||||||
|
};
|
||||||
|
max?: {
|
||||||
|
url?: string;
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
|
mimeType?: string;
|
||||||
|
filesize?: number;
|
||||||
|
filename?: string;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
export interface WeaponsGroup {
|
export interface WeaponsGroup {
|
||||||
|
@ -701,7 +853,7 @@ export interface Video {
|
||||||
id: string;
|
id: string;
|
||||||
uid: string;
|
uid: string;
|
||||||
gone: boolean;
|
gone: boolean;
|
||||||
source: "YouTube" | "NicoNico";
|
source: "YouTube" | "NicoNico" | "Tumblr";
|
||||||
title: string;
|
title: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
likes?: number;
|
likes?: number;
|
||||||
|
|
|
@ -10,3 +10,17 @@ export const isValidDate = (date: Date): boolean => date instanceof Date && !isN
|
||||||
|
|
||||||
export const isNotEmpty = (value: string | null | undefined): value is string =>
|
export const isNotEmpty = (value: string | null | undefined): value is string =>
|
||||||
isDefined(value) && value.trim().length > 0;
|
isDefined(value) && value.trim().length > 0;
|
||||||
|
|
||||||
|
export const isEmpty = (value: string | null | undefined): value is string =>
|
||||||
|
isUndefined(value) || value.trim().length === 0;
|
||||||
|
|
||||||
|
type Span = [number, number];
|
||||||
|
export const hasNoIntersection = (a: Span, b: Span): boolean => {
|
||||||
|
const [aStart, aEnd] = a;
|
||||||
|
const [bStart, bEnd] = b;
|
||||||
|
return aEnd < bStart || aStart > bEnd;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const hasIntersection = (a: Span, b: Span): boolean => !hasNoIntersection(a, b);
|
||||||
|
|
||||||
|
export const hasDuplicates = <T>(list: T[]): boolean => list.length !== new Set(list).size;
|
||||||
|
|
|
@ -1,22 +1,12 @@
|
||||||
import { CollectionConfig } from "payload/types";
|
import { CollectionConfig } from "payload/types";
|
||||||
import { Collections } from "../constants";
|
import { Collections } from "../constants";
|
||||||
|
|
||||||
export type BuildCollectionConfig = Omit<CollectionConfig, "slug" | "typescript" | "labels">;
|
export type BuildCollectionConfig = Omit<CollectionConfig, "slug" | "typescript" | "labels"> & {
|
||||||
|
slug: Collections;
|
||||||
export type GenerationFunctionProps = {
|
labels: { singular: string; plural: string };
|
||||||
uploadDir: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const buildCollectionConfig = (
|
export const buildCollectionConfig = (config: BuildCollectionConfig): CollectionConfig => ({
|
||||||
slug: Collections,
|
...config,
|
||||||
labels: { singular: string; plural: string },
|
typescript: { interface: config.labels.singular },
|
||||||
generationFunction: (props: GenerationFunctionProps) => BuildCollectionConfig
|
});
|
||||||
): CollectionConfig => {
|
|
||||||
const uploadDir = `../uploads/${slug}`;
|
|
||||||
const config = generationFunction({ uploadDir });
|
|
||||||
return {
|
|
||||||
...config,
|
|
||||||
slug,
|
|
||||||
typescript: { interface: labels.singular },
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
import { ImageSize } from "payload/dist/uploads/types";
|
||||||
|
import { CollectionConfig } from "payload/types";
|
||||||
|
import { BuildCollectionConfig, buildCollectionConfig } from "./collectionConfig";
|
||||||
|
|
||||||
|
type BuildImageCollectionConfig = Omit<BuildCollectionConfig, "upload"> & {
|
||||||
|
upload: { imageSizes: ImageSize[] };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const buildImageCollectionConfig = ({
|
||||||
|
upload: { imageSizes },
|
||||||
|
...otherConfig
|
||||||
|
}: BuildImageCollectionConfig): CollectionConfig =>
|
||||||
|
buildCollectionConfig({
|
||||||
|
...otherConfig,
|
||||||
|
upload: {
|
||||||
|
staticDir: `../uploads/${otherConfig.slug}`,
|
||||||
|
mimeTypes: ["image/*"],
|
||||||
|
adminThumbnail: "thumb",
|
||||||
|
imageSizes: [
|
||||||
|
{
|
||||||
|
name: "thumb",
|
||||||
|
height: 128,
|
||||||
|
width: 128,
|
||||||
|
fit: "contain",
|
||||||
|
background: { r: 0, g: 0, b: 0, alpha: 0 },
|
||||||
|
formatOptions: {
|
||||||
|
format: "webp",
|
||||||
|
options: { effort: 6, quality: 50, alphaQuality: 50 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...imageSizes,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
|
@ -1 +0,0 @@
|
||||||
export const hasDuplicates = <T>(list: T[]): boolean => list.length !== new Set(list).size;
|
|
|
@ -1,10 +1,6 @@
|
||||||
import { CollectionBeforeChangeHook, CollectionConfig, RelationshipField } from "payload/types";
|
import { CollectionBeforeChangeHook, CollectionConfig, RelationshipField } from "payload/types";
|
||||||
import { Collections } from "../constants";
|
import { Collections } from "../constants";
|
||||||
import {
|
import { BuildCollectionConfig } from "./collectionConfig";
|
||||||
BuildCollectionConfig,
|
|
||||||
GenerationFunctionProps,
|
|
||||||
buildCollectionConfig,
|
|
||||||
} from "./collectionConfig";
|
|
||||||
|
|
||||||
const fields = { updatedBy: "updatedBy" };
|
const fields = { updatedBy: "updatedBy" };
|
||||||
|
|
||||||
|
@ -23,25 +19,17 @@ const updatedByField = (): RelationshipField => ({
|
||||||
|
|
||||||
type BuildVersionedCollectionConfig = Omit<BuildCollectionConfig, "timestamps" | "versions">;
|
type BuildVersionedCollectionConfig = Omit<BuildCollectionConfig, "timestamps" | "versions">;
|
||||||
|
|
||||||
export const buildVersionedCollectionConfig = (
|
export const buildVersionedCollectionConfig = ({
|
||||||
slug: Collections,
|
hooks: { beforeChange, ...otherHooks } = {},
|
||||||
labels: { singular: string; plural: string },
|
fields,
|
||||||
generationFunction: (props: GenerationFunctionProps) => BuildVersionedCollectionConfig
|
...otherParams
|
||||||
): CollectionConfig => {
|
}: BuildVersionedCollectionConfig): CollectionConfig => ({
|
||||||
const {
|
...otherParams,
|
||||||
hooks: { beforeChange, ...otherHooks } = {},
|
timestamps: true,
|
||||||
fields,
|
versions: { drafts: { autosave: { interval: 2000 } } },
|
||||||
...otherParams
|
hooks: {
|
||||||
} = buildCollectionConfig(slug, labels, generationFunction);
|
...otherHooks,
|
||||||
|
beforeChange: [...(beforeChange ?? []), beforeChangeUpdatedBy],
|
||||||
return {
|
},
|
||||||
...otherParams,
|
fields: [...fields, updatedByField()],
|
||||||
timestamps: true,
|
});
|
||||||
versions: { drafts: { autosave: { interval: 2000 } } },
|
|
||||||
hooks: {
|
|
||||||
...otherHooks,
|
|
||||||
beforeChange: [...(beforeChange ?? []), beforeChangeUpdatedBy],
|
|
||||||
},
|
|
||||||
fields: [...fields, updatedByField()],
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
Loading…
Reference in New Issue