Skip to content

Commit

Permalink
ui: Change analytics access without permissions
Browse files Browse the repository at this point in the history
If workflowitems are redacted the projected budget (total budget) is still visible but the charts are hidden and a well displayed warning is shown
  • Loading branch information
Stezido committed Nov 29, 2019
1 parent dfb31fc commit ade89dc
Show file tree
Hide file tree
Showing 8 changed files with 94 additions and 48 deletions.
2 changes: 2 additions & 0 deletions frontend/src/languages/english.js
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,8 @@ const en = {
available_unspent_budget: "Available Unspent Budget",
converted_amount: "Converted Amount",
disbursed_budget_ratio: "Disbursed Budget Ratio",
insufficient_permissions_text:
"One or more workflowitem are redacted. The analytics are hidden because they would be falsified.",
project_analytics: "Project Analytics",
projected_budget_ratio: "Projected Budget Ratio",
projected_budgets_distribution: "Projected Budgets Distribution",
Expand Down
50 changes: 32 additions & 18 deletions frontend/src/pages/Analytics/ProjectAnalytics.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import Typography from "@material-ui/core/Typography";
import React from "react";
import { Doughnut } from "react-chartjs-2";
import { connect } from "react-redux";

import { toAmountString, toJS } from "../../helper";
import strings from "../../localizeStrings";
import { getProjectKPIs, resetKPIs } from "./actions";
Expand Down Expand Up @@ -38,12 +37,22 @@ const styles = {
display: "flex",
flexDirection: "column",
marginBottom: "24px"
},
warning: {
backgroundColor: "rgb(255, 165, 0, 0.7)",
color: "black",
borderStyle: "solid",
borderRadius: "4px",
borderColor: "orange",
padding: "2px",
textAlign: "center"
}
};

class ProjectAnalytics extends React.Component {
componentDidMount() {
this.props.getProjectKPIs(this.props.projectId);
this.props.getExchangeRates(this.props.indicatedCurrency);
}
componentWillUnmount() {
this.props.resetKPIs();
Expand All @@ -52,7 +61,9 @@ class ProjectAnalytics extends React.Component {
convertToSelectedCurrency(amount, sourceCurrency) {
const sourceExchangeRate = this.props.exchangeRates[sourceCurrency];
const targetExchangeRate = this.props.exchangeRates[this.props.indicatedCurrency];
return sourceExchangeRate && targetExchangeRate ? targetExchangeRate / sourceExchangeRate * parseFloat(amount) : 0;
return sourceExchangeRate && targetExchangeRate
? (targetExchangeRate / sourceExchangeRate) * parseFloat(amount)
: 0;
}

convertTotalBudget() {
Expand Down Expand Up @@ -95,8 +106,9 @@ class ProjectAnalytics extends React.Component {
const disbursedBudget = this.props.disbursedBudget.reduce((acc, next) => {
return acc + this.convertToSelectedCurrency(next.budget, next.currency);
}, 0);
return this.props.canShowAnalytics ? (
<div>

return !this.props.isFetchingKPIs ? (
<>
<div style={styles.container}>
<div style={styles.topContainer}>
<div style={styles.table}>
Expand Down Expand Up @@ -133,19 +145,21 @@ class ProjectAnalytics extends React.Component {
</Table>
</div>
</div>
<Dashboard
indicatedCurrency={this.props.indicatedCurrency}
projectedBudget={projectedBudget}
totalBudgets={totalBudgets}
totalBudget={totalBudget}
disbursedBudget={disbursedBudget}
assignedBudget={assignedBudget}
/>
{this.props.canShowAnalytics ? (
<Dashboard
indicatedCurrency={this.props.indicatedCurrency}
projectedBudget={projectedBudget}
totalBudgets={totalBudgets}
totalBudget={totalBudget}
disbursedBudget={disbursedBudget}
assignedBudget={assignedBudget}
/>
) : (
<Typography style={styles.warning}>{strings.analytics.insufficient_permissions_text}</Typography>
)}
</div>
</div>
) : this.props.canShowAnalytics === undefined ? null : (
<div>Insufficient permissions.</div>
);
</>
) : null;
}
}

Expand Down Expand Up @@ -310,10 +324,10 @@ const mapStateToProps = state => {
projectedBudget: state.getIn(["analytics", "project", "projectedBudget"]),
assignedBudget: state.getIn(["analytics", "project", "assignedBudget"]),
disbursedBudget: state.getIn(["analytics", "project", "disbursedBudget"]),
totalBudget: state.getIn(["analytics", "project", "totalBudget"]),
indicatedCurrency: state.getIn(["analytics", "currency"]),
exchangeRates: state.getIn(["analytics", "exchangeRates"]),
canShowAnalytics: state.getIn(["analytics", "canShowAnalytics"])
canShowAnalytics: state.getIn(["analytics", "canShowAnalytics"]),
isFetchingKPIs: state.getIn(["analytics", "isFetchingKPIs"])
};
};

Expand Down
9 changes: 7 additions & 2 deletions frontend/src/pages/Analytics/ProjectAnalyticsDialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ const ProjectAnalyticsDialog = ({
displayCurrency,
closeAnalyticsDialog,
storeDisplayCurrency,
getExchangeRates
getExchangeRates,
projectProjectedBudgets
}) => (
<Dialog
fullScreen
Expand Down Expand Up @@ -91,7 +92,11 @@ const ProjectAnalyticsDialog = ({
</AppBar>
<div style={styles.container}>
<Suspense fallback={<div>Loading...</div>}>
<ProjectAnalytics projectId={projectId} />
<ProjectAnalytics
projectId={projectId}
totalBudget={projectProjectedBudgets}
getExchangeRates={getExchangeRates}
/>
</Suspense>
</div>
</Dialog>
Expand Down
46 changes: 30 additions & 16 deletions frontend/src/pages/Analytics/SubProjectAnalytics.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,22 @@ const styles = {
display: "flex",
flexDirection: "column",
marginBottom: "24px"
},
warning: {
backgroundColor: "rgb(255, 165, 0, 0.7)",
color: "black",
borderStyle: "solid",
borderRadius: "4px",
borderColor: "orange",
padding: "2px",
textAlign: "center"
}
};

class SubprojectAnalytics extends React.Component {
componentDidMount() {
this.props.getSubProjectKPIs(this.props.projectId, this.props.subProjectId);
this.props.getExchangeRates(this.props.indicatedCurrency);
}
componentWillUnmount() {
this.props.resetKPIs();
Expand All @@ -59,7 +69,9 @@ class SubprojectAnalytics extends React.Component {
convertToSelectedCurrency(amount, sourceCurrency) {
const sourceExchangeRate = this.props.exchangeRates[sourceCurrency];
const targetExchangeRate = this.props.exchangeRates[this.props.indicatedCurrency];
return sourceExchangeRate && targetExchangeRate ? targetExchangeRate / sourceExchangeRate * parseFloat(amount) : 0;
return sourceExchangeRate && targetExchangeRate
? (targetExchangeRate / sourceExchangeRate) * parseFloat(amount)
: 0;
}

convertProjectedBudget() {
Expand All @@ -79,8 +91,8 @@ class SubprojectAnalytics extends React.Component {
}, 0);
const convertedAssignedBudget = this.convertToSelectedCurrency(assignedBudget, subProjectCurrency);
const convertedDisbursedBudget = this.convertToSelectedCurrency(disbursedBudget, subProjectCurrency);
return this.props.canShowAnalytics ? (
<div>
return !this.props.isFetchingKPIs ? (
<>
<div style={styles.container}>
<div style={styles.topContainer}>
<div style={styles.table}>
Expand Down Expand Up @@ -117,18 +129,20 @@ class SubprojectAnalytics extends React.Component {
</Table>
</div>
</div>
<Dashboard
indicatedCurrency={this.props.indicatedCurrency}
projectedBudget={projectedBudget}
projectedBudgets={projectedBudgets}
disbursedBudget={convertedDisbursedBudget}
assignedBudget={convertedAssignedBudget}
/>
{this.props.canShowAnalytics ? (
<Dashboard
indicatedCurrency={this.props.indicatedCurrency}
projectedBudget={projectedBudget}
projectedBudgets={projectedBudgets}
disbursedBudget={convertedDisbursedBudget}
assignedBudget={convertedAssignedBudget}
/>
) : (
<Typography style={styles.warning}>{strings.analytics.insufficient_permissions_text}</Typography>
)}
</div>
</div>
) : this.props.canShowAnalytics === undefined ? null : (
<div>Insufficient permissions.</div>
);
</>
) : null;
}
}

Expand Down Expand Up @@ -245,11 +259,11 @@ const mapStateToProps = state => {
return {
subProjectCurrency: state.getIn(["analytics", "subproject", "currency"]),
indicatedCurrency: state.getIn(["analytics", "currency"]),
projectedBudgets: state.getIn(["analytics", "subproject", "projectedBudgets"]),
assignedBudget: state.getIn(["analytics", "subproject", "assignedBudget"]),
disbursedBudget: state.getIn(["analytics", "subproject", "disbursedBudget"]),
exchangeRates: state.getIn(["analytics", "exchangeRates"]),
canShowAnalytics: state.getIn(["analytics", "canShowAnalytics"])
canShowAnalytics: state.getIn(["analytics", "canShowAnalytics"]),
isFetchingKPIs: state.getIn(["analytics", "isFetchingKPIs"])
};
};

Expand Down
10 changes: 8 additions & 2 deletions frontend/src/pages/Analytics/SubProjectAnalyticsDialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ const SubProjectAnalyticsDialog = ({
displayCurrency,
closeAnalyticsDialog,
storeDisplayCurrency,
getExchangeRates
getExchangeRates,
projectedBudgets
}) => (
<Dialog
fullScreen
Expand Down Expand Up @@ -92,7 +93,12 @@ const SubProjectAnalyticsDialog = ({
</AppBar>
<div style={styles.container}>
<Suspense fallback={<div>Loading...</div>}>
<SubProjectAnalytics projectId={projectId} subProjectId={subProjectId} />{" "}
<SubProjectAnalytics
projectId={projectId}
subProjectId={subProjectId}
projectedBudgets={projectedBudgets}
getExchangeRates={getExchangeRates}
/>
</Suspense>
</div>
</Dialog>
Expand Down
17 changes: 11 additions & 6 deletions frontend/src/pages/Analytics/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import {
OPEN_ANALYTICS_DIALOG,
RESET_KPIS,
STORE_DISPLAY_CURRENCY,
GET_SUBPROJECT_KPIS_FAIL
GET_SUBPROJECT_KPIS_FAIL,
GET_SUBPROJECT_KPIS
} from "./actions";

/**
Expand Down Expand Up @@ -45,29 +46,33 @@ const defaultState = fromJS({
},
dialogOpen: false,
exchangeRates: {},
canShowAnalytics: false
canShowAnalytics: false,
isFetchingKPIs: false
});

export default function detailviewReducer(state = defaultState, action) {
switch (action.type) {
case GET_PROJECT_KPIS:
return state.set("canShowAnalytics", undefined);
case GET_SUBPROJECT_KPIS:
return state.set("isFetchingKPIs", true);
case GET_PROJECT_KPIS_FAIL:
case GET_SUBPROJECT_KPIS_FAIL:
return state.merge({ canShowAnalytics: false, isFetchingKPIs: false });
case GET_PROJECT_KPIS_SUCCESS:
return state.merge({
canShowAnalytics: true,
isFetchingKPIs: false,
project: {
totalBudget: fromJS(action.totalBudget),
projectedBudget: fromJS(action.projectedBudget),
assignedBudget: fromJS(action.assignedBudget),
disbursedBudget: fromJS(action.disbursedBudget)
}
});
case GET_PROJECT_KPIS_FAIL:
case GET_SUBPROJECT_KPIS_FAIL:
return state.set("canShowAnalytics", false);
case GET_SUBPROJECT_KPIS_SUCCESS:
return state.merge({
canShowAnalytics: true,
isFetchingKPIs: false,
subproject: {
currency: action.subProjectCurrency,
projectedBudgets: fromJS(action.projectedBudgets),
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/pages/SubProjects/ProjectDetails.js
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ const ProjectDetails = props => {
</ListItem>
</List>
</Card>
<ProjectAnalyticsDialog projectId={projectId} />
<ProjectAnalyticsDialog projectId={projectId} projectProjectedBudgets={projectProjectedBudgets} />
</div>
);
};
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/pages/Workflows/SubProjectDetails.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ const SubProjectDetails = ({
closeSubproject,
canCloseSubproject,
openAnalyticsDialog,
...props
projectedBudgets
}) => {
const mappedStatus = statusMapping(status);
const statusIcon = statusIconMapping[status];
Expand Down Expand Up @@ -139,7 +139,7 @@ const SubProjectDetails = ({
</TableRow>
</TableHead>
<TableBody>
{props.projectedBudgets.map(budget => (
{projectedBudgets.map(budget => (
<TableRow key={budget.organization + budget.currencyCode}>
<TableCell>{budget.organization}</TableCell>
<TableCell align="right">{toAmountString(budget.value)}</TableCell>
Expand Down Expand Up @@ -196,7 +196,7 @@ const SubProjectDetails = ({
</ListItem>
</List>
</Card>
<SubProjectAnalyticsDialog projectId={parentProject.id} subProjectId={id} />
<SubProjectAnalyticsDialog projectId={parentProject.id} subProjectId={id} projectedBudgets={projectedBudgets} />
</div>
);
};
Expand Down

0 comments on commit ade89dc

Please sign in to comment.