Skip to content

Instantly share code, notes, and snippets.

@robmazan
Last active February 27, 2019 08:00
Show Gist options
  • Save robmazan/a104e33416b5cc36ccb709b764b574e9 to your computer and use it in GitHub Desktop.
Save robmazan/a104e33416b5cc36ccb709b764b574e9 to your computer and use it in GitHub Desktop.
Lead Changes
Lead Changes
Lead creation
ADAM batch upload -> NOT_YET_ISSUED
NOT_YET_ISSUED
Start campaign -> OPEN_AWAITING_ASSIGNMENT
Closed by ADAM -> CLOSED
OPEN_AWAITING_ASSIGNMENT
Assign -> OPEN_AWAITING_ACTION
Closed by ADAM -> CLOSED
Lead expired -> CLOSED
OPEN_AWAITING_ACTION
Take action -> OPEN_AWAITING_ACTION
Reassign -> OPEN_AWAITING_ACTION
Deassign -> OPEN_AWAITING_ASSIGNMENT
Create new lead -> CLOSED
Create new lead for myself -> CLOSED
Close lead -> CLOSED
Closed by ADAM -> CLOSED
Lead expired -> CLOSED
CLOSED
const logs = [];
let lastEvent = null;
let contactAttempts = 0;
let contactAttemptFreeze = false;
const radioStyle = {
fontFamily: 'Arial',
margin: '5px',
padding: '10px',
boxShadow: '1px 1px 2px',
borderRadius: '5px',
background: 'lightblue'
};
const buttonStyle = {
padding: '10px',
margin: '5px',
borderRadius: '5px',
border: 0,
boxShadow: '1px 1px 2px',
backgroundColor: 'lightblue',
fontFamily: 'Arial'
};
const Button = props => <button style={buttonStyle} {...props}>{props.children}</button>;
const logLead = (props, message = 'Lead updated:') => {
logs.push(message + ' ' + JSON.stringify(props));
};
const logAction = (props, message = 'Lead action recorded: ') => {
logs.push(message + ' ' + JSON.stringify(Object.assign({}, props, { creationTime: 'NOW' })));
};
const logT24 = message => {
logs.push(`T24 sync, message: ${message}`);
}
const contact = (increase = true, msg = 'Contact attempt increased') => {
if (contactAttemptFreeze) {
logs.push('Contact attempt counter frozen');
return;
}
if (increase) {
contactAttempts++;
}
logs.push(msg);
};
const contactFreeze = () => {
contactAttemptFreeze = true;
logs.push('Contact attempt counter frozen');
};
const contactReset = () => {
contactAttempts = 0;
contactAttemptFreeze = false;
logs.push('Contact attempt counter reset');
};
const clearLogs = () => logs.length = 0;
const adamCloseHandler = model => () => {
const closeMsg = 'Lead <Lead.ID> Closed by ADAM <action date+time>.';
clearLogs();
logLead({
status: 'CLOSED',
closeReason: closeMsg,
closeTime: 'NOW'
});
logAction({
actionType: 'CLOSE_LEAD',
outcomeNote: closeMsg
});
logT24(closeMsg);
lastLog = 'Closed by ADAM';
model.emit(lastLog);
}
const states = {
'Lead creation': {
render({ model }) {
const batchUpload = () => {
clearLogs();
logLead({ status: 'NOT_YET_ISSUED', creationTime: 'NOW' }, `Lead records created: `);
logAction({ actionType: 'NEW_LEAD_CREATED', outcomeNote: '???' });
contactReset();
lastEvent = 'ADAM batch upload';
model.emit(lastEvent);
};
return <div>
<Button onClick={batchUpload}>ADAM batch upload</Button>
</div>;
}
},
'NOT_YET_ISSUED': {
render({ model }) {
const campaignAllocation = () => {
const note = 'Lead <Lead.ID> for Campaign <Campaign.name> opened as campaign started, allocated to <teamAllocation>, <action date+time>.';
clearLogs();
logLead({
status: 'OPEN_AWAITING_ASSIGNMENT',
teamAllocation: 'as campaign manager set'
});
logAction({
actionType: 'STATUS_CHANGE',
outcomeNote: note
}, 'Lead action recorded for each lead: ');
logT24(note);
lastLog = 'Start campaign';
model.emit(lastLog);
};
return <div>
<Button onClick={campaignAllocation}>Campaign manager allocates leads and starts campaign</Button>
<Button onClick={adamCloseHandler(model)}>ADAM closes lead</Button>
</div>;
}
},
'OPEN_AWAITING_ASSIGNMENT': {
render({ model }) {
const assign = () => {
const note = 'Lead <Lead.ID> assigned to <assignee>, <action date+time>.';
clearLogs();
logLead({
status: 'OPEN_AWAITING_ACTION',
assignee: 'as set'
});
logAction({
actionType: 'ASSIGNED',
outcomeNote: note
});
logT24(note);
lastEvent = 'Assign';
model.emit(lastEvent);
};
return <div>
<Button onClick={assign}>Team manager assigns lead to team member</Button>
<Button onClick={assign}>Team member assigns lead to herself</Button>
<Button onClick={adamCloseHandler(model)}>ADAM closes lead</Button>
</div>;
}
},
'OPEN_AWAITING_ACTION': {
render({ model, state, setState }) {
let formEl;
const setFormRef = el => formEl = el;
const processSaveAndLog = () => {
clearLogs();
// the "action" can be CALL_BACK only, so not checking that
logLead({
callbackDate: 'as set'
});
let note;
switch (formEl.contactType.value) {
case 'contactMade':
note = '<Lead.ID> customer reached by <assignee> Call Back date <callbackDate> for the reason <outcomeNote>, <action date+time>.';
logAction({
actionType: 'CONVERSED_WITH_CUSTOMER',
outcomeNote: note
});
logT24(note);
contact();
contactFreeze();
break;
case 'noContact':
note = '<Lead.ID> No contact by <assignee> Call Back date <callbackDate> for the reason <outcomeNote>, Contact follow up sent <contactFollowUp>, <action date+time>.';
logAction({
actionType: 'FAILED_TO_REACH_CUSTOMER',
outcomeNote: note
});
logT24(note);
contact();
break;
case 'inbound':
note = '<Lead.ID> Incoming call taken by <assignee> Call Back date <callbackDate> for the reason <outcomeNote>, <action date+time>.';
logAction({
actionType: 'INBOUND_CALL',
outcomeNote: note
});
logT24(note);
contactFreeze();
break;
}
lastEvent = 'Save & log lead';
model.emit('Take action');
};
const create = ({ forMyself, closeOldLead }) => {
clearLogs();
logLead({
status: forMyself ? 'OPEN_AWAITING_ACTION': 'OPEN_AWAITING_ASSIGNMENT',
parentLead: 'ID of the parent lead',
callbackDate: 'as entered',
assignee: 'as (and if) set',
teamAllocation: 'as (and if) set'
}, 'New lead record created with data copied over from parent lead: ');
logAction({
lead: 'Old Lead.ID',
actionType: 'NEW_LEAD_CREATED',
outcomeNote: `New lead created. New lead ID: <New lead's ID>, status: ${forMyself ? 'OPEN_AWAITING_ACTION': 'OPEN_AWAITING_ASSIGNMENT'}`
}, 'Lead action recorded for the old lead: ');
if (closeOldLead) {
logLead({
status: 'CLOSED',
closeReason: '???',
closeDetails: '???',
closeTime: 'NOW'
}, 'Closing old lead: ');
logAction({
lead: 'Old Lead.ID',
actionType: 'CLOSE_LEAD',
outcomeNote: `Lead closed by <USER> for the reason <old lead's outcomeNote>`
}, 'Lead action recorded for the old lead: ');
}
const newLeadNote = 'New Lead <Lead.ID> created from <parentLeadID> for the reason <outcomeNote>, <action date+time>.';
logAction({
lead: 'New Lead.ID',
actionType: 'NEW_LEAD_CREATED',
outcomeNote: newLeadNote
}, 'Lead action recorded for the new lead: ');
logT24(newLeadNote);
lastEvent = forMyself ? 'Create new lead for myself': 'Create new lead';
model.emit(lastEvent);
};
const processClose = () => {
if (formEl.closeReason.value === 'CREATED_OTHER_LEADS') {
create({
forMyself: formEl.assignTo.value === 'myself',
closeOldLead: formEl.closeAfterCreate.checked
});
return;
}
clearLogs();
const note = 'Lead <Lead.ID> Closed by <Actioner> for the reason <closeReason> [,Optional: <outcomeNote>], <action date+time>.';
logAction({
actionType: 'CLOSE_LEAD',
outcomeNote: 'as set'
});
logLead({
status: 'CLOSED',
closeReason: formEl.closeReason.value,
closeDetails: note,
closeTime: 'NOW'
});
logT24(note);
contactFreeze();
lastEvent = 'Action saved & lead closed';
model.emit('Close lead');
};
const reAssign = () => {
clearLogs();
logLead({
assignee: 'New assignee'
});
const note = '<Lead.ID> assigned by <Actioner> to <assignee>, <action date+time>.';
logAction({
actionType: 'ASSIGNED',
outcomeNote: note
});
logT24(note);
model.emit('Reassign');
};
const deAssign = () => {
clearLogs();
logLead({
assignee: 'NULL',
status: 'OPEN_AWAITING_ASSIGNMENT'
});
const note = '<Lead.ID> De-assigned from <assignee> by <Actioner>, <action date+time>.';
logAction({
actionType: 'STATUS_CHANGE',
outcomeNote: note
});
logT24(note);
model.emit('Deassign');
};
const leadExpires = () => {
clearLogs();
logLead({
status: 'CLOSED'
});
const note = 'Lead <Lead.ID> Closed as expired <action date+time>.';
logAction({
actionType: 'CLOSE_LEAD',
outcomeNote: note
});
logT24(note);
model.emit('Lead expired');
};
const updateContactType = (ev) => {
setState({ contactType: ev.target.value });
};
const updateAssignTo = (ev) => {
setState({ assignTo: ev.target.value });
};
const updateCloseAfterCreate = (ev) => {
setState({ closeAfter: ev.target.value });
};
return <form ref={setFormRef}>
<p>
<Button onClick={reAssign}>Re-assign lead</Button>
<Button onClick={deAssign}>De-assign lead</Button>
<Button onClick={adamCloseHandler(model)}>ADAM closes lead</Button>
<Button onClick={leadExpires}>Lead expires</Button>
</p>
<p>
<label style={radioStyle}>
<input type="radio" name="contactType" value="contactMade"
checked={state.contactType === 'contactMade'}
onChange={updateContactType} />
&nbsp;Contact made
</label>
<label style={radioStyle}>
<input type="radio" name="contactType" value="noContact"
checked={state.contactType === 'noContact'}
onChange={updateContactType} />
&nbsp;No contact
</label>
<label style={radioStyle}>
<input type="radio" name="contactType" value="inbound"
checked={state.contactType === 'inbound'}
onChange={updateContactType} />
&nbsp;Inbound
</label>
</p>
<p style={{lineHeight: '3em'}}>
In case of "Create/Refer new lead", assign new lead to:&nbsp;
<label style={radioStyle}>
<input type="radio" name="assignTo" value="myself"
checked={state.assignTo === 'myself'}
onChange={updateAssignTo} />
&nbsp;Myself
</label>
<label style={radioStyle}>
<input type="radio" name="assignTo" value="team"
checked={state.assignTo === 'team'}
onChange={updateAssignTo} />
&nbsp;Team
</label>
<label style={radioStyle}>
<input type="checkbox" name="closeAfterCreate"
checked={state.closeAfterCreate}
onChange={updateCloseAfterCreate} />
&nbsp;Close after create
</label>
</p>
<label>
Save &amp; log lead:&nbsp;
<select name="saveAndLogAction" onChange={processSaveAndLog}>
<option></option>
<option value="CALL_BACK">Call back date</option>
</select>
</label>
&nbsp;
<label>
Action saved &amp; lead closed:&nbsp;
<select name="closeReason" onChange={processClose}>
<option></option>
<option value="APPLICATION_COMPLETED" disabled={state.contactType === 'noContact'}>
Application completed
</option>
<option value="MEETING_SCHEDULED" disabled={state.contactType === 'noContact'}>
Meeting scheduled
</option>
<option value="CATCH_UP_COMPLETED" disabled={state.contactType === 'noContact'}>
Catch Up completed on call
</option>
<option value="CREATED_OTHER_LEADS" disabled={state.contactType === 'noContact'}>
Create/Refer new lead
</option>
<option value="NO_SALE" disabled={state.contactType === 'noContact'}>
No sale
</option>
<option value="LEAD_INAPPROPRIATE" disabled={state.contactType === 'noContact'}>
Lead inapropriate for customer
</option>
<option value="INVALID_CUSTOMER_DETAILS" disabled={state.contactType === 'noContact' || state.contactType === 'inbound'}>
Invalid customer details
</option>
<option value="UNABLE_TO_REACH_CUSTOMER" disabled={state.contactType === 'contactMade' || state.contactType === 'inbound'}>
Unable to reach customer
</option>
</select>
</label>
</form>;
}
},
'CLOSED': {
render({ model }) {
return <div></div>;
}
},
};
const LogDisplay = ({ items }) => {
return <div>
<h2>{lastEvent}</h2>
<ol>
{items.map(item => <li>{item}</li>)}
</ol>
</div>;
};
const render = model => {
const currentStateName = model.active_states[0].name;
const stateStyle = {
background: 'aliceblue',
padding: '10px',
borderRadius: '5px',
boxShadow: '0 0 5px',
margin: '5px',
height: '350px',
overflow: 'auto'
};
class StateDisplay extends React.Component {
constructor(props) {
super(props);
this.state = {
contactType: 'contactMade',
assignTo: 'myself'
};
}
render() {
return states[this.props.stateName].render({
model: this.props.model,
setState: this.setState.bind(this),
state: this.state
});
};
}
return <div style={stateStyle}>
<h1>
Current state: {model.active_states[0].name}&nbsp;
(contact attempts: {contactAttempts})
</h1>
<StateDisplay stateName={currentStateName} model={model}/>
<hr />
<LogDisplay items={logs} />
</div>;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment