From 43cfaee1b61cc02e2dbac5ed39777d58100058e8 Mon Sep 17 00:00:00 2001 From: chanhee Date: Fri, 15 Nov 2024 16:22:16 +0900 Subject: [PATCH 01/11] =?UTF-8?q?=E2=9C=A8feat:=20=EC=95=8C=EB=A6=BC=20api?= =?UTF-8?q?=20=EC=84=B8=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AnnounceNotiComponent.jsx | 8 +++-- .../NotificationPage/GeneralNotiComponent.jsx | 35 +++++++++++++++---- .../NotificationItemComponent.jsx | 2 +- .../src/pages/FriendPage/FriendListPage.jsx | 7 ---- .../NotificationPage/NotificationPage.jsx | 18 ++-------- 5 files changed, 37 insertions(+), 33 deletions(-) diff --git a/bookduck/src/components/NotificationPage/AnnounceNotiComponent.jsx b/bookduck/src/components/NotificationPage/AnnounceNotiComponent.jsx index daa8dac..77ec791 100644 --- a/bookduck/src/components/NotificationPage/AnnounceNotiComponent.jsx +++ b/bookduck/src/components/NotificationPage/AnnounceNotiComponent.jsx @@ -1,6 +1,10 @@ -import React from "react"; +import React, { useState, useEffect } from "react"; import NotificationItemComponent from "./NotificationItemComponent"; -const AnnounceNotiComponent = ({ notifications }) => { +import { patch } from "../../api/example"; +const AnnounceNotiComponent = () => { + //상태 관리 + const [notifications, setNotifications] = useState([]); + return notifications.map((notification) => (
{ - return notifications.map((notification) => ( -
+const GeneralNotiComponent = () => { + //상태 관리 + const [notifications, setNotifications] = useState([]); + //API 연결 + //API-알람 목록 받아오기 + const patchAlarmList = async () => { + try { + const response = await patch(`/alarms`); + console.log(response); + setNotifications(response.alarmList); + } catch (error) { + console.error("알람 읽기 오류", error); + } + }; + + //useEffect 훅 + useEffect(() => { + patchAlarmList(); + console.log(notifications); + }, [notifications]); + + return notifications.map((notification, index) => ( +
)); diff --git a/bookduck/src/components/NotificationPage/NotificationItemComponent.jsx b/bookduck/src/components/NotificationPage/NotificationItemComponent.jsx index a278902..23c0ea1 100644 --- a/bookduck/src/components/NotificationPage/NotificationItemComponent.jsx +++ b/bookduck/src/components/NotificationPage/NotificationItemComponent.jsx @@ -5,7 +5,7 @@ import alarmhand from "../../assets/common/waving-hand-alarm.svg"; import alarmbadge from "../../assets/common/badge-alarm.svg"; import alarmCircle from "../../assets/common/circle-alarm.svg"; const notificationTemplates = { - 친구요청: { + 치: { icon: alarmhand, message: (text) => ( diff --git a/bookduck/src/pages/FriendPage/FriendListPage.jsx b/bookduck/src/pages/FriendPage/FriendListPage.jsx index 243613a..c312155 100644 --- a/bookduck/src/pages/FriendPage/FriendListPage.jsx +++ b/bookduck/src/pages/FriendPage/FriendListPage.jsx @@ -7,13 +7,6 @@ import FriendRequestComponent from "../../components/FriendPage/FriendRequestCom const FriendListPage = () => { const [tab, setTab] = useState("친구"); - // const friendList = [ - // { id: "1", userName: "유저1" }, - // { id: "2", userName: "유저2" }, - // { id: "3", userName: "유저3" }, - // { id: "4", userName: "유저4" }, - // { id: "5", userName: "유저5" }, - // ]; return (
diff --git a/bookduck/src/pages/NotificationPage/NotificationPage.jsx b/bookduck/src/pages/NotificationPage/NotificationPage.jsx index b595852..f20f4cb 100644 --- a/bookduck/src/pages/NotificationPage/NotificationPage.jsx +++ b/bookduck/src/pages/NotificationPage/NotificationPage.jsx @@ -7,18 +7,6 @@ import AnnounceNotiComponent from "../../components/NotificationPage/AnnounceNot const NotificationPage = () => { const [tab, setTab] = useState("일반"); - const [notifications, setNotifications] = useState([]); - - useEffect(() => { - const fetchedNotifications = [ - { id: 1, type: "친구요청", text: "찬희", read: false }, - { id: 2, type: "친구수락", text: "찬희", read: false }, - { id: 3, type: "레벨업", text: "2", read: true }, - { id: 4, type: "기록", text: "찬희", read: true }, - { id: 5, type: "업적", text: "ㅇㅇ", read: true }, - ]; - setNotifications(fetchedNotifications); - }, []); return (
@@ -31,10 +19,8 @@ const NotificationPage = () => { size="small" isNoti={true} /> - {tab === "일반" && } - {tab === "공지" && ( - - )} + {tab === "일반" && } + {tab === "공지" && }
); }; From 5a40f00b0dcc59aba77eff230722889b4eac4deb Mon Sep 17 00:00:00 2001 From: chanhee Date: Fri, 15 Nov 2024 21:10:40 +0900 Subject: [PATCH 02/11] =?UTF-8?q?=E2=9C=A8feat:=20=EC=95=8C=EB=A6=BC=20API?= =?UTF-8?q?=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bookduck/src/assets/common/badge-alarm.svg | 14 ++--- .../src/assets/common/waving-hand-alarm.svg | 6 +- .../NotificationPage/GeneralNotiComponent.jsx | 10 ++-- .../NotificationItemComponent.jsx | 56 +++++++++++++++---- .../src/components/common/TabBarComponent.jsx | 4 +- .../NotificationPage/NotificationPage.jsx | 20 ++++--- 6 files changed, 74 insertions(+), 36 deletions(-) diff --git a/bookduck/src/assets/common/badge-alarm.svg b/bookduck/src/assets/common/badge-alarm.svg index 5844936..c9d9487 100644 --- a/bookduck/src/assets/common/badge-alarm.svg +++ b/bookduck/src/assets/common/badge-alarm.svg @@ -1,9 +1,9 @@ - - - - - - - + + + + + + + \ No newline at end of file diff --git a/bookduck/src/assets/common/waving-hand-alarm.svg b/bookduck/src/assets/common/waving-hand-alarm.svg index 9088c9d..68a224e 100644 --- a/bookduck/src/assets/common/waving-hand-alarm.svg +++ b/bookduck/src/assets/common/waving-hand-alarm.svg @@ -1,7 +1,7 @@ - - + + - + diff --git a/bookduck/src/components/NotificationPage/GeneralNotiComponent.jsx b/bookduck/src/components/NotificationPage/GeneralNotiComponent.jsx index 2019224..2f74a13 100644 --- a/bookduck/src/components/NotificationPage/GeneralNotiComponent.jsx +++ b/bookduck/src/components/NotificationPage/GeneralNotiComponent.jsx @@ -4,11 +4,12 @@ import NotificationItemComponent from "./NotificationItemComponent"; const GeneralNotiComponent = () => { //상태 관리 const [notifications, setNotifications] = useState([]); + //API 연결 //API-알람 목록 받아오기 const patchAlarmList = async () => { try { - const response = await patch(`/alarms`); + const response = await patch(`/alarms/common`); console.log(response); setNotifications(response.alarmList); } catch (error) { @@ -20,14 +21,15 @@ const GeneralNotiComponent = () => { useEffect(() => { patchAlarmList(); console.log(notifications); - }, [notifications]); + }, []); return notifications.map((notification, index) => (
)); diff --git a/bookduck/src/components/NotificationPage/NotificationItemComponent.jsx b/bookduck/src/components/NotificationPage/NotificationItemComponent.jsx index 23c0ea1..bad1db1 100644 --- a/bookduck/src/components/NotificationPage/NotificationItemComponent.jsx +++ b/bookduck/src/components/NotificationPage/NotificationItemComponent.jsx @@ -4,8 +4,9 @@ import alarmheart from "../../assets/common/heart-alarm.svg"; import alarmhand from "../../assets/common/waving-hand-alarm.svg"; import alarmbadge from "../../assets/common/badge-alarm.svg"; import alarmCircle from "../../assets/common/circle-alarm.svg"; + const notificationTemplates = { - 치: { + FRIEND_REQUEST: { icon: alarmhand, message: (text) => ( @@ -14,7 +15,7 @@ const notificationTemplates = { ), }, - 친구수락: { + FRIEND_APPROVED: { icon: alarmhand, message: (text) => ( @@ -23,7 +24,7 @@ const notificationTemplates = { ), }, - 레벨업: { + LEVEL_UP: { icon: alarmduck, message: (text) => ( @@ -32,7 +33,7 @@ const notificationTemplates = { ), }, - 기록: { + ONELINELIKE_ADDED: { icon: alarmheart, message: (text) => ( @@ -41,7 +42,7 @@ const notificationTemplates = { ), }, - 업적: { + BADGE_UNLOCKED: { icon: alarmbadge, message: (text) => ( @@ -53,23 +54,54 @@ const notificationTemplates = { }, }; -const NotificationItemComponent = ({ type, text, read = false }) => { - const notificationData = notificationTemplates[type]; +const formatNotiTime = (rawTime) => { + const now = new Date(); + const past = new Date(rawTime); + + const diff = Math.floor((now - past) / 1000); // 총 시간 차이를 초 단위로 계산 + const weeks = Math.floor(diff / (3600 * 24 * 7)); // 주 단위로 계산 + const days = Math.floor(diff / (3600 * 24)); // 일 단위로 계산 + const hours = Math.floor((diff % (3600 * 24)) / 3600); // 시간 단위로 계산 + const minutes = Math.floor((diff % 3600) / 60); // 분 단위로 계산 + if (weeks > 0) { + return `${weeks}주`; + } else if (days > 0) { + return `${days}일`; + } else if (hours > 0) { + return `${hours}시간`; + } else if (minutes > 0) { + return `${minutes}분`; + } else { + return `방금 전`; + } +}; + +const NotificationItemComponent = ({ + alarmType, + boldText, + isRead, + createdTime, +}) => { + const notificationData = notificationTemplates[alarmType]; + console.log(notificationData); return (
-
- {type} - {!read && ( +
+ {alarmType} + {!isRead && ( )}
-
+
- {notificationData.message(text)} + {notificationData?.message(boldText)} + + + {formatNotiTime(createdTime)}
diff --git a/bookduck/src/components/common/TabBarComponent.jsx b/bookduck/src/components/common/TabBarComponent.jsx index c3d500b..5c04715 100644 --- a/bookduck/src/components/common/TabBarComponent.jsx +++ b/bookduck/src/components/common/TabBarComponent.jsx @@ -14,7 +14,7 @@ const TabBarComponent = ({ onTabClick, size = "big", borderWidth, - isNoti = "false", + isNoti = false, ...props }) => { const isBig = size === "big"; @@ -52,7 +52,7 @@ const TabBarComponent = ({ )}
))} - {!isNoti ? ( + {isNoti ? ( diff --git a/bookduck/src/pages/NotificationPage/NotificationPage.jsx b/bookduck/src/pages/NotificationPage/NotificationPage.jsx index f20f4cb..07f92ed 100644 --- a/bookduck/src/pages/NotificationPage/NotificationPage.jsx +++ b/bookduck/src/pages/NotificationPage/NotificationPage.jsx @@ -9,16 +9,20 @@ const NotificationPage = () => { const [tab, setTab] = useState("일반"); return ( -
+
- +
+ +
+ {tab === "일반" && } {tab === "공지" && }
From 7dac579fd3efebce94f497b75a2ce485a0914fa5 Mon Sep 17 00:00:00 2001 From: chanhee Date: Sat, 16 Nov 2024 21:31:10 +0900 Subject: [PATCH 03/11] =?UTF-8?q?=E2=9C=A8feat:=20=EA=B2=80=EC=83=89=20?= =?UTF-8?q?=EC=B1=85=20=EC=84=9C=EC=9E=AC=20=EB=84=A3=EA=B8=B0(=EB=B0=B1?= =?UTF-8?q?=20=EC=98=A4=EB=A5=98=EB=A1=9C=20=EC=A4=91=EB=8B=A8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SearchPage/SearchBookComponent.jsx | 200 +++++++++++++----- .../src/components/common/ListBottomSheet.jsx | 2 +- bookduck/src/components/common/TextField.jsx | 3 +- .../src/pages/SearchPage/SearchMainPage.jsx | 36 ++-- .../src/pages/SettingPage/SettingPage.jsx | 1 + 5 files changed, 170 insertions(+), 72 deletions(-) diff --git a/bookduck/src/components/SearchPage/SearchBookComponent.jsx b/bookduck/src/components/SearchPage/SearchBookComponent.jsx index 47701ac..041c9a3 100644 --- a/bookduck/src/components/SearchPage/SearchBookComponent.jsx +++ b/bookduck/src/components/SearchPage/SearchBookComponent.jsx @@ -1,56 +1,80 @@ -import React, { useState, useEffect } from "react"; +import React, { useState, useEffect, useRef } from "react"; import { useNavigate } from "react-router-dom"; import BookListView from "../common/BookListView"; -import Divider1 from "../../components/common/Divider1"; import ButtonComponent from "../common/ButtonComponent"; import BottomSheetModal from "../common/BottomSheetModal"; +import Divider1 from "../../components/common/Divider1"; import ListBottomSheet from "../common/ListBottomSheet"; -import { get } from "../../api/example"; +import { get, patch } from "../../api/example"; + const SearchBookComponent = ({ search }) => { const navigate = useNavigate(); - const [isCancel, setIsCancel] = useState(false); const [bottomSheetShow, setBottomSheetShow] = useState(false); const [visible, setVisible] = useState(false); - const [status1, setStatus1] = useState("읽고 싶어요"); - const [status2, setStatus2] = useState("서재에 담기"); + const statusArr = ["읽고 싶어요", "읽고 있어요", "다 읽었어요", "중단했어요"]; + const [currentState, setCurrentStatus] = useState(); + const [selectedBookId, setSelectedBookId] = useState(); + const [isCancel, setIsCancel] = useState(false); + const [registeredBooks, setRegisteredBooks] = useState([]); const [books, setBooks] = useState([]); + const [currentPage, setCurrentPage] = useState(1); + const [totalPages, setTotalPages] = useState(1); + const loaderRef = useRef(null); + const DATA_LIMIT = 10; + + const getReadingStatusKey = (status) => { + switch (status) { + case "읽고 싶어요": + return "NOT_STARTED"; + case "읽고 있어요": + return "READING"; + case "다 읽었어요": + return "FINISHED"; + case "중단했어요": + return "STOPPED"; + default: + return "NOT_STARTED"; + } + }; //API연결 //API-등록 책 정보받기 - const getRegisteredBooks = async (keyword, page, size) => { + const getRegisteredBooks = async (keyword, page = 1) => { try { - let data = []; const response = await get( - `/bookinfo/search/custom?keyword=${keyword}&page=${page}&size=${size}` + `/bookinfo/search/custom?keyword=${encodeURIComponent( + keyword + )}&page=${page}&size=${DATA_LIMIT}` ); - console.log(response); - if (response.bookCount > 0) { - data = response.bookList.map((book) => ({ - id: book.bookinfoId, - title: book.title, - author: book.author, - img: book.imgPath, - myRating: book.myRating, - readStatus: book.readStatus, - })); - } - + console.log("response", response); + const data = response.bookList.map((book) => ({ + bookinfoId: book.bookinfoId, + title: book.title, + author: book.author, + imgPath: book.imgPath, + myRating: book.myRating, + readStatus: book.readStatus, + })); console.log("data", data); - setRegisteredBooks(data); + + setRegisteredBooks((b) => [...b, data]); console.log("registeredBooks:", registeredBooks); } catch (error) { console.error("등록 책 읽어오기 오류", error); } }; - - //API-일반 책 정보받기 - const getBooks = async (keyword, page, size) => { + // API-일반 책 받아오기 + const getBooks = async (keyword, page = 1) => { + if (!keyword) return; try { const response = await get( - `/bookinfo/search?keyword=${keyword}&page=${page}&size=${size}` + `/bookinfo/search?keyword=${encodeURIComponent( + keyword + )}&page=${page}&size=${DATA_LIMIT}` ); + console.log(response); const data = response.bookList.map((book) => ({ id: book.bookinfoId, title: book.title, @@ -59,34 +83,94 @@ const SearchBookComponent = ({ search }) => { myRating: book.myRating, readStatus: book.readStatus, })); - setBooks(data); - console.log("books:", books); + + setBooks((b) => [...b, ...data]); + setTotalPages(response.totalPages || 1); // 서버에서 전체 페이지 수 반환 } catch (error) { - console.error("일반 책 읽어오기 오류", error); + console.error("책 데이터 불러오기 오류:", error); } }; - //useEffect 훅 + //API-직접 등록 책 상태 변경 + const patchRegisteredStatus = async (selectedBookId, currentStatus) => { + try { + await patch(`/books/${selectedBookId}?status=${currentStatus}`); + } catch (error) { + console.error(error); + } + }; + + // useEffect 훅 + // 검색어 변경 시 데이터 초기화 및 첫 페이지 호출 useEffect(() => { if (search) { - // getRegisteredBooks(search, 1, 10); - getBooks(search, 1, 10); + setBooks([]); // 기존 데이터 초기화 + setCurrentPage(1); // 첫 페이지로 초기화 + getBooks(search, 1); + setRegisteredBooks([]); + getRegisteredBooks(search); } }, [search]); + // 무한 스크롤 감지 + useEffect(() => { + const handleObserver = (entries) => { + const [entry] = entries; + if (entry.isIntersecting && currentPage < totalPages) { + console.log("다음 페이지 로드"); + setCurrentPage((p) => p + 1); + } + }; + + const observer = new IntersectionObserver(handleObserver, { + root: null, // viewport 사용 + rootMargin: "0px", + threshold: 1.0, + }); + + if (loaderRef.current) observer.observe(loaderRef.current); + + return () => observer.disconnect(); + }, [currentPage, totalPages]); + + // 현재 페이지 데이터 로드 + useEffect(() => { + if (currentPage > 1 && search) { + console.log(`페이지 ${currentPage} 데이터 로드`); + getBooks(search, currentPage); + } + }, [currentPage, search]); + //이벤트 핸들러 - const handleOpenClick1 = () => { - setIsCancel(false); - setBottomSheetShow(true); - }; + const handleStatusClick = (id) => { + console.log("id", id); + setSelectedBookId(id); - const handleOpenClick2 = (inLibary) => { - setIsCancel(inLibary); + console.log("id", selectedBookId); setBottomSheetShow(true); }; + /*직접 등록 책 처음 등록*/ + const handleStatusRegister = async (status) => { + setCurrentStatus(status); + const res = await patchRegisteredStatus( + selectedBookId, + getReadingStatusKey(status) + ); + console.log(res); + setVisible(false); + setTimeout(() => { + setBottomSheetShow(false); + }, 200); + }; - const handleStatusChange = (newStatus) => { - setStatus(newStatus); + /*직접 등록 책 상태 변경*/ + const handleStatusChange = async (status) => { + setCurrentStatus(status); + const res = await patchRegisteredStatus( + selectedBookId, + getReadingStatusKey(status) + ); + console.log(res); setVisible(false); setTimeout(() => { setBottomSheetShow(false); @@ -98,35 +182,37 @@ const SearchBookComponent = ({ search }) => { {registeredBooks.length > 0 || books.length > 0 ? ( <>
+ {/* 직접 등록한 책 */} {registeredBooks.length > 0 && - registeredBooks.map((book) => ( + registeredBooks.map((book, index) => ( handleStatusClick(book.bookinfoId)} /> ))}
{registeredBooks.length > 0 && books.length > 0 && } + {/* 일반 책 */}
{books.length > 0 && - books.map((book) => ( + books.map((book, index) => ( handleOpenClick2(book.inLibrary)} + handleStatusClick={() => handleStatusClick(book.userBookdId)} /> ))}
@@ -159,13 +245,15 @@ const SearchBookComponent = ({ search }) => { visible={visible} setVisible={setVisible} > - +
+ +
); diff --git a/bookduck/src/components/common/ListBottomSheet.jsx b/bookduck/src/components/common/ListBottomSheet.jsx index 717726e..3296980 100644 --- a/bookduck/src/components/common/ListBottomSheet.jsx +++ b/bookduck/src/components/common/ListBottomSheet.jsx @@ -69,7 +69,7 @@ const ListBottomSheet = ({ {isCancel && (
{cancelText} diff --git a/bookduck/src/components/common/TextField.jsx b/bookduck/src/components/common/TextField.jsx index 8fe1182..2326bd1 100644 --- a/bookduck/src/components/common/TextField.jsx +++ b/bookduck/src/components/common/TextField.jsx @@ -14,6 +14,7 @@ const TextField = ({ check = true, error = null, inputError, + nickname = false, handleEdit, isSubmitted, defaultType, @@ -54,7 +55,7 @@ const TextField = ({
)}
- {!defaultType && !isSubmitted && ( + {nickname && !defaultType && !isSubmitted && (

{ const navigate = useNavigate(); const [search, setSearch] = useState(""); const [submittedSearch, setSubmittedSearch] = useState(""); const [tab, setTab] = useState("책"); - const recentBooks = [ - { id: "1", img: duck, title: "가나다라" }, - { id: "2", img: duck, title: "마바사" }, - { id: "3", img: duck, title: "큐큐" }, - ]; + const [recentBooks, setRecentBooks] = useState([]); + + //API 연결 + const getRecentBooks = async () => { + try { + const response = await get(`/books/recent`); + setRecentBooks(response.bookList); + } catch (error) { + console.error("최근 책 정보 읽기 오류", error); + } + }; + + //useEffect 훅 + useEffect(() => { + getRecentBooks(); + }, []); //이벤트 핸들러 const handleSearch = () => { setSubmittedSearch(search); }; + return (

@@ -41,11 +51,11 @@ const SearchMainPage = () => {
최근 기록한 책
- {recentBooks.map((book) => { + {recentBooks.map((book, index) => { return ( ); @@ -102,8 +112,6 @@ const SearchMainPage = () => { )}
)} - -
); }; diff --git a/bookduck/src/pages/SettingPage/SettingPage.jsx b/bookduck/src/pages/SettingPage/SettingPage.jsx index cedf4ad..35ceeff 100644 --- a/bookduck/src/pages/SettingPage/SettingPage.jsx +++ b/bookduck/src/pages/SettingPage/SettingPage.jsx @@ -329,6 +329,7 @@ const SettingPage = () => { inputError={inputError} handleEdit={handleEdit} isSubmitted={isSubmitted} + nickname={true} defaultType={false} handleValue={(e) => handleValue(e)} inputValue={inputValue} From d47b540fa1156e814eca3ad2414da2d81da80356 Mon Sep 17 00:00:00 2001 From: chanhee Date: Sun, 17 Nov 2024 15:12:25 +0900 Subject: [PATCH 04/11] =?UTF-8?q?=E2=9C=A8feat:=20=EB=A6=AC=EB=94=A9=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EB=B2=84=ED=8A=BC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/MainPage/OneBookCard.jsx | 52 ++++++++++--- .../src/pages/MainPage/SelectCustomPage.jsx | 78 +++++++++++++++---- 2 files changed, 102 insertions(+), 28 deletions(-) diff --git a/bookduck/src/components/MainPage/OneBookCard.jsx b/bookduck/src/components/MainPage/OneBookCard.jsx index 5f246e7..1fa44f4 100644 --- a/bookduck/src/components/MainPage/OneBookCard.jsx +++ b/bookduck/src/components/MainPage/OneBookCard.jsx @@ -1,5 +1,4 @@ import React, { useState, useRef, useEffect } from "react"; -import bookCard from "../../assets/mainPage/bookcard.svg"; import music from "../../assets/mainPage/music.svg"; import infoMusicBox from "../../assets/mainPage/info-musicbox.svg"; import infoMemoBox from "../../assets/mainPage/info-memobox.svg"; @@ -8,19 +7,25 @@ const OneBookCard = ({ setBottomSheetShow, selected, setSelected, + imgPath, bookNumber = 1, readOnly, setVisible, + setEnabled, }) => { + //상태 관리 const singerRef = useRef(null); const memoRef = useRef(null); + const [isMusic, setIsMusic] = useState(true); const [singer, setSinger] = useState(""); const [song, setSong] = useState(""); - const [memo, setMemo] = useState(""); const [bookTitle, setBookTitle] = useState(""); - const [isMusic, setIsMusic] = useState(true); + const [memo, setMemo] = useState(""); + //API-API 연결 + + //useEffect hook useEffect(() => { if (!readOnly && selected === "music") { if (isMusic) { @@ -31,6 +36,13 @@ const OneBookCard = ({ } }, [isMusic, selected]); + useEffect(() => { + if (imgPath && isMusic && singer && song && bookTitle) { + setEnabled(true); + } + }, [imgPath, isMusic, singer, song, bookTitle, memo]); + + //이벤트 핸들러 const handleToggle = () => { setIsMusic((prev) => !prev); }; @@ -84,7 +96,7 @@ const OneBookCard = ({ selected === "firstBook" ? "border-[1px] border-[#6B7FF0]" : "" } flex items-center justify-center w-[5.125rem] h-full bg-gray-custom rounded-[0.375rem] shadow-custom`} > - Book Card + Book Card
{/* 두 번째 북박스 */} @@ -103,7 +115,7 @@ const OneBookCard = ({ selected === "secondBook" ? "border-[1px] border-[#6B7FF0]" : "" } flex items-center justify-center w-[5.125rem] h-full bg-gray-custom rounded-[0.375rem] shadow-custom`} > - Book Card + Book Card
)} @@ -117,30 +129,48 @@ const OneBookCard = ({ > {isMusic ? ( <> -
+

- OST by{" "} + by

+ {!singer && ( +
+ + * + + 가수명 +
+ )} Music Icon
-
+
+ {!song && ( +
+ + * + + + 노래 제목 + +
+ )}
{selected === "music" && ( -
+
{isMusic ? ( Music Box Icon ) : ( diff --git a/bookduck/src/pages/MainPage/SelectCustomPage.jsx b/bookduck/src/pages/MainPage/SelectCustomPage.jsx index fa2f969..28251bc 100644 --- a/bookduck/src/pages/MainPage/SelectCustomPage.jsx +++ b/bookduck/src/pages/MainPage/SelectCustomPage.jsx @@ -1,45 +1,84 @@ -import React, { useState } from "react"; +import React, { useEffect, useState } from "react"; import { useLocation } from "react-router-dom"; +import { get } from "../../api/example"; import StatusBar from "../../components/common/StatusBar"; import Header3 from "../../components/common/Header3"; import OneBookCard from "../../components/MainPage/OneBookCard"; import BottomSheetModal from "../../components/common/BottomSheetModal"; import SearchComponent from "../../components/common/SearchComponent"; import BookListView from "../../components/common/BookListView"; +import ButtonComponent from "../../components/common/ButtonComponent"; const SelectCustomPage = () => { + //상태 관리 const location = useLocation(); const bookNumber = location.state?.bookNumber; - - const cards = [ - { - id: 1, - content: "한번 피면 광장을 보게 되는 책이다.", - title: "가나다", - author: "마바사", - }, - { id: 2, content: "또 다른 책 제목", title: "가나다", author: "마바사" }, - ]; - const [search, setSearch] = useState(""); const [bottomSheetShow, setBottomSheetShow] = useState(true); const [visible, setVisible] = useState(true); const [selected, setSelected] = useState("firstBook"); + const [selectedBookId, setSelectedBookId] = useState(); + const [firstImg, setFirstImg] = useState(); + const [secImg, setSecImg] = useState(); + const [books, setBooks] = useState([]); + const [enabled, setEnabled] = useState(false); + + //API 연결 + const getBooks = async () => { + try { + const response = await get(`/books/list?sort=latest`); + setBooks(response.bookList); + console.log(response); + } catch (error) { + console.error(error); + } + }; + + //useEffect hook= + useEffect(() => { + getBooks(); + }, []); + + useEffect(() => { + if (selectedBookId) { + console.log("선택된 책 ID:", selectedBookId); + } + }, [selectedBookId]); + + useEffect(() => {}); + + //이벤트 핸들러 + const handleStatusClick = (bookInfoId, imgPath) => { + setSelectedBookId(bookInfoId); + setFirstImg(imgPath); + + setVisible(false); + setTimeout(() => { + setBottomSheetShow(false); + }, 200); + }; return ( -
+
+
+

꾸미기 카드는 수정이 불가능해요.

+

꼼꼼히 확인해주세요:)

+ +
{ placeholder="서재에 담긴 책을 검색하세요" />
- {cards.map((card) => ( + {books.map((book, index) => ( + handleStatusClick(book.bookInfoId, book.imgPath) + } /> ))}
From 1204523882dd38cbed804d4f82cee65102576e54 Mon Sep 17 00:00:00 2001 From: chanhee Date: Sun, 17 Nov 2024 19:42:42 +0900 Subject: [PATCH 05/11] =?UTF-8?q?=E2=9C=A8feat:=20=EB=A6=AC=EB=94=A9?= =?UTF-8?q?=EC=8A=A4=ED=8E=98=EC=9D=B4=EC=8A=A4=20api=201=EC=B0=A8=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 --- .../src/components/MainPage/OneBookCard.jsx | 65 ++++++++------ .../components/common/BottomSheetModal.jsx | 31 +++---- .../src/pages/MainPage/SelectCustomPage.jsx | 84 +++++++++++++++---- 3 files changed, 123 insertions(+), 57 deletions(-) diff --git a/bookduck/src/components/MainPage/OneBookCard.jsx b/bookduck/src/components/MainPage/OneBookCard.jsx index 1fa44f4..8b8662e 100644 --- a/bookduck/src/components/MainPage/OneBookCard.jsx +++ b/bookduck/src/components/MainPage/OneBookCard.jsx @@ -1,4 +1,5 @@ import React, { useState, useRef, useEffect } from "react"; +import { post } from "../../api/example"; import music from "../../assets/mainPage/music.svg"; import infoMusicBox from "../../assets/mainPage/info-musicbox.svg"; import infoMemoBox from "../../assets/mainPage/info-memobox.svg"; @@ -7,44 +8,59 @@ const OneBookCard = ({ setBottomSheetShow, selected, setSelected, - imgPath, + bookNumber = 1, + firstImg, + secondImg, + firstId, + secondId, + readOnly, setVisible, - setEnabled, + setEnabled = () => {}, + setCardData = () => {}, }) => { //상태 관리 - const singerRef = useRef(null); - const memoRef = useRef(null); - - const [isMusic, setIsMusic] = useState(true); + const [cardType, setCardType] = useState("BOOK_WITH_SONG"); const [singer, setSinger] = useState(""); const [song, setSong] = useState(""); const [bookTitle, setBookTitle] = useState(""); const [memo, setMemo] = useState(""); - //API-API 연결 - //useEffect hook useEffect(() => { - if (!readOnly && selected === "music") { - if (isMusic) { - singerRef.current?.focus(); + let enabled = false; + if (bookNumber === 1) { + if (cardType === "BOOK_WITH_SONG") { + enabled = Boolean(firstImg && singer && song); } else { - memoRef.current?.focus(); + enabled = Boolean(firstImg && memo); } } - }, [isMusic, selected]); - - useEffect(() => { - if (imgPath && isMusic && singer && song && bookTitle) { - setEnabled(true); + if (bookNumber === 2) { + if (cardType === "BOOK_WITH_SONG") { + enabled = Boolean(firstImg && secondImg && singer && song); + } else { + enabled = Boolean(firstImg && secondImg && memo); + } } - }, [imgPath, isMusic, singer, song, bookTitle, memo]); + console.log(enabled); + setEnabled(enabled); + setCardData({ + cardType: cardType, + resourceId1: firstId, + resourceId2: secondId, + text1: cardType === "BOOK_WITH_SONG" ? singer : memo, + text2: cardType === "BOOK_WITH_SONG" ? song : null, + text3: cardType === "BOOK_WITH_SONG" ? bookTitle : null, + }); + }, [firstImg, secondImg, singer, song, memo, cardType, bookNumber]); //이벤트 핸들러 const handleToggle = () => { - setIsMusic((prev) => !prev); + setCardType((prev) => + prev === "BOOK_WITH_SONG" ? "BOOK_WITH_MEMO" : "BOOK_WITH_SONG" + ); }; const handleChange = (e) => { @@ -68,6 +84,7 @@ const OneBookCard = ({ setBottomSheetShow(true); } }; + const handleMusicClick = () => { if (!readOnly) { setVisible(false); @@ -96,7 +113,7 @@ const OneBookCard = ({ selected === "firstBook" ? "border-[1px] border-[#6B7FF0]" : "" } flex items-center justify-center w-[5.125rem] h-full bg-gray-custom rounded-[0.375rem] shadow-custom`} > - Book Card + Book Card
{/* 두 번째 북박스 */} @@ -115,7 +132,7 @@ const OneBookCard = ({ selected === "secondBook" ? "border-[1px] border-[#6B7FF0]" : "" } flex items-center justify-center w-[5.125rem] h-full bg-gray-custom rounded-[0.375rem] shadow-custom`} > - Book Card + Book Card
)} @@ -127,7 +144,7 @@ const OneBookCard = ({ } ${bookNumber === 1 ? "w-[16.4375rem]" : "w-[10.8125rem]"} py-3 px-4 flex flex-col justify-between bg-gray-10 shadow-custom rounded-[0.75rem]`} > - {isMusic ? ( + {cardType === "BOOK_WITH_SONG" ? ( <>

@@ -136,7 +153,6 @@ const OneBookCard = ({ type="text" value={singer} name="가수명" - ref={singerRef} placeholder="" className="text-c1 text-gray-500 font-semibold bg-gray-10 focus w-[3.25rem]" onChange={handleChange} @@ -186,7 +202,6 @@ const OneBookCard = ({ type="text" value={memo} name="메모" - ref={memoRef} placeholder="글을 메모해보세요" className="text-gray-800 text-c1 bg-gray-10 w-full" onChange={handleChange} @@ -196,7 +211,7 @@ const OneBookCard = ({

{selected === "music" && (
- {isMusic ? ( + {cardType === "BOOK_WITH_SONG" ? ( Music Box Icon ) : ( Memo Box Icon diff --git a/bookduck/src/components/common/BottomSheetModal.jsx b/bookduck/src/components/common/BottomSheetModal.jsx index 349d24b..da801d6 100644 --- a/bookduck/src/components/common/BottomSheetModal.jsx +++ b/bookduck/src/components/common/BottomSheetModal.jsx @@ -32,6 +32,7 @@ const BottomSheetModal = ({ visible, setVisible, children, + custom = false, }) => { // 모달이 열릴 때 visible을 true로 설정해 애니메이션을 실행 useEffect(() => { @@ -53,21 +54,23 @@ const BottomSheetModal = ({ const slideModal = (
-
+ )} + +
e.stopPropagation()} + className={`bg-white ${ + visible ? "animate-slideUp" : "animate-slideDown" + } bg-opacity-100 absolute bottom-0 w-[24.5625rem] h-fit rounded-t-xl pt-8 pb-4 transition-transform shadow-custom duration-300`} > -
e.stopPropagation()} - className={`bg-white ${ - visible ? "animate-slideUp" : "animate-slideDown" - } bg-opacity-100 absolute bottom-0 w-[24.5625rem] h-fit rounded-t-xl pt-8 pb-4 transition-transform duration-300`} - > - {children} -
-
+ {children} +
); diff --git a/bookduck/src/pages/MainPage/SelectCustomPage.jsx b/bookduck/src/pages/MainPage/SelectCustomPage.jsx index 28251bc..4ff8fbd 100644 --- a/bookduck/src/pages/MainPage/SelectCustomPage.jsx +++ b/bookduck/src/pages/MainPage/SelectCustomPage.jsx @@ -1,6 +1,6 @@ import React, { useEffect, useState } from "react"; import { useLocation } from "react-router-dom"; -import { get } from "../../api/example"; +import { get, post } from "../../api/example"; import StatusBar from "../../components/common/StatusBar"; import Header3 from "../../components/common/Header3"; import OneBookCard from "../../components/MainPage/OneBookCard"; @@ -12,18 +12,31 @@ import ButtonComponent from "../../components/common/ButtonComponent"; const SelectCustomPage = () => { //상태 관리 const location = useLocation(); + const bookNumber = location.state?.bookNumber; + const [selected, setSelected] = useState("firstBook"); + const [firstImg, setFirstImg] = useState(); + const [secondImg, setSecondImg] = useState(); + const [firstId, setFirstId] = useState(); + const [secondId, setSecondId] = useState(); + const [search, setSearch] = useState(""); const [bottomSheetShow, setBottomSheetShow] = useState(true); const [visible, setVisible] = useState(true); - const [selected, setSelected] = useState("firstBook"); - const [selectedBookId, setSelectedBookId] = useState(); - const [firstImg, setFirstImg] = useState(); - const [secImg, setSecImg] = useState(); + const [books, setBooks] = useState([]); const [enabled, setEnabled] = useState(false); + const [cardData, setCardData] = useState({ + cardType: "BOOK_WITH_SONG", + resourceId1: null, + text1: "", + text2: "", + text3: "", + }); + //API 연결 + //리스트 책 받아오기 const getBooks = async () => { try { const response = await get(`/books/list?sort=latest`); @@ -34,30 +47,55 @@ const SelectCustomPage = () => { } }; - //useEffect hook= + // 카드 등록하기 + const postCard = async () => { + try { + const response = await post(`/readingspace`, cardData); + console.log("Card successfully posted:", response); + } catch (error) { + console.error("Error posting card:", error); + } + }; + + //useEffect hook useEffect(() => { getBooks(); }, []); + //첫번째 책 아이디 + useEffect(() => { + if (firstId) { + console.log("선택된 책 ID 1:", firstId); + } + }, [firstId]); + + //두번째 책 아이디 useEffect(() => { - if (selectedBookId) { - console.log("선택된 책 ID:", selectedBookId); + if (secondId) { + console.log("선택된 책 ID 2:", secondId); } - }, [selectedBookId]); + }, [secondId]); - useEffect(() => {}); + //enabled + useEffect(() => { + console.log(enabled); + }, [enabled]); //이벤트 핸들러 - const handleStatusClick = (bookInfoId, imgPath) => { - setSelectedBookId(bookInfoId); - setFirstImg(imgPath); + const handleStatusClick = (bookInfoId, bookImg) => { + if (selected === "firstBook") { + setFirstId(bookInfoId); + setFirstImg(bookImg); + } else if (selected === "secondBook") { + setSecondId(bookInfoId); + setSecondImg(bookImg); + } setVisible(false); setTimeout(() => { setBottomSheetShow(false); }, 200); }; - return (
@@ -65,19 +103,28 @@ const SelectCustomPage = () => {
-
+

꾸미기 카드는 수정이 불가능해요.

꼼꼼히 확인해주세요:)

- +
{ bottomSheetShow={bottomSheetShow} setBottomSheetShow={setBottomSheetShow} visible={visible} + custom={true} setVisible={setVisible} > Date: Sun, 17 Nov 2024 19:54:49 +0900 Subject: [PATCH 06/11] =?UTF-8?q?=E2=99=BB=EF=B8=8Frefactor:=20oneBookCard?= =?UTF-8?q?=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bookduck/src/components/MainPage/OneBookCard.jsx | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/bookduck/src/components/MainPage/OneBookCard.jsx b/bookduck/src/components/MainPage/OneBookCard.jsx index 8b8662e..48dde41 100644 --- a/bookduck/src/components/MainPage/OneBookCard.jsx +++ b/bookduck/src/components/MainPage/OneBookCard.jsx @@ -209,15 +209,13 @@ const OneBookCard = ({ )}
- {selected === "music" && ( -
- {cardType === "BOOK_WITH_SONG" ? ( - Music Box Icon - ) : ( - Memo Box Icon - )} -
- )} +
+ {cardType === "BOOK_WITH_SONG" ? ( + Music Box Icon + ) : ( + Memo Box Icon + )} +
); }; From 2656818718518fea121a70376ab4fc9a7b20b5b8 Mon Sep 17 00:00:00 2001 From: chanhee Date: Sun, 17 Nov 2024 19:55:11 +0900 Subject: [PATCH 07/11] =?UTF-8?q?=E2=9C=8F=EF=B8=8Ffix:=20=EB=A6=AC?= =?UTF-8?q?=EB=94=A9=EC=8A=A4=ED=8E=98=EC=9D=B4=EC=8A=A4=20=EC=BC=80?= =?UTF-8?q?=EB=B0=A5=EB=B0=94=20=EC=9C=A0=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/MainPage/ReadingSpaceComponent.jsx | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/bookduck/src/components/MainPage/ReadingSpaceComponent.jsx b/bookduck/src/components/MainPage/ReadingSpaceComponent.jsx index d4e0368..b32850e 100644 --- a/bookduck/src/components/MainPage/ReadingSpaceComponent.jsx +++ b/bookduck/src/components/MainPage/ReadingSpaceComponent.jsx @@ -90,6 +90,9 @@ const ReadingSpaceComponent = ({ setColor, setIsNavBar }) => { setBottomSheetShow(true); }; const handleEditClick = () => { + if (height.get() < expandedHeight) { + api.start({ height: expandedHeight }); + } setIsEditMode(true); }; @@ -131,12 +134,11 @@ const ReadingSpaceComponent = ({ setColor, setIsNavBar }) => { {!isEditMode && isHelpVisible && ( )} - {isHelp && ( - - )} +
{/*
From 8b55beb267c2296d87f05aed2c37e08b8626abd3 Mon Sep 17 00:00:00 2001 From: chanhee Date: Sun, 17 Nov 2024 21:15:04 +0900 Subject: [PATCH 08/11] =?UTF-8?q?=E2=9C=A8feat:=EB=B0=9C=EC=B7=8C=20?= =?UTF-8?q?=EC=B9=B4=EB=93=9C=20=EB=93=B1=EB=A1=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/MainPage/OneBookCard.jsx | 16 ++- .../MainPage/ReadingSpaceComponent.jsx | 51 ++++++-- .../SearchPage/SearchBookComponent.jsx | 1 + .../src/pages/MainPage/SelectExtractPage.jsx | 121 +++++++++++++----- 4 files changed, 138 insertions(+), 51 deletions(-) diff --git a/bookduck/src/components/MainPage/OneBookCard.jsx b/bookduck/src/components/MainPage/OneBookCard.jsx index 48dde41..8b8662e 100644 --- a/bookduck/src/components/MainPage/OneBookCard.jsx +++ b/bookduck/src/components/MainPage/OneBookCard.jsx @@ -209,13 +209,15 @@ const OneBookCard = ({ )}
-
- {cardType === "BOOK_WITH_SONG" ? ( - Music Box Icon - ) : ( - Memo Box Icon - )} -
+ {selected === "music" && ( +
+ {cardType === "BOOK_WITH_SONG" ? ( + Music Box Icon + ) : ( + Memo Box Icon + )} +
+ )}
); }; diff --git a/bookduck/src/components/MainPage/ReadingSpaceComponent.jsx b/bookduck/src/components/MainPage/ReadingSpaceComponent.jsx index b32850e..568d91f 100644 --- a/bookduck/src/components/MainPage/ReadingSpaceComponent.jsx +++ b/bookduck/src/components/MainPage/ReadingSpaceComponent.jsx @@ -69,6 +69,7 @@ const ReadingSpaceComponent = ({ setColor, setIsNavBar }) => { setCards(reorderedCards); }; + //useEffect 훅 useEffect(() => { if (isEditMode) { @@ -87,13 +88,18 @@ const ReadingSpaceComponent = ({ setColor, setIsNavBar }) => { setIsHelp(!isHelp); }; const handleMenuClick = () => { - setBottomSheetShow(true); - }; - const handleEditClick = () => { if (height.get() < expandedHeight) { api.start({ height: expandedHeight }); } + setBottomSheetShow(true); + }; + + const handleEditClick = () => { setIsEditMode(true); + setVisible(false); // 닫는 애니메이션 시작 + setTimeout(() => { + setBottomSheetShow(false); // 애니메이션이 끝난 후 모달 완전히 닫기 + }, 200); }; const handleDelete = () => { @@ -128,25 +134,25 @@ const ReadingSpaceComponent = ({ setColor, setIsNavBar }) => {
-
+

리딩 스페이스

{!isEditMode && isHelpVisible && ( )} - + + {isHelp && ( + + )}
{/*
{isEditMode ? "편집" : "완료"}
*/} - {isHelpVisible && ( - menu - )} + menu
@@ -210,6 +216,22 @@ const ReadingSpaceComponent = ({ setColor, setIsNavBar }) => { )}
+ {isEditMode && height.get() === expandedHeight && ( +
+ + +
+ )} {isFloatingVisible && (
@@ -234,7 +256,10 @@ const ReadingSpaceComponent = ({ setColor, setIsNavBar }) => { >
-
+
navigate("/selectcard")} + > 추가하기
diff --git a/bookduck/src/components/SearchPage/SearchBookComponent.jsx b/bookduck/src/components/SearchPage/SearchBookComponent.jsx index 041c9a3..9cbea34 100644 --- a/bookduck/src/components/SearchPage/SearchBookComponent.jsx +++ b/bookduck/src/components/SearchPage/SearchBookComponent.jsx @@ -149,6 +149,7 @@ const SearchBookComponent = ({ search }) => { console.log("id", selectedBookId); setBottomSheetShow(true); }; + /*직접 등록 책 처음 등록*/ const handleStatusRegister = async (status) => { setCurrentStatus(status); diff --git a/bookduck/src/pages/MainPage/SelectExtractPage.jsx b/bookduck/src/pages/MainPage/SelectExtractPage.jsx index 62a0df4..a4dd5d6 100644 --- a/bookduck/src/pages/MainPage/SelectExtractPage.jsx +++ b/bookduck/src/pages/MainPage/SelectExtractPage.jsx @@ -1,4 +1,5 @@ -import React, { useState } from "react"; +import React, { useState, useEffect, useRef } from "react"; +import { get } from "../../api/example"; import StatusBar from "../../components/common/StatusBar"; import Header3 from "../../components/common/Header3"; import SearchComponent from "../../components/common/SearchComponent"; @@ -6,23 +7,85 @@ import ExtractCard from "../../components/MainPage/ExtractCard"; import ButtonComponent from "../../components/common/ButtonComponent"; const SelectExtractPage = () => { + //상태 관리 const [search, setSearch] = useState(""); - const [selectedCard, setSelectedCard] = useState(""); + const [excerpts, setExcerpts] = useState([]); + const [excerptId, setExcerptId] = useState(); + const [currentPage, setCurrentPage] = useState(1); + const [totalPages, setTotalPages] = useState(1); + const loaderRef = useRef(null); + const DATA_LIMIT = 10; + //API 연결 + //리스트 책 받아오기 + const getExcerpts = async (keyword, page = 1) => { + try { + const response = await get( + `/readingspace/excerpts/search?keyword=${keyword}&page=${page}&size=${DATA_LIMIT}` + ); + + console.log("response", response); + const data = response.excerptList.map((excerpt) => ({ + visibility: excerpt.visibility, + pageNumber: excerpt.pageNumber, + excerptContext: excerpt.excerptContext, + })); + console.log("data", data); + setExcerpts((e) => [...e, data]); + setTotalPages(response.totalPages || 1); // 서버에서 전체 페이지 수 반환 + } catch (error) { + console.error(error); + } + }; + + // useEffect 훅 + // 검색어 변경 시 데이터 초기화 및 첫 페이지 호출 + + useEffect(() => { + getExcerpts(); + }, []); + + useEffect(() => { + if (search) { + setExcerpts([]); // 기존 데이터 초기화 + setCurrentPage(1); // 첫 페이지로 초기화 + getExcerpts(search, 1); + } + }, [search]); + + // 무한 스크롤 감지 + useEffect(() => { + const handleObserver = (entries) => { + const [entry] = entries; + if (entry.isIntersecting && currentPage < totalPages) { + console.log("다음 페이지 로드"); + setCurrentPage((p) => p + 1); + } + }; + + const observer = new IntersectionObserver(handleObserver, { + root: null, // viewport 사용 + rootMargin: "0px", + threshold: 1.0, + }); + + if (loaderRef.current) observer.observe(loaderRef.current); + + return () => observer.disconnect(); + }, [currentPage, totalPages]); + + // 현재 페이지 데이터 로드 + useEffect(() => { + if (currentPage > 1 && search) { + console.log(`페이지 ${currentPage} 데이터 로드`); + getBooks(search, currentPage); + } + }, [currentPage, search]); + + //이벤트 핸들러 const handleSelectCard = (id) => { setSelectedCard(id); }; - const cards = [ - { - id: 1, - content: "한번 피면 광장을 보게 되는 책이다.", - title: "가나다", - author: "마바사", - }, - { id: 2, content: "또 다른 책 제목", title: "가나다", author: "마바사" }, - { id: 3, content: "다른 책 제목", title: "가나다", author: "마바사" }, - { id: 4, content: "다른 책 제목", title: "가나다", author: "마바사" }, - { id: 5, content: "다른 책 제목", title: "가나다", author: "마바사" }, - ]; + return (
@@ -35,25 +98,21 @@ const SelectExtractPage = () => { />
- {cards.map((card) => ( - handleSelectCard(card.id)} - key={card.id} - selected={selectedCard === card.id ? true : false} - title={card.title} - content={card.content} - page={card.page} - author={card.author} - /> - ))} + {excerpts.totalElements > 0 && + excerpts.map((excerpt, index) => ( + handleSelectCard(excerpt.excerptId)} + selected={excerptId === excerpt.excerptId ? true : false} + excerptContext={excerpt.excerptContext} + visibility={excerpt.visibility} + pageNumber={excerpt.pageNumber} + /> + ))}
- {selectedCard && ( + {excerptId && (
- +
)}
From 89331a4e157b39945e19fb032178f7dbdc7f4f52 Mon Sep 17 00:00:00 2001 From: chanhee Date: Sun, 17 Nov 2024 21:51:34 +0900 Subject: [PATCH 09/11] =?UTF-8?q?=E2=99=BB=EF=B8=8Frefactor:=20=EB=A6=AC?= =?UTF-8?q?=EB=94=A9=EC=8A=A4=ED=8E=98=EC=9D=B4=EC=8A=A4=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 --- .../MainPage/ReadingSpaceComponent.jsx | 30 +++++++++++++++++-- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/bookduck/src/components/MainPage/ReadingSpaceComponent.jsx b/bookduck/src/components/MainPage/ReadingSpaceComponent.jsx index 568d91f..03be1e6 100644 --- a/bookduck/src/components/MainPage/ReadingSpaceComponent.jsx +++ b/bookduck/src/components/MainPage/ReadingSpaceComponent.jsx @@ -20,6 +20,7 @@ import helpCircle from "../../assets/mainPage/help-circle.svg"; const ReadingSpaceComponent = ({ setColor, setIsNavBar }) => { const navigate = useNavigate(); const [showDeleteModal, setShowDeleteModal] = useState(false); + const [showOutModal, setShowOutModal] = useState(false); const [isEditMode, setIsEditMode] = useState(false); const [isHelp, setIsHelp] = useState(false); const [bottomSheetShow, setBottomSheetShow] = useState(false); @@ -84,6 +85,11 @@ const ReadingSpaceComponent = ({ setColor, setIsNavBar }) => { setShowDeleteModal(false); }; + const handleOutModal = () => { + setShowOutModal(false); + handleEditMode(); + }; + const handleHelpClick = () => { setIsHelp(!isHelp); }; @@ -119,6 +125,10 @@ const ReadingSpaceComponent = ({ setColor, setIsNavBar }) => { } }; + const handleOutClick = () => { + setShowOutModal(true); + }; + return ( <>
@@ -134,12 +144,17 @@ const ReadingSpaceComponent = ({ setColor, setIsNavBar }) => {
-
+
-

리딩 스페이스

+

리딩 스페이스

{!isEditMode && isHelpVisible && ( )} + {isEditMode && ( +
+ 카드를 드래그하여 이동해보세요 +
+ )} {isHelp && ( {
@@ -298,6 +313,15 @@ const ReadingSpaceComponent = ({ setColor, setIsNavBar }) => { onRightClick={handleDeleteModal} /> )} + {showOutModal && ( + {}} + /> + )} ); }; From 7cc968da2226a10a9d915c3b8f8a682eefcb4939 Mon Sep 17 00:00:00 2001 From: chanhee Date: Mon, 18 Nov 2024 13:30:23 +0900 Subject: [PATCH 10/11] =?UTF-8?q?=E2=9C=A8feat:=20=EB=A6=AC=EB=94=A9?= =?UTF-8?q?=EC=8A=A4=ED=8E=98=EC=9D=B4=EC=8A=A4=20=EC=B9=B4=EB=93=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/MainPage/ExtractCard.jsx | 12 +- .../src/components/MainPage/OneBookCard.jsx | 6 +- .../MainPage/ReadingSpaceComponent.jsx | 29 +--- .../src/components/MainPage/ReviewCard.jsx | 4 +- bookduck/src/pages/MainPage/MainPage.jsx | 28 ++- .../src/pages/MainPage/SelectCustomPage.jsx | 2 + .../src/pages/MainPage/SelectExtractPage.jsx | 79 ++++++--- .../src/pages/MainPage/SelectReviewPage.jsx | 163 ++++++++++++------ 8 files changed, 225 insertions(+), 98 deletions(-) diff --git a/bookduck/src/components/MainPage/ExtractCard.jsx b/bookduck/src/components/MainPage/ExtractCard.jsx index 17811c5..1f24157 100644 --- a/bookduck/src/components/MainPage/ExtractCard.jsx +++ b/bookduck/src/components/MainPage/ExtractCard.jsx @@ -1,6 +1,14 @@ import { useNavigate } from "react-router-dom"; -const ExtractCard = ({ content, title, author, page, onClick, selected }) => { +const ExtractCard = ({ + onClick, + selected, + content, + visibility, + pageNumber, + title, + author, +}) => { const navigate = useNavigate(); return ( @@ -10,7 +18,7 @@ const ExtractCard = ({ content, title, author, page, onClick, selected }) => { ${selected && " border-[1px] border-[#6B7FF0]"}`} >
- {page || "페이지"}p + {pageNumber || "페이지"}p
diff --git a/bookduck/src/components/MainPage/OneBookCard.jsx b/bookduck/src/components/MainPage/OneBookCard.jsx index 8b8662e..a71b656 100644 --- a/bookduck/src/components/MainPage/OneBookCard.jsx +++ b/bookduck/src/components/MainPage/OneBookCard.jsx @@ -49,13 +49,17 @@ const OneBookCard = ({ setCardData({ cardType: cardType, resourceId1: firstId, - resourceId2: secondId, + resourceId2: secondId || null, text1: cardType === "BOOK_WITH_SONG" ? singer : memo, text2: cardType === "BOOK_WITH_SONG" ? song : null, text3: cardType === "BOOK_WITH_SONG" ? bookTitle : null, }); }, [firstImg, secondImg, singer, song, memo, cardType, bookNumber]); + useEffect(() => { + console.log(bookTitle); + }, [bookTitle]); + //이벤트 핸들러 const handleToggle = () => { setCardType((prev) => diff --git a/bookduck/src/components/MainPage/ReadingSpaceComponent.jsx b/bookduck/src/components/MainPage/ReadingSpaceComponent.jsx index 03be1e6..14b5d64 100644 --- a/bookduck/src/components/MainPage/ReadingSpaceComponent.jsx +++ b/bookduck/src/components/MainPage/ReadingSpaceComponent.jsx @@ -17,10 +17,14 @@ import deleteIcon from "../../assets/bookinfoPage/trash.svg"; import plusIcon from "../../assets/mainPage/plus.svg"; import helpCircle from "../../assets/mainPage/help-circle.svg"; -const ReadingSpaceComponent = ({ setColor, setIsNavBar }) => { +const ReadingSpaceComponent = ({ + setColor, + setIsNavBar, + setShowDeleteModal = () => {}, + setShowOutModal = () => {}, +}) => { const navigate = useNavigate(); - const [showDeleteModal, setShowDeleteModal] = useState(false); - const [showOutModal, setShowOutModal] = useState(false); + const [isEditMode, setIsEditMode] = useState(false); const [isHelp, setIsHelp] = useState(false); const [bottomSheetShow, setBottomSheetShow] = useState(false); @@ -303,25 +307,6 @@ const ReadingSpaceComponent = ({ setColor, setIsNavBar }) => {
- {showDeleteModal && ( - {}} - onRightClick={handleDeleteModal} - /> - )} - {showOutModal && ( - {}} - /> - )} ); }; diff --git a/bookduck/src/components/MainPage/ReviewCard.jsx b/bookduck/src/components/MainPage/ReviewCard.jsx index aa1550d..27eb8d3 100644 --- a/bookduck/src/components/MainPage/ReviewCard.jsx +++ b/bookduck/src/components/MainPage/ReviewCard.jsx @@ -1,7 +1,7 @@ import React from "react"; import emptyImage from "../../assets/recordingPage/rating-empty.svg"; import filledImage from "../../assets/recordingPage/rating-filled.svg"; -const ReviewCard = ({ selected, review, rating, title, author, onClick }) => { +const ReviewCard = ({ selected, content, rating, title, author, onClick }) => { return (
{

- {review || "한줄평 카드를 선택해주세요"} + {content || "한줄평 카드를 선택해주세요"}

{title || "제목"} / {author || "작가"} diff --git a/bookduck/src/pages/MainPage/MainPage.jsx b/bookduck/src/pages/MainPage/MainPage.jsx index ee2db43..0b51e4a 100644 --- a/bookduck/src/pages/MainPage/MainPage.jsx +++ b/bookduck/src/pages/MainPage/MainPage.jsx @@ -17,6 +17,8 @@ const MainPage = () => { const [userInfo, setUserInfo] = useState(null); const [color, setColor] = useState("bg-gray-50"); const [isNavBar, setIsNavBar] = useState("true"); + const [showDeleteModal, setShowDeleteModal] = useState(false); + const [showOutModal, setShowOutModal] = useState(false); //API 연결 const getUserInfo = async (userId) => { @@ -77,9 +79,33 @@ const MainPage = () => { arrow - +

{isNavBar && } + {showDeleteModal && ( + {}} + onRightClick={handleDeleteModal} + /> + )} + {showOutModal && ( + {}} + /> + )}
); }; diff --git a/bookduck/src/pages/MainPage/SelectCustomPage.jsx b/bookduck/src/pages/MainPage/SelectCustomPage.jsx index 4ff8fbd..4e2cb6d 100644 --- a/bookduck/src/pages/MainPage/SelectCustomPage.jsx +++ b/bookduck/src/pages/MainPage/SelectCustomPage.jsx @@ -30,6 +30,7 @@ const SelectCustomPage = () => { const [cardData, setCardData] = useState({ cardType: "BOOK_WITH_SONG", resourceId1: null, + resourceId2: null, text1: "", text2: "", text3: "", @@ -50,6 +51,7 @@ const SelectCustomPage = () => { // 카드 등록하기 const postCard = async () => { try { + console.log(cardData); const response = await post(`/readingspace`, cardData); console.log("Card successfully posted:", response); } catch (error) { diff --git a/bookduck/src/pages/MainPage/SelectExtractPage.jsx b/bookduck/src/pages/MainPage/SelectExtractPage.jsx index a4dd5d6..88531d3 100644 --- a/bookduck/src/pages/MainPage/SelectExtractPage.jsx +++ b/bookduck/src/pages/MainPage/SelectExtractPage.jsx @@ -1,5 +1,5 @@ import React, { useState, useEffect, useRef } from "react"; -import { get } from "../../api/example"; +import { get, post } from "../../api/example"; import StatusBar from "../../components/common/StatusBar"; import Header3 from "../../components/common/Header3"; import SearchComponent from "../../components/common/SearchComponent"; @@ -11,44 +11,71 @@ const SelectExtractPage = () => { const [search, setSearch] = useState(""); const [excerpts, setExcerpts] = useState([]); const [excerptId, setExcerptId] = useState(); - const [currentPage, setCurrentPage] = useState(1); - const [totalPages, setTotalPages] = useState(1); + const [currentPage, setCurrentPage] = useState(0); + const [totalPages, setTotalPages] = useState(0); + const [cardData, setCardData] = useState({ + cardType: "EXCERPT", + }); + const loaderRef = useRef(null); const DATA_LIMIT = 10; + //API 연결 - //리스트 책 받아오기 - const getExcerpts = async (keyword, page = 1) => { + //발췌 리스트 받아오기 + const getExcerpts = async (keyword, page = 0) => { try { const response = await get( `/readingspace/excerpts/search?keyword=${keyword}&page=${page}&size=${DATA_LIMIT}` ); console.log("response", response); - const data = response.excerptList.map((excerpt) => ({ + const data = response.pageContent.map((excerpt) => ({ + author: excerpt.author, + excerptContent: excerpt.excerptContent, + excerptId: excerpt.excerptId, visibility: excerpt.visibility, pageNumber: excerpt.pageNumber, - excerptContext: excerpt.excerptContext, + title: excerpt.title, + author: excerpt.author, })); - console.log("data", data); - setExcerpts((e) => [...e, data]); - setTotalPages(response.totalPages || 1); // 서버에서 전체 페이지 수 반환 + + if (page === 0) { + // 첫 페이지인 경우 기존 데이터 초기화 + setExcerpts(data); + } else { + // 다음 페이지 데이터를 추가 + setExcerpts((prev) => [...prev, ...data]); + } + + setTotalPages(response.totalPages || 0); // 서버에서 전체 페이지 수 반환 } catch (error) { console.error(error); } }; + // 카드 등록하기 + const postCard = async () => { + try { + console.log(cardData); + const response = await post(`/readingspace`, cardData); + console.log("Card successfully posted:", response); + } catch (error) { + console.error("Error posting card:", error); + } + }; + // useEffect 훅 // 검색어 변경 시 데이터 초기화 및 첫 페이지 호출 useEffect(() => { - getExcerpts(); - }, []); + console.log("excerpts", excerpts); + }, [excerpts]); useEffect(() => { if (search) { setExcerpts([]); // 기존 데이터 초기화 - setCurrentPage(1); // 첫 페이지로 초기화 - getExcerpts(search, 1); + setCurrentPage(0); // 첫 페이지로 초기화 + getExcerpts(search, 0); } }, [search]); @@ -75,15 +102,20 @@ const SelectExtractPage = () => { // 현재 페이지 데이터 로드 useEffect(() => { - if (currentPage > 1 && search) { + if (currentPage > 0 && search) { console.log(`페이지 ${currentPage} 데이터 로드`); - getBooks(search, currentPage); + getExcerpts(search, currentPage); } }, [currentPage, search]); + //카드 데이터 업데이트 + useEffect(() => { + setCardData((c) => ({ ...c, resourceId1: excerptId })); + }, [excerptId]); + //이벤트 핸들러 const handleSelectCard = (id) => { - setSelectedCard(id); + setExcerptId(id); }; return ( @@ -98,21 +130,28 @@ const SelectExtractPage = () => { />
- {excerpts.totalElements > 0 && + {excerpts.length > 0 && excerpts.map((excerpt, index) => ( handleSelectCard(excerpt.excerptId)} selected={excerptId === excerpt.excerptId ? true : false} - excerptContext={excerpt.excerptContext} + content={excerpt.excerptContent} visibility={excerpt.visibility} pageNumber={excerpt.pageNumber} + title={excerpt.title} + author={excerpt.author} /> ))}
{excerptId && (
- +
)}
diff --git a/bookduck/src/pages/MainPage/SelectReviewPage.jsx b/bookduck/src/pages/MainPage/SelectReviewPage.jsx index d9f0395..4a2c663 100644 --- a/bookduck/src/pages/MainPage/SelectReviewPage.jsx +++ b/bookduck/src/pages/MainPage/SelectReviewPage.jsx @@ -1,4 +1,5 @@ -import React, { useState } from "react"; +import React, { useState, useEffect, useRef } from "react"; +import { get, post } from "../../api/example"; import StatusBar from "../../components/common/StatusBar"; import Header3 from "../../components/common/Header3"; import SearchComponent from "../../components/common/SearchComponent"; @@ -6,49 +7,110 @@ import ReviewCard from "../../components/MainPage/ReviewCard"; import ButtonComponent from "../../components/common/ButtonComponent"; const SelectReviewPage = () => { + //상태 관리 const [search, setSearch] = useState(""); - const [selectedCard, setSelectedCard] = useState(""); + const [reviews, setReviews] = useState([]); + const [reviewId, setReviewId] = useState(); + const [currentPage, setCurrentPage] = useState(0); + const [totalPages, setTotalPages] = useState(0); + const loaderRef = useRef(null); + const DATA_LIMIT = 10; + const [cardData, setCardData] = useState({ + cardType: "ONELINE", + }); + + //API 연결 + //리뷰 리스트 받아오기 + const getReviews = async (keyword, page = 0) => { + try { + const response = await get( + `/readingspace/onelines/search?keyword=${keyword}&page=${page}&size=${DATA_LIMIT}` + ); + console.log("response", response); + const data = response.pageContent.map((review) => ({ + oneLineId: review.oneLineId, + oneLineContent: review.oneLineContent, + rating: review.rating, + title: review.title, + author: review.author, + })); + if (page === 0) { + // 첫 페이지인 경우 기존 데이터 초기화 + setReviews(data); + } else { + // 다음 페이지 데이터를 추가 + setReviews((prev) => [...prev, ...data]); + } + + setTotalPages(response.totalPages || 0); // 서버에서 전체 페이지 수 반환 + } catch (error) { + console.error(error); + } + }; + + // 카드 등록하기 + const postCard = async () => { + try { + console.log(cardData); + const response = await post(`/readingspace`, cardData); + console.log("Card successfully posted:", response); + } catch (error) { + console.error("Error posting card:", error); + } + }; + + //useEffect 훅 + useEffect(() => { + console.log("reviews", reviews); + }, [reviews]); + + useEffect(() => { + if (search) { + setReviews([]); // 기존 데이터 초기화 + setCurrentPage(0); // 첫 페이지로 초기화 + getReviews(search, 0); + } + }, [search]); + + // 무한 스크롤 감지 + useEffect(() => { + const handleObserver = (entries) => { + const [entry] = entries; + if (entry.isIntersecting && currentPage < totalPages) { + console.log("다음 페이지 로드"); + setCurrentPage((p) => p + 1); + } + }; + + const observer = new IntersectionObserver(handleObserver, { + root: null, // viewport 사용 + rootMargin: "0px", + threshold: 1.0, + }); + + if (loaderRef.current) observer.observe(loaderRef.current); + + return () => observer.disconnect(); + }, [currentPage, totalPages]); + + // 현재 페이지 데이터 로드 + useEffect(() => { + if (currentPage > 0 && search) { + console.log(`페이지 ${currentPage} 데이터 로드`); + getBooks(search, currentPage); + } + }, [currentPage, search]); + + //카드 데이터 업데이트 + useEffect(() => { + setCardData((c) => ({ ...c, resourceId1: reviewId })); + }, [reviewId]); + + //이벤트 핸들러 const handleSelectCard = (id) => { - setSelectedCard(id); + setReviewId(id); }; - const cards = [ - { - id: 1, - rating: 2, - review: "한번 피면 광장을 보게 되는 책이다.", - title: "가나다", - author: "마바사", - }, - { - id: 2, - review: "또 다른 책 제목", - rating: 2, - title: "가나다", - author: "마바사", - }, - { - id: 3, - review: "다른 책 제목", - rating: 2, - title: "가나다", - author: "마바사", - }, - { - id: 4, - review: "다른 책 제목", - rating: 2, - title: "가나다", - author: "마바사", - }, - { - id: 5, - review: "다른 책 제목", - rating: 2, - title: "가나다", - author: "마바사", - }, - ]; return (
@@ -61,24 +123,25 @@ const SelectReviewPage = () => { />
- {cards.map((card) => ( + {reviews.map((review, index) => ( handleSelectCard(card.id)} + key={index} + selected={reviewId === review.oneLineId ? true : false} + content={review.oneLineContent} + rating={review.rating} + title={review.title} + author={review.author} + onClick={() => handleSelectCard(review.oneLineId)} /> ))}
- {selectedCard && ( + {reviewId && (
)} From 98cd0de5a788485fa3bb1727012d4efbece9bb7c Mon Sep 17 00:00:00 2001 From: chanhee Date: Mon, 18 Nov 2024 22:27:57 +0900 Subject: [PATCH 11/11] =?UTF-8?q?=F0=9F=92=84ui:=20BookDisplay=20Card=20?= =?UTF-8?q?=EC=A0=9C=EC=9E=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/MainPage/BookDisplay.jsx | 44 +++++++++++++++++++ .../src/components/MainPage/OneBookCard.jsx | 5 ++- .../src/pages/MainPage/SelectCardPage.jsx | 6 +-- 3 files changed, 50 insertions(+), 5 deletions(-) create mode 100644 bookduck/src/components/MainPage/BookDisplay.jsx diff --git a/bookduck/src/components/MainPage/BookDisplay.jsx b/bookduck/src/components/MainPage/BookDisplay.jsx new file mode 100644 index 0000000..cecb445 --- /dev/null +++ b/bookduck/src/components/MainPage/BookDisplay.jsx @@ -0,0 +1,44 @@ +import React, { useEffect } from "react"; +import bookCard from "../../assets/mainPage/bookcard.svg"; +import music from "../../assets/mainPage/music.svg"; +const BookDisplay = ({ bookNumber }) => { + useEffect(() => { + console.log(bookNumber); + }, []); + return ( +
+
+ {/* 첫 번째 북박스 */} +
+ Book Card +
+ {/* 두 번째 북박스 */} + {bookNumber === 2 && ( +
+ Book Card +
+ )} + {/* 음악 박스 */} +
+
+ by + Music Icon +
+
+ + 노래 제목 + + + 책 제목 + +
+
+
+
+ ); +}; + +export default BookDisplay; diff --git a/bookduck/src/components/MainPage/OneBookCard.jsx b/bookduck/src/components/MainPage/OneBookCard.jsx index a71b656..82eb592 100644 --- a/bookduck/src/components/MainPage/OneBookCard.jsx +++ b/bookduck/src/components/MainPage/OneBookCard.jsx @@ -1,5 +1,6 @@ import React, { useState, useRef, useEffect } from "react"; import { post } from "../../api/example"; +import bookCard from "../../assets/mainPage/bookcard.svg"; import music from "../../assets/mainPage/music.svg"; import infoMusicBox from "../../assets/mainPage/info-musicbox.svg"; import infoMemoBox from "../../assets/mainPage/info-memobox.svg"; @@ -117,7 +118,7 @@ const OneBookCard = ({ selected === "firstBook" ? "border-[1px] border-[#6B7FF0]" : "" } flex items-center justify-center w-[5.125rem] h-full bg-gray-custom rounded-[0.375rem] shadow-custom`} > - Book Card + Book Card
{/* 두 번째 북박스 */} @@ -136,7 +137,7 @@ const OneBookCard = ({ selected === "secondBook" ? "border-[1px] border-[#6B7FF0]" : "" } flex items-center justify-center w-[5.125rem] h-full bg-gray-custom rounded-[0.375rem] shadow-custom`} > - Book Card + Book Card
)} diff --git a/bookduck/src/pages/MainPage/SelectCardPage.jsx b/bookduck/src/pages/MainPage/SelectCardPage.jsx index 538aa5c..7445448 100644 --- a/bookduck/src/pages/MainPage/SelectCardPage.jsx +++ b/bookduck/src/pages/MainPage/SelectCardPage.jsx @@ -2,9 +2,9 @@ import React from "react"; import { useNavigate } from "react-router-dom"; import StatusBar from "../../components/common/StatusBar"; import Header3 from "../../components/common/Header3"; -import OneBookCard from "../../components/MainPage/OneBookCard"; import ExtractCard from "../../components/MainPage/ExtractCard"; import ReviewCard from "../../components/MainPage/ReviewCard"; +import BookDisplay from "../../components/MainPage/BookDisplay"; const SelectCardPage = () => { const navigate = useNavigate(); @@ -25,14 +25,14 @@ const SelectCardPage = () => { navigate("/selectcard/custom", { state: { bookNumber: 1 } }) } > - +
navigate("/selectcard/custom", { state: { bookNumber: 2 } }) } > - +
navigate("/selectcard/extract")} /> navigate("/selectcard/review")} />