UNPKG

instantjob-recruiter-client

Version:

a set of tools for creating an instantjob recruiter react client

367 lines (346 loc) 10.7 kB
import React, {Component} from 'react' import styled from 'styled-components' import {connect} from 'react-redux' import {MdFlag, MdPhone, MdEmail, MdPerson, MdBusiness} from 'react-icons/lib/md' import {browserHistory} from 'react-router' import LastConnection from 'components/icons/last_connection' import ProfileImage from 'components/profile_image' import TimeAgo from 'components/utils/time_ago' import Percentage from 'components/utils/percentage' import Tag from 'components/utils/tag' import FilterableList from 'components/filterable_list/filterable_list' import {get_filter_on_click} from 'components/filterable_list/make_filter_maker_from_field' import {FilterContent, FilterInput} from 'components/filterable_list/shared' import persistent_state from 'common/persistent_state' import {make_user_statuses, new_users_fuse} from 'common/users' import auto_bind from 'common/auto_bind' import {link, color} from 'common/styles' import { property_getter, distance, set_from_array, set_count, set_intersection, array_from_set, } from 'common/utilities' import moment from 'common/moment' import {get_user_workplaces_count} from 'selectors/user_missions' import {get_workplaces} from 'selectors/workplaces' function make_sort_by_distance(setState) { return (place) => get_filter_on_click(`place_${place.latitude}_${place.longitude}`, setState, (item) => { const user_distance = distance(item, place) if (user_distance === null) { return 1e9 } else { return user_distance } }) } class UsersList extends Component { constructor(props) { super(props) this.state = persistent_state.get(props.persistent_state_key, { ...FilterableList.initial_state, exclusive_filters: { status: { 'mail': true, 'active': true, }, }, order_by({last_connection}) { return last_connection ? -moment(last_connection).valueOf() : 0 }, workplace: props.workplace, radius_filter: null, }) auto_bind(this) } static defaultProps = { users: [], fields: [], clickable: true, innerRef() {}, } componentDidMount() { this.props.innerRef(this) } componentWillUnmount() { persistent_state.store(this.props.persistent_state_key, this.state) } set_state(arg) { this.setState(arg) } order_by_distance() { make_sort_by_distance(this.set_state)(this.state.workplace)() } get_selected_count(selected_user_ids) { return set_count(set_intersection(selected_user_ids, set_from_array(this.props.users.map(property_getter('id'))))) } get_status_filter_maker() { return FilterableList.make_filter_maker_from_field({ id: 'status', name: 'Statut', category: 'exclusive', value_ids: ['active', 'mail', 'rejected', 'inactive', 'blacklisted', 'invited', 'new'], get_value_props(status, selected = false) { const {name, icon} = make_user_statuses(selected ? 'white' : color('black', 'light'))[status] return { id: status, name: ( <Tag icon={icon}> {name} </Tag> ), } }, get_item_values({status: {status}}) { return [status] }, }) } get_missions_count_sort_maker() { return FilterableList.make_sort_maker({ id: 'missions_count', name: ( <Tag icon={<MdFlag />}> Missions réalisées </Tag> ), compare_with: ({missions_count}) => -missions_count, }) } get_profile_rate_sort_maker() { return FilterableList.make_sort_maker({ id: 'profile_rate', name: ( <Tag icon={<MdPerson />}> Remplissage du profil </Tag> ), compare_with: ({profile_rate}) => -profile_rate, }) } get_place_filter_maker() { return { keep_item(item, {workplace, radius_filter}) { return !workplace || !radius_filter || distance(item, workplace) < radius_filter * 1000 }, make_filter_props({setState, state: {workplace, radius_filter}}) { const sort_by_distance = make_sort_by_distance(setState) return { id: 'place', name: 'Distance', content: ( <PlaceFilter place={workplace} radius_filter={radius_filter} setState={setState} sort_by_distance={sort_by_distance} /> ), onClick: workplace && sort_by_distance(workplace) } } } } get_workplace_filter_maker() { const {workplaces, user_workplaces_count} = this.props return FilterableList.make_filter_maker_from_field({ id: 'workplaces', name: 'Lieux où le candidat a travaillé', category: 'exclusive', tolerant: true, value_ids: Object.keys(workplaces), get_value_props(workplace_id) { return workplaces[workplace_id] || {} }, get_item_values({id}) { return array_from_set(user_workplaces_count[id]) }, }) } get_filters_and_users() { const {users, fields} = this.props const name_filter_maker = FilterableList.make_name_filter_maker('full_name') const filter_makers = [ this.get_status_filter_maker(), name_filter_maker, this.get_missions_count_sort_maker(), this.get_profile_rate_sort_maker(), this.get_place_filter_maker(), this.get_workplace_filter_maker(), ...fields.map(FilterableList.make_filter_maker_from_field).filter(Boolean) ] return { filters: FilterableList.get_filters(filter_makers, users, this.state, this.set_state), users: FilterableList.get_filtered_items( filter_makers, users, this.state, ), name_filter: name_filter_maker.make_filter(this.set_state, users, new_users_fuse), } } render_user(user) { const {clickable, user_workplaces_count} = this.props const {id, first_name, last_name, phone_number, postal_code, missions_count, profile_rate, last_connection, email} = user const {workplace} = this.state const user_distance = workplace && distance(user, workplace) return ( <User onClick={clickable && !user.not_clickable ? () => browserHistory.push(`/users/${id}`) : null}> <ProfileImage large {...user} /> <Information> <Top> <Name> {last_name} {first_name} </Name> <Statistics> {[ workplace ? {value: user_workplaces_count[id][workplace.id] || 0, icon: <MdBusiness />} : null, {value: missions_count, icon: <MdFlag />}, {value: <Percentage>{profile_rate}</Percentage>, icon: <MdPerson />}, last_connection ? {value: <TimeAgo>{last_connection}</TimeAgo>, icon: <LastConnection />} : null, ].filter(Boolean).map(({value, icon}, index) => ( <Statistic key={index}> {value} <StatisticIcon> {icon} </StatisticIcon> </Statistic> ))} </Statistics> </Top> <Bottom> <Contacts> {[ {icon: <MdPhone />, value: phone_number}, {icon: <MdEmail />, value: email}, ].map(({icon, value}, index) => value ? ( <Contact key={index}> <ContactIcon> {icon} </ContactIcon> {value} </Contact> ) : null)} </Contacts> <Location> {workplace && user_distance !== null ? `${(user_distance / 1000).toFixed(1)} km` : postal_code} </Location> </Bottom> </Information> </User> ) } render() { const {secondary_actions, main_action, selectable} = this.props const {filters, users, name_filter} = this.get_filters_and_users() return ( <FilterableList icon={MdPerson} empty_text={"Aucun candidat"} filters={filters} name_filter={name_filter} secondary_actions={secondary_actions} main_action={main_action} render_item={this.render_user} get_selected_count={this.get_selected_count} selectable={selectable} > {users} </FilterableList> ) } } const PlaceFilter = ({place, radius_filter, setState, sort_by_distance}) => { function on_place_select({label: address, location: {lat: latitude, lng: longitude}}) { const place = {address, latitude, longitude} setState({place}) sort_by_distance(place)() } function on_clear_place_input() { setState({place: null}) } function on_radius_change(radius_filter) { setState({radius_filter}) } return ( <FilterContent> <FilterInput label="Trier en fonction de la distance à" type='place' on_place_select={on_place_select} on_clear_place_input={on_clear_place_input} initial_query={(place && place.address) || ""} /> <FilterInput label="Filtrer les candidats au delà de (km)" value={radius_filter || ""} onChange={on_radius_change} /> </FilterContent> ) } export default connect( (state) => ({ workplaces: get_workplaces(state), user_workplaces_count: get_user_workplaces_count(state), }) )(UsersList) const User = 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 Name = styled.div` font-size: 18px; ` 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; ` const Contacts = styled.div` display: flex; align-items: center; ` const Contact = styled.div` display: flex; margin-right: 10px; ` const ContactIcon = styled.div` margin-right: 5px; ` const Location = styled.div`` const Status = styled.div` display: flex; align-items: center; ` const StatusIcon = styled.div` padding: 5px; font-size: 16px; `