Skip to content

Commit

Permalink
feat: handling single choice quiz questions correctly
Browse files Browse the repository at this point in the history
  • Loading branch information
MarcoEscaleira committed Mar 16, 2024
1 parent 7867c51 commit e75234e
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 68 deletions.
50 changes: 28 additions & 22 deletions src/components/QuizAttempt/QuizAttempt.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { QuizAttemptCancelDialog } from "@components/QuizAttemptCancelDialog/Qui
import { QuestionType, QuizByIdQuery } from "@generated/graphql.ts";
import { useAttemptStore } from "@state/attemptStore.ts";
import { useUserStore } from "@state/userStore.ts";
import { BREAKPOINTS, COLOURS } from "@utils/constants.ts";
import { BREAKPOINTS } from "@utils/constants.ts";

interface QuizAttemptProps {
quiz: QuizByIdQuery["quizById"];
Expand All @@ -29,6 +29,8 @@ export function QuizAttempt({ quiz }: QuizAttemptProps) {
submitAttempt,
resetAttempt,
setQuestionResponse,
goToPreviousQuestion,
goToNextQuestion,
} = useAttemptStore();

useEffect(() => () => resetAttempt(), []);
Expand Down Expand Up @@ -62,14 +64,6 @@ export function QuizAttempt({ quiz }: QuizAttemptProps) {
if (quizTimeLimit > 0) start();
};

const handleOptionSelection = (optionName: string) => {
setQuestionResponse(optionName);

if (isLastQuestion) {
submitAttempt(quiz.id, navigate);
}
};

if (!isAttemptRunning) {
return (
<Button variant="gradient" className="mt-2 md:mt-6" color="green" fullWidth onClick={handleStartQuiz}>
Expand Down Expand Up @@ -124,40 +118,52 @@ export function QuizAttempt({ quiz }: QuizAttemptProps) {

<div className="mt-10 flex flex-wrap gap-5 sm:gap-7">
{question.type === QuestionType.Single &&
question.options.map(({ text }, index) => (
question.options.map(({ text, chosen }) => (
<Button
key={text}
fullWidth
variant="outlined"
color={COLOURS[index]}
onClick={() => {
handleOptionSelection(text);
}}
variant={chosen ? "filled" : "outlined"}
color="blue-gray"
onClick={() => setQuestionResponse(text)}
>
{text}
</Button>
))}

{question.type === QuestionType.Multi &&
question.options.map(({ text }, index) => (
question.options.map(({ text }) => (
<Button
key={text}
fullWidth
variant="outlined"
color={COLOURS[index]}
onClick={() => {
handleOptionSelection(text);
}}
color="blue-gray"
onClick={() => setQuestionResponse(text)}
>
{text}
</Button>
))}
</div>
<div className="mt-12 flex w-full justify-between">
<Button variant="outlined" color="blue-gray">
<Button variant="outlined" color="gray" disabled={currentQuestion === 0} onClick={goToPreviousQuestion}>
Previous
</Button>
<Button color="blue">Next</Button>
{isLastQuestion ? (
<Button
variant="gradient"
color="green"
onClick={() => {
// TODO: Confirm if all questions were answered and if the user wants to submit the attempt
// TODO: Handle attempt submission
submitAttempt(quiz.id, navigate);
}}
>
Finish
</Button>
) : (
<Button color="blue" onClick={goToNextQuestion}>
Next
</Button>
)}
</div>
</section>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/pages/Quiz/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export function Component() {
<section className="container">
<Accordion open={quizAccordion === 1}>
<AccordionHeader className="py-3 outline-none" onClick={() => handleQuizAccordion(1)}>
<div className="flex w-full items-center justify-between md:justify-start md:gap-3">
<div className="flex w-full items-center justify-between">
<Typography className="font-medium">Quiz information</Typography>
{role === Roles.Admin && (
<Button
Expand Down
4 changes: 2 additions & 2 deletions src/pages/QuizResult/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -130,12 +130,12 @@ export function Component() {

<div className="mb-5 flex flex-wrap gap-3">
{type === QuestionType.Single &&
options.map(({ text, correct, chosen }, index) => (
options.map(({ text, correct, chosen }) => (
<Button
key={text}
fullWidth
variant={handleOptionVariant(correct, !!chosen)}
color={handleOptionColor(correct, !!chosen, index)}
color={handleOptionColor(correct, !!chosen)}
disabled
>
{text}
Expand Down
6 changes: 2 additions & 4 deletions src/pages/QuizResult/utils.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import { COLOURS } from "@utils/constants";

export const handleOptionColor = (correct: boolean, chosen: boolean, index: number) => {
export const handleOptionColor = (correct: boolean, chosen: boolean) => {
if (chosen && !correct) return "red";
if (correct) return "green";
return COLOURS[index];
return "blue-gray";
};

export const handleOptionVariant = (correct: boolean, chosen: boolean) => {
Expand Down
59 changes: 44 additions & 15 deletions src/state/attemptStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { toast } from "react-toastify";
import { create } from "zustand";
import { devtools } from "zustand/middleware";
import type {} from "@redux-devtools/extension";
import { QuizByIdQuery } from "@generated/graphql.ts";
import { QuestionType, QuizByIdQuery } from "@generated/graphql.ts";
import { useUserStore } from "@state/userStore.ts";
import { apolloClient } from "@utils/apolloSetup.ts";
import { GET_QUIZ_ATTEMPTS } from "@utils/queries/QuizAttempts.ts";
Expand All @@ -24,6 +24,8 @@ interface AttemptState {
toggleCancelQuizDialog: () => void;
quizAccordion: number;
handleQuizAccordion: (value: number) => void;
goToPreviousQuestion: () => void;
goToNextQuestion: () => void;
}

export const useAttemptStore = create<AttemptState>()(
Expand All @@ -33,6 +35,9 @@ export const useAttemptStore = create<AttemptState>()(
questions: [],
currentQuestion: 0,
startTime: "",
isCancelQuizDialogOpen: false,
quizAccordion: 1,

startAttempt: (questions, startTime) =>
setState(({ handleQuizAccordion }) => {
handleQuizAccordion(0);
Expand All @@ -45,6 +50,7 @@ export const useAttemptStore = create<AttemptState>()(
startTime,
};
}),

submitAttempt: async (quizId, navigate) => {
const { questions, startTime } = getState();
const endTime = new Date().toISOString();
Expand All @@ -66,32 +72,41 @@ export const useAttemptStore = create<AttemptState>()(
toast.error("Failed to submit the quiz attempt. Please try again.");
}
},

setQuestionResponse: chosenOption => {
const { questions, currentQuestion } = getState();

const isSingleChoice = questions[currentQuestion].type === QuestionType.Single;
const isMultipleChoice = questions[currentQuestion].type === QuestionType.Multi;

const mappedQuestions = questions.map((question, index) => {
if (index === currentQuestion) {
// Update the option chosen
return {
...question,
options: question.options.map(option => {
if (option.text === chosenOption) {
if (isSingleChoice) {
return {
...question,
options: question.options.map(option => {
if (option.text === chosenOption) {
return {
...option,
chosen: true,
};
}
return {
...option,
chosen: true,
chosen: false,
};
}
return option;
}),
};
}
}),
};
}

if (isMultipleChoice) {
}
}
return question;
});

return setState(() => ({
questions: mappedQuestions,
currentQuestion: currentQuestion + 1,
}));
},
resetAttempt: () =>
Expand All @@ -102,12 +117,26 @@ export const useAttemptStore = create<AttemptState>()(
startTime: "",
quizAccordion: 1,
})),
isCancelQuizDialogOpen: false,

toggleCancelQuizDialog: () =>
setState(({ isCancelQuizDialogOpen }) => ({ isCancelQuizDialogOpen: !isCancelQuizDialogOpen })),
quizAccordion: 1,

handleQuizAccordion: value =>
setState(({ quizAccordion }) => ({ quizAccordion: quizAccordion === value ? 0 : value })),

goToPreviousQuestion: () => {
const { currentQuestion } = getState();
if (currentQuestion > 0) {
return setState(() => ({ currentQuestion: currentQuestion - 1 }));
}
},

goToNextQuestion: () => {
const { currentQuestion, questions } = getState();
if (currentQuestion < questions.length - 1) {
return setState(() => ({ currentQuestion: currentQuestion + 1 }));
}
},
}),
{
name: "attempt-storage",
Expand Down
24 changes: 0 additions & 24 deletions src/utils/constants.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,4 @@
import { colors } from "@material-tailwind/react/types/generic";

export const BREAKPOINTS = { mobile: 0, tablet: 768, desktop: 1280 };

export const DATE_TIME = "yyyy-MM-dd hh:mm";
export const DATE_TIME_READ = "dd MMM yyyy hh:mm";

export const COLOURS: colors[] = [
"purple",
"indigo",
"blue",
"orange",
"brown",
"yellow",
"blue-gray",
"gray",
"lime",
"deep-orange",
"amber",
"light-green",
"cyan",
"light-blue",
"deep-purple",
"teal",
"pink",
"green",
"red",
];

0 comments on commit e75234e

Please sign in to comment.