feat/topic-chat
Arik Chakma 2 weeks ago
parent 5f7fafc86a
commit 8ae8688996
  1. 16
      src/components/GenerateCourse/GenerateAICourse.tsx
  2. 2
      src/components/TopicDetail/TopicDetail.tsx
  3. 52
      src/components/TopicDetail/TopicDetailAI.tsx
  4. 3
      src/helper/generate-ai-course.ts

@ -54,6 +54,7 @@ export function GenerateAICourse(props: GenerateAICourseProps) {
const params = getUrlParams(); const params = getUrlParams();
const paramsTerm = params?.term; const paramsTerm = params?.term;
const paramsDifficulty = params?.difficulty; const paramsDifficulty = params?.difficulty;
const paramsSrc = params?.src || 'search';
if (!paramsTerm || !paramsDifficulty) { if (!paramsTerm || !paramsDifficulty) {
return; return;
} }
@ -87,6 +88,7 @@ export function GenerateAICourse(props: GenerateAICourseProps) {
instructions: paramsCustomInstructions, instructions: paramsCustomInstructions,
goal: paramsGoal, goal: paramsGoal,
about: paramsAbout, about: paramsAbout,
src: paramsSrc,
}); });
}, [term, difficulty]); }, [term, difficulty]);
@ -98,9 +100,18 @@ export function GenerateAICourse(props: GenerateAICourseProps) {
about?: string; about?: string;
isForce?: boolean; isForce?: boolean;
prompt?: string; prompt?: string;
src?: string;
}) => { }) => {
const { term, difficulty, isForce, prompt, instructions, goal, about } = const {
options; term,
difficulty,
isForce,
prompt,
instructions,
goal,
about,
src,
} = options;
if (!isLoggedIn()) { if (!isLoggedIn()) {
window.location.href = '/ai'; window.location.href = '/ai';
@ -121,6 +132,7 @@ export function GenerateAICourse(props: GenerateAICourseProps) {
about, about,
isForce, isForce,
prompt, prompt,
src,
}); });
}; };

@ -70,7 +70,7 @@ type PaidResourceType = {
const paidResourcesCache: Record<string, PaidResourceType[]> = {}; const paidResourcesCache: Record<string, PaidResourceType[]> = {};
const defaultChatHistory: AIChatHistoryType[] = [ export const defaultChatHistory: AIChatHistoryType[] = [
{ {
role: 'assistant', role: 'assistant',
content: 'Hey, I am your AI instructor. How can I help you today? 🤖', content: 'Hey, I am your AI instructor. How can I help you today? 🤖',

@ -12,7 +12,13 @@ import { billingDetailsOptions } from '../../queries/billing';
import { getAiCourseLimitOptions } from '../../queries/ai-course'; import { getAiCourseLimitOptions } from '../../queries/ai-course';
import { queryClient } from '../../stores/query-client'; import { queryClient } from '../../stores/query-client';
import { isLoggedIn, removeAuthToken } from '../../lib/jwt'; import { isLoggedIn, removeAuthToken } from '../../lib/jwt';
import { BotIcon, Loader2Icon, LockIcon, SendIcon } from 'lucide-react'; import {
BotIcon,
Loader2Icon,
LockIcon,
RotateCcwIcon,
SendIcon,
} from 'lucide-react';
import { showLoginPopup } from '../../lib/popup'; import { showLoginPopup } from '../../lib/popup';
import { cn } from '../../lib/classname'; import { cn } from '../../lib/classname';
import TextareaAutosize from 'react-textarea-autosize'; import TextareaAutosize from 'react-textarea-autosize';
@ -27,6 +33,7 @@ import { markdownToHtmlWithHighlighting } from '../../lib/markdown';
import type { ResourceType } from '../../lib/resource-progress'; import type { ResourceType } from '../../lib/resource-progress';
import { getPercentage } from '../../lib/number'; import { getPercentage } from '../../lib/number';
import { roadmapTreeMappingOptions } from '../../queries/roadmap-tree'; import { roadmapTreeMappingOptions } from '../../queries/roadmap-tree';
import { defaultChatHistory } from './TopicDetail';
type TopicDetailAIProps = { type TopicDetailAIProps = {
resourceId: string; resourceId: string;
@ -217,9 +224,16 @@ export function TopicDetailAI(props: TopicDetailAIProps) {
); );
const hasSubjects = const hasSubjects =
roadmapTreeMapping?.subjects && roadmapTreeMapping?.subjects?.length > 0; roadmapTreeMapping?.subjects && roadmapTreeMapping?.subjects?.length > 0;
const hasChatHistory = aiChatHistory.length > 1;
return ( return (
<div className="mt-4 flex grow flex-col overflow-hidden rounded-lg border border-gray-200"> <div className="relative mt-4 flex grow flex-col overflow-hidden rounded-lg border border-gray-200">
{isDataLoading && (
<div className="absolute inset-0 z-20 flex items-center justify-center gap-2 bg-white text-black">
<Loader2Icon className="size-8 animate-spin stroke-3 text-gray-500" />
</div>
)}
{hasSubjects && ( {hasSubjects && (
<div className="border-b border-gray-200 px-4 py-2"> <div className="border-b border-gray-200 px-4 py-2">
<h4 className="flex items-center gap-2 text-base"> <h4 className="flex items-center gap-2 text-base">
@ -232,7 +246,7 @@ export function TopicDetailAI(props: TopicDetailAIProps) {
<a <a
key={subject} key={subject}
target="_blank" target="_blank"
href={`/ai/search?term=${subject}&difficulty=beginner`} href={`/ai/search?term=${subject}&difficulty=beginner&src=topic`}
className="rounded-md border px-1.5" className="rounded-md border px-1.5"
> >
{subject} {subject}
@ -265,10 +279,33 @@ export function TopicDetailAI(props: TopicDetailAIProps) {
</h4> </h4>
)} )}
{!isDataLoading && !isPaidUser && ( {!isDataLoading && (
<p className="text-sm text-gray-500"> <div className="flex items-center gap-2.5">
<span className="font-medium">{usagePercentage}%</span> used {hasChatHistory && (
</p> <button
className="rounded-md bg-white p-1 text-xs font-medium text-black hover:bg-gray-200"
onClick={() => {
setAiChatHistory(defaultChatHistory);
}}
>
<RotateCcwIcon className="size-3.5" />
</button>
)}
{!isPaidUser && (
<>
<button
className="underline underline-offset-2 hover:no-underline"
onClick={onUpgrade}
>
Upgrade
</button>
<p className="text-sm text-gray-500">
<span className="font-medium">{usagePercentage}%</span> used
</p>
</>
)}
</div>
)} )}
</div> </div>
@ -349,7 +386,6 @@ export function TopicDetailAI(props: TopicDetailAIProps) {
<TextareaAutosize <TextareaAutosize
className={cn( className={cn(
'h-full min-h-[41px] grow resize-none bg-transparent px-4 py-2 focus:outline-hidden', 'h-full min-h-[41px] grow resize-none bg-transparent px-4 py-2 focus:outline-hidden',
// isDisabled ? 'cursor-not-allowed opacity-50' : 'cursor-auto',
)} )}
placeholder="Ask AI anything about the lesson..." placeholder="Ask AI anything about the lesson..."
value={message} value={message}

@ -20,6 +20,7 @@ type GenerateCourseOptions = {
onCourseChange?: (course: AiCourse, rawData: string) => void; onCourseChange?: (course: AiCourse, rawData: string) => void;
onLoadingChange?: (isLoading: boolean) => void; onLoadingChange?: (isLoading: boolean) => void;
onError?: (error: string) => void; onError?: (error: string) => void;
src?: string;
}; };
export async function generateCourse(options: GenerateCourseOptions) { export async function generateCourse(options: GenerateCourseOptions) {
@ -37,6 +38,7 @@ export async function generateCourse(options: GenerateCourseOptions) {
instructions, instructions,
goal, goal,
about, about,
src = 'search',
} = options; } = options;
onLoadingChange?.(true); onLoadingChange?.(true);
@ -85,6 +87,7 @@ export async function generateCourse(options: GenerateCourseOptions) {
instructions, instructions,
goal, goal,
about, about,
src,
}), }),
credentials: 'include', credentials: 'include',
}, },

Loading…
Cancel
Save