instantjob-recruiter-client
Version:
a set of tools for creating an instantjob recruiter react client
360 lines (338 loc) • 11.3 kB
JSX
import React from 'react'
import moment from 'common/moment'
import {browserHistory} from 'react-router'
import {MdEdit, MdDone, MdDoneAll, MdSend, MdClose} from 'react-icons/lib/md'
import request from './request'
import store from './store'
import {
set_from_array, set_intersection, human_plural, set_count, download_file, array_from_set, empty_set,
make_memoized,
} from 'common/utilities'
import {color} from 'common/styles'
import {cancelable_timeout} from 'common/cancelable'
import {
store_deals, store_missions, update_mission, update_deal, store_available_users,
remove_mission, remove_deal, send_mission, assign_mission, unassign_mission, replace_mission_events,
remove_deals,
} from '../actions/missions'
import {dismiss_popover, show_popover, alert_success} from 'actions/display'
import CancelMission from 'components/popovers/cancel_mission'
import SendMission from 'components/popovers/send_mission'
import AssignMission from 'components/popovers/assign_mission'
import UnassignMission from 'components/popovers/unassign_mission'
import ConfirmMissionUpdate from 'components/popovers/confirm_mission_update'
import CustomizeDealFields from 'components/popovers/customize_deal_fields'
import {get_missions, get_deals, get_mission_proposals} from 'selectors/missions'
export function mission_period(mission) {
if (mission.start) {
let day_format = `D MMM${moment().isSame(mission.start, 'year') ? '' : ' YYYY'}`
let hour_format = 'HH:mm'
let day_with_hour_format = `${day_format} ${mission.full_days ? '' : hour_format}`.trim()
if (moment(mission.start).isSame(mission.end, 'day')) {
return `${moment(mission.start).format(day_format)}${mission.full_days ? '' : ` : ${moment(mission.start).format(hour_format)} ▸ ${moment(mission.end).format(hour_format)}`}`
} else {
return `${moment(mission.start).format(day_with_hour_format)} ▸ ${moment(mission.end).format(day_with_hour_format)}`
}
} else {
return ""
}
}
export function action_create_deal() {
request.post(`recruiters/${store.getState().profile.id}/deals`)
.then((deal) => {
store.dispatch(store_deals([deal]))
browserHistory.push(`/deals/${deal.id}`)
})
}
export function action_update_mission({users_count, grouped_users_status: {assigned, accepted}, status}, mission) {
const action = () => {
store.dispatch(update_mission(mission))
request.delayed_put(`missions/${mission.id}`, {mission})
}
if (
status != 'assigned' &&
mission.users_count &&
assigned.length >= mission.users_count &&
accepted.length
) {
store.dispatch(show_popover(
ConfirmMissionUpdate,
{on_confirm: action},
"Attention"
))
} else {
action()
}
}
export function action_cancel_mission({id, status}) {
function action() {
store.dispatch(remove_mission(id))
request.delete(`missions/${id}`)
}
if (status == 'draft') {
action()
} else {
store.dispatch(show_popover(
CancelMission,
{
on_click: () => {
action()
store.dispatch(dismiss_popover())
},
mission_status: status,
},
'Annuler la prestation',
))
}
}
export function action_cancel_deal({id, status, missions}) {
function action() {
store.dispatch(remove_deal(id))
request.delete(`deals/${id}`)
browserHistory.push("/deals")
}
store.dispatch(show_popover(
CancelMission,
{
on_click: () => {
action()
store.dispatch(dismiss_popover())
},
mission_status: status,
},
'Annuler la mission',
))
}
export function action_update_mission_events(mission_id, events) {
return request.post(`missions/${mission_id}/events`, {events})
.then((events) => store.dispatch(replace_mission_events(mission_id, events)))
}
export function place_from_workplace({latitude, longitude, name, address}) {
return {
location: {
lat: latitude,
lng: longitude,
},
label: name,
address,
}
}
export function action_send_mission(mission, user_ids) {
return new Promise((resolve, reject) => {
store.dispatch(show_popover(
SendMission,
{
mission_id: mission.id,
on_confirm() {
request.put(`missions/${mission.id}/send`, {user_ids})
.then(() => store.dispatch(alert_success("La proposition de mission a bien été envoyée aux candidats que vous aviez sélectionnés.")))
store.dispatch(send_mission(mission.id, user_ids))
store.dispatch(dismiss_popover())
resolve()
}
},
store.getState().profile.agency.name
))
})
}
export function action_assign_mission(mission, user_ids, status = 'accepted') {
return new Promise((resolve, reject) => {
store.dispatch(show_popover(
AssignMission,
{
status,
mission,
on_confirm() {
request.put(`missions/${mission.id}/assign`, {user_ids})
.then(() => {
const {grouped_users_status: {accepted}, status} = get_missions(store.getState())[mission.id]
const has_users_with_status_not_retained = status == "assigned" && set_count(set_intersection(set_from_array(mission.grouped_users_status.accepted), accepted))
store.dispatch(alert_success(`La mission a bien été validée auprès ${human_plural(
user_ids.length,
"du candidat que vous aviez sélectionné, et il a été prévenu par mail",
"des candidats que vous aviez sélectionnés, et ils ont été prévenus par mail"
)}.${
has_users_with_status_not_retained ?
" Les candidats qui s'étaient déclarés intéressés et que vous n'avez pas sélectionés ont reçu un message pour les prévenir." :
""
}`))
})
store.dispatch(assign_mission(mission.id, user_ids))
store.dispatch(dismiss_popover())
resolve()
}
},
'Valider la mission pour ce candidat'
))
})
}
export function action_unassign_mission(mission, user_ids) {
return new Promise((resolve, reject) => {
store.dispatch(show_popover(
UnassignMission,
{on_confirm() {
request.put(`missions/${mission.id}/unassign`, {user_ids})
.then(() => store.dispatch(alert_success(`Nous avons envoyé un mail ${human_plural(user_ids.length, 'au candidat que vous aviez sélectionné pour lui dire que sa', 'aux candidats que vous aviez sélectionnés pour leur dire que leur')} mission est annulée.`)))
store.dispatch(unassign_mission(mission.id, user_ids))
store.dispatch(dismiss_popover())
resolve()
}},
'Annuler la mission pour ce candidat'
))
})
}
export function action_duplicate_deal({id}) {
browserHistory.push('/deals')
request.post(`deals/${id}/duplicate`)
.then((deal) => {
browserHistory.push(`/deals/${deal.id}`)
store.dispatch(store_deals([deal]))
})
}
export function deal_has_custom_fields({has_custom_events, has_custom_workplace, custom_field_ids}) {
return has_custom_events || has_custom_workplace || !empty_set(custom_field_ids)
}
export function action_add_mission(deal) {
function action() {
request.post(`deals/${deal.id}/missions`)
.then((mission) => store.dispatch(store_missions([mission])))
}
if (!deal_has_custom_fields(deal)) {
action_customize_deal_fields(deal)
.then(action)
} else {
action()
}
}
export function action_customize_deal_fields({id}) {
return new Promise((resolve, reject) => {
store.dispatch(show_popover(
CustomizeDealFields,
{
deal_id: id,
update(deal) {
store.dispatch(update_deal({...deal, id}))
const {has_custom_events, has_custom_workplace, custom_field_ids} = deal
request.put(`deals/${id}`, {deal: {has_custom_events, has_custom_workplace, custom_field_ids: array_from_set(custom_field_ids)}})
store.dispatch(dismiss_popover())
resolve()
}
},
"Sélectionner les champs spécifiques à chaque prestation"
))
})
}
export function action_export_users({id}) {
return request.asynchronous(`deals/${id}/export_users`)
.then((response) => {
download_file(response.url, "mission")
})
}
export const get_mission_status_elements = make_memoized((status) => {
switch (status) {
case 'draft':
return {
priority: 1,
name: 'non proposée',
color: color('black', 'pale'),
icon: <MdEdit />,
}
case 'sent':
return {
priority: 4,
name: 'en attente de réponse',
color: color('primary', 'light'),
icon: "?",
}
case 'pending_assignement':
return {
priority: 3,
name: 'en attente de validation',
color: color('attention'),
icon: <MdDone />,
}
case 'assigned':
return {
priority: 5,
name: 'validée',
color: color('primary'),
icon: <MdDoneAll />,
}
case 'pending_selection':
return {
priority: 2,
name: 'à proposer à nouveau',
color: color('important'),
icon: <MdSend />,
}
}
})
export function get_mission_proposal_status_elements(status) {
switch (status) {
case 'new':
return {
icon: "?",
color: color('primary', 'light'),
}
case 'accepted':
return {
icon: <MdDone />,
color: color('attention'),
}
case 'rejected':
return {
icon: <MdClose />,
color: color('black', 'pale'),
}
case 'assigned':
return {
icon: <MdDoneAll />,
color: color('primary'),
}
default:
return {}
}
}
export function action_cancel_drafts(deal_ids) {
const deals = get_deals(store.getState())
deal_ids = deal_ids.filter((id) => (deals[id] || {}).status == 'draft')
request.put(`deals/cancel`, {deal_ids})
store.dispatch(remove_deals(deal_ids))
}
export function action_toggle_publish_mission({id, published}) {
store.dispatch(update_mission({id, published: !published}))
if (published) {
return request.put(`missions/${id}/unpublish`)
} else {
return request.put(`missions/${id}/publish`)
}
}
export function action_notify_updated_mission(mission_id) {
const {id, notification_timeout, sent} = get_mission_proposals(store.getState())[mission_id]
if (!sent) {
return
}
if (notification_timeout) {
notification_timeout.cancel()
}
const will_send_notification_at = moment().add(1, 'minute')
store.dispatch(update_mission({
id,
notification_timeout: cancelable_timeout(() => {
request.put(`missions/${id}/notify_of_update`)
store.dispatch(update_mission({
id,
notification_timeout: cancelable_timeout(() => {
store.dispatch(update_mission({id, will_send_notification_at: false}))
}, 2 * 1000)
}))
}, -moment().diff(will_send_notification_at)),
will_send_notification_at,
}))
}
export function action_cancel_notify_updated_mission({id, notification_timeout}) {
if (notification_timeout) {
notification_timeout.cancel()
}
store.dispatch(update_mission({id, will_send_notification_at: false}))
}