UNPKG

instantjob-recruiter-client

Version:

a set of tools for creating an instantjob recruiter react client

306 lines (288 loc) 10.5 kB
import React, {Component} from 'react' import { MdSortByAlpha, MdCheck, MdPlace, } from 'react-icons/lib/md' import classNames from 'classnames/bind' import FilterableTable from 'components/filterable_table/filterable_table' import UserCard from 'components/user_card' import Filter from 'components/filter' import AddressPicker from 'components/address_picker' import SearchBar from 'components/search_bar' import { map_hash, distance, filter_hash, for_all, array_from_set, empty_set, empty_array, } from 'common/utilities' import { action_add_user_value, action_remove_user_value, action_update_user_comment, action_create_user_field, } from 'common/fields' import {new_users_fuse, make_user_statuses, get_user_status} from 'common/users' import auto_bind from 'common/auto_bind' import store from 'common/store' import persistent_state from 'common/persistent_state' import {tolerant_selector} from 'selectors/base' import {get_user_fields} from 'selectors/fields' import NonExclusiveFieldFilter from 'components/non_exclusive_field_filter' const cx = classNames.bind(require('styles/filterable_table.scss')) const get_users = (_, props) => props.users const get_fuse_users = tolerant_selector( [get_users], (users) => Object.keys(users).map((id) => users[id]).filter((user) => user.full_name != "") ) class UserTable extends Component { constructor(props) { super(props) this.state = { name_filtered_user_ids: [], name_query: "", place: props.place, radius_filter: null, users: props.users, selected_statuses: {...props.initial_selected_statuses}, ...persistent_state.get(props.persistent_state_key), } auto_bind(this) } componentDidMount() { this.mounted = true this.update_distances() this.name_search_bar.set_query(this.state.name_query) } componentWillReceiveProps(props) { let place_has_changed = false if (props.place != this.props.place) { this.setState({place: props.place}) place_has_changed = true this.order_by_distance() } if (place_has_changed || props.users !== this.props.users) { this.update_distances(props.users) } } componentWillUnmount() { this.mounted = false persistent_state.store(this.props.persistent_state_key, this.state) } on_name_query_change(query, results) { if (this.mounted) { this.setState({name_filtered_user_ids: results, name_query: query}) } } update_distances(users) { if (!users) { users = this.props.users } this.setState({users: map_hash(users, (user) => { user = {...user, status: this.get_table_user_status(user)} if (this.state.place) { user.distance = distance(user, { latitude: this.state.place.location.lat, longitude: this.state.place.location.lng, }) } else if (user.distance) { user.distance = null } return user })}) } order_by(order_by, ascending) { if (this.mounted) { this.table.order_by(order_by, ascending) } } order_by_distance() { if (this.mounted) { this.table.order_by('distance', true) } } get_table_user_status(user) { if (user && user.status) { return user.status } else { return get_user_status(user) } } get_forbidden_statuses() { return { ...(this.props.forbidden_statuses || {}), applied: true, } } render() { let fields = filter_hash(this.props.fields, (field) => field.for_entity == 'User') let fake_state = {fields: {values: this.props.values, fields}, users: this.props} let filtered_user_ids = this.state.name_filtered_user_ids filtered_user_ids = filtered_user_ids.filter((id) => this.state.users[id]) if (this.state.radius_filter && this.state.place) { filtered_user_ids = filtered_user_ids.filter((id) => { let user = this.state.users[id] return user.distance && user.distance < this.state.radius_filter * 1000 }) } filtered_user_ids = filtered_user_ids.filter((id) => { let user = this.state.users[id] return !this.get_forbidden_statuses()[this.get_table_user_status(user).status] }) let existing_user_statuses = {} filtered_user_ids.forEach((user_id) => existing_user_statuses[this.state.users[user_id].status.status] = true) if (!empty_array(array_from_set(this.state.selected_statuses).filter((status) => existing_user_statuses[status]))) { filtered_user_ids = filtered_user_ids.filter((id) => { let user = this.state.users[id] return this.state.selected_statuses[this.get_table_user_status(user).status] }) } return ( <FilterableTable get_table_ref={(table) => this.table = table} {...this.props} persistent_state_key={this.props.persistent_state_key ? `${this.props.persistent_state_key}-inner` : ''} fields={fields} items={this.state.users} render_card={(user) => <UserCard selectable={this.props.selectable} selected={this.props.selected[user.id]} {...user} />} filtered_item_ids={filtered_user_ids} on_card_click={this.props.on_user_card_click} add_value={action_add_user_value} remove_value={action_remove_user_value} update_comment={action_update_user_comment} get_item_field_values={(item_id, field_id) => get_user_fields(item_id)(fake_state)[field_id].selected_values} item_type={'user'} filter_value={(value_id) => { let value = this.props.values[value_id] if (value) { return (id) => value.entity_ids[id] } else { return (id) => true } }} > <div className={cx('filterable-table__card-name-arrange')}> <div className={cx('filterable-table__sort-name')} onClick={() => this.table.order_by('full_name')}> <MdSortByAlpha /> </div> <div className={cx('filterable-table__search-bar-container')}> <SearchBar ref={(name_search_bar) => this.name_search_bar = name_search_bar} className={cx('filterable-table__search-bar')} make_fuse={new_users_fuse} items={get_fuse_users({}, this.props)} default_results={Object.keys(this.props.users)} on_query_change={this.on_name_query_change} /> </div> </div> <div className={cx('filterable-table__card-filters')}> {this.props.status_filterable ? ( <NonExclusiveFieldFilter values={ array_from_set(existing_user_statuses).map( (id) => ({id, ...make_user_statuses(this.state.selected_statuses[id] ? 'white' : null)[id], selected: this.state.selected_statuses[id]}) ).filter((status) => !this.get_forbidden_statuses()[status.id]) } name={"Statut"} toggle_filtered_value={(status) => this.setState({selected_statuses: {...this.state.selected_statuses, [status.id]: !this.state.selected_statuses[status.id]}})} search_enabled={false} /> ) : null} <Filter label={"Dernière connexion"} onClick={() => this.table.order_by('last_connection')} /> {this.props.place_filterable ? ( <PlaceFilter active={this.state.place !== null} place_selectable={this.props.place_selectable} onClick={this.props.place_selectable ? null : (() => this.order_by_distance())} on_place_select={(place) => { this.setState({place}) this.update_distances() this.order_by_distance() }} on_clear_place_input={() => { this.setState({place: null}) this.update_distances() }} on_radius_change={(radius_filter) => this.setState({radius_filter})} initial_query={this.state.place ? this.state.place.label : ""} /> ) : null} </div> </FilterableTable> ) } } class PlaceFilter extends Component { constructor(props) { super(props) this.state = { radius: "", } } render() { return ( <Filter className={cx('filterable-table__filter')} active={this.props.active} label={<MdPlace />} onClick={this.props.onClick}> <div className={cx('filterable-table__filter-place')}> {this.props.place_selectable ? ( <div className={cx('filterable-table__filter-place-group')}> <div className={cx('filterable-table__filter-place-label')}> Trier en fonction de la distance à </div> <div className={cx('filterable-table__filter-place-input')}> <AddressPicker {...this.props} /> </div> </div> ) : null} <div className={cx('filterable-table__filter-place-group')}> <div className={cx('filterable-table__filter-place-label')}> Filtrer les candidats au delà de </div> <div className={cx('filterable-table__filter-place-input')}> <input ref={(radius_input) => this.radius_input = radius_input} type="text" placeholder="" className={cx('filterable-table__filter-place-radius-input')} value={this.state.radius} onChange={(event) => { let radius = event.target.value this.setState({radius}) this.props.on_radius_change(radius) }} /> <div className={cx('filterable-table__filter-place-radius-unit')}> km </div> </div> </div> </div> </Filter> ) } } UserTable.defaultProps = { items: {}, fields: {}, values: {}, on_user_card_click: (user_id) => {}, editable: true, selectable: false, place_filterable: true, place_selectable: true, status_filterable: true, place: null, selected: {}, on_select_all: (filtered_item_ids) => {}, store_field_name: (field_id, name) => {}, delete_field: (field_id) => {}, initial_selected_statuses: {}, forbidden_statuses: {}, } export default UserTable