From 7c60b79942d8f8316c3baae4fb97c2edb02cd8ae Mon Sep 17 00:00:00 2001 From: Kamran Ahmed Date: Mon, 10 Mar 2025 16:19:18 +0000 Subject: [PATCH] Update course functionality --- src/components/GenerateCourse/AICourse.tsx | 41 +----- .../GenerateCourse/AICourseCard.tsx | 36 ++--- .../GenerateCourse/AICourseModuleView.tsx | 4 +- .../GenerateCourse/UserCoursesList.tsx | 125 ++++++++++++++++++ 4 files changed, 144 insertions(+), 62 deletions(-) create mode 100644 src/components/GenerateCourse/UserCoursesList.tsx diff --git a/src/components/GenerateCourse/AICourse.tsx b/src/components/GenerateCourse/AICourse.tsx index 785e6ddf6..60289b60d 100644 --- a/src/components/GenerateCourse/AICourse.tsx +++ b/src/components/GenerateCourse/AICourse.tsx @@ -3,10 +3,7 @@ import { useState } from 'react'; import { cn } from '../../lib/classname'; import { isLoggedIn } from '../../lib/jwt'; import { showLoginPopup } from '../../lib/popup'; -import { useQuery } from '@tanstack/react-query'; -import { listUserAiCoursesOptions } from '../../queries/ai-course'; -import { queryClient } from '../../stores/query-client'; -import { AICourseCard } from './AICourseCard'; +import { UserCoursesList } from './UserCoursesList'; export const difficultyLevels = [ 'beginner', @@ -21,11 +18,6 @@ export function AICourse(props: AICourseProps) { const [keyword, setKeyword] = useState(''); const [difficulty, setDifficulty] = useState('beginner'); - const { data: userAiCourses, isLoading: isUserAiCoursesLoading } = useQuery( - listUserAiCoursesOptions(), - queryClient, - ); - const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'Enter' && keyword.trim()) { onSubmit(); @@ -48,12 +40,12 @@ export function AICourse(props: AICourseProps) { AI Course Generator

- Enter a topic below to generate a course on it. + Enter a topic below to generate a course on it

{ e.preventDefault(); onSubmit(); @@ -62,7 +54,7 @@ export function AICourse(props: AICourseProps) {
@@ -80,14 +72,11 @@ export function AICourse(props: AICourseProps) { className="w-full rounded-md border border-gray-300 bg-white p-3 pl-10 text-gray-900 focus:outline-none focus:ring-1 focus:ring-gray-500" maxLength={50} /> - - {keyword.length}/50 -
-
diff --git a/src/components/GenerateCourse/AICourseCard.tsx b/src/components/GenerateCourse/AICourseCard.tsx index f378670a7..3a4a6bd6c 100644 --- a/src/components/GenerateCourse/AICourseCard.tsx +++ b/src/components/GenerateCourse/AICourseCard.tsx @@ -1,6 +1,6 @@ import type { AICourseListItem } from '../../queries/ai-course'; import type { DifficultyLevel } from './AICourse'; -import { BookOpen, Calendar } from 'lucide-react'; +import { BookOpen } from 'lucide-react'; type AICourseCardProps = { course: AICourseListItem; @@ -20,16 +20,10 @@ export function AICourseCard(props: AICourseCardProps) { // Map difficulty to color const difficultyColor = { - beginner: 'bg-green-100 text-green-700', - intermediate: 'bg-blue-100 text-blue-700', - advanced: 'bg-purple-100 text-purple-700', - }[course.difficulty as DifficultyLevel] || 'bg-gray-100 text-gray-700'; - - // Get a short description preview if available - const descriptionPreview = course.data - ? JSON.parse(course.data)?.description?.substring(0, 100) + - (JSON.parse(course.data)?.description?.length > 100 ? '...' : '') - : null; + beginner: 'text-green-700', + intermediate: 'text-blue-700', + advanced: 'text-purple-700', + }[course.difficulty as DifficultyLevel] || 'text-gray-700'; // Calculate progress percentage const totalTopics = course.lessonCount || 0; @@ -40,34 +34,24 @@ export function AICourseCard(props: AICourseCardProps) { return ( -
+
{course.difficulty} - {formattedDate && ( - - - {formattedDate} - - )}
-

+

{course.title}

- {descriptionPreview && ( -

{descriptionPreview}

- )} -
- {totalTopics} topics + {totalTopics} lessons
{totalTopics > 0 && ( diff --git a/src/components/GenerateCourse/AICourseModuleView.tsx b/src/components/GenerateCourse/AICourseModuleView.tsx index c2f3b17a5..34e07a91b 100644 --- a/src/components/GenerateCourse/AICourseModuleView.tsx +++ b/src/components/GenerateCourse/AICourseModuleView.tsx @@ -293,8 +293,8 @@ export function AICourseModuleView(props: AICourseModuleViewProps) { )} {!isLoggedIn() && ( -
- +
+

Please login to generate course content

diff --git a/src/components/GenerateCourse/UserCoursesList.tsx b/src/components/GenerateCourse/UserCoursesList.tsx new file mode 100644 index 000000000..b6f39cd5e --- /dev/null +++ b/src/components/GenerateCourse/UserCoursesList.tsx @@ -0,0 +1,125 @@ +import { useQuery } from '@tanstack/react-query'; +import { listUserAiCoursesOptions } from '../../queries/ai-course'; +import { queryClient } from '../../stores/query-client'; +import { AICourseCard } from './AICourseCard'; +import { useEffect, useState } from 'react'; +import { Loader2, Search, Lock } from 'lucide-react'; +import { isLoggedIn } from '../../lib/jwt'; +import { showLoginPopup } from '../../lib/popup'; + +type UserCoursesListProps = {}; + +export function UserCoursesList(props: UserCoursesListProps) { + const [searchTerm, setSearchTerm] = useState(''); + const [isInitialLoading, setIsInitialLoading] = useState(true); + + const { data: userAiCourses, isFetching: isUserAiCoursesLoading } = useQuery( + listUserAiCoursesOptions(), + queryClient, + ); + + useEffect(() => { + setIsInitialLoading(false); + }, [userAiCourses]); + + const filteredCourses = userAiCourses?.filter((course) => { + if (!searchTerm.trim()) { + return true; + } + + const searchLower = searchTerm.toLowerCase(); + + return ( + course.title.toLowerCase().includes(searchLower) || + course.keyword.toLowerCase().includes(searchLower) + ); + }); + + const isAuthenticated = isLoggedIn(); + + return ( + <> +
+
+

Your Courses

+
+ +
+
+ +
+ setSearchTerm(e.target.value)} + /> +
+
+ + {!isInitialLoading && !isUserAiCoursesLoading && !isAuthenticated && ( +
+ +

+ {' '} + to start generating courses. +

+
+ )} + + {!isUserAiCoursesLoading && + !isInitialLoading && + userAiCourses?.length === 0 && ( +
+

+ You haven't generated any courses yet. +

+
+ )} + + {(isUserAiCoursesLoading || isInitialLoading) && ( +
+ +

Loading...

+
+ )} + + {!isUserAiCoursesLoading && + filteredCourses && + filteredCourses.length > 0 && ( +
+ {filteredCourses.map((course) => ( + + ))} +
+ )} + + {!isUserAiCoursesLoading && + (userAiCourses?.length || 0 > 0) && + filteredCourses?.length === 0 && ( +
+

+ No courses match your search. +

+
+ )} + + ); +}