Compare commits

..

No commits in common. "9455bcc6c39e8b781c933d9fd2e334e4969463dd" and "3e66cfa3a9a40b228b27be102107aa0cbb8603d2" have entirely different histories.

7 changed files with 176 additions and 233 deletions

View File

@ -11,7 +11,6 @@ import {
POLICY_PRIVACY_PAGE, POLICY_PRIVACY_PAGE,
LOGIN_REQUIRED_PAGES, LOGIN_REQUIRED_PAGES,
ROOT_URL, ROOT_URL,
CHALLENGE_SECTIONS,
} from './utils/globals'; } from './utils/globals';
import KeyCloakService from './services/KeyCloakService'; import KeyCloakService from './services/KeyCloakService';
import React from 'react'; import React from 'react';
@ -122,36 +121,36 @@ const App = () => {
<Routes> <Routes>
<Route <Route
path={`${CHALLENGE_PAGE}/:challengeId`} path={`${CHALLENGE_PAGE}/:challengeId`}
element={<Challenge section={CHALLENGE_SECTIONS.LEADERBOARD} />} element={<Challenge section={0} />}
/> />
<Route <Route
path={`${CHALLENGE_PAGE}/:challengeId/leaderboard`} path={`${CHALLENGE_PAGE}/:challengeId/leaderboard`}
element={<Challenge section={CHALLENGE_SECTIONS.LEADERBOARD} />} element={<Challenge section={0} />}
/> />
<Route <Route
path={`${CHALLENGE_PAGE}/:challengeId/allentries`} path={`${CHALLENGE_PAGE}/:challengeId/allentries`}
element={<Challenge section={CHALLENGE_SECTIONS.ALL_ENTRIES} />} element={<Challenge section={1} />}
/> />
<Route <Route
path={`${CHALLENGE_PAGE}/:challengeId/readme`} path={`${CHALLENGE_PAGE}/:challengeId/readme`}
element={<Challenge section={CHALLENGE_SECTIONS.README} />} element={<Challenge section={2} />}
/> />
<Route <Route
path={`${CHALLENGE_PAGE}/:challengeId/howto`} path={`${CHALLENGE_PAGE}/:challengeId/howto`}
element={ element={
<Challenge <Challenge
popUpMessageHandler={popUpMessageHandler} popUpMessageHandler={popUpMessageHandler}
section={CHALLENGE_SECTIONS.HOW_TO} section={3}
/> />
} }
/> />
<Route <Route
path={`${CHALLENGE_PAGE}/:challengeId/myentries`} path={`${CHALLENGE_PAGE}/:challengeId/myentries`}
element={<Challenge section={CHALLENGE_SECTIONS.MY_ENTRIES} />} element={<Challenge section={4} />}
/> />
<Route <Route
path={`${CHALLENGE_PAGE}/:challengeId/submit`} path={`${CHALLENGE_PAGE}/:challengeId/submit`}
element={<Challenge section={CHALLENGE_SECTIONS.SUBMIT} />} element={<Challenge section={5} />}
/> />
<Route path={CHALLENGES_PAGE} element={<Challenges />} /> <Route path={CHALLENGES_PAGE} element={<Challenges />} />
<Route <Route

View File

@ -11,7 +11,7 @@ import MyEntries from './MyEntries/MyEntries';
import Submit from './Submit'; import Submit from './Submit';
import Media from 'react-media'; import Media from 'react-media';
import DesktopChallengeMenu from './DesktopChallengeMenu'; import DesktopChallengeMenu from './DesktopChallengeMenu';
import { CHALLENGE_SECTIONS, RENDER_ICO } from '../../utils/globals'; import { RENDER_ICO } from '../../utils/globals';
import textIco from '../../assets/text_ico.svg'; import textIco from '../../assets/text_ico.svg';
import getChallengeInfo from '../../api/getChallengeInfo'; import getChallengeInfo from '../../api/getChallengeInfo';
import Loading from '../generic/Loading'; import Loading from '../generic/Loading';
@ -31,7 +31,7 @@ const Challenge = (props) => {
const sectionRender = () => { const sectionRender = () => {
switch (props.section) { switch (props.section) {
case CHALLENGE_SECTIONS.LEADERBOARD: case 0:
return ( return (
<Leaderboard <Leaderboard
challengeName={challengeName} challengeName={challengeName}
@ -39,11 +39,11 @@ const Challenge = (props) => {
user={user} user={user}
/> />
); );
case CHALLENGE_SECTIONS.ALL_ENTRIES: case 1:
return ( return (
<AllEntries challengeName={challengeName} setLoading={setLoading} /> <AllEntries challengeName={challengeName} setLoading={setLoading} />
); );
case CHALLENGE_SECTIONS.README: case 2:
return ( return (
<Readme <Readme
challengeName={challengeName} challengeName={challengeName}
@ -52,7 +52,7 @@ const Challenge = (props) => {
deadline={challenge.deadline} deadline={challenge.deadline}
/> />
); );
case CHALLENGE_SECTIONS.HOW_TO: case 3:
return ( return (
<HowTo <HowTo
popUpMessageHandler={props.popUpMessageHandler} popUpMessageHandler={props.popUpMessageHandler}
@ -60,9 +60,9 @@ const Challenge = (props) => {
user={user} user={user}
/> />
); );
case CHALLENGE_SECTIONS.MY_ENTRIES: case 4:
return <MyEntries challengeName={challengeName} />; return <MyEntries challengeName={challengeName} />;
case CHALLENGE_SECTIONS.SUBMIT: case 5:
return <Submit challengeName={challengeName} setLoading={setLoading} />; return <Submit challengeName={challengeName} setLoading={setLoading} />;
default: default:
return ( return (

View File

@ -5,10 +5,6 @@ import { H3 } from '../../utils/fonts';
import PropsTypes from 'prop-types'; import PropsTypes from 'prop-types';
import KeyCloakService from '../../services/KeyCloakService'; import KeyCloakService from '../../services/KeyCloakService';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import {
MENU_CHALEENGE_SECTIONS_WITH_LOGIN,
MENU_CHALLENGE_SECTIONS_NO_LOGIN,
} from '../../utils/globals';
const DesktopChallengeMenuStyle = styled(FlexColumn)` const DesktopChallengeMenuStyle = styled(FlexColumn)`
justify-content: flex-start; justify-content: flex-start;
@ -31,7 +27,6 @@ const Option = styled(FlexColumn)`
cursor: pointer; cursor: pointer;
background-color: ${({ theme, active }) => background-color: ${({ theme, active }) =>
active ? theme.colors.green05 : theme.colors.white}; active ? theme.colors.green05 : theme.colors.white};
text-decoration: none;
* { * {
cursor: pointer; cursor: pointer;
@ -43,9 +38,16 @@ const Option = styled(FlexColumn)`
`; `;
const DesktopChallengeMenu = (props) => { const DesktopChallengeMenu = (props) => {
let options = MENU_CHALLENGE_SECTIONS_NO_LOGIN; let options = ['Leaderboard', 'All entries', 'Readme', 'How to'];
if (KeyCloakService.isLoggedIn()) if (KeyCloakService.isLoggedIn())
options = MENU_CHALEENGE_SECTIONS_WITH_LOGIN; options = [
'Leaderboard',
'All entries',
'Readme',
'How to',
'My entries',
'Submit',
];
return ( return (
<DesktopChallengeMenuStyle> <DesktopChallengeMenuStyle>
{options.map((option, index) => { {options.map((option, index) => {

View File

@ -4,12 +4,6 @@ import styled from 'styled-components';
import { Medium } from '../../utils/fonts'; import { Medium } from '../../utils/fonts';
import PropsTypes from 'prop-types'; import PropsTypes from 'prop-types';
import KeyCloakService from '../../services/KeyCloakService'; import KeyCloakService from '../../services/KeyCloakService';
import {
CHALLENGE_SECTIONS,
MENU_CHALEENGE_SECTIONS_WITH_LOGIN,
MENU_CHALLENGE_SECTIONS_NO_LOGIN,
} from '../../utils/globals';
import { Link } from 'react-router-dom';
const MenuOption = styled(Medium)` const MenuOption = styled(Medium)`
cursor: pointer; cursor: pointer;
@ -23,83 +17,54 @@ const MenuOption = styled(Medium)`
`; `;
const MobileChallengeMenu = (props) => { const MobileChallengeMenu = (props) => {
let options = MENU_CHALLENGE_SECTIONS_NO_LOGIN; let options = ['Leaderboard', 'Readme', 'How to'];
if (KeyCloakService.isLoggedIn()) if (KeyCloakService.isLoggedIn())
options = MENU_CHALEENGE_SECTIONS_WITH_LOGIN; options = ['Leaderboard', 'Readme', 'How to', 'My entries', 'Submit'];
const renderLoggedOptions = () => { const renderLoggedOptions = () => {
return ( if (options.length > 3) {
<FlexRow gap="36px"> return (
<MenuOption <FlexRow gap="36px">
as={Link} <MenuOption
active={CHALLENGE_SECTIONS.HOW_TO === props.section} as="button"
to={`/challenge/${props.challengeName}/${options[ active={3 === props.section}
CHALLENGE_SECTIONS.HOW_TO to={`${props.challengeName}/${options[3].toLowerCase()}`}
] >
.toLowerCase() {options[3]}
.replace(' ', '')}`} </MenuOption>
> <MenuOption
{options[CHALLENGE_SECTIONS.HOW_TO]} as="button"
</MenuOption> active={4 === props.section}
<MenuOption to={`${props.challengeName}/${options[4].toLowerCase()}`}
as={Link} >
active={CHALLENGE_SECTIONS.MY_ENTRIES === props.section} {options[4]}
to={`/challenge/${props.challengeName}/${options[ </MenuOption>
CHALLENGE_SECTIONS.MY_ENTRIES </FlexRow>
] );
.toLowerCase() }
.replace(' ', '')}`}
>
{options[CHALLENGE_SECTIONS.MY_ENTRIES]}
</MenuOption>
<MenuOption
as={Link}
active={CHALLENGE_SECTIONS.SUBMIT === props.section}
to={`/challenge/${props.challengeName}/${options[
CHALLENGE_SECTIONS.SUBMIT
]
.toLowerCase()
.replace(' ', '')}`}
>
{options[CHALLENGE_SECTIONS.SUBMIT]}
</MenuOption>
</FlexRow>
);
}; };
return ( return (
<> <>
<FlexRow gap="32px"> <FlexRow gap="32px">
<MenuOption <MenuOption
as={Link} as="button"
active={CHALLENGE_SECTIONS.LEADERBOARD === props.section} active={0 === props.section}
to={`/challenge/${props.challengeName}/${options[ to={`${props.challengeName}/${options[0].toLowerCase()}`}
CHALLENGE_SECTIONS.LEADERBOARD
]
.toLowerCase()
.replace(' ', '')}`}
> >
{options[CHALLENGE_SECTIONS.LEADERBOARD]} {options[0]}
</MenuOption> </MenuOption>
<MenuOption <MenuOption
as={Link} as="button"
active={CHALLENGE_SECTIONS.ALL_ENTRIES === props.section} active={1 === props.section}
to={`/challenge/${props.challengeName}/${options[ to={`${props.challengeName}/${options[1].toLowerCase()}`}
CHALLENGE_SECTIONS.ALL_ENTRIES
]
.toLowerCase()
.replace(' ', '')}`}
> >
{options[CHALLENGE_SECTIONS.ALL_ENTRIES]} {options[1]}
</MenuOption> </MenuOption>
<MenuOption <MenuOption
as={Link} as="button"
active={CHALLENGE_SECTIONS.README === props.section} active={2 === props.section}
to={`/challenge/${props.challengeName}/${options[ to={`${props.challengeName}/${options[2].toLowerCase()}`}
CHALLENGE_SECTIONS.README
]
.toLowerCase()
.replace(' ', '')}`}
> >
{options[CHALLENGE_SECTIONS.README]} {options[2]}
</MenuOption> </MenuOption>
</FlexRow> </FlexRow>
{renderLoggedOptions()} {renderLoggedOptions()}

View File

@ -132,27 +132,6 @@ const MyEntries = (props) => {
{!loading ? ( {!loading ? (
<> <>
<Search searchQueryHandler={searchQueryHandler} /> <Search searchQueryHandler={searchQueryHandler} />
<Table
challengeName={props.challengeName}
headerElements={getMyEntriesHeader()}
possibleMetrics={getPossibleMetrics()}
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 <Pager
pageNr={pageNr} pageNr={pageNr}
width="48px" width="48px"
@ -197,6 +176,7 @@ const MyEntries = (props) => {
format: EVALUATIONS_FORMAT, format: EVALUATIONS_FORMAT,
order: 2, order: 2,
align: 'left', align: 'left',
mobileRender,
}} }}
pageNr={pageNr} pageNr={pageNr}
elements={myEntries} elements={myEntries}

View File

@ -1,9 +1,10 @@
import React from 'react'; import React from 'react';
import { FlexColumn } from '../../utils/containers'; import {FlexColumn} from '../../utils/containers';
import { Body, H2 } from '../../utils/fonts'; import {Body, H2} from '../../utils/fonts';
import Media from 'react-media'; import Media from 'react-media';
import theme from '../../utils/theme'; import theme from '../../utils/theme';
import getChallengeFullDescription from '../../api/getChallengeFullDescription'; import getChallengeFullDescription from '../../api/getChallengeFullDescription';
// import {markdown} from 'markdown';
import styled from 'styled-components'; import styled from 'styled-components';
import InfoList from '../generic/InfoList'; import InfoList from '../generic/InfoList';
import Loading from '../generic/Loading'; import Loading from '../generic/Loading';
@ -28,7 +29,7 @@ const ReadmeStyle = styled(Body)`
line-height: 22px; line-height: 22px;
margin: 24px 0; margin: 24px 0;
@media (min-width: ${({ theme }) => theme.overMobile}) { @media (min-width: ${({theme}) => theme.overMobile}) {
font-size: 22px; font-size: 22px;
line-height: 26px; line-height: 26px;
} }
@ -40,7 +41,7 @@ const ReadmeStyle = styled(Body)`
font-size: 14px; font-size: 14px;
line-height: 20px; line-height: 20px;
@media (min-width: ${({ theme }) => theme.overMobile}) { @media (min-width: ${({theme}) => theme.overMobile}) {
font-weight: 400; font-weight: 400;
font-size: 16px; font-size: 16px;
line-height: 22px; line-height: 22px;
@ -52,10 +53,10 @@ const ReadmeStyle = styled(Body)`
font-weight: 400; font-weight: 400;
font-size: 14px; font-size: 14px;
line-height: 20px; line-height: 20px;
color: ${({ theme }) => theme.colors.dark}; color: ${({theme}) => theme.colors.dark};
text-decoration: none; text-decoration: none;
@media (min-width: ${({ theme }) => theme.overMobile}) { @media (min-width: ${({theme}) => theme.overMobile}) {
font-size: 16px; font-size: 16px;
line-height: 22px; line-height: 22px;
font-weight: 500; font-weight: 500;
@ -64,104 +65,127 @@ const ReadmeStyle = styled(Body)`
`; `;
const Readme = (props) => { const Readme = (props) => {
const [fullDescription, setFullDescription] = React.useState(''); const [fullDescription, setFullDescription] = React.useState('');
const [loading, setLoading] = React.useState(true); const [loading, setLoading] = React.useState(true);
React.useEffect(() => { React.useEffect(() => {
getChallengeFullDescription( getChallengeFullDescription(setFullDescription, setLoading, props.challengeName);
setFullDescription, }, [props.challengeName]);
setLoading,
props.challengeName
);
}, [props.challengeName]);
const parseMarkdownResponse = (response) => { const parseMarkdownResponse = (response) => {
let result = marked.parse(response); let result = marked.parse(response);
let regex = /<h3 /g; let regex = /<h3 /g;
result = result.replace(regex, '<h4 '); result = result.replace(regex, '<h4 ');
regex = /<\/h3>/g; regex = /<\/h3>/g;
result = result.replace(regex, '</h4>'); result = result.replace(regex, '</h4>');
regex = /<h2 /g; regex = /<h2 /g;
result = result.replace(regex, '<h3 '); result = result.replace(regex, '<h3 ');
regex = /<\/h2>/g; regex = /<\/h2>/g;
result = result.replace(regex, '</h3>'); result = result.replace(regex, '</h3>');
regex = /<h1 /g; regex = /<h1 /g;
result = result.replace(regex, '<h2 '); result = result.replace(regex, '<h2 ');
regex = /<\/h1>/g; regex = /<\/h1>/g;
result = result.replace(regex, '</h2>'); result = result.replace(regex, '</h2>');
return result; return result;
}; };
const mobileRender = () => {
return (
<FlexColumn as='section' padding='20px' gap='24px'>
<FlexColumn gap='12px' alignmentX='flex-start'>
<H2 as='h2'>
Info
</H2>
<InfoList iconsSize='24px' metric={props.metric} deadline={props.deadline}/>
</FlexColumn>
<FlexColumn alignmentX='flex-start' maxWidth='260px'>
{/* <H2 as='h2'>
Description
</H2> */}
<ReadmeStyle as={fullDescription ? 'article' : 'p'} dangerouslySetInnerHTML={{
__html: fullDescription
? parseMarkdownResponse(fullDescription) : props.description
}}/>
</FlexColumn>
{/* <FlexColumn gap='16px' alignmentX='flex-start' maxWidth='260px'>
<H2 as='h2'>
Baseline
</H2>
<FlexColumn gap='12px' alignmentX='flex-start'>
<Body as='p'>
In metus ex, venenatis quis risus eget, sodales venenatis nibh. Sed ullamcorper leo non nunc
euismod, id faucibus justo finibus. Nullam malesuada eros quam, eu lobortis leo feugiat non.
</Body>
<Body as='p'>
See notebook&nbsp;
<Medium as='a' href='#' display='inline-block' cursor='pointer'>
here.
</Medium>
</Body>
</FlexColumn>
</FlexColumn> */}
</FlexColumn>
);
};
const desktopRender = () => {
return (
<FlexColumn as='section' padding='20px' gap='64px'>
<FlexColumn gap='32px'>
<H2 as='h2'>
Info
</H2>
<InfoList iconsSize='32px' metric={props.metric} deadline={props.deadline}/>
</FlexColumn>
<FlexColumn alignmentX='flex-start' width='80%' maxWidth='1200px'>
{/* <H2 as='h2'>
Description
</H2> */}
<ReadmeStyle as={fullDescription ? 'section' : 'p'} dangerouslySetInnerHTML={{
__html: fullDescription ? parseMarkdownResponse(fullDescription) : props.description
}}/>
</FlexColumn>
{/* <FlexColumn gap='16px' alignmentX='flex-start' width='80%' maxWidth='1000px'>
<H2 as='h2'>
Baseline
</H2>
<FlexColumn gap='12px' alignmentX='flex-start'>
<Body as='p'>
In metus ex, venenatis quis risus eget, sodales venenatis nibh. Sed ullamcorper leo non nunc
euismod, id faucibus justo finibus. Nullam malesuada eros quam, eu lobortis leo feugiat non.
</Body>
<Body as='p'>
See notebook&nbsp;
<Medium as='a' href='#' display='inline-block' cursor='pointer'>
here.
</Medium>
</Body>
</FlexColumn>
</FlexColumn> */}
</FlexColumn>
);
};
const mobileRender = () => {
return ( return (
<FlexColumn as="section" padding="20px" gap="24px"> <>
<FlexColumn gap="12px" alignmentX="flex-start"> <Media query={theme.mobile}>
<H2 as="h2">Info</H2> {!loading ? mobileRender() : <Loading visible={loading}/>}
<InfoList </Media>
iconsSize="24px" <Media query={theme.desktop}>
metric={props.metric} {!loading ? desktopRender() : <Loading visible={loading}/>}
deadline={props.deadline} </Media>
/> </>
</FlexColumn>
<FlexColumn alignmentX="flex-start" maxWidth="260px">
<ReadmeStyle
as={fullDescription ? 'article' : 'p'}
dangerouslySetInnerHTML={{
__html: fullDescription
? parseMarkdownResponse(fullDescription)
: props.description,
}}
/>
</FlexColumn>
</FlexColumn>
); );
};
const desktopRender = () => {
return (
<FlexColumn as="section" padding="20px" gap="64px">
<FlexColumn gap="32px">
<H2 as="h2">Info</H2>
<InfoList
iconsSize="32px"
metric={props.metric}
deadline={props.deadline}
/>
</FlexColumn>
<FlexColumn alignmentX="flex-start" width="80%" maxWidth="1200px">
<ReadmeStyle
as={fullDescription ? 'section' : 'p'}
dangerouslySetInnerHTML={{
__html: fullDescription
? parseMarkdownResponse(fullDescription)
: props.description,
}}
/>
</FlexColumn>
</FlexColumn>
);
};
return (
<>
<Media query={theme.mobile}>
{!loading ? mobileRender() : <Loading visible={loading} />}
</Media>
<Media query={theme.desktop}>
{!loading ? desktopRender() : <Loading visible={loading} />}
</Media>
</>
);
}; };
MiniChallenge.propTypes = { MiniChallenge.propTypes = {
challengeName: PropsTypes.string, challengeName: PropsTypes.string,
description: PropsTypes.string, description: PropsTypes.string,
}; };
MiniChallenge.defaultProps = { MiniChallenge.defaultProps = {
challengeName: '', challengeName: '',
description: '', description: '',
}; };
export default Readme; export default Readme;

View File

@ -18,30 +18,6 @@ const ROOT_URL = window.location.origin;
const LOGIN_REQUIRED_PAGES = ['myentries', 'submit']; const LOGIN_REQUIRED_PAGES = ['myentries', 'submit'];
const MENU_CHALLENGE_SECTIONS_NO_LOGIN = [
'Leaderboard',
'All entries',
'Readme',
'How to',
];
const MENU_CHALEENGE_SECTIONS_WITH_LOGIN = [
'Leaderboard',
'All entries',
'Readme',
'How to',
'My entries',
'Submit',
];
const CHALLENGE_SECTIONS = {
LEADERBOARD: 0,
ALL_ENTRIES: 1,
README: 2,
HOW_TO: 3,
MY_ENTRIES: 4,
SUBMIT: 5,
};
const MINI_DESCRIPTION_RENDER = (description) => { const MINI_DESCRIPTION_RENDER = (description) => {
if (description) { if (description) {
if (description.length <= MINI_DESCRIPTION_LENGTH) return description; if (description.length <= MINI_DESCRIPTION_LENGTH) return description;
@ -110,9 +86,6 @@ export {
POLICY_PRIVACY_PAGE, POLICY_PRIVACY_PAGE,
ROOT_URL, ROOT_URL,
LOGIN_REQUIRED_PAGES, LOGIN_REQUIRED_PAGES,
CHALLENGE_SECTIONS,
MENU_CHALLENGE_SECTIONS_NO_LOGIN,
MENU_CHALEENGE_SECTIONS_WITH_LOGIN,
MINI_DESCRIPTION_RENDER, MINI_DESCRIPTION_RENDER,
RENDER_ICO, RENDER_ICO,
CALC_PAGES, CALC_PAGES,