Update inventory
+ Filter containers
+ setSearch(event.currentTarget.value)}
+ placeholder="Type drink name, type or inventory"
+ autoComplete="off"
+ />
+
Drink container
- { lData.containers.map((container) => (
-
+ { filteredContainers.map((container) => (
+
))}
diff --git a/app/routes/scan/route.tsx b/app/routes/scan/route.tsx
index 599247f..fa1244a 100644
--- a/app/routes/scan/route.tsx
+++ b/app/routes/scan/route.tsx
@@ -1,6 +1,7 @@
import type { ActionFunctionArgs, LoaderFunctionArgs } from "@remix-run/node";
import { json, redirect } from "@remix-run/node";
import { useActionData, useLoaderData } from "@remix-run/react";
+import { useEffect, useRef, useState } from "react";
import { Col, Container, Form, Row, Table } from "react-bootstrap";
import timeAgo from "~/utils/datetime";
@@ -27,9 +28,13 @@ export async function action({
const form = await request.formData();
const barcode = form.get("barcode")?.toString() || "";
- const container = await dbe.container.findUnique({
- select: {id: true, inventory: true, drink_id: true},
- where: {barcode: barcode}
+ const container = await dbe.container.findUnique({
+ select: {
+ id: true,
+ inventory: true,
+ drink: { select: { name: true, image: true } },
+ },
+ where: { barcode: barcode },
});
// No drink found for barcode, do nothing
@@ -48,58 +53,138 @@ export async function action({
// Log entry
await dbe.history.create({data: {container: {connect: {id: container.id}}, inventoryAfter: newInventory}});
- return json({error: ""});
+ return json({
+ error: "",
+ lastScanned: {
+ name: container.drink.name,
+ image: container.drink.image ?? null,
+ },
+ });
};
export default function ScanRoute() {
const aData = useActionData
();
const lData = useLoaderData();
+ const [lastScanned, setLastScanned] = useState<{name:string;image:string|null} | null>(null);
+ const [showLastScanned, setShowLastScanned] = useState(false);
+ const inputRef = useRef(null);
+
const hasError = aData?.error ? aData.error.length > 0 : false;
+ const focusInput = () => {
+ window.setTimeout(() => inputRef.current?.focus(), 0);
+ };
+
+ useEffect(() => {
+ focusInput();
+ }, []);
+
+ useEffect(() => {
+ focusInput();
+ }, [aData]);
+
+ useEffect(() => {
+ const handleBlur = () => {
+ focusInput();
+ };
+
+ const input = inputRef.current;
+ input?.addEventListener("blur", handleBlur);
+
+ return () => {
+ input?.removeEventListener("blur", handleBlur);
+ };
+ }, []);
+
+ useEffect(() => {
+ if (!aData?.lastScanned) {
+ return;
+ }
+
+ setLastScanned(aData.lastScanned);
+ setShowLastScanned(true);
+
+ const timer = window.setTimeout(() => {
+ setShowLastScanned(false);
+ }, 5000);
+
+ return () => window.clearTimeout(timer);
+ }, [aData?.lastScanned]);
+
return (
-
-
-
-
-
- Scan barcode:
-
-
-
- {aData?.error}
-
-
-
-
-
-
-
+
+
+
History
-
-
-
- | Drink |
- When |
- Amount left |
-
-
-
- {lData.history.map((entry) => (
-
- | {entry.container.drink.name} |
- {timeAgo(new Date(entry.checkoutAt))} |
- {entry.inventoryAfter} |
-
- ))}
-
-
+
+
+
+
+ | Drink |
+ When |
+ Amount left |
+
+
+
+ {lData.history.map((entry) => (
+
+ | {entry.container.drink.name} |
+ {timeAgo(new Date(entry.checkoutAt))} |
+ {entry.inventoryAfter} |
+
+ ))}
+
+
+
+
+
+ Scan Barcode
+
+
+
+
+
+ {aData?.error}
+
+
+
+
+
+
+
+
+ {showLastScanned && lastScanned ? (
+ <>
+
{lastScanned.name}
+ {lastScanned.image ? (
+

+ ) : (
+
No image available
+ )}
+ >
+ ) : (
+
+
Please scan your drink
+
+ )}
+
+
+