From 560bfd037f5a73048d06428425081fedba5204d3 Mon Sep 17 00:00:00 2001 From: gs0428 Date: Sat, 10 Feb 2024 01:43:03 +0900 Subject: [PATCH] =?UTF-8?q?feature-046:=20=EC=B9=B4=ED=85=8C=EA=B3=A0?= =?UTF-8?q?=EB=A6=AC=20=EC=82=AD=EC=A0=9C,=20=EC=B6=94=EA=B0=80=20API=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 | 18 +++----- src/apis/category.ts | 37 ++++++++++----- src/components/layout/sideBar/SubCategory.tsx | 3 ++ src/components/layout/sideBar/TopCategory.tsx | 4 ++ src/components/layout/sideBar/UserMode.tsx | 35 +++++++-------- src/components/modals/AddCategoryModal.tsx | 23 +++++++--- .../modals/SuccessAddCategoryModal.tsx | 45 ++----------------- src/hooks/useUpdateCategories.ts | 18 ++++++++ src/models/category.ts | 5 +++ src/styles/layout/footer/index.ts | 3 +- src/styles/layout/sideBar/Option.style.ts | 3 +- 11 files changed, 102 insertions(+), 92 deletions(-) create mode 100644 src/hooks/useUpdateCategories.ts create mode 100644 src/models/category.ts diff --git a/src/App.tsx b/src/App.tsx index b91763b..01e2be0 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,4 +1,4 @@ -import { useRecoilValue, useSetRecoilState } from 'recoil'; +import { useRecoilValue } from 'recoil'; import { BrowserRouter, Navigate, Route, Routes } from 'react-router-dom'; import { ThemeProvider } from 'styled-components'; @@ -30,22 +30,14 @@ 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'; +import useUpdateCategories from './hooks/useUpdateCategories'; const App = () => { - const setCategories = useSetRecoilState(categoryState); const userToken = useRecoilValue(userTokenState); - const { initializeCategory } = handleCategory(); + const { updateCategories } = useUpdateCategories(); useEffect(() => { - userToken && - getCategories() - .then((res) => { - setCategories(initializeCategory(res)); - }) - .catch((err) => console.log(err)); - }, [userToken]); + userToken && updateCategories(); + }, [updateCategories, userToken]); return ( diff --git a/src/apis/category.ts b/src/apis/category.ts index 1de5bbe..dfc9cad 100644 --- a/src/apis/category.ts +++ b/src/apis/category.ts @@ -1,9 +1,11 @@ +import { APIResponse } from '@/models/config/axios'; import axiosInstance from './config/instance'; +import { ICreateCategoryResponse } from '@/models/category'; // 모든 카테고리 가져오는 API export const getCategories = async () => { const response = await axiosInstance.get('/category'); - return response.data.result; + return response.data; }; // 카테고리 이동1 API @@ -14,13 +16,13 @@ export const putSubToOtherTop = async ( const response = await axiosInstance.put( `/category/${categoryId}/${topCategoryId}`, ); - return response.data.result; + return response.data; }; // 카테고리 이동2 API export const putSubToTop = async (categoryId: number) => { const response = await axiosInstance.put(`/category/up/${categoryId}`); - return response.data.result; + return response.data; }; // 카테고리 이동3 API @@ -31,17 +33,30 @@ export const putTopToOtherTop = async ( const response = await axiosInstance.put( `/category/down/${categoryId}/${topCategoryId}`, ); - return response.data.result; + return response.data; }; // 상위 카테고리 추가 API -export const postTopCategroy = async () => { - const response = await axiosInstance.post('/category'); - return response.data.result; +export const postTopCategroy = async ( + name: string, +): Promise> => { + const response = await axiosInstance.post('/category', { name }); + return response.data; }; -// 상위 카테고리 추가 API -export const postSubCategroy = async (topCategoryId: number) => { - const response = await axiosInstance.post(`/category/${topCategoryId}`); - return response.data.result; +// 하위 카테고리 추가 API +export const postSubCategroy = async ( + name: string, + topCategoryId: number, +): Promise> => { + const response = await axiosInstance.post(`/category/${topCategoryId}`, { + name, + }); + return response.data; +}; + +// 카테고리 삭제 API +export const deleteCategory = async (category_id: number) => { + const response = await axiosInstance.delete(`/category/${category_id}`); + return response.data; }; diff --git a/src/components/layout/sideBar/SubCategory.tsx b/src/components/layout/sideBar/SubCategory.tsx index e3fd88f..eb87ba9 100644 --- a/src/components/layout/sideBar/SubCategory.tsx +++ b/src/components/layout/sideBar/SubCategory.tsx @@ -15,6 +15,7 @@ interface ISubCategoryProps { setIsDeleteModalOpen: React.Dispatch>; grabedCategory: React.MutableRefObject; putCategoryFolder: () => void; + setCategoryId: React.Dispatch>; } const SubCategory = ({ @@ -25,6 +26,7 @@ const SubCategory = ({ setIsDeleteModalOpen, grabedCategory, putCategoryFolder, + setCategoryId, }: ISubCategoryProps) => { const [subFolderOptionModalOpen, setSubFolderOptionModalOpen] = useState(false); @@ -45,6 +47,7 @@ const SubCategory = ({ if (option === '수정') { setIsEditing(true); } else if (option === '삭제') { + setCategoryId(categoryId); setIsDeleteModalOpen(true); } setSubFolderOptionModalOpen(false); diff --git a/src/components/layout/sideBar/TopCategory.tsx b/src/components/layout/sideBar/TopCategory.tsx index 7b7145e..aa0d47e 100644 --- a/src/components/layout/sideBar/TopCategory.tsx +++ b/src/components/layout/sideBar/TopCategory.tsx @@ -21,6 +21,7 @@ interface ITopCategoryProps { dropedCategory: React.MutableRefObject; setIsSubCategoryModalOpen: React.Dispatch>; setIsDeleteModalOpen: React.Dispatch>; + setCategoryId: React.Dispatch>; putCategoryFolder: () => void; } @@ -36,6 +37,7 @@ const TopCategory = ({ setIsSubCategoryModalOpen, setIsDeleteModalOpen, putCategoryFolder, + setCategoryId, }: ITopCategoryProps) => { const [folderOptionModalOpen, setFolderOptionModalOpen] = useState(false); const [folderOptionModalRef] = useOutsideClick(() => @@ -66,6 +68,7 @@ const TopCategory = ({ setIsEditing(true); setBeforeEdit(edit); } else if (option === '삭제') { + setCategoryId(categoryId); setIsDeleteModalOpen(true); } setFolderOptionModalOpen(false); @@ -176,6 +179,7 @@ const TopCategory = ({ setIsDeleteModalOpen={setIsDeleteModalOpen} grabedCategory={grabedCategory} putCategoryFolder={putCategoryFolder} + setCategoryId={setCategoryId} key={`${subFolder.name}-${subFolder.categoryId}`} /> ))} diff --git a/src/components/layout/sideBar/UserMode.tsx b/src/components/layout/sideBar/UserMode.tsx index be82a49..9b5fe42 100644 --- a/src/components/layout/sideBar/UserMode.tsx +++ b/src/components/layout/sideBar/UserMode.tsx @@ -1,30 +1,33 @@ import LookSvg from '@/assets/icons/look.svg?react'; import * as UserModeStyle from '@/styles/layout/sideBar/UserMode.style'; -import { useLocation } from 'react-router-dom'; -import { useRecoilState, useRecoilValue } from 'recoil'; +import { useLocation, useNavigate } from 'react-router-dom'; +import { useRecoilValue } from 'recoil'; import { topCategoryModalState } from '@/stores/modal'; import AddCategoryModal from '@/components/modals/AddCategoryModal'; import SuccessAddCategoryModal from '@/components/modals/SuccessAddCategoryModal'; import { useRef, useState } from 'react'; import TopCategory from './TopCategory'; import DeleteCategory from './DeleteCategory'; -import handleCategory from '@/utils/handleCategory'; import { categoryState } from '@/stores/category'; import { IFolderProps, ISubFolderProps } from 'types/category'; import useMoveCategory from '@/hooks/useMoveCategory'; +import { deleteCategory } from '@/apis/category'; +import useUpdateCategories from '@/hooks/useUpdateCategories'; const UserMode = () => { const isTopCategoryModalOpen = useRecoilValue(topCategoryModalState); const [isSuccessAddCategoryModalOpen, setIsSuccessAddCategoryModalOpen] = useState(false); const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false); - const [isSubAdded, setIsSubAdded] = useState(false); const [categoryName, setCategoryName] = useState(''); - const [categories, setCategories] = useRecoilState(categoryState); + const [categoryId, setCategoryId] = useState(null); + const categories = useRecoilValue(categoryState); const [isSubCategoryModalOpen, setIsSubCategoryModalOpen] = useState(false); const grabedCategory = useRef(undefined); const dropedCategory = useRef(undefined); - const { deleteSubCategory } = handleCategory(); + const navigate = useNavigate(); + + const { updateCategories } = useUpdateCategories(); const { subToOtherTop, subToTop, topToOtherTop } = useMoveCategory(); @@ -34,14 +37,11 @@ const UserMode = () => { const topId = Number(href[0]); const subId = Number(href[1]); - const handleDeleteCategory = () => { - if (!isNaN(subId)) { - setCategories([...deleteSubCategory(categories, topId, subId)]); - } else { - const newData = categories.filter( - (myFolder) => myFolder.categoryId !== topId, - ); - setCategories([...newData]); + const handleDeleteCategory = async () => { + const response = await deleteCategory(categoryId!); + if (response.isSuccess) { + updateCategories(); + navigate('/category/recent'); } setIsDeleteModalOpen(false); }; @@ -85,6 +85,7 @@ const UserMode = () => { setIsSubCategoryModalOpen={setIsSubCategoryModalOpen} setIsDeleteModalOpen={setIsDeleteModalOpen} putCategoryFolder={putCategoryFolder} + setCategoryId={setCategoryId} key={`${category.name}-${category.categoryId}`} /> ))} @@ -96,8 +97,8 @@ const UserMode = () => { categoryName={categoryName} setCategoryName={setCategoryName} setIsSuccessAddCategoryModalOpen={setIsSuccessAddCategoryModalOpen} - setIsSubAdded={setIsSubAdded} topCategoryId={topId} + setCategoryId={setCategoryId} /> )} {isDeleteModalOpen && ( @@ -111,9 +112,7 @@ const UserMode = () => { categoryName={categoryName} setCategoryName={setCategoryName} setIsSuccessAddCategoryModalOpen={setIsSuccessAddCategoryModalOpen} - isSubAdded={isSubAdded} - setIsSubAdded={setIsSubAdded} - topId={topId} + categoryId={categoryId} /> )} diff --git a/src/components/modals/AddCategoryModal.tsx b/src/components/modals/AddCategoryModal.tsx index 4e934e0..e69219f 100644 --- a/src/components/modals/AddCategoryModal.tsx +++ b/src/components/modals/AddCategoryModal.tsx @@ -13,12 +13,13 @@ import { import { ICommonModalProps } from 'types/modal'; import handleEdit from '@/utils/handleEdit'; import { postSubCategroy, postTopCategroy } from '@/apis/category'; +import useUpdateCategories from '@/hooks/useUpdateCategories'; interface IAddTopCategoryModalProps extends ICommonModalProps { isTopCategoryModalOpen: boolean; setIsSubCategoryModalOpen: React.Dispatch>; - setIsSubAdded: React.Dispatch>; topCategoryId: number; + setCategoryId: React.Dispatch>; } const AddCategoryModal = ({ @@ -27,10 +28,11 @@ const AddCategoryModal = ({ categoryName, setCategoryName, setIsSuccessAddCategoryModalOpen, - setIsSubAdded, topCategoryId, + setCategoryId, }: IAddTopCategoryModalProps) => { const setIsTopCategoryModalOpen = useSetRecoilState(topCategoryModalState); + const { updateCategories } = useUpdateCategories(); const [isFocused, setIsFocused] = useState(false); @@ -42,7 +44,6 @@ const AddCategoryModal = ({ isTopCategoryModalOpen ? setIsTopCategoryModalOpen(false) : setIsSubCategoryModalOpen(false); - !isTopCategoryModalOpen && setIsSubAdded(true); }; const [topCategoryModalRef] = useOutsideClick(onCloseModal); @@ -50,11 +51,21 @@ const AddCategoryModal = ({ const handleInputCategoryName = (e: React.ChangeEvent) => handleEdit(e, setCategoryName); - const addCategory = (e: React.MouseEvent) => { - isTopCategoryModalOpen ? postTopCategroy() : postSubCategroy(topCategoryId); + const addCategory = async (e: React.MouseEvent) => { + const response = isTopCategoryModalOpen + ? await postTopCategroy(categoryName) + : await postSubCategroy(categoryName, topCategoryId); + if (response.isSuccess) { + updateCategories(); + setCategoryId( + isTopCategoryModalOpen + ? response.result.categoryId + : response.result.topCategoryId, + ); + setIsSuccessAddCategoryModalOpen(true); + } e.stopPropagation(); onCloseModal(); - setIsSuccessAddCategoryModalOpen(true); }; return ( diff --git a/src/components/modals/SuccessAddCategoryModal.tsx b/src/components/modals/SuccessAddCategoryModal.tsx index 02c1f7c..b3d12c2 100644 --- a/src/components/modals/SuccessAddCategoryModal.tsx +++ b/src/components/modals/SuccessAddCategoryModal.tsx @@ -7,58 +7,25 @@ import { import CloseSvg from '@/assets/icons/close.svg?react'; import * as SuccessAddCategoryStyles from '@/styles/modals/SuccessAddCategoryModal.style'; import { ICommonModalProps } from 'types/modal'; -import { useRecoilState } from 'recoil'; -import { categoryState } from '@/stores/category'; - import FileImage from '@/assets/file.png'; interface ISuccessAddCategory extends ICommonModalProps { - isSubAdded: boolean; - setIsSubAdded: React.Dispatch>; - topId: number; + categoryId: number | null; } const SuccessAddCategoryModal = ({ categoryName, setCategoryName, setIsSuccessAddCategoryModalOpen, - isSubAdded, - setIsSubAdded, - topId, + categoryId, }: ISuccessAddCategory) => { const onCloseModal = () => { setIsSuccessAddCategoryModalOpen(false); setCategoryName(''); }; - const [categories, setCategories] = useRecoilState(categoryState); const [successAddCategoryModalRef] = useOutsideClick(onCloseModal); - - const handleGoToCategory = () => { - if (isSubAdded) { - const index = categories.findIndex( - (folder) => folder.categoryId === topId, - ); - categories[index].subFolders.push({ - name: categoryName, - categoryId: categories[index].categoryId, - topCategoryId: topId, - }); - } else { - setCategories([ - ...categories, - { - categoryId: categories.length + 1, - name: categoryName, - topCategoryId: null, - subFolders: [], - }, - ]); - } - setIsSubAdded(false); - onCloseModal(); - }; return ( @@ -73,12 +40,8 @@ const SuccessAddCategoryModal = ({ 생성 완료! 보러가기 diff --git a/src/hooks/useUpdateCategories.ts b/src/hooks/useUpdateCategories.ts new file mode 100644 index 0000000..50e8fd9 --- /dev/null +++ b/src/hooks/useUpdateCategories.ts @@ -0,0 +1,18 @@ +import { getCategories } from '@/apis/category'; +import { categoryState } from '@/stores/category'; +import handleCategory from '@/utils/handleCategory'; +import { useSetRecoilState } from 'recoil'; + +const useUpdateCategories = () => { + const setCategories = useSetRecoilState(categoryState); + const { initializeCategory } = handleCategory(); + const updateCategories = async () => { + await getCategories() + .then((res) => setCategories(initializeCategory(res.result))) + .catch((err) => console.log(err)); + }; + + return { updateCategories }; +}; + +export default useUpdateCategories; diff --git a/src/models/category.ts b/src/models/category.ts new file mode 100644 index 0000000..802adf4 --- /dev/null +++ b/src/models/category.ts @@ -0,0 +1,5 @@ +export interface ICreateCategoryResponse { + topCategoryId: number | null; + categoryId: number; + name: string; +} diff --git a/src/styles/layout/footer/index.ts b/src/styles/layout/footer/index.ts index e2a6ea3..52b87e6 100644 --- a/src/styles/layout/footer/index.ts +++ b/src/styles/layout/footer/index.ts @@ -6,7 +6,7 @@ export const Container = styled.footer` background-color: ${theme.color.gray100}; ${theme.typography.Body1}; position: relative; - z-index: 1; + z-index: -1; `; export const SendEmailWrap = styled.div` @@ -38,7 +38,6 @@ export const SendEmailInput = styled.input` `; export const SendEmailButton = styled.button` - /* padding: 7px 28px; */ width: 98px; height: 40px; border-radius: 8px; diff --git a/src/styles/layout/sideBar/Option.style.ts b/src/styles/layout/sideBar/Option.style.ts index 0b48db9..2adfb59 100644 --- a/src/styles/layout/sideBar/Option.style.ts +++ b/src/styles/layout/sideBar/Option.style.ts @@ -21,7 +21,8 @@ export const OptionsWrap = styled.div` export const OptionButton = styled.button` cursor: pointer; background-color: ${theme.color.white}; - padding: 12px 71.25px; + padding: 12px 0; + width: 167.5px; border: 0; color: ${theme.color.gray400}; ${theme.typography.Body3}