refactor: AllEntries Table replaced by new table and logic

This commit is contained in:
Mateusz Tylka 2023-06-09 13:57:11 +02:00
parent 098e865e91
commit e5d0e50102
27 changed files with 289 additions and 877 deletions

View File

@ -47,10 +47,6 @@ const RoutingManager = (props) => {
path={`${CHALLENGE_PAGE}/:challengeId/submit`} path={`${CHALLENGE_PAGE}/:challengeId/submit`}
element={<Challenge section={CHALLENGE_SECTIONS.SUBMIT} />} element={<Challenge section={CHALLENGE_SECTIONS.SUBMIT} />}
/> />
<Route
path={`${CHALLENGE_PAGE}/:challengeId/NewTablePageTest`}
element={<Challenge section={7} />}
/>
<Route path={CHALLENGES_PAGE} element={<Challenges />} /> <Route path={CHALLENGES_PAGE} element={<Challenges />} />
<Route <Route
path={POLICY_PRIVACY_PAGE} path={POLICY_PRIVACY_PAGE}

View File

@ -3,8 +3,7 @@ import KeyCloakService from '../services/KeyCloakService';
const getAllEntries = ( const getAllEntries = (
challengeName, challengeName,
setDataOriginalState, setDataStates,
setDataState,
setLoadingState, setLoadingState,
setScoreSorted setScoreSorted
) => { ) => {
@ -13,7 +12,6 @@ const getAllEntries = (
}) })
.then((response) => response.json()) .then((response) => response.json())
.then((data) => { .then((data) => {
if (setDataOriginalState) setDataOriginalState(data);
let item = {}; let item = {};
let result = []; let result = [];
let initSetScoreSorted = []; let initSetScoreSorted = [];
@ -57,7 +55,7 @@ const getAllEntries = (
for (let _ of tests) { for (let _ of tests) {
initSetScoreSorted.push(false); initSetScoreSorted.push(false);
} }
setDataState(result); for (let setDataState of setDataStates) setDataState(result);
if (setScoreSorted) setScoreSorted(initSetScoreSorted); if (setScoreSorted) setScoreSorted(initSetScoreSorted);
if (setLoadingState) setLoadingState(false); if (setLoadingState) setLoadingState(false);
}); });

View File

@ -1,222 +1,43 @@
import React from 'react'; import React from 'react';
import { import pensilIco from '../../../assets/pencil_ico.svg';
Container, import deleteIco from '../../../assets/delete_ico.svg';
FlexColumn,
FlexRow,
Grid,
} from '../../../utils/containers';
import Media from 'react-media';
import theme from '../../../utils/theme';
import { ELEMENTS_PER_PAGE, IS_MOBILE } from '../../../utils/globals';
import { Body, Medium } from '../../../utils/fonts';
import ColumnFilterIcon from '../ColumnFilterIcon';
// import deleteSubmission from '../../api/deleteSubmission';
import TableStyle from './styles/TableStyle'; import TableStyle from './styles/TableStyle';
import TableLine from './styles/TableLine'; import TableHeader from './components/TableHeader';
import MobileTableStyle from './styles/MobileTableStyle'; import TableRowTags from './components/TableRowTags';
import TableRowItems from './components/TableRowItems';
import TableRowButtons from './components/TableRowButtons';
import RowsBackgroundStyle from './styles/RowsBackgroundStyle';
import { FlexRow } from '../../../utils/containers';
const Table = (props) => { const Table = ({ items, orderedKeys, sortByUpdate }) => {
const [, updateState] = React.useState(); const [, updateState] = React.useState();
const forceUpdate = React.useCallback(() => updateState({}), []); const tableUpdate = React.useCallback(() => updateState({}), []);
const [activeIcon, setActiveIcon] = React.useState(null);
const [rotateActiveIcon, setRotateActiveIcon] = React.useState(false);
const metricsRender = (elem) => {
if (!props.iterableColumnElement) return <></>;
if (Array.isArray(elem[props.iterableColumnElement.name]))
elem = elem[props.iterableColumnElement.name];
else {
let newElem = [];
for (let metric of props.possibleMetrics) {
if (Object.hasOwn(elem, props.iterableColumnElement.name)) {
if (elem[props.iterableColumnElement.name][metric] === '_')
newElem.push('N/A');
else newElem.push(elem[props.iterableColumnElement.name][metric]);
} else {
newElem.push('N/A');
}
}
elem = newElem;
}
let indexModificator = 2;
if (props.tableType === 'leaderboard') indexModificator = 4;
if (props.tableType === 'allEntries') indexModificator = 3;
return elem.map((iterableElem, i) => {
return (
<Body
key={`metric-result-${i}`}
as="td"
order={props.iterableColumnElement.order}
textAlign={props.iterableColumnElement.align}
minWidth="88px"
margin="auto 0"
overflowWrap="anywhere"
>
{IS_MOBILE() && (
<Container className="mobile-table-header">
{props.headerElements[indexModificator + i]}
</Container>
)}
{props.iterableColumnElement.format
? props.iterableColumnElement.format(iterableElem)
: iterableElem}
</Body>
);
});
};
const rowRender = (elem) => {
let RowStyle = Body;
if (elem.submitter === props.user) RowStyle = Medium;
return props.staticColumnElements.map((elemName, i) => {
return (
<RowStyle
key={`leaderboard-static-elemName-${i}-${elem[elemName.name]}`}
as="td"
order={elemName.order}
textAlign={elemName.align}
margin="auto 0"
minWidth="88px"
overflowWrap="anywhere"
cursor="pointer"
// onClick={props.myEntries && (() => deleteSubmission(elem.id))}
>
{IS_MOBILE() && (
<Container className="mobile-table-header">
{props.headerElements[i]}
</Container>
)}
{elemName.format
? elemName.format(elem[elemName.name])
: elem[elemName.name]}
</RowStyle>
);
});
};
const desktopRender = () => {
const n = (props.pageNr - 1) * (ELEMENTS_PER_PAGE * 2);
let elementsToMap = props.elements.slice(n, n + ELEMENTS_PER_PAGE * 2);
if (elementsToMap.length > 0) {
return (
<TableStyle as="table" margin="32px 0 72px 0" width="100%">
<FlexColumn as="tbody" width="100%">
<Grid
as="tr"
gridGap="20px"
position="relative"
width="100%"
padding="0 6px"
minHeight="44px"
margin="0 0 6px 0"
gridTemplateColumns={props.gridTemplateColumns}
>
{props.headerElements.map((elem, i) => {
return (
<FlexRow
key={`table-header-${i}`}
alignmentX="flex-start"
as="td"
cursor="pointer"
onClick={() => {
if (activeIcon === i) {
let newRotateActiveIcon = !rotateActiveIcon;
setRotateActiveIcon(newRotateActiveIcon);
} else {
setRotateActiveIcon(false);
}
setActiveIcon(i);
props.sortByUpdate(elem, i);
forceUpdate();
}}
>
<Medium
cursor={elem !== '#' ? 'pointer' : ''}
textAlign={elem === 'when' ? 'right' : 'left'}
width={elem === 'when' ? '100%' : 'auto'}
padding="0 4px 0 0"
overflowWrap="anywhere"
minWidth="72px"
>
{elem.replace('.', ' ')}
</Medium>
{elem !== '#' && (
<ColumnFilterIcon
cursor="pointer"
index={i}
active={activeIcon}
rotateIcon={rotateActiveIcon}
/>
)}
</FlexRow>
);
})}
<TableLine
height="2px"
top="calc(100% + 2px)"
as="td"
shadow={theme.shadow}
/>
</Grid>
{elementsToMap.map((elem, index) => {
return (
<Grid
as="tr"
key={`leaderboard-row-${index}`}
backgroundColor={
index % 2 === 1 ? theme.colors.dark01 : 'transparent'
}
gridTemplateColumns={props.gridTemplateColumns}
gridGap="20px"
position="relative"
width="100%"
padding="4px"
minHeight="48px"
>
{rowRender(elem)}
{props.headerElements ? metricsRender(elem) : ''}
</Grid>
);
})}
</FlexColumn>
</TableStyle>
);
}
return <Medium margin="72px 0">No results ;c</Medium>;
};
const mobileRender = () => {
const n = (props.pageNr - 1) * ELEMENTS_PER_PAGE;
let elementsToMap = props.elements.slice(n, n + ELEMENTS_PER_PAGE);
if (elementsToMap.length > 0) {
return (
<MobileTableStyle
as="table"
staticColumnElements={props.staticColumnElements}
headerElements={props.headerElements}
>
<Container as="tbody">
{elementsToMap.map((elem, index) => {
return (
<Grid as="tr" key={`leaderboard-row-${index}`}>
{rowRender(elem)}
{props.headerElements ? metricsRender(elem) : ''}
</Grid>
);
})}
</Container>
</MobileTableStyle>
);
}
return <Medium margin="72px 0">No results ;c</Medium>;
};
return ( return (
<> <TableStyle>
<Media query={theme.mobile}>{mobileRender()}</Media> <TableHeader
<Media query={theme.desktop}>{desktopRender()}</Media> orderedKeys={orderedKeys}
</> sortByUpdate={sortByUpdate}
tableUpdate={tableUpdate}
/>
{items.map((item, i) => {
return (
<tr key={`table-row-${i}`} className="TableStyle__tr">
<TableRowItems orderedKeys={orderedKeys} item={item} i={i} />
<FlexRow className="TableStyle__row-footer">
<TableRowTags item={item} i={i} />
<TableRowButtons
buttons={[
{ icon: pensilIco, handler: () => console.log('edit') },
{ icon: deleteIco, handler: () => console.log('delete') },
]}
/>
</FlexRow>
<RowsBackgroundStyle i={i} />
</tr>
);
})}
</TableStyle>
); );
}; };

View File

@ -1,19 +1,18 @@
import React from 'react'; import React from 'react';
import SortButtonContainerStyle from '../../styles/SortButtonContainerStyle'; import { FlexRow } from '../../../../../utils/containers';
import ColumnFilterIcon from '../../../../components/generic/ColumnFilterIcon'; import ColumnFilterIcon from '../../../ColumnFilterIcon';
import { FlexRow } from '../../../../utils/containers';
const TableHeader = (props) => { const TableHeader = (props) => {
const [activeIcon, setActiveIcon] = React.useState(null); const [activeIcon, setActiveIcon] = React.useState(null);
const [rotateActiveIcon, setRotateActiveIcon] = React.useState(false); const [rotateActiveIcon, setRotateActiveIcon] = React.useState(false);
return ( return (
<tr className="NewTableStyle__tr-header"> <tr className="TableStyle__tr-header">
{props.orderedKeys.map((keyValue, i) => { {props.orderedKeys.map((keyValue, i) => {
return ( return (
<th <th
key={`table-header-${i}`} key={`table-header-${i}`}
className="NewTableStyle__th" className="TableStyle__th"
onClick={() => { onClick={() => {
if (activeIcon === i) { if (activeIcon === i) {
let newRotateActiveIcon = !rotateActiveIcon; let newRotateActiveIcon = !rotateActiveIcon;
@ -27,17 +26,21 @@ const TableHeader = (props) => {
}} }}
> >
{keyValue} {keyValue}
<SortButtonContainerStyle as="span" column={keyValue}> <FlexRow
as="span"
className="TableStyle__sort-button"
column={keyValue}
>
<ColumnFilterIcon <ColumnFilterIcon
index={i} index={i}
active={activeIcon} active={activeIcon}
rotateIcon={rotateActiveIcon} rotateIcon={rotateActiveIcon}
/> />
</SortButtonContainerStyle> </FlexRow>
</th> </th>
); );
})} })}
<FlexRow className="NewTableStyle__line" as="td" /> <FlexRow className="TableStyle__line" as="td" />
</tr> </tr>
); );
}; };

View File

@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import { FlexRow, Svg } from '../../../../utils/containers'; import { FlexRow, Svg } from '../../../../../utils/containers';
import theme from '../../../../utils/theme'; import theme from '../../../../../utils/theme';
const TableRowButtons = ({ buttons, i }) => { const TableRowButtons = ({ buttons, i }) => {
return ( return (

View File

@ -1,12 +1,12 @@
import React from 'react'; import React from 'react';
import { RENDER_WHEN, RENDER_METRIC_VALUE } from '../../../../utils/globals'; import { RENDER_WHEN, RENDER_METRIC_VALUE } from '../../../../../utils/globals';
const TableRowItems = ({ orderedKeys, item, i }) => { const TableRowItems = ({ orderedKeys, item, i }) => {
return ( return (
<> <>
{orderedKeys.map((keyValue, j) => { {orderedKeys.map((keyValue, j) => {
return ( return (
<td key={`table-item-${i}-${j}`} className="NewTableStyle__td"> <td key={`table-item-${i}-${j}`} className="TableStyle__td">
{keyValue === 'when' {keyValue === 'when'
? RENDER_WHEN(item[keyValue]) ? RENDER_WHEN(item[keyValue])
: RENDER_METRIC_VALUE(item[keyValue])} : RENDER_METRIC_VALUE(item[keyValue])}

View File

@ -1,9 +1,9 @@
import { FlexRow } from '../../../../utils/containers';
import renderTags from './renderTags'; import renderTags from './renderTags';
import { FlexRow } from '../../../../../utils/containers';
const TableRowTags = ({ item, i }) => { const TableRowTags = ({ item, i }) => {
return ( return (
<FlexRow className="NewTableStyle__tags-container"> <FlexRow className="TableStyle__tags-container">
{renderTags(item.tags)} {renderTags(item.tags)}
</FlexRow> </FlexRow>
); );

View File

@ -0,0 +1,16 @@
import { FlexRow } from '../../../../../utils/containers';
const renderTags = (tags, i) => {
if (tags.length > 0) {
return tags.map((tag, j) => {
return (
<FlexRow className="TableStyle__tag" key={`submissionTag-${i}-${j}`}>
{tag.name}
</FlexRow>
);
});
}
return <FlexRow className="TableStyle__tag">submission without tags</FlexRow>;
};
export default renderTags;

View File

@ -1,58 +0,0 @@
import styled from 'styled-components';
import { Container } from '../../../../utils/containers';
const MobileTableStyle = styled(Container)`
width: 100%;
border-collapse: collapse;
margin: 32px 0;
tr:nth-of-type(odd) {
background: ${({ theme }) => theme.colors.dark03};
}
th {
background: ${({ theme }) => theme.colors.dark05};
color: ${({ theme }) => theme.colors.white};
}
td,
th {
padding: 6px;
border: 1px solid ${({ theme }) => theme.colors.white};
text-align: left;
}
display: block;
thead,
tbody,
th,
td {
display: block;
}
thead tr {
position: absolute;
top: -9999px;
left: -9999px;
}
td {
border: none;
border-bottom: 1px solid ${({ theme }) => theme.colors.dark01};
position: relative;
padding-left: 50%;
}
.mobile-table-header {
font-weight: 400;
position: absolute;
top: 6px;
left: 6px;
width: 45%;
padding-right: 10px;
white-space: nowrap;
}
`;
export default MobileTableStyle;

View File

@ -1,5 +1,5 @@
import styled from 'styled-components'; import styled from 'styled-components';
import { FlexRow } from '../../../utils/containers'; import { FlexRow } from '../../../../utils/containers';
const RowsBackgroundStyle = styled(FlexRow)` const RowsBackgroundStyle = styled(FlexRow)`
width: calc(100% + 12px); width: calc(100% + 12px);

View File

@ -1,14 +0,0 @@
import styled from 'styled-components';
import { FlexRow } from '../../../../utils/containers';
const TableLine = 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')};
`;
export default TableLine;

View File

@ -1,8 +1,71 @@
import styled from 'styled-components'; import styled from 'styled-components';
import { FlexColumn } from '../../../../utils/containers';
const TableStyle = styled(FlexColumn)` const TableStyle = styled.table`
overflow-x: ${({ metrics }) => (metrics > 10 ? 'scroll' : 'auto')}; border-collapse: separate;
width: 100%;
.TableStyle__th {
position: relative;
cursor: pointer;
* {
cursor: pointer;
}
}
.TableStyle__tr-header {
height: 48px;
position: relative;
}
.TableStyle__tr {
position: relative;
height: 72px;
}
.TableStyle__td {
padding: 0 0 32px 0;
margin: 0 0 0 2px;
}
.TableStyle_line {
position: absolute;
top: 94%;
bottom: ${({ bottom }) => (bottom ? bottom : 'auto')};
left: -6px;
width: calc(100% + 12px);
background-color: ${({ theme }) => theme.colors.dark04};
height: 3px;
box-shadow: ${({ theme }) => theme.shadow};
}
.TableStyle__tag {
color: ${({ theme }) => theme.colors.white};
background-color: ${({ theme }) => theme.colors.green08};
padding: 4px;
border-radius: 2px;
font-size: 12px;
font-weight: 600;
}
.TableStyle__row-footer {
width: 100%;
justify-content: space-between;
position: absolute;
top: 55%;
left: 0;
z-index: 2;
}
.TableStyle__tags-container {
gap: 4px;
padding: 0 2px;
}
.TableStyle__sort-button {
position: absolute;
top: 15px;
right: 16%;
}
`; `;
export default TableStyle; export default TableStyle;

View File

@ -1,244 +1,154 @@
import React from 'react'; import React from 'react';
import theme from '../../utils/theme';
import Media from 'react-media';
import { FlexColumn } from '../../utils/containers'; import { FlexColumn } from '../../utils/containers';
import { H2 } from '../../utils/fonts'; import { H2 } from '../../utils/fonts';
import {
CALC_PAGES,
EVALUATIONS_FORMAT,
RENDER_WHEN,
IS_MOBILE,
} from '../../utils/globals';
import Loading from '../../components/generic/Loading';
import Pager from '../../components/generic/Pager'; import Pager from '../../components/generic/Pager';
import Table from '../../components/generic/Table/Table';
import Search from '../../components/generic/Search'; import Search from '../../components/generic/Search';
import allEntriesSearchQueryHandler from './allEntriesSearchQueryHandler';
import getAllEntries from '../../api/getAllEntries'; import getAllEntries from '../../api/getAllEntries';
import Table from '../../components/generic/Table';
import Loading from '../../components/generic/Loading';
import { CALC_PAGES, ELEMENTS_PER_PAGE } from '../../utils/globals';
import searchQueryHandler from './searchHandler';
import orderKeys from './orderKeys';
const AllEntries = (props) => { const NewTablePageTest = (props) => {
const [entriesFromApi, setEntriesFromApi] = React.useState([]);
const [entriesAll, setEntriesAll] = React.useState([]); const [entriesAll, setEntriesAll] = 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 [idSorted, setIdSorted] = React.useState([]);
const [scoresSorted, setScoresSorted] = React.useState([]); const [scoresSorted, setScoresSorted] = React.useState([]);
const [submitterSorted, setSubmitterSorted] = React.useState(false); const [submitterSorted, setSubmitterSorted] = React.useState(false);
const [whenSorted, setWhenSorted] = React.useState(false); const [whenSorted, setWhenSorted] = React.useState(false);
React.useEffect(() => { React.useMemo(() => {
if (props.challengeName) challengeDataRequest(props.challengeName); if (props.challengeName) {
getAllEntries(
props.challengeName,
[setEntries, setEntriesAll],
setLoading,
setScoresSorted
);
}
}, [props.challengeName]); }, [props.challengeName]);
const challengeDataRequest = (challengeName) => { const sortByUpdate = React.useCallback(
getAllEntries(challengeName, setEntriesFromApi, setEntriesAll); (elem) => {
getAllEntries( let newEntries = entries.slice();
challengeName, const possibleMetrics = orderKeys(entries[0]).filter(
undefined, (key) => !['id', 'submitter', 'when'].includes(key)
setEntries, );
setLoading, let metricIndex = possibleMetrics.indexOf(elem);
setScoresSorted let newScoresSorted = scoresSorted.slice();
); switch (elem) {
}; case 'id':
if (idSorted) {
const getPossibleMetrics = () => { setIdSorted(false);
let metrics = []; newEntries = newEntries.sort((a, b) =>
if (entriesFromApi.tests) { a.id > b.id ? 1 : b.id > a.id ? -1 : 0
for (let test of entriesFromApi.tests) { );
let myEval = `${test.metric}.${test.name}`; } else {
if (myEval && !metrics.includes(myEval)) { setIdSorted(true);
metrics.push(myEval); newEntries = newEntries.sort((a, b) =>
} a.id < b.id ? 1 : b.id < a.id ? -1 : 0
);
}
break;
case 'submitter':
if (submitterSorted) {
setSubmitterSorted(false);
newEntries = newEntries.sort((a, b) =>
a.submitter.toLowerCase() < b.submitter.toLowerCase()
? 1
: b.submitter.toLowerCase() < a.submitter.toLowerCase()
? -1
: 0
);
} else {
setSubmitterSorted(true);
newEntries = newEntries.sort((a, b) =>
a.submitter.toLowerCase() > b.submitter.toLowerCase()
? 1
: b.submitter.toLowerCase() > a.submitter.toLowerCase()
? -1
: 0
);
}
break;
case 'when':
if (whenSorted) {
setWhenSorted(false);
newEntries = newEntries.sort((a, b) =>
a.when < b.when ? 1 : b.when < a.when ? -1 : 0
);
} else {
setWhenSorted(true);
newEntries = newEntries.sort((a, b) =>
a.when > b.when ? 1 : b.when > a.when ? -1 : 0
);
}
break;
default:
if (scoresSorted[metricIndex]) {
newEntries = newEntries.sort(
(a, b) => (b ? b[elem] : -1) - (a ? a[elem] : -1)
);
newScoresSorted[metricIndex] = false;
setScoresSorted(newScoresSorted);
} else {
newEntries = newEntries.sort(
(a, b) => (a ? a[elem] : -1) - (b ? b[elem] : -1)
);
newScoresSorted[metricIndex] = true;
setScoresSorted(newScoresSorted);
}
break;
} }
} setEntries(newEntries);
return metrics; },
}; [entries, idSorted, scoresSorted, submitterSorted, whenSorted]
);
const getAllEntriesHeader = () => { const n = (pageNr - 1) * (ELEMENTS_PER_PAGE * 2);
let header = ['#', 'submitter']; let elements = entries.slice(n, n + ELEMENTS_PER_PAGE * 2);
if (IS_MOBILE()) header.push('when');
for (let metric of getPossibleMetrics()) {
header.push(metric);
}
if (!IS_MOBILE()) header.push('when');
return header;
};
const searchQueryHandler = (event) => {
allEntriesSearchQueryHandler(event, entriesAll, setPageNr, setEntries);
};
const sortByUpdate = (elem, i) => {
let newEntries = entries;
switch (elem) {
case '#':
break;
case 'submitter':
if (submitterSorted) {
setSubmitterSorted(false);
newEntries = newEntries.sort((a, b) =>
a.submitter.toLowerCase() < b.submitter.toLowerCase()
? 1
: b.submitter.toLowerCase() < a.submitter.toLowerCase()
? -1
: 0
);
} else {
setSubmitterSorted(true);
newEntries = newEntries.sort((a, b) =>
a.submitter.toLowerCase() > b.submitter.toLowerCase()
? 1
: b.submitter.toLowerCase() > a.submitter.toLowerCase()
? -1
: 0
);
}
break;
case 'when':
if (whenSorted) {
setWhenSorted(false);
newEntries = newEntries.sort((a, b) =>
a.when < b.when ? 1 : b.when < a.when ? -1 : 0
);
} else {
setWhenSorted(true);
newEntries = newEntries.sort((a, b) =>
a.when > b.when ? 1 : b.when > a.when ? -1 : 0
);
}
break;
default:
// eslint-disable-next-line no-case-declarations
let metricIndex = getPossibleMetrics().indexOf(elem);
// eslint-disable-next-line no-case-declarations
let newScoresSorted = scoresSorted;
if (scoresSorted[metricIndex]) {
newEntries = newEntries.sort(
(a, b) =>
(b.evaluations ? b.evaluations[elem] : -1) -
(a.evaluations ? a.evaluations[elem] : -1)
);
newScoresSorted[metricIndex] = false;
setScoresSorted(newScoresSorted);
} else {
newEntries = newEntries.sort(
(a, b) =>
(a.evaluations ? a.evaluations[elem] : -1) -
(b.evaluations ? b.evaluations[elem] : -1)
);
newScoresSorted[metricIndex] = true;
setScoresSorted(newScoresSorted);
}
break;
}
setEntries(newEntries);
};
const mobileRender = () => {
return (
<FlexColumn padding="24px 12px" width="70%" as="section" id="start">
<H2 as="h2" margin="0 0 12px 0">
All Entries
</H2>
{!loading ? (
<>
<Search searchQueryHandler={searchQueryHandler} />
<Table
challengeName={props.challengeName}
headerElements={getAllEntriesHeader()}
possibleMetrics={getPossibleMetrics()}
tableType="allEntries"
gridTemplateColumns={
'1fr ' + '4fr '.repeat(getAllEntriesHeader().length - 1)
}
staticColumnElements={[
{ name: 'id', format: null, order: 1, align: 'left' },
{ name: 'submitter', format: null, order: 2, align: 'left' },
{ name: 'when', format: RENDER_WHEN, order: 5, align: 'right' },
]}
iterableColumnElement={{
name: 'evaluations',
format: EVALUATIONS_FORMAT,
order: 3,
align: 'left',
}}
pageNr={pageNr}
elements={entries}
sortByUpdate={sortByUpdate}
/>
<Pager
pageNr={pageNr}
elements={entries}
setPageNr={setPageNr}
width="48px"
borderRadius="64px"
pages={CALC_PAGES(entries)}
number={`${pageNr} / ${CALC_PAGES(entries)}`}
/>
</>
) : (
<Loading />
)}
</FlexColumn>
);
};
const desktopRender = () => {
return (
<FlexColumn padding="24px" as="section" width="100%" maxWidth="1600px">
<H2 as="h2" margin="0 0 32px 0">
All Entries
</H2>
{!loading ? (
<>
<Search searchQueryHandler={searchQueryHandler} />
<Table
challengeName={props.challengeName}
headerElements={getAllEntriesHeader()}
possibleMetrics={getPossibleMetrics()}
gridTemplateColumns={
'1fr 3fr ' + '3fr '.repeat(getPossibleMetrics().length) + ' 3fr'
}
user={props.user}
staticColumnElements={[
{ name: 'id', format: null, order: 1, align: 'left' },
{ name: 'submitter', format: null, order: 2, align: 'left' },
{ name: 'when', format: RENDER_WHEN, order: 5, align: 'right' },
]}
metrics={getPossibleMetrics()}
iterableColumnElement={{
name: 'evaluations',
format: EVALUATIONS_FORMAT,
order: 3,
align: 'left',
}}
pageNr={pageNr}
elements={entries}
setPageNr={setPageNr}
sortByUpdate={sortByUpdate}
/>
<Pager
pageNr={pageNr}
elements={entries}
setPageNr={setPageNr}
width="72px"
borderRadius="64px"
pages={CALC_PAGES(entries, 2)}
number={`${pageNr} / ${CALC_PAGES(entries, 2)}`}
/>
</>
) : (
<Loading />
)}
</FlexColumn>
);
};
return ( return (
<> <FlexColumn
<Media query={theme.mobile}>{mobileRender()}</Media> padding="24px"
<Media query={theme.desktop}>{desktopRender()}</Media> gap="32px"
</> as="section"
width="100%"
maxWidth="1600px"
>
<H2 as="h2">All Entries</H2>
{!loading ? (
<>
<Search
searchQueryHandler={(event) =>
searchQueryHandler(event, entriesAll, setPageNr, setEntries)
}
/>
{elements.length > 0 && entries[0] && (
<Table
items={elements}
orderedKeys={orderKeys(entries[0])}
sortByUpdate={sortByUpdate}
/>
)}
<Pager
pageNr={pageNr}
elements={entries}
setPageNr={setPageNr}
width="72px"
borderRadius="64px"
pages={CALC_PAGES(entries, 2)}
number={`${pageNr} / ${CALC_PAGES(entries, 2)}`}
/>
</>
) : (
<Loading />
)}
</FlexColumn>
); );
}; };
export default AllEntries; export default NewTablePageTest;

View File

@ -1,31 +0,0 @@
const allEntriesSearchQueryHandler = (
event,
entriesFromApi,
setPageNr,
setEntries
) => {
let searchQuery = event.target.value;
let submissionsToRender = [];
setPageNr(1);
if (searchQuery === '') setEntries(entriesFromApi);
else {
for (let entry of entriesFromApi) {
const { id, when, submitter } = entry;
let evaluations = '';
if (entry.evaluations) {
for (let evaluation of Object.values(entry.evaluations)) {
evaluations += ` ${evaluation}`;
}
}
const str = `${id} ${submitter} ${when.slice(11, 16)} ${when.slice(
0,
10
)} ${evaluations}`;
if (str.toLowerCase().includes(searchQuery.toLowerCase()))
submissionsToRender.push(entry);
}
setEntries(submissionsToRender);
}
};
export default allEntriesSearchQueryHandler;

View File

@ -0,0 +1,23 @@
const orderKeys = (elem) => {
if (elem) {
let result = ['id', 'submitter'];
const elemKeys = Object.keys(elem);
const dev0keys = elemKeys
.filter((key) => key.split('.')[1] === 'dev-0')
.sort();
const dev1keys = elemKeys
.filter((key) => key.split('.')[1] === 'dev-1')
.sort();
const testAkeys = elemKeys
.filter((key) => key.split('.')[1] === 'test-A')
.sort();
result = result.concat(dev0keys);
result = result.concat(dev1keys);
result = result.concat(testAkeys);
result.push('when');
return result;
}
return null;
};
export default orderKeys;

View File

@ -17,7 +17,6 @@ import getChallengeInfo from '../../api/getChallengeInfo';
import Loading from '../../components/generic/Loading'; import Loading from '../../components/generic/Loading';
import getUser from '../../api/getUser'; import getUser from '../../api/getUser';
import AllEntries from '../AllEntries/AllEntries'; import AllEntries from '../AllEntries/AllEntries';
import NewTablePageTest from '../NewTable/NewTablePageTest';
const Challenge = (props) => { const Challenge = (props) => {
const challengeName = useParams().challengeId; const challengeName = useParams().challengeId;
@ -65,8 +64,6 @@ const Challenge = (props) => {
return <MyEntries challengeName={challengeName} />; return <MyEntries challengeName={challengeName} />;
case CHALLENGE_SECTIONS.SUBMIT: case CHALLENGE_SECTIONS.SUBMIT:
return <Submit challengeName={challengeName} setLoading={setLoading} />; return <Submit challengeName={challengeName} setLoading={setLoading} />;
case 7:
return <NewTablePageTest challengeName={challengeName} />;
default: default:
return ( return (
<Leaderboard <Leaderboard

View File

@ -1,44 +0,0 @@
import React from 'react';
import { FlexRow } from '../../utils/containers';
import pensilIco from '../../assets/pencil_ico.svg';
import deleteIco from '../../assets/delete_ico.svg';
import NewTableStyle from './styles/NewTableStyle';
import RowsBackgroundStyle from './styles/RowsBackgroundStyle';
import TableHeader from './components/TableHeader';
import TableRowTags from './components/TableRowTags';
import TableRowItems from './components/TableRowItems';
import TableRowButtons from './components/TableRowButtons';
const NewTable = ({ items, orderedKeys, sortByUpdate }) => {
const [, updateState] = React.useState();
const tableUpdate = React.useCallback(() => updateState({}), []);
return (
<NewTableStyle>
<TableHeader
orderedKeys={orderedKeys}
sortByUpdate={sortByUpdate}
tableUpdate={tableUpdate}
/>
{items.map((item, i) => {
return (
<tr key={`table-row-${i}`} className="NewTableStyle__tr">
<TableRowItems orderedKeys={orderedKeys} item={item} i={i} />
<FlexRow className="NewTableStyle__row-footer">
<TableRowTags item={item} i={i} />
<TableRowButtons
buttons={[
{ icon: pensilIco, handler: () => console.log('edit') },
{ icon: deleteIco, handler: () => console.log('delete') },
]}
/>
</FlexRow>
<RowsBackgroundStyle i={i} />
</tr>
);
})}
</NewTableStyle>
);
};
export default NewTable;

View File

@ -1,174 +0,0 @@
import React from 'react';
import { FlexColumn } from '../../utils/containers';
import { H2 } from '../../utils/fonts';
import Pager from '../../components/generic/Pager';
import Search from '../../components/generic/Search';
import getAllEntries from '../../api/getAllEntries';
import NewTable from './NewTable';
import Loading from '../../components/generic/Loading';
import { CALC_PAGES, ELEMENTS_PER_PAGE } from '../../utils/globals';
import searchQueryHandler from './searchHandler';
const NewTablePageTest = (props) => {
// eslint-disable-next-line
const [entriesFromApi, setEntriesFromApi] = React.useState([]);
const [entriesAll, setEntriesAll] = React.useState([]);
const [entries, setEntries] = React.useState([]);
const [pageNr, setPageNr] = React.useState(1);
const [loading, setLoading] = React.useState(true);
const [idSorted, setIdSorted] = React.useState([]);
const [scoresSorted, setScoresSorted] = React.useState([]);
const [submitterSorted, setSubmitterSorted] = React.useState(false);
const [whenSorted, setWhenSorted] = React.useState(false);
React.useEffect(() => {
if (props.challengeName) challengeDataRequest(props.challengeName);
}, [props.challengeName]);
const challengeDataRequest = (challengeName) => {
getAllEntries(challengeName, setEntriesFromApi, setEntriesAll);
getAllEntries(
challengeName,
undefined,
setEntries,
setLoading,
setScoresSorted
);
};
const sortByUpdate = (elem) => {
let newEntries = entries.slice();
const possibleMetrics = orderKeys(entries[0]).filter(
(key) => !['id', 'submitter', 'when'].includes(key)
);
let metricIndex = possibleMetrics.indexOf(elem);
let newScoresSorted = scoresSorted.slice();
switch (elem) {
case 'id':
if (idSorted) {
setIdSorted(false);
newEntries = newEntries.sort((a, b) =>
a.id > b.id ? 1 : b.id > a.id ? -1 : 0
);
} else {
setIdSorted(true);
newEntries = newEntries.sort((a, b) =>
a.id < b.id ? 1 : b.id < a.id ? -1 : 0
);
}
break;
case 'submitter':
if (submitterSorted) {
setSubmitterSorted(false);
newEntries = newEntries.sort((a, b) =>
a.submitter.toLowerCase() < b.submitter.toLowerCase()
? 1
: b.submitter.toLowerCase() < a.submitter.toLowerCase()
? -1
: 0
);
} else {
setSubmitterSorted(true);
newEntries = newEntries.sort((a, b) =>
a.submitter.toLowerCase() > b.submitter.toLowerCase()
? 1
: b.submitter.toLowerCase() > a.submitter.toLowerCase()
? -1
: 0
);
}
break;
case 'when':
if (whenSorted) {
setWhenSorted(false);
newEntries = newEntries.sort((a, b) =>
a.when < b.when ? 1 : b.when < a.when ? -1 : 0
);
} else {
setWhenSorted(true);
newEntries = newEntries.sort((a, b) =>
a.when > b.when ? 1 : b.when > a.when ? -1 : 0
);
}
break;
default:
if (scoresSorted[metricIndex]) {
newEntries = newEntries.sort(
(a, b) => (b ? b[elem] : -1) - (a ? a[elem] : -1)
);
newScoresSorted[metricIndex] = false;
setScoresSorted(newScoresSorted);
} else {
newEntries = newEntries.sort(
(a, b) => (a ? a[elem] : -1) - (b ? b[elem] : -1)
);
newScoresSorted[metricIndex] = true;
setScoresSorted(newScoresSorted);
}
break;
}
setEntries(newEntries);
};
const orderKeys = (elem) => {
if (elem) {
let result = ['id', 'submitter'];
const elemKeys = Object.keys(elem);
const dev0keys = elemKeys
.filter((key) => key.split('.')[1] === 'dev-0')
.sort();
const dev1keys = elemKeys
.filter((key) => key.split('.')[1] === 'dev-1')
.sort();
const testAkeys = elemKeys
.filter((key) => key.split('.')[1] === 'test-A')
.sort();
result = result.concat(dev0keys);
result = result.concat(dev1keys);
result = result.concat(testAkeys);
result.push('when');
return result;
}
return null;
};
const n = (pageNr - 1) * (ELEMENTS_PER_PAGE * 2);
let elements = entries.slice(n, n + ELEMENTS_PER_PAGE * 2);
if (!loading) {
return (
<FlexColumn
padding="24px"
gap="32px"
as="section"
width="100%"
maxWidth="1600px"
>
<H2 as="h2">New Table Test</H2>
<Search
searchQueryHandler={(event) =>
searchQueryHandler(event, entriesAll, setPageNr, setEntries)
}
/>
{elements.length > 0 && entries[0] && (
<NewTable
items={elements}
orderedKeys={orderKeys(entries[0])}
sortByUpdate={sortByUpdate}
/>
)}
<Pager
pageNr={pageNr}
elements={entries}
setPageNr={setPageNr}
width="72px"
borderRadius="64px"
pages={CALC_PAGES(entries, 2)}
number={`${pageNr} / ${CALC_PAGES(entries, 2)}`}
/>
</FlexColumn>
);
} else return <Loading />;
};
export default NewTablePageTest;

View File

@ -1,18 +0,0 @@
import { FlexRow } from '../../../../utils/containers';
const renderTags = (tags, i) => {
if (tags.length > 0) {
return tags.map((tag, j) => {
return (
<FlexRow className="NewTableStyle__tag" key={`submissionTag-${i}-${j}`}>
{tag.name}
</FlexRow>
);
});
}
return (
<FlexRow className="NewTableStyle__tag">submission without tags</FlexRow>
);
};
export default renderTags;

View File

@ -1 +0,0 @@
export { default } from './NewTablePageTest';

View File

@ -1,65 +0,0 @@
import styled from 'styled-components';
const NewTableStyle = styled.table`
border-collapse: separate;
width: 100%;
.NewTableStyle__th {
position: relative;
cursor: pointer;
* {
cursor: pointer;
}
}
.NewTableStyle__tr-header {
height: 48px;
position: relative;
}
.NewTableStyle__tr {
position: relative;
height: 72px;
}
.NewTableStyle__td {
padding: 0 0 32px 0;
margin: 0 0 0 2px;
}
.NewTableStyle__line {
position: absolute;
top: 94%;
bottom: ${({ bottom }) => (bottom ? bottom : 'auto')};
left: -6px;
width: calc(100% + 12px);
background-color: ${({ theme }) => theme.colors.dark04};
height: 3px;
box-shadow: ${({ theme }) => theme.shadow};
}
.NewTableStyle__tag {
color: ${({ theme }) => theme.colors.white};
background-color: ${({ theme }) => theme.colors.green08};
padding: 4px;
border-radius: 2px;
font-size: 12px;
font-weight: 600;
}
.NewTableStyle__row-footer {
width: 100%;
justify-content: space-between;
position: absolute;
top: 55%;
left: 0;
z-index: 2;
}
.NewTableStyle__tags-container {
gap: 4px;
padding: 0 2px;
}
`;
export default NewTableStyle;

View File

@ -1,10 +0,0 @@
import styled from 'styled-components';
import { FlexRow } from '../../../utils/containers';
const SortButtonContainerStyle = styled(FlexRow)`
position: absolute;
top: 15px;
right: 16%;
`;
export default SortButtonContainerStyle;