UNPKG

instantjob-recruiter-client

Version:

a set of tools for creating an instantjob recruiter react client

312 lines (287 loc) 9.14 kB
import React, {Component} from 'react' import classNames from 'classnames/bind' import {connect} from 'react-redux' import {MdArrowBack, MdKeyboardArrowLeft, MdKeyboardArrowRight} from 'react-icons/lib/md' import Calendar from '../generic_calendar' import Button from 'components/utils/button' import TimePickers from './time_pickers' import {color} from '../../common/constants' import { array_from_hash, map_hash, set_from_array, array_from_set, range, empty_set, for_all, filter_hash, flatten_array, create_hash, } from '../../common/utilities' import request from '../../common/request' import store from '../../common/store' import auto_bind from '../../common/auto_bind' import moment from '../../common/moment' import {events_difference} from '../../common/calendar' import event_system from '../../common/event_system' import {get_user_events} from '../../selectors/calendar' import {get_user_missions_events} from '../../selectors/missions' import {toggle_item_id} from '../../reducers/base' const cx = classNames.bind(require('../../styles/input_calendar.scss')) const chunks_count_in_hour = 4 const chunk_length_in_minutes = 60 / chunks_count_in_hour const chunks_count_in_day = 24 * chunks_count_in_hour const day_id = (day) => day.format("YYYY-MM-DD") const get_periods = (chunk_set) => { let periods = [], period_started, start = null let add_period = (end) => periods.push({start, end}) range(chunks_count_in_day).forEach((chunk) => { if (chunk_set[chunk]) { if (!period_started) { start = chunk period_started = true } } else { if (period_started) { add_period(chunk - 1) period_started = false } } }) if (period_started) { add_period(chunks_count_in_day - 1) } return periods } const get_start_end = (day, start_id, end_id) => { let start_of_day = moment(day) let start = start_id, end = end_id if (start > end) { start = end end = start_id } return { start: moment(start_of_day).add(start * chunk_length_in_minutes, 'minutes'), end: moment(start_of_day).add((end + 1) * chunk_length_in_minutes, 'minutes'), } } class AgencyEditUserCalendarStatic extends Component { constructor(props) { super(props) auto_bind(this) let active = {} props.agency_events.forEach((event) => { let current = moment(event.start) while (current.isBefore(event.end)) { let id = day_id(current) if (!active[id]) { active[id] = {} } active[id][(60 * current.hour() + current.minutes()) / chunk_length_in_minutes] = true current.add(chunk_length_in_minutes, 'minutes') } }) this.state = { active, mode: "month", week: 0, } } componentDidMount() { event_system.register('dismiss_popover', this.update_user_events) } componentDidUpdate() { if (this.should_scroll_to_week) { this.calendar.scroll_to_week(this.should_scroll_to_week) this.should_scroll_to_week = false } } on_day_click(date) { let id = day_id(date) if (this.is_full_day(id)) { this.setState({ active: { ...this.state.active, [id]: this.empty_day(), } }) } else if (this.is_empty_day(id)) { this.setState({ active: { ...this.state.active, [id]: this.full_day(), }, }) } else { this.on_navigate_to_week(date.endOf('week').diff(moment(), 'week')) } } full_day() { return set_from_array(range(chunks_count_in_day)) } empty_day() { return false } is_empty_day(id) { let day = this.state.active[id] return !day || empty_set(day) } is_full_day(id) { let day = this.state.active[id] return day && for_all(range(chunks_count_in_day), (chunk) => this.state.active[id][chunk]) } on_create_days_period(start, end, paint = true) { let day_ids = [], current = moment(start), condition = paint ? this.is_empty_day : this.is_full_day while (current.isBefore(end)) { let id = day_id(current) if (condition(id)) { day_ids.push(id) } current.add(1, 'day') } this.setState({active: { ...this.state.active, ...map_hash(set_from_array(day_ids), paint ? this.full_day : this.empty_day), }}) } on_create_chunks_period(date, chunks, paint = true) { let id = day_id(date) this.setState(({active}) => ({active: { ...active, [id]: { ...(active[id] || {}), ...create_hash(chunks, () => paint) }, }})) } get_day_events() { return array_from_set(this.state.active).filter(this.is_full_day).map((day) => ({ start: moment(day), end: moment(day).endOf('day'), full_days: true, })) } get_hour_events() { let partially_busy_days = array_from_set(this.state.active).filter((id) => !this.is_full_day(id) && !this.is_empty_day(id)) let events = [] let add_events = (day, active_chunks) => { get_periods(active_chunks).forEach(({start, end}) => { events.push({ ...get_start_end(day, start, end), full_days: false, }) }) } partially_busy_days.forEach((day) => add_events( day, this.state.active[day] )) return events } update_user_events() { this.props.update_user_events([...this.get_day_events(), ...this.get_hour_events()]) } on_navigate_to_week(week) { this.setState({week, mode: "week"}) } on_navigate_to_month() { this.setState({mode: "month"}) this.should_scroll_to_week = this.state.week } on_next_week() { this.setState({week: this.state.week + 1}) } on_previous_week() { this.setState({week: this.state.week - 1}) } get_start_of_week() { return moment().startOf('week').add(this.state.week, 'weeks') } render() { const {user_events, mission_events} = this.props const events = { [color('important', 'light')]: user_events, [color('primary')]: mission_events, } switch (this.state.mode) { case "week": let start_of_week = this.get_start_of_week() return ( <div className={cx('input-calendar', 'input-calendar_week')}> <div className={cx('input-calendar__nav')}> <Button className={cx('input-calendar__nav-back')} back onClick={this.on_navigate_to_month} > Retour vue {start_of_week.format(`MMMM${moment().isSame(start_of_week, 'year') ? '' : ' YYYY'}`)} </Button> <div className={cx('input-calendar__nav-weeks')}> <div onClick={this.on_previous_week} className={cx('input-calendar__nav-weeks-control')}> <MdKeyboardArrowLeft /> </div> <div className={cx('input-calendar__nav-weeks-current')}> Semaine {start_of_week.week()} </div> <div onClick={this.on_next_week} className={cx('input-calendar__nav-weeks-control')}> <MdKeyboardArrowRight /> </div> </div> </div> <TimePickers events={events} days={range(7).map((day) => { let date = moment(start_of_week).add(day, 'days') return { date, chunks: this.state.active[day_id(date)] || {}, } })} drag_color={color('attention')} full_day_color={color('important')} flip={this.on_create_chunks_period} /> </div> ) case "month": return ( <div className={cx('input-calendar', 'input-calendar_month')}> <Calendar ref={(calendar) => this.calendar = calendar} events={{ ...events, [color('attention')]: this.get_hour_events(), [color('important')]: this.get_day_events(), }} on_click={this.on_day_click} drag_color={color('important')} on_drag_and_drop={this.on_create_days_period} on_navigate_to_week={this.on_navigate_to_week} /> </div> ) } } } const AgencyEditUserCalendar = connect( (state, {user_id}) => { const {user: user_events, agency: agency_events} = get_user_events(user_id)(state) const mission_events = get_user_missions_events(user_id)(state) return { user_events, agency_events, mission_events, } }, (dispatch, {user_id}) => { return { update_user_events(new_events) { let state = store.getState() const {agency: old_events} = get_user_events(user_id)(state) let {remove, add} = events_difference(old_events, new_events) request.put(`users/${user_id}/events`, {remove_events: (remove || []).map((event) => event.id), events: add}) }, } } )(AgencyEditUserCalendarStatic) export default AgencyEditUserCalendar