|
|
|
@ -2,9 +2,13 @@ import { useListExploreAiCourses } from '../../queries/ai-course'; |
|
|
|
|
import { useEffect, useState } from 'react'; |
|
|
|
|
import { AlertCircle, Loader2 } from 'lucide-react'; |
|
|
|
|
import { AICourseCard } from '../GenerateCourse/AICourseCard'; |
|
|
|
|
import { AILoadingState } from './AILoadingState'; |
|
|
|
|
import { AITutorHeader } from './AITutorHeader'; |
|
|
|
|
import { UpgradeAccountModal } from '../Billing/UpgradeAccountModal'; |
|
|
|
|
|
|
|
|
|
export function AIExploreCourseListing() { |
|
|
|
|
const [isInitialLoading, setIsInitialLoading] = useState(true); |
|
|
|
|
const [showUpgradePopup, setShowUpgradePopup] = useState(false); |
|
|
|
|
|
|
|
|
|
const { |
|
|
|
|
data, |
|
|
|
@ -23,50 +27,51 @@ export function AIExploreCourseListing() { |
|
|
|
|
|
|
|
|
|
const courses = data?.pages.flatMap((page) => page.data) ?? []; |
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
|
<> |
|
|
|
|
<div className="mb-3 flex min-h-[35px] items-center justify-between max-sm:mb-1"> |
|
|
|
|
<div className="flex items-center gap-2"> |
|
|
|
|
<h2 className="text-lg font-semibold">Explore Courses</h2> |
|
|
|
|
</div> |
|
|
|
|
if (isInitialLoading || isExploreAiCoursesLoading) { |
|
|
|
|
return ( |
|
|
|
|
<AILoadingState |
|
|
|
|
title="Loading courses" |
|
|
|
|
subtitle="This may take a moment..." |
|
|
|
|
/> |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (error) { |
|
|
|
|
return ( |
|
|
|
|
<div className="flex min-h-[152px] items-center justify-center gap-2 rounded-lg border border-gray-200 bg-white py-4"> |
|
|
|
|
<AlertCircle className="size-4 text-red-500" /> |
|
|
|
|
<p className="text-sm font-medium text-red-600"> |
|
|
|
|
{error?.message ?? 'Error loading courses.'} |
|
|
|
|
</p> |
|
|
|
|
</div> |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
{(isExploreAiCoursesLoading || isInitialLoading) && ( |
|
|
|
|
<div className="flex min-h-[152px] items-center justify-center gap-2 rounded-lg border border-gray-200 bg-white py-4"> |
|
|
|
|
<Loader2 |
|
|
|
|
className="size-4 animate-spin text-gray-400" |
|
|
|
|
strokeWidth={2.5} |
|
|
|
|
/> |
|
|
|
|
<p className="text-sm font-medium text-gray-600">Loading...</p> |
|
|
|
|
</div> |
|
|
|
|
return ( |
|
|
|
|
<> |
|
|
|
|
{showUpgradePopup && ( |
|
|
|
|
<UpgradeAccountModal onClose={() => setShowUpgradePopup(false)} /> |
|
|
|
|
)} |
|
|
|
|
|
|
|
|
|
{error && !isExploreAiCoursesLoading && !isInitialLoading && ( |
|
|
|
|
<div className="flex min-h-[152px] items-center justify-center gap-2 rounded-lg border border-gray-200 bg-white py-4"> |
|
|
|
|
<AlertCircle className="size-4 text-red-500" /> |
|
|
|
|
<p className="text-sm font-medium text-red-600"> |
|
|
|
|
{error?.message ?? 'Error loading courses.'} |
|
|
|
|
</p> |
|
|
|
|
<AITutorHeader |
|
|
|
|
title="Explore Courses" |
|
|
|
|
onUpgradeClick={() => setShowUpgradePopup(true)} |
|
|
|
|
/> |
|
|
|
|
|
|
|
|
|
{courses && courses.length > 0 && ( |
|
|
|
|
<div className="grid grid-cols-3 gap-2"> |
|
|
|
|
{courses.map((course) => ( |
|
|
|
|
<AICourseCard |
|
|
|
|
key={course._id} |
|
|
|
|
course={course} |
|
|
|
|
showActions={false} |
|
|
|
|
showProgress={false} |
|
|
|
|
/> |
|
|
|
|
))} |
|
|
|
|
</div> |
|
|
|
|
)} |
|
|
|
|
|
|
|
|
|
{!isExploreAiCoursesLoading && |
|
|
|
|
courses && |
|
|
|
|
courses.length > 0 && |
|
|
|
|
!error && ( |
|
|
|
|
<div className="grid grid-cols-2 gap-2"> |
|
|
|
|
{courses.map((course) => ( |
|
|
|
|
<AICourseCard |
|
|
|
|
key={course._id} |
|
|
|
|
course={course} |
|
|
|
|
showActions={false} |
|
|
|
|
showProgress={false} |
|
|
|
|
/> |
|
|
|
|
))} |
|
|
|
|
</div> |
|
|
|
|
)} |
|
|
|
|
|
|
|
|
|
{hasNextPage && !isFetchingNextPage && !error && ( |
|
|
|
|
{hasNextPage && !isFetchingNextPage && ( |
|
|
|
|
<div className="mt-4 flex items-center justify-center"> |
|
|
|
|
<button |
|
|
|
|
onClick={() => fetchNextPage()} |
|
|
|
@ -78,7 +83,7 @@ export function AIExploreCourseListing() { |
|
|
|
|
</div> |
|
|
|
|
)} |
|
|
|
|
|
|
|
|
|
{isFetchingNextPage && !error && ( |
|
|
|
|
{isFetchingNextPage && ( |
|
|
|
|
<div className="mt-4 flex items-center justify-center gap-2"> |
|
|
|
|
<Loader2 |
|
|
|
|
className="size-4 animate-spin text-gray-400" |
|
|
|
|