diff --git a/packages/service/src/Demo/pages/ButtonDemoPage.tsx b/packages/service/src/Demo/pages/ButtonDemoPage.tsx index 4071930c..f15ea452 100644 --- a/packages/service/src/Demo/pages/ButtonDemoPage.tsx +++ b/packages/service/src/Demo/pages/ButtonDemoPage.tsx @@ -1,7 +1,6 @@ import { Button, ButtonVariant } from "@service/common/components/Button"; import { theme } from "@watermelon-clap/core/src/theme"; import { css } from "@emotion/react"; -import { ClipBoardButton } from "@service/common/components/ClipBoardButton"; import { CheckBox } from "@service/common/components/CheckBox"; import { useState } from "react"; import { ReactComponent as ClipBoardIcon } from "public/icons/clipboard.svg"; @@ -67,8 +66,6 @@ const ButtonDemoPage = () => { button - - + customFetch( + ` + ${import.meta.env.VITE_BACK_BASE_URL}/expectations + `, + { + method: "POST", + body: JSON.stringify({ + expectation: expectation, + }), + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${getAccessToken()}`, + }, + }, + ); diff --git a/packages/service/src/apis/link/apiGetMyShareLink.ts b/packages/service/src/apis/link/apiGetMyShareLink.ts new file mode 100644 index 00000000..db4a65db --- /dev/null +++ b/packages/service/src/apis/link/apiGetMyShareLink.ts @@ -0,0 +1,13 @@ +import { customFetch, getAccessToken } from "@watermelon-clap/core/src/utils"; + +interface IApiGetMyShareLink { + link: string; +} + +export const apiGetMyShareLink = (): Promise => { + return customFetch(`${import.meta.env.VITE_BACK_BASE_URL}/link`, { + headers: { + Authorization: `Bearer ${getAccessToken()}`, + }, + }).then((res) => res.json()); +}; diff --git a/packages/service/src/apis/lottery/apiGetLotteryStatus.ts b/packages/service/src/apis/lottery/apiGetLotteryStatus.ts new file mode 100644 index 00000000..f1ec9eca --- /dev/null +++ b/packages/service/src/apis/lottery/apiGetLotteryStatus.ts @@ -0,0 +1,16 @@ +import { customFetch, getAccessToken } from "@watermelon-clap/core/src/utils"; + +interface IApiGetLotteryStatus { + rank: number; + miniature: boolean; + applied: boolean; +} + +export const apiGetLotteryStatus = (): Promise => + customFetch(`${import.meta.env.VITE_BACK_BASE_URL}/event/lotteries/rank`, { + headers: { + Authorization: `Bearer ${getAccessToken()}`, + }, + }).then((res) => { + return res.json(); + }); diff --git a/packages/service/src/common/components/ClipBoardButton/ClipBoardButton.tsx b/packages/service/src/common/components/ClipBoardButton/ClipBoardButton.tsx index d66e9bf0..5a0b35b0 100644 --- a/packages/service/src/common/components/ClipBoardButton/ClipBoardButton.tsx +++ b/packages/service/src/common/components/ClipBoardButton/ClipBoardButton.tsx @@ -8,51 +8,61 @@ import { } from "./ClipBoardButton.css"; import { ReactComponent as ClipBoardIcon } from "public/icons/clipboard.svg"; -const ClipBoardButton = () => { +interface IClipBoardButton { + copyRef: any; +} +const ClipBoardButton = ({ copyRef }: IClipBoardButton) => { const [isCliped, setIsCliped] = useState(false); const initialText = "클립보드에 복사"; const changeText = "복사되었습니다!"; + const handleCopy = () => { + const textToCopy = copyRef?.current?.textContent; + navigator.clipboard.writeText(textToCopy as string); + }; + return ( - - {isCliped ? ( - setIsCliped(false)} - initial={{ opacity: 0 }} - animate={{ opacity: 1 }} - exit={{ opacity: 0 }} - > - + + {isCliped ? ( + setIsCliped(false)} + initial={{ opacity: 0 }} + animate={{ opacity: 1 }} + exit={{ opacity: 0 }} > - - {changeText} - - - ) : ( - setIsCliped(true)} - initial={{ opacity: 0 }} - animate={{ opacity: 1 }} - exit={{ opacity: 0 }} - > - + + {changeText} + + + ) : ( + setIsCliped(true)} + initial={{ opacity: 0 }} + animate={{ opacity: 1 }} + exit={{ opacity: 0 }} > - - {initialText} - - - )} - + + + {initialText} + + + )} + + ); }; diff --git a/packages/service/src/components/main/EventPeriod/Timer/Timer.css.ts b/packages/service/src/components/main/EventPeriod/Timer/Timer.css.ts index 6b44a8d5..1173c272 100644 --- a/packages/service/src/components/main/EventPeriod/Timer/Timer.css.ts +++ b/packages/service/src/components/main/EventPeriod/Timer/Timer.css.ts @@ -15,7 +15,7 @@ export const staticCardStyles = (position: "upper" | "lower") => css` `; export const textStyles = (translateY?: string, title?: string) => css` - font-family: "PyeongChang Peace"; + ${theme.font.pcpL} font-size: calc(20px + 3vw); -webkit-text-stroke-width: 2px; font-weight: normal; @@ -96,6 +96,7 @@ export const rendererWrap2 = css` align-items: center; gap: calc(10px + 2vw); justify-content: center; + ${mobile(css` gap: 0.3rem; `)}; diff --git a/packages/service/src/constants/routes.ts b/packages/service/src/constants/routes.ts index e316236d..c6efaac7 100644 --- a/packages/service/src/constants/routes.ts +++ b/packages/service/src/constants/routes.ts @@ -16,3 +16,4 @@ export const PARTS_COLLECTION_PAGE_ROUTE = "/parts-collection" as const; export const SHARE_PAGE_ROUTE = "/share/:linkKey" as const; export const N_QUIZ_EVENT_PAGE_WINNER_APLLY_PAGE_ROUTE = "/quiz-event-apply" as const; +export const LOTTER_APPLY_FINISH_PAGE_ROUTE = "/lottery/apply-finish" as const; diff --git a/packages/service/src/index.tsx b/packages/service/src/index.tsx index c4442152..caa10eed 100644 --- a/packages/service/src/index.tsx +++ b/packages/service/src/index.tsx @@ -23,6 +23,6 @@ ReactDOM.createRoot(document.getElementById("root")!).render( - s + , ); diff --git a/packages/service/src/pages/LotteryApplyFinish/LotteryApplyFinish.css.ts b/packages/service/src/pages/LotteryApplyFinish/LotteryApplyFinish.css.ts new file mode 100644 index 00000000..6e6c26ac --- /dev/null +++ b/packages/service/src/pages/LotteryApplyFinish/LotteryApplyFinish.css.ts @@ -0,0 +1,97 @@ +import { css } from "@emotion/react"; +import { mobile } from "@service/common/responsive/responsive"; +import { theme } from "@watermelon-clap/core/src/theme"; + +export const mainBg = css` + background-image: url("/images/common/main-bg.webp"); + background-size: cover; + padding-bottom: 200px; + + color: white; + + ${mobile(css` + padding-bottom: 100px; + `)} +`; + +export const pageTitle = css` + text-align: center; + ${theme.font.pcpB} + font-size : calc(50px + 2vw); + padding-top: 120px; + color: ${theme.color.white}; + + ${mobile(css` + font-size: calc(20px + 2vw); + padding: 100px 0 50px 0; + `)} +`; +export const applyBtn = (isExpectationNull: boolean) => css` + padding: 50px 50px; + height: 100px; + background-color: ${isExpectationNull + ? theme.color.gray400 + : theme.color.eventBlue}; + color: ${isExpectationNull ? theme.color.gray300 : theme.color.white}; + cursor: ${isExpectationNull ? "default" : "pointer"}; + + &:active { + background-color: ${isExpectationNull && theme.color.gray400}; + } + + width: fit-content; +`; + +export const sectionTitle = css` + ${theme.font.pcB28}; + ${mobile(css` + font-size: 24px; + `)} +`; + +export const btn = css` + margin: 0 auto; + background-color: ${theme.color.gray100}; + color: black; + ${theme.font.preB} + font-size : 24px; + + &:hover { + background-color: ${theme.color.gray200}; + } + ${mobile(css` + padding: 10px 20px; + width: 200px; + `)} +`; + +export const shareLinkBox = css` + background-color: ${theme.color.gray100}; + color: ${theme.color.gray300}; + ${theme.font.preM14} + border-radius: 10px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + padding: 16px 18px; + width: 400px; + + ${mobile(css` + width: 100%; + padding: 10px; + `)} +`; + +export const expectationInput = css` + padding: 18px; + height: 100px; + resize: none; + width: 600px; + border-radius: 14px; + background-color: ${theme.color.gray100}; + outline: none; + + ${mobile(css` + width: 100%; + `)} +`; diff --git a/packages/service/src/pages/LotteryApplyFinish/LotteryApplyFinish.tsx b/packages/service/src/pages/LotteryApplyFinish/LotteryApplyFinish.tsx new file mode 100644 index 00000000..bf08a84d --- /dev/null +++ b/packages/service/src/pages/LotteryApplyFinish/LotteryApplyFinish.tsx @@ -0,0 +1,187 @@ +import { useLocation, useNavigate } from "react-router-dom"; +import * as style from "./LotteryApplyFinish.css"; +import { Button, ButtonVariant } from "@service/common/components/Button"; +import { ClipBoardButton } from "@service/common/components/ClipBoardButton"; +import { theme } from "@watermelon-clap/core/src/theme"; +import { Space } from "@service/common/styles/Space"; +import { css } from "@emotion/react"; +import { MAIN_PAGE_ROUTE } from "@service/constants/routes"; +import { ChangeEvent, useEffect, useRef, useState } from "react"; +import { apiGetMyShareLink } from "@service/apis/link/apiGetMyShareLink"; +import { apiPostExpectation } from "@service/apis/expectation/apiPostExpectation"; +import { useModal } from "@watermelon-clap/core/src/hooks"; +import { mobile } from "@service/common/responsive/responsive"; +import { useMobile } from "@service/common/hooks/useMobile"; + +export const LotteryApplyFinish = () => { + const navigate = useNavigate(); + const shareLinkRef = useRef(null); + const [shareLink, setShareLink] = useState(); + const [expectation, setExpectation] = useState(""); + const [isExpectationNull, setIsExpectationNull] = useState(true); + const [isPostExpectation, setIsPostExpectation] = useState(false); + + const { openModal } = useModal(); + const { + state: { isApplied }, + } = useLocation(); + + useEffect(() => { + apiGetMyShareLink().then(({ link }) => { + setShareLink(link); + }); + }, []); + + const handleChange = (event: ChangeEvent) => { + const text = event.currentTarget.value; + setIsExpectationNull(!text.length ? true : false); + if (text.length >= 50) { + openModal({ + type: "alert", + props: { content: "기대평은 50자 이내 작성 가능합니다" }, + }); + return; + } + setExpectation(text); + }; + + const handleSubmit = (event: React.FormEvent) => { + event.preventDefault(); + if (!expectation) { + return; + } + + apiPostExpectation(expectation) + .then(() => { + openModal({ + type: "alert", + props: { + content: "기대평을 성공적으로 등록하였습니다", + }, + }); + setIsPostExpectation(true); + setIsExpectationNull(true); + }) + .catch(() => + openModal({ + type: "alert", + props: { content: "기대평 등록에 실패했습니다" }, + }), + ); + }; + + const isMobile = useMobile(); + + return ( +
+

응모완료

+ + + +
+
+ 내 컬렉션 URL + + 링크를 통해 친구가 이벤트를 참여하면 추가 뽑기권을 드려요! + +
+
+ {shareLink} +
+ +
+
+ + + + {isApplied || ( +
+ + 새롭게 출시된 아반떼 N에 대한 기대평을 남겨주세요 🥳 + + + 남겨주신 기대평은 홈화면에 노출될 수 있습니다! 최대 50자까지 + 작성할 수 있어요. + + +
+