refactor Table, Leaderboard and MyEntries

This commit is contained in:
mattyl006 2022-11-04 13:32:28 +01:00
parent b8dcd62da9
commit f02c071fd7
6 changed files with 147 additions and 206 deletions

View File

@ -2,9 +2,7 @@ import React from 'react';
import {FlexColumn, FlexRow, Grid, Svg} from '../../utils/containers'; import {FlexColumn, FlexRow, Grid, Svg} from '../../utils/containers';
import Media from 'react-media'; import Media from 'react-media';
import theme from '../../utils/theme'; import theme from '../../utils/theme';
import Loading from './Loading'; import {ELEMENTS_PER_PAGE} from '../../utils/globals';
import PropsTypes from 'prop-types';
import {ELEMENTS_PER_PAGE, IS_MOBILE} from '../../utils/globals';
import {Body, Medium} from '../../utils/fonts'; import {Body, Medium} from '../../utils/fonts';
import arrow from '../../assets/arrow.svg'; import arrow from '../../assets/arrow.svg';
import styled from 'styled-components'; import styled from 'styled-components';
@ -32,68 +30,71 @@ const Table = (props) => {
}; };
const desktopRender = () => { const desktopRender = () => {
const n = (props.pageNr - 1) * ELEMENTS_PER_PAGE; const n = (props.pageNr - 1) * (ELEMENTS_PER_PAGE * 2);
let submissionToMap = props.submissions.slice(n, n + ELEMENTS_PER_PAGE); console.log(props.elements);
let elementsToMap = props.elements.slice(n, n + (ELEMENTS_PER_PAGE * 2));
return ( return (
<FlexColumn as='table' margin='32px 0 72px 0' width='100%'> <FlexColumn as='table' margin='32px 0 72px 0' width='100%'>
<FlexColumn as='tbody' width='100%'> <FlexColumn as='tbody' width='100%'>
{submissionToMap.map(({ <Grid
submitter, gridGap='20px' position='relative' width='100%' padding='4px' margin='0 0 6px 0'
when, gridTemplateColumns={props.gridTemplateColumns}>
evaluations, {props.headerElements.map((elem, i) => {
times return (
}, index) => { <FlexRow key={`leaderboard-header-${i}`} alignmentX='flex-start' as='td'
onClick={() => {
props.sortByUpdate(elem);
forceUpdate();
}}>
<Medium textAlign={elem === 'submitter' ? 'left' : 'right'}
width={elem === 'when' ? '100%' : 'auto'} padding='0 6px 0 0'
minWidth={elem === 'result' ? '72px' : 'none'}>
{elem}
</Medium>
{elem !== '#' ?
<>
<Svg width='8px' rotate='180deg' src={arrow}
backgroundColor={theme.colors.dark} margin='2px 0 0 0'/>
<Svg width='8px' src={arrow} backgroundColor={theme.colors.dark}
margin='0 0 2px 0'/>
</> : ''}
</FlexRow>
);
})}
<Line height='2px' top='32px' shadow={theme.shadow}/>
</Grid>
{elementsToMap.map((elem, index) => {
return ( return (
<Grid as='tr' key={`leaderboard-row-${index}`} <Grid as='tr' key={`leaderboard-row-${index}`}
backgroundColor={index % 2 === 1 ? theme.colors.dark01 : 'transparent'} backgroundColor={index % 2 === 1 ? theme.colors.dark01 : 'transparent'}
gridTemplateColumns={!IS_MOBILE() ? '1fr 3fr ' + '2fr '.repeat(evaluations.length) + '1fr 2fr' : '1fr 3fr 1fr 1fr 2fr'} gridTemplateColumns={props.gridTemplateColumns}
gridGap='20px' position='relative' width='100%' padding='4px'> gridGap='20px' position='relative' width='100%' padding='4px'>
{index === 0 ? props.headerElements.map((elem, i) => { {props.staticColumnElements.map((elemName, i) => {
return ( return (
<FlexRow key={`leaderboard-header-${i}`} alignmentX='flex-start' as='td' <Body key={`leaderboard-static-elemName-${i}-${elem[elemName.name]}`} as='td'
onClick={() => { order={elemName.order} textAlign={elemName.align}>
props.sortByUpdate(elem); {elemName.format ? elemName.format(elem[elemName.name]) : elem[elemName.name]}
forceUpdate();
}}>
<Medium textAlign={elem === 'submitter' ? 'left' : 'right'}
width={elem === 'when' ? '100%' : 'auto'} padding='0 6px 0 0'
minWidth={elem === 'result' ? '72px' : 'none'} fontSize='18px'>
{elem}
</Medium>
{elem !== '#' ?
<>
<Svg width='8px' rotate='180deg' src={arrow}
backgroundColor={theme.colors.dark} margin='2px 0 0 0'/>
<Svg width='8px' src={arrow} backgroundColor={theme.colors.dark}
margin='0 0 2px 0'/>
</> : ''}
</FlexRow>
);
}) : ''}
{index === 0 ? <Line as='td' height='2px' top='38px' shadow={theme.shadow}/> : ''}
<Body as='td'>
{index + n}
</Body>
<Body as='td'>
{submitter ? submitter : '[anonymous]'}
</Body>
{!IS_MOBILE() ? evaluations.map((metric, i) => {
return (
<Body key={`metric-result-${i}`} as='td' textAlign='left' minWidth='72px'>
{metric.score.slice(0, 7)}
</Body> </Body>
); );
}) : <Body as='td' textAlign='left' minWidth='72px'> })}
{evaluations[0] ? evaluations[0].score.slice(0, 7) : 'xxx'} {props.iterableColumnElement ? elem[props.iterableColumnElement.name].map((iterableElem, i) => {
</Body>} return (
<Body as='td' padding='0 2px 0 0' textAlign='left'> <Body key={`metric-result-${i}`} as='td'
{times ? times : 1} order={props.iterableColumnElement.order}
</Body> textAlign={props.iterableColumnElement.align} minWidth='72px'>
<Body as='td' textAlign='right'> {props.iterableColumnElement.format ?
{when ? `${when.slice(11, 16)} ${when.slice(0, 10)}` props.iterableColumnElement.format(iterableElem) : iterableElem}
: 'xxx'} </Body>
</Body> );
{index !== 0 ? <Line top='0' as='td'/> : ''} }) : ''}
{props.myEntries ? props.myEntries.tests.map((test, i) => {
return (
<Body key={`eval-result-${i}`} width='80px' as='td' textAlign='left'
minWidth='72px' order={2}>
{props.renderEvalResult(elem.evaluations, test)}
</Body>
);
}) : ''}
</Grid> </Grid>
); );
})} })}
@ -104,29 +105,14 @@ const Table = (props) => {
return ( return (
<> <>
<Loading visible={props.loading}/>
<Media query={theme.mobile}> <Media query={theme.mobile}>
{!props.loading ? mobileRender() : ''} {mobileRender()}
</Media> </Media>
<Media query={theme.desktop}> <Media query={theme.desktop}>
{!props.loading ? desktopRender() : ''} {desktopRender()}
</Media> </Media>
</> </>
); );
}; };
Table.propTypes = {
challengeName: PropsTypes.string,
loading: PropsTypes.bool,
renderElements: PropsTypes.func,
headerElements: PropsTypes.arrayOf(PropsTypes.string),
};
Table.defaultProps = {
challengeName: '',
loading: true,
renderElements: null,
headerElements: [],
};
export default Table; export default Table;

View File

@ -7,9 +7,10 @@ import Table from '../../elements/Table';
import PropsTypes from 'prop-types'; import PropsTypes from 'prop-types';
import getChallengeLeaderboard from '../../../api/getChallengeLeaderboard'; import getChallengeLeaderboard from '../../../api/getChallengeLeaderboard';
import _tableSearchQueryHandler from './_tableSearchQueryHandler'; import _tableSearchQueryHandler from './_tableSearchQueryHandler';
import {CALC_PAGES} from '../../../utils/globals'; import {CALC_PAGES, RENDER_WHEN} from '../../../utils/globals';
import Search from '../../elements/Search'; import Search from '../../elements/Search';
import Pager from '../../elements/Pager'; import Pager from '../../elements/Pager';
import Loading from '../../elements/Loading';
const Leaderboard = (props) => { const Leaderboard = (props) => {
const [entriesFromApi, setEntriesFromApi] = React.useState([]); const [entriesFromApi, setEntriesFromApi] = React.useState([]);
@ -82,6 +83,7 @@ const Leaderboard = (props) => {
}; };
const sortByUpdate = (elem) => { const sortByUpdate = (elem) => {
let metricIndex = 0;
let newEntries = entries; let newEntries = entries;
console.log(elem); console.log(elem);
switch (elem) { switch (elem) {
@ -113,8 +115,7 @@ const Leaderboard = (props) => {
} }
break; break;
default: default:
// eslint-disable-next-line no-case-declarations metricIndex = getMetricIndex(elem);
let metricIndex = getMetricIndex(elem);
if (scoreSorted) { if (scoreSorted) {
newEntries = newEntries.sort((a, b) => b.evaluations[metricIndex].score - a.evaluations[metricIndex].score); newEntries = newEntries.sort((a, b) => b.evaluations[metricIndex].score - a.evaluations[metricIndex].score);
setScoreSorted(false); setScoreSorted(false);
@ -145,19 +146,43 @@ const Leaderboard = (props) => {
); );
}; };
const evaluationsFormat = (evaluate) => {
return evaluate.score.slice(0, 7);
};
const desktopRender = () => { const desktopRender = () => {
return ( return (
<FlexColumn padding='24px' as='section' width='100%' maxWidth='1200px'> <FlexColumn padding='24px' as='section' width='100%' maxWidth='1200px'>
<H2 as='h2' margin='0 0 32px 0'> <H2 as='h2' margin='0 0 32px 0'>
Leaderboard Leaderboard
</H2> </H2>
<Search searchQueryHandler={tableSearchQueryHandler}/> {!loading ?
<Table challengeName={props.challengeName} loading={loading} headerElements={getLeaderboardHeader()} <>
pageNr={pageNr} submissions={entries} sortByUpdate={sortByUpdate}/> <Search searchQueryHandler={tableSearchQueryHandler}/>
<Pager pageNr={pageNr} width='72px' borderRadius='64px' <Table challengeName={props.challengeName} headerElements={getLeaderboardHeader()}
pages={CALC_PAGES(entries ? entries : [])} gridTemplateColumns={entries[0] ? '1fr 3fr ' + '2fr '.repeat(entries[0].evaluations.length) + '1fr 2fr' : ''}
nextPage={nextPage} previousPage={previousPage} staticColumnElements={
number={`${pageNr} / ${CALC_PAGES(entries ? entries : [])}`}/> [
{name: 'id', format: null, order: 1, align: 'left'},
{name: 'submitter', format: null, order: 2, align: 'left'},
{name: 'times', format: null, order: 4, align: 'left'},
{name: 'when', format: RENDER_WHEN, order: 5, align: 'right'}
]
}
metrics={getPossibleMetrics()}
iterableColumnElement={{
name: 'evaluations',
format: evaluationsFormat,
order: 3,
align: 'left'
}}
pageNr={pageNr} elements={entries} sortByUpdate={sortByUpdate}/>
<Pager pageNr={pageNr} width='72px' borderRadius='64px'
pages={CALC_PAGES(entries, 2)}
nextPage={nextPage} previousPage={previousPage}
number={`${pageNr} / ${CALC_PAGES(entries, 2)}`}/>
</>
: <Loading/>}
</FlexColumn> </FlexColumn>
); );
}; };

View File

@ -1,14 +1,14 @@
import React from 'react'; import React from 'react';
import {FlexColumn} from '../../../utils/containers'; import {FlexColumn} from '../../utils/containers';
import {H2} from '../../../utils/fonts'; import {H2} from '../../utils/fonts';
import getMyEntries from '../../../api/getMyEntries'; import getMyEntries from '../../api/getMyEntries';
import Pager from '../../elements/Pager'; import Pager from '../elements/Pager';
import {CALC_PAGES} from '../../../utils/globals'; import {CALC_PAGES, RENDER_WHEN} from '../../utils/globals';
import Media from 'react-media'; import Media from 'react-media';
import theme from '../../../utils/theme'; import theme from '../../utils/theme';
import _tableSearchQueryHandler from '../Leaderboard/_tableSearchQueryHandler'; import _tableSearchQueryHandler from './Leaderboard/_tableSearchQueryHandler';
import Loading from '../../elements/Loading'; import Loading from '../elements/Loading';
import _renderMySubmissions from './_renderMySubmissions'; import Table from '../elements/Table';
const MyEntries = (props) => { const MyEntries = (props) => {
const [myEntriesFromAPI, setMyEntriesFromAPI] = React.useState({}); const [myEntriesFromAPI, setMyEntriesFromAPI] = React.useState({});
@ -23,11 +23,6 @@ const MyEntries = (props) => {
challengesRequest(); challengesRequest();
}, []); }, []);
const renderSubmissions = (gridGap, headerElements) => {
return _renderMySubmissions(pageNr, myEntries
? myEntries : [], gridGap, metricChoose, sortBy, headerElements);
};
const tableSearchQueryHandler = (event) => { const tableSearchQueryHandler = (event) => {
_tableSearchQueryHandler(event, myEntriesFromAPI, setPageNr, setMyEntries); _tableSearchQueryHandler(event, myEntriesFromAPI, setPageNr, setMyEntries);
}; };
@ -67,18 +62,28 @@ const MyEntries = (props) => {
}; };
const challengesRequest = () => { const challengesRequest = () => {
getMyEntries(props.challengeName, setMyEntriesFromAPI); getMyEntries(props.challengeName, setMyEntriesFromAPI, setLoading);
getMyEntries(props.challengeName, setMyEntries, setLoading); getMyEntries(props.challengeName, setMyEntries, setLoading);
}; };
const sortByHandler = (value) => {
setSortBy(value);
};
const mobileRender = () => { const mobileRender = () => {
} }
const renderEvalResult = (evaluations, test) => {
for (let myEval of evaluations) {
if (myEval.test.name === test.name && myEval.test.metric === test.metric) {
return myEval.score.slice(0, 7);
}
}
return 'xxx';
};
const sortByUpdate = (elem) => {
let newEntries = myEntries;
return myEntries;
};
const desktopRender = () => { const desktopRender = () => {
if (loading) { if (loading) {
return ( return (
@ -90,16 +95,27 @@ const MyEntries = (props) => {
<H2 as='h2' margin='0 0 32px 0'> <H2 as='h2' margin='0 0 32px 0'>
My entries My entries
</H2> </H2>
<FlexColumn as='table' margin='32px 0 72px 0' width='100%'> {myEntries.submissions ?
{renderSubmissions('32px', getMyEntriesHeader())} <>
</FlexColumn> <Table challengeName={props.challengeName} loading={loading}
{/*<Table challengeName={props.challengeName} loading={loading}*/} headerElements={getMyEntriesHeader()}
{/* renderElements={renderSubmissions}*/} gridTemplateColumns={'1fr ' + '4fr '.repeat(getMyEntriesHeader().length - 1)}
{/* headerElements={getMyEntriesHeader()}/>*/} staticColumnElements={
<Pager pageNr={pageNr} width='72px' borderRadius='64px' [
pages={CALC_PAGES(myEntries.submissions ? myEntries.submissions : [])} {name: 'id', format: null, order: 1, align: 'left'},
nextPage={nextPage} previousPage={previousPage} {name: 'when', format: RENDER_WHEN, order: 3, align: 'right'}
number={`${pageNr} / ${CALC_PAGES(myEntries.submissions ? myEntries.submissions : [])}`}/> ]
}
metrics={getPossibleMetrics()} myEntries={myEntries}
renderEvalResult={renderEvalResult}
pageNr={pageNr} elements={myEntries.submissions}
sortByUpdate={sortByUpdate}/>
<Pager pageNr={pageNr} width='72px' borderRadius='64px'
pages={CALC_PAGES(myEntries.submissions, 2)}
nextPage={nextPage} previousPage={previousPage}
number={`${pageNr} / ${CALC_PAGES(myEntries.submissions, 2)}`}/>
</>
: <Loading/>}
</FlexColumn> </FlexColumn>
); );
} }

View File

@ -1,91 +0,0 @@
import {ELEMENTS_PER_PAGE, IS_MOBILE} from '../../../utils/globals';
import {FlexColumn, FlexRow, Grid, Svg} from '../../../utils/containers';
import {Body, Medium} from '../../../utils/fonts';
import styled from 'styled-components';
import React from 'react';
import theme from '../../../utils/theme';
import arrow from '../../../assets/arrow.svg';
const Line = styled(FlexRow)`
position: absolute;
top: ${({top}) => top ? top : 'auto'};
bottom: ${({bottom}) => bottom ? bottom : 'auto'};
left: 0;
width: 100%;
background-color: ${({theme}) => theme.colors.dark04};
height: ${({height}) => height ? height : '1px'};
`;
const renderEvalResult = (evaluations, test) => {
for (let myEval of evaluations) {
if (myEval.test.name === test.name && myEval.test.metric === test.metric) {
return myEval.score.slice(0, 7);
}
}
return 'xxx';
};
const _renderMySubmissions = (pageNr, myEntries, gridGap, metricChoose, sortBy, headerElements) => {
const n = (pageNr - 1) * ELEMENTS_PER_PAGE;
if (myEntries) {
// submissions = sortBySwitch(submissions, metricChoose, sortBy);
let submissions = myEntries.submissions.slice(n, n + ELEMENTS_PER_PAGE);
return (
<FlexColumn as='tbody' width='100%'>
{submissions.map(({
submitter,
when,
evaluations,
times,
description
}, index) => {
return (
<Grid as='tr' key={`leaderboard-row-${index}`}
backgroundColor={index % 2 === 1 ? theme.colors.dark01 : 'transparent'}
gridTemplateColumns={!IS_MOBILE() ? '1fr ' + '4fr '.repeat(headerElements.length - 1) : '1fr 3fr 1fr 1fr 2fr'}
gridGap='20px' position='relative' width='100%' padding='4px'>
{index === 0 ? headerElements.map((elem, i) => {
return (
<FlexRow alignmentX='flex-start'>
<Medium key={`leaderboard-header-${i}`} fontSize='16px' as='td'
textAlign={elem === 'when' ? 'right' : 'left'}
width={elem === 'when' ? '100%' : 'auto'} padding='0 6px 0 0'>
{elem}
</Medium>
{elem !== '#' ?
<>
<Svg width='8px' rotate='180deg' src={arrow}
backgroundColor={theme.colors.dark} margin='2px 0 0 0'/>
<Svg width='8px' src={arrow} backgroundColor={theme.colors.dark}
margin='0 0 2px 0'/>
</> : ''}
</FlexRow>
);
}) : ''}
{index === 0 ? <Line height='2px' top='50%' shadow={theme.shadow}/> : ''}
<Body as='td'>
{index + n + 1}
</Body>
{myEntries.tests.map((test, i) => {
return (
<Body key={`eval-result-${i}`} width='80px' as='td' textAlign='left'
minWidth='72px'>
{renderEvalResult(evaluations, test)}
</Body>
);
})}
<Body as='td' textAlign='right'>
{when ? `${when.slice(11, 16)} ${when.slice(0, 10)}`
: 'xxx'}
</Body>
{index !== 0 ? <Line as='td'/> : ''}
</Grid>
);
})}
</FlexColumn>
);
}
};
export default _renderMySubmissions;

View File

@ -7,7 +7,7 @@ import MobileChallengeMenu from '../components/elements/MobileChallengeMenu';
import Leaderboard from '../components/sections/Leaderboard/Leaderboard'; import Leaderboard from '../components/sections/Leaderboard/Leaderboard';
import Readme from '../components/sections/Readme'; import Readme from '../components/sections/Readme';
import HowTo from '../components/sections/HowTo'; import HowTo from '../components/sections/HowTo';
import MyEntries from '../components/sections/MyEntries/MyEntries'; import MyEntries from '../components/sections/MyEntries';
import Submit from '../components/sections/Submit'; import Submit from '../components/sections/Submit';
import Media from 'react-media'; import Media from 'react-media';
import DesktopChallengeMenu from '../components/elements/DesktopChallengeMenu'; import DesktopChallengeMenu from '../components/elements/DesktopChallengeMenu';

View File

@ -46,9 +46,9 @@ const RENDER_ICO = (type) => {
} }
}; };
const CALC_PAGES = (objects) => { const CALC_PAGES = (objects, n = 1) => {
if (objects.length === 0) return 1; if (objects.length === 0) return 1;
return Math.ceil(objects.length / ELEMENTS_PER_PAGE); return Math.ceil(objects.length / (ELEMENTS_PER_PAGE * n));
}; };
const RENDER_DEADLINE_TIME = (time) => { const RENDER_DEADLINE_TIME = (time) => {
@ -60,6 +60,10 @@ const RENDER_DEADLINE_TIME = (time) => {
return ''; return '';
}; };
const RENDER_WHEN = (when) => {
return `${when.slice(0, 10)} ${when.slice(11, 16)}`;
};
const IS_MOBILE = () => { const IS_MOBILE = () => {
return document.body.clientWidth <= 768; return document.body.clientWidth <= 768;
}; };
@ -75,5 +79,6 @@ export {
RENDER_ICO, RENDER_ICO,
CALC_PAGES, CALC_PAGES,
RENDER_DEADLINE_TIME, RENDER_DEADLINE_TIME,
IS_MOBILE IS_MOBILE,
RENDER_WHEN
}; };