Added chronology events, removed eras

This commit is contained in:
DrMint 2024-03-20 13:31:24 +01:00
parent e91642616d
commit 4fd59464ba
24 changed files with 420 additions and 378 deletions

View File

@ -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 } },
],
}),
}),
],
});

View File

@ -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);
},
};

View File

@ -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) } : {}),
};
}),
}),
},
});

View File

@ -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.");
}
};

View File

@ -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})`
);
}
});
};

View File

@ -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,

View File

@ -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,
},
],
}),
],
},
],
},
],
};

View File

@ -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,
},
],
};

View File

@ -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 }],
};

View File

@ -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;
}
};

View File

@ -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);
}
},
};

View File

@ -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] }),
};

View File

@ -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 "????-??-??";

View File

@ -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;

View File

@ -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)) {

View File

@ -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)) {

View File

@ -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;
};

View File

@ -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 }),

View File

@ -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;
};

View File

@ -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",

View File

@ -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,16 +43,15 @@ export default buildConfig({
Pages,
Collectibles,
Folders,
ChronologyItems,
ChronologyEras,
ChronologyEvents,
Notes,
Images,
BackgroundImages,
RecordersThumbnails,
Videos,
VideosChannels,
Tags,
TagsGroups,
Recorders,

View File

@ -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(),
};

View File

@ -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 {

View File

@ -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;
};