+
{children}
diff --git a/apps/customer/src/app/(tabs)/main/store/[id]/page.tsx b/apps/customer/src/app/(tabs)/main/store/[id]/page.tsx
new file mode 100644
index 0000000..60ad0eb
--- /dev/null
+++ b/apps/customer/src/app/(tabs)/main/store/[id]/page.tsx
@@ -0,0 +1,23 @@
+import { notFound } from "next/navigation";
+import StoreDetailContent from "../_components/StoreDetailContent";
+import { MOCK_MAIN_STORE_DETAIL_MAP } from "../_constants/mockStoreDetail";
+
+interface StoreDetailPageProps {
+ params: Promise<{
+ id: string;
+ }>;
+}
+
+export default async function StoreDetailPage({
+ params,
+}: StoreDetailPageProps) {
+ const { id } = await params;
+ const storeId = Number(id);
+ const store = MOCK_MAIN_STORE_DETAIL_MAP[storeId];
+
+ if (!store) {
+ 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
new file mode 100644
index 0000000..8b0b118
--- /dev/null
+++ b/apps/customer/src/app/(tabs)/main/store/_components/StoreDetailContent.tsx
@@ -0,0 +1,179 @@
+"use client";
+
+import { useMemo, useState } from "react";
+import { Icon } from "@compasser/design-system";
+import StoreMenuCard from "./StoreMenuCard";
+import type { DayKey, StoreDetailItem } from "../_types/store-detail";
+
+interface StoreDetailContentProps {
+ store: StoreDetailItem;
+}
+
+const DAY_LABEL: Record
= {
+ mon: "월",
+ tue: "화",
+ wed: "수",
+ thu: "목",
+ fri: "금",
+ sat: "토",
+ sun: "일",
+};
+
+const DAY_ORDER: DayKey[] = ["sun", "mon", "tue", "wed", "thu", "fri", "sat"];
+
+const getTodayDayKeyInKorea = (): DayKey => {
+ const weekday = new Intl.DateTimeFormat("en-US", {
+ weekday: "short",
+ timeZone: "Asia/Seoul",
+ }).format(new Date());
+
+ const map: Record = {
+ Mon: "mon",
+ Tue: "tue",
+ Wed: "wed",
+ Thu: "thu",
+ Fri: "fri",
+ Sat: "sat",
+ Sun: "sun",
+ };
+
+ return map[weekday];
+};
+
+export default function StoreDetailContent({
+ store,
+}: StoreDetailContentProps) {
+ const [isHoursOpen, setIsHoursOpen] = useState(false);
+
+ const todayKey = useMemo(() => getTodayDayKeyInKorea(), []);
+ const todayHours = store.businessHours[todayKey];
+
+ const currentBusinessText = `영업중 ${todayHours.open} ~ ${todayHours.close}`;
+
+ return (
+
+
+

+
+
+
+
+
{store.storeName}
+
+
+
+
+
+
+
+
+
{store.roadAddress}
+
{store.lotAddress}
+
+
+
+
+
+
+
+
+
+
+
+
+
{currentBusinessText}
+
+
+
+
+
+ {isHoursOpen && (
+
+ {DAY_ORDER.map((day) => {
+ const hour = store.businessHours[day];
+ const isToday = day === todayKey;
+
+ return (
+
+
+ {DAY_LABEL[day]}
+
+
+ {hour.open} ~ {hour.close}
+
+
+ );
+ })}
+
+ )}
+
+
+
+
+
+
+
메뉴
+
+
+
+ {store.menus.map((menu) => (
+
+ ))}
+
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/apps/customer/src/app/(tabs)/main/store/_components/StoreMenuCard.tsx b/apps/customer/src/app/(tabs)/main/store/_components/StoreMenuCard.tsx
new file mode 100644
index 0000000..8c5469b
--- /dev/null
+++ b/apps/customer/src/app/(tabs)/main/store/_components/StoreMenuCard.tsx
@@ -0,0 +1,40 @@
+"use client";
+
+import { Card, Icon } from "@compasser/design-system";
+import type { StoreMenuItem } from "../_types/store-detail";
+
+interface StoreMenuCardProps {
+ item: StoreMenuItem;
+}
+
+const formatPrice = (price: number) => `${price.toLocaleString()}원`;
+
+export default function StoreMenuCard({ item }: StoreMenuCardProps) {
+ return (
+
+
+
+
+
+
+
+
{item.name}
+
+
+ 잔여개수 {item.remainingCount}개
+
+
+
+ 픽업시간: {item.pickupStartTime} ~ {item.pickupEndTime}
+
+
+
+
+ {formatPrice(item.price)}
+
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/apps/customer/src/app/(tabs)/main/store/_constants/mockStoreDetail.ts b/apps/customer/src/app/(tabs)/main/store/_constants/mockStoreDetail.ts
new file mode 100644
index 0000000..45bd561
--- /dev/null
+++ b/apps/customer/src/app/(tabs)/main/store/_constants/mockStoreDetail.ts
@@ -0,0 +1,360 @@
+import type { DayKey, StoreBusinessHour, StoreDetailItem } from "../_types/store-detail";
+
+const createWeeklyHours = (
+ defaultOpen: string,
+ defaultClose: string,
+ overrides?: Partial>,
+): Record => ({
+ mon: { open: defaultOpen, close: defaultClose },
+ tue: { open: defaultOpen, close: defaultClose },
+ wed: { open: defaultOpen, close: defaultClose },
+ thu: { open: defaultOpen, close: defaultClose },
+ fri: { open: defaultOpen, close: defaultClose },
+ sat: { open: defaultOpen, close: defaultClose },
+ sun: { open: defaultOpen, close: defaultClose },
+ ...overrides,
+});
+
+export const MOCK_MAIN_STORE_DETAIL_LIST: StoreDetailItem[] = [
+ {
+ id: 1,
+ storeName: "가톨릭대 정문 브레드하우스",
+ roadAddress: "경기 부천시 지봉로 43",
+ lotAddress: "경기 부천시 역곡동 543-1",
+ email: "breadhouse@gmail.com",
+ thumbnailImageUrl: "/images/mock/store/store-1.jpg",
+ businessHours: createWeeklyHours("09:00", "18:00", {
+ sat: { open: "09:00", close: "17:00" },
+ sun: { open: "10:00", close: "17:00" },
+ }),
+ menus: [
+ {
+ id: 101,
+ name: "딸기 생크림 박스",
+ remainingCount: 3,
+ pickupStartTime: "19:00",
+ pickupEndTime: "21:00",
+ originalPrice: 12000,
+ price: 5500,
+ imageUrl: "/images/mock/menu/menu-1-1.jpg",
+ },
+ {
+ id: 102,
+ name: "소금빵 랜덤박스",
+ remainingCount: 2,
+ pickupStartTime: "19:30",
+ pickupEndTime: "21:00",
+ originalPrice: 11000,
+ price: 5000,
+ imageUrl: "/images/mock/menu/menu-1-2.jpg",
+ },
+ {
+ id: 103,
+ name: "크루아상 4종 세트",
+ remainingCount: 1,
+ pickupStartTime: "18:30",
+ pickupEndTime: "20:00",
+ originalPrice: 15000,
+ price: 7000,
+ imageUrl: "/images/mock/menu/menu-1-3.jpg",
+ },
+ ],
+ },
+ {
+ id: 2,
+ storeName: "부천역 카페 오븐데이",
+ roadAddress: "경기 부천시 부일로 460",
+ lotAddress: "경기 부천시 심곡동 221-9",
+ email: "ovenday@gmail.com",
+ thumbnailImageUrl: "/images/mock/store/store-1.jpg",
+ businessHours: createWeeklyHours("10:00", "20:00", {
+ sun: { open: "11:00", close: "19:00" },
+ }),
+ menus: [
+ {
+ id: 201,
+ name: "수제 쿠키 랜덤박스",
+ remainingCount: 3,
+ pickupStartTime: "18:30",
+ pickupEndTime: "20:30",
+ originalPrice: 9000,
+ price: 4000,
+ imageUrl: "/images/mock/menu/menu-2-1.jpg",
+ },
+ {
+ id: 202,
+ name: "스콘 디저트팩",
+ remainingCount: 2,
+ pickupStartTime: "19:00",
+ pickupEndTime: "20:30",
+ originalPrice: 10000,
+ price: 4500,
+ imageUrl: "/images/mock/menu/menu-2-2.jpg",
+ },
+ ],
+ },
+ {
+ id: 3,
+ storeName: "역곡 브런치살롱",
+ roadAddress: "경기 부천시 역곡로 8",
+ lotAddress: "경기 부천시 역곡동 112-4",
+ email: "brunchsalon@gmail.com",
+ thumbnailImageUrl: "/images/mock/store/store-1.jpg",
+ businessHours: createWeeklyHours("09:30", "19:30"),
+ menus: [
+ {
+ id: 301,
+ name: "샌드위치 브런치팩",
+ remainingCount: 4,
+ pickupStartTime: "10:00",
+ pickupEndTime: "13:00",
+ originalPrice: 13500,
+ price: 6900,
+ imageUrl: "/images/mock/menu/menu-3-1.jpg",
+ },
+ {
+ id: 302,
+ name: "샐러드 브런치 세트",
+ remainingCount: 2,
+ pickupStartTime: "11:00",
+ pickupEndTime: "13:30",
+ originalPrice: 14000,
+ price: 7500,
+ imageUrl: "/images/mock/menu/menu-3-2.jpg",
+ },
+ ],
+ },
+ {
+ id: 4,
+ storeName: "역곡역 앞 솔트베이크",
+ roadAddress: "경기 부천시 역곡로 3",
+ lotAddress: "경기 부천시 역곡동 98-7",
+ email: "saltbake@gmail.com",
+ thumbnailImageUrl: "/images/mock/store/store-1.jpg",
+ businessHours: createWeeklyHours("08:30", "19:30"),
+ menus: [
+ {
+ id: 401,
+ name: "소금빵 4종 박스",
+ remainingCount: 3,
+ pickupStartTime: "17:00",
+ pickupEndTime: "20:00",
+ originalPrice: 13000,
+ price: 6000,
+ imageUrl: "/images/mock/menu/menu-4-1.jpg",
+ },
+ {
+ id: 402,
+ name: "베이커리 랜덤팩",
+ remainingCount: 1,
+ pickupStartTime: "18:00",
+ pickupEndTime: "19:30",
+ originalPrice: 14000,
+ price: 6500,
+ imageUrl: "/images/mock/menu/menu-4-2.jpg",
+ },
+ ],
+ },
+ {
+ id: 5,
+ storeName: "가톨릭대 후문 모어커피",
+ roadAddress: "경기 부천시 지봉로 50",
+ lotAddress: "경기 부천시 역곡동 601-12",
+ email: "morecoffee@gmail.com",
+ thumbnailImageUrl: "/images/mock/store/store-1.jpg",
+ businessHours: createWeeklyHours("09:00", "22:00"),
+ menus: [
+ {
+ id: 501,
+ name: "오늘의 디저트팩",
+ remainingCount: 5,
+ pickupStartTime: "20:00",
+ pickupEndTime: "22:00",
+ originalPrice: 9500,
+ price: 4500,
+ imageUrl: "/images/mock/menu/menu-5-1.jpg",
+ },
+ {
+ id: 502,
+ name: "쿠키+음료 세트",
+ remainingCount: 2,
+ pickupStartTime: "19:30",
+ pickupEndTime: "21:30",
+ originalPrice: 11000,
+ price: 5200,
+ imageUrl: "/images/mock/menu/menu-5-2.jpg",
+ },
+ ],
+ },
+ {
+ id: 6,
+ storeName: "부천 디저트랩",
+ roadAddress: "경기 부천시 부일로 448",
+ lotAddress: "경기 부천시 심곡동 203-15",
+ email: "dessertlab@gmail.com",
+ thumbnailImageUrl: "/images/mock/store/store-1.jpg",
+ businessHours: createWeeklyHours("12:00", "21:00"),
+ menus: [
+ {
+ id: 601,
+ name: "마카롱 혼합 박스",
+ remainingCount: 2,
+ pickupStartTime: "18:00",
+ pickupEndTime: "21:00",
+ originalPrice: 15000,
+ price: 7000,
+ imageUrl: "/images/mock/menu/menu-6-1.jpg",
+ },
+ {
+ id: 602,
+ name: "케이크 조각 세트",
+ remainingCount: 1,
+ pickupStartTime: "19:00",
+ pickupEndTime: "20:30",
+ originalPrice: 16000,
+ price: 7500,
+ imageUrl: "/images/mock/menu/menu-6-2.jpg",
+ },
+ ],
+ },
+ {
+ id: 7,
+ storeName: "역곡 한끼식당",
+ roadAddress: "경기 부천시 역곡로 22",
+ lotAddress: "경기 부천시 역곡동 231-6",
+ email: "hankki@gmail.com",
+ thumbnailImageUrl: "/images/mock/store/store-1.jpg",
+ businessHours: createWeeklyHours("10:30", "20:30"),
+ menus: [
+ {
+ id: 701,
+ name: "한식 도시락 세트",
+ remainingCount: 4,
+ pickupStartTime: "13:00",
+ pickupEndTime: "15:00",
+ originalPrice: 12000,
+ price: 6500,
+ imageUrl: "/images/mock/menu/menu-7-1.jpg",
+ },
+ {
+ id: 702,
+ name: "반찬 랜덤팩",
+ remainingCount: 2,
+ pickupStartTime: "18:30",
+ pickupEndTime: "20:00",
+ originalPrice: 10000,
+ price: 5000,
+ imageUrl: "/images/mock/menu/menu-7-2.jpg",
+ },
+ ],
+ },
+ {
+ id: 8,
+ storeName: "가톨릭대 베이글스팟",
+ roadAddress: "경기 부천시 지봉로 59",
+ lotAddress: "경기 부천시 역곡동 614-8",
+ email: "bagelspot@gmail.com",
+ thumbnailImageUrl: "/images/mock/store/store-1.jpg",
+ businessHours: createWeeklyHours("08:00", "17:00", {
+ sun: { open: "09:00", close: "16:00" },
+ }),
+ menus: [
+ {
+ id: 801,
+ name: "베이글 랜덤팩",
+ remainingCount: 3,
+ pickupStartTime: "16:00",
+ pickupEndTime: "19:00",
+ originalPrice: 11000,
+ price: 5200,
+ imageUrl: "/images/mock/menu/menu-8-1.jpg",
+ },
+ {
+ id: 802,
+ name: "크림치즈 베이글 세트",
+ remainingCount: 2,
+ pickupStartTime: "15:30",
+ pickupEndTime: "17:30",
+ originalPrice: 12500,
+ price: 5800,
+ imageUrl: "/images/mock/menu/menu-8-2.jpg",
+ },
+ ],
+ },
+ {
+ id: 9,
+ storeName: "부천 로컬키친",
+ roadAddress: "경기 부천시 부일로 472",
+ lotAddress: "경기 부천시 심곡동 227-2",
+ email: "localkitchen@gmail.com",
+ thumbnailImageUrl: "/images/mock/store/store-1.jpg",
+ businessHours: createWeeklyHours("11:00", "21:00"),
+ menus: [
+ {
+ id: 901,
+ name: "파스타 투고 박스",
+ remainingCount: 2,
+ pickupStartTime: "11:30",
+ pickupEndTime: "14:00",
+ originalPrice: 15000,
+ price: 7900,
+ imageUrl: "/images/mock/menu/menu-9-1.jpg",
+ },
+ {
+ id: 902,
+ name: "리조또 데일리팩",
+ remainingCount: 3,
+ pickupStartTime: "12:00",
+ pickupEndTime: "14:30",
+ originalPrice: 14500,
+ price: 7600,
+ imageUrl: "/images/mock/menu/menu-9-2.jpg",
+ },
+ ],
+ },
+ {
+ id: 10,
+ storeName: "역곡 스윗테이블",
+ roadAddress: "경기 부천시 역곡로 15",
+ lotAddress: "경기 부천시 역곡동 142-10",
+ email: "sweettable@gmail.com",
+ thumbnailImageUrl: "/images/mock/store/store-1.jpg",
+ businessHours: createWeeklyHours("10:00", "22:00"),
+ menus: [
+ {
+ id: 1001,
+ name: "조각케이크 랜덤팩",
+ remainingCount: 3,
+ pickupStartTime: "19:30",
+ pickupEndTime: "21:30",
+ originalPrice: 13000,
+ price: 6000,
+ imageUrl: "/images/mock/menu/menu-10-1.jpg",
+ },
+ {
+ id: 1002,
+ name: "디저트 박스 Level.2",
+ remainingCount: 1,
+ pickupStartTime: "20:00",
+ pickupEndTime: "21:30",
+ originalPrice: 16000,
+ price: 8000,
+ imageUrl: "/images/mock/menu/menu-10-2.jpg",
+ },
+ {
+ id: 1003,
+ name: "쿠키&케이크 세트",
+ remainingCount: 2,
+ pickupStartTime: "19:00",
+ pickupEndTime: "22:00",
+ originalPrice: 15000,
+ price: 7200,
+ imageUrl: "/images/mock/menu/menu-10-3.jpg",
+ },
+ ],
+ },
+];
+
+export const MOCK_MAIN_STORE_DETAIL_MAP = Object.fromEntries(
+ MOCK_MAIN_STORE_DETAIL_LIST.map((store) => [store.id, store]),
+) as Record;
\ No newline at end of file
diff --git a/apps/customer/src/app/(tabs)/main/store/_types/store-detail.ts b/apps/customer/src/app/(tabs)/main/store/_types/store-detail.ts
new file mode 100644
index 0000000..cddc6bb
--- /dev/null
+++ b/apps/customer/src/app/(tabs)/main/store/_types/store-detail.ts
@@ -0,0 +1,35 @@
+export type DayKey =
+ | "mon"
+ | "tue"
+ | "wed"
+ | "thu"
+ | "fri"
+ | "sat"
+ | "sun";
+
+export interface StoreBusinessHour {
+ open: string;
+ close: string;
+}
+
+export interface StoreMenuItem {
+ id: number;
+ name: string;
+ remainingCount: number;
+ pickupStartTime: string;
+ pickupEndTime: string;
+ price: number;
+ originalPrice?: number;
+ imageUrl: string;
+}
+
+export interface StoreDetailItem {
+ id: number;
+ storeName: string;
+ roadAddress: string;
+ lotAddress: string;
+ email: string;
+ thumbnailImageUrl: string;
+ businessHours: Record;
+ menus: StoreMenuItem[];
+}
\ No newline at end of file
diff --git a/packages/design-system/src/icons/generated/iconNames.ts b/packages/design-system/src/icons/generated/iconNames.ts
index b82fdd5..eeada31 100644
--- a/packages/design-system/src/icons/generated/iconNames.ts
+++ b/packages/design-system/src/icons/generated/iconNames.ts
@@ -16,6 +16,7 @@ export const iconNames = [
"Mail",
"MapIcon",
"MapPin",
+ "Menu",
"My",
"NextButton",
"Notice",
@@ -25,6 +26,8 @@ export const iconNames = [
"RadioActive",
"RadioDeactive",
"Stamp",
- "StoreIcon"
+ "StoreIcon",
+ "ToggleDown",
+ "ToggleUp"
] as const;
export type IconName = typeof iconNames[number];
diff --git a/packages/design-system/src/icons/generated/spriteSymbols.ts b/packages/design-system/src/icons/generated/spriteSymbols.ts
index 571eaaa..7964c06 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/Menu.svg b/packages/design-system/src/icons/source/Menu.svg
new file mode 100644
index 0000000..c345ea0
--- /dev/null
+++ b/packages/design-system/src/icons/source/Menu.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/packages/design-system/src/icons/source/ToggleDown.svg b/packages/design-system/src/icons/source/ToggleDown.svg
new file mode 100644
index 0000000..9bc0f0d
--- /dev/null
+++ b/packages/design-system/src/icons/source/ToggleDown.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/packages/design-system/src/icons/source/ToggleUp.svg b/packages/design-system/src/icons/source/ToggleUp.svg
new file mode 100644
index 0000000..de2847d
--- /dev/null
+++ b/packages/design-system/src/icons/source/ToggleUp.svg
@@ -0,0 +1 @@
+
\ No newline at end of file