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..ef11c3fa 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/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}`)} //주문 내역 페이지로 변경하기 + > + 주문내역 확인 + + )}
    + - -
    - ); -}; - -export default OrderListPage; diff --git a/apps/nowait-user/src/pages/order/StorePage.tsx b/apps/nowait-user/src/pages/order/StorePage.tsx deleted file mode 100644 index 636f4fae..00000000 --- a/apps/nowait-user/src/pages/order/StorePage.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import { 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"; - - -const StorePage = () => { - const navigate = useNavigate(); - const { storeId } = useParams(); - const { cart } = useCartStore(); -const SERVER_URI = import.meta.env.VITE_SERVER_URI; - const a = async() => { - try { - const res = await axios.get(`${SERVER_URI}/orders/items/7/1`) - console.log(res) - } catch (error) { - console.log(error) - } - } - - return ( -
    -
    -
    -
    -

    스페이시스

    -

    5번 테이블

    -
    - -
    - -
    - {cart && cart.length > 0 && ( - - - - )} -
    - ); -}; - -export default StorePage; diff --git a/apps/nowait-user/src/pages/order/AddMenuPage.tsx b/apps/nowait-user/src/pages/order/addMenu/AddMenuPage.tsx similarity index 60% rename from apps/nowait-user/src/pages/order/AddMenuPage.tsx rename to apps/nowait-user/src/pages/order/addMenu/AddMenuPage.tsx index 826adf23..fac5ce74 100644 --- a/apps/nowait-user/src/pages/order/AddMenuPage.tsx +++ b/apps/nowait-user/src/pages/order/addMenu/AddMenuPage.tsx @@ -1,15 +1,16 @@ import { useState } from "react"; -import QuantitySelector from "../../components/common/QuantitySelector"; -import { useLocation, useNavigate } from "react-router-dom"; -import PageFooterButton from "../../components/order/PageFooterButton"; +import QuantitySelector from "../../../components/common/QuantitySelector"; +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"; -import { useCartStore } from "../../stores/cartStore"; -import type { MenuType } from "../../types/order/menu"; +import type { CartType } from "../../../types/order/cart"; +import { useCartStore } from "../../../stores/cartStore"; +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/RedirectToStorePage.tsx b/apps/nowait-user/src/pages/order/home/RedirectToStorePage.tsx similarity index 92% rename from apps/nowait-user/src/pages/order/RedirectToStorePage.tsx rename to apps/nowait-user/src/pages/order/home/RedirectToStorePage.tsx index 815c21c7..63d4d314 100644 --- a/apps/nowait-user/src/pages/order/RedirectToStorePage.tsx +++ b/apps/nowait-user/src/pages/order/home/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/home/StorePage.tsx b/apps/nowait-user/src/pages/order/home/StorePage.tsx new file mode 100644 index 00000000..429e6266 --- /dev/null +++ b/apps/nowait-user/src/pages/order/home/StorePage.tsx @@ -0,0 +1,45 @@ +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/common/MenuList"; +import { useEffect } from "react"; +import { useToastStore } from "../../../stores/toastStore"; +import StoreHeader from "./components/StoreHeader"; + +const StorePage = () => { + const navigate = useNavigate(); + const { storeId } = useParams(); + const location = useLocation(); + const added = (location.state as { added?: boolean } | null)?.added; + const { cart } = useCartStore(); + const { showToast } = useToastStore(); + + //메뉴 추가 시 toast 띄우기 + useEffect(() => { + if (added) showToast("메뉴를 담았습니다"); + navigate(location.pathname, { replace: true }); + }, [added]); + + return ( +
    +
    + + +
    + {cart && cart.length > 0 && ( + + + + )} +
    + ); +}; + +export default StorePage; diff --git a/apps/nowait-user/src/pages/order/home/components/StoreHeader.tsx b/apps/nowait-user/src/pages/order/home/components/StoreHeader.tsx new file mode 100644 index 00000000..d438eb30 --- /dev/null +++ b/apps/nowait-user/src/pages/order/home/components/StoreHeader.tsx @@ -0,0 +1,34 @@ +import { useNavigate, useParams } from "react-router-dom"; +import { getMyOrderList } from "../../../../lib/order"; + +const StoreHeader = () => { + const navigate = useNavigate(); + const { storeId } = useParams(); + const tableId = localStorage.getItem("tableId"); + + const getMyOrderListButton = async () => { + try { + const res = await getMyOrderList(storeId, tableId!); + navigate(`/${storeId}/myOrderList`, { state: res }); + } catch (error) { + console.log(error); + } + }; + + return ( +
    +
    +

    스페이시스

    +

    5번 테이블

    +
    + +
    + ); +}; + +export default StoreHeader; diff --git a/apps/nowait-user/src/pages/order/orderList/OrderListPage.tsx b/apps/nowait-user/src/pages/order/orderList/OrderListPage.tsx new file mode 100644 index 00000000..5cd96b61 --- /dev/null +++ b/apps/nowait-user/src/pages/order/orderList/OrderListPage.tsx @@ -0,0 +1,85 @@ +import CartItem from "./components/CartItem"; +import PageFooterButton from "../../../components/order/PageFooterButton"; +import { Button } from "@repo/ui"; +import { useNavigate, useParams } from "react-router-dom"; +import TotalButton from "../../../components/order/TotalButton"; +import { useCartStore } from "../../../stores/cartStore"; +import { AnimatePresence } from "framer-motion"; +import EmptyCart from "./components/EmptyCart"; +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(); + const { storeId } = useParams(); + const tableId = getTableId(); + const { cart } = useCartStore(); + + const orderHandleButton = async () => { + try { + const payload = { + depositorName: "홍길동", + items: cart.map((item) => ({ + menuId: item.menuId, + quantity: item.quantity, + })), + totalPrice: sumTotalPrice(cart), + }; + 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); + } + }; + + if (cart.length === 0) return ; + + return ( +
    +
    +

    총 주문 {cart.length}건

    +
      + + {cart.map((item) => { + return ( + + ); + })} + + navigate(`/${storeId}`)} + icon={} + className="py-5 border-none" + > + 메뉴 추가하기 + + +
    +
    + + + +
    + ); +}; + +export default OrderListPage; diff --git a/apps/nowait-user/src/components/order/MenuItem.tsx b/apps/nowait-user/src/pages/order/orderList/components/CartItem.tsx similarity index 82% rename from apps/nowait-user/src/components/order/MenuItem.tsx rename to apps/nowait-user/src/pages/order/orderList/components/CartItem.tsx index 75a9559b..377d86d2 100644 --- a/apps/nowait-user/src/components/order/MenuItem.tsx +++ b/apps/nowait-user/src/pages/order/orderList/components/CartItem.tsx @@ -1,6 +1,6 @@ -import QuantitySelector from "../common/QuantitySelector"; -import Close from "../../assets/icon/close.svg?react"; -import { useCartStore } from "../../stores/cartStore"; +import QuantitySelector from "../../../../components/common/QuantitySelector"; +import Close from "../../../../assets/icon/close.svg?react"; +import { useCartStore } from "../../../../stores/cartStore"; import { motion } from "framer-motion"; import { useState } from "react"; @@ -11,7 +11,7 @@ interface PropsType { quantity: number; } -const MenuItem = ({ id, name, price, quantity }: PropsType) => { +const CartItem = ({ id, name, price, quantity }: PropsType) => { const { removeFromCart, increaseQuantity, decreaseQuantity } = useCartStore(); const [isRemoving, setIsRemoving] = useState(false); return ( @@ -53,4 +53,4 @@ const MenuItem = ({ id, name, price, quantity }: PropsType) => { ); }; -export default MenuItem; +export default CartItem; diff --git a/apps/nowait-user/src/components/order/EmptyCart.tsx b/apps/nowait-user/src/pages/order/orderList/components/EmptyCart.tsx similarity index 66% rename from apps/nowait-user/src/components/order/EmptyCart.tsx rename to apps/nowait-user/src/pages/order/orderList/components/EmptyCart.tsx index 505a43ce..076bfa20 100644 --- a/apps/nowait-user/src/components/order/EmptyCart.tsx +++ b/apps/nowait-user/src/pages/order/orderList/components/EmptyCart.tsx @@ -1,5 +1,6 @@ import { useNavigate, useParams } from "react-router-dom"; -import Add from "../../assets/icon/Add.svg?react"; +import Add from "../../../../assets/icon/Add.svg?react"; +import { SmallActionButton } from "../../../../components/SmallActionButton"; const EmptyCart = () => { const navigate = useNavigate(); @@ -11,15 +12,14 @@ const EmptyCart = () => {
    마음에 드는 메뉴를 담아주세요! - +
    ); }; diff --git a/apps/nowait-user/src/pages/order/OrderSuccessPage.tsx b/apps/nowait-user/src/pages/order/orderSuccess/OrderSuccessPage.tsx similarity index 86% rename from apps/nowait-user/src/pages/order/OrderSuccessPage.tsx rename to apps/nowait-user/src/pages/order/orderSuccess/OrderSuccessPage.tsx index f7fad5ce..5c241db9 100644 --- a/apps/nowait-user/src/pages/order/OrderSuccessPage.tsx +++ b/apps/nowait-user/src/pages/order/orderSuccess/OrderSuccessPage.tsx @@ -1,5 +1,5 @@ import { useNavigate, useParams } from "react-router-dom"; -import SuccessMessagePage from "../../components/common/SuccessMessagePage"; +import SuccessMessagePage from "../../../components/common/SuccessMessagePage"; const OrderSuccessPage = () => { const navigate = useNavigate(); diff --git a/apps/nowait-user/src/pages/order/PayerNameInput.tsx b/apps/nowait-user/src/pages/order/payer/PayerNameInput.tsx similarity index 80% rename from apps/nowait-user/src/pages/order/PayerNameInput.tsx rename to apps/nowait-user/src/pages/order/payer/PayerNameInput.tsx index 961a2567..28cfa030 100644 --- a/apps/nowait-user/src/pages/order/PayerNameInput.tsx +++ b/apps/nowait-user/src/pages/order/payer/PayerNameInput.tsx @@ -1,11 +1,11 @@ import { useState } from "react"; import { Button } from "@repo/ui"; -import PageFooterButton from "../../components/order/PageFooterButton"; +import PageFooterButton from "../../../components/order/PageFooterButton"; import { useNavigate, useParams } from "react-router-dom"; -import useModal from "../../hooks/useModal"; -import ConfirmModal from "../../components/order/ConfirmModal"; -import { useCartStore } from "../../stores/cartStore"; -import { sumTotalPrice } from "../../utils/sumUtils"; +import useModal from "../../../hooks/useModal"; +import ConfirmModal from "../../../components/order/ConfirmModal"; +import { useCartStore } from "../../../stores/cartStore"; +import { sumTotalPrice } from "../../../utils/sumUtils"; const PayerNameInput = () => { const [payer, setPayer] = useState(""); @@ -15,8 +15,8 @@ const PayerNameInput = () => { const { cart } = useCartStore(); return ( -
    -
    +
    +

    정확한 결제 확인을 위해 @@ -33,7 +33,7 @@ const PayerNameInput = () => { value={payer} onChange={(e) => setPayer(e.target.value)} /> -

    +
    {modal.isOpen && ( diff --git a/apps/nowait-user/src/pages/order/RemittanceRequestPage.tsx b/apps/nowait-user/src/pages/order/remittance/RemittanceRequestPage.tsx similarity index 72% rename from apps/nowait-user/src/pages/order/RemittanceRequestPage.tsx rename to apps/nowait-user/src/pages/order/remittance/RemittanceRequestPage.tsx index 1dd02c42..177cdcf7 100644 --- a/apps/nowait-user/src/pages/order/RemittanceRequestPage.tsx +++ b/apps/nowait-user/src/pages/order/remittance/RemittanceRequestPage.tsx @@ -1,32 +1,29 @@ -import { useState } from "react"; -import PageFooterButton from "../../components/order/PageFooterButton"; +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 copy from "../../../assets/icon/copy.svg"; +import useThrottle from "../../../hooks/useThrottle"; +import useModal from "../../../hooks/useModal"; import { useLocation, useNavigate, useParams } from "react-router-dom"; -import ConfirmModal from "../../components/order/ConfirmModal"; +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,6 @@ const RemittanceRequestPage = () => {

    {price}원

    -
    - {showToast && } -
    {modal.isOpen && ( navigate(`/${storeId}/order/success`)} 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/routes/Router.tsx b/apps/nowait-user/src/routes/Router.tsx index b2d1770b..6634c2ca 100644 --- a/apps/nowait-user/src/routes/Router.tsx +++ b/apps/nowait-user/src/routes/Router.tsx @@ -3,17 +3,17 @@ import HomePage from "../pages/home/HomePage"; import ReserveSuccessPage from "../pages/reserve/ReserveSuccessPage"; import MapPage from "../pages/reserve/MapPage"; import StoreDetailPage from "../pages/reserve/StoreDetailPage"; -import RedirectToStorePage from "../pages/order/RedirectToStorePage"; -import StorePage from "../pages/order/StorePage"; -import AddMenuPage from "../pages/order/AddMenuPage"; -import OrderListPage from "../pages/order/OrderListPage"; -import OrderSuccessPage from "../pages/order/OrderSuccessPage"; -import RemittanceRequestPage from "../pages/order/RemittanceRequestPage"; +import RedirectToStorePage from "../pages/order/home/RedirectToStorePage"; +import StorePage from "../pages/order/home/StorePage"; +import AddMenuPage from "../pages/order/addMenu/AddMenuPage"; +import OrderListPage from "../pages/order/orderList/OrderListPage"; +import OrderSuccessPage from "../pages/order/orderSuccess/OrderSuccessPage"; +import RemittanceRequestPage from "../pages/order/remittance/RemittanceRequestPage"; import StoreReservePage from "../pages/reserve/StoreReservePage"; import LoginPage from "../pages/login/LoginPage"; import KakaoRedirectHandler from "../pages/login/KakaoRedirectHandler"; import AuthGuard from "../components/AuthGuard"; -import PayerNameInput from "../pages/order/PayerNameInput"; +import PayerNameInput from "../pages/order/payer/PayerNameInput"; // AuthGuard로 래핑하는 헬퍼 함수 const withAuth = (Component: React.ComponentType) => ( 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"