File Extension Image for Test.jsx Test.jsx
5609 Chars | 231 Lines | 5.61 kb | Text File Parent Directory
import React, { useState, useMemo, useReducer, useRef } from 'react'

import { useShallow } from 'zustand/react/shallow'
import { usePack } from '@/stores/pack'
import { useReactToPrint } from 'react-to-print'

import { shuffle } from '@/lib/utilities'

import Button from 'react-bootstrap/Button'
import ProgressBar from 'react-bootstrap/ProgressBar'

import MultipleChoice from './Questions/MultipleChoice'
import TrueOrFalse from './Questions/TrueOrFalse'
import Written from './Questions/Written'

// We have to use a reducer since the next state depends on the previous state,
// and state updates are called at the same time (submit)

const initialState = { answers: {} }

function reducer(state, action) {
	let answers = { ...state.answers, [action.payload.i]: action.payload.val }
	let arr = Object.values(answers)
	let sum = arr.reduce((a, b) => a + b)
	let right = (sum / arr.length) * 100
	let wrong = ((arr.length - sum) / arr.length) * 100
	return {
		answers: answers,
		right: right,
		wrong: wrong,
		fraction: `${sum}/${arr.length}`,
		percentage: Math.round((sum / arr.length) * 100 * 100) / 100,
	}
}

export function Test({ questions, paper }) {
	const [content] = usePack(useShallow((state) => [state.pack.content]))
	const [submit, setSubmit] = useState(false)
	const [answers, dispatch] = useReducer(reducer, initialState)

	// Only randomize the questions once
	const randomContent = useMemo(() => shuffle(content), [questions])
	const questionList = useMemo(() => questionGenerator(), [submit, randomContent])

	const printRef = useRef()
	const print = useReactToPrint({
		content: () => printRef.current,
	})

	function questionGenerator(submitQ = submit, interactive = false) {
		return randomContent.slice(0, questions.total).map((card, i) => {
			// TODO: Fix repetition
			if (i < questions.mcq) {
				return (
					<div key={i}>
						<MultipleChoice
							i={i}
							submit={submitQ}
							save={
								interactive
									? (val) => dispatch({ payload: { i: i, val: val } })
									: () => {}
							}
						/>
					</div>
				)
			} else if (i < questions.mcq + questions.written) {
				return (
					<div key={i}>
						<Written
							key={i}
							i={i}
							submit={submitQ}
							save={
								interactive
									? (val) => dispatch({ payload: { i: i, val: val } })
									: () => {}
							}
						/>
						{!interactive && <div className="page-break" />}
					</div>
				)
			} else if (i < questions.mcq + questions.written + questions.tfq) {
				return (
					<div key={i}>
						<TrueOrFalse
							key={i}
							i={i}
							submit={submitQ}
							save={
								interactive
									? (val) => dispatch({ payload: { i: i, val: val } })
									: () => {}
							}
						/>
						{!interactive && <div className="page-break" />}
					</div>
				)
			} else if (i < questions.mcq + questions.written + questions.tfq + questions.hmcq) {
				return (
					<div key={i}>
						<MultipleChoice
							key={i}
							i={i}
							submit={submitQ}
							hard
							save={
								interactive
									? (val) => dispatch({ payload: { i: i, val: val } })
									: () => {}
							}
						/>
						{!interactive && <div className="page-break" />}
					</div>
				)
			}
		})
	}

	if (paper) {
		return (
			<>
				<style type="text/css" media="print">
					{`
					@media all {
						.page-break {
							display: none;
						}
					}
					
					@media print {
						html,
						body {
							height: initial !important;
							overflow: initial !important;
							print-color-adjust: exact;
						}
						@page {
							size: 210mm 1250mm;
						}
					}
					
					@media print {
						.page-break {
							margin-top: 1rem;
							display: block;
							page-break-before: auto;
						}
					}
					
					@page {
						size: auto;
						margin: 20mm;
					}
					`}
				</style>
				<div className="d-flex mb-4">
					<Button size="lg" className="mx-auto" onClick={print}>
						Print Test
					</Button>
				</div>
				<div ref={printRef}>
					{questionGenerator(false)}
					<hr />
					{[...Array(25)].map((_, i) => (
						<div key={i}>
							<h5
								className="text-center"
								style={{
									color: `rgb(${255 - Math.random() * 100}, ${
										255 - Math.random() * 100
									}, ${255 - Math.random() * 100})`,
								}}
							>
								TEST FINISHED
							</h5>
							<hr />
						</div>
					))}
					{questionGenerator(true)}
				</div>
			</>
		)
	} else {
		return (
			<>
				{questionList}
				<Button
					className="mb-3 mx-auto d-block"
					variant="primary"
					size="lg"
					onClick={() => setSubmit(true)}
					disabled={submit}
				>
					Submit
				</Button>
				{submit && (
					<div className="text-center">
						<hr />
						{answers.percentage < 75 ? (
							<p>Use the Blitz or Master mode to guarantee memorization!</p>
						) : (
							<>
								<h1>🎉</h1>
								<h2>You got {answers.percentage} percent!</h2>
							</>
						)}
						<hr className="mb-3" />
						<h3>{answers.fraction}</h3>
						<ProgressBar className="mb-3">
							<ProgressBar
								striped
								animated
								variant="success"
								now={answers.right}
								key={1}
							/>
							<ProgressBar
								striped
								animated
								variant="danger"
								now={answers.wrong}
								key={2}
							/>
						</ProgressBar>
					</div>
				)}
			</>
		)
	}
}