AI explore page with search

feat/ai-tutor-redesign
Kamran Ahmed 3 days ago
parent 2868fa3c27
commit b28eb5fecf
  1. 72
      src/components/AITutor/AIExploreCourseListing.tsx
  2. 2
      src/components/AITutor/AILoadingState.tsx
  3. 2
      src/components/AITutor/AITutorTallMessage.tsx
  4. 6
      src/queries/ai-course.ts

@ -1,6 +1,5 @@
import { useQuery } from '@tanstack/react-query';
import { useEffect, useState } from 'react';
import { AlertCircle } from 'lucide-react';
import { AICourseCard } from '../GenerateCourse/AICourseCard';
import { AILoadingState } from './AILoadingState';
import { AITutorHeader } from './AITutorHeader';
@ -12,6 +11,9 @@ import {
import { queryClient } from '../../stores/query-client';
import { deleteUrlParam, getUrlParams, setUrlParams } from '../../lib/browser';
import { Pagination } from '../Pagination/Pagination';
import { AICourseSearch } from '../GenerateCourse/AICourseSearch';
import { AITutorTallMessage } from './AITutorTallMessage';
import { BookOpen } from 'lucide-react';
export function AIExploreCourseListing() {
const [isInitialLoading, setIsInitialLoading] = useState(true);
@ -20,10 +22,14 @@ export function AIExploreCourseListing() {
const [pageState, setPageState] = useState<ListExploreAiCoursesQuery>({
perPage: '21',
currPage: '1',
query: '',
});
const { data: exploreAiCourses, isFetching: isExploreAiCoursesLoading } =
useQuery(listExploreAiCoursesOptions(pageState), queryClient);
const {
data: exploreAiCourses,
isFetching: isExploreAiCoursesLoading,
isRefetching: isExploreAiCoursesRefetching,
} = useQuery(listExploreAiCoursesOptions(pageState), queryClient);
useEffect(() => {
setIsInitialLoading(false);
@ -49,26 +55,6 @@ export function AIExploreCourseListing() {
}
}, [pageState]);
if (isInitialLoading || isExploreAiCoursesLoading) {
return (
<AILoadingState
title="Loading courses"
subtitle="This may take a moment..."
/>
);
}
if (!exploreAiCourses?.data) {
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 loading courses.
</p>
</div>
);
}
return (
<>
{showUpgradePopup && (
@ -78,9 +64,27 @@ export function AIExploreCourseListing() {
<AITutorHeader
title="Explore Courses"
onUpgradeClick={() => setShowUpgradePopup(true)}
/>
>
<AICourseSearch
value={pageState?.query || ''}
onChange={(value) => {
setPageState({
...pageState,
query: value,
currPage: '1',
});
}}
/>
</AITutorHeader>
{(isInitialLoading || isExploreAiCoursesLoading) && (
<AILoadingState
title="Loading courses"
subtitle="This may take a moment..."
/>
)}
{courses && courses.length > 0 && (
{!isExploreAiCoursesLoading && courses && courses.length > 0 && (
<div className="flex flex-col gap-2">
<div className="grid grid-cols-3 gap-2">
{courses.map((course) => (
@ -106,11 +110,19 @@ export function AIExploreCourseListing() {
</div>
)}
{!isExploreAiCoursesLoading && courses.length === 0 && (
<div className="flex min-h-[114px] items-center justify-center rounded-lg border border-gray-200 bg-white py-4">
<p className="text-sm text-gray-600">No courses found.</p>
</div>
)}
{!isInitialLoading &&
!isExploreAiCoursesLoading &&
courses.length === 0 && (
<AITutorTallMessage
title="No courses found"
subtitle="Try a different search or check back later."
icon={BookOpen}
buttonText="Create your first course"
onButtonClick={() => {
window.location.href = '/ai';
}}
/>
)}
</>
);
}

@ -9,7 +9,7 @@ export function AILoadingState(props: AILoadingStateProps) {
const { title, subtitle } = props;
return (
<div className="flex min-h-full w-full flex-col items-center justify-center gap-4 rounded-lg border border-gray-200 bg-white p-8">
<div className="flex flex-grow w-full flex-col items-center justify-center gap-4 rounded-lg border border-gray-200 bg-white p-8">
<div className="relative">
<Loader2 className="size-12 animate-spin text-gray-300" />
<div className="absolute inset-0 flex items-center justify-center">

@ -12,7 +12,7 @@ export function AITutorTallMessage(props: AITutorTallMessageProps) {
const { title, subtitle, icon: Icon, buttonText, onButtonClick } = props;
return (
<div className="flex min-h-full flex-grow flex-col items-center justify-center rounded-lg">
<div className="flex flex-grow flex-col items-center justify-center rounded-lg border border-gray-200 bg-white p-8">
<Icon className="size-12 text-gray-300" />
<div className="my-4 text-center">
<h2 className="mb-2 text-xl font-semibold">{title}</h2>

@ -83,7 +83,7 @@ type ListUserAiCoursesResponse = {
export function listUserAiCoursesOptions(
params: ListUserAiCoursesQuery = {
perPage: '10',
perPage: '21',
currPage: '1',
query: '',
},
@ -117,7 +117,7 @@ type ListFeaturedAiCoursesResponse = {
export function listFeaturedAiCoursesOptions(
params: ListFeaturedAiCoursesQuery = {
perPage: '10',
perPage: '21',
currPage: '1',
},
) {
@ -137,6 +137,7 @@ type ListExploreAiCoursesParams = {};
export type ListExploreAiCoursesQuery = {
perPage?: string;
currPage?: string;
query?: string;
};
type ListExploreAiCoursesResponse = {
@ -151,6 +152,7 @@ export function listExploreAiCoursesOptions(
params: ListExploreAiCoursesQuery = {
perPage: '21',
currPage: '1',
query: '',
},
) {
return {

Loading…
Cancel
Save