From 7a9b8143ffa42c60dafec0519132b7d886cd3215 Mon Sep 17 00:00:00 2001 From: leeleeleeleejun Date: Tue, 17 Feb 2026 17:21:20 +0900 Subject: [PATCH 01/43] =?UTF-8?q?design:=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=20?= =?UTF-8?q?=ED=83=AD=20=EC=9D=B4=EB=A6=84=20=EB=B3=80=EA=B2=BD=20-=20'?= =?UTF-8?q?=EC=9D=91=EB=AA=A8=20=ED=95=98=EA=B8=B0'=20=E2=86=92=20'?= =?UTF-8?q?=EC=A7=84=ED=96=89=EC=A4=91=EC=9D=B8=20=EC=9D=B4=EB=B2=A4?= =?UTF-8?q?=ED=8A=B8'=EB=A1=9C=20=EB=B3=80=EA=B2=BD=20-=20'=EC=9D=91?= =?UTF-8?q?=EB=AA=A8=20=EA=B2=B0=EA=B3=BC'=20=E2=86=92=20'=EC=A2=85?= =?UTF-8?q?=EB=A3=8C=EB=90=9C=20=EC=9D=B4=EB=B2=A4=ED=8A=B8'=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/app/events/lucky-draw/LuckyDraw.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/web/app/events/lucky-draw/LuckyDraw.tsx b/apps/web/app/events/lucky-draw/LuckyDraw.tsx index 24a1c144..e6c54218 100644 --- a/apps/web/app/events/lucky-draw/LuckyDraw.tsx +++ b/apps/web/app/events/lucky-draw/LuckyDraw.tsx @@ -8,8 +8,8 @@ import { Participation, Result } from './_components/Pages' export type StepType = 'participation' | 'result' const STEP_NAME = { - participation: '응모 하기', - result: '응모 결과', + participation: '진행중인 이벤트', + result: '종료된 이벤트', } const TABS: StepType[] = ['participation', 'result'] From 04e219787aee7d3a93421d79b1d53dda5166c4c5 Mon Sep 17 00:00:00 2001 From: leeleeleeleejun Date: Tue, 17 Feb 2026 17:26:57 +0900 Subject: [PATCH 02/43] =?UTF-8?q?refactor:=20LuckyDraw=20=ED=83=AD=20key?= =?UTF-8?q?=20=EC=9D=B4=EB=A6=84=EC=9D=84=20=EC=9D=98=EB=AF=B8=EC=97=90=20?= =?UTF-8?q?=EB=A7=9E=EA=B2=8C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - participation → inProgress (진행중인 이벤트) - result → finished (종료된 이벤트) - StepType 타입 정의 및 모든 참조 업데이트 --- apps/web/app/events/lucky-draw/LuckyDraw.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/web/app/events/lucky-draw/LuckyDraw.tsx b/apps/web/app/events/lucky-draw/LuckyDraw.tsx index e6c54218..26d53317 100644 --- a/apps/web/app/events/lucky-draw/LuckyDraw.tsx +++ b/apps/web/app/events/lucky-draw/LuckyDraw.tsx @@ -5,16 +5,16 @@ import { Column, JustifyBetween } from '@repo/ui/components/Layout' import { NavBarItem } from './_components/NavBarItem' import { Participation, Result } from './_components/Pages' -export type StepType = 'participation' | 'result' +export type StepType = 'inProgress' | 'finished' const STEP_NAME = { - participation: '진행중인 이벤트', - result: '종료된 이벤트', + inProgress: '진행중인 이벤트', + finished: '종료된 이벤트', } -const TABS: StepType[] = ['participation', 'result'] +const TABS: StepType[] = ['inProgress', 'finished'] export const LuckyDraw = () => { - const [currentTab, setCurrentTab] = useState('participation') + const [currentTab, setCurrentTab] = useState('inProgress') return ( <> @@ -31,8 +31,8 @@ export const LuckyDraw = () => { /> ))} - {currentTab === 'participation' && } - {currentTab === 'result' && } + {currentTab === 'inProgress' && } + {currentTab === 'finished' && } ) From cc09c1b949d84911fd022cfb8f69af8b71ffc26d Mon Sep 17 00:00:00 2001 From: leeleeleeleejun Date: Tue, 17 Feb 2026 17:49:54 +0900 Subject: [PATCH 03/43] =?UTF-8?q?refactor:=20=EB=9F=AD=ED=82=A4=EB=93=9C?= =?UTF-8?q?=EB=A1=9C=EC=9A=B0=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20?= =?UTF-8?q?=EB=84=A4=EC=9D=B4=EB=B0=8D=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Participation을 InProgressEvent로 변경하면서 하위 컴포넌트들도 Participation 접두사를 제거하고 각 역할을 명확히 표현하도록 변경 - Participation → InProgressEvent (진행중인 이벤트) - ParticipationAction → EventEntryAction (이벤트 참가 액션) - ParticipationCountdown → EventCountdown (카운트다운) - ParticipationModal → EntryTicketModal (응모권 선택 모달) - ParticipationPrize → PrizeInfo (상품 정보) --- apps/web/app/events/lucky-draw/LuckyDraw.tsx | 4 ++-- .../EntryTicketModal.tsx} | 2 +- .../EventCountdown.tsx} | 6 +----- .../EventEntryAction.tsx} | 2 +- .../InProgressEvent.tsx} | 18 +++++++++--------- .../PrizeInfo.tsx} | 2 +- .../RemainingTickets.tsx | 0 .../Pages/InProgressEvent/index.tsx | 1 + .../_components/Pages/Participation/index.tsx | 1 - .../lucky-draw/_components/Pages/index.tsx | 2 +- 10 files changed, 17 insertions(+), 21 deletions(-) rename apps/web/app/events/lucky-draw/_components/Pages/{Participation/ParticipationModal.tsx => InProgressEvent/EntryTicketModal.tsx} (98%) rename apps/web/app/events/lucky-draw/_components/Pages/{Participation/ParticipationCountdown.tsx => InProgressEvent/EventCountdown.tsx} (88%) rename apps/web/app/events/lucky-draw/_components/Pages/{Participation/ParticipationAction.tsx => InProgressEvent/EventEntryAction.tsx} (95%) rename apps/web/app/events/lucky-draw/_components/Pages/{Participation/Participation.tsx => InProgressEvent/InProgressEvent.tsx} (80%) rename apps/web/app/events/lucky-draw/_components/Pages/{Participation/ParticipationPrize.tsx => InProgressEvent/PrizeInfo.tsx} (93%) rename apps/web/app/events/lucky-draw/_components/Pages/{Participation => InProgressEvent}/RemainingTickets.tsx (100%) create mode 100644 apps/web/app/events/lucky-draw/_components/Pages/InProgressEvent/index.tsx delete mode 100644 apps/web/app/events/lucky-draw/_components/Pages/Participation/index.tsx diff --git a/apps/web/app/events/lucky-draw/LuckyDraw.tsx b/apps/web/app/events/lucky-draw/LuckyDraw.tsx index 26d53317..a0119123 100644 --- a/apps/web/app/events/lucky-draw/LuckyDraw.tsx +++ b/apps/web/app/events/lucky-draw/LuckyDraw.tsx @@ -3,7 +3,7 @@ import { useState } from 'react' import { Column, JustifyBetween } from '@repo/ui/components/Layout' import { NavBarItem } from './_components/NavBarItem' -import { Participation, Result } from './_components/Pages' +import { InProgressEvent, Result } from './_components/Pages' export type StepType = 'inProgress' | 'finished' @@ -31,7 +31,7 @@ export const LuckyDraw = () => { /> ))} - {currentTab === 'inProgress' && } + {currentTab === 'inProgress' && } {currentTab === 'finished' && } diff --git a/apps/web/app/events/lucky-draw/_components/Pages/Participation/ParticipationModal.tsx b/apps/web/app/events/lucky-draw/_components/Pages/InProgressEvent/EntryTicketModal.tsx similarity index 98% rename from apps/web/app/events/lucky-draw/_components/Pages/Participation/ParticipationModal.tsx rename to apps/web/app/events/lucky-draw/_components/Pages/InProgressEvent/EntryTicketModal.tsx index f01dc872..82fd2bbd 100644 --- a/apps/web/app/events/lucky-draw/_components/Pages/Participation/ParticipationModal.tsx +++ b/apps/web/app/events/lucky-draw/_components/Pages/InProgressEvent/EntryTicketModal.tsx @@ -17,7 +17,7 @@ type Props = { remainingTicketsCount: number } -export const ParticipationModal = ({ +export const EntryTicketModal = ({ isOpen, onOpenChange, eventId, diff --git a/apps/web/app/events/lucky-draw/_components/Pages/Participation/ParticipationCountdown.tsx b/apps/web/app/events/lucky-draw/_components/Pages/InProgressEvent/EventCountdown.tsx similarity index 88% rename from apps/web/app/events/lucky-draw/_components/Pages/Participation/ParticipationCountdown.tsx rename to apps/web/app/events/lucky-draw/_components/Pages/InProgressEvent/EventCountdown.tsx index e5a29618..c31bd1a9 100644 --- a/apps/web/app/events/lucky-draw/_components/Pages/Participation/ParticipationCountdown.tsx +++ b/apps/web/app/events/lucky-draw/_components/Pages/InProgressEvent/EventCountdown.tsx @@ -4,11 +4,7 @@ import { useEffect, useState } from 'react' import { Text } from '@repo/ui/components/Text' import { formatRemainingTime } from '@/events/lucky-draw/_utils/formatRemainingTime' -export const ParticipationCountdown = ({ - eventEndDate, -}: { - eventEndDate: string -}) => { +export const EventCountdown = ({ eventEndDate }: { eventEndDate: string }) => { const [remainingTime, setRemainingTime] = useState('') useEffect(() => { diff --git a/apps/web/app/events/lucky-draw/_components/Pages/Participation/ParticipationAction.tsx b/apps/web/app/events/lucky-draw/_components/Pages/InProgressEvent/EventEntryAction.tsx similarity index 95% rename from apps/web/app/events/lucky-draw/_components/Pages/Participation/ParticipationAction.tsx rename to apps/web/app/events/lucky-draw/_components/Pages/InProgressEvent/EventEntryAction.tsx index f2757512..fee97e9b 100644 --- a/apps/web/app/events/lucky-draw/_components/Pages/Participation/ParticipationAction.tsx +++ b/apps/web/app/events/lucky-draw/_components/Pages/InProgressEvent/EventEntryAction.tsx @@ -8,7 +8,7 @@ type Props = { onParticipate: () => void } -export const ParticipationAction = ({ +export const EventEntryAction = ({ remainingTicketsCount, onParticipate, }: Props) => { diff --git a/apps/web/app/events/lucky-draw/_components/Pages/Participation/Participation.tsx b/apps/web/app/events/lucky-draw/_components/Pages/InProgressEvent/InProgressEvent.tsx similarity index 80% rename from apps/web/app/events/lucky-draw/_components/Pages/Participation/Participation.tsx rename to apps/web/app/events/lucky-draw/_components/Pages/InProgressEvent/InProgressEvent.tsx index abbb3375..14004719 100644 --- a/apps/web/app/events/lucky-draw/_components/Pages/Participation/Participation.tsx +++ b/apps/web/app/events/lucky-draw/_components/Pages/InProgressEvent/InProgressEvent.tsx @@ -4,14 +4,14 @@ import { useDisclosure } from '@heroui/react' import { useSuspenseQuery } from '@tanstack/react-query' import { useEventQueries } from '@/_apis/queries/event' import { Column } from '@repo/ui/components/Layout' -import { ParticipationModal } from './ParticipationModal' -import { ParticipationCountdown } from './ParticipationCountdown' import { Text } from '@repo/ui/components/Text' -import { ParticipationPrize } from './ParticipationPrize' -import { ParticipationAction } from './ParticipationAction' +import { EntryTicketModal } from './EntryTicketModal' +import { EventCountdown } from './EventCountdown' +import { EventEntryAction } from './EventEntryAction' +import { PrizeInfo } from './PrizeInfo' import { ParticipationStatus } from '@/events/lucky-draw/_components/ParticipationStatus' -export const Participation = () => { +export const InProgressEvent = () => { const { isOpen, onOpen, onOpenChange } = useDisclosure() const { data } = useSuspenseQuery(useEventQueries.privateInfo()) const { @@ -31,14 +31,14 @@ export const Participation = () => { 응모 종료까지 남은 시간 - + 이번주 행운의 상품은? - { /> - {/*응모하기 버튼 클릭 시 응모권 갯수 선택 모달*/} - Date: Tue, 17 Feb 2026 21:41:51 +0900 Subject: [PATCH 04/43] =?UTF-8?q?feat:=20=EC=A2=85=EB=A3=8C=EB=90=9C=20?= =?UTF-8?q?=EC=9D=B4=EB=B2=A4=ED=8A=B8=20=EB=AA=A9=EB=A1=9D=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/app/events/lucky-draw/LuckyDraw.tsx | 4 +- .../Pages/FinishedEvent/FinishedEvent.tsx | 37 +++++++++++++++++++ .../_components/Pages/FinishedEvent/index.ts | 1 + .../lucky-draw/_components/Pages/index.tsx | 1 + 4 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 apps/web/app/events/lucky-draw/_components/Pages/FinishedEvent/FinishedEvent.tsx create mode 100644 apps/web/app/events/lucky-draw/_components/Pages/FinishedEvent/index.ts diff --git a/apps/web/app/events/lucky-draw/LuckyDraw.tsx b/apps/web/app/events/lucky-draw/LuckyDraw.tsx index a0119123..0663a640 100644 --- a/apps/web/app/events/lucky-draw/LuckyDraw.tsx +++ b/apps/web/app/events/lucky-draw/LuckyDraw.tsx @@ -3,7 +3,7 @@ import { useState } from 'react' import { Column, JustifyBetween } from '@repo/ui/components/Layout' import { NavBarItem } from './_components/NavBarItem' -import { InProgressEvent, Result } from './_components/Pages' +import { InProgressEvent, FinishedEvent } from './_components/Pages' export type StepType = 'inProgress' | 'finished' @@ -32,7 +32,7 @@ export const LuckyDraw = () => { ))} {currentTab === 'inProgress' && } - {currentTab === 'finished' && } + {currentTab === 'finished' && } ) diff --git a/apps/web/app/events/lucky-draw/_components/Pages/FinishedEvent/FinishedEvent.tsx b/apps/web/app/events/lucky-draw/_components/Pages/FinishedEvent/FinishedEvent.tsx new file mode 100644 index 00000000..4258b16a --- /dev/null +++ b/apps/web/app/events/lucky-draw/_components/Pages/FinishedEvent/FinishedEvent.tsx @@ -0,0 +1,37 @@ +import Link from 'next/link' +import Image from 'next/image' +import { Column, Flex } from '@repo/ui/components/Layout' +import { Text } from '@repo/ui/components/Text' + +export const FinishedEvent = () => { + return ( + + + + + ) +} + +const FinishedEventItem = () => { + return ( +
  • + + {'종료된 + + BBQ 황금 올리브 치킨 + + 당첨자 3명 | 참여자 27명 + + + 종료 일자: 2025.12.23 + + + +
  • + ) +} diff --git a/apps/web/app/events/lucky-draw/_components/Pages/FinishedEvent/index.ts b/apps/web/app/events/lucky-draw/_components/Pages/FinishedEvent/index.ts new file mode 100644 index 00000000..29f08e22 --- /dev/null +++ b/apps/web/app/events/lucky-draw/_components/Pages/FinishedEvent/index.ts @@ -0,0 +1 @@ +export { FinishedEvent } from './FinishedEvent' diff --git a/apps/web/app/events/lucky-draw/_components/Pages/index.tsx b/apps/web/app/events/lucky-draw/_components/Pages/index.tsx index 3e240976..77890e5a 100644 --- a/apps/web/app/events/lucky-draw/_components/Pages/index.tsx +++ b/apps/web/app/events/lucky-draw/_components/Pages/index.tsx @@ -1,2 +1,3 @@ export { InProgressEvent } from './InProgressEvent' +export { FinishedEvent } from './FinishedEvent' export { Result } from './Result' From 5c2883688f291287efc141d7ee98a7ecc9c76250 Mon Sep 17 00:00:00 2001 From: leeleeleeleejun Date: Tue, 17 Feb 2026 23:16:31 +0900 Subject: [PATCH 05/43] =?UTF-8?q?feat:=20=EC=A2=85=EB=A3=8C=EB=90=9C=20?= =?UTF-8?q?=EC=9D=B4=EB=B2=A4=ED=8A=B8=20=EB=AA=A9=EB=A1=9D=EC=97=90=20Emp?= =?UTF-8?q?tyFallback=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Pages/FinishedEvent/FinishedEvent.tsx | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/apps/web/app/events/lucky-draw/_components/Pages/FinishedEvent/FinishedEvent.tsx b/apps/web/app/events/lucky-draw/_components/Pages/FinishedEvent/FinishedEvent.tsx index 4258b16a..755b50b3 100644 --- a/apps/web/app/events/lucky-draw/_components/Pages/FinishedEvent/FinishedEvent.tsx +++ b/apps/web/app/events/lucky-draw/_components/Pages/FinishedEvent/FinishedEvent.tsx @@ -1,21 +1,35 @@ import Link from 'next/link' import Image from 'next/image' +import { CLIENT_PATH } from '@/_constants/path' import { Column, Flex } from '@repo/ui/components/Layout' import { Text } from '@repo/ui/components/Text' +import { EmptyFallback } from '@/_components/EmptyFallback' export const FinishedEvent = () => { return ( - - - - + // Todo: EmptyFallback isEmpty 값 API 연동 후 동적으로 변경 + + + + + + ) } const FinishedEventItem = () => { return (
  • - + {'종료된 Date: Tue, 17 Feb 2026 23:28:27 +0900 Subject: [PATCH 06/43] =?UTF-8?q?feat:=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=20?= =?UTF-8?q?=EA=B2=B0=EA=B3=BC=20=EC=A1=B0=ED=9A=8C=20=EC=8B=9C=20eventId?= =?UTF-8?q?=20=ED=8C=8C=EB=9D=BC=EB=AF=B8=ED=84=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/app/_apis/queries/event.ts | 4 ++-- apps/web/app/_apis/services/event.ts | 6 ++++-- apps/web/app/_constants/path.ts | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/apps/web/app/_apis/queries/event.ts b/apps/web/app/_apis/queries/event.ts index 88494e56..0c3559ae 100644 --- a/apps/web/app/_apis/queries/event.ts +++ b/apps/web/app/_apis/queries/event.ts @@ -23,9 +23,9 @@ export const useEventQueries = { queryKey: EventQueryKeys.privateInfo(), queryFn: getPrivateEventInfo, }), - result: () => + result: (eventId: string) => queryOptions({ queryKey: EventQueryKeys.result(), - queryFn: getEventResult, + queryFn: () => getEventResult(eventId), }), } diff --git a/apps/web/app/_apis/services/event.ts b/apps/web/app/_apis/services/event.ts index 924fb43f..5bcc85f2 100644 --- a/apps/web/app/_apis/services/event.ts +++ b/apps/web/app/_apis/services/event.ts @@ -27,7 +27,9 @@ export const participationEvent = async (body: { return data } -export const getEventResult = async (): Promise => { - const { data } = await axiosInstance.get(API_PATH.EVENT.RESULT) +export const getEventResult = async ( + eventId: string, +): Promise => { + const { data } = await axiosInstance.get(API_PATH.EVENT.RESULT(eventId)) return EventResultSchema.parse(data) } diff --git a/apps/web/app/_constants/path.ts b/apps/web/app/_constants/path.ts index 0f474d52..addd07f6 100644 --- a/apps/web/app/_constants/path.ts +++ b/apps/web/app/_constants/path.ts @@ -37,7 +37,7 @@ export const API_PATH = { EVENT: { INFO: '/events', PARTICIPATIONS: '/events/entries', - RESULT: '/events/results', + RESULT: (eventId: string) => `/events/${eventId}/entries`, }, REQUEST: { LIST: '/requests/places', From 9f988049e9b2e6c6a99c96b8e69ba69aa17f5efd Mon Sep 17 00:00:00 2001 From: leeleeleeleejun Date: Tue, 17 Feb 2026 23:33:06 +0900 Subject: [PATCH 07/43] =?UTF-8?q?refactor:=20=ED=96=89=EC=9A=B4=20?= =?UTF-8?q?=EB=B3=B5=EA=B6=8C=20=ED=8E=98=EC=9D=B4=EC=A7=80=20Header?= =?UTF-8?q?=EB=A5=BC=20layout=EC=9C=BC=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/app/events/lucky-draw/layout.tsx | 27 +++++++++++++++++ apps/web/app/events/lucky-draw/page.tsx | 36 +++++------------------ 2 files changed, 34 insertions(+), 29 deletions(-) create mode 100644 apps/web/app/events/lucky-draw/layout.tsx diff --git a/apps/web/app/events/lucky-draw/layout.tsx b/apps/web/app/events/lucky-draw/layout.tsx new file mode 100644 index 00000000..646c83af --- /dev/null +++ b/apps/web/app/events/lucky-draw/layout.tsx @@ -0,0 +1,27 @@ +import { Header } from '@repo/ui/components/Header' +import { HeaderBackButton } from '@/_components/HeaderBackButton' +import { Flex } from '@repo/ui/components/Layout' +import { Icon } from '@repo/ui/components/Icon' +import { Text } from '@repo/ui/components/Text' +import { InfoPopover } from '@/events/lucky-draw/_components/InfoPopover' + +const LuckyDrawLayout = ({ children }: { children: React.ReactNode }) => { + return ( + <> +
    } + center={ + + + 행운 복권 + + } + right={} + className={'border-b-1 border-gray-50'} + /> + {children} + + ) +} + +export default LuckyDrawLayout diff --git a/apps/web/app/events/lucky-draw/page.tsx b/apps/web/app/events/lucky-draw/page.tsx index 82519414..39cde34b 100644 --- a/apps/web/app/events/lucky-draw/page.tsx +++ b/apps/web/app/events/lucky-draw/page.tsx @@ -1,40 +1,18 @@ -import { Header } from '@repo/ui/components/Header' -import { HeaderBackButton } from '@/_components/HeaderBackButton' -import { Flex } from '@repo/ui/components/Layout' -import { Icon } from '@repo/ui/components/Icon' -import { Text } from '@repo/ui/components/Text' import { LuckyDraw } from './LuckyDraw' import { HydrationBoundaryPage } from '@/_components/HydrationBoundaryPage' import { useEventQueries } from '@/_apis/queries/event' -import { InfoPopover } from './_components/InfoPopover' export const dynamic = 'force-dynamic' const Page = () => { return ( - <> -
    } - center={ - - - 행운 복권 - - } - right={} - className={'border-b-1 border-gray-50'} - /> - { - await Promise.all([ - queryClient.prefetchQuery(useEventQueries.privateInfo()), - queryClient.prefetchQuery(useEventQueries.result()), - ]) - }} - > - - - + { + await queryClient.prefetchQuery(useEventQueries.privateInfo()) + }} + > + + ) } From 10fe338cec5f0599d04e6ff526a9c8bc9ed5f608 Mon Sep 17 00:00:00 2001 From: leeleeleeleejun Date: Tue, 17 Feb 2026 23:45:35 +0900 Subject: [PATCH 08/43] =?UTF-8?q?refactor:=20=EC=9D=B4=EB=B2=A4=ED=8A=B8?= =?UTF-8?q?=20=EA=B2=B0=EA=B3=BC=20=ED=8E=98=EC=9D=B4=EC=A7=80=EB=A5=BC=20?= =?UTF-8?q?=EB=8F=99=EC=A0=81=20=EB=9D=BC=EC=9A=B0=ED=8A=B8=EB=A1=9C=20?= =?UTF-8?q?=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_components/Pages/Result/NoResult.tsx | 26 ----------------- .../_components/Pages/Result/index.tsx | 1 - .../lucky-draw/_components/Pages/index.tsx | 1 - .../[id]/EventResultClient.tsx} | 28 ++++++++++++------- .../Result => result/[id]}/ResultModal.tsx | 0 .../_components/LottoBalls/LottoBalls.tsx | 0 .../[id]}/_components/LottoBalls/index.tsx | 0 .../events/lucky-draw/result/[id]/page.tsx | 13 +++++++++ 8 files changed, 31 insertions(+), 38 deletions(-) delete mode 100644 apps/web/app/events/lucky-draw/_components/Pages/Result/NoResult.tsx delete mode 100644 apps/web/app/events/lucky-draw/_components/Pages/Result/index.tsx rename apps/web/app/events/lucky-draw/{_components/Pages/Result/Result.tsx => result/[id]/EventResultClient.tsx} (67%) rename apps/web/app/events/lucky-draw/{_components/Pages/Result => result/[id]}/ResultModal.tsx (100%) rename apps/web/app/events/lucky-draw/{ => result/[id]}/_components/LottoBalls/LottoBalls.tsx (100%) rename apps/web/app/events/lucky-draw/{ => result/[id]}/_components/LottoBalls/index.tsx (100%) create mode 100644 apps/web/app/events/lucky-draw/result/[id]/page.tsx diff --git a/apps/web/app/events/lucky-draw/_components/Pages/Result/NoResult.tsx b/apps/web/app/events/lucky-draw/_components/Pages/Result/NoResult.tsx deleted file mode 100644 index b5d8bcab..00000000 --- a/apps/web/app/events/lucky-draw/_components/Pages/Result/NoResult.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import Link from 'next/link' -import { Column } from '@repo/ui/components/Layout' -import { Text } from '@repo/ui/components/Text' -import { Button } from '@repo/ui/components/Button' -import { CLIENT_PATH } from '@/_constants/path' - -export const NoResult = () => ( - -
    - - 아직 발표된 당첨 결과 없어요! - - - 맛집을 등록하고 행운의 주인공이 되어보세요! - -
    - -
    -) diff --git a/apps/web/app/events/lucky-draw/_components/Pages/Result/index.tsx b/apps/web/app/events/lucky-draw/_components/Pages/Result/index.tsx deleted file mode 100644 index 0964f1d0..00000000 --- a/apps/web/app/events/lucky-draw/_components/Pages/Result/index.tsx +++ /dev/null @@ -1 +0,0 @@ -export { Result } from './Result' diff --git a/apps/web/app/events/lucky-draw/_components/Pages/index.tsx b/apps/web/app/events/lucky-draw/_components/Pages/index.tsx index 77890e5a..d6768741 100644 --- a/apps/web/app/events/lucky-draw/_components/Pages/index.tsx +++ b/apps/web/app/events/lucky-draw/_components/Pages/index.tsx @@ -1,3 +1,2 @@ export { InProgressEvent } from './InProgressEvent' export { FinishedEvent } from './FinishedEvent' -export { Result } from './Result' diff --git a/apps/web/app/events/lucky-draw/_components/Pages/Result/Result.tsx b/apps/web/app/events/lucky-draw/result/[id]/EventResultClient.tsx similarity index 67% rename from apps/web/app/events/lucky-draw/_components/Pages/Result/Result.tsx rename to apps/web/app/events/lucky-draw/result/[id]/EventResultClient.tsx index 09e48487..a69ad3d5 100644 --- a/apps/web/app/events/lucky-draw/_components/Pages/Result/Result.tsx +++ b/apps/web/app/events/lucky-draw/result/[id]/EventResultClient.tsx @@ -2,22 +2,30 @@ import { useState } from 'react' import { useDisclosure } from '@heroui/react' -import { useSuspenseQuery } from '@tanstack/react-query' -import { useEventQueries } from '@/_apis/queries/event' +// import { useSuspenseQuery } from '@tanstack/react-query' +// import { useEventQueries } from '@/_apis/queries/event' import { Text } from '@repo/ui/components/Text' import { Button } from '@repo/ui/components/Button' import { Column } from '@repo/ui/components/Layout' -import { LottoBalls } from '../../LottoBalls' -import { ParticipationStatus } from '../../ParticipationStatus' +import { LottoBalls } from './_components/LottoBalls' import { ResultModal } from './ResultModal' -import { NoResult } from './NoResult' +import { ParticipationStatus } from '../../_components/ParticipationStatus' -export const Result = () => { +interface Props { + eventId: string +} + +export const EventResultClient = ({ eventId }: Props) => { + console.log(eventId) const [isRunning, setIsRunning] = useState(false) const { isOpen, onOpen, onOpenChange } = useDisclosure() - const { data } = useSuspenseQuery(useEventQueries.result()) - - if (!data) return + // Todo: 백엔드 API 작업 완료 시 연동 필요 + // const { data } = useSuspenseQuery(useEventQueries.result(eventId)) + const data = { + isWinner: true, + participantsCount: 100, + usedTicketsCount: 150, + } const { isWinner, participantsCount, usedTicketsCount } = data @@ -34,7 +42,7 @@ export const Result = () => { return ( <> - + 행운의 주인공이 되어보세요! diff --git a/apps/web/app/events/lucky-draw/_components/Pages/Result/ResultModal.tsx b/apps/web/app/events/lucky-draw/result/[id]/ResultModal.tsx similarity index 100% rename from apps/web/app/events/lucky-draw/_components/Pages/Result/ResultModal.tsx rename to apps/web/app/events/lucky-draw/result/[id]/ResultModal.tsx diff --git a/apps/web/app/events/lucky-draw/_components/LottoBalls/LottoBalls.tsx b/apps/web/app/events/lucky-draw/result/[id]/_components/LottoBalls/LottoBalls.tsx similarity index 100% rename from apps/web/app/events/lucky-draw/_components/LottoBalls/LottoBalls.tsx rename to apps/web/app/events/lucky-draw/result/[id]/_components/LottoBalls/LottoBalls.tsx diff --git a/apps/web/app/events/lucky-draw/_components/LottoBalls/index.tsx b/apps/web/app/events/lucky-draw/result/[id]/_components/LottoBalls/index.tsx similarity index 100% rename from apps/web/app/events/lucky-draw/_components/LottoBalls/index.tsx rename to apps/web/app/events/lucky-draw/result/[id]/_components/LottoBalls/index.tsx diff --git a/apps/web/app/events/lucky-draw/result/[id]/page.tsx b/apps/web/app/events/lucky-draw/result/[id]/page.tsx new file mode 100644 index 00000000..0baf35fa --- /dev/null +++ b/apps/web/app/events/lucky-draw/result/[id]/page.tsx @@ -0,0 +1,13 @@ +import { EventResultClient } from './EventResultClient' + +const EventResultPage = async ({ + params, +}: { + params: Promise<{ id: string }> +}) => { + const { id } = await params + + return +} + +export default EventResultPage From da019681e7bce4d245834713676d8ba154bc9ced Mon Sep 17 00:00:00 2001 From: leeleeleeleejun Date: Tue, 17 Feb 2026 23:46:37 +0900 Subject: [PATCH 09/43] =?UTF-8?q?feat:=20EventSummary=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80=EB=A1=9C=20=EC=9D=B4?= =?UTF-8?q?=EB=B2=A4=ED=8A=B8=20=EC=A0=95=EB=B3=B4=20=ED=91=9C=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../result/[id]/EventResultClient.tsx | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/apps/web/app/events/lucky-draw/result/[id]/EventResultClient.tsx b/apps/web/app/events/lucky-draw/result/[id]/EventResultClient.tsx index a69ad3d5..0b32c0b0 100644 --- a/apps/web/app/events/lucky-draw/result/[id]/EventResultClient.tsx +++ b/apps/web/app/events/lucky-draw/result/[id]/EventResultClient.tsx @@ -6,10 +6,11 @@ import { useDisclosure } from '@heroui/react' // import { useEventQueries } from '@/_apis/queries/event' import { Text } from '@repo/ui/components/Text' import { Button } from '@repo/ui/components/Button' -import { Column } from '@repo/ui/components/Layout' +import { Column, Flex } from '@repo/ui/components/Layout' import { LottoBalls } from './_components/LottoBalls' import { ResultModal } from './ResultModal' import { ParticipationStatus } from '../../_components/ParticipationStatus' +import Image from 'next/image' interface Props { eventId: string @@ -43,6 +44,7 @@ export const EventResultClient = ({ eventId }: Props) => { return ( <> + 행운의 주인공이 되어보세요! @@ -67,3 +69,25 @@ export const EventResultClient = ({ eventId }: Props) => { ) } + +const EventSummary = () => { + return ( + + {'종료된 + + BBQ 황금 올리브 치킨 + + 당첨자 3명 | 참여자 27명 + + + 종료 일자: 2025.12.23 + + + + ) +} From 2024c3172f4118099311d9606fc3cf4c03f3d130 Mon Sep 17 00:00:00 2001 From: leeleeleeleejun Date: Tue, 17 Feb 2026 23:51:15 +0900 Subject: [PATCH 10/43] =?UTF-8?q?feat:=20=EB=8B=B9=EC=B2=A8=20=EC=8B=9C=20?= =?UTF-8?q?=EC=A0=84=ED=99=94=EB=B2=88=ED=98=B8=20=EC=9E=85=EB=A0=A5=20?= =?UTF-8?q?=ED=8F=BC(WinnerInfoForm)=20=EC=B6=94=EA=B0=80=20-=20SuccessMod?= =?UTF-8?q?alContent=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lucky-draw/result/[id]/ResultModal.tsx | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/apps/web/app/events/lucky-draw/result/[id]/ResultModal.tsx b/apps/web/app/events/lucky-draw/result/[id]/ResultModal.tsx index 3bf91006..a2df9b17 100644 --- a/apps/web/app/events/lucky-draw/result/[id]/ResultModal.tsx +++ b/apps/web/app/events/lucky-draw/result/[id]/ResultModal.tsx @@ -1,7 +1,8 @@ -import { Modal, ModalContent } from '@heroui/react' +import { Input, Modal, ModalContent } from '@heroui/react' import { Icon, type IconType } from '@repo/ui/components/Icon' import { Column } from '@repo/ui/components/Layout' import { Text } from '@repo/ui/components/Text' +import { Button } from '@repo/ui/components/Button' type Props = { isWinner: boolean @@ -20,8 +21,8 @@ type ModalContent = { const SuccessModalContent: ModalContent = { icon: 'congratulation', title: '축하합니다!', - subTitle: '이번주 행운의 주인공으로 선정되셨습니다!', - description: '마이페이지에서 상품을 확인하실 수 있습니다', + subTitle: '행운의 주인공으로 선정되셨습니다!', + description: '전화번호를 입력하시면 3일 이내 기프티콘이 도착해요!', } const FailModalContent: ModalContent = { @@ -80,8 +81,24 @@ export const ResultModal = ({ {modalContent.description} + {isWinner && } ) } + +const WinnerInfoForm = () => { + // Todo: 전송하기 버튼에 api 연동 필요 + + return ( + + + + + ) +} From 5f752b7776cc74f9e4fe1ad7ea956951a2c0dad1 Mon Sep 17 00:00:00 2001 From: leeleeleeleejun Date: Wed, 18 Feb 2026 13:28:07 +0900 Subject: [PATCH 11/43] =?UTF-8?q?style:=20FinishedEvent=20=EB=B0=8F=20Luck?= =?UTF-8?q?yDraw=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=EC=9D=98=20CSS=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=88=98=EC=A0=95=20-=20?= =?UTF-8?q?=EC=A2=85=EB=A3=8C=EB=90=9C=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=20?= =?UTF-8?q?=EB=AA=A9=EB=A1=9D=EC=97=90=20=EC=8A=A4=ED=81=AC=EB=A1=A4=20?= =?UTF-8?q?=EA=B0=80=EB=8A=A5=ED=95=98=EB=8F=84=EB=A1=9D=20overflow-y-auto?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80=20-=20flex=20=EC=BB=A8=ED=85=8C=EC=9D=B4?= =?UTF-8?q?=EB=84=88=20=EC=98=A4=EB=B2=84=ED=94=8C=EB=A1=9C=EC=9A=B0=20?= =?UTF-8?q?=EB=B0=A9=EC=A7=80=EB=A5=BC=20=EC=9C=84=ED=95=B4=20min-h-0=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/app/events/lucky-draw/LuckyDraw.tsx | 2 +- .../_components/Pages/FinishedEvent/FinishedEvent.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/web/app/events/lucky-draw/LuckyDraw.tsx b/apps/web/app/events/lucky-draw/LuckyDraw.tsx index 0663a640..ba7c9303 100644 --- a/apps/web/app/events/lucky-draw/LuckyDraw.tsx +++ b/apps/web/app/events/lucky-draw/LuckyDraw.tsx @@ -18,7 +18,7 @@ export const LuckyDraw = () => { return ( <> - + {TABS.map((tab) => ( { fallbackTitle={'아직 종료된 이벤트가 없어요.'} fallbackDescription={'진행 중인 이벤트에 참여하고 행운을 잡아보세요!'} > - + From f67a9cb9c1566b9761f0ea766c0c8acf8ea46b7b Mon Sep 17 00:00:00 2001 From: leeleeleeleejun Date: Wed, 18 Feb 2026 13:29:24 +0900 Subject: [PATCH 12/43] =?UTF-8?q?feat:=20=EB=9F=AD=ED=82=A4=EB=93=9C?= =?UTF-8?q?=EB=A1=9C=EC=9A=B0=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=20=EB=B9=88=20?= =?UTF-8?q?=EC=83=81=ED=83=9C=20=EC=B2=98=EB=A6=AC=20=EB=B0=8F=20=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EC=95=84=EC=9B=83=20=EA=B0=9C=EC=84=A0=20-=20?= =?UTF-8?q?=EC=9D=B4=EB=B2=A4=ED=8A=B8=20=EC=8A=A4=ED=82=A4=EB=A7=88?= =?UTF-8?q?=EC=97=90=20nullable=20=ED=83=80=EC=9E=85=20=EC=B6=94=EA=B0=80?= =?UTF-8?q?=20(eventId,=20prize,=20eventEndDate)=20-=20=EC=A7=84=ED=96=89?= =?UTF-8?q?=20=EC=A4=91=EC=9D=B8=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=EA=B0=80=20?= =?UTF-8?q?=EC=97=86=EC=9D=84=20=EB=95=8C=20EmptyEventState=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=ED=91=9C=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/app/_apis/schemas/event.ts | 18 +++++---- .../Pages/InProgressEvent/InProgressEvent.tsx | 37 ++++++++++++++++++- 2 files changed, 46 insertions(+), 9 deletions(-) diff --git a/apps/web/app/_apis/schemas/event.ts b/apps/web/app/_apis/schemas/event.ts index a94e73ae..e07bee25 100644 --- a/apps/web/app/_apis/schemas/event.ts +++ b/apps/web/app/_apis/schemas/event.ts @@ -1,23 +1,25 @@ import { z } from 'zod' -export const PublicEventSchema = z.object({ - prize: z.object({ +export const PrizeSchema = z.nullable( + z.object({ description: z.string(), imageUrl: z.string(), }), +) + +export const PublicEventSchema = z.object({ + prize: PrizeSchema, }) +// Todo: API에서 진행 중인 이벤트가 없는 경우에 대한 명확한 응답이 필요할 것 같습니다. (예: data: null 등) export const PrivateEventSchema = z.object({ - eventId: z.number().transform(String), - prize: z.object({ - description: z.string(), - imageUrl: z.string(), - }), + eventId: z.nullable(z.number().transform(String)), + prize: PrizeSchema, totalWinnersCount: z.number(), participantsCount: z.number(), usedTicketsCount: z.number(), remainingTicketsCount: z.number(), - eventEndDate: z.string(), + eventEndDate: z.nullable(z.string()), }) export const EventResultSchema = z.object({ diff --git a/apps/web/app/events/lucky-draw/_components/Pages/InProgressEvent/InProgressEvent.tsx b/apps/web/app/events/lucky-draw/_components/Pages/InProgressEvent/InProgressEvent.tsx index 14004719..03591dd7 100644 --- a/apps/web/app/events/lucky-draw/_components/Pages/InProgressEvent/InProgressEvent.tsx +++ b/apps/web/app/events/lucky-draw/_components/Pages/InProgressEvent/InProgressEvent.tsx @@ -3,7 +3,8 @@ import { useDisclosure } from '@heroui/react' import { useSuspenseQuery } from '@tanstack/react-query' import { useEventQueries } from '@/_apis/queries/event' -import { Column } from '@repo/ui/components/Layout' +import { Column, Flex } from '@repo/ui/components/Layout' +import { Icon } from '@repo/ui/components/Icon' import { Text } from '@repo/ui/components/Text' import { EntryTicketModal } from './EntryTicketModal' import { EventCountdown } from './EventCountdown' @@ -24,6 +25,12 @@ export const InProgressEvent = () => { eventEndDate, } = data + // 진행 중인 이벤트가 없는 경우 + // Todo: API에서 진행 중인 이벤트가 없는 경우에 대한 명확한 응답이 필요 (예: data: null 등) + if (!eventId || !prize || !eventEndDate) { + return + } + return ( <> @@ -65,3 +72,31 @@ export const InProgressEvent = () => { ) } + +const EmptyEventState = () => { + return ( + + + + 현재 진행 중인 럭키드로우가 없습니다 + + + + + + + 맛집 리뷰를 작성하고 응모권을 모아보세요 + + + 다음 럭키드로우 이벤트에서 행운의 주인공이 되실 수 있습니다! + + + + ) +} From 7cc3bec957098f5b0083ef263b97595d4802a9d4 Mon Sep 17 00:00:00 2001 From: leeleeleeleejun Date: Wed, 18 Feb 2026 13:38:40 +0900 Subject: [PATCH 13/43] =?UTF-8?q?fix:=20null=20=EC=B2=B4=ED=81=AC=20?= =?UTF-8?q?=EC=A0=84=20=EA=B5=AC=EC=A1=B0=20=EB=B6=84=ED=95=B4=20=ED=95=A0?= =?UTF-8?q?=EB=8B=B9=EC=9C=BC=EB=A1=9C=20=EC=9D=B8=ED=95=9C=20=EB=9F=B0?= =?UTF-8?q?=ED=83=80=EC=9E=84=20=EC=97=90=EB=9F=AC=20=EB=B0=A9=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 구조 분해 할당을 null 체크 이후로 이동하여 prize, eventEndDate가 null일 때 발생하는 에러 해결 --- .../Pages/InProgressEvent/InProgressEvent.tsx | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/apps/web/app/events/lucky-draw/_components/Pages/InProgressEvent/InProgressEvent.tsx b/apps/web/app/events/lucky-draw/_components/Pages/InProgressEvent/InProgressEvent.tsx index 03591dd7..bf44a84a 100644 --- a/apps/web/app/events/lucky-draw/_components/Pages/InProgressEvent/InProgressEvent.tsx +++ b/apps/web/app/events/lucky-draw/_components/Pages/InProgressEvent/InProgressEvent.tsx @@ -15,6 +15,13 @@ import { ParticipationStatus } from '@/events/lucky-draw/_components/Participati export const InProgressEvent = () => { const { isOpen, onOpen, onOpenChange } = useDisclosure() const { data } = useSuspenseQuery(useEventQueries.privateInfo()) + + // 진행 중인 이벤트가 없는 경우 + // Todo: API에서 진행 중인 이벤트가 없는 경우에 대한 명확한 응답이 필요 (예: data: null 등) + if (!data.eventId || !data.prize || !data.eventEndDate) { + return + } + const { eventId, prize, @@ -25,12 +32,6 @@ export const InProgressEvent = () => { eventEndDate, } = data - // 진행 중인 이벤트가 없는 경우 - // Todo: API에서 진행 중인 이벤트가 없는 경우에 대한 명확한 응답이 필요 (예: data: null 등) - if (!eventId || !prize || !eventEndDate) { - return - } - return ( <> From 3859fe2b1ef3efc0a05721afafaac9f55c37e970 Mon Sep 17 00:00:00 2001 From: leeleeleeleejun Date: Wed, 18 Feb 2026 13:40:34 +0900 Subject: [PATCH 14/43] =?UTF-8?q?fix:=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=20?= =?UTF-8?q?=EA=B2=B0=EA=B3=BC=20=EC=BF=BC=EB=A6=AC=20=ED=82=A4=EC=97=90=20?= =?UTF-8?q?eventId=20=EC=B6=94=EA=B0=80=ED=95=98=EC=97=AC=20=EC=BA=90?= =?UTF-8?q?=EC=8B=9C=20=EC=B6=A9=EB=8F=8C=20=EB=B0=A9=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 서로 다른 이벤트 결과가 같은 캐시를 공유하는 문제 해결 --- apps/web/app/_apis/queries/event.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/web/app/_apis/queries/event.ts b/apps/web/app/_apis/queries/event.ts index 0c3559ae..7cc3d0f2 100644 --- a/apps/web/app/_apis/queries/event.ts +++ b/apps/web/app/_apis/queries/event.ts @@ -9,7 +9,8 @@ export const EventQueryKeys = { all: () => ['event'] as const, publicInfo: () => [...EventQueryKeys.all(), 'info', 'public'] as const, privateInfo: () => [...EventQueryKeys.all(), 'info', 'private'] as const, - result: () => [...EventQueryKeys.all(), 'result'] as const, + result: (eventId: string) => + [...EventQueryKeys.all(), 'result', eventId] as const, } export const useEventQueries = { @@ -25,7 +26,7 @@ export const useEventQueries = { }), result: (eventId: string) => queryOptions({ - queryKey: EventQueryKeys.result(), + queryKey: EventQueryKeys.result(eventId), queryFn: () => getEventResult(eventId), }), } From db2d655583fc8860fff18559d4ab337bca522ae9 Mon Sep 17 00:00:00 2001 From: leeleeleeleejun Date: Wed, 18 Feb 2026 13:57:39 +0900 Subject: [PATCH 15/43] =?UTF-8?q?fix:=20TypeScript=20=EC=BB=B4=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=EC=97=90=EB=9F=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - MSW handler에서 동적 라우트 파라미터 처리 - EventWelcome에서 nullable prize 처리 및 리다이렉트 추가 --- apps/web/app/_mocks/handlers/eventHandlers.ts | 2 +- .../_components/Step/EventWelcome/EventWelcome.tsx | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/apps/web/app/_mocks/handlers/eventHandlers.ts b/apps/web/app/_mocks/handlers/eventHandlers.ts index 42760a1b..48c1b231 100644 --- a/apps/web/app/_mocks/handlers/eventHandlers.ts +++ b/apps/web/app/_mocks/handlers/eventHandlers.ts @@ -10,7 +10,7 @@ export const EventHandlers = [ http.post(addBaseUrl(API_PATH.EVENT.PARTICIPATIONS), () => { return HttpResponse.json({ message: '성공' }) }), - http.get(addBaseUrl(API_PATH.EVENT.RESULT), () => { + http.get(addBaseUrl('/events/:eventId/entries'), () => { return HttpResponse.json(eventResult) }), ] diff --git a/apps/web/app/places/new/_components/Step/EventWelcome/EventWelcome.tsx b/apps/web/app/places/new/_components/Step/EventWelcome/EventWelcome.tsx index 00f80a2b..d21e4cd5 100644 --- a/apps/web/app/places/new/_components/Step/EventWelcome/EventWelcome.tsx +++ b/apps/web/app/places/new/_components/Step/EventWelcome/EventWelcome.tsx @@ -1,6 +1,7 @@ 'use client' import Link from 'next/link' import Image from 'next/image' +import { useRouter } from 'next/navigation' import { useEffect, useState } from 'react' import { motion, stagger, type Variants } from 'motion/react' import { CLIENT_PATH } from '@/_constants/path' @@ -37,9 +38,20 @@ const itemVariants: Variants = { } export const EventWelcome = ({ nextStep }: Props) => { + const { replace } = useRouter() const { data } = useSuspenseQuery(useEventQueries.publicInfo()) const { prize } = data + useEffect(() => { + if (!prize) { + replace(`${CLIENT_PATH.PLACE_NEW}?step=1`) + } + }, [replace, prize]) + + if (!prize) { + return null + } + return ( Date: Wed, 18 Feb 2026 14:01:50 +0900 Subject: [PATCH 16/43] =?UTF-8?q?refactor:=20=EB=94=94=EB=B2=84=EA=B7=B8?= =?UTF-8?q?=20=EC=BD=94=EB=93=9C=20=EC=A0=9C=EA=B1=B0=20=EB=B0=8F=20import?= =?UTF-8?q?=20=EC=88=9C=EC=84=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - console.log 제거 - 미사용 변수 경고 해결 (eslint-disable 주석 추가) - AGENTS.md 규칙에 따라 import 순서 정리 (Node/built-in → External → Absolute → Relative) --- .../lucky-draw/result/[id]/EventResultClient.tsx | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/apps/web/app/events/lucky-draw/result/[id]/EventResultClient.tsx b/apps/web/app/events/lucky-draw/result/[id]/EventResultClient.tsx index 0b32c0b0..9ab5cddf 100644 --- a/apps/web/app/events/lucky-draw/result/[id]/EventResultClient.tsx +++ b/apps/web/app/events/lucky-draw/result/[id]/EventResultClient.tsx @@ -1,27 +1,28 @@ 'use client' +import Image from 'next/image' import { useState } from 'react' import { useDisclosure } from '@heroui/react' // import { useSuspenseQuery } from '@tanstack/react-query' // import { useEventQueries } from '@/_apis/queries/event' -import { Text } from '@repo/ui/components/Text' import { Button } from '@repo/ui/components/Button' import { Column, Flex } from '@repo/ui/components/Layout' +import { Text } from '@repo/ui/components/Text' +import { ParticipationStatus } from '../../_components/ParticipationStatus' import { LottoBalls } from './_components/LottoBalls' import { ResultModal } from './ResultModal' -import { ParticipationStatus } from '../../_components/ParticipationStatus' -import Image from 'next/image' interface Props { eventId: string } -export const EventResultClient = ({ eventId }: Props) => { - console.log(eventId) +export const EventResultClient = ({ + eventId: _eventId, // eslint-disable-line @typescript-eslint/no-unused-vars +}: Props) => { const [isRunning, setIsRunning] = useState(false) const { isOpen, onOpen, onOpenChange } = useDisclosure() // Todo: 백엔드 API 작업 완료 시 연동 필요 - // const { data } = useSuspenseQuery(useEventQueries.result(eventId)) + // const { data } = useSuspenseQuery(useEventQueries.result(_eventId)) const data = { isWinner: true, participantsCount: 100, From 5d90756b640b1785b150ecd3b8ca7ac714d0a8ed Mon Sep 17 00:00:00 2001 From: leeleeleeleejun Date: Wed, 18 Feb 2026 14:05:28 +0900 Subject: [PATCH 17/43] =?UTF-8?q?refactor:=20=EC=9D=B4=EB=B2=A4=ED=8A=B8?= =?UTF-8?q?=20=EC=9D=91=EB=AA=A8=20=EC=97=90=EB=9F=AC=20=EC=B2=98=EB=A6=AC?= =?UTF-8?q?=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 에러 로깅 추가 (console.error) - AxiosError를 통한 서버 에러 메시지 표시 - 중복 color/severity prop 제거 - queryKey spread 연산자 제거 --- .../app/_apis/mutations/useParticipationEvent.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/apps/web/app/_apis/mutations/useParticipationEvent.ts b/apps/web/app/_apis/mutations/useParticipationEvent.ts index 4aaa2d0d..dae03c74 100644 --- a/apps/web/app/_apis/mutations/useParticipationEvent.ts +++ b/apps/web/app/_apis/mutations/useParticipationEvent.ts @@ -1,4 +1,5 @@ import { useMutation, useQueryClient } from '@tanstack/react-query' +import { AxiosError } from 'axios' import { participationEvent } from '@/_apis/services/event' import { EventQueryKeys } from '@/_apis/queries/event' import { addToast } from '@heroui/react' @@ -12,17 +13,23 @@ export const useParticipationEvent = () => { }, onSuccess: () => { queryClient.invalidateQueries({ - queryKey: [...EventQueryKeys.privateInfo()], + queryKey: EventQueryKeys.privateInfo(), }) addToast({ title: '응모가 성공적으로 완료되었습니다!', severity: 'success', }) }, - onError: () => { + onError: (error) => { + console.error('Event participation failed:', error) + + const message = + error instanceof AxiosError && error.response?.data?.message + ? error.response.data.message + : '응모 중 오류가 발생했습니다. 잠시 후 다시 시도해주세요.' + addToast({ - title: '응모 중 오류가 발생했습니다.', - color: 'danger', + title: message, severity: 'danger', }) }, From a9c2269145c07da56935c0805db8beabed5b9f92 Mon Sep 17 00:00:00 2001 From: leeleeleeleejun Date: Wed, 18 Feb 2026 20:37:25 +0900 Subject: [PATCH 18/43] =?UTF-8?q?feat:=20=EB=9F=AD=ED=82=A4=EB=93=9C?= =?UTF-8?q?=EB=A1=9C=EC=9A=B0=20=EB=8B=B9=EC=B2=A8=EC=9E=90=20=EC=A0=84?= =?UTF-8?q?=ED=99=94=EB=B2=88=ED=98=B8=20=EC=A0=9C=EC=B6=9C=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - WinnerInfoForm 컴포넌트로 전화번호 입력 폼 구현 - useSubmitWinnerPhoneNumber mutation hook 추가 - WinnerPhoneNumberSchema로 전화번호 유효성 검증 (010-XXXX-XXXX 형식) - submitWinnerPhoneNumber API 서비스 함수 추가 - ResultModal에 eventId prop 전달 및 폼 통합 - 성공/실패 시 토스트 메시지 표시 --- .../mutations/useSubmitWinnerPhoneNumber.ts | 31 +++++++++++ apps/web/app/_apis/schemas/event.ts | 11 ++++ apps/web/app/_apis/services/event.ts | 10 ++++ apps/web/app/_constants/path.ts | 1 + .../result/[id]/EventResultClient.tsx | 11 ++-- .../lucky-draw/result/[id]/ResultModal.tsx | 29 +++------- .../WinnerInfoForm/WinnerInfoForm.tsx | 54 +++++++++++++++++++ .../[id]/_components/WinnerInfoForm/index.ts | 1 + 8 files changed, 121 insertions(+), 27 deletions(-) create mode 100644 apps/web/app/_apis/mutations/useSubmitWinnerPhoneNumber.ts create mode 100644 apps/web/app/events/lucky-draw/result/[id]/_components/WinnerInfoForm/WinnerInfoForm.tsx create mode 100644 apps/web/app/events/lucky-draw/result/[id]/_components/WinnerInfoForm/index.ts diff --git a/apps/web/app/_apis/mutations/useSubmitWinnerPhoneNumber.ts b/apps/web/app/_apis/mutations/useSubmitWinnerPhoneNumber.ts new file mode 100644 index 00000000..b9620fff --- /dev/null +++ b/apps/web/app/_apis/mutations/useSubmitWinnerPhoneNumber.ts @@ -0,0 +1,31 @@ +import { useMutation } from '@tanstack/react-query' +import { addToast } from '@heroui/react' + +import { submitWinnerPhoneNumber } from '../services/event' + +interface UseSubmitWinnerPhoneNumberParams { + eventId: string +} + +export const useSubmitWinnerPhoneNumber = ({ + eventId, +}: UseSubmitWinnerPhoneNumberParams) => { + return useMutation({ + mutationFn: (phoneNumber: string) => + submitWinnerPhoneNumber(eventId, phoneNumber), + onSuccess: () => { + addToast({ + title: '전화번호가 성공적으로 제출되었습니다!', + severity: 'success', + }) + }, + onError: (error) => { + console.error('Failed to submit winner phone number:', error) + + addToast({ + title: '전화번호 제출에 실패했습니다. 잠시 후 다시 시도해주세요.', + severity: 'danger', + }) + }, + }) +} diff --git a/apps/web/app/_apis/schemas/event.ts b/apps/web/app/_apis/schemas/event.ts index e07bee25..0be138e8 100644 --- a/apps/web/app/_apis/schemas/event.ts +++ b/apps/web/app/_apis/schemas/event.ts @@ -29,6 +29,17 @@ export const EventResultSchema = z.object({ usedTicketsCount: z.number(), }) +export const WinnerPhoneNumberSchema = z.object({ + phoneNumber: z + .string() + .min(1, '전화번호를 입력해주세요.') + .regex( + /^010-\d{4}-\d{4}$/, + '올바른 형식으로 입력해주세요 (예: 010-1234-5678)', + ), +}) + export type PublicEvent = z.infer export type PrivateEvent = z.infer export type EventResult = z.infer +export type WinnerPhoneNumber = z.infer diff --git a/apps/web/app/_apis/services/event.ts b/apps/web/app/_apis/services/event.ts index 5bcc85f2..1bf5b887 100644 --- a/apps/web/app/_apis/services/event.ts +++ b/apps/web/app/_apis/services/event.ts @@ -33,3 +33,13 @@ export const getEventResult = async ( const { data } = await axiosInstance.get(API_PATH.EVENT.RESULT(eventId)) return EventResultSchema.parse(data) } + +// TODO: API 명세 확정 후 (인자, 반환값) 명확히 하기 +export const submitWinnerPhoneNumber = async ( + eventId: string, + phoneNumber: string, +): Promise => { + await axiosInstance.post(API_PATH.EVENT.APPLY(eventId), { + phoneNumber, + }) +} diff --git a/apps/web/app/_constants/path.ts b/apps/web/app/_constants/path.ts index addd07f6..dca3b123 100644 --- a/apps/web/app/_constants/path.ts +++ b/apps/web/app/_constants/path.ts @@ -38,6 +38,7 @@ export const API_PATH = { INFO: '/events', PARTICIPATIONS: '/events/entries', RESULT: (eventId: string) => `/events/${eventId}/entries`, + APPLY: (eventId: string) => `/events/${eventId}/apply`, }, REQUEST: { LIST: '/requests/places', diff --git a/apps/web/app/events/lucky-draw/result/[id]/EventResultClient.tsx b/apps/web/app/events/lucky-draw/result/[id]/EventResultClient.tsx index 9ab5cddf..177b62fc 100644 --- a/apps/web/app/events/lucky-draw/result/[id]/EventResultClient.tsx +++ b/apps/web/app/events/lucky-draw/result/[id]/EventResultClient.tsx @@ -16,13 +16,11 @@ interface Props { eventId: string } -export const EventResultClient = ({ - eventId: _eventId, // eslint-disable-line @typescript-eslint/no-unused-vars -}: Props) => { +export const EventResultClient = ({ eventId }: Props) => { const [isRunning, setIsRunning] = useState(false) const { isOpen, onOpen, onOpenChange } = useDisclosure() // Todo: 백엔드 API 작업 완료 시 연동 필요 - // const { data } = useSuspenseQuery(useEventQueries.result(_eventId)) + // const { data } = useSuspenseQuery(useEventQueries.result(eventId)) const data = { isWinner: true, participantsCount: 100, @@ -31,7 +29,7 @@ export const EventResultClient = ({ const { isWinner, participantsCount, usedTicketsCount } = data - const stopRunning = () => { + const stopLotteryAnimation = () => { setIsRunning(false) } @@ -62,10 +60,11 @@ export const EventResultClient = ({ ) diff --git a/apps/web/app/events/lucky-draw/result/[id]/ResultModal.tsx b/apps/web/app/events/lucky-draw/result/[id]/ResultModal.tsx index a2df9b17..2cb2e991 100644 --- a/apps/web/app/events/lucky-draw/result/[id]/ResultModal.tsx +++ b/apps/web/app/events/lucky-draw/result/[id]/ResultModal.tsx @@ -1,14 +1,15 @@ -import { Input, Modal, ModalContent } from '@heroui/react' +import { Modal, ModalContent } from '@heroui/react' import { Icon, type IconType } from '@repo/ui/components/Icon' import { Column } from '@repo/ui/components/Layout' import { Text } from '@repo/ui/components/Text' -import { Button } from '@repo/ui/components/Button' +import { WinnerInfoForm } from './_components/WinnerInfoForm' type Props = { + eventId: string isWinner: boolean isOpen: boolean onOpenChange: VoidFunction - stopRunning: VoidFunction + onAnimationStop: VoidFunction } type ModalContent = { @@ -33,16 +34,17 @@ const FailModalContent: ModalContent = { } export const ResultModal = ({ + eventId, isWinner, isOpen, onOpenChange, - stopRunning, + onAnimationStop, }: Props) => { const modalContent = isWinner ? SuccessModalContent : FailModalContent return ( - {isWinner && } + {isWinner && } ) } - -const WinnerInfoForm = () => { - // Todo: 전송하기 버튼에 api 연동 필요 - - return ( - - - - - ) -} diff --git a/apps/web/app/events/lucky-draw/result/[id]/_components/WinnerInfoForm/WinnerInfoForm.tsx b/apps/web/app/events/lucky-draw/result/[id]/_components/WinnerInfoForm/WinnerInfoForm.tsx new file mode 100644 index 00000000..09a935ef --- /dev/null +++ b/apps/web/app/events/lucky-draw/result/[id]/_components/WinnerInfoForm/WinnerInfoForm.tsx @@ -0,0 +1,54 @@ +import { useForm } from 'react-hook-form' +import { zodResolver } from '@hookform/resolvers/zod' +import { Input } from '@heroui/react' +import { + type WinnerPhoneNumber, + WinnerPhoneNumberSchema, +} from '@/_apis/schemas/event' +import { useSubmitWinnerPhoneNumber } from '@/_apis/mutations/useSubmitWinnerPhoneNumber' +import { Column } from '@repo/ui/components/Layout' +import { Button } from '@repo/ui/components/Button' + +interface WinnerInfoFormProps { + eventId: string +} + +export const WinnerInfoForm = ({ eventId }: WinnerInfoFormProps) => { + const { + register, + handleSubmit, + formState: { errors, isSubmitting }, + } = useForm({ + resolver: zodResolver(WinnerPhoneNumberSchema), + }) + + const { mutateAsync: submitPhoneNumber } = useSubmitWinnerPhoneNumber({ + eventId, + }) + + const onSubmit = async (data: WinnerPhoneNumber): Promise => { + await submitPhoneNumber(data.phoneNumber) + // TODO: 성공 시 모달 닫기 처리 + } + + return ( +
    + + + + + + +
    + ) +} diff --git a/apps/web/app/events/lucky-draw/result/[id]/_components/WinnerInfoForm/index.ts b/apps/web/app/events/lucky-draw/result/[id]/_components/WinnerInfoForm/index.ts new file mode 100644 index 00000000..2bc7645f --- /dev/null +++ b/apps/web/app/events/lucky-draw/result/[id]/_components/WinnerInfoForm/index.ts @@ -0,0 +1 @@ +export { WinnerInfoForm } from './WinnerInfoForm' From 470105a369e96f8fd0f2844f828735832a4084a2 Mon Sep 17 00:00:00 2001 From: leeleeleeleejun Date: Tue, 24 Feb 2026 18:50:54 +0900 Subject: [PATCH 19/43] =?UTF-8?q?fix:=20=EB=9F=AD=ED=82=A4=EB=93=9C?= =?UTF-8?q?=EB=A1=9C=EC=9A=B0=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=20=EB=AF=B8?= =?UTF-8?q?=EB=93=A4=EC=9B=A8=EC=96=B4=20matcher=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EA=B2=BD=EB=A1=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/middleware.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/web/middleware.ts b/apps/web/middleware.ts index bcabd2f0..2df76b76 100644 --- a/apps/web/middleware.ts +++ b/apps/web/middleware.ts @@ -25,7 +25,6 @@ export const config = { '/profile', '/requests', '/requests/:path*', - '/events/lucky-draw', '/events/gifticon', '/events/gifticon/:path*', ], From 486b979c15bec4fae1bb8dd4f11247443db7f089 Mon Sep 17 00:00:00 2001 From: leeleeleeleejun Date: Tue, 24 Feb 2026 18:58:44 +0900 Subject: [PATCH 20/43] =?UTF-8?q?refactor:=20EmptyEventState=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=EB=A5=BC=20=EB=B3=84=EB=8F=84=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=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 --- .../_components/Pages/EmptyEventState.tsx | 31 ++++++++++++++++++ .../InProgressEvent/InProgressEvent.tsx | 32 ++----------------- 2 files changed, 33 insertions(+), 30 deletions(-) create mode 100644 apps/web/app/events/lucky-draw/_components/Pages/EmptyEventState.tsx rename apps/web/app/events/lucky-draw/_components/Pages/{ => MemberView}/InProgressEvent/InProgressEvent.tsx (71%) diff --git a/apps/web/app/events/lucky-draw/_components/Pages/EmptyEventState.tsx b/apps/web/app/events/lucky-draw/_components/Pages/EmptyEventState.tsx new file mode 100644 index 00000000..38285636 --- /dev/null +++ b/apps/web/app/events/lucky-draw/_components/Pages/EmptyEventState.tsx @@ -0,0 +1,31 @@ +import { Column, Flex } from '@repo/ui/components/Layout' +import { Text } from '@repo/ui/components/Text' +import { Icon } from '@repo/ui/components/Icon' + +export const EmptyEventState = () => { + return ( + + + + 현재 진행 중인 럭키드로우가 없습니다 + + + + + + + 맛집 리뷰를 작성하고 응모권을 모아보세요 + + + 다음 럭키드로우 이벤트에서 행운의 주인공이 되실 수 있습니다! + + + + ) +} diff --git a/apps/web/app/events/lucky-draw/_components/Pages/InProgressEvent/InProgressEvent.tsx b/apps/web/app/events/lucky-draw/_components/Pages/MemberView/InProgressEvent/InProgressEvent.tsx similarity index 71% rename from apps/web/app/events/lucky-draw/_components/Pages/InProgressEvent/InProgressEvent.tsx rename to apps/web/app/events/lucky-draw/_components/Pages/MemberView/InProgressEvent/InProgressEvent.tsx index bf44a84a..38cefd4c 100644 --- a/apps/web/app/events/lucky-draw/_components/Pages/InProgressEvent/InProgressEvent.tsx +++ b/apps/web/app/events/lucky-draw/_components/Pages/MemberView/InProgressEvent/InProgressEvent.tsx @@ -3,14 +3,14 @@ import { useDisclosure } from '@heroui/react' import { useSuspenseQuery } from '@tanstack/react-query' import { useEventQueries } from '@/_apis/queries/event' -import { Column, Flex } from '@repo/ui/components/Layout' -import { Icon } from '@repo/ui/components/Icon' +import { Column } from '@repo/ui/components/Layout' import { Text } from '@repo/ui/components/Text' import { EntryTicketModal } from './EntryTicketModal' import { EventCountdown } from './EventCountdown' import { EventEntryAction } from './EventEntryAction' import { PrizeInfo } from './PrizeInfo' import { ParticipationStatus } from '@/events/lucky-draw/_components/ParticipationStatus' +import { EmptyEventState } from '../../EmptyEventState' export const InProgressEvent = () => { const { isOpen, onOpen, onOpenChange } = useDisclosure() @@ -73,31 +73,3 @@ export const InProgressEvent = () => { ) } - -const EmptyEventState = () => { - return ( - - - - 현재 진행 중인 럭키드로우가 없습니다 - - - - - - - 맛집 리뷰를 작성하고 응모권을 모아보세요 - - - 다음 럭키드로우 이벤트에서 행운의 주인공이 되실 수 있습니다! - - - - ) -} From 8b9c40db6d32004a5123fe01ed362b98d21595cc Mon Sep 17 00:00:00 2001 From: leeleeleeleejun Date: Tue, 24 Feb 2026 18:59:32 +0900 Subject: [PATCH 21/43] =?UTF-8?q?feat:=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=ED=9A=8C=EC=9B=90/=EB=B9=84?= =?UTF-8?q?=ED=9A=8C=EC=9B=90=20=EB=B6=84=EA=B8=B0=20=EB=B0=8F=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EA=B5=AC=EC=A1=B0=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/app/events/lucky-draw/LuckyDraw.tsx | 39 --------- .../_components/Pages/GuestView/GuestView.tsx | 85 +++++++++++++++++++ .../_components/Pages/GuestView/index.ts | 1 + .../FinishedEvent/FinishedEvent.tsx | 0 .../{ => MemberView}/FinishedEvent/index.ts | 0 .../InProgressEvent/EntryTicketModal.tsx | 0 .../InProgressEvent/EventCountdown.tsx | 0 .../InProgressEvent/EventEntryAction.tsx | 0 .../InProgressEvent/PrizeInfo.tsx | 0 .../InProgressEvent/RemainingTickets.tsx | 0 .../InProgressEvent/index.tsx | 0 .../Pages/MemberView/MemberView.tsx | 38 +++++++++ .../_components/Pages/MemberView/index.ts | 1 + .../lucky-draw/_components/Pages/index.tsx | 2 - apps/web/app/events/lucky-draw/page.tsx | 26 +++--- 15 files changed, 137 insertions(+), 55 deletions(-) delete mode 100644 apps/web/app/events/lucky-draw/LuckyDraw.tsx create mode 100644 apps/web/app/events/lucky-draw/_components/Pages/GuestView/GuestView.tsx create mode 100644 apps/web/app/events/lucky-draw/_components/Pages/GuestView/index.ts rename apps/web/app/events/lucky-draw/_components/Pages/{ => MemberView}/FinishedEvent/FinishedEvent.tsx (100%) rename apps/web/app/events/lucky-draw/_components/Pages/{ => MemberView}/FinishedEvent/index.ts (100%) rename apps/web/app/events/lucky-draw/_components/Pages/{ => MemberView}/InProgressEvent/EntryTicketModal.tsx (100%) rename apps/web/app/events/lucky-draw/_components/Pages/{ => MemberView}/InProgressEvent/EventCountdown.tsx (100%) rename apps/web/app/events/lucky-draw/_components/Pages/{ => MemberView}/InProgressEvent/EventEntryAction.tsx (100%) rename apps/web/app/events/lucky-draw/_components/Pages/{ => MemberView}/InProgressEvent/PrizeInfo.tsx (100%) rename apps/web/app/events/lucky-draw/_components/Pages/{ => MemberView}/InProgressEvent/RemainingTickets.tsx (100%) rename apps/web/app/events/lucky-draw/_components/Pages/{ => MemberView}/InProgressEvent/index.tsx (100%) create mode 100644 apps/web/app/events/lucky-draw/_components/Pages/MemberView/MemberView.tsx create mode 100644 apps/web/app/events/lucky-draw/_components/Pages/MemberView/index.ts delete mode 100644 apps/web/app/events/lucky-draw/_components/Pages/index.tsx diff --git a/apps/web/app/events/lucky-draw/LuckyDraw.tsx b/apps/web/app/events/lucky-draw/LuckyDraw.tsx deleted file mode 100644 index ba7c9303..00000000 --- a/apps/web/app/events/lucky-draw/LuckyDraw.tsx +++ /dev/null @@ -1,39 +0,0 @@ -'use client' - -import { useState } from 'react' -import { Column, JustifyBetween } from '@repo/ui/components/Layout' -import { NavBarItem } from './_components/NavBarItem' -import { InProgressEvent, FinishedEvent } from './_components/Pages' - -export type StepType = 'inProgress' | 'finished' - -const STEP_NAME = { - inProgress: '진행중인 이벤트', - finished: '종료된 이벤트', -} -const TABS: StepType[] = ['inProgress', 'finished'] - -export const LuckyDraw = () => { - const [currentTab, setCurrentTab] = useState('inProgress') - - return ( - <> - - - {TABS.map((tab) => ( - { - setCurrentTab(tab) - }} - /> - ))} - - {currentTab === 'inProgress' && } - {currentTab === 'finished' && } - - - ) -} diff --git a/apps/web/app/events/lucky-draw/_components/Pages/GuestView/GuestView.tsx b/apps/web/app/events/lucky-draw/_components/Pages/GuestView/GuestView.tsx new file mode 100644 index 00000000..d7046b20 --- /dev/null +++ b/apps/web/app/events/lucky-draw/_components/Pages/GuestView/GuestView.tsx @@ -0,0 +1,85 @@ +'use client' + +import Image from 'next/image' +import Link from 'next/link' +import { useSuspenseQuery } from '@tanstack/react-query' +import { useEventQueries } from '@/_apis/queries/event' +import { EmptyEventState } from '../EmptyEventState' +import { Column, Flex } from '@repo/ui/components/Layout' +import { Text } from '@repo/ui/components/Text' +import { Icon } from '@repo/ui/components/Icon' +import { Button } from '@repo/ui/components/Button' +import { CLIENT_PATH } from '@/_constants/path' + +export const GuestView = () => { + const { data } = useSuspenseQuery(useEventQueries.publicInfo()) + const { prize } = data + + if (!prize) { + return + } + + return ( + + + <Prize {...prize} /> + <Button + as={Link} + href={CLIENT_PATH.LOGIN} + size={'medium'} + className={'w-full'} + > + 참여하기 + </Button> + </Column> + ) +} + +// 하위 컴포넌트 +const Title = () => ( + <Column className={'items-center gap-2'}> + <Text fontSize={'2xl'} fontWeight={'bold'}> + 근처 맛집을 간단하게 알리고 + </Text> + <Flex className={'gap-2'}> + <Icon type={'headerGift'} size={28} /> + <Text fontSize={'2xl'} fontWeight={'bold'}> + 기프티콘 응모권 까지!! + </Text> + <Icon type={'headerGift'} size={28} /> + </Flex> + <Text variant={'body3'} className={'mt-2 text-center text-gray-300'}> + 작은 정보가 행운의 기회가 될 수 있어요. + <br /> + 지금 바로 등록해보세요. + </Text> + </Column> +) + +const Prize = ({ + description, + imageUrl, +}: { + description: string + imageUrl: string +}) => { + return ( + <Column className={'gap-15 items-center'}> + <Image + src={imageUrl} + alt={description} + width={220} + height={220} + priority + /> + <Column className='items-center gap-1'> + <Text variant={'body1'} className='text-gray-400'> + 이번 주 행운의 상품 + </Text> + <Text variant={'heading2'} className='text-gray-800'> + {description} + </Text> + </Column> + </Column> + ) +} diff --git a/apps/web/app/events/lucky-draw/_components/Pages/GuestView/index.ts b/apps/web/app/events/lucky-draw/_components/Pages/GuestView/index.ts new file mode 100644 index 00000000..4b36b64d --- /dev/null +++ b/apps/web/app/events/lucky-draw/_components/Pages/GuestView/index.ts @@ -0,0 +1 @@ +export { GuestView } from './GuestView' diff --git a/apps/web/app/events/lucky-draw/_components/Pages/FinishedEvent/FinishedEvent.tsx b/apps/web/app/events/lucky-draw/_components/Pages/MemberView/FinishedEvent/FinishedEvent.tsx similarity index 100% rename from apps/web/app/events/lucky-draw/_components/Pages/FinishedEvent/FinishedEvent.tsx rename to apps/web/app/events/lucky-draw/_components/Pages/MemberView/FinishedEvent/FinishedEvent.tsx diff --git a/apps/web/app/events/lucky-draw/_components/Pages/FinishedEvent/index.ts b/apps/web/app/events/lucky-draw/_components/Pages/MemberView/FinishedEvent/index.ts similarity index 100% rename from apps/web/app/events/lucky-draw/_components/Pages/FinishedEvent/index.ts rename to apps/web/app/events/lucky-draw/_components/Pages/MemberView/FinishedEvent/index.ts diff --git a/apps/web/app/events/lucky-draw/_components/Pages/InProgressEvent/EntryTicketModal.tsx b/apps/web/app/events/lucky-draw/_components/Pages/MemberView/InProgressEvent/EntryTicketModal.tsx similarity index 100% rename from apps/web/app/events/lucky-draw/_components/Pages/InProgressEvent/EntryTicketModal.tsx rename to apps/web/app/events/lucky-draw/_components/Pages/MemberView/InProgressEvent/EntryTicketModal.tsx diff --git a/apps/web/app/events/lucky-draw/_components/Pages/InProgressEvent/EventCountdown.tsx b/apps/web/app/events/lucky-draw/_components/Pages/MemberView/InProgressEvent/EventCountdown.tsx similarity index 100% rename from apps/web/app/events/lucky-draw/_components/Pages/InProgressEvent/EventCountdown.tsx rename to apps/web/app/events/lucky-draw/_components/Pages/MemberView/InProgressEvent/EventCountdown.tsx diff --git a/apps/web/app/events/lucky-draw/_components/Pages/InProgressEvent/EventEntryAction.tsx b/apps/web/app/events/lucky-draw/_components/Pages/MemberView/InProgressEvent/EventEntryAction.tsx similarity index 100% rename from apps/web/app/events/lucky-draw/_components/Pages/InProgressEvent/EventEntryAction.tsx rename to apps/web/app/events/lucky-draw/_components/Pages/MemberView/InProgressEvent/EventEntryAction.tsx diff --git a/apps/web/app/events/lucky-draw/_components/Pages/InProgressEvent/PrizeInfo.tsx b/apps/web/app/events/lucky-draw/_components/Pages/MemberView/InProgressEvent/PrizeInfo.tsx similarity index 100% rename from apps/web/app/events/lucky-draw/_components/Pages/InProgressEvent/PrizeInfo.tsx rename to apps/web/app/events/lucky-draw/_components/Pages/MemberView/InProgressEvent/PrizeInfo.tsx diff --git a/apps/web/app/events/lucky-draw/_components/Pages/InProgressEvent/RemainingTickets.tsx b/apps/web/app/events/lucky-draw/_components/Pages/MemberView/InProgressEvent/RemainingTickets.tsx similarity index 100% rename from apps/web/app/events/lucky-draw/_components/Pages/InProgressEvent/RemainingTickets.tsx rename to apps/web/app/events/lucky-draw/_components/Pages/MemberView/InProgressEvent/RemainingTickets.tsx diff --git a/apps/web/app/events/lucky-draw/_components/Pages/InProgressEvent/index.tsx b/apps/web/app/events/lucky-draw/_components/Pages/MemberView/InProgressEvent/index.tsx similarity index 100% rename from apps/web/app/events/lucky-draw/_components/Pages/InProgressEvent/index.tsx rename to apps/web/app/events/lucky-draw/_components/Pages/MemberView/InProgressEvent/index.tsx diff --git a/apps/web/app/events/lucky-draw/_components/Pages/MemberView/MemberView.tsx b/apps/web/app/events/lucky-draw/_components/Pages/MemberView/MemberView.tsx new file mode 100644 index 00000000..c7f37ea1 --- /dev/null +++ b/apps/web/app/events/lucky-draw/_components/Pages/MemberView/MemberView.tsx @@ -0,0 +1,38 @@ +'use client' + +import { useState } from 'react' +import { Column, JustifyBetween } from '@repo/ui/components/Layout' +import { NavBarItem } from '@/events/lucky-draw/_components/NavBarItem' +import { InProgressEvent } from './InProgressEvent' +import { FinishedEvent } from './FinishedEvent' + +export type StepType = 'inProgress' | 'finished' + +const STEP_NAME = { + inProgress: '진행중인 이벤트', + finished: '종료된 이벤트', +} +const TABS: StepType[] = ['inProgress', 'finished'] + +export const MemberView = () => { + const [currentTab, setCurrentTab] = useState<StepType>('inProgress') + + return ( + <Column className={'h-full min-h-0 p-5'}> + <JustifyBetween as={'nav'} className={'gap-10'}> + {TABS.map((tab) => ( + <NavBarItem + key={tab} + isActive={currentTab === tab} + name={STEP_NAME[tab]} + onClick={() => { + setCurrentTab(tab) + }} + /> + ))} + </JustifyBetween> + {currentTab === 'inProgress' && <InProgressEvent />} + {currentTab === 'finished' && <FinishedEvent />} + </Column> + ) +} diff --git a/apps/web/app/events/lucky-draw/_components/Pages/MemberView/index.ts b/apps/web/app/events/lucky-draw/_components/Pages/MemberView/index.ts new file mode 100644 index 00000000..1699787a --- /dev/null +++ b/apps/web/app/events/lucky-draw/_components/Pages/MemberView/index.ts @@ -0,0 +1 @@ +export { MemberView } from './MemberView' diff --git a/apps/web/app/events/lucky-draw/_components/Pages/index.tsx b/apps/web/app/events/lucky-draw/_components/Pages/index.tsx deleted file mode 100644 index d6768741..00000000 --- a/apps/web/app/events/lucky-draw/_components/Pages/index.tsx +++ /dev/null @@ -1,2 +0,0 @@ -export { InProgressEvent } from './InProgressEvent' -export { FinishedEvent } from './FinishedEvent' diff --git a/apps/web/app/events/lucky-draw/page.tsx b/apps/web/app/events/lucky-draw/page.tsx index 39cde34b..acc77092 100644 --- a/apps/web/app/events/lucky-draw/page.tsx +++ b/apps/web/app/events/lucky-draw/page.tsx @@ -1,19 +1,17 @@ -import { LuckyDraw } from './LuckyDraw' -import { HydrationBoundaryPage } from '@/_components/HydrationBoundaryPage' -import { useEventQueries } from '@/_apis/queries/event' +import { cookies } from 'next/headers' +import { MemberView } from './_components/Pages/MemberView' +import { GuestView } from './_components/Pages/GuestView' -export const dynamic = 'force-dynamic' - -const Page = () => { - return ( - <HydrationBoundaryPage - prefetch={async (queryClient) => { - await queryClient.prefetchQuery(useEventQueries.privateInfo()) - }} - > - <LuckyDraw /> - </HydrationBoundaryPage> +const Page = async () => { + const accessToken = await cookies().then( + (cookies) => cookies.get('accessToken')?.value, ) + + if (accessToken) { + return <MemberView /> + } else { + return <GuestView /> + } } export default Page From fe0c5d82ad97cfc38486d42a99d55d0675bede68 Mon Sep 17 00:00:00 2001 From: leeleeleeleejun <wnstjr6293@naver.com> Date: Tue, 24 Feb 2026 22:06:48 +0900 Subject: [PATCH 22/43] =?UTF-8?q?refactor:=20NavBarItem=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=ED=8C=8C=EC=9D=BC=20=EC=9D=B4?= =?UTF-8?q?=EB=8F=99=20-=20MemberView=20=ED=8F=B4=EB=8D=94=20=EB=82=B4?= =?UTF-8?q?=EB=B6=80=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lucky-draw/_components/Pages/MemberView/MemberView.tsx | 2 +- .../{ => Pages/MemberView}/NavBarItem/NavBarItem.tsx | 0 .../_components/{ => Pages/MemberView}/NavBarItem/index.tsx | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename apps/web/app/events/lucky-draw/_components/{ => Pages/MemberView}/NavBarItem/NavBarItem.tsx (100%) rename apps/web/app/events/lucky-draw/_components/{ => Pages/MemberView}/NavBarItem/index.tsx (100%) diff --git a/apps/web/app/events/lucky-draw/_components/Pages/MemberView/MemberView.tsx b/apps/web/app/events/lucky-draw/_components/Pages/MemberView/MemberView.tsx index c7f37ea1..8077d2a7 100644 --- a/apps/web/app/events/lucky-draw/_components/Pages/MemberView/MemberView.tsx +++ b/apps/web/app/events/lucky-draw/_components/Pages/MemberView/MemberView.tsx @@ -2,7 +2,7 @@ import { useState } from 'react' import { Column, JustifyBetween } from '@repo/ui/components/Layout' -import { NavBarItem } from '@/events/lucky-draw/_components/NavBarItem' +import { NavBarItem } from './NavBarItem' import { InProgressEvent } from './InProgressEvent' import { FinishedEvent } from './FinishedEvent' diff --git a/apps/web/app/events/lucky-draw/_components/NavBarItem/NavBarItem.tsx b/apps/web/app/events/lucky-draw/_components/Pages/MemberView/NavBarItem/NavBarItem.tsx similarity index 100% rename from apps/web/app/events/lucky-draw/_components/NavBarItem/NavBarItem.tsx rename to apps/web/app/events/lucky-draw/_components/Pages/MemberView/NavBarItem/NavBarItem.tsx diff --git a/apps/web/app/events/lucky-draw/_components/NavBarItem/index.tsx b/apps/web/app/events/lucky-draw/_components/Pages/MemberView/NavBarItem/index.tsx similarity index 100% rename from apps/web/app/events/lucky-draw/_components/NavBarItem/index.tsx rename to apps/web/app/events/lucky-draw/_components/Pages/MemberView/NavBarItem/index.tsx From 0c3e879ea5dad95df5077799d6225960662a2ddd Mon Sep 17 00:00:00 2001 From: leeleeleeleejun <wnstjr6293@naver.com> Date: Tue, 24 Feb 2026 22:14:33 +0900 Subject: [PATCH 23/43] =?UTF-8?q?fix:=20=EB=9F=AD=ED=82=A4=EB=93=9C?= =?UTF-8?q?=EB=A1=9C=EC=9A=B0=20=EA=B2=B0=EA=B3=BC=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20=EB=AF=B8=EB=93=A4=EC=9B=A8=EC=96=B4=20matcher?= =?UTF-8?q?=EC=97=90=20=EA=B2=BD=EB=A1=9C=20=EC=B6=94=EA=B0=80=20-=20/even?= =?UTF-8?q?ts/lucky-draw/result/:path*=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/middleware.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/web/middleware.ts b/apps/web/middleware.ts index 2df76b76..f706258d 100644 --- a/apps/web/middleware.ts +++ b/apps/web/middleware.ts @@ -25,6 +25,7 @@ export const config = { '/profile', '/requests', '/requests/:path*', + '/events/lucky-draw/result/:path*', '/events/gifticon', '/events/gifticon/:path*', ], From 4c99c78a6b07b5ca8b6d488e03bd81fc9dff5788 Mon Sep 17 00:00:00 2001 From: leeleeleeleejun <wnstjr6293@naver.com> Date: Tue, 24 Feb 2026 22:22:05 +0900 Subject: [PATCH 24/43] =?UTF-8?q?fix:=20'=EC=A2=85=EB=A3=8C=EB=90=9C=20?= =?UTF-8?q?=EC=9D=B4=EB=B2=A4=ED=8A=B8'=20=ED=83=AD=EB=AA=85=EC=9D=84=20'?= =?UTF-8?q?=EC=B0=B8=EC=97=AC=ED=96=88=EB=8D=98=20=EC=9D=B4=EB=B2=A4?= =?UTF-8?q?=ED=8A=B8'=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lucky-draw/_components/Pages/MemberView/MemberView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web/app/events/lucky-draw/_components/Pages/MemberView/MemberView.tsx b/apps/web/app/events/lucky-draw/_components/Pages/MemberView/MemberView.tsx index 8077d2a7..23678438 100644 --- a/apps/web/app/events/lucky-draw/_components/Pages/MemberView/MemberView.tsx +++ b/apps/web/app/events/lucky-draw/_components/Pages/MemberView/MemberView.tsx @@ -10,7 +10,7 @@ export type StepType = 'inProgress' | 'finished' const STEP_NAME = { inProgress: '진행중인 이벤트', - finished: '종료된 이벤트', + finished: '참여했던 이벤트', } const TABS: StepType[] = ['inProgress', 'finished'] From 5f830b13789297bff735bfd423db0cd76e49dd31 Mon Sep 17 00:00:00 2001 From: leeleeleeleejun <wnstjr6293@naver.com> Date: Thu, 26 Feb 2026 00:21:40 +0900 Subject: [PATCH 25/43] =?UTF-8?q?refactor:=20=EC=9D=B4=EB=B2=A4=ED=8A=B8?= =?UTF-8?q?=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20null=20=EC=B2=98=EB=A6=AC=20?= =?UTF-8?q?=EB=B0=A9=EC=8B=9D=20=EA=B0=9C=EC=84=A0=20=EB=B0=8F=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/app/_apis/schemas/event.ts | 34 ++++++++++--------- .../_components/Pages/GuestView/GuestView.tsx | 5 +-- .../InProgressEvent/InProgressEvent.tsx | 3 +- .../Step/EventWelcome/EventWelcome.tsx | 9 ++--- 4 files changed, 27 insertions(+), 24 deletions(-) diff --git a/apps/web/app/_apis/schemas/event.ts b/apps/web/app/_apis/schemas/event.ts index 0be138e8..f1f3e956 100644 --- a/apps/web/app/_apis/schemas/event.ts +++ b/apps/web/app/_apis/schemas/event.ts @@ -1,26 +1,28 @@ import { z } from 'zod' -export const PrizeSchema = z.nullable( +export const PrizeSchema = z.object({ + description: z.string(), + imageUrl: z.string(), +}) + +export const PublicEventSchema = z.nullable( z.object({ - description: z.string(), - imageUrl: z.string(), + prize: PrizeSchema, }), ) -export const PublicEventSchema = z.object({ - prize: PrizeSchema, -}) - // Todo: API에서 진행 중인 이벤트가 없는 경우에 대한 명확한 응답이 필요할 것 같습니다. (예: data: null 등) -export const PrivateEventSchema = z.object({ - eventId: z.nullable(z.number().transform(String)), - prize: PrizeSchema, - totalWinnersCount: z.number(), - participantsCount: z.number(), - usedTicketsCount: z.number(), - remainingTicketsCount: z.number(), - eventEndDate: z.nullable(z.string()), -}) +export const PrivateEventSchema = z.nullable( + z.object({ + eventId: z.number().transform(String), + prize: PrizeSchema, + totalWinnersCount: z.number(), + participantsCount: z.number(), + usedTicketsCount: z.number(), + remainingTicketsCount: z.number(), + eventEndDate: z.string(), + }), +) export const EventResultSchema = z.object({ eventId: z.string(), diff --git a/apps/web/app/events/lucky-draw/_components/Pages/GuestView/GuestView.tsx b/apps/web/app/events/lucky-draw/_components/Pages/GuestView/GuestView.tsx index d7046b20..d7cdb0f5 100644 --- a/apps/web/app/events/lucky-draw/_components/Pages/GuestView/GuestView.tsx +++ b/apps/web/app/events/lucky-draw/_components/Pages/GuestView/GuestView.tsx @@ -13,12 +13,13 @@ import { CLIENT_PATH } from '@/_constants/path' export const GuestView = () => { const { data } = useSuspenseQuery(useEventQueries.publicInfo()) - const { prize } = data - if (!prize) { + if (!data) { return <EmptyEventState /> } + const { prize } = data + return ( <Column className={'h-full min-h-0 justify-between gap-10 p-5'}> <Title /> diff --git a/apps/web/app/events/lucky-draw/_components/Pages/MemberView/InProgressEvent/InProgressEvent.tsx b/apps/web/app/events/lucky-draw/_components/Pages/MemberView/InProgressEvent/InProgressEvent.tsx index 38cefd4c..e11a3897 100644 --- a/apps/web/app/events/lucky-draw/_components/Pages/MemberView/InProgressEvent/InProgressEvent.tsx +++ b/apps/web/app/events/lucky-draw/_components/Pages/MemberView/InProgressEvent/InProgressEvent.tsx @@ -17,8 +17,7 @@ export const InProgressEvent = () => { const { data } = useSuspenseQuery(useEventQueries.privateInfo()) // 진행 중인 이벤트가 없는 경우 - // Todo: API에서 진행 중인 이벤트가 없는 경우에 대한 명확한 응답이 필요 (예: data: null 등) - if (!data.eventId || !data.prize || !data.eventEndDate) { + if (!data) { return <EmptyEventState /> } diff --git a/apps/web/app/places/new/_components/Step/EventWelcome/EventWelcome.tsx b/apps/web/app/places/new/_components/Step/EventWelcome/EventWelcome.tsx index d21e4cd5..86525cda 100644 --- a/apps/web/app/places/new/_components/Step/EventWelcome/EventWelcome.tsx +++ b/apps/web/app/places/new/_components/Step/EventWelcome/EventWelcome.tsx @@ -40,18 +40,19 @@ const itemVariants: Variants = { export const EventWelcome = ({ nextStep }: Props) => { const { replace } = useRouter() const { data } = useSuspenseQuery(useEventQueries.publicInfo()) - const { prize } = data useEffect(() => { - if (!prize) { + if (!data) { replace(`${CLIENT_PATH.PLACE_NEW}?step=1`) } - }, [replace, prize]) + }, [replace, data]) - if (!prize) { + if (!data) { return null } + const { prize } = data + return ( <VerticalScrollArea as={motion.div} From 3383d157eef0b1663bb083cf278b520cd70b9e39 Mon Sep 17 00:00:00 2001 From: leeleeleeleejun <wnstjr6293@naver.com> Date: Thu, 26 Feb 2026 13:01:48 +0900 Subject: [PATCH 26/43] =?UTF-8?q?docs:=20=EC=A7=84=ED=96=89=20=EC=A4=91?= =?UTF-8?q?=EC=9D=B8=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=20=EC=97=86=EC=9D=84=20?= =?UTF-8?q?=EC=8B=9C=20=EC=B2=AB=20=EB=B2=88=EC=A7=B8=20=EB=8B=A8=EA=B3=84?= =?UTF-8?q?=EB=A1=9C=20=EC=9E=90=EB=8F=99=20=EB=A6=AC=EB=8B=A4=EC=9D=B4?= =?UTF-8?q?=EB=A0=89=ED=8A=B8=20=EC=A3=BC=EC=84=9D=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../places/new/_components/Step/EventWelcome/EventWelcome.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/web/app/places/new/_components/Step/EventWelcome/EventWelcome.tsx b/apps/web/app/places/new/_components/Step/EventWelcome/EventWelcome.tsx index 86525cda..f0112018 100644 --- a/apps/web/app/places/new/_components/Step/EventWelcome/EventWelcome.tsx +++ b/apps/web/app/places/new/_components/Step/EventWelcome/EventWelcome.tsx @@ -41,6 +41,7 @@ export const EventWelcome = ({ nextStep }: Props) => { const { replace } = useRouter() const { data } = useSuspenseQuery(useEventQueries.publicInfo()) + // 진행 중인 이벤트가 없는 경우, 첫 번째 단계로 리다이렉트 useEffect(() => { if (!data) { replace(`${CLIENT_PATH.PLACE_NEW}?step=1`) From d06717164de7a6427234e30de91721af9ead6c1a Mon Sep 17 00:00:00 2001 From: leeleeleeleejun <wnstjr6293@naver.com> Date: Thu, 26 Feb 2026 13:17:58 +0900 Subject: [PATCH 27/43] =?UTF-8?q?feat:=20=EC=B0=B8=EC=97=AC=ED=96=88?= =?UTF-8?q?=EB=8D=98=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=20=EB=AA=A9=EB=A1=9D=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20API=20=EB=B0=8F=20=EC=8A=A4=ED=82=A4?= =?UTF-8?q?=EB=A7=88=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/app/_apis/queries/event.ts | 7 +++++++ apps/web/app/_apis/schemas/event.ts | 10 +++++++++- apps/web/app/_apis/services/event.ts | 7 +++++++ apps/web/app/_constants/path.ts | 2 +- 4 files changed, 24 insertions(+), 2 deletions(-) diff --git a/apps/web/app/_apis/queries/event.ts b/apps/web/app/_apis/queries/event.ts index 7cc3d0f2..e8005104 100644 --- a/apps/web/app/_apis/queries/event.ts +++ b/apps/web/app/_apis/queries/event.ts @@ -3,12 +3,14 @@ import { getPublicEventInfo, getPrivateEventInfo, getEventResult, + getEntriesEvent, } from '@/_apis/services/event' export const EventQueryKeys = { all: () => ['event'] as const, publicInfo: () => [...EventQueryKeys.all(), 'info', 'public'] as const, privateInfo: () => [...EventQueryKeys.all(), 'info', 'private'] as const, + entry: () => [...EventQueryKeys.all(), 'entry'] as const, result: (eventId: string) => [...EventQueryKeys.all(), 'result', eventId] as const, } @@ -24,6 +26,11 @@ export const useEventQueries = { queryKey: EventQueryKeys.privateInfo(), queryFn: getPrivateEventInfo, }), + entry: () => + queryOptions({ + queryKey: EventQueryKeys.entry(), + queryFn: getEntriesEvent, + }), result: (eventId: string) => queryOptions({ queryKey: EventQueryKeys.result(eventId), diff --git a/apps/web/app/_apis/schemas/event.ts b/apps/web/app/_apis/schemas/event.ts index f1f3e956..7910a8a8 100644 --- a/apps/web/app/_apis/schemas/event.ts +++ b/apps/web/app/_apis/schemas/event.ts @@ -11,7 +11,6 @@ export const PublicEventSchema = z.nullable( }), ) -// Todo: API에서 진행 중인 이벤트가 없는 경우에 대한 명확한 응답이 필요할 것 같습니다. (예: data: null 등) export const PrivateEventSchema = z.nullable( z.object({ eventId: z.number().transform(String), @@ -24,6 +23,14 @@ export const PrivateEventSchema = z.nullable( }), ) +export const EntryEventSchema = z.object({ + eventId: z.string(), + prize: PrizeSchema, + totalWinnersCount: z.number(), + participantsCount: z.number(), + eventEndDate: z.string(), +}) + export const EventResultSchema = z.object({ eventId: z.string(), isWinner: z.boolean(), @@ -43,5 +50,6 @@ export const WinnerPhoneNumberSchema = z.object({ export type PublicEvent = z.infer<typeof PublicEventSchema> export type PrivateEvent = z.infer<typeof PrivateEventSchema> +export type EntryEvent = z.infer<typeof EntryEventSchema> export type EventResult = z.infer<typeof EventResultSchema> export type WinnerPhoneNumber = z.infer<typeof WinnerPhoneNumberSchema> diff --git a/apps/web/app/_apis/services/event.ts b/apps/web/app/_apis/services/event.ts index 1bf5b887..1a5d915a 100644 --- a/apps/web/app/_apis/services/event.ts +++ b/apps/web/app/_apis/services/event.ts @@ -3,7 +3,9 @@ import { API_PATH } from '@/_constants/path' import { PrivateEventSchema, PublicEventSchema, + EntryEventSchema, EventResultSchema, + type EntryEvent, type EventResult, type PrivateEvent, type PublicEvent, @@ -27,6 +29,11 @@ export const participationEvent = async (body: { return data } +export const getEntriesEvent = async (): Promise<EntryEvent[]> => { + const { data } = await axiosInstance.get(API_PATH.EVENT.ENTRIES) + return EntryEventSchema.array().parse(data) +} + export const getEventResult = async ( eventId: string, ): Promise<EventResult | null> => { diff --git a/apps/web/app/_constants/path.ts b/apps/web/app/_constants/path.ts index dca3b123..dcf0a3c1 100644 --- a/apps/web/app/_constants/path.ts +++ b/apps/web/app/_constants/path.ts @@ -36,7 +36,7 @@ export const API_PATH = { }, EVENT: { INFO: '/events', - PARTICIPATIONS: '/events/entries', + ENTRIES: '/events/entries', RESULT: (eventId: string) => `/events/${eventId}/entries`, APPLY: (eventId: string) => `/events/${eventId}/apply`, }, From ebb1fc334ddf8fcfabfce7a5897dffb9cc8bcd76 Mon Sep 17 00:00:00 2001 From: leeleeleeleejun <wnstjr6293@naver.com> Date: Thu, 26 Feb 2026 13:33:39 +0900 Subject: [PATCH 28/43] =?UTF-8?q?refactor:=20=EC=9D=B4=EB=B2=A4=ED=8A=B8?= =?UTF-8?q?=20=EC=8A=A4=ED=82=A4=EB=A7=88=20=EB=B0=8F=20=ED=83=80=EC=9E=85?= =?UTF-8?q?=20=EB=AA=85=EC=B9=AD=20=EC=9D=BC=EA=B4=80=EC=84=B1=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0=20=EB=B0=8F=20=EA=B4=80=EB=A0=A8=20API=20=ED=95=A8?= =?UTF-8?q?=EC=88=98=EB=AA=85=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/app/_apis/schemas/event.ts | 43 +++++++++++++--------------- apps/web/app/_apis/services/event.ts | 24 ++++++++-------- 2 files changed, 32 insertions(+), 35 deletions(-) diff --git a/apps/web/app/_apis/schemas/event.ts b/apps/web/app/_apis/schemas/event.ts index 7910a8a8..65dd07f3 100644 --- a/apps/web/app/_apis/schemas/event.ts +++ b/apps/web/app/_apis/schemas/event.ts @@ -1,44 +1,41 @@ import { z } from 'zod' -export const PrizeSchema = z.object({ +export const EventPrizeSchema = z.object({ description: z.string(), imageUrl: z.string(), }) -export const PublicEventSchema = z.nullable( +export const BaseEventSchema = z.object({ + eventId: z.number().transform(String), + prize: EventPrizeSchema, + totalWinnersCount: z.number(), + participantsCount: z.number(), + eventEndDate: z.string(), +}) + +export const EventByPublicSchema = z.nullable( z.object({ - prize: PrizeSchema, + prize: EventPrizeSchema, }), ) -export const PrivateEventSchema = z.nullable( - z.object({ - eventId: z.number().transform(String), - prize: PrizeSchema, - totalWinnersCount: z.number(), - participantsCount: z.number(), +export const EventByPrivateSchema = z.nullable( + BaseEventSchema.extend({ usedTicketsCount: z.number(), remainingTicketsCount: z.number(), - eventEndDate: z.string(), }), ) -export const EntryEventSchema = z.object({ - eventId: z.string(), - prize: PrizeSchema, - totalWinnersCount: z.number(), - participantsCount: z.number(), - eventEndDate: z.string(), -}) +export const EventEntrySchema = BaseEventSchema export const EventResultSchema = z.object({ - eventId: z.string(), + eventId: z.number().transform(String), isWinner: z.boolean(), participantsCount: z.number(), usedTicketsCount: z.number(), }) -export const WinnerPhoneNumberSchema = z.object({ +export const EventWinnerPhoneSchema = z.object({ phoneNumber: z .string() .min(1, '전화번호를 입력해주세요.') @@ -48,8 +45,8 @@ export const WinnerPhoneNumberSchema = z.object({ ), }) -export type PublicEvent = z.infer<typeof PublicEventSchema> -export type PrivateEvent = z.infer<typeof PrivateEventSchema> -export type EntryEvent = z.infer<typeof EntryEventSchema> +export type EventByPublic = z.infer<typeof EventByPublicSchema> +export type EventByPrivate = z.infer<typeof EventByPrivateSchema> +export type EventEntry = z.infer<typeof EventEntrySchema> export type EventResult = z.infer<typeof EventResultSchema> -export type WinnerPhoneNumber = z.infer<typeof WinnerPhoneNumberSchema> +export type EventWinnerPhone = z.infer<typeof EventWinnerPhoneSchema> diff --git a/apps/web/app/_apis/services/event.ts b/apps/web/app/_apis/services/event.ts index 1a5d915a..7a58949c 100644 --- a/apps/web/app/_apis/services/event.ts +++ b/apps/web/app/_apis/services/event.ts @@ -1,24 +1,24 @@ import axiosInstance from '@/_lib/axiosInstance' import { API_PATH } from '@/_constants/path' import { - PrivateEventSchema, - PublicEventSchema, - EntryEventSchema, + EventByPrivateSchema, + EventByPublicSchema, + EventEntrySchema, EventResultSchema, - type EntryEvent, + type EventEntry, type EventResult, - type PrivateEvent, - type PublicEvent, + type EventByPrivate, + type EventByPublic, } from '@/_apis/schemas/event' -export const getPublicEventInfo = async (): Promise<PublicEvent> => { +export const getPublicEventInfo = async (): Promise<EventByPublic> => { const { data } = await axiosInstance.get(API_PATH.EVENT.INFO) - return PublicEventSchema.parse(data) + return EventByPublicSchema.parse(data) } -export const getPrivateEventInfo = async (): Promise<PrivateEvent> => { +export const getPrivateEventInfo = async (): Promise<EventByPrivate> => { const { data } = await axiosInstance.get(API_PATH.EVENT.INFO) - return PrivateEventSchema.parse(data) + return EventByPrivateSchema.parse(data) } export const participationEvent = async (body: { @@ -29,9 +29,9 @@ export const participationEvent = async (body: { return data } -export const getEntriesEvent = async (): Promise<EntryEvent[]> => { +export const getEventByEntries = async (): Promise<EventEntry[]> => { const { data } = await axiosInstance.get(API_PATH.EVENT.ENTRIES) - return EntryEventSchema.array().parse(data) + return EventEntrySchema.array().parse(data) } export const getEventResult = async ( From cd89e0b8f5e7c9c86553f9d425e44dda807810b0 Mon Sep 17 00:00:00 2001 From: leeleeleeleejun <wnstjr6293@naver.com> Date: Thu, 26 Feb 2026 13:37:44 +0900 Subject: [PATCH 29/43] =?UTF-8?q?refactor:=20=EC=9D=B4=EB=B2=A4=ED=8A=B8?= =?UTF-8?q?=20=EC=8A=A4=ED=82=A4=EB=A7=88=20=EB=B0=8F=20=ED=83=80=EC=9E=85?= =?UTF-8?q?=20=EB=AA=85=EC=B9=AD=20=EC=9D=BC=EA=B4=80=EC=84=B1=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0=20=EB=B0=8F=20=EA=B4=80=EB=A0=A8=20API=20=ED=95=A8?= =?UTF-8?q?=EC=88=98=EB=AA=85=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/app/_apis/queries/event.ts | 28 +++++++++---------- apps/web/app/_apis/services/event.ts | 4 +-- .../_components/Pages/GuestView/GuestView.tsx | 2 +- .../InProgressEvent/InProgressEvent.tsx | 2 +- .../Step/EventWelcome/EventWelcome.tsx | 2 +- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/apps/web/app/_apis/queries/event.ts b/apps/web/app/_apis/queries/event.ts index e8005104..aa8cffd2 100644 --- a/apps/web/app/_apis/queries/event.ts +++ b/apps/web/app/_apis/queries/event.ts @@ -1,35 +1,35 @@ import { queryOptions } from '@tanstack/react-query' import { - getPublicEventInfo, - getPrivateEventInfo, + getEventByPublic, + getEventByPrivate, getEventResult, - getEntriesEvent, + getEventByEntries, } from '@/_apis/services/event' export const EventQueryKeys = { all: () => ['event'] as const, - publicInfo: () => [...EventQueryKeys.all(), 'info', 'public'] as const, - privateInfo: () => [...EventQueryKeys.all(), 'info', 'private'] as const, - entry: () => [...EventQueryKeys.all(), 'entry'] as const, + byPublic: () => [...EventQueryKeys.all(), 'info', 'public'] as const, + byPrivate: () => [...EventQueryKeys.all(), 'info', 'private'] as const, + byEntry: () => [...EventQueryKeys.all(), 'entry'] as const, result: (eventId: string) => [...EventQueryKeys.all(), 'result', eventId] as const, } export const useEventQueries = { - publicInfo: () => + byPublic: () => queryOptions({ - queryKey: EventQueryKeys.publicInfo(), - queryFn: getPublicEventInfo, + queryKey: EventQueryKeys.byPublic(), + queryFn: getEventByPublic, }), - privateInfo: () => + byPrivate: () => queryOptions({ - queryKey: EventQueryKeys.privateInfo(), - queryFn: getPrivateEventInfo, + queryKey: EventQueryKeys.byPrivate(), + queryFn: getEventByPrivate, }), - entry: () => + byEntry: () => queryOptions({ queryKey: EventQueryKeys.entry(), - queryFn: getEntriesEvent, + queryFn: getEventByEntries, }), result: (eventId: string) => queryOptions({ diff --git a/apps/web/app/_apis/services/event.ts b/apps/web/app/_apis/services/event.ts index 7a58949c..ba7df3ed 100644 --- a/apps/web/app/_apis/services/event.ts +++ b/apps/web/app/_apis/services/event.ts @@ -11,12 +11,12 @@ import { type EventByPublic, } from '@/_apis/schemas/event' -export const getPublicEventInfo = async (): Promise<EventByPublic> => { +export const getEventByPublic = async (): Promise<EventByPublic> => { const { data } = await axiosInstance.get(API_PATH.EVENT.INFO) return EventByPublicSchema.parse(data) } -export const getPrivateEventInfo = async (): Promise<EventByPrivate> => { +export const getEventByPrivate = async (): Promise<EventByPrivate> => { const { data } = await axiosInstance.get(API_PATH.EVENT.INFO) return EventByPrivateSchema.parse(data) } diff --git a/apps/web/app/events/lucky-draw/_components/Pages/GuestView/GuestView.tsx b/apps/web/app/events/lucky-draw/_components/Pages/GuestView/GuestView.tsx index d7cdb0f5..789debe7 100644 --- a/apps/web/app/events/lucky-draw/_components/Pages/GuestView/GuestView.tsx +++ b/apps/web/app/events/lucky-draw/_components/Pages/GuestView/GuestView.tsx @@ -12,7 +12,7 @@ import { Button } from '@repo/ui/components/Button' import { CLIENT_PATH } from '@/_constants/path' export const GuestView = () => { - const { data } = useSuspenseQuery(useEventQueries.publicInfo()) + const { data } = useSuspenseQuery(useEventQueries.byPublic()) if (!data) { return <EmptyEventState /> diff --git a/apps/web/app/events/lucky-draw/_components/Pages/MemberView/InProgressEvent/InProgressEvent.tsx b/apps/web/app/events/lucky-draw/_components/Pages/MemberView/InProgressEvent/InProgressEvent.tsx index e11a3897..6a380306 100644 --- a/apps/web/app/events/lucky-draw/_components/Pages/MemberView/InProgressEvent/InProgressEvent.tsx +++ b/apps/web/app/events/lucky-draw/_components/Pages/MemberView/InProgressEvent/InProgressEvent.tsx @@ -14,7 +14,7 @@ import { EmptyEventState } from '../../EmptyEventState' export const InProgressEvent = () => { const { isOpen, onOpen, onOpenChange } = useDisclosure() - const { data } = useSuspenseQuery(useEventQueries.privateInfo()) + const { data } = useSuspenseQuery(useEventQueries.byPrivate()) // 진행 중인 이벤트가 없는 경우 if (!data) { diff --git a/apps/web/app/places/new/_components/Step/EventWelcome/EventWelcome.tsx b/apps/web/app/places/new/_components/Step/EventWelcome/EventWelcome.tsx index f0112018..528b4be5 100644 --- a/apps/web/app/places/new/_components/Step/EventWelcome/EventWelcome.tsx +++ b/apps/web/app/places/new/_components/Step/EventWelcome/EventWelcome.tsx @@ -39,7 +39,7 @@ const itemVariants: Variants = { export const EventWelcome = ({ nextStep }: Props) => { const { replace } = useRouter() - const { data } = useSuspenseQuery(useEventQueries.publicInfo()) + const { data } = useSuspenseQuery(useEventQueries.byPublic()) // 진행 중인 이벤트가 없는 경우, 첫 번째 단계로 리다이렉트 useEffect(() => { From f1d51ce3d09dfdb007f41d7828bdcde631577a7d Mon Sep 17 00:00:00 2001 From: leeleeleeleejun <wnstjr6293@naver.com> Date: Thu, 26 Feb 2026 13:38:57 +0900 Subject: [PATCH 30/43] =?UTF-8?q?refactor:=20=EC=9D=B4=EB=B2=A4=ED=8A=B8?= =?UTF-8?q?=20=EC=8A=A4=ED=82=A4=EB=A7=88=20=EB=B0=8F=20=ED=83=80=EC=9E=85?= =?UTF-8?q?=20=EB=AA=85=EC=B9=AD=20=EC=9D=BC=EA=B4=80=EC=84=B1=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0=20=EB=B0=8F=20=EA=B4=80=EB=A0=A8=20API=20=ED=95=A8?= =?UTF-8?q?=EC=88=98=EB=AA=85=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/app/_apis/schemas/event.ts | 2 +- apps/web/app/_apis/services/event.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/web/app/_apis/schemas/event.ts b/apps/web/app/_apis/schemas/event.ts index 65dd07f3..7f56b67c 100644 --- a/apps/web/app/_apis/schemas/event.ts +++ b/apps/web/app/_apis/schemas/event.ts @@ -47,6 +47,6 @@ export const EventWinnerPhoneSchema = z.object({ export type EventByPublic = z.infer<typeof EventByPublicSchema> export type EventByPrivate = z.infer<typeof EventByPrivateSchema> -export type EventEntry = z.infer<typeof EventEntrySchema> +export type EventByEntry = z.infer<typeof EventEntrySchema> export type EventResult = z.infer<typeof EventResultSchema> export type EventWinnerPhone = z.infer<typeof EventWinnerPhoneSchema> diff --git a/apps/web/app/_apis/services/event.ts b/apps/web/app/_apis/services/event.ts index ba7df3ed..1310da13 100644 --- a/apps/web/app/_apis/services/event.ts +++ b/apps/web/app/_apis/services/event.ts @@ -5,7 +5,7 @@ import { EventByPublicSchema, EventEntrySchema, EventResultSchema, - type EventEntry, + type EventByEntry, type EventResult, type EventByPrivate, type EventByPublic, @@ -29,7 +29,7 @@ export const participationEvent = async (body: { return data } -export const getEventByEntries = async (): Promise<EventEntry[]> => { +export const getEventByEntries = async (): Promise<EventByEntry[]> => { const { data } = await axiosInstance.get(API_PATH.EVENT.ENTRIES) return EventEntrySchema.array().parse(data) } From f4e1607e646d5f18731a67c865f64babc6126a58 Mon Sep 17 00:00:00 2001 From: leeleeleeleejun <wnstjr6293@naver.com> Date: Thu, 26 Feb 2026 14:34:28 +0900 Subject: [PATCH 31/43] =?UTF-8?q?refactor:=20=EC=9D=B4=EB=B2=A4=ED=8A=B8?= =?UTF-8?q?=20=EC=97=94=ED=8A=B8=EB=A6=AC=20=EA=B4=80=EB=A0=A8=20API=20?= =?UTF-8?q?=EA=B2=BD=EB=A1=9C=20=EB=B0=8F=20=EC=BF=BC=EB=A6=AC=20=ED=82=A4?= =?UTF-8?q?=20=EB=AA=85=EC=B9=AD=20=EC=9D=BC=EA=B4=80=EC=84=B1=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/app/_apis/mutations/useParticipationEvent.ts | 2 +- apps/web/app/_apis/queries/event.ts | 2 +- apps/web/app/_apis/services/event.ts | 2 +- .../[id]/_components/WinnerInfoForm/WinnerInfoForm.tsx | 10 +++++----- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/web/app/_apis/mutations/useParticipationEvent.ts b/apps/web/app/_apis/mutations/useParticipationEvent.ts index dae03c74..bdd49f2c 100644 --- a/apps/web/app/_apis/mutations/useParticipationEvent.ts +++ b/apps/web/app/_apis/mutations/useParticipationEvent.ts @@ -13,7 +13,7 @@ export const useParticipationEvent = () => { }, onSuccess: () => { queryClient.invalidateQueries({ - queryKey: EventQueryKeys.privateInfo(), + queryKey: EventQueryKeys.byPrivate(), }) addToast({ title: '응모가 성공적으로 완료되었습니다!', diff --git a/apps/web/app/_apis/queries/event.ts b/apps/web/app/_apis/queries/event.ts index aa8cffd2..5451c7c7 100644 --- a/apps/web/app/_apis/queries/event.ts +++ b/apps/web/app/_apis/queries/event.ts @@ -28,7 +28,7 @@ export const useEventQueries = { }), byEntry: () => queryOptions({ - queryKey: EventQueryKeys.entry(), + queryKey: EventQueryKeys.byEntry(), queryFn: getEventByEntries, }), result: (eventId: string) => diff --git a/apps/web/app/_apis/services/event.ts b/apps/web/app/_apis/services/event.ts index 1310da13..2ed22cdf 100644 --- a/apps/web/app/_apis/services/event.ts +++ b/apps/web/app/_apis/services/event.ts @@ -25,7 +25,7 @@ export const participationEvent = async (body: { eventId: string ticketsCount: number }) => { - const { data } = await axiosInstance.post(API_PATH.EVENT.PARTICIPATIONS, body) + const { data } = await axiosInstance.post(API_PATH.EVENT.ENTRIES, body) return data } diff --git a/apps/web/app/events/lucky-draw/result/[id]/_components/WinnerInfoForm/WinnerInfoForm.tsx b/apps/web/app/events/lucky-draw/result/[id]/_components/WinnerInfoForm/WinnerInfoForm.tsx index 09a935ef..1c3198b4 100644 --- a/apps/web/app/events/lucky-draw/result/[id]/_components/WinnerInfoForm/WinnerInfoForm.tsx +++ b/apps/web/app/events/lucky-draw/result/[id]/_components/WinnerInfoForm/WinnerInfoForm.tsx @@ -2,8 +2,8 @@ import { useForm } from 'react-hook-form' import { zodResolver } from '@hookform/resolvers/zod' import { Input } from '@heroui/react' import { - type WinnerPhoneNumber, - WinnerPhoneNumberSchema, + type EventWinnerPhone, + EventWinnerPhoneSchema, } from '@/_apis/schemas/event' import { useSubmitWinnerPhoneNumber } from '@/_apis/mutations/useSubmitWinnerPhoneNumber' import { Column } from '@repo/ui/components/Layout' @@ -18,15 +18,15 @@ export const WinnerInfoForm = ({ eventId }: WinnerInfoFormProps) => { register, handleSubmit, formState: { errors, isSubmitting }, - } = useForm<WinnerPhoneNumber>({ - resolver: zodResolver(WinnerPhoneNumberSchema), + } = useForm<EventWinnerPhone>({ + resolver: zodResolver(EventWinnerPhoneSchema), }) const { mutateAsync: submitPhoneNumber } = useSubmitWinnerPhoneNumber({ eventId, }) - const onSubmit = async (data: WinnerPhoneNumber): Promise<void> => { + const onSubmit = async (data: EventWinnerPhone): Promise<void> => { await submitPhoneNumber(data.phoneNumber) // TODO: 성공 시 모달 닫기 처리 } From 49f083bbfe338572984a6efeb2d2805db266d910 Mon Sep 17 00:00:00 2001 From: leeleeleeleejun <wnstjr6293@naver.com> Date: Thu, 26 Feb 2026 14:36:44 +0900 Subject: [PATCH 32/43] =?UTF-8?q?feat:=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=97=94=ED=8A=B8=EB=A6=AC=20=EB=AA=A9=EB=A1=9D=20mock=20?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EB=B0=8F=20=ED=95=B8=EB=93=A4?= =?UTF-8?q?=EB=9F=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/app/_mocks/data/event.ts | 31 +++++++++++++++++-- apps/web/app/_mocks/handlers/eventHandlers.ts | 15 +++++---- 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/apps/web/app/_mocks/data/event.ts b/apps/web/app/_mocks/data/event.ts index eb3dba51..c7e1d057 100644 --- a/apps/web/app/_mocks/data/event.ts +++ b/apps/web/app/_mocks/data/event.ts @@ -1,6 +1,6 @@ -import type { PrivateEvent, EventResult } from '@/_apis/schemas/event' +import type { EventByPrivate, EventResult } from '@/_apis/schemas/event' -export const event: PrivateEvent = { +export const event: EventByPrivate = { eventId: '1', prize: { description: 'BHC 뿌링클 치킨 기프티콘 1장', @@ -20,3 +20,30 @@ export const eventResult: EventResult = { participantsCount: 3, usedTicketsCount: 3, } + +export const EVENT_ENTRIES = { + data: [ + { + eventId: 1, + totalWinnersCount: 3, + participantsCount: 27, + prize: { + description: 'BHC 뿌링클 치킨 기프티콘 1장', + imageUrl: + 'https://search.pstatic.net/common/?src=https%3A%2F%2Fpup-review-phinf.pstatic.net%2FMjAyNTA3MjNfNzkg%2FMDAxNzUzMjYzMDcwMDI4.YZPG9RYk5jn6jJtaNcaba70NF9uqeD_o8hY4xJ9xc6Eg.mMpLZc8GqkgQAslKGy-gPgAjiktsTSUGuMPQbNdIDWYg.JPEG%2F20250723_182109.jpg', + }, + eventEndDate: '2025-12-18T00:00:00.000000', + }, + { + eventId: 2, + totalWinnersCount: 4, + participantsCount: 27, + prize: { + description: 'BHC 뿌링클 치킨 기프티콘 1장', + imageUrl: + 'https://search.pstatic.net/common/?src=https%3A%2F%2Fpup-review-phinf.pstatic.net%2FMjAyNTA3MjNfNzkg%2FMDAxNzUzMjYzMDcwMDI4.YZPG9RYk5jn6jJtaNcaba70NF9uqeD_o8hY4xJ9xc6Eg.mMpLZc8GqkgQAslKGy-gPgAjiktsTSUGuMPQbNdIDWYg.JPEG%2F20250723_182109.jpg', + }, + eventEndDate: '2025-12-18T00:00:00.000000', + }, + ], +} diff --git a/apps/web/app/_mocks/handlers/eventHandlers.ts b/apps/web/app/_mocks/handlers/eventHandlers.ts index 48c1b231..bab8216b 100644 --- a/apps/web/app/_mocks/handlers/eventHandlers.ts +++ b/apps/web/app/_mocks/handlers/eventHandlers.ts @@ -1,5 +1,5 @@ import { http, HttpResponse } from 'msw' -import { eventResult } from '../data/event' +import { EVENT_ENTRIES } from '../data/event' import { API_PATH } from '@/_constants/path' import { addBaseUrl } from './addBaseUrl' @@ -7,10 +7,13 @@ export const EventHandlers = [ // http.get(addBaseUrl(API_PATH.EVENT.INFO), () => { // return HttpResponse.json(event) // }), - http.post(addBaseUrl(API_PATH.EVENT.PARTICIPATIONS), () => { - return HttpResponse.json({ message: '성공' }) - }), - http.get(addBaseUrl('/events/:eventId/entries'), () => { - return HttpResponse.json(eventResult) + // http.post(addBaseUrl(API_PATH.EVENT.ENTRIES), () => { + // return HttpResponse.json({ message: '성공' }) + // }), + // http.get(addBaseUrl('/events/:eventId/entries'), () => { + // return HttpResponse.json(eventResult) + // }), + http.get(addBaseUrl(API_PATH.EVENT.ENTRIES), () => { + return HttpResponse.json(EVENT_ENTRIES) }), ] From ea51bf3bd51484e34eb58e4e5ec912ae21ca3c4d Mon Sep 17 00:00:00 2001 From: leeleeleeleejun <wnstjr6293@naver.com> Date: Thu, 26 Feb 2026 14:59:33 +0900 Subject: [PATCH 33/43] =?UTF-8?q?feat:=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=20?= =?UTF-8?q?=EB=AA=A9=EB=A1=9D=20=EB=A0=8C=EB=8D=94=EB=A7=81=EC=97=90=20Sus?= =?UTF-8?q?pense=20=EB=B0=8F=20Spinner=20=EB=A1=9C=EB=94=A9=20UI=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_components/Pages/MemberView/MemberView.tsx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/apps/web/app/events/lucky-draw/_components/Pages/MemberView/MemberView.tsx b/apps/web/app/events/lucky-draw/_components/Pages/MemberView/MemberView.tsx index 23678438..20b1e9dc 100644 --- a/apps/web/app/events/lucky-draw/_components/Pages/MemberView/MemberView.tsx +++ b/apps/web/app/events/lucky-draw/_components/Pages/MemberView/MemberView.tsx @@ -1,6 +1,7 @@ 'use client' -import { useState } from 'react' +import { Suspense, useState } from 'react' +import { Spinner } from '@heroui/react' import { Column, JustifyBetween } from '@repo/ui/components/Layout' import { NavBarItem } from './NavBarItem' import { InProgressEvent } from './InProgressEvent' @@ -31,8 +32,12 @@ export const MemberView = () => { /> ))} </JustifyBetween> - {currentTab === 'inProgress' && <InProgressEvent />} - {currentTab === 'finished' && <FinishedEvent />} + <Suspense fallback={<Spinner className={'m-auto'} />}> + {currentTab === 'inProgress' && <InProgressEvent />} + </Suspense> + <Suspense fallback={<Spinner className={'m-auto'} />}> + {currentTab === 'finished' && <FinishedEvent />} + </Suspense> </Column> ) } From 5659b41479eff76d06ce126933f19e957b766e6a Mon Sep 17 00:00:00 2001 From: leeleeleeleejun <wnstjr6293@naver.com> Date: Thu, 26 Feb 2026 15:04:31 +0900 Subject: [PATCH 34/43] =?UTF-8?q?feat:=20=EC=A2=85=EB=A3=8C=EB=90=9C=20?= =?UTF-8?q?=EC=9D=B4=EB=B2=A4=ED=8A=B8=20=EB=AA=A9=EB=A1=9D=20API=20?= =?UTF-8?q?=EC=97=B0=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/app/_constants/path.ts | 1 + .../FinishedEvent/FinishedEvent.tsx | 35 ++++++++++++------- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/apps/web/app/_constants/path.ts b/apps/web/app/_constants/path.ts index dcf0a3c1..2dc74085 100644 --- a/apps/web/app/_constants/path.ts +++ b/apps/web/app/_constants/path.ts @@ -67,6 +67,7 @@ export const CLIENT_PATH = { REQUEST_DETAIL: (id: string | number) => `/requests/${id}`, EVENTS_FOOD_SLOT: '/events/food-slot', EVENTS_LUCKY_DRAW: '/events/lucky-draw', + EVENTS_RESULT: (id: string | number) => `/events/lucky-draw/result/${id}`, EVENT_GIFTICON: '/events/gifticon', EVENT_GIFTICON_DETAIL: (id: string | number) => `/events/gifticon/${id}`, LOGIN: '/login', diff --git a/apps/web/app/events/lucky-draw/_components/Pages/MemberView/FinishedEvent/FinishedEvent.tsx b/apps/web/app/events/lucky-draw/_components/Pages/MemberView/FinishedEvent/FinishedEvent.tsx index b2e32ca1..eff9f103 100644 --- a/apps/web/app/events/lucky-draw/_components/Pages/MemberView/FinishedEvent/FinishedEvent.tsx +++ b/apps/web/app/events/lucky-draw/_components/Pages/MemberView/FinishedEvent/FinishedEvent.tsx @@ -1,48 +1,57 @@ import Link from 'next/link' import Image from 'next/image' +import { useSuspenseQuery } from '@tanstack/react-query' +import type { EventByEntry } from '@/_apis/schemas/event' +import { useEventQueries } from '@/_apis/queries/event' import { CLIENT_PATH } from '@/_constants/path' -import { Column, Flex } from '@repo/ui/components/Layout' import { Text } from '@repo/ui/components/Text' +import { Column, Flex } from '@repo/ui/components/Layout' import { EmptyFallback } from '@/_components/EmptyFallback' export const FinishedEvent = () => { + const { data } = useSuspenseQuery(useEventQueries.byEntry()) + return ( - // Todo: EmptyFallback isEmpty 값 API 연동 후 동적으로 변경 <EmptyFallback - isEmpty={true} + isEmpty={data.length === 0} fallbackTitle={'아직 종료된 이벤트가 없어요.'} fallbackDescription={'진행 중인 이벤트에 참여하고 행운을 잡아보세요!'} > <Column as={'ul'} className={'h-full overflow-y-auto py-2'}> - <FinishedEventItem /> - <FinishedEventItem /> + {data.map((item) => ( + <FinishedEventItem key={item.eventId} event={item} /> + ))} </Column> </EmptyFallback> ) } -const FinishedEventItem = () => { +// Todo: 따로 분리 후 EventSummary로 결과페이지에서 재사용 +const FinishedEventItem = ({ event }: { event: EventByEntry }) => { + const { eventId, prize, totalWinnersCount, participantsCount, eventEndDate } = + event + return ( <li className={'border-b-1 border-gray-100 py-3.5'}> <Flex as={Link} - // Todo: href 동적 라우팅으로 변경 - href={CLIENT_PATH.EVENTS_LUCKY_DRAW + '/result/1'} - className={'gap-2'} + href={CLIENT_PATH.EVENTS_RESULT(eventId)} + className={'gap-4'} > <Image - src={'/images/chicken.png'} + src={prize.imageUrl} alt={'종료된 이벤트 상품'} width={50} height={50} + className={'rounded-lg'} /> <Column> - <Text variant={'title3'}>BBQ 황금 올리브 치킨</Text> + <Text variant={'title3'}>{prize.description}</Text> <Text variant={'caption2'} className={'text-gray-300'}> - 당첨자 3명 | 참여자 27명 + 당첨자 {totalWinnersCount}명 | 참여자 {participantsCount}명 </Text> <Text variant={'caption2'} className={'text-gray-300'}> - 종료 일자: 2025.12.23 + 종료 일자: {eventEndDate.slice(0, 10).replace(/-/g, '.')} </Text> </Column> </Flex> From 062aa3e1dd62638153f6dd9abe01bb98059ef974 Mon Sep 17 00:00:00 2001 From: leeleeleeleejun <wnstjr6293@naver.com> Date: Thu, 26 Feb 2026 15:45:47 +0900 Subject: [PATCH 35/43] =?UTF-8?q?refactor:=20FinishedEventItem=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=EB=AA=85=EC=9D=84=20EventSummary?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Pages/MemberView/FinishedEvent/FinishedEvent.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/web/app/events/lucky-draw/_components/Pages/MemberView/FinishedEvent/FinishedEvent.tsx b/apps/web/app/events/lucky-draw/_components/Pages/MemberView/FinishedEvent/FinishedEvent.tsx index eff9f103..321fe3ad 100644 --- a/apps/web/app/events/lucky-draw/_components/Pages/MemberView/FinishedEvent/FinishedEvent.tsx +++ b/apps/web/app/events/lucky-draw/_components/Pages/MemberView/FinishedEvent/FinishedEvent.tsx @@ -19,15 +19,15 @@ export const FinishedEvent = () => { > <Column as={'ul'} className={'h-full overflow-y-auto py-2'}> {data.map((item) => ( - <FinishedEventItem key={item.eventId} event={item} /> + <EventSummary key={item.eventId} event={item} /> ))} </Column> </EmptyFallback> ) } -// Todo: 따로 분리 후 EventSummary로 결과페이지에서 재사용 -const FinishedEventItem = ({ event }: { event: EventByEntry }) => { +// Todo: 따로 분리 후 EventSummary로 결과페이지에서 재사용 고려 +const EventSummary = ({ event }: { event: EventByEntry }) => { const { eventId, prize, totalWinnersCount, participantsCount, eventEndDate } = event From 68b01a344f747580cba490bc00856aeb59fcf0a4 Mon Sep 17 00:00:00 2001 From: leeleeleeleejun <wnstjr6293@naver.com> Date: Thu, 26 Feb 2026 15:46:16 +0900 Subject: [PATCH 36/43] =?UTF-8?q?feat:=20EventResultClient=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=20=EC=83=81=ED=92=88=20?= =?UTF-8?q?=EC=9D=B4=EB=AF=B8=EC=A7=80=EC=97=90=20sizes,=20priority,=20cla?= =?UTF-8?q?ssName=20=EC=86=8D=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/events/lucky-draw/result/[id]/EventResultClient.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/web/app/events/lucky-draw/result/[id]/EventResultClient.tsx b/apps/web/app/events/lucky-draw/result/[id]/EventResultClient.tsx index 177b62fc..48054b6d 100644 --- a/apps/web/app/events/lucky-draw/result/[id]/EventResultClient.tsx +++ b/apps/web/app/events/lucky-draw/result/[id]/EventResultClient.tsx @@ -78,6 +78,9 @@ const EventSummary = () => { alt={'종료된 이벤트 상품'} width={80} height={80} + sizes={'auto'} + priority={true} + className={'rounded-lg'} /> <Column> <Text variant={'title3'}>BBQ 황금 올리브 치킨</Text> From 7a4bbf273d5047c20caafbb07e3b8bca1ab94b7f Mon Sep 17 00:00:00 2001 From: leeleeleeleejun <wnstjr6293@naver.com> Date: Thu, 26 Feb 2026 17:53:40 +0900 Subject: [PATCH 37/43] =?UTF-8?q?feat:=20=EB=8B=B9=EC=B2=A8=EC=9E=90=20?= =?UTF-8?q?=ED=8F=BC=20=EC=8A=A4=ED=82=A4=EB=A7=88=EC=97=90=20=EC=95=BD?= =?UTF-8?q?=EA=B4=80=20=EB=8F=99=EC=9D=98=20=EC=B6=94=EA=B0=80,=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=ED=95=A8=EC=88=98=20=EB=B0=8F=20=ED=9B=85?= =?UTF-8?q?=20=EB=A6=AC=ED=8C=A9=ED=84=B0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...itWinnerPhoneNumber.ts => useSubmitWinnerForm.ts} | 12 +++++------- apps/web/app/_apis/schemas/event.ts | 8 ++++++-- apps/web/app/_apis/services/event.ts | 10 ++++------ 3 files changed, 15 insertions(+), 15 deletions(-) rename apps/web/app/_apis/mutations/{useSubmitWinnerPhoneNumber.ts => useSubmitWinnerForm.ts} (64%) diff --git a/apps/web/app/_apis/mutations/useSubmitWinnerPhoneNumber.ts b/apps/web/app/_apis/mutations/useSubmitWinnerForm.ts similarity index 64% rename from apps/web/app/_apis/mutations/useSubmitWinnerPhoneNumber.ts rename to apps/web/app/_apis/mutations/useSubmitWinnerForm.ts index b9620fff..0ec944f6 100644 --- a/apps/web/app/_apis/mutations/useSubmitWinnerPhoneNumber.ts +++ b/apps/web/app/_apis/mutations/useSubmitWinnerForm.ts @@ -1,18 +1,16 @@ import { useMutation } from '@tanstack/react-query' import { addToast } from '@heroui/react' -import { submitWinnerPhoneNumber } from '../services/event' +import { submitWinnerForm } from '../services/event' +import type { EventWinnerForm } from '@/_apis/schemas/event' -interface UseSubmitWinnerPhoneNumberParams { +type UseSubmitWinnerFormParams = { eventId: string } -export const useSubmitWinnerPhoneNumber = ({ - eventId, -}: UseSubmitWinnerPhoneNumberParams) => { +export const useSubmitWinnerForm = ({ eventId }: UseSubmitWinnerFormParams) => { return useMutation({ - mutationFn: (phoneNumber: string) => - submitWinnerPhoneNumber(eventId, phoneNumber), + mutationFn: (data: EventWinnerForm) => submitWinnerForm(eventId, data), onSuccess: () => { addToast({ title: '전화번호가 성공적으로 제출되었습니다!', diff --git a/apps/web/app/_apis/schemas/event.ts b/apps/web/app/_apis/schemas/event.ts index 7f56b67c..67e55fb2 100644 --- a/apps/web/app/_apis/schemas/event.ts +++ b/apps/web/app/_apis/schemas/event.ts @@ -35,7 +35,7 @@ export const EventResultSchema = z.object({ usedTicketsCount: z.number(), }) -export const EventWinnerPhoneSchema = z.object({ +export const EventWinnerFormSchema = z.object({ phoneNumber: z .string() .min(1, '전화번호를 입력해주세요.') @@ -43,10 +43,14 @@ export const EventWinnerPhoneSchema = z.object({ /^010-\d{4}-\d{4}$/, '올바른 형식으로 입력해주세요 (예: 010-1234-5678)', ), + agreements: z.object({ + termsAgreed: z.boolean().refine((val) => val === true), + privacyAgreed: z.boolean().refine((val) => val === true), + }), }) export type EventByPublic = z.infer<typeof EventByPublicSchema> export type EventByPrivate = z.infer<typeof EventByPrivateSchema> export type EventByEntry = z.infer<typeof EventEntrySchema> export type EventResult = z.infer<typeof EventResultSchema> -export type EventWinnerPhone = z.infer<typeof EventWinnerPhoneSchema> +export type EventWinnerForm = z.infer<typeof EventWinnerFormSchema> diff --git a/apps/web/app/_apis/services/event.ts b/apps/web/app/_apis/services/event.ts index 2ed22cdf..12da2aa3 100644 --- a/apps/web/app/_apis/services/event.ts +++ b/apps/web/app/_apis/services/event.ts @@ -9,6 +9,7 @@ import { type EventResult, type EventByPrivate, type EventByPublic, + type EventWinnerForm, } from '@/_apis/schemas/event' export const getEventByPublic = async (): Promise<EventByPublic> => { @@ -41,12 +42,9 @@ export const getEventResult = async ( return EventResultSchema.parse(data) } -// TODO: API 명세 확정 후 (인자, 반환값) 명확히 하기 -export const submitWinnerPhoneNumber = async ( +export const submitWinnerForm = async ( eventId: string, - phoneNumber: string, + data: EventWinnerForm, ): Promise<void> => { - await axiosInstance.post(API_PATH.EVENT.APPLY(eventId), { - phoneNumber, - }) + await axiosInstance.post(API_PATH.EVENT.APPLY(eventId), data) } From 87781fbe55492aa571171e73eacff265ec533297 Mon Sep 17 00:00:00 2001 From: leeleeleeleejun <wnstjr6293@naver.com> Date: Thu, 26 Feb 2026 22:17:36 +0900 Subject: [PATCH 38/43] =?UTF-8?q?feat:=20=EB=8B=B9=EC=B2=A8=EC=9E=90=20?= =?UTF-8?q?=ED=8F=BC=EC=97=90=20=EC=95=BD=EA=B4=80=20=EB=8F=99=EC=9D=98=20?= =?UTF-8?q?UI=20=EB=B0=8F=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80,=20?= =?UTF-8?q?=EC=A0=9C=EC=B6=9C=20=EC=84=B1=EA=B3=B5=20=EC=8B=9C=20=EB=AA=A8?= =?UTF-8?q?=EB=8B=AC=20=EC=9E=90=EB=8F=99=20=EB=8B=AB=ED=9E=98=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lucky-draw/result/[id]/ResultModal.tsx | 24 ++-- .../WinnerInfoForm/TermAgreementCheckbox.tsx | 64 ++++++++++ .../WinnerInfoForm/WinnerInfoForm.tsx | 112 +++++++++++++----- .../_components/WinnerInfoForm/constants.ts | 36 ++++++ 4 files changed, 197 insertions(+), 39 deletions(-) create mode 100644 apps/web/app/events/lucky-draw/result/[id]/_components/WinnerInfoForm/TermAgreementCheckbox.tsx create mode 100644 apps/web/app/events/lucky-draw/result/[id]/_components/WinnerInfoForm/constants.ts diff --git a/apps/web/app/events/lucky-draw/result/[id]/ResultModal.tsx b/apps/web/app/events/lucky-draw/result/[id]/ResultModal.tsx index 2cb2e991..4f4042c2 100644 --- a/apps/web/app/events/lucky-draw/result/[id]/ResultModal.tsx +++ b/apps/web/app/events/lucky-draw/result/[id]/ResultModal.tsx @@ -74,17 +74,21 @@ export const ResultModal = ({ }} > <ModalContent> - <Column className={'items-center gap-2.5 px-5 py-8'}> - <Icon type={modalContent.icon} size={60} /> - <Text variant={'heading2'}>{modalContent.title}</Text> - <Column className={'items-center'}> - <Text fontWeight={'medium'}>{modalContent.subTitle}</Text> - <Text variant={'body1'} className={'text-gray-300'}> - {modalContent.description} - </Text> + {(onClose) => ( + <Column className={'items-center gap-2.5 px-5 py-8'}> + <Icon type={modalContent.icon} size={60} /> + <Text variant={'heading2'}>{modalContent.title}</Text> + <Column className={'items-center'}> + <Text fontWeight={'medium'}>{modalContent.subTitle}</Text> + <Text variant={'body1'} className={'text-gray-300'}> + {modalContent.description} + </Text> + </Column> + {isWinner && ( + <WinnerInfoForm eventId={eventId} onSuccess={onClose} /> + )} </Column> - {isWinner && <WinnerInfoForm eventId={eventId} />} - </Column> + )} </ModalContent> </Modal> ) diff --git a/apps/web/app/events/lucky-draw/result/[id]/_components/WinnerInfoForm/TermAgreementCheckbox.tsx b/apps/web/app/events/lucky-draw/result/[id]/_components/WinnerInfoForm/TermAgreementCheckbox.tsx new file mode 100644 index 00000000..c2a02bf6 --- /dev/null +++ b/apps/web/app/events/lucky-draw/result/[id]/_components/WinnerInfoForm/TermAgreementCheckbox.tsx @@ -0,0 +1,64 @@ +import { + Checkbox, + Modal, + ModalBody, + ModalContent, + ModalHeader, + useDisclosure, +} from '@heroui/react' +import { Text } from '@repo/ui/components/Text' +import { JustifyBetween } from '@repo/ui/components/Layout' + +type Props = { + isSelected: boolean + onChange: (isSelected: boolean) => void + label: string + disabled?: boolean + detailContent: string +} + +export const TermAgreementCheckbox = ({ + label, + disabled, + onChange, + isSelected, + detailContent, +}: Props) => { + const { isOpen, onOpen, onOpenChange } = useDisclosure() + + return ( + <> + <JustifyBetween className='w-full items-center'> + <Checkbox + isSelected={isSelected} + onValueChange={onChange} + disabled={disabled} + > + <Text variant={'body2'}>{label}</Text> + </Checkbox> + <button + type='button' + onClick={onOpen} + className='text-xs text-gray-500 underline' + > + 보기 + </button> + </JustifyBetween> + + {/* 약관 내용 모달 */} + <Modal isOpen={isOpen} onOpenChange={onOpenChange} isDismissable={false}> + <ModalContent> + <ModalHeader>{label}</ModalHeader> + <ModalBody> + <Text + variant={'body2'} + className={'whitespace-pre-wrap text-gray-400'} + > + {detailContent} + </Text> + </ModalBody> + </ModalContent> + </Modal> + </> + ) +} diff --git a/apps/web/app/events/lucky-draw/result/[id]/_components/WinnerInfoForm/WinnerInfoForm.tsx b/apps/web/app/events/lucky-draw/result/[id]/_components/WinnerInfoForm/WinnerInfoForm.tsx index 1c3198b4..f31f900b 100644 --- a/apps/web/app/events/lucky-draw/result/[id]/_components/WinnerInfoForm/WinnerInfoForm.tsx +++ b/apps/web/app/events/lucky-draw/result/[id]/_components/WinnerInfoForm/WinnerInfoForm.tsx @@ -1,54 +1,108 @@ -import { useForm } from 'react-hook-form' +import { Controller, useForm } from 'react-hook-form' import { zodResolver } from '@hookform/resolvers/zod' import { Input } from '@heroui/react' import { - type EventWinnerPhone, - EventWinnerPhoneSchema, + EventWinnerFormSchema, + type EventWinnerForm, } from '@/_apis/schemas/event' -import { useSubmitWinnerPhoneNumber } from '@/_apis/mutations/useSubmitWinnerPhoneNumber' +import { AGREEMENT } from './constants' +import { useSubmitWinnerForm } from '@/_apis/mutations/useSubmitWinnerForm' import { Column } from '@repo/ui/components/Layout' import { Button } from '@repo/ui/components/Button' +import { Text } from '@repo/ui/components/Text' +import { TermAgreementCheckbox } from './TermAgreementCheckbox' -interface WinnerInfoFormProps { +type Props = { eventId: string + onSuccess: VoidFunction } -export const WinnerInfoForm = ({ eventId }: WinnerInfoFormProps) => { +export const WinnerInfoForm = ({ eventId, onSuccess }: Props) => { const { register, handleSubmit, + control, formState: { errors, isSubmitting }, - } = useForm<EventWinnerPhone>({ - resolver: zodResolver(EventWinnerPhoneSchema), + } = useForm<EventWinnerForm>({ + resolver: zodResolver(EventWinnerFormSchema), + defaultValues: { + phoneNumber: '', + agreements: { termsAgreed: false, privacyAgreed: false }, + }, }) - const { mutateAsync: submitPhoneNumber } = useSubmitWinnerPhoneNumber({ + const { mutateAsync: submitPhoneNumber } = useSubmitWinnerForm({ eventId, }) - const onSubmit = async (data: EventWinnerPhone): Promise<void> => { - await submitPhoneNumber(data.phoneNumber) - // TODO: 성공 시 모달 닫기 처리 + const onSubmit = async (data: EventWinnerForm): Promise<void> => { + await submitPhoneNumber(data) + onSuccess() } return ( - <form onSubmit={handleSubmit(onSubmit)}> - <Column className={'items-center gap-2'}> - <Column className={'items-center gap-1'}> - <Input - type={'tel'} - placeholder={'010-1234-5678'} - className={'w-[270px]'} - isInvalid={!!errors.phoneNumber} - errorMessage={errors.phoneNumber?.message} - disabled={isSubmitting} - {...register('phoneNumber')} - /> - </Column> - <Button size={'small'} type={'submit'} disabled={isSubmitting}> - {isSubmitting ? '전송 중...' : '전송하기'} - </Button> + <Column + as={'form'} + onSubmit={handleSubmit(onSubmit)} + className={'mt-5 w-[270px] items-center gap-10'} + > + <Column className={'w-full gap-3'}> + {/* 1. 전화번호 입력 */} + <Input + type={'tel'} + placeholder={'010-1234-5678'} + isInvalid={!!errors.phoneNumber} + errorMessage={errors.phoneNumber?.message} + disabled={isSubmitting} + autoFocus={true} + {...register('phoneNumber')} + /> + + {/* 2. 약관 동의 영역 */} + <Controller + control={control} + name={'agreements'} + render={({ field }) => ( + <Column className={'w-full gap-2'}> + <TermAgreementCheckbox + label={AGREEMENT.termsAgreed.label} + disabled={isSubmitting} + isSelected={field.value.termsAgreed} + onChange={(isSelected) => + field.onChange({ + ...field.value, + termsAgreed: isSelected, + }) + } + detailContent={AGREEMENT.termsAgreed.detailContent} + /> + <TermAgreementCheckbox + label={AGREEMENT.privacyAgreed.label} + disabled={isSubmitting} + isSelected={field.value.privacyAgreed} + onChange={(isSelected) => + field.onChange({ + ...field.value, + privacyAgreed: isSelected, + }) + } + detailContent={AGREEMENT.privacyAgreed.detailContent} + /> + {/* 3. 약관 동의 에러 메시지 */} + {errors.agreements && ( + <Text variant={'caption2'} className={'text-red-500'}> + 필수 약관에 모두 동의해 주세요. + </Text> + )} + </Column> + )} + /> </Column> - </form> + + {/* 4. 전송 버튼 */} + <Button size={'small'} type={'submit'} disabled={isSubmitting}> + {isSubmitting ? '전송 중...' : '전송하기'} + </Button> + </Column> ) } diff --git a/apps/web/app/events/lucky-draw/result/[id]/_components/WinnerInfoForm/constants.ts b/apps/web/app/events/lucky-draw/result/[id]/_components/WinnerInfoForm/constants.ts new file mode 100644 index 00000000..fb6e7c6e --- /dev/null +++ b/apps/web/app/events/lucky-draw/result/[id]/_components/WinnerInfoForm/constants.ts @@ -0,0 +1,36 @@ +export const AGREEMENT = { + termsAgreed: { + label: '[필수] 서비스 이용약관 동의', + detailContent: `제 1조 (목적) +본 약관은 '공주대 맛집'에서 주관하는 이벤트(이하 '이벤트')의 참여 조건 및 운영에 관한 제반 사항을 규정함을 목적으로 합니다. + +제 2조 (참여 자격 및 제한) +1. 본 이벤트는 당사 서비스 이용자에 한해 참여 가능합니다. +2. 타인의 정보를 도용하거나 매크로 등 부정한 방법으로 참여한 사실이 확인될 경우, 사전 통보 없이 당첨이 취소될 수 있습니다. + +제 3조 (경품 지급 및 책임의 한계) +1. 당첨 경품은 기재해주신 휴대전화 번호로 모바일 교환권(기프티콘) 형태로 발송됩니다. +2. 연락처 오기입, 수신 거부, 번호 변경 등으로 인해 경품을 수령하지 못한 경우 주최측은 이를 보상하거나 재발송하지 않습니다. +3. 당사의 사정에 따라 사전 고지 없이 동일한 가치의 타 상품으로 대체될 수 있습니다. +4. 지급된 기프티콘의 유효기간 연장 및 환불은 불가합니다.`, + }, + privacyAgreed: { + label: '[필수] 개인정보 수집 및 이용 동의', + detailContent: `'공주대 맛집'은 이벤트 진행 및 경품 제공을 위해 아래와 같이 개인정보를 수집 및 이용합니다. 내용을 자세히 읽으신 후 동의 여부를 결정해 주시기 바랍니다. + +1. 수집하는 개인정보 항목 +- 필수 항목: 휴대전화 번호 + +2. 개인정보 수집 및 이용 목적 +- 이벤트 당첨자 본인 확인 +- 모바일 교환권(기프티콘) 발송 +- 불량 이용자 적발 및 이벤트 관련 문의 응대 + +3. 개인정보의 보유 및 이용 기간 +- 원칙적으로 이벤트 종료 및 경품 발송 완료 후 3개월 이내에 지체 없이 파기합니다. +- 단, 관련 법령에 의해 보존할 필요가 있는 경우 해당 법령에서 정한 기간 동안 안전하게 보관합니다. + +4. 동의를 거부할 권리 및 불이익 +귀하는 위 개인정보 수집 및 이용에 대한 동의를 거부할 권리가 있습니다. 단, 필수 항목 동의 거부 시 이벤트 참여 및 경품 수령이 불가능합니다.`, + }, +} From 63ac813afa0db72355eab10e8b0f47fbbb69d63a Mon Sep 17 00:00:00 2001 From: leeleeleeleejun <wnstjr6293@naver.com> Date: Thu, 26 Feb 2026 22:24:06 +0900 Subject: [PATCH 39/43] =?UTF-8?q?refactor:=20=EC=B6=94=EC=B2=A8=20?= =?UTF-8?q?=EC=95=A0=EB=8B=88=EB=A9=94=EC=9D=B4=EC=85=98=20=EB=8C=80?= =?UTF-8?q?=EA=B8=B0=20=EC=8B=9C=EA=B0=84(800ms)=20=EB=A7=A4=EC=A7=81=20?= =?UTF-8?q?=EB=84=98=EB=B2=84=20=EC=83=81=EC=88=98=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/events/lucky-draw/result/[id]/EventResultClient.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/web/app/events/lucky-draw/result/[id]/EventResultClient.tsx b/apps/web/app/events/lucky-draw/result/[id]/EventResultClient.tsx index 48054b6d..1741fdbe 100644 --- a/apps/web/app/events/lucky-draw/result/[id]/EventResultClient.tsx +++ b/apps/web/app/events/lucky-draw/result/[id]/EventResultClient.tsx @@ -16,6 +16,8 @@ interface Props { eventId: string } +const LOTTERY_ANIMATION_DURATION_MS = 800 + export const EventResultClient = ({ eventId }: Props) => { const [isRunning, setIsRunning] = useState(false) const { isOpen, onOpen, onOpenChange } = useDisclosure() @@ -37,7 +39,7 @@ export const EventResultClient = ({ eventId }: Props) => { setIsRunning(true) setTimeout(() => { onOpen() - }, 500) + }, LOTTERY_ANIMATION_DURATION_MS) } return ( From 9516ba4dab2f9e57f4f9319979b1487da3a5de81 Mon Sep 17 00:00:00 2001 From: leeleeleeleejun <wnstjr6293@naver.com> Date: Fri, 27 Feb 2026 15:26:46 +0900 Subject: [PATCH 40/43] =?UTF-8?q?feat:=20=EC=A2=85=EB=A3=8C=EB=90=9C=20?= =?UTF-8?q?=EC=9D=B4=EB=B2=A4=ED=8A=B8=20=ED=83=AD=20=EB=AA=85=EC=B9=AD=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EB=B0=8F=20=EC=B0=B8=EC=97=AC=20=EA=B8=B0?= =?UTF-8?q?=EB=A1=9D=20=EC=95=88=EB=82=B4=20=EB=AC=B8=EA=B5=AC=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Pages/MemberView/FinishedEvent/FinishedEvent.tsx | 5 ++++- .../lucky-draw/_components/Pages/MemberView/MemberView.tsx | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/web/app/events/lucky-draw/_components/Pages/MemberView/FinishedEvent/FinishedEvent.tsx b/apps/web/app/events/lucky-draw/_components/Pages/MemberView/FinishedEvent/FinishedEvent.tsx index 321fe3ad..859d48ab 100644 --- a/apps/web/app/events/lucky-draw/_components/Pages/MemberView/FinishedEvent/FinishedEvent.tsx +++ b/apps/web/app/events/lucky-draw/_components/Pages/MemberView/FinishedEvent/FinishedEvent.tsx @@ -17,7 +17,10 @@ export const FinishedEvent = () => { fallbackTitle={'아직 종료된 이벤트가 없어요.'} fallbackDescription={'진행 중인 이벤트에 참여하고 행운을 잡아보세요!'} > - <Column as={'ul'} className={'h-full overflow-y-auto py-2'}> + <Text variant={'caption2'} className={'py-4 text-center text-gray-600'}> + 참여하신 이벤트 기록만 모아서 보여드려요! + </Text> + <Column as={'ul'} className={'h-full overflow-y-auto'}> {data.map((item) => ( <EventSummary key={item.eventId} event={item} /> ))} diff --git a/apps/web/app/events/lucky-draw/_components/Pages/MemberView/MemberView.tsx b/apps/web/app/events/lucky-draw/_components/Pages/MemberView/MemberView.tsx index 20b1e9dc..8989ef8c 100644 --- a/apps/web/app/events/lucky-draw/_components/Pages/MemberView/MemberView.tsx +++ b/apps/web/app/events/lucky-draw/_components/Pages/MemberView/MemberView.tsx @@ -11,7 +11,7 @@ export type StepType = 'inProgress' | 'finished' const STEP_NAME = { inProgress: '진행중인 이벤트', - finished: '참여했던 이벤트', + finished: '종료된 이벤트', } const TABS: StepType[] = ['inProgress', 'finished'] From 04598bce72fab5d89deb073919a3970fc4db2cff Mon Sep 17 00:00:00 2001 From: leeleeleeleejun <wnstjr6293@naver.com> Date: Fri, 27 Feb 2026 15:33:31 +0900 Subject: [PATCH 41/43] =?UTF-8?q?feat:=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=20?= =?UTF-8?q?=EA=B2=B0=EA=B3=BC=20API=20=EC=97=B0=EB=8F=99=20=EB=B0=8F=20?= =?UTF-8?q?=EC=8A=A4=ED=82=A4=EB=A7=88=20=ED=99=95=EC=9E=A5,=20=EB=8B=B9?= =?UTF-8?q?=EC=B2=A8=EC=9E=90=20=ED=8F=BC=20=EC=A0=9C=EC=B6=9C=20=EC=97=AC?= =?UTF-8?q?=EB=B6=80=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/app/_apis/schemas/event.ts | 5 +- apps/web/app/_apis/services/event.ts | 4 +- .../result/[id]/EventResultClient.tsx | 51 +++++++++++++------ .../lucky-draw/result/[id]/ResultModal.tsx | 4 +- 4 files changed, 41 insertions(+), 23 deletions(-) diff --git a/apps/web/app/_apis/schemas/event.ts b/apps/web/app/_apis/schemas/event.ts index 67e55fb2..021585c0 100644 --- a/apps/web/app/_apis/schemas/event.ts +++ b/apps/web/app/_apis/schemas/event.ts @@ -28,11 +28,10 @@ export const EventByPrivateSchema = z.nullable( export const EventEntrySchema = BaseEventSchema -export const EventResultSchema = z.object({ - eventId: z.number().transform(String), +export const EventResultSchema = BaseEventSchema.extend({ isWinner: z.boolean(), - participantsCount: z.number(), usedTicketsCount: z.number(), + isPhoneSubmitted: z.boolean(), }) export const EventWinnerFormSchema = z.object({ diff --git a/apps/web/app/_apis/services/event.ts b/apps/web/app/_apis/services/event.ts index 12da2aa3..10eb4e3a 100644 --- a/apps/web/app/_apis/services/event.ts +++ b/apps/web/app/_apis/services/event.ts @@ -35,9 +35,7 @@ export const getEventByEntries = async (): Promise<EventByEntry[]> => { return EventEntrySchema.array().parse(data) } -export const getEventResult = async ( - eventId: string, -): Promise<EventResult | null> => { +export const getEventResult = async (eventId: string): Promise<EventResult> => { const { data } = await axiosInstance.get(API_PATH.EVENT.RESULT(eventId)) return EventResultSchema.parse(data) } diff --git a/apps/web/app/events/lucky-draw/result/[id]/EventResultClient.tsx b/apps/web/app/events/lucky-draw/result/[id]/EventResultClient.tsx index 1741fdbe..4f8d0f85 100644 --- a/apps/web/app/events/lucky-draw/result/[id]/EventResultClient.tsx +++ b/apps/web/app/events/lucky-draw/result/[id]/EventResultClient.tsx @@ -3,14 +3,15 @@ import Image from 'next/image' import { useState } from 'react' import { useDisclosure } from '@heroui/react' -// import { useSuspenseQuery } from '@tanstack/react-query' -// import { useEventQueries } from '@/_apis/queries/event' +import { useSuspenseQuery } from '@tanstack/react-query' +import { useEventQueries } from '@/_apis/queries/event' import { Button } from '@repo/ui/components/Button' import { Column, Flex } from '@repo/ui/components/Layout' import { Text } from '@repo/ui/components/Text' import { ParticipationStatus } from '../../_components/ParticipationStatus' import { LottoBalls } from './_components/LottoBalls' import { ResultModal } from './ResultModal' +import { EventResult } from '@/_apis/schemas/event' interface Props { eventId: string @@ -21,15 +22,17 @@ const LOTTERY_ANIMATION_DURATION_MS = 800 export const EventResultClient = ({ eventId }: Props) => { const [isRunning, setIsRunning] = useState(false) const { isOpen, onOpen, onOpenChange } = useDisclosure() - // Todo: 백엔드 API 작업 완료 시 연동 필요 - // const { data } = useSuspenseQuery(useEventQueries.result(eventId)) - const data = { - isWinner: true, - participantsCount: 100, - usedTicketsCount: 150, - } + const { data } = useSuspenseQuery(useEventQueries.result(eventId)) - const { isWinner, participantsCount, usedTicketsCount } = data + const { + isWinner, + participantsCount, + usedTicketsCount, + prize, + totalWinnersCount, + eventEndDate, + isPhoneSubmitted, + } = data const stopLotteryAnimation = () => { setIsRunning(false) @@ -45,7 +48,12 @@ export const EventResultClient = ({ eventId }: Props) => { return ( <> <Column className='h-full items-center gap-4 p-5'> - <EventSummary /> + <EventSummary + prize={prize} + totalWinnersCount={totalWinnersCount} + participantsCount={participantsCount} + eventEndDate={eventEndDate} + /> <Column className='mt-10 items-center gap-4'> <Text className='text-center' variant='title1'> 행운의 주인공이 되어보세요! @@ -64,6 +72,7 @@ export const EventResultClient = ({ eventId }: Props) => { <ResultModal eventId={eventId} isWinner={isWinner} + isPhoneSubmitted={isPhoneSubmitted} isOpen={isOpen} onOpenChange={onOpenChange} onAnimationStop={stopLotteryAnimation} @@ -72,11 +81,21 @@ export const EventResultClient = ({ eventId }: Props) => { ) } -const EventSummary = () => { +type EventSummaryProps = Pick< + EventResult, + 'prize' | 'totalWinnersCount' | 'eventEndDate' | 'participantsCount' +> + +const EventSummary = ({ + prize, + totalWinnersCount, + participantsCount, + eventEndDate, +}: EventSummaryProps) => { return ( <Flex className={'border-b-1 w-full gap-4 border-gray-100 py-2'}> <Image - src={'/images/chicken.png'} + src={prize.imageUrl} alt={'종료된 이벤트 상품'} width={80} height={80} @@ -85,12 +104,12 @@ const EventSummary = () => { className={'rounded-lg'} /> <Column> - <Text variant={'title3'}>BBQ 황금 올리브 치킨</Text> + <Text variant={'title3'}>{prize.description}</Text> <Text variant={'caption2'} className={'text-gray-300'}> - 당첨자 3명 | 참여자 27명 + 당첨자 {totalWinnersCount}명 | 참여자 {participantsCount}명 </Text> <Text variant={'caption2'} className={'text-gray-300'}> - 종료 일자: 2025.12.23 + 종료 일자: {eventEndDate.slice(0, 10).replace(/-/g, '.')} </Text> </Column> </Flex> diff --git a/apps/web/app/events/lucky-draw/result/[id]/ResultModal.tsx b/apps/web/app/events/lucky-draw/result/[id]/ResultModal.tsx index 4f4042c2..5d8bd1c5 100644 --- a/apps/web/app/events/lucky-draw/result/[id]/ResultModal.tsx +++ b/apps/web/app/events/lucky-draw/result/[id]/ResultModal.tsx @@ -7,6 +7,7 @@ import { WinnerInfoForm } from './_components/WinnerInfoForm' type Props = { eventId: string isWinner: boolean + isPhoneSubmitted: boolean isOpen: boolean onOpenChange: VoidFunction onAnimationStop: VoidFunction @@ -36,6 +37,7 @@ const FailModalContent: ModalContent = { export const ResultModal = ({ eventId, isWinner, + isPhoneSubmitted, isOpen, onOpenChange, onAnimationStop, @@ -84,7 +86,7 @@ export const ResultModal = ({ {modalContent.description} </Text> </Column> - {isWinner && ( + {isWinner && !isPhoneSubmitted && ( <WinnerInfoForm eventId={eventId} onSuccess={onClose} /> )} </Column> From 1e00a658e4b1570bfa0a2995a62f755244139041 Mon Sep 17 00:00:00 2001 From: leeleeleeleejun <wnstjr6293@naver.com> Date: Fri, 27 Feb 2026 16:51:01 +0900 Subject: [PATCH 42/43] =?UTF-8?q?feat:=20=EB=8B=B9=EC=B2=A8=EC=9E=90=20?= =?UTF-8?q?=ED=8F=BC=20=EC=A0=9C=EC=B6=9C=20=EC=84=B1=EA=B3=B5=20=EC=8B=9C?= =?UTF-8?q?=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=20=EA=B2=B0=EA=B3=BC=20=EC=BF=BC?= =?UTF-8?q?=EB=A6=AC=20=EB=AC=B4=ED=9A=A8=ED=99=94=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/app/_apis/mutations/useSubmitWinnerForm.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/apps/web/app/_apis/mutations/useSubmitWinnerForm.ts b/apps/web/app/_apis/mutations/useSubmitWinnerForm.ts index 0ec944f6..1b6b3739 100644 --- a/apps/web/app/_apis/mutations/useSubmitWinnerForm.ts +++ b/apps/web/app/_apis/mutations/useSubmitWinnerForm.ts @@ -1,17 +1,24 @@ -import { useMutation } from '@tanstack/react-query' +import { useMutation, useQueryClient } from '@tanstack/react-query' import { addToast } from '@heroui/react' import { submitWinnerForm } from '../services/event' import type { EventWinnerForm } from '@/_apis/schemas/event' +import { EventQueryKeys } from '@/_apis/queries/event' type UseSubmitWinnerFormParams = { eventId: string } export const useSubmitWinnerForm = ({ eventId }: UseSubmitWinnerFormParams) => { + const queryClient = useQueryClient() + return useMutation({ mutationFn: (data: EventWinnerForm) => submitWinnerForm(eventId, data), onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: [...EventQueryKeys.result(eventId)], + }) + addToast({ title: '전화번호가 성공적으로 제출되었습니다!', severity: 'success', From 61575ce206f8aa125bc718fd4be8dbdda0560d2e Mon Sep 17 00:00:00 2001 From: leeleeleeleejun <wnstjr6293@naver.com> Date: Fri, 27 Feb 2026 17:25:09 +0900 Subject: [PATCH 43/43] =?UTF-8?q?fix:=20WinnerInfoForm=20=EC=A0=84?= =?UTF-8?q?=ED=99=94=EB=B2=88=ED=98=B8=20=EC=9E=85=EB=A0=A5=20=ED=95=84?= =?UTF-8?q?=EB=93=9C=20disabled=20prop=20=EB=AA=85=EC=B9=AD=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../result/[id]/_components/WinnerInfoForm/WinnerInfoForm.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web/app/events/lucky-draw/result/[id]/_components/WinnerInfoForm/WinnerInfoForm.tsx b/apps/web/app/events/lucky-draw/result/[id]/_components/WinnerInfoForm/WinnerInfoForm.tsx index f31f900b..86f3d3a0 100644 --- a/apps/web/app/events/lucky-draw/result/[id]/_components/WinnerInfoForm/WinnerInfoForm.tsx +++ b/apps/web/app/events/lucky-draw/result/[id]/_components/WinnerInfoForm/WinnerInfoForm.tsx @@ -53,7 +53,7 @@ export const WinnerInfoForm = ({ eventId, onSuccess }: Props) => { placeholder={'010-1234-5678'} isInvalid={!!errors.phoneNumber} errorMessage={errors.phoneNumber?.message} - disabled={isSubmitting} + isDisabled={isSubmitting} autoFocus={true} {...register('phoneNumber')} />