(function () { 'use strict'; var data = createAtom(); function toKey(s, postfix) { if (postfix) { s += ' ' + postfix; } return s.replace(/[^0-9a-z ]/gi, '').toLowerCase().split(/\s+/).join('-'); } var Controls = React.createClass({ render: function () { var state = this.props.p; return React.DOM.div(null, operations.map(function (op) { var label = op.get('label'), operations = op.get('operations'); return React.DOM.label( { key: toKey(label), style: {display: 'block'}, className: (operations === state.get('active') ? 'highlight-op' : void 0) }, React.DOM.input({ type: 'checkbox', checked: state.get('operations').contains(operations), onChange: function (e) { if (e.target.checked) { data.swap(function (d) { return d.set('active', operations) .update('operations', function (ops) { return ops.add(operations); }); }); } else { data.swap(function (d) { return d.set('active', null) .update('operations', function (ops) { return ops.remove(operations); }); }); } } }), ' ', label ); })); } }); var UNIT_PRICE = 100; function format_qty(val) { if (val == null) { return ''; } if (val < 0) { return val; } return '+' + String(val); } function format_value(val) { if (isNaN(val)) { return ''; } if (val < 0) { return '-$' + String(Math.abs(val)); } return '$' + String(val); } var Chart = React.createClass({ render: function () { return React.DOM.div( null, React.DOM.table( {className: 'table table-condensed'}, React.DOM.thead( null, React.DOM.tr( null, React.DOM.th(null, "Location"), React.DOM.th({className: 'text-right'}, "Quantity"), React.DOM.th({className: 'text-right'}, "Value")) ), React.DOM.tbody( null, this.locations().map(function (data) { var highlight = false; return React.DOM.tr( {key: toKey(data.get('label'))}, React.DOM.th(null, data.get('level') ? '\u2001' : '', data.get('label')), React.DOM.td({ className: React.addons.classSet({ 'text-right': true, 'highlight-op': highlight })}, format_qty(data.get('qty'))), React.DOM.td({ className: React.addons.classSet({ 'text-right': true, 'highlight-op': highlight })}, format_value(data.get('qty') * UNIT_PRICE)) ); }) ) ) ); }, locations: function () { var data = this.props.p.get('operations'); // {location: total_qty} var totals = data.toIndexedSeq().flatten(true).reduce(function(acc, op) { return acc.update(op.get('location'), function (qty) { return (qty || 0) + op.get('qty'); }); }, Immutable.Map()); return locations.valueSeq().flatMap(function (loc) { var sub_locations = loc.get('locations').valueSeq().map(function (subloc) { return subloc.set('level', 1).set('qty', totals.get(subloc)); }); return Immutable.Seq.of(loc.set('level', 0)).concat(sub_locations); }); } }); data.addWatch('chart', function (k, m, prev, next) { React.render( React.createElement(Controls, {p: next}), document.getElementById('chart-of-locations-controls')); React.render( React.createElement(Chart, {p: next}), document.getElementById('chart-of-locations')); }); document.addEventListener('DOMContentLoaded', function () { var chart = document.querySelector('.chart-of-locations'); if (!chart) { return; } chart.setAttribute('id', 'chart-of-locations'); var controls = document.createElement('div'); controls.setAttribute('id', 'chart-of-locations-controls'); chart.parentNode.insertBefore(controls, chart); data.reset(Immutable.Map({ active: null, operations: Immutable.OrderedSet() })); }); var locations = Immutable.fromJS({ warehouse: { label: "Warehouse", locations: { zone1: {label: "Zone 1"}, zone2: {label: "Zone 2"} } }, partners: { label: "Partner Locations", locations: { customers: {label: "Customers"}, suppliers: {label: "Suppliers"} } }, virtual: { label: "Virtual Locations", locations: { initial: {label: "Initial Inventory"}, loss: {label: "Inventory Loss"}, scrap: {label: "Scrapped"}, manufacturing: {label: "Manufacturing"} } } }, function (k, v) { return Immutable.Iterable.isIndexed(v) ? v.toList() : v.toOrderedMap(); }); var operations = Immutable.fromJS([{ label: "Initial Inventory", operations: [ {location: locations.getIn(['virtual','locations','initial']), qty: -3}, {location: locations.getIn(['warehouse','locations','zone1']), qty: +3} ] }, { label: "Reception", operations: [ {location: locations.getIn(['partners','locations','suppliers']), qty: -2}, {location: locations.getIn(['warehouse','locations','zone1']), qty: +2} ] }, { label: "Delivery", operations: [ {location: locations.getIn(['warehouse','locations','zone1']), qty: -1}, {location: locations.getIn(['partners','locations','customers']), qty: +1} ] }, { label: "Return", operations: [ {location: locations.getIn(['partners','locations','customers']), qty: -1}, {location: locations.getIn(['warehouse','locations','zone1']), qty: +1} ] }, { label: "1 product broken in Zone 1", operations: [ {location: locations.getIn(['warehouse','locations','zone1']), qty: -1}, {location: locations.getIn(['virtual','locations','scrap']), qty: +1} ] }, { label: "Inventory check of Zone 1", operations: [ {location: locations.getIn(['warehouse','locations','zone1']), qty: -1}, {location: locations.getIn(['virtual','locations','loss']), qty: +1} ] }, { label: "Move from Zone 1 to Zone 2", operations: [ {location: locations.getIn(['warehouse','locations','zone1']), qty: -1}, {location: locations.getIn(['warehouse','locations','zone2']), qty: +1} ] }]); })();