Skip to content

Commit

Permalink
Merge pull request #63 from devgateway/feature/OCVN-93/comparison
Browse files Browse the repository at this point in the history
OCVN-93 Refactored data flow, now using Nuclear getters
Comparison charts are no longer stacked in a row
Enforcing uniformity for X axis
Enforcing left to right year order
Fixed several issues causing apps to crash when navigating to tender tab while data wasn't loaded
  • Loading branch information
alexeisavca committed Mar 28, 2016
2 parents fb3de3d + a2e1312 commit a18c7e1
Show file tree
Hide file tree
Showing 12 changed files with 301 additions and 160 deletions.
8 changes: 7 additions & 1 deletion ui/components/app/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,13 @@ export default class App extends React.Component{
locations={globalState.getIn(['data', 'locations'])}
/>
);
default: return <Tender {...props}/>
default: return (
<Tender
actions={actions}
state={state.get('tender')}
width={width}
/>
)
}
}(globalState.get('tab'), this.props)}
<div className="col-sm-12 thick-red-line"></div>
Expand Down
8 changes: 4 additions & 4 deletions ui/components/comparison.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@ import Component from "./pure-render-component";

export default class Comparison extends Component{
render(){
var {state, Component, title} = this.props;
var {data, yAxisRange, xAxisRange} = state;
if(!data) return null;
var {state, Component, title, width} = this.props;
var {data, yAxisRange, xAxisRange, criteriaNames} = state;
return (
<div>
<h3 className="page-header">{title}</h3>
Expand All @@ -15,7 +14,8 @@ export default class Comparison extends Component{
xAxisRange={xAxisRange}
data={datum}
pageHeaderTitle={false}
title={index}
title={criteriaNames[index] || "Other"}
width={width}
/>
))}
</div>
Expand Down
11 changes: 9 additions & 2 deletions ui/components/plot.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,17 @@ Plotly.register([
]);

class Plot extends Component{
componentDidMount(){
getDecoratedLayout(){
var {pageHeaderTitle, title, xAxisRange, yAxisRange} = this.props;
var layout = this.getLayout();
if(!pageHeaderTitle) layout.title = title;
if(xAxisRange) layout.xaxis.range = xAxisRange;
if(yAxisRange) layout.yaxis.range = yAxisRange;
Plotly.newPlot(this.refs.chartContainer, this.getData(), layout);
return layout;
}

componentDidMount(){
Plotly.newPlot(this.refs.chartContainer, this.getData(), this.getDecoratedLayout());
}

componentDidUpdate(prevProps){
Expand All @@ -22,6 +26,9 @@ class Plot extends Component{
this.refs.chartContainer.data = this.getData();
Plotly.redraw(this.refs.chartContainer);
}
if(['pageHeaderTitle', 'title', 'xAxisRange', 'yAxisRange'].some(prop => prevProps[prop] != this.props[prop])){
Plotly.relayout(this.refs.chartContainer, this.getDecoratedLayout());
}
}

render(){
Expand Down
21 changes: 7 additions & 14 deletions ui/components/tender/bidding-period.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,18 @@ import Plot from "../plot";
import {pluck} from "../../tools";

export default class BiddingPeriod extends Plot{
getTitle() {
return "Bidding period"
}

getData(){
var {data, years} = this.props;
if(!data) return [];
var filteredData = data.filter(({year}) => years.get(year, false));
var filteredYears = filteredData.map(pluck('year'));
var {data} = this.props;
var years = data.map(pluck('year'));
return [{
x: filteredData.map(pluck('tender')),
y: filteredYears,
x: data.map(pluck('tender')),
y: years,
name: "Tender",
type: "bar",
orientation: 'h'
}, {
x: filteredData.map(pluck('award')),
y: filteredYears,
x: data.map(pluck('award')),
y: years,
name: "Award",
type: "bar",
orientation: 'h'
Expand All @@ -34,8 +28,7 @@ export default class BiddingPeriod extends Plot{
title: "Days",
titlefont: {
color: "#cc3c3b"
},
range: this.props.xAxisRange
}
},
yaxis: {
title: "Years",
Expand Down
8 changes: 3 additions & 5 deletions ui/components/tender/cancelled.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,10 @@ export default class BiddingPeriod extends Plot{
}

getData(){
var {years, data} = this.props;
if(!data) return [];
var filteredData = data.filter(({_id}) => years.get(_id, false));
var {data} = this.props;
return [{
x: filteredData.map(pluck('_id')),
y: filteredData.map(pluck('totalCancelledTendersAmount')),
x: data.map(pluck('year')),
y: data.map(pluck('count')),
type: 'scatter',
fill: 'tonexty'
}];
Expand Down
29 changes: 16 additions & 13 deletions ui/components/tender/cost-effectiveness.jsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,21 @@
import Plot from "../plot";
import {pluck} from "../../tools";

export default class CostEffectiveness extends Plot{
getTitle(){
return "Cost effectiveness";
}

getData(){
var {years, data} = this.props;
var filteredData = data.filter(({year}) => years.get(+year, false));
var filteredYears = filteredData.map(datum => datum.year);
var {data} = this.props;
var years = data.map(pluck('year'));

var bidPrice = {
x: filteredYears,
y: filteredData.map(datum => datum.tender),
x: years,
y: data.map(pluck('tender')),
name: 'Bid price',
type: 'bar'
};

var diff = {
x: filteredYears,
y: filteredData.map(datum => datum.diff),
x: years,
y: data.map(pluck('diff')),
name: 'Difference',
type: 'bar'
};
Expand All @@ -31,10 +27,17 @@ export default class CostEffectiveness extends Plot{
return {
barmode: "stack",
xaxis: {
type: "category"
title: "Years",
type: "category",
titlefont: {
color: "#cc3c3b"
}
},
yaxis: {
range: this.props.yAxisRange
title: "Amount",
titlefont: {
color: "#cc3c3b"
}
}
}
}
Expand Down
20 changes: 14 additions & 6 deletions ui/components/tender/funding-by-bid-type.jsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,30 @@
import Plot from "../plot";
import {pluck} from "../../tools";

export default class FundingByBidType extends Plot {
getTitle() {
return "Funding by bid type"
}

getData(){
return [{
x: this.props.data.map(datum => datum._id),
y: this.props.data.map(datum => datum.totalTenderAmount),
x: this.props.data.map(pluck('_id')),
y: this.props.data.map(pluck('totalTenderAmount')),
type: 'bar'
}];
}

getLayout(){
return {
xaxis: {
title: "Category",
type: "category",
titlefont: {
color: "#cc3c3b"
}
},
yaxis: {
range: this.props.yAxisRange
title: "Amount",
titlefont: {
color: "#cc3c3b"
}
}
}
}
Expand Down
125 changes: 66 additions & 59 deletions ui/components/tender/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,59 +8,7 @@ import {toImmutable} from "nuclear-js";
import Comparison from "../comparison";
import {pluck} from "../../tools";

var sortByYear = (a, b) => {
if(a.year < b.year) return -1;
if(a.year == b.year) return 0;
if(a.year < b.year) return 1;
};

var filterBidTypeData = years => data => data
.filter(bidType => years.get(bidType.get('year'), false))
.groupBy(bidType => bidType.get('procurementMethodDetails'))
.map(bidTypes => bidTypes.reduce((reducedBidType, bidType) => {
return {
_id: bidType.get('procurementMethodDetails') || "unspecified",
totalTenderAmount: reducedBidType.totalTenderAmount + bidType.get('totalTenderAmount')
}
}, {
totalTenderAmount: 0
}))
.toArray();

export default class Tender extends Component{
getCostEffectiveness(){
var globalState = this.props.state.get('globalState');
var selectedYears = globalState.getIn(['filters', 'years']);
var width = globalState.get('contentWidth');
var data = globalState.get('data');
if(globalState.get('compareBy')){
var costEffectivenessData = globalState.getIn(['comparisonData', 'costEffectiveness']);
if(!costEffectivenessData) return null;
var minValue = Math.min.apply(Math, costEffectivenessData.map(datum =>
Math.min.apply(Math, datum.map(pluck('tender')))
));
var maxValue = Math.max.apply(Math, costEffectivenessData.map(datum =>
Math.max.apply(Math, datum.map(pluck('tender')))
));
return (
<Comparison
years={selectedYears}
width={width}
data={costEffectivenessData}
Component={CostEffectiveness}
yAxisRange={[minValue, maxValue]}
/>
)
} else {
return (
<CostEffectiveness
years={selectedYears}
width={width}
data={data.get('costEffectiveness')}/>
)
}
}

getBiddingPeriod(){
var globalState = this.props.state.get('globalState');
var selectedYears = globalState.getIn(['filters', 'years']);
Expand Down Expand Up @@ -170,15 +118,74 @@ export default class Tender extends Component{
}

render(){
var {state} = this.props;
var globalState = state.get('globalState');
var selectedYears = globalState.getIn(['filters', 'years']);
var width = globalState.get('contentWidth');
var data = globalState.get('data');
var {state, width} = this.props;
var {compare, costEffectiveness, bidPeriod, bidType, cancelled} = state;
return (
<div className="col-sm-12 content">
{compare ?
<Comparison
width={width}
state={costEffectiveness}
Component={CostEffectiveness}
title="Cost effectiveness"
/>
:
<CostEffectiveness
title="Cost effectiveness"
data={costEffectiveness}
width={width}
/>
}

{compare ?
<Comparison
width={width}
state={bidPeriod}
Component={BiddingPeriod}
title="Bid period"
/>
:
<BiddingPeriod
title="Bid period"
data={bidPeriod}
width={width}
/>
}

{compare ?
<Comparison
width={width}
state={bidType}
Component={FundingByBidType}
title="Funding by bid type"
/>
:
<FundingByBidType
title="Funding by bid type"
data={bidType}
width={width}
/>
}

{compare ?
<Comparison
width={width}
state={cancelled}
Component={Cancelled}
title="Cancelled funding"
/>
:
<Cancelled
title="Cancelled funding"
data={cancelled}
width={width}
/>
}
</div>
);

return (
<div className="col-sm-12 content">
{this.getCostEffectiveness()}
{this.getBiddingPeriod()}
{this.getBidType()}
{this.getCancelled()}
</div>
Expand Down
1 change: 1 addition & 0 deletions ui/flux/actions/constants.es6
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,5 @@ export default keyMirror({
BID_PERIOD_COMPARISON_DATA_UPDATED: null,
BID_TYPE_COMPARISON_DATA_UPDATED: null,
CANCELLED_COMPARISON_DATA_UPDATED: null,
COMPARISON_CRITERIA_NAMES_UPDATED: null
})
Loading

0 comments on commit a18c7e1

Please sign in to comment.