From 3b0a4fe4c8ec7aa0e078cb873bb85461ad3bd741 Mon Sep 17 00:00:00 2001 From: gs0428 Date: Mon, 5 Feb 2024 02:44:59 +0900 Subject: [PATCH 01/11] =?UTF-8?q?feature-046:=20Footer=20=EB=94=94?= =?UTF-8?q?=EC=9E=90=EC=9D=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/styles/layout/footer/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/styles/layout/footer/index.ts b/src/styles/layout/footer/index.ts index 88b708a..4adbefd 100644 --- a/src/styles/layout/footer/index.ts +++ b/src/styles/layout/footer/index.ts @@ -28,7 +28,7 @@ export const SendEmailInput = styled.input` outline: none; margin: 0px 20px; color: ${theme.color.gray500}; - ${theme.typography.Subheader2}; + ${theme.typography.Body1}; &::placeholder { color: ${theme.color.gray300}; @@ -52,7 +52,7 @@ export const AboutViNOWrap = styled.div` `; export const AboutViNO = styled.p` - color: ${theme.color.gray400}; + color: ${theme.color.gray300}; margin-right: 40px; `; From 569c9b9724601d245f36cb9fdc07c0a912d3ff23 Mon Sep 17 00:00:00 2001 From: gs0428 Date: Mon, 5 Feb 2024 02:48:33 +0900 Subject: [PATCH 02/11] =?UTF-8?q?feature-046:=20side=20bar=20sticky=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/layout/sideBar/index.tsx | 12 +++++++----- src/styles/layout/sideBar/index.ts | 5 +++++ 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/components/layout/sideBar/index.tsx b/src/components/layout/sideBar/index.tsx index b424303..72fcc44 100644 --- a/src/components/layout/sideBar/index.tsx +++ b/src/components/layout/sideBar/index.tsx @@ -1,4 +1,4 @@ -import * as SideBarStyle from '@/styles/layout/sideBar'; +import * as SideBarStyles from '@/styles/layout/sideBar'; import { useRecoilValue } from 'recoil'; import { userState } from '@/stores/user'; import GuestMode from './GuestMode'; @@ -11,12 +11,14 @@ const SideBar = () => { const isUser = useRecoilValue(userState); return ( - + - - {isUser ? : } - + + + {isUser ? : } + + ); }; diff --git a/src/styles/layout/sideBar/index.ts b/src/styles/layout/sideBar/index.ts index 4b06700..36feb45 100644 --- a/src/styles/layout/sideBar/index.ts +++ b/src/styles/layout/sideBar/index.ts @@ -8,6 +8,11 @@ export const Container = styled.div` width: 288px; `; +export const StickySection = styled.div` + position: sticky; + top: 72.39; +`; + export const CommonWrapStyle = styled.div` display: flex; align-items: center; From 858d4ae2105a2a3b7706d795670352f1de0353aa Mon Sep 17 00:00:00 2001 From: gs0428 Date: Mon, 5 Feb 2024 03:00:27 +0900 Subject: [PATCH 03/11] =?UTF-8?q?feature-046:=20=EC=82=AC=EC=9D=B4?= =?UTF-8?q?=EB=93=9C=20=EB=B0=94=20=EA=B7=B8=EB=A6=BC=EC=9E=90=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/styles/layout/footer/index.ts | 2 ++ src/styles/layout/sideBar/index.ts | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/styles/layout/footer/index.ts b/src/styles/layout/footer/index.ts index 4adbefd..e2a6ea3 100644 --- a/src/styles/layout/footer/index.ts +++ b/src/styles/layout/footer/index.ts @@ -5,6 +5,8 @@ export const Container = styled.footer` padding: 60px 145px; background-color: ${theme.color.gray100}; ${theme.typography.Body1}; + position: relative; + z-index: 1; `; export const SendEmailWrap = styled.div` diff --git a/src/styles/layout/sideBar/index.ts b/src/styles/layout/sideBar/index.ts index 36feb45..be25c00 100644 --- a/src/styles/layout/sideBar/index.ts +++ b/src/styles/layout/sideBar/index.ts @@ -6,11 +6,13 @@ export const Container = styled.div` flex-direction: column; margin: 60px 0px 0px 60px; width: 288px; + box-shadow: 4px 0px 40px 0px rgba(0, 0, 0, 0.05); + z-index: 0; `; export const StickySection = styled.div` position: sticky; - top: 72.39; + top: 72px; `; export const CommonWrapStyle = styled.div` From f67383e648cc96299ead966f2de2bf819f8243d9 Mon Sep 17 00:00:00 2001 From: gs0428 Date: Thu, 8 Feb 2024 22:34:50 +0900 Subject: [PATCH 04/11] =?UTF-8?q?feature-046:=20=EC=B9=B4=ED=85=8C?= =?UTF-8?q?=EA=B3=A0=EB=A6=AC=20API=20=EC=97=B0=EB=8F=99=20=EB=B0=8F=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99=ED=95=98=EB=8A=94=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.tsx | 15 +- src/apis/category.ts | 6 + src/components/category/Card.tsx | 123 ++++++++----- src/components/category/CategoryTitle.tsx | 6 +- src/components/category/EmptyCard.tsx | 21 +++ src/components/layout/sideBar/SubCategory.tsx | 20 +-- src/components/layout/sideBar/TopCategory.tsx | 38 ++-- src/components/layout/sideBar/UserMode.tsx | 128 +++---------- .../modals/SuccessAddCategoryModal.tsx | 30 ++-- src/hooks/useMoveCategory.ts | 83 +++++++++ src/pages/CategoryPage.tsx | 169 +++++++++++------- src/stores/category.ts | 7 + src/styles/category/EmptyCard.style.ts | 28 +++ src/styles/category/index.style.ts | 75 ++++---- src/utils/handleCategory.ts | 46 +++-- types/category.ts | 12 ++ 16 files changed, 492 insertions(+), 315 deletions(-) create mode 100644 src/apis/category.ts create mode 100644 src/components/category/EmptyCard.tsx create mode 100644 src/hooks/useMoveCategory.ts create mode 100644 src/stores/category.ts create mode 100644 src/styles/category/EmptyCard.style.ts create mode 100644 types/category.ts diff --git a/src/App.tsx b/src/App.tsx index 5342b8b..1cdf285 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,4 +1,4 @@ -import { useRecoilValue } from 'recoil'; +import { useRecoilValue, useSetRecoilState } from 'recoil'; import { BrowserRouter, Navigate, Route, Routes } from 'react-router-dom'; import { ThemeProvider } from 'styled-components'; @@ -25,9 +25,22 @@ import { ToastList } from './components/common'; // Store import { userTokenState } from './stores/user'; +import { useEffect } from 'react'; +import { categoryState } from './stores/category'; +import { getCategories } from './apis/category'; +import handleCategory from './utils/handleCategory'; const App = () => { + const setCategories = useSetRecoilState(categoryState); const userToken = useRecoilValue(userTokenState); + const { initializeCategory } = handleCategory(); + useEffect(() => { + getCategories() + .then((res) => { + setCategories(initializeCategory(res)); + }) + .catch((err) => console.log(err)); + }, []); return ( diff --git a/src/apis/category.ts b/src/apis/category.ts new file mode 100644 index 0000000..18014ec --- /dev/null +++ b/src/apis/category.ts @@ -0,0 +1,6 @@ +import axiosInstance from './config/instance'; + +export const getCategories = async () => { + const response = await axiosInstance.get('/category'); + return response.data.result; +}; diff --git a/src/components/category/Card.tsx b/src/components/category/Card.tsx index cb8a449..9f43cfe 100644 --- a/src/components/category/Card.tsx +++ b/src/components/category/Card.tsx @@ -2,73 +2,99 @@ import React, { useEffect, useState } from 'react'; import VideoTag from '../common/videoTag'; import * as CardStyles from '@/styles/category/Card.style'; -interface cardDummy { - imageURL: string; +interface IVideoProps { + video_id: number; + category_id: number; title: string; - summary: string; - tags: string[]; + description: string; + image: string; + link: string; + created_at: string; + youtube_created_at: string; + tag: [{ name: string }]; } -interface CardInputProps { - categoryItems : Array; - checkedItems : boolean[]; - setCheckedItems : (value : boolean[]) => void; +interface ICardProps { + videos: IVideoProps[]; + checkedVideos: boolean[]; + setCheckedVideos: (value: boolean[]) => void; } -const Card : React.FC = ({ categoryItems, checkedItems, setCheckedItems}) => { - const [isShadow,setIsShadow] = useState(new Array(6).fill(false)); - +const Card: React.FC = ({ + videos, + checkedVideos, + setCheckedVideos, +}) => { + const [isShadow, setIsShadow] = useState(new Array(6).fill(false)); + useEffect(() => { - if(checkedItems.includes(true)){ // 1개 이상 클릭 시 모든 hover event 활성화 - setIsShadow(isShadow.map(() => true)) - } else if(!isShadow.includes(false)){ + if (checkedVideos.includes(true)) { + // 1개 이상 클릭 시 모든 hover event 활성화 + setIsShadow(isShadow.map(() => true)); + } else if (!isShadow.includes(false)) { //모든 hover 활성화, 모든 체크 비활성화 시 모든 hover 활성화 제거 - setIsShadow(isShadow.map(() => false)) + setIsShadow(isShadow.map(() => false)); } - }, [checkedItems]) + }, [checkedVideos]); - const handleMouseEnter = (id : number) => { - let prev = checkedItems.includes(true) ? [...isShadow] : new Array(isShadow.length).fill(false); - // 체크박스 미선택 이동 시 isshadow 중복 작동으로 인해 방식 변경 - prev[id] = true; - setIsShadow(prev); - } + const handleMouseEnter = (id: number) => { + const prev = checkedVideos.includes(true) + ? [...isShadow] + : new Array(isShadow.length).fill(false); + // 체크박스 미선택 이동 시 isshadow 중복 작동으로 인해 방식 변경 + prev[id] = true; + setIsShadow(prev); + }; - const handleMouseLeave = (id : number) => { - if(!checkedItems.includes(true)){ // 선택되면 유지 - let prev = [...isShadow]; - prev[id] = false; - setIsShadow(prev); + const handleMouseLeave = (id: number) => { + if (!checkedVideos.includes(true)) { + // 선택되면 유지 + const prev = [...isShadow]; + prev[id] = false; + setIsShadow(prev); } - } + }; - const checkBoxHandler = (id : number) => { - let prev = [...checkedItems]; - prev[id] = !prev[id]; - setCheckedItems(prev); - } - + const checkBoxHandler = (id: number) => { + const prev = [...checkedVideos]; + prev[id] = !prev[id]; + setCheckedVideos(prev); + }; + console.log(videos); return ( - {categoryItems.map((categoryItem, idx) => ( - handleMouseEnter(idx)} onMouseLeave={() => handleMouseLeave(idx)}> - 썸네일 이미지 - {isShadow[idx] && checkBoxHandler(idx)}/>} - - - {categoryItem.title} + {videos.map((video, idx) => ( + handleMouseEnter(idx)} + onMouseLeave={() => handleMouseLeave(idx)} + > + 썸네일 이미지 + {isShadow[idx] && ( + checkBoxHandler(idx)} + /> + )} + + + {video.title} - - {categoryItem.summary} + + {video.description} - - {categoryItem.tags.map((tag) => ( + + {video.tag.map((tag) => ( ))} @@ -80,4 +106,3 @@ const Card : React.FC = ({ categoryItems, checkedItems, setCheck }; export default Card; - diff --git a/src/components/category/CategoryTitle.tsx b/src/components/category/CategoryTitle.tsx index 84f34cf..f049bae 100644 --- a/src/components/category/CategoryTitle.tsx +++ b/src/components/category/CategoryTitle.tsx @@ -1,14 +1,14 @@ import * as CategoryTitleStyles from '@/styles/category/CategoryTitle.style'; interface ICategoryTitleProps { - title: string; + name: string; totalVideos: number; } -const CategoryTitle = ({ title, totalVideos }: ICategoryTitleProps) => { +const CategoryTitle = ({ name, totalVideos }: ICategoryTitleProps) => { return ( - {title} + {name} {totalVideos} ); diff --git a/src/components/category/EmptyCard.tsx b/src/components/category/EmptyCard.tsx new file mode 100644 index 0000000..1278d61 --- /dev/null +++ b/src/components/category/EmptyCard.tsx @@ -0,0 +1,21 @@ +import EmptyFile from '@/assets/empty-file.png'; +import * as EmptyCardStyles from '@/styles/category/EmptyCard.style'; + +const EmptyCard = () => { + return ( + + 비어있는 폴더 + + + 아직 관련 영상이 없어요! + + + 관련 영상들을 모아보세요 + + + 영상 정리해보기 + + ); +}; + +export default EmptyCard; diff --git a/src/components/layout/sideBar/SubCategory.tsx b/src/components/layout/sideBar/SubCategory.tsx index a1a027e..e3fd88f 100644 --- a/src/components/layout/sideBar/SubCategory.tsx +++ b/src/components/layout/sideBar/SubCategory.tsx @@ -4,13 +4,13 @@ import { useState } from 'react'; import * as SubCategoryStyles from '@/styles/layout/sideBar/SubCategory.style'; import Option from './Option'; import handleEdit from '@/utils/handleEdit'; -import { ISubFolderProps } from './UserMode'; import handleDrag from '@/utils/handleDrag'; +import { ISubFolderProps } from 'types/category'; interface ISubCategoryProps { topId: number; subId: number; - categoryID: number; + categoryId: number; name: string; setIsDeleteModalOpen: React.Dispatch>; grabedCategory: React.MutableRefObject; @@ -20,7 +20,7 @@ interface ISubCategoryProps { const SubCategory = ({ topId, subId, - categoryID, + categoryId, name, setIsDeleteModalOpen, grabedCategory, @@ -61,14 +61,14 @@ const SubCategory = ({ const handleDragStart = () => (grabedCategory.current = { - categoryID: categoryID, + categoryId: categoryId, name, - topCategoryID: topId, + topCategoryId: topId, }); const handleDropZoneDragEnter = (e: React.DragEvent) => { dragEnter(e); - if (grabedCategory.current?.topCategoryID === null) return; + if (grabedCategory.current?.topCategoryId === null) return; }; return (
{edit} - {subId === categoryID && ( + {subId === categoryId && ( )} - {subFolderOptionModalOpen && subId === categoryID && ( + {subFolderOptionModalOpen && subId === categoryId && (