From 40bbf6a4f9962bab99647d5dbcb5aca205e8ab42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=ED=99=A9=EC=A4=80=EC=8A=B9?= <78203399+turtle601@users.noreply.github.com> Date: Sun, 17 Mar 2024 17:27:37 +0900 Subject: [PATCH] Develop frontend (#680) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: webpack 캐시를 활용한 빌드 속도 개선 (#672) * chore: dev환경 webpack 설정 구현 * chore: common환경 webpack 설정 구현 * chore: prod환경 webpack 설정 구현 * chore: 기존 웹팩 환경 파일 제거 * chore: 불필요한 babelrc 파일 제거 * chore: 빌드 명령어 변경 및 사용하지 않는 라이브러리 제거 * chore: 타입체킹에 실패하면 빌드가 되지 않도록 설정 * fix: 모달 열기 이벤트 동작 안하는 에러 (#675) * chore: dev환경 webpack 설정 구현 * chore: common환경 webpack 설정 구현 * chore: prod환경 webpack 설정 구현 * chore: 기존 웹팩 환경 파일 제거 * chore: 불필요한 babelrc 파일 제거 * chore: 빌드 명령어 변경 및 사용하지 않는 라이브러리 제거 * chore: 타입체킹에 실패하면 빌드가 되지 않도록 설정 * fix: 페이지 code 스플리팅으로 인한 suspense 오류 해결 * fix: axios 401 에러로 인한 주석 처리 * fix: 간헐적 에러 발생 오류 해결 #674 * refactor: 변경된 모달 컴포넌트 적용 * fix: 모달 content 내용 넘어가는 버그 해결 * fix: modal 버전 업그레이드 및 필요없는 파일 제거 --- frontend/.babelrc | 36 - frontend/.webpack/webpack.common.js | 53 +- frontend/.webpack/webpack.config.js | 74 - frontend/.webpack/webpack.dev.js | 42 + frontend/.webpack/webpack.prod.js | 65 + frontend/package.json | 17 +- frontend/src/App.tsx | 24 +- .../src/components/@common/Dialog/Dialog.tsx | 70 - .../src/components/@common/Dialog/index.tsx | 3 - .../@common/ErrorBoundary/ErrorBoundary.tsx | 92 +- .../CelebDropDown/CelebDropDown.tsx | 2 +- .../Form/SuggestionForm/SuggestionForm.tsx | 44 +- .../src/components/FormModal/FormModal.tsx | 50 - frontend/src/components/FormModal/index.tsx | 3 - .../components/InfoDropDown/InfoDropDown.tsx | 29 +- .../src/components/LoginModal/LoginModal.tsx | 11 +- .../RestaurantReviewItem.tsx | 47 +- .../RestaurantReviewWrapper.tsx | 27 +- .../SuggestionButton/SuggestionButton.tsx | 14 +- .../src/hooks/server/useRestaurantReview.ts | 5 +- ...otUpdate.ts => useToggleLikeNotUpdate.tsx} | 21 +- frontend/src/hooks/useCeluveatModal.tsx | 32 - .../src/mocks/handler/mapPages/handler.ts | 4 +- frontend/src/router/Root.tsx | 28 +- frontend/tsconfig.json | 1 + frontend/yarn.lock | 1390 ++++++++--------- 26 files changed, 1021 insertions(+), 1163 deletions(-) delete mode 100644 frontend/.babelrc delete mode 100644 frontend/.webpack/webpack.config.js create mode 100644 frontend/.webpack/webpack.dev.js create mode 100644 frontend/.webpack/webpack.prod.js delete mode 100644 frontend/src/components/@common/Dialog/Dialog.tsx delete mode 100644 frontend/src/components/@common/Dialog/index.tsx delete mode 100644 frontend/src/components/FormModal/FormModal.tsx delete mode 100644 frontend/src/components/FormModal/index.tsx rename frontend/src/hooks/server/{useToggleLikeNotUpdate.ts => useToggleLikeNotUpdate.tsx} (85%) delete mode 100644 frontend/src/hooks/useCeluveatModal.tsx diff --git a/frontend/.babelrc b/frontend/.babelrc deleted file mode 100644 index 6e5b1e902..000000000 --- a/frontend/.babelrc +++ /dev/null @@ -1,36 +0,0 @@ -{ - "presets": [ - [ - "@babel/preset-env", - { - "targets": { "browsers": ["last 2 versions", ">= 5% in KR"] }, - "useBuiltIns": "usage", // 폴리필 사용 방식 지정 - "corejs": { - "version": 3 // 폴리필 버전 지정 - } - } - ], - [ - "@babel/react", - { - "runtime": "automatic" - } - ], - "@babel/preset-typescript" - ], - "plugins": [ - [ - "babel-plugin-root-import", - { - "rootPathPrefix": "~", - "rootPathSuffix": "src" - }, - "react-refresh/babel" - ] - ], - "env": { - "development": { - "plugins": ["babel-plugin-styled-components"] - } - } -} diff --git a/frontend/.webpack/webpack.common.js b/frontend/.webpack/webpack.common.js index 61a9ad48d..fc130e966 100644 --- a/frontend/.webpack/webpack.common.js +++ b/frontend/.webpack/webpack.common.js @@ -1,18 +1,57 @@ const path = require('path'); -const CssMinimizerPlugin = require('css-minimizer-webpack-plugin'); +const HtmlWebpackPlugin = require('html-webpack-plugin'); +const MiniCssExtractPlugin = require('mini-css-extract-plugin'); module.exports = { - entry: ['./src/index.tsx'], + entry: './src/index.tsx', + output: { - path: path.resolve(__dirname, '../dist/'), publicPath: '/', - filename: '[name].[chunkhash:8].js', + path: path.join(__dirname, '../dist'), + filename: '[name].[chunkhash].js', clean: true, }, + resolve: { - extensions: ['.tsx', '.ts', '.jsx', '.js', '.json'], + extensions: ['.js', '.jsx', '.ts', '.tsx'], + }, + + module: { + rules: [ + { + test: /\.css$/i, + use: [MiniCssExtractPlugin.loader, 'css-loader'], + sideEffects: true, + }, + { + test: /\.(jpg|jpeg|gif|png|ico)?$/, + type: 'asset', + generator: { + filename: 'images/[name][ext]', + }, + }, + { + test: /\.(woff|woff2|eot|ttf|otf)?$/, + type: 'asset', + generator: { + filename: 'fonts/[name][ext]', + }, + }, + { + test: /\.svg$/, + use: ['@svgr/webpack'], + }, + ], }, + plugins: [ + new HtmlWebpackPlugin({ + template: './public/index.html', + filename: 'index.html', + }), + new MiniCssExtractPlugin(), + ], + devServer: { static: { directory: path.resolve(__dirname, '../public'), @@ -23,10 +62,6 @@ module.exports = { allowedHosts: 'all', }, - optimization: { - minimizer: ['...', new CssMinimizerPlugin()], - }, - performance: { hints: false, maxEntrypointSize: 512000, diff --git a/frontend/.webpack/webpack.config.js b/frontend/.webpack/webpack.config.js deleted file mode 100644 index 4e7d4a751..000000000 --- a/frontend/.webpack/webpack.config.js +++ /dev/null @@ -1,74 +0,0 @@ -const path = require('path'); -const Dotenv = require('dotenv-webpack'); -const commonConfig = require('./webpack.common.js'); -const HtmlWebpackPlugin = require('html-webpack-plugin'); -const MiniCssExtractPlugin = require('mini-css-extract-plugin'); -const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin'); -const RefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin'); -const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; - -const commonPlugins = [ - new HtmlWebpackPlugin({ - template: './public/index.html', - filename: 'index.html', - }), - new ForkTsCheckerWebpackPlugin(), - new MiniCssExtractPlugin({ - filename: 'fonts/font.css', - }), - // new BundleAnalyzerPlugin({ - // analyzerMode: 'static', - // }), -]; - -const commonRules = [ - { - test: /\.(ts|tsx)$/, - use: ['babel-loader'], - exclude: /node_modules/, - }, - { - test: /\.(jpg|jpeg|gif|png|ico)?$/, - type: 'asset', - generator: { - filename: 'images/[name][ext]', - }, - }, - { - test: /\.(woff|woff2|eot|ttf|otf)?$/, - type: 'asset', - generator: { - filename: 'fonts/[name][ext]', - }, - }, - { - test: /\.svg$/, - use: ['@svgr/webpack'], - }, -]; - -module.exports = (env, args) => { - const { TARGET_ENV } = env; - - return { - mode: args.mode, - ...commonConfig, - module: { - rules: [ - ...commonRules, - { - test: /\.css$/i, - use: [MiniCssExtractPlugin.loader, 'css-loader'], - sideEffects: true, - }, - ], - }, - plugins: [ - ...commonPlugins, - new Dotenv({ - path: path.resolve(__dirname, `../.${TARGET_ENV}.env`), - }), - new RefreshWebpackPlugin({}), - ], - }; -}; diff --git a/frontend/.webpack/webpack.dev.js b/frontend/.webpack/webpack.dev.js new file mode 100644 index 000000000..5eb5157dd --- /dev/null +++ b/frontend/.webpack/webpack.dev.js @@ -0,0 +1,42 @@ +const path = require('path'); +const Dotenv = require('dotenv-webpack'); +const { merge } = require('webpack-merge'); + +const common = require('./webpack.common'); + +module.exports = merge(common, { + mode: 'development', + devtool: 'eval-cheap-module-source-map', + cache: { + type: 'filesystem', + }, + module: { + rules: [ + { + test: /\.(js|jsx|ts|tsx)$/i, + exclude: /node_modules/, + loader: 'babel-loader', + options: { + cacheCompression: false, + cacheDirectory: true, + presets: ['@babel/preset-env', ['@babel/preset-react', { runtime: 'automatic' }], '@babel/preset-typescript'], + plugins: [ + ['babel-plugin-styled-components'], + [ + 'babel-plugin-root-import', + { + rootPathPrefix: '~', + rootPathSuffix: 'src', + }, + ], + ], + }, + }, + ], + }, + plugins: [ + new Dotenv({ + path: path.resolve(__dirname, `../.msw.env`), + }), + ], +}); diff --git a/frontend/.webpack/webpack.prod.js b/frontend/.webpack/webpack.prod.js new file mode 100644 index 000000000..079913415 --- /dev/null +++ b/frontend/.webpack/webpack.prod.js @@ -0,0 +1,65 @@ +const path = require('path'); +const Dotenv = require('dotenv-webpack'); +const { merge } = require('webpack-merge'); +const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin'); + +const common = require('./webpack.common'); + +module.exports = merge(common, { + mode: 'production', + devtool: false, + module: { + rules: [ + { + test: /\.(js|jsx|ts|tsx)$/i, + exclude: /node_modules/, + loader: 'babel-loader', + options: { + presets: [ + [ + '@babel/preset-env', + { + targets: { browsers: ['last 2 versions', '>= 5% in KR'] }, + useBuiltIns: 'usage', + corejs: { + version: 3, + }, + }, + ], + ['@babel/preset-react', { runtime: 'automatic' }], + '@babel/preset-typescript', + ], + plugins: [ + [ + 'babel-plugin-styled-components', + { + displayName: false, + minify: true, + transpileTemplateLiterals: true, + pure: true, + }, + ], + [ + 'babel-plugin-root-import', + { + rootPathPrefix: '~', + rootPathSuffix: 'src', + }, + ], + ], + }, + }, + ], + }, + optimization: { + splitChunks: { + chunks: 'all', + }, + }, + plugins: [ + new ForkTsCheckerWebpackPlugin(), + new Dotenv({ + path: path.resolve(__dirname, `../.prod.env`), + }), + ], +}); diff --git a/frontend/package.json b/frontend/package.json index 29754ab2c..142c3e409 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -5,12 +5,10 @@ "license": "MIT", "sideEffects": false, "scripts": { - "start:msw": "webpack serve --port 3000 --env TARGET_ENV=msw --mode development", - "start:dev": "webpack serve --port 3000 --env TARGET_ENV=dev --mode production", - "start:prod": "webpack serve --port 3000 --env TARGET_ENV=prod --mode production", - "build:msw": "webpack --env TARGET_ENV=msw --mode development", - "build:dev": "webpack --env TARGET_ENV=dev --mode production", - "build:prod": "webpack --env TARGET_ENV=prod --mode production", + "start:dev": "webpack serve --port 3000 --config .webpack/webpack.dev.js", + "start:prod": "webpack serve --port 3000 --config .webpack/webpack.prod.js", + "build:dev": "webpack --config .webpack/webpack.dev.js", + "build:prod": "webpack --config .webpack/webpack.prod.js", "lint": "eslint \"src/**/*.{js,jsx,ts,tsx}\"", "lint:css": "stylelint './src/**/*.{tsx,ts,jsx,js}'", "lint:css:fix": "stylelint './src/**/*.{tsx,ts,jsx,js}' --fix", @@ -30,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", @@ -45,7 +43,6 @@ "@babel/preset-env": "^7.23.2", "@babel/preset-react": "^7.22.15", "@babel/preset-typescript": "^7.23.2", - "@pmmmwh/react-refresh-webpack-plugin": "^0.5.11", "@storybook/addon-essentials": "^7.4.6", "@storybook/addon-interactions": "^7.4.6", "@storybook/addon-links": "^7.4.6", @@ -61,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", @@ -77,7 +75,6 @@ "css-loader": "^6.8.1", "css-minimizer-webpack-plugin": "^5.0.1", "cypress": "^13.3.1", - "dotenv": "^16.3.1", "dotenv-webpack": "^8.0.1", "eslint": "^8.51.0", "eslint-config-airbnb": "^19.0.4", @@ -96,7 +93,6 @@ "msw": "^1.3.2", "postcss-styled-syntax": "^0.5.0", "prettier": "^3.0.3", - "react-refresh": "^0.14.0", "storybook": "^7.0.25", "style-loader": "^3.3.3", "stylelint": "^15.10.3", @@ -105,7 +101,6 @@ "stylelint-order": "^6.0.3", "terser-webpack-plugin": "^5.3.9", "ts-jest": "^29.1.1", - "ts-loader": "^9.5.0", "webpack": "^5.89.0", "webpack-bundle-analyzer": "^4.9.1", "webpack-cli": "^5.1.4", 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 &&