+
Date: Thu, 15 Oct 2026 00:32:24 +0900
Subject: [PATCH 03/13] =?UTF-8?q?refactor:=20=ED=86=A0=EB=84=88=EB=A8=BC?=
=?UTF-8?q?=ED=8A=B8=20=EA=B8=B0=EB=A1=9D=20=ED=83=AD=20=ED=83=80=EC=9E=85?=
=?UTF-8?q?=EC=9D=84=20=5Ftypes=20=EB=A1=9C=20=EB=B6=84=EB=A6=AC?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../tournament/history/_components/TournamentHistoryList.tsx | 2 +-
.../history/_components/TournamentHistorySection.tsx | 5 +++--
.../history/_components/TournamentPlayTypeFilter.tsx | 3 ++-
.../tournament/history/_components/TournamentStatusTab.tsx | 2 +-
.../src/app/tournament/history/_consts/tournamentPlayType.ts | 4 ++--
apps/web/src/app/tournament/history/_consts/tournamentTab.ts | 2 +-
.../src/app/tournament/history/_types/tournamentPlayType.ts | 3 +++
apps/web/src/app/tournament/history/_types/tournamentTab.ts | 1 +
.../app/tournament/history/_utils/tournamentSearchParams.ts | 5 +++--
9 files changed, 17 insertions(+), 10 deletions(-)
create mode 100644 apps/web/src/app/tournament/history/_types/tournamentPlayType.ts
create mode 100644 apps/web/src/app/tournament/history/_types/tournamentTab.ts
diff --git a/apps/web/src/app/tournament/history/_components/TournamentHistoryList.tsx b/apps/web/src/app/tournament/history/_components/TournamentHistoryList.tsx
index 34f0af51..6d87f84b 100644
--- a/apps/web/src/app/tournament/history/_components/TournamentHistoryList.tsx
+++ b/apps/web/src/app/tournament/history/_components/TournamentHistoryList.tsx
@@ -7,7 +7,7 @@ import { useScrollRestoration } from '@/hooks/useScrollRestoration';
import type { TournamentPlayTypeT, TournamentStatusT } from '@/types/tournament';
import { SCROLL_NAMESPACE } from '@/utils/scrollRestoration';
-import type { TournamentStatusTabT } from '../_consts/tournamentTab';
+import type { TournamentStatusTabT } from '../_types/tournamentTab';
type Props = {
statuses: TournamentStatusT[];
diff --git a/apps/web/src/app/tournament/history/_components/TournamentHistorySection.tsx b/apps/web/src/app/tournament/history/_components/TournamentHistorySection.tsx
index eaabb1f0..f90b4c4d 100644
--- a/apps/web/src/app/tournament/history/_components/TournamentHistorySection.tsx
+++ b/apps/web/src/app/tournament/history/_components/TournamentHistorySection.tsx
@@ -6,9 +6,10 @@ import { Suspense } from 'react';
import Spacing from '@/components/spacing';
import { Z_INDEX } from '@/consts/zIndex';
-import type { TournamentPlayTypeFilterT } from '../_consts/tournamentPlayType';
import { PLAY_TYPE_BY_FILTER, PLAY_TYPE_FILTER } from '../_consts/tournamentPlayType';
-import { STATUS_BY_TAB, type TournamentStatusTabT } from '../_consts/tournamentTab';
+import { STATUS_BY_TAB } from '../_consts/tournamentTab';
+import type { TournamentPlayTypeFilterT } from '../_types/tournamentPlayType';
+import type { TournamentStatusTabT } from '../_types/tournamentTab';
import { parsePlayParam, parseTabParam } from '../_utils/tournamentSearchParams';
import TournamentHistoryList from './TournamentHistoryList';
import TournamentHistorySkeleton from './TournamentHistorySkeleton';
diff --git a/apps/web/src/app/tournament/history/_components/TournamentPlayTypeFilter.tsx b/apps/web/src/app/tournament/history/_components/TournamentPlayTypeFilter.tsx
index c07a5e72..82b0b9b5 100644
--- a/apps/web/src/app/tournament/history/_components/TournamentPlayTypeFilter.tsx
+++ b/apps/web/src/app/tournament/history/_components/TournamentPlayTypeFilter.tsx
@@ -5,7 +5,8 @@ import type { ComponentType, SVGProps } from 'react';
import { GroupIconFill, PersonIconFill } from '@/assets/icons';
import { cn } from '@/utils/cn';
-import { PLAY_TYPE_FILTER, type TournamentPlayTypeFilterT } from '../_consts/tournamentPlayType';
+import { PLAY_TYPE_FILTER } from '../_consts/tournamentPlayType';
+import type { TournamentPlayTypeFilterT } from '../_types/tournamentPlayType';
type Props = {
activeFilter: TournamentPlayTypeFilterT;
diff --git a/apps/web/src/app/tournament/history/_components/TournamentStatusTab.tsx b/apps/web/src/app/tournament/history/_components/TournamentStatusTab.tsx
index 3d1bb52a..8de4fbf7 100644
--- a/apps/web/src/app/tournament/history/_components/TournamentStatusTab.tsx
+++ b/apps/web/src/app/tournament/history/_components/TournamentStatusTab.tsx
@@ -5,7 +5,7 @@ import type { ComponentType, SVGProps } from 'react';
import { BasketIconOutline, ReceiptIconOutline } from '@/assets/icons';
import { cn } from '@/utils/cn';
-import type { TournamentStatusTabT } from '../_consts/tournamentTab';
+import type { TournamentStatusTabT } from '../_types/tournamentTab';
type Props = {
activeTab: TournamentStatusTabT;
diff --git a/apps/web/src/app/tournament/history/_consts/tournamentPlayType.ts b/apps/web/src/app/tournament/history/_consts/tournamentPlayType.ts
index c2ac90e9..8d33a1c7 100644
--- a/apps/web/src/app/tournament/history/_consts/tournamentPlayType.ts
+++ b/apps/web/src/app/tournament/history/_consts/tournamentPlayType.ts
@@ -1,14 +1,14 @@
import { TOURNAMENT_PLAY_TYPE } from '@/consts/tournament';
import type { TournamentPlayTypeT } from '@/types/tournament';
+import type { TournamentPlayTypeFilterT } from '../_types/tournamentPlayType';
+
export const PLAY_TYPE_FILTER = {
ALL: 'all',
SOLO: 'solo',
SOCIAL: 'social',
} as const;
-export type TournamentPlayTypeFilterT = (typeof PLAY_TYPE_FILTER)[keyof typeof PLAY_TYPE_FILTER];
-
/** NOTE: '전체'는 의도적으로 비워둔다 — 조회 시 playType 파라미터 자체가 빠진다. */
export const PLAY_TYPE_BY_FILTER: Partial> =
{
diff --git a/apps/web/src/app/tournament/history/_consts/tournamentTab.ts b/apps/web/src/app/tournament/history/_consts/tournamentTab.ts
index 4aa4f6e2..e5144755 100644
--- a/apps/web/src/app/tournament/history/_consts/tournamentTab.ts
+++ b/apps/web/src/app/tournament/history/_consts/tournamentTab.ts
@@ -1,7 +1,7 @@
import { TOURNAMENT_STATUS } from '@/consts/tournament';
import type { TournamentStatusT } from '@/types/tournament';
-export type TournamentStatusTabT = 'ongoing' | 'completed';
+import type { TournamentStatusTabT } from '../_types/tournamentTab';
/**
* 탭 → 조회할 토너먼트 상태 매핑
diff --git a/apps/web/src/app/tournament/history/_types/tournamentPlayType.ts b/apps/web/src/app/tournament/history/_types/tournamentPlayType.ts
new file mode 100644
index 00000000..51cba50e
--- /dev/null
+++ b/apps/web/src/app/tournament/history/_types/tournamentPlayType.ts
@@ -0,0 +1,3 @@
+import type { PLAY_TYPE_FILTER } from '../_consts/tournamentPlayType';
+
+export type TournamentPlayTypeFilterT = (typeof PLAY_TYPE_FILTER)[keyof typeof PLAY_TYPE_FILTER];
diff --git a/apps/web/src/app/tournament/history/_types/tournamentTab.ts b/apps/web/src/app/tournament/history/_types/tournamentTab.ts
new file mode 100644
index 00000000..933bc628
--- /dev/null
+++ b/apps/web/src/app/tournament/history/_types/tournamentTab.ts
@@ -0,0 +1 @@
+export type TournamentStatusTabT = 'ongoing' | 'completed';
diff --git a/apps/web/src/app/tournament/history/_utils/tournamentSearchParams.ts b/apps/web/src/app/tournament/history/_utils/tournamentSearchParams.ts
index 5cae3f88..ca1620a8 100644
--- a/apps/web/src/app/tournament/history/_utils/tournamentSearchParams.ts
+++ b/apps/web/src/app/tournament/history/_utils/tournamentSearchParams.ts
@@ -1,5 +1,6 @@
-import { PLAY_TYPE_FILTER, type TournamentPlayTypeFilterT } from '../_consts/tournamentPlayType';
-import type { TournamentStatusTabT } from '../_consts/tournamentTab';
+import { PLAY_TYPE_FILTER } from '../_consts/tournamentPlayType';
+import type { TournamentPlayTypeFilterT } from '../_types/tournamentPlayType';
+import type { TournamentStatusTabT } from '../_types/tournamentTab';
export const parseTabParam = (tab?: string | null): TournamentStatusTabT =>
tab === 'completed' ? 'completed' : 'ongoing';
From 6c826ea982408efdea0f32198e2b06bbecf25aee Mon Sep 17 00:00:00 2001
From: iOdiO89 <117376841+iOdiO89@users.noreply.github.com>
Date: Thu, 15 Oct 2026 00:52:03 +0900
Subject: [PATCH 04/13] =?UTF-8?q?refactor:=20history=20state=20=EB=B3=B4?=
=?UTF-8?q?=EC=A1=B4=20URL=20=EA=B5=90=EC=B2=B4=20=EB=A1=9C=EC=A7=81?=
=?UTF-8?q?=EC=9D=84=20replaceUrlKeepingState=20=EC=9C=A0=ED=8B=B8?=
=?UTF-8?q?=EB=A1=9C=20=EC=B6=94=EC=B6=9C?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../history/_components/TournamentHistorySection.tsx | 10 ++--------
apps/web/src/utils/replaceUrlKeepingState.ts | 7 +++++++
2 files changed, 9 insertions(+), 8 deletions(-)
create mode 100644 apps/web/src/utils/replaceUrlKeepingState.ts
diff --git a/apps/web/src/app/tournament/history/_components/TournamentHistorySection.tsx b/apps/web/src/app/tournament/history/_components/TournamentHistorySection.tsx
index f90b4c4d..153d3119 100644
--- a/apps/web/src/app/tournament/history/_components/TournamentHistorySection.tsx
+++ b/apps/web/src/app/tournament/history/_components/TournamentHistorySection.tsx
@@ -5,6 +5,7 @@ import { Suspense } from 'react';
import Spacing from '@/components/spacing';
import { Z_INDEX } from '@/consts/zIndex';
+import { replaceUrlKeepingState } from '@/utils/replaceUrlKeepingState';
import { PLAY_TYPE_BY_FILTER, PLAY_TYPE_FILTER } from '../_consts/tournamentPlayType';
import { STATUS_BY_TAB } from '../_consts/tournamentTab';
@@ -21,15 +22,8 @@ function TournamentHistorySection() {
const activeTab = parseTabParam(searchParams.get('tab'));
const activePlayTypeFilter = parsePlayParam(searchParams.get('play'));
- /**
- * 서버 왕복 없이 주소만 동기화.
- * 스크롤 복원 키가 history.state 에 저장되므로 기존 state 를 반드시 보존한다.
- */
const syncSearchParams = (tab: TournamentStatusTabT, play: TournamentPlayTypeFilterT) => {
- const { __NA, _N, __PRIVATE_NEXTJS_INTERNALS_TREE, ...customState } =
- (window.history.state as Record | null) ?? {};
-
- window.history.replaceState(customState, '', `?tab=${tab}&play=${play}`);
+ replaceUrlKeepingState(`?tab=${tab}&play=${play}`);
};
/** 탭을 옮기면 플레이 유형 필터는 항상 '전체'로 초기화 */
diff --git a/apps/web/src/utils/replaceUrlKeepingState.ts b/apps/web/src/utils/replaceUrlKeepingState.ts
new file mode 100644
index 00000000..d6595e9c
--- /dev/null
+++ b/apps/web/src/utils/replaceUrlKeepingState.ts
@@ -0,0 +1,7 @@
+/** history state 보존하면서 url만 변경 */
+export const replaceUrlKeepingState = (url: string) => {
+ const { __NA, _N, __PRIVATE_NEXTJS_INTERNALS_TREE, ...customState }: Record =
+ window.history.state ?? {};
+
+ window.history.replaceState(customState, '', url);
+};
From d4c3a3c8a4308d068488d08d4645cb34560a49a7 Mon Sep 17 00:00:00 2001
From: iOdiO89 <117376841+iOdiO89@users.noreply.github.com>
Date: Thu, 15 Oct 2026 00:52:03 +0900
Subject: [PATCH 05/13] =?UTF-8?q?feat:=20=EC=9C=84=EC=8B=9C=20=EB=AA=A9?=
=?UTF-8?q?=EB=A1=9D=C2=B7=ED=8F=B4=EB=8D=94=20=EC=83=81=EC=84=B8=20?=
=?UTF-8?q?=EA=B2=BD=EB=A1=9C=EC=99=80=20=ED=9A=8C=EC=9B=90=20=EC=A0=84?=
=?UTF-8?q?=EC=9A=A9=20=EA=B6=8C=ED=95=9C=20=EC=B6=94=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
apps/web/src/consts/route.ts | 5 ++++-
apps/web/src/utils/getRouteType.test.ts | 6 +++++-
apps/web/src/utils/getRouteType.ts | 1 +
apps/web/src/utils/scrollRestoration.ts | 3 ++-
4 files changed, 12 insertions(+), 3 deletions(-)
diff --git a/apps/web/src/consts/route.ts b/apps/web/src/consts/route.ts
index 25a7dcec..4986b5de 100644
--- a/apps/web/src/consts/route.ts
+++ b/apps/web/src/consts/route.ts
@@ -20,8 +20,11 @@ export const ROUTES = {
PLAY_FROM_LINK: (sourceTournamentId: number) => `/play/${sourceTournamentId}`,
/** 3. Member Only */
- WISHLIST: '/archive/wish',
+ WISHLIST: '/archive/wish', // TODO: /wish 경로로 변경 필요
WISH_EDIT: (wishId: number) => `/archive/wish/${wishId}`,
+ WISH: '/wish', // TEMP
+ WISHLIST_FOLDER_TAB: '/wish?tab=folder',
+ WISHLIST_FOLDER: (folderId: number) => `/wish/folder/${folderId}`,
NOTIFICATION: '/notification',
MYPAGE_EDIT: '/mypage/edit',
MYPAGE_WITHDRAW: '/mypage/withdraw',
diff --git a/apps/web/src/utils/getRouteType.test.ts b/apps/web/src/utils/getRouteType.test.ts
index a0d737b1..f03ab0b0 100644
--- a/apps/web/src/utils/getRouteType.test.ts
+++ b/apps/web/src/utils/getRouteType.test.ts
@@ -8,9 +8,13 @@ describe('getRouteType', () => {
expect(getRouteType('/home/anything')).toBe('MEMBER_AND_GUEST');
expect(getRouteType('/homework')).toBeNull();
- expect(getRouteType('/archive/wish')).toBe('MEMBER_ONLY');
+ expect(getRouteType('/archive/wish')).toBe('MEMBER_ONLY'); // TODO: 위시 경로 이관 시 제거
expect(getRouteType('/archive/wish/3')).toBe('MEMBER_ONLY');
expect(getRouteType('/archive/wishlist')).toBeNull();
+
+ expect(getRouteType('/wish')).toBe('MEMBER_ONLY');
+ expect(getRouteType('/wish/folder/3')).toBe('MEMBER_ONLY');
+ expect(getRouteType('/wishlist')).toBeNull(); // 올바르지 않은 경로 테스트
});
it('토너먼트 동적 경로는 id 세그먼트 하나와 정확한 끝만 허용한다', () => {
diff --git a/apps/web/src/utils/getRouteType.ts b/apps/web/src/utils/getRouteType.ts
index 20cd493d..1f065551 100644
--- a/apps/web/src/utils/getRouteType.ts
+++ b/apps/web/src/utils/getRouteType.ts
@@ -41,6 +41,7 @@ const isMemberOnlyRoute = (pathname: string) => {
if (pathname === ROUTES.MYPAGE_WITHDRAW) return true;
if (pathname === ROUTES.MYPAGE_EDIT) return true;
if (matchesPath(pathname, ROUTES.WISHLIST)) return true;
+ if (matchesPath(pathname, ROUTES.WISH)) return true; // TEMP
if (matchesPath(pathname, ROUTES.NOTIFICATION)) return true;
if (matchesPath(pathname, ROUTES.TOURNAMENT_HISTORY)) return true;
diff --git a/apps/web/src/utils/scrollRestoration.ts b/apps/web/src/utils/scrollRestoration.ts
index 0cb5da04..e5ff1389 100644
--- a/apps/web/src/utils/scrollRestoration.ts
+++ b/apps/web/src/utils/scrollRestoration.ts
@@ -8,7 +8,8 @@ const HISTORY_STATE_KEY = '__pikiScrollKey';
export const SCROLL_ANCHOR_ID_ATTR = 'data-scroll-anchor-id';
export const SCROLL_NAMESPACE = {
- ARCHIVE_WISH: 'archiveWish',
+ ARCHIVE_WISH: 'archiveWish', // TODO: 제거 필요
+ WISH: 'wish', // TODO: WISHLIST: 'wishlist'로 변경 예정
TOURNAMENT_HISTORY: 'tournamentHistory',
} as const;
From bfcbc33e1f69ab3a10dc91b84185522afdff0d58 Mon Sep 17 00:00:00 2001
From: iOdiO89 <117376841+iOdiO89@users.noreply.github.com>
Date: Thu, 15 Oct 2026 00:52:03 +0900
Subject: [PATCH 06/13] =?UTF-8?q?feat:=20=EC=9C=84=EC=8B=9C=20=EB=AA=A9?=
=?UTF-8?q?=EB=A1=9D=C2=B7=ED=8F=B4=EB=8D=94=20=EC=83=81=EC=84=B8=EC=97=90?=
=?UTF-8?q?=EC=84=9C=20=EB=B0=94=ED=85=80=ED=83=AD=20=EB=85=B8=EC=B6=9C?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
apps/web/src/components/bottom-tab-bar/index.tsx | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/apps/web/src/components/bottom-tab-bar/index.tsx b/apps/web/src/components/bottom-tab-bar/index.tsx
index 73063bec..c8f8d493 100644
--- a/apps/web/src/components/bottom-tab-bar/index.tsx
+++ b/apps/web/src/components/bottom-tab-bar/index.tsx
@@ -85,7 +85,10 @@ function BottomTabBar() {
const [isPressed, setIsPressed] = useState(false);
const [isGrabbing, setIsGrabbing] = useState(false);
- const activeIndex = TABS.findIndex(({ href }) => matchesTabPath(pathname, href));
+ const isWishFolderRoute = /^\/wish(?:\/folder\/[^/]+)?$/.test(pathname); // TODO: /wish/folder/:folderId 만 해당되도록 조건 변경 필요. 현재는 /wish, /wish/folder/:folderId 둘 다 해당. 변수명은 이관 이후 기준으로 작성
+ const activeIndex = TABS.findIndex(
+ ({ href }) => matchesTabPath(pathname, href) || (isWishFolderRoute && href === ROUTES.WISHLIST)
+ );
useEffect(
() => () => {
@@ -95,7 +98,7 @@ function BottomTabBar() {
);
// 탭 경로와 정확히 일치할 때만 노출 — 하위 상세/편집 라우트에서는 숨김
- if (!TABS.some(({ href }) => href === pathname)) return null;
+ if (!isWishFolderRoute && !TABS.some(({ href }) => href === pathname)) return null;
const handlePointerDown = (event: React.PointerEvent) => {
if (event.button !== 0 || isGrabbingRef.current) return;
@@ -289,8 +292,8 @@ function BottomTabBar() {
)}
- {TABS.map(({ label, activeIcon: ActiveIcon, inactiveIcon: InactiveIcon, href }) => {
- const isActive = matchesTabPath(pathname, href);
+ {TABS.map(({ label, activeIcon: ActiveIcon, inactiveIcon: InactiveIcon, href }, index) => {
+ const isActive = index === activeIndex;
const Icon = isActive ? ActiveIcon : InactiveIcon;
return (
Date: Thu, 15 Oct 2026 00:52:03 +0900
Subject: [PATCH 07/13] =?UTF-8?q?feat:=20=EC=9C=84=EC=8B=9C=20=EB=AA=A9?=
=?UTF-8?q?=EB=A1=9D=C2=B7=ED=8F=B4=EB=8D=94=20=EC=83=81=EC=84=B8=20?=
=?UTF-8?q?=ED=99=94=EB=A9=B4=20=EC=B6=94=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
apps/web/public/mock/wish/wish-1.jpg | Bin 0 -> 30427 bytes
apps/web/public/mock/wish/wish-2.jpg | Bin 0 -> 59886 bytes
apps/web/public/mock/wish/wish-3.jpg | Bin 0 -> 79544 bytes
apps/web/public/mock/wish/wish-4.jpg | Bin 0 -> 39946 bytes
apps/web/public/mock/wish/wish-5.jpg | Bin 0 -> 28621 bytes
apps/web/public/mock/wish/wish-6.jpg | Bin 0 -> 46391 bytes
.../app/wish/_common/_components/WishCard.tsx | 68 +++++++++++++++
.../app/wish/_common/_components/WishGrid.tsx | 31 +++++++
.../_common/_components/WishSearchLink.tsx | 15 ++++
.../src/app/wish/_common/_consts/wishMock.ts | 28 ++++++
apps/web/src/app/wish/_common/_types/wish.ts | 17 ++++
.../wish/_components/AllWishTabContent.tsx | 27 ++++++
.../app/wish/_components/FolderAddCard.tsx | 20 +++++
.../src/app/wish/_components/FolderCard.tsx | 78 +++++++++++++++++
.../app/wish/_components/FolderTabContent.tsx | 44 ++++++++++
.../src/app/wish/_components/WishTabBar.tsx | 65 ++++++++++++++
.../app/wish/_components/WishTabContent.tsx | 45 ++++++++++
apps/web/src/app/wish/_consts/wishTab.ts | 4 +
apps/web/src/app/wish/_types/wishTab.ts | 3 +
.../src/app/wish/_utils/wishSearchParams.ts | 5 ++
.../_components/FolderDetailContent.tsx | 82 ++++++++++++++++++
.../src/app/wish/folder/[folderId]/page.tsx | 19 ++++
apps/web/src/app/wish/layout.tsx | 30 +++++++
apps/web/src/app/wish/page.tsx | 25 ++++++
24 files changed, 606 insertions(+)
create mode 100644 apps/web/public/mock/wish/wish-1.jpg
create mode 100644 apps/web/public/mock/wish/wish-2.jpg
create mode 100644 apps/web/public/mock/wish/wish-3.jpg
create mode 100644 apps/web/public/mock/wish/wish-4.jpg
create mode 100644 apps/web/public/mock/wish/wish-5.jpg
create mode 100644 apps/web/public/mock/wish/wish-6.jpg
create mode 100644 apps/web/src/app/wish/_common/_components/WishCard.tsx
create mode 100644 apps/web/src/app/wish/_common/_components/WishGrid.tsx
create mode 100644 apps/web/src/app/wish/_common/_components/WishSearchLink.tsx
create mode 100644 apps/web/src/app/wish/_common/_consts/wishMock.ts
create mode 100644 apps/web/src/app/wish/_common/_types/wish.ts
create mode 100644 apps/web/src/app/wish/_components/AllWishTabContent.tsx
create mode 100644 apps/web/src/app/wish/_components/FolderAddCard.tsx
create mode 100644 apps/web/src/app/wish/_components/FolderCard.tsx
create mode 100644 apps/web/src/app/wish/_components/FolderTabContent.tsx
create mode 100644 apps/web/src/app/wish/_components/WishTabBar.tsx
create mode 100644 apps/web/src/app/wish/_components/WishTabContent.tsx
create mode 100644 apps/web/src/app/wish/_consts/wishTab.ts
create mode 100644 apps/web/src/app/wish/_types/wishTab.ts
create mode 100644 apps/web/src/app/wish/_utils/wishSearchParams.ts
create mode 100644 apps/web/src/app/wish/folder/[folderId]/_components/FolderDetailContent.tsx
create mode 100644 apps/web/src/app/wish/folder/[folderId]/page.tsx
create mode 100644 apps/web/src/app/wish/layout.tsx
create mode 100644 apps/web/src/app/wish/page.tsx
diff --git a/apps/web/public/mock/wish/wish-1.jpg b/apps/web/public/mock/wish/wish-1.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..16ea3606b9150c6bd33b255b6bf2be871573fdca
GIT binary patch
literal 30427
zcmbTdWl&r}*DgAEAOwftkl-3@V9)^)+}$C81PwB{YY4%ekO>U#GT7h*g8SeO!QI_C
zyzja9J5~4Bsr&4`d#~DU_3U1?s@Ll7r