Merge pull request #20 from turniere/ticket/TURNIERE-141

Ticket/turniere 141
This commit is contained in:
Jonny 2019-05-01 15:58:36 +02:00 committed by GitHub
commit e5788281c2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 233 additions and 71 deletions

View File

@ -32,7 +32,7 @@ const actiontypes_userinfo = {
'STORE_AUTH_HEADERS' : 'STORE_AUTH_HEADERS',
'REHYDRATE' : 'USERINFO_REHYDRATE',
'CLEAR' : 'USERINFO_CLEAR',
'CLEAR' : 'USERINFO_CLEAR'
};
const defaultstate_userinfo = {
@ -72,6 +72,16 @@ const defaultstate_tournamentinfo = {
teams : []
};
const actiontypes_tournamentlist = {
'FETCH': 'FETCH',
'FETCH_SUCCESS': 'FETCH_SUCCESS',
'REHYDRATE': 'REHYDRATE'
};
const defaultstate_tournamentlist = {
tournaments: []
};
export function postRequest(state, url, data) {
return axios.post(api_url + url, data, {
headers : generateHeaders(state)
@ -332,14 +342,40 @@ const reducer_tournamentinfo = (state = defaultstate_tournamentinfo, action) =>
}
};
const reducer_tournamentlist = (state = defaultstate_tournamentlist, action) => {
switch (action.type) {
case actiontypes_tournamentlist.FETCH:
getRequest(action.state, '/tournaments?type=' + action.parameters.type).then((resp) => {
__store.dispatch({
type: actiontypes_tournamentlist.FETCH_SUCCESS,
parameters: resp.data
});
storeOptionalToken(resp);
action.parameters.successCallback(resp.data);
}).catch((error) => {
if(error.response) {
storeOptionalToken(error.response);
}
action.parameters.errorCallback();
});
return state;
case actiontypes_tournamentlist.FETCH_SUCCESS:
return Object.assign({}, state, {tournaments: action.parameters});
default:
return state;
}
};
const reducers = {
userinfo: reducer_userinfo,
tournamentinfo: reducer_tournamentinfo
tournamentinfo: reducer_tournamentinfo,
tournamentlist: reducer_tournamentlist
};
const default_applicationstate = {
userinfo : defaultstate_userinfo,
tournamentinfo: defaultstate_tournamentinfo
tournamentinfo: defaultstate_tournamentinfo,
tournamentlist: defaultstate_tournamentlist
};
var __store;
@ -451,6 +487,18 @@ export function getState() {
return __store.getState();
}
export function requestTournamentList(type, successCallback, errorCallback) {
__store.dispatch({
type: actiontypes_tournamentlist.FETCH,
parameters: {
type: type,
successCallback: successCallback,
errorCallback: errorCallback
},
state: __store.getState()
});
}
function rehydrateApplicationState() {
const persistedState = localStorage.getItem('reduxState') ?
JSON.parse(localStorage.getItem('reduxState')) :
@ -465,6 +513,10 @@ function rehydrateApplicationState() {
type : actiontypes_tournamentinfo.REHYDRATE,
parameters : Object.assign({}, persistedState.tournamentinfo)
});
__store.dispatch({
type : actiontypes_tournamentlist.REHYDRATE,
parameters : Object.assign({}, persistedState.tournamentlist)
});
applicationHydrated = true;
}
}

View File

@ -41,11 +41,7 @@ export class TurniereNavigation extends React.Component {
<Betabadge/>
<NavbarToggler onClick={this.toggle} />
<Collapse isOpen={!this.state.collapsed} navbar>
<Nav navbar className="mr-auto">
<Navlink target="/create" text="Turnier erstellen"/>
<Navlink target="/list" text="Öffentliche Turniere"/>
<Navlink target="/faq" text="FAQ"/>
</Nav>
<NavLinks/>
<LoginLogoutButtons/>
</Collapse>
</Navbar>
@ -61,6 +57,18 @@ function Navlink(props) {
);
}
class SmartNavLinks extends React.Component {
render() {
return (<Nav navbar className="mr-auto">
<Navlink target="/create" text="Turnier erstellen"/>
<Navlink target="/list" text="Öffentliche Turniere"/>
{this.props.isSignedIn && <Navlink target="/private" text="Private Turniere"/>}
<Navlink target="/faq" text="FAQ"/>
</Nav>);
}
}
function Betabadge() {
return (<Badge color="danger" className="mr-2">BETA</Badge>);
}
@ -92,12 +100,15 @@ class InvisibleLoginLogoutButtons extends React.Component {
}
}
const mapStateToLoginLogoutButtonProperties = (state) => {
const mapStateToUserinfo = (state) => {
const { isSignedIn, username } = state.userinfo;
return { isSignedIn, username };
};
const LoginLogoutButtons = connect(
mapStateToLoginLogoutButtonProperties
mapStateToUserinfo
)(InvisibleLoginLogoutButtons);
const NavLinks = connect(
mapStateToUserinfo
)(SmartNavLinks);

View File

@ -0,0 +1,39 @@
import React from 'react';
import {requestTournamentList} from '../api';
export default class TournamentList extends React.Component {
constructor(props) {
super(props);
this.state = {
tournaments: []
};
}
componentDidMount() {
requestTournamentList(this.props.type, tournaments => {
this.setState({
tournaments: tournaments
});
}, () => {});
}
render() {
if (this.state.tournaments.length === 0) {
return <p className="text-center border-light font-italic text-secondary border-top border-bottom p-1">keine
Turniere vorhanden</p>;
} else {
return this.state.tournaments.map(item => (
//The code should be item.code but the api just supports it this way by now
<TournamentListEntry name={item.name} code={item.id} key={item.id}/>
));
}
}
}
function TournamentListEntry(props) {
return (
<a className="w-100 d-inline-block mt-2 text-left btn btn-outline-primary" href={'/t/' + props.code}>
{props.name}
</a>
);
}

View File

@ -1,22 +1,16 @@
import Head from 'next/head';
import React from 'react';
import {
Card,
CardBody,
Container
} from 'reactstrap';
import Head from 'next/head';
import React from 'react';
import {Card, CardBody, Container} from 'reactstrap';
import { TurniereNavigation } from '../js/components/Navigation';
import { Footer } from '../js/components/Footer';
import {
getRequest,
getState
} from '../js/api';
import {TurniereNavigation} from '../js/components/Navigation';
import {Footer} from '../js/components/Footer';
import '../static/everypage.css';
import TournamentList from '../js/components/TournamentList';
import {connect} from 'react-redux';
export default class PublicTournamentsPage extends React.Component {
export default class ListPage extends React.Component {
render() {
return (
<div className="main generic-fullpage-bg">
@ -25,7 +19,7 @@ export default class ListPage extends React.Component {
</Head>
<TurniereNavigation/>
<div>
<TournamentList/>
<PublicTournamentPageContent/>
</div>
<Footer/>
</div>
@ -33,55 +27,38 @@ export default class ListPage extends React.Component {
}
}
class TournamentList extends React.Component {
constructor(props) {
super(props);
this.state = {
error: null,
isLoaded: false,
items: []
};
}
function mapStateToProperties(state) {
const {isSignedIn} = state.userinfo;
return {isSignedIn};
}
componentDidMount() {
getRequest(getState(), '/tournaments?type=public')
.then(
response => {
this.setState({
isLoaded: true,
items: response.data
});
},
error => {
this.setState({
isLoaded: true,
error
});
}
);
}
const PublicTournamentPageContent = connect(
mapStateToProperties,
)(PublicTournaments);
render() {
return (
<Container className="py-5">
<Card className="shadow">
<CardBody>
<h1 className="custom-font">Öffentliche Turniere</h1>
{this.state.items.map(item => (
//The code should be item.code but the api just supports it this way by now
<TournamentListEntry name={item.name} code={item.id} key={item.id}/>
))}
</CardBody>
</Card>
function PublicTournaments(props) {
if (props.isSignedIn) {
return (<div>
<Container className='pt-5'>
<PublicTournamentsCard/>
</Container>
);
<Container className="pb-5 pt-3">
<a href='/private' className="btn btn-success shadow">zu den privaten Turnieren</a>
</Container>
</div>);
} else {
return (<Container className='py-5'>
<PublicTournamentsCard/>
</Container>);
}
}
function TournamentListEntry(props) {
return (
<a className="w-100 d-inline-block mt-2 text-left btn btn-outline-primary" href={ '/t/' + props.code }>
{props.name}
</a>
);
function PublicTournamentsCard() {
return (<Card className="shadow">
<CardBody>
<h1 className="custom-font">Öffentliche Turniere</h1>
<TournamentList type='public'/>
</CardBody>
</Card>);
}

83
pages/private.js Normal file
View File

@ -0,0 +1,83 @@
import Head from 'next/head';
import React from 'react';
import {connect} from 'react-redux';
import {Card, CardBody, Container,} from 'reactstrap';
import {TurniereNavigation} from '../js/components/Navigation';
import {Footer} from '../js/components/Footer';
import {Option, UserRestrictor} from '../js/components/UserRestrictor';
import {Login} from '../js/components/Login';
import '../static/everypage.css';
import TournamentList from '../js/components/TournamentList';
class PrivateTournamentsPage extends React.Component {
render() {
const {isSignedIn} = this.props;
return (
<UserRestrictor>
<Option condition={isSignedIn}>
<div className="main generic-fullpage-bg">
<Head>
<title>Private Turniere: turnie.re</title>
</Head>
<TurniereNavigation/>
<PrivateTournamentsPageContent/>
<Footer/>
</div>
</Option>
<Option condition={true}>
<div className="main generic-fullpage-bg">
<Head>
<title>Anmeldung</title>
</Head>
<TurniereNavigation/>
<div>
<Login
hint="Sie müssen angemeldet sein, um diesen Inhalt anzuzeigen!"/>
</div>
<Footer/>
</div>
</Option>
</UserRestrictor>
);
}
}
function mapStateToProperties(state) {
const {isSignedIn} = state.userinfo;
return {isSignedIn};
}
const PrivateTournamentListPage = connect(
mapStateToProperties,
)(PrivateTournamentsPage);
export default PrivateTournamentListPage;
function PrivateTournamentsPageContent() {
return (<div>
<Container className="pt-5">
<PrivateTournamentsCard/>
</Container>
<Container className="pb-5 pt-3">
<a href='/list' className="btn btn-success shadow">zu den öffentlichen Turnieren</a>
</Container>
</div>);
}
class PrivateTournamentsCard extends React.Component {
render() {
return (
<Card className="shadow">
<CardBody>
<h1 className="custom-font">Private Turniere</h1>
<TournamentList type='private'/>
</CardBody>
</Card>
);
}
}