Add live team reports

This commit is contained in:
Pranav
2025-02-05 19:26:46 -08:00
parent b235d66f81
commit 698642fecb
6 changed files with 192 additions and 0 deletions
@@ -476,6 +476,17 @@
"STATUS": "Status"
}
},
"TEAM_CONVERSATIONS": {
"HEADER": "Conversations by teams",
"LOADING_MESSAGE": "Loading team metrics...",
"NO_TEAMS": "There is no data available",
"TABLE_HEADER": {
"AGENT": "Team",
"OPEN": "Open",
"UNATTENDED": "Unattended",
"STATUS": "Status"
}
},
"AGENT_STATUS": {
"HEADER": "Agent status",
"ONLINE": "Online",
@@ -6,6 +6,7 @@ import { OVERVIEW_METRICS } from './constants';
import ReportHeader from './components/ReportHeader.vue';
import HeatmapContainer from './components/HeatmapContainer.vue';
import AgentLiveReportContainer from './components/AgentLiveReportContainer.vue';
import TeamLiveReportContainer from './components/TeamLiveReportContainer.vue';
export const FETCH_INTERVAL = 60000;
export default {
@@ -15,6 +16,7 @@ export default {
MetricCard,
HeatmapContainer,
AgentLiveReportContainer,
TeamLiveReportContainer,
},
data() {
return {
@@ -127,5 +129,6 @@ export default {
</div>
<HeatmapContainer />
<AgentLiveReportContainer />
<TeamLiveReportContainer />
</div>
</template>
@@ -0,0 +1,36 @@
<script setup>
import { onMounted } from 'vue';
import MetricCard from './overview/MetricCard.vue';
import { useStore, useMapGetter } from 'dashboard/composables/store';
import { useLiveRefresh } from 'dashboard/composables/useLiveRefresh';
import TeamTable from './overview/TeamTable.vue';
const store = useStore();
const uiFlags = useMapGetter('getOverviewUIFlags');
const teamConversationMetric = useMapGetter('getTeamConversationMetric');
const teams = useMapGetter('teams/getTeams');
const fetchData = () => store.dispatch('fetchTeamConversationMetric');
const { startRefetching } = useLiveRefresh(fetchData);
onMounted(() => {
store.dispatch('teams/get');
fetchData();
startRefetching();
});
</script>
<template>
<div class="flex flex-row flex-wrap max-w-full">
<MetricCard :header="$t('OVERVIEW_REPORTS.TEAM_CONVERSATIONS.HEADER')">
<TeamTable
:teams="teams"
:team-metrics="teamConversationMetric"
:is-loading="uiFlags.isFetchingTeamConversationMetric"
/>
</MetricCard>
</div>
</template>
@@ -0,0 +1,115 @@
<script setup>
import { computed, h } from 'vue';
import {
useVueTable,
createColumnHelper,
getCoreRowModel,
getPaginationRowModel,
} from '@tanstack/vue-table';
import { useI18n } from 'vue-i18n';
import Spinner from 'shared/components/Spinner.vue';
import EmptyState from 'dashboard/components/widgets/EmptyState.vue';
import Table from 'dashboard/components/table/Table.vue';
import Pagination from 'dashboard/components/table/Pagination.vue';
const { teams, teamMetrics } = defineProps({
teams: {
type: Array,
default: () => [],
},
teamMetrics: {
type: Array,
default: () => [],
},
isLoading: {
type: Boolean,
default: false,
},
});
const { t } = useI18n();
const getTeamMetrics = id =>
teamMetrics.find(metrics => metrics.team_id === Number(id)) || {};
const tableData = computed(() =>
teams
.map(team => {
const metric = getTeamMetrics(team.id);
return {
agent: team.name,
open: metric.open || 0,
unattended: metric.unattended || 0,
};
})
.sort((a, b) => {
// First sort by open tickets (descending)
const openDiff = b.open - a.open;
// If open tickets are equal, sort by name (ascending)
if (openDiff === 0) {
return a.agent.localeCompare(b.agent);
}
return openDiff;
})
);
const defaulSpanRender = cellProps =>
h(
'span',
{
class: cellProps.getValue() ? '' : 'text-slate-300 dark:text-slate-700',
},
cellProps.getValue() ? cellProps.getValue() : '---'
);
const columnHelper = createColumnHelper();
const columns = [
columnHelper.accessor('agent', {
header: t('OVERVIEW_REPORTS.TEAM_CONVERSATIONS.TABLE_HEADER.AGENT'),
cell: defaulSpanRender,
size: 250,
}),
columnHelper.accessor('open', {
header: t('OVERVIEW_REPORTS.TEAM_CONVERSATIONS.TABLE_HEADER.OPEN'),
cell: defaulSpanRender,
size: 100,
}),
columnHelper.accessor('unattended', {
header: t('OVERVIEW_REPORTS.TEAM_CONVERSATIONS.TABLE_HEADER.UNATTENDED'),
cell: defaulSpanRender,
size: 100,
}),
];
const table = useVueTable({
get data() {
return tableData.value;
},
columns,
enableSorting: false,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
});
</script>
<template>
<div class="flex flex-col flex-1">
<Table :table="table" class="max-h-[calc(100vh-21.875rem)]" />
<Pagination class="mt-2" :table="table" />
<div
v-if="isLoading"
class="items-center flex text-base justify-center p-8"
>
<Spinner />
<span>
{{ $t('OVERVIEW_REPORTS.TEAM_CONVERSATIONS.LOADING_MESSAGE') }}
</span>
</div>
<EmptyState
v-else-if="!isLoading && !teams.length"
:title="$t('OVERVIEW_REPORTS.TEAM_CONVERSATIONS.NO_TEAMS')"
/>
</div>
</template>
@@ -55,10 +55,12 @@ const state = {
isFetchingAccountConversationMetric: false,
isFetchingAccountConversationsHeatmap: false,
isFetchingAgentConversationMetric: false,
isFetchingTeamConversationMetric: false,
},
accountConversationMetric: {},
accountConversationHeatmap: [],
agentConversationMetric: [],
teamConversationMetric: [],
},
};
@@ -81,6 +83,9 @@ const getters = {
getAgentConversationMetric(_state) {
return _state.overview.agentConversationMetric;
},
getTeamConversationMetric(_state) {
return _state.overview.teamConversationMetric;
},
getOverviewUIFlags($state) {
return $state.overview.uiFlags;
},
@@ -175,6 +180,18 @@ export const actions = {
commit(types.default.TOGGLE_AGENT_CONVERSATION_METRIC_LOADING, false);
});
},
fetchTeamConversationMetric({ commit }) {
commit(types.default.TOGGLE_TEAM_CONVERSATION_METRIC_LOADING, true);
liveReports
.getGroupedConversations({ groupBy: 'team_id' })
.then(teamMetric => {
commit(types.default.SET_TEAM_CONVERSATION_METRIC, teamMetric.data);
commit(types.default.TOGGLE_TEAM_CONVERSATION_METRIC_LOADING, false);
})
.catch(() => {
commit(types.default.TOGGLE_TEAM_CONVERSATION_METRIC_LOADING, false);
});
},
downloadAgentReports(_, reportObj) {
return Report.getAgentReports(reportObj)
.then(response => {
@@ -280,6 +297,12 @@ const mutations = {
[types.default.TOGGLE_AGENT_CONVERSATION_METRIC_LOADING](_state, flag) {
_state.overview.uiFlags.isFetchingAgentConversationMetric = flag;
},
[types.default.SET_TEAM_CONVERSATION_METRIC](_state, metricData) {
_state.overview.teamConversationMetric = metricData;
},
[types.default.TOGGLE_TEAM_CONVERSATION_METRIC_LOADING](_state, flag) {
_state.overview.uiFlags.isFetchingTeamConversationMetric = flag;
},
};
export default {
@@ -335,4 +335,8 @@ export default {
SET_SLA_REPORTS: 'SET_SLA_REPORTS',
SET_SLA_REPORTS_METRICS: 'SET_SLA_REPORTS_METRICS',
SET_SLA_REPORTS_META: 'SET_SLA_REPORTS_META',
SET_TEAM_CONVERSATION_METRIC: 'SET_TEAM_CONVERSATION_METRIC',
TOGGLE_TEAM_CONVERSATION_METRIC_LOADING:
'TOGGLE_TEAM_CONVERSATION_METRIC_LOADING',
};