diff --git a/.gitignore b/.gitignore index f21e3db1..7ecf8651 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ dist-ssr server/dist public/dist .yarn/install-state.gz +yarn.lock .pnp .pnp.js diff --git a/.yarn/cache/react-toastify-npm-11.0.5-6e5a4fd835-50f5b81323.zip b/.yarn/cache/react-toastify-npm-11.0.5-6e5a4fd835-50f5b81323.zip new file mode 100644 index 00000000..afacc668 Binary files /dev/null and b/.yarn/cache/react-toastify-npm-11.0.5-6e5a4fd835-50f5b81323.zip differ diff --git a/apps/nowait-admin/tsconfig.app.tsbuildinfo b/apps/nowait-admin/tsconfig.app.tsbuildinfo index 695e97be..4056a50e 100644 --- a/apps/nowait-admin/tsconfig.app.tsbuildinfo +++ b/apps/nowait-admin/tsconfig.app.tsbuildinfo @@ -1 +1 @@ -{"root":["./src/App.tsx","./src/main.tsx","./src/vite-env.d.ts","./src/components/AdminSidebar.tsx","./src/components/ImageBox.tsx","./src/components/MenuItem.tsx","./src/components/MobileMenuBar.tsx","./src/components/RedBadge.tsx","./src/components/closeButton.tsx","./src/hooks/usePostAdminLogin.tsx","./src/hooks/useWindowWidth.tsx","./src/layout/AdminLayout.tsx","./src/pages/AdminAnalytics/AdminAnalytics.tsx","./src/pages/AdminAuth/AdminAuth.tsx","./src/pages/AdminHome/AdminHome.tsx","./src/pages/AdminHome/components/CardBox.tsx","./src/pages/AdminHome/components/RoundTabButton.tsx","./src/pages/AdminHome/components/WaitingCard.tsx","./src/pages/AdminOrders/AdminOrders.tsx","./src/pages/AdminOrders/CookedPage.tsx","./src/pages/AdminOrders/OrderCard.tsx","./src/pages/LoingPage/LoginPage.tsx","./src/pages/NotFound/NotFound.tsx","./src/utils/UserApi.tsx"],"version":"5.8.3"} +{"root":["./src/App.tsx","./src/main.tsx","./src/vite-env.d.ts","./src/components/AdminSidebar.tsx","./src/components/ImageBox.tsx","./src/components/MenuItem.tsx","./src/components/MobileMenuBar.tsx","./src/components/RedBadge.tsx","./src/components/closeButton.tsx","./src/hooks/useGetReservationList.tsx","./src/hooks/usePostAdminLogin.tsx","./src/hooks/useWindowWidth.tsx","./src/layout/AdminLayout.tsx","./src/pages/AdminAnalytics/AdminAnalytics.tsx","./src/pages/AdminAuth/AdminAuth.tsx","./src/pages/AdminHome/AdminHome.tsx","./src/pages/AdminHome/components/CardBox.tsx","./src/pages/AdminHome/components/RoundTabButton.tsx","./src/pages/AdminHome/components/WaitingCard.tsx","./src/pages/AdminOrders/AdminOrders.tsx","./src/pages/AdminOrders/CookedPage.tsx","./src/pages/AdminOrders/OrderCard.tsx","./src/pages/LoingPage/LoginPage.tsx","./src/pages/NotFound/NotFound.tsx","./src/utils/UserApi.tsx"],"errors":true,"version":"5.8.3"} \ No newline at end of file diff --git a/apps/nowait-user/package.json b/apps/nowait-user/package.json index bf266d2a..86052587 100644 --- a/apps/nowait-user/package.json +++ b/apps/nowait-user/package.json @@ -18,6 +18,7 @@ "react": "^19.1.0", "react-dom": "^19.1.0", "react-router-dom": "^7.6.2", + "react-toastify": "^11.0.5", "zustand": "^5.0.6" }, "devDependencies": { diff --git a/apps/nowait-user/src/App.tsx b/apps/nowait-user/src/App.tsx index 37a97a5f..b4f04bf5 100644 --- a/apps/nowait-user/src/App.tsx +++ b/apps/nowait-user/src/App.tsx @@ -1,6 +1,8 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import Router from "./routes/Router"; import { BrowserRouter } from "react-router-dom"; +import GlobalToast from "./components/common/Toast"; +import 'react-toastify/dist/ReactToastify.css'; function App() { const queryClient = new QueryClient(); @@ -9,6 +11,7 @@ function App() { + ); diff --git a/apps/nowait-user/src/assets/icon/minus.svg b/apps/nowait-user/src/assets/icon/minus.svg index 065671b3..f11159e6 100644 --- a/apps/nowait-user/src/assets/icon/minus.svg +++ b/apps/nowait-user/src/assets/icon/minus.svg @@ -1,3 +1,3 @@ - + diff --git a/apps/nowait-user/src/components/SmallActionButton.tsx b/apps/nowait-user/src/components/SmallActionButton.tsx new file mode 100644 index 00000000..4c9cc4e4 --- /dev/null +++ b/apps/nowait-user/src/components/SmallActionButton.tsx @@ -0,0 +1,30 @@ +interface PropsType { + type?: "button" | "submit" | "reset"; + ariaLabel: string; + children: React.ReactNode; + onClick: () => void; + icon?: React.ReactNode; + iconPosition?: "left" | "right"; + className?: string; +} +export const SmallActionButton = ({ + type = "button", + children, + onClick, + ariaLabel, + + className, +}: PropsType) => { + return ( + + ); +}; diff --git a/apps/nowait-user/src/components/MenuList.tsx b/apps/nowait-user/src/components/common/MenuList.tsx similarity index 58% rename from apps/nowait-user/src/components/MenuList.tsx rename to apps/nowait-user/src/components/common/MenuList.tsx index 87a32fac..5d19807f 100644 --- a/apps/nowait-user/src/components/MenuList.tsx +++ b/apps/nowait-user/src/components/common/MenuList.tsx @@ -1,5 +1,5 @@ import { useNavigate, useParams } from "react-router-dom"; -import type { MenuType } from "../types/order/menu"; +import type { MenuType } from "../../types/order/menu"; const dummyData: MenuType[] = [ { @@ -25,9 +25,25 @@ const dummyData: MenuType[] = [ price: 9000, image: "", }, + { + id: "4", + name: "파인애플 샤베트2", + description: + "시원한 파인애플 샤베트 입니다.시원한 파인애플 샤베트 입니다.시원한 파인애플 샤베트 입니다.시원한 파인애플 샤베트 입니다.시원한 파인애플 샤베트 입니다.시원한 파인애플 샤베트 입니다.시원한 파인애플 샤베트 입니다.시원한 파인애플 샤베트 입니다.시원한 파인애플 샤베트 입니다.시원한 파인애플 샤베트 입니다.시원한 파인애플 샤베트 입니다.시원한 파인애플 샤베트 입니다.시원한 파인애플 샤베트 입니다.시원한 파인애플 샤베트 입니다.시원한 파인애플 샤베트 입니다.시원한 파인애플 샤베트 입니다.", + price: 9000, + image: "", + }, + { + id: "5", + name: "파인애플 샤베트3", + description: + "시원한 파인애플 샤베트 입니다.시원한 파인애플 샤베트 입니다.시원한 파인애플 샤베트 입니다.시원한 파인애플 샤베트 입니다.시원한 파인애플 샤베트 입니다.시원한 파인애플 샤베트 입니다.시원한 파인애플 샤베트 입니다.시원한 파인애플 샤베트 입니다.시원한 파인애플 샤베트 입니다.시원한 파인애플 샤베트 입니다.시원한 파인애플 샤베트 입니다.시원한 파인애플 샤베트 입니다.시원한 파인애플 샤베트 입니다.시원한 파인애플 샤베트 입니다.시원한 파인애플 샤베트 입니다.시원한 파인애플 샤베트 입니다.", + price: 9000, + image: "", + }, ]; -const MenuList = ({ mode } : { mode: string }) => { +const MenuList = ({ mode }: { mode: string }) => { const navigate = useNavigate(); const { storeId } = useParams(); return ( @@ -39,7 +55,9 @@ const MenuList = ({ mode } : { mode: string }) => {
  • { className="bg-[#F2F6F9] rounded-[7px] w-[28px] h-[28px] flex items-center justify-center" onClick={increaseQuantityButton} > - 수량 마이너스 아이콘 + ); diff --git a/apps/nowait-user/src/components/common/SuccessMessagePage.tsx b/apps/nowait-user/src/components/common/SuccessMessagePage.tsx index 3e6a6fe2..85f4dc5e 100644 --- a/apps/nowait-user/src/components/common/SuccessMessagePage.tsx +++ b/apps/nowait-user/src/components/common/SuccessMessagePage.tsx @@ -1,5 +1,7 @@ import PageFooterButton from "../order/PageFooterButton"; import { Button } from "@repo/ui"; +import { SmallActionButton } from "../SmallActionButton"; +import { useNavigate, useParams } from "react-router-dom"; interface PropsType { imageSrc: string; @@ -18,19 +20,31 @@ const SuccessMessagePage = ({ onClick, buttonText, }: PropsType) => { + const navigate = useNavigate(); + const { storeId } = useParams(); return ( -

    -
    +
    +
    {imageAlt}

    {title}

    -

    +

    {message}

    + {storeId && ( + navigate(`/${storeId}`)} //주문 내역 페이지로 변경하기 + > + 주문내역 확인 + + )}
    + +
    ); }; diff --git a/apps/nowait-user/src/components/order/Toast.tsx b/apps/nowait-user/src/components/order/Toast.tsx deleted file mode 100644 index fec45fdc..00000000 --- a/apps/nowait-user/src/components/order/Toast.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import React from "react"; -import { motion } from "framer-motion"; - -interface PropsType { - message: string; -} - -const Toast = ({ message }: PropsType) => { - return ( - -

    {message}

    -
    - ); -}; - -export default Toast; diff --git a/apps/nowait-user/src/components/order/TotalButton.tsx b/apps/nowait-user/src/components/order/TotalButton.tsx index 4e2129e2..1ad19b4e 100644 --- a/apps/nowait-user/src/components/order/TotalButton.tsx +++ b/apps/nowait-user/src/components/order/TotalButton.tsx @@ -1,21 +1,23 @@ import { sumQuantity, sumTotalPrice } from "../../utils/sumUtils"; import { useCartStore } from "../../stores/cartStore"; -import { useLocation, useParams } from "react-router-dom"; -const TotalButton = () => { +interface PropsType { + variant?: "default" | "orderPage"; + actionText: string; +} + +const TotalButton = ({ variant = "default", actionText }: PropsType) => { const { cart } = useCartStore(); - const { storeId } = useParams(); - const pathname = useLocation().pathname; return ( <> - {pathname !== `/${storeId}/order` && ( + {variant !== "orderPage" && ( {`${sumQuantity( cart, "quantity" )}`} )} - {`${sumTotalPrice(cart)}원 주문하기`} + {`${sumTotalPrice(cart).toLocaleString()}원 ${actionText}`} ); }; diff --git a/apps/nowait-user/src/global.css b/apps/nowait-user/src/global.css index 4b5faff2..f0bd0239 100644 --- a/apps/nowait-user/src/global.css +++ b/apps/nowait-user/src/global.css @@ -37,11 +37,13 @@ body { display: flex; min-width: 100vw; min-height: 100vh; + background-color: #222; } #root { max-width: 430px; min-width: 360px; + background-color: #fff; width: 100%; margin: 0 auto; } @@ -131,3 +133,19 @@ body { .scrollbar-hide::-webkit-scrollbar { display: none; /* Chrome, Safari and Opera */ } + +.order-toast { + font-family: 'pretendard'; + font-size: 14px; + font-weight: 500; + line-height: 144%; + max-width: 11.25rem; + max-height: 17px; + color: var(--navy-70); + background-color: #F2F6F9; + /* display: flex; */ + padding: unset !important; + justify-content: center; + text-align: center; + border-radius: 999px; +} diff --git a/apps/nowait-user/src/hooks/useTimeout.ts b/apps/nowait-user/src/hooks/useTimeout.ts new file mode 100644 index 00000000..3edb83bd --- /dev/null +++ b/apps/nowait-user/src/hooks/useTimeout.ts @@ -0,0 +1,10 @@ +import { useEffect } from "react"; + +const useTimeout = (delay:number) => { + useEffect(() => { + const timeOut = setTimeout(() => console.log(), delay); + return () => { + clearTimeout(timeOut); + }; + }); +}; diff --git a/apps/nowait-user/src/lib/order.ts b/apps/nowait-user/src/lib/order.ts index 834406e7..b8e368ae 100644 --- a/apps/nowait-user/src/lib/order.ts +++ b/apps/nowait-user/src/lib/order.ts @@ -8,21 +8,27 @@ interface OrderType { items: { menuId: string; quantity: number }[]; } -// interface ServerResponse { -// success: boolean; -// response: { -// hasNext: boolean; -// storeReadDtos: Store[]; -// }; -// } +interface ServerResponse { + success: boolean; + response: { + orderId: number; + storeId: number; + storeName: string; + sessionId: string; + depositorName: string; + orderItems: { menuId: string; quantity: number }[]; + status: string; + totalPrice: number; + }; +} export const createOrder = async ( - storeId: string | undefined, + storeId: string, tableId: string, payload: OrderType ) => { try { - const res = await axios.post( + const res = await axios.post( `${SERVER_URI}/orders/create/${storeId}/${tableId}`, payload ); @@ -31,3 +37,17 @@ export const createOrder = async ( console.log(error); } }; + +export const getMyOrderList = async ( + storeId: string | undefined, + tableId: string +) => { + try { + const res = await axios.get( + `${SERVER_URI}/orders/items/${storeId}/${tableId}` + ); + return res.data; + } catch (error) { + console.log(error); + } +}; diff --git a/apps/nowait-user/src/pages/order/AddMenuPage.tsx b/apps/nowait-user/src/pages/order/AddMenuPage.tsx index 826adf23..4d1f47d5 100644 --- a/apps/nowait-user/src/pages/order/AddMenuPage.tsx +++ b/apps/nowait-user/src/pages/order/AddMenuPage.tsx @@ -1,6 +1,6 @@ import { useState } from "react"; import QuantitySelector from "../../components/common/QuantitySelector"; -import { useLocation, useNavigate } from "react-router-dom"; +import { useLocation, useNavigate, useParams } from "react-router-dom"; import PageFooterButton from "../../components/order/PageFooterButton"; import { Button } from "@repo/ui"; import type { CartType } from "../../types/order/cart"; @@ -10,6 +10,7 @@ import type { MenuType } from "../../types/order/menu"; const AddMenuPage = () => { const location = useLocation(); const navigate = useNavigate(); + const { storeId } = useParams(); const { id, image, name, description, price } = location.state as MenuType; const [quantity, setQuantity] = useState(1); const { addToCart } = useCartStore(); @@ -22,21 +23,22 @@ const AddMenuPage = () => { price: price * quantity, }; addToCart(item); - navigate(-1); + navigate(`/${storeId}`, { state: { added: true }, replace: true }); }; return (

    - 음식 메뉴 이미지 + 음식 메뉴 이미지

    -

    {name}

    +

    {name}

    {description}

    -
    -
    + {/* 메뉴 가격 및 수량 컨트롤 */} +
    +

    {(price * quantity).toLocaleString()}원

    diff --git a/apps/nowait-user/src/pages/order/OrderListPage.tsx b/apps/nowait-user/src/pages/order/OrderListPage.tsx index 0340e181..d8eddd29 100644 --- a/apps/nowait-user/src/pages/order/OrderListPage.tsx +++ b/apps/nowait-user/src/pages/order/OrderListPage.tsx @@ -1,4 +1,4 @@ -import MenuItem from "../../components/order/MenuItem"; +import CartItem from "../../components/order/CartItem"; import PageFooterButton from "../../components/order/PageFooterButton"; import { Button } from "@repo/ui"; import { useNavigate, useParams } from "react-router-dom"; @@ -6,8 +6,11 @@ import TotalButton from "../../components/order/TotalButton"; import { useCartStore } from "../../stores/cartStore"; import { AnimatePresence } from "framer-motion"; import EmptyCart from "../../components/order/EmptyCart"; -import axios from "axios"; -import { getTableId } from "../../utils/cartStorage"; +import { getTableId, setSessionData } from "../../utils/cartStorage"; +import { SmallActionButton } from "../../components/SmallActionButton"; +import Add from "../../assets/icon/Add.svg?react"; +import { sumTotalPrice } from "../../utils/sumUtils"; +import { createOrder } from "../../lib/order"; const OrderListPage = () => { const navigate = useNavigate(); @@ -15,8 +18,6 @@ const OrderListPage = () => { const tableId = getTableId(); const { cart } = useCartStore(); - const SERVER_URI = import.meta.env.VITE_SERVER_URI; - const orderHandleButton = async () => { try { const payload = { @@ -25,17 +26,15 @@ const OrderListPage = () => { menuId: item.menuId, quantity: item.quantity, })), + totalPrice: sumTotalPrice(cart), }; - const url = `${SERVER_URI}/orders/create/${storeId}/${tableId}`; - const res = await axios.post(url, payload); - if (res.status === 201 && res.data.success) { - console.log(res); - localStorage.setItem("sessionId", res.data.response.sessionId); - localStorage.setItem("depositorName", res.data.response.depositorName); + const res = await createOrder(storeId!, tableId!, payload); + if (res?.success) { + //세션 아이디, 입금자명 로컬스토리지 저장 + setSessionData(res.response.sessionId, res.response.depositorName); } else { console.log("error"); } - navigate(`/${storeId}/payer`); } catch (e) { console.log(e); @@ -48,13 +47,13 @@ const OrderListPage = () => {

    총 주문 {cart.length}건

    -
      +
        {cart.map((item) => { return ( - { ); })} + navigate(`/${storeId}`)} + icon={} + className="py-5 border-none" + > + 메뉴 추가하기 + +
    diff --git a/apps/nowait-user/src/pages/order/RedirectToStorePage.tsx b/apps/nowait-user/src/pages/order/RedirectToStorePage.tsx index 815c21c7..63d4d314 100644 --- a/apps/nowait-user/src/pages/order/RedirectToStorePage.tsx +++ b/apps/nowait-user/src/pages/order/RedirectToStorePage.tsx @@ -1,4 +1,4 @@ -import React, { useEffect } from "react"; +import { useEffect } from "react"; import { useNavigate, useParams } from "react-router-dom"; const RedirectToStorePage = () => { diff --git a/apps/nowait-user/src/pages/order/RemittanceRequestPage.tsx b/apps/nowait-user/src/pages/order/RemittanceRequestPage.tsx index 1dd02c42..c49cccfc 100644 --- a/apps/nowait-user/src/pages/order/RemittanceRequestPage.tsx +++ b/apps/nowait-user/src/pages/order/RemittanceRequestPage.tsx @@ -1,32 +1,29 @@ -import { useState } from "react"; import PageFooterButton from "../../components/order/PageFooterButton"; import { Button } from "@repo/ui"; import copy from "../../assets/icon/copy.svg"; import useThrottle from "../../hooks/useThrottle"; -import Toast from "../../components/order/Toast"; import useModal from "../../hooks/useModal"; import { useLocation, useNavigate, useParams } from "react-router-dom"; import ConfirmModal from "../../components/order/ConfirmModal"; +import { useToastStore } from "../../stores/toastStore"; const RemittanceRequestPage = () => { - const [showToast, setShowToast] = useState(false); - + const { showToast } = useToastStore(); const navigate = useNavigate(); const { storeId } = useParams(); const modal = useModal(); const price = useLocation().state; const account = "기업은행 611-000202-01-010"; - const clipBoardDelay = 3000; + const clipBoardDelay = 2000; const handleCopyClipBoard = useThrottle(() => { navigator.clipboard.writeText(account); - setShowToast(true); - setTimeout(() => setShowToast(false), clipBoardDelay); + showToast("계좌번호가 복사되었습니다"); }, clipBoardDelay); return ( -
    -
    +
    +

    주문을 위해 이체해 주세요 @@ -50,9 +47,9 @@ const RemittanceRequestPage = () => {

    {price}원

    -
    + {/*
    {showToast && } -
    +
    */} {modal.isOpen && ( navigate(`/${storeId}/order/success`)} diff --git a/apps/nowait-user/src/pages/order/StorePage.tsx b/apps/nowait-user/src/pages/order/StorePage.tsx index 636f4fae..f5186383 100644 --- a/apps/nowait-user/src/pages/order/StorePage.tsx +++ b/apps/nowait-user/src/pages/order/StorePage.tsx @@ -1,25 +1,36 @@ -import { useNavigate, useParams } from "react-router-dom"; +import { useLocation, useNavigate, useParams } from "react-router-dom"; import PageFooterButton from "../../components/order/PageFooterButton"; import { Button } from "@repo/ui"; import TotalButton from "../../components/order/TotalButton"; import { useCartStore } from "../../stores/cartStore"; -import MenuList from "../../components/MenuList"; -import axios from "axios"; - +import MenuList from "../../components/common/MenuList"; +import { useEffect } from "react"; +import { getMyOrderList } from "../../lib/order"; +import { useToastStore } from "../../stores/toastStore"; const StorePage = () => { const navigate = useNavigate(); const { storeId } = useParams(); + const tableId = localStorage.getItem("tableId"); + const location = useLocation(); + const added = (location.state as { added?: boolean } | null)?.added; const { cart } = useCartStore(); -const SERVER_URI = import.meta.env.VITE_SERVER_URI; - const a = async() => { + const { showToast } = useToastStore(); + + //메뉴 추가 시 toast 띄우기 + useEffect(() => { + if (added) showToast("메뉴를 담았습니다"); + navigate(location.pathname, { replace: true }); + }, [added]); + + const getMyOrderListButton = async () => { try { - const res = await axios.get(`${SERVER_URI}/orders/items/7/1`) - console.log(res) + const res = await getMyOrderList(storeId, tableId!); + navigate(`/${storeId}/myOrderList`, { state: res }); } catch (error) { - console.log(error) + console.log(error); } - } + }; return (
    @@ -30,7 +41,7 @@ const SERVER_URI = import.meta.env.VITE_SERVER_URI;

    5번 테이블

    {cart && cart.length > 0 && ( - )} diff --git a/apps/nowait-user/src/pages/reserve/StoreDetailPage.tsx b/apps/nowait-user/src/pages/reserve/StoreDetailPage.tsx index b797fe71..5e68b5f0 100644 --- a/apps/nowait-user/src/pages/reserve/StoreDetailPage.tsx +++ b/apps/nowait-user/src/pages/reserve/StoreDetailPage.tsx @@ -1,4 +1,4 @@ -import MenuList from "../../components/MenuList"; +import MenuList from "../../components/common/MenuList"; import Arrow from "../../assets/icon/arrow-right.svg?react"; import MapPin from "../../assets/icon/map-pin.svg?react"; import Clock from "../../assets/icon/clock.svg?react"; diff --git a/apps/nowait-user/src/stores/cartStore.ts b/apps/nowait-user/src/stores/cartStore.ts index 700d9fd5..77a924d0 100644 --- a/apps/nowait-user/src/stores/cartStore.ts +++ b/apps/nowait-user/src/stores/cartStore.ts @@ -1,12 +1,12 @@ import { create } from "zustand"; -import type { CartItem } from "../types/order/cart"; import { persist } from "zustand/middleware"; +import type { CartType } from "../types/order/cart"; interface CartState { - cart: CartItem[]; + cart: CartType[]; increaseQuantity: (id: string, price: number) => void; decreaseQuantity: (id: string, price: number) => void; - addToCart: (item: CartItem) => void; + addToCart: (item: CartType) => void; removeFromCart: (id: string) => void; clearCart: () => void; } @@ -15,10 +15,11 @@ export const useCartStore = create()( persist( (set) => ({ cart: [], + // 메뉴 수량 증가 increaseQuantity: (id, price) => set(({ cart }) => ({ cart: cart.map((item) => { - return item.id === id + return item.menuId === id ? { ...item, quantity: item.quantity + 1, @@ -27,10 +28,11 @@ export const useCartStore = create()( : item; }), })), + //메뉴 수량 감소 decreaseQuantity: (id, price) => set(({ cart }) => ({ cart: cart.map((item) => { - return item.id === id + return item.menuId === id ? { ...item, quantity: item.quantity - 1, @@ -39,15 +41,17 @@ export const useCartStore = create()( : item; }), })), + //메뉴 추가(같은 메뉴가 있다면 기존 수량에 합산, 없으면 새 메뉴 추가) addToCart: (item) => set(({ cart }) => { const existingIndex = cart.findIndex( - (cartItem) => cartItem.id === item.id + (cartItem) => cartItem.menuId === item.menuId ); if (existingIndex !== -1) { const updatedCart = [...cart]; updatedCart[existingIndex] = { ...updatedCart[existingIndex], + price : updatedCart[existingIndex].price + item.price, quantity: updatedCart[existingIndex].quantity + item.quantity, }; return { cart: updatedCart }; // 수량 변경 @@ -55,10 +59,12 @@ export const useCartStore = create()( return { cart: [...cart, item] }; // 새 메뉴 추가 } }), + // 메뉴 삭제 removeFromCart: (id) => set((state) => ({ - cart: state.cart.filter((item) => item.id !== id), + cart: state.cart.filter((item) => item.menuId !== id), })), + // 장바구니 전체 삭제(key는 남아있고, value만 삭제) clearCart: () => set({ cart: [] }), }), { name: "cart-storage" } diff --git a/apps/nowait-user/src/stores/toastStore.ts b/apps/nowait-user/src/stores/toastStore.ts new file mode 100644 index 00000000..eb9d7749 --- /dev/null +++ b/apps/nowait-user/src/stores/toastStore.ts @@ -0,0 +1,27 @@ +import { create } from "zustand"; + +let toastTimeout: ReturnType | null = null; + +interface ToastState { + message: string; + isOpen: boolean; + showToast: (message: string, duration?: number) => void; + clearToastTimeout: () => void; +} + +export const useToastStore = create((set) => ({ + message: "", + isOpen: false, + showToast: (message, duration = 2000) => { + if (toastTimeout) clearTimeout(toastTimeout); + set({ message, isOpen: true }); + toastTimeout = setTimeout(() => { + set({ message: "", isOpen: false }); + toastTimeout = null; + }, duration); + }, + clearToastTimeout: () => { + if (toastTimeout) clearTimeout(toastTimeout); + toastTimeout = null; + }, +})); diff --git a/apps/nowait-user/src/utils/cartStorage.ts b/apps/nowait-user/src/utils/cartStorage.ts index 51c58929..8f50e87f 100644 --- a/apps/nowait-user/src/utils/cartStorage.ts +++ b/apps/nowait-user/src/utils/cartStorage.ts @@ -1,4 +1,4 @@ -import type { CartItem } from "../types/order/cart"; +import type { CartType } from "../types/order/cart"; const CART_KEY = "cart"; @@ -7,7 +7,15 @@ export const getTableId = (): string => { return tableId || ""; }; -export const getcart = (): CartItem[] => { + +//주문 시 세션 아이디, 입금자명 로컬스토리지에 저장 +export const setSessionData = (sessionId: string, depositorName: string) => { + localStorage.setItem("sessionId", sessionId); + localStorage.setItem("depositorName", depositorName); +}; + + +export const getcart = (): CartType[] => { const cartString = localStorage.getItem(CART_KEY); try { const parsedCart = cartString ? JSON.parse(cartString) : []; @@ -17,9 +25,9 @@ export const getcart = (): CartItem[] => { } }; -export const addToCart = (newItem: CartItem) => { +export const addToCart = (newItem: CartType) => { const cart = getcart(); - const existingItem = cart.find((item) => item.id === newItem.id); + const existingItem = cart.find((item) => item.menuId === newItem.menuId); if (existingItem) { existingItem.quantity += newItem.quantity; existingItem.price += newItem.price; diff --git a/apps/nowait-user/src/utils/sumUtils.ts b/apps/nowait-user/src/utils/sumUtils.ts index 3294ffde..84874533 100644 --- a/apps/nowait-user/src/utils/sumUtils.ts +++ b/apps/nowait-user/src/utils/sumUtils.ts @@ -1,4 +1,4 @@ -import type { CartItem } from "../types/order/cart"; +import type { CartType } from "../types/order/cart"; export const sumQuantity = ( items: T[], @@ -7,10 +7,10 @@ export const sumQuantity = ( return items.reduce((acc, cur) => acc + Number(cur[key]), 0); }; -export const sumTotalPrice = (items: CartItem[]) => { +export const sumTotalPrice = (items: CartType[]) => { const totalPrice = items.reduce( (acc, cur) => acc + cur.price, 0 ); - return totalPrice.toLocaleString(); + return totalPrice; }; diff --git a/package.json b/package.json index 14d19b23..c84625d6 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,8 @@ }, "dependencies": { "clsx": "^2.1.1", + "framer-motion": "^12.20.1", + "react-toastify": "^11.0.5", "zustand": "^5.0.6" } } diff --git a/yarn.lock b/yarn.lock index d142d3da..463e6bb3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3751,6 +3751,8 @@ __metadata: dependencies: "@yarnpkg/sdks": "npm:^3.2.2" clsx: "npm:^2.1.1" + framer-motion: "npm:^12.20.1" + react-toastify: "npm:^11.0.5" turbo: "npm:^2.5.4" typescript: "npm:^5.8.3" zustand: "npm:^5.0.6" @@ -3942,6 +3944,7 @@ __metadata: react: "npm:^19.1.0" react-dom: "npm:^19.1.0" react-router-dom: "npm:^7.6.2" + react-toastify: "npm:^11.0.5" tailwindcss: "npm:^4.1.5" typescript: "npm:~5.8.3" typescript-eslint: "npm:^8.30.1" @@ -4228,6 +4231,18 @@ __metadata: languageName: node linkType: hard +"react-toastify@npm:^11.0.5": + version: 11.0.5 + resolution: "react-toastify@npm:11.0.5" + dependencies: + clsx: "npm:^2.1.1" + peerDependencies: + react: ^18 || ^19 + react-dom: ^18 || ^19 + checksum: 10c0/50f5b81323ebb1957b2efd0963fac24aa1407155d16ab756ffd6d0f42f8af17e796b3958a9fce13e9d1b945d6c3a5a9ebf13529478474d8a2af4bf1dd0db67d2 + languageName: node + linkType: hard + "react@npm:^19.1.0": version: 19.1.0 resolution: "react@npm:19.1.0"