feat: Create custom attributes for a contact from CRM (#2299)

* feat: Creates cutom attributes for a contact from CRM

* Review fixes

* Change inline forms edit icon size

* Review fixes

* Fix validation labels color

Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
This commit is contained in:
Nithin David Thomas 2021-05-21 19:22:47 +05:30 committed by GitHub
parent 64718eb879
commit 26ba8e6ff7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 248 additions and 11 deletions

View file

@ -2,6 +2,7 @@ import { addDecorator } from '@storybook/vue';
import Vue from 'vue';
import Vuex from 'vuex';
import VueI18n from 'vue-i18n';
import Vuelidate from 'vuelidate';
import WootUiKit from '../app/javascript/dashboard/components';
import i18n from '../app/javascript/dashboard/i18n';
@ -9,6 +10,7 @@ import i18n from '../app/javascript/dashboard/i18n';
import '../app/javascript/dashboard/assets/scss/storybook.scss';
Vue.use(VueI18n);
Vue.use(Vuelidate);
Vue.use(WootUiKit);
Vue.use(Vuex);

View file

@ -17,9 +17,6 @@
"NO_RECORDS_FOUND": "There are no previous conversations associated to this contact.",
"TITLE": "Previous Conversations"
},
"CUSTOM_ATTRIBUTES": {
"TITLE": "Custom Attributes"
},
"LABELS": {
"TITLE": "Conversation Labels",
"MODAL": {
@ -170,5 +167,26 @@
"FOOTER": {
"BUTTON": "View all notes"
}
},
"CUSTOM_ATTRIBUTES": {
"TITLE": "Custom Attributes",
"BUTTON": "Add custom attribute",
"ADD": {
"TITLE": "Create custom attribute",
"DESC": "Add custom information to this contact."
},
"FORM": {
"CREATE": "Add attribute",
"CANCEL": "Cancel",
"NAME": {
"LABEL": "Custom attribute name",
"PLACEHOLDER": "Eg: shopify id",
"ERROR": "Invalid custom attribute name"
},
"VALUE": {
"LABEL": "Attribute value",
"PLACEHOLDER": "Eg: 11901 "
}
}
}
}

View file

@ -0,0 +1,97 @@
<template>
<modal :show.sync="show" :on-close="onClose">
<woot-modal-header
:header-title="$t('CUSTOM_ATTRIBUTES.ADD.TITLE')"
:header-content="$t('CUSTOM_ATTRIBUTES.ADD.DESC')"
/>
<form class="row" @submit.prevent="addCustomAttribute">
<woot-input
v-model.trim="attributeName"
:class="{ error: $v.attributeName.$error }"
class="medium-12 columns"
:error="attributeNameError"
:label="$t('CUSTOM_ATTRIBUTES.FORM.NAME.LABEL')"
:placeholder="$t('CUSTOM_ATTRIBUTES.FORM.NAME.PLACEHOLDER')"
@input="$v.attributeName.$touch"
/>
<woot-input
v-model.trim="attributeValue"
class="medium-12 columns"
:label="$t('CUSTOM_ATTRIBUTES.FORM.VALUE.LABEL')"
:placeholder="$t('CUSTOM_ATTRIBUTES.FORM.VALUE.PLACEHOLDER')"
/>
<div class="modal-footer">
<woot-button
:is-disabled="$v.attributeName.$invalid || isCreating"
:is-loading="isCreating"
>
{{ $t('CUSTOM_ATTRIBUTES.FORM.CREATE') }}
</woot-button>
<woot-button variant="clear" @click.prevent="onClose">
{{ $t('CUSTOM_ATTRIBUTES.FORM.CANCEL') }}
</woot-button>
</div>
</form>
</modal>
</template>
<script>
import Modal from 'dashboard/components/Modal';
import alertMixin from 'shared/mixins/alertMixin';
import { required, minLength } from 'vuelidate/lib/validators';
export default {
components: {
Modal,
},
mixins: [alertMixin],
props: {
show: {
type: Boolean,
default: true,
},
isCreating: {
type: Boolean,
default: false,
},
},
data() {
return {
attributeValue: '',
attributeName: '',
};
},
validations: {
attributeName: {
required,
minLength: minLength(2),
},
},
computed: {
attributeNameError() {
if (this.$v.attributeName.$error) {
return this.$t('CUSTOM_ATTRIBUTES.FORM.NAME.ERROR');
}
return '';
},
},
methods: {
addCustomAttribute() {
this.$emit('create', {
attributeValue: this.attributeValue,
attributeName: this.attributeName || '',
});
this.reset();
this.$emit('cancel');
},
onClose() {
this.reset();
this.$emit('cancel');
},
reset() {
this.attributeValue = '';
this.attributeName = '';
},
},
};
</script>

View file

@ -34,7 +34,7 @@
<woot-button
v-if="showEdit"
variant="clear link"
size="tiny"
size="small"
color-scheme="secondary"
icon="ion-compose"
class-names="edit-button"

View file

@ -26,6 +26,27 @@
:show-edit="true"
@update="onLocationUpdate"
/>
<div
v-for="attribute in customAttributekeys"
:key="attribute"
class="custom-attribute--row"
>
<attribute
:label="attribute"
icon="ion-arrow-right-c"
:value="customAttributes[attribute]"
:show-edit="true"
@update="value => onCustomAttributeUpdate(attribute, value)"
/>
</div>
<woot-button
size="small"
variant="link"
icon="ion-plus"
@click="handleCustomCreate"
>
{{ $t('CUSTOM_ATTRIBUTES.ADD.TITLE') }}
</woot-button>
</div>
</template>
<script>
@ -48,17 +69,33 @@ export default {
const { company = {} } = this.contact;
return company;
},
customAttributes() {
const { custom_attributes: customAttributes = {} } = this.contact;
return customAttributes;
},
customAttributekeys() {
return Object.keys(this.customAttributes).filter(key => {
const value = this.customAttributes[key];
return value !== null && value !== undefined;
});
},
},
methods: {
onEmailUpdate(value) {
this.$emit('update', { email: value });
},
onPhoneUpdate(value) {
this.$emit('update', { phone: value });
this.$emit('update', { phone_number: value });
},
onLocationUpdate(value) {
this.$emit('update', { location: value });
},
handleCustomCreate() {
this.$emit('create-attribute');
},
onCustomAttributeUpdate(key, value) {
this.$emit('update', { custom_attributes: { [key]: value } });
},
},
};
</script>

View file

@ -5,7 +5,11 @@
@message="toggleConversationModal"
@edit="toggleEditModal"
/>
<contact-fields :contact="contact" :edit="null" />
<contact-fields
:contact="contact"
@update="updateField"
@create-attribute="toggleCustomAttributeModal"
/>
<edit-contact
v-if="showEditModal"
:show="showEditModal"
@ -13,24 +17,32 @@
@cancel="toggleEditModal"
/>
<new-conversation
v-if="enableNewConversation"
:show="showConversationModal"
:contact="contact"
@cancel="toggleConversationModal"
/>
<add-custom-attribute
:show="showCustomAttributeModal"
@cancel="toggleCustomAttributeModal"
@create="createCustomAttribute"
/>
</div>
</template>
<script>
import EditContact from 'dashboard/routes/dashboard/conversation/contact/EditContact';
import NewConversation from 'dashboard/routes/dashboard/conversation/contact/NewConversation';
import AddCustomAttribute from 'dashboard/modules/contact/components/AddCustomAttribute';
import ContactIntro from './ContactIntro';
import ContactFields from './ContactFields';
export default {
components: {
AddCustomAttribute,
ContactFields,
ContactIntro,
EditContact,
NewConversation,
ContactIntro,
ContactFields,
},
props: {
contact: {
@ -40,17 +52,48 @@ export default {
},
data() {
return {
showCustomAttributeModal: false,
showEditModal: false,
showConversationModal: false,
};
},
computed: {
enableNewConversation() {
return this.contact && this.contact.id;
},
},
methods: {
toggleCustomAttributeModal() {
this.showCustomAttributeModal = !this.showCustomAttributeModal;
},
toggleEditModal() {
this.showEditModal = !this.showEditModal;
},
toggleConversationModal() {
this.showConversationModal = !this.showConversationModal;
},
createCustomAttribute(data) {
const { id } = this.contact;
const { attributeValue, attributeName } = data;
const updatedFields = {
id,
custom_attributes: {
[attributeName]: attributeValue,
},
};
this.updateContact(updatedFields);
},
updateField(data) {
const { id } = this.contact;
const updatedFields = {
id,
...data,
};
this.updateContact(updatedFields);
},
updateContact(contactItem) {
this.$store.dispatch('contacts/update', contactItem);
},
},
};
</script>

View file

@ -55,10 +55,14 @@ export default {
.wrap {
@include three-column-grid(27.2rem);
background: var(--color-background);
min-height: 0;
background: var(--color-background-light);
border-top: 1px solid var(--color-border);
}
.left {
overflow: auto;
}
.center {
border-right: 1px solid var(--color-border);
border-left: 1px solid var(--color-border);

View file

@ -0,0 +1,32 @@
import AddCustomAttribute from '../components/AddCustomAttribute';
import { action } from '@storybook/addon-actions';
export default {
title: 'Components/Contact/AddCustomAttribute',
component: AddCustomAttribute,
argTypes: {
show: {
defaultValue: true,
control: {
type: 'boolean',
},
},
isCreating: {
defaultValue: false,
control: {
type: 'boolean',
},
},
},
};
const Template = (args, { argTypes }) => ({
props: Object.keys(argTypes),
components: { AddCustomAttribute },
template: '<add-custom-attribute v-bind="$props" @create="onCreate" />',
});
export const DefaultAttribute = Template.bind({});
DefaultAttribute.args = {
onCreate: action('edit'),
};

View file

@ -10,7 +10,7 @@ const Template = (args, { argTypes }) => ({
props: Object.keys(argTypes),
components: { ContactFields },
template:
'<contact-fields v-bind="$props" :contact="contact" @update="onUpdate" />',
'<contact-fields v-bind="$props" :contact="contact" @update="onUpdate" @create-attribute="onCreate" />',
});
export const DefaultContactFields = Template.bind({});
@ -39,4 +39,5 @@ DefaultContactFields.args = {
},
},
onUpdate: action('update'),
onCreate: action('create'),
};

View file

@ -73,6 +73,9 @@ export default {
<style lang="scss" scoped>
.contact-manage-view {
display: flex;
flex-direction: column;
width: 100%;
flex: 1 1 0;
}
</style>