fix(sla): freeze misses after resolution

This commit is contained in:
Sony Mathew
2026-07-15 13:00:51 +05:30
parent 13db36609d
commit 92d5d5ecff
16 changed files with 598 additions and 66 deletions
@@ -1,6 +1,10 @@
<script setup>
import { ref, computed, onMounted, onUnmounted, watch } from 'vue';
import { evaluateSLAStatus } from 'dashboard/helper/slaHelper';
import { useI18n } from 'vue-i18n';
import {
evaluateSLAStatus,
shouldRefreshSLAStatus,
} from 'dashboard/helper/slaHelper';
const props = defineProps({
conversation: {
@@ -12,6 +16,7 @@ const props = defineProps({
const REFRESH_INTERVAL = 60000;
const timer = ref(null);
const { t } = useI18n();
const slaStatus = ref({
threshold: null,
isSlaMissed: false,
@@ -24,12 +29,15 @@ const slaEvents = computed(() => props.conversation?.slaEvents);
const isSlaMissed = computed(() => slaStatus.value?.isSlaMissed);
const hasSlaThreshold = computed(() => {
return slaStatus.value?.threshold && appliedSLA.value?.id;
return slaStatus.value?.type && appliedSLA.value?.id;
});
const slaStatusText = computed(() => {
return slaStatus.value?.type?.toUpperCase();
});
const slaValueText = computed(
() => slaStatus.value?.threshold || t('CONVERSATION.HEADER.SLA_STATUS.MISSED')
);
const updateSlaStatus = () => {
slaStatus.value = evaluateSLAStatus({
@@ -39,7 +47,24 @@ const updateSlaStatus = () => {
});
};
const clearTimer = () => {
if (timer.value) {
clearTimeout(timer.value);
timer.value = null;
}
};
const createTimer = () => {
clearTimer();
if (
!shouldRefreshSLAStatus({
appliedSla: appliedSLA.value,
chat: props.conversation,
})
) {
return;
}
timer.value = setTimeout(() => {
updateSlaStatus();
createTimer();
@@ -52,12 +77,16 @@ onMounted(() => {
});
onUnmounted(() => {
if (timer.value) {
clearTimeout(timer.value);
}
clearTimer();
});
watch(() => props.conversation, updateSlaStatus);
watch(
() => props.conversation,
() => {
updateSlaStatus();
createTimer();
}
);
// This expose is to provide context to the parent component, so that it can decided weather
// a new row has to be added to the conversation card or not
@@ -96,7 +125,7 @@ defineExpose({
class="text-sm truncate"
:class="isSlaMissed ? 'text-n-ruby-11' : 'text-n-slate-11'"
>
{{ `${slaStatusText}: ${slaStatus.threshold}` }}
{{ `${slaStatusText}: ${slaValueText}` }}
</span>
</div>
</template>
@@ -1,6 +1,10 @@
<script setup>
import { ref, computed, onMounted, onUnmounted, watch } from 'vue';
import { evaluateSLAStatus } from 'dashboard/helper/slaHelper';
import { useI18n } from 'vue-i18n';
import {
evaluateSLAStatus,
shouldRefreshSLAStatus,
} from 'dashboard/helper/slaHelper';
import Icon from 'dashboard/components-next/icon/Icon.vue';
import Label from 'dashboard/components-next/label/Label.vue';
@@ -15,6 +19,7 @@ const props = defineProps({
const REFRESH_INTERVAL = 60000;
const timer = ref(null);
const { t } = useI18n();
const slaStatus = ref({
threshold: null,
isSlaMissed: false,
@@ -28,8 +33,18 @@ defineOptions({
const appliedSLA = computed(() => props.chat?.applied_sla);
const slaEvents = computed(() => props.chat?.sla_events);
const hasSlaThreshold = computed(() => slaStatus.value?.threshold);
const hasSlaThreshold = computed(() => slaStatus.value?.type);
const isSlaMissed = computed(() => slaStatus.value?.isSlaMissed);
const slaLabel = computed(() => {
if (slaStatus.value?.threshold) return slaStatus.value.threshold;
const status = t('CONVERSATION.HEADER.SLA_STATUS.MISSED');
return {
FRT: t('CONVERSATION.HEADER.SLA_STATUS.FRT', { status }),
NRT: t('CONVERSATION.HEADER.SLA_STATUS.NRT', { status }),
RT: t('CONVERSATION.HEADER.SLA_STATUS.RT', { status }),
}[slaStatus.value.type];
});
const updateSlaStatus = () => {
slaStatus.value = evaluateSLAStatus({
@@ -39,7 +54,24 @@ const updateSlaStatus = () => {
});
};
const clearTimer = () => {
if (timer.value) {
clearTimeout(timer.value);
timer.value = null;
}
};
const createTimer = () => {
clearTimer();
if (
!shouldRefreshSLAStatus({
appliedSla: appliedSLA.value,
chat: props.chat,
})
) {
return;
}
timer.value = setTimeout(() => {
updateSlaStatus();
createTimer();
@@ -52,12 +84,16 @@ onMounted(() => {
});
onUnmounted(() => {
if (timer.value) {
clearTimeout(timer.value);
}
clearTimer();
});
watch(() => props.chat, updateSlaStatus);
watch(
() => props.chat,
() => {
updateSlaStatus();
createTimer();
}
);
defineExpose({
hasSlaThreshold,
@@ -70,11 +106,7 @@ defineExpose({
v-bind="$attrs"
class="relative flex items-center cursor-pointer min-w-fit group"
>
<Label
:label="slaStatus.threshold"
:color="isSlaMissed ? 'ruby' : 'amber'"
compact
>
<Label :label="slaLabel" :color="isSlaMissed ? 'ruby' : 'amber'" compact>
<template #icon>
<Icon icon="i-lucide-flame" class="flex-shrink-0 size-3.5" />
</template>