From ce33770e651944d20a13506594a337c4667fc839 Mon Sep 17 00:00:00 2001 From: ysandler Date: Wed, 30 Apr 2025 12:27:04 -0500 Subject: [PATCH] feat: init ui for user checked out books --- src/app/(frontend)/page.tsx | 32 +++- src/collections/Checkouts/Checkouts.ts | 16 ++ src/components/Manage/CheckedOutBooks.tsx | 174 ++++++++++++++++++++++ src/components/Manage/Manage.tsx | 10 +- src/components/SiteNavigation.tsx | 1 - 5 files changed, 228 insertions(+), 5 deletions(-) create mode 100644 src/components/Manage/CheckedOutBooks.tsx diff --git a/src/app/(frontend)/page.tsx b/src/app/(frontend)/page.tsx index 7faf30c..4189bc3 100644 --- a/src/app/(frontend)/page.tsx +++ b/src/app/(frontend)/page.tsx @@ -4,7 +4,7 @@ import config from '@/payload.config' import React from 'react' import UserFeed from '@/components/Feed/UserFeed' -import { Book, Repository } from '@/payload-types' +import { Book, Checkout, Repository } from '@/payload-types' import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' import { TextShimmer } from '@/components/ui/text-shimmer' import { LoginForm } from '@/components/login-form' @@ -59,6 +59,34 @@ export default async function HomePage() { }, })) as PaginatedDocs + let userCheckouts: PaginatedDocs | null = null + if (user?.id) + userCheckouts = await payload.find({ + collection: 'checkouts', + depth: 3, + limit: 10, + select: { + id: true, + copy: true, + dateDue: true, + }, + sort: 'dateDue', + where: { + and: [ + { + isReturned: { + not_equals: true, + }, + }, + { + 'user.id': { + equals: user.id, + }, + }, + ], + }, + }) + return (
@@ -136,7 +164,7 @@ export default async function HomePage() { - + ) : ( diff --git a/src/collections/Checkouts/Checkouts.ts b/src/collections/Checkouts/Checkouts.ts index 8e44fbd..1294d39 100644 --- a/src/collections/Checkouts/Checkouts.ts +++ b/src/collections/Checkouts/Checkouts.ts @@ -21,6 +21,22 @@ const Checkouts: CollectionConfig = { relationTo: 'copies', hasMany: false, }, + { + name: 'isReturned', + type: 'checkbox', + }, + { + name: 'dateDue', + type: 'date', + }, + { + name: 'ownerVerifiedReturnedDate', + type: 'date', + }, + { + name: 'loanerReturnedDate', + type: 'date', + }, { name: 'book', type: 'join', diff --git a/src/components/Manage/CheckedOutBooks.tsx b/src/components/Manage/CheckedOutBooks.tsx new file mode 100644 index 0000000..654d72e --- /dev/null +++ b/src/components/Manage/CheckedOutBooks.tsx @@ -0,0 +1,174 @@ +import { Author, Book, Checkout, Copy, Repository } from '@/payload-types' +import { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/react' +import { EllipsisVerticalIcon } from '@heroicons/react/20/solid' +import clsx from 'clsx' +import { Loader2Icon } from 'lucide-react' +import { PaginatedDocs, User } from 'payload' +import { useCallback, useState } from 'react' + +const statuses = { + 'Passed Due': 'text-gray-200 bg-red-500 ring-red-700/10', + 'Due Soon': 'text-amber-800 bg-amber-50 ring-amber-600/20', + '': '', +} + +type Row = { + id: number + title: string + authors: string[] + owners: string[] + repositoryAbbreviation: string + status: 'Passed Due' | 'Due Soon' | '' + dueDate: Date + href: string +} + +type ListProps = { rows: Row[] } +const CheckedOutBooksList = (props: ListProps) => { + const { rows } = props + + const [returningBookId, setReturningBookId] = useState(0) + + const isReturningBook = useCallback((id: number) => id === returningBookId, [returningBookId]) + + const handleReturnClick = useCallback( + (id: number) => { + setReturningBookId(id) + }, + [returningBookId], + ) + + return ( +
    + {rows.map((r) => ( +
  • +
    +
    +

    + {`[${r.repositoryAbbreviation}] `} + {r.title} +

    + {!!r.status && ( +

    + {r.status} +

    + )} +
    +
    +

    + Due on +

    + + + +

    Borrowed from {r.owners.join(', ')}

    +
    +
    +
    + + + + Open options + + + + + + + + Repository, {r.repositoryAbbreviation} + + + + + View {r.title} + + + + +
    +
  • + ))} +
+ ) +} + +type Props = { initialCheckoutPage: PaginatedDocs | null } +const CheckedOutBooks = (props: Props) => { + const { initialCheckoutPage } = props + + const checkouts = initialCheckoutPage?.docs + const rows: Row[] = + checkouts?.map((c) => { + const copy = c.copy as Copy + const book = copy.book as Book + const authors = book.authors as Author[] + const repository = copy.repository as Repository + const owners = repository.owner as (number | User)[] + + const parsedDateDue = c.dateDue ? new Date(c.dateDue) : new Date() + const milisecondsUntilDue = parsedDateDue.getTime() - new Date().getTime() + const secondsUntilDue = Math.floor(milisecondsUntilDue / 1000) + const minutesUntilDue = Math.floor(secondsUntilDue / 60) + const hoursUntilDue = Math.floor(minutesUntilDue / 60) + const daysUntilDue = Math.floor(hoursUntilDue / 24) + + let status = '' + if (daysUntilDue <= 0) status = 'Passed Due' + else if (daysUntilDue <= 5) status = 'Due Soon' + + return { + id: c.id || 0, + title: book.title || '', + authors: authors.map((a) => a.lf) || ([] as string[]), + owners: + owners.map((o) => `${(o as User).firstName} ${(o as User).lastName}`) || ([] as string[]), + repositoryAbbreviation: repository.abbreviation || '', + status, + dueDate: parsedDateDue, + href: `/books/${book.id}`, + } as Row + }) || [] + + if (!rows.length) return null + + return ( +
+
+

Inbound Checked Out Books

+
+ +
+ ) +} + +export default CheckedOutBooks diff --git a/src/components/Manage/Manage.tsx b/src/components/Manage/Manage.tsx index 4cffe5f..89f2761 100644 --- a/src/components/Manage/Manage.tsx +++ b/src/components/Manage/Manage.tsx @@ -1,15 +1,17 @@ 'use client' -import { Repository } from '@/payload-types' +import { Checkout, Repository } from '@/payload-types' import { PaginatedDocs } from 'payload' import RepoList from './RepoList' import HoldRequestNotifications from './HoldRequests' +import CheckedOutBooks from './CheckedOutBooks' type Props = { repos: PaginatedDocs | null + checkouts: PaginatedDocs | null } const Manage = (props: Props) => { - const { repos } = props + const { repos, checkouts } = props return (
@@ -20,6 +22,10 @@ const Manage = (props: Props) => {
+ +
+ +
) } diff --git a/src/components/SiteNavigation.tsx b/src/components/SiteNavigation.tsx index d6131f2..5aa0229 100644 --- a/src/components/SiteNavigation.tsx +++ b/src/components/SiteNavigation.tsx @@ -56,7 +56,6 @@ export default function SiteNavigation(props: { children: React.ReactNode }) { }).then(async (response) => { const userRequest = await response.json() setUser(userRequest.user) - console.log(userRequest.user) }) }, [user, setUser])