926 lines
32 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import ContentPanel, {
ContentPanelWidthSizes,
} from "components/Panels/ContentPanel";
import { GetStaticPaths, GetStaticProps } from "next";
import {
getLibraryItem,
getLibraryItemsSlugs,
getWebsiteInterface,
} from "graphql/operations";
import {
Enum_Componentmetadatabooks_Binding_Type,
Enum_Componentmetadatabooks_Page_Order,
GetLibraryItemQuery,
GetWebsiteInterfaceQuery,
} from "graphql/operations-types";
import {
convertMmToInch,
prettyDate,
prettyinlineTitle,
prettyItemType,
prettyItemSubType,
prettyPrice,
prettySlug,
prettyTestError,
prettyTestWarning,
sortContent,
} from "queries/helpers";
import SubPanel from "components/Panels/SubPanel";
import ReturnButton from "components/PanelComponents/ReturnButton";
import NavOption from "components/PanelComponents/NavOption";
import Chip from "components/Chip";
import Button from "components/Button";
import HorizontalLine from "components/HorizontalLine";
import AppLayout from "components/AppLayout";
import LibraryItemsPreview from "components/Library/LibraryItemsPreview";
import InsetBox from "components/InsetBox";
import Img, { ImageQuality } from "components/Img";
import { useAppLayout } from "contexts/AppLayoutContext";
import { useRouter } from "next/router";
interface LibrarySlugProps {
libraryItem: GetLibraryItemQuery;
langui: GetWebsiteInterfaceQuery;
}
export default function LibrarySlug(props: LibrarySlugProps): JSX.Element {
useTesting(props);
const item = props.libraryItem.libraryItems.data[0].attributes;
const langui = props.langui.websiteInterfaces.data[0].attributes;
const appLayout = useAppLayout();
const isVariantSet =
item.metadata.length > 0 &&
item.metadata[0].__typename === "ComponentMetadataOther" &&
item.metadata[0].subtype.data.attributes.slug === "variant-set";
sortContent(item.contents);
const subPanel = (
<SubPanel>
<ReturnButton
href="/library/"
title={langui.main_library}
langui={langui}
/>
<HorizontalLine />
<div className="grid gap-4">
<NavOption
title={langui.library_item_summary}
url="#summary"
border
onClick={() => appLayout.setSubPanelOpen(false)}
/>
{item.gallery.data.length > 0 ? (
<NavOption
title={langui.library_item_gallery}
url="#gallery"
border
onClick={() => appLayout.setSubPanelOpen(false)}
/>
) : (
""
)}
<NavOption
title={langui.library_item_details}
url="#details"
border
onClick={() => appLayout.setSubPanelOpen(false)}
/>
{item.subitems.data.length > 0 ? (
<NavOption
title={
isVariantSet
? langui.library_item_variants
: langui.library_item_subitems
}
url={isVariantSet ? "#variants" : "#subitems"}
border
onClick={() => appLayout.setSubPanelOpen(false)}
/>
) : (
""
)}
{item.contents.data.length > 0 ? (
<NavOption
title={langui.library_item_content}
url="#content"
border
/>
) : (
""
)}
</div>
</SubPanel>
);
const contentPanel = (
<ContentPanel width={ContentPanelWidthSizes.large}>
<div className="grid place-items-center gap-12">
<div className="drop-shadow-shade-xl w-full h-[50vh] mobile:h-[80vh] mb-16 relative cursor-pointer">
{item.thumbnail.data ? (
<Img
image={item.thumbnail.data.attributes}
quality={ImageQuality.Medium}
layout="fill"
objectFit="contain"
priority
/>
) : (
<div className="w-full aspect-[21/29.7] bg-light rounded-xl"></div>
)}
</div>
<InsetBox id="summary" className="grid place-items-center">
<div className="w-[clamp(0px,100%,42rem)] grid place-items-center gap-8">
{item.subitem_of.data.length > 0 ? (
<div className="grid place-items-center">
<p>{langui.global_subitem_of}</p>
<Button
href={`/library/${item.subitem_of.data[0].attributes.slug}`}
>
{prettyinlineTitle(
"",
item.subitem_of.data[0].attributes.title,
item.subitem_of.data[0].attributes.subtitle
)}
</Button>
</div>
) : (
""
)}
<div className="grid place-items-center">
<h1 className="text-3xl">{item.title}</h1>
{item.subtitle ? (
<h2 className="text-2xl">{item.subtitle}</h2>
) : (
""
)}
</div>
{item.descriptions.length > 0 ? (
<p className="text-justify">{item.descriptions[0].description}</p>
) : (
""
)}
</div>
</InsetBox>
{item.gallery.data.length > 0 ? (
<div id="gallery" className="grid place-items-center gap-8 w-full">
<h2 className="text-2xl">{langui.library_item_gallery}</h2>
<div className="grid w-full gap-8 items-end grid-cols-[repeat(auto-fill,_minmax(15rem,1fr))]">
{item.gallery.data.map((galleryItem) => (
<div
key={galleryItem.id}
className="relative aspect-square hover:scale-[1.02] transition-transform cursor-pointer"
>
<div className="bg-light absolute inset-0 rounded-lg drop-shadow-shade-md"></div>
<Img
className="rounded-lg"
image={galleryItem.attributes}
layout="fill"
objectFit="cover"
/>
</div>
))}
</div>
</div>
) : (
""
)}
<InsetBox id="details" className="grid place-items-center">
<div className="w-[clamp(0px,100%,42rem)] grid place-items gap-8">
<h2 className="text-2xl text-center">
{langui.library_item_details}
</h2>
<div className="grid grid-flow-col w-full place-content-between">
{item.metadata.length > 0 ? (
<div className="grid place-items-center">
<h3 className="text-xl">{langui.global_type}</h3>
<div className="grid grid-flow-col gap-1">
<Chip>{prettyItemType(item.metadata[0], langui)}</Chip>
{""}
<Chip>{prettyItemSubType(item.metadata[0])}</Chip>
</div>
</div>
) : (
""
)}
{item.release_date ? (
<div className="grid place-items-center">
<h3 className="text-xl">{langui.global_release_date}</h3>
<p>{prettyDate(item.release_date)}</p>
</div>
) : (
""
)}
{item.price ? (
<div className="grid place-items-center">
<h3 className="text-xl">{langui.global_price}</h3>
<p>{prettyPrice(item.price)}</p>
</div>
) : (
""
)}
</div>
{item.size ? (
<>
<h3 className="text-xl">{langui.library_item_physical_size}</h3>
<div className="grid grid-flow-col w-full place-content-between">
<div className="flex flex-row flex-wrap place-items-start gap-4">
<p className="font-bold">{langui.global_width}:</p>
<div>
<p>{item.size.width} mm</p>
<p>{convertMmToInch(item.size.width)} in</p>
</div>
</div>
<div className="flex flex-row flex-wrap place-items-start gap-4">
<p className="font-bold">{langui.global_height}:</p>
<div>
<p>{item.size.height} mm</p>
<p>{convertMmToInch(item.size.height)} in</p>
</div>
</div>
{item.size.thickness ? (
<div className="flex flex-row flex-wrap place-items-start gap-4">
<p className="font-bold">{langui.global_thickness}:</p>
<div>
<p>{item.size.thickness} mm</p>
<p>{convertMmToInch(item.size.thickness)} in</p>
</div>
</div>
) : (
""
)}
</div>
</>
) : (
""
)}
{item.metadata.length > 0 ? (
<>
<h3 className="text-xl">
{langui.library_item_type_information}
</h3>
<div className="grid grid-cols-2 w-full place-content-between">
{item.metadata[0].__typename === "ComponentMetadataBooks" ? (
<>
<div className="flex flex-row place-content-start gap-4">
<p className="font-bold">{langui.global_pages}:</p>
<p>{item.metadata[0].page_count}</p>
</div>
<div className="flex flex-row place-content-start gap-4">
<p className="font-bold">{langui.global_binding}:</p>
<p>
{item.metadata[0].binding_type ===
Enum_Componentmetadatabooks_Binding_Type.Paperback
? langui.global_paperback
: item.metadata[0].binding_type ===
Enum_Componentmetadatabooks_Binding_Type.Hardcover
? langui.global_hardcover
: ""}
</p>
</div>
<div className="flex flex-row place-content-start gap-4">
<p className="font-bold">{langui.global_page_order}:</p>
<p>
{item.metadata[0].page_order ===
Enum_Componentmetadatabooks_Page_Order.LeftToRight
? langui.global_left_to_right
: item.metadata[0].page_order ===
Enum_Componentmetadatabooks_Page_Order.RightToLeft
? langui.global_right_to_left
: ""}
</p>
</div>
<div className="flex flex-row place-content-start gap-4">
<p className="font-bold">{langui.global_languages}:</p>
{item.metadata[0].languages.data.map((lang) => (
<p key={lang.attributes.code}>
{lang.attributes.name}
</p>
))}
</div>
</>
) : item.metadata[0].__typename ===
"ComponentMetadataAudio" ? (
<></>
) : item.metadata[0].__typename ===
"ComponentMetadataVideo" ? (
<></>
) : item.metadata[0].__typename ===
"ComponentMetadataGame" ? (
<></>
) : item.metadata[0].__typename ===
"ComponentMetadataOther" ? (
<>
<div className="flex flex-row place-content-start gap-4">
<p className="font-bold">{langui.global_type}:</p>
<Chip>
{item.metadata[0].subtype.data.attributes.titles
.length > 0
? item.metadata[0].subtype.data.attributes.titles[0]
.title
: prettySlug(
item.metadata[0].subtype.data.attributes.slug
)}
</Chip>
</div>
</>
) : (
""
)}
</div>
</>
) : (
""
)}
</div>
</InsetBox>
{item.subitems.data.length > 0 ? (
<div
id={isVariantSet ? "variants" : "subitems"}
className="grid place-items-center gap-8 w-full"
>
<h2 className="text-2xl">
{isVariantSet
? langui.library_item_variants
: langui.library_item_subitems}
</h2>
<div className="grid gap-8 items-end mobile:grid-cols-2 grid-cols-[repeat(auto-fill,minmax(15rem,1fr))] w-full">
{item.subitems.data.map((subitem) => (
<LibraryItemsPreview
key={subitem.id}
item={subitem.attributes}
/>
))}
</div>
</div>
) : (
""
)}
{item.contents.data.length > 0 ? (
<div id="content" className="w-full grid place-items-center gap-8">
<h2 className="text-2xl">{langui.library_item_content}</h2>
<div className="grid gap-4 w-full">
{item.contents.data.map((content) => (
<div
id={content.attributes.slug}
key={content.id}
className="grid gap-2 px-4 rounded-lg target:bg-mid target:shadow-inner-sm target:shadow-shade target:h-auto target:py-3 target:my-2 target:[--displaySubContentMenu:grid] [--displaySubContentMenu:none]"
>
<div className="grid gap-4 place-items-center grid-cols-[auto_auto_1fr_auto_12ch] thin:grid-cols-[auto_auto_1fr_auto]">
<a href={`#${content.attributes.slug}`}>
<h3>
{content.attributes.content.data &&
content.attributes.content.data.attributes.titles
.length > 0
? prettyinlineTitle(
content.attributes.content.data.attributes
.titles[0].pre_title,
content.attributes.content.data.attributes
.titles[0].title,
content.attributes.content.data.attributes
.titles[0].subtitle
)
: prettySlug(content.attributes.slug, item.slug)}
</h3>
</a>
<div className="flex flex-row flex-wrap gap-1">
{content.attributes.content.data?.attributes.categories.data.map(
(category) => (
<Chip key={category.id}>
{category.attributes.short}
</Chip>
)
)}
</div>
<p className="border-b-2 h-4 w-full border-black border-dotted opacity-30"></p>
<p>
{content.attributes.range[0].__typename ===
"ComponentRangePageRange"
? content.attributes.range[0].starting_page
: ""}
</p>
{content.attributes.content.data ? (
<Chip className="justify-self-end thin:hidden">
{content.attributes.content.data.attributes.type.data
.attributes.titles.length > 0
? content.attributes.content.data.attributes.type.data
.attributes.titles[0].title
: prettySlug(
content.attributes.content.data.attributes.type
.data.attributes.slug
)}
</Chip>
) : (
""
)}
</div>
<div className="grid-flow-col place-content-start place-items-center gap-2 [display:var(--displaySubContentMenu)]">
<span className="material-icons text-dark">
subdirectory_arrow_right
</span>
{content.attributes.scan_set.length > 0 ? (
<Button
href={`/contents/${content.attributes.content.data.attributes.slug}/scans/`}
>
{langui.library_item_view_scans}
</Button>
) : (
""
)}
{content.attributes.content.data ? (
<Button
href={`/contents/${content.attributes.content.data.attributes.slug}`}
>
{langui.library_item_open_content}
</Button>
) : (
""
)}
{content.attributes.scan_set.length === 0 &&
!content.attributes.content.data
? "The content is not available"
: ""}
</div>
</div>
))}
</div>
</div>
) : (
""
)}
</div>
</ContentPanel>
);
return (
<AppLayout
navTitle={langui.main_library}
title={prettyinlineTitle("", item.title, item.subtitle)}
langui={langui}
contentPanel={contentPanel}
subPanel={subPanel}
thumbnail={item.thumbnail.data?.attributes}
description={
item.descriptions.length > 0
? item.descriptions[0].description
: undefined
}
/>
);
}
export const getStaticProps: GetStaticProps = async (context) => {
if (context.params) {
if (context.params.slug && context.locale) {
if (context.params.slug instanceof Array)
context.params.slug = context.params.slug.join("");
const props: LibrarySlugProps = {
libraryItem: await getLibraryItem({
slug: context.params.slug,
language_code: context.locale,
}),
langui: await getWebsiteInterface({
language_code: context.locale,
}),
};
return {
props: props,
};
}
}
return { props: {} };
};
export const getStaticPaths: GetStaticPaths = async (context) => {
type Path = {
params: {
slug: string;
};
locale: string;
};
const data = await getLibraryItemsSlugs({});
const paths: Path[] = [];
data.libraryItems.data.map((item) => {
context.locales?.map((local) => {
paths.push({ params: { slug: item.attributes.slug }, locale: local });
});
});
return {
paths,
fallback: false,
};
};
function useTesting(props: LibrarySlugProps) {
const libraryItem = props.libraryItem.libraryItems.data[0].attributes;
const router = useRouter();
const libraryItemURL =
"/admin/content-manager/collectionType/api::library-item.library-item/" +
props.libraryItem.libraryItems.data[0].id;
sortContent(libraryItem.contents);
if (router.locale === "en") {
if (!libraryItem.thumbnail.data) {
prettyTestError(
router,
"Missing thumbnail",
["libraryItem"],
libraryItemURL
);
}
if (libraryItem.metadata.length === 0) {
prettyTestError(
router,
"Missing metadata",
["libraryItem"],
libraryItemURL
);
} else {
if (
libraryItem.metadata[0].__typename === "ComponentMetadataOther" &&
(libraryItem.metadata[0].subtype.data.attributes.slug ===
"relation-set" ||
libraryItem.metadata[0].subtype.data.attributes.slug ===
"variant-set")
) {
// This is a group type item
if (libraryItem.price) {
prettyTestError(
router,
"Group-type items shouldn't have price",
["libraryItem"],
libraryItemURL
);
}
if (libraryItem.size) {
prettyTestError(
router,
"Group-type items shouldn't have size",
["libraryItem"],
libraryItemURL
);
}
if (libraryItem.release_date) {
prettyTestError(
router,
"Group-type items shouldn't have release_date",
["libraryItem"],
libraryItemURL
);
}
if (libraryItem.contents.data.length > 0) {
prettyTestError(
router,
"Group-type items shouldn't have contents",
["libraryItem"],
libraryItemURL
);
}
if (libraryItem.subitems.data.length === 0) {
prettyTestError(
router,
"Group-type items should have subitems",
["libraryItem"],
libraryItemURL
);
}
} else {
// This is a normal item
if (libraryItem.metadata[0].__typename === "ComponentMetadataOther") {
if (
libraryItem.metadata[0].subtype.data.attributes.slug ===
"audio-case"
) {
let hasAudioSubItem = false;
libraryItem.subitems.data.map((subitem) => {
if (
subitem.attributes.metadata.length > 0 &&
subitem.attributes.metadata[0].__typename ===
"ComponentMetadataAudio"
)
hasAudioSubItem = true;
});
if (!hasAudioSubItem) {
prettyTestError(
router,
"Audio-case item doesn't have an audio-typed subitem",
["libraryItem"],
libraryItemURL
);
}
} else if (
libraryItem.metadata[0].subtype.data.attributes.slug === "game-case"
) {
let hasGameSubItem = false;
libraryItem.subitems.data.map((subitem) => {
if (
subitem.attributes.metadata.length > 0 &&
subitem.attributes.metadata[0].__typename ===
"ComponentMetadataGame"
)
hasGameSubItem = true;
});
if (!hasGameSubItem) {
prettyTestError(
router,
"Game-case item doesn't have an Game-typed subitem",
["libraryItem"],
libraryItemURL
);
}
} else if (
libraryItem.metadata[0].subtype.data.attributes.slug ===
"video-case"
) {
let hasVideoSubItem = false;
libraryItem.subitems.data.map((subitem) => {
if (
subitem.attributes.metadata.length > 0 &&
subitem.attributes.metadata[0].__typename ===
"ComponentMetadataVideo"
)
hasVideoSubItem = true;
});
if (!hasVideoSubItem) {
prettyTestError(
router,
"Video-case item doesn't have an Video-typed subitem",
["libraryItem"],
libraryItemURL
);
}
} else if (
libraryItem.metadata[0].subtype.data.attributes.slug === "item-set"
) {
if (libraryItem.subitems.data.length === 0) {
prettyTestError(
router,
"Item-set item should have subitems",
["libraryItem"],
libraryItemURL
);
}
}
}
if (!libraryItem.price) {
prettyTestWarning(
router,
"Missing price",
["libraryItem"],
libraryItemURL
);
} else {
if (!libraryItem.price.amount) {
prettyTestError(
router,
"Missing amount",
["libraryItem", "price"],
libraryItemURL
);
}
if (!libraryItem.price.currency) {
prettyTestError(
router,
"Missing currency",
["libraryItem", "price"],
libraryItemURL
);
}
}
if (!libraryItem.digital) {
if (!libraryItem.size) {
prettyTestWarning(
router,
"Missing size",
["libraryItem"],
libraryItemURL
);
} else {
if (!libraryItem.size.width) {
prettyTestWarning(
router,
"Missing width",
["libraryItem", "size"],
libraryItemURL
);
}
if (!libraryItem.size.height) {
prettyTestWarning(
router,
"Missing height",
["libraryItem", "size"],
libraryItemURL
);
}
if (!libraryItem.size.thickness) {
prettyTestWarning(
router,
"Missing thickness",
["libraryItem", "size"],
libraryItemURL
);
}
}
}
if (!libraryItem.release_date) {
prettyTestWarning(
router,
"Missing release_date",
["libraryItem"],
libraryItemURL
);
} else {
if (!libraryItem.release_date.year) {
prettyTestError(
router,
"Missing year",
["libraryItem", "release_date"],
libraryItemURL
);
}
if (!libraryItem.release_date.month) {
prettyTestError(
router,
"Missing month",
["libraryItem", "release_date"],
libraryItemURL
);
}
if (!libraryItem.release_date.day) {
prettyTestError(
router,
"Missing day",
["libraryItem", "release_date"],
libraryItemURL
);
}
}
if (libraryItem.contents.data.length === 0) {
prettyTestWarning(
router,
"Missing contents",
["libraryItem"],
libraryItemURL
);
} else {
let currentRangePage = 0;
libraryItem.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 (libraryItem.metadata[0].__typename === "ComponentMetadataBooks") {
if (currentRangePage < libraryItem.metadata[0].page_count) {
prettyTestError(
router,
`Missing pages ${currentRangePage + 1} to ${
libraryItem.metadata[0].page_count
}`,
["libraryItem", "content"],
libraryItemURL
);
} else if (currentRangePage > libraryItem.metadata[0].page_count) {
prettyTestError(
router,
`Page overflow, content references pages up to ${currentRangePage} when the highest expected was ${libraryItem.metadata[0].page_count}`,
["libraryItem", "content"],
libraryItemURL
);
}
if (libraryItem.metadata[0].languages.data.length === 0) {
prettyTestWarning(
router,
"Missing language",
["libraryItem", "metadata"],
libraryItemURL
);
}
if (!libraryItem.metadata[0].page_count) {
prettyTestWarning(
router,
"Missing page_count",
["libraryItem", "metadata"],
libraryItemURL
);
}
}
}
}
}
if (!libraryItem.root_item && libraryItem.subitem_of.data.length === 0) {
prettyTestError(
router,
"This item is inaccessible (not root item and not subitem of another item)",
["libraryItem"],
libraryItemURL
);
}
if (libraryItem.gallery.data.length === 0) {
prettyTestWarning(
router,
"Missing gallery",
["libraryItem"],
libraryItemURL
);
}
}
if (libraryItem.descriptions.length === 0) {
prettyTestWarning(
router,
"Missing description",
["libraryItem"],
libraryItemURL
);
}
}