diff --git a/src/apis/category.ts b/src/apis/category.ts index be348b5..62479aa 100644 --- a/src/apis/category.ts +++ b/src/apis/category.ts @@ -8,6 +8,12 @@ export const getCategories = async () => { return response.data; }; +// 카테고리 별 태그 가져오는 API +export const getCategoryTags = async (categoryId: string) => { + const response = await axiosInstance.get(`/category/${categoryId}/`); + return response.data; +}; + // 카테고리 이동1 API export const putSubToOtherTop = async ( categoryId: number, diff --git a/src/apis/videos.ts b/src/apis/videos.ts index 06bf8b4..83393b9 100644 --- a/src/apis/videos.ts +++ b/src/apis/videos.ts @@ -29,7 +29,7 @@ export const getRecentVideos = async (): Promise< }; export const getVideoById = async ( - videoId: number, + videoId: string, ): Promise>> => { const response = await axiosInstance.get(`/videos/${videoId}/get`); return response.data; diff --git a/src/components/category/Card.tsx b/src/components/category/Card.tsx index f4302f0..4b38c5a 100644 --- a/src/components/category/Card.tsx +++ b/src/components/category/Card.tsx @@ -7,6 +7,7 @@ import { CategorySelectBox } from '@/components/SummaryPage/SummaryDetailBox/Cat import { categoryState } from '@/stores/category'; import * as CardStyles from '@/styles/category/Card.style'; +import Chip from '../common/chip/Chip'; interface ICardProps { mode: 'default' | 'category' | 'recommend'; @@ -65,7 +66,7 @@ const Card: React.FC = ({ {video.description} {video.tag.map((tag) => ( - {`# ${tag.name}`} + ))} diff --git a/src/components/common/chip/Chip.style.ts b/src/components/common/chip/Chip.style.ts new file mode 100644 index 0000000..891e912 --- /dev/null +++ b/src/components/common/chip/Chip.style.ts @@ -0,0 +1,23 @@ +import theme from '@/styles/theme'; +import styled from 'styled-components'; + +export const ChipContainer = styled.div` + cursor: pointer; + margin-right: 18px; + margin-bottom: 18px; + padding: 3px 9.5px; + background-color: ${theme.color.gray100}; + border-radius: 8px; + color: ${theme.color.gray400}; + ${theme.typography.Caption1}; + + &.light { + border: 1px solid ${theme.color.gray200}; + background-color: ${theme.color.white}; + } + + &.selected { + border-color: ${theme.color.gray300}; + background-color: ${theme.color.gray100}; + } +`; diff --git a/src/components/common/chip/Chip.tsx b/src/components/common/chip/Chip.tsx new file mode 100644 index 0000000..02a224c --- /dev/null +++ b/src/components/common/chip/Chip.tsx @@ -0,0 +1,19 @@ +import { ChipContainer } from './Chip.style'; + +interface IChipProps { + name: string; + light?: boolean; + selected?: boolean; + onSelectTag?: (name: string) => void; +} + +const Chip = ({ name, light, selected, onSelectTag }: IChipProps) => { + return ( + onSelectTag && onSelectTag(name)} + >{`# ${name}`} + ); +}; + +export default Chip; diff --git a/src/pages/CategoryPage.tsx b/src/pages/CategoryPage.tsx index 1f3615c..221a35f 100644 --- a/src/pages/CategoryPage.tsx +++ b/src/pages/CategoryPage.tsx @@ -9,27 +9,38 @@ import * as CategoryPageStyles from '@/styles/category/index.style'; import Card from '@/components/category/Card'; import { useRecoilValue } from 'recoil'; import { categoryState } from '@/stores/category'; -import { ISubFolderProps } from 'types/category'; +import { ISubFolderProps, ITagProps } from 'types/category'; import EmptyCard from '@/components/category/EmptyCard'; import { deleteVideos, getRecentVideos, getVideoById } from '@/apis/videos'; import { IVideoProps } from 'types/videos'; import { sortVideos } from '@/utils/sortVideos'; import { CardContainer } from '@/styles/category/Card.style'; import { CategorySelectBox } from '@/components/SummaryPage/SummaryDetailBox/CategorySelectBox'; +import Chip from '@/components/common/chip/Chip'; +import { getCategoryTags } from '@/apis/category'; const CategoryPage = () => { const params = useParams(); const [name, setName] = useState(''); - const [menus, setMenus] = useState([]); + const [menus, setMenus] = useState([]); const [videos, setVideos] = useState([]); const [recentRegisterMode, setRecentRegisterMode] = useState(false); const [checkedVideos, setCheckedVideos] = useState([]); + const [selectedTags, setSelectedTags] = useState([]); const categories = useRecoilValue(categoryState); const [selectedCategoryId, setSelectedCategoryId] = useState( categories.length ? categories[0].categoryId : -1, ); + const onSelectTag = (name: string) => { + if (selectedTags.includes(name)) { + setSelectedTags(selectedTags.filter((tag) => tag !== name)); + } else { + setSelectedTags([...selectedTags, name]); + } + }; + const handleSelectCategory = (categoryId: number) => { setSelectedCategoryId(categoryId); }; @@ -45,33 +56,31 @@ const CategoryPage = () => { .then((res) => { setVideos(res.result.videos); setName('최근 읽은 영상'); + setMenus([]); }) .catch((err) => console.log(err)); } else if (!params.sub_folder) { - getVideoById(Number(params.top_folder)).then((res) => { + getVideoById(params.top_folder).then((res) => { const index = categories.findIndex( (category) => category.categoryId === Number(params.top_folder), ); - if (!res.isSuccess) { - setName(categories[index].name); - setMenus([]); - setVideos([]); - return; - } setName(categories[index].name); setMenus(categories[index].subFolders); setVideos(res.isSuccess ? res.result.videos : []); }); } else { - getVideoById(Number(params.sub_folder)).then((res) => { + getVideoById(params.sub_folder).then((res) => { const index = categories.findIndex( (category) => category.categoryId === Number(params.top_folder), ); const subIndex = categories[index].subFolders.findIndex( (subFolder) => subFolder.categoryId === Number(params.sub_folder), ); + getCategoryTags(params.sub_folder!).then((res) => + setMenus(res.result.tags), + ); setName(categories[index].subFolders[subIndex].name); - setMenus([]); + setVideos(res.isSuccess ? res.result.videos : []); }); } @@ -147,14 +156,27 @@ const CategoryPage = () => { ) : ( <> -
- {menus.map((menu) => ( - - {menu.name} - +
+ {menus.map((menu: ISubFolderProps | ITagProps) => ( + <> + {'tag_id' in menu && ( + + )} + {!('tag_id' in menu) && ( + + {menu.name} + + )} + ))}
diff --git a/src/styles/category/Card.style.ts b/src/styles/category/Card.style.ts index 33760df..32a6263 100644 --- a/src/styles/category/Card.style.ts +++ b/src/styles/category/Card.style.ts @@ -143,13 +143,3 @@ export const DropdownWrap = styled.div` } } `; - -export const Chip = styled.div` - margin-right: 18px; - margin-bottom: 18px; - padding: 3px 9.5px; - background-color: ${theme.color.gray100}; - border-radius: 8px; - color: ${theme.color.gray400}; - ${theme.typography.Caption1}; -`; diff --git a/types/category.ts b/types/category.ts index 760296a..ac6a12b 100644 --- a/types/category.ts +++ b/types/category.ts @@ -15,3 +15,8 @@ export interface ISelectedCategoryProps { name: string; categoryId: number; } + +export interface ITagProps { + tag_id: number; + name: string; +}