diff --git a/src/app/admin/hurtigutdeling/page.tsx b/src/app/admin/hurtigutdeling/page.tsx
index 4b54d3e..333ea20 100644
--- a/src/app/admin/hurtigutdeling/page.tsx
+++ b/src/app/admin/hurtigutdeling/page.tsx
@@ -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",
@@ -24,9 +22,6 @@ export default function HandoutPage() {
}}
>
Hurtigutdeling
-
-
-
diff --git a/src/components/RapidHandoutDetails.tsx b/src/components/RapidHandoutDetails.tsx
index e3eeff6..286c138 100644
--- a/src/components/RapidHandoutDetails.tsx
+++ b/src/components/RapidHandoutDetails.tsx
@@ -1,13 +1,17 @@
-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)
@@ -15,15 +19,16 @@ function mapOrdersToItemStatuses(orders: Order[]): ItemStatus[] {
(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({
@@ -31,12 +36,44 @@ export default function RapidHandoutDetails({
}: {
customer: UserDetail;
}) {
- const { data: orders } = useSWR(
+ const { data: orders, mutate: updateOrders } = useSWR(
`${BL_CONFIG.collection.order}?placed=true&customer=${customer.id}`,
BlFetcher.get,
{ refreshInterval: 5000 },
);
- const itemStatuses = mapOrdersToItemStatuses(orders ?? []);
+ const [itemStatuses, setItemStatuses] = useState([]);
+ const [scanModalOpen, setScanModalOpen] = useState(false);
+ const [redirectCountdownStarted, setRedirectCountdownStarted] =
+ useState(false);
+
+ const isFulfilled = itemStatuses.every((itemStatus) => itemStatus.fulfilled);
+
+ useEffect(() => {
+ BlFetcher.get(
+ `${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 ? (
@@ -44,10 +81,40 @@ export default function RapidHandoutDetails({
) : (
<>
-
+
Plukkliste
+ }
+ variant={"contained"}
+ onClick={() => setScanModalOpen(true)}
+ >
+ Scan bøker
+
+ {redirectCountdownStarted && (
+
+ )}
+
+ 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)}
+ />
>
);
}
diff --git a/src/components/matches/Scanner/ScannerModal.tsx b/src/components/matches/Scanner/ScannerModal.tsx
index 4081104..0a4500f 100644
--- a/src/components/matches/Scanner/ScannerModal.tsx
+++ b/src/components/matches/Scanner/ScannerModal.tsx
@@ -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 {
@@ -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[];
@@ -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 {
@@ -90,7 +84,7 @@ const ScannerModal = ({
severity: feedback ? "info" : "success",
visible: true,
});
- handleItemTransferred?.();
+ handleSuccessfulScan?.();
} catch (error) {
if (assertBlApiError(error) && error.msg) {
setFeedback({
diff --git a/src/components/matches/UserMatchDetail.tsx b/src/components/matches/UserMatchDetail.tsx
index 393da74..5065648 100644
--- a/src/components/matches/UserMatchDetail.tsx
+++ b/src/components/matches/UserMatchDetail.tsx
@@ -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,
@@ -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 = ({
@@ -147,8 +149,13 @@ const UserMatchDetail = ({
+ BlFetcher.post(BL_CONFIG.collection.match + "/transfer-item", {
+ blid,
+ })
+ }
open={scanModalOpen}
- handleItemTransferred={handleItemTransferred}
+ handleSuccessfulScan={handleItemTransferred}
handleClose={() => {
setScanModalOpen(false);
setRedirectCountdownStarted(isFulfilled);
diff --git a/src/components/search/UserDetailSearchField.tsx b/src/components/search/UserDetailSearchField.tsx
index dd19c3f..ad35ab8 100644
--- a/src/components/search/UserDetailSearchField.tsx
+++ b/src/components/search/UserDetailSearchField.tsx
@@ -18,7 +18,7 @@ export default function UserDetailSearchField({
return (
- Søk etter en kunde for å se plukkliste
+ Søk etter en kunde for å starte utdeling