Add course regeneration

refactor/ai-courses
Kamran Ahmed 1 month ago
parent 3b01fb209e
commit f72d4ddc40
  1. 2
      src/components/GenerateCourse/AICourseContent.tsx
  2. 13
      src/components/GenerateCourse/GenerateAICourse.tsx
  3. 3
      src/components/GenerateCourse/GetAICourse.tsx
  4. 69
      src/components/GenerateCourse/ModifyCoursePrompt.tsx
  5. 27
      src/components/GenerateCourse/RegenerateOutline.tsx
  6. 7
      src/helper/generate-ai-course.ts

@ -27,7 +27,7 @@ type AICourseContentProps = {
course: AiCourse;
isLoading: boolean;
error?: string;
onRegenerateOutline: () => void;
onRegenerateOutline: (prompt?: string) => void;
};
export function AICourseContent(props: AICourseContentProps) {

@ -43,8 +43,9 @@ export function GenerateAICourse(props: GenerateAICourseProps) {
term: string;
difficulty: string;
isForce?: boolean;
prompt?: string;
}) => {
const { term, difficulty, isForce } = options;
const { term, difficulty, isForce, prompt } = options;
if (!isLoggedIn()) {
window.location.href = '/ai-tutor';
@ -61,6 +62,7 @@ export function GenerateAICourse(props: GenerateAICourseProps) {
onLoadingChange: setIsLoading,
onError: setError,
isForce,
prompt,
});
};
@ -95,8 +97,13 @@ export function GenerateAICourse(props: GenerateAICourseProps) {
course={course}
isLoading={isLoading}
error={error}
onRegenerateOutline={() => {
handleGenerateCourse({ term, difficulty, isForce: true });
onRegenerateOutline={(prompt) => {
handleGenerateCourse({
term,
difficulty,
isForce: true,
prompt,
});
}}
/>
);

@ -58,7 +58,7 @@ export function GetAICourse(props: GetAICourseProps) {
setError(queryError.message);
}, [queryError]);
const handleRegenerateCourse = async () => {
const handleRegenerateCourse = async (prompt?: string) => {
if (!aiCourse) {
return;
}
@ -67,6 +67,7 @@ export function GetAICourse(props: GetAICourseProps) {
term: aiCourse.keyword,
difficulty: aiCourse.difficulty,
slug: courseSlug,
prompt,
onCourseChange: (course, rawData) => {
queryClient.setQueryData(
getAiCourseOptions({ aiCourseSlug: courseSlug }).queryKey,

@ -0,0 +1,69 @@
import { useState } from 'react';
import { Modal } from '../Modal';
export type ModifyCoursePromptProps = {
onClose: () => void;
onSubmit: (prompt: string) => void;
};
export function ModifyCoursePrompt(props: ModifyCoursePromptProps) {
const { onClose, onSubmit } = props;
const [prompt, setPrompt] = useState('');
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
onSubmit(prompt);
};
return (
<Modal
onClose={onClose}
wrapperClassName="rounded-xl max-w-xl w-full h-auto"
bodyClassName="p-6"
overlayClassName="items-start md:items-center"
>
<div className="flex flex-col gap-4">
<div>
<h2 className="mb-2 text-left text-xl font-semibold">
Give AI more context
</h2>
<p className="text-sm text-gray-500">
Pass additional information to the AI to generate a course outline.
</p>
</div>
<form className="flex flex-col gap-2" onSubmit={handleSubmit}>
<textarea
id="prompt"
autoFocus
rows={3}
value={prompt}
onChange={(e) => setPrompt(e.target.value)}
className="w-full rounded-md border border-gray-200 p-2 placeholder:text-sm focus:outline-black"
placeholder="e.g. make sure to add a section on React hooks"
/>
<p className="text-sm text-gray-500">
Complete the sentence: "I want AI to..."
</p>
<div className="flex justify-end gap-2">
<button
className="rounded-md bg-gray-200 px-4 py-2.5 text-sm text-black hover:opacity-80"
onClick={onClose}
>
Cancel
</button>
<button
type="submit"
disabled={!prompt.trim()}
className="rounded-md bg-black px-4 py-2.5 text-sm text-white hover:opacity-80 disabled:opacity-50"
>
Modify Prompt
</button>
</div>
</form>
</div>
</Modal>
);
}

@ -4,9 +4,10 @@ import { useOutsideClick } from '../../hooks/use-outside-click';
import { cn } from '../../lib/classname';
import { useIsPaidUser } from '../../queries/billing';
import { UpgradeAccountModal } from '../Billing/UpgradeAccountModal';
import { ModifyCoursePrompt } from './ModifyCoursePrompt';
type RegenerateOutlineProps = {
onRegenerateOutline: () => void;
onRegenerateOutline: (prompt?: string) => void;
};
export function RegenerateOutline(props: RegenerateOutlineProps) {
@ -14,6 +15,8 @@ export function RegenerateOutline(props: RegenerateOutlineProps) {
const [isDropdownVisible, setIsDropdownVisible] = useState(false);
const [showUpgradeModal, setShowUpgradeModal] = useState(false);
const [showPromptModal, setShowPromptModal] = useState(false);
const ref = useRef<HTMLDivElement>(null);
const { isPaidUser } = useIsPaidUser();
@ -30,6 +33,16 @@ export function RegenerateOutline(props: RegenerateOutlineProps) {
/>
)}
{showPromptModal && (
<ModifyCoursePrompt
onClose={() => setShowPromptModal(false)}
onSubmit={(prompt) => {
setShowPromptModal(false);
onRegenerateOutline(prompt);
}}
/>
)}
<div className="absolute right-3 top-3" ref={ref}>
<button
className={cn('text-gray-400 hover:text-black', {
@ -59,7 +72,17 @@ export function RegenerateOutline(props: RegenerateOutlineProps) {
/>
Regenerate
</button>
<button className="flex w-full items-center gap-2.5 px-3 py-2 text-left text-sm text-gray-600 hover:bg-gray-100">
<button
onClick={() => {
setIsDropdownVisible(false);
if (!isPaidUser) {
setShowUpgradeModal(true);
} else {
setShowPromptModal(true);
}
}}
className="flex w-full items-center gap-2.5 px-3 py-2 text-left text-sm text-gray-600 hover:bg-gray-100"
>
<PenSquare
size={16}
className="text-gray-400"

@ -11,6 +11,7 @@ type GenerateCourseOptions = {
difficulty: string;
slug?: string;
isForce?: boolean;
prompt?: string;
onCourseIdChange?: (courseId: string) => void;
onCourseSlugChange?: (courseSlug: string) => void;
onCourseChange?: (course: AiCourse, rawData: string) => void;
@ -29,6 +30,7 @@ export async function generateCourse(options: GenerateCourseOptions) {
onLoadingChange,
onError,
isForce = false,
prompt,
} = options;
onLoadingChange?.(true);
@ -54,6 +56,10 @@ export async function generateCourse(options: GenerateCourseOptions) {
'Content-Type': 'application/json',
},
credentials: 'include',
body: JSON.stringify({
isForce,
customPrompt: prompt,
}),
},
);
} else {
@ -68,6 +74,7 @@ export async function generateCourse(options: GenerateCourseOptions) {
keyword: term,
difficulty,
isForce,
customPrompt: prompt,
}),
credentials: 'include',
},

Loading…
Cancel
Save