Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
65e01da
style: expands book width on book library
DarrellRoberts Feb 5, 2026
7ddfc1c
Merge pull request #215 from DarrellRoberts/bcb-214-353-fix-book-scor…
DarrellRoberts Feb 5, 2026
8a650ae
style: restyle book cards on book library
DarrellRoberts Feb 5, 2026
0a738c9
Merge pull request #217 from DarrellRoberts/bcb-216-374-style-rating-…
DarrellRoberts Feb 5, 2026
ef8ec16
style: tweak width and colors of cards
DarrellRoberts Feb 6, 2026
dabf584
Merge pull request #218 from DarrellRoberts/dr-book-card-style-tweak
DarrellRoberts Feb 6, 2026
b02e53e
feat: add toast to all forms
DarrellRoberts Feb 6, 2026
83a5db8
remove unneeded prop
DarrellRoberts Feb 6, 2026
371d819
fix: add extra toast object to delete
DarrellRoberts Feb 6, 2026
9fd67df
Merge pull request #221 from DarrellRoberts/bcb-219-287-add-toast-not…
DarrellRoberts Feb 6, 2026
f081c56
style: diff design for single book
DarrellRoberts Feb 16, 2026
bac35e4
Merge pull request #222 from DarrellRoberts/bcb-single-book-redesign
DarrellRoberts Feb 16, 2026
8d05178
fix: fixes single book review image & fixes suggested profile pic CLS
DarrellRoberts Feb 19, 2026
89aaf05
Merge pull request #226 from DarrellRoberts/bcb-225-181-fix-books-rev…
DarrellRoberts Feb 19, 2026
69b81d0
chor: replaces 4 custom get hooks with one custom hook
DarrellRoberts Feb 20, 2026
4de7c40
adds useMutuation hook & setups fail safe for no data
DarrellRoberts Feb 26, 2026
e9084f6
add more mutation hooks
DarrellRoberts Feb 27, 2026
f03c162
adds hook to brother form
DarrellRoberts Mar 2, 2026
233f85c
adds usemutation hook to login form
DarrellRoberts Mar 3, 2026
6f772b3
fixes genre bug
DarrellRoberts Mar 3, 2026
d0f7700
Merge pull request #227 from DarrellRoberts/bcb-224-779-refactor-crud…
DarrellRoberts Mar 3, 2026
3a9b1ee
fixes bug on BookStatsMeetingData component
DarrellRoberts Mar 3, 2026
ea125fd
Merge pull request #229 from DarrellRoberts/bcb-fix-book-stats-page
DarrellRoberts Mar 3, 2026
cb9597d
fixes book stat page
DarrellRoberts Mar 3, 2026
2897778
passes all unit tests
DarrellRoberts Mar 3, 2026
179488f
Merge pull request #230 from DarrellRoberts/bcb-book-stats-page-fix
DarrellRoberts Mar 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ yarn-error.log*

# local env files
.env*.local
.env.production
.env

# vercel
Expand Down
Binary file added public/Profile.unknown-profile-image.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/book-library/book-card-cert-fresh.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/book-library/book-card-cert-rotten.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 4 additions & 4 deletions src/__tests__/unit/stat-functions/genreScoreFunction.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,22 @@ describe("functions that return the book scores by genre", () => {
const mockBookArray = [
{
id: 1,
genre: [["Horror"]],
genre: ["Horror"],
totalScore: 8,
},
{
id: 2,
genre: [["Thriller"]],
genre: ["Thriller"],
totalScore: 5,
},
{
id: 3,
genre: [["Comedy"]],
genre: ["Comedy"],
totalScore: 4,
},
{
id: 4,
genre: [["Horror"]],
genre: ["Horror"],
totalScore: 3,
},
] as unknown as Book[]
Expand Down
31 changes: 20 additions & 11 deletions src/app/(home)/books/library/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import Loader from "@/components/loader/Loader"
import RatingCon from "../../../../../components/books/library/single-book/RatingCon"
import CommentCon from "../../../../../components/books/library/single-book/CommentCon"
import { handleHideScores_NoSetter } from "@/utils/time-functions/hideScores"
import useBookFetch from "@/hooks/fetch-hooks/useReadBookFetch"
import { useAppSelector } from "@/store/lib/hooks"
import { useAuth } from "@/hooks/auth-hooks/useAuth"
import UserViewLeftSide from "@/components/books/library/single-book/UserViewLeftSide"
Expand All @@ -15,7 +14,11 @@ import AdminViewRightSide from "@/components/books/library/single-book/AdminView
import UserViewRightSide from "@/components/books/library/single-book/UserViewRightSide"
import { useState } from "react"
import NavigateBook from "@/components/books/library/single-book/NavigateBook"
import { config } from "@/configs/config"
import { API_SINGLE_BOOK, config } from "@/configs/config"
import SuggestedByIcon from "@/components/books/library/single-book/SuggestedByIcon"
import { Book } from "@/types/BookInterface"
import { useGetQuery } from "@/hooks/fetch-hooks/useGetQuery"
import { TIME_MILLISECONDS } from "@/hooks/timeVars"

const SingleBook: React.FC = () => {
const [showLeftNavArrows, setShowLeftNavArrows] = useState<boolean>(true)
Expand All @@ -25,25 +28,31 @@ const SingleBook: React.FC = () => {
const adminId = process.env.NEXT_PUBLIC_ADMIN_ID
const { decodedToken } = useAuth()

const { bookData, loadingBooks, error } = useBookFetch(
`${config.API_URL}/books/${id}`,
id
)
const {
data: bookData,
isLoading,
isError,
error,
} = useGetQuery<Book>({
queryKey: ["books", id],
apiPath: `${API_SINGLE_BOOK}${id}`,
})

const showDelete = useAppSelector((state) => state.editBookButtons.showDelete)
return showDelete ? (
<h1 className="font-main text-[5rem] m-4 pb-[5rem] md:text-[3.5rem] md:mt-8 md:text-center sm:text-[2.5rem]">
Book is deleted
</h1>
) : error ? (
) : isError ? (
<h1>{error.message}</h1>
) : loadingBooks && !bookData ? (
) : isLoading && !bookData ? (
<Loader />
) : (
<div className="gap-6 flex flex-col items-center">
<h1 className="text-7xl max-lg:text-5xl m-0 text-center mt-8">
<h1 className="text-7xl max-lg:text-5xl m-0 text-center mt-8 max-lg:h-45">
{bookData?.title}
</h1>
<SuggestedByIcon bookData={bookData} />
<div className="flex w-full justify-around items-center my-8">
{showLeftNavArrows && (
<NavigateBook
Expand All @@ -59,7 +68,7 @@ const SingleBook: React.FC = () => {
/>
)}
</div>
<div className="flex justify-around w-full max-lg:flex-col">
<div className="flex justify-around w-full max-lg:flex-col max-lg:gap-5">
{decodedToken?._id === adminId ? (
<AdminViewLeftSide bookData={bookData} bookId={bookData._id} />
) : (
Expand All @@ -78,7 +87,7 @@ const SingleBook: React.FC = () => {
<RatingCon
singleBook={bookData}
id={id}
loading={loadingBooks}
loading={isLoading}
hideScores={handleHideScores_NoSetter(bookData?.actualDateOfMeeting)}
/>{" "}
<CommentCon
Expand Down
113 changes: 54 additions & 59 deletions src/app/(home)/books/library/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,41 @@

import { useState, useEffect } from "react"
import Loader from "@/components/loader/Loader"
import BookCover from "@/components/books/library/BookCover"
import Link from "next/link"
import Search from "@/components/misc/search/Search"
import { Button, ConfigProvider } from "antd"
import BookImageCover from "@/components/books/library/BookImageCover"
import { handleHideScores_NoSetter } from "@/utils/time-functions/hideScores"
import useBookFetch from "@/hooks/fetch-hooks/useReadBookFetch"
import { Book } from "@/types/BookInterface"
import useScrollRef from "@/hooks/scroll-hooks/useScrollRef"
import useLimit from "@/hooks/scroll-hooks/useLimit"
import BookSkeleton from "@/components/books/library/BookSkeleton"
import { config } from "@/configs/config"
import { API_BOOKS, config } from "@/configs/config"
import { UiButton } from "@/components/ui/button/UiButton"
import BookCard from "@/components/books/library/BookCard"
import { useGetQuery } from "@/hooks/fetch-hooks/useGetQuery"
import { TIME_MILLISECONDS } from "@/hooks/timeVars"

const Booklibrary: React.FC = () => {
const [searchBar, setSearchBar] = useState<string>("")
const [books, setBooks] = useState<Book[]>([])
const [books, setBooks] = useState<Book[] | string[]>([])

const { limit, handleLimit, setIsLimit, isLimit } = useLimit()

const { bookData, loadingBooks, error } = useBookFetch(
`${config.API_URL}/books`,
limit,
)
const {
data: bookData,
isLoading,
isError,
error,
} = useGetQuery<Book[]>({
queryKey: ["books"],
apiPath: API_BOOKS,
staleTime: TIME_MILLISECONDS.ONE_MONTH,
})

const readBooks = bookData?.filter((book) => book.read === true)
const readBooks = bookData?.length
? bookData.filter((book) => book.read === true)
: []

const lastItemRef = useScrollRef(loadingBooks, limit, handleLimit)
const lastItemRef = useScrollRef(isLoading, limit, handleLimit)

const filteredResults = Array.isArray(readBooks)
? readBooks?.filter((book) =>
Expand All @@ -39,80 +46,68 @@ const Booklibrary: React.FC = () => {
: ["No results"]

useEffect(() => {
if (!loadingBooks) setBooks(filteredResults)
if (!isLoading) setBooks(filteredResults)
}, [searchBar])

useEffect(() => {
if (books.length === 0 && !loadingBooks) setBooks(filteredResults)
setBooks((prevItems) => [
...prevItems,
...filteredResults.slice(prevItems.length + 1, limit),
])
if (books.length === 0 && !isLoading) setBooks(filteredResults)
setBooks(
(prevItems) =>
[
...prevItems,
...filteredResults.slice(prevItems.length + 1, limit),
] as Book[],
)
const timer = setTimeout(() => setIsLimit(false), 500)
return () => clearTimeout(timer)
}, [limit, loadingBooks])
}, [limit, isLoading])

return (
<>
<div className="flex justify-between m-6 max-xs:flex-col-reverse max-xs:items-center ">
<Search
setSearchBar={setSearchBar}
filteredBooks={filteredResults}
isDisabled={loadingBooks}
isDisabled={isLoading}
/>
<UiButton isLink href="/books/library/3d" textContent="3D View" />
</div>
<h1 className="text-8xl m-5 max-lg:text-6xl max-lg:text-center">
<h1 className="text-8xl m-5 max-lg:text-6xl max-lg:text-center max-lg:mb-20">
Book Library
</h1>
{loadingBooks && books.length === 0 ? (
{isLoading && books.length === 0 ? (
<BookSkeleton freq={8} />
) : error ? (
) : isError ? (
<h2> {error?.message}</h2>
) : (
<div className="flex flex-wrap gap-6 mx-4 max-lg:flex-col max-lg:items-center">
<div className="flex flex-wrap gap-10 mx-4 max-sm:justify-center">
{books?.length > 0 ? (
books?.map((book) => (
<div key={book._id}>
{book.reviewImageURL ? (
<Link href={`/books/library/${book._id}`}>
<Link href={`/books/library/${book._id}`}>
<div className="flex justify-center w-full">
<div className="flex justify-center">
<h2 className="text-3xl sm:text-1.75xl font-bold text-center underline overflow-hidden whitespace-nowrap text-ellipsis max-w-[275px] mb-2">
<h2 className="text-3xl sm:text-1.75xl font-bold text-center underline overflow-hidden whitespace-nowrap text-ellipsis max-w-[250px] mb-2">
{book.title}
</h2>
</div>
<BookImageCover
title={book?.title}
imageURL={book?.reviewImageURL}
totalScore={book?.totalScore}
ratingArr={book?.scoreRatings?.rating}
raterArr={book?.scoreRatings?.raterId}
hideScores={handleHideScores_NoSetter(
book?.actualDateOfMeeting,
)}
/>
</Link>
) : (
<Link href={`/books/library/${book._id}`}>
<div className="flex justify-center w-full">
<div className="flex justify-center">
<h2 className="text-3xl sm:text-1.75xl font-bold text-center underline overflow-hidden whitespace-nowrap text-ellipsis max-w-[275px] mb-2">
{book.title}
</h2>
</div>
</div>
<BookCover
title={book?.title}
totalScore={book?.totalScore}
ratingArr={book?.scoreRatings?.rating}
raterArr={book?.scoreRatings?.raterId}
imageURL={book?.imageURL}
hideScores={handleHideScores_NoSetter(
book?.actualDateOfMeeting,
)}
/>
</Link>
)}
</div>
<BookCard
key={book._id}
title={book.title}
imageURL={
book.reviewImageURL
? book.reviewImageURL
: book?.imageURL
? book?.imageURL
: "/Profile.unknown-profile-image.jpg"
}
totalScore={book?.totalScore}
hideScores={handleHideScores_NoSetter(
book?.actualDateOfMeeting,
)}
/>
</Link>
</div>
))
) : (
Expand Down
36 changes: 22 additions & 14 deletions src/app/(home)/books/randomiser/page.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,39 @@
/* eslint-disable react/react-in-jsx-scope */
"use client"

import useBookFetch from "@/hooks/fetch-hooks/useUnreadBookFetch"
import useUserFetch from "@/hooks/fetch-hooks/useUserFetch"
import RandomSectionLeft from "@/components/books/randomiser/RandomSectionLeft"
import RandomSectionRight from "@/components/books/randomiser/RandomSectionRight"
import RandomiserFilters from "@/components/books/randomiser/RandomiserFilters"
import { useEffect, useState } from "react"
import { useAppSelector } from "@/store/lib/hooks"
import { config } from "@/configs/config"
import { API_UNREAD_BOOKS, API_USERS } from "@/configs/config"
import { useGetQuery } from "@/hooks/fetch-hooks/useGetQuery"
import { Book } from "@/types/BookInterface"
import { User } from "@/types/UserInterface"

const RandomiserHomepage: React.FC = () => {
const [randomiserBooks, setRandomiserBooks] = useState([])

const isRefresh = useAppSelector((state) => state.editButtons.isRefresh)

const { bookData, loadingBooks, error } = useBookFetch(
`${config.API_URL}/books/unread/all`,
null,
)
const { userData, loadingUsers } = useUserFetch(
`${config.API_URL}/users`,
null,
)
const {
data: bookData,
isLoading,
isError,
error,
} = useGetQuery<Book[]>({
queryKey: ["unread books"],
apiPath: API_UNREAD_BOOKS,
})

const { data: userData, isLoading: isLoadingUsers } = useGetQuery<User[]>({
queryKey: ["users"],
apiPath: API_USERS,
})

useEffect(() => {
setRandomiserBooks(bookData)
}, [bookData, loadingBooks, isRefresh])
}, [bookData, isLoading, isRefresh])

return (
<div>
Expand Down Expand Up @@ -55,13 +62,14 @@ const RandomiserHomepage: React.FC = () => {
<RandomSectionLeft
bookData={randomiserBooks}
userData={userData}
loadingBooks={loadingBooks}
loadingUsers={loadingUsers}
loadingBooks={isLoading}
loadingUsers={isLoadingUsers}
/>
<RandomSectionRight
bookData={randomiserBooks}
userData={userData}
error={error}
isError={isError}
/>
</div>
)}
Expand Down
Loading
Loading