Compare commits

..

5 Commits

53 changed files with 487 additions and 309 deletions

View File

@ -21,7 +21,7 @@ import Loading from './components/generic/Loading';
import { FlexColumn } from './utils/containers'; import { FlexColumn } from './utils/containers';
import PopupMessage from './components/generic/PopupMessage'; import PopupMessage from './components/generic/PopupMessage';
import PolicyPrivacy from './pages/PolicyPrivacy'; import PolicyPrivacy from './pages/PolicyPrivacy';
import Challenge from './components/specific_challenge/Challenge'; import Challenge from './pages/Challenge';
const App = () => { const App = () => {
const [loggedBarVisible, setLoggedBarVisible] = React.useState('100vw'); const [loggedBarVisible, setLoggedBarVisible] = React.useState('100vw');

View File

@ -1,12 +1,13 @@
import KeyCloakService from '../services/KeyCloakService'; import KeyCloakService from '../services/KeyCloakService';
import { API } from '../utils/globals'; import { API } from '../utils/globals';
import SUBMIT_ACTION from '../pages/Submit/model/SubmitActionEnum';
const challengeSubmission = ( const challengeSubmission = (
challengeName, challengeName,
repoUrl, repoUrl,
repoBranch, repoBranch,
description, description,
setLoading dispatch
) => { ) => {
const details = { const details = {
f1: description, f1: description,
@ -30,7 +31,7 @@ const challengeSubmission = (
}) })
.then((resp) => resp.json()) .then((resp) => resp.json())
.then((data) => { .then((data) => {
setLoading(true); dispatch({ type: SUBMIT_ACTION.TOGGLE_SUBMISSION_LOADING });
const processUrl = API.replace('/api', ''); const processUrl = API.replace('/api', '');
window.location.replace(`${processUrl}/open-view-progress/${data}#form`); window.location.replace(`${processUrl}/open-view-progress/${data}#form`);
// console.log(data); // console.log(data);

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'; import { API } from '../utils/globals';
const getChallenges = (dispatch) => { const getChallenges = (dispatch) => {

View File

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

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

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

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

@ -13,10 +13,13 @@ const PopUpStyle = styled(FlexColumn)`
.PopUpStyle__body { .PopUpStyle__body {
width: ${({ width }) => (width ? width : '60%')}; width: ${({ width }) => (width ? width : '60%')};
height: ${({ height }) => (height ? height : '50%')};
min-height: ${({ minHeight }) => (minHeight ? minHeight : '50%')}; min-height: ${({ minHeight }) => (minHeight ? minHeight : '50%')};
padding: ${({ padding }) => (padding ? padding : '48px')}; padding: ${({ padding }) => (padding ? padding : '48px')};
margin: ${({ margin }) => (margin ? margin : '0')};
border-radius: 12px; border-radius: 12px;
background-color: ${({ theme }) => theme.colors.white}; background-color: ${({ theme }) => theme.colors.white};
justify-content: flex-start;
} }
`; `;
@ -31,7 +34,9 @@ const PopUp = (props) => {
<PopUpStyle <PopUpStyle
padding={props.padding} padding={props.padding}
width={props.width} width={props.width}
height={props.height}
minHeight={props.minHeight} minHeight={props.minHeight}
margin={props.margin}
onClick={closePopUp} onClick={closePopUp}
> >
<FlexColumn <FlexColumn

View File

@ -1,20 +1,20 @@
import React from 'react'; import React from 'react';
import theme from '../../../utils/theme'; import theme from '../../utils/theme';
import Media from 'react-media'; import Media from 'react-media';
import { FlexColumn } from '../../../utils/containers'; import { FlexColumn } from '../../utils/containers';
import { H2 } from '../../../utils/fonts'; import { H2 } from '../../utils/fonts';
import { import {
CALC_PAGES, CALC_PAGES,
EVALUATIONS_FORMAT, EVALUATIONS_FORMAT,
RENDER_WHEN, RENDER_WHEN,
IS_MOBILE, IS_MOBILE,
} from '../../../utils/globals'; } from '../../utils/globals';
import Loading from '../../generic/Loading'; import Loading from '../../components/generic/Loading';
import Pager from '../../generic/Pager'; import Pager from '../../components/generic/Pager';
import Table from '../Table'; import Table from '../../components/generic/Table';
import Search from '../../generic/Search'; import Search from '../../components/generic/Search';
import allEntriesSearchQueryHandler from './allEntriesSearchQueryHandler'; import allEntriesSearchQueryHandler from './allEntriesSearchQueryHandler';
import getAllEntries from '../../../api/getAllEntries'; import getAllEntries from '../../api/getAllEntries';
const AllEntries = (props) => { const AllEntries = (props) => {
const [entriesFromApi, setEntriesFromApi] = React.useState([]); const [entriesFromApi, setEntriesFromApi] = React.useState([]);

View File

@ -3,13 +3,13 @@ import Media from 'react-media';
import theme from '../../utils/theme'; import theme from '../../utils/theme';
import getChallenges from '../../api/getChallenges'; import getChallenges from '../../api/getChallenges';
import { CHALLENGES_STATUS_FILTER } from '../../utils/globals'; 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 statusFilterHandle from './functions/statusFilterHandle';
import ChallengesMobile from './components/ChallengesMobile'; import ChallengesMobile from './components/ChallengesMobile';
import ChallengesDesktop from './components/ChallengesDesktop'; import ChallengesDesktop from './components/ChallengesDesktop';
import challengeSearchQueryHandler from './functions/challengeSearchQueryHandler'; import challengeSearchQueryHandler from './functions/challengeSearchQueryHandler';
import ChallengesReducer from './model/ChallengesReducer'; import ChallengesReducer from './model/ChallengesReducer';
import CHALLENGES_ACTION from './model/ChallengesActionEnum'; import CHALLENGES_ACTION from './model/ChallengesActions';
const Challenges = () => { const Challenges = () => {
const [state, dispatch] = React.useReducer(ChallengesReducer, { const [state, dispatch] = React.useReducer(ChallengesReducer, {

View File

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

View File

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

View File

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

View File

@ -0,0 +1,121 @@
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>
);
}
};
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>
);
};
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) => { const challengeSearchQueryHandler = (event, challengesFromAPI, dispatch) => {
let searchQuery = event.target.value; let searchQuery = event.target.value;

View File

@ -1,5 +1,5 @@
import { ELEMENTS_PER_PAGE } from '../../../utils/globals'; 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 { Grid } from '../../../utils/containers';
import styled from 'styled-components'; import styled from 'styled-components';

View File

@ -1,5 +1,5 @@
import { CHALLENGES_STATUS_FILTER } from '../../../utils/globals'; import { CHALLENGES_STATUS_FILTER } from '../../../utils/globals';
import CHALLENGES_ACTION from '../model/ChallengesActionEnum'; import CHALLENGES_ACTION from '../model/ChallengesActions';
const dateIsOlder = (newerDate, olderDate) => { const dateIsOlder = (newerDate, olderDate) => {
if (newerDate.year > olderDate.year) return true; 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) => { const ChallengesReducer = (state, action) => {
switch (action.type) { switch (action.type) {

View File

@ -3,20 +3,20 @@ import { Container, FlexColumn, FlexRow, Svg } from '../../utils/containers';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import { H1, Medium } from '../../utils/fonts'; import { H1, Medium } from '../../utils/fonts';
import theme from '../../utils/theme'; import theme from '../../utils/theme';
import MobileChallengeMenu from './MobileChallengeMenu'; import MobileChallengeMenu from './components/MobileChallengeMenu';
import Leaderboard from './Leaderboard/Leaderboard'; import Leaderboard from '../Leaderboard/Leaderboard';
import Readme from './Readme'; import Readme from '../Readme';
import HowTo from './HowTo/HowTo'; import HowTo from '../HowTo/HowTo';
import MyEntries from './MyEntries/MyEntries'; 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 './components/DesktopChallengeMenu';
import { CHALLENGE_SECTIONS, RENDER_ICO } from '../../utils/globals'; import { CHALLENGE_SECTIONS, 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 '../../components/generic/Loading';
import getUser from '../../api/getUser'; import getUser from '../../api/getUser';
import AllEntries from './AllEntries/AllEntries'; import AllEntries from '../AllEntries/AllEntries';
const Challenge = (props) => { const Challenge = (props) => {
const challengeName = useParams().challengeId; const challengeName = useParams().challengeId;

View File

@ -1,15 +1,15 @@
import React from 'react'; import React from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import { FlexColumn } from '../../utils/containers'; import { FlexColumn } from '../../../utils/containers';
import { H3 } from '../../utils/fonts'; 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 { import {
MENU_CHALLENGE_SECTIONS_WITH_LOGIN, MENU_CHALLENGE_SECTIONS_WITH_LOGIN,
MENU_CHALLENGE_SECTIONS_NO_LOGIN, MENU_CHALLENGE_SECTIONS_NO_LOGIN,
IS_MOBILE, IS_MOBILE,
} from '../../utils/globals'; } from '../../../utils/globals';
const DesktopChallengeMenuStyle = styled(FlexColumn)` const DesktopChallengeMenuStyle = styled(FlexColumn)`
justify-content: flex-start; justify-content: flex-start;

View File

@ -1,14 +1,14 @@
import React from 'react'; import React from 'react';
import { FlexRow } from '../../utils/containers'; import { FlexRow } from '../../../utils/containers';
import styled from 'styled-components'; 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 { import {
CHALLENGE_SECTIONS, CHALLENGE_SECTIONS,
MENU_CHALLENGE_SECTIONS_WITH_LOGIN, MENU_CHALLENGE_SECTIONS_WITH_LOGIN,
MENU_CHALLENGE_SECTIONS_NO_LOGIN, MENU_CHALLENGE_SECTIONS_NO_LOGIN,
} from '../../utils/globals'; } from '../../../utils/globals';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
const MenuOption = styled(Medium)` const MenuOption = styled(Medium)`

View File

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

View File

@ -1,9 +1,9 @@
import React from 'react'; import React from 'react';
import getFullUser from '../../../api/getFullUserInfo'; import getFullUser from '../../api/getFullUserInfo';
import KeyCloakService from '../../../services/KeyCloakService'; import KeyCloakService from '../../services/KeyCloakService';
import { FlexColumn } from '../../../utils/containers'; import { FlexColumn } from '../../utils/containers';
import { IS_MOBILE } from '../../../utils/globals'; import { IS_MOBILE } from '../../utils/globals';
import HowToContent from './sections/HowToContent'; import HowToContent from './components/HowToContent';
const HowTo = (props) => { const HowTo = (props) => {
const [userFullInfo, setUserFullInfo] = React.useState(null); const [userFullInfo, setUserFullInfo] = React.useState(null);

View File

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

View File

@ -1,20 +1,20 @@
import React from 'react'; import React from 'react';
import Media from 'react-media'; import Media from 'react-media';
import theme from '../../../utils/theme'; import theme from '../../utils/theme';
import { FlexColumn } from '../../../utils/containers'; import { FlexColumn } from '../../utils/containers';
import { H2 } from '../../../utils/fonts'; import { H2 } from '../../utils/fonts';
import Table from '../Table'; import Table from '../../components/generic/Table';
import PropsTypes from 'prop-types'; import PropsTypes from 'prop-types';
import getChallengeLeaderboard from '../../../api/getChallengeLeaderboard'; import getChallengeLeaderboard from '../../api/getChallengeLeaderboard';
import leaderboardSearchQueryHandler from './leaderboardSearchQueryHandler'; import leaderboardSearchQueryHandler from './leaderboardSearchQueryHandler';
import { import {
CALC_PAGES, CALC_PAGES,
EVALUATIONS_FORMAT, EVALUATIONS_FORMAT,
RENDER_WHEN, RENDER_WHEN,
} from '../../../utils/globals'; } from '../../utils/globals';
import Search from '../../generic/Search'; import Search from '../../components/generic/Search';
import Pager from '../../generic/Pager'; import Pager from '../../components/generic/Pager';
import Loading from '../../generic/Loading'; import Loading from '../../components/generic/Loading';
const Leaderboard = (props) => { const Leaderboard = (props) => {
const [entriesFromApi, setEntriesFromApi] = React.useState([]); const [entriesFromApi, setEntriesFromApi] = React.useState([]);

View File

@ -1,20 +1,20 @@
import React from 'react'; import React from 'react';
import { FlexColumn } from '../../../utils/containers'; import { FlexColumn } from '../../utils/containers';
import { H2 } from '../../../utils/fonts'; import { H2 } from '../../utils/fonts';
import getMyEntries from '../../../api/getMyEntries'; import getMyEntries from '../../api/getMyEntries';
import Pager from '../../generic/Pager'; import Pager from '../../components/generic/Pager';
import { import {
CALC_PAGES, CALC_PAGES,
EVALUATIONS_FORMAT, EVALUATIONS_FORMAT,
IS_MOBILE, IS_MOBILE,
RENDER_WHEN, RENDER_WHEN,
} from '../../../utils/globals'; } from '../../utils/globals';
import Media from 'react-media'; import Media from 'react-media';
import theme from '../../../utils/theme'; import theme from '../../utils/theme';
import Loading from '../../generic/Loading'; import Loading from '../../components/generic/Loading';
import Table from '../Table'; import Table from '../../components/generic/Table';
import myEntriesSearchQueryHandler from './myEntriesSearchQueryHandler'; import myEntriesSearchQueryHandler from './myEntriesSearchQueryHandler';
import Search from '../../generic/Search'; import Search from '../../components/generic/Search';
const MyEntries = (props) => { const MyEntries = (props) => {
const [myEntriesFromAPI, setMyEntriesFromAPI] = React.useState({}); const [myEntriesFromAPI, setMyEntriesFromAPI] = React.useState({});

View File

@ -1,14 +1,12 @@
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 styled from 'styled-components'; import styled from 'styled-components';
import InfoList from '../generic/InfoList'; import InfoList from '../components/generic/InfoList';
import Loading from '../generic/Loading'; import Loading from '../components/generic/Loading';
import PropsTypes from 'prop-types';
import MiniChallenge from '../challenges_list/MiniChallenge';
import { marked } from 'marked'; import { marked } from 'marked';
const ReadmeStyle = styled(Body)` const ReadmeStyle = styled(Body)`
@ -154,14 +152,4 @@ const Readme = (props) => {
); );
}; };
MiniChallenge.propTypes = {
challengeName: PropsTypes.string,
description: PropsTypes.string,
};
MiniChallenge.defaultProps = {
challengeName: '',
description: '',
};
export default Readme; export default Readme;

View File

@ -2,40 +2,58 @@ import React from 'react';
import { createPortal } from 'react-dom'; import { createPortal } from 'react-dom';
import { FlexColumn } from '../../utils/containers'; import { FlexColumn } from '../../utils/containers';
import { H2, Menu } from '../../utils/fonts'; import { H2, Menu } from '../../utils/fonts';
import SubmitInput from '../generic/SubmitInput'; import SubmitInput from '../../components/generic/SubmitInput';
import Button from '../generic/Button'; import Button from '../../components/generic/Button';
import theme from '../../utils/theme'; import theme from '../../utils/theme';
import challengeSubmission from '../../api/challengeSubmissionPost'; import challengeSubmission from '../../api/challengeSubmissionPost';
import Loading from '../generic/Loading'; import Loading from '../../components/generic/Loading';
import getTags from '../../api/getTags'; import getTags from '../../api/getTags';
import DropdownWithPopup from '../generic/DropdownWithPopup'; import TagsChoose from './components/TagsChoose';
import SubmitReducer from './model/SubmitReducer';
import SUBMIT_ACTION from './model/SubmitActionEnum';
const Submit = (props) => { const Submit = (props) => {
const [description, setDescription] = React.useState(''); const [state, dispatch] = React.useReducer(SubmitReducer, {
const [repoUrl, setRepoUrl] = React.useState(''); description: '',
const [repoBranch, setRepoBranch] = React.useState(''); repoUrl: '',
const [loading, setLoading] = React.useState(false); repoBranch: '',
const [tags, setTags] = React.useState([]); submissionLoading: false,
// eslint-disable-next-line no-unused-vars tags: [],
const [submissionTags, setSubmissionTags] = React.useState([]); submissionTags: [],
});
React.useMemo(() => { React.useMemo(() => {
getTags(setTags); getTags(dispatch);
}, []); }, []);
const challengeSubmissionSubmit = () => { const challengeSubmissionSubmit = () => {
setLoading(true); dispatch({ type: SUBMIT_ACTION.TOGGLE_SUBMISSION_LOADING });
challengeSubmission( challengeSubmission(
props.challengeName, props.challengeName,
repoUrl, state.repoUrl,
repoBranch, state.repoBranch,
description, state.description,
setLoading dispatch
); );
}; };
if (!loading) { const setDescription = (value) => {
console.log(tags); dispatch({ type: SUBMIT_ACTION.SET_DESCRIPTION, payload: value });
};
const setRepoUrl = (value) => {
dispatch({ type: SUBMIT_ACTION.SET_REPO_URL, payload: value });
};
const setRepoBranch = (value) => {
dispatch({ type: SUBMIT_ACTION.SET_REPO_BRANCH, payload: value });
};
const addSubmissionTag = React.useCallback((value) => {
dispatch({ type: SUBMIT_ACTION.ADD_SUBMISSION_TAG, payload: value });
}, []);
if (!state.submissionLoading) {
return ( return (
<FlexColumn <FlexColumn
margin="40px 0 0 0" margin="40px 0 0 0"
@ -56,9 +74,11 @@ const Submit = (props) => {
/> />
<SubmitInput label="Submission repo URL" handler={setRepoUrl} /> <SubmitInput label="Submission repo URL" handler={setRepoUrl} />
<SubmitInput label="Submission repo branch" handler={setRepoBranch} /> <SubmitInput label="Submission repo branch" handler={setRepoBranch} />
<DropdownWithPopup <TagsChoose
label="Submission tags" label="Submission tags"
handler={setSubmissionTags} addSubmissionTag={addSubmissionTag}
tags={state.tags}
submissionTags={state.submissionTags}
/> />
</FlexColumn> </FlexColumn>
<Button width="122px" height="44px" handler={challengeSubmissionSubmit}> <Button width="122px" height="44px" handler={challengeSubmissionSubmit}>

View File

@ -0,0 +1,43 @@
import React from 'react';
import { FlexRow, Grid } from '../../../../utils/containers';
import { Medium } from '../../../../utils/fonts';
import ImageButton from '../../../../components/generic/ImageButton';
import pencilIco from '../../../../assets/pencil_ico.svg';
import TagsChoosePopUp from '../TagsChoosePopup/TagsChoosePopUp';
import { createPortal } from 'react-dom';
import TagsChooseStyle from './TagsChooseStyle';
const TagsChoose = (props) => {
const [tagsPopUp, setTagsPopUp] = React.useState(false);
return (
<TagsChooseStyle
onClick={() => {
if (!tagsPopUp) setTagsPopUp(true);
}}
>
<Medium as="label" htmlFor={props.label}>
{props.label}
</Medium>
<Grid
className="TagsChooseStyle__grid"
onChange={(e) => props.handler(e.target.value)}
>
<FlexRow className="TagsChooseStyle__tags-container">tags</FlexRow>
<ImageButton src={pencilIco} width="20px" height="20px" />
</Grid>
{tagsPopUp &&
createPortal(
<TagsChoosePopUp
tags={props.tags}
submissionTags={props.submissionTags}
addSubmissionTag={props.addSubmissionTag}
setTagsPopUp={setTagsPopUp}
/>,
document.body
)}
</TagsChooseStyle>
);
};
export default TagsChoose;

View File

@ -0,0 +1,30 @@
import styled from 'styled-components';
import { FlexColumn } from '../../../../utils/containers';
const TagsChooseStyle = styled(FlexColumn)`
cursor: pointer;
gap: 8px;
width: 100%;
align-items: flex-start;
* {
cursor: pointer;
}
.TagsChooseStyle__grid {
border-radius: 4px;
width: 100%;
height: 100px;
border: 1px solid ${({ theme }) => theme.colors.dark};
box-shadow: 1px 2px 4px rgba(52, 52, 52, 0.25);
padding: 12px;
grid-template-columns: 1fr auto;
}
.TagsChooseStyle__tags-container {
height: 100%;
justify-content: flex-start;
align-items: flex-start;
}
`;
export default TagsChooseStyle;

View File

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

View File

@ -0,0 +1,64 @@
import React from 'react';
import PopUp from '../../../../components/generic/PopUp';
import { FlexColumn, FlexRow } from '../../../../utils/containers';
import Search from '../../../../components/generic/Search';
import theme from '../../../../utils/theme';
import Button from '../../../../components/generic/Button';
import TagsChoosePopUpStyle from './TagsChoosePopUpStyle';
import tagColorHandle from './functions/tagColorHandle';
const TagsChoosePopUp = (props) => {
return (
<PopUp
width="50%"
height="80vh"
padding="36px 32px 0"
closeHandler={() => props.setTagsPopUp(false)}
>
<TagsChoosePopUpStyle>
<Search />
<FlexColumn as="list" className="TagsChoosePopUpStyle__tags-list">
{props.tags.map((tag, index) => {
return (
<FlexRow
key={`tag-${index}`}
onClick={() => props.addSubmissionTag(tag.name)}
className="TagsChoosePopUpStyle__tag-item"
backgroundColor={tagColorHandle(
theme,
index,
tag,
props.submissionTags
)}
>
{tag.name}
</FlexRow>
);
})}
</FlexColumn>
<FlexRow width="100%" gap="20px" alignmentX="flex-start">
<Button height="32px" width="76px">
Done
</Button>
<Button
height="32px"
width="76px"
backgroundColor={theme.colors.dark08}
>
Clear
</Button>
<Button
height="32px"
width="76px"
backgroundColor={theme.colors.dark}
margin="0 0 0 auto"
>
Cancel
</Button>
</FlexRow>
</TagsChoosePopUpStyle>
</PopUp>
);
};
export default TagsChoosePopUp;

View File

@ -0,0 +1,31 @@
import styled from 'styled-components';
import { FlexColumn } from '../../../../utils/containers';
const TagsChoosePopUpStyle = styled(FlexColumn)`
width: 100%;
justify-content: flex-start;
height: 100%;
gap: 24px;
.TagsChoosePopUpStyle__tags-list {
height: 80%;
width: 100%;
overflow-y: scroll;
justify-content: flex-start;
}
.TagsChoosePopUpStyle__tag-item {
height: 42px;
width: 100%;
justify-content: flex-start;
padding: 12px;
cursor: pointer;
transition: background-color 0.3s ease-in-out;
&:hover {
background-color: ${({ theme }) => theme.colors.green03};
}
}
`;
export default TagsChoosePopUpStyle;

View File

@ -0,0 +1,7 @@
const tagColorHandle = (theme, index, tag, tags) => {
if (tags.includes(tag.name)) return theme.colors.green;
if (index % 2 === 0) return theme.colors.dark01;
return theme.colors.white;
};
export default tagColorHandle;

View File

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

View File

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

View File

@ -0,0 +1,12 @@
const SUBMIT_ACTION = {
SET_DESCRIPTION: 'set_description',
SET_REPO_URL: 'set_repo_url',
SET_REPO_BRANCH: 'set_repo_branch',
TOGGLE_SUBMISSION_LOADING: 'toggle_submission_loading',
LOAD_TAGS: 'load_tags',
ADD_SUBMISSION_TAGS: 'add_submission_tag',
REMOVE_SUBMISSION_TAGS: 'remove_submission_tag',
CLEAR_SUBMISSION_TAGS: 'clear_submission_tags',
};
export default SUBMIT_ACTION;

View File

@ -0,0 +1,30 @@
import SUBMIT_ACTION from './SubmitActionEnum';
const SubmitReducer = (state, action) => {
console.log('SubmitReducer');
let initTags = state.tags;
let newSubmissionTags = state.submissionTags;
switch (action.type) {
case SUBMIT_ACTION.SET_DESCRIPTION:
return { ...state, description: action.payload };
case SUBMIT_ACTION.SET_REPO_BRANCH:
return { ...state, repoBranch: action.payload };
case SUBMIT_ACTION.SET_REPO_URL:
return { ...state, repoUrl: action.payload };
case SUBMIT_ACTION.TOGGLE_SUBMISSION_LOADING:
return { ...state, submissionLoading: !state.submissionLoading };
case SUBMIT_ACTION.LOAD_TAGS:
if (state.tags.length === 0) initTags = action.payload;
return { ...state, tags: initTags };
case SUBMIT_ACTION.ADD_SUBMISSION_TAG:
if (!newSubmissionTags.includes(action.payload))
newSubmissionTags.push(action.payload);
return { ...state, submissionTags: newSubmissionTags };
case SUBMIT_ACTION.CLEAR_SUBMISSION_TAGS:
return { ...state, submissionTags: [] };
default:
throw new Error('Undefined action in SubmitReducer!');
}
};
export default SubmitReducer;

View File

@ -1,15 +1,16 @@
const colors = { const colors = {
white: '#FCFCFC', white: '#FCFCFC',
green: '#1B998B', green: '#1B998B',
green03: 'rgba(27, 153, 139, 0.3)', blue: '#4B8FF0',
green05: 'rgba(27, 153, 139, 0.5)', green03: 'rgba(27, 153, 139, 0.3)',
dark: '#343434', green05: 'rgba(27, 153, 139, 0.5)',
dark01: 'rgba(52, 52, 52, 0.1)', dark: '#343434',
dark03: 'rgba(52, 52, 52, 0.3)', dark01: 'rgba(52, 52, 52, 0.1)',
dark04: 'rgba(52, 52, 52, 0.4)', dark03: 'rgba(52, 52, 52, 0.3)',
dark05: 'rgba(52, 52, 52, 0.5)', dark04: 'rgba(52, 52, 52, 0.4)',
dark07: 'rgba(52, 52, 52, 0.7)', dark05: 'rgba(52, 52, 52, 0.5)',
dark08: 'rgba(52, 52, 52, 0.8)', dark07: 'rgba(52, 52, 52, 0.7)',
dark08: 'rgba(52, 52, 52, 0.8)',
}; };
export default colors; export default colors;

View File

@ -37,6 +37,7 @@ const Container = styled.div`
list-style: ${({ listStyle }) => (listStyle ? listStyle : 'none')}; list-style: ${({ listStyle }) => (listStyle ? listStyle : 'none')};
overflow-wrap: ${({ overflowWrap }) => overflow-wrap: ${({ overflowWrap }) =>
overflowWrap ? overflowWrap : 'normal'}; overflowWrap ? overflowWrap : 'normal'};
overflow-y: ${({ overflowY }) => (overflowY ? overflowY : 'none')};
`; `;
const FlexRow = styled(Container)` const FlexRow = styled(Container)`