Merge pull request #2 from turniere/ticket/TURNIERE-111
Ticket/turniere 111
This commit is contained in:
commit
d46062f453
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es6": true,
|
||||
"node": true
|
||||
},
|
||||
"plugins": [
|
||||
"react"
|
||||
],
|
||||
"extends": "eslint:recommended",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2018,
|
||||
"sourceType": "module",
|
||||
"ecmaFeatures": {
|
||||
"jsx": true
|
||||
}
|
||||
},
|
||||
"rules": {
|
||||
"react/jsx-uses-react": "error",
|
||||
"react/jsx-uses-vars": "error",
|
||||
"indent": [
|
||||
"error",
|
||||
4
|
||||
],
|
||||
"linebreak-style": [
|
||||
"error",
|
||||
"unix"
|
||||
],
|
||||
"quotes": [
|
||||
"error",
|
||||
"single"
|
||||
],
|
||||
"semi": [
|
||||
"error",
|
||||
"always"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
fail_on_violations: true
|
||||
|
||||
jshint:
|
||||
enabled: false
|
||||
|
||||
eslint:
|
||||
enabled: true
|
||||
config_file: .eslintrc.json
|
||||
|
|
@ -10,7 +10,12 @@ import {
|
|||
NavItem,
|
||||
NavLink
|
||||
} from 'reactstrap';
|
||||
import React from "react";
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { logout } from './api';
|
||||
|
||||
export function BigImage(props) {
|
||||
return (
|
||||
|
|
@ -26,6 +31,7 @@ export class TurniereNavigation extends React.Component {
|
|||
super(props);
|
||||
|
||||
this.toggle = this.toggle.bind(this);
|
||||
|
||||
this.state = {
|
||||
collapsed: true
|
||||
};
|
||||
|
|
@ -68,15 +74,39 @@ function Betabadge() {
|
|||
return (<Badge color="danger" className="mr-2">BETA</Badge>);
|
||||
}
|
||||
|
||||
function LoginLogoutButtons() {
|
||||
class InvisibleLoginLogoutButtons extends React.Component {
|
||||
|
||||
render() {
|
||||
const { isSignedIn, username } = this.props;
|
||||
|
||||
if(isSignedIn) {
|
||||
return (
|
||||
<ButtonGroup className="nav-item">
|
||||
<Button href="/profile" className="btn navbar-btn btn-outline-success my-2 my-sm-0 px-5">{ username }</Button>
|
||||
<Button onClick={logout.bind(this)} className="btn navbar-btn btn-outline-success my-2 my-sm-0 px-5">Logout</Button>
|
||||
</ButtonGroup>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<ButtonGroup className="nav-item">
|
||||
<Button href="/login" className="btn navbar-btn btn-outline-success my-2 my-sm-0 px-5">Login</Button>
|
||||
<Button href="/register" className="btn navbar-btn btn-outline-success my-2 my-sm-0 px-5">Registrieren</Button>
|
||||
</ButtonGroup>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const mapStateToLoginLogoutButtonProperties = (state) => {
|
||||
const { isSignedIn, username } = state.userinfo;
|
||||
return { isSignedIn, username };
|
||||
};
|
||||
|
||||
const LoginLogoutButtons = connect(
|
||||
mapStateToLoginLogoutButtonProperties
|
||||
)(InvisibleLoginLogoutButtons);
|
||||
|
||||
export function Footer() {
|
||||
return (
|
||||
<footer className="footer mt-5 bg-dark text-light">
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import React from "react";
|
||||
import {Alert, Button, Input, InputGroup, InputGroupAddon} from "reactstrap";
|
||||
import React from 'react';
|
||||
import { Alert, Button, Input, InputGroup, InputGroupAddon } from 'reactstrap';
|
||||
|
||||
export default class EditableStringList extends React.Component {
|
||||
constructor(props) {
|
||||
|
|
@ -31,17 +31,14 @@ export default class EditableStringList extends React.Component {
|
|||
if ((typeof this.state.entries !== 'undefined') && this.state.entries.length > 0) {
|
||||
return (
|
||||
<div className="bg-light p-3 text-secondary font-italic">
|
||||
<StringInput submit={this.add} placeholder={this.props.inputPlaceholder}
|
||||
addButtonText={this.props.addButtonText}/>
|
||||
{this.state.entries.map((text) => <Item text={text} key={text}
|
||||
removeItem={this.remove}/>)}
|
||||
<StringInput submit={this.add} placeholder={this.props.inputPlaceholder} addButtonText={this.props.addButtonText}/>
|
||||
{this.state.entries.map((text) => <Item text={text} key={text} removeItem={this.remove}/>)}
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div className="bg-light p-3 text-secondary text-center font-italic">
|
||||
<StringInput submit={this.add} placeholder={this.props.inputPlaceholder}
|
||||
addButtonText={this.props.addButtonText}/>
|
||||
<StringInput submit={this.add} placeholder={this.props.inputPlaceholder} addButtonText={this.props.addButtonText}/>
|
||||
{this.props.placeholder}
|
||||
</div>
|
||||
);
|
||||
|
|
@ -64,8 +61,7 @@ class StringInput extends React.Component {
|
|||
render() {
|
||||
return (
|
||||
<InputGroup className="mb-3">
|
||||
<Input placeholder={this.props.placeholder} type="text" size="255" value={this.state.value} required
|
||||
onChange={this.handleChange} onKeyPress={(e) => {
|
||||
<Input placeholder={this.props.placeholder} type="text" size="255" value={this.state.value} required onChange={this.handleChange} onKeyPress={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
this.submit();
|
||||
return false;
|
||||
|
|
@ -73,10 +69,10 @@ class StringInput extends React.Component {
|
|||
}}/>
|
||||
<InputGroupAddon addonType="append">
|
||||
<Button color="success" outline={true}
|
||||
onClick={(e) => this.submit()}>{this.props.addButtonText}</Button>
|
||||
onClick={() => this.submit()}>{this.props.addButtonText}</Button>
|
||||
</InputGroupAddon>
|
||||
</InputGroup>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
submit() {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,301 @@
|
|||
import { createStore, applyMiddleware, combineReducers } from 'redux';
|
||||
import { composeWithDevTools } from 'redux-devtools-extension';
|
||||
import thunkMiddleware from 'redux-thunk';
|
||||
import { errorMessages } from './constants';
|
||||
|
||||
const axios = require('axios');
|
||||
|
||||
const api_url = 'https://api.turnie.re';
|
||||
|
||||
const actiontypes_userinfo = {
|
||||
'REGISTER' : 'REGISTER',
|
||||
'REGISTER_RESULT_SUCCESS' : 'REGISTER_RESULT_SUCCESS',
|
||||
'REGISTER_RESULT_ERROR' : 'REGISTER_RESULT_ERROR',
|
||||
|
||||
'LOGIN' : 'LOGIN',
|
||||
'LOGIN_RESULT_SUCCESS' : 'LOGIN_RESULT_SUCCESS',
|
||||
'LOGIN_RESULT_ERROR' : 'LOGIN_RESULT_ERROR',
|
||||
|
||||
'LOGOUT' : 'LOGOUT',
|
||||
|
||||
'VERIFY_CREDENTIALS' : 'VERIFY_CREDENTIALS',
|
||||
'VERIFY_CREDENTIALS_SUCCESS' : 'VERIFY_CREDENTIALS_SUCCESS',
|
||||
'VERIFY_CREDENTIALS_ERROR' : 'VERIFY_CREDENTIALS_ERROR',
|
||||
|
||||
'STORE_AUTH_HEADERS' : 'STORE_AUTH_HEADERS',
|
||||
|
||||
'REHYDRATE' : 'USERINFO_REHYDRATE',
|
||||
'CLEAR' : 'USERINFO_CLEAR',
|
||||
};
|
||||
|
||||
const defaultstate_userinfo = {
|
||||
isSignedIn : false,
|
||||
username : null,
|
||||
error : false,
|
||||
errorMessages : [],
|
||||
|
||||
accesstoken : null,
|
||||
client : null,
|
||||
expiry : null,
|
||||
uid : null
|
||||
};
|
||||
|
||||
export function postRequest(state, url, data) {
|
||||
return axios.post(api_url + url, data, {
|
||||
headers : generateHeaders(state)
|
||||
});
|
||||
}
|
||||
|
||||
export function getRequest(state, url) {
|
||||
return axios.get(api_url + url, {
|
||||
headers : generateHeaders(state)
|
||||
});
|
||||
}
|
||||
|
||||
export function deleteRequest(state, url) {
|
||||
return axios.delete(api_url + url, {
|
||||
headers : generateHeaders(state)
|
||||
});
|
||||
}
|
||||
|
||||
function generateHeaders(state) {
|
||||
if(state.isSignedIn) {
|
||||
return {
|
||||
'access-token' : state.accesstoken,
|
||||
'client' : state.client,
|
||||
'uid' : state.uid
|
||||
};
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
function storeOptionalToken(response) {
|
||||
if(checkForAuthenticationHeaders(response)) {
|
||||
__store.dispatch({
|
||||
type : actiontypes_userinfo.STORE_AUTH_HEADERS,
|
||||
parameters : {
|
||||
accesstoken : response.headers['access-token'],
|
||||
client : response.headers['client'],
|
||||
expiry : response.headers['expiry'],
|
||||
uid : response.headers['uid']
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function checkForAuthenticationHeaders(response) {
|
||||
if(response.headers) {
|
||||
const requiredHeaders = [
|
||||
'access-token', 'client', 'uid', 'expiry'
|
||||
];
|
||||
for(var i = 0; i < requiredHeaders.length; i++) {
|
||||
if(!response.headers[requiredHeaders[i]]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const reducer_userinfo = (state = defaultstate_userinfo, action) => {
|
||||
switch(action.type) {
|
||||
case actiontypes_userinfo.REGISTER:
|
||||
postRequest(state, '/users', {
|
||||
'username' : action.parameters.username,
|
||||
'email' : action.parameters.email,
|
||||
'password' : action.parameters.password
|
||||
}).then((resp) => {
|
||||
__store.dispatch({
|
||||
type : actiontypes_userinfo.REGISTER_RESULT_SUCCESS
|
||||
});
|
||||
storeOptionalToken(resp);
|
||||
}).catch((error) => {
|
||||
if (error.response) {
|
||||
__store.dispatch({
|
||||
'type' : actiontypes_userinfo.REGISTER_RESULT_ERROR,
|
||||
'parameters' : {
|
||||
'errorMessages' : error.response.data.errors.full_messages
|
||||
}
|
||||
});
|
||||
storeOptionalToken(error.response);
|
||||
} else {
|
||||
__store.dispatch({
|
||||
'type' : actiontypes_userinfo.REGISTER_RESULT_ERROR,
|
||||
'parameters' : {
|
||||
'errorMessages' : [
|
||||
errorMessages['registration_errorunknown']['en']
|
||||
]
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
return Object.assign({}, state, {});
|
||||
case actiontypes_userinfo.REGISTER_RESULT_SUCCESS:
|
||||
return Object.assign({}, state, {
|
||||
error : false,
|
||||
errorMessages : []
|
||||
});
|
||||
case actiontypes_userinfo.REGISTER_RESULT_ERROR:
|
||||
return Object.assign({}, state, {
|
||||
error : true,
|
||||
errorMessages : action.parameters.errorMessages
|
||||
});
|
||||
case actiontypes_userinfo.LOGIN:
|
||||
postRequest(state, '/users/sign_in', {
|
||||
email : action.parameters.email,
|
||||
password : action.parameters.password
|
||||
}).then((resp) => {
|
||||
__store.dispatch({
|
||||
type : actiontypes_userinfo.LOGIN_RESULT_SUCCESS,
|
||||
parameters : {
|
||||
username : resp.data.data.username,
|
||||
}
|
||||
});
|
||||
storeOptionalToken(resp);
|
||||
}).catch((error) => {
|
||||
if(error.response) {
|
||||
__store.dispatch({
|
||||
'type' : actiontypes_userinfo.LOGIN_RESULT_ERROR,
|
||||
'parameters' : {
|
||||
'errorMessages' : error.response.data.errors
|
||||
}
|
||||
});
|
||||
storeOptionalToken(error.response);
|
||||
} else {
|
||||
__store.dispatch({
|
||||
'type' : actiontypes_userinfo.LOGIN_RESULT_ERROR,
|
||||
'parameters' : {
|
||||
'errorMessages' : [ errorMessages['login_errorunknown']['en'] ]
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
return Object.assign({}, state, {});
|
||||
case actiontypes_userinfo.LOGIN_RESULT_SUCCESS:
|
||||
return Object.assign({}, state, {
|
||||
isSignedIn : true,
|
||||
error : false,
|
||||
errorMessages : [],
|
||||
username : action.parameters.username,
|
||||
});
|
||||
case actiontypes_userinfo.LOGIN_RESULT_ERROR:
|
||||
return Object.assign({}, state, {
|
||||
error : true,
|
||||
errorMessages : action.parameters.errorMessages
|
||||
});
|
||||
case actiontypes_userinfo.LOGOUT:
|
||||
deleteRequest(state, '/users/sign_out').then(() => {
|
||||
__store.dispatch({ type : actiontypes_userinfo.CLEAR });
|
||||
}).catch(() => {
|
||||
__store.dispatch({ type : actiontypes_userinfo.CLEAR });
|
||||
});
|
||||
return Object.assign({}, state, {});
|
||||
case actiontypes_userinfo.STORE_AUTH_HEADERS:
|
||||
return Object.assign({}, state, {
|
||||
accesstoken : action.parameters.accesstoken,
|
||||
client : action.parameters.client,
|
||||
expiry : action.parameters.expiry,
|
||||
uid : action.parameters.uid
|
||||
});
|
||||
case actiontypes_userinfo.VERIFY_CREDENTIALS:
|
||||
getRequest(state, '/users/validate_token').then((resp) => {
|
||||
storeOptionalToken(resp);
|
||||
}).catch(() => {
|
||||
__store.dispatch({ type: actiontypes_userinfo.CLEAR });
|
||||
});
|
||||
return Object.assign({}, state, {});
|
||||
case actiontypes_userinfo.REHYDRATE:
|
||||
return Object.assign({}, state, action.parameters);
|
||||
case actiontypes_userinfo.CLEAR:
|
||||
return Object.assign({}, state, {
|
||||
isSignedIn : false,
|
||||
username : null,
|
||||
error : false,
|
||||
errorMessages : [],
|
||||
|
||||
accesstoken : null,
|
||||
client : null,
|
||||
expiry : null,
|
||||
uid : null
|
||||
});
|
||||
default: return state;
|
||||
}
|
||||
};
|
||||
|
||||
const reducers = {
|
||||
userinfo: reducer_userinfo
|
||||
};
|
||||
|
||||
const default_applicationstate = {
|
||||
userinfo : defaultstate_userinfo
|
||||
};
|
||||
|
||||
var __store;
|
||||
|
||||
export function initializeStore(initialState = default_applicationstate) {
|
||||
__store = createStore(
|
||||
combineReducers(reducers),
|
||||
initialState,
|
||||
composeWithDevTools(applyMiddleware(thunkMiddleware))
|
||||
);
|
||||
__store.subscribe(() => {
|
||||
localStorage.setItem('reduxState', JSON.stringify(__store.getState()));
|
||||
});
|
||||
return __store;
|
||||
}
|
||||
|
||||
export function verifyCredentials() {
|
||||
rehydrateApplicationState();
|
||||
|
||||
if(__store.getState().userinfo.isSignedIn) {
|
||||
__store.dispatch({ type: actiontypes_userinfo.VERIFY_CREDENTIALS });
|
||||
}
|
||||
}
|
||||
|
||||
export function register(username, email, password) {
|
||||
__store.dispatch({
|
||||
type: actiontypes_userinfo.REGISTER,
|
||||
parameters: {
|
||||
username: username,
|
||||
email: email,
|
||||
password: password
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function login(email, password) {
|
||||
__store.dispatch({
|
||||
type: actiontypes_userinfo.LOGIN,
|
||||
parameters: {
|
||||
email: email,
|
||||
password: password
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function logout() {
|
||||
__store.dispatch({ type : actiontypes_userinfo.LOGOUT });
|
||||
}
|
||||
|
||||
export function getState() {
|
||||
return __store.getState();
|
||||
}
|
||||
|
||||
function rehydrateApplicationState() {
|
||||
const persistedState = localStorage.getItem('reduxState') ?
|
||||
JSON.parse(localStorage.getItem('reduxState')) :
|
||||
undefined;
|
||||
|
||||
if(persistedState) {
|
||||
__store.dispatch({
|
||||
type : actiontypes_userinfo.REHYDRATE,
|
||||
parameters : Object.assign({}, persistedState.userinfo, {})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
import React from 'react';
|
||||
|
||||
export function BigImage(props) {
|
||||
return (
|
||||
<div className="big-image">
|
||||
<h1 className="display-1">{props.text}</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
|
||||
|
||||
export const errorMessages = {
|
||||
registration_errorunknown : {
|
||||
en : 'An unknown error prevented a successful registration.'
|
||||
},
|
||||
login_errorunknown : {
|
||||
en : 'An unknown error prevented a successful login.'
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
import React from 'react';
|
||||
import { initializeStore } from '../api';
|
||||
|
||||
const isServer = typeof window === 'undefined';
|
||||
const __NEXT_REDUX_STORE__ = '__NEXT_REDUX_STORE__';
|
||||
|
||||
function getOrCreateStore (initialState) {
|
||||
// Always make a new store if server, otherwise state is shared between requests
|
||||
if (isServer) {
|
||||
return initializeStore(initialState);
|
||||
}
|
||||
|
||||
// Create store if unavailable on the client and set it on the window object
|
||||
if (!window[__NEXT_REDUX_STORE__]) {
|
||||
window[__NEXT_REDUX_STORE__] = initializeStore(initialState);
|
||||
}
|
||||
return window[__NEXT_REDUX_STORE__];
|
||||
}
|
||||
|
||||
export default (App) => {
|
||||
return class AppWithRedux extends React.Component {
|
||||
static async getInitialProps (appContext) {
|
||||
// Get or Create the store with `undefined` as initialState
|
||||
// This allows you to set a custom default initialState
|
||||
const reduxStore = getOrCreateStore();
|
||||
|
||||
// Provide the store to getInitialProps of pages
|
||||
appContext.ctx.reduxStore = reduxStore;
|
||||
|
||||
let appProps = {};
|
||||
if (typeof App.getInitialProps === 'function') {
|
||||
appProps = await App.getInitialProps(appContext);
|
||||
}
|
||||
|
||||
return {
|
||||
...appProps,
|
||||
initialReduxState: reduxStore.getState()
|
||||
};
|
||||
}
|
||||
|
||||
constructor (props) {
|
||||
super(props);
|
||||
this.reduxStore = getOrCreateStore(props.initialReduxState);
|
||||
}
|
||||
|
||||
render () {
|
||||
return <App {...this.props} reduxStore={this.reduxStore} />;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
|
||||
const withCSS = require('@zeit/next-css')
|
||||
module.exports = withCSS()
|
||||
const withCSS = require('@zeit/next-css');
|
||||
module.exports = withCSS();
|
||||
|
|
@ -20,9 +20,15 @@
|
|||
"next": "^7.0.2",
|
||||
"react": "^16.6.1",
|
||||
"react-dom": "^16.6.1",
|
||||
"reactstrap": "^6.5.0"
|
||||
"react-redux": "^5.1.1",
|
||||
"reactstrap": "^6.5.0",
|
||||
"redux": "^4.0.1",
|
||||
"redux-devtools-extension": "^2.13.7",
|
||||
"redux-thunk": "^2.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^5.9.0",
|
||||
"eslint-plugin-react": "^7.11.1",
|
||||
"react-editable-list": "0.0.3"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
import App, {Container} from 'next/app';
|
||||
import React from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import withReduxStore from '../js/redux/reduxStoreBinder';
|
||||
import { verifyCredentials } from '../js/api';
|
||||
|
||||
class TurniereApp extends App {
|
||||
|
||||
componentDidMount() {
|
||||
verifyCredentials();
|
||||
}
|
||||
|
||||
render () {
|
||||
const {Component, pageProps, reduxStore} = this.props;
|
||||
return (
|
||||
<Container>
|
||||
<Provider store={reduxStore}>
|
||||
<Component {...pageProps} />
|
||||
</Provider>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withReduxStore(TurniereApp);
|
||||
|
|
@ -1,15 +1,15 @@
|
|||
import Head from 'next/head'
|
||||
import React from 'react'
|
||||
import {Footer, TurniereNavigation} from "../js/CommonComponents";
|
||||
import Head from 'next/head';
|
||||
import React from 'react';
|
||||
import {Footer, TurniereNavigation} from '../js/CommonComponents';
|
||||
import 'bootstrap/dist/css/bootstrap.min.css';
|
||||
import {Container} from "reactstrap";
|
||||
import '../static/everypage.css'
|
||||
import '../static/css/error.css'
|
||||
import {Container} from 'reactstrap';
|
||||
import '../static/everypage.css';
|
||||
import '../static/css/error.css';
|
||||
|
||||
export default class Error extends React.Component {
|
||||
static getInitialProps({ res, err }) {
|
||||
const statusCode = res ? res.statusCode : err ? err.statusCode : 400;
|
||||
return { statusCode }
|
||||
return { statusCode };
|
||||
}
|
||||
|
||||
render() {
|
||||
|
|
@ -22,7 +22,7 @@ export default class Error extends React.Component {
|
|||
<ErrorPage statusCode={this.props.statusCode}/>
|
||||
<Footer/>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,22 @@
|
|||
import Head from 'next/head'
|
||||
import '../static/everypage.css'
|
||||
import {Footer, TurniereNavigation} from "../js/CommonComponents";
|
||||
import React from "react";
|
||||
import {Button, Card, CardBody, Container, CustomInput, Fade, Form, FormGroup, Input, Label} from "reactstrap";
|
||||
import EditableStringList from "../js/EditableStringList";
|
||||
import Head from 'next/head';
|
||||
import '../static/everypage.css';
|
||||
import { Footer, TurniereNavigation } from '../js/CommonComponents';
|
||||
import React from 'react';
|
||||
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
CardBody,
|
||||
Container,
|
||||
CustomInput,
|
||||
Fade,
|
||||
Form,
|
||||
FormGroup,
|
||||
Input,
|
||||
Label
|
||||
} from 'reactstrap';
|
||||
|
||||
import EditableStringList from '../js/EditableStringList';
|
||||
|
||||
export default () => (
|
||||
<div className="main generic-fullpage-bg">
|
||||
|
|
@ -16,7 +29,7 @@ export default () => (
|
|||
</div>
|
||||
<Footer/>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
|
||||
function CreateTournamentCard() {
|
||||
return (
|
||||
|
|
|
|||
58
pages/faq.js
58
pages/faq.js
|
|
@ -1,9 +1,9 @@
|
|||
import Head from 'next/head'
|
||||
import React from 'react'
|
||||
import Head from 'next/head';
|
||||
import React from 'react';
|
||||
import {Col, Container, Row} from 'reactstrap';
|
||||
import 'bootstrap/dist/css/bootstrap.min.css';
|
||||
import {BigImage, Footer, TurniereNavigation} from '../js/CommonComponents.js'
|
||||
import '../static/everypage.css'
|
||||
import { BigImage, Footer, TurniereNavigation } from '../js/CommonComponents.js';
|
||||
import '../static/everypage.css';
|
||||
|
||||
function Main() {
|
||||
return (
|
||||
|
|
@ -33,19 +33,22 @@ function GeneralFaq() {
|
|||
mit allen Leuten teilen kannst, ohne dir Gedanken machen zu müssen, wer gegen wen spielen
|
||||
muss. Du trägst ein, wer gewonnen hat, und turnie.re sagt, wer als nächstes spielt.
|
||||
</p>
|
||||
</Col><Col sm="4">
|
||||
</Col>
|
||||
<Col sm="4">
|
||||
<h4>Für welche Sportarten ist turnie.re geeignet?</h4>
|
||||
<p>
|
||||
turnie.re ist prinzipiell für jede Sportart geeignet. Die einzige Vor­aus­setzung ist, dass
|
||||
in jedem Spiel zwei Mannschaften oder Spieler gegeinander antreten und dass derjenige mit
|
||||
den meisten Punkten gewinnt.
|
||||
</p>
|
||||
</Col><Col sm="4">
|
||||
</Col>
|
||||
<Col sm="4">
|
||||
<h4>Für welche Anzahl an Teams ist turnie.re geeignet?</h4>
|
||||
<p>
|
||||
turnie.re ist unabhängig von der Anzahl der Teams nutzbar.
|
||||
</p>
|
||||
</Col><Col sm="4">
|
||||
</Col>
|
||||
<Col sm="4">
|
||||
<h4>Fallen für die Nutzung von turnie.re Kosten an?</h4>
|
||||
<p>
|
||||
turnie.re ist ein kostenloser Service! Wir erheben keine Kosten und sind nur darauf aus,
|
||||
|
|
@ -69,34 +72,40 @@ function AccountFaq() {
|
|||
dich als berechtigt verifizieren können, benötigst du einen Acoount, sodass wir dir die
|
||||
entsprechenden Bearbeitungsrechte zuteilen können.
|
||||
</p>
|
||||
</Col><Col sm="4">
|
||||
</Col>
|
||||
<Col sm="4">
|
||||
<h4>Welche Daten muss ich bei der Accounterstellung angeben?</h4>
|
||||
<p>
|
||||
Um einen Account anzulegen musst du einen Nutzernamen, eine gültige E-Mailadresse sowie ein
|
||||
Passwort angeben.
|
||||
</p>
|
||||
</Col><Col sm="4">
|
||||
</Col>
|
||||
<Col sm="4">
|
||||
<h4>Wie werden meine Daten verarbeitet?</h4>
|
||||
<p>
|
||||
Deine Daten werden in unserer Datenbank gespeichert. Eine Weitergabe dieser Daten an Dritte
|
||||
erfolgt nicht!</p>
|
||||
erfolgt nicht!
|
||||
</p>
|
||||
<p>
|
||||
Zusätlich wird dein Passwort verschlüsselt gespeichert, das bedeutet auch wir kennen dein
|
||||
Passwort nicht und dein Account wird zuverlässig geschützt.
|
||||
</p>
|
||||
</Col><Col sm="4">
|
||||
</Col>
|
||||
<Col sm="4">
|
||||
<h4>Wie kann ich meinen Nutzernamen ändern?</h4>
|
||||
<p>
|
||||
Über deinen Nutzernamen, der in der Kopfzeile angezeigt wird, gelangst du auf deine
|
||||
Profilseite. Hier kannst du deinen Nutzernamen ändern.
|
||||
</p>
|
||||
</Col><Col sm="4">
|
||||
</Col>
|
||||
<Col sm="4">
|
||||
<h4>Wie kann ich meine E-Mailadresse ändern?</h4>
|
||||
<p>
|
||||
Über deinen Nutzernamen, der in der Kopfzeile angezeigt wird, gelangst du auf deine
|
||||
Profilseite. Hier kannst du deine E-Mailadresse ändern ändern.
|
||||
</p>
|
||||
</Col><Col sm="4">
|
||||
</Col>
|
||||
<Col sm="4">
|
||||
<h4>Wie kann ich mein Passwort ändern?</h4>
|
||||
<p>
|
||||
Auf deiner Profilseite findest du einen "Passwort ändern" Button. Auf der sich dann
|
||||
|
|
@ -121,15 +130,16 @@ function TournamentFaq() {
|
|||
Über "Turnier erstellen" gelangst du auf die "Turnier erstellen" Seite. Auf dieser kannst du
|
||||
deinem Turnier einen Namen geben und eine (optionale) Beschreibung hinzufügen. Dann kannst
|
||||
du dein Turnier <a href="#public-tournament">öffentlich oder privat</a> erstellen, die Teams
|
||||
für die Spielpaarungen <a href="#randomize-teams">mischen</a> lassen und eine <a
|
||||
href="#groupstage">Gruppenphase</a> hinzufügen. Im Feld "Teams" kannst du die
|
||||
für die Spielpaarungen <a href="#randomize-teams">mischen</a> lassen und eine <a href="#groupstage">Gruppenphase</a>
|
||||
hinzufügen. Im Feld "Teams" kannst du die
|
||||
teilnehmenden <a href="#add-teams">Teams
|
||||
eintragen</a> und hinzufügen. </p>
|
||||
<p>
|
||||
Wenn du die Option Gruppenphase aktiviert hast, kannst du zusätzlich noch die Größe der
|
||||
Gruppen angeben.
|
||||
</p>
|
||||
</Col><Col sm="4">
|
||||
</Col>
|
||||
<Col sm="4">
|
||||
<h4 id="public-tournament">Was ist der Unterschied zwischen einem öffentlichen und einem
|
||||
privaten Turnier?</h4>
|
||||
<p>
|
||||
|
|
@ -147,7 +157,8 @@ function TournamentFaq() {
|
|||
Trotzdem bleibt der Turnierersteller der Einzige, der die Turnierinformationen bearbeiten
|
||||
und Spielstände eintragen kann.
|
||||
</p>
|
||||
</Col><Col sm="4">
|
||||
</Col>
|
||||
<Col sm="4">
|
||||
<h4 id="randomize-teams">Was bedeutet "Teams mischen"?</h4>
|
||||
<p>
|
||||
Die Spielpaarungen werden anhand der Eingabereihenfolge der Teams erstellt. So spielt z.B.
|
||||
|
|
@ -157,7 +168,8 @@ function TournamentFaq() {
|
|||
Wenn du das nicht möchtest kannst du die Option "Teams mischen" aktivieren und die
|
||||
Spielpaarungen werden in einer zufälligen Reihenfolge erstellt.
|
||||
</p>
|
||||
</Col><Col sm="4">
|
||||
</Col>
|
||||
<Col sm="4">
|
||||
<h4 id="groupstage">Was passiert wenn ich die Gruppenphase aktiviere?</h4>
|
||||
<p>
|
||||
Grundsätzlich erstellt turnie.re dir einen Spielplan für ein Turnier <strong>ohne</strong>
|
||||
|
|
@ -168,7 +180,8 @@ function TournamentFaq() {
|
|||
<p>
|
||||
Bitte beachte, dass die Anzahl der Teams durch die Gruppengröße teilbar sein muss.
|
||||
</p>
|
||||
</Col><Col sm="4">
|
||||
</Col>
|
||||
<Col sm="4">
|
||||
<h4 id="add-teams">Wie kann ich Teams hinzufügen?</h4>
|
||||
<p>
|
||||
Auf der "Turnier erstellen" Seite kannst du im Feld "Teams" deine Teams eintragen. </p>
|
||||
|
|
@ -177,14 +190,15 @@ function TournamentFaq() {
|
|||
Button "Team hinzufügen" das Team deinem Turnier hinzufügen. Du kannst aber auch deine Teams
|
||||
als eine mit Kommas getrennte Liste eingeben und dann hinzufügen.
|
||||
</p>
|
||||
</Col><Col sm="4">
|
||||
|
||||
</Col>
|
||||
<Col sm="4">
|
||||
<h4>Wie starte ich ein Spiel?</h4>
|
||||
<p>
|
||||
Auf der Turnierübersicht Seite gibt es für jede Partie einen "Spiel starten" Button. Über
|
||||
diesen kannst du einfach das jeweilige Spiel starten.
|
||||
</p>
|
||||
</Col><Col sm="4">
|
||||
</Col>
|
||||
<Col sm="4">
|
||||
<h4>Wie trage ich einen Spielstand für eine Partie ein?</h4>
|
||||
<p>
|
||||
Auf der Turnierübersicht Seite gibt es für jede Partie einen "Spielstand ändern" Button.
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import Head from 'next/head'
|
||||
import React from 'react'
|
||||
import Head from 'next/head';
|
||||
import React from 'react';
|
||||
import {Container} from 'reactstrap';
|
||||
import 'bootstrap/dist/css/bootstrap.min.css';
|
||||
import {BigImage, Footer, TurniereNavigation} from '../js/CommonComponents.js'
|
||||
import '../static/everypage.css'
|
||||
import {BigImage, Footer, TurniereNavigation} from '../js/CommonComponents.js';
|
||||
import '../static/everypage.css';
|
||||
|
||||
function Main() {
|
||||
return (
|
||||
|
|
@ -14,7 +14,8 @@ function Main() {
|
|||
}
|
||||
|
||||
function ImprintText(){
|
||||
return (<Container>
|
||||
return (
|
||||
<Container>
|
||||
<h3>
|
||||
Angaben gemäß §5 TMG:
|
||||
</h3>
|
||||
|
|
@ -24,22 +25,16 @@ function ImprintText(){
|
|||
76133 Karlsruhe<br/>
|
||||
Germany<br/>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<strong>Vertreten durch</strong><br/>
|
||||
Jonas Seydel
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<strong>Kontakt</strong><br/>
|
||||
jon@s-seydel.de
|
||||
</p>
|
||||
|
||||
|
||||
<h3>Haftungsausschluss</h3>
|
||||
|
||||
<h4>Haftung für Inhalte</h4>
|
||||
|
||||
<p>
|
||||
Die Inhalte unserer Seiten wurden mit größter Sorgfalt erstellt. Für die Richtigkeit, Vollständigkeit und Aktualität
|
||||
der Inhalte können wir jedoch keine Gewähr übernehmen. Als Diensteanbieter sind wir gemäß § 7 Abs.1 TMG für eigene
|
||||
|
|
@ -50,9 +45,7 @@ function ImprintText(){
|
|||
jedoch erst ab dem Zeitpunkt der Kenntnis einer konkreten Rechtsverletzung möglich. Bei Bekanntwerden von
|
||||
entsprechenden Rechtsverletzungen werden wir diese Inhalte umgehend entfernen.
|
||||
</p>
|
||||
|
||||
<h4>Haftung für Links</h4>
|
||||
|
||||
<p>
|
||||
Unser Angebot enthält Links zu externen Webseiten Dritter, auf deren Inhalte wir keinen Einfluss haben. Deshalb können
|
||||
wir für diese fremden Inhalte auch keine Gewähr übernehmen. Für die Inhalte der verlinkten Seiten ist stets der
|
||||
|
|
@ -61,9 +54,7 @@ function ImprintText(){
|
|||
permanente inhaltliche Kontrolle der verlinkten Seiten ist jedoch ohne konkrete Anhaltspunkte einer Rechtsverletzung
|
||||
nicht zumutbar. Bei Bekanntwerden von Rechtsverletzungen werden wir derartige Links umgehend entfernen.
|
||||
</p>
|
||||
|
||||
<h4>Urheberrecht</h4>
|
||||
|
||||
<p>
|
||||
Die durch die Seitenbetreiber erstellten Inhalte und Werke auf diesen Seiten unterliegen dem deutschen Urheberrecht.
|
||||
Die Vervielfältigung, Bearbeitung, Verbreitung und jede Art der Verwertung außerhalb der Grenzen des Urheberrechtes
|
||||
|
|
@ -73,9 +64,8 @@ function ImprintText(){
|
|||
gekennzeichnet. Sollten Sie trotzdem auf eine Urheberrechtsverletzung aufmerksam werden, bitten wir um einen
|
||||
entsprechenden Hinweis. Bei Bekanntwerden von Rechtsverletzungen werden wir derartige Inhalte umgehend entfernen.
|
||||
</p>
|
||||
|
||||
|
||||
</Container>);
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,16 @@
|
|||
import Head from 'next/head'
|
||||
import React from 'react'
|
||||
import Head from 'next/head';
|
||||
import React from 'react';
|
||||
|
||||
import {Alert, Button, Card, CardBody} from 'reactstrap';
|
||||
import { Alert, Button, Card, CardBody } from 'reactstrap';
|
||||
|
||||
import 'bootstrap/dist/css/bootstrap.min.css';
|
||||
|
||||
import {BigImage, Footer, TurniereNavigation} from '../js/CommonComponents.js'
|
||||
import { BigImage, Footer, TurniereNavigation } from '../js/CommonComponents.js';
|
||||
|
||||
import '../static/everypage.css'
|
||||
import '../static/css/index.css'
|
||||
import '../static/everypage.css';
|
||||
import '../static/css/index.css';
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
function Main() {
|
||||
return (
|
||||
|
|
@ -24,7 +26,6 @@ function Marketing() {
|
|||
return (
|
||||
<div className="container marketing my-5">
|
||||
<div className="row">
|
||||
|
||||
<div className="col-lg-4">
|
||||
<h2>Für jede Sportart</h2>
|
||||
<p>
|
||||
|
|
@ -86,12 +87,10 @@ function MainBottomSummary() {
|
|||
<div className="col-lg-6">
|
||||
<h2>Ich habe einen Turniercode bekommen. Was nun?</h2>
|
||||
<p>
|
||||
Der Turniercode führt dich direkt zu einem Turnier. Gebe dafür den Code <a className="text-success"
|
||||
href="#turniercode-form">oben </a>
|
||||
ein, dann wirst du sofort weitergeleitet.
|
||||
Der Turniercode führt dich direkt zu einem Turnier. Gebe dafür den Code
|
||||
<a className="text-success" href="#turniercode-form">oben</a> ein, dann wirst du sofort weitergeleitet.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div className="my-5 text-center">
|
||||
<p>
|
||||
|
|
@ -164,7 +163,10 @@ function PromotedLinkCreateTournament() {
|
|||
</Card>);
|
||||
}
|
||||
|
||||
export default () => (
|
||||
|
||||
class Index extends React.Component {
|
||||
render () {
|
||||
return (
|
||||
<div>
|
||||
<Head>
|
||||
<title>turnie.re</title>
|
||||
|
|
@ -174,4 +176,8 @@ export default () => (
|
|||
<Main/>
|
||||
<Footer/>
|
||||
</div>
|
||||
);
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default connect()(Index);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import Head from 'next/head'
|
||||
import Head from 'next/head';
|
||||
|
||||
export default () => (
|
||||
<div>
|
||||
|
|
@ -7,4 +7,4 @@ export default () => (
|
|||
</Head>
|
||||
<p>Turnierliste</p>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
import Head from 'next/head'
|
||||
import '../static/everypage.css'
|
||||
import {Footer, TurniereNavigation} from "../js/CommonComponents";
|
||||
import React from "react";
|
||||
import {Button, Card, CardBody, Container, Form, FormGroup, Input, Label} from "reactstrap";
|
||||
import Head from 'next/head';
|
||||
import '../static/everypage.css';
|
||||
import {Footer, TurniereNavigation} from '../js/CommonComponents';
|
||||
import React from 'react';
|
||||
import { Button, Card, CardBody, Container, Form, FormGroup, Input, Label } from 'reactstrap';
|
||||
import { login } from '../js/api';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
export default () => (
|
||||
<div className="main generic-fullpage-bg">
|
||||
|
|
@ -15,7 +17,7 @@ export default () => (
|
|||
</div>
|
||||
<Footer/>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
|
||||
function Login() {
|
||||
return (
|
||||
|
|
@ -34,18 +36,74 @@ function Login() {
|
|||
);
|
||||
}
|
||||
|
||||
function LoginForm() {
|
||||
class LoginErrorList extends React.Component {
|
||||
render() {
|
||||
const { error, errorMessages } = this.props;
|
||||
if(error) {
|
||||
return (
|
||||
<ul>
|
||||
{ errorMessages.map((message, index) =>
|
||||
<li key={index}>
|
||||
<style jsx>{`
|
||||
li {
|
||||
color:red;
|
||||
}
|
||||
`}</style>
|
||||
{message}
|
||||
</li>
|
||||
|
||||
) }
|
||||
</ul>
|
||||
);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToErrorMessages = (state) => {
|
||||
const { errorMessages, error } = state.userinfo;
|
||||
return { errorMessages, error };
|
||||
};
|
||||
|
||||
const VisibleLoginErrorList = connect(
|
||||
mapStateToErrorMessages
|
||||
)(LoginErrorList);
|
||||
|
||||
|
||||
class LoginForm extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
email : '',
|
||||
password : ''
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Form>
|
||||
<FormGroup>
|
||||
<Label for="username">E-Mail-Adresse</Label>
|
||||
<Input type="email" name="username"/>
|
||||
<Input type="email" name="username" value={this.state.email} onChange={ this.handleEmailInput.bind(this) } />
|
||||
</FormGroup>
|
||||
<FormGroup>
|
||||
<Label for="password">Passwort</Label>
|
||||
<Input type="password" name="password"/>
|
||||
<Input type="password" name="password" value={this.state.password} onChange={ this.handlePasswordInput.bind(this) } />
|
||||
</FormGroup>
|
||||
<Button color="success" size="lg" className="w-100 shadow-sm">Anmelden</Button>
|
||||
<Button onClick={login.bind(this, this.state.email, this.state.password)} color="success" size="lg" className="w-100 shadow-sm">Anmelden</Button>
|
||||
<VisibleLoginErrorList/>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
|
||||
handlePasswordInput(input) {
|
||||
this.setState({ password : input.target.value });
|
||||
}
|
||||
|
||||
handleEmailInput(input) {
|
||||
this.setState({ email : input.target.value });
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
import Head from 'next/head'
|
||||
import React from 'react'
|
||||
import Head from 'next/head';
|
||||
import React from 'react';
|
||||
import {Container} from 'reactstrap';
|
||||
import 'bootstrap/dist/css/bootstrap.min.css';
|
||||
import {BigImage, Footer, TurniereNavigation} from '../js/CommonComponents.js'
|
||||
import '../static/everypage.css'
|
||||
import { BigImage, Footer, TurniereNavigation } from '../js/CommonComponents.js';
|
||||
import '../static/everypage.css';
|
||||
|
||||
function Main() {
|
||||
return (
|
||||
|
|
@ -14,7 +14,8 @@ function Main() {
|
|||
}
|
||||
|
||||
function PrivacyText(){
|
||||
return (<Container>
|
||||
return (
|
||||
<Container>
|
||||
<p>
|
||||
Die nachfolgende Datenschutzerklärung gilt für die Nutzung unseres Online-Angebots turnie.re (nachfolgend
|
||||
„Website“).<br/>
|
||||
|
|
@ -24,8 +25,6 @@ function PrivacyText(){
|
|||
Datenschutzgrundverordnung
|
||||
(DSGVO).
|
||||
</p>
|
||||
|
||||
|
||||
<h3>1 Verantwortlicher</h3>
|
||||
<p>
|
||||
Verantwortlicher für die Erhebung, Verarbeitung und Nutzung Ihrer personenbezogenen Daten im Sinne von Art.
|
||||
|
|
@ -47,16 +46,11 @@ function PrivacyText(){
|
|||
richten.<br/>
|
||||
Sie können diese Datenschutzerklärung jederzeit speichern und ausdrucken.
|
||||
</p>
|
||||
|
||||
|
||||
<h3>2 Allgemeine Zwecke der Verarbeitung</h3>
|
||||
<p>
|
||||
Wir verwenden personenbezogene Daten zum Zweck des Betriebs der Website.
|
||||
</p>
|
||||
|
||||
|
||||
<h3>3 Welche Daten wir verwenden und warum</h3>
|
||||
|
||||
<h4>3.1 Hosting</h4>
|
||||
<p>
|
||||
Die von uns in Anspruch genommenen Hosting-Leistungen dienen der Zurverfügungstellung der folgenden
|
||||
|
|
@ -72,7 +66,6 @@ function PrivacyText(){
|
|||
Art. 6
|
||||
Abs. 1 S. 1 f) DSGVO i.V.m. Art. 28 DSGVO.
|
||||
</p>
|
||||
|
||||
<h4>3.2 Zugriffsdaten</h4>
|
||||
<p>
|
||||
Wir sammeln Informationen über Sie, wenn Sie diese Website nutzen. Wir erfassen automatisch Informationen
|
||||
|
|
@ -124,7 +117,6 @@ function PrivacyText(){
|
|||
Registrierung,
|
||||
Login, Klicken von Links etc.).
|
||||
</p>
|
||||
|
||||
<h4>3.3 Cookies</h4>
|
||||
<p>
|
||||
Wir verwenden sogenannte Session-Cookies, um unsere Website zu optimieren. Ein Session-Cookie ist eine
|
||||
|
|
@ -177,7 +169,6 @@ function PrivacyText(){
|
|||
Cookies
|
||||
komplett verhindert werden. Dadurch kann die Funktionalität der Website eingeschränkt werden.
|
||||
</p>
|
||||
|
||||
<h4>3.4 Nutzerkonto</h4>
|
||||
<p>Sie können auf unserer Website ein Nutzerkonto anlegen. Wünschen Sie dies, so benötigen wir die beim Login
|
||||
abgefragten
|
||||
|
|
@ -199,7 +190,6 @@ function PrivacyText(){
|
|||
Rechtgrundlage für die Verarbeitung dieser Daten ist Ihre Einwilligung gemäß Art. 6 Abs. 1 S. 1 a)
|
||||
DSGVO.
|
||||
</p>
|
||||
|
||||
<h4>3.5 E-Mail Kontakt</h4>
|
||||
<p>
|
||||
Wenn Sie mit uns in Kontakt treten (z. B. per Kontaktformular oder E-Mail), verarbeiten wir Ihre Angaben zur
|
||||
|
|
@ -215,8 +205,6 @@ function PrivacyText(){
|
|||
berechtigtes
|
||||
Interesse liegt z. B. darin, auf Ihre E-Mail zu antworten.
|
||||
</p>
|
||||
|
||||
|
||||
<h3>4 Speicherdauer</h3>
|
||||
<p>
|
||||
Sofern nicht spezifisch angegeben speichern wir personenbezogene Daten nur so lange, wie dies zur Erfüllung
|
||||
|
|
@ -228,8 +216,6 @@ function PrivacyText(){
|
|||
gespeichert,
|
||||
aber nicht anderweitig verarbeitet und nach Ablauf der gesetzlichen Aufbewahrungsfrist gelöscht.
|
||||
</p>
|
||||
|
||||
|
||||
<h3>5 Ihre Rechte als von der Datenverarbeitung Betroffener</h3>
|
||||
<p>
|
||||
Nach den anwendbaren Gesetzen haben Sie verschiedene Rechte bezüglich Ihrer personenbezogenen Daten. Möchten
|
||||
|
|
@ -283,14 +269,12 @@ function PrivacyText(){
|
|||
und die angestrebten Auswirkungen einer derartigen Verarbeitung für Sie.
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<p>
|
||||
Werden personenbezogene Daten an ein Drittland oder an eine internationale Organisation übermittelt, so
|
||||
haben Sie das
|
||||
Recht, über die geeigneten Garantien gemäß Art. 46 DSGVO im Zusammenhang mit der Übermittlung unterrichtet
|
||||
zu werden.
|
||||
</p>
|
||||
|
||||
<h4>5.2 Recht auf Berichtigung</h4>
|
||||
<p>
|
||||
Sie haben das Recht, von uns die Berichtigung und ggf. auch Vervollständigung Sie betreffender
|
||||
|
|
@ -349,7 +333,6 @@ function PrivacyText(){
|
|||
personenbezogenen
|
||||
Daten oder von Kopien oder Replikationen dieser personenbezogenen Daten verlangt haben.
|
||||
</p>
|
||||
|
||||
<h4>5.4 Recht auf Einschränkung der Verarbeitung</h4>
|
||||
<p>
|
||||
In einer Reihe von Fällen sind Sie berechtigt, von uns eine Einschränkung der Verarbeitung Ihrer
|
||||
|
|
@ -378,7 +361,6 @@ function PrivacyText(){
|
|||
ob die berechtigten Gründe unseres Unternehmens gegenüber den Ihren überwiegen.
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<h4>5.5 Recht auf Datenübertragbarkeit</h4>
|
||||
<p>
|
||||
Sie haben das Recht, Sie betreffende personenbezogene Daten maschinenlesbar zu erhalten, zu übermitteln,
|
||||
|
|
@ -405,7 +387,6 @@ function PrivacyText(){
|
|||
technisch
|
||||
machbar ist.
|
||||
</p>
|
||||
|
||||
<h4>5.6 Widerspruchsrecht</h4>
|
||||
<p>
|
||||
Sie haben das Recht, auch einer rechtmäßigen Verarbeitung Ihrer personenbezogenen Daten durch uns zu
|
||||
|
|
@ -434,7 +415,6 @@ function PrivacyText(){
|
|||
oder zu statistischen Zwecken gemäß Art. 89 Abs. 1 DSGVO erfolgt, Widerspruch einzulegen, es sei denn, die
|
||||
Verarbeitung ist zur Erfüllung einer im öffentlichen Interesse liegenden Aufgabe erforderlich.<br/>
|
||||
</p>
|
||||
|
||||
<h4>5.7 Automatisierte Entscheidungen einschließlich Profiling</h4>
|
||||
<p>
|
||||
Sie haben das Recht, nicht einer ausschließlich auf einer automatisierten Verarbeitung – einschließlich
|
||||
|
|
@ -445,7 +425,6 @@ function PrivacyText(){
|
|||
Eine automatisierte Entscheidungsfindung auf der Grundlage der erhobenen personenbezogenen Daten findet
|
||||
nicht statt.
|
||||
</p>
|
||||
|
||||
<h4>5.8 Recht auf Widerruf einer datenschutzrechtlichen Einwilligung</h4>
|
||||
<p>
|
||||
Sie haben das Recht, eine Einwilligung zur Verarbeitung personenbezogener Daten jederzeit zu widerrufen.
|
||||
|
|
@ -458,8 +437,6 @@ function PrivacyText(){
|
|||
dass die
|
||||
Verarbeitung der Sie betreffenden personenbezogenen Daten rechtswidrig ist.
|
||||
</p>
|
||||
|
||||
|
||||
<h3>6 Datensicherheit</h3>
|
||||
<p>
|
||||
Wir sind um die Sicherheit Ihrer Daten im Rahmen der geltenden Datenschutzgesetze und technischen
|
||||
|
|
@ -479,8 +456,6 @@ function PrivacyText(){
|
|||
regelmäßig
|
||||
sorgfältig gesichert.
|
||||
</p>
|
||||
|
||||
|
||||
<h3>7 Weitergabe von Daten an Dritte, keine Datenübertragung ins Nicht-EU-Ausland</h3>
|
||||
<p>
|
||||
Grundsätzlich verwenden wir Ihre personenbezogenen Daten nur innerhalb unseres Unternehmens.<br/>
|
||||
|
|
@ -496,8 +471,6 @@ function PrivacyText(){
|
|||
4
|
||||
genannten Falls findet nicht statt und ist nicht geplant.
|
||||
</p>
|
||||
|
||||
|
||||
<h3>8 Datenschutzbeauftragter</h3>
|
||||
<p>
|
||||
Sollten Sie noch Fragen oder Bedenken zum Datenschutz haben, so wenden Sie sich bitte an unseren
|
||||
|
|
@ -510,8 +483,8 @@ function PrivacyText(){
|
|||
Germany<br/>
|
||||
jon@s-seydel.de
|
||||
</p>
|
||||
|
||||
</Container>);
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
import Head from 'next/head'
|
||||
import '../static/everypage.css'
|
||||
import {Footer, TurniereNavigation} from "../js/CommonComponents";
|
||||
import React from "react";
|
||||
import {Button, Card, CardBody, Container, Form, FormGroup, FormText, Input, Label} from "reactstrap";
|
||||
import Head from 'next/head';
|
||||
import '../static/everypage.css';
|
||||
import {Footer, TurniereNavigation} from '../js/CommonComponents';
|
||||
import React from 'react';
|
||||
import { Button, Card, CardBody, Container, Form, FormGroup, FormText, Input, Label } from 'reactstrap';
|
||||
import { register } from '../js/api';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
export default () => (
|
||||
<div className="main generic-fullpage-bg">
|
||||
|
|
@ -16,7 +18,7 @@ export default () => (
|
|||
</div>
|
||||
<Footer/>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
|
||||
function Register() {
|
||||
return (
|
||||
|
|
@ -34,30 +36,90 @@ function Register() {
|
|||
);
|
||||
}
|
||||
|
||||
function RegisterForm() {
|
||||
class RegisterErrorList extends React.Component {
|
||||
render() {
|
||||
const { error, errorMessages } = this.props;
|
||||
if(error) {
|
||||
return (
|
||||
<ul>
|
||||
{ errorMessages.map((message, index) =>
|
||||
<li key={index}>
|
||||
<style jsx>{`
|
||||
li {
|
||||
color:red;
|
||||
}
|
||||
`}</style>
|
||||
{message}
|
||||
</li>
|
||||
|
||||
) }
|
||||
</ul>
|
||||
);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToErrorMessages = (state) => {
|
||||
const { errorMessages, error } = state.userinfo;
|
||||
return { errorMessages, error };
|
||||
};
|
||||
|
||||
const VisibleRegisterErrorList = connect(
|
||||
mapStateToErrorMessages
|
||||
)(RegisterErrorList);
|
||||
|
||||
class RegisterForm extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
username : '',
|
||||
email : '',
|
||||
password : ''
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Form>
|
||||
<FormGroup>
|
||||
<Label for="username">Benutzername</Label>
|
||||
<Input name="username"/>
|
||||
<Input name="username" value={this.state.username} onChange={ this.handleUsernameInput.bind(this) } />
|
||||
<FormText>Wenn du anderen dein Turnier zeigst, können sie deinen Benutzernamen sehen.</FormText>
|
||||
</FormGroup>
|
||||
<FormGroup>
|
||||
<Label for="email">E-Mail-Adresse</Label>
|
||||
<Input type="email" name="email"/>
|
||||
<Input type="email" name="email" value={this.state.email} onChange={ this.handleEmailInput.bind(this) } />
|
||||
<FormText>Deine E-Mail-Adresse kann nur von dir gesehen werden.</FormText>
|
||||
</FormGroup>
|
||||
<FormGroup>
|
||||
<Label for="password">Passwort</Label>
|
||||
<Input type="password" name="password"/>
|
||||
<Input type="password" name="password" value={this.state.password} onChange={ this.handlePasswordInput.bind(this) } />
|
||||
<FormText>Dein Passwort muss mindestens 12 Zeichen lang sein. Alle Zeichen sind erlaubt.</FormText>
|
||||
</FormGroup>
|
||||
<FormText className="mb-2 mt-4">
|
||||
Du akzeptierst die <a href="/privacy">Datenschutzbestimmungen</a>, wenn du auf Registrieren klickst.
|
||||
</FormText>
|
||||
<Button color="success" size="lg" className="w-100 shadow-sm">Registrieren</Button>
|
||||
<Button onClick={ register.bind(this, this.state.username, this.state.email, this.state.password) } color="success" size="lg" className="w-100 shadow-sm">Registrieren</Button>
|
||||
<VisibleRegisterErrorList/>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
|
||||
handlePasswordInput(input) {
|
||||
this.setState({ password : input.target.value });
|
||||
}
|
||||
|
||||
handleEmailInput(input) {
|
||||
this.setState({ email : input.target.value });
|
||||
}
|
||||
|
||||
handleUsernameInput(input) {
|
||||
this.setState({ username : input.target.value });
|
||||
}
|
||||
}
|
||||
|
||||
function AccountRequirementMarketing() {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
import Head from 'next/head'
|
||||
import Head from 'next/head';
|
||||
import React from 'react';
|
||||
|
||||
class FullscreenTournamentPage extends React.Component {
|
||||
|
||||
static async getInitialProps({query}) {
|
||||
return {query}
|
||||
return {query};
|
||||
}
|
||||
|
||||
render() {
|
||||
|
|
@ -19,4 +20,4 @@ class FullscreenTournamentPage extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
export default FullscreenTournamentPage
|
||||
export default FullscreenTournamentPage;
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
import Head from 'next/head'
|
||||
import "../style.css"
|
||||
import Head from 'next/head';
|
||||
import React from 'react';
|
||||
import '../style.css';
|
||||
|
||||
class TournamentPage extends React.Component {
|
||||
|
||||
static async getInitialProps({query}) {
|
||||
return {query}
|
||||
return {query};
|
||||
}
|
||||
|
||||
render() {
|
||||
|
|
@ -20,4 +21,4 @@ class TournamentPage extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
export default TournamentPage
|
||||
export default TournamentPage;
|
||||
|
|
|
|||
46
server.js
46
server.js
|
|
@ -1,36 +1,34 @@
|
|||
const express = require('express')
|
||||
const next = require('next')
|
||||
const express = require('express');
|
||||
const next = require('next');
|
||||
|
||||
const dev = process.env.NODE_ENV !== 'production'
|
||||
const app = next({ dev })
|
||||
const handle = app.getRequestHandler()
|
||||
const dev = process.env.NODE_ENV !== 'production';
|
||||
const app = next({ dev });
|
||||
const handle = app.getRequestHandler();
|
||||
|
||||
app.prepare()
|
||||
.then(() => {
|
||||
const server = express()
|
||||
.then(() => {
|
||||
const server = express();
|
||||
|
||||
server.get('/t/:code', (req, res) => {
|
||||
const actualPage = '/tournament'
|
||||
const queryParam = { code: req.params.code }
|
||||
app.render(req, res, actualPage, queryParam)
|
||||
})
|
||||
const actualPage = '/tournament';
|
||||
const queryParam = { code: req.params.code };
|
||||
app.render(req, res, actualPage, queryParam);
|
||||
});
|
||||
|
||||
server.get('/t/:code/fullscreen', (req, res) => {
|
||||
const actualPage = '/tournament-fullscreen'
|
||||
const queryParam = { code: req.params.code }
|
||||
app.render(req, res, actualPage, queryParam)
|
||||
})
|
||||
const actualPage = '/tournament-fullscreen';
|
||||
const queryParam = { code: req.params.code };
|
||||
app.render(req, res, actualPage, queryParam);
|
||||
});
|
||||
|
||||
server.get('*', (req, res) => {
|
||||
return handle(req, res)
|
||||
})
|
||||
return handle(req, res);
|
||||
});
|
||||
|
||||
server.listen(3000, (err) => {
|
||||
if (err) throw err
|
||||
console.log('> Ready on http://localhost:3000')
|
||||
if (err) throw err;
|
||||
});
|
||||
})
|
||||
})
|
||||
.catch((ex) => {
|
||||
console.error(ex.stack)
|
||||
process.exit(1)
|
||||
})
|
||||
.catch(() => {
|
||||
process.exit(1);
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue