diff --git a/src/graphql/operations/devGetContents.graphql b/src/graphql/operations/devGetContents.graphql new file mode 100644 index 0000000..b0be407 --- /dev/null +++ b/src/graphql/operations/devGetContents.graphql @@ -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 + } + } + } + } + } +} + diff --git a/src/graphql/operations/devGetLibraryItems.graphql b/src/graphql/operations/devGetLibraryItems.graphql new file mode 100644 index 0000000..6d32ac7 --- /dev/null +++ b/src/graphql/operations/devGetLibraryItems.graphql @@ -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 + } + } + } + } + } + } + } +} diff --git a/src/pages/dev/checkup.tsx b/src/pages/dev/checkup.tsx deleted file mode 100644 index d0f2fae..0000000 --- a/src/pages/dev/checkup.tsx +++ /dev/null @@ -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 ; -} - -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 - ); - } - }); -} diff --git a/src/pages/dev/checkup/contents.tsx b/src/pages/dev/checkup/contents.tsx new file mode 100644 index 0000000..15eab1b --- /dev/null +++ b/src/pages/dev/checkup/contents.tsx @@ -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 = ( + + {

{testReport.title}

} + +
+

+

+

Ref

+

Name

+

Type

+

Severity

+

Description

+
+ + {testReport.lines.map((line, index) => ( +
+ + +

{line.subitems.join(" -> ")}

+

{line.name}

+ {line.type} + + {line.severity} + + +

{line.description}

+
+
+ ))} +
+ ); + return ( + + ); +} + +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; +} diff --git a/src/pages/dev/checkup/libraryitems.tsx b/src/pages/dev/checkup/libraryitems.tsx new file mode 100644 index 0000000..4513f03 --- /dev/null +++ b/src/pages/dev/checkup/libraryitems.tsx @@ -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 = ( + + {

{testReport.title}

} + +
+

+

+

Ref

+

Name

+

Type

+

Severity

+

Description

+
+ + {testReport.lines.map((line, index) => ( +
+ + +

{line.subitems.join(" -> ")}

+

{line.name}

+ {line.type} + + {line.severity} + + +

{line.description}

+
+
+ ))} +
+ ); + return ( + + ); +} + +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; +}