From 8226d5cdcd6fc6d6aa276741bb244f778121f0a7 Mon Sep 17 00:00:00 2001 From: AnhAnNek Date: Tue, 1 Oct 2024 14:48:33 +0700 Subject: [PATCH] #2 add PreviewTest.jsx and TakeTest.jsx --- package-lock.json | 10 + package.json | 1 + src/App.js | 3 + src/components/NotFound.jsx | 43 +++++ src/components/NotFound/index.jsx | 10 - src/components/Test/Question/QuestionItem.jsx | 171 ++++++++++++++++++ src/components/Test/TestSection.jsx | 42 +++++ src/config/routes.jsx | 4 +- src/pages/PreviewTest.jsx | 64 +++++++ src/pages/TakeTest.jsx | 153 ++++++++++++++++ src/routes/routes.jsx | 4 + src/utils/constants.jsx | 9 + src/utils/methods.jsx | 6 + 13 files changed, 509 insertions(+), 11 deletions(-) create mode 100644 src/components/NotFound.jsx delete mode 100644 src/components/NotFound/index.jsx create mode 100644 src/components/Test/Question/QuestionItem.jsx create mode 100644 src/components/Test/TestSection.jsx create mode 100644 src/pages/PreviewTest.jsx create mode 100644 src/pages/TakeTest.jsx diff --git a/package-lock.json b/package-lock.json index e895457..5b59368 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,6 +20,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", @@ -14663,6 +14664,15 @@ "yallist": "^3.0.2" } }, + "node_modules/luxon": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.5.0.tgz", + "integrity": "sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, "node_modules/lz-string": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", diff --git a/package.json b/package.json index 0151ff4..89b275b 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/App.js b/src/App.js index a3cdb0f..6bb088a 100644 --- a/src/App.js +++ b/src/App.js @@ -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 ( @@ -33,6 +34,8 @@ function App() { /> ); })} + + } /> diff --git a/src/components/NotFound.jsx b/src/components/NotFound.jsx new file mode 100644 index 0000000..bffbda3 --- /dev/null +++ b/src/components/NotFound.jsx @@ -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 ( + + + 404 + + Page Not Found + + + The page you are looking for does not exist or has been moved. + + + + + + + ); +}; + +export default NotFound; diff --git a/src/components/NotFound/index.jsx b/src/components/NotFound/index.jsx deleted file mode 100644 index 34f01d2..0000000 --- a/src/components/NotFound/index.jsx +++ /dev/null @@ -1,10 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; - -NotFound.propTypes = {}; - -function NotFound(props) { - return
Not Found!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
; -} - -export default NotFound; diff --git a/src/components/Test/Question/QuestionItem.jsx b/src/components/Test/Question/QuestionItem.jsx new file mode 100644 index 0000000..ac8c9a5 --- /dev/null +++ b/src/components/Test/Question/QuestionItem.jsx @@ -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) => ( + onAnswerChange(question.id, value)} + > + + {question.options.map((option, index) => ( + + {option} + + ))} + + +); + +const renderMultiChoice = (question, selectedAnswer, onAnswerChange) => ( + onAnswerChange(question.id, value)} + > + + {question.options.map((option, index) => ( + + {option} + + ))} + + +); + +const renderTrueFalse = (question, selectedAnswer, onAnswerChange) => ( + onAnswerChange(question.id, value)} + > + + True + False + + +); + +const renderItemMatch = (question, selectedAnswer, onAnswerChange) => ( + + {question.items.map((item, index) => ( + + {item} + { + const newAnswer = [...(selectedAnswer || [])]; + newAnswer[index] = e.target.value; + onAnswerChange(question.id, newAnswer); + }} + /> + + ))} + +); + +const renderImageMatch = (question, selectedAnswer, onAnswerChange) => ( + + {question.images.map((image, index) => ( + + {`Option + onAnswerChange(question.id, value)} + > + {image.options.map((option, idx) => ( + + {option} + + ))} + + + ))} + +); + +const renderKeywords = (question, selectedAnswer, onAnswerChange) => ( + + onAnswerChange(question.id, e.target.value)} + /> + +); + +const renderFillTheGap = (question, selectedAnswer, onAnswerChange) => ( + + {question.sentence.split('__').map((part, index) => ( + + {part} + {index < question.sentence.split('__').length - 1 && ( + { + const newAnswer = [...(selectedAnswer || [])]; + newAnswer[index] = e.target.value; + onAnswerChange(question.id, newAnswer); + }} + /> + )} + + ))} + +); + +// 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 Unknown question type; + } + }; + + return ( + + {question.text} + {renderQuestion()} + + ); +}; + +export default QuestionItem; \ No newline at end of file diff --git a/src/components/Test/TestSection.jsx b/src/components/Test/TestSection.jsx new file mode 100644 index 0000000..f134bfd --- /dev/null +++ b/src/components/Test/TestSection.jsx @@ -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 ( + + + {section.title} + + + + {section.questions.map((question) => ( + + + + ))} + + + ); +}; + +export default TestSectionComponent; \ No newline at end of file diff --git a/src/config/routes.jsx b/src/config/routes.jsx index 51b3579..a5f215d 100644 --- a/src/config/routes.jsx +++ b/src/config/routes.jsx @@ -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; diff --git a/src/pages/PreviewTest.jsx b/src/pages/PreviewTest.jsx new file mode 100644 index 0000000..0f8021f --- /dev/null +++ b/src/pages/PreviewTest.jsx @@ -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 ( + + + + + {/* Test Title and Description */} + + + {test.title} + + {test.description} + + Course: {test.course.name} + + + Duration: {test.durationInMilis / 60000} minutes + + + + {/* Tabs showing each section of the test */} + + + {test.sections.map((section) => ( + {section.title} + ))} + + + + {test.sections.map((section) => ( + + + + {section.title} + + Questions in this section: + {section.questions.map((question) => ( + + {question.text} (Type: {question.type}) + + ))} + + + ))} + + + + {/* Start Test Button */} + + + +