Chapter 03 (Invoice App Static Data) - Intuit-London/imperial-react-workshop GitHub Wiki
- Create a new folder say
chapter03 npm init- Add the below dependencies inside the package.json file
"dependencies": {
"react": "^15.3.2",
"react-dom": "^15.3.2"
},
"devDependencies": {
"babel-core": "^6.18.2",
"babel-loader": "^6.2.7",
"babel-preset-es2015": "^6.18.0",
"babel-preset-react": "^6.16.0"
"html-loader": "^0.4.4",
"html-webpack-plugin": "^2.24.1",
"webpack": "^1.13.3",
"webpack-dev-server": "^1.16.2"
}- Create
webpack.config.jsunder the folderchapter03with the below contents
var webpack = require('webpack');
var path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
var BUILD_DIR = path.resolve(__dirname, 'build');
var APP_DIR = path.resolve(__dirname, 'src');
module.exports = {
entry: APP_DIR + '/index.js',
output: {
path: BUILD_DIR,
filename: 'app.bundle.js',
},
debug: true,
devtool: 'source-map',
module: {
loaders: [{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader'
},{
test: /\.html$/,
loader: 'html'
}
]
},
plugins: [
new HtmlWebpackPlugin({
title: 'Chapter03',
template: 'src/index.html'
})
]
};- Create a file
.babelrcunder the folderchapter03with the below contents
{
"presets": [
"react",
"es2015"
]
}
npm install --save bootstrap@3npm install --save-dev css-loadernpm install --save-dev style-loadernpm install --save-dev file-loadernpm install --save-dev url-loader- Add loaders in the
webpack.config.jsas follows
.......
{
test: /\.css$/,
include: /node_modules/,
loader: 'style-loader!css-loader'
},{test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, loader: 'file'
},{test: /\.(woff|woff2)$/, loader: 'url?prefix=font/&limit=5000'
},{test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=application/octet-stream'
},{test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=image/svg+xml'
}
......- Add
index.htmlfile undersrcfolder
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Chapter 04</title>
</head>
<body>
<div id="root"></div>
</body>
</html>- Create React Component
AppHeader.jsundersrc/componentsfolder.
import React, { Component } from 'react';
class AppHeader extends Component {
render() {
return (
<nav className="navbar navbar-inverse navbar-static-top">
<div className="container-fluid">
<a className="navbar-brand" href="#">Invoicing App</a>
</div>
</nav>
);
}
}
export default AppHeader;- Create React Component
InvoiceRow.jsundersrc/componentsfolder
import React, { Component } from 'react';
class InvoiceRow extends Component {
render() {
return (
<tr>
<td>{this.props.invoice.number}</td>
<td>{this.props.invoice.customer.businessName}</td>
<td>{this.props.invoice.creationDate}</td>
<td>{this.props.invoice.totalAmount}</td>
<td>{this.props.invoice.paid ? "Yes" : "No"}</td>
</tr>
);
}
}
export default InvoiceRow;- Create React Component
InvoiceList.jsundersrc/componentsfolder
import React, { Component } from 'react';
import InvoiceRow from './InvoiceRow';
class InvoiceList extends Component {
render() {
var invoices = [
{"number": "1", "customer": {"businessName": "Manish"}, "creationDate": "20/06/2016", "totalAmount": "£345", "paid": true},
{"number": "2", "customer": {"businessName": "Manish"}, "creationDate": "10/12/2016", "totalAmount": "£35", "paid": true},
{"number": "3", "customer": {"businessName": "Manish"}, "creationDate": "23/05/2016", "totalAmount": "£34", "paid": false},
{"number": "4", "customer": {"businessName": "Manish"}, "creationDate": "10/06/2016", "totalAmount": "£90", "paid": false},
{"number": "5", "customer": {"businessName": "Manish"}, "creationDate": "09/08/2016", "totalAmount": "£12", "paid": true},
{"number": "6", "customer": {"businessName": "Manish"}, "creationDate": "01/06/2016", "totalAmount": "£34", "paid": true},
{"number": "7", "customer": {"businessName": "Manish"}, "creationDate": "24/11/2015", "totalAmount": "£98", "paid": false}
], rows = [];
for (var i = 0; i < invoices.length; i++) {
rows.push(<InvoiceRow key={i} invoice={invoices[i]}/>);
}
return (
<table className="table table-hover">
<thead>
<tr>
<th>#</th>
<th>Name</th>
<th>Date</th>
<th>Amount</th>
<th>Paid</th>
</tr>
</thead>
<tbody>{rows}</tbody>
</table>
);
}
}
export default InvoiceList;- Create React Component
InvoiceSummary.jsundersrc/componentsfolder
import React, { Component } from 'react';
class InvoiceSummary extends Component {
render() {
return (
<div>Invoice Summary</div>
);
}
}
export default InvoiceSummary;- Create React Component
InvoiceApp.jsundersrc/appfolder
import React, { Component } from 'react';
import AppHeader from "../components/AppHeader"
import InvoiceList from '../components/InvoiceList';
import InvoiceSummary from '../components/InvoiceSummary';
import 'bootstrap/dist/css/bootstrap.css';
class InvoiceApp extends Component {
render() {
return (
<div>
<AppHeader/>
<InvoiceSummary/>
<InvoiceList/>
</div>
);
}
}
export default InvoiceApp;- Create
index.jsundersrcfolder to render theInvoiceAppon document
import React from 'react';
import ReactDOM from 'react-dom';
import InvoiceApp from './app/InvoiceApp';
var InvoiceAppFactory = React.createFactory(InvoiceApp);
ReactDOM.render(
InvoiceAppFactory({}),document.getElementById('root')
);- Open package.json and replace the existing script section with the below content.
"scripts": {
"dev": "node_modules/.bin/webpack-dev-server --content-base build/ --port 8000",
"build": "node_modules/.bin/webpack",
"test": "echo \"Error: no test specified\" && exit 1"
}- Run
npm install - Run
npm run dev - Open url
http://localhost:8000/index.htmlto see it working.
- Enhance Invoice Summary Component to show a Graph.
return (
<div className="progress" id="summary" style={{height: 40 +'px'}}>
<div className="progress-bar progress-bar-success summaryLabel" style={{width: 15 + '%', paddingTop: 10 + 'px'}}>
15 Invoices Paid
</div>
<div className="progress-bar progress-bar-warning progress-bar summaryLabel" style={{width: 65 + '%', paddingTop: 10 + 'px'}}>
65 Open Invoices
</div>
<div className="progress-bar progress-bar-danger summaryLabel" style={{width: 20 + '%', paddingTop: 10 + 'px'}}>
20 OverDue invoices
</div>
</div>
);- Create InvoiceCreate component
import React, { Component } from 'react';
class InvoiceCreate extends Component {
render() {
return (
<div>
<form className="form-horizontal" ref={(ref) => this.createInvoiceForm = ref}>
<div className="form-group">
<label htmlFor="refNum" className="col-sm-offset-1 col-sm-2">Reference Number</label>
<div className="col-sm-8">
<input type="number" className="form-control" id="refNum" placeholder="Number" ref={(ref) => this.invoiceNumber = ref}/>
</div>
</div>
<div className="form-group">
<label htmlFor="name" className="col-sm-offset-1 col-sm-2">Customer Name</label>
<div className="col-sm-4">
<input type="text" className="form-control" id="name" placeholder="Customer Name" ref={(ref) => this.customername = ref}/>
</div>
<div className="col-sm-4">
<input type="email" className="form-control" id="email" placeholder="Customer Email"/>
</div>
</div>
<div className="form-group">
<label htmlFor="email" className="col-sm-offset-1 col-sm-2">Billing Address</label>
<div className="col-sm-8 paddingBottom10">
<input type="text" className="form-control" id="email" placeholder="Line 1"/>
</div>
<div className="col-sm-offset-3 col-sm-8 paddingBottom10">
<input type="text" className="form-control" id="email" placeholder="Line 2"/>
</div>
<div className="col-sm-offset-3 col-sm-4 paddingBottom10">
<input type="text" className="form-control" id="email" placeholder="City"/>
</div>
<div className="col-sm-4 paddingBottom10">
<input type="text" className="form-control" id="email" placeholder="ZipCode"/>
</div>
</div>
<div className="form-group">
<label htmlFor="date" className="col-sm-offset-1 col-sm-2">Date</label>
<div className="col-sm-8">
<input type="date" className="form-control" id="date" placeholder="Date" ref={(ref) => this.invoiceDate = ref}/>
</div>
</div>
<div className="form-group">
<label htmlFor="amount" className="col-sm-offset-1 col-sm-2">Total Amount</label>
<div className="col-sm-8">
<input type="number" className="form-control" id="date" placeholder="Amount" ref={(ref) => this.invoiceAmount = ref}/>
</div>
</div>
<div className="form-group">
<label htmlFor="amountPaid" className="col-sm-offset-1 col-sm-2">Amount Paid</label>
<div className="col-sm-8">
<input type="checkbox" className="form-control" placeholder="Amount Paid" ref={(ref) => this.invoicePaid = ref}/>
</div>
</div>
<div className="form-group">
<div className="col-sm-offset-3 col-sm-8">
<button type="button" className="btn btn-primary">Save</button>
</div>
</div>
</form>
</div>
);
}
}
export default InvoiceCreate;- Enhance InvoiceApp to have a
stateby modifyingconstructorandrenderfunction.
import InvoiceCreate from '../components/InvoiceCreate'; constructor(props) {
super(props);
this.state = {
isCreateInvoice: false
}
};setCreateInvoice() {
this.setState({
isCreateInvoice: true
});
};
setListInvoice() {
this.setState({
isCreateInvoice: false
})
};return (
<div>
<AppHeader createInvoice = {this.setCreateInvoice.bind(this)} listInvoice = {this.setListInvoice.bind(this)}/>
<InvoiceSummary user={this.props.user}/>
{this.state.isCreateInvoice ? <InvoiceCreate/> : <InvoiceList/> }
</div>
);- Enhance
AppHeadercomponent to have button to change thestateby modifying therenderfunction
<nav className="navbar navbar-inverse navbar-static-top">
<div className="container-fluid">
<a className="navbar-brand" href="#">Invoicing App</a>
<button type="button" className="btn btn-default navbar-btn btn-primary pull-right" onClick={this.props.createInvoice}>New Invoice</button>
<button type="button" className="btn btn-default navbar-btn btn-link pull-right" onClick={this.props.listInvoice}>Invoice Lists</button>
</div>
</nav>- Create InvoiceList.json under
/src/dataas follows
{
"invoices" : [
{"number": "1", "customer": {"businessName": "Manish"}, "creationDate": "20/06/2016", "totalAmount": "£345", "paid": true},
{"number": "2", "customer": {"businessName": "Manish"}, "creationDate": "10/12/2016", "totalAmount": "£35", "paid": true},
{"number": "3", "customer": {"businessName": "Manish"}, "creationDate": "23/05/2016", "totalAmount": "£34", "paid": false},
{"number": "4", "customer": {"businessName": "Manish"}, "creationDate": "10/06/2016", "totalAmount": "£90", "paid": false},
{"number": "5", "customer": {"businessName": "Manish"}, "creationDate": "09/08/2016", "totalAmount": "£12", "paid": true},
{"number": "6", "customer": {"businessName": "Manish"}, "creationDate": "01/06/2016", "totalAmount": "£34", "paid": true},
{"number": "7", "customer": {"businessName": "Manish"}, "creationDate": "24/11/2015", "totalAmount": "£98", "paid": false}
]
}- Lets install json-loader using the command
npm install --save-dev json-loader - Lets configure webpack to bundle the json into
app.bundle.js
{
test: /\.json$/,
exclude: /node_modules/,
loader: 'json-loader'
}- Lets enhance
index.jsto consume the data from data file and pass it down to components
import invoiceList from './data/InvoiceList';var invoices = invoiceList;
ReactDOM.render(
InvoiceAppFactory(invoices),document.getElementById('root')
);"Enhance InvoiceApp.js to pass the data into sub-components"
<div>
<AppHeader createInvoice = {this.setCreateInvoice.bind(this)} listInvoice = {this.setListInvoice.bind(this)}/>
<InvoiceSummary user={this.props.user}/>
{this.state.isCreateInvoice ? <InvoiceCreate/> : <InvoiceList invoices={this.props.invoices}/> }
</div>- Change
InvoiceList.jsto use the props.
for (var i = 0; i < this.props.invoices.length; i++) {
rows.push(<InvoiceRow key={i} invoice={this.props.invoices[i]}/>);
}- npm install
- npm run dev
- Open url http://localhost:8000/index.html