From fd7f95c1a526c60b9b1dedf8d968186b722114c5 Mon Sep 17 00:00:00 2001 From: Arik Chakma Date: Wed, 19 Mar 2025 00:17:39 +0600 Subject: [PATCH] feat: delete ai course (#8345) * feat: delete ai course * Improve UI --------- Co-authored-by: Kamran Ahmed --- .../GenerateCourse/AICourseActions.tsx | 116 ++++++++++++++++++ .../GenerateCourse/AICourseCard.tsx | 75 ++++++----- 2 files changed, 158 insertions(+), 33 deletions(-) create mode 100644 src/components/GenerateCourse/AICourseActions.tsx diff --git a/src/components/GenerateCourse/AICourseActions.tsx b/src/components/GenerateCourse/AICourseActions.tsx new file mode 100644 index 000000000..a33b219fc --- /dev/null +++ b/src/components/GenerateCourse/AICourseActions.tsx @@ -0,0 +1,116 @@ +import { MoreVertical, Play, Trash2 } from 'lucide-react'; +import { useRef, useState } from 'react'; +import { useOutsideClick } from '../../hooks/use-outside-click'; +import { useKeydown } from '../../hooks/use-keydown'; +import { useToast } from '../../hooks/use-toast'; +import { useMutation } from '@tanstack/react-query'; +import { queryClient } from '../../stores/query-client'; +import { httpDelete } from '../../lib/query-http'; + +type AICourseActionsType = { + courseSlug: string; + onDeleted?: () => void; +}; + +export function AICourseActions(props: AICourseActionsType) { + const { courseSlug, onDeleted } = props; + + const toast = useToast(); + const dropdownRef = useRef(null); + + const [isOpen, setIsOpen] = useState(false); + const [isConfirming, setIsConfirming] = useState(false); + + const { mutate: deleteCourse, isPending: isDeleting } = useMutation( + { + mutationFn: async () => { + return httpDelete(`/v1-delete-ai-course/${courseSlug}`); + }, + onSuccess: () => { + toast.success('Course deleted'); + queryClient.invalidateQueries({ + predicate: (query) => query.queryKey?.[0] === 'user-ai-courses', + }); + onDeleted?.(); + }, + onError: (error) => { + toast.error(error?.message || 'Failed to delete course'); + }, + }, + queryClient, + ); + + useOutsideClick(dropdownRef, () => { + setIsOpen(false); + }); + + useKeydown('Escape', () => { + setIsOpen(false); + }); + + return ( +
+ + + {isOpen && ( +
+ + + Start Course + + {!isConfirming && ( + + )} + + {isConfirming && ( + + Are you sure? +
+ + +
+
+ )} +
+ )} +
+ ); +} diff --git a/src/components/GenerateCourse/AICourseCard.tsx b/src/components/GenerateCourse/AICourseCard.tsx index 329969ade..5528232c9 100644 --- a/src/components/GenerateCourse/AICourseCard.tsx +++ b/src/components/GenerateCourse/AICourseCard.tsx @@ -1,6 +1,7 @@ import type { AICourseWithLessonCount } from '../../queries/ai-course'; import type { DifficultyLevel } from './AICourse'; import { BookOpen } from 'lucide-react'; +import { AICourseActions } from './AICourseActions'; type AICourseCardProps = { course: AICourseWithLessonCount; @@ -32,42 +33,50 @@ export function AICourseCard(props: AICourseCardProps) { totalTopics > 0 ? Math.round((completedTopics / totalTopics) * 100) : 0; return ( - -
- - {course.difficulty} - -
+
+ +
+ + {course.difficulty} + +
-

- {course.title} -

+

+ {course.title} +

-
-
- - {totalTopics} lessons -
+