Compare commits

...

51 Commits

Author SHA1 Message Date
a77a31d01c table buttons acessibility and label correction 2023-10-01 21:31:27 +02:00
970a948cec get current user profile info to disable edit and delete submission 2023-09-29 16:07:36 +02:00
4dbcc540d6 TableStyle__td padding with rowFooter correction 2023-08-11 13:20:25 +02:00
836ec0aef2 ColumnFilterButton correction and table DOM structure correction 2023-08-11 13:10:56 +02:00
6c4da8c181 disable buttons and leaderboard iterator 2023-08-11 12:28:24 +02:00
aa1c1c3737 edit and delete submission refactor and handle errors 2023-08-04 16:35:15 +02:00
00a9a82bcb edit submission handle 2023-07-31 19:21:20 +02:00
01f82b1365 init editSubmission design and profile data and manage research 2023-07-28 13:32:20 +02:00
afd2361689 separate MobileTableStyles to another file 2023-07-28 12:25:00 +02:00
cfeac6b920 edit submission request init 2023-07-23 11:23:09 +02:00
d26a91b65c init new mobile table 2023-07-21 16:00:05 +02:00
0b3d809e3b refactor and style DeletePopUp 2023-07-14 14:57:11 +02:00
7d7b6d88b9 warning popUp for delete submission 2023-07-14 13:10:20 +02:00
38735c7982 Pager wrong props in Challenges and Leaderboard correction 2023-07-05 22:36:25 +02:00
671d0ed650 test-B evaluations visible in Tables 2023-07-02 19:29:32 +02:00
d645964266 correct popUpMessenge in MyEntries 2023-06-30 14:05:14 +02:00
3610415657 scroll-x for tables 2023-06-30 11:20:55 +02:00
69f65846c2 fix popUpMessageHandler, and popUp message when delete submission impossible 2023-06-28 19:12:06 +02:00
74e0ef19db prototype delete worked 2023-06-14 12:36:22 +02:00
decd4faf22 refactor Leaderboard and new Table component structure 2023-06-14 11:17:09 +02:00
1abe2ddfaf refactor Leaderboard in progress 2023-06-09 17:05:37 +02:00
b8b4c31b45 MyEntries with new table and logic 2023-06-09 15:50:45 +02:00
e5d0e50102 refactor: AllEntries Table replaced by new table and logic 2023-06-09 13:57:11 +02:00
098e865e91 sort columns in New Table complete 2023-06-09 12:42:19 +02:00
3fcd4b4154 New table search 2023-06-09 12:06:28 +02:00
8211ed629c Loading and Paging to new table 2023-06-09 11:40:13 +02:00
5a968ff026 new table structure complete 2023-06-09 11:10:41 +02:00
60ecbd331a NewTable styles 2023-06-07 12:14:38 +02:00
916796ef8e start creating new Table component 2023-06-02 15:49:33 +02:00
fccedac8f2 App component structure refactor 2023-06-02 12:13:05 +02:00
e442179718 sessionStorage globals 2023-05-31 15:57:30 +02:00
3f33dc4d65 try deleteSubmission and some refactor 2023-05-31 14:21:51 +02:00
8647164413 new Landing Page Hero and some refactor 2023-05-31 13:29:27 +02:00
c29df33a03 Landing page upgrade skelethon 2023-05-26 15:03:52 +02:00
203d2f8cd0 Submit TagsChoosePopUp search handler 2023-05-24 11:13:54 +02:00
2c4c89ef80 refactor in Submit: map submit inputs 2023-05-24 10:16:18 +02:00
e31bb7ff0f don't render baseline if not exist 2023-05-22 10:29:31 +02:00
564996604c bestScore format 2023-05-22 10:24:57 +02:00
02a0f1435c App.js small refactor 2023-05-22 10:19:25 +02:00
2751eda1e8 LandingPage refactor 2023-05-19 13:38:39 +02:00
70562c1f74 some Submit refactor 2023-05-19 13:31:10 +02:00
ea64e06b3e handle Done, Clear, Cancel in TagsChoosePopUp 2023-05-19 11:58:33 +02:00
b75309f4af submit tags request 2023-05-19 10:38:18 +02:00
d32ce4a685 refactor: create SubmissionLoading component 2023-05-17 15:50:19 +02:00
Mateusz Tylka
10789d7e39
Merge pull request #124 from amu-cai/submit_tags
Submit tags
2023-05-17 14:17:03 +02:00
55316d5509 submission tags add, remove, clear, post 2023-05-17 14:16:12 +02:00
9458061f29 adding submission tags 2023-05-15 11:31:02 +02:00
ce92499528 refactor TagsChoosePopUp component 2023-05-12 12:58:43 +02:00
ea33a44326 TagsChoose component styles refactor 2023-05-12 12:48:03 +02:00
6270a6f194 refactor files structure 2023-05-12 12:18:05 +02:00
1464bc084a popup with tags to choose init design complete 2023-05-12 11:50:02 +02:00
162 changed files with 3679 additions and 2489 deletions

4
.env
View File

@ -2,4 +2,6 @@ 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
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

View File

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

View File

@ -1,225 +0,0 @@
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 './components/specific_challenge/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;

33
src/App/App.js Normal file
View File

@ -0,0 +1,33 @@
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

@ -0,0 +1,47 @@
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

@ -0,0 +1,44 @@
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

@ -0,0 +1,146 @@
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

@ -0,0 +1,29 @@
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

@ -0,0 +1,55 @@
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;

1
src/App/index.js Normal file
View File

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

View File

@ -1,15 +1,19 @@
import KeyCloakService from '../services/KeyCloakService';
import { API } from '../utils/globals';
import SUBMIT_ACTION from '../pages/Submit/model/SubmitActionEnum';
const challengeSubmission = (
challengeName,
repoUrl,
repoBranch,
description,
setLoading
submissionTags,
dispatch
) => {
const tagNames = submissionTags.map((tag) => tag.name).join(',');
const details = {
f1: description,
f2: tagNames,
f3: repoUrl,
f4: repoBranch,
};
@ -17,6 +21,8 @@ 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('&');
@ -30,9 +36,14 @@ const challengeSubmission = (
})
.then((resp) => resp.json())
.then((data) => {
setLoading(true);
dispatch({ type: SUBMIT_ACTION.TOGGLE_SUBMISSION_LOADING });
const processUrl = API.replace('/api', '');
window.location.replace(`${processUrl}/open-view-progress/${data}#form`);
if (Number.isInteger(Number(data))) {
console.log(`${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

@ -0,0 +1,43 @@
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;

47
src/api/editSubmission.js Normal file
View File

@ -0,0 +1,47 @@
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,19 +1,62 @@
import { API } from '../utils/globals';
import KeyCloakService from '../services/KeyCloakService';
const getChallengeLeaderboard = async (
setDataState,
const getChallengeLeaderboard = (
endpoint,
challengeName,
setLoading
setDataStates,
setLoadingState,
setScoreSorted
) => {
await fetch(`${API}/leaderboard/${challengeName}`)
fetch(`${API}/${endpoint}/${challengeName}`, {
headers: { Authorization: `Bearer ${KeyCloakService.getToken()}` },
})
.then((response) => response.json())
.then((data) => {
setDataState(data.entries);
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);
});
if (setLoading) {
setLoading(false);
}
};
export default getChallengeLeaderboard;

View File

@ -1,4 +1,4 @@
import CHALLENGES_ACTION from '../pages/Challanges/model/ChallengesActionEnum';
import CHALLENGES_ACTION from '../pages/Challanges/model/ChallengesActions';
import { API } from '../utils/globals';
const getChallenges = (dispatch) => {

View File

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

View File

@ -1,16 +1,15 @@
import {API} from '../utils/globals';
import { API } from '../utils/globals';
import KeyCloakService from '../services/KeyCloakService';
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);
});
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);
});
};
export default getFullUser;
export default getFullUser;

View File

@ -1,68 +0,0 @@
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,10 @@
import { API } from '../utils/globals';
const getTags = (setTags) => {
const getTags = (setState) => {
fetch(`${API}/list-tags`)
.then((response) => response.json())
.then((data) => {
setTags(data);
setState(data);
});
};

View File

@ -0,0 +1,39 @@
<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>

After

Width:  |  Height:  |  Size: 7.5 KiB

8
src/assets/csi_logo.svg Normal file
View File

@ -0,0 +1,8 @@
<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>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,33 @@
<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>

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

@ -0,0 +1,3 @@
<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>

After

Width:  |  Height:  |  Size: 654 B

View File

@ -0,0 +1,3 @@
<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>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -1,122 +0,0 @@
import React from 'react';
import {Container, FlexColumn, FlexRow, Grid} from '../../utils/containers';
import {Body, H3} from '../../utils/fonts';
import styled from 'styled-components';
import IconLabel from '../generic/IconLabel';
import {Link} from 'react-router-dom';
import {CHALLENGE_PAGE, MINI_DESCRIPTION_RENDER} from '../../utils/globals';
import theme from '../../utils/theme';
import PropsTypes from 'prop-types';
const ChallengeStyle = styled(FlexColumn)`
padding: 12px;
border: 1px solid ${({theme}) => theme.colors.dark05};
cursor: pointer;
transition: transform 0.3s ease-in-out;
position: relative;
max-width: 420px;
* {
cursor: pointer;
}
&:hover {
transform: scale(1.05);
}
article {
width: 100%;
align-items: flex-start;
p {
width: 80%;
}
}
@media (min-width: ${({theme}) => theme.overMobile}) {
width: 360px;
padding: 20px;
justify-content: flex-start;
}
`;
const IconsGrid = styled(Grid)`
width: 100%;
grid-gap: 14px;
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 1fr;
@media (min-width: 500px) {
grid-template-columns: auto auto auto;
}
`;
const MiniChallenge = (props) => {
const deadlineRender = () => {
if (props.deadline) {
return (
<IconLabel size='24px' gap='8px' type='deadline' time={props.deadline}>
{props.deadline.slice(0, 10)}
</IconLabel>
);
}
};
return (
<ChallengeStyle as={Link} to={`${CHALLENGE_PAGE}/${props.name}`}>
<FlexColumn as='article'>
<FlexRow margin='0 0 14px 0' gap='12px' width='100%' alignmentX='space-between'>
<H3 as='h3' width='85%'>
{props.title}
</H3>
{props.type ? <IconLabel type={props.type} size='30px'/> : 'xxx'}
</FlexRow>
<Container margin='0 0 14px 0' width='85%' height='1px' backgroundColor={theme.colors.dark05}/>
<Body as='p' margin='0 0 14px 0'>
{props.description ? MINI_DESCRIPTION_RENDER(props.description) : 'xxx'}
</Body>
<IconsGrid>
<IconLabel size='24px' gap='8px' type='metric'>
{props.metric ? props.metric : 'xxx'}
</IconLabel>
<IconLabel size='24px' gap='8px' type='bestScore'>
{props.bestScore ? props.bestScore : 'xxx'}
</IconLabel>
{deadlineRender()}
<IconLabel size='24px' gap='8px' type='baseline'>
{props.baseline ? props.baseline : 'xxx'}
</IconLabel>
{props.prize ? <IconLabel size='24px' gap='8px' type='prize'>
{props.prize}
</IconLabel> : ''}
</IconsGrid>
</FlexColumn>
</ChallengeStyle>
);
};
MiniChallenge.propTypes = {
name: PropsTypes.string,
title: PropsTypes.string,
type: PropsTypes.string,
description: PropsTypes.string,
metric: PropsTypes.string,
bestScore: PropsTypes.string,
deadline: PropsTypes.string,
baseline: PropsTypes.string,
prize: PropsTypes.string
};
MiniChallenge.defaultProps = {
name: 'xxx',
title: 'xxx',
type: 'xxx',
description: 'xxx',
metric: 'xxx',
bestScore: 'xxx',
deadline: 'xxx',
baseline: 'xxx',
prize: 'xxx'
};
export default MiniChallenge;

View File

@ -1,105 +0,0 @@
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,45 +0,0 @@
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

@ -1,93 +0,0 @@
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

@ -1,82 +0,0 @@
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

@ -1,48 +0,0 @@
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

@ -33,6 +33,7 @@ const Button = (props) => {
onClick={() => props.handler()}
width={props.width}
height={props.height}
margin={props.margin}
color={props.color}
backgroundColor={props.backgroundColor}
to={props.to}

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: 122px;
width: 144px;
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: 44px;
height: 48px;
}
`;
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,60 +0,0 @@
import React from 'react';
import { FlexColumn, FlexRow, Grid } from '../../utils/containers';
import { Medium } from '../../utils/fonts';
import theme from '../../utils/theme';
import ImageButton from './ImageButton';
import pencilIco from '../../assets/pencil_ico.svg';
import styled from 'styled-components';
import PopUp from './PopUp';
import { createPortal } from 'react-dom';
const DropdownWithPopupStyle = styled(FlexColumn)`
cursor: pointer;
gap: 8px;
width: 100%;
align-items: flex-start;
* {
cursor: pointer;
}
`;
const DropdownWithPopup = (props) => {
const [tagsPopUp, setTagsPopUp] = React.useState(false);
return (
<DropdownWithPopupStyle
onClick={() => {
if (!tagsPopUp) setTagsPopUp(true);
}}
>
<Medium as="label" htmlFor={props.label}>
{props.label}
</Medium>
<Grid
borderRadius="4px"
width="100%"
height="100px"
border={`1px solid ${theme.colors.dark}`}
shadow={theme.shadow}
onChange={(e) => props.handler(e.target.value)}
padding="12px"
gridTemplateColumns="1fr auto"
>
<FlexRow height="100%" alignmentX="flex-start" alignmentY="flex-start">
dsa dsa dsa dsa dsa dsa dsa dsa dsa dsa dsa dsa dsa dsa dsa dsa dsa
dsa dsa dsa dsa dsa dsa dsa dsa dsa dsa dsa dsa dsa dsa dsa dsa dsa
dsa dsa dsa dsa dsa dsa dsa dsa dsa dsa dsa dsa dsa dsa dsa dsa dsa
dsa dsa dsa dsa dsa dsa dsa dsa dsa dsa dsa dsa dsa
</FlexRow>
<ImageButton src={pencilIco} width="20px" height="20px" />
</Grid>
{tagsPopUp &&
createPortal(
<PopUp closeHandler={() => setTagsPopUp(false)}></PopUp>,
document.body
)}
</DropdownWithPopupStyle>
);
};
export default DropdownWithPopup;

View File

@ -0,0 +1,14 @@
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

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

View File

@ -1,24 +1,29 @@
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 = () => {
return (
<LogoStyle as={Link} cursor='pointer' to='/' color={theme.colors.green}>
Gonito
</LogoStyle>
);
const Logo = (props) => {
return (
<LogoStyle
as={props.navOptions ? Link : 'span'}
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.setPage)}
onClick={() => PREVIOUS_PAGE(props.pageNr, props.setPageNr)}
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.setPage)}
onClick={() => NEXT_PAGE(props.elements, props.pageNr, props.setPageNr)}
size="cover"
backgroundColor={rightArrowVisible()}
/>

View File

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

View File

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

View File

@ -18,6 +18,7 @@ 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,81 @@
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

@ -0,0 +1,74 @@
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

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

View File

@ -0,0 +1,48 @@
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

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

View File

@ -0,0 +1,105 @@
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

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

View File

@ -0,0 +1,33 @@
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

@ -0,0 +1,66 @@
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

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

View File

@ -0,0 +1,50 @@
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

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

View File

@ -0,0 +1,33 @@
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

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

View File

@ -0,0 +1,46 @@
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

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

View File

@ -0,0 +1,37 @@
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

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

View File

@ -0,0 +1,12 @@
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

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

View File

@ -0,0 +1,15 @@
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

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

View File

@ -0,0 +1,14 @@
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

@ -0,0 +1,66 @@
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,6 +1,12 @@
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';
@ -15,8 +21,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 {
@ -32,11 +38,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};
}
}
@ -47,37 +53,61 @@ 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={() => 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>
);
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>
);
};
export default LoggedBar;
export default LoggedBar;

View File

@ -43,6 +43,7 @@ const NavBar = (props) => {
const mobileMenuHoverFalse = () => {
setMobileMenuHover(false);
};
const toggleNavMenu = () => {
if (navMenuTranslateY === 'calc(-100vh - 42px)') setNavMenuTranslateY('0');
else if (!mobileMenuHover) setNavMenuTranslateY('calc(-100vh - 42px)');
@ -51,51 +52,57 @@ const NavBar = (props) => {
return (
<NavBarStyle as="header">
<FlexRow height="100%" alignmentX="space-between" as="nav">
<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>
<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>
)}
</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

@ -1,244 +0,0 @@
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 {
CALC_PAGES,
EVALUATIONS_FORMAT,
RENDER_WHEN,
IS_MOBILE,
} from '../../../utils/globals';
import Loading from '../../generic/Loading';
import Pager from '../../generic/Pager';
import Table from '../Table';
import Search from '../../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 [scoresSorted, setScoresSorted] = React.useState([]);
const [submitterSorted, setSubmitterSorted] = React.useState(false);
const [whenSorted, setWhenSorted] = React.useState(false);
React.useEffect(() => {
if (props.challengeName) challengeDataRequest(props.challengeName);
}, [props.challengeName]);
const challengeDataRequest = (challengeName) => {
getAllEntries(challengeName, setEntriesFromApi, setEntriesAll);
getAllEntries(
challengeName,
undefined,
setEntries,
setLoading,
setScoresSorted
);
};
const 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);
}
}
}
return metrics;
};
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 (
<>
<Media query={theme.mobile}>{mobileRender()}</Media>
<Media query={theme.desktop}>{desktopRender()}</Media>
</>
);
};
export default AllEntries;

View File

@ -1,32 +0,0 @@
const allEntriesSearchQueryHandler = (
event,
entriesFromApi,
setPageNr,
setEntries
) => {
let searchQuery = event.target.value;
let submissionsToRender = [];
setPageNr(1);
if (searchQuery === '') setEntries(entriesFromApi);
else {
for (let entry of entriesFromApi) {
const { id, when, submitter } = entry;
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,42 +0,0 @@
import React from 'react';
import getFullUser from '../../../api/getFullUserInfo';
import KeyCloakService from '../../../services/KeyCloakService';
import { FlexColumn } from '../../../utils/containers';
import { IS_MOBILE } from '../../../utils/globals';
import HowToContent from './sections/HowToContent';
const HowTo = (props) => {
const [userFullInfo, setUserFullInfo] = React.useState(null);
React.useEffect(() => {
getFullUser(setUserFullInfo);
if (!KeyCloakService.isLoggedIn()) {
props.popUpMessageHandler(
'Please log in',
'To see everything you must log in',
() => KeyCloakService.doLogin
);
}
}, [props]);
return (
<FlexColumn
margin={IS_MOBILE() ? null : '64px 0 0 0'}
padding={IS_MOBILE() ? '12px 20px' : null}
gap={IS_MOBILE() ? '24px' : '48px'}
alignmentX={IS_MOBILE() ? 'flex-start' : 'center'}
maxWidth={IS_MOBILE() ? '668px' : 'none'}
>
<FlexColumn maxWidth="680px" alignmentX="flex-start" gap="48px">
<HowToContent
userFullInfo={userFullInfo}
user={props.user ? props.user : 'yourID'}
challengeName={props.challengeName}
/>
</FlexColumn>
</FlexColumn>
);
};
export default HowTo;

View File

@ -1,307 +0,0 @@
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 '../Table';
import PropsTypes from 'prop-types';
import getChallengeLeaderboard from '../../../api/getChallengeLeaderboard';
import leaderboardSearchQueryHandler from './leaderboardSearchQueryHandler';
import {
CALC_PAGES,
EVALUATIONS_FORMAT,
RENDER_WHEN,
} from '../../../utils/globals';
import Search from '../../generic/Search';
import Pager from '../../generic/Pager';
import Loading from '../../generic/Loading';
const Leaderboard = (props) => {
const [entriesFromApi, setEntriesFromApi] = React.useState([]);
const [entries, setEntries] = React.useState([]);
const [pageNr, setPageNr] = React.useState(1);
const [loading, setLoading] = React.useState(true);
const [submitterSorted, setSubmitterSorted] = React.useState(false);
const [descriptionSorted, setDescriptionSorted] = React.useState(false);
const [entriesSorted, setEntriesSorted] = React.useState(false);
const [whenSorted, setWhenSorted] = React.useState(false);
const [scoresSorted, setScoresSorted] = React.useState([]);
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 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}`);
}
}
}
return metrics;
};
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 (
<>
<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,220 +0,0 @@
import React from 'react';
import { FlexColumn } from '../../../utils/containers';
import { H2 } from '../../../utils/fonts';
import getMyEntries from '../../../api/getMyEntries';
import Pager from '../../generic/Pager';
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 '../../generic/Loading';
import Table from '../Table';
import myEntriesSearchQueryHandler from './myEntriesSearchQueryHandler';
import Search from '../../generic/Search';
const MyEntries = (props) => {
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 [whenSorted, setWhenSorted] = React.useState(false);
const [scoresSorted, setScoresSorted] = React.useState([]);
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,
setMyEntriesFromAPI,
setMyEntriesAll,
setMyEntries,
setLoading,
setScoresSorted
);
};
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 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 (
<>
<Media query={theme.mobile}>{mobileRender()}</Media>
<Media query={theme.desktop}>{desktopRender()}</Media>
</>
);
};
export default MyEntries;

View File

@ -1,33 +0,0 @@
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;

View File

@ -1,88 +0,0 @@
import React from 'react';
import { createPortal } from 'react-dom';
import { FlexColumn } from '../../utils/containers';
import { H2, Menu } from '../../utils/fonts';
import SubmitInput from '../generic/SubmitInput';
import Button from '../generic/Button';
import theme from '../../utils/theme';
import challengeSubmission from '../../api/challengeSubmissionPost';
import Loading from '../generic/Loading';
import getTags from '../../api/getTags';
import DropdownWithPopup from '../generic/DropdownWithPopup';
const Submit = (props) => {
const [description, setDescription] = React.useState('');
const [repoUrl, setRepoUrl] = React.useState('');
const [repoBranch, setRepoBranch] = React.useState('');
const [loading, setLoading] = React.useState(false);
const [tags, setTags] = React.useState([]);
// eslint-disable-next-line no-unused-vars
const [submissionTags, setSubmissionTags] = React.useState([]);
React.useMemo(() => {
getTags(setTags);
}, []);
const challengeSubmissionSubmit = () => {
setLoading(true);
challengeSubmission(
props.challengeName,
repoUrl,
repoBranch,
description,
setLoading
);
};
if (!loading) {
console.log(tags);
return (
<FlexColumn
margin="40px 0 0 0"
padding="24px"
as="section"
gap="64px"
maxWidth="624px"
width="100%"
alignmentX="flex-start"
>
<H2 as="h2" width="100%" textAlign="center">
Submit a solution to the challenge
</H2>
<FlexColumn width="100%" gap="32px">
<SubmitInput
label="Submission description"
handler={setDescription}
/>
<SubmitInput label="Submission repo URL" handler={setRepoUrl} />
<SubmitInput label="Submission repo branch" handler={setRepoBranch} />
<DropdownWithPopup
label="Submission tags"
handler={setSubmissionTags}
/>
</FlexColumn>
<Button width="122px" height="44px" handler={challengeSubmissionSubmit}>
<Menu color={theme.colors.white}>Submit</Menu>
</Button>
</FlexColumn>
);
} else {
return createPortal(
<FlexColumn
position="fixed"
top="0"
left="0"
width="100%"
height="100vh"
zIndex="100"
backgroundColor={theme.colors.white}
>
<H2 as="h1">Submission processing...</H2>
<Loading />
</FlexColumn>,
document.body
);
}
};
export default Submit;

View File

@ -1,283 +0,0 @@
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

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

View File

@ -0,0 +1,170 @@
import React from 'react';
import { FlexColumn } from '../../utils/containers';
import { H2 } from '../../utils/fonts';
import Pager from '../../components/generic/Pager';
import Search from '../../components/generic/Search';
import getEntries from '../../api/getEntries';
import Table from '../../components/generic/Table';
import Loading from '../../components/generic/Loading';
import { CALC_PAGES, ELEMENTS_PER_PAGE } from '../../utils/globals';
import searchQueryHandler from './searchHandler';
import orderKeys from './orderKeys';
import KeyCloakService from '../../services/KeyCloakService';
const AllEntries = (props) => {
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();
}, [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;
}
setEntries(newEntries);
},
[entries, idSorted, scoresSorted, submitterSorted, whenSorted]
);
const n = (pageNr - 1) * (ELEMENTS_PER_PAGE * 2);
const elements = entries.slice(n, n + ELEMENTS_PER_PAGE * 2);
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>
);
};
export default AllEntries;

View File

@ -0,0 +1,27 @@
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

@ -0,0 +1,28 @@
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

@ -3,13 +3,13 @@ import Media from 'react-media';
import theme from '../../utils/theme';
import getChallenges from '../../api/getChallenges';
import { CHALLENGES_STATUS_FILTER } from '../../utils/globals';
import FiltersMenu from '../../components/challenges_list/FiltersMenu';
import FiltersMenu from './components/FiltersMenu';
import statusFilterHandle from './functions/statusFilterHandle';
import ChallengesMobile from './components/ChallengesMobile';
import ChallengesDesktop from './components/ChallengesDesktop';
import challengeSearchQueryHandler from './functions/challengeSearchQueryHandler';
import ChallengesReducer from './model/ChallengesReducer';
import CHALLENGES_ACTION from './model/ChallengesActionEnum';
import CHALLENGES_ACTION from './model/ChallengesActions';
const Challenges = () => {
const [state, dispatch] = React.useReducer(ChallengesReducer, {
@ -33,7 +33,7 @@ const Challenges = () => {
statusFilterHandle(state.statusFilter, state.challenges, dispatch);
}, [state.statusFilter, state.challenges]);
const setPage = React.useCallback((value) => {
const setPageNr = React.useCallback((value) => {
dispatch({ type: CHALLENGES_ACTION.SET_PAGE, payload: value });
}, []);
@ -73,7 +73,7 @@ const Challenges = () => {
dispatch={dispatch}
filtersMenuRender={filtersMenuRender}
searchQueryHandler={searchQueryHandler}
setPage={setPage}
setPageNr={setPageNr}
filtersMenu={state.filtersMenu}
loading={state.loading}
pageNr={state.pageNr}
@ -85,7 +85,7 @@ const Challenges = () => {
dispatch={dispatch}
filtersMenuRender={filtersMenuRender}
searchQueryHandler={searchQueryHandler}
setPage={setPage}
setPageNr={setPageNr}
filtersMenu={state.filtersMenu}
loading={state.loading}
pageNr={state.pageNr}

View File

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

View File

@ -7,7 +7,7 @@ import Search from '../../../components/generic/Search';
import { CALC_PAGES } from '../../../utils/globals';
import renderChallenges from '../functions/renderChallenges';
import Loading from '../../../components/generic/Loading';
import CHALLENGES_ACTION from '../model/ChallengesActionEnum';
import CHALLENGES_ACTION from '../model/ChallengesActions';
const ChallengesMobile = (props) => {
return (

View File

@ -1,10 +1,10 @@
import React from 'react';
import { FlexColumn, Grid, Svg } from '../../utils/containers';
import Filter from '../generic/Filter';
import { Body, H3, Medium } from '../../utils/fonts';
import arrow from '../../assets/arrow.svg';
import { FlexColumn, Grid, Svg } from '../../../utils/containers';
import Filter from '../../../components/generic/Filter';
import { Body, H3, Medium } from '../../../utils/fonts';
import arrow from '../../../assets/arrow.svg';
import Media from 'react-media';
import theme from '../../utils/theme';
import theme from '../../../utils/theme';
const FilterBy = (props) => {
const renderFilterOptions = () => {

View File

@ -1,12 +1,12 @@
import React from 'react';
import { FlexColumn, FlexRow, TransBack } from '../../../utils/containers';
import Button from '../../generic/Button';
import theme from '../../../utils/theme';
import { FlexColumn, FlexRow, TransBack } from '../../../../utils/containers';
import Button from '../../../../components/generic/Button';
import theme from '../../../../utils/theme';
import styled from 'styled-components';
import FilterBy from '../FilterBy';
import filterOptions from './filterOptions';
import Media from 'react-media';
import CHALLENGES_ACTION from '../../../pages/Challanges/model/ChallengesActionEnum';
import CHALLENGES_ACTION from '../../model/ChallengesActions';
const FiltersMenuStyle = styled(FlexColumn)`
position: fixed;

View File

@ -0,0 +1,129 @@
import React from 'react';
import {
Container,
FlexColumn,
FlexRow,
Grid,
} from '../../../utils/containers';
import { Body, H3 } from '../../../utils/fonts';
import styled from 'styled-components';
import IconLabel from '../../../components/generic/IconLabel';
import { Link } from 'react-router-dom';
import {
CHALLENGE_PAGE,
MINI_DESCRIPTION_RENDER,
} from '../../../utils/globals';
import theme from '../../../utils/theme';
const ChallengeStyle = styled(FlexColumn)`
padding: 12px;
border: 1px solid ${({ theme }) => theme.colors.dark05};
cursor: pointer;
transition: transform 0.3s ease-in-out;
position: relative;
max-width: 420px;
* {
cursor: pointer;
}
&:hover {
transform: scale(1.05);
}
article {
width: 100%;
align-items: flex-start;
p {
width: 80%;
}
}
@media (min-width: ${({ theme }) => theme.overMobile}) {
width: 360px;
padding: 20px;
justify-content: flex-start;
}
`;
const IconsGrid = styled(Grid)`
width: 100%;
grid-gap: 14px;
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 1fr;
@media (min-width: 500px) {
grid-template-columns: auto auto auto;
}
`;
const MiniChallenge = (props) => {
const deadlineRender = () => {
if (props.deadline) {
return (
<IconLabel size="24px" gap="8px" type="deadline" time={props.deadline}>
{props.deadline.slice(0, 10)}
</IconLabel>
);
}
};
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">
<FlexRow
margin="0 0 14px 0"
gap="12px"
width="100%"
alignmentX="space-between"
>
<H3 as="h3" width="85%">
{props.title}
</H3>
{props.type ? <IconLabel type={props.type} size="30px" /> : 'xxx'}
</FlexRow>
<Container
margin="0 0 14px 0"
width="85%"
height="1px"
backgroundColor={theme.colors.dark05}
/>
<Body as="p" margin="0 0 14px 0">
{props.description
? MINI_DESCRIPTION_RENDER(props.description)
: 'xxx'}
</Body>
<IconsGrid>
<IconLabel size="24px" gap="8px" type="metric">
{props.metric ? props.metric : 'xxx'}
</IconLabel>
<IconLabel size="24px" gap="8px" type="bestScore">
{props.bestScore ? props.bestScore.slice(0, 6) : 'xxx'}
</IconLabel>
{deadlineRender()}
{baselineRender()}
{props.prize ? (
<IconLabel size="24px" gap="8px" type="prize">
{props.prize}
</IconLabel>
) : (
''
)}
</IconsGrid>
</FlexColumn>
</ChallengeStyle>
);
};
export default MiniChallenge;

View File

@ -1,4 +1,4 @@
import CHALLENGES_ACTION from '../model/ChallengesActionEnum';
import CHALLENGES_ACTION from '../model/ChallengesActions';
const challengeSearchQueryHandler = (event, challengesFromAPI, dispatch) => {
let searchQuery = event.target.value;

View File

@ -1,5 +1,5 @@
import { ELEMENTS_PER_PAGE } from '../../../utils/globals';
import MiniChallenge from '../../../components/challenges_list/MiniChallenge';
import MiniChallenge from '../components/MiniChallenge';
import { Grid } from '../../../utils/containers';
import styled from 'styled-components';

View File

@ -1,5 +1,5 @@
import { CHALLENGES_STATUS_FILTER } from '../../../utils/globals';
import CHALLENGES_ACTION from '../model/ChallengesActionEnum';
import CHALLENGES_ACTION from '../model/ChallengesActions';
const dateIsOlder = (newerDate, olderDate) => {
if (newerDate.year > olderDate.year) return true;

View File

@ -1,4 +1,4 @@
import CHALLENGES_ACTION from './ChallengesActionEnum';
import CHALLENGES_ACTION from './ChallengesActions';
const ChallengesReducer = (state, action) => {
switch (action.type) {

View File

@ -3,30 +3,27 @@ import { Container, FlexColumn, FlexRow, Svg } from '../../utils/containers';
import { useParams } from 'react-router-dom';
import { H1, Medium } from '../../utils/fonts';
import theme from '../../utils/theme';
import MobileChallengeMenu from './MobileChallengeMenu';
import Leaderboard from './Leaderboard/Leaderboard';
import Readme from './Readme';
import HowTo from './HowTo/HowTo';
import MyEntries from './MyEntries/MyEntries';
import Submit from './Submit';
import MobileChallengeMenu from './components/MobileChallengeMenu';
import Leaderboard from '../Leaderboard';
import Readme from '../Readme';
import HowTo from '../HowTo';
import MyEntries from '../MyEntries';
import Submit from '../Submit';
import Media from 'react-media';
import DesktopChallengeMenu from './DesktopChallengeMenu';
import DesktopChallengeMenu from './components/DesktopChallengeMenu';
import { CHALLENGE_SECTIONS, RENDER_ICO } from '../../utils/globals';
import textIco from '../../assets/text_ico.svg';
import getChallengeInfo from '../../api/getChallengeInfo';
import Loading from '../generic/Loading';
import getUser from '../../api/getUser';
import AllEntries from './AllEntries/AllEntries';
import Loading from '../../components/generic/Loading';
import AllEntries from '../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 = () => {
@ -36,12 +33,15 @@ const Challenge = (props) => {
<Leaderboard
challengeName={challengeName}
mainMetric={challenge.mainMetric}
user={user}
/>
);
case CHALLENGE_SECTIONS.ALL_ENTRIES:
return (
<AllEntries challengeName={challengeName} setLoading={setLoading} />
<AllEntries
challengeName={challengeName}
setLoading={setLoading}
popUpMessageHandler={props.popUpMessageHandler}
/>
);
case CHALLENGE_SECTIONS.README:
return (
@ -57,11 +57,15 @@ const Challenge = (props) => {
<HowTo
popUpMessageHandler={props.popUpMessageHandler}
challengeName={challengeName}
user={user}
/>
);
case CHALLENGE_SECTIONS.MY_ENTRIES:
return <MyEntries challengeName={challengeName} />;
return (
<MyEntries
challengeName={challengeName}
popUpMessageHandler={props.popUpMessageHandler}
/>
);
case CHALLENGE_SECTIONS.SUBMIT:
return <Submit challengeName={challengeName} setLoading={setLoading} />;
default:

View File

@ -1,15 +1,15 @@
import React from 'react';
import styled from 'styled-components';
import { FlexColumn } from '../../utils/containers';
import { H3 } from '../../utils/fonts';
import { FlexColumn } from '../../../utils/containers';
import { H3 } from '../../../utils/fonts';
import PropsTypes from 'prop-types';
import KeyCloakService from '../../services/KeyCloakService';
import KeyCloakService from '../../../services/KeyCloakService';
import { Link } from 'react-router-dom';
import {
MENU_CHALLENGE_SECTIONS_WITH_LOGIN,
MENU_CHALLENGE_SECTIONS_NO_LOGIN,
IS_MOBILE,
} from '../../utils/globals';
} from '../../../utils/globals';
const DesktopChallengeMenuStyle = styled(FlexColumn)`
justify-content: flex-start;

View File

@ -1,14 +1,14 @@
import React from 'react';
import { FlexRow } from '../../utils/containers';
import { FlexRow } from '../../../utils/containers';
import styled from 'styled-components';
import { Medium } from '../../utils/fonts';
import { Medium } from '../../../utils/fonts';
import PropsTypes from 'prop-types';
import KeyCloakService from '../../services/KeyCloakService';
import KeyCloakService from '../../../services/KeyCloakService';
import {
CHALLENGE_SECTIONS,
MENU_CHALLENGE_SECTIONS_WITH_LOGIN,
MENU_CHALLENGE_SECTIONS_NO_LOGIN,
} from '../../utils/globals';
} from '../../../utils/globals';
import { Link } from 'react-router-dom';
const MenuOption = styled(Medium)`

View File

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

44
src/pages/HowTo/HowTo.js Normal file
View File

@ -0,0 +1,44 @@
import React from 'react';
import getFullUser from '../../api/getFullUserInfo';
import KeyCloakService from '../../services/KeyCloakService';
import { FlexColumn } from '../../utils/containers';
import { IS_MOBILE } from '../../utils/globals';
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);
}, [props]);
return (
<FlexColumn
margin={IS_MOBILE() ? null : '64px 0 0 0'}
padding={IS_MOBILE() ? '12px 20px' : null}
gap={IS_MOBILE() ? '24px' : '48px'}
alignmentX={IS_MOBILE() ? 'flex-start' : 'center'}
maxWidth={IS_MOBILE() ? '668px' : 'none'}
>
<FlexColumn maxWidth="680px" alignmentX="flex-start" gap="48px">
<HowToContent
userFullInfo={userFullInfo}
user={username ? username : 'yourID'}
challengeName={props.challengeName}
/>
</FlexColumn>
</FlexColumn>
);
};
export default HowTo;

View File

@ -1,9 +1,9 @@
import React from 'react';
import { IS_MOBILE } from '../../../../utils/globals';
import { Body, H2, Medium } from '../../../../utils/fonts';
import { FlexColumn, Grid } from '../../../../utils/containers';
import CircleNumber from '../../../generic/CircleNumber';
import CodeShell from '../../../generic/CodeShell';
import { IS_MOBILE } from '../../../utils/globals';
import { Body, H2, Medium } from '../../../utils/fonts';
import { FlexColumn, Grid } from '../../../utils/containers';
import CircleNumber from '../../../components/generic/CircleNumber';
import CodeShell from '../../../components/generic/CodeShell';
const HowToContent = (props) => {
const pullCodeLineRender = () => {

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