feat: Custom fields in pre-chat form (#4189)
This commit is contained in:
parent
1ccd29140d
commit
26f23a6e21
25 changed files with 824 additions and 160 deletions
|
@ -17,8 +17,12 @@ export default {
|
||||||
value: { type: Boolean, default: false },
|
value: { type: Boolean, default: false },
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
onClick() {
|
onClick(event) {
|
||||||
|
if (event.pointerId === -1) {
|
||||||
|
event.preventDefault();
|
||||||
|
} else {
|
||||||
this.$emit('input', !this.value);
|
this.$emit('input', !this.value);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
100
app/javascript/dashboard/helper/preChat.js
Normal file
100
app/javascript/dashboard/helper/preChat.js
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
import i18n from 'widget/i18n/index';
|
||||||
|
const defaultTranslations = Object.fromEntries(
|
||||||
|
Object.entries(i18n).filter(([key]) => key.includes('en'))
|
||||||
|
).en;
|
||||||
|
|
||||||
|
export const standardFieldKeys = {
|
||||||
|
emailAddress: {
|
||||||
|
key: 'EMAIL_ADDRESS',
|
||||||
|
label: 'Email Id',
|
||||||
|
placeholder: 'Please enter your email address',
|
||||||
|
},
|
||||||
|
fullName: {
|
||||||
|
key: 'FULL_NAME',
|
||||||
|
label: 'Full Name',
|
||||||
|
placeholder: 'Please enter your full name',
|
||||||
|
},
|
||||||
|
phoneNumber: {
|
||||||
|
key: 'PHONE_NUMBER',
|
||||||
|
label: 'Phone Number',
|
||||||
|
placeholder: 'Please enter your phone number',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getLabel = ({ key, label }) => {
|
||||||
|
return defaultTranslations.PRE_CHAT_FORM.FIELDS[key]
|
||||||
|
? defaultTranslations.PRE_CHAT_FORM.FIELDS[key].LABEL
|
||||||
|
: label;
|
||||||
|
};
|
||||||
|
export const getPlaceHolder = ({ key, placeholder }) => {
|
||||||
|
return defaultTranslations.PRE_CHAT_FORM.FIELDS[key]
|
||||||
|
? defaultTranslations.PRE_CHAT_FORM.FIELDS[key].PLACEHOLDER
|
||||||
|
: placeholder;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getCustomFields = ({ standardFields, customAttributes }) => {
|
||||||
|
let customFields = [];
|
||||||
|
const { pre_chat_fields: preChatFields } = standardFields;
|
||||||
|
customAttributes.forEach(attribute => {
|
||||||
|
const itemExist = preChatFields.find(
|
||||||
|
item => item.name === attribute.attribute_key
|
||||||
|
);
|
||||||
|
if (!itemExist) {
|
||||||
|
customFields.push({
|
||||||
|
label: attribute.attribute_display_name,
|
||||||
|
placeholder: attribute.attribute_display_name,
|
||||||
|
name: attribute.attribute_key,
|
||||||
|
type: attribute.attribute_display_type,
|
||||||
|
values: attribute.attribute_values,
|
||||||
|
field_type: attribute.attribute_model,
|
||||||
|
required: false,
|
||||||
|
enabled: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return customFields;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getFormattedPreChatFields = ({ preChatFields }) => {
|
||||||
|
return preChatFields.map(item => {
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
label: getLabel({
|
||||||
|
key: standardFieldKeys[item.name]
|
||||||
|
? standardFieldKeys[item.name].key
|
||||||
|
: item.name,
|
||||||
|
label: item.label ? item.label : item.name,
|
||||||
|
}),
|
||||||
|
placeholder: getPlaceHolder({
|
||||||
|
key: standardFieldKeys[item.name]
|
||||||
|
? standardFieldKeys[item.name].key
|
||||||
|
: item.name,
|
||||||
|
placeholder: item.placeholder ? item.placeholder : item.name,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getPreChatFields = ({
|
||||||
|
preChatFormOptions = {},
|
||||||
|
customAttributes = [],
|
||||||
|
}) => {
|
||||||
|
const { pre_chat_message, pre_chat_fields } = preChatFormOptions;
|
||||||
|
let customFields = {};
|
||||||
|
let preChatFields = {};
|
||||||
|
|
||||||
|
const formattedPreChatFields = getFormattedPreChatFields({
|
||||||
|
preChatFields: pre_chat_fields,
|
||||||
|
});
|
||||||
|
|
||||||
|
customFields = getCustomFields({
|
||||||
|
standardFields: { pre_chat_fields: formattedPreChatFields },
|
||||||
|
customAttributes,
|
||||||
|
});
|
||||||
|
preChatFields = [...formattedPreChatFields, ...customFields];
|
||||||
|
|
||||||
|
return {
|
||||||
|
pre_chat_message,
|
||||||
|
pre_chat_fields: preChatFields,
|
||||||
|
};
|
||||||
|
};
|
47
app/javascript/dashboard/helper/specs/inboxFixture.js
Normal file
47
app/javascript/dashboard/helper/specs/inboxFixture.js
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
export default {
|
||||||
|
customFields: {
|
||||||
|
pre_chat_message: 'Share your queries or comments here.',
|
||||||
|
pre_chat_fields: [
|
||||||
|
{
|
||||||
|
label: 'Email Address',
|
||||||
|
name: 'emailAddress',
|
||||||
|
type: 'email',
|
||||||
|
field_type: 'standard',
|
||||||
|
required: false,
|
||||||
|
enabled: false,
|
||||||
|
|
||||||
|
placeholder: 'Please enter your email address',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Full Name',
|
||||||
|
name: 'fullName',
|
||||||
|
type: 'text',
|
||||||
|
field_type: 'standard',
|
||||||
|
required: false,
|
||||||
|
enabled: false,
|
||||||
|
placeholder: 'Please enter your full name',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Phone Number',
|
||||||
|
name: 'phoneNumber',
|
||||||
|
type: 'text',
|
||||||
|
field_type: 'standard',
|
||||||
|
required: false,
|
||||||
|
enabled: false,
|
||||||
|
placeholder: 'Please enter your phone number',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
customAttributes: [
|
||||||
|
{
|
||||||
|
id: 101,
|
||||||
|
attribute_description: 'Order Identifier',
|
||||||
|
attribute_display_name: 'Order Id',
|
||||||
|
attribute_display_type: 'number',
|
||||||
|
attribute_key: 'order_id',
|
||||||
|
attribute_model: 'conversation_attribute',
|
||||||
|
attribute_values: Array(0),
|
||||||
|
created_at: '2021-11-29T10:20:04.563Z',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
76
app/javascript/dashboard/helper/specs/preChat.spec.js
Normal file
76
app/javascript/dashboard/helper/specs/preChat.spec.js
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
import {
|
||||||
|
getPreChatFields,
|
||||||
|
getFormattedPreChatFields,
|
||||||
|
getCustomFields,
|
||||||
|
} from '../preChat';
|
||||||
|
import inboxFixture from './inboxFixture';
|
||||||
|
|
||||||
|
const { customFields, customAttributes } = inboxFixture;
|
||||||
|
describe('#Pre chat Helpers', () => {
|
||||||
|
describe('getPreChatFields', () => {
|
||||||
|
it('should return correct pre-chat fields form options passed', () => {
|
||||||
|
expect(getPreChatFields({ preChatFormOptions: customFields })).toEqual(
|
||||||
|
customFields
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('getFormattedPreChatFields', () => {
|
||||||
|
it('should return correct custom fields', () => {
|
||||||
|
expect(
|
||||||
|
getFormattedPreChatFields({
|
||||||
|
preChatFields: customFields.pre_chat_fields,
|
||||||
|
})
|
||||||
|
).toEqual([
|
||||||
|
{
|
||||||
|
label: 'Email Address',
|
||||||
|
name: 'emailAddress',
|
||||||
|
placeholder: 'Please enter your email address',
|
||||||
|
type: 'email',
|
||||||
|
field_type: 'standard',
|
||||||
|
|
||||||
|
required: false,
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Full Name',
|
||||||
|
name: 'fullName',
|
||||||
|
placeholder: 'Please enter your full name',
|
||||||
|
type: 'text',
|
||||||
|
field_type: 'standard',
|
||||||
|
required: false,
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Phone Number',
|
||||||
|
name: 'phoneNumber',
|
||||||
|
placeholder: 'Please enter your phone number',
|
||||||
|
type: 'text',
|
||||||
|
field_type: 'standard',
|
||||||
|
required: false,
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('getCustomFields', () => {
|
||||||
|
it('should return correct custom fields', () => {
|
||||||
|
expect(
|
||||||
|
getCustomFields({
|
||||||
|
standardFields: { pre_chat_fields: customFields.pre_chat_fields },
|
||||||
|
customAttributes,
|
||||||
|
})
|
||||||
|
).toEqual([
|
||||||
|
{
|
||||||
|
enabled: false,
|
||||||
|
label: 'Order Id',
|
||||||
|
placeholder: 'Order Id',
|
||||||
|
name: 'order_id',
|
||||||
|
required: false,
|
||||||
|
field_type: 'conversation_attribute',
|
||||||
|
type: 'number',
|
||||||
|
values: [],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -211,7 +211,6 @@
|
||||||
"PLACEHOLDER": "API key",
|
"PLACEHOLDER": "API key",
|
||||||
"APPLY_FOR_ACCESS": "Don't have any API key? Apply for access here",
|
"APPLY_FOR_ACCESS": "Don't have any API key? Apply for access here",
|
||||||
"ERROR": "Please enter a valid value."
|
"ERROR": "Please enter a valid value."
|
||||||
|
|
||||||
},
|
},
|
||||||
"SUBMIT_BUTTON": "Create WhatsApp Channel",
|
"SUBMIT_BUTTON": "Create WhatsApp Channel",
|
||||||
"API": {
|
"API": {
|
||||||
|
@ -433,6 +432,15 @@
|
||||||
},
|
},
|
||||||
"PRE_CHAT_FORM": {
|
"PRE_CHAT_FORM": {
|
||||||
"DESCRIPTION": "Pre chat forms enable you to capture user information before they start conversation with you.",
|
"DESCRIPTION": "Pre chat forms enable you to capture user information before they start conversation with you.",
|
||||||
|
"SET_FIELDS": "Pre chat form fields",
|
||||||
|
"SET_FIELDS_HEADER": {
|
||||||
|
"FIELDS": "Fields",
|
||||||
|
"LABEL": "Label",
|
||||||
|
"PLACE_HOLDER":"Placeholder",
|
||||||
|
"KEY": "Key",
|
||||||
|
"TYPE": "Type",
|
||||||
|
"REQUIRED": "Required"
|
||||||
|
},
|
||||||
"ENABLE": {
|
"ENABLE": {
|
||||||
"LABEL": "Enable pre chat form",
|
"LABEL": "Enable pre chat form",
|
||||||
"OPTIONS": {
|
"OPTIONS": {
|
||||||
|
@ -441,7 +449,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"PRE_CHAT_MESSAGE": {
|
"PRE_CHAT_MESSAGE": {
|
||||||
"LABEL": "Pre Chat Message",
|
"LABEL": "Pre chat message",
|
||||||
"PLACEHOLDER": "This message would be visible to the users along with the form"
|
"PLACEHOLDER": "This message would be visible to the users along with the form"
|
||||||
},
|
},
|
||||||
"REQUIRE_EMAIL": {
|
"REQUIRE_EMAIL": {
|
||||||
|
@ -465,7 +473,7 @@
|
||||||
"VALIDATION_ERROR": "Starting time should be before closing time.",
|
"VALIDATION_ERROR": "Starting time should be before closing time.",
|
||||||
"CHOOSE": "Choose"
|
"CHOOSE": "Choose"
|
||||||
},
|
},
|
||||||
"ALL_DAY":"All-Day"
|
"ALL_DAY": "All-Day"
|
||||||
},
|
},
|
||||||
"IMAP": {
|
"IMAP": {
|
||||||
"TITLE": "IMAP",
|
"TITLE": "IMAP",
|
||||||
|
|
|
@ -0,0 +1,91 @@
|
||||||
|
<template>
|
||||||
|
<draggable v-model="preChatFields" tag="tbody">
|
||||||
|
<tr v-for="(item, index) in preChatFields" :key="index">
|
||||||
|
<td class="pre-chat-field"><fluent-icon icon="drag" /></td>
|
||||||
|
<td class="pre-chat-field">
|
||||||
|
<woot-switch
|
||||||
|
:value="item['enabled']"
|
||||||
|
@input="handlePreChatFieldOptions($event, 'enabled', item)"
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td class="pre-chat-field" :class="{ 'disabled-text': !item['enabled'] }">
|
||||||
|
{{ item.name }}
|
||||||
|
</td>
|
||||||
|
<td class="pre-chat-field" :class="{ 'disabled-text': !item['enabled'] }">
|
||||||
|
{{ item.type }}
|
||||||
|
</td>
|
||||||
|
<td class="pre-chat-field">
|
||||||
|
<input
|
||||||
|
v-model="item['required']"
|
||||||
|
type="checkbox"
|
||||||
|
:value="`${item.name}-required`"
|
||||||
|
:disabled="!item['enabled']"
|
||||||
|
@click="handlePreChatFieldOptions($event, 'required', item)"
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td class="pre-chat-field" :class="{ 'disabled-text': !item['enabled'] }">
|
||||||
|
<input
|
||||||
|
v-model.trim="item.label"
|
||||||
|
type="text"
|
||||||
|
:disabled="isFieldEditable(item)"
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td class="pre-chat-field" :class="{ 'disabled-text': !item['enabled'] }">
|
||||||
|
<input
|
||||||
|
v-model.trim="item.placeholder"
|
||||||
|
type="text"
|
||||||
|
:disabled="isFieldEditable(item)"
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</draggable>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import draggable from 'vuedraggable';
|
||||||
|
import { standardFieldKeys } from 'dashboard/helper/preChat';
|
||||||
|
export default {
|
||||||
|
components: { draggable },
|
||||||
|
props: {
|
||||||
|
preChatFields: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
handlePreChatFieldOptions: {
|
||||||
|
type: Function,
|
||||||
|
default: () => {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
isFieldEditable(item) {
|
||||||
|
return !!standardFieldKeys[item.name] || !item.enabled;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.pre-chat-field {
|
||||||
|
padding: var(--space-normal) var(--space-small);
|
||||||
|
|
||||||
|
svg {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.disabled-text {
|
||||||
|
color: var(--s-500);
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
thead th {
|
||||||
|
text-transform: none;
|
||||||
|
}
|
||||||
|
input {
|
||||||
|
font-size: var(--font-size-small);
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
checkbox {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,10 +1,10 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="settings--content">
|
<div class="settings--content">
|
||||||
<div class="prechat--title">
|
<div class="pre-chat--title">
|
||||||
{{ $t('INBOX_MGMT.PRE_CHAT_FORM.DESCRIPTION') }}
|
{{ $t('INBOX_MGMT.PRE_CHAT_FORM.DESCRIPTION') }}
|
||||||
</div>
|
</div>
|
||||||
<form class="medium-6" @submit.prevent="updateInbox">
|
<form @submit.prevent="updateInbox">
|
||||||
<label class="medium-9 columns">
|
<label class="medium-3 columns">
|
||||||
{{ $t('INBOX_MGMT.PRE_CHAT_FORM.ENABLE.LABEL') }}
|
{{ $t('INBOX_MGMT.PRE_CHAT_FORM.ENABLE.LABEL') }}
|
||||||
<select v-model="preChatFormEnabled">
|
<select v-model="preChatFormEnabled">
|
||||||
<option :value="true">
|
<option :value="true">
|
||||||
|
@ -15,8 +15,8 @@
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
|
<div v-if="preChatFormEnabled">
|
||||||
<label class="medium-9">
|
<label class="medium-3 columns">
|
||||||
{{ $t('INBOX_MGMT.PRE_CHAT_FORM.PRE_CHAT_MESSAGE.LABEL') }}
|
{{ $t('INBOX_MGMT.PRE_CHAT_FORM.PRE_CHAT_MESSAGE.LABEL') }}
|
||||||
<textarea
|
<textarea
|
||||||
v-model.trim="preChatMessage"
|
v-model.trim="preChatMessage"
|
||||||
|
@ -26,17 +26,44 @@
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
<div>
|
<label class="medium-8 columns">
|
||||||
<input
|
{{ $t('INBOX_MGMT.PRE_CHAT_FORM.SET_FIELDS') }}
|
||||||
v-model="preChatFieldOptions"
|
<table class="table table-striped w-full">
|
||||||
type="checkbox"
|
<thead class="thead-dark">
|
||||||
value="requireEmail"
|
<tr>
|
||||||
@input="handlePreChatFieldOptions"
|
<th scope="col"></th>
|
||||||
|
<th scope="col"></th>
|
||||||
|
<th scope="col">
|
||||||
|
{{ $t('INBOX_MGMT.PRE_CHAT_FORM.SET_FIELDS_HEADER.KEY') }}
|
||||||
|
</th>
|
||||||
|
<th scope="col">
|
||||||
|
{{ $t('INBOX_MGMT.PRE_CHAT_FORM.SET_FIELDS_HEADER.TYPE') }}
|
||||||
|
</th>
|
||||||
|
<th scope="col">
|
||||||
|
{{
|
||||||
|
$t('INBOX_MGMT.PRE_CHAT_FORM.SET_FIELDS_HEADER.REQUIRED')
|
||||||
|
}}
|
||||||
|
</th>
|
||||||
|
<th scope="col">
|
||||||
|
{{ $t('INBOX_MGMT.PRE_CHAT_FORM.SET_FIELDS_HEADER.LABEL') }}
|
||||||
|
</th>
|
||||||
|
<th scope="col">
|
||||||
|
{{
|
||||||
|
$t(
|
||||||
|
'INBOX_MGMT.PRE_CHAT_FORM.SET_FIELDS_HEADER.PLACE_HOLDER'
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<pre-chat-fields
|
||||||
|
:pre-chat-fields="preChatFields"
|
||||||
|
:handle-pre-chat-field-options="handlePreChatFieldOptions"
|
||||||
/>
|
/>
|
||||||
<label for="requireEmail">
|
</table>
|
||||||
{{ $t('INBOX_MGMT.PRE_CHAT_FORM.REQUIRE_EMAIL.LABEL') }}
|
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<woot-submit-button
|
<woot-submit-button
|
||||||
:button-text="$t('INBOX_MGMT.SETTINGS_POPUP.UPDATE')"
|
:button-text="$t('INBOX_MGMT.SETTINGS_POPUP.UPDATE')"
|
||||||
:loading="uiFlags.isUpdatingInbox"
|
:loading="uiFlags.isUpdatingInbox"
|
||||||
|
@ -47,8 +74,13 @@
|
||||||
<script>
|
<script>
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import alertMixin from 'shared/mixins/alertMixin';
|
import alertMixin from 'shared/mixins/alertMixin';
|
||||||
|
import PreChatFields from './PreChatFields.vue';
|
||||||
|
import { getPreChatFields, standardFieldKeys } from 'dashboard/helper/preChat';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
components: {
|
||||||
|
PreChatFields,
|
||||||
|
},
|
||||||
mixins: [alertMixin],
|
mixins: [alertMixin],
|
||||||
props: {
|
props: {
|
||||||
inbox: {
|
inbox: {
|
||||||
|
@ -60,11 +92,21 @@ export default {
|
||||||
return {
|
return {
|
||||||
preChatFormEnabled: false,
|
preChatFormEnabled: false,
|
||||||
preChatMessage: '',
|
preChatMessage: '',
|
||||||
preChatFieldOptions: [],
|
preChatFields: [],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({ uiFlags: 'inboxes/getUIFlags' }),
|
...mapGetters({
|
||||||
|
uiFlags: 'inboxes/getUIFlags',
|
||||||
|
customAttributes: 'attributes/getAttributes',
|
||||||
|
}),
|
||||||
|
preChatFieldOptions() {
|
||||||
|
const { pre_chat_form_options: preChatFormOptions } = this.inbox;
|
||||||
|
return getPreChatFields({
|
||||||
|
preChatFormOptions,
|
||||||
|
customAttributes: this.customAttributes,
|
||||||
|
});
|
||||||
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
inbox() {
|
inbox() {
|
||||||
|
@ -76,25 +118,26 @@ export default {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
setDefaults() {
|
setDefaults() {
|
||||||
const {
|
const { pre_chat_form_enabled: preChatFormEnabled } = this.inbox;
|
||||||
pre_chat_form_enabled: preChatFormEnabled,
|
|
||||||
pre_chat_form_options: preChatFormOptions,
|
|
||||||
} = this.inbox;
|
|
||||||
this.preChatFormEnabled = preChatFormEnabled;
|
this.preChatFormEnabled = preChatFormEnabled;
|
||||||
const { pre_chat_message: preChatMessage, require_email: requireEmail } =
|
const {
|
||||||
preChatFormOptions || {};
|
pre_chat_message: preChatMessage,
|
||||||
|
pre_chat_fields: preChatFields,
|
||||||
|
} = this.preChatFieldOptions || {};
|
||||||
this.preChatMessage = preChatMessage;
|
this.preChatMessage = preChatMessage;
|
||||||
if (requireEmail) {
|
this.preChatFields = preChatFields;
|
||||||
this.preChatFieldOptions = ['requireEmail'];
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
handlePreChatFieldOptions(event) {
|
isFieldEditable(item) {
|
||||||
if (this.preChatFieldOptions.includes(event.target.value)) {
|
return !!standardFieldKeys[item.name] || !item.enabled;
|
||||||
this.preChatFieldOptions = [];
|
|
||||||
} else {
|
|
||||||
this.preChatFieldOptions = [event.target.value];
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
handlePreChatFieldOptions(event, type, item) {
|
||||||
|
this.preChatFields.forEach((field, index) => {
|
||||||
|
if (field.name === item.name) {
|
||||||
|
this.preChatFields[index][type] = !item[type];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
async updateInbox() {
|
async updateInbox() {
|
||||||
try {
|
try {
|
||||||
const payload = {
|
const payload = {
|
||||||
|
@ -104,7 +147,7 @@ export default {
|
||||||
pre_chat_form_enabled: this.preChatFormEnabled,
|
pre_chat_form_enabled: this.preChatFormEnabled,
|
||||||
pre_chat_form_options: {
|
pre_chat_form_options: {
|
||||||
pre_chat_message: this.preChatMessage,
|
pre_chat_message: this.preChatMessage,
|
||||||
require_email: this.preChatFieldOptions.includes('requireEmail'),
|
pre_chat_fields: this.preChatFields,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -117,12 +160,11 @@ export default {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<style scoped>
|
<style scoped lang="scss">
|
||||||
.settings--content {
|
.settings--content {
|
||||||
font-size: var(--font-size-default);
|
font-size: var(--font-size-default);
|
||||||
}
|
}
|
||||||
|
.pre-chat--title {
|
||||||
.prechat--title {
|
|
||||||
margin: var(--space-medium) 0 var(--space-slab);
|
margin: var(--space-medium) 0 var(--space-slab);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -16,6 +16,9 @@ export const getters = {
|
||||||
getUIFlags(_state) {
|
getUIFlags(_state) {
|
||||||
return _state.uiFlags;
|
return _state.uiFlags;
|
||||||
},
|
},
|
||||||
|
getAttributes: _state => {
|
||||||
|
return _state.records;
|
||||||
|
},
|
||||||
getAttributesByModel: _state => attributeModel => {
|
getAttributesByModel: _state => attributeModel => {
|
||||||
return _state.records.filter(
|
return _state.records.filter(
|
||||||
record => record.attribute_model === attributeModel
|
record => record.attribute_model === attributeModel
|
||||||
|
|
|
@ -2,6 +2,26 @@ import { getters } from '../../attributes';
|
||||||
import attributesList from './fixtures';
|
import attributesList from './fixtures';
|
||||||
|
|
||||||
describe('#getters', () => {
|
describe('#getters', () => {
|
||||||
|
it('getAttributes', () => {
|
||||||
|
const state = { records: attributesList };
|
||||||
|
expect(getters.getAttributes(state)).toEqual([
|
||||||
|
{
|
||||||
|
attribute_display_name: 'Language',
|
||||||
|
attribute_display_type: 1,
|
||||||
|
attribute_description: 'The conversation language',
|
||||||
|
attribute_key: 'language',
|
||||||
|
attribute_model: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
attribute_display_name: 'Language one',
|
||||||
|
attribute_display_type: 2,
|
||||||
|
attribute_description: 'The conversation language one',
|
||||||
|
attribute_key: 'language_one',
|
||||||
|
attribute_model: 1,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
it('getAttributesByModel', () => {
|
it('getAttributesByModel', () => {
|
||||||
const state = { records: attributesList };
|
const state = { records: attributesList };
|
||||||
expect(getters.getAttributesByModel(state)(1)).toEqual([
|
expect(getters.getAttributesByModel(state)(1)).toEqual([
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import Vuelidate from 'vuelidate';
|
import Vuelidate from 'vuelidate';
|
||||||
import VueI18n from 'vue-i18n';
|
import VueI18n from 'vue-i18n';
|
||||||
|
import VueFormulate from '@braid/vue-formulate';
|
||||||
import store from '../widget/store';
|
import store from '../widget/store';
|
||||||
import App from '../widget/App.vue';
|
import App from '../widget/App.vue';
|
||||||
import ActionCableConnector from '../widget/helpers/actionCable';
|
import ActionCableConnector from '../widget/helpers/actionCable';
|
||||||
import i18n from '../widget/i18n';
|
import i18n from '../widget/i18n';
|
||||||
|
import { isPhoneE164OrEmpty } from 'shared/helpers/Validators';
|
||||||
import router from '../widget/router';
|
import router from '../widget/router';
|
||||||
Vue.use(VueI18n);
|
Vue.use(VueI18n);
|
||||||
Vue.use(Vuelidate);
|
Vue.use(Vuelidate);
|
||||||
|
@ -14,7 +15,15 @@ const i18nConfig = new VueI18n({
|
||||||
locale: 'en',
|
locale: 'en',
|
||||||
messages: i18n,
|
messages: i18n,
|
||||||
});
|
});
|
||||||
|
Vue.use(VueFormulate, {
|
||||||
|
rules: {
|
||||||
|
isPhoneE164OrEmpty: ({ value }) => isPhoneE164OrEmpty(value),
|
||||||
|
},
|
||||||
|
classes: {
|
||||||
|
outer: 'mb-4 wrapper',
|
||||||
|
error: 'text-red-400 mt-2 text-xs font-medium',
|
||||||
|
},
|
||||||
|
});
|
||||||
// Event Bus
|
// Event Bus
|
||||||
window.bus = new Vue();
|
window.bus = new Vue();
|
||||||
|
|
||||||
|
|
|
@ -126,5 +126,6 @@
|
||||||
"brand-whatsapp-outline": "M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.48-8.413Z",
|
"brand-whatsapp-outline": "M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.48-8.413Z",
|
||||||
"brand-github-outline": "M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385c.6.105.825-.255.825-.57c0-.285-.015-1.23-.015-2.235c-3.015.555-3.795-.735-4.035-1.41c-.135-.345-.72-1.41-1.23-1.695c-.42-.225-1.02-.78-.015-.795c.945-.015 1.62.87 1.845 1.23c1.08 1.815 2.805 1.305 3.495.99c.105-.78.42-1.305.765-1.605c-2.67-.3-5.46-1.335-5.46-5.925c0-1.305.465-2.385 1.23-3.225c-.12-.3-.54-1.53.12-3.18c0 0 1.005-.315 3.3 1.23c.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23c.66 1.65.24 2.88.12 3.18c.765.84 1.23 1.905 1.23 3.225c0 4.605-2.805 5.625-5.475 5.925c.435.375.81 1.095.81 2.22c0 1.605-.015 2.895-.015 3.3c0 .315.225.69.825.57A12.02 12.02 0 0 0 24 12c0-6.63-5.37-12-12-12Z",
|
"brand-github-outline": "M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385c.6.105.825-.255.825-.57c0-.285-.015-1.23-.015-2.235c-3.015.555-3.795-.735-4.035-1.41c-.135-.345-.72-1.41-1.23-1.695c-.42-.225-1.02-.78-.015-.795c.945-.015 1.62.87 1.845 1.23c1.08 1.815 2.805 1.305 3.495.99c.105-.78.42-1.305.765-1.605c-2.67-.3-5.46-1.335-5.46-5.925c0-1.305.465-2.385 1.23-3.225c-.12-.3-.54-1.53.12-3.18c0 0 1.005-.315 3.3 1.23c.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23c.66 1.65.24 2.88.12 3.18c.765.84 1.23 1.905 1.23 3.225c0 4.605-2.805 5.625-5.475 5.925c.435.375.81 1.095.81 2.22c0 1.605-.015 2.895-.015 3.3c0 .315.225.69.825.57A12.02 12.02 0 0 0 24 12c0-6.63-5.37-12-12-12Z",
|
||||||
"add-solid": "M11.883 3.007 12 3a1 1 0 0 1 .993.883L13 4v7h7a1 1 0 0 1 .993.883L21 12a1 1 0 0 1-.883.993L20 13h-7v7a1 1 0 0 1-.883.993L12 21a1 1 0 0 1-.993-.883L11 20v-7H4a1 1 0 0 1-.993-.883L3 12a1 1 0 0 1 .883-.993L4 11h7V4a1 1 0 0 1 .883-.993L12 3l-.117.007Z",
|
"add-solid": "M11.883 3.007 12 3a1 1 0 0 1 .993.883L13 4v7h7a1 1 0 0 1 .993.883L21 12a1 1 0 0 1-.883.993L20 13h-7v7a1 1 0 0 1-.883.993L12 21a1 1 0 0 1-.993-.883L11 20v-7H4a1 1 0 0 1-.993-.883L3 12a1 1 0 0 1 .883-.993L4 11h7V4a1 1 0 0 1 .883-.993L12 3l-.117.007Z",
|
||||||
"subtract-solid": "M3.997 13H20a1 1 0 1 0 0-2H3.997a1 1 0 1 0 0 2Z"
|
"subtract-solid": "M3.997 13H20a1 1 0 1 0 0-2H3.997a1 1 0 1 0 0 2Z",
|
||||||
|
"drag-outline": "M15 3.707V8.5a.5.5 0 0 0 1 0V3.707l1.146 1.147a.5.5 0 0 0 .708-.708l-2-2a.499.499 0 0 0-.708 0l-2 2a.5.5 0 0 0 .708.708L15 3.707ZM2 4.5a.5.5 0 0 1 .5-.5h6a.5.5 0 0 1 0 1h-6a.5.5 0 0 1-.5-.5Zm0 5a.5.5 0 0 1 .5-.5h9a.5.5 0 0 1 0 1h-9a.5.5 0 0 1-.5-.5Zm.5 4.5a.5.5 0 0 0 0 1h6a.5.5 0 0 0 0-1h-6ZM15 16.293V11.5a.5.5 0 0 1 1 0v4.793l1.146-1.147a.5.5 0 0 1 .708.708l-2 2a.5.5 0 0 1-.708 0l-2-2a.5.5 0 0 1 .708-.708L15 16.293Z"
|
||||||
}
|
}
|
||||||
|
|
|
@ -151,10 +151,7 @@ export default {
|
||||||
},
|
},
|
||||||
registerCampaignEvents() {
|
registerCampaignEvents() {
|
||||||
bus.$on(ON_CAMPAIGN_MESSAGE_CLICK, () => {
|
bus.$on(ON_CAMPAIGN_MESSAGE_CLICK, () => {
|
||||||
const showPreChatForm =
|
if (this.shouldShowPreChatForm) {
|
||||||
this.preChatFormEnabled && this.preChatFormOptions.requireEmail;
|
|
||||||
const isUserEmailAvailable = !!this.currentUser.email;
|
|
||||||
if (showPreChatForm && !isUserEmailAvailable) {
|
|
||||||
this.replaceRoute('prechat-form');
|
this.replaceRoute('prechat-form');
|
||||||
} else {
|
} else {
|
||||||
this.replaceRoute('messages');
|
this.replaceRoute('messages');
|
||||||
|
|
|
@ -1,47 +1,44 @@
|
||||||
<template>
|
<template>
|
||||||
<form
|
<FormulateForm
|
||||||
|
v-model="formValues"
|
||||||
class="flex flex-1 flex-col p-6 overflow-y-auto"
|
class="flex flex-1 flex-col p-6 overflow-y-auto"
|
||||||
@submit.prevent="onSubmit"
|
@submit="onSubmit"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
v-if="shouldShowHeaderMessage"
|
v-if="shouldShowHeaderMessage"
|
||||||
class="text-sm leading-5"
|
class="mb-4 text-sm leading-5"
|
||||||
:class="$dm('text-black-800', 'dark:text-slate-50')"
|
:class="$dm('text-black-800', 'dark:text-slate-50')"
|
||||||
>
|
>
|
||||||
{{ headerMessage }}
|
{{ headerMessage }}
|
||||||
</div>
|
</div>
|
||||||
<form-input
|
<FormulateInput
|
||||||
v-if="areContactFieldsVisible"
|
v-for="item in enabledPreChatFields"
|
||||||
v-model="fullName"
|
:key="item.name"
|
||||||
class="mt-5"
|
:name="item.name"
|
||||||
:label="$t('PRE_CHAT_FORM.FIELDS.FULL_NAME.LABEL')"
|
:type="item.type"
|
||||||
:placeholder="$t('PRE_CHAT_FORM.FIELDS.FULL_NAME.PLACEHOLDER')"
|
:label="getLabel(item)"
|
||||||
type="text"
|
:placeholder="getPlaceHolder(item)"
|
||||||
:error="
|
:validation="getValidation(item)"
|
||||||
$v.fullName.$error ? $t('PRE_CHAT_FORM.FIELDS.FULL_NAME.ERROR') : ''
|
:options="getOptions(item)"
|
||||||
"
|
:label-class="context => labelClass(context)"
|
||||||
|
:input-class="context => inputClass(context)"
|
||||||
|
:validation-messages="{
|
||||||
|
isPhoneE164OrEmpty: $t('PRE_CHAT_FORM.FIELDS.PHONE_NUMBER.VALID_ERROR'),
|
||||||
|
email: $t('PRE_CHAT_FORM.FIELDS.EMAIL_ADDRESS.VALID_ERROR'),
|
||||||
|
required: getRequiredErrorMessage(item),
|
||||||
|
}"
|
||||||
/>
|
/>
|
||||||
<form-input
|
<FormulateInput
|
||||||
v-if="areContactFieldsVisible"
|
|
||||||
v-model="emailAddress"
|
|
||||||
class="mt-5"
|
|
||||||
:label="$t('PRE_CHAT_FORM.FIELDS.EMAIL_ADDRESS.LABEL')"
|
|
||||||
:placeholder="$t('PRE_CHAT_FORM.FIELDS.EMAIL_ADDRESS.PLACEHOLDER')"
|
|
||||||
type="email"
|
|
||||||
:error="
|
|
||||||
$v.emailAddress.$error
|
|
||||||
? $t('PRE_CHAT_FORM.FIELDS.EMAIL_ADDRESS.ERROR')
|
|
||||||
: ''
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
<form-text-area
|
|
||||||
v-if="!hasActiveCampaign"
|
v-if="!hasActiveCampaign"
|
||||||
v-model="message"
|
name="message"
|
||||||
class="my-5"
|
type="textarea"
|
||||||
|
:label-class="context => labelClass(context)"
|
||||||
|
:input-class="context => inputClass(context)"
|
||||||
:label="$t('PRE_CHAT_FORM.FIELDS.MESSAGE.LABEL')"
|
:label="$t('PRE_CHAT_FORM.FIELDS.MESSAGE.LABEL')"
|
||||||
:placeholder="$t('PRE_CHAT_FORM.FIELDS.MESSAGE.PLACEHOLDER')"
|
:placeholder="$t('PRE_CHAT_FORM.FIELDS.MESSAGE.PLACEHOLDER')"
|
||||||
:error="$v.message.$error ? $t('PRE_CHAT_FORM.FIELDS.MESSAGE.ERROR') : ''"
|
validation="required"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<custom-button
|
<custom-button
|
||||||
class="font-medium my-5"
|
class="font-medium my-5"
|
||||||
block
|
block
|
||||||
|
@ -52,24 +49,20 @@
|
||||||
<spinner v-if="isCreating" class="p-0" />
|
<spinner v-if="isCreating" class="p-0" />
|
||||||
{{ $t('START_CONVERSATION') }}
|
{{ $t('START_CONVERSATION') }}
|
||||||
</custom-button>
|
</custom-button>
|
||||||
</form>
|
</FormulateForm>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import CustomButton from 'shared/components/Button';
|
import CustomButton from 'shared/components/Button';
|
||||||
import FormInput from '../Form/Input';
|
|
||||||
import FormTextArea from '../Form/TextArea';
|
|
||||||
import Spinner from 'shared/components/Spinner';
|
import Spinner from 'shared/components/Spinner';
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import { getContrastingTextColor } from '@chatwoot/utils';
|
import { getContrastingTextColor } from '@chatwoot/utils';
|
||||||
import { required, minLength, email } from 'vuelidate/lib/validators';
|
|
||||||
import { isEmptyObject } from 'widget/helpers/utils';
|
import { isEmptyObject } from 'widget/helpers/utils';
|
||||||
import routerMixin from 'widget/mixins/routerMixin';
|
import routerMixin from 'widget/mixins/routerMixin';
|
||||||
import darkModeMixin from 'widget/mixins/darkModeMixin';
|
import darkModeMixin from 'widget/mixins/darkModeMixin';
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
FormInput,
|
|
||||||
FormTextArea,
|
|
||||||
CustomButton,
|
CustomButton,
|
||||||
Spinner,
|
Spinner,
|
||||||
},
|
},
|
||||||
|
@ -77,47 +70,23 @@ export default {
|
||||||
props: {
|
props: {
|
||||||
options: {
|
options: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => ({}),
|
default: () => {},
|
||||||
},
|
},
|
||||||
disableContactFields: {
|
disableContactFields: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
validations() {
|
|
||||||
const identityValidations = {
|
|
||||||
fullName: {
|
|
||||||
required,
|
|
||||||
},
|
|
||||||
emailAddress: {
|
|
||||||
required,
|
|
||||||
email,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const messageValidation = {
|
|
||||||
message: {
|
|
||||||
required,
|
|
||||||
minLength: minLength(1),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
// For campaign, message field is not required
|
|
||||||
if (this.hasActiveCampaign) {
|
|
||||||
return identityValidations;
|
|
||||||
}
|
|
||||||
if (this.areContactFieldsVisible) {
|
|
||||||
return {
|
|
||||||
...identityValidations,
|
|
||||||
...messageValidation,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return messageValidation;
|
|
||||||
},
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
fullName: '',
|
locale: this.$root.$i18n.locale,
|
||||||
emailAddress: '',
|
|
||||||
message: '',
|
message: '',
|
||||||
|
formValues: {},
|
||||||
|
labels: {
|
||||||
|
emailAddress: 'EMAIL_ADDRESS',
|
||||||
|
fullName: 'FULL_NAME',
|
||||||
|
phoneNumber: 'PHONE_NUMBER',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -125,6 +94,7 @@ export default {
|
||||||
widgetColor: 'appConfig/getWidgetColor',
|
widgetColor: 'appConfig/getWidgetColor',
|
||||||
isCreating: 'conversation/getIsCreating',
|
isCreating: 'conversation/getIsCreating',
|
||||||
activeCampaign: 'campaign/getActiveCampaign',
|
activeCampaign: 'campaign/getActiveCampaign',
|
||||||
|
currentUser: 'contacts/getCurrentUser',
|
||||||
}),
|
}),
|
||||||
textColor() {
|
textColor() {
|
||||||
return getContrastingTextColor(this.widgetColor);
|
return getContrastingTextColor(this.widgetColor);
|
||||||
|
@ -141,23 +111,207 @@ export default {
|
||||||
}
|
}
|
||||||
return this.options.preChatMessage;
|
return this.options.preChatMessage;
|
||||||
},
|
},
|
||||||
areContactFieldsVisible() {
|
preChatFields() {
|
||||||
return this.options.requireEmail && !this.disableContactFields;
|
return this.disableContactFields ? [] : this.options.preChatFields;
|
||||||
|
},
|
||||||
|
filteredPreChatFields() {
|
||||||
|
const isUserEmailAvailable = !!this.currentUser.email;
|
||||||
|
const isUserPhoneNumberAvailable = !!this.currentUser.phone_number;
|
||||||
|
return this.preChatFields.filter(field => {
|
||||||
|
if (
|
||||||
|
(isUserEmailAvailable && field.name === 'emailAddress') ||
|
||||||
|
(isUserPhoneNumberAvailable && field.name === 'phoneNumber')
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
enabledPreChatFields() {
|
||||||
|
return this.filteredPreChatFields
|
||||||
|
.filter(field => field.enabled)
|
||||||
|
.map(field => ({
|
||||||
|
...field,
|
||||||
|
type: this.findFieldType(field.type),
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
conversationCustomAttributes() {
|
||||||
|
let conversationAttributes = {};
|
||||||
|
this.enabledPreChatFields.forEach(field => {
|
||||||
|
if (field.field_type === 'conversation_attribute') {
|
||||||
|
conversationAttributes = {
|
||||||
|
...conversationAttributes,
|
||||||
|
[field.name]: this.getValue(field),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return conversationAttributes;
|
||||||
|
},
|
||||||
|
contactCustomAttributes() {
|
||||||
|
let contactAttributes = {};
|
||||||
|
this.enabledPreChatFields.forEach(field => {
|
||||||
|
if (field.field_type === 'contact_attribute') {
|
||||||
|
contactAttributes = {
|
||||||
|
...contactAttributes,
|
||||||
|
[field.name]: this.getValue(field),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return contactAttributes;
|
||||||
|
},
|
||||||
|
inputStyles() {
|
||||||
|
return `mt-2 border rounded w-full py-2 px-3 text-slate-700 outline-none`;
|
||||||
|
},
|
||||||
|
isInputDarkOrLightMode() {
|
||||||
|
return `${this.$dm('bg-white', 'dark:bg-slate-600')} ${this.$dm(
|
||||||
|
'text-slate-700',
|
||||||
|
'dark:text-slate-50'
|
||||||
|
)}`;
|
||||||
|
},
|
||||||
|
inputBorderColor() {
|
||||||
|
return `${this.$dm('border-black-200', 'dark:border-black-500')}`;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
onSubmit() {
|
labelClass(context) {
|
||||||
this.$v.$touch();
|
const { hasErrors } = context;
|
||||||
if (this.$v.$invalid) {
|
if (!hasErrors) {
|
||||||
return;
|
return `text-xs font-medium ${this.$dm(
|
||||||
|
'text-black-800',
|
||||||
|
'dark:text-slate-50'
|
||||||
|
)}`;
|
||||||
}
|
}
|
||||||
|
return `text-xs font-medium ${this.$dm(
|
||||||
|
'text-red-400',
|
||||||
|
'dark:text-red-400'
|
||||||
|
)}`;
|
||||||
|
},
|
||||||
|
inputClass(context) {
|
||||||
|
const { hasErrors, classification, type } = context;
|
||||||
|
if (classification === 'box' && type === 'checkbox') {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
if (!hasErrors) {
|
||||||
|
return `${this.inputStyles} hover:border-black-300 focus:border-black-300 ${this.isInputDarkOrLightMode} ${this.inputBorderColor}`;
|
||||||
|
}
|
||||||
|
return `${this.inputStyles} border-red-200 hover:border-red-300 focus:border-red-300 ${this.isInputDarkOrLightMode}`;
|
||||||
|
},
|
||||||
|
isContactFieldRequired(field) {
|
||||||
|
return this.preChatFields.find(option => option.name === field).required;
|
||||||
|
},
|
||||||
|
getLabel({ name, label }) {
|
||||||
|
if (this.labels[name])
|
||||||
|
return this.$t(`PRE_CHAT_FORM.FIELDS.${this.labels[name]}.LABEL`);
|
||||||
|
return label;
|
||||||
|
},
|
||||||
|
getPlaceHolder({ name, placeholder }) {
|
||||||
|
if (this.labels[name])
|
||||||
|
return this.$t(`PRE_CHAT_FORM.FIELDS.${this.labels[name]}.PLACEHOLDER`);
|
||||||
|
return placeholder;
|
||||||
|
},
|
||||||
|
getValue({ name, type }) {
|
||||||
|
if (type === 'select') {
|
||||||
|
return this.enabledPreChatFields.find(option => option.name === name)
|
||||||
|
.values[this.formValues[name]];
|
||||||
|
}
|
||||||
|
return this.formValues[name] || null;
|
||||||
|
},
|
||||||
|
|
||||||
|
getRequiredErrorMessage({ name, label }) {
|
||||||
|
if (this.labels[name])
|
||||||
|
return this.$t(
|
||||||
|
`PRE_CHAT_FORM.FIELDS.${this.labels[name]}.REQUIRED_ERROR`
|
||||||
|
);
|
||||||
|
return `${label} ${this.$t('PRE_CHAT_FORM.IS_REQUIRED')}`;
|
||||||
|
},
|
||||||
|
getValidation({ type, name }) {
|
||||||
|
if (!this.isContactFieldRequired(name)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
const validations = {
|
||||||
|
emailAddress: 'email',
|
||||||
|
phoneNumber: 'isPhoneE164OrEmpty',
|
||||||
|
url: 'url',
|
||||||
|
date: 'date',
|
||||||
|
text: null,
|
||||||
|
select: null,
|
||||||
|
number: null,
|
||||||
|
};
|
||||||
|
const validationKeys = Object.keys(validations);
|
||||||
|
const validation = 'bail|required';
|
||||||
|
if (validationKeys.includes(name) || validationKeys.includes(type)) {
|
||||||
|
const validationType = validations[type] || validations[name];
|
||||||
|
return validationType ? `${validation}|${validationType}` : validation;
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
},
|
||||||
|
findFieldType(type) {
|
||||||
|
if (type === 'link') {
|
||||||
|
return 'url';
|
||||||
|
}
|
||||||
|
if (type === 'list') {
|
||||||
|
return 'select';
|
||||||
|
}
|
||||||
|
|
||||||
|
return type;
|
||||||
|
},
|
||||||
|
getOptions(item) {
|
||||||
|
if (item.type === 'select') {
|
||||||
|
let values = {};
|
||||||
|
item.values.forEach((value, index) => {
|
||||||
|
values = {
|
||||||
|
...values,
|
||||||
|
[index]: value,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
onSubmit() {
|
||||||
|
const { emailAddress, fullName, phoneNumber, message } = this.formValues;
|
||||||
|
const { email } = this.currentUser;
|
||||||
this.$emit('submit', {
|
this.$emit('submit', {
|
||||||
fullName: this.fullName,
|
fullName,
|
||||||
emailAddress: this.emailAddress,
|
phoneNumber,
|
||||||
message: this.message,
|
emailAddress: emailAddress || email,
|
||||||
|
message,
|
||||||
activeCampaignId: this.activeCampaign.id,
|
activeCampaignId: this.activeCampaign.id,
|
||||||
|
conversationCustomAttributes: this.conversationCustomAttributes,
|
||||||
|
contactCustomAttributes: this.contactCustomAttributes,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
::v-deep {
|
||||||
|
.wrapper[data-type='checkbox'] {
|
||||||
|
.formulate-input-wrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
label {
|
||||||
|
margin-left: 0.2rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
.wrapper {
|
||||||
|
.formulate-input-element--date,
|
||||||
|
.formulate-input-element--checkbox {
|
||||||
|
input {
|
||||||
|
color-scheme: dark;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.wrapper[data-type='textarea'] {
|
||||||
|
.formulate-input-element--textarea {
|
||||||
|
textarea {
|
||||||
|
min-height: 8rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -44,12 +44,19 @@
|
||||||
"FULL_NAME": {
|
"FULL_NAME": {
|
||||||
"LABEL": "Full Name",
|
"LABEL": "Full Name",
|
||||||
"PLACEHOLDER": "Please enter your full name",
|
"PLACEHOLDER": "Please enter your full name",
|
||||||
"ERROR": "Full Name is required"
|
"REQUIRED_ERROR": "Full Name is required"
|
||||||
},
|
},
|
||||||
"EMAIL_ADDRESS": {
|
"EMAIL_ADDRESS": {
|
||||||
"LABEL": "Email Address",
|
"LABEL": "Email Address",
|
||||||
"PLACEHOLDER": "Please enter your email address",
|
"PLACEHOLDER": "Please enter your email address",
|
||||||
"ERROR": "Invalid email address"
|
"REQUIRED_ERROR": "Email Address is required",
|
||||||
|
"VALID_ERROR": "Please enter a valid email address"
|
||||||
|
},
|
||||||
|
"PHONE_NUMBER": {
|
||||||
|
"LABEL": "Phone Number",
|
||||||
|
"PLACEHOLDER": "Please enter your phone number",
|
||||||
|
"REQUIRED_ERROR": "Phone Number is required",
|
||||||
|
"VALID_ERROR": "Phone number should be of E.164 format eg: +1415555555"
|
||||||
},
|
},
|
||||||
"MESSAGE": {
|
"MESSAGE": {
|
||||||
"LABEL": "Message",
|
"LABEL": "Message",
|
||||||
|
@ -57,7 +64,8 @@
|
||||||
"ERROR": "Message too short"
|
"ERROR": "Message too short"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"CAMPAIGN_HEADER": "Please provide your name and email before starting the conversation"
|
"CAMPAIGN_HEADER": "Please provide your name and email before starting the conversation",
|
||||||
|
"IS_REQUIRED": "is required"
|
||||||
},
|
},
|
||||||
"FILE_SIZE_LIMIT": "File exceeds the {MAXIMUM_FILE_UPLOAD_SIZE} attachment limit",
|
"FILE_SIZE_LIMIT": "File exceeds the {MAXIMUM_FILE_UPLOAD_SIZE} attachment limit",
|
||||||
"CHAT_FORM": {
|
"CHAT_FORM": {
|
||||||
|
|
|
@ -25,15 +25,21 @@ export default {
|
||||||
return window.chatwootWebChannel.preChatFormEnabled;
|
return window.chatwootWebChannel.preChatFormEnabled;
|
||||||
},
|
},
|
||||||
preChatFormOptions() {
|
preChatFormOptions() {
|
||||||
let requireEmail = false;
|
|
||||||
let preChatMessage = '';
|
let preChatMessage = '';
|
||||||
const options = window.chatwootWebChannel.preChatFormOptions || {};
|
const options = window.chatwootWebChannel.preChatFormOptions || {};
|
||||||
requireEmail = options.require_email;
|
|
||||||
preChatMessage = options.pre_chat_message;
|
preChatMessage = options.pre_chat_message;
|
||||||
|
const { pre_chat_fields: preChatFields = [] } = options;
|
||||||
return {
|
return {
|
||||||
requireEmail,
|
|
||||||
preChatMessage,
|
preChatMessage,
|
||||||
|
preChatFields,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
shouldShowPreChatForm() {
|
||||||
|
const { preChatFields } = this.preChatFormOptions;
|
||||||
|
// Check if at least one enabled field in pre-chat fields
|
||||||
|
const hasEnabledFields =
|
||||||
|
preChatFields.filter(field => field.enabled).length > 0;
|
||||||
|
return this.preChatFormEnabled && hasEnabledFields;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,12 +1,30 @@
|
||||||
import { createWrapper } from '@vue/test-utils';
|
import { createWrapper } from '@vue/test-utils';
|
||||||
import configMixin from '../configMixin';
|
import configMixin from '../configMixin';
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
|
const preChatFields = [
|
||||||
|
{
|
||||||
|
label: 'Email Id',
|
||||||
|
name: 'emailAddress',
|
||||||
|
type: 'email',
|
||||||
|
field_type: 'standard',
|
||||||
|
required: false,
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Full name',
|
||||||
|
name: 'fullName',
|
||||||
|
type: 'text',
|
||||||
|
field_type: 'standard',
|
||||||
|
required: true,
|
||||||
|
enabled: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
global.chatwootWebChannel = {
|
global.chatwootWebChannel = {
|
||||||
avatarUrl: 'https://test.url',
|
avatarUrl: 'https://test.url',
|
||||||
hasAConnectedAgentBot: 'AgentBot',
|
hasAConnectedAgentBot: 'AgentBot',
|
||||||
enabledFeatures: ['emoji_picker', 'attachments', 'end_conversation'],
|
enabledFeatures: ['emoji_picker', 'attachments', 'end_conversation'],
|
||||||
preChatFormOptions: { require_email: false, pre_chat_message: '' },
|
preChatFormOptions: { pre_chat_fields: preChatFields, pre_chat_message: '' },
|
||||||
|
preChatFormEnabled: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
global.chatwootWidgetDefaults = {
|
global.chatwootWidgetDefaults = {
|
||||||
|
@ -29,18 +47,22 @@ describe('configMixin', () => {
|
||||||
expect(wrapper.vm.hasAConnectedAgentBot).toBe(true);
|
expect(wrapper.vm.hasAConnectedAgentBot).toBe(true);
|
||||||
expect(wrapper.vm.useInboxAvatarForBot).toBe(true);
|
expect(wrapper.vm.useInboxAvatarForBot).toBe(true);
|
||||||
expect(wrapper.vm.inboxAvatarUrl).toBe('https://test.url');
|
expect(wrapper.vm.inboxAvatarUrl).toBe('https://test.url');
|
||||||
|
|
||||||
expect(wrapper.vm.channelConfig).toEqual({
|
expect(wrapper.vm.channelConfig).toEqual({
|
||||||
avatarUrl: 'https://test.url',
|
avatarUrl: 'https://test.url',
|
||||||
hasAConnectedAgentBot: 'AgentBot',
|
hasAConnectedAgentBot: 'AgentBot',
|
||||||
enabledFeatures: ['emoji_picker', 'attachments', 'end_conversation'],
|
enabledFeatures: ['emoji_picker', 'attachments', 'end_conversation'],
|
||||||
preChatFormOptions: {
|
preChatFormOptions: {
|
||||||
pre_chat_message: '',
|
pre_chat_message: '',
|
||||||
require_email: false,
|
pre_chat_fields: preChatFields,
|
||||||
},
|
},
|
||||||
|
preChatFormEnabled: true,
|
||||||
});
|
});
|
||||||
expect(wrapper.vm.preChatFormOptions).toEqual({
|
expect(wrapper.vm.preChatFormOptions).toEqual({
|
||||||
requireEmail: false,
|
|
||||||
preChatMessage: '',
|
preChatMessage: '',
|
||||||
|
preChatFields: preChatFields,
|
||||||
});
|
});
|
||||||
|
expect(wrapper.vm.preChatFormEnabled).toEqual(true);
|
||||||
|
expect(wrapper.vm.shouldShowPreChatForm).toEqual(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="flex flex-1 flex-col justify-end">
|
<div class="flex flex-1 flex-col justify-end">
|
||||||
<div class="flex flex-1 overflow-auto">
|
<div class="flex flex-1 overflow-auto">
|
||||||
<!-- Load Converstion List Components Here -->
|
<!-- Load Conversation List Components Here -->
|
||||||
</div>
|
</div>
|
||||||
<team-availability
|
<team-availability
|
||||||
:available-agents="availableAgents"
|
:available-agents="availableAgents"
|
||||||
|
@ -40,16 +40,12 @@ export default {
|
||||||
availableAgents: 'agent/availableAgents',
|
availableAgents: 'agent/availableAgents',
|
||||||
activeCampaign: 'campaign/getActiveCampaign',
|
activeCampaign: 'campaign/getActiveCampaign',
|
||||||
conversationSize: 'conversation/getConversationSize',
|
conversationSize: 'conversation/getConversationSize',
|
||||||
currentUser: 'contacts/getCurrentUser',
|
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
startConversation() {
|
startConversation() {
|
||||||
const isUserEmailAvailable = !!this.currentUser.email;
|
|
||||||
if (this.preChatFormEnabled && !this.conversationSize) {
|
if (this.preChatFormEnabled && !this.conversationSize) {
|
||||||
return this.replaceRoute('prechat-form', {
|
return this.replaceRoute('prechat-form');
|
||||||
disableContactFields: isUserEmailAvailable,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
return this.replaceRoute('messages');
|
return this.replaceRoute('messages');
|
||||||
},
|
},
|
||||||
|
|
|
@ -12,6 +12,7 @@ import { mapGetters } from 'vuex';
|
||||||
import PreChatForm from '../components/PreChat/Form';
|
import PreChatForm from '../components/PreChat/Form';
|
||||||
import configMixin from '../mixins/configMixin';
|
import configMixin from '../mixins/configMixin';
|
||||||
import routerMixin from '../mixins/routerMixin';
|
import routerMixin from '../mixins/routerMixin';
|
||||||
|
import { isEmptyObject } from 'widget/helpers/utils';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
@ -35,13 +36,22 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
onSubmit({ fullName, emailAddress, message, activeCampaignId }) {
|
onSubmit({
|
||||||
|
fullName,
|
||||||
|
emailAddress,
|
||||||
|
message,
|
||||||
|
activeCampaignId,
|
||||||
|
phoneNumber,
|
||||||
|
contactCustomAttributes,
|
||||||
|
conversationCustomAttributes,
|
||||||
|
}) {
|
||||||
if (activeCampaignId) {
|
if (activeCampaignId) {
|
||||||
bus.$emit('execute-campaign', activeCampaignId);
|
bus.$emit('execute-campaign', activeCampaignId);
|
||||||
this.$store.dispatch('contacts/update', {
|
this.$store.dispatch('contacts/update', {
|
||||||
user: {
|
user: {
|
||||||
email: emailAddress,
|
email: emailAddress,
|
||||||
name: fullName,
|
name: fullName,
|
||||||
|
phone_number: phoneNumber,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
@ -49,8 +59,16 @@ export default {
|
||||||
fullName: fullName,
|
fullName: fullName,
|
||||||
emailAddress: emailAddress,
|
emailAddress: emailAddress,
|
||||||
message: message,
|
message: message,
|
||||||
|
phoneNumber: phoneNumber,
|
||||||
|
customAttributes: conversationCustomAttributes,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (!isEmptyObject(contactCustomAttributes)) {
|
||||||
|
this.$store.dispatch(
|
||||||
|
'contacts/setCustomAttributes',
|
||||||
|
contactCustomAttributes
|
||||||
|
);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -32,9 +32,13 @@ class Channel::WebWidget < ApplicationRecord
|
||||||
self.table_name = 'channel_web_widgets'
|
self.table_name = 'channel_web_widgets'
|
||||||
EDITABLE_ATTRS = [:website_url, :widget_color, :welcome_title, :welcome_tagline, :reply_time, :pre_chat_form_enabled,
|
EDITABLE_ATTRS = [:website_url, :widget_color, :welcome_title, :welcome_tagline, :reply_time, :pre_chat_form_enabled,
|
||||||
:continuity_via_email, :hmac_mandatory,
|
:continuity_via_email, :hmac_mandatory,
|
||||||
{ pre_chat_form_options: [:pre_chat_message, :require_email] },
|
{ pre_chat_form_options: [:pre_chat_message, :require_email,
|
||||||
|
{ pre_chat_fields:
|
||||||
|
[:field_type, :label, :placeholder, :name, :enabled, :type, :enabled, :required,
|
||||||
|
:locale, { values: [] }] }] },
|
||||||
{ selected_feature_flags: [] }].freeze
|
{ selected_feature_flags: [] }].freeze
|
||||||
|
|
||||||
|
before_validation :validate_pre_chat_options
|
||||||
validates :website_url, presence: true
|
validates :website_url, presence: true
|
||||||
validates :widget_color, presence: true
|
validates :widget_color, presence: true
|
||||||
|
|
||||||
|
@ -74,6 +78,25 @@ class Channel::WebWidget < ApplicationRecord
|
||||||
"
|
"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def validate_pre_chat_options
|
||||||
|
return if pre_chat_form_options.with_indifferent_access['pre_chat_fields'].present?
|
||||||
|
|
||||||
|
self.pre_chat_form_options = {
|
||||||
|
pre_chat_message: 'Share your queries or comments here.',
|
||||||
|
pre_chat_fields: [
|
||||||
|
{
|
||||||
|
'field_type': 'standard', 'label': 'Email Id', 'name': 'emailAddress', 'type': 'email', 'required': true, 'enabled': false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'field_type': 'standard', 'label': 'Full name', 'name': 'fullName', 'type': 'text', 'required': false, 'enabled': false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'field_type': 'standard', 'label': 'Phone number', 'name': 'phoneNumber', 'type': 'text', 'required': false, 'enabled': false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
def create_contact_inbox(additional_attributes = {})
|
def create_contact_inbox(additional_attributes = {})
|
||||||
ActiveRecord::Base.transaction do
|
ActiveRecord::Base.transaction do
|
||||||
contact = inbox.account.contacts.create!(
|
contact = inbox.account.contacts.create!(
|
||||||
|
|
|
@ -39,6 +39,6 @@ class CustomAttributeDefinition < ApplicationRecord
|
||||||
private
|
private
|
||||||
|
|
||||||
def sync_widget_pre_chat_custom_fields
|
def sync_widget_pre_chat_custom_fields
|
||||||
::Inboxes::SyncWidgetPreChatCustomFieldsJob.perform_later(attribute_key)
|
::Inboxes::SyncWidgetPreChatCustomFieldsJob.perform_now(account, attribute_key)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
json.id @contact.id
|
json.id @contact.id
|
||||||
json.name @contact.name
|
json.name @contact.name
|
||||||
json.email @contact.email
|
json.email @contact.email
|
||||||
|
json.phone_number @contact.phone_number
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
json.id @contact.id
|
json.id @contact.id
|
||||||
json.name @contact.name
|
json.name @contact.name
|
||||||
json.email @contact.email
|
json.email @contact.email
|
||||||
|
json.phone_number @contact.phone_number
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
class AddCustomFieldsToPreChatForm < ActiveRecord::Migration[6.1]
|
||||||
|
def change
|
||||||
|
Channel::WebWidget.find_in_batches do |channels_batch|
|
||||||
|
channels_batch.each do |channel|
|
||||||
|
pre_chat_message = channel[:pre_chat_form_options]['pre_chat_message'] || 'Share your queries or comments here.'
|
||||||
|
pre_chat_fields = pre_chat_fields?(channel)
|
||||||
|
channel[:pre_chat_form_options] = {
|
||||||
|
'pre_chat_message': pre_chat_message,
|
||||||
|
'pre_chat_fields': pre_chat_fields
|
||||||
|
}
|
||||||
|
channel.save!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def pre_chat_fields?(channel)
|
||||||
|
email_enabled = channel[:pre_chat_form_options]['require_email'] || false
|
||||||
|
[
|
||||||
|
{
|
||||||
|
'field_type': 'standard', 'label': 'Email Id', 'name': 'emailAddress', 'type': 'email', 'required': true, 'enabled': email_enabled
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'field_type': 'standard', 'label': 'Full name', 'name': 'fullName', 'type': 'text', 'required': false, 'enabled': false
|
||||||
|
}, {
|
||||||
|
'field_type': 'standard', 'label': 'Phone number', 'name': 'phoneNumber', 'type': 'text', 'required': false, 'enabled': false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
end
|
||||||
|
end
|
15
spec/models/channel/web_widget_spec.rb
Normal file
15
spec/models/channel/web_widget_spec.rb
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe Channel::WebWidget do
|
||||||
|
context 'when
|
||||||
|
web widget channel' do
|
||||||
|
let!(:channel_widget) { create(:channel_widget) }
|
||||||
|
|
||||||
|
it 'pre chat options' do
|
||||||
|
expect(channel_widget.pre_chat_form_options['pre_chat_message']).to eq 'Share your queries or comments here.'
|
||||||
|
expect(channel_widget.pre_chat_form_options['pre_chat_fields'].length).to eq 3
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,7 +0,0 @@
|
||||||
require 'test_helper'
|
|
||||||
|
|
||||||
class Channel::WidgetTest < ActiveSupport::TestCase
|
|
||||||
# test "the truth" do
|
|
||||||
# assert true
|
|
||||||
# end
|
|
||||||
end
|
|
Loading…
Add table
Add a link
Reference in a new issue