feat: Add the option for changing the contact/conversation sidebar items order (#3362)
This commit is contained in:
@@ -1,7 +1,9 @@
|
|||||||
import { shallowMount, createLocalVue } from '@vue/test-utils';
|
import { shallowMount, createLocalVue } from '@vue/test-utils';
|
||||||
import uiSettingsMixin from '../uiSettings';
|
import uiSettingsMixin, {
|
||||||
|
DEFAULT_CONVERSATION_SIDEBAR_ITEMS_ORDER,
|
||||||
|
DEFAULT_CONTACT_SIDEBAR_ITEMS_ORDER,
|
||||||
|
} from '../uiSettings';
|
||||||
import Vuex from 'vuex';
|
import Vuex from 'vuex';
|
||||||
|
|
||||||
const localVue = createLocalVue();
|
const localVue = createLocalVue();
|
||||||
localVue.use(Vuex);
|
localVue.use(Vuex);
|
||||||
|
|
||||||
@@ -17,6 +19,8 @@ describe('uiSettingsMixin', () => {
|
|||||||
display_rich_content_editor: false,
|
display_rich_content_editor: false,
|
||||||
enter_to_send_enabled: false,
|
enter_to_send_enabled: false,
|
||||||
is_ct_labels_open: true,
|
is_ct_labels_open: true,
|
||||||
|
conversation_sidebar_items_order: DEFAULT_CONVERSATION_SIDEBAR_ITEMS_ORDER,
|
||||||
|
contact_sidebar_items_order: DEFAULT_CONTACT_SIDEBAR_ITEMS_ORDER,
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
store = new Vuex.Store({ actions, getters });
|
store = new Vuex.Store({ actions, getters });
|
||||||
@@ -33,6 +37,8 @@ describe('uiSettingsMixin', () => {
|
|||||||
display_rich_content_editor: false,
|
display_rich_content_editor: false,
|
||||||
enter_to_send_enabled: false,
|
enter_to_send_enabled: false,
|
||||||
is_ct_labels_open: true,
|
is_ct_labels_open: true,
|
||||||
|
conversation_sidebar_items_order: DEFAULT_CONVERSATION_SIDEBAR_ITEMS_ORDER,
|
||||||
|
contact_sidebar_items_order: DEFAULT_CONTACT_SIDEBAR_ITEMS_ORDER,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -52,6 +58,8 @@ describe('uiSettingsMixin', () => {
|
|||||||
display_rich_content_editor: false,
|
display_rich_content_editor: false,
|
||||||
enter_to_send_enabled: true,
|
enter_to_send_enabled: true,
|
||||||
is_ct_labels_open: true,
|
is_ct_labels_open: true,
|
||||||
|
conversation_sidebar_items_order: DEFAULT_CONVERSATION_SIDEBAR_ITEMS_ORDER,
|
||||||
|
contact_sidebar_items_order: DEFAULT_CONTACT_SIDEBAR_ITEMS_ORDER,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
undefined
|
undefined
|
||||||
@@ -75,6 +83,8 @@ describe('uiSettingsMixin', () => {
|
|||||||
display_rich_content_editor: false,
|
display_rich_content_editor: false,
|
||||||
enter_to_send_enabled: false,
|
enter_to_send_enabled: false,
|
||||||
is_ct_labels_open: false,
|
is_ct_labels_open: false,
|
||||||
|
conversation_sidebar_items_order: DEFAULT_CONVERSATION_SIDEBAR_ITEMS_ORDER,
|
||||||
|
contact_sidebar_items_order: DEFAULT_CONTACT_SIDEBAR_ITEMS_ORDER,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
undefined
|
undefined
|
||||||
@@ -98,4 +108,36 @@ describe('uiSettingsMixin', () => {
|
|||||||
).toEqual(false);
|
).toEqual(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('#conversationSidebarItemsOrder', () => {
|
||||||
|
it('returns correct values', () => {
|
||||||
|
const Component = {
|
||||||
|
render() {},
|
||||||
|
title: 'TestComponent',
|
||||||
|
mixins: [uiSettingsMixin],
|
||||||
|
};
|
||||||
|
const wrapper = shallowMount(Component, { store, localVue });
|
||||||
|
expect(wrapper.vm.conversationSidebarItemsOrder).toEqual([
|
||||||
|
{ name: 'conversation_info' },
|
||||||
|
{ name: 'contact_attributes' },
|
||||||
|
{ name: 'previous_conversation' },
|
||||||
|
{ name: 'conversation_actions' },
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('#contactSidebarItemsOrder', () => {
|
||||||
|
it('returns correct values', () => {
|
||||||
|
const Component = {
|
||||||
|
render() {},
|
||||||
|
title: 'TestComponent',
|
||||||
|
mixins: [uiSettingsMixin],
|
||||||
|
};
|
||||||
|
const wrapper = shallowMount(Component, { store, localVue });
|
||||||
|
expect(wrapper.vm.contactSidebarItemsOrder).toEqual([
|
||||||
|
{ name: 'contact_attributes' },
|
||||||
|
{ name: 'contact_labels' },
|
||||||
|
{ name: 'previous_conversation' },
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,10 +1,28 @@
|
|||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
|
export const DEFAULT_CONVERSATION_SIDEBAR_ITEMS_ORDER = [
|
||||||
|
{ name: 'conversation_info' },
|
||||||
|
{ name: 'contact_attributes' },
|
||||||
|
{ name: 'previous_conversation' },
|
||||||
|
{ name: 'conversation_actions' },
|
||||||
|
];
|
||||||
|
export const DEFAULT_CONTACT_SIDEBAR_ITEMS_ORDER = [
|
||||||
|
{ name: 'contact_attributes' },
|
||||||
|
{ name: 'contact_labels' },
|
||||||
|
{ name: 'previous_conversation' },
|
||||||
|
];
|
||||||
export default {
|
export default {
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
uiSettings: 'getUISettings',
|
uiSettings: 'getUISettings',
|
||||||
}),
|
}),
|
||||||
|
conversationSidebarItemsOrder() {
|
||||||
|
const { conversation_sidebar_items_order: itemsOrder } = this.uiSettings;
|
||||||
|
return itemsOrder || DEFAULT_CONVERSATION_SIDEBAR_ITEMS_ORDER;
|
||||||
|
},
|
||||||
|
contactSidebarItemsOrder() {
|
||||||
|
const { contact_sidebar_items_order: itemsOrder } = this.uiSettings;
|
||||||
|
return itemsOrder || DEFAULT_CONTACT_SIDEBAR_ITEMS_ORDER;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
updateUISettings(uiSettings = {}) {
|
updateUISettings(uiSettings = {}) {
|
||||||
|
|||||||
@@ -12,42 +12,71 @@
|
|||||||
:contact="contact"
|
:contact="contact"
|
||||||
@panel-close="onClose"
|
@panel-close="onClose"
|
||||||
/>
|
/>
|
||||||
<accordion-item
|
<draggable
|
||||||
:title="$t('CONVERSATION_SIDEBAR.ACCORDION.CONTACT_ATTRIBUTES')"
|
:list="contactSidebarItems"
|
||||||
:is-open="isContactSidebarItemOpen('is_ct_custom_attr_open')"
|
:disabled="!dragEnabled"
|
||||||
compact
|
class="list-group"
|
||||||
@click="value => toggleSidebarUIState('is_ct_custom_attr_open', value)"
|
ghost-class="ghost"
|
||||||
|
@start="dragging = true"
|
||||||
|
@end="onDragEnd"
|
||||||
>
|
>
|
||||||
<custom-attributes
|
<transition-group>
|
||||||
:contact-id="contact.id"
|
<div
|
||||||
attribute-type="contact_attribute"
|
v-for="element in contactSidebarItems"
|
||||||
attribute-class="conversation--attribute"
|
:key="element.name"
|
||||||
:custom-attributes="contact.custom_attributes"
|
class="list-group-item"
|
||||||
class="even"
|
>
|
||||||
/>
|
<div v-if="element.name === 'contact_attributes'">
|
||||||
<custom-attribute-selector
|
<accordion-item
|
||||||
attribute-type="contact_attribute"
|
:title="$t('CONVERSATION_SIDEBAR.ACCORDION.CONTACT_ATTRIBUTES')"
|
||||||
:contact-id="contact.id"
|
:is-open="isContactSidebarItemOpen('is_ct_custom_attr_open')"
|
||||||
/>
|
compact
|
||||||
</accordion-item>
|
@click="
|
||||||
<accordion-item
|
value => toggleSidebarUIState('is_ct_custom_attr_open', value)
|
||||||
:title="$t('CONTACT_PANEL.SIDEBAR_SECTIONS.CONTACT_LABELS')"
|
"
|
||||||
:is-open="isContactSidebarItemOpen('is_ct_labels_open')"
|
>
|
||||||
@click="value => toggleSidebarUIState('is_ct_labels_open', value)"
|
<custom-attributes
|
||||||
>
|
:contact-id="contact.id"
|
||||||
<contact-label :contact-id="contact.id" class="contact-labels" />
|
attribute-type="contact_attribute"
|
||||||
</accordion-item>
|
attribute-class="conversation--attribute"
|
||||||
<accordion-item
|
:custom-attributes="contact.custom_attributes"
|
||||||
:title="$t('CONTACT_PANEL.SIDEBAR_SECTIONS.PREVIOUS_CONVERSATIONS')"
|
class="even"
|
||||||
:is-open="isContactSidebarItemOpen('is_ct_prev_conv_open')"
|
/>
|
||||||
@click="value => toggleSidebarUIState('is_ct_prev_conv_open', value)"
|
<custom-attribute-selector
|
||||||
>
|
attribute-type="contact_attribute"
|
||||||
<contact-conversations
|
:contact-id="contact.id"
|
||||||
v-if="contact.id"
|
/>
|
||||||
:contact-id="contact.id"
|
</accordion-item>
|
||||||
conversation-id=""
|
</div>
|
||||||
/>
|
<div v-if="element.name === 'contact_labels'">
|
||||||
</accordion-item>
|
<accordion-item
|
||||||
|
:title="$t('CONTACT_PANEL.SIDEBAR_SECTIONS.CONTACT_LABELS')"
|
||||||
|
:is-open="isContactSidebarItemOpen('is_ct_labels_open')"
|
||||||
|
@click="value => toggleSidebarUIState('is_ct_labels_open', value)"
|
||||||
|
>
|
||||||
|
<contact-label :contact-id="contact.id" class="contact-labels" />
|
||||||
|
</accordion-item>
|
||||||
|
</div>
|
||||||
|
<div v-if="element.name === 'previous_conversation'">
|
||||||
|
<accordion-item
|
||||||
|
:title="
|
||||||
|
$t('CONTACT_PANEL.SIDEBAR_SECTIONS.PREVIOUS_CONVERSATIONS')
|
||||||
|
"
|
||||||
|
:is-open="isContactSidebarItemOpen('is_ct_prev_conv_open')"
|
||||||
|
@click="
|
||||||
|
value => toggleSidebarUIState('is_ct_prev_conv_open', value)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<contact-conversations
|
||||||
|
v-if="contact.id"
|
||||||
|
:contact-id="contact.id"
|
||||||
|
conversation-id=""
|
||||||
|
/>
|
||||||
|
</accordion-item>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</transition-group>
|
||||||
|
</draggable>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -58,7 +87,7 @@ import ContactInfo from 'dashboard/routes/dashboard/conversation/contact/Contact
|
|||||||
import ContactLabel from 'dashboard/routes/dashboard/contacts/components/ContactLabels.vue';
|
import ContactLabel from 'dashboard/routes/dashboard/contacts/components/ContactLabels.vue';
|
||||||
import CustomAttributes from 'dashboard/routes/dashboard/conversation/customAttributes/CustomAttributes.vue';
|
import CustomAttributes from 'dashboard/routes/dashboard/conversation/customAttributes/CustomAttributes.vue';
|
||||||
import CustomAttributeSelector from 'dashboard/routes/dashboard/conversation/customAttributes/CustomAttributeSelector.vue';
|
import CustomAttributeSelector from 'dashboard/routes/dashboard/conversation/customAttributes/CustomAttributeSelector.vue';
|
||||||
|
import draggable from 'vuedraggable';
|
||||||
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -69,6 +98,7 @@ export default {
|
|||||||
ContactLabel,
|
ContactLabel,
|
||||||
CustomAttributes,
|
CustomAttributes,
|
||||||
CustomAttributeSelector,
|
CustomAttributeSelector,
|
||||||
|
draggable,
|
||||||
},
|
},
|
||||||
mixins: [uiSettingsMixin],
|
mixins: [uiSettingsMixin],
|
||||||
props: {
|
props: {
|
||||||
@@ -85,12 +115,30 @@ export default {
|
|||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
dragEnabled: true,
|
||||||
|
contactSidebarItems: [],
|
||||||
|
dragging: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
hasContactAttributes() {
|
hasContactAttributes() {
|
||||||
const { custom_attributes: customAttributes } = this.contact;
|
const { custom_attributes: customAttributes } = this.contact;
|
||||||
return customAttributes && Object.keys(customAttributes).length;
|
return customAttributes && Object.keys(customAttributes).length;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
mounted() {
|
||||||
|
this.contactSidebarItems = this.contactSidebarItemsOrder;
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onDragEnd() {
|
||||||
|
this.dragging = false;
|
||||||
|
this.updateUISettings({
|
||||||
|
contact_sidebar_items_order: this.contactSidebarItems,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -4,118 +4,95 @@
|
|||||||
<i class="ion-chevron-right" />
|
<i class="ion-chevron-right" />
|
||||||
</span>
|
</span>
|
||||||
<contact-info :contact="contact" :channel-type="channelType" />
|
<contact-info :contact="contact" :channel-type="channelType" />
|
||||||
<div class="conversation--actions">
|
<draggable
|
||||||
<accordion-item
|
:list="conversationSidebarItems"
|
||||||
:title="$t('CONVERSATION_SIDEBAR.ACCORDION.CONVERSATION_ACTIONS')"
|
:disabled="!dragEnabled"
|
||||||
:is-open="isContactSidebarItemOpen('is_conv_actions_open')"
|
class="list-group"
|
||||||
@click="value => toggleSidebarUIState('is_conv_actions_open', value)"
|
ghost-class="ghost"
|
||||||
>
|
@start="dragging = true"
|
||||||
<div>
|
@end="onDragEnd"
|
||||||
<div class="multiselect-wrap--small">
|
>
|
||||||
<contact-details-item
|
<transition-group>
|
||||||
compact
|
<div
|
||||||
:title="$t('CONVERSATION_SIDEBAR.ASSIGNEE_LABEL')"
|
v-for="element in conversationSidebarItems"
|
||||||
|
:key="element.name"
|
||||||
|
class="list-group-item"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-if="element.name === 'conversation_actions'"
|
||||||
|
class="conversation--actions"
|
||||||
|
>
|
||||||
|
<accordion-item
|
||||||
|
:title="$t('CONVERSATION_SIDEBAR.ACCORDION.CONVERSATION_ACTIONS')"
|
||||||
|
:is-open="isContactSidebarItemOpen('is_conv_actions_open')"
|
||||||
|
@click="
|
||||||
|
value => toggleSidebarUIState('is_conv_actions_open', value)
|
||||||
|
"
|
||||||
>
|
>
|
||||||
<template v-slot:button>
|
<conversation-action
|
||||||
<woot-button
|
:conversation-id="conversationId"
|
||||||
v-if="showSelfAssign"
|
:inbox-id="inboxId"
|
||||||
icon="ion-arrow-right-c"
|
/>
|
||||||
variant="link"
|
</accordion-item>
|
||||||
size="small"
|
|
||||||
@click="onSelfAssign"
|
|
||||||
>
|
|
||||||
{{ $t('CONVERSATION_SIDEBAR.SELF_ASSIGN') }}
|
|
||||||
</woot-button>
|
|
||||||
</template>
|
|
||||||
</contact-details-item>
|
|
||||||
<multiselect-dropdown
|
|
||||||
:options="agentsList"
|
|
||||||
:selected-item="assignedAgent"
|
|
||||||
:multiselector-title="$t('AGENT_MGMT.MULTI_SELECTOR.TITLE.AGENT')"
|
|
||||||
:multiselector-placeholder="
|
|
||||||
$t('AGENT_MGMT.MULTI_SELECTOR.PLACEHOLDER')
|
|
||||||
"
|
|
||||||
:no-search-result="
|
|
||||||
$t('AGENT_MGMT.MULTI_SELECTOR.SEARCH.NO_RESULTS.AGENT')
|
|
||||||
"
|
|
||||||
:input-placeholder="
|
|
||||||
$t('AGENT_MGMT.MULTI_SELECTOR.SEARCH.PLACEHOLDER.AGENT')
|
|
||||||
"
|
|
||||||
@click="onClickAssignAgent"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="multiselect-wrap--small">
|
<div v-else-if="element.name === 'conversation_info'">
|
||||||
<contact-details-item
|
<accordion-item
|
||||||
|
:title="$t('CONVERSATION_SIDEBAR.ACCORDION.CONVERSATION_INFO')"
|
||||||
|
:is-open="isContactSidebarItemOpen('is_conv_details_open')"
|
||||||
compact
|
compact
|
||||||
:title="$t('CONVERSATION_SIDEBAR.TEAM_LABEL')"
|
@click="
|
||||||
/>
|
value => toggleSidebarUIState('is_conv_details_open', value)
|
||||||
<multiselect-dropdown
|
|
||||||
:options="teamsList"
|
|
||||||
:selected-item="assignedTeam"
|
|
||||||
:multiselector-title="$t('AGENT_MGMT.MULTI_SELECTOR.TITLE.TEAM')"
|
|
||||||
:multiselector-placeholder="
|
|
||||||
$t('AGENT_MGMT.MULTI_SELECTOR.PLACEHOLDER')
|
|
||||||
"
|
"
|
||||||
:no-search-result="
|
>
|
||||||
$t('AGENT_MGMT.MULTI_SELECTOR.SEARCH.NO_RESULTS.TEAM')
|
<conversation-info
|
||||||
"
|
:conversation-attributes="conversationAdditionalAttributes"
|
||||||
:input-placeholder="
|
:contact-attributes="contactAdditionalAttributes"
|
||||||
$t('AGENT_MGMT.MULTI_SELECTOR.SEARCH.PLACEHOLDER.TEAM')
|
>
|
||||||
"
|
</conversation-info>
|
||||||
@click="onClickAssignTeam"
|
</accordion-item>
|
||||||
/>
|
</div>
|
||||||
|
<div v-else-if="element.name === 'contact_attributes'">
|
||||||
|
<accordion-item
|
||||||
|
:title="$t('CONVERSATION_SIDEBAR.ACCORDION.CONTACT_ATTRIBUTES')"
|
||||||
|
:is-open="isContactSidebarItemOpen('is_contact_attributes_open')"
|
||||||
|
compact
|
||||||
|
@click="
|
||||||
|
value =>
|
||||||
|
toggleSidebarUIState('is_contact_attributes_open', value)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<custom-attributes
|
||||||
|
attribute-type="contact_attribute"
|
||||||
|
attribute-class="conversation--attribute"
|
||||||
|
class="even"
|
||||||
|
:contact-id="contact.id"
|
||||||
|
/>
|
||||||
|
<custom-attribute-selector
|
||||||
|
attribute-type="contact_attribute"
|
||||||
|
:contact-id="contact.id"
|
||||||
|
/>
|
||||||
|
</accordion-item>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="element.name === 'previous_conversation'">
|
||||||
|
<accordion-item
|
||||||
|
v-if="contact.id"
|
||||||
|
:title="
|
||||||
|
$t('CONVERSATION_SIDEBAR.ACCORDION.PREVIOUS_CONVERSATION')
|
||||||
|
"
|
||||||
|
:is-open="isContactSidebarItemOpen('is_previous_conv_open')"
|
||||||
|
@click="
|
||||||
|
value => toggleSidebarUIState('is_previous_conv_open', value)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<contact-conversations
|
||||||
|
:contact-id="contact.id"
|
||||||
|
:conversation-id="conversationId"
|
||||||
|
/>
|
||||||
|
</accordion-item>
|
||||||
</div>
|
</div>
|
||||||
<contact-details-item
|
|
||||||
compact
|
|
||||||
:title="$t('CONVERSATION_SIDEBAR.ACCORDION.CONVERSATION_LABELS')"
|
|
||||||
/>
|
|
||||||
<conversation-labels :conversation-id="conversationId" />
|
|
||||||
</div>
|
</div>
|
||||||
</accordion-item>
|
</transition-group>
|
||||||
</div>
|
</draggable>
|
||||||
<accordion-item
|
|
||||||
:title="$t('CONVERSATION_SIDEBAR.ACCORDION.CONVERSATION_INFO')"
|
|
||||||
:is-open="isContactSidebarItemOpen('is_conv_details_open')"
|
|
||||||
compact
|
|
||||||
@click="value => toggleSidebarUIState('is_conv_details_open', value)"
|
|
||||||
>
|
|
||||||
<conversation-info
|
|
||||||
:conversation-attributes="conversationAdditionalAttributes"
|
|
||||||
:contact-attributes="contactAdditionalAttributes"
|
|
||||||
>
|
|
||||||
</conversation-info>
|
|
||||||
</accordion-item>
|
|
||||||
<accordion-item
|
|
||||||
:title="$t('CONVERSATION_SIDEBAR.ACCORDION.CONTACT_ATTRIBUTES')"
|
|
||||||
:is-open="isContactSidebarItemOpen('is_contact_attributes_open')"
|
|
||||||
compact
|
|
||||||
@click="
|
|
||||||
value => toggleSidebarUIState('is_contact_attributes_open', value)
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<custom-attributes
|
|
||||||
attribute-type="contact_attribute"
|
|
||||||
attribute-class="conversation--attribute"
|
|
||||||
class="even"
|
|
||||||
:contact-id="contact.id"
|
|
||||||
/>
|
|
||||||
<custom-attribute-selector
|
|
||||||
attribute-type="contact_attribute"
|
|
||||||
:contact-id="contact.id"
|
|
||||||
/>
|
|
||||||
</accordion-item>
|
|
||||||
|
|
||||||
<accordion-item
|
|
||||||
v-if="contact.id"
|
|
||||||
:title="$t('CONVERSATION_SIDEBAR.ACCORDION.PREVIOUS_CONVERSATION')"
|
|
||||||
:is-open="isContactSidebarItemOpen('is_previous_conv_open')"
|
|
||||||
@click="value => toggleSidebarUIState('is_previous_conv_open', value)"
|
|
||||||
>
|
|
||||||
<contact-conversations
|
|
||||||
:contact-id="contact.id"
|
|
||||||
:conversation-id="conversationId"
|
|
||||||
/>
|
|
||||||
</accordion-item>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -126,27 +103,25 @@ import agentMixin from '../../../mixins/agentMixin';
|
|||||||
|
|
||||||
import AccordionItem from 'dashboard/components/Accordion/AccordionItem';
|
import AccordionItem from 'dashboard/components/Accordion/AccordionItem';
|
||||||
import ContactConversations from './ContactConversations.vue';
|
import ContactConversations from './ContactConversations.vue';
|
||||||
import ContactDetailsItem from './ContactDetailsItem.vue';
|
import ConversationAction from './ConversationAction.vue';
|
||||||
|
|
||||||
import ContactInfo from './contact/ContactInfo';
|
import ContactInfo from './contact/ContactInfo';
|
||||||
import ConversationInfo from './ConversationInfo';
|
import ConversationInfo from './ConversationInfo';
|
||||||
import ConversationLabels from './labels/LabelBox.vue';
|
|
||||||
import MultiselectDropdown from 'shared/components/ui/MultiselectDropdown.vue';
|
|
||||||
import CustomAttributes from './customAttributes/CustomAttributes.vue';
|
import CustomAttributes from './customAttributes/CustomAttributes.vue';
|
||||||
import CustomAttributeSelector from './customAttributes/CustomAttributeSelector.vue';
|
import CustomAttributeSelector from './customAttributes/CustomAttributeSelector.vue';
|
||||||
|
import draggable from 'vuedraggable';
|
||||||
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
AccordionItem,
|
AccordionItem,
|
||||||
ContactConversations,
|
ContactConversations,
|
||||||
ContactDetailsItem,
|
|
||||||
ContactInfo,
|
ContactInfo,
|
||||||
ConversationInfo,
|
ConversationInfo,
|
||||||
ConversationLabels,
|
|
||||||
MultiselectDropdown,
|
|
||||||
CustomAttributes,
|
CustomAttributes,
|
||||||
CustomAttributeSelector,
|
CustomAttributeSelector,
|
||||||
|
ConversationAction,
|
||||||
|
draggable,
|
||||||
},
|
},
|
||||||
mixins: [alertMixin, agentMixin, uiSettingsMixin],
|
mixins: [alertMixin, agentMixin, uiSettingsMixin],
|
||||||
props: {
|
props: {
|
||||||
@@ -163,6 +138,13 @@ export default {
|
|||||||
default: () => {},
|
default: () => {},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
dragEnabled: true,
|
||||||
|
conversationSidebarItems: [],
|
||||||
|
dragging: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
currentChat: 'getSelectedChat',
|
currentChat: 'getSelectedChat',
|
||||||
@@ -194,61 +176,6 @@ export default {
|
|||||||
const { custom_attributes: customAttributes } = this.contact;
|
const { custom_attributes: customAttributes } = this.contact;
|
||||||
return customAttributes && Object.keys(customAttributes).length;
|
return customAttributes && Object.keys(customAttributes).length;
|
||||||
},
|
},
|
||||||
teamsList() {
|
|
||||||
if (this.assignedTeam) {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
id: 0,
|
|
||||||
name: 'None',
|
|
||||||
},
|
|
||||||
...this.teams,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
return this.teams;
|
|
||||||
},
|
|
||||||
assignedAgent: {
|
|
||||||
get() {
|
|
||||||
return this.currentChat.meta.assignee;
|
|
||||||
},
|
|
||||||
set(agent) {
|
|
||||||
const agentId = agent ? agent.id : 0;
|
|
||||||
this.$store.dispatch('setCurrentChatAssignee', agent);
|
|
||||||
this.$store
|
|
||||||
.dispatch('assignAgent', {
|
|
||||||
conversationId: this.currentChat.id,
|
|
||||||
agentId,
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
this.showAlert(this.$t('CONVERSATION.CHANGE_AGENT'));
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
assignedTeam: {
|
|
||||||
get() {
|
|
||||||
return this.currentChat.meta.team;
|
|
||||||
},
|
|
||||||
set(team) {
|
|
||||||
const teamId = team ? team.id : 0;
|
|
||||||
this.$store.dispatch('setCurrentChatTeam', team);
|
|
||||||
this.$store
|
|
||||||
.dispatch('assignTeam', {
|
|
||||||
conversationId: this.currentChat.id,
|
|
||||||
teamId,
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
this.showAlert(this.$t('CONVERSATION.CHANGE_TEAM'));
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
showSelfAssign() {
|
|
||||||
if (!this.assignedAgent) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (this.assignedAgent.id !== this.currentUser.id) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
conversationId(newConversationId, prevConversationId) {
|
conversationId(newConversationId, prevConversationId) {
|
||||||
@@ -261,6 +188,7 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
this.conversationSidebarItems = this.conversationSidebarItemsOrder;
|
||||||
this.getContactDetails();
|
this.getContactDetails();
|
||||||
this.$store.dispatch('attributes/get', 0);
|
this.$store.dispatch('attributes/get', 0);
|
||||||
},
|
},
|
||||||
@@ -281,43 +209,12 @@ export default {
|
|||||||
openTranscriptModal() {
|
openTranscriptModal() {
|
||||||
this.showTranscriptModal = true;
|
this.showTranscriptModal = true;
|
||||||
},
|
},
|
||||||
onSelfAssign() {
|
|
||||||
const {
|
|
||||||
account_id,
|
|
||||||
availability_status,
|
|
||||||
available_name,
|
|
||||||
email,
|
|
||||||
id,
|
|
||||||
name,
|
|
||||||
role,
|
|
||||||
avatar_url,
|
|
||||||
} = this.currentUser;
|
|
||||||
const selfAssign = {
|
|
||||||
account_id,
|
|
||||||
availability_status,
|
|
||||||
available_name,
|
|
||||||
email,
|
|
||||||
id,
|
|
||||||
name,
|
|
||||||
role,
|
|
||||||
thumbnail: avatar_url,
|
|
||||||
};
|
|
||||||
this.assignedAgent = selfAssign;
|
|
||||||
},
|
|
||||||
onClickAssignAgent(selectedItem) {
|
|
||||||
if (this.assignedAgent && this.assignedAgent.id === selectedItem.id) {
|
|
||||||
this.assignedAgent = null;
|
|
||||||
} else {
|
|
||||||
this.assignedAgent = selectedItem;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
onClickAssignTeam(selectedItemTeam) {
|
onDragEnd() {
|
||||||
if (this.assignedTeam && this.assignedTeam.id === selectedItemTeam.id) {
|
this.dragging = false;
|
||||||
this.assignedTeam = null;
|
this.updateUISettings({
|
||||||
} else {
|
conversation_sidebar_items_order: this.conversationSidebarItems,
|
||||||
this.assignedTeam = selectedItemTeam;
|
});
|
||||||
}
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,189 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="multiselect-wrap--small">
|
||||||
|
<contact-details-item
|
||||||
|
compact
|
||||||
|
:title="$t('CONVERSATION_SIDEBAR.ASSIGNEE_LABEL')"
|
||||||
|
>
|
||||||
|
<template v-slot:button>
|
||||||
|
<woot-button
|
||||||
|
v-if="showSelfAssign"
|
||||||
|
icon="ion-arrow-right-c"
|
||||||
|
variant="link"
|
||||||
|
size="small"
|
||||||
|
@click="onSelfAssign"
|
||||||
|
>
|
||||||
|
{{ $t('CONVERSATION_SIDEBAR.SELF_ASSIGN') }}
|
||||||
|
</woot-button>
|
||||||
|
</template>
|
||||||
|
</contact-details-item>
|
||||||
|
<multiselect-dropdown
|
||||||
|
:options="agentsList"
|
||||||
|
:selected-item="assignedAgent"
|
||||||
|
:multiselector-title="$t('AGENT_MGMT.MULTI_SELECTOR.TITLE.AGENT')"
|
||||||
|
:multiselector-placeholder="$t('AGENT_MGMT.MULTI_SELECTOR.PLACEHOLDER')"
|
||||||
|
:no-search-result="
|
||||||
|
$t('AGENT_MGMT.MULTI_SELECTOR.SEARCH.NO_RESULTS.AGENT')
|
||||||
|
"
|
||||||
|
:input-placeholder="
|
||||||
|
$t('AGENT_MGMT.MULTI_SELECTOR.SEARCH.PLACEHOLDER.AGENT')
|
||||||
|
"
|
||||||
|
@click="onClickAssignAgent"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="multiselect-wrap--small">
|
||||||
|
<contact-details-item
|
||||||
|
compact
|
||||||
|
:title="$t('CONVERSATION_SIDEBAR.TEAM_LABEL')"
|
||||||
|
/>
|
||||||
|
<multiselect-dropdown
|
||||||
|
:options="teamsList"
|
||||||
|
:selected-item="assignedTeam"
|
||||||
|
:multiselector-title="$t('AGENT_MGMT.MULTI_SELECTOR.TITLE.TEAM')"
|
||||||
|
:multiselector-placeholder="$t('AGENT_MGMT.MULTI_SELECTOR.PLACEHOLDER')"
|
||||||
|
:no-search-result="
|
||||||
|
$t('AGENT_MGMT.MULTI_SELECTOR.SEARCH.NO_RESULTS.TEAM')
|
||||||
|
"
|
||||||
|
:input-placeholder="
|
||||||
|
$t('AGENT_MGMT.MULTI_SELECTOR.SEARCH.PLACEHOLDER.TEAM')
|
||||||
|
"
|
||||||
|
@click="onClickAssignTeam"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<contact-details-item
|
||||||
|
compact
|
||||||
|
:title="$t('CONVERSATION_SIDEBAR.ACCORDION.CONVERSATION_LABELS')"
|
||||||
|
/>
|
||||||
|
<conversation-labels :conversation-id="conversationId" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapGetters } from 'vuex';
|
||||||
|
import alertMixin from 'shared/mixins/alertMixin';
|
||||||
|
import ContactDetailsItem from './ContactDetailsItem.vue';
|
||||||
|
import MultiselectDropdown from 'shared/components/ui/MultiselectDropdown.vue';
|
||||||
|
import ConversationLabels from './labels/LabelBox.vue';
|
||||||
|
import agentMixin from '../../../mixins/agentMixin';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
ContactDetailsItem,
|
||||||
|
MultiselectDropdown,
|
||||||
|
ConversationLabels,
|
||||||
|
},
|
||||||
|
mixins: [agentMixin, alertMixin],
|
||||||
|
props: {
|
||||||
|
conversationId: {
|
||||||
|
type: [Number, String],
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
inboxId: {
|
||||||
|
type: Number,
|
||||||
|
default: undefined,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters({
|
||||||
|
currentChat: 'getSelectedChat',
|
||||||
|
teams: 'teams/getTeams',
|
||||||
|
currentUser: 'getCurrentUser',
|
||||||
|
}),
|
||||||
|
teamsList() {
|
||||||
|
if (this.assignedTeam) {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
id: 0,
|
||||||
|
name: 'None',
|
||||||
|
},
|
||||||
|
...this.teams,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return this.teams;
|
||||||
|
},
|
||||||
|
assignedAgent: {
|
||||||
|
get() {
|
||||||
|
return this.currentChat.meta.assignee;
|
||||||
|
},
|
||||||
|
set(agent) {
|
||||||
|
const agentId = agent ? agent.id : 0;
|
||||||
|
this.$store.dispatch('setCurrentChatAssignee', agent);
|
||||||
|
this.$store
|
||||||
|
.dispatch('assignAgent', {
|
||||||
|
conversationId: this.currentChat.id,
|
||||||
|
agentId,
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
this.showAlert(this.$t('CONVERSATION.CHANGE_AGENT'));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
assignedTeam: {
|
||||||
|
get() {
|
||||||
|
return this.currentChat.meta.team;
|
||||||
|
},
|
||||||
|
set(team) {
|
||||||
|
const teamId = team ? team.id : 0;
|
||||||
|
this.$store.dispatch('setCurrentChatTeam', team);
|
||||||
|
this.$store
|
||||||
|
.dispatch('assignTeam', {
|
||||||
|
conversationId: this.currentChat.id,
|
||||||
|
teamId,
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
this.showAlert(this.$t('CONVERSATION.CHANGE_TEAM'));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
showSelfAssign() {
|
||||||
|
if (!this.assignedAgent) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (this.assignedAgent.id !== this.currentUser.id) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onSelfAssign() {
|
||||||
|
const {
|
||||||
|
account_id,
|
||||||
|
availability_status,
|
||||||
|
available_name,
|
||||||
|
email,
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
role,
|
||||||
|
avatar_url,
|
||||||
|
} = this.currentUser;
|
||||||
|
const selfAssign = {
|
||||||
|
account_id,
|
||||||
|
availability_status,
|
||||||
|
available_name,
|
||||||
|
email,
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
role,
|
||||||
|
thumbnail: avatar_url,
|
||||||
|
};
|
||||||
|
this.assignedAgent = selfAssign;
|
||||||
|
},
|
||||||
|
onClickAssignAgent(selectedItem) {
|
||||||
|
if (this.assignedAgent && this.assignedAgent.id === selectedItem.id) {
|
||||||
|
this.assignedAgent = null;
|
||||||
|
} else {
|
||||||
|
this.assignedAgent = selectedItem;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onClickAssignTeam(selectedItemTeam) {
|
||||||
|
if (this.assignedTeam && this.assignedTeam.id === selectedItemTeam.id) {
|
||||||
|
this.assignedTeam = null;
|
||||||
|
} else {
|
||||||
|
this.assignedTeam = selectedItemTeam;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@@ -65,6 +65,7 @@
|
|||||||
"vue-template-compiler": "2.6.12",
|
"vue-template-compiler": "2.6.12",
|
||||||
"vue-upload-component": "2.8.22",
|
"vue-upload-component": "2.8.22",
|
||||||
"vue2-datepicker": "^3.9.1",
|
"vue2-datepicker": "^3.9.1",
|
||||||
|
"vuedraggable": "^2.24.3",
|
||||||
"vuelidate": "0.7.6",
|
"vuelidate": "0.7.6",
|
||||||
"vuex": "~2.1.1",
|
"vuex": "~2.1.1",
|
||||||
"vuex-router-sync": "~4.1.2"
|
"vuex-router-sync": "~4.1.2"
|
||||||
|
|||||||
12
yarn.lock
12
yarn.lock
@@ -13540,6 +13540,11 @@ sort-keys@^1.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
is-plain-obj "^1.0.0"
|
is-plain-obj "^1.0.0"
|
||||||
|
|
||||||
|
sortablejs@1.10.2:
|
||||||
|
version "1.10.2"
|
||||||
|
resolved "https://registry.npmjs.org/sortablejs/-/sortablejs-1.10.2.tgz#6e40364d913f98b85a14f6678f92b5c1221f5290"
|
||||||
|
integrity sha512-YkPGufevysvfwn5rfdlGyrGjt7/CRHwvRPogD/lC+TnvcN29jDpCifKP+rBqf+LRldfXSTh+0CGLcSg0VIxq3A==
|
||||||
|
|
||||||
source-list-map@^2.0.0:
|
source-list-map@^2.0.0:
|
||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34"
|
resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34"
|
||||||
@@ -15112,6 +15117,13 @@ vue@2.6.12, vue@^2.6.12:
|
|||||||
resolved "https://registry.yarnpkg.com/vue/-/vue-2.6.12.tgz#f5ebd4fa6bd2869403e29a896aed4904456c9123"
|
resolved "https://registry.yarnpkg.com/vue/-/vue-2.6.12.tgz#f5ebd4fa6bd2869403e29a896aed4904456c9123"
|
||||||
integrity sha512-uhmLFETqPPNyuLLbsKz6ioJ4q7AZHzD8ZVFNATNyICSZouqP2Sz0rotWQC8UNBF6VGSCs5abnKJoStA6JbCbfg==
|
integrity sha512-uhmLFETqPPNyuLLbsKz6ioJ4q7AZHzD8ZVFNATNyICSZouqP2Sz0rotWQC8UNBF6VGSCs5abnKJoStA6JbCbfg==
|
||||||
|
|
||||||
|
vuedraggable@^2.24.3:
|
||||||
|
version "2.24.3"
|
||||||
|
resolved "https://registry.npmjs.org/vuedraggable/-/vuedraggable-2.24.3.tgz#43c93849b746a24ce503e123d5b259c701ba0d19"
|
||||||
|
integrity sha512-6/HDXi92GzB+Hcs9fC6PAAozK1RLt1ewPTLjK0anTYguXLAeySDmcnqE8IC0xa7shvSzRjQXq3/+dsZ7ETGF3g==
|
||||||
|
dependencies:
|
||||||
|
sortablejs "1.10.2"
|
||||||
|
|
||||||
vuelidate@0.7.6:
|
vuelidate@0.7.6:
|
||||||
version "0.7.6"
|
version "0.7.6"
|
||||||
resolved "https://registry.yarnpkg.com/vuelidate/-/vuelidate-0.7.6.tgz#84100c13b943470660d0416642845cd2a1edf4b2"
|
resolved "https://registry.yarnpkg.com/vuelidate/-/vuelidate-0.7.6.tgz#84100c13b943470660d0416642845cd2a1edf4b2"
|
||||||
|
|||||||
Reference in New Issue
Block a user