diff --git a/app/routes/cashier-v2.tsx b/app/routes/cashier-v2.tsx index 1ab366e4..a5452737 100644 --- a/app/routes/cashier-v2.tsx +++ b/app/routes/cashier-v2.tsx @@ -1,9 +1,19 @@ import { parseWithZod } from "@conform-to/zod"; import { type ClientActionFunction, useSubmit } from "@remix-run/react"; import { REGEXP_ONLY_DIGITS } from "input-otp"; -import { useEffect, useState } from "react"; +import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import useSWRSubscription from "swr/subscription"; import { z } from "zod"; +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, +} from "~/components/ui/alert-dialog"; import { Button } from "~/components/ui/button"; import { Input } from "~/components/ui/input"; import { @@ -21,6 +31,14 @@ import { orderRepository } from "~/repositories/order"; const keys = ["a", "s", "d", "f", "g", "h", "j", "k", "l", ";"]; +const InputStatus = [ + "discount", + "items", + "received", + "description", + "submit", +] as const; + export default function Cashier() { const { data: items } = useSWRSubscription( "items", @@ -35,6 +53,9 @@ export default function Cashier() { const [received, setReceived] = useState(""); const [discountOrderId, setDiscountOrderId] = useState(""); const [description, setDescription] = useState(""); + const [inputStatus, setInputStatus] = + useState<(typeof InputStatus)[number]>("discount"); + const [DialogOpen, setDialogOpen] = useState(false); const discountOrderIdNum = Number(discountOrderId); const discountOrder = orders?.find( @@ -58,7 +79,25 @@ export default function Cashier() { const charge = newOrder.received - newOrder.billingAmount; const chargeView: string | number = charge < 0 ? "不足しています" : charge; - const submitOrder = () => { + const discountOrderDOM = useRef( + document.getElementById("discountOrderId"), + ); + const receivedDOM = useRef(null); + const descriptionDOM = useRef(null); + + const proceedStatus = useCallback(() => { + const idx = InputStatus.indexOf(inputStatus); + setInputStatus(InputStatus[(idx + 1) % InputStatus.length]); + }, [inputStatus]); + + const prevousStatus = useCallback(() => { + const idx = InputStatus.indexOf(inputStatus); + setInputStatus( + InputStatus[(idx - 1 + InputStatus.length) % InputStatus.length], + ); + }, [inputStatus]); + + const submitOrder = useCallback(() => { if (charge < 0) { return; } @@ -73,49 +112,86 @@ export default function Cashier() { setReceived(""); setDiscountOrderId(""); setDescription(""); - }; + setInputStatus("discount"); + }, [charge, newOrder, orderItems, submit]); + + const moveFocus = useCallback(() => { + switch (inputStatus) { + case "discount": + discountOrderDOM.current?.focus(); + break; + case "items": + break; + case "received": + receivedDOM.current?.focus(); + break; + case "description": + descriptionDOM.current?.focus(); + break; + case "submit": + setDialogOpen(true); + break; + } + }, [inputStatus]); + + useEffect(moveFocus); + + const keyEventHandlers = useMemo(() => { + return { + ArrowRight: proceedStatus, + ArrowLeft: prevousStatus, + Escape: () => { + setInputStatus("discount"); + setDialogOpen(false); + setOrderItems([]); + setReceived(""); + setDiscountOrderId(""); + setDescription(""); + }, + }; + }, [proceedStatus, prevousStatus]); useEffect(() => { - items?.forEach((item, idx) => { + const handlers = items?.map((item, idx) => { const handler = (event: KeyboardEvent) => { - const active = document.activeElement; - if (active?.id === "description") { + if (inputStatus !== "items") { return; } if (event.key === keys[idx]) { setOrderItems((prevItems) => [...prevItems, item]); } }; - window.addEventListener("keydown", handler); - return () => window.removeEventListener("keydown", handler); + return handler; }); - }, [items]); + for (const handler of handlers ?? []) { + window.addEventListener("keydown", handler); + } - useEffect(() => { - const handler = (event: KeyboardEvent) => { - if (event.key === "Enter") { - submitOrder(); + return () => { + for (const handler of handlers ?? []) { + window.removeEventListener("keydown", handler); } }; - window.addEventListener("keydown", handler); - return () => window.removeEventListener("keydown", handler); - }); + }, [items, inputStatus]); useEffect(() => { const handler = (event: KeyboardEvent) => { - if (event.key === "Escape") { - setOrderItems([]); - setReceived(""); - setDiscountOrderId(""); + const key = event.key; + for (const [keyName, keyHandler] of Object.entries(keyEventHandlers)) { + if (key === keyName) { + keyHandler(); + } } }; window.addEventListener("keydown", handler); - return () => window.removeEventListener("keydown", handler); - }); + return () => { + window.removeEventListener("keydown", handler); + }; + }, [keyEventHandlers]); return ( <> -
+
{items?.map((item) => (
@@ -152,10 +228,12 @@ export default function Cashier() {

割引券番号

setDiscountOrderId(value)} + disabled={inputStatus !== "discount"} > @@ -172,14 +250,20 @@ export default function Cashier() { value={received} onChange={(e) => setReceived(e.target.value)} placeholder="お預かり金額を入力" + disabled={inputStatus !== "received"} + ref={receivedDOM} /> setDescription(e.target.value)} placeholder="備考" + disabled={inputStatus !== "description"} + ref={descriptionDOM} /> +
+
+

{inputStatus}

{orderItems.map((item, idx) => (

{idx + 1}

@@ -198,6 +282,45 @@ export default function Cashier() { )}
+ + + + オーダーを確定しますか? + + 以下の内容で提出します + + {orderItems.map((item, idx) => ( + + {`${idx + 1} ―― ${item.name} ¥${item.price} ${type2label[item.type]}`} + + ))} + + 合計金額: ¥{newOrder.total} + + {discountOrder && ( + + 割引: -¥{newOrder.discountInfo.discount} + + )} + + 支払金額: ¥{newOrder.billingAmount} + + + お預かり金額: ¥{receivedNum} + + + お釣り: ¥{chargeView} + + 備考: {description} + + + + キャンセル + + 確定 + + + ); }