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