feat: user feed stats use real data now
This commit is contained in:
parent
0e9891b5a2
commit
6f2b32ee4a
1
public/images/book-loan.svg
Normal file
1
public/images/book-loan.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 80 80" width="80px" height="80px"> <path d="M 27 7 L 27 36 L 29 36 L 29 37 C 29 38.64497 30.35503 40 32 40 L 45.505859 40 C 46.284666 41.745175 47.971465 43 50 43 C 52.028851 43 53.715042 41.74513 54.494141 40 L 68 40 C 69.64497 40 71 38.64497 71 37 L 71 36 L 73 36 L 73 7 L 53 7 C 51.789062 7 50.735556 7.5762461 50 8.4355469 C 49.264444 7.5762461 48.210938 7 47 7 L 27 7 z M 29 9 L 47 9 C 48.116666 9 49 9.8833339 49 11 A 1 1 0 0 0 50 12 A 1 1 0 0 0 51 11 C 51 9.8833339 51.883334 9 53 9 L 71 9 L 71 34 L 53 34 C 51.789062 34 50.735556 34.576246 50 35.435547 C 49.264444 34.576246 48.210938 34 47 34 L 29 34 L 29 9 z M 50 14 A 1 1 0 0 0 49 15 A 1 1 0 0 0 50 16 A 1 1 0 0 0 51 15 A 1 1 0 0 0 50 14 z M 50 18 A 1 1 0 0 0 49 19 A 1 1 0 0 0 50 20 A 1 1 0 0 0 51 19 A 1 1 0 0 0 50 18 z M 50 22 A 1 1 0 0 0 49 23 A 1 1 0 0 0 50 24 A 1 1 0 0 0 51 23 A 1 1 0 0 0 50 22 z M 50 26 A 1 1 0 0 0 49 27 A 1 1 0 0 0 50 28 A 1 1 0 0 0 51 27 A 1 1 0 0 0 50 26 z M 50 30 A 1 1 0 0 0 49 31 A 1 1 0 0 0 50 32 A 1 1 0 0 0 51 31 A 1 1 0 0 0 50 30 z M 31 36 L 47 36 C 48.116666 36 49 36.883334 49 38 L 51 38 C 51 36.883334 51.883334 36 53 36 L 69 36 L 69 37 C 69 37.56503 68.56503 38 68 38 L 53.085938 38 L 52.890625 38.748047 C 52.554647 40.042353 51.404427 41 50 41 C 48.595573 41 47.444396 40.043513 47.109375 38.75 L 46.914062 38 L 32 38 C 31.43497 38 31 37.56503 31 37 L 31 36 z M 23 42 C 22.017889 42 21.08157 42.18834 20.21875 42.509766 C 20.18892 42.514128 20.227207 42.495228 20.113281 42.533203 L 20.138672 42.523438 C 17.978144 43.181327 7.7324219 46.037109 7.7324219 46.037109 A 1.0001 1.0001 0 1 0 8.2675781 47.962891 C 8.2675781 47.962891 18.280242 45.176411 20.652344 44.457031 A 1.0001 1.0001 0 0 0 20.859375 44.402344 C 21.530108 44.145103 22.246496 44 23 44 C 24.098229 44 25.118963 44.297956 26.005859 44.814453 A 1.0001 1.0001 0 0 0 26.113281 44.867188 C 26.120981 44.870787 26.128969 44.875286 26.136719 44.878906 C 32.438552 47.85405 45.236876 54.261963 45.271484 54.279297 L 45.275391 54.283203 C 45.216481 54.251753 45.22225 54.255524 45.34375 54.333984 A 1.0001 1.0001 0 0 0 45.412109 54.375 C 46.357688 54.884801 47 55.853888 47 57 C 47 57.599629 46.823064 58.148508 46.519531 58.619141 C 45.639673 59.98846 43.938917 60.277762 42.347656 59.511719 A 1.0001 1.0001 0 0 0 42.291016 59.486328 L 41.609375 59.208984 A 1.0001 1.0001 0 1 0 40.855469 61.060547 L 41.537109 61.337891 L 41.480469 61.3125 C 43.492541 62.281125 45.947929 62.063701 47.521484 60.533203 L 48.609375 61.277344 A 1.0001 1.0001 0 0 0 49.556641 61.375 L 65.964844 54.591797 L 65.548828 53.761719 A 1.0001 1.0001 0 0 0 66.833984 54.234375 C 67.191771 54.083173 67.583266 54 68 54 C 69.668484 54 71 55.331516 71 57 C 71 58.138835 70.365199 59.105047 69.427734 59.619141 A 1.0001 1.0001 0 0 0 68.994141 60.902344 L 68.619141 60.058594 L 48.904297 70.523438 C 48.937547 70.507148 48.908401 70.521236 48.863281 70.535156 A 1.0001 1.0001 0 0 0 48.726562 70.589844 C 48.18739 70.849566 47.611406 71 47 71 C 46.411018 71 45.857148 70.864391 45.339844 70.626953 A 1.0001 1.0001 0 0 0 45.269531 70.597656 C 45.194433 70.569844 45.181733 70.564316 45.234375 70.587891 L 20.388672 60.078125 A 1.0001 1.0001 0 0 0 19.683594 60.050781 L 7.6835938 64.050781 A 1.0005646 1.0005646 0 0 0 8.3164062 65.949219 L 19.958984 62.068359 L 44.429688 72.417969 C 44.580551 72.486539 44.612518 72.488069 44.576172 72.474609 C 45.309494 72.800661 46.128375 73 47 73 C 47.942594 73 48.820929 72.764852 49.59375 72.392578 C 49.65405 72.371198 49.707407 72.357443 49.783203 72.320312 A 1.0001 1.0001 0 0 0 49.8125 72.304688 L 70.380859 61.388672 A 1.0001 1.0001 0 0 0 70.775391 61.003906 C 72.066526 60.104048 73 58.69514 73 57 C 73 54.250484 70.749516 52 68 52 C 67.452814 52 66.946312 52.148716 66.451172 52.3125 L 66.449219 52.3125 A 1.0001 1.0001 0 0 0 66.060547 52.388672 L 49.291016 59.320312 L 48.470703 58.759766 C 48.697951 58.189724 49 57.649238 49 57 C 49 55.100112 47.919752 53.453486 46.361328 52.613281 L 46.429688 52.654297 C 46.462986 52.675807 46.396628 52.613357 46.21875 52.517578 A 1.0001 1.0001 0 0 0 46.193359 52.503906 C 46.193359 52.503906 33.328754 46.060283 26.96875 43.058594 A 1.0001 1.0001 0 0 0 26.947266 43.048828 C 26.837778 43.000168 26.826829 42.999864 26.880859 43.021484 C 25.732293 42.380042 24.409831 42 23 42 z M 29 55 A 1 1 0 0 0 28 56 A 1 1 0 0 0 29 57 A 1 1 0 0 0 30 56 A 1 1 0 0 0 29 55 z M 32 56 A 1 1 0 0 0 31 57 A 1 1 0 0 0 32 58 A 1 1 0 0 0 33 57 A 1 1 0 0 0 32 56 z M 35 57 A 1 1 0 0 0 34 58 A 1 1 0 0 0 35 59 A 1 1 0 0 0 36 58 A 1 1 0 0 0 35 57 z M 38 58 A 1 1 0 0 0 37 59 A 1 1 0 0 0 38 60 A 1 1 0 0 0 39 59 A 1 1 0 0 0 38 58 z"/></svg>
|
After Width: | Height: | Size: 4.6 KiB |
1
public/images/book-shelf.svg
Normal file
1
public/images/book-shelf.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 5.0 KiB |
1
public/images/mail.svg
Normal file
1
public/images/mail.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 6.1 KiB |
@ -103,7 +103,9 @@ export default async function HomePage() {
|
|||||||
<TabsTrigger value="manage">Manage</TabsTrigger>
|
<TabsTrigger value="manage">Manage</TabsTrigger>
|
||||||
</TabsList>
|
</TabsList>
|
||||||
|
|
||||||
<TabsContent value="feed">{user && <UserFeed user={user} />}</TabsContent>
|
<TabsContent value="feed">
|
||||||
|
{user && <UserFeed user={user} repos={userRepos} />}
|
||||||
|
</TabsContent>
|
||||||
|
|
||||||
<TabsContent value="search">
|
<TabsContent value="search">
|
||||||
<SearchBooks initBrowseBooks={initBrowseBooks} />
|
<SearchBooks initBrowseBooks={initBrowseBooks} />
|
||||||
|
@ -43,7 +43,7 @@ const Checkouts: CollectionConfig = {
|
|||||||
collection: 'copies',
|
collection: 'copies',
|
||||||
on: 'book',
|
on: 'book',
|
||||||
hasMany: false,
|
hasMany: false,
|
||||||
}
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import type { Book, HoldRequest, Repository, User } from '@/payload-types'
|
import type { Book, Checkout, HoldRequest, Repository, User } from '@/payload-types'
|
||||||
import { getPayload, PaginatedDocs } from 'payload'
|
import { getPayload, PaginatedDocs } from 'payload'
|
||||||
import config from '@/payload.config'
|
import config from '@/payload.config'
|
||||||
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '../ui/card'
|
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '../ui/card'
|
||||||
@ -16,9 +16,10 @@ const stats = [
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
user?: User
|
user?: User
|
||||||
|
repos: PaginatedDocs<Repository> | null
|
||||||
}
|
}
|
||||||
const UserFeed = async (props: Props) => {
|
const UserFeed = async (props: Props) => {
|
||||||
const { user } = props
|
const { user, repos } = props
|
||||||
const isLoggedIn = !!user
|
const isLoggedIn = !!user
|
||||||
|
|
||||||
if (!isLoggedIn)
|
if (!isLoggedIn)
|
||||||
@ -51,42 +52,137 @@ const UserFeed = async (props: Props) => {
|
|||||||
},
|
},
|
||||||
})) as PaginatedDocs<HoldRequest>
|
})) as PaginatedDocs<HoldRequest>
|
||||||
|
|
||||||
|
const currentlyHeldBooks = (await payload.find({
|
||||||
|
collection: 'holdRequests',
|
||||||
|
limit: 10,
|
||||||
|
depth: 3,
|
||||||
|
select: {
|
||||||
|
copy: true,
|
||||||
|
dateRequested: true,
|
||||||
|
repository: true,
|
||||||
|
book: true,
|
||||||
|
},
|
||||||
|
where: {
|
||||||
|
'repository.owner': {
|
||||||
|
equals: user?.id,
|
||||||
|
},
|
||||||
|
isHolding: {
|
||||||
|
equals: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})) as PaginatedDocs<HoldRequest>
|
||||||
|
|
||||||
|
let loanedOutBooks: PaginatedDocs<Checkout> | null = null
|
||||||
|
if (user.id)
|
||||||
|
loanedOutBooks = (await payload.find({
|
||||||
|
collection: 'checkouts',
|
||||||
|
limit: 10,
|
||||||
|
depth: 2,
|
||||||
|
select: {
|
||||||
|
book: true,
|
||||||
|
dateDue: true,
|
||||||
|
loanerReturnedDate: true,
|
||||||
|
isReturned: true,
|
||||||
|
ownerVerifiedReturnedDate: true,
|
||||||
|
},
|
||||||
|
where: {
|
||||||
|
and: [
|
||||||
|
{
|
||||||
|
'copy.repository.owner': {
|
||||||
|
equals: user.id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
isReturned: {
|
||||||
|
not_equals: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
})) as PaginatedDocs<Checkout>
|
||||||
|
|
||||||
|
const totalHoldNotifications = repos?.docs.flatMap((r) => r.holdRequests?.docs).length || 0
|
||||||
|
const outBoundLoanCount = loanedOutBooks?.totalDocs || 0
|
||||||
|
const currentlyHoldingCount = currentlyHeldBooks?.totalDocs || 0
|
||||||
|
|
||||||
|
const stats = [
|
||||||
|
{
|
||||||
|
name: 'Hold Request',
|
||||||
|
iconSrc: '/images/mail.svg',
|
||||||
|
value: totalHoldNotifications,
|
||||||
|
href: '#',
|
||||||
|
ctaText: 'Answer Requests',
|
||||||
|
shouldAnimate: totalHoldNotifications > 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Loaned Out',
|
||||||
|
iconSrc: '/images/book-loan.svg',
|
||||||
|
value: outBoundLoanCount,
|
||||||
|
href: '#',
|
||||||
|
ctaText: 'Handle Returns',
|
||||||
|
shouldAnimate: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Currently Holding',
|
||||||
|
iconSrc: '/images/book-shelf.svg',
|
||||||
|
value: currentlyHoldingCount,
|
||||||
|
href: '#',
|
||||||
|
ctaText: 'Loan Out',
|
||||||
|
shouldAnimate: false,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section>
|
<section>
|
||||||
<div className="my-6">
|
<div className="my-6">
|
||||||
<h3 className="text-lg font-semibold text-foreground">Outbound Activity</h3>
|
<h3 className="text-lg font-semibold text-foreground">Outbound Activity</h3>
|
||||||
<dl className="mt-5 grid grid-cols-1 gap-5 sm:grid-cols-3">
|
<dl className="mt-5 grid grid-cols-1 gap-5 sm:grid-cols-3">
|
||||||
{stats.map((item) => (
|
{stats.map((s) => {
|
||||||
<div
|
return (
|
||||||
key={item.name}
|
<div
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'relative overflow-hidden rounded-lg accent-background px-4 py-5 shadow-sm sm:p-6',
|
'relative overflow-hidden rounded-lg rounded-lg shadow-sm border border-accent px-4 pt-5 pb-12 shadow-sm sm:px-6 sm:pt-6',
|
||||||
!item.shouldHighlight ? 'border-1 border-muted-foreground' : '',
|
s.shouldAnimate && 'animate-pulse',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{!!item.shouldHighlight && (
|
<dt>
|
||||||
<BorderTrail
|
<div className="absolute rounded-md bg-emerald-500 p-3">
|
||||||
style={{
|
<Image
|
||||||
boxShadow:
|
src={s.iconSrc}
|
||||||
'0px 0px 60px 30px rgb(200 255 200 / 50%), 0 0 100px 60px rgb(0 0 0 / 50%), 0 0 140px 90px rgb(0 0 0 / 50%)',
|
alt={s.name}
|
||||||
}}
|
width={30}
|
||||||
size={100}
|
height={30}
|
||||||
/>
|
className="dark:invert"
|
||||||
)}
|
/>
|
||||||
<dt className="truncate text-sm font-medium text-muted-foreground">{item.name}</dt>
|
</div>
|
||||||
<dd className="mt-1 text-3xl font-semibold tracking-tight text-foreground">
|
<p className="ml-16 truncate text-sm font-medium text-muted-foreground">
|
||||||
{item.stat}
|
{s.name}
|
||||||
</dd>
|
</p>
|
||||||
</div>
|
</dt>
|
||||||
))}
|
<dd className="ml-16 flex items-baseline pb-6 sm:pb-7">
|
||||||
|
<p className="text-2xl font-semibold text-foreground">{s.value}</p>
|
||||||
|
<div className="absolute inset-x-0 bottom-0 px-4 py-4 sm:px-6">
|
||||||
|
<div className="text-sm">
|
||||||
|
<a
|
||||||
|
href={s.href}
|
||||||
|
className="font-medium text-emerald-800 hover:text-emerald-300"
|
||||||
|
>
|
||||||
|
<span>{s.ctaText}</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="my-6">
|
<div className="my-6">
|
||||||
<h2 className="text-lg font-semibold text-foreground">Inbound Activity</h2>
|
<h2 className="text-lg font-semibold text-foreground">Inbound Activity</h2>
|
||||||
<div className="my-3">
|
<div className="my-3">
|
||||||
<h3 className="text-base font-semibold text-muted-foreground">Your Holds</h3>
|
<h3 className="text-base font-semibold text-muted-foreground mb-4">Your Holds</h3>
|
||||||
<ul className="flex flex-wrap justify gap-4 last-child-adjustment">
|
<ul className="flex flex-wrap justify-around gap-3 last-child-adjustment">
|
||||||
{holdRequests.docs?.map((h) => {
|
{holdRequests.docs?.map((h) => {
|
||||||
const book = h.book as Book
|
const book = h.book as Book
|
||||||
const repository = h.repository as Repository
|
const repository = h.repository as Repository
|
||||||
@ -98,7 +194,7 @@ const UserFeed = async (props: Props) => {
|
|||||||
return (
|
return (
|
||||||
<li className="inline-block" key={book.isbn}>
|
<li className="inline-block" key={book.isbn}>
|
||||||
<Link className="block hover:scale-105 transition-all" href={`/books/${book.id}`}>
|
<Link className="block hover:scale-105 transition-all" href={`/books/${book.id}`}>
|
||||||
<Card className="w-48 pt-4 pb-2">
|
<Card className="w-40 pt-4 pb-2">
|
||||||
<CardHeader className="-mb-4">
|
<CardHeader className="-mb-4">
|
||||||
<CardTitle className="text-overflow-ellipsis line-clamp-2">
|
<CardTitle className="text-overflow-ellipsis line-clamp-2">
|
||||||
{book.title}
|
{book.title}
|
||||||
|
@ -77,7 +77,7 @@ const HomeHero = (props: Props) => {
|
|||||||
<Magnetic intensity={0.2} springOptions={{ bounce: 0.1 }} actionArea="global" range={200}>
|
<Magnetic intensity={0.2} springOptions={{ bounce: 0.1 }} actionArea="global" range={200}>
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
className="animate-pulse mx-auto block items-center rounded-lg border border-zinc-100/10 bg-transparent text-foreground px-8 py-2 text-sm transition-all duration-200 h-fit"
|
className="animate-pulse animate-bounce mx-auto block items-center rounded-lg border-zinc-100/10 bg-transparent text-foreground px-8 py-2 text-sm transition-all duration-200 h-fit"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const tabs = document.getElementById('tabs')
|
const tabs = document.getElementById('tabs')
|
||||||
tabs?.scrollIntoView({ behavior: 'smooth' })
|
tabs?.scrollIntoView({ behavior: 'smooth' })
|
||||||
|
Loading…
x
Reference in New Issue
Block a user