From 08daf8530cd35e8fb53b434bc6d108f3a931d206 Mon Sep 17 00:00:00 2001 From: Ryunosuke Tokinaga <59079411+toririm@users.noreply.github.com> Date: Sat, 28 Sep 2024 21:00:19 +0900 Subject: [PATCH 01/28] =?UTF-8?q?/cashier-v2=20=E5=89=B2=E5=BC=95=E3=81=B8?= =?UTF-8?q?=E3=81=AE=E5=AF=BE=E5=BF=9C=20(#161)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit close #130 --- .vscode/settings.json | 2 +- app/components/ui/input-otp.tsx | 71 ++++++++++++++++++++++++++++++++ app/models/order.ts | 16 +++++++ app/routes/cashier-v2.tsx | 57 +++++++++++++++++++++---- bun.lockb | Bin 480264 -> 480704 bytes package.json | 1 + tailwind.config.js | 7 ++++ 7 files changed, 146 insertions(+), 8 deletions(-) create mode 100644 app/components/ui/input-otp.tsx diff --git a/.vscode/settings.json b/.vscode/settings.json index 48fd8222..8d75b2e1 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,7 +4,7 @@ "editor.formatOnSaveMode": "file", "editor.codeActionsOnSave": { "source.organizeImports.biome": "explicit", - "source.fixAll.biome": "explicit", + "source.fixAll.biome": "always", "source.addMissingImports.ts": "explicit" }, "editor.tabSize": 2, diff --git a/app/components/ui/input-otp.tsx b/app/components/ui/input-otp.tsx new file mode 100644 index 00000000..12adfbb1 --- /dev/null +++ b/app/components/ui/input-otp.tsx @@ -0,0 +1,71 @@ +import { OTPInput, OTPInputContext } from "input-otp"; +import { Dot } from "lucide-react"; +import * as React from "react"; + +import { cn } from "~/lib/utils"; + +const InputOTP = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, containerClassName, ...props }, ref) => ( + +)); +InputOTP.displayName = "InputOTP"; + +const InputOTPGroup = React.forwardRef< + React.ElementRef<"div">, + React.ComponentPropsWithoutRef<"div"> +>(({ className, ...props }, ref) => ( +
+)); +InputOTPGroup.displayName = "InputOTPGroup"; + +const InputOTPSlot = React.forwardRef< + React.ElementRef<"div">, + React.ComponentPropsWithoutRef<"div"> & { index: number } +>(({ index, className, ...props }, ref) => { + const inputOTPContext = React.useContext(OTPInputContext); + const { char, hasFakeCaret, isActive } = inputOTPContext.slots[index]; + + return ( +
+ {char} + {hasFakeCaret && ( +
+
+
+ )} +
+ ); +}); +InputOTPSlot.displayName = "InputOTPSlot"; + +const InputOTPSeparator = React.forwardRef< + React.ElementRef<"div">, + React.ComponentPropsWithoutRef<"div"> +>(({ ...props }, ref) => ( + // biome-ignore lint/a11y/useSemanticElements: + // biome-ignore lint/a11y/useFocusableInteractive: +
+ +
+)); +InputOTPSeparator.displayName = "InputOTPSeparator"; + +export { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator }; diff --git a/app/models/order.ts b/app/models/order.ts index 5516d1a5..68cc67aa 100644 --- a/app/models/order.ts +++ b/app/models/order.ts @@ -93,6 +93,22 @@ export class OrderEntity implements Order { ) as WithId; } + static fromOrderWOId(order: Order): OrderEntity { + return new OrderEntity( + undefined, + order.orderId, + order.createdAt, + order.servedAt, + order.items, + order.total, + order.orderReady, + order.description, + order.billingAmount, + order.received, + DiscountInfoEntity.fromDiscountInfo(order.discountInfo), + ); + } + // -------------------------------------------------- // getter / setter // -------------------------------------------------- diff --git a/app/routes/cashier-v2.tsx b/app/routes/cashier-v2.tsx index 8b111435..5abd359e 100644 --- a/app/routes/cashier-v2.tsx +++ b/app/routes/cashier-v2.tsx @@ -1,10 +1,16 @@ 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 useSWRSubscription from "swr/subscription"; import { z } from "zod"; import { Button } from "~/components/ui/button"; import { Input } from "~/components/ui/input"; +import { + InputOTP, + InputOTPGroup, + InputOTPSlot, +} from "~/components/ui/input-otp"; import { itemConverter, orderConverter } from "~/firebase/converter"; import { collectionSub } from "~/firebase/subscription"; import { stringToJSONSchema } from "~/lib/custom-zod"; @@ -24,17 +30,28 @@ export default function Cashier() { "orders", collectionSub({ converter: orderConverter }), ); - const [orderItems, setOrderItems] = useState[]>([]); const submit = useSubmit(); - console.log("orders", orders); + const [orderItems, setOrderItems] = useState[]>([]); + const [received, setReceived] = useState(""); + const [discountOrderId, setDiscountOrderId] = useState(""); + + const discountOrderIdNum = Number(discountOrderId); + const discountOrder = orders?.find( + (order) => order.orderId === discountOrderIdNum, + ); + const lastPurchasedCups = discountOrder?._getCoffeeCount() ?? 0; const curOrderId = orders?.reduce((acc, cur) => Math.max(acc, cur.orderId), 0) ?? 0; const nextOrderId = curOrderId + 1; const newOrder = OrderEntity.createNew({ orderId: nextOrderId }); + const receivedNum = Number(received); newOrder.items = orderItems; - const [received, setReceived] = useState(""); - const charge = Number(received) - newOrder.total; + newOrder.received = receivedNum; + if (discountOrder) { + newOrder.applyDiscount(discountOrder); + } + const charge = newOrder.received - newOrder.billingAmount; const chargeView: string | number = charge < 0 ? "不足しています" : charge; const submitOrder = () => { @@ -50,6 +67,7 @@ export default function Cashier() { ); setOrderItems([]); setReceived(""); + setDiscountOrderId(""); }; useEffect(() => { @@ -78,6 +96,8 @@ export default function Cashier() { const handler = (event: KeyboardEvent) => { if (event.key === "Escape") { setOrderItems([]); + setReceived(""); + setDiscountOrderId(""); } }; window.addEventListener("keydown", handler); @@ -118,7 +138,25 @@ export default function Cashier() {

{`No. ${nextOrderId}`}

合計金額

-

{newOrder.total}

+

{newOrder.billingAmount}

+
+
+

割引券番号

+ setDiscountOrderId(value)} + > + + + + + +

+ {discountOrder === undefined ? "見つかりません" : null} + {discountOrder && `有効杯数: ${lastPurchasedCups}`} +

))} + {discountOrder && ( +
+

割引

+
-¥{newOrder.discountInfo.discount}
+
+ )}
@@ -159,8 +203,7 @@ export const clientAction: ClientActionFunction = async ({ request }) => { } const { newOrder } = submission.value; - const order = OrderEntity.createNew({ orderId: newOrder.orderId }); - order.items = newOrder.items; + const order = OrderEntity.fromOrderWOId(newOrder); const savedOrder = await orderRepository.save(order); diff --git a/bun.lockb b/bun.lockb index 3ce2aa3f8469fa99740a75b9d394f1a9bec6f05d..ef1ba1efbc1b903e07e08a7fb894356dae6a044c 100755 GIT binary patch delta 87485 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 z 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

4eH&Zbsi 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 BbpZeX diff --git a/package.json b/package.json index 66ca40d7..d86bbcc8 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "clsx": "^2.1.1", "dayjs": "^1.11.13", "firebase": "^10.13.2", + "input-otp": "^1.2.4", "lodash": "^4.17.21", "lucide-react": "0.441.0", "react": "^18.3.1", diff --git a/tailwind.config.js b/tailwind.config.js index 0730ff9c..64699f4b 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -66,10 +66,17 @@ module.exports = { from: { height: "var(--radix-accordion-content-height)" }, to: { height: "0" }, }, + keyframes: { + "caret-blink": { + "0%,70%,100%": { opacity: "1" }, + "20%,50%": { opacity: "0" }, + }, + }, }, animation: { "accordion-down": "accordion-down 0.2s ease-out", "accordion-up": "accordion-up 0.2s ease-out", + "caret-blink": "caret-blink 1.25s ease-out infinite", }, }, }, From c8ea1d8255764f5d891f2f4f1e14eaa154a8eecf Mon Sep 17 00:00:00 2001 From: Ryunosuke Tokinaga <59079411+toririm@users.noreply.github.com> Date: Sat, 28 Sep 2024 21:33:39 +0900 Subject: [PATCH 02/28] =?UTF-8?q?/cashier-v2=20=E5=82=99=E8=80=83=E6=AC=84?= =?UTF-8?q?=E3=81=AE=E5=AF=BE=E5=BF=9C=20(#162)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit close #126 --- app/routes/cashier-v2.tsx | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/app/routes/cashier-v2.tsx b/app/routes/cashier-v2.tsx index 5abd359e..1ab366e4 100644 --- a/app/routes/cashier-v2.tsx +++ b/app/routes/cashier-v2.tsx @@ -34,6 +34,7 @@ export default function Cashier() { const [orderItems, setOrderItems] = useState[]>([]); const [received, setReceived] = useState(""); const [discountOrderId, setDiscountOrderId] = useState(""); + const [description, setDescription] = useState(""); const discountOrderIdNum = Number(discountOrderId); const discountOrder = orders?.find( @@ -48,6 +49,9 @@ export default function Cashier() { const receivedNum = Number(received); newOrder.items = orderItems; newOrder.received = receivedNum; + if (description !== "") { + newOrder.description = description; + } if (discountOrder) { newOrder.applyDiscount(discountOrder); } @@ -68,11 +72,16 @@ export default function Cashier() { setOrderItems([]); setReceived(""); setDiscountOrderId(""); + setDescription(""); }; useEffect(() => { items?.forEach((item, idx) => { const handler = (event: KeyboardEvent) => { + const active = document.activeElement; + if (active?.id === "description") { + return; + } if (event.key === keys[idx]) { setOrderItems((prevItems) => [...prevItems, item]); } @@ -165,6 +174,12 @@ export default function Cashier() { placeholder="お預かり金額を入力" /> + setDescription(e.target.value)} + placeholder="備考" + /> {orderItems.map((item, idx) => (

{idx + 1}

From cae2c8a70eb2d2dff9291d17638b91159f9f6d91 Mon Sep 17 00:00:00 2001 From: Ryunosuke Tokinaga <59079411+toririm@users.noreply.github.com> Date: Sat, 28 Sep 2024 21:55:37 +0900 Subject: [PATCH 03/28] =?UTF-8?q?=E5=89=B2=E5=BC=95=E5=88=B8=E7=95=AA?= =?UTF-8?q?=E5=8F=B7=E3=81=AE=E8=A1=A8=E7=A4=BA=E3=82=92=E3=83=87=E3=82=AB?= =?UTF-8?q?=E3=81=8F=20(#163)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/components/ui/input-otp.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/components/ui/input-otp.tsx b/app/components/ui/input-otp.tsx index 12adfbb1..fc957e3a 100644 --- a/app/components/ui/input-otp.tsx +++ b/app/components/ui/input-otp.tsx @@ -39,7 +39,7 @@ const InputOTPSlot = React.forwardRef<
Date: Sun, 29 Sep 2024 04:54:24 +0900 Subject: [PATCH 04/28] =?UTF-8?q?/cashier-v2=20=E3=81=AA=E3=82=93=E3=81=8B?= =?UTF-8?q?=E3=81=84=E3=81=84=E6=84=9F=E3=81=98=E3=81=AB=20(#164)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/routes/cashier-v2.tsx | 171 ++++++++++++++++++++++++++++++++------ 1 file changed, 147 insertions(+), 24 deletions(-) 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} + + + + キャンセル + + 確定 + + + ); } From a3d9af108cc079d477cf6075e9f4af393c59b9ce Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 29 Sep 2024 05:35:56 +0900 Subject: [PATCH 05/28] Update dependency lucide-react to v0.446.0 (#165) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [lucide-react](https://lucide.dev) ([source](https://redirect.github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react)) | [`0.441.0` -> `0.446.0`](https://renovatebot.com/diffs/npm/lucide-react/0.441.0/0.446.0) | [![age](https://developer.mend.io/api/mc/badges/age/npm/lucide-react/0.446.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/lucide-react/0.446.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/lucide-react/0.441.0/0.446.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/lucide-react/0.441.0/0.446.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes
lucide-icons/lucide (lucide-react) ### [`v0.446.0`](https://redirect.github.com/lucide-icons/lucide/releases/tag/0.446.0): New icons 0.446.0 [Compare Source](https://redirect.github.com/lucide-icons/lucide/compare/0.445.0...0.446.0) #### New icons 🎨 - `file-user` ([#​2457](https://redirect.github.com/lucide-icons/lucide/issues/2457)) by [@​jguddas](https://redirect.github.com/jguddas) ### [`v0.445.0`](https://redirect.github.com/lucide-icons/lucide/releases/tag/0.445.0): New icons 0.445.0 [Compare Source](https://redirect.github.com/lucide-icons/lucide/compare/0.444.0...0.445.0) #### New icons 🎨 - `briefcase-conveyor-belt` ([#​2431](https://redirect.github.com/lucide-icons/lucide/issues/2431)) by [@​jguddas](https://redirect.github.com/jguddas) - `message-square-lock` ([#​2430](https://redirect.github.com/lucide-icons/lucide/issues/2430)) by [@​jguddas](https://redirect.github.com/jguddas) ### [`v0.444.0`](https://redirect.github.com/lucide-icons/lucide/releases/tag/0.444.0): New icons 0.444.0 [Compare Source](https://redirect.github.com/lucide-icons/lucide/compare/0.443.0...0.444.0) #### Modified Icons 🔨 - `loader-pinwheel` ([#​2470](https://redirect.github.com/lucide-icons/lucide/issues/2470)) by [@​jguddas](https://redirect.github.com/jguddas) ### [`v0.443.0`](https://redirect.github.com/lucide-icons/lucide/releases/tag/0.443.0): New icons 0.443.0 [Compare Source](https://redirect.github.com/lucide-icons/lucide/compare/0.442.0...0.443.0) #### Modified Icons 🔨 - `circle-stop` ([#​2479](https://redirect.github.com/lucide-icons/lucide/issues/2479)) by [@​jguddas](https://redirect.github.com/jguddas) ### [`v0.442.0`](https://redirect.github.com/lucide-icons/lucide/releases/tag/0.442.0): New icons 0.442.0 [Compare Source](https://redirect.github.com/lucide-icons/lucide/compare/0.441.0...0.442.0) #### Modified Icons 🔨 - `messages-square` ([#​2429](https://redirect.github.com/lucide-icons/lucide/issues/2429)) by [@​jguddas](https://redirect.github.com/jguddas) - `octagon-pause` ([#​2485](https://redirect.github.com/lucide-icons/lucide/issues/2485)) by [@​jguddas](https://redirect.github.com/jguddas)
--- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/toririm/cafeore-2024). --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Ryunosuke Tokinaga <59079411+toririm@users.noreply.github.com> --- .github/workflows/pr-assign.yml | 2 +- bun.lockb | Bin 480704 -> 480952 bytes package.json | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pr-assign.yml b/.github/workflows/pr-assign.yml index fe2b3b98..3d958459 100644 --- a/.github/workflows/pr-assign.yml +++ b/.github/workflows/pr-assign.yml @@ -10,7 +10,7 @@ permissions: jobs: assign: - if: ${{ github.event.pull_request.user.login != 'dependabot[bot]' || toJSON(github.event.pull_request.assignees) == '[]' }} + if: ${{ github.event.pull_request.user.login != 'dependabot[bot]' && github.event.pull_request.user.login != 'renovate[bot]' && toJSON(github.event.pull_request.assignees) == '[]' }} runs-on: ubuntu-latest timeout-minutes: 1 steps: diff --git a/bun.lockb b/bun.lockb index ef1ba1efbc1b903e07e08a7fb894356dae6a044c..af409e5bb8505f171401f92078ddf6015118257f 100755 GIT binary patch delta 483 zcmX@GT6V`;*$H||WfM1d*5IesozgwVELR%wjV?av= z$Tp2vK9;qQH#w?lirFh*`voBLUK^<5@Ejb^v@y)PJnm}&do7tFtYvoppS z>KW;oO#cwiDl`4o6BaQ>v+WL?Nj( zGbcqqC^aWFu{c$~DAh<0tX)?(18AmhSz?hwkgHL=i+{MEkH4c!ysu|akbjVZZoO`C zc4mQYL1IY;rlMeP&w%&<#}E&oMPOxRsYN(cg}DYP)YK?|?9@%KEKtzRQpioLP$)_T oy9<{m85krPz@Q|kG`PgoB}F2P9n9AVW1p_Si$!s|LMZzJ0DRfB_5c6? delta 233 zcmdn7R`$SZ*$H||HkIXfzy7*4F)wd%$&{)6snv{phU!<1|LnOlbzkGk_+o?5jR7qg zj5{ZJYfnDl&(X}0xt$}Ev9#Xd()S}ryYoFPQn)j-ntg7B=7#LPxJJ2CeP_Iz{k2ce zZN*#9Np??(>$QnLf9XxHWYfviVi~3UBS|wOj}+}?m=@N)_XQ&mGi~4dg8A2Pb|wZe znEoN2Rc89FCoE!&hTA_rVY&XF=|RZ$Q%_ivR2U80bvfC#>vFPh_LhdIW?(1@Dh)1i cbxBbOWd{oxgtAZ9-^HTH4CGB04`W{l0JQX86aWAK diff --git a/package.json b/package.json index d86bbcc8..689c9d27 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "firebase": "^10.13.2", "input-otp": "^1.2.4", "lodash": "^4.17.21", - "lucide-react": "0.441.0", + "lucide-react": "0.446.0", "react": "^18.3.1", "react-dom": "^18.3.1", "swr": "^2.2.5", From 3ec3c0e9bf88c14c864a0efdd52c92454c01ab94 Mon Sep 17 00:00:00 2001 From: Ryunosuke Tokinaga <59079411+toririm@users.noreply.github.com> Date: Sun, 29 Sep 2024 05:37:06 +0900 Subject: [PATCH 06/28] Update pr-assign.yml (#167) From 5fb7595381520b625661b164e4b4eaa0b91082fe Mon Sep 17 00:00:00 2001 From: Ryunosuke Tokinaga <59079411+toririm@users.noreply.github.com> Date: Sun, 29 Sep 2024 13:50:23 +0900 Subject: [PATCH 07/28] =?UTF-8?q?=E7=8F=88=E7=90=B2=E3=83=BB=E4=BF=BA?= =?UTF-8?q?=E3=83=AD=E3=82=B4=E3=82=92favicon=E3=81=AB=E3=81=99=E3=82=8B?= =?UTF-8?q?=20(#168)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #108 --- public/favicon.ico | Bin 16958 -> 270622 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/public/favicon.ico b/public/favicon.ico index 8830cf6821b354114848e6354889b8ecf6d2bc61..9893beba90c47fed1399ebfe8f06090237df814c 100644 GIT binary patch literal 270622 zcmeF42b@&Z`S;BmlW2^8mEL>rz4xW}rT1!yEha&Y2#7V-Sg<2@V?j}4#V&|qi5iU! zH7F9hfJ8+saNpne%$(V~ccM_4{uPhEcKrnp{_~zQ?s@)+WotIR@W)jTzusxU%=cS&8^5Jy^TA&n zT)*$GL+bbafrHO{*QWb~Ep2;Fe820!8E^I-KL4fR6OMd(^4#O^JM0gq-T0T~7hib( ziW^S4@wWTtt$pIT;jg~_PRFeue^%$aUElvUk3VG-fAZ^=`|GxMud~nB-|epa-e)^H zJ+tZKDXU-Dv~0yAuim_T&2t-$yZNcjhhFpO&RJJJym#7V4?0s;Jm5@TalbkEJoVDh zF>B>xPKSwqc7AnmduRV@U7g?6?C$(utsc&S;e*e7$8Q|_Rqf*Zrb=h$*Ofat`&R1c z{PK_v&i~WB9DM$@emk&sPp5jL{{QP$c+3ZXzVhA;f4S(ETduu-!}2FyeQ)afpMTwv zF*Ei&j^$yH#{vI1u>HH;Rp0pUSA8CMb@Q>8J@&8bPhP#@#UrkJ{L7hFJQShtDWYrW zea7VvIeahrcV@>p=3M=VGvnfWo$4)yI=`#Y&G|pkZ4d{%WB;n1otm8rouMaQ>9m>f zXQ%suGn~OEt#o+j5!XHT?NdCZx&>Tzem zbx%6O7Oil8SG{W>bnoUJ@RM2{#yMk_UF&q3e7sX|%mvPfldm@KXUuM$ggG&{<(lJ- zn(OnLv*unekK^SV$Mfr3e0_KB`r(k5Klps;P0ze_?vmBdKEGh)Bi|s;p?6E`F=e@> z?=Wr8zxD}-W3K+@o*66t;Y>dNPN(q9o1M{1uW^R_`4VUHd8?hSqRrn*R=a68h|dSs z?B-N&J<=Jw{CcOugg-e$j=RVidcx&~_e7h675&%zxmm>&fV~@O~bzU zVq4`rz7)roJUq%iJU-d+ZG%Vuy=CF~5B_t_@2`9O)2Ww+zzq?bV`4DI9sW&+7#~WR$&nL}Ubni7ct~u=e&08Dgalh>2ennlwkGFr@7#seyyEi^D@9IasMdsm;n<<;! zO#}8bdOP~MrS%yr?sLYUag(9*E>ll*nh%`k)b23W&^P`%=oy*L!DsY)^zj4b$FqJt ze0swqkLz_BAE@nbb%z6Mb$2R>-;6!=dZ*pEW1ZnAT;>cs_5#DJrd@PT)Op(Zy3fqH z%cMolujO0B--w^FO2dA;hK@ht=@nPq`p0+Q-_o?A;!%FyWrqiT_09Lyk@IKV`}`ww zuU@;$()=vh!ft%$vzy-Mu6)Fqa=~5B@Ds0ay3ah>X*OVvQ=|0==OD>(OXr5h!}JaP zTN;dc)-?y!>*I|0%au-zmctBRhHqD<9;DouBcmho99s87=P_uP zdk<;Y-_U_{q0E{P>39Km7RPugW`q<>!~1yzcXzyXxHi(t9Tyf9tx}r^>gDuO55W zi{F;!IZr(2HmCpZ&vsf2Ug%T{$KwpWSML(Ie1`rjH5y>(zp~Eh@DevI7XKW|AsBh` zN~b;azxXPr*MifW1=l|%yX8V>%HP(Qn1mN5-oK;C|Kjf{idh~YzG!n0I`y6U=D8PN zchZL+eUgtOr!HFTeW8r=>-y8WKrvECk(KAE;J&n#XTsB4r`Ts^PzS`+IZ+VFR z_HD+_@xsUZcQW+vh5>m>zc{G9Q@zQ+oePgye#3JwzSbizFLP5~mgRNXvv==4FTDTh zDB1OEm2oUy0fXn3HP-o1aPO8=2D)adT~^56~*y1|HvM?LnB zhn^a{XV0Erlx3XDk3IW*>?hgwPrbc$`rgbf2}vnXjCqp^5{JTDq!K z=-7`Q_7ZZ=H4GZR=k}))S|9 z(>$?lE9)oz^;W0b^pl*LZAUrC!rxX?ZhVGl&EmP=vyK1N?=m4H@ju1^pX(=|KQI_} z;^iiO0c?l`j*vZIbCZg}&%;C0mH*M;L5%`_g02Imzkc)W_s{+Dr=RxC%hc@4)LdQz z>Q&ag^WQ0=`&Vr4ts8IA@xfJMcj&q;haBpteoi#dDP!~%4Y&P%cYyqg1%JF)xdU@E zlK*4rzt_Ukou0}?8oKCGL;rK+_ZTJGC%%y_`nNEmjw#Fs{&nkJ4^7`ITP~MzFh92J z@F-$(LVPQxBkjEqP2m8MN^aoX>h@%26YuTE##=6v>hUozM6 z+-}nO`^-Ph@EY`g>XF7Szb;e@5YA0@!_)gY6Y}3X2lxQFMg2z1e~#E;p3bsEXSoLV z|9tUv+jAdy>27pZb=K5}pZva!9m?DubIv$OIG21-BA zB>i`qa)Q(U@Uu+rA34c0E_=W!RPJIf$bT=a$SL}b;8HmAPybL4gZ8=n9;?9to?P5 z5xBG)_XlT?>I}&J>pABXXPR=G@QdIN$hQ9P9;^L~&bF_70|(XZwfoN}pMTL8Uv00F zr@PXjyIg?t<8OR4=cwzS_>lO0vE1CN9(Lx*zMri;o&mz;TJ1)M=w5Lpi#uiM48N+^ zZIY=4G~*^F5U@@Hk7+sL_onu*k9Z}y4wL_OmtvrI7=J)6(!YfP{!#J=8n++0^}1W` zou3Q1nIA*u_|WDrcD7aw|6zRkGi1xV?NI9Lh~-f;Td(s3>G_iPHG;31x42gtXW|f5 zm1l^r5-NkdSTKg@!zQsGvHdhaSRnva6jE+wn zFCM;>ncpPusUaHt$BUe*O$stY|AJM+J~N$>$}{LL+rOXu0OQZR#q=C1(0k#jZg@xg zoNfBI>xqA`QvE*Poq7JsW%+nv`QQQXe!8QxdT2d2>5}`4=jVrGc~d2)I>>%KNOdg4 z?kz5qmoxS`bV&|SCh`(OHpXOUv}yplC9O|IYMz4>`NYeBs&m-KM&oT|PXljPrX*W`u#`cFFJPru1&+-F9JM#`vf&*G`~ z3_ar4YC2%9>HPuy%~UUuT5U#VBu9z&RoAnzpE|0?@L=@-8>T)6>VM!2Qm%xbK0vuh z|G@Qtuzzsy*u$QF=e-Y_=jC$xayip5eDbZWGZ$X-=vQ7lADKT}{U~}UKZTwZ-nh=_;N7M#c50|En3qok^|$dcHXmww0EK#hUE&Ou4;6Sr|L(YIG}+I0 zxk~>QM*AMp(W%jN;PxADyKio$@i+hc)Od{c!uZXVPrP;|bWdMwi)%*as~%;Xdek-O zKH0?cGGPZ>T*=lMIb20`e&dvPbJ)$#ID=(-)ARecRWh5K3@mLAu=et!3|HTefuehQ z0MVmxrYFu~*TsF#N%}W7RH(libNsD%E44*O{PFZ_zWr|3{&_l0jZQNGw-3MiUyGA& zf96Ged~SJz?-~8Kmuk|f!^I}f7JX*&y~cMT|0QWu04 zWqN=~_wO%%12kQ5+=V6=&`s;nKIak5G?5$X4r@}uvRmE5FqY@XwH`t?K=F4xq z{eG)V;Bx+%$?=tE-~D6)`o7o42TqZL(zM^~ax3#KZG)R#)i>}^>HHnkhXMy^l`L(`$0{8YH}K-?-@%a`sE;`=b3J%001s+tNvSJd>kTv;7#O=k%Dd*wDVO z{-BcGfxtub53d=y3|AfFVYh5B ze1QBW{3-0EMlHdqXJ2u8kT{)xR$_j#>&Kt=yYS(cZ^ggw&fm4UCiJ4fW-AT-HxL?1 zgBDHy4e8m`+a4%=ztrTuYs|oTxO#thdw>l){!(8(0CSE0k^e*F$AF*icSw5!Q}iox znX#|v-P&Eb4)~Gx*}tVTeZh&VVMdeXpUlm?zQrM&MEp2 z>ofRKJE^B3x&SF&pqo@T6mxB_b^xb?e>e#}to<0;3_DD5;v%0h@UE$vurZ1yF=q4udL6O9;=zpzoQAE3e)!_c|L&4W z8mOqx{-^fL(|&Q=+%@{PPM17B_Pi=LdHXTsy_>$lQ~LXl(msD?Z(rFv?%&;?>HUvR zn<@A}7yXYs*XgES1ED=kmN;`o7o+79D3lMaOwqsK$ew2Gztl{=V{goezu^x96<>u1 zfP?6swJdBl7;oo^qY`FuU2* zSb24R_usiB|FNm?r{Y&?sC1Unf)*;; zGavld2S=*M{vNlFpPJH{ipjK6ZwqiQ8$K^DJ(l+0=%x7C8*5(yFOI?s=}}a#>qMuI z=yKw@w;O*veyd87T|vW(HK4|*kLrKPJ+wW*#;gCgpB~^jMgQWN^}0_n^$T^??+IFu zwzj~KdAHI2tK^(m-!pSz-;)+Y4&A%rikpwEX!IK7eAj4y9kXYqYhNAgt!awMHtIPo zt2S>moG7jDtnN>Ktyi9dulS|=Y9B7*?%*Q__(|-_j8V87|Qze^M43Xtaz$}tb|f;m?| zs(uKkITu`Z(@8( z-UA@F=)bf2869Eld9!a~9KVWPd+JB)s?QRAkHAsG+r)eEfebt8YUlEs{&8wWq1zzl zdDn~YEyKs}rhRaPy1nWxhZnbe|qYr=8 zrlI`ROu-1g(n@0)tUQE1%Khs-?^L4$PtiU|@FJ@lyJf!nb1u=p;bqGCrGHT}wi5T@ zH<+M39wR(JYk(hQ{5f|xEAM$KpC{xaKR~pSQ)cZ` z@DCf@(0*9&@27rzTyL+*B>gulSYYB{#3TF2&X_2j0~#8!_^L8T|DC5Vb|y>DJXriR z8GQ*pSWPy=IN3e$vD1oKYAxPH{kc@glIotKVPglvNrh4S*xzfnWc9!IWqJB zBOjow(SKj%GK@L->JWUTKgF7NtncB+)MAhE@Bn(zfa7cb^`E2Dp_TG>?^ADWouRy* zJ?>sT;PGhXwy>9Gp7x7y5IvumQ&;6Z@ELj2Sh5!ygZ{@XTV-;g;0dwtGwAQ(NwqtT zH}CGOahV=qT08ov4?E%VGL`>hav8N}+HmPtz&2G_SspM}J}Y`5SX%%bS3G;~`nR^s zEN^t2%6yd9H*D4w5AUAo>e&J9(@!d|^B2**#Y6fQ^*QuRr%B(LM(>CAhN@NtyPq7i zI;saq2XF0rk~uSm8mf&Y-zU@q>_TUl^nT(G!{mSDZ1F$Zne`@j7=D(_doyt@w#-o3 zF@vN7q&ptgf&S~`&-!~YpPz7cpn_SEa04)UF|5BlImD~wJuOuVTq z)&CzLUN%xXRA0qb)1`m-6Fi`L>ygGEYV!l&fzu`L7Oq^o<3FEm>y(PzEbI4tzVo}< zN8j+|2kv}+;4?{n;~MH!UD`4%6S5@eXF_K+ROd&WjnBlLdn%{*(3{p9*-H=eWc<}Z zjgPTZQr|)7pB`Y){2=AWv!2L*cTc9=mjCjlh{ujS{YIy|Y8ir{{}2!8?9@_^RQot~2!KlS^I@7VCXyN(b0m0l5bWk;6B-kd?u8a%PN^7zTG z*}qCh=a2^dja)&0X77@0!3~~MvIp3C#uthGPcB_Fy0_oujQ$_iuc%q-Bt8}t{j>Hw ztA5X^rXL!6&e*v_1558%|HAGceylJ(1uuN)+g+pO8(1b7W+0qMHLH z<3<(h0d|I={}J*7QcF8qd#lRB9OD~!+inAHI zMtAkxWB=xNdZXY|%@hxSH_<1rs0Y}ohW;ndKH0ev=ObQB{c8Lg%lL9JCf>)8g=O`6^rKFSk+&OtwDX&T+dBP} z<3}BIs758WUXKpjP(1?Z$4kGI7V2R~&S20y0GU>xy}vqZj3cB6;HRK34*5vb!J%4RkqVZT`ee%tZW}BKGIaNeEHT@S5W5R0zL!uMOZ zebcmr%`cr0+(;z1G6nCl_+03;ZkLIw>p5Gte|yE&&NjU@;^q5rPYvzM+wbtd8owa2 z44Ai5T>G$_H#l7;EedoEcy@Q${JoWrguY>WfT4FxP#=)8K>z4i4di1Pr#vS3Y|u7R zEZjp!>#bUCQ@b6Ole49#6&!Z@*Z=tL zwmf_fZM9X+Pp1igbjVLdpQhJdJRS=D_f!pVoetw7HWofa_y~6SaP{d7vaX7;bXLwB zJqO7BqX!sq|B1?_DOCP@nV^5sVH4@4qgDIIUMi)9JMd=m4o1sX!Y_t?gq=2W*>%p) z!Xuvj`RAX0;ia!!|9kZd?=JP){8KMnWA;u867NcDjk5V3M8NZ(VHik{x#P`?*zu9^6rcjJ4QE?L0f0)9qcv!*)mNd1cRF z-?UWjYCM^h3AmQ|XUg^`w~oCu50Q*O#uoYgqsRQwTj3Y%3*hx(p%)OR$KO<2ds*;z z5HO+^317ot#gMdpi&%hVZ zX6&&M9RRp=Q(sll%nuu1{8#r}m-&qEr=R!y@}xVSdC48?V;=pK4+@V<1|Ioq)w@gm zXW4UxJPF`8S~1fCeU8OPkz4cw=o-SC67c4}p0XX-D>&Nz7OBFVu{9HaA141|FXgtF z9$?yk8D2G3y+FzW{R10v^ZKcOSuFUb3LjthiEm@y4U(T1n!y$rE_kxvu=X5(b@vZH z{3_RZ!0Hz^EpzAgBlp=y+4S*{{^<)2b#+=mfD93IY4jUnX08}sT}fHyp)(V zytbx%f6?+P!0Xndj&csY`5C8UfHf|L?`XfA5z6-`_lI8K*!?qP1B7~j=f3|R*q~qa z%%Xo9eXNS~w#l;5-~sd@qYvRe|JT+z^MY$~IzM>x7hktgeD6EA-j6NQMDi#r{IneK z5#UCRZxi{Yn+}>E>Py|UpoCrzf5ETPpvP4C22YF0AMg#-*EQq=;j0g7U3fRoWgp_M zGfp-=$moGb4>0P2tW9fme)kzX&xQUUz@nRYS7+$}LBS;zzR$Z`3_IM!1Ey&2mde_* zWk1;l4SP=AwdJdwZF5xzIQ^dIpYZzo`zj`!O<1M^|6IC&I)r(||@{Bu6R~aN5nw;a=(pN?+ z2e7Qre=q6&?MA8=O|XnN-^~6-PuH%_Anm0zL%GwHmHWZp2e%mRoPOu}Cv%bhAAe)Z zeDanoU&r1fkBS~%narci>^fvO_ZZ(D{iMnJIat2GN%Glukbfu|KZXAA6AV`D&Fk}X z!wBCX`vXmqUv8}EGTycB`x1TT{WaQ-4)p+2ZUN&Mt~%g3s{4ZvlnMHm50kp(mdcS1 ziZ3RDTdd!KXLZ?drvI(vKYv$}-Z%E#)y~szZpo>4ZX?om? zuLP0j0w1ZWoO$wnO%E{T01a7msfjbNM{!xm{|DX!jy_MZ)oC&HCpR23{#kZEc@*qz z4SWx7IKUZq_U+EGH$M5v&hNgjoGW_3wNJlsiQDIgJ%`K+#rdPhs4@<_a$@^aM?>6{ z{eGyIik6oG{o-F8uG}h08nDUDT9T(~W z2K`&TFxoj+?0@5XSNyVm=fbEwHXcla{w>5$ha27xzh@73dJ{~LKVZsb4>>nH{o0CL zp#S$j|GK@_bhoYPN6$k})sill&+{oR&y&Rwk&!baZ8RQqQ zD(ZEH|EtF@`O-DiKbD&GddmI#U5%2yKllVSH}zFZ7VUR0459zFqETp*dO|<pf6; zAXQroGdY#WSZMD2`<{O=oAm$B&7Ti<_wU8$N51zV>@hE#^gGx5btUx#;_3AG7^Ga< zrt0%oc*aczo?cts#NFiYA1?a$3v=w;rUU00+GHQ~Sh0en)H#KOaE#)A@p=H}g#6d|53b+O#9AuLmh!?mDOdzxd&1M{^;Wy>DC0k8o$GX+80w=W zy$yY8n(8ZF-TcXjZ14c;dzP%;@PeEE$yaJU{P3tcYH7o-tiR9d=+x;!N3nE!uES^D zUU{p0_A_sAsczRvCclh+AVK-M!CChE>@fZhrUy_jE_jkp>yD*NS#iJf%-dULlqUTv$A8}wZd?CGMD{Dji|*gJkM^XJ zZkg`3~T&^2yUm*Lr zvvih74=~Av@!C&g+Qs)4!`ob>fASa1-l+ljL;b8ntZRtR&`YUt&m!B5*u=nNE>Ldu zDW-q8g&#Edms_8H^QWJG-nX=Pz+?a3GS8jsgX|x=NcH4dhFdInmbLGpqsIMbn_Sv1 z@&OG{zmF319oDl(YR}zvW#r^pmZXO&Ml*fAgW*<4rkV;r*OgFKRzVs%{hC#E2y;P5&p$1Bksp`R@f@ zsqrU2ETk92kNFpa1-wIc<#_eE>Z^R$61*iv{}s>aqy8W86!!hHJ-{ZY4k%s^(43%u z!LxyEtX|T&(Z7qqD@Ayu@(f@|4RR;xb#`5hk5_9wOgUBiSL)z29xx}=FHF7@cmT9` z!rz~I>&Ksd%6NUC_UfKh%$^nNOeBYO?>VKZiX-x+=G`oA7}i0pQ1Q)mfo;TO9yD!1}j7oRdi%;O9Mi zf1y6!uNK+;A=zKQn`FP@Re{5!0`dda96wZ{WOG;Ti!xC3>6J(J8vFxQU3iiV{IM|$ zBu|D)&kRc6h5ox}%;?bM{`Hs@>H$U%AZiz4)d1vz{09atM;uX1?j{4zAg*N$O$+8b zo!mS<%oBzxpRyLY-@*^BYoyq)=@}Z<0iel~Z(sMXJ$v?KtXJ5^_dgxy?(1Rfe(9&^ zCqc|V|BTRn(d_YwUOzyz-&(c*UiyU2Iw>!VexRw!ZtkOx=*T$rgvqW4&!m6GK*_HG zs{f-0*mTwZ(*r1858zy&f5zTUbeFB#Ka=09yqVtW>F(~!X6(E^GfYo{12jgnCgKU? zDvpqDNS%$<0iewn-}~?QOz8lp-?Q<_2>r|F4?k}tyTps1$>L4^`l48qp-cJm@k6tB zZZ*%IUB=dzjz~%?{*qUOeQP)*dHQUI3ODph#rpgVR>#_l+UizSO z=H1UeoeBDX>*H-bRo}PQ=5ZtY$?-Baig=M7SN>c?%SpomI6~Ws=aV~QbbpUs1#XhN zH;Nv$E__J#UEn+THcosbo#(jU%h;L^In)^{ov^q3itJB<-alRYx=%R!*4(Cl`TRnC zRwU0-p0JwF%y&3L7Jt%<8a7tnG;$`2${uqZ|-fazVN`$+5l$nphsT5B}n@J?CbC9 zEm--;H@2@AxjyuCHg(AX;Ej7u75OvkNj}do(f1Jf=BUkc%VxtLRIfukJr+**g&(-x zRJp$sv_D6@eMI~|kLygFO8P53z`7|0h<{?N_zSuJg=gK8)AAqK4wl|R{vUf7L3gc( zAK^4p?L|ZNHLE4QV0u{UI$-BsmtKpnRD4FuEw834YK?GMY0v*_Jn(HS#M1bsg73+cj0wO>d9Z z>hlNO+`0w7*Av}i4|s9FyXN3TS3Re=4sgG7&x`LY34;E^`9AMV5BK(A4X3FmSyk1h zy8RK}apnImqH8~}fR|TRERXtmbbt2wb@Nw#CoV`VJ&2lpaE`r1&?SP}>yBsAgN!~U z&(JL36n{NVl?f+0NK4`1fJTt7oP zmW400di*Uy8hIdjA5&&P|^$ zmw#{n${kJ54rFQ&u&189y7~%?JM9M3U(zjq?3j{0!=vaeYI=Z4KP0z+J?`0SB&X?L z!V zeUzZl?lYH|JUr?cC!ep}C5@eN!V8#((G4UI8|c2@9Nf;-Q#I;6!|;O6%FXDagFcA# zz^o^mhE3?M zHvI!r_EzXrc%1X=gW4K?3(x(n^mc4q{C?!PqPJ5s!Fq8$qrHf z5MT!!@Tpc-KLOScelSjcQ0S9;k)cLc7av)8{nJht*%8D|$Er_Co%UmrUSspV`hA`8 zf4w!tp9E)U_ONRn*}3h1-_=f)2dsVl!y~+XJ@bCQ;Q1N6zER(qrjCOC6X^cv`weZV>e z<^N8NH?Vf>)!keB_L!Vr;aN5H(xzqx-5LC1U%epoe)k%I*XSG7Rd32?_irhE7EcNF z09zI50oF(Rzkrj_f2=gb3Nh$ZWkalWU@hqh{l9=iKgC??Y5yX>G-!CSXts^wG}t!i z_RPch1+-q#=E*wpJJ<1;W6A2WVF5AG*{28pK>oEoBmP2+cwd*aY-wWq-B_ ziXkF{(c74pJ@|W;_#b)@dC~Rd7l79UDNh+oJNYhor{zD;@I$Y>wtxloXF;~0_f%0XOMz;ZLcKN+vbe9uK=7Vm}Mx{nH^H-InAr7MlK+DtL41$>HEi)!M*jeZumR>@_3-Xb zxBst+UmoD~`?Ef+)f*w%^@+ejvcF}g1BviI2hDdv`G_yw^B zex70Gp?Sks+o%qQ+z?{K*vLjcO6Qs(dWp5)pPzRmx`s8VrFtXyPY}4z`0NBsWk*#zHa9jSrxGf#Lb_=K)gU2)Nd$}GVy`;##K)*N z{8;seCTDDna>dEFpqHr0y;cn@d8Fh}5r3_w{Z9^(tp@)vd_?aE%5UXoUO`?%EQ$JC z_DUkx1bU%ohFv#w0Ay&odH!+7ELvsHi$TZ!E$wS#R{GYR*MJzH}!wM>$y z&=T}NL-y8@*FL&Udj?lc!~hR1odatYZ_KP8c~amKeop8-UMz%XCv}*9 zL!tNyyu&_!>V!AE#a>>9g z&g9+J?>fo&&d@b0DJGik+!Ec-+{ydxjr}A3l<4}h@;kKJO@3Es#L`%}9$-`=`oCfQ zn-@mnev-M|U$=AooO9ksA252nptmBc?RztQHo8TaAHa98AHXGe5#!1we}UmiviW;x zZJKCrSL`A5{=SF*HL_N~kDlDvrKN>$2iEH8P|pUwq zkG>!`uYdjGc=W$ld-^TDW5dff?uYzFhmW}*ns_~OpZ#8WkJUHqJ4^cv-?6&EB=wvn z1`Vupfe)YygVXpk8pz*FeLVTR?Gztk%ocu;-X0m&`-^Z;x#}8MpM_@_xt%Wk^IYf` zIwqHaXH&;YUo-HC-|RuG%pB0XenTF^Bh7&xtAoAoipCmT?1N|W{7TYO$h~5(jZD5P zQ*(&+T%%)ZzmQ49k+CU7o6z*qJJ!GAh{j{_fKPUO+t3~NLw}$bIW}Un_wYUV4BZ>s zHCdfLgV&opE6Gy&I5bgiJ^7XJ0dOqZTr&9`JhF=X6x4&&={U~F#Fj%AntCwcX8rx> zFAcg*GCVbt<0O^V0f3&sJ>)6}aUAmSIF3Km#9q z#nqGpUsbiZRaBG2b%uu++6 z6pQ{hZ2EX|q}CUCF1gfNxoFUh7an}aZ}{#?Q{PWkr+_!X3#fC)M}Tb!J#qw5^X7$% z`)_y+N5h^|op!1btEHYV_?oB}Yd&~ED7QE4^8?m=pHY9mWS)C`=Cg2~oTL8g1&+Tl z9UlPo#C2rvllMYBd_UPW>{&;AhngU>56F3|O`QyNuh;>`7P$CcXUx)7#uosbS`Izb z)X!5pN4*I=qr|wQ%_W8Jpp%Xh7b*8*r13vy>OH0XT)yD; z+Wycq@(7&u%1mH}9HVxjH03_~$G~r(6?7Bt!S|Nltz5PIg8R6hSZ0CZUHIOxcY#xg z7X-umL-H>amjp)C!DH*QR~Io(d|z#a<7Rwe-3*(b{k<7SI`o$cjv=4vAxE!aqo=1K z1FfCdY4Y)AA1(GSBaVCMP3u(`cZXRwvqr3w>)`jHis_iXo5C&h2E#kC1x#IXFl!SH zKES-S{6*B=`i*Z#drq!>2Mp`VzBD22VM zouAd`(5qQPOJ5l~hi!V^1?{qzG;yXA=xHhgKE`MW^971(zcUpn%|n};2WGluFf z;1kqpvd13#7FSX&JoXIy!0y&sgC3|2{Kf!p7 zy`*^oGiaaO(-2%U8|#ZUe%@Od8<_mpy0U?N&b#=tLbkfq{mK6!*A>4peKYZM00;7b zIOv%bI;NPqCh|jSD)*7PI^du1&^kf4#SY=ST?fNE(7htQgizjC2#MiPu zSDSTDL5C;~izqr)7M^K*0OIQtRfp{M0W81snHLIee^KnM`E3$;Rs`8&EZ+t+vJ%Mcfv<8;K*~OgI+6r zbB(cA&;{uAwD5+f4L!mO=)HsPi5&{A*msZ@0z9M91~ML90$T__Xe-6@!TXxhCp#*( zMco8>PQZ*j3f@aipQZgF@{60=PoAG8JfW8(ITef}#D}0~BQQ2QKQpKBeCWG_ zK1S|!X~bJgbB)SX<2SoPtMJ@?XiQ%x;MPm;6 zcZjiNn*O1E@_q2#S==-FnGgA7aMU)BWj6YL*w(kY ze^=Gr0#Bn?WI^r&OWqByqy`D!+<>FbGdc>ijJ*#1Kuh2PR3k2HsLC-f)xu|WN- z?C*w#!?YWrS9pM%Uc+{f#m^FFe%Aq9BRqs>z(Xt_aq|bQ7yXjVVaHkGtTAfbcC3tz z&y&O>Dw6|IPIv(Pys>mFU}fPNs~&j8Bd^|KegD829$r=cTsJJB8R{9`{KP&t$g_FB z_%^aS)BG6xhYpz=@S-Ok{Y{~5dI82kWV49=)Otd&o~J8;?fSR{9-b;s9-Jpey| zcDNVVa2@xtw@)M4yS$Hgr_Iw+Cp%A_`JQW-(=_n~_`~=! zZVJg$?M;jS0C@@w$ZI065B(jTo%)UD(jS^i=Oepj^5=DpR!HK$?O&N?s$@(l1( zP_x3m9(*6o&*Oz(S@;`VCbwg}a`=p#68+=fU!r}&_wFs)Cw%+2yQ>|3?PJ@m?+;wS zHumQIz)$dDS(z53%y&PF_rt$g%WT6LISKwS7UmDmU@y?$9p4nP#`f;R&rh5iSz`B$ zAl{FRB8JT~vMKZ3uw)$QIwNHdkIrqHi_>2SUih-HyWI2|^k?3W{apP0x#5ceXTT536D)n0v*K6C z4`KrJgSN7sYeTe>8*>1EpkZ`weC2j7oRP&L`c9Ls1t;WagwE_|ctuz|Sc!T8?#rU3ZjPH>( zTnC;Q`cxbN8v#CO@_wZ|+1y`r4Csx6SfTr{-*XLd7aLpQ89a+;l51gjOT4jRcQ7Vh zlZgLv9s73>12E&yVVR!Hy0fQH3;9Q^{6|(#S#iI!;q5K6EdAfU@txD%d4Ke2BX7ft z8^8#g(EY5SWk1hDzZfOIRA?Q7ibbgJqYX?8@{FWwRFu|0|R;!7mEYR z=Y~&@m?nNcn|n1-H8kJ`J`#Lvqvby$SBqRMdjI18z~4qJjz0PL7)76I_@3+mezDHv2HhoWpL}VEU%@n!DWiXlkjq0kmI9etmQY^aj?!(!JfI2YKhk_gq@9RL8=csz|4q zDi~VW69c&P;g{~P^uNrV_p5b-uST=4@Z%E8$+Y}8I)IGulV2rirKH6C}nCU2(SicLuuF;w8u;^3u^O=nXzhIRxFNon-it*-KBh zKI4n{6|nQe{lu_YOw3WT!Tc_{WbGEt@#HwaRgzBu{}z10*gr1bP#)KvJm6vK#cbs^ zw0_);Pki+K4?q6yi=DgbF1+TEZ{4v!;#t5k8Z3+tz!gsj6fZoy!RRZJ7vzYCct8;k zk2bdacd5k}482G%W^a0O@yY+)uTp!{laQWHwtgfdYaQkV#%_?VfqqX9lzz(FX)V7M zxj)b;V@y{@8`_s%LH$8_mib;fAdcTxx#rexht>~Wx%RuSc79v$z0Y=Z;%|5CuVmhT z5njP>+3O_JF+huJmIqJ+l8Co^=bit%Xx;q40^W|kO|Bh&clNSz?2NTL9m#snbO_Dd##{qBYIb??xBUI@gt5 zJXLnO;lCceEYs_4pO4?c@B#V$*cX-BBkU4%|77Qn?ts6OK0oQ|5y`$g+t)+?t(2>V zZ^y!xexR>z{$$h>Z+x_Xz5wQX(_^<}-H&(t@CNd@4Q^@zcZ)_c%jh$UeUV7G;M zRNf}=u1~su!{a#yDyQGnKV^yjEk3~qSo`6M^AU4wslE!aEg^7=s1 zGSR!H?!WQ1(HCfpV))i>wDg#*Gq_fu8qHqwPIYRs54~i%d(6>38=GIYOmpQeMEkuW ze22bNHRmj6a8bP zYV@evu=(}au+~P2HTL}X{`>;Kl72>o%DE|ZKR`5{Lwdk0ESTVLd+(Tsl< zKEMY!$S;Ef4m5w|qc7ibo@=i^Xc&2J>pK0w0~*7pN*p&UJRmG5 zc(=vRvCFSBxh3?9f*+7a4h$Hp;R)i06;&4aSyS&d&>1=*IlZNA?|Eg&9IbZ`<@TcU z#iD=Y9r_@B!c0#e{q3DoMfw}u$CjI@yj~m2r5Etu?tkII)9&85F4FTeocm{JBMEyN znrb$1Zlo^WyJn^Ow{!xV8*1$m^3ADR>a9JV>E}hRCp^yZ1I0!wqE9GU{6~+1rV7-r z5I&1fu2k1mbPtc8Ao)aXXsmwSA)95m*>Bd!{R&R|tTXhC9W>6>2VkoD0GxUEv(KEo zdc!|0{X;M8*`5kLKug3|@E=&ZD{cIU`aa<)egg6pz+1*f9yz^h*rU|+`;?8#oULw< zENxVj>wyt^G(LD>3ZIP%^Junvy5A`_1^pxY(d)c8ii~4y4SQ;@Y54{!iC5vntSD;} zcb=kuvnS}OMSFrm^UJg!(DApfe`~sQch(Ww81m;8CT}|K^J2tLpdaeV=^tnG#4N}N zKlq63vi1UV#(zN%Nb*eR?O|<&ifl8+T7P5->)cp2-$do@vd*QU3&6VzRAWdUnAxjW zw9XhA4>F(L9j&#;JaK8(&*-o*@HE!k%J28;Ss|E=SW?ux1DgNKtxv!Ghg;UYKf^`; z^hJ$jSNVYt_fkvNU-=ryT`NbBHtuG9bw z{Yz2ruIZ`)IR2I=H~;?nC$_qC|LI$q4*fG8_%F|(pA>t(mX?eNf`_~SR-s=czdevao=ZTg!nUk@sO()2kiTD{fv!k$4uWta!)O9 zbI-G)e+IWqj)`I|N@(!FBp22Sxw9-^d89Dg9OH5zV^{O_f6Zu$@=h=dgW%zl(j(s2?*puYG~s zFs|sIE&W2P$Y0Q`(Z&y!J~Bc7mL|Xr zcs+Y#u@@KeVg9vGmI?oWg%4-=Coxp~B*-9mW!~3RTm}ITz;BB|NFDf4E3&|FR!fwwm6kP7l(U1ctHOH zwC}CW;|$Y3v|lRpZ^wq*1TW}4NgRoMBWwfKyUZM6dzSiC`Xn2C*Szc;^XDSG#P2*@ zwB2RWB16B_-Xg;de*hQ4ht=cl-edIosjptV^z|X;2foy$Ut!+HPYKWB(0@kt|5&uZ zdb79JA@%#2J!Hy4oWRSUtbYJGRzvt2q}>z?Ze{6vbaeLrME4KbOX0jr=wH5jzALUf z*?!@Q<)c67cVKFCEIo^QFVn*>AJU8XHvCi<4>e2y-@fGJ8MY=|;d-LCi z_Hr2{TJ!q&%y6F&IYOrFdmi}%0FX;Rq_dBC~hA!au z1^6PB*O4l{<>Gxd&zK&x_&6#&51^-@dWpH=fKE_b{eJtaADzj+R*ncZ|NqsFPygn= za36p7^P=Hc{=1SoQ7HeXs0XOc|MBMk4psgyJUUY_L_el}yS&l9Kc z+_EoIFwNEHqUTW)V&uC%Gfrq1xDeO>%^~f~nZL~;yo1i8(Kp|rOB}4&Vy@0NcgH1v zi{KY6|F>BEkK)_JWm$tv%!NLrtRZsJg8l>wru@RwEABovjWB+){}fK z_HVLdNJK}u@H@0zc-Ae>nl|>6zDqPV%QIT2S0Q+uE?yx2cn&axZs>*CXW{9} zr8vjz$`N7X=wtXx+24jdIi5?*A=<&XW?pu` zp)>O9>6^_SUslHsdR_g@2_C?nKIDF}zX-WJe6FX@=mGTjC)VFUenIMY;cw(x)3XoV zfZusfHpcE}ez|@P{6ALz5B2}L$@c$(|4vrDj@kRqM@+`=yun|3NQL$d@~=lHlCh=o zyJ#I9pn>`){zf({bj&^(rWcNAFwq#`A7f9s*4VA7$YEf|ez@ezkq>9%4DsH}crB0M z{p8b;d)-VuVBsV1;?k~-hjy7u=m^ydz!x}xX~`q(6OjJGJ$`X5e_csi6TGXUxQTZU zK>B}`=>K`~J%Q(0f^TNu(9-nxtJX9gjU@9Ma&~Z}E;{7DBDO{CEA(adR*mD!KTLpTcUV27n+uexCZZjr+sBo4cA8u<%O<5%jV{(kg^!iKCbeVu&K zc4Ln*d&5J&Mpnm>)9&YbKU1sJU2#jd42K60W{C8!SM<8<=%a7vryW9OgqV4~M&nOu=6%RMC zGl?lCqK{;88Jg#yj|BcA>+gm>BetaGZT{AoT2w=)lBKEW0=#R2;sDssZu*6{QkU+> zw*e2N#~5@L4`%!Z9ae5qVD{wBq~2usfNTr&fv)l|+IMq8fByH};TOBZufbE&SSu{)$+&baixlxQv%PhgCb&b{6EhmHS8^zEj} zXyb(^Fdpj9!TD5Z3Lel{I)#@v5})p__*Nnvhj&6}>|<%|h-hQwJASVvo<*K)D*P%I z>|%Xy`2hZb7K(GwV~ai=vBsDGK6mIl(ON*uUC6l;-LU?K*2DXMM%(`zUtJLN&of#{ z9}iOYGY9Ghh-oD|MtB6Y+G@lR#uiGJ2k>re4DWjlpHrU8j7#rNNUJ=9n7x;Wc-IiV zR!8wML$7K0E@IJptnbkg@e>YJKd<_V%^6+F2Y>aBKmT{`@CR!D`l{!OrGNMS-@AVJ z>3~1n^yHQq>IVo;K&yk*<2?x851sTo{I8Ms#=<$j-!uLps$s&0hCWioF?vt6Q6E|4 zc`~w<>)D&098|Bq0i6u-;RnD6!#f-GoEn!082PV!_tKL8-Zij1dx-k**t}cT6TX%A z5#$bS`=J4RRKwJR!RD+$>&M;r#7E!%@Z$lFwGledV@dIY7$4WR26| z^W00`N)T~B@PEdNf0%uH66wu;VafQ=Bj8V#FZ!Vy=$jf}@ZSBdkiK5%TVLm$&6ML7 zYd&V(q-TtizG2tR&-gNR4P#*LoTprHr?J7{2gF;*-NUDW?~vSabRo+dLi{0IlOUK^ zXupbCI8a5iaQRErHAJ7@ta}Akd_=DzU`9eA}CP-W; zug^j*)m06)H}-$$#<$M2^uO-y|IV1Q;(-W`p|3aXKPRSa@&hYympx+Am0jQ(It01| zc8s5QCvy$5YVe;f@l9jEX1w~=mW<r0=owgf%xG=^wnhDJIoCI_9RO5=uB zHD;a>>wt#MI{D;*CC0+O9=exj03XX6EPvp8@-L|C8KXT!2Of2v(@Fin;S2b2tt|l@ zyndBvaLIq?-+lmxT4+Bs*2JzGvHuNkZ<%H3f77SiyW$gY#{j7>fy;La*vfcw;O+4()7<5 zfg5w8FWIP*uQvObbf0musS)g;XHqNJZN_4!ulCkpZv$jE>tbkDG@P!^=3Qg>1LJ}} z@Q%p%RGTnDIZo&b^;8qY{F!$=y~qut{O9<*09bZ&`MIERa(v(abXzw||J%RaUG4B| zAKM<}K0pR{Whb-FUR+4#Z}&!S$KI8 z&mq6a@0`v2q!-sxkKAbU1OJ<;e}Er*!sstr+g@`|F+DOF3-|lAui-!F-`vMN^yVCR zjP`5M8u)pCD(hR~9=*%z2jq+u=>61802@25iblWb%-6`aD8BzCx3Bxx-o1N%Vd31 zN(>^Mb+mI$<_vG-ot#I=KEaQO{}33bKfm}ob7J_Ejr)O9b;%ZY-5)f5#Un5O-A(&^ zzVDU){Mp6;SSM(YIvTHz>lZexQ`^x;r-e6+f!cL^M1JL%-|@MxBX(U)F%px*FMLac zr{E=WHX8l6R$kFO{hsI<_IIAuUA+Rm&tiP&z76FgnXk(htmdH~YQ$wO#G_+-KsI_}ZvV^ZMHSjKS|UMo&^*&-gQM zNhoVAY@+!jfmJJyuhl)DXrF=efZvl3TwIr6>_+~pe`Y-S&;4Dd9B=l_^gG{Vue0)Y z$nlq@wVu)DpUijgI_5h|Hq)S^&oll)U}VRa?7YhR_26UIS?akZm{~Ya7wq2r=<|VI z`rr2L_tg(u_1M?e2MFB`(tcsUHx)P`yQsg7hr4!6vCimzrtV&`<4mtH&mT^mqKj9? z8W*rBP+Z;%n-Kl`_t)`!o<**Gv=|`Ik7xfw59s|ShL(ZYsC(TRUzMhVL-`GH^pRLF z3-Wtw8<2}RP(1}}wHamPR3>#qH!RA^XVddrwv&~?(Do6l9@)9$n_V@$^v~Z*?|k<8 zNFK1(m|8Gzy{q53Lr?70oL2t2=%2NwmOs!z+ST0Q0y(} zU6XX?TbA#I7ak&i$)pSJGW*p6XK&uGmj_(8?u|>V4*=MbyT@MZsmNa5kH2QJY&PUn zs_TqArVnJM*A@R;e^-rEyz$1P|F)hw>v+%Mw;~!Y+2aoSA8_>f#q>A#^Ps~)%B9IP zY%Cps2g&$J++)q;XJZ$rhe4rYxQ*nq0&dabsdgR};f%bjr#_W#+MB%M0q4f2U%S*x z_xA4%n?9awV*sodc0*hFvw&wRFoOq-QY_hzP8|zB7z1&HRL9_V+=l+8OS)~PSYtAD zC;QV&cZ@NfypHxf=zo~vvtIq5yzRKY z?lL+1Vfr5!1<(D?mofBKy}Wx2$*wo|(g!}%^bai$`h!3HpE31Se!F)}{GDvRE;Nk& zU(CJ{?(Sc?W5S*y@pK2jYn1GD@W}y+%iDeAlU>hDUvF#z=>o&Fj}`ll7K3Y!(rU76 z;OBbgJ=o>1#r9ut_1f=0-|bVO| zFTG>^D~@Q~(tEVC`hj0)eE=5!>MIv46+TMLjy+q6;koUQWY(MerpSkpZauOWkKKQp z_@8QR$BUNy#4DixcFJvtM*n@2?ROIPvve#x&Z(h25~!u6S3l#3NBh8#---Q`D_dH+ zE_3eYi7EM6Ph+DCZpZ@W#u+$rP8By2y_e@tQ0@}78mZu~8%N60XJkmjeyY8bY_TxH z_P=@k>la6(dG~iOZT@T|c7nYR7*doobcN$5+g5BBbUl{&}{O`~6nPu&EMLbOb@%k8r{Cu* zn?D)lruAr_cYL?I@)6fQ_EjVgKx;R0aky8FPaJW>j|HN$>jg)T#m4t+%9@osR0=VFJkUpP7eb4pr|60SsrtHyzD(Nekx@9`@a%HnnK z(^~3JhY#P#6TyU--Uau+@PL=jWBtAM^$(AT_yBZ2Yn@IU5SaIxcWPRDe}doa2^ph&3>oY$|>%jAoGxTJj4jCR01V2R&!545yy}so-H-I(w#{Hn>$6ou- zk+JCA`~An;ziq5I;P*BT$Qle)JSA2AiS=YpYSSM(+=DDx{AUdGi7+~d@FZP4w)cZ? z^|iOOU)`IyS8Me(@yZcoShW`PS;NSD|<(&lX5>V)@RlQzDzv@JQdz%$NPQm)a>DmSW?u3 zYStB!-&Z}n`_t|JYvQHzcz-Wn^W1tH2Lz`~&zm&raG)2H3n1K#w?^@PgNK`1*`WFW zB5$NG4H0kiqib3?M5{+a@2V5lyNurJM`ma68p{Xhw^gUZxDxy-6`VD3k|gV-x=lMV z<#+Qw@P^n=Z{>g(->7gP9*)HO4W2c2>8el<(g0;hDzMDNJ;=ry>f7ngxu2@I-x+J3 z-4Kt~qy6Ts2Znd1V*@ZY{5<%9Q`P6VZ}R!81B>U7&)#zAVG%z`Jlx?oeAFiIzj|@q zpS46@1<~&?(HN5b9lnPD#`G%@EkRSM;-2y4G#l(pQOyzjCDB;GnP`0yl6}AR4>X_` z!Ra^o^F2i4t@tD4E3K2^elp3Hg8`eWTI5`@5~%zG*6bYq#4574_>BB$sy-pmy7#%vt+nDD@Q6gu ziTAsa+j<7H!(2J|Z2yJ_CaVW?4+njwvIky1bkT~`dARsGYdCW8)!{x@>It5NOk)hlefnEX zRv&w83NMaC``hS_f-UPAO@@L`=%|b>+WmgMgVyPB(f^3Eo%Y&Gq^0^Nv>bY<;hEiM zE^!L}@Hc0i;tSIy&n;i@BZql6w1V$~ext-Yqs<`^u0nrJ2F&pdGhoqPd5l5Q8u-IF zI*OMucleqg*xG9tQ;#UQn~CP0>%SZOQ*jp4!xueAYd-bT2c0wTes-N-diP!zEe_CG za8GAF;T_eQ4|OKfC*LRS*|l?@!GHWhEehrve#Dr-jachsbx3}Pw(vzJ^M2@`cX@mM znfJlhbc~j*&2{c^`ueQ(;y!8$Y>zAELr)M6K8OD9V)~9(Z#CR$u01*jsP+&21)TOJ z3*iB}wwv;9qV*ez1v~paV`#6QFg(N0ytuBH_C<{s`?JqZ>MU3@mS%zh^9L9FjE8HA z#uSWBlGM0UkePm;4?Mbi@-mY3`+MpC%m3}Fx$v4tBRxT&mvK>h-$#!X#~M3hZP;_F z$y*_}&+poKud{kBdug>Cve4ui;Lqdv<_Oac@&dY|ANd&cBjJl*xoL5Ry*dnk5$<{C z#rO0|oSNcZUEpNz`KOvOC(^CpGhAc&0{PWFlvjt(#}Ds;PjFqk(aG0!0@FU~E9{s4 z!I!b(iRfpk=o?-*d;exUHPsttr2O5)Jp8OFWA6~KPU0O`Y5z^^`C#>Lo}eD?Rv#jN z@33ng*}3h1-_`cgbuxcn^zh5KyK91ZK7Dp=S>21h6gW zYY(mC+oK=v;6Gku`Vg4wgp=-j6MbfE_|HtwxfuQicuxK9lS49C{lODG)BQU%V)heZ zPtiE~ODtH!3+VY-SGr%}S-1F-g$CZj4|?k+vyQyW^hjFeABOPv=Gsdp6TJz6iHU70 z?~1xh>>}tpR{WKD(>tto8a8aKb;#!TmGsK08u zbBqNC#>TU2h_3s|cLKj=Zx!Ic`#JawUUZc_2m*IwjU^l3!vhB&U)1BDH4o?ek95;= zDxZJ+dC#vF{e9g#)1_bXT} zD|O@zm1hO4jLa6zddHvc-$oY_49IP_xFg25YnY^+mIuz+8q8)6T@UyzUR8ZRDY>Tht99EWyej!r5@ z&X^12V+1CC$Hu$*7@vPII+S}2Sd#(j+h%Dk(U^d7w0;KOXZo)D^YCGwZ|!k27V!c2 zJL7}b1M^AJ(b+qh90&5msmCN&j2wtY%KJ0>tqRV@cF15H%{}K_|I9kK^Yn+N&xiK+ zxj7mQr}N!gA8+fSJ%abzJ%U-c(aNPZyfu#8NCk$>>yQTh9dZrG6R>!ak-U2O>!Dlb zGhBVR*=sPw)BM|~A^N}AKmCXG?gr}hlL>rg{MZ0~=^tBRh-&)cjm6acDCY_|CE_RU z-=Xudin-FmwLm@7;RWac?rW_7muIsUjN2ak%{|ng4E@u^5nH9a%5=Zb z_|->9~GGgu*NN=g;a4U>o;23}Nz580eL*MMDJ{Ih82p$<8mLe^(7RmVk4BTSu z`H?QXxS#Q)vJYXbTzz;#jkd-2k@Uj@SeL%i<;hPl^AddVPj-@S@5?{wu_Fvm(V8xh zUD#8;gG|VsAmE?zX9$j=T>tBe>0{95i|_q+e7bZV>;9kj?ES@GZhz(#TNjKxhL*6; zGQJj?GqDbE8+xrSS=Cf|CD@4rwMWLNB`Zy=ha4buMPk6@__BX6v`&8ydYN}pT&RKK zQ23j8mgy;|=cTh|JSUmH-{3y`*JTQ3jHjDqB=qNpPPr!Dm>6fHzUn8LgzV>;=tFIW z7xxhvUMqfLdWegT{f^Ilov|0dJK`28=xgb$buQd%@_m&1j7+m|fhJGZzTSKG6yMi7 z7A>dx{Znsko#pid4pZ(yG`%rnu!0xhD->QD8rB&-fml!F#)C}HVPe1>)Epb%vhImo zFPsTdcjUP(lz;5j#q7F7I~!V4yny>M0V7~DTshIn*y_-Bq5AY4q+B{LY=CjH`~jUG4cqPvZ)?j0BO-!p}MT%i9Z>gN}Y&mp(5 zDW)g}>c(R?oZ@|Eyi?^{sG?Y>cf6^>t}NWgT9E7Omi^G;34ed;E#>?C$}b(KbKPTa zY?()#!LAQ-Z-kE+a#?_HDr1E2P?ui#*T4JHk-=ST6w^mYyd#x&#<~amNGVPyK29Iu zI^rGhGxzhM)xkb{Bw3u`3|+T~vv(t7t*5$)=_^9M9QVD6KEvC{RvUR-7FBG#-9=hy_SkEs{-vg_b>ZjtiGoitgZhQKTpML&%-(+Yw z-D}sqy>)sqKY-|e?DFdju6XUo^0WrA=7NsfME!~V(g->Lc`xj(6!DMCu8Z~jMBjrO z)W4ATm#m+z82xuHPXFlE$>bBd`2zG$eL+0>XI}hHU!n2p&r8miAKnEGj63Bz=OEo* z5%7(5z-LmZyg2vzV)t)&d&`V;={ec^_h=unC95~Q;MM_H@3z{*s;8qd+T&AG<7a+y)fE4V}2Jy|3|m3o?_h( zyl@;c!3T}PKgls5j!GU2xCD=kHeSBx_twJ>i|Sh_i};gh%*cq2s$FE>7AExeJZ1GW zFS2iEvh*9o_5b|f^I>lKhcD5SCzE^iC4y5ne~0fSnKJpJ!$hAA))}^Q@|RTX%mMc;qzU&8wR~84(06r}~U@ z?tk%qcYhG_yxHfiLh1m*Ut*2M4=B9#LnB;cB0gdSk@ zF{2+Y@x)lN((f4HL+sP#jsJ!08vi~2yzhfc0Pc{&hR8d8AJ;JRf;h zhc(hVW&>W?d?&QpoL(%@f3m%RM1xkpD?IaNldsfy(juqLs3VPCMLk4c+0)d?*j_ho zThL2;$^0EF|A+Y`lS}MiQ=>o)PX5#5fsm>k&s($Tdf9;mh*d zi_5NSw$4UpQ@(T!*%V&c3e9$u-jNRdTOA@b9#j?c|32^vfS|j=uh} zkF}R?Ez3i*XhV$Z7d0j8}yi9(Z{O0Ue;QDaTGET6?xW&? zT`ZWE_wUh>S}Bjltv5nb+RJO#hhOYy8w9Op>KUtF*tBetbQFst(EsG~?r^F!EvS%o z0Jw@=B`(-s@hf~`z{J8QTW1?{Gr2`;$yeu)Nc} zogaFCHPx+7z4)GD`%>cbTl4(er(}w5gL&V4k>8eSo?zsPT=kfG!(Gu6eKY%lI#b@e^1|5CA zdZo=aaOZu=(iidD(7t~~IVgT<+|PApzS4v63$#|hFnkf@yvFiLltucEH)hihN%R*KAn7ckiC8)c2&zBerh)riu0s-mbpk5xIfBYxla12a}5Q8}tbO2j}T~ zPJcr7uNrjh1txC5J|V>DpcC)|`gR{iXAp0orwIF94PA7p>66+-_BFLwJewm~y0>cp z&*(rNN~-yc@%WbOH_N+5FTKWT+Hbb82N-v`<^!y~Y^;7q72mG`nmP2EM|OO?ZD-ST z=`fr3Kl$cIv*BZQ-vApEY^A-}^8VkFabUv>gb&c1!F}oKRiuB{QeU2i>UT+x74}VT zB{|9dE6vphvax!8)K&d2dx6sf5u4rKAB0Vf{I8~dtMpuk2l0ASEgBP$bQB(CD zwf0h~{LAvOTs&j*w^e>MbG9&pR`8#%d-KDL_wy?a-f+d^udQ_Z0_LvNp4!4CQzHa_ z&&PdZ!$9%@{D%gS6Cqh%R1+BbTYKo~H+;vLq7nE>kGZFq-rE*WGkvyvfL@6$RX+f4 zLuQnhd{SUqU-?kR$LHd!(8|ikUtLuiv{@SO`2NSAf1|$PFS}!ctS$Xs%>L8D?K}@C zsSD&1OyEJ-Ce$?GTgWu+yPprPa8MITJtTXH!}rJ`3PQih2Atv7Ri&>@RBnvh=7&ZW z-@fjZ-9P@cUun>0X}x31SO05K!WSSs8uF)0jLgcE+{(tOO0M2eJR)BZ+Mpawv3QoZv+;wX6I}DS z$tNbathIb0HCl}b#SX*^3_lRRg4S|LwlEg>XkW#8-2T*ASIB>>z8M=gZJkt_G+Em3 zx^3e-r@DOs_yf^@=w(yUbAvKXyFoo4-Ie-g_B=N>y4KH2qowt?;RTW*jDtO&#%Qn6 zJ_}DbwdeKJM~u94;?3443SY2?^_`fr=45;*vPCn=Gk7=kl=OCR!-)Le)f?YBt+eSe z8}GaDp_gxGzIOhsA@S|z>J^{o0lDo5usj4B`CSd=Gb@f$sF)*q9DF=so`NghXV*Wd zGw-%}RPY0yOYD@qC-$hKhZz0UI;vkHJ(bv7yxG9H4sp_k^260ro+EV$mS?ax9Q2C3 z4nl_jw_B-yH+GC0PSC<755IIrHfS*$@7yKd#>uNUJnzm80wyyqy-%_4$p+>Y&+_M7 z<_F5t5q*%q)MnICrY{+`o#6$dKlbW1eM2)y^Kp4cl$8y*#A!NI(Wu- z^;I1F$BUdU>bcvnhxUuq9GPpXu?ON7jkRB;*(1v3>xTxG-m(6L-9P@ge>P|_TkrgQ z=dRjE-|*xI#rg+pEr5M(#k!3@ARiaZd0!U%8QBPJaHfwx@^y%6*XerzeMaaF{lJC$ zacMUjJOh3ag=a8kaue}W)ovHU&1Cohu&LLLeU9!kyvEKO+&}h)$3OgX=eKpTMTgmZ z@4KJv=(u3zBipBY`i4z9=Qh)eD$fIQS@uU6o9G{#0DMPhuGDCNsU1Q$pl>OAFjMnn z{rK6o7c3q6Ih!#n|B*e%`fK0#YO38}o{98R_Uf%Ao;c;g;`zn>_^N``kU#l_QK^R;b(nU<@85f^q)$D72UZx|8t{X#JoQ4)gcfj+ZG zd%Lm6IwKZem2lr5OFy}AMjznlL=Oec$2)JntFF0CIQzC@xxL^xxcxWDTRNAya@PVa);|0RuB&Adw{y z4IzXq5Ee(toA0}w8y`)8&`F2a-G{2KZaUqs?>qOL@0@#9Z4i9mWS$-1wsRm+K?C)mQ`;>k1bVO2Wh}GoHit7OeOtr@V(z8D>mHmU1&&AG; zEDK-BXIu+6XbJ4TSf0X)W3-S z0Ai;(E7&>O^f}m6_J}=9^RDiRuT>&en`xJsm-9f@@cfrwZb!b6+BxMDSm#3 z^u9et?LV2}kDf?3&3S@4_QErltrkUkh z_MAPFmHVnQcweq*vmIDBh}z+U=KcZPYsLmxTcT%;ici2Zu;6j_&qePTbIh%>WuN_Q zLN>-FTrRlok#&5PjZN$yFmLB~Uz6_Ud|~!BW+zw^j9=-25{_4}1A^QHaX`h?U_p1I7i0ac%c^FWUYOH>W9}P+qf%C%A<0)AA z1zBI>u{zQM{yJyVo;crT*vFMpFF;*FD46n*(or4MK=q)~ReO!_{55O0o zaX_61kT3KG`Zajf=^%N-Zj1x$JECj=)C@VRZjf_%d_UiY{`|2?894xQLi26&AaZ*2xse1SHC{<1G|h}=oDWdd5U`f%iYJ8 zy4!3HN94__-`SgzF@)y<`xv3l&1P?C&KwfIfcyCcxTlTK*Vn$cwzdZyeI_r_-j7K+ zA7E9!mv#MSi)7?ptM`xaggrbDm}>`YzCp~HMjavJgwz+b8(m%R$@*Q3v4(%L(z9NH7GY4oR(m%*rvK|wcs98|-4e(*lU-Exz;{xTs zaqssYO8(i4-w)^fPa_f{)3fuvMV1~cORh|#-6t*t{&m%!bozG8`NPBk=8@A+*ya6s zMlJNCr9CM)U~QQ0)vKIVd#r5=ELPapB<)w|E{J#jwJm3dm${&1L;fBk2#l`$vg z&_77KkGUNszM|r~;&;Fwokq_#UTof1aKIIEhv5JFz1J&7?bin0*Z;@8=AP5Pw>swd zoPry-yhMH;PJY~lUT3oNo6OvPIGl9^ck~tjkLX$g>WR){=BU1dswX9MjMNue$0p!j zaLyKUyz8_cWOVyz@jd4Z4pjTR-I%p4Dg0b^UK4lWW4Vv=;n#QPE_DsrbRdp;;a)hv zTtd!~uJ=ZO@(b{+DRIL`hXbTN_u#1)+Zqj!&(UMzQYH7L+5X&9?(Nh~@$t#$vbu`A zluusVk$y!*MwOUTKl*+2O>8oI58LAa<)6I8*mCdCl|~cvAFNupqvAoSHLfHapst5s zmU910{Vql`%-?zo@qYSlmE2>GO3rbx;gsBCFDGD6<4$Gx84}BvTn6L**vEJwdJYBv z2aYjTa2F1MuNW_2jLC2ICZo;JY05|NWDVOaE3pYG9t%on3;D0r(pSw1V|^$0fO}z< zdA>Hu{ju1S7Xl=ncYmIDM}1!2d-~N2r8*N7AzuKYY_S}Qui5#Yd5P$9ZwI2pCCHu%g7t|#R8mRbqs;Ffj`c-Z-#uOWTKJJ2;&9Jws;yd zO&C|yvP1ir=VL440@tL@?xyw9uJ@|b-l+L%U&Fh{NTc2G87gk-zu?~`I7k|l*00MZ z^EUf_C}=rjYeMuc(Dur;8`aCZPx5_{b!YRyPq~G4w;w$_W;*(ifPV`$7Qjb!mt8e71+51IpcDIJmUOpqi;UKRQw{I#BawgD-*~oRSC#K_2QE~@83J-gdDHk%aTo!) zrfe6>H-F=a=itM@Z)#lPdw;CePWk9-w;Eu0<9x%IXm{k{`Oe&+YAm$fb1!)(4M-d2 z=#W;R+4YY>yKh0m%-m~roxNXjeZd50@jzx?;Y-oS&j-)maU}fRmAkVBF0|=2#;B?1 z?9T}b4`?4C&q&LSvW@W`XPoq%5vKAGUxz+z?mHGej)$w7MV_OdZO|37(ApbM;ap8D z(D}+*R?GBEJfTmb*xq1%9rbuMU=Qt*I+(xNsM`pm#ei|f>mxojx{d!r)lbt$PhHOb zTN=mc|6pvSw0q=Po;0Amb5`ys&}Pn#!{OBP7eO;;?T1`fWc^X`K*5clpN7A{XKh@{ z%qg1=9QyOJsB2p1Q5H(`A(qGvm0Gm}A+tDw!J+86mO4mi3{( zN8bdmMLo`Imv&ayzptOURxRti2kLj;6Yp)><^yr7V^7RYz&&T>7uI?dG?(8LS+^<= zSr##%7Rp~D45=^in53nXF@A|E0(z5cJ$DuZ9V`6IPf~ccm)3zLp ze?M&B+2PBh3owVSNXcNauBE=E9O_(H{*C&Vv+3FQs5D!G@9}T^3_r{7&`-f@Xx!m- zbv*?97DfDRu7PV=7?m8h32WmTF{inCKGrOKGexGXC{ymTCd@o}{PNWs&!4%J-z+&j z+h<=&MqtE|)8p61r_5dqy$gS1#Eh+n_M)yiX~O!r;~%WspZ524`_pmmz6g2GgDp_N z2Ve6&{5_qY;b-|Bes>P8!E1%#n!NVDlna5$>6d)?n>qMxuEAaC>52DIMg9o|gaSeV zp@2|8C?FIN3J3*+0zv_yfKWgvAQTV^2nB=!LII(GP(Uak6c7ps1%v`Z0il3UKqw#- Ius8+&4@pk9YJpaf7TUbu)A5fv|h7OMfR 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 EKbET!0{{R3 From 2032fb982c230ec28ce4f39a2ead21cccdf4a2b8 Mon Sep 17 00:00:00 2001 From: Astalum <131961897+Astalum@users.noreply.github.com> Date: Sun, 29 Sep 2024 17:49:51 +0900 Subject: [PATCH 08/28] =?UTF-8?q?/casher=20Order=E3=81=A7=E3=81=AF?= =?UTF-8?q?=E3=81=AA=E3=81=8FOrderEntity=E3=82=92=E4=BD=BF=E3=81=86=20(#15?= =?UTF-8?q?9)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/routes/_header.casher.tsx | 112 +++++++--------------------------- 1 file changed, 21 insertions(+), 91 deletions(-) diff --git a/app/routes/_header.casher.tsx b/app/routes/_header.casher.tsx index 0fa05ac9..ab6b9c2e 100644 --- a/app/routes/_header.casher.tsx +++ b/app/routes/_header.casher.tsx @@ -1,7 +1,5 @@ -import { parseWithZod } from "@conform-to/zod"; import { AlertDialogCancel } from "@radix-ui/react-alert-dialog"; import { TrashIcon } from "@radix-ui/react-icons"; -import { type ClientActionFunction, json } from "@remix-run/react"; import { useState } from "react"; import useSWRSubscription from "swr/subscription"; import { @@ -25,44 +23,28 @@ import { TableHeader, TableRow, } from "~/components/ui/table"; -import { itemConverter } from "~/firebase/converter"; +import { itemConverter, orderConverter } from "~/firebase/converter"; import { collectionSub } from "~/firebase/subscription"; -import { ItemEntity, itemSchema } from "~/models/item"; -import type { Order } from "~/models/order"; -import { itemRepository } from "~/repositories/item"; - -const mockOrder: Order = { - orderId: 1, - createdAt: new Date(), - servedAt: null, - items: [ - // { - // id: "1", - // type: "ice", - // name: "珈琲・俺ブレンド", - // price: 300, - // }, - ], - total: 0, - orderReady: false, - description: "", - discountInfo: { - previousOrderId: null, - validCups: 0, - discount: 0, - }, - received: 0, - billingAmount: 0, -}; +import type { WithId } from "~/lib/typeguard"; +import type { ItemEntity } from "~/models/item"; +import { OrderEntity } from "~/models/order"; export default function Casher() { - // const total = mockOrder.items.reduce((acc, cur) => acc + cur.price, 0); const { data: items } = useSWRSubscription( "items", collectionSub({ converter: itemConverter }), ); + const { data: orders } = useSWRSubscription( + "orders", + collectionSub({ converter: orderConverter }), + ); + const curOrderId = + orders?.reduce((acc, cur) => Math.max(acc, cur.orderId), 0) ?? 0; + const nextOrderId = curOrderId + 1; + const order = OrderEntity.createNew({ orderId: nextOrderId }); const [recieved, setText] = useState(0); - const [order, setOrder] = useState(mockOrder); + const [queue, setQueue] = useState[]>([]); + order.items = queue; return (
@@ -73,18 +55,7 @@ export default function Casher() {
); } - -export const clientAction: ClientActionFunction = async ({ request }) => { - const formData = await request.formData(); - const submission = parseWithZod(formData, { schema: itemSchema }); - - if (submission.status !== "success") { - return json(submission.reply()); - } - - const newItem = submission.value; - // あとでマシなエラーハンドリングにする - const savedItem = await itemRepository.save( - ItemEntity.createNew({ - name: newItem.name, - price: newItem.price, - type: newItem.type, - }), - ); - - console.log("Document written with ID: ", savedItem.id); - return new Response(null, { status: 204 }); -}; - -function mockOrderInitialize() { - mockOrder.items = []; - mockOrder.total = 0; - console.log(mockOrder); -} From 14d407d5bfbd0d5511631a52c39fbe0e7e9290ad Mon Sep 17 00:00:00 2001 From: Ryunosuke Tokinaga <59079411+toririm@users.noreply.github.com> Date: Sun, 29 Sep 2024 21:51:51 +0900 Subject: [PATCH 09/28] =?UTF-8?q?/cashier-v2=20=E6=8C=87=E5=90=8D=E3=81=B8?= =?UTF-8?q?=E3=81=AE=E5=AF=BE=E5=BF=9C=20(#170)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #128 Closes #125 --- app/routes/cashier-v2.tsx | 143 ++++++++++++++++++++++++++++++++++---- 1 file changed, 129 insertions(+), 14 deletions(-) diff --git a/app/routes/cashier-v2.tsx b/app/routes/cashier-v2.tsx index a5452737..2ea0740c 100644 --- a/app/routes/cashier-v2.tsx +++ b/app/routes/cashier-v2.tsx @@ -1,7 +1,15 @@ import { parseWithZod } from "@conform-to/zod"; import { type ClientActionFunction, useSubmit } from "@remix-run/react"; import { REGEXP_ONLY_DIGITS } from "input-otp"; -import { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { + type Dispatch, + type SetStateAction, + useCallback, + useEffect, + useMemo, + useRef, + useState, +} from "react"; import useSWRSubscription from "swr/subscription"; import { z } from "zod"; import { @@ -25,6 +33,7 @@ import { itemConverter, orderConverter } from "~/firebase/converter"; import { collectionSub } from "~/firebase/subscription"; import { stringToJSONSchema } from "~/lib/custom-zod"; import type { WithId } from "~/lib/typeguard"; +import { cn } from "~/lib/utils"; import { type ItemEntity, type2label } from "~/models/item"; import { OrderEntity, orderSchema } from "~/models/order"; import { orderRepository } from "~/repositories/order"; @@ -39,6 +48,87 @@ const InputStatus = [ "submit", ] as const; +const ItemAssign = ({ + item, + idx, + setOrderItems, + focus, +}: { + item: WithId; + idx: number; + setOrderItems: Dispatch[]>>; + focus: boolean; +}) => { + const [edit, setEdit] = useState(false); + const [assignee, setAssinee] = useState(null); + + const assignInputRef = useRef(null); + + const closeAssignInput = useCallback(() => { + setOrderItems((prevItems) => { + const newItems = [...prevItems]; + newItems[idx].assignee = assignee; + return newItems; + }); + setEdit(false); + }, [idx, assignee, setOrderItems]); + + const change = useCallback(() => { + if (edit) { + closeAssignInput(); + } else { + setEdit(true); + } + }, [edit, closeAssignInput]); + + useEffect(() => { + if (!focus) { + closeAssignInput(); + } + }, [focus, closeAssignInput]); + + useEffect(() => { + const handler = (event: KeyboardEvent) => { + if (event.key === "Enter") { + change(); + } + }; + if (focus) { + window.addEventListener("keydown", handler); + } + return () => { + window.removeEventListener("keydown", handler); + }; + }, [focus, change]); + + useEffect(() => { + if (edit) { + assignInputRef.current?.focus(); + } + }, [edit]); + + return ( +
+

{idx + 1}

+
+

{item.name}

+

{item.price}

+

{type2label[item.type]}

+ {edit ? ( + setAssinee(e.target.value || null)} + placeholder="指名" + /> + ) : ( +

{item.assignee ?? "指名なし"}

+ )} +
+
+ ); +}; + export default function Cashier() { const { data: items } = useSWRSubscription( "items", @@ -56,6 +146,7 @@ export default function Cashier() { const [inputStatus, setInputStatus] = useState<(typeof InputStatus)[number]>("discount"); const [DialogOpen, setDialogOpen] = useState(false); + const [itemFocus, setItemFocus] = useState(0); const discountOrderIdNum = Number(discountOrderId); const discountOrder = orders?.find( @@ -79,12 +170,35 @@ export default function Cashier() { const charge = newOrder.received - newOrder.billingAmount; const chargeView: string | number = charge < 0 ? "不足しています" : charge; - const discountOrderDOM = useRef( - document.getElementById("discountOrderId"), - ); const receivedDOM = useRef(null); const descriptionDOM = useRef(null); + const proceedItemFocus = useCallback(() => { + setItemFocus((prev) => (prev + 1) % orderItems.length); + }, [orderItems]); + + const prevousItemFocus = useCallback(() => { + setItemFocus((prev) => (prev - 1 + orderItems.length) % orderItems.length); + }, [orderItems]); + + useEffect(() => { + const handler = (event: KeyboardEvent) => { + if (inputStatus !== "items") { + return; + } + if (event.key === "ArrowUp") { + prevousItemFocus(); + } + if (event.key === "ArrowDown") { + proceedItemFocus(); + } + }; + window.addEventListener("keydown", handler); + return () => { + window.removeEventListener("keydown", handler); + }; + }, [proceedItemFocus, prevousItemFocus, inputStatus]); + const proceedStatus = useCallback(() => { const idx = InputStatus.indexOf(inputStatus); setInputStatus(InputStatus[(idx + 1) % InputStatus.length]); @@ -118,11 +232,13 @@ export default function Cashier() { const moveFocus = useCallback(() => { switch (inputStatus) { case "discount": - discountOrderDOM.current?.focus(); + document.getElementById("discountOrderId")?.focus(); + setItemFocus(-1); break; case "items": break; case "received": + setItemFocus(-1); receivedDOM.current?.focus(); break; case "description": @@ -158,7 +274,7 @@ export default function Cashier() { return; } if (event.key === keys[idx]) { - setOrderItems((prevItems) => [...prevItems, item]); + setOrderItems((prevItems) => [...prevItems, structuredClone(item)]); } }; return handler; @@ -265,14 +381,13 @@ export default function Cashier() {

{inputStatus}

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

{idx + 1}

-
-

{item.name}

-

{item.price}

-

{type2label[item.type]}

-
-
+ ))} {discountOrder && (
From f56c93a347d634b3f1bf2868bb82cd9a00a23547 Mon Sep 17 00:00:00 2001 From: Ryunosuke Tokinaga <59079411+toririm@users.noreply.github.com> Date: Sun, 29 Sep 2024 22:46:18 +0900 Subject: [PATCH 10/28] =?UTF-8?q?chore:=20=E3=83=87=E3=83=90=E3=83=83?= =?UTF-8?q?=E3=82=B0=E7=94=A8=E3=82=B3=E3=83=A1=E3=83=B3=E3=83=88=E3=81=AA?= =?UTF-8?q?=E3=81=A9=E3=82=92=E8=BF=BD=E5=8A=A0=20(#171)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/routes/cashier-v2.tsx | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/app/routes/cashier-v2.tsx b/app/routes/cashier-v2.tsx index 2ea0740c..77270c7f 100644 --- a/app/routes/cashier-v2.tsx +++ b/app/routes/cashier-v2.tsx @@ -232,6 +232,7 @@ export default function Cashier() { const moveFocus = useCallback(() => { switch (inputStatus) { case "discount": + setDialogOpen(false); document.getElementById("discountOrderId")?.focus(); setItemFocus(-1); break; @@ -243,6 +244,7 @@ export default function Cashier() { break; case "description": descriptionDOM.current?.focus(); + setDialogOpen(false); break; case "submit": setDialogOpen(true); @@ -330,8 +332,7 @@ export default function Cashier() { お預かり金額入力欄にフォーカスを合わせたまま商品の追加やクリアができます

    -
  • 商品を追加: キーボードの a, s, d, f, g, h, j, k, l, ;
  • -
  • 注文を提出: Enter
  • +
  • 入力ステータスを移動 ←・→
  • 注文をクリア: Esc
@@ -379,7 +380,14 @@ export default function Cashier() { />
-

{inputStatus}

+

入力ステータス: {inputStatus}

+ {inputStatus === "items" && ( + <> +

商品を追加: キーボードの a, s, d, f, g, h, j, k, l, ;

+

↑・↓でアイテムのフォーカスを移動

+

Enterで指名の入力欄を開く

+ + )} {orderItems.map((item, idx) => ( 備考: {description} + + Tabで選択し、Enterで確定 + From bd9b5bb05166d75ecad82134990143deeb5183aa Mon Sep 17 00:00:00 2001 From: Ryunosuke Tokinaga <59079411+toririm@users.noreply.github.com> Date: Sun, 29 Sep 2024 22:53:48 +0900 Subject: [PATCH 11/28] chore: fix (#172) --- app/routes/cashier-v2.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/routes/cashier-v2.tsx b/app/routes/cashier-v2.tsx index 77270c7f..3e604649 100644 --- a/app/routes/cashier-v2.tsx +++ b/app/routes/cashier-v2.tsx @@ -328,9 +328,7 @@ export default function Cashier() {

操作

-

- お預かり金額入力欄にフォーカスを合わせたまま商品の追加やクリアができます -

+

入力ステータスを移動して一つ一つの項目を入力していきます

  • 入力ステータスを移動 ←・→
  • 注文をクリア: Esc
  • From bdf3387dc6459faf034475ef69cd383cb0b4723c Mon Sep 17 00:00:00 2001 From: Ryunosuke Tokinaga <59079411+toririm@users.noreply.github.com> Date: Mon, 30 Sep 2024 20:45:55 +0900 Subject: [PATCH 12/28] =?UTF-8?q?/cashier-v2=20=E3=83=AA=E3=83=95=E3=82=A1?= =?UTF-8?q?=E3=82=AF=E3=82=BF=E3=83=AA=E3=83=B3=E3=82=B0=20(#174)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit close #173 Atomic Design を採用 --- app/components/molecules/ThreeDigitsInput.tsx | 28 ++ app/components/organisms/DiscountInput.tsx | 30 ++ app/components/organisms/ItemAssign.tsx | 96 ++++ app/components/organisms/OrderAlertDialog.tsx | 70 +++ app/components/organisms/OrderItemView.tsx | 51 ++ app/components/pages/CashierV2.tsx | 275 +++++++++++ app/components/ui/input-otp.tsx | 2 +- app/routes/cashier-v2.tsx | 435 +----------------- 8 files changed, 562 insertions(+), 425 deletions(-) create mode 100644 app/components/molecules/ThreeDigitsInput.tsx create mode 100644 app/components/organisms/DiscountInput.tsx create mode 100644 app/components/organisms/ItemAssign.tsx create mode 100644 app/components/organisms/OrderAlertDialog.tsx create mode 100644 app/components/organisms/OrderItemView.tsx create mode 100644 app/components/pages/CashierV2.tsx diff --git a/app/components/molecules/ThreeDigitsInput.tsx b/app/components/molecules/ThreeDigitsInput.tsx new file mode 100644 index 00000000..3c0be6a5 --- /dev/null +++ b/app/components/molecules/ThreeDigitsInput.tsx @@ -0,0 +1,28 @@ +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, + Omit< + ComponentPropsWithoutRef, + "maxLength" | "pattern" | "render" + > +>(({ ...props }, ref) => { + return ( + + + + + + + + ); +}); + +export { ThreeDigitsInput }; diff --git a/app/components/organisms/DiscountInput.tsx b/app/components/organisms/DiscountInput.tsx new file mode 100644 index 00000000..05ca5599 --- /dev/null +++ b/app/components/organisms/DiscountInput.tsx @@ -0,0 +1,30 @@ +import { + type ComponentPropsWithoutRef, + type ElementRef, + forwardRef, +} from "react"; +import type { WithId } from "~/lib/typeguard"; +import type { OrderEntity } from "~/models/order"; +import { ThreeDigitsInput } from "../molecules/ThreeDigitsInput"; + +// 割引券番号を入力するためのコンポーネント +const DiscountInput = forwardRef< + ElementRef, + ComponentPropsWithoutRef & { + discountOrder: WithId | undefined; + lastPurchasedCups: number; + } +>(({ discountOrder, lastPurchasedCups, ...props }, ref) => { + return ( +
    +

    割引券番号

    + +

    + {discountOrder === undefined ? "見つかりません" : null} + {discountOrder && `有効杯数: ${lastPurchasedCups}`} +

    +
    + ); +}); + +export { DiscountInput }; diff --git a/app/components/organisms/ItemAssign.tsx b/app/components/organisms/ItemAssign.tsx new file mode 100644 index 00000000..5d99b445 --- /dev/null +++ b/app/components/organisms/ItemAssign.tsx @@ -0,0 +1,96 @@ +import { + type Dispatch, + type SetStateAction, + useCallback, + useEffect, + useRef, + useState, +} from "react"; +import type { WithId } from "~/lib/typeguard"; +import { cn } from "~/lib/utils"; +import { type ItemEntity, type2label } from "~/models/item"; +import { Input } from "../ui/input"; + +type props = { + item: WithId; + idx: number; + setOrderItems: Dispatch[]>>; + focus: boolean; +}; + +const ItemAssign = ({ item, idx, setOrderItems, focus }: props) => { + const [edit, setEdit] = useState(false); + const [assignee, setAssinee] = useState(null); + + const assignInputRef = useRef(null); + + const closeAssignInput = useCallback(() => { + setOrderItems((prevItems) => { + const newItems = [...prevItems]; + newItems[idx].assignee = assignee; + return newItems; + }); + setEdit(false); + }, [idx, assignee, setOrderItems]); + + // edit の状態に応じて assign 入力欄を開くか閉じる + const change = useCallback(() => { + if (edit) { + closeAssignInput(); + } else { + setEdit(true); + } + }, [edit, closeAssignInput]); + + // focus が変化したときに assign 入力欄を閉じる + useEffect(() => { + if (!focus) { + closeAssignInput(); + } + }, [focus, closeAssignInput]); + + // Enter が押されたときに assign 入力欄を開く + useEffect(() => { + const handler = (event: KeyboardEvent) => { + if (event.key === "Enter") { + change(); + } + }; + if (focus) { + window.addEventListener("keydown", handler); + } + return () => { + window.removeEventListener("keydown", handler); + }; + }, [focus, change]); + + // edit が true に変化したとき assign 入力欄にフォーカスする + useEffect(() => { + if (edit) { + assignInputRef.current?.focus(); + } + }, [edit]); + + return ( +
    +

    {idx + 1}

    +
    +

    {item.name}

    +

    {item.price}

    +

    {type2label[item.type]}

    + {edit ? ( + setAssinee(e.target.value || null)} + placeholder="指名" + /> + ) : ( +

    {item.assignee ?? "指名なし"}

    + )} +
    +
    + ); +}; + +export { ItemAssign }; diff --git a/app/components/organisms/OrderAlertDialog.tsx b/app/components/organisms/OrderAlertDialog.tsx new file mode 100644 index 00000000..a19c4a10 --- /dev/null +++ b/app/components/organisms/OrderAlertDialog.tsx @@ -0,0 +1,70 @@ +import { type ComponentPropsWithoutRef, forwardRef } from "react"; +import { type2label } from "~/models/item"; +import type { OrderEntity } from "~/models/order"; +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, +} from "../ui/alert-dialog"; + +// 確定前にオーダーの内容を表示するダイアログ +const OrderAlertDialog = forwardRef< + null, + ComponentPropsWithoutRef & { + order: OrderEntity; + chargeView: number | string; + onConfirm: () => void; + } +>(({ order, chargeView, onConfirm, ...props }) => { + return ( + + + + オーダーを確定しますか? + + 以下の内容で提出します + + {order.items.map((item, idx) => ( + + {`${idx + 1} ―― ${item.name} ¥${item.price} ${type2label[item.type]}`} + + ))} + + 合計金額: ¥{order.total} + + {order.discountInfo.previousOrderId !== null && ( + + 割引: -¥{order.discountInfo.discount} + + )} + + 支払金額: ¥{order.billingAmount} + + + お預かり金額: ¥{order.received} + + + お釣り: ¥{chargeView} + + + 備考: {order.description} + + + Tabで選択し、Enterで確定 + + + + キャンセル + 確定 + + + + ); +}); + +export { OrderAlertDialog }; diff --git a/app/components/organisms/OrderItemView.tsx b/app/components/organisms/OrderItemView.tsx new file mode 100644 index 00000000..1806047a --- /dev/null +++ b/app/components/organisms/OrderItemView.tsx @@ -0,0 +1,51 @@ +import type { WithId } from "~/lib/typeguard"; +import type { ItemEntity } from "~/models/item"; +import type { OrderEntity } from "~/models/order"; +import { ItemAssign } from "./ItemAssign"; + +type props = { + order: OrderEntity; + setOrderItems: React.Dispatch[]>>; + inputStatus: "items" | "discount" | "received" | "description" | "submit"; + itemFocus: number; + setItemFocus: React.Dispatch>; + discountOrder: boolean; +}; + +// オーダーのアイテムや割引情報を表示するコンポーネント +const OrderItemView = ({ + inputStatus, + discountOrder, + setOrderItems, + itemFocus, + order, +}: props) => { + return ( + <> + {inputStatus === "items" && ( + <> +

    商品を追加: キーボードの a, s, d, f, g, h, j, k, l, ;

    +

    ↑・↓でアイテムのフォーカスを移動

    +

    Enterで指名の入力欄を開く

    + + )} + {order.items.map((item, idx) => ( + + ))} + {discountOrder && ( +
    +

    割引

    +
    -¥{order.discountInfo.discount}
    +
    + )} + + ); +}; + +export { OrderItemView }; diff --git a/app/components/pages/CashierV2.tsx b/app/components/pages/CashierV2.tsx new file mode 100644 index 00000000..5afc9a66 --- /dev/null +++ b/app/components/pages/CashierV2.tsx @@ -0,0 +1,275 @@ +import { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { Input } from "~/components/ui/input"; +import type { WithId } from "~/lib/typeguard"; +import { type ItemEntity, type2label } from "~/models/item"; +import { OrderEntity } from "~/models/order"; +import { DiscountInput } from "../organisms/DiscountInput"; +import { OrderAlertDialog } from "../organisms/OrderAlertDialog"; +import { OrderItemView } from "../organisms/OrderItemView"; +import { Button } from "../ui/button"; + +const keys = ["a", "s", "d", "f", "g", "h", "j", "k", "l", ";"]; + +const InputStatus = [ + "discount", + "items", + "received", + "description", + "submit", +] as const; + +type props = { + items: WithId[] | undefined; + orders: WithId[] | undefined; + submitPayload: (order: OrderEntity) => void; +}; + +const CashierV2 = ({ items, orders, submitPayload }: props) => { + const [orderItems, setOrderItems] = useState[]>([]); + 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 [itemFocus, setItemFocus] = useState(0); + + const discountOrderIdNum = Number(discountOrderId); + const discountOrder = orders?.find( + (order) => order.orderId === discountOrderIdNum, + ); + const lastPurchasedCups = discountOrder?._getCoffeeCount() ?? 0; + + const curOrderId = + orders?.reduce((acc, cur) => Math.max(acc, cur.orderId), 0) ?? 0; + const nextOrderId = curOrderId + 1; + const newOrder = OrderEntity.createNew({ orderId: nextOrderId }); + const receivedNum = Number(received); + newOrder.items = orderItems; + newOrder.received = receivedNum; + if (description !== "") { + newOrder.description = description; + } + if (discountOrder) { + newOrder.applyDiscount(discountOrder); + } + const charge = newOrder.received - newOrder.billingAmount; + const chargeView: string | number = charge < 0 ? "不足しています" : charge; + + const receivedDOM = useRef(null); + const descriptionDOM = useRef(null); + const discountInputDOM = useRef(null); + + const proceedItemFocus = useCallback(() => { + setItemFocus((prev) => (prev + 1) % orderItems.length); + }, [orderItems]); + + const prevousItemFocus = useCallback(() => { + setItemFocus((prev) => (prev - 1 + orderItems.length) % orderItems.length); + }, [orderItems]); + + useEffect(() => { + const handler = (event: KeyboardEvent) => { + if (inputStatus !== "items") { + return; + } + if (event.key === "ArrowUp") { + prevousItemFocus(); + } + if (event.key === "ArrowDown") { + proceedItemFocus(); + } + }; + window.addEventListener("keydown", handler); + return () => { + window.removeEventListener("keydown", handler); + }; + }, [proceedItemFocus, prevousItemFocus, inputStatus]); + + 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; + } + if (orderItems.length === 0) { + return; + } + submitPayload(newOrder); + setOrderItems([]); + setReceived(""); + setDiscountOrderId(""); + setDescription(""); + setInputStatus("discount"); + }, [charge, newOrder, orderItems, submitPayload]); + + const moveFocus = useCallback(() => { + switch (inputStatus) { + case "discount": + setDialogOpen(false); + discountInputDOM.current?.focus(); + setItemFocus(-1); + break; + case "items": + break; + case "received": + setItemFocus(-1); + receivedDOM.current?.focus(); + break; + case "description": + descriptionDOM.current?.focus(); + setDialogOpen(false); + 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(() => { + const handlers = items?.map((item, idx) => { + const handler = (event: KeyboardEvent) => { + if (inputStatus !== "items") { + return; + } + if (event.key === keys[idx]) { + setOrderItems((prevItems) => [...prevItems, structuredClone(item)]); + } + }; + return handler; + }); + for (const handler of handlers ?? []) { + window.addEventListener("keydown", handler); + } + + return () => { + for (const handler of handlers ?? []) { + window.removeEventListener("keydown", handler); + } + }; + }, [items, inputStatus]); + + useEffect(() => { + const handler = (event: KeyboardEvent) => { + 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); + }; + }, [keyEventHandlers]); + + return ( + <> +
    +
    + {items?.map((item) => ( +
    +

    {item.name}

    +

    {item.price}

    +

    {type2label[item.type]}

    + +
    + ))} +
    +
    +

    操作

    +

    入力ステータスを移動して一つ一つの項目を入力していきます

    +
      +
    • 入力ステータスを移動 ←・→
    • +
    • 注文をクリア: Esc
    • +
    + + +

    {`No. ${nextOrderId}`}

    +
    +

    合計金額

    +

    {newOrder.billingAmount}

    +
    + setDiscountOrderId(value)} + disabled={inputStatus !== "discount"} + discountOrder={discountOrder} + lastPurchasedCups={lastPurchasedCups} + /> + setReceived(e.target.value)} + placeholder="お預かり金額を入力" + disabled={inputStatus !== "received"} + ref={receivedDOM} + /> + + setDescription(e.target.value)} + placeholder="備考" + disabled={inputStatus !== "description"} + ref={descriptionDOM} + /> +
    +
    +

    入力ステータス: {inputStatus}

    + +
    +
    + + + ); +}; + +export { CashierV2 }; diff --git a/app/components/ui/input-otp.tsx b/app/components/ui/input-otp.tsx index fc957e3a..ea82f11f 100644 --- a/app/components/ui/input-otp.tsx +++ b/app/components/ui/input-otp.tsx @@ -39,7 +39,7 @@ const InputOTPSlot = React.forwardRef<
    ; - idx: number; - setOrderItems: Dispatch[]>>; - focus: boolean; -}) => { - const [edit, setEdit] = useState(false); - const [assignee, setAssinee] = useState(null); - - const assignInputRef = useRef(null); - - const closeAssignInput = useCallback(() => { - setOrderItems((prevItems) => { - const newItems = [...prevItems]; - newItems[idx].assignee = assignee; - return newItems; - }); - setEdit(false); - }, [idx, assignee, setOrderItems]); - - const change = useCallback(() => { - if (edit) { - closeAssignInput(); - } else { - setEdit(true); - } - }, [edit, closeAssignInput]); - - useEffect(() => { - if (!focus) { - closeAssignInput(); - } - }, [focus, closeAssignInput]); - - useEffect(() => { - const handler = (event: KeyboardEvent) => { - if (event.key === "Enter") { - change(); - } - }; - if (focus) { - window.addEventListener("keydown", handler); - } - return () => { - window.removeEventListener("keydown", handler); - }; - }, [focus, change]); - - useEffect(() => { - if (edit) { - assignInputRef.current?.focus(); - } - }, [edit]); - - return ( -
    -

    {idx + 1}

    -
    -

    {item.name}

    -

    {item.price}

    -

    {type2label[item.type]}

    - {edit ? ( - setAssinee(e.target.value || null)} - placeholder="指名" - /> - ) : ( -

    {item.assignee ?? "指名なし"}

    - )} -
    -
    - ); -}; - export default function Cashier() { const { data: items } = useSWRSubscription( "items", @@ -139,313 +20,19 @@ export default function Cashier() { collectionSub({ converter: orderConverter }), ); const submit = useSubmit(); - const [orderItems, setOrderItems] = useState[]>([]); - 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 [itemFocus, setItemFocus] = useState(0); - const discountOrderIdNum = Number(discountOrderId); - const discountOrder = orders?.find( - (order) => order.orderId === discountOrderIdNum, + const submitPayload = useCallback( + (newOrder: OrderEntity) => { + submit( + { newOrder: JSON.stringify(newOrder.toOrder()) }, + { method: "POST" }, + ); + }, + [submit], ); - const lastPurchasedCups = discountOrder?._getCoffeeCount() ?? 0; - - const curOrderId = - orders?.reduce((acc, cur) => Math.max(acc, cur.orderId), 0) ?? 0; - const nextOrderId = curOrderId + 1; - const newOrder = OrderEntity.createNew({ orderId: nextOrderId }); - const receivedNum = Number(received); - newOrder.items = orderItems; - newOrder.received = receivedNum; - if (description !== "") { - newOrder.description = description; - } - if (discountOrder) { - newOrder.applyDiscount(discountOrder); - } - const charge = newOrder.received - newOrder.billingAmount; - const chargeView: string | number = charge < 0 ? "不足しています" : charge; - - const receivedDOM = useRef(null); - const descriptionDOM = useRef(null); - - const proceedItemFocus = useCallback(() => { - setItemFocus((prev) => (prev + 1) % orderItems.length); - }, [orderItems]); - - const prevousItemFocus = useCallback(() => { - setItemFocus((prev) => (prev - 1 + orderItems.length) % orderItems.length); - }, [orderItems]); - - useEffect(() => { - const handler = (event: KeyboardEvent) => { - if (inputStatus !== "items") { - return; - } - if (event.key === "ArrowUp") { - prevousItemFocus(); - } - if (event.key === "ArrowDown") { - proceedItemFocus(); - } - }; - window.addEventListener("keydown", handler); - return () => { - window.removeEventListener("keydown", handler); - }; - }, [proceedItemFocus, prevousItemFocus, inputStatus]); - - 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; - } - if (orderItems.length === 0) { - return; - } - submit( - { newOrder: JSON.stringify(newOrder.toOrder()) }, - { method: "POST" }, - ); - setOrderItems([]); - setReceived(""); - setDiscountOrderId(""); - setDescription(""); - setInputStatus("discount"); - }, [charge, newOrder, orderItems, submit]); - - const moveFocus = useCallback(() => { - switch (inputStatus) { - case "discount": - setDialogOpen(false); - document.getElementById("discountOrderId")?.focus(); - setItemFocus(-1); - break; - case "items": - break; - case "received": - setItemFocus(-1); - receivedDOM.current?.focus(); - break; - case "description": - descriptionDOM.current?.focus(); - setDialogOpen(false); - 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(() => { - const handlers = items?.map((item, idx) => { - const handler = (event: KeyboardEvent) => { - if (inputStatus !== "items") { - return; - } - if (event.key === keys[idx]) { - setOrderItems((prevItems) => [...prevItems, structuredClone(item)]); - } - }; - return handler; - }); - for (const handler of handlers ?? []) { - window.addEventListener("keydown", handler); - } - - return () => { - for (const handler of handlers ?? []) { - window.removeEventListener("keydown", handler); - } - }; - }, [items, inputStatus]); - - useEffect(() => { - const handler = (event: KeyboardEvent) => { - 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); - }; - }, [keyEventHandlers]); return ( - <> -
    -
    - {items?.map((item) => ( -
    -

    {item.name}

    -

    {item.price}

    -

    {type2label[item.type]}

    - -
    - ))} -
    -
    -

    操作

    -

    入力ステータスを移動して一つ一つの項目を入力していきます

    -
      -
    • 入力ステータスを移動 ←・→
    • -
    • 注文をクリア: Esc
    • -
    - - -

    {`No. ${nextOrderId}`}

    -
    -

    合計金額

    -

    {newOrder.billingAmount}

    -
    -
    -

    割引券番号

    - setDiscountOrderId(value)} - disabled={inputStatus !== "discount"} - > - - - - - -

    - {discountOrder === undefined ? "見つかりません" : null} - {discountOrder && `有効杯数: ${lastPurchasedCups}`} -

    -
    - setReceived(e.target.value)} - placeholder="お預かり金額を入力" - disabled={inputStatus !== "received"} - ref={receivedDOM} - /> - - setDescription(e.target.value)} - placeholder="備考" - disabled={inputStatus !== "description"} - ref={descriptionDOM} - /> -
    -
    -

    入力ステータス: {inputStatus}

    - {inputStatus === "items" && ( - <> -

    商品を追加: キーボードの a, s, d, f, g, h, j, k, l, ;

    -

    ↑・↓でアイテムのフォーカスを移動

    -

    Enterで指名の入力欄を開く

    - - )} - {orderItems.map((item, idx) => ( - - ))} - {discountOrder && ( -
    -

    割引

    -
    -¥{newOrder.discountInfo.discount}
    -
    - )} -
    -
    - - - - オーダーを確定しますか? - - 以下の内容で提出します - - {orderItems.map((item, idx) => ( - - {`${idx + 1} ―― ${item.name} ¥${item.price} ${type2label[item.type]}`} - - ))} - - 合計金額: ¥{newOrder.total} - - {discountOrder && ( - - 割引: -¥{newOrder.discountInfo.discount} - - )} - - 支払金額: ¥{newOrder.billingAmount} - - - お預かり金額: ¥{receivedNum} - - - お釣り: ¥{chargeView} - - 備考: {description} - - Tabで選択し、Enterで確定 - - - - - キャンセル - - 確定 - - - - + ); } From 278ffcf53817ede65e16c37dfb11218c1b7c65f9 Mon Sep 17 00:00:00 2001 From: Astalum <131961897+Astalum@users.noreply.github.com> Date: Mon, 30 Sep 2024 21:29:02 +0900 Subject: [PATCH 13/28] =?UTF-8?q?/casher=20=E3=82=AA=E3=83=BC=E3=83=80?= =?UTF-8?q?=E3=83=BC=E3=82=92firebase=E3=81=AB=E9=A3=9B=E3=81=B0=E3=81=99?= =?UTF-8?q?=20(#175)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/routes/_header.casher.tsx | 90 ++++++++++++++++++++++++++++------- 1 file changed, 73 insertions(+), 17 deletions(-) diff --git a/app/routes/_header.casher.tsx b/app/routes/_header.casher.tsx index ab6b9c2e..2060c2fe 100644 --- a/app/routes/_header.casher.tsx +++ b/app/routes/_header.casher.tsx @@ -1,7 +1,10 @@ +import { parseWithZod } from "@conform-to/zod"; import { AlertDialogCancel } from "@radix-ui/react-alert-dialog"; import { TrashIcon } from "@radix-ui/react-icons"; +import { type ClientActionFunction, useSubmit } from "@remix-run/react"; import { useState } from "react"; import useSWRSubscription from "swr/subscription"; +import { z } from "zod"; import { AlertDialog, AlertDialogAction, @@ -25,9 +28,11 @@ import { } from "~/components/ui/table"; import { itemConverter, orderConverter } from "~/firebase/converter"; import { collectionSub } from "~/firebase/subscription"; +import { stringToJSONSchema } from "~/lib/custom-zod"; import type { WithId } from "~/lib/typeguard"; import type { ItemEntity } from "~/models/item"; -import { OrderEntity } from "~/models/order"; +import { OrderEntity, orderSchema } from "~/models/order"; +import { orderRepository } from "~/repositories/order"; export default function Casher() { const { data: items } = useSWRSubscription( @@ -41,13 +46,32 @@ export default function Casher() { const curOrderId = orders?.reduce((acc, cur) => Math.max(acc, cur.orderId), 0) ?? 0; const nextOrderId = curOrderId + 1; + const submit = useSubmit(); const order = OrderEntity.createNew({ orderId: nextOrderId }); - const [recieved, setText] = useState(0); + const [recieved, setReceived] = useState(0); const [queue, setQueue] = useState[]>([]); order.items = queue; + const charge = recieved - order.total; + const [description, setDescription] = useState(""); + order.description = description; + + const submitOrder = () => { + console.log(charge); + if (charge < 0) { + return; + } + if (queue.length === 0) { + return; + } + submit({ newOrder: JSON.stringify(order.toOrder()) }, { method: "POST" }); + console.log("送信"); + setQueue([]); + setReceived(0); + setDescription(""); + }; return ( -
    +
    @@ -70,7 +94,7 @@ export default function Casher() { - No. {curOrderId} + No. {nextOrderId} @@ -103,7 +127,15 @@ export default function Casher() {
    • -

      合計金額:{order.total} 円

      + setDescription(e.target.value)} + /> +

      合計金額:{order.total} 円

    • @@ -114,34 +146,34 @@ export default function Casher() { - 金額を確認してください + 金額・備考欄を確認してください +

      備考欄:{order.description}

      受領額: - setText(Number.parseInt(event.target.value)) - } + onChange={(e) => { + const value = Number.parseInt(e.target.value); + setReceived(Number.isNaN(value) ? 0 : value); // NaN のチェック + }} />

      合計: {order.total} 円

      - お釣り: {recieved - order.total < 0 && 0} - {recieved - order.total >= 0 && - recieved - order.total}{" "} - 円 + お釣り:{" "} + {Number.isNaN(charge) || charge < 0 ? 0 : charge} 円

      - - 戻る - - + 戻る + 送信 @@ -156,3 +188,27 @@ export default function Casher() {
    ); } + +export const clientAction: ClientActionFunction = async ({ request }) => { + const formData = await request.formData(); + + const schema = z.object({ + newOrder: stringToJSONSchema.pipe(orderSchema), + }); + const submission = parseWithZod(formData, { + schema, + }); + if (submission.status !== "success") { + console.error(submission.error); + return submission.reply(); + } + + const { newOrder } = submission.value; + const order = OrderEntity.fromOrderWOId(newOrder); + + const savedOrder = await orderRepository.save(order); + + console.log("savedOrder", savedOrder); + + return new Response("ok"); +}; From b5034064f3408a51b1868ea61b3e31fa3d97c43c Mon Sep 17 00:00:00 2001 From: Ryunosuke Tokinaga <59079411+toririm@users.noreply.github.com> Date: Wed, 2 Oct 2024 08:51:03 +0900 Subject: [PATCH 14/28] =?UTF-8?q?/cashier-v2=20useReducer=20=E3=82=92?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E3=81=97=E3=81=A6=20state=20=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E3=81=AE=E3=83=AA=E3=83=95=E3=82=A1=E3=82=AF=E3=82=BF?= =?UTF-8?q?=E3=83=AA=E3=83=B3=E3=82=B0=20(#178)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #176 --- .../molecules/AttractiveTextBox.tsx | 46 +++ app/components/organisms/DiscountInput.tsx | 58 +++- app/components/organisms/ItemAssign.tsx | 52 ++-- app/components/organisms/OrderItemView.tsx | 78 ++++- app/components/pages/CashierV2.tsx | 274 ++++++++++-------- app/models/order.ts | 52 ++-- app/routes/_header.casher.tsx | 2 +- app/routes/cashier-v2.tsx | 2 +- 8 files changed, 372 insertions(+), 192 deletions(-) create mode 100644 app/components/molecules/AttractiveTextBox.tsx diff --git a/app/components/molecules/AttractiveTextBox.tsx b/app/components/molecules/AttractiveTextBox.tsx new file mode 100644 index 00000000..a80ad8c6 --- /dev/null +++ b/app/components/molecules/AttractiveTextBox.tsx @@ -0,0 +1,46 @@ +import { + type ChangeEventHandler, + useCallback, + useEffect, + useRef, + useState, +} from "react"; +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 = useRef(null); + + const onChangeHandler: ChangeEventHandler = useCallback( + (event) => setText(event.target.value), + [], + ); + + useEffect(() => { + onTextSet(text); + }, [text, onTextSet]); + + useEffect(() => { + if (focus) { + DOMRef.current?.focus(); + } + }, [focus]); + + return ( + + ); +}; + +export { AttractiveTextBox }; diff --git a/app/components/organisms/DiscountInput.tsx b/app/components/organisms/DiscountInput.tsx index 05ca5599..58e68f54 100644 --- a/app/components/organisms/DiscountInput.tsx +++ b/app/components/organisms/DiscountInput.tsx @@ -2,26 +2,70 @@ import { type ComponentPropsWithoutRef, type ElementRef, forwardRef, + useEffect, + useMemo, + useState, } from "react"; import type { WithId } from "~/lib/typeguard"; -import type { OrderEntity } from "~/models/order"; +import { OrderEntity } from "~/models/order"; import { ThreeDigitsInput } from "../molecules/ThreeDigitsInput"; +const findByOrderId = ( + orders: WithId[] | undefined, + orderId: number, +): WithId | undefined => { + return orders?.find((order) => order.orderId === orderId); +}; + // 割引券番号を入力するためのコンポーネント const DiscountInput = forwardRef< ElementRef, ComponentPropsWithoutRef & { - discountOrder: WithId | undefined; - lastPurchasedCups: number; + orders: WithId[] | undefined; + onDiscountOrderFind: (discountOrder: WithId) => void; + onDiscountOrderRemoved: () => void; } ->(({ discountOrder, lastPurchasedCups, ...props }, ref) => { +>(({ orders, onDiscountOrderFind, onDiscountOrderRemoved, ...props }, ref) => { + const [discountOrderId, setDiscountOrderId] = useState(""); + + const isComplete = useMemo( + () => discountOrderId.length === 3, + [discountOrderId], + ); + + const discountOrder = useMemo(() => { + if (!isComplete) return; + const discountOrderIdNum = Number(discountOrderId); + return findByOrderId(orders, discountOrderIdNum); + }, [orders, isComplete, discountOrderId]); + + const lastPurchasedCups = useMemo( + () => discountOrder?.getCoffeeCount() ?? 0, + [discountOrder], + ); + + useEffect(() => { + if (isComplete && discountOrder) { + onDiscountOrderFind(discountOrder); + } + return onDiscountOrderRemoved; + }, [isComplete, discountOrder, onDiscountOrderFind, onDiscountOrderRemoved]); + return (

    割引券番号

    - + setDiscountOrderId(value)} + {...props} + />

    - {discountOrder === undefined ? "見つかりません" : null} - {discountOrder && `有効杯数: ${lastPurchasedCups}`} + {!isComplete && "3桁の割引券番号を入力してください"} + {isComplete && + (discountOrder instanceof OrderEntity + ? `有効杯数: ${lastPurchasedCups}` + : "見つかりません")}

    ); diff --git a/app/components/organisms/ItemAssign.tsx b/app/components/organisms/ItemAssign.tsx index 5d99b445..1d3cfa08 100644 --- a/app/components/organisms/ItemAssign.tsx +++ b/app/components/organisms/ItemAssign.tsx @@ -1,11 +1,4 @@ -import { - type Dispatch, - type SetStateAction, - useCallback, - useEffect, - useRef, - useState, -} from "react"; +import { useCallback, useEffect, useRef, useState } from "react"; import type { WithId } from "~/lib/typeguard"; import { cn } from "~/lib/utils"; import { type ItemEntity, type2label } from "~/models/item"; @@ -14,46 +7,49 @@ import { Input } from "../ui/input"; type props = { item: WithId; idx: number; - setOrderItems: Dispatch[]>>; + mutateItem: ( + idx: number, + action: (prev: WithId) => WithId, + ) => void; focus: boolean; }; -const ItemAssign = ({ item, idx, setOrderItems, focus }: props) => { - const [edit, setEdit] = useState(false); +const ItemAssign = ({ item, idx, mutateItem, focus }: props) => { + const [editable, setEditable] = useState(false); const [assignee, setAssinee] = useState(null); const assignInputRef = useRef(null); const closeAssignInput = useCallback(() => { - setOrderItems((prevItems) => { - const newItems = [...prevItems]; - newItems[idx].assignee = assignee; - return newItems; + mutateItem(idx, (prev) => { + const copy = structuredClone(prev); + copy.assignee = assignee; + return copy; }); - setEdit(false); - }, [idx, assignee, setOrderItems]); + setEditable(false); + }, [assignee, idx, mutateItem]); // edit の状態に応じて assign 入力欄を開くか閉じる - const change = useCallback(() => { - if (edit) { + const switchEditable = useCallback(() => { + if (editable) { closeAssignInput(); } else { - setEdit(true); + setEditable(true); } - }, [edit, closeAssignInput]); + }, [editable, closeAssignInput]); // focus が変化したときに assign 入力欄を閉じる useEffect(() => { - if (!focus) { + if (!focus && editable) { closeAssignInput(); } - }, [focus, closeAssignInput]); + }, [focus, editable, closeAssignInput]); // Enter が押されたときに assign 入力欄を開く useEffect(() => { const handler = (event: KeyboardEvent) => { if (event.key === "Enter") { - change(); + switchEditable(); } }; if (focus) { @@ -62,14 +58,14 @@ const ItemAssign = ({ item, idx, setOrderItems, focus }: props) => { return () => { window.removeEventListener("keydown", handler); }; - }, [focus, change]); + }, [focus, switchEditable]); // edit が true に変化したとき assign 入力欄にフォーカスする useEffect(() => { - if (edit) { + if (editable) { assignInputRef.current?.focus(); } - }, [edit]); + }, [editable]); return (
    @@ -78,7 +74,7 @@ const ItemAssign = ({ item, idx, setOrderItems, focus }: props) => {

    {item.name}

    {item.price}

    {type2label[item.type]}

    - {edit ? ( + {editable ? ( []>>; - inputStatus: "items" | "discount" | "received" | "description" | "submit"; - itemFocus: number; - setItemFocus: React.Dispatch>; + items: WithId[] | undefined; + focus: boolean; + onAddItem: (item: WithId) => void; + mutateItem: ( + idx: number, + action: (prev: WithId) => WithId, + ) => void; discountOrder: boolean; }; // オーダーのアイテムや割引情報を表示するコンポーネント const OrderItemView = ({ - inputStatus, + focus, discountOrder, - setOrderItems, - itemFocus, + onAddItem, + mutateItem, order, + items, }: props) => { + const [itemFocus, setItemFocus] = useState(0); + + const proceedItemFocus = useCallback(() => { + setItemFocus((prev) => (prev + 1) % order.items.length); + }, [order.items]); + + const prevousItemFocus = useCallback(() => { + setItemFocus( + (prev) => (prev - 1 + order.items.length) % order.items.length, + ); + }, [order.items]); + + useEffect(() => { + const handler = (event: KeyboardEvent) => { + if (!focus) { + return; + } + if (event.key === "ArrowUp") { + prevousItemFocus(); + } + if (event.key === "ArrowDown") { + proceedItemFocus(); + } + }; + window.addEventListener("keydown", handler); + return () => { + window.removeEventListener("keydown", handler); + }; + }, [proceedItemFocus, prevousItemFocus, focus]); + + useEffect(() => { + const handlers = items?.map((item, idx) => { + const handler = (event: KeyboardEvent) => { + if (!focus) { + return; + } + if (event.key === keys[idx]) { + onAddItem(item); + } + }; + return handler; + }); + for (const handler of handlers ?? []) { + window.addEventListener("keydown", handler); + } + + return () => { + for (const handler of handlers ?? []) { + window.removeEventListener("keydown", handler); + } + }; + }, [items, focus, onAddItem]); + return ( <> - {inputStatus === "items" && ( + {focus && ( <>

    商品を追加: キーボードの a, s, d, f, g, h, j, k, l, ;

    ↑・↓でアイテムのフォーカスを移動

    @@ -34,7 +94,7 @@ const OrderItemView = ({ key={`${idx}-${item.id}`} item={item} idx={idx} - setOrderItems={setOrderItems} + mutateItem={mutateItem} focus={idx === itemFocus} /> ))} diff --git a/app/components/pages/CashierV2.tsx b/app/components/pages/CashierV2.tsx index 5afc9a66..aab3ffb8 100644 --- a/app/components/pages/CashierV2.tsx +++ b/app/components/pages/CashierV2.tsx @@ -1,15 +1,21 @@ -import { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { + useCallback, + useEffect, + useMemo, + useReducer, + useRef, + useState, +} from "react"; import { Input } from "~/components/ui/input"; import type { WithId } from "~/lib/typeguard"; import { type ItemEntity, type2label } from "~/models/item"; import { OrderEntity } from "~/models/order"; +import { AttractiveTextBox } from "../molecules/AttractiveTextBox"; import { DiscountInput } from "../organisms/DiscountInput"; import { OrderAlertDialog } from "../organisms/OrderAlertDialog"; import { OrderItemView } from "../organisms/OrderItemView"; import { Button } from "../ui/button"; -const keys = ["a", "s", "d", "f", "g", "h", "j", "k", "l", ";"]; - const InputStatus = [ "discount", "items", @@ -24,68 +30,116 @@ type props = { submitPayload: (order: OrderEntity) => void; }; +export type Action = + | { type: "clear"; effectFn?: () => void } + | { type: "updateOrderId"; orderId: number } + | { + type: "addItem"; + item: WithId; + } + | { + type: "mutateItem"; + idx: number; + action: (prev: WithId) => WithId; + } + | { type: "applyDiscount"; discountOrder: WithId } + | { type: "removeDiscount" } + | { type: "setReceived"; received: string } + | { type: "setDescription"; description: string }; + +const reducer = (state: OrderEntity, action: Action): OrderEntity => { + const addItem = (item: WithId) => { + const updated = state.clone(); + updated.items = [...updated.items, item]; + return updated; + }; + const applyDiscount = (discountOrder: WithId) => { + const updated = state.clone(); + updated.applyDiscount(discountOrder); + return updated; + }; + const removeDiscount = () => { + const updated = state.clone(); + updated.removeDiscount(); + return updated; + }; + const mutateItem = ( + idx: number, + action: (prev: WithId) => WithId, + ) => { + const updated = state.clone(); + updated.items[idx] = action(updated.items[idx]); + return updated; + }; + const updateOrderId = (orderId: number) => { + const updated = state.clone(); + updated.orderId = orderId; + return updated; + }; + const setReceived = (received: string) => { + const updated = state.clone(); + updated.received = Number(received); + return updated; + }; + const setDescription = (description: string) => { + const updated = state.clone(); + updated.description = description; + return updated; + }; + const clear = (effectFn?: () => void) => { + if (effectFn) { + effectFn(); + } + return OrderEntity.createNew({ orderId: state.orderId }); + }; + + switch (action.type) { + case "clear": + return clear(action.effectFn); + case "applyDiscount": + return applyDiscount(action.discountOrder); + case "removeDiscount": + return removeDiscount(); + case "addItem": + return addItem(action.item); + case "mutateItem": + return mutateItem(action.idx, action.action); + case "setReceived": + return setReceived(action.received); + case "setDescription": + return setDescription(action.description); + case "updateOrderId": + return updateOrderId(action.orderId); + } +}; + +const latestOrderId = (orders: WithId[] | undefined): number => { + if (!orders) { + return 0; + } + return orders.reduce((acc, cur) => Math.max(acc, cur.orderId), 0); +}; + const CashierV2 = ({ items, orders, submitPayload }: props) => { - const [orderItems, setOrderItems] = useState[]>([]); - const [received, setReceived] = useState(""); - const [discountOrderId, setDiscountOrderId] = useState(""); - const [description, setDescription] = useState(""); + const [newOrder, dispatch] = useReducer( + reducer, + OrderEntity.createNew({ orderId: -1 }), + ); const [inputStatus, setInputStatus] = useState<(typeof InputStatus)[number]>("discount"); const [dialogOpen, setDialogOpen] = useState(false); - const [itemFocus, setItemFocus] = useState(0); + const [inputSession, setInputSession] = useState(new Date()); - const discountOrderIdNum = Number(discountOrderId); - const discountOrder = orders?.find( - (order) => order.orderId === discountOrderIdNum, - ); - const lastPurchasedCups = discountOrder?._getCoffeeCount() ?? 0; + const nextOrderId = useMemo(() => latestOrderId(orders) + 1, [orders]); + useEffect(() => { + dispatch({ type: "updateOrderId", orderId: nextOrderId }); + }, [nextOrderId]); - const curOrderId = - orders?.reduce((acc, cur) => Math.max(acc, cur.orderId), 0) ?? 0; - const nextOrderId = curOrderId + 1; - const newOrder = OrderEntity.createNew({ orderId: nextOrderId }); - const receivedNum = Number(received); - newOrder.items = orderItems; - newOrder.received = receivedNum; - if (description !== "") { - newOrder.description = description; - } - if (discountOrder) { - newOrder.applyDiscount(discountOrder); - } const charge = newOrder.received - newOrder.billingAmount; const chargeView: string | number = charge < 0 ? "不足しています" : charge; - const receivedDOM = useRef(null); - const descriptionDOM = useRef(null); const discountInputDOM = useRef(null); - const proceedItemFocus = useCallback(() => { - setItemFocus((prev) => (prev + 1) % orderItems.length); - }, [orderItems]); - - const prevousItemFocus = useCallback(() => { - setItemFocus((prev) => (prev - 1 + orderItems.length) % orderItems.length); - }, [orderItems]); - - useEffect(() => { - const handler = (event: KeyboardEvent) => { - if (inputStatus !== "items") { - return; - } - if (event.key === "ArrowUp") { - prevousItemFocus(); - } - if (event.key === "ArrowDown") { - proceedItemFocus(); - } - }; - window.addEventListener("keydown", handler); - return () => { - window.removeEventListener("keydown", handler); - }; - }, [proceedItemFocus, prevousItemFocus, inputStatus]); - const proceedStatus = useCallback(() => { const idx = InputStatus.indexOf(inputStatus); setInputStatus(InputStatus[(idx + 1) % InputStatus.length]); @@ -102,32 +156,24 @@ const CashierV2 = ({ items, orders, submitPayload }: props) => { if (charge < 0) { return; } - if (orderItems.length === 0) { + if (newOrder.items.length === 0) { return; } + dispatch({ type: "clear", effectFn: () => setInputSession(new Date()) }); submitPayload(newOrder); - setOrderItems([]); - setReceived(""); - setDiscountOrderId(""); - setDescription(""); - setInputStatus("discount"); - }, [charge, newOrder, orderItems, submitPayload]); + }, [charge, newOrder, submitPayload]); const moveFocus = useCallback(() => { switch (inputStatus) { case "discount": setDialogOpen(false); discountInputDOM.current?.focus(); - setItemFocus(-1); break; case "items": break; case "received": - setItemFocus(-1); - receivedDOM.current?.focus(); break; case "description": - descriptionDOM.current?.focus(); setDialogOpen(false); break; case "submit": @@ -145,37 +191,11 @@ const CashierV2 = ({ items, orders, submitPayload }: props) => { Escape: () => { setInputStatus("discount"); setDialogOpen(false); - setOrderItems([]); - setReceived(""); - setDiscountOrderId(""); - setDescription(""); + dispatch({ type: "clear" }); }, }; }, [proceedStatus, prevousStatus]); - useEffect(() => { - const handlers = items?.map((item, idx) => { - const handler = (event: KeyboardEvent) => { - if (inputStatus !== "items") { - return; - } - if (event.key === keys[idx]) { - setOrderItems((prevItems) => [...prevItems, structuredClone(item)]); - } - }; - return handler; - }); - for (const handler of handlers ?? []) { - window.addEventListener("keydown", handler); - } - - return () => { - for (const handler of handlers ?? []) { - window.removeEventListener("keydown", handler); - } - }; - }, [items, inputStatus]); - useEffect(() => { const handler = (event: KeyboardEvent) => { const key = event.key; @@ -200,13 +220,6 @@ const CashierV2 = ({ items, orders, submitPayload }: props) => {

    {item.name}

    {item.price}

    {type2label[item.type]}

    -
    ))}
    @@ -218,46 +231,63 @@ const CashierV2 = ({ items, orders, submitPayload }: props) => {
  • 注文をクリア: Esc
- -

{`No. ${nextOrderId}`}

+

{`No. ${newOrder.orderId}`}

合計金額

{newOrder.billingAmount}

setDiscountOrderId(value)} disabled={inputStatus !== "discount"} - discountOrder={discountOrder} - lastPurchasedCups={lastPurchasedCups} + orders={orders} + onDiscountOrderFind={useCallback( + (discountOrder) => + dispatch({ type: "applyDiscount", discountOrder }), + [], + )} + onDiscountOrderRemoved={useCallback( + () => dispatch({ type: "removeDiscount" }), + [], + )} /> - setReceived(e.target.value)} - placeholder="お預かり金額を入力" - disabled={inputStatus !== "received"} - ref={receivedDOM} + onTextSet={useCallback( + (text) => dispatch({ type: "setReceived", received: text }), + [], + )} + focus={inputStatus === "received"} /> - setDescription(e.target.value)} - placeholder="備考" - disabled={inputStatus !== "description"} - ref={descriptionDOM} + dispatch({ type: "setDescription", description: text }), + [], + )} + focus={inputStatus === "description"} />

入力ステータス: {inputStatus}

dispatch({ type: "addItem", item }), + [], + )} + mutateItem={useCallback( + (idx, action) => dispatch({ type: "mutateItem", idx, action }), + [], + )} + focus={inputStatus === "items"} + discountOrder={useMemo( + () => newOrder.discountInfo.previousOrderId !== null, + [newOrder], + )} />
diff --git a/app/models/order.ts b/app/models/order.ts index 68cc67aa..c830a1e8 100644 --- a/app/models/order.ts +++ b/app/models/order.ts @@ -46,8 +46,8 @@ export class OrderEntity implements Order { // 全てのプロパティを private にして外部からの直接アクセスを禁止 private constructor( private readonly _id: string | undefined, - private readonly _orderId: number, - private readonly _createdAt: Date, + private _orderId: number, + private _createdAt: Date, private _servedAt: Date | null, private _items: WithId[], private _total: number, @@ -77,7 +77,11 @@ export class OrderEntity implements Order { ); } - static fromOrder(order: WithId): WithId { + static fromOrder(order: WithId): WithId; + static fromOrder(order: Order): OrderEntity; + static fromOrder( + order: WithId | Order, + ): WithId | OrderEntity { return new OrderEntity( order.id, order.orderId, @@ -90,22 +94,6 @@ export class OrderEntity implements Order { order.billingAmount, order.received, DiscountInfoEntity.fromDiscountInfo(order.discountInfo), - ) as WithId; - } - - static fromOrderWOId(order: Order): OrderEntity { - return new OrderEntity( - undefined, - order.orderId, - order.createdAt, - order.servedAt, - order.items, - order.total, - order.orderReady, - order.description, - order.billingAmount, - order.received, - DiscountInfoEntity.fromDiscountInfo(order.discountInfo), ); } @@ -120,6 +108,9 @@ export class OrderEntity implements Order { get orderId() { return this._orderId; } + set orderId(orderId: number) { + this._orderId = orderId; + } get createdAt() { return this._createdAt; @@ -175,7 +166,7 @@ export class OrderEntity implements Order { // methods // -------------------------------------------------- - _getCoffeeCount() { + getCoffeeCount() { // milk 以外のアイテムの数を返す // TODO(toririm): このメソッドは items が変更された時だけでいい return this.items.filter((item) => item.type !== "milk").length; @@ -191,11 +182,10 @@ export class OrderEntity implements Order { this._servedAt = new Date(); } - /* このメソッドのみで discountInfo を更新する */ applyDiscount(previousOrder: OrderEntity) { const validCups = Math.min( - this._getCoffeeCount(), - previousOrder._getCoffeeCount(), + this.getCoffeeCount(), + previousOrder.getCoffeeCount(), ); const discount = validCups * DISCOUNT_RATE_PER_CUP; @@ -204,7 +194,15 @@ export class OrderEntity implements Order { validCups, discount, }); - return this._discountInfo; + } + + removeDiscount() { + this._discountInfo = new DiscountInfoEntity(null, 0, 0); + } + + nowCreated() { + // createdAt を更新 + this._createdAt = new Date(); } toOrder(): Order { @@ -222,4 +220,10 @@ export class OrderEntity implements Order { discountInfo: this.discountInfo, }; } + + clone(): WithId; + clone(): OrderEntity; + clone(): WithId | OrderEntity { + return OrderEntity.fromOrder(this.toOrder()); + } } diff --git a/app/routes/_header.casher.tsx b/app/routes/_header.casher.tsx index 2060c2fe..702f1f2e 100644 --- a/app/routes/_header.casher.tsx +++ b/app/routes/_header.casher.tsx @@ -204,7 +204,7 @@ export const clientAction: ClientActionFunction = async ({ request }) => { } const { newOrder } = submission.value; - const order = OrderEntity.fromOrderWOId(newOrder); + const order = OrderEntity.fromOrder(newOrder); const savedOrder = await orderRepository.save(order); diff --git a/app/routes/cashier-v2.tsx b/app/routes/cashier-v2.tsx index e9927903..6f4d4207 100644 --- a/app/routes/cashier-v2.tsx +++ b/app/routes/cashier-v2.tsx @@ -52,7 +52,7 @@ export const clientAction: ClientActionFunction = async ({ request }) => { } const { newOrder } = submission.value; - const order = OrderEntity.fromOrderWOId(newOrder); + const order = OrderEntity.fromOrder(newOrder); const savedOrder = await orderRepository.save(order); From b697fd970b90587eae4f5313bf69009e12f3006a Mon Sep 17 00:00:00 2001 From: Ryunosuke Tokinaga <59079411+toririm@users.noreply.github.com> Date: Wed, 2 Oct 2024 09:37:42 +0900 Subject: [PATCH 15/28] =?UTF-8?q?[=E3=82=B9=E3=82=AD=E3=83=BC=E3=83=9E?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3]=20applyDiscount=20=E5=AE=9F=E8=A1=8C?= =?UTF-8?q?=E5=BE=8C=E3=81=AB=20items=20=E3=82=92=E7=B7=A8=E9=9B=86?= =?UTF-8?q?=E3=81=97=E3=81=A6=E3=82=82=E5=89=B2=E5=BC=95=E9=A1=8D=E3=81=8C?= =?UTF-8?q?=E6=AD=A3=E5=B8=B8=E3=81=AB=E8=A8=88=E7=AE=97=E3=81=95=E3=82=8C?= =?UTF-8?q?=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=20(#181)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/components/organisms/OrderAlertDialog.tsx | 4 +- app/components/organisms/OrderItemView.tsx | 2 +- app/components/pages/CashierV2.tsx | 2 +- app/models/order.test.ts | 90 +++++++++++++++++-- app/models/order.ts | 90 +++++++++---------- 5 files changed, 130 insertions(+), 58 deletions(-) diff --git a/app/components/organisms/OrderAlertDialog.tsx b/app/components/organisms/OrderAlertDialog.tsx index a19c4a10..2195eaa4 100644 --- a/app/components/organisms/OrderAlertDialog.tsx +++ b/app/components/organisms/OrderAlertDialog.tsx @@ -37,9 +37,9 @@ const OrderAlertDialog = forwardRef< 合計金額: ¥{order.total} - {order.discountInfo.previousOrderId !== null && ( + {order.discountOrderId !== null && ( - 割引: -¥{order.discountInfo.discount} + 割引: -¥{order.discount} )} diff --git a/app/components/organisms/OrderItemView.tsx b/app/components/organisms/OrderItemView.tsx index 530129e6..513f42af 100644 --- a/app/components/organisms/OrderItemView.tsx +++ b/app/components/organisms/OrderItemView.tsx @@ -101,7 +101,7 @@ const OrderItemView = ({ {discountOrder && (

割引

-
-¥{order.discountInfo.discount}
+
-¥{order.discount}
)} diff --git a/app/components/pages/CashierV2.tsx b/app/components/pages/CashierV2.tsx index aab3ffb8..72bb9739 100644 --- a/app/components/pages/CashierV2.tsx +++ b/app/components/pages/CashierV2.tsx @@ -285,7 +285,7 @@ const CashierV2 = ({ items, orders, submitPayload }: props) => { )} focus={inputStatus === "items"} discountOrder={useMemo( - () => newOrder.discountInfo.previousOrderId !== null, + () => newOrder.discountOrderId !== null, [newOrder], )} /> diff --git a/app/models/order.test.ts b/app/models/order.test.ts index dde0798e..a3c39d06 100644 --- a/app/models/order.test.ts +++ b/app/models/order.test.ts @@ -3,6 +3,22 @@ import type { WithId } from "~/lib/typeguard"; import { type Item, ItemEntity } from "./item"; import { OrderEntity } from "./order"; +const coffeeItem = ItemEntity.fromItem({ + id: "1", + name: "item1", + price: 300, + type: "hot", + assignee: null, +}); + +const milkItem = ItemEntity.fromItem({ + id: "2", + name: "item2", + price: 100, + type: "milk", + assignee: null, +}); + describe("[unit] order entity", () => { test("total auto calc", () => { const order = OrderEntity.createNew({ orderId: 2024 }); @@ -94,17 +110,16 @@ describe("[unit] order entity", () => { description: null, billingAmount: 900, received: 0, - discountInfo: { - previousOrderId: null, - validCups: 0, - discount: 0, - }, + discountOrderId: null, + discountOrderCups: 0, + DISCOUNT_PER_CUP: 100, + discount: 0, }); order.applyDiscount(previousOrder); - expect(order.discountInfo.previousOrderId).toBe(99999); - expect(order.discountInfo.validCups).toBe(1); - expect(order.discountInfo.discount).toBe(100); + expect(order.discountOrderId).toBe(99999); + expect(order.discountOrderCups).toBe(1); + expect(order.discount).toBe(100); expect(order.billingAmount).toBe(800); }); @@ -115,4 +130,63 @@ describe("[unit] order entity", () => { order.received = 1000; expect(order.received).toBe(1000); }); + + test("applyDiscount", () => { + const order = OrderEntity.createNew({ orderId: 2024 }); + expect(order.billingAmount).toBe(0); + + const items: WithId[] = [ + { + id: "1", + name: "item1", + price: 400, + type: "hot", + assignee: null, + }, + { + id: "2", + name: "item2", + price: 500, + type: "ice", + assignee: null, + }, + ]; + const itemEntities = items.map((item) => ItemEntity.fromItem(item)); + + order.items = itemEntities; + expect(order.billingAmount).toBe(900); + + const previousOrder = OrderEntity.fromOrder({ + id: "1", + orderId: 99999, + createdAt: new Date(), + servedAt: null, + items: itemEntities, + total: 900, + orderReady: false, + description: null, + billingAmount: 900, + received: 0, + discountOrderId: null, + discountOrderCups: 0, + DISCOUNT_PER_CUP: 100, + discount: 0, + }); + + order.applyDiscount(previousOrder); + expect(order.discountOrderId).toBe(99999); + expect(order.discountOrderCups).toBe(2); + expect(order.discount).toBe(200); + expect(order.billingAmount).toBe(700); + + order.items.pop(); + expect(order.discount).toBe(100); + expect(order.total).toBe(400); + expect(order.billingAmount).toBe(300); + + order.items.push(milkItem); + expect(order.discount).toBe(100); + expect(order.total).toBe(500); + expect(order.billingAmount).toBe(400); + }); }); diff --git a/app/models/order.ts b/app/models/order.ts index c830a1e8..9070f9ed 100644 --- a/app/models/order.ts +++ b/app/models/order.ts @@ -13,34 +13,16 @@ export const orderSchema = z.object({ description: z.string().nullable(), billingAmount: z.number(), // total - discount received: z.number(), // お預かり金額 - discountInfo: z.object({ - previousOrderId: z.number().nullable(), - validCups: z.number(), // min(this.items.length, previousOrder.items.length) - discount: z.number(), // validCups * 100 - }), + discountOrderId: z.number().nullable(), + discountOrderCups: z.number(), + DISCOUNT_PER_CUP: z.number(), + discount: z.number(), // min(this.getCoffeeCount(), discountOrderCups) * DISCOUNT_PER_CUP }); export type Order = z.infer; -type DiscountInfo = Order["discountInfo"]; -const DISCOUNT_RATE_PER_CUP = 100; - -// OrderEntity の内部でのみ使うクラス -class DiscountInfoEntity implements DiscountInfo { - constructor( - readonly previousOrderId: number | null, - readonly validCups: number, - readonly discount: number, - ) {} - - static fromDiscountInfo(discountInfo: DiscountInfo): DiscountInfoEntity { - return new DiscountInfoEntity( - discountInfo.previousOrderId, - discountInfo.validCups, - discountInfo.discount, - ); - } -} +// 途中から割引額を変更する場合はこの値を変更する +const STATIC_DISCOUNT_PER_CUP = 100; export class OrderEntity implements Order { // 全てのプロパティを private にして外部からの直接アクセスを禁止 @@ -55,11 +37,10 @@ export class OrderEntity implements Order { private _description: string | null, private _billingAmount: number, private _received: number, - private _discountInfo: DiscountInfoEntity = new DiscountInfoEntity( - null, - 0, - 0, - ), + private _discountOrderId: number | null, + private _discountOrderCups: number, + private readonly _DISCOUNT_PER_CUP: number, + private _discount: number, ) {} static createNew({ orderId }: { orderId: number }): OrderEntity { @@ -74,6 +55,10 @@ export class OrderEntity implements Order { null, 0, 0, + null, + 0, + STATIC_DISCOUNT_PER_CUP, + 0, ); } @@ -93,7 +78,10 @@ export class OrderEntity implements Order { order.description, order.billingAmount, order.received, - DiscountInfoEntity.fromDiscountInfo(order.discountInfo), + order.discountOrderId, + order.discountOrderCups, + order.DISCOUNT_PER_CUP, + order.discount, ); } @@ -147,7 +135,7 @@ export class OrderEntity implements Order { } get billingAmount() { - this._billingAmount = this.total - this._discountInfo.discount; + this._billingAmount = this.total - this.discount; return this._billingAmount; } @@ -158,8 +146,23 @@ export class OrderEntity implements Order { this._received = received; } - get discountInfo() { - return this._discountInfo; + get discountOrderId() { + return this._discountOrderId; + } + + get discountOrderCups() { + return this._discountOrderCups; + } + + get DISCOUNT_PER_CUP() { + return this._DISCOUNT_PER_CUP; + } + + get discount() { + this._discount = + Math.min(this.getCoffeeCount(), this._discountOrderCups) * + this._DISCOUNT_PER_CUP; + return this._discount; } // -------------------------------------------------- @@ -183,21 +186,13 @@ export class OrderEntity implements Order { } applyDiscount(previousOrder: OrderEntity) { - const validCups = Math.min( - this.getCoffeeCount(), - previousOrder.getCoffeeCount(), - ); - const discount = validCups * DISCOUNT_RATE_PER_CUP; - - this._discountInfo = DiscountInfoEntity.fromDiscountInfo({ - previousOrderId: previousOrder.orderId, - validCups, - discount, - }); + this._discountOrderId = previousOrder.orderId; + this._discountOrderCups = previousOrder.getCoffeeCount(); } removeDiscount() { - this._discountInfo = new DiscountInfoEntity(null, 0, 0); + this._discountOrderId = null; + this._discountOrderCups = 0; } nowCreated() { @@ -217,7 +212,10 @@ export class OrderEntity implements Order { description: this.description, billingAmount: this.billingAmount, received: this.received, - discountInfo: this.discountInfo, + discountOrderId: this.discountOrderId, + discountOrderCups: this.discountOrderCups, + DISCOUNT_PER_CUP: this.DISCOUNT_PER_CUP, + discount: this.discount, }; } From 67d4d771e1278dbf7aee0f2a6b18a295f8cf0f3d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 2 Oct 2024 10:36:15 +0900 Subject: [PATCH 16/28] chore(deps): update dependency lucide-react to v0.447.0 (#180) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [lucide-react](https://lucide.dev) ([source](https://redirect.github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react)) | [`0.446.0` -> `0.447.0`](https://renovatebot.com/diffs/npm/lucide-react/0.446.0/0.447.0) | [![age](https://developer.mend.io/api/mc/badges/age/npm/lucide-react/0.447.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/lucide-react/0.447.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/lucide-react/0.446.0/0.447.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/lucide-react/0.446.0/0.447.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes
lucide-icons/lucide (lucide-react) ### [`v0.447.0`](https://redirect.github.com/lucide-icons/lucide/releases/tag/0.447.0): New icons 0.447.0 [Compare Source](https://redirect.github.com/lucide-icons/lucide/compare/0.446.0...0.447.0)
--- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/toririm/cafeore-2024). Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- bun.lockb | Bin 480952 -> 480952 bytes package.json | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/bun.lockb b/bun.lockb index af409e5bb8505f171401f92078ddf6015118257f..a013985ef8dfcd3605afa766b0e6b52b82d35dbd 100755 GIT binary patch delta 181 zcmV;m080P3tsS_n9gr>{J)DhTi@8cWc`}HTTZ!Zve`XfplvA=3Z^(WufpZ|pkuZ!seDzjds3e}NTj_$ac@j-f^J(gTr9Yspv~ilz32glz32kBz32lo`~f$I@#F-z j@#F{c9FA+KGO`1Lq^oR$hld)Y#Zj1Az#=Q&%AGo5P29Yu}+3) z0ltz;E0e%a2!jY|w+Lwgb$>twPT}Y@idP(rJ55Jh6x2~ zoMHRGXak-D+oAiuDce Date: Wed, 2 Oct 2024 10:38:03 +0900 Subject: [PATCH 17/28] chore(deps): update dependency @biomejs/biome to v1.9.3 (#179) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [@biomejs/biome](https://biomejs.dev) ([source](https://redirect.github.com/biomejs/biome/tree/HEAD/packages/@biomejs/biome)) | [`1.9.2` -> `1.9.3`](https://renovatebot.com/diffs/npm/@biomejs%2fbiome/1.9.2/1.9.3) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@biomejs%2fbiome/1.9.3?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@biomejs%2fbiome/1.9.3?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@biomejs%2fbiome/1.9.2/1.9.3?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@biomejs%2fbiome/1.9.2/1.9.3?slim=true)](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes
biomejs/biome (@​biomejs/biome) ### [`v1.9.3`](https://redirect.github.com/biomejs/biome/blob/HEAD/CHANGELOG.md#v193-2024-10-01) [Compare Source](https://redirect.github.com/biomejs/biome/compare/5f2287e005eadb261a4c78e4ec0fdc0d46068551...3d498ed6399dc642d045f9bb9e9782a88c6bd4c9) ##### CLI ##### New features - GritQL queries that match functions or methods will now match async functions or methods as well. If this is not what you want, you can capture the `async` keyword (or its absence) in a metavariable and assert its emptiness: ```grit $async function foo() {} where $async <: . ``` Contributed by [@​arendjr](https://redirect.github.com/arendjr) ##### Bug fixes - Fix [#​4077](https://redirect.github.com/biomejs/biome/issues/4077): Grit queries no longer need to match the statement's trailing semicolon. Contributed by [@​arendjr](https://redirect.github.com/arendjr) - Fix [#​4102](https://redirect.github.com/biomejs/biome/issues/4102). Now the CLI command `lint` doesn't exit with an error code when using `--write`/`--fix`. Contributed by [@​ematipico](https://redirect.github.com/ematipico) ##### Configuration ##### Bug fixes - Fix [#​4125](https://redirect.github.com/biomejs/biome/issues/4125), where `noLabelWithoutControl` options where incorrectly marked as mandatory. Contributed by [@​ematipico](https://redirect.github.com/ematipico) ##### Editors - Fix a case where CSS files weren't correctly linted using the default configuration. Contributed by [@​ematipico](https://redirect.github.com/ematipico) ##### Formatter ##### Bug fixes - Fix [#​3924](https://redirect.github.com/biomejs/biome/issues/3924) where GraphQL formatter panics in block comments with empty line. Contributed by [@​vohoanglong0107](https://redirect.github.com/vohoanglong0107) - Fix a case where raw values inside `url()` functions weren't properly trimmed. ```diff .value { - background: url( - whitespace-around-string - ); + background: url(whitespace-around-string); } ``` Contributed by [@​ematipico](https://redirect.github.com/ematipico) - Fixed [#​4076](https://redirect.github.com/biomejs/biome/issues/4076), where a media query wasn't correctly formatted: ```diff .class { - @​media (1024px <= width <=1280px) { + @​media (1024px <= width <= 1280px) { color: red; } } ``` Contributed by [@​blaze-d83](https://redirect.github.com/blaze-d83) ##### JavaScript API ##### Bug fixes - Fix [#​3881](https://redirect.github.com/biomejs/biome/issues/3881), by updating the APIs to use the latest WASM changes. Contributed by [@​ematipico](https://redirect.github.com/ematipico) ##### Linter ##### New features - Add [noDescendingSpecificity](https://biomejs.dev/linter/rules/no-descending-specificity/). Contributed by [@​tunamaguro](https://redirect.github.com/tunamaguro) - Add [noNestedTernary](https://biomejs.dev/linter/rules/no-nested-ternary/). Contributed by [@​kaykdm](https://redirect.github.com/kaykdm) - Add [noTemplateCurlyInString](https://biomejs.dev/linter/rules/no-template-curly-in-string/). Contributed by [@​fireairforce](https://redirect.github.com/fireairforce) - Add [noOctalEscape](https://biomejs.dev/linter/rules/no-octal-escape/). Contributed by [@​fireairforce](https://redirect.github.com/fireairforce) ##### Bug fixes - [noControlCharactersInRegex](https://www.biomejs.dev/linter/rules/no-control-characters-in-regex) no longer panics on regexes with incomplete escape sequences. Contributed by [@​Conaclos](https://redirect.github.com/Conaclos) - [noMisleadingCharacterClass](https://biomejs.dev/linter/rules/no-misleading-character-class/) no longer reports issues outside of character classes. The following code is no longer reported: ```js /[a-z]👍/; ``` Contributed by [@​Conaclos](https://redirect.github.com/Conaclos) - [noUndeclaredDependencies](https://biomejs.dev/linter/rules/no-undeclared-dependencies/) no longer reports Node.js builtin modules as undeclared dependencies. The rule no longer reports the following code: ```js import * as fs from "fs"; ``` Contributed by [@​Conaclos](https://redirect.github.com/Conaclos) - [noUnusedVariables](https://biomejs.dev/linter/rules/no-unused-variables/) no longer panics when suggesting the renaming of a variable at the start of a file ([#​4114](https://redirect.github.com/biomejs/biome/issues/4114)). Contributed by [@​Conaclos](https://redirect.github.com/Conaclos) - [noUselessEscapeInRegex](https://biomejs.dev/linter/rules/no-useless-escape-in-regex/) no longer panics on regexes that start with an empty character class. Contributed by [@​Conaclos](https://redirect.github.com/Conaclos) - [noUselessStringConcat](https://biomejs.dev/linter/rules/no-useless-string-concat/) no longer panics when it encounters malformed code. Contributed by [@​Conaclos](https://redirect.github.com/Conaclos) - [noUnusedFunctionParameters](https://biomejs.dev/linter/rules/no-unused-function-parameters/) no longer reports unused parameters inside an object pattern with a rest parameter. In the following code, the rule no longer reports `a` as unused. ```js function f({ a, ...rest }) { return rest; } ``` This matches the behavior of [noUnusedVariables](https://biomejs.dev/linter/rules/no-unused-variables/). Contributed by [@​Conaclos](https://redirect.github.com/Conaclos) - [useButtonType](https://biomejs.dev/linter/rules/use-button-type/) no longer reports dynamically created button with a valid type ([#​4072](https://redirect.github.com/biomejs/biome/issues/4072)). The following code is no longer reported: ```js React.createElement("button", { type: "button" }, "foo") ``` Contributed by [@​Conaclos](https://redirect.github.com/Conaclos) - [useSemanticElements](https://biomejs.dev/linter/rules/use-semantic-elements/) now ignores elements with the `img` role ([#​3994](https://redirect.github.com/biomejs/biome/issues/3994)). [MDN recommends](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/img_role) using `role="img"` for grouping images or creating an image from other elements. The following code is no longer reported: ```jsx

🐈 😂

``` Contributed by [@​Conaclos](https://redirect.github.com/Conaclos) - [useSemanticElements](https://biomejs.dev/linter/rules/use-semantic-elements/) now ignores `alert` and `alertdialog` roles ([#​3858](https://redirect.github.com/biomejs/biome/issues/3858)). Contributed by [@​Conaclos](https://redirect.github.com/Conaclos) - [noUselessFragments](https://biomejs.dev/linter/rules/no-useless-fragments/) don't create invaild JSX code when Fragments children contains JSX Expression and in a LogicalExpression. Contributed by [@​fireairforce](https://redirect.github.com/fireairforce) ##### Parser ##### Bug fixes - Forbid undefined as type name for typescript parser. Contributed by [@​fireairforce](https://redirect.github.com/fireairforce)
--- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/toririm/cafeore-2024). Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- bun.lockb | Bin 480952 -> 480928 bytes package.json | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/bun.lockb b/bun.lockb index a013985ef8dfcd3605afa766b0e6b52b82d35dbd..373b51507b49cf9859aa4c11b1166a5b27c94e4e 100755 GIT binary patch delta 1200 zcmdn7R(8Q!*$H}zOHZB&xUKs=d=j^O@T{x(zdt_SspYb#XYvud^)<4-Ki>;9`fv4T z>@1o*W2wP(j$%duFvA1J=zuX!z!)4QFf}kn2aIt7!qAv3;LXy`Qp&iUrIg9?p@Yqf z1NOEIHk{M%m-aaXPFTsg`ZHa&e0Q$`gum$gz8`_E?P0CVxIq6-;cb%xr_3zytrR3sj6&Ze7T`R|N6V+^R+MDu3ske?aP9wK!xz> zy%9j?P2>NRrfnvBg>AFC$ez+k=lWBuejaQ3ot-G5!QsQt&cmR8{#ViFmG__jcz5}O z@~0D5uVuGtJbLpvM`B@nK2JkC)Oig+=PgHeULmv5ZL{TUuHMTsH^yGtmH7Dc%sC~S zr&Visewej&=cDc7+LfC)u3eDK_76IcR@41oN1vDdOYHSG>$Hysy9J*={BV-D{d9qP zW|sDbNapPgkt}r`jK`)FPg9HOZ!$q^rPW#V&ieq6k)U(tB$=m|U zhy!K#KTn?~Z+CtHRAzeaLsm&?kOHn;Mg}pUf~P-?v!5t4Gr<%r1S()Op1vEVE*Pjz z7N{4;wT^vcQHJDuviF+ZKT?LiPK3yY(T@o%P zJiR1@T?Wq8hPpvGl>OxN2O;bd(m;=bK}k?)aEYr+3PTt>m@g2W2PgBrTmSBV`u8ug zGn-OY%{KZ{z1Q?Y;`G`uW{LU~miM`?3(dtBcraa=qcm;h>)^Cir`~6Mb35pK{c_hc z)5HD$UR;woab?Y6le|BAQIbc0XCK=8PWj3zjvXbhP6VYhGB5xUhX4Zu!$KAy0R=2v z#%Erce4V9Qxopo~?p^CYHl<_Bs!yJ88ByVtOTo#qFz4Cmq zt(ZAWE}uI4agHUgi`BfQ>xKiJ=YQnnPMtg!^9h_hAJg8hxbxJaTIt)(1PeWpc)gRRl`h^jwk*QOSms_}_FaWS1# zxF)F}ziyvMcYj3p+C1y78(ps@I@I;nXF#0?a@kU3=dp!f(OI$Bbg@z+znlE^SE|u< zccvV+)R2`!$Vo_Cl|Pj`!Q3&T9ZVZ#lB_RCKTJy(ZI_y;Uqht~Ew9++|+Qt~gW6 zb2q2;lq5{Dyt2Fg@LsXC6_WnRUmFd#I_C#`urBrXec0|K8#YPulymo|N#6F;1?rhu z+8ZL7w>Lzx)O9c#ZU6s}#qz(Y5zw+jIgAVv3=9ny%{DvjKldq)h0##YQV%3^3n(KF zl;Qt8eVV-8`2|p!>A4SCC8a?MxN;d8#DEH({xr^hqRh+$Q?L-IfYE6BZkW1YpgLKg zy3l@yrgdJAl%eXjzkkSjlZo+8`^hJ)+fP1WJD0<1q*sz&H9aweU3mNYKz3Hf=}QFI z6sOk)vU5zo8^o@I%H^7F6U;7yD#km#FPL2vNlZ3`oqPKAV0KwlF%=|kRtUQqDwk{e z;ShES3$SbQ@>5b3ij#{n3rdRh3-XIgGV_W{5_59&vWjs@O@9!=eo`73KwwZ3R2p33 Y>XIT6#t!Cdgt1T8-^HRhEsT8u09WVV*8l(j diff --git a/package.json b/package.json index 547e99e3..decb19ef 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "zod": "^3.23.8" }, "devDependencies": { - "@biomejs/biome": "1.9.2", + "@biomejs/biome": "1.9.3", "@firebase/rules-unit-testing": "^3.0.4", "@remix-run/dev": "^2.12.1", "@types/bun": "^1.1.9", From 9128f96c45f205f43b238a2e108134a10253aced Mon Sep 17 00:00:00 2001 From: Ryunosuke Tokinaga <59079411+toririm@users.noreply.github.com> Date: Wed, 2 Oct 2024 10:43:52 +0900 Subject: [PATCH 18/28] chore(deps): bump versions (#183) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ↑ @types/bun 1.1.9 → 1.1.10 ↑ @types/lodash 4.17.7 → 4.17.9 ↑ @types/react 18.3.7 → 18.3.10 ↑ firebase-tools 13.17.0 → 13.20.2 ↑ tailwindcss 3.4.12 → 3.4.13 ↑ vite 5.4.6 → 5.4.8 ↑ @conform-to/react 1.2.1 → 1.2.2 ↑ @conform-to/zod 1.2.1 → 1.2.2 ↑ @radix-ui/react-alert-dialog 1.1.1 → 1.1.2 ↑ @radix-ui/react-radio-group 1.2.0 → 1.2.1 ↑ firebase 10.13.2 → 10.14.0 --- bun.lockb | Bin 480928 -> 484440 bytes package.json | 22 +++++++++++----------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/bun.lockb b/bun.lockb index 373b51507b49cf9859aa4c11b1166a5b27c94e4e..f0598df13daf6bd4194f4c54e9210c634264f646 100755 GIT binary patch delta 85103 zcmeFad3;URqyE3oNe8j6_Xgm4IA2nj(XW+6usnI}SNLaQZhO6;=KP*hV? z)tHv5DlH;KrLCgcDymf-%~8eodG=Z-&F$Ci9bVtx>-UdyKh3kA_nODO&px}oYc|(E zT~vQj+b6qEt9UJQ^!WLG8-H*}v{+l39Jjp3)#q~jZ~Q*_z{KgtI~7f?sPVHcKcb;E zaox78@7$i8)NFrqu_HY_4f_ zpn*ywRVlSw%lNg(%~HC9KMrlyM$_s+Ya@LvXcO?qply9MtufR?`L9&I0c|D!4e6Qg z@phV4SJSd{4yy#k04z8&DLQU)oTh!D61uw44(YrQ|8aXWV_A20s{DgG%KTlS)sb#S zR9Z|rx>?Xk)4buALaRb^p_QS|^vO{PQO0w&>e(Sco`Wd(8IEcgOEr`WHDtl$kOm%s`rJCp}yd@}O0VP_qj^v5y# zD1bd^jfmP%3zR*m-phEiLYM4Ly=8qZpd66XP!8B(D6>sYPL7XrYT8D4F1?jd&eflR zGQJKxJ6s9M{M{_FU9*GaoHc~!V(QahwreAli>L(@Yhq-M1pzcE=gI(0YXrU2Pi8y{ z zrBIGY?V*~cL%oz{gR`a@D&IX--UTSTbO_32T>xd5o*JfUO`yeCXIPHeIbDa#b-5dW zV>vA@!2XQv8Ocdn<4jp@a+1?GBQDW7C8=#vRH9S! z0mot)loORcBMC#RRRrL&jn7PQqHfKZ6g4Tq8LcgtCXa{=XI6%9tTStJazZlW*G`w? z@GF!R_RKPhD>cZDMn7u6-kBlm{Y`1QGjSTrb?F6g4r3bY#wXz^HBE6zafxy17$tMj zYf<1Fmrs$N>rA^R&!R34kX=N@fbjN4u!_J`}`x%*d7n z{EQvK0`9?6`{c-h%t-6ph55E2y*Ux-zDX&Gnzj-i73I7!NAeX=Hc&R$_@Q$3Vfjcx z6dRT9i>p^sZ&X2__lz83?OC~mufy|17>n{4e;J;dqj>Z@nXb;$MzB}&Vc$F_Yh24%O;p>nc|wwzO|FPAqsfDl;v? zcWQE6(&wwC$5Jt?x$&Z$o{Z$IxXI}4i8XRmBG$@I9fD^oE+HL9Dv~OQC%W0j{X`O6tD<~^Y&xp#%On0VdCZ)vja`qJpVs|FKZd|L@Ji7?C zI&4#{LE<{l%1}?J7c|Kkm6qU~p`}G7MLV-nG%OpmQqy*>m+ec0dLvz8aWBz#iT@~rQ@hfOpBU=YoA(n4=}Z;FFU8fn{qa)LfOHSP&OdRnUNTk9T zPeB<`+D3R*;JQOX&obf?(zTgy$$?s<;$xf{zL{7cQ__8LK&D0A-YmqwuHmK>AjOi#a=VsxwCy!Y>0<$#=p)n zMv61N%XT>sSsBixXbi=Z@EpK;P_8zw?Z#`>+jbK>Wwzur^YGJp6w2b-L7A*6l->7% zviK@cPw47*49^<USd>QJT48|8}G^4SqU|B?~#4F z0Ii2~&cx)Yarlz51fKEppw*y@ReIOOY92g0;kwmVxAxgNlM~`H<52Mrd*z^+bL^*S zF8vCe-M}?4As*>wpdk7-AIfeeq$f{d{)|}c&Y0Ma|Gc_(*e{30wHceiGyg37(Y0(` z4darceUqnX`wqyS6=MXrj)#0CeS&kEJcGfRamvTC;s_{5st=UY*jf1}pd5jkN^Ocy zot}fk$F(PTv z*hH=oaa|O8A{{5ZISOU^n}?-#QTf$KX;b22GHYX9YLh>aJxI&qY-!p;tkYUuy7fCM zJN(`;S)SW*H2|42XY#yv?T6>kZq{=H@f?tWpBiu0YS4S-3Au7!fO20y1+4~k9hS-P z9M5#Q%C*VL4~DY&`#&>0Yd6nU$5Bp%if)!7qtl|M`%X%Zo-t_#?!nO-ZXMsabj^NJ z#;3~B);7bl8lNxawx|T{&c{e_b~q%(c&|>s;I7KHS8Co2Vk{@Kp=ihX?`1Jlr)IKQT0Nv=_oLD# z$Hw9Quh9?E*GV&ieHvs}LUc7mzy70K1yc}B-~Y0lmdR<8eLHp3v_CG%>GAnVX0NS^ z!L`X5m6)ad0nTCh9?I#Q)LHJLuT=WwP#xS8`56BcrUpJc=ROK1aE)VyU*v9cjXzFy zUoLys|6dO8(bI4agB9Gr7%NPc=mhQ?{a=`D((A+^ywLylhSd% zXs){%*WHcluEw}3N90Fjtb&5tU6T{s2+E3F+iEW2Ys1fgvWNNCWrvdBIU?%GL{8WZ z*>Kzp#7)8e{N-0H4Ah_V6#`tiA3?bQHbH%$`OwFpGZp_bS$^%UbzAn#0_6f<1LX$3 z3*|^2Ksxg2n0@+6cVvS#C>wO6RPwXXwoK6fPuYWRP%rrAP!xCU=WD5cO>qbB6G3&DV)L(Kq4Q@rqE+RR0PV zb6{5?JsUb7S_?W0%JH~@ifTYlDLsRZFx>9PP1 zC^JlsO`e{VrfDCc09HKD(_((jp9-x9@2n)#XC!0)(? zOJFt<;D!*_Qcau2`x|6D?PW2aJbnV@3{HxYBQgXT8^VvRDo3h6ls#<+A*;GY%Vx%S1X$5Wh~PDGPYsLK2->Hnoa0>h z4)9wL&nfa%Jy;3Pp?wL;5qcdxW;rXM+&t!8g>NG60G$~ban7_C!K;FM`p7A~QOA;P zws2`3i@CkeLD{2yP&QxVXo2~D9d{i`I5oakBr%4{;t- zBOaZM`{rr5VEyA;;{E&b_Uc?Ki`D`i?E}wU8{L4TiuGaE+7}riNX4f6-Kfl!HVx6$!@hFEg6$<6zcP$ki zFZ)kB!Rk z180x^`Ir)4o6Uq%c&m-)l|AuLnIq6yX^L+`^5m!tr*m4Ac}}$rz%JzCy@8?O;`|QE z7Pss!&!Os2o+EdmJRY9uA$xiU%7$Hma!RH{S&kdh(U%6uiZgr4^wVSGc>HM5@VvyO z_Oh7IODFWomP0WVz$_?GM&PU5WHdHr1TwIqlV})w(yEUf;`&gIz&)rr1%2hzJsu=0 z%!6mcXF%DZDNv5=HKb=bW8v9hZykUwZ`I%MY|gYqENF5(V{%T4N_YNeKQg+0rTfo*PIUczh@TA`m@RhwZurmN3x6}tJkfBV z>qkJ>k9@8l%KrHS*?;xJSh5k=tl{XcgO%)&M9McvBVcmAvDIUB*sDp6kI%Qc zvg5nf)hr&bR!JzCAG~Mrjz8MWuUsvB^`x%t`+u<{YL4D-(>v=UjqiH2^l8;@z+a_f zYF=xeRi{$J$G-jWl?#I}Ea`I8Z%08`$d#`M6 zsbW|LwKhH+;$eB&I1<;>($>(Y_OxUg1Mzpau>gN>7)S88xuM7Rv_u;N<9q6_K8d?& zbflu;J=m@vg^PsKjIeOKvVk}7NX|=XBijy3A z9GWAeP=Mu#F)+EO#m`uPzw?YE$vyQS+Mz0$HNtM`ZVXK6>7CkM(}t7btOn(()MNBJKjMo#m(d(vSJPq%?t&N;Dx(DD;KsO_7u-)>up{MrLFN5_3b2mzU zwA*5^iAEZEp#hd##u3Cubka2ZFUQ*mOSS9oAQ%j1<#5`x&YITG$cqTj`ymtvUd!aq z!}%LtAp!b6gt#j`jQmi$<*KnDy{F#TPt&@CRpcb-4ZCO>`jBHY!XhwkaQ<+$4eud# ztI@^C&2Z>n05XGx&{oY3CqhPBx_dAr`3`@Y-mXM?F`R==yQ4^l;q_d14+Q$j zL?L$Di*SQQ$XJww5Hnj1PTpf(HLaH`l|CA70Gzww9cH(@W-OTA)AEyXWO`3M9K*}O z)C?|^0%KqnV-{rfw1x*5#aRv@eMV3HN`OpeG4j*x`Y^OO)GRz7{aphmTa7r&ABH}& zr!}aD5j@kOzt_W6de|hp?up*8LpH;Etlc`mVR$~}u)gRpf}e8Ot|4NuaV#r9cl4Cg zfELBs^=vp!mS&W^?QiL6c+PTIoA)w;A&I?=T*!%DM)53%?vAPB9GJ7JcZQ2Jvw4rk zATdsOxm&P50*&I?4%=Wfc&Kq~c7T2XAr6$)2#c}Xn)JbR7$GwQY~v9MHoQg$=o=8i z5?~Y1+ppocv@OEh-D1}}V%1@6WTZ8*uTh-sux{>ac;+~4w-6U-c;y7>_Moyk8)DZF z!GWgJ{9OyS@8a^R+x#d!|YwDDyw7EY!t`Oe=R zW~kYFy&Hy7P8Ca^1;@EJ*Q9ghvJDag5zIijF6M)fj37Kku;p1mG}2#sugs!pc{k3a3UNjXnsc znu!k8L+NVt;a0&-k`2QSegN0aI5r}{HUNj349!KzL8ccY#3dsa;}58SdtACE;jV`A z_!$cqY~&5=?ty^n_Yr@Zj>zF=p7>4CB+eSThITmNx|ps636(L=LcIj8J)*F|*!&2& z=x`u}+VvwrCToKg>@-99 z2u(0Uf0TuWOmO9R3n3X>3G*|`2wBwK9f1gD(Dxw39f_kV!fw4a(Fk7b(0!xI7CzQW zG8`wwT=>>^q6|;NVS9HnmZ9Ng1lVlRJc}3#D+{eE3;k3U^275H91%#DjnDuybhs>3 zb&88cl!aE8g?=mxwTp43n_d>$hfqJ+mRMJ4P+2IyEOZK?zGk|5ajwvWve3G+(2uT= z-Wo>+SC-sf!_ zhX3I1!*R7@Ne;8ydL@{n8Wx}ngt)SB--0Dq0w~^Cm?Ua=BwGjP_>=9u+ooN%u{X>E$k&1;>P%d8llMGgq!CiN`Vh8!Q`)jLCMdR>_)% z>q?HBrfKd*eujBNrq2TH2kK>%?66zsry9ktI;_W14bRmMTk|w-?Y!^+TQowrU--Ja z2LgSO93vTqqY#d5H?O|d&gn+*8i#FlI!-7f1oseE5mG}t3P*m1oCFLz?-$^3Mc{&s zw(Bp$$+d~AjrBx^QM}e+ZIfwuzUI*5Gi4>}+OrOpJvNuIeg;nNTx@r1jcJDGI)@%K zO*Rro2PVUS>uBzC+d%~7_*?HzGlC~Mbo=!3h1-&0I~rbN1FV~;8^y0X^t%A;FD`>4 z?0RsPJWk-!{ViEWZh=ED22`~78%|%pr!bs`*T?`p8=+8etMInqCJ7EVZ6gD$wpm8* z2HXeFDr+W2Iu(wI<*9d!aYo+o0KM;QdCP$r7-hFDgu^|+m;l=+2;sJRaDcT5Ueb8e zp+`L}FFA6=cffItVZXfS?*ZeG1_x)GY(V#?7Y(9OG<8NwH~y%WA3lth|YEGfHsV z84cGPacYIW0jruY$gY2{9Qrj0w_5X%R5mCJL3M{xgdn#jwq=rCulSs71=a`J)KfXE zfiSymI$URY(b|MyM>7-eCw_+O3s=#|himt|EJCfg7+5An8HhXrC)>yJE6$gbVh)(K z&wQhJheLlFup2U9l5yqOrCbHG3D@94#j)nN`CcHuqM>UMc3To$Z*$#jU0?(kI&9wp zbT;oks^_Za7-1{@Jz%(j%>mJK;AAhj{I|okMI35Ey+6aL$(if#@q%kqcxp$%u@Kck zp(4#gT>n%#xm;}CFR~qZBLi$f49U^UMUZ9dTyEAbc}DRrhrTEekL&?G%|W!jq?#s| zYiDKUww(gUqYqy${_yvJ;o8M_)v8tc#K1z zxJXVMwjnm_>&js^Fr>HOx*B<-0`#u=a+YxN;9SUu>xF`3cT30dP3!bnHmDesJ=D(eJ==4%H&C3)i}4Co|zXGp%`BxDn1=W^yjCz?ub_ zTWr8GIb^DwTv)m6*!Lp14k*XdC_!)Uz=f8T+W!^TKytaxfoq32+|uV^%$B?Eqj7<= zO@+f6+z_CDjF6lyly3D{VHAJt&t$-B-$$4~N!u zgJZvNsheuICBU_j>w6i3s+yJf{-!eFHesc|`znk)H>w_rkm@PE(7y<$>cPFr$8df| z-qZk_+p9=rc;Rj)5TWj7ZM?ajO%7+CDz-gv^6RGlGlE=SxTK+r%~l)1M;z8ktBu?v z4*iwYG7inhBKR7PRpM(e3aq)t6&F@$w}!3}p6(Wh?K#k1{8po%Lx`QVh_J30mbG#Y zvCz@@sc@Va>2|@fq3T4wp^~CfT5I8w*VVIHM;JEHo)lFV6%Q(|+O?chNJ?^l*2iV124SyiSE~$xc zQc$*;P}DHE07T+A!sz6|4K6F#dZxheJmIj_d4r>KY)XKhf>0mCS2eujaiXm^icjDm zUoU%zUAD?@J-prsE^+9$06A8wiG4T7U8?L$ux$}*J{+;WzrhIp%%R@~WRE@M{^_t$ zcG)~+^f)*_L}DSlX1Bh&(Fp$Bp_c%1byVOZ2EEOjuI0#6A_b1y52wVN{vI$K=!zmN z4THH!Hdub`n*=A97)Rioio>A<*Xu3WBpiY$@>w{}IIb#d?D~groLKV?P`{<(%uA@X z<7OlHONXAgSth9^{5@a>n}&zjVK@#8?y&dy zTecXUrybTYTa4h-4t?ns*AVdnb9{?YjJQf$4bL+UYtOAlFeG)Wk$c9WuiL6=z9_^4 z&-(1TZJRs_%nf4e4>#1vJA-c$2yrNI%AxNb!13?fKVIc#DyCzZM{qO3FWM| z>#=Yw6=RQQb+7I+a?d;T(}29d$%X8(+chN2-W`tfk4A-C{P8S1_ku$|3@Dc(m#N!( zGP``x(+N(l{4hNEgyFvNG7puzaG@;AY}mj(vJi|5j;sxE-Q>Z355e(BW{#h~$NR3H z^Ff{gCr|cI5$c9icr=VJRQg^c_Y%Gr?UiL>31QI)IGz_+LKm>?KG4jk#nsFu3-?Gmr{t=Hu!I<0JeflqO za<`jL673(VRV}=uQ5YPP%C%uz4~yx;Q|faFaoEt-4X9<`nhs4uHGS6 z3HQ(XTR!Lf@tMet4b#1SL+H-}!S*tO01 zM0_9|hts^y>(3~M`myQvDyJ@Nw_!Qf<^-kqg%kY{Co5;e5s(u6<1IH{7dYIrM{LN{lS#W+oi_ zgp|m45YEB6)lxa`np1D=jv;WIZwv-*`{uxPg~QJ=$oL+dxw&LUw@>BZq7N992)Mp~ zm%0L$^MLCTE`y&dC(n{fC*-XzHfgrqy5)oseA8jO2Z(2*IE(w2$Teay?>ra6^=7$7 z$#Q=Wm_XBb54BsXe`e&~a_D_NlN~TmFWWP44tcyEV}x0x*XOR|!rZNJvXA%~M4t}V zURHpc_4Q>g{|`JXh3h5qY?!bwT$S*aJ_4?%%(KI8TMCE$Iwin%93ebM2??-jCynAe z4!!qD*TUhN&VlnqwhBhc1iQW)jwM!{dR&*|;FwX~ zO|MW6haFO$foq32Jeb0P)9PE--7>dD7Mv^-tHQPw4v%4RWZgn23VE=VR{2}biDIk8 zp?`8tjsW%^E=;!X%=y80iExCvAhEozz5q884p+TVcI(ye%v*W=$@8vL4_75U6^>(x z3&R=QZ~yJ^UFwMovMV@RkTMaD3l%#I4+&Po@q~rL_*{bPO((~v!$rB_@w6)yKO4ZY zXYx#3r5rNiusL~A6j!h~thVokXGLhQ??o_Ff9iW#f?Qf#;N%|Rm#m8gZ?}@zd%Dxs2r1aT**Oj`cCcZhij;k*lM=8=##*F{9WTEq{~_ zCj`F4j)mi8LY<<8u)(kxv6KEDDh??zhr=(+O(fTlZ4GQ^bLD@5P+v0-l3APlWaNfA z^zlE*cHyG(vE91vC-b|)T|fs?V!_1Q^{!W3yAq>popD73SHU2yzhdUUh)7Ny#sO!2 z{hwV)xg^HH;kxn$ZdqO2oEqz?pG9$1bhyzkxMazCuxMw&aeZUaqNt;A91ILmIDV%7 z6@$r#l{SBba1Nvf=>EUSb*6?S36>*-4DtRRFdSg>(UR>irZgW!cl=#;*DO^pfa8jh z&mpXTNT;jKr>wa~bOVmFuPQqTE61OcTlK1IaybEm;MfrL(CKy99_I0DJ%3f?)MeOr zC_4_;>?4<)wcwU0_CbLSZ_9DOs>FPbfeS(jHDn3fU|FDiE?Kcu_5m$Qx7+%|_2#=7 z)>le}=VK`9u{*8_^v2en2*(A90mH-nSMP{oq_JMQBRuON?YKY7{fKFM53Y}qcRav) z?@v+8_>jADVym0`Y9SoQ5-!qi{pzmBeH_mg8vey4!m}NtXjjn z;25aZ;=?B`dL*;2bUwX$0HgbDWz;}IGRj_U_j>6>ug?X0osnyR8l0|L$i#NJ>M9ryg35Q?d|_ z^M=#mjNNt}ZiM*-Gz#yDk0qB&V=pY*kA;ZkZLMfA-;_b$UbE}raI!t#*jr2B`XHs7 z2pfxU2`T9svy4Xs%f_wodw*t$ntz+2DI9@@JEs%$a$FzzbRjBRkt=`O$E!m&3vj9;_cT6@8n--Mh9sXdK6ufVaB_=y7L z-GrM2C%;BSRI!-H92{Olc?FIQktfR$xG8Y5KPgq^qJTqxK7<=4~zXwbt3c$gE z1#lUzw{*AxXoVNvC%Rf;I|~q@1!uH{XZJ z*ia;uyTYwDCfRiCa(_6kFZ5(Bo=n3rqr8NF49BG{zcW^<<8onHcD)ZAuQ%$(t(K2# z-LpT#;kahh*|{E;Yfhe$7vVU_Xbqa`R~Ox5QuA(S6&xo=e${lt3-=rrRZJMHxxUO> zp?o;*C=`0Z-vfrH6Rv*v(YT&$8j8f1k{mcroctQJ9gbySzHxBWejM41yz>G2V1(ov z;q=aeGsn;TTJ;W`+|%sa4d!79awoT{FZ-tK6xhEn!*9X0H{Zbf8X*oPT8mv&6EEni z6^{81g<}iNINJfZQ2AZ2T0_}?IsU`o%uQl=Pxg1Gk*PjIkfSSaw5l|+XlZPok$=Xn z--2Uvm%VhA5wL+rNX&o0p|c`aAf}-@*F`_ERluQ5MYoJ9y&nVC|OJB{J>kzk{#- z9lTH$)SEvkM_G5X87uZvpO`HI{=$e_0s#nh;_ z!m}^#1J<^ctF96suUgNv6}h0cy6xDtkXTE1cZ7P2ygs(5u7L+?uUQJA=0eX zmWSYI8T=8!XfuKS6#k_Ku1vXE3gNina3*Z>cZV4-j-kc2ft@*p=1?qTkU!JdP9ii> z7C6MuqK!0@>IDc6m7T*Inm7GKa4<$kbaCAt;5(7+Te!jIA>PW4keNCR50RgRlYP)n zBFH(zS@{RPr}@iP;+Pq3w~dDzAoA*CDBnPk$3C{;PP_gq9CyC?;KkOgs|7!6dL`mk z1tE6QBJw-o!Vbp`ho^^lC!`2Y9%tSs?D~D>QtPstU8Df!I}q+c|2L~ zcN4{-=xetCd3fNqrpVtCAcBVgZUvNABp!M}-ODbaIHK0W@d#Iy*6AUyNjS7|DO(0N z43St6BhYq-yk$b%al3689PW(p4$Lxy)On2;S5KSV3roL2PuV;9bvYM~cgga*+!t_M zhA0b7s?bZu;hQ^}lnXbyEbevU#whQVU2`{WN&VXZwr2DZfE)3to2U*I!kh&ReWLZMD!Ey#j)oOP!A&ho z`AJzEo6??a`VEtFjrl~XEV!u}L<@**8E!lEUZalH5V2u>bkd{$5=Tpp_O^1B?4YXi07!Oj&pb>`rXHD#on0`q4q z{U{vc%ypv=j8MBkuI6>Hk+OQ+2Ep*yMoO%=XChtoh5h310mEe`D|Z_u2f!*yJg^7g z`iZ{7`>)n9)WE-bY73E?Dm&{xourbG_CcBUd=a6(?sjo#7Q3 zptm233Q#Ogp9gk*0UU2fHR0VFrw1Gtn7l=4Jx-2_yevMYoNUe~aI$eIU4L@CD_{PX z{vI%LLb1TCpN$uuG1yuj6Xe#!lQ`U^kEWBChFb`x$*V~0MC^Kb6&Vm^(VmkDjw3it zUbwbTvS>49+BTE1Yh;4u2#%M*QUq}g2c?o$+v76P`xc z=XSjSjwcnqJ>pDw;1r(mSkwKch+wEKcZzwnm=R$8Xo@H%sTzZ|AVT7CnHhs%SysIe zj@!>H$W|$qS0gjoa0Gc)<2DZ)zW{EybZ^?NrLiJ65%oFZL@_l#PIx9kPsfR1YW1li zml`ou6hm!qPQ{8cyjBOe{WjHN3`*`{4U8AL$w)CTUKCSL#|zICq^zHSc{CqHCn6MT z{t&qbA@k>0+dYJah>(gHvcZWs@Qjep1FU(8A~zL9e3>YUp|)yC*p}w+zrzvYkeNrM zH7`lzrXl__&;TQ^Ai!3VY{5&x47EzJXyIlk8=)a)=r}@y%uwA_ixy^vrXmz#hTcWU zOlPf-_HJ>8#nV0eaSi{OL;O<_|KNGGX2U<<;U6phxqyH8q~jv~;fG59J^o=jJV!J? z<+TC`p1YaxH=y)?;veS2qZ#uAMv zXnv@b5yI9pKU4-MDqp7A{{#>5oC@qq^YhInQ8|)W9_GkRQ#_ji4$&;7 zbD=EYStzIC1t|Wfy@)^T;X<1KL|M)f#52Euvci>6^4Fj&Z=;IeB)v#}%2LZ5gdG6< zP&t=-lzyc6|3*25hmoHRIs#<}jw*i~S_S@VE<#3}QxWHtUWAhWp!{X17yL~qd-kV_ zzo+6KD7BzAc0gyXN;v{m;fcKQhxuzmkwvRx#Uk`XpfNyoXeTI#svDF&a6tK?vO$4R z7SIpM0{W|XD$@^Ad@z(14}-G2QBeF(8-qVw?9ot`7n7|b;uwn$mBD!YsSAA;%7W)Z znc)Q}D}E8m50ySoc`E%&%2U~3L-~Jdc6q>x1QK%ltb=ky3RTAck2L##Q~^g|mugUX ztpon4;{P+n{Qu7~{69|5L%4hcICm#hPs%HM{y8|~zko8ym-xfEJ*)WFihs*UeEvJi z`ptx#`wOa|i>d%B*TV10Q)|Q9(3-x`flx*bg0g|3{G*h?F#KVIhbd0wu833~YSv#K zAo@Qf;J?DIO;#DGEHGO6hbg0CRD5}5L*o>u(#N~uNjn3H04z93C7{wLD_>r#foCgD zWx5=C5i-Y8Cz}H~PeuQmGTU=V$D|AJhs}6F<)bqGMdiyYd7k1_=3A&dG}{FED#B0^ z|E4TpiAqOh1xuBu(hKDurkv4Lh-b&wx?w_@;Wd@vVaoV*D!#muzYb0UdFrQ2OIvt1=DuPkV%;#B%~phUYBf0(jC?;)N&{SZpOU+DprkIM3VKUUyj zS_27AA)ZOT!XHmj{~0V9IPDTF%l(0IO1by0DNkkmb)`3;9Pv`+?lrOL3zKT;hP90VHPD(qg`0~np zek#6;il=gTySn$mUo-H3D-(2A8G9&or~=BXMa-V3j+j1(=0F6iWdEj27p96G2IT~d zQ2G8%*`IKgj>=|6y5oo;8;d_IXuL}Bzfo2&LFK1%N5?2nrB8;ko>awC-LYTTk_-i? z3}z|+Fl9wERJz$v=Fd^-sjOhG^8cmsh-U%wpe*<~rOzv!59N?Ag!1z+<&t_?rC*}b z{dbi0WiM43g~~`}#4_cnyvtmx{KJ$zUZ?WC4yAuX`SmIvmE~?wx>53MjRB6pTTo_v zTSoi`WyURvQ|Y&=^xG9LuN<-6ic?wc9w^a1{9*nNX*6+YzNLydI^W`9{^2dOcEF60 z%450+o+W;&IF&8_49b!|SNz|U>Aq0usI2EprKh1R{~Wgi6Mm->K1>;Z9`V!*Djk*n zqVnaH-$;H{{J&PbQo&F8gly^WD1be_rYgFw3Z!y`?m(-GTDg{n=E3a66^Rd(?W&@D zd1Z0lia$)5ueyq-($|2py1Gy{s~(i)G*G^g4Fia<){KAtq0G<%oCUR1@#U2TwFalQ zRq5KPbPv;pNEfQo4N>Wa*y@4e^EZ%-Za5NniJQ5WcH;3Du-5n|n{%F^a#DH8%Yt%n zXDLo)%b!-B${u7xneG|IsSM6n{$a|fJpADh8XPJX^fCZ-iPEJ|4#6u>R=fhrPkCkf zmEhjc%_`j%DEU^Eo=U$>c`E&O<+t0+Tb1$vu_^%*?m$BJ@LiSRVaoVDD*j>0a^6Qg zJMsaPexFLek3+~00V6(y5*<|jkkZ41`23r)f+H#&mE{#HPo@7v=~2bAT>&cB!sk#9 z{g+UtImtOY1^eMgu4dY^VyLvZsv| zZvtidW-6Y_hCQi#d1bmb;Fd-R_^N~*RKoI#8_|W9I^yw#+^_%lAM;Y-r}B4I`KfGZ zHz-jr#mg(Fq`%@1Q#O1M;@Qwpm9D(9{2>)EeH?)>m7u(m4_BPZ3?rcABbAR(@l?h~ zLh-m-TMXr$(sC6~Wxkcl|E*}AhX`4u?!^mKJeB3Wq4I4|oXVNptaLk+>2@gH3FU`M zUZ^~k{vD<7LfM|(%D?9ti1z_FV*8-{lvj@EN8oHgk&1tqGTl)XPi4i&lrOJLR|3wN zJY7MZW2aTZhbc3BtqS}G${w6k>8XsrsC;>4x=Y|}$PX%>%8vd7uzUYdrnf5Y(rgBJi&#lTP+5Q%lo_fhUk%EJ z)Kt8d(%Mjds7zl+`G+amrqxG0N1zcD^<-;J5MaU0Rm77}e#$EgZl(CclpW}#;>#=Z zbymE*vOa&4WBz48Dtq1y5o{s$~ge9E4xd>XVW{2VAh<&`7#tl|$-M!lfosZ9S8lq0>UBIch%{IZG=P&Qy0lpiYn zD@vC`nf_H3Ph~-CpgbQ6lwYrOqvCHvSJ&kLg`5re~JUf4+3S%;J3X6ORt=iij+>`2E34p8|AtNbBK zL!lg@p-P8A%?G}d5a5T(hB%>YK%CM+)53~s?6X50$pVk=bo9+V??2+9hIlomtzDX&DIDPCT! znh0HHX<*)cpFuna;A<%B`$naw^0fFx@n04HH)a0cR5~ily{bHw{sxp+kUu@tsc~Nc zcMKmpQ~}C7Rg|v=Wx?J`t3x?cwN<>2(#N2zpgxpe0sWy|);*ydz`jtH-%t5LIv(G$ zC1DDTgjNtCui$iGz7&Nsq4cvD4V|m_^GaWYvb`^>_~lUiPg{pS%=adg^>2YPf1%Pn zO7}zAu8*KhU(El$I}@G&V8)Xw;%lWBq0Ddz$_`&v{tA>0xd!Fuf2;VuXU(*H=Jx1? z26Np#dZFRb3k{E6Xn6EO!++lI&Q;D<_|4COj|^g;t)c;JXVdZFRb3k~JoUwHIFgL%*Q z=!J$yFEnV6UT7%)VgoN0k6vgf_iDqV7aAVD(11Ph=!J$yFEl)Qp}~Bk;?WBY{BHT^ zg$DV}^3e+ok6vgf{~`mnhWUcSqZb-55RYDH_)lJZzzYp?_gUOp^?J}K+ds74tlB>m z-f4TH{i-n=zidCPR(#W$2PbX)`RK1RzMfk5=W`RgRbAK8GQPcr^M1yN6Q ztsgev+3G?Y{_eq_EEDzY$DZAp@J!{!-3Gndc5z`F?`CiQv?qD-4~eChzuYr-Uu4W5 zqwXa>xYqbkmwiJ%Znyfg9izs-zH3%=%=O{{p54w=ukbzpy?u*zzv_Lc;0jhB_xG=L zwhnsY>Wb$-@mO=Dd;H-)YbSkI`eoqNsrIY=#_oA<+wodHqII1IbvMQQIya{Bh}%Uo zy*xjkbbaE9DX-OEIHrU58)ENui_hSvwW&`nyX&**?!Ai*kA!-C5ix1Zy22W*eh%O9 z*tPAULkyoTi^Rsy9(@1#*fyOv4;nSoUB$m z$hu+i5+|=)28FC}H5Te_w~Z(@&ODfQ{{1f&w0~m9><>pxsakz#!j72e8{UZzl7bq{ z_+$Ig1J#4ieLnd4kL!Q>`1EFnUM^l;_tytM$M4)}z)gbnBCr%- zz#V{sQh<%(DnThh_#J>vV(lG(4SxdI{sh=8!u|w^ybDlButiwz0(krd;Jgd4O>86B zMNsQ6fE^<0FM!y400#*Qh4(!GpZfrr_W<4%`w5B&n%xK3EmH3TWIX^lNw7yWeqf1M z($eZB<~@MlD@y23TfLU}SqEA6EtzA*SXf{$!R{9wEb#tTBwu0y_(+^5xJ(da1vn`3 ztpLm1tX_lnw%EPnaOA11f@gQ1o7{g{zvwoZT@L?p(>CSAn2n(e!m}@4^_=atc6UzF zzWYConQ-pr6T|0yF)y#%^u6gH@2mXXg1BFJK zpgX_@8^C6FfMengL1YDhaW;TY#YP)|M@0ay3IHWyR0V)t1bYcS7kWj2SPy{2iU23Y zZUP@2puPvdDG}!ZP(*Ny;EbrN17vvu%+{@gLe7?7r}TxAULFzv=f^{HC_G-@beDp{f2FR-fa9*4t@b?1fQ5oQ(SWp?@GQqC| zmqb@DfMrzxR(S#ZD1IUsP}S;{-S~XO$hJO%hV9%xGJWhbYXA3ZDNBt>ZE&poa>-9EII9k8rR|V}>XuZCAanajzUMM;nxNzjR>qa$R>vg5> z$;~+d#Xkqm|K@tqOpo86wja3n&fV&Fb{7?i5O1r`;HFOhW?^jtf4W-Px;%XI$(U>7 zn~9wdCSL6^>95e99oww;?e%h#=;PDg$UYP~df58I6W-}$FKAS8#L|Qm{lJ5E+Xt3w z-tQu;8hY(f9n)4=4a0C%SiAvt5jed8u8VC1u{8i{RR_2!qN)S<)C4#P5OTY$gE&4% zeRsOerg*Z}M2vzPjEj=))i0 zs$n&p6aTp1Xxuw9a|8DLS?l7CiLZX~=7jgpYT)DiZp5_bOIyUueec8L1r-j7 z#9A1+z%~=lo|&-audy@lzdp!u)H>>!)m=V4d7)=j{dVZ8N$YzX-#2q_do+4xVBZcy z`>uP^-WvPErVkIM{F=6?^Rd8{4PO}UvEunHb?1#={_^5)m-}paaa7*7 zUytqgEC1s=u8R}?BhqCb_w3NVo})v0&W^njUh2ExjkX)wEa>!nM$GBodx)`bzkFq9 zTI|Jxgu&{LtOQzYFh_cEfz7iwsoZN_W`*?;${&Y>wsJ)SyBfp#wO0! zL8fJO0fKx0DvEp`fB}yI+$7LNU|oPxf`YmLmBdv5OJyj zBgI=-9*0yHqbN1RHcCyQ*N4;+QIy(ZH>Hm7ZUFHSag@4ZKjkq|w;`mSNTob3j!^20 z#*H8i#0*M9Q34V3o1k%ijnTNqVoqc5CgKdGsp!xI@`PAGX(rB7nv1SYAuU8crKR|Z z@}vlS0@6ynLTN3oQrd`t%^<#FEv2ovLun_%nnT))jg$_;(gM;^jG}ZB+bErd-V)*` zq9|R&Zi-!aKMC;{ag?rNKc$tY^NSJFsN&)aCgV8?F4H!UA!x zBMy{8mW%(;VcEyM)rQ0Vi#Oz4^8LPEz>f*9>zB8F;M?%YQLTT-eXHi#>}xBtcUEna zGQ4Q%;5FOsuN(jFfelgDe~q~0wq<+q$+tUQI$uX@?u4ziwEC<{r6D`Ne(uNZ9-~&T zyW4y8{Q4h0@C-=5cJQ_DC*=&;*Z48NZzJA$?}b2ZSitzs$DgTFaOl(aK2_d+@cehZ zD*uK5PF>l~A1=0a#4yBm#xT_CgmDQMQJnyM`~VITLIcx$4seoSoM_wy;55O!E&vlm3Bi1S06#lGl$c`&@b3z6iD0tm;16(_V2M9~ zQ=BJQ)(s%2D?p6M?+P#=0N^G;oCxd&P)bnH4Io}zCD_m%AUpsdQLGIBi0lDi>kg1C z!ny-^H~QiY`lz%Bx34}f&BjUcusKrIJArigL?`1AreNHAS^_XH>+$m|I)L+mHW z3Iu4@3*ae{+6$m%Z-A2ovqj@TfYSu?0s*o`3Bmk60Dipz=7>4H0sQ*{Tq1Zzbm#+c znP5pDfO+CP!LlHLpuPali~PO-1Ns5nBv>E`5jY0`7-AbiY%oBrfdETH)Ib2AK>!B{gzydqC?d!V z26#p6C&(HM&}qV0WK3P2?Zz+=Lwb#1qd1fuwLX30T>VlaFbx82pkGfN>DHqV3W8?uwfWLco@KD zu{I1KayWo(7{C@0HVnXH1VACdHeneKu#3Ps9AJmoMi3hgP-_H0p@27Ai=xB zI~<^hATu0bx7bgR6#>v}B)}e#Iuf8|B*006y`pghz-fYc5dix{3BmkP0Dh4G`^B6{ z0RPbdmk2%*9Yz6MCRj2G;Gj59uxt!K&}e|eB7Zc%fUy8K366-sF#x3m1!Dj{5myN| zi~|TC3vf)V9SaaS9>6vZ;8PJc4!~mqKp{biu#5-TMc^C{@VVGV5IYf|)&zi)B5DGF zPZYpGf>XkKB0v#A=0t!qVn0FFB!Fg70B1#N6hO<#04E8)5sfDSoF5DjpdU`aH>EF%o`$-CCSKS6X%nWX;~^jPzpdrk)HxEAPwLq zfi4150ZIu9QUNN7s{|X;0m9P&yu{ixfXEC0TRK2h5ta_%kqJ;p;4Lf}0J{jB82~lJ zHiFn`0JSm!YKf>!0H5gq2MOv3?`Z%<1ewzS>WcjYSy=$hrUTRysnY>k&Hy+`P+v68 z0ys@DFAJcdC?S|X6ToiMzX(u7koh9OD6yX)>m`6@ zc>rTXY92t#g#afB#)-x+0h}h7_Y%MaQ9>|(5rE%9fG9C%A%K5Az$JpoqQfG9%LGdn z0XW5Zf@O;Vg7N`kM1DTN00ZDAL7WI&3{Xl?uoxg-TqW4>GC;TikSNv~0Fg@oY%c>O zi?EjgJeC3!5~K>t5`bL<&LsfpVjDrM0I0PTAX7vw1@Ku0aFAfS@D>0?1epS0hS*P# z^$I|6>ZLcq#(>AqI(ern&zj(UE z;w87j=GF*n_qx|Jg(=56O+TC$pHs?U_~ zQs+y118qmw=C8S*yMDx1ULhTA)#k4{wRului*rM(_suvOX!-A*Z;bmQc>UpxU0(jNT=V9M)Mc20 zWveg_Cou)MJ!||5z<^f)=Dh;2K$H-a68J3#ctOlr4zOW0z$Jn_(P0HZj0K*060moUNn9kV8BLzd9MR(6eR?u1bzhoo5Y*~fDLZ~Tq4*kI=lf8 zxd~v&8vt9xc><5O0D{&7Y!mtG0d^7GB-kMWHvq(L1}NA7P$;ev_`D4ez7gPEv34Us z5rOSZfZZbOO@OQ|0EGm5gk=*z%dG&;O#pkvHiFXxwcY~QC!*d0n7<9+Ai;j&y&1rN zJ3!`UfRDs}g3AQW-Uc`*Qr`wxwgccK!C}#O3&4P#0Q0s091$f1r38Lk0X`9PwgPM@ z1h_?*asE2PhHw+W~eF+$8v11nvNc-33sv1K^~%O5n2_ zAbcmlDY14ZKoNnh5a5gmD+I`T51^3XtgyTT&~gue^BsV1#5RJ{1hw7;I47ds1(^Rn zz(IoZ!h08h|6YL1T>uxweuB#c&2|G^5~;fZmVE$llHf2k?_9At)vA z+XL{kn6n39!-oKu2!0hE-Uo=>53uBYfZxS=0*?a#L3;tNiu}C*y9jO)To-{K0K|R- zQ1Ai3O>vdL=VO5IeE_$`+I;{;1hx+W?uf7t0kRGP6cXGOmi+)N4*@v$1Kbna2u>5! zIsouML>&N_e;D8(fYstAs(-{ou?QsdBM>(?ae(A9NwbeZY;Gd$V~}M>Ku(fWbQ4Vu zf($4InRgIGcN3qHl#=)z0;%LC<{kps@CnEz5-&H=@i0i_QII8v(VVK{{9!c5;}}3t z5rDVIF9O&_u(=qZhPYD<5PKY8+$R9F#Kunmd_D#6Itoxnj5-QXM6j2juF#JGWSsy= zJO)rt>?UYg0#N@rKz$K+9N;v;F@lDo?xz6rKLeQkDL`X!guwrE0N)b;O~s580GA2A zA!sI^ECE>d1wdX2Knrn(V8BU$9-jd`DHePNP)hJCL2J?VbASzB0<8KRz*qc45P1q9 z?PQaQDK&-5m-9_u^10P~6?2c#-e^?anrV z!t=cEcfGlKb7p?$e{{|q+v$uMbcuw@UL&E~63Q9>%Ls2JOudXy!CaTH{5nFZD+ra$ zq$>z@Zy>yqP{kC#iV*E4!jh{9)y#7VTM+qFR21V;v){pnBt*J*aHtJG=pkY49|wGNAvyIPTR zmter>DuEOHJ}kLiu4dF4QBU-3xMt#oN%?oo$hqnAjeAY%?3-UYym054<-Bu6u`Mb0 zXiU*r1C!ku8S`ziWh1u)ZVeh-)Wp86eZ=e3W*y^sgW5cDo9b4a5~kimXm74dSpEp1)P01GX3~9xx{ncF zN$6~fKR}4~1YyYogf8Z}gsl>4Jw)hc7Cb~~^Ay4H2%(3m_6WiI8Ny}>y^QB$gd-AK zJx1tbHc06G93k-&gnp*k6ND@;5Pp_0z{G!wa85#xrwE~DpM;TrA!K}pFvxUyhLGjA2@mrrAdHNTFdz!TK64bobH7O&73YBIBj=#`RnE^QYc!lgW{{l2=DeIE z#y>jFQ8P-;F>_tcag#p=&IvO~&Pj7$&M8woCeANrhMd#pxtw23xmY;AnFVrwH}B;9 zVXDQ(Ib&AHIcq%Q;QVRo$vI~>$T@GK$Hlo|n#s9nw#m6<;>W|eY}(4XV)n_oYLdlQ z5fiD1@hReUb5ugU#0WVPAlx*45+K}@a9P4_lQkj2^dt!55+d9+=OvU)icmNa!hJI; z5yD#uk0m@b`4b~7_ePkL7~!$GFQINSgi1*eo|+j+5TYeV_$cAIDVG#stAsU45&kmo zB(zC^(8wF%m0979;O&DDI~l?oQ!g395ed5_yfe|0BlJ#*&@nl}-)5VHEU6Gura<^; z+NMA_C*h=o&nB4=Qp-fMU zGAc@K^$~w(JPYeG%qlM_|?_p>8gO zN;wdiwdFvF=7;c6g1;%36Je`_H8~M-n|Bi0_#-s(MaW}T_#$`*AjHmvkk{19g>Xc| zE(!TfbU%dNxe+@0Arv&*BxDIhNa>GI*tGRWI49wxgrX){0K&*T2m=BTn6*jB7le>A zHv+S^+z9t1T$WJEWDP`^o)=+UAVL{)UP9S?2!-MG<~5=OvUafl#;@LR&Mc7{Xf# zk0rD>`86giFNrXxI6_BrUqand2$g~nI-4262+>L-e3a0|lq-R-Rl=GQ2;Iy(32n+C zG%AVE!>lNY;9V9Wb}58jrd}z8BNBE==wqUnM(AA*p<`);erB75EaeeWmO&U`+Ll2$ zC*h=oP?M}I!pI5;1Ii){GDjuktB8=Z9KsOOryRmP36~`dGg-?cOs|A6t~|mBb6!H( z$_RxkAdE7jDj>X-@L0kalfNRu@+t^(Dk6+C_a)S=icqN%!UQv;5<;|U2p=U(GUX~G zY?ZL4GQt$|PC}dN2#u;BOf@U2Ab8h6h+P%In0i$aj!4)gVTOrb4WV~UgpSn^W|?gg zveZIISsh`HXnHh4{hd;0D)jrmgconkn zp>%ufU3`*}ms5uQj+;Jw-zcwjUg2R=z3zF**Ieu170t1i&$7h`|FyGMR8NU{2Jlfl zo`VgG&DZ^S`(v1PAbYm$-b#zFnrOVR(5ZFp+@V8{5MBn47B_s~6fff$qgA)&J=%6? z>9ML=_^HlbqiwcSHBJ4lUP~Q$_~>I?)2yFY3X`UrS2SDAx+YsUuL6#?op~d;5-(#@ z;?aBbXxmZ=MKNi6dZkb?ruFoSl`O1pL|(eJ>e#bYS27{Z8D)5}o?bo&iPI{XZ~L#cD_F^>@`{}Ll&|t2QfNz4(YBO z%pD*8sh`)UC^2FXo$8^^((oulyq4NXCw%QNuhL$QZQGRtLWVm-4%+3axY4`9caQSg z=w-sXc^&yislw5>I_o;T<^-?#UXC)?cv+p|sbS)UGSXI^;^k*+e#20OzAC;g^Ud(x zQ@j>>+NRu#h5L@R ze{Oin_Fz|PryCR6dD&deE2`x<~^(-xsrRg<~ zEiElEnv&OhL)%zdQp--+39&S9mlo=LgRA@X!pST{<#Cv0=>9;9-qkq9(%c_w(VH&a zZ=rU6xP^1QEi;C-+)ru6rw@b0vb0o|rf=IM(&vBlm)a5&aqMjwra@ClxDP#2TUt6x z(+hPoSXu@vtloi@&C)VjcHU??&?INJ?DUzmoR(ddP|GkmVs1;!W@#zV3R{{!_o736 zgRzLEkCaf<(sCoHMWh1vwv_wLD5=p(TXycZq=cqHe4}FWr&p#Za9X&9rpWX1 zk4l>k+#l$wWZ9)h>yDk8Rb@-dz_I&>t}2$65$z8}%wJVY%fxXv_j_WiSz>0y9%_mF zRYz04vcLsPt7+L~MQe?whF8n7%f@kgORHnq>9x5Xt*~`1EeBc?ORJ}EvnXRZp_wJt zw+wyJnp;`}OUs28pyKl9{-BH>$AM^S-FnxG;`4`6mYv?KB8?%=BZU=TQ%lRO_UB`X z%`7nxO`k;3Uvo?2!|5K!tk&Pc(t^tty~IG%1bxXzYV6ir`4 z(CruE{x(lBPUcz>cDD?RqlJO~dRSU8$Ag>-W4@)8;5@1owwI-qMAHXK)Np%SS}Be< zbF9BUmR4Hj_vJ+QWM4}x!?A8H{q?i7vK$ZLShs0^ODo5*8k+v}jULrtc{pU1cp#b% zE5Kn(8-%7tT@j8-Q~fJq1+D~tScXF_!^&v8EzSMio+@a2(9{=# zD86do{t(Yr|GxePTU{usn79eYcSJydlXLtcx(k; zX=x47o>|%|OKXI77)|}dYBXh-vzl$7@UL!Le-g_tuKAC&wAE)8*24f|On_ z&^JEx_rbF3&9S$oeMD3LxrwtePJS~%I#>XnSLrWk%!VZKhRxLzBQw{3U-_+;-RBbUV!!V9VScWkzZ4lZ>ON(V`gVEeCS&VII zL(upRy|X6u86+h<6!hA9)nr^t8^-ZeOAC!>iNg_-Q8lU}eJe?UM?i8*bAKOcBwAOi zbO|lHQD}F`ph}qtO{E(RkE~25vFyg6=?##|d{UQI#$zledV#)j>TMa0seZ=Cffi2nNFt7z&!x4u=si z5=Oyj(7bjmXfCU-tW<|$P#l7xguXyhiUS7~5Cx(_G>8tG?8XF5aN|H+hzIc@0VIS( zkOY!~=C{cp1^7TJ(EK(Hq=j^l9yGtr2$>*@Cdyek&;&O-XabuH{2>4|cMXI*5CnN4 zKNNt1PzW@a)!bEc)nZT_f}sSI1kG19KP?M-8ErT`gXi$Vn}1%yE6{xPExd#GX3jD@ z-)ba`4eX%Lz}^8(SFge~(D#WuLMP}9AuS1e;-t$-lzhIrMj0@LJdat6(+g-R8GI->zUf?414f z#O(!rpg#APZ!LZ2H() zQu5*r$sjpsy6*$dK8~(xCd`64Fc;>*e6V5T1$J;i6i|IdgXrK1`Z&-j_yzP~p`)M` z!5-KP`}C0@EeCeNZqR3zrht|M)4;$^TH{`*$#GdI2U-T?fqbB^XnllF@EJVNJi!KD zpw&SXhzijlI_OhjZ{V#yx9|?$!wEPEr$EaGEf;=+P7nfJpc`}teY{K`G&8ggEf;3O zESL>P(09Tvm8GiV&N?81T7La zgC_i4p&N9E9?%nd0Tcgh9(_3I3;jTo{<=^P8bTvz0!^Ws*|yf6IvsO#4^7E6-PYov z3}{VQ2o}-#EwPzSTkS|Nh)&j1-A6J&-gkQ00% z253F;6apa+eJ6_bKjGxCYnZ2Hb+%a0l*2MFcOyHH;|@X1Ghh}KE*f_nn6p*fu5CkGJzIAS_)`ZrWM&T z(DE-a`du=96+*A^&vm!~YlvVi{LJxem;-ZR9xQ}Kps&wq#S{w?Ktf0i`o7;iI0xt9 z0z8H%@D%<4O@j5M!^ISAAWAIS1!q3xvZ8SP5%jElh(c@FR>5<)5)I48ou#w1U=918PDG z&~i;vd@XRAKvU3u!dPy*CXgEfAqeuq819hL5RJPa2E+t?F=r$6hCa|2Li_Pge;5Fn zAq!-MY>*vtfFC4)q~Hz7;BPYZl7{pOUc(Kz2?gMH*bQl@k+d+EjB4qpfhG(F!C=U! zeMBuxFTxtoUcy5f(<8VI>oD4ZyBLZ<0BCKfwO|cOR}*SMZKwlvp&rzS2G9_m6UPhq z3$DObm_%l#z*N$Ae)u&7qLyt($bi;>TH|X;uO<8*(8g0|VCT!j0Z~Bvh>f5yT%a~C zg4T*xVJZxV5uhdDAQ%ciKpUt`;#!xsAbscRH<)Wy0t&!bay=e)Q_cHeKOBJNumVS!Vwq))u9H|g4&wg*5jZ) z=tEX{U>up51e0L}nOF&{U^S$JRM4GF^nlkS_y#(_Z-m{7pS~iw4mzT3!u<*KiOiCa z1I-t-Nv;j>joQc2XN$t9l9`~*X>CUTKq&&rKoQ*I1pEEyX~+8cET>$4SQfO?1TMq z01m>>a0m{=5jYCR;5eLslW+=t0R!3&d`KG$9ZzYsmA3}&!(q_2+t08Q{vtyya9`lo zrj&Ic8>MN5n;QKr$J)j_LG2udSx_4)LJZJOT0BSue-cKoSylK&#J5j#@BIkcMLP%^ zL7NRjK|3ptG290VkQ;_WU8n%s0ILE;p%@f_^pF|$6Zu})2CHB-j04s7mo>}v|G%nQ zRjUoO8K%vz$*>mm-LMZN{G8)26W8@8!2iW{ElG`)0@akrKw=kgFTrIfO7-W0N7U*e z*bLez3)Rk;cEYY>^c(br{@{+NG{@zv^J|piCJg>pHU59sV(7nGsCL3C!H<++D%2%& zkxP=1QfR|V8(b+MB}DdLjQv8G2fvWG z!5cne)XCyK?pv6D01y7!Jalecg=)Z3qtrxPL$!wN>QEW9C89*qf?iut74%w!itw2@ zKEVfg1P|c>Tm(&aPr(T|4#z;lejm^jKpUjmdSpX2{Fm+aX0~e6IXenkJN*p1U^|4v zNYE5X!=0v21`dO6vqLZphQa~Z0b5~n_<&vZP|vgqHv<%g8^uHED2*`CZs}lCZ8uA! znH(w^nTV;d3EZZd%gS7{G)3_fEQKX77Z$@J&{eAo3t&FX1D$IQ%$Bor1+WoU`!MTa zEv#YX@7$1C#mP!o0n0!MEC($cv|%Gn8Bqq^8B_fzV_Qt6-S(vB+8%qX=&Cnq+oWZ$ zx63-+iK}4la_V?Dgn`U;3u&Ic2lm50*bDCDbXjC~5clZDRcfzk+OK zr?9fIT9fP8Jl|(en(;331-K8l;2zw7>u?pWz$G*206Rj3_SvJEiTvYv&G_%LCrqob zcR=B9gTmhgg}(+@&2`Lpu=dN~vEBBvJ1?80rse^=kLL}ieKO@Ta<(3G{*P(GJdwTL zM41?h@$sQVCwe6KBjRUvc;XRPE2fIZ=~1NK;nUvm0aB(E+U zzi@h!#cBuaNu#R-&QiK^?2f~oHFpL4?euPQEmB+`Ii_FmkgH^)aZ(txR4xP+Ks{qQ z&;a8FaiA1vDIE;Ozy?tuHh6*}HC+zcy=yB*g_5s)?AEzLNOwR~kRA(SLJWuwMM1ey zPLyf}P`4J>)5JN?{(ZcIc6*XE2!W)ig?lPU2|k`?+Ch76Z{+lx@+6N(K_~$EA)mQ^ z&|am!drv^oD?2}6u483W*)0jhLG`IWrBnt=gR)f_G-SAZKYxzPu`7!a*-OxoNIO7# zNC{~@&Fk&VocA#ybPS742kx&QRj?ftv!vdHJKZ3T8Cc!uu3u9my41xa86M8^5=nCDT7xaU^ z&<8?$^UnYn3}G-3lqneuf}x=59}XiywKE!b6ik5eFcBuhJeUh}Knc!*X)qIJz;rN{ zI~$h_g?h~ApaCUah`R_>!WFO_mO(B2mf|jfx*WR+;aqpY23P~@VI8c6BCrj%!e$7E zE$|a;0%#yHPl0bZj3EI+&240}ZK7$(PN8Ata242BScmel8jcEtmgS)U3?!avb zCah{?KkS7)pv$rmcB!g&!#=nL7eK3I8J&l7a0-sYA@~^%!U0eTgzQziV{jObz)_Ij zNjL$OusMVKC!B>}KsY0}4maV3OAGaQ#=%o~0*~PlJcI}E9R7mW@Hfcz9lV6MApJd5CGtO82$No|-xfI7JKpguH&x@fhb zF~^OdDKv*_MA!ni5A=o*=mc%x2hcd8{zUysM~*u{duR*oK>bK(8}+1I_ChFx&=a>i zbb-a3cf(zXE90&l&&Qny>XcL>={?ZJrf$5-5Wj3z5YuTs<7ICh|rpu!Yt$@|A9G1c|PzEDs zST^!orSV_AQ47!@vl3CBYhW!zZn2v<-v}SE`w90rs82Y6yAu+FZqt3Zn)N5dRljl+ zSNHl}+&FL!4nb_#t?U0vfe4_^=V!!&unTs;c5rt_+c@3|;UIe@s)!Y-BGV;uXJ|j? z?##sC*qyj5QCFHu<8R&nY9#9IWVFW$7!Nmct`$%jb(i``Bs(RbQXU3fuH7J86-IW7 z%dH>dST%S8_csXrm48mbNzic;N_HCe7f8(UnMk_qeuqE5(#~@J2gp`7!W~Y27vMZx zf=6%DMC9Rs3+9)77q7~Lg}AQtC(@FkA6=G6L=lZFCGlpD;sTEG1CWQ=m_wi!SK zNzl9@q2(sUO$>=Ze#t?jVrpE~N-ErxkOqoE5hx6yh4`l+6oCA&3gab^iQ|lr0n$S{ z$P06bGzeFv3k2Ef)=hz)oAb=DQgLvei(|!?1ve*T2ia!Dl}$GN5LOsL8`N^ON$oxk z;CLC4=#d>ihSNNz2T>q2d)11iKFTNa`1)4xoOk z9WGreLtzLEj&wYd;}MoFKOK*UaWEFfL~8O68MQrvK)6AEQQ6e2y`DT zG4aoG#brhV{pa?O*GkJ|3+`sv1V6!g=uf?D#8stkz;*ZC?)z80Ssrw?64}W<9Je;E zdcQp!tL1BMxZBI4wZ|@mov;Hc;Bf$VKkS1H82yZU5RQP33!xphTGf=4SZ?V&5#1h~5Uj&hucW9_fUhdZ1{!PW1n zUT~XpX*Q0Z;i{YX2=CQ7zT-fB##49<_u(F>v%HJj1G;nk2=^g80Cyzra0(;)C%8J6 zE*te>&v9QsSB`bAI9|bDutHBE;b&HpGJHpdVtDQ~f9#17d>KBt?l(Rj!ii zipwT0ZUz(myghSh61);aQmDigRHvMb<6vBMae9Cx1L&u;l;8u>6X7o{6{G__sG{HY z(^$uH)gm(Cy5rHg*6TUZ^s`(>J-00b%_?N1ADOa37SOpnpc2yK6tzKvKwoG8y`dy_ z`We16bcAZ4M>70i5cQM`SKW%bh2fwcK#yqXF89~-9x}>@;1*@5=Q#9`NgxEkI5hp> zs4hbR6^ZQR3TXxKS6ps?buH?Xg3;C8OoQT99O~DUse+*h#X!GW>KDsGpa%$)P!%0h zy0W-sK$$3sTN)IhGNcGgfihVF6i$&V@q(Po-mU4}eJovep^8)ol@Tg|64Fnp`bpKD zV0n)9o1Z(eZzUvKcjWHOe(yYzZKyMskp$->HH}HEI;eMQgxe76L0zZ;ssUv>ja54e zUlYxppsu+}tKp>%uIzM~YDKbf`E&i%sN^Xlp@`jq8sJwSzAlkFa@qf<#N{73eB?|j zj>8&B)o?mMQ)mJj;FKwMgOvZ*nljuT-R;+k6E->)ZaYQX3%w_(#`GwL3Z%kxhi;(fMuK26wq0?p@S)B} zY4jtMB2!=`G8ESi9xwp(1GG9pX@BD~1USzJbFMo``atl4$epY@)5q8jL6_|y2m{%= z?703iP_OLnncYV21S7l42)G1Bz%*D4Gs)*d-1#sEX2D3b8Mp?fz*JZS6JZ>Tg#|EI zxt|R)VLJQ>lVK7}fblRA$fq-*Fx=6cD}o^$kFxy6;3^|-f4BW$H23k>emW0TWXh2u zh-@rfnme%DZUlkciAGLPCDZ>a@-07i{O-7vuroeIuS8T`Zcllr$`sMp8B#d%&UunqB`Mj45CH#mx4wQf*h@2r^Oa1cgPC&ExP2f&MYb?bv z8#i*;FwWH;dybmSZMVQBKxltbZ`g60yDSEufuN!H`H4%XT|QahjXTg}NT zSP3g&`a}NVFUJ$J`?@`oxp&>(%I5E9s@$+A^U3erW@C08mOOFB_=D2dI1$aWv41|_ z+`d>0y%Y znKmD6p4j{5BgA~uQKs8X(Nm5j_B<>Ec`ungi5(jPQ1&O z)B=3{{h5QArwUuh>^tR1oH{?T)x|ayIeT@$!?a8KG^5K_K0Qt0Hx%=U8T21oElwQu>i6!@jYyLu?`AS*%>FN&kGJ94Wol9SWd*v^ssXsaf+j0aLZ%;>n<&z>m?H~cy=3#}ZWk(G!wwPw< z=bKlJz;!ij6#~pm>}`c}n^STtqiG;h85J3Q~ zh&ID3<7|!}f*Ns{7z6Kh+v8O#)TV&5q0`hEeeqG+8MBtX(YwoD{!emk_&q`C4?#I{8Jk2)kS6DF#6=F^Gpj z#hIfE6l?l=K!kxk$mJ8WeW%6yhmX7%;gcxH6tO!3d`4iQn>>2wLWdqyo4Ld0IWWlA zKR35lqdaD;-QgYd{p1r7HYQ;w<*Gj})8@kYZJt+Y_c2iN2bc?XM@6nf+6#_c4&8ND zjTb6lLM}L(x|+ooEln-Cjn%Adg$kPS7r$xtjpjP((wAuzC}gf(qGx#@)zN`xM4DZu zF26|X%Z8XIPhnRrC0G}*ed+Q1lWm?w3UiTt{XNFv!%8l!%;Qfly1(HKuK)PVGAW}I z?NXCBxWd2?Sh6|T^DA4PY#?+@4Q`K`pw&=2-N-6F}&*%*#Mcfk`BGnryKHhr02 z+p=ONUM%YKy=fH7k<1n|*nEoZ@b*a>?8e9VfR9C}l#D5_h(Qj^xwg zIuh7slrq=iI09nKD8=?1>2xV=_Qoex%g{Eow5b}`k*EN@uE(s>uFkw~t=3T|HQ`PA zbmp$^e-l2si6&>9w&hH@;&og;3Ui{gDdtTj-NwQPi@kQwL)Ut=J{J*|SC~yqpmLNk zkK*E%C}X0;b2L`Yo}vGfvlV5`sT7WY$hmgsKDs;i4@}|sj#6&DG1rPL96Qs6rLV5}qAMOF|15w+uP7qj!RcrgI1%HJN3bTNFDG=M+7& znw!Ghz=xk2!iL_7mu70z__l~^@cpavkuaLO{JA3a&0T@3-V-K0(JAl$t@Q?2*V5aT zu(BCXYfhZ1vTHCHc5Ps2(_|Zdw|NHm2AS(A92MMGBZIq1hGMFUJUGkm=eL8SF*D%? z&h1O5J*~1SnT*bV6&4ye^6rWnzQd==VJ!TypeNW-*-TF6sA#iSG1rngO4<@sF}ssF z(wONf=&zb4cT~2;u4>jN=cY+g)f`Js<8sBB(=uPG2IwF-LWzz3?E{N|)Y)+@*TU?)Nm^i7aq!=|#L%9iSnt^HQ;`XPezvEqL zS5h;4`qy*~As=%Mt`IsTQ#D;MS6eD-`D|HzF5l4pX%0sCRH|umq@n9-ghgVm&-qW; z|9aY^{1oREbO!EP(=?TZ`z>4hdtcjeyyC|m5pfK!Y363-`b?>5)?yj7fVgz6yG%OJ zHB*is`XutqMg!E}*TZ-s&kfjW#&NKw`H+Tb(aD;|kAm4Q)imYPa&8sWX63?WrDcw# zrQmMQMm49UBaHhrNo^CD&JpJ0S;sYL$kncFtwAH>oQ)_`tUBg&I>)_4-^!0Uk=~KU zHlZ&6BQzBpB?IZqs%z3^py55LYer`vQ}61U-5DroqI#~Ick|~*FJdG=meKAR=*v=t zM(u}BYKm2D=94GI=KuOB!lz0-6DuRt+0nB2DWkoS-@Rc8BP_<&GsUn7T7^YA;utgG zYI3(uvm(PI<2@je)Z4s;rNFL zi~aRY$xPJL<7Q^HEI&7M4J2*16^aS+Cf4SQh&|o|MUS-&QQb;up*3;*i#*{ij{n z7vXcWx%s59Z!C-ENj^l6n>x|!2#c64TgHo7EqQ&6P|$_=TRcItdXDxc{zG>y&k77x_MMtN?z`$H>V?|gMf zV{RupaNpcaxShR|x3On)1nMT4y_-9ydN%Hy@g2fDWpfPm^!e7E=DK*+otAu7T~{(m zr{(B#dCkWM5xqwE%G>rrHlO{ST{Ty#RN1lp6WQC4zShdzZD%{1fxgt-8_OcO$@=cy zfjMol2xM}NMa&R0us@kP=1UG!giOmtO=Ju)$#Xd>`uK%7pN0*q*Y!i3O^f_)YFW+J z17mRh5HmOzbrl?9w#cm%Vjjq?9b!`X5kr#@lh2Ryo*|}#pXxBgZ1;17*(QXTLjLFr zLrjQ2cDqB&Xq_JmF}rb-cj(r+bz8c_TJxIc_bcg_u|Y+(i#V zO!olddlh0{2N3>qh{=}Q(b~uTX34)EJgfDrdLnY%*gubRG)>yYRITbr>tgx`;a807?O51VR;B?#bh?-A@?4!Gx zPI)83_(bjDn)0XLb@rssrsd33oXcHiDunzkh*Z3LnA0t|WU2Bx0)3Kpbq$}#A3N$6 z&$K59^W3ZuxUyEnK52B%zd8paYg+5#74FDj_JneOXi(p>$^UtLt)Ce zx&Y0xSwC~509DnYpYbcmUB0KkYdC*dD|PL?dj`gER*`cl5_5oSB@&eAT(A7qmZidn z=u{i&2bh6`^~r~Y*4kZ$XP$A`RydqjBmqK>Vewrit}Z*lJ5BV*0_;xvp4W6iDNByxMKt2Nh6 zzQ^vJDr^}+b^Yi`Uyd~ugR%4&XTs!0A7?fN6C&X_>uQy1wXnHY(guWJ_NZ*78fSb; zkgY7^Oi&3za67k?+iIMdTY}bfbewr4|KG-$C?&sn-KlFP_WV3QF1%(*N06tj?gTTo z6t&lEf~$mG9qsSjD>m^hiE8yuHrr1yd$9=WjfIAW(}nB(*}h@-Y0hjp`-)-s#KC9k z_FWaX_(ZRaj|K}eJY|B3Q<~TpU=bgSE`uJud_HGD3oJAxrsvu?!33A4+U`v>2TMBw z0&`7r<>TnOBHLTmEX0uH%m;nNFnrV!-^EV1IdQT1&upGOP~xGO4)Yu-OsO)CO12M^ zr)xGp%l%A1mfCyQi3zWvT1ZKbq-sKTI~AsyVX7pbXAGXDp{|d8e7v$^p&c z(Og`s25iNrnKbjLiK^3FdA^fA@0CS6e_2EV+WI4d3nrMF^BmdDy19<1K2I=H|5S5n z-?gdwXVDg;I%cZW)%GjeE*kTMd%(wUy2~=!s1i}HE?QXFiV(|!(@nYxRPEfErhjF! zvSOy$U4al;XkWTqF?;?Hdgkh`)lj*ge5z7_e>ZKbWqY_r&;Z*{OU=-#bPOGrnMYO0%BE!|O*OLSy2b%M zQD_xvXaPxAXX@+Ue*}fe>$+%G12aEyt)u#@UyQrbJgdg-l4_-^md9_8eRt-f`D~&& z?|ZJxcUmG9x@Dy)Qk^1O>6+2i>EFI=%icj}h*{ajfqIseTRv1-S0wvKyQ`|WCqAqUp&}yCdWZh_H6T2;FquElMi)%KSRCRFUY&H{{5~ufOSGH_@ zV{aLqE*@)K=Ricr^!TWT7uQ<6a>nuc>a7A?Q_EbN&67IBS$MN4RF@kiOO%xz)h+i$++(|Ne_cGIOG#qfJ1Ly{;;MJ$>fNHVcYJCmpL(=udqs2ibC!{qVLy zB6Szq&+wZz-I2^e#V`#`HJ$V`?lYZwlfM5xv$MCu+cg7C6xBJwVpjUBw|iEas34&k zyxMkZnX_|ik4(Lj#!Q|3%3k8t&_wgyv)`5Cj2jhiObt4b$C(%BbfL-tQ?(Jb;yCCU zxi|l{{@IT^k|%XZ$J?{$RkcZetQV`>i0&t>tWY^v zG~`Zj-w{4VvDBcGeZnk{HR+}va5c+(u4Z{fmgSF_1kG@BO){l%gHBm-pKlRo^^jA| zH${vM{~oO?A9dYT%&FEk<34P0+{|9gJ>=V*d&qTXBzBN1O4YJCX_P);rZ@lJSA%KY zf?>$&m(_HvwS^W~4_Y`X25mp-DrNA5J3T@cM!iOTSdGx@lq-jQnr<&&wn)p`_-ODa zPha+N0k)T?Oh_n8FY1O-=83w!a=(N}YvpL?`Oo+jeAau``d>oK8re z%hbo&1l=QC#na|gTW+F%PyF=Dh_3W36ZZ#3IBO*LsdL(u(G*Spnl!XQlB!m<)9x5d z8#96jwXiIr+)I0+QjAy|oEF?sJZWe7%<7-Rar?lV>&gxbNSw1Sl?9;}V;ha{gqL`*_SOtAK z)ZhE++}mbV%r|Dl?uuz~+SuCB*qOOhZAYWo_nR5ojycblF*_#_1=>6Asp0PJz}5Qi zG1ob5x^^JuuS4WLn$6!{?3Gax=EaS+iD4MI#Ty*u+Z=kJIUMn&&FPJ{ch^) z72e^aTd`>47d_vXn77($dQ81+XH7%G+G3tHA3EWBpEVP8Zt3P)CvK88XU!FfTh5wd zBr2&`3-M$ZO@}VDk-8Vne8v=$rYj$?k9Nh3?n)+I!M$xs zub6W>uXEMJ>PEsXuet_7?hGpPhMj zc0g(FnDtl$JtHp7-FIXwP$XrM3iGhgbj*)YgRr_M$Dhx7G-u}>weVq#*GT71#l0`) z*OLyC!7{8TNxM4UoKc;PV)0#bswX)vbI+CI)w`l>u9tj&Q!6e~adoDNjejpkO6?7i zedivLieYB=qLI3~N?NdUODD0j2fl9}_2SZ48S(zc29IWQdCapu4jV;x$zPt!kX~*jyowpjB9SW6@#ktHH&Z|K*w; zIxG5s6?XR9zV^Zk>S;P_EzK@GHaYr{pGS{P>3%HaC(xV|5=WEseLn5aG&u{F`CWZQ z+$UyjKk||6iK|tmts84!zYf37apu%t6NoHN%m*y==!qYjnL*!q=EOg@N2jN*hJMCW zTIc;XI7dW&tP3902Mgy6VAGN*_qSDC5@GTE95Py-=ui5CpH3S)ClPr~W ze)p+qqw`l!&1}xUwL-Egl#%$fEWWeFl%HotpBei=CIqZ{It(PvDbLOPfu!L+XQ%ZE z=Rsd@E89G?F|Oz@%?npG?!VmZmtj{*r-<;$^}-YiB~I6X?X5i)jn+Pun2E*U(j+}o z_j$27hj&sXdFic~SJNx(Cd9XA@y>bXq`yq8Fyi?BC8_q(6bqxl^WnYmVZ`;#YIgRG ziQkw@#ATcR#=KStc9TmEBENsXF}>wpd~0?u<;k<$9xvaz?#bMfTlC#GXFj9KU6k0A z!Q-8|Hi$T4zBBI!v19yw%S`W0|H0(Ez=%3G{%zLwbu%luk z_ve8+PyZ$Ez>yVAJkJg9O}rtF0=A=no3J6=>@A0Ivs))YwvSfd|LBL4BNII2!8~fp zH-L*<+VWX+e0raG?S5mMeqcU51LMcrWZZ>$@neOqXEQy=977Ze~N3wpnj(8#{F%Q zS18ts7}OKc&sdMdyu?7Q%ohA^-N_Qpm9Mj-<5!eVrsgomCR_5)CdP2CbJowMh}_Db zP1oVv3w1u53vye0Ho+sX>-yQe9fAMY&nCx6+*zMZ(~%T!@n_eSTskS-`;!GS zKrTEs;FEx~XE$v%uf*aIPwn}-W?~0Eo3)DLaIm={_xxv*WE8FRE+OI);@YspB_D@v z4R!wL>l_zckNbGrUVk)d0f;~Kq=V+dyv!3ip`R@s#25t>YwK1)NsaG2g+KzVk zyPqBM^7QmHzv7ekT(GN_Zavzbd~ceMzdFm{T%Z2w>FMl1eM7c%d0eaYWqdS-6YT>6 zryk=-n(`wix{A*NZf)(6x5Uv1lf*Vp6MuX}CT&)CXk+7I{us*p+~(=LeD#(;JX~_Y ziC$JvF5f~g^8^dq6TUJ&7R&y2Pv?v--d{uFCC${?wN`Y_=tkO2$XKq!94s_0uRK$C z<)Ja_hZC17fL>-5KJoFXySm|vv>mGRte4Y=aXG5P>?W-3-5GOkEW<@g2cOu&vPO$I zXS2_49PYGqj^NgWFK8teTA;-||8i>GEipB7vQjA%#Z&(yZPE zXR>HZ=`fdYeZDgXB|nx$%J0|uKUM0gAa4^qo>_d8Xr}jgYAafFvs!MFJLZ~PS5N9~ zs~+9>PT-v3zxf3ExOOqj`U%w8uo!w}yGPjCy}`bD({yDOLus5tY^#{A`u0v0+#ubc ztef$%Hk5P5GM@+nVpmApUO{k4EAD?Z+@9b8(0#{ zJVQS$L_GOC5j1{Rna@)DsX6qyhnk%je z3#ZD<#5J9^;WmtG(vbMq=cdDCx=^b`whZyjwfz`b`Ts|^5(yd~-_^pFWlhm&!FiDO4Ji@t)*sc>Pbh=*2CnC9 z8AX$twO9u2!7>dA>>hr!@6_-LrLeTF@>P5i<8ye%xr`e+@51@E{F*Grn#v&hn#)u`4CrFo(~Db zBIsMshqx@tT2FseFLFA&Ps;eqB4TgM(;o`^t>;5rVMkkGJ2YMR`Rt@^#UsM<#E8#a z<{SSXKVzwpavQsT##(LboXyzgGhZA~(-fIcZb~p0(%7}>U8)Nc!{%Cd$0*kx|cK!b1eE8~WHnaEpvFG;9 z@3APm>yGzmz5C$fiCr{OSih!j&2Hi>AZOp6M5@UjC2U&K4eUQ|`iuS>uekEe?9}5y zcGHlsKGAcydgU@VI+V(`FMONZ!cP;>iLywFg+}$@Dz&Oq8aHKkgoS&0srX#eOYcOP zu-2XM@ZQXsHF6zb3DG#fJSpl(VKOdc7|WN#6j|sf;1eyUr}KHyj6W}}kvT;>_JmcL zdWt%IPE&a?-B_udX6-^;tB~KBDXWmKjB1JT{j9pO#}CZTS#T1&JJdh%n`4W(;~V8N zcNWnvxiS&pb9uOHx1!X67A;O@{;Mn5bUnJ_;oAIlN9b*AO9*boZUUD$g4`#m{Qg6> zKKYyZODJUW0CVLCuDkG&YnG+L+N1mrIs7l{pQath$mQux0+COHD&%(6{2`x&pXS_; z>+QZy0UnL;(LCkhs#Z(p{x)w*L}j~HXESYi@|e?#+Le~KEl-dczl;ru;2;xkIW4Su zkgHEEdG^4JTYJ3M5rck*=Wc0$kFNL47BM^*{^?l{ANAPybPO_$mowCaVxb9cNW&#( z@=xlJ1`FM3tWC*-5vt7?hIq#nf`NFR){~tY(?{ zBWy4JuWHR6`WDN6BX`c4k)}IC3Y$&}`v42wa#4$R7`;8s?v{kLHW=UG6AzzE$1`l( zbM2m%OV;pjg|)paY|gHB%=G#G(+)L@nz3srOH2OWd`;|s9X_CkqsZY9;?e^Jl(t7v zvv-Z7bx_q}u6z{AF?i$agGXK7R(gTFJJJTZ>H| z6So~nT62R3!DcO%K_9Wu_?|y`<#xZu&vF$Dt8${2aQUP;>$z?EUw^6~R@nF@%m<~C zvV=*po-sZfmg=dO+`4P`Z&54Ky!`vqXxFSX;Gb#bixQ^t2I6#220xaVwt*X=A^&f& zvX?CG)W)&ejnFo?=HY)ojL81DlIFB57Ga_1 z?>n~d`@H8*b&g}9g%#E6p5ywIGO0F_DfgV-ojLd1*qx?z?(^U0xY^2>V*6YRm55b< zJ399ini+fuGk2evWn4W-=~aU}jOcwR($27Z^6j4EFDhfMZH|;t_u_-aNv2JV?Ct_g zEN5PCVxl^`oN2n#^@z;oa-Pmz(QO+p|GX(``by4PaJFc7Eu1K4)^8y@H?Y*e`1;V6 zD(%yL()goJiTZM9$6a4mc7nXhyB4Si^OdWd%JWW!h!EebduLoJ%Nt)(`zOn;qBysd zsh>34$i0=JuS~p6)jfbBYyR z(D!R-5@EHYoaw=EpYSwM)GJvvvb>`4*-9zgsad%QI!FlB;qiHntxe*LKNu0>Ha_b6 z7I$eG96R*ah`~qOF6ELx%Xz71k{#5P)iJ%VVjAwCfpn~DChp+M^{ndYe8Aq3 z-oz^ZFqP|)L+* zK5(}&>j`P=Hj8Kl6|0%ud;it8y=~b_m>WCEx3&9VJ5|m2?ZVC-kGq4oP|XaK{>_SX z2X+k_|8%UxQJFzRaXDRg9=}e*J%qHcVNPq%`Cm5-*X4BQ!ku;Z<^2yukG3~b(cR(x z=h^s9%+BnX4Er1jVtlOW>V~Y&H>g1^Pv`&tFstH`RpZt)>m6}vyW^uFI$c8hr0c_m z4vJ`6!)uxGbllppsroanRm)$GLRJ$0&CI%>X61frbq4C#+QAV|FDzg zzEtkkzMTl=~u^9O^3r)oteI2Nu;sY9m3k{3HrWeQC^j$`)uO5V+|&xe4aU? zLao8hr#=>%Q56qZm45Ti#45dYH@D~i&^gGR*aHjQ?p|?fp5f&%}ce&)Eb!FD9)fH%{=vm_^>5U(%zpnddX9FTzNgdy#=9? zxKsIZJ!#$_rFtS3qJADFn;XAlNE;iR`80qcq0448jc2OcJgL zCMw_zj~pG#0U7g^M1AMn3oM8;-M{X4&Ue4>oNq6jd(ZjK?^eEnA5gjO@xG_L!fFU9 zAsJ?8vfD}CeSvjqiUT=->`izq_+w3CKkXO^NTwAQ@VPM92uB8NfkV_3weGo|-Q| zJhfdd#bfZLu1t1;R9$;)Kk1+2lS11w>|()IsX8O#e~Wz=_3njdF6M9IEqN<0Tv zDpO8KcPs!h#bI)#*5wys;Ujp%xG@Rbu;~$I!=t^#kJ%kjx zcauAmw4hG8BXm0(@-eob{#7j|4fevJzKvA@vF8pJulVkj0mFnw3Ht^l9rthHNWYMP z@dLsMpMgAS&0@2~>{OI^kFwJEvJ?BIY5hV?Q{|PTzRArbvxez@EqGUTG%2iyWH#P4 zSKgjiUh>1uA(Gh$c_-y&)b7pA)p@q8BD<~Bdbz63-aGt+wc((&K;2#Q*T111WsA>{ z%w~dymKgHd?|s;MiDWi(-7P(7Pg-s$+x&-oLE6=hqJ0CI`UfPl!MXO^)msjw?LDGC zB&82rzj4TgeK2+bbqju!y zl$8gYNoG?LzdI^&eX^JLI7wy=_-6f@oo_BHTcYzjCMlGjJw8%Va@`z2GJC>#@2!hV z`*KX4hv@>g*ittb((=);du{~PGY9I-l~0CMzkGg0fmDCmE&E77Lx%2XzqDO+$7#!! zQ|aceCX(4qm%6KZ>%*!ujps;a5wsQNo-g$(fzmMc!9+Srtzzp{AvuH-k^DWtL*bd7 z<;XHx^Yij-R*aktPl+Z^$ng*?3miEcP_aVgi?x0*AB|ox2bau)AdE;>d0^afl{X=l zctJ{{Y8Dpwfd^jmgHIGuVz~x3dSQDYD7CU>N4PQE#gW4pfm!}Am)jCY4e&lFBE^mK zVTXVrXCM$?GlLI8IOtr&=mQ;;-w1*eu*mE5=aZ4JUN}0`ecTykuE>Be4fDqg^3Km?j z3WCMPR!COh-eEz5<eE^V%*p8dMK9t zNk5Ij2|s${y#$pmc$%h=L8(nf*4{;8XEz8+EPquMjJGZTb+?xyuun}f<6=xNcrzYX zQZKxySS0T41*=*d8H8PGv1t?nSJ4e59gftu(;P1?&8}mg45XvQs>1MkWf1<8uKMaf Dzu)MF delta 83831 zcmeFad0drMzyE#hEnD~HjHV){M&&HdxKR{m#aSE?HQnk~P?z2 z;#8TT=2U9tl%#PYq+kx z>ppY1?%SK|E^pB!=g1dB&iURR^;(7c!?I3C-7mWManqi=ml&S~zCX9_z`NJ0jx49~ zlbb%OzBMgtOX81ZnoMY`w&>QO8CK4R;F zc5kR@wV=(Ec2}in&18Hwa!VG}p8`(3(hJ16mQ>8|vk&X+F>!;I#8qzBVl+ zKcjfrmYP;u(o zR^_&u<_Y^z8%?VMz1hZCY^#^N4Q6?mGom7=a>VBOX<7x?ze6iRBcN5G%5@ z1C9YoSsNfL2nT1u{X5BNd=ttFs&tmq>;Ywm{#5a!k)I9g8OVu!N=per0qlVV5jCK# zyBPb+woe|~P1e&J%7M5IQl!?7%dk#Z=VE2Y%z6Yf|gFr(7JxJ4@fqKY@l&igD!)}AK<2#kT7D`V& z4^9tT4x1zYHPp*EUan5E*8okc2~q@Q%(8)UXugAT1C6r#FassrbB;JIux8W z{Dk}*(i5t@JSe-e0*a=mBtqGhLBX2#Ec7V+3NDwNQgNvCy^jGnhBL$CU2*Z6Hf5M> zz-VYC@K#VZ;3uSKMKhujqFu;yWw?y*g?RG#@W`aN=p=1Ym=h~Q(|#Bs%l`(-_{lC; zw67~>Mzk|NY^F1LhBgEl*&|lW46#u&W+X&wTE=L(nvF4VhZ|nBtIBY{4 z=W$TZS0AOVmDW*O7Rq6|;gqiRz0#vfw<%qtbeYoGN~b6t2Ca^Ax=L-E;;VqSQahBc z_U8ntmz17T`soBCw0y&%&tP=vKPNG56`|E&)2}>~el|@`UlHP2pL4pLjkxfs;gM6K zG)`x9RMZTrE73LS{7~am`9aC;F+ube8TSvNk-X3KmL;gR7H;c*xx)9JNm zz}fy~NY8TT&ym&LhBAN8xw3f|V8dOKQ@#aYhNA#p(CCC{j*qrxp5*gUP*w1@^JM`W zqGSQPVRH+#Cd+}0k8RhU`J$1Y{R@xtjf{@av@x*hm#!4aL!qosHrOazsoKyXNJ13m zjPu0_6=^{gvWZ8cH90(T>Tj>gN_#Gs!?_MN(>aiieT#L)$0ngN zS8S{=a=4l*{!_XvH!f<@ba@ja!MQm?1jxxy@{I`$=2>d2J+J?<3T9r@fj3I zUJJ@zPjce_!dzOF3>mLMtAKBYa&X^TB=yhNWxikXzGtEJpSfGxicWrn!8 zSko2LF*5YPG$<>Ibeiqbo`=nCH8v_Co*tgCLXN=Cuc+yQ&GN>rFn+1hu$vo)$2ntT zok`kxBxCzeL0O&4loiLtJL41LTyY7J(cwJKR-hop*I#Q?^K6*B z5N0))7osHo7RvSH8g0oM#-CkK=a?1H!IU8R>t03d+>tzEXUGWjlxar!Du-RT$ zTr|pXYGE5>!S0(8dKMo(BTnnEQ4Z926+hJ#@0$PznG)xV4KmibZIdi_T3l4*l<*m@ z>9DKeSWTIT09%}Zit0n(z#&LGIyP!*tSc_=qbMV^TEp(|y)OqO3(7s>RVdpPJ=Hfp zDcTix>jOCuiSe$;$ry-2*c`xoXl3XZ9~k?qwdgoomvfFW4;ZHz<=`fO7o5 zQ|XRFIsY%@8BMGAw$|Hj#8z*ZJa>nr^fDH+GD;3!gfn(Jjt0&3v7DVTP&4;V*{5u1 z9hByZh?*9T2an;f8Q&9H6&kG4yAP|Lu-OUsb^c}>vvtWSlV*e`grnk|-Ez>(IrhV* z%(;fH0B1LF2F{p{bp9xa_SLpHk-ZkrW|MCmj zy**Ga!_80*OV|oIaoMmd!j6+psbwg8K9sG$`K2+tX2WE)d!#H<(U-8lF*?>c%Xebb z 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≷QBUp4+94&(#wm)DsH&$b&0%xmZha+KI z0tCN_6S+Y(rq?%@G?nYr@9heGr`&-z!(we#;p`75g%t#=jJvzmF7plV&4IR* z`K*9flFu1J5}P}T{#3H85}t?RC%|Iy7->xDa#(T}=!f6H;%Ldd%P;Eo6xlK)o{D=J zEG9NrtG*o;TZUqP^Jl^qFiKqGxzxWq%LtJFa2EX)5VLcJ&pdxS47mt63YLG#C@er& z<6!Xwlr?`1Ylvw%M>*`zy(sq(x2x$c0M#%YT5FYsF5qfepc0p~)A7>ZZ$Jw{x}bPG zC`38*&tb7y@Fz6np|Wsrj`p`NaG&7r^Kcr7T$|i5H^AzNtmVwh+i$Q~P>HKHd|9SO zVKe>hFl4I>E|~@=QBIk4q5B%?^R`3p3`;Il-Y{kz0Lv_qIU&5g=y+eIDho z`KDojjPnBmtWjx3{$-Z#u#K2;as0X!ffF&o6 zgHi}f4iOKAXBNv+WIvZr((M3@HctwDnX*hD6U$p#bPterM%_^Xy2mSjAJD!atVpM~ zT5r8#gzgN~2QSgI4uIv&aoqA2MOl*_A82 zq%*odKqaGKxW9dwbPH_vILS^gGxEpa>F`xKq2^^lpQJ2IQi#Kr2djfo7c1}%LYzEo z5ZGmgELTUOaJIq%hDDdKn5*v_Sl!Jgz{#yO(~YdXfqLI`_i%B6roxi5&292FtbWLV z$IVd=Yq!^otWU5_z9wsczv4!C1Qu)8*($w?AuVh-*h2fm;sBubGyZlJh0(w=*$#^( zV{1I?Z+~5GGkAhUR9{%d{jn{8W!|9mPY`k-OEvRBvVMllW1b)SR9L+cCpW4g*}5Ho z6ItT@e<~u^bh|g?Y{+dp4Ay_lsck(-Z{z$&o$Nx6SxG^CL8LRX{|jNYMn&ZfC!X?l zz#3$>gPkm!=^jivaaUL^5oa}=uVUO_O@L({q_!q2OrP8kpudQaoH3McJ+#6I{X9^w zkR=;Yybs$3f?($#9H75~5ZxU^jtMvbi<=%S>OsLI}S z=qq6{FP^z^5S@gDGh{%3UTIZvheu*kVX6A?%Hl;>en#Et0k-{2Wvs?+N?R=_P)<@RCrY{(ql4$u{e;dvkUd%$R8j@1c-=q6|xdeY?W;)8dj!#)dEC&U%+ z>stYs*z{%FU0D9+&8l^_dx$yzQDo2@mxPXyYM)}msgE}Z0jggCFo{-76t$fd@4_k2h0;MlPS zJM05s4JcK*HS--K>tvwqTkx*NxTyhp%N$MXffQAY?CIF{a*WVZf!5CN8d;E+-!<}2 z1={o8m4oFT9($Q}as|7AJ`g~j0(fO(owLr!|2k0L2FTg97hey42Vu{pcQnq0|G>hf zXsyFKF4xHaCQx4v$ljMVuK`zK^@N3az~O=%phMUeUWUa+;I4sN^QW7Qth0eu@Ar)S zvw`}E_uQky18C`cMreMZ_2_#>7UUt4Kw7uiqwatJ`)3Go_|19d z9qmJDm#p9cARIW<9p>*r!>F6xiRtLm^6>gqSv)lS?OSCAdB<_WViU2j@g@ldod{!t z6%nI9Qq|#EC)v@hWyz)Ik&Qo8L*gDS|7k-gE6;Q zEI+}LgMxgvdfUwQ4-T*mM+hr^RDf*>LfAlXW7vyObF+?u7ya!p+R0*YPinMX7K%~8 z+lX=74eyJAx&Y*|l%9MD7Bg6cv%SUNvfaoU7-;p|VR&B()L-7=_C+7Gem^Yc!&!k% z{RS-VL^wJTx8P&BWN>eQpYDXEP8`J9cgj+5_rdu)6_)Ak=Hjawp_c=A0Rt(VtjD#V7CR2C0$3bu zcq-hX(LS!Eakys9LWo@6ANIfslBv)q+kW?R5~pM!ELj3?xdtqHbQwN)+V;`nF=BoC zfZ_c+UjH7@w6W&lbO@mdNGLauR-eh4!Sa5`p}zu4Rt^uap8U)REef>h2Qf+JMZ3pA zBflt6Ujon`CCE3<`(d$@cyQ+f?B_<-4ZQsS+`ZWN!h8-aPBTUyJ3yYY&;YEx>&hxQ zW11Y2#Up-$Lm#g!^lO*D9mWt8WG;5=-9tv!AA!~;hmHI{@N(p^9DtG=)Q5_cY#~2_ z&<&)AKXt_I6xmoe`{mJi`9C7$lyu-c-R?p&+H|TcQ+%x2Zs_hGD$yMGNmIHBc zpj3yo;0q)4ZlJB@aqP9`G$$cM&%w(iTp0Gj>TX)txAih#x}V~+hd8V~zcjM$1?mZa z?4!A{=y|Z%$KtWGJz~7EdUYrJSMJ5kftm$N_7=ynz8)3_5w&7NIa_RH|AFVQ6YdJ= z1%2I?$gYJm4ptX4AD#tmhhbq$oEl)$PNH;vC1q`M(#U@hsK*1aT^L+A{fDr4IN?$^ z0p~I-*#mYtK0+Z}2lj6f;yEiF;DqjR8olPI+lC@E#5g}OKwpDUC!~QFZ+l-{j97Cdlh`5FRb2(H8(c>Rak16*ZVUe z4f*P(LcV;yzzZuZ-?6X;nQ>ev8(^{Z<^#U%0xZ0(9gXi3&*6sbIsR?V5FP{;BEz+%1dV6>uXf%_{Mc%v-^Rts}_ z-$MvzN=1Xy7?k?q1dxgfnto@2SNIA?GcT!nwTAhIfIY=?8)V8|H7nB%|d8ase!^r(%b)odRjc#h{nHEO#pN1)+8LWy5XN$)cU&b}BHr?_~OKLwC$m(A3Emfggr|GC53>t{YF=&J!aeQ-m( zfH((BW`J*7>l6xaPxQTSq0ES(#>ozYz5oL+89sp}Gtk=}G0r%@F2LU5isWWfh7{_S zDmy;&Cou3M9kw;FuwCKxXg)&SC5Nx%UzMFK8SJXR zx_y;H5(10EWxm6+y$P$m`C|MVggUuP*ZW+PWtKC)G*E_KyvA=TBJaO`ze(pRsd6ev zKNKe4IBtQ(qU8i%gB4`PxsRjX*EOv(Nb&NrzIa`P*1~XHLQEgTpv$*rmul{G8xqSnBZl%HL8XLTjV2ayQ&naDoTH;uvGP z!~88bgts@6TmWSI&|ADosDINvobL8d1z~SXHq!OI+f4b+B*MLu9(Oq%3#a-&* zpCTV=ti5gv?|MkP=eGNcSf5G$9(QoZHR_%Su#USU@)^AugpO3raK7wshe1EC#vQ}j z@vg|KZwa&|-=)8Bcg;gc#-pZ6*I}97qw)J2e7!7|hv9AHJ>2HS`9YQd&u|0-P@Dw` z_)i`5(foahb@M&p?Sn@Cd{2Z@8{ZdMP}|h|TrBwQfNk;v6mI^C;1og}0xXE}{?><@ zSoy4_vp(&i>@?Oqh9DOf{Yzdceuu>|g+EMp*wP+xE8&}^-3Sc;mv4pbXcPTRo*P@mxy-vrJVL{fMz+ghw`h~4g;8|DdO=z^Exv-q z(n?xVMYotYx)Ra_g!6?9|L6X87+lEZ%?bA^FSE;&I|LTHg;lcFVSA01`KzNZ5R%>D z9Y@1Y+1XWmU4Z6{g*6ctoB{80VRSbQe#xO#lr1q|yXdW8O+lPR_=MqHghfA*UelYdHy4h#AM~PQo%rR%Ew9zRI#G^6NSWEICdEo6RU?@G-PXahowAi(%2R(NDzP zhQ$i7bzq|#j95HdRX)#9Fq%7gARtSz!i%S#eDLORkNq0uVW@qLu|3- zOv0TF7EdAblowU2TXcI3_gY|ocEaK~lx(S$Yf1-{TV+33LlJLYGWAuk!pzh-CUg({ zD4&xgm%ubw92r>>-<|_VZ{v3{cVKa;$zmsZS>!{eC>V^M!eS#a;CP<=85ZYIKAhFA zEla`NV_%sE3x{z?fGrOpydt_7pkGC(71En`5511J8d8IgeIsDWg^$kK4Ope`CH5l# zTx{;;rQ7PrmdHc9Kde^fPX*^7#O(yF#~R%VOSwLr;2JFH;*4|RhyOg0iqBa6eGm?e zoSq_B92N8xH?q3YkcpQs(h1lgc>~-GD;5^c!*dQjqn>P=Tp{ORu`ejK(BEEPHVDmz z#|~E(h72>j3>Jq676xQ5tQMx_yo(>%!Dl7ckdFI2!1}^7BEK_^h76EF z;(TXIfbAlJ17xtJPjPTgN$^lfu==wWt)EOg4#6N9%qa<8D+&5NSDav8N$_Avu+sCz z2}YCzS0mV4wX7uAq(L!_DhcKx*i)vxTN3Qtu$ZP62layp(iL&+V05(RF7lyz*XGh6EAYLoHLy7EE=nJPagZdx@>7VcvT)a|XaaY0p2#x&=7X7+-|F``Gf^T5P2G|C-fG>*kEbSEp zQCc4ak0XfJn0Q8ggixS_om*lbH2Yxt2Eh>7hUWMeBSK~HWdtY7PI|YNzABf}a9DI> z>>+rNUJUC6v6@vDBG_9N*`f`C{8GpkkI)2J;%^9!G?VH*+sZxFTwvDM+Y0Z27^3#= z+*b|kRknGs2ACI;BMh0tforVoIX`zN^mqh0jaWo@pK$;d*Er7b;rLaMpU5ACLVLBB z2L)E-Hiy0h7Dvx~ab)`pRtIBsM1cMPA?B6uNgNKj0C7jhuU+s%RJrpR*(dRPI%Spo z@ZkywcdU{Pxsksl#Sdz_3quBl zlQ%cKAjT;RgICasW_9DbyDv{Xt53ihDzl;TXM4DZt>B`+9R?3|c>;Y7OFolu{GRG5 z+hr9#&)|q30FxU$1`wsB!RiJJ9XjoAhY?~{z*iQoUf8IO)%gLsfsoq3>6hd(6&h6p zizA6a#*ZeR>&^43?w|mD7(((A#^)QE#of0npSKL=!^ANY^{OCS(7Q>?&rQ1ap1K6rSjRP!J1&Kp4rKcKuH153@B~Q zghdxcgB#&HjsYTcJT|{N14S0pHh3V$(A*C`L@3l;)r$tH9DIPa>4UM|80Ya_%VLD+ zcxV`Q@NZ!87G*w8+iDHrg2iij+Yp50`}0i*$qO@YI8B4)eFx7CSnjXEnvC4$KBNB$ zYlbWtmzIe`-Mbp!Mr?z{O;$QYhhgsPD=$KeV5$8F*WN0_FC7X3`NU=1veg0$|#;u$BcZlh$6rS%pp={PL& zYglqx*(=|WV&DD6-ws3G?%41=SoC=eGd9&SqhT4V@vk6!g%GRKM0Oc?f8$V@4BHbj z&4k6fr@YV{g~dfAPf_nNYDvkfFf7gr`i*>tVX1s@?x)APXBf_AjTtNQ!?ES91LbPP zdqsRDxlO97bQWns` ziMGg~KoAEUkHsPcRSi8Rx(_LRErRrMxs{fmBz>g3QSgmJ?+J@zi?JB#u)aA-*lj)mvX9wsL5u#7v=Kq$%_8u)!Hxjpndk7X6pbvM+s?6eS8(;;=WH%7x zNZ?q*X!V>TA8BE2bXdg{5gLtJ&rcCq)K*hPK6Tbq;T;3rGF609|C}naptinY@G)cc z+W}>khFOerQJt+nhKbNvqS zy6XaL(GeE>Mw+1`2#qj9^&(M)8HzxtzZu$rP_P-&qp;DNp^*rg>8v?X+p}g_%6lZ& z(KPLA{8JA9X2Nn%MpR}kN?8z|08Q?#IF$uIqim`jc4K9iR;FtK&JN*X zW;O)ZE&jWLT1Whw+2*GsfCoZ2bIpoy(wLur(lSVaQ^NdEDg)?+q&g33qb$YDL0BL$~2KU9uLjMC&;2(W-8D2F};ivQPA@rONp ziN=4SENC&}nSTkC6{bU3PNw2n%Fc%JQ(A#)YHL&km2%CL_q{-xq4l%7)jw6ed0RzmtqP2SZulaK%S55+5pqA^1}p8Utm)2~eg_gtFo!C_hx%vz1MyJxAGd zk`Z8wQxy0oWkoL`9aqP4C`V+C%J;w3`1Z75|$i|5p+JA92k8 z-&*vKHs@}S>Pcy3&p!oc{5~j?9Kat=?P0}_D1MBQ_?X(*9r$n5Lv&tesg=yB|F_Cd ztqJ=Ev^%sN8o{XcP`0N7|0rd!6aKJ$ofW5Y5%f^@|Ai*=UyIiUsSH#WI9S<_Q$`I{ z@uigw9j-W)_DH26P?kGd#ZzgAmQhDcsEVLgMZ`2^Q<-qOvP&!Z3~(-@XchlY%JeZv z$E5N2!v@YwRvBkPnP8T(ODlPz;#6jwt!ygoxk~3N{!hyEDJq??ueVfe4WOlhu%>^h zn8zt6Hx2O~B4Ry!huM~@<7?k;^tMpWsAEE3LRm-;oAeBnU0-}+D1;;3jRT>B7 z(9VYP^EjmwC9Cu)D&7A>nSh_t5v(v(W&D?lr;@*@Y$|V*OO*XMWsjGse6K=jrz`t4 zl`okA7HlYeT}4ni0s_j6D-*#`V!0UK%j zH_8TWR_UnRy0=1E!A`}g?7$u<%iXK^e=6>fr2!Lu!h~Y=2bTKD+`*5keE%C|JHJ4F zw&N=(i$9^tqq5!KLaRV8Lz%7+%5+z_IQT)p1iwJ}p)$c$WtUbK@SEa~Q|7y_;;FQM zhq57mLfPOuP?mGwqlW|!0O-tC{`s3Sg9kVZvZ?seT1L!Uf`MeViYl5~A9gd9t-0cl zQ`D@rLOdV;I;(V42D>Z!|3bT)p3zrjpz_Qb0c8cF6{oU4W0Xy0zs5pwQQm54nar4J z_`~svR?#s^W0l51Ies&tEG`ksPibYbN#LH)#VXwrDEU&Ao=SU}vZ2W)cvXRaQf6F^ zbnM^jD*fY>@tG?AamsR5Af6prX~R4ctWp_DEBR`0My{9Vm20z6g#_w00%5slDnLhcb z0w)zX1?A^)%7&aqJcszKN=Icu`O1ErGX9)O_ivSsigJ>*9~CIA%ut{>mG4%rEBl|6 zYv4Z8v7ry4%u)uP&GJ0x;Yv9oWlfIxFNXj#lvfF;>|tePmsY0p1ZRQORJxifU1?*P$+MK5RkoP~D^5R38(Kyje+`d_9L`*r ztZy@vpT{ZFZBg-5*88EdODofD1LrjEQStwv&zcUpPZhWy%JzPyGEzOnQUmQIKB|%( zQ_)nq!Iw~u_qU2u$Q(3?bDAV6m_AMwIa#!(tO7BB8QE8`obpMMcK2!#+7*rPL0cHQnDO-oK zxJrswh4NEcS-q#?k5e|=Tg5}ABNH%V9TibpSz&#}ODh}b1I`9M4`sT>D&6Ch@3R6` ze2L2X9}|JpkInG^GB+P<|A!TE&+dnwaK!pUc`*u6_Gl-;Or@90GsZ5Zo3Mj4S?gh>YcBptN8?;l| zROa8MbT{M0(mhygOmUEZlyZQMFdBMH>2VcbS{eTpIEy_2&{p|T+Zk`Z75LzE6z8ln=6 zfwID}P=2T^_ys7@6#U`-6{YN1P{t=gnQykzIZEe3`S|!U6#uU!FXLZ)s0_Y=Kg=kg zbgflNv!NWa%}`eGzS1pFeo8CR$BLI$=G&#X()RxWSmCEgz@a$+WkWtw8K~Swz9tu| zKef~s%~z^x&uNv0%DiWkO{IMf%2T8O%F({8?5j{V>KYXPuiaAiZ7A!$qx3G6`5vhF zM@lU^et^RYJTM3RLcBhdzT6PX&NPFv0$*jffwCd~%I>V}9#H1%r|coh9tE|D)Bv2G7uZ8=xw$&U%l(R=b^f+s&Fz_!B`_~gd~Pkv0m z9~C_LF#*rvCqE|Op^FcHRKRnWKO*3VO8d!=2_E;O0**|I8mT8gCV28=0_6%%eoXM> z#{^G)Okn=%fGgt3j|raqn1C-g@OkoM0xKWB@F}glMm+g3f%!u0$&U%1{FtEhj|zBx zJoz!flOGe9n?nb@YUW<~l9}~#0QJ(ym0AI53Jyz)-5zs+s^XgTfU&8!N zxssmzn4mvK=E;u<=o?RdOd!|FlOGdwz&EZ>eoVmq@yU+~c++_DV*>7v`0>H^v->R- zn|5_x2psU+^oB>30GA?*iN+cuRD<2T(|ma}OX}6cJ?J0~m21V6Di$4-j-8 z!1fnFjtKq>;0{3^!8&1i0Fe6^fa?Lkda;EdSHWW-oUWq4UDWt@h!Rrpw79=28z3oNkn#7Wp9(E{LS1=uc9 ztpJU!0GA0q7Ol$woFT|41F%aJ5Tum>=;Z;hN2GfI_i|1%Fh{TU71Rf%1=DtLgLJy?*^0B$XZdRVl|kD;8Bov1d@MxKr`1cis-vQx#g^)*D7TumQu6)@xsB}iqnc&x zeD-K=o6F-r$m!dw-JTz2#5Y=Uf8wwAe!lg?&rO1|D!y28Q@zbYYmb=RFmTjcy}o^A zSXj?SliOtRe|^4qHD4*++^?EdyVt;f!h=%-)(*YW^Zgp3X%{wZ`Mux7{gdww`Y|OZ z*b?*pn>`zTS-X7;j~5-s`(2*eVb1=`Jq|s6_}8KuUlz80P*x1AZuJ^abN1IEzg4*Y zyK}*V{S&Hp-PG-qBh@oboqBiB!$;rOA7USK=$9ks6Gz?p^xm?VEhBbiblo}pa9zaI1PH4EaGIb<_|yXM zstK^57G{M10T%biqfCNti@_a+=4XEqy7}#vRZna@d1lIYwURamYSzA+@|SJcS!U@J zPkZB^6JI^P?AgMn%X~i6wtk||qDn7ckD7Xa$_Ed{o|>2uKQEMv|7LLU$KycGS-&2q zKiSkhC!=Z9UrYaJb8p(!E4AnCfAI6px@Yo!dGPu$-;e8#TYh)IHG#@L0D zUs&F4X~#W%7k(4Ew(9s6pSNju`{=;6@e7AuIyj)~l?NfeU8yTZR&|Y=xBR6ZEhO%5PhH#@FqyXFlBf)epYE)b6%#*6Tg*Z7kE*GIs0sd9RgE`(XZ#_rK1p^^rL4 zg_TzAZU#-}=S#)0>5x-`lpOLaFB67wO*Uby{sq{w;57f9nHHbgyF_Dg3=ba_WFQ z(!>ptLXr_r!(%KW`)Op#tOH=H3s6P`*98cA8X%9rCM@*;?hv@@0hAM42y*KJ)Tj@j z3uk?Rka_?I2`UKBXRMf$8jDbeIP zNDYxnsVUAR9pwt&d5K-6wjT_Mj zjq?%NjliE3cPY<_;Kq>W#d=BuVQB(sC`MBni7k}ILT?IbBAk?_;$uoP;n@t*T!d46 z#Xd?4QM);$rHG-l5=SVlg^w?!jYy)j6(=d}M3WW}KaomlFV0dNqIFA%zgR@+APOiQ zMTb_90Fe$6X)V!>TdmNI&Z1ju0RL71IjsS@h$4bQf)Q;1y5YYH43OCxz}6O^hX`&9 z5Yz@BkD!;Zv;(+9;A#iZM{FU;Z3|Gt51_Ab`T>Nr12_mUus@au8_?vG%71yHq zlGzC-TGvj#bXPoRhYcm6J<7#@+OGKHQSZA)DT#mHJ6a{`wD*x7V_&%udj5wdgQoBC z|K^8PKA&^%UGEJ)RVs{}eq_N-vDptx>qBvW zVcM7%oV%KYPRoBI;kkiRo~|`~{;NZdynL(4v8S##K76iU#Pti8Kbo_q**8@_>E}3b ztljsYF79bvbG5sE-hZY?siieo#5gbvVeK&tryUrVVBzBr;N<{V;14iNoFq6*;MW0Q zgh=fGkmwI^nP8M?-4UR12Y`%@0HZ|#!5M;H0RUq}dH_ILM}S)d<3zVk0R900Ih_E; ziz0$Tf)SkooFcn3KxQWZTOhzB5gZ5*)EOX;z$GkQ0PYaDx&TZSTL^Lk0cvyw2p7(- z03lrf4iZckp4|ZKT>%oh0Yr#>1Um>CbO(qMG2H>ex&fRfh!H+L0KB>bEa(9cCr%O^ zCh+SCkRVcf0wneTxJ)ohwC)AaxFD@(=?8F!z|{|6q1Zx@+ZUil ze}F~!-*E*9=?8F-;1%IH0KncKAYlN&Qn8O<2SI~@0I!OefdF9x08SI63!gy%UIPIZ z3<5C3NrJ-!euDurMCxFG#6bX;34~}p1fcO?fQ%skD?|ao8G>HH04qg$FhJT6fLjEs zMYo{<{=on_Ljm3rMFfQeBZdKFi|kCgaEuRVnP7IMgg2A*dlyJ z19*i1EEo;2Rh%R^OyCy^kS9_@0TM?8Tqf8qT8{x}914&z2H;~+KyZej*I0mEB7H1C z+8BUa1balcaRB~f0dmFxd?Jbn3JFHM0I*MFzW|Uq4!|}Z;D87o4-oVMKpw$CVVMAM zhrl%f;E>otkUJirh7;h3a5@1(CIB2HI3_$N0@$4Z2@?U1i+uz;2pUWR_)5e~0tlN3 zaGKzx@R5NrJ-!elCF1BGmF@ z0nUl^sQ_tH0B#YS7u~`D{HFrsgaP~@iU;WOQc2vBt`*TCb%tH#{e{r2Ea3Cf6HA_ zKyZejS1iDNksb?>76Wh#!1}-2wgiAO zA~*pcC>|h>z$Pp+0qzjEW&)HGTL^L!0BXzv(1mjrK*&shg9H_XXCi=o7C=HGKqaw{ zU$w1p z=Ky5P1*k0w2+k1nng>uvq|XCLn+tG@pswgPAHaVeK+b%C`l5)SkYGeIfRD&d2FRQb zU`qjbP6Vd_1SJFH5i}5%RDe4Ku2g_VVhcfT3P6p20W=ZLe*uJ~0vsf0COlsRu>T7n z;Y9#nv5#N}L4%h7T8fyL0K#4bI8D%6_$&bMdI?~`0)V#SB*9?j93iN zO=K?y$XoAdg^Oz!`!(f|bIu86Yhez_l4*wb(-7 zzaF5*djM|<=X(H!1P2MSh3ESKnHvBS-UnDK_7Mba1ZeO9K#qv{0N@V6X@YgaXA3~? zCV&N70M?6>1R{q_eB9g*!uvzJ_6Vx(mw+5 z`T*b-!B){N58yCCP98v>C?ZJQ0x)75z;=NN|u~pYYrXkeLUNuoK{b*hdhw4WPj;fP*4t7r-5Y z(*%cv&u)O+?EnjQ0~`@22|{)N`0W8WCQ|nR*gpoiOmJMZ-V3mUAY(7USE7I*Y$rgk zPXJDe^iKf1b^+WX_*!)P6yPvH&ZhvUMG-;bZh#T{0KOC1`v4m60kG`{I4gqp1Dqkq zBRD542LRIc0=NzUoEKXN{67Jx@fpAm!uc6MA;Cd{0^xZOAoEjzgo6MV#Xf?deER^J_oo%aGKy};d2NecR#>_LjYIANrI3A0Dgx7u8P#d0QS!SE)!f6t&afgAjmiZ za9tD-gdGIvbrhgTq#p(F`W)aEfc2(TbU%ix!C{h|V<5Mz;s#0LA&?PYfZVoA(nal)$Q1G= zz`T4xp~MOW=PR zVB8si`eOYVfI@;wX90Y~=(7Ns-vaC*cuwf~072gYMC1cB5FZoVA*g!}ppgha2atOP z;5b1OQTyKjA!h;R{TrZ}I6`302k<=);46~O1MDC;PtZ~{`5qwb9Kh1=0a}Z*1YZ9J z===jfTe0W|fWrjW2>e8c9|0221FZfLz#*;>H2xl7U;#h}v7!Lr48a3}0MYjXK-v!g zwu=CrMes!c{~rPJ2)YQ%C4fQ#*Cl{%VhcfL0YHt*06m2BGCQQJD5y>~eyt?!x% z89_k{zu0~0U*Z9pCl(Z9I0uT8g&58qmodJ6R{#c!)GGjCKLK1O2o|k>0r2`6AmbN+ zVWNQG@XyxDqW4v6KU~t%uL2|%BE_w%NHI!u`xT(^6@Z*y0Y-}=f-?jot^te@+1CKl zegUxk1~5(p{|4ZH6(Embys%scC?s%Q2XKll1ew1A)c74>l5qYG5OfXTAc0GG76IHL zNGJlBD)tfN{sz$C20*xoxd9Mz9pE&3{1qD1N+0AWP{ zmkDA->stU`Hvlqj0mO*{g2M#8{sc%6>3;$w-UPTsFiUj14bb=xfSlU^Nur4048e#y z0CPn49e}i30JggT^F;7n0RKM$@(7ZJBt3|K}68KvH@(A7%78^hzfy)MvEw&J3S^;X5 z1z0Pb|A(`)fXgCl|2I551K6mjppS@!t$+&Hh+WuS*e!Mo*xjqg?(W9!u2t7wTh~}y zcU|2z{?|Pr=<55v{~sSO&zbMJPtKV$XU@zsvv=OH^_f$Cjr75)i(jcHW6-!uukPjh|tyzVO&Cl4eF{0 zUhW9R5+Q6-qY@z;6XBT%TU4RM2z?SD%t?$8s~(GxJt0DsBnaEpj3fwGMEEGePE{c( z!pKAjYmy@DR_{d!NsQ1q8Nyz*A{oLX5t4Wy>{sP#eTKs1hW{Ii#A2b69N` z=ZH$40_Uh|C(bdoU!3F0J0;Eu)m5C6>bN+kR7Ovn)2gpHXVf`yepWeB;ha^2#5t#~ zigR8Cq{jJ0jS}aAx+Bg-RVWS4B{fN$%j&T>S5!$aoU3YvIM>uGaeh@5ym79p1>)RL z@5Q;Ps;9-drB>jmM`@&r>8RozRX-iVbT5S6BHU9690=vT5jr^#epA~;cqc;o^au}C zyYvXl(;}P};j!}eL8zAwVW1Df6Lnk!cLzeA3<%Fu-wX)bM7SZs3zfqcp>2AEalQzz z)Kw9@d=QFdM0le{Wkfh8!ZQ*6RE07j^vQrQCzA^^F6KCyT$pkBqEyL@^4>+w%#3nH zl#inP#cNV!07gs!_QSj*0M0gjA|f9)vzQ5$5DU zV7?_n_FM>6@**(b%8PJCgpVRH-^zzDGB?7Sd)h<86a({%=BIHos1rX{5APg*kkV_pG z!95TmPeFt{s&7GrZ6e$dA)m?-gwQrW!nh!W0CiOauL20gf)VnoQNakuM0h4bK~*RO zp-(}CIUxwlw?xPugixgr0`sjx2vt|UTrbyWnf5(ve@5NfJX zVF<@WcqT$^Rj3p~pHPH3r4Z_>$0B4eiBP39LVY!(G{O}TK8ny#RVafnG7Mo&8HC2_ zy$B(t5E_?7XsT9}MR+7al5z;mRQ+-Y(@P`l7NLboP#&Rt8H7&d5n8G3BD@nJeFcOz zs$B(yD|Tr21Au*e1da5h7KN$_Q;MAdIVw z&{bU(!K)%du__4N)u<{6$3%E0LQhpF9HCDoggM~|z13q8vR6i^QWc@Eno$+uiU=P? z=&veNLl{{FVNErJf$F^oA>jy(t0P3K71a?QiIAiQ!XQ<@2Ez2J2)jiXq7u|ZC|?bs zQ%!_nYP$&UL`Yu?VT5W|3t@S6gwrC7Qr@)@>eWCPSQ}xCIxd2HO@usk5XPy#br80R za6^O%Do0&}wzUw()kT=3u8QDQ8=+V|gehuNJ%nQ-JQHE6DpVh#PaT9g^%0bMEJF6W z2vr&&%uq8LAY2jQqX@H9g@ymjUZh%iUJ7a^oRLgPjV^VEt)oNMMQ=f*e-RDE$4 zstw{SQVE*iELP3LS)#U!vs5K-inA=XT~n71iKm>cWKZH3sDB$GrYLUa*noa6vmMpgVEEg6kyR33?*P%kc-&VNI2L4 zE_;%M?lRi40&bGHUBdUUq!9*CA=}~Ju}P=8^wPAx*LhE?dezdUrn)}UrGWO+O-f;u zN@{Mae=9b8mdiqC?eP7t66-;;&vH2{DJG<`5aJMgONpS#AcUAdcBe~YZ2M4GbMSN0W%}ow;e;eWNv5ma;=M9E6nGm zQb|QK=s8Lbnx2C+*gZW-pa0w`WAkos8SiY{GB$S4MwfoBT5CSxX7)hIsFu;(*LRU{ zTRsGqK&tnPo_XxP-7bIJOL*3NQTpm7s`ny$u}l=6x9)y6{={#6Fv0vulare|x5%Ed znDzYxxki>3fXQD1e##=pjQpZSUKJ;QiP(@wG~{Qs@&aJ_ORO?3wx^6vVg|`?Uu@?4 z!;+esBq{HnmA_=BCMLyA&BN5>Rdy9jEjgNGFE2u@VrnT(J1I_WQ}Z;mX#IC3Elp8A z-yr3bGPXB0>(dPK*10aGCZA^zJ9+-zdYP`gO+hqyA>dmn0e{}6mID7fQ%h@V@<9$K z>+3G*Owkj~Zmu{u&?F8=sZ%0T^D#AfXQ+p%`I>Rlh^?u~ryeA(7o4($M z)B^E4n|a7LEF|Cj@|s!evki?+u>jgSv!^#PwSs8NOs%P@1)=pbwFpxSMk|aaM@BPK z<5ROv`Aoa!rd9~;cM2`}>tD`5*WWR{+N?<%)36Bo8dH-mXGp1wLS-~LtlF7cG5kZ! z&eh)3ilfPE;N?*3fW|+k642jFFH*i@API&-49K7LB8-yw2kA=O7dFD!zAvfp*9}+V zmV$lwrIU6ywbJk}X0Xe-g=xVJv@ zQ5C;@!$^*M={wR2)xi2h)lf6z>S!&@^oE;uHPG6c+6XlL6EDoT^pCt{#vO?$O<4obIr)L@drt6{LM49I{15;y;_=FYFrnlp!wl0H0}6Wu+szc9JR>Q>P!7En33h1 zACh(hxNK@mOsyf>S~M98ol6egJ!ae{_`}fTFx_ivP4Oqy z^Pv5u^GL=KATPL<<8(iov{Ey$-Wh+$v}=y`h6LphI&5k!#E&L_M@+3H{`qKf6dX0R zR`})13?*=ni6;BMHH-)OJB}zBw}B1#Yr-uR1Y*a`n3DFJ^MOvBFjt?!y$GVPd*>U~D~$z?Rz zC0&3IWIIVed1l7#ieFw4E&b$$sddAj2#wtk?eq#!@?(0ae{cZD=?^riaS!-tHtU~g z{B!CF^2r5h^7m%kUic@1wB!dfuFORzo0@#2MAGX6KbhJmU8DW`B5Y%F44Sl%yqH81901)+I~P+Mh}PZItQVF@eJfMSH3d2kqW!{UWhwB$I`wp& zy=Y09b_PK($X6r$ARALpCD7xzd)v;f5Sg&_Xc~K+BTZV1!U5xfg89( zLXb&kVn_l>AsNV|GdZLHPml>`YLE$KT5v#m^>MvDO_D5lvw|OFD`?>MvfxM6p zWQHg+!u%i~*IW$piN{-T8)Ra57w*A*_zfPwLwE#F;3+(Z7w{5Z!CCkPWWsm~P6MAW zjdp5)ry(?g#?S>Xq zSPV;Go0ICb(cWI&-)Q%UmRBdA1bHpKOih`JIyHtS5CJWq6%2u)An%&K0XN|mbVZX_ z=(mIRAn#274IaQlcm$8(cX$F%;Tb%K7j91FoL=I21+U=^>;swl9)N>z2;^t?m7y9` z2btd1hB{CeLfw>Yv)xad$nKk@e%oZvkSaTJ4#)|)AUEVuT{qj)MCZp_018471VacE zg2GS)ib64vS0XqdJtT(|kP=csYDfcK;0-c@Bmo&UWR#FmL5}#8;0dW9wS1l^4IVG> zhP2>-^xy*-z!x%tOr|qK7RU;IkPWg!4#){Ih{~8LW2AhY;v;;5&ma?TnP`v2EAyja zFx;*(#oALu%ZD-^!DILxp1@Oh2G4;nZRlSW*@U|psws?+*iD6&-FJ$lF-*Kwe-D9<6_gCOsSSEuOn@5AMTn@BkjdBaq4Y6L<>G;5o>b ziR5EHH{d4Rf)%h5R>5kJx%xV&1XUm$szG&FCf{gUif1q#MkedSU^t9`CFt@orhkKqmGw?>YbnLFQXBzmoZs%%5bwBonxKFdr6x%r0gAF4ylfU@*u^ssW1%`OotgT6K26|m;-ZR9?XXYAa695k3kQBfe;1qd7FJ8@Ay3d z^3muRNDe6=C3wmRo(fM|NC)rf;8)-#+@p;5;Q>5^{%BDk6CC;3rd&6KfFDc)`K@Fx zXa`LoH~7G3;{F9!U>(S3YX^dSlD(H3{Z~E*BHs!b2}?0p0rDjQxwv=)m*5B-g=26W zPQocT4QJqII1A_CJRAV|rbA;$2!E0GRu0nbumg6%Ww;_=T-pnA(Q*+k!DToGyFtD; z;|JM5KL4>DIu^{gy`voq*26#;7%W)@AvvDvIM#C78x42D(X`o;d zOo2q;D+Q+#6F_FxZ`pn>%Qe5_nT0qTe#5^9*1~$&02@KB5tqSoSOK$P5cGroFaQQZ z6v+CKTvgSB7Zj>AZW$;GMIZ}g1wY6r<5wog45{G&5e~v3kk1J(gr8t4OotgT2;{xU z(a;xqLl=-Ms;W>8szY%o0pZY;%0+-&>dUf#T)61db$K6qW$=ZJkQuT-42MS%ctP)d z1+PKA(J=#>LIgB}=FkFKLQ+Tu9*`VTKuSmh@99II;4{e5@&gL>t1Q=DN4NpU;RIxa z9Ux!RNkoezhPlL%HGoF!x5m%}nnGf1PT-z~>DWzyvvjO;a014n&&QR|m1TxBAm5<- z2<53<1-V?Vh(~_MSQ+GZhv85aszG(Qgz;s#0y|+BOrkJTU@FKZ**h|R1e-uE{bFGo zY=@Q55*mOk-Mysdub>?M^0NQ4!(oIYAlHZ|AO^ZZH;`*T`RaEh)PS1Mf{dF%ILLKm zK6aTuWP?EzdI&6`nU})~SP2tgB1{JPU`!i`mQUK;rdj0EH-3;Ea)K9lLt01&cWLS~ za1!LhMzZ|Uiki2B=FkGhkj_|03`xKlmSES23d%KO6L?MG-@rxS@-W)zDxT|b7J5K! zXi20^WV#u)LM&{9b0xeb*_ zP63MJt|wk!kd@T6Xq90$^poLtFgSrT?4wk3U@puAZ*V{-kTt~hWV{3Rz!F#r%V8zV zg9;D~4V{$FHhb~tEfjDo#KJb%4m)5c?1G^nt8zUlu&l8C1ZQC_$YR)PSOhmovm&m1 zDLg;rltr;rlqU?=72O8Y*xo<|S3?Z&!oX;!06Z@+dINvLd)N%uKoS^Ff^+fDgZZ!k zCcqflWGFnu<^-$;vG;+lPzr(|7z#s9koBk>AWKjwV2z8H@sah#WHJpD^nvy+dg~Pa zPpu;@BWq8+saao`3i3grhs3|)_TO8#G=(n%QfMI&@lR2)Gw?HHpUC(jG;GxUOH&lD|laXFF7c}!-p(w}8UE3@08AcJ#ykbznTY#F#^B>#sr{)Tt(2p+-% zxB{2pBpioha1`X+C+l>5Ko<67$&7`-*bIm4&1?wfdw!h>VfPHIW5X0Pqt-}R~y#AdSI$+b6TsMkK0ogTF&+|SOSvfQV_e9Ae!VY z<+94M1;3PKvs!S}o<=n~Zcm(Go2l_Dn4|Vwjdz-xJ8&hMRW<;1=y94m0nN-GO_LQpQQMM`4K<^|&iy(Q>R_ycak8?eeRM}?ICHTykOI-gw+s+`a zlRm_&lI==%`LvMme4vQT^mWWjeiEQ3WL zi_5a~Aj=YA*vfLn2pA5s)F78Ny`c|bvJNa0wFr)2eNo0 z;R7KCr1=NKP-phP^wQyM41+N+8pgtSm<2Oo21teqCc`wC3O~UV)18hh1)2>Fsp(wY zc_0;B0*hf0)Fx~p?gFSMgPoi|1=)9U_^g2yuo_muN+=9lU^8rhO|TKx!FmwQa%F8x z*0_>@8_10XJAB4Q()tIaW4_0ICr$eXUcpOv4!?nPrdYTS_h1{`g*y;NWNDE-unTsA z?3T5#9d^KOxDA&;t`f!OB3ytIa1;(e^nU)^2YW$kAV>tM-4Qqlhu|XG>YaoTY0+&GwDdAUfW&ex6 zEA9=r1vgFYDee>a9Uj9YcnA;R8N7hk@E1Il6@WkSyo5hM0^UM368{_b6MTdZ;KDw1 z24+_J{t`%64LNI}vU|NC1+)tbcld*d)V!Df?eCOv;92_#R{*$$*mq zd>{>^2M44BFOW4;ZwRA2R&!drq9D2~;$#NdEd_8bo4oifyR7)5#UL9VKga_)AUouS zT&6!Kt_)Q9aRWj2r7Stjzzx8ar3XQ_3*#1o5a=n}AicdD+hw5y6o+C^RK|Zf{>!jY z8cIPJghEM>VYxh1gt{QdaxL6yAXDC|xZzL*WEo4s#jOrCU@F@+ace^zs0X#sYCr@0 z^=15Tgr^BqC&8w;J)sA5g!a%9T0nD{M?@J|+Tm{tZJ-sj1{p&-;7TF7;}&JR8*Ufq z1Pj@Y#GQ{Tww>|Mm1dm-GAc=pB%mt+y`T>ahCvVmvZyYL>jPl`^oP1+CP%>#{CkNz z40ovMm+f)*e}W9yOv0T8BcPJ$*T;X^KVmcmCW9DG1gn`R;FpA?a3gWY!Wb9@qd~@o z@whVVij7ocD%(;yX=W+VESL*3VLHqJ3K;!WiN#0)=YR|29a2{ zx57GD1FOLrR94|%2`fPCB~uv-Buz<6c9B(}&1_qRamBw;_NirPWh$9S1nH?VYDob% zK>UHlDzs!E1+{9uBc7dPAhp~Mvb(JGr7~id7^1C!U2I4b?!i3-2Vo!VHT|in*#Z3f zAr=0k@pQ2}49CCGjm*o-=S0uEAB1-5{mB0+%6-3@_nc1nI07 zaDRcHL252Fl%wE0?pZizx-$KeG;hFlkodpCUAP0c<@mpaCj$x!BhAh z9z%J;p5RKS3dR2#_Ye3JY|L^aC`1a}_iTIMCV+Ql*k@ePKf(w22mX%cznVm9fLk9v zfisbuKzhGSxh0Zl;)by;yk4vi(Qq~F#4itjB!h&e8=Vx7 zJQb1%BrrM1*-xGVk!DMU>j|>%83YBP0OW^22mpVWgRO$}_~ogQbRbWV$XaP$m_?fM ztccV;7p~ZHXh-W86gd%mK#uc882aIt1T)}fflMIdfG@7tWW+7Rc4pJfifj3E;9o)- z*>ST${PAHCy7Uq29FSWc*p`yz!_cZlA^fv2k`jjCFAP!`OWTdV2$~cm6jyGt6o+Dv zl(f7dF^FCY!k{F~N0V@gQwGXG*=WmyTM5FUGE@Png{-(fhi4$SndEj;H@MICZID|@ zve+q07|C&eMU%_9tGIF^l1nYQn!3XFW!ML?umNN>IeIy@SP1LbSOSZo14zeOgj*H2 z65(2WR0;QG=@eX`?@u5 zD`*K#pgG7O7lF$_6s_N9myxX*LJJV1rXWV*N~V&TBrL5XGn@9fT_6%xo|mobI^Ypa)EX;SdA8r4vd&7y#1I`s4P4zR)M0KMMaqQJRY+#qn z#hnAQVJ6G~IVvPvT!H?oyZoZ;&MlrNn{S4#!CeijU?nVrrC^7WG|&oMY2D?x){t)< z)?$+zq`Qcn#9IqBh$n;QR{U~6$kcEP{>`unHo7Caa0U}HOM17D1G;qHXJApQ`v zJ*F%AZgu#!JvjO}-eYhSj=*6!1P9>&><76(3nTB_xYp%PLCW>*<<2#1_hBQ~Tghl7 z$uj;TFhPdJ|W= zb{G8jaPPt$u+p&NNgT2N4Ojf4Ka?&kW7{J(9z!?$vMq@`h2LQ+HGP8Hp2!mM1&G@k zyS5;HDcBo$4H8!hDV32htH9#EgTEjY{)8^FuJIO+mDoS{C88TmRM%HqmiD}h@aq!9ApMKO>Br9hHkQIOk(g+by; z;*xnF+hT8NvTgZA7rSUlR6J!6N`qu1_awu>%21vG3k55)?`0&mR^nD+f3zLXHd-%b zJVAe)pf;IR02!TX;noCsC`}$ts|eBpQfP|VJlJ%!m7i!HL?;H`~Q@=gvXB`zmSr~o@g?Yj?)6_LLC@^CWW#(q=bK~ zNrB~_q!rc(e|@M2(y^rGVk4cjAzA~FZ8_YfViAiM1q(82VdonQgz~3EY z3=~ZU)K?(eebIZFwmos>tRcEJ$jTW&2HAduiESU~4PtB9vHz{n*otHsSsBK6=Mr%? zM8PBJB& zd5GOq`Ckl0ux{~629iMh0?EEq=vD?Yo!y<()9U;G00!}EX0?b_6REA$(;XnRf-%;J3*!?(y zos@cFZ*42&q&`2fdpm;l6*^|#F=SF{HN=k-^E zpV|ZYM8Otug4Cm@_A2QDSRNxeZd}GxsvJ~hP0HDsY(&i)m^VOGduI3M#VqZg*{krG zgKf|3vt07$4UDbx+@8<_z1b)GX;NF@uBBI%KidP4_FdJymHKSYsLp@3r*;lg$**at zGRR%S`hLuZ$Ruo&`b+Ew#M>*QryhYu-V7uxI%r z=9f>y+nxITLw3VHe=rxkX2{=n^Zz<`;jDFLO2mBaH+8<|#Rn zZRua7dsuz4*aqg6@`O!&My?Sm->+IK<#5(Qc;S+>E7Bu1$pyzKx~rJ}lTDraRcoQ# zY+CxStyDdmw)Njqfv+WAhx7Aie!LX!s-;yoU9@mB4|UVyi{G?yc2c`6DD*qtm~)n4~6 zahx}+h6!j5Jt6OJ%smonsWo;WkE7&DNTGLlYT;^qBCUXJYJhr>NDDN}XCQ7 zWMkVLpxP$Z0&PnJ)ogLM2CB1(wVAdzfvRH?&CB)~w=L#L)x0EHo1i1?NY*7|yw>gT zs8Y0TVZD3OLAkmjBn2T^bCkc^r}ITEF63DORWzyQmCmW4(d%Zm?)%G(WcyZ_7XGLz zGO6ZWFb4+7N#gUH2ct*+S?ZZ?Kp$cDNJvUT9)+~%o@4TB_qeE4)NY~%HO9aLgUT~U z7Y>bhJ21|mlNpkzL+8Z@hL8L;E@WUql{gtyepyh3CeysC{h)3flt0dXAgOatK4wzB z2IKs;6f0zNE>AB_h+?+0+LuhTx}Zwx!9Fz`en}y<@_`niCVsNJe%*(pE4KY&R+u*z ze%I_fT}?%$h|6G65w$sm7Q_3FW<93YeAR)!?n(vcauY>bFU7jR9ZOFZ;>avs%s+3z zya7&c3E`qFrrfhnuY3G8{X$%bT|G%jvdNU@`K8l3msTS@wJlZ`rXB^9M;Z>a(9|@7 zS@D1pYGP__^VbEk9VwyurO_($8m&RznztioNqx;CrqPr-F~2wE?WE2@c?0>!#Yjo@ zA&utcC~I0I*gG%prgOs{+MEOP7RXCya;i~M`Fm-tt)zBhn1m`kDZX!Bwy5n-=>YzD zgT(S!Np;GL4)CDlG;dP;3q#p+_n+=ayU+LIIo&WgFKc^cOR56iT41^#mr5^o6(h_1 zw%MiC{Ir^va-`LA+qRWf&mCG|!fmCQHIw|rGU{bUjx5v2wy2CclR@(e+EGSd)Qss@ zyS3Y-rpd?JoCER(=oR{f5E-r~XPvh7Vug}*<3gU4Q8zPlblO$Mbegx#x2!6fPTHod zG1BH6UFJ-1?QuinA~Y|nx}?*ZNTJSR`K3_D%BoL3T4}4WmYcvTbONw+2iV@()wy;tzx@d#hT`; zMOfmOLj9mFU&6mBl9^%PS2_I4m_+CLey8Eg(yERbY*;6H4Rf*+>AMbUC zS7nwY2={ouZa|U;6*3zw$C%?}r2KqBxGNp9idmR*kQ?2{sHN6a8Z&c4gG zvXWty zl~wL6npc`vq$NZ7u-j455#Aeqk>j>t-e5J!hmXgY-7<@{e{1lVg0$YDS^kIL4R+7U zAsNU_g;PztD(bACmPfs?X__MnGa0{vce};zbW}ZxSpa4n;^V6*|EyYN+vO@MDyvpn z`wh$V`kqmFvuT;s`m999lwcQR^;eK?#$Kk52pVr!DWDsL@N867&fYqD6>b6a- znuz-!kN&TsdE3IPtI!;A-Rzs=?@O;tTtj)})I5EqpY)4t$^Try2Hks7u5|Q%59cOK z3}xgnt(XdCbTF~%oReyvtD#osq(N@fP=Aa2poVhfVW^78rKPpKs-XttV*IezG)Dc8 z`36^t9^zMB_7S~>Oj6WT>#+#(#X?%V#=;Y^Q#w?LjI;13Bo!f7KjnJ!qG!b^`YzKW zm#L{fN$S>XnGW>1z4K({k3Hk!)~~6mQ5ak6nyPYca_M2_(sk0oZhpDj^!0SksaKU{ zgm2Efp3XzDO-a70rka_XL4AHrb%r!->uRb;vTat>W|qQc;lWl`<(X}-4Rb=BB>2iJJJS+1+yixj&rz5N9#9zPcNrwGP@BVaz6P-rPH9Rp9PI zcDYsv$je?oZ-%tnUUYc@*G>=OLheVXs6a~g*0k9F+oa_w)0F!w&ceN!k!n!oQ(G5I z-rO%PByBTwinxwESV%8gxFV|kbN8(0<19j(P0LT>HBAHeJJH$W$=6(XK}NcIxDHaw(@@)lVQN(+Ed~zT$>w*6(uz6tv!V*9-_Ic z6lh-Zunvzf#Po5rJ9}qjlh@`E?yvq1(HyGVP-4^=#eb6}z0Kn_4^GleLYQm@=GUK6 zkomEHp1T~{Vjbt}r3hr%P(A4Q4TjF@t~=E)RYt8=~X>_Se%{!)U0kt(bh zF-JwJUbv1<&AN4H#ra69k!4Ql`FzHTnsFj#;yx=<-7Ll!xH3|eEl%2-Bh|6u#M~39 zJ`~qlJFFKazWM!S?Uyw??@7#p`bqp!q>3s*z3xV;=_QEqEK;2k_g&<)P#mp`>Jmn< zM;8?yikrEMDq5AMiwUJS1$9wdL$%6|9$kzkY}mGs*iME?1?OyG*c%s>q-NsnyTRn5no8TYGpnx`Ah!2OMG>Ul|W4(_Jh z!)U$=-Bf5Pw#|r+Bi)Vb+RVEzopx+q&h$xdK9VNp;xOuPxx4Zh#wq$)m=^r~lIWK< zX2Oo!Bq)betD-HkMJN53K~_@y^Y(H2QdX(=_);WXzNb1+ip%+kUaCoHYPq_Xab|b( zZ0gznaTx10Tnq(DEkE^At4p(gj9%lVa+jgD)%x&mS(0hkM-33SOCPnq3^j}DqfW^7 z_&(~BY%l1eyvnk@rjM#zmhEkQRPhSzgQ&9X1K)mXb6NI5u71W5TXoI3-p?+3bF4&} zy(p@`a+H%nuD@}i6XbcNccJRbe54N;*Fbmst7>BL7Z!=BdDr3DW*l|8z1y@12yiMr zKn*RY1qNLjV6;t0#S_m1XQl09lXGg`0Cv;Dfkw!fdHwR|-*AyRi@qBOp-(=Q)bmFf zsXxCq{=xd#Pus9yvMIe}SCsNC&#tvatIp-IY!G9lbvHro*iIdDz0oc8Gk2>PwXi(% zhJTlX9;dIAn2i%uVz4m`p09iH?yc7oe~K$lWkQlu%OqL%7A<`)U#qy(mkm~9E6~sz z2djNFuwyTla?&Y2FiGlr-D0cimil?pw6J9!s*+Y@zs??}dR1gE)EjP$0wc7^*?%qa ztE`96%cUDP8?J6vWaqUXuHIE7kM6@&?n-PA93ES?lD5s+HgD9lDjc~Z#;Czn2%j-V zZLLCzi^mv8PQA2y?Or}H%Sci78l%~!G0G<#%e`Y%4RMc+Q6A6e=Ci`-=I4nfoj9aS ztA)*7(ljI*9Z?E$V~lzpPR0+%DEF#FGmhv$Tb8k^OI3El$gyePQfdg$!gAuf}+F8w*>+cr|_%ZrAb3vj*+5Y=W9r zLkrCNe1ehd@pZ*_w5(NBMoY<+W32QoXwxV$5X3 zrTvUP?4?H6VQ`o^P3@~gsxPJ)=I*0Px!qc{u$Y-D=6_F9f7GFQ8_!VT4Jc3B8ERr( zVmze($PP->t4;L9Tc2{tAQ-?BJ9p4#D4(8;KG*ANgKgVptB87--@sf>qB{fb-th=? zFTv=;XcsG`->y63w2c3&%>$yUCG@)i@@~Tl2 zP8eoyb^M^WGJpSRg<4ItA0^Ce)OQorv0npHSugdl#XY@Ao#{k@FK<#~ThMHGHyK07;@XQ>&N$higf6CRYeVxekt!hWj9sd28 zwYH8YdyJj`cKXbfZ5NbGfVFwF58A87x2GV%`-}r-%bWEtf7+Q^#)AUJN>kZ=>Uw)B zQwIwVa$J>U%-|7s>gLle^jUW6eafQ)BY96Oq$ABgx1-Csq@Bl`7D3!xH&f3sV||W% zAlNba}Fp1Im0>FOb8ns1I9R{wpni@{cQ1`~{@o$1?CkE&XcG^$Umkw)Db zvz zWxHwZoxhwIdYn?Hy3=9qcQ$g&ed6NNHD^;Y%rQ&U2QBl|5p_x>?#<5r_k>CAcu);? zc9kCV3hN9x>XcgFS4*WaG;vm%EvKKUr&K6u*cO~pXW0&FJHt49hgNm(Ty)5c25~F8 z|Mj#QwC0r2!)NWv6FUm1{5U>^GXttonE6idNN)idD98&L7{Kp7Q35I?$Wh!q9Sy=~l! z)JK3^=T!_AwgTtX%K=yxJ8$fTr|XI>@Z3_H`|5NyYQ}?P=hc~bmecw(mft$B9Q}#? z=)77xg8@Dq(Q)e+V@baqd6Pku-Km<$oO4$T84y^P({>^#9{hq3Bgz z>Pb`U7YI%dFRJ1LDaZXwsvEAu?Xs~`e#u&)&24qBE47dnYmRGUg~Tgp6kStxO1g+Q zlZG}-UCnGKnmUCU_sl1kR>^LR&Kh^FOMh7EQt7f<8ATr( zeOcv>=Hj~WHFYeSaKp~aR^ggT9mCi->Q`g;{xbT-^CT($jy3b7ZKn~EjMDUP9r>eR<7hrl6Lx*7jxNn!cgW*S?tBD*mQ&4r7;?F}^pSk*OB* z%uBzi(e1R#>F)k!oNZ4QDO*JKU&0l#H5t>FaJ;3Yi8-}1G_j(SS~VW7rMDG*sE!O{ z4;FoB^uQsP>iFI3SvtAil-yn6X3!DnDF{ z2`YTe*w^02CMCErZ2c-MdDce8?cDS{nFw4R>G!F4!;v!GV3?4Q<&V{;;p}Y36XX8Z zlt1S`(mWy}urPN={wJ#P2#%sESa^{}$FYA74sHIXCl=;qR;wo}Ml1$kkphc`>)q?M zYB#i_euge2yL9{$wRr@M$ughsC`!EHiE@l&AMJdisI)l>%RIU1Qh6kb;!6b%Pcr&sV4Br_{Q?GzAjE7Q zE^vyErt&Kzjomcy?UMOB(z^`CLMH8ncn1EN>Nc852$wI{Mw4s77b@u(a<#Tu18pSR zJSEa)BJP0~YNBkPd!bg2!QStrx+&Y%`Q3PM#!DX*#u82sjTlP_^1m{U*X7IRM?TNy zA?u2Ax}*!4&8g9xZ(gajW7!jn{!kwzGEL+^j>xfp7{&LkHSd%+?VmHB(^5GUY1oLj zDttUu>F}11oolVD{n&E&Uq+98Qlwe4X~lXSzJB)Xr~7(CGZhVS zTK1RnnV?nn{I+Bs&dJ}Ys0mtOTaNeY`2A9^rg*|$*9Wm+ zo=G!97M+~lcV7GR?dZ@U^010OcLh~Z_Lbu$$G;5r2_lOg`Mvtgomdo<$43f?h3w6s z1^*cRck_FnYZGJUZlk!ta)T+vX*D4-&OK@0e|UnzK{5jBAqAaw{iF6s+~Zitowqet zea|15+;6^NA#03}2$6&FugVi%1l-Tu){0B}d{Bue(ZpUKRJBW*r>l%1**>UHw1W9R z7-dP>`Q+J0Q?knXncU>2e-|f228i+-5425NDYO+K=Bl;%m`n`}WE8T6y(WUc`v*G>7(fx&tFjuMj1U=DW=FJ|S88z=XIW@9t`ReSRF0{n z@t~xtCT{RYH5AuTh8S|zyFDyb>1Q$9qV*fUw43$Fj+d?0NA+~77UMGiqmzo9#vK#m z5go7e|DJQ|=QP@AGp4&Sbvc6P@-!{LW<38B=+g9)lPaOKG{N6}ut&eXEcn@&{p9Vk zwd=Fmt#42}8JkJA{AVXMj>zd7Vj}zWW#Ij7J%dXfk2C3x$$m_%LRu$48;cD|r&Hgm zPR{!7tH1oo(b5Y}^){nYUnghP2n$8`iB(d&#IYw(Dcb-Hh5Q@NQa+RSOEYBGbH zN&PXGolvty(o4B!H?bxqj7R3YGRWP94upUWlPNj$WO71CT0(aA`Mvs; zvL8#B#iY3s*whEI`cdjwPze8-npf}-^2g%GJL5}B8|~($*3abnVYj9p%%l-pyQ;La zaLeCS;o{1)jFH#2-c`+(ZARj=vluFm@j|)TH03=vXZ@b~+I?YpgMGVMQ$b2_(B0S# zUOr(BGY!hIh5DKEqcI6o%mV7C5;*HON{{?iZ{Uj^Ew0D4z?uYV{%mcgi%UXhRcsEu zB5^|1WFEyh=&riV(Y*d05SU0+JB~q#L@Ht*t`)&@O)#H3Jm6xJBZV^L{ zq)mQlQjML}Vl^@3WX9p}jpdh^R;6EgsGED^RW?&{HIZ${%H+=a5R}I;tjY8V`^u4` zxzJ`cleOOxCp7k~-<=Q1KIXhD(=~d${8?>fmQrn8Km)$?RA*#&fAnvGhyj?Bq-3_6$GXZ)$9%GFY1Pu(GLCHL+-jMQSYOL>*rK zWKq&naTZ-usi6yLu#cW<+d{4@a0@TOU7belT7+KTTfJOF_1k)@gp1kk=B$GD#-7_^-wvFo%{QyY1gi+-?w=~V>$^S|+os zSbXo{4yK%y;T5ev@qk6e-=^as@lQhk)ZetM?g#|xDh#6h{ z?$G?9f7Zc5%E5{D`>E&WQXwpd%DaZT*3H33L~vLC!v_s<+vZRM&>f?4IP3Gl`u!H~ z{xj9hwWP_6kqM{M@*HZtSnQ(Ya)2Z&Rrc3p#h%F`i*DiXv^9sSCTSV-!qj$aR_N&} z@ek)vforwW?w83y;>&q2jhebvOYc~g(^-FLGwb1{HM6B_FT*cM7GMHvxFIexn&V4j1)9i5rcb;44b-w~9}&)9?O9 zYaVf={50orGA|=#*-0P#)lG?Awb}&2&^FSD6Lbog44DTp>I%Z+AaUnqf?Gwf{h7Zo$4pUn|W+L zN?NkC!y$JwSUum${;@8CPlu>FvFuD$NbQN`a9YSufMqh-Alv?+K@)48*JJ9Z^bLf_ z+3ug3O}ww(9DS1z^V0liA(eO=X)Y|JKJB0;GkJHFOeAK#Ou6dd^%+U9l&J=XWa=Vn z{5EpQ#LvW2VNrNp<;D-Il8YMzi)EQ#b_@FB9q`cMm ziK)~zb(+&dw*6A^s$L^~U)*z3F=EjQi{w~Djd=I-k~e9MTMha(i}5^ND!HA}D^#7q zIA|2cvb;OFv%32Jw+|W3N?|dZ5V>?Lls3Hmx#ZbptTbnMOGA}o7a6U`LQeQEU)QZO zec`0vB(71-U7@PlF3!itv5+g_CHEiN16tIMHygI*w8mV}OP$@tQEyH2CY4m7=c%(b z^_pE$72ZwX{->nsyc_-7a5E8`7N(l~Lbz9$dWl_7?l7Z|_1KwwZ|##eq$SK&4I@Ox zfleLzz3R28?n%AcdK+0&%AH};4v^G0^TaPjw6Zjd?D+5V$K$0{mvby)gzlxLR?Vy$ zn-kWc)J%arDc7lt-l6G_bdo!r=JjTwGR6o{cGcjHBl;XUtcU3P-JJXe{dmG1MqGKE zH&>~$`%4XdE^C0&bJ@vO^{lG}W4CPnQtf=@l>dHqWRY@4|Ll6dmVaJ;=lH9}?EY)*CN0CzxsT%vfjzkP)M6>nRvo&G~itG%q=Vr_q@ zpq@)i?~2a)?-H67{#>ru<(qPzFCiZ1Z@rm zsR+qLGisSt;)+jvWcFq5tl)6h4z8FrQog(V@h_Xw~saxH@y}>t3y2${%OkJx({Z)&)9MRl81LXQph8O^&`* zjT69}4Grezm|gXAT$$qS4ZT&Zl&q7CH5`#*XESBpwd(L~j@BtBVRhr;_V<{iT|O`C zX3d(Z(|_m>UzNbB!+&W5k8jT?D(0k?YpS)U=@waeWt@!GD^A}?((vLxYxY!<G%^1 zspF_&9}cN@)pEpHSf^;KlUSX@JoN>|^6n>Du4&N0IZI&R;0R)xs|P<5;!ViWm{k|2 zZ&)IaxR_H1GmW4hFUQP8fQF;hDJ``cTu$1ad1ShGRm z9cl8b2HUsUnhoZtUf<|Yy?YO+-*ensx45dbsjtRMr?ig6!B|SCTo83VQ@JR+@mQ69 zvNyZ08AIM2WX^oeirM0|=lAcU6yQof z?&8Y0?2?PMN{!X}3+(Jpjh*Ek>M_-?Hrp3`s^Bupu3r)db8Rn|dR&OoS+D%&>~p(^l_<}j~TS+eGxYBGx`pJ4s7LHm}-RLNXG_;GPQH&t^l$tV`#?3A0h zm%^Lu^*-Hssa1>+rym#4C{*0FacX;r6H63a9rMhXd9`f0#?l3E3NZ3>{5Ze3RO5D| zRsDY}+#W7|h5X((kF9q_o9oQ)-RIrl7w7uRAiVh*TCQuFce<{;fI`NiQGGvW&ZpXb zFdjMLE}@hD5({r*>i_e=JSH@}q6X1u5(=IcNeb6p$!Z^?Q=fb%1zC!yC= zUKPQXybvNz^fq2f5$~-N5~k|ivffA0Om(@f#e1QE88zAM_;0GvMg27r{S9*?3sv?G zrTaQN{iPIUqNY{UUF*#hRn$voE_puP)g0ecD*RsjcV_5iJS&o!_;=o9@pT^0?~{lA zDvK)m8wAX^SmcghyLhj%kPNI>St$RzU%hc5wI;p6$k=AQb)ofFrEQ}oKh$5Fpr^7# zf8oN{7T)x|cr7TeS@aJJ*Q~=(uEtv z9aWQO3?BzN8q8B8X;*oE|R@b(=rtQd462TtBz_laUBUe z8E3WM-skUdKeG$JUbW)-c2c*oC?0}^T&VAu*~+WjPwfxJS=1y%X5O_YgbrR?@owh0 zkkGptx$}<3`zq);r=?!cwT-qdom9>jT6kdObmJC5*VOxlUti@JK8S4@9~{TK^mbcS zIr~hu<@9-A_?sMq`fPc+cDh>mLi1_fe~EE#`r6z-KKq@EE;5>JS^RiWGIQs%e+@Y? zjcr*-dE0k$$I2Bl4_#r}*SvqO+oZcqeYUYJ*ToyxG#Iw5=ERj}*!E!i;p1h2skhZ_ ze{+e-`BL+V?z+;@kB#2=;b67uO9!zn%P{wP3~uMqvcj)p*p_^jbZ9uHb-9v`3ubuV z)9XGA?(?wVO}1tAr+=qQrEj+hOw^WbSvlPHJnOM3uiV?cU|aG{*0g$>?9Bn z^DRNXKZm(S#!H;|{_0L)*Hr3dDc2P0a4AgFomKt6UAGP_Pf1nC%quQQ%*oNqDu%Lj(~9zQb(0g5Gg1|F zbrXw9GSh(GRY)q$%t_G?O3g`4EKUV_-AE6tT~{|FzqmxVEU`!-$kiy`#XsE7$KTN< z-q$lI$Ug{ZKyh|vfo?%!Nd|_R`uJdP&w%&<#}E&gy0X+F3}p~iVXi?6H8lz#J9X15 z3lwy-6mk Date: Wed, 2 Oct 2024 16:50:42 +0900 Subject: [PATCH 19/28] =?UTF-8?q?/cashier-v2=20=E3=82=AB=E3=82=B9=E3=82=BF?= =?UTF-8?q?=E3=83=A0=E3=83=95=E3=83=83=E3=82=AF=E5=88=87=E3=82=8A=E5=87=BA?= =?UTF-8?q?=E3=81=97=20(#182)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/components/functional/useLatestOrderId.ts | 14 ++ app/components/functional/useOrderState.ts | 116 +++++++++++++ app/components/functional/useUISession.ts | 25 +++ app/components/pages/CashierV2.tsx | 161 ++++-------------- 4 files changed, 190 insertions(+), 126 deletions(-) create mode 100644 app/components/functional/useLatestOrderId.ts create mode 100644 app/components/functional/useOrderState.ts create mode 100644 app/components/functional/useUISession.ts diff --git a/app/components/functional/useLatestOrderId.ts b/app/components/functional/useLatestOrderId.ts new file mode 100644 index 00000000..3b997159 --- /dev/null +++ b/app/components/functional/useLatestOrderId.ts @@ -0,0 +1,14 @@ +import { useMemo } from "react"; +import type { WithId } from "~/lib/typeguard"; +import type { OrderEntity } from "~/models/order"; + +const useLatestOrderId = (orders: WithId[] | 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 }; diff --git a/app/components/functional/useOrderState.ts b/app/components/functional/useOrderState.ts new file mode 100644 index 00000000..079aaa77 --- /dev/null +++ b/app/components/functional/useOrderState.ts @@ -0,0 +1,116 @@ +import { useReducer } from "react"; +import type { WithId } from "~/lib/typeguard"; +import type { ItemEntity } from "~/models/item"; +import { OrderEntity } from "~/models/order"; + +type BaseAction = { type: TypeName }; +type Action< + TypeName extends string, + U = Record, +> = BaseAction & U; + +type Clear = Action<"clear", { effectFn?: () => void }>; +type UpdateOrderId = Action<"updateOrderId", { orderId: number }>; +type AddItem = Action<"addItem", { item: WithId }>; +type MutateItem = Action< + "mutateItem", + { idx: number; action: (prev: WithId) => WithId } +>; +type ApplyDiscount = Action< + "applyDiscount", + { discountOrder: WithId } +>; +type RemoveDiscount = Action<"removeDiscount">; +type SetReceived = Action<"setReceived", { received: string }>; +type SetDescription = Action<"setDescription", { description: string }>; + +export type OrderAction = + | Clear + | UpdateOrderId + | AddItem + | MutateItem + | ApplyDiscount + | RemoveDiscount + | SetReceived + | SetDescription; + +type OrderReducer = ( + state: OrderEntity, + action: T, +) => OrderEntity; + +const clear: OrderReducer = (state, action) => { + const effectFn = action.effectFn; + if (effectFn) { + effectFn(); + } + return OrderEntity.createNew({ orderId: state.orderId }); +}; + +const updateOrderId: OrderReducer = (state, action) => { + const updated = state.clone(); + updated.orderId = action.orderId; + return updated; +}; + +const addItem: OrderReducer = (state, action) => { + const updated = state.clone(); + updated.items = [...updated.items, action.item]; + return updated; +}; + +const mutateItem: OrderReducer = (state, action) => { + const updated = state.clone(); + updated.items[action.idx] = action.action(updated.items[action.idx]); + return updated; +}; + +const applyDiscount: OrderReducer = (state, action) => { + const updated = state.clone(); + updated.applyDiscount(action.discountOrder); + return updated; +}; + +const removeDiscount: OrderReducer = (state, action) => { + const updated = state.clone(); + updated.removeDiscount(); + return updated; +}; + +const setReceived: OrderReducer = (state, action) => { + const updated = state.clone(); + updated.received = Number(action.received); + return updated; +}; + +const setDescription: OrderReducer = (state, action) => { + const updated = state.clone(); + updated.description = action.description; + return updated; +}; + +const reducer: OrderReducer = (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 "mutateItem": + return mutateItem(state, action); + case "setReceived": + return setReceived(state, action); + case "setDescription": + return setDescription(state, action); + case "updateOrderId": + return updateOrderId(state, action); + } +}; + +const useOrderState = () => + useReducer(reducer, OrderEntity.createNew({ orderId: -1 })); + +export { useOrderState }; diff --git a/app/components/functional/useUISession.ts b/app/components/functional/useUISession.ts new file mode 100644 index 00000000..53239bad --- /dev/null +++ b/app/components/functional/useUISession.ts @@ -0,0 +1,25 @@ +import { useCallback, useMemo, useState } from "react"; + +type UISession = { + date: Date; + key: string; +}; + +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 }; diff --git a/app/components/pages/CashierV2.tsx b/app/components/pages/CashierV2.tsx index 72bb9739..2ed42c3c 100644 --- a/app/components/pages/CashierV2.tsx +++ b/app/components/pages/CashierV2.tsx @@ -1,15 +1,11 @@ -import { - useCallback, - useEffect, - useMemo, - useReducer, - useRef, - useState, -} from "react"; +import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { Input } from "~/components/ui/input"; import type { WithId } from "~/lib/typeguard"; import { type ItemEntity, type2label } from "~/models/item"; -import { OrderEntity } from "~/models/order"; +import type { OrderEntity } from "~/models/order"; +import { useLatestOrderId } from "../functional/useLatestOrderId"; +import { useOrderState } from "../functional/useOrderState"; +import { useUISession } from "../functional/useUISession"; import { AttractiveTextBox } from "../molecules/AttractiveTextBox"; import { DiscountInput } from "../organisms/DiscountInput"; import { OrderAlertDialog } from "../organisms/OrderAlertDialog"; @@ -30,110 +26,17 @@ type props = { submitPayload: (order: OrderEntity) => void; }; -export type Action = - | { type: "clear"; effectFn?: () => void } - | { type: "updateOrderId"; orderId: number } - | { - type: "addItem"; - item: WithId; - } - | { - type: "mutateItem"; - idx: number; - action: (prev: WithId) => WithId; - } - | { type: "applyDiscount"; discountOrder: WithId } - | { type: "removeDiscount" } - | { type: "setReceived"; received: string } - | { type: "setDescription"; description: string }; - -const reducer = (state: OrderEntity, action: Action): OrderEntity => { - const addItem = (item: WithId) => { - const updated = state.clone(); - updated.items = [...updated.items, item]; - return updated; - }; - const applyDiscount = (discountOrder: WithId) => { - const updated = state.clone(); - updated.applyDiscount(discountOrder); - return updated; - }; - const removeDiscount = () => { - const updated = state.clone(); - updated.removeDiscount(); - return updated; - }; - const mutateItem = ( - idx: number, - action: (prev: WithId) => WithId, - ) => { - const updated = state.clone(); - updated.items[idx] = action(updated.items[idx]); - return updated; - }; - const updateOrderId = (orderId: number) => { - const updated = state.clone(); - updated.orderId = orderId; - return updated; - }; - const setReceived = (received: string) => { - const updated = state.clone(); - updated.received = Number(received); - return updated; - }; - const setDescription = (description: string) => { - const updated = state.clone(); - updated.description = description; - return updated; - }; - const clear = (effectFn?: () => void) => { - if (effectFn) { - effectFn(); - } - return OrderEntity.createNew({ orderId: state.orderId }); - }; - - switch (action.type) { - case "clear": - return clear(action.effectFn); - case "applyDiscount": - return applyDiscount(action.discountOrder); - case "removeDiscount": - return removeDiscount(); - case "addItem": - return addItem(action.item); - case "mutateItem": - return mutateItem(action.idx, action.action); - case "setReceived": - return setReceived(action.received); - case "setDescription": - return setDescription(action.description); - case "updateOrderId": - return updateOrderId(action.orderId); - } -}; - -const latestOrderId = (orders: WithId[] | undefined): number => { - if (!orders) { - return 0; - } - return orders.reduce((acc, cur) => Math.max(acc, cur.orderId), 0); -}; - const CashierV2 = ({ items, orders, submitPayload }: props) => { - const [newOrder, dispatch] = useReducer( - reducer, - OrderEntity.createNew({ orderId: -1 }), - ); + const [newOrder, newOrderDispatch] = useOrderState(); const [inputStatus, setInputStatus] = useState<(typeof InputStatus)[number]>("discount"); const [dialogOpen, setDialogOpen] = useState(false); - const [inputSession, setInputSession] = useState(new Date()); + const [UISession, renewUISession] = useUISession(); + const { nextOrderId } = useLatestOrderId(orders); - const nextOrderId = useMemo(() => latestOrderId(orders) + 1, [orders]); useEffect(() => { - dispatch({ type: "updateOrderId", orderId: nextOrderId }); - }, [nextOrderId]); + newOrderDispatch({ type: "updateOrderId", orderId: nextOrderId }); + }, [nextOrderId, newOrderDispatch]); const charge = newOrder.received - newOrder.billingAmount; const chargeView: string | number = charge < 0 ? "不足しています" : charge; @@ -159,9 +62,12 @@ const CashierV2 = ({ items, orders, submitPayload }: props) => { if (newOrder.items.length === 0) { return; } - dispatch({ type: "clear", effectFn: () => setInputSession(new Date()) }); + newOrderDispatch({ + type: "clear", + effectFn: renewUISession, + }); submitPayload(newOrder); - }, [charge, newOrder, submitPayload]); + }, [charge, newOrder, submitPayload, newOrderDispatch, renewUISession]); const moveFocus = useCallback(() => { switch (inputStatus) { @@ -191,10 +97,10 @@ const CashierV2 = ({ items, orders, submitPayload }: props) => { Escape: () => { setInputStatus("discount"); setDialogOpen(false); - dispatch({ type: "clear" }); + newOrderDispatch({ type: "clear" }); }, }; - }, [proceedStatus, prevousStatus]); + }, [proceedStatus, prevousStatus, newOrderDispatch]); useEffect(() => { const handler = (event: KeyboardEvent) => { @@ -237,35 +143,37 @@ const CashierV2 = ({ items, orders, submitPayload }: props) => {

{newOrder.billingAmount}

- dispatch({ type: "applyDiscount", discountOrder }), - [], + newOrderDispatch({ type: "applyDiscount", discountOrder }), + [newOrderDispatch], )} onDiscountOrderRemoved={useCallback( - () => dispatch({ type: "removeDiscount" }), - [], + () => newOrderDispatch({ type: "removeDiscount" }), + [newOrderDispatch], )} /> dispatch({ type: "setReceived", received: text }), - [], + (text) => + newOrderDispatch({ type: "setReceived", received: text }), + [newOrderDispatch], )} focus={inputStatus === "received"} /> dispatch({ type: "setDescription", description: text }), - [], + (text) => + newOrderDispatch({ type: "setDescription", description: text }), + [newOrderDispatch], )} focus={inputStatus === "description"} /> @@ -276,12 +184,13 @@ const CashierV2 = ({ items, orders, submitPayload }: props) => { items={items} order={newOrder} onAddItem={useCallback( - (item) => dispatch({ type: "addItem", item }), - [], + (item) => newOrderDispatch({ type: "addItem", item }), + [newOrderDispatch], )} mutateItem={useCallback( - (idx, action) => dispatch({ type: "mutateItem", idx, action }), - [], + (idx, action) => + newOrderDispatch({ type: "mutateItem", idx, action }), + [newOrderDispatch], )} focus={inputStatus === "items"} discountOrder={useMemo( From f3dabf0d1dc6576d446120254b69f8cf8dcd233f Mon Sep 17 00:00:00 2001 From: Ryunosuke Tokinaga <59079411+toririm@users.noreply.github.com> Date: Thu, 3 Oct 2024 01:25:01 +0900 Subject: [PATCH 20/28] =?UTF-8?q?/cashier-v2=20=E3=83=AA=E3=83=95=E3=82=A1?= =?UTF-8?q?=E3=82=AF=E3=82=BF=E7=B6=9A=E3=81=8D=20(#184)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/components/functional/useFocusRef.ts | 20 ++++ app/components/functional/useInputStatus.ts | 36 +++++++ app/components/functional/useLatestOrderId.ts | 5 + app/components/functional/useOrderState.ts | 13 +++ app/components/functional/useUISession.ts | 5 + .../molecules/AttractiveTextBox.tsx | 14 +-- app/components/molecules/InputNumber.tsx | 28 +++++ app/components/molecules/ThreeDigitsInput.tsx | 4 +- app/components/organisms/ChargeView.tsx | 26 +++++ app/components/organisms/DiscountInput.tsx | 33 +++--- app/components/organisms/ItemAssign.tsx | 3 + app/components/organisms/OrderAlertDialog.tsx | 13 ++- .../{OrderItemView.tsx => OrderItemEdit.tsx} | 55 +++++----- app/components/pages/CashierV2.tsx | 100 +++++------------- app/firebase/converter.ts | 6 ++ app/firebase/subscription.ts | 3 + app/lib/custom-loader.ts | 3 + app/lib/typeguard.ts | 15 +++ app/lib/webhook.ts | 4 + app/models/order.ts | 36 +++++++ app/routes/cashier-v2.tsx | 1 + app/tailwind.css | 10 ++ 22 files changed, 309 insertions(+), 124 deletions(-) create mode 100644 app/components/functional/useFocusRef.ts create mode 100644 app/components/functional/useInputStatus.ts create mode 100644 app/components/molecules/InputNumber.tsx create mode 100644 app/components/organisms/ChargeView.tsx rename app/components/organisms/{OrderItemView.tsx => OrderItemEdit.tsx} (74%) diff --git a/app/components/functional/useFocusRef.ts b/app/components/functional/useFocusRef.ts new file mode 100644 index 00000000..d3ad2633 --- /dev/null +++ b/app/components/functional/useFocusRef.ts @@ -0,0 +1,20 @@ +import { useEffect, useRef } from "react"; + +/** + * focus が true に変化した際に ref が指す DOM にフォーカスを当てる + * @param focus フォーカスを当てるかどうか + * @returns + */ +const useFocusRef = (focus: boolean) => { + const DOMRef = useRef(null); + + useEffect(() => { + if (focus) { + DOMRef.current?.focus(); + } + }, [focus]); + + return DOMRef; +}; + +export { useFocusRef }; diff --git a/app/components/functional/useInputStatus.ts b/app/components/functional/useInputStatus.ts new file mode 100644 index 00000000..65f41708 --- /dev/null +++ b/app/components/functional/useInputStatus.ts @@ -0,0 +1,36 @@ +import { useCallback, useState } from "react"; + +const InputStatusList = [ + "discount", + "items", + "received", + "description", + "submit", +] as const; + +/** + * CashierV2 のドメイン固有のフック + * 入力ステータスを管理する + */ +const useInputStatus = () => { + const [inputStatus, setInputStatus] = + useState<(typeof InputStatusList)[number]>("discount"); + + const proceedStatus = useCallback(() => { + const idx = InputStatusList.indexOf(inputStatus); + setInputStatus(InputStatusList[(idx + 1) % InputStatusList.length]); + }, [inputStatus]); + + const previousStatus = useCallback(() => { + const idx = InputStatusList.indexOf(inputStatus); + setInputStatus( + InputStatusList[ + (idx - 1 + InputStatusList.length) % InputStatusList.length + ], + ); + }, [inputStatus]); + + return { inputStatus, proceedStatus, previousStatus, setInputStatus }; +}; + +export { useInputStatus }; diff --git a/app/components/functional/useLatestOrderId.ts b/app/components/functional/useLatestOrderId.ts index 3b997159..ec69238d 100644 --- a/app/components/functional/useLatestOrderId.ts +++ b/app/components/functional/useLatestOrderId.ts @@ -2,6 +2,11 @@ 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[] | undefined) => { const latestOrderId = useMemo( () => orders?.reduce((acc, cur) => Math.max(acc, cur.orderId), 0) ?? 0, diff --git a/app/components/functional/useOrderState.ts b/app/components/functional/useOrderState.ts index 079aaa77..b8bae951 100644 --- a/app/components/functional/useOrderState.ts +++ b/app/components/functional/useOrderState.ts @@ -110,6 +110,19 @@ const reducer: OrderReducer = (state, action): OrderEntity => { } }; +/** + * オーダーの状態を管理する + * reducer が受け付ける状態には下記がある: + * - clear + * - applyDiscount + * - removeDiscount + * - addItem + * - mutateItem + * - setReceived + * - setDescription + * - updateOrderId + * @returns オーダーの状態とそれを更新する関数 + */ const useOrderState = () => useReducer(reducer, OrderEntity.createNew({ orderId: -1 })); diff --git a/app/components/functional/useUISession.ts b/app/components/functional/useUISession.ts index 53239bad..bfac4efe 100644 --- a/app/components/functional/useUISession.ts +++ b/app/components/functional/useUISession.ts @@ -5,6 +5,11 @@ type UISession = { key: string; }; +/** + * UI のセッションを管理するためのフック + * renewUISession を呼ぶことでセッションを更新できる + * UISession.key を DOM の key に指定することで、セッションが変更されたときに再描画される + */ const useUISession = (): [UISession, () => void] => { const [date, setDate] = useState(new Date()); diff --git a/app/components/molecules/AttractiveTextBox.tsx b/app/components/molecules/AttractiveTextBox.tsx index a80ad8c6..850a620c 100644 --- a/app/components/molecules/AttractiveTextBox.tsx +++ b/app/components/molecules/AttractiveTextBox.tsx @@ -2,9 +2,9 @@ import { type ChangeEventHandler, useCallback, useEffect, - useRef, useState, } from "react"; +import { useFocusRef } from "../functional/useFocusRef"; import { Input, type InputProps } from "../ui/input"; type props = InputProps & { @@ -12,10 +12,12 @@ type props = InputProps & { focus: boolean; }; -// focus が true のときにフォーカスを当てるテキストボックス +/** + * focus が true のときに自動でフォーカスを当てるテキストボックス + */ const AttractiveTextBox = ({ focus, onTextSet, ...props }: props) => { const [text, setText] = useState(""); - const DOMRef = useRef(null); + const DOMRef = useFocusRef(focus); const onChangeHandler: ChangeEventHandler = useCallback( (event) => setText(event.target.value), @@ -26,12 +28,6 @@ const AttractiveTextBox = ({ focus, onTextSet, ...props }: props) => { onTextSet(text); }, [text, onTextSet]); - useEffect(() => { - if (focus) { - DOMRef.current?.focus(); - } - }, [focus]); - return ( ; + +/** + * 上下キーで数値を増減させない数値専用のテキストボックス + */ +const InputNumber = ({ ...props }: props) => { + 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); + }; + }, []); + + return ; +}; + +export { InputNumber }; diff --git a/app/components/molecules/ThreeDigitsInput.tsx b/app/components/molecules/ThreeDigitsInput.tsx index 3c0be6a5..373ebf47 100644 --- a/app/components/molecules/ThreeDigitsInput.tsx +++ b/app/components/molecules/ThreeDigitsInput.tsx @@ -6,7 +6,9 @@ import { } from "react"; import { InputOTP, InputOTPGroup, InputOTPSlot } from "../ui/input-otp"; -// 3桁の数字を入力するためのコンポーネント +/** + * 3桁の数字を入力するためのコンポーネント + */ const ThreeDigitsInput = forwardRef< ElementRef, Omit< diff --git a/app/components/organisms/ChargeView.tsx b/app/components/organisms/ChargeView.tsx new file mode 100644 index 00000000..74c256fb --- /dev/null +++ b/app/components/organisms/ChargeView.tsx @@ -0,0 +1,26 @@ +import { useMemo } from "react"; +import type { OrderEntity } from "~/models/order"; +import { Input } from "../ui/input"; + +type props = { + order: OrderEntity; +}; + +/** + * おつりの表示をするコンポーネント + * @param props + * @returns + */ +const ChargeView = ({ order }: props) => { + const chargeView: string | number = useMemo(() => { + const charge = order.getCharge(); + if (charge < 0) { + return "不足しています"; + } + return charge; + }, [order]); + + return ; +}; + +export { ChargeView }; diff --git a/app/components/organisms/DiscountInput.tsx b/app/components/organisms/DiscountInput.tsx index 58e68f54..939cda1b 100644 --- a/app/components/organisms/DiscountInput.tsx +++ b/app/components/organisms/DiscountInput.tsx @@ -1,13 +1,12 @@ import { type ComponentPropsWithoutRef, - type ElementRef, - forwardRef, useEffect, useMemo, useState, } from "react"; import type { WithId } from "~/lib/typeguard"; import { OrderEntity } from "~/models/order"; +import { useFocusRef } from "../functional/useFocusRef"; import { ThreeDigitsInput } from "../molecules/ThreeDigitsInput"; const findByOrderId = ( @@ -17,16 +16,25 @@ const findByOrderId = ( return orders?.find((order) => order.orderId === orderId); }; -// 割引券番号を入力するためのコンポーネント -const DiscountInput = forwardRef< - ElementRef, - ComponentPropsWithoutRef & { - orders: WithId[] | undefined; - onDiscountOrderFind: (discountOrder: WithId) => void; - onDiscountOrderRemoved: () => void; - } ->(({ orders, onDiscountOrderFind, onDiscountOrderRemoved, ...props }, ref) => { +type props = ComponentPropsWithoutRef & { + focus: boolean; + orders: WithId[] | undefined; + onDiscountOrderFind: (order: WithId) => void; + onDiscountOrderRemoved: () => void; +}; + +/** + * 割引券番号を入力するためのコンポーネント + */ +const DiscountInput = ({ + focus, + orders, + onDiscountOrderFind, + onDiscountOrderRemoved, + ...props +}: props) => { const [discountOrderId, setDiscountOrderId] = useState(""); + const ref = useFocusRef(focus); const isComplete = useMemo( () => discountOrderId.length === 3, @@ -58,6 +66,7 @@ const DiscountInput = forwardRef< ref={ref} value={discountOrderId} onChange={(value) => setDiscountOrderId(value)} + disabled={!focus} {...props} />

@@ -69,6 +78,6 @@ const DiscountInput = forwardRef<

); -}); +}; export { DiscountInput }; diff --git a/app/components/organisms/ItemAssign.tsx b/app/components/organisms/ItemAssign.tsx index 1d3cfa08..c3058675 100644 --- a/app/components/organisms/ItemAssign.tsx +++ b/app/components/organisms/ItemAssign.tsx @@ -14,6 +14,9 @@ type props = { focus: boolean; }; +/** + * Enterでアサイン入力欄を開けて、アイテムのアサインを変更できるコンポーネント + */ const ItemAssign = ({ item, idx, mutateItem, focus }: props) => { const [editable, setEditable] = useState(false); const [assignee, setAssinee] = useState(null); diff --git a/app/components/organisms/OrderAlertDialog.tsx b/app/components/organisms/OrderAlertDialog.tsx index 2195eaa4..c8bb94d4 100644 --- a/app/components/organisms/OrderAlertDialog.tsx +++ b/app/components/organisms/OrderAlertDialog.tsx @@ -12,15 +12,18 @@ import { AlertDialogTitle, } from "../ui/alert-dialog"; -// 確定前にオーダーの内容を表示するダイアログ +// TODO: 表示内容が整ってないので、きれいにする +/** + * 確定前にオーダーの内容を表示するダイアログ + */ const OrderAlertDialog = forwardRef< null, ComponentPropsWithoutRef & { order: OrderEntity; - chargeView: number | string; onConfirm: () => void; + onCanceled: () => void; } ->(({ order, chargeView, onConfirm, ...props }) => { +>(({ order, onConfirm, onCanceled, ...props }) => { return ( @@ -49,7 +52,7 @@ const OrderAlertDialog = forwardRef< お預かり金額: ¥{order.received} - お釣り: ¥{chargeView} + お釣り: ¥{order.getCharge()} 備考: {order.description} @@ -59,7 +62,7 @@ const OrderAlertDialog = forwardRef< - キャンセル + キャンセル 確定 diff --git a/app/components/organisms/OrderItemView.tsx b/app/components/organisms/OrderItemEdit.tsx similarity index 74% rename from app/components/organisms/OrderItemView.tsx rename to app/components/organisms/OrderItemEdit.tsx index 513f42af..fa664150 100644 --- a/app/components/organisms/OrderItemView.tsx +++ b/app/components/organisms/OrderItemEdit.tsx @@ -18,8 +18,10 @@ type props = { discountOrder: boolean; }; -// オーダーのアイテムや割引情報を表示するコンポーネント -const OrderItemView = ({ +/** + * オーダーのアイテムや割引情報を表示するコンポーネント + */ +const OrderItemEdit = ({ focus, discountOrder, onAddItem, @@ -27,7 +29,7 @@ const OrderItemView = ({ order, items, }: props) => { - const [itemFocus, setItemFocus] = useState(0); + const [itemFocus, setItemFocus] = useState(-1); const proceedItemFocus = useCallback(() => { setItemFocus((prev) => (prev + 1) % order.items.length); @@ -41,45 +43,46 @@ const OrderItemView = ({ useEffect(() => { const handler = (event: KeyboardEvent) => { - if (!focus) { - return; - } - if (event.key === "ArrowUp") { - prevousItemFocus(); - } - if (event.key === "ArrowDown") { - proceedItemFocus(); + switch (event.key) { + case "ArrowUp": + prevousItemFocus(); + break; + case "ArrowDown": + proceedItemFocus(); + break; } }; - window.addEventListener("keydown", handler); + if (focus) { + window.addEventListener("keydown", handler); + } return () => { window.removeEventListener("keydown", handler); }; }, [proceedItemFocus, prevousItemFocus, focus]); useEffect(() => { - const handlers = items?.map((item, idx) => { - const handler = (event: KeyboardEvent) => { - if (!focus) { - return; - } + if (!items) return; + const handler = (event: KeyboardEvent) => { + for (const [idx, item] of items.entries()) { if (event.key === keys[idx]) { onAddItem(item); } - }; - return handler; - }); - for (const handler of handlers ?? []) { + } + }; + if (focus) { window.addEventListener("keydown", handler); } - return () => { - for (const handler of handlers ?? []) { - window.removeEventListener("keydown", handler); - } + window.removeEventListener("keydown", handler); }; }, [items, focus, onAddItem]); + useEffect(() => { + if (!focus) { + setItemFocus(-1); + } + }, [focus]); + return ( <> {focus && ( @@ -108,4 +111,4 @@ const OrderItemView = ({ ); }; -export { OrderItemView }; +export { OrderItemEdit }; diff --git a/app/components/pages/CashierV2.tsx b/app/components/pages/CashierV2.tsx index 2ed42c3c..8265ad7c 100644 --- a/app/components/pages/CashierV2.tsx +++ b/app/components/pages/CashierV2.tsx @@ -1,36 +1,34 @@ -import { useCallback, useEffect, useMemo, useRef, useState } from "react"; -import { Input } from "~/components/ui/input"; +import { useCallback, useEffect, useMemo } from "react"; import type { WithId } from "~/lib/typeguard"; import { type ItemEntity, type2label } from "~/models/item"; import type { OrderEntity } from "~/models/order"; +import { useInputStatus } from "../functional/useInputStatus"; import { useLatestOrderId } from "../functional/useLatestOrderId"; import { useOrderState } from "../functional/useOrderState"; import { useUISession } from "../functional/useUISession"; import { AttractiveTextBox } from "../molecules/AttractiveTextBox"; +import { InputNumber } from "../molecules/InputNumber"; +import { ChargeView } from "../organisms/ChargeView"; import { DiscountInput } from "../organisms/DiscountInput"; import { OrderAlertDialog } from "../organisms/OrderAlertDialog"; -import { OrderItemView } from "../organisms/OrderItemView"; +import { OrderItemEdit } from "../organisms/OrderItemEdit"; import { Button } from "../ui/button"; -const InputStatus = [ - "discount", - "items", - "received", - "description", - "submit", -] as const; - type props = { items: WithId[] | undefined; orders: WithId[] | undefined; submitPayload: (order: OrderEntity) => void; }; +/** + * キャッシャー画面のコンポーネント + * + * データの入出力は親コンポーネントに任せる + */ const CashierV2 = ({ items, orders, submitPayload }: props) => { const [newOrder, newOrderDispatch] = useOrderState(); - const [inputStatus, setInputStatus] = - useState<(typeof InputStatus)[number]>("discount"); - const [dialogOpen, setDialogOpen] = useState(false); + const { inputStatus, proceedStatus, previousStatus, setInputStatus } = + useInputStatus(); const [UISession, renewUISession] = useUISession(); const { nextOrderId } = useLatestOrderId(orders); @@ -38,69 +36,32 @@ const CashierV2 = ({ items, orders, submitPayload }: props) => { newOrderDispatch({ type: "updateOrderId", orderId: nextOrderId }); }, [nextOrderId, newOrderDispatch]); - const charge = newOrder.received - newOrder.billingAmount; - const chargeView: string | number = charge < 0 ? "不足しています" : charge; - - const discountInputDOM = 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 resetAll = useCallback(() => { + newOrderDispatch({ type: "clear" }); + setInputStatus("discount"); + renewUISession(); + }, [newOrderDispatch, setInputStatus, renewUISession]); const submitOrder = useCallback(() => { - if (charge < 0) { + if (newOrder.getCharge() < 0) { return; } if (newOrder.items.length === 0) { return; } - newOrderDispatch({ - type: "clear", - effectFn: renewUISession, - }); submitPayload(newOrder); - }, [charge, newOrder, submitPayload, newOrderDispatch, renewUISession]); - - const moveFocus = useCallback(() => { - switch (inputStatus) { - case "discount": - setDialogOpen(false); - discountInputDOM.current?.focus(); - break; - case "items": - break; - case "received": - break; - case "description": - setDialogOpen(false); - break; - case "submit": - setDialogOpen(true); - break; - } - }, [inputStatus]); - - useEffect(moveFocus); + resetAll(); + }, [newOrder, submitPayload, resetAll]); const keyEventHandlers = useMemo(() => { return { ArrowRight: proceedStatus, - ArrowLeft: prevousStatus, + ArrowLeft: previousStatus, Escape: () => { - setInputStatus("discount"); - setDialogOpen(false); - newOrderDispatch({ type: "clear" }); + resetAll(); }, }; - }, [proceedStatus, prevousStatus, newOrderDispatch]); + }, [proceedStatus, previousStatus, resetAll]); useEffect(() => { const handler = (event: KeyboardEvent) => { @@ -144,8 +105,7 @@ const CashierV2 = ({ items, orders, submitPayload }: props) => {
@@ -157,9 +117,8 @@ const CashierV2 = ({ items, orders, submitPayload }: props) => { [newOrderDispatch], )} /> - newOrderDispatch({ type: "setReceived", received: text }), @@ -167,7 +126,7 @@ const CashierV2 = ({ items, orders, submitPayload }: props) => { )} focus={inputStatus === "received"} /> - + {

入力ステータス: {inputStatus}

- {
); diff --git a/app/firebase/converter.ts b/app/firebase/converter.ts index 5d917af4..e8198f2c 100644 --- a/app/firebase/converter.ts +++ b/app/firebase/converter.ts @@ -59,6 +59,9 @@ const parseDateProperty = (data: DocumentData): DocumentData => { return recursivelyParsedData; }; +/** + * Firestore のデータを ItemEntity に変換する + */ export const itemConverter: FirestoreDataConverter> = { toFirestore: converter(itemSchema).toFirestore, fromFirestore: ( @@ -73,6 +76,9 @@ export const itemConverter: FirestoreDataConverter> = { }, }; +/** + * Firestore のデータを OrderEntity に変換する + */ export const orderConverter: FirestoreDataConverter> = { toFirestore: converter(orderSchema).toFirestore, fromFirestore: ( diff --git a/app/firebase/subscription.ts b/app/firebase/subscription.ts index 3605dfe6..155ec67c 100644 --- a/app/firebase/subscription.ts +++ b/app/firebase/subscription.ts @@ -8,6 +8,9 @@ import { import type { SWRSubscription } from "swr/subscription"; import { prodDB } from "./firestore"; +/** + * Firestore のコレクションを監視する SWRSubscription を生成する + */ export const collectionSub = ( { converter }: { converter: FirestoreDataConverter }, ...queryConstraints: QueryConstraint[] diff --git a/app/lib/custom-loader.ts b/app/lib/custom-loader.ts index 268c8f50..1b76592a 100644 --- a/app/lib/custom-loader.ts +++ b/app/lib/custom-loader.ts @@ -1,4 +1,7 @@ import { type ClientLoaderFunction, useLoaderData } from "@remix-run/react"; +/** + * clientLoader のデータを JSON にシリアライズせずに直接取得する関数 + */ export const useClientLoaderData = () => useLoaderData() as Awaited>; diff --git a/app/lib/typeguard.ts b/app/lib/typeguard.ts index 28a0bac4..550f9944 100644 --- a/app/lib/typeguard.ts +++ b/app/lib/typeguard.ts @@ -1,6 +1,21 @@ +/** + * オブジェクトに id プロパティを持っていることを保証するユーティリティ型 + */ export type WithId = T & Record<"id", NonNullable>; +/** + * オブジェクトが id プロパティを持っているかどうかを判定する + * TypeGuard として使用する + * + * @param obj 判定するオブジェクト + * @returns obj が id プロパティを持っている場合は true + * @example + * const obj = { id: 1, name: "name" }; + * if (hasId(obj)) { + * console.log(obj.id); + * } + */ export const hasId = (obj: T): obj is WithId => { return obj.id !== undefined && obj.id !== null; }; diff --git a/app/lib/webhook.ts b/app/lib/webhook.ts index 3ae01d55..db7475b4 100644 --- a/app/lib/webhook.ts +++ b/app/lib/webhook.ts @@ -1,3 +1,7 @@ +/** + * Slackにメッセージを送信する + * @param message 送信するメッセージ + */ export const sendSlackMessage = async (message: string) => { const webhookUrl = import.meta.env.VITE_WEBHOOK_URL; diff --git a/app/models/order.ts b/app/models/order.ts index 9070f9ed..ee33f35f 100644 --- a/app/models/order.ts +++ b/app/models/order.ts @@ -169,37 +169,69 @@ export class OrderEntity implements Order { // methods // -------------------------------------------------- + /** + * コーヒーの数を取得する + * @returns 割引の対象となるコーヒーの数 + */ getCoffeeCount() { // milk 以外のアイテムの数を返す // TODO(toririm): このメソッドは items が変更された時だけでいい return this.items.filter((item) => item.type !== "milk").length; } + /** + * オーダーを準備完了状態に変更する + */ beReady() { // orderReady は false -> true にしか変更できないようにする this._orderReady = true; } + /** + * オーダーを提供済み状態に変更する + */ beServed() { // servedAt は null -> Date にしか変更できないようにする this._servedAt = new Date(); } + /** + * 割引を適用する + * @param previousOrder 割引の参照となる前回のオーダー + */ applyDiscount(previousOrder: OrderEntity) { this._discountOrderId = previousOrder.orderId; this._discountOrderCups = previousOrder.getCoffeeCount(); } + /** + * 割引を解除する + */ removeDiscount() { this._discountOrderId = null; this._discountOrderCups = 0; } + /** + * オーダーを作成した時刻を更新する + */ nowCreated() { // createdAt を更新 this._createdAt = new Date(); } + /** + * お釣りを計算する + * @returns お釣り + */ + getCharge() { + return this.received - this.billingAmount; + } + + /** + * メソッドを持たない Order オブジェクトに変換する + * @returns Order オブジェクト + */ toOrder(): Order { return { id: this.id, @@ -219,6 +251,10 @@ export class OrderEntity implements Order { }; } + /** + * オーダーを複製する + * ただし、items は参照を共有することに注意 + */ clone(): WithId; clone(): OrderEntity; clone(): WithId | OrderEntity { diff --git a/app/routes/cashier-v2.tsx b/app/routes/cashier-v2.tsx index 6f4d4207..a3da62e8 100644 --- a/app/routes/cashier-v2.tsx +++ b/app/routes/cashier-v2.tsx @@ -10,6 +10,7 @@ import { stringToJSONSchema } from "~/lib/custom-zod"; import { OrderEntity, orderSchema } from "~/models/order"; import { orderRepository } from "~/repositories/order"; +// コンポーネントではデータの取得と更新のみを行う export default function Cashier() { const { data: items } = useSWRSubscription( "items", diff --git a/app/tailwind.css b/app/tailwind.css index f450d1e2..b192f066 100644 --- a/app/tailwind.css +++ b/app/tailwind.css @@ -67,3 +67,13 @@ @apply bg-background text-foreground; } } + +/* input要素でtype="number"のものについては、数値の上下を変更するボタンを非表示にする */ +@layer base { + input[type="number"]::-webkit-outer-spin-button, + input[type="number"]::-webkit-inner-spin-button, + input[type="number"] { + -webkit-appearance: none; + -moz-appearance: textfield !important; + } +} From d719ab118c785f35afdaf0e624536b0ffebef5f1 Mon Sep 17 00:00:00 2001 From: Ryunosuke Tokinaga <59079411+toririm@users.noreply.github.com> Date: Thu, 3 Oct 2024 19:18:06 +0900 Subject: [PATCH 21/28] chore(docs): fix JSDoc (#189) --- app/components/functional/useInputStatus.ts | 1 + app/components/functional/useOrderState.ts | 5 ++++- app/components/functional/useUISession.ts | 2 ++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/app/components/functional/useInputStatus.ts b/app/components/functional/useInputStatus.ts index 65f41708..3d59c5d7 100644 --- a/app/components/functional/useInputStatus.ts +++ b/app/components/functional/useInputStatus.ts @@ -10,6 +10,7 @@ const InputStatusList = [ /** * CashierV2 のドメイン固有のフック + * * 入力ステータスを管理する */ const useInputStatus = () => { diff --git a/app/components/functional/useOrderState.ts b/app/components/functional/useOrderState.ts index b8bae951..3cedc9d9 100644 --- a/app/components/functional/useOrderState.ts +++ b/app/components/functional/useOrderState.ts @@ -23,7 +23,9 @@ type ApplyDiscount = Action< type RemoveDiscount = Action<"removeDiscount">; type SetReceived = Action<"setReceived", { received: string }>; type SetDescription = Action<"setDescription", { description: string }>; - +/** + * オーダーの状態を更新するためのアクション型 + */ export type OrderAction = | Clear | UpdateOrderId @@ -112,6 +114,7 @@ const reducer: OrderReducer = (state, action): OrderEntity => { /** * オーダーの状態を管理する + * * reducer が受け付ける状態には下記がある: * - clear * - applyDiscount diff --git a/app/components/functional/useUISession.ts b/app/components/functional/useUISession.ts index bfac4efe..39605fc6 100644 --- a/app/components/functional/useUISession.ts +++ b/app/components/functional/useUISession.ts @@ -7,7 +7,9 @@ type UISession = { /** * UI のセッションを管理するためのフック + * * renewUISession を呼ぶことでセッションを更新できる + * * UISession.key を DOM の key に指定することで、セッションが変更されたときに再描画される */ const useUISession = (): [UISession, () => void] => { From 6afe3447c045535fa143dde49c2b2f99203b942d Mon Sep 17 00:00:00 2001 From: Ryunosuke Tokinaga <59079411+toririm@users.noreply.github.com> Date: Thu, 3 Oct 2024 20:15:49 +0900 Subject: [PATCH 22/28] =?UTF-8?q?bugfix:=20=E6=8C=87=E5=90=8D=E5=85=A5?= =?UTF-8?q?=E5=8A=9B=E4=B8=AD=E3=81=AF=E3=82=AD=E3=83=BC=E3=81=A7=E3=81=AE?= =?UTF-8?q?=E3=82=A2=E3=82=A4=E3=83=86=E3=83=A0=E8=BF=BD=E5=8A=A0=E3=82=92?= =?UTF-8?q?=E7=84=A1=E5=8A=B9=E5=8C=96=E3=81=99=E3=82=8B=20(#190)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fixes #188 --- app/components/organisms/ItemAssign.tsx | 51 ++++-------------- app/components/organisms/OrderItemEdit.tsx | 63 +++++++++++++++++----- 2 files changed, 61 insertions(+), 53 deletions(-) diff --git a/app/components/organisms/ItemAssign.tsx b/app/components/organisms/ItemAssign.tsx index c3058675..ed6ab10d 100644 --- a/app/components/organisms/ItemAssign.tsx +++ b/app/components/organisms/ItemAssign.tsx @@ -1,7 +1,8 @@ -import { useCallback, useEffect, useRef, useState } from "react"; +import { useCallback, useEffect, useState } from "react"; import type { WithId } from "~/lib/typeguard"; import { cn } from "~/lib/utils"; import { type ItemEntity, type2label } from "~/models/item"; +import { useFocusRef } from "../functional/useFocusRef"; import { Input } from "../ui/input"; type props = { @@ -11,64 +12,32 @@ type props = { idx: number, action: (prev: WithId) => WithId, ) => void; + editable: boolean; focus: boolean; }; /** * Enterでアサイン入力欄を開けて、アイテムのアサインを変更できるコンポーネント */ -const ItemAssign = ({ item, idx, mutateItem, focus }: props) => { - const [editable, setEditable] = useState(false); +const ItemAssign = ({ item, idx, mutateItem, editable, focus }: props) => { const [assignee, setAssinee] = useState(null); - const assignInputRef = useRef(null); + const assignInputRef = useFocusRef(editable); - const closeAssignInput = useCallback(() => { + const saveAssignInput = useCallback(() => { mutateItem(idx, (prev) => { const copy = structuredClone(prev); copy.assignee = assignee; return copy; }); - setEditable(false); }, [assignee, idx, mutateItem]); - // edit の状態に応じて assign 入力欄を開くか閉じる - const switchEditable = useCallback(() => { - if (editable) { - closeAssignInput(); - } else { - setEditable(true); - } - }, [editable, closeAssignInput]); - - // focus が変化したときに assign 入力欄を閉じる - useEffect(() => { - if (!focus && editable) { - closeAssignInput(); - } - }, [focus, editable, closeAssignInput]); - - // Enter が押されたときに assign 入力欄を開く - useEffect(() => { - const handler = (event: KeyboardEvent) => { - if (event.key === "Enter") { - switchEditable(); - } - }; - if (focus) { - window.addEventListener("keydown", handler); - } - return () => { - window.removeEventListener("keydown", handler); - }; - }, [focus, switchEditable]); - - // edit が true に変化したとき assign 入力欄にフォーカスする + // アサイン入力欄を閉じるときに保存 useEffect(() => { - if (editable) { - assignInputRef.current?.focus(); + if (!editable) { + saveAssignInput(); } - }, [editable]); + }, [editable, saveAssignInput]); return (
diff --git a/app/components/organisms/OrderItemEdit.tsx b/app/components/organisms/OrderItemEdit.tsx index fa664150..b76eaeff 100644 --- a/app/components/organisms/OrderItemEdit.tsx +++ b/app/components/organisms/OrderItemEdit.tsx @@ -30,25 +30,45 @@ const OrderItemEdit = ({ items, }: props) => { const [itemFocus, setItemFocus] = useState(-1); + const [editable, setEditable] = useState(false); - const proceedItemFocus = useCallback(() => { - setItemFocus((prev) => (prev + 1) % order.items.length); - }, [order.items]); + /** + * step だけ itemFocus を移動する + * + * - step が正の場合、次のアイテムに移動 + * - step が負の場合、前のアイテムに移動 + */ + const moveItemFocus = useCallback( + (step: number) => { + setItemFocus( + (prev) => (prev + step + order.items.length) % order.items.length, + ); + }, + [order.items], + ); - const prevousItemFocus = useCallback(() => { - setItemFocus( - (prev) => (prev - 1 + order.items.length) % order.items.length, - ); - }, [order.items]); + /** + * assign 入力欄を開く/閉じる + */ + const switchEditable = useCallback(() => { + if (editable) { + setEditable(false); + } else { + setEditable(true); + } + }, [editable]); + // ↑・↓ が押されたときに itemFocus を移動 useEffect(() => { const handler = (event: KeyboardEvent) => { switch (event.key) { case "ArrowUp": - prevousItemFocus(); + moveItemFocus(-1); + setEditable(false); break; case "ArrowDown": - proceedItemFocus(); + moveItemFocus(1); + setEditable(false); break; } }; @@ -58,12 +78,29 @@ const OrderItemEdit = ({ return () => { window.removeEventListener("keydown", handler); }; - }, [proceedItemFocus, prevousItemFocus, focus]); + }, [focus, moveItemFocus]); + + // Enter が押されたときに assign 入力欄を開く + useEffect(() => { + const handler = (event: KeyboardEvent) => { + if (event.key === "Enter") { + switchEditable(); + } + }; + if (focus) { + window.addEventListener("keydown", handler); + } + return () => { + window.removeEventListener("keydown", handler); + }; + }, [focus, switchEditable]); + // キー操作でアイテムを追加 useEffect(() => { if (!items) return; const handler = (event: KeyboardEvent) => { for (const [idx, item] of items.entries()) { + if (editable) return; if (event.key === keys[idx]) { onAddItem(item); } @@ -75,8 +112,9 @@ const OrderItemEdit = ({ return () => { window.removeEventListener("keydown", handler); }; - }, [items, focus, onAddItem]); + }, [items, focus, editable, onAddItem]); + // focus が外れたときに itemFocus をリセット useEffect(() => { if (!focus) { setItemFocus(-1); @@ -98,6 +136,7 @@ const OrderItemEdit = ({ item={item} idx={idx} mutateItem={mutateItem} + editable={editable && idx === itemFocus} focus={idx === itemFocus} /> ))} From b4f8297fec9a473ad3133d9c664b27b542e1bd6b Mon Sep 17 00:00:00 2001 From: Ryunosuke Tokinaga <59079411+toririm@users.noreply.github.com> Date: Thu, 3 Oct 2024 21:25:41 +0900 Subject: [PATCH 23/28] =?UTF-8?q?enhancement:=20inputStatus=20=E3=82=92?= =?UTF-8?q?=E7=AB=AF=E3=81=A7=E6=AD=A2=E3=82=81=E3=82=8B=20(#192)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #191 ついでに内部実装もマシなものにリファクタリング --- app/components/functional/useInputStatus.ts | 33 +++++++++++---------- app/components/organisms/ItemAssign.tsx | 14 ++++----- app/components/organisms/OrderItemEdit.tsx | 4 +-- app/components/pages/CashierV2.tsx | 6 ++-- 4 files changed, 30 insertions(+), 27 deletions(-) diff --git a/app/components/functional/useInputStatus.ts b/app/components/functional/useInputStatus.ts index 3d59c5d7..b77c405b 100644 --- a/app/components/functional/useInputStatus.ts +++ b/app/components/functional/useInputStatus.ts @@ -1,4 +1,4 @@ -import { useCallback, useState } from "react"; +import { useCallback, useMemo, useState } from "react"; const InputStatusList = [ "discount", @@ -8,30 +8,33 @@ const InputStatusList = [ "submit", ] as const; +const inputLen = InputStatusList.length; + +const narrowInLen = (idx: number) => Math.min(Math.max(idx, 0), inputLen - 1); + /** * CashierV2 のドメイン固有のフック * * 入力ステータスを管理する */ const useInputStatus = () => { - const [inputStatus, setInputStatus] = - useState<(typeof InputStatusList)[number]>("discount"); + const [idx, setIdx] = useState(0); const proceedStatus = useCallback(() => { - const idx = InputStatusList.indexOf(inputStatus); - setInputStatus(InputStatusList[(idx + 1) % InputStatusList.length]); - }, [inputStatus]); + setIdx((prev) => narrowInLen(prev + 1)); + }, []); const previousStatus = useCallback(() => { - const idx = InputStatusList.indexOf(inputStatus); - setInputStatus( - InputStatusList[ - (idx - 1 + InputStatusList.length) % InputStatusList.length - ], - ); - }, [inputStatus]); - - return { inputStatus, proceedStatus, previousStatus, setInputStatus }; + setIdx((prev) => narrowInLen(prev - 1)); + }, []); + + const inputStatus = useMemo(() => InputStatusList[idx], [idx]); + + const resetStatus = useCallback(() => { + setIdx(0); + }, []); + + return { inputStatus, proceedStatus, previousStatus, resetStatus }; }; export { useInputStatus }; diff --git a/app/components/organisms/ItemAssign.tsx b/app/components/organisms/ItemAssign.tsx index ed6ab10d..0605dcce 100644 --- a/app/components/organisms/ItemAssign.tsx +++ b/app/components/organisms/ItemAssign.tsx @@ -12,17 +12,17 @@ type props = { idx: number, action: (prev: WithId) => WithId, ) => void; - editable: boolean; + highlight: boolean; focus: boolean; }; /** * Enterでアサイン入力欄を開けて、アイテムのアサインを変更できるコンポーネント */ -const ItemAssign = ({ item, idx, mutateItem, editable, focus }: props) => { +const ItemAssign = ({ item, idx, mutateItem, focus, highlight }: props) => { const [assignee, setAssinee] = useState(null); - const assignInputRef = useFocusRef(editable); + const assignInputRef = useFocusRef(focus); const saveAssignInput = useCallback(() => { mutateItem(idx, (prev) => { @@ -34,19 +34,19 @@ const ItemAssign = ({ item, idx, mutateItem, editable, focus }: props) => { // アサイン入力欄を閉じるときに保存 useEffect(() => { - if (!editable) { + if (!focus) { saveAssignInput(); } - }, [editable, saveAssignInput]); + }, [focus, saveAssignInput]); return ( -
+

{idx + 1}

{item.name}

{item.price}

{type2label[item.type]}

- {editable ? ( + {focus ? ( ))} {discountOrder && ( diff --git a/app/components/pages/CashierV2.tsx b/app/components/pages/CashierV2.tsx index 8265ad7c..a3bb7734 100644 --- a/app/components/pages/CashierV2.tsx +++ b/app/components/pages/CashierV2.tsx @@ -27,7 +27,7 @@ type props = { */ const CashierV2 = ({ items, orders, submitPayload }: props) => { const [newOrder, newOrderDispatch] = useOrderState(); - const { inputStatus, proceedStatus, previousStatus, setInputStatus } = + const { inputStatus, proceedStatus, previousStatus, resetStatus } = useInputStatus(); const [UISession, renewUISession] = useUISession(); const { nextOrderId } = useLatestOrderId(orders); @@ -38,9 +38,9 @@ const CashierV2 = ({ items, orders, submitPayload }: props) => { const resetAll = useCallback(() => { newOrderDispatch({ type: "clear" }); - setInputStatus("discount"); + resetStatus(); renewUISession(); - }, [newOrderDispatch, setInputStatus, renewUISession]); + }, [newOrderDispatch, resetStatus, renewUISession]); const submitOrder = useCallback(() => { if (newOrder.getCharge() < 0) { From 4186f664a05c4cf76e05ec66d481f7fb42836d5a Mon Sep 17 00:00:00 2001 From: shoga <132179328+Yayoi2078@users.noreply.github.com> Date: Thu, 3 Oct 2024 22:36:36 +0900 Subject: [PATCH 24/28] =?UTF-8?q?=E6=B3=A8=E6=96=87=E7=95=AA=E5=8F=B7?= =?UTF-8?q?=E9=A0=86=E3=81=AB=E3=82=BD=E3=83=BC=E3=83=88=20(#193)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit close #103 --- app/routes/_header.serve.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/routes/_header.serve.tsx b/app/routes/_header.serve.tsx index d0f46177..248e60ae 100644 --- a/app/routes/_header.serve.tsx +++ b/app/routes/_header.serve.tsx @@ -1,4 +1,5 @@ import type { MetaFunction } from "@remix-run/react"; +import { orderBy } from "firebase/firestore"; import useSWRSubscription from "swr/subscription"; import { Button } from "~/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card"; @@ -19,7 +20,7 @@ export const clientLoader = async () => { export default function Serve() { const { data: orders } = useSWRSubscription( "orders", - collectionSub({ converter: orderConverter }), + collectionSub({ converter: orderConverter }, orderBy("orderId", "desc")), ); return ( From 25ed9700e8e15f971dfe1ed4b6aebaa3dbe602bf Mon Sep 17 00:00:00 2001 From: Ryunosuke Tokinaga <59079411+toririm@users.noreply.github.com> Date: Thu, 3 Oct 2024 22:46:34 +0900 Subject: [PATCH 25/28] =?UTF-8?q?backspace=20=E3=81=A7=20focus=20=E3=81=AE?= =?UTF-8?q?=E3=82=A2=E3=82=A4=E3=83=86=E3=83=A0=E3=82=92=E5=89=8A=E9=99=A4?= =?UTF-8?q?=20(#194)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit closes #186 --- app/components/functional/useOrderState.ts | 10 ++++++++++ app/components/organisms/OrderItemEdit.tsx | 13 ++++++++++++- app/components/pages/CashierV2.tsx | 4 ++++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/app/components/functional/useOrderState.ts b/app/components/functional/useOrderState.ts index 3cedc9d9..0674626b 100644 --- a/app/components/functional/useOrderState.ts +++ b/app/components/functional/useOrderState.ts @@ -12,6 +12,7 @@ type Action< type Clear = Action<"clear", { effectFn?: () => void }>; type UpdateOrderId = Action<"updateOrderId", { orderId: number }>; type AddItem = Action<"addItem", { item: WithId }>; +type RemoveItem = Action<"removeItem", { idx: number }>; type MutateItem = Action< "mutateItem", { idx: number; action: (prev: WithId) => WithId } @@ -30,6 +31,7 @@ export type OrderAction = | Clear | UpdateOrderId | AddItem + | RemoveItem | MutateItem | ApplyDiscount | RemoveDiscount @@ -61,6 +63,12 @@ const addItem: OrderReducer = (state, action) => { return updated; }; +const removeItem: OrderReducer = (state, action) => { + const updated = state.clone(); + updated.items = updated.items.filter((_, idx) => idx !== action.idx); + return updated; +}; + const mutateItem: OrderReducer = (state, action) => { const updated = state.clone(); updated.items[action.idx] = action.action(updated.items[action.idx]); @@ -101,6 +109,8 @@ const reducer: OrderReducer = (state, action): OrderEntity => { return removeDiscount(state, action); case "addItem": return addItem(state, action); + case "removeItem": + return removeItem(state, action); case "mutateItem": return mutateItem(state, action); case "setReceived": diff --git a/app/components/organisms/OrderItemEdit.tsx b/app/components/organisms/OrderItemEdit.tsx index 1a0e88ee..3124e799 100644 --- a/app/components/organisms/OrderItemEdit.tsx +++ b/app/components/organisms/OrderItemEdit.tsx @@ -11,6 +11,7 @@ type props = { items: WithId[] | undefined; focus: boolean; onAddItem: (item: WithId) => void; + onRemoveItem: (idx: number) => void; mutateItem: ( idx: number, action: (prev: WithId) => WithId, @@ -25,6 +26,7 @@ const OrderItemEdit = ({ focus, discountOrder, onAddItem, + onRemoveItem, mutateItem, order, items, @@ -58,6 +60,11 @@ const OrderItemEdit = ({ } }, [editable]); + const removeItem = useCallback(() => { + if (itemFocus === -1) return; + onRemoveItem(itemFocus); + }, [itemFocus, onRemoveItem]); + // ↑・↓ が押されたときに itemFocus を移動 useEffect(() => { const handler = (event: KeyboardEvent) => { @@ -96,6 +103,7 @@ const OrderItemEdit = ({ }, [focus, switchEditable]); // キー操作でアイテムを追加 + // Backspace でアイテムを削除 useEffect(() => { if (!items) return; const handler = (event: KeyboardEvent) => { @@ -105,6 +113,9 @@ const OrderItemEdit = ({ onAddItem(item); } } + if (event.key === "Backspace") { + removeItem(); + } }; if (focus) { window.addEventListener("keydown", handler); @@ -112,7 +123,7 @@ const OrderItemEdit = ({ return () => { window.removeEventListener("keydown", handler); }; - }, [items, focus, editable, onAddItem]); + }, [items, focus, editable, onAddItem, removeItem]); // focus が外れたときに itemFocus をリセット useEffect(() => { diff --git a/app/components/pages/CashierV2.tsx b/app/components/pages/CashierV2.tsx index a3bb7734..5d09e6f6 100644 --- a/app/components/pages/CashierV2.tsx +++ b/app/components/pages/CashierV2.tsx @@ -146,6 +146,10 @@ const CashierV2 = ({ items, orders, submitPayload }: props) => { (item) => newOrderDispatch({ type: "addItem", item }), [newOrderDispatch], )} + onRemoveItem={useCallback( + (idx) => newOrderDispatch({ type: "removeItem", idx }), + [newOrderDispatch], + )} mutateItem={useCallback( (idx, action) => newOrderDispatch({ type: "mutateItem", idx, action }), From 37bcae93c3cc86aed51e45fa6757d7b20292a29c Mon Sep 17 00:00:00 2001 From: Astalum <131961897+Astalum@users.noreply.github.com> Date: Fri, 4 Oct 2024 13:31:14 +0900 Subject: [PATCH 26/28] =?UTF-8?q?/casher=20=E6=8C=87=E5=90=8D=E6=AC=84?= =?UTF-8?q?=E3=82=92=E4=BD=9C=E3=82=8B=20(#187)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix #177 --- app/components/ui/select.tsx | 158 ++++++++++++++++++++++++++++ app/routes/_header.casher.tsx | 193 ++++++++++++++++++++-------------- bun.lockb | Bin 484440 -> 492192 bytes package.json | 1 + 4 files changed, 271 insertions(+), 81 deletions(-) create mode 100644 app/components/ui/select.tsx diff --git a/app/components/ui/select.tsx b/app/components/ui/select.tsx new file mode 100644 index 00000000..aaa155e8 --- /dev/null +++ b/app/components/ui/select.tsx @@ -0,0 +1,158 @@ +import * as SelectPrimitive from "@radix-ui/react-select"; +import { Check, ChevronDown, ChevronUp } from "lucide-react"; +import * as React from "react"; + +import { cn } from "~/lib/utils"; + +const Select = SelectPrimitive.Root; + +const SelectGroup = SelectPrimitive.Group; + +const SelectValue = SelectPrimitive.Value; + +const SelectTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + span]:line-clamp-1", + className, + )} + {...props} + > + {children} + + + + +)); +SelectTrigger.displayName = SelectPrimitive.Trigger.displayName; + +const SelectScrollUpButton = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + +)); +SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName; + +const SelectScrollDownButton = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + +)); +SelectScrollDownButton.displayName = + SelectPrimitive.ScrollDownButton.displayName; + +const SelectContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, position = "popper", ...props }, ref) => ( + + + + + {children} + + + + +)); +SelectContent.displayName = SelectPrimitive.Content.displayName; + +const SelectLabel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +SelectLabel.displayName = SelectPrimitive.Label.displayName; + +const SelectItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + + + + + + {children} + +)); +SelectItem.displayName = SelectPrimitive.Item.displayName; + +const SelectSeparator = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +SelectSeparator.displayName = SelectPrimitive.Separator.displayName; + +export { + Select, + SelectGroup, + SelectValue, + SelectTrigger, + SelectContent, + SelectLabel, + SelectItem, + SelectSeparator, + SelectScrollUpButton, + SelectScrollDownButton, +}; diff --git a/app/routes/_header.casher.tsx b/app/routes/_header.casher.tsx index 702f1f2e..e76b75da 100644 --- a/app/routes/_header.casher.tsx +++ b/app/routes/_header.casher.tsx @@ -17,6 +17,15 @@ import { } from "~/components/ui/alert-dialog"; import { Button } from "~/components/ui/button"; import { Input } from "~/components/ui/input"; +import { + Select, + SelectContent, + SelectGroup, + SelectItem, + SelectLabel, + SelectTrigger, + SelectValue, +} from "~/components/ui/select"; import { Table, TableBody, @@ -54,6 +63,7 @@ export default function Casher() { const charge = recieved - order.total; const [description, setDescription] = useState(""); order.description = description; + const [assignee, setAssignee] = useState(""); const submitOrder = () => { console.log(charge); @@ -88,26 +98,47 @@ export default function Casher() { ))}
-
-
- - - - - No. {nextOrderId} - - - - {queue?.map((item, index) => ( - - -
{item.name}
+
+
+ + + + No. {nextOrderId} + + + + {queue?.map((item, index) => ( + + +
+ {item.name} +
+
+ +
+
-
-
- ))} -
-
-
    -
  • - setDescription(e.target.value)} - /> -

    合計金額:{order.total} 円

    -
  • -
  • - - - - - - - - - 金額・備考欄を確認してください - - -

    備考欄:{order.description}

    -

    - 受領額: - { - const value = Number.parseInt(e.target.value); - setReceived(Number.isNaN(value) ? 0 : value); // NaN のチェック - }} - /> -

    -

    合計: {order.total} 円

    -

    - お釣り:{" "} - {Number.isNaN(charge) || charge < 0 ? 0 : charge} 円 -

    -
    -
    - - 戻る - - 送信 - - -
    -
    - -
  • -
-
+
+ + + ))} + + +
    +
  • + setDescription(e.target.value)} + /> +

    合計金額:{order.total} 円

    +
  • +
  • +
    + + + + + + + + 金額・備考欄を確認してください + + +

    備考欄:{order.description}

    +

    合計: {order.total} 円

    +

    + 受領額: + { + const value = Number.parseInt(e.target.value); + setReceived(Number.isNaN(value) ? 0 : value); // NaN のチェック + }} + /> +

    +

    + お釣り:{" "} + {Number.isNaN(charge) || charge < 0 ? 0 : charge} 円 +

    +
    +
    + + 戻る + + 送信 + + +
    +
    +
    +
  • +
diff --git a/bun.lockb b/bun.lockb index f0598df13daf6bd4194f4c54e9210c634264f646..2ec45b685aea55f16fd796ee7f1cd99ed46ad026 100755 GIT binary patch delta 94681 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 p|8N|5Vt+6ff!EzQ&br2rJG Date: Fri, 4 Oct 2024 20:18:30 +0900 Subject: [PATCH 27/28] =?UTF-8?q?[=E3=82=B9=E3=82=AD=E3=83=BC=E3=83=9E?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3]=20itemType=20=E3=82=92=E4=BF=AE=E6=AD=A3=20?= =?UTF-8?q?(#198)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 四役と話して、これがよいということになった --- app/models/item.ts | 15 ++++++++++++--- app/models/order.test.ts | 2 +- app/repositories/item.test.ts | 6 +++++- app/routes/items/route.tsx | 9 ++++++++- 4 files changed, 26 insertions(+), 6 deletions(-) diff --git a/app/models/item.ts b/app/models/item.ts index 211cc3bd..3e3d8e45 100644 --- a/app/models/item.ts +++ b/app/models/item.ts @@ -1,7 +1,14 @@ import { z } from "zod"; import type { WithId } from "~/lib/typeguard"; -export const itemtypes = ["hot", "ice", "ore", "milk"] as const; +export const itemtypes = [ + "hot", + "ice", + "hotOre", + "iceOre", + "milk", + "others", +] as const; export const itemSchema = z.object({ id: z.string().optional(), // Firestore のドキュメント ID @@ -21,8 +28,10 @@ export type ItemType = Item["type"]; export const type2label = { hot: "ホット", ice: "アイス", - ore: "オレ", - milk: "ミルク", + hotOre: "ホットオレ", + iceOre: "アイスオレ", + milk: "アイスミルク", + others: "その他", } as const satisfies Record; export class ItemEntity implements Item { diff --git a/app/models/order.test.ts b/app/models/order.test.ts index a3c39d06..cb6f9234 100644 --- a/app/models/order.test.ts +++ b/app/models/order.test.ts @@ -51,7 +51,7 @@ describe("[unit] order entity", () => { id: "3", name: "item3", price: 100, - type: "ore", + type: "hotOre", assignee: null, }); expect(order.total).toBe(541); diff --git a/app/repositories/item.test.ts b/app/repositories/item.test.ts index 17cd9592..566efd24 100644 --- a/app/repositories/item.test.ts +++ b/app/repositories/item.test.ts @@ -61,7 +61,11 @@ describe("[db] itemRepository", async () => { }); test("itemRepository.findAll", async () => { - const item = ItemEntity.createNew({ name: "Foo", price: 300, type: "ore" }); + const item = ItemEntity.createNew({ + name: "Foo", + price: 300, + type: "hotOre", + }); const savedItem = await itemRepository.save(item); const items = await itemRepository.findAll(); expect(items).toContainEqual(savedItem); diff --git a/app/routes/items/route.tsx b/app/routes/items/route.tsx index e5365255..bd50df69 100644 --- a/app/routes/items/route.tsx +++ b/app/routes/items/route.tsx @@ -44,7 +44,14 @@ export default function Item() { // Sort and group items by type const sortedItems = useMemo>(() => { - const base = { hot: [], ice: [], ore: [], milk: [] }; + const base = { + hot: [], + ice: [], + hotOre: [], + iceOre: [], + milk: [], + others: [], + }; if (!items) return base; return items.reduce>((acc, item) => { acc[item.type].push(item); From 439fad9dd73cfe6ce39cf911955f2c6e037edf81 Mon Sep 17 00:00:00 2001 From: Ryunosuke Tokinaga <59079411+toririm@users.noreply.github.com> Date: Fri, 4 Oct 2024 21:01:01 +0900 Subject: [PATCH 28/28] =?UTF-8?q?chore:=20=E3=81=A1=E3=82=87=E3=81=84?= =?UTF-8?q?=E3=83=AA=E3=83=95=E3=82=A1=E3=82=AF=E3=82=BF=E3=83=AA=E3=83=B3?= =?UTF-8?q?=E3=82=B0=20(#199)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/components/functional/useOrderState.ts | 2 +- .../usePreventNumberKeyUpDown.tsx} | 13 ++++--------- app/components/pages/CashierV2.tsx | 6 ++++-- app/routes/items/route.tsx | 3 +++ 4 files changed, 12 insertions(+), 12 deletions(-) rename app/components/{molecules/InputNumber.tsx => functional/usePreventNumberKeyUpDown.tsx} (55%) diff --git a/app/components/functional/useOrderState.ts b/app/components/functional/useOrderState.ts index 0674626b..951db6e7 100644 --- a/app/components/functional/useOrderState.ts +++ b/app/components/functional/useOrderState.ts @@ -6,7 +6,7 @@ import { OrderEntity } from "~/models/order"; type BaseAction = { type: TypeName }; type Action< TypeName extends string, - U = Record, + U extends Record = Record, > = BaseAction & U; type Clear = Action<"clear", { effectFn?: () => void }>; diff --git a/app/components/molecules/InputNumber.tsx b/app/components/functional/usePreventNumberKeyUpDown.tsx similarity index 55% rename from app/components/molecules/InputNumber.tsx rename to app/components/functional/usePreventNumberKeyUpDown.tsx index 8eea8c8c..d8941165 100644 --- a/app/components/molecules/InputNumber.tsx +++ b/app/components/functional/usePreventNumberKeyUpDown.tsx @@ -1,12 +1,9 @@ -import { type ComponentPropsWithRef, useEffect } from "react"; -import { AttractiveTextBox } from "./AttractiveTextBox"; - -type props = ComponentPropsWithRef; +import { useEffect } from "react"; /** - * 上下キーで数値を増減させない数値専用のテキストボックス + * 上下キーで数値を増減させないEffect */ -const InputNumber = ({ ...props }: props) => { +const usePreventNumberKeyUpDown = () => { useEffect(() => { const handler = (event: KeyboardEvent) => { if (event.key === "ArrowUp" || event.key === "ArrowDown") { @@ -21,8 +18,6 @@ const InputNumber = ({ ...props }: props) => { window.removeEventListener("keyup", handler); }; }, []); - - return ; }; -export { InputNumber }; +export { usePreventNumberKeyUpDown }; diff --git a/app/components/pages/CashierV2.tsx b/app/components/pages/CashierV2.tsx index 5d09e6f6..ab1d14a8 100644 --- a/app/components/pages/CashierV2.tsx +++ b/app/components/pages/CashierV2.tsx @@ -5,9 +5,9 @@ import type { OrderEntity } from "~/models/order"; import { useInputStatus } from "../functional/useInputStatus"; import { useLatestOrderId } from "../functional/useLatestOrderId"; import { useOrderState } from "../functional/useOrderState"; +import { usePreventNumberKeyUpDown } from "../functional/usePreventNumberKeyUpDown"; import { useUISession } from "../functional/useUISession"; import { AttractiveTextBox } from "../molecules/AttractiveTextBox"; -import { InputNumber } from "../molecules/InputNumber"; import { ChargeView } from "../organisms/ChargeView"; import { DiscountInput } from "../organisms/DiscountInput"; import { OrderAlertDialog } from "../organisms/OrderAlertDialog"; @@ -31,6 +31,7 @@ const CashierV2 = ({ items, orders, submitPayload }: props) => { useInputStatus(); const [UISession, renewUISession] = useUISession(); const { nextOrderId } = useLatestOrderId(orders); + usePreventNumberKeyUpDown(); useEffect(() => { newOrderDispatch({ type: "updateOrderId", orderId: nextOrderId }); @@ -117,7 +118,8 @@ const CashierV2 = ({ items, orders, submitPayload }: props) => { [newOrderDispatch], )} /> - diff --git a/app/routes/items/route.tsx b/app/routes/items/route.tsx index bd50df69..62905ef3 100644 --- a/app/routes/items/route.tsx +++ b/app/routes/items/route.tsx @@ -8,6 +8,7 @@ import { } from "@remix-run/react"; import { useMemo } from "react"; import useSWRSubscription from "swr/subscription"; +import { usePreventNumberKeyUpDown } from "~/components/functional/usePreventNumberKeyUpDown"; import { Button } from "~/components/ui/button"; import { Input } from "~/components/ui/input"; import { Label } from "~/components/ui/label"; @@ -59,6 +60,8 @@ export default function Item() { }, base); }, [items]); + usePreventNumberKeyUpDown(); + return (

アイテム管理