1
0
mirror of https://github.com/kamranahmedse/developer-roadmap.git synced 2025-09-08 16:20:40 +02:00

feat: remove question progress

This commit is contained in:
Arik Chakma
2025-06-23 23:33:28 +06:00
parent 4038681fb5
commit 831b1a6f5d
2 changed files with 26 additions and 142 deletions

View File

@@ -1,12 +1,9 @@
import { useEffect, useRef, useState } from 'react';
import { useRef, useState } from 'react';
import { QuestionsProgress } from './QuestionsProgress';
import { CheckCircle, SkipForward, Sparkles } from 'lucide-react';
import { QuestionCard } from './QuestionCard';
import { QuestionLoader } from './QuestionLoader';
import { isLoggedIn } from '../../lib/jwt';
import type { QuestionType } from '../../lib/question-group';
import { httpGet, httpPut } from '../../lib/http';
import { useToast } from '../../hooks/use-toast';
import { QuestionFinished } from './QuestionFinished';
import { Confetti } from '../Confetti';
@@ -24,142 +21,42 @@ type QuestionsListProps = {
};
export function QuestionsList(props: QuestionsListProps) {
const { questions: defaultQuestions, groupId } = props;
const { questions } = props;
const toast = useToast();
const [questions, setQuestions] = useState(defaultQuestions);
const [isLoading, setIsLoading] = useState(true);
const [showConfetti, setShowConfetti] = useState(false);
const [currQuestionIndex, setCurrQuestionIndex] = useState(0);
const [userProgress, setUserProgress] = useState<UserQuestionProgress>();
const [userProgress, setUserProgress] = useState<UserQuestionProgress>({
know: [],
dontKnow: [],
skip: [],
});
const containerRef = useRef<HTMLDivElement>(null);
async function fetchUserProgress(): Promise<
UserQuestionProgress | undefined
> {
if (!isLoggedIn()) {
return;
}
const { response, error } = await httpGet<UserQuestionProgress>(
`${
import.meta.env.PUBLIC_API_URL
}/v1-get-user-question-progress/${groupId}`,
);
if (error) {
toast.error(error.message || 'Error fetching user progress');
return;
}
return response;
}
async function prepareProgress() {
const userProgress = await fetchUserProgress();
setUserProgress(userProgress);
const knownQuestions = userProgress?.know || [];
const didNotKnowQuestions = userProgress?.dontKnow || [];
const skipQuestions = userProgress?.skip || [];
const pendingQuestionIndex = questions.findIndex((question) => {
return (
!knownQuestions.includes(question.id) &&
!didNotKnowQuestions.includes(question.id) &&
!skipQuestions.includes(question.id)
);
});
setCurrQuestionIndex(pendingQuestionIndex);
setIsLoading(false);
}
async function resetProgress() {
let knownQuestions = userProgress?.know || [];
let didNotKnowQuestions = userProgress?.dontKnow || [];
let skipQuestions = userProgress?.skip || [];
if (!isLoggedIn()) {
setQuestions(defaultQuestions);
knownQuestions = [];
didNotKnowQuestions = [];
skipQuestions = [];
} else {
setIsLoading(true);
const { response, error } = await httpPut<UserQuestionProgress>(
`${
import.meta.env.PUBLIC_API_URL
}/v1-reset-question-progress/${groupId}`,
{
status: 'reset',
},
);
if (error) {
toast.error(error.message || 'Error resetting progress');
return;
}
knownQuestions = response?.know || [];
didNotKnowQuestions = response?.dontKnow || [];
skipQuestions = response?.skip || [];
}
setCurrQuestionIndex(0);
setUserProgress({
know: knownQuestions,
dontKnow: didNotKnowQuestions,
skip: skipQuestions,
know: [],
dontKnow: [],
skip: [],
});
setIsLoading(false);
}
async function updateQuestionStatus(
function updateQuestionStatus(
status: QuestionProgressType,
questionId: string,
) {
setIsLoading(true);
let newProgress = userProgress || { know: [], dontKnow: [], skip: [] };
if (!isLoggedIn()) {
if (status === 'know') {
newProgress.know.push(questionId);
} else if (status == 'dontKnow') {
newProgress.dontKnow.push(questionId);
} else if (status == 'skip') {
newProgress.skip.push(questionId);
}
} else {
const { response, error } = await httpPut<UserQuestionProgress>(
`${
import.meta.env.PUBLIC_API_URL
}/v1-update-question-status/${groupId}`,
{
status,
questionId,
questionGroupId: groupId,
},
);
if (error || !response) {
toast.error(error?.message || 'Error marking question status');
return;
}
newProgress = response;
if (status === 'know') {
newProgress.know.push(questionId);
} else if (status == 'dontKnow') {
newProgress.dontKnow.push(questionId);
} else if (status == 'skip') {
newProgress.skip.push(questionId);
}
const nextQuestionIndex = currQuestionIndex + 1;
setUserProgress(newProgress);
setIsLoading(false);
if (!nextQuestionIndex || !questions[nextQuestionIndex]) {
setShowConfetti(true);
}
@@ -167,17 +64,13 @@ export function QuestionsList(props: QuestionsListProps) {
setCurrQuestionIndex(nextQuestionIndex);
}
useEffect(() => {
prepareProgress().then(() => null);
}, [questions]);
const knowCount = userProgress?.know.length || 0;
const dontKnowCount = userProgress?.dontKnow.length || 0;
const skipCount = userProgress?.skip.length || 0;
const hasProgress = knowCount > 0 || dontKnowCount > 0 || skipCount > 0;
const currQuestion = questions[currQuestionIndex];
const hasFinished = !isLoading && hasProgress && currQuestionIndex === -1;
const hasFinished = hasProgress && currQuestionIndex === -1;
return (
<div className="mb-0 gap-3 text-center sm:mb-40">
@@ -186,7 +79,6 @@ export function QuestionsList(props: QuestionsListProps) {
didNotKnowCount={dontKnowCount}
skippedCount={skipCount}
totalCount={questions?.length}
isLoading={isLoading}
showLoginAlert={!isLoggedIn() && hasProgress}
onResetClick={() => {
resetProgress().finally(() => null);
@@ -196,7 +88,7 @@ export function QuestionsList(props: QuestionsListProps) {
currQuestionIndex !== -1 &&
currQuestionIndex < questions.length - 1
) {
updateQuestionStatus('skip', currQuestion.id).finally(() => null);
updateQuestionStatus('skip', currQuestion.id);
}
}}
onPrevClick={() => {
@@ -244,8 +136,7 @@ export function QuestionsList(props: QuestionsListProps) {
}}
/>
)}
{!isLoading && currQuestion && <QuestionCard question={currQuestion} />}
{isLoading && <QuestionLoader />}
{currQuestion && <QuestionCard question={currQuestion} />}
</div>
<div
@@ -254,11 +145,11 @@ export function QuestionsList(props: QuestionsListProps) {
}`}
>
<button
disabled={isLoading || !currQuestion}
disabled={!currQuestion}
onClick={(e) => {
e.stopPropagation();
e.preventDefault();
updateQuestionStatus('know', currQuestion.id).finally(() => null);
updateQuestionStatus('know', currQuestion.id);
}}
className="flex flex-1 items-center rounded-md border border-gray-300 bg-white px-2 py-2 text-sm text-black transition-colors hover:border-black hover:bg-black hover:text-white disabled:pointer-events-none disabled:opacity-50 sm:rounded-lg sm:px-4 sm:py-3 sm:text-base"
>
@@ -267,11 +158,9 @@ export function QuestionsList(props: QuestionsListProps) {
</button>
<button
onClick={() => {
updateQuestionStatus('dontKnow', currQuestion.id).finally(
() => null,
);
updateQuestionStatus('dontKnow', currQuestion.id);
}}
disabled={isLoading || !currQuestion}
disabled={!currQuestion}
className="flex flex-1 items-center rounded-md border border-gray-300 bg-white px-2 py-2 text-sm text-black transition-colors hover:border-black hover:bg-black hover:text-white disabled:pointer-events-none disabled:opacity-50 sm:rounded-lg sm:px-4 sm:py-3 sm:text-base"
>
<Sparkles className="mr-1 h-4 text-current" />
@@ -279,9 +168,9 @@ export function QuestionsList(props: QuestionsListProps) {
</button>
<button
onClick={() => {
updateQuestionStatus('skip', currQuestion.id).finally(() => null);
updateQuestionStatus('skip', currQuestion.id);
}}
disabled={isLoading || !currQuestion}
disabled={!currQuestion}
data-next-question="skip"
className="flex flex-1 items-center rounded-md border border-red-600 px-2 py-2 text-sm text-red-600 hover:bg-red-600 hover:text-white disabled:pointer-events-none disabled:opacity-50 sm:rounded-lg sm:px-4 sm:py-3 sm:text-base"
>

View File

@@ -1,9 +1,6 @@
---
import GridItem from '../../components/GridItem.astro';
import SimplePageHeader from '../../components/SimplePageHeader.astro';
import BaseLayout from '../../layouts/BaseLayout.astro';
import Footer from '../../components/Footer.astro';
import AstroIcon from '../../components/AstroIcon.astro';
import { QuestionsList } from '../../components/Questions/QuestionsList';
import {
@@ -11,8 +8,6 @@ import {
type QuestionGroupType,
} from '../../lib/question-group';
import QuestionGuide from '../../components/Questions/QuestionGuide.astro';
import { markdownToHtml } from '../../lib/markdown';
import MarkdownFile from '../../components/MarkdownFile.astro';
export const prerender = true;