diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 00000000..ba76ad84 Binary files /dev/null and b/.DS_Store differ diff --git a/components/LevelThermometer.js b/components/LevelThermometer.js index 1de6202f..c2d0b238 100644 --- a/components/LevelThermometer.js +++ b/components/LevelThermometer.js @@ -1,7 +1,7 @@ // @flow import * as d3 from 'd3' -import { pointsToLevels, categoryPointsFromMilestoneMap, categoryColorScale, categoryIds, maxLevel } from '../constants' +import { pointsToLevels, categoryPointsFromMilestoneMap, categoryColorScale, maxLevel } from '../constants' import React from 'react' import type { MilestoneMap } from '../constants' @@ -71,7 +71,7 @@ class LevelThermometer extends React.Component { + "z"; } render() { - let categoryPoints = categoryPointsFromMilestoneMap(this.props.milestoneByTrack) + let categoryPoints = categoryPointsFromMilestoneMap(this.props.milestoneByTrack, this.props.trackIds, this.props.tracks) let lastCategoryIndex = 0 categoryPoints.forEach((categoryPoint, i) => { if (categoryPoint.points) lastCategoryIndex = i diff --git a/components/NightingaleChart.js b/components/NightingaleChart.js index acbc28f7..52f67a57 100644 --- a/components/NightingaleChart.js +++ b/components/NightingaleChart.js @@ -2,7 +2,7 @@ import React from 'react' import * as d3 from 'd3' -import { trackIds, milestones, tracks, categoryColorScale } from '../constants' +import { milestones, categoryColorScale } from '../constants' import type { TrackId, Milestone, MilestoneMap } from '../constants' const width = 400 @@ -33,8 +33,8 @@ class NightingaleChart extends React.Component { this.arcFn = d3.arc() .innerRadius(milestone => this.radiusScale(milestone)) .outerRadius(milestone => this.radiusScale(milestone) + this.radiusScale.bandwidth()) - .startAngle(- Math.PI / trackIds.length) - .endAngle(Math.PI / trackIds.length) + .startAngle(- Math.PI / this.props.trackIds.length) + .endAngle(Math.PI / this.props.trackIds.length) .padAngle(Math.PI / 200) .padRadius(.45 * width) .cornerRadius(2) @@ -64,10 +64,10 @@ class NightingaleChart extends React.Component { `} - {trackIds.map((trackId, i) => { + {this.props.trackIds.map((trackId, i) => { const isCurrentTrack = trackId == this.props.focusedTrackId return ( - + {arcMilestones.map((milestone) => { const isCurrentMilestone = isCurrentTrack && milestone == currentMilestoneId const isMet = this.props.milestoneByTrack[trackId] >= milestone || milestone == 0 @@ -77,14 +77,14 @@ class NightingaleChart extends React.Component { className={'track-milestone ' + (isMet ? 'is-met ' : ' ') + (isCurrentMilestone ? 'track-milestone-current' : '')} onClick={() => this.props.handleTrackMilestoneChangeFn(trackId, milestone)} d={this.arcFn(milestone)} - style={{fill: isMet ? categoryColorScale(tracks[trackId].category) : undefined}} /> + style={{fill: isMet ? categoryColorScale(this.props.tracks[trackId].category) : undefined}} /> ) })} this.props.handleTrackMilestoneChangeFn(trackId, 0)} /> diff --git a/components/PointSummaries.js b/components/PointSummaries.js index 61934b57..4d04deac 100644 --- a/components/PointSummaries.js +++ b/components/PointSummaries.js @@ -1,16 +1,17 @@ // @flow -import { pointsToLevels, milestoneToPoints, trackIds, totalPointsFromMilestoneMap, maxLevel } from '../constants' +import { pointsToLevels, milestoneToPoints, totalPointsFromMilestoneMap, maxLevel } from '../constants' import type { MilestoneMap } from '../constants' import React from 'react' type Props = { - milestoneByTrack: MilestoneMap + milestoneByTrack: MilestoneMap, + trackIds: Array } class PointSummaries extends React.Component { render() { - const totalPoints = totalPointsFromMilestoneMap(this.props.milestoneByTrack) + const totalPoints = totalPointsFromMilestoneMap(this.props.milestoneByTrack, this.props.trackIds) let currentLevel, nextLevel diff --git a/components/SnowflakeApp.js b/components/SnowflakeApp.js index 6a31b740..b3af7212 100644 --- a/components/SnowflakeApp.js +++ b/components/SnowflakeApp.js @@ -1,16 +1,15 @@ // @flow +import React from 'react' import TrackSelector from '../components/TrackSelector' import NightingaleChart from '../components/NightingaleChart' import KeyboardListener from '../components/KeyboardListener' import Track from '../components/Track' import Wordmark from '../components/Wordmark' import LevelThermometer from '../components/LevelThermometer' -import { eligibleTitles, trackIds, milestones, milestoneToPoints } from '../constants' import PointSummaries from '../components/PointSummaries' -import type { Milestone, MilestoneMap, TrackId } from '../constants' -import React from 'react' import TitleSelector from '../components/TitleSelector' +import { getTrackIds, eligibleTitles, milestones } from '../constants'; type SnowflakeAppState = { milestoneByTrack: MilestoneMap, @@ -19,9 +18,9 @@ type SnowflakeAppState = { focusedTrackId: TrackId, } -const hashToState = (hash: String): ?SnowflakeAppState => { +const hashToState = (hash: String, trackIds): ?SnowflakeAppState => { if (!hash) return null - const result = defaultState() + const result = defaultState(trackIds) const hashValues = hash.split('#')[1].split(',') if (!hashValues) return null trackIds.forEach((trackId, i) => { @@ -45,59 +44,38 @@ const coerceMilestone = (value: number): Milestone => { } } -const emptyState = (): SnowflakeAppState => { +const emptyState = (trackIds): SnowflakeAppState => { return { name: '', title: '', - milestoneByTrack: { - 'MOBILE': 0, - 'WEB_CLIENT': 0, - 'FOUNDATIONS': 0, - 'SERVERS': 0, - 'PROJECT_MANAGEMENT': 0, - 'COMMUNICATION': 0, - 'CRAFT': 0, - 'INITIATIVE': 0, - 'CAREER_DEVELOPMENT': 0, - 'ORG_DESIGN': 0, - 'WELLBEING': 0, - 'ACCOMPLISHMENT': 0, - 'MENTORSHIP': 0, - 'EVANGELISM': 0, - 'RECRUITING': 0, - 'COMMUNITY': 0 - }, - focusedTrackId: 'MOBILE' + milestoneByTrack: trackIds.reduce((acc, trackId) => { + acc[trackId] = 0 + return acc + }, {}), + focusedTrackId: trackIds[0] } } -const defaultState = (): SnowflakeAppState => { +const getRandomIntInclusive = (min, max) => { + min = Math.ceil(min); + max = Math.floor(max); + return Math.floor(Math.random() * (max - min + 1)) + min; +} + +const defaultState = (trackIds): SnowflakeAppState => { return { name: 'Soapbox Simon', title: 'Senior Engineer', - milestoneByTrack: { - 'MOBILE': 1, - 'WEB_CLIENT': 2, - 'FOUNDATIONS': 3, - 'SERVERS': 1, - 'PROJECT_MANAGEMENT': 3, - 'COMMUNICATION': 1, - 'CRAFT': 1, - 'INITIATIVE': 1, - 'CAREER_DEVELOPMENT': 3, - 'ORG_DESIGN': 2, - 'WELLBEING': 0, - 'ACCOMPLISHMENT': 2, - 'MENTORSHIP': 2, - 'EVANGELISM': 2, - 'RECRUITING': 3, - 'COMMUNITY': 0 - }, - focusedTrackId: 'MOBILE' + milestoneByTrack: trackIds.reduce((acc, trackId) => { + acc[trackId] = getRandomIntInclusive(0, 3) + + return acc + }, {}), + focusedTrackId: trackIds[0] } } -const stateToHash = (state: SnowflakeAppState) => { +const stateToHash = (state: SnowflakeAppState, trackIds) => { if (!state || !state.milestoneByTrack) return null const values = trackIds.map(trackId => state.milestoneByTrack[trackId]).concat(encodeURI(state.name), encodeURI(state.title)) return values.join(',') @@ -108,20 +86,22 @@ type Props = {} class SnowflakeApp extends React.Component { constructor(props: Props) { super(props) - this.state = emptyState() + this.state = {...props.constants}; + this.state.trackIds = getTrackIds(this.state.tracks); + this.state.user = emptyState(this.state.trackIds); } componentDidUpdate() { - const hash = stateToHash(this.state) + const hash = stateToHash(this.state.user, this.state.trackIds) if (hash) window.location.replace(`#${hash}`) } componentDidMount() { - const state = hashToState(window.location.hash) + const state = hashToState(window.location.hash, this.state.trackIds) if (state) { this.setState(state) } else { - this.setState(defaultState()) + this.setState({ ...this.state, user: defaultState(this.state.trackIds) }) } } @@ -168,28 +148,34 @@ class SnowflakeApp extends React.Component {
this.handleTrackMilestoneChange(track, milestone)} />
{ increaseFocusedMilestoneFn={this.shiftFocusedTrackMilestoneByDelta.bind(this, 1)} decreaseFocusedMilestoneFn={this.shiftFocusedTrackMilestoneByDelta.bind(this, -1)} /> this.handleTrackMilestoneChange(track, milestone)} />
@@ -207,7 +195,7 @@ class SnowflakeApp extends React.Component { 👩‍🔬 Learn about our Soapbox growth framework.
@@ -215,30 +203,31 @@ class SnowflakeApp extends React.Component { } handleTrackMilestoneChange(trackId: TrackId, milestone: Milestone) { - const milestoneByTrack = this.state.milestoneByTrack + const milestoneByTrack = this.state.user.milestoneByTrack milestoneByTrack[trackId] = milestone - const titles = eligibleTitles(milestoneByTrack) + const titles = eligibleTitles(milestoneByTrack, this.state.titles, this.state.trackIds) const title = titles.indexOf(this.state.title) === -1 ? titles[0] : this.state.title - this.setState({ milestoneByTrack, focusedTrackId: trackId, title }) + this.setState({ user: { ...this.state.user, milestoneByTrack, focusedTrackId: trackId, title } }) } shiftFocusedTrack(delta: number) { - let index = trackIds.indexOf(this.state.focusedTrackId) + const trackIds = this.state.trackIds + let index = trackIds.indexOf(this.state.user.focusedTrackId) index = (index + delta + trackIds.length) % trackIds.length - const focusedTrackId = trackIds[index] - this.setState({ focusedTrackId }) + const focusedTrackId = this.state.trackIds[index] + this.setState({ user: { ...this.state.user, focusedTrackId } }) } setFocusedTrackId(trackId: TrackId) { - let index = trackIds.indexOf(trackId) - const focusedTrackId = trackIds[index] - this.setState({ focusedTrackId }) + let index = this.state.trackIds.indexOf(trackId) + const focusedTrackId = this.state.trackIds[index] + this.setState({ user: { ...this.state.user, focusedTrackId } }) } shiftFocusedTrackMilestoneByDelta(delta: number) { - let prevMilestone = this.state.milestoneByTrack[this.state.focusedTrackId] + let prevMilestone = this.state.user.milestoneByTrack[this.state.focusedTrackId] let milestone = prevMilestone + delta if (milestone < 0) milestone = 0 if (milestone > 5) milestone = 5 @@ -246,9 +235,9 @@ class SnowflakeApp extends React.Component { } setTitle(title: string) { - let titles = eligibleTitles(this.state.milestoneByTrack) + let titles = eligibleTitles(this.state.user.milestoneByTrack, this.state.trackIds) title = titles.indexOf(title) == -1 ? titles[0] : title - this.setState({ title }) + this.setState({ user: { ...this.state.user, title } }) } } diff --git a/components/TitleSelector.js b/components/TitleSelector.js index ec9b1d2e..1ac0fb9c 100644 --- a/components/TitleSelector.js +++ b/components/TitleSelector.js @@ -7,12 +7,13 @@ import type { MilestoneMap } from '../constants' type Props = { milestoneByTrack: MilestoneMap, currentTitle: String, - setTitleFn: (string) => void + setTitleFn: (string) => void, + trackIds: Array } class TitleSelector extends React.Component { render() { - const titles = eligibleTitles(this.props.milestoneByTrack) + const titles = eligibleTitles(this.props.milestoneByTrack, this.props.titles, this.props.trackIds) return