From 1f73a24fe673d0a0f1ffe5443259ddeea3750fcf Mon Sep 17 00:00:00 2001 From: yongaricode Date: Wed, 21 May 2025 13:09:44 +0900 Subject: [PATCH 1/8] =?UTF-8?q?feat:=20=EB=A7=88=EC=9D=B4=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20UI=20=EB=B0=8F=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=95=84=EC=9B=83=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/login.api.ts | 12 ++++++++ src/components/Common/Header.tsx | 12 ++++++-- src/components/MyPage/LoginBox.tsx | 47 ++++++++++++++++++++++++++++++ src/pages/MyPage.tsx | 5 ---- src/pages/MyPage/MyPage.tsx | 16 ++++++++++ src/routes/router.tsx | 2 +- 6 files changed, 86 insertions(+), 8 deletions(-) create mode 100644 src/components/MyPage/LoginBox.tsx delete mode 100644 src/pages/MyPage.tsx create mode 100644 src/pages/MyPage/MyPage.tsx diff --git a/src/api/login.api.ts b/src/api/login.api.ts index f0ac3de..18a4698 100644 --- a/src/api/login.api.ts +++ b/src/api/login.api.ts @@ -66,3 +66,15 @@ export const getTempPw = async (email: string) => { console.log(err); } }; + +// 로그아웃 +export const logOut = async (refreshToken: string) => { + try { + const response = await instance.post('auth/logout', { refreshToken: refreshToken }); + if (response.status === 200) { + return response.data; + } + } catch (err) { + console.log(err); + } +}; diff --git a/src/components/Common/Header.tsx b/src/components/Common/Header.tsx index 1352d59..c44fbdc 100644 --- a/src/components/Common/Header.tsx +++ b/src/components/Common/Header.tsx @@ -31,18 +31,22 @@ const Header = ({ title }: HeaderProps) => { '/booksnap/create/2', '/booksnap/create/indi/2', '/booksnap/create/book', + '/mypage', ]; const showBackButton = showBackButtonPaths.includes(location.pathname); const showCloseButtonPaths = ['/bookie', '/booksnap/create/indi/1', '/booksnap/create/1']; const showCloseButton = showCloseButtonPaths.includes(location.pathname); + // 마이페이지만 헤더 글씨, 아이콘 흰색 + const myPage = location.pathname.startsWith('/mypage'); + return (
{showBackButton && (
- +
)} {showCloseButton && ( @@ -50,7 +54,11 @@ const Header = ({ title }: HeaderProps) => {
)} -
{title}
+
+ {title} +
{showBackButton || showCloseButton ?
: ''}
diff --git a/src/components/MyPage/LoginBox.tsx b/src/components/MyPage/LoginBox.tsx new file mode 100644 index 0000000..6fc935f --- /dev/null +++ b/src/components/MyPage/LoginBox.tsx @@ -0,0 +1,47 @@ +import kakao from '../../../public/icons/login-signup/Kakao.svg'; +import BookmarkRoundedIcon from '@mui/icons-material/BookmarkRounded'; +import RateReviewRoundedIcon from '@mui/icons-material/RateReviewRounded'; +import { logOut } from '../../api/login.api'; +import { useNavigate } from 'react-router-dom'; +import toast from 'react-hot-toast'; + +const LoginBox = () => { + const nav = useNavigate(); + + const handleLogout = async () => { + const token = localStorage.getItem('refreshToken'); + logOut(token!).then(() => { + toast.success('로그아웃 되었습니다.'); + nav('/'); + }); + }; + + const nickname = localStorage.getItem('nickname'); + return ( +
+
+ + 카카오 계정 회원 +
+
+

{nickname}

+

+ 로그아웃 +

+
+
+
+ +

나의 책장

+
+
+
+ +

리뷰 관리

+
+
+
+ ); +}; + +export default LoginBox; diff --git a/src/pages/MyPage.tsx b/src/pages/MyPage.tsx deleted file mode 100644 index 706b622..0000000 --- a/src/pages/MyPage.tsx +++ /dev/null @@ -1,5 +0,0 @@ -const MyPage = () => { - return
MyPage
; -}; - -export default MyPage; diff --git a/src/pages/MyPage/MyPage.tsx b/src/pages/MyPage/MyPage.tsx new file mode 100644 index 0000000..2f3ad80 --- /dev/null +++ b/src/pages/MyPage/MyPage.tsx @@ -0,0 +1,16 @@ +import { useNavigate } from 'react-router-dom'; +import Header from '../../components/Common/Header'; +import LoginBox from '../../components/MyPage/LoginBox'; + +const MyPage = () => { + const nav = useNavigate(); + + return ( +
+
+ +
+ ); +}; + +export default MyPage; diff --git a/src/routes/router.tsx b/src/routes/router.tsx index 05482c4..80fec70 100644 --- a/src/routes/router.tsx +++ b/src/routes/router.tsx @@ -1,7 +1,7 @@ import { createBrowserRouter, RouterProvider } from 'react-router-dom'; import Home from '../pages/Home'; import Zip from '../pages/Zip/Zip'; -import MyPage from '../pages/MyPage'; +import MyPage from '../pages/MyPage/MyPage'; import Bookie from '../pages/Bookie'; import BookSnap from '../pages/Booksnap/BookSnap'; import Layout from '../components/layout'; From 559bffcef8e2eb44013ef1493d1917ed8317c41c Mon Sep 17 00:00:00 2001 From: yongaricode Date: Wed, 21 May 2025 13:21:18 +0900 Subject: [PATCH 2/8] =?UTF-8?q?feat:=20=EB=A7=88=EC=9D=B4=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=ED=86=A0=EC=8A=A4=ED=8A=B8=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/components/MyPage/LoginBox.tsx | 50 ++++++++++++++++++++++-------- src/pages/MyPage/MyPage.tsx | 3 -- 2 files changed, 37 insertions(+), 16 deletions(-) diff --git a/src/components/MyPage/LoginBox.tsx b/src/components/MyPage/LoginBox.tsx index 6fc935f..5a6b55a 100644 --- a/src/components/MyPage/LoginBox.tsx +++ b/src/components/MyPage/LoginBox.tsx @@ -7,35 +7,59 @@ import toast from 'react-hot-toast'; const LoginBox = () => { const nav = useNavigate(); + const isLogin = localStorage.getItem('accessToken'); const handleLogout = async () => { const token = localStorage.getItem('refreshToken'); logOut(token!).then(() => { toast.success('로그아웃 되었습니다.'); nav('/'); + localStorage.clear(); }); }; + const handleBook = async () => { + if (isLogin) { + nav('myBook'); + } else { + toast.error('로그인이 필요한 서비스입니다'); + } + }; + const nickname = localStorage.getItem('nickname'); return ( -
-
- - 카카오 계정 회원 -
-
-

{nickname}

-

- 로그아웃 -

-
+
+ {isLogin ? ( +
+
+ + 카카오 계정 회원 +
+
+

{nickname}

+

+ 로그아웃 +

+
+
+ ) : ( +
nav('/login')}> + 로그인을 해주세요 +
+ )}
-
+

나의 책장

-
+
toast.error('아직 준비중인 서비스입니다.')} + >

리뷰 관리

diff --git a/src/pages/MyPage/MyPage.tsx b/src/pages/MyPage/MyPage.tsx index 2f3ad80..024ab27 100644 --- a/src/pages/MyPage/MyPage.tsx +++ b/src/pages/MyPage/MyPage.tsx @@ -1,10 +1,7 @@ -import { useNavigate } from 'react-router-dom'; import Header from '../../components/Common/Header'; import LoginBox from '../../components/MyPage/LoginBox'; const MyPage = () => { - const nav = useNavigate(); - return (
From 6802f2ee21d46d5f627a6a911e6d4bab0d402ab5 Mon Sep 17 00:00:00 2001 From: yongaricode Date: Wed, 21 May 2025 15:16:49 +0900 Subject: [PATCH 3/8] =?UTF-8?q?fix:=20=EB=B9=84=EB=A1=9C=EA=B7=B8=EC=9D=B8?= =?UTF-8?q?=20=EC=8B=9C=20=EC=9D=B4=EB=8F=99=20=EB=A7=89=EA=B3=A0=20toast?= =?UTF-8?q?=20=EB=85=B8=EC=B6=9C=EB=90=98=EB=8F=84=EB=A1=9D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Booksnap/WriteButton.tsx | 3 +- src/components/Common/Header.tsx | 1 - src/components/Common/MenuBar.tsx | 5 ++-- src/components/MyPage/LoginBox.tsx | 38 ++++++++----------------- src/components/ProtectedRoute.tsx | 14 --------- src/pages/MyPage/MyBook.tsx | 5 ++++ src/pages/Zip/ZipDetail.tsx | 6 ++-- src/routes/router.tsx | 37 ++++-------------------- src/utils/ProtectedNavigate.ts | 17 +++++++++++ 9 files changed, 49 insertions(+), 77 deletions(-) delete mode 100644 src/components/ProtectedRoute.tsx create mode 100644 src/pages/MyPage/MyBook.tsx create mode 100644 src/utils/ProtectedNavigate.ts diff --git a/src/components/Booksnap/WriteButton.tsx b/src/components/Booksnap/WriteButton.tsx index fe4d1c4..c0f8004 100644 --- a/src/components/Booksnap/WriteButton.tsx +++ b/src/components/Booksnap/WriteButton.tsx @@ -1,5 +1,6 @@ import { HiPencil } from 'react-icons/hi2'; import { useNavigate } from 'react-router-dom'; +import { protectedNavigate } from '../../utils/ProtectedNavigate'; const WriteButton = () => { const nav = useNavigate(); @@ -9,7 +10,7 @@ const WriteButton = () => {
nav('create/1')} + onClick={() => protectedNavigate(nav, 'create/1')} >
diff --git a/src/components/Common/Header.tsx b/src/components/Common/Header.tsx index c44fbdc..e1b46e3 100644 --- a/src/components/Common/Header.tsx +++ b/src/components/Common/Header.tsx @@ -31,7 +31,6 @@ const Header = ({ title }: HeaderProps) => { '/booksnap/create/2', '/booksnap/create/indi/2', '/booksnap/create/book', - '/mypage', ]; const showBackButton = showBackButtonPaths.includes(location.pathname); diff --git a/src/components/Common/MenuBar.tsx b/src/components/Common/MenuBar.tsx index f223b89..cd4f2cd 100644 --- a/src/components/Common/MenuBar.tsx +++ b/src/components/Common/MenuBar.tsx @@ -12,6 +12,7 @@ const MenuBar = () => { const nav = useNavigate(); const location = useLocation(); const [currentMenu, setCurrentMenu] = useState('home'); + const isLogin = localStorage.getItem('accessToken'); useEffect(() => { const pathname = location.pathname; @@ -48,8 +49,8 @@ const MenuBar = () => { active: , }, { - menu: 'My page', - name: 'mypage', + menu: isLogin ? 'My Page' : 'Login', + name: isLogin ? 'mypage' : 'login', inactive: , active: , }, diff --git a/src/components/MyPage/LoginBox.tsx b/src/components/MyPage/LoginBox.tsx index 5a6b55a..da16dac 100644 --- a/src/components/MyPage/LoginBox.tsx +++ b/src/components/MyPage/LoginBox.tsx @@ -7,7 +7,6 @@ import toast from 'react-hot-toast'; const LoginBox = () => { const nav = useNavigate(); - const isLogin = localStorage.getItem('accessToken'); const handleLogout = async () => { const token = localStorage.getItem('refreshToken'); @@ -18,39 +17,26 @@ const LoginBox = () => { }); }; - const handleBook = async () => { - if (isLogin) { - nav('myBook'); - } else { - toast.error('로그인이 필요한 서비스입니다'); - } - }; - const nickname = localStorage.getItem('nickname'); return (
- {isLogin ? ( -
-
- - 카카오 계정 회원 -
-
-

{nickname}

-

- 로그아웃 -

-
+
+
+ + 카카오 계정 회원
- ) : ( -
nav('/login')}> - 로그인을 해주세요 +
+

{nickname}

+

+ 로그아웃 +

- )} +
+
nav('myBook')} >

나의 책장

diff --git a/src/components/ProtectedRoute.tsx b/src/components/ProtectedRoute.tsx deleted file mode 100644 index 31498c2..0000000 --- a/src/components/ProtectedRoute.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { ReactNode } from 'react'; -import { Navigate } from 'react-router-dom'; - -interface ProtectedRouteProps { - children: ReactNode; -} - -const ProtectedRoute = ({ children }: ProtectedRouteProps) => { - const isAuthenticated = !!localStorage.getItem('accessToken'); - // 모달 띄울수없나? - return isAuthenticated ? <>{children} : ; -}; - -export default ProtectedRoute; diff --git a/src/pages/MyPage/MyBook.tsx b/src/pages/MyPage/MyBook.tsx new file mode 100644 index 0000000..d1fcd30 --- /dev/null +++ b/src/pages/MyPage/MyBook.tsx @@ -0,0 +1,5 @@ +const MyBook = () => { + return
; +}; + +export default MyBook; diff --git a/src/pages/Zip/ZipDetail.tsx b/src/pages/Zip/ZipDetail.tsx index a8d86e8..c76cf11 100644 --- a/src/pages/Zip/ZipDetail.tsx +++ b/src/pages/Zip/ZipDetail.tsx @@ -12,6 +12,7 @@ import { bookstoreReview, zipPreview } from '../../model/zip.model'; import NoBookStoreResult from '../../components/Zip/NoBookStoreResult'; import { BookDetailInfo } from '../../model/booksnap.model'; import { set } from 'lodash'; +import { protectedNavigate } from '../../utils/ProtectedNavigate'; interface ZipDetailProps { currentState: string; @@ -30,8 +31,9 @@ const ZipDetail = ({ currentState, id }: ZipDetailProps) => { const [filter, setFilter] = useState<'createdAt' | 'rating'>('createdAt'); const handleWriteReview = () => { - // setBottomSheet(({ currentState }) => , '서점 상세 정보'); - nav('create-review', { state: { id: id, name: bookstoreInfo?.name } }); + protectedNavigate(nav, 'create-review', { + state: { id: id, name: bookstoreInfo?.name }, + }); }; useEffect(() => { diff --git a/src/routes/router.tsx b/src/routes/router.tsx index 80fec70..4d0230c 100644 --- a/src/routes/router.tsx +++ b/src/routes/router.tsx @@ -13,7 +13,6 @@ import FindPw2 from '../pages/FindPW/FindPw2'; import ResetPw from '../pages/FindPW/ResetPw'; import KakaoLogin from '../components/Login/KakaoLogin'; import CreateReview from '../pages/Zip/CreateReview'; -import ProtectedRoute from '../components/ProtectedRoute'; import CreateBooksnapReview1 from '../pages/Booksnap/CreateBooksnapReview1'; import CreateBooksnapReview2 from '../pages/Booksnap/CreateBooksnapReview2'; import BookSearch from '../pages/Booksnap/BookSearch'; @@ -44,51 +43,27 @@ export const router = createBrowserRouter([ { path: '/bookie', element: }, { path: 'zip/create-review', - element: ( - - - - ), + element: , }, { path: 'booksnap/create/1', - element: ( - - - - ), + element: , }, { path: 'booksnap/create/2', - element: ( - - - - ), + element: , }, { path: 'booksnap/create/indi/1', - element: ( - - - - ), + element: , }, { path: 'booksnap/create/indi/2', - element: ( - - - - ), + element: , }, { path: 'booksnap/create/book', - element: ( - - - - ), + element: , }, { path: '/reset-pw', element: }, ]); diff --git a/src/utils/ProtectedNavigate.ts b/src/utils/ProtectedNavigate.ts new file mode 100644 index 0000000..b954038 --- /dev/null +++ b/src/utils/ProtectedNavigate.ts @@ -0,0 +1,17 @@ +import { toast } from 'react-hot-toast'; +import { NavigateOptions } from 'react-router-dom'; + +export const protectedNavigate = ( + navigate: (to: string, options?: NavigateOptions) => void, + path: string, + options?: NavigateOptions, +) => { + const isAuthenticated = !!localStorage.getItem('accessToken'); + + if (!isAuthenticated) { + toast.error('로그인이 필요한 서비스입니다.'); + return; + } + + navigate(path, options); +}; From 8d5e450d5ea19e221d9059d62647c7ea99aa20cb Mon Sep 17 00:00:00 2001 From: yongaricode Date: Wed, 21 May 2025 16:01:38 +0900 Subject: [PATCH 4/8] =?UTF-8?q?feat:=20=ED=99=88=20=ED=99=94=EB=A9=B4=20UI?= =?UTF-8?q?=20=EB=AA=A8=EB=91=90=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Home/BookReview.tsx | 17 ++++++++++++++--- src/components/Home/BookieBox.tsx | 8 +++++++- src/components/Home/MadeBy.tsx | 2 +- src/components/Home/PopularBox.tsx | 11 +++++++---- src/components/Home/RegionBox.tsx | 3 +++ src/components/Home/ReportBookstore.tsx | 6 +++++- src/components/Home/ReportStore.tsx | 13 +++++++++---- src/components/Home/ReviewBox.tsx | 18 ++++++++++++------ src/components/Modal/AddBookstoreModal.tsx | 3 ++- src/pages/Home.tsx | 10 +++++++++- 10 files changed, 69 insertions(+), 22 deletions(-) diff --git a/src/components/Home/BookReview.tsx b/src/components/Home/BookReview.tsx index c648345..ce32c9f 100644 --- a/src/components/Home/BookReview.tsx +++ b/src/components/Home/BookReview.tsx @@ -1,6 +1,17 @@ +import { useEffect, useState } from 'react'; +import { getReview } from '../../api/booksnap.api'; import ReviewBox from './ReviewBox'; +import { BooksnapPreview } from '../../model/booksnap.model'; const BookReview = () => { + const [reviews, setReview] = useState([]); + + useEffect(() => { + getReview('trend', 1).then((data) => { + setReview(data.data.booksnapPreview.slice(0, 3)); // 배열의 첫 3개만 저장 + }); + }, []); + return (
@@ -11,9 +22,9 @@ const BookReview = () => { {/* 가로 스크롤 가능한 영역 */}
- - - + {reviews.map((review, index) => ( + + ))}
diff --git a/src/components/Home/BookieBox.tsx b/src/components/Home/BookieBox.tsx index 6c2ab1a..d85ebf6 100644 --- a/src/components/Home/BookieBox.tsx +++ b/src/components/Home/BookieBox.tsx @@ -1,9 +1,15 @@ import MenuBookIcon from '@mui/icons-material/MenuBook'; import Bookie from '../../../public/icons/home/bookie.png'; +import { protectedNavigate } from '../../utils/ProtectedNavigate'; +import { useNavigate } from 'react-router-dom'; const BookieBox = () => { + const nav = useNavigate(); return ( -
+
protectedNavigate(nav, '/bookie')} + >

도서 추천 메이트

diff --git a/src/components/Home/MadeBy.tsx b/src/components/Home/MadeBy.tsx index 27068d5..d81608f 100644 --- a/src/components/Home/MadeBy.tsx +++ b/src/components/Home/MadeBy.tsx @@ -14,7 +14,7 @@ const MadeBy = () => {

@yongaricode

-

용가리 먹방 시작~

+

용가리가 코드를 짠다~

@hyuna

diff --git a/src/components/Home/PopularBox.tsx b/src/components/Home/PopularBox.tsx index 4721a3e..4938778 100644 --- a/src/components/Home/PopularBox.tsx +++ b/src/components/Home/PopularBox.tsx @@ -1,15 +1,18 @@ -import MenuBookIcon from '@mui/icons-material/MenuBook'; -import Bookie from '../../../public/icons/home/bookie.png'; +import { useNavigate } from 'react-router-dom'; import Marker from '../../../public/icons/zip/markerHome.svg?react'; const PopularBox = () => { + const nav = useNavigate(); return ( -
+
nav('/zip?search=우주소년')} + >

ZIPZIP이들이 PICK한

이달의 인기 서점
- “고요서사” + "우주소년"
); diff --git a/src/components/Home/RegionBox.tsx b/src/components/Home/RegionBox.tsx index 9e0baf0..0c9cc98 100644 --- a/src/components/Home/RegionBox.tsx +++ b/src/components/Home/RegionBox.tsx @@ -1,13 +1,16 @@ import LocationSearchingIcon from '@mui/icons-material/LocationSearching'; +import { useNavigate } from 'react-router-dom'; interface RegionBoxProps { text: string; } const RegionBox = ({ text }: RegionBoxProps) => { + const nav = useNavigate(); return (
nav(`/zip?search=${text}`)} >

{text}

diff --git a/src/components/Home/ReportBookstore.tsx b/src/components/Home/ReportBookstore.tsx index 68c6bc7..c588d6b 100644 --- a/src/components/Home/ReportBookstore.tsx +++ b/src/components/Home/ReportBookstore.tsx @@ -1,9 +1,13 @@ import sunglass from '../../../public/icons/home/sunglass.png'; import hand from '../../../public/icons/home/hand.png'; +import toast from 'react-hot-toast'; const ReportBookstore = () => { return ( -
+
toast.error('아직 준비중인 서비스입니다')} + >

To. zipzip이들

diff --git a/src/components/Home/ReportStore.tsx b/src/components/Home/ReportStore.tsx index 0d3d92f..0245aca 100644 --- a/src/components/Home/ReportStore.tsx +++ b/src/components/Home/ReportStore.tsx @@ -1,17 +1,22 @@ import Vector from '../../../public/icons/home/Vector.svg?react'; -const ReportStore = () => { +interface ReportStore { + onClick: () => void; +} + +const ReportStore = ({ onClick }: ReportStore) => { + const name = localStorage.getItem('nickname'); return ( -

+
서점ZIP에 없는 독립서점

제보해주세요!

-

독서광시온님의 제보가 서점ZIP을 살려요!

+

{name}님의 제보가 서점ZIP을 살려요!

-
+

더 나은 서점ZIP 서비스를 위해
독립 서점 제보하러 가기

diff --git a/src/components/Home/ReviewBox.tsx b/src/components/Home/ReviewBox.tsx index 82233ca..ff99ec6 100644 --- a/src/components/Home/ReviewBox.tsx +++ b/src/components/Home/ReviewBox.tsx @@ -1,8 +1,14 @@ -const ReviewBox = () => { +import { BooksnapPreview } from '../../model/booksnap.model'; + +interface ReviewBoxProps { + reviews: BooksnapPreview; +} + +const ReviewBox = ({ reviews }: ReviewBoxProps) => { return (
{/* 상단 텍스트 */} -
독서왕용가리
+
{reviews.userName}
{/* 배경 이미지 영역 */}
@@ -10,7 +16,7 @@ const ReviewBox = () => {
@@ -21,12 +27,12 @@ const ReviewBox = () => {


- 사이키쿠스오가 될래... + {reviews.review.length > 15 ? `${reviews.review.slice(0, 15)}...` : reviews.review}

- 수레바퀴 아래서 - ★ 4 + {reviews.bookInfo.title} + ★ {reviews.rating}
diff --git a/src/components/Modal/AddBookstoreModal.tsx b/src/components/Modal/AddBookstoreModal.tsx index a3ca9c1..cfa8ec7 100644 --- a/src/components/Modal/AddBookstoreModal.tsx +++ b/src/components/Modal/AddBookstoreModal.tsx @@ -14,12 +14,13 @@ const AddBookstoreModal = ({ setModalOpen, name }: ModalProps) => { const [closeTime, setCloseTime] = useState(''); const [feature, setFeature] = useState(''); const [detail, setDetail] = useState(''); + const nickname = localStorage.getItem('nickname'); return (

독립서점 제보하기

-

용가리님만 알고있는 독립서점을 제보해주세요!

+

{nickname}님만 알고있는 독립서점을 제보해주세요!

{ const nav = useNavigate(); const [searchWord, setSearchWord] = useState(''); + const [modal, setModal] = useState(false); const handleSearch = () => { nav(`/zip?search=${searchWord}`); @@ -39,10 +42,15 @@ const Home = () => { - + setModal(true)} />
+ {modal && ( + + + + )}
); }; From 17f4a0ef0f82bef44e383979764147943e6a4f55 Mon Sep 17 00:00:00 2001 From: yongaricode Date: Wed, 21 May 2025 16:12:36 +0900 Subject: [PATCH 5/8] =?UTF-8?q?feat:=20=EC=84=9C=EC=A0=90=20=ED=95=B4?= =?UTF-8?q?=EC=8B=9C=ED=83=9C=EA=B7=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/zip.api.ts | 12 ++++++++++++ src/components/Zip/TagBar.tsx | 17 ++++++++++++----- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/api/zip.api.ts b/src/api/zip.api.ts index 11e9a32..d47927a 100644 --- a/src/api/zip.api.ts +++ b/src/api/zip.api.ts @@ -94,3 +94,15 @@ export const getTrendZip = async () => { console.log(err); } }; + +// 해시태그 검색 +export const getHashTag = async () => { + try { + const response = await instance.get('/api/bookstores/hashtag'); + if (response.status === 200) { + return response.data; + } + } catch (err) { + console.log(err); + } +}; diff --git a/src/components/Zip/TagBar.tsx b/src/components/Zip/TagBar.tsx index d73ae51..4d6bae7 100644 --- a/src/components/Zip/TagBar.tsx +++ b/src/components/Zip/TagBar.tsx @@ -1,15 +1,22 @@ +import { useEffect, useState } from 'react'; +import { getHashTag } from '../../api/zip.api'; import FilterButton from '../../components/Button/FilterButton'; const TagBar = () => { + const [tags, setTag] = useState<{ tag: string }[]>([]); + useEffect(() => { + getHashTag().then((data) => { + setTag(data.data); + }); + }, []); + return (
- - - - - + {tags.map((tag, index) => ( + + ))}
From 177dccf78b247989279b9dac70af00aaf44d6af9 Mon Sep 17 00:00:00 2001 From: yongaricode Date: Wed, 21 May 2025 16:55:35 +0900 Subject: [PATCH 6/8] =?UTF-8?q?feat:=20=EC=84=9C=EC=A0=90=20search=20?= =?UTF-8?q?=EA=B4=91=EA=B3=A0=20=EB=B0=95=EC=8A=A4=20=EB=A7=81=ED=81=AC=20?= =?UTF-8?q?=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/icons/zip/123.svg | 49 +++++++++++++++++++++++++++++++++ public/icons/zip/22.svg | 15 ++++++++++ src/components/Home/Ranking.tsx | 6 ++-- src/components/Zip/Box.tsx | 33 ++++++++++++---------- 4 files changed, 86 insertions(+), 17 deletions(-) create mode 100644 public/icons/zip/123.svg create mode 100644 public/icons/zip/22.svg diff --git a/public/icons/zip/123.svg b/public/icons/zip/123.svg new file mode 100644 index 0000000..2c4a186 --- /dev/null +++ b/public/icons/zip/123.svg @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/icons/zip/22.svg b/public/icons/zip/22.svg new file mode 100644 index 0000000..c9e06e3 --- /dev/null +++ b/public/icons/zip/22.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/src/components/Home/Ranking.tsx b/src/components/Home/Ranking.tsx index 87a4775..9acd273 100644 --- a/src/components/Home/Ranking.tsx +++ b/src/components/Home/Ranking.tsx @@ -1,7 +1,9 @@ import { useEffect, useState } from 'react'; import { getTrendZip } from '../../api/zip.api'; +import { useNavigate } from 'react-router-dom'; const Ranking = () => { + const nav = useNavigate(); const [bookstores, setBookstores] = useState([ '게으른 정원', '고요서사', @@ -30,7 +32,7 @@ const Ranking = () => { {/* 왼쪽 열 */}
{left.map((bookstore, i) => ( -

+

nav(`/zip?search=${bookstore}`)}> {i + 1}. {bookstore}

))} @@ -39,7 +41,7 @@ const Ranking = () => { {/* 오른쪽 열 */}
{right.map((bookstore, i) => ( -

+

nav(`/zip?search=${bookstore}`)}> {i + 6}. {bookstore}

// 6부터 시작 ))} diff --git a/src/components/Zip/Box.tsx b/src/components/Zip/Box.tsx index 8c5f0d8..4b4b1a7 100644 --- a/src/components/Zip/Box.tsx +++ b/src/components/Zip/Box.tsx @@ -1,6 +1,6 @@ -import Image1 from '../../../public/icons/zip/image1.svg?react'; -import Image2 from '../../../public/icons/zip/image2.svg?react'; +import Image2 from '../../../public/icons/zip/22.svg?react'; import Image3 from '../../../public/icons/zip/image3.svg?react'; +import Image4 from '../../../public/icons/zip/123.svg?react'; const Box = () => { return ( @@ -9,26 +9,29 @@ const Box = () => {
{/* 첫 번째 박스 */} -
- -

- 나에게 -
잘 맞는
- 서점 찾아보기 +

window.open('https://sibf.or.kr/', '_blank')}> + +

+ 2025
+ 서울국제도서전

{/* 두 번째 박스 - 스타일 다르게 가능 */} -
- -

- 유명 연예인들의
- 추천도서 -

+
+ window.open( + 'https://turquoise-dill-eee.notion.site/Pick-1fa2ab1fb8c980049788dac428d46835?pvs=4', + '_blank', + ) + } + > +
{/* 세 번째 박스 */} -
+
window.open('https://payge.kr/speed', '_blank')}>

나의
책 읽는 속도
알아보기 From 438624e2eb685aef29af6341e482f895d5b5615a Mon Sep 17 00:00:00 2001 From: yongaricode Date: Thu, 22 May 2025 08:32:55 +0900 Subject: [PATCH 7/8] =?UTF-8?q?fix:=20reissue=EC=8B=9C=20nickname=EC=9D=B4?= =?UTF-8?q?=20undefined=EB=90=98=EB=8A=94=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/instance.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/api/instance.ts b/src/api/instance.ts index f12f8be..a40063a 100644 --- a/src/api/instance.ts +++ b/src/api/instance.ts @@ -28,6 +28,8 @@ instance.interceptors.response.use( if (accessToken && refreshToken) { localStorage.setItem('accessToken', accessToken); localStorage.setItem('refreshToken', refreshToken); + } + if (nickname) { localStorage.setItem('nickname', nickname); } return response; From ce41b38dcdc2a8cc91574eb3126b3c0863ac28b0 Mon Sep 17 00:00:00 2001 From: yongaricode Date: Thu, 22 May 2025 09:52:22 +0900 Subject: [PATCH 8/8] =?UTF-8?q?feat:=20=EB=A7=88=EC=9D=B4=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EB=82=98=EC=9D=98=20=EC=B1=85=EC=9E=A5=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/api/booksnap.api.ts | 12 ++++++ src/api/mypage.api.ts | 12 ++++++ src/components/Booksnap/BookInfo.tsx | 5 ++- src/components/Common/Header.tsx | 1 + src/components/MyPage/LoginBox.tsx | 2 +- src/pages/MyPage/MyBook.tsx | 64 +++++++++++++++++++++++++++- src/routes/router.tsx | 2 + 7 files changed, 94 insertions(+), 4 deletions(-) create mode 100644 src/api/mypage.api.ts diff --git a/src/api/booksnap.api.ts b/src/api/booksnap.api.ts index 140c6f1..4824c97 100644 --- a/src/api/booksnap.api.ts +++ b/src/api/booksnap.api.ts @@ -103,6 +103,18 @@ export const pickBook = async (bookId: string) => { } }; +// 책 담기 +export const deleteBook = async (bookId: string) => { + try { + const response = await instance.delete(`api/pick-book`, { data: { bookId: bookId } }); + if (response.status == 200) { + return { success: true, message: '책 담기가 취소되었습니다!' }; + } + } catch (error: any) { + console.log(error); + } +}; + // 서점 검색 export const searchBookstore = async (query: string | null) => { try { diff --git a/src/api/mypage.api.ts b/src/api/mypage.api.ts new file mode 100644 index 0000000..934a928 --- /dev/null +++ b/src/api/mypage.api.ts @@ -0,0 +1,12 @@ +import instance from './instance'; + +export const getMyBook = async () => { + try { + const response = await instance.get('/api/pick-book'); + if (response.status == 200) { + return response.data; + } + } catch (err) { + console.log(err); + } +}; diff --git a/src/components/Booksnap/BookInfo.tsx b/src/components/Booksnap/BookInfo.tsx index 3a69570..dc40b65 100644 --- a/src/components/Booksnap/BookInfo.tsx +++ b/src/components/Booksnap/BookInfo.tsx @@ -2,13 +2,14 @@ import { BookDetailInfo } from '../../model/booksnap.model'; import image from '../../../public/icons/book-snap/image.png'; interface BookInfoProps { bookInfo: BookDetailInfo; - onClick: React.MouseEventHandler; + + onClick?: React.MouseEventHandler; } const BookInfo = ({ bookInfo, onClick }: BookInfoProps) => { const author = bookInfo.authors.join(', '); return ( -

+
{ '/booksnap/create/2', '/booksnap/create/indi/2', '/booksnap/create/book', + '/mypage/book', ]; const showBackButton = showBackButtonPaths.includes(location.pathname); diff --git a/src/components/MyPage/LoginBox.tsx b/src/components/MyPage/LoginBox.tsx index da16dac..7f515c5 100644 --- a/src/components/MyPage/LoginBox.tsx +++ b/src/components/MyPage/LoginBox.tsx @@ -36,7 +36,7 @@ const LoginBox = () => {
nav('myBook')} + onClick={() => nav('book')} >

나의 책장

diff --git a/src/pages/MyPage/MyBook.tsx b/src/pages/MyPage/MyBook.tsx index d1fcd30..fea98a9 100644 --- a/src/pages/MyPage/MyBook.tsx +++ b/src/pages/MyPage/MyBook.tsx @@ -1,5 +1,67 @@ +import { useEffect, useState } from 'react'; +import Header from '../../components/Common/Header'; +import SearchBar from '../../components/Zip/SearchBar'; +import { getMyBook } from '../../api/mypage.api'; +import { BookDetailInfo } from '../../model/booksnap.model'; +import BookInfo from '../../components/Booksnap/BookInfo'; +import { deleteBook } from '../../api/booksnap.api'; +import toast from 'react-hot-toast'; +import { MdBookmarkRemove } from 'react-icons/md'; + const MyBook = () => { - return
; + const [searchWord, setSearchWord] = useState(''); + const [books, setBooks] = useState([]); + const [filteredBooks, setFilteredBooks] = useState([]); + + useEffect(() => { + getMyBook().then((data) => { + setBooks(data.pickBooks); + setFilteredBooks(data.pickBooks); + }); + }, []); + + const handleSearch = () => { + const result = books.filter((book) => book.title.toLowerCase().includes(searchWord.toLowerCase())); + setFilteredBooks(result); + }; + + const handlePickBook = (book: BookDetailInfo) => { + deleteBook(book.bookId).then((data) => { + if (data?.success) { + toast.success(`${book.title}을(를) 책장에서 삭제했습니다!`); + + setBooks((prev) => prev.filter((b) => b.bookId !== book.bookId)); + setFilteredBooks((prev) => prev.filter((b) => b.bookId !== book.bookId)); + } else { + toast.error(`${data?.message}`); + } + }); + }; + + return ( +
+
+ handleSearch()} + text="내가 담은 책을 검색해보세요!" + /> +
+ {filteredBooks.map((book) => ( +
+ + +
+ ))} +
+
+ ); }; export default MyBook; diff --git a/src/routes/router.tsx b/src/routes/router.tsx index 4d0230c..af7ff9f 100644 --- a/src/routes/router.tsx +++ b/src/routes/router.tsx @@ -20,6 +20,7 @@ import IndiCreateBookReview1 from '../pages/Booksnap/IndiCreateBookReveiw1'; import IndiCreateBookReview2 from '../pages/Booksnap/IndiCreateBookReview2'; import CreateBook from '../pages/Booksnap/CreateBook'; import Search from '../pages/Zip/Search'; +import MyBook from '../pages/MyPage/MyBook'; export const router = createBrowserRouter([ { @@ -41,6 +42,7 @@ export const router = createBrowserRouter([ { path: '/find-pw', element: }, { path: '/find-pw2', element: }, { path: '/bookie', element: }, + { path: '/mypage/book', element: }, { path: 'zip/create-review', element: ,