@@ -22,6 +29,7 @@
export default {
props: {
heading: { type: String, default: '' },
+ infoText: { type: String, default: '' },
point: { type: [Number, String], default: '' },
trend: { type: Number, default: null },
index: { type: Number, default: null },
diff --git a/app/javascript/dashboard/components/widgets/chart/BarChart.js b/app/javascript/dashboard/components/widgets/chart/BarChart.js
index fbe42bc5c..a4dca263b 100644
--- a/app/javascript/dashboard/components/widgets/chart/BarChart.js
+++ b/app/javascript/dashboard/components/widgets/chart/BarChart.js
@@ -3,7 +3,7 @@ import { Bar } from 'vue-chartjs';
const fontFamily =
'-apple-system,system-ui,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif';
-const chartOptions = {
+const defaultChartOptions = {
responsive: true,
maintainAspectRatio: false,
legend: {
@@ -11,10 +11,14 @@ const chartOptions = {
fontFamily,
},
},
+ datasets: {
+ bar: {
+ barPercentage: 1.0,
+ },
+ },
scales: {
xAxes: [
{
- barPercentage: 1.1,
ticks: {
fontFamily,
},
@@ -39,8 +43,20 @@ const chartOptions = {
export default {
extends: Bar,
- props: ['collection'],
+ props: {
+ collection: {
+ type: Object,
+ default: () => {},
+ },
+ chartOptions: {
+ type: Object,
+ default: () => {},
+ },
+ },
mounted() {
- this.renderChart(this.collection, chartOptions);
+ this.renderChart(this.collection, {
+ ...defaultChartOptions,
+ ...this.chartOptions,
+ });
},
};
diff --git a/app/javascript/dashboard/i18n/locale/en/report.json b/app/javascript/dashboard/i18n/locale/en/report.json
index 053c730e1..ed730d798 100644
--- a/app/javascript/dashboard/i18n/locale/en/report.json
+++ b/app/javascript/dashboard/i18n/locale/en/report.json
@@ -18,12 +18,14 @@
"DESC": "( Total )"
},
"FIRST_RESPONSE_TIME": {
- "NAME": "First response time",
- "DESC": "( Avg )"
+ "NAME": "First Response Time",
+ "DESC": "( Avg )",
+ "INFO_TEXT": "Total number of conversations used for computation:"
},
"RESOLUTION_TIME": {
"NAME": "Resolution Time",
- "DESC": "( Avg )"
+ "DESC": "( Avg )",
+ "INFO_TEXT": "Total number of conversations used for computation:"
},
"RESOLUTION_COUNT": {
"NAME": "Resolution Count",
@@ -98,12 +100,14 @@
"DESC": "( Total )"
},
"FIRST_RESPONSE_TIME": {
- "NAME": "First response time",
- "DESC": "( Avg )"
+ "NAME": "First Response Time",
+ "DESC": "( Avg )",
+ "INFO_TEXT": "Total number of conversations used for computation:"
},
"RESOLUTION_TIME": {
"NAME": "Resolution Time",
- "DESC": "( Avg )"
+ "DESC": "( Avg )",
+ "INFO_TEXT": "Total number of conversations used for computation:"
},
"RESOLUTION_COUNT": {
"NAME": "Resolution Count",
@@ -161,12 +165,14 @@
"DESC": "( Total )"
},
"FIRST_RESPONSE_TIME": {
- "NAME": "First response time",
- "DESC": "( Avg )"
+ "NAME": "First Response Time",
+ "DESC": "( Avg )",
+ "INFO_TEXT": "Total number of conversations used for computation:"
},
"RESOLUTION_TIME": {
"NAME": "Resolution Time",
- "DESC": "( Avg )"
+ "DESC": "( Avg )",
+ "INFO_TEXT": "Total number of conversations used for computation:"
},
"RESOLUTION_COUNT": {
"NAME": "Resolution Count",
@@ -224,12 +230,14 @@
"DESC": "( Total )"
},
"FIRST_RESPONSE_TIME": {
- "NAME": "First response time",
- "DESC": "( Avg )"
+ "NAME": "First Response Time",
+ "DESC": "( Avg )",
+ "INFO_TEXT": "Total number of conversations used for computation:"
},
"RESOLUTION_TIME": {
"NAME": "Resolution Time",
- "DESC": "( Avg )"
+ "DESC": "( Avg )",
+ "INFO_TEXT": "Total number of conversations used for computation:"
},
"RESOLUTION_COUNT": {
"NAME": "Resolution Count",
@@ -287,12 +295,14 @@
"DESC": "( Total )"
},
"FIRST_RESPONSE_TIME": {
- "NAME": "First response time",
- "DESC": "( Avg )"
+ "NAME": "First Response Time",
+ "DESC": "( Avg )",
+ "INFO_TEXT": "Total number of conversations used for computation:"
},
"RESOLUTION_TIME": {
"NAME": "Resolution Time",
- "DESC": "( Avg )"
+ "DESC": "( Avg )",
+ "INFO_TEXT": "Total number of conversations used for computation:"
},
"RESOLUTION_COUNT": {
"NAME": "Resolution Count",
diff --git a/app/javascript/dashboard/mixins/reportMixin.js b/app/javascript/dashboard/mixins/reportMixin.js
index 8ef7fdfe5..d5bb8c9cc 100644
--- a/app/javascript/dashboard/mixins/reportMixin.js
+++ b/app/javascript/dashboard/mixins/reportMixin.js
@@ -5,6 +5,7 @@ export default {
computed: {
...mapGetters({
accountSummary: 'getAccountSummary',
+ accountReport: 'getAccountReports',
}),
calculateTrend() {
return metric_key => {
@@ -19,15 +20,32 @@ export default {
},
displayMetric() {
return metric_key => {
- if (
- ['avg_first_response_time', 'avg_resolution_time'].includes(
- metric_key
- )
- ) {
+ if (this.isAverageMetricType(metric_key)) {
return formatTime(this.accountSummary[metric_key]);
}
return this.accountSummary[metric_key];
};
},
+ displayInfoText() {
+ return metric_key => {
+ if (this.metrics[this.currentSelection].KEY !== metric_key) {
+ return '';
+ }
+ if (this.isAverageMetricType(metric_key)) {
+ const total = this.accountReport.data
+ .map(item => item.count)
+ .reduce((prev, curr) => prev + curr, 0);
+ return `${this.metrics[this.currentSelection].INFO_TEXT} ${total}`;
+ }
+ return '';
+ };
+ },
+ isAverageMetricType() {
+ return metric_key => {
+ return ['avg_first_response_time', 'avg_resolution_time'].includes(
+ metric_key
+ );
+ };
+ },
},
};
diff --git a/app/javascript/dashboard/mixins/specs/reportMixin.spec.js b/app/javascript/dashboard/mixins/specs/reportMixin.spec.js
index 003295f95..ca1c45751 100644
--- a/app/javascript/dashboard/mixins/specs/reportMixin.spec.js
+++ b/app/javascript/dashboard/mixins/specs/reportMixin.spec.js
@@ -11,6 +11,7 @@ describe('reportMixin', () => {
beforeEach(() => {
getters = {
getAccountSummary: () => reportFixtures.summary,
+ getAccountReports: () => reportFixtures.report,
};
store = new Vuex.Store({ getters });
});
@@ -38,4 +39,67 @@ describe('reportMixin', () => {
expect(wrapper.vm.calculateTrend('conversations_count')).toEqual(25);
expect(wrapper.vm.calculateTrend('resolutions_count')).toEqual(0);
});
+
+ it('display info text', () => {
+ const Component = {
+ render() {},
+ title: 'TestComponent',
+ mixins: [reportMixin],
+ data() {
+ return {
+ currentSelection: 0,
+ };
+ },
+ computed: {
+ metrics() {
+ return [
+ {
+ DESC: '( Avg )',
+ INFO_TEXT: 'Total number of conversations used for computation:',
+ KEY: 'avg_first_response_time',
+ NAME: 'First Response Time',
+ },
+ ];
+ },
+ },
+ };
+ const wrapper = shallowMount(Component, { store, localVue });
+ expect(wrapper.vm.displayInfoText('avg_first_response_time')).toEqual(
+ 'Total number of conversations used for computation: 4'
+ );
+ });
+
+ it('do not display info text', () => {
+ const Component = {
+ render() {},
+ title: 'TestComponent',
+ mixins: [reportMixin],
+ data() {
+ return {
+ currentSelection: 0,
+ };
+ },
+ computed: {
+ metrics() {
+ return [
+ {
+ DESC: '( Total )',
+ INFO_TEXT: '',
+ KEY: 'conversation_count',
+ NAME: 'Conversations',
+ },
+ {
+ DESC: '( Avg )',
+ INFO_TEXT: 'Total number of conversations used for computation:',
+ KEY: 'avg_first_response_time',
+ NAME: 'First Response Time',
+ },
+ ];
+ },
+ },
+ };
+ const wrapper = shallowMount(Component, { store, localVue });
+ expect(wrapper.vm.displayInfoText('conversation_count')).toEqual('');
+ expect(wrapper.vm.displayInfoText('incoming_messages_count')).toEqual('');
+ });
});
diff --git a/app/javascript/dashboard/mixins/specs/reportMixinFixtures.js b/app/javascript/dashboard/mixins/specs/reportMixinFixtures.js
index 5c8315ab1..8402c3940 100644
--- a/app/javascript/dashboard/mixins/specs/reportMixinFixtures.js
+++ b/app/javascript/dashboard/mixins/specs/reportMixinFixtures.js
@@ -15,4 +15,15 @@ export default {
},
resolutions_count: 3,
},
+ report: {
+ data: [
+ { value: '0.00', timestamp: 1647541800, count: 0 },
+ { value: '0.00', timestamp: 1647628200, count: 0 },
+ { value: '0.00', timestamp: 1647714600, count: 0 },
+ { value: '0.00', timestamp: 1647801000, count: 0 },
+ { value: '0.01', timestamp: 1647887400, count: 4 },
+ { value: '0.00', timestamp: 1647973800, count: 0 },
+ { value: '0.00', timestamp: 1648060200, count: 0 },
+ ],
+ },
};
diff --git a/app/javascript/dashboard/routes/dashboard/settings/reports/Index.vue b/app/javascript/dashboard/routes/dashboard/settings/reports/Index.vue
index fe0240d74..1b46c474a 100644
--- a/app/javascript/dashboard/routes/dashboard/settings/reports/Index.vue
+++ b/app/javascript/dashboard/routes/dashboard/settings/reports/Index.vue
@@ -22,6 +22,7 @@
:key="metric.NAME"
:desc="metric.DESC"
:heading="metric.NAME"
+ :info-text="displayInfoText(metric.KEY)"
:index="index"
:on-click="changeSelection"
:point="displayMetric(metric.KEY)"
@@ -35,7 +36,11 @@
:message="$t('REPORT.LOADING_CHART')"
/>
-
+
{{ $t('REPORT.NO_ENOUGH_DATA') }}
@@ -49,7 +54,7 @@ import { mapGetters } from 'vuex';
import fromUnixTime from 'date-fns/fromUnixTime';
import format from 'date-fns/format';
import ReportFilterSelector from './components/FilterSelector';
-import { GROUP_BY_FILTER } from './constants';
+import { GROUP_BY_FILTER, METRIC_CHART } from './constants';
import reportMixin from '../../../../mixins/reportMixin';
const REPORTS_KEYS = {
@@ -108,16 +113,38 @@ export default {
}
return format(fromUnixTime(element.timestamp), 'dd-MMM-yyyy');
});
- const data = this.accountReport.data.map(element => element.value);
+
+ const datasets = METRIC_CHART[
+ this.metrics[this.currentSelection].KEY
+ ].datasets.map(dataset => {
+ switch (dataset.type) {
+ case 'bar':
+ return {
+ ...dataset,
+ yAxisID: 'y-left',
+ label: this.metrics[this.currentSelection].NAME,
+ data: this.accountReport.data.map(element => element.value),
+ };
+ case 'line':
+ return {
+ ...dataset,
+ yAxisID: 'y-right',
+ label: this.metrics[0].NAME,
+ data: this.accountReport.data.map(element => element.count),
+ };
+ default:
+ return dataset;
+ }
+ });
+
return {
labels,
- datasets: [
- {
- label: this.metrics[this.currentSelection].NAME,
- backgroundColor: '#1f93ff',
- data,
- },
- ],
+ datasets,
+ };
+ },
+ chartOptions() {
+ return {
+ scales: METRIC_CHART[this.metrics[this.currentSelection].KEY].scales,
};
},
metrics() {
@@ -133,6 +160,7 @@ export default {
NAME: this.$t(`REPORT.METRICS.${key}.NAME`),
KEY: REPORTS_KEYS[key],
DESC: this.$t(`REPORT.METRICS.${key}.DESC`),
+ INFO_TEXT: this.$t(`REPORT.METRICS.${key}.INFO_TEXT`),
}));
},
},
diff --git a/app/javascript/dashboard/routes/dashboard/settings/reports/components/WootReports.vue b/app/javascript/dashboard/routes/dashboard/settings/reports/components/WootReports.vue
index 8ce264080..c5cf3993b 100644
--- a/app/javascript/dashboard/routes/dashboard/settings/reports/components/WootReports.vue
+++ b/app/javascript/dashboard/routes/dashboard/settings/reports/components/WootReports.vue
@@ -25,6 +25,7 @@
:key="metric.NAME"
:desc="metric.DESC"
:heading="metric.NAME"
+ :info-text="displayInfoText(metric.KEY)"
:index="index"
:on-click="changeSelection"
:point="displayMetric(metric.KEY)"
@@ -41,6 +42,7 @@
{{ $t('REPORT.NO_ENOUGH_DATA') }}
@@ -55,7 +57,7 @@
import ReportFilters from './ReportFilters';
import fromUnixTime from 'date-fns/fromUnixTime';
import format from 'date-fns/format';
-import { GROUP_BY_FILTER } from '../constants';
+import { GROUP_BY_FILTER, METRIC_CHART } from '../constants';
import reportMixin from '../../../../../mixins/reportMixin';
const REPORTS_KEYS = {
@@ -137,16 +139,38 @@ export default {
}
return format(fromUnixTime(element.timestamp), 'dd-MMM-yyyy');
});
- const data = this.accountReport.data.map(element => element.value);
+
+ const datasets = METRIC_CHART[
+ this.metrics[this.currentSelection].KEY
+ ].datasets.map(dataset => {
+ switch (dataset.type) {
+ case 'bar':
+ return {
+ ...dataset,
+ yAxisID: 'y-left',
+ label: this.metrics[this.currentSelection].NAME,
+ data: this.accountReport.data.map(element => element.value),
+ };
+ case 'line':
+ return {
+ ...dataset,
+ yAxisID: 'y-right',
+ label: this.metrics[0].NAME,
+ data: this.accountReport.data.map(element => element.count),
+ };
+ default:
+ return dataset;
+ }
+ });
+
return {
labels,
- datasets: [
- {
- label: this.metrics[this.currentSelection].NAME,
- backgroundColor: '#1f93ff',
- data,
- },
- ],
+ datasets,
+ };
+ },
+ chartOptions() {
+ return {
+ scales: METRIC_CHART[this.metrics[this.currentSelection].KEY].scales,
};
},
metrics() {
@@ -168,6 +192,7 @@ export default {
NAME: this.$t(`REPORT.METRICS.${key}.NAME`),
KEY: REPORTS_KEYS[key],
DESC: this.$t(`REPORT.METRICS.${key}.DESC`),
+ INFO_TEXT: this.$t(`REPORT.METRICS.${key}.INFO_TEXT`),
}));
},
},
diff --git a/app/javascript/dashboard/routes/dashboard/settings/reports/constants.js b/app/javascript/dashboard/routes/dashboard/settings/reports/constants.js
index f285061cb..91348543e 100644
--- a/app/javascript/dashboard/routes/dashboard/settings/reports/constants.js
+++ b/app/javascript/dashboard/routes/dashboard/settings/reports/constants.js
@@ -4,3 +4,142 @@ export const GROUP_BY_FILTER = {
3: { id: 3, period: 'month' },
4: { id: 4, period: 'year' },
};
+
+export const CHART_FONT_FAMILY =
+ '-apple-system,system-ui,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif';
+
+export const DEFAULT_LINE_CHART = {
+ type: 'line',
+ fill: false,
+ borderColor: '#779BBB',
+ pointBackgroundColor: '#779BBB',
+};
+
+export const DEFAULT_BAR_CHART = {
+ type: 'bar',
+ backgroundColor: 'rgb(31, 147, 255, 0.5)',
+};
+
+export const DEFAULT_CHART = {
+ datasets: [DEFAULT_BAR_CHART],
+ scales: {
+ xAxes: [
+ {
+ ticks: {
+ fontFamily: CHART_FONT_FAMILY,
+ },
+ gridLines: {
+ drawOnChartArea: false,
+ },
+ },
+ ],
+ yAxes: [
+ {
+ id: 'y-left',
+ type: 'linear',
+ position: 'left',
+ ticks: {
+ fontFamily: CHART_FONT_FAMILY,
+ beginAtZero: true,
+ stepSize: 1,
+ },
+ gridLines: {
+ drawOnChartArea: false,
+ },
+ },
+ ],
+ },
+};
+
+export const METRIC_CHART = {
+ conversations_count: DEFAULT_CHART,
+ incoming_messages_count: DEFAULT_CHART,
+ outgoing_messages_count: DEFAULT_CHART,
+ avg_first_response_time: {
+ datasets: [DEFAULT_BAR_CHART, DEFAULT_LINE_CHART],
+ scales: {
+ xAxes: [
+ {
+ ticks: {
+ fontFamily: CHART_FONT_FAMILY,
+ },
+ gridLines: {
+ drawOnChartArea: false,
+ },
+ },
+ ],
+ yAxes: [
+ {
+ id: 'y-left',
+ type: 'linear',
+ position: 'left',
+ ticks: {
+ fontFamily: CHART_FONT_FAMILY,
+ beginAtZero: true,
+ precision: 2,
+ },
+ gridLines: {
+ drawOnChartArea: false,
+ },
+ },
+ {
+ id: 'y-right',
+ type: 'linear',
+ position: 'right',
+ ticks: {
+ fontFamily: CHART_FONT_FAMILY,
+ beginAtZero: true,
+ stepSize: 1,
+ },
+ gridLines: {
+ drawOnChartArea: false,
+ },
+ },
+ ],
+ },
+ },
+ avg_resolution_time: {
+ datasets: [DEFAULT_BAR_CHART, DEFAULT_LINE_CHART],
+ scales: {
+ xAxes: [
+ {
+ ticks: {
+ fontFamily: CHART_FONT_FAMILY,
+ },
+ gridLines: {
+ drawOnChartArea: false,
+ },
+ },
+ ],
+ yAxes: [
+ {
+ id: 'y-left',
+ type: 'linear',
+ position: 'left',
+ ticks: {
+ fontFamily: CHART_FONT_FAMILY,
+ beginAtZero: true,
+ precision: 2,
+ },
+ gridLines: {
+ drawOnChartArea: false,
+ },
+ },
+ {
+ id: 'y-right',
+ type: 'linear',
+ position: 'right',
+ ticks: {
+ fontFamily: CHART_FONT_FAMILY,
+ beginAtZero: true,
+ stepSize: 1,
+ },
+ gridLines: {
+ drawOnChartArea: false,
+ },
+ },
+ ],
+ },
+ },
+ resolutions_count: DEFAULT_CHART,
+};