diff --git a/apps/customer/src/app/(tabs)/main/_components/MainListView.tsx b/apps/customer/src/app/(tabs)/main/_components/MainListView.tsx index 5a8347f..1b1f567 100644 --- a/apps/customer/src/app/(tabs)/main/_components/MainListView.tsx +++ b/apps/customer/src/app/(tabs)/main/_components/MainListView.tsx @@ -73,7 +73,7 @@ export default function MainListView() { onCategoryClick={handleCategoryClick} /> -
+
{filteredAndSortedItems.map((item) => ( ))} diff --git a/apps/customer/src/app/(tabs)/main/store/[id]/purchase/_components/PurchaseCompleteModal.tsx b/apps/customer/src/app/(tabs)/main/store/[id]/purchase/_components/PurchaseCompleteModal.tsx new file mode 100644 index 0000000..3e48053 --- /dev/null +++ b/apps/customer/src/app/(tabs)/main/store/[id]/purchase/_components/PurchaseCompleteModal.tsx @@ -0,0 +1,107 @@ +"use client"; + +import { useRouter } from "next/navigation"; +import { Button, Header, Icon } from "@compasser/design-system"; +import type { StoreDetailItem, StoreMenuItem } from "../../../_types/store-detail"; + +interface PurchaseCompleteModalProps { + isOpen: boolean; + store: StoreDetailItem; + menu: StoreMenuItem; + onClose?: () => void; +} + +const formatPrice = (price: number) => `${price.toLocaleString()}원`; + +export default function PurchaseCompleteModal({ + isOpen, + store, + menu, +}: PurchaseCompleteModalProps) { + const router = useRouter(); + + if (!isOpen) return null; + + const handleConfirm = () => { + router.push("/main"); + }; + + return ( +
+
+
+ +
+
+
+ + +

+ 결제가 완료되었어요. +

+ +

+ 픽업 시간에 맞춰 상품을 수령해주세요! +

+
+ +
+ +
+
+
+ +

+ {store.storeName} +

+
+
+ +
+
+
+ +
+ +
+

{menu.name}

+ +

+ 픽업시간: {menu.pickupStartTime} ~ {menu.pickupEndTime} +

+ +

+ {formatPrice(menu.price)} +

+ +
+ + 결제 완료 + +
+
+
+
+
+ +
+ +
+
+
+
+
+ ); +} \ No newline at end of file diff --git a/apps/customer/src/app/(tabs)/main/store/[id]/purchase/_components/PurchaseContent.tsx b/apps/customer/src/app/(tabs)/main/store/[id]/purchase/_components/PurchaseContent.tsx new file mode 100644 index 0000000..24b8b7f --- /dev/null +++ b/apps/customer/src/app/(tabs)/main/store/[id]/purchase/_components/PurchaseContent.tsx @@ -0,0 +1,124 @@ +"use client"; + +import { useMemo, useState } from "react"; +import { useRouter } from "next/navigation"; +import { Button, Header } from "@compasser/design-system"; +import type { StoreDetailItem, StoreMenuItem } from "../../../_types/store-detail"; +import PurchaseGuideModal from "./PurchaseGuideModal"; +import PurchaseInfoSection from "./PurchaseInfoSection"; +import PurchaseNoticeCard from "./PurchaseNoticeCard"; +import PurchaseOrderCard from "./PurchaseOrderCard"; +import PurchaseCompleteModal from "./PurchaseCompleteModal"; + +interface PurchaseContentProps { + store: StoreDetailItem; + menu: StoreMenuItem; +} + +const ACCOUNT_INFO = { + bankName: "나무은행", + accountNumber: "123-4567-891011", + depositor: "000", +}; + +const NOTICE_LIST = [ + "결제 정보를 확인하고 이체를 진행해주세요.", + "10분 이내로 입금을 완료해주세요.", + "픽업 시간에 맞춰 상품을 수령해주세요.", +]; + +export default function PurchaseContent({ + store, + menu, +}: PurchaseContentProps) { + const router = useRouter(); + const [count, setCount] = useState(1); + const [isGuideModalOpen, setIsGuideModalOpen] = useState(false); + const [isCompleteModalOpen, setIsCompleteModalOpen] = useState(false); + + const totalPrice = useMemo(() => menu.price * count, [menu.price, count]); + + const handleDecrease = () => { + setCount((prev) => Math.max(prev - 1, 0)); + }; + + const handleIncrease = () => { + setCount((prev) => Math.min(prev + 1, menu.remainingCount)); + }; + + const handleOpenGuideModal = () => { + if (count === 0) return; + setIsGuideModalOpen(true); + }; + + const handleCloseGuideModal = () => { + setIsGuideModalOpen(false); + }; + + const handleCompleteTransfer = () => { + setIsGuideModalOpen(false); + setIsCompleteModalOpen(true); + }; + + const handleCloseCompleteModal = () => { + setIsCompleteModalOpen(false); + }; + + return ( + <> +
+
router.back()} + /> + +
+
+ + + + + + + +
+
+
+ + + + + + ); +} \ No newline at end of file diff --git a/apps/customer/src/app/(tabs)/main/store/[id]/purchase/_components/PurchaseGuideModal.tsx b/apps/customer/src/app/(tabs)/main/store/[id]/purchase/_components/PurchaseGuideModal.tsx new file mode 100644 index 0000000..ca232e4 --- /dev/null +++ b/apps/customer/src/app/(tabs)/main/store/[id]/purchase/_components/PurchaseGuideModal.tsx @@ -0,0 +1,75 @@ +"use client"; + +import { Button, Icon } from "@compasser/design-system"; + +interface PurchaseGuideModalProps { + isOpen: boolean; + accountText: string; + onClose: () => void; + onConfirm: () => void; +} + +export default function PurchaseGuideModal({ + isOpen, + accountText, + onClose, + onConfirm, +}: PurchaseGuideModalProps) { + if (!isOpen) return null; + + return ( +
+
+
+ +
+ +
+
+ +

+ 카카오 계좌이체 안내 +

+
+ +
+

+ 카카오 계좌이체로 결제가 진행됩니다. +

+

+ 아래의 계좌로 계좌이체를 진행해주세요. +

+
+ +

{accountText}

+ + +
+
+
+ ); +} \ No newline at end of file diff --git a/apps/customer/src/app/(tabs)/main/store/[id]/purchase/_components/PurchaseInfoSection.tsx b/apps/customer/src/app/(tabs)/main/store/[id]/purchase/_components/PurchaseInfoSection.tsx new file mode 100644 index 0000000..0712d6f --- /dev/null +++ b/apps/customer/src/app/(tabs)/main/store/[id]/purchase/_components/PurchaseInfoSection.tsx @@ -0,0 +1,40 @@ +"use client"; + +interface PurchaseInfoSectionProps { + bankName: string; + accountNumber: string; + depositor: string; + totalPrice: number; +} + +export default function PurchaseInfoSection({ + bankName, + accountNumber, + depositor, + totalPrice, +}: PurchaseInfoSectionProps) { + return ( +
+

결제 정보

+ +
+ +
+
+

계좌정보

+

예금주명

+

총 결제금액

+
+ +
+

+ {bankName} + {accountNumber} +

+

{depositor}

+

{totalPrice.toLocaleString()}

+
+
+
+ ); +} \ No newline at end of file diff --git a/apps/customer/src/app/(tabs)/main/store/[id]/purchase/_components/PurchaseItemCard.tsx b/apps/customer/src/app/(tabs)/main/store/[id]/purchase/_components/PurchaseItemCard.tsx new file mode 100644 index 0000000..e69de29 diff --git a/apps/customer/src/app/(tabs)/main/store/[id]/purchase/_components/PurchaseNoticeCard.tsx b/apps/customer/src/app/(tabs)/main/store/[id]/purchase/_components/PurchaseNoticeCard.tsx new file mode 100644 index 0000000..7327c22 --- /dev/null +++ b/apps/customer/src/app/(tabs)/main/store/[id]/purchase/_components/PurchaseNoticeCard.tsx @@ -0,0 +1,27 @@ +"use client"; + +import { Card, Icon } from "@compasser/design-system"; + +interface PurchaseNoticeCardProps { + noticeList: string[]; +} + +export default function PurchaseNoticeCard({ + noticeList, +}: PurchaseNoticeCardProps) { + return ( + + {noticeList.map((text) => ( +
+
+ +
+

{text}

+
+ ))} +
+ ); +} \ No newline at end of file diff --git a/apps/customer/src/app/(tabs)/main/store/[id]/purchase/_components/PurchaseOrderCard.tsx b/apps/customer/src/app/(tabs)/main/store/[id]/purchase/_components/PurchaseOrderCard.tsx new file mode 100644 index 0000000..51e8126 --- /dev/null +++ b/apps/customer/src/app/(tabs)/main/store/[id]/purchase/_components/PurchaseOrderCard.tsx @@ -0,0 +1,105 @@ +"use client"; + +import { Card, Icon } from "@compasser/design-system"; +import type { StoreDetailItem, StoreMenuItem } from "../../../_types/store-detail"; + +interface PurchaseOrderCardProps { + store: StoreDetailItem; + menu: StoreMenuItem; + count: number; + totalPrice: number; + onDecrease: () => void; + onIncrease: () => void; +} + +const formatPrice = (price: number) => `${price.toLocaleString()}원`; + +export default function PurchaseOrderCard({ + store, + menu, + count, + totalPrice, + onDecrease, + onIncrease, +}: PurchaseOrderCardProps) { + return ( + +
+
+ +

{store.storeName}

+
+
+ +
+
+
+ +
+ +
+

{menu.name}

+ +

+ 픽업시간: {menu.pickupStartTime} ~ {menu.pickupEndTime} +

+ +
+
+ + + {count} + + +
+ + + {formatPrice(menu.price)} + +
+
+
+
+ +
+
+

총 수량 {count}개

+

{formatPrice(totalPrice)}

+
+
+
+ ); +} \ No newline at end of file diff --git a/apps/customer/src/app/(tabs)/main/store/[id]/purchase/page.tsx b/apps/customer/src/app/(tabs)/main/store/[id]/purchase/page.tsx new file mode 100644 index 0000000..c5c74e5 --- /dev/null +++ b/apps/customer/src/app/(tabs)/main/store/[id]/purchase/page.tsx @@ -0,0 +1,37 @@ +import { notFound } from "next/navigation"; +import PurchaseContent from "./_components/PurchaseContent"; +import { MOCK_MAIN_STORE_DETAIL_MAP } from "../../_constants/mockStoreDetail"; + +interface PurchasePageProps { + params: Promise<{ + id: string; + }>; + searchParams: Promise<{ + menuId?: string; + }>; +} + +export default async function PurchasePage({ + params, + searchParams, +}: PurchasePageProps) { + const { id } = await params; + const { menuId } = await searchParams; + + const storeId = Number(id); + const selectedMenuId = Number(menuId); + + const store = MOCK_MAIN_STORE_DETAIL_MAP[storeId]; + + if (!store || !selectedMenuId) { + notFound(); + } + + const menu = store.menus.find((item) => item.id === selectedMenuId); + + if (!menu) { + notFound(); + } + + return ; +} \ No newline at end of file diff --git a/apps/customer/src/app/(tabs)/main/store/_components/StoreDetailContent.tsx b/apps/customer/src/app/(tabs)/main/store/_components/StoreDetailContent.tsx index 8b0b118..e339987 100644 --- a/apps/customer/src/app/(tabs)/main/store/_components/StoreDetailContent.tsx +++ b/apps/customer/src/app/(tabs)/main/store/_components/StoreDetailContent.tsx @@ -1,6 +1,7 @@ "use client"; import { useMemo, useState } from "react"; +import { useRouter } from "next/navigation"; import { Icon } from "@compasser/design-system"; import StoreMenuCard from "./StoreMenuCard"; import type { DayKey, StoreDetailItem } from "../_types/store-detail"; @@ -43,6 +44,7 @@ const getTodayDayKeyInKorea = (): DayKey => { export default function StoreDetailContent({ store, }: StoreDetailContentProps) { + const router = useRouter(); const [isHoursOpen, setIsHoursOpen] = useState(false); const todayKey = useMemo(() => getTodayDayKeyInKorea(), []); @@ -50,6 +52,10 @@ export default function StoreDetailContent({ const currentBusinessText = `영업중 ${todayHours.open} ~ ${todayHours.close}`; + const handleMenuClick = (menuId: number) => { + router.push(`/main/store/${store.id}/purchase?menuId=${menuId}`); + }; + return (
@@ -60,7 +66,7 @@ export default function StoreDetailContent({ />
-
+

{store.storeName}

@@ -168,7 +174,11 @@ export default function StoreDetailContent({
{store.menus.map((menu) => ( - + handleMenuClick(menu.id)} + /> ))}
diff --git a/apps/customer/src/app/(tabs)/main/store/_components/StoreMenuCard.tsx b/apps/customer/src/app/(tabs)/main/store/_components/StoreMenuCard.tsx index 8c5469b..e466516 100644 --- a/apps/customer/src/app/(tabs)/main/store/_components/StoreMenuCard.tsx +++ b/apps/customer/src/app/(tabs)/main/store/_components/StoreMenuCard.tsx @@ -5,36 +5,46 @@ import type { StoreMenuItem } from "../_types/store-detail"; interface StoreMenuCardProps { item: StoreMenuItem; + onClick?: () => void; } const formatPrice = (price: number) => `${price.toLocaleString()}원`; -export default function StoreMenuCard({ item }: StoreMenuCardProps) { +export default function StoreMenuCard({ + item, + onClick, +}: StoreMenuCardProps) { return ( - -
-
- -
+ ); } \ No newline at end of file diff --git a/apps/customer/src/app/(tabs)/order/_constants/mockOrders.ts b/apps/customer/src/app/(tabs)/order/_constants/mockOrders.ts index ce839de..81d5997 100644 --- a/apps/customer/src/app/(tabs)/order/_constants/mockOrders.ts +++ b/apps/customer/src/app/(tabs)/order/_constants/mockOrders.ts @@ -15,14 +15,28 @@ export const IN_PROGRESS_ORDERS: OrderItem[] = [ totalPrice: "15,000원", pickupTime: "2/27 19:00 ~ 20:00", }, + { + id: 3, + storeName: "별동네 베이커리카페 별내본점", + orderSummary: "랜덤박스 Level.1 3개", + totalPrice: "15,000원", + pickupTime: "2/27 19:00 ~ 20:00", + }, + { + id: 4, + storeName: "별동네 베이커리카페 별내본점", + orderSummary: "랜덤박스 Level.1 3개", + totalPrice: "15,000원", + pickupTime: "2/27 19:00 ~ 20:00", + }, ]; export const DONE_ORDERS: OrderItem[] = [ { - id: 3, + id: 5, storeName: "별동네 베이커리카페 별내본점", orderSummary: "랜덤박스 Level.1 1개", totalPrice: "5,000원", pickupTime: "2/28 18:00 ~ 19:00", }, -]; \ No newline at end of file +]; diff --git a/apps/customer/src/app/(tabs)/order/page.tsx b/apps/customer/src/app/(tabs)/order/page.tsx index 420d34a..a7512f4 100644 --- a/apps/customer/src/app/(tabs)/order/page.tsx +++ b/apps/customer/src/app/(tabs)/order/page.tsx @@ -25,7 +25,7 @@ export default function OrderPage() { onTabChange={(key) => setActiveTab(key as OrderTabKey)} /> -
+
{orders.length > 0 ? (
{orders.map((order) => ( diff --git a/packages/design-system/src/icons/generated/iconNames.ts b/packages/design-system/src/icons/generated/iconNames.ts index eeada31..942fb9b 100644 --- a/packages/design-system/src/icons/generated/iconNames.ts +++ b/packages/design-system/src/icons/generated/iconNames.ts @@ -3,13 +3,16 @@ export const iconNames = [ "Alarm", "BackButton", "Camera", + "Check", "ChevronDown", "Clock", "CloseButton", "CustomerSelect", "Gift", + "GiftOpen", "Home", "KakaoLogo", + "KakaoPay", "ListIcon", "LocationIcon", "LogoText", @@ -17,11 +20,13 @@ export const iconNames = [ "MapIcon", "MapPin", "Menu", + "Minus", "My", "NextButton", "Notice", "Order", "OwnerSelect", + "Plus", "ProfileCharacter", "RadioActive", "RadioDeactive", diff --git a/packages/design-system/src/icons/generated/spriteSymbols.ts b/packages/design-system/src/icons/generated/spriteSymbols.ts index 7964c06..bda40e4 100644 --- a/packages/design-system/src/icons/generated/spriteSymbols.ts +++ b/packages/design-system/src/icons/generated/spriteSymbols.ts @@ -1,2 +1,2 @@ // 이 파일은 자동 생성 파일입니다. (직접 수정 금지) -export const spriteSymbols = ""; +export const spriteSymbols = ""; diff --git a/packages/design-system/src/icons/source/Check.svg b/packages/design-system/src/icons/source/Check.svg new file mode 100644 index 0000000..9dcc51a --- /dev/null +++ b/packages/design-system/src/icons/source/Check.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/design-system/src/icons/source/GiftOpen.svg b/packages/design-system/src/icons/source/GiftOpen.svg new file mode 100644 index 0000000..6858989 --- /dev/null +++ b/packages/design-system/src/icons/source/GiftOpen.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/design-system/src/icons/source/KakaoPay.svg b/packages/design-system/src/icons/source/KakaoPay.svg new file mode 100644 index 0000000..2b49efb --- /dev/null +++ b/packages/design-system/src/icons/source/KakaoPay.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/design-system/src/icons/source/Minus.svg b/packages/design-system/src/icons/source/Minus.svg new file mode 100644 index 0000000..b286029 --- /dev/null +++ b/packages/design-system/src/icons/source/Minus.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/design-system/src/icons/source/Plus.svg b/packages/design-system/src/icons/source/Plus.svg new file mode 100644 index 0000000..e35e2da --- /dev/null +++ b/packages/design-system/src/icons/source/Plus.svg @@ -0,0 +1 @@ + \ No newline at end of file