Simplified the GraphQL Query system

This commit is contained in:
DrMint 2022-01-02 05:39:54 +01:00
parent 702dc74ab6
commit 8e077d2c6f
12 changed files with 588 additions and 4305 deletions

View File

@ -1,8 +1,8 @@
import { ChronologyItemEntity, Maybe } from "graphql/operations-types"; import { GetChronologyItemsQuery } from "graphql/operations-types";
import styles from "styles/Chronology/ChronologyItemComponent.module.css"; import styles from "styles/Chronology/ChronologyItemComponent.module.css";
export type ChronologyItemComponentProps = { export type ChronologyItemComponentProps = {
item: ChronologyItemEntity; item: GetChronologyItemsQuery['chronologyItems']['data'][number];
displayYear: boolean; displayYear: boolean;
}; };
@ -11,8 +11,9 @@ export default function ChronologyItemComponent(
): JSX.Element { ): JSX.Element {
function generateAnchor( function generateAnchor(
year: number, year: number,
month: Maybe<number> | undefined, month: number,
day: Maybe<number> | undefined day: number,
event?: number
): string { ): string {
let result: string = ""; let result: string = "";
result += year; result += year;
@ -21,10 +22,7 @@ export default function ChronologyItemComponent(
return result; return result;
} }
function generateYear( function generateYear(displayed_date: string, year: number): string {
displayed_date: Maybe<string> | undefined,
year: number
): string {
if (displayed_date) { if (displayed_date) {
return displayed_date; return displayed_date;
} else { } else {
@ -32,10 +30,7 @@ export default function ChronologyItemComponent(
} }
} }
function generateDate( function generateDate(month: number, day: number): string {
month: Maybe<number> | undefined,
day: Maybe<number> | undefined
): string {
let lut = [ let lut = [
"Jan", "Jan",
"Feb", "Feb",
@ -62,84 +57,64 @@ export default function ChronologyItemComponent(
return result; return result;
} }
if (props.item && props.item.attributes) { return (
return ( <div
<div className={styles.chronologyItem}
className={styles.chronologyItem} id={generateAnchor(
id={generateAnchor( props.item.attributes.year,
props.item.attributes.year, props.item.attributes.month,
props.item.attributes.month, props.item.attributes.day
props.item.attributes.day )}
)} >
> {props.displayYear ? (
{props.displayYear ? ( <p className={styles.year}>
<p className={styles.year}> {generateYear(
{generateYear( props.item.attributes.displayed_date,
props.item.attributes.displayed_date, props.item.attributes.year
props.item.attributes.year )}
)}
</p>
) : (
""
)}
<p className={styles.date}>
{generateDate(props.item.attributes.month, props.item.attributes.day)}
</p> </p>
) : (
""
)}
<div className={styles.events}> <p className={styles.date}>
{props.item.attributes.events?.map((event) => { {generateDate(props.item.attributes.month, props.item.attributes.day)}
if (event) { </p>
return (
<div className={styles.event} key={event.id}>
{event.translations?.map((translation) => {
if (translation)
return (
<>
{translation.title ? (
<h3>{translation.title}</h3>
) : (
""
)}
{translation.description ? ( <div className={styles.events}>
<p {props.item.attributes.events.map((event) => (
className={ <div className={styles.event} key={event.id}>
event.translations && {event.translations.map((translation) => (
event.translations.length > 1 <>
? styles.bulletItem {translation.title ? <h3>{translation.title}</h3> : ""}
: ""
}
>
{translation.description}
</p>
) : (
""
)}
{translation.note ? (
<em>{"Notes: " + translation.note}</em>
) : (
""
)}
</>
);
})}
<p className={styles.source}> {translation.description ? (
{event.source && <p
event.source.data && className={
event.source.data.attributes event.translations.length > 1 ? styles.bulletItem : ""
? "(" + event.source.data.attributes.name + ")" }
: "(WARNING: NO SOURCE!)"} >
{translation.description}
</p> </p>
</div> ) : (
); ""
} )}
})} {translation.note ? (
</div> <em>{"Notes: " + translation.note}</em>
) : (
""
)}
</>
))}
<p className={styles.source}>
{event.source.data
? "(" + event.source.data.attributes.name + ")"
: "(WARNING: NO SOURCE!)"}
</p>
</div>
))}
</div> </div>
); </div>
} else { );
return <></>;
}
} }

View File

@ -1,10 +1,10 @@
import styles from "styles/Chronology/ChronologyYearComponent.module.css"; import styles from "styles/Chronology/ChronologyYearComponent.module.css";
import ChronologyItemComponent from "components/Chronology/ChronologyItemComponent"; import ChronologyItemComponent from "components/Chronology/ChronologyItemComponent";
import { ChronologyItemEntity } from "graphql/operations-types"; import { GetChronologyItemsQuery } from "graphql/operations-types";
type ChronologyYearComponentProps = { type ChronologyYearComponentProps = {
year: number; year: number;
items: ChronologyItemEntity[]; items: GetChronologyItemsQuery["chronologyItems"]["data"][number][];
}; };
export default function ChronologyYearComponent( export default function ChronologyYearComponent(
@ -15,7 +15,7 @@ export default function ChronologyYearComponent(
className={styles.chronologyYear} className={styles.chronologyYear}
id={props.items.length > 1 ? props.year.toString() : undefined} id={props.items.length > 1 ? props.year.toString() : undefined}
> >
{props.items.map((item: ChronologyItemEntity, index: number) => ( {props.items.map((item, index) => (
<ChronologyItemComponent <ChronologyItemComponent
key={index} key={index}
item={item} item={item}

View File

@ -1,11 +1,11 @@
import styles from "styles/Library/LibraryItemComponent.module.css"; import styles from "styles/Library/LibraryItemComponent.module.css";
import Link from "next/link"; import Link from "next/link";
import Image from "next/image"; import Image from "next/image";
import { BasicDate, getAssetURL } from "queries/helpers"; import { GetLibraryItemsPreviewQuery } from "graphql/operations-types";
import { LibraryItemEntity } from "graphql/operations-types"; import { getAssetURL } from "queries/helpers";
export type LibraryItemComponentProps = { export type LibraryItemComponentProps = {
item: LibraryItemEntity; item: GetLibraryItemsPreviewQuery["libraryItems"]["data"][number];
}; };
export default function LibraryItemComponent( export default function LibraryItemComponent(
@ -17,7 +17,9 @@ export default function LibraryItemComponent(
return result; return result;
} }
function prettyDate(date: BasicDate): string { function prettyDate(
date: GetLibraryItemsPreviewQuery["libraryItems"]["data"][number]["attributes"]["release_date"]
): string {
return ( return (
date.year + date.year +
"/" + "/" +

View File

@ -101,3 +101,105 @@ query getLibraryItemsPreview($language_code: String) {
} }
} }
} }
query getLibraryItemsSkeleton {
libraryItems(filters: { root_item: { eq: true } }) {
data {
attributes {
slug
subitems {
data {
attributes {
slug
subitems {
data {
attributes {
slug
subitems {
data {
attributes {
slug
}
}
}
}
}
}
}
}
}
}
}
}
}
query getLibraryItem($slug: String, $language_code: String) {
libraryItems(filters: { slug: { eq: $slug } }) {
data {
id
attributes {
title
subtitle
slug
thumbnail {
data {
attributes {
name
alternativeText
caption
width
height
url
}
}
}
release_date {
year
month
day
}
price {
amount
currency {
data {
attributes {
symbol
code
}
}
}
}
size {
width
height
thickness
}
descriptions(filters: { language: { code: { eq: $language_code } } }) {
description
}
subitems {
data {
id
attributes {
slug
title
subtitle
thumbnail {
data {
attributes {
name
alternativeText
caption
width
height
url
}
}
}
}
}
}
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -3,12 +3,16 @@ import {
GetChronologyItemsQueryVariables, GetChronologyItemsQueryVariables,
GetErasQuery, GetErasQuery,
GetErasQueryVariables, GetErasQueryVariables,
GetLibraryItemQuery,
GetLibraryItemQueryVariables,
GetLibraryItemsPreviewQuery, GetLibraryItemsPreviewQuery,
GetLibraryItemsPreviewQueryVariables, GetLibraryItemsPreviewQueryVariables,
GetLibraryItemsSkeletonQuery,
GetLibraryItemsSkeletonQueryVariables,
} from "graphql/operations-types"; } from "graphql/operations-types";
const graphQL = async (query: string, variables?: string) => { const graphQL = async (query: string, variables?: string) => {
const res = await fetch(process.env.URL_GRAPHQL, { const res = await fetch(`${process.env.URL_GRAPHQL}`, {
method: "POST", method: "POST",
body: JSON.stringify({ body: JSON.stringify({
query: query, query: query,
@ -33,7 +37,8 @@ function getQueryFromOperations(queryName: string): string {
const lines = operations.split("\n"); const lines = operations.split("\n");
lines.map((line, index) => { lines.map((line, index) => {
if (startingIndex === -1) { if (startingIndex === -1) {
if (line.startsWith(`query ${queryName}`)) startingIndex = index; if (line.startsWith(`query ${queryName}(`)) startingIndex = index;
if (line.startsWith(`query ${queryName} {`)) startingIndex = index;
} else if (endingIndex === -1) { } else if (endingIndex === -1) {
if (line.startsWith("query")) endingIndex = index; if (line.startsWith("query")) endingIndex = index;
} }
@ -61,3 +66,18 @@ export async function getLibraryItemsPreview(
const query = getQueryFromOperations("getLibraryItemsPreview"); const query = getQueryFromOperations("getLibraryItemsPreview");
return await graphQL(query, JSON.stringify(variables)); return await graphQL(query, JSON.stringify(variables));
} }
export async function getLibraryItemsSkeleton(
variables: GetLibraryItemsSkeletonQueryVariables
): Promise<GetLibraryItemsSkeletonQuery> {
const query = getQueryFromOperations("getLibraryItemsSkeleton");
return await graphQL(query, JSON.stringify(variables));
}
export async function getLibraryItem(
variables: GetLibraryItemQueryVariables
): Promise<GetLibraryItemQuery> {
const query = getQueryFromOperations("getLibraryItem");
return await graphQL(query, JSON.stringify(variables));
}

View File

@ -6,7 +6,6 @@ import NavOption from "components/Panels/NavOption";
import ChronologyYearComponent from "components/Chronology/ChronologyYearComponent"; import ChronologyYearComponent from "components/Chronology/ChronologyYearComponent";
import { applyCustomAppProps } from "pages/_app"; import { applyCustomAppProps } from "pages/_app";
import { import {
ChronologyItemEntity,
GetChronologyItemsQuery, GetChronologyItemsQuery,
GetErasQuery, GetErasQuery,
} from "graphql/operations-types"; } from "graphql/operations-types";
@ -25,16 +24,15 @@ applyCustomAppProps(ChronologyOverview, {
export default function ChronologyOverview(props: Props): JSX.Element { export default function ChronologyOverview(props: Props): JSX.Element {
// Group by year the Chronology items // Group by year the Chronology items
let chronologyItemYearGroups: ChronologyItemEntity[][] = []; let chronologyItemYearGroups: GetChronologyItemsQuery["chronologyItems"]["data"][number][][] =
[];
if (props.chronologyItems.chronologyItems) { if (props.chronologyItems.chronologyItems) {
props.chronologyItems.chronologyItems.data.map((item) => { props.chronologyItems.chronologyItems.data.map((item) => {
if (item && item.attributes) { if (!chronologyItemYearGroups.hasOwnProperty(item.attributes.year)) {
if (!chronologyItemYearGroups.hasOwnProperty(item.attributes.year)) { chronologyItemYearGroups[item.attributes.year] = [item];
chronologyItemYearGroups[item.attributes.year] = [item]; } else {
} else { chronologyItemYearGroups[item.attributes.year].push(item);
chronologyItemYearGroups[item.attributes.year].push(item);
}
} }
}); });
} }
@ -45,40 +43,26 @@ export default function ChronologyOverview(props: Props): JSX.Element {
<ReturnButton url="/chronology" title="Chronology" /> <ReturnButton url="/chronology" title="Chronology" />
<hr /> <hr />
{props.chronologyEras.chronologyEras {props.chronologyEras.chronologyEras.data.map((era) => (
? props.chronologyEras.chronologyEras.data.map((era) => ( <NavOption
<> key={era.id}
{era.attributes && era.attributes.title ? ( url={"#" + era.attributes.slug}
<NavOption title={era.attributes.title[0] ? era.attributes.title[0].title : ""}
key={era.id} subtitle={
url={"#" + era.attributes.slug} era.attributes.starting_year + " → " + era.attributes.ending_year
title={ }
era.attributes.title[0] border={true}
? era.attributes.title[0].title />
: "" ))}
}
subtitle={
era.attributes.starting_year +
" → " +
era.attributes.ending_year
}
border={true}
/>
) : (
""
)}
</>
))
: ""}
</SubPanel> </SubPanel>
<ContentPanel> <ContentPanel>
{chronologyItemYearGroups.map((items, index: number) => { {chronologyItemYearGroups.map((items, index: number) => {
if (items && items[0].attributes?.year) { if (items && items[0].attributes.year) {
return ( return (
<ChronologyYearComponent <ChronologyYearComponent
key={index} key={index}
year={items[0].attributes?.year} year={items[0].attributes.year}
items={items} items={items}
/> />
); );
@ -90,12 +74,16 @@ export default function ChronologyOverview(props: Props): JSX.Element {
} }
export const getStaticProps: GetStaticProps = async (context) => { export const getStaticProps: GetStaticProps = async (context) => {
return { if (context.locale)
props: { return {
chronologyItems: await getChronologyItems({ props: {
language_code: context.locale, chronologyItems: await getChronologyItems({
}), language_code: context.locale,
chronologyEras: await getEras({ language_code: context.locale }), }),
}, chronologyEras: await getEras({ language_code: context.locale }),
}; },
};
else {
return { props: {} };
}
}; };

View File

@ -1,20 +1,18 @@
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import ContentPanel from "components/Panels/ContentPanel"; import ContentPanel from "components/Panels/ContentPanel";
import { getAssetURL } from "queries/helpers";
import {
getLibraryItem,
getBreadcrumbs,
getLibraryItemsSkeleton,
LibraryItem,
LibrarySubItem,
} from "queries/library/[...slug]";
import Image from "next/image"; import Image from "next/image";
import Link from "next/link"; import Link from "next/link";
import { GetStaticProps } from "next"; import { GetStaticPaths, GetStaticProps } from "next";
import { applyCustomAppProps } from "pages/_app"; import { applyCustomAppProps } from "pages/_app";
import {
getLibraryItem,
getLibraryItemsSkeleton,
} from "graphql/operations";
import { GetLibraryItemQuery } from "graphql/operations-types";
import { getAssetURL } from "queries/helpers";
type Props = { type Props = {
libraryItem: LibraryItem; libraryItem: GetLibraryItemQuery;
}; };
applyCustomAppProps(Library, { applyCustomAppProps(Library, {
@ -24,67 +22,69 @@ applyCustomAppProps(Library, {
export default function Library(props: Props): JSX.Element { export default function Library(props: Props): JSX.Element {
const router = useRouter(); const router = useRouter();
const libraryItem = props.libraryItem.libraryItems.data[0];
return ( return (
<> <>
<ContentPanel> <ContentPanel>
<h1>{props.libraryItem.attributes.title}</h1> <h1>{libraryItem.attributes.title}</h1>
<h2>{props.libraryItem.attributes.subtitle}</h2> <h2>{libraryItem.attributes.subtitle}</h2>
<Image <Image
src={getAssetURL( src={getAssetURL(
props.libraryItem.attributes.thumbnail.data.attributes.url libraryItem.attributes.thumbnail.data.attributes.url
)} )}
alt={ alt={libraryItem.attributes.thumbnail.data.attributes.alternativeText}
props.libraryItem.attributes.thumbnail.data.attributes width={libraryItem.attributes.thumbnail.data.attributes.width}
.alternativeText height={libraryItem.attributes.thumbnail.data.attributes.height}
}
width={props.libraryItem.attributes.thumbnail.data.attributes.width}
height={props.libraryItem.attributes.thumbnail.data.attributes.height}
/> />
{props.libraryItem.attributes.subitems.data.map( {libraryItem.attributes.subitems.data.map((subitem) => (
(subitem: LibrarySubItem) => ( <Link
<Link href={router.asPath + "/" + subitem.attributes.slug}
href={router.asPath + "/" + subitem.attributes.slug} key={subitem.id}
key={subitem.id} passHref
passHref >
> <div>
<div> {subitem.attributes.thumbnail.data ? (
{subitem.attributes.thumbnail.data ? ( <Image
<Image src={getAssetURL(
src={getAssetURL( subitem.attributes.thumbnail.data.attributes.url
subitem.attributes.thumbnail.data.attributes.url )}
)} alt={
alt={ subitem.attributes.thumbnail.data.attributes.alternativeText
subitem.attributes.thumbnail.data.attributes }
.alternativeText width={subitem.attributes.thumbnail.data.attributes.width}
} height={subitem.attributes.thumbnail.data.attributes.height}
width={subitem.attributes.thumbnail.data.attributes.width} />
height={subitem.attributes.thumbnail.data.attributes.height} ) : (
/> ""
) : ( )}
"" </div>
)} </Link>
</div> ))}
</Link>
)
)}
</ContentPanel> </ContentPanel>
</> </>
); );
} }
export const getStaticProps: GetStaticProps = async (context) => { export const getStaticProps: GetStaticProps = async (context) => {
if (context.params && Array.isArray(context.params.slug) && context.locale) { if (context.params && Array.isArray(context.params.slug)) {
return { const slug = context.params.slug.pop();
props: { if (slug && context.locale) {
libraryItem: await getLibraryItem(context.params.slug, context.locale), return {
}, props: {
}; libraryItem: await getLibraryItem({
slug: slug,
language_code: context.locale,
}),
},
};
}
} }
return { props: {} }; return { props: {} };
}; };
export async function getStaticPaths() { export const getStaticPaths: GetStaticPaths = async () => {
const paths = await getAllSlugs(); const paths = await getAllSlugs();
return { return {
paths, paths,
@ -99,9 +99,10 @@ async function getAllSlugs() {
}; };
}; };
const data = await getLibraryItemsSkeleton(); const data = await getLibraryItemsSkeleton({});
console.log(data);
const paths: Path[] = []; const paths: Path[] = [];
data.map((item) => { data.libraryItems.data.map((item) => {
const breadcrumbs = getBreadcrumbs([], item); const breadcrumbs = getBreadcrumbs([], item);
breadcrumbs.map((breadcrumb) => { breadcrumbs.map((breadcrumb) => {
paths.push({ params: { slug: breadcrumb } }); paths.push({ params: { slug: breadcrumb } });
@ -109,3 +110,22 @@ async function getAllSlugs() {
}); });
return paths; return paths;
} }
export type LibraryItemSkeleton = {
attributes: {
slug: string;
subitems: {
data: LibraryItemSkeleton[];
};
};
};
function getBreadcrumbs(parentBreadcrumb: string[], data: LibraryItemSkeleton) {
const result: string[][] = [];
const itemBreadcrumb = [...parentBreadcrumb, data.attributes.slug];
result.push(itemBreadcrumb);
data.attributes.subitems.data.map((subitem) => {
result.push(...getBreadcrumbs(itemBreadcrumb, subitem));
});
return result;
}

View File

@ -16,7 +16,6 @@ applyCustomAppProps(Library, {
}); });
export default function Library(props: Props): JSX.Element { export default function Library(props: Props): JSX.Element {
console.log(props); console.log(props);
return ( return (
<> <>
@ -32,7 +31,7 @@ export default function Library(props: Props): JSX.Element {
</SubPanel> </SubPanel>
<ContentPanel> <ContentPanel>
{props.libraryItems.libraryItems?.data.map((item) => ( {props.libraryItems.libraryItems.data.map((item) => (
<LibraryItemComponent key={item.id} item={item} /> <LibraryItemComponent key={item.id} item={item} />
))} ))}
</ContentPanel> </ContentPanel>
@ -41,9 +40,15 @@ export default function Library(props: Props): JSX.Element {
} }
export const getStaticProps: GetStaticProps = async (context) => { export const getStaticProps: GetStaticProps = async (context) => {
return { if (context.locale)
props: { return {
libraryItems: await getLibraryItemsPreview({language_code: context.locale}), props: {
}, libraryItems: await getLibraryItemsPreview({
}; language_code: context.locale,
}),
},
};
else {
return { props: {} };
}
}; };

View File

@ -1,65 +1,3 @@
export const queryGraphQL = async (query: string, variables?: string) => {
const res = await fetch(process.env.URL_GRAPHQL, {
method: "POST",
body: JSON.stringify({
query: query,
variables: variables
}),
headers: {
"content-type": "application/json",
Authorization: "Bearer " + process.env.ACCESS_TOKEN,
},
});
return (await res.json()).data;
};
export function getAssetURL(url: string): string { export function getAssetURL(url: string): string {
return process.env.NEXT_PUBLIC_URL_CMS + url; return process.env.NEXT_PUBLIC_URL_CMS + url;
} }
export type Source = {
data: {
attributes: {
name: string;
};
};
};
export type UploadImage = {
data: {
attributes: {
name: string;
alternativeText: string;
caption: string;
width: number;
height: number;
url: string;
};
};
};
export type BasicPrice = {
amount: number;
currency: BasicCurrency;
};
export type BasicCurrency = {
data: {
attributes: {
symbol: string;
code: string;
};
};
};
export type BasicSize = {
width: number;
height: number;
thickness: number;
};
export type BasicDate = {
year: number;
month: number;
day: number;
};

View File

@ -1,183 +0,0 @@
import {
BasicDate,
BasicPrice,
BasicSize,
queryGraphQL,
UploadImage,
} from "queries/helpers";
export type LibraryItemSkeleton = {
attributes: {
slug: string;
subitems: {
data: LibraryItemSkeleton[];
};
};
};
export async function getLibraryItemsSkeleton(): Promise<
LibraryItemSkeleton[]
> {
return (
await queryGraphQL(
`
{
libraryItems(filters: { root_item: { eq: true } }) {
data {
attributes {
slug
subitems {
data {
attributes {
slug
subitems {
data {
attributes {
slug
subitems {
data {
attributes {
slug
}
}
}
}
}
}
}
}
}
}
}
}
}
`
)
).libraryItems.data;
}
export function getBreadcrumbs(
parentBreadcrumb: string[],
data: LibraryItemSkeleton
) {
const result: string[][] = [];
const itemBreadcrumb = [...parentBreadcrumb, data.attributes.slug];
result.push(itemBreadcrumb);
data.attributes.subitems.data.map((subitem) => {
result.push(...getBreadcrumbs(itemBreadcrumb, subitem));
});
return result;
}
export type LibraryItem = {
id: string;
attributes: {
title: string;
subtitle: string;
slug: string;
thumbnail: UploadImage;
release_date: BasicDate;
price: BasicPrice;
size: BasicSize;
description: {
description: string;
};
subitems: {
data: LibrarySubItem[];
};
};
};
export type LibrarySubItem = {
id: string;
attributes: {
title: string;
subtitle: string;
slug: string;
thumbnail: UploadImage;
};
};
export async function getLibraryItem(
slug: string[],
language_code: string | undefined
): Promise<LibraryItem> {
return (
await queryGraphQL(
`
{
libraryItems(
filters: {slug: {eq: "${slug.pop()}"}}
) {
data {
id
attributes {
title
subtitle
slug
thumbnail {
data {
attributes {
name
alternativeText
caption
width
height
url
}
}
}
release_date {
year
month
day
}
price {
amount
currency {
data {
attributes {
symbol
code
}
}
}
}
size {
width
height
thickness
}
descriptions(filters: { language: { code: { eq: "${language_code}" } } }) {
description
}
subitems {
data {
id
attributes {
slug
title
subtitle
thumbnail {
data {
attributes {
name
alternativeText
caption
width
height
url
}
}
}
}
}
}
}
}
}
}
`
)
).libraryItems.data[0];
}

View File

@ -1,86 +0,0 @@
import {
UploadImage,
queryGraphQL,
BasicPrice,
BasicDate,
BasicSize,
} from "queries/helpers";
export type LibraryItem = {
id: string;
attributes: {
title: string;
subtitle: string;
slug: string;
thumbnail: UploadImage;
release_date: BasicDate;
price: BasicPrice;
size: BasicSize;
description: {
description: string;
};
};
};
export async function getLibraryItems(
language_code: string | undefined
): Promise<LibraryItem[]> {
return (
await queryGraphQL(
`
{
libraryItems(
filters: { root_item: { eq: true } }
pagination: { limit: -1 }
sort: ["slug:asc"]
) {
data {
id
attributes {
title
subtitle
slug
thumbnail {
data {
attributes {
name
alternativeText
caption
width
height
url
}
}
}
release_date {
year
month
day
}
price {
amount
currency {
data {
attributes {
symbol
code
}
}
}
}
size {
width
height
thickness
}
descriptions(filters: { language: { code: { eq: "${language_code}" } } }) {
description
}
}
}
}
}
`
)
).libraryItems.data;
}