sort method correction on desktop

This commit is contained in:
mattyl006 2022-11-02 16:30:44 +01:00
parent 5bc8831824
commit a653176baf
4 changed files with 144 additions and 163 deletions

View File

@ -5,8 +5,11 @@ const getChallengeLeaderboard = (setDataState, challengeName, setLoading) => {
.then(response => response.json()) .then(response => response.json())
.then(data => { .then(data => {
setDataState(data.entries); setDataState(data.entries);
if (setLoading) if (setLoading) {
setTimeout(() => {
setLoading(false); setLoading(false);
}, 3000);
}
}); });
}; };

View File

@ -1,23 +1,103 @@
import React from 'react'; import React from 'react';
import {FlexColumn} 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 Loading from './Loading';
import PropsTypes from 'prop-types'; import PropsTypes from 'prop-types';
import {ELEMENTS_PER_PAGE, IS_MOBILE} from '../../utils/globals';
import {Body, Medium} from '../../utils/fonts';
import arrow from '../../assets/arrow.svg';
import styled from 'styled-components';
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 Table = (props) => { const Table = (props) => {
const [, updateState] = React.useState();
const forceUpdate = React.useCallback(() => updateState({}), []);
const mobileRender = () => { const mobileRender = () => {
return ( return (
<FlexColumn as='table' margin='20px 0 32px 0' minHeight='380px'> <FlexColumn as='table' margin='20px 0 32px 0' minHeight='380px'>
{props.renderElements('10px', props.headerElements)} {/*{props.renderElements('10px', props.headerElements)}*/}
</FlexColumn> </FlexColumn>
); );
}; };
const desktopRender = () => { const desktopRender = () => {
const n = (props.pageNr - 1) * ELEMENTS_PER_PAGE;
let submissionToMap = props.submissions.slice(n, n + ELEMENTS_PER_PAGE);
return ( return (
<FlexColumn as='table' margin='32px 0 72px 0' width='100%'> <FlexColumn as='table' margin='32px 0 72px 0' width='100%'>
{props.renderElements('32px', props.headerElements)} <FlexColumn as='tbody' width='100%'>
{submissionToMap.map(({
submitter,
when,
evaluations,
times
}, index) => {
return (
<Grid as='tr' key={`leaderboard-row-${index}`}
backgroundColor={index % 2 === 1 ? theme.colors.dark01 : 'transparent'}
gridTemplateColumns={!IS_MOBILE() ? '1fr 3fr ' + '2fr '.repeat(evaluations.length) + '1fr 2fr' : '1fr 3fr 1fr 1fr 2fr'}
gridGap='20px' position='relative' width='100%' padding='4px'>
{index === 0 ? props.headerElements.map((elem, i) => {
return (
<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'} 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 as='td' textAlign='left' minWidth='72px'>
{evaluations[0] ? evaluations[0].score.slice(0, 7) : 'xxx'}
</Body>}
<Body as='td' padding='0 2px 0 0' textAlign='left'>
{times ? times : 1}
</Body>
<Body as='td' textAlign='right'>
{when ? `${when.slice(11, 16)} ${when.slice(0, 10)}`
: 'xxx'}
</Body>
{index !== 0 ? <Line top='0' as='td'/> : ''}
</Grid>
);
})}
</FlexColumn>
</FlexColumn> </FlexColumn>
); );
}; };

View File

@ -1,27 +1,25 @@
import React from 'react'; import React from 'react';
import Media from 'react-media'; import Media from 'react-media';
import theme from '../../../utils/theme'; import theme from '../../../utils/theme';
import {FlexColumn, FlexRow} from '../../../utils/containers'; import {FlexColumn} from '../../../utils/containers';
import {H2, H3} from '../../../utils/fonts'; import {H2} from '../../../utils/fonts';
import Table from '../../elements/Table'; 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 _renderSubmissions from './_renderSubmissions';
import _tableSearchQueryHandler from './_tableSearchQueryHandler'; import _tableSearchQueryHandler from './_tableSearchQueryHandler';
import {CALC_PAGES} from '../../../utils/globals'; import {CALC_PAGES} from '../../../utils/globals';
import Search from '../../elements/Search'; import Search from '../../elements/Search';
import Pager from '../../elements/Pager'; import Pager from '../../elements/Pager';
import Filter from '../../elements/Filter';
import FilterBy from '../FilterBy';
import sortOptions from './sortOptions';
const Leaderboard = (props) => { const Leaderboard = (props) => {
const [entriesFromApi, setEntriesFromApi] = React.useState([]); const [entriesFromApi, setEntriesFromApi] = React.useState([]);
const [entries, setEntries] = React.useState([]); const [entries, setEntries] = React.useState([]);
const [pageNr, setPageNr] = React.useState(1); const [pageNr, setPageNr] = React.useState(1);
const [loading, setLoading] = React.useState(true); const [loading, setLoading] = React.useState(true);
const [metricChoose, setMetricChoose] = React.useState(null); const [submitterSorted, setSubmitterSorted] = React.useState(false);
const [sortBy, setSortBy] = React.useState(5); const [entriesSorted, setEntriesSorted] = React.useState(false);
const [whenSorted, setWhenSorted] = React.useState(false);
const [scoreSorted, setScoreSorted] = React.useState(false);
React.useEffect(() => { React.useEffect(() => {
challengeDataRequest(props.challengeName); challengeDataRequest(props.challengeName);
@ -32,21 +30,16 @@ const Leaderboard = (props) => {
getChallengeLeaderboard(setEntries, challengeName, setLoading); getChallengeLeaderboard(setEntries, challengeName, setLoading);
}; };
const getMainMetricIndex = () => { const getMetricIndex = (metricName) => {
let i = 0; let i = 0;
for (let evaluation of entriesFromApi[0].evaluations) { for (let evaluation of entriesFromApi[0].evaluations) {
if (evaluation.test.metric === props.mainMetric) { if (evaluation.test.metric === metricName) {
return i; return i;
} }
i++; i++;
} }
}; };
const renderSubmissions = (gridGap, headerElements) => {
return _renderSubmissions(pageNr, entries
? entries : [], gridGap, (metricChoose ? metricChoose : getMainMetricIndex()), sortBy, headerElements);
};
const tableSearchQueryHandler = (event) => { const tableSearchQueryHandler = (event) => {
_tableSearchQueryHandler(event, entriesFromApi, setPageNr, setEntries); _tableSearchQueryHandler(event, entriesFromApi, setPageNr, setEntries);
}; };
@ -88,12 +81,50 @@ const Leaderboard = (props) => {
return header; return header;
}; };
const metricChooseHandler = (value) => { const sortByUpdate = (elem) => {
setMetricChoose(value); let newEntries = entries;
}; console.log(elem);
switch (elem) {
const sortByHandler = (value) => { case 'submitter':
setSortBy(value); if (submitterSorted) {
newEntries = newEntries.sort((a, b) => (a.submitter > b.submitter) ? 1 : ((b.submitter > a.submitter) ? -1 : 0));
setSubmitterSorted(false);
} else {
newEntries = newEntries.sort((a, b) => (a.submitter < b.submitter) ? 1 : ((b.submitter < a.submitter) ? -1 : 0));
setSubmitterSorted(true);
}
break;
case 'entries':
if (entriesSorted) {
newEntries = newEntries.sort((a, b) => a.times - b.times);
setEntriesSorted(false);
} else {
newEntries = newEntries.sort((a, b) => b.times - a.times);
setEntriesSorted(true);
}
break;
case 'when':
if (whenSorted) {
newEntries = newEntries.sort((a, b) => (a.when > b.when) ? 1 : ((b.when > a.when) ? -1 : 0));
setWhenSorted(false);
} else {
newEntries = newEntries.sort((a, b) => (a.when < b.when) ? 1 : ((b.when < a.when) ? -1 : 0));
setWhenSorted(true);
}
break;
default:
// eslint-disable-next-line no-case-declarations
let metricIndex = getMetricIndex(elem);
if (scoreSorted) {
newEntries = newEntries.sort((a, b) => b.evaluations[metricIndex].score - a.evaluations[metricIndex].score);
setScoreSorted(false);
} else {
newEntries = newEntries.sort((a, b) => a.evaluations[metricIndex].score - b.evaluations[metricIndex].score);
setScoreSorted(true);
}
break;
}
setEntries(newEntries);
}; };
const mobileRender = () => { const mobileRender = () => {
@ -103,22 +134,9 @@ const Leaderboard = (props) => {
Leaderboard Leaderboard
</H2> </H2>
<Search searchQueryHandler={tableSearchQueryHandler}/> <Search searchQueryHandler={tableSearchQueryHandler}/>
{!loading ? <FlexRow width='100%' gap='16px' as='section' margin='16px 0'>
<H3>
Metric:
</H3>
{getPossibleMetrics() ? getPossibleMetrics().map((metric, index) => {
return (
<Filter option={metricChoose} index={index} borderRadius='4px'
key={`metric-${index}`} handler={metricChooseHandler}
id={`metric-${index}`} name={`metric-${index}`}>
{metric}
</Filter>);
}) : ''}
</FlexRow> : ''}
<Table challengeName={props.challengeName} loading={loading} <Table challengeName={props.challengeName} loading={loading}
renderElements={renderSubmissions} headerElements={['#', 'submitter', 'result', 'entries', 'when']}
headerElements={['#', 'submitter', 'result', 'entries', 'when']}/> pageNr={pageNr} submissions={entries} sortByUpdate={sortByUpdate}/>
<Pager pageNr={pageNr} width='48px' borderRadius='64px' <Pager pageNr={pageNr} width='48px' borderRadius='64px'
pages={CALC_PAGES(entries ? entries : [])} pages={CALC_PAGES(entries ? entries : [])}
nextPage={nextPage} previousPage={previousPage} nextPage={nextPage} previousPage={previousPage}
@ -134,12 +152,8 @@ const Leaderboard = (props) => {
Leaderboard Leaderboard
</H2> </H2>
<Search searchQueryHandler={tableSearchQueryHandler}/> <Search searchQueryHandler={tableSearchQueryHandler}/>
<FilterBy header='Sort by' options={sortOptions} gridTemplateColumns='auto auto auto auto' <Table challengeName={props.challengeName} loading={loading} headerElements={getLeaderboardHeader()}
option={sortBy} textAlign='center' margin='32px 0 0 0' pageNr={pageNr} submissions={entries} sortByUpdate={sortByUpdate}/>
alignmentX='center' handler={sortByHandler}/>
<Table challengeName={props.challengeName} loading={loading}
renderElements={renderSubmissions}
headerElements={getLeaderboardHeader()}/>
<Pager pageNr={pageNr} width='72px' borderRadius='64px' <Pager pageNr={pageNr} width='72px' borderRadius='64px'
pages={CALC_PAGES(entries ? entries : [])} pages={CALC_PAGES(entries ? entries : [])}
nextPage={nextPage} previousPage={previousPage} nextPage={nextPage} previousPage={previousPage}

View File

@ -1,116 +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 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 sortBySwitch = (submissions, metricChoose, sortBy) => {
switch (sortBy) {
case 0:
return submissions.sort((a, b) => (a.submitter < b.submitter) ? 1 : ((b.submitter < a.submitter) ? -1 : 0));
case 1:
if (metricChoose)
return submissions.sort((a, b) => a.evaluations[metricChoose].score - b.evaluations[metricChoose].score);
return submissions;
case 2:
return submissions.sort((a, b) => a.times - b.times);
case 3:
return submissions.sort((a, b) => (a.when > b.when) ? 1 : ((b.when > a.when) ? -1 : 0));
case 4:
return submissions.sort((a, b) => (a.submitter > b.submitter) ? 1 : ((b.submitter > a.submitter) ? -1 : 0));
case 5:
if (metricChoose)
return submissions.sort((a, b) => b.evaluations[metricChoose].score - a.evaluations[metricChoose].score);
return submissions;
case 6:
return submissions.sort((a, b) => b.times - a.times);
case 7:
return submissions.sort((a, b) => (a.when < b.when) ? 1 : ((b.when < a.when) ? -1 : 0));
default:
return submissions.sort((a, b) => b.evaluations[metricChoose].score - a.evaluations[metricChoose].score);
}
};
const _renderSubmissions = (pageNr, submissions, gridGap, metricChoose, sortBy, headerElements) => {
const n = (pageNr - 1) * ELEMENTS_PER_PAGE;
if (submissions) {
submissions = sortBySwitch(submissions, metricChoose, sortBy);
submissions = submissions.slice(n, n + ELEMENTS_PER_PAGE);
return (
<FlexColumn as='tbody' width='100%'>
{submissions.map(({
submitter,
when,
evaluations,
times
}, index) => {
return (
<Grid as='tr' key={`leaderboard-row-${index}`}
backgroundColor={index % 2 === 1 ? theme.colors.dark01 : 'transparent'}
gridTemplateColumns={!IS_MOBILE() ? '1fr 3fr ' + '2fr '.repeat(evaluations.length) + '1fr 2fr' : '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}`}
textAlign={elem === 'submitter' ? 'left' : 'right'}
width={elem === 'when' ? '100%' : 'auto'} padding='0 6px 0 0'
minWidth={elem === 'result' ? '72px' : 'none'} fontSize='18px' as='td'>
{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='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 as='td' textAlign='left' minWidth='72px'>
{evaluations[metricChoose] ? evaluations[metricChoose].score.slice(0, 7) : 'xxx'}
</Body>}
<Body as='td' padding='0 2px 0 0' textAlign='left'>
{times ? times : 1}
</Body>
<Body as='td' textAlign='right'>
{when ? `${when.slice(11, 16)} ${when.slice(0, 10)}`
: 'xxx'}
</Body>
{index !== 0 ? <Line top='0' as='td'/> : ''}
</Grid>
);
})}
</FlexColumn>
);
}
};
export default _renderSubmissions;