Added back the data checkups in their own separate page

This commit is contained in:
DrMint 2022-05-04 04:11:03 +02:00
parent 0e130da9ea
commit 379ed4ce20
5 changed files with 1511 additions and 595 deletions

View File

@ -0,0 +1,85 @@
query devGetContents {
contents(pagination: { limit: -1 }) {
data {
id
attributes {
slug
categories {
data {
id
}
}
type {
data {
id
}
}
titles {
language {
data {
id
}
}
title
description
}
ranged_contents {
data {
id
}
}
text_set {
language {
data {
id
}
}
source_language {
data {
id
}
}
status
transcribers {
data {
id
}
}
translators {
data {
id
}
}
proofreaders {
data {
id
}
}
text
}
video_set {
id
}
audio_set {
id
}
thumbnail {
data {
id
}
}
next_recommended {
data {
id
}
}
previous_recommended {
data {
id
}
}
}
}
}
}

View File

@ -0,0 +1,183 @@
query devGetLibraryItems {
libraryItems(pagination: { limit: -1 }) {
data {
id
attributes {
slug
thumbnail {
data {
id
}
}
subitems {
data {
id
}
}
subitem_of {
data {
id
}
}
root_item
price {
amount
currency {
data {
id
}
}
}
metadata {
__typename
}
size {
width
height
thickness
}
release_date {
year
month
day
}
descriptions {
description
language {
data {
id
}
}
}
contents {
data {
id
}
}
digital
categories {
data {
id
}
}
urls {
url
}
images {
language {
data {
id
}
}
source_language {
data {
id
}
}
status
scanners {
data {
id
}
}
cleaners {
data {
id
}
}
typesetters {
data {
id
}
}
cover {
front {
data {
id
}
}
spine {
data {
id
}
}
back {
data {
id
}
}
full {
data {
id
}
}
}
dust_jacket {
front {
data {
id
}
}
spine {
data {
id
}
}
back {
data {
id
}
}
flap_front {
data {
id
}
}
flap_back {
data {
id
}
}
full {
data {
id
}
}
}
obi_belt {
front {
data {
id
}
}
spine {
data {
id
}
}
back {
data {
id
}
}
flap_front {
data {
id
}
}
flap_back {
data {
id
}
}
full {
data {
id
}
}
}
}
}
}
}
}

View File

@ -1,595 +0,0 @@
import AppLayout from "components/AppLayout";
import {
GetChronologyItemsQuery,
GetContentTextQuery,
GetErasQuery,
GetLibraryItemQuery,
} from "graphql/generated";
import { GetStaticPropsContext } from "next";
import { NextRouter, useRouter } from "next/router";
import { AppStaticProps, getAppStaticProps } from "queries/getAppStaticProps";
import { sortContent } from "queries/helpers";
interface Props extends AppStaticProps {}
export default function Checkup(props: Props): JSX.Element {
return <AppLayout navTitle={"Checkup"} {...props} />;
}
export async function getStaticProps(
context: GetStaticPropsContext
): Promise<{ notFound: boolean } | { props: Props }> {
const props: Props = {
...(await getAppStaticProps(context)),
};
return {
props: props,
};
}
function prettyTestWarning(
router: NextRouter,
message: string,
subCategory: string[],
url: string
): void {
prettyTestWritter(TestingLevel.Warning, router, message, subCategory, url);
}
function prettyTestError(
router: NextRouter,
message: string,
subCategory: string[],
url: string
): void {
prettyTestWritter(TestingLevel.Error, router, message, subCategory, url);
}
enum TestingLevel {
Warning = "warn",
Error = "error",
}
function prettyTestWritter(
level: TestingLevel,
{ asPath, locale }: NextRouter,
message: string,
subCategory: string[],
url: string
): void {
const line = [
level,
`${process.env.NEXT_PUBLIC_URL_SELF}/${locale}${asPath}`,
locale,
subCategory.join(" -> "),
message,
process.env.NEXT_PUBLIC_URL_CMS + url,
];
if (process.env.ENABLE_TESTING_LOG) {
if (level === TestingLevel.Warning) {
console.warn(line.join("\t"));
} else {
console.error(line.join("\t"));
}
}
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
function useTestingContent(props: {
content: Exclude<
GetContentTextQuery["contents"],
null | undefined
>["data"][number]["attributes"];
contentId: Exclude<
GetContentTextQuery["contents"],
null | undefined
>["data"][number]["id"];
}) {
const router = useRouter();
const { content, contentId } = props;
const contentURL = `/admin/content-manager/collectionType/api::content.content/${contentId}`;
if (router.locale === "en") {
if (content?.categories?.data.length === 0) {
prettyTestError(router, "Missing categories", ["content"], contentURL);
}
}
if (content?.ranged_contents?.data.length === 0) {
prettyTestWarning(
router,
"Unconnected to any source",
["content"],
contentURL
);
}
if (content?.text_set?.length === 0) {
prettyTestWarning(
router,
"Has no textset, nor audioset, nor videoset",
["content"],
contentURL
);
}
if (content?.text_set && content.text_set.length > 1) {
prettyTestError(
router,
"More than one textset for this language",
["content", "text_set"],
contentURL
);
}
if (content?.text_set?.length === 1) {
const textset = content.text_set[0];
if (!textset?.text) {
prettyTestError(
router,
"Missing text",
["content", "text_set"],
contentURL
);
}
if (!textset?.source_language?.data) {
prettyTestError(
router,
"Missing source language",
["content", "text_set"],
contentURL
);
} else if (
textset.source_language.data.attributes?.code === router.locale
) {
// This is a transcript
if (textset.transcribers?.data.length === 0) {
prettyTestError(
router,
"Missing transcribers attribution",
["content", "text_set"],
contentURL
);
}
if (textset.translators && textset.translators.data.length > 0) {
prettyTestError(
router,
"Transcripts shouldn't have translators",
["content", "text_set"],
contentURL
);
}
} else {
// This is a translation
if (textset.translators?.data.length === 0) {
prettyTestError(
router,
"Missing translators attribution",
["content", "text_set"],
contentURL
);
}
if (textset.transcribers && textset.transcribers.data.length > 0) {
prettyTestError(
router,
"Translations shouldn't have transcribers",
["content", "text_set"],
contentURL
);
}
}
}
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
function useTestingLibrary(props: {
item: Exclude<
GetLibraryItemQuery["libraryItems"],
null | undefined
>["data"][number]["attributes"];
itemId: Exclude<
GetLibraryItemQuery["libraryItems"],
null | undefined
>["data"][number]["id"];
}) {
const { item, itemId } = props;
const router = useRouter();
const libraryItemURL = `/admin/content-manager/collectionType/api::library-item.library-item/${itemId}`;
sortContent(item?.contents);
if (router.locale === "en") {
if (!item?.thumbnail?.data) {
prettyTestError(
router,
"Missing thumbnail",
["libraryItem"],
libraryItemURL
);
}
if (item?.metadata?.length === 0) {
prettyTestError(
router,
"Missing metadata",
["libraryItem"],
libraryItemURL
);
} else if (
item?.metadata?.[0]?.__typename === "ComponentMetadataGroup" &&
(item.metadata[0].subtype?.data?.attributes?.slug === "relation-set" ||
item.metadata[0].subtype?.data?.attributes?.slug === "variant-set")
) {
// This is a group type item
if (item.price) {
prettyTestError(
router,
"Group-type items shouldn't have price",
["libraryItem"],
libraryItemURL
);
}
if (item.size) {
prettyTestError(
router,
"Group-type items shouldn't have size",
["libraryItem"],
libraryItemURL
);
}
if (item.release_date) {
prettyTestError(
router,
"Group-type items shouldn't have release_date",
["libraryItem"],
libraryItemURL
);
}
if (item.contents && item.contents.data.length > 0) {
prettyTestError(
router,
"Group-type items shouldn't have contents",
["libraryItem"],
libraryItemURL
);
}
if (item.subitems && item.subitems.data.length === 0) {
prettyTestError(
router,
"Group-type items should have subitems",
["libraryItem"],
libraryItemURL
);
}
} else {
// This is a normal item
if (item?.metadata?.[0]?.__typename === "ComponentMetadataGroup") {
if (item.subitems?.data.length === 0) {
prettyTestError(
router,
"Group-type item should have subitems",
["libraryItem"],
libraryItemURL
);
}
}
if (item?.price) {
if (!item.price.amount) {
prettyTestError(
router,
"Missing amount",
["libraryItem", "price"],
libraryItemURL
);
}
if (!item.price.currency) {
prettyTestError(
router,
"Missing currency",
["libraryItem", "price"],
libraryItemURL
);
}
} else {
prettyTestWarning(
router,
"Missing price",
["libraryItem"],
libraryItemURL
);
}
if (!item?.digital) {
if (item?.size) {
if (!item.size.width) {
prettyTestWarning(
router,
"Missing width",
["libraryItem", "size"],
libraryItemURL
);
}
if (!item.size.height) {
prettyTestWarning(
router,
"Missing height",
["libraryItem", "size"],
libraryItemURL
);
}
if (!item.size.thickness) {
prettyTestWarning(
router,
"Missing thickness",
["libraryItem", "size"],
libraryItemURL
);
}
} else {
prettyTestWarning(
router,
"Missing size",
["libraryItem"],
libraryItemURL
);
}
}
if (item?.release_date) {
if (!item.release_date.year) {
prettyTestError(
router,
"Missing year",
["libraryItem", "release_date"],
libraryItemURL
);
}
if (!item.release_date.month) {
prettyTestError(
router,
"Missing month",
["libraryItem", "release_date"],
libraryItemURL
);
}
if (!item.release_date.day) {
prettyTestError(
router,
"Missing day",
["libraryItem", "release_date"],
libraryItemURL
);
}
} else {
prettyTestWarning(
router,
"Missing release_date",
["libraryItem"],
libraryItemURL
);
}
if (item?.contents?.data.length === 0) {
prettyTestWarning(
router,
"Missing contents",
["libraryItem"],
libraryItemURL
);
} else {
let currentRangePage = 0;
item?.contents?.data.map((content) => {
const contentURL = `/admin/content-manager/collectionType/api::content.content/${content.id}`;
if (content.attributes?.scan_set?.length === 0) {
prettyTestWarning(
router,
"Missing scan_set",
["libraryItem", "content", content.id ?? ""],
contentURL
);
}
if (content.attributes?.range.length === 0) {
prettyTestWarning(
router,
"Missing range",
["libraryItem", "content", content.id ?? ""],
contentURL
);
} else if (
content.attributes?.range[0]?.__typename ===
"ComponentRangePageRange"
) {
if (
content.attributes.range[0].starting_page <
currentRangePage + 1
) {
prettyTestError(
router,
`Overlapping pages ${content.attributes.range[0].starting_page} to ${currentRangePage}`,
["libraryItem", "content", content.id ?? "", "range"],
libraryItemURL
);
} else if (
content.attributes.range[0].starting_page >
currentRangePage + 1
) {
prettyTestError(
router,
`Missing pages ${currentRangePage + 1} to ${
content.attributes.range[0].starting_page - 1
}`,
["libraryItem", "content", content.id ?? "", "range"],
libraryItemURL
);
}
if (!content.attributes.content?.data) {
prettyTestWarning(
router,
"Missing content",
["libraryItem", "content", content.id ?? "", "range"],
libraryItemURL
);
}
currentRangePage = content.attributes.range[0].ending_page;
}
});
if (item?.metadata?.[0]?.__typename === "ComponentMetadataBooks") {
if (item.metadata[0].languages?.data.length === 0) {
prettyTestWarning(
router,
"Missing language",
["libraryItem", "metadata"],
libraryItemURL
);
}
if (item.metadata[0].page_count) {
if (currentRangePage < item.metadata[0].page_count) {
prettyTestError(
router,
`Missing pages ${currentRangePage + 1} to ${
item.metadata[0].page_count
}`,
["libraryItem", "content"],
libraryItemURL
);
} else if (currentRangePage > item.metadata[0].page_count) {
prettyTestError(
router,
`Page overflow, content references pages up to ${currentRangePage} when the highest expected was ${item.metadata[0].page_count}`,
["libraryItem", "content"],
libraryItemURL
);
}
} else {
prettyTestWarning(
router,
"Missing page_count",
["libraryItem", "metadata"],
libraryItemURL
);
}
}
}
}
if (!item?.root_item && item?.subitem_of?.data.length === 0) {
prettyTestError(
router,
"This item is inaccessible (not root item and not subitem of another item)",
["libraryItem"],
libraryItemURL
);
}
if (item?.gallery?.data.length === 0) {
prettyTestWarning(
router,
"Missing gallery",
["libraryItem"],
libraryItemURL
);
}
}
if (item?.descriptions?.length === 0) {
prettyTestWarning(
router,
"Missing description",
["libraryItem"],
libraryItemURL
);
}
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
function useTestingChronology(props: {
chronologyItems: Exclude<
GetChronologyItemsQuery["chronologyItems"],
null | undefined
>["data"];
chronologyEras: Exclude<
GetErasQuery["chronologyEras"],
null | undefined
>["data"];
}) {
const router = useRouter();
const { chronologyItems, chronologyEras } = props;
chronologyEras.map((era) => {
const chronologyErasURL = `/admin/content-manager/collectionType/api::chronology-era.chronology-era/${chronologyItems[0].id}`;
if (era.attributes?.title?.length === 0) {
prettyTestError(
router,
"Missing translation for title and description, using slug instead",
["chronologyEras", era.attributes.slug],
chronologyErasURL
);
} else if (era.attributes?.title && era.attributes.title.length > 1) {
prettyTestError(
router,
"More than one title and description",
["chronologyEras", era.attributes.slug],
chronologyErasURL
);
} else {
if (!era.attributes?.title?.[0]?.title)
prettyTestError(
router,
"Missing title, using slug instead",
["chronologyEras", era.attributes?.slug ?? ""],
chronologyErasURL
);
if (!era.attributes?.title?.[0]?.description)
prettyTestError(
router,
"Missing description",
["chronologyEras", era.attributes?.slug ?? ""],
chronologyErasURL
);
}
});
chronologyItems.map((item) => {
const chronologyItemsURL = `/admin/content-manager/collectionType/api::chronology-item.chronology-item/${chronologyItems[0].id}`;
const date = `${item.attributes?.year}/${item.attributes?.month}/${item.attributes?.day}`;
if (item.attributes?.events && item.attributes.events.length > 0) {
item.attributes.events.map((event) => {
if (!event?.source?.data) {
prettyTestError(
router,
"No source for this event",
["chronologyItems", date, event?.id ?? ""],
chronologyItemsURL
);
}
if (!(event?.translations && event.translations.length > 0)) {
prettyTestWarning(
router,
"No translation for this event",
["chronologyItems", date, event?.id ?? ""],
chronologyItemsURL
);
}
});
} else {
prettyTestError(
router,
"No events for this date",
["chronologyItems", date],
chronologyItemsURL
);
}
});
}

View File

@ -0,0 +1,486 @@
import AppLayout from "components/AppLayout";
import Chip from "components/Chip";
import Button from "components/Inputs/Button";
import ContentPanel, {
ContentPanelWidthSizes,
} from "components/Panels/ContentPanel";
import ToolTip from "components/ToolTip";
import {
DevGetContentsQuery,
Enum_Componentsetstextset_Status,
} from "graphql/generated";
import { getReadySdk } from "graphql/sdk";
import { GetStaticPropsContext } from "next";
import { AppStaticProps, getAppStaticProps } from "queries/getAppStaticProps";
interface Props extends AppStaticProps {
contents: DevGetContentsQuery;
}
export default function CheckupContents(props: Props): JSX.Element {
const { contents } = props;
const testReport = testingContent(contents);
const contentPanel = (
<ContentPanel width={ContentPanelWidthSizes.large}>
{<h2 className="text-2xl">{testReport.title}</h2>}
<div className="grid grid-cols-[2em,3em,2fr,1fr,0.5fr,0.5fr,2fr] gap-2 items-center my-4">
<p></p>
<p></p>
<p className="font-headers">Ref</p>
<p className="font-headers">Name</p>
<p className="font-headers">Type</p>
<p className="font-headers">Severity</p>
<p className="font-headers">Description</p>
</div>
{testReport.lines.map((line, index) => (
<div
key={index}
className="grid grid-cols-[2em,3em,2fr,1fr,0.5fr,0.5fr,2fr] gap-2 items-center mb-2 justify-items-start"
>
<Button
href={line.frontendUrl}
target="_blank"
className="text-xs w-4"
>
F
</Button>
<Button
href={line.backendUrl}
target="_blank"
className="text-xs w-4"
>
B
</Button>
<p>{line.subitems.join(" -> ")}</p>
<p>{line.name}</p>
<Chip>{line.type}</Chip>
<Chip
className={
line.severity === "Very High"
? "bg-[#f00] !opacity-100 font-bold"
: line.severity === "High"
? "bg-[#ff6600] !opacity-100 font-bold"
: line.severity === "Medium"
? "bg-[#fff344] !opacity-100"
: ""
}
>
{line.severity}
</Chip>
<ToolTip content={line.recommandation} placement="left">
<p>{line.description}</p>
</ToolTip>
</div>
))}
</ContentPanel>
);
return (
<AppLayout navTitle={"Checkup"} contentPanel={contentPanel} {...props} />
);
}
export async function getStaticProps(
context: GetStaticPropsContext
): Promise<{ notFound: boolean } | { props: Props }> {
const sdk = getReadySdk();
const contents = await sdk.devGetContents();
const props: Props = {
...(await getAppStaticProps(context)),
contents: contents,
};
return {
props: props,
};
}
type Report = {
title: string;
lines: ReportLine[];
};
type ReportLine = {
subitems: string[];
name: string;
type: "Error" | "Improvement" | "Missing";
severity: "High" | "Low" | "Medium" | "Very High" | "Very Low";
description: string;
recommandation: string;
backendUrl: string;
frontendUrl: string;
};
function testingContent(contents: Props["contents"]): Report {
const report: Report = {
title: "Contents",
lines: [],
};
contents.contents?.data.map((content) => {
if (content.attributes) {
const backendUrl = `${process.env.NEXT_PUBLIC_URL_CMS}/admin/content-manager/collectionType/api::content.content/${content.id}`;
const frontendUrl = `${process.env.NEXT_PUBLIC_URL_SELF}/contents/${content.attributes.slug}`;
if (content.attributes.categories?.data.length === 0) {
report.lines.push({
subitems: [content.attributes.slug],
name: "No Category",
type: "Missing",
severity: "Medium",
description: "The Content has no Category.",
recommandation: "Select a Category in relation with the Content",
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
}
if (!content.attributes.type?.data?.id) {
report.lines.push({
subitems: [content.attributes.slug],
name: "No Category",
type: "Missing",
severity: "Medium",
description: "The Content has no Type.",
recommandation: 'If unsure, use the "Other" Type.',
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
}
if (content.attributes.ranged_contents?.data.length === 0) {
report.lines.push({
subitems: [content.attributes.slug],
name: "No Ranged Content",
type: "Improvement",
severity: "Low",
description: "The Content has no Ranged Content.",
recommandation:
"If this Content is available in one or multiple Library Item(s), create a Range Content to connect the Content to its Library Item(s).",
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
}
if (
content.attributes.next_recommended?.data?.id === content.id ||
content.attributes.previous_recommended?.data?.id === content.id
) {
report.lines.push({
subitems: [content.attributes.slug],
name: "Self Recommendation",
type: "Error",
severity: "Very High",
description:
"The Content is referring to itself as a Next or Previous Recommended.",
recommandation: "",
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
}
if (!content.attributes.thumbnail?.data?.id) {
report.lines.push({
subitems: [content.attributes.slug],
name: "No Thumbnail",
type: "Missing",
severity: "High",
description: "The Content has no Thumbnail.",
recommandation: "",
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
}
if (content.attributes.titles?.length === 0) {
report.lines.push({
subitems: [content.attributes.slug],
name: "No Titles",
type: "Missing",
severity: "High",
description: "The Content has no Titles.",
recommandation: "",
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
} else {
const titleLanguages: string[] = [];
content.attributes.titles?.map((title, titleIndex) => {
if (title && content.attributes) {
if (title.language?.data?.id) {
if (title.language.data.id in titleLanguages) {
report.lines.push({
subitems: [
content.attributes.slug,
`Title ${titleIndex.toString()}`,
],
name: "Duplicate Language",
type: "Error",
severity: "High",
description: "",
recommandation: "",
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
} else {
titleLanguages.push(title.language.data.id);
}
} else {
report.lines.push({
subitems: [
content.attributes.slug,
`Title ${titleIndex.toString()}`,
],
name: "No Language",
type: "Error",
severity: "Very High",
description: "",
recommandation: "",
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
}
if (!title.description) {
report.lines.push({
subitems: [
content.attributes.slug,
`Title ${titleIndex.toString()}`,
],
name: "No Description",
type: "Missing",
severity: "Medium",
description: "",
recommandation: "",
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
}
}
});
}
if (
content.attributes.text_set?.length === 0 &&
content.attributes.audio_set?.length === 0 &&
content.attributes.video_set?.length === 0
) {
report.lines.push({
subitems: [content.attributes.slug],
name: "No Sets",
type: "Missing",
severity: "Medium",
description: "The Content has no Sets.",
recommandation: "",
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
} else {
if (content.attributes.video_set?.length === 0) {
report.lines.push({
subitems: [content.attributes.slug],
name: "No Video Sets",
type: "Missing",
severity: "Very Low",
description: "The Content has no Video Sets.",
recommandation: "",
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
}
if (content.attributes.audio_set?.length === 0) {
report.lines.push({
subitems: [content.attributes.slug],
name: "No Audio Sets",
type: "Missing",
severity: "Very Low",
description: "The Content has no Audio Sets.",
recommandation: "",
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
}
if (content.attributes.text_set?.length === 0) {
report.lines.push({
subitems: [content.attributes.slug],
name: "No Text Set",
type: "Missing",
severity: "Medium",
description: "The Content has no Text Set.",
recommandation: "",
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
} else {
const textSetLanguages: string[] = [];
content.attributes.text_set?.map((textSet, textSetIndex) => {
if (content.attributes && textSet) {
if (textSet.language?.data?.id) {
if (textSet.language.data.id in textSetLanguages) {
report.lines.push({
subitems: [
content.attributes.slug,
`TextSet ${textSetIndex.toString()}`,
],
name: "Duplicate Language",
type: "Error",
severity: "High",
description: "",
recommandation: "",
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
} else {
textSetLanguages.push(textSet.language.data.id);
}
} else {
report.lines.push({
subitems: [
content.attributes.slug,
`TextSet ${textSetIndex.toString()}`,
],
name: "No Language",
type: "Error",
severity: "Very High",
description: "",
recommandation: "",
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
}
if (!textSet.source_language?.data?.id) {
report.lines.push({
subitems: [
content.attributes.slug,
`TextSet ${textSetIndex.toString()}`,
],
name: "No Source Language",
type: "Error",
severity: "High",
description: "",
recommandation: "",
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
}
if (textSet.status !== Enum_Componentsetstextset_Status.Done) {
report.lines.push({
subitems: [
content.attributes.slug,
`TextSet ${textSetIndex.toString()}`,
],
name: "Not Done Status",
type: "Improvement",
severity: "Low",
description: "",
recommandation: "",
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
}
if (!textSet.text || textSet.text.length < 10) {
report.lines.push({
subitems: [
content.attributes.slug,
`TextSet ${textSetIndex.toString()}`,
],
name: "No Text",
type: "Missing",
severity: "Medium",
description: "",
recommandation: "",
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
}
if (
textSet.source_language?.data?.id === textSet.language?.data?.id
) {
if (textSet.transcribers?.data.length === 0) {
report.lines.push({
subitems: [
content.attributes.slug,
`TextSet ${textSetIndex.toString()}`,
],
name: "No Transcribers",
type: "Missing",
severity: "High",
description:
"The Content is a Transcription but doesn't credit any Transcribers.",
recommandation: "Add the appropriate Transcribers.",
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
}
if (
textSet.translators?.data &&
textSet.translators.data.length > 0
) {
report.lines.push({
subitems: [
content.attributes.slug,
`TextSet ${textSetIndex.toString()}`,
],
name: "Credited Translators",
type: "Error",
severity: "High",
description:
"The Content is a Transcription but credits one or more Translators.",
recommandation:
"If appropriate, create a Translation Text Set with the Translator credited there.",
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
}
} else {
if (textSet.translators?.data.length === 0) {
report.lines.push({
subitems: [
content.attributes.slug,
`TextSet ${textSetIndex.toString()}`,
],
name: "No Translators",
type: "Missing",
severity: "High",
description:
"The Content is a Transcription but doesn't credit any Translators.",
recommandation: "Add the appropriate Translators.",
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
}
if (
textSet.transcribers?.data &&
textSet.transcribers.data.length > 0
) {
report.lines.push({
subitems: [
content.attributes.slug,
`TextSet ${textSetIndex.toString()}`,
],
name: "Credited Transcribers",
type: "Error",
severity: "High",
description:
"The Content is a Translation but credits one or more Transcribers.",
recommandation:
"If appropriate, create a Transcription Text Set with the Transcribers credited there.",
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
}
}
}
});
}
}
}
});
return report;
}

View File

@ -0,0 +1,757 @@
import AppLayout from "components/AppLayout";
import Chip from "components/Chip";
import Button from "components/Inputs/Button";
import ContentPanel, {
ContentPanelWidthSizes,
} from "components/Panels/ContentPanel";
import ToolTip from "components/ToolTip";
import {
DevGetLibraryItemsQuery,
Enum_Componentcollectionscomponentlibraryimages_Status,
} from "graphql/generated";
import { getReadySdk } from "graphql/sdk";
import { GetStaticPropsContext } from "next";
import { AppStaticProps, getAppStaticProps } from "queries/getAppStaticProps";
interface Props extends AppStaticProps {
libraryItems: DevGetLibraryItemsQuery;
}
export default function CheckupLibraryItems(props: Props): JSX.Element {
const { libraryItems } = props;
const testReport = testingLibraryItem(libraryItems);
const contentPanel = (
<ContentPanel width={ContentPanelWidthSizes.large}>
{<h2 className="text-2xl">{testReport.title}</h2>}
<div className="grid grid-cols-[2em,3em,2fr,1fr,0.5fr,0.5fr,2fr] gap-2 items-center my-4">
<p></p>
<p></p>
<p className="font-headers">Ref</p>
<p className="font-headers">Name</p>
<p className="font-headers">Type</p>
<p className="font-headers">Severity</p>
<p className="font-headers">Description</p>
</div>
{testReport.lines.map((line, index) => (
<div
key={index}
className="grid grid-cols-[2em,3em,2fr,1fr,0.5fr,0.5fr,2fr] gap-2 items-center mb-2 justify-items-start"
>
<Button
href={line.frontendUrl}
target="_blank"
className="text-xs w-4"
>
F
</Button>
<Button
href={line.backendUrl}
target="_blank"
className="text-xs w-4"
>
B
</Button>
<p>{line.subitems.join(" -> ")}</p>
<p>{line.name}</p>
<Chip>{line.type}</Chip>
<Chip
className={
line.severity === "Very High"
? "bg-[#f00] !opacity-100 font-bold"
: line.severity === "High"
? "bg-[#ff6600] !opacity-100 font-bold"
: line.severity === "Medium"
? "bg-[#fff344] !opacity-100"
: ""
}
>
{line.severity}
</Chip>
<ToolTip content={line.recommandation} placement="left">
<p>{line.description}</p>
</ToolTip>
</div>
))}
</ContentPanel>
);
return (
<AppLayout navTitle={"Checkup"} contentPanel={contentPanel} {...props} />
);
}
export async function getStaticProps(
context: GetStaticPropsContext
): Promise<{ notFound: boolean } | { props: Props }> {
const sdk = getReadySdk();
const libraryItems = await sdk.devGetLibraryItems();
const props: Props = {
...(await getAppStaticProps(context)),
libraryItems: libraryItems,
};
return {
props: props,
};
}
type Report = {
title: string;
lines: ReportLine[];
};
type ReportLine = {
subitems: string[];
name: string;
type: "Error" | "Improvement" | "Missing";
severity: "High" | "Low" | "Medium" | "Very High" | "Very Low";
description: string;
recommandation: string;
backendUrl: string;
frontendUrl: string;
};
// eslint-disable-next-line @typescript-eslint/no-unused-vars
function testingLibraryItem(libraryItems: Props["libraryItems"]): Report {
const report: Report = {
title: "Contents",
lines: [],
};
libraryItems.libraryItems?.data.map((item) => {
if (item.attributes) {
const backendUrl = `${process.env.NEXT_PUBLIC_URL_CMS}/admin/content-manager/collectionType/api::library-item.library-item/${item.id}`;
const frontendUrl = `${process.env.NEXT_PUBLIC_URL_SELF}/library/${item.attributes.slug}`;
if (item.attributes.categories?.data.length === 0) {
report.lines.push({
subitems: [item.attributes.slug],
name: "No Category",
type: "Missing",
severity: "Medium",
description: "The Item has no Category.",
recommandation: "Select a Category in relation with the Item",
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
}
if (
!item.attributes.root_item &&
item.attributes.subitem_of?.data.length === 0
) {
report.lines.push({
subitems: [item.attributes.slug],
name: "Disconnected Item",
type: "Error",
severity: "Very High",
description:
"The Item is neither a Root Item, nor is it a subitem of another item.",
recommandation: "",
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
}
if (item.attributes.contents?.data.length === 0) {
report.lines.push({
subitems: [item.attributes.slug],
name: "No Contents",
type: "Missing",
severity: "Low",
description: "The Item has no Contents.",
recommandation: "",
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
}
if (!item.attributes.thumbnail?.data?.id) {
report.lines.push({
subitems: [item.attributes.slug],
name: "No Thumbnail",
type: "Missing",
severity: "High",
description: "The Item has no Thumbnail.",
recommandation: "",
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
}
if (item.attributes.images?.length === 0) {
report.lines.push({
subitems: [item.attributes.slug],
name: "No Images",
type: "Missing",
severity: "Low",
description: "The Item has no Images.",
recommandation: "",
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
} else {
item.attributes.images?.map((image, imageIndex) => {
const imagesLanguages: string[] = [];
if (image && item.attributes) {
if (image.language?.data?.id) {
if (image.language.data.id in imagesLanguages) {
report.lines.push({
subitems: [
item.attributes.slug,
`Images ${imageIndex.toString()}`,
],
name: "Duplicate Language",
type: "Error",
severity: "High",
description: "",
recommandation: "",
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
} else {
imagesLanguages.push(image.language.data.id);
}
} else {
report.lines.push({
subitems: [
item.attributes.slug,
`Images ${imageIndex.toString()}`,
],
name: "No Language",
type: "Error",
severity: "Very High",
description: "",
recommandation: "",
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
}
if (!image.source_language?.data?.id) {
report.lines.push({
subitems: [
item.attributes.slug,
`Images ${imageIndex.toString()}`,
],
name: "No Source Language",
type: "Error",
severity: "Very High",
description: "",
recommandation: "",
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
}
if (
image.status !==
Enum_Componentcollectionscomponentlibraryimages_Status.Done
) {
report.lines.push({
subitems: [
item.attributes.slug,
`Images ${imageIndex.toString()}`,
],
name: "Not Done Status",
type: "Improvement",
severity: "Low",
description: "",
recommandation: "",
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
}
if (image.source_language?.data?.id === image.language?.data?.id) {
if (image.scanners?.data.length === 0) {
report.lines.push({
subitems: [
item.attributes.slug,
`Images ${imageIndex.toString()}`,
],
name: "No Scanners",
type: "Missing",
severity: "High",
description:
"The Item is a Scan but doesn't credit any Scanners.",
recommandation: "Add the appropriate Scanners.",
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
}
if (image.cleaners?.data.length === 0) {
report.lines.push({
subitems: [
item.attributes.slug,
`Images ${imageIndex.toString()}`,
],
name: "No Cleaners",
type: "Missing",
severity: "High",
description:
"The Item is a Scan but doesn't credit any Cleaners.",
recommandation: "Add the appropriate Cleaners.",
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
}
if (
image.typesetters?.data &&
image.typesetters.data.length > 0
) {
report.lines.push({
subitems: [
item.attributes.slug,
`Images ${imageIndex.toString()}`,
],
name: "Credited Typesetters",
type: "Error",
severity: "High",
description:
"The Item is a Scan but credits one or more Typesetters.",
recommandation:
"If appropriate, create a Scanlation Images Set Set with the Typesetters credited there.",
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
}
} else {
if (image.typesetters?.data.length === 0) {
report.lines.push({
subitems: [
item.attributes.slug,
`Images ${imageIndex.toString()}`,
],
name: "No Typesetters",
type: "Missing",
severity: "High",
description:
"The Item is a Scanlation but doesn't credit any Typesetters.",
recommandation: "Add the appropriate Typesetters.",
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
}
if (image.scanners?.data && image.scanners.data.length > 0) {
report.lines.push({
subitems: [
item.attributes.slug,
`Images ${imageIndex.toString()}`,
],
name: "Credited Scanners",
type: "Error",
severity: "High",
description:
"The Item is a Scanlation but credits one or more Scanners.",
recommandation:
"If appropriate, create a Scanners Images Set Set with the Scanners credited there.",
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
}
}
if (image.cover) {
if (!image.cover.front?.data?.id) {
report.lines.push({
subitems: [
item.attributes.slug,
`Images ${imageIndex.toString()}`,
"Cover",
],
name: "No Front",
type: "Missing",
severity: "Very High",
description: "",
recommandation: "",
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
}
if (!image.cover.spine?.data?.id) {
report.lines.push({
subitems: [
item.attributes.slug,
`Images ${imageIndex.toString()}`,
"Cover",
],
name: "No spine",
type: "Missing",
severity: "Low",
description: "",
recommandation: "",
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
}
if (!image.cover.back?.data?.id) {
report.lines.push({
subitems: [
item.attributes.slug,
`Images ${imageIndex.toString()}`,
"Cover",
],
name: "No Back",
type: "Missing",
severity: "High",
description: "",
recommandation: "",
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
}
if (!image.cover.full?.data?.id) {
report.lines.push({
subitems: [
item.attributes.slug,
`Images ${imageIndex.toString()}`,
"Cover",
],
name: "No Full",
type: "Missing",
severity: "Low",
description: "",
recommandation: "",
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
}
} else {
report.lines.push({
subitems: [
item.attributes.slug,
`Images ${imageIndex.toString()}`,
],
name: "No Cover",
type: "Missing",
severity: "Medium",
description: "",
recommandation: "",
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
}
if (image.dust_jacket) {
if (!image.dust_jacket.front?.data?.id) {
report.lines.push({
subitems: [
item.attributes.slug,
`Images ${imageIndex.toString()}`,
"Dust Jacket",
],
name: "No Front",
type: "Missing",
severity: "Very High",
description: "",
recommandation: "",
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
}
if (!image.dust_jacket.spine?.data?.id) {
report.lines.push({
subitems: [
item.attributes.slug,
`Images ${imageIndex.toString()}`,
"Dust Jacket",
],
name: "No spine",
type: "Missing",
severity: "Low",
description: "",
recommandation: "",
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
}
if (!image.dust_jacket.back?.data?.id) {
report.lines.push({
subitems: [
item.attributes.slug,
`Images ${imageIndex.toString()}`,
"Dust Jacket",
],
name: "No Back",
type: "Missing",
severity: "High",
description: "",
recommandation: "",
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
}
if (!image.dust_jacket.full?.data?.id) {
report.lines.push({
subitems: [
item.attributes.slug,
`Images ${imageIndex.toString()}`,
"Dust Jacket",
],
name: "No Full",
type: "Missing",
severity: "Low",
description: "",
recommandation: "",
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
}
if (!image.dust_jacket.flap_front?.data?.id) {
report.lines.push({
subitems: [
item.attributes.slug,
`Images ${imageIndex.toString()}`,
"Dust Jacket",
],
name: "No Flap Front",
type: "Missing",
severity: "Medium",
description: "",
recommandation: "",
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
}
if (!image.dust_jacket.flap_back?.data?.id) {
report.lines.push({
subitems: [
item.attributes.slug,
`Images ${imageIndex.toString()}`,
"Dust Jacket",
],
name: "No Flap Back",
type: "Missing",
severity: "Medium",
description: "",
recommandation: "",
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
}
} else {
report.lines.push({
subitems: [
item.attributes.slug,
`Images ${imageIndex.toString()}`,
],
name: "No Dust Jacket",
type: "Missing",
severity: "Very Low",
description: "",
recommandation: "",
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
}
if (image.obi_belt) {
if (!image.obi_belt.front?.data?.id) {
report.lines.push({
subitems: [
item.attributes.slug,
`Images ${imageIndex.toString()}`,
"Obi Belt",
],
name: "No Front",
type: "Missing",
severity: "Very High",
description: "",
recommandation: "",
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
}
if (!image.obi_belt.spine?.data?.id) {
report.lines.push({
subitems: [
item.attributes.slug,
`Images ${imageIndex.toString()}`,
"Obi Belt",
],
name: "No spine",
type: "Missing",
severity: "Low",
description: "",
recommandation: "",
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
}
if (!image.obi_belt.back?.data?.id) {
report.lines.push({
subitems: [
item.attributes.slug,
`Images ${imageIndex.toString()}`,
"Obi Belt",
],
name: "No Back",
type: "Missing",
severity: "High",
description: "",
recommandation: "",
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
}
if (!image.obi_belt.full?.data?.id) {
report.lines.push({
subitems: [
item.attributes.slug,
`Images ${imageIndex.toString()}`,
"Obi Belt",
],
name: "No Full",
type: "Missing",
severity: "Low",
description: "",
recommandation: "",
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
}
if (!image.obi_belt.flap_front?.data?.id) {
report.lines.push({
subitems: [
item.attributes.slug,
`Images ${imageIndex.toString()}`,
"Obi Belt",
],
name: "No Flap Front",
type: "Missing",
severity: "Medium",
description: "",
recommandation: "",
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
}
if (!image.obi_belt.flap_back?.data?.id) {
report.lines.push({
subitems: [
item.attributes.slug,
`Images ${imageIndex.toString()}`,
"Obi Belt",
],
name: "No Flap Back",
type: "Missing",
severity: "Medium",
description: "",
recommandation: "",
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
}
} else {
report.lines.push({
subitems: [
item.attributes.slug,
`Images ${imageIndex.toString()}`,
],
name: "No Obi Belt",
type: "Missing",
severity: "Very Low",
description: "",
recommandation: "",
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
}
}
});
}
if (
item.attributes.descriptions &&
item.attributes.descriptions.length > 0
) {
const descriptionLanguages: string[] = [];
item.attributes.descriptions?.map((description, descriptionIndex) => {
if (description && item.attributes) {
if (description?.description.length < 10) {
report.lines.push({
subitems: [
item.attributes.slug,
`Description ${descriptionIndex}`,
],
name: "No Text",
type: "Missing",
severity: "Very High",
description: "",
recommandation: "",
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
}
if (description.language?.data?.id) {
if (description.language.data.id in descriptionLanguages) {
report.lines.push({
subitems: [
item.attributes.slug,
`Description ${descriptionIndex}`,
],
name: "Duplicate Language",
type: "Error",
severity: "High",
description: "",
recommandation: "",
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
} else {
descriptionLanguages.push(description.language.data.id);
}
} else {
report.lines.push({
subitems: [
item.attributes.slug,
`Description ${descriptionIndex}`,
],
name: "No Language",
type: "Error",
severity: "Very High",
description: "",
recommandation: "",
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
}
}
});
} else {
report.lines.push({
subitems: [item.attributes.slug],
name: "No Description",
type: "Missing",
severity: "Medium",
description: "The Item has no Description.",
recommandation: "",
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
}
if (item.attributes.urls?.length === 0) {
report.lines.push({
subitems: [item.attributes.slug],
name: "No URLs",
type: "Missing",
severity: "Very Low",
description: "The Item has no URLs.",
recommandation: "",
backendUrl: backendUrl,
frontendUrl: frontendUrl,
});
}
}
});
return report;
}