Show user progress

pull/4448/head
Kamran Ahmed 1 year ago
parent 548b7f31f9
commit a847d0b08d
  1. 43
      src/components/Questions/QuestionsList.tsx
  2. 47
      src/components/Questions/QuestionsProgress.tsx

@ -12,6 +12,7 @@ import { useToast } from '../../hooks/use-toast';
type UserQuestionProgress = { type UserQuestionProgress = {
know: string[]; know: string[];
didNotKnow: string[]; didNotKnow: string[];
skipped: string[];
}; };
type QuestionsListProps = { type QuestionsListProps = {
@ -20,13 +21,11 @@ type QuestionsListProps = {
}; };
export function QuestionsList(props: QuestionsListProps) { export function QuestionsList(props: QuestionsListProps) {
const { questions: defaultQuestions, groupId } = props; const { questions: unshuffledQuestions, groupId } = props;
const toast = useToast(); const toast = useToast();
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
const [isUpdatingStatus, setIsUpdatingStatus] = useState(false);
const [confettiEl, setConfettiEl] = useState<HTMLElement | null>(null); const [confettiEl, setConfettiEl] = useState<HTMLElement | null>(null);
const [questions, setQuestions] = useState<QuestionType[]>(); const [questions, setQuestions] = useState<QuestionType[]>();
@ -72,33 +71,37 @@ export function QuestionsList(props: QuestionsListProps) {
const knownQuestions = userProgress?.know || []; const knownQuestions = userProgress?.know || [];
const didNotKnowQuestions = userProgress?.didNotKnow || []; const didNotKnowQuestions = userProgress?.didNotKnow || [];
const skippedQuestions = userProgress?.skipped || [];
const pendingQuestions = defaultQuestions.filter((question) => { const pendingQuestions = unshuffledQuestions.filter((question) => {
return ( return (
!knownQuestions.includes(question.id) && !knownQuestions.includes(question.id) &&
!didNotKnowQuestions.includes(question.id) !didNotKnowQuestions.includes(question.id) &&
!skippedQuestions.includes(question.id)
); );
}); });
// Shuffle and set pending questions // Shuffle and set pending questions
setPendingQuestions(pendingQuestions.sort(() => Math.random() - 0.5)); setPendingQuestions(pendingQuestions.sort(() => Math.random() - 0.5));
setQuestions(defaultQuestions); setQuestions(unshuffledQuestions);
setIsLoading(false); setIsLoading(false);
} }
async function updateQuestionStatus( async function updateQuestionStatus(
status: 'know' | 'dontKnow', status: 'know' | 'dontKnow' | 'skip',
questionId: string questionId: string
) { ) {
setIsUpdatingStatus(true); setIsLoading(true);
let newProgress = userProgress || { know: [], didNotKnow: [] }; let newProgress = userProgress || { know: [], didNotKnow: [], skipped: [] };
if (!isLoggedIn()) { if (!isLoggedIn()) {
if (status === 'know') { if (status === 'know') {
newProgress.know.push(questionId); newProgress.know.push(questionId);
} else { } else if (status == 'dontKnow') {
newProgress.didNotKnow.push(questionId); newProgress.didNotKnow.push(questionId);
} else if (status == 'skip') {
newProgress.skipped.push(questionId);
} }
} else { } else {
const { response, error } = await httpPut<UserQuestionProgress>( const { response, error } = await httpPut<UserQuestionProgress>(
@ -120,16 +123,17 @@ export function QuestionsList(props: QuestionsListProps) {
setUserProgress(newProgress); setUserProgress(newProgress);
setPendingQuestions(pendingQuestions.filter((q) => q.id !== questionId)); setPendingQuestions(pendingQuestions.filter((q) => q.id !== questionId));
setIsUpdatingStatus(false); setIsLoading(false);
} }
useEffect(() => { useEffect(() => {
loadQuestions().then(() => null); loadQuestions().then(() => null);
}, [defaultQuestions]); }, [unshuffledQuestions]);
const knownCount = userProgress?.know.length || 0; const knownCount = userProgress?.know.length || 0;
const didNotKnowCount = userProgress?.didNotKnow.length || 0; const didNotKnowCount = userProgress?.didNotKnow.length || 0;
const hasProgress = knownCount > 0 || didNotKnowCount > 0; const skippedCount = userProgress?.skipped.length || 0;
const hasProgress = knownCount > 0 || didNotKnowCount > 0 || skippedCount > 0;
const currQuestion = pendingQuestions[0]; const currQuestion = pendingQuestions[0];
@ -143,6 +147,10 @@ export function QuestionsList(props: QuestionsListProps) {
/> />
<QuestionsProgress <QuestionsProgress
knowCount={knownCount}
didNotKnowCount={didNotKnowCount}
skippedCount={skippedCount}
totalCount={unshuffledQuestions?.length || questions?.length}
isLoading={isLoading} isLoading={isLoading}
showLoginAlert={!isLoggedIn() && hasProgress} showLoginAlert={!isLoggedIn() && hasProgress}
/> />
@ -154,7 +162,7 @@ export function QuestionsList(props: QuestionsListProps) {
<div className="flex flex-col gap-3 sm:flex-row"> <div className="flex flex-col gap-3 sm:flex-row">
<button <button
disabled={isLoading || isUpdatingStatus} disabled={isLoading}
ref={alreadyKnowRef} ref={alreadyKnowRef}
onClick={(e) => { onClick={(e) => {
showConfetti(alreadyKnowRef.current); showConfetti(alreadyKnowRef.current);
@ -173,14 +181,17 @@ export function QuestionsList(props: QuestionsListProps) {
() => null () => null
); );
}} }}
disabled={isLoading || isUpdatingStatus} disabled={isLoading}
className="flex flex-1 items-center rounded-xl border border-gray-300 bg-white py-3 px-4 text-black transition-colors hover:border-black hover:bg-black hover:text-white disabled:pointer-events-none disabled:opacity-50" className="flex flex-1 items-center rounded-xl border border-gray-300 bg-white py-3 px-4 text-black transition-colors hover:border-black hover:bg-black hover:text-white disabled:pointer-events-none disabled:opacity-50"
> >
<Sparkles className="mr-1 h-4 text-current" /> <Sparkles className="mr-1 h-4 text-current" />
Didn't Know that Didn't Know that
</button> </button>
<button <button
disabled={isLoading || isUpdatingStatus} onClick={() => {
updateQuestionStatus('skip', currQuestion.id).finally(() => null);
}}
disabled={isLoading}
data-next-question="skip" data-next-question="skip"
className="flex flex-1 items-center rounded-xl border border-red-600 p-3 text-red-600 hover:bg-red-600 hover:text-white disabled:pointer-events-none disabled:opacity-50" className="flex flex-1 items-center rounded-xl border border-red-600 p-3 text-red-600 hover:bg-red-600 hover:text-white disabled:pointer-events-none disabled:opacity-50"
> >

@ -1,43 +1,72 @@
import { CheckCircle, RotateCcw, Sparkles } from 'lucide-react'; import { CheckCircle, RotateCcw, SkipForward, Sparkles } from 'lucide-react';
import { showLoginPopup } from '../../lib/popup'; import { showLoginPopup } from '../../lib/popup';
type QuestionsProgressProps = { type QuestionsProgressProps = {
isLoading?: boolean; isLoading?: boolean;
showLoginAlert?: boolean; showLoginAlert?: boolean;
knowCount?: number;
didNotKnowCount?: number;
totalCount?: number;
skippedCount?: number;
}; };
export function QuestionsProgress(props: QuestionsProgressProps) { export function QuestionsProgress(props: QuestionsProgressProps) {
const { showLoginAlert, isLoading = false } = props; const {
showLoginAlert,
isLoading = false,
knowCount = 0,
didNotKnowCount = 0,
totalCount = 0,
skippedCount = 0,
} = props;
const totalSolved = knowCount + didNotKnowCount + skippedCount;
const donePercentage = (totalSolved / totalCount) * 100;
return ( return (
<div className="mb-5 overflow-hidden rounded-lg border border-gray-300 bg-white p-6"> <div className="mb-5 overflow-hidden rounded-lg border border-gray-300 bg-white p-6">
<div className="mb-3 flex items-center text-gray-600"> <div className="mb-3 flex items-center text-gray-600">
<div className="relative w-full flex-1 rounded-xl bg-gray-200 p-1"> <div className="relative w-full flex-1 rounded-xl bg-gray-200 p-1">
<div className="absolute bottom-0 left-0 top-0 w-[30%] rounded-xl bg-slate-800"></div> <div
className="absolute bottom-0 left-0 top-0 rounded-xl bg-slate-800"
style={{
width: `${donePercentage}%`,
}}
/>
</div> </div>
<span className="ml-3 text-sm">5 / 100</span> <span className="ml-3 text-sm">
{totalSolved} / {totalCount}
</span>
</div> </div>
<div className="relative -left-1 flex flex-col gap-2 text-sm text-black sm:flex-row sm:gap-3"> <div className="relative -left-1 flex flex-col gap-2 text-sm text-black sm:flex-row sm:gap-3">
<span className="flex items-center"> <span className="flex items-center">
<CheckCircle className="mr-1 h-4" /> <CheckCircle className="mr-1 h-4" />
<span>Already knew</span> <span>Knew</span>
<span className="ml-2 rounded-md bg-gray-200/80 px-1.5 font-medium text-black"> <span className="ml-2 rounded-md bg-gray-200/80 px-1.5 font-medium text-black">
44 Questions {knowCount} Questions
</span> </span>
</span> </span>
<span className="flex items-center"> <span className="flex items-center">
<Sparkles className="mr-1 h-4" /> <Sparkles className="mr-1 h-4" />
<span>Didn't Know</span> <span>Learnt</span>
<span className="ml-2 rounded-md bg-gray-200/80 px-1.5 font-medium text-black">
{didNotKnowCount} Questions
</span>
</span>
<span className="flex items-center">
<SkipForward className="mr-1 h-4" />
<span>Skipped</span>
<span className="ml-2 rounded-md bg-gray-200/80 px-1.5 font-medium text-black"> <span className="ml-2 rounded-md bg-gray-200/80 px-1.5 font-medium text-black">
20 Questions {skippedCount} Questions
</span> </span>
</span> </span>
<button className="flex items-center text-red-600 hover:text-red-900"> <button className="flex items-center text-red-600 hover:text-red-900">
<RotateCcw className="mr-1 h-4" /> <RotateCcw className="mr-1 h-4" />
Reset Progress Reset
</button> </button>
</div> </div>

Loading…
Cancel
Save