-
![{type}]({notificationData.icon})
- {!read && (
+
+
![{alarmType}]({notificationData?.icon})
+ {!isRead && (
![]({alarmCircle})
)}
-
+
- {notificationData.message(text)}
+ {notificationData?.message(boldText)}
+
+
+ {formatNotiTime(createdTime)}
diff --git a/bookduck/src/components/SearchPage/SearchBookComponent.jsx b/bookduck/src/components/SearchPage/SearchBookComponent.jsx
index 47701ac..9cbea34 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,95 @@ 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);
+ }
+ };
+
+ //API-직접 등록 책 상태 변경
+ const patchRegisteredStatus = async (selectedBookId, currentStatus) => {
+ try {
+ await patch(`/books/${selectedBookId}?status=${currentStatus}`);
} catch (error) {
- console.error("일반 책 읽어오기 오류", error);
+ console.error(error);
}
};
- //useEffect 훅
+ // 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);
+ const handleStatusClick = (id) => {
+ console.log("id", id);
+ setSelectedBookId(id);
+
+ console.log("id", selectedBookId);
setBottomSheetShow(true);
};
- const handleOpenClick2 = (inLibary) => {
- setIsCancel(inLibary);
- 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 +183,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 +246,15 @@ const SearchBookComponent = ({ search }) => {
visible={visible}
setVisible={setVisible}
>
-
+
+
+
>
);
diff --git a/bookduck/src/components/common/BottomSheetModal.jsx b/bookduck/src/components/common/BottomSheetModal.jsx
index c10f7f6..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/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 = ({
)}