-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge remote-tracking branch 'origin/main' into issue/145
- Loading branch information
Showing
36 changed files
with
1,630 additions
and
354 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import { useEffect, useRef } from "react"; | ||
|
||
/** | ||
* focus が true に変化した際に ref が指す DOM にフォーカスを当てる | ||
* @param focus フォーカスを当てるかどうか | ||
* @returns | ||
*/ | ||
const useFocusRef = (focus: boolean) => { | ||
const DOMRef = useRef<HTMLInputElement>(null); | ||
|
||
useEffect(() => { | ||
if (focus) { | ||
DOMRef.current?.focus(); | ||
} | ||
}, [focus]); | ||
|
||
return DOMRef; | ||
}; | ||
|
||
export { useFocusRef }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import { useCallback, useMemo, useState } from "react"; | ||
|
||
const InputStatusList = [ | ||
"discount", | ||
"items", | ||
"received", | ||
"description", | ||
"submit", | ||
] as const; | ||
|
||
const inputLen = InputStatusList.length; | ||
|
||
const narrowInLen = (idx: number) => Math.min(Math.max(idx, 0), inputLen - 1); | ||
|
||
/** | ||
* CashierV2 のドメイン固有のフック | ||
* | ||
* 入力ステータスを管理する | ||
*/ | ||
const useInputStatus = () => { | ||
const [idx, setIdx] = useState(0); | ||
|
||
const proceedStatus = useCallback(() => { | ||
setIdx((prev) => narrowInLen(prev + 1)); | ||
}, []); | ||
|
||
const previousStatus = useCallback(() => { | ||
setIdx((prev) => narrowInLen(prev - 1)); | ||
}, []); | ||
|
||
const inputStatus = useMemo(() => InputStatusList[idx], [idx]); | ||
|
||
const resetStatus = useCallback(() => { | ||
setIdx(0); | ||
}, []); | ||
|
||
return { inputStatus, proceedStatus, previousStatus, resetStatus }; | ||
}; | ||
|
||
export { useInputStatus }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { useMemo } from "react"; | ||
import type { WithId } from "~/lib/typeguard"; | ||
import type { OrderEntity } from "~/models/order"; | ||
|
||
/** | ||
* オーダーのIDの最大値と次のIDを取得する | ||
* @param orders オーダーのリスト | ||
* @returns オーダーIDの最大値と次のID | ||
*/ | ||
const useLatestOrderId = (orders: WithId<OrderEntity>[] | undefined) => { | ||
const latestOrderId = useMemo( | ||
() => orders?.reduce((acc, cur) => Math.max(acc, cur.orderId), 0) ?? 0, | ||
[orders], | ||
); | ||
const nextOrderId = useMemo(() => latestOrderId + 1, [latestOrderId]); | ||
|
||
return { latestOrderId, nextOrderId }; | ||
}; | ||
export { useLatestOrderId }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
import { useReducer } from "react"; | ||
import type { WithId } from "~/lib/typeguard"; | ||
import type { ItemEntity } from "~/models/item"; | ||
import { OrderEntity } from "~/models/order"; | ||
|
||
type BaseAction<TypeName extends string> = { type: TypeName }; | ||
type Action< | ||
TypeName extends string, | ||
U extends Record<string, unknown> = Record<never, never>, | ||
> = BaseAction<TypeName> & U; | ||
|
||
type Clear = Action<"clear", { effectFn?: () => void }>; | ||
type UpdateOrderId = Action<"updateOrderId", { orderId: number }>; | ||
type AddItem = Action<"addItem", { item: WithId<ItemEntity> }>; | ||
type RemoveItem = Action<"removeItem", { idx: number }>; | ||
type MutateItem = Action< | ||
"mutateItem", | ||
{ idx: number; action: (prev: WithId<ItemEntity>) => WithId<ItemEntity> } | ||
>; | ||
type ApplyDiscount = Action< | ||
"applyDiscount", | ||
{ discountOrder: WithId<OrderEntity> } | ||
>; | ||
type RemoveDiscount = Action<"removeDiscount">; | ||
type SetReceived = Action<"setReceived", { received: string }>; | ||
type SetDescription = Action<"setDescription", { description: string }>; | ||
/** | ||
* オーダーの状態を更新するためのアクション型 | ||
*/ | ||
export type OrderAction = | ||
| Clear | ||
| UpdateOrderId | ||
| AddItem | ||
| RemoveItem | ||
| MutateItem | ||
| ApplyDiscount | ||
| RemoveDiscount | ||
| SetReceived | ||
| SetDescription; | ||
|
||
type OrderReducer<T extends OrderAction> = ( | ||
state: OrderEntity, | ||
action: T, | ||
) => OrderEntity; | ||
|
||
const clear: OrderReducer<Clear> = (state, action) => { | ||
const effectFn = action.effectFn; | ||
if (effectFn) { | ||
effectFn(); | ||
} | ||
return OrderEntity.createNew({ orderId: state.orderId }); | ||
}; | ||
|
||
const updateOrderId: OrderReducer<UpdateOrderId> = (state, action) => { | ||
const updated = state.clone(); | ||
updated.orderId = action.orderId; | ||
return updated; | ||
}; | ||
|
||
const addItem: OrderReducer<AddItem> = (state, action) => { | ||
const updated = state.clone(); | ||
updated.items = [...updated.items, action.item]; | ||
return updated; | ||
}; | ||
|
||
const removeItem: OrderReducer<RemoveItem> = (state, action) => { | ||
const updated = state.clone(); | ||
updated.items = updated.items.filter((_, idx) => idx !== action.idx); | ||
return updated; | ||
}; | ||
|
||
const mutateItem: OrderReducer<MutateItem> = (state, action) => { | ||
const updated = state.clone(); | ||
updated.items[action.idx] = action.action(updated.items[action.idx]); | ||
return updated; | ||
}; | ||
|
||
const applyDiscount: OrderReducer<ApplyDiscount> = (state, action) => { | ||
const updated = state.clone(); | ||
updated.applyDiscount(action.discountOrder); | ||
return updated; | ||
}; | ||
|
||
const removeDiscount: OrderReducer<RemoveDiscount> = (state, action) => { | ||
const updated = state.clone(); | ||
updated.removeDiscount(); | ||
return updated; | ||
}; | ||
|
||
const setReceived: OrderReducer<SetReceived> = (state, action) => { | ||
const updated = state.clone(); | ||
updated.received = Number(action.received); | ||
return updated; | ||
}; | ||
|
||
const setDescription: OrderReducer<SetDescription> = (state, action) => { | ||
const updated = state.clone(); | ||
updated.description = action.description; | ||
return updated; | ||
}; | ||
|
||
const reducer: OrderReducer<OrderAction> = (state, action): OrderEntity => { | ||
switch (action.type) { | ||
case "clear": | ||
return clear(state, action); | ||
case "applyDiscount": | ||
return applyDiscount(state, action); | ||
case "removeDiscount": | ||
return removeDiscount(state, action); | ||
case "addItem": | ||
return addItem(state, action); | ||
case "removeItem": | ||
return removeItem(state, action); | ||
case "mutateItem": | ||
return mutateItem(state, action); | ||
case "setReceived": | ||
return setReceived(state, action); | ||
case "setDescription": | ||
return setDescription(state, action); | ||
case "updateOrderId": | ||
return updateOrderId(state, action); | ||
} | ||
}; | ||
|
||
/** | ||
* オーダーの状態を管理する | ||
* | ||
* reducer が受け付ける状態には下記がある: | ||
* - clear | ||
* - applyDiscount | ||
* - removeDiscount | ||
* - addItem | ||
* - mutateItem | ||
* - setReceived | ||
* - setDescription | ||
* - updateOrderId | ||
* @returns オーダーの状態とそれを更新する関数 | ||
*/ | ||
const useOrderState = () => | ||
useReducer(reducer, OrderEntity.createNew({ orderId: -1 })); | ||
|
||
export { useOrderState }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import { useEffect } from "react"; | ||
|
||
/** | ||
* 上下キーで数値を増減させないEffect | ||
*/ | ||
const usePreventNumberKeyUpDown = () => { | ||
useEffect(() => { | ||
const handler = (event: KeyboardEvent) => { | ||
if (event.key === "ArrowUp" || event.key === "ArrowDown") { | ||
// 上下キーで数値を増減させない | ||
event.preventDefault(); | ||
} | ||
}; | ||
window.addEventListener("keydown", handler); | ||
window.addEventListener("keyup", handler); | ||
return () => { | ||
window.removeEventListener("keydown", handler); | ||
window.removeEventListener("keyup", handler); | ||
}; | ||
}, []); | ||
}; | ||
|
||
export { usePreventNumberKeyUpDown }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import { useCallback, useMemo, useState } from "react"; | ||
|
||
type UISession = { | ||
date: Date; | ||
key: string; | ||
}; | ||
|
||
/** | ||
* UI のセッションを管理するためのフック | ||
* | ||
* renewUISession を呼ぶことでセッションを更新できる | ||
* | ||
* UISession.key を DOM の key に指定することで、セッションが変更されたときに再描画される | ||
*/ | ||
const useUISession = (): [UISession, () => void] => { | ||
const [date, setDate] = useState(new Date()); | ||
|
||
const UISession = useMemo(() => { | ||
return { | ||
date, | ||
key: date.toJSON(), | ||
}; | ||
}, [date]); | ||
|
||
const renewUISession = useCallback(() => { | ||
setDate(new Date()); | ||
}, []); | ||
|
||
return [UISession, renewUISession]; | ||
}; | ||
|
||
export { useUISession }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import { | ||
type ChangeEventHandler, | ||
useCallback, | ||
useEffect, | ||
useState, | ||
} from "react"; | ||
import { useFocusRef } from "../functional/useFocusRef"; | ||
import { Input, type InputProps } from "../ui/input"; | ||
|
||
type props = InputProps & { | ||
onTextSet: (text: string) => void; | ||
focus: boolean; | ||
}; | ||
|
||
/** | ||
* focus が true のときに自動でフォーカスを当てるテキストボックス | ||
*/ | ||
const AttractiveTextBox = ({ focus, onTextSet, ...props }: props) => { | ||
const [text, setText] = useState(""); | ||
const DOMRef = useFocusRef(focus); | ||
|
||
const onChangeHandler: ChangeEventHandler<HTMLInputElement> = useCallback( | ||
(event) => setText(event.target.value), | ||
[], | ||
); | ||
|
||
useEffect(() => { | ||
onTextSet(text); | ||
}, [text, onTextSet]); | ||
|
||
return ( | ||
<Input | ||
value={text} | ||
onChange={onChangeHandler} | ||
ref={DOMRef} | ||
disabled={!focus} | ||
{...props} | ||
/> | ||
); | ||
}; | ||
|
||
export { AttractiveTextBox }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import { REGEXP_ONLY_DIGITS } from "input-otp"; | ||
import { | ||
type ComponentPropsWithoutRef, | ||
type ElementRef, | ||
forwardRef, | ||
} from "react"; | ||
import { InputOTP, InputOTPGroup, InputOTPSlot } from "../ui/input-otp"; | ||
|
||
/** | ||
* 3桁の数字を入力するためのコンポーネント | ||
*/ | ||
const ThreeDigitsInput = forwardRef< | ||
ElementRef<typeof InputOTP>, | ||
Omit< | ||
ComponentPropsWithoutRef<typeof InputOTP>, | ||
"maxLength" | "pattern" | "render" | ||
> | ||
>(({ ...props }, ref) => { | ||
return ( | ||
<InputOTP ref={ref} maxLength={3} pattern={REGEXP_ONLY_DIGITS} {...props}> | ||
<InputOTPGroup> | ||
<InputOTPSlot index={0} className="font-mono text-3xl" /> | ||
<InputOTPSlot index={1} className="font-mono text-3xl" /> | ||
<InputOTPSlot index={2} className="font-mono text-3xl" /> | ||
</InputOTPGroup> | ||
</InputOTP> | ||
); | ||
}); | ||
|
||
export { ThreeDigitsInput }; |
Oops, something went wrong.