Skip to content

Instantly share code, notes, and snippets.

@gauravpan
Created January 10, 2022 17:06
Show Gist options
  • Select an option

  • Save gauravpan/8bc490b73728a3e86161687e3055646b to your computer and use it in GitHub Desktop.

Select an option

Save gauravpan/8bc490b73728a3e86161687e3055646b to your computer and use it in GitHub Desktop.
MCQ Test Page in next js page
import Head from "next/head";
import { HiClock } from "react-icons/hi";
import {
Box,
Button,
Container,
Heading,
HStack,
Radio,
RadioGroup,
Spacer,
Stack,
BoxProps,
Icon,
useToast,
} from "@chakra-ui/react";
import React, { FC, ReactNode, useEffect, useState } from "react";
import { AppPage } from "../../types";
import { AnimatePresence, motion } from "framer-motion";
const MotionBox = motion<BoxProps>(Box);
const item = {
question: "This is a question.",
a: "a",
b: "b",
c: "c",
d: "d",
id: 1,
currentCount: 1,
};
const list: typeof item[] = Array(200)
.fill(item)
.map((item, i) => ({
...item,
currentCount: i,
id: i,
}));
const Home: AppPage = () => {
const [mcq, setmcq] = React.useState(list[0]);
return (
<div>
<Head>
<title>Test</title>
<meta name="description" content="" />
<link rel="icon" href="/favicon.ico" />
</Head>
<TestHeader />
<Box h="8" />
<Box pos="relative" overflowX={"hidden"} h="50vh">
<AnimatePresence initial={false}>
<ListItem
totalCount={list.length}
{...mcq}
key={mcq.id}
onNext={(selectedOption, currentCount) =>
setmcq(() => list[currentCount + 1])
}
onSkip={(currentCount) => {
setmcq(() => list[currentCount + 1]);
}}
/>
</AnimatePresence>
</Box>
</div>
);
};
const timeOutDate = new Date().getTime() / 1000 + 30 * 60;
const TestHeader = () => {
const [timeLeft, setTimeLeft] = useState<string | undefined>(undefined);
useEffect(() => {
const updateTime = () => {
let timestart = new Date().getTime() / 1000;
let diff = timeOutDate - timestart;
let readableDiff = `${(diff / 60).toFixed(0)}:${(diff % 60).toFixed(0)}`;
setTimeLeft(() => readableDiff);
console.log({ readableDiff });
};
let interval = window.setInterval(updateTime, 1000);
return () => window.clearInterval(interval);
}, []);
return (
<HStack h="16">
<Heading size="lg">Scholarship test</Heading>
<Spacer />
<HStack fontSize="lg" color="gray.700">
<Icon as={HiClock} boxSize="6" />
<Box> {timeLeft}</Box>
</HStack>
</HStack>
);
};
type listItemProps = {
question: string;
a: string;
b: string;
c: string;
d: string;
totalCount: number;
currentCount: number;
onNext: (selectedOption: string, currentCount: number) => void;
onSkip: (currentCount: number) => void;
};
const ListItem = ({
totalCount,
a,
b,
c,
d,
question,
onNext,
onSkip,
currentCount,
}: listItemProps) => {
const [value, setValue] = React.useState<string | undefined>(undefined);
const isThereNextItem = currentCount + 1 !== totalCount;
const toast = useToast();
useEffect(() => {
let timeOut: number;
if (isThereNextItem) {
timeOut = window.setTimeout(() => {
onSkip(currentCount);
toast({
title: "Sorry, time out!",
description: "Only 5 seconds for a question.",
});
}, 5 * 1000);
}
return () => window.clearTimeout(timeOut);
}, []);
return (
<MotionBox
pos="absolute"
w="full"
className="box"
animate={{ left: 0 }}
initial={{ left: `100%` }}
exit={{ left: `-100%` }}
>
<HStack bg="gray.50" rounded={"md"} p="3">
<Box
color="gray.600"
fontWeight={"medium"}
fontSize={"sm"}
alignSelf={"flex-start"}
>
{currentCount}/{totalCount}
</Box>
<Box
flex={"1"}
borderLeftWidth={"thick"}
borderLeftRadius={"md"}
px="3"
>
<Heading size={"md"}>{question}</Heading>
<RadioGroup onChange={setValue} value={value}>
<Stack py="3">
<Radio py="3" _hover={{ bg: "gray.100" }} value="1">
{a}
</Radio>
<Radio py="3" _hover={{ bg: "gray.100" }} value="2">
{b}
</Radio>
<Radio py="3" _hover={{ bg: "gray.100" }} value="3">
{c}
</Radio>
<Radio py="3" _hover={{ bg: "gray.100" }} value="4">
{d}
</Radio>
</Stack>
</RadioGroup>
<HStack>
<Spacer />
<Button
minW="20"
onClick={() => onSkip(currentCount)}
disabled={!isThereNextItem}
>
Skip
</Button>
<Button
minW="20"
onClick={() => {
if (value) onNext(value, currentCount);
}}
disabled={!value || !isThereNextItem}
>
Next
</Button>
</HStack>
</Box>
</HStack>
</MotionBox>
);
};
Home.getLayout = (page: ReactNode) => <Layout> {page} </Layout>;
const Layout: FC = ({ children }) => {
return (
<Box bg={"gray.200"} h="100vh" overflow={"auto"}>
<Box h="16"></Box>
<Container maxW={"container.lg"} py="6" px="2">
{children}
</Container>
</Box>
);
};
export default Home;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment