Merge branch 'develop' into chore/chat-list-design
This commit is contained in:
commit
00b9803959
51 changed files with 762 additions and 53 deletions
14
app/javascript/dashboard/api/customViews.js
Normal file
14
app/javascript/dashboard/api/customViews.js
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
/* global axios */
|
||||||
|
import ApiClient from './ApiClient';
|
||||||
|
|
||||||
|
class CustomViewsAPI extends ApiClient {
|
||||||
|
constructor() {
|
||||||
|
super('custom_filters', { accountScoped: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
getCustomViews() {
|
||||||
|
return axios.get(this.url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new CustomViewsAPI();
|
|
@ -1,26 +1,51 @@
|
||||||
l<template>
|
<template>
|
||||||
<div class="conversations-list-wrap">
|
<div class="conversations-list-wrap">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
<div class="chat-list__top" :class="{ filter__applied: hasAppliedFilters }">
|
<div
|
||||||
|
class="chat-list__top"
|
||||||
|
:class="{ filter__applied: hasAppliedFiltersOrActiveCustomViews }"
|
||||||
|
>
|
||||||
<h1 class="page-sub-title text-truncate" :title="pageTitle">
|
<h1 class="page-sub-title text-truncate" :title="pageTitle">
|
||||||
{{ pageTitle }}
|
{{ pageTitle }}
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<div class="filter--actions">
|
<div class="filter--actions">
|
||||||
<chat-filter
|
<chat-filter
|
||||||
v-if="!hasAppliedFilters"
|
v-if="!hasAppliedFiltersOrActiveCustomViews"
|
||||||
@statusFilterChange="updateStatusType"
|
@statusFilterChange="updateStatusType"
|
||||||
/>
|
/>
|
||||||
|
<div v-if="hasAppliedFilters && !hasActiveCustomViews">
|
||||||
|
<woot-button
|
||||||
|
v-tooltip.top-end="$t('FILTER.CUSTOM_VIEWS.ADD.SAVE_BUTTON')"
|
||||||
|
size="tiny"
|
||||||
|
variant="smooth"
|
||||||
|
color-scheme="secondary"
|
||||||
|
icon="save"
|
||||||
|
@click="onClickOpenAddCustomViewsModal"
|
||||||
|
/>
|
||||||
|
<woot-button
|
||||||
|
v-tooltip.top-end="$t('FILTER.CLEAR_BUTTON_LABEL')"
|
||||||
|
size="tiny"
|
||||||
|
variant="smooth"
|
||||||
|
color-scheme="alert"
|
||||||
|
icon="dismiss-circle"
|
||||||
|
@click="resetAndFetchData"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div v-if="hasActiveCustomViews">
|
||||||
|
<woot-button
|
||||||
|
v-tooltip.top-end="$t('FILTER.CUSTOM_VIEWS.DELETE.DELETE_BUTTON')"
|
||||||
|
size="tiny"
|
||||||
|
variant="smooth"
|
||||||
|
color-scheme="alert"
|
||||||
|
icon="delete"
|
||||||
|
class="delete-custom-view__button"
|
||||||
|
@click="onClickOpenDeleteCustomViewsModal"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<woot-button
|
<woot-button
|
||||||
v-else
|
v-else
|
||||||
size="small"
|
|
||||||
variant="clear"
|
|
||||||
color-scheme="alert"
|
|
||||||
@click="resetAndFetchData"
|
|
||||||
>
|
|
||||||
{{ $t('FILTER.CLEAR_BUTTON_LABEL') }}
|
|
||||||
</woot-button>
|
|
||||||
<woot-button
|
|
||||||
v-tooltip.top-end="$t('FILTER.TOOLTIP_LABEL')"
|
v-tooltip.top-end="$t('FILTER.TOOLTIP_LABEL')"
|
||||||
variant="clear"
|
variant="clear"
|
||||||
color-scheme="secondary"
|
color-scheme="secondary"
|
||||||
|
@ -33,8 +58,22 @@ l<template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<add-custom-views
|
||||||
|
v-if="showAddCustomViewsModal"
|
||||||
|
:custom-views-query="customViewsQuery"
|
||||||
|
@close="onCloseAddCustomViewsModal"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<delete-custom-views
|
||||||
|
v-if="showDeleteCustomViewsModal"
|
||||||
|
:show-delete-popup.sync="showDeleteCustomViewsModal"
|
||||||
|
:active-custom-view="activeCustomView"
|
||||||
|
:custom-views-id="customViewsId"
|
||||||
|
@close="onCloseDeleteCustomViewsModal"
|
||||||
|
/>
|
||||||
|
|
||||||
<chat-type-tabs
|
<chat-type-tabs
|
||||||
v-if="!hasAppliedFilters"
|
v-if="!hasAppliedFiltersOrActiveCustomViews"
|
||||||
:items="assigneeTabItems"
|
:items="assigneeTabItems"
|
||||||
:active-tab="activeAssigneeTab"
|
:active-tab="activeAssigneeTab"
|
||||||
@chatTabChange="updateAssigneeTab"
|
@chatTabChange="updateAssigneeTab"
|
||||||
|
@ -50,6 +89,7 @@ l<template>
|
||||||
:key="chat.id"
|
:key="chat.id"
|
||||||
:active-label="label"
|
:active-label="label"
|
||||||
:team-id="teamId"
|
:team-id="teamId"
|
||||||
|
:custom-views-id="customViewsId"
|
||||||
:chat="chat"
|
:chat="chat"
|
||||||
:conversation-type="conversationType"
|
:conversation-type="conversationType"
|
||||||
:show-assignee="showAssigneeInConversationCard"
|
:show-assignee="showAssigneeInConversationCard"
|
||||||
|
@ -107,6 +147,8 @@ import conversationMixin from '../mixins/conversations';
|
||||||
import wootConstants from '../constants';
|
import wootConstants from '../constants';
|
||||||
import advancedFilterTypes from './widgets/conversation/advancedFilterItems';
|
import advancedFilterTypes from './widgets/conversation/advancedFilterItems';
|
||||||
import filterQueryGenerator from '../helper/filterQueryGenerator.js';
|
import filterQueryGenerator from '../helper/filterQueryGenerator.js';
|
||||||
|
import AddCustomViews from 'dashboard/routes/dashboard/customviews/AddCustomViews';
|
||||||
|
import DeleteCustomViews from 'dashboard/routes/dashboard/customviews/DeleteCustomViews.vue';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
hasPressedAltAndJKey,
|
hasPressedAltAndJKey,
|
||||||
|
@ -115,10 +157,12 @@ import {
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
AddCustomViews,
|
||||||
ChatTypeTabs,
|
ChatTypeTabs,
|
||||||
ConversationCard,
|
ConversationCard,
|
||||||
ChatFilter,
|
ChatFilter,
|
||||||
ConversationAdvancedFilter,
|
ConversationAdvancedFilter,
|
||||||
|
DeleteCustomViews,
|
||||||
},
|
},
|
||||||
mixins: [timeMixin, conversationMixin, eventListenerMixins],
|
mixins: [timeMixin, conversationMixin, eventListenerMixins],
|
||||||
props: {
|
props: {
|
||||||
|
@ -138,6 +182,10 @@ export default {
|
||||||
type: String,
|
type: String,
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
|
customViewsId: {
|
||||||
|
type: [String, Number],
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -148,6 +196,9 @@ export default {
|
||||||
...filter,
|
...filter,
|
||||||
attributeName: this.$t(`FILTER.ATTRIBUTES.${filter.attributeI18nKey}`),
|
attributeName: this.$t(`FILTER.ATTRIBUTES.${filter.attributeI18nKey}`),
|
||||||
})),
|
})),
|
||||||
|
customViewsQuery: {},
|
||||||
|
showAddCustomViewsModal: false,
|
||||||
|
showDeleteCustomViewsModal: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -162,10 +213,24 @@ export default {
|
||||||
activeInbox: 'getSelectedInbox',
|
activeInbox: 'getSelectedInbox',
|
||||||
conversationStats: 'conversationStats/getStats',
|
conversationStats: 'conversationStats/getStats',
|
||||||
appliedFilters: 'getAppliedConversationFilters',
|
appliedFilters: 'getAppliedConversationFilters',
|
||||||
|
customViews: 'customViews/getCustomViews',
|
||||||
}),
|
}),
|
||||||
hasAppliedFilters() {
|
hasAppliedFilters() {
|
||||||
return this.appliedFilters.length;
|
return this.appliedFilters.length;
|
||||||
},
|
},
|
||||||
|
hasActiveCustomViews() {
|
||||||
|
return this.activeCustomView && this.customViewsId !== 0;
|
||||||
|
},
|
||||||
|
hasAppliedFiltersOrActiveCustomViews() {
|
||||||
|
return this.hasAppliedFilters || this.hasActiveCustomViews;
|
||||||
|
},
|
||||||
|
savedCustomViewsValue() {
|
||||||
|
if (this.hasActiveCustomViews) {
|
||||||
|
const payload = this.activeCustomView.query;
|
||||||
|
this.fetchSavedFilteredConversations(payload);
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
},
|
||||||
assigneeTabItems() {
|
assigneeTabItems() {
|
||||||
return this.$t('CHAT_LIST.ASSIGNEE_TYPE_TABS').map(item => {
|
return this.$t('CHAT_LIST.ASSIGNEE_TYPE_TABS').map(item => {
|
||||||
const count = this.conversationStats[item.COUNT_KEY] || 0;
|
const count = this.conversationStats[item.COUNT_KEY] || 0;
|
||||||
|
@ -188,7 +253,9 @@ export default {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
currentPageFilterKey() {
|
currentPageFilterKey() {
|
||||||
return this.hasAppliedFilters ? 'appliedFilters' : this.activeAssigneeTab;
|
return this.hasAppliedFiltersOrActiveCustomViews
|
||||||
|
? 'appliedFilters'
|
||||||
|
: this.activeAssigneeTab;
|
||||||
},
|
},
|
||||||
currentFiltersPage() {
|
currentFiltersPage() {
|
||||||
return this.$store.getters['conversationPage/getCurrentPageFilter'](
|
return this.$store.getters['conversationPage/getCurrentPageFilter'](
|
||||||
|
@ -211,6 +278,9 @@ export default {
|
||||||
conversationType: this.conversationType
|
conversationType: this.conversationType
|
||||||
? this.conversationType
|
? this.conversationType
|
||||||
: undefined,
|
: undefined,
|
||||||
|
customViews: this.hasActiveCustomViews
|
||||||
|
? this.savedCustomViewsValue
|
||||||
|
: undefined,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
pageTitle() {
|
pageTitle() {
|
||||||
|
@ -226,11 +296,14 @@ export default {
|
||||||
if (this.conversationType === 'mention') {
|
if (this.conversationType === 'mention') {
|
||||||
return this.$t('CHAT_LIST.MENTION_HEADING');
|
return this.$t('CHAT_LIST.MENTION_HEADING');
|
||||||
}
|
}
|
||||||
|
if (this.hasActiveCustomViews) {
|
||||||
|
return this.activeCustomView.name;
|
||||||
|
}
|
||||||
return this.$t('CHAT_LIST.TAB_HEADING');
|
return this.$t('CHAT_LIST.TAB_HEADING');
|
||||||
},
|
},
|
||||||
conversationList() {
|
conversationList() {
|
||||||
let conversationList = [];
|
let conversationList = [];
|
||||||
if (!this.hasAppliedFilters) {
|
if (!this.hasAppliedFiltersOrActiveCustomViews) {
|
||||||
const filters = this.conversationFilters;
|
const filters = this.conversationFilters;
|
||||||
if (this.activeAssigneeTab === 'me') {
|
if (this.activeAssigneeTab === 'me') {
|
||||||
conversationList = [...this.mineChatsList(filters)];
|
conversationList = [...this.mineChatsList(filters)];
|
||||||
|
@ -245,6 +318,16 @@ export default {
|
||||||
|
|
||||||
return conversationList;
|
return conversationList;
|
||||||
},
|
},
|
||||||
|
activeCustomView() {
|
||||||
|
if (this.customViewsId) {
|
||||||
|
const activeView = this.customViews.filter(
|
||||||
|
view => view.id === Number(this.customViewsId)
|
||||||
|
);
|
||||||
|
const [firstValue] = activeView;
|
||||||
|
return firstValue;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
},
|
||||||
activeTeam() {
|
activeTeam() {
|
||||||
if (this.teamId) {
|
if (this.teamId) {
|
||||||
return this.$store.getters['teams/getTeam'](this.teamId);
|
return this.$store.getters['teams/getTeam'](this.teamId);
|
||||||
|
@ -265,6 +348,9 @@ export default {
|
||||||
conversationType() {
|
conversationType() {
|
||||||
this.resetAndFetchData();
|
this.resetAndFetchData();
|
||||||
},
|
},
|
||||||
|
activeCustomView() {
|
||||||
|
this.resetAndFetchData();
|
||||||
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.$store.dispatch('setChatFilter', this.activeStatus);
|
this.$store.dispatch('setChatFilter', this.activeStatus);
|
||||||
|
@ -279,10 +365,23 @@ export default {
|
||||||
if (this.$route.name !== 'home') {
|
if (this.$route.name !== 'home') {
|
||||||
this.$router.push({ name: 'home' });
|
this.$router.push({ name: 'home' });
|
||||||
}
|
}
|
||||||
|
this.customViewsQuery = { payload: payload };
|
||||||
this.$store.dispatch('conversationPage/reset');
|
this.$store.dispatch('conversationPage/reset');
|
||||||
this.$store.dispatch('emptyAllConversations');
|
this.$store.dispatch('emptyAllConversations');
|
||||||
this.fetchFilteredConversations(payload);
|
this.fetchFilteredConversations(payload);
|
||||||
},
|
},
|
||||||
|
onClickOpenAddCustomViewsModal() {
|
||||||
|
this.showAddCustomViewsModal = true;
|
||||||
|
},
|
||||||
|
onCloseAddCustomViewsModal() {
|
||||||
|
this.showAddCustomViewsModal = false;
|
||||||
|
},
|
||||||
|
onClickOpenDeleteCustomViewsModal() {
|
||||||
|
this.showDeleteCustomViewsModal = true;
|
||||||
|
},
|
||||||
|
onCloseDeleteCustomViewsModal() {
|
||||||
|
this.showDeleteCustomViewsModal = false;
|
||||||
|
},
|
||||||
onToggleAdvanceFiltersModal() {
|
onToggleAdvanceFiltersModal() {
|
||||||
this.showAdvancedFilters = !this.showAdvancedFilters;
|
this.showAdvancedFilters = !this.showAdvancedFilters;
|
||||||
},
|
},
|
||||||
|
@ -334,6 +433,13 @@ export default {
|
||||||
this.$store.dispatch('conversationPage/reset');
|
this.$store.dispatch('conversationPage/reset');
|
||||||
this.$store.dispatch('emptyAllConversations');
|
this.$store.dispatch('emptyAllConversations');
|
||||||
this.$store.dispatch('clearConversationFilters');
|
this.$store.dispatch('clearConversationFilters');
|
||||||
|
if (this.hasActiveCustomViews) {
|
||||||
|
const payload = this.activeCustomView.query;
|
||||||
|
this.fetchSavedFilteredConversations(payload);
|
||||||
|
}
|
||||||
|
if (this.customViewsId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.fetchConversations();
|
this.fetchConversations();
|
||||||
},
|
},
|
||||||
fetchConversations() {
|
fetchConversations() {
|
||||||
|
@ -342,8 +448,12 @@ export default {
|
||||||
.then(() => this.$emit('conversation-load'));
|
.then(() => this.$emit('conversation-load'));
|
||||||
},
|
},
|
||||||
loadMoreConversations() {
|
loadMoreConversations() {
|
||||||
if (!this.hasAppliedFilters) {
|
if (!this.hasAppliedFiltersOrActiveCustomViews) {
|
||||||
this.fetchConversations();
|
this.fetchConversations();
|
||||||
|
}
|
||||||
|
if (this.hasActiveCustomViews) {
|
||||||
|
const payload = this.activeCustomView.query;
|
||||||
|
this.fetchSavedFilteredConversations(payload);
|
||||||
} else {
|
} else {
|
||||||
this.fetchFilteredConversations(this.appliedFilters);
|
this.fetchFilteredConversations(this.appliedFilters);
|
||||||
}
|
}
|
||||||
|
@ -358,6 +468,15 @@ export default {
|
||||||
.then(() => this.$emit('conversation-load'));
|
.then(() => this.$emit('conversation-load'));
|
||||||
this.showAdvancedFilters = false;
|
this.showAdvancedFilters = false;
|
||||||
},
|
},
|
||||||
|
fetchSavedFilteredConversations(payload) {
|
||||||
|
let page = this.currentFiltersPage + 1;
|
||||||
|
this.$store
|
||||||
|
.dispatch('fetchFilteredConversations', {
|
||||||
|
queryData: payload,
|
||||||
|
page,
|
||||||
|
})
|
||||||
|
.then(() => this.$emit('conversation-load'));
|
||||||
|
},
|
||||||
updateAssigneeTab(selectedTab) {
|
updateAssigneeTab(selectedTab) {
|
||||||
if (this.activeAssigneeTab !== selectedTab) {
|
if (this.activeAssigneeTab !== selectedTab) {
|
||||||
bus.$emit('clearSearchInput');
|
bus.$emit('clearSearchInput');
|
||||||
|
@ -412,11 +531,14 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
.filter__applied {
|
.filter__applied {
|
||||||
padding: var(--space-slab) 0 !important;
|
padding: 0 0 var(--space-slab) 0 !important;
|
||||||
border-bottom: 1px solid var(--color-border);
|
border-bottom: 1px solid var(--color-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-sub-title {
|
.page-sub-title {
|
||||||
margin-left: var(--space-normal);
|
margin-left: var(--space-normal);
|
||||||
}
|
}
|
||||||
|
.delete-custom-view__button {
|
||||||
|
margin-right: var(--space-normal);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
:inboxes="inboxes"
|
:inboxes="inboxes"
|
||||||
:labels="labels"
|
:labels="labels"
|
||||||
:teams="teams"
|
:teams="teams"
|
||||||
|
:custom-views="customViews"
|
||||||
:menu-config="activeSecondaryMenu"
|
:menu-config="activeSecondaryMenu"
|
||||||
:current-role="currentRole"
|
:current-role="currentRole"
|
||||||
@add-label="showAddLabelPopup"
|
@add-label="showAddLabelPopup"
|
||||||
|
@ -87,6 +88,7 @@ export default {
|
||||||
currentUser: 'getCurrentUser',
|
currentUser: 'getCurrentUser',
|
||||||
globalConfig: 'globalConfig/get',
|
globalConfig: 'globalConfig/get',
|
||||||
inboxes: 'inboxes/getInboxes',
|
inboxes: 'inboxes/getInboxes',
|
||||||
|
customViews: 'customViews/getCustomViews',
|
||||||
accountId: 'getCurrentAccountId',
|
accountId: 'getCurrentAccountId',
|
||||||
currentRole: 'getCurrentRole',
|
currentRole: 'getCurrentRole',
|
||||||
labels: 'labels/getLabelsOnSidebar',
|
labels: 'labels/getLabelsOnSidebar',
|
||||||
|
@ -122,6 +124,7 @@ export default {
|
||||||
mounted() {
|
mounted() {
|
||||||
this.$store.dispatch('labels/get');
|
this.$store.dispatch('labels/get');
|
||||||
this.$store.dispatch('inboxes/get');
|
this.$store.dispatch('inboxes/get');
|
||||||
|
this.$store.dispatch('customViews/get');
|
||||||
this.$store.dispatch('notifications/unReadCount');
|
this.$store.dispatch('notifications/unReadCount');
|
||||||
this.$store.dispatch('teams/get');
|
this.$store.dispatch('teams/get');
|
||||||
this.$store.dispatch('attributes/get');
|
this.$store.dispatch('attributes/get');
|
||||||
|
|
|
@ -14,6 +14,8 @@ const conversations = accountId => ({
|
||||||
'conversations_through_team',
|
'conversations_through_team',
|
||||||
'conversation_mentions',
|
'conversation_mentions',
|
||||||
'conversation_through_mentions',
|
'conversation_through_mentions',
|
||||||
|
'custom_view_conversations',
|
||||||
|
'conversations_through_custom_view',
|
||||||
],
|
],
|
||||||
menuItems: [
|
menuItems: [
|
||||||
{
|
{
|
||||||
|
|
|
@ -40,6 +40,10 @@ export default {
|
||||||
type: Array,
|
type: Array,
|
||||||
default: () => [],
|
default: () => [],
|
||||||
},
|
},
|
||||||
|
customViews: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
menuConfig: {
|
menuConfig: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => {},
|
default: () => {},
|
||||||
|
@ -150,11 +154,35 @@ export default {
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
customViewsSection() {
|
||||||
|
return {
|
||||||
|
icon: 'folder',
|
||||||
|
label: 'CUSTOM_VIEWS',
|
||||||
|
hasSubMenu: true,
|
||||||
|
key: 'custom_view',
|
||||||
|
children: this.customViews
|
||||||
|
.filter(view => view.filter_type === 'conversation')
|
||||||
|
.map(view => ({
|
||||||
|
id: view.id,
|
||||||
|
label: view.name,
|
||||||
|
truncateLabel: true,
|
||||||
|
toState: frontendURL(
|
||||||
|
`accounts/${this.accountId}/custom_view/${view.id}`
|
||||||
|
),
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
},
|
||||||
additionalSecondaryMenuItems() {
|
additionalSecondaryMenuItems() {
|
||||||
let conversationMenuItems = [this.inboxSection, this.labelSection];
|
let conversationMenuItems = [this.inboxSection, this.labelSection];
|
||||||
if (this.teams.length) {
|
if (this.teams.length) {
|
||||||
conversationMenuItems = [this.teamSection, ...conversationMenuItems];
|
conversationMenuItems = [this.teamSection, ...conversationMenuItems];
|
||||||
}
|
}
|
||||||
|
if (this.customViews.length) {
|
||||||
|
conversationMenuItems = [
|
||||||
|
this.customViewsSection,
|
||||||
|
...conversationMenuItems,
|
||||||
|
];
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
conversations: conversationMenuItems,
|
conversations: conversationMenuItems,
|
||||||
contacts: [this.contactLabelSection],
|
contacts: [this.contactLabelSection],
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
v-else
|
v-else
|
||||||
class="secondary-menu--title secondary-menu--link fs-small"
|
class="secondary-menu--title secondary-menu--link fs-small"
|
||||||
:class="computedClass"
|
:class="computedClass"
|
||||||
:to="menuItem.toState"
|
:to="menuItem && menuItem.toState"
|
||||||
>
|
>
|
||||||
<fluent-icon
|
<fluent-icon
|
||||||
:icon="menuItem.icon"
|
:icon="menuItem.icon"
|
||||||
|
|
|
@ -192,7 +192,6 @@ export default {
|
||||||
'setConversationFilters',
|
'setConversationFilters',
|
||||||
JSON.parse(JSON.stringify(this.appliedFilters))
|
JSON.parse(JSON.stringify(this.appliedFilters))
|
||||||
);
|
);
|
||||||
this.appliedFilters[this.appliedFilters.length - 1].query_operator = null;
|
|
||||||
this.$emit('applyFilter', this.appliedFilters);
|
this.$emit('applyFilter', this.appliedFilters);
|
||||||
},
|
},
|
||||||
resetFilter(index, currentFilter) {
|
resetFilter(index, currentFilter) {
|
||||||
|
|
|
@ -128,6 +128,10 @@ export default {
|
||||||
type: [String, Number],
|
type: [String, Number],
|
||||||
default: 0,
|
default: 0,
|
||||||
},
|
},
|
||||||
|
customViewsId: {
|
||||||
|
type: [String, Number],
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
showAssignee: {
|
showAssignee: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
|
@ -242,6 +246,7 @@ export default {
|
||||||
id: chat.id,
|
id: chat.id,
|
||||||
label: this.activeLabel,
|
label: this.activeLabel,
|
||||||
teamId: this.teamId,
|
teamId: this.teamId,
|
||||||
|
customViewsId: this.customViewsId,
|
||||||
conversationType: this.conversationType,
|
conversationType: this.conversationType,
|
||||||
});
|
});
|
||||||
router.push({ path: frontendURL(path) });
|
router.push({ path: frontendURL(path) });
|
||||||
|
|
|
@ -12,6 +12,7 @@ export const conversationUrl = ({
|
||||||
label,
|
label,
|
||||||
teamId,
|
teamId,
|
||||||
conversationType = '',
|
conversationType = '',
|
||||||
|
customViewsId,
|
||||||
}) => {
|
}) => {
|
||||||
let url = `accounts/${accountId}/conversations/${id}`;
|
let url = `accounts/${accountId}/conversations/${id}`;
|
||||||
if (activeInbox) {
|
if (activeInbox) {
|
||||||
|
@ -20,6 +21,8 @@ export const conversationUrl = ({
|
||||||
url = `accounts/${accountId}/label/${label}/conversations/${id}`;
|
url = `accounts/${accountId}/label/${label}/conversations/${id}`;
|
||||||
} else if (teamId) {
|
} else if (teamId) {
|
||||||
url = `accounts/${accountId}/team/${teamId}/conversations/${id}`;
|
url = `accounts/${accountId}/team/${teamId}/conversations/${id}`;
|
||||||
|
} else if (customViewsId && customViewsId !== 0) {
|
||||||
|
url = `accounts/${accountId}/custom_view/${customViewsId}/conversations/${id}`;
|
||||||
} else if (conversationType === 'mention') {
|
} else if (conversationType === 'mention') {
|
||||||
url = `accounts/${accountId}/mentions/conversations/${id}`;
|
url = `accounts/${accountId}/mentions/conversations/${id}`;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,10 @@ const generatePayload = data => {
|
||||||
}
|
}
|
||||||
return item;
|
return item;
|
||||||
});
|
});
|
||||||
|
// For every query added, the query_operator is set default to and so the
|
||||||
|
// last query will have an extra query_operator, this would break the api.
|
||||||
|
// Setting this to null for all query payload
|
||||||
|
payload[payload.length - 1].query_operator = undefined;
|
||||||
return { payload };
|
return { payload };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@ const testData = [
|
||||||
attribute_key: 'id',
|
attribute_key: 'id',
|
||||||
filter_operator: 'equal_to',
|
filter_operator: 'equal_to',
|
||||||
values: 'This is a test',
|
values: 'This is a test',
|
||||||
query_operator: null,
|
query_operator: 'or',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -53,7 +53,6 @@ const finalResult = {
|
||||||
attribute_key: 'id',
|
attribute_key: 'id',
|
||||||
filter_operator: 'equal_to',
|
filter_operator: 'equal_to',
|
||||||
values: ['This is a test'],
|
values: ['This is a test'],
|
||||||
query_operator: null,
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
|
@ -40,7 +40,7 @@
|
||||||
"AUTO_RESOLVE_DURATION": {
|
"AUTO_RESOLVE_DURATION": {
|
||||||
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
|
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
|
||||||
"PLACEHOLDER": "30",
|
"PLACEHOLDER": "30",
|
||||||
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day)"
|
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day and maximum 999 days)"
|
||||||
},
|
},
|
||||||
"FEATURES": {
|
"FEATURES": {
|
||||||
"INBOUND_EMAIL_ENABLED": "Conversation continuity with emails is enabled for your account.",
|
"INBOUND_EMAIL_ENABLED": "Conversation continuity with emails is enabled for your account.",
|
||||||
|
|
|
@ -32,6 +32,35 @@
|
||||||
"BROWSER_LANGUAGE": "Browser Language",
|
"BROWSER_LANGUAGE": "Browser Language",
|
||||||
"COUNTRY_NAME": "Country Name",
|
"COUNTRY_NAME": "Country Name",
|
||||||
"REFERER_LINK": "Referer link"
|
"REFERER_LINK": "Referer link"
|
||||||
|
},
|
||||||
|
"CUSTOM_VIEWS": {
|
||||||
|
"ADD": {
|
||||||
|
"TITLE": "Do you want to save this filter?",
|
||||||
|
"LABEL": "Name this filter",
|
||||||
|
"PLACEHOLDER": "Enter a name for this filter",
|
||||||
|
"ERROR_MESSAGE": "Name is required",
|
||||||
|
"SAVE_BUTTON": "Save filter",
|
||||||
|
"CANCEL_BUTTON": "Cancel",
|
||||||
|
"API": {
|
||||||
|
"SUCCESS_MESSAGE": "Custom view created successfully",
|
||||||
|
"ERROR_MESSAGE": "Error while creating custom view"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"DELETE": {
|
||||||
|
"DELETE_BUTTON": "Delete filter",
|
||||||
|
"MODAL": {
|
||||||
|
"CONFIRM": {
|
||||||
|
"TITLE": "Confirm Deletion",
|
||||||
|
"MESSAGE": "Are you sure to delete the filter ",
|
||||||
|
"YES": "Yes, Delete ",
|
||||||
|
"NO": "No, Keep "
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"API": {
|
||||||
|
"SUCCESS_MESSAGE": "Custom view deleted successfully",
|
||||||
|
"ERROR_MESSAGE": "Error while deleting custom view"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,7 @@
|
||||||
"AUTO_RESOLVE_DURATION": {
|
"AUTO_RESOLVE_DURATION": {
|
||||||
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
|
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
|
||||||
"PLACEHOLDER": "30",
|
"PLACEHOLDER": "30",
|
||||||
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day)"
|
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day and maximum 999 days)"
|
||||||
},
|
},
|
||||||
"FEATURES": {
|
"FEATURES": {
|
||||||
"INBOUND_EMAIL_ENABLED": "Conversation continuity with emails is enabled for your account.",
|
"INBOUND_EMAIL_ENABLED": "Conversation continuity with emails is enabled for your account.",
|
||||||
|
|
|
@ -72,11 +72,7 @@
|
||||||
},
|
},
|
||||||
"AVAILABILITY": {
|
"AVAILABILITY": {
|
||||||
"LABEL": "Availability",
|
"LABEL": "Availability",
|
||||||
"STATUSES_LIST": [
|
"STATUSES_LIST": ["Online", "Busy", "Offline"]
|
||||||
"Online",
|
|
||||||
"Busy",
|
|
||||||
"Offline"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"EMAIL": {
|
"EMAIL": {
|
||||||
"LABEL": "Your email address",
|
"LABEL": "Your email address",
|
||||||
|
@ -153,6 +149,7 @@
|
||||||
"CUSTOM_ATTRIBUTES": "Custom Attributes",
|
"CUSTOM_ATTRIBUTES": "Custom Attributes",
|
||||||
"AUTOMATION": "Automation",
|
"AUTOMATION": "Automation",
|
||||||
"TEAMS": "Teams",
|
"TEAMS": "Teams",
|
||||||
|
"CUSTOM_VIEWS": "Folders",
|
||||||
"ALL_CONTACTS": "All Contacts",
|
"ALL_CONTACTS": "All Contacts",
|
||||||
"TAGGED_WITH": "Tagged with",
|
"TAGGED_WITH": "Tagged with",
|
||||||
"NEW_LABEL": "New label",
|
"NEW_LABEL": "New label",
|
||||||
|
|
|
@ -40,7 +40,7 @@
|
||||||
"AUTO_RESOLVE_DURATION": {
|
"AUTO_RESOLVE_DURATION": {
|
||||||
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
|
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
|
||||||
"PLACEHOLDER": "30",
|
"PLACEHOLDER": "30",
|
||||||
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day)"
|
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day and maximum 999 days)"
|
||||||
},
|
},
|
||||||
"FEATURES": {
|
"FEATURES": {
|
||||||
"INBOUND_EMAIL_ENABLED": "Conversation continuity with emails is enabled for your account.",
|
"INBOUND_EMAIL_ENABLED": "Conversation continuity with emails is enabled for your account.",
|
||||||
|
|
|
@ -40,7 +40,7 @@
|
||||||
"AUTO_RESOLVE_DURATION": {
|
"AUTO_RESOLVE_DURATION": {
|
||||||
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
|
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
|
||||||
"PLACEHOLDER": "30",
|
"PLACEHOLDER": "30",
|
||||||
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day)"
|
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day and maximum 999 days)"
|
||||||
},
|
},
|
||||||
"FEATURES": {
|
"FEATURES": {
|
||||||
"INBOUND_EMAIL_ENABLED": "Conversation continuity with emails is enabled for your account.",
|
"INBOUND_EMAIL_ENABLED": "Conversation continuity with emails is enabled for your account.",
|
||||||
|
|
|
@ -40,7 +40,7 @@
|
||||||
"AUTO_RESOLVE_DURATION": {
|
"AUTO_RESOLVE_DURATION": {
|
||||||
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
|
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
|
||||||
"PLACEHOLDER": "30",
|
"PLACEHOLDER": "30",
|
||||||
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day)"
|
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day and maximum 999 days)"
|
||||||
},
|
},
|
||||||
"FEATURES": {
|
"FEATURES": {
|
||||||
"INBOUND_EMAIL_ENABLED": "あなたのアカウントでは、メールでの会話が継続できるようになっています。",
|
"INBOUND_EMAIL_ENABLED": "あなたのアカウントでは、メールでの会話が継続できるようになっています。",
|
||||||
|
|
|
@ -40,7 +40,7 @@
|
||||||
"AUTO_RESOLVE_DURATION": {
|
"AUTO_RESOLVE_DURATION": {
|
||||||
"LABEL": "പ്രവർത്തനമൊന്നുമില്ലെങ്കിൽ ടിക്കറ്റിന് ശേഷമുള്ള ദിവസങ്ങളുടെ എണ്ണം യാന്ത്രികമായി പരിഹരിക്കേണ്ടതാണ്",
|
"LABEL": "പ്രവർത്തനമൊന്നുമില്ലെങ്കിൽ ടിക്കറ്റിന് ശേഷമുള്ള ദിവസങ്ങളുടെ എണ്ണം യാന്ത്രികമായി പരിഹരിക്കേണ്ടതാണ്",
|
||||||
"PLACEHOLDER": "30",
|
"PLACEHOLDER": "30",
|
||||||
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day)"
|
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day and maximum 999 days)"
|
||||||
},
|
},
|
||||||
"FEATURES": {
|
"FEATURES": {
|
||||||
"INBOUND_EMAIL_ENABLED": "നിങ്ങളുടെ അക്കൗണ്ടിനായി ഇമെയിലുകളുമായുള്ള സംഭാഷണ തുടർച്ച പ്രവർത്തനക്ഷമമാക്കി.",
|
"INBOUND_EMAIL_ENABLED": "നിങ്ങളുടെ അക്കൗണ്ടിനായി ഇമെയിലുകളുമായുള്ള സംഭാഷണ തുടർച്ച പ്രവർത്തനക്ഷമമാക്കി.",
|
||||||
|
|
|
@ -40,7 +40,7 @@
|
||||||
"AUTO_RESOLVE_DURATION": {
|
"AUTO_RESOLVE_DURATION": {
|
||||||
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
|
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
|
||||||
"PLACEHOLDER": "30",
|
"PLACEHOLDER": "30",
|
||||||
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day)"
|
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day and maximum 999 days)"
|
||||||
},
|
},
|
||||||
"FEATURES": {
|
"FEATURES": {
|
||||||
"INBOUND_EMAIL_ENABLED": "Conversation continuity with emails is enabled for your account.",
|
"INBOUND_EMAIL_ENABLED": "Conversation continuity with emails is enabled for your account.",
|
||||||
|
|
|
@ -40,7 +40,7 @@
|
||||||
"AUTO_RESOLVE_DURATION": {
|
"AUTO_RESOLVE_DURATION": {
|
||||||
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
|
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
|
||||||
"PLACEHOLDER": "30",
|
"PLACEHOLDER": "30",
|
||||||
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day)"
|
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day and maximum 999 days)"
|
||||||
},
|
},
|
||||||
"FEATURES": {
|
"FEATURES": {
|
||||||
"INBOUND_EMAIL_ENABLED": "Conversation continuity with emails is enabled for your account.",
|
"INBOUND_EMAIL_ENABLED": "Conversation continuity with emails is enabled for your account.",
|
||||||
|
|
|
@ -40,7 +40,7 @@
|
||||||
"AUTO_RESOLVE_DURATION": {
|
"AUTO_RESOLVE_DURATION": {
|
||||||
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
|
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
|
||||||
"PLACEHOLDER": "30",
|
"PLACEHOLDER": "30",
|
||||||
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day)"
|
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day and maximum 999 days)"
|
||||||
},
|
},
|
||||||
"FEATURES": {
|
"FEATURES": {
|
||||||
"INBOUND_EMAIL_ENABLED": "Conversation continuity with emails is enabled for your account.",
|
"INBOUND_EMAIL_ENABLED": "Conversation continuity with emails is enabled for your account.",
|
||||||
|
|
|
@ -40,7 +40,7 @@
|
||||||
"AUTO_RESOLVE_DURATION": {
|
"AUTO_RESOLVE_DURATION": {
|
||||||
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
|
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
|
||||||
"PLACEHOLDER": "30",
|
"PLACEHOLDER": "30",
|
||||||
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day)"
|
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day and maximum 999 days)"
|
||||||
},
|
},
|
||||||
"FEATURES": {
|
"FEATURES": {
|
||||||
"INBOUND_EMAIL_ENABLED": "การสนทนาด้วยอีเมล์ถูกเปิดสำหรับบัญชีของคุณ",
|
"INBOUND_EMAIL_ENABLED": "การสนทนาด้วยอีเมล์ถูกเปิดสำหรับบัญชีของคุณ",
|
||||||
|
|
|
@ -40,7 +40,7 @@
|
||||||
"AUTO_RESOLVE_DURATION": {
|
"AUTO_RESOLVE_DURATION": {
|
||||||
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
|
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
|
||||||
"PLACEHOLDER": "30",
|
"PLACEHOLDER": "30",
|
||||||
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day)"
|
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day and maximum 999 days)"
|
||||||
},
|
},
|
||||||
"FEATURES": {
|
"FEATURES": {
|
||||||
"INBOUND_EMAIL_ENABLED": "Conversation continuity with emails is enabled for your account.",
|
"INBOUND_EMAIL_ENABLED": "Conversation continuity with emails is enabled for your account.",
|
||||||
|
|
|
@ -40,7 +40,7 @@
|
||||||
"AUTO_RESOLVE_DURATION": {
|
"AUTO_RESOLVE_DURATION": {
|
||||||
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
|
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
|
||||||
"PLACEHOLDER": "30",
|
"PLACEHOLDER": "30",
|
||||||
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day)"
|
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day and maximum 999 days)"
|
||||||
},
|
},
|
||||||
"FEATURES": {
|
"FEATURES": {
|
||||||
"INBOUND_EMAIL_ENABLED": "Tính liên tục của cuộc trò chuyện với email được kích hoạt cho tài khoản của bạn.",
|
"INBOUND_EMAIL_ENABLED": "Tính liên tục của cuộc trò chuyện với email được kích hoạt cho tài khoản của bạn.",
|
||||||
|
|
|
@ -40,7 +40,7 @@
|
||||||
"AUTO_RESOLVE_DURATION": {
|
"AUTO_RESOLVE_DURATION": {
|
||||||
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
|
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
|
||||||
"PLACEHOLDER": "30",
|
"PLACEHOLDER": "30",
|
||||||
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day)"
|
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day and maximum 999 days)"
|
||||||
},
|
},
|
||||||
"FEATURES": {
|
"FEATURES": {
|
||||||
"INBOUND_EMAIL_ENABLED": "您的帐户启用了与电子邮件的对话连续性。",
|
"INBOUND_EMAIL_ENABLED": "您的帐户启用了与电子邮件的对话连续性。",
|
||||||
|
|
|
@ -40,7 +40,7 @@
|
||||||
"AUTO_RESOLVE_DURATION": {
|
"AUTO_RESOLVE_DURATION": {
|
||||||
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
|
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
|
||||||
"PLACEHOLDER": "30",
|
"PLACEHOLDER": "30",
|
||||||
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day)"
|
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day and maximum 999 days)"
|
||||||
},
|
},
|
||||||
"FEATURES": {
|
"FEATURES": {
|
||||||
"INBOUND_EMAIL_ENABLED": "您的帳戶啟用了電子信箱與對話的持續性功能。",
|
"INBOUND_EMAIL_ENABLED": "您的帳戶啟用了電子信箱與對話的持續性功能。",
|
||||||
|
|
|
@ -159,6 +159,12 @@ export default {
|
||||||
border-right: 1px solid var(--color-border);
|
border-right: 1px solid var(--color-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.list-group {
|
||||||
|
.list-group-item {
|
||||||
|
background-color: var(--white);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.close-button {
|
.close-button {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: var(--space-normal);
|
right: var(--space-normal);
|
||||||
|
|
|
@ -160,7 +160,6 @@ export default {
|
||||||
'contacts/setContactFilters',
|
'contacts/setContactFilters',
|
||||||
JSON.parse(JSON.stringify(this.appliedFilters))
|
JSON.parse(JSON.stringify(this.appliedFilters))
|
||||||
);
|
);
|
||||||
this.appliedFilters[this.appliedFilters.length - 1].query_operator = null;
|
|
||||||
this.$emit('applyFilter', this.appliedFilters);
|
this.$emit('applyFilter', this.appliedFilters);
|
||||||
},
|
},
|
||||||
resetFilter(index, currentFilter) {
|
resetFilter(index, currentFilter) {
|
||||||
|
|
|
@ -236,6 +236,12 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.list-group {
|
||||||
|
.list-group-item {
|
||||||
|
background-color: var(--white);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
::v-deep {
|
::v-deep {
|
||||||
.contact--profile {
|
.contact--profile {
|
||||||
padding-bottom: var(--space-slab);
|
padding-bottom: var(--space-slab);
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
:label="label"
|
:label="label"
|
||||||
:team-id="teamId"
|
:team-id="teamId"
|
||||||
:conversation-type="conversationType"
|
:conversation-type="conversationType"
|
||||||
|
:custom-views-id="customViewsId"
|
||||||
@conversation-load="onConversationLoad"
|
@conversation-load="onConversationLoad"
|
||||||
>
|
>
|
||||||
<pop-over-search />
|
<pop-over-search />
|
||||||
|
@ -54,6 +55,10 @@ export default {
|
||||||
type: String,
|
type: String,
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
|
customViewsId: {
|
||||||
|
type: [String, Number],
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -83,6 +83,25 @@ export default {
|
||||||
teamId: route.params.teamId,
|
teamId: route.params.teamId,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: frontendURL('accounts/:accountId/custom_view/:id'),
|
||||||
|
name: 'custom_view_conversations',
|
||||||
|
roles: ['administrator', 'agent'],
|
||||||
|
component: ConversationView,
|
||||||
|
props: route => ({ customViewsId: route.params.id }),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: frontendURL(
|
||||||
|
'accounts/:accountId/custom_view/:id/conversations/:conversation_id'
|
||||||
|
),
|
||||||
|
name: 'conversations_through_custom_view',
|
||||||
|
roles: ['administrator', 'agent'],
|
||||||
|
component: ConversationView,
|
||||||
|
props: route => ({
|
||||||
|
conversationId: route.params.conversation_id,
|
||||||
|
customViewsId: route.params.id,
|
||||||
|
}),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: frontendURL('accounts/:accountId/mentions/conversations'),
|
path: frontendURL('accounts/:accountId/mentions/conversations'),
|
||||||
name: 'conversation_mentions',
|
name: 'conversation_mentions',
|
||||||
|
|
|
@ -0,0 +1,97 @@
|
||||||
|
<template>
|
||||||
|
<woot-modal :show.sync="show" :on-close="onClose">
|
||||||
|
<woot-modal-header :header-title="$t('FILTER.CUSTOM_VIEWS.ADD.TITLE')" />
|
||||||
|
<form class="row" @submit.prevent="saveCustomViews">
|
||||||
|
<div class="medium-12 columns">
|
||||||
|
<woot-input
|
||||||
|
v-model="name"
|
||||||
|
:label="$t('FILTER.CUSTOM_VIEWS.ADD.LABEL')"
|
||||||
|
type="text"
|
||||||
|
:error="
|
||||||
|
$v.name.$error ? $t('FILTER.CUSTOM_VIEWS.ADD.ERROR_MESSAGE') : ''
|
||||||
|
"
|
||||||
|
:class="{ error: $v.name.$error }"
|
||||||
|
:placeholder="$t('FILTER.CUSTOM_VIEWS.ADD.PLACEHOLDER')"
|
||||||
|
@blur="$v.name.$touch"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="modal-footer">
|
||||||
|
<woot-button :disabled="isButtonDisabled">
|
||||||
|
{{ $t('FILTER.CUSTOM_VIEWS.ADD.SAVE_BUTTON') }}
|
||||||
|
</woot-button>
|
||||||
|
<woot-button variant="clear" @click.prevent="onClose">
|
||||||
|
{{ $t('FILTER.CUSTOM_VIEWS.ADD.CANCEL_BUTTON') }}
|
||||||
|
</woot-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</woot-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { required, minLength } from 'vuelidate/lib/validators';
|
||||||
|
import alertMixin from 'shared/mixins/alertMixin';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
mixins: [alertMixin],
|
||||||
|
props: {
|
||||||
|
filterType: {
|
||||||
|
type: Number,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
customViewsQuery: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
show: true,
|
||||||
|
name: '',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
isButtonDisabled() {
|
||||||
|
return this.$v.name.$invalid;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
validations: {
|
||||||
|
name: {
|
||||||
|
required,
|
||||||
|
minLength: minLength(1),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
onClose() {
|
||||||
|
this.$emit('close');
|
||||||
|
},
|
||||||
|
async saveCustomViews() {
|
||||||
|
this.$v.$touch();
|
||||||
|
if (this.$v.$invalid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await this.$store.dispatch('customViews/create', {
|
||||||
|
name: this.name,
|
||||||
|
filter_type: this.filterType,
|
||||||
|
query: this.customViewsQuery,
|
||||||
|
});
|
||||||
|
this.alertMessage = this.$t(
|
||||||
|
'FILTER.CUSTOM_VIEWS.ADD.API.SUCCESS_MESSAGE'
|
||||||
|
);
|
||||||
|
this.onClose();
|
||||||
|
} catch (error) {
|
||||||
|
const errorMessage = error?.message;
|
||||||
|
this.alertMessage =
|
||||||
|
errorMessage || this.$t('FILTER.CUSTOM_VIEWS.ADD.API.ERROR_MESSAGE');
|
||||||
|
} finally {
|
||||||
|
this.showAlert(this.alertMessage);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,77 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<woot-delete-modal
|
||||||
|
v-if="showDeletePopup"
|
||||||
|
:show.sync="showDeletePopup"
|
||||||
|
:on-close="closeDeletePopup"
|
||||||
|
:on-confirm="deleteSavedCustomViews"
|
||||||
|
:title="$t('FILTER.CUSTOM_VIEWS.DELETE.MODAL.CONFIRM.TITLE')"
|
||||||
|
:message="deleteMessage"
|
||||||
|
:confirm-text="deleteConfirmText"
|
||||||
|
:reject-text="deleteRejectText"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import alertMixin from 'shared/mixins/alertMixin';
|
||||||
|
export default {
|
||||||
|
mixins: [alertMixin],
|
||||||
|
props: {
|
||||||
|
showDeletePopup: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
activeCustomView: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {},
|
||||||
|
},
|
||||||
|
customViewsId: {
|
||||||
|
type: [String, Number],
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
deleteMessage() {
|
||||||
|
return `${this.$t(
|
||||||
|
'FILTER.CUSTOM_VIEWS.DELETE.MODAL.CONFIRM.MESSAGE'
|
||||||
|
)} ${this.activeCustomView && this.activeCustomView.name} ?`;
|
||||||
|
},
|
||||||
|
deleteConfirmText() {
|
||||||
|
return `${this.$t('FILTER.CUSTOM_VIEWS.DELETE.MODAL.CONFIRM.YES')} ${this
|
||||||
|
.activeCustomView && this.activeCustomView.name}`;
|
||||||
|
},
|
||||||
|
deleteRejectText() {
|
||||||
|
return `${this.$t('FILTER.CUSTOM_VIEWS.DELETE.MODAL.CONFIRM.NO')} ${this
|
||||||
|
.activeCustomView && this.activeCustomView.name}`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
async deleteSavedCustomViews() {
|
||||||
|
try {
|
||||||
|
await this.$store.dispatch(
|
||||||
|
'customViews/delete',
|
||||||
|
Number(this.customViewsId)
|
||||||
|
);
|
||||||
|
this.closeDeletePopup();
|
||||||
|
this.showAlert(
|
||||||
|
this.$t('FILTER.CUSTOM_VIEWS.DELETE.API.SUCCESS_MESSAGE')
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
const errorMessage =
|
||||||
|
error?.response?.message ||
|
||||||
|
this.$t('FILTER.CUSTOM_VIEWS.DELETE.API.ERROR_MESSAGE');
|
||||||
|
this.showAlert(errorMessage);
|
||||||
|
}
|
||||||
|
if (this.$route.name !== 'home') {
|
||||||
|
this.$router.push({ name: 'home' });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
closeDeletePopup() {
|
||||||
|
this.$emit('close');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -116,7 +116,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { required, minValue } from 'vuelidate/lib/validators';
|
import { required, minValue, maxValue } from 'vuelidate/lib/validators';
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import alertMixin from 'shared/mixins/alertMixin';
|
import alertMixin from 'shared/mixins/alertMixin';
|
||||||
import configMixin from 'shared/mixins/configMixin';
|
import configMixin from 'shared/mixins/configMixin';
|
||||||
|
@ -146,6 +146,7 @@ export default {
|
||||||
},
|
},
|
||||||
autoResolveDuration: {
|
autoResolveDuration: {
|
||||||
minValue: minValue(1),
|
minValue: minValue(1),
|
||||||
|
maxValue: maxValue(999),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<ve-table
|
<ve-table
|
||||||
v-else
|
v-else
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
scroll-width="155rem"
|
scroll-width="190rem"
|
||||||
:table-data="tableData"
|
:table-data="tableData"
|
||||||
:border-around="true"
|
:border-around="true"
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -31,6 +31,7 @@ import teams from './modules/teams';
|
||||||
import userNotificationSettings from './modules/userNotificationSettings';
|
import userNotificationSettings from './modules/userNotificationSettings';
|
||||||
import webhooks from './modules/webhooks';
|
import webhooks from './modules/webhooks';
|
||||||
import attributes from './modules/attributes';
|
import attributes from './modules/attributes';
|
||||||
|
import customViews from './modules/customViews';
|
||||||
|
|
||||||
Vue.use(Vuex);
|
Vue.use(Vuex);
|
||||||
export default new Vuex.Store({
|
export default new Vuex.Store({
|
||||||
|
@ -65,5 +66,6 @@ export default new Vuex.Store({
|
||||||
userNotificationSettings,
|
userNotificationSettings,
|
||||||
webhooks,
|
webhooks,
|
||||||
attributes,
|
attributes,
|
||||||
|
customViews,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
79
app/javascript/dashboard/store/modules/customViews.js
Normal file
79
app/javascript/dashboard/store/modules/customViews.js
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
import * as MutationHelpers from 'shared/helpers/vuex/mutationHelpers';
|
||||||
|
import types from '../mutation-types';
|
||||||
|
import CustomViewsAPI from '../../api/customViews';
|
||||||
|
|
||||||
|
export const state = {
|
||||||
|
records: [],
|
||||||
|
uiFlags: {
|
||||||
|
isFetching: false,
|
||||||
|
isCreating: false,
|
||||||
|
isDeleting: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getters = {
|
||||||
|
getUIFlags(_state) {
|
||||||
|
return _state.uiFlags;
|
||||||
|
},
|
||||||
|
getCustomViews(_state) {
|
||||||
|
return _state.records;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const actions = {
|
||||||
|
get: async function getCustomViews({ commit }) {
|
||||||
|
commit(types.SET_CUSTOM_VIEW_UI_FLAG, { isFetching: true });
|
||||||
|
try {
|
||||||
|
const response = await CustomViewsAPI.getCustomViews();
|
||||||
|
commit(types.SET_CUSTOM_VIEW, response.data);
|
||||||
|
} catch (error) {
|
||||||
|
// Ignore error
|
||||||
|
} finally {
|
||||||
|
commit(types.SET_CUSTOM_VIEW_UI_FLAG, { isFetching: false });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
create: async function createCustomViews({ commit }, obj) {
|
||||||
|
commit(types.SET_CUSTOM_VIEW_UI_FLAG, { isCreating: true });
|
||||||
|
try {
|
||||||
|
const response = await CustomViewsAPI.create(obj);
|
||||||
|
commit(types.ADD_CUSTOM_VIEW, response.data);
|
||||||
|
} catch (error) {
|
||||||
|
const errorMessage = error?.response?.data?.message;
|
||||||
|
throw new Error(errorMessage);
|
||||||
|
} finally {
|
||||||
|
commit(types.SET_CUSTOM_VIEW_UI_FLAG, { isCreating: false });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
delete: async ({ commit }, id) => {
|
||||||
|
commit(types.SET_CUSTOM_VIEW_UI_FLAG, { isDeleting: true });
|
||||||
|
try {
|
||||||
|
await CustomViewsAPI.delete(id);
|
||||||
|
commit(types.DELETE_CUSTOM_VIEW, id);
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(error);
|
||||||
|
} finally {
|
||||||
|
commit(types.SET_CUSTOM_VIEW_UI_FLAG, { isDeleting: false });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const mutations = {
|
||||||
|
[types.SET_CUSTOM_VIEW_UI_FLAG](_state, data) {
|
||||||
|
_state.uiFlags = {
|
||||||
|
..._state.uiFlags,
|
||||||
|
...data,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
[types.ADD_CUSTOM_VIEW]: MutationHelpers.create,
|
||||||
|
[types.SET_CUSTOM_VIEW]: MutationHelpers.set,
|
||||||
|
[types.DELETE_CUSTOM_VIEW]: MutationHelpers.destroy,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
namespaced: true,
|
||||||
|
actions,
|
||||||
|
state,
|
||||||
|
getters,
|
||||||
|
mutations,
|
||||||
|
};
|
|
@ -0,0 +1,72 @@
|
||||||
|
import axios from 'axios';
|
||||||
|
import { actions } from '../../customViews';
|
||||||
|
import * as types from '../../../mutation-types';
|
||||||
|
import customViewList from './fixtures';
|
||||||
|
|
||||||
|
const commit = jest.fn();
|
||||||
|
global.axios = axios;
|
||||||
|
jest.mock('axios');
|
||||||
|
|
||||||
|
describe('#actions', () => {
|
||||||
|
describe('#get', () => {
|
||||||
|
it('sends correct actions if API is success', async () => {
|
||||||
|
axios.get.mockResolvedValue({ data: customViewList });
|
||||||
|
await actions.get({ commit });
|
||||||
|
expect(commit.mock.calls).toEqual([
|
||||||
|
[types.default.SET_CUSTOM_VIEW_UI_FLAG, { isFetching: true }],
|
||||||
|
[types.default.SET_CUSTOM_VIEW, customViewList],
|
||||||
|
[types.default.SET_CUSTOM_VIEW_UI_FLAG, { isFetching: false }],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
it('sends correct actions if API is error', async () => {
|
||||||
|
axios.get.mockRejectedValue({ message: 'Incorrect header' });
|
||||||
|
await actions.get({ commit });
|
||||||
|
expect(commit.mock.calls).toEqual([
|
||||||
|
[types.default.SET_CUSTOM_VIEW_UI_FLAG, { isFetching: true }],
|
||||||
|
[types.default.SET_CUSTOM_VIEW_UI_FLAG, { isFetching: false }],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#create', () => {
|
||||||
|
it('sends correct actions if API is success', async () => {
|
||||||
|
axios.post.mockResolvedValue({ data: customViewList[0] });
|
||||||
|
await actions.create({ commit }, customViewList[0]);
|
||||||
|
expect(commit.mock.calls).toEqual([
|
||||||
|
[types.default.SET_CUSTOM_VIEW_UI_FLAG, { isCreating: true }],
|
||||||
|
[types.default.ADD_CUSTOM_VIEW, customViewList[0]],
|
||||||
|
[types.default.SET_CUSTOM_VIEW_UI_FLAG, { isCreating: false }],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
it('sends correct actions if API is error', async () => {
|
||||||
|
axios.post.mockRejectedValue({ message: 'Incorrect header' });
|
||||||
|
await expect(actions.create({ commit })).rejects.toThrow(Error);
|
||||||
|
expect(commit.mock.calls).toEqual([
|
||||||
|
[types.default.SET_CUSTOM_VIEW_UI_FLAG, { isCreating: true }],
|
||||||
|
[types.default.SET_CUSTOM_VIEW_UI_FLAG, { isCreating: false }],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#delete', () => {
|
||||||
|
it('sends correct actions if API is success', async () => {
|
||||||
|
axios.delete.mockResolvedValue({ data: customViewList[0] });
|
||||||
|
await actions.delete({ commit }, customViewList[0].id);
|
||||||
|
expect(commit.mock.calls).toEqual([
|
||||||
|
[types.default.SET_CUSTOM_VIEW_UI_FLAG, { isDeleting: true }],
|
||||||
|
[types.default.DELETE_CUSTOM_VIEW, customViewList[0].id],
|
||||||
|
[types.default.SET_CUSTOM_VIEW_UI_FLAG, { isDeleting: false }],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
it('sends correct actions if API is error', async () => {
|
||||||
|
axios.delete.mockRejectedValue({ message: 'Incorrect header' });
|
||||||
|
await expect(
|
||||||
|
actions.delete({ commit }, customViewList[0].id)
|
||||||
|
).rejects.toThrow(Error);
|
||||||
|
expect(commit.mock.calls).toEqual([
|
||||||
|
[types.default.SET_CUSTOM_VIEW_UI_FLAG, { isDeleting: true }],
|
||||||
|
[types.default.SET_CUSTOM_VIEW_UI_FLAG, { isDeleting: false }],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,42 @@
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
name: 'Custom view',
|
||||||
|
filter_type: 'conversation',
|
||||||
|
query: {
|
||||||
|
payload: [
|
||||||
|
{
|
||||||
|
attribute_key: 'assignee_id',
|
||||||
|
filter_operator: 'equal_to',
|
||||||
|
values: [45],
|
||||||
|
query_operator: 'and',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
attribute_key: 'inbox_id',
|
||||||
|
filter_operator: 'equal_to',
|
||||||
|
values: [144],
|
||||||
|
query_operator: 'and',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Custom view 1',
|
||||||
|
filter_type: 'conversation',
|
||||||
|
query: {
|
||||||
|
payload: [
|
||||||
|
{
|
||||||
|
attribute_key: 'browser_language',
|
||||||
|
filter_operator: 'equal_to',
|
||||||
|
values: ['eng'],
|
||||||
|
query_operator: 'or',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
attribute_key: 'campaign_id',
|
||||||
|
filter_operator: 'equal_to',
|
||||||
|
values: [15],
|
||||||
|
query_operator: 'and',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
|
@ -0,0 +1,65 @@
|
||||||
|
import { getters } from '../../customViews';
|
||||||
|
import customViewList from './fixtures';
|
||||||
|
|
||||||
|
describe('#getters', () => {
|
||||||
|
it('getCustomViews', () => {
|
||||||
|
const state = { records: customViewList };
|
||||||
|
expect(getters.getCustomViews(state)).toEqual([
|
||||||
|
{
|
||||||
|
name: 'Custom view',
|
||||||
|
filter_type: 'conversation',
|
||||||
|
query: {
|
||||||
|
payload: [
|
||||||
|
{
|
||||||
|
attribute_key: 'assignee_id',
|
||||||
|
filter_operator: 'equal_to',
|
||||||
|
values: [45],
|
||||||
|
query_operator: 'and',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
attribute_key: 'inbox_id',
|
||||||
|
filter_operator: 'equal_to',
|
||||||
|
values: [144],
|
||||||
|
query_operator: 'and',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Custom view 1',
|
||||||
|
filter_type: 'conversation',
|
||||||
|
query: {
|
||||||
|
payload: [
|
||||||
|
{
|
||||||
|
attribute_key: 'browser_language',
|
||||||
|
filter_operator: 'equal_to',
|
||||||
|
values: ['eng'],
|
||||||
|
query_operator: 'or',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
attribute_key: 'campaign_id',
|
||||||
|
filter_operator: 'equal_to',
|
||||||
|
values: [15],
|
||||||
|
query_operator: 'and',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getUIFlags', () => {
|
||||||
|
const state = {
|
||||||
|
uiFlags: {
|
||||||
|
isFetching: true,
|
||||||
|
isCreating: false,
|
||||||
|
isDeleting: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
expect(getters.getUIFlags(state)).toEqual({
|
||||||
|
isFetching: true,
|
||||||
|
isCreating: false,
|
||||||
|
isDeleting: false,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,29 @@
|
||||||
|
import types from '../../../mutation-types';
|
||||||
|
import { mutations } from '../../customViews';
|
||||||
|
import customViewList from './fixtures';
|
||||||
|
|
||||||
|
describe('#mutations', () => {
|
||||||
|
describe('#SET_CUSTOM_VIEW', () => {
|
||||||
|
it('set custom view records', () => {
|
||||||
|
const state = { records: [] };
|
||||||
|
mutations[types.SET_CUSTOM_VIEW](state, customViewList);
|
||||||
|
expect(state.records).toEqual(customViewList);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#ADD_CUSTOM_VIEW', () => {
|
||||||
|
it('push newly created custom views to the store', () => {
|
||||||
|
const state = { records: [customViewList] };
|
||||||
|
mutations[types.ADD_CUSTOM_VIEW](state, customViewList[0]);
|
||||||
|
expect(state.records).toEqual([customViewList, customViewList[0]]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#DELETE_CUSTOM_VIEW', () => {
|
||||||
|
it('delete custom view record', () => {
|
||||||
|
const state = { records: [customViewList[0]] };
|
||||||
|
mutations[types.DELETE_CUSTOM_VIEW](state, customViewList[0]);
|
||||||
|
expect(state.records).toEqual([customViewList[0]]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -188,4 +188,10 @@ export default {
|
||||||
ADD_CUSTOM_ATTRIBUTE: 'ADD_CUSTOM_ATTRIBUTE',
|
ADD_CUSTOM_ATTRIBUTE: 'ADD_CUSTOM_ATTRIBUTE',
|
||||||
EDIT_CUSTOM_ATTRIBUTE: 'EDIT_CUSTOM_ATTRIBUTE',
|
EDIT_CUSTOM_ATTRIBUTE: 'EDIT_CUSTOM_ATTRIBUTE',
|
||||||
DELETE_CUSTOM_ATTRIBUTE: 'DELETE_CUSTOM_ATTRIBUTE',
|
DELETE_CUSTOM_ATTRIBUTE: 'DELETE_CUSTOM_ATTRIBUTE',
|
||||||
|
|
||||||
|
// Custom Views
|
||||||
|
SET_CUSTOM_VIEW_UI_FLAG: 'SET_CUSTOM_VIEW_UI_FLAG',
|
||||||
|
SET_CUSTOM_VIEW: 'SET_CUSTOM_VIEW',
|
||||||
|
ADD_CUSTOM_VIEW: 'ADD_CUSTOM_VIEW',
|
||||||
|
DELETE_CUSTOM_VIEW: 'DELETE_CUSTOM_VIEW',
|
||||||
};
|
};
|
||||||
|
|
|
@ -71,6 +71,7 @@
|
||||||
"power-outline": "M8.204 4.82a.75.75 0 0 1 .634 1.36A7.51 7.51 0 0 0 4.5 12.991c0 4.148 3.358 7.51 7.499 7.51s7.499-3.362 7.499-7.51a7.51 7.51 0 0 0-4.323-6.804.75.75 0 1 1 .637-1.358 9.01 9.01 0 0 1 5.186 8.162c0 4.976-4.029 9.01-9 9.01C7.029 22 3 17.966 3 12.99a9.01 9.01 0 0 1 5.204-8.17ZM12 2.496a.75.75 0 0 1 .743.648l.007.102v7.5a.75.75 0 0 1-1.493.102l-.007-.102v-7.5a.75.75 0 0 1 .75-.75Z",
|
"power-outline": "M8.204 4.82a.75.75 0 0 1 .634 1.36A7.51 7.51 0 0 0 4.5 12.991c0 4.148 3.358 7.51 7.499 7.51s7.499-3.362 7.499-7.51a7.51 7.51 0 0 0-4.323-6.804.75.75 0 1 1 .637-1.358 9.01 9.01 0 0 1 5.186 8.162c0 4.976-4.029 9.01-9 9.01C7.029 22 3 17.966 3 12.99a9.01 9.01 0 0 1 5.204-8.17ZM12 2.496a.75.75 0 0 1 .743.648l.007.102v7.5a.75.75 0 0 1-1.493.102l-.007-.102v-7.5a.75.75 0 0 1 .75-.75Z",
|
||||||
"quote-outline": "M7.5 6a2.5 2.5 0 0 1 2.495 2.336l.005.206c-.01 3.555-1.24 6.614-3.705 9.223a.75.75 0 1 1-1.09-1.03c1.64-1.737 2.66-3.674 3.077-5.859A2.5 2.5 0 1 1 7.5 6Zm9 0a2.5 2.5 0 0 1 2.495 2.336l.005.206c-.01 3.56-1.238 6.614-3.705 9.223a.75.75 0 1 1-1.09-1.03c1.643-1.738 2.662-3.672 3.078-5.859A2.5 2.5 0 1 1 16.5 6Zm-9 1.5a1 1 0 1 0 .993 1.117l.007-.124a1 1 0 0 0-1-.993Zm9 0a1 1 0 1 0 .993 1.117l.007-.124a1 1 0 0 0-1-.993Z",
|
"quote-outline": "M7.5 6a2.5 2.5 0 0 1 2.495 2.336l.005.206c-.01 3.555-1.24 6.614-3.705 9.223a.75.75 0 1 1-1.09-1.03c1.64-1.737 2.66-3.674 3.077-5.859A2.5 2.5 0 1 1 7.5 6Zm9 0a2.5 2.5 0 0 1 2.495 2.336l.005.206c-.01 3.56-1.238 6.614-3.705 9.223a.75.75 0 1 1-1.09-1.03c1.643-1.738 2.662-3.672 3.078-5.859A2.5 2.5 0 1 1 16.5 6Zm-9 1.5a1 1 0 1 0 .993 1.117l.007-.124a1 1 0 0 0-1-.993Zm9 0a1 1 0 1 0 .993 1.117l.007-.124a1 1 0 0 0-1-.993Z",
|
||||||
"resize-large-outline": "M6.25 4.5A1.75 1.75 0 0 0 4.5 6.25v1.5a.75.75 0 0 1-1.5 0v-1.5A3.25 3.25 0 0 1 6.25 3h1.5a.75.75 0 0 1 0 1.5h-1.5ZM19.5 6.25a1.75 1.75 0 0 0-1.75-1.75h-1.5a.75.75 0 0 1 0-1.5h1.5A3.25 3.25 0 0 1 21 6.25v1.5a.75.75 0 0 1-1.5 0v-1.5ZM19.5 17.75a1.75 1.75 0 0 1-1.75 1.75h-1.5a.75.75 0 0 0 0 1.5h1.5A3.25 3.25 0 0 0 21 17.75v-1.5a.75.75 0 0 0-1.5 0v1.5ZM4.5 17.75c0 .966.784 1.75 1.75 1.75h1.5a.75.75 0 0 1 0 1.5h-1.5A3.25 3.25 0 0 1 3 17.75v-1.5a.75.75 0 0 1 1.5 0v1.5ZM8.25 6A2.25 2.25 0 0 0 6 8.25v7.5A2.25 2.25 0 0 0 8.25 18h7.5A2.25 2.25 0 0 0 18 15.75v-7.5A2.25 2.25 0 0 0 15.75 6h-7.5ZM7.5 8.25a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 .75.75v7.5a.75.75 0 0 1-.75.75h-7.5a.75.75 0 0 1-.75-.75v-7.5Z",
|
"resize-large-outline": "M6.25 4.5A1.75 1.75 0 0 0 4.5 6.25v1.5a.75.75 0 0 1-1.5 0v-1.5A3.25 3.25 0 0 1 6.25 3h1.5a.75.75 0 0 1 0 1.5h-1.5ZM19.5 6.25a1.75 1.75 0 0 0-1.75-1.75h-1.5a.75.75 0 0 1 0-1.5h1.5A3.25 3.25 0 0 1 21 6.25v1.5a.75.75 0 0 1-1.5 0v-1.5ZM19.5 17.75a1.75 1.75 0 0 1-1.75 1.75h-1.5a.75.75 0 0 0 0 1.5h1.5A3.25 3.25 0 0 0 21 17.75v-1.5a.75.75 0 0 0-1.5 0v1.5ZM4.5 17.75c0 .966.784 1.75 1.75 1.75h1.5a.75.75 0 0 1 0 1.5h-1.5A3.25 3.25 0 0 1 3 17.75v-1.5a.75.75 0 0 1 1.5 0v1.5ZM8.25 6A2.25 2.25 0 0 0 6 8.25v7.5A2.25 2.25 0 0 0 8.25 18h7.5A2.25 2.25 0 0 0 18 15.75v-7.5A2.25 2.25 0 0 0 15.75 6h-7.5ZM7.5 8.25a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 .75.75v7.5a.75.75 0 0 1-.75.75h-7.5a.75.75 0 0 1-.75-.75v-7.5Z",
|
||||||
|
"save-outline": "M3 5.75A2.75 2.75 0 0 1 5.75 3h9.964a3.25 3.25 0 0 1 2.299.952l2.035 2.035c.61.61.952 1.437.952 2.299v9.964A2.75 2.75 0 0 1 18.25 21H5.75A2.75 2.75 0 0 1 3 18.25V5.75ZM5.75 4.5c-.69 0-1.25.56-1.25 1.25v12.5c0 .69.56 1.25 1.25 1.25H6v-5.25A2.25 2.25 0 0 1 8.25 12h7.5A2.25 2.25 0 0 1 18 14.25v5.25h.25c.69 0 1.25-.56 1.25-1.25V8.286c0-.465-.184-.91-.513-1.238l-2.035-2.035a1.75 1.75 0 0 0-.952-.49V7.25a2.25 2.25 0 0 1-2.25 2.25h-4.5A2.25 2.25 0 0 1 7 7.25V4.5H5.75Zm10.75 15v-5.25a.75.75 0 0 0-.75-.75h-7.5a.75.75 0 0 0-.75.75v5.25h9Zm-8-15v2.75c0 .414.336.75.75.75h4.5a.75.75 0 0 0 .75-.75V4.5h-6Z",
|
||||||
"search-outline": "M10 2.75a7.25 7.25 0 0 1 5.63 11.819l4.9 4.9a.75.75 0 0 1-.976 1.134l-.084-.073-4.901-4.9A7.25 7.25 0 1 1 10 2.75Zm0 1.5a5.75 5.75 0 1 0 0 11.5 5.75 5.75 0 0 0 0-11.5Z",
|
"search-outline": "M10 2.75a7.25 7.25 0 0 1 5.63 11.819l4.9 4.9a.75.75 0 0 1-.976 1.134l-.084-.073-4.901-4.9A7.25 7.25 0 1 1 10 2.75Zm0 1.5a5.75 5.75 0 1 0 0 11.5 5.75 5.75 0 0 0 0-11.5Z",
|
||||||
"send-outline": "M5.694 12 2.299 3.272c-.236-.607.356-1.188.942-.982l.093.04 18 9a.75.75 0 0 1 .097 1.283l-.097.058-18 9c-.583.291-1.217-.244-1.065-.847l.03-.096L5.694 12 2.299 3.272 5.694 12ZM4.402 4.54l2.61 6.71h6.627a.75.75 0 0 1 .743.648l.007.102a.75.75 0 0 1-.649.743l-.101.007H7.01l-2.609 6.71L19.322 12 4.401 4.54Z",
|
"send-outline": "M5.694 12 2.299 3.272c-.236-.607.356-1.188.942-.982l.093.04 18 9a.75.75 0 0 1 .097 1.283l-.097.058-18 9c-.583.291-1.217-.244-1.065-.847l.03-.096L5.694 12 2.299 3.272 5.694 12ZM4.402 4.54l2.61 6.71h6.627a.75.75 0 0 1 .743.648l.007.102a.75.75 0 0 1-.649.743l-.101.007H7.01l-2.609 6.71L19.322 12 4.401 4.54Z",
|
||||||
"send-clock-outline": "M5.694 12 2.299 3.272c-.236-.608.356-1.189.942-.982l.093.04 18 9a.752.752 0 0 1 .264 1.124 6.473 6.473 0 0 0-4.272-1.452L4.402 4.54l2.61 6.71h6.627a.75.75 0 0 1 .724.556c-.472.26-.909.578-1.3.944H7.011l-2.609 6.71 6.753-3.377a6.522 6.522 0 0 0-.147 1.75l-7.674 3.838c-.583.291-1.217-.245-1.065-.847l.03-.096L5.694 12ZM23 17.5a5.5 5.5 0 1 0-11 0 5.5 5.5 0 0 0 11 0Zm-5.5 0h2a.5.5 0 1 1 0 1H17a.5.5 0 0 1-.5-.5v-3a.5.5 0 0 1 1 0v2.5Z",
|
"send-clock-outline": "M5.694 12 2.299 3.272c-.236-.608.356-1.189.942-.982l.093.04 18 9a.752.752 0 0 1 .264 1.124 6.473 6.473 0 0 0-4.272-1.452L4.402 4.54l2.61 6.71h6.627a.75.75 0 0 1 .724.556c-.472.26-.909.578-1.3.944H7.011l-2.609 6.71 6.753-3.377a6.522 6.522 0 0 0-.147 1.75l-7.674 3.838c-.583.291-1.217-.245-1.065-.847l.03-.096L5.694 12ZM23 17.5a5.5 5.5 0 1 0-11 0 5.5 5.5 0 0 0 11 0Zm-5.5 0h2a.5.5 0 1 1 0 1H17a.5.5 0 0 1-.5-.5v-3a.5.5 0 0 1 1 0v2.5Z",
|
||||||
|
|
|
@ -135,9 +135,5 @@ export default {
|
||||||
color: $color-body;
|
color: $color-body;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.close-unread-wrap {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -32,7 +32,7 @@ class Account < ApplicationRecord
|
||||||
}.freeze
|
}.freeze
|
||||||
|
|
||||||
validates :name, presence: true
|
validates :name, presence: true
|
||||||
validates :auto_resolve_duration, numericality: { greater_than_or_equal_to: 1, allow_nil: true }
|
validates :auto_resolve_duration, numericality: { greater_than_or_equal_to: 1, less_than_or_equal_to: 999, allow_nil: true }
|
||||||
|
|
||||||
has_many :account_users, dependent: :destroy_async
|
has_many :account_users, dependent: :destroy_async
|
||||||
has_many :agent_bot_inboxes, dependent: :destroy_async
|
has_many :agent_bot_inboxes, dependent: :destroy_async
|
||||||
|
|
|
@ -4,7 +4,7 @@ module AssignmentHandler
|
||||||
|
|
||||||
included do
|
included do
|
||||||
before_save :ensure_assignee_is_from_team
|
before_save :ensure_assignee_is_from_team
|
||||||
after_update :notify_assignment_change, :process_assignment_activities
|
after_commit :notify_assignment_change, :process_assignment_activities
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
|
@ -71,6 +71,7 @@ class Notification < ApplicationRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
# TODO: move to a data presenter
|
# TODO: move to a data presenter
|
||||||
|
# rubocop:disable Metrics/CyclomaticComplexity
|
||||||
def push_message_title
|
def push_message_title
|
||||||
case notification_type
|
case notification_type
|
||||||
when 'conversation_creation'
|
when 'conversation_creation'
|
||||||
|
@ -81,14 +82,15 @@ class Notification < ApplicationRecord
|
||||||
I18n.t(
|
I18n.t(
|
||||||
'notifications.notification_title.assigned_conversation_new_message',
|
'notifications.notification_title.assigned_conversation_new_message',
|
||||||
display_id: conversation.display_id,
|
display_id: conversation.display_id,
|
||||||
content: primary_actor.content&.truncate_words(10)
|
content: primary_actor&.content&.truncate_words(10)
|
||||||
)
|
)
|
||||||
when 'conversation_mention'
|
when 'conversation_mention'
|
||||||
"[##{conversation.display_id}] #{transform_user_mention_content primary_actor.content}"
|
"[##{conversation&.display_id}] #{transform_user_mention_content primary_actor&.content}"
|
||||||
else
|
else
|
||||||
''
|
''
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
# rubocop:enable Metrics/CyclomaticComplexity
|
||||||
|
|
||||||
def conversation
|
def conversation
|
||||||
return primary_actor.conversation if %w[assigned_conversation_new_message conversation_mention].include? notification_type
|
return primary_actor.conversation if %w[assigned_conversation_new_message conversation_mention].include? notification_type
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
shared: &shared
|
shared: &shared
|
||||||
version: '2.1.0'
|
version: '2.1.1'
|
||||||
|
|
||||||
development:
|
development:
|
||||||
<<: *shared
|
<<: *shared
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@chatwoot/chatwoot",
|
"name": "@chatwoot/chatwoot",
|
||||||
"version": "2.1.0",
|
"version": "2.1.1",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"eslint": "eslint app/**/*.{js,vue} --fix",
|
"eslint": "eslint app/**/*.{js,vue} --fix",
|
||||||
|
|
|
@ -5,6 +5,7 @@ require 'rails_helper'
|
||||||
RSpec.describe Account do
|
RSpec.describe Account do
|
||||||
it { is_expected.to validate_presence_of(:name) }
|
it { is_expected.to validate_presence_of(:name) }
|
||||||
it { is_expected.to validate_numericality_of(:auto_resolve_duration).is_greater_than_or_equal_to(1) }
|
it { is_expected.to validate_numericality_of(:auto_resolve_duration).is_greater_than_or_equal_to(1) }
|
||||||
|
it { is_expected.to validate_numericality_of(:auto_resolve_duration).is_less_than_or_equal_to(999) }
|
||||||
|
|
||||||
it { is_expected.to have_many(:users).through(:account_users) }
|
it { is_expected.to have_many(:users).through(:account_users) }
|
||||||
it { is_expected.to have_many(:account_users) }
|
it { is_expected.to have_many(:account_users) }
|
||||||
|
|
Loading…
Reference in a new issue