From 97d14a1e8dcc415243015d4c9980075a71e00a5d Mon Sep 17 00:00:00 2001 From: DaeWon9 Date: Wed, 14 Aug 2024 18:50:34 +0900 Subject: [PATCH 1/7] =?UTF-8?q?feat:=20phoneNumberAutoFormat=20util=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/service/src/common/utils/formatter.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/service/src/common/utils/formatter.ts b/packages/service/src/common/utils/formatter.ts index 35a373cc..e4355fd8 100644 --- a/packages/service/src/common/utils/formatter.ts +++ b/packages/service/src/common/utils/formatter.ts @@ -5,3 +5,13 @@ export function toPx(value: number | string): string { export function toS(value: number | string): string { return typeof value === "number" ? `${value}s` : value; } + +export const phoneNumberAutoFormat = (phoneNumber: string): string => { + const number = phoneNumber.trim().replace(/[^0-9]/g, ""); + + if (number.length < 4) return number; + if (number.length < 7) return number.replace(/(\d{3})(\d{1})/, "$1-$2"); + if (number.length < 11) + return number.replace(/(\d{3})(\d{3})(\d{1})/, "$1-$2-$3"); + return number.replace(/(\d{3})(\d{4})(\d{4})/, "$1-$2-$3"); +}; From d73b589de10acd8b93a573b056f0290448359ce3 Mon Sep 17 00:00:00 2001 From: DaeWon9 Date: Wed, 14 Aug 2024 18:52:02 +0900 Subject: [PATCH 2/7] =?UTF-8?q?feat:=20=EC=84=A0=EC=B0=A9=EC=88=9C=20?= =?UTF-8?q?=EC=9D=B4=EB=B2=A4=ED=8A=B8=EC=97=90=20=EB=8B=B9=EC=B2=A8?= =?UTF-8?q?=EB=90=A0=20=EA=B2=BD=EC=9A=B0=20=ED=8E=98=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99=20=EB=B0=8F=20=EB=A1=9C=EC=A7=81=EB=B6=84?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../NQuizSection/NQuizInput/NQuizInput.tsx | 56 +++++++++++-------- 1 file changed, 34 insertions(+), 22 deletions(-) diff --git a/packages/service/src/components/nQuizEvent/NQuizSection/NQuizInput/NQuizInput.tsx b/packages/service/src/components/nQuizEvent/NQuizSection/NQuizInput/NQuizInput.tsx index 1a1ae39b..9b318244 100644 --- a/packages/service/src/components/nQuizEvent/NQuizSection/NQuizInput/NQuizInput.tsx +++ b/packages/service/src/components/nQuizEvent/NQuizSection/NQuizInput/NQuizInput.tsx @@ -15,6 +15,8 @@ import { MODAL_N_QUIZ_TITLE, } from "@service/common/components/ModalContainer/content/modalContent"; import { useErrorBoundary } from "react-error-boundary"; +import { useNavigate } from "react-router-dom"; +import { N_QUIZ_EVENT_PAGE_WINNER_APLLY_PAGE_ROUTE } from "@service/constants/routes"; interface NQuizInputProps { openedQuiz: IOrderEvent; @@ -24,6 +26,7 @@ export const NQuizInput = ({ openedQuiz }: NQuizInputProps) => { const [inputValue, setInputValue] = useState(""); const { showBoundary } = useErrorBoundary(); const { closeModal, openModal } = useModal(); + const navigate = useNavigate(); const handleSubmit = () => { if (inputValue.trim() === "") return; @@ -46,32 +49,14 @@ export const NQuizInput = ({ openedQuiz }: NQuizInputProps) => { const handleApiResponse = (response: IPostOrderEventResponse) => { switch (response.result) { case "SUCCESS": - craftSideCannons(2); - openModal({ - type: "alert", - props: { - title: MODAL_N_QUIZ_TITLE, - content: "정답!! 너 재능있어~", - }, - }); + handleSuccessResponse(response.applyTicket as string); break; case "WRONG": - openModal({ - type: "navigate", - props: { - title: MODAL_N_QUIZ_TITLE, - content: MODAL_CONTENT_QUIZ_WRONG, - }, - }); + handleWrongResponse(); break; case "CLOSED": - openModal({ - type: "alert", - props: { - title: MODAL_N_QUIZ_TITLE, - content: MODAL_CONTENT_QUIZ_CLOSED, - }, - }); + handleClosedResponse(); + break; } }; @@ -90,6 +75,33 @@ export const NQuizInput = ({ openedQuiz }: NQuizInputProps) => { } }; + const handleSuccessResponse = (applyTicket: string) => { + localStorage.setItem("ApplyTicket", applyTicket); + closeModal(); + navigate(N_QUIZ_EVENT_PAGE_WINNER_APLLY_PAGE_ROUTE); + craftSideCannons(2); + }; + + const handleWrongResponse = () => { + openModal({ + type: "navigate", + props: { + title: MODAL_N_QUIZ_TITLE, + content: MODAL_CONTENT_QUIZ_WRONG, + }, + }); + }; + + const handleClosedResponse = () => { + openModal({ + type: "alert", + props: { + title: MODAL_N_QUIZ_TITLE, + content: MODAL_CONTENT_QUIZ_CLOSED, + }, + }); + }; + return (
Date: Wed, 14 Aug 2024 18:52:28 +0900 Subject: [PATCH 3/7] =?UTF-8?q?chore:=20IPostOrderEventResponse=20?= =?UTF-8?q?=EB=82=B4=EB=B6=80=EC=97=90=20ApplyTicket=20=EB=A7=A8=EC=95=9E?= =?UTF-8?q?=EC=97=90=20=EC=86=8C=EB=AC=B8=EC=9E=90=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/core/src/types/orderEvent/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/types/orderEvent/index.ts b/packages/core/src/types/orderEvent/index.ts index e594a09e..54dd90ca 100644 --- a/packages/core/src/types/orderEvent/index.ts +++ b/packages/core/src/types/orderEvent/index.ts @@ -30,5 +30,5 @@ export interface IPostOrderEventRequest { export interface IPostOrderEventResponse { result: OrderEventResultType; - ApplyTicket?: string; + applyTicket?: string; } From 872be5d5adc9f52330f086541dda1b20881d8a95 Mon Sep 17 00:00:00 2001 From: DaeWon9 Date: Wed, 14 Aug 2024 18:53:00 +0900 Subject: [PATCH 4/7] =?UTF-8?q?feat:=20=EB=8B=B9=EC=B2=A8=EC=9E=90=20?= =?UTF-8?q?=EB=93=B1=EB=A1=9D=20API=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apis/orderEvent/apiPostOrderEventApply.ts | 27 +++++++++++++++++++ packages/service/src/apis/orderEvent/type.ts | 6 +++++ 2 files changed, 33 insertions(+) create mode 100644 packages/service/src/apis/orderEvent/apiPostOrderEventApply.ts create mode 100644 packages/service/src/apis/orderEvent/type.ts diff --git a/packages/service/src/apis/orderEvent/apiPostOrderEventApply.ts b/packages/service/src/apis/orderEvent/apiPostOrderEventApply.ts new file mode 100644 index 00000000..3924ed28 --- /dev/null +++ b/packages/service/src/apis/orderEvent/apiPostOrderEventApply.ts @@ -0,0 +1,27 @@ +import { customFetch } from "@watermelon-clap/core/src/utils"; +import { IPostOrderEventApplyRequest } from "./type"; + +export const apiPostOrderEventApply = async ({ + eventId, + quizId, + phoneNumber, + appplyTicket, +}: IPostOrderEventApplyRequest): Promise => { + return customFetch( + `${import.meta.env.VITE_BACK_BASE_URL}/event/order/${eventId}/${quizId}/apply`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + ApplyTicket: appplyTicket, + }, + body: JSON.stringify({ phoneNumber: phoneNumber }), + }, + ) + .then((response) => { + return response; + }) + .catch((error) => { + throw error; + }); +}; diff --git a/packages/service/src/apis/orderEvent/type.ts b/packages/service/src/apis/orderEvent/type.ts new file mode 100644 index 00000000..e4c1db4b --- /dev/null +++ b/packages/service/src/apis/orderEvent/type.ts @@ -0,0 +1,6 @@ +export interface IPostOrderEventApplyRequest { + eventId: string; + quizId: string; + phoneNumber: string; + appplyTicket: string; +} From 382f150f8574d6a49b19e26b59b59130e0a165f1 Mon Sep 17 00:00:00 2001 From: DaeWon9 Date: Wed, 14 Aug 2024 18:53:40 +0900 Subject: [PATCH 5/7] =?UTF-8?q?feat:=20NQuizEventWinnerApply=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 --- .../NQuizEventWinnerApply.css.ts | 134 ++++++++++++++++ .../NQuizEventWinnerApply.tsx | 143 ++++++++++++++++++ .../src/pages/NQuizEventWinnerApply/index.ts | 1 + 3 files changed, 278 insertions(+) create mode 100644 packages/service/src/pages/NQuizEventWinnerApply/NQuizEventWinnerApply.css.ts create mode 100644 packages/service/src/pages/NQuizEventWinnerApply/NQuizEventWinnerApply.tsx create mode 100644 packages/service/src/pages/NQuizEventWinnerApply/index.ts diff --git a/packages/service/src/pages/NQuizEventWinnerApply/NQuizEventWinnerApply.css.ts b/packages/service/src/pages/NQuizEventWinnerApply/NQuizEventWinnerApply.css.ts new file mode 100644 index 00000000..916b3bde --- /dev/null +++ b/packages/service/src/pages/NQuizEventWinnerApply/NQuizEventWinnerApply.css.ts @@ -0,0 +1,134 @@ +import { css } from "@emotion/react"; +import { mobile } from "@service/common/responsive/responsive"; +import { theme } from "@watermelon-clap/core/src/theme"; + +export const backgroundStyle = css` + background-image: url("/images/quiz/nQuizBackground.png"); + background-size: cover; + background-position: top; + background-repeat: no-repeat; + width: 100%; + + ${theme.flex.center} + ${theme.flex.column} + + padding: 100px 18vw; + padding-bottom: 94px; + + gap: 20px; + + ${mobile(css` + min-width: 0px; + padding: 20vw 6vw; + padding-bottom: 47px; + `)}; +`; + +export const logoContainerStyle = css` + ${theme.flex.center}; + ${theme.gap.gap32}; + + ${mobile(css` + ${theme.gap.gap16}; + `)} +`; + +export const logoStyle = css` + width: 208px; + height: 87px; + text-shadow: 0 0 40px rgba(255, 255, 255, 0.6); + + ${mobile(css` + width: 104px; + height: 43px; + `)} +`; + +export const titleStyle = css` + ${theme.font.pcpB82} + color: ${theme.color.white}; + text-shadow: 0 0 40px rgba(255, 255, 255, 0.6); + + ${mobile(css` + font-size: 41px; + `)}; +`; + +export const contentContainerStyle = css` + ${theme.flex.center}; + ${theme.flex.column}; + ${theme.gap.gap8}; + + ${mobile(css` + ${theme.gap.gap4}; + `)} +`; + +export const cheersTextStyle = css` + ${theme.font.preB38} + color: ${theme.color.eventBlue}; + text-align: center; + + ${mobile(css` + font-size: 14px; + `)} +`; + +export const contentTextStyle = css` + ${theme.font.preB24} + color: ${theme.color.white}; + text-align: center; + + ${mobile(css` + font-size: 14px; + `)} +`; + +export const inputContainerStyle = css` + width: 100%; + display: flex; + align-items: center; + justify-content: center; +`; + +export const inputStyle = css` + display: flex; + width: 566px; + height: 52px; + padding: 15px 18px; + align-items: center; + gap: 10px; + align-self: stretch; + border-radius: 8px; + background: var(--Gray-100, #ececec); +`; + +export const listStyle = css` + width: 566px; + display: flex; + flex-direction: column; + color: ${theme.color.gray300}; + align-items: start; + gap: 10px; +`; + +export const listItemStyle = css` + ${theme.font.preM18} + color: ${theme.color.gray300}; + margin-left: 22px; +`; + +export const rewardContainerStyle = css` + width: 200px; + height: 300px; + display: flex; + align-items: center; + justify-content: center; +`; + +export const centeredContainerStyle = css` + width: 100%; + display: flex; + align-items: center; + justify-content: center; +`; diff --git a/packages/service/src/pages/NQuizEventWinnerApply/NQuizEventWinnerApply.tsx b/packages/service/src/pages/NQuizEventWinnerApply/NQuizEventWinnerApply.tsx new file mode 100644 index 00000000..257ede53 --- /dev/null +++ b/packages/service/src/pages/NQuizEventWinnerApply/NQuizEventWinnerApply.tsx @@ -0,0 +1,143 @@ +import { ChangeEvent, useEffect, useState } from "react"; +import { useNavigate } from "react-router-dom"; +import { useSuspenseQuery } from "@tanstack/react-query"; +import { apiGetOrderEvent } from "@service/apis/orderEvent"; +import { apiPostOrderEventApply } from "@service/apis/orderEvent/apiPostOrderEventApply"; +import { phoneNumberAutoFormat } from "@service/common/utils"; +import { IOrderEvent } from "@watermelon-clap/core/src/types"; +import { NQuizReward } from "@service/components/nQuizEvent"; +import { ReactComponent as NLogo } from "public/images/gnb/n-logo.svg"; +import { CheckBox } from "@service/common/components/CheckBox"; +import { Button } from "@service/common/components/Button"; +import { MAIN_PAGE_ROUTE } from "@service/constants/routes"; +import { + backgroundStyle, + logoContainerStyle, + logoStyle, + titleStyle, + contentContainerStyle, + cheersTextStyle, + contentTextStyle, + inputContainerStyle, + inputStyle, + listStyle, + listItemStyle, + rewardContainerStyle, + centeredContainerStyle, +} from "./NQuizEventWinnerApply.css"; +import { useModal } from "@watermelon-clap/core/src/hooks"; +import { + MODAL_CONTENT_ORDER_EVENT_APPLY_SUCCESS, + MODAL_N_QUIZ_TITLE, +} from "@service/common/components/ModalContainer/content/modalContent"; + +export const NQuizEventWinnerApply = () => { + const [isChecked, setIsChecked] = useState(false); + const [phoneNumber, setPhoneNumber] = useState(""); + const { openModal } = useModal(); + const navigate = useNavigate(); + + const { data: quizList } = useSuspenseQuery({ + queryKey: ["orderEvent"], + queryFn: () => apiGetOrderEvent(), + staleTime: Infinity, + }); + + const openedQuiz = quizList.find( + (quiz) => quiz.status === "OPEN", + ) as IOrderEvent; + + const handlePhoneNumberChange = (e: ChangeEvent) => { + const targetValue = phoneNumberAutoFormat(e.target.value); + setPhoneNumber(targetValue); + }; + + const handleSubmit = () => { + if (!localStorage.getItem("ApplyTicket") || !openedQuiz) { + alert("올바른 접근이 아닙니다."); + return; + } + if (phoneNumber === "") { + alert("전화번호를 입력해주세요"); + return; + } + if (!isChecked) { + alert("약관에 동의해주세요."); + return; + } + + apiPostOrderEventApply({ + eventId: openedQuiz.eventId, + quizId: openedQuiz.quiz?.quizId as string, + phoneNumber: phoneNumber.replace(/-/g, ""), + appplyTicket: localStorage.getItem("ApplyTicket") as string, + }).then((response) => { + if (response.ok) { + openModal({ + type: "alert", + props: { + title: MODAL_N_QUIZ_TITLE, + content: MODAL_CONTENT_ORDER_EVENT_APPLY_SUCCESS, + }, + }); + } + }); + }; + + useEffect(() => { + if (!localStorage.getItem("ApplyTicket")) { + alert("권한이 없습니다."); + navigate(MAIN_PAGE_ROUTE); + } + }, [navigate]); + + return ( +
+
+ + 퀴즈 +
+
+
+ +
+
+ +
+
축하드립니다!
+
선착순 이벤트 대상자에 선정되었습니다!
+
아래 칸에 휴대폰 번호를 입력해주세요.
+
+ +
+ +
+ + +
    +
  • 경품은 추후 문자를 통해 발송됩니다.
  • +
  • + 번호 제출 전 페이지를 이탈하면 당첨이 취소됩니다. +
  • +
+ + +
+ ); +}; diff --git a/packages/service/src/pages/NQuizEventWinnerApply/index.ts b/packages/service/src/pages/NQuizEventWinnerApply/index.ts new file mode 100644 index 00000000..282a7320 --- /dev/null +++ b/packages/service/src/pages/NQuizEventWinnerApply/index.ts @@ -0,0 +1 @@ +export { NQuizEventWinnerApply } from "./NQuizEventWinnerApply"; From 2b52b0adc975f7f35e99bd3e5a9a7c5bb0fa475b Mon Sep 17 00:00:00 2001 From: DaeWon9 Date: Wed, 14 Aug 2024 18:54:10 +0900 Subject: [PATCH 6/7] =?UTF-8?q?feat:=20=EC=8B=A0=EC=B2=AD=EC=99=84?= =?UTF-8?q?=EB=A3=8C=20=EC=BB=A8=ED=85=90=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/ModalContainer/content/modalContent.tsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/service/src/common/components/ModalContainer/content/modalContent.tsx b/packages/service/src/common/components/ModalContainer/content/modalContent.tsx index 7989cf28..3f6ccd0c 100644 --- a/packages/service/src/common/components/ModalContainer/content/modalContent.tsx +++ b/packages/service/src/common/components/ModalContainer/content/modalContent.tsx @@ -40,3 +40,10 @@ export const MODAL_CONTENT_QUIZ_WRONG = (

아반떼 N 페이지에서 정답을 확인해 주세요.

); + +export const MODAL_CONTENT_ORDER_EVENT_APPLY_SUCCESS = ( +
+

신청되었습니다.

+

경품은 추후 문자를 통해 발송됩니다.

+
+); From 63fbf1f1e66af9f0bab72dc8544ff12fa428a7cf Mon Sep 17 00:00:00 2001 From: DaeWon9 Date: Wed, 14 Aug 2024 18:54:27 +0900 Subject: [PATCH 7/7] =?UTF-8?q?chore:=20NQuizEventWinnerApply=20Route?= =?UTF-8?q?=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/service/src/constants/routes.ts | 2 ++ packages/service/src/pages/index.ts | 1 + packages/service/src/router.tsx | 6 ++++++ 3 files changed, 9 insertions(+) diff --git a/packages/service/src/constants/routes.ts b/packages/service/src/constants/routes.ts index 8d9b1b8a..14ce8a65 100644 --- a/packages/service/src/constants/routes.ts +++ b/packages/service/src/constants/routes.ts @@ -13,3 +13,5 @@ export const NEW_CAR_PAGE_ROUTE = "/new-car" as const; export const N_PARTS_PICK_PAGE_ROUTE = "/parts-pick" as const; export const PICK_EVENT_PAGE_ROUTE = "/pick-event" as const; export const PARTS_COLLECTION_PAGE_ROUTE = "/parts-collection" as const; +export const N_QUIZ_EVENT_PAGE_WINNER_APLLY_PAGE_ROUTE = + "/quiz-event-apply" as const; diff --git a/packages/service/src/pages/index.ts b/packages/service/src/pages/index.ts index 23c8321d..0230fa7c 100644 --- a/packages/service/src/pages/index.ts +++ b/packages/service/src/pages/index.ts @@ -4,3 +4,4 @@ export { NewCar } from "./NewCar"; export { PartsCollection } from "./PartsCollection"; export { PartsPick } from "./PartsPick"; export { PickEvent } from "./PickEvent"; +export { NQuizEventWinnerApply } from "./NQuizEventWinnerApply"; diff --git a/packages/service/src/router.tsx b/packages/service/src/router.tsx index d59f3355..c15845d8 100644 --- a/packages/service/src/router.tsx +++ b/packages/service/src/router.tsx @@ -14,6 +14,7 @@ import { PICK_EVENT_PAGE_ROUTE, N_PARTS_PICK_PAGE_ROUTE, PARTS_COLLECTION_PAGE_ROUTE, + N_QUIZ_EVENT_PAGE_WINNER_APLLY_PAGE_ROUTE, } from "./constants/routes"; import { RotateDemoPage } from "./Demo/pages/RotateDemoPage"; import { AuthDemoPage } from "./Demo/pages/AuthDemoPage"; @@ -31,6 +32,7 @@ import { NewCar, PartsPick, PartsCollection, + NQuizEventWinnerApply, } from "./pages"; export const router = createBrowserRouter([ @@ -44,6 +46,10 @@ export const router = createBrowserRouter([ { path: NEW_CAR_PAGE_ROUTE, element: }, { path: N_PARTS_PICK_PAGE_ROUTE, element: }, { path: PARTS_COLLECTION_PAGE_ROUTE, element: }, + { + path: N_QUIZ_EVENT_PAGE_WINNER_APLLY_PAGE_ROUTE, + element: , + }, ], }, {