diff --git a/code/13 Demo Project - React Quiz/08-finished/.gitignore b/code/13 Demo Project - React Quiz/08-finished/.gitignore deleted file mode 100644 index a547bf36d..000000000 --- a/code/13 Demo Project - React Quiz/08-finished/.gitignore +++ /dev/null @@ -1,24 +0,0 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -pnpm-debug.log* -lerna-debug.log* - -node_modules -dist -dist-ssr -*.local - -# Editor directories and files -.vscode/* -!.vscode/extensions.json -.idea -.DS_Store -*.suo -*.ntvs* -*.njsproj -*.sln -*.sw? diff --git a/code/13 Demo Project - React Quiz/08-finished/index.html b/code/13 Demo Project - React Quiz/08-finished/index.html deleted file mode 100644 index 9ae9a44e2..000000000 --- a/code/13 Demo Project - React Quiz/08-finished/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - ReactQuiz - - -
- - - diff --git a/code/13 Demo Project - React Quiz/08-finished/package.json b/code/13 Demo Project - React Quiz/08-finished/package.json deleted file mode 100644 index f7646cbb5..000000000 --- a/code/13 Demo Project - React Quiz/08-finished/package.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "effects-adv-prj", - "private": true, - "version": "0.0.0", - "type": "module", - "scripts": { - "dev": "vite", - "build": "vite build", - "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", - "preview": "vite preview" - }, - "dependencies": { - "react": "^18.2.0", - "react-dom": "^18.2.0" - }, - "devDependencies": { - "@types/react": "^18.2.15", - "@types/react-dom": "^18.2.7", - "@vitejs/plugin-react": "^4.0.3", - "eslint": "^8.45.0", - "eslint-plugin-react": "^7.32.2", - "eslint-plugin-react-hooks": "^4.6.0", - "eslint-plugin-react-refresh": "^0.4.3", - "vite": "^4.4.5" - } -} diff --git a/code/13 Demo Project - React Quiz/08-finished/public/quiz-logo.png b/code/13 Demo Project - React Quiz/08-finished/public/quiz-logo.png deleted file mode 100644 index dbcd77059..000000000 Binary files a/code/13 Demo Project - React Quiz/08-finished/public/quiz-logo.png and /dev/null differ diff --git a/code/13 Demo Project - React Quiz/08-finished/src/App.jsx b/code/13 Demo Project - React Quiz/08-finished/src/App.jsx deleted file mode 100644 index 6e001d8a6..000000000 --- a/code/13 Demo Project - React Quiz/08-finished/src/App.jsx +++ /dev/null @@ -1,15 +0,0 @@ -import Header from './components/Header.jsx'; -import Quiz from './components/Quiz.jsx'; - -function App() { - return ( - <> -
-
- -
- - ); -} - -export default App; diff --git a/code/13 Demo Project - React Quiz/08-finished/src/assets/quiz-complete.png b/code/13 Demo Project - React Quiz/08-finished/src/assets/quiz-complete.png deleted file mode 100644 index 4e235aea8..000000000 Binary files a/code/13 Demo Project - React Quiz/08-finished/src/assets/quiz-complete.png and /dev/null differ diff --git a/code/13 Demo Project - React Quiz/08-finished/src/assets/quiz-logo.png b/code/13 Demo Project - React Quiz/08-finished/src/assets/quiz-logo.png deleted file mode 100644 index dbcd77059..000000000 Binary files a/code/13 Demo Project - React Quiz/08-finished/src/assets/quiz-logo.png and /dev/null differ diff --git a/code/13 Demo Project - React Quiz/08-finished/src/components/Answers.jsx b/code/13 Demo Project - React Quiz/08-finished/src/components/Answers.jsx deleted file mode 100644 index 5a6114aad..000000000 --- a/code/13 Demo Project - React Quiz/08-finished/src/components/Answers.jsx +++ /dev/null @@ -1,47 +0,0 @@ -import { useRef } from 'react'; - -export default function Answers({ - answers, - selectedAnswer, - answerState, - onSelect, -}) { - const shuffledAnswers = useRef(); - - if (!shuffledAnswers.current) { - shuffledAnswers.current = [...answers]; - shuffledAnswers.current.sort(() => Math.random() - 0.5); - } - - return ( - - ); -} diff --git a/code/13 Demo Project - React Quiz/08-finished/src/components/Header.jsx b/code/13 Demo Project - React Quiz/08-finished/src/components/Header.jsx deleted file mode 100644 index 0d2d8a37a..000000000 --- a/code/13 Demo Project - React Quiz/08-finished/src/components/Header.jsx +++ /dev/null @@ -1,10 +0,0 @@ -import logoImg from '../assets/quiz-logo.png'; - -export default function Header() { - return ( -
- Quiz logo -

ReactQuiz

-
- ); -} diff --git a/code/13 Demo Project - React Quiz/08-finished/src/components/Question.jsx b/code/13 Demo Project - React Quiz/08-finished/src/components/Question.jsx deleted file mode 100644 index f80550fab..000000000 --- a/code/13 Demo Project - React Quiz/08-finished/src/components/Question.jsx +++ /dev/null @@ -1,66 +0,0 @@ -import { useState } from 'react'; - -import QuestionTimer from './QuestionTimer.jsx'; -import Answers from './Answers.jsx'; -import QUESTIONS from '../questions.js'; - -export default function Question({ index, onSelectAnswer, onSkipAnswer }) { - const [answer, setAnswer] = useState({ - selectedAnswer: '', - isCorrect: null, - }); - - let timer = 10000; - - if (answer.selectedAnswer) { - timer = 1000; - } - - if (answer.isCorrect !== null) { - timer = 2000; - } - - function handleSelectAnswer(answer) { - setAnswer({ - selectedAnswer: answer, - isCorrect: null, - }); - - setTimeout(() => { - setAnswer({ - selectedAnswer: answer, - isCorrect: QUESTIONS[index].answers[0] === answer, - }); - - setTimeout(() => { - onSelectAnswer(answer); - }, 2000); - }, 1000); - } - - let answerState = ''; - - if (answer.selectedAnswer && answer.isCorrect !== null) { - answerState = answer.isCorrect ? 'correct' : 'wrong'; - } else if (answer.selectedAnswer) { - answerState = 'answered'; - } - - return ( -
- -

{QUESTIONS[index].text}

- -
- ); -} diff --git a/code/13 Demo Project - React Quiz/08-finished/src/components/QuestionTimer.jsx b/code/13 Demo Project - React Quiz/08-finished/src/components/QuestionTimer.jsx deleted file mode 100644 index a82728716..000000000 --- a/code/13 Demo Project - React Quiz/08-finished/src/components/QuestionTimer.jsx +++ /dev/null @@ -1,34 +0,0 @@ -import { useState, useEffect } from 'react'; - -export default function QuestionTimer({ timeout, onTimeout, mode }) { - const [remainingTime, setRemainingTime] = useState(timeout); - - useEffect(() => { - console.log('SETTING TIMEOUT'); - const timer = setTimeout(onTimeout, timeout); - - return () => { - clearTimeout(timer); - }; - }, [timeout, onTimeout]); - - useEffect(() => { - console.log('SETTING INTERVAL'); - const interval = setInterval(() => { - setRemainingTime((prevRemainingTime) => prevRemainingTime - 100); - }, 100); - - return () => { - clearInterval(interval); - }; - }, []); - - return ( - - ); -} diff --git a/code/13 Demo Project - React Quiz/08-finished/src/components/Quiz.jsx b/code/13 Demo Project - React Quiz/08-finished/src/components/Quiz.jsx deleted file mode 100644 index 51da1f7c0..000000000 --- a/code/13 Demo Project - React Quiz/08-finished/src/components/Quiz.jsx +++ /dev/null @@ -1,41 +0,0 @@ -import { useState, useCallback } from 'react'; - -import QUESTIONS from '../questions.js'; -import Question from './Question.jsx'; -import Summary from './Summary.jsx'; - -export default function Quiz() { - const [userAnswers, setUserAnswers] = useState([]); - - const activeQuestionIndex = userAnswers.length; - const quizIsComplete = activeQuestionIndex === QUESTIONS.length; - - const handleSelectAnswer = useCallback(function handleSelectAnswer( - selectedAnswer - ) { - setUserAnswers((prevUserAnswers) => { - return [...prevUserAnswers, selectedAnswer]; - }); - }, - []); - - const handleSkipAnswer = useCallback( - () => handleSelectAnswer(null), - [handleSelectAnswer] - ); - - if (quizIsComplete) { - return - } - - return ( -
- -
- ); -} diff --git a/code/13 Demo Project - React Quiz/08-finished/src/components/Summary.jsx b/code/13 Demo Project - React Quiz/08-finished/src/components/Summary.jsx deleted file mode 100644 index 74b54fcc7..000000000 --- a/code/13 Demo Project - React Quiz/08-finished/src/components/Summary.jsx +++ /dev/null @@ -1,59 +0,0 @@ -import quizCompleteImg from '../assets/quiz-complete.png'; -import QUESTIONS from '../questions.js'; - -export default function Summary({ userAnswers }) { - const skippedAnswers = userAnswers.filter((answer) => answer === null); - const correctAnswers = userAnswers.filter( - (answer, index) => answer === QUESTIONS[index].answers[0] - ); - - const skippedAnswersShare = Math.round( - (skippedAnswers.length / userAnswers.length) * 100 - ); - const correctAnswersShare = Math.round( - (correctAnswers.length / userAnswers.length) * 100 - ); - const wrongAnswersShare = 100 - skippedAnswersShare - correctAnswersShare; - - return ( -
- Trophy icon -

Quiz Completed!

-
-

- {skippedAnswersShare}% - skipped -

-

- {correctAnswersShare}% - answered correctly -

-

- {wrongAnswersShare}% - answered incorrectly -

-
-
    - {userAnswers.map((answer, index) => { - let cssClass = 'user-answer'; - - if (answer === null) { - cssClass += ' skipped'; - } else if (answer === QUESTIONS[index].answers[0]) { - cssClass += ' correct'; - } else { - cssClass += ' wrong'; - } - - return ( -
  1. -

    {index + 1}

    -

    {QUESTIONS[index].text}

    -

    {answer ?? 'Skipped'}

    -
  2. - ); - })} -
-
- ); -} diff --git a/code/13 Demo Project - React Quiz/08-finished/src/index.css b/code/13 Demo Project - React Quiz/08-finished/src/index.css deleted file mode 100644 index 0825029aa..000000000 --- a/code/13 Demo Project - React Quiz/08-finished/src/index.css +++ /dev/null @@ -1,336 +0,0 @@ -@import url('https://fonts.googleapis.com/css2?family=Roboto+Condensed:wght@400;700&family=Roboto:wght@400;700&display=swap'); - -* { - box-sizing: border-box; -} - -html { - font-family: 'Roboto', sans-serif; - line-height: 1.5; - - color: #ebe7ef; - - font-synthesis: none; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - -webkit-text-size-adjust: 100%; - height: 100%; - /* min-height: 100rem; */ -} - -body { - margin: 0; - padding: 2rem; - /* background: linear-gradient(180deg, #22182f 0%, #2c2437 100%); */ - background-color: #1d0433; - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100%25' height='100%25' viewBox='0 0 800 400'%3E%3Cdefs%3E%3CradialGradient id='a' cx='396' cy='281' r='514' gradientUnits='userSpaceOnUse'%3E%3Cstop offset='0' stop-color='%237616DD'/%3E%3Cstop offset='1' stop-color='%231D0433'/%3E%3C/radialGradient%3E%3ClinearGradient id='b' gradientUnits='userSpaceOnUse' x1='400' y1='148' x2='400' y2='333'%3E%3Cstop offset='0' stop-color='%2318E0FF' stop-opacity='0'/%3E%3Cstop offset='1' stop-color='%2318E0FF' stop-opacity='0.5'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect fill='url(%23a)' width='800' height='400'/%3E%3Cg fill-opacity='0.4'%3E%3Ccircle fill='url(%23b)' cx='267.5' cy='61' r='300'/%3E%3Ccircle fill='url(%23b)' cx='532.5' cy='61' r='300'/%3E%3Ccircle fill='url(%23b)' cx='400' cy='30' r='300'/%3E%3C/g%3E%3C/svg%3E"); - background-attachment: fixed; - background-size: cover; - background-position: center; -} - -header { - margin: 2rem 0; - text-align: center; -} - -header img { - width: 3rem; - height: 3rem; - filter: drop-shadow(0 0 4px rgba(0, 0, 0, 0.6)); -} - -header h1 { - font-family: 'Roboto Condensed', sans-serif; - font-weight: bold; - font-size: 2.5rem; - letter-spacing: 0.6rem; - margin: 0; - text-transform: uppercase; - background: linear-gradient(90deg, #e781fb 40%, #8e76fa 60%); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; -} - -#last-try { - max-width: 40rem; - margin: 2rem auto; - font-size: 0.8rem; - font-family: 'Roboto Condensed', sans-serif; - text-align: center; -} - -#last-try h2 { - margin: 0; - font-size: 1rem; - color: #9c7fd3; - text-transform: uppercase; -} - -#last-try ul { - list-style: none; - margin: 0; - padding: 0; - display: flex; - gap: 1rem; - font-size: 1.25rem; - color: #a690c5; -} - -#quiz { - max-width: 50rem; - margin: auto; - padding: 2rem; - background: linear-gradient(180deg, #3e2a60 0%, #321061 100%); - border-radius: 8px; - /* box-shadow: 1px 1px 4px 1px rgba(0, 0, 0, 0.6); */ - box-shadow: 1px 1px 8px 4px rgba(12, 5, 32, 0.6); - text-align: center; -} - -#question-overview { - font-family: 'Roboto Condensed', sans-serif; - font-size: 0.8rem; - color: #9082a3; - margin: 0; - text-transform: uppercase; -} - -#question progress { - width: 50%; - height: 0.5rem; - border-radius: 24px; - background: #9e5ef8; - margin: 0; -} - -#question progress::-webkit-progress-bar { - background: #6a558a; - border-radius: 24px; -} - -#question progress::-webkit-progress-value { - background: #9e5ef8; - border-radius: 24px; -} - -#question progress.answered { - background: #f8e59c; -} - -#question progress.answered::-webkit-progress-value { - background: #f8e59c; -} - -#question progress.answered::-webkit-progress-bar { - background: #6a558a; -} - -#question h2 { - font-family: 'Roboto', sans-serif; - font-size: 1.5rem; - font-weight: normal; - margin: 0.5rem 0 2.5rem 0; - color: #c1b2dd; -} - -#answers { - list-style: none; - margin: 0; - padding: 0; - display: flex; - flex-direction: column; - align-items: center; - gap: 0.5rem; -} - -.answer { - width: 90%; - margin: 0 auto; -} - -.answer button { - display: inline-block; - width: 100%; - font-family: 'Roboto Condensed', sans-serif; - font-size: 0.9rem; - padding: 1rem 2rem; - border: none; - border-radius: 24px; - /* background: linear-gradient(180deg, #81f1fb 0%, #73d2f8 100%); */ - background: #6cb7f5; - cursor: pointer; - transition: all 0.2s ease-in-out; -} - -.answer button:hover, -.answer button:focus { - /* background: linear-gradient(180deg, #bf48f6 0%, #9b50f2 100%); */ - background: #9d5af5; - color: white; - /* box-shadow: 0 0 4px 2px rgba(255, 200, 60, 0.8); */ -} - -.answer button.selected { - /* background: linear-gradient(180deg, #fbda81 0%, #f8b173 100%); */ - background: #f5a76c; - color: #2c203d; -} - -.answer button.correct { - /* background: linear-gradient(180deg, #a1fa61 0%, #52d482 100%); */ - background: #5af59d; - color: #2c203d; -} - -.answer button.wrong { - /* background: linear-gradient(180deg, #f96fb1 0%, #f52b8c 100%); */ - background: #f55a98; - color: #2c203d; -} - -#skip-action { - margin-top: 2rem; -} - -#skip-action button { - font-family: 'Roboto Condensed', sans-serif; - font-size: 1rem; - border: none; - background: transparent; - color: #9082a3; - cursor: pointer; -} - -#skip-action button:hover, -#skip-action button:focus { - color: #c7bfd6; -} - -#summary { - max-width: 40rem; - margin: 2rem auto; - padding: 2rem; - background: linear-gradient(180deg, #a17eda 0%, #895fc4 100%); - color: #191321; - border-radius: 8px; - box-shadow: 1px 1px 8px 1px rgba(0, 0, 0, 0.6); - animation: slide-in-from-bottom 0.7s ease-out; -} - -#summary img { - display: block; - width: 8rem; - height: 8rem; - object-fit: contain; - margin: 0 auto 1rem auto; - padding: 1rem; - filter: drop-shadow(0 0 4px rgba(0, 0, 0, 0.6)); - border: 2px solid #3a2353; - border-radius: 50%; - background: #c18cfa; -} - -#summary h2 { - font-family: 'Roboto', sans-serif; - font-size: 3rem; - text-align: center; - margin: 0; - text-transform: uppercase; - color: #3a2353; -} - -#summary ol { - list-style: none; - margin: 2rem auto; - padding: 0; - text-align: center; -} - -#summary li { - margin: 2rem 0; -} - -#summary h3 { - font-family: 'Roboto Condensed', sans-serif; - font-size: 1rem; - margin: 0 auto; - display: flex; - justify-content: center; - align-items: center; - background: #2c203d; - color: #d8cde8; - width: 2rem; - height: 2rem; - border-radius: 50%; -} - -#summary .question { - margin: 0.25rem 0; - font-size: 1rem; - color: #30273a; -} - -#summary .user-answer { - margin: 0.25rem 0; - font-family: 'Roboto Condensed', sans-serif; - font-weight: bold; - color: #251e2f; -} - -#summary .user-answer.correct { - color: #054e37; -} - -#summary .user-answer.wrong { - color: #730b4b; -} - -#summary .user-answer.skipped { - color: #d1baf2; - font-weight: normal; -} - -#summary-stats { - display: flex; - gap: 3rem; - width: 60%; - margin: 2rem auto; - padding-bottom: 2rem; - border-bottom: 2px solid #594276; -} - -#summary-stats p { - flex: 1; - display: flex; - flex-direction: column; - margin: 0; -} - -#summary-stats .number { - font-family: 'Roboto Condensed', sans-serif; - font-size: 3rem; - color: #594276; -} - -#summary-stats .text { - font-family: 'Roboto Condensed', sans-serif; - text-transform: uppercase; - font-size: 0.8rem; - color: #30273a; - margin-top: -0.7rem; - margin-left: 0.2rem; - letter-spacing: 0.1rem; -} - -@keyframes slide-in-from-bottom { - 0% { - opacity: 0; - transform: translateY(10%); - } - 100% { - opacity: 1; - transform: translateY(0); - } -} diff --git a/code/13 Demo Project - React Quiz/08-finished/src/main.jsx b/code/13 Demo Project - React Quiz/08-finished/src/main.jsx deleted file mode 100644 index 54b39dd1d..000000000 --- a/code/13 Demo Project - React Quiz/08-finished/src/main.jsx +++ /dev/null @@ -1,10 +0,0 @@ -import React from 'react' -import ReactDOM from 'react-dom/client' -import App from './App.jsx' -import './index.css' - -ReactDOM.createRoot(document.getElementById('root')).render( - - - , -) diff --git a/code/13 Demo Project - React Quiz/08-finished/src/questions.js b/code/13 Demo Project - React Quiz/08-finished/src/questions.js deleted file mode 100644 index a8828069d..000000000 --- a/code/13 Demo Project - React Quiz/08-finished/src/questions.js +++ /dev/null @@ -1,72 +0,0 @@ -export default [ - { - id: 'q1', - text: 'Which of the following definitions best describes React.js?', - answers: [ - 'A library to build user interfaces with help of declarative code.', - 'A library for managing state in web applications.', - 'A framework to build user interfaces with help of imperative code.', - 'A library used for building mobile applications only.', - ], - }, - { - id: 'q2', - text: 'What purpose do React hooks serve?', - answers: [ - 'Enabling the use of state and other React features in functional components.', - 'Creating responsive layouts in React applications.', - 'Handling errors within the application.', - 'Part of the Redux library for managing global state.', - ], - }, - { - id: 'q3', - text: 'Can you identify what JSX is?', - answers: [ - 'A JavaScript extension that adds HTML-like syntax to JavaScript.', - 'A JavaScript library for building dynamic user interfaces.', - 'A specific HTML version that was explicitly created for React.', - 'A tool for making HTTP requests in a React application.', - ], - }, - { - id: 'q4', - text: 'What is the most common way to create a component in React?', - answers: [ - 'By defining a JavaScript function that returns a renderable value.', - 'By defining a custom HTML tag in JavaScript.', - 'By creating a file with a .jsx extension.', - 'By using the "new" keyword followed by the component name.', - ], - }, - { - id: 'q5', - text: 'What does the term "React state" imply?', - answers: [ - 'An object in a component that holds values and may cause the component to render on change.', - 'The lifecycle phase a React component is in.', - 'The overall status of a React application, including all props and components.', - 'A library for managing global state in React applications.', - ], - }, - { - id: 'q6', - text: 'How do you typically render list content in React apps?', - answers: [ - 'By using the map() method to iterate over an array of data and returning JSX.', - 'By using the for() loop to iterate over an array of data and returning JSX.', - 'By using the forEach() method to iterate over an array of data and returning JSX.', - 'By using the loop() method to iterate over an array of data and returning JSX.', - ], - }, - { - id: 'q7', - text: 'Which approach can NOT be used to render content conditionally?', - answers: [ - 'Using the #if template syntax.', - 'Using a ternary operator.', - 'Using the && operator.', - 'Using an if-else statement.', - ], - }, -]; diff --git a/code/13 Demo Project - React Quiz/08-finished/vite.config.js b/code/13 Demo Project - React Quiz/08-finished/vite.config.js deleted file mode 100644 index 5a33944a9..000000000 --- a/code/13 Demo Project - React Quiz/08-finished/vite.config.js +++ /dev/null @@ -1,7 +0,0 @@ -import { defineConfig } from 'vite' -import react from '@vitejs/plugin-react' - -// https://vitejs.dev/config/ -export default defineConfig({ - plugins: [react()], -})