UNPKG

instantjob-recruiter-client

Version:

a set of tools for creating an instantjob recruiter react client

443 lines (427 loc) 16.9 kB
import {tolerant_selector} from 'selectors/base' import {date_to_id} from 'selectors/calendar' import {get_users} from 'selectors/users' import {get_recruiters} from 'selectors/recruiters' import { get_fields, get_fields_by_mission, get_mission_fields_by_deal, get_fields_by_proposal, get_mission_values_by_mission, get_values_by_field, get_fields_by_deal, } from 'selectors/fields' import moment from 'common/moment' import { make_memoized, array_from_hash, array_from_set, split_hash, filter_hash, map_hash, group_hash_by_value, for_all, flatten_array, split_array, set_from_array, property_getter, set_from_hash, array_sum, array_minimum, array_maximum, set_intersection, filter_set, create_hash, exist, } from 'common/utilities' import { action_update_mission, action_cancel_mission, action_cancel_deal, action_send_mission, action_assign_mission, action_unassign_mission, action_duplicate_deal, action_add_mission, action_customize_deal_fields, deal_has_custom_fields, action_export_users, action_toggle_publish_mission, action_notify_updated_mission, action_cancel_notify_updated_mission, action_update_mission_events, } from 'common/missions' import {action_create_workplace} from 'common/workplaces' import {make_periods} from 'common/calendar' const get_raw_deals = (state) => state.missions.deals const get_raw_missions = (state) => state.missions.missions const get_raw_events = (state) => state.missions.events const get_raw_mission_users = (state) => state.missions.users const get_raw_workplaces = (state) => state.missions.workplaces const get_raw_users = (state) => state.users.users const get_profile_id = (state) => state.profile.id const get_raw_agency = (state) => state.profile.agency const get_raw_values = (state) => state.fields.values const get_raw_fields = (state) => state.fields.fields export const get_missions = tolerant_selector( [ get_raw_missions, get_raw_mission_users, get_raw_events, get_raw_workplaces, get_users, get_fields, get_fields_by_mission, get_raw_agency, ], (missions, mission_users, events, workplaces, users, fields, fields_by_mission, {workplace_shared_with_user}) => { let day_events_by_mission = {} let hour_events_by_mission = {} let not_full_days = {} let start = {} let end = {} array_from_hash(events).forEach((e) => { if (!e.deleted_at) { let events_by_mission if (e.full_days) { events_by_mission = day_events_by_mission } else { not_full_days[e.calenderable_id] = true events_by_mission = hour_events_by_mission } events_by_mission[e.calenderable_id] = [ ...(events_by_mission[e.calenderable_id] || []), e ] if (!start[e.calenderable_id] || moment(e.start).isBefore(start[e.calenderable_id])) { start[e.calenderable_id] = e.start } if (!end[e.calenderable_id] || moment(e.end).isAfter(end[e.calenderable_id])) { end[e.calenderable_id] = e.end } } }) const allowed_users_statuses = set_from_array(["active", "mail", "pending"]) return map_hash(missions, (mission) => { const {id, users_count, workplace_id, recruiter_id} = mission const users_status = mission_users[id] || {} const grouped_users_status = group_hash_by_value( users_status, ['new', 'accepted', 'rejected', 'assigned', 'impossible'] ) grouped_users_status.available = Object.keys(users) .filter((user_id) => !users_status[user_id]) .filter((user_id) => users[user_id] && allowed_users_statuses[users[user_id].status.status]) const status = mission_status({...mission, grouped_users_status}) const assigned_count = grouped_users_status.assigned.length const accepted_count = grouped_users_status.accepted.length const day_events = day_events_by_mission[id] || [] const hour_events = hour_events_by_mission[id] || [] const fields = array_from_hash(fields_by_mission[id] || {}).map((field) => { const {shared_with_user, } = field if (shared_with_user) { return { ...field, ...create_hash([ "on_select_value", "on_deselect_value", "on_clear", "on_submit_comment", ], (action) => (...args) => { action_notify_updated_mission(mission.id) return field[action](...args) }) } } return field }) mission = { ...mission, status, sent: status != 'draft', day_events, hour_events, events: [...day_events, ...hour_events], grouped_users_status, users_status, workplace: workplaces[workplace_id] || {}, full_days: !not_full_days[id], start: start[id], end: end[id], assigned_count, accepted_count, empty_count: mission.users_count - assigned_count, waiting_count: grouped_users_status.new.length, fields, } return { ...mission, update(new_mission) { action_update_mission(mission, {id, ...new_mission}) }, cancel() { action_cancel_mission(mission) }, create_workplace(workplace_name) { return action_create_workplace(workplace_name) }, set_workplace(workplace_id) { action_notify_updated_mission(mission.id) action_update_mission(mission, {id, workplace_id}) }, update_events(events) { action_notify_updated_mission(mission.id) return action_update_mission_events(mission.id, events) }, send(user_ids) { return action_send_mission(mission, user_ids) }, assign(user_ids) { return action_assign_mission(mission, user_ids) }, unassign(user_ids) { return action_unassign_mission(mission, user_ids) }, toggle_publish() { return action_toggle_publish_mission(mission) }, cancel_update_notification() { return action_cancel_notify_updated_mission(mission) }, } }) } ) function mission_status({grouped_users_status, users_count, published}) { if (grouped_users_status.assigned.length >= users_count) { return "assigned" } else if (for_all(["new", "rejected", "assigned", "accepted"], (status) => grouped_users_status[status].length == 0) && !published) { return "draft" } else if (grouped_users_status.new.length + grouped_users_status.accepted.length + grouped_users_status.assigned.length < users_count && !published) { return "pending_selection" } else if (grouped_users_status.accepted.length > 0) { return "pending_assignement" } else { return "sent" } } const make_mission_periods = (mission) => { return make_periods(mission.events).map((period) => { return ({ ...period, mission_start: mission.start, mission_end: mission.end, mission_status: mission.status, mission_workplace: mission.workplace, mission_id: mission.id, }) }) } export const get_user_missions = make_memoized((user_id) => tolerant_selector( [get_mission_proposals], (missions) => { let user_missions = filter_hash(missions, (mission) => mission.users_status[user_id]) return array_from_hash(user_missions).map((mission) => { return { ...mission, user_status: mission.users_status[user_id], periods: make_mission_periods(mission), // deprecated, do not use } }) .filter(({user_status}) => set_from_array(["new", "accepted", "rejected", "assigned"])[user_status]) } )) export const get_user_missions_events = make_memoized((user_id) => tolerant_selector( [get_user_missions(user_id)], (missions) => flatten_array(missions.map(property_getter('events'))) )) export const get_mission_users = make_memoized((mission_id, status) => tolerant_selector( [get_missions, get_users, get_mission_values_by_mission, get_values_by_field, get_raw_values, get_raw_fields], (missions, users, mission_values_by_mission, values_by_field, values, fields) => { let {grouped_users_status} = missions[mission_id] || {} if (grouped_users_status) { let user_ids_set = set_from_array(grouped_users_status[status]) if (status == "available") { const accepted_values = {} mission_values_by_mission[mission_id].forEach((value_id) => { const {field_id} = values[value_id] const {for_entity, category, recycled} = fields[field_id] if (for_entity == 'User' && recycled) { if (category == 'non_exclusive') { user_ids_set = set_intersection(user_ids_set, values[value_id].entity_ids) } else if (category == 'exclusive') { accepted_values[field_id] = { ...(accepted_values[field_id] || {}), [value_id]: true, } } } }) array_from_hash(fields).forEach(({id, category, for_entity, recycled, value_id_for}) => { if (for_entity == 'User' && recycled && category == 'exclusive') { const values = accepted_values[id] if (values) { user_ids_set = filter_set(user_ids_set, (user_id) => { return values[value_id_for[user_id]] }) } } }) } return filter_hash(users, ({id, status: {status}}) => user_ids_set[id] && status != 'applied') } else { return {} } } )) export const get_initially_selected_recruiters = tolerant_selector( [get_recruiters, get_profile_id], (recruiters, profile_id) => { if (profile_id) { const {team_id, administrator} = recruiters[profile_id] || {} if (administrator) { return [] } else if (team_id) { return array_from_hash(recruiters).filter(({team_id: id}) => id == team_id).map(property_getter('id')) } else { return [profile_id] } } else { return [] } } ) export const get_deals = tolerant_selector( [get_raw_deals, get_missions, get_raw_workplaces, get_recruiters, get_mission_fields_by_deal, get_fields_by_deal], (deals, all_missions, workplaces, recruiters, mission_fields_by_deal, fields_by_deal) => { const template_mission_ids = set_from_hash(map_hash(deals, property_getter('template_mission_id'))) const missions_by_deal = split_array( array_from_hash(filter_hash(all_missions, ({id, deleted_at}) => !deleted_at && !template_mission_ids[id])), property_getter('deal_id'), Object.keys(deals), ) return map_hash( deals, (deal) => { const { id, template_mission_id, custom_field_ids, has_custom_events, has_custom_workplace, recruiter_id, } = deal function filter_fields({fields, ...mission}, keep_custom = true) { return { ...mission, fields: (fields || []).filter(({id, category}) => Boolean(keep_custom) == Boolean(custom_field_ids[id] || category == 'computed') ), } } function add_deals_fields({fields, ...mission}) { return { ...mission, fields: [...fields, ...array_from_hash(fields_by_deal[id] || {})] } } const template_mission = add_deals_fields(filter_fields(all_missions[template_mission_id] || {}, false)) const missions = missions_by_deal[id] .map((mission) => filter_fields(mission, true)) .filter(({deleted_at}) => !deleted_at) const counts = {}; (new Array('users_count', 'accepted_count', 'assigned_count', 'empty_count', 'waiting_count')).forEach((count) => { counts[count] = array_sum(missions.map(property_getter(count, 0))) }) const start = has_custom_events ? array_minimum(missions.map(property_getter('start')), (a, b) => moment(a).isBefore(b)) : template_mission.start const end = has_custom_events ? array_maximum(missions.map(property_getter('end')), (a, b) => moment(b).isBefore(b)) : template_mission.end const events = has_custom_events ? flatten_array(missions.map(property_getter('events'))) : template_mission.events const workplace_ids = has_custom_workplace ? missions.map(property_getter('workplace_id')) : [template_mission.workplace_id] const own_workplaces = workplace_ids.map((workplace_id) => workplaces[workplace_id] || {}) deal = { ...deal, ...counts, missions, sent: exist(missions, property_getter('sent')), template_mission, status: deal_status(missions), workplace_ids, workplaces: own_workplaces, workplace: own_workplaces[0], recruiter: recruiters[recruiter_id] || {}, start, end, events, fields: mission_fields_by_deal[id], has_custom_fields: deal_has_custom_fields(deal), } return { ...deal, cancel() { action_cancel_deal(deal) }, duplicate() { action_duplicate_deal(deal) }, add_mission() { action_add_mission(deal) }, customize_fields() { action_customize_deal_fields(deal) }, export_users() { return action_export_users(deal) } } } ) } ) export function deal_status(missions) { const statuses = split_array(missions, property_getter('status'), ['pending_selection', 'pending_assignement', 'assigned', 'sent', 'draft']) if (statuses['assigned'].length == missions.length) { return 'assigned' } else if (statuses['pending_selection'].length) { return 'pending_selection' } else if (statuses['pending_assignement'].length) { return 'pending_assignement' } else if (statuses['draft'].length) { return 'draft' } else { return 'sent' } } export const get_displayed_deals = tolerant_selector( [get_deals, get_recruiters], (deals, recruiters) => { let default_deal_ids = set_from_array(array_from_hash(recruiters).map(property_getter('default_deal_id'))) return array_from_hash(deals).filter(({id, deleted_at}) => !deleted_at && !default_deal_ids[id]) } ) export const get_default_deal = tolerant_selector( [get_profile_id, get_recruiters, get_deals], (id, recruiters, deals) => { const {default_deal_id} = recruiters[id] || {} return deals[default_deal_id] } ) export const get_mission_proposals = tolerant_selector( [get_missions, get_deals, get_raw_agency, get_recruiters], (missions, deals, agency, recruiters) => map_hash(missions, (mission) => { const deal = deals[mission.deal_id] const { has_custom_events, has_custom_workplace, template_mission_id, custom_field_ids, recruiter_id, } = deal || {custom_field_ids: {}} const template_mission = missions[template_mission_id] || {} let status = mission.status let sent = mission.sent if (template_mission_id == mission.id) { status = deal.status sent = deal.sent } const {fields, workplace} = template_mission const {workplace_shared_with_user} = agency function get_event_properties({start, end, events}) { return {start, end, events} } const real_workplace = has_custom_workplace ? mission.workplace : workplace return { ...mission, deal, status, sent, real_workplace, workplace: workplace_shared_with_user ? real_workplace : null, agency, recruiter: recruiters[recruiter_id] || {}, ...get_event_properties(has_custom_events ? mission : template_mission), fields: (fields || []).filter(property_getter('shared_with_user')).map((field, index) => { if (custom_field_ids[field.id]) { return mission.fields[index] } else { return field } }) } }) ) export const get_user_deals = make_memoized((user_id) => tolerant_selector( [get_user_missions(user_id), get_deals], (missions, deals) => { const deal_ids = {} missions.forEach(({deal_id}) => deal_ids[deal_id] = true) return array_from_hash(deals).filter(({id}) => deal_ids[id]) } )) export const get_proposals = make_memoized((mission_id) => tolerant_selector( [get_missions, get_users, get_fields_by_proposal], (missions, users, fields_by_proposal) => { const {assigned_proposals} = missions[mission_id] || {assigned_proposals: []} return assigned_proposals.map(({user_id, ...proposal}) => ({ ...proposal, mission_id, user: users[user_id] || {}, fields: array_from_hash(fields_by_proposal[proposal.id]) || [], })) } ))