From 9db57364878e2497d3cd6dcfd86916fa8f8be00c Mon Sep 17 00:00:00 2001 From: ysandler Date: Thu, 1 May 2025 14:53:57 -0500 Subject: [PATCH] feat: conditional UX around if logged in and hold request exists on book page --- .../(frontend)/books/[bookId]/page.client.tsx | 117 ++++++++++-------- src/app/(frontend)/books/[bookId]/page.tsx | 83 +++++++++---- src/app/(frontend)/login/page.tsx | 4 +- src/collections/Checkouts/HoldRequests.ts | 4 + src/components/SiteNavigation.tsx | 39 +++--- src/payload-types.ts | 2 + 6 files changed, 156 insertions(+), 93 deletions(-) diff --git a/src/app/(frontend)/books/[bookId]/page.client.tsx b/src/app/(frontend)/books/[bookId]/page.client.tsx index 45727d0..ee143d9 100644 --- a/src/app/(frontend)/books/[bookId]/page.client.tsx +++ b/src/app/(frontend)/books/[bookId]/page.client.tsx @@ -1,6 +1,6 @@ 'use client' -import { Book, Genre, Repository } from '@/payload-types' +import { Book, Genre, HoldRequest, Repository } from '@/payload-types' import { Combobox, ComboboxLabel, ComboboxOption, ComboboxDescription } from '@/components/combobox' import { Field, Label } from '@/components/fieldset' @@ -12,11 +12,13 @@ import { Loader2 } from 'lucide-react' import { RichText } from '@/components/RichText' import { useMemo, useState } from 'react' import requestHold from '../../serverCalls/requestHold' +import { useGlobal } from '@/providers/GlobalProvider' type DropDownProps = { currentRepository: Repository repositories: Repository[] isDisabled: boolean + doesHoldExist: boolean isRequesting: boolean onClickRequest: () => void onChange: (repo: Repository | null) => void @@ -25,38 +27,48 @@ function RepoDropdown({ currentRepository, repositories, isRequesting, + doesHoldExist, isDisabled, onClickRequest, onChange, }: DropDownProps) { return ( - -
- repo?.name} - defaultValue={currentRepository} - className="" - onChange={(repo) => onChange(repo)} - > - {(repo) => ( - - {repo.abbreviation} - {repo.name} - - )} - - -
+ {doesHoldExist ? ( + + You have requested a hold for this book + + ) : ( + <> + +
+ repo?.name} + defaultValue={currentRepository} + className="" + onChange={(repo) => onChange(repo)} + > + {(repo) => ( + + {repo.abbreviation} + {repo.name} + + )} + + +
+ + )}
) } @@ -64,39 +76,41 @@ function RepoDropdown({ type Props = { book: Book repositories: PaginatedDocs + existingHolds: PaginatedDocs | null } export default function BookByIdPageClient(props: Props) { - const { book, repositories } = props - + const { book, repositories, existingHolds } = props const repos = repositories.docs + const { user } = useGlobal() + const [isRequestingCopy, setIsRequestingCopy] = useState(false) const [selectedRepository, setSelectedRepository] = useState( repos.length ? repos[0] : null, ) + const [doesHoldExist, setDoesHoldExist] = useState(!!existingHolds?.totalDocs) + const isRequestDisabled = useMemo(() => { return isRequestingCopy }, [isRequestingCopy]) const onClickRequest = async () => { - if (isRequestingCopy || !selectedRepository || !book) return + if (isRequestingCopy || !selectedRepository || !book || doesHoldExist) return setIsRequestingCopy(true) - const response = await requestHold({ + const newHoldResponse = await requestHold({ repositoryId: selectedRepository.id, bookId: book.id, }) - console.log(response) - + if (newHoldResponse?.id) setDoesHoldExist(true) setIsRequestingCopy(false) } return (
- {/* Book */}
{/* Book Cover */}
@@ -113,24 +127,22 @@ export default function BookByIdPageClient(props: Props) { {/* Book details */}
-
-
-

- {book.title} -

+
+

+ {book.title} +

-

- Book information -

-

- {book.publication && ( - - Published:  - - - )} -

-
+

+ Book information +

+

+ {book.publication && ( + + Published:  + + + )} +

{book.summary}

@@ -139,7 +151,8 @@ export default function BookByIdPageClient(props: Props) { currentRepository={repos[0]} repositories={repos} isRequesting={isRequestingCopy} - isDisabled={isRequestDisabled} + doesHoldExist={doesHoldExist} + isDisabled={isRequestDisabled || !user} onClickRequest={onClickRequest} onChange={(repo) => setSelectedRepository(repo)} /> diff --git a/src/app/(frontend)/books/[bookId]/page.tsx b/src/app/(frontend)/books/[bookId]/page.tsx index 795cc44..627ab79 100644 --- a/src/app/(frontend)/books/[bookId]/page.tsx +++ b/src/app/(frontend)/books/[bookId]/page.tsx @@ -1,6 +1,7 @@ -import { Book, Copy, Repository } from '@/payload-types' +import { Book, Copy, HoldRequest, Repository } from '@/payload-types' import { getPayload, PaginatedDocs } from 'payload' import configPromise from '@payload-config' +import { headers as getHeaders } from 'next/headers.js' import BookByIdPageClient from './page.client' import { PageBreadCrumb, Route } from '@/components/PageBreadCrumb' @@ -31,7 +32,8 @@ const BookByIdPage = async (props: Props) => { const payload = await getPayload({ config: configPromise }) - const defaultRepositoryLimit = 10 + const headers = await getHeaders() + const { user } = await payload.auth({ headers }) const foundBook = (await payload.findByID({ collection: 'books', @@ -51,16 +53,45 @@ const BookByIdPage = async (props: Props) => { }, })) as Book - const repositoryIds = foundBook.copies?.docs?.map((c) => (c as Copy).repository) - - const orQueries = repositoryIds?.map((c) => { - return { - id: { - equals: c, + let existingHolds: PaginatedDocs | null = null + if (user?.id) + existingHolds = (await payload.find({ + collection: 'holdRequests', + depth: 1, + limit: 1, + select: { + dateRequested: true, + holdingUntilDate: true, }, - } - }) + where: { + and: [ + { + 'book.id': { + equals: bookId, + }, + }, + { + userRequested: { + equals: user.id, + }, + }, + { + isCheckedOut: { + not_equals: true, + }, + }, + { + isRejected: { + not_equals: true, + }, + }, + ], + }, + })) as PaginatedDocs + const defaultRepositoryLimit = 10 + const repositoryIds = foundBook.copies?.docs?.map((c) => (c as Copy).repository) + const repositoryOrQueries = repositoryIds?.map((c) => ({ id: { equals: c } })) const repositories = (await payload.find({ collection: 'repositories', depth: 3, @@ -72,28 +103,38 @@ const BookByIdPage = async (props: Props) => { name: true, }, where: { - or: orQueries?.length ? orQueries : [], + or: repositoryOrQueries?.length ? repositoryOrQueries : [], }, })) as PaginatedDocs - const createBreadcrumbRoutes =() => { + const createBreadcrumbRoutes = () => { + const maxTitleLength = 18 + let title if (!foundBook?.title) title = 'Book Not Found' - else title = foundBook.title.length > 25 ? `${foundBook.title.slice(0, 25)}...` : foundBook.title - + else + title = + foundBook.title.length > maxTitleLength + ? `${foundBook.title.slice(0, maxTitleLength)}...` + : foundBook.title - return [...staticBreadcrumRoutes, { - label: title, - href: `/books/${bookId}` - }] + return [ + ...staticBreadcrumRoutes, + { + label: title, + href: `/books/${bookId}`, + }, + ] } - console.log(createBreadcrumbRoutes()) - return ( <> - + ) } diff --git a/src/app/(frontend)/login/page.tsx b/src/app/(frontend)/login/page.tsx index 7105ba4..b9d3340 100644 --- a/src/app/(frontend)/login/page.tsx +++ b/src/app/(frontend)/login/page.tsx @@ -12,7 +12,7 @@ const LoginPage = async () => { if (Boolean(userResult.user)) redirect('/profile') return ( -
+
Developed with 💜 - Beitzah.tech + by Beitzah.tech
diff --git a/src/collections/Checkouts/HoldRequests.ts b/src/collections/Checkouts/HoldRequests.ts index df553dd..cb6a0c7 100644 --- a/src/collections/Checkouts/HoldRequests.ts +++ b/src/collections/Checkouts/HoldRequests.ts @@ -49,6 +49,10 @@ const HoldRequests: CollectionConfig = { name: 'holdingUntilDate', type: 'date', }, + { + name: 'isRejected', + type: 'checkbox', + }, { name: 'isCheckedOut', type: 'checkbox', diff --git a/src/components/SiteNavigation.tsx b/src/components/SiteNavigation.tsx index e67c0d6..99728ae 100644 --- a/src/components/SiteNavigation.tsx +++ b/src/components/SiteNavigation.tsx @@ -105,28 +105,31 @@ export default function SiteNavigation(props: { children: React.ReactNode }) { - - - My profile - - {/* - - Settings - - - - - Privacy policy - */} + {!user ? ( + + + Login + + ) : ( + + + My profile + + )} Share feedback - - - - Sign out - + + {!!user && ( + <> + + + + Sign out + + + )} diff --git a/src/payload-types.ts b/src/payload-types.ts index 91423df..ef3c2a1 100644 --- a/src/payload-types.ts +++ b/src/payload-types.ts @@ -234,6 +234,7 @@ export interface HoldRequest { dateRequested?: string | null; isHolding?: boolean | null; holdingUntilDate?: string | null; + isRejected?: boolean | null; isCheckedOut?: boolean | null; updatedAt: string; createdAt: string; @@ -583,6 +584,7 @@ export interface HoldRequestsSelect { dateRequested?: T; isHolding?: T; holdingUntilDate?: T; + isRejected?: T; isCheckedOut?: T; updatedAt?: T; createdAt?: T;