Skip to content

Commit

Permalink
fix: add my posts pagination
Browse files Browse the repository at this point in the history
  • Loading branch information
ValeryVerkhoturov committed Jun 30, 2023
1 parent d8a0a7c commit d0a2059
Show file tree
Hide file tree
Showing 2 changed files with 161 additions and 32 deletions.
132 changes: 104 additions & 28 deletions src/components/posts/my-posts-list/my-posts-list.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { type PostItemReason } from '@prisma/client'
import Window from '@/components/form/window'
import { ChevronRightIcon } from '@heroicons/react/20/solid'
import { api, type RouterOutputs } from '@/lib/api'
import { api, type RouterInputs, type RouterOutputs } from '@/lib/api'
import { useState } from 'react'
import { formatDate } from '@/lib/format-date'
import Link from 'next/link'
import { Campus } from '@/lib/campus'
import Image from 'next/image'
import classNames from 'classnames/dedupe'

const pageSize = 5

interface MyPostsListProps {
reason: PostItemReason
Expand All @@ -30,13 +33,51 @@ function MyPostSkeleton() {
)
}

export default function MyPostsList(props: MyPostsListProps) {
const myPostsListQuery = api.posts.getMyPosts.useQuery(
{ reason: props.reason },
{ onSuccess: (data) => setMyPosts(data) },
export default function MyPostsList({ reason }: MyPostsListProps) {
const [myPosts, setMyPosts] = useState<RouterOutputs['posts']['getMyPosts']['items']>([])
const [page, setPage] = useState(0)
const [hasMore, setHasMore] = useState<RouterOutputs['posts']['getMyPosts']['hasMore']>()
const [previousCursor, setPreviousCursor] =
useState<RouterInputs['posts']['getMyPosts']['cursor']>()

const myPostsListQuery = api.posts.getMyPosts.useInfiniteQuery(
{ reason, limit: pageSize },
{
onSuccess: (data) => {
const result = data.pages[page]
setMyPosts(result?.items ?? [])
setHasMore(result?.hasMore)
setPreviousCursor(previousCursor)
},
getNextPageParam: (lastPage) => lastPage.nextCursor,
getPreviousPageParam: () => previousCursor,
},
)

const [countPost, setCountPosts] = useState<number>()

const countMyPostsQuery = api.posts.countMyPosts.useQuery(
{ reason },
{
onSuccess: (data) => {
setCountPosts(data)
},
},
)

const [myPosts, setMyPosts] = useState<RouterOutputs['posts']['getMyPosts']>([])
const previousPageButtonIsDisabled = page === 0
const nextPageButtonIsDisabled = !hasMore

const fetchNextPage = async () => {
setPage(page + 1)
await myPostsListQuery.fetchNextPage()
}

const fetchPreviousPage = async () => {
setPage(page - 1)
await myPostsListQuery.fetchPreviousPage()
}

return (
<Window>
<ul role='list' className='divide-y divide-gray-100'>
Expand All @@ -54,30 +95,65 @@ export default function MyPostsList(props: MyPostsListProps) {
Постов нет
</div>
)}
{!myPostsListQuery.isLoading &&
myPosts.length > 0 &&
myPosts.map((myPost, index) => (
<Link href={myPost.id} key={index}>
<li className='flex justify-between gap-x-6 py-5'>
<div className='min-w-0'>
<p className='text-sm font-semibold leading-6 text-gray-900'>{myPost.name}</p>
<p className='mt-1 truncate text-xs leading-5 text-gray-500'>
{myPost.description}
</p>
</div>
<div className='hidden sm:flex sm:flex-row sm:items-center sm:gap-x-4'>
<div className='flex flex-col items-end'>
<p className='text-sm leading-6 text-gray-900'>{Campus[myPost.campus]}</p>
<p className='mt-1 text-xs leading-5 text-gray-500'>
{formatDate(myPost.created.toString())}{' '}
{formatDate(myPost.expires.toString())}
{!myPostsListQuery.isLoading && myPosts.length > 0 && (
<>
{myPosts.map((myPost, index) => (
<Link href={myPost.id} key={index}>
<li className='flex justify-between gap-x-6 py-5'>
<div className='min-w-0'>
<p className='text-sm font-semibold leading-6 text-gray-900'>{myPost.name}</p>
<p className='mt-1 truncate text-xs leading-5 text-gray-500'>
{myPost.description}
</p>
</div>
<ChevronRightIcon className='h-5 w-5 text-gray-500' />
</div>
</li>
</Link>
))}
<div className='hidden sm:flex sm:flex-row sm:items-center sm:gap-x-4'>
<div className='flex flex-col items-end'>
<p className='text-sm leading-6 text-gray-900'>{Campus[myPost.campus]}</p>
<p className='mt-1 text-xs leading-5 text-gray-500'>
Дата создания: {formatDate(myPost.created.toString())}, истекает:{' '}
{formatDate(myPost.expires.toString())}
</p>
</div>
<ChevronRightIcon className='h-5 w-5 text-gray-500' />
</div>
</li>
</Link>
))}
<nav
className='flex items-center justify-between border-t border-gray-200 bg-white px-4 py-3 sm:px-6'
aria-label='Pagination'
>
<div className='hidden sm:block'>
<p className='text-sm text-gray-700'>
<span className='font-medium'>{page * pageSize + 1}</span>
<span className='font-medium'>{page * pageSize + myPosts.length}</span> из{' '}
</p>
</div>
<div className='flex flex-1 justify-between sm:justify-end'>
<button
disabled={previousPageButtonIsDisabled}
onClick={() => void fetchPreviousPage()}
className={classNames(
'relative ml-3 inline-flex items-center rounded-md border border-gray-300 px-4 py-2 text-sm font-medium text-gray-700',
previousPageButtonIsDisabled ? 'bg-gray-100' : 'bg-white hover:bg-gray-50',
)}
>
Предыдущая
</button>
<button
disabled={nextPageButtonIsDisabled}
onClick={() => void fetchNextPage()}
className={classNames(
'relative ml-3 inline-flex items-center rounded-md border border-gray-300 px-4 py-2 text-sm font-medium text-gray-700',
nextPageButtonIsDisabled ? 'bg-gray-100' : 'bg-white hover:bg-gray-50',
)}
>
Следующая
</button>
</div>
</nav>
</>
)}
</ul>
</Window>
)
Expand Down
61 changes: 57 additions & 4 deletions src/server/api/routers/posts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,11 +118,64 @@ export const postsRouter = createTRPCRouter({
}),

getMyPosts: protectedProcedure
.input(z.object({ reason: z.nativeEnum(PostItemReason) }))
.input(
z.object({
cursor: z.string().nullish(), // cursor is required for infinite query
limit: z.number().min(1).max(25),
reason: z.nativeEnum(PostItemReason),
}),
)
.query(async ({ ctx, input }) => {
const { cursor, limit, reason } = input
const items = await prisma.lostAndFoundItem.findMany({
where: {
userId: ctx.session.user.id,
reason,
},
orderBy: {
created: 'desc',
},
take: limit + 1,
cursor: cursor ? { id: cursor } : undefined,
})
const previousCursor = (
await prisma.lostAndFoundItem.findFirst({
select: {
id: true,
},
where: {
userId: ctx.session.user.id,
reason,
},
orderBy: {
created: 'desc',
},
take: -limit,
cursor: cursor ? { id: cursor } : undefined,
})
)?.id
let nextCursor: typeof cursor | undefined = undefined
if (items.length > limit) {
const nextItem = items.pop()
nextCursor = nextItem?.id
}
return { items, nextCursor, previousCursor, hasMore: nextCursor !== undefined }
}),

countMyPosts: protectedProcedure
.input(
z.object({
reason: z.nativeEnum(PostItemReason),
}),
)
.query(({ ctx, input }) => {
const myPosts = prisma.lostAndFoundItem.findMany({
where: { userId: ctx.session.user.id, reason: input.reason },
const { reason } = input
const countMyPosts = prisma.lostAndFoundItem.count({
where: {
userId: ctx.session.user.id,
reason,
},
})
return myPosts
return countMyPosts
}),
})

0 comments on commit d0a2059

Please sign in to comment.