From c04674fa74c2e43535181431aef5d891f8839619 Mon Sep 17 00:00:00 2001 From: Kevin Hoerr Date: Sun, 14 Aug 2022 21:35:45 +0000 Subject: Merge planner code (#3) Reviewed-on: https://git.submelon.dev/kjhoerr/pantry/pulls/3 --- src/pages/_app.tsx | 16 ++++++ src/pages/index.tsx | 162 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 178 insertions(+) create mode 100644 src/pages/_app.tsx create mode 100644 src/pages/index.tsx (limited to 'src/pages') diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx new file mode 100644 index 0000000..10121ab --- /dev/null +++ b/src/pages/_app.tsx @@ -0,0 +1,16 @@ +import "semantic-ui-css/semantic.min.css"; +import "../styles/globals.css"; +import type { AppProps } from "next/app"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; + +const queryClient = new QueryClient(); + +function MyApp({ Component, pageProps }: AppProps) { + return ( + + + + ); +} + +export default MyApp; diff --git a/src/pages/index.tsx b/src/pages/index.tsx new file mode 100644 index 0000000..674878a --- /dev/null +++ b/src/pages/index.tsx @@ -0,0 +1,162 @@ +import type { NextPage } from "next"; +import Head from "next/head"; +import { List } from "immutable"; +import { + Header, + Table, + Message, + Container, + Pagination, +} from "semantic-ui-react"; + +import styles from "../styles/Home.module.css"; +import { useMemo, useState } from "react"; +import AddItem from "../components/add-item"; +import { + getGetItemsQueryKey, + useGetItems, + usePostItemsHook, +} from "../util/pantry-item-resource"; +import { PantryItem } from "../model"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; + +const ENTRIES_PER_PAGE = Number(process.env.ENTRIES_PER_PAGE ?? "10"); + +interface SortStateProps { + field: keyof PantryItem; + order: "ascending" | "descending"; +} + +const Home: NextPage = () => { + const { data, error } = useGetItems(); + const postItems = usePostItemsHook(); + const queryClient = useQueryClient(); + const { mutate } = useMutation(postItems, { + onSuccess: async (item) => { + queryClient.setQueryData( + getGetItemsQueryKey(), + (data ?? []).concat(item) + ); + }, + }); + const [activePage, setActivePage] = useState(1); + const [sortState, setSortState] = useState({ + field: "name", + order: "ascending", + }); + + const hasEntries = useMemo( + () => error === null && (data === undefined || data.length > 0), + [error, data] + ); + const entries = useMemo(() => { + const list = List(data); + // case insensitive sort + const sorted = list.sortBy((item) => + item[sortState.field]?.toString().toUpperCase() + ); + + return sortState.order === "ascending" ? sorted : sorted.reverse(); + }, [data, sortState]); + const handleSortChange = (field: keyof PantryItem) => { + setSortState((state) => + state.field === field + ? { + ...state, + order: state.order === "ascending" ? "descending" : "ascending", + } + : { field: field, order: "ascending" } + ); + }; + + return ( + + + Pantry + + + + +
+ Pantry +
+ + Promise.resolve(mutate(newItem))} /> + + + + + handleSortChange("name")} + > + Name + + handleSortChange("description")} + > + Description + + handleSortChange("quantity")} + > + Quantity + + + + + {entries + .valueSeq() + .slice( + (activePage - 1) * ENTRIES_PER_PAGE, + activePage * ENTRIES_PER_PAGE + ) + .map((item: PantryItem) => ( + + {item.name} + + {item.description === "" ? "—" : item.description} + + + {item.quantity} {item.quantityUnitType} + + + ))} + + + + + + setActivePage(Number(activePage ?? 1)) + } + totalPages={Math.max( + 1, + Math.ceil(entries.size / ENTRIES_PER_PAGE) + )} + /> + + + +
+
+ ); +}; + +export default Home; -- cgit