instantjob-recruiter-client
Version:
a set of tools for creating an instantjob recruiter react client
306 lines (288 loc) • 10.5 kB
JSX
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