Skip to content

Commit

Permalink
#2 add PreviewTest.jsx and TakeTest.jsx
Browse files Browse the repository at this point in the history
  • Loading branch information
AnhAnNek committed Oct 1, 2024
1 parent cc7fed2 commit 8226d5c
Show file tree
Hide file tree
Showing 13 changed files with 509 additions and 11 deletions.
10 changes: 10 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"babel-plugin-module-resolver": "^5.0.0",
"classnames": "^2.5.1",
"framer-motion": "^11.3.8",
"luxon": "^3.5.0",
"query-string": "^9.1.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
Expand Down
3 changes: 3 additions & 0 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { publicRoutes } from '~/routes';
import DefaultLayout from '~/layouts/DefaultLayout';
import { ChakraProvider } from '@chakra-ui/react';
import customTheme from '~/themes/customTheme';
import NotFound from '~/components/NotFound';

function App() {
return (
Expand Down Expand Up @@ -33,6 +34,8 @@ function App() {
/>
);
})}

<Route path="*" element={<NotFound />} />
</Routes>
</div>
</ChakraProvider>
Expand Down
43 changes: 43 additions & 0 deletions src/components/NotFound.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React from 'react';
import { Link } from 'react-router-dom';
import { Box, Heading, Text, Button, VStack } from '@chakra-ui/react';

const NotFound = () => {
return (
<Box
display="flex"
justifyContent="center"
alignItems="center"
minH="100vh"
bg="white"
>
<VStack spacing={6} textAlign="center">
<Heading
as="h1"
size="4xl"
color="cyan.600"
>404</Heading>
<Text fontSize="lg" fontWeight="bold" color="cyan.500">
Page Not Found
</Text>
<Text color="cyan.500" fontSize="lg">
The page you are looking for does not exist or has been moved.
</Text>
<Link to="/">
<Button
colorScheme="cyan"
bg="cyan.400"
_hover={{ bg: "cyan.500" }}
color="white"
variant="solid"
size="lg"
>
Go Back to Home
</Button>
</Link>
</VStack>
</Box>
);
};

export default NotFound;
10 changes: 0 additions & 10 deletions src/components/NotFound/index.jsx

This file was deleted.

171 changes: 171 additions & 0 deletions src/components/Test/Question/QuestionItem.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
import React from 'react';
import {
Box,
Radio,
RadioGroup,
Checkbox,
CheckboxGroup,
VStack,
Text,
Image,
Input,
} from '@chakra-ui/react';
import { QUESTION_TYPES } from '~/utils/constants';

class TestQuestion {
constructor(id, text, options, type, items = [], images = [], sentence = "") {
this.id = id;
this.text = text;
this.options = options;
this.type = type;
this.items = items;
this.images = images;
this.sentence = sentence;
}
}

// Helper functions for each question type
const renderSingleChoice = (question, selectedAnswer, onAnswerChange) => (
<RadioGroup
value={selectedAnswer || ''}
onChange={(value) => onAnswerChange(question.id, value)}
>
<VStack align="start">
{question.options.map((option, index) => (
<Radio key={index} value={option}>
{option}
</Radio>
))}
</VStack>
</RadioGroup>
);

const renderMultiChoice = (question, selectedAnswer, onAnswerChange) => (
<CheckboxGroup
value={selectedAnswer || []}
onChange={(value) => onAnswerChange(question.id, value)}
>
<VStack align="start">
{question.options.map((option, index) => (
<Checkbox key={index} value={option}>
{option}
</Checkbox>
))}
</VStack>
</CheckboxGroup>
);

const renderTrueFalse = (question, selectedAnswer, onAnswerChange) => (
<RadioGroup
value={selectedAnswer || ''}
onChange={(value) => onAnswerChange(question.id, value)}
>
<VStack align="start">
<Radio value="True">True</Radio>
<Radio value="False">False</Radio>
</VStack>
</RadioGroup>
);

const renderItemMatch = (question, selectedAnswer, onAnswerChange) => (
<VStack align="start">
{question.items.map((item, index) => (
<Box key={index}>
<Text>{item}</Text>
<Input
placeholder="Enter corresponding match"
value={selectedAnswer ? selectedAnswer[index] : ''}
onChange={(e) => {
const newAnswer = [...(selectedAnswer || [])];
newAnswer[index] = e.target.value;
onAnswerChange(question.id, newAnswer);
}}
/>
</Box>
))}
</VStack>
);

const renderImageMatch = (question, selectedAnswer, onAnswerChange) => (
<VStack align="start">
{question.images.map((image, index) => (
<Box key={index}>
<Image src={image.src} alt={`Option ${index}`} />
<RadioGroup
value={selectedAnswer}
onChange={(value) => onAnswerChange(question.id, value)}
>
{image.options.map((option, idx) => (
<Radio key={idx} value={option}>
{option}
</Radio>
))}
</RadioGroup>
</Box>
))}
</VStack>
);

const renderKeywords = (question, selectedAnswer, onAnswerChange) => (
<VStack align="start">
<Input
placeholder="Enter keywords"
value={selectedAnswer || ''}
onChange={(e) => onAnswerChange(question.id, e.target.value)}
/>
</VStack>
);

const renderFillTheGap = (question, selectedAnswer, onAnswerChange) => (
<VStack align="start">
{question.sentence.split('__').map((part, index) => (
<span key={index}>
{part}
{index < question.sentence.split('__').length - 1 && (
<Input
placeholder="Fill the gap"
value={selectedAnswer ? selectedAnswer[index] : ''}
onChange={(e) => {
const newAnswer = [...(selectedAnswer || [])];
newAnswer[index] = e.target.value;
onAnswerChange(question.id, newAnswer);
}}
/>
)}
</span>
))}
</VStack>
);

// Main component
const QuestionItem = ({ question, onAnswerChange, selectedAnswer }) => {
const renderQuestion = () => {
switch (question.type) {
case QUESTION_TYPES.SINGLE_CHOICE:
return renderSingleChoice(question, selectedAnswer, onAnswerChange);
case QUESTION_TYPES.MULTI_CHOICE:
return renderMultiChoice(question, selectedAnswer, onAnswerChange);
case QUESTION_TYPES.TRUE_FALSE:
return renderTrueFalse(question, selectedAnswer, onAnswerChange);
case QUESTION_TYPES.ITEM_MATCH:
return renderItemMatch(question, selectedAnswer, onAnswerChange);
case QUESTION_TYPES.IMAGE_MATCH:
return renderImageMatch(question, selectedAnswer, onAnswerChange);
case QUESTION_TYPES.KEYWORDS:
return renderKeywords(question, selectedAnswer, onAnswerChange);
case QUESTION_TYPES.FILL_THE_GAP:
return renderFillTheGap(question, selectedAnswer, onAnswerChange);
default:
return <Text>Unknown question type</Text>;
}
};

return (
<Box borderWidth="1px" borderRadius="lg" p={4} w="100%">
<Text fontWeight="bold">{question.text}</Text>
{renderQuestion()}
</Box>
);
};

export default QuestionItem;
42 changes: 42 additions & 0 deletions src/components/Test/TestSection.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React from 'react';
import { Box, VStack, Text } from '@chakra-ui/react';
import QuestionItem from '~/components/Test/Question/QuestionItem';

class TestSection {
constructor(id, title, questions) {
this.id = id;
this.title = title;
this.questions = questions;
}
}

const TestSectionComponent = ({ section, onAnswerChange, answers }) => {
return (
<Box>
<Text fontSize="2xl" fontWeight="bold">
{section.title}
</Text>

<VStack spacing={6} w="100%">
{section.questions.map((question) => (
<Box
key={question.id}
w="100%"
borderWidth="1px"
borderRadius="lg"
p={4}
mb={4}
>
<QuestionItem
question={question}
selectedAnswer={answers[question.id]}
onAnswerChange={onAnswerChange}
/>
</Box>
))}
</VStack>
</Box>
);
};

export default TestSectionComponent;
4 changes: 3 additions & 1 deletion src/config/routes.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ const routes = {
user_profile_edit: '/profile',
otp_validation: '/otp-validation',
course_management_for_student: '/course-management-for-student',
notifications_for_student: '/notifications-for-student'
notifications_for_student: '/notifications-for-student',
preview_test: '/preview-test',
take_test: '/take-test'
};

export default routes;
64 changes: 64 additions & 0 deletions src/pages/PreviewTest.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import React from 'react';
import { Box, VStack, Text, Button, Tabs, TabList, TabPanels, Tab, TabPanel } from '@chakra-ui/react';
import NavbarForStudent from '~/components/Navbars/NavbarForStudent';
import Footer from '~/components/Footer';

const PreviewTest = ({ test, onStart }) => {
return (
<Box>
<NavbarForStudent />

<Box p={4}>
{/* Test Title and Description */}
<VStack spacing={4} align="stretch">
<Text fontSize="2xl" fontWeight="bold">
{test.title}
</Text>
<Text>{test.description}</Text>
<Text fontSize="lg" color="gray.600">
Course: {test.course.name}
</Text>
<Text fontSize="lg" color="gray.600">
Duration: {test.durationInMilis / 60000} minutes
</Text>
</VStack>

{/* Tabs showing each section of the test */}
<Tabs variant="soft-rounded" mt="50px">
<TabList>
{test.sections.map((section) => (
<Tab key={section.id}>{section.title}</Tab>
))}
</TabList>

<TabPanels>
{test.sections.map((section) => (
<TabPanel key={section.id}>
<VStack spacing={4} align="stretch">
<Text fontSize="xl" fontWeight="bold">
{section.title}
</Text>
<Text color="gray.600">Questions in this section:</Text>
{section.questions.map((question) => (
<Text key={question.id}>
{question.text} (Type: {question.type})
</Text>
))}
</VStack>
</TabPanel>
))}
</TabPanels>
</Tabs>

{/* Start Test Button */}
<Button colorScheme="blue" size="lg" mt={6} onClick={onStart}>
Start Test
</Button>
</Box>

<Footer />
</Box>
);
};

export default PreviewTest;
Loading

0 comments on commit 8226d5c

Please sign in to comment.