From 8776326dc0fbcab8fafecb24f2a307df15fc0b60 Mon Sep 17 00:00:00 2001 From: DarrellRoberts Date: Thu, 29 Jan 2026 15:25:38 +0100 Subject: [PATCH 1/3] chor: add placeholder bg for ol images --- src/components/books/library/BookCover.tsx | 21 ++++++++------ .../books/randomiser/RandomSectionRight.tsx | 28 +++++++++++-------- 2 files changed, 30 insertions(+), 19 deletions(-) diff --git a/src/components/books/library/BookCover.tsx b/src/components/books/library/BookCover.tsx index b8bea65..5cc4394 100644 --- a/src/components/books/library/BookCover.tsx +++ b/src/components/books/library/BookCover.tsx @@ -5,6 +5,7 @@ import useUserFetch from "@/hooks/fetch-hooks/useUserFetch" import { useAppSelector } from "@/store/lib/hooks" import { config } from "@/configs/config" import Image from "next/image" +import { useState } from "react" type Props = { title: string @@ -25,6 +26,7 @@ const BookCover: React.FC = ({ isSingleBook, imageURL, }) => { + const [isLoading, setIsLoading] = useState(true) const token = useAppSelector((state) => state.token.tokenState) const { decodedToken }: { decodedToken?: { username: string; _id: string } } = useJwt(token) @@ -64,7 +66,15 @@ const BookCover: React.FC = ({ } >
- {imageURL.length ? ( + {isLoading && ( +
+

{title}

+

+ (Image pending) +

+
+ )} + {imageURL && (
= ({ height={500} alt={title} className="w-fit h-full" + style={{ display: isLoading ? "hidden" : "block" }} + onLoad={() => setIsLoading(false)} />
- ) : ( -
-

{title}

-

- (Image pending) -

-
)}

Book Club Brothers

diff --git a/src/components/books/randomiser/RandomSectionRight.tsx b/src/components/books/randomiser/RandomSectionRight.tsx index 6c72101..4f60de2 100644 --- a/src/components/books/randomiser/RandomSectionRight.tsx +++ b/src/components/books/randomiser/RandomSectionRight.tsx @@ -1,4 +1,4 @@ -import React from "react" +import React, { useState } from "react" import Randomiser from "./Randomiser" import EditUnreadBook from "@/components/forms/bookform-randomise/edit/EditUnreadBook" import DeleteBook from "@/components/forms/bookform-randomise/DeleteBook" @@ -16,6 +16,7 @@ type Props = { } const RandomSectionRight: React.FC = ({ bookData, error, userData }) => { + const [isLoading, setIsLoading] = useState(true) const index = useAppSelector((state) => state.randomise.index) const showRandom = useAppSelector((state) => state.randomise.showRandom) const { decodedToken } = useAuth() @@ -28,16 +29,21 @@ const RandomSectionRight: React.FC = ({ bookData, error, userData }) => { return (
- {bookData && bookData[index] ? ( - {bookData[index]?.title} - ) : null} + {bookData && bookData[index] && ( + <> + {bookData[index]?.title} setIsLoading(false)} + /> +
+ + )} {!bookData ? ( <>
From 405a209539d1aa633b640eb6bb8445eb0dabd665 Mon Sep 17 00:00:00 2001 From: DarrellRoberts Date: Fri, 30 Jan 2026 23:58:59 +0100 Subject: [PATCH 2/3] style: add dynamic rating to old style images --- src/app/(home)/books/library/page.tsx | 6 ++ src/components/books/library/BookCover.tsx | 74 ++++--------------- .../books/library/BookImageCover.tsx | 52 ++++++++++--- .../books/library/BookRatingsBox.tsx | 74 +++++++++++++++++++ .../library/single-book/AdminViewLeftSide.tsx | 17 +++-- .../library/single-book/UserViewLeftSide.tsx | 17 +++-- .../brothers/dashboard/BrotherBanner.tsx | 20 ++++- .../image/EditImage.tsx | 26 +------ 8 files changed, 180 insertions(+), 106 deletions(-) create mode 100644 src/components/books/library/BookRatingsBox.tsx diff --git a/src/app/(home)/books/library/page.tsx b/src/app/(home)/books/library/page.tsx index f8995f9..587d54d 100644 --- a/src/app/(home)/books/library/page.tsx +++ b/src/app/(home)/books/library/page.tsx @@ -84,6 +84,12 @@ const Booklibrary: React.FC = () => { ) : ( diff --git a/src/components/books/library/BookCover.tsx b/src/components/books/library/BookCover.tsx index 5cc4394..e446ed5 100644 --- a/src/components/books/library/BookCover.tsx +++ b/src/components/books/library/BookCover.tsx @@ -1,11 +1,8 @@ "use client" -import { useJwt } from "react-jwt" -import useUserFetch from "@/hooks/fetch-hooks/useUserFetch" -import { useAppSelector } from "@/store/lib/hooks" -import { config } from "@/configs/config" import Image from "next/image" import { useState } from "react" +import BookRatingsBox from "./BookRatingsBox" type Props = { title: string @@ -27,41 +24,13 @@ const BookCover: React.FC = ({ imageURL, }) => { const [isLoading, setIsLoading] = useState(true) - const token = useAppSelector((state) => state.token.tokenState) - const { decodedToken }: { decodedToken?: { username: string; _id: string } } = - useJwt(token) - const username = decodedToken?.username - const { userData, loadingUsers, error } = useUserFetch( - `${config.API_URL}/users`, - null, - ) - - const findUser = (id) => { - const user = userData?.find((user) => user._id === id) - return user ? user.username : "user not found" - } - - const raterArr2 = raterArr?.map((id) => findUser(id)) - - let raterObj: object = {} - const findBookScore = () => { - if (raterArr2) { - for (let i = 0; i < raterArr2.length; i++) { - raterObj[raterArr2[i]] = ratingArr[i] - findUser(raterObj[raterArr[i]]) - } - raterObj = Object.entries(raterObj) - return raterObj - } - } - findBookScore() return ( <>
@@ -75,44 +44,29 @@ const BookCover: React.FC = ({
)} {imageURL && ( -
+
{title} setIsLoading(false)} />
)} -
-

Book Club Brothers

- - {Array.isArray(raterObj) && !loadingUsers ? ( - raterObj.map(([name, value]) => ( -
  • - {name}:{" "} - {hideScores && username !== name ? "?" : value.toFixed(2)} -
  • - )) - ) : error ? ( -
  • {error?.message}
  • - ) : ( -
  • Score Pending...
  • - )} - -
  • - Group Rating:{" "} - {totalScore - ? hideScores - ? "?" - : Math.floor(totalScore * 100) / 100 - : "Pending..."} -
  • -
    +
    diff --git a/src/components/books/library/BookImageCover.tsx b/src/components/books/library/BookImageCover.tsx index da25c6a..fd00dfe 100644 --- a/src/components/books/library/BookImageCover.tsx +++ b/src/components/books/library/BookImageCover.tsx @@ -1,21 +1,53 @@ import Image from "next/image" +import BookRatingsBox from "./BookRatingsBox" type Props = { title?: string imageURL: string + totalScore: number + ratingArr: number[] | number + raterArr: string[] + hideScores: boolean + isSingleBook: boolean } -const BookImageCover: React.FC = ({ title, imageURL }) => { +const BookImageCover: React.FC = ({ + title, + imageURL, + totalScore, + ratingArr, + raterArr, + hideScores, + isSingleBook, +}) => { return ( - <> - {title} - +
    +
    + {title} +
    +
    + +
    +
    ) } diff --git a/src/components/books/library/BookRatingsBox.tsx b/src/components/books/library/BookRatingsBox.tsx new file mode 100644 index 0000000..c5f6ba9 --- /dev/null +++ b/src/components/books/library/BookRatingsBox.tsx @@ -0,0 +1,74 @@ +import { config } from "@/configs/config" +import useUserFetch from "@/hooks/fetch-hooks/useUserFetch" +import { useAppSelector } from "@/store/lib/hooks" +import React, { memo, useMemo } from "react" +import { useJwt } from "react-jwt" + +type Props = { + totalScore: number + ratingArr: number[] | number + raterArr: string[] + hideScores: boolean +} + +const BookRatingsBox = ({ + totalScore, + ratingArr, + raterArr, + hideScores, +}: Props) => { + const token = useAppSelector((state) => state.token.tokenState) + const { decodedToken }: { decodedToken?: { username: string; _id: string } } = + useJwt(token) + const username = decodedToken?.username + + const { userData, loadingUsers, error } = useUserFetch( + `${config.API_URL}/users`, + null, + ) + + const formattedRatings = useMemo(() => { + if (!userData || !raterArr || !Array.isArray(ratingArr)) return [] + + const userLookup = userData.reduce( + (acc, user) => { + acc[user._id] = user.username + return acc + }, + {} as Record, + ) + + return raterArr.map((id, index) => ({ + name: userLookup[id] || "User not found", + score: Array.isArray(ratingArr) ? ratingArr[index] : 0, + })) + }, [userData, raterArr, ratingArr]) + return ( +
    +

    Book Club Brothers

    + + {!loadingUsers && formattedRatings.length > 0 ? ( + formattedRatings.map(({ name, score }) => ( +
  • + {name}: {hideScores && username !== name ? "?" : score.toFixed(2)} +
  • + )) + ) : error ? ( +
  • {error?.message}
  • + ) : ( +
  • Score Pending...
  • + )} + +
  • + Group Rating:{" "} + {totalScore + ? hideScores + ? "?" + : Math.floor(totalScore * 100) / 100 + : "Pending..."} +
  • +
    + ) +} + +export default BookRatingsBox diff --git a/src/components/books/library/single-book/AdminViewLeftSide.tsx b/src/components/books/library/single-book/AdminViewLeftSide.tsx index 3e9af31..da039c7 100644 --- a/src/components/books/library/single-book/AdminViewLeftSide.tsx +++ b/src/components/books/library/single-book/AdminViewLeftSide.tsx @@ -12,6 +12,7 @@ import Profile from "@/components/misc/profile/Profile" import useSingleUserFetch from "@/hooks/fetch-hooks/useSingleUserFetch" import Image from "next/image" import { config } from "@/configs/config" +import BookImageCover from "../BookImageCover" type Props = { bookData: Book @@ -49,12 +50,16 @@ const AdminViewSingleBook: React.FC = ({ bookData, bookId }) => {
    {bookData?.reviewImageURL ? ( - book_review_image ) : ( {
    {bookData?.reviewImageURL ? ( - book_review_image ) : ( = ({ user, readBooks }) => {
    {findMinBook?.reviewImageURL ? ( - + ) : ( = ({ user, readBooks }) => {
    {findMaxBook?.reviewImageURL ? ( - + ) : ( = ({ id }) => { try { const formData = new FormData() formData.append("picture", image, image?.name) - await axios.post(`${config.API_URL}/books/${id}`, formData, { + await axios.post(`${config.API_URL}books/${id}`, formData, { headers: { Authorization: `Bearer ${token}`, }, @@ -71,7 +71,7 @@ const EditImage: React.FC = ({ id }) => { setLoadings((prevLoadings) => { const newLoadings = [...prevLoadings] newLoadings[index] = false - document.location.reload() + // document.location.reload() return newLoadings }) }, 4000) @@ -84,6 +84,7 @@ const EditImage: React.FC = ({ id }) => { name="picture_upload_form" initialValues={{ fileList: [] }} > + {/* */} = ({ id }) => {
    - - - false} - > -
    - -
    Upload
    -
    -
    -
    -
    + {/* */} Date: Sat, 31 Jan 2026 00:07:32 +0100 Subject: [PATCH 3/3] fix: vercel err --- src/components/books/library/BookImageCover.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/books/library/BookImageCover.tsx b/src/components/books/library/BookImageCover.tsx index fd00dfe..4d2a897 100644 --- a/src/components/books/library/BookImageCover.tsx +++ b/src/components/books/library/BookImageCover.tsx @@ -8,7 +8,7 @@ type Props = { ratingArr: number[] | number raterArr: string[] hideScores: boolean - isSingleBook: boolean + isSingleBook?: boolean } const BookImageCover: React.FC = ({