Filter for live conversations

This commit is contained in:
Pranav
2024-02-13 14:36:04 -08:00
parent 1a272e7a18
commit bcd5633d2a
11 changed files with 288 additions and 52 deletions
+2 -2
View File
@@ -6,8 +6,8 @@ class LiveReportsAPI extends ApiClient {
super('live_reports', { accountScoped: true, apiVersion: 'v2' });
}
getConversationMetric() {
return axios.get(`${this.url}/conversation_metrics`);
getConversationMetric(params = {}) {
return axios.get(`${this.url}/conversation_metrics`, { params });
}
getGroupedConversations({ groupBy } = { groupBy: 'assignee_id' }) {
@@ -0,0 +1,30 @@
/* global axios */
import ApiClient from './ApiClient';
class SummaryReportsAPI extends ApiClient {
constructor() {
super('summary_reports', { accountScoped: true, apiVersion: 'v2' });
}
getTeamReports({ since, until, businessHours }) {
return axios.get(`${this.url}/team`, {
params: {
since,
until,
business_hours: businessHours,
},
});
}
getAgentReports({ since, until, businessHours }) {
return axios.get(`${this.url}/agents`, {
params: {
since,
until,
business_hours: businessHours,
},
});
}
}
export default new SummaryReportsAPI();
@@ -1,28 +1,13 @@
<template>
<div class="flex-1 overflow-auto p-4">
<div class="row">
<div class="column small-12 medium-8 conversation-metric">
<open-conversations />
<div class="column small-12 medium-4 flex">
<metric-card
:header="$t('OVERVIEW_REPORTS.ACCOUNT_CONVERSATIONS.HEADER')"
:is-loading="uiFlags.isFetchingAccountConversationMetric"
:loading-message="
$t('OVERVIEW_REPORTS.ACCOUNT_CONVERSATIONS.LOADING_MESSAGE')
"
:header="$t('OVERVIEW_REPORTS.AGENT_STATUS.HEADER')"
class="flex-1"
>
<div
v-for="(metric, name, index) in conversationMetrics"
:key="index"
class="metric-content column"
>
<h3 class="heading">
{{ name }}
</h3>
<p class="metric">{{ metric }}</p>
</div>
</metric-card>
</div>
<div class="column small-12 medium-4">
<metric-card :header="$t('OVERVIEW_REPORTS.AGENT_STATUS.HEADER')">
<div
v-for="(metric, name, index) in agentStatusMetrics"
:key="index"
@@ -79,20 +64,22 @@
</template>
<script>
import { mapGetters } from 'vuex';
import OpenConversations from './components/LiveReports/OpenConversations.vue';
import AgentTable from './components/overview/AgentTable.vue';
import TeamTable from './components/overview/TeamTable.vue';
import MetricCard from './components/overview/MetricCard.vue';
import { OVERVIEW_METRICS } from './constants';
import ReportHeatmap from './components/Heatmap.vue';
import endOfDay from 'date-fns/endOfDay';
import getUnixTime from 'date-fns/getUnixTime';
import startOfDay from 'date-fns/startOfDay';
import subDays from 'date-fns/subDays';
import { OVERVIEW_METRICS } from './constants';
export default {
name: 'LiveReports',
components: {
OpenConversations,
TeamTable,
AgentTable,
MetricCard,
@@ -108,7 +95,6 @@ export default {
agentStatus: 'agents/getAgentStatus',
agents: 'agents/getAgents',
teams: 'teams/getTeams',
accountConversationMetric: 'getAccountConversationMetric',
agentConversationMetric: 'getAgentConversationMetric',
teamConversationMetric: 'getTeamConversationMetric',
accountConversationHeatmap: 'getAccountConversationHeatmapData',
@@ -124,16 +110,6 @@ export default {
});
return metric;
},
conversationMetrics() {
let metric = {};
Object.keys(this.accountConversationMetric).forEach(key => {
const metricName = this.$t(
`OVERVIEW_REPORTS.ACCOUNT_CONVERSATIONS.${OVERVIEW_METRICS[key]}`
);
metric[metricName] = this.accountConversationMetric[key];
});
return metric;
},
},
mounted() {
this.$store.dispatch('agents/get');
@@ -146,7 +122,6 @@ export default {
},
methods: {
fetchAllData() {
this.$store.dispatch('fetchLiveConversationMetric');
this.$store.dispatch('fetchAgentConversationMetric');
this.$store.dispatch('fetchTeamConversationMetric');
this.fetchHeatmapData();
@@ -1,19 +1,27 @@
<template>
<woot-reports
key="team-reports"
type="team"
getter-key="teams/getTeams"
action-key="teams/get"
:download-button-label="$t('TEAM_REPORTS.DOWNLOAD_TEAM_REPORTS')"
/>
<div class="overflow-auto">
<metric-card :is-live="false" header="Team wise reports" />
<metric-card :is-live="false" header="Team wise reports">
<woot-reports
key="team-reports"
class="!p-0"
type="team"
getter-key="teams/getTeams"
action-key="teams/get"
:download-button-label="$t('TEAM_REPORTS.DOWNLOAD_TEAM_REPORTS')"
/>
</metric-card>
</div>
</template>
<script>
import WootReports from './components/WootReports.vue';
import MetricCard from './components/overview/MetricCard.vue';
export default {
components: {
WootReports,
MetricCard,
},
};
</script>
@@ -0,0 +1,79 @@
<script>
import { mapGetters } from 'vuex';
import { OVERVIEW_METRICS } from '../../constants';
import MetricCard from '../overview/MetricCard.vue';
import MultiselectDropdown from 'shared/components/ui/MultiselectDropdown.vue';
export default {
components: {
MultiselectDropdown,
MetricCard,
},
data() {
return {
selectedTeam: null,
};
},
computed: {
...mapGetters({
teams: 'teams/getTeams',
accountConversationMetric: 'getAccountConversationMetric',
uiFlags: 'getOverviewUIFlags',
}),
conversationMetrics() {
let metric = {};
Object.keys(this.accountConversationMetric).forEach(key => {
const metricName = this.$t(
`OVERVIEW_REPORTS.ACCOUNT_CONVERSATIONS.${OVERVIEW_METRICS[key]}`
);
metric[metricName] = this.accountConversationMetric[key];
});
return metric;
},
},
mounted() {
this.$store.dispatch('fetchLiveConversationMetric');
},
methods: {
onSelectTeam(team) {
this.$store.dispatch('fetchLiveConversationMetric', { team_id: team.id });
this.selectedTeam = team;
},
},
};
</script>
<template>
<div class="column small-12 medium-8 conversation-metric">
<metric-card
class="overflow-visible"
:header="$t('OVERVIEW_REPORTS.ACCOUNT_CONVERSATIONS.HEADER')"
:is-loading="uiFlags.isFetchingAccountConversationMetric"
loading-message="Loading metrics"
>
<template #control>
<multiselect-dropdown
class="!mb-0 !w-1/2"
:options="teams"
:selected-item="selectedTeam"
multiselector-title=""
multiselector-placeholder="Select a team"
no-search-result="No teams found"
input-placeholder="Search for a team"
:is-filter="true"
@click="onSelectTeam"
/>
</template>
<div
v-for="(metric, name, index) in conversationMetrics"
:key="index"
class="metric-content column"
>
<h3 class="heading">
{{ name }}
</h3>
<p class="metric">{{ metric }}</p>
</div>
</metric-card>
</div>
</template>
@@ -4,7 +4,7 @@
<slot name="header">
<div class="card-header--title-area">
<h5>{{ header }}</h5>
<span class="live">
<span v-if="isLive" class="live">
<span class="ellipse" /><span>{{
$t('OVERVIEW_REPORTS.LIVE')
}}</span>
@@ -15,7 +15,7 @@
</div>
</slot>
</div>
<div v-if="!isLoading" class="card-body row">
<div v-if="!isLoading" class="card-body row" :class="!isLive ? 'px-0' : ''">
<slot />
</div>
<div v-else-if="isLoading" class="conversation-metric-loader">
@@ -33,6 +33,10 @@ export default {
Spinner,
},
props: {
isLive: {
type: Boolean,
default: true,
},
header: {
type: String,
default: '',
@@ -45,6 +49,10 @@ export default {
type: String,
default: '',
},
isFilter: {
type: Boolean,
default: false,
},
},
};
</script>
@@ -95,9 +103,11 @@ export default {
.card-body {
.metric-content {
@apply pb-2;
.heading {
@apply text-base text-slate-700 dark:text-slate-100;
}
.metric {
@apply text-woot-800 dark:text-woot-300 text-3xl mb-0 mt-1;
}
@@ -131,10 +131,10 @@ export const actions = {
commit(types.default.TOGGLE_ACCOUNT_REPORT_LOADING, false);
});
},
fetchLiveConversationMetric({ commit }) {
fetchLiveConversationMetric({ commit }, params = {}) {
commit(types.default.TOGGLE_ACCOUNT_CONVERSATION_METRIC_LOADING, true);
liveReports
.getConversationMetric()
.getConversationMetric(params)
.then(accountConversationMetric => {
commit(
types.default.SET_ACCOUNT_CONVERSATION_METRIC,
@@ -0,0 +1,121 @@
import * as MutationHelpers from 'shared/helpers/vuex/mutationHelpers';
import types from '../mutation-types';
import LabelsAPI from '../../api/labels';
import AnalyticsHelper from '../../helper/AnalyticsHelper';
import { LABEL_EVENTS } from '../../helper/AnalyticsHelper/events';
export const state = {
teamSummaryReports: [],
agentSummaryReports: [],
uiFlags: {
isFetching: false,
isFetchingItem: false,
isCreating: false,
isDeleting: false,
},
};
export const getters = {
getAgentSummaryReport(_state) {
return _state.agentSummaryReports;
},
getTeamSummaryReports(_state) {
return _state.teamSummaryReports;
},
getLabelsOnSidebar(_state) {
return _state.records
.filter(record => record.show_on_sidebar)
.sort((a, b) => a.title.localeCompare(b.title));
},
};
export const actions = {
revalidate: async function revalidate({ commit }, { newKey }) {
try {
const isExistingKeyValid = await LabelsAPI.validateCacheKey(newKey);
if (!isExistingKeyValid) {
const response = await LabelsAPI.refetchAndCommit(newKey);
commit(types.SET_LABELS, response.data.payload);
}
} catch (error) {
// Ignore error
}
},
get: async function getLabels({ commit }) {
commit(types.SET_LABEL_UI_FLAG, { isFetching: true });
try {
const response = await LabelsAPI.get(true);
const sortedLabels = response.data.payload.sort((a, b) =>
a.title.localeCompare(b.title)
);
commit(types.SET_LABELS, sortedLabels);
} catch (error) {
// Ignore error
} finally {
commit(types.SET_LABEL_UI_FLAG, { isFetching: false });
}
},
create: async function createLabels({ commit }, cannedObj) {
commit(types.SET_LABEL_UI_FLAG, { isCreating: true });
try {
const response = await LabelsAPI.create(cannedObj);
AnalyticsHelper.track(LABEL_EVENTS.CREATE);
commit(types.ADD_LABEL, response.data);
} catch (error) {
const errorMessage = error?.response?.data?.message;
throw new Error(errorMessage);
} finally {
commit(types.SET_LABEL_UI_FLAG, { isCreating: false });
}
},
update: async function updateLabels({ commit }, { id, ...updateObj }) {
commit(types.SET_LABEL_UI_FLAG, { isUpdating: true });
try {
const response = await LabelsAPI.update(id, updateObj);
AnalyticsHelper.track(LABEL_EVENTS.UPDATE);
commit(types.EDIT_LABEL, response.data);
} catch (error) {
throw new Error(error);
} finally {
commit(types.SET_LABEL_UI_FLAG, { isUpdating: false });
}
},
delete: async function deleteLabels({ commit }, id) {
commit(types.SET_LABEL_UI_FLAG, { isDeleting: true });
try {
await LabelsAPI.delete(id);
AnalyticsHelper.track(LABEL_EVENTS.DELETED);
commit(types.DELETE_LABEL, id);
} catch (error) {
throw new Error(error);
} finally {
commit(types.SET_LABEL_UI_FLAG, { isDeleting: false });
}
},
};
export const mutations = {
[types.SET_LABEL_UI_FLAG](_state, data) {
_state.uiFlags = {
..._state.uiFlags,
...data,
};
},
[types.SET_LABELS]: MutationHelpers.set,
[types.ADD_LABEL]: MutationHelpers.create,
[types.EDIT_LABEL]: MutationHelpers.update,
[types.DELETE_LABEL]: MutationHelpers.destroy,
};
export default {
namespaced: true,
state,
getters,
actions,
mutations,
};