Add functionality to go next and back on questions

pull/5774/head
Kamran Ahmed 5 months ago
parent a14d8b5f90
commit 4c615f85e5
  1. 36
      src/components/Questions/QuestionFinished.tsx
  2. 99
      src/components/Questions/QuestionsList.tsx
  3. 40
      src/components/Questions/QuestionsProgress.tsx

@ -13,28 +13,19 @@ type ProgressStatButtonProps = {
icon: ReactNode;
label: string;
count: number;
onClick: () => void;
};
function ProgressStatButton(props: ProgressStatButtonProps) {
const { icon, label, count, onClick, isDisabled = false } = props;
function ProgressStatLabel(props: ProgressStatButtonProps) {
const { icon, label, count } = props;
return (
<button
disabled={isDisabled}
onClick={onClick}
className="group relative flex flex-1 items-center overflow-hidden rounded-md border border-gray-300 bg-white px-2 py-2 text-sm text-black transition-colors hover:border-black disabled:pointer-events-none disabled:opacity-50 sm:rounded-xl sm:px-4 sm:py-3 sm:text-base"
>
<span className="group relative flex flex-1 items-center overflow-hidden rounded-md border border-gray-300 bg-white px-2 py-2 text-sm text-black transition-colors disabled:opacity-50 sm:rounded-xl sm:px-4 sm:py-3 sm:text-base">
{icon}
<span className="flex flex-grow justify-between">
<span>{label}</span>
<span>{count}</span>
</span>
<span className="absolute left-0 right-0 top-full flex h-full items-center justify-center border border-black bg-black text-white transition-all duration-200 group-hover:top-0">
Restart Asking
</span>
</button>
</span>
);
}
@ -43,12 +34,11 @@ type QuestionFinishedProps = {
didNotKnowCount: number;
skippedCount: number;
totalCount: number;
onReset: (type: QuestionProgressType | 'reset') => void;
onReset: () => void;
};
export function QuestionFinished(props: QuestionFinishedProps) {
const { knowCount, didNotKnowCount, skippedCount, totalCount, onReset } =
props;
const { knowCount, didNotKnowCount, skippedCount, onReset } = props;
return (
<div className="relative flex flex-grow flex-col items-center justify-center px-4 sm:px-0">
@ -63,31 +53,25 @@ export function QuestionFinished(props: QuestionFinishedProps) {
</p>
<div className="mb-5 mt-5 flex w-full flex-col gap-1.5 px-2 sm:flex-row sm:gap-3 sm:px-16">
<ProgressStatButton
<ProgressStatLabel
icon={<ThumbsUp className="mr-1 h-4" />}
label="Knew"
count={knowCount}
isDisabled={knowCount === 0}
onClick={() => onReset('know')}
/>
<ProgressStatButton
<ProgressStatLabel
icon={<Sparkles className="mr-1 h-4" />}
label="Learned"
count={didNotKnowCount}
isDisabled={didNotKnowCount === 0}
onClick={() => onReset('dontKnow')}
/>
<ProgressStatButton
<ProgressStatLabel
icon={<SkipForward className="mr-1 h-4" />}
label="Skipped"
count={skippedCount}
isDisabled={skippedCount === 0}
onClick={() => onReset('skip')}
/>
</div>
<div className="mb-4 mt-2 text-sm sm:mb-0">
<button
onClick={() => onReset('reset')}
onClick={() => onReset()}
className="flex items-center gap-0.5 text-sm text-red-700 hover:text-black sm:text-base"
>
<RefreshCcw className="mr-1 h-4" />

@ -24,14 +24,14 @@ type QuestionsListProps = {
};
export function QuestionsList(props: QuestionsListProps) {
const { questions: unshuffledQuestions, groupId } = props;
const { questions: defaultQuestions, groupId } = props;
const toast = useToast();
const [questions, setQuestions] = useState(defaultQuestions);
const [isLoading, setIsLoading] = useState(true);
const [showConfetti, setShowConfetti] = useState(false);
const [questions, setQuestions] = useState<QuestionType[]>();
const [pendingQuestions, setPendingQuestions] = useState<QuestionType[]>([]);
const [currQuestionIndex, setCurrQuestionIndex] = useState(0);
const [userProgress, setUserProgress] = useState<UserQuestionProgress>();
const containerRef = useRef<HTMLDivElement>(null);
@ -57,7 +57,7 @@ export function QuestionsList(props: QuestionsListProps) {
return response;
}
async function loadQuestions() {
async function prepareProgress() {
const userProgress = await fetchUserProgress();
setUserProgress(userProgress);
@ -65,7 +65,7 @@ export function QuestionsList(props: QuestionsListProps) {
const didNotKnowQuestions = userProgress?.dontKnow || [];
const skipQuestions = userProgress?.skip || [];
const pendingQuestions = unshuffledQuestions.filter((question) => {
const pendingQuestionIndex = questions.findIndex((question) => {
return (
!knownQuestions.includes(question.id) &&
!didNotKnowQuestions.includes(question.id) &&
@ -73,31 +73,21 @@ export function QuestionsList(props: QuestionsListProps) {
);
});
// Shuffle and set pending questions
// setPendingQuestions(pendingQuestions.sort(() => Math.random() - 0.5));
setPendingQuestions(pendingQuestions);
setQuestions(unshuffledQuestions);
setCurrQuestionIndex(pendingQuestionIndex);
setIsLoading(false);
}
async function resetProgress(type: QuestionProgressType | 'reset' = 'reset') {
async function resetProgress() {
let knownQuestions = userProgress?.know || [];
let didNotKnowQuestions = userProgress?.dontKnow || [];
let skipQuestions = userProgress?.skip || [];
if (!isLoggedIn()) {
if (type === 'know') {
knownQuestions = [];
} else if (type === 'dontKnow') {
didNotKnowQuestions = [];
} else if (type === 'skip') {
skipQuestions = [];
} else if (type === 'reset') {
knownQuestions = [];
didNotKnowQuestions = [];
skipQuestions = [];
}
setQuestions(defaultQuestions);
knownQuestions = [];
didNotKnowQuestions = [];
skipQuestions = [];
} else {
setIsLoading(true);
@ -106,7 +96,7 @@ export function QuestionsList(props: QuestionsListProps) {
import.meta.env.PUBLIC_API_URL
}/v1-reset-question-progress/${groupId}`,
{
status: type,
status: 'reset',
},
);
@ -120,21 +110,13 @@ export function QuestionsList(props: QuestionsListProps) {
skipQuestions = response?.skip || [];
}
const pendingQuestions = unshuffledQuestions.filter((question) => {
return (
!knownQuestions.includes(question.id) &&
!didNotKnowQuestions.includes(question.id) &&
!skipQuestions.includes(question.id)
);
});
setCurrQuestionIndex(0);
setUserProgress({
know: knownQuestions,
dontKnow: didNotKnowQuestions,
skip: skipQuestions,
});
setPendingQuestions(pendingQuestions.sort(() => Math.random() - 0.5));
setIsLoading(false);
}
@ -173,30 +155,29 @@ export function QuestionsList(props: QuestionsListProps) {
newProgress = response;
}
const updatedQuestionList = pendingQuestions.filter(
(q) => q.id !== questionId,
);
const nextQuestionIndex = currQuestionIndex + 1;
setUserProgress(newProgress);
setPendingQuestions(updatedQuestionList);
setIsLoading(false);
if (updatedQuestionList.length === 0) {
if (!nextQuestionIndex || !questions[nextQuestionIndex]) {
setShowConfetti(true);
}
setCurrQuestionIndex(nextQuestionIndex);
}
useEffect(() => {
loadQuestions().then(() => null);
}, [unshuffledQuestions]);
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 = pendingQuestions[0];
const hasFinished = !isLoading && hasProgress && !currQuestion;
const currQuestion = questions[currQuestionIndex];
const hasFinished = !isLoading && hasProgress && currQuestionIndex === -1;
return (
<div className="mb-0 gap-3 text-center sm:mb-40">
@ -204,11 +185,37 @@ export function QuestionsList(props: QuestionsListProps) {
knowCount={knowCount}
didNotKnowCount={dontKnowCount}
skippedCount={skipCount}
totalCount={unshuffledQuestions?.length || questions?.length}
totalCount={questions?.length}
isLoading={isLoading}
showLoginAlert={!isLoggedIn() && hasProgress}
onResetClick={() => {
resetProgress('reset').finally(() => null);
resetProgress().finally(() => null);
}}
onNextClick={() => {
if (
currQuestionIndex !== -1 &&
currQuestionIndex < questions.length - 1
) {
updateQuestionStatus('skip', currQuestion.id).finally(() => null);
}
}}
onPrevClick={() => {
if (currQuestionIndex > 0) {
const prevQuestion = questions[currQuestionIndex - 1];
// remove last question from the progress of the user
const tempUserProgress = {
know:
userProgress?.know.filter((id) => id !== prevQuestion.id) || [],
dontKnow:
userProgress?.dontKnow.filter((id) => id !== prevQuestion.id) ||
[],
skip:
userProgress?.skip.filter((id) => id !== prevQuestion.id) || [],
};
setUserProgress(tempUserProgress);
setCurrQuestionIndex(currQuestionIndex - 1);
}
}}
/>
@ -228,12 +235,12 @@ export function QuestionsList(props: QuestionsListProps) {
>
{hasFinished && (
<QuestionFinished
totalCount={unshuffledQuestions?.length || questions?.length || 0}
totalCount={questions?.length || 0}
knowCount={knowCount}
didNotKnowCount={dontKnowCount}
skippedCount={skipCount}
onReset={(type: QuestionProgressType | 'reset') => {
resetProgress(type).finally(() => null);
onReset={() => {
resetProgress().finally(() => null);
}}
/>
)}

@ -1,4 +1,11 @@
import { CheckCircle, RotateCcw, SkipForward, Sparkles } from 'lucide-react';
import {
CheckCircle,
ChevronLeft,
ChevronRight,
RotateCcw,
SkipForward,
Sparkles,
} from 'lucide-react';
import { showLoginPopup } from '../../lib/popup';
type QuestionsProgressProps = {
@ -9,6 +16,8 @@ type QuestionsProgressProps = {
totalCount?: number;
skippedCount?: number;
onResetClick?: () => void;
onPrevClick?: () => void;
onNextClick?: () => void;
};
export function QuestionsProgress(props: QuestionsProgressProps) {
@ -20,6 +29,8 @@ export function QuestionsProgress(props: QuestionsProgressProps) {
totalCount = 0,
skippedCount = 0,
onResetClick = () => null,
onPrevClick = () => null,
onNextClick = () => null,
} = props;
const totalSolved = knowCount + didNotKnowCount + skippedCount;
@ -36,8 +47,22 @@ export function QuestionsProgress(props: QuestionsProgressProps) {
}}
/>
</div>
<span className="ml-3 text-sm">
{totalSolved} / {totalCount}
<span className="ml-3 flex items-center text-sm">
<button
onClick={onPrevClick}
className="text-zinc-400 hover:text-black"
>
<ChevronLeft className="h-4" strokeWidth={3} />
</button>
<span className="block min-w-[41px] text-center">
<span className="tabular-nums">{totalSolved}</span> / {totalCount}
</span>
<button
onClick={onNextClick}
className="text-zinc-400 hover:text-black"
>
<ChevronRight className="h-4" strokeWidth={3} />
</button>
</span>
</div>
@ -46,8 +71,7 @@ export function QuestionsProgress(props: QuestionsProgressProps) {
<CheckCircle className="mr-1 h-4" />
<span>Knew</span>
<span className="ml-2 rounded-md bg-gray-200/80 px-1.5 font-medium text-black">
<span className="tabular-nums">{knowCount}</span>{' '}
Items
<span className="tabular-nums">{knowCount}</span> Items
</span>
</span>
@ -55,8 +79,7 @@ export function QuestionsProgress(props: QuestionsProgressProps) {
<Sparkles className="mr-1 h-4" />
<span>Learnt</span>
<span className="ml-2 rounded-md bg-gray-200/80 px-1.5 font-medium text-black">
<span className="tabular-nums">{didNotKnowCount}</span>{' '}
Items
<span className="tabular-nums">{didNotKnowCount}</span> Items
</span>
</span>
@ -64,8 +87,7 @@ export function QuestionsProgress(props: QuestionsProgressProps) {
<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="tabular-nums">{skippedCount}</span>{' '}
Items
<span className="tabular-nums">{skippedCount}</span> Items
</span>
</span>

Loading…
Cancel
Save