From 8cbf0737b78037a5782fdbed801ef124ab62da1f Mon Sep 17 00:00:00 2001 From: Arik Chakma Date: Fri, 28 Mar 2025 01:14:46 +0600 Subject: [PATCH] wip: remove old code --- .../GenerateCourse/AICourseFollowUp.tsx | 74 ---- .../AICourseFollowUpPopover.tsx | 399 ------------------ .../GenerateCourse/AICourseLesson.tsx | 3 +- ...rseFollowUp.css => AICourseLessonChat.css} | 0 4 files changed, 1 insertion(+), 475 deletions(-) delete mode 100644 src/components/GenerateCourse/AICourseFollowUp.tsx delete mode 100644 src/components/GenerateCourse/AICourseFollowUpPopover.tsx rename src/components/GenerateCourse/{AICourseFollowUp.css => AICourseLessonChat.css} (100%) diff --git a/src/components/GenerateCourse/AICourseFollowUp.tsx b/src/components/GenerateCourse/AICourseFollowUp.tsx deleted file mode 100644 index 917d1d6f9..000000000 --- a/src/components/GenerateCourse/AICourseFollowUp.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import { ArrowRightIcon, BotIcon } from 'lucide-react'; -import { useState } from 'react'; -import { - AICourseFollowUpPopover, - type AIChatHistoryType, -} from './AICourseFollowUpPopover'; -import { UpgradeAccountModal } from '../Billing/UpgradeAccountModal'; - -type AICourseFollowUpProps = { - courseSlug: string; - moduleTitle: string; - lessonTitle: string; -}; - -export function AICourseFollowUp(props: AICourseFollowUpProps) { - const { courseSlug, moduleTitle, lessonTitle } = props; - - const [isOpen, setIsOpen] = useState(false); - const [showUpgradeModal, setShowUpgradeModal] = useState(false); - - const [courseAIChatHistory, setCourseAIChatHistory] = useState< - AIChatHistoryType[] - >([ - { - role: 'assistant', - content: - 'Hey, I am your AI instructor. Here are some examples of what you can ask me about 🤖', - isDefault: true, - }, - ]); - - return ( -
- - - {showUpgradeModal && ( - setShowUpgradeModal(false)} /> - )} - - {isOpen && ( - { - setIsOpen(false); - setShowUpgradeModal(true); - }} - onOutsideClick={() => { - if (!isOpen) { - return; - } - - setIsOpen(false); - }} - /> - )} - - {isOpen && ( -
- )} -
- ); -} diff --git a/src/components/GenerateCourse/AICourseFollowUpPopover.tsx b/src/components/GenerateCourse/AICourseFollowUpPopover.tsx deleted file mode 100644 index 35b49051f..000000000 --- a/src/components/GenerateCourse/AICourseFollowUpPopover.tsx +++ /dev/null @@ -1,399 +0,0 @@ -import { useQuery } from '@tanstack/react-query'; -import { - BookOpen, - Bot, - Hammer, - HelpCircle, - LockIcon, - Send, -} from 'lucide-react'; -import { useEffect, useMemo, useRef, useState, type FormEvent } from 'react'; -import { flushSync } from 'react-dom'; -import TextareaAutosize from 'react-textarea-autosize'; -import { useOutsideClick } from '../../hooks/use-outside-click'; -import { useToast } from '../../hooks/use-toast'; -import { readStream } from '../../lib/ai'; -import { cn } from '../../lib/classname'; -import { isLoggedIn, removeAuthToken } from '../../lib/jwt'; -import { - markdownToHtml, - markdownToHtmlWithHighlighting, -} from '../../lib/markdown'; -import { getAiCourseLimitOptions } from '../../queries/ai-course'; -import { queryClient } from '../../stores/query-client'; -import { billingDetailsOptions } from '../../queries/billing'; - -export type AllowedAIChatRole = 'user' | 'assistant'; -export type AIChatHistoryType = { - role: AllowedAIChatRole; - content: string; - isDefault?: boolean; - html?: string; -}; - -type AICourseFollowUpPopoverProps = { - courseSlug: string; - moduleTitle: string; - lessonTitle: string; - - courseAIChatHistory: AIChatHistoryType[]; - setCourseAIChatHistory: (value: AIChatHistoryType[]) => void; - - onOutsideClick?: () => void; - onUpgradeClick: () => void; -}; - -export function AICourseFollowUpPopover(props: AICourseFollowUpPopoverProps) { - const { - courseSlug, - moduleTitle, - lessonTitle, - onOutsideClick, - onUpgradeClick, - - courseAIChatHistory, - setCourseAIChatHistory, - } = props; - - const toast = useToast(); - const containerRef = useRef(null); - const scrollareaRef = useRef(null); - - const [isStreamingMessage, setIsStreamingMessage] = useState(false); - const [message, setMessage] = useState(''); - const [streamedMessage, setStreamedMessage] = useState(''); - - useOutsideClick(containerRef, onOutsideClick); - - const { data: tokenUsage, isLoading } = useQuery( - getAiCourseLimitOptions(), - queryClient, - ); - - const { data: userBillingDetails, isLoading: isBillingDetailsLoading } = - useQuery(billingDetailsOptions(), queryClient); - - const isLimitExceeded = (tokenUsage?.used || 0) >= (tokenUsage?.limit || 0); - const isPaidUser = userBillingDetails?.status === 'active'; - - const handleChatSubmit = (e: FormEvent) => { - e.preventDefault(); - - const trimmedMessage = message.trim(); - if ( - !trimmedMessage || - isStreamingMessage || - !isLoggedIn() || - isLimitExceeded || - isLoading - ) { - return; - } - - const newMessages: AIChatHistoryType[] = [ - ...courseAIChatHistory, - { - role: 'user', - content: trimmedMessage, - }, - ]; - - flushSync(() => { - setCourseAIChatHistory(newMessages); - setMessage(''); - }); - - scrollToBottom(); - completeCourseAIChat(newMessages); - }; - - const scrollToBottom = () => { - scrollareaRef.current?.scrollTo({ - top: scrollareaRef.current.scrollHeight, - behavior: 'smooth', - }); - }; - - const completeCourseAIChat = async (messages: AIChatHistoryType[]) => { - setIsStreamingMessage(true); - - const response = await fetch( - `${import.meta.env.PUBLIC_API_URL}/v1-follow-up-ai-course/${courseSlug}`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - credentials: 'include', - body: JSON.stringify({ - moduleTitle, - lessonTitle, - messages: messages.slice(-10), - }), - }, - ); - - if (!response.ok) { - const data = await response.json(); - - toast.error(data?.message || 'Something went wrong'); - setCourseAIChatHistory([...messages].slice(0, messages.length - 1)); - setIsStreamingMessage(false); - - if (data.status === 401) { - removeAuthToken(); - window.location.reload(); - } - } - - const reader = response.body?.getReader(); - - if (!reader) { - setIsStreamingMessage(false); - toast.error('Something went wrong'); - return; - } - - await readStream(reader, { - onStream: async (content) => { - flushSync(() => { - setStreamedMessage(content); - }); - - scrollToBottom(); - }, - onStreamEnd: async (content) => { - const newMessages: AIChatHistoryType[] = [ - ...messages, - { - role: 'assistant', - content, - html: await markdownToHtmlWithHighlighting(content), - }, - ]; - - flushSync(() => { - setStreamedMessage(''); - setIsStreamingMessage(false); - setCourseAIChatHistory(newMessages); - }); - - queryClient.invalidateQueries(getAiCourseLimitOptions()); - scrollToBottom(); - }, - }); - - setIsStreamingMessage(false); - }; - - useEffect(() => { - scrollToBottom(); - }, []); - - return ( -
-
-

Course AI

-
- -
-
-
-
- {courseAIChatHistory.map((chat, index) => { - return ( - <> - - - {chat.isDefault && ( -
-
- {capabilities.map((capability, index) => ( - - ))} -
-
- )} - - ); - })} - - {isStreamingMessage && !streamedMessage && ( - - )} - - {streamedMessage && ( - - )} -
-
-
-
- -
- {isLimitExceeded && ( -
- -

- Limit reached for today - {isPaidUser ? '. Please wait until tomorrow.' : ''} -

- {!isPaidUser && ( - - )} -
- )} - setMessage(e.target.value)} - autoFocus={true} - onKeyDown={(e) => { - if (e.key === 'Enter' && !e.shiftKey) { - handleChatSubmit(e as unknown as FormEvent); - } - }} - /> - - -
- ); -} - -type AIChatCardProps = { - role: AllowedAIChatRole; - content: string; - html?: string; -}; - -function AIChatCard(props: AIChatCardProps) { - const { role, content, html: defaultHtml } = props; - - const html = useMemo(() => { - if (defaultHtml) { - return defaultHtml; - } - - return markdownToHtml(content, false); - }, [content, defaultHtml]); - - return ( -
-
-
- -
-
-
-
- ); -} - -type CapabilityCardProps = { - icon: React.ReactNode; - title: string; - description: string; - className?: string; -}; - -function CapabilityCard({ - icon, - title, - description, - className, -}: CapabilityCardProps) { - return ( -
-
- {icon} - - {title} - -
-

{description}

-
- ); -} - -const capabilities = [ - { - icon: ( - - ), - title: 'Clarify Concepts', - description: "If you don't understand a concept, ask me to clarify it", - }, - { - icon: ( - - ), - title: 'More Details', - description: 'Get deeper insights about topics covered in the lesson', - }, - { - icon: ( - - ), - title: 'Real-world Examples', - description: 'Ask for real-world examples to understand better', - }, - { - icon: , - title: 'Best Practices', - description: 'Learn about best practices and common pitfalls', - }, -] as const; diff --git a/src/components/GenerateCourse/AICourseLesson.tsx b/src/components/GenerateCourse/AICourseLesson.tsx index 6310c04b3..971b35463 100644 --- a/src/components/GenerateCourse/AICourseLesson.tsx +++ b/src/components/GenerateCourse/AICourseLesson.tsx @@ -24,8 +24,7 @@ import { } from '../../queries/ai-course'; import { useIsPaidUser } from '../../queries/billing'; import { queryClient } from '../../stores/query-client'; -import { AICourseFollowUp } from './AICourseFollowUp'; -import './AICourseFollowUp.css'; +import './AICourseLessonChat.css'; import { RegenerateLesson } from './RegenerateLesson'; import { TestMyKnowledgeAction } from './TestMyKnowledgeAction'; import { AICourseLessonChat } from './AICourseLessonChat'; diff --git a/src/components/GenerateCourse/AICourseFollowUp.css b/src/components/GenerateCourse/AICourseLessonChat.css similarity index 100% rename from src/components/GenerateCourse/AICourseFollowUp.css rename to src/components/GenerateCourse/AICourseLessonChat.css