diff --git a/common/app/routes/challenges/components/map/Block.jsx b/common/app/routes/challenges/components/map/Block.jsx index ab5dec96ae..b26ffdc64c 100644 --- a/common/app/routes/challenges/components/map/Block.jsx +++ b/common/app/routes/challenges/components/map/Block.jsx @@ -1,97 +1,76 @@ import React, { PropTypes } from 'react'; -import { Link } from 'react-router'; import { connect } from 'react-redux'; +import { createSelector } from 'reselect'; import FA from 'react-fontawesome'; import PureComponent from 'react-pure-render/component'; import { Panel } from 'react-bootstrap'; -import classnames from 'classnames'; -import { updateCurrentChallenge } from '../../redux/actions'; +import Challenge from './Challenge.jsx'; +import { toggleThisPanel } from '../../redux/actions'; -const dispatchActions = { updateCurrentChallenge }; +const dispatchActions = { toggleThisPanel }; +const mapStateToProps = createSelector( + (_, props) => props.dashedName, + state => state.entities.block, + state => state.entities.challenge, + (state, props) => state.challengesApp.mapUi[props.dashedName], + (dashedName, blockMap, challengeMap, isOpen) => { + const block = blockMap[dashedName]; + return { + isOpen, + dashedName, + title: block.title, + time: block.time, + challenges: block.challenges + }; + } +); export class Block extends PureComponent { + constructor(...props) { + super(...props); + this.handleSelect = this.handleSelect.bind(this); + } static displayName = 'Block'; static propTypes = { title: PropTypes.string, dashedName: PropTypes.string, time: PropTypes.string, + isOpen: PropTypes.bool, challenges: PropTypes.array, - updateCurrentChallenge: PropTypes.func + toggleThisPanel: PropTypes.func }; - renderChallenges(blockName, challenges, updateCurrentChallenge) { + handleSelect(eventKey, e) { + e.preventDefault(); + this.props.toggleThisPanel(eventKey); + } + + renderChallenges(challenges) { if (!Array.isArray(challenges) || !challenges.length) { return
- { title } - { - isRequired ? - * : - '' - } -
- ); - } - return ( -- - updateCurrentChallenge(challenge) } - > - { title } - complete - { - isRequired ? - * : - '' - } - - -
- ); - }); + return challenges.map(dashedName => ( ++ { title } + { this.renderRequired(isRequired) } +
+ ); + } + + render() { + const { + title, + dashedName, + block, + isLocked, + isRequired, + isCompleted, + challenge, + updateCurrentChallenge + } = this.props; + const challengeClassName = classnames({ + 'text-primary': true, + 'padded-ionic-icon': true, + 'negative-15': true, + 'challenge-title': true, + 'ion-checkmark-circled faded': !isLocked && isCompleted, + 'ion-ios-circle-outline': !isLocked && !isCompleted, + 'ion-locked': isLocked, + disabled: isLocked + }); + if (isLocked) { + return this.renderLocked(title, isRequired, challengeClassName); + } + return ( ++ + updateCurrentChallenge(challenge) }> + { title } + { this.renderCompleted(isCompleted, isLocked) } + { this.renderRequired(isRequired) } + + +
+ ); + } +} + +export default connect(mapStateToProps, bindableActions)(Challenge); diff --git a/common/app/routes/challenges/components/map/Full-Stack.jsx b/common/app/routes/challenges/components/map/Full-Stack.jsx index 247c61ca95..f880b7de23 100644 --- a/common/app/routes/challenges/components/map/Full-Stack.jsx +++ b/common/app/routes/challenges/components/map/Full-Stack.jsx @@ -15,26 +15,31 @@ const nonprofitProjects = { challenges: [ { title: 'Greenfield Nonprofit Project #1', + dashedName: 'greenfield-1', isLocked: true, isRequired: true }, { title: 'Greenfield Nonprofit Project #2', + dashedName: 'greenfield-2', isLocked: true, isRequired: true }, { title: 'Legacy Code Nonprofit Project #1', + dashedName: 'legacy-1', isLocked: true, isRequired: true }, { title: 'Legacy Code Nonprofit Project #2', + dashedName: 'legacy-2', isLocked: true, isRequired: true }, { title: 'Claim your Full Stack Development Certification', + dashedName: 'claim-full-stack', isLocked: true } ] diff --git a/common/app/routes/challenges/components/map/Header.jsx b/common/app/routes/challenges/components/map/Header.jsx index 61543bb334..7f0c26e657 100644 --- a/common/app/routes/challenges/components/map/Header.jsx +++ b/common/app/routes/challenges/components/map/Header.jsx @@ -1,20 +1,24 @@ import React, { PropTypes } from 'react'; +import { connect } from 'react-redux'; import PureComponent from 'react-pure-render/component'; import { InputGroup, FormControl, Button, Row } from 'react-bootstrap'; import classnames from 'classnames'; +import { clearFilter, updateFilter } from '../../redux/actions'; +const ESC = 27; const clearIcon = ; const searchIcon = ; -const ESC = 27; -export default class Header extends PureComponent { +const bindableActions = { clearFilter, updateFilter }; +const mapStateToProps = state => ({ filter: state.challengesApp.filter }); +export class Header extends PureComponent { constructor(...props) { super(...props); this.handleKeyDown = this.handleKeyDown.bind(this); } static displayName = 'MapHeader'; static propTypes = { - filter: PropTypes.string, clearFilter: PropTypes.func, + filter: PropTypes.string, updateFilter: PropTypes.func }; @@ -79,3 +83,4 @@ export default class Header extends PureComponent { ); } } +export default connect(mapStateToProps, bindableActions)(Header); diff --git a/common/app/routes/challenges/components/map/Map.jsx b/common/app/routes/challenges/components/map/Map.jsx index 10a4571224..a7d2371a2d 100644 --- a/common/app/routes/challenges/components/map/Map.jsx +++ b/common/app/routes/challenges/components/map/Map.jsx @@ -2,141 +2,45 @@ import React, { PropTypes } from 'react'; import { compose } from 'redux'; import { contain } from 'redux-epic'; import { connect } from 'react-redux'; -import { createSelector } from 'reselect'; import PureComponent from 'react-pure-render/component'; import MapHeader from './Header.jsx'; import SuperBlock from './Super-Block.jsx'; -import FullStack from './Full-Stack.jsx'; -import CodingPrep from './Coding-Prep.jsx'; -import { - clearFilter, - fetchChallenges, - toggleThisPanel, - updateFilter -} from '../../redux/actions'; - -const bindableActions = { - clearFilter, - fetchChallenges, - toggleThisPanel, - updateFilter -}; -const superBlocksSelector = createSelector( - state => state.challengesApp.superBlocks, - state => state.entities.superBlock, - state => state.entities.block, - state => state.entities.challenge, - (superBlocks, superBlockMap, blockMap, challengeMap) => { - if (!superBlockMap || !blockMap || !challengeMap) { - return { - superBlocks: [] - }; - } - return { - superBlocks: superBlocks - .map(superBlockName => superBlockMap[superBlockName]) - .map(superBlock => ({ - ...superBlock, - blocks: superBlock.blocks - .map(blockName => blockMap[blockName]) - .map(block => ({ - ...block, - challenges: block.challenges - .map(dashedName => challengeMap[dashedName]) - })) - })) - }; - } -); - -const mapStateToProps = createSelector( - superBlocksSelector, - state => state.challengesApp.filter, - state => state.challengesApp.map, - ({ superBlocks }, filter, mapUi) => { - return { - superBlocks, - filter, - mapUi - }; - } -); +import { fetchChallenges } from '../../redux/actions'; +const bindableActions = { fetchChallenges }; +const mapStateToProps = state => ({ + superBlocks: state.challengesApp.superBlocks +}); const fetchOptions = { fetchAction: 'fetchChallenges', isPrimed({ superBlocks }) { return Array.isArray(superBlocks) && superBlocks.length > 1; } }; - export class ShowMap extends PureComponent { static displayName = 'Map'; - static propTypes = { - clearFilter: PropTypes.func, - filter: PropTypes.string, - superBlocks: PropTypes.array, - updateFilter: PropTypes.func, - mapUi: PropTypes.object - }; + static propTypes = { superBlocks: PropTypes.array }; - renderSuperBlocks( - superBlocks, - updateCurrentChallenge, - mapUi, - toggleThisPanel - ) { + renderSuperBlocks(superBlocks) { if (!Array.isArray(superBlocks) || !superBlocks.length) { return