diff --git a/src/App.js b/src/App.js index b0eb553..546dc83 100644 --- a/src/App.js +++ b/src/App.js @@ -21,7 +21,7 @@ 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'; +import Challenge from './pages/Challenge'; const App = () => { const [loggedBarVisible, setLoggedBarVisible] = React.useState('100vw'); diff --git a/src/api/challengeSubmissionPost.js b/src/api/challengeSubmissionPost.js index b0d5d10..a9df22f 100644 --- a/src/api/challengeSubmissionPost.js +++ b/src/api/challengeSubmissionPost.js @@ -1,15 +1,18 @@ 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 details = { f1: description, + f2: submissionTags, f3: repoUrl, f4: repoBranch, }; @@ -30,7 +33,7 @@ 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`); // console.log(data); diff --git a/src/api/getChallenges.js b/src/api/getChallenges.js index a5c790a..26d8a3f 100644 --- a/src/api/getChallenges.js +++ b/src/api/getChallenges.js @@ -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) => { diff --git a/src/api/getTags.js b/src/api/getTags.js index 4ed6e76..fc1f959 100644 --- a/src/api/getTags.js +++ b/src/api/getTags.js @@ -1,10 +1,11 @@ +import SUBMIT_ACTION from '../pages/Submit/model/SubmitActionEnum'; import { API } from '../utils/globals'; -const getTags = (setTags) => { +const getTags = (dispatch) => { fetch(`${API}/list-tags`) .then((response) => response.json()) .then((data) => { - setTags(data); + dispatch({ type: SUBMIT_ACTION.LOAD_TAGS, payload: data }); }); }; diff --git a/src/assets/remove_ico.svg b/src/assets/remove_ico.svg new file mode 100644 index 0000000..8246ed8 --- /dev/null +++ b/src/assets/remove_ico.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/challenges_list/MiniChallenge.js b/src/components/challenges_list/MiniChallenge.js deleted file mode 100644 index d8505a0..0000000 --- a/src/components/challenges_list/MiniChallenge.js +++ /dev/null @@ -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 ( - - {props.deadline.slice(0, 10)} - - ); - } - }; - - return ( - - - -

- {props.title} -

- {props.type ? : 'xxx'} -
- - - {props.description ? MINI_DESCRIPTION_RENDER(props.description) : 'xxx'} - - - - {props.metric ? props.metric : 'xxx'} - - - {props.bestScore ? props.bestScore : 'xxx'} - - {deadlineRender()} - - {props.baseline ? props.baseline : 'xxx'} - - {props.prize ? - {props.prize} - : ''} - -
-
- ); -}; - -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; \ No newline at end of file diff --git a/src/components/generic/Button.js b/src/components/generic/Button.js index 706c4a8..30ea679 100644 --- a/src/components/generic/Button.js +++ b/src/components/generic/Button.js @@ -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} diff --git a/src/components/specific_challenge/ColumnFilterIcon.js b/src/components/generic/ColumnFilterIcon.js similarity index 100% rename from src/components/specific_challenge/ColumnFilterIcon.js rename to src/components/generic/ColumnFilterIcon.js diff --git a/src/components/generic/DropdownWithPopup.js b/src/components/generic/DropdownWithPopup.js deleted file mode 100644 index cca1014..0000000 --- a/src/components/generic/DropdownWithPopup.js +++ /dev/null @@ -1,36 +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'; - -const DropdownWithPopup = (props) => { - return ( - - - {props.label} - - props.handler(e.target.value)} - padding="12px" - gridTemplateColumns="1fr auto" - > - - 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 - - - - - ); -}; - -export default DropdownWithPopup; diff --git a/src/components/generic/ImageButton.js b/src/components/generic/ImageButton.js index 91ca906..fd02d9e 100644 --- a/src/components/generic/ImageButton.js +++ b/src/components/generic/ImageButton.js @@ -5,6 +5,9 @@ const ImageButtonStyle = styled(FlexRow)` cursor: pointer; transition: transform ease-in-out 0.3s; transform: rotate(${({ rotate }) => (rotate ? rotate : '0')}); + * { + cursor: pointer; + } &:hover { transform: rotate(${({ rotate }) => (rotate ? rotate : '0')}), scale(1.2); } diff --git a/src/components/generic/PopUp.js b/src/components/generic/PopUp.js new file mode 100644 index 0000000..fee1adc --- /dev/null +++ b/src/components/generic/PopUp.js @@ -0,0 +1,53 @@ +import React from 'react'; +import { FlexColumn } from '../../utils/containers'; +import styled from 'styled-components'; + +const PopUpStyle = styled(FlexColumn)` + position: fixed; + top: 0; + left: 0; + z-index: 100; + width: 100%; + height: 100vh; + background-color: ${({ theme }) => theme.colors.dark01}; + + .PopUpStyle__body { + width: ${({ width }) => (width ? width : '60%')}; + height: ${({ height }) => (height ? height : '50%')}; + min-height: ${({ minHeight }) => (minHeight ? minHeight : '50%')}; + padding: ${({ padding }) => (padding ? padding : '48px')}; + margin: ${({ margin }) => (margin ? margin : '0')}; + border-radius: 12px; + background-color: ${({ theme }) => theme.colors.white}; + justify-content: flex-start; + } +`; + +const PopUp = (props) => { + const [onHover, setOnHover] = React.useState(false); + + const closePopUp = () => { + if (!onHover) props.closeHandler(); + }; + + return ( + + setOnHover(true)} + onMouseLeave={() => setOnHover(false)} + className="PopUpStyle__body" + > + {props.children} + + + ); +}; + +export default PopUp; diff --git a/src/components/specific_challenge/Table.js b/src/components/generic/Table.js similarity index 100% rename from src/components/specific_challenge/Table.js rename to src/components/generic/Table.js diff --git a/src/components/specific_challenge/Submit.js b/src/components/specific_challenge/Submit.js deleted file mode 100644 index 04feace..0000000 --- a/src/components/specific_challenge/Submit.js +++ /dev/null @@ -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 ( - -

- Submit a solution to the challenge -

- - - - - - - -
- ); - } else { - return createPortal( - -

Submission processing...

- -
, - document.body - ); - } -}; - -export default Submit; diff --git a/src/index.js b/src/index.js index fad2077..a4f9fd8 100644 --- a/src/index.js +++ b/src/index.js @@ -8,13 +8,9 @@ import HttpService from './services/HttpService'; const root = ReactDOM.createRoot(document.getElementById('root')); -const renderApp = () => root.render( - - - -); +const renderApp = () => root.render(); KeyCloakService.initKeycloak(renderApp); HttpService.configure(); -renderApp(); \ No newline at end of file +renderApp(); diff --git a/src/components/specific_challenge/AllEntries/AllEntries.js b/src/pages/AllEntries/AllEntries.js similarity index 94% rename from src/components/specific_challenge/AllEntries/AllEntries.js rename to src/pages/AllEntries/AllEntries.js index cb5d7a7..eddcdd1 100644 --- a/src/components/specific_challenge/AllEntries/AllEntries.js +++ b/src/pages/AllEntries/AllEntries.js @@ -1,20 +1,20 @@ import React from 'react'; -import theme from '../../../utils/theme'; +import theme from '../../utils/theme'; import Media from 'react-media'; -import { FlexColumn } from '../../../utils/containers'; -import { H2 } from '../../../utils/fonts'; +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'; +} from '../../utils/globals'; +import Loading from '../../components/generic/Loading'; +import Pager from '../../components/generic/Pager'; +import Table from '../../components/generic/Table'; +import Search from '../../components/generic/Search'; import allEntriesSearchQueryHandler from './allEntriesSearchQueryHandler'; -import getAllEntries from '../../../api/getAllEntries'; +import getAllEntries from '../../api/getAllEntries'; const AllEntries = (props) => { const [entriesFromApi, setEntriesFromApi] = React.useState([]); diff --git a/src/components/specific_challenge/AllEntries/allEntriesSearchQueryHandler.js b/src/pages/AllEntries/allEntriesSearchQueryHandler.js similarity index 100% rename from src/components/specific_challenge/AllEntries/allEntriesSearchQueryHandler.js rename to src/pages/AllEntries/allEntriesSearchQueryHandler.js diff --git a/src/components/specific_challenge/AllEntries/index.js b/src/pages/AllEntries/index.js similarity index 100% rename from src/components/specific_challenge/AllEntries/index.js rename to src/pages/AllEntries/index.js diff --git a/src/pages/Challanges/Challenges.js b/src/pages/Challanges/Challenges.js index c4f7186..ae98353 100644 --- a/src/pages/Challanges/Challenges.js +++ b/src/pages/Challanges/Challenges.js @@ -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, { diff --git a/src/pages/Challanges/components/ChallengesMobile.js b/src/pages/Challanges/components/ChallengesMobile.js index 63cceaa..939da5f 100644 --- a/src/pages/Challanges/components/ChallengesMobile.js +++ b/src/pages/Challanges/components/ChallengesMobile.js @@ -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 ( diff --git a/src/components/challenges_list/FilterBy.js b/src/pages/Challanges/components/FilterBy.js similarity index 87% rename from src/components/challenges_list/FilterBy.js rename to src/pages/Challanges/components/FilterBy.js index f198cc6..93ea726 100644 --- a/src/components/challenges_list/FilterBy.js +++ b/src/pages/Challanges/components/FilterBy.js @@ -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 = () => { diff --git a/src/components/challenges_list/FiltersMenu/FiltersMenu.js b/src/pages/Challanges/components/FiltersMenu/FiltersMenu.js similarity index 93% rename from src/components/challenges_list/FiltersMenu/FiltersMenu.js rename to src/pages/Challanges/components/FiltersMenu/FiltersMenu.js index acef5ef..7f76e7c 100644 --- a/src/components/challenges_list/FiltersMenu/FiltersMenu.js +++ b/src/pages/Challanges/components/FiltersMenu/FiltersMenu.js @@ -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; diff --git a/src/components/challenges_list/FiltersMenu/filterOptions.js b/src/pages/Challanges/components/FiltersMenu/filterOptions.js similarity index 100% rename from src/components/challenges_list/FiltersMenu/filterOptions.js rename to src/pages/Challanges/components/FiltersMenu/filterOptions.js diff --git a/src/components/challenges_list/FiltersMenu/index.js b/src/pages/Challanges/components/FiltersMenu/index.js similarity index 100% rename from src/components/challenges_list/FiltersMenu/index.js rename to src/pages/Challanges/components/FiltersMenu/index.js diff --git a/src/pages/Challanges/components/MiniChallenge.js b/src/pages/Challanges/components/MiniChallenge.js new file mode 100644 index 0000000..33ad4ec --- /dev/null +++ b/src/pages/Challanges/components/MiniChallenge.js @@ -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 ( + + {props.deadline.slice(0, 10)} + + ); + } + }; + + return ( + + + +

+ {props.title} +

+ {props.type ? : 'xxx'} +
+ + + {props.description + ? MINI_DESCRIPTION_RENDER(props.description) + : 'xxx'} + + + + {props.metric ? props.metric : 'xxx'} + + + {props.bestScore ? props.bestScore : 'xxx'} + + {deadlineRender()} + + {props.baseline ? props.baseline : 'xxx'} + + {props.prize ? ( + + {props.prize} + + ) : ( + '' + )} + +
+
+ ); +}; + +export default MiniChallenge; diff --git a/src/pages/Challanges/functions/challengeSearchQueryHandler.js b/src/pages/Challanges/functions/challengeSearchQueryHandler.js index 90f279e..7cc4a90 100644 --- a/src/pages/Challanges/functions/challengeSearchQueryHandler.js +++ b/src/pages/Challanges/functions/challengeSearchQueryHandler.js @@ -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; diff --git a/src/pages/Challanges/functions/renderChallenges.js b/src/pages/Challanges/functions/renderChallenges.js index bf2f530..5f83f06 100644 --- a/src/pages/Challanges/functions/renderChallenges.js +++ b/src/pages/Challanges/functions/renderChallenges.js @@ -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'; diff --git a/src/pages/Challanges/functions/statusFilterHandle.js b/src/pages/Challanges/functions/statusFilterHandle.js index 4e3bfef..48af319 100644 --- a/src/pages/Challanges/functions/statusFilterHandle.js +++ b/src/pages/Challanges/functions/statusFilterHandle.js @@ -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; diff --git a/src/pages/Challanges/model/ChallengesActionEnum.js b/src/pages/Challanges/model/ChallengesActions.js similarity index 100% rename from src/pages/Challanges/model/ChallengesActionEnum.js rename to src/pages/Challanges/model/ChallengesActions.js diff --git a/src/pages/Challanges/model/ChallengesReducer.js b/src/pages/Challanges/model/ChallengesReducer.js index 6b42f7a..99ac043 100644 --- a/src/pages/Challanges/model/ChallengesReducer.js +++ b/src/pages/Challanges/model/ChallengesReducer.js @@ -1,4 +1,4 @@ -import CHALLENGES_ACTION from './ChallengesActionEnum'; +import CHALLENGES_ACTION from './ChallengesActions'; const ChallengesReducer = (state, action) => { switch (action.type) { diff --git a/src/components/specific_challenge/Challenge.js b/src/pages/Challenge/Challenge.js similarity index 90% rename from src/components/specific_challenge/Challenge.js rename to src/pages/Challenge/Challenge.js index f739a5d..5f58272 100644 --- a/src/components/specific_challenge/Challenge.js +++ b/src/pages/Challenge/Challenge.js @@ -3,20 +3,20 @@ 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/Leaderboard'; +import Readme from '../Readme'; +import HowTo from '../HowTo/HowTo'; +import MyEntries from '../MyEntries/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 Loading from '../../components/generic/Loading'; import getUser from '../../api/getUser'; -import AllEntries from './AllEntries/AllEntries'; +import AllEntries from '../AllEntries/AllEntries'; const Challenge = (props) => { const challengeName = useParams().challengeId; diff --git a/src/components/specific_challenge/DesktopChallengeMenu.js b/src/pages/Challenge/components/DesktopChallengeMenu.js similarity index 90% rename from src/components/specific_challenge/DesktopChallengeMenu.js rename to src/pages/Challenge/components/DesktopChallengeMenu.js index 6d56525..05d43f6 100644 --- a/src/components/specific_challenge/DesktopChallengeMenu.js +++ b/src/pages/Challenge/components/DesktopChallengeMenu.js @@ -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; diff --git a/src/components/specific_challenge/MobileChallengeMenu.js b/src/pages/Challenge/components/MobileChallengeMenu.js similarity index 94% rename from src/components/specific_challenge/MobileChallengeMenu.js rename to src/pages/Challenge/components/MobileChallengeMenu.js index c3325a3..4a315fa 100644 --- a/src/components/specific_challenge/MobileChallengeMenu.js +++ b/src/pages/Challenge/components/MobileChallengeMenu.js @@ -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)` diff --git a/src/pages/Challenge/index.js b/src/pages/Challenge/index.js new file mode 100644 index 0000000..eeccdd9 --- /dev/null +++ b/src/pages/Challenge/index.js @@ -0,0 +1 @@ +export { default } from './Challenge'; diff --git a/src/components/specific_challenge/HowTo/HowTo.js b/src/pages/HowTo/HowTo.js similarity index 77% rename from src/components/specific_challenge/HowTo/HowTo.js rename to src/pages/HowTo/HowTo.js index d60de4f..07b50ae 100644 --- a/src/components/specific_challenge/HowTo/HowTo.js +++ b/src/pages/HowTo/HowTo.js @@ -1,9 +1,9 @@ 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'; +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); diff --git a/src/components/specific_challenge/HowTo/sections/HowToContent.js b/src/pages/HowTo/components/HowToContent.js similarity index 92% rename from src/components/specific_challenge/HowTo/sections/HowToContent.js rename to src/pages/HowTo/components/HowToContent.js index 8472f2e..2394acf 100644 --- a/src/components/specific_challenge/HowTo/sections/HowToContent.js +++ b/src/pages/HowTo/components/HowToContent.js @@ -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 = () => { diff --git a/src/components/specific_challenge/HowTo/index.js b/src/pages/HowTo/index.js similarity index 100% rename from src/components/specific_challenge/HowTo/index.js rename to src/pages/HowTo/index.js diff --git a/src/components/specific_challenge/Leaderboard/Leaderboard.js b/src/pages/Leaderboard/Leaderboard.js similarity index 84% rename from src/components/specific_challenge/Leaderboard/Leaderboard.js rename to src/pages/Leaderboard/Leaderboard.js index 8a67b25..e02cdf2 100644 --- a/src/components/specific_challenge/Leaderboard/Leaderboard.js +++ b/src/pages/Leaderboard/Leaderboard.js @@ -1,21 +1,20 @@ import React from 'react'; import Media from 'react-media'; -import theme from '../../../utils/theme'; -import { FlexColumn } from '../../../utils/containers'; -import { H2 } from '../../../utils/fonts'; -import Table from '../Table'; +import theme from '../../utils/theme'; +import { FlexColumn } from '../../utils/containers'; +import { H2 } from '../../utils/fonts'; +import Table from '../../components/generic/Table'; import PropsTypes from 'prop-types'; -import getChallengeLeaderboard from '../../../api/getChallengeLeaderboard'; +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'; +} from '../../utils/globals'; +import Search from '../../components/generic/Search'; +import Pager from '../../components/generic/Pager'; +import Loading from '../../components/generic/Loading'; const Leaderboard = (props) => { const [entriesFromApi, setEntriesFromApi] = React.useState([]); @@ -23,6 +22,7 @@ const Leaderboard = (props) => { 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([]); @@ -65,7 +65,7 @@ const Leaderboard = (props) => { }; const getLeaderboardHeader = () => { - let header = ['#', 'submitter']; + let header = ['#', 'submitter', 'description']; for (let metric of getPossibleMetrics()) { header.push(metric); } @@ -75,7 +75,7 @@ const Leaderboard = (props) => { }; const getLeaderboardHeaderMobile = () => { - let header = ['#', 'submitter', 'entries', 'when']; + let header = ['#', 'submitter', 'description', 'entries', 'when']; for (let metric of getPossibleMetrics()) { header.push(metric); } @@ -107,6 +107,27 @@ const Leaderboard = (props) => { 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); @@ -170,7 +191,7 @@ const Leaderboard = (props) => { tableType="leaderboard" gridTemplateColumns={ entries[0] - ? '1fr 3fr ' + + ? '1fr 2fr 3fr ' + '2fr '.repeat(entries[0].evaluations.length) + '1fr 2fr' : '' @@ -179,6 +200,7 @@ const Leaderboard = (props) => { 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' }, ]} @@ -224,7 +246,7 @@ const Leaderboard = (props) => { headerElements={getLeaderboardHeader()} gridTemplateColumns={ entries[0] - ? '1fr 3fr ' + + ? '1fr 2fr 3fr ' + '2fr '.repeat(entries[0].evaluations.length) + '1fr 2fr' : '' @@ -233,6 +255,7 @@ const Leaderboard = (props) => { 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' }, ]} @@ -255,7 +278,6 @@ const Leaderboard = (props) => { width="72px" borderRadius="64px" pages={CALC_PAGES(entries, 2)} - number={`${pageNr} / ${CALC_PAGES(entries, 2)}`} /> diff --git a/src/components/specific_challenge/Leaderboard/index.js b/src/pages/Leaderboard/index.js similarity index 100% rename from src/components/specific_challenge/Leaderboard/index.js rename to src/pages/Leaderboard/index.js diff --git a/src/components/specific_challenge/Leaderboard/leaderboardSearchQueryHandler.js b/src/pages/Leaderboard/leaderboardSearchQueryHandler.js similarity index 100% rename from src/components/specific_challenge/Leaderboard/leaderboardSearchQueryHandler.js rename to src/pages/Leaderboard/leaderboardSearchQueryHandler.js diff --git a/src/components/specific_challenge/Leaderboard/sortOptions.js b/src/pages/Leaderboard/sortOptions.js similarity index 100% rename from src/components/specific_challenge/Leaderboard/sortOptions.js rename to src/pages/Leaderboard/sortOptions.js diff --git a/src/components/specific_challenge/MyEntries/MyEntries.js b/src/pages/MyEntries/MyEntries.js similarity index 93% rename from src/components/specific_challenge/MyEntries/MyEntries.js rename to src/pages/MyEntries/MyEntries.js index 0411c63..a83e867 100644 --- a/src/components/specific_challenge/MyEntries/MyEntries.js +++ b/src/pages/MyEntries/MyEntries.js @@ -1,20 +1,20 @@ 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 { FlexColumn } from '../../utils/containers'; +import { H2 } from '../../utils/fonts'; +import getMyEntries from '../../api/getMyEntries'; +import Pager from '../../components/generic/Pager'; import { CALC_PAGES, EVALUATIONS_FORMAT, IS_MOBILE, RENDER_WHEN, -} from '../../../utils/globals'; +} from '../../utils/globals'; import Media from 'react-media'; -import theme from '../../../utils/theme'; -import Loading from '../../generic/Loading'; -import Table from '../Table'; +import theme from '../../utils/theme'; +import Loading from '../../components/generic/Loading'; +import Table from '../../components/generic/Table'; import myEntriesSearchQueryHandler from './myEntriesSearchQueryHandler'; -import Search from '../../generic/Search'; +import Search from '../../components/generic/Search'; const MyEntries = (props) => { const [myEntriesFromAPI, setMyEntriesFromAPI] = React.useState({}); diff --git a/src/components/specific_challenge/MyEntries/index.js b/src/pages/MyEntries/index.js similarity index 100% rename from src/components/specific_challenge/MyEntries/index.js rename to src/pages/MyEntries/index.js diff --git a/src/components/specific_challenge/MyEntries/myEntriesSearchQueryHandler.js b/src/pages/MyEntries/myEntriesSearchQueryHandler.js similarity index 100% rename from src/components/specific_challenge/MyEntries/myEntriesSearchQueryHandler.js rename to src/pages/MyEntries/myEntriesSearchQueryHandler.js diff --git a/src/components/specific_challenge/Readme.js b/src/pages/Readme.js similarity index 86% rename from src/components/specific_challenge/Readme.js rename to src/pages/Readme.js index cac39d9..463d625 100644 --- a/src/components/specific_challenge/Readme.js +++ b/src/pages/Readme.js @@ -1,14 +1,12 @@ import React from 'react'; -import { FlexColumn } from '../../utils/containers'; -import { Body, H2 } from '../../utils/fonts'; +import { FlexColumn } from '../utils/containers'; +import { Body, H2 } from '../utils/fonts'; import Media from 'react-media'; -import theme from '../../utils/theme'; -import getChallengeFullDescription from '../../api/getChallengeFullDescription'; +import theme from '../utils/theme'; +import getChallengeFullDescription from '../api/getChallengeFullDescription'; import styled from 'styled-components'; -import InfoList from '../generic/InfoList'; -import Loading from '../generic/Loading'; -import PropsTypes from 'prop-types'; -import MiniChallenge from '../challenges_list/MiniChallenge'; +import InfoList from '../components/generic/InfoList'; +import Loading from '../components/generic/Loading'; import { marked } from 'marked'; 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; diff --git a/src/pages/Submit/Submit.js b/src/pages/Submit/Submit.js new file mode 100644 index 0000000..397c272 --- /dev/null +++ b/src/pages/Submit/Submit.js @@ -0,0 +1,114 @@ +import React from 'react'; +import { createPortal } from 'react-dom'; +import { FlexColumn } from '../../utils/containers'; +import { H2, Menu } from '../../utils/fonts'; +import SubmitInput from '../../components/generic/SubmitInput'; +import Button from '../../components/generic/Button'; +import theme from '../../utils/theme'; +import challengeSubmission from '../../api/challengeSubmissionPost'; +import Loading from '../../components/generic/Loading'; +import getTags from '../../api/getTags'; +import TagsChoose from './components/TagsChoose'; +import SubmitReducer from './model/SubmitReducer'; +import SUBMIT_ACTION from './model/SubmitActionEnum'; +import SubmitStyle from './SubmitStyle'; + +const Submit = (props) => { + const [state, dispatch] = React.useReducer(SubmitReducer, { + description: '', + repoUrl: '', + repoBranch: '', + submissionLoading: false, + tags: [], + submissionTags: [], + }); + + React.useMemo(() => { + getTags(dispatch); + }, []); + + const challengeSubmissionSubmit = () => { + dispatch({ type: SUBMIT_ACTION.TOGGLE_SUBMISSION_LOADING }); + challengeSubmission( + props.challengeName, + state.repoUrl, + state.repoBranch, + state.description, + state.submissionTags, + dispatch + ); + }; + + const setDescription = (value) => { + 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 toggleSubmissionTag = React.useCallback( + (tag) => { + let actionType = ''; + if (state.submissionTags.includes(tag)) + actionType = SUBMIT_ACTION.REMOVE_SUBMISSION_TAG; + else actionType = SUBMIT_ACTION.ADD_SUBMISSION_TAG; + dispatch({ type: actionType, payload: tag }); + }, + [state.submissionTags] + ); + + const clearSubmissionTags = () => { + dispatch({ type: SUBMIT_ACTION.CLEAR_SUBMISSION_TAGS }); + }; + + if (!state.submissionLoading) { + return ( + +

+ Submit a solution to the challenge +

+ + + + + + + +
+ ); + } else { + return createPortal( + +

Submission processing...

+ +
, + document.body + ); + } +}; + +export default Submit; diff --git a/src/pages/Submit/SubmitStyle.js b/src/pages/Submit/SubmitStyle.js new file mode 100644 index 0000000..a06c8e6 --- /dev/null +++ b/src/pages/Submit/SubmitStyle.js @@ -0,0 +1,23 @@ +import styled from 'styled-components'; +import { FlexColumn } from '../../utils/containers'; + +const SubmitStyle = styled(FlexColumn)` + margin: 40px 0 0 0; + padding: 24px; + gap: 64px; + max-width: 624px; + width: 100%; + align-items: flex-start; + + .SubmitStyle__header { + width: 100%; + text-align: center; + } + + .SubmitStyle__form { + width: 100%; + gap: 32px; + } +`; + +export default SubmitStyle; diff --git a/src/pages/Submit/components/TagsChoose/TagsChoose.js b/src/pages/Submit/components/TagsChoose/TagsChoose.js new file mode 100644 index 0000000..2e99c34 --- /dev/null +++ b/src/pages/Submit/components/TagsChoose/TagsChoose.js @@ -0,0 +1,53 @@ +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 ( + { + if (!tagsPopUp) setTagsPopUp(true); + }} + > + + {props.label} + + props.handler(e.target.value)} + > + + {props.submissionTags.map((tag, i) => + i === 0 ? tag.name : `, ${tag.name}` + )} + + + + {tagsPopUp && + createPortal( + , + document.body + )} + + ); +}; + +export default TagsChoose; diff --git a/src/pages/Submit/components/TagsChoose/TagsChooseStyle.js b/src/pages/Submit/components/TagsChoose/TagsChooseStyle.js new file mode 100644 index 0000000..7d859c2 --- /dev/null +++ b/src/pages/Submit/components/TagsChoose/TagsChooseStyle.js @@ -0,0 +1,31 @@ +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; + overflow-y: scroll; + } +`; + +export default TagsChooseStyle; diff --git a/src/pages/Submit/components/TagsChoose/index.js b/src/pages/Submit/components/TagsChoose/index.js new file mode 100644 index 0000000..05aa6f3 --- /dev/null +++ b/src/pages/Submit/components/TagsChoose/index.js @@ -0,0 +1 @@ +export { default } from './TagsChoose'; diff --git a/src/pages/Submit/components/TagsChoosePopup/TagsChoosePopUp.js b/src/pages/Submit/components/TagsChoosePopup/TagsChoosePopUp.js new file mode 100644 index 0000000..9df83b2 --- /dev/null +++ b/src/pages/Submit/components/TagsChoosePopup/TagsChoosePopUp.js @@ -0,0 +1,57 @@ +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 renderTagItems from './functions/renderTagItems'; + +const TagsChoosePopUp = (props) => { + return ( + props.setTagsPopUp(false)} + > + + + + {renderTagItems( + props.submissionTags, + props.toggleSubmissionTag, + `1px dotted ${theme.colors.green}`, + theme.colors.green03, + true + )} + {renderTagItems(props.tags, props.toggleSubmissionTag, 'none')} + + + + + + + + + ); +}; + +export default TagsChoosePopUp; diff --git a/src/pages/Submit/components/TagsChoosePopup/TagsChoosePopUpStyle.js b/src/pages/Submit/components/TagsChoosePopup/TagsChoosePopUpStyle.js new file mode 100644 index 0000000..fce2abf --- /dev/null +++ b/src/pages/Submit/components/TagsChoosePopup/TagsChoosePopUpStyle.js @@ -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: space-between; + padding: 12px; + cursor: pointer; + transition: background-color 0.3s ease-in-out; + + &:hover { + background-color: ${({ theme }) => theme.colors.green03}; + } + } +`; + +export default TagsChoosePopUpStyle; diff --git a/src/pages/Submit/components/TagsChoosePopup/functions/renderTagItems.js b/src/pages/Submit/components/TagsChoosePopup/functions/renderTagItems.js new file mode 100644 index 0000000..fa83db4 --- /dev/null +++ b/src/pages/Submit/components/TagsChoosePopup/functions/renderTagItems.js @@ -0,0 +1,42 @@ +import theme from '../../../../../utils/theme'; +import tagBackgroundColorHandle from './tagBackgroundColorHandle'; +import { FlexRow } from '../../../../../utils/containers'; +import removeIco from '../../../../../assets/remove_ico.svg'; +import { Svg } from '../../../../../utils/containers'; + +const renderTagItems = ( + tags, + toggleTag, + border, + backgroundColor, + submissionTags = false +) => { + return tags.map((tag, index) => { + return ( + toggleTag(tag)} + className="TagsChoosePopUpStyle__tag-item" + backgroundColor={ + backgroundColor + ? backgroundColor + : tagBackgroundColorHandle(theme, index) + } + border={border} + > + {tag.name} + {submissionTags && ( + + )} + + ); + }); +}; + +export default renderTagItems; diff --git a/src/pages/Submit/components/TagsChoosePopup/functions/tagBackgroundColorHandle.js b/src/pages/Submit/components/TagsChoosePopup/functions/tagBackgroundColorHandle.js new file mode 100644 index 0000000..aaf118f --- /dev/null +++ b/src/pages/Submit/components/TagsChoosePopup/functions/tagBackgroundColorHandle.js @@ -0,0 +1,6 @@ +const tagBackgroundColorHandle = (theme, index) => { + if (index % 2 === 0) return theme.colors.white; + return theme.colors.dark01; +}; + +export default tagBackgroundColorHandle; diff --git a/src/pages/Submit/components/TagsChoosePopup/index.js b/src/pages/Submit/components/TagsChoosePopup/index.js new file mode 100644 index 0000000..a4781d6 --- /dev/null +++ b/src/pages/Submit/components/TagsChoosePopup/index.js @@ -0,0 +1 @@ +export { default } from './TagsChoosePopUp'; diff --git a/src/pages/Submit/index.js b/src/pages/Submit/index.js new file mode 100644 index 0000000..b5be4d5 --- /dev/null +++ b/src/pages/Submit/index.js @@ -0,0 +1 @@ +export { default } from './Submit'; diff --git a/src/pages/Submit/model/SubmitActionEnum.js b/src/pages/Submit/model/SubmitActionEnum.js new file mode 100644 index 0000000..c57bb6e --- /dev/null +++ b/src/pages/Submit/model/SubmitActionEnum.js @@ -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_TAG: 'add_submission_tag', + REMOVE_SUBMISSION_TAG: 'remove_submission_tag', + CLEAR_SUBMISSION_TAGS: 'clear_submission_tags', +}; + +export default SUBMIT_ACTION; diff --git a/src/pages/Submit/model/SubmitReducer.js b/src/pages/Submit/model/SubmitReducer.js new file mode 100644 index 0000000..803269b --- /dev/null +++ b/src/pages/Submit/model/SubmitReducer.js @@ -0,0 +1,42 @@ +import SUBMIT_ACTION from './SubmitActionEnum'; + +const SubmitReducer = (state, action) => { + console.log(`SubmitReducer: ${action.type}`); + let newSubmissionTags = state.submissionTags; + let newTags = state.tags; + 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) newTags = action.payload; + return { ...state, tags: newTags }; + case SUBMIT_ACTION.ADD_SUBMISSION_TAG: + if (!newSubmissionTags.includes(action.payload)) { + newTags = state.tags; + newSubmissionTags.push(action.payload); + newTags = newTags.filter((tag) => tag.name !== action.payload.name); + } + return { ...state, submissionTags: newSubmissionTags, tags: newTags }; + case SUBMIT_ACTION.REMOVE_SUBMISSION_TAG: + if (newSubmissionTags.includes(action.payload)) { + newSubmissionTags = newSubmissionTags.filter( + (tag) => tag.name !== action.payload.name + ); + newTags.push(action.payload); + newTags = newTags.sort((a, b) => a.name.localeCompare(b.name)); + } + return { ...state, submissionTags: newSubmissionTags, tags: newTags }; + case SUBMIT_ACTION.CLEAR_SUBMISSION_TAGS: + return { ...state, submissionTags: [] }; + default: + throw new Error('Undefined action in SubmitReducer!'); + } +}; + +export default SubmitReducer; diff --git a/src/utils/colors.js b/src/utils/colors.js index 40edebe..841cb3f 100644 --- a/src/utils/colors.js +++ b/src/utils/colors.js @@ -1,15 +1,16 @@ const colors = { - white: '#FCFCFC', - green: '#1B998B', - green03: 'rgba(27, 153, 139, 0.3)', - green05: 'rgba(27, 153, 139, 0.5)', - dark: '#343434', - dark01: 'rgba(52, 52, 52, 0.1)', - dark03: 'rgba(52, 52, 52, 0.3)', - dark04: 'rgba(52, 52, 52, 0.4)', - dark05: 'rgba(52, 52, 52, 0.5)', - dark07: 'rgba(52, 52, 52, 0.7)', - dark08: 'rgba(52, 52, 52, 0.8)', + white: '#FCFCFC', + green: '#1B998B', + blue: '#4B8FF0', + green03: 'rgba(27, 153, 139, 0.3)', + green05: 'rgba(27, 153, 139, 0.5)', + dark: '#343434', + dark01: 'rgba(52, 52, 52, 0.1)', + dark03: 'rgba(52, 52, 52, 0.3)', + dark04: 'rgba(52, 52, 52, 0.4)', + dark05: 'rgba(52, 52, 52, 0.5)', + dark07: 'rgba(52, 52, 52, 0.7)', + dark08: 'rgba(52, 52, 52, 0.8)', }; -export default colors; \ No newline at end of file +export default colors; diff --git a/src/utils/containers.js b/src/utils/containers.js index 24c794a..35ec2dc 100644 --- a/src/utils/containers.js +++ b/src/utils/containers.js @@ -37,6 +37,7 @@ const Container = styled.div` list-style: ${({ listStyle }) => (listStyle ? listStyle : 'none')}; overflow-wrap: ${({ overflowWrap }) => overflowWrap ? overflowWrap : 'normal'}; + overflow-y: ${({ overflowY }) => (overflowY ? overflowY : 'none')}; `; const FlexRow = styled(Container)`