feat: add bootcamp cost calc guide (#35278)

* feat: add bootcamp cost calc guide

* feat: use ReactJS portals

* feat: fix eslint errors

* feat: update with change requests

* feat: update package-lock.json
This commit is contained in:
Ron Sogueco
2019-08-17 00:57:36 -04:00
committed by Quincy Larson
parent 9e4abad1cb
commit 869f5edfc5
8 changed files with 928 additions and 61 deletions

View File

@ -3981,6 +3981,19 @@
"resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
"integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=" "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII="
}, },
"d3": {
"version": "3.5.17",
"resolved": "https://registry.npmjs.org/d3/-/d3-3.5.17.tgz",
"integrity": "sha1-vEZ0gAQ3iyGjYMn8fPUjF5B2L7g="
},
"d3-3": {
"version": "0.0.0",
"resolved": "https://registry.npmjs.org/d3-3/-/d3-3-0.0.0.tgz",
"integrity": "sha1-xrj+3d590sqJMiKxJ7n9qY41Aq0=",
"requires": {
"d3": "^3"
}
},
"cheerio": { "cheerio": {
"version": "1.0.0-rc.3", "version": "1.0.0-rc.3",
"resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.3.tgz", "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.3.tgz",
@ -4030,8 +4043,7 @@
}, },
"ansi-regex": { "ansi-regex": {
"version": "2.1.1", "version": "2.1.1",
"bundled": true, "bundled": true
"optional": true
}, },
"aproba": { "aproba": {
"version": "1.2.0", "version": "1.2.0",
@ -4049,13 +4061,11 @@
}, },
"balanced-match": { "balanced-match": {
"version": "1.0.0", "version": "1.0.0",
"bundled": true, "bundled": true
"optional": true
}, },
"brace-expansion": { "brace-expansion": {
"version": "1.1.11", "version": "1.1.11",
"bundled": true, "bundled": true,
"optional": true,
"requires": { "requires": {
"balanced-match": "^1.0.0", "balanced-match": "^1.0.0",
"concat-map": "0.0.1" "concat-map": "0.0.1"
@ -4068,18 +4078,15 @@
}, },
"code-point-at": { "code-point-at": {
"version": "1.1.0", "version": "1.1.0",
"bundled": true, "bundled": true
"optional": true
}, },
"concat-map": { "concat-map": {
"version": "0.0.1", "version": "0.0.1",
"bundled": true, "bundled": true
"optional": true
}, },
"console-control-strings": { "console-control-strings": {
"version": "1.1.0", "version": "1.1.0",
"bundled": true, "bundled": true
"optional": true
}, },
"core-util-is": { "core-util-is": {
"version": "1.0.2", "version": "1.0.2",
@ -4182,8 +4189,7 @@
}, },
"inherits": { "inherits": {
"version": "2.0.3", "version": "2.0.3",
"bundled": true, "bundled": true
"optional": true
}, },
"ini": { "ini": {
"version": "1.3.5", "version": "1.3.5",
@ -4193,7 +4199,6 @@
"is-fullwidth-code-point": { "is-fullwidth-code-point": {
"version": "1.0.0", "version": "1.0.0",
"bundled": true, "bundled": true,
"optional": true,
"requires": { "requires": {
"number-is-nan": "^1.0.0" "number-is-nan": "^1.0.0"
} }
@ -4206,20 +4211,17 @@
"minimatch": { "minimatch": {
"version": "3.0.4", "version": "3.0.4",
"bundled": true, "bundled": true,
"optional": true,
"requires": { "requires": {
"brace-expansion": "^1.1.7" "brace-expansion": "^1.1.7"
} }
}, },
"minimist": { "minimist": {
"version": "0.0.8", "version": "0.0.8",
"bundled": true, "bundled": true
"optional": true
}, },
"minipass": { "minipass": {
"version": "2.3.5", "version": "2.3.5",
"bundled": true, "bundled": true,
"optional": true,
"requires": { "requires": {
"safe-buffer": "^5.1.2", "safe-buffer": "^5.1.2",
"yallist": "^3.0.0" "yallist": "^3.0.0"
@ -4236,7 +4238,6 @@
"mkdirp": { "mkdirp": {
"version": "0.5.1", "version": "0.5.1",
"bundled": true, "bundled": true,
"optional": true,
"requires": { "requires": {
"minimist": "0.0.8" "minimist": "0.0.8"
} }
@ -4309,8 +4310,7 @@
}, },
"number-is-nan": { "number-is-nan": {
"version": "1.0.1", "version": "1.0.1",
"bundled": true, "bundled": true
"optional": true
}, },
"object-assign": { "object-assign": {
"version": "4.1.1", "version": "4.1.1",
@ -4320,7 +4320,6 @@
"once": { "once": {
"version": "1.4.0", "version": "1.4.0",
"bundled": true, "bundled": true,
"optional": true,
"requires": { "requires": {
"wrappy": "1" "wrappy": "1"
} }
@ -4396,8 +4395,7 @@
}, },
"safe-buffer": { "safe-buffer": {
"version": "5.1.2", "version": "5.1.2",
"bundled": true, "bundled": true
"optional": true
}, },
"safer-buffer": { "safer-buffer": {
"version": "2.1.2", "version": "2.1.2",
@ -4427,7 +4425,6 @@
"string-width": { "string-width": {
"version": "1.0.2", "version": "1.0.2",
"bundled": true, "bundled": true,
"optional": true,
"requires": { "requires": {
"code-point-at": "^1.0.0", "code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0", "is-fullwidth-code-point": "^1.0.0",
@ -4445,7 +4442,6 @@
"strip-ansi": { "strip-ansi": {
"version": "3.0.1", "version": "3.0.1",
"bundled": true, "bundled": true,
"optional": true,
"requires": { "requires": {
"ansi-regex": "^2.0.0" "ansi-regex": "^2.0.0"
} }
@ -4484,13 +4480,11 @@
}, },
"wrappy": { "wrappy": {
"version": "1.0.2", "version": "1.0.2",
"bundled": true, "bundled": true
"optional": true
}, },
"yallist": { "yallist": {
"version": "3.0.3", "version": "3.0.3",
"bundled": true, "bundled": true
"optional": true
} }
} }
}, },
@ -14664,7 +14658,7 @@
"dependencies": { "dependencies": {
"minimist": { "minimist": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
} }
} }

View File

@ -20,6 +20,7 @@
"axios": "^0.19.0", "axios": "^0.19.0",
"browser-cookies": "^1.2.0", "browser-cookies": "^1.2.0",
"chai": "^4.2.0", "chai": "^4.2.0",
"d3-3": "0.0.0",
"date-fns": "^1.30.1", "date-fns": "^1.30.1",
"entities": "^1.1.2", "entities": "^1.1.2",
"enzyme": "^3.10.0", "enzyme": "^3.10.0",

View File

@ -0,0 +1,39 @@
.article .d3-chart {
position: relative;
}
.article div.tooltip {
position: absolute;
text-align: center;
width: 80px;
padding: 5px;
font: 12px sans-serif;
background: rgba(0, 0, 0, 0.8);
border: 0px;
border-radius: 8px;
pointer-events: none;
color: #fff;
display: table;
}
.article div.tooltip > div {
display: table-cell;
vertical-align: middle;
}
.article div.tooltip:after {
box-sizing: border-box;
font-size: 10px;
width: 100%;
line-height: 1;
color: rgba(0, 0, 0, 0.8);
content: "\25BC";
position: absolute;
text-align: center;
bottom: -9px;
left: 0;
}
.select-city {
display: block;
}

View File

@ -0,0 +1,228 @@
import React from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import { graphql } from 'gatsby';
import ArticleLayout from '../../ArticleLayout';
import CostCalculatorChart from './CostCalculatorChart';
import './CodingBootcampCostCalculator.css';
const propTypes = {
data: PropTypes.object
};
class CostCalculator extends React.Component {
state = {
cities: [],
incomes: [],
city: null,
cityLabel: '_______',
lastYearsIncome: null,
lastYearsIncomeLabel: '_______',
bootcamps: null
};
initComponent() {
const bootcamps = JSON.parse(
document.getElementById('bootcamps').innerHTML
);
document
.getElementById('bootcamps-data-link')
.setAttribute(
'href',
`data:text/plain;charset=utf-8,${encodeURIComponent(
JSON.stringify(bootcamps, null, 2)
)}`
);
this.setState({
bootcamps: bootcamps,
cities: bootcamps
.reduce((previous, current) => {
return previous.concat(current.cities);
}, [])
.filter((city, idx, me) => {
return me.indexOf(city) === idx;
})
.sort(),
incomes: [
'0',
'10000',
'20000',
'30000',
'40000',
'50000',
'60000',
'70000',
'80000',
'90000',
'100000',
'120000',
'140000',
'160000',
'180000',
'200000'
]
});
const selectCityDiv = document.getElementById('select-city');
const selectIncomeDiv = document.getElementById('select-income');
const cityLabelSpan = document.getElementById('city-label');
const lastYearsIncomeLabelSpan = document.getElementById(
'last-years-income-label'
);
const chartComponentDiv = document.getElementById('chart-component');
this.setState({
init: true,
selectCityDiv,
selectIncomeDiv,
cityLabelSpan,
lastYearsIncomeLabelSpan,
chartComponentDiv
});
this.handleCitySelector = this.handleCitySelector.bind(this);
this.handleIncomeSelector = this.handleIncomeSelector.bind(this);
}
componentDidMount() {
this.initComponent();
}
handleCitySelector(event) {
let el = event.target;
this.setState({
city: event.target.value,
cityLabel: el.options[el.selectedIndex].text
});
}
handleIncomeSelector(event) {
let el = event.target;
this.setState({
lastYearsIncome: parseInt(event.target.value, 10),
lastYearsIncomeLabel: el.options[el.selectedIndex].text
});
}
renderSelectCity() {
return (
<select
className='form-control'
defaultValue=''
onChange={this.handleCitySelector}
>
<option disabled='true' value=''>
Select City
</option>
{this.state.cities.map((city, idx) => {
let cityLabel =
typeof city !== 'undefined'
? city.replace(/-/g, ' ').replace(/\b\w/g, l => l.toUpperCase())
: '';
return (
<option key={idx} value={city}>
{cityLabel}
</option>
);
})}
</select>
);
}
renderSelectIncome() {
return (
<select
className='form-control'
defaultValue=''
onChange={this.handleIncomeSelector}
>
<option disabled='true' value=''>
Select Income
</option>
{this.state.incomes.map((income, idx) => {
let incomeLabel =
typeof income !== 'undefined'
? parseInt(income, 10).toLocaleString()
: '0';
return (
<option key={idx} value={income}>
${incomeLabel}
</option>
);
})}
</select>
);
}
renderChartComponent() {
return (
<CostCalculatorChart
bootcamps={this.state.bootcamps}
city={this.state.city}
lastYearsIncome={this.state.lastYearsIncome}
/>
);
}
render() {
if (!this.state.init) {
return null;
}
return (
<div className='CodingBootcampCostCalculator'>
{ReactDOM.createPortal(
this.renderSelectCity(),
this.state.selectCityDiv
)}
{ReactDOM.createPortal(
this.renderSelectIncome(),
this.state.selectIncomeDiv
)}
{ReactDOM.createPortal(this.state.cityLabel, this.state.cityLabelSpan)}
{ReactDOM.createPortal(
this.state.lastYearsIncomeLabel,
this.state.lastYearsIncomeLabelSpan
)}
{ReactDOM.createPortal(
this.renderChartComponent(),
this.state.chartComponentDiv
)}
</div>
);
}
}
const CodingBootcampCostCalculator = props => {
const {
data: {
markdownRemark: { html }
}
} = props;
return (
<ArticleLayout {...props}>
<article
className='article'
dangerouslySetInnerHTML={{ __html: html }}
id='article'
tabIndex='-1'
/>
<CostCalculator />
</ArticleLayout>
);
};
CodingBootcampCostCalculator.displayName = 'CodingBootcampCostCalculator';
CodingBootcampCostCalculator.propTypes = propTypes;
export default CodingBootcampCostCalculator;
export const pageQuery = graphql`
query CodingBootcampCostCalculator($id: String!) {
markdownRemark(id: { eq: $id }) {
html
...ArticleLayout
}
}
`;

View File

@ -0,0 +1,40 @@
import React from 'react';
import PropTypes from 'prop-types';
import { updateCalculator } from './CostCalculatorChartD3.js';
const propTypes = {
bootcamps: PropTypes.array,
city: PropTypes.string,
lastYearsIncome: PropTypes.number
};
class CostCalculatorChart extends React.Component {
constructor(props) {
super(props);
this.rootRef = React.createRef();
}
componentDidMount() {
this.updateChart();
}
componentDidUpdate() {
this.updateChart();
}
updateChart() {
const { bootcamps, city, lastYearsIncome } = this.props;
if (city !== null && lastYearsIncome !== null) {
const node = this.rootRef.current;
updateCalculator(node, bootcamps, city, lastYearsIncome);
}
}
render() {
return <div ref={this.rootRef} />;
}
}
CostCalculatorChart.propTypes = propTypes;
export default CostCalculatorChart;

View File

@ -0,0 +1,240 @@
import d3 from 'd3-3';
export function updateCalculator(d3Node, bootcamps, city, lastYearsIncome) {
d3Node.className = 'd3-chart';
var categoryNames = [
'Lost Wages',
'Financing Cost',
'Housing Cost',
'Tuition / Est. Wage Garnishing'
];
// Tooltips
var tip = d3.select(d3Node).select('div.tooltip');
if (tip.empty()) {
tip = d3
.select(d3Node)
.append('div')
.attr('class', 'tooltip')
.style('opacity', 0);
}
var xAxis = d3.svg.axis().orient('bottom');
var yAxis = d3.svg.axis().orient('left');
bootcamps.forEach(function(camp) {
var x0 = 0;
var weeklyHousing;
if (camp.cities.indexOf(city) > -1) {
weeklyHousing = 0;
} else {
weeklyHousing = +camp.housing;
}
camp.mapping = [
{
name: camp.name,
label: 'Tuition / Est. Wage Garnishing',
value: +camp.cost,
x0: x0,
x1: (x0 += +camp.cost)
},
{
name: camp.name,
label: 'Financing Cost',
value: camp.finance ? +Math.floor(camp.cost * 0.09519) : 0,
x0: camp.finance ? +camp.cost : 0,
x1: camp.finance ? (x0 += +Math.floor(camp.cost * 0.09519)) : 0
},
{
name: camp.name,
label: 'Housing Cost',
value: +weeklyHousing * camp.weeks,
x0: camp.finance ? +Math.floor(camp.cost * 1.09519) : camp.cost,
x1: (x0 += weeklyHousing * camp.weeks)
},
{
name: camp.name,
label: 'Lost Wages',
value: +Math.floor((camp.weeks * lastYearsIncome) / 50),
x0: camp.finance
? +(Math.floor(camp.cost * 1.09519) + weeklyHousing * camp.weeks)
: +camp.cost + weeklyHousing * camp.weeks,
x1: (x0 += +(Math.floor(camp.weeks * lastYearsIncome) / 50))
}
];
camp.total = camp.mapping[camp.mapping.length - 1].x1;
});
bootcamps.sort(function(a, b) {
return a.total - b.total;
});
var xStackMax = d3.max(bootcamps, function(d) {
return d.total;
});
var margin = {
top: 30,
right: 60,
bottom: 60,
left: 155
},
width = 650 - margin.left - margin.right,
height = 1200 - margin.top - margin.bottom;
var xScale = d3.scale
.linear()
.domain([0, xStackMax])
.rangeRound([0, width]);
var y0Scale = d3.scale
.ordinal()
.domain(
bootcamps.map(function(d) {
return d.name;
})
)
.rangeRoundBands([0, height], 0.1);
var color = d3.scale
.ordinal()
.range(['#215f1e', '#5f5c1e', '#1e215f', '#5c1e5f'])
.domain(categoryNames);
var svg = d3
.select(d3Node)
.select('svg')
.select('g');
var selection, rect;
if (svg.empty()) {
svg = d3
.select(d3Node)
.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 + ')');
// Legends
var legend = svg
.selectAll('.legend')
.data(categoryNames.slice().reverse())
.enter()
.append('g')
.attr('class', 'legend')
.attr('transform', function(d, i) {
return 'translate(30,' + i * y0Scale.rangeBand() * 1.1 + ')';
});
legend
.append('rect')
.attr('x', width - y0Scale.rangeBand())
.attr('width', y0Scale.rangeBand())
.attr('height', y0Scale.rangeBand())
.style('fill', color)
.style('stroke', 'white');
legend
.append('text')
.attr('x', width - y0Scale.rangeBand() * 1.2)
.attr('y', 12)
.attr('dy', '.35em')
.style('text-anchor', 'end')
.text(function(d) {
return d;
});
}
selection = svg.selectAll('.series').data(bootcamps);
if (!selection.empty()) {
selection.exit().remove();
}
selection
.enter()
.append('g')
.attr('class', 'series')
.attr('transform', function(d) {
return 'translate(0,' + y0Scale(d.name) + ')';
});
rect = selection.selectAll('rect').data(function(d) {
return d.mapping;
});
if (!rect.empty()) {
rect.exit().remove();
}
rect
.enter()
.append('rect')
.attr('class', 'series-part')
.attr('x', 0)
.attr('width', 0)
.attr('height', y0Scale.rangeBand())
.style('fill', function(d) {
return color(d.label);
})
.style('stroke', 'white')
.on('mouseover', function(d) {
var component = d3.select('.d3-chart');
var componentPos = component[0][0].getBoundingClientRect();
var box = this.getBoundingClientRect();
var tooltip = component.select('.tooltip');
component
.select('.tooltip')
.transition()
.duration(200)
.style('opacity', 0.9);
component
.select('.tooltip')
.html('<div>' + d.label + '<br />$' + d.value + '</div>')
.style(
'left',
box.left +
box.width / 2 -
componentPos.left -
tooltip[0][0].offsetWidth / 2 +
'px'
)
.style(
'top',
box.top - componentPos.top - tooltip[0][0].offsetHeight - 10 + 'px'
);
})
.on('mouseout', function() {
d3.select('.d3-chart')
.select('.tooltip')
.transition()
.duration(500)
.style('opacity', 0);
});
rect
.transition()
.delay(function(d, i) {
return i * 10;
})
.attr('x', function(d) {
return xScale(d.x0);
})
.attr('width', function(d) {
return xScale(d.x1 - d.x0);
});
// Axes
var svgXAxis = svg.select('.x.axis');
var svgYAxis = svg.select('.y.axis');
xAxis.scale(xScale);
yAxis.scale(y0Scale);
if (svgXAxis.empty() || svgYAxis.empty()) {
svg
.append('g')
.attr('class', 'y axis')
.call(yAxis);
svg
.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(0,' + height + ')')
.call(xAxis)
.selectAll('text')
.style('text-anchor', 'end')
.attr('dx', '-.8em')
.attr('dy', '.15em')
.attr('transform', 'rotate(-45)');
} else {
svgXAxis
.call(xAxis)
.selectAll('text')
.style('text-anchor', 'end')
.attr('dx', '-.8em')
.attr('dy', '.15em')
.attr('transform', 'rotate(-45)');
svgYAxis.call(yAxis);
}
return d3Node;
}

View File

@ -0,0 +1,344 @@
---
title: Coding Bootcamp Cost Calculator
component: calculators/CodingBootcampCostCalculator/CodingBootcampCostCalculator.js
---
## Coding Bootcamp Cost Calculator
<label class='h4' id='select-city'>
Where do you <strong>live</strong>?
</label>
<div id='select-city'></div>
<label class='h4' id='select-income'>
How much <strong>money</strong> did you make last year (in USD)?
</label>
<div id='select-income'></div>
### Coming from <span id='city-label'></span>, and making <span id='last-years-income-label'></span>, your true costs will be:
<div id='chart-component'></div>
<div class='text-center'>
<a
href='javascript:void(0)'
download='bootcamps.json'
id='bootcamps-data-link'
>
Save Data Source JSON
</a>
</div>
### Notes:
<ol>
<li>
We assumed an APR of 6% and a term of 3 years. If you happen
to have around $15,000 in cash set aside for a coding
bootcamp, please ignore this cost.
</li>
<li>
We assume a cost of living of $500 for cities like San
Francisco and New York City, and $400 per week for
everywhere else.
</li>
<li>
The most substantial cost for most people is lost wages. A
40-hour-per-week job at the US Federal minimum wage would
pay at least $15,000 per year. You can read more about
economic cost
<a href='https://en.wikipedia.org/wiki/Economic_cost' rel='noopener noreferrer' target='_blank'>
here
</a>.
</li>
</ol>
### Built by Suzanne Atkinson
<div class='col-sm-4 col-md-4'>
<img alt='Suzanne Atkinson selfie in front of the pool' class='img-responsive testimonial-image img-center' src='https://www.evernote.com/l/AHRIBndcq-5GwZVnSy1_D7lskpH4OcJcUKUB/image.png' />
</div>
<div class='col-sm-8 col-md-8'>
<p>
Suzanne is an emergency medicine physician, triathlon
coach and web developer from Pittsburgh. You should
&thinsp;
<a href='https://twitter.com/intent/user?screen_name=SteelCityCoach' rel='noopener noreferrer' target='_blank'>
follow her on Twitter
</a>.
</p>
</div>
<script type="application/json" id="bootcamps">
[
{
"name": "Hack Reactor",
"cost": "17780",
"housing": "500",
"weeks": "12",
"finance": true,
"cities": [
"new-york-city",
"san-francisco"
]
},
{
"name": "Hack Reactor Online",
"cost": "17780",
"housing": "0",
"weeks": "12",
"finance": true,
"cities": [
"online"
]
},
{
"name": "Hackbright Academy",
"cost": "15000",
"housing": "500",
"weeks": "10",
"finance": true,
"cities": [
"san-francisco"
]
},
{
"name": "Dev Bootcamp",
"cost": "13950",
"finance": true,
"housing": "500",
"weeks": "19",
"cities": [
"new-york-city",
"san-francisco",
"chicago"
]
},
{
"name": "General Asssembly",
"cost": "11500",
"housing": "500",
"finance": true,
"weeks": "12",
"cities": [
"washington-dc",
"austin",
"boston",
"chicago",
"hong-kong",
"london",
"los-angeles",
"melbourne",
"new-york-city",
"san-francisco",
"seattle",
"singapore"
]
},
{
"name": "Angel Hack",
"cost": "14250",
"housing": "500",
"finance": true,
"weeks": "12",
"cities": [
"san-francisco"
]
},
{
"name": "Bitmaker Labs",
"cost": "12000",
"housing": "500",
"finance": true,
"weeks": "12",
"cities": [
"toronto"
]
},
{
"name": "CoderVox",
"cost": "9980",
"housing": "400",
"finance": true,
"weeks": "12",
"cities": [
"austin"
]
},
{
"name": "Coding Dojo",
"cost": "12500",
"housing": "500",
"finance": true,
"weeks": "12",
"cities": [
"new-york-city",
"san-francisco",
"chicago"
]
},
{
"name": "DevMountain",
"cost": "8900",
"housing": "0",
"finance": true,
"weeks": "12",
"cities": [
"provo",
"salt-lake-city",
"dallas"
]
},
{
"name": "Epicodus",
"cost": "4500",
"housing": "400",
"finance": false,
"weeks": "15",
"cities": [
"portland"
]
},
{
"name": "Flat Iron School",
"cost": "15000",
"housing": "500",
"finance": true,
"weeks": "12",
"cities": [
"new-york-city"
]
},
{
"name": "Galvanize",
"cost": "21000",
"housing": "500",
"finance": true,
"weeks": "24",
"cities": [
"boulder",
"denver",
"seattle",
"san-francisco"
]
},
{
"name": "The Iron Yard",
"cost": "12000",
"housing": "500",
"finance": true,
"weeks": "19",
"cities": [
"austin",
"washington-dc",
"raleigh-durham",
"atlanta"
]
},
{
"name": "Launch Academy",
"cost": "12500",
"housing": "500",
"finance": true,
"weeks": "10",
"cities": [
"boston"
]
},
{
"name": "Maker Square",
"cost": "16920",
"housing": "500",
"finance": true,
"weeks": "12",
"cities": [
"los-angeles",
"san-francisco",
"austin"
]
},
{
"name": "Refactor U",
"cost": "13500",
"housing": "400",
"finance": true,
"weeks": "10",
"cities": [
"boulder"
]
},
{
"name": "Rocket U",
"cost": "12500",
"housing": "500",
"finance": true,
"weeks": "12",
"cities": [
"new-york-city",
"san-francisco",
"chicago"
]
},
{
"name": "Sabio",
"cost": "13450",
"housing": "500",
"finance": true,
"weeks": "12",
"cities": [
"los-angeles"
]
},
{
"name": "Shillington School",
"cost": "12950",
"housing": "500",
"finance": true,
"weeks": "12",
"cities": [
"new-york-city",
"sydney",
"brisbane",
"london",
"manchester",
"melbourne"
]
},
{
"name": "The Tech Academy",
"cost": "9000",
"housing": "400",
"finance": true,
"weeks": "20",
"cities": [
"portland"
]
},
{
"name": "Viking Code School",
"cost": "18000",
"housing": "0",
"finance": false,
"weeks": "16",
"cities": [
"online"
]
},
{
"name": "App Academy",
"cost": "18000",
"housing": "500",
"finance": false,
"weeks": "12",
"cities": [
"san-francisco"
]
},
{
"name": "Turing School",
"cost": "17500",
"housing": "400",
"finance": true,
"weeks": "27",
"cities": [
"denver"
]
}
]
</script>

41
package-lock.json generated
View File

@ -5304,8 +5304,7 @@
"ansi-regex": { "ansi-regex": {
"version": "2.1.1", "version": "2.1.1",
"bundled": true, "bundled": true,
"dev": true, "dev": true
"optional": true
}, },
"aproba": { "aproba": {
"version": "1.2.0", "version": "1.2.0",
@ -5326,14 +5325,12 @@
"balanced-match": { "balanced-match": {
"version": "1.0.0", "version": "1.0.0",
"bundled": true, "bundled": true,
"dev": true, "dev": true
"optional": true
}, },
"brace-expansion": { "brace-expansion": {
"version": "1.1.11", "version": "1.1.11",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"balanced-match": "^1.0.0", "balanced-match": "^1.0.0",
"concat-map": "0.0.1" "concat-map": "0.0.1"
@ -5348,20 +5345,17 @@
"code-point-at": { "code-point-at": {
"version": "1.1.0", "version": "1.1.0",
"bundled": true, "bundled": true,
"dev": true, "dev": true
"optional": true
}, },
"concat-map": { "concat-map": {
"version": "0.0.1", "version": "0.0.1",
"bundled": true, "bundled": true,
"dev": true, "dev": true
"optional": true
}, },
"console-control-strings": { "console-control-strings": {
"version": "1.1.0", "version": "1.1.0",
"bundled": true, "bundled": true,
"dev": true, "dev": true
"optional": true
}, },
"core-util-is": { "core-util-is": {
"version": "1.0.2", "version": "1.0.2",
@ -5478,8 +5472,7 @@
"inherits": { "inherits": {
"version": "2.0.3", "version": "2.0.3",
"bundled": true, "bundled": true,
"dev": true, "dev": true
"optional": true
}, },
"ini": { "ini": {
"version": "1.3.5", "version": "1.3.5",
@ -5491,7 +5484,6 @@
"version": "1.0.0", "version": "1.0.0",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"number-is-nan": "^1.0.0" "number-is-nan": "^1.0.0"
} }
@ -5506,7 +5498,6 @@
"version": "3.0.4", "version": "3.0.4",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"brace-expansion": "^1.1.7" "brace-expansion": "^1.1.7"
} }
@ -5514,14 +5505,12 @@
"minimist": { "minimist": {
"version": "0.0.8", "version": "0.0.8",
"bundled": true, "bundled": true,
"dev": true, "dev": true
"optional": true
}, },
"minipass": { "minipass": {
"version": "2.3.5", "version": "2.3.5",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"safe-buffer": "^5.1.2", "safe-buffer": "^5.1.2",
"yallist": "^3.0.0" "yallist": "^3.0.0"
@ -5540,7 +5529,6 @@
"version": "0.5.1", "version": "0.5.1",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"minimist": "0.0.8" "minimist": "0.0.8"
} }
@ -5621,8 +5609,7 @@
"number-is-nan": { "number-is-nan": {
"version": "1.0.1", "version": "1.0.1",
"bundled": true, "bundled": true,
"dev": true, "dev": true
"optional": true
}, },
"object-assign": { "object-assign": {
"version": "4.1.1", "version": "4.1.1",
@ -5634,7 +5621,6 @@
"version": "1.4.0", "version": "1.4.0",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"wrappy": "1" "wrappy": "1"
} }
@ -5720,8 +5706,7 @@
"safe-buffer": { "safe-buffer": {
"version": "5.1.2", "version": "5.1.2",
"bundled": true, "bundled": true,
"dev": true, "dev": true
"optional": true
}, },
"safer-buffer": { "safer-buffer": {
"version": "2.1.2", "version": "2.1.2",
@ -5757,7 +5742,6 @@
"version": "1.0.2", "version": "1.0.2",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"code-point-at": "^1.0.0", "code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0", "is-fullwidth-code-point": "^1.0.0",
@ -5777,7 +5761,6 @@
"version": "3.0.1", "version": "3.0.1",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"ansi-regex": "^2.0.0" "ansi-regex": "^2.0.0"
} }
@ -5821,14 +5804,12 @@
"wrappy": { "wrappy": {
"version": "1.0.2", "version": "1.0.2",
"bundled": true, "bundled": true,
"dev": true, "dev": true
"optional": true
}, },
"yallist": { "yallist": {
"version": "3.0.3", "version": "3.0.3",
"bundled": true, "bundled": true,
"dev": true, "dev": true
"optional": true
} }
} }
}, },