Skip to content

Commit

Permalink
/cashier-v2 なんかいい感じに
Browse files Browse the repository at this point in the history
  • Loading branch information
toririm committed Sep 28, 2024
1 parent cae2c8a commit 394934a
Showing 1 changed file with 147 additions and 24 deletions.
171 changes: 147 additions & 24 deletions app/routes/cashier-v2.tsx
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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",
Expand All @@ -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(
Expand All @@ -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<HTMLElement>(
document.getElementById("discountOrderId"),
);
const receivedDOM = useRef<HTMLInputElement>(null);
const descriptionDOM = useRef<HTMLInputElement>(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;
}
Expand All @@ -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 (
<>
<div className="grid grid-cols-2">
<div className="grid grid-cols-3">
<div>
{items?.map((item) => (
<div key={item.id}>
Expand Down Expand Up @@ -152,10 +228,12 @@ export default function Cashier() {
<div>
<p>割引券番号</p>
<InputOTP
id="discountOrderId"
maxLength={3}
pattern={REGEXP_ONLY_DIGITS}
value={discountOrderId}
onChange={(value) => setDiscountOrderId(value)}
disabled={inputStatus !== "discount"}
>
<InputOTPGroup />
<InputOTPSlot index={0} />
Expand All @@ -172,14 +250,20 @@ export default function Cashier() {
value={received}
onChange={(e) => setReceived(e.target.value)}
placeholder="お預かり金額を入力"
disabled={inputStatus !== "received"}
ref={receivedDOM}
/>
<Input disabled value={chargeView} />
<Input
id="description"
value={description}
onChange={(e) => setDescription(e.target.value)}
placeholder="備考"
disabled={inputStatus !== "description"}
ref={descriptionDOM}
/>
</div>
<div>
<p>{inputStatus}</p>
{orderItems.map((item, idx) => (
<div key={`${idx}-${item.id}`} className="grid grid-cols-2">
<p className="font-bold text-lg">{idx + 1}</p>
Expand All @@ -198,6 +282,45 @@ export default function Cashier() {
)}
</div>
</div>
<AlertDialog open={DialogOpen} onOpenChange={setDialogOpen}>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>オーダーを確定しますか?</AlertDialogTitle>
<AlertDialogDescription>
以下の内容で提出します
</AlertDialogDescription>
{orderItems.map((item, idx) => (
<AlertDialogDescription key={`${idx}-${item.id}`}>
{`${idx + 1} ―― ${item.name} ¥${item.price} ${type2label[item.type]}`}
</AlertDialogDescription>
))}
<AlertDialogDescription>
合計金額: &yen;{newOrder.total}
</AlertDialogDescription>
{discountOrder && (
<AlertDialogDescription>
割引: -&yen;{newOrder.discountInfo.discount}
</AlertDialogDescription>
)}
<AlertDialogDescription>
支払金額: &yen;{newOrder.billingAmount}
</AlertDialogDescription>
<AlertDialogDescription>
お預かり金額: &yen;{receivedNum}
</AlertDialogDescription>
<AlertDialogDescription>
お釣り: &yen;{chargeView}
</AlertDialogDescription>
<AlertDialogDescription>備考: {description}</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel onClick={proceedStatus}>
キャンセル
</AlertDialogCancel>
<AlertDialogAction onClick={submitOrder}>確定</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</>
);
}
Expand Down

0 comments on commit 394934a

Please sign in to comment.