From 4a444babd45197a447f546410a21103ebf115261 Mon Sep 17 00:00:00 2001 From: Felix Hamme Date: Wed, 29 May 2019 08:40:11 +0200 Subject: [PATCH 01/37] Bugfix: match scores weren't editable --- js/components/Match.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/js/components/Match.js b/js/components/Match.js index ebf91a7..55a4e9a 100644 --- a/js/components/Match.js +++ b/js/components/Match.js @@ -155,8 +155,8 @@ function MatchModal(props) { return ( {title} - {props.matchState === 'in_progress' ? : - } + {props.match.state === 'in_progress' ? : + } {actionButton} From 071ed703dbe0aeb59433e0f520d70ae825f44420 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=A4dler?= <32635600+Malaber@users.noreply.github.com> Date: Tue, 4 Jun 2019 17:39:23 +0200 Subject: [PATCH 02/37] Add quick install link --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 83eb16d..15292d3 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,9 @@ [![Codacy Badge](https://api.codacy.com/project/badge/Grade/300915a8466f4f059150b543e9a6d1b0)](https://app.codacy.com/app/JP1998/turniere-frontend?utm_source=github.com&utm_medium=referral&utm_content=turniere/turniere-frontend&utm_campaign=Badge_Grade_Dashboard) +## Quick install with Docker +[turnie.re - Quickstart](https://github.com/turniere/turniere-quickstart) + ## Development Setup ### Prerequisites From 5826a06ab6549981c6db1da4aac50ecc64dc85c5 Mon Sep 17 00:00:00 2001 From: Felix Hamme Date: Tue, 4 Jun 2019 13:27:28 +0200 Subject: [PATCH 03/37] Split up Match into more files --- js/components/EditableMatchTable.js | 54 ++++++++++ js/components/Match.js | 158 +--------------------------- js/components/MatchModal.js | 40 +++++++ js/components/MatchTable.js | 57 ++++++++++ 4 files changed, 154 insertions(+), 155 deletions(-) create mode 100644 js/components/EditableMatchTable.js create mode 100644 js/components/MatchModal.js create mode 100644 js/components/MatchTable.js diff --git a/js/components/EditableMatchTable.js b/js/components/EditableMatchTable.js new file mode 100644 index 0000000..c19f883 --- /dev/null +++ b/js/components/EditableMatchTable.js @@ -0,0 +1,54 @@ +import React from 'react'; +import {Button, Input, InputGroup, InputGroupAddon, Table} from 'reactstrap'; + +export function EditableMatchTable(props) { + return ( + + + + + + + + + + +
+ + {props.match.team1.name}
+ + {props.match.team2.name}
); +} + +class ScoreInput extends React.Component { + constructor(props) { + super(props); + this.state = {score: this.props.score}; + this.updateScore = this.updateScore.bind(this); + this.increaseScore = this.increaseScore.bind(this); + this.decreaseScore = this.decreaseScore.bind(this); + } + + updateScore(event) { + this.setState({score: event.target.value}); + } + + increaseScore() { + this.setState({score: Number(this.state.score) + 1}); + } + + decreaseScore() { + this.setState({score: Number(this.state.score) - 1}); + } + + render() { + return ( + + + + ); + } +} diff --git a/js/components/Match.js b/js/components/Match.js index 55a4e9a..f12481e 100644 --- a/js/components/Match.js +++ b/js/components/Match.js @@ -1,19 +1,9 @@ -import { - Button, - Card, - CardBody, - Input, - InputGroup, - InputGroupAddon, - Modal, - ModalBody, - ModalFooter, - ModalHeader, - Table -} from 'reactstrap'; +import {Card, CardBody} from 'reactstrap'; import React from 'react'; import {endMatch, startMatch} from '../api'; import {notify} from 'react-notify-toast'; +import {MatchModal} from './MatchModal'; +import {MatchTable} from './MatchTable'; export class Match extends React.Component { @@ -129,145 +119,3 @@ export class Match extends React.Component { } } -function MatchModal(props) { - let title; - let actionButton = ''; - // possible states: single_team not_ready not_started in_progress finished - switch (props.match.state) { - case 'in_progress': - title = 'Spiel läuft'; - actionButton = ; - break; - case 'finished': - title = 'Spiel beendet'; - break; - case 'single_team': - title = 'kein Gegner, Team kommt weiter'; - break; - case 'not_ready': - title = 'Spiel kann noch nicht gestartet werden'; - break; - case 'not_started': - title = 'Spiel kann gestartet werden'; - actionButton = ; - break; - } - return ( - {title} - - {props.match.state === 'in_progress' ? : - } - - - {actionButton} - - - ); -} - -function MatchTable(props) { - let team1Class; - let team2Class; - // possible states: single_team not_ready not_started in_progress finished - switch (props.matchState) { - case 'in_progress': - break; - case 'finished': - if (props.match.winnerTeamId === undefined) { - break; - } - if (props.winnerTeamId === props.match.team1.id) { - team1Class = 'font-weight-bold'; - team2Class = 'lost-team'; - } - if (props.winnerTeamId === props.match.team2.id) { - team1Class = 'lost-team'; - team2Class = 'font-weight-bold'; - } - break; - case 'single_team': - team2Class = 'text-muted'; - break; - case 'not_ready': - break; - case 'not_started': - break; - } - if (props.match.state === 'single_team') { - return ( - - - - - - - - -
{props.match.team1.name}
kein Gegner
); - } else { - return ( - - - - - - - - - - -
{props.match.team1.score}{props.match.team1.name}
{props.match.team2.score}{props.match.team2.name}
); - } -} - -function EditableMatchTable(props) { - return ( - - - - - - - - - - -
- - {props.match.team1.name}
- - {props.match.team2.name}
); -} - -class ScoreInput extends React.Component { - constructor(props) { - super(props); - this.state = {score: this.props.score}; - this.updateScore = this.updateScore.bind(this); - this.increaseScore = this.increaseScore.bind(this); - this.decreaseScore = this.decreaseScore.bind(this); - } - - updateScore(event) { - this.setState({score: event.target.value}); - } - - increaseScore() { - this.setState({score: Number(this.state.score) + 1}); - } - - decreaseScore() { - this.setState({score: Number(this.state.score) - 1}); - } - - render() { - return ( - - - - ); - } -} diff --git a/js/components/MatchModal.js b/js/components/MatchModal.js new file mode 100644 index 0000000..e2eef0c --- /dev/null +++ b/js/components/MatchModal.js @@ -0,0 +1,40 @@ +import {Button, Modal, ModalBody, ModalFooter, ModalHeader} from 'reactstrap'; +import React from 'react'; +import {EditableMatchTable} from './EditableMatchTable'; +import {MatchTable} from './MatchTable'; + +export function MatchModal(props) { + let title; + let actionButton = ''; + // possible states: single_team not_ready not_started in_progress finished + switch (props.match.state) { + case 'in_progress': + title = 'Spiel läuft'; + actionButton = ; + break; + case 'finished': + title = 'Spiel beendet'; + break; + case 'single_team': + title = 'kein Gegner, Team kommt weiter'; + break; + case 'not_ready': + title = 'Spiel kann noch nicht gestartet werden'; + break; + case 'not_started': + title = 'Spiel kann gestartet werden'; + actionButton = ; + break; + } + return ( + {title} + + {props.match.state === 'in_progress' ? : + } + + + {actionButton} + + + ); +} diff --git a/js/components/MatchTable.js b/js/components/MatchTable.js new file mode 100644 index 0000000..fda69d9 --- /dev/null +++ b/js/components/MatchTable.js @@ -0,0 +1,57 @@ +import {Table} from 'reactstrap'; +import React from 'react'; + +export function MatchTable(props) { + let team1Class; + let team2Class; + // possible states: single_team not_ready not_started in_progress finished + switch (props.matchState) { + case 'in_progress': + break; + case 'finished': + if (props.match.winnerTeamId === undefined) { + break; + } + if (props.winnerTeamId === props.match.team1.id) { + team1Class = 'font-weight-bold'; + team2Class = 'lost-team'; + } + if (props.winnerTeamId === props.match.team2.id) { + team1Class = 'lost-team'; + team2Class = 'font-weight-bold'; + } + break; + case 'single_team': + team2Class = 'text-muted'; + break; + case 'not_ready': + break; + case 'not_started': + break; + } + if (props.match.state === 'single_team') { + return ( + + + + + + + + +
{props.match.team1.name}
kein Gegner
); + } else { + return ( + + + + + + + + + + +
{props.match.team1.score}{props.match.team1.name}
{props.match.team2.score}{props.match.team2.name}
); + } +} From 78e511f228ca9d40adc9f9205f6c3d80587ad526 Mon Sep 17 00:00:00 2001 From: Felix Hamme Date: Thu, 6 Jun 2019 10:58:02 +0200 Subject: [PATCH 04/37] Disable end-match-button if the scores are equal and the match is in playoff stage --- js/components/MatchModal.js | 6 +++++- pages/tournament.js | 9 +++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/js/components/MatchModal.js b/js/components/MatchModal.js index e2eef0c..9a84a30 100644 --- a/js/components/MatchModal.js +++ b/js/components/MatchModal.js @@ -10,7 +10,11 @@ export function MatchModal(props) { switch (props.match.state) { case 'in_progress': title = 'Spiel läuft'; - actionButton = ; + if (!props.match.allowUndecided && props.match.team1.score === props.match.team2.score) { + actionButton = ; + } else { + actionButton = ; + } break; case 'finished': title = 'Spiel beendet'; diff --git a/pages/tournament.js b/pages/tournament.js index a7bc42e..575e47f 100644 --- a/pages/tournament.js +++ b/pages/tournament.js @@ -94,7 +94,7 @@ function convertTournament(apiTournament) { } else { // playoff stage playoffStages.push({ - id: stage.id, level: stage.level, matches: stage.matches.map(match => convertMatch(match)) + id: stage.id, level: stage.level, matches: stage.matches.map(match => convertMatch(match, false)) }); } } @@ -115,13 +115,14 @@ function convertGroup(apiGroup) { id: apiGroup.id, number: apiGroup.number, scores: apiGroup.group_scores, - matches: apiGroup.matches.map(match => convertMatch(match)) + matches: apiGroup.matches.map(match => convertMatch(match, true)) }; } -function convertMatch(apiMatch) { +function convertMatch(apiMatch, allowUndecided) { const result = { - id: apiMatch.id, state: apiMatch.state, winnerTeamId: apiMatch.winner === null ? null : apiMatch.winner.id + id: apiMatch.id, state: apiMatch.state, allowUndecided: allowUndecided, + winnerTeamId: apiMatch.winner === null ? null : apiMatch.winner.id }; if (apiMatch.match_scores.length === 2) { From 85a36a02c9eab05302525c906d6c888a13f8255c Mon Sep 17 00:00:00 2001 From: Felix Hamme Date: Thu, 6 Jun 2019 11:04:25 +0200 Subject: [PATCH 05/37] Add change score button (no functionality yet) --- js/components/MatchModal.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/js/components/MatchModal.js b/js/components/MatchModal.js index 9a84a30..9fd14a3 100644 --- a/js/components/MatchModal.js +++ b/js/components/MatchModal.js @@ -6,15 +6,19 @@ import {MatchTable} from './MatchTable'; export function MatchModal(props) { let title; let actionButton = ''; + let submitScoresButton = ''; + let matchTable = ; // possible states: single_team not_ready not_started in_progress finished switch (props.match.state) { case 'in_progress': title = 'Spiel läuft'; + submitScoresButton = ; if (!props.match.allowUndecided && props.match.team1.score === props.match.team2.score) { actionButton = ; } else { actionButton = ; } + matchTable = ; break; case 'finished': title = 'Spiel beendet'; @@ -33,10 +37,10 @@ export function MatchModal(props) { return ( {title} - {props.match.state === 'in_progress' ? : - } + {matchTable} + {submitScoresButton} {actionButton} From 4e8a690d2af2ca8038c88dcc7fb0db3010ac1104 Mon Sep 17 00:00:00 2001 From: Felix Hamme Date: Thu, 6 Jun 2019 11:41:35 +0200 Subject: [PATCH 06/37] Make calls to api for changing match scores --- js/api.js | 40 +++++++++ js/components/EditableMatchTable.js | 22 +++-- js/components/MatchModal.js | 123 ++++++++++++++++++---------- js/redux/tournamentInfo.js | 1 + pages/tournament.js | 9 +- 5 files changed, 142 insertions(+), 53 deletions(-) diff --git a/js/api.js b/js/api.js index d90ea5d..281ee0d 100644 --- a/js/api.js +++ b/js/api.js @@ -235,6 +235,31 @@ const reducerTournamentinfo = (state = defaultStateTournamentinfo, action) => { action.parameters.errorCallback(); }); return Object.assign({}, state, {}); + case actionTypesTournamentinfo.SUBMIT_MATCH_SCORES: + patchRequest(action.state, '/match_scores/' + action.parameters.scoreIdTeam1, { + points: action.parameters.scoreTeam1 + }).then(resp => { + storeOptionalToken(resp); + + patchRequest(action.state, '/match_scores/' + action.parameters.scoreIdTeam2, { + points: action.parameters.scoreTeam2 + }).then(resp => { + storeOptionalToken(resp); + + action.parameters.successCallback(); + }).catch(error => { + if (error.response) { + storeOptionalToken(error.response); + } + action.parameters.errorCallback(); + }); + }).catch(error => { + if (error.response) { + storeOptionalToken(error.response); + } + action.parameters.errorCallback(); + }); + return Object.assign({}, state, {}); case actionTypesTournamentinfo.END_MATCH: patchRequest(action.state, '/matches/' + action.parameters.matchId, { state: 'finished' @@ -414,6 +439,21 @@ export function endMatch(matchId, successCallback, errorCallback) { }); } +export function submitMatchScores(scoreTeam1, scoreIdTeam1, scoreTeam2, scoreIdTeam2, successCallback, errorCallback) { + __store.dispatch({ + type: actionTypesTournamentinfo.SUBMIT_MATCH_SCORES, + parameters: { + scoreTeam1: scoreTeam1, + scoreIdTeam1: scoreIdTeam1, + scoreTeam2: scoreTeam2, + scoreIdTeam2: scoreIdTeam2, + successCallback: successCallback, + errorCallback: errorCallback + }, + state: __store.getState() + }); +} + export function getState() { return __store.getState(); } diff --git a/js/components/EditableMatchTable.js b/js/components/EditableMatchTable.js index c19f883..2589354 100644 --- a/js/components/EditableMatchTable.js +++ b/js/components/EditableMatchTable.js @@ -6,13 +6,13 @@ export function EditableMatchTable(props) { - + {props.match.team1.name} - + {props.match.team2.name} @@ -24,28 +24,34 @@ class ScoreInput extends React.Component { constructor(props) { super(props); this.state = {score: this.props.score}; - this.updateScore = this.updateScore.bind(this); + this.inputScore = this.inputScore.bind(this); this.increaseScore = this.increaseScore.bind(this); this.decreaseScore = this.decreaseScore.bind(this); } - updateScore(event) { - this.setState({score: event.target.value}); + inputScore(event) { + const newScore = event.target.value; + this.setState({score: newScore}); + this.props.update(newScore); } increaseScore() { - this.setState({score: Number(this.state.score) + 1}); + const newScore = Number(this.state.score) + 1; + this.setState({score: newScore}); + this.props.update(newScore); } decreaseScore() { - this.setState({score: Number(this.state.score) - 1}); + const newScore = Number(this.state.score) - 1; + this.setState({score: newScore}); + this.props.update(newScore); } render() { return ( - diff --git a/js/components/MatchModal.js b/js/components/MatchModal.js index 9fd14a3..fd5b81f 100644 --- a/js/components/MatchModal.js +++ b/js/components/MatchModal.js @@ -1,48 +1,87 @@ import {Button, Modal, ModalBody, ModalFooter, ModalHeader} from 'reactstrap'; -import React from 'react'; +import React, {Component} from 'react'; import {EditableMatchTable} from './EditableMatchTable'; import {MatchTable} from './MatchTable'; +import {submitMatchScores} from '../api'; +import {notify} from 'react-notify-toast'; -export function MatchModal(props) { - let title; - let actionButton = ''; - let submitScoresButton = ''; - let matchTable = ; - // possible states: single_team not_ready not_started in_progress finished - switch (props.match.state) { - case 'in_progress': - title = 'Spiel läuft'; - submitScoresButton = ; - if (!props.match.allowUndecided && props.match.team1.score === props.match.team2.score) { - actionButton = ; - } else { - actionButton = ; - } - matchTable = ; - break; - case 'finished': - title = 'Spiel beendet'; - break; - case 'single_team': - title = 'kein Gegner, Team kommt weiter'; - break; - case 'not_ready': - title = 'Spiel kann noch nicht gestartet werden'; - break; - case 'not_started': - title = 'Spiel kann gestartet werden'; - actionButton = ; - break; +export class MatchModal extends Component { + constructor(props) { + super(props); + this.state = {scoreTeam1: this.props.match.team1.score, scoreTeam2: this.props.match.team2.score}; + this.updateScoreTeam1 = this.updateScoreTeam1.bind(this); + this.updateScoreTeam2 = this.updateScoreTeam2.bind(this); + this.submitScores = this.submitScores.bind(this); + this.onSubmitScoresError = this.onSubmitScoresError.bind(this); + this.onSubmitScoresSuccess = this.onSubmitScoresSuccess.bind(this); + } + + updateScoreTeam1(newScore) { + this.setState({scoreTeam1: newScore}); + } + + updateScoreTeam2(newScore) { + this.setState({scoreTeam2: newScore}); + } + + submitScores() { + const match = this.props.match; + submitMatchScores(this.state.scoreTeam1, match.team1.scoreId, this.state.scoreTeam2, match.team2.scoreId, + this.onSubmitScoresSuccess, this.onSubmitScoresError); + } + + onSubmitScoresError() { + this.props.toggle(); + notify.show('Der Spielstand konnte nicht geändert werden.', 'error', 2500); + } + + onSubmitScoresSuccess() { + this.props.toggle(); + notify.show('Der Spielstand wurde geändert.', 'success', 2000); + } + + render() { + let title; + let actionButton = ''; + let submitScoresButton = ''; + let matchTable = ; + // possible states: single_team not_ready not_started in_progress finished + switch (this.props.match.state) { + case 'in_progress': + title = 'Spiel läuft'; + submitScoresButton = ; + if (!this.props.match.allowUndecided && this.props.match.team1.score === this.props.match.team2.score) { + actionButton = ; + } else { + actionButton = ; + } + matchTable = ; + break; + case 'finished': + title = 'Spiel beendet'; + break; + case 'single_team': + title = 'kein Gegner, Team kommt weiter'; + break; + case 'not_ready': + title = 'Spiel kann noch nicht gestartet werden'; + break; + case 'not_started': + title = 'Spiel kann gestartet werden'; + actionButton = ; + break; + } + return ( + {title} + + {matchTable} + + + {submitScoresButton} + {actionButton} + + + ); } - return ( - {title} - - {matchTable} - - - {submitScoresButton} - {actionButton} - - - ); } diff --git a/js/redux/tournamentInfo.js b/js/redux/tournamentInfo.js index f5aedbe..b93f325 100644 --- a/js/redux/tournamentInfo.js +++ b/js/redux/tournamentInfo.js @@ -9,6 +9,7 @@ export const actionTypesTournamentinfo = { 'MODIFY_TOURNAMENT_ERROR': 'MODIFY_TOURNAMENT_ERROR', 'START_MATCH': 'START_MATCH', + 'SUBMIT_MATCH_SCORES': 'SUBMIT_MATCH_SCORES', 'END_MATCH': 'END_MATCH', 'REHYDRATE': 'TOURNAMENTINFO_REHYDRATE', diff --git a/pages/tournament.js b/pages/tournament.js index 575e47f..bd0de1b 100644 --- a/pages/tournament.js +++ b/pages/tournament.js @@ -129,18 +129,21 @@ function convertMatch(apiMatch, allowUndecided) { result.team1 = { name: apiMatch.match_scores[0].team.name, id: apiMatch.match_scores[0].team.id, - score: apiMatch.match_scores[0].points + score: apiMatch.match_scores[0].points, + scoreId: apiMatch.match_scores[0].id }; result.team2 = { name: apiMatch.match_scores[1].team.name, id: apiMatch.match_scores[1].team.id, - score: apiMatch.match_scores[1].points + score: apiMatch.match_scores[1].points, + scoreId: apiMatch.match_scores[1].id }; } else if (apiMatch.match_scores.length === 1) { result.team1 = { name: apiMatch.match_scores[0].team.name, id: apiMatch.match_scores[0].team.id, - score: apiMatch.match_scores[0].points + score: apiMatch.match_scores[0].points, + scoreId: apiMatch.match_scores[0].id }; result.team2 = { name: 'TBD', From ba38864a1c87a5cc6356f4cbdc43ae9554eb94a3 Mon Sep 17 00:00:00 2001 From: Felix Hamme Date: Thu, 6 Jun 2019 11:54:10 +0200 Subject: [PATCH 07/37] Update the Match when the scores are submitted --- js/components/Match.js | 10 +++++++++- js/components/MatchModal.js | 1 + 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/js/components/Match.js b/js/components/Match.js index f12481e..95293d8 100644 --- a/js/components/Match.js +++ b/js/components/Match.js @@ -21,6 +21,7 @@ export class Match extends React.Component { this.onEndMatchSuccess = this.onEndMatchSuccess.bind(this); this.onEndMatchError = this.onEndMatchError.bind(this); this.getMatchFinishedMessage = this.getMatchFinishedMessage.bind(this); + this.changeScores = this.changeScores.bind(this); } toggleModal() { @@ -64,6 +65,13 @@ export class Match extends React.Component { notify.show('Das Match konnte nicht beendet werden.', 'error', 3000); } + changeScores(scoreTeam1, scoreTeam2) { + const updatedMatch = this.state.match; + updatedMatch.team1.score = scoreTeam1; + updatedMatch.team2.score = scoreTeam2; + this.setState({match: updatedMatch}); + } + getMatchFinishedMessage() { const match = this.state.match; if (match.winnerTeamId === null) { @@ -114,7 +122,7 @@ export class Match extends React.Component { {smallMessage} + startMatch={this.startMatch} endMatch={this.endMatch} changeScores={this.changeScores}/> ); } } diff --git a/js/components/MatchModal.js b/js/components/MatchModal.js index fd5b81f..824a8fe 100644 --- a/js/components/MatchModal.js +++ b/js/components/MatchModal.js @@ -37,6 +37,7 @@ export class MatchModal extends Component { onSubmitScoresSuccess() { this.props.toggle(); + this.props.changeScores(this.state.scoreTeam1, this.state.scoreTeam2); notify.show('Der Spielstand wurde geändert.', 'success', 2000); } From 9ff387cced544b4dc172a604484eab2b8bea56fd Mon Sep 17 00:00:00 2001 From: Felix Hamme Date: Thu, 6 Jun 2019 14:39:39 +0200 Subject: [PATCH 08/37] Create a profile page that displays the username and e-mail --- pages/profile.js | 54 ++++++++++++++++++++++++++++++++++++++++++ static/css/profile.css | 3 +++ 2 files changed, 57 insertions(+) create mode 100644 pages/profile.js create mode 100644 static/css/profile.css diff --git a/pages/profile.js b/pages/profile.js new file mode 100644 index 0000000..ac6b6ff --- /dev/null +++ b/pages/profile.js @@ -0,0 +1,54 @@ +import Head from 'next/head'; +import React from 'react'; +import {Container, Table} from 'reactstrap'; + +import {TurniereNavigation} from '../js/components/Navigation'; +import {BigImage} from '../js/components/BigImage'; +import {Footer} from '../js/components/Footer'; + +import 'bootstrap/dist/css/bootstrap.min.css'; + +import '../static/css/everypage.css'; +import '../static/css/profile.css'; +import {connect} from 'react-redux'; + +function Main() { + return (
+ + + +
); +} + +export default class ProfilePage extends React.Component { + render() { + return (
+ + Profil: turnie.re + + + +
+
+
); + } +} + +const UserData = connect(state => { + return {email: state.userinfo.uid, name: state.userinfo.username}; +})(UserDataTable); + +function UserDataTable(props) { + return ( + + + + + + + + + + +
Name{props.name}
E-Mail-Adresse{props.email}
); +} diff --git a/static/css/profile.css b/static/css/profile.css new file mode 100644 index 0000000..396dd13 --- /dev/null +++ b/static/css/profile.css @@ -0,0 +1,3 @@ +.w-small { + min-width: 10em; +} From f2af9fac128a6388d7d0e958b6af6d08547a81e2 Mon Sep 17 00:00:00 2001 From: Felix Hamme Date: Thu, 6 Jun 2019 15:46:32 +0200 Subject: [PATCH 09/37] Add mail address change form with corresponding backend calls --- js/api.js | 24 ++++++++++++++- js/redux/backendApi.js | 6 ++++ js/redux/userInfo.js | 2 ++ pages/profile.js | 69 ++++++++++++++++++++++++++++++++++-------- 4 files changed, 88 insertions(+), 13 deletions(-) diff --git a/js/api.js b/js/api.js index d90ea5d..934cc55 100644 --- a/js/api.js +++ b/js/api.js @@ -7,7 +7,7 @@ import {errorMessages} from './constants'; import {actionTypesUserinfo, defaultStateUserinfo} from './redux/userInfo'; import {actionTypesTournamentinfo, defaultStateTournamentinfo} from './redux/tournamentInfo'; import {actionTypesTournamentlist, defaultStateTournamentlist} from './redux/tournamentList'; -import {deleteRequest, getRequest, patchRequest, postRequest} from './redux/backendApi'; +import {deleteRequest, getRequest, patchRequest, postRequest, putRequest} from './redux/backendApi'; function storeOptionalToken(response) { @@ -149,6 +149,16 @@ const reducerUserinfo = (state = defaultStateUserinfo, action) => { __store.dispatch({type: actionTypesUserinfo.CLEAR}); }); return Object.assign({}, state, {}); + case actionTypesUserinfo.CHANGE_MAIL: + putRequest(action.state, '/users', { + email: action.parameters.newMail + }).then(resp => { + storeOptionalToken(resp); + action.parameters.successCallback(); + }).catch(() => { + action.parameters.errorCallback(); + }); + return Object.assign({}, state, {}); case actionTypesUserinfo.REHYDRATE: return Object.assign({}, state, action.parameters, {error: false, errorMessages: []}); case actionTypesUserinfo.CLEAR: @@ -353,6 +363,18 @@ export function logout(successCallback) { }); } +export function changeMail(newMail, successCallback, errorCallback) { + __store.dispatch({ + type: actionTypesUserinfo.CHANGE_MAIL, + parameters: { + newMail: newMail, + successCallback: successCallback, + errorCallback: errorCallback + }, + state: __store.getState() + }); +} + export function createTournament(data, successCallback, errorCallback) { __store.dispatch({ type: actionTypesTournamentinfo.CREATE_TOURNAMENT, diff --git a/js/redux/backendApi.js b/js/redux/backendApi.js index 2653edd..f052388 100644 --- a/js/redux/backendApi.js +++ b/js/redux/backendApi.js @@ -29,6 +29,12 @@ export function patchRequest(state, url, data) { }); } +export function putRequest(state, url, data) { + return axios.put(apiUrl + url, data, { + headers: generateHeaders(state) + }); +} + function generateHeaders(state) { if (state.userinfo.isSignedIn) { return { diff --git a/js/redux/userInfo.js b/js/redux/userInfo.js index a8172b4..d3c87ed 100644 --- a/js/redux/userInfo.js +++ b/js/redux/userInfo.js @@ -13,6 +13,8 @@ export const actionTypesUserinfo = { 'VERIFY_CREDENTIALS_SUCCESS': 'VERIFY_CREDENTIALS_SUCCESS', 'VERIFY_CREDENTIALS_ERROR': 'VERIFY_CREDENTIALS_ERROR', + 'CHANGE_MAIL': 'CHANGE_MAIL', + 'STORE_AUTH_HEADERS': 'STORE_AUTH_HEADERS', 'REHYDRATE': 'USERINFO_REHYDRATE', diff --git a/pages/profile.js b/pages/profile.js index ac6b6ff..1c288f0 100644 --- a/pages/profile.js +++ b/pages/profile.js @@ -1,6 +1,6 @@ import Head from 'next/head'; -import React from 'react'; -import {Container, Table} from 'reactstrap'; +import React, {Component} from 'react'; +import {Button, Container, Form, Input, InputGroup, InputGroupAddon, Table} from 'reactstrap'; import {TurniereNavigation} from '../js/components/Navigation'; import {BigImage} from '../js/components/BigImage'; @@ -11,13 +11,15 @@ import 'bootstrap/dist/css/bootstrap.min.css'; import '../static/css/everypage.css'; import '../static/css/profile.css'; import {connect} from 'react-redux'; +import {changeMail} from '../js/api'; +import {notify} from 'react-notify-toast'; -function Main() { - return (
- - - -
); +function ContentContainer(props) { + return ( + +

E-Mail-Adresse ändern

+ +
); } export default class ProfilePage extends React.Component { @@ -28,17 +30,19 @@ export default class ProfilePage extends React.Component { -
+
+ +