From 8684640ef4a0311d47110b585746b895c586f323 Mon Sep 17 00:00:00 2001 From: DrMint Date: Fri, 4 Mar 2022 23:21:42 +0100 Subject: [PATCH] Added support for filtering Library items --- src/components/Select.tsx | 87 +++++++++++++++ src/graphql/operation.graphql | 1 - src/pages/library/index.tsx | 193 +++++++++++++++++++++++++++++++++- 3 files changed, 275 insertions(+), 6 deletions(-) create mode 100644 src/components/Select.tsx diff --git a/src/components/Select.tsx b/src/components/Select.tsx new file mode 100644 index 0000000..5b256dc --- /dev/null +++ b/src/components/Select.tsx @@ -0,0 +1,87 @@ +import { useEffect, useState } from "react"; + +export type SelectProps = { + options: SelectOption[]; + selected?: number; + allowEmpty?: boolean; + className?: string; + onChange?: Function; +}; + +export type SelectOption = { + name: string; + label: string; +}; + +export function selectOptionsIncludes( + options: SelectOption[], + newOption: SelectOption +) { + options.map((option) => { + if (option.label === newOption.label) return true; + }); + return false; +} + +export default function Select(props: SelectProps): JSX.Element { + const [selected, setSelected] = useState( + props.selected ? props.selected : props.allowEmpty ? -1 : 0 + ); + const [opened, setOpened] = useState(false); + + return ( +
+
+

setOpened(!opened)} className="w-full"> + {selected === -1 ? "—" : props.options[selected].label} +

+ {selected >= 0 && props.allowEmpty && ( + { + setSelected(-1); + props.onChange && props.onChange(""); + }} + className="material-icons !text-xs" + > + close + + )} + setOpened(!opened)} className="material-icons"> + {opened ? "arrow_drop_up" : "arrow_drop_down"} + +
+
+ {props.options.map((option, index) => ( + <> + {index !== selected && ( +
{ + setOpened(false); + setSelected(index); + props.onChange && props.onChange(props.options[index].name); + }} + > + {option.label} +
+ )} + + ))} +
+
+ ); +} diff --git a/src/graphql/operation.graphql b/src/graphql/operation.graphql index 63d9963..a91da45 100644 --- a/src/graphql/operation.graphql +++ b/src/graphql/operation.graphql @@ -128,7 +128,6 @@ query getLibraryItemsPreview($language_code: String) { libraryItems( filters: { root_item: { eq: true } } pagination: { limit: -1 } - sort: ["title:asc", "subtitle:asc"] ) { data { id diff --git a/src/pages/library/index.tsx b/src/pages/library/index.tsx index 915a284..32cd19c 100644 --- a/src/pages/library/index.tsx +++ b/src/pages/library/index.tsx @@ -14,14 +14,43 @@ import { import PanelHeader from "components/PanelComponents/PanelHeader"; import AppLayout from "components/AppLayout"; import LibraryItemsPreview from "components/Library/LibraryItemsPreview"; +import Select from "components/Select"; +import { useEffect, useState } from "react"; +import { prettyDate, prettyinlineTitle } from "queries/helpers"; type LibraryProps = { libraryItems: GetLibraryItemsPreviewQuery; langui: GetWebsiteInterfaceQuery; }; +type GroupLibraryItems = Map< + string, + GetLibraryItemsPreviewQuery["libraryItems"]["data"] +>; + export default function Library(props: LibraryProps): JSX.Element { const langui = props.langui.websiteInterfaces.data[0].attributes; + + const [sortedItems, setSortedItem] = useState< + LibraryProps["libraryItems"]["libraryItems"]["data"] + >(sortBy("title", props.libraryItems.libraryItems.data)); + + const [sortingMethod, setSortingMethod] = useState("title"); + + const [groups, setGroups] = useState( + getGroups("", sortedItems) + ); + + const [groupingMethod, setGroupingMethod] = useState(""); + + useEffect(() => { + setSortedItem(sortBy(sortingMethod, props.libraryItems.libraryItems.data)); + }, [props.libraryItems.libraryItems.data, sortingMethod]); + + useEffect(() => { + setGroups(getGroups(groupingMethod, sortedItems)); + }, [groupingMethod, sortedItems]); + const subPanel = ( + +
+

Group by:

+ +
); const contentPanel = ( -
- {props.libraryItems.libraryItems.data.map((item) => ( - - ))} -
+ {[...groups].map(([name, items]) => ( + <> + {items.length > 0 && ( + <> +

{name}

+
+ {items.map((item) => ( + + ))} +
+ + )} + + ))}
); return ( @@ -67,3 +135,118 @@ export const getStaticProps: GetStaticProps = async (context) => { return { props: {} }; } }; + +function getGroups( + groupByType: string, + items: LibraryProps["libraryItems"]["libraryItems"]["data"] +): GroupLibraryItems { + switch (groupByType) { + case "category": + return new Map(); + + case "type": + const groupType: GroupLibraryItems = new Map(); + groupType.set("Audio", []); + groupType.set("Game", []); + groupType.set("Textual", []); + groupType.set("Video", []); + groupType.set("Other", []); + groupType.set("No type", []); + items.map((item) => { + if (item.attributes.metadata.length > 0) { + switch (item.attributes.metadata[0].__typename) { + case "ComponentMetadataAudio": + groupType.get("Audio")?.push(item); + break; + case "ComponentMetadataGame": + groupType.get("Game")?.push(item); + break; + case "ComponentMetadataBooks": + groupType.get("Textual")?.push(item); + break; + case "ComponentMetadataVideo": + groupType.get("Video")?.push(item); + break; + case "ComponentMetadataOther": + groupType.get("Other")?.push(item); + break; + } + } else { + groupType.get("No type")?.push(item); + } + }); + return groupType; + + case "releaseYear": + const years: number[] = []; + items.map((item) => { + if (item.attributes.release_date) { + if (!years.includes(item.attributes.release_date.year)) + years.push(item.attributes.release_date.year); + } + }); + const groupYear: GroupLibraryItems = new Map(); + years.sort(); + years.map((year) => { + groupYear.set(year.toString(), []); + }); + groupYear.set("No year", []); + items.map((item) => { + if (item.attributes.release_date) { + groupYear + .get(item.attributes.release_date.year.toString()) + ?.push(item); + } else { + groupYear.get("No year")?.push(item); + } + }); + + return groupYear; + + default: + const groupDefault: GroupLibraryItems = new Map(); + groupDefault.set("", items); + return groupDefault; + } +} + +function sortBy( + orderByType: string, + items: LibraryProps["libraryItems"]["libraryItems"]["data"] +): LibraryProps["libraryItems"]["libraryItems"]["data"] { + switch (orderByType) { + case "title": + return [...items].sort((a, b) => { + const titleA = prettyinlineTitle( + "", + a.attributes.title, + a.attributes.subtitle + ); + const titleB = prettyinlineTitle( + "", + b.attributes.title, + b.attributes.subtitle + ); + return titleA.localeCompare(titleB); + }); + case "price": + return [...items].sort((a, b) => { + const priceA = a.attributes.price ? a.attributes.price.amount : 99999; + const priceB = b.attributes.price ? b.attributes.price.amount : 99999; + return priceA - priceB; + }); + case "releaseDate": + return [...items].sort((a, b) => { + const dateA = + a.attributes.release_date !== null + ? prettyDate(a.attributes.release_date) + : "9999"; + const dateB = + b.attributes.release_date !== null + ? prettyDate(b.attributes.release_date) + : "9999"; + return dateA.localeCompare(dateB); + }); + } + return items; +}