diff --git a/index.html b/index.html index 6ccc736..130ded5 100644 --- a/index.html +++ b/index.html @@ -2,7 +2,10 @@ - + misik diff --git a/public/assets/img/img-graphic-logo-blur.png b/public/assets/img/img-graphic-logo-blur.png new file mode 100644 index 0000000..5f4b21d Binary files /dev/null and b/public/assets/img/img-graphic-logo-blur.png differ diff --git a/public/assets/img/img-graphic-logo.png b/public/assets/img/img-graphic-logo.png index c0964b5..8d4faa3 100644 Binary files a/public/assets/img/img-graphic-logo.png and b/public/assets/img/img-graphic-logo.png differ diff --git a/public/assets/img/img-style-cute.png b/public/assets/img/img-style-cute.png index 2010cfa..5331fc1 100644 Binary files a/public/assets/img/img-style-cute.png and b/public/assets/img/img-style-cute.png differ diff --git a/public/assets/img/img-style-friendly.png b/public/assets/img/img-style-friendly.png index 920c03a..aa88f8e 100644 Binary files a/public/assets/img/img-style-friendly.png and b/public/assets/img/img-style-friendly.png differ diff --git a/public/assets/img/img-style-trust.png b/public/assets/img/img-style-trust.png index 1566502..e4b5878 100644 Binary files a/public/assets/img/img-style-trust.png and b/public/assets/img/img-style-trust.png differ diff --git a/src/components/CreateReviewLoading/CreateReviewLoading.tsx b/src/components/CreateReviewLoading/CreateReviewLoading.tsx deleted file mode 100644 index 285e4b5..0000000 --- a/src/components/CreateReviewLoading/CreateReviewLoading.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import styles from "@/components/CreateReviewLoading/CreateReviewLoading.module.scss"; -import Text from "@/components/ui/Text/Text"; - -const CreateReviewLoading = () => { - return ( -
-
- - 리뷰를 만들고 있어요 - - - 최대 30초까지 소요될 수 있어요 - -
- -
- createReviewImg -
-
- ); -}; - -export default CreateReviewLoading; diff --git a/src/components/Home/Home.module.scss b/src/components/Home/Home.module.scss index 606134c..a831ebf 100644 --- a/src/components/Home/Home.module.scss +++ b/src/components/Home/Home.module.scss @@ -46,8 +46,8 @@ margin-top: 3.75rem; & > img { - width: 14.375rem; - height: 14.375rem; + width: 22.5rem; + height: 22.5rem; } } diff --git a/src/components/Home/Home.tsx b/src/components/Home/Home.tsx index e786ada..63e2b28 100644 --- a/src/components/Home/Home.tsx +++ b/src/components/Home/Home.tsx @@ -15,10 +15,14 @@ const Home = () => { const { scanData } = useScanDataStore(); - const { navigateToReceiptEdit } = useRoute(); + const { navigateToReceiptEdit, navigateToRecognitionFail } = useRoute(); useEffect(() => { - if (scanData.parsed && scanData.parsed.length > 0) { + if (scanData === "error") { + navigateToRecognitionFail(); + } + + if (typeof scanData !== "string" && scanData.parsed && scanData.parsed.length > 0) { navigateToReceiptEdit(); } }, [scanData]); diff --git a/src/components/HomeNavigateConfirmModal/HomeNavigateConfirmModal.module.scss b/src/components/HomeNavigateConfirmModal/HomeNavigateConfirmModal.module.scss index a5f12e8..a89d74a 100644 --- a/src/components/HomeNavigateConfirmModal/HomeNavigateConfirmModal.module.scss +++ b/src/components/HomeNavigateConfirmModal/HomeNavigateConfirmModal.module.scss @@ -1,5 +1,8 @@ .Modal { padding: 1.875rem 1.25rem 1.25rem; + display: flex; + flex-direction: column; + align-items: center; & > p { margin-top: 0.375rem; @@ -16,25 +19,3 @@ width: 8.75rem; } } - -.ShowButtonWrapper { - display: flex; - align-items: flex-end; - justify-content: center; - justify-self: center; - gap: 0.375rem; - margin-top: 0.25rem; - width: 6.6875rem; - height: 1.75rem; - cursor: pointer; - - & > svg > path { - fill: rgba(0, 0, 0, 0.15); - } - - &.isChecked { - & > svg > path { - fill: rgb(54, 54, 66); - } - } -} diff --git a/src/components/HomeNavigateConfirmModal/HomeNavigateConfirmModal.stories.tsx b/src/components/HomeNavigateConfirmModal/HomeNavigateConfirmModal.stories.tsx index f6946a8..31514b2 100644 --- a/src/components/HomeNavigateConfirmModal/HomeNavigateConfirmModal.stories.tsx +++ b/src/components/HomeNavigateConfirmModal/HomeNavigateConfirmModal.stories.tsx @@ -1,10 +1,5 @@ -import { useState } from "react"; - -import classNames from "classnames"; - import styles from "@/components/HomeNavigateConfirmModal/HomeNavigateConfirmModal.module.scss"; import Button from "@/components/ui/Button/Button"; -import Icon from "@/components/ui/Icon/Icon"; import Modal from "@/components/ui/Modal/Modal"; import Text from "@/components/ui/Text/Text"; @@ -18,13 +13,8 @@ interface HomeNavigateConfirmModalProps { } const HomeNavigateConfirmModalStory = ({ isOpen, handleClose }: HomeNavigateConfirmModalProps) => { - const [isShowButtonChecked, setIsShowButtonChecked] = useState(false); - - const handleShowButtonClick = () => { - setIsShowButtonChecked((prev) => !prev); - }; return ( - +
홈으로 가시겠어요? @@ -36,17 +26,6 @@ const HomeNavigateConfirmModalStory = ({ isOpen, handleClose }: HomeNavigateConf
-
); diff --git a/src/components/HomeNavigateConfirmModal/HomeNavigateConfirmModal.tsx b/src/components/HomeNavigateConfirmModal/HomeNavigateConfirmModal.tsx index 33daaa4..e02ad19 100644 --- a/src/components/HomeNavigateConfirmModal/HomeNavigateConfirmModal.tsx +++ b/src/components/HomeNavigateConfirmModal/HomeNavigateConfirmModal.tsx @@ -1,10 +1,5 @@ -import { useState } from "react"; - -import classNames from "classnames"; - import styles from "@/components/HomeNavigateConfirmModal/HomeNavigateConfirmModal.module.scss"; import Button from "@/components/ui/Button/Button"; -import Icon from "@/components/ui/Icon/Icon"; import Modal from "@/components/ui/Modal/Modal"; import Text from "@/components/ui/Text/Text"; @@ -14,27 +9,19 @@ import { useGenerateReviewStore } from "@/store/useGenerateReviewStore"; import { useCreateReviewStore } from "@/store/useReviewStore"; import { useScanDataStore } from "@/store/useScanDataStore"; -interface HomeNavigateConfirmModalProps { +export interface ModalProps { isOpen: boolean; handleClose: () => void; } -const HomeNavigateConfirmModal = ({ isOpen, handleClose }: HomeNavigateConfirmModalProps) => { +const HomeNavigateConfirmModal = ({ isOpen, handleClose }: ModalProps) => { const { navigateToHome } = useRoute(); const { resetGenerateReviewData } = useGenerateReviewStore(); const { resetCreateReviewData } = useCreateReviewStore(); const { resetScanData } = useScanDataStore(); - // 이후 상태 초기값 재설정 - const [isShowButtonChecked, setIsShowButtonChecked] = useState(false); - - const handleShowButtonClick = () => { - setIsShowButtonChecked((prev) => !prev); - }; - const handleNavigateHome = () => { - handleClose(); resetGenerateReviewData(); resetCreateReviewData(); resetScanData(); @@ -42,7 +29,7 @@ const HomeNavigateConfirmModal = ({ isOpen, handleClose }: HomeNavigateConfirmMo }; return ( - +
홈으로 가시겠어요? @@ -54,17 +41,6 @@ const HomeNavigateConfirmModal = ({ isOpen, handleClose }: HomeNavigateConfirmMo
-
); diff --git a/src/components/ReceiptEdit/ReceiptEdit.module.scss b/src/components/ReceiptEdit/ReceiptEdit.module.scss index 43f03b1..9310cf1 100644 --- a/src/components/ReceiptEdit/ReceiptEdit.module.scss +++ b/src/components/ReceiptEdit/ReceiptEdit.module.scss @@ -4,6 +4,7 @@ padding-bottom: 2.5rem; height: calc(100vh - 2.75rem); overflow: hidden; + overflow-y: auto; position: relative; display: flex; flex-direction: column; @@ -52,4 +53,5 @@ align-items: center; gap: 0.875rem; z-index: 1; + margin-top: 2.5rem; } diff --git a/src/components/ReceiptEdit/ReceiptEdit.tsx b/src/components/ReceiptEdit/ReceiptEdit.tsx index 773a78e..5e05cf6 100644 --- a/src/components/ReceiptEdit/ReceiptEdit.tsx +++ b/src/components/ReceiptEdit/ReceiptEdit.tsx @@ -10,7 +10,44 @@ import { useRoute } from "@/hooks/common/useRoute"; import { useCreateReviewStore } from "@/store/useReviewStore"; import { useScanDataStore } from "@/store/useScanDataStore"; +const useKeyboardAvoidance = () => { + const [keyboardVisible, setKeyboardVisible] = useState(false); + const [keyboardHeight, setKeyboardHeight] = useState(0); + + useEffect(() => { + const handleVisualViewportChange = () => { + const isKeyboardVisible = !!( + window.visualViewport && window.visualViewport.height < window.innerHeight + ); + setKeyboardVisible(isKeyboardVisible); + + if (isKeyboardVisible) { + setKeyboardHeight( + window.visualViewport ? window.innerHeight - window.visualViewport.height : 0, + ); + } else { + setKeyboardHeight(0); + } + }; + + if (window.visualViewport) { + window.visualViewport.addEventListener("resize", handleVisualViewportChange); + handleVisualViewportChange(); + } + + return () => { + if (window.visualViewport) { + window.visualViewport.removeEventListener("resize", handleVisualViewportChange); + } + }; + }, []); + + return { keyboardVisible, keyboardHeight }; +}; + const ReceiptEdit = () => { + const { keyboardVisible, keyboardHeight } = useKeyboardAvoidance(); + const { navigateToHome, navigateToSelectTag } = useRoute(); const { scanData, resetScanData } = useScanDataStore(); @@ -21,14 +58,16 @@ const ReceiptEdit = () => { const [focusState, setFocusState] = useState<{ [key: string]: boolean }>({}); useEffect(() => { - if (Array.isArray(scanData.parsed) && scanData.parsed.length > 0) { + if ( + typeof scanData !== "string" && + Array.isArray(scanData.parsed) && + scanData.parsed.length > 0 + ) { setFormData(scanData.parsed); const initialFocusState = scanData.parsed.reduce( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (acc: any, data: any) => { - const keys = Object.keys(data); - keys.forEach((key) => (acc[key] = false)); + (acc: { [key: string]: boolean }, data: { key: string; value: string }) => { + acc[data.key] = false; return acc; }, {} as { [key: string]: boolean }, @@ -45,14 +84,9 @@ const ReceiptEdit = () => { setFocusState((prevState) => ({ ...prevState, [key]: false })); }; - const handleInputChange = (index: number, key: string, value: string) => { + const handleInputChange = (key: string, value: string) => { setFormData((prevData) => - prevData.map((item, idx) => { - if (idx === index) { - return { ...item, [key]: value }; - } - return item; - }), + prevData.map((item) => (item.key === key ? { ...item, value } : item)), ); }; @@ -105,7 +139,7 @@ const ReceiptEdit = () => { onFocus={() => handleFocus(data.key)} onBlur={() => handleBlur(data.key)} isFocus={focusState[data.key] || false} - onChange={(e) => handleInputChange(index, data.key, e.target.value)} + onChange={(e) => handleInputChange(data.key, e.target.value)} /> @@ -113,7 +147,12 @@ const ReceiptEdit = () => { -
+
{Object.values(focusState).some((isFocus) => isFocus) ? ( +
- {`${selectedStyle.name}-img`} -
- -
- {IMG_STYLE_DATA.map((style) => ( -
handleStyleClick(style)} - > - - {style.name} - - -
- ))} + {`${selectedStyle.name}-img`}
+
+ {IMG_STYLE_DATA.map((style) => ( +
handleStyleClick(style)} + > + + {style.name} + + +
+ ))} +
+
+ + ); }; diff --git a/src/components/SelectStyle/StyleExampleModal.module.scss b/src/components/SelectStyle/StyleExampleModal.module.scss new file mode 100644 index 0000000..9b8e383 --- /dev/null +++ b/src/components/SelectStyle/StyleExampleModal.module.scss @@ -0,0 +1,21 @@ +.Modal { + padding: 1.875rem 1.25rem; + display: flex; + flex-direction: column; + gap: 1.5rem; + width: 20.5rem; + + position: relative; + + p { + margin-top: 0.3125rem; + } +} + +.IconButton { + width: 3rem; + height: 3rem; + position: absolute; + right: 0; + top: 0; +} diff --git a/src/components/SelectStyle/StyleExampleModal.tsx b/src/components/SelectStyle/StyleExampleModal.tsx new file mode 100644 index 0000000..923840b --- /dev/null +++ b/src/components/SelectStyle/StyleExampleModal.tsx @@ -0,0 +1,47 @@ +import type { ModalProps } from "@/components/HomeNavigateConfirmModal/HomeNavigateConfirmModal"; +import styles from "@/components/SelectStyle/StyleExampleModal.module.scss"; +import Icon from "@/components/ui/Icon/Icon"; +import Modal from "@/components/ui/Modal/Modal"; +import Text from "@/components/ui/Text/Text"; + +const EXAMPLE_DATA = [ + { + title: "친근한 말투", + content: + "매장도 전체적으로 깔끔하고 넓어서 오래 앉아있기에도 좋았어~ 직원들도 친절하게 응대해줘서 편하게 있다가 왔어ᄒᄒ", + }, + { + title: "믿음직한 말투", + content: + "매장이 매우 쾌적해요. 인테리어 디자인도 깔끔하고 테이블과 의자도 편안하게 배치되어 있어요. 직원분들도 친절하시고 서비스도 빨라요. 그래서 저는 앞으로도 자주 이용할 생각이에요.", + }, + { + title: "귀여운 말투", + content: + "매장도 가봤는데 깔끔하고 넓어서 좋더라🤩 직원들도 친절하게 응대해주고..^^ 다음에도 또 방문할 거예요! 🍕💕 추천추천!!", + }, +]; + +const StyleExampleModal = ({ isOpen, handleClose }: ModalProps) => { + return ( + +
+ + {EXAMPLE_DATA.map((data) => ( +
+ + {data.title} + + + {data.content} + +
+ ))} +
+
+ ); +}; + +export default StyleExampleModal; diff --git a/src/components/SelectTag/SelectTag.tsx b/src/components/SelectTag/SelectTag.tsx index 3733a60..2837a6e 100644 --- a/src/components/SelectTag/SelectTag.tsx +++ b/src/components/SelectTag/SelectTag.tsx @@ -45,6 +45,7 @@ const SelectTag = () => { setIsBottomSheetOpen(false); if (!tagList.includes(newTag)) { setTagList((prevTagList: string[]) => [...prevTagList, newTag]); + setSelectedTagList((prevSelectedTags) => [...prevSelectedTags, newTag]); } }; diff --git a/src/components/provider/AppBridgeProvider/AppBridgeProvider.tsx b/src/components/provider/AppBridgeProvider/AppBridgeProvider.tsx index 1198572..de18a88 100644 --- a/src/components/provider/AppBridgeProvider/AppBridgeProvider.tsx +++ b/src/components/provider/AppBridgeProvider/AppBridgeProvider.tsx @@ -43,7 +43,11 @@ export function AppBridgeProvider({ children }: AppBridgeProviderProps) { window.response = { receiveScanResult: (jsonData: string) => { try { - setScanData(JSON.parse(jsonData)); + if (jsonData === "error") { + setScanData("error"); + } else { + setScanData(JSON.parse(jsonData)); + } } catch (error) { console.error("Invalid JSON data for scan result:", error); } diff --git a/src/components/ui/IconButton/IconButton.module.scss b/src/components/ui/IconButton/IconButton.module.scss index cad698d..a6118fa 100644 --- a/src/components/ui/IconButton/IconButton.module.scss +++ b/src/components/ui/IconButton/IconButton.module.scss @@ -7,7 +7,7 @@ &.size-md { gap: 0.375rem; - background-color: var(--color-gray400); + background-color: var(--color-text-primary); color: var(--color-white); @include buttonSecondary; } diff --git a/src/components/ui/Input/Input.module.scss b/src/components/ui/Input/Input.module.scss index b974222..5c7703d 100644 --- a/src/components/ui/Input/Input.module.scss +++ b/src/components/ui/Input/Input.module.scss @@ -1,20 +1,5 @@ @use "@/styles/_mixins.scss" as *; -.InputWrapper { - position: relative; - display: flex; - align-items: center; - - button { - position: absolute; - right: 0.875rem; - color: var(--color-primary-purple); - line-height: 1.3125rem; - - @include bodySm; - } -} - .Input { width: 100%; height: 3rem; diff --git a/src/components/ui/Input/Input.tsx b/src/components/ui/Input/Input.tsx index 854bf31..e2a709d 100644 --- a/src/components/ui/Input/Input.tsx +++ b/src/components/ui/Input/Input.tsx @@ -28,23 +28,20 @@ const Input = forwardRef( }, [isFocus]); return ( -
- - {variant === "primary" && } -
+ ); }, ); diff --git a/src/components/ui/Modal/Modal.tsx b/src/components/ui/Modal/Modal.tsx index 2da4521..b1c394e 100644 --- a/src/components/ui/Modal/Modal.tsx +++ b/src/components/ui/Modal/Modal.tsx @@ -1,19 +1,18 @@ import type { PropsWithChildren } from "react"; -import { useEffect, useRef } from "react"; +import { useEffect } from "react"; import classNames from "classnames"; import styles from "@/components/ui/Modal/Modal.module.scss"; import Portal from "@/components/ui/Modal/Portal"; -import useClickOutside from "@/hooks/common/useClickOutside"; - interface ModalProps extends PropsWithChildren { - onClose: () => void; isOpen: boolean; + handleClose?: () => void; + isBackdropClose?: boolean; } -const Modal = ({ isOpen, children, onClose }: ModalProps) => { +const Modal = ({ isOpen, isBackdropClose, handleClose, children }: ModalProps) => { useEffect(() => { document.body.style.overflow = "hidden"; @@ -22,10 +21,6 @@ const Modal = ({ isOpen, children, onClose }: ModalProps) => { }; }, []); - const modalRef = useRef(null); - - useClickOutside(modalRef, onClose); - return ( <> {isOpen && ( @@ -34,10 +29,10 @@ const Modal = ({ isOpen, children, onClose }: ModalProps) => { className={classNames(styles.ModalBackdrop, { [styles.Open]: isOpen, })} + onClick={() => isBackdropClose && handleClose && handleClose()} />
{ navigateToHome: () => navigate(PATH.HOME), navigateToBack: () => navigate(-1), navigateToReceiptEdit: () => navigate(PATH.RECEIPT_EDIT), + navigateToRecognitionFail: () => navigate(PATH.RECOGNITION_FAIL), navigateToSelectStyle: () => navigate(PATH.SELECT_STYLE), navigateToSelectTag: () => navigate(PATH.SELECT_TAG), navigateToReviewResult: () => navigate(PATH.REVIEW_RESULT), + navigateToLoading: () => navigate(PATH.LOADING), + navigateToCreateReviewFail: () => navigate(PATH.CREATE_REVIEW_FAIL), }; return routes; diff --git a/src/pages/CreateReviewFailPage/CreateReviewFailPage.module.scss b/src/pages/CreateReviewFailPage/CreateReviewFailPage.module.scss new file mode 100644 index 0000000..a1ead19 --- /dev/null +++ b/src/pages/CreateReviewFailPage/CreateReviewFailPage.module.scss @@ -0,0 +1,28 @@ +.CreateReviewFail { + padding: 5rem 1.25rem 2.5rem; + height: 100vh; + overflow: hidden; + display: flex; + flex-direction: column; + justify-content: space-between; + background: var(--color-bg-gradient); +} + +.Top { + h2 { + white-space: pre-line; + margin-top: 0.625rem; + } +} + +.Image { + margin-top: 6.25rem; + display: flex; + justify-content: center; + align-items: center; + + & > img { + width: 22.5rem; + height: 22.5rem; + } +} diff --git a/src/pages/CreateReviewFailPage/CreateReviewFailPage.tsx b/src/pages/CreateReviewFailPage/CreateReviewFailPage.tsx new file mode 100644 index 0000000..0f6786a --- /dev/null +++ b/src/pages/CreateReviewFailPage/CreateReviewFailPage.tsx @@ -0,0 +1,30 @@ +import Button from "@/components/ui/Button/Button"; +import Text from "@/components/ui/Text/Text"; + +import { useRoute } from "@/hooks/common/useRoute"; + +import styles from "@/pages/CreateReviewFailPage/CreateReviewFailPage.module.scss"; + +const CreateReviewFailPage = () => { + const { navigateToHome } = useRoute(); + + return ( +
+
+ + 리뷰 생성에 실패했어요 + + + {`네트워크 오류로 인해 리뷰 생성에\n실패했어요. 다시 시도해주세요.`} + + +
+ createReviewFailImg +
+
+
+ ); +}; + +export default CreateReviewFailPage; diff --git a/src/components/CreateReviewLoading/CreateReviewLoading.module.scss b/src/pages/LoadingPage/LoadingPage.module.scss similarity index 100% rename from src/components/CreateReviewLoading/CreateReviewLoading.module.scss rename to src/pages/LoadingPage/LoadingPage.module.scss diff --git a/src/pages/LoadingPage/LoadingPage.tsx b/src/pages/LoadingPage/LoadingPage.tsx new file mode 100644 index 0000000..8467414 --- /dev/null +++ b/src/pages/LoadingPage/LoadingPage.tsx @@ -0,0 +1,49 @@ +import { useEffect } from "react"; + +import { AppBridgeMessageType } from "@/components/provider/AppBridgeProvider/AppBridgeMessage.types"; +import { useAppBridge } from "@/components/provider/AppBridgeProvider/AppBridgeProvider"; +import Text from "@/components/ui/Text/Text"; + +import { useRoute } from "@/hooks/common/useRoute"; + +import styles from "@/pages/LoadingPage/LoadingPage.module.scss"; + +import { useGenerateReviewStore } from "@/store/useGenerateReviewStore"; + +const LoadingPage = () => { + const { send } = useAppBridge(); + + const { navigateToReviewResult } = useRoute(); + + const { generateReviewData } = useGenerateReviewStore(); + + useEffect(() => { + send({ + type: AppBridgeMessageType.RECEIVE_GENERATED_REVIEW, + payload: { result: String(generateReviewData) }, + }); + + if (generateReviewData.length > 0) { + navigateToReviewResult(); + } + }, [generateReviewData]); + + return ( +
+
+ + 리뷰를 만들고 있어요 + + + 최대 30초까지 소요될 수 있어요 + +
+ +
+ createReviewImg +
+
+ ); +}; + +export default LoadingPage; diff --git a/src/pages/ReviewResultPage.tsx b/src/pages/ReviewResultPage.tsx deleted file mode 100644 index cffc64d..0000000 --- a/src/pages/ReviewResultPage.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { useEffect, useState } from "react"; - -import CreateReviewLoading from "@/components/CreateReviewLoading/CreateReviewLoading"; -import ReviewResult from "@/components/ReviewResult/ReviewResult"; - -// 로딩 코드 api 연결 후 삭제 -export default function ReviewResultPage() { - const [isLoading, setIsLoading] = useState(true); - - useEffect(() => { - const timer = setTimeout(() => { - setIsLoading(false); - }, 3000); - - return () => clearTimeout(timer); - }, []); - - if (isLoading) { - return ; - } - - return <>{!isLoading && }; -} diff --git a/src/components/ReviewResult/ReviewResult.module.scss b/src/pages/ReviewResultPage/ReviewResultPage.module.scss similarity index 100% rename from src/components/ReviewResult/ReviewResult.module.scss rename to src/pages/ReviewResultPage/ReviewResultPage.module.scss diff --git a/src/components/ReviewResult/ReviewResult.tsx b/src/pages/ReviewResultPage/ReviewResultPage.tsx similarity index 87% rename from src/components/ReviewResult/ReviewResult.tsx rename to src/pages/ReviewResultPage/ReviewResultPage.tsx index 25430d5..61c8154 100644 --- a/src/components/ReviewResult/ReviewResult.tsx +++ b/src/pages/ReviewResultPage/ReviewResultPage.tsx @@ -5,24 +5,28 @@ import confetti from "canvas-confetti"; import HomeNavigateConfirmModal from "@/components/HomeNavigateConfirmModal/HomeNavigateConfirmModal"; import { AppBridgeMessageType } from "@/components/provider/AppBridgeProvider/AppBridgeMessage.types"; import { useAppBridge } from "@/components/provider/AppBridgeProvider/AppBridgeProvider"; -import styles from "@/components/ReviewResult/ReviewResult.module.scss"; import Button from "@/components/ui/Button/Button"; import IconButton from "@/components/ui/IconButton/IconButton"; import Text from "@/components/ui/Text/Text"; import Toast from "@/components/ui/Toast/Toast"; import { useOverlay } from "@/hooks/common/useOverlay"; +import { useRoute } from "@/hooks/common/useRoute"; import useToast from "@/hooks/common/useToast"; +import styles from "@/pages/ReviewResultPage/ReviewResultPage.module.scss"; + import { useGenerateReviewStore } from "@/store/useGenerateReviewStore"; import type { Options as ConfettiOptions } from "canvas-confetti"; -const ReviewResult = () => { +export default function ReviewResultPage() { const { send } = useAppBridge(); const { generateReviewData } = useGenerateReviewStore(); + const { navigateToCreateReviewFail } = useRoute(); + const { isOpen, handleClose, handleOpen } = useOverlay(); const { isToast, showToast } = useToast(1000); @@ -39,8 +43,12 @@ const ReviewResult = () => { }; useEffect(() => { - handleConfetti(); - }, []); + if (generateReviewData === "error") { + navigateToCreateReviewFail(); + } else { + handleConfetti(); + } + }, [generateReviewData]); return (
@@ -82,6 +90,4 @@ const ReviewResult = () => {
); -}; - -export default ReviewResult; +} diff --git a/src/router/AppRouter.tsx b/src/router/AppRouter.tsx index 3988153..25dca7c 100644 --- a/src/router/AppRouter.tsx +++ b/src/router/AppRouter.tsx @@ -4,10 +4,12 @@ import App from "@/App"; import { PATH } from "@/constants/path"; +import CreateReviewFailPage from "@/pages/CreateReviewFailPage/CreateReviewFailPage"; import HomePage from "@/pages/HomePage"; +import LoadingPage from "@/pages/LoadingPage/LoadingPage"; import ReceiptEditPage from "@/pages/ReceiptEditPage"; import RecognitionFailPage from "@/pages/RecognitionFailPage"; -import ReviewResultPage from "@/pages/ReviewResultPage"; +import ReviewResultPage from "@/pages/ReviewResultPage/ReviewResultPage"; import SelectStylePage from "@/pages/SelectStylePage"; import SelectTagPage from "@/pages/SelectTagPage"; @@ -37,10 +39,15 @@ const AppRouter = () => { path: PATH.SELECT_STYLE, element: , }, + { + path: PATH.LOADING, + element: , + }, { path: PATH.REVIEW_RESULT, element: , }, + { path: PATH.CREATE_REVIEW_FAIL, element: }, ], }, ]); diff --git a/src/store/useScanDataStore.ts b/src/store/useScanDataStore.ts index 19d4baa..45c94c0 100644 --- a/src/store/useScanDataStore.ts +++ b/src/store/useScanDataStore.ts @@ -1,13 +1,13 @@ import { create } from "zustand"; interface ScanDataStoreProps { - scanData: ScanResultPayload; - setScanData: (scanData: ScanResultPayload) => void; + scanData: ScanResultPayload | string; + setScanData: (scanData: ScanResultPayload | string) => void; resetScanData: () => void; } export const useScanDataStore = create((set) => ({ scanData: { parsed: [] }, - setScanData: (scanData: ScanResultPayload) => set({ scanData }), + setScanData: (scanData: ScanResultPayload | string) => set({ scanData }), resetScanData: () => set({ scanData: { parsed: [] } }), })); diff --git a/src/styles/global.scss b/src/styles/global.scss index 852b28b..7aee436 100644 --- a/src/styles/global.scss +++ b/src/styles/global.scss @@ -34,6 +34,11 @@ font-style: normal; } +html { + font-family: "Pretendard", sans-serif; + overscroll-behavior: none; +} + * { font-family: "Pretendard", sans-serif; padding: 0; @@ -44,6 +49,10 @@ body { max-width: 37.5rem; margin: 0 auto; + overscroll-behavior: none; + position: relative; + overflow-x: hidden; + -webkit-overflow-scrolling: touch; } a {