From ade89dcec2de70612d3e7840370d30eeb0e1127c Mon Sep 17 00:00:00 2001 From: Stezido Date: Fri, 29 Nov 2019 14:05:30 +0100 Subject: [PATCH] ui: Change analytics access without permissions If workflowitems are redacted the projected budget (total budget) is still visible but the charts are hidden and a well displayed warning is shown --- frontend/src/languages/english.js | 2 + .../src/pages/Analytics/ProjectAnalytics.js | 50 ++++++++++++------- .../pages/Analytics/ProjectAnalyticsDialog.js | 9 +++- .../pages/Analytics/SubProjectAnalytics.js | 46 +++++++++++------ .../Analytics/SubProjectAnalyticsDialog.js | 10 +++- frontend/src/pages/Analytics/reducer.js | 17 ++++--- .../src/pages/SubProjects/ProjectDetails.js | 2 +- .../src/pages/Workflows/SubProjectDetails.js | 6 +-- 8 files changed, 94 insertions(+), 48 deletions(-) diff --git a/frontend/src/languages/english.js b/frontend/src/languages/english.js index a4ce46b2ef..037fe93f2b 100644 --- a/frontend/src/languages/english.js +++ b/frontend/src/languages/english.js @@ -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", diff --git a/frontend/src/pages/Analytics/ProjectAnalytics.js b/frontend/src/pages/Analytics/ProjectAnalytics.js index 51d882ad9b..171fa9f1b5 100644 --- a/frontend/src/pages/Analytics/ProjectAnalytics.js +++ b/frontend/src/pages/Analytics/ProjectAnalytics.js @@ -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"; @@ -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(); @@ -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() { @@ -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 ? ( -
+ + return !this.props.isFetchingKPIs ? ( + <>
@@ -133,19 +145,21 @@ class ProjectAnalytics extends React.Component {
- + {this.props.canShowAnalytics ? ( + + ) : ( + {strings.analytics.insufficient_permissions_text} + )}
-
- ) : this.props.canShowAnalytics === undefined ? null : ( -
Insufficient permissions.
- ); + + ) : null; } } @@ -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"]) }; }; diff --git a/frontend/src/pages/Analytics/ProjectAnalyticsDialog.js b/frontend/src/pages/Analytics/ProjectAnalyticsDialog.js index 3ef05594f9..8e27d8ae4f 100644 --- a/frontend/src/pages/Analytics/ProjectAnalyticsDialog.js +++ b/frontend/src/pages/Analytics/ProjectAnalyticsDialog.js @@ -51,7 +51,8 @@ const ProjectAnalyticsDialog = ({ displayCurrency, closeAnalyticsDialog, storeDisplayCurrency, - getExchangeRates + getExchangeRates, + projectProjectedBudgets }) => (
Loading...
}> - +
diff --git a/frontend/src/pages/Analytics/SubProjectAnalytics.js b/frontend/src/pages/Analytics/SubProjectAnalytics.js index 49dc590aee..26dd21c71e 100644 --- a/frontend/src/pages/Analytics/SubProjectAnalytics.js +++ b/frontend/src/pages/Analytics/SubProjectAnalytics.js @@ -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(); @@ -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() { @@ -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 ? ( -
+ return !this.props.isFetchingKPIs ? ( + <>
@@ -117,18 +129,20 @@ class SubprojectAnalytics extends React.Component {
- + {this.props.canShowAnalytics ? ( + + ) : ( + {strings.analytics.insufficient_permissions_text} + )}
-
- ) : this.props.canShowAnalytics === undefined ? null : ( -
Insufficient permissions.
- ); + + ) : null; } } @@ -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"]) }; }; diff --git a/frontend/src/pages/Analytics/SubProjectAnalyticsDialog.js b/frontend/src/pages/Analytics/SubProjectAnalyticsDialog.js index d833b0e954..35ede24860 100644 --- a/frontend/src/pages/Analytics/SubProjectAnalyticsDialog.js +++ b/frontend/src/pages/Analytics/SubProjectAnalyticsDialog.js @@ -52,7 +52,8 @@ const SubProjectAnalyticsDialog = ({ displayCurrency, closeAnalyticsDialog, storeDisplayCurrency, - getExchangeRates + getExchangeRates, + projectedBudgets }) => (
Loading...
}> - {" "} +
diff --git a/frontend/src/pages/Analytics/reducer.js b/frontend/src/pages/Analytics/reducer.js index 9d249e8b18..1493989010 100644 --- a/frontend/src/pages/Analytics/reducer.js +++ b/frontend/src/pages/Analytics/reducer.js @@ -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"; /** @@ -45,16 +46,22 @@ 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), @@ -62,12 +69,10 @@ export default function detailviewReducer(state = defaultState, action) { 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), diff --git a/frontend/src/pages/SubProjects/ProjectDetails.js b/frontend/src/pages/SubProjects/ProjectDetails.js index 3066239d2c..dc847f9c49 100644 --- a/frontend/src/pages/SubProjects/ProjectDetails.js +++ b/frontend/src/pages/SubProjects/ProjectDetails.js @@ -184,7 +184,7 @@ const ProjectDetails = props => { - + ); }; diff --git a/frontend/src/pages/Workflows/SubProjectDetails.js b/frontend/src/pages/Workflows/SubProjectDetails.js index ff408c2f10..f6d4c7126c 100644 --- a/frontend/src/pages/Workflows/SubProjectDetails.js +++ b/frontend/src/pages/Workflows/SubProjectDetails.js @@ -100,7 +100,7 @@ const SubProjectDetails = ({ closeSubproject, canCloseSubproject, openAnalyticsDialog, - ...props + projectedBudgets }) => { const mappedStatus = statusMapping(status); const statusIcon = statusIconMapping[status]; @@ -139,7 +139,7 @@ const SubProjectDetails = ({ - {props.projectedBudgets.map(budget => ( + {projectedBudgets.map(budget => ( {budget.organization} {toAmountString(budget.value)} @@ -196,7 +196,7 @@ const SubProjectDetails = ({ - + ); };