diff --git a/src/api/getQuizQuestions.js b/src/api/getQuizQuestions.js index b135066..ad4d96a 100644 --- a/src/api/getQuizQuestions.js +++ b/src/api/getQuizQuestions.js @@ -28,7 +28,7 @@ async function retrieveQuizQuestions(animal) { return data; } catch (error) { - console.error('Error:', error); + return console.error('Error:', error); } } diff --git a/src/components/Answer/Answer.js b/src/components/Answer/Answer.js index 4bcf535..aed2bf9 100644 --- a/src/components/Answer/Answer.js +++ b/src/components/Answer/Answer.js @@ -16,7 +16,7 @@ export default class Answer { if (this.checkIfCorrect()) { score = 1; if (this.changed) { - score = score / 2; + score /= score / 2; } } return score; diff --git a/src/components/Dropdown/Dropdown.css b/src/components/Dropdown/Dropdown.css new file mode 100644 index 0000000..13efdd8 --- /dev/null +++ b/src/components/Dropdown/Dropdown.css @@ -0,0 +1,23 @@ +select { + -webkit-appearance: button; + -moz-appearance: button; + -webkit-user-select: none; + -moz-user-select: none; + -webkit-padding-end: 10px; + -moz-padding-end: 10px; + -webkit-padding-start: 5px; + -moz-padding-start: 5px; + background-color: #648080; + border: 1px solid #aaa; + border-radius: 10px; + box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.1); + color: #2c3e50; + font-size: inherit; + margin: 0; + overflow: hidden; + padding-top: 2px; + padding-bottom: 2px; + text-overflow: ellipsis; + white-space: nowrap; + margin-right: 20px; +} diff --git a/src/components/Dropdown/Dropdown.js b/src/components/Dropdown/Dropdown.js new file mode 100644 index 0000000..3d7c7c2 --- /dev/null +++ b/src/components/Dropdown/Dropdown.js @@ -0,0 +1,28 @@ +import './Dropdown.css'; + +export default function Dropdown(className, dropdownLabel, option1, option2) { + const dropdown = document.createElement('div'); + dropdown.classList.add('custom-select'); + dropdown.classList.add(className); + + const select = document.createElement('select'); + select.setAttribute('name', 'settings'); + select.setAttribute('id', 'settings'); + + const defaultSetting = document.createElement('option'); + defaultSetting.setAttribute('value', dropdownLabel); + defaultSetting.innerHTML = dropdownLabel; + + const option1Setting = document.createElement('option'); + option1Setting.setAttribute('value', `${option1}`); + option1Setting.innerHTML = option1.toLowerCase(); + + const option2Setting = document.createElement('option'); + option2Setting.setAttribute('value', `${option2}`); + option2Setting.innerHTML = option2.toLowerCase(); + + select.append(defaultSetting, option1Setting, option2Setting); + dropdown.append(select); + + return dropdown; +} diff --git a/src/components/RangeSelect/RangeSelect.css b/src/components/RangeSelect/RangeSelect.css new file mode 100644 index 0000000..43c51b8 --- /dev/null +++ b/src/components/RangeSelect/RangeSelect.css @@ -0,0 +1,4 @@ +.range { + display: flex; + padding: 0.5em 0.5em; +} diff --git a/src/components/RangeSelect/RangeSelect.js b/src/components/RangeSelect/RangeSelect.js new file mode 100644 index 0000000..380d8a0 --- /dev/null +++ b/src/components/RangeSelect/RangeSelect.js @@ -0,0 +1,19 @@ +import './RangeSelect.css'; +import { DEFAULT_MIN, DEFAULT_MAX } from '../ScoreFilter/ScoreFilter'; + +export default function RangeSelect(labelName) { + const range = document.createElement('div'); + range.className = 'range'; + const label = document.createElement('p'); + label.innerHTML = labelName; + + const input = document.createElement('input'); + input.className = labelName; + input.type = 'number'; + input.min = DEFAULT_MIN; + input.max = DEFAULT_MAX; + + range.append(label, input); + + return range; +} diff --git a/src/components/ScoreFilter/ScoreFilter.css b/src/components/ScoreFilter/ScoreFilter.css new file mode 100644 index 0000000..c91ab19 --- /dev/null +++ b/src/components/ScoreFilter/ScoreFilter.css @@ -0,0 +1,41 @@ +.score-filter { + display: flex; + align-items: center; + align-content: center; + margin-top: 5vh; +} + +.score-range { + display: flex; +} + +input { + text-align: center; + margin-left: 10px; + height: 5vh; + width: 40px; + font-size: 60%; + text-align: right; +} + +button { + margin-top: 2vh; +} + +#settings { + text-align: center; +} + +#option { + text-align: center; +} + +@media only screen and (min-device-width: 280px) and (max-device-width: 1200px) { + .score-filter { + flex-direction: column; + margin-top: 2vh; + } + #settings { + margin: 10px auto; + } +} diff --git a/src/components/ScoreFilter/ScoreFilter.js b/src/components/ScoreFilter/ScoreFilter.js new file mode 100644 index 0000000..8a23686 --- /dev/null +++ b/src/components/ScoreFilter/ScoreFilter.js @@ -0,0 +1,133 @@ +import './ScoreFilter.css'; + +import Dropdown from '../Dropdown/Dropdown'; +import RangeSelect from '../RangeSelect/RangeSelect'; +import { getScoreFromLocalStorage, renderScores } from '../../views/Leaderboard/Leaderboard'; + +const DEFAULT_ABOUT = 'Select animal'; +const DEFAULT_TYPE = 'Select quiz type'; +export const DEFAULT_MIN = 1; +export const DEFAULT_MAX = 20; + +export const settings = { + about: DEFAULT_ABOUT, + type: DEFAULT_TYPE, + min: DEFAULT_MIN, + max: DEFAULT_MAX, +}; + +export default function ScoreFilter() { + const scoreFilter = document.createElement('div'); + scoreFilter.classList.add('score-filter'); + + const button = ClearButton(); + + const dropdownAbout = Dropdown('about-dropdown', DEFAULT_ABOUT, 'CATS', 'DOGS'); + const dropdownType = Dropdown('type-dropdown', DEFAULT_TYPE, 'MULTIPLE CHOICE', 'OPEN'); + scoreFilter.append(dropdownAbout, dropdownType); + + dropdownAbout.addEventListener('change', (e) => { + settings.about = e.target.value; + filterScores(); + scoreFilter.append(button); + }); + dropdownType.addEventListener('change', (e) => { + settings.type = e.target.value; + filterScores(); + scoreFilter.append(button); + }); + + const minRange = RangeSelect('min'); + const maxRange = RangeSelect('max'); + + scoreFilter.append(minRange, maxRange); + + minRange.addEventListener('change', (e) => { + settings.min = e.target.value; + filterScores(); + scoreFilter.append(button); + }); + + maxRange.addEventListener('change', (e) => { + settings.max = e.target.value; + filterScores(); + scoreFilter.append(button); + }); + + if (settings.about !== DEFAULT_ABOUT || settings.type !== DEFAULT_TYPE || settings.min !== 1 || settings.max !== 20) { + scoreFilter.append(button); + } + + return scoreFilter; +} + +// TODO: use Button component instead +function ClearButton() { + const button = document.createElement('button'); + button.innerHTML = 'clear filters'; + button.addEventListener('click', () => { + settings.about = DEFAULT_ABOUT; + settings.type = DEFAULT_TYPE; + settings.min = DEFAULT_MIN; + settings.max = DEFAULT_MAX; + filterScores(); + + const dropdowns = document.querySelectorAll('select'); + dropdowns.forEach((select) => { + select.selectedIndex = 0; + }); + + const minRange = document.querySelector('.min'); + minRange.value = DEFAULT_MIN; + + const maxRange = document.querySelector('.max'); + maxRange.value = DEFAULT_MAX; + + button.remove(); + }); + + return button; +} + +/* function resetSettings() { + settings.about = DEFAULT_ABOUT; + settings.type = DEFAULT_TYPE; + settings.min = DEFAULT_MIN; + settings.max = DEFAULT_MAX; + filterScores(); + + const dropdowns = document.querySelectorAll('select'); + dropdowns.forEach((select) => { + select.selectedIndex = 0; + }); + + const button = document.querySelector('.reset-settings'); + button.remove(); +} */ + +function filterScores() { + const scores = getScoreFromLocalStorage(); + const filteredScores = scores.filter(filterAbout).filter(filterType).filter(filterQuestionNumber); + renderScores(filteredScores); +} + +function filterAbout(item) { + if (settings.about === DEFAULT_ABOUT) { + return item; + } + return item.ABOUT === settings.about; +} + +function filterType(item) { + if (settings.type === DEFAULT_TYPE) { + return item; + } + return item.TYPE === settings.type; +} + +function filterQuestionNumber(item) { + if (item.NUMBER >= settings.min && item.NUMBER <= settings.max) { + return item; + } + return false; +} diff --git a/src/model/randomizer.js b/src/model/randomizer.js index ef86aa0..7eecb71 100644 --- a/src/model/randomizer.js +++ b/src/model/randomizer.js @@ -1,4 +1,4 @@ -import { getQuizQuestions } from '../api/getQuizQuestions.js'; +import { getQuizQuestions } from '../api/getQuizQuestions'; export async function getRandomQuizQuestions(animal, numberOfQuestions) { const questionToRandomize = await getQuizQuestions(animal); @@ -6,7 +6,6 @@ export async function getRandomQuizQuestions(animal, numberOfQuestions) { if (numberOfQuestions < randomQuestions.length) { return randomQuestions.slice(0, numberOfQuestions); - } else { - return randomQuestions; } + return randomQuestions; } diff --git a/src/views/Leaderboard/Leaderboard.css b/src/views/Leaderboard/Leaderboard.css index 716313b..62e4979 100644 --- a/src/views/Leaderboard/Leaderboard.css +++ b/src/views/Leaderboard/Leaderboard.css @@ -23,7 +23,7 @@ div#scorePage > * { justify-content: center; } -.podiumNick { +.podiumNickname { margin-top: 30px; margin-left: 15px; display: flex; @@ -94,7 +94,7 @@ button.resetButton { } @media (max-width: 960px) { - .podiumNick { + .podiumNickname { font-size: 1rem; } .listItem { diff --git a/src/views/Leaderboard/Leaderboard.js b/src/views/Leaderboard/Leaderboard.js index fdf4864..1f1a3f8 100644 --- a/src/views/Leaderboard/Leaderboard.js +++ b/src/views/Leaderboard/Leaderboard.js @@ -2,11 +2,13 @@ import './Leaderboard.css'; import Button from '../../components/Button/Button'; import '../../components/Button/Button.css'; +import ScoreFilter from '../../components/ScoreFilter/ScoreFilter'; + export function renderLeaderboard() { renderPodium(); createMenuButton(); renderResetBtn(); - getScoreFromLocalStorage(); + renderScores(getScoreFromLocalStorage()); } function renderPodium() { @@ -14,12 +16,14 @@ function renderPodium() {

LEADERBOARD

+
+
- - - + + + @@ -31,15 +35,15 @@ function renderPodium() {
    -
  1. -
  2. -
  3. +
  4. +
  5. +
    -
  1. -
  2. -
  3. +
  4. +
  5. +
`; + const filter = document.querySelector('.filter'); + const scorefilter = ScoreFilter(); + filter.append(scorefilter); } -function getScoreFromLocalStorage() { - const highScores = JSON.parse(localStorage.getItem('quizScores')) || []; - highScores.sort((a, b) => b.SCORE - a.SCORE); - function showScore() { - for (let i = 1; i <= highScores.length; i++) { - if (i > 9) { - break; - } - const nick = `nick${i}`; - document.getElementById(nick).innerText = `${highScores[i - 1].NAME} Pts:${highScores[i - 1].SCORE}`; +export function renderScores(highScores) { + for (let i = 0; i < 9; i++) { + const nicknameContainer = document.getElementById(`nickname${i + 1}`); + if (highScores[i]) { + nicknameContainer.innerText = `${highScores[i].NAME} Pts:${highScores[i].SCORE}`; + } else { + nicknameContainer.innerText = '-'; } } - return showScore(); } function createMenuButton() { @@ -84,3 +87,10 @@ function renderResetBtn() { const resetButton = document.getElementById('resetButton'); resetButton.append(Button('RESET SCORE', 'resetButton', false, 'click', resetLocalStorage)); } + +export function getScoreFromLocalStorage() { + const scores = JSON.parse(localStorage.getItem('quizScores')) || []; + scores.sort((a, b) => b.SCORE - a.SCORE); + + return scores; +} diff --git a/src/views/ScorePage/ScorePage.css b/src/views/ScorePage/ScorePage.css index eacf678..ffbe5bf 100644 --- a/src/views/ScorePage/ScorePage.css +++ b/src/views/ScorePage/ScorePage.css @@ -9,7 +9,7 @@ h2 { color: white; } -#nickFormDiv { +#nicknameFormDiv { margin-top: 10px; display: flex; justify-content: center; @@ -18,20 +18,20 @@ h2 { gap: 5%; } -#nickname { +#nicknamename { border: 0px solid; width: 250px; height: 48px; } -#nickname:hover { +#nicknamename:hover { color: white; } -#nickname:disabled { +#nicknamename:disabled { opacity: 0; } -#nickname::placeholder { +#nicknamename::placeholder { color: lightgray; text-align: center; font-weight: lighter; @@ -73,10 +73,10 @@ input { bottom: 0%; left: 0%; } - #nickFormDiv { + #nicknameFormDiv { flex-direction: column; } - #nickname { + #nicknamename { width: auto; margin-bottom: 10px; } diff --git a/src/views/ScorePage/ScorePage.js b/src/views/ScorePage/ScorePage.js index 49570f3..a27f82b 100644 --- a/src/views/ScorePage/ScorePage.js +++ b/src/views/ScorePage/ScorePage.js @@ -13,7 +13,7 @@ export function renderScorePage() { container.append( renderGraphics(), renderScore(recentUserScore, totalNumber), - renderNickForm(), + renderNicknameForm(), renderMenuButton(), renderPlayAgainButton(), ); @@ -65,27 +65,27 @@ function renderGraphics() { return img; } -function renderNickForm() { - const nickFormContainer = document.createElement('div'); - nickFormContainer.setAttribute('id', 'nickFormDiv'); +function renderNicknameForm() { + const nicknameFormContainer = document.createElement('div'); + nicknameFormContainer.setAttribute('id', 'nicknameFormDiv'); const input = document.createElement('input'); - input.setAttribute('id', 'nickname'); - input.setAttribute('placeholder', 'Enter Nickname'); + input.setAttribute('id', 'nicknamename'); + input.setAttribute('placeholder', 'Enter Nicknamename'); input.setAttribute('type', 'text'); input.setAttribute('style', 'text-transform: uppercase'); input.addEventListener('keyup', () => { document.querySelector('.saveScoreBtn').disabled = !input.value; }); - nickFormContainer.append(input, Button('SUBMIT', 'saveScoreBtn', false, 'click', saveQuizScore)); - return nickFormContainer; + nicknameFormContainer.append(input, Button('SUBMIT', 'saveScoreBtn', false, 'click', saveQuizScore)); + return nicknameFormContainer; } function getCurrentScore(Answers) { return Answers.reduce((sum, ans) => sum + ans.getScore(), 0); } -function nicknameValidation() { - const input = document.getElementById('nickname'); +function nicknamenameValidation() { + const input = document.getElementById('nicknamename'); if (input.value !== '') { return true; } @@ -96,19 +96,19 @@ function saveQuizScore() { alert('YOUR SCORE HAS BEEN SAVED'); const quizScores = JSON.parse(localStorage.getItem('quizScores')) || []; const recentUserScore = getCurrentScore(userAnswers); - const nickName = document.getElementById('nickname'); - if (nicknameValidation()) { + const nicknameName = document.getElementById('nicknamename'); + if (nicknamenameValidation()) { const score = { SCORE: recentUserScore, ABOUT: QuizSettings.quizAbout, NUMBER: QuizSettings.questionsNum, TYPE: QuizSettings.questionsType, - NAME: nickName.value, + NAME: nicknameName.value, }; quizScores.push(score); localStorage.setItem('quizScores', JSON.stringify(quizScores)); - nickName.value = null; - nickName.disabled = true; + nicknameName.value = null; + nicknameName.disabled = true; document.querySelector('.saveScoreBtn').disabled = true; } } diff --git a/src/views/quiz-settings/quiz-settings.js b/src/views/quiz-settings/quiz-settings.js index c1a449d..dd4639c 100644 --- a/src/views/quiz-settings/quiz-settings.js +++ b/src/views/quiz-settings/quiz-settings.js @@ -4,7 +4,9 @@ import Button from '../../components/Button/Button'; class QuizSettings { quizAbout; + questionsNum; + questionsType; static createRadioButton(value, settingName) { @@ -32,7 +34,7 @@ class QuizSettings { this.questionsType = value; } if (settingName === 'questionsNum') { - this.questionsNum = parseInt(component.value); + this.questionsNum = parseInt(component.value, 10); } }); } @@ -79,6 +81,7 @@ class QuizSettings { buttonStartQuiz.value = 'START QUIZ'; return buttonStartQuiz; } + static navigateToMenu() { window.location.hash = ''; } @@ -100,7 +103,7 @@ class QuizSettings { ); form.addEventListener('submit', (e) => { e.preventDefault(); - if (this.questionsNum === undefined || this.questionsNum < 1 || this.questionsNum > 20) { + if (validateQuestionNumber(this.questionsNum)) { alert('Insert questions number between 1 and 20'); } else if (this.quizAbout === undefined) { alert('Choose animals'); @@ -129,3 +132,7 @@ export { QuizSettings }; export function renderQuizSettings() { QuizSettings.showSettings(); } + +function validateQuestionNumber(questionsNum) { + return !Number.isInteger(questionsNum) || questionsNum < 1 || questionsNum > 20; +}
2