first commit

This commit is contained in:
ryanwong
2022-11-26 01:33:31 -05:00
commit f97be44d41
40 changed files with 28697 additions and 0 deletions
Vendored
BIN
View File
Binary file not shown.
+9
View File
@@ -0,0 +1,9 @@
# frontend task 1
Update the Quiz Question type to match the png screenshots in the root of repo.
Make a result page after last question showing the questions and answers summary page.
Add a route /admin. Open wireframe.pdf. Implement this wireframe on /admin route.
This need to be completed at end of day.
Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 309 KiB

+28026
View File
File diff suppressed because it is too large Load Diff
+41
View File
@@ -0,0 +1,41 @@
{
"name": "react task",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^5.16.2",
"@testing-library/react": "^12.1.2",
"@testing-library/user-event": "^13.5.0",
"bootstrap": "^5.1.3",
"react": "^17.0.2",
"react-bootstrap": "^2.1.2",
"react-dom": "^17.0.2",
"react-scripts": "5.0.0",
"react-select": "^5.2.2",
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

+50
View File
@@ -0,0 +1,50 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<!-- bootstrap stylessheet -->
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"
integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3"
crossorigin="anonymous"
/>
<title>Quiz App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

+25
View File
@@ -0,0 +1,25 @@
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}
+3
View File
@@ -0,0 +1,3 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

BIN
View File
Binary file not shown.
+42
View File
@@ -0,0 +1,42 @@
.App {
text-align: center;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
.App-logo {
height: 40vmin;
pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}
.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
.App-link {
color: #61dafb;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
+15
View File
@@ -0,0 +1,15 @@
import "./App.css";
import Home from "./pages/Home/Home";
import AuthProvider from "./context/AuthProvider";
function App() {
return (
<div className="App">
<AuthProvider>
<Home></Home>
</AuthProvider>
</div>
);
}
export default App;
+8
View File
@@ -0,0 +1,8 @@
import { render, screen } from '@testing-library/react';
import App from './App';
test('renders learn react link', () => {
render(<App />);
const linkElement = screen.getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();
});
+23
View File
@@ -0,0 +1,23 @@
import React from "react";
import useAuth from "../../hooks/useAuth";
const Description = (props) => {
const { question } = props.quiz;
const { handleSimpleInput } = useAuth();
return (
<div className=" p-2 ">
<h4 className=" mb-5">{question}</h4>
<textarea
placeholder="description"
onChange={handleSimpleInput}
className="w-100"
style={{ height: "100px" }}
required
type="text"
/>
</div>
);
};
export default Description;
+23
View File
@@ -0,0 +1,23 @@
import React from "react";
import useAuth from "../../hooks/useAuth";
import styles from "./LongText.module.css";
const LongText = (props) => {
const { question } = props.quiz;
const { handleSimpleInput } = useAuth();
return (
<div className=" p-2 ">
<h4 className=" text-success">{question}</h4>
<input
placeholder="long text"
onChange={handleSimpleInput}
className={styles.LongTextInput}
required
type="text"
/>
</div>
);
};
export default LongText;
@@ -0,0 +1,3 @@
.LongTextInput{
width: 500px;
}
@@ -0,0 +1,29 @@
import React from "react";
import useAuth from "../../hooks/useAuth";
import styles from "./MultipleChoice.module.css";
const MultipleChoice = (props) => {
const { question, answers } = props.quiz;
const { handleSimpleInput } = useAuth();
return (
<div>
<div className={styles.MultipleChoice}>
<div className="py-2 h5">
<b>{question}</b>
</div>
<select onChange={handleSimpleInput} multiple className={styles.select}>
{answers.map((ans) => (
<option
className="d-block mx-2 border-2 border my-3 "
value={ans.answer}
label={ans.answer}
key={ans.id}
/>
))}
</select>
</div>
</div>
);
};
export default MultipleChoice;
@@ -0,0 +1,19 @@
.MultipleChoice {
width: 100%;
}
.select {
min-height: 200px;
border: transparent;
background-color: transparent;
overflow-y: auto;
width: 400px;
}
.select:focus {
border: transparent;
outline: transparent;
}
.select option {
padding: 5px 10px;
/* background-color: tomato; */
background-color: white;
}
@@ -0,0 +1,39 @@
import React, { useState } from "react";
import Select from "react-select";
import useAuth from "../../hooks/useAuth";
const MultipleSelectionChoice = (props) => {
const { question, answers } = props.quiz;
const { answeredQuiz, currentQuestion } = useAuth();
//state for multiple selection
const [selectedCategories, setSelectedCategories] = useState([]);
//
const handleAnswer = (e) => {
const updateCategory = selectedCategories.map((category) => category.value);
// updating the answer for the current quiz
answeredQuiz[currentQuestion].correct_answer = updateCategory;
};
// handeling the changes for multiple choices
const handleChange = (selectedCategories) => {
setSelectedCategories(selectedCategories);
};
return (
<div>
<h4 className="text-primary mb-3">{question}</h4>
<Select
onBlur={handleAnswer}
isMulti
value={selectedCategories}
name="categories"
onChange={handleChange}
options={answers}
className="mb-2 rounded border-2 mt-1"
/>
</div>
);
};
export default MultipleSelectionChoice;
+19
View File
@@ -0,0 +1,19 @@
import React from 'react';
import useAuth from '../../hooks/useAuth';
const ShortAnswer = (props) => {
const {question}= props.quiz;
const { answeredQuiz, setAnsweredQuiz,currentQuestion} = useAuth();
const handleAnswer=(e)=>{
answeredQuiz[currentQuestion].correct_answer= e.target.value;
}
return (
<div className=' p-2 '>
<h4 className=' text-danger' >{question}</h4>
<input placeholder='short answer' onChange={handleAnswer} className='border-0 rounded mt-2 text-start' required type="text" />
</div>
);
};
export default ShortAnswer;
+24
View File
@@ -0,0 +1,24 @@
import React from "react";
import useAuth from "../../hooks/useAuth";
import styles from "./TrueFalse.module.css";
const TrueFalse = (props) => {
const { question } = props.quiz;
const { handleSimpleInput } = useAuth();
return (
<div className=" p-2 ">
<h4 className=" mb-4">{question}</h4>
<select onChange={handleSimpleInput} multiple className={styles.select}>
<option className="d-block mx-2 border-2 border my-3" value="True">
True
</option>
<option className="d-block mx-2 border-2 border my-3" value="False">
False
</option>
</select>
</div>
);
};
export default TrueFalse;
@@ -0,0 +1,24 @@
.MultipleChoice{
width: 100%;
}
.select{
min-height: 200px;
border: transparent;
background-color: transparent;
overflow-y: auto;
}
.select:focus{
border: transparent;
outline: transparent;
}
.select option{
padding: 5px 10px;
/* background-color: tomato; */
background-color: white;
border: transparent;
border-radius: 10px;
font-weight: bold;
color: tomato;
}
+19
View File
@@ -0,0 +1,19 @@
import React from 'react';
import { createContext } from 'react';
import useQuizes from '../hooks/useQuizes';
export const AuthContext = createContext();
const AuthProvider = ({ children }) => {
const allContents = useQuizes();
return (
<div>
<AuthContext.Provider value={allContents}>
{children}
</AuthContext.Provider>
</div>
);
};
export default AuthProvider;
+8
View File
@@ -0,0 +1,8 @@
import { useContext } from 'react';
import { AuthContext } from '../context/AuthProvider';
const useAuth = () => {
return useContext(AuthContext);
};
export default useAuth;
+32
View File
@@ -0,0 +1,32 @@
import { useState } from "react";
import { quiz } from "../main";
const useQuizes = () => {
//
const [currentQuestion, setCurrentQuestion] = useState(0);
const [answeredQuiz, setAnsweredQuiz] = useState(quiz);
// taking value from each input and updating the correct answer from quiz
const handleSimpleInput = (e) => {
answeredQuiz[currentQuestion].correct_answer = e.target.value;
};
// ------------------------------
// allAnswers holds the correct answers to all questions
const handleSubmit = () => {
const allAnswers = answeredQuiz.map((answer) => answer.correct_answer);
console.log(allAnswers);
};
// ------------------------------
return {
currentQuestion,
setCurrentQuestion,
answeredQuiz,
setAnsweredQuiz,
handleSimpleInput,
handleSubmit,
};
};
export default useQuizes;
+13
View File
@@ -0,0 +1,13 @@
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}
+17
View File
@@ -0,0 +1,17 @@
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3"><g fill="#61DAFB"><path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/><circle cx="420.9" cy="296.5" r="45.7"/><path d="M520.5 78.1z"/></g></svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

+55
View File
@@ -0,0 +1,55 @@
export let quiz = [
{
id: 1,
type: "short_answer",
question: "Why is the sky blue?",
answers: [],
correct_answer: 0,
},
{
id: 2,
type: "multiple_choice",
question: "Why is the sky blue?",
answers: [
{ id: 1, answer: "Because of physics" },
{ id: 2, answer: "Because that the way it always was" },
{ id: 3, answer: "I don't know" },
],
correct_answer: 1,
},
{
id: 3,
type: "multiple_selection_choice",
question: "Why is the sky blue?",
answers: [
{ value: "Because of physics", label: "Because of physics" },
{
value: "Because that the way it always was",
label: "Because that the way it always was",
},
{ value: "I don't know", label: "I don't know" },
],
correct_answer: 1,
},
{
id: 4,
type: "long_text",
question: "Why is the sky blue?",
answers: [],
correct_answer: 0,
},
{
id: 5,
type: "description",
question: "random text to show to the user",
answers: [],
correct_answer: 0,
},
{
id: 6,
type: "true_false",
question: "Is the sky blue",
answers: [],
correct_answer: 2,
},
];
+73
View File
@@ -0,0 +1,73 @@
import { quiz } from "../../main";
import useAuth from "../../hooks/useAuth";
import styles from "./Home.module.css";
import Description from "../../components/Description/Description";
import LongText from "../../components/LongText/LongText";
import MultipleChoice from "../../components/MultipleChoice/MultipleChoice";
import MultipleSelectionChoice from "../../components/MultipleSelectionChoice/MultipleSelectionChoice";
import ShortAnswer from "../../components/ShortAnswer/ShortAnswer";
import TrueFalse from "../../components/TrueFalse/TrueFalse";
const Home = () => {
const { currentQuestion, setCurrentQuestion, handleSubmit } = useAuth();
// increasing quiz set no by 1 after clicking
const handleNext = () => {
setCurrentQuestion(currentQuestion + 1);
};
// decreasing quiz set no by 1 after clicking
const handlePrev = () => {
setCurrentQuestion(currentQuestion - 1);
};
// matching quiz type with components to render
const renderElement = () => {
if (quiz[currentQuestion].type === "short_answer") {
return <ShortAnswer quiz={quiz[currentQuestion]}></ShortAnswer>;
} else if (quiz[currentQuestion].type === "multiple_choice") {
return <MultipleChoice quiz={quiz[currentQuestion]}></MultipleChoice>;
} else if (quiz[currentQuestion].type === "multiple_selection_choice") {
return (
<MultipleSelectionChoice
quiz={quiz[currentQuestion]}
></MultipleSelectionChoice>
);
} else if (quiz[currentQuestion].type === "long_text") {
return <LongText quiz={quiz[currentQuestion]}></LongText>;
} else if (quiz[currentQuestion].type === "description") {
return <Description quiz={quiz[currentQuestion]}></Description>;
} else if (quiz[currentQuestion].type === "true_false") {
return <TrueFalse quiz={quiz[currentQuestion]}></TrueFalse>;
}
};
return (
<div className={styles.home}>
{/* redering the matched quiz with type and curent number */}
<div className={styles.quizContainer}>{renderElement()}</div>
<div className={styles.HomeButtonContainer}>
{currentQuestion > 0 && (
<button onClick={handlePrev} className={styles.prevBtns}>
PREV
</button>
)}
{currentQuestion < quiz.length - 1 ? (
<button onClick={handleNext} className={styles.nextBtns}>
NEXT
</button>
) : (
<button
onClick={handleSubmit}
className={styles.nextBtns}
style={{ backgroundColor: "skyblue", color: "black" }}
>
SUBMIT
</button>
)}
</div>
</div>
);
};
export default Home;
+39
View File
@@ -0,0 +1,39 @@
.home {
}
.quizContainer{
background-color: #99673817;
width: 800px ;
display: flex;
align-items: center;
justify-content: center;
min-height: 400px;
padding: 0 30px;
border-radius: 15px;
}
.HomeButtonContainer{
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 80px;
}
.prevBtns{
border: transparent;
background-color: tomato;
padding: 5px 15px;
border-radius: 10px;
color: white;
font-weight: bold;
}
.nextBtns{
border: transparent;
background-color: tomato;
padding: 5px 15px;
border-radius: 10px;
color: white;
font-weight: bold;
margin-left: auto;
}
+13
View File
@@ -0,0 +1,13 @@
const reportWebVitals = onPerfEntry => {
if (onPerfEntry && onPerfEntry instanceof Function) {
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
getCLS(onPerfEntry);
getFID(onPerfEntry);
getFCP(onPerfEntry);
getLCP(onPerfEntry);
getTTFB(onPerfEntry);
});
}
};
export default reportWebVitals;
+5
View File
@@ -0,0 +1,5 @@
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom';
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.