feat: Disable attachments and emoji picker in the web widget (#1102)
Signed-off-by: Pranav Raj Sreepuram <pranavrajs@gmail.com>
This commit is contained in:
parent
3b23aa7913
commit
db877453a4
23 changed files with 422 additions and 141 deletions
|
@ -4,7 +4,7 @@ class Api::V1::Accounts::InboxesController < Api::V1::Accounts::BaseController
|
|||
before_action :check_authorization
|
||||
|
||||
def index
|
||||
@inboxes = policy_scope(Current.account.inboxes.includes(:channel, :avatar_attachment))
|
||||
@inboxes = policy_scope(Current.account.inboxes.order_by_id.includes(:channel, :avatar_attachment))
|
||||
end
|
||||
|
||||
def create
|
||||
|
@ -23,7 +23,10 @@ class Api::V1::Accounts::InboxesController < Api::V1::Accounts::BaseController
|
|||
|
||||
def update
|
||||
@inbox.update(inbox_update_params.except(:channel))
|
||||
@inbox.channel.update!(inbox_update_params[:channel]) if @inbox.channel.is_a?(Channel::WebWidget) && inbox_update_params[:channel].present?
|
||||
return unless @inbox.channel.is_a?(Channel::WebWidget) && inbox_update_params[:channel].present?
|
||||
|
||||
@inbox.channel.update!(inbox_update_params[:channel])
|
||||
update_channel_feature_flags
|
||||
end
|
||||
|
||||
def set_agent_bot
|
||||
|
@ -67,6 +70,13 @@ class Api::V1::Accounts::InboxesController < Api::V1::Accounts::BaseController
|
|||
end
|
||||
end
|
||||
|
||||
def update_channel_feature_flags
|
||||
return unless inbox_update_params[:channel].key? :selected_feature_flags
|
||||
|
||||
@inbox.channel.selected_feature_flags = inbox_update_params[:channel][:selected_feature_flags]
|
||||
@inbox.channel.save!
|
||||
end
|
||||
|
||||
def permitted_params
|
||||
params.permit(:id, :avatar, :name, :greeting_message, :greeting_enabled, channel:
|
||||
[:type, :website_url, :widget_color, :welcome_title, :welcome_tagline, :webhook_url, :email])
|
||||
|
@ -74,6 +84,14 @@ class Api::V1::Accounts::InboxesController < Api::V1::Accounts::BaseController
|
|||
|
||||
def inbox_update_params
|
||||
params.permit(:enable_auto_assignment, :name, :avatar, :greeting_message, :greeting_enabled,
|
||||
channel: [:website_url, :widget_color, :welcome_title, :welcome_tagline, :webhook_url, :email])
|
||||
channel: [
|
||||
:website_url,
|
||||
:widget_color,
|
||||
:welcome_title,
|
||||
:welcome_tagline,
|
||||
:webhook_url,
|
||||
:email,
|
||||
selected_feature_flags: []
|
||||
])
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.app-wrapper {
|
||||
|
@ -26,36 +26,40 @@ body {
|
|||
|
||||
.view-box {
|
||||
@include full-height;
|
||||
height: 100vh;
|
||||
@include margin(0);
|
||||
@include space-between-column;
|
||||
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.view-panel {
|
||||
flex-direction: column;
|
||||
@include margin($zero);
|
||||
@include padding($space-normal);
|
||||
|
||||
flex-direction: column;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.content-box {
|
||||
overflow: auto;
|
||||
@include padding($space-normal);
|
||||
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.back-button {
|
||||
@include flex;
|
||||
|
||||
align-items: center;
|
||||
color: $color-woot;
|
||||
cursor: pointer;
|
||||
font-size: $font-size-default;
|
||||
font-weight: $font-weight-normal;
|
||||
margin-right: $space-normal;
|
||||
cursor: pointer;
|
||||
|
||||
&:before {
|
||||
vertical-align: text-bottom;
|
||||
margin-right: $space-smaller;
|
||||
&::before {
|
||||
font-size: $font-size-large;
|
||||
margin-right: $space-small;
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -66,12 +70,14 @@ body {
|
|||
.no-items-error-message {
|
||||
@include flex;
|
||||
@include full-height;
|
||||
justify-content: center;
|
||||
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
|
||||
img {
|
||||
max-width: $space-mega;
|
||||
@include padding($space-one);
|
||||
|
||||
max-width: $space-mega;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -202,7 +202,7 @@
|
|||
}
|
||||
|
||||
.settings--content {
|
||||
@include margin($space-small $space-larger);
|
||||
@include margin($space-small $space-large);
|
||||
|
||||
.title {
|
||||
font-weight: $font-weight-medium;
|
||||
|
|
|
@ -7,15 +7,25 @@
|
|||
<p v-if="headerContent" class="small-12 column">
|
||||
{{ headerContent }}
|
||||
</p>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
headerTitle: String,
|
||||
headerContent: String,
|
||||
headerImage: String,
|
||||
headerTitle: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
headerContent: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
headerImage: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -57,15 +57,8 @@ import { mapGetters } from 'vuex';
|
|||
|
||||
import router from '../../routes';
|
||||
import adminMixin from '../../mixins/isAdmin';
|
||||
import { INBOX_TYPES } from 'shared/mixins/inboxMixin';
|
||||
|
||||
const INBOX_TYPES = {
|
||||
WEB: 'Channel::WebWidget',
|
||||
FB: 'Channel::FacebookPage',
|
||||
TWITTER: 'Channel::TwitterProfile',
|
||||
TWILIO: 'Channel::TwilioSms',
|
||||
API: 'Channel::Api',
|
||||
EMAIL: 'Channel::Email',
|
||||
};
|
||||
const getInboxClassByType = type => {
|
||||
switch (type) {
|
||||
case INBOX_TYPES.WEB:
|
||||
|
|
|
@ -1,43 +1,43 @@
|
|||
<template>
|
||||
<div
|
||||
class="small-3 columns channel"
|
||||
:class="{ inactive: !isActive(channel) }"
|
||||
:class="{ inactive: !isActive }"
|
||||
@click="onItemClick"
|
||||
>
|
||||
<img
|
||||
v-if="channel === 'facebook'"
|
||||
v-if="channel.key === 'facebook'"
|
||||
src="~dashboard/assets/images/channels/facebook.png"
|
||||
/>
|
||||
<img
|
||||
v-if="channel === 'twitter'"
|
||||
v-if="channel.key === 'twitter'"
|
||||
src="~dashboard/assets/images/channels/twitter.png"
|
||||
/>
|
||||
<img
|
||||
v-if="channel === 'telegram'"
|
||||
v-if="channel.key === 'telegram'"
|
||||
src="~dashboard/assets/images/channels/telegram.png"
|
||||
/>
|
||||
<img
|
||||
v-if="channel === 'api'"
|
||||
v-if="channel.key === 'api'"
|
||||
src="~dashboard/assets/images/channels/api.png"
|
||||
/>
|
||||
<img
|
||||
v-if="channel === 'email'"
|
||||
v-if="channel.key === 'email'"
|
||||
src="~dashboard/assets/images/channels/email.png"
|
||||
/>
|
||||
<img
|
||||
v-if="channel === 'line'"
|
||||
v-if="channel.key === 'line'"
|
||||
src="~dashboard/assets/images/channels/line.png"
|
||||
/>
|
||||
<img
|
||||
v-if="channel === 'website'"
|
||||
v-if="channel.key === 'website'"
|
||||
src="~dashboard/assets/images/channels/website.png"
|
||||
/>
|
||||
<img
|
||||
v-if="channel === 'twilio'"
|
||||
v-if="channel.key === 'twilio'"
|
||||
src="~dashboard/assets/images/channels/twilio.png"
|
||||
/>
|
||||
<h3 class="channel__title">
|
||||
{{ channel }}
|
||||
{{ channel.name }}
|
||||
</h3>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -45,7 +45,7 @@
|
|||
export default {
|
||||
props: {
|
||||
channel: {
|
||||
type: String,
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
enabledFeatures: {
|
||||
|
@ -53,25 +53,28 @@ export default {
|
|||
required: true,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
isActive(channel) {
|
||||
computed: {
|
||||
isActive() {
|
||||
const { key } = this.channel;
|
||||
if (Object.keys(this.enabledFeatures) === 0) {
|
||||
return false;
|
||||
}
|
||||
if (channel === 'facebook') {
|
||||
if (key === 'facebook') {
|
||||
return this.enabledFeatures.channel_facebook;
|
||||
}
|
||||
if (channel === 'twitter') {
|
||||
if (key === 'twitter') {
|
||||
return this.enabledFeatures.channel_twitter;
|
||||
}
|
||||
if (channel === 'email') {
|
||||
if (key === 'email') {
|
||||
return this.enabledFeatures.channel_email;
|
||||
}
|
||||
return ['website', 'twilio', 'api'].includes(channel);
|
||||
return ['website', 'twilio', 'api'].includes(key);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onItemClick() {
|
||||
if (this.isActive(this.channel)) {
|
||||
this.$emit('channel-item-click', this.channel);
|
||||
if (this.isActive) {
|
||||
this.$emit('channel-item-click', this.channel.key);
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
|
@ -96,6 +96,7 @@ import {
|
|||
hasPressedShift,
|
||||
} from 'shared/helpers/KeyboardHelpers';
|
||||
import { MESSAGE_MAX_LENGTH } from 'shared/helpers/MessageTypeHelper';
|
||||
import inboxMixin from 'shared/mixins/inboxMixin';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
@ -104,7 +105,7 @@ export default {
|
|||
FileUpload,
|
||||
ResizableTextArea,
|
||||
},
|
||||
mixins: [clickaway],
|
||||
mixins: [clickaway, inboxMixin],
|
||||
data() {
|
||||
return {
|
||||
message: '',
|
||||
|
@ -148,9 +149,6 @@ export default {
|
|||
this.message.length > this.maxLength
|
||||
);
|
||||
},
|
||||
channelType() {
|
||||
return this.inbox.channel_type;
|
||||
},
|
||||
conversationType() {
|
||||
const { additional_attributes: additionalAttributes } = this.currentChat;
|
||||
const type = additionalAttributes ? additionalAttributes.type : '';
|
||||
|
@ -174,29 +172,6 @@ export default {
|
|||
}
|
||||
return MESSAGE_MAX_LENGTH.GENERAL;
|
||||
},
|
||||
isATwitterInbox() {
|
||||
return this.channelType === 'Channel::TwitterProfile';
|
||||
},
|
||||
isAFacebookInbox() {
|
||||
return this.channelType === 'Channel::FacebookPage';
|
||||
},
|
||||
isAWebWidgetInbox() {
|
||||
return this.channelType === 'Channel::WebWidget';
|
||||
},
|
||||
isATwilioSMSChannel() {
|
||||
const { phone_number: phoneNumber = '' } = this.inbox;
|
||||
return (
|
||||
this.channelType === 'Channel::TwilioSms' &&
|
||||
!phoneNumber.startsWith('whatsapp')
|
||||
);
|
||||
},
|
||||
isATwilioWhatsappChannel() {
|
||||
const { phone_number: phoneNumber = '' } = this.inbox;
|
||||
return (
|
||||
this.channelType === 'Channel::TwilioSms' &&
|
||||
phoneNumber.startsWith('whatsapp')
|
||||
);
|
||||
},
|
||||
showFileUpload() {
|
||||
return (
|
||||
this.isAWebWidgetInbox ||
|
||||
|
|
|
@ -117,16 +117,16 @@
|
|||
},
|
||||
"API_CHANNEL": {
|
||||
"TITLE": "API Channel",
|
||||
"DESC": "Integrate with API channel and start supporting your customers via chatwoot.",
|
||||
"DESC": "Integrate with API channel and start supporting your customers.",
|
||||
"CHANNEL_NAME": {
|
||||
"LABEL": "Channel Name",
|
||||
"PLACEHOLDER": "Please enter a channel name",
|
||||
"ERROR": "This field is required"
|
||||
},
|
||||
"WEBHOOK_URL": {
|
||||
"LABEL": "Webhook Url",
|
||||
"SUBTITLE": "Configure the url where you want to recieve callbacks from chatwoot on events.",
|
||||
"PLACEHOLDER": "Webhook Url"
|
||||
"LABEL": "Webhook URL",
|
||||
"SUBTITLE": "Configure the URL where you want to recieve callbacks on events.",
|
||||
"PLACEHOLDER": "Webhook URL"
|
||||
},
|
||||
"SUBMIT_BUTTON": "Create API Channel",
|
||||
"API": {
|
||||
|
@ -135,7 +135,7 @@
|
|||
},
|
||||
"EMAIL_CHANNEL": {
|
||||
"TITLE": "Email Channel",
|
||||
"DESC": "Integrate you email inbox with chatwoot.",
|
||||
"DESC": "Integrate you email inbox.",
|
||||
"CHANNEL_NAME": {
|
||||
"LABEL": "Channel Name",
|
||||
"PLACEHOLDER": "Please enter a channel name",
|
||||
|
@ -212,7 +212,17 @@
|
|||
"ERROR_MESSAGE": "Could not delete inbox. Please try again later."
|
||||
}
|
||||
},
|
||||
"TABS": {
|
||||
"SETTINGS": "Settings",
|
||||
"COLLABORATORS": "Collaborators",
|
||||
"CONFIGURATION": "Configuration"
|
||||
},
|
||||
"SETTINGS": "Settings",
|
||||
"FEATURES": {
|
||||
"LABEL": "Features",
|
||||
"DISPLAY_FILE_PICKER": "Display file picker on the widget",
|
||||
"DISPLAY_EMOJI_PICKER": "Display emoji picker on the widget"
|
||||
},
|
||||
"SETTINGS_POPUP": {
|
||||
"MESSENGER_HEADING": "Messenger Script",
|
||||
"MESSENGER_SUB_HEAD": "Place this button inside your body tag",
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
v-model="selectedAgents"
|
||||
:options="agentList"
|
||||
track-by="id"
|
||||
label="name"
|
||||
label="available_name"
|
||||
:multiple="true"
|
||||
:close-on-select="false"
|
||||
:clear-on-select="false"
|
||||
|
|
|
@ -30,14 +30,14 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
channelList: [
|
||||
'website',
|
||||
'facebook',
|
||||
'twitter',
|
||||
'twilio',
|
||||
'email',
|
||||
'api',
|
||||
'telegram',
|
||||
'line',
|
||||
{ key: 'website', name: 'Website' },
|
||||
{ key: 'facebook', name: 'Facebook' },
|
||||
{ key: 'twitter', name: 'Twitter' },
|
||||
{ key: 'twilio', name: 'Twilio' },
|
||||
{ key: 'email', name: 'Email' },
|
||||
{ key: 'api', name: 'API' },
|
||||
{ key: 'telegram', name: 'Telegram' },
|
||||
{ key: 'line', name: 'Line' },
|
||||
],
|
||||
enabledFeatures: {},
|
||||
};
|
||||
|
|
|
@ -3,9 +3,18 @@
|
|||
<woot-modal-header
|
||||
:header-image="inbox.avatarUrl"
|
||||
:header-title="inboxName"
|
||||
>
|
||||
<woot-tabs :index="selectedTabIndex" @change="onTabChange">
|
||||
<woot-tabs-item
|
||||
v-for="tab in tabs"
|
||||
:key="tab.key"
|
||||
:name="tab.name"
|
||||
:show-badge="false"
|
||||
/>
|
||||
</woot-tabs>
|
||||
</woot-modal-header>
|
||||
|
||||
<div class="settings--content">
|
||||
<div v-if="selectedTabKey === 'inbox_settings'" class="settings--content">
|
||||
<settings-section
|
||||
:title="$t('INBOX_MGMT.SETTINGS_POPUP.INBOX_UPDATE_TITLE')"
|
||||
:sub-title="$t('INBOX_MGMT.SETTINGS_POPUP.INBOX_UPDATE_SUB_TEXT')"
|
||||
|
@ -16,7 +25,7 @@
|
|||
@change="handleImageUpload"
|
||||
/>
|
||||
<woot-input
|
||||
v-if="isAWidgetInbox"
|
||||
v-if="isAWebWidgetInbox"
|
||||
v-model.trim="selectedInboxName"
|
||||
class="medium-9 columns"
|
||||
:label="$t('INBOX_MGMT.ADD.WEBSITE_CHANNEL.CHANNEL_NAME.LABEL')"
|
||||
|
@ -25,7 +34,7 @@
|
|||
"
|
||||
/>
|
||||
<woot-input
|
||||
v-if="isAWidgetInbox"
|
||||
v-if="isAWebWidgetInbox"
|
||||
v-model.trim="channelWebsiteUrl"
|
||||
class="medium-9 columns"
|
||||
:label="$t('INBOX_MGMT.ADD.WEBSITE_CHANNEL.CHANNEL_DOMAIN.LABEL')"
|
||||
|
@ -34,7 +43,7 @@
|
|||
"
|
||||
/>
|
||||
<woot-input
|
||||
v-if="isAWidgetInbox"
|
||||
v-if="isAWebWidgetInbox"
|
||||
v-model.trim="channelWelcomeTitle"
|
||||
class="medium-9 columns"
|
||||
:label="
|
||||
|
@ -48,7 +57,7 @@
|
|||
/>
|
||||
|
||||
<woot-input
|
||||
v-if="isAWidgetInbox"
|
||||
v-if="isAWebWidgetInbox"
|
||||
v-model.trim="channelWelcomeTagline"
|
||||
class="medium-9 columns"
|
||||
:label="
|
||||
|
@ -61,7 +70,7 @@
|
|||
"
|
||||
/>
|
||||
|
||||
<label v-if="isAWidgetInbox" class="medium-9 columns">
|
||||
<label v-if="isAWebWidgetInbox" class="medium-9 columns">
|
||||
{{ $t('INBOX_MGMT.ADD.WEBSITE_CHANNEL.WIDGET_COLOR.LABEL') }}
|
||||
<woot-color-picker v-model="inbox.widget_color" />
|
||||
</label>
|
||||
|
@ -94,7 +103,6 @@
|
|||
}}
|
||||
</p>
|
||||
</label>
|
||||
|
||||
<woot-input
|
||||
v-if="greetingEnabled"
|
||||
v-model.trim="greetingMessage"
|
||||
|
@ -122,6 +130,33 @@
|
|||
{{ $t('INBOX_MGMT.SETTINGS_POPUP.AUTO_ASSIGNMENT_SUB_TEXT') }}
|
||||
</p>
|
||||
</label>
|
||||
|
||||
<label v-if="isAWebWidgetInbox">
|
||||
{{ $t('INBOX_MGMT.FEATURES.LABEL') }}
|
||||
</label>
|
||||
<div v-if="isAWebWidgetInbox" class="widget--feature-flag">
|
||||
<input
|
||||
v-model="selectedFeatureFlags"
|
||||
type="checkbox"
|
||||
value="attachments"
|
||||
@input="handleFeatureFlag"
|
||||
/>
|
||||
<label for="attachments">
|
||||
{{ $t('INBOX_MGMT.FEATURES.DISPLAY_FILE_PICKER') }}
|
||||
</label>
|
||||
</div>
|
||||
<div v-if="isAWebWidgetInbox">
|
||||
<input
|
||||
v-model="selectedFeatureFlags"
|
||||
type="checkbox"
|
||||
value="emoji_picker"
|
||||
@input="handleFeatureFlag"
|
||||
/>
|
||||
<label for="emoji_picker">
|
||||
{{ $t('INBOX_MGMT.FEATURES.DISPLAY_EMOJI_PICKER') }}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<woot-submit-button
|
||||
:button-text="$t('INBOX_MGMT.SETTINGS_POPUP.UPDATE')"
|
||||
:loading="uiFlags.isUpdatingInbox"
|
||||
|
@ -132,7 +167,7 @@
|
|||
|
||||
<!-- update agents in inbox -->
|
||||
|
||||
<div class="settings--content">
|
||||
<div v-if="selectedTabKey === 'collaborators'" class="settings--content">
|
||||
<settings-section
|
||||
:title="$t('INBOX_MGMT.SETTINGS_POPUP.INBOX_AGENTS')"
|
||||
:sub-title="$t('INBOX_MGMT.SETTINGS_POPUP.INBOX_AGENTS_SUB_TEXT')"
|
||||
|
@ -141,7 +176,7 @@
|
|||
v-model="selectedAgents"
|
||||
:options="agentList"
|
||||
track-by="id"
|
||||
label="name"
|
||||
label="available_name"
|
||||
:multiple="true"
|
||||
:close-on-select="false"
|
||||
:clear-on-select="false"
|
||||
|
@ -157,11 +192,8 @@
|
|||
/>
|
||||
</settings-section>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="inbox.channel_type === 'Channel::TwilioSms'"
|
||||
class="settings--content"
|
||||
>
|
||||
<div v-if="selectedTabKey === 'configuration'">
|
||||
<div v-if="isATwilioChannel" class="settings--content">
|
||||
<settings-section
|
||||
:title="$t('INBOX_MGMT.ADD.TWILIO.API_CALLBACK.TITLE')"
|
||||
:sub-title="$t('INBOX_MGMT.ADD.TWILIO.API_CALLBACK.SUBTITLE')"
|
||||
|
@ -169,19 +201,7 @@
|
|||
<woot-code :script="twilioCallbackURL" lang="html"></woot-code>
|
||||
</settings-section>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="inbox.channel_type === 'Channel::FacebookPage'"
|
||||
class="settings--content"
|
||||
>
|
||||
<settings-section
|
||||
:title="$t('INBOX_MGMT.SETTINGS_POPUP.MESSENGER_HEADING')"
|
||||
:sub-title="$t('INBOX_MGMT.SETTINGS_POPUP.MESSENGER_SUB_HEAD')"
|
||||
>
|
||||
<woot-code :script="messengerScript"></woot-code>
|
||||
</settings-section>
|
||||
</div>
|
||||
<div v-else-if="inbox.channel_type === 'Channel::WebWidget'">
|
||||
<div v-else-if="isAWebWidgetInbox">
|
||||
<div class="settings--content">
|
||||
<settings-section
|
||||
:title="$t('INBOX_MGMT.SETTINGS_POPUP.MESSENGER_HEADING')"
|
||||
|
@ -192,21 +212,23 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/* eslint no-console: 0 */
|
||||
/* global bus */
|
||||
import { mapGetters } from 'vuex';
|
||||
import { createMessengerScript } from 'dashboard/helper/scriptGenerator';
|
||||
import configMixin from 'shared/mixins/configMixin';
|
||||
import alertMixin from 'shared/mixins/alertMixin';
|
||||
import SettingsSection from '../../../../components/SettingsSection';
|
||||
import inboxMixin from 'shared/mixins/inboxMixin';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
SettingsSection,
|
||||
},
|
||||
mixins: [configMixin],
|
||||
mixins: [alertMixin, configMixin, inboxMixin],
|
||||
data() {
|
||||
return {
|
||||
avatarFile: null,
|
||||
|
@ -220,6 +242,7 @@ export default {
|
|||
channelWebsiteUrl: '',
|
||||
channelWelcomeTitle: '',
|
||||
channelWelcomeTagline: '',
|
||||
selectedFeatureFlags: [],
|
||||
autoAssignmentOptions: [
|
||||
{
|
||||
value: true,
|
||||
|
@ -230,6 +253,7 @@ export default {
|
|||
label: this.$t('INBOX_MGMT.EDIT.AUTO_ASSIGNMENT.DISABLED'),
|
||||
},
|
||||
],
|
||||
selectedTabIndex: 0,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
@ -237,17 +261,41 @@ export default {
|
|||
agentList: 'agents/getAgents',
|
||||
uiFlags: 'inboxes/getUIFlags',
|
||||
}),
|
||||
selectedTabKey() {
|
||||
return this.tabs[this.selectedTabIndex]?.key;
|
||||
},
|
||||
tabs() {
|
||||
const visibleToAllChannelTabs = [
|
||||
{
|
||||
key: 'inbox_settings',
|
||||
name: this.$t('INBOX_MGMT.TABS.SETTINGS'),
|
||||
},
|
||||
{
|
||||
key: 'collaborators',
|
||||
name: this.$t('INBOX_MGMT.TABS.COLLABORATORS'),
|
||||
},
|
||||
];
|
||||
|
||||
if (this.isAWebWidgetInbox || this.isATwilioChannel) {
|
||||
return [
|
||||
...visibleToAllChannelTabs,
|
||||
{
|
||||
key: 'configuration',
|
||||
name: this.$t('INBOX_MGMT.TABS.CONFIGURATION'),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
return visibleToAllChannelTabs;
|
||||
},
|
||||
currentInboxId() {
|
||||
return this.$route.params.inboxId;
|
||||
},
|
||||
inbox() {
|
||||
return this.$store.getters['inboxes/getInbox'](this.currentInboxId);
|
||||
},
|
||||
isAWidgetInbox() {
|
||||
return this.inbox.channel_type === 'Channel::WebWidget';
|
||||
},
|
||||
inboxName() {
|
||||
if (this.inbox.channel_type === 'Channel::TwilioSms') {
|
||||
if (this.isATwilioSMSChannel || this.isATwilioWhatsappChannel) {
|
||||
return `${this.inbox.name} (${this.inbox.phone_number})`;
|
||||
}
|
||||
return this.inbox.name;
|
||||
|
@ -267,10 +315,25 @@ export default {
|
|||
this.fetchInboxSettings();
|
||||
},
|
||||
methods: {
|
||||
showAlert(message) {
|
||||
bus.$emit('newToastMessage', message);
|
||||
handleFeatureFlag(e) {
|
||||
console.log(e.target.value);
|
||||
this.selectedFeatureFlags = this.toggleInput(
|
||||
this.selectedFeatureFlags,
|
||||
e.target.value
|
||||
);
|
||||
},
|
||||
toggleInput(selected, current) {
|
||||
if (selected.includes(current)) {
|
||||
const newSelectedFlags = selected.filter(flag => flag !== current);
|
||||
return newSelectedFlags;
|
||||
}
|
||||
return [...selected, current];
|
||||
},
|
||||
onTabChange(selectedTabIndex) {
|
||||
this.selectedTabIndex = selectedTabIndex;
|
||||
},
|
||||
fetchInboxSettings() {
|
||||
this.selectedTabIndex = 0;
|
||||
this.selectedAgents = [];
|
||||
this.$store.dispatch('agents/get');
|
||||
this.$store.dispatch('inboxes/get').then(() => {
|
||||
|
@ -283,6 +346,7 @@ export default {
|
|||
this.channelWebsiteUrl = this.inbox.website_url;
|
||||
this.channelWelcomeTitle = this.inbox.welcome_title;
|
||||
this.channelWelcomeTagline = this.inbox.welcome_tagline;
|
||||
this.selectedFeatureFlags = this.inbox.selected_feature_flags || [];
|
||||
});
|
||||
},
|
||||
async fetchAttachedAgents() {
|
||||
|
@ -325,6 +389,7 @@ export default {
|
|||
website_url: this.channelWebsiteUrl,
|
||||
welcome_title: this.channelWelcomeTitle || '',
|
||||
welcome_tagline: this.channelWelcomeTagline || '',
|
||||
selectedFeatureFlags: this.selectedFeatureFlags,
|
||||
},
|
||||
};
|
||||
if (this.avatarFile) {
|
||||
|
@ -370,7 +435,16 @@ export default {
|
|||
.page-top-bar {
|
||||
@include background-light;
|
||||
@include border-normal-bottom;
|
||||
padding: $space-normal $space-larger;
|
||||
padding: $space-normal $space-large 0;
|
||||
|
||||
.tabs {
|
||||
padding: 0;
|
||||
margin-bottom: -1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.widget--feature-flag {
|
||||
padding-top: var(--space-small);
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -11,7 +11,15 @@ const buildInboxData = inboxParams => {
|
|||
Object.keys(inboxProperties).forEach(key => {
|
||||
formData.append(key, inboxProperties[key]);
|
||||
});
|
||||
Object.keys(channel).forEach(key => {
|
||||
const { selectedFeatureFlags = [], ...channelParams } = channel;
|
||||
if (selectedFeatureFlags.length) {
|
||||
selectedFeatureFlags.forEach(featureFlag => {
|
||||
formData.append(`channel[selected_feature_flags][]`, featureFlag);
|
||||
});
|
||||
} else {
|
||||
formData.append('channel[selected_feature_flags][]', '');
|
||||
}
|
||||
Object.keys(channelParams).forEach(key => {
|
||||
formData.append(`channel[${key}]`, channel[key]);
|
||||
});
|
||||
return formData;
|
||||
|
|
42
app/javascript/shared/mixins/inboxMixin.js
Normal file
42
app/javascript/shared/mixins/inboxMixin.js
Normal file
|
@ -0,0 +1,42 @@
|
|||
export const INBOX_TYPES = {
|
||||
WEB: 'Channel::WebWidget',
|
||||
FB: 'Channel::FacebookPage',
|
||||
TWITTER: 'Channel::TwitterProfile',
|
||||
TWILIO: 'Channel::TwilioSms',
|
||||
API: 'Channel::Api',
|
||||
EMAIL: 'Channel::Email',
|
||||
};
|
||||
|
||||
export default {
|
||||
computed: {
|
||||
channelType() {
|
||||
return this.inbox.channel_type;
|
||||
},
|
||||
isAPIInbox() {
|
||||
return this.channelType === INBOX_TYPES.API;
|
||||
},
|
||||
isATwitterInbox() {
|
||||
return this.channelType === INBOX_TYPES.TWITTER;
|
||||
},
|
||||
isAFacebookInbox() {
|
||||
return this.channelType === INBOX_TYPES.FB;
|
||||
},
|
||||
isAWebWidgetInbox() {
|
||||
return this.channelType === INBOX_TYPES.WEB;
|
||||
},
|
||||
isATwilioChannel() {
|
||||
return this.channelType === INBOX_TYPES.TWILIO;
|
||||
},
|
||||
isAnEmailChannel() {
|
||||
return this.channelType === INBOX_TYPES.EMAIL;
|
||||
},
|
||||
isATwilioSMSChannel() {
|
||||
const { phone_number: phoneNumber = '' } = this.inbox;
|
||||
return this.isATwilioChannel && !phoneNumber.startsWith('whatsapp');
|
||||
},
|
||||
isATwilioWhatsappChannel() {
|
||||
const { phone_number: phoneNumber = '' } = this.inbox;
|
||||
return this.isATwilioChannel && phoneNumber.startsWith('whatsapp');
|
||||
},
|
||||
},
|
||||
};
|
114
app/javascript/shared/mixins/specs/inboxMixin.spec.js
Normal file
114
app/javascript/shared/mixins/specs/inboxMixin.spec.js
Normal file
|
@ -0,0 +1,114 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import inboxMixin from '../inboxMixin';
|
||||
|
||||
describe('inboxMixin', () => {
|
||||
it('returns the correct channel type', () => {
|
||||
const Component = {
|
||||
render() {},
|
||||
mixins: [inboxMixin],
|
||||
data() {
|
||||
return { inbox: { channel_type: 'Channel::WebWidget' } };
|
||||
},
|
||||
};
|
||||
const wrapper = shallowMount(Component);
|
||||
expect(wrapper.vm.channelType).toBe('Channel::WebWidget');
|
||||
});
|
||||
|
||||
it('isAPIInbox returns true if channel type is API', () => {
|
||||
const Component = {
|
||||
render() {},
|
||||
mixins: [inboxMixin],
|
||||
data() {
|
||||
return { inbox: { channel_type: 'Channel::Api' } };
|
||||
},
|
||||
};
|
||||
const wrapper = shallowMount(Component);
|
||||
expect(wrapper.vm.isAPIInbox).toBe(true);
|
||||
});
|
||||
|
||||
it('isATwitterInbox returns true if channel type is twitter', () => {
|
||||
const Component = {
|
||||
render() {},
|
||||
mixins: [inboxMixin],
|
||||
data() {
|
||||
return { inbox: { channel_type: 'Channel::TwitterProfile' } };
|
||||
},
|
||||
};
|
||||
const wrapper = shallowMount(Component);
|
||||
expect(wrapper.vm.isATwitterInbox).toBe(true);
|
||||
});
|
||||
|
||||
it('isAFacebookInbox returns true if channel type is Facebook', () => {
|
||||
const Component = {
|
||||
render() {},
|
||||
mixins: [inboxMixin],
|
||||
data() {
|
||||
return { inbox: { channel_type: 'Channel::FacebookPage' } };
|
||||
},
|
||||
};
|
||||
const wrapper = shallowMount(Component);
|
||||
expect(wrapper.vm.isAFacebookInbox).toBe(true);
|
||||
});
|
||||
|
||||
it('isAWebWidgetInbox returns true if channel type is Facebook', () => {
|
||||
const Component = {
|
||||
render() {},
|
||||
mixins: [inboxMixin],
|
||||
data() {
|
||||
return { inbox: { channel_type: 'Channel::WebWidget' } };
|
||||
},
|
||||
};
|
||||
const wrapper = shallowMount(Component);
|
||||
expect(wrapper.vm.isAWebWidgetInbox).toBe(true);
|
||||
});
|
||||
|
||||
it('isATwilioChannel returns true if channel type is Twilio', () => {
|
||||
const Component = {
|
||||
render() {},
|
||||
mixins: [inboxMixin],
|
||||
data() {
|
||||
return {
|
||||
inbox: {
|
||||
channel_type: 'Channel::TwilioSms',
|
||||
phone_number: '+91944444444',
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
const wrapper = shallowMount(Component);
|
||||
expect(wrapper.vm.isATwilioChannel).toBe(true);
|
||||
expect(wrapper.vm.isATwilioSMSChannel).toBe(true);
|
||||
});
|
||||
|
||||
it('isATwilioWhatsappChannel returns true if channel type is Twilio and phonenumber is a whatsapp number', () => {
|
||||
const Component = {
|
||||
render() {},
|
||||
mixins: [inboxMixin],
|
||||
data() {
|
||||
return {
|
||||
inbox: {
|
||||
channel_type: 'Channel::TwilioSms',
|
||||
phone_number: 'whatsapp:+91944444444',
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
const wrapper = shallowMount(Component);
|
||||
expect(wrapper.vm.isATwilioChannel).toBe(true);
|
||||
expect(wrapper.vm.isATwilioWhatsappChannel).toBe(true);
|
||||
});
|
||||
|
||||
it('isAnEmailChannel returns true if channel type is email', () => {
|
||||
const Component = {
|
||||
render() {},
|
||||
mixins: [inboxMixin],
|
||||
data() {
|
||||
return {
|
||||
inbox: { channel_type: 'Channel::Email' },
|
||||
};
|
||||
},
|
||||
};
|
||||
const wrapper = shallowMount(Component);
|
||||
expect(wrapper.vm.isAnEmailChannel).toBe(true);
|
||||
});
|
||||
});
|
|
@ -18,6 +18,7 @@
|
|||
:on-click="emojiOnClick"
|
||||
/>
|
||||
<i
|
||||
v-if="hasEmojiPickerEnabled"
|
||||
class="emoji-toggle icon ion-happy-outline"
|
||||
:class="{ active: showEmojiPicker }"
|
||||
@click="toggleEmojiPicker()"
|
||||
|
@ -39,6 +40,7 @@ import ChatSendButton from 'widget/components/ChatSendButton.vue';
|
|||
import ChatAttachmentButton from 'widget/components/ChatAttachment.vue';
|
||||
import ResizableTextArea from 'shared/components/ResizableTextArea';
|
||||
import EmojiInput from 'dashboard/components/widgets/emoji/EmojiInput';
|
||||
import configMixin from '../mixins/configMixin';
|
||||
|
||||
export default {
|
||||
name: 'ChatInputWrap',
|
||||
|
@ -48,7 +50,7 @@ export default {
|
|||
EmojiInput,
|
||||
ResizableTextArea,
|
||||
},
|
||||
mixins: [clickaway],
|
||||
mixins: [clickaway, configMixin],
|
||||
props: {
|
||||
onSendMessage: {
|
||||
type: Function,
|
||||
|
@ -72,7 +74,7 @@ export default {
|
|||
widgetColor: 'appConfig/getWidgetColor',
|
||||
}),
|
||||
showAttachment() {
|
||||
return this.userInput.length === 0;
|
||||
return this.hasAttachmentsEnabled && this.userInput.length === 0;
|
||||
},
|
||||
showSendButton() {
|
||||
return this.userInput.length > 0;
|
||||
|
|
|
@ -15,5 +15,11 @@ export default {
|
|||
channelConfig() {
|
||||
return window.chatwootWebChannel;
|
||||
},
|
||||
hasEmojiPickerEnabled() {
|
||||
return this.channelConfig.enabledFeatures.includes('emoji_picker');
|
||||
},
|
||||
hasAttachmentsEnabled() {
|
||||
return this.channelConfig.enabledFeatures.includes('attachments');
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -6,6 +6,7 @@ global.chatwootWebChannel = {
|
|||
hideInputForBotConversations: true,
|
||||
avatarUrl: 'https://test.url',
|
||||
hasAConnectedAgentBot: 'AgentBot',
|
||||
enabledFeatures: ['emoji_picker', 'attachments'],
|
||||
};
|
||||
|
||||
global.chatwootWidgetDefaults = {
|
||||
|
@ -22,6 +23,8 @@ describe('configMixin', () => {
|
|||
const Constructor = Vue.extend(Component);
|
||||
const vm = new Constructor().$mount();
|
||||
const wrapper = createWrapper(vm);
|
||||
expect(wrapper.vm.hasEmojiPickerEnabled).toBe(true);
|
||||
expect(wrapper.vm.hasAttachmentsEnabled).toBe(true);
|
||||
expect(wrapper.vm.hideInputForBotConversations).toBe(true);
|
||||
expect(wrapper.vm.hasAConnectedAgentBot).toBe(true);
|
||||
expect(wrapper.vm.useInboxAvatarForBot).toBe(true);
|
||||
|
@ -30,6 +33,7 @@ describe('configMixin', () => {
|
|||
hideInputForBotConversations: true,
|
||||
avatarUrl: 'https://test.url',
|
||||
hasAConnectedAgentBot: 'AgentBot',
|
||||
enabledFeatures: ['emoji_picker', 'attachments'],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
# Table name: channel_web_widgets
|
||||
#
|
||||
# id :integer not null, primary key
|
||||
# feature_flags :integer default(3), not null
|
||||
# website_token :string
|
||||
# website_url :string
|
||||
# welcome_tagline :string
|
||||
|
@ -18,6 +19,8 @@
|
|||
#
|
||||
|
||||
class Channel::WebWidget < ApplicationRecord
|
||||
include FlagShihTzu
|
||||
|
||||
self.table_name = 'channel_web_widgets'
|
||||
|
||||
validates :website_url, presence: true
|
||||
|
@ -26,6 +29,9 @@ class Channel::WebWidget < ApplicationRecord
|
|||
belongs_to :account
|
||||
has_one :inbox, as: :channel, dependent: :destroy
|
||||
has_secure_token :website_token
|
||||
has_flags 1 => :attachments,
|
||||
2 => :emoji_picker,
|
||||
:column => 'feature_flags'
|
||||
|
||||
def has_24_hour_messaging_window?
|
||||
false
|
||||
|
|
|
@ -46,6 +46,8 @@ class Inbox < ApplicationRecord
|
|||
|
||||
after_destroy :delete_round_robin_agents
|
||||
|
||||
scope :order_by_id, -> { order(id: :asc) }
|
||||
|
||||
def add_member(user_id)
|
||||
member = inbox_members.new(user_id: user_id)
|
||||
member.save!
|
||||
|
|
|
@ -14,3 +14,4 @@ json.enable_auto_assignment resource.enable_auto_assignment
|
|||
json.web_widget_script resource.channel.try(:web_widget_script)
|
||||
json.forward_to_address resource.channel.try(:forward_to_address)
|
||||
json.phone_number resource.channel.try(:phone_number)
|
||||
json.selected_feature_flags resource.channel.try(:selected_feature_flags)
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
welcomeTagline: '<%= @web_widget.welcome_tagline %>',
|
||||
welcomeTitle: '<%= @web_widget.welcome_title %>',
|
||||
widgetColor: '<%= @web_widget.widget_color %>',
|
||||
enabledFeatures: <%= @web_widget.selected_feature_flags.to_json.html_safe %>,
|
||||
enabledLanguages: <%= available_locales_with_name.to_json.html_safe %>,
|
||||
}
|
||||
window.chatwootWidgetDefaults = {
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
class AddFeatureSettingToWebsiteInbox < ActiveRecord::Migration[6.0]
|
||||
def change
|
||||
add_column :channel_web_widgets, :feature_flags, :integer, default: 3, null: false
|
||||
end
|
||||
end
|
|
@ -178,6 +178,7 @@ ActiveRecord::Schema.define(version: 2020_08_02_170002) do
|
|||
t.string "widget_color", default: "#1f93ff"
|
||||
t.string "welcome_title"
|
||||
t.string "welcome_tagline"
|
||||
t.integer "feature_flags", default: 3, null: false
|
||||
t.index ["website_token"], name: "index_channel_web_widgets_on_website_token", unique: true
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in a new issue