From 745e7e0889bf36d30dbd8a7ef283ba6823c4b764 Mon Sep 17 00:00:00 2001 From: Jungwoo Date: Mon, 25 Sep 2023 14:55:01 +0900 Subject: [PATCH 01/23] =?UTF-8?q?refactor:=20=EC=A0=84=EC=B2=B4=20?= =?UTF-8?q?=EC=84=A0=ED=83=9D=EC=9E=90=EB=A5=BC=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=ED=95=B4=20=ED=8F=B0=ED=8A=B8=EB=A5=BC=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/styles/GlobalStyle.tsx | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/client/src/styles/GlobalStyle.tsx b/client/src/styles/GlobalStyle.tsx index ce3e6c0fd..255e09133 100644 --- a/client/src/styles/GlobalStyle.tsx +++ b/client/src/styles/GlobalStyle.tsx @@ -33,12 +33,17 @@ const GlobalStyle = createGlobalStyle` * { scrollbar-width: none; + font-family: 'Noto Sans KR', 'Noto Sans' , sans-serif; &::-webkit-scrollbar { display: none; /* 크롬, 사파리, 오페라, 엣지 */ } } + body { + font-family: 'Noto Sans KR', 'Noto Sans' , sans-serif; + } + *, :after, :before { box-sizing: inherit; } @@ -67,13 +72,14 @@ const GlobalStyle = createGlobalStyle` @font-face { font-family: 'Noto Sans KR'; src: url(${require('../assets/fonts/NotoSansKR-Regular.woff')}) format('woff'); + unicode-range: U+0020-007E; } @font-face { font-family: 'Noto Sans'; font-display: swap; src: url(${require('../assets/fonts/NotoSans-Regular.woff')}) format('woff'); - unicode-range: U+0041-005A, U+0061-007A; + unicode-range: U+0020-007E; } :root { @@ -91,11 +97,6 @@ const GlobalStyle = createGlobalStyle` } body { - font-family: 'Noto Sans KR', 'Noto Sans' , sans-serif; - - button { - border:none; - } } input { From 12d781f08c722f23914087807f19b1735ca2e43e Mon Sep 17 00:00:00 2001 From: Jungwoo Date: Mon, 25 Sep 2023 14:55:53 +0900 Subject: [PATCH 02/23] =?UTF-8?q?refactor:=20=EC=9D=B8=EC=A6=9D/=EC=9D=B8?= =?UTF-8?q?=EA=B0=80=20=ED=94=8C=EB=A1=9C=EC=9A=B0=EC=9D=98=20=EB=B2=84?= =?UTF-8?q?=ED=8A=BC=20=EC=8A=A4=ED=83=80=EC=9D=BC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/signUpPage/signUpForm/SignUpForm.styles.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/client/src/components/signUpPage/signUpForm/SignUpForm.styles.ts b/client/src/components/signUpPage/signUpForm/SignUpForm.styles.ts index ff02e9d37..9dc8d429d 100644 --- a/client/src/components/signUpPage/signUpForm/SignUpForm.styles.ts +++ b/client/src/components/signUpPage/signUpForm/SignUpForm.styles.ts @@ -39,12 +39,16 @@ export const BoldText = styled.span` `; export const SubmitButton = styled.button` + ${({ theme }) => theme.fonts.button1}; + display: flex; + align-items: center; + justify-content: center; + width: 100%; height: 3rem; margin-top: 1rem; - padding: 1rem; + padding: 2rem; - ${({ theme }) => theme.fonts.button1}; color: ${({ theme }) => theme.colors.white}; background-color: ${({ theme }) => theme.colors.main_dark}; From 84777ac31e1f241671cdcc1adf0d202d9dd14da1 Mon Sep 17 00:00:00 2001 From: Jungwoo Date: Tue, 26 Sep 2023 17:00:09 +0900 Subject: [PATCH 03/23] =?UTF-8?q?feat:=20Slider=20Compound=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=EC=9D=98=20=EB=BC=88=EB=8C=80=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/_common/slider/Slider.tsx | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 client/src/components/_common/slider/Slider.tsx diff --git a/client/src/components/_common/slider/Slider.tsx b/client/src/components/_common/slider/Slider.tsx new file mode 100644 index 000000000..bb4f72c8f --- /dev/null +++ b/client/src/components/_common/slider/Slider.tsx @@ -0,0 +1,79 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import React, { + Dispatch, + PropsWithChildren, + SetStateAction, + createContext, + useContext, + useState, +} from 'react'; + +type SliderContextType = { + curIndex: number; + slideToPrevContent: () => void; + slideToNextContent: () => void; + setCurIndex: Dispatch>; + setContentLength: Dispatch>; +}; + +const SliderContext = createContext(null); + +const Slider = ({ children }: PropsWithChildren) => { + const [curIndex, setCurIndex] = useState(0); + const [contentLength, setContentLength] = useState(1); + + const isFirstContentIndex = curIndex === 0; + const isLastContentIndex = curIndex === contentLength - 1; + + const slideToPrevContent = () => { + setCurIndex((prev) => { + return isFirstContentIndex ? prev : prev - 1; + }); + }; + + const slideToNextContent = () => { + setCurIndex((prev) => { + return isLastContentIndex ? prev : prev + 1; + }); + }; + + return ( + + {children} + + ); +}; + +const PrevButton = ({ children }: PropsWithChildren) => { + const { slideToPrevContent } = useContext(SliderContext)!; + + return ; +}; + +const NextButton = ({ children }: PropsWithChildren) => { + const { slideToNextContent } = useContext(SliderContext)!; + + return ; +}; + +const Contents = ({ children }: PropsWithChildren) => { + const { curIndex, setContentLength } = useContext(SliderContext)!; + const childrenArray = React.Children.toArray(children); + setContentLength(childrenArray.length); + + return
{childrenArray[curIndex]}
; +}; + +Slider.PrevButton = PrevButton; +Slider.NextButton = NextButton; +Slider.Contents = Contents; + +export default Slider; From ae546f52a2ccae7fe4afbe650043c810f40a3294 Mon Sep 17 00:00:00 2001 From: sh981013s Date: Tue, 26 Sep 2023 17:57:50 +0900 Subject: [PATCH 04/23] =?UTF-8?q?feat:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=EC=95=88=EB=90=98=EC=96=B4=20=EC=9E=88=EB=8A=94=20=EA=B8=B0?= =?UTF-8?q?=EC=A4=80=EC=97=90=20accessToken=20=EA=B0=80=EC=A7=80=EA=B3=A0?= =?UTF-8?q?=20=EC=9E=88=EB=8A=94=EC=A7=80=20=EC=B2=B4=ED=81=AC=ED=95=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/App.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/src/App.tsx b/client/src/App.tsx index 42132db37..0bea35225 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -22,6 +22,7 @@ import useToast from '@hooks/_common/useToast'; import OAuthRedirect from './components/loginPage/OAuthRedirect'; import AsyncBoundary from './components/_common/errorBoundary/AsyncBoundary'; import SessionHandler from '@components/_common/sessionHandler/SessionHandler'; +import { getCookie } from '@utils/_common/cookies'; const GoalRoomDashboardPage = lazy( () => import('@pages/goalRoomDashboardPage/GoalRoomDashboardPage') @@ -36,9 +37,10 @@ const PrivateRouter = (props: PropsWithChildren) => { const { userInfo } = useUserInfoContext(); const { triggerToast } = useToast(); const navigate = useNavigate(); + const accessToken = getCookie('access_token'); useEffect(() => { - if (userInfo.id === null) { + if (userInfo.id === null && !accessToken) { navigate('/login'); triggerToast({ message: '로그인이 필요한 서비스입니다.' }); } From 908b3e5f80fa52bf4da5112d5190caf2989fbba1 Mon Sep 17 00:00:00 2001 From: sh981013s Date: Tue, 26 Sep 2023 18:00:26 +0900 Subject: [PATCH 05/23] =?UTF-8?q?feat:=20privateRouter=20=EA=B8=B0?= =?UTF-8?q?=EC=A1=B4=20App=20=EC=97=90=EC=84=9C=20=EB=B6=84=EB=A6=AC=20?= =?UTF-8?q?=ED=9B=84=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/App.tsx | 28 +++---------------- .../_common/privateRouter/PrivateRouter.tsx | 24 ++++++++++++++++ 2 files changed, 28 insertions(+), 24 deletions(-) create mode 100644 client/src/components/_common/privateRouter/PrivateRouter.tsx diff --git a/client/src/App.tsx b/client/src/App.tsx index 0bea35225..3ca656e84 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -1,5 +1,5 @@ -import { lazy, PropsWithChildren, Suspense, useEffect } from 'react'; -import { BrowserRouter, Route, Routes, useNavigate } from 'react-router-dom'; +import { lazy, Suspense } from 'react'; +import { BrowserRouter, Route, Routes } from 'react-router-dom'; import theme from '@styles/theme'; import GlobalStyle from '@styles/GlobalStyle'; import { ThemeProvider } from 'styled-components'; @@ -13,16 +13,13 @@ import RoadmapDetailPage from './pages/roadmapDetailPage/RoadmapDetailPage'; import RoadmapCreatePage from './pages/roadmapCreatePage/RoadmapCreatePage'; import ToastProvider from '@components/_common/toastProvider/ToastProvider'; import MyPage from '@pages/myPage/MyPage'; -import UserInfoProvider, { - useUserInfoContext, -} from './components/_providers/UserInfoProvider'; +import UserInfoProvider from './components/_providers/UserInfoProvider'; import RoadmapSearchResult from './components/roadmapListPage/roadmapSearch/RoadmapSearchResult'; import MainPage from '@pages/mainPage/MainPage'; -import useToast from '@hooks/_common/useToast'; import OAuthRedirect from './components/loginPage/OAuthRedirect'; import AsyncBoundary from './components/_common/errorBoundary/AsyncBoundary'; import SessionHandler from '@components/_common/sessionHandler/SessionHandler'; -import { getCookie } from '@utils/_common/cookies'; +import PrivateRouter from '@components/_common/privateRouter/PrivateRouter'; const GoalRoomDashboardPage = lazy( () => import('@pages/goalRoomDashboardPage/GoalRoomDashboardPage') @@ -32,23 +29,6 @@ const GoalRoomCreatePage = lazy( () => import('@pages/goalRoomCreatePage/GoalRoomCreatePage') ); -const PrivateRouter = (props: PropsWithChildren) => { - const { children } = props; - const { userInfo } = useUserInfoContext(); - const { triggerToast } = useToast(); - const navigate = useNavigate(); - const accessToken = getCookie('access_token'); - - useEffect(() => { - if (userInfo.id === null && !accessToken) { - navigate('/login'); - triggerToast({ message: '로그인이 필요한 서비스입니다.' }); - } - }, [userInfo.id, navigate]); - - return <>{children}; -}; - const App = () => { return ( diff --git a/client/src/components/_common/privateRouter/PrivateRouter.tsx b/client/src/components/_common/privateRouter/PrivateRouter.tsx new file mode 100644 index 000000000..c02e0323d --- /dev/null +++ b/client/src/components/_common/privateRouter/PrivateRouter.tsx @@ -0,0 +1,24 @@ +import { PropsWithChildren, useEffect } from 'react'; +import { useUserInfoContext } from '@components/_providers/UserInfoProvider'; +import useToast from '@hooks/_common/useToast'; +import { useNavigate } from 'react-router-dom'; +import { getCookie } from '@utils/_common/cookies'; + +const PrivateRouter = (props: PropsWithChildren) => { + const { children } = props; + const { userInfo } = useUserInfoContext(); + const { triggerToast } = useToast(); + const navigate = useNavigate(); + const accessToken = getCookie('access_token'); + + useEffect(() => { + if (userInfo.id === null && !accessToken) { + navigate('/login'); + triggerToast({ message: '로그인이 필요한 서비스입니다.' }); + } + }, [userInfo.id, navigate]); + + return <>{children}; +}; + +export default PrivateRouter; From bb3d8bd07fef102c971f215dc7d3f0ebfcfb43f8 Mon Sep 17 00:00:00 2001 From: Jungwoo Date: Thu, 28 Sep 2023 22:16:45 +0900 Subject: [PATCH 06/23] =?UTF-8?q?feat:=20Slider=EC=97=90=20=EA=B8=B0?= =?UTF-8?q?=EB=B3=B8=20=EC=8A=A4=ED=83=80=EC=9D=BC=EC=9D=84=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/_common/slider/Slider.tsx | 43 ++++++++++++++++--- .../_common/slider/Sliders.styles.ts | 23 ++++++++++ 2 files changed, 61 insertions(+), 5 deletions(-) create mode 100644 client/src/components/_common/slider/Sliders.styles.ts diff --git a/client/src/components/_common/slider/Slider.tsx b/client/src/components/_common/slider/Slider.tsx index bb4f72c8f..ef2409b92 100644 --- a/client/src/components/_common/slider/Slider.tsx +++ b/client/src/components/_common/slider/Slider.tsx @@ -1,3 +1,4 @@ +/* eslint-disable react/no-array-index-key */ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import React, { Dispatch, @@ -7,6 +8,7 @@ import React, { useContext, useState, } from 'react'; +import * as S from './Sliders.styles'; type SliderContextType = { curIndex: number; @@ -47,7 +49,7 @@ const Slider = ({ children }: PropsWithChildren) => { setContentLength, }} > - {children} + {children} ); }; @@ -55,21 +57,52 @@ const Slider = ({ children }: PropsWithChildren) => { const PrevButton = ({ children }: PropsWithChildren) => { const { slideToPrevContent } = useContext(SliderContext)!; - return ; + if (!React.isValidElement(children)) { + throw new Error('React Element children이 필요합니다'); + } + + // React.isValidElement는 런타임에 대상이 유요한 React Element인지 확인하기 때문에 as 키워드를 사용한 타입 단언이 필요합니다 + return React.cloneElement(children as React.ReactElement, { + onClick: slideToPrevContent, + style: { + position: 'absolute', + zIndex: 1, + ...children.props.style, + }, + }); }; const NextButton = ({ children }: PropsWithChildren) => { const { slideToNextContent } = useContext(SliderContext)!; - return ; + if (!React.isValidElement(children)) { + throw new Error('React Element children이 필요합니다'); + } + + return React.cloneElement(children as React.ReactElement, { + onClick: slideToNextContent, + style: { + position: 'absolute', + zIndex: 1, + right: 0, + ...children.props.style, + }, + }); }; const Contents = ({ children }: PropsWithChildren) => { const { curIndex, setContentLength } = useContext(SliderContext)!; - const childrenArray = React.Children.toArray(children); + // ReactElement이길 기대하지만 다른 타입이 오더라도(string 등) 정상적으로 동작합니다 + const childrenArray = React.Children.toArray(children) as React.ReactElement[]; setContentLength(childrenArray.length); - return
{childrenArray[curIndex]}
; + return ( + + {childrenArray.map((child, index) => ( + {child} + ))} + + ); }; Slider.PrevButton = PrevButton; diff --git a/client/src/components/_common/slider/Sliders.styles.ts b/client/src/components/_common/slider/Sliders.styles.ts new file mode 100644 index 000000000..d09b67136 --- /dev/null +++ b/client/src/components/_common/slider/Sliders.styles.ts @@ -0,0 +1,23 @@ +import styled from 'styled-components'; + +export const Slider = styled.div` + position: relative; + + overflow: hidden; + display: flex; + align-items: center; + + width: 100%; +`; + +export const Contents = styled.article<{ curIndex: number; length: number }>` + transform: ${({ curIndex }) => `translateX(${-curIndex * 100}%)`}; + display: flex; + width: ${({ length }) => `${length * 100}%`}; + transition: transform 0.3s ease; +`; + +export const Content = styled.div` + flex-shrink: 0; + width: 100%; +`; From 53ec1228556f46dee5cc97e7d586d8ea5f8b357f Mon Sep 17 00:00:00 2001 From: Jungwoo Date: Fri, 29 Sep 2023 16:40:18 +0900 Subject: [PATCH 07/23] =?UTF-8?q?refactor:=20media=20desktop=EC=9D=98=20?= =?UTF-8?q?=EC=A0=9C=EC=95=BD=EC=A1=B0=EA=B1=B4=EC=9D=84=20min-width?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/styles/media.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/styles/media.ts b/client/src/styles/media.ts index 1efe3875d..1ea234e2f 100644 --- a/client/src/styles/media.ts +++ b/client/src/styles/media.ts @@ -30,7 +30,7 @@ const media = { `, desktop: (styles: TemplateStringsArray) => css` - @media (max-width: ${BREAK_POINTS.DESKTOP}px) { + @media (min-width: ${BREAK_POINTS.DESKTOP}px) { ${styles} } `, From b5c0d1537d351c147e62dc4d8fe90974d414ff81 Mon Sep 17 00:00:00 2001 From: Jungwoo Date: Fri, 29 Sep 2023 16:54:25 +0900 Subject: [PATCH 08/23] =?UTF-8?q?refactor:=20css``=EB=A1=9C=20=EB=84=98?= =?UTF-8?q?=EA=B8=B4=20=EC=8A=A4=ED=83=80=EC=9D=BC=20=EA=B5=AC=EB=AC=B8?= =?UTF-8?q?=EC=9D=B4=20=EB=8F=99=EC=9E=91=ED=95=A0=20=EC=88=98=20=EC=9E=88?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=ED=83=80=EC=9E=85=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/styles/media.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/client/src/styles/media.ts b/client/src/styles/media.ts index 1ea234e2f..6bb9ba128 100644 --- a/client/src/styles/media.ts +++ b/client/src/styles/media.ts @@ -1,4 +1,4 @@ -import { css } from 'styled-components'; +import { RuleSet, css } from 'styled-components'; import BREAK_POINTS from '@constants/_common/breakPoints'; /* @@ -17,19 +17,19 @@ import BREAK_POINTS from '@constants/_common/breakPoints'; */ const media = { - mobile: (styles: TemplateStringsArray) => css` + mobile: (styles: TemplateStringsArray | RuleSet) => css` @media (max-width: ${BREAK_POINTS.MOBILE}px) { ${styles} } `, - tablet: (styles: TemplateStringsArray) => css` + tablet: (styles: TemplateStringsArray | RuleSet) => css` @media (max-width: ${BREAK_POINTS.TABLET}px) { ${styles} } `, - desktop: (styles: TemplateStringsArray) => css` + desktop: (styles: TemplateStringsArray | RuleSet) => css` @media (min-width: ${BREAK_POINTS.DESKTOP}px) { ${styles} } From 5cea6a8c965cc9f305f271dedb051bf92ec0879f Mon Sep 17 00:00:00 2001 From: Jungwoo Date: Fri, 29 Sep 2023 17:01:05 +0900 Subject: [PATCH 09/23] =?UTF-8?q?refactor:=20cloneElement=EB=A5=BC=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0=ED=95=98=EA=B3=A0=20Slider=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EB=82=B4=EB=B6=80=EC=97=90=20?= =?UTF-8?q?=EC=8A=A4=ED=83=80=EC=9D=BC=20=EC=A7=80=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/_common/slider/Slider.tsx | 92 +++++++++---------- .../_common/slider/Sliders.styles.ts | 39 +++++++- client/src/myTypes/_common/slider.ts | 25 +++++ 3 files changed, 107 insertions(+), 49 deletions(-) create mode 100644 client/src/myTypes/_common/slider.ts diff --git a/client/src/components/_common/slider/Slider.tsx b/client/src/components/_common/slider/Slider.tsx index ef2409b92..c67e233b8 100644 --- a/client/src/components/_common/slider/Slider.tsx +++ b/client/src/components/_common/slider/Slider.tsx @@ -1,28 +1,19 @@ -/* eslint-disable react/no-array-index-key */ /* eslint-disable @typescript-eslint/no-non-null-assertion */ -import React, { - Dispatch, - PropsWithChildren, - SetStateAction, - createContext, - useContext, - useState, -} from 'react'; +import React, { PropsWithChildren, createContext, useContext, useState } from 'react'; import * as S from './Sliders.styles'; - -type SliderContextType = { - curIndex: number; - slideToPrevContent: () => void; - slideToNextContent: () => void; - setCurIndex: Dispatch>; - setContentLength: Dispatch>; -}; +import type { + NextButtonProps, + PrevButtonProps, + SliderContextType, +} from '@myTypes/_common/slider'; +import useHover from '@hooks/_common/useHover'; const SliderContext = createContext(null); const Slider = ({ children }: PropsWithChildren) => { const [curIndex, setCurIndex] = useState(0); const [contentLength, setContentLength] = useState(1); + const { isHovered, handleMouseEnter, handleMouseLeave } = useHover(); const isFirstContentIndex = curIndex === 0; const isLastContentIndex = curIndex === contentLength - 1; @@ -47,47 +38,52 @@ const Slider = ({ children }: PropsWithChildren) => { slideToPrevContent, slideToNextContent, setContentLength, + isHovered, + isFirstContentIndex, + isLastContentIndex, }} > - {children} + + {children} + ); }; -const PrevButton = ({ children }: PropsWithChildren) => { - const { slideToPrevContent } = useContext(SliderContext)!; - - if (!React.isValidElement(children)) { - throw new Error('React Element children이 필요합니다'); - } +const PrevButton = ({ children, left, width, height }: PrevButtonProps) => { + const { slideToPrevContent, isHovered, isFirstContentIndex } = + useContext(SliderContext)!; - // React.isValidElement는 런타임에 대상이 유요한 React Element인지 확인하기 때문에 as 키워드를 사용한 타입 단언이 필요합니다 - return React.cloneElement(children as React.ReactElement, { - onClick: slideToPrevContent, - style: { - position: 'absolute', - zIndex: 1, - ...children.props.style, - }, - }); + return ( + + {children} + + ); }; -const NextButton = ({ children }: PropsWithChildren) => { - const { slideToNextContent } = useContext(SliderContext)!; - - if (!React.isValidElement(children)) { - throw new Error('React Element children이 필요합니다'); - } +const NextButton = ({ children, right, width, height }: NextButtonProps) => { + const { slideToNextContent, isHovered, isLastContentIndex } = + useContext(SliderContext)!; - return React.cloneElement(children as React.ReactElement, { - onClick: slideToNextContent, - style: { - position: 'absolute', - zIndex: 1, - right: 0, - ...children.props.style, - }, - }); + return ( + + {children} + + ); }; const Contents = ({ children }: PropsWithChildren) => { diff --git a/client/src/components/_common/slider/Sliders.styles.ts b/client/src/components/_common/slider/Sliders.styles.ts index d09b67136..ea1ee11e0 100644 --- a/client/src/components/_common/slider/Sliders.styles.ts +++ b/client/src/components/_common/slider/Sliders.styles.ts @@ -1,4 +1,5 @@ -import styled from 'styled-components'; +import media from '@styles/media'; +import styled, { css } from 'styled-components'; export const Slider = styled.div` position: relative; @@ -10,6 +11,42 @@ export const Slider = styled.div` width: 100%; `; +export const Button = styled.button<{ + isHovered: boolean; + width: number; + height: number; +}>` + position: absolute; + z-index: 1; + + display: flex; + align-items: center; + justify-content: center; + + width: ${({ width }) => `${width}rem`}; + height: ${({ height }) => `${height}rem`}; + + background-color: rgba(1, 1, 1, 0.2); + border-radius: 8px; + box-shadow: ${({ theme }) => theme.shadows.box}; + + ${({ isHovered }) => + media.desktop(css` + opacity: ${isHovered ? 1 : 0}; + transition: opacity 0.2s ease; + `)} +`; + +export const PrevButton = styled(Button)<{ isFirstContentIndex: boolean; left: number }>` + left: ${({ left }) => `${left}rem` || 0}; + display: ${({ isFirstContentIndex }) => isFirstContentIndex && 'none'}; +`; + +export const NextButton = styled(Button)<{ isLastContentIndex: boolean; right: number }>` + right: ${({ right }) => `${right}rem` || 0}; + display: ${({ isLastContentIndex }) => isLastContentIndex && 'none'}; +`; + export const Contents = styled.article<{ curIndex: number; length: number }>` transform: ${({ curIndex }) => `translateX(${-curIndex * 100}%)`}; display: flex; diff --git a/client/src/myTypes/_common/slider.ts b/client/src/myTypes/_common/slider.ts new file mode 100644 index 000000000..ed4a4fa57 --- /dev/null +++ b/client/src/myTypes/_common/slider.ts @@ -0,0 +1,25 @@ +import { Dispatch, PropsWithChildren, SetStateAction } from 'react'; + +export type SliderContextType = { + curIndex: number; + slideToPrevContent: () => void; + slideToNextContent: () => void; + setCurIndex: Dispatch>; + setContentLength: Dispatch>; + isHovered: boolean; + isFirstContentIndex: boolean; + isLastContentIndex: boolean; +}; + +type ButtonProps = { + width: number; + height: number; +} & PropsWithChildren; + +export type PrevButtonProps = { + left: number; +} & ButtonProps; + +export type NextButtonProps = { + right: number; +} & ButtonProps; From cec594635a7e3b9d24dc78ef069950ab3c39f711 Mon Sep 17 00:00:00 2001 From: Jungwoo Date: Fri, 29 Sep 2023 17:31:35 +0900 Subject: [PATCH 10/23] =?UTF-8?q?refactor:=20z-index=20=EC=A0=9C=EA=B1=B0?= =?UTF-8?q?=EB=A1=9C=20=EC=82=AC=EC=9A=A9=EC=B2=98=EC=97=90=EC=84=9C=20DOM?= =?UTF-8?q?=20=EB=A0=88=EC=9D=B4=EC=95=84=EC=9B=83=EC=9D=84=20=EA=B2=B0?= =?UTF-8?q?=EC=A0=95=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/components/_common/slider/Sliders.styles.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/client/src/components/_common/slider/Sliders.styles.ts b/client/src/components/_common/slider/Sliders.styles.ts index ea1ee11e0..6413e94c7 100644 --- a/client/src/components/_common/slider/Sliders.styles.ts +++ b/client/src/components/_common/slider/Sliders.styles.ts @@ -17,7 +17,6 @@ export const Button = styled.button<{ height: number; }>` position: absolute; - z-index: 1; display: flex; align-items: center; From 0d7a073977a3fea9252dd3a11a8b3c1823db5203 Mon Sep 17 00:00:00 2001 From: Jungwoo Date: Fri, 29 Sep 2023 18:10:08 +0900 Subject: [PATCH 11/23] =?UTF-8?q?feat:=20left,=20right=20=EC=95=84?= =?UTF-8?q?=EC=9D=B4=EC=BD=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/components/icons/svgIcons.tsx | 64 ++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/client/src/components/icons/svgIcons.tsx b/client/src/components/icons/svgIcons.tsx index e9dbf5940..b9ab881fa 100644 --- a/client/src/components/icons/svgIcons.tsx +++ b/client/src/components/icons/svgIcons.tsx @@ -2454,3 +2454,67 @@ export const ImageUploadIcon = ({ width, ...props }: SVGProps) => /> ); + +export const TriangleLeftIcon = ({ width, ...props }: SVGProps) => ( + + + +); + +export const TriangleRightIcon = ({ width, ...props }: SVGProps) => ( + + + +); + +export const LeftIcon = ({ width, ...props }: SVGProps) => ( + + + +); + +export const RightIcon = ({ width, ...props }: SVGProps) => ( + + + +); From bfa83502c2502a3256b2ce9b232ee2278ecb66d8 Mon Sep 17 00:00:00 2001 From: Jungwoo Date: Fri, 29 Sep 2023 18:26:33 +0900 Subject: [PATCH 12/23] =?UTF-8?q?refactor:=20=EC=BB=B4=ED=8C=8C=EC=9A=B4?= =?UTF-8?q?=EB=93=9C=20=ED=8C=A8=ED=84=B4=20=EC=A0=9C=EA=B1=B0=20=EB=B0=8F?= =?UTF-8?q?=20=ED=9B=85=EA=B3=BC=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8?= =?UTF-8?q?=EC=9D=98=20=EC=A1=B0=ED=95=A9=EC=9C=BC=EB=A1=9C=20Slider=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{Sliders.styles.ts => Slider.styles.ts} | 21 ++- .../src/components/_common/slider/Slider.tsx | 131 +++++------------- client/src/hooks/_common/useSlider.ts | 29 ++++ client/src/myTypes/_common/slider.ts | 25 ---- 4 files changed, 72 insertions(+), 134 deletions(-) rename client/src/components/_common/slider/{Sliders.styles.ts => Slider.styles.ts} (77%) create mode 100644 client/src/hooks/_common/useSlider.ts delete mode 100644 client/src/myTypes/_common/slider.ts diff --git a/client/src/components/_common/slider/Sliders.styles.ts b/client/src/components/_common/slider/Slider.styles.ts similarity index 77% rename from client/src/components/_common/slider/Sliders.styles.ts rename to client/src/components/_common/slider/Slider.styles.ts index 6413e94c7..25868a8c6 100644 --- a/client/src/components/_common/slider/Sliders.styles.ts +++ b/client/src/components/_common/slider/Slider.styles.ts @@ -11,19 +11,15 @@ export const Slider = styled.div` width: 100%; `; -export const Button = styled.button<{ - isHovered: boolean; - width: number; - height: number; -}>` +export const Button = styled.button<{ isHovered: boolean }>` position: absolute; display: flex; align-items: center; justify-content: center; - width: ${({ width }) => `${width}rem`}; - height: ${({ height }) => `${height}rem`}; + width: 7rem; + height: 12rem; background-color: rgba(1, 1, 1, 0.2); border-radius: 8px; @@ -36,13 +32,13 @@ export const Button = styled.button<{ `)} `; -export const PrevButton = styled(Button)<{ isFirstContentIndex: boolean; left: number }>` - left: ${({ left }) => `${left}rem` || 0}; +export const PrevButton = styled(Button)<{ isFirstContentIndex: boolean }>` + left: 2rem; display: ${({ isFirstContentIndex }) => isFirstContentIndex && 'none'}; `; -export const NextButton = styled(Button)<{ isLastContentIndex: boolean; right: number }>` - right: ${({ right }) => `${right}rem` || 0}; +export const NextButton = styled(Button)<{ isLastContentIndex: boolean }>` + right: 2rem; display: ${({ isLastContentIndex }) => isLastContentIndex && 'none'}; `; @@ -56,4 +52,7 @@ export const Contents = styled.article<{ curIndex: number; length: number }>` export const Content = styled.div` flex-shrink: 0; width: 100%; + & > * { + min-height: 12rem; + } `; diff --git a/client/src/components/_common/slider/Slider.tsx b/client/src/components/_common/slider/Slider.tsx index c67e233b8..5db87845b 100644 --- a/client/src/components/_common/slider/Slider.tsx +++ b/client/src/components/_common/slider/Slider.tsx @@ -1,108 +1,43 @@ -/* eslint-disable @typescript-eslint/no-non-null-assertion */ -import React, { PropsWithChildren, createContext, useContext, useState } from 'react'; -import * as S from './Sliders.styles'; -import type { - NextButtonProps, - PrevButtonProps, - SliderContextType, -} from '@myTypes/_common/slider'; +import { PropsWithChildren } from 'react'; +import * as S from './Slider.styles'; import useHover from '@hooks/_common/useHover'; - -const SliderContext = createContext(null); +import SVGIcon from '@components/icons/SVGIcon'; +import useSlider from '@hooks/_common/useSlider'; const Slider = ({ children }: PropsWithChildren) => { - const [curIndex, setCurIndex] = useState(0); - const [contentLength, setContentLength] = useState(1); + const { + curIndex, + slideToPrevContent, + slideToNextContent, + isFirstContentIndex, + isLastContentIndex, + childrenArray, + } = useSlider(children); const { isHovered, handleMouseEnter, handleMouseLeave } = useHover(); - const isFirstContentIndex = curIndex === 0; - const isLastContentIndex = curIndex === contentLength - 1; - - const slideToPrevContent = () => { - setCurIndex((prev) => { - return isFirstContentIndex ? prev : prev - 1; - }); - }; - - const slideToNextContent = () => { - setCurIndex((prev) => { - return isLastContentIndex ? prev : prev + 1; - }); - }; - - return ( - - - {children} - - - ); -}; - -const PrevButton = ({ children, left, width, height }: PrevButtonProps) => { - const { slideToPrevContent, isHovered, isFirstContentIndex } = - useContext(SliderContext)!; - - return ( - - {children} - - ); -}; - -const NextButton = ({ children, right, width, height }: NextButtonProps) => { - const { slideToNextContent, isHovered, isLastContentIndex } = - useContext(SliderContext)!; - - return ( - - {children} - - ); -}; - -const Contents = ({ children }: PropsWithChildren) => { - const { curIndex, setContentLength } = useContext(SliderContext)!; - // ReactElement이길 기대하지만 다른 타입이 오더라도(string 등) 정상적으로 동작합니다 - const childrenArray = React.Children.toArray(children) as React.ReactElement[]; - setContentLength(childrenArray.length); - return ( - - {childrenArray.map((child, index) => ( - {child} - ))} - + + + {childrenArray.map((child, index) => ( + {child} + ))} + + + + + + + + ); }; -Slider.PrevButton = PrevButton; -Slider.NextButton = NextButton; -Slider.Contents = Contents; - export default Slider; diff --git a/client/src/hooks/_common/useSlider.ts b/client/src/hooks/_common/useSlider.ts new file mode 100644 index 000000000..1204c7552 --- /dev/null +++ b/client/src/hooks/_common/useSlider.ts @@ -0,0 +1,29 @@ +import React, { useState, ReactNode } from 'react'; + +const useSlider = (children: ReactNode) => { + const [curIndex, setCurIndex] = useState(0); + const childrenArray = React.Children.toArray(children) as React.ReactElement[]; + const contentLength = childrenArray.length; + + const isFirstContentIndex = curIndex === 0; + const isLastContentIndex = curIndex === contentLength - 1; + + const slideToPrevContent = () => { + setCurIndex((prev) => (isFirstContentIndex ? prev : prev - 1)); + }; + + const slideToNextContent = () => { + setCurIndex((prev) => (isLastContentIndex ? prev : prev + 1)); + }; + + return { + curIndex, + slideToPrevContent, + slideToNextContent, + isFirstContentIndex, + isLastContentIndex, + childrenArray, + }; +}; + +export default useSlider; diff --git a/client/src/myTypes/_common/slider.ts b/client/src/myTypes/_common/slider.ts deleted file mode 100644 index ed4a4fa57..000000000 --- a/client/src/myTypes/_common/slider.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { Dispatch, PropsWithChildren, SetStateAction } from 'react'; - -export type SliderContextType = { - curIndex: number; - slideToPrevContent: () => void; - slideToNextContent: () => void; - setCurIndex: Dispatch>; - setContentLength: Dispatch>; - isHovered: boolean; - isFirstContentIndex: boolean; - isLastContentIndex: boolean; -}; - -type ButtonProps = { - width: number; - height: number; -} & PropsWithChildren; - -export type PrevButtonProps = { - left: number; -} & ButtonProps; - -export type NextButtonProps = { - right: number; -} & ButtonProps; From 8d25393cb6ee3c03d2aa43443c392752ca17a810 Mon Sep 17 00:00:00 2001 From: Jungwoo Date: Fri, 29 Sep 2023 18:35:13 +0900 Subject: [PATCH 13/23] =?UTF-8?q?refactor:=20=EC=82=AC=EC=9A=A9=EB=90=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EC=95=84=EC=9D=B4=EC=BD=98=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/components/icons/svgIcons.tsx | 30 ------------------------ 1 file changed, 30 deletions(-) diff --git a/client/src/components/icons/svgIcons.tsx b/client/src/components/icons/svgIcons.tsx index b9ab881fa..603aa7295 100644 --- a/client/src/components/icons/svgIcons.tsx +++ b/client/src/components/icons/svgIcons.tsx @@ -2455,36 +2455,6 @@ export const ImageUploadIcon = ({ width, ...props }: SVGProps) => ); -export const TriangleLeftIcon = ({ width, ...props }: SVGProps) => ( - - - -); - -export const TriangleRightIcon = ({ width, ...props }: SVGProps) => ( - - - -); - export const LeftIcon = ({ width, ...props }: SVGProps) => ( Date: Sat, 30 Sep 2023 17:26:28 +0900 Subject: [PATCH 14/23] =?UTF-8?q?feat:=20No=20Image=20=EC=95=84=EC=9D=B4?= =?UTF-8?q?=EC=BD=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/components/icons/svgIcons.tsx | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/client/src/components/icons/svgIcons.tsx b/client/src/components/icons/svgIcons.tsx index e9dbf5940..4a0c0e400 100644 --- a/client/src/components/icons/svgIcons.tsx +++ b/client/src/components/icons/svgIcons.tsx @@ -2454,3 +2454,18 @@ export const ImageUploadIcon = ({ width, ...props }: SVGProps) => /> ); + +export const NoImageIcon = ({ width, ...props }: SVGProps) => ( + + + +); From 13aeb995f903af77c4df33966806f0b8219be7d0 Mon Sep 17 00:00:00 2001 From: Jungwoo Date: Sat, 30 Sep 2023 17:27:34 +0900 Subject: [PATCH 15/23] =?UTF-8?q?feat:=20=EB=A1=9C=EB=93=9C=EB=A7=B5?= =?UTF-8?q?=EC=9D=98=20ExtraInfo(Meta=20data)=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../extraInfo/ExtraInfo.styles.ts | 44 +++++++++++++++++++ .../roadmapDetailPage/extraInfo/ExtraInfo.tsx | 33 ++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 client/src/components/roadmapDetailPage/extraInfo/ExtraInfo.styles.ts create mode 100644 client/src/components/roadmapDetailPage/extraInfo/ExtraInfo.tsx diff --git a/client/src/components/roadmapDetailPage/extraInfo/ExtraInfo.styles.ts b/client/src/components/roadmapDetailPage/extraInfo/ExtraInfo.styles.ts new file mode 100644 index 000000000..5485af792 --- /dev/null +++ b/client/src/components/roadmapDetailPage/extraInfo/ExtraInfo.styles.ts @@ -0,0 +1,44 @@ +import styled from 'styled-components'; +import media from '@styles/media'; + +export const ExtraInfo = styled.div` + ${({ theme }) => theme.fonts.description5}; + display: flex; + flex-direction: column; + align-items: flex-end; + justify-content: space-between; + + width: 30%; + height: 55rem; + + ${media.mobile` + display: none; + `} +`; + +export const RoadmapMetadata = styled.div` + display: flex; + flex-direction: column; + + & > div:not(:last-child) { + margin-bottom: 3rem; + } +`; + +export const Category = styled.div` + display: flex; + align-items: center; + justify-content: flex-end; +`; + +export const Difficulty = styled.div` + text-align: end; +`; + +export const RecommendedRoadmapPeriod = styled.div` + text-align: end; +`; + +export const Tags = styled.div` + color: ${({ theme }) => theme.colors.main_dark}; +`; diff --git a/client/src/components/roadmapDetailPage/extraInfo/ExtraInfo.tsx b/client/src/components/roadmapDetailPage/extraInfo/ExtraInfo.tsx new file mode 100644 index 000000000..4959bca11 --- /dev/null +++ b/client/src/components/roadmapDetailPage/extraInfo/ExtraInfo.tsx @@ -0,0 +1,33 @@ +import { RoadmapDetailType } from '@myTypes/roadmap/internal'; +import * as S from './ExtraInfo.styles'; +import SVGIcon from '@components/icons/SVGIcon'; +import { CategoriesInfo } from '@constants/roadmap/category'; + +type ExtraInfoProps = { + roadmapInfo: RoadmapDetailType; +}; + +const ExtraInfo = ({ roadmapInfo }: ExtraInfoProps) => { + return ( + +
Created by {roadmapInfo.creator.name}
+ + + 카테고리: {roadmapInfo.category.name} + + + 난이도: {roadmapInfo.difficulty} + + 예상 소요시간: {roadmapInfo.recommendedRoadmapPeriod}일 + + + + {roadmapInfo.tags.map((tag) => ( +
#{tag.name}
+ ))} +
+
+ ); +}; + +export default ExtraInfo; From 51212f5846a964c0129cbd967910fa424b9daa27 Mon Sep 17 00:00:00 2001 From: Jungwoo Date: Sat, 30 Sep 2023 17:29:02 +0900 Subject: [PATCH 16/23] =?UTF-8?q?feat:=20Introduction(=EB=A1=9C=EB=93=9C?= =?UTF-8?q?=EB=A7=B5=20=EC=84=A4=EB=AA=85)=20=EC=BB=B4=ED=8F=AC=EB=84=8C?= =?UTF-8?q?=ED=8A=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../introduction/Introduction.styles.ts | 47 +++++++++++++++++ .../introduction/Introduction.tsx | 51 +++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 client/src/components/roadmapDetailPage/introduction/Introduction.styles.ts create mode 100644 client/src/components/roadmapDetailPage/introduction/Introduction.tsx diff --git a/client/src/components/roadmapDetailPage/introduction/Introduction.styles.ts b/client/src/components/roadmapDetailPage/introduction/Introduction.styles.ts new file mode 100644 index 000000000..bd5f1e6bf --- /dev/null +++ b/client/src/components/roadmapDetailPage/introduction/Introduction.styles.ts @@ -0,0 +1,47 @@ +import media from '@styles/media'; +import styled from 'styled-components'; + +export const IntroductionWrapper = styled.div` + width: 70%; + + ${media.mobile` + width:100%; + `} +`; + +export const Introduction = styled.div<{ isExpanded: boolean }>` + ${({ theme }) => theme.fonts.description5}; + overflow: hidden; + max-height: ${({ isExpanded }) => (isExpanded ? 'auto' : '55rem')}; + + & > p:not(:last-child) { + margin-bottom: 2rem; + } + + & div { + ${({ theme }) => theme.fonts.h1}; + margin-bottom: 0.5rem; + color: ${({ theme }) => theme.colors.main_dark}; + } +`; + +export const LineShadow = styled.div` + position: relative; + width: 100%; + height: 0.2rem; + box-shadow: 0 -4px 6px rgba(0, 0, 0, 1); +`; + +export const ReadMoreButton = styled.button` + position: relative; + top: calc(-2rem - 4px); + left: 50%; + transform: translateX(-5rem); + + width: 10rem; + height: 4rem; + + background-color: ${({ theme }) => theme.colors.white}; + border-radius: 8px; + box-shadow: ${({ theme }) => theme.shadows.main}; +`; diff --git a/client/src/components/roadmapDetailPage/introduction/Introduction.tsx b/client/src/components/roadmapDetailPage/introduction/Introduction.tsx new file mode 100644 index 000000000..2fae32dd3 --- /dev/null +++ b/client/src/components/roadmapDetailPage/introduction/Introduction.tsx @@ -0,0 +1,51 @@ +import { RoadmapDetailType } from '@myTypes/roadmap/internal'; +import * as S from './Introduction.styles'; +import { useEffect, useRef, useState } from 'react'; + +type IntroductionType = { + roadmapInfo: RoadmapDetailType; +}; + +const Introduction = ({ roadmapInfo }: IntroductionType) => { + const [isExpanded, setIsExpanded] = useState(false); + const [showMoreButton, setShowMoreButton] = useState(false); + const introRef = useRef(null); + + useEffect(() => { + if (!introRef.current) return; + + const element = introRef.current; + if (element.scrollHeight > element.clientHeight) { + setShowMoreButton(true); + } + }, []); + + const toggleExpand = () => { + setIsExpanded(!isExpanded); + }; + + return ( + + +

+

설명
+ {roadmapInfo.introduction} +

+

+

본문
+ {roadmapInfo.content.content === '' + ? '로드맵에 대한 설명이 없어요🥲' + : roadmapInfo.content.content} +

+
+ {showMoreButton && !isExpanded && ( + <> + + 더 보기 + + )} +
+ ); +}; + +export default Introduction; From e01e873566b684429c242fd2be1cfa8adb86299a Mon Sep 17 00:00:00 2001 From: Jungwoo Date: Sat, 30 Sep 2023 17:29:49 +0900 Subject: [PATCH 17/23] =?UTF-8?q?feat:=20NodeContent(=EA=B0=81=20=EB=85=B8?= =?UTF-8?q?=EB=93=9C=20=EC=A0=95=EB=B3=B4=20Slider)=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nodeContent/NodeContent.styles.ts | 92 +++++++++++++++++++ .../nodeContent/NodeContent.tsx | 38 ++++++++ 2 files changed, 130 insertions(+) create mode 100644 client/src/components/roadmapDetailPage/nodeContent/NodeContent.styles.ts create mode 100644 client/src/components/roadmapDetailPage/nodeContent/NodeContent.tsx diff --git a/client/src/components/roadmapDetailPage/nodeContent/NodeContent.styles.ts b/client/src/components/roadmapDetailPage/nodeContent/NodeContent.styles.ts new file mode 100644 index 000000000..9db8597f8 --- /dev/null +++ b/client/src/components/roadmapDetailPage/nodeContent/NodeContent.styles.ts @@ -0,0 +1,92 @@ +import styled from 'styled-components'; +import media from '@styles/media'; + +export const SliderContent = styled.div` + display: flex; + aspect-ratio: 5 / 3.5; + background-color: ${({ theme }) => theme.colors.gray100}; + border-radius: 8px; + + ${media.mobile` + flex-direction: column; + aspect-ratio: 0; + `} +`; + +export const LeftContent = styled.div` + flex-shrink: 0; + width: 45%; + + ${media.mobile` + display:none; + `} +`; + +export const NodeImg = styled.img` + width: 100%; + height: 100%; + padding: 1.5rem; + object-fit: cover; +`; + +export const NoImg = styled.div` + ${({ theme }) => theme.fonts.title_large} + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + + width: 100%; + height: 100%; +`; + +export const Separator = styled.div` + display: flex; + flex-direction: column; + width: 0.2rem; + height: 100%; + + & > div { + height: 50%; + } + + & > div:last-child { + background-color: black; + } +`; + +export const RightContent = styled.div` + ${({ theme }) => theme.fonts.h1} + overflow: scroll; + width: 55%; + padding: 1.5rem; + padding-top: 3rem; + + ${media.mobile` + width: 100%; + height: 60rem; + padding-top: 1.5rem; + `} +`; + +export const ContentTitle = styled.div` + ${({ theme }) => theme.fonts.title_large} + display: flex; + align-items: center; + margin-bottom: 1rem; +`; + +export const Step = styled.div` + ${({ theme }) => theme.fonts.h2} + display: flex; + flex-shrink: 0; + align-items: center; + justify-content: center; + + width: 3rem; + height: 3rem; + margin-right: 0.5rem; + + border: 0.3rem solid black; + border-radius: 50%; +`; diff --git a/client/src/components/roadmapDetailPage/nodeContent/NodeContent.tsx b/client/src/components/roadmapDetailPage/nodeContent/NodeContent.tsx new file mode 100644 index 000000000..f2ffb7e60 --- /dev/null +++ b/client/src/components/roadmapDetailPage/nodeContent/NodeContent.tsx @@ -0,0 +1,38 @@ +import type { NodeType } from '@myTypes/roadmap/internal'; +import * as S from './NodeContent.styles'; +import SVGIcon from '@components/icons/SVGIcon'; + +type NodeContentProps = { + node: NodeType; + index: number; +}; + +const NodeContent = ({ node, index }: NodeContentProps) => { + return ( + + + {node.imageUrls[0] ? ( + + ) : ( + + +
No Image
+
+ )} +
+ +
+
+ + + + {index + 1} +

{node.title}

+
+ {node.description} +
+ + ); +}; + +export default NodeContent; From efe0c38d7169a70d7eb6482c48549a3f8f10bf78 Mon Sep 17 00:00:00 2001 From: Jungwoo Date: Sat, 30 Sep 2023 17:30:31 +0900 Subject: [PATCH 18/23] =?UTF-8?q?feat:=20=EB=A1=9C=EB=93=9C=EB=A7=B5=20?= =?UTF-8?q?=EB=94=94=ED=85=8C=EC=9D=BC=20=EC=BB=B4=ED=8F=AC=EB=84=8C?= =?UTF-8?q?=ED=8A=B8=20UI=20=EA=B0=9C=ED=8E=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../roadmapDetail/RoadmapDetail.styles.ts | 41 ++------- .../roadmapDetail/RoadmapDetail.tsx | 46 +++++----- .../roadmapNodeList/RoadmapNodeList.tsx | 35 -------- .../roadmapNodeList/roadmapNodeList.styles.ts | 89 ------------------- 4 files changed, 30 insertions(+), 181 deletions(-) delete mode 100644 client/src/components/roadmapDetailPage/roadmapNodeList/RoadmapNodeList.tsx delete mode 100644 client/src/components/roadmapDetailPage/roadmapNodeList/roadmapNodeList.styles.ts diff --git a/client/src/components/roadmapDetailPage/roadmapDetail/RoadmapDetail.styles.ts b/client/src/components/roadmapDetailPage/roadmapDetail/RoadmapDetail.styles.ts index f7ee43222..ce6785c43 100644 --- a/client/src/components/roadmapDetailPage/roadmapDetail/RoadmapDetail.styles.ts +++ b/client/src/components/roadmapDetailPage/roadmapDetail/RoadmapDetail.styles.ts @@ -1,42 +1,19 @@ -import media from '@styles/media'; import styled from 'styled-components'; export const RoadmapDetail = styled.div` - position: relative; - margin: 4rem 0; - padding: 0 2rem; - - ${media.mobile` - flex-direction: column; - align-items: center; - `} + padding: 2rem 0 4rem 0; `; -export const RoadmapBody = styled.p` - ${({ theme }) => theme.fonts.button1} - width: 50%; - padding: 4rem 4rem; - height: 35rem; - - overflow: scroll; - - border-radius: 18px; - box-shadow: ${({ theme }) => theme.shadows.box}; - - color: ${({ theme }) => theme.colors.gray300}; - - ${media.mobile` - padding: 4rem 4rem; - `} +export const RoadmapInfo = styled.div` + margin-bottom: 4rem; +`; - & > strong { - ${({ theme }) => theme.fonts.h1}; - margin-bottom: 4rem; - color: ${({ theme }) => theme.colors.main_dark}; - } +export const Title = styled.div` + ${({ theme }) => theme.fonts.title_large}; + margin-bottom: 2rem; + color: ${({ theme }) => theme.colors.main_dark}; `; -export const PageOnTop = styled.div` +export const Description = styled.div` display: flex; - justify-content: space-around; `; diff --git a/client/src/components/roadmapDetailPage/roadmapDetail/RoadmapDetail.tsx b/client/src/components/roadmapDetailPage/roadmapDetail/RoadmapDetail.tsx index 94da78baa..76ee29aec 100644 --- a/client/src/components/roadmapDetailPage/roadmapDetail/RoadmapDetail.tsx +++ b/client/src/components/roadmapDetailPage/roadmapDetail/RoadmapDetail.tsx @@ -1,36 +1,32 @@ -import RoadmapItem from '../../_common/roadmapItem/RoadmapItem'; -import Button from '../../_common/button/Button'; import * as S from './RoadmapDetail.styles'; -import useValidParams from '@/hooks/_common/useValidParams'; -import { useNavigate } from 'react-router-dom'; -import { useRoadmapDetail } from '@/hooks/queries/roadmap'; -import RoadmapNodeList from '../roadmapNodeList/RoadmapNodeList'; +import useValidParams from '@hooks/_common/useValidParams'; +// import { useNavigate } from 'react-router-dom'; +import { useRoadmapDetail } from '@hooks/queries/roadmap'; + +import Slider from '@components/_common/slider/Slider'; +import NodeContent from '../nodeContent/NodeContent'; +import ExtraInfo from '../extraInfo/ExtraInfo'; +import Introduction from '../introduction/Introduction'; const RoadmapDetail = () => { const { id: roadmapId } = useValidParams<{ id: string }>(); - const navigate = useNavigate(); + // const navigate = useNavigate(); const { roadmapInfo } = useRoadmapDetail(Number(roadmapId)); - const moveToGoalRoomCreatePage = () => { - navigate(`/roadmap/${roadmapId}/goalroom-create`); - }; - return ( - - - - 로드맵 설명
- {roadmapInfo.content.content === '' - ? '로드맵에 대한 설명이 없어요🥲' - : roadmapInfo.content.content} -
-
- - + + {roadmapInfo.roadmapTitle} + + + + + + + {roadmapInfo.content.nodes.map((node, index) => ( + + ))} +
); }; diff --git a/client/src/components/roadmapDetailPage/roadmapNodeList/RoadmapNodeList.tsx b/client/src/components/roadmapDetailPage/roadmapNodeList/RoadmapNodeList.tsx deleted file mode 100644 index 2f6ab88d6..000000000 --- a/client/src/components/roadmapDetailPage/roadmapNodeList/RoadmapNodeList.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { NodeType } from '@/myTypes/roadmap/internal'; -import * as S from './roadmapNodeList.styles'; - -type RoadmapNodeListProps = { - roadmapTitle: string; - nodeInfo: NodeType[]; -}; - -const RoadmapNodeList = ({ roadmapTitle, nodeInfo }: RoadmapNodeListProps) => { - return ( - - - 🐘 {roadmapTitle}의 로드맵 🐘 - - {nodeInfo.map((node, index) => { - return ( - - {index + 1} - - {node.title} - {node.description} - - {node.imageUrls.map((nodeImage) => { - return ; - })} - - - - ); - })} - - ); -}; - -export default RoadmapNodeList; diff --git a/client/src/components/roadmapDetailPage/roadmapNodeList/roadmapNodeList.styles.ts b/client/src/components/roadmapDetailPage/roadmapNodeList/roadmapNodeList.styles.ts deleted file mode 100644 index da3d3cd20..000000000 --- a/client/src/components/roadmapDetailPage/roadmapNodeList/roadmapNodeList.styles.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { styled } from 'styled-components'; - -export const RoadmapNodeList = styled.div` - display: flex; - flex-direction: column; - align-items: center; - justify-content: space-around; - - width: 100%; - min-height: 30rem; - margin-top: 3rem; - padding: 2rem 0; - - border-radius: 12px; - box-shadow: ${({ theme }) => theme.shadows.box}; -`; - -export const Title = styled.h1` - ${({ theme }) => theme.fonts.nav_title} - color: ${({ theme }) => theme.colors.gray300}; - - & > strong { - color: ${({ theme }) => theme.colors.main_dark}; - } -`; - -export const NodeItemContainer = styled.article` - display: flex; - align-items: center; - justify-content: space-around; - margin-top: 2rem; -`; - -export const NodeIndicator = styled.div` - ${({ theme }) => theme.fonts.nav_title} - position: relative; - left: 2rem; - - display: flex; - align-items: center; - justify-content: center; - - width: 4rem; - height: 4rem; - - color: ${({ theme }) => theme.colors.white}; - - background-color: ${({ theme }) => theme.colors.main_dark}; - border-radius: 50%; -`; - -export const NodeContent = styled.div` - overflow-y: scroll; - display: flex; - flex-direction: column; - align-items: center; - - width: 50rem; - padding: 2rem 0; - - word-wrap: break-word; - overflow-wrap: break-word; - white-space: normal; - - border: 0.3rem solid ${({ theme }) => theme.colors.main_dark}; - border-radius: 24px; -`; - -export const NodeTitle = styled.h2` - ${({ theme }) => theme.fonts.h2} - color: ${({ theme }) => theme.colors.black}; -`; - -export const NodeDescription = styled.div` - ${({ theme }) => theme.fonts.description5} - width: 40rem; - color: ${({ theme }) => theme.colors.gray300}; - text-align: center; -`; - -export const ImageWrapper = styled.div` - display: flex; -`; - -export const NodeImage = styled.img` - width: 15rem; - height: 20rem; - object-fit: cover; -`; From 1e1a180a605eb467e91cdab518f38db5bce56dea Mon Sep 17 00:00:00 2001 From: NaveOWO <87578512+NaveOWO@users.noreply.github.com> Date: Sat, 30 Sep 2023 23:32:05 +0900 Subject: [PATCH 19/23] =?UTF-8?q?[feat/CK-213]=20=EB=A1=9C=EB=93=9C?= =?UTF-8?q?=EB=A7=B5=20=EA=B2=80=EC=83=89=20=EC=98=A4=EB=A5=98=EB=A5=BC=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0=ED=95=9C=EB=8B=A4.=20(#172)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: enter키로 검색하는 로직 추가 * feat: no result 페이지 생성 * feat: 검색결과 초기화할 때 검색어 초기화로직 추가 * refactor: 검색바 form으로 변경 * refactor: 구조분해할당 --- .../roadmapList/RoadmapList.tsx | 4 +-- .../roadmapSearch/NoResult.tsx | 7 ++++++ .../roadmapSearch/RoadmapSearch.tsx | 14 ++++++++--- .../roadmapSearch/RoadmapSearchResult.tsx | 25 +++++++++++-------- .../roadmapSearch/roadmapSearch.styles.ts | 16 +++++++++++- 5 files changed, 48 insertions(+), 18 deletions(-) create mode 100644 client/src/components/roadmapListPage/roadmapSearch/NoResult.tsx diff --git a/client/src/components/roadmapListPage/roadmapList/RoadmapList.tsx b/client/src/components/roadmapListPage/roadmapList/RoadmapList.tsx index 166299b20..221a65953 100644 --- a/client/src/components/roadmapListPage/roadmapList/RoadmapList.tsx +++ b/client/src/components/roadmapListPage/roadmapList/RoadmapList.tsx @@ -20,7 +20,7 @@ const RoadmapList = ({ selectedCategoryId }: RoadmapListProps) => { }); const loadMoreRef = useInfiniteScroll({ - hasNextPage: roadmapListResponse?.hasNext, + hasNextPage: roadmapListResponse.hasNext, fetchNextPage, }); @@ -35,7 +35,7 @@ const RoadmapList = ({ selectedCategoryId }: RoadmapListProps) => { {roadmapListResponse.responses.map((item) => ( ))} - {roadmapListResponse?.hasNext && } + {roadmapListResponse.hasNext && } + ); diff --git a/client/src/components/roadmapListPage/roadmapSearch/NoResult.tsx b/client/src/components/roadmapListPage/roadmapSearch/NoResult.tsx new file mode 100644 index 000000000..45bcb5ef5 --- /dev/null +++ b/client/src/components/roadmapListPage/roadmapSearch/NoResult.tsx @@ -0,0 +1,7 @@ +import * as S from './roadmapSearch.styles'; + +const NoResult = () => { + return 검색결과가 존재하지 않습니다. 😭; +}; + +export default NoResult; diff --git a/client/src/components/roadmapListPage/roadmapSearch/RoadmapSearch.tsx b/client/src/components/roadmapListPage/roadmapSearch/RoadmapSearch.tsx index 0a97e2943..635c02c44 100644 --- a/client/src/components/roadmapListPage/roadmapSearch/RoadmapSearch.tsx +++ b/client/src/components/roadmapListPage/roadmapSearch/RoadmapSearch.tsx @@ -1,6 +1,6 @@ import { SearchIcon } from '@/components/icons/svgIcons'; import { Select } from '@/components/roadmapCreatePage/selector/SelectBox'; -import { useRef, useState } from 'react'; +import { FormEvent, useRef, useState } from 'react'; import { useNavigate } from 'react-router-dom'; import * as S from './roadmapSearch.styles'; @@ -24,11 +24,17 @@ const RoadmapSearch = () => { } }; - const searchRoadmap = () => { + const searchRoadmap = (e: FormEvent) => { + e.preventDefault(); + if (searchWordRef.current?.value === '') return; + navigate(`/roadmap-list/${searchCategory}/${searchWordRef.current?.value}`); }; const resetSearchResult = () => { + if (searchWordRef.current === null) return; + + searchWordRef.current.value = ''; navigate('/roadmap-list'); }; @@ -52,7 +58,7 @@ const RoadmapSearch = () => {

(으)로 검색하기

- + ) => searchRoadmap(e)}> { ref={searchWordRef} /> - + diff --git a/client/src/components/roadmapListPage/roadmapSearch/RoadmapSearchResult.tsx b/client/src/components/roadmapListPage/roadmapSearch/RoadmapSearchResult.tsx index 4382412b1..2f81bdc0a 100644 --- a/client/src/components/roadmapListPage/roadmapSearch/RoadmapSearchResult.tsx +++ b/client/src/components/roadmapListPage/roadmapSearch/RoadmapSearchResult.tsx @@ -4,34 +4,37 @@ import { useSearchRoadmapList } from '@/hooks/queries/roadmap'; import { useInfiniteScroll } from '@/hooks/_common/useInfiniteScroll'; import useValidParams from '@/hooks/_common/useValidParams'; import { useNavigate } from 'react-router-dom'; +import NoResult from './NoResult'; import * as S from './roadmapSearch.styles'; const RoadmapSearchResult = () => { const { category, search } = useValidParams(); - + const navigate = useNavigate(); const { searchRoadmapListResponse, fetchNextPage } = useSearchRoadmapList({ category, search, }); + const { hasNext, responses: roadmapList } = searchRoadmapListResponse; const loadMoreRef = useInfiniteScroll({ - hasNextPage: searchRoadmapListResponse?.hasNext, + hasNextPage: hasNext, fetchNextPage, }); - const navigate = useNavigate(); - const moveRoadmapCreatePage = () => { navigate('/roadmap-create'); }; return ( - - {searchRoadmapListResponse.responses.map((item) => ( - - ))} - {searchRoadmapListResponse?.hasNext && } - + - + <> + {roadmapList.length === 0 && } + + {roadmapList.map((item) => ( + + ))} + {hasNext && } + + + + ); }; diff --git a/client/src/components/roadmapListPage/roadmapSearch/roadmapSearch.styles.ts b/client/src/components/roadmapListPage/roadmapSearch/roadmapSearch.styles.ts index b61f1a4b4..142b511af 100644 --- a/client/src/components/roadmapListPage/roadmapSearch/roadmapSearch.styles.ts +++ b/client/src/components/roadmapListPage/roadmapSearch/roadmapSearch.styles.ts @@ -92,7 +92,7 @@ export const CreateRoadmapButton = styled.button` border-radius: 50%; `; -export const InputFlex = styled.div` +export const InputFlex = styled.form` display: flex; align-items: flex-end; `; @@ -101,3 +101,17 @@ export const ResetSearchButton = styled.button` ${({ theme }) => theme.fonts.description3} color: ${({ theme }) => theme.colors.main_dark}; `; + +export const NoResultWrapper = styled.h1` + ${({ theme }) => theme.fonts.title_large}; + width: 100%; + height: 10rem; + + text-align: center; + + display: flex; + justify-content: center; + align-items: center; + + color: ${({ theme }) => theme.colors.main_dark}; +`; From 552796fe5659a208c10da5c0d51089371e7fc432 Mon Sep 17 00:00:00 2001 From: Jungwoo Date: Sun, 1 Oct 2023 00:09:57 +0900 Subject: [PATCH 20/23] =?UTF-8?q?feat:=20=EB=AA=A8=EC=9E=84=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1,=20=EC=A7=84=ED=96=89=EC=A4=91=EC=9D=B8=20=EB=AA=A8?= =?UTF-8?q?=EC=9E=84=EB=B3=B4=EA=B8=B0=20=EB=B2=84=ED=8A=BC=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../extraInfo/ExtraInfo.styles.ts | 4 ++ .../roadmapDetail/RoadmapDetail.styles.ts | 39 +++++++++++++++++-- .../roadmapDetail/RoadmapDetail.tsx | 13 ++++++- 3 files changed, 51 insertions(+), 5 deletions(-) diff --git a/client/src/components/roadmapDetailPage/extraInfo/ExtraInfo.styles.ts b/client/src/components/roadmapDetailPage/extraInfo/ExtraInfo.styles.ts index 5485af792..98aa0449c 100644 --- a/client/src/components/roadmapDetailPage/extraInfo/ExtraInfo.styles.ts +++ b/client/src/components/roadmapDetailPage/extraInfo/ExtraInfo.styles.ts @@ -41,4 +41,8 @@ export const RecommendedRoadmapPeriod = styled.div` export const Tags = styled.div` color: ${({ theme }) => theme.colors.main_dark}; + + & > div { + text-align: end; + } `; diff --git a/client/src/components/roadmapDetailPage/roadmapDetail/RoadmapDetail.styles.ts b/client/src/components/roadmapDetailPage/roadmapDetail/RoadmapDetail.styles.ts index ce6785c43..93c0b17ce 100644 --- a/client/src/components/roadmapDetailPage/roadmapDetail/RoadmapDetail.styles.ts +++ b/client/src/components/roadmapDetailPage/roadmapDetail/RoadmapDetail.styles.ts @@ -4,9 +4,7 @@ export const RoadmapDetail = styled.div` padding: 2rem 0 4rem 0; `; -export const RoadmapInfo = styled.div` - margin-bottom: 4rem; -`; +export const RoadmapInfo = styled.div``; export const Title = styled.div` ${({ theme }) => theme.fonts.title_large}; @@ -17,3 +15,38 @@ export const Title = styled.div` export const Description = styled.div` display: flex; `; + +export const ButtonsWrapper = styled.div` + position: relative; + width: 100%; +`; + +export const Buttons = styled.div` + bottom: 3rem; + + display: flex; + align-items: center; + justify-content: space-around; + + margin: 2rem 0; + + background-color: ${({ theme }) => theme.colors.main_dark}; + border-radius: 8px; + + & > div { + width: 0.2rem; + height: 5.5rem; + background-color: ${({ theme }) => theme.colors.white}; + } +`; + +export const Button = styled.button` + ${({ theme }) => theme.fonts.nav_text} + width: 50%; + height: 5.5rem; + + color: ${({ theme }) => theme.colors.white}; + + background-color: ${({ theme }) => theme.colors.main_dark}; + border-radius: 8px; +`; diff --git a/client/src/components/roadmapDetailPage/roadmapDetail/RoadmapDetail.tsx b/client/src/components/roadmapDetailPage/roadmapDetail/RoadmapDetail.tsx index 76ee29aec..912425ee5 100644 --- a/client/src/components/roadmapDetailPage/roadmapDetail/RoadmapDetail.tsx +++ b/client/src/components/roadmapDetailPage/roadmapDetail/RoadmapDetail.tsx @@ -1,6 +1,6 @@ import * as S from './RoadmapDetail.styles'; import useValidParams from '@hooks/_common/useValidParams'; -// import { useNavigate } from 'react-router-dom'; +import { useNavigate } from 'react-router-dom'; import { useRoadmapDetail } from '@hooks/queries/roadmap'; import Slider from '@components/_common/slider/Slider'; @@ -10,7 +10,7 @@ import Introduction from '../introduction/Introduction'; const RoadmapDetail = () => { const { id: roadmapId } = useValidParams<{ id: string }>(); - // const navigate = useNavigate(); + const navigate = useNavigate(); const { roadmapInfo } = useRoadmapDetail(Number(roadmapId)); return ( @@ -22,6 +22,15 @@ const RoadmapDetail = () => { + + navigate(`/roadmap/${roadmapId}/goalroom-create`)}> + 모임 생성하기 + +
+ navigate(`/roadmap/${roadmapId}/goalroom-list`)}> + 진행중인 모임보기 + + {roadmapInfo.content.nodes.map((node, index) => ( From 1619ac8573cec39c3a1a9d531344f5ede0c4941d Mon Sep 17 00:00:00 2001 From: Jungwoo Date: Sun, 1 Oct 2023 00:34:45 +0900 Subject: [PATCH 21/23] =?UTF-8?q?chore:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20css=20=EC=BD=94=EB=93=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../roadmapDetailPage/nodeContent/NodeContent.styles.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/client/src/components/roadmapDetailPage/nodeContent/NodeContent.styles.ts b/client/src/components/roadmapDetailPage/nodeContent/NodeContent.styles.ts index 9db8597f8..695e6a26b 100644 --- a/client/src/components/roadmapDetailPage/nodeContent/NodeContent.styles.ts +++ b/client/src/components/roadmapDetailPage/nodeContent/NodeContent.styles.ts @@ -8,17 +8,15 @@ export const SliderContent = styled.div` border-radius: 8px; ${media.mobile` - flex-direction: column; aspect-ratio: 0; `} `; export const LeftContent = styled.div` - flex-shrink: 0; width: 45%; ${media.mobile` - display:none; + display: none; `} `; From 11542954aedba0c8a315f58276a51f57c1c7d9b4 Mon Sep 17 00:00:00 2001 From: NaveOWO <87578512+NaveOWO@users.noreply.github.com> Date: Sun, 1 Oct 2023 04:35:47 +0900 Subject: [PATCH 22/23] =?UTF-8?q?[feat/CK-212]=20=EB=A1=9C=EB=93=9C?= =?UTF-8?q?=EB=A7=B5=20=EC=83=9D=EC=84=B1=ED=8E=98=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?=EC=98=A4=EB=A5=98=EB=A5=BC=20=ED=95=B4=EA=B2=B0=ED=95=9C?= =?UTF-8?q?=EB=8B=A4.=20(#171)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: 400에러시에 요청이 다시 보내지지 않는 오류 해결 * refactor: 로드맵의 첫번째 노드 기본으로 노출 * fix: difficulty 요청이 잘못가는 오류 수정 * fix: tag무한생성 오류 해결 * fix: 중복되는 태그생성 오류 해결 * refactor: ref 예외처리 단순화 --- .../difficulty/Difficulty.tsx | 45 ++++++++++--------- .../roadmapCreateForm/RoadmapCreateForm.tsx | 8 ++++ .../components/roadmapCreatePage/tag/Tag.tsx | 17 +++++-- client/src/constants/roadmap/tag.ts | 2 +- .../hooks/roadmap/useCollectRoadmapData.ts | 10 +++-- client/src/hooks/roadmap/useCreateTag.ts | 19 ++++++-- client/src/myTypes/roadmap/internal.ts | 13 +++++- 7 files changed, 82 insertions(+), 32 deletions(-) diff --git a/client/src/components/roadmapCreatePage/difficulty/Difficulty.tsx b/client/src/components/roadmapCreatePage/difficulty/Difficulty.tsx index 8e6dcdb88..c5b07ce0a 100644 --- a/client/src/components/roadmapCreatePage/difficulty/Difficulty.tsx +++ b/client/src/components/roadmapCreatePage/difficulty/Difficulty.tsx @@ -1,31 +1,32 @@ /* eslint-disable react/no-unused-prop-types */ import { useSelect } from '@/hooks/_common/useSelect'; +import { DifficultiesType, DifficultyKeyType } from '@/myTypes/roadmap/internal'; +import { getInvariantObjectKeys, invariantOf } from '@/utils/_common/invariantType'; import { useEffect } from 'react'; import { Select, SelectBox } from '../selector/SelectBox'; import * as S from './difficulty.styles'; -// 임시 더미데이터 -export type DummyDifficultyType = { - [key: number]: string; +const Difficulties: DifficultiesType = { + VERY_EASY: '매우쉬움', + EASY: '쉬움', + NORMAL: '보통', + DIFFICULT: '어려움', + VERY_DIFFICULT: '매우어려움', }; -const DummyDifficulty: DummyDifficultyType = { - 1: '매우쉬움', - 2: '쉬움', - 3: '보통', - 4: '어려움', - 5: '매우어려움', +type DifficultyProps = { + getSelectedDifficulty: (difficulty: DifficultyKeyType | null) => void; }; -type DifficultyType = { - getSelectedDifficulty: (difficulty: keyof DummyDifficultyType | null) => void; -}; - -const Difficulty = ({ getSelectedDifficulty }: DifficultyType) => { +const Difficulty = ({ getSelectedDifficulty }: DifficultyProps) => { const { selectOption, selectedOption } = useSelect(); useEffect(() => { - getSelectedDifficulty(selectedOption); + if (selectedOption === null) return; + + getSelectedDifficulty( + getInvariantObjectKeys(invariantOf(Difficulties))[selectedOption] + ); }, [selectedOption]); return ( @@ -46,7 +47,11 @@ const Difficulty = ({ getSelectedDifficulty }: DifficultyType) => { {({ selectedId }: { selectedId: number | null }) => { return ( - {selectedId === null ? '선택안함' : DummyDifficulty[selectedId]} + {selectedId === null + ? '선택안함' + : Difficulties[ + getInvariantObjectKeys(invariantOf(Difficulties))[selectedId] + ]} ); }} @@ -55,14 +60,14 @@ const Difficulty = ({ getSelectedDifficulty }: DifficultyType) => { - {Object.keys(DummyDifficulty).map((difficultyId) => { + {getInvariantObjectKeys(invariantOf(Difficulties)).map((difficulty, idx) => { return ( - + - + - {DummyDifficulty[Number(difficultyId)]} + {Difficulties[difficulty]} ); diff --git a/client/src/components/roadmapCreatePage/roadmapCreateForm/RoadmapCreateForm.tsx b/client/src/components/roadmapCreatePage/roadmapCreateForm/RoadmapCreateForm.tsx index a84968ee7..2c11169b6 100644 --- a/client/src/components/roadmapCreatePage/roadmapCreateForm/RoadmapCreateForm.tsx +++ b/client/src/components/roadmapCreatePage/roadmapCreateForm/RoadmapCreateForm.tsx @@ -50,6 +50,14 @@ const RoadmapCreateForm = () => { <> + {roadmapValue.roadmapNodes.length === 0 && ( + + )} {roadmapValue.roadmapNodes.map((_, index) => { return ( void; }; const Tag = ({ getTags }: TagProps) => { - const { tags, ref, addTagByButton, addTagByEnter, checkIsTagCountMax, deleteTag } = - useCreateTag(); + const { + tags, + ref, + addTagByButton, + addTagByEnter, + checkIsTagCountMax, + checkIsAddCountMax, + deleteTag, + } = useCreateTag(); useEffect(() => { getTags(tags); @@ -29,8 +36,10 @@ const Tag = ({ getTags }: TagProps) => { ))} - - {checkIsTagCountMax() && +} + {checkIsTagCountMax() && ( + + )} + {checkIsAddCountMax() && +} ); diff --git a/client/src/constants/roadmap/tag.ts b/client/src/constants/roadmap/tag.ts index 8c4b6e9f2..6662ea4e7 100644 --- a/client/src/constants/roadmap/tag.ts +++ b/client/src/constants/roadmap/tag.ts @@ -1,3 +1,3 @@ -export const TAG_LIMIT = 4; +export const TAG_LIMIT = 5; export const TAG_ITEM_MAX_LENGTH = 10; diff --git a/client/src/hooks/roadmap/useCollectRoadmapData.ts b/client/src/hooks/roadmap/useCollectRoadmapData.ts index 2bfed9fce..d5b1658f1 100644 --- a/client/src/hooks/roadmap/useCollectRoadmapData.ts +++ b/client/src/hooks/roadmap/useCollectRoadmapData.ts @@ -1,6 +1,9 @@ import { DummyCategoryType } from '@/components/roadmapCreatePage/category/Category'; -import { DummyDifficultyType } from '@/components/roadmapCreatePage/difficulty/Difficulty'; -import { NodeImagesType, RoadmapValueType } from '@/myTypes/roadmap/internal'; +import { + DifficultyKeyType, + NodeImagesType, + RoadmapValueType, +} from '@/myTypes/roadmap/internal'; import { getInvariantObjectKeys, invariantOf } from '@/utils/_common/invariantType'; import { useEffect, useState } from 'react'; import { useCreateRoadmap } from '../queries/roadmap'; @@ -27,7 +30,7 @@ export const useCollectRoadmapData = () => { })); }; - const getSelectedDifficulty = (difficulty: keyof DummyDifficultyType | null) => { + const getSelectedDifficulty = (difficulty: DifficultyKeyType | null) => { setRoadmapValue((prev) => ({ ...prev, difficulty, @@ -106,6 +109,7 @@ export const useCollectRoadmapData = () => { if (isSumbited) { createRoadmap(formData); } + setIsSubmited(false); }, [isSumbited]); return { diff --git a/client/src/hooks/roadmap/useCreateTag.ts b/client/src/hooks/roadmap/useCreateTag.ts index 913ec80c8..28ee13847 100644 --- a/client/src/hooks/roadmap/useCreateTag.ts +++ b/client/src/hooks/roadmap/useCreateTag.ts @@ -6,8 +6,9 @@ export const useCreateTag = () => { const ref = useRef(null); const getAddedTagText = () => { - if (ref.current === null) return; - if (ref.current.value === '') return; + if (!ref.current?.value) return; + + if (tags.includes(ref.current.value)) return; setTags((prev) => { return [...prev, ref.current?.value as string]; @@ -31,6 +32,10 @@ export const useCreateTag = () => { return tags.length < TAG_LIMIT; }; + const checkIsAddCountMax = () => { + return tags.length < TAG_LIMIT - 1; + }; + const deleteTag = (e: React.MouseEvent) => { e.preventDefault(); @@ -40,5 +45,13 @@ export const useCreateTag = () => { return prev.filter((tag) => tag !== target.value); }); }; - return { tags, ref, addTagByButton, addTagByEnter, checkIsTagCountMax, deleteTag }; + return { + tags, + ref, + addTagByButton, + addTagByEnter, + checkIsTagCountMax, + checkIsAddCountMax, + deleteTag, + }; }; diff --git a/client/src/myTypes/roadmap/internal.ts b/client/src/myTypes/roadmap/internal.ts index aeb9a0fbe..76aba525a 100644 --- a/client/src/myTypes/roadmap/internal.ts +++ b/client/src/myTypes/roadmap/internal.ts @@ -1,6 +1,17 @@ import { DIFFICULTY_ICON_NAME } from '@constants/roadmap/difficulty'; import { CategoriesInfo } from '@constants/roadmap/category'; +export type DifficultyKeyType = + | 'VERY_EASY' + | 'EASY' + | 'NORMAL' + | 'DIFFICULT' + | 'VERY_DIFFICULT'; + +export type DifficultyValueType = '매우쉬움' | '쉬움' | '보통' | '어려움' | '매우어려움'; + +export type DifficultiesType = { [key in DifficultyKeyType]: DifficultyValueType }; + export type CategoryType = { id: keyof typeof CategoriesInfo; name: string; @@ -64,7 +75,7 @@ export type RoadmapValueType = { title: null | string; introduction: null | string; content: null | string; - difficulty: null | number; + difficulty: null | DifficultyKeyType; requiredPeriod: null | string; roadmapTags: { name: string }[]; roadmapNodes: RoadmapNodes[]; From 0fa6668cb74e82efdcbe867871091616fafb43e5 Mon Sep 17 00:00:00 2001 From: Jungwoo Date: Sun, 1 Oct 2023 12:56:56 +0900 Subject: [PATCH 23/23] =?UTF-8?q?refactor:=20=ED=83=80=EC=9E=85=20?= =?UTF-8?q?=EC=BB=A8=EB=B2=A4=EC=85=98=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../roadmapDetailPage/introduction/Introduction.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/src/components/roadmapDetailPage/introduction/Introduction.tsx b/client/src/components/roadmapDetailPage/introduction/Introduction.tsx index 2fae32dd3..3069efacf 100644 --- a/client/src/components/roadmapDetailPage/introduction/Introduction.tsx +++ b/client/src/components/roadmapDetailPage/introduction/Introduction.tsx @@ -2,11 +2,11 @@ import { RoadmapDetailType } from '@myTypes/roadmap/internal'; import * as S from './Introduction.styles'; import { useEffect, useRef, useState } from 'react'; -type IntroductionType = { +type IntroductionProps = { roadmapInfo: RoadmapDetailType; }; -const Introduction = ({ roadmapInfo }: IntroductionType) => { +const Introduction = ({ roadmapInfo }: IntroductionProps) => { const [isExpanded, setIsExpanded] = useState(false); const [showMoreButton, setShowMoreButton] = useState(false); const introRef = useRef(null); @@ -21,7 +21,7 @@ const Introduction = ({ roadmapInfo }: IntroductionType) => { }, []); const toggleExpand = () => { - setIsExpanded(!isExpanded); + setIsExpanded((prev) => !prev); }; return (