Skip to content
Snippets Groups Projects
Commit 4d538895 authored by Iurii Lebedev's avatar Iurii Lebedev :speech_balloon:
Browse files

Main page without pagination (waiting for endoint of total page count) + Quiz...

parent 91616f4d
No related branches found
No related tags found
1 merge request!2Main page without pagination (waiting for endoint of total page count) + Quiz...
Showing
with 1034 additions and 73 deletions
This diff is collapsed.
...@@ -3,16 +3,19 @@ ...@@ -3,16 +3,19 @@
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"dependencies": { "dependencies": {
"@mui/material": "^5.14.18",
"@testing-library/jest-dom": "^5.17.0", "@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0", "@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0", "@testing-library/user-event": "^13.5.0",
"axios": "^1.6.0", "axios": "^1.6.0",
"bootstrap": "^4.6.0",
"js-cookie": "^3.0.5", "js-cookie": "^3.0.5",
"mobx": "^6.10.2", "mobx": "^6.0.5",
"mobx-react-lite": "^4.0.5", "mobx-react-lite": "^4.0.5",
"react": "^18.2.0", "react": "^18.2.0",
"react-bootstrap": "2.9.1",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-router-dom": "^6.18.0", "react-router-dom": "^5.1.0",
"react-scripts": "5.0.1", "react-scripts": "5.0.1",
"web-vitals": "^2.1.4" "web-vitals": "^2.1.4"
}, },
......
public/favicon.ico

3.78 KiB

<!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" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/css/bootstrap.min.css" integrity="sha256-2TnSHycBDAm2wpZmgdi0z81kykGPJAkiUY+Wf97RbvY=" crossorigin="anonymous">
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/js/bootstrap.min.js" integrity="sha256-gOQJIa9+K/XdfAuBkg2ONAdw5EnQbokw/s2b8BqsRFg=" crossorigin="anonymous"></script>
</body>
</html>
public/logo192.png

5.22 KiB

public/logo512.png

9.44 KiB

public/logo_Qwiz_Wiz.png

7.01 KiB

{
"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"
}
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:
...@@ -6,8 +6,8 @@ import AppRouter from "./components/AppRouter"; ...@@ -6,8 +6,8 @@ import AppRouter from "./components/AppRouter";
function App() { function App() {
return ( return (
<BrowserRouter> <BrowserRouter>
<NavBar/> <NavBar/>
<AppRouter/> <AppRouter/>
</BrowserRouter> </BrowserRouter>
); );
} }
......
import React, {createElement, useContext, useState} from 'react';
import {Button, Form} from "react-bootstrap";
import {QuestionForm} from "./QuestionForm";
import {observer} from "mobx-react-lite";
import QuizService from "../service/quiz.service";
import {AddQuiz} from "../models/addQuiz";
import {Context} from "../index";
export const AddQuizForm = observer(() => {
const {user} = useContext(Context)
const [errMsg, setErrMsg] = useState('')
const [quizName, setQuizName] = useState('');
const questions = []
const publish = () => {
QuizService.create(new AddQuiz(quizName, user.id, questions), user.token).then(r =>{
try {
console.log('Quiz created by ' + user.username)
} catch (e) {
setErrMsg(e.response.data.message);
}
})
}
const addQuestion = () => {
let quiz = questions;
quiz.push(
<li className='quiz-form-item'>
<QuestionForm className='quiz-form-question'/>
</li>
);
}
return (
<Form className='quiz-form'>
<p className="errorMsg">{errMsg}</p>
<div className='new-quiz-form-body'>
<div id='quiz-info' className='quiz-form-item'>
<Form.Control id='quiz-name' type="text" value={quizName} onChange={e => setQuizName(e.target.value)}/>
<span className='quiz-controls'>
<Button>Delete</Button>
<Button onClick={() => publish()}>Save and Publish</Button>
</span>
</div>
<div>
<Button variant='primary' className='btn-btn plus' onClick={() => addQuestion}>+</Button>
<ul id='quiz-questions'>
<li className='quiz-form-item'>
<QuestionForm className='quiz-form-question'/>
</li>
{questions}
</ul>
<Button variant='primary' className='btn-btn plus' onClick={() => addQuestion}>+</Button>
</div>
</div>
</Form>
);
});
\ No newline at end of file
import React, {useContext} from 'react'; import React, {useContext} from 'react';
import {Route, Routes} from 'react-router-dom' import {Route, Switch} from 'react-router-dom'
import {observer} from "mobx-react-lite"; import {observer} from "mobx-react-lite";
import {Context} from "../index.js"; import {Context} from "../index.js";
import {authRoutes, publicRoutes} from "../routes/routes"; import {authRoutes, publicRoutes} from "../routes/routes";
import {Auth} from "../pages/Auth"; import {Redirect} from "react-router-dom";
import {AUTH_ROUTE} from "../utils/consts";
const AppRouter = observer(() => { const AppRouter = observer(() => {
const {user} = useContext(Context) const {user} = useContext(Context)
return ( return (
<Routes> <Switch>
{user.getUser !== undefined && authRoutes.map(({path, Element}) => {user.isAuth && authRoutes.map(({path, Component}) =>
<Route key={path} path={path} component={Element} exact/> <Route key={path} path={path} component={Component} exact/>
)} )}
{publicRoutes.map(({path, Element}) => {publicRoutes.map(({path, Component}) =>
<Route key={path} path={path} element={Element} exact/> <Route key={path} path={path} component={Component} exact/>
)} )}
<Route path="*" element ={<Auth/>}/> <Redirect to={AUTH_ROUTE}/>
</Routes> </Switch>
); );
}); });
......
import React, {useContext, useEffect, useState} from 'react';
import Row from "react-bootstrap/Row";
import {observer} from "mobx-react-lite";
import {Context} from "../index";
import QuizService from "../service/quiz.service";
import UserService from "../service/user.service";
import {useHistory} from "react-router-dom/cjs/react-router-dom";
import {QUIZ_ROUTE} from "../utils/consts";
export const GlobalQuizzesList = observer(() => {
const {globalQuizzes} = useContext(Context);
const history = useHistory();
const [update, setUpdate] = useState(0);
const [author, setAuthor] = useState('');
const [errMsg, setErrMsg] = useState('');
useEffect(() => {
QuizService.fetchQuizzes(globalQuizzes.page, globalQuizzes.limit).then(resp => {
console.log(resp)
globalQuizzes.setQuizzes(resp.data)
globalQuizzes.setTotalCount(resp.data.length)
console.log(globalQuizzes.quizzes)
setUpdate(prevState => prevState + 1)
});
}, [setUpdate, globalQuizzes]);
return (
<Row className="ml-4 ">
<p className="errorMsg">{errMsg}</p>
{ globalQuizzes.totalCount > 0 &&
globalQuizzes.quizzes.map(quiz =>
<Row className='m-2 ml-4 border-bottom' onClick={() => history.push(QUIZ_ROUTE + '/' + quiz.id)}>
{quiz.name} by {quiz.authorId}
</Row>)
}
</Row>
);
});
\ No newline at end of file
import React from 'react'; import React, {useContext} from 'react';
import {Button, Col, Container, Nav, Navbar} from "react-bootstrap";
import {Context} from "../index";
import {useHistory} from "react-router-dom/cjs/react-router-dom";
import {AUTH_ROUTE, MAIN_ROUTE, MYQUIZ_ROUTE, NEWQUIZ_ROUTE, STAT_ROUTE, USER_ROUTE} from "../utils/consts";
import {observer} from "mobx-react-lite";
const NavBar = observer(() => {
const {user} = useContext(Context)
const history = useHistory();
function logOut() {
user.setUser({})
user.setIsAuth(false)
history.push(AUTH_ROUTE)
}
const NavBar = () => {
return ( return (
<div></div> <Navbar className={user.isAuth ? 'd-flex flex-column m-0' : 'd-none'}>
<Container className='mt-3 mb-5'>
<Button onClick={() => history.push(NEWQUIZ_ROUTE)}>
Create New Quiz
</Button>
</Container>
<Col>
<Nav variant='underline' className='d-flex flex-column'>
<Nav.Link variant="text" onClick = {() => history.push(MYQUIZ_ROUTE)}>My quizzes</Nav.Link>
<Nav.Link variant="text" onClick = {() => history.push(MAIN_ROUTE)}>Global quizzes</Nav.Link>
<Nav.Link variant="text" onClick = {() => history.push(STAT_ROUTE)}>Statistics</Nav.Link>
<Nav.Link variant="text" onClick = {() => history.push(USER_ROUTE)}>Account</Nav.Link>
<Nav.Link className='last-link mt-4' variant="text" onClick={() => logOut()}>Log out</Nav.Link>
</Nav>
</Col>
</Navbar>
); );
} });
export default NavBar; export default NavBar;
\ No newline at end of file
import {observer} from "mobx-react-lite";
import {Card} from "react-bootstrap";
import Row from "react-bootstrap/Row";
export const QuestionForm = observer((question, counter) => {
const types = ['TEXT', 'SELECT_RIGHT_OPTION', 'MULTIPLE_RIGHT_OPTIONS']
return (
<Card className='d-flex flex-column align-items-center'>
<Row>
<select name="question-type" id="q-type">
{types.map(type =>
<option key={type} value={type}>{type}</option>
)}
</select>
<input type="number" placeholder="0 points"/>
</Row>
<input type="text"/>
</Card>
)
});
\ No newline at end of file
import React from 'react';
import {observer} from "mobx-react-lite";
import {Card, Col, Form} from "react-bootstrap";
import Row from "react-bootstrap/Row";
export const QuestionItem = observer((question) => {
return (
<Card>
<Col>
<Row>
<div>{question.type}</div>
<div>{question.points} {question.points > 1 ? 'points' : 'point'}</div>
</Row>
<Row>
<div>{question.text}</div>
</Row>
</Col>
<Col>
{ question.type === "TEXT" ?
question.answerOptions.map(answer =>
<Col>
<input type="checkbox" name={answer.text} id={answer.id}/>
<label htmlFor={answer.id}></label>
</Col>
)
:
<input type="text"/>
}
</Col>
</Card>
);
});
\ No newline at end of file
...@@ -3,13 +3,23 @@ import ReactDOM from 'react-dom/client'; ...@@ -3,13 +3,23 @@ import ReactDOM from 'react-dom/client';
import './index.css'; import './index.css';
import App from './App'; import App from './App';
import UserStore from './stores/UserStore'; import UserStore from './stores/UserStore';
import QuizStore from "./stores/QuizStore";
import GlobalQuizzesStore from "./stores/GlobalQuizzesStore";
import reportWebVitals from './reportWebVitals'; import reportWebVitals from './reportWebVitals';
// Bootstrap CSS
import "bootstrap/dist/css/bootstrap.min.css";
// Bootstrap Bundle JS
import "bootstrap/dist/js/bootstrap.bundle.min";
export const Context = createContext(null) export const Context = createContext(null)
const root = ReactDOM.createRoot( document.getElementById('root')) const root = ReactDOM.createRoot( document.getElementById('root'))
root.render( root.render(
<StrictMode> <StrictMode>
<Context.Provider value ={{ <Context.Provider value ={{
user: new UserStore(), user: new UserStore(),
quiz: new QuizStore(),
globalQuizzes: new GlobalQuizzesStore()
}}> }}>
<App /> <App />
</Context.Provider> </Context.Provider>
......
.App { @import url('https://fonts.googleapis.com/css2?family=Rubik:wght@300&display=swap');
@import url('https://fonts.googleapis.com/css2?family=Rubik:wght@500&display=swap');
* {
margin: 0;
padding: 0;
font-family: 'Rubik', sans-serif;
box-sizing: border-box;
}
.errorMsg {
text-align: center; text-align: center;
margin-top: 50px;
color: red;
margin-bottom: -60px;
}
.instructions {
width: auto;
font-size: 0.78rem;
border-radius: 0.5rem;
background: #A895C5;
color: #F0F0F0FF;
position: relative;
margin: 0;
} }
.App-logo { .hide {
height: 40vmin; display: none;
pointer-events: none;
} }
@media (prefers-reduced-motion: no-preference) { #root{
.App-logo { display: flex;
animation: App-logo-spin infinite 20s linear; background-color: #CFC4DF;
} flex-direction: row;
} }
.App-header { .navbar {
background-color: #282c34;
min-height: 100vh;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; background-color: #CFC4DF;
justify-content: center; height: 100vh;
font-size: calc(10px + 2vmin); width: 15vw;
color: white;
} & button {
height: 50px;
width: 200px;
background-color: #7A61A8;
color: #F0F0F0FF;
padding: 0.25rem;
margin: 1rem;
border: none;
font-size: x-large;
border-radius: 5px;
}
.App-link {
color: #61dafb;
} }
@keyframes App-logo-spin { .content-block{
from { background-color: white;
transform: rotate(0deg); width: auto;
& h1 {
font-size: xxx-large;
font-weight: 500;
color: #A895C5;
} }
to {
transform: rotate(360deg); }
.btn-primary{
background-color: #7A61A8 !important;
border-color: #7A61A8 !important;
}
.btn-outline-primary{
background: none !important;
color: #7A61A8 !important;
border-color: #7A61A8 !important;
}
.plus{
border-radius: 50% !important;
margin-left: 50%;
}
#auth {
margin: auto;
display: flex;
justify-content: center;
align-items: center;
text-align: center;
min-height: 100vh;
.card {
border-radius: 20px;
display: flex;
height: 600px;
flex-direction: column;
align-items: center;
margin: auto;
padding: 50px;
& h2{
font-size: xx-large;
font-weight: 500;
color: #A895C5;
}
& img {
width: 70px;
height: 70px;
}
& form {
max-height: 400px;
& div {
display: flex;
flex-direction: column;
}
& input {
min-width: 20vw;
min-height: 5vh;
::placeholder{
color: #9C9C9C;
}
margin-bottom: 8px;
margin-top: 8px;
}
}
} }
} }
export class AddQuiz{
constructor(name, authorId, questions) {
this.name = name
this.authorId = authorId
this.questions = questions
}
}
\ No newline at end of file
export class LogUser{ export class LogUser{
constructor(username, password) { constructor(emailOrName, rawPwd) {
this.username = username; this.emailOrName = emailOrName;
this.password = password; this.rawPwd = rawPwd;
} }
} }
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment