Skip to content
This repository has been archived by the owner on Dec 18, 2024. It is now read-only.

Commit

Permalink
feat(rapid-handout): enable scanner in rapid handout
Browse files Browse the repository at this point in the history
  • Loading branch information
AdrianAndersen committed Aug 20, 2024
1 parent 3d53a7e commit 7810fb5
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 35 deletions.
7 changes: 1 addition & 6 deletions src/app/admin/hurtigutdeling/page.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import { Button, Card, Container, Typography } from "@mui/material";
import { 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",
Expand All @@ -24,9 +22,6 @@ export default function HandoutPage() {
}}
>
<Typography variant="h1">Hurtigutdeling</Typography>
<DynamicLink href={BL_CONFIG.blAdmin.basePath}>
<Button sx={{ mt: 2 }}>tilbake til bl-admin</Button>
</DynamicLink>
<RapidHandout />
</Box>
</Container>
Expand Down
97 changes: 82 additions & 15 deletions src/components/RapidHandoutDetails.tsx
Original file line number Diff line number Diff line change
@@ -1,53 +1,120 @@
import { Order, UserDetail } from "@boklisten/bl-model";
import { Alert, Typography } from "@mui/material";
import { Order, OrderItem, UserDetail } from "@boklisten/bl-model";
import QrCodeScannerIcon from "@mui/icons-material/QrCodeScanner";
import { Alert, Button, Typography } from "@mui/material";
import React, { useEffect, useState } from "react";
import useSWR from "swr";

import BlFetcher from "@/api/blFetcher";
import CountdownToRedirect from "@/components/CountdownToRedirect";
import { ItemStatus } from "@/components/matches/matches-helper";
import MatchItemTable from "@/components/matches/MatchItemTable";
import ScannerModal from "@/components/matches/Scanner/ScannerModal";
import BL_CONFIG from "@/utils/bl-config";

function mapOrdersToItemStatuses(orders: Order[]): ItemStatus[] {
function calculateUnfulfilledOrderItems(orders: Order[]): OrderItem[] {
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,
}));
orderItem.type === "rent",
);
}

function mapOrdersToItemStatuses(orders: Order[]): ItemStatus[] {
return calculateUnfulfilledOrderItems(orders).map((oi) => ({
id: oi.item,
title: oi.title,
fulfilled: false,
}));
}

export default function RapidHandoutDetails({
customer,
}: {
customer: UserDetail;
}) {
const { data: orders } = useSWR(
const { data: orders, mutate: updateOrders } = useSWR(
`${BL_CONFIG.collection.order}?placed=true&customer=${customer.id}`,
BlFetcher.get<Order[]>,
{ refreshInterval: 5000 },
);
const itemStatuses = mapOrdersToItemStatuses(orders ?? []);
const [itemStatuses, setItemStatuses] = useState<ItemStatus[]>([]);
const [scanModalOpen, setScanModalOpen] = useState(false);
const [redirectCountdownStarted, setRedirectCountdownStarted] =
useState(false);

const isFulfilled = itemStatuses.every((itemStatus) => itemStatus.fulfilled);

useEffect(() => {
BlFetcher.get<Order[]>(
`${BL_CONFIG.collection.order}?placed=true&customer=${customer.id}`,
)
.then((originalOrders) => {
return setItemStatuses(mapOrdersToItemStatuses(originalOrders));
})
.catch((error) => {
console.error("Failed to fetch original orders, error:", error);
});
}, [customer.id]);

useEffect(() => {
if (!orders) {
return;
}
const unfulfilledOrderItems = calculateUnfulfilledOrderItems(orders);
setItemStatuses((previousState) =>
previousState.map((itemStatus) => ({
...itemStatus,
fulfilled: !unfulfilledOrderItems.some(
(orderItem) => orderItem.item === itemStatus.id,
),
})),
);
}, [orders]);

return itemStatuses.length === 0 ? (
<Alert severity={"info"} sx={{ mt: 2 }}>
Denne kunden har for øyeblikket ingen bestilte bøker
</Alert>
) : (
<>
<Typography variant={"h2"} textAlign={"center"} mt={6}>
<Typography variant={"h2"} textAlign={"center"} mt={6} mb={2}>
Plukkliste
</Typography>
<Button
color="success"
startIcon={<QrCodeScannerIcon />}
variant={"contained"}
onClick={() => setScanModalOpen(true)}
>
Scan bøker
</Button>
<MatchItemTable itemStatuses={itemStatuses} isSender={true} />
{redirectCountdownStarted && (
<CountdownToRedirect path={"/matches"} seconds={5} />
)}
<ScannerModal
onScan={(blid) =>
BlFetcher.post(BL_CONFIG.collection.order + "/rapid-handout", {
blid,
customerId: customer.id,
})
}
open={scanModalOpen}
handleSuccessfulScan={updateOrders}
handleClose={() => {
setScanModalOpen(false);
setRedirectCountdownStarted(isFulfilled);
}}
itemStatuses={itemStatuses}
expectedItems={itemStatuses.map((itemStatus) => itemStatus.id)}
fulfilledItems={itemStatuses
.filter((itemStatus) => itemStatus.fulfilled)
.map((itemStatus) => itemStatus.id)}
/>
</>
);
}
18 changes: 6 additions & 12 deletions src/components/matches/Scanner/ScannerModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,11 @@ import Typography from "@mui/material/Typography";
import { Scanner, IDetectedBarcode } from "@yudiel/react-qr-scanner";
import React, { useEffect, useState } from "react";

import BlFetcher from "@/api/blFetcher";
import { ItemStatus } from "@/components/matches/matches-helper";
import ProgressBar from "@/components/matches/matchesList/ProgressBar";
import MatchItemTable from "@/components/matches/MatchItemTable";
import ManualRegistrationModal from "@/components/matches/Scanner/ManualRegistrationModal";
import ScannerFeedback from "@/components/matches/Scanner/ScannerFeedback";
import BL_CONFIG from "@/utils/bl-config";
import { assertBlApiError, ScannedTextType, TextType } from "@/utils/types";

function determineScannedTextType(scannedText: string): ScannedTextType {
Expand All @@ -30,16 +28,18 @@ type Feedback = {
};

const ScannerModal = ({
onScan,
open,
handleClose,
handleItemTransferred,
handleSuccessfulScan,
itemStatuses,
expectedItems,
fulfilledItems,
}: {
onScan: (blid: string) => Promise<[{ feedback: string }]>;
open: boolean;
handleClose: () => void;
handleItemTransferred?: (() => void) | undefined;
handleSuccessfulScan?: (() => void) | undefined;
itemStatuses: ItemStatus[];
expectedItems: string[];
fulfilledItems: string[];
Expand Down Expand Up @@ -73,13 +73,7 @@ const ScannerModal = ({
}

try {
const [{ feedback }] = await BlFetcher.post<
[
{
feedback: string;
},
]
>(BL_CONFIG.collection.match + "/transfer-item", { blid: scannedText });
const [{ feedback }] = await onScan(scannedText);
try {
navigator?.vibrate(100);
} catch {
Expand All @@ -90,7 +84,7 @@ const ScannerModal = ({
severity: feedback ? "info" : "success",
visible: true,
});
handleItemTransferred?.();
handleSuccessfulScan?.();
} catch (error) {
if (assertBlApiError(error) && error.msg) {
setFeedback({
Expand Down
9 changes: 8 additions & 1 deletion src/components/matches/UserMatchDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import QrCodeScannerIcon from "@mui/icons-material/QrCodeScanner";
import { Alert, AlertTitle, Box, Button, Typography } from "@mui/material";
import React, { useState } from "react";

import BlFetcher from "@/api/blFetcher";
import CountdownToRedirect from "@/components/CountdownToRedirect";
import {
calculateFulfilledUserMatchItems,
Expand All @@ -16,6 +17,7 @@ import MeetingInfo from "@/components/matches/MeetingInfo";
import OtherPersonContact from "@/components/matches/OtherPersonContact";
import ScannerModal from "@/components/matches/Scanner/ScannerModal";
import ScannerTutorial from "@/components/matches/Scanner/ScannerTutorial";
import BL_CONFIG from "@/utils/bl-config";
import { UserMatchWithDetails } from "@/utils/types";

const UserMatchDetail = ({
Expand Down Expand Up @@ -147,8 +149,13 @@ const UserMatchDetail = ({
<MatchItemTable itemStatuses={itemStatuses} isSender={isSender} />

<ScannerModal
onScan={(blid) =>
BlFetcher.post(BL_CONFIG.collection.match + "/transfer-item", {
blid,
})
}
open={scanModalOpen}
handleItemTransferred={handleItemTransferred}
handleSuccessfulScan={handleItemTransferred}
handleClose={() => {
setScanModalOpen(false);
setRedirectCountdownStarted(isFulfilled);
Expand Down
2 changes: 1 addition & 1 deletion src/components/search/UserDetailSearchField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export default function UserDetailSearchField({
return (
<Box sx={{ width: "100%" }}>
<Typography sx={{ mt: 2, mb: 1, textAlign: "center" }}>
Søk etter en kunde for å se plukkliste
Søk etter en kunde for å starte utdeling
</Typography>
<Autocomplete
autoComplete
Expand Down

0 comments on commit 7810fb5

Please sign in to comment.