From 2868fa3c27c052aee8aa1e043a0607ef8802ca37 Mon Sep 17 00:00:00 2001 From: Kamran Ahmed Date: Fri, 11 Apr 2025 11:25:08 +0100 Subject: [PATCH] Implement pagination of ai tutor ai courses --- .../AITutor/AIExploreCourseListing.tsx | 107 ++++++++++-------- src/queries/ai-course.ts | 50 +++----- 2 files changed, 80 insertions(+), 77 deletions(-) diff --git a/src/components/AITutor/AIExploreCourseListing.tsx b/src/components/AITutor/AIExploreCourseListing.tsx index c8b25f560..97ddd2f5b 100644 --- a/src/components/AITutor/AIExploreCourseListing.tsx +++ b/src/components/AITutor/AIExploreCourseListing.tsx @@ -1,31 +1,53 @@ -import { useListExploreAiCourses } from '../../queries/ai-course'; +import { useQuery } from '@tanstack/react-query'; import { useEffect, useState } from 'react'; -import { AlertCircle, Loader2 } from 'lucide-react'; +import { AlertCircle } from 'lucide-react'; import { AICourseCard } from '../GenerateCourse/AICourseCard'; import { AILoadingState } from './AILoadingState'; import { AITutorHeader } from './AITutorHeader'; import { UpgradeAccountModal } from '../Billing/UpgradeAccountModal'; +import { + listExploreAiCoursesOptions, + type ListExploreAiCoursesQuery, +} from '../../queries/ai-course'; +import { queryClient } from '../../stores/query-client'; +import { deleteUrlParam, getUrlParams, setUrlParams } from '../../lib/browser'; +import { Pagination } from '../Pagination/Pagination'; export function AIExploreCourseListing() { const [isInitialLoading, setIsInitialLoading] = useState(true); const [showUpgradePopup, setShowUpgradePopup] = useState(false); - const { - data, - error, - fetchNextPage, - hasNextPage, - isFetching, - isFetchingNextPage, - status, - isLoading: isExploreAiCoursesLoading, - } = useListExploreAiCourses(); + const [pageState, setPageState] = useState({ + perPage: '21', + currPage: '1', + }); + + const { data: exploreAiCourses, isFetching: isExploreAiCoursesLoading } = + useQuery(listExploreAiCoursesOptions(pageState), queryClient); useEffect(() => { setIsInitialLoading(false); - }, [data]); + }, [exploreAiCourses]); + + const courses = exploreAiCourses?.data ?? []; - const courses = data?.pages.flatMap((page) => page.data) ?? []; + useEffect(() => { + const queryParams = getUrlParams(); + setPageState({ + ...pageState, + currPage: queryParams?.p || '1', + }); + }, []); + + useEffect(() => { + if (pageState?.currPage !== '1') { + setUrlParams({ + p: pageState?.currPage || '1', + }); + } else { + deleteUrlParam('p'); + } + }, [pageState]); if (isInitialLoading || isExploreAiCoursesLoading) { return ( @@ -36,12 +58,12 @@ export function AIExploreCourseListing() { ); } - if (error) { + if (!exploreAiCourses?.data) { return (

- {error?.message ?? 'Error loading courses.'} + Error loading courses.

); @@ -59,39 +81,34 @@ export function AIExploreCourseListing() { /> {courses && courses.length > 0 && ( -
- {courses.map((course) => ( - - ))} -
- )} +
+
+ {courses.map((course) => ( + + ))} +
- {hasNextPage && !isFetchingNextPage && ( -
- + { + setPageState({ ...pageState, currPage: String(page) }); + }} + className="rounded-lg border border-gray-200 bg-white p-4" + />
)} - {isFetchingNextPage && ( -
- -

- Loading more courses... -

+ {!isExploreAiCoursesLoading && courses.length === 0 && ( +
+

No courses found.

)} diff --git a/src/queries/ai-course.ts b/src/queries/ai-course.ts index 8b269124f..846bdb63e 100644 --- a/src/queries/ai-course.ts +++ b/src/queries/ai-course.ts @@ -1,7 +1,6 @@ import { httpGet } from '../lib/query-http'; import { isLoggedIn } from '../lib/jwt'; -import { queryOptions, useInfiniteQuery } from '@tanstack/react-query'; -import { queryClient } from '../stores/query-client'; +import { queryOptions } from '@tanstack/react-query'; export interface AICourseProgressDocument { _id: string; @@ -135,45 +134,32 @@ export function listFeaturedAiCoursesOptions( type ListExploreAiCoursesParams = {}; -type ListExploreAiCoursesQuery = { +export type ListExploreAiCoursesQuery = { perPage?: string; currPage?: string; }; type ListExploreAiCoursesResponse = { data: AICourseWithLessonCount[]; + totalCount: number; + totalPages: number; currPage: number; perPage: number; }; -export function useListExploreAiCourses() { - return useInfiniteQuery( - { - queryKey: ['explore-ai-courses'], - queryFn: ({ pageParam = 1 }) => { - return httpGet( - `/v1-list-explore-ai-courses`, - { - perPage: '21', - currPage: String(pageParam), - }, - ); - }, - getNextPageParam: (lastPage, pages, lastPageParam) => { - if (lastPage?.data?.length === 0) { - return undefined; - } - - return lastPageParam + 1; - }, - getPreviousPageParam: (firstPage, allPages, firstPageParam) => { - if (firstPageParam <= 1) { - return undefined; - } - return firstPageParam - 1; - }, - initialPageParam: 1, +export function listExploreAiCoursesOptions( + params: ListExploreAiCoursesQuery = { + perPage: '21', + currPage: '1', + }, +) { + return { + queryKey: ['explore-ai-courses', params], + queryFn: () => { + return httpGet( + `/v1-list-explore-ai-courses`, + params, + ); }, - queryClient, - ); + }; }