├── .gitignore ├── LICENSE ├── README.md ├── package-lock.json ├── package.json ├── public ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt └── src ├── index.css ├── index.js ├── modules ├── components │ ├── data.js │ ├── errorMessage.js │ ├── loadingMessage │ │ ├── index.js │ │ └── loading.gif │ ├── map.js │ ├── pagination.js │ ├── resultsProgressPetition.js │ ├── startEndDate.js │ ├── topBar.js │ └── userChecker.js ├── contexts │ └── user.js ├── hooks │ └── useFetch.js ├── pages │ ├── authentication │ │ └── index.js │ ├── homePage │ │ ├── images │ │ │ └── LabelVoting.png │ │ └── index.js │ ├── myProfile │ │ ├── images │ │ │ └── userIcon.jpg │ │ └── index.js │ ├── newPetition │ │ └── index.js │ ├── newVoting │ │ └── index.js │ ├── petition │ │ ├── components │ │ │ └── resultsCircle.js │ │ └── index.js │ ├── petitionList │ │ └── index.js │ ├── voting │ │ ├── components │ │ │ └── results.js │ │ └── index.js │ └── votingList │ │ └── index.js └── routes.js └── utils ├── dateDecoder.js ├── dateStringify.js ├── makeTwoDigit.js ├── queryCoder.js ├── queryDecoder.js └── resizeImage.js /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | 25 | .eslintcache -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 MaksGovor 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Voting-front 2 | Front for Voting System 3 | 4 | ## Table of contents 5 | * [Voting-front](#Voting-front) 6 | * [How to use](#how-to-use) 7 | * [License](#license) 8 | * [Contact](#contact) 9 | 10 | ## How to use 11 | - At first you should register 12 | - Then you can view petition or voting list 13 | - You can add petition by clicking 'Додати петицію' 14 | - You can add voting by clicking 'Додати голосування' 15 | - You can open voting and after having voted, you can see results of this voting in general, and by regions. 16 | - You can open petition see results in general, and by regions. Also you can sign it. 17 | - On profile page you can find information about you and logout. 18 | 19 | ## License 20 | 21 | MIT © 22 | 23 | ## Contact 24 | 25 | You can ask me a question here: 26 | * [Telegram](https://t.me/tedi4t) 27 | 28 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "voting-system-front", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@material-ui/core": "^4.11.2", 7 | "@material-ui/lab": "^4.0.0-alpha.57", 8 | "@testing-library/jest-dom": "^4.2.4", 9 | "@testing-library/react": "^9.5.0", 10 | "@testing-library/user-event": "^7.2.1", 11 | "axios": "^0.21.1", 12 | "nodemon": "^2.0.7", 13 | "react": "^16.13.1", 14 | "react-cookie": "^4.0.3", 15 | "react-dom": "^16.13.1", 16 | "react-router-dom": "^5.2.0", 17 | "react-scripts": "^4.0.1" 18 | }, 19 | "scripts": { 20 | "start": "react-scripts start", 21 | "build": "react-scripts build", 22 | "test": "react-scripts test", 23 | "eject": "react-scripts eject" 24 | }, 25 | "eslintConfig": { 26 | "extends": "react-app" 27 | }, 28 | "browserslist": { 29 | "production": [ 30 | ">0.2%", 31 | "not dead", 32 | "not op_mini all" 33 | ], 34 | "development": [ 35 | "last 1 chrome version", 36 | "last 1 firefox version", 37 | "last 1 safari version" 38 | ] 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tedi4t/voting-front/62f24596e61eb2784a8b97d7177c1bd035df511d/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | Vote! 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tedi4t/voting-front/62f24596e61eb2784a8b97d7177c1bd035df511d/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tedi4t/voting-front/62f24596e61eb2784a8b97d7177c1bd035df511d/public/logo512.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | overflow-x: hidden; 9 | } 10 | 11 | code { 12 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 13 | monospace; 14 | } 15 | 16 | .MuiPaginationItem-page.Mui-selected, 17 | .MuiPaginationItem-page.Mui-selected:hover, 18 | .MuiPaginationItem-page.Mui-selected.Mui-focusVisible { 19 | background: rgba(255, 193, 7, 0.1)!important; 20 | border: 1px solid #ffc107!important; 21 | color: #ffbf00; 22 | } 23 | 24 | .MuiPaginationItem-outlined { 25 | border: 1px solid #ffc107!important; 26 | } 27 | 28 | .MuiPagination-ul { 29 | display: grid; 30 | grid-template-columns: repeat(auto-fit, minmax(40px, 1fr)); 31 | justify-content: center; 32 | grid-gap: 7px; 33 | list-style: none; 34 | } 35 | 36 | nav { 37 | display: grid; 38 | align-items: center; 39 | } 40 | 41 | /* .input-file-button:hover { 42 | background: #ccc; 43 | color: white; 44 | } */ -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import { CookiesProvider } from "react-cookie"; 5 | import { 6 | BrowserRouter as Router, 7 | } from "react-router-dom"; 8 | import { UserProvider } from './modules/contexts/user'; 9 | import UserChecker from './modules/components/userChecker'; 10 | 11 | import TopBar from "./modules/components/topBar"; 12 | import Routes from "./modules/routes"; 13 | 14 | function App () { 15 | return ( 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | ); 29 | } 30 | 31 | ReactDOM.render( 32 | , 33 | document.getElementById('root') 34 | ); 35 | -------------------------------------------------------------------------------- /src/modules/components/data.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const data = ({ mouseLocation, content }) => { 4 | return ( 5 |
21 | { content } 22 |
23 | ) 24 | } 25 | 26 | export default data; -------------------------------------------------------------------------------- /src/modules/components/errorMessage.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const errorMessage = ({ error }) => { 4 | return ( 5 |
6 | 7 | { error || '' } 8 | 9 |
10 | ) 11 | } 12 | 13 | export default errorMessage; -------------------------------------------------------------------------------- /src/modules/components/loadingMessage/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import loadingLogo from './loading.gif' 3 | 4 | const loadingMessage = () => { 5 | return ( 6 |
19 | loading... 27 |
28 | ) 29 | } 30 | 31 | export default loadingMessage; -------------------------------------------------------------------------------- /src/modules/components/loadingMessage/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tedi4t/voting-front/62f24596e61eb2784a8b97d7177c1bd035df511d/src/modules/components/loadingMessage/loading.gif -------------------------------------------------------------------------------- /src/modules/components/map.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from "react"; 2 | import Data from "./data"; 3 | import useFetch from "../hooks/useFetch"; 4 | import LoadingMessage from "./loadingMessage"; 5 | import { Fragment } from "react"; 6 | 7 | export default ({ data, DisplayDataModule }) => { 8 | const hoverStates = Array.from({length: 28}, () => useState(false)); 9 | const fillColor = '#ffc107'; 10 | const [{ 11 | response: responseAllDistricts, 12 | isLoading: isLoadingAllDistricts 13 | }, doFetchAllDistricts] 14 | = useFetch(`/districts`); 15 | 16 | const [mouseLocation, setMouseLocation] = useState({ x: 0, y: 0 }); 17 | const [isDistrictClicked, setIsDistrictClicked] = useState(false); 18 | const [selectedDistrictId, setSelectedDistrictId] = useState(0); 19 | 20 | useEffect(() => { 21 | doFetchAllDistricts(); 22 | }, [doFetchAllDistricts]) 23 | 24 | const handleMapClick = e => { 25 | const targetId = e.target.id; 26 | if (targetId) { 27 | setSelectedDistrictId(e.target.id); 28 | setMouseLocation({ x: e.pageX, y: e.pageY }); 29 | setIsDistrictClicked(true); 30 | } else { 31 | setIsDistrictClicked(false); 32 | } 33 | } 34 | 35 | return ( 36 |
37 | { 38 | isLoadingAllDistricts && ( 39 | 40 | ) 41 | } 42 | { 43 | isDistrictClicked && selectedDistrictId && ( 44 | 48 |
54 | { responseAllDistricts[selectedDistrictId].name } 55 |
56 | 59 | 60 | } 61 | /> 62 | ) 63 | } 64 | 80 | hoverStates[0][1](true)} 88 | onMouseLeave = {e => hoverStates[0][1](false)} 89 | > 90 | 91 | hoverStates[13][1](true)} 99 | onMouseLeave = {e => hoverStates[13][1](false)} 100 | > 101 | 102 | hoverStates[24][1](true)} 110 | onMouseLeave = {e => hoverStates[24][1](false)} 111 | > 112 | 113 | hoverStates[16][1](true)} 121 | onMouseLeave = {e => hoverStates[16][1](false)} 122 | > 123 | 124 | hoverStates[23][1](true)} 132 | onMouseLeave = {e => hoverStates[23][1](false)} 133 | > 134 | 135 | hoverStates[8][1](true)} 143 | onMouseLeave = {e => hoverStates[8][1](false)} 144 | > 145 | 146 | hoverStates[21][1](true)} 154 | onMouseLeave = {e => hoverStates[21][1](false)} 155 | > 156 | 157 | hoverStates[12][1](true)} 165 | onMouseLeave = {e => hoverStates[12][1](false)} 166 | > 167 | 168 | hoverStates[18][1](true)} 176 | onMouseLeave = {e => hoverStates[18][1](false)} 177 | > 178 | 179 | hoverStates[6][1](true)} 187 | onMouseLeave = {e => hoverStates[6][1](false)} 188 | > 189 | 190 | hoverStates[2][1](true)} 198 | onMouseLeave = {e => hoverStates[2][1](false)} 199 | > 200 | 201 | hoverStates[22][1](true)} 209 | onMouseLeave = {e => hoverStates[22][1](false)} 210 | > 211 | 212 | hoverStates[10][1](true)} 220 | onMouseLeave = {e => hoverStates[10][1](false)} 221 | > 222 | 223 | hoverStates[9][1](true)} 231 | onMouseLeave = {e => hoverStates[9][1](false)} 232 | > 233 | 234 | hoverStates[14][1](true)} 242 | onMouseLeave = {e => hoverStates[14][1](false)} 243 | > 244 | 245 | hoverStates[1][1](true)} 253 | onMouseLeave = {e => hoverStates[1][1](false)} 254 | > 255 | 256 | hoverStates[5][1](true)} 264 | onMouseLeave = {e => hoverStates[5][1](false)} 265 | > 266 | 267 | hoverStates[17][1](true)} 275 | onMouseLeave = {e => hoverStates[17][1](false)} 276 | > 277 | 278 | hoverStates[3][1](true)} 286 | onMouseLeave = {e => hoverStates[3][1](false)} 287 | > 288 | 289 | hoverStates[4][1](true)} 297 | onMouseLeave = {e => hoverStates[4][1](false)} 298 | > 299 | 300 | hoverStates[19][1](true)} 308 | onMouseLeave = {e => hoverStates[19][1](false)} 309 | > 310 | 311 | hoverStates[11][1](true)} 319 | onMouseLeave = {e => hoverStates[11][1](false)} 320 | > 321 | 322 | hoverStates[15][1](true)} 330 | onMouseLeave = {e => hoverStates[15][1](false)} 331 | > 332 | 333 | hoverStates[7][1](true)} 341 | onMouseLeave = {e => hoverStates[7][1](false)} 342 | > 343 | 344 | hoverStates[25][1](true)} 352 | onMouseLeave = {e => hoverStates[25][1](false)} 353 | > 354 | 355 | hoverStates[20][1](true)} 363 | onMouseLeave = {e => hoverStates[20][1](false)} 364 | > 365 | 366 | hoverStates[26][1](true)} 374 | onMouseLeave = {e => hoverStates[26][1](false)} 375 | > 376 | 377 | 380 | 381 | 384 | 385 | 388 | 389 | 390 |
391 | ) 392 | } 393 | 394 | // export default Map; -------------------------------------------------------------------------------- /src/modules/components/pagination.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from "react"; 2 | import queryDecoder from "../../utils/queryDecoder"; 3 | import { Pagination } from '@material-ui/lab'; 4 | import '../../index.css'; 5 | import { Redirect } from "react-router-dom"; 6 | 7 | const PaginationElement = ({ location, totalPages }) => { 8 | const currentUrl = location.pathname; 9 | const decodedQuery = queryDecoder(location.search); 10 | const [currentPage, setCurrentPage] = useState(Number(decodedQuery.page) || 1); 11 | const [pageIsSelected, setPageIsSelected] = useState(false); 12 | 13 | const handlePaginationChange = (e, page) => { 14 | setPageIsSelected(true); 15 | setCurrentPage(page); 16 | } 17 | 18 | useEffect(() => { 19 | setPageIsSelected(false); 20 | }, [currentPage]) 21 | 22 | if (pageIsSelected) { 23 | return ( 24 | 25 | ) 26 | } 27 | 28 | return ( 29 | 39 | ) 40 | } 41 | 42 | export default PaginationElement; -------------------------------------------------------------------------------- /src/modules/components/resultsProgressPetition.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const resultsProgressPetition = ({ results }) => { 4 | if (!results.length) { 5 | return ( 6 |
11 | Немає жодної інформації про результати голосування 12 |
13 | ) 14 | } 15 | 16 | const votesNumber = results[0] && Number(results[0].votes); 17 | const totalVotes = 200; 18 | 19 | return ( 20 |
21 |
24 | 29 | {votesNumber} 30 | 31 |    32 | 33 | голос 34 | 35 |
36 |
42 |
52 |
53 |
54 |
55 | ) 56 | } 57 | 58 | export default resultsProgressPetition; -------------------------------------------------------------------------------- /src/modules/components/startEndDate.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import dateDecoder from "../../utils/dateDecoder"; 3 | 4 | const startEndDate = ({ start_date, end_date }) => { 5 | let startDateDecoded = dateDecoder(start_date); 6 | let endDateDecoded = dateDecoder(end_date); 7 | 8 | const { 9 | day: startDay, 10 | month: startMonth, 11 | year: startYear, 12 | hour: startHour, 13 | minute: startMinute 14 | } = startDateDecoded; 15 | const { 16 | day: endDay, 17 | month: endMonth, 18 | year: endYear, 19 | hour: endHour, 20 | minute: endMinute 21 | } = endDateDecoded; 22 | 23 | if ( 24 | startDay === endDay && 25 | startMonth === endMonth && 26 | startYear === endYear 27 | ) { 28 | return ( 29 | 30 | {startDay}.{startMonth}.{startYear} {startHour}:{startMinute} - {endHour}:{endMinute} 31 | 32 | ) 33 | } 34 | 35 | return ( 36 | 37 | {startDay}.{startMonth}.{startYear} {startHour}:{startMinute} -  38 | {endDay}.{endMonth}.{endYear} {endHour}:{endMinute} 39 | 40 | ) 41 | } 42 | 43 | export default startEndDate; -------------------------------------------------------------------------------- /src/modules/components/topBar.js: -------------------------------------------------------------------------------- 1 | import React, { useContext } from "react"; 2 | import { Fragment } from "react"; 3 | import { Link, NavLink } from "react-router-dom"; 4 | import { userContext } from '../contexts/user'; 5 | 6 | const TopBar = () => { 7 | const [userState] = useContext(userContext); 8 | 9 | const { isLoggedIn, user } = userState; 10 | 11 | return ( 12 | 66 | ) 67 | } 68 | 69 | export default TopBar; -------------------------------------------------------------------------------- /src/modules/components/userChecker.js: -------------------------------------------------------------------------------- 1 | import { useContext, useEffect } from "react"; 2 | import { userContext } from "../contexts/user"; 3 | import { useCookies } from "react-cookie"; 4 | import useFetch from "../hooks/useFetch"; 5 | 6 | const UserChecker = ({ children }) => { 7 | const [, dispatch] = useContext(userContext); 8 | const [cookie] = useCookies(); 9 | const [{ response }, doFetch] = useFetch(`/user/token`); 10 | 11 | useEffect(() => { 12 | const token = cookie.token; 13 | if (!token) { 14 | dispatch({ 15 | type: 'SET_UNAUTHORIZED' 16 | }) 17 | return; 18 | } 19 | doFetch({ 20 | queryFields: { 21 | token 22 | } 23 | }); 24 | dispatch({ 25 | type: 'SET_ISLOADING' 26 | }) 27 | }, [cookie, dispatch, doFetch]); 28 | 29 | useEffect(() => { 30 | if (!response) return; 31 | dispatch({ 32 | type: 'SET_AUTHORIZED', 33 | payload: { 34 | user: response 35 | } 36 | }); 37 | }, [response, dispatch]); 38 | 39 | return children; 40 | } 41 | 42 | export default UserChecker; -------------------------------------------------------------------------------- /src/modules/contexts/user.js: -------------------------------------------------------------------------------- 1 | import React, { createContext, useReducer } from 'react'; 2 | 3 | const reducer = (state, action) => { 4 | const actions = { 5 | 'SET_AUTHORIZED': () => ({ 6 | isLoading: false, 7 | isLoggedIn: true, 8 | user: action.payload.user 9 | }), 10 | 'SET_UNAUTHORIZED': () => ({ 11 | isLoading: false, 12 | isLoggedIn: false, 13 | user: null, 14 | }), 15 | 'SET_ISLOADING': () => ({ 16 | ...state, 17 | isLoading: true, 18 | }), 19 | } 20 | return actions[action.type]() || state; 21 | } 22 | 23 | export const userContext = createContext(); 24 | 25 | export const UserProvider = ({ children }) => { 26 | const initialState = { 27 | user: null, 28 | isLoggedIn: false, 29 | isLoading: false, 30 | } 31 | const value = useReducer(reducer, initialState); 32 | 33 | return ( 34 | 35 | {children} 36 | 37 | ) 38 | } -------------------------------------------------------------------------------- /src/modules/hooks/useFetch.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import { useCallback, useState, useEffect } from 'react'; 3 | import { useCookies } from "react-cookie"; 4 | import queryCoder from '../../utils/queryCoder'; 5 | 6 | const UseFetch = url => { 7 | const baseUrl = 'https://voting--system.herokuapp.com'; 8 | 9 | const [ response, setResponse ] = useState(''); 10 | const [ isLoading, setIsLoading ] = useState(false); 11 | const [ error, setError ] = useState(null); 12 | const [ options, setOptions ] = useState(null); 13 | const [cookie] = useCookies(['token']); 14 | const token = cookie.token; 15 | 16 | const doFetch = useCallback((options = {}) => { 17 | setOptions(options); 18 | setIsLoading(true); 19 | }, []) 20 | 21 | useEffect(() => { 22 | if (!isLoading) return; 23 | 24 | if (options.queryFields) 25 | options.queryFields.token = token; 26 | const fullUrl = baseUrl + url + queryCoder(options.queryFields); 27 | 28 | axios(fullUrl, options) 29 | .then(res => { 30 | setIsLoading(false); 31 | setResponse(res.data); 32 | setError(null); 33 | }) 34 | .catch(err => { 35 | setIsLoading(false); 36 | setError(err.response.data); 37 | }) 38 | }, [isLoading, options, error, response, token, url]); 39 | 40 | return [{response, error, isLoading}, doFetch]; 41 | } 42 | 43 | export default UseFetch; -------------------------------------------------------------------------------- /src/modules/pages/authentication/index.js: -------------------------------------------------------------------------------- 1 | import React, { Fragment, useEffect, useState } from "react"; 2 | import useFetch from '../../hooks/useFetch'; 3 | import { useCookies } from "react-cookie"; 4 | import ErrorMessage from '../../components/errorMessage'; 5 | import LoadingMessage from '../../components/loadingMessage'; 6 | import { Redirect } from "react-router-dom"; 7 | 8 | const Auth = ({ location }) => { 9 | const isLoginPage = location.pathname === '/login'; 10 | const signText = isLoginPage ? "Вхід" : "Реєстрація"; 11 | const url = isLoginPage ? '/user/login' : '/user/register'; 12 | 13 | const [, setCookie] = useCookies(['token']); 14 | const [{response, isLoading, error}, doFetch] = useFetch(url); 15 | 16 | const [name, setName] = useState(''); 17 | const [surname, setSurname] = useState(''); 18 | const [birthday_date, setBirthday_date] = useState(''); 19 | const [gender, setGender] = useState('male'); 20 | const [district_id, setDistrict_id] = useState('0'); 21 | const [email, setEmail] = useState(''); 22 | const [password, setPassword] = useState(''); 23 | const [successfulSubmit, setSuccessfulSubmit] = useState(false); 24 | 25 | const regions = [ 26 | 'Автономна Республіка Крим', 27 | 'Вінницька область', 28 | 'Волинська область', 29 | 'Дніпровська область', 30 | 'Донецька область', 31 | 'Житомирська область', 32 | 'Закарпатська область', 33 | 'Запорізька область', 34 | 'Івано-Франківська область', 35 | 'Київська область', 36 | 'Кіровоградська область', 37 | 'Луганська область', 38 | 'Львівська область', 39 | 'Миколаївська область', 40 | 'Одеська область', 41 | 'Полтавська область', 42 | 'Рівненська область', 43 | 'Сумська область', 44 | 'Тернопільська область', 45 | 'Харківська область', 46 | 'Херсонська область', 47 | 'Хмельницька область', 48 | 'Черкаська область', 49 | 'Чернівецька область', 50 | 'Чернігівська область', 51 | 'Місто Київ', 52 | 'Місто Севастополь' 53 | ]; 54 | 55 | useEffect(() => { 56 | if (!response) return; 57 | const token = response.token; 58 | setCookie('token', token); 59 | setSuccessfulSubmit(true); 60 | }, [response, setCookie, isLoginPage]); 61 | 62 | const handleSubmit = event => { 63 | event.preventDefault(); 64 | 65 | doFetch({ 66 | method: 'post', 67 | queryFields: { 68 | name, surname, birthday_date, gender, 69 | district_id, email, password, 70 | status: -1 71 | } 72 | }) 73 | } 74 | 75 | if (successfulSubmit) { 76 | return ( 77 | 78 | ) 79 | } 80 | 81 | return ( 82 |
83 |
84 |
85 |

86 | {signText} 87 |

88 | {isLoading && ( 89 | 90 | )} 91 | {error && ( 92 | 93 | )} 94 |
95 | {!isLoginPage && ( 96 | 97 |
98 | setSurname(e.target.value)} 103 | /> 104 |
105 |
106 | setName(e.target.value)} 111 | /> 112 |
113 |
114 | setBirthday_date(e.target.value)} 118 | /> 119 |
120 |
121 | 129 |
130 | 141 |
142 | )} 143 | setEmail(e.target.value)} 148 | /> 149 | setPassword(e.target.value)} 154 | /> 155 | 158 |
159 |
160 |
161 |
162 | ) 163 | } 164 | 165 | export default Auth; -------------------------------------------------------------------------------- /src/modules/pages/homePage/images/LabelVoting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tedi4t/voting-front/62f24596e61eb2784a8b97d7177c1bd035df511d/src/modules/pages/homePage/images/LabelVoting.png -------------------------------------------------------------------------------- /src/modules/pages/homePage/index.js: -------------------------------------------------------------------------------- 1 | import React, { useContext } from "react"; 2 | import logo from "./images/LabelVoting.png"; 3 | import { userContext } from "../../contexts/user"; 4 | 5 | const HomePage = () => { 6 | const [userState] = useContext(userContext); 7 | const isLoggedIn = userState.isLoggedIn; 8 | 9 | return ( 10 |
11 | 20 | 21 |
27 | { !isLoggedIn && ( 28 |

29 | Зареєструйтесь щоб продовжити 30 |

31 | ) 32 | } 33 |
34 |
35 | ) 36 | } 37 | 38 | export default HomePage; -------------------------------------------------------------------------------- /src/modules/pages/myProfile/images/userIcon.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tedi4t/voting-front/62f24596e61eb2784a8b97d7177c1bd035df511d/src/modules/pages/myProfile/images/userIcon.jpg -------------------------------------------------------------------------------- /src/modules/pages/myProfile/index.js: -------------------------------------------------------------------------------- 1 | import React, { useContext, useEffect, useState } from "react"; 2 | import { useCookies } from "react-cookie"; 3 | import { userContext } from "../../contexts/user"; 4 | import useFetch from "../../hooks/useFetch"; 5 | import UserIcon from "./images/userIcon.jpg"; 6 | import LoadingMessage from "../../components/loadingMessage"; 7 | import ErrorMessage from "../../components/errorMessage"; 8 | import dateStrinfify from "../../../utils/dateStringify"; 9 | import { Redirect } from "react-router-dom"; 10 | 11 | const MyProfile = () => { 12 | const [, , removeCookie] = useCookies(['token']); 13 | const [userState] = useContext(userContext); 14 | const [tokenRemoved, setTokenRemoved] = useState(false); 15 | const user = userState && userState.user; 16 | 17 | const [{response, isLoading, error}, doFetch] = useFetch('/districts'); 18 | 19 | useEffect(() => { 20 | doFetch(); 21 | }, [doFetch]); 22 | 23 | const handleLogoutClick = (e) => { 24 | e.preventDefault(); 25 | 26 | removeCookie('token'); 27 | setTokenRemoved(true); 28 | } 29 | 30 | if (tokenRemoved) { 31 | return ( 32 | 33 | ) 34 | } 35 | 36 | return ( 37 |
38 | { 39 | response && user && ( 40 |
41 |
42 | user icon 43 |
44 |
50 |
51 | Ім'я: { user.name } 52 |
53 |
54 | Прізвище: { user.surname } 55 |
56 |
57 | Дата народження: { dateStrinfify(user.birthday_date) } 58 |
59 |
60 | Стать: { user.gender } 61 |
62 |
63 | Email: { user.email } 64 |
65 |
66 | Округ: { response[user.district_id].name } 67 |
68 | 74 |
75 |
76 | ) 77 | } 78 | { 79 | isLoading && ( 80 | 81 | ) 82 | } 83 | { 84 | error && ( 85 | 86 | ) 87 | } 88 |
89 | ) 90 | } 91 | 92 | export default MyProfile; -------------------------------------------------------------------------------- /src/modules/pages/newPetition/index.js: -------------------------------------------------------------------------------- 1 | import React, { useContext, useState, useEffect } from "react"; 2 | import { userContext } from '../../contexts/user'; 3 | import useFetch from '../../hooks/useFetch'; 4 | import ErrorMessage from '../../components/errorMessage'; 5 | import LoadingMessage from '../../components/loadingMessage'; 6 | import { Redirect } from "react-router-dom"; 7 | 8 | const NewPetition = () => { 9 | const [name, setName] = useState(''); 10 | const [description, setDescription] = useState(''); 11 | const [{ response, isLoading, error}, doFetch] = useFetch('/petition/create'); 12 | const [userState] = useContext(userContext); 13 | const [successfulSubmit, setSuccessfulSubmit] = useState(false); 14 | const user_id = userState && userState.user && userState.user.user_id; 15 | 16 | const handleFormSubmit = e => { 17 | e.preventDefault(); 18 | doFetch({ 19 | method: 'post', 20 | queryFields: { 21 | author_user_id: user_id, 22 | name, 23 | description 24 | } 25 | }) 26 | } 27 | 28 | useEffect(() => { 29 | if (!response) return; 30 | setSuccessfulSubmit(true); 31 | }, [response]); 32 | 33 | if (successfulSubmit) { 34 | return ( 35 | 36 | ) 37 | } 38 | 39 | return ( 40 |
41 | {error && } 42 | {isLoading && } 43 | {!error && !isLoading && ( 44 |
45 |
46 | 52 | { 58 | setName(e.target.value); 59 | }} 60 | /> 61 |
62 |
63 | 69 |