fetch leaderboard table data from backend; optimize table style

This commit is contained in:
Songyi Huang 2020-07-19 18:28:39 -07:00
parent 362da09294
commit d7daa1da05
2 changed files with 153 additions and 72 deletions

View File

@ -10,19 +10,6 @@ import ExpandMore from '@material-ui/icons/ExpandMore';
import {makeStyles} from "@material-ui/core/styles"; import {makeStyles} from "@material-ui/core/styles";
import qs from 'query-string'; import qs from 'query-string';
const gameList = [
{game: 'leduc-holdem', dispName: 'Leduc Hold\'em'},
{game: 'doudizhu', dispName: 'Dou Dizhu'},
];
const modelList = [
{model: 'leduc-holdem-random', dispName: 'Leduc Hold\'em Random'},
{model: 'leduc-holdem-cfr', dispName: 'Leduc Hold\'em CFR'},
{model: 'leduc-holdem-rule-v1', dispName: 'Leduc Hold\'em Rule V1'},
{model: 'doudizhu-random', dispName: 'Dou Dizhu Random'},
{model: 'doudizhu-rule-v1', dispName: 'Dou Dizhu Rule V1'}
];
const drawerWidth = 250; const drawerWidth = 250;
const useStyles = makeStyles((theme) => ({ const useStyles = makeStyles((theme) => ({
@ -60,7 +47,7 @@ const useStyles = makeStyles((theme) => ({
active: {} active: {}
})); }));
function MenuBar () { function MenuBar (props) {
const classes = useStyles(); const classes = useStyles();
const [open, setOpen] = React.useState({game: true, agent: true}); const [open, setOpen] = React.useState({game: true, agent: true});
@ -80,7 +67,7 @@ function MenuBar () {
} }
const { type, name } = qs.parse(window.location.search); const { type, name } = qs.parse(window.location.search);
const gameMenu = gameList.map(game => { const gameMenu = props.gameList.map(game => {
return <List component="div" disablePadding key={"game-menu-"+game.game}> return <List component="div" disablePadding key={"game-menu-"+game.game}>
<ListItem button className={classes.nested} onClick={() => {handleGameJump(game.game)}}> <ListItem button className={classes.nested} onClick={() => {handleGameJump(game.game)}}>
<ListItemText primary={game.dispName} className={`${classes.menuLayer2} ${(type === 'game' && name === game.game) ? classes.active : classes.inactive}`} /> <ListItemText primary={game.dispName} className={`${classes.menuLayer2} ${(type === 'game' && name === game.game) ? classes.active : classes.inactive}`} />
@ -88,7 +75,7 @@ function MenuBar () {
</List> </List>
}); });
const agentMenu = modelList.map(model => { const agentMenu = props.modelList.map(model => {
return <List component="div" disablePadding key={"game-menu-"+model.model}> return <List component="div" disablePadding key={"game-menu-"+model.model}>
<ListItem button className={classes.nested} onClick={() => {handleAgentJump(model.model)}}> <ListItem button className={classes.nested} onClick={() => {handleAgentJump(model.model)}}>
<ListItemText primary={model.dispName} className={`${classes.menuLayer2} ${(type === 'agent' && name === model.model) ? classes.active : classes.inactive}`} /> <ListItemText primary={model.dispName} className={`${classes.menuLayer2} ${(type === 'agent' && name === model.model) ? classes.active : classes.inactive}`} />

View File

@ -1,4 +1,5 @@
import React from 'react'; import React, {useEffect} from 'react';
import qs from 'query-string';
import MenuBar from "../components/MenuBar"; import MenuBar from "../components/MenuBar";
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
@ -14,73 +15,97 @@ import Typography from '@material-ui/core/Typography';
import Paper from '@material-ui/core/Paper'; import Paper from '@material-ui/core/Paper';
import { apiUrl } from "../utils/config"; import { apiUrl } from "../utils/config";
import axios from 'axios'; import axios from 'axios';
import TablePagination from "@material-ui/core/TablePagination";
import Breadcrumbs from "@material-ui/core/Breadcrumbs";
import withStyles from "@material-ui/core/styles/withStyles";
const gameList = [
{game: 'leduc-holdem', dispName: 'Leduc Hold\'em'},
{game: 'doudizhu', dispName: 'Dou Dizhu'},
];
const modelList = [
{model: 'leduc-holdem-random', dispName: 'Leduc Hold\'em Random'},
{model: 'leduc-holdem-cfr', dispName: 'Leduc Hold\'em CFR'},
{model: 'leduc-holdem-rule-v1', dispName: 'Leduc Hold\'em Rule V1'},
{model: 'doudizhu-random', dispName: 'Dou Dizhu Random'},
{model: 'doudizhu-rule-v1', dispName: 'Dou Dizhu Rule V1'}
];
function LeaderBoard () { function LeaderBoard () {
console.log(`${apiUrl}/tournament/query_game?name=doudizhu`); const [rows, setRows] = React.useState([]);
axios.get(`${apiUrl}/tournament/query_game?name=doudizhu`)
const { type, name } = qs.parse(window.location.search);
let requestUrl = `${apiUrl}/tournament/`;
if (type === 'game') {
requestUrl += `query_game?name=${name}`
} else if (type === 'agent') {
requestUrl += `query_game?agent0=${name}`
}
console.log(requestUrl);
useEffect(() => {
async function fetchData() {
const res = await axios.get(requestUrl);
console.log('wdnmd', res);
setRows(res.data.map((resRow) => {return createData(resRow);}));
}
fetchData();
}, [requestUrl])
return ( return (
<div> <div>
<MenuBar /> <MenuBar gameList={gameList} modelList={modelList} />
<div style={{marginLeft: '250px'}}> <div style={{marginLeft: '250px'}}>
<div style={{padding: 20}}> <div style={{padding: 20}}>
{EnhancedTable()} <EnhancedTable tableRows={rows} routeInfo={{type, name}}/>
</div> </div>
</div> </div>
</div> </div>
) )
} }
function createData(name, calories, fat, carbs, protein) { function createData(resData) {
return { name, calories, fat, carbs, protein }; return {
id: resData.pk,
game: resData.fields.name,
agent0: resData.fields.agent0,
agent1: resData.fields.agent1,
win: resData.fields.win ? 'Win' : 'Lose',
payoff: resData.fields.payoff
};
} }
const rows = [ const headCells = [
createData('Cupcake', 305, 3.7, 67, 4.3), { id: 'id', numeric: false, disablePadding: false, label: 'ID' },
createData('Donut', 452, 25.0, 51, 4.9), { id: 'game', numeric: false, disablePadding: false, label: 'Game' },
createData('Eclair', 262, 16.0, 24, 6.0), { id: 'agent0', numeric: false, disablePadding: false, label: 'Agent 0' },
createData('Frozen yoghurt', 159, 6.0, 24, 4.0), { id: 'agent1', numeric: false, disablePadding: false, label: 'Agent 1' },
createData('Gingerbread', 356, 16.0, 49, 3.9), { id: 'win', numeric: false, disablePadding: false, label: 'Result' },
createData('Honeycomb', 408, 3.2, 87, 6.5), { id: 'payoff', numeric: false, disablePadding: false, label: 'Payoff' }
createData('Ice cream sandwich', 237, 9.0, 37, 4.3),
createData('Jelly Bean', 375, 0.0, 94, 0.0),
createData('KitKat', 518, 26.0, 65, 7.0),
createData('Lollipop', 392, 0.2, 98, 0.0),
createData('Marshmallow', 318, 0, 81, 2.0),
createData('Nougat', 360, 19.0, 9, 37.0),
createData('Oreo1', 437, 18.0, 63, 4.0),
createData('Oreo2', 437, 18.0, 63, 4.0),
createData('Oreo3', 437, 18.0, 63, 4.0),
createData('Oreo4', 437, 18.0, 63, 4.0),
createData('Oreo5', 437, 18.0, 63, 4.0),
createData('Oreo6', 437, 18.0, 63, 4.0),
createData('Oreo7', 437, 18.0, 63, 4.0),
createData('Oreo8', 437, 18.0, 63, 4.0),
createData('Oreo9', 437, 18.0, 63, 4.0),
createData('Oreo10', 437, 18.0, 63, 4.0),
createData('Oreo11', 437, 18.0, 63, 4.0)
]; ];
const headCells = [ const StyledTableCell = withStyles((theme) => ({
{ id: 'name', numeric: false, disablePadding: false, label: 'Dessert (100g serving)' }, head: {
{ id: 'calories', numeric: true, disablePadding: false, label: 'Calories' }, backgroundColor: '#373538',
{ id: 'fat', numeric: true, disablePadding: false, label: 'Fat (g)' }, color: theme.palette.common.white,
{ id: 'carbs', numeric: true, disablePadding: false, label: 'Carbs (g)' }, fontWeight: 600,
{ id: 'protein', numeric: true, disablePadding: false, label: 'Protein (g)' }, fontSize: 15
]; }
}))(TableCell);
function EnhancedTableHead() { function EnhancedTableHead() {
return ( return (
<TableHead> <TableHead>
<TableRow> <TableRow>
{headCells.map((headCell) => ( {headCells.map((headCell) => (
<TableCell <StyledTableCell
key={headCell.id} key={headCell.id}
align={headCell.numeric ? 'right' : 'left'} align={headCell.numeric ? 'right' : 'left'}
padding={headCell.disablePadding ? 'none' : 'default'} padding={headCell.disablePadding ? 'none' : 'default'}
> >
{headCell.label} {headCell.label}
</TableCell> </StyledTableCell>
))} ))}
</TableRow> </TableRow>
</TableHead> </TableHead>
@ -96,6 +121,7 @@ const useToolbarStyles = makeStyles((theme) => ({
root: { root: {
paddingLeft: theme.spacing(2), paddingLeft: theme.spacing(2),
paddingRight: theme.spacing(1), paddingRight: theme.spacing(1),
minHeight: 52,
}, },
highlight: highlight:
theme.palette.type === 'light' theme.palette.type === 'light'
@ -110,22 +136,49 @@ const useToolbarStyles = makeStyles((theme) => ({
title: { title: {
flex: '1 1 100%', flex: '1 1 100%',
}, },
capitalize: {
textTransform: 'capitalize'
}
})); }));
const EnhancedTableToolbar = () => { const EnhancedTableToolbar = (props) => {
const { routeInfo } = props;
const classes = useToolbarStyles(); const classes = useToolbarStyles();
let name = '';
if (routeInfo.type === 'game') {
const foundItem = gameList.find(game => {
return game.game === routeInfo.name;
});
name = foundItem.dispName;
} else if (routeInfo.type === 'agent') {
const foundItem = modelList.find(model => {
return model.model === routeInfo.name;
});
name = foundItem.dispName;
}
return ( return (
<Toolbar <Toolbar
className={classes.root} className={classes.root}
> >
<Typography className={classes.title} variant="h6" id="tableTitle" component="div"> <Breadcrumbs aria-label="breadcrumb">
Nutrition <Typography color="inherit">
</Typography> LeaderBoards
</Typography>
<Typography color="inherit" className={classes.capitalize}>
{routeInfo.type}
</Typography>
<Typography color="textPrimary">{name}</Typography>
</Breadcrumbs>
</Toolbar> </Toolbar>
); );
}; };
EnhancedTableToolbar.propTypes = {
routeInfo: PropTypes.object.isRequired
}
const useStyles = makeStyles((theme) => ({ const useStyles = makeStyles((theme) => ({
root: { root: {
width: '100%', width: '100%',
@ -135,7 +188,7 @@ const useStyles = makeStyles((theme) => ({
marginBottom: theme.spacing(2), marginBottom: theme.spacing(2),
}, },
table: { table: {
minWidth: 750, minWidth: 900,
}, },
visuallyHidden: { visuallyHidden: {
border: 0, border: 0,
@ -150,13 +203,33 @@ const useStyles = makeStyles((theme) => ({
}, },
})); }));
function EnhancedTable() { const EnhancedTable = (props) => {
const initRowsPerPage = 10;
const { tableRows, routeInfo } = props;
const classes = useStyles(); const classes = useStyles();
const [rowsPerPage, setRowsPerPage] = React.useState(initRowsPerPage);
const [page, setPage] = React.useState(0);
useEffect(() => {
setRowsPerPage(initRowsPerPage);
setPage(0);
}, [tableRows]);
const handleChangePage = (event, newPage) => {
setPage(newPage);
};
const handleChangeRowsPerPage = (event) => {
setRowsPerPage(parseInt(event.target.value, 10));
setPage(0);
};
const emptyRows = rowsPerPage - Math.min(rowsPerPage, tableRows.length - page * rowsPerPage);
return ( return (
<div className={classes.root}> <div className={classes.root}>
<Paper className={classes.paper}> <Paper className={classes.paper}>
<EnhancedTableToolbar /> <EnhancedTableToolbar routeInfo={routeInfo}/>
<TableContainer> <TableContainer>
<Table <Table
className={classes.table} className={classes.table}
@ -166,35 +239,56 @@ function EnhancedTable() {
> >
<EnhancedTableHead <EnhancedTableHead
classes={classes} classes={classes}
rowCount={rows.length} rowCount={tableRows.length}
/> />
<TableBody> <TableBody>
{rows.map((row, index) => { {tableRows
.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
.map((row, index) => {
const labelId = `enhanced-table-checkbox-${index}`; const labelId = `enhanced-table-checkbox-${index}`;
return ( return (
<TableRow <TableRow
hover hover
role="checkbox" role="checkbox"
tabIndex={-1} tabIndex={-1}
key={row.name} key={row.id}
> >
<TableCell component="th" id={labelId} scope="row"> <TableCell component="th" id={labelId} scope="row">
{row.name} {row.id}
</TableCell> </TableCell>
<TableCell align="right">{row.calories}</TableCell> <TableCell>{row.game}</TableCell>
<TableCell align="right">{row.fat}</TableCell> <TableCell>{row.agent0}</TableCell>
<TableCell align="right">{row.carbs}</TableCell> <TableCell>{row.agent1}</TableCell>
<TableCell align="right">{row.protein}</TableCell> <TableCell>{row.win}</TableCell>
<TableCell>{row.payoff}</TableCell>
</TableRow> </TableRow>
); );
})} })}
{emptyRows > 0 && (
<TableRow style={{ height: 53 * emptyRows }}>
<TableCell colSpan={6} />
</TableRow>
)}
</TableBody> </TableBody>
</Table> </Table>
</TableContainer> </TableContainer>
<TablePagination
rowsPerPageOptions={[10, 50, 100]}
component="div"
count={tableRows.length}
rowsPerPage={rowsPerPage}
page={page}
onChangePage={handleChangePage}
onChangeRowsPerPage={handleChangeRowsPerPage}
/>
</Paper> </Paper>
</div> </div>
); );
} }
EnhancedTable.propTypes = {
routeInfo: PropTypes.object.isRequired,
tableRows: PropTypes.array.isRequired
}
export default LeaderBoard; export default LeaderBoard;