chore: Update contact sidebar with accordion (#3002)

This commit is contained in:
Pranav Raj S
2021-09-13 18:08:58 +05:30
committed by GitHub
parent 328edd24de
commit f2e2a0b7ed
9 changed files with 147 additions and 129 deletions

View File

@@ -1,33 +1,27 @@
<template> <template>
<div> <div v-on-clickaway="closeDropdownLabel" class="label-wrap">
<h6 class="text-block-title"> <add-label @add="toggleLabels" />
<i class="title-icon ion-pricetags" /> <woot-label
{{ $t('CONTACT_PANEL.LABELS.CONTACT.TITLE') }} v-for="label in savedLabels"
</h6> :key="label.id"
<div v-on-clickaway="closeDropdownLabel" class="label-wrap"> :title="label.title"
<add-label @add="toggleLabels" /> :description="label.description"
<woot-label :show-close="true"
v-for="label in savedLabels" :bg-color="label.color"
:key="label.id" @click="removeItem"
:title="label.title" />
:description="label.description" <div class="dropdown-wrap">
:show-close="true" <div
:bg-color="label.color" :class="{ 'dropdown-pane--open': showSearchDropdownLabel }"
@click="removeItem" class="dropdown-pane"
/> >
<div class="dropdown-wrap"> <label-dropdown
<div v-if="showSearchDropdownLabel"
:class="{ 'dropdown-pane--open': showSearchDropdownLabel }" :account-labels="allLabels"
class="dropdown-pane" :selected-labels="selectedLabels"
> @add="addItem"
<label-dropdown @remove="removeItem"
v-if="showSearchDropdownLabel" />
:account-labels="allLabels"
:selected-labels="selectedLabels"
@add="addItem"
@remove="removeItem"
/>
</div>
</div> </div>
</div> </div>
</div> </div>
@@ -96,7 +90,6 @@ export default {
.label-wrap { .label-wrap {
position: relative; position: relative;
margin-left: var(--space-two);
line-height: var(--space-medium); line-height: var(--space-medium);
.dropdown-wrap { .dropdown-wrap {

View File

@@ -37,7 +37,12 @@
"MUTED_SUCCESS": "This conversation is muted for 6 hours", "MUTED_SUCCESS": "This conversation is muted for 6 hours",
"UNMUTED_SUCCESS": "This conversation is unmuted", "UNMUTED_SUCCESS": "This conversation is unmuted",
"SEND_TRANSCRIPT": "Send Transcript", "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": { "EDIT_CONTACT": {
"BUTTON_LABEL": "Edit Contact", "BUTTON_LABEL": "Edit Contact",
@@ -186,8 +191,8 @@
} }
}, },
"CUSTOM_ATTRIBUTES": { "CUSTOM_ATTRIBUTES": {
"TITLE": "Custom Attributes",
"BUTTON": "Add custom attribute", "BUTTON": "Add custom attribute",
"NOT_AVAILABLE": "There are no custom attributes available for this contact.",
"ADD": { "ADD": {
"TITLE": "Create custom attribute", "TITLE": "Create custom attribute",
"DESC": "Add custom information to this contact." "DESC": "Add custom information to this contact."

View File

@@ -11,11 +11,12 @@ describe('uiSettingsMixin', () => {
let store; let store;
beforeEach(() => { beforeEach(() => {
actions = { updateUISettings: jest.fn() }; actions = { updateUISettings: jest.fn(), toggleSidebarUIState: jest.fn() };
getters = { getters = {
getUISettings: () => ({ getUISettings: () => ({
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,
}), }),
}; };
store = new Vuex.Store({ actions, getters }); store = new Vuex.Store({ actions, getters });
@@ -31,26 +32,70 @@ describe('uiSettingsMixin', () => {
expect(wrapper.vm.uiSettings).toEqual({ expect(wrapper.vm.uiSettings).toEqual({
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,
}); });
}); });
it('dispatches store actions correctly', () => { describe('#updateUISettings', () => {
const Component = { it('dispatches store actions correctly', () => {
render() {}, const Component = {
title: 'TestComponent', render() {},
mixins: [uiSettingsMixin], title: 'TestComponent',
}; mixins: [uiSettingsMixin],
const wrapper = shallowMount(Component, { store, localVue }); };
wrapper.vm.updateUISettings({ enter_to_send_enabled: true }); const wrapper = shallowMount(Component, { store, localVue });
expect(actions.updateUISettings).toHaveBeenCalledWith( wrapper.vm.updateUISettings({ enter_to_send_enabled: true });
expect.anything(), expect(actions.updateUISettings).toHaveBeenCalledWith(
{ expect.anything(),
uiSettings: { {
display_rich_content_editor: false, uiSettings: {
enter_to_send_enabled: true, 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);
});
}); });
}); });

View File

@@ -15,5 +15,12 @@ export default {
}, },
}); });
}, },
isContactSidebarItemOpen(key) {
const { [key]: isOpen } = this.uiSettings;
return !!isOpen;
},
toggleSidebarUIState(key) {
this.updateUISettings({ [key]: !this.isContactSidebarItemOpen(key) });
},
}, },
}; };

View File

@@ -4,32 +4,54 @@
<i class="ion-android-close close-icon" /> <i class="ion-android-close close-icon" />
</span> </span>
<contact-info show-new-message :contact="contact" /> <contact-info show-new-message :contact="contact" />
<contact-custom-attributes <accordion-item
v-if="hasContactAttributes" :title="$t('CONTACT_PANEL.SIDEBAR_SECTIONS.CUSTOM_ATTRIBUTES')"
:custom-attributes="contact.custom_attributes" :is-open="isContactSidebarItemOpen('is_ct_custom_attr_open')"
/> @click="value => toggleSidebarUIState('is_ct_custom_attr_open', value)"
<contact-label :contact-id="contact.id" class="contact-labels" /> >
<contact-conversations <contact-custom-attributes
v-if="contact.id" :custom-attributes="contact.custom_attributes"
:contact-id="contact.id" />
conversation-id="" </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> </div>
</template> </template>
<script> <script>
import AccordionItem from 'dashboard/components/Accordion/AccordionItem';
import ContactConversations from 'dashboard/routes/dashboard/conversation/ContactConversations'; 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 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 ContactLabel from 'dashboard/routes/dashboard/contacts/components/ContactLabels.vue';
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
export default { export default {
components: { components: {
ContactCustomAttributes, AccordionItem,
ContactConversations, ContactConversations,
ContactCustomAttributes,
ContactInfo, ContactInfo,
ContactLabel, ContactLabel,
}, },
mixins: [uiSettingsMixin],
props: { props: {
contact: { contact: {
type: Object, type: Object,
@@ -64,11 +86,6 @@ export default {
overflow: auto; overflow: auto;
position: relative; position: relative;
border-left: 1px solid var(--color-border); border-left: 1px solid var(--color-border);
padding: var(--space-medium) var(--space-two);
.contact-labels {
padding-bottom: var(--space-normal);
}
} }
.close-button { .close-button {

View File

@@ -1,11 +1,5 @@
<template> <template>
<div class="contact-conversation--panel"> <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="!uiFlags.isFetching" class="contact-conversation__wrap">
<div v-if="!previousConversations.length" class="no-label-message"> <div v-if="!previousConversations.length" class="no-label-message">
<span> <span>
@@ -28,22 +22,16 @@
</template> </template>
<script> <script>
import ConversationCard from 'dashboard/components/widgets/conversation/ConversationCard.vue'; import ConversationCard from 'dashboard/components/widgets/conversation/ConversationCard';
import { mapGetters } from 'vuex'; import { mapGetters } from 'vuex';
import Spinner from 'shared/components/Spinner.vue'; import Spinner from 'shared/components/Spinner';
import ContactDetailsItem from './ContactDetailsItem.vue';
export default { export default {
components: { components: {
ConversationCard, ConversationCard,
ContactDetailsItem,
Spinner, Spinner,
}, },
props: { props: {
showTitle: {
type: Boolean,
default: true,
},
contactId: { contactId: {
type: [String, Number], type: [String, Number],
required: true, required: true,

View File

@@ -1,11 +1,5 @@
<template> <template>
<div class="custom-attributes--panel"> <div class="custom-attributes--panel">
<contact-details-item
v-if="showTitle"
:title="$t('CUSTOM_ATTRIBUTES.TITLE')"
icon="ion-code"
emoji="📕"
/>
<div <div
v-for="attribute in listOfAttributes" v-for="attribute in listOfAttributes"
:key="attribute" :key="attribute"
@@ -18,23 +12,17 @@
<span v-html="valueWithLink(customAttributes[attribute])"></span> <span v-html="valueWithLink(customAttributes[attribute])"></span>
</div> </div>
</div> </div>
<p v-if="!listOfAttributes.length">
{{ $t('CUSTOM_ATTRIBUTES.NOT_AVAILABLE') }}
</p>
</div> </div>
</template> </template>
<script> <script>
import ContactDetailsItem from './ContactDetailsItem.vue';
import MessageFormatter from 'shared/helpers/MessageFormatter.js'; import MessageFormatter from 'shared/helpers/MessageFormatter.js';
export default { export default {
components: {
ContactDetailsItem,
},
props: { props: {
showTitle: {
type: Boolean,
default: true,
},
customAttributes: { customAttributes: {
type: Object, type: Object,
default: () => ({}), default: () => ({}),

View File

@@ -8,7 +8,7 @@
<accordion-item <accordion-item
:title="$t('CONVERSATION_SIDEBAR.ACCORDION.CONVERSATION_ACTIONS')" :title="$t('CONVERSATION_SIDEBAR.ACCORDION.CONVERSATION_ACTIONS')"
:is-open="isContactSidebarItemOpen('is_conv_actions_open')" :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>
<div class="multiselect-wrap--small"> <div class="multiselect-wrap--small">
@@ -66,10 +66,7 @@
<contact-details-item <contact-details-item
:title="$t('CONVERSATION_SIDEBAR.ACCORDION.CONVERSATION_LABELS')" :title="$t('CONVERSATION_SIDEBAR.ACCORDION.CONVERSATION_LABELS')"
/> />
<conversation-labels <conversation-labels :conversation-id="conversationId" />
:show-title="false"
:conversation-id="conversationId"
/>
</div> </div>
</accordion-item> </accordion-item>
</div> </div>
@@ -78,7 +75,7 @@
v-if="browser.browser_name" v-if="browser.browser_name"
:title="$t('CONVERSATION_SIDEBAR.ACCORDION.CONVERSATION_INFO')" :title="$t('CONVERSATION_SIDEBAR.ACCORDION.CONVERSATION_INFO')"
:is-open="isContactSidebarItemOpen('is_conv_details_open')" :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"> <div class="conversation--details">
<contact-details-item <contact-details-item
@@ -133,10 +130,11 @@
v-if="hasContactAttributes" v-if="hasContactAttributes"
:title="$t('CONVERSATION_SIDEBAR.ACCORDION.CONTACT_ATTRIBUTES')" :title="$t('CONVERSATION_SIDEBAR.ACCORDION.CONTACT_ATTRIBUTES')"
:is-open="isContactSidebarItemOpen('is_contact_attributes_open')" :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 <contact-custom-attributes
:show-title="false"
:custom-attributes="contact.custom_attributes" :custom-attributes="contact.custom_attributes"
/> />
</accordion-item> </accordion-item>
@@ -144,10 +142,9 @@
v-if="contact.id" v-if="contact.id"
:title="$t('CONVERSATION_SIDEBAR.ACCORDION.PREVIOUS_CONVERSATION')" :title="$t('CONVERSATION_SIDEBAR.ACCORDION.PREVIOUS_CONVERSATION')"
:is-open="isContactSidebarItemOpen('is_previous_conv_open')" :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 <contact-conversations
:show-title="false"
:contact-id="contact.id" :contact-id="contact.id"
:conversation-id="conversationId" :conversation-id="conversationId"
/> />
@@ -335,16 +332,6 @@ export default {
this.getContactDetails(); this.getContactDetails();
}, },
methods: { 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() { onPanelToggle() {
this.onToggle(); this.onToggle();
}, },

View File

@@ -4,12 +4,6 @@
v-if="!conversationUiFlags.isFetching" v-if="!conversationUiFlags.isFetching"
class="contact-conversation--list" class="contact-conversation--list"
> >
<contact-details-item
v-if="showTitle"
:title="$t('CONTACT_PANEL.LABELS.CONVERSATION.TITLE')"
icon="ion-pricetags"
emoji="🏷️"
/>
<div <div
v-on-clickaway="closeDropdownLabel" v-on-clickaway="closeDropdownLabel"
class="label-wrap" class="label-wrap"
@@ -48,7 +42,6 @@
<script> <script>
import { mapGetters } from 'vuex'; import { mapGetters } from 'vuex';
import ContactDetailsItem from '../ContactDetailsItem';
import Spinner from 'shared/components/Spinner'; import Spinner from 'shared/components/Spinner';
import LabelDropdown from 'shared/components/ui/label/LabelDropdown'; import LabelDropdown from 'shared/components/ui/label/LabelDropdown';
import AddLabel from 'shared/components/ui/dropdown/AddLabel'; import AddLabel from 'shared/components/ui/dropdown/AddLabel';
@@ -56,7 +49,6 @@ import { mixin as clickaway } from 'vue-clickaway';
export default { export default {
components: { components: {
ContactDetailsItem,
Spinner, Spinner,
LabelDropdown, LabelDropdown,
AddLabel, AddLabel,
@@ -64,10 +56,6 @@ export default {
mixins: [clickaway], mixins: [clickaway],
props: { props: {
showTitle: {
type: Boolean,
default: true,
},
conversationId: { conversationId: {
type: Number, type: Number,
required: true, required: true,
@@ -89,7 +77,7 @@ export default {
}, },
...mapGetters({ ...mapGetters({
conversationUiFlags: 'contactConversations/getUIFlags', conversationUiFlags: 'conversationLabels/getUIFlags',
labelUiFlags: 'conversationLabels/getUIFlags', labelUiFlags: 'conversationLabels/getUIFlags',
accountLabels: 'labels/getLabels', accountLabels: 'labels/getLabels',
}), }),