Skip to content

Instantly share code, notes, and snippets.

@jonsadka
Last active March 1, 2017 17:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jonsadka/3bddce9b5f6b167fffbb to your computer and use it in GitHub Desktop.
Save jonsadka/3bddce9b5f6b167fffbb to your computer and use it in GitHub Desktop.
Time to Hire
license: mit

Each colored bar represents a different step / stage in the hiring process

Built with blockbuilder.org

<!DOCTYPE html>
<head>
<meta charset="utf-8">
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<style>
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; }
svg { width: 100%; height: 100%; }
a {position: absolute; right: 15px; padding: 5px; border-bottom: 3px solid black; cursor: pointer;}
</style>
</head>
<body>
<a onclick="toggleStage();">Toggle</a>
<script>
var colors = d3.scale.category20c();
var TIME = [
{
"team": "Engineering",
"stages": [
{"text": "New applicant", "days": 35, "candidates": 11156},
{"text": "New lead", "days": 180, "candidates": 1503},
{"text": "Recruiter Screen", "days": 32, "candidates": 1230},
{"text": "Phone Interview", "days": 25, "candidates": 1322},
{"text": "2nd Phone Interview", "days": 8, "candidates": 13},
{"text": "Initial Onsite", "days": 29, "candidates": 176},
{"text": "Homework", "days": 16, "candidates": 240},
{"text": "On-site interview", "days": 34, "candidates": 540},
{"text": "Offer", "days": 31, "candidates": 179}
]
},
{
"team": "Marketing",
"stages": [
{"text": "New applicant", "days": 38, "candidates": 4317},
{"text": "New lead", "days": 26, "candidates": 575},
{"text": "Recruiter Screen", "days": 25, "candidates": 584},
{"text": "Phone Interview", "days": 25, "candidates": 378},
{"text": "2nd Phone Interview", "days": 14, "candidates": 14},
{"text": "Initial Onsite", "days": 35, "candidates": 113},
{"text": "Homework", "days": 28, "candidates": 197},
{"text": "On-site interview", "days": 21, "candidates": 69},
{"text": "Offer", "days": 106, "candidates": 80}
]
},
{
"team": "Operations",
"stages": [
{"text": "New applicant", "days": 46, "candidates": 5427},
{"text": "New lead", "days": 42, "candidates": 586},
{"text": "Recruiter Screen", "days": 37, "candidates": 435},
{"text": "Phone Interview", "days": 22, "candidates": 340},
{"text": "2nd Phone Interview", "days": 11, "candidates": 22},
{"text": "Initial Onsite", "days": 35, "candidates": 166},
{"text": "Homework", "days": 12, "candidates": 73},
{"text": "On-site interview", "days": 24, "candidates": 87},
{"text": "Offer", "days": 18, "candidates": 53}
]
},
{
"team": "Creative",
"stages": [
{"text": "New applicant", "days": 190, "candidates": 1286},
{"text": "New lead", "days": 23, "candidates": 168},
{"text": "Recruiter Screen", "days": 78, "candidates": 152},
{"text": "Phone Interview", "days": 42, "candidates": 49},
{"text": "2nd Phone Interview", "days": 0, "candidates": 0},
{"text": "Initial Onsite", "days": 83, "candidates": 46},
{"text": "Homework", "days": 18, "candidates": 11},
{"text": "On-site interview", "days": 56, "candidates": 17},
{"text": "Offer", "days": 19, "candidates": 6}
]
},
{
"team": "Support",
"stages": [
{"text": "New applicant", "days": 14, "candidates": 2423},
{"text": "New lead", "days": 33, "candidates": 209},
{"text": "Recruiter Screen", "days": 13, "candidates": 212},
{"text": "Phone Interview", "days": 38, "candidates": 56},
{"text": "2nd Phone Interview", "days": 0, "candidates": 0},
{"text": "Initial Onsite", "days": 18, "candidates": 144},
{"text": "Homework", "days": 5, "candidates": 239},
{"text": "On-site interview", "days": 13, "candidates": 71},
{"text": "Offer", "days": 24, "candidates": 23}
]
},
{
"team": "People",
"stages": [
{"text": "New applicant", "days": 30, "candidates": 3182},
{"text": "New lead", "days": 31, "candidates": 369},
{"text": "Recruiter Screen", "days": 19, "candidates": 332},
{"text": "Phone Interview", "days": 14, "candidates": 117},
{"text": "2nd Phone Interview", "days": 7, "candidates": 1},
{"text": "Initial Onsite", "days": 21, "candidates": 173},
{"text": "Homework", "days": 4, "candidates": 8},
{"text": "On-site interview", "days": 32, "candidates": 81},
{"text": "Offer", "days": 17, "candidates": 27 }
]
},
{
"team": "Finance",
"stages": [
{"text": "New applicant", "days": 168, "candidates": 1083},
{"text": "New lead", "days": 7, "candidates": 60},
{"text": "Recruiter Screen", "days": 23, "candidates": 59},
{"text": "Phone Interview", "days": 49, "candidates": 37},
{"text": "2nd Phone Interview", "days": 0, "candidates": 0},
{"text": "Initial Onsite", "days": 43, "candidates": 32},
{"text": "Homework", "days": 2, "candidates": 8},
{"text": "On-site interview", "days": 136, "candidates": 13},
{"text": "Offer", "days": 53, "candidates": 5}
]
},
{
"team": "Legal",
"stages": [
{"text": "New applicant", "days": 11, "candidates": 1055},
{"text": "New lead", "days": 8, "candidates": 154},
{"text": "Recruiter Screen", "days": 6, "candidates": 134},
{"text": "Phone Interview", "days": 16, "candidates": 102},
{"text": "2nd Phone Interview", "days": 0, "candidates": 0},
{"text": "Initial Onsite", "days": 31, "candidates": 59},
{"text": "Homework", "days": 0, "candidates": 5},
{"text": "On-site interview", "days": 19, "candidates": 25},
{"text": "Offer", "days": 34, "candidates": 10}
]
},
]
var margin = {top: 0, right: 0, bottom: 0, left: 0};
var width = 960 - margin.left - margin.right;
var height = 500 - margin.top - margin.bottom;
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.attr('id', 'transformGroup')
// Get max time
var totals = {};
totals.days = TIME.reduce(function(prevMax, currentTeam){
var currentDays = currentTeam.stages.reduce(function(pv, stage){return pv + stage.days;},0);
return Math.max(prevMax, currentDays);
},0)
totals.candidates = TIME.reduce(function(prevMax, currentTeam){
var currentCandidates = currentTeam.stages.reduce(function(pv, stage){return pv + stage.candidates;},0);
return Math.max(prevMax, currentCandidates);
},0)
// INITIAL RENDER
TIME.forEach(function(team, idx){
team.stages.forEach(function(currentStage, jdx){
totals[currentStage.text + '-days'] = Math.max((totals[currentStage.text + '-days']||0), currentStage.days)
totals[currentStage.text + '-candidates'] = Math.max((totals[currentStage.text + '-candidates']||0), currentStage.candidates)
if (jdx === 0){
TIME[idx].stages[jdx].cumulDays = 0;
TIME[idx].stages[jdx].cumulCandidates = 0;
} else {
var prevConversion = TIME[idx].stages[jdx - 1];
TIME[idx].stages[jdx].cumulDays = prevConversion.cumulDays + prevConversion.days;
TIME[idx].stages[jdx].cumulCandidates = prevConversion.cumulCandidates + prevConversion.candidates;
}
})
// console.log(totals.days, team.stages[team.stages.length-1].cumulDays + team.stages[team.stages.length-1].days)
d3.select('#transformGroup').append('g').attr('class', team.team)
.selectAll('rect').data(team.stages).enter().append('rect')
.attr({
width: function(d){return width * d.days/totals.days;},
x: function(d){
var barWidth = width * d.cumulDays/totals.days;
return barWidth;
},
height: height * 1 / TIME.length,
y: function(d, i){
var barHeight = height * idx / TIME.length;
return barHeight;
},
stroke: 'black',
'stroke-width': 1
})
.style({
fill: function(d,i){ return colors(i)},
opacity: 0.2
})
// APPEND TEAM TEXT
var teamTotalDays = team.stages[team.stages.length - 1].cumulDays + team.stages[team.stages.length - 1].days
d3.select('.' + team.team).append('text')
.text(team.team + ': ' + teamTotalDays + ' days')
.attr({
x: 0,
y: function(d){
var barHeight = height * idx / TIME.length;
return barHeight + 14;
}
})
})
var scaleStage = true;
function toggleStage(){
scaleStage = !scaleStage;
TIME.forEach(function(team, idx){
d3.select('.' + team.team).selectAll('rect').data(team.stages)
.transition().duration(1000)
.attr({
width: function(d){
if (scaleStage){
return width * d.days/totals.days;
} else {
return (width * 1/(TIME.length + 1)) * d.days / totals[d.text + '-days'];
}
},
x: function(d, i){
if (scaleStage){
var barWidth = width * d.cumulDays/totals.days;
return barWidth;
} else {
return width * i/(TIME.length + 1);
}
}
})
})
}
</script>
</body>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment