feat: Better SLA missed events popover (#9215)
* feat: SLA events component * feat: Add SLA event item component * Update SLAPopoverCard.vue * Update SLAPopoverCard.vue * fix: Translation * Update SLAEventItem.vue * feat: complete sticky nrt * chore: code cleanup * Update SLACardLabel.vue * chore: code cleanup * chore: away click fixes * feat: use tabular nums --------- Co-authored-by: Shivam Mishra <scm.mymail@gmail.com>
This commit is contained in:
@@ -5,10 +5,10 @@
|
||||
:class="showExtendedInfo ? 'h-[26px] rounded-lg' : 'rounded h-5'"
|
||||
>
|
||||
<div
|
||||
v-on-clickaway="closeSlaPopover"
|
||||
class="flex items-center w-full truncate"
|
||||
:class="showExtendedInfo ? 'px-1.5' : 'px-2 gap-1'"
|
||||
@mouseover="openSlaPopover()"
|
||||
@mouseleave="closeSlaPopover()"
|
||||
>
|
||||
<div
|
||||
class="flex items-center gap-1"
|
||||
@@ -42,7 +42,7 @@
|
||||
</div>
|
||||
<SLA-popover-card
|
||||
v-if="showSlaPopoverCard"
|
||||
:all-missed-slas="slaEvents"
|
||||
:sla-missed-events="slaEvents"
|
||||
class="right-0 top-7"
|
||||
/>
|
||||
</div>
|
||||
@@ -51,6 +51,7 @@
|
||||
<script>
|
||||
import { evaluateSLAStatus } from '../helpers/SLAHelper';
|
||||
import SLAPopoverCard from './SLAPopoverCard.vue';
|
||||
import { mixin as clickaway } from 'vue-clickaway';
|
||||
|
||||
const REFRESH_INTERVAL = 60000;
|
||||
|
||||
@@ -58,6 +59,7 @@ export default {
|
||||
components: {
|
||||
SLAPopoverCard,
|
||||
},
|
||||
mixins: [clickaway],
|
||||
props: {
|
||||
chat: {
|
||||
type: Object,
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
<script setup>
|
||||
import { format, fromUnixTime } from 'date-fns';
|
||||
|
||||
defineProps({
|
||||
label: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
items: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
const formatDate = timestamp =>
|
||||
format(fromUnixTime(timestamp), 'MMM dd, yyyy, hh:mm a');
|
||||
</script>
|
||||
<template>
|
||||
<div class="flex justify-between w-full">
|
||||
<span
|
||||
class="text-sm sticky top-0 h-fit font-normal tracking-[-0.6%] min-w-[140px] truncate text-slate-600 dark:text-slate-200"
|
||||
>
|
||||
{{ label }}
|
||||
</span>
|
||||
<div class="flex flex-col w-full gap-2">
|
||||
<span
|
||||
v-for="item in items"
|
||||
:key="item.id"
|
||||
class="text-sm font-normal text-slate-900 dark:text-slate-25 text-right tabular-nums"
|
||||
>
|
||||
{{ formatDate(item.created_at) }}
|
||||
</span>
|
||||
<slot name="showMore" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,45 +1,85 @@
|
||||
<script setup>
|
||||
import { format, fromUnixTime } from 'date-fns';
|
||||
import { ref, computed } from 'vue';
|
||||
|
||||
defineProps({
|
||||
allMissedSlas: {
|
||||
import wootConstants from 'dashboard/constants/globals';
|
||||
import SLAEventItem from './SLAEventItem.vue';
|
||||
|
||||
const { SLA_MISS_TYPES } = wootConstants;
|
||||
|
||||
const props = defineProps({
|
||||
slaMissedEvents: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
const formatDate = timestamp => format(fromUnixTime(timestamp), 'PP');
|
||||
const shouldShowAllNrts = ref(false);
|
||||
|
||||
const upperCase = str => str.toUpperCase();
|
||||
const frtMisses = computed(() =>
|
||||
props.slaMissedEvents.filter(
|
||||
slaEvent => slaEvent.event_type === SLA_MISS_TYPES.FRT
|
||||
)
|
||||
);
|
||||
const nrtMisses = computed(() => {
|
||||
const missedEvents = props.slaMissedEvents.filter(
|
||||
slaEvent => slaEvent.event_type === SLA_MISS_TYPES.NRT
|
||||
);
|
||||
return shouldShowAllNrts.value ? missedEvents : missedEvents.slice(0, 6);
|
||||
});
|
||||
const rtMisses = computed(() =>
|
||||
props.slaMissedEvents.filter(
|
||||
slaEvent => slaEvent.event_type === SLA_MISS_TYPES.RT
|
||||
)
|
||||
);
|
||||
|
||||
const shouldShowMoreNRTButton = computed(() => nrtMisses.value.length > 6);
|
||||
const toggleShowAllNRT = () => {
|
||||
shouldShowAllNrts.value = !shouldShowAllNrts.value;
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<div
|
||||
class="absolute flex flex-col items-start bg-[#fdfdfd] dark:bg-slate-800 z-50 p-4 border border-solid border-slate-75 dark:border-slate-700 w-[384px] rounded-xl gap-4"
|
||||
class="absolute flex flex-col items-start bg-[#fdfdfd] dark:bg-slate-800 z-50 p-4 border border-solid border-slate-75 dark:border-slate-700 w-[384px] rounded-xl gap-4 max-h-96 overflow-auto"
|
||||
>
|
||||
<div
|
||||
v-for="missedSLA in allMissedSlas"
|
||||
:key="missedSLA.id"
|
||||
class="flex items-center justify-between w-full"
|
||||
<span class="text-sm font-medium text-slate-900 dark:text-slate-25">
|
||||
{{ $t('SLA.EVENTS.TITLE') }}
|
||||
</span>
|
||||
<SLA-event-item
|
||||
v-if="frtMisses.length"
|
||||
:label="$t('SLA.EVENTS.FRT')"
|
||||
:items="frtMisses"
|
||||
/>
|
||||
<SLA-event-item
|
||||
v-if="nrtMisses.length"
|
||||
:label="$t('SLA.EVENTS.NRT')"
|
||||
:items="nrtMisses"
|
||||
>
|
||||
<span
|
||||
class="text-sm font-normal tracking-[-0.6%] w-[140px] truncate text-slate-900 dark:text-slate-50"
|
||||
>
|
||||
{{
|
||||
$t(
|
||||
`CONVERSATION.HEADER.SLA_POPOVER.${upperCase(missedSLA.event_type)}`
|
||||
)
|
||||
}}
|
||||
</span>
|
||||
<span
|
||||
class="text-sm font-normal tracking-[-0.6%] text-slate-600 dark:text-slate-200"
|
||||
>
|
||||
{{ $t('CONVERSATION.HEADER.SLA_POPOVER.MISSED') }}
|
||||
</span>
|
||||
<span
|
||||
class="text-sm font-normal tracking-[-0.6%] text-slate-900 dark:text-slate-50"
|
||||
>
|
||||
{{ formatDate(missedSLA.created_at) }}
|
||||
</span>
|
||||
</div>
|
||||
<template #showMore>
|
||||
<div
|
||||
v-if="shouldShowMoreNRTButton"
|
||||
class="flex flex-col items-end w-full"
|
||||
>
|
||||
<woot-button
|
||||
size="small"
|
||||
:icon="!shouldShowAllNrts ? 'plus-sign' : ''"
|
||||
variant="link"
|
||||
color-scheme="secondary"
|
||||
class="hover:!no-underline !gap-1 hover:!bg-transparent dark:hover:!bg-transparent"
|
||||
@click="toggleShowAllNRT"
|
||||
>
|
||||
{{
|
||||
shouldShowAllNrts
|
||||
? $t('SLA.EVENTS.HIDE', { count: nrtMisses.length })
|
||||
: $t('SLA.EVENTS.SHOW_MORE', { count: nrtMisses.length })
|
||||
}}
|
||||
</woot-button>
|
||||
</div>
|
||||
</template>
|
||||
</SLA-event-item>
|
||||
<SLA-event-item
|
||||
v-if="rtMisses.length"
|
||||
:label="$t('SLA.EVENTS.RT')"
|
||||
:items="rtMisses"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -59,5 +59,10 @@ export default {
|
||||
TYPE: 'type',
|
||||
SORT_ORDER: 'sort_order',
|
||||
},
|
||||
SLA_MISS_TYPES: {
|
||||
FRT: 'frt',
|
||||
NRT: 'nrt',
|
||||
RT: 'rt',
|
||||
},
|
||||
};
|
||||
export const DEFAULT_REDIRECT_URL = '/app/';
|
||||
|
||||
@@ -71,12 +71,6 @@
|
||||
"RT": "RT {status}",
|
||||
"MISSED": "missed",
|
||||
"DUE": "due"
|
||||
},
|
||||
"SLA_POPOVER": {
|
||||
"FRT": "First Response Time",
|
||||
"NRT": "Next Response Time",
|
||||
"RT": "Resolution Time",
|
||||
"MISSED": "missed on"
|
||||
}
|
||||
},
|
||||
"RESOLVE_DROPDOWN": {
|
||||
|
||||
@@ -83,6 +83,14 @@
|
||||
"YES": "Yes, Delete ",
|
||||
"NO": "No, Keep "
|
||||
}
|
||||
},
|
||||
"EVENTS": {
|
||||
"TITLE": "SLA Misses",
|
||||
"FRT": "First response time",
|
||||
"NRT": "Next response time",
|
||||
"RT": "Resolution time",
|
||||
"SHOW_MORE": "{count} more",
|
||||
"HIDE": "Hide {count} rows"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
</woot-button>
|
||||
<SLA-popover-card
|
||||
v-if="showSlaPopoverCard"
|
||||
:all-missed-slas="slaEvents"
|
||||
:sla-missed-events="slaEvents"
|
||||
class="right-0"
|
||||
/>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user