feat: Update the design for SLA policy management pages (#9136)
Co-authored-by: Shivam Mishra <scm.mymail@gmail.com> Co-authored-by: Pranav <pranav@chatwoot.com>
This commit is contained in:
@@ -4,22 +4,9 @@
|
|||||||
"ADD_ACTION": "Add SLA",
|
"ADD_ACTION": "Add SLA",
|
||||||
"DESCRIPTION": "Service Level Agreements (SLAs) are contracts that define clear expectations between your team and customers. They establish standards for response and resolution times, creating a framework for accountability and ensures a consistent, high-quality experience.",
|
"DESCRIPTION": "Service Level Agreements (SLAs) are contracts that define clear expectations between your team and customers. They establish standards for response and resolution times, creating a framework for accountability and ensures a consistent, high-quality experience.",
|
||||||
"LEARN_MORE": "Learn more about SLA",
|
"LEARN_MORE": "Learn more about SLA",
|
||||||
"HEADER_BTN_TXT": "Add SLA",
|
|
||||||
"LOADING": "Fetching SLAs",
|
"LOADING": "Fetching SLAs",
|
||||||
"SEARCH_404": "There are no items matching this query",
|
|
||||||
"SIDEBAR_TXT": "<p><b>SLA</b> <p>Think of Service Level Agreements (SLAs) like friendly promises between a service provider and a customer.</p> <p> These promises set clear expectations for things like how quickly the team will respond to issues, making sure you always get a reliable and top-notch experience!</p>",
|
|
||||||
"LIST": {
|
"LIST": {
|
||||||
"404": "There are no SLAs available in this account.",
|
"404": "There are no SLAs available in this account.",
|
||||||
"TITLE": "Manage SLA",
|
|
||||||
"DESC": "SLAs: Friendly promises for great service!",
|
|
||||||
"TABLE_HEADER": [
|
|
||||||
"Name",
|
|
||||||
"Description",
|
|
||||||
"FRT",
|
|
||||||
"NRT",
|
|
||||||
"RT",
|
|
||||||
"Business Hours"
|
|
||||||
],
|
|
||||||
"BUSINESS_HOURS_ON": "Business hours on",
|
"BUSINESS_HOURS_ON": "Business hours on",
|
||||||
"BUSINESS_HOURS_OFF": "Business hours off",
|
"BUSINESS_HOURS_OFF": "Business hours off",
|
||||||
"RESPONSE_TYPES": {
|
"RESPONSE_TYPES": {
|
||||||
|
|||||||
@@ -1,6 +1,23 @@
|
|||||||
|
<script setup>
|
||||||
|
defineProps({
|
||||||
|
isLoading: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
loadingMessage: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col w-full h-full gap-10 font-inter">
|
<div class="flex flex-col w-full h-full gap-10 font-inter">
|
||||||
<slot name="header" />
|
<slot name="header" />
|
||||||
<slot name="body" />
|
<div>
|
||||||
|
<slot v-if="isLoading" name="loading">
|
||||||
|
<woot-loading-state :message="loadingMessage" />
|
||||||
|
</slot>
|
||||||
|
<slot v-else name="body" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ defineProps({
|
|||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="flex relative flex-col sm:flex-row p-4 gap-4 sm:p-6 justify-between shadow-sm sm:divide-x sm:divide-slate-75 sm:dark:divide-slate-700/50 group bg-white border border-solid rounded-xl dark:bg-slate-800 border-slate-75 dark:border-slate-700/50 max-w-[900px] w-full"
|
class="flex relative flex-col sm:flex-row p-4 gap-4 sm:p-6 justify-between shadow-sm group bg-white border border-solid rounded-xl dark:bg-slate-800 border-slate-75 dark:border-slate-700/50 max-w-[900px] w-full"
|
||||||
>
|
>
|
||||||
<!-- left side section -->
|
<!-- left side section -->
|
||||||
<slot name="leftSection">
|
<slot name="leftSection">
|
||||||
|
|||||||
@@ -1,82 +1,36 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex-1 overflow-auto p-4">
|
<settings-layout
|
||||||
<woot-button
|
:is-loading="uiFlags.isFetching"
|
||||||
color-scheme="success"
|
:loading-message="$t('SLA.LOADING')"
|
||||||
class-names="button--fixed-top"
|
|
||||||
icon="add-circle"
|
|
||||||
@click="openAddPopup"
|
|
||||||
>
|
>
|
||||||
{{ $t('SLA.HEADER_BTN_TXT') }}
|
<template #header>
|
||||||
</woot-button>
|
<SLA-header @click="openAddPopup" />
|
||||||
<div class="flex flex-row gap-4">
|
</template>
|
||||||
<div class="w-full xl:w-3/5">
|
<template #loading>
|
||||||
|
<SLAListItemLoading v-for="ii in 2" :key="ii" class="mb-3" />
|
||||||
|
</template>
|
||||||
|
<template #body>
|
||||||
<p
|
<p
|
||||||
v-if="!uiFlags.isFetching && !records.length"
|
v-if="!records.length"
|
||||||
class="flex h-full items-center flex-col justify-center"
|
class="flex flex-col items-center justify-center h-full"
|
||||||
>
|
>
|
||||||
{{ $t('SLA.LIST.404') }}
|
{{ $t('SLA.LIST.404') }}
|
||||||
</p>
|
</p>
|
||||||
<woot-loading-state
|
<div v-if="records.length" class="flex flex-col w-full h-full gap-3">
|
||||||
v-if="uiFlags.isFetching"
|
<SLA-list-item
|
||||||
:message="$t('SLA.LOADING')"
|
v-for="sla in records"
|
||||||
/>
|
:key="sla.title"
|
||||||
<table v-if="!uiFlags.isFetching && records.length" class="woot-table">
|
:sla-name="sla.name"
|
||||||
<thead>
|
:description="sla.description"
|
||||||
<th v-for="thHeader in $t('SLA.LIST.TABLE_HEADER')" :key="thHeader">
|
:first-response="displayTime(sla.first_response_time_threshold)"
|
||||||
{{ thHeader }}
|
:next-response="displayTime(sla.next_response_time_threshold)"
|
||||||
</th>
|
:resolution-time="displayTime(sla.resolution_time_threshold)"
|
||||||
</thead>
|
:has-business-hours="sla.only_during_business_hours"
|
||||||
<tbody>
|
|
||||||
<tr v-for="sla in records" :key="sla.title">
|
|
||||||
<td>
|
|
||||||
<span
|
|
||||||
class="inline-block overflow-hidden whitespace-nowrap text-ellipsis"
|
|
||||||
>
|
|
||||||
{{ sla.name }}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td>{{ sla.description }}</td>
|
|
||||||
<td>
|
|
||||||
<span class="flex items-center">
|
|
||||||
{{ displayTime(sla.first_response_time_threshold) }}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<span class="flex items-center">
|
|
||||||
{{ displayTime(sla.next_response_time_threshold) }}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<span class="flex items-center">
|
|
||||||
{{ displayTime(sla.resolution_time_threshold) }}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<span class="flex items-center">
|
|
||||||
{{ sla.only_during_business_hours }}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td class="button-wrapper">
|
|
||||||
<woot-button
|
|
||||||
v-tooltip.top="$t('SLA.FORM.DELETE')"
|
|
||||||
variant="smooth"
|
|
||||||
color-scheme="alert"
|
|
||||||
size="tiny"
|
|
||||||
icon="dismiss-circle"
|
|
||||||
class-names="grey-btn"
|
|
||||||
:is-loading="loading[sla.id]"
|
:is-loading="loading[sla.id]"
|
||||||
@click="openDeletePopup(sla)"
|
@click="openDeletePopup(sla)"
|
||||||
/>
|
/>
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="w-1/3 hidden xl:block">
|
|
||||||
<span v-dompurify-html="$t('SLA.SIDEBAR_TXT')" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<woot-modal :show.sync="showAddPopup" :on-close="hideAddPopup">
|
<woot-modal :show.sync="showAddPopup" :on-close="hideAddPopup">
|
||||||
<add-SLA @close="hideAddPopup" />
|
<add-SLA @close="hideAddPopup" />
|
||||||
</woot-modal>
|
</woot-modal>
|
||||||
@@ -91,9 +45,14 @@
|
|||||||
:confirm-text="deleteConfirmText"
|
:confirm-text="deleteConfirmText"
|
||||||
:reject-text="deleteRejectText"
|
:reject-text="deleteRejectText"
|
||||||
/>
|
/>
|
||||||
</div>
|
</template>
|
||||||
|
</settings-layout>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
|
import SettingsLayout from '../SettingsLayout.vue';
|
||||||
|
import SLAHeader from './components/SLAHeader.vue';
|
||||||
|
import SLAListItem from './components/SLAListItem.vue';
|
||||||
|
import SLAListItemLoading from './components/SLAListItemLoading.vue';
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import { convertSecondsToTimeUnit } from '@chatwoot/utils';
|
import { convertSecondsToTimeUnit } from '@chatwoot/utils';
|
||||||
|
|
||||||
@@ -103,6 +62,10 @@ import alertMixin from 'shared/mixins/alertMixin';
|
|||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
AddSLA,
|
AddSLA,
|
||||||
|
SLAHeader,
|
||||||
|
SLAListItem,
|
||||||
|
SLAListItemLoading,
|
||||||
|
SettingsLayout,
|
||||||
},
|
},
|
||||||
mixins: [alertMixin],
|
mixins: [alertMixin],
|
||||||
data() {
|
data() {
|
||||||
|
|||||||
@@ -35,7 +35,11 @@ defineProps({
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<base-settings-list-item :title="slaName" :description="description">
|
<base-settings-list-item
|
||||||
|
class="sm:divide-x sm:divide-slate-75 sm:dark:divide-slate-700/50"
|
||||||
|
:title="slaName"
|
||||||
|
:description="description"
|
||||||
|
>
|
||||||
<template #label>
|
<template #label>
|
||||||
<SLA-business-hours-label :has-business-hours="hasBusinessHours" />
|
<SLA-business-hours-label :has-business-hours="hasBusinessHours" />
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
<script setup>
|
||||||
|
import BaseSettingsListItem from '../../components/BaseSettingsListItem.vue';
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<base-settings-list-item class="opacity-50">
|
||||||
|
<template #title>
|
||||||
|
<div class="w-24 h-[26px] rounded-md bg-slate-50 animate-pulse" />
|
||||||
|
</template>
|
||||||
|
<template #description>
|
||||||
|
<div class="w-64 h-4 mb-0.5 rounded-md bg-slate-50 animate-pulse" />
|
||||||
|
<div class="w-48 h-4 rounded-md bg-slate-50 animate-pulse" />
|
||||||
|
</template>
|
||||||
|
<template #label>
|
||||||
|
<div class="w-32 h-[26px] bg-slate-50 animate-pulse rounded-md" />
|
||||||
|
</template>
|
||||||
|
<template #rightSection>
|
||||||
|
<div
|
||||||
|
class="flex items-center sm:rtl:!border-l-0 sm:rtl:!border-r sm:rtl:border-solid sm:rtl:border-slate-75 sm:rtl:dark:border-slate-700/50 gap-1.5 w-fit sm:w-full sm:gap-0 sm:justify-between"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-for="ii in 3"
|
||||||
|
:key="ii"
|
||||||
|
class="flex justify-end w-1/3 h-full px-4"
|
||||||
|
>
|
||||||
|
<div class="w-32 h-full rounded-md bg-slate-50 animate-pulse" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</base-settings-list-item>
|
||||||
|
</template>
|
||||||
@@ -1,18 +1,14 @@
|
|||||||
import { frontendURL } from '../../../../helper/URLHelper';
|
import { frontendURL } from '../../../../helper/URLHelper';
|
||||||
|
|
||||||
const SettingsContent = () => import('../Wrapper.vue');
|
const SettingsWrapper = () => import('../SettingsWrapper.vue');
|
||||||
const Index = () => import('./Index.vue');
|
const Index = () => import('./Index.vue');
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
routes: [
|
routes: [
|
||||||
{
|
{
|
||||||
path: frontendURL('accounts/:accountId/settings/sla'),
|
path: frontendURL('accounts/:accountId/settings/sla'),
|
||||||
component: SettingsContent,
|
component: SettingsWrapper,
|
||||||
props: {
|
props: {},
|
||||||
headerTitle: 'SLA.HEADER',
|
|
||||||
icon: 'document-list-clock',
|
|
||||||
showNewButton: true,
|
|
||||||
},
|
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
|
|||||||
Reference in New Issue
Block a user