diff --git a/frontend/package.json b/frontend/package.json index 0efe66939..142c3e409 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -28,7 +28,7 @@ "@tanstack/react-query-devtools": "^4.36.1", "axios": "^1.5.1", "browser-image-compression": "^2.0.2", - "celuveat-ui-library": "^1.2.8", + "celuveat-ui-library": "^1.4.9", "react": "^18.2.0", "react-dom": "^18.2.0", "react-router-dom": "^6.14.2", @@ -58,6 +58,7 @@ "@testing-library/react": "^14.0.0", "@types/google.maps": "^3.53.5", "@types/jest": "^29.5.3", + "@types/node": "^20.11.0", "@types/react": "^18.2.14", "@types/react-dom": "^18.2.6", "@types/react-slick": "^0.23.11", diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 8e60fbd08..17d094721 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,7 +1,29 @@ +import { Suspense } from 'react'; +import { styled } from 'styled-components'; +import LoadingIndicator from '~/components/@common/LoadingIndicator'; + import Router from './router/Router'; function App() { - return ; + return ( + + + + } + > + + + ); } export default App; + +const StyledProcessing = styled.div` + display: flex; + justify-content: center; + align-items: center; + + height: 100vh; +`; diff --git a/frontend/src/components/@common/Dialog/Dialog.tsx b/frontend/src/components/@common/Dialog/Dialog.tsx deleted file mode 100644 index fabf6278d..000000000 --- a/frontend/src/components/@common/Dialog/Dialog.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import { Modal } from 'celuveat-ui-library'; -import React, { ReactNode } from 'react'; -import styled from 'styled-components'; -import Exit from '~/assets/icons/exit.svg'; -import useCeluveatModal from '~/hooks/useCeluveatModal'; - -interface Props { - title?: string; - children: ReactNode; -} - -function Dialog({ title, children }: Props) { - const { closeModal } = useCeluveatModal(); - - return ( - <> - } /> - - {title} - - {children} - - - ); -} - -export default Dialog; - -const StyledOverlay = styled.div` - position: absolute; - top: 0; - - width: 100%; - height: 100%; - - background-color: black; - - opacity: 0.2; -`; - -const StyledContent = styled.div` - position: fixed; - top: 50%; - left: 50vw; - - min-width: 540px; - max-width: 66.6%; - - padding: 2.4rem; - margin: 0 auto; - - border-radius: 12px; - background-color: white; - - transform: translateX(-50%) translateY(-50%); -`; - -const StyledTitle = styled.h4` - text-align: center; - - margin-bottom: 2.4rem; -`; - -const StyledExitButton = styled(Exit)` - position: absolute; - top: 12px; - right: 12px; - - cursor: pointer; -`; diff --git a/frontend/src/components/@common/Dialog/index.tsx b/frontend/src/components/@common/Dialog/index.tsx deleted file mode 100644 index d0a54d2ee..000000000 --- a/frontend/src/components/@common/Dialog/index.tsx +++ /dev/null @@ -1,3 +0,0 @@ -import Dialog from './Dialog'; - -export default Dialog; diff --git a/frontend/src/components/@common/ErrorBoundary/ErrorBoundary.tsx b/frontend/src/components/@common/ErrorBoundary/ErrorBoundary.tsx index b4f54fa1e..43c245a6f 100644 --- a/frontend/src/components/@common/ErrorBoundary/ErrorBoundary.tsx +++ b/frontend/src/components/@common/ErrorBoundary/ErrorBoundary.tsx @@ -1,46 +1,46 @@ -import { Component, ReactElement, ReactNode } from 'react'; - -interface FallbackRenderProps { - resetErrorBoundary: () => void; -} -interface Props { - children: ReactNode; - fallbackRender: ({ resetErrorBoundary }: FallbackRenderProps) => ReactElement; - reset: () => void; -} - -interface State { - hasError: boolean; -} - -class ErrorBoundary extends Component { - constructor(props: Props) { - super(props); - this.state = { hasError: false }; - } - - static getDerivedStateFromError(): State { - return { - hasError: true, - }; - } - - resetErrorBoundary() { - const { reset } = this.props; - reset(); - this.setState({ hasError: false }); - } - - render() { - const { hasError } = this.state; - const { children, fallbackRender } = this.props; - - if (hasError) { - return fallbackRender({ resetErrorBoundary: () => this.resetErrorBoundary() }); - } - - return children; - } -} - -export default ErrorBoundary; +// import { Component, ReactElement, ReactNode } from 'react'; + +// interface FallbackRenderProps { +// resetErrorBoundary: () => void; +// } +// interface Props { +// children: ReactNode; +// fallbackRender: ({ resetErrorBoundary }: FallbackRenderProps) => ReactElement; +// reset: () => void; +// } + +// interface State { +// hasError: boolean; +// } + +// class ErrorBoundary extends Component { +// constructor(props: Props) { +// super(props); +// this.state = { hasError: false }; +// } + +// static getDerivedStateFromError(): State { +// return { +// hasError: true, +// }; +// } + +// resetErrorBoundary() { +// const { reset } = this.props; +// reset(); +// this.setState({ hasError: false }); +// } + +// render() { +// const { hasError } = this.state; +// const { children, fallbackRender } = this.props; + +// if (hasError) { +// return fallbackRender({ resetErrorBoundary: () => this.resetErrorBoundary() }); +// } + +// return children; +// } +// } + +// export default ErrorBoundary; diff --git a/frontend/src/components/CelebDropDown/CelebDropDown.tsx b/frontend/src/components/CelebDropDown/CelebDropDown.tsx index 4ffe3f55d..194e7b79d 100644 --- a/frontend/src/components/CelebDropDown/CelebDropDown.tsx +++ b/frontend/src/components/CelebDropDown/CelebDropDown.tsx @@ -34,7 +34,7 @@ function CelebDropDown() { {[OPTION_FOR_CELEB_ALL, ...celebOptions].map(celeb => ( - + diff --git a/frontend/src/components/Form/SuggestionForm/SuggestionForm.tsx b/frontend/src/components/Form/SuggestionForm/SuggestionForm.tsx index 61c20d25c..ba2d4f622 100644 --- a/frontend/src/components/Form/SuggestionForm/SuggestionForm.tsx +++ b/frontend/src/components/Form/SuggestionForm/SuggestionForm.tsx @@ -3,7 +3,7 @@ import { useParams } from 'react-router-dom'; import styled from 'styled-components'; import { postRevisedInfo } from '~/api/restaurant'; import TextButton from '~/components/@common/Button'; -import Dialog from '~/components/@common/Dialog'; + import { BORDER_RADIUS, FONT_SIZE } from '~/styles/common'; const labels = [ @@ -37,28 +37,26 @@ function SuggestionForm() { }; return ( - - -
수정 항목
-

잘못되었거나 수정이 필요한 정보들을 모두 선택해주세요.

- - {labels.map(label => ( - - - {label} - - ))} - - - -
-
+ +
수정 항목
+

잘못되었거나 수정이 필요한 정보들을 모두 선택해주세요.

+ + {labels.map(label => ( + + + {label} + + ))} + + + +
); } diff --git a/frontend/src/components/FormModal/FormModal.tsx b/frontend/src/components/FormModal/FormModal.tsx deleted file mode 100644 index ba65c308d..000000000 --- a/frontend/src/components/FormModal/FormModal.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import Dialog from '../@common/Dialog'; -import RestaurantReviewList from '../RestaurantReviewList/RestaurantReviewList'; -import { ReviewDeleteForm, ReviewForm, ReviewReportForm } from '../ReviewForm'; - -const REVIEW_FORM_TITLE = { - create: '리뷰 작성하기', - update: '리뷰 수정하기', - delete: '리뷰 삭제하기', - report: '리뷰 신고하기', - all: '리뷰 모두 보기', -} as const; - -interface FormModalProps { - type: keyof typeof REVIEW_FORM_TITLE; - reviewId?: number; -} - -function FormModal({ type, reviewId }: FormModalProps) { - return ( -
- {type === 'create' && ( - - - - )} - {type === 'update' && ( - - - - )} - {type === 'delete' && ( - - - - )} - {type === 'report' && ( - - - - )} - {type === 'all' && ( - - - - )} -
- ); -} - -export default FormModal; diff --git a/frontend/src/components/FormModal/index.tsx b/frontend/src/components/FormModal/index.tsx deleted file mode 100644 index 9028427d0..000000000 --- a/frontend/src/components/FormModal/index.tsx +++ /dev/null @@ -1,3 +0,0 @@ -import FormModal from '~/components/FormModal/FormModal'; - -export default FormModal; diff --git a/frontend/src/components/InfoDropDown/InfoDropDown.tsx b/frontend/src/components/InfoDropDown/InfoDropDown.tsx index e33bd933a..93e73a101 100644 --- a/frontend/src/components/InfoDropDown/InfoDropDown.tsx +++ b/frontend/src/components/InfoDropDown/InfoDropDown.tsx @@ -1,15 +1,16 @@ +import { styled } from 'styled-components'; import { useQuery } from '@tanstack/react-query'; -import { DropDown } from 'celuveat-ui-library'; import { useNavigate } from 'react-router-dom'; -import { styled } from 'styled-components'; -import { getProfile } from '~/api/user'; +import { DropDown, Modal } from 'celuveat-ui-library'; + import InfoButton from '~/components/@common/InfoButton'; +import LoginModal from '~/components/LoginModal'; -import { ProfileData } from '~/@types/api.types'; -import useCeluveatModal from '~/hooks/useCeluveatModal'; +import { getProfile } from '~/api/user'; + +import type { ProfileData } from '~/@types/api.types'; function InfoDropDown() { - const { openLoginModal } = useCeluveatModal(); const { data, isSuccess: isLogin } = useQuery({ queryKey: ['profile'], queryFn: getProfile, @@ -40,20 +41,22 @@ function InfoDropDown() { {isLogin ? ( <> - + 마이 페이지 - + 위시리스트 - + 회원 탈퇴 ) : ( - - 로그인 - + }> + + 로그인 + + )} @@ -99,6 +102,8 @@ const StyledDropDownOption = styled.li` padding: 0 1rem; + border-radius: 10px; + &:hover { background: var(--gray-1); } diff --git a/frontend/src/components/LoginModal/LoginModal.tsx b/frontend/src/components/LoginModal/LoginModal.tsx index 6b7e93319..637eef921 100644 --- a/frontend/src/components/LoginModal/LoginModal.tsx +++ b/frontend/src/components/LoginModal/LoginModal.tsx @@ -1,15 +1,12 @@ import { styled } from 'styled-components'; import LoginButton from '~/components/@common/LoginButton'; -import Dialog from '../@common/Dialog'; function LoginModal() { return ( - - - - - - + + + + ); } diff --git a/frontend/src/components/RestaurantReviewItem/RestaurantReviewItem.tsx b/frontend/src/components/RestaurantReviewItem/RestaurantReviewItem.tsx index fdaf47cce..e06c49020 100644 --- a/frontend/src/components/RestaurantReviewItem/RestaurantReviewItem.tsx +++ b/frontend/src/components/RestaurantReviewItem/RestaurantReviewItem.tsx @@ -2,6 +2,7 @@ import { styled, css } from 'styled-components'; import { useQuery } from '@tanstack/react-query'; +import { Modal } from 'celuveat-ui-library'; import ThumpUpIcon from '~/assets/icons/etc/thumb-up.svg'; import SpeakerphoneIcon from '~/assets/icons/etc/speakerphone.svg'; @@ -15,7 +16,7 @@ import { getProfile } from '~/api/user'; import { getReviewImgUrl, lookImage } from '~/utils/image'; import type { ProfileData, RestaurantReview } from '~/@types/api.types'; -import useCeluveatModal from '~/hooks/useCeluveatModal'; +import { ReviewDeleteForm, ReviewForm, ReviewReportForm } from '~/components/ReviewForm'; interface RestaurantReviewItemProps { review: RestaurantReview; @@ -29,8 +30,6 @@ function RestaurantReviewItem({ review }: RestaurantReviewItemProps) { const { getReviewIsLiked, toggleRestaurantReviewLike } = useRestaurantReview(); - const { openReviewFormModal } = useCeluveatModal(); - const isUsersReview = profileData?.memberId === review.memberId; return ( @@ -46,23 +45,21 @@ function RestaurantReviewItem({ review }: RestaurantReviewItemProps) { {isUsersReview && ( - + + - + + )} @@ -92,14 +89,16 @@ function RestaurantReviewItem({ review }: RestaurantReviewItemProps) { {review.likeCount} - { - openReviewFormModal({ type: 'report', reviewId: review.id }); - }} + } > - - 신고 - + + + 신고 + + )} diff --git a/frontend/src/components/RestaurantReviewWrapper/RestaurantReviewWrapper.tsx b/frontend/src/components/RestaurantReviewWrapper/RestaurantReviewWrapper.tsx index 51c86bd9c..9d7e6994b 100644 --- a/frontend/src/components/RestaurantReviewWrapper/RestaurantReviewWrapper.tsx +++ b/frontend/src/components/RestaurantReviewWrapper/RestaurantReviewWrapper.tsx @@ -1,5 +1,6 @@ import { styled } from 'styled-components'; +import { Modal } from 'celuveat-ui-library'; import Pencil from '~/assets/icons/pencil.svg'; import RestaurantReviewList from '~/components/RestaurantReviewList'; @@ -8,34 +9,30 @@ import useRestaurantReview from '~/hooks/server/useRestaurantReview'; import { FONT_SIZE } from '~/styles/common'; import { REVIEW_SHOW_COUNT } from '~/constants/options'; -import { ReviewFormType } from '~/@types/review.types'; -import useCeluveatModal from '~/hooks/useCeluveatModal'; + +import { ReviewForm } from '~/components/ReviewForm'; function RestaurantReviewWrapper() { const { restaurantReviewsData } = useRestaurantReview(); - const { openReviewFormModal } = useCeluveatModal(); const { totalElementsCount: reviewCount } = restaurantReviewsData; const isMoreReviews = reviewCount > REVIEW_SHOW_COUNT; - const onClickOpenModal = (reviewFormType: ReviewFormType) => () => { - openReviewFormModal({ type: reviewFormType }); - }; - return ( 리뷰 {reviewCount ? `${reviewCount}개` : '없음'} - - - 리뷰 작성하기 - + }> + + + 리뷰 작성하기 + + {isMoreReviews && ( - {`리뷰 ${reviewCount}개 모두 보기`} + }> + {`리뷰 ${reviewCount}개 모두 보기`} + )} diff --git a/frontend/src/components/SuggestionButton/SuggestionButton.tsx b/frontend/src/components/SuggestionButton/SuggestionButton.tsx index 013095e67..d0846d1d8 100644 --- a/frontend/src/components/SuggestionButton/SuggestionButton.tsx +++ b/frontend/src/components/SuggestionButton/SuggestionButton.tsx @@ -1,15 +1,15 @@ +import { Modal } from 'celuveat-ui-library'; import { styled } from 'styled-components'; import Pencil from '~/assets/icons/pencil.svg'; -import useCeluveatModal from '~/hooks/useCeluveatModal'; function SuggestionButton() { - const { openSuggestionModal } = useCeluveatModal(); - return ( - - -
정보 수정 제안하기
-
+ }> + + +
정보 수정 제안하기
+
+
); } diff --git a/frontend/src/hooks/server/useRestaurantReview.ts b/frontend/src/hooks/server/useRestaurantReview.ts index 6cd982f1a..c81ed4c1b 100644 --- a/frontend/src/hooks/server/useRestaurantReview.ts +++ b/frontend/src/hooks/server/useRestaurantReview.ts @@ -4,6 +4,8 @@ import { useParams } from 'react-router-dom'; import { shallow } from 'zustand/shallow'; import { useState } from 'react'; + +import { useModalStore } from 'celuveat-ui-library'; import { deleteRestaurantReview, getRestaurantReview, @@ -16,7 +18,6 @@ import { import useToastState from '~/hooks/store/useToastState'; import type { RestaurantReviewData, RestaurantReviewPatchBody } from '~/@types/api.types'; -import useCeluveatModal from '../useCeluveatModal'; const useRestaurantReview = () => { const queryClient = useQueryClient(); @@ -35,7 +36,7 @@ const useRestaurantReview = () => { shallow, ); - const { closeModal } = useCeluveatModal(); + const { closeModal } = useModalStore(); const errorHandler = (error: AxiosError) => { switch (error.response.status) { diff --git a/frontend/src/hooks/server/useToggleLikeNotUpdate.ts b/frontend/src/hooks/server/useToggleLikeNotUpdate.tsx similarity index 85% rename from frontend/src/hooks/server/useToggleLikeNotUpdate.ts rename to frontend/src/hooks/server/useToggleLikeNotUpdate.tsx index 359dafb83..f0c749251 100644 --- a/frontend/src/hooks/server/useToggleLikeNotUpdate.ts +++ b/frontend/src/hooks/server/useToggleLikeNotUpdate.tsx @@ -1,14 +1,19 @@ +import { AxiosError } from 'axios'; import { shallow } from 'zustand/shallow'; +import { useModalStore } from 'celuveat-ui-library'; import { Query, useMutation, useQueryClient } from '@tanstack/react-query'; -import { AxiosError } from 'axios'; -import { Restaurant } from '../../@types/restaurant.types'; -import useToastState from '~/hooks/store/useToastState'; -import useBooleanState from '~/hooks/useBooleanState'; + import { postRestaurantLike } from '~/api/user'; -import useCeluveatModal from '../useCeluveatModal'; + +import useBooleanState from '~/hooks/useBooleanState'; +import useToastState from '~/hooks/store/useToastState'; + +import type { Restaurant } from '~/@types/restaurant.types'; + +import LoginModal from '~/components/LoginModal'; const useToggleLikeNotUpdate = (restaurant: Restaurant) => { - const { openLoginModal } = useCeluveatModal(); + const { openModal } = useModalStore(); const { value: isLiked, toggle: toggleIsLiked } = useBooleanState(restaurant.isLiked ?? true); const { onSuccess, onFailure, close } = useToastState( @@ -28,7 +33,7 @@ const useToggleLikeNotUpdate = (restaurant: Restaurant) => { onError: (error: AxiosError) => { if (error.response.status < 500) { - openLoginModal(); + openModal({ title: '로그인 하기', content: }); toggleIsLiked(); } else { onFailure(error.response.data as string); @@ -46,7 +51,7 @@ const useToggleLikeNotUpdate = (restaurant: Restaurant) => { query.queryKey[0] === 'restaurants' && query.queryKey[1]?.type !== 'wish-list', }); }, - }); + }); const toggleRestaurantLike = () => { toggleLike.mutate(restaurant.id); diff --git a/frontend/src/hooks/useCeluveatModal.tsx b/frontend/src/hooks/useCeluveatModal.tsx deleted file mode 100644 index 53dcec96b..000000000 --- a/frontend/src/hooks/useCeluveatModal.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { useModal } from 'celuveat-ui-library'; -import SuggestionForm from '~/components/Form/SuggestionForm'; -import FormModal from '~/components/FormModal/FormModal'; -import LoginModal from '~/components/LoginModal'; - -const REVIEW_FORM_TITLE = { - create: '리뷰 작성하기', - update: '리뷰 수정하기', - delete: '리뷰 삭제하기', - report: '리뷰 신고하기', - all: '리뷰 모두 보기', -} as const; - -const useCeluveatModal = () => { - const { openModal, closeModal } = useModal(); - - const openLoginModal = () => { - openModal({ content: }); - }; - - const openReviewFormModal = ({ type, reviewId }: { type: keyof typeof REVIEW_FORM_TITLE; reviewId?: number }) => { - openModal({ content: }); - }; - - const openSuggestionModal = () => { - openModal({ content: }); - }; - - return { openLoginModal, openReviewFormModal, openSuggestionModal, closeModal }; -}; - -export default useCeluveatModal; diff --git a/frontend/src/mocks/handler/mapPages/handler.ts b/frontend/src/mocks/handler/mapPages/handler.ts index a9a40f11b..abeeeb740 100644 --- a/frontend/src/mocks/handler/mapPages/handler.ts +++ b/frontend/src/mocks/handler/mapPages/handler.ts @@ -130,8 +130,8 @@ export const MainPageSuccessHandler = [ const restaurant = restaurants.find(restaurant => restaurant.id === Number(restaurantId)); restaurant.isLiked ? (restaurant['isLiked'] = false) : (restaurant['isLiked'] = true); - const responses = [res(ctx.status(429)), res(ctx.status(429)), res(ctx.status(200))]; - return responses[getRandomNumber()]; + // const responses = [res(ctx.status(429)), res(ctx.status(429)), res(ctx.status(200))]; + // return responses[getRandomNumber()]; if (JSESSION === undefined) { return res(ctx.status(401), ctx.json({ message: '만료된 세션입니다.' })); diff --git a/frontend/src/router/Root.tsx b/frontend/src/router/Root.tsx index 325c1093d..ecbd590fb 100644 --- a/frontend/src/router/Root.tsx +++ b/frontend/src/router/Root.tsx @@ -1,16 +1,13 @@ -/* stylelint-disable declaration-property-unit-allowed-list */ -import { Suspense, lazy } from 'react'; -import { styled } from 'styled-components'; import { Outlet, ScrollRestoration } from 'react-router-dom'; +import { Modal } from 'celuveat-ui-library'; import Footer from '~/components/@common/Footer'; import { Header, MobileHeader } from '~/components/@common/Header'; -import LoadingIndicator from '~/components/@common/LoadingIndicator'; + import useMediaQuery from '~/hooks/useMediaQuery'; import BottomNavBar from '~/components/BottomNavBar'; import useScrollDirection from '~/hooks/useScrollDirection'; import useBooleanState from '~/hooks/useBooleanState'; - -const Toast = lazy(() => import('~/components/@common/Toast')); +import Toast from '~/components/@common/Toast'; function Root() { const scrollDirection = useScrollDirection(); @@ -20,32 +17,19 @@ function Root() { return ( <> - - - - } - > + <> {isMobile ? :
} {isMobile && } {!isMobile &&