diff --git a/src/Home/api/getLecueBook.ts b/src/Home/api/getLecueBook.ts index 336d2a2b..28a38c07 100644 --- a/src/Home/api/getLecueBook.ts +++ b/src/Home/api/getLecueBook.ts @@ -2,7 +2,7 @@ import { api } from '../../libs/api'; const getLecueBook = async () => { const { data } = await api.get('/api/common/home'); - return data; + return data.data; }; export default getLecueBook; diff --git a/src/Home/components/LecueBookList/LecueBookList.style.ts b/src/Home/components/LecueBookList/LecueBookList.style.ts index 411e6889..d1079d34 100644 --- a/src/Home/components/LecueBookList/LecueBookList.style.ts +++ b/src/Home/components/LecueBookList/LecueBookList.style.ts @@ -25,11 +25,11 @@ export const Title = styled.header` export const LecueBookList = styled.section` display: grid; - gap: 2.2rem; + gap: 2em 2.4rem; grid-template-columns: repeat(3, 1fr); width: 100%; - padding: 3rem 1.6rem 2.2rem; + padding: 3rem 1.6rem; `; export const LecueBook = styled.li` @@ -38,13 +38,19 @@ export const LecueBook = styled.li` justify-content: center; align-items: center; flex-direction: column; + position: relative; width: 100%; - height: 14rem; cursor: pointer; `; +export const IconWrapper = styled.button` + position: absolute; + top: 0; + left: 0.1rem; +`; + export const BookImage = styled.img` width: 9.8rem; height: 9.8rem; diff --git a/src/Home/components/LecueBookList/index.tsx b/src/Home/components/LecueBookList/index.tsx index 0bad82f7..4eda671e 100644 --- a/src/Home/components/LecueBookList/index.tsx +++ b/src/Home/components/LecueBookList/index.tsx @@ -1,8 +1,10 @@ -// import { useNavigate } from 'react-router-dom'; - -import { useNavigate } from 'react-router-dom'; +import { useNavigate } from 'react-router'; +import { IcHomeFavorite } from '../../../assets'; +import useDeleteFavorite from '../../../libs/hooks/useDeleteFavorite'; +import useGetFavorite from '../../../libs/hooks/useGetFavorite'; import useGetLecueBook from '../../hooks/useGetLecueBook'; +import NoBookmarkList from '../NoBookmarkList'; import * as S from './LecueBookList.style'; interface BookProps { @@ -12,29 +14,51 @@ interface BookProps { favoriteName: string; } -function LecueBookList() { - const { data } = useGetLecueBook(); +interface LecueBookListProps { + title: string; +} + +function LecueBookList({ title }: LecueBookListProps) { const navigate = useNavigate(); + const deleteMutation = useDeleteFavorite(); + const isBookmark = title.includes('즐겨찾기'); + const { data } = isBookmark ? useGetFavorite() : useGetLecueBook(); const handleClickLecueBook = (uuid: string) => { navigate(`/lecue-book/${uuid}`); }; + const handleClickFavoriteIcon = (bookId: number) => { + deleteMutation.mutate(bookId); + }; + return ( - 인기 레큐북 구경하기 - - {data && - data.data.map((book: BookProps) => ( - handleClickLecueBook(book.bookUuid)} - > - + {title} + {data && data.length !== 0 ? ( + + {data.map((book: BookProps) => ( + + {isBookmark && ( + handleClickFavoriteIcon(book.bookId)} + > + + + )} + + handleClickLecueBook(book.bookUuid)} + /> {book.favoriteName} ))} - + + ) : ( + + )} ); } diff --git a/src/Home/components/NavigateLecueBook/NavigateLecueBook.style.ts b/src/Home/components/NavigateLecueBook/NavigateLecueBook.style.ts index 3af55d88..03fbf33a 100644 --- a/src/Home/components/NavigateLecueBook/NavigateLecueBook.style.ts +++ b/src/Home/components/NavigateLecueBook/NavigateLecueBook.style.ts @@ -8,32 +8,30 @@ export const MainWrapper = styled.div` export const IconWrapper = styled.section` display: flex; - gap: 15.7rem; - justify-content: space-between; + + /* 바뀔 수 있을 것 같으니 디자인 나오면 다시 확인해보기 ! */ + gap: 16rem; align-items: baseline; width: 100%; - padding: 6rem 1.6rem 5rem; + margin: 4rem 1.8rem 3.5rem 1.6rem; `; -export const ButtonWrapper = styled.section` - display: flex; - gap: 1rem; - flex-direction: column; +export const DummyGraphic = styled.div` + width: 37.5rem; + height: 20rem; - padding: 0 9.5rem 4.9rem 0; + background-color: ${({ theme }) => theme.colors.LG}; `; -export const Button = styled.button<{ variant?: boolean }>` - width: 28rem; - height: 6.4rem; +export const Button = styled.button` + padding: 2.1rem 9.4rem 2.2rem 8.8rem; + margin: 2rem 0 4rem; border: 0.1rem solid ${({ theme }) => theme.colors.BG}; border-radius: 0 0.2rem 0.2rem 0; border-left: none; - background-color: ${({ theme, variant }) => - variant ? theme.colors.white : theme.colors.BG}; - color: ${({ theme, variant }) => - variant ? theme.colors.BG : theme.colors.white}; + background-color: ${({ theme }) => theme.colors.BG}; + color: ${({ theme }) => theme.colors.white}; ${({ theme }) => theme.fonts.Title1_SB_16} `; diff --git a/src/Home/components/NavigateLecueBook/index.tsx b/src/Home/components/NavigateLecueBook/index.tsx index 29b9e660..6215939f 100644 --- a/src/Home/components/NavigateLecueBook/index.tsx +++ b/src/Home/components/NavigateLecueBook/index.tsx @@ -1,18 +1,23 @@ import { useState } from 'react'; import { useNavigate } from 'react-router-dom'; -import { IcNotice, ImgLogoLecue } from '../../../assets'; +import { IcProfile, ImgLogoLecue } from '../../../assets'; import CommonModal from '../../../components/common/Modal/CommonModal'; import * as S from './NavigateLecueBook.style'; function NavigateLecueBook() { - const NAVIGATE_CATEGORY = ['레큐북 만들기', '내 기록 보기']; const navigate = useNavigate(); const [modalOn, setModalOn] = useState(false); - const handleClickNavBtn = (idx: number) => { + const handleClickIcProfile = () => { + const token = localStorage.getItem('token'); + + navigate('/mypage', { state: token }); + }; + + const handleClickNavBtn = () => { if (localStorage.getItem('token')) { - idx === 0 ? navigate('/target') : navigate('/mypage'); + navigate('/target'); } else { setModalOn(true); } @@ -22,29 +27,16 @@ function NavigateLecueBook() { - - - + + - - {NAVIGATE_CATEGORY.map((category, idx) => { - return ( - handleClickNavBtn(idx)} - > - {category} - - ); - })} - + {/* 임시로 넣은 것! 추후 새로운 그래픽으로 수정 */} + + + + 레큐북 만들기 + {modalOn && ( theme.colors.BG}; + ${({ theme }) => theme.fonts.Body2_M_14}; +`; + +export const NavigateBtn = styled.button` + padding: 0.8rem 2.8rem; + + border: 0.1rem solid ${({ theme }) => theme.colors.BG}; + border-radius: 0.6rem; + background-color: ${({ theme }) => theme.colors.white}; + color: ${({ theme }) => theme.colors.BG}; + ${({ theme }) => theme.fonts.Body4_SB_14}; +`; diff --git a/src/Home/components/NoBookmarkList/index.tsx b/src/Home/components/NoBookmarkList/index.tsx new file mode 100644 index 00000000..c2c43909 --- /dev/null +++ b/src/Home/components/NoBookmarkList/index.tsx @@ -0,0 +1,28 @@ +import { useNavigate } from 'react-router'; + +import * as S from './NoBookmarkList.style'; + +const NoBookmarkList = () => { + const navigate = useNavigate(); + + const handleClickNavigateBtn = () => { + navigate('/mypage'); + }; + + return ( + + + 아직 즐겨찾기한 레큐북이 없어요. + + 자주 보고 싶은 레큐북을 즐겨찾기 해보세요. + + + + + 레큐북 보러가기 + + + ); +}; + +export default NoBookmarkList; diff --git a/src/Home/hooks/.gitkeep b/src/Home/hooks/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/Home/hooks/useGetLecueBook.ts b/src/Home/hooks/useGetLecueBook.ts index 0fa4627e..a43b43ea 100644 --- a/src/Home/hooks/useGetLecueBook.ts +++ b/src/Home/hooks/useGetLecueBook.ts @@ -6,14 +6,14 @@ import getLecueBook from '../api/getLecueBook'; const useGetLecueBook = () => { const navigate = useNavigate(); - const { isLoading, data } = useQuery({ + const { isLoading: isLoadingLecueBook, data: lecueBook } = useQuery({ queryKey: ['get-lecue-book'], queryFn: () => getLecueBook(), onError: () => navigate('/error'), refetchOnWindowFocus: false, }); - return { isLoading, data }; + return { isLoading: isLoadingLecueBook, data: lecueBook }; }; export default useGetLecueBook; diff --git a/src/Home/page/index.tsx b/src/Home/page/index.tsx index 1c4c3db5..9d640380 100644 --- a/src/Home/page/index.tsx +++ b/src/Home/page/index.tsx @@ -8,18 +8,21 @@ import useGetLecueBook from '../hooks/useGetLecueBook'; import * as S from './Home.style'; function Home({ handleStep }: StepProps) { - const { isLoading } = useGetLecueBook(); + const token = localStorage.getItem('token'); + const { isLoading: isLoadingLecueBook } = useGetLecueBook(); useEffect(() => { handleStep(1); }, []); - return isLoading ? ( + return isLoadingLecueBook ? ( ) : ( - + + {token && } + ); } diff --git a/src/assets/icon/ic_alert_o.svg b/src/assets/icon/ic_alert_o.svg new file mode 100644 index 00000000..a8163791 --- /dev/null +++ b/src/assets/icon/ic_alert_o.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/assets/icon/ic_alert_x.svg b/src/assets/icon/ic_alert_x.svg new file mode 100644 index 00000000..6d3e3628 --- /dev/null +++ b/src/assets/icon/ic_alert_x.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/assets/icon/ic_home_favorite.svg b/src/assets/icon/ic_home_favorite.svg new file mode 100644 index 00000000..d8b3cc7c --- /dev/null +++ b/src/assets/icon/ic_home_favorite.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/icon/ic_profile.svg b/src/assets/icon/ic_profile.svg new file mode 100644 index 00000000..0b5f53f7 --- /dev/null +++ b/src/assets/icon/ic_profile.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/index.ts b/src/assets/index.ts index 48d600dd..5d5b41fe 100644 --- a/src/assets/index.ts +++ b/src/assets/index.ts @@ -6,6 +6,8 @@ import BtnFloatingStickerOrange from './button/btn_floating_sticker_orange.svg?r import BtnFloatingWrite from './button/btn_floating_write.svg?react'; import BtnFloatingWriteOrange from './button/btn_floating_write_orange.svg?react'; import BtnKakaologin from './button/btn_kakaologin.svg?react'; +import IcAlertO from './icon/ic_alert_o.svg?react'; +import IcAlertX from './icon/ic_alert_x.svg?react'; import IcArrowLeftBlack from './icon/ic_arrow_left_black.svg?react'; import IcArrowLeftWhite from './icon/ic_arrow_left_white.svg?react'; import IcCamera from './icon/ic_camera.svg?react'; @@ -15,7 +17,9 @@ import IcCheck from './icon/ic_check.svg?react'; import IcCrown from './icon/ic_crown.svg?react'; import IcDate from './icon/ic_date.svg?react'; import IcHome from './icon/ic_home.svg?react'; +import IcHomeFavorite from './icon/ic_home_favorite.svg?react'; import IcNotice from './icon/ic_notice.svg?react'; +import IcProfile from './icon/ic_profile.svg?react'; import IcSharing from './icon/ic_sharing.svg?react'; import IcWaste from './icon/ic_waste.svg?react'; import IcX from './icon/ic_x.svg?react'; @@ -56,6 +60,8 @@ export { BtnFloatingWrite, BtnFloatingWriteOrange, BtnKakaologin, + IcAlertO, + IcAlertX, IcArrowLeftBlack, IcArrowLeftWhite, IcCamera, @@ -65,7 +71,9 @@ export { IcCrown, IcDate, IcHome, + IcHomeFavorite, IcNotice, + IcProfile, IcSharing, IcWaste, IcX, diff --git a/src/libs/api/deleteFavorite.ts b/src/libs/api/deleteFavorite.ts new file mode 100644 index 00000000..d47d7c76 --- /dev/null +++ b/src/libs/api/deleteFavorite.ts @@ -0,0 +1,16 @@ +import { api } from '../../libs/api'; + +const deleteFavorite = async (bookId: number) => { + const token = localStorage.getItem('token'); + const { data } = await api.delete('/api/favorite', { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${token}`, + }, + data: { bookId: bookId }, + }); + + return data; +}; + +export default deleteFavorite; diff --git a/src/libs/api/getFavorite.ts b/src/libs/api/getFavorite.ts new file mode 100644 index 00000000..7c7122d7 --- /dev/null +++ b/src/libs/api/getFavorite.ts @@ -0,0 +1,14 @@ +import { api } from '../../libs/api'; + +const getFavorite = async () => { + const token = localStorage.getItem('token'); + const { data } = await api.get('/api/favorite', { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${token}`, + }, + }); + return data.data; +}; + +export default getFavorite; diff --git a/src/libs/hooks/.keep b/src/libs/hooks/.keep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/libs/hooks/useDeleteFavorite.ts b/src/libs/hooks/useDeleteFavorite.ts new file mode 100644 index 00000000..81a364e1 --- /dev/null +++ b/src/libs/hooks/useDeleteFavorite.ts @@ -0,0 +1,21 @@ +import { useMutation, useQueryClient } from 'react-query'; +import { useNavigate } from 'react-router-dom'; + +import deleteFavorite from '../api/deleteFavorite'; + +const useDeleteFavorite = () => { + const navigate = useNavigate(); + const queryClient = useQueryClient(); + const mutation = useMutation({ + mutationFn: (bookId: number) => { + return deleteFavorite(bookId); + }, + onError: () => navigate('/error'), + onSuccess: () => { + queryClient.refetchQueries(['get-favorite'], { exact: true }); + }, + }); + return mutation; +}; + +export default useDeleteFavorite; diff --git a/src/libs/hooks/useGetFavorite.ts b/src/libs/hooks/useGetFavorite.ts new file mode 100644 index 00000000..fc48f529 --- /dev/null +++ b/src/libs/hooks/useGetFavorite.ts @@ -0,0 +1,19 @@ +import { useQuery } from 'react-query'; +import { useNavigate } from 'react-router-dom'; + +import getFavorite from '../api/getFavorite'; + +const useGetFavorite = () => { + const navigate = useNavigate(); + + const { isLoading: isLoadingFavorite, data: favorite } = useQuery({ + queryKey: ['get-favorite'], + queryFn: () => getFavorite(), + onError: () => navigate('/error'), + refetchOnWindowFocus: false, + }); + + return { isLoading: isLoadingFavorite, data: favorite }; +}; + +export default useGetFavorite;