q3stats/frontend/src/views/SessionsView.js

244 lines
7.2 KiB
JavaScript

/**
* Copyright (c) 2017 Tomek Wójcik <tomek@bthlabs.pl>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
import ClassName from "classnames";
import React from "react";
import {connect} from "react-redux";
import {Link} from "react-router";
import ChartComponent from "../components/ChartComponent";
import ContainerComponent from "../components/ContainerComponent";
import LoaderComponent from "../components/LoaderComponent";
import WeaponIconComponent from "../components/WeaponIconComponent";
import {cleanup, setDay} from "../actions/sessions";
class NavComponent extends React.PureComponent {
render () {
let previousClassName = ClassName("previous", {
disabled: !this.props.previousDay
});
let nextClassName = ClassName("next", {
disabled: !this.props.nextDay
});
let previousDayLinkTo = this.props.previousDay ? "/sessions/" + this.props.previousDay : "";
let nextDayLinkTo = this.props.nextDay ? "/sessions/" + this.props.nextDay : "";
return (
<nav>
<ul className="pager">
<li className={previousClassName}>
<Link to={previousDayLinkTo}>&larr; Previous</Link>
</li>
<li className={nextClassName}>
<Link to={nextDayLinkTo}>Next &rarr;</Link>
</li>
</ul>
</nav>
);
}
}
NavComponent.propTypes = {
previousDay: React.PropTypes.string,
nextDay: React.PropTypes.string
};
class GamesTableView extends React.PureComponent {
render () {
return (
<table className="table table-striped table-bordered table-hover">
<thead>
<tr>
<th rowSpan="2">Map</th>
<th rowSpan="2">Player</th>
<th colSpan="4" className="text-center">Scores</th>
<th rowSpan="2">Favourite weapon</th>
<th rowSpan="2">Quad freak?</th>
</tr>
<tr>
<th>Total</th>
<th>Frags</th>
<th>Deaths</th>
<th>Suicides</th>
</tr>
</thead>
<tbody>
{(this.props.games.length == 0) &&
<tr>
<td colSpan="8"><strong>No stats :(</strong></td>
</tr>
}
{this.props.games.map((game) => {
return game.scores.map((score, index) => {
let playerStatsLinkTo = "/players/" + score.player + "/game/" + game.uuid;
return (
<tr key={game.uuid + index}>
{(index == 0) &&
<td rowSpan={game.scores.length}>{game.map}</td>
}
<td>
<Link to={playerStatsLinkTo}>{score.player}</Link>
</td>
<td className="text-right">
<strong>{score.score}</strong>
</td>
<td className="text-right">{score.kills}</td>
<td className="text-right">{score.deaths}</td>
<td className="text-right">{score.suicides}</td>
<td className="text-right">
{(!score.favourite_weapon) && "?"}
{(score.favourite_weapon) &&
<WeaponIconComponent weapon={score.favourite_weapon} />
}
</td>
<td className="text-right">
{(!score.quad_freak) && "No"}
{(score.quad_freak) &&
"Yes (" + score.quad_pickups + " pickups)"
}
</td>
</tr>
);
});
})}
</tbody>
</table>
);
}
}
GamesTableView.propTypes = {
games: React.PropTypes.array.isRequired
};
class SessionsView extends React.PureComponent {
constructor (props) {
super(props);
this.chartConfig = {
"chart": {
"type": "column"
},
"title": {
"text": "Scores by map and player"
},
"credits": {
"enabled": false
},
"yAxis": {
"min": -10,
"title": {
"text": "Score"
}
},
"plotOptions": {
"column": {
"pointPadding": 0.2,
"borderWidth": 0
}
}
};
}
setDay (day) {
this.props.dispatch(setDay(this.props.dispatch, day));
}
componentDidMount () {
this.setDay(this.props.params.day);
}
componentWillUnmount () {
this.props.dispatch(cleanup());
}
componentDidUpdate (prevProps) {
if (prevProps.params.day != this.props.params.day) {
this.setDay(this.props.params.day);
}
}
render () {
return (
<ContainerComponent>
{!this.props.loading &&
<div>
<NavComponent
previousDay={this.props.previousDay}
nextDay={this.props.nextDay} />
<h2>Results</h2>
<ChartComponent
config={this.chartConfig}
categories={this.props.categories}
series={this.props.series}
subtitle={this.props.day} />
<h2>Stats</h2>
<GamesTableView
games={this.props.games} />
<NavComponent
onNavigateToDay={this.onNavigateToDay}
previousDay={this.props.previousDay}
nextDay={this.props.nextDay} />
</div>
}
<LoaderComponent visible={this.props.loading} />
</ContainerComponent>
);
}
}
SessionsView.propTypes = {
categories: React.PropTypes.array.isRequired,
day: React.PropTypes.string.isRequired,
games: React.PropTypes.array.isRequired,
loading: React.PropTypes.bool.isRequired,
nextDay: React.PropTypes.string.isRequired,
params: React.PropTypes.object.isRequired,
previousDay: React.PropTypes.string.isRequired,
series: React.PropTypes.array.isRequired
};
const mapStateToProps = (state, props) => {
return {
loading: state.sessions.loading,
day: state.sessions.day,
previousDay: state.sessions.previousDay,
nextDay: state.sessions.nextDay,
games: state.sessions.games,
categories: state.sessions.categories,
series: state.sessions.series,
params: props.params
};
};
const reduxContainer = connect(mapStateToProps)(SessionsView);
export default reduxContainer;