feat: Render contact custom attributes in contact/conversation sidebar (#3310)
This commit is contained in:
@@ -13,12 +13,21 @@
|
||||
@panel-close="onClose"
|
||||
/>
|
||||
<accordion-item
|
||||
:title="$t('CONTACT_PANEL.SIDEBAR_SECTIONS.CUSTOM_ATTRIBUTES')"
|
||||
:title="$t('CONVERSATION_SIDEBAR.ACCORDION.CONTACT_ATTRIBUTES')"
|
||||
:is-open="isContactSidebarItemOpen('is_ct_custom_attr_open')"
|
||||
compact
|
||||
@click="value => toggleSidebarUIState('is_ct_custom_attr_open', value)"
|
||||
>
|
||||
<contact-custom-attributes
|
||||
<custom-attributes
|
||||
:contact-id="contact.id"
|
||||
attribute-type="contact_attribute"
|
||||
attribute-class="conversation--attribute"
|
||||
:custom-attributes="contact.custom_attributes"
|
||||
class="even"
|
||||
/>
|
||||
<custom-attribute-selector
|
||||
attribute-type="contact_attribute"
|
||||
:contact-id="contact.id"
|
||||
/>
|
||||
</accordion-item>
|
||||
<accordion-item
|
||||
@@ -45,9 +54,10 @@
|
||||
<script>
|
||||
import AccordionItem from 'dashboard/components/Accordion/AccordionItem';
|
||||
import ContactConversations from 'dashboard/routes/dashboard/conversation/ContactConversations';
|
||||
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 CustomAttributes from 'dashboard/routes/dashboard/conversation/customAttributes/CustomAttributes.vue';
|
||||
import CustomAttributeSelector from 'dashboard/routes/dashboard/conversation/customAttributes/CustomAttributeSelector.vue';
|
||||
|
||||
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
||||
|
||||
@@ -55,9 +65,10 @@ export default {
|
||||
components: {
|
||||
AccordionItem,
|
||||
ContactConversations,
|
||||
ContactCustomAttributes,
|
||||
ContactInfo,
|
||||
ContactLabel,
|
||||
CustomAttributes,
|
||||
CustomAttributeSelector,
|
||||
},
|
||||
mixins: [uiSettingsMixin],
|
||||
props: {
|
||||
|
||||
@@ -85,19 +85,26 @@
|
||||
>
|
||||
</conversation-info>
|
||||
</accordion-item>
|
||||
|
||||
<accordion-item
|
||||
v-if="hasContactAttributes"
|
||||
:title="$t('CONVERSATION_SIDEBAR.ACCORDION.CONTACT_ATTRIBUTES')"
|
||||
:is-open="isContactSidebarItemOpen('is_contact_attributes_open')"
|
||||
compact
|
||||
@click="
|
||||
value => toggleSidebarUIState('is_contact_attributes_open', value)
|
||||
"
|
||||
>
|
||||
<contact-custom-attributes
|
||||
:custom-attributes="contact.custom_attributes"
|
||||
<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')"
|
||||
@@ -119,24 +126,27 @@ import agentMixin from '../../../mixins/agentMixin';
|
||||
|
||||
import AccordionItem from 'dashboard/components/Accordion/AccordionItem';
|
||||
import ContactConversations from './ContactConversations.vue';
|
||||
import ContactCustomAttributes from './ContactCustomAttributes';
|
||||
import ContactDetailsItem from './ContactDetailsItem.vue';
|
||||
import ContactInfo from './contact/ContactInfo';
|
||||
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 CustomAttributeSelector from './customAttributes/CustomAttributeSelector.vue';
|
||||
|
||||
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
AccordionItem,
|
||||
ContactConversations,
|
||||
ContactCustomAttributes,
|
||||
ContactDetailsItem,
|
||||
ContactInfo,
|
||||
ConversationInfo,
|
||||
ConversationLabels,
|
||||
MultiselectDropdown,
|
||||
CustomAttributes,
|
||||
CustomAttributeSelector,
|
||||
},
|
||||
mixins: [alertMixin, agentMixin, uiSettingsMixin],
|
||||
props: {
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
{{ $t('CUSTOM_ATTRIBUTES.FORM.ATTRIBUTE_SELECT.NO_RESULT') }}
|
||||
</div>
|
||||
<woot-button
|
||||
variant="hollow"
|
||||
class="add"
|
||||
icon="ion-plus-round"
|
||||
size="tiny"
|
||||
@@ -53,6 +52,7 @@ export default {
|
||||
type: String,
|
||||
default: 'conversation_attribute',
|
||||
},
|
||||
contactId: { type: Number, default: null },
|
||||
},
|
||||
|
||||
data() {
|
||||
@@ -76,7 +76,7 @@ export default {
|
||||
},
|
||||
|
||||
noResult() {
|
||||
return this.filteredAttributes.length === 0 && this.search !== '';
|
||||
return this.filteredAttributes.length === 0;
|
||||
},
|
||||
},
|
||||
|
||||
@@ -90,7 +90,7 @@ export default {
|
||||
},
|
||||
addNewAttribute() {
|
||||
this.$router.push(
|
||||
`/app/accounts/${this.accountId}/settings/attributes/list`
|
||||
`/app/accounts/${this.accountId}/settings/custom-attributes/list`
|
||||
);
|
||||
},
|
||||
async onAddAttribute(attribute) {
|
||||
@@ -138,7 +138,6 @@ export default {
|
||||
width: 100%;
|
||||
.add {
|
||||
float: right;
|
||||
margin-top: var(--space-one);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
<custom-attribute-drop-down
|
||||
v-if="showAttributeDropDown"
|
||||
:attribute-type="attributeType"
|
||||
:contact-id="contactId"
|
||||
@add-attribute="addAttribute"
|
||||
/>
|
||||
</div>
|
||||
@@ -47,6 +48,7 @@ export default {
|
||||
type: String,
|
||||
default: 'conversation_attribute',
|
||||
},
|
||||
contactId: { type: Number, default: null },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -55,15 +57,25 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
async addAttribute(attribute) {
|
||||
const { attribute_key } = attribute;
|
||||
try {
|
||||
await this.$store.dispatch('updateCustomAttributes', {
|
||||
conversationId: this.conversationId,
|
||||
customAttributes: {
|
||||
...this.customAttributes,
|
||||
[attribute_key]: null,
|
||||
},
|
||||
});
|
||||
const { attribute_key } = attribute;
|
||||
if (this.attributeType === 'conversation_attribute') {
|
||||
await this.$store.dispatch('updateCustomAttributes', {
|
||||
conversationId: this.conversationId,
|
||||
customAttributes: {
|
||||
...this.customAttributes,
|
||||
[attribute_key]: null,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
await this.$store.dispatch('contacts/update', {
|
||||
id: this.contactId,
|
||||
custom_attributes: {
|
||||
...this.customAttributes,
|
||||
[attribute_key]: null,
|
||||
},
|
||||
});
|
||||
}
|
||||
bus.$emit(BUS_EVENTS.FOCUS_CUSTOM_ATTRIBUTE, attribute_key);
|
||||
this.showAlert(this.$t('CUSTOM_ATTRIBUTES.FORM.ADD.SUCCESS'));
|
||||
} catch (error) {
|
||||
|
||||
@@ -37,14 +37,23 @@ export default {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
contactId: { type: Number, default: null },
|
||||
},
|
||||
methods: {
|
||||
async onUpdate(key, value) {
|
||||
const updatedAttributes = { ...this.customAttributes, [key]: value };
|
||||
try {
|
||||
await this.$store.dispatch('updateCustomAttributes', {
|
||||
conversationId: this.conversationId,
|
||||
customAttributes: { ...this.customAttributes, [key]: value },
|
||||
});
|
||||
if (this.attributeType === 'conversation_attribute') {
|
||||
await this.$store.dispatch('updateCustomAttributes', {
|
||||
conversationId: this.conversationId,
|
||||
customAttributes: updatedAttributes,
|
||||
});
|
||||
} else {
|
||||
this.$store.dispatch('contacts/update', {
|
||||
id: this.contactId,
|
||||
custom_attributes: updatedAttributes,
|
||||
});
|
||||
}
|
||||
this.showAlert(this.$t('CUSTOM_ATTRIBUTES.FORM.UPDATE.SUCCESS'));
|
||||
} catch (error) {
|
||||
const errorMessage =
|
||||
@@ -54,13 +63,20 @@ export default {
|
||||
}
|
||||
},
|
||||
async onDelete(key) {
|
||||
const { [key]: remove, ...updatedAttributes } = this.customAttributes;
|
||||
|
||||
try {
|
||||
await this.$store.dispatch('updateCustomAttributes', {
|
||||
conversationId: this.conversationId,
|
||||
customAttributes: updatedAttributes,
|
||||
});
|
||||
const { [key]: remove, ...updatedAttributes } = this.customAttributes;
|
||||
if (this.attributeType === 'conversation_attribute') {
|
||||
await this.$store.dispatch('updateCustomAttributes', {
|
||||
conversationId: this.conversationId,
|
||||
customAttributes: updatedAttributes,
|
||||
});
|
||||
} else {
|
||||
this.$store.dispatch('contacts/deleteCustomAttributes', {
|
||||
id: this.contactId,
|
||||
customAttributes: [key],
|
||||
});
|
||||
}
|
||||
|
||||
this.showAlert(this.$t('CUSTOM_ATTRIBUTES.FORM.DELETE.SUCCESS'));
|
||||
} catch (error) {
|
||||
const errorMessage =
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
: ''
|
||||
"
|
||||
:placeholder="$t('ATTRIBUTES_MGMT.ADD.FORM.NAME.PLACEHOLDER')"
|
||||
@input="onDisplayNameChange"
|
||||
@blur="$v.displayName.$touch"
|
||||
/>
|
||||
<label :class="{ error: $v.description.$error }">
|
||||
@@ -53,22 +54,22 @@
|
||||
{{ $t('ATTRIBUTES_MGMT.ADD.FORM.TYPE.ERROR') }}
|
||||
</span>
|
||||
</label>
|
||||
<div v-if="displayName" class="medium-12 columns">
|
||||
<label>
|
||||
{{ $t('ATTRIBUTES_MGMT.ADD.FORM.KEY.LABEL') }}
|
||||
<i class="ion-help" />
|
||||
</label>
|
||||
<p class="key-value text-truncate">
|
||||
{{ attributeKey }}
|
||||
</p>
|
||||
</div>
|
||||
<woot-input
|
||||
v-model="attributeKey"
|
||||
:label="$t('ATTRIBUTES_MGMT.ADD.FORM.KEY.LABEL')"
|
||||
type="text"
|
||||
:class="{ error: $v.attributeKey.$error }"
|
||||
:error="
|
||||
$v.attributeKey.$error
|
||||
? $t('ATTRIBUTES_MGMT.ADD.FORM.KEY.ERROR')
|
||||
: ''
|
||||
"
|
||||
:placeholder="$t('ATTRIBUTES_MGMT.ADD.FORM.KEY.PLACEHOLDER')"
|
||||
@blur="$v.attributeKey.$touch"
|
||||
/>
|
||||
<div class="modal-footer">
|
||||
<woot-submit-button
|
||||
:disabled="
|
||||
$v.displayName.$invalid ||
|
||||
$v.description.$invalid ||
|
||||
uiFlags.isCreating
|
||||
"
|
||||
:disabled="isButtonDisabled"
|
||||
:button-text="$t('ATTRIBUTES_MGMT.ADD.SUBMIT')"
|
||||
/>
|
||||
<button class="button clear" @click.prevent="onClose">
|
||||
@@ -103,6 +104,7 @@ export default {
|
||||
description: '',
|
||||
attributeModel: 0,
|
||||
attributeType: 0,
|
||||
attributeKey: '',
|
||||
models: ATTRIBUTE_MODELS,
|
||||
types: ATTRIBUTE_TYPES,
|
||||
show: true,
|
||||
@@ -113,8 +115,12 @@ export default {
|
||||
...mapGetters({
|
||||
uiFlags: 'getUIFlags',
|
||||
}),
|
||||
attributeKey() {
|
||||
return convertToSlug(this.displayName);
|
||||
isButtonDisabled() {
|
||||
return (
|
||||
this.$v.displayName.$invalid ||
|
||||
this.$v.description.$invalid ||
|
||||
this.uiFlags.isCreating
|
||||
);
|
||||
},
|
||||
},
|
||||
|
||||
@@ -132,9 +138,15 @@ export default {
|
||||
attributeType: {
|
||||
required,
|
||||
},
|
||||
attributeKey: {
|
||||
required,
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
onDisplayNameChange() {
|
||||
this.attributeKey = convertToSlug(this.displayName);
|
||||
},
|
||||
async addAttributes() {
|
||||
try {
|
||||
await this.$store.dispatch('attributes/create', {
|
||||
|
||||
@@ -135,6 +135,10 @@ export default {
|
||||
key: 0,
|
||||
name: this.$t('ATTRIBUTES_MGMT.TABS.CONVERSATION'),
|
||||
},
|
||||
{
|
||||
key: 1,
|
||||
name: this.$t('ATTRIBUTES_MGMT.TABS.CONTACT'),
|
||||
},
|
||||
];
|
||||
},
|
||||
deleteConfirmText() {
|
||||
|
||||
@@ -41,15 +41,20 @@
|
||||
{{ $t('ATTRIBUTES_MGMT.ADD.FORM.TYPE.ERROR') }}
|
||||
</span>
|
||||
</label>
|
||||
<div v-if="displayName" class="medium-12 columns">
|
||||
<label>
|
||||
{{ $t('ATTRIBUTES_MGMT.ADD.FORM.KEY.LABEL') }}
|
||||
<i class="ion-help" />
|
||||
</label>
|
||||
<p class="key-value text-truncate">
|
||||
{{ attributeKey }}
|
||||
</p>
|
||||
</div>
|
||||
<woot-input
|
||||
v-model.trim="attributeKey"
|
||||
:label="$t('ATTRIBUTES_MGMT.ADD.FORM.KEY.LABEL')"
|
||||
type="text"
|
||||
:class="{ error: $v.attributeKey.$error }"
|
||||
:error="
|
||||
$v.attributeKey.$error
|
||||
? $t('ATTRIBUTES_MGMT.ADD.FORM.KEY.ERROR')
|
||||
: ''
|
||||
"
|
||||
:placeholder="$t('ATTRIBUTES_MGMT.ADD.FORM.KEY.PLACEHOLDER')"
|
||||
readonly
|
||||
@blur="$v.attributeKey.$touch"
|
||||
/>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<woot-button
|
||||
@@ -69,7 +74,6 @@
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import { required, minLength } from 'vuelidate/lib/validators';
|
||||
import { convertToSlug } from 'dashboard/helper/commons.js';
|
||||
import { ATTRIBUTE_TYPES } from './constants';
|
||||
import alertMixin from 'shared/mixins/alertMixin';
|
||||
export default {
|
||||
@@ -92,6 +96,7 @@ export default {
|
||||
attributeType: 0,
|
||||
types: ATTRIBUTE_TYPES,
|
||||
show: true,
|
||||
attributeKey: '',
|
||||
};
|
||||
},
|
||||
validations: {
|
||||
@@ -105,6 +110,9 @@ export default {
|
||||
required,
|
||||
minLength: minLength(1),
|
||||
},
|
||||
attributeKey: {
|
||||
required,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
@@ -115,9 +123,6 @@ export default {
|
||||
this.selectedAttribute.attribute_display_name
|
||||
}`;
|
||||
},
|
||||
attributeKey() {
|
||||
return convertToSlug(this.displayName);
|
||||
},
|
||||
selectedAttributeType() {
|
||||
return this.types.find(
|
||||
item =>
|
||||
@@ -137,6 +142,7 @@ export default {
|
||||
this.displayName = this.selectedAttribute.attribute_display_name;
|
||||
this.description = this.selectedAttribute.attribute_description;
|
||||
this.attributeType = this.selectedAttributeType;
|
||||
this.attributeKey = this.selectedAttribute.attribute_key;
|
||||
},
|
||||
async editAttributes() {
|
||||
this.$v.$touch();
|
||||
|
||||
@@ -5,7 +5,7 @@ import { frontendURL } from '../../../../helper/URLHelper';
|
||||
export default {
|
||||
routes: [
|
||||
{
|
||||
path: frontendURL('accounts/:accountId/settings/attributes'),
|
||||
path: frontendURL('accounts/:accountId/settings/custom-attributes'),
|
||||
component: SettingsContent,
|
||||
props: {
|
||||
headerTitle: 'ATTRIBUTES_MGMT.HEADER',
|
||||
|
||||
@@ -3,6 +3,10 @@ export const ATTRIBUTE_MODELS = [
|
||||
id: 0,
|
||||
option: 'Conversation',
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
option: 'Contact',
|
||||
},
|
||||
];
|
||||
|
||||
export const ATTRIBUTE_TYPES = [
|
||||
|
||||
Reference in New Issue
Block a user