feat: reject inbound hold requests
This commit is contained in:
parent
7a52261b1e
commit
bce3a7c23e
@ -86,8 +86,6 @@ export default function BookByIdPageClient(props: Props) {
|
|||||||
|
|
||||||
const { user } = useGlobal()
|
const { user } = useGlobal()
|
||||||
|
|
||||||
console.log(user)
|
|
||||||
|
|
||||||
const [isRequestingCopy, setIsRequestingCopy] = useState(false)
|
const [isRequestingCopy, setIsRequestingCopy] = useState(false)
|
||||||
const [selectedRepository, setSelectedRepository] = useState<Repository | null>(
|
const [selectedRepository, setSelectedRepository] = useState<Repository | null>(
|
||||||
repos.length ? repos[0] : null,
|
repos.length ? repos[0] : null,
|
||||||
|
|||||||
@ -90,7 +90,7 @@ export default async function HomePage() {
|
|||||||
return (
|
return (
|
||||||
<div className="home">
|
<div className="home">
|
||||||
<HomeHero user={user} />
|
<HomeHero user={user} />
|
||||||
<div id="homeContent" className='flex flex-col justify-around min-h-[90vh]'>
|
<div id="homeContent" className="flex flex-col justify-around min-h-[90vh]">
|
||||||
{user ? (
|
{user ? (
|
||||||
<Tabs id="tabs" defaultValue="feed" className="p-4">
|
<Tabs id="tabs" defaultValue="feed" className="p-4">
|
||||||
<TabsList className="grid w-full grid-cols-3">
|
<TabsList className="grid w-full grid-cols-3">
|
||||||
@ -108,7 +108,7 @@ export default async function HomePage() {
|
|||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
<TabsContent value="manage">
|
<TabsContent value="manage">
|
||||||
<Manage repos={userRepos} checkouts={userCheckouts} />
|
<Manage repos={userRepos} user={user} checkouts={userCheckouts} />
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@ -42,6 +42,9 @@ const UserFeed = async (props: Props) => {
|
|||||||
isCheckedOut: {
|
isCheckedOut: {
|
||||||
not_equals: true,
|
not_equals: true,
|
||||||
},
|
},
|
||||||
|
isRejected: {
|
||||||
|
not_equals: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
})) as PaginatedDocs<HoldRequest>
|
})) as PaginatedDocs<HoldRequest>
|
||||||
|
|
||||||
|
|||||||
@ -3,23 +3,50 @@ import { Author, Book, HoldRequest, Repository, User } from '@/payload-types'
|
|||||||
import { Button } from '../ui/button'
|
import { Button } from '../ui/button'
|
||||||
import Image from 'next/image'
|
import Image from 'next/image'
|
||||||
import ApproveHoldRequestModal from './ApproveHoldRequestModal'
|
import ApproveHoldRequestModal from './ApproveHoldRequestModal'
|
||||||
import { useState } from 'react'
|
import { useCallback, useState } from 'react'
|
||||||
import CheckoutFromHoldModal from './CheckoutFromHoldModal'
|
import CheckoutFromHoldModal from './CheckoutFromHoldModal'
|
||||||
|
import rejectHoldRequest from '@/serverActions/RejectHoldRequests'
|
||||||
|
import { getUserRepos } from '@/serverActions/GetUserRepos'
|
||||||
|
import { Loader2 } from 'lucide-react'
|
||||||
|
import { toast } from 'sonner'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
repos: PaginatedDocs<Repository> | null
|
repos: PaginatedDocs<Repository> | null
|
||||||
|
user: User
|
||||||
}
|
}
|
||||||
const HoldRequestNotifications = (props: Props) => {
|
const HoldRequestNotifications = (props: Props) => {
|
||||||
const { repos } = props
|
const [repos, setRepos] = useState<PaginatedDocs<Repository> | null>(props.repos)
|
||||||
|
|
||||||
const [openedModalId, setOpenedModalId] = useState<number | null>(null)
|
const [openedModalId, setOpenedModalId] = useState<number | null>(null)
|
||||||
|
|
||||||
const totalHoldNotifications = repos?.docs.flatMap((r) => r.holdRequests?.docs).length || 0
|
const totalHoldNotifications = repos?.docs.flatMap((r) => r.holdRequests?.docs).length || 0
|
||||||
|
|
||||||
|
const [isRejectingId, setIsRejectingId] = useState<number | null>()
|
||||||
|
|
||||||
|
const handleRejectHoldClick = useCallback(
|
||||||
|
async (holdRequestId: number) => {
|
||||||
|
if (isRejectingId) return
|
||||||
|
setIsRejectingId(holdRequestId)
|
||||||
|
const rejectRequest = await rejectHoldRequest({ holdRequestId })
|
||||||
|
|
||||||
|
if (rejectRequest?.isRejected) {
|
||||||
|
const updatedRepos = await getUserRepos({ userId: props.user.id })
|
||||||
|
setRepos(updatedRepos)
|
||||||
|
toast('Request was rejected')
|
||||||
|
} else {
|
||||||
|
toast('Error rejecting request')
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsRejectingId(null)
|
||||||
|
},
|
||||||
|
[isRejectingId, setIsRejectingId, props.user.id],
|
||||||
|
)
|
||||||
|
|
||||||
const holdRequestsByRepoElements = repos?.docs.map((r) => {
|
const holdRequestsByRepoElements = repos?.docs.map((r) => {
|
||||||
return (
|
return (
|
||||||
<ul key={r.id} role="list" className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
|
<ul key={r.id} role="list" className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
|
||||||
{r.holdRequests?.docs?.map((h) => {
|
{r.holdRequests?.docs
|
||||||
|
?.map((h) => {
|
||||||
const hold = h as HoldRequest
|
const hold = h as HoldRequest
|
||||||
const book = hold.book as Book
|
const book = hold.book as Book
|
||||||
const authors = book.authors as Author[]
|
const authors = book.authors as Author[]
|
||||||
@ -30,6 +57,8 @@ const HoldRequestNotifications = (props: Props) => {
|
|||||||
const userRequested = hold.userRequested as User
|
const userRequested = hold.userRequested as User
|
||||||
const userName = `${userRequested.firstName} ${userRequested.lastName}`
|
const userName = `${userRequested.firstName} ${userRequested.lastName}`
|
||||||
|
|
||||||
|
if (hold.isRejected) return null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<li key={hold.id} className="col-span-1 rounded-lg shadow-sm border border-accent">
|
<li key={hold.id} className="col-span-1 rounded-lg shadow-sm border border-accent">
|
||||||
<div className="flex w-full items-center justify-between space-x-6 p-6">
|
<div className="flex w-full items-center justify-between space-x-6 p-6">
|
||||||
@ -68,12 +97,32 @@ const HoldRequestNotifications = (props: Props) => {
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div className="flex gap-2 justify-around">
|
<div className="flex gap-2 justify-around">
|
||||||
<Button className="inline-flex flex-1 items-center justify-center gap-3 rounded-bl-lg border border-transparent py-4 text-sm font-semibold text-foreground bg-foreground/5 hover:bg-red-400/10 cursor-pointer hover:scale-105">
|
<Button
|
||||||
<Image width={24} height={24} src="/images/reject.svg" alt="approve hold" />
|
className="inline-flex flex-1 items-center justify-center gap-3 rounded-bl-lg border border-transparent py-4 text-sm font-semibold text-foreground bg-foreground/5 hover:bg-red-400/10 cursor-pointer [&:not(:disabled)]:hover:scale-105"
|
||||||
<span>Decline</span>
|
disabled={!!isRejectingId}
|
||||||
|
onClick={() => handleRejectHoldClick(hold.id)}
|
||||||
|
>
|
||||||
|
{isRejectingId === hold.id ? (
|
||||||
|
<>
|
||||||
|
<Loader2 className="animate-spin size-[24px] text-red-200 dark:text-red-700" />
|
||||||
|
<span className="text-red-200 dark:text-red-700">Rejecting</span>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Image
|
||||||
|
width={24}
|
||||||
|
height={24}
|
||||||
|
src="/images/reject.svg"
|
||||||
|
alt="approve hold"
|
||||||
|
/>
|
||||||
|
<span>Reject</span>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
className="inline-flex flex-1 items-center justify-center gap-3 rounded-br-lg border border-transparent py-4 text-sm font-semibold text-foreground bg-emerald-400/30 hover:bg-emerald-300/60 cursor-pointer hover:scale-105"
|
className="inline-flex flex-1 items-center justify-center gap-3 rounded-br-lg border border-transparent py-4 text-sm font-semibold text-foreground bg-emerald-400/30 hover:bg-emerald-300/60 cursor-pointer hover:scale-105"
|
||||||
|
disabled={!!isRejectingId || !!openedModalId}
|
||||||
onClick={() => setOpenedModalId(hold.id)}
|
onClick={() => setOpenedModalId(hold.id)}
|
||||||
>
|
>
|
||||||
<Image width={24} height={24} src="/images/approve.svg" alt="approve hold" />
|
<Image width={24} height={24} src="/images/approve.svg" alt="approve hold" />
|
||||||
@ -97,7 +146,8 @@ const HoldRequestNotifications = (props: Props) => {
|
|||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
)
|
)
|
||||||
})}
|
})
|
||||||
|
.filter((element) => !!element)}
|
||||||
</ul>
|
</ul>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { Checkout, Repository } from '@/payload-types'
|
import { Checkout, Repository, User } from '@/payload-types'
|
||||||
import { PaginatedDocs } from 'payload'
|
import { PaginatedDocs } from 'payload'
|
||||||
import RepoList from './RepoList'
|
import RepoList from './RepoList'
|
||||||
import HoldRequestNotifications from './HoldRequests'
|
import HoldRequestNotifications from './HoldRequests'
|
||||||
@ -9,9 +9,10 @@ import CheckedOutBooks from './CheckedOutBooks'
|
|||||||
type Props = {
|
type Props = {
|
||||||
repos: PaginatedDocs<Repository> | null
|
repos: PaginatedDocs<Repository> | null
|
||||||
checkouts: PaginatedDocs<Checkout> | null
|
checkouts: PaginatedDocs<Checkout> | null
|
||||||
|
user: User | null
|
||||||
}
|
}
|
||||||
const Manage = (props: Props) => {
|
const Manage = (props: Props) => {
|
||||||
const { repos, checkouts } = props
|
const { repos, checkouts, user } = props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section>
|
<section>
|
||||||
@ -19,9 +20,7 @@ const Manage = (props: Props) => {
|
|||||||
<RepoList repos={repos} />
|
<RepoList repos={repos} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>{user && <HoldRequestNotifications repos={repos} user={user} />}</div>
|
||||||
<HoldRequestNotifications repos={repos} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<CheckedOutBooks initialCheckoutPage={checkouts} />
|
<CheckedOutBooks initialCheckoutPage={checkouts} />
|
||||||
|
|||||||
44
src/serverActions/GetUserRepos.ts
Normal file
44
src/serverActions/GetUserRepos.ts
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
'use server'
|
||||||
|
|
||||||
|
import { getPayload } from 'payload'
|
||||||
|
import config from '@/payload.config'
|
||||||
|
import { PaginatedDocs } from "payload"
|
||||||
|
import { Repository } from '@/payload-types'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
userId: number
|
||||||
|
}
|
||||||
|
export const getUserRepos = async (props: Props) => {
|
||||||
|
const { userId } = props
|
||||||
|
|
||||||
|
const payloadConfig = await config
|
||||||
|
const payload = await getPayload({ config: payloadConfig })
|
||||||
|
|
||||||
|
let userRepos: PaginatedDocs<Repository> | null = null
|
||||||
|
if (userId)
|
||||||
|
userRepos = (await payload.find({
|
||||||
|
collection: 'repositories',
|
||||||
|
depth: 3,
|
||||||
|
limit: 10,
|
||||||
|
select: {
|
||||||
|
name: true,
|
||||||
|
abbreviation: true,
|
||||||
|
image: true,
|
||||||
|
description: true,
|
||||||
|
dateOpenToPublic: true,
|
||||||
|
holdRequests: true,
|
||||||
|
},
|
||||||
|
where: {
|
||||||
|
'owner.id': {
|
||||||
|
equals: userId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
joins: {
|
||||||
|
holdRequests: {
|
||||||
|
limit: 100,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})) as PaginatedDocs<Repository>
|
||||||
|
|
||||||
|
return userRepos
|
||||||
|
}
|
||||||
31
src/serverActions/RejectHoldRequests.ts
Normal file
31
src/serverActions/RejectHoldRequests.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
'use server'
|
||||||
|
|
||||||
|
import { getPayload } from 'payload'
|
||||||
|
import config from '@/payload.config'
|
||||||
|
import { HoldRequest } from '@/payload-types'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
holdRequestId: number,
|
||||||
|
}
|
||||||
|
export const rejectHoldRequest = async (props: Props): Promise<HoldRequest | null> => {
|
||||||
|
const { holdRequestId } = props
|
||||||
|
|
||||||
|
const payloadConfig = await config
|
||||||
|
const payload = await getPayload({ config: payloadConfig })
|
||||||
|
|
||||||
|
try {
|
||||||
|
const updatedHold = await payload.update({
|
||||||
|
collection: 'holdRequests',
|
||||||
|
id: holdRequestId,
|
||||||
|
data: {
|
||||||
|
isRejected: true,
|
||||||
|
isHolding: false,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return updatedHold
|
||||||
|
} catch (_) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default rejectHoldRequest
|
||||||
Loading…
x
Reference in New Issue
Block a user