Initial version of frontend

This commit is contained in:
Stachu 2023-02-26 16:49:26 +01:00 committed by Maciej Krzyżanowski
parent 8f45411981
commit 32cb76d18c
64 changed files with 31973 additions and 0 deletions

23
client/.gitignore vendored Normal file
View File

@ -0,0 +1,23 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*

29917
client/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

40
client/package.json Normal file
View File

@ -0,0 +1,40 @@
{
"name": "skrytka-test",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"react": "^18.2.0",
"react-confetti": "^6.1.0",
"react-dom": "^18.2.0",
"react-lazy-load-image-component": "^1.5.6",
"react-router-dom": "^6.7.0",
"react-scripts": "5.0.1",
"react-use": "^17.4.0",
"react-use-window-size": "^1.0.1",
"sass": "^1.57.1",
"start": "^5.1.0",
"use-sound": "^4.0.1",
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"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: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

BIN
client/public/img/Lupa.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="#f5f6fa" d="M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6 1.41-1.41z"/><path fill="none" d="M0 0h24v24H0V0z"/></svg>

After

Width:  |  Height:  |  Size: 210 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

BIN
client/public/img/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 249 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

24
client/public/index.html Normal file
View File

@ -0,0 +1,24 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<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="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.1/css/all.min.css" integrity="sha512-MV7K8+y+gLIBoVD59lQIYicR65iaqukzvf/nwasF0nqhPay5w/9lJmVM2hMDcnK1OnMGCdVK+iQrJ7lzPJQd1w==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Alegreya:ital@1&display=swap" rel="stylesheet">
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>

View File

@ -0,0 +1,8 @@
{
"short_name": "React App",
"name": "Create React App Sample",
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

3
client/public/robots.txt Normal file
View File

@ -0,0 +1,3 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:

34
client/src/App.js Normal file
View File

@ -0,0 +1,34 @@
import React from 'react';
import BottomFPage from './components/BottomFPage';
import './style/App.scss';
import AppProvider from './components/AppContext';
import SearchBox from './components/SelectBox';
function App() {
return (
<>
<div className="container">
<div className="logo">
<img className='box' src='img/skrytka.png' alt="" />
</div>
<h1 className='welcome'>Witaj!</h1>
<AppProvider>
<SearchBox />
<BottomFPage />
</AppProvider>
</div>
</>
);
}
export default App;

8
client/src/App.test.js Normal file
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();
});

29
client/src/AppRoutes.js Normal file
View File

@ -0,0 +1,29 @@
import Truck from './components/Truck';
import { Route, Routes } from 'react-router-dom';
import App from './App';
import NotFound from './components/NotFound'
import Quiz from './components/Quiz';
import Result from './components/Result';
import AppProvider from './components/AppContext';
const AppRoutes = () => {
return (
<Routes>
<Route path='/' element={<App />} />
<Route path='/:id' element={<Truck />} />
<Route path='*' element={<NotFound />} />
<Route path='/:id/:id' element={<Quiz />} />
<Route path="/result" element={<AppProvider><Result />
</AppProvider>} />
</Routes>
)
}
export default AppRoutes;

View File

@ -0,0 +1,33 @@
import React, { createContext, useState } from 'react';
export const AppContext = createContext();
const AppProvider = ({ children }) => {
let localizationFromDBArr = ['Kraków', 'Poznań', 'Warszawa', 'Gdańsk', 'Koszalin', 'Słupsk', 'Gdynia'];
const [unitOsp, setunitOsp] = useState("");
const [isContainerActive, setIsContainerActive] = useState(true);
const [localization, setLocalization] = useState(localizationFromDBArr);
const [isPopUpActive, setIsPopUpActive] = useState(true);
return (
<AppContext.Provider value={{
unitOsp,
setunitOsp,
isContainerActive,
setIsContainerActive,
localization,
setLocalization,
localizationFromDBArr,
isPopUpActive,
setIsPopUpActive,
}}>
{children}
</AppContext.Provider>
)
}
export default AppProvider

View File

@ -0,0 +1,42 @@
import React, {useContext} from 'react';
import { AppContext } from './AppContext';
import {Link} from 'react-router-dom';
import FooterQuiz from './FooterQuiz';
const BottomFPage = () => {
const {unitOsp} = useContext(AppContext);
const buttonShow = unitOsp ? <div className='imgArrow'>
<Link to = {`/${unitOsp}`}>
<i id ='fa-solid' className="fa-solid fa-arrow-right"></i>
</Link>
</div>
: "";
return (
<>
{buttonShow}
<div className='FirstFooter'>Nie widzisz swojej jednostki? <li className='li-write-to-us'><a className='a-write-to-us' href = '/linkdostrony'> Napisz do nas!</a></li></div>
<footer className='footerFirstSite'>@Wszelkie prawa zastrzeżone 2023 Skrytka.app<p className='skrytka-date'>Skrytka 2023</p></footer>
</>
)
}
export default BottomFPage;

View File

@ -0,0 +1,14 @@
import React from 'react'
import useWindowSize from 'react-use/lib/useWindowSize'
import Confetti from 'react-confetti';
const Conffetti = () => {
const { width, height } = useWindowSize()
return (
<Confetti
width={width}
height={height}
/>
)
}
export default Conffetti;

View File

@ -0,0 +1,49 @@
const FooterQuiz = () => (
<div className='FooterSecondSite'>
<a className='a-write-to-us-1' href = '/linkdostrony'>Zgłoś błąd lub pomysł ulepszenia aplikacji!</a>
<footer className='footerSecondSite'>General Public License<p className='skrytka-date'>Skrytka 2023</p>
<div className="footerRealization">
<div className="firstPatron">
<h4>Projekt zrealizowany w ramach Olimpiady Zwolnieni z Teorii</h4>
<img className='patronImg1' src="/img/logoZzt.png" alt="logo" />
</div>
<div className="secondPatron">
<h4>Patronat Szkoły Głównej Służby Pożarniczej</h4>
<img className='patronImg' src="/img/logoSgsp.png" alt="logo" />
</div>
</div>
<div className="footerMedia">
<div className="firstMedia">
<h2>Media społecznościowe</h2>
<i className="fa-brands fa-facebook"></i>
<i className="fa-brands fa-instagram"></i>
<i className="fa-brands fa-youtube"></i>
</div>
<div className="SecondMedia">
<h2>Skontaktuj się z nami: </h2>
<h3>Skrytka@Email.com</h3>
</div>
</div>
</footer>
</div>
)
export default FooterQuiz;

View File

@ -0,0 +1,258 @@
import React, {useRef} from 'react';
export let EndCorrectAnswer = "";
export let buttonQuiz = "";
let buttonsElement = "";
let EndDrawQuestion = "";
let ButtonShowCorrectAnswer = "";
export const QuizGetQuestion = ({score,isClick,setScore,setIsClick,navigate}) => {
const QuizDataQuestionFromDb = [
{
question: "Pytanie 1",
coorectAnswer: "Kabina",
},
{
question: "Pytanie 2",
coorectAnswer: "Dach",
},
{
question: "Pytanie 3",
coorectAnswer: "Dowódca 3",
},
{
question: "Pytanie 4",
coorectAnswer: "Kierowca 3",
}
];
let DrawQuestion = Math.floor(Math.random() * QuizDataQuestionFromDb.length);
if(isClick === true) {
EndDrawQuestion = QuizDataQuestionFromDb[DrawQuestion].question;
}
EndCorrectAnswer = QuizDataQuestionFromDb[DrawQuestion].coorectAnswer;
return (
<div className="questionDiv">
<h1 className='questionText1'>{EndDrawQuestion}</h1>
<button onClick={() => ButtonShowCorrectAnswer(isClick, setIsClick , score , setScore, navigate)} className='NextQuestionButton'>{isClick ? "Pokaż odpowiedź" : "Następne pytanie"}</button>
</div>
)
}
export const QuizDataImageFromDb = [
{
img: '/img/fire-truck/img1.jpg',
boxStyle: {
name: "Kierowca 1",
top: "48%",
left: "49%",
height: "39%",
width: "15.5%",
},
boxStyle1: {
name1: "Kierowca 3",
top1: "45%",
left1: "83%",
height1: "35%",
width1: "13%",
},
boxStyle2: {
name2: "Kierowca 2",
top2: "40%",
left2: "67%",
height2: "23%",
width2: "18%",
},
boxStyle3: {
name3: "Kabina",
top3: "38%",
left3: "30%",
height3: "35%",
width3: "13%",
}
},
{
img: '/img/fire-truck/imgTrack2.jpg',
boxStyle: {
name: "Dach",
top: "48%",
left: "69%",
height: "35%",
width: "35%",
},boxStyle1: {
width: 0,
height: 0,
},
boxStyle2: {
width: 0,
height: 0,
},
boxStyle3: {
width: 0,
height: 0,
},
},
{
img: '/img/fire-truck/imgTrack4.jpg',
boxStyle: {
name: "Dowódca 3",
top: "50%",
left: "15%",
height: "39%",
width: "15.5%",
},
boxStyle1: {
name1: "Dowódca 2",
top1: "42%",
left1: "32%",
height1: "24%",
width1: "18%",
},
boxStyle2: {
name2: "Dowódca 1",
top2: "52%",
left2: "49%",
height2: "39%",
width2: "14%",
},
boxStyle3: {
top2: "50%",
left2: "49%",
height2: "0",
width2: "0",
},
},
{
img: '/img/fire-truck/imgTrack3.jpg',
boxStyle: {
name: "Tył",
top: "42%",
left: "50.5%",
height: "41%",
width: "18%",
},
boxStyle1: {},
boxStyle2: {},
boxStyle3: {},
}
];
export const QuizGetImage = ({onPress, isClick}) => {
buttonQuiz = useRef();
const data = QuizDataImageFromDb.map(({img,boxStyle,boxStyle1,boxStyle2,boxStyle3}, index)=> {
let {top,width,height,left,name} = boxStyle;
let {top1,width1,height1,left1, name1} = boxStyle1;
let {top2,width2,height2,left2, name2} = boxStyle2;
let {top3,width3,height3,left3, name3} = boxStyle3;
ButtonShowCorrectAnswer = (isClick, setIsClick, score,setScore,navigate) => {
buttonsElement = document.querySelectorAll('.quiz_button');
const buttonsElementArray = Array.from(buttonsElement)
setIsClick(!isClick);
if(score === 9) {
navigate('/result');
}
if(isClick === true) {
buttonsElementArray.forEach(button => {
if(EndCorrectAnswer === button.name){
button.style.color = "yellow";
button.style.pointerEvents = "none";
button.style.display = "block";
}else {
button.style.pointerEvents = "none";
button.style.display = "none";
}
})
}else {
buttonsElementArray.forEach(button => {
button.style.color = "black";
button.style.display = "block";
button.style.pointerEvents = "auto";
setScore(score + 1);
})
}
}
return (
<>
<div className="boxElement">
<div className="quiz_container">
<img key = {index}src= {img} alt="track"/>
<button ref = {buttonQuiz} name = {name} onClick = {onPress} className="quiz_button" style = {{
"width": width,
"height": height,
"top": top,
"left": left,
}}>{name}</button>
<button ref = {buttonQuiz} name = {name1} className="quiz_button" onClick = {onPress}
style = {{
"width": width1,
"height": height1,
"top": top1,
"left": left1,
}}>{name1}</button>
<button ref = {buttonQuiz} name = {name2} className="quiz_button" onClick = {onPress}
style = {{
"width": width2,
"height": height2,
"top": top2,
"left": left2,
}}>{name2}</button>
<button ref = {buttonQuiz} name = {name3} className="quiz_button" onClick = {onPress}
style = {{
"width": width3,
"height": height3,
"top": top3,
"left": left3,
}}>{name3}</button>
</div>
</div>
</>
)
});
return (
data
)
}

View File

@ -0,0 +1,6 @@
const NotFound = () => {
return (
<h1>Not found page</h1>
)
}
export default NotFound

View File

@ -0,0 +1,41 @@
import React, {useState} from 'react';
import {useNavigate} from 'react-router-dom';
import QuizGame from './QuizGame';
const Quiz = () => {
const [isPopUpActive, setIsPopUpActive] = useState(false);
const navigate = useNavigate();
const PopUp = () => {
return (
<div className="quizContainer">
<div className="popup">
<h1 className='startQuizText'>Zacznij Quiz !</h1>
<div className="twoButton">
<button onClick={() => setIsPopUpActive(false)} className='startQuizBtn'>Start</button>
<button onClick = {() => navigate(-1)} className='backSiteQuizBtn'>Wróc do poprzedniej strony</button>
</div>
</div>
</div>
)
}
return (
isPopUpActive ? <PopUp/> : <QuizGame/>
)
}
export default Quiz;

View File

@ -0,0 +1,93 @@
import {useNavigate} from 'react-router-dom';
import React, {useState} from 'react';
import { QuizGetImage,QuizGetQuestion } from './GetImageAndQuestion';
import AppProvider from './AppContext';
import { EndCorrectAnswer } from './GetImageAndQuestion';
import Timer from './Timer';
import FooterQuiz from './FooterQuiz';
export let link = "";
console.log(link)
export let endScore = 1;
const NavigationQuiz = ({score}) => {
link = /[^/]*$/.exec(`${window.location.href}`)[0];
const navigate = useNavigate();
return (
<div id = 'navigationQuiz' className='navigation'>
<img onClick = {() => navigate(-1)} id = 'imgArrow' className='imgArrow' src="/img/arrow-turn.png" alt="arrow" />
<div className="timer">
<i className="fa-regular fa-hourglass"></i>
<AppProvider>
<Timer/>
</AppProvider>
</div>
<h1 className = 'score'>{score}/10</h1>
</div>
)
}
const QuizGame = () => {
const navigate = useNavigate();
const [score,setScore] = useState(0);
const [isClick, setIsClick] = useState(true);
const ButtonNextQuestionClick = (e) => {
setScore(score + 1);
if(score === 9)
navigate("/result");
else if (e.target.textContent === EndCorrectAnswer)
endScore++;
}
return (
<>
<NavigationQuiz score = {score} />
<div className="question">
<AppProvider>
<QuizGetQuestion
isClick = {isClick}
setIsClick = {setIsClick}
score = {score}
setScore = {setScore}
navigate = {navigate}
/>
</AppProvider>
</div>
<div className="trackImgBox">
<QuizGetImage
isClick = {isClick}
onPress={(e) => ButtonNextQuestionClick(e)}/>
</div>
<FooterQuiz/>
</>
)
}
export default QuizGame;

View File

@ -0,0 +1,47 @@
import React from 'react';
import { endScore } from './QuizGame';
import { SecondSeconds } from './Timer';
import { SecondMinutes } from './Timer';
import { useNavigate } from 'react-router-dom';
import Conffetti from './Conffetti';
import Player from './Sound';
import url from '../style/congratulation.mp3';
import { link } from './QuizGame';
import { InformationTrackFromDB } from './Truck';
const UpdateTrackScore = () => {
InformationTrackFromDB.forEach(track => {
if(link === track.img.split(/\.(?=[^\.]+$)/)[0].slice(18)){
track.progress = endScore * 10
}
})
}
const Result = () => {
const navigate = useNavigate();
UpdateTrackScore();
return (
<>
<div className="container">
<h2 className='congratulationh2'>BRAWO !!!</h2>
<img className='trophy' src="\img\trophy.webp" alt="" />
<h2 className='endScoreh2'>Twój wynik to: {endScore} / 10</h2>
<h3>Czas: {SecondMinutes} minut {SecondSeconds} sekund</h3>
<button id = "resultButton" className='NextQuestionButton' onClick = {() => navigate(-1)}>Zagraj jeszcze raz</button>
<button id = "resultButton" className='NextQuestionButton'onClick={() => navigate('/')}>Wróć do strony głównej</button>
<Conffetti/>
<Player url = {url}/>
</div>
</>
)
}
export default Result;

View File

@ -0,0 +1,84 @@
import React, {useContext, useState ,useRef} from 'react';
import { AppContext } from './AppContext';
const SearchBox = () => {
const {unitOsp, isContainerActive, setunitOsp,setIsContainerActive,localization} = useContext(AppContext);
const [value, setValue] = useState("");
const inputSearchRef = useRef(true);
const handleChangeInput = (e) => {
setIsContainerActive(true);
const tasks = localization.filter(localization => localization.toLowerCase().includes(e.target.value.toLowerCase()));
setValue(tasks);
}
const OptionJSXTag = () => {
let showListFromArray;
if(value.length > 0){
showListFromArray = value.map((item, index) => {
return (
<div onClick={handleLabelClick} name={item} key={index} className="option">
<input type="radio" className="radio" name="localization" />
{item}
</div>
)
})
}
return (
showListFromArray
)
}
const handleLabelClick = (e) => {
const nameOfUnit = e.target.getAttribute('name');
setunitOsp(nameOfUnit);
setIsContainerActive(!isContainerActive);
inputSearchRef.value = "";
}
const unitOspActive = unitOsp ? `Wybrana jednostka OSP: ${unitOsp}` : "Wyszukaj jednostkę OSP aby kontynuować";
return (
<div className="select-box">
<h3 className='OspUnitText'>{unitOspActive}</h3>
<input onChange={handleChangeInput} className='inputSearch' ref = {inputSearchRef} type="text" placeholder='Search' />
<div className={`options-container${isContainerActive ? " active" : ""}`}>
<ul className='ulInputSearch'>
</ul>
<OptionJSXTag/>
</div>
</div>
)
}
export default SearchBox;

View File

@ -0,0 +1,6 @@
const Player = ({ url }) => {
const audio = new Audio(url);
const play = audio.play();
};
export default Player;

View File

@ -0,0 +1,37 @@
import React, {useState, useEffect} from 'react';
export let SecondSeconds = 1;
export let SecondMinutes = 0;
const Timer = () => {
const [seconds, setSeconds] = useState(0);
const [minutes, setMinutes] = useState(0);
useEffect(() => {
let timer = setInterval(() => {
SecondSeconds = seconds;
SecondMinutes = minutes
setSeconds(seconds + 1);
if(seconds === 59) {
setMinutes(minutes + 1);
setSeconds(0);
}
}, 1000);
return () => clearInterval(timer);
});
return (
<h1 className='timerTime'>{minutes < 10 ? "0"+minutes: minutes} : {seconds < 10 ? "0"+seconds: seconds}</h1>
)
}
export default Timer;

View File

@ -0,0 +1,135 @@
import React, {useRef} from 'react';
import { useState } from 'react';
import { useParams } from 'react-router-dom';
import {Link} from 'react-router-dom';
export let InformationTrackFromDB = [
{
img: '../img/fire-truck/track1.jpg',
name: "Wóz 1",
progress: 70,
},
{
img: '../img/fire-truck/track2.jpg',
name: "Wóz 2",
progress: 20,
},
{
img: '../img/fire-truck/track3.jpg',
name: "Wóz 3",
progress: 0,
}
];
const Truck = () => {
const {id} = useParams();
const [trackName, setTrackName] = useState('');
const textChooseTrack = useRef(true);
const handleImageClick = (e) => {
const imgWithoutExtends = e.target.id.split(/\.(?=[^\.]+$)/);
const TrackName = imgWithoutExtends[0].slice(18);
setTrackName(TrackName);
textChooseTrack.current.style.color= 'black';
}
const handleArrowQuizClick = () => {
if(trackName === "") {
textChooseTrack.current.innerText = "Musisz wybrać wóz";
textChooseTrack.current.style.color= 'red';
}
}
const BoxComponent = () => {
const track = InformationTrackFromDB.map(({img,name,progress},index)=> {
return (
<>
<div className="boxTrack" key = {index} id = {img} onClick={handleImageClick} >
<img className='imageFireTrack' id = {img} src={img} alt="img" />
<div className="informationAboutTrack">
<h3 className= 'TrackName'>{name}</h3>
<progress className="progress" max="100" value={progress}></progress>
<div className='ScoringValueTrack'>
<h4>{progress / 10}/10</h4>
<h4>{progress}%</h4>
</div>
</div>
</div>
</>
)
});
return (
<>
{track}
<Link to = {trackName} style={{ textDecoration: 'none', color: 'black'}}>
<i onClick={handleArrowQuizClick} className="fa-solid fa-arrow-right"></i>
</Link>
</>
)
}
return (
<>
<div className='SecondContainer'>
<div className='navigation'>
<Link to = '/'> <img className='imgArrow' src="../img/arrow-turn.png" alt="arrow" /></Link>
<h1 className='unith1Id'>{id}</h1>
<img className='skrytkaImage' src="../img/skrytka.png" alt="skrytka" />
</div>
<div className='containerTrack'>
<h2 className='chooseCarH2' ref = {textChooseTrack}>{trackName ? `Wybrany wóz to: ${trackName}`: "Wybierz wóz: "}</h2>
<BoxComponent/>
</div>
<div className='FooterSecondSite'></div>
<a className='a-write-to-us-1' href = '/linkdostrony'>Zgłoś błąd lub pomysł ulepszenia aplikacji!</a>
<footer className='footerSecondSite'>General Public License<p className='skrytka-date'>Skrytka 2023</p></footer>
</div>
</>
)
}
export default Truck;

13
client/src/index.css Normal file
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;
}

20
client/src/index.js Normal file
View File

@ -0,0 +1,20 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import reportWebVitals from './reportWebVitals';
import { BrowserRouter } from 'react-router-dom';
import AppRoutes from './AppRoutes';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<BrowserRouter>
<AppRoutes />
</BrowserRouter>
</React.StrictMode >
);
// 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();

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
client/src/setupTests.js Normal file
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';

236
client/src/style/App.scss Normal file
View File

@ -0,0 +1,236 @@
@import './BottomFPage.scss';
@import './Mixins.scss';
@import "./Variables.scss";
@import "./Truck.scss";
@import './Quiz.scss';
@import './Result.scss';
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
::-webkit-scrollbar {
width: 1.5em;
}
::-webkit-scrollbar-track {
background-color: rgb(17,17,17);
}
::-webkit-scrollbar-thumb {
background-color: #9b9b9a;
border-radius: 100vw;
}
.container {
@include display-flex();
font-family: $priamry-font-family;
}
body {
font-family: $priamry-font-family;
background: #f7f6ff;
}
.container {
display: flex;
justify-content: space-around;
padding: 32px;
}
.imgArrrow {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
position: fixed;
}
#fa-solid {
color: rgb(171, 19, 19);
position: fixed;
top: 60%;
left :50%;
transform: translate(-50%,0%);
font-size: 100px;
&:hover {
color: rgb(220, 90, 90);
}
}
@media (max-width: 600px) {
#fa-solid {
top: 70%;
font-size: 80px;
}
}
/// SEARCH BOX
.select-box {
display: flex;
width: 360px;
justify-content: center;
align-items: center;
flex-direction: column;
margin-top: 2rem;
.options-container {
background: #2f3640;
color: #f5f6fa;
max-height: 0;
width: 100%;
opacity: 0;
margin-top: .7em;
transition: all 0.4s;
border-radius: 8px;
overflow: hidden;
order: 1;
}
.radio {
display: none;
}
label {
cursor: pointer;
}
.option:hover {
background: rgba(186,25,26,255);
}
.options-container {
&::-webkit-scrollbar {
width: 8px;
background: #f56a6a;
border-radius: 0 8px 8px 0;
}
&::-webkit-scrollbar-thumb {
background:rgba(186,25,26,255) ;
border-radius: 0 8px 8px 0;
}
}
.options-container.active {
max-height: 240px;
opacity: 1;
overflow-y: scroll;
z-index: 100;
.selected::after {
transform: rotateX(180deg);
top: -6px;
}
}
}
.inputSearch {
width: 360px;
font-size: 15px;
padding: .5em;
}
.OspUnitText {
text-align: center;
width: 100vw;
font-size: 18px;
margin-bottom: 1em;
font-weight: bold;
}
.select-box .option,
.selected {
padding: 12px 24px;
cursor: pointer;
}
//
// LOGO
.logo {
@include display-flex();
width: 230px;
height: 230px;
box-shadow: 16px 14px 20px #0000008c;
border-radius: 10px;
position: relative;
overflow: hidden;
&::before{
content: "";
background-image: conic-gradient(
#BB2723 60deg,
#BC3B2E 120deg,
#C48D7B 160deg,
#C4A093 200deg,
transparent 120deg
);
width: 150%;
height: 150%;
position: absolute;
animation: rotate 3s linear infinite;
}
&::after{
content: "";
@include display-flex();
width: 190px;
height: 190px;
position: absolute;
border-radius: 10px;
color: #ff0052;
font-size: larger;
letter-spacing: 5px;
box-shadow: inset 20px 20px 20px #0000008c;
}
}
.box {
z-index: 1000;
width: 220px;
height: 220px;
}
//
.welcome {
margin-top: .3em;
letter-spacing: 2px;
font-size: 55px;
text-align: center;
font-weight: bolder;
}
@keyframes rotate {
0%{
transform: rotate(0deg);
}
100%{
transform: rotate(-360deg);
}
}

View File

@ -0,0 +1,46 @@
@import './Mixins';
@import "./Variables.scss";
.FirstFooter {
display: flex;
font-size: 15px;
position: absolute;
top: 86%;
}
.a-write-to-us {
margin-left: 1em;
text-decoration: none;
color: blue;
}
.li-write-to-us {
list-style-type: none;
}
.footerFirstSite {
@include display-flex();
position: absolute;
top: 90.8%;
width: 100vw;
padding: 1.6rem;
color:white;
background-color: $primary-color-button;
font-size: .8em;
}
@media (orientation: portrait) {
.btnUnit {
top: 55%;
}
}
@media (min-width: 1200px) {
.btnUnit {
top: 55%;
}
}

View File

@ -0,0 +1,22 @@
@mixin display-flex(){
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
@mixin quizBtnView() {
width: 60%;
height: 4em;
font-size: 20px;
border: none;
border-radius: 5%;
background-color: #ba191a;
color: white;
margin-top: 15%;
&:hover {
background-color: #eb4b4b;
}
}

367
client/src/style/_Quiz.scss Normal file
View File

@ -0,0 +1,367 @@
@import './Mixins';
@import './Variables';
.quizContainer {
height: 100vh;
width: 100vw;
display: flex;
justify-content: center;
align-items: center;
}
.popup {
display: flex;
justify-content: space-around;
align-items: center;
flex-direction: column;
width: 70vw;
height: 80vh;
border: 2px solid black;
.startQuizText {
font-size: 50px;
text-align: center;
}
.twoButton {
width: 70vw;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-around;
.startQuizBtn {
@include quizBtnView();
}
.backSiteQuizBtn {
@include quizBtnView();
}
}
}
#navigationQuiz {
z-index: 1000;
position:fixed;
display: flex;
justify-content: space-between;
align-items: center;
}
.score {
color: white;
margin-right: 1em;
text-align: center;
font-size: 35px;
}
.fa-regular {
font-size: 50px;
color: white;
}
.question {
background-color: rgb(226, 74, 74);
width: 100vw;
height: 100px;
position: fixed;
top: 10%;
z-index: 1000;
display: flex;
align-items: center;
justify-content: space-around;
.questionText1 {
color: white;
font-size: 25px;
}
}
.bixBox img {
width: 100%;
height: auto;
}
.bigBox .boxStyle{
position: absolute;
outline: none;
background: none;
border: greenyellow solid 2px;
transform: translate(-50%, -50%);
font-family: monospace;
}
.bixBox {
position: relative;
width: 100%;
overflow: clip;
}
.boxStyle {
width: 110px;
height:160px;
position: absolute;
top:45%;
left: 45%;
border: 8px solid black;
}
.timer {
color: white;
display: flex;
justify-content: center;
.timerTime {
text-align: center;
margin-left: 1.5em;
}
}
.imgArrow {
cursor: pointer;
}
.trackImgBox {
width: 100vw;
display: flex;
flex-wrap: wrap;
justify-content: space-around;
align-items: center;
.box1 {
margin-top: 250px;
min-width: 400px;
min-height: 400px;
max-width: 650px;
max-height: 650px;
}
}
.questionDiv {
display: flex;
justify-content: space-around;
align-items: center;
width: 100vw;
}
.NextQuestionButton {
font-size: 30px;
padding: .5em;
border: none;
background-color: #8880ff;
max-width: 90vw;
width: 400px;
color: white;
border-radius: 5%;
&:hover {
background-color: #605abe;
}
}
.footerRealization {
display: flex;
justify-content: space-around;
margin-top: 1em;
border-top: 3px solid white;
}
.footerMedia {
display: flex;
justify-content: space-around;
margin-top: 1em;
border-top: 3px solid white;
}
.firstMedia {
.fa-brands {
font-size: 30px;
padding: .3em;
&:hover {
color: black;
}
}
}
@media (max-width: 500px) {
.score {
font-size: 20px;
}
.fa-regular {
font-size: 34px;
}
.NextQuestionButton {
font-size: 24px;
width: 245px;
}
.imgArrow {
width: 45px;
height: 45px;
}
.timerTime {
font-size: 25px;
}
}
.patronImg {
width: $width-footer-icon / 3;
height: $width-footer-icon / 3;;
}
.patronImg1 {
width: $width-footer-icon / 2;
height: 70px;
}
.firstPatron {
width: $width-footer-icon;
}
.secondPatron {
width: $width-footer-icon;
}
.footerSecondSite {
font-size: 15px;
}
.a-write-to-us-1 {
font-size: 20px;
}
@media (max-width: 650px) {
.box1 {
width: 500px;
height: 500px;
padding: 3em;
}
.a-write-to-us-1 {
font-size: 16px;
}
.questionText1 {
font-size: 20px;
}
}
.boxElement {
@include display-flex;
}
@media (max-width: 1478px) {
.boxElement:nth-child(1) {
margin-top: 20%;
}
}
@media (min-width: 1478px) {
.boxElement:nth-child(1),
.boxElement:nth-child(2) {
margin-top: 10%;
}
}
@media (min-width: 1200px) {
.boxElement:nth-child(1) {
margin-top: 10%;
}
}
.boxOfImage {
display: flex;
flex-direction: row;
justify-content: space-around;
align-items: center;
flex-grow: 1;
}
.quiz_container {
position: relative;
width: 90%;
max-width: 750px;
margin-top: 10%;
}
@media (min-width: 1000px) {
.quiz_container {
width: 95%;
}
}
.quiz_container img {
width: 100%;
height: auto;
}
.quiz_container .quiz_button {
position: absolute;
outline: none;
background: none;
border: greenyellow solid 2px;
transform: translate(-50%, -50%);
}
.quiz_button {
font-size: 20px;
font-weight: bolder;
color: black;
z-index: 900;
&:hover {
cursor: pointer;
}
}
@media (max-width: 600px) {
.quiz_button {
font-size: 12px;
font-weight: bold;
}
.quiz_container {
margin-top: 20%;
}
}
.quiz_container button:hover {
border: white solid 2px;
}
.quiz_container button:active {
border: blue solid 2px;
}
.quiz_container button span { background-color: darkorange; }
.skrytka {
display: block;
border: 3px solid yellow;
}

View File

@ -0,0 +1,43 @@
.trophy {
min-width: 450px;
min-height: 450px;
max-width: 450px;
max-height: 450px;
}
.congratulationh2 {
font-size: 40px;
letter-spacing: 3px;
margin-bottom: 0.1em;
}
#resultButton {
margin-top: 5%;
}
.endScoreh2 {
font-size: 40px;
}
@media (max-width: 800px) {
.trophy {
width: 200px;
height: 200px;
}
.endScoreh2 {
font-size: 35px;
}
.congratulationh2 {
font-size: 30px;
}
}
@media (max-width: 400px) {
.endScoreh2 {
font-size: 30px;
}
.trophy {
width: 150px;
height: 150px;
}
}

View File

@ -0,0 +1,273 @@
$progress-bar-color: linear-gradient(90deg, rgba(185,25,25,1) 0%, rgba(185,25,25,1) 10%, rgba(220,45,45,1) 20%, rgba(220,45,45,1) 30%, rgba(255,62,62,1) 40%, rgba(255,62,62,1) 50%, rgba(255,110,110,1) 60%, rgba(255,161,161,1) 70%, rgba(255,161,161,1) 80%, rgba(255,197,197,1) 90%, rgba(255,197,197,1) 100%);
body {
overflow-x: hidden;
}
.SecondContainer {
margin: 0;
padding: 0;
width: 100vw;
overflow-x: hidden;
}
.navigation {
width: 100vw;
height: 10vh;
background-color: rgba(186,25,26,255);
display: flex;
justify-content: space-between;
align-items: center;
}
.imgArrow {
width: 60px;
height: 60px;
margin-left: .5em;
}
.unith1Id {
font-size: 40px;
color: white;
text-align: center;
margin-left: 2em;
}
.skrytkaImage {
padding: 2em;
width: 140px;
height: 140px;
}
@media (max-width: 600px) {
.unith1Id {
margin-left: 25px;
font-size: 32px;
}
}
@media (max-width: 370px) {
.unith1Id {
margin-left: 30px;
}
}
@media (max-width: 400px) {
.unith1Id {
margin-left: 30px;
}
}
@media (max-width: 500px) {
.unith1Id {
margin-left: 75px;
}
}
.a-write-to-us-1 {
width: 100vw;
padding: 0.5em;
display: flex;
justify-content: center;
text-decoration: none;
color: #1f5bb5;
padding-top: 1em;
padding-bottom: 1em;
&:hover {
color: #709bdb;
}
}
.containerTrack {
min-height: 78vh;
width: 100vw;
.chooseCarH2 {
text-align: center;
}
}
@media (min-width: 1200px) {
.chooseCarH2 {
margin-top: 2%;
}
.boxTrack {
margin-top: 2%;
}
}
.footerSecondSite {
width: 100vw;
padding-top: 2em;
padding-bottom: 2em;
color:white;
text-align: center;
background-color: $primary-color-button;
font-size: .8em;
}
.skryka-date {
text-align: center;
}
.containerTrack {
display: flex;
flex-direction: column;
justify-content: space-around;
align-items: center;
cursor: pointer;
}
.boxTrack {
display: flex;
align-items: center;
justify-content: space-around;
border: 2px solid black;
width: 80vw;
border-radius: 1em;
&:hover {
background-color: rgb(235, 218, 218);
}
// transform:
// scale(0.9)
// rotateY(-20deg)
// rotateX(35deg)
// translateZ(2.5rem);
// transform-origin: 50% 100%;
// transform-style: preserve-3d;
// box-shadow: 1rem 1rem 2rem rgba(0,0,0,0.25);
// transition: .6s ease transform;
// &:hover {
// transform: scale(1);
// }
// &::before {
// transform: translateZ(4rem);
// &:hover {
// transform: translateZ(0);
// }
// }
// &::after {
// transform: translateZ(-4rem);
// &:hover {
// transform: translateZ(-1px);
// }
// }
}
.informationAboutTrack {
display: flex;
flex-direction: column;
justify-content: center;
font-size: 24px;
}
.imageFireTrack {
width: 170px;
height: 80px;
}
.progress {
border: none;
width: 200px;
height: 40px;
}
progress::-moz-progress-bar {
background: $progress-bar-color;
}
progress::-webkit-progress-value {
background: $progress-bar-color;
}
progress::-webkit-progress-bar {
background: rgba(213,194,194,1);
}
.ScoringValueTrack {
display: flex;
justify-content: space-between;
}
.fa-solid {
color: rgb(171, 19, 19);
font-size: 80px;
&:hover {
transform: scale(1.2);
}
}
@media (max-width: 600px) {
.imageFireTrack {
width: 150px;
height: 70px;
}
.progress {
width: 100px;
height: 30px;
}
.ScoringValueTrack {
font-size: 16px;
}
.TrackName {
font-size: 20px;
}
.skrytkaImage {
height: 124px;
}
}
@media (min-width: 800px) {
.imageFireTrack {
width: 230px;
height: 100px;
}
}
@media (min-width: 1000px) {
.imageFireTrack {
width: 270px;
height: 140px;
}
}
@media (min-width: 1300px) {
.imageFireTrack {
width: 300px;
height: 180px;
}
}

View File

@ -0,0 +1,3 @@
$priamry-font-family: "Segoe UI";
$primary-color-button: rgba(186,25,26,255);
$width-footer-icon: 300px;

Binary file not shown.