From 3d53a7ea643231d22a5fd57add735feee8c83639 Mon Sep 17 00:00:00 2001 From: Adrian Andersen Date: Mon, 19 Aug 2024 19:09:32 +0200 Subject: [PATCH] feat(rapid-handout): barebones solution --- src/app/admin/hurtigutdeling/page.tsx | 35 +++++++++ src/components/RapidHandout.tsx | 37 +++++++++ src/components/RapidHandoutDetails.tsx | 53 +++++++++++++ .../search/UserDetailSearchField.tsx | 76 +++++++++++++++++++ .../search/UserDetailSearchResult.tsx | 30 ++++++++ 5 files changed, 231 insertions(+) create mode 100644 src/app/admin/hurtigutdeling/page.tsx create mode 100644 src/components/RapidHandout.tsx create mode 100644 src/components/RapidHandoutDetails.tsx create mode 100644 src/components/search/UserDetailSearchField.tsx create mode 100644 src/components/search/UserDetailSearchResult.tsx diff --git a/src/app/admin/hurtigutdeling/page.tsx b/src/app/admin/hurtigutdeling/page.tsx new file mode 100644 index 0000000..4b54d3e --- /dev/null +++ b/src/app/admin/hurtigutdeling/page.tsx @@ -0,0 +1,35 @@ +import { Button, Card, Container, Typography } from "@mui/material"; +import { Box } from "@mui/system"; +import { Metadata } from "next"; + +import DynamicLink from "@/components/DynamicLink"; +import RapidHandout from "@/components/RapidHandout"; +import BL_CONFIG from "@/utils/bl-config"; + +export const metadata: Metadata = { + title: "Hurtigutdeling", + description: "Adminverktøy for å raskt dele ut bøker.", +}; + +export default function HandoutPage() { + return ( + + + + Hurtigutdeling + + + + + + + + ); +} diff --git a/src/components/RapidHandout.tsx b/src/components/RapidHandout.tsx new file mode 100644 index 0000000..e6f32f6 --- /dev/null +++ b/src/components/RapidHandout.tsx @@ -0,0 +1,37 @@ +"use client"; +import { UserDetail } from "@boklisten/bl-model"; +import { Alert, Button } from "@mui/material"; +import { useState } from "react"; + +import { isLoggedIn } from "@/api/auth"; +import DynamicLink from "@/components/DynamicLink"; +import RapidHandoutDetails from "@/components/RapidHandoutDetails"; +import UserDetailSearchField from "@/components/search/UserDetailSearchField"; +import useIsHydrated from "@/utils/useIsHydrated"; + +export default function RapidHandout() { + const hydrated = useIsHydrated(); + const [customer, setCustomer] = useState(null); + + return hydrated && isLoggedIn() ? ( + <> + { + setCustomer(userDetail); + }} + /> + {customer && } + + ) : ( + <> + + Du må logge inn som administrator for å bruke denne siden. + + + + + + ); +} diff --git a/src/components/RapidHandoutDetails.tsx b/src/components/RapidHandoutDetails.tsx new file mode 100644 index 0000000..e3eeff6 --- /dev/null +++ b/src/components/RapidHandoutDetails.tsx @@ -0,0 +1,53 @@ +import { Order, UserDetail } from "@boklisten/bl-model"; +import { Alert, Typography } from "@mui/material"; +import useSWR from "swr"; + +import BlFetcher from "@/api/blFetcher"; +import { ItemStatus } from "@/components/matches/matches-helper"; +import MatchItemTable from "@/components/matches/MatchItemTable"; +import BL_CONFIG from "@/utils/bl-config"; + +function mapOrdersToItemStatuses(orders: Order[]): ItemStatus[] { + return orders + .filter((order) => order.byCustomer && !order.handoutByDelivery) + .flatMap((order) => order.orderItems) + .filter( + (orderItem) => + !orderItem.movedToOrder && + !orderItem.handout && + (orderItem.type === "rent" || + orderItem.type === "buy" || + orderItem.type === "partly-payment"), + ) + .map((oi) => ({ + id: oi.item, + title: oi.title, + fulfilled: false, + })); +} + +export default function RapidHandoutDetails({ + customer, +}: { + customer: UserDetail; +}) { + const { data: orders } = useSWR( + `${BL_CONFIG.collection.order}?placed=true&customer=${customer.id}`, + BlFetcher.get, + { refreshInterval: 5000 }, + ); + const itemStatuses = mapOrdersToItemStatuses(orders ?? []); + + return itemStatuses.length === 0 ? ( + + Denne kunden har for øyeblikket ingen bestilte bøker + + ) : ( + <> + + Plukkliste + + + + ); +} diff --git a/src/components/search/UserDetailSearchField.tsx b/src/components/search/UserDetailSearchField.tsx new file mode 100644 index 0000000..dd19c3f --- /dev/null +++ b/src/components/search/UserDetailSearchField.tsx @@ -0,0 +1,76 @@ +import { UserDetail } from "@boklisten/bl-model"; +import { Autocomplete, Typography } from "@mui/material"; +import TextField from "@mui/material/TextField"; +import { Box } from "@mui/system"; +import { useState } from "react"; + +import BlFetcher from "@/api/blFetcher"; +import UserDetailSearchResult from "@/components/search/UserDetailSearchResult"; +import BL_CONFIG from "@/utils/bl-config"; + +export default function UserDetailSearchField({ + onSelectedResult, +}: { + onSelectedResult: (userDetail: UserDetail | null) => void; +}) { + const [searchValue, setSearchValue] = useState(null); + const [searchResults, setSearchResults] = useState([]); + return ( + + + Søk etter en kunde for å se plukkliste + + a.id === b.id} + filterSelectedOptions + getOptionLabel={(option) => option.name ?? option.email} + getOptionKey={(option) => option.id} + filterOptions={(x) => x} + noOptionsText={null} + onInputChange={async (event, newInputValue) => { + if (event === null) return; + if (newInputValue.length < 3) { + onSelectedResult(null); + setSearchValue(null); + setSearchResults([]); + return; + } + try { + const result = await BlFetcher.get( + `${BL_CONFIG.collection.userDetail}?s=${newInputValue}`, + ); + setSearchResults(result); + } catch { + setSearchResults([]); + } + }} + options={searchResults} + renderOption={({ key }, userDetail) => ( + { + setSearchValue(userDetail); + setSearchResults([userDetail]); + onSelectedResult(userDetail); + }} + /> + )} + renderInput={(params) => ( + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error Using example from https://mui.com/material-ui/react-autocomplete/#system-FreeSolo.tsx + + )} + /> + + ); +} diff --git a/src/components/search/UserDetailSearchResult.tsx b/src/components/search/UserDetailSearchResult.tsx new file mode 100644 index 0000000..35eb7ce --- /dev/null +++ b/src/components/search/UserDetailSearchResult.tsx @@ -0,0 +1,30 @@ +import { UserDetail } from "@boklisten/bl-model"; +import { Email, Person, Phone } from "@mui/icons-material"; +import { ListItemButton, Stack, Typography } from "@mui/material"; + +export default function UserDetailSearchResult({ + userDetail, + onClick, +}: { + userDetail: UserDetail; + onClick: () => void; +}) { + return ( + + + + + {userDetail.name} + + + + {userDetail.email} + + + + {userDetail.phone} + + + + ); +}