Compare commits

..

No commits in common. "6c65af9bd72f0070ed5cc8ed6a1cabcc987ef115" and "ba0cdb944e128127b68fd45d92b25a4a6a58facd" have entirely different histories.

16 changed files with 414 additions and 594 deletions

View File

@ -1,14 +1,11 @@
import CHALLENGES_ACTION from '../pages/Challanges/model/ChallengesActionEnum';
import { API } from '../utils/globals'; import { API } from '../utils/globals';
const getChallenges = (dispatch) => { const getChallenges = (setDataStates, setLoadingState) => {
fetch(`${API}/list-challenges`) fetch(`${API}/list-challenges`)
.then((response) => response.json()) .then((response) => response.json())
.then((data) => { .then((data) => {
dispatch({ for (let setState of setDataStates) setState(data);
type: CHALLENGES_ACTION.LOAD_CHALLENGES_FROM_API, if (setLoadingState) setLoadingState(false);
payload: data,
});
}); });
}; };

View File

@ -1,75 +1,66 @@
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 '../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';
import PropsTypes from 'prop-types';
const FilterBy = (props) => { const FilterBy = (props) => {
const renderFilterOptions = () => { const renderFilterOptions = () => {
return props.options.map((option, index) => { return (
return ( props.options.map((option, index) => {
<Filter return (
key={`filter_option-${index}`} <Filter key={`filter_option-${index}`}
option={props.option} option={props.option} handler={props.handler}
handler={props.handler} id={`${props.header}-${option.name}-${index}`} name={props.header} index={index}>
id={`${props.header}-${option.name}-${index}`} <Body as='p'>
name={props.header} {option.name}
index={index} </Body>
> {option.sort ?
<Body as="p">{option.name}</Body> <Svg as='span' src={arrow} rotate={option.rotate ? option.rotate : '0'}
{option.sort && ( margin={option.rotate ? '2px 0 0 0' : '0 0 2px 0'}/>
<Svg : ''}
as="span" </Filter>
src={arrow} );
rotate={option.rotate ? option.rotate : '0'} })
margin={option.rotate ? '2px 0 0 0' : '0 0 2px 0'} );
/> };
)}
</Filter>
);
});
};
return ( return (
<FlexColumn <FlexColumn as='fieldset' width='100%' padding='8px' margin={props.margin ? props.margin : '0'}
as="fieldset" alignmentX={props.alignmentX ? props.alignmentX : 'flex-start'}>
width="100%" <Media query={theme.mobile}>
padding="8px" <Medium as='legend' textTransform='uppercase' margin='0 0 12px 0'>
margin={props.margin ? props.margin : '0'} {props.header}
alignmentX={props.alignmentX ? props.alignmentX : 'flex-start'} </Medium>
> </Media>
<Media query={theme.mobile}> <Media query={theme.desktop}>
<Medium as="legend" textTransform="uppercase" margin="0 0 12px 0"> <H3 as='legend' textTransform='uppercase' width='100%'
{props.header} textAlign={props.textAlign ? props.textAlign : 'left'} margin='0 0 24px 0'>
</Medium> {props.header}
</Media> </H3>
<Media query={theme.desktop}> </Media>
<H3 <Grid gridTemplateColumns={props.gridTemplateColumns ? props.gridTemplateColumns : 'auto auto'}
as="legend" gridTemplateRows={props.gridTemplateRows ? props.gridTemplateRows : 'auto'}
textTransform="uppercase" gridGap='12px' position='relative'>
width="100%" {renderFilterOptions()}
textAlign={props.textAlign ? props.textAlign : 'left'} </Grid>
margin="0 0 24px 0" </FlexColumn>
> );
{props.header}
</H3>
</Media>
<Grid
gridTemplateColumns={
props.gridTemplateColumns ? props.gridTemplateColumns : 'auto auto'
}
gridTemplateRows={
props.gridTemplateRows ? props.gridTemplateRows : 'auto'
}
gridGap="12px"
position="relative"
>
{renderFilterOptions()}
</Grid>
</FlexColumn>
);
}; };
export default FilterBy; FilterBy.propTypes = {
options: PropsTypes.arrayOf(PropsTypes.shape({
name: PropsTypes.string,
sort: PropsTypes.bool,
rotate: PropsTypes.string
}))
};
FilterBy.defaultProps = {
options: [],
};
export default FilterBy;

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 '../../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 PropsTypes from 'prop-types';
const FiltersMenuStyle = styled(FlexColumn)` const FiltersMenuStyle = styled(FlexColumn)`
position: fixed; position: fixed;
@ -18,8 +18,8 @@ const FiltersMenuStyle = styled(FlexColumn)`
max-height: 650px; max-height: 650px;
justify-content: flex-start; justify-content: flex-start;
padding: 14px 16px 14px 24px; padding: 14px 16px 14px 24px;
box-shadow: ${({ theme }) => theme.shadowLeft}; box-shadow: ${({theme}) => theme.shadowLeft};
background-color: ${({ theme }) => theme.colors.white}; background-color: ${({theme}) => theme.colors.white};
transition: transform 0.5s ease-in-out; transition: transform 0.5s ease-in-out;
z-index: 5; z-index: 5;
@ -27,122 +27,89 @@ const FiltersMenuStyle = styled(FlexColumn)`
display: none; display: none;
} }
@media (min-width: ${({ theme }) => theme.overMobile}) { @media (min-width: ${({theme}) => theme.overMobile}) {
width: 310px; width: 310px;
max-height: none; max-height: none;
top: 50px; top: 50px;
right: auto; right: auto;
left: 0; left: 0;
box-shadow: ${({ theme }) => theme.shadowRight}; box-shadow: ${({theme}) => theme.shadowRight};
padding: 32px 32px 64px; padding: 32px 32px 64px;
} }
`; `;
const FiltersMenu = (props) => { const FiltersMenu = (props) => {
const sortByHandler = (value) => { const resetHandler = () => {
props.dispatch({ type: CHALLENGES_ACTION.SET_SORT_BY, payload: value }); props.sortByHandler(0);
}; props.statusHandler(0);
props.challengeTypeHandler(0);
props.commercialHandler(0);
};
const statusHandler = (value) => { return (
props.dispatch({ <>
type: CHALLENGES_ACTION.SET_STATUS_FILTER, <TransBack backgroundColor={theme.colors.dark03} translateX={props.translateX}
payload: value, opacity={props.opacity} onClick={props.toggleFiltersMenu}
}); display={props.transBackDisplay ? props.transBackDisplay : 'flex'}/>
}; <FiltersMenuStyle translateX={props.translateX} gap='16px'>
<FilterBy header='Sort by' options={filterOptions[0]}
const challengeTypeHandler = (value) => { handler={props.sortByHandler} option={props.sortBy}/>
props.dispatch({ <FilterBy header='Status' options={filterOptions[1]}
type: CHALLENGES_ACTION.SET_CHALLENGE_TYPE_FILTER, handler={props.statusHandler} option={props.status}/>
payload: value, <FilterBy header='Challenge type' options={filterOptions[2]}
}); handler={props.challengeTypeHandler} option={props.challengeType}/>
}; <FilterBy header='Commercial' options={filterOptions[3]}
handler={props.commercialHandler} option={props.commercial}/>
const commercialHandler = (value) => { <Media query={theme.mobile}>
props.dispatch({ <FlexRow gap='16px' margin='14px 0 0 0'>
type: CHALLENGES_ACTION.SET_COMMERCIAL_FILTER, <Button width='64px' height='28px' handler={props.toggleFiltersMenu}>
payload: value, Done
}); </Button>
}; <Button width='64px' height='28px' backgroundColor={theme.colors.dark} handler={resetHandler}>
Reset
const resetHandler = () => { </Button>
sortByHandler(0); </FlexRow>
statusHandler(0); </Media>
challengeTypeHandler(0); <Media query={theme.desktop}>
commercialHandler(0); <FlexRow margin='8px 0 0 0' width='94%' alignmentX='flex-start'>
}; <Button width='72px' height='34px' backgroundColor={theme.colors.green} handler={resetHandler}>
Reset
return ( </Button>
<> </FlexRow>
<TransBack </Media>
backgroundColor={theme.colors.dark03} </FiltersMenuStyle>
translateX={props.translateX} </>
opacity={props.opacity} );
onClick={() =>
props.dispatch({ type: CHALLENGES_ACTION.TOGGLE_FILTERS_MENU })
}
display={props.transBackDisplay ? props.transBackDisplay : 'flex'}
/>
<FiltersMenuStyle translateX={props.translateX} gap="16px">
<FilterBy
header="Sort by"
options={filterOptions[0]}
handler={sortByHandler}
option={props.sortBy}
/>
<FilterBy
header="Status"
options={filterOptions[1]}
handler={statusHandler}
option={props.status}
/>
<FilterBy
header="Challenge type"
options={filterOptions[2]}
handler={challengeTypeHandler}
option={props.challengeTypeFilter}
/>
<FilterBy
header="Commercial"
options={filterOptions[3]}
handler={commercialHandler}
option={props.commercialFilter}
/>
<Media query={theme.mobile}>
<FlexRow gap="16px" margin="14px 0 0 0">
<Button
width="64px"
height="28px"
handler={() =>
props.dispatch({ type: CHALLENGES_ACTION.TOGGLE_FILTERS_MENU })
}
>
Done
</Button>
<Button
width="64px"
height="28px"
backgroundColor={theme.colors.dark}
handler={resetHandler}
>
Reset
</Button>
</FlexRow>
</Media>
<Media query={theme.desktop}>
<FlexRow margin="8px 0 0 0" width="94%" alignmentX="flex-start">
<Button
width="72px"
height="34px"
backgroundColor={theme.colors.green}
handler={resetHandler}
>
Reset
</Button>
</FlexRow>
</Media>
</FiltersMenuStyle>
</>
);
}; };
export default FiltersMenu; FiltersMenu.propTypes = {
translateX: PropsTypes.string,
opacity: PropsTypes.string,
transBackDisplay: PropsTypes.string,
toggleFiltersMenu: PropsTypes.func,
sortByHandler: PropsTypes.func,
statusHandler: PropsTypes.func,
challengeTypeHandler: PropsTypes.func,
commercialHandler: PropsTypes.func,
sortBy: PropsTypes.number,
status: PropsTypes.number,
challengeType: PropsTypes.number,
commercial: PropsTypes.number,
};
FiltersMenu.defaultProps = {
translateX: '',
opacity: '',
transBackDisplay: 'flex',
toggleFiltersMenu: null,
sortByHandler: null,
statusHandler: null,
challengeTypeHandler: null,
commercialHandler: null,
sortBy: 0,
status: 0,
challengeType: 0,
commercial: 0,
};
export default FiltersMenu;

View File

@ -1,35 +1,31 @@
import React from 'react'; import React from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import { FlexRow } from '../../utils/containers'; import {FlexRow} from '../../utils/containers';
import PropsTypes from 'prop-types';
const FilterStyle = styled(FlexRow)` const FilterStyle = styled(FlexRow)`
width: fit-content; width: fit-content;
height: 36px; height: 36px;
padding: 8px 16px; padding: 8px 16px;
border-radius: ${({ borderRadius }) => border-radius: ${({borderRadius}) => borderRadius ? borderRadius : '32px'};
borderRadius ? borderRadius : '32px'}; border: 1px solid ${({theme}) => theme.colors.dark};
border: 1px solid ${({ theme }) => theme.colors.dark}; box-shadow: ${({theme}) => theme.shadow};
box-shadow: ${({ theme }) => theme.shadow};
cursor: pointer; cursor: pointer;
background-color: ${({ theme, active }) => background-color: ${({theme, active}) => active ? theme.colors.dark : theme.colors.white};
active ? theme.colors.dark : theme.colors.white};
transition: transform 0.3s ease-in-out; transition: transform 0.3s ease-in-out;
z-index: 2; z-index: 2;
color: ${({ theme, active }) => color: ${({theme, active}) => active ? theme.colors.white : theme.colors.dark};
active ? theme.colors.white : theme.colors.dark};
&:hover { &:hover {
transform: scale(1.1); transform: scale(1.1);
} }
p { p {
color: ${({ theme, active }) => color: ${({theme, active}) => active ? theme.colors.white : theme.colors.dark};
active ? theme.colors.white : theme.colors.dark};
} }
span { span {
background-color: ${({ theme, active }) => background-color: ${({theme, active}) => active ? theme.colors.white : theme.colors.dark};
active ? theme.colors.white : theme.colors.dark};
} }
* { * {
@ -38,32 +34,36 @@ const FilterStyle = styled(FlexRow)`
`; `;
const Filter = (props) => { const Filter = (props) => {
const onCheckHandler = (e) => { const onCheckHandler = (e) => {
if (e.target.checked) props.handler(Number(e.target.value)); if (e.target.checked)
}; props.handler(Number(e.target.value));
};
return ( return (
<> <>
<FilterStyle <FilterStyle as='label' borderRadius={props.borderRadius} htmlFor={props.id}
as="label" active={props.option === props.index}>
borderRadius={props.borderRadius} {props.children}
htmlFor={props.id} </FilterStyle>
active={props.option === props.index} <FlexRow display='none' as='input' type='radio' value={props.index}
> id={props.id} name={props.name} checked={props.option === props.index}
{props.children} onChange={(e) => onCheckHandler(e)}/>
</FilterStyle> </>
<FlexRow );
display="none"
as="input"
type="radio"
value={props.index}
id={props.id}
name={props.name}
checked={props.option === props.index}
onChange={(e) => onCheckHandler(e)}
/>
</>
);
}; };
export default Filter; Filter.propTypes = {
index: PropsTypes.number.isRequired,
option: PropsTypes.number.isRequired,
handler: PropsTypes.func,
id: PropsTypes.string.isRequired,
name: PropsTypes.string.isRequired,
children: PropsTypes.node,
};
Filter.defaultProps = {
handler: null,
children: '',
};
export default Filter;

View File

@ -1,35 +1,37 @@
import React from 'react'; import React from 'react';
import { FlexRow, Svg } from '../../utils/containers'; import {FlexRow, Svg} from '../../utils/containers';
import CircleNumber from './CircleNumber'; import CircleNumber from './CircleNumber';
import polygon from '../../assets/polygon.svg'; import polygon from '../../assets/polygon.svg';
import styled from 'styled-components'; import styled from 'styled-components';
import theme from '../../utils/theme'; import theme from '../../utils/theme';
import PropsTypes from 'prop-types';
import { NEXT_PAGE, PREVIOUS_PAGE } from '../../utils/globals'; import { NEXT_PAGE, PREVIOUS_PAGE } from '../../utils/globals';
const PagerStyle = styled(FlexRow)` const PagerStyle = styled(FlexRow)`
gap: 14px; gap: 14px;
@media (min-width: ${({ theme }) => theme.overMobile}) { @media (min-width: ${({theme}) => theme.overMobile}) {
gap: 20px; gap: 20px;
} }
`; `;
const LeftArrow = styled(Svg)` const LeftArrow = styled(Svg)`
background-color: ${({ backgroundColor }) => backgroundColor}; background-color: ${({backgroundColor}) => backgroundColor};
cursor: ${({ backgroundColor }) => cursor: ${({backgroundColor}) => (backgroundColor === 'transparent') ? 'auto' : 'pointer'};
backgroundColor === 'transparent' ? 'auto' : 'pointer'};
width: 10px; width: 10px;
height: 10px; height: 10px;
transition: background-color, transform 0.3s ease-in-out; transition: background-color, transform 0.3s ease-in-out;
&:hover, &:hover, &:focus {
&:focus { background-color: ${({
background-color: ${({ theme, backgroundColor }) => theme,
backgroundColor === 'transparent' ? 'transparent' : theme.colors.green}; backgroundColor
}) => (backgroundColor === 'transparent') ? 'transparent' : theme.colors.green};
transform: scale(1.1); transform: scale(1.1);
} }
@media (min-width: ${({ theme }) => theme.overMobile}) { @media (min-width: ${({theme}) => theme.overMobile}) {
width: 12px; width: 12px;
height: 12px; height: 12px;
} }
@ -38,50 +40,57 @@ const LeftArrow = styled(Svg)`
const RightArrow = styled(LeftArrow)` const RightArrow = styled(LeftArrow)`
transform: rotate(180deg); transform: rotate(180deg);
&:hover, &:hover, &:focus {
&:focus { background-color: ${({
background-color: ${({ theme, backgroundColor }) => theme,
backgroundColor === 'transparent' ? 'transparent' : theme.colors.green}; backgroundColor
}) => (backgroundColor === 'transparent') ? 'transparent' : theme.colors.green};
transform: scale(1.1) rotate(180deg); transform: scale(1.1) rotate(180deg);
} }
`; `;
const Pager = (props) => { const Pager = (props) => {
const leftArrowVisible = () => { const leftArrowVisible = () => {
if (props.pageNr === 1) return 'transparent'; if (props.pageNr === 1)
return theme.colors.dark; return 'transparent';
}; return theme.colors.dark;
};
const rightArrowVisible = () => { const rightArrowVisible = () => {
if (props.pageNr === props.pages) return 'transparent'; if (props.pageNr === props.pages)
return theme.colors.dark; return 'transparent';
}; return theme.colors.dark;
};
return ( return (
<PagerStyle> <PagerStyle>
<LeftArrow <LeftArrow as='a' href='#start' src={polygon} onClick={() => PREVIOUS_PAGE(props.pageNr, props.setPageNr)} size='cover'
as="a" backgroundColor={leftArrowVisible()}/>
href="#start" <CircleNumber number={props.number} width={props.width} borderRadius={props.borderRadius}/>
src={polygon} <RightArrow as='a' href='#start' src={polygon} onClick={() => NEXT_PAGE(props.elements, props.pageNr, props.setPageNr)} size='cover'
onClick={() => PREVIOUS_PAGE(props.pageNr, props.setPage)} backgroundColor={rightArrowVisible()}/>
size="cover" </PagerStyle>
backgroundColor={leftArrowVisible()} );
/>
<CircleNumber
number={props.number}
width={props.width}
borderRadius={props.borderRadius}
/>
<RightArrow
as="a"
href="#start"
src={polygon}
onClick={() => NEXT_PAGE(props.elements, props.pageNr, props.setPage)}
size="cover"
backgroundColor={rightArrowVisible()}
/>
</PagerStyle>
);
}; };
export default Pager; Pager.propTypes = {
previousPage: PropsTypes.func,
pageNr: PropsTypes.number,
nextPage: PropsTypes.func,
pages: PropsTypes.number,
number: PropsTypes.string,
width: PropsTypes.string,
borderRadius: PropsTypes.string
};
Pager.defaultProps = {
previousPage: null,
pageNr: 1,
nextPage: null,
pages: 1,
number: '',
width: null,
borderRadius: null
};
export default Pager;

View File

@ -7,10 +7,6 @@ import { Body, Medium } from '../../utils/fonts';
import styled from 'styled-components'; import styled from 'styled-components';
import ColumnFilterIcon from './ColumnFilterIcon'; import ColumnFilterIcon from './ColumnFilterIcon';
const TableStyle = styled(FlexColumn)`
overflow-x: ${({metrics}) => metrics > 10 ? 'scroll' : 'auto'};
`;
const Line = styled(FlexRow)` const Line = styled(FlexRow)`
position: absolute; position: absolute;
top: ${({ top }) => (top ? top : 'auto')}; top: ${({ top }) => (top ? top : 'auto')};
@ -109,7 +105,7 @@ const Table = (props) => {
as="td" as="td"
order={props.iterableColumnElement.order} order={props.iterableColumnElement.order}
textAlign={props.iterableColumnElement.align} textAlign={props.iterableColumnElement.align}
minWidth="88px" minWidth="72px"
margin="auto 0" margin="auto 0"
overflowWrap="anywhere" overflowWrap="anywhere"
> >
@ -137,7 +133,6 @@ const Table = (props) => {
order={elemName.order} order={elemName.order}
textAlign={elemName.align} textAlign={elemName.align}
margin="auto 0" margin="auto 0"
minWidth="88px"
overflowWrap="anywhere" overflowWrap="anywhere"
> >
{IS_MOBILE() && ( {IS_MOBILE() && (
@ -158,7 +153,7 @@ const Table = (props) => {
let elementsToMap = props.elements.slice(n, n + ELEMENTS_PER_PAGE * 2); let elementsToMap = props.elements.slice(n, n + ELEMENTS_PER_PAGE * 2);
if (elementsToMap.length > 0) { if (elementsToMap.length > 0) {
return ( return (
<TableStyle as="table" margin="32px 0 72px 0" width="100%"> <FlexColumn as="table" margin="32px 0 72px 0" width="100%">
<FlexColumn as="tbody" width="100%"> <FlexColumn as="tbody" width="100%">
<Grid <Grid
as="tr" as="tr"
@ -195,9 +190,7 @@ const Table = (props) => {
width={elem === 'when' ? '100%' : 'auto'} width={elem === 'when' ? '100%' : 'auto'}
padding="0 4px 0 0" padding="0 4px 0 0"
overflowWrap="anywhere" overflowWrap="anywhere"
minWidth="72px" minWidth={elem === 'result' ? '72px' : 'none'}
// minWidth={elem === 'result' ? '72px' : 'none'}
> >
{elem.replace('.', ' ')} {elem.replace('.', ' ')}
</Medium> </Medium>
@ -240,7 +233,7 @@ const Table = (props) => {
); );
})} })}
</FlexColumn> </FlexColumn>
</TableStyle> </FlexColumn>
); );
} }
return <Medium margin="72px 0">No results ;c</Medium>; return <Medium margin="72px 0">No results ;c</Medium>;

View File

@ -1,102 +1,163 @@
import React from 'react'; import React from 'react';
import { Body, H1 } from '../../utils/fonts';
import { FlexColumn, FlexRow, Svg } from '../../utils/containers';
import Search from '../../components/generic/Search';
import Pager from '../../components/generic/Pager';
import challengeSearchQueryHandler from './challengeSearchQueryHandler';
import renderChallenges from './renderChallenges';
import Media from 'react-media'; import Media from 'react-media';
import theme from '../../utils/theme'; import theme from '../../utils/theme';
import cupIco from '../../assets/cup_ico.svg';
import getChallenges from '../../api/getChallenges'; import getChallenges from '../../api/getChallenges';
import { CHALLENGES_STATUS_FILTER } from '../../utils/globals'; import { CALC_PAGES, CHALLENGES_STATUS_FILTER } from '../../utils/globals';
import Loading from '../../components/generic/Loading';
import ChallengesStyle from './ChallengesStyle';
import FiltersMenu from '../../components/challenges_list/FiltersMenu'; import FiltersMenu from '../../components/challenges_list/FiltersMenu';
import statusFilterHandle from './functions/statusFilterHandle'; import statusFilter from './statusFilter';
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';
const Challenges = () => { const Challenges = () => {
const [state, dispatch] = React.useReducer(ChallengesReducer, { const [pageNr, setPageNr] = React.useState(1);
pageNr: 1, const [challengesFromAPI, setChallengesFromAPI] = React.useState([]);
challengesFromAPI: [], const [challenges, setChallenges] = React.useState([]);
challenges: [], const [challengesFiltered, setChallengesFiltered] = React.useState([]);
challengesFiltered: [], const [filtersMenu, setFiltersMenu] = React.useState(false);
filtersMenu: false, const [sortBy, setSortBy] = React.useState(0);
sortBy: 0, const [status, setStatus] = React.useState(CHALLENGES_STATUS_FILTER.BOTH);
statusFilter: CHALLENGES_STATUS_FILTER.BOTH, const [challengeType, setChallengeType] = React.useState(0);
challengeTypeFilter: 0, const [commercial, setCommercial] = React.useState(0);
commercialFilter: 0, const [loading, setLoading] = React.useState(true);
loading: true,
});
React.useMemo(() => { React.useEffect(() => {
getChallenges(dispatch); challengesRequest();
}, []); }, []);
React.useEffect(() => { React.useEffect(() => {
statusFilterHandle(state.statusFilter, state.challenges, dispatch); statusFilter(status, challenges, setChallengesFiltered);
}, [state.statusFilter, state.challenges]); }, [status, challenges]);
const setPage = React.useCallback((value) => { const challengesRequest = () => {
dispatch({ type: CHALLENGES_ACTION.SET_PAGE, payload: value }); getChallenges(
}, []); [setChallengesFromAPI, setChallenges, setChallengesFiltered],
setLoading
);
};
const searchQueryHandler = React.useCallback( const searchQueryHandler = (event) => {
(event) => challengeSearchQueryHandler(
challengeSearchQueryHandler( event,
event, challengesFromAPI,
state.challengesFromAPI, setPageNr,
state.setPageNr, setChallenges
dispatch );
), };
[state.challengesFromAPI, state.setPageNr]
);
const filtersMenuRender = React.useCallback( const toggleFiltersMenu = () => {
(translateX = '0', opacity = '1', transBackDisplay = 'none') => { let newFiltersMenu = !filtersMenu;
return ( setFiltersMenu(newFiltersMenu);
<FiltersMenu };
dispatch={dispatch}
sortBy={state.sortBy} const filtersMenuRender = (
status={state.statusFilter} translateX = '0',
challengeTypeFilter={state.challengeTypeFilter} opacity = '1',
commercialFilter={state.commercialFilter} transBackDisplay = 'none'
translateX={translateX} ) => {
opacity={opacity} return (
transBackDisplay={transBackDisplay} <FiltersMenu
/> toggleFiltersMenu={toggleFiltersMenu}
); sortByHandler={setSortBy}
}, statusHandler={setStatus}
[ challengeTypeHandler={setChallengeType}
state.sortBy, commercialHandler={setCommercial}
state.statusFilter, sortBy={sortBy}
state.challengeTypeFilter, status={status}
state.commercialFilter, challengeType={challengeType}
] commercial={commercial}
); translateX={translateX}
opacity={opacity}
transBackDisplay={transBackDisplay}
/>
);
};
const mobileRender = () => {
return (
<>
{filtersMenuRender(
filtersMenu ? '0' : '100vw',
filtersMenu ? '1' : '0',
'flex'
)}
<ChallengesStyle as="main" id="start">
<FlexColumn className="ChallengesStyle__page-container">
<H1 as="h1">Challenges</H1>
<Search
searchQueryHandler={searchQueryHandler}
filterButton
toggleFiltersMenu={toggleFiltersMenu}
/>
<FlexColumn width="100%">
<Loading visible={loading} />
{renderChallenges(pageNr, challengesFiltered)}
</FlexColumn>
</FlexColumn>
{!loading && (
<Pager
elements={challengesFiltered}
pageNr={pageNr}
setPageNr={setPageNr}
pages={CALC_PAGES(challengesFiltered)}
width="48px"
borderRadius="64px"
number={`${pageNr} / ${CALC_PAGES(challengesFiltered)}`}
/>
)}
</ChallengesStyle>
</>
);
};
const desktopRender = () => {
return (
<>
{filtersMenuRender()}
<ChallengesStyle as="main" id="start">
<FlexColumn className="ChallengesStyle__page-container">
<FlexRow className="ChallengesStyle__page-header-container">
<FlexColumn className="ChallengesStyle__page-header">
<H1 as="h1">Challenges</H1>
<Body className="ChallengesStyle__header-content">
Increase your machine learning skills by competing in our
exciting challenges.
</Body>
<Search searchQueryHandler={searchQueryHandler} />
</FlexColumn>
<Svg src={cupIco} className="ChallengesStyle__main-image" />
</FlexRow>
<FlexColumn width="100%">
<Loading visible={loading} />
{renderChallenges(pageNr, challengesFiltered)}
</FlexColumn>
</FlexColumn>
{!loading && (
<Pager
pageNr={pageNr}
setPageNr={setPageNr}
elements={challengesFiltered}
pages={CALC_PAGES(challengesFiltered)}
width="72px"
borderRadius="64px"
number={`${pageNr} / ${CALC_PAGES(challengesFiltered)}`}
/>
)}
</ChallengesStyle>
</>
);
};
return ( return (
<> <>
<Media query={theme.mobile}> <Media query={theme.mobile}>{mobileRender()}</Media>
<ChallengesMobile <Media query={theme.desktop}>{desktopRender()}</Media>
dispatch={dispatch}
filtersMenuRender={filtersMenuRender}
searchQueryHandler={searchQueryHandler}
setPage={setPage}
filtersMenu={state.filtersMenu}
loading={state.loading}
pageNr={state.pageNr}
challengesFiltered={state.challengesFiltered}
/>
</Media>
<Media query={theme.desktop}>
<ChallengesDesktop
dispatch={dispatch}
filtersMenuRender={filtersMenuRender}
searchQueryHandler={searchQueryHandler}
setPage={setPage}
filtersMenu={state.filtersMenu}
loading={state.loading}
pageNr={state.pageNr}
challengesFiltered={state.challengesFiltered}
/>
</Media>
</> </>
); );
}; };

View File

@ -0,0 +1,19 @@
const challengeSearchQueryHandler = (event, challengesFromAPI, setPageNr, setChallenges) => {
let searchQuery = event.target.value;
let challengesToRender = [];
setPageNr(1);
if (searchQuery === '')
setChallenges(challengesFromAPI);
else {
for (let challenge of challengesFromAPI) {
const {title, description, type, mainMetric, bestScore, deadline, baseline, prize} = challenge;
const str = `${title} ${description} ${type} ${mainMetric} ${bestScore}
${deadline ? deadline.slice(11, 16) : ''} ${deadline ? deadline.slice(0, 10) : ''} ${baseline} ${prize}`;
if (str.toLowerCase().includes(searchQuery.toLowerCase()))
challengesToRender.push(challenge);
}
setChallenges(challengesToRender);
}
};
export default challengeSearchQueryHandler;

View File

@ -1,51 +0,0 @@
import React from 'react';
import ChallengesStyle from '../ChallengesStyle';
import { FlexColumn, FlexRow } from '../../../utils/containers';
import Pager from '../../../components/generic/Pager';
import { H1, Body } from '../../../utils/fonts';
import Search from '../../../components/generic/Search';
import { CALC_PAGES } from '../../../utils/globals';
import renderChallenges from '../functions/renderChallenges';
import Loading from '../../../components/generic/Loading';
import cupIco from '../../../assets/cup_ico.svg';
import { Svg } from '../../../utils/containers';
const ChallengesDesktop = (props) => {
return (
<>
{props.filtersMenuRender()}
<ChallengesStyle as="main" id="start">
<FlexColumn className="ChallengesStyle__page-container">
<FlexRow className="ChallengesStyle__page-header-container">
<FlexColumn className="ChallengesStyle__page-header">
<H1 as="h1">Challenges</H1>
<Body className="ChallengesStyle__header-content">
Increase your machine learning skills by competing in our
exciting challenges.
</Body>
<Search searchQueryHandler={props.searchQueryHandler} />
</FlexColumn>
<Svg src={cupIco} className="ChallengesStyle__main-image" />
</FlexRow>
<FlexColumn width="100%">
<Loading visible={props.loading} />
{renderChallenges(props.pageNr, props.challengesFiltered)}
</FlexColumn>
</FlexColumn>
{!props.loading && (
<Pager
pageNr={props.pageNr}
setPage={props.setPage}
elements={props.challengesFiltered}
pages={CALC_PAGES(props.challengesFiltered)}
width="72px"
borderRadius="64px"
number={`${props.pageNr} / ${CALC_PAGES(props.challengesFiltered)}`}
/>
)}
</ChallengesStyle>
</>
);
};
export default ChallengesDesktop;

View File

@ -1,53 +0,0 @@
import React from 'react';
import ChallengesStyle from '../ChallengesStyle';
import { FlexColumn } from '../../../utils/containers';
import Pager from '../../../components/generic/Pager';
import { H1 } from '../../../utils/fonts';
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';
const ChallengesMobile = (props) => {
return (
<>
{props.filtersMenuRender(
props.filtersMenu ? '0' : '100vw',
props.filtersMenu ? '1' : '0',
'flex'
)}
<ChallengesStyle as="main" id="start">
<FlexColumn className="ChallengesStyle__page-container">
<H1 as="h1">Challenges</H1>
<Search
searchQueryHandler={props.searchQueryHandler}
filterButton
toggleFiltersMenu={() =>
props.dispatch({
type: CHALLENGES_ACTION.TOGGLE_FILTERS_MENU,
})
}
/>
<FlexColumn width="100%">
<Loading visible={props.loading} />
{renderChallenges(props.pageNr, props.challengesFiltered)}
</FlexColumn>
</FlexColumn>
{!props.loading && (
<Pager
width="48px"
borderRadius="64px"
elements={props.challengesFiltered}
pageNr={props.pageNr}
setPage={props.setPage}
pages={CALC_PAGES(props.challengesFiltered)}
number={`${props.pageNr} / ${CALC_PAGES(props.challengesFiltered)}`}
/>
)}
</ChallengesStyle>
</>
);
};
export default ChallengesMobile;

View File

@ -1,38 +0,0 @@
import CHALLENGES_ACTION from '../model/ChallengesActionEnum';
const challengeSearchQueryHandler = (event, challengesFromAPI, dispatch) => {
let searchQuery = event.target.value;
let challengesToRender = [];
dispatch({ type: CHALLENGES_ACTION.SET_PAGE, payload: 1 });
if (searchQuery === '')
dispatch({
type: CHALLENGES_ACTION.SET_CHALLENGES,
payload: challengesFromAPI,
});
else {
for (let challenge of challengesFromAPI) {
const {
title,
description,
type,
mainMetric,
bestScore,
deadline,
baseline,
prize,
} = challenge;
const str = `${title} ${description} ${type} ${mainMetric} ${bestScore}
${deadline ? deadline.slice(11, 16) : ''} ${
deadline ? deadline.slice(0, 10) : ''
} ${baseline} ${prize}`;
if (str.toLowerCase().includes(searchQuery.toLowerCase()))
challengesToRender.push(challenge);
}
dispatch({
type: CHALLENGES_ACTION.SET_CHALLENGES,
payload: challengesToRender,
});
}
};
export default challengeSearchQueryHandler;

View File

@ -1,16 +0,0 @@
const CHALLENGES_ACTION = {
NEXT_PAGE: 'next_page',
PREVIOUS_PAGE: 'previous_page',
SET_PAGE: 'set_page',
LOAD_CHALLENGES_FROM_API: 'load_challenges_from_api',
SET_CHALLENGES: 'set_challenges',
SET_CHALLENGES_FILTERED: 'set_challenges_filtered',
TOGGLE_FILTERS_MENU: 'toggle_filters_menu',
TOGGLE_LOADING: 'toggle_loading',
SET_SORT_BY: 'set_sort_by',
SET_STATUS_FILTER: 'set_status_filter',
SET_CHALLENGE_TYPE_FILTER: 'set_challenge_type_filter',
SET_COMMERCIAL_FILTER: 'set_commercial_filter',
};
export default CHALLENGES_ACTION;

View File

@ -1,58 +0,0 @@
import CHALLENGES_ACTION from './ChallengesActionEnum';
const ChallengesReducer = (state, action) => {
switch (action.type) {
case CHALLENGES_ACTION.NEXT_PAGE:
return { ...state, pageNr: state.pageNr + 1 };
case CHALLENGES_ACTION.PREVIOUS_PAGE:
return { ...state, pageNr: state.pageNr - 1 };
case CHALLENGES_ACTION.SET_PAGE:
return { ...state, pageNr: action.payload };
case CHALLENGES_ACTION.LOAD_CHALLENGES_FROM_API:
return {
...state,
challengesFromAPI: action.payload,
challenges: action.payload,
challengesFiltered: action.payload,
loading: !state.loading,
};
case CHALLENGES_ACTION.SET_CHALLENGES:
return {
...state,
challenges: action.payload,
};
case CHALLENGES_ACTION.SET_CHALLENGES_FILTERED:
return {
...state,
challengesFiltered: action.payload,
};
case CHALLENGES_ACTION.TOGGLE_FILTERS_MENU:
return { ...state, filtersMenu: !state.filtersMenu };
case CHALLENGES_ACTION.TOGGLE_LOADING:
return { ...state, loading: !state.loading };
case CHALLENGES_ACTION.SET_SORT_BY:
return {
...state,
sortBy: action.payload,
};
case CHALLENGES_ACTION.SET_STATUS_FILTER:
return {
...state,
statusFilter: action.payload,
};
case CHALLENGES_ACTION.SET_CHALLENGE_TYPE_FILTER:
return {
...state,
challengeTypeFilter: action.payload,
};
case CHALLENGES_ACTION.SET_COMMERCIAL_FILTER:
return {
...state,
commercialFilter: action.payload,
};
default:
throw new Error('Undefined action in ChallengesReducer!');
}
};
export default ChallengesReducer;

View File

@ -1,6 +1,6 @@
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/challenges_list/MiniChallenge';
import { Grid } from '../../../utils/containers'; import { Grid } from '../../utils/containers';
import styled from 'styled-components'; import styled from 'styled-components';
const ChallengesGrid = styled(Grid)` const ChallengesGrid = styled(Grid)`
@ -18,7 +18,7 @@ const ChallengesGrid = styled(Grid)`
} }
`; `;
const renderChallenges = (pageNr, challenges) => { const renderChallenges = (pageNr, challenges, statusFilter) => {
const n = (pageNr - 1) * ELEMENTS_PER_PAGE; const n = (pageNr - 1) * ELEMENTS_PER_PAGE;
if (challenges && challenges !== []) { if (challenges && challenges !== []) {
return ( return (

View File

@ -1,7 +1,8 @@
import { CHALLENGES_STATUS_FILTER } from '../../../utils/globals'; import { CHALLENGES_STATUS_FILTER } from '../../utils/globals';
import CHALLENGES_ACTION from '../model/ChallengesActionEnum';
const dateIsOlder = (newerDate, olderDate) => { const dateIsOlder = (newerDate, olderDate) => {
console.log(newerDate);
console.log(olderDate);
if (newerDate.year > olderDate.year) return true; if (newerDate.year > olderDate.year) return true;
else if (newerDate.month > olderDate.month) return true; else if (newerDate.month > olderDate.month) return true;
else if (newerDate.day > olderDate.day) return true; else if (newerDate.day > olderDate.day) return true;
@ -18,7 +19,7 @@ const getDeadlineTime = (deadline) => {
} }
}; };
const statusFilterHandle = (status, challenges, dispatch) => { const statusFilter = (status, challenges, setChallengesFiltered) => {
let result = challenges; let result = challenges;
const date = new Date(); const date = new Date();
const currentDate = { const currentDate = {
@ -48,10 +49,7 @@ const statusFilterHandle = (status, challenges, dispatch) => {
result = challenges; result = challenges;
break; break;
} }
dispatch({ setChallengesFiltered(result);
type: CHALLENGES_ACTION.SET_CHALLENGES_FILTERED,
payload: result,
});
}; };
export default statusFilterHandle; export default statusFilter;

View File

@ -92,17 +92,18 @@ const RENDER_DEADLINE_TIME = (time) => {
} }
return ''; return '';
}; };
const NEXT_PAGE = (elements, pageNr, setPage) => { const NEXT_PAGE = (elements, pageNr, setPageNr) => {
if (pageNr !== CALC_PAGES(elements ? elements : [])) { if (pageNr !== CALC_PAGES(elements ? elements : [])) {
let newPage = pageNr + 1; let newPage = pageNr + 1;
setPage(newPage); setPageNr(newPage);
} }
}; };
const PREVIOUS_PAGE = (pageNr, setPage) => {
const PREVIOUS_PAGE = (pageNr, setPageNr) => {
if (pageNr !== 1) { if (pageNr !== 1) {
let newPage = pageNr - 1; let newPage = pageNr - 1;
setPage(newPage); setPageNr(newPage);
} }
}; };
@ -141,5 +142,5 @@ export {
RENDER_WHEN, RENDER_WHEN,
EVALUATIONS_FORMAT, EVALUATIONS_FORMAT,
PREVIOUS_PAGE, PREVIOUS_PAGE,
NEXT_PAGE, NEXT_PAGE
}; };