diff --git a/next.config.js b/next.config.js
index aadb4d1..881082a 100644
--- a/next.config.js
+++ b/next.config.js
@@ -3,11 +3,17 @@ module.exports = {
images: {
remotePatterns: [
{
- protocol: 'https',
- hostname: 'res.cloudinary.com',
- port: '',
+ protocol: "https",
+ hostname: "res.cloudinary.com",
+ port: "",
// pathname: 'dhqqzaaid/image/upload/v1706643301/**',
},
+ {
+ protocol: "https",
+ hostname: "covers.openlibrary.org",
+ port: "",
+ pathname: "/b/**", // Matches the book cover path structure
+ },
],
- }
-};
\ No newline at end of file
+ },
+}
diff --git a/src/app/(home)/books/library/page.tsx b/src/app/(home)/books/library/page.tsx
index 20aa0e4..f8995f9 100644
--- a/src/app/(home)/books/library/page.tsx
+++ b/src/app/(home)/books/library/page.tsx
@@ -25,7 +25,7 @@ const Booklibrary: React.FC = () => {
const { bookData, loadingBooks, error } = useBookFetch(
`${config.API_URL}/books`,
- limit
+ limit,
)
const readBooks = bookData?.filter((book) => book.read === true)
@@ -34,7 +34,7 @@ const Booklibrary: React.FC = () => {
const filteredResults = Array.isArray(readBooks)
? readBooks?.filter((book) =>
- book.title.toLowerCase().includes(searchBar.toLowerCase())
+ book.title.toLowerCase().includes(searchBar.toLowerCase()),
)
: ["No results"]
@@ -100,8 +100,9 @@ const Booklibrary: React.FC = () => {
totalScore={book?.totalScore}
ratingArr={book?.scoreRatings?.rating}
raterArr={book?.scoreRatings?.raterId}
+ imageURL={book?.imageURL}
hideScores={handleHideScores_NoSetter(
- book?.actualDateOfMeeting
+ book?.actualDateOfMeeting,
)}
/>
diff --git a/src/app/(home)/books/randomiser/page.tsx b/src/app/(home)/books/randomiser/page.tsx
index 3dd41f2..6087898 100644
--- a/src/app/(home)/books/randomiser/page.tsx
+++ b/src/app/(home)/books/randomiser/page.tsx
@@ -17,16 +17,16 @@ const RandomiserHomepage: React.FC = () => {
const { bookData, loadingBooks, error } = useBookFetch(
`${config.API_URL}/books/unread/all`,
- null
+ null,
)
const { userData, loadingUsers } = useUserFetch(
`${config.API_URL}/users`,
- null
+ null,
)
useEffect(() => {
setRandomiserBooks(bookData)
- }, [loadingBooks, isRefresh])
+ }, [bookData, loadingBooks, isRefresh])
return (
diff --git a/src/components/books/library/BookCover.tsx b/src/components/books/library/BookCover.tsx
index f8697e2..b8bea65 100644
--- a/src/components/books/library/BookCover.tsx
+++ b/src/components/books/library/BookCover.tsx
@@ -4,6 +4,7 @@ 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"
type Props = {
title: string
@@ -12,6 +13,7 @@ type Props = {
raterArr: string[]
hideScores: boolean
isSingleBook?: boolean
+ imageURL: string
}
const BookCover: React.FC
= ({
@@ -21,6 +23,7 @@ const BookCover: React.FC = ({
raterArr,
hideScores,
isSingleBook,
+ imageURL,
}) => {
const token = useAppSelector((state) => state.token.tokenState)
const { decodedToken }: { decodedToken?: { username: string; _id: string } } =
@@ -29,7 +32,7 @@ const BookCover: React.FC = ({
const { userData, loadingUsers, error } = useUserFetch(
`${config.API_URL}/users`,
- null
+ null,
)
const findUser = (id) => {
@@ -51,7 +54,6 @@ const BookCover: React.FC = ({
}
}
findBookScore()
-
return (
<>
= ({
}
>
-
-
{title}
-
- (Image pending)
-
-
+ {imageURL.length ? (
+
+
+
+ ) : (
+
+
{title}
+
+ (Image pending)
+
+
+ )}
Book Club Brothers
diff --git a/src/components/books/library/single-book/AdminViewLeftSide.tsx b/src/components/books/library/single-book/AdminViewLeftSide.tsx
index f05df25..3e9af31 100644
--- a/src/components/books/library/single-book/AdminViewLeftSide.tsx
+++ b/src/components/books/library/single-book/AdminViewLeftSide.tsx
@@ -1,14 +1,13 @@
import DeleteBook from "@/components/forms/bookform-delete/DeleteBook"
import EditTitleButton from "@/components/forms/editbookform-single-book/title/EditTitleButton"
import { Book } from "@/types/BookInterface"
-import React, { useState } from "react"
+import React from "react"
import BookCover from "../BookCover"
import { handleHideScores_NoSetter } from "@/utils/time-functions/hideScores"
import EditImageButton from "@/components/forms/editbookform-single-book/image/EditImageButton"
import EditTitle from "@/components/forms/editbookform-single-book/title/EditTitle"
import EditImage from "@/components/forms/editbookform-single-book/image/EditImage"
import { useAppSelector } from "@/store/lib/hooks"
-import NavigateBook from "./NavigateBook"
import Profile from "@/components/misc/profile/Profile"
import useSingleUserFetch from "@/hooks/fetch-hooks/useSingleUserFetch"
import Image from "next/image"
@@ -21,11 +20,11 @@ type Props = {
const AdminViewSingleBook: React.FC
= ({ bookData, bookId }) => {
const { showTitle, showBookImage } = useAppSelector(
- (state) => state.editBookButtons
+ (state) => state.editBookButtons,
)
const { singleUserData } = useSingleUserFetch(
`${config.API_URL}/users/id/${bookData?.suggestedBy}`,
- bookData?.suggestedBy
+ bookData?.suggestedBy,
)
return (
<>
@@ -63,8 +62,9 @@ const AdminViewSingleBook: React.FC = ({ bookData, bookId }) => {
totalScore={bookData?.totalScore}
ratingArr={bookData?.scoreRatings?.rating}
raterArr={bookData?.scoreRatings?.raterId}
+ imageURL={bookData?.imageURL}
hideScores={handleHideScores_NoSetter(
- bookData?.actualDateOfMeeting
+ bookData?.actualDateOfMeeting,
)}
isSingleBook={true}
/>
diff --git a/src/components/books/library/single-book/UserViewLeftSide.tsx b/src/components/books/library/single-book/UserViewLeftSide.tsx
index 77b4e96..7e3430d 100644
--- a/src/components/books/library/single-book/UserViewLeftSide.tsx
+++ b/src/components/books/library/single-book/UserViewLeftSide.tsx
@@ -18,7 +18,7 @@ type Props = {
const UserViewLeftSide = ({ bookData }: Props) => {
const { singleUserData, loadingUser } = useSingleUserFetch(
`${config.API_URL}/users/id/${bookData?.suggestedBy}`,
- bookData?.suggestedBy
+ bookData?.suggestedBy,
)
const isDarkMode = useAppSelector((state) => state.darkMode.darkMode)
@@ -50,8 +50,9 @@ const UserViewLeftSide = ({ bookData }: Props) => {
totalScore={bookData?.totalScore}
ratingArr={bookData?.scoreRatings?.rating}
raterArr={bookData?.scoreRatings?.raterId}
+ imageURL={bookData?.imageURL}
hideScores={handleHideScores_NoSetter(
- bookData?.actualDateOfMeeting
+ bookData?.actualDateOfMeeting,
)}
isSingleBook={true}
/>
diff --git a/src/components/books/randomiser/RandomSectionLeft.tsx b/src/components/books/randomiser/RandomSectionLeft.tsx
index 6f1c1f6..3a1afa8 100644
--- a/src/components/books/randomiser/RandomSectionLeft.tsx
+++ b/src/components/books/randomiser/RandomSectionLeft.tsx
@@ -23,7 +23,6 @@ const RandomSectionLeft: React.FC = ({
}) => {
const { decodedToken } = useAuth()
const dispatch = useAppDispatch()
- const isDarkMode = useAppSelector((state) => state.darkMode.darkMode)
const findUser = (id) => {
const user = userData?.find((user) => user._id === id)
diff --git a/src/components/books/randomiser/RandomSectionRight.tsx b/src/components/books/randomiser/RandomSectionRight.tsx
index df1835d..6c72101 100644
--- a/src/components/books/randomiser/RandomSectionRight.tsx
+++ b/src/components/books/randomiser/RandomSectionRight.tsx
@@ -6,7 +6,7 @@ import { Book } from "@/types/BookInterface"
import { useAppSelector } from "@/store/lib/hooks"
import { User } from "@/types/UserInterface"
import { useAuth } from "@/hooks/auth-hooks/useAuth"
-import { useMediaQuery } from "react-responsive"
+import Image from "next/image"
import { UiSkeletonTitle } from "@/components/ui/skeleton/UiSkeletonTitle"
type Props = {
@@ -27,17 +27,17 @@ const RandomSectionRight: React.FC = ({ bookData, error, userData }) => {
}
return (
-
+
+ {bookData && bookData[index] ? (
+
+ ) : null}
{!bookData ? (
<>
@@ -63,7 +63,7 @@ const RandomSectionRight: React.FC
= ({ bookData, error, userData }) => {
>
) : (
-
+
{error ? (
{error.message}
) : !bookData[index] ? (
diff --git a/src/components/brothers/dashboard/BrotherBanner.tsx b/src/components/brothers/dashboard/BrotherBanner.tsx
index ca36ff5..08cb19e 100644
--- a/src/components/brothers/dashboard/BrotherBanner.tsx
+++ b/src/components/brothers/dashboard/BrotherBanner.tsx
@@ -85,8 +85,9 @@ const BrotherBanner: React.FC
= ({ user, readBooks }) => {
totalScore={findMinBook?.totalScore}
ratingArr={findMinBook?.scoreRatings?.rating}
raterArr={findMinBook?.scoreRatings?.raterId}
+ imageURL={findMinBook?.imageURL}
hideScores={handleHideScores_NoSetter(
- findMinBook?.actualDateOfMeeting
+ findMinBook?.actualDateOfMeeting,
)}
/>
)}
@@ -106,8 +107,9 @@ const BrotherBanner: React.FC = ({ user, readBooks }) => {
totalScore={findMaxBook?.totalScore}
ratingArr={findMaxBook?.scoreRatings?.rating}
raterArr={findMaxBook?.scoreRatings?.raterId}
+ imageURL={findMaxBook?.imageURL}
hideScores={handleHideScores_NoSetter(
- findMaxBook?.actualDateOfMeeting
+ findMaxBook?.actualDateOfMeeting,
)}
/>
)}
diff --git a/src/components/forms/bookform-randomise/CreateUnreadBookForm.tsx b/src/components/forms/bookform-randomise/CreateUnreadBookForm.tsx
index 5fb27d3..8577e41 100644
--- a/src/components/forms/bookform-randomise/CreateUnreadBookForm.tsx
+++ b/src/components/forms/bookform-randomise/CreateUnreadBookForm.tsx
@@ -1,14 +1,13 @@
-import { Button, Form, Input, Space, Select } from "antd"
+import { Form, Input, Space, Select } from "antd"
import useForm from "@/hooks/crud-hooks/useForm"
import { useAppDispatch, useAppSelector } from "@/store/lib/hooks"
import { setFormData } from "@/store/lib/features/books/bookFormDataSlice"
import { useEffect, useState } from "react"
-import { editBookButtonSlice } from "@/store/lib/features/books/editBookButtonsSlice"
import { setShowCreate } from "@/store/lib/features/auth/editButtonsSlice"
import { config } from "@/configs/config"
-import { UiInput } from "@/components/ui/input/UiInput"
import { InputConfigWrapper } from "../InputConfigWrapper"
import { UiButton } from "@/components/ui/button/UiButton"
+import useBookImage from "@/hooks/book-hooks/useBookImage"
const { Option } = Select
@@ -18,21 +17,28 @@ const CreateBook: React.FC = () => {
author: false,
yearPublished: false,
pages: false,
- imageURL: false,
})
- const [noImageMessage, setNoImageMessage] = useState()
const { handleSubmit, error, enterLoading, loadings, setError } = useForm(
`${config.API_URL}/books/unread/create`,
- "POST"
+ "POST",
)
const formData = useAppSelector((state) => state.bookFormData.formData)
const dispatch = useAppDispatch()
+ const fetchCoverId = useBookImage()
const handleLoading = () => {
enterLoading()
setTimeout(() => dispatch(setShowCreate()), 1250)
}
+ const handleSubmitSuggestion = async () => {
+ if (!formData.title) return
+ const coverUrl = await fetchCoverId(formData.title)
+ if (!coverUrl) return
+ const finalSubmissionData = { ...formData, imageURL: coverUrl }
+ dispatch(setFormData(finalSubmissionData))
+ }
+
useEffect(() => {
if (Object.values(errorObject).some((value) => value === false)) {
setError("Please check all required fields are correct")
@@ -82,6 +88,7 @@ const CreateBook: React.FC = () => {
>
handleSubmitSuggestion()}
onChange={(e) =>
dispatch(setFormData({ ...formData, title: e.target.value }))
}
@@ -146,7 +153,7 @@ const CreateBook: React.FC = () => {
type="number"
onChange={(e) =>
dispatch(
- setFormData({ ...formData, pages: Number(e.target.value) })
+ setFormData({ ...formData, pages: Number(e.target.value) }),
)
}
value={formData["pages"]}
@@ -180,7 +187,7 @@ const CreateBook: React.FC = () => {
setFormData({
...formData,
yearPublished: Number(e.target.value),
- })
+ }),
)
}
value={formData["yearPublished"]}
@@ -318,56 +325,6 @@ const CreateBook: React.FC = () => {
-
- {/* ImageURL */}
-
- No image URL?? Let me help with that. Click{" "}
-
- here
- {" "}
- you lazy bastard, find one you like, right-click and
- copy the image URL and paste it in the above field
- >
- )
- return Promise.reject()
- }
- if (imageRegex.test(value)) {
- setErrorObject({ ...errorObject, imageURL: true })
- setNoImageMessage("")
- return Promise.resolve()
- } else if (value && !imageRegex.test(value)) {
- setErrorObject({ ...errorObject, imageURL: false })
- return Promise.reject()
- }
- },
- message:
- "URLs must end in either .jpg, .jpeg, .png, .svg, or .webp",
- },
- ]}
- >
-
- dispatch(setFormData({ ...formData, imageURL: e.target.value }))
- }
- value={formData["imageURL"]}
- />
-
{/* Submission */}
@@ -391,11 +348,6 @@ const CreateBook: React.FC = () => {
{error}
) : null}
- {noImageMessage ? (
-
- {noImageMessage}
-
- ) : null}
>
diff --git a/src/configs/config.ts b/src/configs/config.ts
index 41146bc..a5064da 100644
--- a/src/configs/config.ts
+++ b/src/configs/config.ts
@@ -1,3 +1,5 @@
export const config = {
API_URL: process.env.NEXT_PUBLIC_API_URL,
+ IA_API_URL: "https://openlibrary.org/search.json?title=",
+ OL_BOOK_COVER_URL: "https://covers.openlibrary.org/b/id/",
}
diff --git a/src/hooks/book-hooks/useBookImage.ts b/src/hooks/book-hooks/useBookImage.ts
new file mode 100644
index 0000000..d69e255
--- /dev/null
+++ b/src/hooks/book-hooks/useBookImage.ts
@@ -0,0 +1,21 @@
+import { config } from "@/configs/config"
+
+const useBookImage = () => {
+ const fetchCoverId = async (title: string) => {
+ try {
+ const response = await fetch(`${config.IA_API_URL}${title}`)
+ if (!response.ok) return
+ const data = await response.json()
+ const filteredData = data?.docs.filter(
+ (book) => book.cover_i !== undefined,
+ )
+ const bookCoverUrl = `${config.OL_BOOK_COVER_URL}${filteredData[0].cover_i}-L.jpg`
+ return bookCoverUrl
+ } catch (err) {
+ console.error(err)
+ }
+ }
+ return fetchCoverId
+}
+
+export default useBookImage