Tidy up initial basic leaderboard

This commit is contained in:
Tom Russell 2020-04-09 16:52:41 +01:00
parent a2393a019a
commit c4ffdf4db9
4 changed files with 134 additions and 116 deletions

View File

@ -6,8 +6,9 @@ async function getLeaders(number_limit: number, time_limit: number) {
number_limit = Math.min(number_limit, max_limit); number_limit = Math.min(number_limit, max_limit);
try { try {
let leaders;
if(time_limit > 0){ if(time_limit > 0){
return await db.manyOrNone( leaders = await db.manyOrNone(
`SELECT count(log_id) as number_edits, username `SELECT count(log_id) as number_edits, username
FROM logs, users FROM logs, users
WHERE logs.user_id = users.user_id WHERE logs.user_id = users.user_id
@ -19,7 +20,7 @@ async function getLeaders(number_limit: number, time_limit: number) {
LIMIT $2`, [time_limit, number_limit] LIMIT $2`, [time_limit, number_limit]
); );
} else { } else {
return await db.manyOrNone( leaders = await db.manyOrNone(
`SELECT count(log_id) as number_edits, username `SELECT count(log_id) as number_edits, username
FROM logs, users FROM logs, users
WHERE logs.user_id = users.user_id WHERE logs.user_id = users.user_id
@ -30,6 +31,9 @@ async function getLeaders(number_limit: number, time_limit: number) {
LIMIT $1`, [number_limit] LIMIT $1`, [number_limit]
); );
} }
return leaders.map(d => {
return {username: d.username, number_edits: Number(d.number_edits)};
})
} catch(error) { } catch(error) {
console.error(error); console.error(error);
return []; return [];

View File

@ -58,6 +58,8 @@ class Header extends React.Component<HeaderProps, HeaderState> {
</button> </button>
</div> </div>
<nav className={this.state.collapseMenu ? 'collapse navbar-collapse' : 'navbar-collapse'}> <nav className={this.state.collapseMenu ? 'collapse navbar-collapse' : 'navbar-collapse'}>
<Categories mode='view' />
<hr />
<ul className="navbar-nav flex-column"> <ul className="navbar-nav flex-column">
<li className="nav-item"> <li className="nav-item">
<NavLink to="/view/categories" className="nav-link" onClick={this.handleNavigate}> <NavLink to="/view/categories" className="nav-link" onClick={this.handleNavigate}>
@ -133,8 +135,6 @@ class Header extends React.Component<HeaderProps, HeaderState> {
</li> </li>
</ul> </ul>
<hr /> <hr />
<Categories mode='view' />
<hr />
<ul className="navbar-nav flex-column"> <ul className="navbar-nav flex-column">
<li className="nav-item"> <li className="nav-item">
<NavLink to="/contact.html" className="nav-link" onClick={this.handleNavigate}> <NavLink to="/contact.html" className="nav-link" onClick={this.handleNavigate}>

View File

@ -1,34 +0,0 @@
table {
table-layout: fixed;
width: 60%;
margin-left: 20%;
margin-right: 20%;
border: 1px solid black;
}
table th, td {
border: 1px solid black;
text-align: left;
padding-left: 1%;
}
table tr:nth-child(odd) {
background: #f6f8fa;
}
table tr:nth-child(1) {
background: #fff;
}
#title {
text-align: center;
padding-bottom: 1%;
}
#radiogroup {
padding: 1%;
}
input[type="radio"] {
margin: 0 2px 0 10px;
}

View File

@ -1,25 +1,23 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import './leaderboard.css';
interface Leader { interface Leader {
number_edits: string; number_edits: number;
username: string; username: string;
} }
interface LeaderboardProps { interface LeaderboardProps {}
}
interface LeaderboardState { interface LeaderboardState {
leaders: Leader[]; leaders: Leader[];
fetching: boolean; fetching: boolean;
//We need to track the state of the radio buttons to ensure their current state is shown correctly when the view is (re)rendered //We need to track the state of the radio buttons to ensure their current state is shown correctly when the view is (re)rendered
number_limit: number; number_limit: number;
time_limit: number; time_limit: number;
} }
class LeaderboardPage extends Component<LeaderboardProps, LeaderboardState> { class LeaderboardPage extends Component<LeaderboardProps, LeaderboardState> {
@ -27,14 +25,14 @@ class LeaderboardPage extends Component<LeaderboardProps, LeaderboardState> {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
leaders: [], leaders: [],
fetching: false, fetching: false,
number_limit: 10, number_limit: 10,
time_limit: -1 time_limit: -1
}; };
this.getLeaders = this.getLeaders.bind(this); this.getLeaders = this.getLeaders.bind(this);
this.renderTableData = this.renderTableData.bind(this); this.renderTableData = this.renderTableData.bind(this);
this.handleChange = this.handleChange.bind(this); this.handleChange = this.handleChange.bind(this);
} }
@ -42,29 +40,23 @@ class LeaderboardPage extends Component<LeaderboardProps, LeaderboardState> {
if(e.target.name == 'number_limit'){ if(e.target.name == 'number_limit'){
this.getLeaders(e.target.value, this.state.time_limit); this.getLeaders(e.target.value, this.state.time_limit);
this.setState({number_limit: e.target.value}); this.setState({number_limit: e.target.value});
}else { } else {
this.getLeaders(this.state.number_limit, e.target.value); this.getLeaders(this.state.number_limit, e.target.value);
this.setState({time_limit: e.target.value}); this.setState({time_limit: e.target.value});
} }
} }
componentDidMount() { componentDidMount() {
this.getLeaders(this.state.number_limit, this.state.time_limit); this.getLeaders(this.state.number_limit, this.state.time_limit);
} }
getLeaders(number_limit: number, time_limit: number) {
componentWillUnmount() {}
getLeaders(number_limit, time_limit) {
this.setState({ this.setState({
fetching: true fetching: true
}); });
fetch( fetch(
'/api/leaderboard/leaders?number_limit=' + number_limit + '&time_limit='+time_limit `/api/leaderboard/leaders?number_limit=${number_limit}&time_limit=${time_limit}`
).then( ).then(
(res) => res.json() (res) => res.json()
).then((data) => { ).then((data) => {
@ -95,55 +87,111 @@ class LeaderboardPage extends Component<LeaderboardProps, LeaderboardState> {
renderTableData() { renderTableData() {
return this.state.leaders.map((u, i) => { return this.state.leaders.map((u, i) => {
const username = u.username; const username = u.username;
const number_edits = u.number_edits; const number_edits = u.number_edits;
return ( return (
<tr key={username}> <tr key={username}>
<td>{i+1}</td> <th scope="row">{i+1}</th>
<td>{username}</td> <td>{username}</td>
<td>{number_edits}</td> <td>{number_edits.toLocaleString()}</td>
</tr> </tr>
); );
}); });
} }
render() { render() {
return( return (
<div> <article>
<form id="radiogroup"> <section className="main-col">
<div id="number-radiogroup" > <h1 className="h2">Leaderboard</h1>
<p>Select number of users to be displayed: <br/> <form>
<input type="radio" name="number_limit" value="10" onChange={this.handleChange} checked={10 == this.state.number_limit} />10 <label>Select number of users to be displayed</label>
<input type="radio" name="number_limit" value="100" onChange={this.handleChange} checked={100 == this.state.number_limit} />100 <div className="form-group">
</p> <div className="form-check-inline">
</div> <input
<div id="time-radiogroup" > type="radio"
<p>Select time period: <br/> name="number_limit"
<input type="radio" name="time_limit" value="-1" onChange={this.handleChange} checked={-1 == this.state.time_limit} /> All time id="number_10"
<input type="radio" name="time_limit" value="7" onChange={this.handleChange} checked={7 == this.state.time_limit} /> Last 7 days className="form-check-input"
<input type="radio" name="time_limit" value="30" onChange={this.handleChange} checked={30 == this.state.time_limit} /> Last 30 days value="10"
</p> onChange={this.handleChange}
</div> checked={10 == this.state.number_limit}
</form> />
<h1 id='title'>Leader Board</h1> <label className="form-check-label" htmlFor="number_10">10</label>
<table id='leaderboard'> </div>
<tbody> <div className="form-check-inline">
<tr> <input
<th>Rank</th> type="radio"
<th>Username</th> name="number_limit"
<th>Contributions</th> id="number_100"
</tr> className="form-check-input"
{this.renderTableData()} value="100"
</tbody> onChange={this.handleChange}
</table> checked={100 == this.state.number_limit}
</div> />
); <label className="form-check-label" htmlFor="number_100">100</label>
</div>
</div>
<label>Select time period</label>
<div className="form-group">
<div className="form-check-inline">
<input
type="radio"
name="time_limit"
id="time_all"
className="form-check-input"
value="-1"
onChange={this.handleChange}
checked={-1 == this.state.time_limit}
/>
<label className="form-check-label" htmlFor="time_all">All time</label>
</div>
<div className="form-check-inline">
<input
type="radio"
name="time_limit"
id="time_7"
className="form-check-input"
value="7"
onChange={this.handleChange}
checked={7 == this.state.time_limit}
/>
<label className="form-check-label" htmlFor="time_7">Last 7 days</label>
</div>
<div className="form-check-inline">
<input
type="radio"
name="time_limit"
id="time_30"
className="form-check-input"
value="30"
onChange={this.handleChange}
checked={30 == this.state.time_limit}
/>
<label className="form-check-label" htmlFor="time_30">Last 30 days</label>
</div>
</div>
</form>
<table className="table table-striped">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Username</th>
<th scope="col">Contributions</th>
</tr>
</thead>
<tbody>
{this.renderTableData()}
</tbody>
</table>
</section>
</article>
);
} }
} }
export default LeaderboardPage; export default LeaderboardPage;