From 60fea9e5d252e7626dfcaec145726f0937d40cec Mon Sep 17 00:00:00 2001 From: Yo Wook Kim Date: Sun, 31 Jul 2022 20:11:38 +0900 Subject: [PATCH 1/2] =?UTF-8?q?[FE]=20=EB=AA=A8=EB=8B=AC=20UI=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20(#305)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit feat: 모달 UI 구현 및 스토리 작성 --- .../components/common/Modal/Modal.stories.tsx | 87 +++++++++++++++++++ .../components/common/Modal/Modal.style.tsx | 76 ++++++++++++++++ .../src/components/common/Modal/Modal.tsx | 57 ++++++++++++ 3 files changed, 220 insertions(+) create mode 100644 frontend/src/components/common/Modal/Modal.stories.tsx create mode 100644 frontend/src/components/common/Modal/Modal.style.tsx create mode 100644 frontend/src/components/common/Modal/Modal.tsx diff --git a/frontend/src/components/common/Modal/Modal.stories.tsx b/frontend/src/components/common/Modal/Modal.stories.tsx new file mode 100644 index 00000000..fdc368cb --- /dev/null +++ b/frontend/src/components/common/Modal/Modal.stories.tsx @@ -0,0 +1,87 @@ +import Modal from '@/components/common/Modal/Modal'; +import { PropsWithChildren, useState } from 'react'; + +export default { + component: Modal, + title: 'Components/Modal', +}; + +type Props = { + showConfirm: boolean; +}; + +const Template = ({ showConfirm, children }: PropsWithChildren) => { + const [show, setShow] = useState(false); + + const handleClose = () => { + setShow(false); + }; + const handleSubmit = () => { + alert('제출됨'); + handleClose(); + }; + return ( + <> + + {show && ( + + {children} + + )} + + ); +}; + +export const Default = () => ( + +); + +export const NoTitle = () => ( + +); + +export const NoConfirm = () => ( + +); + +export const NoTitleAndConfirm = () => ( + +); diff --git a/frontend/src/components/common/Modal/Modal.style.tsx b/frontend/src/components/common/Modal/Modal.style.tsx new file mode 100644 index 00000000..487a0f02 --- /dev/null +++ b/frontend/src/components/common/Modal/Modal.style.tsx @@ -0,0 +1,76 @@ +import styled from 'styled-components'; + +export const Container = styled.section` + width: 100vw; + height: 100vh; + + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +`; + +export const Backdrop = styled.div` + width: 100%; + height: 100%; + + position: absolute; + top: 0; + left: 0; + z-index: 1; + + background-color: #00000033; + + height: 100%; +`; + +export const Content = styled.section` + width: 30rem; + min-height: 10rem; + padding: 1.5rem; + + z-index: 2; + + display: flex; + flex-direction: column; + justify-content: space-between; + align-items: center; + gap: 1.5rem; + + border-radius: 0.5rem; + + filter: drop-shadow(2px 2px 5px rgba(0, 0, 0, 0.25)); + background-color: ${({ theme }) => theme.colors.white}; +`; + +export const Title = styled.h1` + font-size: 1.5rem; +`; + +export const Body = styled.div``; + +export const ButtonContainer = styled.div` + display: flex; + justify-content: center; + gap: 2rem; +`; + +export const ActionButton = styled.button` + padding: 0.5rem 1rem; + border-radius: 0.3rem; + border: none; + + filter: drop-shadow(1px 1px 2px rgba(0, 0, 0, 0.25)); + + &:hover { + filter: drop-shadow(2px 2px 4px rgba(0, 0, 0, 0.25)); + } +`; + +export const ConfirmButton = styled(ActionButton)` + background-color: ${({ theme }) => theme.colors.primary}; +`; + +export const CloseButton = styled(ActionButton)` + background-color: ${({ theme }) => theme.colors.secondary}; +`; diff --git a/frontend/src/components/common/Modal/Modal.tsx b/frontend/src/components/common/Modal/Modal.tsx new file mode 100644 index 00000000..c51cc899 --- /dev/null +++ b/frontend/src/components/common/Modal/Modal.tsx @@ -0,0 +1,57 @@ +import { createPortal } from 'react-dom'; +import * as S from '@/components/common/Modal/Modal.style'; +import { PropsWithChildren } from 'react'; + +type Props = { + handleClose: () => void; + handleConfirm?: () => void; +}; + +function Modal({ + handleClose, + handleConfirm, + children, +}: PropsWithChildren) { + return createPortal( + + + + {children} + + + , + document.querySelector('#root') + ); +} + +function Title({ children }: PropsWithChildren) { + return {children}; +} + +function Body({ children }: PropsWithChildren) { + return {children}; +} + +type ActionButtonProps = { + handleClose: () => void; + handleConfirm?: () => void; +}; + +function ActionButtons({ handleClose, handleConfirm }: ActionButtonProps) { + return ( + + 닫기 + {handleConfirm && ( + 확인 + )} + + ); +} + +Modal.Title = Title; +Modal.Body = Body; + +export default Modal; From 0ccf768145ea3d126f5e57050955647c8b746e66 Mon Sep 17 00:00:00 2001 From: Yo Wook Kim Date: Sun, 31 Jul 2022 22:19:33 +0900 Subject: [PATCH 2/2] =?UTF-8?q?[FE]=20=EB=AA=A8=EB=8B=AC=EB=A1=9C=20window?= =?UTF-8?q?.alert=20=EB=B0=8F=20window.confirm=20=EB=8C=80=EC=B2=B4=20(#30?= =?UTF-8?q?7)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: modal 렌더링 컨텍스트 구현 * feat: 모달 스타일 수정 * chore: 이벤트 핸들러에서 promise 사용할 수 있도록 lint 설정 변경 * feat: useModal 훅 구현 및 적용 --- frontend/.eslintrc.json | 8 ++- frontend/src/components/Login/Login.tsx | 4 +- .../components/common/HeaderNav/HeaderNav.tsx | 11 ++-- .../components/common/Modal/Modal.style.tsx | 14 +++-- .../src/components/common/Modal/Modal.tsx | 15 ++++- .../src/contexts/ModalContextProvider.tsx | 60 +++++++++++++++++++ frontend/src/hooks/api/useDelete.tsx | 3 +- frontend/src/hooks/api/useGet.tsx | 3 +- frontend/src/hooks/api/useGetMany.tsx | 4 +- frontend/src/hooks/api/useGetOne.tsx | 5 +- frontend/src/hooks/api/usePatch.tsx | 3 +- frontend/src/hooks/api/usePost.tsx | 7 ++- frontend/src/hooks/api/usePut.tsx | 7 ++- frontend/src/hooks/useConfirm.tsx | 0 frontend/src/hooks/useError.tsx | 20 +++++++ frontend/src/hooks/useModal.tsx | 13 ++++ frontend/src/index.tsx | 5 +- frontend/src/pages/Product/Product.tsx | 9 ++- frontend/src/pages/Register/Register.tsx | 21 +++---- frontend/src/utils/handleError.ts | 13 ---- 20 files changed, 177 insertions(+), 48 deletions(-) create mode 100644 frontend/src/contexts/ModalContextProvider.tsx create mode 100644 frontend/src/hooks/useConfirm.tsx create mode 100644 frontend/src/hooks/useError.tsx create mode 100644 frontend/src/hooks/useModal.tsx delete mode 100644 frontend/src/utils/handleError.ts diff --git a/frontend/.eslintrc.json b/frontend/.eslintrc.json index 1a9fc7bd..2843f4d6 100644 --- a/frontend/.eslintrc.json +++ b/frontend/.eslintrc.json @@ -19,6 +19,12 @@ "prettier" ], "rules": { - "react-hooks/exhaustive-deps": 0 + "react-hooks/exhaustive-deps": 0, + "@typescript-eslint/no-misused-promises": [ + "error", + { + "checksVoidReturn": false + } + ] } } diff --git a/frontend/src/components/Login/Login.tsx b/frontend/src/components/Login/Login.tsx index 2a135709..b48e1373 100644 --- a/frontend/src/components/Login/Login.tsx +++ b/frontend/src/components/Login/Login.tsx @@ -2,11 +2,13 @@ import Loading from '@/components/common/Loading/Loading'; import ROUTES from '@/constants/routes'; import { UserDataContext } from '@/contexts/LoginContextProvider'; import useAuth from '@/hooks/useAuth'; +import useModal from '@/hooks/useModal'; import { useContext, useEffect } from 'react'; import { useNavigate, useSearchParams } from 'react-router-dom'; function Login() { const { login } = useAuth(); + const { showAlert } = useModal(); const [searchParam] = useSearchParams(); const userData = useContext(UserDataContext); @@ -14,7 +16,7 @@ function Login() { useEffect(() => { login(searchParam.get('code')).catch(() => { - alert('로그인에 실패했습니다. 잠시 후 다시 시도해주세요.'); + showAlert('로그인에 실패했습니다. 잠시 후 다시 시도해주세요.'); navigate(ROUTES.HOME); }); }, []); diff --git a/frontend/src/components/common/HeaderNav/HeaderNav.tsx b/frontend/src/components/common/HeaderNav/HeaderNav.tsx index d10c44c4..1fca59cd 100644 --- a/frontend/src/components/common/HeaderNav/HeaderNav.tsx +++ b/frontend/src/components/common/HeaderNav/HeaderNav.tsx @@ -7,9 +7,11 @@ import * as S from '@/components/common/HeaderNav/HeaderNav.style'; import { useEffect, useState } from 'react'; import CategoryNav from '@/components/common/CategoryNav/CategoryNav'; import useAnimation from '@/hooks/useAnimation'; +import useModal from '@/hooks/useModal'; function HeaderNav() { const { logout, isLoggedIn } = useAuth(); + const { showAlert, getConfirm } = useModal(); const [categoryOpen, setCategoryOpen] = useState(false); const [shouldRenderCategory, handleTransitionEnd, triggerAnimation] = @@ -21,13 +23,14 @@ function HeaderNav() { setCategoryOpen((prevState) => !prevState); }; - const handleLogout: React.MouseEventHandler = () => { - if (window.confirm('로그아웃 하시겠습니까?')) { + const handleLogout = async () => { + const confirmation = await getConfirm('로그아웃 하시겠습니까?'); + if (confirmation) { try { logout(); - window.alert('로그아웃이 완료되었습니다.'); + showAlert('로그아웃이 완료되었습니다.'); } catch { - window.alert('로그아웃에 실패했습니다. 다시 시도해주세요.'); + showAlert('로그아웃에 실패했습니다. 다시 시도해주세요.'); } } }; diff --git a/frontend/src/components/common/Modal/Modal.style.tsx b/frontend/src/components/common/Modal/Modal.style.tsx index 487a0f02..9e44fbcc 100644 --- a/frontend/src/components/common/Modal/Modal.style.tsx +++ b/frontend/src/components/common/Modal/Modal.style.tsx @@ -1,6 +1,10 @@ import styled from 'styled-components'; -export const Container = styled.section` +export const Container = styled.section<{ scrollOffset: number }>` + position: absolute; + top: ${({ scrollOffset }) => scrollOffset}px; + left: 0; + width: 100vw; height: 100vh; @@ -17,7 +21,7 @@ export const Backdrop = styled.div` position: absolute; top: 0; left: 0; - z-index: 1; + z-index: 10; background-color: #00000033; @@ -29,11 +33,13 @@ export const Content = styled.section` min-height: 10rem; padding: 1.5rem; - z-index: 2; + position: relative; + + z-index: 15; display: flex; flex-direction: column; - justify-content: space-between; + justify-content: center; align-items: center; gap: 1.5rem; diff --git a/frontend/src/components/common/Modal/Modal.tsx b/frontend/src/components/common/Modal/Modal.tsx index c51cc899..7c0bcffa 100644 --- a/frontend/src/components/common/Modal/Modal.tsx +++ b/frontend/src/components/common/Modal/Modal.tsx @@ -1,6 +1,6 @@ import { createPortal } from 'react-dom'; import * as S from '@/components/common/Modal/Modal.style'; -import { PropsWithChildren } from 'react'; +import { PropsWithChildren, useEffect, useState } from 'react'; type Props = { handleClose: () => void; @@ -12,8 +12,19 @@ function Modal({ handleConfirm, children, }: PropsWithChildren) { + const [scrollOffset, setScrollOffset] = useState(0); + + useEffect(() => { + document.body.style.overflow = 'hidden'; + setScrollOffset(window.pageYOffset); + + return () => { + document.body.style.overflow = 'auto'; + }; + }, []); + return createPortal( - + {children} diff --git a/frontend/src/contexts/ModalContextProvider.tsx b/frontend/src/contexts/ModalContextProvider.tsx new file mode 100644 index 00000000..a1a166dd --- /dev/null +++ b/frontend/src/contexts/ModalContextProvider.tsx @@ -0,0 +1,60 @@ +import Modal from '@/components/common/Modal/Modal'; +import { createContext, PropsWithChildren, useState } from 'react'; + +export const ShowAlertContext = createContext<(message: string) => void>(null); +export const GetConfirmContext = + createContext<(message: string) => Promise>(null); + +let resolveConfirm: (value: boolean | PromiseLike) => void; +function ModalContextProvider({ children }: PropsWithChildren) { + const [message, setMessage] = useState(''); + const [alertOpen, setAlertOpen] = useState(false); + const [confirmOpen, setConfirmOpen] = useState(false); + + const showAlert = (message: string) => { + setMessage(message); + setAlertOpen(true); + }; + const showConfirm = (message: string) => { + setMessage(message); + setConfirmOpen(true); + }; + + const handleClose = () => { + setAlertOpen(false); + setConfirmOpen(false); + }; + + const handleConfirm = () => { + resolveConfirm(true); + setConfirmOpen(false); + }; + + const getConfirm: (message: string) => Promise = (message) => { + showConfirm(message); + + return new Promise((resolve) => { + resolveConfirm = resolve; + }); + }; + + return ( + + + {children} + {alertOpen && ( + + {message} + + )} + {confirmOpen && ( + + {message} + + )} + + + ); +} + +export default ModalContextProvider; diff --git a/frontend/src/hooks/api/useDelete.tsx b/frontend/src/hooks/api/useDelete.tsx index a7924882..64f3aa9d 100644 --- a/frontend/src/hooks/api/useDelete.tsx +++ b/frontend/src/hooks/api/useDelete.tsx @@ -1,6 +1,6 @@ import { AxiosRequestHeaders } from 'axios'; import useAxios from '@/hooks/api/useAxios'; -import handleError from '@/utils/handleError'; +import useError from '@/hooks/useError'; type Props = { url: string; @@ -9,6 +9,7 @@ type Props = { function useDelete({ url, headers }: Props): (id: number) => Promise { const { axiosInstance } = useAxios(); + const handleError = useError(); const deleteData = async (id: number) => { try { diff --git a/frontend/src/hooks/api/useGet.tsx b/frontend/src/hooks/api/useGet.tsx index 133e8d29..0bbbdd1f 100644 --- a/frontend/src/hooks/api/useGet.tsx +++ b/frontend/src/hooks/api/useGet.tsx @@ -1,6 +1,6 @@ import { AxiosRequestHeaders, AxiosResponse } from 'axios'; -import handleError from '@/utils/handleError'; import useAxios from '@/hooks/api/useAxios'; +import useError from '@/hooks/useError'; type Props = { url: string; @@ -12,6 +12,7 @@ function useGet({ headers, }: Props): (params: Record) => Promise { const { axiosInstance } = useAxios(); + const handleError = useError(); const fetchData = async (params: unknown) => { try { diff --git a/frontend/src/hooks/api/useGetMany.tsx b/frontend/src/hooks/api/useGetMany.tsx index 7cabcf40..fec7a68d 100644 --- a/frontend/src/hooks/api/useGetMany.tsx +++ b/frontend/src/hooks/api/useGetMany.tsx @@ -2,6 +2,7 @@ import { AxiosRequestHeaders, AxiosResponse } from 'axios'; import { useState, useEffect } from 'react'; import useAxios from '@/hooks/api/useAxios'; import logError from '@/utils/logError'; +import useModal from '@/hooks/useModal'; type SearchParams = Record; @@ -38,6 +39,7 @@ function useGetMany({ url, params, body, headers }: Props): Return { const [refetchTrigger, setRefetchTrigger] = useState(0); const { axiosInstance, isLoading, isError } = useAxios(); + const { showAlert } = useModal(); const fetchData = async () => { const { @@ -91,7 +93,7 @@ function useGetMany({ url, params, body, headers }: Props): Return { }) .catch((error: Error) => { logError(error, getErrorStateMessage()); - alert('사용자에게 표시할 오류 메시지'); + showAlert('사용자에게 표시할 오류 메시지'); }); }, [nextPageTrigger, refetchTrigger]); diff --git a/frontend/src/hooks/api/useGetOne.tsx b/frontend/src/hooks/api/useGetOne.tsx index d3593b42..aa9dabb0 100644 --- a/frontend/src/hooks/api/useGetOne.tsx +++ b/frontend/src/hooks/api/useGetOne.tsx @@ -2,6 +2,7 @@ import { AxiosRequestHeaders, AxiosResponse } from 'axios'; import { useState, useEffect } from 'react'; import useAxios from '@/hooks/api/useAxios'; import logError from '@/utils/logError'; +import useModal from '@/hooks/useModal'; type Props = { url: string; @@ -19,6 +20,8 @@ function useGetOne({ url, headers }: Props): Return { const [data, setData] = useState(null); const [refetchTrigger, setRefetchTrigger] = useState(0); const { axiosInstance, isLoading, isError } = useAxios(); + const { showAlert } = useModal(); + const fetchData = async () => { const { data }: AxiosResponse = await axiosInstance.get(url, { headers, @@ -41,7 +44,7 @@ function useGetOne({ url, headers }: Props): Return { }) .catch((error: Error) => { logError(error, getErrorStateMessage()); - alert('사용자에게 표시할 오류 메시지'); + showAlert('사용자에게 표시할 오류 메시지'); }); }, [refetchTrigger]); diff --git a/frontend/src/hooks/api/usePatch.tsx b/frontend/src/hooks/api/usePatch.tsx index 206b094c..d23b212e 100644 --- a/frontend/src/hooks/api/usePatch.tsx +++ b/frontend/src/hooks/api/usePatch.tsx @@ -1,6 +1,6 @@ import { AxiosRequestHeaders, AxiosResponse } from 'axios'; import useAxios from '@/hooks/api/useAxios'; -import handleError from '@/utils/handleError'; +import useError from '@/hooks/useError'; type Props = { url: string; @@ -12,6 +12,7 @@ function usePatch({ headers, }: Props): (data: Record) => Promise> { const { axiosInstance } = useAxios(); + const handleError = useError(); const patchData = async (data: Record) => { try { diff --git a/frontend/src/hooks/api/usePost.tsx b/frontend/src/hooks/api/usePost.tsx index c5e46a7a..70856a1b 100644 --- a/frontend/src/hooks/api/usePost.tsx +++ b/frontend/src/hooks/api/usePost.tsx @@ -1,8 +1,9 @@ import { AxiosRequestHeaders } from 'axios'; import { useContext } from 'react'; import { UserDataContext } from '@/contexts/LoginContextProvider'; -import handleError from '@/utils/handleError'; import useAxios from '@/hooks/api/useAxios'; +import useModal from '@/hooks/useModal'; +import useError from '@/hooks/useError'; type Props = { url: string; @@ -13,10 +14,12 @@ function usePost({ url, headers }: Props): (input: T) => Promise { const userData = useContext(UserDataContext); const { axiosInstance } = useAxios(); + const { showAlert } = useModal(); + const handleError = useError(); const postData = async (body: T) => { if (!userData || !userData.token) { - alert('로그인이 필요합니다.'); + showAlert('로그인이 필요합니다.'); return; } diff --git a/frontend/src/hooks/api/usePut.tsx b/frontend/src/hooks/api/usePut.tsx index 212a6e59..7d7722f6 100644 --- a/frontend/src/hooks/api/usePut.tsx +++ b/frontend/src/hooks/api/usePut.tsx @@ -2,7 +2,8 @@ import { AxiosRequestHeaders } from 'axios'; import { useContext } from 'react'; import { UserDataContext } from '@/contexts/LoginContextProvider'; import useAxios from '@/hooks/api/useAxios'; -import handleError from '@/utils/handleError'; +import useModal from '@/hooks/useModal'; +import useError from '@/hooks/useError'; type Props = { url: string; @@ -16,10 +17,12 @@ function usePut({ const userData = useContext(UserDataContext); const { axiosInstance } = useAxios(); + const { showAlert } = useModal(); + const handleError = useError(); const putData = async (body: T, id: number) => { if (!userData || !userData.token) { - alert('로그인이 필요합니다.'); + showAlert('로그인이 필요합니다.'); return; } diff --git a/frontend/src/hooks/useConfirm.tsx b/frontend/src/hooks/useConfirm.tsx new file mode 100644 index 00000000..e69de29b diff --git a/frontend/src/hooks/useError.tsx b/frontend/src/hooks/useError.tsx new file mode 100644 index 00000000..943d06a6 --- /dev/null +++ b/frontend/src/hooks/useError.tsx @@ -0,0 +1,20 @@ +import useModal from '@/hooks/useModal'; +import logError from '@/utils/logError'; + +function useError() { + const { showAlert } = useModal(); + + const handleError = (error: Error, additionalMessage?: string) => { + if (!(error instanceof Error)) { + showAlert('알 수 없는 오류 발생'); + console.log(error); + } + + logError(error, additionalMessage); + showAlert('사용자에게 표시할 오류 메시지'); + }; + + return handleError; +} + +export default useError; diff --git a/frontend/src/hooks/useModal.tsx b/frontend/src/hooks/useModal.tsx new file mode 100644 index 00000000..1f4974d3 --- /dev/null +++ b/frontend/src/hooks/useModal.tsx @@ -0,0 +1,13 @@ +import { + GetConfirmContext, + ShowAlertContext, +} from '@/contexts/ModalContextProvider'; +import { useContext } from 'react'; + +function useModal() { + const showAlert = useContext(ShowAlertContext); + const getConfirm = useContext(GetConfirmContext); + return { showAlert, getConfirm }; +} + +export default useModal; diff --git a/frontend/src/index.tsx b/frontend/src/index.tsx index 4d722202..dc1843ce 100644 --- a/frontend/src/index.tsx +++ b/frontend/src/index.tsx @@ -6,6 +6,7 @@ import { ThemeProvider } from 'styled-components'; import theme from '@/style/theme'; import GlobalStyles from '@/style/GlobalStyles'; import LoginContextProvider from '@/contexts/LoginContextProvider'; +import ModalContextProvider from '@/contexts/ModalContextProvider'; /* eslint-disable */ @@ -35,7 +36,9 @@ root.render( - + + + diff --git a/frontend/src/pages/Product/Product.tsx b/frontend/src/pages/Product/Product.tsx index 839acc0f..2c49a0ca 100644 --- a/frontend/src/pages/Product/Product.tsx +++ b/frontend/src/pages/Product/Product.tsx @@ -14,6 +14,7 @@ import theme from '@/style/theme'; import useAuth from '@/hooks/useAuth'; import AsyncWrapper from '@/components/common/AsyncWrapper/AsyncWrapper'; import Loading from '@/components/common/Loading/Loading'; +import useModal from '@/hooks/useModal'; function Product() { const { isLoggedIn } = useAuth(); @@ -42,6 +43,7 @@ function Product() { (isSheetOpen: boolean) => !isSheetOpen, false ); + const { showAlert, getConfirm } = useModal(); const reviewListRef = useRef(); @@ -58,7 +60,7 @@ function Product() { const handleReviewEdit = (reviewInput: ReviewInput, id: number) => { editReview(reviewInput, id) .then(() => { - alert('리뷰가 수정되었습니다.'); + showAlert('리뷰가 수정되었습니다.'); refetchReview(); }) .catch((error) => { @@ -66,8 +68,9 @@ function Product() { }); }; - const handleReviewDeletion = (id: number) => { - if (!confirm('리뷰를 삭제하시겠습니까?')) return; + const handleReviewDeletion = async (id: number) => { + const confirmation = await getConfirm('리뷰를 삭제하시겠습니까?'); + if (!confirmation) return; deleteReview(id) .then(() => { diff --git a/frontend/src/pages/Register/Register.tsx b/frontend/src/pages/Register/Register.tsx index 799be33c..5fc054a3 100644 --- a/frontend/src/pages/Register/Register.tsx +++ b/frontend/src/pages/Register/Register.tsx @@ -6,6 +6,7 @@ import { UserDataContext } from '@/contexts/LoginContextProvider'; import { ENDPOINTS } from '@/constants/api'; import { useNavigate } from 'react-router-dom'; import ROUTES from '@/constants/routes'; +import useModal from '@/hooks/useModal'; const messages = { 1: '경력을 선택해주세요', @@ -38,6 +39,7 @@ function Register() { career: null, jobType: null, }); + const { showAlert, getConfirm } = useModal(); const userData = useContext(UserDataContext); @@ -76,12 +78,11 @@ function Register() { } }; - const handleAdditionalInfoSubmit = (input: UserInfo) => { - if ( - confirm( - `${careers[input.career]}, ${jobTypes[input.jobType]} 개발자이신가요?` - ) - ) { + const handleAdditionalInfoSubmit = async (input: UserInfo) => { + const confirmation = await getConfirm( + `${careers[input.career]}, ${jobTypes[input.jobType]} 개발자이신가요?` + ); + if (confirmation) { patchAdditionalInfo(input) .then(() => { navigate(ROUTES.HOME); @@ -92,13 +93,13 @@ function Register() { } }; - const handleConfirmButtonClick = () => { + const handleConfirmButtonClick = async () => { if (step === 1 && additionalInfo.career === null) { - alert('경력을 선택해주세요.'); + showAlert('경력을 선택해주세요.'); return; } if (step === 2 && additionalInfo.jobType === null) { - alert('직군을 선택해주세요.'); + showAlert('직군을 선택해주세요.'); return; } if (step === 1 || step === 2) { @@ -106,7 +107,7 @@ function Register() { return; } - handleAdditionalInfoSubmit(additionalInfo); + await handleAdditionalInfoSubmit(additionalInfo); }; const handleEditButtonClick = () => { diff --git a/frontend/src/utils/handleError.ts b/frontend/src/utils/handleError.ts deleted file mode 100644 index 1866a9a4..00000000 --- a/frontend/src/utils/handleError.ts +++ /dev/null @@ -1,13 +0,0 @@ -import logError from '@/utils/logError'; - -const handleError = (error: Error, additionalMessage?: string) => { - if (!(error instanceof Error)) { - alert('알 수 없는 오류 발생'); - console.log(error); - } - - logError(error, additionalMessage); - alert('사용자에게 표시할 오류 메시지'); -}; - -export default handleError;