diff --git a/next.config.js b/next.config.js index 0249033..b38fa01 100644 --- a/next.config.js +++ b/next.config.js @@ -2,7 +2,10 @@ module.exports = { reactStrictMode: true, i18n: { - locales: ['en', 'fr', 'jp', 'pt-br', 'pt-pt'], + locales: ['en', 'fr', 'ja', 'pt-br', 'pt-pt'], defaultLocale: 'en', - } + }, + images: { + domains: ['strapi.accords-library.com'], + }, } diff --git a/src/pages/chronology/overview.tsx b/src/pages/chronology/overview.tsx index 59e9c3f..6c22034 100644 --- a/src/pages/chronology/overview.tsx +++ b/src/pages/chronology/overview.tsx @@ -1,49 +1,49 @@ -import type { NextPage } from "next"; import { GetStaticProps } from "next"; import ContentPanel from "components/Panels/ContentPanel"; import SubPanel from "components/Panels/SubPanel"; import ReturnButton from "components/Panels/ReturnButton"; import NavOption from "components/Panels/NavOption"; -import { getChronologyEras, getChronologyItems } from "queries/queries"; +import { + getChronologyItems, + getChronologyEras, + ChronologyItem, + ChronologyEra, +} from "queries/chronology/overview"; -const ChronologyOverview: NextPage = (props) => { +type Props = { + chronologyItems: ChronologyItem[]; + chronologyEras: ChronologyEra[]; +}; +export default function ChronologyOverview(props: Props): JSX.Element { return ( <> -
- - {props.chronologyEras.map((era: any) => ( + + {console.log(props.chronologyEras)} + + {props.chronologyEras.map((era: ChronologyEra) => ( ))}
- - - {props.chronologyItems.map((item: any) => ( -
- {item.year} - {" "} - {item.translations[0].title} -
- ))} -
+ ); -}; -export default ChronologyOverview; +} export const getStaticProps: GetStaticProps = async (context) => { return { props: { - chronologyItems: await getChronologyItems(context.locale), + /*chronologyItems: await getChronologyItems(context.locale),*/ chronologyEras: await getChronologyEras(context.locale), }, }; diff --git a/src/pages/library.tsx b/src/pages/library.tsx deleted file mode 100644 index 1185ab9..0000000 --- a/src/pages/library.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import type { NextPage } from "next"; -import Head from "next/head"; - -const Home: NextPage = () => { - return ( - <> - - Library - - - - - ); -}; - -export default Home; diff --git a/src/pages/library/[...slug].tsx b/src/pages/library/[...slug].tsx new file mode 100644 index 0000000..78c369a --- /dev/null +++ b/src/pages/library/[...slug].tsx @@ -0,0 +1,88 @@ +import { useRouter } from "next/router"; +import SubPanel from "components/Panels/SubPanel"; +import ContentPanel from "components/Panels/ContentPanel"; +import { getAssetURL } from "queries/helpers"; +import { + getLibraryItem, + getRecursiveSlugs, + LibraryItem, + Subitem, +} from "queries/library/[...slug]"; +import Image from "next/image"; +import Link from "next/link"; + +type Props = { + libraryItem: LibraryItem; +}; + +export default function Library(props: Props): JSX.Element { + const router = useRouter(); + return ( + <> + +

{props.libraryItem.title}

+

{props.libraryItem.subtitle}

+ {props.libraryItem.thumbnail.title} + + {props.libraryItem.subitems.map((subitem: Subitem) => ( + +
+ {subitem.subitem_id.thumbnail ? ( + {subitem.subitem_id.thumbnail.title} + ) : ( + "" + )} +
+ + ))} +
+ + ); +} + +export async function getStaticProps({ params }) { + return { + props: { + libraryItem: await getLibraryItem(params.slug), + }, + }; +} + +export async function getStaticPaths() { + const paths = await getAllSlugs(); + + /* + paths.map((item) => { + console.log(item.params.slug); + }); + */ + + return { + paths, + fallback: false, + }; +} + +async function getAllSlugs() { + return (await getRecursiveSlugs()).map((item) => { + return { + params: { + slug: item, + }, + }; + }); +} diff --git a/src/pages/library/index.tsx b/src/pages/library/index.tsx new file mode 100644 index 0000000..f48c642 --- /dev/null +++ b/src/pages/library/index.tsx @@ -0,0 +1,72 @@ +import { GetStaticProps } from "next"; +import SubPanel from "components/Panels/SubPanel"; +import ContentPanel from "components/Panels/ContentPanel"; +import { LibraryItem, getLibraryItems } from "queries/library/index"; +import { getAssetURL } from "queries/helpers"; +import Image from "next/image"; +import Link from "next/link"; + +type Props = { + libraryItems: LibraryItem[]; +}; + +export default function Library(props: Props): JSX.Element { + return ( + <> + +

Library

+

+ A comprehensive list of all Yokoverse’s side materials (books, + novellas, artbooks, stage plays, manga, drama CDs, and comics). For + each, we provide photos and/or scans of the content, information about + what it is, when and how it was released, size, initial price… +

+
+
+ + + {props.libraryItems.map((item: LibraryItem) => ( + +
+

+ {item.subitem_of.length > 0 + ? prettyTitleSubtitle( + item.subitem_of[0].item_id.title, + item.subitem_of[0].item_id.subtitle + ) + " • " + : ""} + {prettyTitleSubtitle(item.title, item.subtitle)} +

+

{item.release_date}

+ + {item.thumbnail ? ( + {item.thumbnail.title} + ) : ( + "" + )} +
+ + ))} +
+ + ); +} + +export const getStaticProps: GetStaticProps = async (context) => { + return { + props: { + libraryItems: await getLibraryItems(), + }, + }; +}; + +function prettyTitleSubtitle(title: string, subtitle: string): string { + let result = title; + if (subtitle !== null) result += " - " + subtitle; + return result; +} diff --git a/src/queries/chronology/overview.ts b/src/queries/chronology/overview.ts new file mode 100644 index 0000000..f222860 --- /dev/null +++ b/src/queries/chronology/overview.ts @@ -0,0 +1,75 @@ +import { queryGraphQL } from "queries/helpers"; + +export type ChronologyItem = { + id: string; + year: number; + month: number; + day: number; + translations: ChronologyItemsTranslation[]; +}; + +export type ChronologyItemsTranslation = { + title: string; +}; + +export async function getChronologyItems( + languages_code: String | undefined +): Promise { + return ( + await queryGraphQL( + ` + { + chronology_items { + id + year + month + day + translations(filter: { languages_code: { code: { _eq: "` + languages_code + `" } } }) { + title + } + } + } + ` + ) + ).chronology_items; +} + +export type ChronologyEra = { + id: string; + attributes: ChronologyEraAttributes; +}; + +export type ChronologyEraAttributes = { + starting_year: number; + ending_year: number; + slug: string; + title: ChronologyEraTranslation[]; +} + +export type ChronologyEraTranslation = { + title: string; +}; + +export async function getChronologyEras( + language_code: String | undefined +): Promise { + return ( + await queryGraphQL( + ` + { + chronologyEras { + data { + attributes { + starting_year + ending_year + title (filters: {language: {code: {eq: "` + language_code + `"}}}){ + title + } + } + } + } + } + ` + ) + ).chronologyEras.data; +} diff --git a/src/queries/helpers.ts b/src/queries/helpers.ts new file mode 100644 index 0000000..e79ecbf --- /dev/null +++ b/src/queries/helpers.ts @@ -0,0 +1,49 @@ +/* +export const queryGraphQL = async (query: string) => { + const res = await fetch( + process.env.URL_GRAPHQL + + "?access_token=" + + process.env.ACCESS_TOKEN + + "&query=" + + query + ); + return (await res.json()).data; +}; +*/ + +export const queryGraphQL = async (query: String) => { + const res = await fetch(process.env.URL_GRAPHQL, { + method: "POST", + body: JSON.stringify({ + query: query, + }), + headers: { + "content-type": "application/json", + Authorization: "Bearer " + process.env.ACCESS_TOKEN, + }, + }); + return (await res.json()).data; +}; + + +export const queryGraphQLSystem = async (query: string) => { + const res = await fetch( + process.env.URL_GRAPHQL_SYSTEM + + "?access_token=" + + process.env.ACCESS_TOKEN + + "&query=" + + query + ); + return (await res.json()).data; +}; + +export type AssetImage = { + id: string; + title: string; + width: number; + height: number; +}; + +export function getAssetURL(id: string): string { + return "https://cms.accords-library.com/assets/" + id; +} \ No newline at end of file diff --git a/src/queries/library/[...slug].ts b/src/queries/library/[...slug].ts new file mode 100644 index 0000000..6502910 --- /dev/null +++ b/src/queries/library/[...slug].ts @@ -0,0 +1,170 @@ +import { queryGraphQL, AssetImage } from "queries/helpers"; + +export type LibraryItem = { + title: string; + subtitle: string; + slug: string; + thumbnail: AssetImage; + subitems: Subitem[]; +}; + +export type Subitem = { + subitem_id: LibrarySubitem; +}; + +export type LibrarySubitem = { + id: string; + title: string; + subtitle: string; + slug: string; + thumbnail: AssetImage; +}; + +export async function getLibraryItem(slug: string[]) { + return ( + await queryGraphQL( + ` + { + compendium_items(filter: {_and: [` + getFilterForItem(slug) + `]}) { + title + subtitle + slug + thumbnail { + id + title + width + height + } + subitems { + subitem_id { + id + title + subtitle + slug + thumbnail { + id + title + width + height + } + } + } + } + } + ` + ) + ).compendium_items[0]; +} + +export async function getRecursiveSlugs() { + const yetToExploreSlugs = level0Filtering( + ( + await queryGraphQL( + ` + { + compendium_items( + filter: { _and: [{ not_sold_separately: { _eq: false } }] } + ) { + subitem_of { + item_id { + virtual_set + } + } + slug + } + } + ` + ) + ).compendium_items + ); + + const result: string[][] = []; + while (yetToExploreSlugs.length > 0) { + const slug = yetToExploreSlugs.pop(); + if (slug !== undefined) { + const subitems = levelnFiltering((await queryGraphQL( + ` + { + compendium_items(filter: {_and: [` + getFilterForSubitemsOf(slug) + `]}) { + slug + } + } + ` + ) + ).compendium_items + ); + result.push(slug); + subitems.map((subitemSlug) => { + const newSlug = [...slug]; + newSlug.push(subitemSlug); + yetToExploreSlugs.push(newSlug); + }); + } + } + return result; +} + +export function getFilterForSubitemsOf(slug: string[]) { + let filter = ""; + slug.map((segment, index) => { + const depth = slug.length - index; + filter += "{ subitem_of: { item_id: ".repeat(depth); + filter += '{ slug: { _eq: "' + segment + '" } } '; + filter += "} } ".repeat(depth); + filter += ","; + }); + return filter; +} + +export function getFilterForItem(slug: string[]) { + let filter = ""; + slug.map((segment, index) => { + const depth = slug.length - index - 1; + filter += "{ subitem_of: { item_id: ".repeat(depth); + filter += '{ slug: { _eq: "' + segment + '" } } '; + filter += "} } ".repeat(depth); + filter += ","; + }); + return filter; +} + + +function level0Filtering(data: SlugLvl0[]): string[][] { + // Remove element if their parent item is a virtual_set + let result: string[][] = []; + data.map((item: SlugLvl0) => { + if (item.subitem_of.length > 0) { + if (item.subitem_of[0].item_id.virtual_set === false) { + result.push([item.slug]); + } + } else { + result.push([item.slug]); + } + }); + return result; +} + +function levelnFiltering(data: SlugLvln[]): string[] { + let result: string[] = []; + data.map((item: SlugLvln) => { + result.push(item.slug); + }); + return result; +} + +type SlugLvl0 = { + subitem_of: SlugLvl0Subitem_of[]; + slug: string; +}; + +type SlugLvl0Subitem_of = { + item_id: SlugLvl0Subitem; +}; + +type SlugLvl0Subitem = { + virtual_set: boolean; +}; + +type SlugLvln = { + slug: string; +}; diff --git a/src/queries/library/index.ts b/src/queries/library/index.ts new file mode 100644 index 0000000..2915040 --- /dev/null +++ b/src/queries/library/index.ts @@ -0,0 +1,72 @@ +import { queryGraphQL, AssetImage } from "queries/helpers"; + +export type LibraryItem = { + id: string; + subitem_of: Subitem_of[]; + title: string; + subtitle: string; + slug: string; + thumbnail: AssetImage; + release_date: string; + type: "Text" | "Video" | "Games" | "Soundtrack" | "Audiobooks" | "Other"; +}; + +export type Subitem_of = { + item_id: LibrarySubitem; +}; + +export type LibrarySubitem = { + title: string; + subtitle: string; + virtual_set: boolean; +}; + +export async function getLibraryItems() { + return filterGetLibraryItems((await queryGraphQL( + ` + { + compendium_items( + filter: { _and: [{ not_sold_separately: { _eq: false } }] } + sort: "title" + ) { + id + subitem_of { + item_id { + title + subtitle + virtual_set + } + } + title + subtitle + slug + thumbnail { + id + title + width + height + } + release_date + type + } + } + ` + ) + ).compendium_items); +} + +function filterGetLibraryItems(data: LibraryItem[]): LibraryItem[] { + + // Remove element if their parent item is a virtual_set + let result: LibraryItem[] = []; + data.map((item: LibraryItem) => { + if (item.subitem_of.length > 0) { + if (item.subitem_of[0].item_id.virtual_set === false) { + result.push(item); + } + } else { + result.push(item); + } + }); + return result; +} \ No newline at end of file diff --git a/src/queries/queries.ts b/src/queries/queries.ts deleted file mode 100644 index 04271df..0000000 --- a/src/queries/queries.ts +++ /dev/null @@ -1,49 +0,0 @@ -export async function getChronologyItems(languages_code: String | undefined) { - return ( - await queryCMS( - ` - { - chronology_items { - id - year - month - day - translations(filter: { languages_code: { code: { _eq: "` + languages_code + `" } } }) { - title - } - } - } - ` - ) - ).chronology_items; -} - -export async function getChronologyEras(languages_code: String | undefined) { - return ( - await queryCMS( - ` - { - chronology_eras(sort: "starting_year") { - id - starting_year - ending_year - translations(filter: { languages_code: { code: { _eq: "` + languages_code + `" } } }) { - title - } - } - } - ` - ) - ).chronology_eras; -} - -export const queryCMS = async (query: String) => { - const res = await fetch( - process.env.GRAPHQL + - "?access_token=" + - process.env.ACCESS_TOKEN + - "&query=" + - query - ); - return (await res.json()).data; -};