feat: Reverse the contact merge (#8057)
Co-authored-by: Pranav Raj S <pranav@chatwoot.com>
This commit is contained in:
@@ -9,7 +9,6 @@ class Api::V1::Accounts::Actions::ContactMergesController < Api::V1::Accounts::B
|
|||||||
mergee_contact: @mergee_contact
|
mergee_contact: @mergee_contact
|
||||||
)
|
)
|
||||||
contact_merge_action.perform
|
contact_merge_action.perform
|
||||||
render json: @base_contact
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|||||||
@@ -343,17 +343,17 @@
|
|||||||
"DESCRIPTION": "Merge contacts to combine two profiles into one, including all attributes and conversations. In case of conflict, the Primary contact’s attributes will take precedence.",
|
"DESCRIPTION": "Merge contacts to combine two profiles into one, including all attributes and conversations. In case of conflict, the Primary contact’s attributes will take precedence.",
|
||||||
"PRIMARY": {
|
"PRIMARY": {
|
||||||
"TITLE": "Primary contact",
|
"TITLE": "Primary contact",
|
||||||
"HELP_LABEL": "To be kept"
|
"HELP_LABEL": "To be deleted"
|
||||||
},
|
},
|
||||||
"CHILD": {
|
"PARENT": {
|
||||||
"TITLE": "Contact to merge",
|
"TITLE": "Contact to merge",
|
||||||
"PLACEHOLDER": "Search for a contact",
|
"PLACEHOLDER": "Search for a contact",
|
||||||
"HELP_LABEL": "To be deleted"
|
"HELP_LABEL": "To be kept"
|
||||||
},
|
},
|
||||||
"SUMMARY": {
|
"SUMMARY": {
|
||||||
"TITLE": "Summary",
|
"TITLE": "Summary",
|
||||||
"DELETE_WARNING": "Contact of <strong>%{childContactName}</strong> will be deleted.",
|
"DELETE_WARNING": "Contact of <strong>%{primaryContactName}</strong> will be deleted.",
|
||||||
"ATTRIBUTE_WARNING": "Contact details of <strong>%{childContactName}</strong> will be copied to <strong>%{primaryContactName}</strong>."
|
"ATTRIBUTE_WARNING": "Contact details of <strong>%{primaryContactName}</strong> will be copied to <strong>%{parentContactName}</strong>."
|
||||||
},
|
},
|
||||||
"SEARCH": {
|
"SEARCH": {
|
||||||
"ERROR": "ERROR_MESSAGE"
|
"ERROR": "ERROR_MESSAGE"
|
||||||
|
|||||||
@@ -73,12 +73,12 @@ export default {
|
|||||||
this.isSearching = false;
|
this.isSearching = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async onMergeContacts(childContactId) {
|
async onMergeContacts(parentContactId) {
|
||||||
this.$track(CONTACTS_EVENTS.MERGED_CONTACTS);
|
this.$track(CONTACTS_EVENTS.MERGED_CONTACTS);
|
||||||
try {
|
try {
|
||||||
await this.$store.dispatch('contacts/merge', {
|
await this.$store.dispatch('contacts/merge', {
|
||||||
childId: childContactId,
|
childId: this.primaryContact.id,
|
||||||
parentId: this.primaryContact.id,
|
parentId: parentContactId,
|
||||||
});
|
});
|
||||||
this.showAlert(this.$t('MERGE_CONTACTS.FORM.SUCCESS_MESSAGE'));
|
this.showAlert(this.$t('MERGE_CONTACTS.FORM.SUCCESS_MESSAGE'));
|
||||||
this.onClose();
|
this.onClose();
|
||||||
|
|||||||
@@ -1,62 +1,29 @@
|
|||||||
<template>
|
<template>
|
||||||
<form @submit.prevent="onSubmit">
|
<form @submit.prevent="onSubmit">
|
||||||
<div class="merge-contacts">
|
<div>
|
||||||
<div class="multiselect-wrap--medium">
|
<div>
|
||||||
<label class="multiselect__label">
|
|
||||||
{{ $t('MERGE_CONTACTS.PRIMARY.TITLE') }}
|
|
||||||
<woot-label
|
|
||||||
:title="$t('MERGE_CONTACTS.PRIMARY.HELP_LABEL')"
|
|
||||||
color-scheme="success"
|
|
||||||
small
|
|
||||||
class="label--merge-warning"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<multiselect
|
|
||||||
:value="primaryContact"
|
|
||||||
disabled
|
|
||||||
:options="[]"
|
|
||||||
:show-labels="false"
|
|
||||||
label="name"
|
|
||||||
track-by="id"
|
|
||||||
>
|
|
||||||
<template slot="singleLabel" slot-scope="props">
|
|
||||||
<contact-dropdown-item
|
|
||||||
:thumbnail="props.option.thumbnail"
|
|
||||||
:name="props.option.name"
|
|
||||||
:identifier="props.option.id"
|
|
||||||
:email="props.option.email"
|
|
||||||
:phone-number="props.option.phoneNumber"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</multiselect>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="child-contact-wrap">
|
|
||||||
<div class="child-arrow">
|
|
||||||
<fluent-icon icon="arrow-up" class="up" size="17" />
|
|
||||||
</div>
|
|
||||||
<div
|
<div
|
||||||
class="child-contact multiselect-wrap--medium"
|
class="mt-1 multiselect-wrap--medium"
|
||||||
:class="{ error: $v.childContact.$error }"
|
:class="{ error: $v.parentContact.$error }"
|
||||||
>
|
>
|
||||||
<label class="multiselect__label">
|
<label class="multiselect__label">
|
||||||
{{ $t('MERGE_CONTACTS.CHILD.TITLE')
|
{{ $t('MERGE_CONTACTS.PARENT.TITLE') }}
|
||||||
}}<woot-label
|
<woot-label
|
||||||
:title="$t('MERGE_CONTACTS.CHILD.HELP_LABEL')"
|
:title="$t('MERGE_CONTACTS.PARENT.HELP_LABEL')"
|
||||||
color-scheme="alert"
|
color-scheme="success"
|
||||||
small
|
small
|
||||||
class="label--merge-warning"
|
class="ml-2"
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
<multiselect
|
<multiselect
|
||||||
v-model="childContact"
|
v-model="parentContact"
|
||||||
:options="searchResults"
|
:options="searchResults"
|
||||||
label="name"
|
label="name"
|
||||||
track-by="id"
|
track-by="id"
|
||||||
:internal-search="false"
|
:internal-search="false"
|
||||||
:clear-on-select="false"
|
:clear-on-select="false"
|
||||||
:show-labels="false"
|
:show-labels="false"
|
||||||
:placeholder="$t('MERGE_CONTACTS.CHILD.PLACEHOLDER')"
|
:placeholder="$t('MERGE_CONTACTS.PARENT.PLACEHOLDER')"
|
||||||
:allow-empty="true"
|
:allow-empty="true"
|
||||||
:loading="isSearching"
|
:loading="isSearching"
|
||||||
:max-height="150"
|
:max-height="150"
|
||||||
@@ -85,17 +52,57 @@
|
|||||||
{{ $t('AGENT_MGMT.SEARCH.NO_RESULTS') }}
|
{{ $t('AGENT_MGMT.SEARCH.NO_RESULTS') }}
|
||||||
</span>
|
</span>
|
||||||
</multiselect>
|
</multiselect>
|
||||||
<span v-if="$v.childContact.$error" class="message">
|
<span v-if="$v.parentContact.$error" class="message">
|
||||||
{{ $t('MERGE_CONTACTS.FORM.CHILD_CONTACT.ERROR') }}
|
{{ $t('MERGE_CONTACTS.FORM.CHILD_CONTACT.ERROR') }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="multiselect-wrap--medium flex">
|
||||||
|
<div
|
||||||
|
class="w-8 relative text-base text-slate-100 dark:text-slate-600 after:content-[''] after:h-12 after:w-0 after:left-4 after:absolute after:border-l after:border-solid after:border-slate-100 after:dark:border-slate-600 before:content-[''] before:h-0 before:w-4 before:left-4 before:top-12 before:absolute before:border-b before:border-solid before:border-slate-100 before:dark:border-slate-600"
|
||||||
|
>
|
||||||
|
<fluent-icon
|
||||||
|
icon="arrow-up"
|
||||||
|
class="absolute -top-1 left-2"
|
||||||
|
size="17"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col w-full">
|
||||||
|
<label class="multiselect__label">
|
||||||
|
{{ $t('MERGE_CONTACTS.PRIMARY.TITLE') }}
|
||||||
|
<woot-label
|
||||||
|
:title="$t('MERGE_CONTACTS.PRIMARY.HELP_LABEL')"
|
||||||
|
color-scheme="alert"
|
||||||
|
small
|
||||||
|
class="ml-2"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<multiselect
|
||||||
|
:value="primaryContact"
|
||||||
|
disabled
|
||||||
|
:options="[]"
|
||||||
|
:show-labels="false"
|
||||||
|
label="name"
|
||||||
|
track-by="id"
|
||||||
|
>
|
||||||
|
<template slot="singleLabel" slot-scope="props">
|
||||||
|
<contact-dropdown-item
|
||||||
|
:thumbnail="props.option.thumbnail"
|
||||||
|
:name="props.option.name"
|
||||||
|
:identifier="props.option.id"
|
||||||
|
:email="props.option.email"
|
||||||
|
:phone-number="props.option.phoneNumber"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</multiselect>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<merge-contact-summary
|
<merge-contact-summary
|
||||||
:primary-contact-name="primaryContact.name"
|
:primary-contact-name="primaryContact.name"
|
||||||
:child-contact-name="childContactName"
|
:parent-contact-name="parentContactName"
|
||||||
/>
|
/>
|
||||||
<div class="footer">
|
<div class="mt-6 flex justify-end">
|
||||||
<woot-button variant="clear" @click.prevent="onCancel">
|
<woot-button variant="clear" @click.prevent="onCancel">
|
||||||
{{ $t('MERGE_CONTACTS.FORM.CANCEL') }}
|
{{ $t('MERGE_CONTACTS.FORM.CANCEL') }}
|
||||||
</woot-button>
|
</woot-button>
|
||||||
@@ -138,19 +145,19 @@ export default {
|
|||||||
primaryContact: {
|
primaryContact: {
|
||||||
required,
|
required,
|
||||||
},
|
},
|
||||||
childContact: {
|
parentContact: {
|
||||||
required,
|
required,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
childContact: undefined,
|
parentContact: undefined,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
childContactName() {
|
parentContactName() {
|
||||||
return this.childContact ? this.childContact.name : '';
|
return this.parentContact ? this.parentContact.name : '';
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@@ -162,7 +169,7 @@ export default {
|
|||||||
if (this.$v.$invalid) {
|
if (this.$v.$invalid) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.$emit('submit', this.childContact.id);
|
this.$emit('submit', this.parentContact.id);
|
||||||
},
|
},
|
||||||
onCancel() {
|
onCancel() {
|
||||||
this.$emit('cancel');
|
this.$emit('cancel');
|
||||||
@@ -172,51 +179,24 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.child-contact-wrap {
|
|
||||||
@apply flex w-full;
|
|
||||||
}
|
|
||||||
.child-contact {
|
|
||||||
@apply min-w-0 flex-grow flex-shrink-0;
|
|
||||||
}
|
|
||||||
.child-arrow {
|
|
||||||
@apply w-8 relative text-base text-slate-100 dark:text-slate-600;
|
|
||||||
}
|
|
||||||
.multiselect {
|
|
||||||
@apply mb-2;
|
|
||||||
}
|
|
||||||
.child-contact {
|
|
||||||
@apply mt-1;
|
|
||||||
}
|
|
||||||
.child-arrow::after {
|
|
||||||
@apply content-[''] h-12 w-0 left-5 absolute border-l border-solid border-slate-100 dark:border-slate-600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.child-arrow::before {
|
|
||||||
@apply content-[''] h-0 w-4 left-5 top-12 absolute border-b border-solid border-slate-100 dark:border-slate-600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.up {
|
|
||||||
@apply absolute -top-1 left-3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer {
|
|
||||||
@apply mt-6 flex justify-end;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TDOD: Clean errors in forms style */
|
/* TDOD: Clean errors in forms style */
|
||||||
.error .message {
|
.error .message {
|
||||||
@apply mt-0;
|
@apply mt-0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.label--merge-warning {
|
|
||||||
@apply ml-2;
|
|
||||||
}
|
|
||||||
|
|
||||||
::v-deep {
|
::v-deep {
|
||||||
.multiselect {
|
.multiselect {
|
||||||
@apply rounded-md;
|
@apply rounded-md;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.multiselect--disabled {
|
||||||
|
@apply border-0;
|
||||||
|
|
||||||
|
.multiselect__tags {
|
||||||
|
@apply border;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.multiselect__tags {
|
.multiselect__tags {
|
||||||
@apply h-[52px];
|
@apply h-[52px];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +1,29 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-if="childContactName" class="merge-summary callout">
|
<div
|
||||||
<h5 class="text-block-title">
|
v-if="parentContactName"
|
||||||
|
class="my-4 relative p-2.5 border rounded-[4px] text-slate-800 dark:text-slate-100 border-slate-100 dark:border-slate-700 bg-white dark:bg-slate-800"
|
||||||
|
>
|
||||||
|
<h5 class="text-base font-medium text-slate-900 dark:text-white">
|
||||||
{{ $t('MERGE_CONTACTS.SUMMARY.TITLE') }}
|
{{ $t('MERGE_CONTACTS.SUMMARY.TITLE') }}
|
||||||
</h5>
|
</h5>
|
||||||
<ul class="summary-items">
|
<ul class="ml-0 list-none">
|
||||||
<li>
|
<li>
|
||||||
<span class="bullet">❌</span>
|
<span class="inline-block mr-1">❌</span>
|
||||||
<span
|
<span
|
||||||
v-dompurify-html="
|
v-dompurify-html="
|
||||||
$t('MERGE_CONTACTS.SUMMARY.DELETE_WARNING', {
|
$t('MERGE_CONTACTS.SUMMARY.DELETE_WARNING', {
|
||||||
childContactName,
|
primaryContactName,
|
||||||
})
|
})
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<span class="bullet">✅</span>
|
<span class="inline-block mr-1">✅</span>
|
||||||
<span
|
<span
|
||||||
v-dompurify-html="
|
v-dompurify-html="
|
||||||
$t('MERGE_CONTACTS.SUMMARY.ATTRIBUTE_WARNING', {
|
$t('MERGE_CONTACTS.SUMMARY.ATTRIBUTE_WARNING', {
|
||||||
childContactName,
|
|
||||||
primaryContactName,
|
primaryContactName,
|
||||||
|
parentContactName,
|
||||||
})
|
})
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
@@ -36,32 +39,10 @@ export default {
|
|||||||
type: String,
|
type: String,
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
childContactName: {
|
parentContactName: {
|
||||||
type: String,
|
type: String,
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {},
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.merge-summary {
|
|
||||||
margin-top: var(--space-normal);
|
|
||||||
}
|
|
||||||
|
|
||||||
.summary-items {
|
|
||||||
margin-left: 0;
|
|
||||||
list-style: none;
|
|
||||||
|
|
||||||
li {
|
|
||||||
margin-bottom: var(--space-smaller);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.bullet {
|
|
||||||
display: inline-block;
|
|
||||||
margin-right: var(--space-smaller);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
json.partial! 'api/v1/models/contact', formats: [:json], resource: @base_contact
|
||||||
Reference in New Issue
Block a user