```
// eslint-disable-next-line eslint-comments/disable-enable-pair
/* eslint-disable @typescript-eslint/consistent-type-assertions */
import { Box } from '@material-ui/core';
import assertNever from 'assert-never';
import { flow, mapValues } from 'lodash';
import { FormattedMessage } from 'react-intl';
import {
candidacyEventVariant,
candidacySelectionEventVariant,
} from '../../../constants';
import useDialogQueue from '../../../hooks/use-dialog-queue';
import type {
CandidacySelectionEventVariant,
CandidateRejectionReason,
} from '../../../interfaces/candidacy';
import type {
JobOrder,
JobOrderCandidacy,
} from '../../../interfaces/job-order';
import type { Recruiter } from '../../../interfaces/recruiter';
import type {
MergedTalentProfile,
Talent,
TalentProfile,
TalentProfileDataSource,
} from '../../../interfaces/talent';
import type { LinkedInTalentDocument } from '../../../interfaces/talent-linkedin';
import TalentProfileDialogContext from '../../organisms/Dialogs/TalentProfile/context';
import PipelineBoard from './Board';
import type { PipelineContextValue } from './Board/context';
import PipelineContext from './Board/context';
import { dataSourceCandidacies, sortCandidacies } from './constants';
import type { Props as DialogsProps } from './Dialogs';
import Dialogs, { DialogsName } from './Dialogs';
import type { Props as PipelineHeaderProps } from './Header';
import PipelineHeader from './Header';
import type { Props as PipelineMenuProps } from './Menu';
type Data = {
candidacy: Record<string, JobOrderCandidacy>;
jobOrder: Record<string, JobOrder>;
talent: Record<string, Talent>;
talentProfile: Record<string, TalentProfile>;
linkedInTalent: Record<string, LinkedInTalentDocument>;
mergedTalentProfile: Record<string, MergedTalentProfile>;
};
type CandidacyEventPayload = {
candidacyId: string;
note?: string;
};
type CandidacySourcedEventPayload = {
mergedTalentProfileId: string;
note?: string;
};
type CandidacySelectionHappenedEventPayload = {
candidacySelectionScheduledId: string;
conductedAt: string;
note?: string;
};
type CandidacyEventHandlers = {
[candidacyEventVariant.SOURCED]: (
payload: CandidacySourcedEventPayload,
) => void;
[candidacyEventVariant.RECOMMENDED]: (payload: CandidacyEventPayload) => void;
[candidacyEventVariant.SELECTION_SCHEDULED]: (
payload: CandidacyEventPayload & {
variant: CandidacySelectionEventVariant;
},
) => void;
[candidacyEventVariant.SELECTION_HAPPENED]: (
payload: CandidacySelectionHappenedEventPayload,
) => void;
[candidacyEventVariant.OFFERED]: (payload: CandidacyEventPayload) => void;
[candidacyEventVariant.HIRED]: (payload: CandidacyEventPayload) => void;
[candidacyEventVariant.UNSUITABLE]: (
payload: CandidacyEventPayload & {
reasons: CandidateRejectionReason[];
},
) => void;
};
type Props = {
data: Data;
currentJobOrderId: string;
headerProps: PipelineHeaderProps;
candidacyEventHandlers?: CandidacyEventHandlers;
canEdit?: boolean;
canEditCandidacy?: PipelineContextValue['canDragItem'];
onFetchMergedTalentProfile?: (id: string) => void;
onFetchTalentProfileSource?: (
id: string,
source?: TalentProfileDataSource,
) => void;
onAddRecruiterToJobOrder?: (recruiterId: string, jobOrderId: string) => void;
onRemoveRecruiterFromJobOrder?: (
recruiterId: string,
jobOrderId: string,
) => void;
};
type TalentProfileDialogData = {
id: string;
candidacyId: string;
};
const PipelineTemplate: React.FunctionComponent<Props> = ({
data,
currentJobOrderId,
headerProps,
candidacyEventHandlers,
canEdit = false,
canEditCandidacy,
onFetchMergedTalentProfile,
onFetchTalentProfileSource,
onAddRecruiterToJobOrder,
onRemoveRecruiterFromJobOrder,
}) => {
const {
queue: dialogQueue,
lastInQueue: activeDialog,
appendDialog,
closeDialog,
} = useDialogQueue();
const currentJobOrder = data.jobOrder[currentJobOrderId];
if (!currentJobOrder) {
return null;
}
const { recruiters, candidacies } = currentJobOrder;
const mergedTalentProfileIds = new Set(
candidacies.map((candidacy) => candidacy.mergedTalentProfile.id),
);
const talentSearchProps: PipelineContextValue['talentSearchProps'] = {
filterOptions: (options) =>
options.filter(
(option) => !mergedTalentProfileIds.has(option.MergedTalentProfileId),
),
onSelect: (option) => {
appendDialog({
key: DialogsName.TALENT_PROFILE,
data: {
id: option.MergedTalentProfileId,
} as TalentProfileDialogData,
});
// candidacyEventHandlers?.[candidacyEventVariant.SOURCED]?.({
// mergedTalentProfileId: option.MergedTalentProfileId,
// });
},
};
const dialogsData = mapValues(DialogsName, (name) => dialogQueue[name]?.data);
const talentProfileDialogData = dialogsData.TALENT_PROFILE as TalentProfileDialogData;
const talentProfileDialogTalentProfile =
data.talentProfile[talentProfileDialogData.id];
const talentProfileDialogCandidacy =
data.candidacy[talentProfileDialogData.candidacyId];
const rejectTalentDialogData =
data.candidacy[dialogsData.REJECT_TALENT as string];
const removeAssigneeDialogData = recruiters.find(
(recruiter) => recruiter.id === dialogsData.REMOVE_ASSIGNEE,
);
const selectionEventTypeDialogData =
data.candidacy[dialogsData.SELECTION_EVENT_TYPE as string];
const selectionHappenedDialogData =
data.candidacy[dialogsData.SELECTION_HAPPENED as string];
const offerConfirmationDialogData =
data.candidacy[dialogsData.OFFER_CONFIRMATION as string];
const handleDropItem: PipelineContextValue['onDropItem'] = (
candidacy,
event,
) => {
switch (event.stage) {
case candidacyEventVariant.SOURCED:
candidacyEventHandlers?.[event.stage]?.({
mergedTalentProfileId: candidacy.mergedTalentProfile.id,
});
break;
case candidacyEventVariant.RECOMMENDED:
case candidacyEventVariant.HIRED:
candidacyEventHandlers?.[event.stage]?.({
candidacyId: candidacy.id,
});
break;
case candidacyEventVariant.SELECTION_SCHEDULED:
appendDialog({
key: DialogsName.SELECTION_EVENT_TYPE,
data: candidacy.id,
});
break;
case candidacyEventVariant.OFFERED:
appendDialog({
key: DialogsName.OFFER_CONFIRMATION,
data: candidacy.id,
});
break;
case candidacyEventVariant.UNSUITABLE:
appendDialog({
key: DialogsName.REJECT_TALENT,
data: candidacy.id,
});
break;
default:
assertNever(event);
}
};
const handleAddRecruiterToJobOrder = (recruiter: Recruiter) => {
onAddRecruiterToJobOrder?.(recruiter.id, currentJobOrderId);
};
const handlePipelineMenuOnClick: PipelineMenuProps['onClick'] = (key) => {
switch (key) {
case 'viewMembers':
appendDialog({
key: DialogsName.EDIT_MEMBERS,
});
break;
default:
assertNever(key);
}
};
const handleTalentCardMenuItemOnClick: PipelineContextValue['onClickTalentCardMenuItem'] = (
key,
candidacy,
) => {
switch (key) {
case 'unsuitable':
appendDialog({
key: DialogsName.REJECT_TALENT,
data: candidacy.id,
});
break;
case 'nextSelection':
appendDialog({
key: DialogsName.SELECTION_EVENT_TYPE,
data: candidacy.id,
});
break;
case 'view': {
const talentProfile =
data.talentProfile[candidacy.mergedTalentProfile.id];
if (talentProfile) {
onFetchTalentProfileSource?.(
talentProfile.id,
talentProfile.sources[0],
);
}
onFetchMergedTalentProfile?.(candidacy.mergedTalentProfile.id);
appendDialog({
key: DialogsName.TALENT_PROFILE,
data: {
id: candidacy.mergedTalentProfile.id,
candidacyId: candidacy.id,
},
});
break;
}
default:
assertNever(key);
}
};
const handleInterviewHappenedOnClick: PipelineContextValue['onClickInterviewHappened'] = (
candidacy,
) => {
appendDialog({
key: DialogsName.SELECTION_HAPPENED,
data: candidacy.id,
});
};
const filterCandidacies = flow(
dataSourceCandidacies[headerProps.options.dataSource],
sortCandidacies[headerProps.options.sort],
);
const dialogProps: DialogsProps['dialogProps'] = {
editMembers: dialogQueue[DialogsName.EDIT_MEMBERS] && {
data: currentJobOrder,
onAddRecruiter: handleAddRecruiterToJobOrder,
onRemoveRecruiter: (recruiter) => {
appendDialog({
key: DialogsName.REMOVE_ASSIGNEE,
data: recruiter.id,
});
},
},
rejectTalent: rejectTalentDialogData?.mergedTalentProfile_ && {
data: rejectTalentDialogData.mergedTalentProfile_,
job: currentJobOrder,
onSubmit: (formData) => {
candidacyEventHandlers?.[candidacyEventVariant.UNSUITABLE]({
candidacyId: rejectTalentDialogData.id,
note: formData.additionalNote,
reasons: formData.reasons,
});
closeDialog(DialogsName.REJECT_TALENT);
},
},
removeAssignee: removeAssigneeDialogData && {
data: removeAssigneeDialogData,
onSubmit: () => {
onRemoveRecruiterFromJobOrder?.(
removeAssigneeDialogData.id,
currentJobOrderId,
);
closeDialog(DialogsName.REMOVE_ASSIGNEE);
},
},
talentProfile: talentProfileDialogTalentProfile && {
data: talentProfileDialogTalentProfile,
additionalInfo: {
source: {
glints: data.talent[talentProfileDialogTalentProfile.id],
linkedIn: data.linkedInTalent[talentProfileDialogTalentProfile.id],
},
},
onSelectAction: (key) => {
if(!talentProfileDialogCandidacy) {
return;
}
switch (key) {
case candidacyEventVariant.SOURCED:
candidacyEventHandlers?.[key]({
mergedTalentProfileId:
talentProfileDialogTalentProfile.MergedTalentProfileId,
});
break;
case candidacyEventVariant.RECOMMENDED:
case candidacyEventVariant.OFFERED:
case candidacyEventVariant.HIRED:
candidacyEventHandlers?.[key]({
candidacyId: talentProfileDialogCandidacy.id,
});
break;
case candidacyEventVariant.SELECTION_SCHEDULED:
appendDialog({
key: DialogsName.SELECTION_EVENT_TYPE,
data: talentProfileDialogCandidacy.id,
});
break;
case candidacyEventVariant.SELECTION_HAPPENED:
appendDialog({
key: DialogsName.SELECTION_HAPPENED,
data: talentProfileDialogCandidacy.id,
});
break;
case candidacyEventVariant.UNSUITABLE:
appendDialog({
key: DialogsName.REJECT_TALENT,
data: talentProfileDialogCandidacy.id,
});
break;
default:
assertNever(key);
}
},
onSource: ()
},
selectionEventType: selectionEventTypeDialogData && {
defaultValues: {
value: candidacySelectionEventVariant.ON_SITE_INTERVIEW,
},
onSubmit: ({ value }) => {
candidacyEventHandlers?.[candidacyEventVariant.SELECTION_SCHEDULED]?.({
candidacyId: selectionEventTypeDialogData.id,
variant: value,
});
closeDialog(DialogsName.SELECTION_EVENT_TYPE);
},
},
selectionHappened: selectionHappenedDialogData && {
onSubmit: ({ date }) => {
candidacyEventHandlers?.[candidacyEventVariant.SELECTION_HAPPENED]?.({
candidacySelectionScheduledId: selectionHappenedDialogData.latest.id,
conductedAt: date,
});
closeDialog(DialogsName.SELECTION_HAPPENED);
},
},
offerConfirmation: offerConfirmationDialogData && {
name: offerConfirmationDialogData.mergedTalentProfile_?.name ??
offerConfirmationDialogData.mergedTalentProfile_?.email ?? (
<FormattedMessage
id="unnamed.talent"
defaultMessage="Unnamed Talent"
/>
),
onConfirm: () => {
candidacyEventHandlers?.[candidacyEventVariant.OFFERED]?.({
candidacyId: offerConfirmationDialogData.id,
});
closeDialog(DialogsName.OFFER_CONFIRMATION);
},
},
};
return (
<PipelineContext.Provider
value={{
canEdit,
talentSearchProps,
canDragItem: canEditCandidacy,
onDropItem: handleDropItem,
onClickTalentCardMenuItem: handleTalentCardMenuItemOnClick,
onClickInterviewHappened: handleInterviewHappenedOnClick,
}}
>
<Box
py={2}
display="flex"
flexDirection="column"
flex={1}
overflow="hidden"
>
<PipelineHeader
{...headerProps}
pipelineMenuProps={{
onClick: handlePipelineMenuOnClick,
}}
/>
<PipelineBoard candidacies={filterCandidacies(candidacies)} />
<TalentProfileDialogContext.Provider
value={{
canEditActiveCandidacy: canEdit,
activeCandidacy: talentProfileDialogData && {
...talentProfileDialogData,
jobOrder: currentJobOrder,
},
candidacies:
talentProfileDialogData &&
data.mergedTalentProfile[
talentProfileDialogData.mergedTalentProfile.id
]?.candidacies,
}}
>
<Dialogs
activeDialog={activeDialog?.key}
dialogProps={dialogProps}
onCloseDialog={closeDialog}
/>
</TalentProfileDialogContext.Provider>
</Box>
</PipelineContext.Provider>
);
};
export type { Props };
export default PipelineTemplate;
```