feat: Group by filter in reports (#3973)
This commit is contained in:
@@ -49,3 +49,4 @@ exclude_patterns:
|
|||||||
- 'app/javascript/dashboard/routes/dashboard/contacts/contactFilterItems/index.js'
|
- 'app/javascript/dashboard/routes/dashboard/contacts/contactFilterItems/index.js'
|
||||||
- 'app/javascript/dashboard/routes/dashboard/settings/automation/constants.js'
|
- 'app/javascript/dashboard/routes/dashboard/settings/automation/constants.js'
|
||||||
- 'app/javascript/dashboard/components/widgets/FilterInput/FilterOperatorTypes.js'
|
- 'app/javascript/dashboard/components/widgets/FilterInput/FilterOperatorTypes.js'
|
||||||
|
- 'app/javascript/dashboard/routes/dashboard/settings/reports/constants.js'
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ class V2::ReportBuilder
|
|||||||
include DateRangeHelper
|
include DateRangeHelper
|
||||||
attr_reader :account, :params
|
attr_reader :account, :params
|
||||||
|
|
||||||
|
DEFAULT_GROUP_BY = 'day'.freeze
|
||||||
|
|
||||||
def initialize(account, params)
|
def initialize(account, params)
|
||||||
@account = account
|
@account = account
|
||||||
@params = params
|
@params = params
|
||||||
@@ -64,26 +66,30 @@ class V2::ReportBuilder
|
|||||||
|
|
||||||
def conversations_count
|
def conversations_count
|
||||||
scope.conversations
|
scope.conversations
|
||||||
.group_by_day(:created_at, range: range, default_value: 0)
|
.group_by_period(params[:group_by] || DEFAULT_GROUP_BY,
|
||||||
|
:created_at, range: range, default_value: 0, permit: %w[day week month year])
|
||||||
.count
|
.count
|
||||||
end
|
end
|
||||||
|
|
||||||
def incoming_messages_count
|
def incoming_messages_count
|
||||||
scope.messages.incoming.unscope(:order)
|
scope.messages.incoming.unscope(:order)
|
||||||
.group_by_day(:created_at, range: range, default_value: 0)
|
.group_by_period(params[:group_by] || DEFAULT_GROUP_BY,
|
||||||
|
:created_at, range: range, default_value: 0, permit: %w[day week month year])
|
||||||
.count
|
.count
|
||||||
end
|
end
|
||||||
|
|
||||||
def outgoing_messages_count
|
def outgoing_messages_count
|
||||||
scope.messages.outgoing.unscope(:order)
|
scope.messages.outgoing.unscope(:order)
|
||||||
.group_by_day(:created_at, range: range, default_value: 0)
|
.group_by_period(params[:group_by] || DEFAULT_GROUP_BY,
|
||||||
|
:created_at, range: range, default_value: 0, permit: %w[day week month year])
|
||||||
.count
|
.count
|
||||||
end
|
end
|
||||||
|
|
||||||
def resolutions_count
|
def resolutions_count
|
||||||
scope.conversations
|
scope.conversations
|
||||||
.resolved
|
.resolved
|
||||||
.group_by_day(:created_at, range: range, default_value: 0)
|
.group_by_period(params[:group_by] || DEFAULT_GROUP_BY,
|
||||||
|
:created_at, range: range, default_value: 0, permit: %w[day week month year])
|
||||||
.count
|
.count
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -46,7 +46,8 @@ class Api::V2::Accounts::ReportsController < Api::V1::Accounts::BaseController
|
|||||||
type: params[:type].to_sym,
|
type: params[:type].to_sym,
|
||||||
since: params[:since],
|
since: params[:since],
|
||||||
until: params[:until],
|
until: params[:until],
|
||||||
id: params[:id]
|
id: params[:id],
|
||||||
|
group_by: params[:group_by]
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -56,7 +57,8 @@ class Api::V2::Accounts::ReportsController < Api::V1::Accounts::BaseController
|
|||||||
type: params[:type].to_sym,
|
type: params[:type].to_sym,
|
||||||
since: params[:since],
|
since: params[:since],
|
||||||
until: params[:until],
|
until: params[:until],
|
||||||
id: params[:id]
|
id: params[:id],
|
||||||
|
group_by: params[:group_by]
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -6,15 +6,15 @@ class ReportsAPI extends ApiClient {
|
|||||||
super('reports', { accountScoped: true, apiVersion: 'v2' });
|
super('reports', { accountScoped: true, apiVersion: 'v2' });
|
||||||
}
|
}
|
||||||
|
|
||||||
getReports(metric, since, until, type = 'account', id) {
|
getReports(metric, since, until, type = 'account', id, group_by) {
|
||||||
return axios.get(`${this.url}`, {
|
return axios.get(`${this.url}`, {
|
||||||
params: { metric, since, until, type, id },
|
params: { metric, since, until, type, id, group_by },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getSummary(since, until, type = 'account', id) {
|
getSummary(since, until, type = 'account', id, group_by) {
|
||||||
return axios.get(`${this.url}/summary`, {
|
return axios.get(`${this.url}/summary`, {
|
||||||
params: { since, until, type, id },
|
params: { since, until, type, id, group_by },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -59,7 +59,24 @@
|
|||||||
"CUSTOM_DATE_RANGE": {
|
"CUSTOM_DATE_RANGE": {
|
||||||
"CONFIRM": "Apply",
|
"CONFIRM": "Apply",
|
||||||
"PLACEHOLDER": "Select date range"
|
"PLACEHOLDER": "Select date range"
|
||||||
}
|
},
|
||||||
|
"GROUP_BY_FILTER_DROPDOWN_LABEL": "Group By",
|
||||||
|
"GROUP_BY_DAY_OPTIONS": [{ "id": 1, "groupBy": "Day" }],
|
||||||
|
"GROUP_BY_WEEK_OPTIONS": [
|
||||||
|
{ "id": 1, "groupBy": "Day" },
|
||||||
|
{ "id": 2, "groupBy": "Week" }
|
||||||
|
],
|
||||||
|
"GROUP_BY_MONTH_OPTIONS": [
|
||||||
|
{ "id": 1, "groupBy": "Day" },
|
||||||
|
{ "id": 2, "groupBy": "Week" },
|
||||||
|
{ "id": 3, "groupBy": "Month" }
|
||||||
|
],
|
||||||
|
"GROUP_BY_YEAR_OPTIONS": [
|
||||||
|
{ "id": 1, "groupBy": "Day" },
|
||||||
|
{ "id": 2, "groupBy": "Week" },
|
||||||
|
{ "id": 3, "groupBy": "Month" },
|
||||||
|
{ "id": 4, "groupBy": "Year" }
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"AGENT_REPORTS": {
|
"AGENT_REPORTS": {
|
||||||
"HEADER": "Agents Overview",
|
"HEADER": "Agents Overview",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="column content-box">
|
<div class="column content-box">
|
||||||
<report-date-range-selector @date-range-change="onDateRangeChange" />
|
<report-filter-selector @date-range-change="onDateRangeChange" />
|
||||||
<csat-metrics />
|
<csat-metrics />
|
||||||
<csat-table :page-index="pageIndex" @page-change="onPageNumberChange" />
|
<csat-table :page-index="pageIndex" @page-change="onPageNumberChange" />
|
||||||
</div>
|
</div>
|
||||||
@@ -8,14 +8,14 @@
|
|||||||
<script>
|
<script>
|
||||||
import CsatMetrics from './components/CsatMetrics';
|
import CsatMetrics from './components/CsatMetrics';
|
||||||
import CsatTable from './components/CsatTable';
|
import CsatTable from './components/CsatTable';
|
||||||
import ReportDateRangeSelector from './components/DateRangeSelector';
|
import ReportFilterSelector from './components/FilterSelector';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'CsatResponses',
|
name: 'CsatResponses',
|
||||||
components: {
|
components: {
|
||||||
CsatMetrics,
|
CsatMetrics,
|
||||||
CsatTable,
|
CsatTable,
|
||||||
ReportDateRangeSelector,
|
ReportFilterSelector,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return { pageIndex: 1, from: 0, to: 0 };
|
return { pageIndex: 1, from: 0, to: 0 };
|
||||||
|
|||||||
@@ -9,7 +9,12 @@
|
|||||||
{{ $t('REPORT.DOWNLOAD_AGENT_REPORTS') }}
|
{{ $t('REPORT.DOWNLOAD_AGENT_REPORTS') }}
|
||||||
</woot-button>
|
</woot-button>
|
||||||
|
|
||||||
<report-date-range-selector @date-range-change="onDateRangeChange" />
|
<report-filter-selector
|
||||||
|
:selected-group-by-filter="selectedGroupByFilter"
|
||||||
|
:filter-items-list="filterItemsList"
|
||||||
|
@date-range-change="onDateRangeChange"
|
||||||
|
@filter-change="onFilterChange"
|
||||||
|
/>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<woot-report-stats-card
|
<woot-report-stats-card
|
||||||
v-for="(metric, index) in metrics"
|
v-for="(metric, index) in metrics"
|
||||||
@@ -41,7 +46,8 @@
|
|||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import fromUnixTime from 'date-fns/fromUnixTime';
|
import fromUnixTime from 'date-fns/fromUnixTime';
|
||||||
import format from 'date-fns/format';
|
import format from 'date-fns/format';
|
||||||
import ReportDateRangeSelector from './components/DateRangeSelector';
|
import ReportFilterSelector from './components/FilterSelector';
|
||||||
|
import { GROUP_BY_FILTER } from './constants';
|
||||||
|
|
||||||
const REPORTS_KEYS = {
|
const REPORTS_KEYS = {
|
||||||
CONVERSATIONS: 'conversations_count',
|
CONVERSATIONS: 'conversations_count',
|
||||||
@@ -54,10 +60,17 @@ const REPORTS_KEYS = {
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
ReportDateRangeSelector,
|
ReportFilterSelector,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return { from: 0, to: 0, currentSelection: 0 };
|
return {
|
||||||
|
from: 0,
|
||||||
|
to: 0,
|
||||||
|
currentSelection: 0,
|
||||||
|
groupBy: GROUP_BY_FILTER[1],
|
||||||
|
filterItemsList: this.$t('REPORT.GROUP_BY_DAY_OPTIONS'),
|
||||||
|
selectedGroupByFilter: {},
|
||||||
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
@@ -69,9 +82,28 @@ export default {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
if (!this.accountReport.data.length) return {};
|
if (!this.accountReport.data.length) return {};
|
||||||
const labels = this.accountReport.data.map(element =>
|
const labels = this.accountReport.data.map(element => {
|
||||||
format(fromUnixTime(element.timestamp), 'dd/MMM')
|
if (this.groupBy.period === GROUP_BY_FILTER[2].period) {
|
||||||
);
|
let week_date = new Date(fromUnixTime(element.timestamp));
|
||||||
|
const first_day = week_date.getDate() - week_date.getDay();
|
||||||
|
const last_day = first_day + 6;
|
||||||
|
|
||||||
|
const week_first_date = new Date(week_date.setDate(first_day));
|
||||||
|
const week_last_date = new Date(week_date.setDate(last_day));
|
||||||
|
|
||||||
|
return `${format(week_first_date, 'dd/MM/yy')} - ${format(
|
||||||
|
week_last_date,
|
||||||
|
'dd/MM/yy'
|
||||||
|
)}`;
|
||||||
|
}
|
||||||
|
if (this.groupBy.period === GROUP_BY_FILTER[3].period) {
|
||||||
|
return format(fromUnixTime(element.timestamp), 'MMM-yyyy');
|
||||||
|
}
|
||||||
|
if (this.groupBy.period === GROUP_BY_FILTER[4].period) {
|
||||||
|
return format(fromUnixTime(element.timestamp), 'yyyy');
|
||||||
|
}
|
||||||
|
return format(fromUnixTime(element.timestamp), 'dd-MMM-yyyy');
|
||||||
|
});
|
||||||
const data = this.accountReport.data.map(element => element.value);
|
const data = this.accountReport.data.map(element => element.value);
|
||||||
return {
|
return {
|
||||||
labels,
|
labels,
|
||||||
@@ -102,16 +134,21 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
fetchAllData() {
|
fetchAllData() {
|
||||||
const { from, to } = this;
|
const { from, to, groupBy } = this;
|
||||||
this.$store.dispatch('fetchAccountSummary', { from, to });
|
this.$store.dispatch('fetchAccountSummary', {
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
groupBy: groupBy.period,
|
||||||
|
});
|
||||||
this.fetchChartData();
|
this.fetchChartData();
|
||||||
},
|
},
|
||||||
fetchChartData() {
|
fetchChartData() {
|
||||||
const { from, to } = this;
|
const { from, to, groupBy } = this;
|
||||||
this.$store.dispatch('fetchAccountReport', {
|
this.$store.dispatch('fetchAccountReport', {
|
||||||
metric: this.metrics[this.currentSelection].KEY,
|
metric: this.metrics[this.currentSelection].KEY,
|
||||||
from,
|
from,
|
||||||
to,
|
to,
|
||||||
|
groupBy: groupBy.period,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
downloadAgentReports() {
|
downloadAgentReports() {
|
||||||
@@ -126,11 +163,37 @@ export default {
|
|||||||
this.currentSelection = index;
|
this.currentSelection = index;
|
||||||
this.fetchChartData();
|
this.fetchChartData();
|
||||||
},
|
},
|
||||||
onDateRangeChange({ from, to }) {
|
onDateRangeChange({ from, to, groupBy }) {
|
||||||
this.from = from;
|
this.from = from;
|
||||||
this.to = to;
|
this.to = to;
|
||||||
|
this.filterItemsList = this.fetchFilterItems(groupBy);
|
||||||
|
const filterItems = this.filterItemsList.filter(
|
||||||
|
item => item.id === this.groupBy.id
|
||||||
|
);
|
||||||
|
if (filterItems.length > 0) {
|
||||||
|
this.selectedGroupByFilter = filterItems[0];
|
||||||
|
} else {
|
||||||
|
this.selectedGroupByFilter = this.filterItemsList[0];
|
||||||
|
this.groupBy = GROUP_BY_FILTER[this.selectedGroupByFilter.id];
|
||||||
|
}
|
||||||
this.fetchAllData();
|
this.fetchAllData();
|
||||||
},
|
},
|
||||||
|
onFilterChange(payload) {
|
||||||
|
this.groupBy = GROUP_BY_FILTER[payload.id];
|
||||||
|
this.fetchAllData();
|
||||||
|
},
|
||||||
|
fetchFilterItems(group_by) {
|
||||||
|
switch (group_by) {
|
||||||
|
case GROUP_BY_FILTER[2].period:
|
||||||
|
return this.$t('REPORT.GROUP_BY_WEEK_OPTIONS');
|
||||||
|
case GROUP_BY_FILTER[3].period:
|
||||||
|
return this.$t('REPORT.GROUP_BY_MONTH_OPTIONS');
|
||||||
|
case GROUP_BY_FILTER[4].period:
|
||||||
|
return this.$t('REPORT.GROUP_BY_YEAR_OPTIONS');
|
||||||
|
default:
|
||||||
|
return this.$t('REPORT.GROUP_BY_DAY_OPTIONS');
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -23,6 +23,24 @@
|
|||||||
:placeholder="$t('REPORT.CUSTOM_DATE_RANGE.PLACEHOLDER')"
|
:placeholder="$t('REPORT.CUSTOM_DATE_RANGE.PLACEHOLDER')"
|
||||||
@change="onChange"
|
@change="onChange"
|
||||||
/>
|
/>
|
||||||
|
<div
|
||||||
|
v-if="notLast7Days"
|
||||||
|
class="small-12 medium-3 pull-right margin-left-small"
|
||||||
|
>
|
||||||
|
<p aria-hidden="true" class="hide">
|
||||||
|
{{ $t('REPORT.GROUP_BY_FILTER_DROPDOWN_LABEL') }}
|
||||||
|
</p>
|
||||||
|
<multiselect
|
||||||
|
v-model="currentSelectedFilter"
|
||||||
|
track-by="id"
|
||||||
|
label="groupBy"
|
||||||
|
:placeholder="$t('REPORT.GROUP_BY_FILTER_DROPDOWN_LABEL')"
|
||||||
|
:options="filterItemsList"
|
||||||
|
:allow-empty="false"
|
||||||
|
:show-labels="false"
|
||||||
|
@input="changeFilterSelection"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
@@ -31,16 +49,28 @@ const CUSTOM_DATE_RANGE_ID = 5;
|
|||||||
import subDays from 'date-fns/subDays';
|
import subDays from 'date-fns/subDays';
|
||||||
import startOfDay from 'date-fns/startOfDay';
|
import startOfDay from 'date-fns/startOfDay';
|
||||||
import getUnixTime from 'date-fns/getUnixTime';
|
import getUnixTime from 'date-fns/getUnixTime';
|
||||||
|
import { GROUP_BY_FILTER } from '../constants';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
WootDateRangePicker,
|
WootDateRangePicker,
|
||||||
},
|
},
|
||||||
|
props: {
|
||||||
|
filterItemsList: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
selectedGroupByFilter: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {},
|
||||||
|
},
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
currentDateRangeSelection: this.$t('REPORT.DATE_RANGE')[0],
|
currentDateRangeSelection: this.$t('REPORT.DATE_RANGE')[0],
|
||||||
dateRange: this.$t('REPORT.DATE_RANGE'),
|
dateRange: this.$t('REPORT.DATE_RANGE'),
|
||||||
customDateRange: [new Date(), new Date()],
|
customDateRange: [new Date(), new Date()],
|
||||||
|
currentSelectedFilter: null,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -68,13 +98,38 @@ export default {
|
|||||||
const fromDate = subDays(new Date(), diff);
|
const fromDate = subDays(new Date(), diff);
|
||||||
return this.fromCustomDate(fromDate);
|
return this.fromCustomDate(fromDate);
|
||||||
},
|
},
|
||||||
|
groupBy() {
|
||||||
|
if (this.isDateRangeSelected) {
|
||||||
|
return GROUP_BY_FILTER[4].period;
|
||||||
|
}
|
||||||
|
const groupRange = {
|
||||||
|
0: GROUP_BY_FILTER[1].period,
|
||||||
|
1: GROUP_BY_FILTER[2].period,
|
||||||
|
2: GROUP_BY_FILTER[3].period,
|
||||||
|
3: GROUP_BY_FILTER[3].period,
|
||||||
|
4: GROUP_BY_FILTER[3].period,
|
||||||
|
};
|
||||||
|
return groupRange[this.currentDateRangeSelection.id];
|
||||||
|
},
|
||||||
|
notLast7Days() {
|
||||||
|
return this.groupBy !== GROUP_BY_FILTER[1].period;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
filterItemsList() {
|
||||||
|
this.currentSelectedFilter = this.selectedGroupByFilter;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.onDateRangeChange();
|
this.onDateRangeChange();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
onDateRangeChange() {
|
onDateRangeChange() {
|
||||||
this.$emit('date-range-change', { from: this.from, to: this.to });
|
this.$emit('date-range-change', {
|
||||||
|
from: this.from,
|
||||||
|
to: this.to,
|
||||||
|
groupBy: this.groupBy,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
fromCustomDate(date) {
|
fromCustomDate(date) {
|
||||||
return getUnixTime(startOfDay(date));
|
return getUnixTime(startOfDay(date));
|
||||||
@@ -87,6 +142,9 @@ export default {
|
|||||||
this.customDateRange = value;
|
this.customDateRange = value;
|
||||||
this.onDateRangeChange();
|
this.onDateRangeChange();
|
||||||
},
|
},
|
||||||
|
changeFilterSelection() {
|
||||||
|
this.$emit('filter-change', this.currentSelectedFilter);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
@@ -127,6 +127,24 @@
|
|||||||
:placeholder="$t('REPORT.CUSTOM_DATE_RANGE.PLACEHOLDER')"
|
:placeholder="$t('REPORT.CUSTOM_DATE_RANGE.PLACEHOLDER')"
|
||||||
@change="onChange"
|
@change="onChange"
|
||||||
/>
|
/>
|
||||||
|
<div
|
||||||
|
v-if="notLast7Days"
|
||||||
|
class="small-12 medium-3 pull-right margin-left-small"
|
||||||
|
>
|
||||||
|
<p aria-hidden="true" class="hide">
|
||||||
|
{{ $t('REPORT.GROUP_BY_FILTER_DROPDOWN_LABEL') }}
|
||||||
|
</p>
|
||||||
|
<multiselect
|
||||||
|
v-model="currentSelectedGroupByFilter"
|
||||||
|
track-by="id"
|
||||||
|
label="groupBy"
|
||||||
|
:placeholder="$t('REPORT.GROUP_BY_FILTER_DROPDOWN_LABEL')"
|
||||||
|
:options="groupByFilterItemsList"
|
||||||
|
:allow-empty="false"
|
||||||
|
:show-labels="false"
|
||||||
|
@input="changeGroupByFilterSelection"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
@@ -136,6 +154,7 @@ import subDays from 'date-fns/subDays';
|
|||||||
import startOfDay from 'date-fns/startOfDay';
|
import startOfDay from 'date-fns/startOfDay';
|
||||||
import getUnixTime from 'date-fns/getUnixTime';
|
import getUnixTime from 'date-fns/getUnixTime';
|
||||||
import Thumbnail from 'dashboard/components/widgets/Thumbnail.vue';
|
import Thumbnail from 'dashboard/components/widgets/Thumbnail.vue';
|
||||||
|
import { GROUP_BY_FILTER } from '../constants';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@@ -147,10 +166,18 @@ export default {
|
|||||||
type: Array,
|
type: Array,
|
||||||
default: () => [],
|
default: () => [],
|
||||||
},
|
},
|
||||||
|
groupByFilterItemsList: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
type: {
|
type: {
|
||||||
type: String,
|
type: String,
|
||||||
default: 'agent',
|
default: 'agent',
|
||||||
},
|
},
|
||||||
|
selectedGroupByFilter: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -158,6 +185,7 @@ export default {
|
|||||||
currentDateRangeSelection: this.$t('REPORT.DATE_RANGE')[0],
|
currentDateRangeSelection: this.$t('REPORT.DATE_RANGE')[0],
|
||||||
dateRange: this.$t('REPORT.DATE_RANGE'),
|
dateRange: this.$t('REPORT.DATE_RANGE'),
|
||||||
customDateRange: [new Date(), new Date()],
|
customDateRange: [new Date(), new Date()],
|
||||||
|
currentSelectedGroupByFilter: null,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -194,19 +222,42 @@ export default {
|
|||||||
};
|
};
|
||||||
return typeLabels[this.type] || this.$t('FORMS.MULTISELECT.SELECT_ONE');
|
return typeLabels[this.type] || this.$t('FORMS.MULTISELECT.SELECT_ONE');
|
||||||
},
|
},
|
||||||
|
groupBy() {
|
||||||
|
if (this.isDateRangeSelected) {
|
||||||
|
return GROUP_BY_FILTER[4].period;
|
||||||
|
}
|
||||||
|
const groupRange = {
|
||||||
|
0: GROUP_BY_FILTER[1].period,
|
||||||
|
1: GROUP_BY_FILTER[2].period,
|
||||||
|
2: GROUP_BY_FILTER[3].period,
|
||||||
|
3: GROUP_BY_FILTER[3].period,
|
||||||
|
4: GROUP_BY_FILTER[3].period,
|
||||||
|
};
|
||||||
|
return groupRange[this.currentDateRangeSelection.id];
|
||||||
|
},
|
||||||
|
notLast7Days() {
|
||||||
|
return this.groupBy !== GROUP_BY_FILTER[1].period;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
filterItemsList(val) {
|
filterItemsList(val) {
|
||||||
this.currentSelectedFilter = val[0];
|
this.currentSelectedFilter = val[0];
|
||||||
this.changeFilterSelection();
|
this.changeFilterSelection();
|
||||||
},
|
},
|
||||||
|
groupByFilterItemsList() {
|
||||||
|
this.currentSelectedGroupByFilter = this.selectedGroupByFilter;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.onDateRangeChange();
|
this.onDateRangeChange();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
onDateRangeChange() {
|
onDateRangeChange() {
|
||||||
this.$emit('date-range-change', { from: this.from, to: this.to });
|
this.$emit('date-range-change', {
|
||||||
|
from: this.from,
|
||||||
|
to: this.to,
|
||||||
|
groupBy: this.groupBy,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
fromCustomDate(date) {
|
fromCustomDate(date) {
|
||||||
return getUnixTime(startOfDay(date));
|
return getUnixTime(startOfDay(date));
|
||||||
@@ -222,6 +273,9 @@ export default {
|
|||||||
this.customDateRange = value;
|
this.customDateRange = value;
|
||||||
this.onDateRangeChange();
|
this.onDateRangeChange();
|
||||||
},
|
},
|
||||||
|
changeGroupByFilterSelection() {
|
||||||
|
this.$emit('group-by-filter-change', this.currentSelectedGroupByFilter);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -12,8 +12,11 @@
|
|||||||
v-if="filterItemsList"
|
v-if="filterItemsList"
|
||||||
:type="type"
|
:type="type"
|
||||||
:filter-items-list="filterItemsList"
|
:filter-items-list="filterItemsList"
|
||||||
|
:group-by-filter-items-list="groupByfilterItemsList"
|
||||||
|
:selected-group-by-filter="selectedGroupByFilter"
|
||||||
@date-range-change="onDateRangeChange"
|
@date-range-change="onDateRangeChange"
|
||||||
@filter-change="onFilterChange"
|
@filter-change="onFilterChange"
|
||||||
|
@group-by-filter-change="onGroupByFilterChange"
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
<div v-if="filterItemsList.length" class="row">
|
<div v-if="filterItemsList.length" class="row">
|
||||||
@@ -51,6 +54,7 @@
|
|||||||
import ReportFilters from './ReportFilters';
|
import ReportFilters from './ReportFilters';
|
||||||
import fromUnixTime from 'date-fns/fromUnixTime';
|
import fromUnixTime from 'date-fns/fromUnixTime';
|
||||||
import format from 'date-fns/format';
|
import format from 'date-fns/format';
|
||||||
|
import { GROUP_BY_FILTER } from '../constants';
|
||||||
|
|
||||||
const REPORTS_KEYS = {
|
const REPORTS_KEYS = {
|
||||||
CONVERSATIONS: 'conversations_count',
|
CONVERSATIONS: 'conversations_count',
|
||||||
@@ -88,6 +92,9 @@ export default {
|
|||||||
to: 0,
|
to: 0,
|
||||||
currentSelection: 0,
|
currentSelection: 0,
|
||||||
selectedFilter: null,
|
selectedFilter: null,
|
||||||
|
groupBy: GROUP_BY_FILTER[1],
|
||||||
|
groupByfilterItemsList: this.$t('REPORT.GROUP_BY_DAY_OPTIONS'),
|
||||||
|
selectedGroupByFilter: null,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -105,9 +112,28 @@ export default {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
if (!this.accountReport.data.length) return {};
|
if (!this.accountReport.data.length) return {};
|
||||||
const labels = this.accountReport.data.map(element =>
|
const labels = this.accountReport.data.map(element => {
|
||||||
format(fromUnixTime(element.timestamp), 'dd/MMM')
|
if (this.groupBy.period === GROUP_BY_FILTER[2].period) {
|
||||||
);
|
let week_date = new Date(fromUnixTime(element.timestamp));
|
||||||
|
const first_day = week_date.getDate() - week_date.getDay();
|
||||||
|
const last_day = first_day + 6;
|
||||||
|
|
||||||
|
const week_first_date = new Date(week_date.setDate(first_day));
|
||||||
|
const week_last_date = new Date(week_date.setDate(last_day));
|
||||||
|
|
||||||
|
return `${format(week_first_date, 'dd/MM/yy')} - ${format(
|
||||||
|
week_last_date,
|
||||||
|
'dd/MM/yy'
|
||||||
|
)}`;
|
||||||
|
}
|
||||||
|
if (this.groupBy.period === GROUP_BY_FILTER[3].period) {
|
||||||
|
return format(fromUnixTime(element.timestamp), 'MMM-yyyy');
|
||||||
|
}
|
||||||
|
if (this.groupBy.period === GROUP_BY_FILTER[4].period) {
|
||||||
|
return format(fromUnixTime(element.timestamp), 'yyyy');
|
||||||
|
}
|
||||||
|
return format(fromUnixTime(element.timestamp), 'dd-MMM-yyyy');
|
||||||
|
});
|
||||||
const data = this.accountReport.data.map(element => element.value);
|
const data = this.accountReport.data.map(element => element.value);
|
||||||
return {
|
return {
|
||||||
labels,
|
labels,
|
||||||
@@ -148,24 +174,26 @@ export default {
|
|||||||
methods: {
|
methods: {
|
||||||
fetchAllData() {
|
fetchAllData() {
|
||||||
if (this.selectedFilter) {
|
if (this.selectedFilter) {
|
||||||
const { from, to } = this;
|
const { from, to, groupBy } = this;
|
||||||
this.$store.dispatch('fetchAccountSummary', {
|
this.$store.dispatch('fetchAccountSummary', {
|
||||||
from,
|
from,
|
||||||
to,
|
to,
|
||||||
type: this.type,
|
type: this.type,
|
||||||
id: this.selectedFilter.id,
|
id: this.selectedFilter.id,
|
||||||
|
groupBy: groupBy.period,
|
||||||
});
|
});
|
||||||
this.fetchChartData();
|
this.fetchChartData();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
fetchChartData() {
|
fetchChartData() {
|
||||||
const { from, to } = this;
|
const { from, to, groupBy } = this;
|
||||||
this.$store.dispatch('fetchAccountReport', {
|
this.$store.dispatch('fetchAccountReport', {
|
||||||
metric: this.metrics[this.currentSelection].KEY,
|
metric: this.metrics[this.currentSelection].KEY,
|
||||||
from,
|
from,
|
||||||
to,
|
to,
|
||||||
type: this.type,
|
type: this.type,
|
||||||
id: this.selectedFilter.id,
|
id: this.selectedFilter.id,
|
||||||
|
groupBy: groupBy.period,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
downloadReports() {
|
downloadReports() {
|
||||||
@@ -195,9 +223,19 @@ export default {
|
|||||||
this.currentSelection = index;
|
this.currentSelection = index;
|
||||||
this.fetchChartData();
|
this.fetchChartData();
|
||||||
},
|
},
|
||||||
onDateRangeChange({ from, to }) {
|
onDateRangeChange({ from, to, groupBy }) {
|
||||||
this.from = from;
|
this.from = from;
|
||||||
this.to = to;
|
this.to = to;
|
||||||
|
this.groupByfilterItemsList = this.fetchFilterItems(groupBy);
|
||||||
|
const filterItems = this.groupByfilterItemsList.filter(
|
||||||
|
item => item.id === this.groupBy.id
|
||||||
|
);
|
||||||
|
if (filterItems.length > 0) {
|
||||||
|
this.selectedGroupByFilter = filterItems[0];
|
||||||
|
} else {
|
||||||
|
this.selectedGroupByFilter = this.groupByfilterItemsList[0];
|
||||||
|
this.groupBy = GROUP_BY_FILTER[this.selectedGroupByFilter.id];
|
||||||
|
}
|
||||||
this.fetchAllData();
|
this.fetchAllData();
|
||||||
},
|
},
|
||||||
onFilterChange(payload) {
|
onFilterChange(payload) {
|
||||||
@@ -206,6 +244,22 @@ export default {
|
|||||||
this.fetchAllData();
|
this.fetchAllData();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
onGroupByFilterChange(payload) {
|
||||||
|
this.groupBy = GROUP_BY_FILTER[payload.id];
|
||||||
|
this.fetchAllData();
|
||||||
|
},
|
||||||
|
fetchFilterItems(group_by) {
|
||||||
|
switch (group_by) {
|
||||||
|
case GROUP_BY_FILTER[2].period:
|
||||||
|
return this.$t('REPORT.GROUP_BY_WEEK_OPTIONS');
|
||||||
|
case GROUP_BY_FILTER[3].period:
|
||||||
|
return this.$t('REPORT.GROUP_BY_MONTH_OPTIONS');
|
||||||
|
case GROUP_BY_FILTER[4].period:
|
||||||
|
return this.$t('REPORT.GROUP_BY_YEAR_OPTIONS');
|
||||||
|
default:
|
||||||
|
return this.$t('REPORT.GROUP_BY_DAY_OPTIONS');
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
export const GROUP_BY_FILTER = {
|
||||||
|
1: { id: 1, period: 'day' },
|
||||||
|
2: { id: 2, period: 'week' },
|
||||||
|
3: { id: 3, period: 'month' },
|
||||||
|
4: { id: 4, period: 'year' },
|
||||||
|
};
|
||||||
@@ -43,7 +43,8 @@ export const actions = {
|
|||||||
reportObj.from,
|
reportObj.from,
|
||||||
reportObj.to,
|
reportObj.to,
|
||||||
reportObj.type,
|
reportObj.type,
|
||||||
reportObj.id
|
reportObj.id,
|
||||||
|
reportObj.groupBy
|
||||||
).then(accountReport => {
|
).then(accountReport => {
|
||||||
let { data } = accountReport;
|
let { data } = accountReport;
|
||||||
data = data.filter(
|
data = data.filter(
|
||||||
@@ -68,7 +69,8 @@ export const actions = {
|
|||||||
reportObj.from,
|
reportObj.from,
|
||||||
reportObj.to,
|
reportObj.to,
|
||||||
reportObj.type,
|
reportObj.type,
|
||||||
reportObj.id
|
reportObj.id,
|
||||||
|
reportObj.groupBy
|
||||||
)
|
)
|
||||||
.then(accountSummary => {
|
.then(accountSummary => {
|
||||||
commit(types.default.SET_ACCOUNT_SUMMARY, accountSummary.data);
|
commit(types.default.SET_ACCOUNT_SUMMARY, accountSummary.data);
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ en:
|
|||||||
conversations_count: Conversations count
|
conversations_count: Conversations count
|
||||||
avg_first_response_time: Avg first response time (Minutes)
|
avg_first_response_time: Avg first response time (Minutes)
|
||||||
avg_resolution_time: Avg resolution time (Minutes)
|
avg_resolution_time: Avg resolution time (Minutes)
|
||||||
|
default_group_by: day
|
||||||
|
|
||||||
notifications:
|
notifications:
|
||||||
notification_title:
|
notification_title:
|
||||||
|
|||||||
@@ -147,6 +147,18 @@ describe ::V2::ReportBuilder do
|
|||||||
expect(metrics[:avg_resolution_time]).to be 0
|
expect(metrics[:avg_resolution_time]).to be 0
|
||||||
expect(metrics[:resolutions_count]).to be 0
|
expect(metrics[:resolutions_count]).to be 0
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'returns argument error for incorrect group by' do
|
||||||
|
params = {
|
||||||
|
type: :account,
|
||||||
|
since: (Time.zone.today - 3.days).to_time.to_i.to_s,
|
||||||
|
until: Time.zone.today.to_time.to_i.to_s,
|
||||||
|
group_by: 'test'.to_s
|
||||||
|
}
|
||||||
|
|
||||||
|
builder = V2::ReportBuilder.new(account, params)
|
||||||
|
expect { builder.summary }.to raise_error(ArgumentError)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when report type is label' do
|
context 'when report type is label' do
|
||||||
@@ -247,6 +259,38 @@ describe ::V2::ReportBuilder do
|
|||||||
expect(metrics[:avg_resolution_time]).to be 0
|
expect(metrics[:avg_resolution_time]).to be 0
|
||||||
expect(metrics[:resolutions_count]).to be 0
|
expect(metrics[:resolutions_count]).to be 0
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'returns summary for correct group by' do
|
||||||
|
params = {
|
||||||
|
type: :label,
|
||||||
|
id: label_2.id,
|
||||||
|
since: (Time.zone.today - 3.days).to_time.to_i.to_s,
|
||||||
|
until: Time.zone.today.to_time.to_i.to_s,
|
||||||
|
group_by: 'week'.to_s
|
||||||
|
}
|
||||||
|
|
||||||
|
builder = V2::ReportBuilder.new(account, params)
|
||||||
|
metrics = builder.summary
|
||||||
|
|
||||||
|
expect(metrics[:conversations_count]).to be 5
|
||||||
|
expect(metrics[:incoming_messages_count]).to be 5
|
||||||
|
expect(metrics[:outgoing_messages_count]).to be 15
|
||||||
|
expect(metrics[:avg_resolution_time]).to be 0
|
||||||
|
expect(metrics[:resolutions_count]).to be 0
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns argument error for incorrect group by' do
|
||||||
|
params = {
|
||||||
|
type: :label,
|
||||||
|
id: label_2.id,
|
||||||
|
since: (Time.zone.today - 3.days).to_time.to_i.to_s,
|
||||||
|
until: Time.zone.today.to_time.to_i.to_s,
|
||||||
|
group_by: 'test'.to_s
|
||||||
|
}
|
||||||
|
|
||||||
|
builder = V2::ReportBuilder.new(account, params)
|
||||||
|
expect { builder.summary }.to raise_error(ArgumentError)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user