mirror of
https://github.com/kamranahmedse/developer-roadmap.git
synced 2025-09-02 13:52:46 +02:00
wip
This commit is contained in:
@@ -23,6 +23,8 @@ const DEFAULT_QUESTION_STATE: QuestionState = {
|
|||||||
status: 'pending',
|
status: 'pending',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type QuizStatus = 'answering' | 'submitted' | 'reviewing';
|
||||||
|
|
||||||
type AIQuizContentProps = {
|
type AIQuizContentProps = {
|
||||||
quizSlug?: string;
|
quizSlug?: string;
|
||||||
questions: QuizQuestion[];
|
questions: QuizQuestion[];
|
||||||
@@ -38,7 +40,7 @@ export function AIQuizContent(props: AIQuizContentProps) {
|
|||||||
const [questionStates, setQuestionStates] = useState<
|
const [questionStates, setQuestionStates] = useState<
|
||||||
Record<number, QuestionState>
|
Record<number, QuestionState>
|
||||||
>({});
|
>({});
|
||||||
const [isAllQuestionsSubmitted, setIsAllQuestionsSubmitted] = useState(false);
|
const [quizStatus, setQuizStatus] = useState<QuizStatus>('answering');
|
||||||
|
|
||||||
const activeQuestionState =
|
const activeQuestionState =
|
||||||
questionStates[activeQuestionIndex] ?? DEFAULT_QUESTION_STATE;
|
questionStates[activeQuestionIndex] ?? DEFAULT_QUESTION_STATE;
|
||||||
@@ -59,7 +61,9 @@ export function AIQuizContent(props: AIQuizContentProps) {
|
|||||||
return newSelectedOptions;
|
return newSelectedOptions;
|
||||||
});
|
});
|
||||||
|
|
||||||
setIsAllQuestionsSubmitted(activeQuestionIndex === questions.length - 1);
|
setQuizStatus(
|
||||||
|
activeQuestionIndex === questions.length - 1 ? 'submitted' : 'answering',
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSetUserAnswer = (userAnswer: string) => {
|
const handleSetUserAnswer = (userAnswer: string) => {
|
||||||
@@ -112,34 +116,54 @@ export function AIQuizContent(props: AIQuizContentProps) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleNext = () => {
|
|
||||||
setActiveQuestionIndex(activeQuestionIndex + 1);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleRetry = () => {
|
const handleRetry = () => {
|
||||||
setActiveQuestionIndex(0);
|
setActiveQuestionIndex(0);
|
||||||
setQuestionStates({});
|
setQuestionStates({});
|
||||||
setIsAllQuestionsSubmitted(false);
|
setQuizStatus('answering');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const hasNextQuestion = activeQuestionIndex < questions.length - 1;
|
||||||
|
const hasPreviousQuestion = activeQuestionIndex > 0;
|
||||||
const totalQuestions = questions?.length ?? 0;
|
const totalQuestions = questions?.length ?? 0;
|
||||||
|
const isAllQuestionsSubmitted =
|
||||||
|
Object.values(questionStates).filter((state) => state.status !== 'pending')
|
||||||
|
.length === totalQuestions;
|
||||||
|
|
||||||
const progressPercentage = isLoading
|
const progressPercentage = isLoading
|
||||||
? 0
|
? 0
|
||||||
: getPercentage(activeQuestionIndex + 1, totalQuestions);
|
: getPercentage(activeQuestionIndex + 1, totalQuestions);
|
||||||
|
|
||||||
|
const shouldShowQuestions =
|
||||||
|
quizStatus === 'answering' || quizStatus === 'reviewing';
|
||||||
|
|
||||||
|
const handleNextQuestion = () => {
|
||||||
|
if (!hasNextQuestion) {
|
||||||
|
setQuizStatus(isAllQuestionsSubmitted ? 'submitted' : 'reviewing');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setActiveQuestionIndex(activeQuestionIndex + 1);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto w-full max-w-lg py-10">
|
<div className="mx-auto w-full max-w-lg py-10">
|
||||||
{!isAllQuestionsSubmitted && (
|
{shouldShowQuestions && (
|
||||||
<QuizTopNavigation
|
<QuizTopNavigation
|
||||||
activeQuestionIndex={activeQuestionIndex}
|
activeQuestionIndex={activeQuestionIndex}
|
||||||
totalQuestions={totalQuestions}
|
totalQuestions={totalQuestions}
|
||||||
progressPercentage={progressPercentage}
|
progressPercentage={progressPercentage}
|
||||||
onPrevious={() => setActiveQuestionIndex(activeQuestionIndex - 1)}
|
onPrevious={() => {
|
||||||
onNext={() => setActiveQuestionIndex(activeQuestionIndex + 1)}
|
if (!hasPreviousQuestion) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setActiveQuestionIndex(activeQuestionIndex - 1);
|
||||||
|
}}
|
||||||
|
onNext={handleNextQuestion}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{isAllQuestionsSubmitted && (
|
{quizStatus === 'submitted' && (
|
||||||
<AIQuizResults
|
<AIQuizResults
|
||||||
questionStates={questionStates}
|
questionStates={questionStates}
|
||||||
totalQuestions={totalQuestions}
|
totalQuestions={totalQuestions}
|
||||||
@@ -147,10 +171,14 @@ export function AIQuizContent(props: AIQuizContentProps) {
|
|||||||
onNewQuiz={() => {
|
onNewQuiz={() => {
|
||||||
window.location.href = '/ai/quiz';
|
window.location.href = '/ai/quiz';
|
||||||
}}
|
}}
|
||||||
|
onReview={(questionIndex) => {
|
||||||
|
setActiveQuestionIndex(questionIndex);
|
||||||
|
setQuizStatus('reviewing');
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!isAllQuestionsSubmitted && (
|
{shouldShowQuestions && (
|
||||||
<>
|
<>
|
||||||
{activeQuestion && activeQuestion.type === 'mcq' && (
|
{activeQuestion && activeQuestion.type === 'mcq' && (
|
||||||
<AIMCQQuestion
|
<AIMCQQuestion
|
||||||
@@ -158,7 +186,7 @@ export function AIQuizContent(props: AIQuizContentProps) {
|
|||||||
questionState={activeQuestionState}
|
questionState={activeQuestionState}
|
||||||
setSelectedOptions={handleSelectOptions}
|
setSelectedOptions={handleSelectOptions}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
onNext={handleNext}
|
onNext={handleNextQuestion}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -168,7 +196,7 @@ export function AIQuizContent(props: AIQuizContentProps) {
|
|||||||
question={activeQuestion}
|
question={activeQuestion}
|
||||||
questionState={activeQuestionState}
|
questionState={activeQuestionState}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
onNext={handleNext}
|
onNext={handleNextQuestion}
|
||||||
setUserAnswer={handleSetUserAnswer}
|
setUserAnswer={handleSetUserAnswer}
|
||||||
setCorrectAnswer={handleSetCorrectAnswer}
|
setCorrectAnswer={handleSetCorrectAnswer}
|
||||||
/>
|
/>
|
||||||
|
@@ -218,9 +218,7 @@ export function AIQuizGenerator() {
|
|||||||
format={selectedFormatTitle || selectedFormat}
|
format={selectedFormatTitle || selectedFormat}
|
||||||
questionAnswerChatMessages={questionAnswerChatMessages}
|
questionAnswerChatMessages={questionAnswerChatMessages}
|
||||||
setQuestionAnswerChatMessages={setQuestionAnswerChatMessages}
|
setQuestionAnswerChatMessages={setQuestionAnswerChatMessages}
|
||||||
onGenerateNow={() => {
|
from="quiz"
|
||||||
handleSubmit();
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
@@ -22,10 +22,12 @@ type AIQuizResultsProps = {
|
|||||||
totalQuestions: number;
|
totalQuestions: number;
|
||||||
onRetry: () => void;
|
onRetry: () => void;
|
||||||
onNewQuiz: () => void;
|
onNewQuiz: () => void;
|
||||||
|
onReview?: (questionIndex: number) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function AIQuizResults(props: AIQuizResultsProps) {
|
export function AIQuizResults(props: AIQuizResultsProps) {
|
||||||
const { questionStates, totalQuestions, onRetry, onNewQuiz } = props;
|
const { questionStates, totalQuestions, onRetry, onNewQuiz, onReview } =
|
||||||
|
props;
|
||||||
|
|
||||||
const states = Object.values(questionStates);
|
const states = Object.values(questionStates);
|
||||||
const correctCount = states.filter(
|
const correctCount = states.filter(
|
||||||
@@ -66,10 +68,11 @@ export function AIQuizResults(props: AIQuizResultsProps) {
|
|||||||
const isSkipped = status === 'skipped';
|
const isSkipped = status === 'skipped';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<button
|
||||||
key={quizIndex}
|
key={quizIndex}
|
||||||
|
onClick={() => onReview?.(quizIndex)}
|
||||||
className={cn(
|
className={cn(
|
||||||
'flex aspect-square flex-col items-center justify-center rounded-xl border border-gray-200 p-2',
|
'flex aspect-square flex-col items-center justify-center rounded-xl border border-gray-200 p-2 hover:opacity-80',
|
||||||
isCorrect && 'bg-green-700 text-white',
|
isCorrect && 'bg-green-700 text-white',
|
||||||
isIncorrect && 'bg-red-700 text-white',
|
isIncorrect && 'bg-red-700 text-white',
|
||||||
isSkipped && 'bg-gray-700 text-white',
|
isSkipped && 'bg-gray-700 text-white',
|
||||||
@@ -78,7 +81,7 @@ export function AIQuizResults(props: AIQuizResultsProps) {
|
|||||||
{isCorrect && <CheckIcon className="h-6 w-6" />}
|
{isCorrect && <CheckIcon className="h-6 w-6" />}
|
||||||
{isIncorrect && <XIcon className="h-6 w-6" />}
|
{isIncorrect && <XIcon className="h-6 w-6" />}
|
||||||
{isSkipped && <SkipForwardIcon className="h-6 w-6" />}
|
{isSkipped && <SkipForwardIcon className="h-6 w-6" />}
|
||||||
</div>
|
</button>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
@@ -227,9 +227,6 @@ export function ContentGenerator() {
|
|||||||
format={selectedFormat}
|
format={selectedFormat}
|
||||||
questionAnswerChatMessages={questionAnswerChatMessages}
|
questionAnswerChatMessages={questionAnswerChatMessages}
|
||||||
setQuestionAnswerChatMessages={setQuestionAnswerChatMessages}
|
setQuestionAnswerChatMessages={setQuestionAnswerChatMessages}
|
||||||
onGenerateNow={() => {
|
|
||||||
handleSubmit();
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
@@ -27,9 +27,11 @@ type QuestionAnswerChatProps = {
|
|||||||
setQuestionAnswerChatMessages: (
|
setQuestionAnswerChatMessages: (
|
||||||
messages: QuestionAnswerChatMessage[],
|
messages: QuestionAnswerChatMessage[],
|
||||||
) => void;
|
) => void;
|
||||||
onGenerateNow: () => void;
|
|
||||||
defaultQuestions?: AIQuestionSuggestionsResponse['questions'];
|
defaultQuestions?: AIQuestionSuggestionsResponse['questions'];
|
||||||
type?: 'create' | 'update';
|
type?: 'create' | 'update';
|
||||||
|
|
||||||
|
from?: 'content' | 'quiz';
|
||||||
|
|
||||||
className?: string;
|
className?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -40,9 +42,9 @@ export function QuestionAnswerChat(props: QuestionAnswerChatProps) {
|
|||||||
defaultQuestions,
|
defaultQuestions,
|
||||||
questionAnswerChatMessages,
|
questionAnswerChatMessages,
|
||||||
setQuestionAnswerChatMessages,
|
setQuestionAnswerChatMessages,
|
||||||
onGenerateNow,
|
|
||||||
type = 'create',
|
type = 'create',
|
||||||
className = '',
|
className = '',
|
||||||
|
from = 'content',
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const [activeMessageIndex, setActiveMessageIndex] = useState(
|
const [activeMessageIndex, setActiveMessageIndex] = useState(
|
||||||
@@ -58,7 +60,7 @@ export function QuestionAnswerChat(props: QuestionAnswerChatProps) {
|
|||||||
data: aiQuestionSuggestions,
|
data: aiQuestionSuggestions,
|
||||||
isLoading: isLoadingAiQuestionSuggestions,
|
isLoading: isLoadingAiQuestionSuggestions,
|
||||||
} = useQuery(
|
} = useQuery(
|
||||||
aiQuestionSuggestionsOptions({ term, format }, defaultQuestions),
|
aiQuestionSuggestionsOptions({ term, format, from }, defaultQuestions),
|
||||||
queryClient,
|
queryClient,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -113,11 +115,6 @@ export function QuestionAnswerChat(props: QuestionAnswerChatProps) {
|
|||||||
scrollToBottom();
|
scrollToBottom();
|
||||||
};
|
};
|
||||||
|
|
||||||
const canGenerateNow =
|
|
||||||
// user can generate after answering 5 questions -> 5 * 2 messages (user and assistant)
|
|
||||||
!isLoadingAiQuestionSuggestions && questionAnswerChatMessages.length >= 10;
|
|
||||||
|
|
||||||
const canReset = questionAnswerChatMessages.length >= 2;
|
|
||||||
const handleReset = () => {
|
const handleReset = () => {
|
||||||
setQuestionAnswerChatMessages([]);
|
setQuestionAnswerChatMessages([]);
|
||||||
setActiveMessageIndex(0);
|
setActiveMessageIndex(0);
|
||||||
|
@@ -4,6 +4,7 @@ import { httpGet } from '../lib/query-http';
|
|||||||
type AIQuestionSuggestionsQuery = {
|
type AIQuestionSuggestionsQuery = {
|
||||||
term: string;
|
term: string;
|
||||||
format: string;
|
format: string;
|
||||||
|
from?: 'content' | 'quiz';
|
||||||
};
|
};
|
||||||
|
|
||||||
export type AIQuestionSuggestionsResponse = {
|
export type AIQuestionSuggestionsResponse = {
|
||||||
@@ -31,7 +32,7 @@ export function aiQuestionSuggestionsOptions(
|
|||||||
query,
|
query,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
enabled: !!query.term && !!query.format,
|
enabled: !!query.term && !!query.format && !!query.from,
|
||||||
refetchOnMount: false,
|
refetchOnMount: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user