285 lines
10 KiB
TypeScript
285 lines
10 KiB
TypeScript
import { GetStaticProps } from "next";
|
|
import { useMemo } from "react";
|
|
import { AppLayout, AppLayoutRequired } 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 } from "graphql/generated";
|
|
import { getReadySdk } from "graphql/sdk";
|
|
import { filterDefined, filterHasAttributes } from "helpers/others";
|
|
import { Report, Severity } from "helpers/types/Report";
|
|
import { getOpenGraph } from "helpers/openGraph";
|
|
import { getLangui } from "graphql/fetchLocalData";
|
|
|
|
/*
|
|
* ╭────────╮
|
|
* ──────────────────────────────────────────╯ PAGE ╰─────────────────────────────────────────────
|
|
*/
|
|
|
|
interface Props extends AppLayoutRequired {
|
|
contents: DevGetContentsQuery;
|
|
}
|
|
|
|
const CheckupContents = ({ contents, ...otherProps }: Props): JSX.Element => {
|
|
const testReport = useMemo(() => testingContent(contents), [contents]);
|
|
|
|
const contentPanel = useMemo(
|
|
() => (
|
|
<ContentPanel width={ContentPanelWidthSizes.Full}>
|
|
{<h2 className="text-2xl">{testReport.title}</h2>}
|
|
|
|
<div className="my-4 grid grid-cols-[2em,3em,2fr,1fr,0.5fr,0.5fr,2fr] items-center gap-2">
|
|
<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
|
|
.sort((a, b) => a.name.localeCompare(b.name))
|
|
.sort((a, b) => b.severity - a.severity)
|
|
.map((line, index) => (
|
|
<div
|
|
key={index}
|
|
className="mb-2 grid grid-cols-[2em,3em,2fr,1fr,0.5fr,0.5fr,2fr] items-center
|
|
justify-items-start gap-2"
|
|
>
|
|
<Button
|
|
href={line.frontendUrl}
|
|
className="w-4 text-xs"
|
|
text="F"
|
|
alwaysNewTab
|
|
/>
|
|
<Button
|
|
href={line.backendUrl}
|
|
className="w-4 text-xs"
|
|
text="B"
|
|
alwaysNewTab
|
|
/>
|
|
<p>{line.subitems.join(" -> ")}</p>
|
|
<p>{line.name}</p>
|
|
<Chip text={line.type} />
|
|
<Chip
|
|
className={
|
|
line.severity === Severity.VeryHigh
|
|
? "bg-[#f00] font-bold !opacity-100"
|
|
: line.severity === Severity.High
|
|
? "bg-[#ff6600] font-bold !opacity-100"
|
|
: line.severity === Severity.Medium
|
|
? "bg-[#fff344] !opacity-100"
|
|
: ""
|
|
}
|
|
text={Severity[line.severity]}
|
|
/>
|
|
<ToolTip content={line.recommandation} placement="left">
|
|
<p>{line.description}</p>
|
|
</ToolTip>
|
|
</div>
|
|
))}
|
|
</ContentPanel>
|
|
),
|
|
[testReport.lines, testReport.title]
|
|
);
|
|
|
|
return <AppLayout contentPanel={contentPanel} {...otherProps} />;
|
|
};
|
|
export default CheckupContents;
|
|
|
|
/*
|
|
* ╭──────────────────────╮
|
|
* ───────────────────────────────────╯ NEXT DATA FETCHING ╰──────────────────────────────────────
|
|
*/
|
|
|
|
export const getStaticProps: GetStaticProps = async (context) => {
|
|
const sdk = getReadySdk();
|
|
const langui = getLangui(context.locale);
|
|
const contents = await sdk.devGetContents();
|
|
const props: Props = {
|
|
contents: contents,
|
|
openGraph: getOpenGraph(langui, "Checkup Contents"),
|
|
};
|
|
return {
|
|
props: props,
|
|
};
|
|
};
|
|
|
|
/*
|
|
* ╭───────────────────╮
|
|
* ─────────────────────────────────────╯ PRIVATE METHODS ╰───────────────────────────────────────
|
|
*/
|
|
|
|
const testingContent = (contents: Props["contents"]): Report => {
|
|
const report: Report = {
|
|
title: "Contents",
|
|
lines: [],
|
|
};
|
|
|
|
filterHasAttributes(contents.contents?.data, ["attributes"] as const).map(
|
|
(content) => {
|
|
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: Severity.High,
|
|
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 Type",
|
|
type: "Missing",
|
|
severity: Severity.High,
|
|
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: 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.thumbnail?.data?.id) {
|
|
report.lines.push({
|
|
subitems: [content.attributes.slug],
|
|
name: "No Thumbnail",
|
|
type: "Missing",
|
|
severity: Severity.High,
|
|
description: "The Content has no Thumbnail.",
|
|
recommandation: "",
|
|
backendUrl: backendUrl,
|
|
frontendUrl: frontendUrl,
|
|
});
|
|
}
|
|
|
|
if (content.attributes.translations?.length === 0) {
|
|
report.lines.push({
|
|
subitems: [content.attributes.slug],
|
|
name: "No Titles",
|
|
type: "Missing",
|
|
severity: Severity.High,
|
|
description: "The Content has no Titles.",
|
|
recommandation: "",
|
|
backendUrl: backendUrl,
|
|
frontendUrl: frontendUrl,
|
|
});
|
|
} else {
|
|
const titleLanguages: string[] = [];
|
|
|
|
if (
|
|
content.attributes.translations &&
|
|
content.attributes.translations.length > 0
|
|
) {
|
|
filterDefined(content.attributes.translations).map(
|
|
(translation, titleIndex) => {
|
|
if (translation.language?.data?.id) {
|
|
if (translation.language.data.id in titleLanguages) {
|
|
report.lines.push({
|
|
subitems: [
|
|
content.attributes.slug,
|
|
`Title ${titleIndex.toString()}`,
|
|
],
|
|
name: "Duplicate Language",
|
|
type: "Error",
|
|
severity: Severity.High,
|
|
description: "",
|
|
recommandation: "",
|
|
backendUrl: backendUrl,
|
|
frontendUrl: frontendUrl,
|
|
});
|
|
} else {
|
|
titleLanguages.push(translation.language.data.id);
|
|
}
|
|
} else {
|
|
report.lines.push({
|
|
subitems: [
|
|
content.attributes.slug,
|
|
`Title ${titleIndex.toString()}`,
|
|
],
|
|
name: "No Language",
|
|
type: "Error",
|
|
severity: Severity.VeryHigh,
|
|
description: "",
|
|
recommandation: "",
|
|
backendUrl: backendUrl,
|
|
frontendUrl: frontendUrl,
|
|
});
|
|
}
|
|
if (!translation.description) {
|
|
report.lines.push({
|
|
subitems: [
|
|
content.attributes.slug,
|
|
`Title ${titleIndex.toString()}`,
|
|
],
|
|
name: "No Description",
|
|
type: "Missing",
|
|
severity: Severity.Medium,
|
|
description: "",
|
|
recommandation: "",
|
|
backendUrl: backendUrl,
|
|
frontendUrl: frontendUrl,
|
|
});
|
|
}
|
|
|
|
if (!translation.text_set) {
|
|
report.lines.push({
|
|
subitems: [
|
|
content.attributes.slug,
|
|
translation.language?.data?.attributes?.code ?? "",
|
|
],
|
|
name: "No Text Set",
|
|
type: "Missing",
|
|
severity: Severity.High,
|
|
description: "The Content has no Text Set.",
|
|
recommandation: "",
|
|
backendUrl: backendUrl,
|
|
frontendUrl: frontendUrl,
|
|
});
|
|
}
|
|
}
|
|
);
|
|
} else {
|
|
report.lines.push({
|
|
subitems: [content.attributes.slug],
|
|
name: "No Translations",
|
|
type: "Missing",
|
|
severity: Severity.High,
|
|
description: "The Content has no Translations.",
|
|
recommandation: "",
|
|
backendUrl: backendUrl,
|
|
frontendUrl: frontendUrl,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
);
|
|
return report;
|
|
};
|