instantjob-recruiter-client
Version:
a set of tools for creating an instantjob recruiter react client
307 lines (289 loc) • 9.59 kB
JSX
import React, {Component} from 'react'
import classNames from 'classnames/bind'
import {connect} from 'react-redux'
import {browserHistory} from 'react-router'
import styled from 'styled-components'
import {MdFlag, MdAddCircle, MdFileDownload, MdEdit, MdDone, MdDoneAll, MdSend} from 'react-icons/lib/md'
import FilterableList from 'components/filterable_list/filterable_list'
import {get_filter_on_click} from 'components/filterable_list/make_filter_maker_from_field'
import Button from 'components/utils/button'
import Tag from 'components/utils/tag'
import MissionStatus from 'components/mission_status/mission_status'
import persistent_state from 'common/persistent_state'
import {set_from_array, property_getter, set_count, set_intersection} from 'common/utilities'
import auto_bind from 'common/auto_bind'
import request from 'common/request'
import store from 'common/store'
import moment from 'common/moment'
import {
get_mission_status_elements, mission_period, action_create_deal, action_cancel_drafts,
} from 'common/missions'
import {action_export_field} from 'common/fields'
import {link} from 'common/styles'
import {dismiss_popover, show_popover} from 'actions/display'
import {get_missions_filter_fields, get_exports_fields} from 'selectors/fields'
import {get_initially_selected_recruiters} from 'selectors/missions'
import {get_recruiters} from 'selectors/recruiters'
const cx = classNames.bind(require('styles/filterable_table.scss'))
export const get_initial_state = (props) => ({
exclusive_filters: {
status: set_from_array(['draft', 'sent', 'pending_assignement', 'pending_selection', 'assigned']),
recruiter: set_from_array(props.initially_selected_recruiters),
},
order_by({status}) {
return get_mission_status_elements(status).priority
},
loading_export_missions: {},
ascending: true,
})
export function get_filters_and_deals({deals, fields, workplaces, recruiters, state, setState}) {
const status_filter_maker = FilterableList.make_filter_maker_from_field({
id: 'status',
name: 'Statut',
category: 'exclusive',
value_ids: ['draft', 'pending_selection', 'pending_assignement', 'sent', 'assigned'],
get_value_props(status, selected = false) {
const {name, icon} = get_mission_status_elements(status) || {}
return {
id: status,
name: (
<Tag icon={icon}>
{name}
</Tag>
),
}
},
get_item_values({status}) {
return [status]
},
})
const initial_make_status_filter_props = status_filter_maker.make_filter_props
status_filter_maker.make_filter_props = (args) => ({
...initial_make_status_filter_props(args),
onClick: get_filter_on_click('status', args.setState, ({status}) => {
return get_mission_status_elements(status).priority
})
})
const workplace_filter_maker = FilterableList.make_filter_maker_from_field({
id: 'workplace',
category: 'exclusive',
name: 'Lieu de travail',
value_ids: Object.keys(workplaces),
tolerant: true,
get_value_props(id) {
return workplaces[id]
},
get_item_values({workplace_ids}) {
return workplace_ids || []
},
})
const recruiter_filter_maker = FilterableList.make_filter_maker_from_field({
id: 'recruiter',
category: 'exclusive',
name: 'Recruteur',
value_ids: Object.keys(recruiters),
get_value_props(id) {
const recruiter = recruiters[id] || {}
return {...recruiter, name: recruiter.full_name}
},
get_item_values({recruiter}) {
return [recruiter.id]
},
})
const filter_makers = [
status_filter_maker,
workplace_filter_maker,
recruiter_filter_maker,
...[
{id: 'created_at', category: 'date', name: 'Date de création', get_item_value: property_getter('created_at')},
{id: 'start', category: 'date', name: 'Date de début', get_item_value: property_getter('start')},
{id: 'end', category: 'date', name: 'Date de fin', get_item_value: property_getter('end')},
].map(FilterableList.make_filter_maker_from_field),
...fields.map(FilterableList.make_filter_maker_from_field).filter(Boolean)
]
return {
filters: FilterableList.get_filters(filter_makers, deals, state, setState),
deals: FilterableList.get_filtered_items(
filter_makers,
deals,
state,
),
}
}
class DealsList extends Component {
constructor(props) {
super(props)
this.state = persistent_state.get(props.persistent_state_key, {
...FilterableList.initial_state,
...get_initial_state(props),
})
auto_bind(this)
}
componentWillUnmount() {
persistent_state.store(this.props.persistent_state_key, this.state)
}
set_state(arg) {
this.setState(arg)
}
get_filters_and_deals() {
const {deals, fields, workplaces, recruiters} = this.props
return get_filters_and_deals({deals, fields, workplaces, recruiters, state: this.state, setState: this.set_state})
}
get_statistics({users_count, waiting_count, accepted_count, assigned_count, empty_count, status}) {
const assigned = {count: assigned_count, icon: <MdDoneAll />}
switch (status) {
case 'draft':
return [{count: users_count, icon: <MdEdit />}]
case 'pending_assignement':
case 'sent':
return [
{count: accepted_count, icon: <MdDone />},
{count: waiting_count, icon: "?"},
assigned,
]
case 'pending_selection':
return [
{count: empty_count, icon: <MdSend />},
assigned,
]
case 'assigned':
return [assigned]
default:
}
}
get_selected_count(selected_deal_ids) {
return set_count(set_intersection(selected_deal_ids, set_from_array(this.props.deals.map(property_getter('id')))))
}
render_deal({id, workplace, recruiter, created_at, ...deal}) {
workplace = workplace || {}
return (
<Deal onClick={() => browserHistory.push(`/deals/${id}`)}>
<MissionStatus simple {...deal} />
<Information>
<Top>
<Workplace>
<WorkplaceName>
{workplace.name}
</WorkplaceName>
<WorkplaceAddress>
{workplace.address}
</WorkplaceAddress>
</Workplace>
<Statistics>
{this.get_statistics(deal).filter(property_getter('count')).map(({count, icon}, index) => (
<Statistic key={index}>
{count}
<StatisticIcon>
{icon}
</StatisticIcon>
</Statistic>
))}
</Statistics>
</Top>
<Bottom>
<Period>
{mission_period(deal)}
</Period>
<Creation>
Créée {moment(created_at).calendar().toLowerCase()} par {recruiter.full_name}
</Creation>
</Bottom>
</Information>
</Deal>
)
}
make_export_action({name, id}) {
return ({length}) => ({
onClick: (deal_ids, unselect_deals) => {
unselect_deals()
this.setState({loading_export_missions: {[id]: true}})
action_export_field(id, deal_ids)
.then(() => this.setState({loading_export_missions: {[id]: false}}))
},
label: `Exporter ${name} pour les missions sélectionnées`,
loading: this.state.loading_export_missions[id],
disabled: !length,
})
}
render() {
const {export_fields} = this.props
const {filters, deals} = this.get_filters_and_deals()
return (
<FilterableList
icon={MdFlag}
empty_text={"Aucune mission"}
filters={filters}
secondary_actions={[
({length}) => ({onClick: action_cancel_drafts, label: 'Supprimer les brouillons sélectionnés', disabled: !length}),
...export_fields.map(this.make_export_action),
]}
main_action={{icon: MdAddCircle, onClick: action_create_deal, label: 'Nouvelle Mission', action: true}}
render_item={this.render_deal}
selectable
get_selected_count={this.get_selected_count}
>
{deals}
</FilterableList>
)
}
}
export default connect(
(state) => ({
workplaces: state.missions.workplaces,
recruiters: get_recruiters(state),
fields: get_missions_filter_fields(state),
export_fields: get_exports_fields(state),
agency_id: state.profile.agency.id,
initially_selected_recruiters: get_initially_selected_recruiters(state)
})
)(DealsList)
const Workplace = styled.div`
display: flex;
align-items: baseline;
`
const WorkplaceName = styled.div`
font-size: 18px;
margin-right: 6px;
`
const WorkplaceAddress = styled.div``
const Period = styled.div``
const Creation = styled.div``
const Deal = styled.div`
flex: 1;
${({onClick}) => onClick ? link : ''}
display: flex;
align-items: center;
padding-right: 5px;
`
const Information = styled.div`
flex: 1;
display: flex;
align-items: stretch;
justify-content: space-between;
flex-direction: column;
margin-left: 10px;
height: 50px;
`
const Top = styled.div`
display: flex;
align-items: center;
justify-content: space-between;
`
const Statistics = styled.div`
display: flex;
align-items: center;
`
const Statistic = styled.div`
display: flex;
align-items: center;
margin-left: 10px;
`
const StatisticIcon = styled.div`
margin-left: 3px;
font-size: 16px;
`
const Bottom = styled.div`
display: flex;
align-items: center;
justify-content: space-between;
`