diff --git a/apps/customer-service/package.json b/apps/customer-service/package.json index f5001530b..7b0a91cf9 100644 --- a/apps/customer-service/package.json +++ b/apps/customer-service/package.json @@ -31,6 +31,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-hook-form": "^7.46.1", + "react-intersection-observer": "^9.5.2", "react-intl": "^6.4.7", "superjson": "^1.13.1", "zod": "^3.22.2" diff --git a/apps/customer-service/src/components/tickets/ticket-list.tsx b/apps/customer-service/src/components/tickets/ticket-list.tsx index 5fa5349e2..0c3c1e45f 100644 --- a/apps/customer-service/src/components/tickets/ticket-list.tsx +++ b/apps/customer-service/src/components/tickets/ticket-list.tsx @@ -1,13 +1,15 @@ 'use client'; -import { FC, useEffect } from 'react'; +import { FC, Fragment, useEffect } from 'react'; import { useParams, useRouter, useSearchParams } from 'next/navigation'; import { PartyPopper } from 'lucide-react'; +import { useInView } from 'react-intersection-observer'; import { FormattedMessage } from 'react-intl'; import { TicketStatus } from '@cs/database/schema/ticket'; import { TicketListItem } from '~/components/tickets/ticket-list-item'; +import { TicketListItemSkeleton } from '~/components/tickets/ticket-list-item-skeleton'; import { api } from '~/utils/api'; export const TicketList: FC<{ @@ -18,31 +20,66 @@ export const TicketList: FC<{ const params = useParams(); const router = useRouter(); const searchParams = useSearchParams(); - - const [ticketsData] = api.ticket.all.useSuspenseQuery({ - filter: filter, - status: status, - orderBy: orderBy, + const { ref, inView } = useInView({ + triggerOnce: true, + threshold: 1, }); + const [data, allTicketsQuery] = api.ticket.all.useSuspenseInfiniteQuery( + { + filter: filter, + status: status, + orderBy: orderBy, + }, + { + getNextPageParam(lastPage) { + return lastPage.nextCursor; + }, + } + ); + + const { isFetching, fetchNextPage, hasNextPage } = allTicketsQuery; + useEffect(() => { - if (!ticketsData || ticketsData.length === 0 || params.id) return; + if (!data.pages || data.pages.length === 0 || params.id) return; - const firstTicketIdFromList = ticketsData?.[0]?.id; + const firstTicketIdFromList = data.pages[0]?.data?.[0]?.id; if (!firstTicketIdFromList) return; router.replace( `/tickets/${firstTicketIdFromList}?${searchParams.toString()}` ); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [ticketsData]); + }, [data]); + + useEffect(() => { + if (inView && hasNextPage && !isFetching) { + fetchNextPage(); + } + }, [inView, hasNextPage, isFetching, fetchNextPage]); return (