mirror of
https://github.com/kamranahmedse/developer-roadmap.git
synced 2025-09-03 06:12:53 +02:00
wip
This commit is contained in:
@@ -5,10 +5,19 @@ import {
|
||||
SparklesIcon,
|
||||
type LucideIcon,
|
||||
} from 'lucide-react';
|
||||
import { useId, useState } from 'react';
|
||||
import { useEffect, useId, useState, type FormEvent } from 'react';
|
||||
import { FormatItem } from './FormatItem';
|
||||
import { GuideOptions } from './GuideOptions';
|
||||
import { FineTuneCourse } from '../GenerateCourse/FineTuneCourse';
|
||||
import { CourseOptions } from './CourseOptions';
|
||||
import {
|
||||
clearFineTuneData,
|
||||
getCourseFineTuneData,
|
||||
getLastSessionId,
|
||||
storeFineTuneData,
|
||||
} from '../../lib/ai';
|
||||
import { isLoggedIn } from '../../lib/jwt';
|
||||
import { showLoginPopup } from '../../lib/popup';
|
||||
|
||||
const allowedFormats = ['course', 'guide', 'roadmap'] as const;
|
||||
type AllowedFormat = (typeof allowedFormats)[number];
|
||||
@@ -19,6 +28,8 @@ export function ContentGenerator() {
|
||||
|
||||
// guide options
|
||||
const [depth, setDepth] = useState('essentials');
|
||||
// course options
|
||||
const [difficulty, setDifficulty] = useState('beginner');
|
||||
|
||||
// fine-tune options
|
||||
const [showFineTuneOptions, setShowFineTuneOptions] = useState(false);
|
||||
@@ -51,8 +62,59 @@ export function ContentGenerator() {
|
||||
},
|
||||
];
|
||||
|
||||
const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
if (!isLoggedIn()) {
|
||||
showLoginPopup();
|
||||
return;
|
||||
}
|
||||
|
||||
let sessionId = '';
|
||||
if (showFineTuneOptions) {
|
||||
clearFineTuneData();
|
||||
sessionId = storeFineTuneData({
|
||||
about,
|
||||
goal,
|
||||
customInstructions,
|
||||
});
|
||||
}
|
||||
|
||||
if (selectedFormat === 'course') {
|
||||
window.location.href = `/ai/course?term=${encodeURIComponent(title)}&difficulty=${difficulty}&id=${sessionId}&format=${selectedFormat}`;
|
||||
} else if (selectedFormat === 'guide') {
|
||||
window.location.href = `/ai/guide?term=${encodeURIComponent(title)}&depth=${depth}&id=${sessionId}&format=${selectedFormat}`;
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
window?.fireEvent({
|
||||
action: 'tutor_user',
|
||||
category: 'ai_tutor',
|
||||
label: 'Visited AI Course Page',
|
||||
});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const lastSessionId = getLastSessionId();
|
||||
if (!lastSessionId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const fineTuneData = getCourseFineTuneData(lastSessionId);
|
||||
if (!fineTuneData) {
|
||||
return;
|
||||
}
|
||||
|
||||
setAbout(fineTuneData.about);
|
||||
setGoal(fineTuneData.goal);
|
||||
setCustomInstructions(fineTuneData.customInstructions);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<form className="mx-auto mt-20 w-full max-w-md space-y-4 rounded-xl bg-white p-4">
|
||||
<form
|
||||
className="mx-auto mt-20 w-full max-w-md space-y-4 rounded-xl bg-white p-4"
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
<div className="flex flex-col gap-2">
|
||||
<label
|
||||
htmlFor={titleFieldId}
|
||||
@@ -67,6 +129,8 @@ export function ContentGenerator() {
|
||||
value={title}
|
||||
onChange={(e) => setTitle(e.target.value)}
|
||||
className="block h-9 w-full rounded-lg border border-gray-200 bg-white px-3 py-2 text-sm outline-none placeholder:text-gray-500 focus:border-gray-500"
|
||||
required
|
||||
minLength={3}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
@@ -94,6 +158,10 @@ export function ContentGenerator() {
|
||||
<GuideOptions depth={depth} setDepth={setDepth} />
|
||||
)}
|
||||
|
||||
{selectedFormat === 'course' && (
|
||||
<CourseOptions difficulty={difficulty} setDifficulty={setDifficulty} />
|
||||
)}
|
||||
|
||||
{selectedFormat !== 'roadmap' && (
|
||||
<>
|
||||
<div className="flex h-9 items-center gap-2 rounded-lg border border-gray-200 px-3 text-sm">
|
||||
|
76
src/components/ContentGenerator/CourseOptions.tsx
Normal file
76
src/components/ContentGenerator/CourseOptions.tsx
Normal file
@@ -0,0 +1,76 @@
|
||||
import { useId, useState } from 'react';
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '../Select';
|
||||
|
||||
type CourseOptionsProps = {
|
||||
difficulty: string;
|
||||
setDifficulty: (difficulty: string) => void;
|
||||
};
|
||||
|
||||
export function CourseOptions(props: CourseOptionsProps) {
|
||||
const { difficulty, setDifficulty } = props;
|
||||
const difficultySelectId = useId();
|
||||
|
||||
const difficultyOptions = [
|
||||
{
|
||||
label: 'Beginner',
|
||||
value: 'beginner',
|
||||
description: 'Covers fundamental concepts',
|
||||
},
|
||||
{
|
||||
label: 'Intermediate',
|
||||
value: 'intermediate',
|
||||
description: 'Explore advanced topics',
|
||||
},
|
||||
{
|
||||
label: 'Advanced',
|
||||
value: 'advanced',
|
||||
description: 'Deep dives into complex concepts',
|
||||
},
|
||||
];
|
||||
|
||||
const selectedDifficulty = difficultyOptions.find(
|
||||
(option) => option.value === difficulty,
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-2">
|
||||
<label
|
||||
htmlFor={difficultySelectId}
|
||||
className="inline-block text-sm text-gray-500"
|
||||
>
|
||||
Choose difficulty level
|
||||
</label>
|
||||
<Select value={difficulty} onValueChange={setDifficulty}>
|
||||
<SelectTrigger id={difficultySelectId}>
|
||||
{selectedDifficulty && (
|
||||
<div className="flex flex-col gap-1">
|
||||
<span>{selectedDifficulty.label}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!selectedDifficulty && (
|
||||
<SelectValue placeholder="Select a difficulty" />
|
||||
)}
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{difficultyOptions.map((option) => (
|
||||
<SelectItem key={option.value} value={option.value}>
|
||||
<div className="flex flex-col gap-1">
|
||||
<span>{option.label}</span>
|
||||
<span className="text-xs text-gray-500">
|
||||
{option.description}
|
||||
</span>
|
||||
</div>
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
);
|
||||
}
|
@@ -6,7 +6,7 @@ import { getUrlParams } from '../../lib/browser';
|
||||
import { isLoggedIn } from '../../lib/jwt';
|
||||
import { getAiCourseOptions } from '../../queries/ai-course';
|
||||
import { queryClient } from '../../stores/query-client';
|
||||
import { AIDocumentContent } from './AIDocumentContent';
|
||||
import { AIDocumentContent } from './AIGuideContent';
|
||||
|
||||
type GenerateAIDocumentProps = {};
|
||||
|
@@ -3,7 +3,7 @@ import { useEffect, useState } from 'react';
|
||||
import { generateDocument } from '../../helper/generate-ai-document';
|
||||
import { queryClient } from '../../stores/query-client';
|
||||
import { getAiDocumentOptions } from '../../queries/ai-document';
|
||||
import { AIDocumentContent } from './AIDocumentContent';
|
||||
import { AIDocumentContent } from './AIGuideContent';
|
||||
|
||||
type GetAIDocumentProps = {
|
||||
slug: string;
|
@@ -1,6 +1,6 @@
|
||||
---
|
||||
import { AITutorLayout } from '../../../components/AITutor/AITutorLayout';
|
||||
import { GetAIDocument } from '../../../components/GenerateDocument/GetAIDocument';
|
||||
import { GetAIDocument } from '../../../components/GenerateGuide/GetAIGuide';
|
||||
import SkeletonLayout from '../../../layouts/SkeletonLayout.astro';
|
||||
|
||||
export const prerender = false;
|
@@ -1,8 +1,8 @@
|
||||
---
|
||||
import { AITutorLayout } from '../../components/AITutor/AITutorLayout';
|
||||
import { CheckSubscriptionVerification } from '../../components/Billing/CheckSubscriptionVerification';
|
||||
import { GenerateAIDocument } from '../../components/GenerateDocument/GenerateAIDocument';
|
||||
import SkeletonLayout from '../../layouts/SkeletonLayout.astro';
|
||||
import { AITutorLayout } from '../../../components/AITutor/AITutorLayout';
|
||||
import { CheckSubscriptionVerification } from '../../../components/Billing/CheckSubscriptionVerification';
|
||||
import { GenerateAIDocument } from '../../../components/GenerateGuide/GenerateAIGuide';
|
||||
import SkeletonLayout from '../../../layouts/SkeletonLayout.astro';
|
||||
---
|
||||
|
||||
<SkeletonLayout
|
Reference in New Issue
Block a user