chore: Update contact sidebar with accordion (#3002)
This commit is contained in:
parent
328edd24de
commit
f2e2a0b7ed
9 changed files with 147 additions and 129 deletions
|
@ -1,33 +1,27 @@
|
|||
<template>
|
||||
<div>
|
||||
<h6 class="text-block-title">
|
||||
<i class="title-icon ion-pricetags" />
|
||||
{{ $t('CONTACT_PANEL.LABELS.CONTACT.TITLE') }}
|
||||
</h6>
|
||||
<div v-on-clickaway="closeDropdownLabel" class="label-wrap">
|
||||
<add-label @add="toggleLabels" />
|
||||
<woot-label
|
||||
v-for="label in savedLabels"
|
||||
:key="label.id"
|
||||
:title="label.title"
|
||||
:description="label.description"
|
||||
:show-close="true"
|
||||
:bg-color="label.color"
|
||||
@click="removeItem"
|
||||
/>
|
||||
<div class="dropdown-wrap">
|
||||
<div
|
||||
:class="{ 'dropdown-pane--open': showSearchDropdownLabel }"
|
||||
class="dropdown-pane"
|
||||
>
|
||||
<label-dropdown
|
||||
v-if="showSearchDropdownLabel"
|
||||
:account-labels="allLabels"
|
||||
:selected-labels="selectedLabels"
|
||||
@add="addItem"
|
||||
@remove="removeItem"
|
||||
/>
|
||||
</div>
|
||||
<div v-on-clickaway="closeDropdownLabel" class="label-wrap">
|
||||
<add-label @add="toggleLabels" />
|
||||
<woot-label
|
||||
v-for="label in savedLabels"
|
||||
:key="label.id"
|
||||
:title="label.title"
|
||||
:description="label.description"
|
||||
:show-close="true"
|
||||
:bg-color="label.color"
|
||||
@click="removeItem"
|
||||
/>
|
||||
<div class="dropdown-wrap">
|
||||
<div
|
||||
:class="{ 'dropdown-pane--open': showSearchDropdownLabel }"
|
||||
class="dropdown-pane"
|
||||
>
|
||||
<label-dropdown
|
||||
v-if="showSearchDropdownLabel"
|
||||
:account-labels="allLabels"
|
||||
:selected-labels="selectedLabels"
|
||||
@add="addItem"
|
||||
@remove="removeItem"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -96,7 +90,6 @@ export default {
|
|||
|
||||
.label-wrap {
|
||||
position: relative;
|
||||
margin-left: var(--space-two);
|
||||
line-height: var(--space-medium);
|
||||
|
||||
.dropdown-wrap {
|
||||
|
|
|
@ -37,7 +37,12 @@
|
|||
"MUTED_SUCCESS": "This conversation is muted for 6 hours",
|
||||
"UNMUTED_SUCCESS": "This conversation is unmuted",
|
||||
"SEND_TRANSCRIPT": "Send Transcript",
|
||||
"EDIT_LABEL": "Edit"
|
||||
"EDIT_LABEL": "Edit",
|
||||
"SIDEBAR_SECTIONS": {
|
||||
"CUSTOM_ATTRIBUTES": "Custom Attributes",
|
||||
"CONTACT_LABELS": "Contact Labels",
|
||||
"PREVIOUS_CONVERSATIONS": "Previous Conversations"
|
||||
}
|
||||
},
|
||||
"EDIT_CONTACT": {
|
||||
"BUTTON_LABEL": "Edit Contact",
|
||||
|
@ -186,8 +191,8 @@
|
|||
}
|
||||
},
|
||||
"CUSTOM_ATTRIBUTES": {
|
||||
"TITLE": "Custom Attributes",
|
||||
"BUTTON": "Add custom attribute",
|
||||
"NOT_AVAILABLE": "There are no custom attributes available for this contact.",
|
||||
"ADD": {
|
||||
"TITLE": "Create custom attribute",
|
||||
"DESC": "Add custom information to this contact."
|
||||
|
|
|
@ -11,11 +11,12 @@ describe('uiSettingsMixin', () => {
|
|||
let store;
|
||||
|
||||
beforeEach(() => {
|
||||
actions = { updateUISettings: jest.fn() };
|
||||
actions = { updateUISettings: jest.fn(), toggleSidebarUIState: jest.fn() };
|
||||
getters = {
|
||||
getUISettings: () => ({
|
||||
display_rich_content_editor: false,
|
||||
enter_to_send_enabled: false,
|
||||
is_ct_labels_open: true,
|
||||
}),
|
||||
};
|
||||
store = new Vuex.Store({ actions, getters });
|
||||
|
@ -31,26 +32,70 @@ describe('uiSettingsMixin', () => {
|
|||
expect(wrapper.vm.uiSettings).toEqual({
|
||||
display_rich_content_editor: false,
|
||||
enter_to_send_enabled: false,
|
||||
is_ct_labels_open: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('dispatches store actions correctly', () => {
|
||||
const Component = {
|
||||
render() {},
|
||||
title: 'TestComponent',
|
||||
mixins: [uiSettingsMixin],
|
||||
};
|
||||
const wrapper = shallowMount(Component, { store, localVue });
|
||||
wrapper.vm.updateUISettings({ enter_to_send_enabled: true });
|
||||
expect(actions.updateUISettings).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
{
|
||||
uiSettings: {
|
||||
display_rich_content_editor: false,
|
||||
enter_to_send_enabled: true,
|
||||
describe('#updateUISettings', () => {
|
||||
it('dispatches store actions correctly', () => {
|
||||
const Component = {
|
||||
render() {},
|
||||
title: 'TestComponent',
|
||||
mixins: [uiSettingsMixin],
|
||||
};
|
||||
const wrapper = shallowMount(Component, { store, localVue });
|
||||
wrapper.vm.updateUISettings({ enter_to_send_enabled: true });
|
||||
expect(actions.updateUISettings).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
{
|
||||
uiSettings: {
|
||||
display_rich_content_editor: false,
|
||||
enter_to_send_enabled: true,
|
||||
is_ct_labels_open: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
undefined
|
||||
);
|
||||
undefined
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#toggleSidebarUIState', () => {
|
||||
it('dispatches store actions correctly', () => {
|
||||
const Component = {
|
||||
render() {},
|
||||
title: 'TestComponent',
|
||||
mixins: [uiSettingsMixin],
|
||||
};
|
||||
const wrapper = shallowMount(Component, { store, localVue });
|
||||
wrapper.vm.toggleSidebarUIState('is_ct_labels_open');
|
||||
expect(actions.updateUISettings).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
{
|
||||
uiSettings: {
|
||||
display_rich_content_editor: false,
|
||||
enter_to_send_enabled: false,
|
||||
is_ct_labels_open: false,
|
||||
},
|
||||
},
|
||||
undefined
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#isContactSidebarItemOpen', () => {
|
||||
it('returns correct values', () => {
|
||||
const Component = {
|
||||
render() {},
|
||||
title: 'TestComponent',
|
||||
mixins: [uiSettingsMixin],
|
||||
};
|
||||
const wrapper = shallowMount(Component, { store, localVue });
|
||||
expect(wrapper.vm.isContactSidebarItemOpen('is_ct_labels_open')).toEqual(
|
||||
true
|
||||
);
|
||||
expect(
|
||||
wrapper.vm.isContactSidebarItemOpen('is_ct_prev_conv_open')
|
||||
).toEqual(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -15,5 +15,12 @@ export default {
|
|||
},
|
||||
});
|
||||
},
|
||||
isContactSidebarItemOpen(key) {
|
||||
const { [key]: isOpen } = this.uiSettings;
|
||||
return !!isOpen;
|
||||
},
|
||||
toggleSidebarUIState(key) {
|
||||
this.updateUISettings({ [key]: !this.isContactSidebarItemOpen(key) });
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -4,32 +4,54 @@
|
|||
<i class="ion-android-close close-icon" />
|
||||
</span>
|
||||
<contact-info show-new-message :contact="contact" />
|
||||
<contact-custom-attributes
|
||||
v-if="hasContactAttributes"
|
||||
:custom-attributes="contact.custom_attributes"
|
||||
/>
|
||||
<contact-label :contact-id="contact.id" class="contact-labels" />
|
||||
<contact-conversations
|
||||
v-if="contact.id"
|
||||
:contact-id="contact.id"
|
||||
conversation-id=""
|
||||
/>
|
||||
<accordion-item
|
||||
:title="$t('CONTACT_PANEL.SIDEBAR_SECTIONS.CUSTOM_ATTRIBUTES')"
|
||||
:is-open="isContactSidebarItemOpen('is_ct_custom_attr_open')"
|
||||
@click="value => toggleSidebarUIState('is_ct_custom_attr_open', value)"
|
||||
>
|
||||
<contact-custom-attributes
|
||||
:custom-attributes="contact.custom_attributes"
|
||||
/>
|
||||
</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>
|
||||
<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>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AccordionItem from 'dashboard/components/Accordion/AccordionItem';
|
||||
import ContactConversations from 'dashboard/routes/dashboard/conversation/ContactConversations';
|
||||
import ContactInfo from 'dashboard/routes/dashboard/conversation/contact/ContactInfo';
|
||||
import ContactCustomAttributes from 'dashboard/routes/dashboard/conversation/ContactCustomAttributes';
|
||||
import ContactInfo from 'dashboard/routes/dashboard/conversation/contact/ContactInfo';
|
||||
import ContactLabel from 'dashboard/routes/dashboard/contacts/components/ContactLabels.vue';
|
||||
|
||||
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ContactCustomAttributes,
|
||||
AccordionItem,
|
||||
ContactConversations,
|
||||
ContactCustomAttributes,
|
||||
ContactInfo,
|
||||
ContactLabel,
|
||||
},
|
||||
mixins: [uiSettingsMixin],
|
||||
props: {
|
||||
contact: {
|
||||
type: Object,
|
||||
|
@ -64,11 +86,6 @@ export default {
|
|||
overflow: auto;
|
||||
position: relative;
|
||||
border-left: 1px solid var(--color-border);
|
||||
padding: var(--space-medium) var(--space-two);
|
||||
|
||||
.contact-labels {
|
||||
padding-bottom: var(--space-normal);
|
||||
}
|
||||
}
|
||||
|
||||
.close-button {
|
||||
|
|
|
@ -1,11 +1,5 @@
|
|||
<template>
|
||||
<div class="contact-conversation--panel">
|
||||
<contact-details-item
|
||||
v-if="showTitle"
|
||||
:title="$t('CONTACT_PANEL.CONVERSATIONS.TITLE')"
|
||||
icon="ion-chatboxes"
|
||||
emoji="💬"
|
||||
/>
|
||||
<div v-if="!uiFlags.isFetching" class="contact-conversation__wrap">
|
||||
<div v-if="!previousConversations.length" class="no-label-message">
|
||||
<span>
|
||||
|
@ -28,22 +22,16 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import ConversationCard from 'dashboard/components/widgets/conversation/ConversationCard.vue';
|
||||
import ConversationCard from 'dashboard/components/widgets/conversation/ConversationCard';
|
||||
import { mapGetters } from 'vuex';
|
||||
import Spinner from 'shared/components/Spinner.vue';
|
||||
import ContactDetailsItem from './ContactDetailsItem.vue';
|
||||
import Spinner from 'shared/components/Spinner';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ConversationCard,
|
||||
ContactDetailsItem,
|
||||
Spinner,
|
||||
},
|
||||
props: {
|
||||
showTitle: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
contactId: {
|
||||
type: [String, Number],
|
||||
required: true,
|
||||
|
|
|
@ -1,11 +1,5 @@
|
|||
<template>
|
||||
<div class="custom-attributes--panel">
|
||||
<contact-details-item
|
||||
v-if="showTitle"
|
||||
:title="$t('CUSTOM_ATTRIBUTES.TITLE')"
|
||||
icon="ion-code"
|
||||
emoji="📕"
|
||||
/>
|
||||
<div
|
||||
v-for="attribute in listOfAttributes"
|
||||
:key="attribute"
|
||||
|
@ -18,23 +12,17 @@
|
|||
<span v-html="valueWithLink(customAttributes[attribute])"></span>
|
||||
</div>
|
||||
</div>
|
||||
<p v-if="!listOfAttributes.length">
|
||||
{{ $t('CUSTOM_ATTRIBUTES.NOT_AVAILABLE') }}
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ContactDetailsItem from './ContactDetailsItem.vue';
|
||||
import MessageFormatter from 'shared/helpers/MessageFormatter.js';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ContactDetailsItem,
|
||||
},
|
||||
|
||||
props: {
|
||||
showTitle: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
customAttributes: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
<accordion-item
|
||||
:title="$t('CONVERSATION_SIDEBAR.ACCORDION.CONVERSATION_ACTIONS')"
|
||||
:is-open="isContactSidebarItemOpen('is_conv_actions_open')"
|
||||
@click="value => onContactItemClick('is_conv_actions_open', value)"
|
||||
@click="value => toggleSidebarUIState('is_conv_actions_open', value)"
|
||||
>
|
||||
<div>
|
||||
<div class="multiselect-wrap--small">
|
||||
|
@ -66,10 +66,7 @@
|
|||
<contact-details-item
|
||||
:title="$t('CONVERSATION_SIDEBAR.ACCORDION.CONVERSATION_LABELS')"
|
||||
/>
|
||||
<conversation-labels
|
||||
:show-title="false"
|
||||
:conversation-id="conversationId"
|
||||
/>
|
||||
<conversation-labels :conversation-id="conversationId" />
|
||||
</div>
|
||||
</accordion-item>
|
||||
</div>
|
||||
|
@ -78,7 +75,7 @@
|
|||
v-if="browser.browser_name"
|
||||
:title="$t('CONVERSATION_SIDEBAR.ACCORDION.CONVERSATION_INFO')"
|
||||
:is-open="isContactSidebarItemOpen('is_conv_details_open')"
|
||||
@click="value => onContactItemClick('is_conv_details_open', value)"
|
||||
@click="value => toggleSidebarUIState('is_conv_details_open', value)"
|
||||
>
|
||||
<div class="conversation--details">
|
||||
<contact-details-item
|
||||
|
@ -133,10 +130,11 @@
|
|||
v-if="hasContactAttributes"
|
||||
:title="$t('CONVERSATION_SIDEBAR.ACCORDION.CONTACT_ATTRIBUTES')"
|
||||
:is-open="isContactSidebarItemOpen('is_contact_attributes_open')"
|
||||
@click="value => onContactItemClick('is_contact_attributes_open', value)"
|
||||
@click="
|
||||
value => toggleSidebarUIState('is_contact_attributes_open', value)
|
||||
"
|
||||
>
|
||||
<contact-custom-attributes
|
||||
:show-title="false"
|
||||
:custom-attributes="contact.custom_attributes"
|
||||
/>
|
||||
</accordion-item>
|
||||
|
@ -144,10 +142,9 @@
|
|||
v-if="contact.id"
|
||||
:title="$t('CONVERSATION_SIDEBAR.ACCORDION.PREVIOUS_CONVERSATION')"
|
||||
:is-open="isContactSidebarItemOpen('is_previous_conv_open')"
|
||||
@click="value => onContactItemClick('is_previous_conv_open', value)"
|
||||
@click="value => toggleSidebarUIState('is_previous_conv_open', value)"
|
||||
>
|
||||
<contact-conversations
|
||||
:show-title="false"
|
||||
:contact-id="contact.id"
|
||||
:conversation-id="conversationId"
|
||||
/>
|
||||
|
@ -335,16 +332,6 @@ export default {
|
|||
this.getContactDetails();
|
||||
},
|
||||
methods: {
|
||||
onContactItemClick(key) {
|
||||
this.updateUISettings({ [key]: !this.isContactSidebarItemOpen(key) });
|
||||
},
|
||||
isContactSidebarItemOpen(key) {
|
||||
if (this.currentChat.id) {
|
||||
const { [key]: isOpen } = this.uiSettings;
|
||||
return isOpen;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
onPanelToggle() {
|
||||
this.onToggle();
|
||||
},
|
||||
|
|
|
@ -4,12 +4,6 @@
|
|||
v-if="!conversationUiFlags.isFetching"
|
||||
class="contact-conversation--list"
|
||||
>
|
||||
<contact-details-item
|
||||
v-if="showTitle"
|
||||
:title="$t('CONTACT_PANEL.LABELS.CONVERSATION.TITLE')"
|
||||
icon="ion-pricetags"
|
||||
emoji="🏷️"
|
||||
/>
|
||||
<div
|
||||
v-on-clickaway="closeDropdownLabel"
|
||||
class="label-wrap"
|
||||
|
@ -48,7 +42,6 @@
|
|||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import ContactDetailsItem from '../ContactDetailsItem';
|
||||
import Spinner from 'shared/components/Spinner';
|
||||
import LabelDropdown from 'shared/components/ui/label/LabelDropdown';
|
||||
import AddLabel from 'shared/components/ui/dropdown/AddLabel';
|
||||
|
@ -56,7 +49,6 @@ import { mixin as clickaway } from 'vue-clickaway';
|
|||
|
||||
export default {
|
||||
components: {
|
||||
ContactDetailsItem,
|
||||
Spinner,
|
||||
LabelDropdown,
|
||||
AddLabel,
|
||||
|
@ -64,10 +56,6 @@ export default {
|
|||
|
||||
mixins: [clickaway],
|
||||
props: {
|
||||
showTitle: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
conversationId: {
|
||||
type: Number,
|
||||
required: true,
|
||||
|
@ -89,7 +77,7 @@ export default {
|
|||
},
|
||||
|
||||
...mapGetters({
|
||||
conversationUiFlags: 'contactConversations/getUIFlags',
|
||||
conversationUiFlags: 'conversationLabels/getUIFlags',
|
||||
labelUiFlags: 'conversationLabels/getUIFlags',
|
||||
accountLabels: 'labels/getLabels',
|
||||
}),
|
||||
|
|
Loading…
Reference in a new issue