fix: created SLA Section component & added in lead/deal page
This commit is contained in:
parent
86984abd2b
commit
bf996a3cbd
114
frontend/src/components/SLASection.vue
Normal file
114
frontend/src/components/SLASection.vue
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
<template>
|
||||||
|
<div class="flex flex-col gap-1.5 border-b px-6 py-3">
|
||||||
|
<div
|
||||||
|
v-for="s in slaSection"
|
||||||
|
:key="s.label"
|
||||||
|
class="flex items-center gap-2 text-base leading-5"
|
||||||
|
>
|
||||||
|
<div class="w-[106px] text-sm text-gray-600">{{ s.label }}</div>
|
||||||
|
<div class="grid min-h-[28px] items-center">
|
||||||
|
<Tooltip
|
||||||
|
v-if="s.tooltipText"
|
||||||
|
:text="s.tooltipText"
|
||||||
|
class="ml-2 cursor-pointer"
|
||||||
|
>
|
||||||
|
<Badge
|
||||||
|
v-if="s.type == 'Badge'"
|
||||||
|
class="-ml-1"
|
||||||
|
:label="s.value"
|
||||||
|
variant="subtle"
|
||||||
|
:theme="s.color"
|
||||||
|
/>
|
||||||
|
<div v-else>{{ s.value }}</div>
|
||||||
|
</Tooltip>
|
||||||
|
<Dropdown
|
||||||
|
class="form-control"
|
||||||
|
v-if="s.type == 'Select'"
|
||||||
|
:options="s.options"
|
||||||
|
>
|
||||||
|
<template #default="{ open }">
|
||||||
|
<Button :label="s.value">
|
||||||
|
<template #suffix>
|
||||||
|
<FeatherIcon
|
||||||
|
:name="open ? 'chevron-up' : 'chevron-down'"
|
||||||
|
class="h-4"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</Button>
|
||||||
|
</template>
|
||||||
|
</Dropdown>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { Dropdown, Badge, Tooltip, FeatherIcon } from 'frappe-ui'
|
||||||
|
import { timeAgo, dateFormat, formatTime, dateTooltipFormat } from '@/utils'
|
||||||
|
import { statusesStore } from '@/stores/statuses'
|
||||||
|
import { computed, defineModel } from 'vue'
|
||||||
|
|
||||||
|
const data = defineModel()
|
||||||
|
const emit = defineEmits(['updateField'])
|
||||||
|
|
||||||
|
const { communicationStatuses } = statusesStore()
|
||||||
|
|
||||||
|
let slaSection = computed(() => {
|
||||||
|
let sections = []
|
||||||
|
if (data.value.first_response_time) {
|
||||||
|
sections.push({
|
||||||
|
label: 'Fulfilled In',
|
||||||
|
type: 'Duration',
|
||||||
|
value: formatTime(data.value.first_response_time),
|
||||||
|
tooltipText: dateFormat(data.value.first_responded_on, dateTooltipFormat),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
let status = data.value.sla_status
|
||||||
|
let tooltipText = status
|
||||||
|
let color =
|
||||||
|
data.value.sla_status == 'Failed'
|
||||||
|
? 'red'
|
||||||
|
: data.value.sla_status == 'Fulfilled'
|
||||||
|
? 'green'
|
||||||
|
: 'orange'
|
||||||
|
|
||||||
|
if (status == 'First Response Due') {
|
||||||
|
status = timeAgo(data.value.response_by)
|
||||||
|
tooltipText = dateFormat(data.value.response_by, dateTooltipFormat)
|
||||||
|
if (new Date(data.value.response_by) < new Date()) {
|
||||||
|
color = 'red'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sections.push(
|
||||||
|
...[
|
||||||
|
{
|
||||||
|
label: 'SLA',
|
||||||
|
type: 'Badge',
|
||||||
|
value: status,
|
||||||
|
tooltipText: tooltipText,
|
||||||
|
color: color,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Status',
|
||||||
|
value: data.value.communication_status,
|
||||||
|
type: 'Select',
|
||||||
|
options: communicationStatuses.data?.map((status) => ({
|
||||||
|
label: status.name,
|
||||||
|
value: status.name,
|
||||||
|
onClick: () =>
|
||||||
|
emit('updateField', 'communication_status', status.name),
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
)
|
||||||
|
return sections
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
:deep(.form-control button) {
|
||||||
|
border-color: transparent;
|
||||||
|
background: white;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -99,72 +99,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="deal.data.sla_status" class="flex flex-col gap-2 border-b p-5">
|
<SLASection
|
||||||
<div
|
v-if="deal.data.sla_status"
|
||||||
v-if="deal.data.sla_status == 'First Response Due'"
|
v-model="deal.data"
|
||||||
class="flex items-center gap-4 text-base leading-5"
|
@updateField="updateField"
|
||||||
>
|
/>
|
||||||
<div class="w-[106px] text-gray-600">Response By</div>
|
|
||||||
<Tooltip
|
|
||||||
:text="dateFormat(deal.data.response_by, 'ddd, MMM D, YYYY h:mm A')"
|
|
||||||
class="cursor-pointer"
|
|
||||||
>
|
|
||||||
{{ timeAgo(deal.data.response_by) }}
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-if="deal.data.sla_status == 'Fulfilled'"
|
|
||||||
class="flex items-center gap-4 text-base leading-5"
|
|
||||||
>
|
|
||||||
<div class="w-[106px] text-gray-600">Fulfilled In</div>
|
|
||||||
<Tooltip
|
|
||||||
:text="
|
|
||||||
dateFormat(
|
|
||||||
deal.data.first_responded_on,
|
|
||||||
'ddd, MMM D, YYYY h:mm A'
|
|
||||||
)
|
|
||||||
"
|
|
||||||
class="cursor-pointer"
|
|
||||||
>
|
|
||||||
{{ formatTime(deal.data.first_response_time) }}
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-if="
|
|
||||||
deal.data.sla_status == 'Failed' && deal.data.first_responded_on
|
|
||||||
"
|
|
||||||
class="flex items-center gap-4 text-base leading-5"
|
|
||||||
>
|
|
||||||
<div class="w-[106px] text-gray-600">Fulfilled In</div>
|
|
||||||
<Tooltip
|
|
||||||
:text="
|
|
||||||
dateFormat(
|
|
||||||
deal.data.first_responded_on,
|
|
||||||
'ddd, MMM D, YYYY h:mm A'
|
|
||||||
)
|
|
||||||
"
|
|
||||||
class="cursor-pointer"
|
|
||||||
>
|
|
||||||
{{ formatTime(deal.data.first_response_time) }}
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
|
||||||
<div class="flex items-center gap-4 text-base leading-5">
|
|
||||||
<div class="w-[106px] text-gray-600">Status</div>
|
|
||||||
<div class="">
|
|
||||||
<Badge
|
|
||||||
:label="deal.data.sla_status"
|
|
||||||
variant="outline"
|
|
||||||
:theme="
|
|
||||||
deal.data.sla_status === 'Failed'
|
|
||||||
? 'red'
|
|
||||||
: deal.data.sla_status === 'Fulfilled'
|
|
||||||
? 'green'
|
|
||||||
: 'gray'
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-1 flex-col justify-between overflow-hidden">
|
<div class="flex flex-1 flex-col justify-between overflow-hidden">
|
||||||
<div class="flex flex-col overflow-y-auto">
|
<div class="flex flex-col overflow-y-auto">
|
||||||
<div
|
<div
|
||||||
@ -346,13 +285,8 @@ import ContactModal from '@/components/Modals/ContactModal.vue'
|
|||||||
import Link from '@/components/Controls/Link.vue'
|
import Link from '@/components/Controls/Link.vue'
|
||||||
import Section from '@/components/Section.vue'
|
import Section from '@/components/Section.vue'
|
||||||
import SectionFields from '@/components/SectionFields.vue'
|
import SectionFields from '@/components/SectionFields.vue'
|
||||||
import {
|
import SLASection from '@/components/SLASection.vue'
|
||||||
openWebsite,
|
import { openWebsite, createToast } from '@/utils'
|
||||||
createToast,
|
|
||||||
dateFormat,
|
|
||||||
timeAgo,
|
|
||||||
formatTime,
|
|
||||||
} from '@/utils'
|
|
||||||
import { usersStore } from '@/stores/users'
|
import { usersStore } from '@/stores/users'
|
||||||
import { contactsStore } from '@/stores/contacts'
|
import { contactsStore } from '@/stores/contacts'
|
||||||
import { organizationsStore } from '@/stores/organizations'
|
import { organizationsStore } from '@/stores/organizations'
|
||||||
|
|||||||
@ -139,19 +139,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</FileUploader>
|
</FileUploader>
|
||||||
<div v-if="lead.data.sla_status" class="flex flex-col gap-2 border-b p-5">
|
<SLASection
|
||||||
<div
|
v-if="lead.data.sla_status"
|
||||||
v-for="s in slaSection"
|
v-model="lead.data"
|
||||||
:key="s.label"
|
@updateField="updateField"
|
||||||
class="flex items-center gap-4 text-base leading-5"
|
/>
|
||||||
>
|
|
||||||
<div class="w-[106px] text-gray-600">{{ s.label }}</div>
|
|
||||||
<Tooltip :text="s.tooltipText" class="cursor-pointer">
|
|
||||||
<div v-if="!s.isBadge">{{ s.value }}</div>
|
|
||||||
<Badge v-else :label="s.value" variant="subtle" :theme="s.color" />
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-1 flex-col justify-between overflow-hidden">
|
<div class="flex flex-1 flex-col justify-between overflow-hidden">
|
||||||
<div class="flex flex-col overflow-y-auto">
|
<div class="flex flex-col overflow-y-auto">
|
||||||
<div
|
<div
|
||||||
@ -199,15 +191,9 @@ import UserAvatar from '@/components/UserAvatar.vue'
|
|||||||
import OrganizationModal from '@/components/Modals/OrganizationModal.vue'
|
import OrganizationModal from '@/components/Modals/OrganizationModal.vue'
|
||||||
import Section from '@/components/Section.vue'
|
import Section from '@/components/Section.vue'
|
||||||
import SectionFields from '@/components/SectionFields.vue'
|
import SectionFields from '@/components/SectionFields.vue'
|
||||||
|
import SLASection from '@/components/SLASection.vue'
|
||||||
import Link from '@/components/Controls/Link.vue'
|
import Link from '@/components/Controls/Link.vue'
|
||||||
import {
|
import { openWebsite, createToast } from '@/utils'
|
||||||
openWebsite,
|
|
||||||
createToast,
|
|
||||||
dateFormat,
|
|
||||||
timeAgo,
|
|
||||||
formatTime,
|
|
||||||
dateTooltipFormat,
|
|
||||||
} from '@/utils'
|
|
||||||
import { usersStore } from '@/stores/users'
|
import { usersStore } from '@/stores/users'
|
||||||
import { contactsStore } from '@/stores/contacts'
|
import { contactsStore } from '@/stores/contacts'
|
||||||
import { organizationsStore } from '@/stores/organizations'
|
import { organizationsStore } from '@/stores/organizations'
|
||||||
@ -222,7 +208,6 @@ import {
|
|||||||
Avatar,
|
Avatar,
|
||||||
Tabs,
|
Tabs,
|
||||||
Breadcrumbs,
|
Breadcrumbs,
|
||||||
Badge,
|
|
||||||
call,
|
call,
|
||||||
} from 'frappe-ui'
|
} from 'frappe-ui'
|
||||||
import { ref, computed } from 'vue'
|
import { ref, computed } from 'vue'
|
||||||
@ -380,41 +365,4 @@ function updateField(name, value, callback) {
|
|||||||
callback?.()
|
callback?.()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
let slaSection = computed(() => {
|
|
||||||
let sections = []
|
|
||||||
if (lead.data.first_response_time) {
|
|
||||||
sections.push({
|
|
||||||
label: 'Fulfilled In',
|
|
||||||
value: formatTime(lead.data.first_response_time),
|
|
||||||
tooltipText: dateFormat(lead.data.first_responded_on, dateTooltipFormat),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
let status = lead.data.sla_status
|
|
||||||
let tooltipText = status
|
|
||||||
let color =
|
|
||||||
lead.data.sla_status == 'Failed'
|
|
||||||
? 'red'
|
|
||||||
: lead.data.sla_status == 'Fulfilled'
|
|
||||||
? 'green'
|
|
||||||
: 'orange'
|
|
||||||
|
|
||||||
if (status == 'First Response Due') {
|
|
||||||
status = timeAgo(lead.data.response_by)
|
|
||||||
tooltipText = dateFormat(lead.data.response_by, dateTooltipFormat)
|
|
||||||
if (new Date(lead.data.response_by) < new Date()) {
|
|
||||||
color = 'red'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sections.push({
|
|
||||||
label: 'Status',
|
|
||||||
isBadge: true,
|
|
||||||
value: status,
|
|
||||||
tooltipText: tooltipText,
|
|
||||||
color: color,
|
|
||||||
})
|
|
||||||
return sections
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user