feat/chat
Arik Chakma 2 months ago
parent 8cbf0737b7
commit 1323975b28
  1. 13
      src/components/GenerateCourse/AICourseContent.tsx
  2. 19
      src/components/GenerateCourse/AICourseFooter.tsx
  3. 34
      src/components/GenerateCourse/AICourseLesson.tsx
  4. 33
      src/components/GenerateCourse/AICourseLessonChat.tsx

@ -19,6 +19,7 @@ import { AICourseSidebarModuleList } from './AICourseSidebarModuleList';
import { AILimitsPopup } from './AILimitsPopup';
import { AICourseOutlineView } from './AICourseOutlineView';
import { AICourseRoadmapView } from './AICourseRoadmapView';
import { AICourseFooter } from './AICourseFooter';
type AICourseContentProps = {
courseSlug?: string;
@ -35,6 +36,7 @@ export function AICourseContent(props: AICourseContentProps) {
const [showUpgradeModal, setShowUpgradeModal] = useState(false);
const [showAILimitsPopup, setShowAILimitsPopup] = useState(false);
const [isAIChatsOpen, setIsAIChatsOpen] = useState(true);
const [activeModuleIndex, setActiveModuleIndex] = useState(0);
const [activeLessonIndex, setActiveLessonIndex] = useState(0);
@ -412,6 +414,8 @@ export function AICourseContent(props: AICourseContentProps) {
onGoToNextLesson={goToNextLesson}
key={`${courseSlug}-${activeModuleIndex}-${activeLessonIndex}`}
onUpgrade={() => setShowUpgradeModal(true)}
isAIChatsOpen={isAIChatsOpen}
setIsAIChatsOpen={setIsAIChatsOpen}
/>
)}
@ -445,14 +449,7 @@ export function AICourseContent(props: AICourseContentProps) {
/>
)}
<div
className={cn(
'mx-auto mb-10 mt-5 text-center text-sm text-gray-400',
viewMode === 'module' ? 'hidden' : '',
)}
>
AI can make mistakes, check important info.
</div>
<AICourseFooter className={viewMode === 'module' ? 'hidden' : ''} />
</main>
</div>

@ -0,0 +1,19 @@
import { cn } from '../../lib/classname';
type AICourseFooterProps = {
className?: string;
};
export function AICourseFooter(props: AICourseFooterProps) {
const { className } = props;
return (
<div
className={cn(
'mx-auto mb-10 mt-5 text-center text-sm text-gray-400',
className,
)}
>
AI can make mistakes, check important info.
</div>
);
}

@ -5,6 +5,8 @@ import {
ChevronRight,
Loader2Icon,
LockIcon,
MessageCircleIcon,
MessageCircleOffIcon,
XIcon,
} from 'lucide-react';
import { useEffect, useMemo, useState } from 'react';
@ -28,6 +30,7 @@ import './AICourseLessonChat.css';
import { RegenerateLesson } from './RegenerateLesson';
import { TestMyKnowledgeAction } from './TestMyKnowledgeAction';
import { AICourseLessonChat } from './AICourseLessonChat';
import { AICourseFooter } from './AICourseFooter';
type AICourseLessonProps = {
courseSlug: string;
@ -44,6 +47,9 @@ type AICourseLessonProps = {
onGoToNextLesson: () => void;
onUpgrade: () => void;
isAIChatsOpen: boolean;
setIsAIChatsOpen: (isAIChatsOpen: boolean) => void;
};
export function AICourseLesson(props: AICourseLessonProps) {
@ -62,6 +68,9 @@ export function AICourseLesson(props: AICourseLessonProps) {
onGoToNextLesson,
onUpgrade,
isAIChatsOpen,
setIsAIChatsOpen,
} = props;
const [isLoading, setIsLoading] = useState(true);
@ -210,7 +219,12 @@ export function AICourseLesson(props: AICourseLessonProps) {
return (
<div className="grid h-full grid-cols-5">
<div className="relative col-span-3">
<div
className={cn(
'relative',
isAIChatsOpen ? 'col-span-3 max-lg:col-span-5' : 'col-span-5',
)}
>
<div className="absolute inset-0 overflow-y-auto bg-white p-8 pb-0 max-lg:px-4 max-lg:pt-3">
{(isGenerating || isLoading) && (
<div className="absolute right-6 top-6 flex items-center justify-center">
@ -269,6 +283,17 @@ export function AICourseLesson(props: AICourseLessonProps) {
</>
)}
</button>
<button
onClick={() => setIsAIChatsOpen(!isAIChatsOpen)}
className="rounded-full p-1 text-gray-400 hover:text-black max-lg:hidden"
>
{!isAIChatsOpen ? (
<MessageCircleIcon className="size-4 stroke-[2.5]" />
) : (
<MessageCircleOffIcon className="size-4 stroke-[2.5]" />
)}
</button>
</div>
)}
</div>
@ -390,19 +415,20 @@ export function AICourseLesson(props: AICourseLessonProps) {
</div>
</div>
<div className="mx-auto mb-10 mt-5 text-center text-sm text-gray-400">
AI can make mistakes, check important info.
</div>
<AICourseFooter />
</div>
</div>
{isAIChatsOpen && (
<AICourseLessonChat
courseSlug={courseSlug}
moduleTitle={currentModuleTitle}
lessonTitle={currentLessonTitle}
onUpgradeClick={onUpgrade}
isDisabled={isGenerating || isLoading || isTogglingDone}
onClose={() => setIsAIChatsOpen(false)}
/>
)}
</div>
);
}

@ -6,6 +6,7 @@ import {
HelpCircle,
LockIcon,
Send,
XIcon,
} from 'lucide-react';
import {
useCallback,
@ -44,13 +45,22 @@ type AICourseLessonChatProps = {
lessonTitle: string;
onUpgradeClick: () => void;
isDisabled?: boolean;
onClose: () => void;
};
export function AICourseLessonChat(props: AICourseLessonChatProps) {
const { courseSlug, moduleTitle, lessonTitle, onUpgradeClick, isDisabled } =
props;
const {
courseSlug,
moduleTitle,
lessonTitle,
onUpgradeClick,
isDisabled,
onClose,
} = props;
const toast = useToast();
const containerRef = useRef<HTMLDivElement | null>(null);
const scrollareaRef = useRef<HTMLDivElement | null>(null);
const [courseAIChatHistory, setCourseAIChatHistory] = useState<
@ -193,9 +203,24 @@ export function AICourseLessonChat(props: AICourseLessonChatProps) {
scrollToBottom();
}, []);
useOutsideClick(containerRef, () => {
onClose();
});
return (
<div className="relative col-span-2 h-full border-l border-gray-200">
<div className="absolute inset-0 flex w-full flex-col overflow-hidden bg-white">
<div className="relative col-span-2 h-full border-l border-gray-200 max-lg:fixed max-lg:inset-y-0 max-lg:right-0 max-lg:z-10 max-lg:w-[420px] max-lg:border-none">
<div className="fixed inset-0 z-10 bg-black/50 lg:hidden" />
<div
className="absolute inset-0 z-20 flex w-full flex-col overflow-hidden bg-white"
ref={containerRef}
>
<button
onClick={onClose}
className="absolute right-2 top-2 hidden rounded-full p-1 text-gray-400 hover:text-black max-lg:block"
>
<XIcon className="size-4 stroke-[2.5]" />
</button>
<div className="flex items-center justify-between gap-2 border-b border-gray-200 px-4 py-2 text-sm">
<h4 className="text-base font-medium">Course AI</h4>
</div>

Loading…
Cancel
Save