Added chronology events, removed eras
This commit is contained in:
parent
e91642616d
commit
4fd59464ba
|
@ -1,87 +0,0 @@
|
|||
import { CollectionConfig } from "payload/types";
|
||||
import { mustBeAdmin } from "../../accesses/collections/mustBeAdmin";
|
||||
import { CollectionGroups, Collections } from "../../constants";
|
||||
import { backPropagationField } from "../../fields/backPropagationField/backPropagationField";
|
||||
import { rowField } from "../../fields/rowField/rowField";
|
||||
import { slugField } from "../../fields/slugField/slugField";
|
||||
import { translatedFields } from "../../fields/translatedFields/translatedFields";
|
||||
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
||||
import { createEditor } from "../../utils/editor";
|
||||
import { getAllEndpoint } from "./endpoints/getAllEndpoint";
|
||||
import { importFromStrapi } from "./endpoints/importFromStrapi";
|
||||
import { beforeValidateEndingGreaterThanStarting } from "./hooks/beforeValidateEndingGreaterThanStarting";
|
||||
import { beforeValidateNoIntersection } from "./hooks/beforeValidateNoIntersection";
|
||||
|
||||
const fields = {
|
||||
slug: "slug",
|
||||
startingYear: "startingYear",
|
||||
endingYear: "endingYear",
|
||||
translations: "translations",
|
||||
translationsTitle: "title",
|
||||
translationsDescription: "description",
|
||||
events: "events",
|
||||
} as const satisfies Record<string, string>;
|
||||
|
||||
export const ChronologyEras: CollectionConfig = buildCollectionConfig({
|
||||
slug: Collections.ChronologyEras,
|
||||
labels: {
|
||||
singular: "Chronology Era",
|
||||
plural: "Chronology Eras",
|
||||
},
|
||||
defaultSort: fields.startingYear,
|
||||
admin: {
|
||||
group: CollectionGroups.Collections,
|
||||
defaultColumns: [fields.slug, fields.startingYear, fields.endingYear, fields.translations],
|
||||
useAsTitle: fields.slug,
|
||||
},
|
||||
access: {
|
||||
create: mustBeAdmin,
|
||||
delete: mustBeAdmin,
|
||||
},
|
||||
hooks: {
|
||||
beforeValidate: [beforeValidateEndingGreaterThanStarting, beforeValidateNoIntersection],
|
||||
},
|
||||
endpoints: [importFromStrapi, getAllEndpoint],
|
||||
fields: [
|
||||
slugField({ name: fields.slug }),
|
||||
rowField([
|
||||
{
|
||||
name: fields.startingYear,
|
||||
type: "number",
|
||||
min: 0,
|
||||
required: true,
|
||||
admin: { description: "The year the era started (year included)" },
|
||||
},
|
||||
{
|
||||
name: fields.endingYear,
|
||||
type: "number",
|
||||
min: 0,
|
||||
required: true,
|
||||
admin: { description: "The year the era ended (year included)" },
|
||||
},
|
||||
]),
|
||||
translatedFields({
|
||||
name: fields.translations,
|
||||
admin: { useAsTitle: fields.translationsTitle },
|
||||
fields: [
|
||||
{ name: fields.translationsTitle, type: "text", required: true },
|
||||
{
|
||||
name: fields.translationsDescription,
|
||||
type: "richText",
|
||||
editor: createEditor({ inlines: true, lists: true, links: true }),
|
||||
},
|
||||
],
|
||||
}),
|
||||
backPropagationField({
|
||||
name: fields.events,
|
||||
hasMany: true,
|
||||
relationTo: Collections.ChronologyItems,
|
||||
where: ({ startingYear, endingYear }) => ({
|
||||
and: [
|
||||
{ "date.year": { greater_than_equal: startingYear } },
|
||||
{ "date.year": { less_than_equal: endingYear } },
|
||||
],
|
||||
}),
|
||||
}),
|
||||
],
|
||||
});
|
|
@ -1,99 +0,0 @@
|
|||
import payload from "payload";
|
||||
import { Collections } from "../../../constants";
|
||||
import { EndpointEra } from "../../../sdk";
|
||||
import { ChronologyEra, ChronologyItem } from "../../../types/collections";
|
||||
import { CollectionEndpoint } from "../../../types/payload";
|
||||
import { isDefined, isPayloadArrayType, isPayloadType } from "../../../utils/asserts";
|
||||
import { handleRecorder } from "../../../utils/endpoints";
|
||||
|
||||
export const getAllEndpoint: CollectionEndpoint = {
|
||||
method: "get",
|
||||
path: "/all",
|
||||
handler: async (req, res) => {
|
||||
if (!req.user) {
|
||||
return res.status(403).send({
|
||||
errors: [
|
||||
{
|
||||
message: "You are not allowed to perform this action.",
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
const eras: ChronologyEra[] = (
|
||||
await payload.find({
|
||||
collection: Collections.ChronologyEras,
|
||||
pagination: false,
|
||||
})
|
||||
).docs;
|
||||
|
||||
const result = eras.map<EndpointEra>(
|
||||
({ endingYear, startingYear, slug, translations, events: items }) => ({
|
||||
slug,
|
||||
startingYear,
|
||||
endingYear,
|
||||
translations:
|
||||
translations?.map(({ language, title, description }) => ({
|
||||
language: isPayloadType(language) ? language.id : language,
|
||||
title,
|
||||
...(description ? { description } : {}),
|
||||
})) ?? [],
|
||||
items:
|
||||
items
|
||||
?.filter(isPayloadType<ChronologyItem>)
|
||||
.sort((a, b) => {
|
||||
const aYear = a.date.year;
|
||||
const bYear = b.date.year;
|
||||
if (aYear !== bYear) return aYear - bYear;
|
||||
const aMonth = a.date.month ?? 0;
|
||||
const bMonth = b.date.month ?? 0;
|
||||
if (aMonth !== bMonth) return aMonth - bMonth;
|
||||
const aDay = a.date.day ?? 0;
|
||||
const bDay = b.date.day ?? 0;
|
||||
if (aDay !== bDay) return aDay - bDay;
|
||||
return 0;
|
||||
})
|
||||
.map(({ events, date: { year, day, month } }) => ({
|
||||
date: {
|
||||
year,
|
||||
...(isDefined(day) ? { day } : {}),
|
||||
...(isDefined(month) ? { month } : {}),
|
||||
},
|
||||
events: events.map(({ translations }) => ({
|
||||
translations: translations.map(
|
||||
({
|
||||
language,
|
||||
sourceLanguage,
|
||||
description,
|
||||
notes,
|
||||
proofreaders,
|
||||
transcribers,
|
||||
translators,
|
||||
title,
|
||||
}) => ({
|
||||
language: isPayloadType(language) ? language.id : language,
|
||||
sourceLanguage: isPayloadType(sourceLanguage)
|
||||
? sourceLanguage.id
|
||||
: sourceLanguage,
|
||||
...(title ? { title } : {}),
|
||||
...(description ? { description } : {}),
|
||||
...(notes ? { notes } : {}),
|
||||
proofreaders: isPayloadArrayType(proofreaders)
|
||||
? proofreaders.map(handleRecorder)
|
||||
: [],
|
||||
transcribers: isPayloadArrayType(transcribers)
|
||||
? transcribers.map(handleRecorder)
|
||||
: [],
|
||||
translators: isPayloadArrayType(translators)
|
||||
? translators.map(handleRecorder)
|
||||
: [],
|
||||
})
|
||||
),
|
||||
})),
|
||||
})) ?? [],
|
||||
})
|
||||
);
|
||||
|
||||
res.status(200).json(result);
|
||||
},
|
||||
};
|
|
@ -1,38 +0,0 @@
|
|||
import { Collections } from "../../../constants";
|
||||
import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint";
|
||||
import { StrapiLanguage } from "../../../types/strapi";
|
||||
import { isDefined, isUndefined } from "../../../utils/asserts";
|
||||
import { plainTextToLexical } from "../../../utils/string";
|
||||
|
||||
type StrapiChronologyEra = {
|
||||
slug: string;
|
||||
starting_year: number;
|
||||
ending_year: number;
|
||||
title: { title: string; language: StrapiLanguage; description?: string }[];
|
||||
};
|
||||
|
||||
export const importFromStrapi = createStrapiImportEndpoint<StrapiChronologyEra>({
|
||||
strapi: {
|
||||
collection: "chronology-eras",
|
||||
params: {
|
||||
populate: { title: { populate: "language" } },
|
||||
},
|
||||
},
|
||||
payload: {
|
||||
collection: Collections.ChronologyEras,
|
||||
convert: ({ slug, starting_year, ending_year, title: titles }) => ({
|
||||
slug,
|
||||
startingYear: starting_year,
|
||||
endingYear: ending_year,
|
||||
translations: titles.map(({ language, title, description }) => {
|
||||
if (isUndefined(language.data))
|
||||
throw new Error("Language is undefined for one of the translations");
|
||||
return {
|
||||
language: language.data?.attributes.code,
|
||||
title,
|
||||
...(isDefined(description) ? { description: plainTextToLexical(description) } : {}),
|
||||
};
|
||||
}),
|
||||
}),
|
||||
},
|
||||
});
|
|
@ -1,15 +0,0 @@
|
|||
import { CollectionBeforeValidateHook } from "payload/types";
|
||||
import { ChronologyEra } from "../../../types/collections";
|
||||
import { isUndefined } from "../../../utils/asserts";
|
||||
|
||||
export const beforeValidateEndingGreaterThanStarting: CollectionBeforeValidateHook<
|
||||
ChronologyEra
|
||||
> = async ({ data }) => {
|
||||
if (isUndefined(data)) throw new Error("The data is undefined");
|
||||
const { startingYear, endingYear } = data;
|
||||
if (isUndefined(endingYear)) throw new Error("Ending year is undefined");
|
||||
if (isUndefined(startingYear)) throw new Error("Starting year is undefined");
|
||||
if (endingYear < startingYear) {
|
||||
throw new Error("The ending year cannot be before the starting year.");
|
||||
}
|
||||
};
|
|
@ -1,28 +0,0 @@
|
|||
import payload from "payload";
|
||||
import { CollectionBeforeValidateHook } from "payload/types";
|
||||
import { Collections } from "../../../constants";
|
||||
import { ChronologyEra } from "../../../types/collections";
|
||||
import { hasIntersection, isUndefined } from "../../../utils/asserts";
|
||||
|
||||
export const beforeValidateNoIntersection: CollectionBeforeValidateHook<ChronologyEra> = async ({
|
||||
data,
|
||||
}) => {
|
||||
if (isUndefined(data)) throw new Error("The data is undefined");
|
||||
const { startingYear, endingYear } = data;
|
||||
if (isUndefined(endingYear)) throw new Error("Ending year is undefined");
|
||||
if (isUndefined(startingYear)) throw new Error("Starting year is undefined");
|
||||
|
||||
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})`
|
||||
);
|
||||
}
|
||||
});
|
||||
};
|
|
@ -9,6 +9,11 @@ import { rowField } from "../../fields/rowField/rowField";
|
|||
import { translatedFields } from "../../fields/translatedFields/translatedFields";
|
||||
import { createEditor } from "../../utils/editor";
|
||||
import { buildVersionedCollectionConfig } from "../../utils/versionedCollectionConfig";
|
||||
import { collectibleBlock } from "./blocks/collectibleBlock";
|
||||
import { pageBlock } from "./blocks/contentBlock";
|
||||
import { urlBlock } from "./blocks/urlBlock";
|
||||
import { getAllEndpoint } from "./endpoints/getAllEndpoint";
|
||||
import { getByID } from "./endpoints/getByID";
|
||||
import { importFromStrapi } from "./endpoints/importFromStrapi";
|
||||
import { beforeValidatePopulateNameField } from "./hooks/beforeValidatePopulateNameField";
|
||||
import { validateDate } from "./validations/validateDate";
|
||||
|
@ -18,6 +23,7 @@ import { validateEventsTranslationsTitle } from "./validations/validateEventsTra
|
|||
const fields = {
|
||||
name: "name",
|
||||
events: "events",
|
||||
eventsSources: "sources",
|
||||
eventsTranslations: "translations",
|
||||
eventsTranslationsTitle: "title",
|
||||
eventsTranslationsDescription: "description",
|
||||
|
@ -26,15 +32,14 @@ const fields = {
|
|||
dateYear: "year",
|
||||
dateMonth: "month",
|
||||
dateDay: "day",
|
||||
era: "era",
|
||||
status: "_status",
|
||||
} as const satisfies Record<string, string>;
|
||||
|
||||
export const ChronologyItems: CollectionConfig = buildVersionedCollectionConfig({
|
||||
slug: Collections.ChronologyItems,
|
||||
export const ChronologyEvents: CollectionConfig = buildVersionedCollectionConfig({
|
||||
slug: Collections.ChronologyEvents,
|
||||
labels: {
|
||||
singular: "Chronology Item",
|
||||
plural: "Chronology Items",
|
||||
singular: "Chronology Event",
|
||||
plural: "Chronology Events",
|
||||
},
|
||||
defaultSort: fields.name,
|
||||
admin: {
|
||||
|
@ -45,7 +50,7 @@ export const ChronologyItems: CollectionConfig = buildVersionedCollectionConfig(
|
|||
BeforeListTable: [
|
||||
() =>
|
||||
QuickFilters({
|
||||
slug: Collections.ChronologyItems,
|
||||
slug: Collections.ChronologyEvents,
|
||||
filterGroups: [
|
||||
languageBasedFilters("events.translations.language"),
|
||||
publishStatusFilters,
|
||||
|
@ -54,7 +59,7 @@ export const ChronologyItems: CollectionConfig = buildVersionedCollectionConfig(
|
|||
],
|
||||
},
|
||||
},
|
||||
endpoints: [importFromStrapi],
|
||||
endpoints: [importFromStrapi, getAllEndpoint, getByID],
|
||||
fields: [
|
||||
{
|
||||
name: fields.name,
|
||||
|
@ -67,6 +72,11 @@ export const ChronologyItems: CollectionConfig = buildVersionedCollectionConfig(
|
|||
{
|
||||
type: "group",
|
||||
name: fields.date,
|
||||
admin: {
|
||||
description:
|
||||
"Make sure there isn't already an entry in the Chronology Events with the same date.\
|
||||
If you try to create another entry with the same date, it will refuse to publish.",
|
||||
},
|
||||
validate: validateDate,
|
||||
fields: [
|
||||
rowField([
|
||||
|
@ -97,6 +107,12 @@ export const ChronologyItems: CollectionConfig = buildVersionedCollectionConfig(
|
|||
required: true,
|
||||
minRows: 1,
|
||||
fields: [
|
||||
{
|
||||
name: fields.eventsSources,
|
||||
type: "blocks",
|
||||
maxRows: 1,
|
||||
blocks: [urlBlock, collectibleBlock, pageBlock],
|
||||
},
|
||||
translatedFields({
|
||||
name: fields.eventsTranslations,
|
||||
required: true,
|
|
@ -0,0 +1,81 @@
|
|||
import { Block } from "payload/types";
|
||||
import { Collections } from "../../../constants";
|
||||
import { translatedFields } from "../../../fields/translatedFields/translatedFields";
|
||||
|
||||
export const collectibleBlock: Block = {
|
||||
slug: "collectibleBlock",
|
||||
interfaceName: "CollectibleBlock",
|
||||
labels: { singular: "Collectible", plural: "Collectibles" },
|
||||
fields: [
|
||||
{
|
||||
name: "collectible",
|
||||
type: "relationship",
|
||||
hasMany: false,
|
||||
required: true,
|
||||
relationTo: Collections.Collectibles,
|
||||
},
|
||||
{
|
||||
name: "range",
|
||||
type: "blocks",
|
||||
maxRows: 1,
|
||||
admin: { className: "no-label" },
|
||||
blocks: [
|
||||
{
|
||||
slug: "page",
|
||||
labels: { singular: "Page", plural: "Pages" },
|
||||
fields: [
|
||||
{
|
||||
name: "page",
|
||||
type: "number",
|
||||
required: true,
|
||||
admin: {
|
||||
description:
|
||||
"Make sure the page range corresponds to the pages as written\
|
||||
on the collectible. You can use negative page numbers if necessary.",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: "timestamp",
|
||||
labels: { singular: "Timestamp", plural: "Timestamps" },
|
||||
fields: [
|
||||
{
|
||||
name: "timestamp",
|
||||
type: "text",
|
||||
required: true,
|
||||
defaultValue: "00:00:00",
|
||||
validate: (value) =>
|
||||
/\d{2}:\d{2}:\d{2}/g.test(value)
|
||||
? true
|
||||
: "The format should be hh:mm:ss\
|
||||
(e.g: 01:23:45 for 1 hour, 23 minutes, and 45 seconds)",
|
||||
admin: {
|
||||
description: "hh:mm:ss (e.g: 01:23:45 for 1 hour, 23 minutes, and 45 seconds)",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: "other",
|
||||
labels: { singular: "Other", plural: "Others" },
|
||||
fields: [
|
||||
translatedFields({
|
||||
admin: { className: "no-label" },
|
||||
name: "translations",
|
||||
required: true,
|
||||
minRows: 1,
|
||||
fields: [
|
||||
{
|
||||
name: "note",
|
||||
type: "textarea",
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
}),
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
|
@ -0,0 +1,17 @@
|
|||
import { Block } from "payload/types";
|
||||
import { Collections } from "../../../constants";
|
||||
|
||||
export const pageBlock: Block = {
|
||||
slug: "pageBlock",
|
||||
interfaceName: "PageBlock",
|
||||
labels: { singular: "Page", plural: "Pages" },
|
||||
fields: [
|
||||
{
|
||||
name: "page",
|
||||
type: "relationship",
|
||||
hasMany: false,
|
||||
required: true,
|
||||
relationTo: Collections.Pages,
|
||||
},
|
||||
],
|
||||
};
|
|
@ -0,0 +1,8 @@
|
|||
import { Block } from "payload/types";
|
||||
|
||||
export const urlBlock: Block = {
|
||||
slug: "urlBlock",
|
||||
interfaceName: "UrlBlock",
|
||||
labels: { singular: "URL", plural: "URLs" },
|
||||
fields: [{ name: "url", type: "text", required: true }],
|
||||
};
|
|
@ -0,0 +1,150 @@
|
|||
import payload from "payload";
|
||||
import { Collections } from "../../../constants";
|
||||
import { EndpointChronologyEvent, EndpointSource } from "../../../sdk";
|
||||
import { ChronologyEvent, CollectibleBlock } from "../../../types/collections";
|
||||
import { CollectionEndpoint } from "../../../types/payload";
|
||||
import { isDefined, isNotEmpty, isPayloadArrayType, isPayloadType } from "../../../utils/asserts";
|
||||
import { getDomainFromUrl, handleRecorder } from "../../../utils/endpoints";
|
||||
import { convertCollectibleToPreview } from "../../Collectibles/endpoints/getBySlugEndpoint";
|
||||
import { convertPageToPreview } from "../../Pages/endpoints/getBySlugEndpoint";
|
||||
|
||||
export const getAllEndpoint: CollectionEndpoint = {
|
||||
method: "get",
|
||||
path: "/all",
|
||||
handler: async (req, res) => {
|
||||
if (!req.user) {
|
||||
return res.status(403).send({
|
||||
errors: [
|
||||
{
|
||||
message: "You are not allowed to perform this action.",
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
const result = (
|
||||
await payload.find({
|
||||
collection: Collections.ChronologyEvents,
|
||||
pagination: false,
|
||||
draft: false,
|
||||
where: {
|
||||
_status: {
|
||||
equals: "published",
|
||||
},
|
||||
},
|
||||
})
|
||||
).docs;
|
||||
|
||||
const events = result
|
||||
.sort((a, b) => {
|
||||
const aYear = a.date.year;
|
||||
const bYear = b.date.year;
|
||||
if (aYear !== bYear) return aYear - bYear;
|
||||
const aMonth = a.date.month ?? 13;
|
||||
const bMonth = b.date.month ?? 13;
|
||||
if (aMonth !== bMonth) return aMonth - bMonth;
|
||||
const aDay = a.date.day ?? 32;
|
||||
const bDay = b.date.day ?? 32;
|
||||
if (aDay !== bDay) return aDay - bDay;
|
||||
return 0;
|
||||
})
|
||||
.map<EndpointChronologyEvent>(eventToEndpointEvent);
|
||||
|
||||
res.status(200).json(events);
|
||||
},
|
||||
};
|
||||
|
||||
export const eventToEndpointEvent = ({
|
||||
date: { year, day, month },
|
||||
events,
|
||||
id,
|
||||
}: ChronologyEvent): EndpointChronologyEvent => ({
|
||||
id,
|
||||
date: {
|
||||
year,
|
||||
...(isDefined(month) ? { month } : {}),
|
||||
...(isDefined(day) ? { day } : {}),
|
||||
},
|
||||
events: events.map<EndpointChronologyEvent["events"][number]>(({ sources, translations }) => ({
|
||||
translations: translations.map(
|
||||
({
|
||||
language,
|
||||
sourceLanguage,
|
||||
description,
|
||||
notes,
|
||||
proofreaders,
|
||||
title,
|
||||
transcribers,
|
||||
translators,
|
||||
}) => ({
|
||||
language: isPayloadType(language) ? language.id : language,
|
||||
sourceLanguage: isPayloadType(sourceLanguage) ? sourceLanguage.id : sourceLanguage,
|
||||
...(isNotEmpty(title) ? { title } : {}),
|
||||
...(isDefined(description) ? { description } : {}),
|
||||
...(isDefined(notes) ? { notes } : {}),
|
||||
proofreaders: isPayloadArrayType(proofreaders) ? proofreaders.map(handleRecorder) : [],
|
||||
transcribers: isPayloadArrayType(transcribers) ? transcribers.map(handleRecorder) : [],
|
||||
translators: isPayloadArrayType(translators) ? translators.map(handleRecorder) : [],
|
||||
})
|
||||
),
|
||||
sources: handleSources(sources),
|
||||
})),
|
||||
});
|
||||
|
||||
const handleSources = (sources: ChronologyEvent["events"][number]["sources"]): EndpointSource[] => {
|
||||
return (
|
||||
sources?.flatMap<EndpointSource>((source) => {
|
||||
switch (source.blockType) {
|
||||
case "collectibleBlock":
|
||||
const range = handleRange(source.range);
|
||||
if (!isPayloadType(source.collectible)) return [];
|
||||
return {
|
||||
type: "collectible",
|
||||
collectible: convertCollectibleToPreview(source.collectible),
|
||||
...(range ? { range } : {}),
|
||||
};
|
||||
|
||||
case "pageBlock":
|
||||
if (!isPayloadType(source.page)) return [];
|
||||
return {
|
||||
type: "page",
|
||||
page: convertPageToPreview(source.page),
|
||||
};
|
||||
|
||||
case "urlBlock":
|
||||
return {
|
||||
type: "url",
|
||||
url: source.url,
|
||||
label: getDomainFromUrl(source.url),
|
||||
};
|
||||
}
|
||||
}) ?? []
|
||||
);
|
||||
};
|
||||
|
||||
const handleRange = (
|
||||
rawRange: CollectibleBlock["range"]
|
||||
): Extract<EndpointSource, { type: "collectible" }>["range"] => {
|
||||
const range = rawRange?.[0];
|
||||
|
||||
switch (range?.blockType) {
|
||||
case "page":
|
||||
return { type: "page", page: range.page };
|
||||
|
||||
case "timestamp":
|
||||
return { type: "timestamp", timestamp: range.timestamp };
|
||||
|
||||
case "other":
|
||||
return {
|
||||
type: "custom",
|
||||
translations: range.translations.map(({ language, note }) => ({
|
||||
language: isPayloadType(language) ? language.id : language,
|
||||
note,
|
||||
})),
|
||||
};
|
||||
|
||||
case undefined:
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
};
|
|
@ -0,0 +1,35 @@
|
|||
import payload from "payload";
|
||||
import { Collections } from "../../../constants";
|
||||
import { CollectionEndpoint } from "../../../types/payload";
|
||||
import { eventToEndpointEvent } from "./getAllEndpoint";
|
||||
|
||||
export const getByID: CollectionEndpoint = {
|
||||
method: "get",
|
||||
path: "/:id",
|
||||
handler: async (req, res) => {
|
||||
if (!req.user) {
|
||||
return res.status(403).send({
|
||||
errors: [
|
||||
{
|
||||
message: "You are not allowed to perform this action.",
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
if (!req.params.id) {
|
||||
return res.status(400).send({ errors: [{ message: "Missing 'id' query params" }] });
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await payload.findByID({
|
||||
collection: Collections.ChronologyEvents,
|
||||
id: req.params.id,
|
||||
});
|
||||
|
||||
return res.status(200).json(eventToEndpointEvent(result));
|
||||
} catch {
|
||||
return res.sendStatus(404);
|
||||
}
|
||||
},
|
||||
};
|
|
@ -1,7 +1,8 @@
|
|||
import { Collections } from "../../../constants";
|
||||
import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint";
|
||||
import { StrapiLanguage } from "../../../types/strapi";
|
||||
import { isUndefined } from "../../../utils/asserts";
|
||||
import { isDefined, isUndefined } from "../../../utils/asserts";
|
||||
import { plainTextToLexical } from "../../../utils/string";
|
||||
|
||||
type StrapiChronologyItem = {
|
||||
year: number;
|
||||
|
@ -25,20 +26,25 @@ export const importFromStrapi = createStrapiImportEndpoint<StrapiChronologyItem>
|
|||
},
|
||||
},
|
||||
payload: {
|
||||
collection: Collections.ChronologyItems,
|
||||
collection: Collections.ChronologyEvents,
|
||||
convert: ({ year, month, day, events }, user) => ({
|
||||
date: { year, month, day },
|
||||
events: events.map((event) => ({
|
||||
translations: event.translations.map(({ title, description, note, language }) => {
|
||||
if (isUndefined(language.data))
|
||||
throw new Error("A language is required for a chronology item event translation");
|
||||
|
||||
const newLanguage =
|
||||
language.data.attributes.code === "pt-br" ? "pt" : language.data.attributes.code;
|
||||
const sourceLanguage = language.data.attributes.code === "ja" ? "ja" : "en";
|
||||
|
||||
return {
|
||||
title,
|
||||
description,
|
||||
note,
|
||||
language: language.data.attributes.code,
|
||||
sourceLanguage: "en",
|
||||
...(language.data.attributes.code === "en"
|
||||
...(isDefined(title) ? { title } : {}),
|
||||
...(isDefined(note) ? { notes: plainTextToLexical(note) } : {}),
|
||||
...(isDefined(description) ? { description: plainTextToLexical(description) } : {}),
|
||||
language: newLanguage,
|
||||
sourceLanguage,
|
||||
...(newLanguage === sourceLanguage
|
||||
? { transcribers: [user.id] }
|
||||
: { translators: [user.id] }),
|
||||
};
|
|
@ -1,11 +1,11 @@
|
|||
import { FieldHook } from "payload/dist/fields/config/types";
|
||||
import { ChronologyItem } from "../../../types/collections";
|
||||
import { ChronologyEvent } from "../../../types/collections";
|
||||
import { isDefined, isUndefined } from "../../../utils/asserts";
|
||||
|
||||
export const beforeValidatePopulateNameField: FieldHook<
|
||||
ChronologyItem,
|
||||
ChronologyItem["name"],
|
||||
ChronologyItem
|
||||
ChronologyEvent,
|
||||
ChronologyEvent["name"],
|
||||
ChronologyEvent
|
||||
> = ({ data }) => {
|
||||
if (isUndefined(data) || isUndefined(data.date) || isUndefined(data.date.year))
|
||||
return "????-??-??";
|
|
@ -1,9 +1,9 @@
|
|||
import { DateTime } from "luxon";
|
||||
import { Validate } from "payload/types";
|
||||
import { ChronologyItem } from "../../../types/collections";
|
||||
import { ChronologyEvent } from "../../../types/collections";
|
||||
import { isUndefined } from "../../../utils/asserts";
|
||||
|
||||
export const validateDate: Validate<ChronologyItem["date"] | undefined> = (date) => {
|
||||
export const validateDate: Validate<ChronologyEvent["date"] | undefined> = (date) => {
|
||||
if (isUndefined(date)) return "This field is required.";
|
||||
const { year, month, day } = date;
|
||||
if (isUndefined(day)) return true;
|
|
@ -1,11 +1,11 @@
|
|||
import { Validate } from "payload/types";
|
||||
import { ChronologyItem } from "../../../types/collections";
|
||||
import { ChronologyEvent } from "../../../types/collections";
|
||||
import { isEmpty } from "../../../utils/asserts";
|
||||
|
||||
export const validateEventsTranslationsDescription: Validate<
|
||||
string | undefined,
|
||||
ChronologyItem,
|
||||
ChronologyItem["events"][number]["translations"][number],
|
||||
ChronologyEvent,
|
||||
ChronologyEvent["events"][number]["translations"][number],
|
||||
unknown
|
||||
> = (_, { siblingData: { description, title } }) => {
|
||||
if (!description && isEmpty(title)) {
|
|
@ -1,11 +1,11 @@
|
|||
import { Validate } from "payload/types";
|
||||
import { ChronologyItem } from "../../../types/collections";
|
||||
import { ChronologyEvent } from "../../../types/collections";
|
||||
import { isEmpty } from "../../../utils/asserts";
|
||||
|
||||
export const validateEventsTranslationsTitle: Validate<
|
||||
string | undefined,
|
||||
ChronologyItem,
|
||||
ChronologyItem["events"][number]["translations"][number],
|
||||
ChronologyEvent,
|
||||
ChronologyEvent["events"][number]["translations"][number],
|
||||
unknown
|
||||
> = (_, { siblingData: { description, title } }) => {
|
||||
if (!description && isEmpty(title)) {
|
|
@ -8,7 +8,7 @@ import {
|
|||
isPayloadType,
|
||||
isValidPayloadImage,
|
||||
} from "../../../utils/asserts";
|
||||
import { convertTagsToGroups, handleParentPages } from "../../../utils/endpoints";
|
||||
import { convertTagsToGroups, getDomainFromUrl, handleParentPages } from "../../../utils/endpoints";
|
||||
import { convertPageToPreview } from "../../Pages/endpoints/getBySlugEndpoint";
|
||||
|
||||
export const getBySlugEndpoint = createGetByEndpoint(
|
||||
|
@ -41,9 +41,9 @@ export const getBySlugEndpoint = createGetByEndpoint(
|
|||
gallery: handleGallery(gallery),
|
||||
scans: handleScans(collectible.scans),
|
||||
nature: nature === "Physical" ? CollectibleNature.Physical : CollectibleNature.Digital,
|
||||
parentPages: handleParentPages({collectibles: parentItems, folders}),
|
||||
parentPages: handleParentPages({ collectibles: parentItems, folders }),
|
||||
subitems: isPayloadArrayType(subitems) ? subitems.map(convertCollectibleToPreview) : [],
|
||||
urls: urls?.map(({ url }) => ({ url, label: getLabelFromUrl(url) })) ?? [],
|
||||
urls: urls?.map(({ url }) => ({ url, label: getDomainFromUrl(url) })) ?? [],
|
||||
...(weightEnabled && weight ? { weight: weight.amount } : {}),
|
||||
...handleSize(size, sizeEnabled),
|
||||
...handlePageInfo(pageInfo, pageInfoEnabled),
|
||||
|
@ -53,7 +53,7 @@ export const getBySlugEndpoint = createGetByEndpoint(
|
|||
3
|
||||
);
|
||||
|
||||
export const handlePrice = (
|
||||
const handlePrice = (
|
||||
price: Collectible["price"],
|
||||
enabled: Collectible["priceEnabled"]
|
||||
): { price: NonNullable<EndpointCollectible["price"]> } | {} => {
|
||||
|
@ -63,7 +63,7 @@ export const handlePrice = (
|
|||
};
|
||||
};
|
||||
|
||||
export const handleSize = (
|
||||
const handleSize = (
|
||||
size: Collectible["size"],
|
||||
enabled: Collectible["sizeEnabled"]
|
||||
): { size: NonNullable<EndpointCollectible["size"]> } | {} => {
|
||||
|
@ -77,7 +77,7 @@ export const handleSize = (
|
|||
};
|
||||
};
|
||||
|
||||
export const handlePageInfo = (
|
||||
const handlePageInfo = (
|
||||
pageInfo: Collectible["pageInfo"],
|
||||
enabled: Collectible["pageInfoEnabled"]
|
||||
): { pageInfo: NonNullable<EndpointCollectible["pageInfo"]> } | {} => {
|
||||
|
@ -91,7 +91,7 @@ export const handlePageInfo = (
|
|||
};
|
||||
};
|
||||
|
||||
export const handleGallery = (gallery: Collectible["gallery"]): EndpointCollectible["gallery"] => {
|
||||
const handleGallery = (gallery: Collectible["gallery"]): EndpointCollectible["gallery"] => {
|
||||
const result: PayloadImage[] = [];
|
||||
if (!gallery) return result;
|
||||
|
||||
|
@ -104,7 +104,7 @@ export const handleGallery = (gallery: Collectible["gallery"]): EndpointCollecti
|
|||
return result.slice(0, 10);
|
||||
};
|
||||
|
||||
export const handleScans = (scans: Collectible["scans"]): EndpointCollectible["scans"] => {
|
||||
const handleScans = (scans: Collectible["scans"]): EndpointCollectible["scans"] => {
|
||||
const result: PayloadImage[] = [];
|
||||
if (!scans) return result;
|
||||
|
||||
|
@ -133,9 +133,7 @@ export const handleScans = (scans: Collectible["scans"]): EndpointCollectible["s
|
|||
return result.slice(0, 10);
|
||||
};
|
||||
|
||||
export const handleContents = (
|
||||
contents: Collectible["contents"]
|
||||
): EndpointCollectible["contents"] => {
|
||||
const handleContents = (contents: Collectible["contents"]): EndpointCollectible["contents"] => {
|
||||
if (!contents) return [];
|
||||
|
||||
return contents.flatMap(({ content, range: rangeArray }) => {
|
||||
|
@ -229,12 +227,3 @@ export const convertCollectibleToPreview = ({
|
|||
})) ?? [],
|
||||
};
|
||||
};
|
||||
|
||||
const getLabelFromUrl = (url: string): string => {
|
||||
const urlObject = new URL(url);
|
||||
let domain = urlObject.hostname;
|
||||
if (domain.startsWith("www.")) {
|
||||
domain = domain.substring("www.".length);
|
||||
}
|
||||
return domain;
|
||||
};
|
||||
|
|
|
@ -7,7 +7,6 @@ import { translatedFields } from "../../fields/translatedFields/translatedFields
|
|||
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
||||
import { createEditor } from "../../utils/editor";
|
||||
import { getBySlugEndpoint } from "./endpoints/getBySlugEndpoint";
|
||||
import { getRootFoldersEndpoint } from "./endpoints/rootEndpoint";
|
||||
|
||||
const fields = {
|
||||
slug: "slug",
|
||||
|
@ -34,7 +33,7 @@ export const Folders = buildCollectionConfig({
|
|||
"Folders provide a way to structure our content. A folder can contain subfolders and/or files.",
|
||||
preview: ({ slug }) => `https://v3.accords-library.com/en/folders/${slug}`,
|
||||
},
|
||||
endpoints: [getRootFoldersEndpoint, getBySlugEndpoint],
|
||||
endpoints: [getBySlugEndpoint],
|
||||
fields: [
|
||||
rowField([
|
||||
slugField({ name: fields.slug }),
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
import payload from "payload";
|
||||
import { Collections } from "../../../constants";
|
||||
import { Folder } from "../../../types/collections";
|
||||
import { CollectionEndpoint } from "../../../types/payload";
|
||||
import { isPayloadType } from "../../../utils/asserts";
|
||||
import { convertFolderToPreview } from "./getBySlugEndpoint";
|
||||
|
||||
export const getRootFoldersEndpoint: CollectionEndpoint = {
|
||||
method: "get",
|
||||
path: "/root",
|
||||
handler: async (req, res) => {
|
||||
if (!req.user) {
|
||||
return res.status(403).send({
|
||||
errors: [
|
||||
{
|
||||
message: "You are not allowed to perform this action.",
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
const homeFolder = (
|
||||
await payload.find({
|
||||
collection: Collections.Folders,
|
||||
limit: 100,
|
||||
where: { slug: { equals: "home" } },
|
||||
})
|
||||
).docs[0];
|
||||
|
||||
if (!homeFolder) {
|
||||
res.status(404);
|
||||
return;
|
||||
}
|
||||
|
||||
const folders = homeFolder.sections?.[0]?.subfolders;
|
||||
|
||||
if (!folders) {
|
||||
res.status(404);
|
||||
return;
|
||||
}
|
||||
|
||||
const result = folders.filter(isPayloadType).filter(isEmptyFolder).map(convertFolderToPreview);
|
||||
|
||||
res.status(200).json(result);
|
||||
},
|
||||
};
|
||||
|
||||
const isEmptyFolder = ({ sections, files }: Folder): boolean => {
|
||||
if (sections && sections.length > 0) return true;
|
||||
if (files && files.length > 0) return true;
|
||||
return false;
|
||||
};
|
|
@ -3,8 +3,7 @@ import type { BreakBlock, Image, SectionBlock, TranscriptBlock } from "./types/c
|
|||
// END MOCKING SECTION
|
||||
|
||||
export enum Collections {
|
||||
ChronologyEras = "chronology-eras",
|
||||
ChronologyItems = "chronology-items",
|
||||
ChronologyEvents = "chronology-events",
|
||||
Currencies = "currencies",
|
||||
Files = "files",
|
||||
Languages = "languages",
|
||||
|
|
|
@ -3,8 +3,7 @@ import { mongooseAdapter } from "@payloadcms/db-mongodb";
|
|||
import path from "path";
|
||||
import { buildConfig } from "payload/config";
|
||||
import { BackgroundImages } from "./collections/BackgroundImages/BackgroundImages";
|
||||
import { ChronologyEras } from "./collections/ChronologyEras/ChronologyEras";
|
||||
import { ChronologyItems } from "./collections/ChronologyItems/ChronologyItems";
|
||||
import { ChronologyEvents } from "./collections/ChronologyEvents/ChronologyEvents";
|
||||
import { Collectibles } from "./collections/Collectibles/Collectibles";
|
||||
import { Currencies } from "./collections/Currencies/Currencies";
|
||||
import { Folders } from "./collections/Folders/Folders";
|
||||
|
@ -44,8 +43,7 @@ export default buildConfig({
|
|||
Pages,
|
||||
Collectibles,
|
||||
Folders,
|
||||
ChronologyItems,
|
||||
ChronologyEras,
|
||||
ChronologyEvents,
|
||||
Notes,
|
||||
|
||||
Images,
|
||||
|
|
40
src/sdk.ts
40
src/sdk.ts
|
@ -320,6 +320,40 @@ export type TableOfContentEntry = {
|
|||
children: TableOfContentEntry[];
|
||||
};
|
||||
|
||||
export type EndpointChronologyEvent = {
|
||||
id: string;
|
||||
date: {
|
||||
year: number;
|
||||
month?: number;
|
||||
day?: number;
|
||||
};
|
||||
events: {
|
||||
sources: EndpointSource[];
|
||||
translations: {
|
||||
language: string;
|
||||
sourceLanguage: string;
|
||||
title?: string;
|
||||
description?: RichTextContent;
|
||||
notes?: RichTextContent;
|
||||
transcribers: EndpointRecorder[];
|
||||
translators: EndpointRecorder[];
|
||||
proofreaders: EndpointRecorder[];
|
||||
}[];
|
||||
}[];
|
||||
};
|
||||
|
||||
export type EndpointSource =
|
||||
| { type: "url"; url: string; label: string }
|
||||
| {
|
||||
type: "collectible";
|
||||
collectible: EndpointCollectiblePreview;
|
||||
range?:
|
||||
| { type: "page"; page: number }
|
||||
| { type: "timestamp"; timestamp: string }
|
||||
| { type: "custom"; translations: { language: string; note: string }[] };
|
||||
}
|
||||
| { type: "page"; page: EndpointPagePreview };
|
||||
|
||||
export type PayloadImage = {
|
||||
url: string;
|
||||
width: number;
|
||||
|
@ -329,8 +363,6 @@ export type PayloadImage = {
|
|||
};
|
||||
|
||||
export const payload = {
|
||||
getEras: async (): Promise<EndpointEra[]> =>
|
||||
await (await request(payloadApiUrl(Collections.ChronologyEras, `all`))).json(),
|
||||
getHomeFolders: async (): Promise<EndpointHomeFolder[]> =>
|
||||
await (await request(payloadApiUrl(Collections.HomeFolders, `all`, true))).json(),
|
||||
getFolder: async (slug: string): Promise<EndpointFolder> =>
|
||||
|
@ -347,4 +379,8 @@ export const payload = {
|
|||
await (await request(payloadApiUrl(Collections.Pages, `slug/${slug}`))).json(),
|
||||
getCollectible: async (slug: string): Promise<EndpointCollectible> =>
|
||||
await (await request(payloadApiUrl(Collections.Collectibles, `slug/${slug}`))).json(),
|
||||
getChronologyEvents: async (): Promise<EndpointChronologyEvent[]> =>
|
||||
await (await request(payloadApiUrl(Collections.ChronologyEvents, `all`))).json(),
|
||||
getChronologyEventByID: async (id: string): Promise<EndpointChronologyEvent> =>
|
||||
await (await request(payloadApiUrl(Collections.ChronologyEvents, id))).json(),
|
||||
};
|
||||
|
|
|
@ -77,10 +77,28 @@ html[data-theme="light"] {
|
|||
.blocks-field__block-pill-cueBlock + .section-title,
|
||||
.blocks-field__block-pill-pageRange + .section-title,
|
||||
.blocks-field__block-pill-timeRange + .section-title,
|
||||
.blocks-field__block-pill-urlBlock + .section-title,
|
||||
.blocks-field__block-pill-pageBlock + .section-title,
|
||||
.blocks-field__block-pill-collectibleBlock + .section-title,
|
||||
.blocks-field__block-pill-page + .section-title,
|
||||
.blocks-field__block-pill-timestamp + .section-title,
|
||||
.blocks-field__block-pill-other + .section-title,
|
||||
.blocks-field__block-pill-other + .section-title {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.collapsible__toggle-wrap:has(
|
||||
.blocks-field__block-pill-collectibleBlock,
|
||||
.blocks-field__block-pill-page,
|
||||
.blocks-field__block-pill-timestamp,
|
||||
.blocks-field__block-pill-other,
|
||||
.blocks-field__block-pill-pageRange,
|
||||
.blocks-field__block-pill-timeRange
|
||||
)
|
||||
.blocks-field__block-number {
|
||||
display: none;
|
||||
}
|
||||
|
||||
// Reduce margin on Lexical blocks with the classname "reduced-margins"
|
||||
|
||||
.rich-text-lexical.field-type.reduced-margins {
|
||||
|
|
|
@ -100,3 +100,12 @@ export const handleRecorder = ({
|
|||
username: anonymize ? `Recorder#${id.substring(0, 5)}` : username,
|
||||
...(isValidPayloadImage(avatar) ? { avatar } : {}),
|
||||
});
|
||||
|
||||
export const getDomainFromUrl = (url: string): string => {
|
||||
const urlObject = new URL(url);
|
||||
let domain = urlObject.hostname;
|
||||
if (domain.startsWith("www.")) {
|
||||
domain = domain.substring("www.".length);
|
||||
}
|
||||
return domain;
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue