diff --git a/bookduck/package.json b/bookduck/package.json index 6153d127..00bf08d3 100644 --- a/bookduck/package.json +++ b/bookduck/package.json @@ -17,6 +17,7 @@ "@tanstack/react-query": "^5.60.2", "axios": "^1.7.7", "classnames": "^2.5.1", + "html2canvas": "^1.4.1", "event-source-polyfill": "^1.0.31", "firebase": "^11.0.2", "react": "^18.3.1", diff --git a/bookduck/public/assets/characterPage/badges/ARCHIVE/ARCHIVE_01.png b/bookduck/public/assets/characterPage/badges/ARCHIVE/ARCHIVE_01.png new file mode 100644 index 00000000..192356a7 Binary files /dev/null and b/bookduck/public/assets/characterPage/badges/ARCHIVE/ARCHIVE_01.png differ diff --git a/bookduck/public/assets/characterPage/badges/ARCHIVE/ARCHIVE_02.png b/bookduck/public/assets/characterPage/badges/ARCHIVE/ARCHIVE_02.png new file mode 100644 index 00000000..93a4c139 Binary files /dev/null and b/bookduck/public/assets/characterPage/badges/ARCHIVE/ARCHIVE_02.png differ diff --git a/bookduck/public/assets/characterPage/badges/ARCHIVE/ARCHIVE_03.png b/bookduck/public/assets/characterPage/badges/ARCHIVE/ARCHIVE_03.png new file mode 100644 index 00000000..714d5884 Binary files /dev/null and b/bookduck/public/assets/characterPage/badges/ARCHIVE/ARCHIVE_03.png differ diff --git a/bookduck/public/assets/characterPage/badges/ARCHIVE/ARCHIVE_04.png b/bookduck/public/assets/characterPage/badges/ARCHIVE/ARCHIVE_04.png new file mode 100644 index 00000000..97e5adee Binary files /dev/null and b/bookduck/public/assets/characterPage/badges/ARCHIVE/ARCHIVE_04.png differ diff --git a/bookduck/public/assets/characterPage/badges/ARCHIVE/ARCHIVE_05.png b/bookduck/public/assets/characterPage/badges/ARCHIVE/ARCHIVE_05.png new file mode 100644 index 00000000..563186bc Binary files /dev/null and b/bookduck/public/assets/characterPage/badges/ARCHIVE/ARCHIVE_05.png differ diff --git a/bookduck/public/assets/characterPage/badges/ARCHIVE/ARCHIVE_06.png b/bookduck/public/assets/characterPage/badges/ARCHIVE/ARCHIVE_06.png new file mode 100644 index 00000000..7298b90b Binary files /dev/null and b/bookduck/public/assets/characterPage/badges/ARCHIVE/ARCHIVE_06.png differ diff --git a/bookduck/public/assets/characterPage/badges/ARCHIVE/ARCHIVE_07.png b/bookduck/public/assets/characterPage/badges/ARCHIVE/ARCHIVE_07.png new file mode 100644 index 00000000..445df79c Binary files /dev/null and b/bookduck/public/assets/characterPage/badges/ARCHIVE/ARCHIVE_07.png differ diff --git a/bookduck/public/assets/characterPage/badges/ARCHIVE/ARCHIVE_08.png b/bookduck/public/assets/characterPage/badges/ARCHIVE/ARCHIVE_08.png new file mode 100644 index 00000000..441a4491 Binary files /dev/null and b/bookduck/public/assets/characterPage/badges/ARCHIVE/ARCHIVE_08.png differ diff --git a/bookduck/public/assets/characterPage/badges/LEVEL/LEVEL_01.png b/bookduck/public/assets/characterPage/badges/LEVEL/LEVEL_01.png new file mode 100644 index 00000000..a8250969 Binary files /dev/null and b/bookduck/public/assets/characterPage/badges/LEVEL/LEVEL_01.png differ diff --git a/bookduck/public/assets/characterPage/badges/LEVEL/LEVEL_02.png b/bookduck/public/assets/characterPage/badges/LEVEL/LEVEL_02.png new file mode 100644 index 00000000..7a53b52f Binary files /dev/null and b/bookduck/public/assets/characterPage/badges/LEVEL/LEVEL_02.png differ diff --git a/bookduck/public/assets/characterPage/badges/LEVEL/LEVEL_03.png b/bookduck/public/assets/characterPage/badges/LEVEL/LEVEL_03.png new file mode 100644 index 00000000..1b0a9439 Binary files /dev/null and b/bookduck/public/assets/characterPage/badges/LEVEL/LEVEL_03.png differ diff --git a/bookduck/public/assets/characterPage/badges/LEVEL/LEVEL_04.png b/bookduck/public/assets/characterPage/badges/LEVEL/LEVEL_04.png new file mode 100644 index 00000000..120276ac Binary files /dev/null and b/bookduck/public/assets/characterPage/badges/LEVEL/LEVEL_04.png differ diff --git a/bookduck/public/assets/characterPage/badges/LEVEL/LEVEL_05.png b/bookduck/public/assets/characterPage/badges/LEVEL/LEVEL_05.png new file mode 100644 index 00000000..a2d12133 Binary files /dev/null and b/bookduck/public/assets/characterPage/badges/LEVEL/LEVEL_05.png differ diff --git a/bookduck/public/assets/characterPage/badges/LEVEL/LEVEL_06.png b/bookduck/public/assets/characterPage/badges/LEVEL/LEVEL_06.png new file mode 100644 index 00000000..df66f25d Binary files /dev/null and b/bookduck/public/assets/characterPage/badges/LEVEL/LEVEL_06.png differ diff --git a/bookduck/public/assets/characterPage/badges/ONELINE/ONELINE_01.png b/bookduck/public/assets/characterPage/badges/ONELINE/ONELINE_01.png new file mode 100644 index 00000000..11de685c Binary files /dev/null and b/bookduck/public/assets/characterPage/badges/ONELINE/ONELINE_01.png differ diff --git a/bookduck/public/assets/characterPage/badges/ONELINE/ONELINE_02.png b/bookduck/public/assets/characterPage/badges/ONELINE/ONELINE_02.png new file mode 100644 index 00000000..ac1213c5 Binary files /dev/null and b/bookduck/public/assets/characterPage/badges/ONELINE/ONELINE_02.png differ diff --git a/bookduck/public/assets/characterPage/badges/ONELINE/ONELINE_03.png b/bookduck/public/assets/characterPage/badges/ONELINE/ONELINE_03.png new file mode 100644 index 00000000..3a1a2161 Binary files /dev/null and b/bookduck/public/assets/characterPage/badges/ONELINE/ONELINE_03.png differ diff --git a/bookduck/public/assets/characterPage/badges/ONELINE/ONELINE_04.png b/bookduck/public/assets/characterPage/badges/ONELINE/ONELINE_04.png new file mode 100644 index 00000000..c33587b5 Binary files /dev/null and b/bookduck/public/assets/characterPage/badges/ONELINE/ONELINE_04.png differ diff --git a/bookduck/public/assets/characterPage/badges/ONELINE/ONELINE_05.png b/bookduck/public/assets/characterPage/badges/ONELINE/ONELINE_05.png new file mode 100644 index 00000000..43399cbf Binary files /dev/null and b/bookduck/public/assets/characterPage/badges/ONELINE/ONELINE_05.png differ diff --git a/bookduck/public/assets/characterPage/badges/ONELINE/ONELINE_06.png b/bookduck/public/assets/characterPage/badges/ONELINE/ONELINE_06.png new file mode 100644 index 00000000..3839acb3 Binary files /dev/null and b/bookduck/public/assets/characterPage/badges/ONELINE/ONELINE_06.png differ diff --git a/bookduck/public/assets/characterPage/badges/ONELINE/ONELINE_07.png b/bookduck/public/assets/characterPage/badges/ONELINE/ONELINE_07.png new file mode 100644 index 00000000..0a4f7e6a Binary files /dev/null and b/bookduck/public/assets/characterPage/badges/ONELINE/ONELINE_07.png differ diff --git a/bookduck/public/assets/characterPage/badges/ONELINE/ONELINE_08.png b/bookduck/public/assets/characterPage/badges/ONELINE/ONELINE_08.png new file mode 100644 index 00000000..828deccd Binary files /dev/null and b/bookduck/public/assets/characterPage/badges/ONELINE/ONELINE_08.png differ diff --git a/bookduck/public/assets/characterPage/badges/READ/READ_01.png b/bookduck/public/assets/characterPage/badges/READ/READ_01.png new file mode 100644 index 00000000..ffae820b Binary files /dev/null and b/bookduck/public/assets/characterPage/badges/READ/READ_01.png differ diff --git a/bookduck/public/assets/characterPage/badges/READ/READ_02.png b/bookduck/public/assets/characterPage/badges/READ/READ_02.png new file mode 100644 index 00000000..5cd7bf3c Binary files /dev/null and b/bookduck/public/assets/characterPage/badges/READ/READ_02.png differ diff --git a/bookduck/public/assets/characterPage/badges/READ/READ_03.png b/bookduck/public/assets/characterPage/badges/READ/READ_03.png new file mode 100644 index 00000000..bb97725c Binary files /dev/null and b/bookduck/public/assets/characterPage/badges/READ/READ_03.png differ diff --git a/bookduck/public/assets/characterPage/badges/READ/READ_04.png b/bookduck/public/assets/characterPage/badges/READ/READ_04.png new file mode 100644 index 00000000..8acfcb91 Binary files /dev/null and b/bookduck/public/assets/characterPage/badges/READ/READ_04.png differ diff --git a/bookduck/public/assets/characterPage/badges/READ/READ_05.png b/bookduck/public/assets/characterPage/badges/READ/READ_05.png new file mode 100644 index 00000000..6dd7b6b3 Binary files /dev/null and b/bookduck/public/assets/characterPage/badges/READ/READ_05.png differ diff --git a/bookduck/public/assets/characterPage/badges/READ/READ_06.png b/bookduck/public/assets/characterPage/badges/READ/READ_06.png new file mode 100644 index 00000000..cccfb54f Binary files /dev/null and b/bookduck/public/assets/characterPage/badges/READ/READ_06.png differ diff --git a/bookduck/public/assets/characterPage/badges/READ/READ_07.png b/bookduck/public/assets/characterPage/badges/READ/READ_07.png new file mode 100644 index 00000000..740d0f47 Binary files /dev/null and b/bookduck/public/assets/characterPage/badges/READ/READ_07.png differ diff --git a/bookduck/public/assets/characterPage/badges/READ/READ_08.png b/bookduck/public/assets/characterPage/badges/READ/READ_08.png new file mode 100644 index 00000000..e404d50d Binary files /dev/null and b/bookduck/public/assets/characterPage/badges/READ/READ_08.png differ diff --git a/bookduck/src/App.jsx b/bookduck/src/App.jsx index d9fc2813..77e70070 100644 --- a/bookduck/src/App.jsx +++ b/bookduck/src/App.jsx @@ -19,9 +19,10 @@ import RegisterPage from "./pages/SearchPage/SearchRegisterPage"; import SelectCardPage from "./pages/MainPage/SelectCardPage"; import BookInfoPage from "./pages/BookInfoPage/BookInfoPage"; import UserCommentPage from "./pages/BookInfoPage/UserCommentPage"; -import BookInfoAddedPage from "./pages/BookInfoPage/BoonInfoAddedPage"; +import BookInfoAddedPage from "./pages/BookInfoPage/BookInfoAddedPage"; import StatisticsPage from "./pages/StatisticsPage/StatisticsPage"; import CharacterExportPage from "./pages/StatisticsPage/CharacterExportPage"; +import SummaryExportPage from "./pages/StatisticsPage/SummaryExportPage"; import CardDecorationPage from "./pages/RecordingPage/CardDecorationPage"; import LibraryPage from "./pages/LibraryPage/LibraryPage"; import EnterBookCasePage from "./pages/LibraryPage/EnterBookCasePage"; @@ -70,11 +71,15 @@ function App() { } /> } /> } /> - } /> + } /> } /> + } + /> } /> } /> } /> @@ -84,7 +89,10 @@ function App() { } /> } /> } /> - } /> + } + /> } /> } /> } /> diff --git a/bookduck/src/api/bookinfo.js b/bookduck/src/api/bookinfo.js index d22e6651..00ff6927 100644 --- a/bookduck/src/api/bookinfo.js +++ b/bookduck/src/api/bookinfo.js @@ -1,7 +1,7 @@ import { apiAuth } from "./api"; import { get, post, patch, put, del } from "./example"; -// 한줄평,별점 목록 조회 +// 기본 책정보 조회(bookinfoId) export const getBookInfo = async ({ bookinfoId }) => { try { const res = await get(`/bookinfo/${bookinfoId}`); @@ -13,6 +13,18 @@ export const getBookInfo = async ({ bookinfoId }) => { } }; +// custom 책정보 조회(bookinfoId) +export const getCustomBookInfo = async ({ bookinfoId }) => { + try { + const res = await get(`/bookinfo/custom/${bookinfoId}`); + console.log("책정보 조회 성공: ", res); + return res; + } catch (error) { + console.error("책정보 조회 실패: ", error); + throw error; + } +}; + // 한줄평,별점 목록 조회 export const getOneLineRatingsInfo = async ({ bookinfoId, @@ -104,7 +116,7 @@ export const deleteOneLine = async (onelineId) => { } }; -//한줄평 좋아요 +//한줄평 좋아요 등록 export const enrollLike = async (onelineId) => { const url = `onelines/${onelineId}/like`; try { @@ -128,3 +140,27 @@ export const deleteLike = async (onelineId) => { throw error; } }; + +//나의 기록 조회 +export const getMyArchive = async ({ bookinfoId }) => { + try { + const res = await get(`/bookinfo/${bookinfoId}/archives/users/me`); + console.log("나의 기록 조회 성공: ", res); + return res; + } catch (error) { + console.error("나의 기록 조회 실패: ", error); + throw error; + } +}; + +//연관 추천도서 조회 +export const getRelatedBookInfo = async ({ bookinfoId }) => { + try { + const res = await get(`/bookinfo/${bookinfoId}/explore`); + console.log("추천도서 조회 성공: ", res); + return res; + } catch (error) { + console.error("추천도서 조회 실패: ", error); + throw error; + } +}; diff --git a/bookduck/src/api/character.js b/bookduck/src/api/character.js index 8bf3f260..c24a288a 100644 --- a/bookduck/src/api/character.js +++ b/bookduck/src/api/character.js @@ -36,3 +36,15 @@ export const getItemLists = async () => { throw error; } }; + +// 내 뱃지 목록 조회 +export const getBadgeLists = async () => { + try { + const res = await get(`/badges`); + console.log("배지 리스트 조회 성공: ", res); + return res; + } catch (error) { + console.error("배지 리스트 조회 실패: ", error); + throw error; + } +}; diff --git a/bookduck/src/api/statistics.js b/bookduck/src/api/statistics.js index f6c2c4c7..90718535 100644 --- a/bookduck/src/api/statistics.js +++ b/bookduck/src/api/statistics.js @@ -24,3 +24,27 @@ export const getUserStatisticsInfo = async (userId) => { throw error; } }; + +// 키워드 조회 +export const getKeywords = async (userId) => { + try { + const res = await get(`/users/${userId}/keywords`); + console.log("키워드 조회 성공: ", res); + return res; + } catch (error) { + console.error("키워드 조회 실패: ", error); + throw error; + } +}; + +// 통계 요약 내보내기 정보 제공 +export const getExportSummaryInfo = async () => { + try { + const res = await get(`/export/statistics`); + console.log("통계 요약 정보 조회 성공: ", res); + return res; + } catch (error) { + console.error("통계 요약 정보 조회 실패: ", error); + throw error; + } +}; diff --git a/bookduck/src/assets/bookinfoPage/menu-vertical.svg b/bookduck/src/assets/bookinfoPage/menu-vertical.svg index d014ad10..38652054 100644 --- a/bookduck/src/assets/bookinfoPage/menu-vertical.svg +++ b/bookduck/src/assets/bookinfoPage/menu-vertical.svg @@ -1,5 +1,7 @@ - - - - + + + + + + diff --git a/bookduck/src/assets/bookinfoPage/star-half.svg b/bookduck/src/assets/bookinfoPage/star-half.svg index 849ef7a3..c753f692 100644 --- a/bookduck/src/assets/bookinfoPage/star-half.svg +++ b/bookduck/src/assets/bookinfoPage/star-half.svg @@ -1,11 +1,11 @@ - + - - - + + + - - + + diff --git a/bookduck/src/assets/bookinfoPage/star-no.svg b/bookduck/src/assets/bookinfoPage/star-no.svg index 62e76392..d2d89710 100644 --- a/bookduck/src/assets/bookinfoPage/star-no.svg +++ b/bookduck/src/assets/bookinfoPage/star-no.svg @@ -1,3 +1,5 @@ - - + + + + diff --git a/bookduck/src/assets/bookinfoPage/star-yes.svg b/bookduck/src/assets/bookinfoPage/star-yes.svg index 7b3b8f96..1dcade09 100644 --- a/bookduck/src/assets/bookinfoPage/star-yes.svg +++ b/bookduck/src/assets/bookinfoPage/star-yes.svg @@ -1,3 +1,5 @@ - - + + + + diff --git a/bookduck/src/assets/bookinfoPage/start-yes.svg b/bookduck/src/assets/bookinfoPage/start-yes.svg deleted file mode 100644 index 7b3b8f96..00000000 --- a/bookduck/src/assets/bookinfoPage/start-yes.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/bookduck/src/assets/bookinfoPage/user-no-archive.svg b/bookduck/src/assets/bookinfoPage/user-no-archive.svg new file mode 100644 index 00000000..0a22bcbf --- /dev/null +++ b/bookduck/src/assets/bookinfoPage/user-no-archive.svg @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bookduck/src/assets/common/teacher-duck.png b/bookduck/src/assets/common/teacher-duck.png new file mode 100644 index 00000000..026d54d8 Binary files /dev/null and b/bookduck/src/assets/common/teacher-duck.png differ diff --git a/bookduck/src/assets/characterPage/bookduck.svg b/bookduck/src/assets/statisticsPage/bookduck.svg similarity index 100% rename from bookduck/src/assets/characterPage/bookduck.svg rename to bookduck/src/assets/statisticsPage/bookduck.svg diff --git a/bookduck/src/assets/statisticsPage/cancle.svg b/bookduck/src/assets/statisticsPage/cancle.svg new file mode 100644 index 00000000..616343c5 --- /dev/null +++ b/bookduck/src/assets/statisticsPage/cancle.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/bookduck/src/assets/statisticsPage/divider.svg b/bookduck/src/assets/statisticsPage/divider.svg new file mode 100644 index 00000000..57d51d57 --- /dev/null +++ b/bookduck/src/assets/statisticsPage/divider.svg @@ -0,0 +1,3 @@ + + + diff --git a/bookduck/src/assets/statisticsPage/star-summary.svg b/bookduck/src/assets/statisticsPage/star-summary.svg new file mode 100644 index 00000000..a3a82cbe --- /dev/null +++ b/bookduck/src/assets/statisticsPage/star-summary.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/bookduck/src/assets/statisticsPage/summary.svg b/bookduck/src/assets/statisticsPage/summary.svg new file mode 100644 index 00000000..4498d378 --- /dev/null +++ b/bookduck/src/assets/statisticsPage/summary.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/bookduck/src/components/BookInfoPage/ArchiveView.jsx b/bookduck/src/components/BookInfoPage/ArchiveView.jsx index aa1f7583..0d44991c 100644 --- a/bookduck/src/components/BookInfoPage/ArchiveView.jsx +++ b/bookduck/src/components/BookInfoPage/ArchiveView.jsx @@ -1,11 +1,42 @@ -const ArchiveView = () => { +import { useState, useEffect } from "react"; +import { getMyArchive } from "../../api/bookinfo"; +import ReviewComponents from "../../components/RecordingPage/ReviewComponents"; +import ExtractComponents from "../../components/RecordingPage/ExtractComponents"; +const ArchiveView = ({ bookinfoId }) => { + const [myArchiveData, setMyArchiveData] = useState(null); + + useEffect(() => { + const fetchData = async () => { + try { + const res = await getMyArchive({ bookinfoId }); + console.log("조회 성공: ", res); + setMyArchiveData(res.userBookArchiveList); + } catch (err) { + console.error("오류 발생: ", err); + } + }; + fetchData(); + }, []); return ( -
-
- 아직 작성된 독서기록이 없어요.
- ‘독서 기록하기’ 버튼을 눌러 책 구절을
- 발췌하거나 감상평을 기록해 보세요! -
+
+ {myArchiveData?.length > 0 ? ( +
+ {myArchiveData.map((item, index) => { + const { type, data } = item; + if (type === "REVIEW") { + return ; + } else if (type === "EXCERPT") { + return ; + } + })} +
+ ) : ( +
+ 아직 작성된 독서기록이 없어요.
+ ‘독서 기록하기’ 버튼을 눌러 책 구절을
+ 발췌하거나 감상평을 기록해 보세요! +
+ )}
); }; diff --git a/bookduck/src/components/BookInfoPage/BookInfo.jsx b/bookduck/src/components/BookInfoPage/BookInfo.jsx index c0dc61ce..26cf4e92 100644 --- a/bookduck/src/components/BookInfoPage/BookInfo.jsx +++ b/bookduck/src/components/BookInfoPage/BookInfo.jsx @@ -20,7 +20,9 @@ const BookInfo = ({ isMe = "default", bookData }) => { }; const handlePutCancel = () => {}; - const bookBasicData = bookData?.bookInfoBasicDto; + const bookBasicData = + isMe === "default" ? bookData?.bookInfoBasicDto : bookData; + console.log(bookBasicData); // 기본으로 등록되어 있는 책: default 내가 직접 등록한 책: me 타유저가 직접 등록한 책: other return (
@@ -29,8 +31,12 @@ const BookInfo = ({ isMe = "default", bookData }) => { src={bookBasicData?.imgPath} alt="Cover" /> -
-
+
+
{bookBasicData?.title}
@@ -42,7 +48,11 @@ const BookInfo = ({ isMe = "default", bookData }) => { 유저닉네임님이 등록한 책
) : ( -
+
{isMe === "default" ? (
기본평점
diff --git a/bookduck/src/components/BookInfoPage/InfoView.jsx b/bookduck/src/components/BookInfoPage/InfoView.jsx index 384419ce..8524823e 100644 --- a/bookduck/src/components/BookInfoPage/InfoView.jsx +++ b/bookduck/src/components/BookInfoPage/InfoView.jsx @@ -1,18 +1,37 @@ -import { useNavigate } from "react-router-dom"; +import { useState, useEffect } from "react"; +import { useNavigate, useParams } from "react-router-dom"; import right from "../../assets/common/right.svg"; import cover from "../../assets/bookinfoPage/cover.svg"; import BookPlot from "./BookPlot"; import Divider1 from "../common/Divider1"; import Divider2 from "../common/Divider2"; import UserComment from "./UserComment"; +import { getRelatedBookInfo } from "../../api/bookinfo"; const InfoView = ({ bookData, ratingData }) => { + const { bookinfoId } = useParams(); const images = [cover, cover, cover, cover, cover, cover, cover]; const ratingList = ratingData?.oneLineRatingList || []; + const navigate = useNavigate(); const handleCommentClick = () => { navigate("/info/book/comment", { state: { ratingList } }); }; + const [relatedBookData, setRelatedBookData] = useState(null); + useEffect(() => { + const fetchData = async () => { + try { + const res = await getRelatedBookInfo({ bookinfoId }); + console.log("조회 성공: ", res?.bookList); + setRelatedBookData(res?.bookList); + } catch (err) { + console.error("오류 발생: ", err); + } + }; + fetchData(); + }, []); + // 최대 3개만 표시 + const displayedRatings = ratingList.slice(0, 3); return (
@@ -26,7 +45,7 @@ const InfoView = ({ bookData, ratingData }) => { onClick={handleCommentClick} />
- {ratingList.map((oneLine, index) => ( + {displayedRatings.map((oneLine, index) => (
@@ -36,19 +55,27 @@ const InfoView = ({ bookData, ratingData }) => { ))}
-
- 이 책을 읽은 사용자들이 읽은 다른 책 -
- {images.map((cover, index) => ( - {`book-cover-${index}`} - ))} + {ratingList?.length > 0 && ( +
+ 이 책을 읽은 사용자들이 읽은 다른 책 +
+ {relatedBookData.map((book, index) => ( +
+ {`book-cover-${index}`} + {book.title} +
+ ))} +
-
+ )}
); }; diff --git a/bookduck/src/components/CharacterPage/Badge.jsx b/bookduck/src/components/CharacterPage/Badge.jsx index 9987ca28..cc57a388 100644 --- a/bookduck/src/components/CharacterPage/Badge.jsx +++ b/bookduck/src/components/CharacterPage/Badge.jsx @@ -1,8 +1,31 @@ +import { useState } from "react"; import lock from "../../assets/characterPage/lock.svg"; -const Badge = () => { +import BadgeModal from "./BadgeModal"; +const Badge = ({ badgeInfo, currentData }) => { + const [isModalOpen, setIsModalOpen] = useState(false); + const handleBadgeClick = () => { + setIsModalOpen(!isModalOpen); + }; + return ( -
- +
+ {badgeInfo?.isOwned ? ( + {`${badgeInfo?.badgeName} + ) : ( +
+ +
+ )} + {isModalOpen && ( + + )}
); }; diff --git a/bookduck/src/components/CharacterPage/BadgeHeader.jsx b/bookduck/src/components/CharacterPage/BadgeHeader.jsx index 2da7ed90..37057c88 100644 --- a/bookduck/src/components/CharacterPage/BadgeHeader.jsx +++ b/bookduck/src/components/CharacterPage/BadgeHeader.jsx @@ -2,7 +2,7 @@ import { useState } from "react"; import { useNavigate } from "react-router-dom"; import back from "../../assets/characterPage/back.svg"; import help from "../../assets/characterPage/help-circle.svg"; -import BadgeModal from "./BadgeModal"; +import BadgeInfoModal from "./BadgeInfoModal"; const BadgeHeader = () => { const navigate = useNavigate(); @@ -31,7 +31,7 @@ const BadgeHeader = () => {
- {isModalOpen && } + {isModalOpen && }
); }; diff --git a/bookduck/src/components/CharacterPage/BadgeInfoModal.jsx b/bookduck/src/components/CharacterPage/BadgeInfoModal.jsx new file mode 100644 index 00000000..8325c1bf --- /dev/null +++ b/bookduck/src/components/CharacterPage/BadgeInfoModal.jsx @@ -0,0 +1,48 @@ +const BadgeInfoModal = ({ onClick }) => { + return ( +
+
+
+
+ 배지 종류 +
+
+
+
열정적인 독자
+
+ 다 읽은 책 권수에 따라 획득할 수 있어요 +
+
+
+
꼼꼼한 기록자
+
+ 기록카드 개수에 따라 획득할 수 있어요 +
+
+
+
성실한 평가자
+
+ 남긴 한줄평 개수에 따라 획득할 수 있어요 +
+
+
+
레벨업 마스터
+
+ 레벨이 일정 단계에 도달하면 얻을 수 있어요 +
+
+
+
+
+ +
+
+
+ ); +}; +export default BadgeInfoModal; diff --git a/bookduck/src/components/CharacterPage/BadgeList.jsx b/bookduck/src/components/CharacterPage/BadgeList.jsx index ab2b3235..b96c4d62 100644 --- a/bookduck/src/components/CharacterPage/BadgeList.jsx +++ b/bookduck/src/components/CharacterPage/BadgeList.jsx @@ -1,16 +1,25 @@ // import back from "../../assets/common/back.svg"; import Badge from "./Badge"; //title을 props로 받음. default 값은 "제목" -const BadgeList = ({ title = "제목" }) => { +const BadgeList = ({ title = "제목", badgeData, currentData }) => { + const firstRow = badgeData?.slice(0, 4); + const secondRow = badgeData?.slice(4); return ( -
+
{title}
-
- - - - +
+ {firstRow && + firstRow.map((badge, index) => ( + + ))}
+ {firstRow && firstRow[3].isOwned && ( +
+ {secondRow.map((badge, index) => ( + + ))} +
+ )}
); }; diff --git a/bookduck/src/components/CharacterPage/BadgeModal.jsx b/bookduck/src/components/CharacterPage/BadgeModal.jsx index 3e4c6f18..563e5e80 100644 --- a/bookduck/src/components/CharacterPage/BadgeModal.jsx +++ b/bookduck/src/components/CharacterPage/BadgeModal.jsx @@ -1,43 +1,56 @@ -const BadgeModal = ({ onClick }) => { +const BadgeModal = ({ badge, currentData }) => { + const formattedDate = badge.createdDate?.replace(/-/g, "."); + let text = ""; + let next = ""; + + switch (badge.badgeType) { + case "READ": + text = `완독 수 ${badge?.unlockValue}권을`; + next = `현재 완독 수 : ${currentData?.currentReadCount}/${badge?.unlockValue}`; + break; + + case "ARCHIVE": + text = `기록카드 수 ${badge?.unlockValue}개를`; + next = `현재 기록카드 수 : ${currentData?.currentArchiveCount}/${badge?.unlockValue}`; + break; + case "ONELINE": + text = `한줄평 수 ${badge?.unlockValue}개를`; + next = `현재 남긴 한줄평 수 : ${currentData?.currentOneLineCount}/${badge?.unlockValue}`; + break; + case "LEVEL": + text = `${badge?.unlockValue}레벨을`; + next = `현재 레벨 : ${currentData?.currentLevel}`; + break; + } return (
-
-
-
- 배지 종류 -
-
-
-
열정적인 독자
-
- 다 읽은 책 권수에 따라 획득할 수 있어요 -
-
-
-
꼼꼼한 기록자
-
- 기록카드 개수에 따라 획득할 수 있어요 -
+
+ {badge?.isOwned ? ( +
+
+ {badge?.description}
-
-
성실한 평가자
-
- 남긴 한줄평 개수에 따라 획득할 수 있어요 -
+ {`${badge?.badgeName} +
획득일: {formattedDate}
+
+ ) : ( +
+
+ 아직 획득하지 못한 배지예요.
-
-
레벨업 마스터
-
- 레벨이 일정 단계에 도달하면 얻을 수 있어요 -
+
+ {text} 달성해야
+ 해당 배지를 얻을 수 있어요.
+
{next}
-
-
-
diff --git a/bookduck/src/components/RecordingPage/ExtractComponents.jsx b/bookduck/src/components/RecordingPage/ExtractComponents.jsx index 6f270ef3..42835650 100644 --- a/bookduck/src/components/RecordingPage/ExtractComponents.jsx +++ b/bookduck/src/components/RecordingPage/ExtractComponents.jsx @@ -1,21 +1,35 @@ import { useNavigate } from "react-router-dom"; -const ExtractComponents = () => { +const ExtractComponents = ({ excerptData }) => { const navigate = useNavigate(); - + const createdDate = excerptData?.createdTime.split("T")[0].replace(/-/g, "."); return (
navigate("/extract-archive-detail")} className="cursor-pointer" >
-
54p
+ {createdDate ? ( + <> +
+
{createdDate}
+
+ {excerptData?.pageNumber || "54"}p +
+
+ + ) : ( +
+ {excerptData?.pageNumber || "54"}p +
+ )}
- 한번 피면 끝장을 보게 되는 책이다. + {excerptData?.excerptContent || + "한번 피면 끝장을 보게 되는 책이다."}
- 내 편이 아니더라도 적을 만들지 마라 / 류츠신 + {/* 수정 필요 */}내 편이 아니더라도 적을 만들지 마라 / 류츠신
diff --git a/bookduck/src/components/RecordingPage/ReviewComponents.jsx b/bookduck/src/components/RecordingPage/ReviewComponents.jsx index e6f12730..8a472a10 100644 --- a/bookduck/src/components/RecordingPage/ReviewComponents.jsx +++ b/bookduck/src/components/RecordingPage/ReviewComponents.jsx @@ -5,9 +5,11 @@ import { useEffect } from "react"; const ReviewComponents = ({ reviewTitleValue = "감상평 제목", contents = "감상평 내용이 들어갈 자리입니다.", - bookTitleValue = "감상평 책", - authorValue = "감상평 지은이", + bookTitleValue, + authorValue, + reviewData, }) => { + const createdDate = reviewData?.createdTime.split("T")[0].replace(/-/g, "."); const navigate = useNavigate(); const { reviewColor } = useReviewColorStore(); @@ -23,15 +25,22 @@ const ReviewComponents = ({ !reviewColor && "bg-gray-400" } shadow-custom`} > + {createdDate && ( +
{createdDate}
+ )}
- {reviewTitleValue} + {reviewData?.reviewTitle || reviewTitleValue} +
+
+ {reviewData?.reviewContent || contents}
-
{contents}
-
-
- {bookTitleValue} / {authorValue}
+ {bookTitleValue && ( +
+ {bookTitleValue} / {authorValue} +
+ )}
); diff --git a/bookduck/src/components/StatisticsPage/ExportCharacter.jsx b/bookduck/src/components/StatisticsPage/ExportCharacter.jsx index 14fa37aa..86106e02 100644 --- a/bookduck/src/components/StatisticsPage/ExportCharacter.jsx +++ b/bookduck/src/components/StatisticsPage/ExportCharacter.jsx @@ -1,4 +1,4 @@ -import logo from "../../assets/characterPage/bookduck.svg"; +import logo from "../../assets/statisticsPage/bookduck.svg"; import UserDuck from "../CharacterPage/UserDuck"; const ExportCharacter = () => { return ( diff --git a/bookduck/src/components/StatisticsPage/ExportHeader.jsx b/bookduck/src/components/StatisticsPage/ExportHeader.jsx new file mode 100644 index 00000000..ad970b86 --- /dev/null +++ b/bookduck/src/components/StatisticsPage/ExportHeader.jsx @@ -0,0 +1,29 @@ +import { useNavigate } from "react-router-dom"; +import cancleIcon from "../../assets/statisticsPage/cancle.svg"; +//check = true면 취소 / 완료 버튼 표시 +//edit은 "편집" 텍스트 띄울지 여부 +//editState는 편집 텍스트를 클릭했는지 여부 (편집 버튼 클릭했으면 true) +const ExportHeader = ({ + title = "제목", + check = false, + edit = false, + editState = false, + handleEdit, +}) => { + const navigate = useNavigate(); + + const handleBackClick = () => { + navigate(-1); + }; + return ( +
+
{title}
+ +
+ ); +}; +export default ExportHeader; diff --git a/bookduck/src/components/StatisticsPage/MonthlyReading.jsx b/bookduck/src/components/StatisticsPage/MonthlyReading.jsx index c0e2ac67..e1fe84a1 100644 --- a/bookduck/src/components/StatisticsPage/MonthlyReading.jsx +++ b/bookduck/src/components/StatisticsPage/MonthlyReading.jsx @@ -5,7 +5,7 @@ const MonthlyReading = ({ userData }) => { return (
@@ -15,7 +15,7 @@ const MonthlyReading = ({ userData }) => {
- {userData.isFirstHalf ? ( + {userData?.isFirstHalf ? (
1월 2월 diff --git a/bookduck/src/components/StatisticsPage/MyKeyword.jsx b/bookduck/src/components/StatisticsPage/MyKeyword.jsx index 82e77929..d0330baf 100644 --- a/bookduck/src/components/StatisticsPage/MyKeyword.jsx +++ b/bookduck/src/components/StatisticsPage/MyKeyword.jsx @@ -1,3 +1,4 @@ +import { useState, useEffect } from "react"; import rectangle1 from "../../assets/statisticsPage/rectangle1.svg"; import rectangle2 from "../../assets/statisticsPage/rectangle2.svg"; import rectangle3 from "../../assets/statisticsPage/rectangle3.svg"; @@ -6,8 +7,41 @@ import rectangle5 from "../../assets/statisticsPage/rectangle5.svg"; import rectangle6 from "../../assets/statisticsPage/rectangle6.svg"; import rectangle7 from "../../assets/statisticsPage/rectangle7.svg"; import rectangle8 from "../../assets/statisticsPage/rectangle8.svg"; +import { getKeywords } from "../../api/statistics"; const MyKeyword = () => { + const [keywordsData, setKeywordsData] = useState(null); + const [error, setError] = useState(false); + useEffect(() => { + const fetchData = async () => { + try { + const res = await getKeywords(); + console.log("조회 성공: ", res); + setKeywordsData(res); + setError(false); + } catch (err) { + console.error("오류 발생: ", err); + setError(true); + } + }; + fetchData(); + }, []); + if (error || !keywordsData || keywordsData.length === 0) { + return ( +
+ 내 기록 키워드예요 +
+ 북덕이 분석하기에 +
정보가 부족해요 +
+
+ ); + } + + // 키워드 배열에 따른 동적 렌더링 + const [keyword1, keyword2, keyword3, keyword4, keyword5, keyword6] = + keywordsData; + return (
내 기록 키워드예요 @@ -16,13 +50,13 @@ const MyKeyword = () => { className="mt-[0.23rem] pt-[3.75rem] pl-4 text-t1 text-white w-[7.0625rem] h-[6.625rem]" style={{ backgroundImage: `url(${rectangle1})` }} > - 슬픈 + {keyword1}
- 아름 + {keyword2}
{ writingMode: "vertical-rl", }} > - 귀여운운 + {keyword3}
{ className="absolute top-[7.3125rem] flex items-center pl-[1.4375rem] text-b1 text-gray-400 w-[184px] h-[2.125rem]" style={{ backgroundImage: `url(${rectangle6})` }} > - 아름다운 + {keyword4}
- 아름다운 + {keyword5}
- 아름다운 + {keyword6}
diff --git a/bookduck/src/components/StatisticsPage/PreferredAuthor.jsx b/bookduck/src/components/StatisticsPage/PreferredAuthor.jsx index 29954a5f..75be0a67 100644 --- a/bookduck/src/components/StatisticsPage/PreferredAuthor.jsx +++ b/bookduck/src/components/StatisticsPage/PreferredAuthor.jsx @@ -29,7 +29,7 @@ const PreferredAuthor = ({ author, imgPath }) => { ) : (
선호하는 작가예요 -
+
북덕이 분석하기에
정보가 부족해요
diff --git a/bookduck/src/components/StatisticsPage/SummaryExport.jsx b/bookduck/src/components/StatisticsPage/SummaryExport.jsx new file mode 100644 index 00000000..9d7a800c --- /dev/null +++ b/bookduck/src/components/StatisticsPage/SummaryExport.jsx @@ -0,0 +1,113 @@ +import { useEffect, useState } from "react"; +import summary from "../../assets/statisticsPage/summary.svg"; +import star from "../../assets/statisticsPage/star-summary.svg"; +import divider from "../../assets/statisticsPage/divider.svg"; +import { getExportSummaryInfo } from "../../api/statistics"; +const getSeasonAndDateRange = (currentDate) => { + const date = new Date(currentDate); + const year = date.getFullYear(); + const month = date.getMonth() + 1; + + if (month === 12) { + return { + dateRange: `${year}`, // 연말결산은 연도만 표시 + }; + } + if (month >= 1 && month <= 2) { + return { + dateRange: `${year}.01${month === 2 ? `~${year}.02` : ""}`, // 1월: 01, 2월: 01~02 + }; + } + if (month >= 3 && month <= 5) { + return { + dateRange: `${year}.03${ + month > 3 ? `~${year}.${String(month).padStart(2, "0")}` : "" + }`, + }; + } + if (month >= 6 && month <= 8) { + return { + dateRange: `${year}.06${ + month > 6 ? `~${year}.${String(month).padStart(2, "0")}` : "" + }`, + }; + } + if (month >= 9 && month <= 11) { + return { + dateRange: `${year}.09${ + month > 9 ? `~${year}.${String(month).padStart(2, "0")}` : "" + }`, + }; + } +}; +const SummaryExport = () => { + const [exportData, setExportData] = useState(null); + const seasonDate = getSeasonAndDateRange(exportData?.currentDate); + + useEffect(() => { + const fetchData = async () => { + try { + const res = await getExportSummaryInfo(); + console.log("조회 성공: ", res); + setBookData(res); + } catch (err) { + console.error("오류 발생: ", err); + } + }; + fetchData(); + }, []); + return ( +
+
+ {exportData?.nickname}님의 +
+ {exportData?.season} 독서 리포트 +
+
+ Date {seasonDate?.dateRange} +
+
+ +
+ 다 읽은 책{exportData?.finishedBookCount} +
+ +
+
+ 선호하는 작가{exportData?.mostReadAuthor} +
+
+ 선호하는 장르{exportData?.mostReadGenre} +
+
+ 기록 키워드{exportData?.keyword} +
+
+ +
+
+ 발췌{exportData?.excerptCount} +
+
+ 감상{exportData?.reviewCount} +
+
+ TOTAL{exportData?.bookRecordCount} +
+
+ +
+
+ BookDuck +
+ +
+ ); +}; +export default SummaryExport; diff --git a/bookduck/src/components/StatisticsPage/SummaryFloatingButton.jsx b/bookduck/src/components/StatisticsPage/SummaryFloatingButton.jsx index 8fd80054..dcfba1f0 100644 --- a/bookduck/src/components/StatisticsPage/SummaryFloatingButton.jsx +++ b/bookduck/src/components/StatisticsPage/SummaryFloatingButton.jsx @@ -1,9 +1,39 @@ +import { useEffect, useState } from "react"; + const SummaryFloatingButton = () => { + const [isVisible, setIsVisible] = useState(true); // 버튼 표시 여부 상태 + const [lastScrollY, setLastScrollY] = useState(0); // 마지막 스크롤 위치 + const handleScroll = () => { + const currentScrollY = window.scrollY; + + if (currentScrollY > lastScrollY) { + // 스크롤 내릴 때 + setIsVisible(false); + } else { + // 스크롤 올릴 때 + setIsVisible(true); + } + + setLastScrollY(currentScrollY); // 마지막 스크롤 위치 업데이트 + }; + + useEffect(() => { + window.addEventListener("scroll", handleScroll); + return () => { + window.removeEventListener("scroll", handleScroll); + }; + }, [lastScrollY]); return ( -
- 요약 이미지로 내보낼 수 있어요 -
- 보러가기 +
+ 요약된 이미지로 저장할 수 있어요 +
+ 이미지 보기
); diff --git a/bookduck/src/pages/BookInfoPage/BoonInfoAddedPage.jsx b/bookduck/src/pages/BookInfoPage/BookInfoAddedPage.jsx similarity index 52% rename from bookduck/src/pages/BookInfoPage/BoonInfoAddedPage.jsx rename to bookduck/src/pages/BookInfoPage/BookInfoAddedPage.jsx index c7c73714..3a14a2b4 100644 --- a/bookduck/src/pages/BookInfoPage/BoonInfoAddedPage.jsx +++ b/bookduck/src/pages/BookInfoPage/BookInfoAddedPage.jsx @@ -1,19 +1,38 @@ -import { useState } from "react"; +import { useState, useEffect } from "react"; +import { useParams } from "react-router-dom"; +import polygon from "../../assets/characterPage/polygon.svg"; +import noArchive from "../../assets/bookinfoPage/user-no-archive.svg"; import BookInfo from "../../components/BookInfoPage/BookInfo"; import ArchiveView from "../../components/BookInfoPage/ArchiveView"; import FloatingRecordButton from "../../components/common/FloatingRecordButton"; import MyComment from "../../components/BookInfoPage/MyComment"; import Header2 from "../../components/RecordingPage/Header2"; import Header3 from "../../components/common/Header3"; -import UserDuck from "../../components/CharacterPage/UserDuck"; +import { getCustomBookInfo } from "../../api/bookinfo"; // 유저가 직접 추가한 책 정보 페이지 const BookInfoAddedPage = () => { + const { bookinfoId } = useParams(); + const [bookData, setBookData] = useState(null); + console.log(`/bookinfo/custom/${bookinfoId}`); + useEffect(() => { + const fetchData = async () => { + try { + const res = await getCustomBookInfo({ bookinfoId }); + console.log("조회성공: ", res); + setBookData(res); + } catch (err) { + console.error("오류 발생: ", err); + } + }; + fetchData(); + }, [bookinfoId]); + const [activeTab, setActiveTab] = useState("기록"); // api받았을때 isMe가 me이면 내가 등록한 페이지, other이면 타유저가 등록한 페이지 - const isMe = "other"; + const isMe = bookData?.isMine ? "me" : "other"; //true or false return (
- {isMe === "me" ? ( + {bookData?.isMine ? (
@@ -21,7 +40,7 @@ const BookInfoAddedPage = () => {
- +
@@ -39,10 +58,17 @@ const BookInfoAddedPage = () => { ) : (
-
- -
- +
+ +
+
+
+ 다른유저가 직접 등록한 책에는
+ 기록을 남길 수 없어요. +
+ +
+
diff --git a/bookduck/src/pages/BookInfoPage/BookInfoPage.jsx b/bookduck/src/pages/BookInfoPage/BookInfoPage.jsx index 6403ecbd..6186ca46 100644 --- a/bookduck/src/pages/BookInfoPage/BookInfoPage.jsx +++ b/bookduck/src/pages/BookInfoPage/BookInfoPage.jsx @@ -14,7 +14,6 @@ const BookInfoPage = () => { const [activeTab, setActiveTab] = useState("책 정보"); const [RatingListData, setRatingListData] = useState(null); const [bookData, setBookData] = useState(null); - const [loading, setLoading] = useState(true); useEffect(() => { const fetchData = async () => { @@ -27,12 +26,10 @@ const BookInfoPage = () => { console.log("조회2 성공: ", res2); } catch (err) { console.error("오류 발생: ", err); - } finally { - setLoading(false); } }; fetchData(); - }, []); + }, [bookinfoId]); return (
@@ -43,22 +40,25 @@ const BookInfoPage = () => {
+
+
-
- {activeTab === "책 정보" && ( - - )} - {activeTab === "기록" && } -
- + + {activeTab === "책 정보" && ( + + )} + {activeTab === "기록" && } +
+ +
diff --git a/bookduck/src/pages/CharacterPage/CharacterPage.jsx b/bookduck/src/pages/CharacterPage/CharacterPage.jsx index 84254b76..84246161 100644 --- a/bookduck/src/pages/CharacterPage/CharacterPage.jsx +++ b/bookduck/src/pages/CharacterPage/CharacterPage.jsx @@ -87,8 +87,9 @@ const CharacterPage = () => {
+ className={`bg-orange-gradation-level rounded-[6.25rem] h-5`} + style={{ width: `${expProgress}%` }} + />
{expProgress}%
diff --git a/bookduck/src/pages/CharacterPage/MyBadgePage.jsx b/bookduck/src/pages/CharacterPage/MyBadgePage.jsx index 1cba273e..00722e17 100644 --- a/bookduck/src/pages/CharacterPage/MyBadgePage.jsx +++ b/bookduck/src/pages/CharacterPage/MyBadgePage.jsx @@ -1,13 +1,44 @@ +import { useState, useEffect } from "react"; import BadgeHeader from "../../components/CharacterPage/BadgeHeader"; import BadgeList from "../../components/CharacterPage/BadgeList"; +import { getBadgeLists } from "../../api/character"; const MyBadgePage = () => { + const [badgeData, setBadgeData] = useState(null); + useEffect(() => { + const fetchData = async () => { + try { + const res = await getBadgeLists(); + console.log("조회 성공: ", res); + setBadgeData(res); + } catch (err) { + console.error("오류 발생: ", err); + } + }; + fetchData(); + }, []); return (
- - - - + + + +
); }; diff --git a/bookduck/src/pages/StatisticsPage/CharacterExportPage.jsx b/bookduck/src/pages/StatisticsPage/CharacterExportPage.jsx index ffa53909..b3d671d5 100644 --- a/bookduck/src/pages/StatisticsPage/CharacterExportPage.jsx +++ b/bookduck/src/pages/StatisticsPage/CharacterExportPage.jsx @@ -1,10 +1,10 @@ -import Header3 from "../../components/common/Header3"; +import ExportHeader from "../../components/StatisticsPage/ExportHeader"; import ExportCharacter from "../../components/StatisticsPage/ExportCharacter"; const CharacterExportPage = () => { return (
- +
diff --git a/bookduck/src/pages/StatisticsPage/StatisticsPage.jsx b/bookduck/src/pages/StatisticsPage/StatisticsPage.jsx index e86a8e3a..7b749ec6 100644 --- a/bookduck/src/pages/StatisticsPage/StatisticsPage.jsx +++ b/bookduck/src/pages/StatisticsPage/StatisticsPage.jsx @@ -1,4 +1,5 @@ import { useEffect, useState } from "react"; +import { useParams } from "react-router-dom"; import Header3 from "../../components/common/Header3"; import RecordCalender from "../../components/StatisticsPage/RecordCalender"; import Divider2 from "../../components/common/Divider2"; @@ -9,18 +10,16 @@ import PreferredGenre from "../../components/StatisticsPage/PreferredGenre"; import MonthlyReading from "../../components/StatisticsPage/MonthlyReading"; import UserCard from "../../components/StatisticsPage/UserCard"; import { getUserStatisticsInfo } from "../../api/statistics"; -import { getUserId } from "../../api/oauth"; +// import { getUserId } from "../../api/oauth"; const StatisticsPage = () => { + const { userId } = useParams(); const [userData, setUserData] = useState(null); const [loading, setLoading] = useState(true); useEffect(() => { const fetchData = async () => { try { - const userId = getUserId(); - // const userId = 1; - const res = await getUserStatisticsInfo(userId); console.log("조회성공: ", res); setUserData(res); @@ -31,7 +30,7 @@ const StatisticsPage = () => { } }; fetchData(); - }, []); + }, [userId]); if (loading) { return
; } @@ -43,7 +42,7 @@ const StatisticsPage = () => {
{userData?.isFirstHalf ? "상반기" : "하반기"} 기록은 총{" "} - {userData.excerptCount + userData.bookRecordCount} + {userData?.excerptCount + userData?.bookRecordCount} 개!
@@ -58,12 +57,12 @@ const StatisticsPage = () => {
- + {/* 같은 작가의 작품을 최소 2권 이상을 읽어야 ‘선호하는 작가' 카드가 보임 */} diff --git a/bookduck/src/pages/StatisticsPage/SummaryExportPage.jsx b/bookduck/src/pages/StatisticsPage/SummaryExportPage.jsx new file mode 100644 index 00000000..755fada7 --- /dev/null +++ b/bookduck/src/pages/StatisticsPage/SummaryExportPage.jsx @@ -0,0 +1,45 @@ +import { useRef } from "react"; +import html2canvas from "html2canvas"; +import ExportHeader from "../../components/StatisticsPage/ExportHeader"; +import SummaryExport from "../../components/StatisticsPage/SummaryExport"; +const SummaryExportPage = () => { + const exportRef = useRef(null); + + const handleSaveImage = async () => { + if (exportRef.current) { + try { + const canvas = await html2canvas(exportRef.current, { + backgroundColor: null, // 배경을 투명하게 설정 + logging: false, // 디버깅을 위한 로그 비활성화 + }); + const dataUrl = canvas.toDataURL("image/png"); + const link = document.createElement("a"); + link.href = dataUrl; + link.download = "statistics-summary.png"; + link.click(); + } catch (error) { + console.error("이미지 저장 실패: ", error); + } + } + }; + return ( +
+ +
+
+ +
+
+ 이미지 저장 +
+
+
+ ); +}; +export default SummaryExportPage; diff --git a/bookduck/src/styles/global.css b/bookduck/src/styles/global.css index 64eaacc1..275e7d79 100644 --- a/bookduck/src/styles/global.css +++ b/bookduck/src/styles/global.css @@ -1,6 +1,7 @@ @import url("https://fonts.googleapis.com/css2?family=Inter:wght@100..900&display=swap"); @import url("https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/static/pretendard-dynamic-subset.min.css"); @import url("https://fonts.googleapis.com/css2?family=Baloo+2:wght@400..800&family=Nanum+Gothic&family=Nanum+Myeongjo&display=swap"); +@import url("https://fonts.googleapis.com/css2?family=Caprasimo&display=swap"); @tailwind base; @tailwind components; @tailwind utilities; diff --git a/bookduck/tailwind.config.js b/bookduck/tailwind.config.js index 671cd59e..e1a37a2e 100644 --- a/bookduck/tailwind.config.js +++ b/bookduck/tailwind.config.js @@ -48,6 +48,8 @@ export default { 800: "#364390", }, yellow: { + 50: "#FFFAE6", + 80: "#FFF0B9", 100: "#FFECA4", }, red: "#EA4520", @@ -60,6 +62,8 @@ export default { "linear-gradient(0deg, var(--Primary-300, #FFBF68) 0%, var(--Primary-300, #FFBF68) 100%), linear-gradient(110deg, #FFE16E 0%, #FFBF68 53.83%)", "white-gradiation": "linear-gradient(90deg, rgba(255, 255, 255, 0) 0%, #FFF 35.43%)", + "yellow-gradation": + "linear-gradient(199deg, #FFF 53.87%, #FFFAE6 87.72%)", }, fontSize: { t1: ["1.5rem", { lineHeight: "2.25rem" }], //24px @@ -72,13 +76,16 @@ export default { btn3: ["0.875rem", { lineHeight: "1.25rem" }], //14px / 20px btn4: ["0.75rem", { lineHeight: "1rem" }], //12px / 16px c1: ["0.75rem", { lineHeight: "1rem" }], - c2: ["0.6875rem", { lineHeight: "0.8125rem" }], + c2: ["0.6875rem", { lineHeight: "0.8125rem" }], //11px /13px c3: ["2rem", { lineHeight: "2.25rem" }], //32px / 36px }, fontWeight: { semibold: 600, regular: 400, }, + fontFamily: { + caprasimo: ["Caprasimo", "sans-serif"], // Caprasimo 폰트 추가 + }, boxShadow: { custom: "0px 0px 3px 0px rgba(0, 0, 0, 0.10)", custom2: "0px 0px 10px 0px rgba(0, 0, 0, 0.10)", diff --git a/bookduck/yarn.lock b/bookduck/yarn.lock index 9297a190..c20e7d81 100644 --- a/bookduck/yarn.lock +++ b/bookduck/yarn.lock @@ -2391,6 +2391,11 @@ balanced-match@^1.0.0: resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +base64-arraybuffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz#1c37589a7c4b0746e34bd1feb951da2df01c1bdc" + integrity sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ== + base64-js@^1.3.1, base64-js@^1.5.1: version "1.5.1" resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz" @@ -2631,6 +2636,13 @@ css-box-model@^1.2.0, css-box-model@^1.2.1: dependencies: tiny-invariant "^1.0.6" +css-line-break@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/css-line-break/-/css-line-break-2.1.0.tgz#bfef660dfa6f5397ea54116bb3cb4873edbc4fa0" + integrity sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w== + dependencies: + utrie "^1.0.2" + css-select@^5.1.0: version "5.1.0" resolved "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz" @@ -3481,6 +3493,13 @@ hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2: dependencies: react-is "^16.7.0" +html2canvas@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/html2canvas/-/html2canvas-1.4.1.tgz#7cef1888311b5011d507794a066041b14669a543" + integrity sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA== + dependencies: + css-line-break "^2.1.0" + text-segmentation "^1.0.3" http-parser-js@>=0.5.1: version "0.5.8" resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.8.tgz#af23090d9ac4e24573de6f6aecc9d84a48bf20e3" @@ -4927,6 +4946,13 @@ tailwindcss@^3.4.13: resolve "^1.22.2" sucrase "^3.32.0" +text-segmentation@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/text-segmentation/-/text-segmentation-1.0.3.tgz#52a388159efffe746b24a63ba311b6ac9f2d7943" + integrity sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw== + dependencies: + utrie "^1.0.2" + text-table@^0.2.0: version "0.2.0" resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz" @@ -5156,6 +5182,13 @@ utility-types@^3.10.0: resolved "https://registry.npmjs.org/utility-types/-/utility-types-3.11.0.tgz" integrity sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw== +utrie@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/utrie/-/utrie-1.0.2.tgz#d42fe44de9bc0119c25de7f564a6ed1b2c87a645" + integrity sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw== + dependencies: + base64-arraybuffer "^1.0.2" + uuid@^9.0.1: version "9.0.1" resolved "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz"