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'"
|
:class="showExtendedInfo ? 'h-[26px] rounded-lg' : 'rounded h-5'"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
v-on-clickaway="closeSlaPopover"
|
||||||
class="flex items-center w-full truncate"
|
class="flex items-center w-full truncate"
|
||||||
:class="showExtendedInfo ? 'px-1.5' : 'px-2 gap-1'"
|
:class="showExtendedInfo ? 'px-1.5' : 'px-2 gap-1'"
|
||||||
@mouseover="openSlaPopover()"
|
@mouseover="openSlaPopover()"
|
||||||
@mouseleave="closeSlaPopover()"
|
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="flex items-center gap-1"
|
class="flex items-center gap-1"
|
||||||
@@ -42,7 +42,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<SLA-popover-card
|
<SLA-popover-card
|
||||||
v-if="showSlaPopoverCard"
|
v-if="showSlaPopoverCard"
|
||||||
:all-missed-slas="slaEvents"
|
:sla-missed-events="slaEvents"
|
||||||
class="right-0 top-7"
|
class="right-0 top-7"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -51,6 +51,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import { evaluateSLAStatus } from '../helpers/SLAHelper';
|
import { evaluateSLAStatus } from '../helpers/SLAHelper';
|
||||||
import SLAPopoverCard from './SLAPopoverCard.vue';
|
import SLAPopoverCard from './SLAPopoverCard.vue';
|
||||||
|
import { mixin as clickaway } from 'vue-clickaway';
|
||||||
|
|
||||||
const REFRESH_INTERVAL = 60000;
|
const REFRESH_INTERVAL = 60000;
|
||||||
|
|
||||||
@@ -58,6 +59,7 @@ export default {
|
|||||||
components: {
|
components: {
|
||||||
SLAPopoverCard,
|
SLAPopoverCard,
|
||||||
},
|
},
|
||||||
|
mixins: [clickaway],
|
||||||
props: {
|
props: {
|
||||||
chat: {
|
chat: {
|
||||||
type: Object,
|
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>
|
<script setup>
|
||||||
import { format, fromUnixTime } from 'date-fns';
|
import { ref, computed } from 'vue';
|
||||||
|
|
||||||
defineProps({
|
import wootConstants from 'dashboard/constants/globals';
|
||||||
allMissedSlas: {
|
import SLAEventItem from './SLAEventItem.vue';
|
||||||
|
|
||||||
|
const { SLA_MISS_TYPES } = wootConstants;
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
slaMissedEvents: {
|
||||||
type: Array,
|
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>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div
|
<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
|
<span class="text-sm font-medium text-slate-900 dark:text-slate-25">
|
||||||
v-for="missedSLA in allMissedSlas"
|
{{ $t('SLA.EVENTS.TITLE') }}
|
||||||
:key="missedSLA.id"
|
</span>
|
||||||
class="flex items-center justify-between w-full"
|
<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
|
<template #showMore>
|
||||||
class="text-sm font-normal tracking-[-0.6%] w-[140px] truncate text-slate-900 dark:text-slate-50"
|
<div
|
||||||
>
|
v-if="shouldShowMoreNRTButton"
|
||||||
{{
|
class="flex flex-col items-end w-full"
|
||||||
$t(
|
>
|
||||||
`CONVERSATION.HEADER.SLA_POPOVER.${upperCase(missedSLA.event_type)}`
|
<woot-button
|
||||||
)
|
size="small"
|
||||||
}}
|
:icon="!shouldShowAllNrts ? 'plus-sign' : ''"
|
||||||
</span>
|
variant="link"
|
||||||
<span
|
color-scheme="secondary"
|
||||||
class="text-sm font-normal tracking-[-0.6%] text-slate-600 dark:text-slate-200"
|
class="hover:!no-underline !gap-1 hover:!bg-transparent dark:hover:!bg-transparent"
|
||||||
>
|
@click="toggleShowAllNRT"
|
||||||
{{ $t('CONVERSATION.HEADER.SLA_POPOVER.MISSED') }}
|
>
|
||||||
</span>
|
{{
|
||||||
<span
|
shouldShowAllNrts
|
||||||
class="text-sm font-normal tracking-[-0.6%] text-slate-900 dark:text-slate-50"
|
? $t('SLA.EVENTS.HIDE', { count: nrtMisses.length })
|
||||||
>
|
: $t('SLA.EVENTS.SHOW_MORE', { count: nrtMisses.length })
|
||||||
{{ formatDate(missedSLA.created_at) }}
|
}}
|
||||||
</span>
|
</woot-button>
|
||||||
</div>
|
</div>
|
||||||
|
</template>
|
||||||
|
</SLA-event-item>
|
||||||
|
<SLA-event-item
|
||||||
|
v-if="rtMisses.length"
|
||||||
|
:label="$t('SLA.EVENTS.RT')"
|
||||||
|
:items="rtMisses"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -59,5 +59,10 @@ export default {
|
|||||||
TYPE: 'type',
|
TYPE: 'type',
|
||||||
SORT_ORDER: 'sort_order',
|
SORT_ORDER: 'sort_order',
|
||||||
},
|
},
|
||||||
|
SLA_MISS_TYPES: {
|
||||||
|
FRT: 'frt',
|
||||||
|
NRT: 'nrt',
|
||||||
|
RT: 'rt',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
export const DEFAULT_REDIRECT_URL = '/app/';
|
export const DEFAULT_REDIRECT_URL = '/app/';
|
||||||
|
|||||||
@@ -71,12 +71,6 @@
|
|||||||
"RT": "RT {status}",
|
"RT": "RT {status}",
|
||||||
"MISSED": "missed",
|
"MISSED": "missed",
|
||||||
"DUE": "due"
|
"DUE": "due"
|
||||||
},
|
|
||||||
"SLA_POPOVER": {
|
|
||||||
"FRT": "First Response Time",
|
|
||||||
"NRT": "Next Response Time",
|
|
||||||
"RT": "Resolution Time",
|
|
||||||
"MISSED": "missed on"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"RESOLVE_DROPDOWN": {
|
"RESOLVE_DROPDOWN": {
|
||||||
|
|||||||
@@ -83,6 +83,14 @@
|
|||||||
"YES": "Yes, Delete ",
|
"YES": "Yes, Delete ",
|
||||||
"NO": "No, Keep "
|
"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>
|
</woot-button>
|
||||||
<SLA-popover-card
|
<SLA-popover-card
|
||||||
v-if="showSlaPopoverCard"
|
v-if="showSlaPopoverCard"
|
||||||
:all-missed-slas="slaEvents"
|
:sla-missed-events="slaEvents"
|
||||||
class="right-0"
|
class="right-0"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user