diff --git a/.gitignore b/.gitignore index 3121112..1a1e372 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,7 @@ yarn-error.log* # local env files .env*.local +.env.production .env # vercel diff --git a/public/Profile.unknown-profile-image.jpg b/public/Profile.unknown-profile-image.jpg new file mode 100644 index 0000000..b03b035 Binary files /dev/null and b/public/Profile.unknown-profile-image.jpg differ diff --git a/public/book-library/book-card-cert-fresh.webp b/public/book-library/book-card-cert-fresh.webp new file mode 100644 index 0000000..988ec0a Binary files /dev/null and b/public/book-library/book-card-cert-fresh.webp differ diff --git a/public/book-library/book-card-cert-rotten.webp b/public/book-library/book-card-cert-rotten.webp new file mode 100644 index 0000000..8bba12b Binary files /dev/null and b/public/book-library/book-card-cert-rotten.webp differ diff --git a/src/__tests__/unit/stat-functions/genreScoreFunction.test.tsx b/src/__tests__/unit/stat-functions/genreScoreFunction.test.tsx index da61360..cd37af7 100644 --- a/src/__tests__/unit/stat-functions/genreScoreFunction.test.tsx +++ b/src/__tests__/unit/stat-functions/genreScoreFunction.test.tsx @@ -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[] diff --git a/src/app/(home)/books/library/[id]/page.tsx b/src/app/(home)/books/library/[id]/page.tsx index fb89fe8..829a44c 100644 --- a/src/app/(home)/books/library/[id]/page.tsx +++ b/src/app/(home)/books/library/[id]/page.tsx @@ -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" @@ -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(true) @@ -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({ + queryKey: ["books", id], + apiPath: `${API_SINGLE_BOOK}${id}`, + }) const showDelete = useAppSelector((state) => state.editBookButtons.showDelete) return showDelete ? (

Book is deleted

- ) : error ? ( + ) : isError ? (

{error.message}

- ) : loadingBooks && !bookData ? ( + ) : isLoading && !bookData ? ( ) : (
-

+

{bookData?.title}

+
{showLeftNavArrows && ( { /> )}
-
+
{decodedToken?._id === adminId ? ( ) : ( @@ -78,7 +87,7 @@ const SingleBook: React.FC = () => { {" "} { const [searchBar, setSearchBar] = useState("") - const [books, setBooks] = useState([]) + const [books, setBooks] = useState([]) const { limit, handleLimit, setIsLimit, isLimit } = useLimit() - const { bookData, loadingBooks, error } = useBookFetch( - `${config.API_URL}/books`, - limit, - ) + const { + data: bookData, + isLoading, + isError, + error, + } = useGetQuery({ + 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) => @@ -39,18 +46,21 @@ 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 ( <> @@ -58,61 +68,46 @@ const Booklibrary: React.FC = () => {
-

+

Book Library

- {loadingBooks && books.length === 0 ? ( + {isLoading && books.length === 0 ? ( - ) : error ? ( + ) : isError ? (

{error?.message}

) : ( -
+
{books?.length > 0 ? ( books?.map((book) => (
- {book.reviewImageURL ? ( - + +
-

+

{book.title}

- - - ) : ( - -
-
-

- {book.title} -

-
-
- - - )} +
+ +
)) ) : ( diff --git a/src/app/(home)/books/randomiser/page.tsx b/src/app/(home)/books/randomiser/page.tsx index 6087898..7f195b9 100644 --- a/src/app/(home)/books/randomiser/page.tsx +++ b/src/app/(home)/books/randomiser/page.tsx @@ -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({ + queryKey: ["unread books"], + apiPath: API_UNREAD_BOOKS, + }) + + const { data: userData, isLoading: isLoadingUsers } = useGetQuery({ + queryKey: ["users"], + apiPath: API_USERS, + }) useEffect(() => { setRandomiserBooks(bookData) - }, [bookData, loadingBooks, isRefresh]) + }, [bookData, isLoading, isRefresh]) return (
@@ -55,13 +62,14 @@ const RandomiserHomepage: React.FC = () => {
)} diff --git a/src/app/(home)/books/stats/page.tsx b/src/app/(home)/books/stats/page.tsx index e61ef04..a6e0278 100644 --- a/src/app/(home)/books/stats/page.tsx +++ b/src/app/(home)/books/stats/page.tsx @@ -1,24 +1,28 @@ /* eslint-disable react/react-in-jsx-scope */ "use client" import { handleHideScores_NoSetter } from "@/utils/time-functions/hideScores" -import useBookFetch from "@/hooks/fetch-hooks/useReadBookFetch" import BookStatsTotalScores from "@/components/books/stats/BookStatsTotalScores" import BookStatsGenre from "@/components/books/stats/BookStatsGenre" import BookStatsPages from "@/components/books/stats/BookStatsPages" import BookStatsYrPublished from "@/components/books/stats/BookStatsYrPublished" import BookStatsMeetingDate from "@/components/books/stats/BookStatsMeetingDate" -import { config } from "@/configs/config" +import { API_BOOKS, config } from "@/configs/config" +import { useGetQuery } from "@/hooks/fetch-hooks/useGetQuery" +import { Book } from "@/types/BookInterface" const BookStats = () => { - const { bookData, loadingBooks } = useBookFetch( - `${config.API_URL}/books`, - null - ) + const { data: bookData, isLoading } = useGetQuery({ + queryKey: ["books"], + apiPath: API_BOOKS, + }) - const readBooks = bookData?.filter( - (book) => - book.read === true && !handleHideScores_NoSetter(book.actualDateOfMeeting) - ) + const readBooks = bookData?.length + ? bookData?.filter( + (book) => + book.read === true && + !handleHideScores_NoSetter(book.actualDateOfMeeting), + ) + : [] return (
@@ -30,24 +34,21 @@ const BookStats = () => {

Total Scores

- +

By Genre

- +

By Number of Pages

- +
@@ -56,7 +57,7 @@ const BookStats = () => {
@@ -65,10 +66,7 @@ const BookStats = () => {

By Meeting Date

- +
) diff --git a/src/app/(home)/brothers/library/[username]/page.tsx b/src/app/(home)/brothers/library/[username]/page.tsx index ba3cc5b..c1b75f8 100644 --- a/src/app/(home)/brothers/library/[username]/page.tsx +++ b/src/app/(home)/brothers/library/[username]/page.tsx @@ -3,28 +3,31 @@ import { useParams } from "next/navigation" import { useJwt } from "react-jwt" import CommentCon from "@/components/brothers/dashboard/BrotherCommentCon" -import useUserFetch from "@/hooks/fetch-hooks/useUserFetch" -import useBookFetch from "@/hooks/fetch-hooks/useReadBookFetch" import { useAppSelector } from "@/store/lib/hooks" import BrotherBanner from "@/components/brothers/dashboard/BrotherBanner" import BrotherBooksScored from "@/components/brothers/dashboard/BrotherBooksScored" import BrotherLoadingBanner from "@/components/brothers/dashboard/BrotherLoadingBanner" import BrotherLoadingBooksScored from "@/components/brothers/dashboard/BrotherLoadingBooksScored" import BrotherLoadingCommentCon from "@/components/brothers/dashboard/BrotherLoadingCommentCon" -import { config } from "@/configs/config" +import { API_BOOKS, API_USERS, config } from "@/configs/config" +import { useGetQuery } from "@/hooks/fetch-hooks/useGetQuery" +import { User } from "@/types/UserInterface" +import { Book } from "@/types/BookInterface" const Dashboard: React.FC = () => { - const { userData, loadingUsers } = useUserFetch( - `${config.API_URL}/users`, - null - ) + const { data: userData, isLoading: isLoadingUsers } = useGetQuery({ + queryKey: ["users"], + apiPath: API_USERS, + }) - const { bookData, loadingBooks } = useBookFetch( - `${config.API_URL}/books`, - null - ) + const { data: bookData, isLoading: isLoadingBooks } = useGetQuery({ + queryKey: ["books"], + apiPath: API_BOOKS, + }) - const readBooks = bookData?.filter((book) => book.read === true) + const readBooks = bookData?.length + ? bookData?.filter((book) => book.read === true) + : [] const token = useAppSelector((state) => state.token.tokenState) @@ -45,7 +48,7 @@ const Dashboard: React.FC = () => { return ( <> - {loadingBooks ? ( + {isLoadingBooks ? ( <>
@@ -71,8 +74,8 @@ const Dashboard: React.FC = () => {
diff --git a/src/app/(home)/brothers/library/page.tsx b/src/app/(home)/brothers/library/page.tsx index 4416191..389c463 100644 --- a/src/app/(home)/brothers/library/page.tsx +++ b/src/app/(home)/brothers/library/page.tsx @@ -2,30 +2,33 @@ import { useState } from "react" import Search from "@/components/misc/search/Search" -import useBookFetch from "@/hooks/fetch-hooks/useReadBookFetch" -import useUserFetch from "@/hooks/fetch-hooks/useUserFetch" import BrothersProfile from "@/components/brothers/library/BrothersProfile" import BrothersLoadingProfile from "@/components/brothers/library/BrothersLoadingProfile" -import { config } from "@/configs/config" +import { API_BOOKS, API_USERS, config } from "@/configs/config" +import { User } from "@/types/UserInterface" +import { useGetQuery } from "@/hooks/fetch-hooks/useGetQuery" +import { Book } from "@/types/BookInterface" const Brothercat: React.FC = () => { const [searchBar, setSearchBar] = useState("") - const { userData, loadingUsers } = useUserFetch( - `${config.API_URL}/users`, - null - ) + const { data: userData, isLoading: isLoadingUsers } = useGetQuery({ + queryKey: ["users"], + apiPath: API_USERS, + }) - const { bookData, loadingBooks } = useBookFetch( - `${config.API_URL}/books`, - null - ) + const { data: bookData, isLoading: isLoadingBooks } = useGetQuery({ + queryKey: ["books"], + apiPath: API_BOOKS, + }) - const readBooks = bookData?.filter((book) => book.read === true) + const readBooks = bookData?.length + ? bookData?.filter((book) => book.read === true) + : [] const filteredResults = Array.isArray(userData) ? userData?.filter((user) => - user?.username?.toLowerCase().includes(searchBar.toLowerCase()) + user?.username?.toLowerCase().includes(searchBar.toLowerCase()), ) : ["No results"] return ( @@ -34,10 +37,10 @@ const Brothercat: React.FC = () => {
- {loadingUsers && loadingBooks ? ( + {isLoadingUsers && isLoadingBooks ? ( <>

Brothers Library diff --git a/src/app/(home)/brothers/page.tsx b/src/app/(home)/brothers/page.tsx index 92645b9..79376c2 100644 --- a/src/app/(home)/brothers/page.tsx +++ b/src/app/(home)/brothers/page.tsx @@ -1,7 +1,5 @@ /* eslint-disable react/react-in-jsx-scope */ import { UiButton } from "@/components/ui/button/UiButton" -import { Button } from "antd" -import Link from "next/link" const BrothersHomepage: React.FC = () => { const dashboardLinks = [ @@ -29,17 +27,6 @@ const BrothersHomepage: React.FC = () => { key={btn.text} /> ))} - {/* - - - - - - */}
{ - const { userData, loadingUsers } = useUserFetch( - `${config.API_URL}/users`, - null - ) + const { data: userData, isLoading: isLoadingUsers } = useGetQuery({ + queryKey: ["users"], + apiPath: API_USERS, + }) - const { bookData, loadingBooks } = useBookFetch( - `${config.API_URL}/books`, - null - ) - const isDarkMode = useAppSelector((state) => state.darkMode.darkMode) - const handleDesktop = useMediaQuery({ query: "(min-device-width: 601px)" }) - const readBooks = bookData?.filter((book) => book.read === true) + const { data: bookData, isLoading: isLoadingBooks } = useGetQuery({ + queryKey: ["books"], + apiPath: API_BOOKS, + }) + const readBooks = bookData?.length + ? bookData?.filter((book) => book.read === true) + : [] let newArr = new Array() newArr.length = 5 @@ -106,8 +105,8 @@ max-[600px]:grid-cols-[repeat(auto-fill,minmax(150px,1fr))]" Average Scores

diff --git a/src/app/(home)/layout.tsx b/src/app/(home)/layout.tsx index 07952fb..85b3003 100644 --- a/src/app/(home)/layout.tsx +++ b/src/app/(home)/layout.tsx @@ -8,6 +8,7 @@ import StoreProvider from "@/store/StoreProvider" import HeaderCon from "@/components/header/HeaderCon" import { ConfigProvider } from "antd" import theme from "@/theme/theme.config" +import { NotificationProvider } from "@/context/NotificationProvider" export const metadata: Metadata = { title: "Book Club Brothers", @@ -30,9 +31,11 @@ export default function RootLayout({ - - {children} -