Home Identifier Source Repository

src/screens/graduation-status.js

import React, {Component, PropTypes} from 'react'
import cx from 'classnames'
import Immutable from 'immutable'

import debug from 'debug'
import includes from 'lodash/collection/includes'
import difference from 'lodash/array/difference'
import union from 'lodash/array/union'
import values from 'lodash/object/values'

import pathToOverride from '../lib/path-to-override'

import AreaOfStudyGroup from '../components/area-of-study-group'
import Button from '../components/button'
import Student from '../models/student'
import StudentSummary from '../components/student-summary'
import * as areaTypeConstants from '../models/area-types'

import actions from '../flux/student-actions'

import './graduation-status.scss'

const log = debug('gobbldygook:component:render')

export default class GraduationStatus extends Component {
	static propTypes = {
		allAreas: PropTypes.instanceOf(Immutable.List),
		courses: PropTypes.instanceOf(Immutable.List),
		coursesLoaded: PropTypes.bool.isRequired,
		isHidden: PropTypes.bool,
		student: PropTypes.instanceOf(Student).isRequired,
	}

	constructor(props) {
		super(props)
		this.state = {
			graduatability: false,
			areaDetails: Immutable.OrderedMap(),
			allAreas: Immutable.List(),
			showAreaPickerFor: Immutable.Map(),
		}
	}

	componentWillMount() {
		this.componentWillReceiveProps(this.props)
	}

	async componentWillReceiveProps(nextProps) {
		const {canGraduate, details} = await nextProps.student.graduatability
		this.setState({
			graduatability: canGraduate,
			areaDetails: details,
		})
	}

	initiateAddArea = ({ev, type}) => {
		ev.preventDefault()
		this.setState(state => ({
			showAreaPickerFor: state.showAreaPickerFor.set(type, true),
		}))
	}

	endAddArea = ({ev, type}) => {
		ev.preventDefault()
		this.setState(state => ({
			showAreaPickerFor: state.showAreaPickerFor.set(type, false),
		}))
	}

	addAreaToStudent = ({ev, area}) => {
		ev.preventDefault()
		actions.addArea(this.props.student.id, area)
	}

	addOverrideToStudent = ({ev, path}) => {
		ev.preventDefault()
		const codifiedPath = pathToOverride(path)
		actions.setOverride(this.props.student.id, {[codifiedPath]: true})
	}

	removeOverrideFromStudent = ({ev, path}) => {
		ev.preventDefault()
		const codifiedPath = pathToOverride(path)
		actions.setOverride(this.props.student.id, {[codifiedPath]: false})
	}

	toggleOverrideOnStudent = ({ev, path}) => {
		ev.preventDefault()
		const codifiedPath = pathToOverride(path)

		if (this.props.student.overrides.has(codifiedPath)) {
			actions.removeOverride(this.props.student.id, codifiedPath)
		}
		else {
			actions.setOverride(this.props.student.id, {[codifiedPath]: true})
		}
	}

	removeAreaFromStudent = ({ev, areaId}) => {
		ev.preventDefault()
		actions.removeArea(this.props.student.id, areaId)
	}

	render() {
		log('GraduationStatus#render')
		const student = this.props.student

		if (!student) {
			return null
		}

		const allAreasGrouped = this.props.allAreas.groupBy(a => a.type)

		const sections = this.props.student.studies
			// group the studies by their type
			.groupBy(study => study.type.toLowerCase())
			// pull the results out of state, or use a mutable version from props
			.map(areas => areas.map(area => this.state.areaDetails.get(area.id) || area.toObject()))
			// then render them
			.map((areas, areaType) =>
				<AreaOfStudyGroup key={areaType}
					addArea={this.addAreaToStudent}
					addOverride={this.addOverrideToStudent}
					allAreas={allAreasGrouped.get(areaType) || Immutable.List()}
					areas={areas ? areas.toList() : Immutable.List()}
					courses={this.props.courses}
					coursesLoaded={this.props.coursesLoaded}
					endAddArea={this.endAddArea}
					initiateAddArea={this.initiateAddArea}
					removeArea={this.removeAreaFromStudent}
					removeOverride={this.removeOverrideFromStudent}
					showAreaPicker={this.state.showAreaPickerFor.get(areaType)}
					studentId={this.props.student.id}
					toggleOverride={this.toggleOverrideOnStudent}
					type={areaType}
				/>)
			.toArray()

		const usedAreaTypes = this.props.student.studies
			.map(a => a.get('type'))
			.toSet()
			.toArray()

		const allAreaTypes = values(areaTypeConstants)
		const areaTypesToShowButtonsFor = union(usedAreaTypes, [...this.state.showAreaPickerFor.filter(a => a === true).keys()])
		const unusedTypes = difference(allAreaTypes, areaTypesToShowButtonsFor)

		const addUnusedAreaButtonList = (
			<section className='unused-areas-of-study'>
				<span className='unused-areas-title'>Add: </span>
				<span className='unused-areas-buttons'>
					{unusedTypes.map(type => (
						<Button key={type}
							className='add-unused-area-of-study'
							onClick={ev => this.initiateAddArea({ev, type})}
							type='flat'>
							{type}
						</Button>
					))}
				</span>
			</section>
		)

		const unusedTypesToShow = this.state.showAreaPickerFor
			.filter((toShow, type) => toShow === true && !includes(usedAreaTypes, type))
			.map((toShow, type) =>
				<AreaOfStudyGroup key={type}
					addArea={this.addAreaToStudent}
					addOverride={this.addOverrideToStudent}
					allAreas={allAreasGrouped.get(type) || Immutable.List()}
					areas={Immutable.List()}
					endAddArea={this.endAddArea}
					initiateAddArea={this.initiateAddArea}
					removeArea={this.removeAreaFromStudent}
					showAreaPicker={toShow}
					toggleOverride={this.toggleOverrideOnStudent}
					type={type}
				/>)
			.toArray()

		return (
			<section className={cx('graduation-status', {'is-hidden': this.props.isHidden})}>
				<StudentSummary
					courses={this.props.courses}
					coursesLoaded={this.props.coursesLoaded}
					student={student}
					graduatability={this.state.graduatability}
				/>

				{sections}

				{unusedTypesToShow}

				{unusedTypes.length
					? addUnusedAreaButtonList
					: null}
			</section>
		)
	}
}