Compare commits

..

No commits in common. "master" and "submit_tags" have entirely different histories.

129 changed files with 2153 additions and 3165 deletions

4
.env
View File

@ -2,6 +2,4 @@ REACT_APP_KC_URL=https://auth-dev.csi.wmi.amu.edu.pl/
REACT_APP_KC_REALM=gonito-dev
REACT_APP_KC_CLIENT_ID=gonito-dev-localhost
REACT_APP_API=https://gonito-back-dev.csi.wmi.amu.edu.pl/api
# https://gonito.net/api
# https://gonito-back-dev.csi.wmi.amu.edu.pl/api
REACT_APP_API=https://gonito-back-dev.csi.wmi.amu.edu.pl/api

View File

@ -14,6 +14,13 @@
"eqeqeq": [
"error",
"always"
],
"quotes": [
2,
"single",
{
"avoidEscape": true
}
]
}
}

225
src/App.js Normal file
View File

@ -0,0 +1,225 @@
import { ThemeProvider } from 'styled-components';
import theme from './utils/theme';
import LandingPage from './pages/LandingPage';
import Challenges from './pages/Challanges/Challenges';
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import NavBar from './components/navigation/NavBar';
import {
CHALLENGE_PAGE,
CHALLENGES_PAGE,
IS_MOBILE,
POLICY_PRIVACY_PAGE,
LOGIN_REQUIRED_PAGES,
ROOT_URL,
CHALLENGE_SECTIONS,
} from './utils/globals';
import KeyCloakService from './services/KeyCloakService';
import React from 'react';
import LoggedBar from './components/navigation/LoggedBar';
import addUser from './api/addUser';
import Loading from './components/generic/Loading';
import { FlexColumn } from './utils/containers';
import PopupMessage from './components/generic/PopupMessage';
import PolicyPrivacy from './pages/PolicyPrivacy';
import Challenge from './pages/Challenge';
const App = () => {
const [loggedBarVisible, setLoggedBarVisible] = React.useState('100vw');
const [loggedBarHover, setLoggedBarHover] = React.useState(false);
const [popUpHeader, setPopUpHeader] = React.useState('');
const [popUpMessage, setPopUpMessage] = React.useState('');
const [confirmPopUpHandler, setConfirmPopUpHandler] = React.useState(null);
React.useEffect(() => {
if (sessionStorage.getItem('logout') === 'yes') {
const pageName = window.location.pathname.split('/').at(-1);
if (LOGIN_REQUIRED_PAGES.includes(pageName)) {
window.location.replace(`${ROOT_URL}/challenges`);
}
}
if (sessionStorage.getItem('logged') !== 'yes') {
if (KeyCloakService.isLoggedIn()) {
sessionStorage.setItem('logged', 'yes');
addUser();
}
}
if (
sessionStorage.getItem('logged') === 'yes' &&
(window.location.pathname === `${POLICY_PRIVACY_PAGE}/login` ||
window.location.pathname === `${POLICY_PRIVACY_PAGE}/register`)
) {
window.location.replace(`${ROOT_URL}/challenges`);
}
setTimeout(() => {
if (sessionStorage.getItem('logged') === 'yes') {
if (!KeyCloakService.isLoggedIn()) {
KeyCloakService.doLogin();
}
}
}, 1500);
});
const popUpMessageHandler = (header, message, confirmHandler) => {
setPopUpHeader(header);
setPopUpMessage(message);
if (confirmHandler !== null && confirmHandler !== undefined) {
setConfirmPopUpHandler(() => confirmHandler());
} else {
setConfirmPopUpHandler(null);
}
};
const popUpMessageRender = () => {
if (popUpHeader !== '' || popUpMessage !== '') {
return (
<PopupMessage
header={popUpHeader}
message={popUpMessage}
confirmHandler={confirmPopUpHandler}
popUpMessageHandler={popUpMessageHandler}
/>
);
}
};
const loggedBarVisibleHandler = () => {
if (loggedBarVisible === '0' && !loggedBarHover)
setLoggedBarVisible('100vw');
else setLoggedBarVisible('0');
};
const loggedBarHoverTrue = () => {
setLoggedBarHover(true);
};
const loggedBarHoverFalse = () => {
setLoggedBarHover(false);
};
const renderApp = () => {
return (
<BrowserRouter>
<ThemeProvider theme={theme}>
{popUpMessageRender()}
<NavBar
loggedBarVisibleHandler={loggedBarVisibleHandler}
popUpMessageHandler={popUpMessageHandler}
/>
{!IS_MOBILE() ? (
<LoggedBar
visible={loggedBarVisible}
loggedBarVisibleHandler={loggedBarVisibleHandler}
loggedBarHoverTrue={loggedBarHoverTrue}
loggedBarHoverFalse={loggedBarHoverFalse}
username={KeyCloakService.getUsername()}
/>
) : (
''
)}
<Routes>
<Route
path={`${CHALLENGE_PAGE}/:challengeId`}
element={<Challenge section={CHALLENGE_SECTIONS.LEADERBOARD} />}
/>
<Route
path={`${CHALLENGE_PAGE}/:challengeId/leaderboard`}
element={<Challenge section={CHALLENGE_SECTIONS.LEADERBOARD} />}
/>
<Route
path={`${CHALLENGE_PAGE}/:challengeId/allentries`}
element={<Challenge section={CHALLENGE_SECTIONS.ALL_ENTRIES} />}
/>
<Route
path={`${CHALLENGE_PAGE}/:challengeId/readme`}
element={<Challenge section={CHALLENGE_SECTIONS.README} />}
/>
<Route
path={`${CHALLENGE_PAGE}/:challengeId/howto`}
element={
<Challenge
popUpMessageHandler={popUpMessageHandler}
section={CHALLENGE_SECTIONS.HOW_TO}
/>
}
/>
<Route
path={`${CHALLENGE_PAGE}/:challengeId/myentries`}
element={<Challenge section={CHALLENGE_SECTIONS.MY_ENTRIES} />}
/>
<Route
path={`${CHALLENGE_PAGE}/:challengeId/submit`}
element={<Challenge section={CHALLENGE_SECTIONS.SUBMIT} />}
/>
<Route path={CHALLENGES_PAGE} element={<Challenges />} />
<Route
path={POLICY_PRIVACY_PAGE}
element={
<PolicyPrivacy popUpMessageHandler={popUpMessageHandler} />
}
/>
<Route
path={`${POLICY_PRIVACY_PAGE}/login`}
element={
<PolicyPrivacy
popUpMessageHandler={popUpMessageHandler}
beforeLogin
/>
}
/>
<Route
path={`${POLICY_PRIVACY_PAGE}/register`}
element={
<PolicyPrivacy
popUpMessageHandler={popUpMessageHandler}
beforeRegister
/>
}
/>
{KeyCloakService.isLoggedIn() ? (
<>
<Route exact path="/" element={<Challenges />} />
<Route element={<Challenges />} />
</>
) : (
<>
<Route
exact
path="/"
element={
<LandingPage popUpMessageHandler={popUpMessageHandler} />
}
/>
<Route
element={
<LandingPage popUpMessageHandler={popUpMessageHandler} />
}
/>
</>
)}
</Routes>
</ThemeProvider>
</BrowserRouter>
);
};
if (sessionStorage.getItem('logged') === 'yes') {
if (KeyCloakService.isLoggedIn()) {
return renderApp();
} else {
return (
<ThemeProvider theme={theme}>
<FlexColumn width="100vw" height="100vh">
<Loading />
</FlexColumn>
</ThemeProvider>
);
}
} else {
return renderApp();
}
};
export default App;

View File

@ -1,33 +0,0 @@
import React from 'react';
import { ThemeProvider } from 'styled-components';
import theme from '../utils/theme';
import PopUpMessageManager from './components/PopUpMessageManager';
import RoutingManager from './components/RoutingManager';
import NavigationManager from './components/NavigationManager';
import { BrowserRouter } from 'react-router-dom';
import startManage from './functions/startManage';
import startApp from './functions/startApp';
const App = () => {
React.useMemo(() => {
startManage();
}, []);
const renderApp = React.useCallback(() => {
return (
<ThemeProvider theme={theme}>
<BrowserRouter>
<PopUpMessageManager>
<NavigationManager>
<RoutingManager />
</NavigationManager>
</PopUpMessageManager>
</BrowserRouter>
</ThemeProvider>
);
}, []);
return startApp(renderApp);
};
export default App;

View File

@ -1,47 +0,0 @@
import React from 'react';
import NavBar from '../../components/navigation/NavBar/NavBar';
import LoggedBar from '../../components/navigation/LoggedBar';
import KeyCloakService from '../../services/KeyCloakService';
import { CHILDREN_WITH_PROPS, IS_MOBILE } from '../../utils/globals';
const NavigationManager = ({children, popUpMessageHandler}) => {
const [loggedBarVisible, setLoggedBarVisible] = React.useState('100vw');
const [loggedBarHover, setLoggedBarHover] = React.useState(false);
const [navOptions, setNavOptions] = React.useState(true);
const loggedBarVisibleHandler = React.useCallback(() => {
if (loggedBarVisible === '0' && !loggedBarHover)
setLoggedBarVisible('100vw');
else setLoggedBarVisible('0');
}, [loggedBarHover, loggedBarVisible]);
const hideNavOptions = React.useCallback(() => {
setNavOptions(false);
}, []);
const showNavOptions = React.useCallback(() => {
setNavOptions(true);
}, []);
return (
<>
<NavBar
loggedBarVisibleHandler={loggedBarVisibleHandler}
popUpMessageHandler={popUpMessageHandler}
navOptions={navOptions}
/>
{!IS_MOBILE() && (
<LoggedBar
visible={loggedBarVisible}
loggedBarVisibleHandler={loggedBarVisibleHandler}
loggedBarHoverTrue={() => setLoggedBarHover(true)}
loggedBarHoverFalse={() => setLoggedBarHover(false)}
username={KeyCloakService.getUsername()}
/>
)}
{CHILDREN_WITH_PROPS(children, { hideNavOptions, showNavOptions, popUpMessageHandler })}
</>
);
};
export default NavigationManager;

View File

@ -1,44 +0,0 @@
import React from 'react';
import PopupMessage from '../../components/generic/PopupMessage';
import { CHILDREN_WITH_PROPS } from '../../utils/globals';
const PopUpMessageManager = (props) => {
const [popUpHeader, setPopUpHeader] = React.useState('');
const [popUpMessage, setPopUpMessage] = React.useState('');
const [borderColor, setBorderColor] = React.useState(null);
const [confirmPopUpHandler, setConfirmPopUpHandler] = React.useState(null);
const popUpMessageHandler = (header, message, confirmHandler=null, borderColor=null) => {
setPopUpHeader(header);
setPopUpMessage(message);
setBorderColor(borderColor);
if (confirmHandler !== null && confirmHandler !== undefined) {
setConfirmPopUpHandler(() => confirmHandler());
} else {
setConfirmPopUpHandler(null);
}
};
const popUpMessageRender = () => {
if (popUpHeader !== '' || popUpMessage !== '') {
return (
<PopupMessage
header={popUpHeader}
message={popUpMessage}
confirmHandler={confirmPopUpHandler}
borderColor={borderColor}
popUpMessageHandler={popUpMessageHandler}
/>
);
}
};
return (
<>
{popUpMessageRender()}
{CHILDREN_WITH_PROPS(props.children, { popUpMessageHandler })}
</>
);
};
export default PopUpMessageManager;

View File

@ -1,146 +0,0 @@
import { Route, Routes } from 'react-router-dom';
import {
CHALLENGES_PAGE,
CHALLENGE_PAGE,
CHALLENGE_SECTIONS,
POLICY_PRIVACY_PAGE,
} from '../../utils/globals';
import Challenge from '../../pages/Challenge';
import Challenges from '../../pages/Challanges';
import PolicyPrivacy from '../../pages/PolicyPrivacy';
import LandingPage from '../../pages/LandingPage';
import KeyCloakService from '../../services/KeyCloakService';
const RoutingManager = (props) => {
return (
<Routes>
<Route
path={`${CHALLENGE_PAGE}/:challengeId`}
element={
<Challenge
section={CHALLENGE_SECTIONS.LEADERBOARD}
popUpMessageHandler={props.popUpMessageHandler}
/>
}
/>
<Route
path={`${CHALLENGE_PAGE}/:challengeId/leaderboard`}
element={
<Challenge
section={CHALLENGE_SECTIONS.LEADERBOARD}
popUpMessageHandler={props.popUpMessageHandler}
/>
}
/>
<Route
path={`${CHALLENGE_PAGE}/:challengeId/allentries`}
element={
<Challenge
section={CHALLENGE_SECTIONS.ALL_ENTRIES}
popUpMessageHandler={props.popUpMessageHandler}
/>
}
/>
<Route
path={`${CHALLENGE_PAGE}/:challengeId/readme`}
element={<Challenge section={CHALLENGE_SECTIONS.README} />}
/>
<Route
path={`${CHALLENGE_PAGE}/:challengeId/howto`}
element={
<Challenge
popUpMessageHandler={props.popUpMessageHandler}
section={CHALLENGE_SECTIONS.HOW_TO}
/>
}
/>
<Route
path={`${CHALLENGE_PAGE}/:challengeId/myentries`}
element={
<Challenge
section={CHALLENGE_SECTIONS.MY_ENTRIES}
popUpMessageHandler={props.popUpMessageHandler}
/>
}
/>
<Route
path={`${CHALLENGE_PAGE}/:challengeId/submit`}
element={
<Challenge
section={CHALLENGE_SECTIONS.SUBMIT}
popUpMessageHandler={props.popUpMessageHandler}
/>
}
/>
<Route
path={CHALLENGES_PAGE}
element={<Challenges popUpMessageHandler={props.popUpMessageHandler} />}
/>
<Route
path={POLICY_PRIVACY_PAGE}
element={
<PolicyPrivacy popUpMessageHandler={props.popUpMessageHandler} />
}
/>
<Route
path={`${POLICY_PRIVACY_PAGE}/login`}
element={
<PolicyPrivacy
popUpMessageHandler={props.popUpMessageHandler}
beforeLogin
/>
}
/>
<Route
path={`${POLICY_PRIVACY_PAGE}/register`}
element={
<PolicyPrivacy
popUpMessageHandler={props.popUpMessageHandler}
beforeRegister
/>
}
/>
{KeyCloakService.isLoggedIn() ? (
<>
<Route
exact
path="/"
element={
<Challenges popUpMessageHandler={props.popUpMessageHandler} />
}
/>
<Route
element={
<Challenges popUpMessageHandler={props.popUpMessageHandler} />
}
/>
</>
) : (
<>
<Route
exact
path="/"
element={
<LandingPage
popUpMessageHandler={props.popUpMessageHandler}
showNavOptions={props.showNavOptions}
hideNavOptions={props.hideNavOptions}
/>
}
/>
<Route
element={
<LandingPage
popUpMessageHandler={props.popUpMessageHandler}
showNavOptions={props.showNavOptions}
hideNavOptions={props.hideNavOptions}
/>
}
/>
</>
)}
</Routes>
);
};
export default RoutingManager;

View File

@ -1,29 +0,0 @@
import Loading from '../../components/generic/Loading';
import { FlexColumn } from '../../utils/containers';
import SESSION_STORAGE from '../../utils/sessionStorage';
import KeyCloakService from '../../services/KeyCloakService';
import { ThemeProvider } from 'styled-components';
import theme from '../../utils/theme';
const startApp = (renderApp) => {
if (
sessionStorage.getItem(SESSION_STORAGE.LOGGED) ===
SESSION_STORAGE.STATIC_VALUE.YES
) {
if (KeyCloakService.isLoggedIn()) {
return renderApp();
} else {
return (
<ThemeProvider theme={theme}>
<FlexColumn width="100vw" height="100vh">
<Loading />
</FlexColumn>
</ThemeProvider>
);
}
} else {
return renderApp();
}
};
export default startApp;

View File

@ -1,55 +0,0 @@
import {
POLICY_PRIVACY_PAGE,
LOGIN_REQUIRED_PAGES,
ROOT_URL,
} from '../../utils/globals';
import addUser from '../../api/addUser';
import SESSION_STORAGE from '../../utils/sessionStorage';
import KeyCloakService from '../../services/KeyCloakService';
const startManage = () => {
if (
sessionStorage.getItem(SESSION_STORAGE.LOGOUT) ===
SESSION_STORAGE.STATIC_VALUE.YES
) {
const pageName = window.location.pathname.split('/').at(-1);
if (LOGIN_REQUIRED_PAGES.includes(pageName)) {
window.location.replace(`${ROOT_URL}/challenges`);
}
}
if (
sessionStorage.getItem(SESSION_STORAGE.LOGGED) !==
SESSION_STORAGE.STATIC_VALUE.YES
) {
if (KeyCloakService.isLoggedIn()) {
sessionStorage.setItem(
SESSION_STORAGE.LOGGED,
SESSION_STORAGE.STATIC_VALUE.YES
);
addUser();
}
}
if (
sessionStorage.getItem(SESSION_STORAGE.LOGGED) ===
SESSION_STORAGE.STATIC_VALUE.YES &&
(window.location.pathname === `${POLICY_PRIVACY_PAGE}/login` ||
window.location.pathname === `${POLICY_PRIVACY_PAGE}/register`)
) {
window.location.replace(`${ROOT_URL}/challenges`);
}
setTimeout(() => {
if (
sessionStorage.getItem(SESSION_STORAGE.LOGGED) ===
SESSION_STORAGE.STATIC_VALUE.YES
) {
if (!KeyCloakService.isLoggedIn()) {
KeyCloakService.doLogin();
}
}
}, 1500);
};
export default startManage;

View File

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

View File

@ -7,13 +7,10 @@ const challengeSubmission = (
repoUrl,
repoBranch,
description,
submissionTags,
dispatch
) => {
const tagNames = submissionTags.map((tag) => tag.name).join(',');
const details = {
f1: description,
f2: tagNames,
f3: repoUrl,
f4: repoBranch,
};
@ -21,8 +18,6 @@ const challengeSubmission = (
for (let property in details) {
let encodedKey = encodeURIComponent(property);
let encodedValue = encodeURIComponent(details[property]);
if (property === 'f2')
encodedValue = encodedValue.replaceAll('%2C', '%2C+');
formBody.push(encodedKey + '=' + encodedValue);
}
formBody = formBody.join('&');
@ -38,12 +33,7 @@ const challengeSubmission = (
.then((data) => {
dispatch({ type: SUBMIT_ACTION.TOGGLE_SUBMISSION_LOADING });
const processUrl = API.replace('/api', '');
if (Number.isInteger(Number(data))) {
console.log(`${processUrl}/open-view-progress/${data}#form`);
window.location.replace(
`${processUrl}/open-view-progress/${data}#form`
);
}
window.location.replace(`${processUrl}/open-view-progress/${data}#form`);
// console.log(data);
// fetch(`${API}/view-progress-with-web-sockets/${data}`)

View File

@ -1,43 +0,0 @@
import KeyCloakService from '../services/KeyCloakService';
import { API } from '../utils/globals';
import theme from '../utils/theme';
const deleteSubmission = async (
item,
deletedItems,
setDeletedItems,
popUpMessageHandler
) => {
fetch(`${API}/delete-submission/${item.id}`, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
Authorization: `Bearer ${KeyCloakService.getToken()}`,
},
})
.then((resp) => resp.text())
.then((data) => {
if (data === 'deleted') {
let newDeletedItems = deletedItems.slice();
newDeletedItems.push(item);
setDeletedItems(newDeletedItems);
popUpMessageHandler('Complete', `Submission "${item.id}" deleted`);
} else if (data.includes('<!doctype html>') && data.includes('Login')) {
popUpMessageHandler(
'Error',
'You have to be login in to edit submission!',
null,
theme.colors.red
);
} else {
popUpMessageHandler(
'Error',
"You can't delete this submission!",
null,
theme.colors.red
);
}
});
};
export default deleteSubmission;

View File

@ -1,47 +0,0 @@
import KeyCloakService from '../services/KeyCloakService';
import { API } from '../utils/globals';
import theme from '../utils/theme';
const editSubmission = async (
submisssion,
tags,
description,
popUpMessageHandler
) => {
tags = tags.replaceAll(',', '%2C');
fetch(`${API}/edit-submission/${submisssion}/${tags}/${description}`, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
Authorization: `Bearer ${KeyCloakService.getToken()}`,
},
})
.then((resp) => resp.text())
.then((data) => {
console.log(data);
if (data === 'Submission changed') {
popUpMessageHandler(
'Submission changed!',
`Submission ${submisssion} edited`,
null,
theme.colors.green
);
} else if (data === 'Only owner can edit a submission!') {
popUpMessageHandler('Error', data, null, theme.colors.red);
} else if (data.includes('<!doctype html>') && data.includes('Login')) {
popUpMessageHandler(
'Error',
'You have to be login in to edit submission!',
null,
theme.colors.red
);
} else {
if (data.length > 650) {
data = `${data.slice(0, 650)}...`;
}
popUpMessageHandler('Error', data, null, theme.colors.red);
}
});
};
export default editSubmission;

View File

@ -1,18 +1,19 @@
import { API } from '../utils/globals';
import KeyCloakService from '../services/KeyCloakService';
const getEntries = (
endpoint,
const getAllEntries = (
challengeName,
setDataStates,
setDataOriginalState,
setDataState,
setLoadingState,
setScoreSorted
) => {
fetch(`${API}/${endpoint}/${challengeName}`, {
fetch(`${API}/challenge-all-submissions/${challengeName}`, {
headers: { Authorization: `Bearer ${KeyCloakService.getToken()}` },
})
.then((response) => response.json())
.then((data) => {
if (setDataOriginalState) setDataOriginalState(data);
let item = {};
let result = [];
let initSetScoreSorted = [];
@ -29,35 +30,37 @@ const getEntries = (
};
}
for (let test of tests) {
if (!item.evaluations) {
item.evaluations = {};
}
if (!Object.hasOwn(item.evaluations, `${test.metric}.${test.name}`)) {
item = {
...item,
evaluations: {
...item.evaluations,
[`${test.metric}.${test.name}`]: -999999999,
},
};
if (item.evaluations) {
if (
!Object.hasOwn(item.evaluations, `${test.metric}.${test.name}`)
) {
item = {
...item,
evaluations: {
...item.evaluations,
[`${test.metric}.${test.name}`]: '-1',
},
};
}
}
}
item = {
...item.evaluations,
...submission,
...item,
id: submission.id,
submitter: submission.submitter,
when: submission.when,
};
result.push(item);
item = {};
}
result = result.filter((item) => !item.deleted);
// eslint-disable-next-line no-unused-vars
for (let _ of tests) {
initSetScoreSorted.push(false);
}
for (let setDataState of setDataStates) setDataState(result);
setDataState(result);
if (setScoreSorted) setScoreSorted(initSetScoreSorted);
if (setLoadingState) setLoadingState(false);
});
};
export default getEntries;
export default getAllEntries;

View File

@ -1,62 +1,19 @@
import { API } from '../utils/globals';
import KeyCloakService from '../services/KeyCloakService';
const getChallengeLeaderboard = (
endpoint,
const getChallengeLeaderboard = async (
setDataState,
challengeName,
setDataStates,
setLoadingState,
setScoreSorted
setLoading
) => {
fetch(`${API}/${endpoint}/${challengeName}`, {
headers: { Authorization: `Bearer ${KeyCloakService.getToken()}` },
})
await fetch(`${API}/leaderboard/${challengeName}`)
.then((response) => response.json())
.then((data) => {
let item = {};
let result = [];
let initSetScoreSorted = [];
let tests = data.tests;
for (let submission of data.entries) {
for (let evaluation of submission.evaluations) {
item = {
...item,
evaluations: {
...item.evaluations,
[`${evaluation.test.metric}.${evaluation.test.name}`]:
evaluation.score,
},
};
}
for (let test of tests) {
if (!item.evaluations) {
item.evaluations = {};
}
if (!Object.hasOwn(item.evaluations, `${test.metric}.${test.name}`)) {
item = {
...item,
evaluations: {
...item.evaluations,
[`${test.metric}.${test.name}`]: -999999999,
},
};
}
}
item = {
...item.evaluations,
...submission,
};
result.push(item);
item = {};
}
// eslint-disable-next-line no-unused-vars
for (let _ of tests) {
initSetScoreSorted.push(false);
}
for (let setDataState of setDataStates) setDataState(result);
if (setScoreSorted) setScoreSorted(initSetScoreSorted);
if (setLoadingState) setLoadingState(false);
setDataState(data.entries);
});
if (setLoading) {
setLoading(false);
}
};
export default getChallengeLeaderboard;

View File

@ -1,15 +1,16 @@
import { API } from '../utils/globals';
import {API} from '../utils/globals';
import KeyCloakService from '../services/KeyCloakService';
const getFullUser = async (setDataState, setLoadingState) => {
fetch(`${API}/full-user-info`, {
headers: { Authorization: `Bearer ${KeyCloakService.getToken()}` },
})
.then((response) => response.json())
.then((data) => {
setDataState(data);
if (setLoadingState) setLoadingState(false);
});
const getFullUser = (setDataState, setLoadingState) => {
fetch(`${API}/full-user-info`, {
headers: {'Authorization': `Bearer ${KeyCloakService.getToken()}`}
})
.then(response => response.json())
.then(data => {
setDataState(data);
if (setLoadingState)
setLoadingState(false);
});
};
export default getFullUser;
export default getFullUser;

68
src/api/getMyEntries.js Normal file
View File

@ -0,0 +1,68 @@
import { API } from '../utils/globals';
import KeyCloakService from '../services/KeyCloakService';
const getMyEntries = (
challengeName,
setDataOriginalState,
setDataStateForSearch,
setDataState,
setLoadingState,
setScoreSorted
) => {
fetch(`${API}/challenge-my-submissions/${challengeName}`, {
headers: { Authorization: `Bearer ${KeyCloakService.getToken()}` },
})
.then((response) => response.json())
.then((data) => {
setDataOriginalState(data);
let item = {};
let result = [];
let initSetScoreSorted = [];
let tests = data.tests;
for (let submission of data.submissions) {
for (let evaluation of submission.evaluations) {
item = {
...item,
evaluations: {
...item.evaluations,
[`${evaluation.test.metric}.${evaluation.test.name}`]:
evaluation.score,
},
};
}
for (let test of tests) {
if (item.evaluations) {
if (
!Object.hasOwn(item.evaluations, `${test.metric}.${test.name}`)
) {
item = {
...item,
evaluations: {
...item.evaluations,
[`${test.metric}.${test.name}`]: '-1',
},
};
}
}
}
item = {
...item,
id: submission.id,
submitter: submission.submitter,
when: submission.when,
};
result.push(item);
item = {};
}
// eslint-disable-next-line no-unused-vars
for (let _ of tests) {
initSetScoreSorted.push(false);
}
setScoreSorted(initSetScoreSorted);
setDataStateForSearch(result);
setDataState(result);
setLoadingState(false);
});
};
export default getMyEntries;

View File

@ -1,10 +1,11 @@
import SUBMIT_ACTION from '../pages/Submit/model/SubmitActionEnum';
import { API } from '../utils/globals';
const getTags = (setState) => {
const getTags = (dispatch) => {
fetch(`${API}/list-tags`)
.then((response) => response.json())
.then((data) => {
setState(data);
dispatch({ type: SUBMIT_ACTION.LOAD_TAGS, payload: data });
});
};

View File

@ -1,39 +0,0 @@
<svg width="396" height="110" viewBox="0 0 396 110" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M126.589 80.1711V90.4774H116.231V86.1171H105.374V87.7027H100.594V86.1171H90.136V90.4774H79.7778V80.1711H90.136V84.5315H100.594V82.9459H105.374V84.5315H116.231V80.1711H126.589Z" fill="#E52713"/>
<path d="M147.404 58.1712V68.4775H136.947V64.1171H116.23V68.4775H105.872V64.1171H88.0443V72.3423H69.8179V64.1171H49.1017V65.7027H44.321V64.1171H27.5886V68.4775H17.1309V58.1712H27.5886V62.5315H44.321V60.9459H49.1017V62.5315H69.8179V54.2072H88.0443V62.5315H105.872V58.1712H116.23V62.5315H136.947V58.1712H147.404Z" fill="#E52713"/>
<path d="M153.779 35.1802V45.4865H143.42V41.1262H129.477V42.7118H124.596V41.1262H109.856V47.964H94.518V41.1262H81.3712V42.7118H76.5905V41.1262H63.9416V51.7298H40.9346V41.1262H30.7756V45.4865H20.4175V41.1262H10.3581V45.4865H0V35.1802H10.3581V39.5406H20.4175V35.1802H30.7756V39.5406H40.9346V28.8379H63.9416V39.5406H76.5905V37.955H81.3712V39.5406H94.518V32.7028H109.856V39.5406H124.596V37.955H129.477V39.5406H143.42V35.1802H153.779Z" fill="#E52713"/>
<path d="M134.656 15.8558V26.1621H124.298V21.8018H104.976V23.3874H100.195V21.8018H78.0847V26.1621H67.7265V21.8018H32.7678V26.1621H22.4097V15.8558H32.7678V20.2162H67.7265V15.8558H78.0847V20.2162H100.195V18.6306H104.976V20.2162H124.298V15.8558H134.656Z" fill="#E52713"/>
<path d="M104.877 0V10.3063H94.5186V5.94594H81.8698V7.63063H77.0891V5.94594H64.5398V10.3063H54.1816V0H64.5398V4.36036H77.0891V2.77477H81.8698V4.36036H94.5186V0H104.877Z" fill="#E52713"/>
<path d="M117.625 99.6937H107.267V110H117.625V99.6937Z" fill="#E52713"/>
<path d="M175.69 17.9369C175.69 11.991 180.272 7.13513 186.646 7.13513C190.53 7.13513 193.917 9.11711 195.71 12.2883L193.817 13.3784C192.522 10.9009 189.734 9.21621 186.646 9.21621C181.367 9.21621 177.782 13.0811 177.782 17.9369C177.782 22.7928 181.367 26.6576 186.646 26.6576C189.734 26.6576 192.522 24.973 193.817 22.4955L195.71 23.4865C194.016 26.5586 190.53 28.6396 186.646 28.6396C180.272 28.6396 175.69 23.8829 175.69 17.9369Z" fill="black"/>
<path d="M214.135 26.3603V28.3423H201.884V7.53149H214.035V9.51348H204.075V16.8468H213.338V18.8288H204.075V26.3603H214.135Z" fill="black"/>
<path d="M235.947 7.53149V28.3423H234.154L222.501 11.3964V28.3423H220.41V7.53149H222.202L233.855 24.4774V7.53149H235.947Z" fill="black"/>
<path d="M256.563 9.41435H250.089V28.2432H247.998V9.41435H241.524V7.43237H256.464V9.41435H256.563Z" fill="black"/>
<path d="M274.491 26.3603V28.3423H262.241V7.53149H274.392V9.51348H264.432V16.8468H273.694V18.8288H264.432V26.3603H274.491Z" fill="black"/>
<path d="M288.634 20.018H282.758V28.3423H280.666V7.53149H289.032C292.518 7.53149 295.307 10.3063 295.307 13.7747C295.307 16.5495 293.414 19.027 290.825 19.7207L295.904 28.3423H293.514L288.634 20.018ZM282.857 18.036H289.132C291.423 18.036 293.315 16.1531 293.315 13.7747C293.315 11.3964 291.423 9.51348 289.132 9.51348H282.857V18.036Z" fill="black"/>
<path d="M313.931 9.41435V17.045H323.194V19.027H313.931V28.2432H311.84V7.43237H323.592V9.41435H313.931Z" fill="black"/>
<path d="M328.672 17.9369C328.672 11.991 333.353 7.13513 339.528 7.13513C345.703 7.13513 350.285 11.991 350.285 17.9369C350.285 23.8829 345.603 28.7387 339.428 28.7387C333.253 28.7387 328.672 23.8829 328.672 17.9369ZM348.193 17.9369C348.193 13.0811 344.408 9.21621 339.428 9.21621C334.449 9.21621 330.664 13.0811 330.664 17.9369C330.664 22.7928 334.449 26.6576 339.428 26.6576C344.408 26.6576 348.193 22.7928 348.193 17.9369Z" fill="black"/>
<path d="M364.527 20.018H358.651V28.3423H356.559V7.53149H364.925C368.411 7.53149 371.2 10.3063 371.2 13.7747C371.2 16.5495 369.308 19.027 366.718 19.7207L371.798 28.3423H369.407L364.527 20.018ZM358.651 18.036H364.925C367.216 18.036 369.108 16.1531 369.108 13.7747C369.108 11.3964 367.216 9.51348 364.925 9.51348H358.651V18.036Z" fill="black"/>
<path d="M188.837 56.8829H178.877L176.885 62.036H174.694L182.762 41.2252H184.953L193.02 62.036H190.829L188.837 56.8829ZM188.14 54.9009L183.957 43.8018L179.774 54.9009H188.14Z" fill="black"/>
<path d="M206.366 53.7117H200.49V62.036H198.398V41.2252H206.765C210.251 41.2252 213.039 44 213.039 47.4685C213.039 50.2432 211.147 52.7207 208.557 53.4144L213.637 62.036H211.247L206.366 53.7117ZM200.49 51.7297H206.765C209.055 51.7297 210.948 49.8468 210.948 47.4685C210.948 45.0901 209.055 43.2072 206.765 43.2072H200.49V51.7297Z" fill="black"/>
<path d="M232.063 43.1081H225.589V61.9369H223.497V43.1081H217.023V41.1261H231.963V43.1081H232.063Z" fill="black"/>
<path d="M239.831 41.2252V62.036H237.739V41.2252H239.831Z" fill="black"/>
<path d="M249.392 43.1081V50.7387H258.655V52.7207H249.392V61.9369H247.301V41.1261H259.053V43.1081H249.392Z" fill="black"/>
<path d="M267.42 41.2252V62.036H265.328V41.2252H267.42Z" fill="black"/>
<path d="M273.694 51.6307C273.694 45.6847 278.276 40.8289 284.65 40.8289C288.534 40.8289 291.921 42.8108 293.713 45.982L291.821 47.0721C290.526 44.5946 287.738 42.9099 284.65 42.9099C279.371 42.9099 275.786 46.7748 275.786 51.6307C275.786 56.4865 279.371 60.3514 284.65 60.3514C287.738 60.3514 290.526 58.6667 291.821 56.1892L293.713 57.1802C292.02 60.2523 288.534 62.3334 284.65 62.3334C278.276 62.3334 273.694 57.5766 273.694 51.6307Z" fill="black"/>
<path d="M301.98 41.2252V62.036H299.888V41.2252H301.98Z" fill="black"/>
<path d="M321.401 56.8829H311.441L309.449 62.036H307.258L315.326 41.2252H317.517L325.584 62.036H323.393L321.401 56.8829ZM320.604 54.9009L316.421 43.8018L312.238 54.9009H320.604Z" fill="black"/>
<path d="M342.616 60.054V62.036H330.963V41.2252H333.054V60.054H342.616Z" fill="black"/>
<path d="M178.977 74.9189V95.7297H176.885V74.9189H178.977Z" fill="black"/>
<path d="M201.984 74.9189V95.7297H200.191L188.538 78.7837V95.7297H186.447V74.9189H188.24L199.892 91.8648V74.9189H201.984Z" fill="black"/>
<path d="M222.601 76.8017H216.127V95.6306H214.035V76.8017H207.562V74.8198H222.501V76.8017H222.601Z" fill="black"/>
<path d="M240.528 93.7477V95.7297H228.278V74.9189H240.429V76.9009H230.469V84.2342H239.732V86.2162H230.469V93.7477H240.528Z" fill="black"/>
<path d="M258.456 93.7477V95.7297H246.803V74.9189H248.894V93.7477H258.456Z" fill="black"/>
<path d="M275.786 93.7477V95.7297H264.133V74.9189H266.224V93.7477H275.786Z" fill="black"/>
<path d="M283.554 74.9189V95.7297H281.463V74.9189H283.554Z" fill="black"/>
<path d="M311.043 86.9099C311.043 91.8649 307.258 96.1261 300.784 96.1261C294.311 96.1261 289.829 91.2703 289.829 85.3243C289.829 79.3784 294.41 74.5225 300.784 74.5225C304.669 74.5225 308.055 76.6036 309.848 79.6757L307.955 80.7658C306.661 78.2883 303.872 76.6036 300.784 76.6036C295.506 76.6036 291.92 80.4685 291.92 85.3243C291.92 90.1802 295.506 94.045 300.784 94.045C305.665 94.045 308.453 91.2703 308.852 87.8018H300.585V85.8198H311.043V86.9099Z" fill="black"/>
<path d="M329.568 93.7477V95.7297H317.318V74.9189H329.369V76.9009H319.409V84.2342H328.672V86.2162H319.409V93.7477H329.568Z" fill="black"/>
<path d="M351.38 74.9189V95.7297H349.587L337.934 78.7837V95.7297H335.843V74.9189H337.636L349.288 91.8648V74.9189H351.38Z" fill="black"/>
<path d="M357.655 85.3243C357.655 79.3784 362.236 74.5225 368.611 74.5225C372.495 74.5225 375.881 76.5045 377.674 79.6757L375.782 80.7658C374.487 78.2883 371.698 76.6036 368.611 76.6036C363.332 76.6036 359.746 80.4685 359.746 85.3243C359.746 90.1802 363.332 94.045 368.611 94.045C371.698 94.045 374.487 92.3604 375.782 89.8829L377.674 90.8739C375.981 93.9459 372.495 96.027 368.611 96.027C362.137 96.027 357.655 91.2703 357.655 85.3243Z" fill="black"/>
<path d="M396 93.7477V95.7297H383.75V74.9189H395.9V76.9009H385.941V84.2342H395.203V86.2162H385.941V93.7477H396Z" fill="black"/>
</svg>

Before

Width:  |  Height:  |  Size: 7.5 KiB

View File

@ -1,8 +0,0 @@
<svg width="154" height="110" viewBox="0 0 154 110" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M126.771 80.1709V90.4772H116.398V86.1168H105.526V87.7024H100.738V86.1168H90.2656V90.4772H79.8926V80.1709H90.2656V84.5313H100.738V82.9457H105.526V84.5313H116.398V80.1709H126.771Z" fill="#E52713"/>
<path d="M147.617 58.171V68.4773H137.144V64.1169H116.398V68.4773H106.025V64.1169H88.1713V72.3422H69.9187V64.1169H49.1726V65.7025H44.385V64.1169H27.6286V68.4773H17.1558V58.171H27.6286V62.5314H44.385V60.9458H49.1726V62.5314H69.9187V54.207H88.1713V62.5314H106.025V58.171H116.398V62.5314H137.144V58.171H147.617Z" fill="#E52713"/>
<path d="M154 35.1802V45.4865H143.627V41.1262H129.663V42.7118H124.776V41.1262H110.014V47.964H94.6541V41.1262H81.4883V42.7118H76.7008V41.1262H64.0337V51.7298H40.9935V41.1262H30.82V45.4865H20.4469V41.1262H10.3731V45.4865H0V35.1802H10.3731V39.5406H20.4469V35.1802H30.82V39.5406H40.9935V28.8379H64.0337V39.5406H76.7008V37.955H81.4883V39.5406H94.6541V32.7028H110.014V39.5406H124.776V37.955H129.663V39.5406H143.627V35.1802H154Z" fill="#E52713"/>
<path d="M134.85 15.856V26.1623H124.477V21.8019H105.127V23.3875H100.34V21.8019H78.1971V26.1623H67.824V21.8019H32.815V26.1623H22.4419V15.856H32.815V20.2163H67.824V15.856H78.1971V20.2163H100.34V18.6307H105.127V20.2163H124.477V15.856H134.85Z" fill="#E52713"/>
<path d="M105.028 0V10.3063H94.6548V5.94594H81.9877V7.63063H77.2002V5.94594H64.6328V10.3063H54.2598V0H64.6328V4.36036H77.2002V2.77477H81.9877V4.36036H94.6548V0H105.028Z" fill="#E52713"/>
<path d="M117.794 99.6938H107.421V110H117.794V99.6938Z" fill="#E52713"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -1,33 +0,0 @@
<svg width="221" height="88" viewBox="0 0 221 88" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1.68457 10.6815C1.68457 4.80179 6.23903 0 12.5757 0C16.437 0 19.8034 1.95992 21.5856 5.09578L19.7044 6.17373C18.4172 3.72384 15.645 2.05791 12.5757 2.05791C7.32813 2.05791 3.76378 5.87974 3.76378 10.6815C3.76378 15.4833 7.32813 19.3052 12.5757 19.3052C15.645 19.3052 18.4172 17.6392 19.7044 15.1893L21.5856 16.1693C19.9024 19.2072 16.437 21.2651 12.5757 21.2651C6.23903 21.2651 1.68457 16.5613 1.68457 10.6815Z" fill="#343434"/>
<path d="M39.9019 19.0113V20.9712H27.7236V0.39209H39.8028V2.352H29.9018V9.60369H39.1098V11.5636H29.9018V19.0113H39.9019Z" fill="#343434"/>
<path d="M61.5857 0.39209V20.9712H59.8035L48.2193 4.21392V20.9712H46.1401V0.39209H47.9223L59.5065 17.1494V0.39209H61.5857Z" fill="#343434"/>
<path d="M82.0804 2.25337H75.6447V20.8726H73.5655V2.25337H67.1299V0.293457H81.9814V2.25337H82.0804Z" fill="#343434"/>
<path d="M99.9023 19.0113V20.9712H87.7241V0.39209H99.8033V2.352H89.9023V9.60369H99.1103V11.5636H89.9023V19.0113H99.9023Z" fill="#343434"/>
<path d="M113.962 12.7396H108.12V20.9712H106.041V0.39209H114.358C117.823 0.39209 120.595 3.13597 120.595 6.56582C120.595 9.3097 118.714 11.7596 116.14 12.4456L121.19 20.9712H118.813L113.962 12.7396ZM108.219 10.7796H114.457C116.734 10.7796 118.615 8.91772 118.615 6.56582C118.615 4.21392 116.734 2.352 114.457 2.352H108.219V10.7796Z" fill="#343434"/>
<path d="M139.11 2.25337V9.79904H148.318V11.759H139.11V20.8726H137.031V0.293457H148.714V2.25337H139.11Z" fill="#343434"/>
<path d="M153.764 10.6815C153.764 4.80179 158.417 0 164.556 0C170.694 0 175.249 4.80179 175.249 10.6815C175.249 16.5613 170.595 21.3631 164.457 21.3631C158.318 21.3631 153.764 16.5613 153.764 10.6815ZM173.17 10.6815C173.17 5.87974 169.407 2.05791 164.457 2.05791C159.506 2.05791 155.744 5.87974 155.744 10.6815C155.744 15.4833 159.506 19.3052 164.457 19.3052C169.407 19.3052 173.17 15.4833 173.17 10.6815Z" fill="#343434"/>
<path d="M189.407 12.7396H183.566V20.9712H181.486V0.39209H189.803C193.269 0.39209 196.041 3.13597 196.041 6.56582C196.041 9.3097 194.16 11.7596 191.585 12.4456L196.635 20.9712H194.259L189.407 12.7396ZM183.566 10.7796H189.803C192.08 10.7796 193.962 8.91772 193.962 6.56582C193.962 4.21392 192.08 2.352 189.803 2.352H183.566V10.7796Z" fill="#343434"/>
<path d="M14.7537 49.1938H4.85275L2.87255 54.2896H0.694336L8.71414 33.7104H10.8924L18.9122 54.2896H16.7339L14.7537 49.1938ZM14.0607 47.2339L9.90226 36.2583L5.74385 47.2339H14.0607Z" fill="#343434"/>
<path d="M32.1796 46.0579H26.338V54.2896H24.2588V33.7104H32.5756C36.041 33.7104 38.8132 36.4543 38.8132 39.8842C38.8132 42.6281 36.9321 45.078 34.3578 45.7639L39.4073 54.2896H37.0311L32.1796 46.0579ZM26.338 44.098H32.5756C34.8528 44.098 36.734 42.2361 36.734 39.8842C36.734 37.5323 34.8528 35.6704 32.5756 35.6704H26.338V44.098Z" fill="#343434"/>
<path d="M57.7244 35.5722H51.2888V54.1914H49.2096V35.5722H42.7739V33.6123H57.6254V35.5722H57.7244Z" fill="#343434"/>
<path d="M65.4464 33.7104V54.2896H63.3672V33.7104H65.4464Z" fill="#343434"/>
<path d="M74.9518 35.5722V43.1179H84.1597V45.0778H74.9518V54.1914H72.8726V33.6123H84.5557V35.5722H74.9518Z" fill="#343434"/>
<path d="M92.8726 33.7104V54.2896H90.7935V33.7104H92.8726Z" fill="#343434"/>
<path d="M99.1104 43.9999C99.1104 38.1202 103.665 33.3184 110.001 33.3184C113.863 33.3184 117.229 35.2783 119.011 38.4141L117.13 39.4921C115.843 37.0422 113.071 35.3763 110.001 35.3763C104.754 35.3763 101.19 39.1981 101.19 43.9999C101.19 48.8017 104.754 52.6235 110.001 52.6235C113.071 52.6235 115.843 50.9576 117.13 48.5077L119.011 49.4877C117.328 52.5255 113.863 54.5834 110.001 54.5834C103.665 54.5834 99.1104 49.8796 99.1104 43.9999Z" fill="#343434"/>
<path d="M127.229 33.7104V54.2896H125.149V33.7104H127.229Z" fill="#343434"/>
<path d="M146.535 49.1938H136.635L134.654 54.2896H132.476L140.496 33.7104H142.674L150.694 54.2896H148.516L146.535 49.1938ZM145.743 47.2339L141.585 36.2583L137.427 47.2339H145.743Z" fill="#343434"/>
<path d="M167.625 52.3296V54.2896H156.041V33.7104H158.12V52.3296H167.625Z" fill="#343434"/>
<path d="M4.95177 67.0288V87.6079H2.87256V67.0288H4.95177Z" fill="#343434"/>
<path d="M27.823 67.0288V87.6079H26.0408L14.4567 70.8506V87.6079H12.3774V67.0288H14.1596L25.7438 83.7861V67.0288H27.823Z" fill="#343434"/>
<path d="M48.3182 68.8911H41.8825V87.5103H39.8033V68.8911H33.3677V66.9312H48.2192V68.8911H48.3182Z" fill="#343434"/>
<path d="M66.1401 85.648V87.6079H53.9619V67.0288H66.0411V68.9887H56.1401V76.2404H65.3481V78.2003H56.1401V85.648H66.1401Z" fill="#343434"/>
<path d="M83.9616 85.648V87.6079H72.3774V67.0288H74.4567V85.648H83.9616Z" fill="#343434"/>
<path d="M101.189 85.648V87.6079H89.605V67.0288H91.6842V85.648H101.189Z" fill="#343434"/>
<path d="M108.912 67.0288V87.6079H106.833V67.0288H108.912Z" fill="#343434"/>
<path d="M136.239 78.8862C136.239 83.786 132.476 87.9998 126.041 87.9998C119.605 87.9998 115.149 83.198 115.149 77.3183C115.149 71.4385 119.704 66.6367 126.041 66.6367C129.902 66.6367 133.268 68.6946 135.05 71.7325L133.169 72.8104C131.882 70.3606 129.11 68.6946 126.041 68.6946C120.793 68.6946 117.229 72.5165 117.229 77.3183C117.229 82.12 120.793 85.9419 126.041 85.9419C130.892 85.9419 133.664 83.198 134.06 79.7681H125.843V77.8082H136.239V78.8862Z" fill="#343434"/>
<path d="M154.655 85.648V87.6079H142.477V67.0288H154.457V68.9887H144.556V76.2404H153.764V78.2003H144.556V85.648H154.655Z" fill="#343434"/>
<path d="M176.338 67.0288V87.6079H174.555L162.971 70.8506V87.6079H160.892V67.0288H162.674L174.258 83.7861V67.0288H176.338Z" fill="#343434"/>
<path d="M182.575 77.3183C182.575 71.4385 187.13 66.6367 193.466 66.6367C197.328 66.6367 200.694 68.5966 202.476 71.7325L200.595 72.8104C199.308 70.3606 196.536 68.6946 193.466 68.6946C188.219 68.6946 184.654 72.5165 184.654 77.3183C184.654 82.12 188.219 85.9419 193.466 85.9419C196.536 85.9419 199.308 84.2759 200.595 81.8261L202.476 82.806C200.793 85.8439 197.328 87.9018 193.466 87.9018C187.031 87.9018 182.575 83.198 182.575 77.3183Z" fill="#343434"/>
<path d="M220.694 85.648V87.6079H208.516V67.0288H220.595V68.9887H210.694V76.2404H219.902V78.2003H210.694V85.648H220.694Z" fill="#343434"/>
</svg>

Before

Width:  |  Height:  |  Size: 6.0 KiB

View File

@ -1,3 +0,0 @@
<svg width="33" height="33" viewBox="0 0 33 33" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M31.3571 2.24695C31.9857 2.24695 32.5 2.69695 32.5 3.24695V5.24695C32.5 5.79695 31.9857 6.24695 31.3571 6.24695H1.64286C1.01429 6.24695 0.5 5.79695 0.5 5.24695V3.24695C0.5 2.69695 1.01429 2.24695 1.64286 2.24695H10.2143L10.8857 1.0782C11.1429 0.621948 11.8214 0.246948 12.4071 0.246948H12.4143H20.5786C21.1643 0.246948 21.85 0.621948 22.1143 1.0782L22.7857 2.24695H31.3571ZM4.3 29.4344L2.78571 8.24695H30.2143L28.7 29.4344C28.5857 30.9844 27.0571 32.2469 25.2786 32.2469H7.72143C5.94286 32.2469 4.41429 30.9844 4.3 29.4344Z" fill="#343434"/>
</svg>

Before

Width:  |  Height:  |  Size: 654 B

View File

@ -1,3 +0,0 @@
<svg width="33" height="33" viewBox="0 0 33 33" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1.39729 27.0172C0.82276 27.5917 0.5 28.3709 0.5 29.1834C0.5 29.9959 0.82276 30.7751 1.39729 31.3497C1.97181 31.9242 2.75104 32.2469 3.56354 32.2469C4.37604 32.2469 5.15526 31.9242 5.72978 31.3497C9.33002 27.7494 12.8998 24.1492 16.5 20.61L27.2702 31.3497C27.5547 31.6341 27.8924 31.8598 28.2641 32.0138C28.6358 32.1677 29.0342 32.2469 29.4365 32.2469C29.8388 32.2469 30.2371 32.1677 30.6088 32.0138C30.9805 31.8598 31.3182 31.6341 31.6027 31.3497C31.8872 31.0652 32.1128 30.7275 32.2668 30.3558C32.4208 29.9841 32.5 29.5857 32.5 29.1834C32.5 28.7811 32.4208 28.3827 32.2668 28.011C32.1128 27.6394 31.8872 27.3016 31.6027 27.0172C28.0025 23.4779 24.4022 19.8472 20.863 16.2469C23.3649 13.6841 25.8667 11.2127 28.3686 8.71085C29.467 7.61247 30.5043 6.51409 31.6027 5.47673C32.1772 4.9022 32.5 4.12298 32.5 3.31048C32.5 2.49798 32.1772 1.71876 31.6027 1.14423C31.0282 0.569709 30.249 0.246948 29.4365 0.246948C28.624 0.246948 27.8447 0.569709 27.2702 1.14423L16.5 11.9328L5.72978 1.14423C5.15526 0.569709 4.37604 0.246948 3.56354 0.246948C2.75104 0.246948 1.97181 0.569709 1.39729 1.14423C0.82276 1.71876 0.5 2.49798 0.5 3.31048C0.5 4.12298 0.82276 4.9022 1.39729 5.47673L12.1858 16.2469L1.39729 27.0172Z" fill="#343434"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1,105 @@
import React from 'react';
import {FlexColumn, FlexRow, ImageBackground} from '../../utils/containers';
import {Body, H2, Medium} from '../../utils/fonts';
import CircleNumber from '../generic/CircleNumber';
import Media from 'react-media';
import theme from '../../utils/theme';
import commercialImage from '../../assets/commercial-image.svg';
const Commercial = () => {
const listItemsContent = [
'A company comes to CSI with a business need',
'CSI determines the need with an appropriate challenge on Gonito',
'The challenge is solved by willing users',
'The company appropriately rewards users who have contributed to the required outcome',
];
const mobileRender = () => {
return (
<FlexColumn as='section' alignmentX='flex-start'>
<H2 as='h2' margin='0 0 24px 0'>
Commercial challenges
</H2>
<FlexColumn gap='20px'>
<Body as='p'>
The artificial intelligence center works with companies by accepting
machine learning challenges from them that are available to solve on Gonito.
Each commercial challenge is properly scored which translates into an award
for solving it according to the client's requirements.
</Body>
<FlexColumn as='ul' gap='16px' alignmentX='flex-start'>
{
listItemsContent.map((item, index) => {
return (
<FlexRow key={`commercial-item-${index}`} width='100%' gap='8px'>
<CircleNumber number={String(index + 1)}/>
<Medium width='80%' as='li'>
{item}
</Medium>
</FlexRow>
);
})
}
</FlexColumn>
<Body as='p'>
Open challenges can allow you to find the right people to work with.
Find a challenge for your team and take it on!
</Body>
</FlexColumn>
</FlexColumn>
);
};
const desktopRender = () => {
return (
<FlexRow gap='46px'>
<FlexColumn as='section' alignmentX='flex-start' maxWidth='490px'>
<H2 as='h2' margin='0 0 48px 0'>
Commercial challenges
</H2>
<FlexColumn gap='32px'>
<Body as='p'>
The artificial intelligence center works with companies by accepting
machine learning challenges from them that are available to solve on Gonito.
Each commercial challenge is properly scored which translates into an award
for solving it according to the client's requirements.
</Body>
<FlexColumn as='ul' gap='24px' alignmentX='flex-start'>
{
listItemsContent.map((item, index) => {
return (
<FlexRow key={`commercial-item-${index}`} width='100%' gap='16px'>
<CircleNumber number={String(index + 1)}/>
<Medium width='80%' as='li'>
{item}
</Medium>
</FlexRow>
);
})
}
</FlexColumn>
<Body as='p'>
Open challenges can allow you to find the right people to work with.
Find a challenge for your team and take it on!
</Body>
</FlexColumn>
</FlexColumn>
<ImageBackground image={commercialImage} width='200px' height='284px' size='contain'/>
</FlexRow>
);
};
return (
<>
<Media query={theme.mobile}>
{mobileRender()}
</Media>
<Media query={theme.desktop}>
{desktopRender()}
</Media>
</>
);
};
export default Commercial;

View File

@ -1,9 +1,9 @@
import React from 'react';
import { FlexColumn, FlexRow, Svg } from '../../../../utils/containers';
import { Body, H2, Medium } from '../../../../utils/fonts';
import { FlexColumn, FlexRow, Svg } from '../../utils/containers';
import { Body, H2, Medium } from '../../utils/fonts';
import Media from 'react-media';
import theme from '../../../../utils/theme';
import csiLogo from '../../../../assets/csi_logo.svg';
import theme from '../../utils/theme';
import uamLogo from '../../assets/uam-logo.svg';
const Csi = () => {
const mobileRender = () => {
@ -37,31 +37,33 @@ const Csi = () => {
const desktopRender = () => {
return (
<FlexColumn as="section" alignmentX="flex-start" maxWidth="640px">
<FlexRow margin="0 0 48px 0">
<H2 as="h2">Center for Artificial Intelligence (C4AI)</H2>
<Svg src={csiLogo} size="contain" width="230px" height="80px" />
</FlexRow>
<Body as="p" margin="0 0 24px 0">
<Medium as="span" display="inline">
Gonito.net
</Medium>{' '}
belongs to the
<Medium as="span" display="inline">
&nbsp;Center for Artificial Intelligence (C4AI)&nbsp;
<FlexRow gap="46px">
<FlexColumn as="section" alignmentX="flex-start" maxWidth="490px">
<H2 as="h2" margin="0 0 48px 0">
Center for Artificial Intelligence (C4AI)
</H2>
<Body as="p" margin="0 0 24px 0">
<Medium as="span" display="inline">
Gonito.net
</Medium>{' '}
belongs to the
<Medium as="span" display="inline">
&nbsp;Center for Artificial Intelligence (C4AI)&nbsp;
</Medium>
at Adam Mickiewicz University (UAM) which conducts research on the
development of artificial intelligence, carries out scientific and
research and development projects, integrates the research of
scientists from various departments of Adam Mickiewicz University
and outside it - from leading scientific centers in Poland and
abroad as well as those employed in business entities.
</Body>
<Medium as="p">
C4AI also cooperates with business entities in creating new
solutions to be implemented in enterprises.
</Medium>
at Adam Mickiewicz University (UAM) which conducts research on the
development of artificial intelligence, carries out scientific and
research and development projects, integrates the research of
scientists from various departments of Adam Mickiewicz University and
outside it - from leading scientific centers in Poland and abroad as
well as those employed in business entities.
</Body>
<Medium as="p">
C4AI also cooperates with business entities in creating new solutions
to be implemented in enterprises.
</Medium>
</FlexColumn>
</FlexColumn>
<Svg src={uamLogo} width="200px" height="242px" size="contain" />
</FlexRow>
);
};

View File

@ -0,0 +1,45 @@
import React from 'react';
import {Container, FlexRow} from '../../utils/containers';
import styled from 'styled-components';
import {Medium} from '../../utils/fonts';
import {CSI_LINK} from '../../utils/globals';
const FooterStyle = styled(FlexRow)`
width: 100%;
height: 48px;
display: flex;
justify-content: center;
align-items: center;
background-color: ${({theme}) => theme.colors.green};
z-index: 1000;
position: relative;
p, a {
color: ${({theme}) => theme.colors.white}
}
a {
text-decoration: underline;
cursor: pointer;
}
@media (min-width: ${({theme}) => theme.overMobile}) {
height: 72px;
}
`;
const Footer = () => {
return (
<FooterStyle as='footer'>
<Medium as='p'>
Read more about&nbsp;
<Container as='a' display='inline' target='_blank'
href={CSI_LINK}>
CSI
</Container>
</Medium>
</FooterStyle>
);
};
export default Footer;

View File

@ -0,0 +1,93 @@
import React from 'react';
import { Body, H1, Medium } from '../../utils/fonts';
import { Container, FlexColumn, FlexRow, Svg } from '../../utils/containers';
import theme from '../../utils/theme';
import ButtonLink from '../generic/ButtonLink';
import Media from 'react-media';
import codepenIco from '../../assets/codepen_ico.svg';
import styled from 'styled-components';
import KeyCloakService from '../../services/KeyCloakService';
const TitleParagraph = styled(Medium)`
font-size: 20px;
line-height: 28px;
`;
const Hero = (props) => {
const mobileRender = () => {
return (
<FlexColumn alignmentX="flex-start" gap="24px" maxWidth="452px">
<H1 as="h1">
Welcome to
<Container display="inline" color={theme.colors.green}>
&nbsp;Gonito!
</Container>
</H1>
<Body as="p">
A data challenge platform for machine learning research, competition,
cooperation and reproducibility.
</Body>
<ButtonLink
as="button"
onClick={() =>
props.popUpMessageHandler(
'Reminder',
'Remember to check your spam mailbox to confirm your account.',
() => KeyCloakService.doRegister
)
}
>
Join us!
</ButtonLink>
</FlexColumn>
);
};
const desktopRender = () => {
return (
<FlexColumn alignmentX="flex-start" gap="24px">
<H1 as="h1">
Welcome to
<Container display="inline" color={theme.colors.green}>
&nbsp;Gonito!
</Container>
</H1>
<FlexRow gap="20px">
<Container>
<TitleParagraph as="p" maxWidth="286px" margin="0 0 20px 0">
A data challenge platform for machine learning research,
competition, cooperation and reproducibility.
</TitleParagraph>
<ButtonLink
as="button"
onClick={() =>
props.popUpMessageHandler(
'Reminder',
'Remember to check your spam mailbox to confirm your account.',
() => KeyCloakService.doRegister
)
}
>
Join us!
</ButtonLink>
</Container>
<Svg
src={codepenIco}
width="180px"
height="150px"
size="contain"
backgroundColor={theme.colors.green}
/>
</FlexRow>
</FlexColumn>
);
};
return (
<>
<Media query={theme.mobile}>{mobileRender()}</Media>
<Media query={theme.desktop}>{desktopRender()}</Media>
</>
);
};
export default Hero;

View File

@ -0,0 +1,82 @@
import React from 'react';
import {FlexColumn, FlexRow, ImageBackground, Svg} from '../../utils/containers';
import {Body, H2} from '../../utils/fonts';
import cubeIcon from '../../assets/cube_ico.svg';
import theme from '../../utils/theme';
import Media from 'react-media';
import ellipse from '../../assets/ellipse.svg';
const Motivation = () => {
const content = [
'Explore interesting solutions to problems using AI',
'Train by solving our challenges',
'Participate in competitions with commercial challenges'
];
const mobileRender = () => {
return (
<FlexColumn as='section' alignmentX='flex-start' gap='24px' width='100%'>
<H2 as='h2'>
Motivation
</H2>
<FlexColumn as='ul' gap='16px' alignmentX='flex-start'>
{
content.map((paragraph, index) => {
return (
<FlexRow key={`motivation-${index}`} as='li' gap='12px' alignmentX='flex-start'
alignmentY='flex-start'>
<Svg src={cubeIcon} width='14px' height='14px' margin='4px 0 0 0'
backgroundColor={theme.colors.green}/>
<Body as='p' width='90%'>
{paragraph}
</Body>
</FlexRow>
);
})
}
</FlexColumn>
</FlexColumn>
);
};
const desktopRender = () => {
return (
<ImageBackground as='section' image={ellipse}
gap='48px' width='612px' height='458px'>
<H2 as='h2'>
Motivation
</H2>
<FlexColumn as='ul' gap='22px' alignmentX='flex-start'>
{
content.map((paragraph, index) => {
return (
<FlexRow key={`motivation-${index}`} as='li' gap='16px' alignmentY='flex-start'>
<Svg src={cubeIcon} width='20px' height='20px' size='cover' margin='2px 0 0 0'
backgroundColor={theme.colors.green}/>
<Body as='p' maxWidth='380px'>
{paragraph}
</Body>
</FlexRow>
);
})
}
</FlexColumn>
</ImageBackground>
);
};
return (
<>
<Media query={theme.mobile}>
{mobileRender()}
</Media>
<Media query={theme.desktop}>
{desktopRender()}
</Media>
</>
);
};
export default Motivation;

View File

@ -0,0 +1,48 @@
import React from 'react';
import {FlexColumn, Grid} from '../../utils/containers';
import {H2} from '../../utils/fonts';
import Placeholder from '../generic/Placeholder';
import styled from 'styled-components';
const PartnershipsStyle = styled(FlexColumn)`
justify-content: flex-start;
gap: 32px;
@media (min-width: ${({theme}) => theme.overMobile}) {
gap: 64px;
.grid {
grid-template-rows: 1fr 1fr;
grid-template-columns: 1fr 1fr;
grid-gap: 64px;
}
}
`;
const Partnerships = () => {
return (
<PartnershipsStyle as='section'>
<H2 as='h2'>
Our partnerships
</H2>
<FlexColumn width='100%'>
<Grid className='grid' gridGap='32px 0'>
<Placeholder>
1
</Placeholder>
<Placeholder>
2
</Placeholder>
<Placeholder>
3
</Placeholder>
<Placeholder>
4
</Placeholder>
</Grid>
</FlexColumn>
</PartnershipsStyle>
);
};
export default Partnerships;

View File

@ -1,14 +1,14 @@
import React from 'react';
import styled from 'styled-components';
import { Label } from '../../utils/fonts';
import {Label} from '../../utils/fonts';
import PropsTypes from 'prop-types';
const ButtonLinkStyle = styled(Label)`
background-color: ${({ theme }) => theme.colors.green};
color: ${({ theme }) => theme.colors.white};
background-color: ${({theme}) => theme.colors.green};
color: ${({theme}) => theme.colors.white};
border-radius: 12px;
text-align: center;
width: 144px;
width: 122px;
padding: 4px 0;
cursor: pointer;
transition: transform 0.3s ease-in-out;
@ -18,32 +18,32 @@ const ButtonLinkStyle = styled(Label)`
transform: scale(1.15);
}
@media (min-width: ${({ theme }) => theme.overMobile}) {
@media (min-width: ${({theme}) => theme.overMobile}) {
display: flex;
justify-content: center;
align-items: center;
height: 48px;
height: 44px;
}
`;
const ButtonLink = (props) => {
return (
<ButtonLinkStyle as={props.as} onClick={props.onClick}>
{props.children}
</ButtonLinkStyle>
);
return (
<ButtonLinkStyle as={props.as} onClick={props.onClick}>
{props.children}
</ButtonLinkStyle>
);
};
ButtonLink.propTypes = {
as: PropsTypes.string,
onClick: PropsTypes.func,
children: PropsTypes.node,
as: PropsTypes.string,
onClick: PropsTypes.func,
children: PropsTypes.node,
};
ButtonLink.defaultProps = {
children: '',
as: 'div',
onClick: null,
children: '',
as: 'div',
onClick: null,
};
export default ButtonLink;
export default ButtonLink;

View File

@ -1,14 +0,0 @@
import styled from 'styled-components';
import { FlexColumn } from '../../../utils/containers';
const EntireScreenLoadingStyle = styled(FlexColumn)`
width: 100%;
height: 100vh;
z-index: 1000;
position: fixed;
top: 0;
left: 0;
background-color: ${({ theme }) => theme.colors.white};
`;
export default EntireScreenLoadingStyle;

View File

@ -1,13 +0,0 @@
import React from 'react';
import EntireScreenLoadingStyle from './EntireScreenLoadingStyle';
import Loading from '../Loading';
const EntireScreenLoading = () => {
return (
<EntireScreenLoadingStyle>
<Loading />
</EntireScreenLoadingStyle>
);
};
export default EntireScreenLoading;

View File

@ -1,29 +1,24 @@
import React from 'react';
import { H1 } from '../../utils/fonts';
import {H1} from '../../utils/fonts';
import theme from '../../utils/theme';
import { Link } from 'react-router-dom';
import {Link} from 'react-router-dom';
import styled from 'styled-components';
const LogoStyle = styled(H1)`
font-size: 24px;
@media (min-width: ${({ theme }) => theme.overMobile}) {
@media (min-width: ${({theme}) => theme.overMobile}) {
font-size: 32px;
line-height: 32px;
}
`;
const Logo = (props) => {
return (
<LogoStyle
as={props.navOptions ? Link : 'span'}
cursor="pointer"
to="/"
color={theme.colors.green}
>
Gonito
</LogoStyle>
);
const Logo = () => {
return (
<LogoStyle as={Link} cursor='pointer' to='/' color={theme.colors.green}>
Gonito
</LogoStyle>
);
};
export default Logo;
export default Logo;

View File

@ -63,7 +63,7 @@ const Pager = (props) => {
as="a"
href="#start"
src={polygon}
onClick={() => PREVIOUS_PAGE(props.pageNr, props.setPageNr)}
onClick={() => PREVIOUS_PAGE(props.pageNr, props.setPage)}
size="cover"
backgroundColor={leftArrowVisible()}
/>
@ -76,7 +76,7 @@ const Pager = (props) => {
as="a"
href="#start"
src={polygon}
onClick={() => NEXT_PAGE(props.elements, props.pageNr, props.setPageNr)}
onClick={() => NEXT_PAGE(props.elements, props.pageNr, props.setPage)}
size="cover"
backgroundColor={rightArrowVisible()}
/>

View File

@ -9,12 +9,12 @@ const PopUpStyle = styled(FlexColumn)`
z-index: 100;
width: 100%;
height: 100vh;
background-color: ${({ theme, backgroundColor }) =>
backgroundColor ? backgroundColor : theme.colors.dark01};
background-color: ${({ theme }) => theme.colors.dark01};
.PopUpStyle__body {
width: ${({ width }) => (width ? width : '60%')};
height: ${({ height }) => (height ? height : '50%')};
min-height: ${({ minHeight }) => (minHeight ? minHeight : '50%')};
padding: ${({ padding }) => (padding ? padding : '48px')};
margin: ${({ margin }) => (margin ? margin : '0')};
border-radius: 12px;
@ -32,7 +32,6 @@ const PopUp = (props) => {
return (
<PopUpStyle
backgroundColor={props.backgroundColor}
padding={props.padding}
width={props.width}
height={props.height}

View File

@ -28,13 +28,13 @@ const PopupMessage = (props) => {
borderRadius="12px"
backgroundColor={theme.colors.white}
padding="56px"
border={`4px solid ${props.borderColor ? props.borderColor : theme.colors.green}`}
border={`4px solid ${theme.colors.green}`}
>
<FlexColumn gap="48px" margin="0 0 48px 0">
<H3>{props.header}</H3>
<Body>{props.message}</Body>
</FlexColumn>
<Button backgroundColor={props.borderColor ? props.borderColor : theme.colors.green} handler={confirmPopUp}>Ok</Button>
<Button handler={confirmPopUp}>Ok</Button>
</FlexColumn>
</FlexColumn>
);

View File

@ -18,7 +18,6 @@ const SubmitInput = (props) => {
height="36px"
border={`1px solid ${theme.colors.dark}`}
shadow={theme.shadow}
defaultValue={props.defaultValue}
onChange={(e) => props.handler(e.target.value)}
padding="4px"
/>

View File

@ -0,0 +1,283 @@
import React from 'react';
import { Container, 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 styled from 'styled-components';
import ColumnFilterIcon from './ColumnFilterIcon';
const TableStyle = styled(FlexColumn)`
overflow-x: ${({metrics}) => metrics > 10 ? 'scroll' : 'auto'};
`;
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 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;
}
`;
const Table = (props) => {
const [, updateState] = React.useState();
const forceUpdate = 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] === '-1')
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"
>
{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"
// minWidth={elem === 'result' ? '72px' : 'none'}
>
{elem.replace('.', ' ')}
</Medium>
{elem !== '#' && (
<ColumnFilterIcon
cursor="pointer"
index={i}
active={activeIcon}
rotateIcon={rotateActiveIcon}
/>
)}
</FlexRow>
);
})}
<Line
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 (
<>
<Media query={theme.mobile}>{mobileRender()}</Media>
<Media query={theme.desktop}>{desktopRender()}</Media>
</>
);
};
export default Table;

View File

@ -1,81 +0,0 @@
import React from 'react';
import Media from 'react-media';
import theme from '../../../utils/theme';
import MobileTable from './components/MobileTable';
import DesktopTable from './components/DesktopTable';
import EditPopUp from './components/EditPopUp';
import DeletePopUp from './components/DeletePopUp';
const Table = ({
items,
orderedKeys,
popUpMessageHandler,
sortByUpdate,
profileInfo,
rowFooter = true,
}) => {
const [, updateState] = React.useState();
const tableUpdate = React.useCallback(() => updateState({}), []);
const [deletedItems, setDeletedItems] = React.useState([]);
const [deletePopUp, setDeletePopUp] = React.useState(false);
const [editPopUp, setEditPopUp] = React.useState(false);
const [itemToHandle, setItemToHandle] = React.useState(null);
const itemsToRender = items.filter((item) => !deletedItems.includes(item));
return (
<>
<DeletePopUp
item={itemToHandle}
setDeletePopUp={setDeletePopUp}
deletePopUp={deletePopUp}
setDeletedItems={setDeletedItems}
deletedItems={deletedItems}
popUpMessageHandler={popUpMessageHandler}
/>
<EditPopUp
item={itemToHandle}
setEditPopUp={setEditPopUp}
editPopUp={editPopUp}
popUpMessageHandler={popUpMessageHandler}
/>
<Media query={theme.mobile}>
<MobileTable
elements={itemsToRender}
deletePopUp={deletePopUp}
orderedKeys={orderedKeys}
rowFooter={rowFooter}
deletedItems={deletedItems}
popUpMessageHandler={popUpMessageHandler}
itemToHandle={itemToHandle}
sortByUpdate={sortByUpdate}
profileInfo={profileInfo}
tableUpdate={tableUpdate}
setItemToHandle={setItemToHandle}
setEditPopUp={setEditPopUp}
setDeletePopUp={setDeletePopUp}
setDeletedItems={setDeletedItems}
/>
</Media>
<Media query={theme.desktop}>
<DesktopTable
elements={itemsToRender}
deletePopUp={deletePopUp}
orderedKeys={orderedKeys}
rowFooter={rowFooter}
deletedItems={deletedItems}
popUpMessageHandler={popUpMessageHandler}
itemToHandle={itemToHandle}
sortByUpdate={sortByUpdate}
profileInfo={profileInfo}
tableUpdate={tableUpdate}
setItemToHandle={setItemToHandle}
setEditPopUp={setEditPopUp}
setDeletePopUp={setDeletePopUp}
setDeletedItems={setDeletedItems}
/>
</Media>
</>
);
};
export default Table;

View File

@ -1,74 +0,0 @@
import { createPortal } from 'react-dom';
import PopUp from '../../../PopUp';
import Button from '../../../Button';
import { Medium, H3 } from '../../../../../utils/fonts';
import { FlexColumn, FlexRow } from '../../../../../utils/containers';
import theme from '../../../../../utils/theme';
import deleteSubmission from '../../../../../api/deleteSubmission';
const deleteItem = async (
item,
setDeletePopUp,
deletedItems,
setDeletedItems,
popUpMessageHandler
) => {
setDeletePopUp(false);
await deleteSubmission(
item,
deletedItems,
setDeletedItems,
popUpMessageHandler
);
};
const DeletePopUp = ({
deletePopUp,
setDeletePopUp,
item,
deletedItems,
setDeletedItems,
popUpMessageHandler,
}) => {
if (deletePopUp) {
return createPortal(
<PopUp
width="30%"
height="30vh"
padding="32px"
backgroundColor={theme.colors.dark003}
closeHandler={() => setDeletePopUp(false)}
>
<FlexColumn width="100%" height="100%" gap="48px">
<H3>Warning</H3>
<Medium>
Are you sure want to delete submission with id: {item.id}?
</Medium>
<FlexRow gap="48px">
<Button
handler={() =>
deleteItem(
item,
deletedItems,
setDeletedItems,
popUpMessageHandler
)
}
>
Yes
</Button>
<Button
handler={() => setDeletePopUp(false)}
backgroundColor={theme.colors.dark}
>
No
</Button>
</FlexRow>
</FlexColumn>
</PopUp>,
document.body
);
}
};
export default DeletePopUp;

View File

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

View File

@ -1,48 +0,0 @@
import React from 'react';
import TableStyle from '../../styles/TableStyle';
import TableHeader from '../TableHeader/TableHeader';
import TableRowItems from '../TableRowItems/TableRowItems';
import TableRowFooter from '../TableRowFooter/TableRowFooter';
import RowsBackgroundStyle from '../../styles/RowsBackgroundStyle';
const DesktopTable = (props) => {
return (
<TableStyle rowFooter={props.rowFooter}>
<tbody>
<TableHeader
orderedKeys={props.orderedKeys}
sortByUpdate={props.sortByUpdate}
tableUpdate={props.tableUpdate}
/>
{props.elements.map((item, i) => {
return (
<tr key={`table-row-${i}`} className="TableStyle__tr">
<TableRowItems
orderedKeys={props.orderedKeys}
item={item}
i={i}
/>
<TableRowFooter
deleteItem={() => {
props.setItemToHandle(item);
props.setDeletePopUp(true);
}}
editItem={() => {
props.setItemToHandle(item);
props.setEditPopUp(true);
}}
rowFooter={props.rowFooter}
profileInfo={props.profileInfo}
item={item}
i={i}
/>
<RowsBackgroundStyle i={i} as="td" />
</tr>
);
})}
</tbody>
</TableStyle>
);
};
export default DesktopTable;

View File

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

View File

@ -1,105 +0,0 @@
import { createPortal } from 'react-dom';
import PopUp from '../../../PopUp';
import Button from '../../../Button';
import { H3 } from '../../../../../utils/fonts';
import { FlexColumn, FlexRow } from '../../../../../utils/containers';
import theme from '../../../../../utils/theme';
import SubmitInput from '../../../SubmitInput';
import TagsChoose from '../../../../../pages/Submit/components/TagsChoose/TagsChoose';
import editSubmission from '../../../../../api/editSubmission';
import getTags from '../../../../../api/getTags';
import React from 'react';
const editSubmissionHandler = async (
item,
setEditPopUp,
tagsToEdit,
description,
popUpMessageHandler
) => {
setEditPopUp(false);
let tags = '';
if (tagsToEdit) {
tags = tagsToEdit.join(',');
} else {
if (item?.tags) {
tags = item.tags.map((tag) => tag.name).join(',');
}
}
await editSubmission(item.id, tags, description, popUpMessageHandler);
};
const EditPopUp = ({ editPopUp, setEditPopUp, item, popUpMessageHandler }) => {
const [tags, setTags] = React.useState([]);
const [tagsToEdit, setTagsToEdit] = React.useState(item?.tags?.slice());
const [description, setDescription] = React.useState(
item?.description?.slice()
);
React.useMemo(() => {
getTags(setTags);
}, []);
if (editPopUp) {
return createPortal(
<PopUp
width="30%"
height="50vh"
padding="32px"
backgroundColor={theme.colors.dark003}
closeHandler={() => setEditPopUp(false)}
>
<FlexColumn width="100%" height="100%" gap="48px">
<H3>Editing submission</H3>
<SubmitInput
label="Description"
defaultValue={item.description}
handler={(value) => {
setDescription(value);
}}
/>
<TagsChoose
label="Submission tags"
updateTags={(submissionTags, globalTags) => {
setTagsToEdit(submissionTags);
setTags(globalTags);
}}
tags={tags ? tags : []}
submissionTags={tagsToEdit?.length ? tagsToEdit : item.tags}
/>
<FlexRow gap="48px">
<Button
width="100px"
height="32px"
handler={() =>
editSubmissionHandler(
item,
setEditPopUp,
tagsToEdit,
description,
popUpMessageHandler
)
}
>
Confirm
</Button>
<Button
width="100px"
height="32px"
handler={() => {
setTagsToEdit([]);
setEditPopUp(false);
}}
backgroundColor={theme.colors.dark}
>
Cancel
</Button>
</FlexRow>
</FlexColumn>
</PopUp>,
document.body
);
}
};
export default EditPopUp;

View File

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

View File

@ -1,33 +0,0 @@
import React from 'react';
import TableRowItems from '../TableRowItems/TableRowItems';
import TableRowFooter from '../TableRowFooter/TableRowFooter';
import MobileTableStyle from './MobileTableStyle';
const MobileTable = (props) => {
return (
<MobileTableStyle as="table">
{props.elements.map((item, i) => {
return (
<tr key={`table-row-${i}`} className="TableStyle__tr">
<TableRowItems orderedKeys={props.orderedKeys} item={item} i={i} />
<TableRowFooter
deleteItem={() => {
props.setItemToHandle(item);
props.setDeletePopUp(true);
}}
editItem={() => {
props.setItemToHandle(item);
props.setEditPopUp(true);
}}
rowFooter={props.rowFooter}
item={item}
i={i}
/>
</tr>
);
})}
</MobileTableStyle>
);
};
export default MobileTable;

View File

@ -1,66 +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;
}
thead,
tbody,
th,
tr,
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: 500;
position: absolute;
top: 6px;
left: 6px;
width: 45%;
padding-right: 10px;
white-space: nowrap;
}
.TableStyle__row-footer {
padding: 0 8px;
min-height: 36px;
justify-content: space-between;
}
.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__tags-container {
gap: 4px;
}
`;
export default MobileTableStyle;

View File

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

View File

@ -1,50 +0,0 @@
import React from 'react';
import { FlexRow } from '../../../../../utils/containers';
import ColumnFilterIcon from '../../../ColumnFilterIcon';
const TableHeader = (props) => {
const [activeIcon, setActiveIcon] = React.useState(null);
const [rotateActiveIcon, setRotateActiveIcon] = React.useState(false);
return (
<tr className="TableStyle__tr-header">
{props.orderedKeys.map((keyValue, i) => {
return (
<th
key={`table-header-${i}`}
className="TableStyle__th"
onClick={() => {
if (activeIcon === i) {
let newRotateActiveIcon = !rotateActiveIcon;
setRotateActiveIcon(newRotateActiveIcon);
} else {
setRotateActiveIcon(false);
}
setActiveIcon(i);
props.sortByUpdate(keyValue);
props.tableUpdate();
}}
>
<FlexRow as="span" alignmentX="flex-start" gap="8px" width="100%">
{keyValue}
<FlexRow
as="span"
className="TableStyle__sort-button"
column={keyValue}
>
<ColumnFilterIcon
index={i}
active={activeIcon}
rotateIcon={rotateActiveIcon}
/>
</FlexRow>
</FlexRow>
</th>
);
})}
<FlexRow className="TableStyle__line" as="td" />
</tr>
);
};
export default TableHeader;

View File

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

View File

@ -1,33 +0,0 @@
import React from 'react';
import { FlexRow, Svg } from '../../../../../utils/containers';
import theme from '../../../../../utils/theme';
const TableRowButtons = ({ buttons, i, active, buttonAccessMessage }) => {
const getButtonTitle = (defaultTitle) => {
if (buttonAccessMessage === 'default') {
return defaultTitle;
} else return buttonAccessMessage;
};
return (
<FlexRow gap="12px" position='relative'>
{buttons.map((button, j) => {
return (
<Svg
title={getButtonTitle(button.title)}
key={`table-item-button-${i}-${j}`}
onClick={active ? button.handler : null}
src={button.icon}
backgroundColor={active ? theme.colors.dark : theme.colors.dark05}
cursor={active ? 'pointer' : 'auto'}
size="cover"
width="16px"
height="16px"
/>
);
})}
</FlexRow>
);
};
export default TableRowButtons;

View File

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

View File

@ -1,46 +0,0 @@
import React from 'react';
import { FlexRow } from '../../../../../utils/containers';
import TableRowTags from '../TableRowTags/TableRowTags';
import TableRowButtons from '../TableRowButtons/TableRowButtons';
import pensilIco from '../../../../../assets/pencil_ico.svg';
import deleteIco from '../../../../../assets/delete_ico.svg';
import KeyCloakService from '../../../../../services/KeyCloakService';
const TableRowFooter = ({ rowFooter, item, i, deleteItem, editItem, profileInfo }) => {
const buttonsActive = () => {
if (!KeyCloakService.isLoggedIn()) return false;
else if (
profileInfo?.preferred_username !== item.submitter &&
profileInfo?.name !== item.submitter
) return false;
return true;
};
const getButtonAccessMessage = () => {
if (!KeyCloakService.isLoggedIn()) {
return "You must be logged in to use this option.";
} else if (profileInfo?.preferred_username !== item.submitter &&
profileInfo?.name !== item.submitter) {
return "You don't have permission to use this option.";
} return "default";
};
if (rowFooter) {
return (
<FlexRow className="TableStyle__row-footer">
<TableRowTags item={item} i={i} />
<TableRowButtons
buttons={[
{ title: "edit", icon: pensilIco, handler: () => editItem() },
{ title: "delete", icon: deleteIco, handler: () => deleteItem() },
]}
active={buttonsActive()}
buttonAccessMessage={getButtonAccessMessage()}
i={i}
/>
</FlexRow>
);
}
};
export default TableRowFooter;

View File

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

View File

@ -1,37 +0,0 @@
import React from 'react';
import {
RENDER_WHEN,
RENDER_METRIC_VALUE,
IS_MOBILE,
} from '../../../../../utils/globals';
import { Container } from '../../../../../utils/containers';
const TableRowItems = ({ orderedKeys, item, i }) => {
const renderValue = (keyValue) => {
if (keyValue === 'when') {
return RENDER_WHEN(item[keyValue]);
} else {
return RENDER_METRIC_VALUE(item[keyValue]);
}
};
return (
<>
{orderedKeys.map((keyValue, j) => {
return (
<td key={`table-item-${i}-${j}`} className="TableStyle__td">
{IS_MOBILE() && (
<Container as="span" className="mobile-table-header">
{keyValue}
</Container>
)}
{renderValue(keyValue)}
{keyValue === '#' && i + 1}
</td>
);
})}
</>
);
};
export default TableRowItems;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,14 +0,0 @@
import styled from 'styled-components';
import { FlexRow } from '../../../../utils/containers';
const RowsBackgroundStyle = styled(FlexRow)`
width: calc(100% + 12px);
position: absolute;
top: 0;
left: -6px;
height: 100%;
background-color: ${({ theme, i }) =>
i % 2 === 0 ? theme.colors.dark01 : 'transparent'};
`;
export default RowsBackgroundStyle;

View File

@ -1,66 +0,0 @@
import styled from 'styled-components';
const TableStyle = styled.table`
border-collapse: separate;
border-spacing: 12px 0;
width: 100%;
.TableStyle__th {
cursor: pointer;
* {
cursor: pointer;
}
}
.TableStyle__tr-header {
height: 48px;
position: relative;
}
.TableStyle__tr {
position: relative;
height: ${({ rowFooter }) => (rowFooter ? '72px' : 'auto')};
}
.TableStyle__td {
padding: ${({ rowFooter }) => (rowFooter ? '4px 0 32px 0' : '12px 0')};
margin: 0 0 0 2px;
min-width: 80px;
}
.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;
}
`;
export default TableStyle;

View File

@ -1,12 +1,6 @@
import React from 'react';
import {
Container,
FlexColumn,
FlexRow,
Svg,
TransBack,
} from '../../utils/containers';
import { Body, Medium } from '../../utils/fonts';
import {Container, FlexColumn, FlexRow, Svg, TransBack} from '../../utils/containers';
import {Body, Medium} from '../../utils/fonts';
import theme from '../../utils/theme';
import userIco from '../../assets/user_ico.svg';
import KeyCloakService from '../../services/KeyCloakService';
@ -21,8 +15,8 @@ const LoggedBarStyle = styled(FlexColumn)`
right: 0;
align-items: flex-start;
justify-content: flex-start;
background-color: ${({ theme }) => theme.colors.white};
box-shadow: ${({ theme }) => theme.shadow};
background-color: ${({theme}) => theme.colors.white};
box-shadow: ${({theme}) => theme.shadow};
z-index: 3;
button {
@ -38,11 +32,11 @@ const LoggedBarStyle = styled(FlexColumn)`
&:hover {
li {
color: ${({ theme }) => theme.colors.green};
color: ${({theme}) => theme.colors.green};
}
div {
background-color: ${({ theme }) => theme.colors.green};
background-color: ${({theme}) => theme.colors.green};
}
}
@ -53,61 +47,37 @@ const LoggedBarStyle = styled(FlexColumn)`
`;
const LoggedBar = (props) => {
return (
<TransBack
transition="transform"
translateX={props.visible}
onClick={props.loggedBarVisibleHandler}
animTime="0.2s"
>
<LoggedBarStyle
onMouseEnter={props.loggedBarHoverTrue}
onMouseLeave={props.loggedBarHoverFalse}
>
<FlexRow
alignmentX="flex-start"
alignmentY="flex-end"
gap="16px"
width="100%"
padding="12px 16px"
>
<Svg
src={userIco}
width="32px"
height="32px"
backgroundColor={theme.colors.dark}
size="cover"
/>
<Medium as="p">{props.username}</Medium>
</FlexRow>
<Container
width="90%"
backgroundColor={theme.colors.dark05}
height="1px"
/>
<FlexColumn
as="ul"
onClick={KeyCloakService.goToProfile}
gap="24px"
padding="32px 24px"
alignmentX="flex-start"
>
<FlexRow as="button" gap="16px">
<Svg width="16px" height="16px" src={userIco} size="cover" />
<Body as="li">Profile</Body>
</FlexRow>
<FlexRow
as="button"
onClick={props.visible === '0' ? KeyCloakService.doLogout : null}
gap="16px"
>
<Svg width="16px" height="16px" src={loginIco} rotate="180deg" />
<Body as="li">Sign out</Body>
</FlexRow>
</FlexColumn>
</LoggedBarStyle>
</TransBack>
);
return (
<TransBack transition='transform' translateX={props.visible}
onClick={props.loggedBarVisibleHandler} animTime='0.2s'>
<LoggedBarStyle onMouseEnter={props.loggedBarHoverTrue}
onMouseLeave={props.loggedBarHoverFalse}>
<FlexRow alignmentX='flex-start' alignmentY='flex-end'
gap='16px' width='100%' padding='12px 16px'>
<Svg src={userIco} width='32px' height='32px' backgroundColor={theme.colors.dark} size='cover'/>
<Medium as='p'>
{props.username}
</Medium>
</FlexRow>
<Container width='90%' backgroundColor={theme.colors.dark05} height='1px'/>
<FlexColumn as='ul' onClick={() => console.log('profile click')}
gap='24px' padding='32px 24px' alignmentX='flex-start'>
<FlexRow as='button' gap='16px'>
<Svg width='16px' height='16px' src={userIco} size='cover'/>
<Body as='li'>
Profile
</Body>
</FlexRow>
<FlexRow as='button' onClick={props.visible === '0' ? KeyCloakService.doLogout : null} gap='16px'>
<Svg width='16px' height='16px' src={loginIco} rotate='180deg'/>
<Body as='li'>
Sign out
</Body>
</FlexRow>
</FlexColumn>
</LoggedBarStyle>
</TransBack>
);
};
export default LoggedBar;
export default LoggedBar;

View File

@ -43,7 +43,6 @@ const NavBar = (props) => {
const mobileMenuHoverFalse = () => {
setMobileMenuHover(false);
};
const toggleNavMenu = () => {
if (navMenuTranslateY === 'calc(-100vh - 42px)') setNavMenuTranslateY('0');
else if (!mobileMenuHover) setNavMenuTranslateY('calc(-100vh - 42px)');
@ -52,57 +51,51 @@ const NavBar = (props) => {
return (
<NavBarStyle as="header">
<FlexRow height="100%" alignmentX="space-between" as="nav">
<Logo navOptions={props.navOptions} />
{props.navOptions && (
<>
<MenuButton as="button" onClick={toggleNavMenu} />
<FlexRow as="ul" className="ul-desktop" gap="32px">
<FlexRow as={Link} to={CHALLENGES_PAGE} gap="16px">
<Svg width="16px" height="16px" src={cupIco} />
<Menu as="li">Challenges</Menu>
</FlexRow>
<FlexRow as={Link} to={POLICY_PRIVACY_PAGE} gap="12px">
<Svg size="cover" width="16px" height="16px" src={policyIco} />
<Menu as="li">Privacy policy</Menu>
</FlexRow>
{!KeyCloakService.isLoggedIn() && (
<FlexRow
as="button"
onClick={() =>
props.popUpMessageHandler(
'Reminder',
'Remember to check your spam mailbox to confirm your account.',
() => KeyCloakService.doRegister
)
}
gap="16px"
>
<Svg width="16px" height="16px" src={registerIco} />
<Menu as="li">Register</Menu>
</FlexRow>
)}
{KeyCloakService.isLoggedIn() ? (
<Svg
as="button"
onClick={props.loggedBarVisibleHandler}
width="32px"
height="32px"
src={userIco}
margin="0 16px 0 0"
/>
) : (
<FlexRow
as="button"
onClick={KeyCloakService.doLogin}
gap="16px"
>
<Svg width="16px" height="16px" src={loginIco} />
<Menu as="li">Sign in</Menu>
</FlexRow>
)}
<Logo />
<MenuButton as="button" onClick={toggleNavMenu} />
<FlexRow as="ul" className="ul-desktop" gap="32px">
<FlexRow as={Link} to={CHALLENGES_PAGE} gap="16px">
<Svg width="16px" height="16px" src={cupIco} />
<Menu as="li">Challenges</Menu>
</FlexRow>
<FlexRow as={Link} to={POLICY_PRIVACY_PAGE} gap="12px">
<Svg size="cover" width="16px" height="16px" src={policyIco} />
<Menu as="li">Privacy policy</Menu>
</FlexRow>
{!KeyCloakService.isLoggedIn() ? (
<FlexRow
as="button"
onClick={() =>
props.popUpMessageHandler(
'Reminder',
'Remember to check your spam mailbox to confirm your account.',
() => KeyCloakService.doRegister
)
}
gap="16px"
>
<Svg width="16px" height="16px" src={registerIco} />
<Menu as="li">Register</Menu>
</FlexRow>
</>
)}
) : (
''
)}
{KeyCloakService.isLoggedIn() ? (
<Svg
as="button"
onClick={props.loggedBarVisibleHandler}
width="32px"
height="32px"
src={userIco}
margin="0 16px 0 0"
/>
) : (
<FlexRow as="button" onClick={KeyCloakService.doLogin} gap="16px">
<Svg width="16px" height="16px" src={loginIco} />
<Menu as="li">Sign in</Menu>
</FlexRow>
)}
</FlexRow>
</FlexRow>
<MobileNavMenu
mobileMenuHoverTrue={mobileMenuHoverTrue}

View File

@ -8,9 +8,13 @@ import HttpService from './services/HttpService';
const root = ReactDOM.createRoot(document.getElementById('root'));
const renderApp = () => root.render(<App />);
const renderApp = () => root.render(
<React.StrictMode>
<App/>
</React.StrictMode>
);
KeyCloakService.initKeycloak(renderApp);
HttpService.configure();
renderApp();
renderApp();

View File

@ -1,169 +1,243 @@
import React from 'react';
import theme from '../../utils/theme';
import Media from 'react-media';
import { FlexColumn } from '../../utils/containers';
import { H2 } from '../../utils/fonts';
import Pager from '../../components/generic/Pager';
import Search from '../../components/generic/Search';
import getEntries from '../../api/getEntries';
import Table from '../../components/generic/Table';
import {
CALC_PAGES,
EVALUATIONS_FORMAT,
RENDER_WHEN,
IS_MOBILE,
} from '../../utils/globals';
import Loading from '../../components/generic/Loading';
import { CALC_PAGES, ELEMENTS_PER_PAGE } from '../../utils/globals';
import searchQueryHandler from './searchHandler';
import orderKeys from './orderKeys';
import KeyCloakService from '../../services/KeyCloakService';
import Pager from '../../components/generic/Pager';
import Table from '../../components/generic/Table';
import Search from '../../components/generic/Search';
import allEntriesSearchQueryHandler from './allEntriesSearchQueryHandler';
import getAllEntries from '../../api/getAllEntries';
const AllEntries = (props) => {
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);
const [profileInfo, setProfileInfo] = React.useState(null);
const getProfileInfo = () => {
if (KeyCloakService.isLoggedIn()) {
KeyCloakService.getProfileInfo(setProfileInfo);
} else {
setProfileInfo(false);
}
};
React.useMemo(() => {
if (props.challengeName) {
getEntries(
'challenge-all-submissions',
props.challengeName,
[setEntries, setEntriesAll],
setLoading,
setScoresSorted
);
}
getProfileInfo();
React.useEffect(() => {
if (props.challengeName) challengeDataRequest(props.challengeName);
}, [props.challengeName]);
const sortByUpdate = React.useCallback(
(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;
const challengeDataRequest = (challengeName) => {
getAllEntries(challengeName, setEntriesFromApi, setEntriesAll);
getAllEntries(
challengeName,
undefined,
setEntries,
setLoading,
setScoresSorted
);
};
const getPossibleMetrics = () => {
let metrics = [];
if (entriesFromApi.tests) {
for (let test of entriesFromApi.tests) {
let myEval = `${test.metric}.${test.name}`;
if (myEval && !metrics.includes(myEval)) {
metrics.push(myEval);
}
}
setEntries(newEntries);
},
[entries, idSorted, scoresSorted, submitterSorted, whenSorted]
);
}
return metrics;
};
const n = (pageNr - 1) * (ELEMENTS_PER_PAGE * 2);
const elements = entries.slice(n, n + ELEMENTS_PER_PAGE * 2);
const getAllEntriesHeader = () => {
let header = ['#', 'submitter'];
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 (
<FlexColumn
as="section"
padding="24px"
gap="32px"
width="100%"
maxWidth="1600px"
>
<H2 as="h2">All Entries</H2>
{!loading && (profileInfo !== null) ? (
<>
<Search
searchQueryHandler={(event) =>
searchQueryHandler(event, entriesAll, setPageNr, setEntries)
}
/>
{elements.length > 0 && entries[0] && (
<div style={{ width: '100%', overflowX: 'auto' }}>
<Table
items={elements}
orderedKeys={orderKeys(entries[0])}
sortByUpdate={sortByUpdate}
popUpMessageHandler={props.popUpMessageHandler}
profileInfo={profileInfo}
/>
</div>
)}
<Pager
pageNr={pageNr}
elements={entries}
setPageNr={setPageNr}
width="72px"
borderRadius="64px"
pages={CALC_PAGES(entries, 2)}
number={`${pageNr} / ${CALC_PAGES(entries, 2)}`}
/>
</>
) : (
<Loading />
)}
</FlexColumn>
<>
<Media query={theme.mobile}>{mobileRender()}</Media>
<Media query={theme.desktop}>{desktopRender()}</Media>
</>
);
};

View File

@ -0,0 +1,32 @@
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;
console.log(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

@ -1,27 +0,0 @@
const orderKeys = (elem) => {
if (elem) {
let result = ['id', 'submitter', 'description'];
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();
const testBkeys = elemKeys
.filter((key) => key.split('.')[1] === 'test-B')
.sort();
result = result.concat(dev0keys);
result = result.concat(dev1keys);
result = result.concat(testAkeys);
result = result.concat(testBkeys);
result.push('when');
return result;
}
return null;
};
export default orderKeys;

View File

@ -1,28 +0,0 @@
const searchQueryHandler = (event, entriesAll, setPageNr, setEntries) => {
let searchQuery = event.target.value;
let submissionsToRender = [];
setPageNr(1);
if (searchQuery === '') setEntries(entriesAll);
else {
for (let entry of entriesAll) {
let { when, tags } = entry;
tags = Object.values(tags)
.map((tag) => tag.name)
.join(' ');
const otherKeys = Object.values(entry)
.join(' ')
.replaceAll(-999999999, 'N/A')
.replaceAll('[object Object]', '')
.replaceAll(',', '');
const str = `${when.slice(11, 16)} ${when.slice(
0,
10
)} ${otherKeys} ${tags}`;
if (str.toLowerCase().includes(searchQuery.toLowerCase()))
submissionsToRender.push(entry);
}
setEntries(submissionsToRender);
}
};
export default searchQueryHandler;

View File

@ -33,7 +33,7 @@ const Challenges = () => {
statusFilterHandle(state.statusFilter, state.challenges, dispatch);
}, [state.statusFilter, state.challenges]);
const setPageNr = React.useCallback((value) => {
const setPage = React.useCallback((value) => {
dispatch({ type: CHALLENGES_ACTION.SET_PAGE, payload: value });
}, []);
@ -73,7 +73,7 @@ const Challenges = () => {
dispatch={dispatch}
filtersMenuRender={filtersMenuRender}
searchQueryHandler={searchQueryHandler}
setPageNr={setPageNr}
setPage={setPage}
filtersMenu={state.filtersMenu}
loading={state.loading}
pageNr={state.pageNr}
@ -85,7 +85,7 @@ const Challenges = () => {
dispatch={dispatch}
filtersMenuRender={filtersMenuRender}
searchQueryHandler={searchQueryHandler}
setPageNr={setPageNr}
setPage={setPage}
filtersMenu={state.filtersMenu}
loading={state.loading}
pageNr={state.pageNr}

View File

@ -35,7 +35,7 @@ const ChallengesDesktop = (props) => {
{!props.loading && (
<Pager
pageNr={props.pageNr}
setPageNr={props.setPageNr}
setPage={props.setPage}
elements={props.challengesFiltered}
pages={CALC_PAGES(props.challengesFiltered)}
width="72px"

View File

@ -69,16 +69,6 @@ const MiniChallenge = (props) => {
}
};
const baselineRender = () => {
if (props.baseline) {
return (
<IconLabel size="24px" gap="8px" type="baseline">
{props.baseline}
</IconLabel>
);
}
};
return (
<ChallengeStyle as={Link} to={`${CHALLENGE_PAGE}/${props.name}`}>
<FlexColumn as="article">
@ -109,10 +99,12 @@ const MiniChallenge = (props) => {
{props.metric ? props.metric : 'xxx'}
</IconLabel>
<IconLabel size="24px" gap="8px" type="bestScore">
{props.bestScore ? props.bestScore.slice(0, 6) : 'xxx'}
{props.bestScore ? props.bestScore : 'xxx'}
</IconLabel>
{deadlineRender()}
{baselineRender()}
<IconLabel size="24px" gap="8px" type="baseline">
{props.baseline ? props.baseline : 'xxx'}
</IconLabel>
{props.prize ? (
<IconLabel size="24px" gap="8px" type="prize">
{props.prize}

View File

@ -4,10 +4,10 @@ import { useParams } from 'react-router-dom';
import { H1, Medium } from '../../utils/fonts';
import theme from '../../utils/theme';
import MobileChallengeMenu from './components/MobileChallengeMenu';
import Leaderboard from '../Leaderboard';
import Leaderboard from '../Leaderboard/Leaderboard';
import Readme from '../Readme';
import HowTo from '../HowTo';
import MyEntries from '../MyEntries';
import HowTo from '../HowTo/HowTo';
import MyEntries from '../MyEntries/MyEntries';
import Submit from '../Submit';
import Media from 'react-media';
import DesktopChallengeMenu from './components/DesktopChallengeMenu';
@ -15,15 +15,18 @@ import { CHALLENGE_SECTIONS, RENDER_ICO } from '../../utils/globals';
import textIco from '../../assets/text_ico.svg';
import getChallengeInfo from '../../api/getChallengeInfo';
import Loading from '../../components/generic/Loading';
import AllEntries from '../AllEntries';
import getUser from '../../api/getUser';
import AllEntries from '../AllEntries/AllEntries';
const Challenge = (props) => {
const challengeName = useParams().challengeId;
const [challenge, setChallenge] = React.useState([]);
const [loading, setLoading] = React.useState(true);
const [user, setUser] = React.useState('');
React.useEffect(() => {
getChallengeInfo(setChallenge, setLoading, challengeName);
getUser(setUser);
}, [challengeName]);
const sectionRender = () => {
@ -33,15 +36,12 @@ const Challenge = (props) => {
<Leaderboard
challengeName={challengeName}
mainMetric={challenge.mainMetric}
user={user}
/>
);
case CHALLENGE_SECTIONS.ALL_ENTRIES:
return (
<AllEntries
challengeName={challengeName}
setLoading={setLoading}
popUpMessageHandler={props.popUpMessageHandler}
/>
<AllEntries challengeName={challengeName} setLoading={setLoading} />
);
case CHALLENGE_SECTIONS.README:
return (
@ -57,15 +57,11 @@ const Challenge = (props) => {
<HowTo
popUpMessageHandler={props.popUpMessageHandler}
challengeName={challengeName}
user={user}
/>
);
case CHALLENGE_SECTIONS.MY_ENTRIES:
return (
<MyEntries
challengeName={challengeName}
popUpMessageHandler={props.popUpMessageHandler}
/>
);
return <MyEntries challengeName={challengeName} />;
case CHALLENGE_SECTIONS.SUBMIT:
return <Submit challengeName={challengeName} setLoading={setLoading} />;
default:

View File

@ -7,19 +7,17 @@ import HowToContent from './components/HowToContent';
const HowTo = (props) => {
const [userFullInfo, setUserFullInfo] = React.useState(null);
const username = KeyCloakService.getUsername();
React.useMemo(async () => {
await getFullUser(setUserFullInfo);
setTimeout(() => {
if (!KeyCloakService.isLoggedIn()) {
props.popUpMessageHandler(
'Please log in',
'To see everything you must log in',
() => KeyCloakService.doLogin
);
}
}, 1000);
React.useEffect(() => {
getFullUser(setUserFullInfo);
if (!KeyCloakService.isLoggedIn()) {
props.popUpMessageHandler(
'Please log in',
'To see everything you must log in',
() => KeyCloakService.doLogin
);
}
}, [props]);
return (
@ -33,7 +31,7 @@ const HowTo = (props) => {
<FlexColumn maxWidth="680px" alignmentX="flex-start" gap="48px">
<HowToContent
userFullInfo={userFullInfo}
user={username ? username : 'yourID'}
user={props.user ? props.user : 'yourID'}
challengeName={props.challengeName}
/>
</FlexColumn>

46
src/pages/LandingPage.js Normal file
View File

@ -0,0 +1,46 @@
import React from 'react';
import { FlexColumn } from '../utils/containers';
import Motivation from '../components/content_sections/Motivation';
import Csi from '../components/content_sections/Csi';
import Commercial from '../components/content_sections/Commercial';
import Hero from '../components/content_sections/Hero';
import Partnerships from '../components/content_sections/Partnerships';
import styled from 'styled-components';
const LandingPageStyle = styled(FlexColumn)`
justify-content: flex-start;
width: 100%;
min-height: 100vh;
padding: 90px 0 32px;
.main-container {
max-width: 452px;
gap: 48px;
width: 80%;
}
@media (min-width: ${({ theme }) => theme.overMobile}) {
padding: 172px 0 124px;
.main-container {
max-width: none;
gap: 124px;
}
}
`;
const LandingPage = (props) => {
return (
<LandingPageStyle as="main">
<FlexColumn className="main-container">
<Hero popUpMessageHandler={props.popUpMessageHandler} />
<Motivation />
<Csi />
<Commercial />
<Partnerships />
</FlexColumn>
</LandingPageStyle>
);
};
export default LandingPage;

View File

@ -1,35 +0,0 @@
import React from 'react';
import { FlexColumn } from '../../utils/containers';
import Motivation from './components/Motivation/Motivation';
import Csi from './components/Csi/Csi';
import Commercial from './components/Commercial/Commercial';
import Hero from './components/Hero/Hero';
import Partnerships from './components/Partnerships/Partnerships';
import LandingPageStyle from './LandingPageStyle';
import EntireScreenLoading from '../../components/generic/EntireScreenLoading/EntrieScreenLoading';
const LandingPage = (props) => {
const [show, setShow] = React.useState(false);
React.useEffect(() => {
setTimeout(() => {
setShow(true);
}, 1000);
}, []);
if (show) {
return (
<LandingPageStyle as="main">
<Hero popUpMessageHandler={props.popUpMessageHandler} />
<FlexColumn className="LandingPageStyle__main-container">
<Motivation />
<Csi />
<Commercial />
<Partnerships />
</FlexColumn>
</LandingPageStyle>
);
} else return <EntireScreenLoading />;
};
export default LandingPage;

View File

@ -1,25 +0,0 @@
import styled from 'styled-components';
import { FlexColumn } from '../../utils/containers';
const LandingPageStyle = styled(FlexColumn)`
justify-content: flex-start;
width: 100%;
min-height: 100vh;
padding: 0 32px;
.LandingPageStyle__main-container {
max-width: 452px;
gap: 48px;
width: 80%;
}
@media (min-width: ${({ theme }) => theme.overMobile}) {
padding: 0 124px 124px;
.LandingPageStyle__main-container {
max-width: none;
gap: 124px;
}
}
`;
export default LandingPageStyle;

View File

@ -1,115 +0,0 @@
import React from 'react';
import {
FlexColumn,
FlexRow,
ImageBackground,
} from '../../../../utils/containers';
import { Body, H2, Medium } from '../../../../utils/fonts';
import CircleNumber from '../../../../components/generic/CircleNumber';
import Media from 'react-media';
import theme from '../../../../utils/theme';
import commercialImage from '../../../../assets/commercial-image.svg';
const Commercial = () => {
const listItemsContent = [
'A company comes to CSI with a business need',
'CSI determines the need with an appropriate challenge on Gonito',
'The challenge is solved by willing users',
'The company appropriately rewards users who have contributed to the required outcome',
];
const mobileRender = () => {
return (
<FlexColumn as="section" alignmentX="flex-start">
<H2 as="h2" margin="0 0 24px 0">
Commercial challenges
</H2>
<FlexColumn gap="20px">
<Body as="p">
The artificial intelligence center works with companies by accepting
machine learning challenges from them that are available to solve on
Gonito. Each commercial challenge is properly scored which
translates into an award for solving it according to the client's
requirements.
</Body>
<FlexColumn as="ul" gap="16px" alignmentX="flex-start">
{listItemsContent.map((item, index) => {
return (
<FlexRow
key={`commercial-item-${index}`}
width="100%"
gap="8px"
>
<CircleNumber number={String(index + 1)} />
<Medium width="80%" as="li">
{item}
</Medium>
</FlexRow>
);
})}
</FlexColumn>
<Body as="p">
Open challenges can allow you to find the right people to work with.
Find a challenge for your team and take it on!
</Body>
</FlexColumn>
</FlexColumn>
);
};
const desktopRender = () => {
return (
<FlexRow gap="46px">
<FlexColumn as="section" alignmentX="flex-start" maxWidth="490px">
<H2 as="h2" margin="0 0 48px 0">
Commercial challenges
</H2>
<FlexColumn gap="32px">
<Body as="p">
The artificial intelligence center works with companies by
accepting machine learning challenges from them that are available
to solve on Gonito. Each commercial challenge is properly scored
which translates into an award for solving it according to the
client's requirements.
</Body>
<FlexColumn as="ul" gap="24px" alignmentX="flex-start">
{listItemsContent.map((item, index) => {
return (
<FlexRow
key={`commercial-item-${index}`}
width="100%"
gap="16px"
>
<CircleNumber number={String(index + 1)} />
<Medium width="80%" as="li">
{item}
</Medium>
</FlexRow>
);
})}
</FlexColumn>
<Body as="p">
Open challenges can allow you to find the right people to work
with. Find a challenge for your team and take it on!
</Body>
</FlexColumn>
</FlexColumn>
<ImageBackground
image={commercialImage}
width="180px"
height="274px"
size="contain"
/>
</FlexRow>
);
};
return (
<>
<Media query={theme.mobile}>{mobileRender()}</Media>
<Media query={theme.desktop}>{desktopRender()}</Media>
</>
);
};
export default Commercial;

View File

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

View File

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

View File

@ -1,123 +0,0 @@
import React from 'react';
import { Body, H1, Medium } from '../../../../utils/fonts';
import { Container, FlexColumn, FlexRow } from '../../../../utils/containers';
import theme from '../../../../utils/theme';
import ButtonLink from '../../../../components/generic/ButtonLink';
import Media from 'react-media';
import KeyCloakService from '../../../../services/KeyCloakService';
import MadeByCsi from '../MadeByCsi/MadeByCsi';
import HeroStyle from './HeroStyle';
const Hero = (props) => {
const mobileRender = () => {
return (
<FlexColumn
alignmentX="flex-start"
gap="24px"
margin="80px 0 48px 0"
maxWidth="452px"
>
<H1 as="h1">
Welcome to
<Container display="inline" color={theme.colors.green}>
&nbsp;Gonito!
</Container>
</H1>
<Body as="p">
A data challenge platform for machine learning research, competition,
cooperation and reproducibility.
</Body>
<ButtonLink
as="button"
onClick={() =>
props.popUpMessageHandler(
'Reminder',
'Remember to check your spam mailbox to confirm your account.',
() => KeyCloakService.doRegister
)
}
>
Join us!
</ButtonLink>
</FlexColumn>
);
};
const desktopRender = () => {
return (
<HeroStyle>
<FlexColumn alignmentX="flex-start" gap="40px">
<H1 as="h1">
Welcome to
<Container display="inline" color={theme.colors.green}>
&nbsp;Gonito!
</Container>
</H1>
<Medium as="p" className="HeroStyle__title-paragraph">
A data challenge platform for machine learning research,
competition, cooperation and reproducibility.
</Medium>
<MadeByCsi position="horizontal" />
<ButtonLink
as="button"
onClick={() =>
props.popUpMessageHandler(
'Reminder',
'Remember to check your spam mailbox to confirm your account.',
() => KeyCloakService.doRegister
)
}
>
Join us!
</ButtonLink>
</FlexColumn>
</HeroStyle>
);
};
const desktopRender2 = () => {
return (
<HeroStyle>
<FlexRow gap="100px">
<FlexColumn gap="50px" alignmentX="flex-start">
<H1 as="h1">
Welcome to
<Container display="inline" color={theme.colors.green}>
&nbsp;Gonito!
</Container>
</H1>
<Medium as="p" className="HeroStyle__title-paragraph">
A data challenge platform for machine learning research,
competition, cooperation and reproducibility.
</Medium>
<ButtonLink
as="button"
onClick={() =>
props.popUpMessageHandler(
'Reminder',
'Remember to check your spam mailbox to confirm your account.',
() => KeyCloakService.doRegister
)
}
>
Join us!
</ButtonLink>
</FlexColumn>
<MadeByCsi />
</FlexRow>
</HeroStyle>
);
};
return (
<>
<Media query={theme.mobile}>{mobileRender()}</Media>
<Media query="(min-width: 1025px) and (max-width: 1440px)">
{desktopRender()}
</Media>
<Media query="(min-width: 1441px)">{desktopRender2()}</Media>
</>
);
};
export default Hero;

View File

@ -1,31 +0,0 @@
import styled from 'styled-components';
import { FlexColumn } from '../../../../utils/containers';
const HeroStyle = styled(FlexColumn)`
justify-content: flex-start;
gap: 24px;
max-width: 452px;
@media (min-width: 1441px) {
max-width: none;
}
@media (min-width: ${({ theme }) => theme.overMobile}) {
justify-content: center;
width: 100%;
height: calc(100vh - 48px);
}
.HeroStyle__title-paragraph {
font-size: 24px;
line-height: 36px;
font-weight: 300;
max-width: 580px;
@media (min-width: 1441px) {
max-width: 600px;
}
}
`;
export default HeroStyle;

View File

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

View File

@ -1,41 +0,0 @@
import React from 'react';
import { H1 } from '../../../../utils/fonts';
import { Container, FlexRow, Svg } from '../../../../utils/containers';
import theme from '../../../../utils/theme';
import csiLogo from '../../../../assets/csi_logo.svg';
import csiLogoContent from '../../../../assets/csi_logo_content.svg';
import MadeByCsiStyle from './MadeByCsiStyle';
const MadeByCsi = (props) => {
return (
<MadeByCsiStyle
as="a"
horizontal={props.position}
href="https://csi.amu.edu.pl/"
target="_blank"
>
<H1
as="span"
fontSize={props.position === 'horizontal' ? '36px' : '44px'}
>
Made
<Container as="span" display="inline" color={theme.colors.green}>
&nbsp;by
</Container>
</H1>
<FlexRow gap="20px" className="MadeByCsiStyle__logo-container">
<Svg
className="MadeByCsiStyle__csiLogo"
width="154px"
height="110px"
size="cover"
src={csiLogo}
/>
<Svg width="220px" height="110px" src={csiLogoContent} />
</FlexRow>
</MadeByCsiStyle>
);
};
export default MadeByCsi;

View File

@ -1,28 +0,0 @@
import styled from 'styled-components';
import { FlexColumn } from '../../../../utils/containers';
const MadeByCsiStyle = styled(FlexColumn)`
flex-direction: ${({ horizontal }) => (horizontal ? 'row' : 'column')};
gap: ${({ horizontal }) => (horizontal ? '50px' : '48px')};
align-items: ${({ horizontal }) => (horizontal ? 'center' : 'flex-start')};
cursor: pointer;
* {
cursor: pointer;
}
.MadeByCsiStyle__csiLogo {
transition: background-color 0.4s ease-in-out;
}
.MadeByCsiStyle__logo-container {
transition: transform 0.3s ease-in-out;
}
&:hover {
.MadeByCsiStyle__csiLogo {
background-color: #e52713;
}
.MadeByCsiStyle__logo-container {
transform: scale(1.05);
}
}
`;
export default MadeByCsiStyle;

View File

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

View File

@ -1,34 +0,0 @@
import React from 'react';
import { FlexColumn, FlexRow } from '../../../../utils/containers';
import { Body, H2 } from '../../../../utils/fonts';
import cubeIconRender from './functions/cubeIconRender';
import ellipse from '../../../../assets/ellipse.svg';
import MotivationStyle from './MotivationStyle';
import { IS_MOBILE } from '../../../../utils/globals';
import { MOTIVATION__CONTENT } from './utils';
const Motivation = () => {
return (
<MotivationStyle as="section" image={IS_MOBILE() ? null : ellipse}>
<H2 as="h2">Motivation</H2>
<FlexColumn as="ul" className="MotivationStyle__list">
{MOTIVATION__CONTENT.map((paragraph, index) => {
return (
<FlexRow
key={`motivation-${index}`}
as="li"
className="MotivationStyle__item"
>
{cubeIconRender()}
<Body as="p" className="MotivationStyle__paragraph">
{paragraph}
</Body>
</FlexRow>
);
})}
</FlexColumn>
</MotivationStyle>
);
};
export default Motivation;

View File

@ -1,48 +0,0 @@
import styled from 'styled-components';
// import { ImageBackground } from '../../../../utils/containers';
import { ImageBackground } from '../../../../utils/containers';
const MotivationStyle = styled(ImageBackground)`
align-items: flex-start;
gap: 24px;
width: 100%;
.MotivationStyle__list {
gap: 16px;
align-items: flex-start;
}
.MotivationStyle__item {
gap: 12px;
align-items: flex-start;
justify-content: flex-start;
}
.MotivationStyle__paragraph {
width: 90%;
}
@media (min-width: ${({ theme }) => theme.overMobile}) {
gap: 48px;
width: 612px;
height: 458px;
align-items: center;
.MotivationStyle__list {
gap: 22px;
}
.MotivationStyle__item {
gap: 16px;
justify-content: flex-start;
align-items: center;
}
.MotivationStyle__paragraph {
width: auto;
max-width: 380px;
}
}
`;
export default MotivationStyle;

View File

@ -1,30 +0,0 @@
import cubeIcon from '../../../../../assets/cube_ico.svg';
import theme from '../../../../../utils/theme';
import { IS_MOBILE } from '../../../../../utils/globals';
import { Svg } from '../../../../../utils/containers';
const cubeIconRender = () => {
if (IS_MOBILE()) {
return (
<Svg
src={cubeIcon}
width="14px"
height="14px"
margin="4px 0 0 0"
backgroundColor={theme.colors.green}
/>
);
}
return (
<Svg
src={cubeIcon}
width="20px"
height="20px"
size="cover"
margin="2px 0 0 0"
backgroundColor={theme.colors.green}
/>
);
};
export default cubeIconRender;

View File

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

View File

@ -1,7 +0,0 @@
const MOTIVATION__CONTENT = [
'Explore interesting solutions to problems using AI',
'Train by solving our challenges',
'Participate in competitions with commercial challenges',
];
export { MOTIVATION__CONTENT };

View File

@ -1,25 +0,0 @@
import React from 'react';
import { FlexColumn, Grid } from '../../../../utils/containers';
import { H2 } from '../../../../utils/fonts';
import Placeholder from '../../../../components/generic/Placeholder';
import PartnershipsStyle from './PartnershipsStyle';
const Partnerships = () => {
return (
<PartnershipsStyle as="section">
<H2 as="h2">Our partnerships</H2>
<FlexColumn width="100%">
<Grid className="grid" gridGap="32px 0">
<Placeholder>allegro</Placeholder>
<Placeholder>pons</Placeholder>
<Placeholder>samsung</Placeholder>
<Placeholder>domdata</Placeholder>
<Placeholder>pwn.pl</Placeholder>
<Placeholder>cararena.pl</Placeholder>
</Grid>
</FlexColumn>
</PartnershipsStyle>
);
};
export default Partnerships;

View File

@ -1,21 +0,0 @@
import styled from 'styled-components';
import { FlexColumn } from '../../../../utils/containers';
const PartnershipsStyle = styled(FlexColumn)`
justify-content: flex-start;
gap: 32px;
margin: 0 0 48px 0;
@media (min-width: ${({ theme }) => theme.overMobile}) {
gap: 64px;
margin: 0;
.grid {
grid-template-rows: 1fr 1fr;
grid-template-columns: 1fr 1fr 1fr;
grid-gap: 64px;
}
}
`;
export default PartnershipsStyle;

View File

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

View File

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

View File

@ -1,15 +1,20 @@
import React from 'react';
import Media from 'react-media';
import theme from '../../utils/theme';
import { FlexColumn } from '../../utils/containers';
import { H2 } from '../../utils/fonts';
import Table from '../../components/generic/Table/Table';
import Table from '../../components/generic/Table';
import PropsTypes from 'prop-types';
import getChallengeLeaderboard from '../../api/getChallengeLeaderboard';
import leaderboardSearchQueryHandler from './leaderboardSearchQueryHandler';
import { CALC_PAGES } from '../../utils/globals';
import {
CALC_PAGES,
EVALUATIONS_FORMAT,
RENDER_WHEN,
} from '../../utils/globals';
import Search from '../../components/generic/Search';
import Pager from '../../components/generic/Pager';
import Loading from '../../components/generic/Loading';
import orderKeys from './orderKeys';
import { ELEMENTS_PER_PAGE } from '../../utils/globals';
const Leaderboard = (props) => {
const [entriesFromApi, setEntriesFromApi] = React.useState([]);
@ -21,186 +26,282 @@ const Leaderboard = (props) => {
const [entriesSorted, setEntriesSorted] = React.useState(false);
const [whenSorted, setWhenSorted] = React.useState(false);
const [scoresSorted, setScoresSorted] = React.useState([]);
const [idSorted, setIdSorted] = React.useState([]);
React.useMemo(() => {
getChallengeLeaderboard(
'leaderboard',
props.challengeName,
[setEntries, setEntriesFromApi],
setLoading,
setScoresSorted
);
React.useEffect(() => {
challengeDataRequest(props.challengeName);
}, [props.challengeName]);
const challengeDataRequest = (challengeName) => {
getChallengeLeaderboard(setEntriesFromApi, challengeName);
getChallengeLeaderboard(setEntries, challengeName, setLoading);
};
const getMetricIndex = (metricName) => {
let i = 0;
for (let evaluation of entriesFromApi[0].evaluations) {
if (`${evaluation.test.metric}.${evaluation.test.name}` === metricName) {
return i;
}
i++;
}
};
const searchQueryHandler = (event) => {
leaderboardSearchQueryHandler(event, entriesFromApi, setPageNr, setEntries);
};
const sortByUpdate = React.useCallback(
(elem) => {
let newEntries = entries.slice();
const possibleMetrics = orderKeys(entries[0]).filter(
(key) =>
!['id', 'submitter', 'when', 'description', 'times'].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 'description':
if (descriptionSorted) {
newEntries = newEntries.sort((a, b) =>
a.description.toLowerCase() < b.description.toLowerCase()
? 1
: b.description.toLowerCase() < a.description.toLowerCase()
? -1
: 0
);
setDescriptionSorted(false);
} else {
newEntries = newEntries.sort((a, b) =>
a.description.toLowerCase() > b.description.toLowerCase()
? 1
: b.description.toLowerCase() > a.description.toLowerCase()
? -1
: 0
);
setDescriptionSorted(true);
}
break;
case 'times':
if (entriesSorted) {
newEntries = newEntries.sort((a, b) =>
a.times > b.times ? 1 : b.times > a.times ? -1 : 0
);
setEntriesSorted(false);
} else {
newEntries = newEntries.sort((a, b) =>
a.times < b.times ? 1 : b.times < a.times ? -1 : 0
);
setEntriesSorted(true);
}
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;
const getPossibleMetrics = () => {
let metrics = [];
for (let entry of entriesFromApi) {
for (let evaluation of entry.evaluations) {
let metric = evaluation.test.metric;
let name = evaluation.test.name;
if (metric && !metrics.includes(`${metric}.${name}`)) {
metrics.push(`${metric}.${name}`);
}
}
setEntries(newEntries);
},
[
descriptionSorted,
entries,
entriesSorted,
idSorted,
scoresSorted,
submitterSorted,
whenSorted,
]
);
}
return metrics;
};
const n = (pageNr - 1) * (ELEMENTS_PER_PAGE * 2);
const elements = entries.slice(n, n + ELEMENTS_PER_PAGE * 2);
const getLeaderboardHeader = () => {
let header = ['#', 'submitter', 'description'];
for (let metric of getPossibleMetrics()) {
header.push(metric);
}
header.push('entries');
header.push('when');
return header;
};
const getLeaderboardHeaderMobile = () => {
let header = ['#', 'submitter', 'description', 'entries', 'when'];
for (let metric of getPossibleMetrics()) {
header.push(metric);
}
return header;
};
const sortByUpdate = (elem) => {
let metricIndex = 0;
let newEntries = entries;
switch (elem) {
case 'submitter':
if (submitterSorted) {
newEntries = newEntries.sort((a, b) =>
a.submitter.toLowerCase() < b.submitter.toLowerCase()
? 1
: b.submitter.toLowerCase() < a.submitter.toLowerCase()
? -1
: 0
);
setSubmitterSorted(false);
} else {
newEntries = newEntries.sort((a, b) =>
a.submitter.toLowerCase() > b.submitter.toLowerCase()
? 1
: b.submitter.toLowerCase() > a.submitter.toLowerCase()
? -1
: 0
);
setSubmitterSorted(true);
}
break;
case 'description':
if (descriptionSorted) {
newEntries = newEntries.sort((a, b) =>
a.description.toLowerCase() < b.description.toLowerCase()
? 1
: b.description.toLowerCase() < a.description.toLowerCase()
? -1
: 0
);
setDescriptionSorted(false);
} else {
newEntries = newEntries.sort((a, b) =>
a.description.toLowerCase() > b.description.toLowerCase()
? 1
: b.description.toLowerCase() > a.description.toLowerCase()
? -1
: 0
);
setDescriptionSorted(true);
}
break;
case 'entries':
if (entriesSorted) {
newEntries = newEntries.sort((a, b) => b.times - a.times);
setEntriesSorted(false);
} else {
newEntries = newEntries.sort((a, b) => a.times - b.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:
metricIndex = getMetricIndex(elem);
// eslint-disable-next-line no-case-declarations
let newScoresSorted = scoresSorted;
if (scoresSorted[metricIndex]) {
newEntries = newEntries.sort(
(a, b) =>
b.evaluations[metricIndex].score -
a.evaluations[metricIndex].score
);
newScoresSorted[metricIndex] = false;
setScoresSorted(newScoresSorted);
} else {
newEntries = newEntries.sort(
(a, b) =>
a.evaluations[metricIndex].score -
b.evaluations[metricIndex].score
);
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">
Leaderboard
</H2>
{!loading ? (
<>
<Search searchQueryHandler={searchQueryHandler} />
<Table
challengeName={props.challengeName}
headerElements={getLeaderboardHeaderMobile()}
tableType="leaderboard"
gridTemplateColumns={
entries[0]
? '1fr 2fr 3fr ' +
'2fr '.repeat(entries[0].evaluations.length) +
'1fr 2fr'
: ''
}
user={props.user}
staticColumnElements={[
{ name: 'id', format: null, order: 1, align: 'left' },
{ name: 'submitter', format: null, order: 2, align: 'left' },
{ name: 'description', format: null, order: 3, 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: 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">
Leaderboard
</H2>
{!loading ? (
<>
<Search searchQueryHandler={searchQueryHandler} />
<Table
challengeName={props.challengeName}
headerElements={getLeaderboardHeader()}
gridTemplateColumns={
entries[0]
? '1fr 2fr 3fr ' +
'2fr '.repeat(entries[0].evaluations.length) +
'1fr 2fr'
: ''
}
user={props.user}
staticColumnElements={[
{ name: 'id', format: null, order: 1, align: 'left' },
{ name: 'submitter', format: null, order: 2, align: 'left' },
{ name: 'description', format: null, order: 3, 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: 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 (
<FlexColumn
padding="24px"
gap="32px"
as="section"
width="100%"
maxWidth="1600px"
>
<H2 as="h2">Leaderboard</H2>
{!loading ? (
<>
<Search
searchQueryHandler={(event) =>
searchQueryHandler(event, entries, setPageNr, setEntries)
}
/>
{elements.length > 0 && entries[0] && (
<div style={{ width: '100%', overflowX: 'auto' }}>
<Table
items={elements}
orderedKeys={orderKeys(entries[0])}
sortByUpdate={sortByUpdate}
rowFooter={false}
/>
</div>
)}
<Pager
pageNr={pageNr}
elements={entries}
setPageNr={setPageNr}
width="72px"
borderRadius="64px"
pages={CALC_PAGES(entries, 2)}
number={`${pageNr} / ${CALC_PAGES(entries, 2)}`}
/>
</>
) : (
<Loading />
)}
</FlexColumn>
<>
<Media query={theme.mobile}>{mobileRender()}</Media>
<Media query={theme.desktop}>{desktopRender()}</Media>
</>
);
};
Leaderboard.propsTypes = {
challengeName: PropsTypes.string,
};
Leaderboard.defaultProps = {
challengeName: '',
};
export default Leaderboard;

View File

@ -1,27 +0,0 @@
const orderKeys = (elem) => {
if (elem) {
let result = ['#', 'submitter', 'description'];
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();
const testBkeys = elemKeys
.filter((key) => key.split('.')[1] === 'test-B')
.sort();
result = result.concat(dev0keys);
result = result.concat(dev1keys);
result = result.concat(testAkeys);
result = result.concat(testBkeys);
result.push('times', 'when');
return result;
}
return null;
};
export default orderKeys;

View File

@ -1,135 +1,219 @@
import React from 'react';
import { FlexColumn } from '../../utils/containers';
import { H2 } from '../../utils/fonts';
import getMyEntries from '../../api/getMyEntries';
import Pager from '../../components/generic/Pager';
import { CALC_PAGES } from '../../utils/globals';
import {
CALC_PAGES,
EVALUATIONS_FORMAT,
IS_MOBILE,
RENDER_WHEN,
} from '../../utils/globals';
import Media from 'react-media';
import theme from '../../utils/theme';
import Loading from '../../components/generic/Loading';
import Table from '../../components/generic/Table/Table';
import Table from '../../components/generic/Table';
import myEntriesSearchQueryHandler from './myEntriesSearchQueryHandler';
import Search from '../../components/generic/Search';
import orderKeys from './orderKeys';
import { ELEMENTS_PER_PAGE } from '../../utils/globals';
import getEntries from '../../api/getEntries';
import searchHandler from './searchHandler';
const MyEntries = (props) => {
// const [myEntriesFromAPI, setMyEntriesFromAPI] = React.useState({});
const [myEntriesAll, setMyEntriesAll] = React.useState([]);
const [myEntries, setMyEntries] = React.useState([]);
const [myEntriesFromAPI, setMyEntriesFromAPI] = React.useState({});
const [myEntriesAll, setMyEntriesAll] = React.useState({});
const [myEntries, setMyEntries] = React.useState({});
const [loading, setLoading] = React.useState(true);
const [pageNr, setPageNr] = React.useState(1);
const [idSorted, setIdSorted] = React.useState([]);
const [whenSorted, setWhenSorted] = React.useState(false);
const [scoresSorted, setScoresSorted] = React.useState([]);
React.useMemo(() => {
getEntries(
'challenge-my-submissions',
React.useEffect(() => {
challengesRequest();
// eslint-disable-next-line
}, []);
const searchQueryHandler = (event) => {
myEntriesSearchQueryHandler(event, myEntriesAll, setPageNr, setMyEntries);
};
const getPossibleMetrics = () => {
let metrics = [];
for (let test of myEntriesFromAPI.tests) {
let myEval = `${test.metric}.${test.name}`;
if (myEval && !metrics.includes(myEval)) {
metrics.push(myEval);
}
}
return metrics;
};
const getMyEntriesHeader = () => {
let header = ['#'];
if (IS_MOBILE()) header.push('when');
for (let myEval of getPossibleMetrics()) {
header.push(myEval);
}
if (!IS_MOBILE()) header.push('when');
return header;
};
const challengesRequest = () => {
getMyEntries(
props.challengeName,
[setMyEntries, setMyEntriesAll],
setMyEntriesFromAPI,
setMyEntriesAll,
setMyEntries,
setLoading,
setScoresSorted
);
}, [props.challengeName]);
};
const sortByUpdate = React.useCallback(
(elem) => {
let newEntries = myEntries.slice();
const possibleMetrics = orderKeys(myEntries[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 '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;
}
setMyEntries(newEntries);
},
[idSorted, myEntries, scoresSorted, whenSorted]
);
const sortByUpdate = (elem, i) => {
let newEntries = myEntries;
switch (elem) {
case '#':
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 = 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;
}
setMyEntries(newEntries);
};
const n = (pageNr - 1) * (ELEMENTS_PER_PAGE * 2);
let elements = myEntries.slice(n, n + ELEMENTS_PER_PAGE * 2);
const mobileRender = () => {
return (
<FlexColumn padding="24px 12px" width="70%" as="section" id="start">
<H2 as="h2" margin="0 0 12px 0">
My Entries
</H2>
{!loading ? (
<>
<Search searchQueryHandler={searchQueryHandler} />
<Table
challengeName={props.challengeName}
headerElements={getMyEntriesHeader()}
possibleMetrics={getPossibleMetrics()}
tableType="myEntries"
gridTemplateColumns={
'1fr ' + '4fr '.repeat(getMyEntriesHeader().length - 1)
}
staticColumnElements={[
{ name: 'id', format: null, order: 1, align: 'left' },
{ name: 'when', format: RENDER_WHEN, order: 3, align: 'right' },
]}
iterableColumnElement={{
name: 'evaluations',
format: EVALUATIONS_FORMAT,
order: 2,
align: 'left',
}}
pageNr={pageNr}
elements={myEntries}
sortByUpdate={sortByUpdate}
/>
<Pager
pageNr={pageNr}
elements={myEntries}
setPageNr={setPageNr}
width="48px"
borderRadius="64px"
pages={CALC_PAGES(myEntries)}
number={`${pageNr} / ${CALC_PAGES(myEntries)}`}
/>
</>
) : (
<Loading />
)}
</FlexColumn>
);
};
const desktopRender = () => {
return (
<FlexColumn padding="24px" as="section" width="100%" maxWidth="1600px">
<FlexColumn padding="24px 12px" width="70%" as="section" id="start">
<H2 as="h2" margin="0 0 32px 0">
My Entries
</H2>
</FlexColumn>
{myEntries && !loading ? (
<>
<Search searchQueryHandler={searchQueryHandler} />
<Table
challengeName={props.challengeName}
headerElements={getMyEntriesHeader()}
possibleMetrics={getPossibleMetrics()}
gridTemplateColumns={
'1fr ' + '3fr '.repeat(getMyEntriesHeader().length - 2) + ' 4fr'
}
staticColumnElements={[
{ name: 'id', format: null, order: 1, align: 'left' },
{ name: 'when', format: RENDER_WHEN, order: 3, align: 'right' },
]}
iterableColumnElement={{
name: 'evaluations',
format: EVALUATIONS_FORMAT,
order: 2,
align: 'left',
}}
pageNr={pageNr}
elements={myEntries}
sortByUpdate={sortByUpdate}
/>
<Pager
pageNr={pageNr}
elements={myEntries}
setPageNr={setPageNr}
width="72px"
mobileRender
borderRadius="64px"
pages={CALC_PAGES(myEntries, 2)}
number={`${pageNr} / ${CALC_PAGES(myEntries, 2)}`}
/>
</>
) : (
<Loading />
)}
</FlexColumn>
);
};
return (
<FlexColumn
padding="24px"
gap="32px"
as="section"
width="100%"
maxWidth="1600px"
>
<H2 as="h2">My Entries</H2>
{!loading ? (
<>
<Search
searchQueryHandler={(event) =>
searchHandler(event, myEntriesAll, setPageNr, setMyEntries)
}
/>
{elements.length > 0 && myEntries[0] && (
<div style={{ width: '100%', overflowX: 'auto' }}>
<Table
items={elements}
orderedKeys={orderKeys(myEntries[0])}
sortByUpdate={sortByUpdate}
popUpMessageHandler={props.popUpMessageHandler}
/>
</div>
)}
<Pager
pageNr={pageNr}
elements={myEntries}
setPageNr={setPageNr}
width="72px"
borderRadius="64px"
pages={CALC_PAGES(myEntries, 2)}
number={`${pageNr} / ${CALC_PAGES(myEntries, 2)}`}
/>
</>
) : (
<Loading />
)}
</FlexColumn>
<>
<Media query={theme.mobile}>{mobileRender()}</Media>
<Media query={theme.desktop}>{desktopRender()}</Media>
</>
);
};

View File

@ -0,0 +1,33 @@
const myEntriesSearchQueryHandler = (
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 } = entry;
let evaluations = '';
if (entry.evaluations) {
for (let evaluation of Object.values(entry.evaluations)) {
evaluations += ` ${evaluation}`;
}
}
const str = `${id} ${when.slice(11, 16)} ${when.slice(
0,
10
)} ${evaluations}`;
console.log(entry);
console.log(str);
if (str.toLowerCase().includes(searchQuery.toLowerCase()))
submissionsToRender.push(entry);
}
setEntries(submissionsToRender);
}
};
export default myEntriesSearchQueryHandler;

Some files were not shown because too many files have changed in this diff Show More