mirror of
https://github.com/lingble/chatwoot.git
synced 2025-11-02 03:57:52 +00:00
Chore: Web widget Inbox Tech Debts (#738)
* Chore: Webwidget Inbox Tech Debts * Additional customization options creating Web Widget * Changes to edit Page for Web Widget * Remove the WebWidget API end points * Minor chores Address: #680, #502 Co-authored-by: Pranav Raj Sreepuram <pranavrajs@gmail.com>
This commit is contained in:
@@ -252,7 +252,7 @@ linters:
|
|||||||
enabled: false
|
enabled: false
|
||||||
|
|
||||||
UnnecessaryParentReference:
|
UnnecessaryParentReference:
|
||||||
enabled: true
|
enabled: false
|
||||||
|
|
||||||
UrlFormat:
|
UrlFormat:
|
||||||
enabled: true
|
enabled: true
|
||||||
|
|||||||
@@ -4,16 +4,18 @@ class Api::V1::Accounts::CallbacksController < Api::BaseController
|
|||||||
def register_facebook_page
|
def register_facebook_page
|
||||||
user_access_token = params[:user_access_token]
|
user_access_token = params[:user_access_token]
|
||||||
page_access_token = params[:page_access_token]
|
page_access_token = params[:page_access_token]
|
||||||
page_name = params[:page_name]
|
|
||||||
page_id = params[:page_id]
|
page_id = params[:page_id]
|
||||||
inbox_name = params[:inbox_name]
|
inbox_name = params[:inbox_name]
|
||||||
facebook_channel = current_account.facebook_pages.create!(
|
ActiveRecord::Base.transaction do
|
||||||
name: page_name, page_id: page_id, user_access_token: user_access_token,
|
facebook_channel = current_account.facebook_pages.create!(
|
||||||
page_access_token: page_access_token
|
page_id: page_id, user_access_token: user_access_token,
|
||||||
)
|
page_access_token: page_access_token
|
||||||
set_avatar(facebook_channel, page_id)
|
)
|
||||||
inbox = current_account.inboxes.create!(name: inbox_name, channel: facebook_channel)
|
@facebook_inbox = current_account.inboxes.create!(name: inbox_name, channel: facebook_channel)
|
||||||
render json: inbox
|
set_avatar(@facebook_inbox, page_id)
|
||||||
|
rescue StandardError => e
|
||||||
|
Rails.logger e
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def facebook_pages
|
def facebook_pages
|
||||||
@@ -72,13 +74,13 @@ class Api::V1::Accounts::CallbacksController < Api::BaseController
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_avatar(facebook_channel, page_id)
|
def set_avatar(facebook_inbox, page_id)
|
||||||
uri = get_avatar_url(page_id)
|
uri = get_avatar_url(page_id)
|
||||||
|
|
||||||
return unless uri
|
return unless uri
|
||||||
|
|
||||||
avatar_resource = LocalResource.new(uri)
|
avatar_resource = LocalResource.new(uri)
|
||||||
facebook_channel.avatar.attach(io: avatar_resource.file, filename: avatar_resource.tmp_filename, content_type: avatar_resource.encoding)
|
facebook_inbox.avatar.attach(io: avatar_resource.file, filename: avatar_resource.tmp_filename, content_type: avatar_resource.encoding)
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_avatar_url(page_id)
|
def get_avatar_url(page_id)
|
||||||
|
|||||||
@@ -1,14 +1,24 @@
|
|||||||
class Api::V1::Accounts::InboxesController < Api::BaseController
|
class Api::V1::Accounts::InboxesController < Api::BaseController
|
||||||
before_action :check_authorization
|
before_action :check_authorization
|
||||||
before_action :fetch_inbox, except: [:index]
|
before_action :fetch_inbox, except: [:index, :create]
|
||||||
before_action :fetch_agent_bot, only: [:set_agent_bot]
|
before_action :fetch_agent_bot, only: [:set_agent_bot]
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@inboxes = policy_scope(current_account.inboxes)
|
@inboxes = policy_scope(current_account.inboxes)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
ActiveRecord::Base.transaction do
|
||||||
|
channel = web_widgets.create!(permitted_params[:channel].except(:type)) if permitted_params[:channel][:type] == 'web_widget'
|
||||||
|
@inbox = current_account.inboxes.build(name: permitted_params[:name], channel: channel)
|
||||||
|
@inbox.avatar.attach(permitted_params[:avatar])
|
||||||
|
@inbox.save!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def update
|
def update
|
||||||
@inbox.update(inbox_update_params)
|
@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?
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_agent_bot
|
def set_agent_bot
|
||||||
@@ -37,11 +47,20 @@ class Api::V1::Accounts::InboxesController < Api::BaseController
|
|||||||
@agent_bot = AgentBot.find(params[:agent_bot]) if params[:agent_bot]
|
@agent_bot = AgentBot.find(params[:agent_bot]) if params[:agent_bot]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def web_widgets
|
||||||
|
current_account.web_widgets
|
||||||
|
end
|
||||||
|
|
||||||
def check_authorization
|
def check_authorization
|
||||||
authorize(Inbox)
|
authorize(Inbox)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def permitted_params
|
||||||
|
params.permit(:id, :avatar, :name, channel: [:type, :website_url, :widget_color, :welcome_title, :welcome_tagline, :agent_away_message])
|
||||||
|
end
|
||||||
|
|
||||||
def inbox_update_params
|
def inbox_update_params
|
||||||
params.require(:inbox).permit(:enable_auto_assignment, :avatar)
|
params.permit(:enable_auto_assignment, :name, :avatar, channel: [:website_url, :widget_color, :welcome_title,
|
||||||
|
:welcome_tagline, :agent_away_message])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,48 +0,0 @@
|
|||||||
class Api::V1::Accounts::Widget::InboxesController < Api::BaseController
|
|
||||||
before_action :authorize_request
|
|
||||||
before_action :set_web_widget_channel, only: [:update]
|
|
||||||
before_action :set_inbox, only: [:update]
|
|
||||||
|
|
||||||
def create
|
|
||||||
ActiveRecord::Base.transaction do
|
|
||||||
channel = web_widgets.create!(
|
|
||||||
website_name: permitted_params[:website][:website_name],
|
|
||||||
website_url: permitted_params[:website][:website_url],
|
|
||||||
widget_color: permitted_params[:website][:widget_color]
|
|
||||||
)
|
|
||||||
@inbox = inboxes.create!(name: permitted_params[:website][:website_name], channel: channel)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def update
|
|
||||||
@channel.update!(
|
|
||||||
widget_color: permitted_params[:website][:widget_color]
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def authorize_request
|
|
||||||
authorize ::Inbox
|
|
||||||
end
|
|
||||||
|
|
||||||
def inboxes
|
|
||||||
current_account.inboxes
|
|
||||||
end
|
|
||||||
|
|
||||||
def web_widgets
|
|
||||||
current_account.web_widgets
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_web_widget_channel
|
|
||||||
@channel = web_widgets.find_by(id: permitted_params[:id])
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_inbox
|
|
||||||
@inbox = @channel.inbox
|
|
||||||
end
|
|
||||||
|
|
||||||
def permitted_params
|
|
||||||
params.permit(:id, website: [:website_name, :website_url, :widget_color])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -6,7 +6,7 @@ class Twitter::AuthorizationsController < Twitter::BaseController
|
|||||||
::Redis::Alfred.setex(oauth_token, account.id)
|
::Redis::Alfred.setex(oauth_token, account.id)
|
||||||
redirect_to oauth_authorize_endpoint(oauth_token)
|
redirect_to oauth_authorize_endpoint(oauth_token)
|
||||||
else
|
else
|
||||||
redirect_to app_new_twitter_inbox_url
|
redirect_to app_new_twitter_inbox_url(account_id: account.id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -39,8 +39,7 @@ class Twitter::CallbacksController < Twitter::BaseController
|
|||||||
twitter_profile = account.twitter_profiles.create(
|
twitter_profile = account.twitter_profiles.create(
|
||||||
twitter_access_token: parsed_body['oauth_token'],
|
twitter_access_token: parsed_body['oauth_token'],
|
||||||
twitter_access_token_secret: parsed_body['oauth_token_secret'],
|
twitter_access_token_secret: parsed_body['oauth_token_secret'],
|
||||||
profile_id: parsed_body['user_id'],
|
profile_id: parsed_body['user_id']
|
||||||
name: parsed_body['screen_name']
|
|
||||||
)
|
)
|
||||||
account.inboxes.create(
|
account.inboxes.create(
|
||||||
name: parsed_body['screen_name'],
|
name: parsed_body['screen_name'],
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import ApiClient from '../ApiClient';
|
|||||||
|
|
||||||
class WebChannel extends ApiClient {
|
class WebChannel extends ApiClient {
|
||||||
constructor() {
|
constructor() {
|
||||||
super('widget/inboxes', { accountScoped: true });
|
super('inboxes', { accountScoped: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,42 +31,39 @@
|
|||||||
.wizard-box {
|
.wizard-box {
|
||||||
.item {
|
.item {
|
||||||
@include padding($space-normal $space-normal $space-normal $space-medium);
|
@include padding($space-normal $space-normal $space-normal $space-medium);
|
||||||
position: relative;
|
|
||||||
@include background-light;
|
@include background-light;
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
&:before,
|
cursor: pointer;
|
||||||
&:after {
|
position: relative;
|
||||||
content: '';
|
|
||||||
position: absolute;
|
&::before,
|
||||||
width: 2px;
|
&::after {
|
||||||
height: 100%;
|
|
||||||
background: $color-border;
|
background: $color-border;
|
||||||
|
content: '';
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
top: $space-normal;
|
top: $space-normal;
|
||||||
|
width: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:before {
|
&::before {
|
||||||
top: $zero;
|
|
||||||
height: $space-normal;
|
height: $space-normal;
|
||||||
|
top: $zero;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:first-child {
|
&:first-child {
|
||||||
&:before {
|
&::before {
|
||||||
height: 0;
|
height: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:last-child {
|
&:last-child {
|
||||||
&:after {
|
&::after {
|
||||||
height: $zero;
|
height: $zero;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
// left: 1px;
|
|
||||||
// @include background-white;
|
|
||||||
// @include border-light;
|
|
||||||
// border-right: 0;
|
|
||||||
h3 {
|
h3 {
|
||||||
color: $color-woot;
|
color: $color-woot;
|
||||||
}
|
}
|
||||||
@@ -78,7 +75,7 @@
|
|||||||
|
|
||||||
&.over {
|
&.over {
|
||||||
|
|
||||||
&:after {
|
&::after {
|
||||||
background: $color-woot;
|
background: $color-woot;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,18 +83,18 @@
|
|||||||
background: $color-woot;
|
background: $color-woot;
|
||||||
}
|
}
|
||||||
|
|
||||||
&+.item {
|
& + .item {
|
||||||
&:before {
|
&::before {
|
||||||
background: $color-woot;
|
background: $color-woot;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
font-size: $font-size-default;
|
|
||||||
padding-left: $space-medium;
|
|
||||||
line-height: 1;
|
|
||||||
color: $color-body;
|
color: $color-body;
|
||||||
|
font-size: $font-size-default;
|
||||||
|
line-height: 1;
|
||||||
|
padding-left: $space-medium;
|
||||||
|
|
||||||
.completed {
|
.completed {
|
||||||
color: $success-color;
|
color: $success-color;
|
||||||
@@ -105,25 +102,25 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
p {
|
p {
|
||||||
font-size: $font-size-small;
|
|
||||||
color: $color-light-gray;
|
color: $color-light-gray;
|
||||||
padding-left: $space-medium;
|
font-size: $font-size-small;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
padding-left: $space-medium;
|
||||||
}
|
}
|
||||||
|
|
||||||
.step {
|
.step {
|
||||||
position: absolute;
|
|
||||||
left: $space-normal;
|
|
||||||
top: $space-normal;
|
|
||||||
font-size: $font-size-small;
|
|
||||||
font-weight: $font-weight-medium;
|
|
||||||
background: $color-border;
|
background: $color-border;
|
||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
width: $space-normal;
|
color: $color-white;
|
||||||
|
font-size: $font-size-small;
|
||||||
|
font-weight: $font-weight-medium;
|
||||||
height: $space-normal;
|
height: $space-normal;
|
||||||
text-align: center;
|
left: $space-normal;
|
||||||
line-height: $space-normal;
|
line-height: $space-normal;
|
||||||
color: #fff;
|
position: absolute;
|
||||||
|
text-align: center;
|
||||||
|
top: $space-normal;
|
||||||
|
width: $space-normal;
|
||||||
z-index: 999;
|
z-index: 999;
|
||||||
|
|
||||||
i {
|
i {
|
||||||
@@ -141,10 +138,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.inoboxes-list {
|
.inoboxes-list {
|
||||||
// @include margin(auto);
|
|
||||||
// @include background-white;
|
|
||||||
// @include border-light;
|
|
||||||
// width: 50%;
|
|
||||||
|
|
||||||
.inbox-item {
|
.inbox-item {
|
||||||
@include margin($space-normal);
|
@include margin($space-normal);
|
||||||
@@ -152,16 +145,18 @@
|
|||||||
@include flex-shrink;
|
@include flex-shrink;
|
||||||
@include padding($space-normal $space-normal);
|
@include padding($space-normal $space-normal);
|
||||||
@include border-light-bottom();
|
@include border-light-bottom();
|
||||||
flex-direction: column;
|
|
||||||
background: $color-white;
|
background: $color-white;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
width: 20%;
|
flex-direction: column;
|
||||||
float: left;
|
float: left;
|
||||||
min-height: 10rem;
|
min-height: 10rem;
|
||||||
|
width: 20%;
|
||||||
|
|
||||||
&:last-child {
|
&:last-child {
|
||||||
margin-bottom: $zero;
|
|
||||||
@include border-nil;
|
@include border-nil;
|
||||||
|
|
||||||
|
margin-bottom: $zero;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
@@ -174,8 +169,8 @@
|
|||||||
|
|
||||||
.switch {
|
.switch {
|
||||||
align-self: center;
|
align-self: center;
|
||||||
margin-right: $space-normal;
|
|
||||||
margin-bottom: $zero;
|
margin-bottom: $zero;
|
||||||
|
margin-right: $space-normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
.item--details {
|
.item--details {
|
||||||
@@ -187,15 +182,15 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.item--sub {
|
.item--sub {
|
||||||
margin-bottom: 0;
|
|
||||||
font-size: $font-size-small;
|
font-size: $font-size-small;
|
||||||
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.arrow {
|
.arrow {
|
||||||
align-self: center;
|
align-self: center;
|
||||||
font-size: $font-size-small;
|
|
||||||
color: $medium-gray;
|
color: $medium-gray;
|
||||||
|
font-size: $font-size-small;
|
||||||
opacity: .7;
|
opacity: .7;
|
||||||
transform: translateX(0);
|
transform: translateX(0);
|
||||||
transition: opacity 0.100s ease-in 0s, transform 0.200s ease-in 0.030s;
|
transition: opacity 0.100s ease-in 0s, transform 0.200s ease-in 0.030s;
|
||||||
@@ -204,18 +199,19 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.settings--content {
|
.settings--content {
|
||||||
@include margin($space-small $space-medium);
|
@include margin($space-small $space-larger);
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
font-weight: $font-weight-medium;
|
font-weight: $font-weight-medium;
|
||||||
}
|
}
|
||||||
|
|
||||||
.code {
|
.code {
|
||||||
|
@include padding($space-one);
|
||||||
|
|
||||||
|
background: $color-background;
|
||||||
max-height: $space-mega;
|
max-height: $space-mega;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
@include padding($space-one);
|
|
||||||
background: $color-background;
|
|
||||||
|
|
||||||
code {
|
code {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
@@ -225,8 +221,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.login-init {
|
.login-init {
|
||||||
text-align: center;
|
|
||||||
padding-top: 30%;
|
padding-top: 30%;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
p {
|
p {
|
||||||
@include padding($space-medium);
|
@include padding($space-medium);
|
||||||
|
|||||||
@@ -1,69 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="row settings--form--header">
|
|
||||||
<div class="medium-8">
|
|
||||||
<p class="title">
|
|
||||||
{{ title }}
|
|
||||||
</p>
|
|
||||||
<p class="sub-head">
|
|
||||||
{{ subTitle }}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div v-if="buttonText" class="medium-4 text-right">
|
|
||||||
<woot-submit-button
|
|
||||||
class="default"
|
|
||||||
:button-text="buttonText"
|
|
||||||
:loading="isUpdating"
|
|
||||||
@click="onClick()"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
title: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
subTitle: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
buttonText: {
|
|
||||||
type: String,
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
isUpdating: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
onClick() {
|
|
||||||
this.$emit('update');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
@import '~dashboard/assets/scss/variables';
|
|
||||||
|
|
||||||
.settings--form--header {
|
|
||||||
align-items: center;
|
|
||||||
border-bottom: 1px solid $color-border;
|
|
||||||
display: flex;
|
|
||||||
margin-bottom: $space-normal;
|
|
||||||
padding: $space-normal 0;
|
|
||||||
|
|
||||||
.button {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title {
|
|
||||||
margin-bottom: 0;
|
|
||||||
font-size: $font-size-default;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
46
app/javascript/dashboard/components/SettingsSection.vue
Normal file
46
app/javascript/dashboard/components/SettingsSection.vue
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<template>
|
||||||
|
<div class="row settings--section">
|
||||||
|
<div class="medium-4">
|
||||||
|
<p class="sub-block-title">
|
||||||
|
{{ title }}
|
||||||
|
</p>
|
||||||
|
<p class="sub-head">
|
||||||
|
{{ subTitle }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="medium-6">
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
subTitle: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@import '~dashboard/assets/scss/variables';
|
||||||
|
|
||||||
|
.settings--section {
|
||||||
|
border-bottom: 1px solid $color-border;
|
||||||
|
display: flex;
|
||||||
|
padding: $space-normal 0;
|
||||||
|
|
||||||
|
.sub-block-title {
|
||||||
|
color: $color-woot;
|
||||||
|
font-weight: $font-weight-medium;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -30,6 +30,18 @@
|
|||||||
"LABEL": "Website Domain",
|
"LABEL": "Website Domain",
|
||||||
"PLACEHOLDER": "Enter your website domain (eg: acme.com)"
|
"PLACEHOLDER": "Enter your website domain (eg: acme.com)"
|
||||||
},
|
},
|
||||||
|
"CHANNEL_WELCOME_TITLE": {
|
||||||
|
"LABEL": "Welcome Heading",
|
||||||
|
"PLACEHOLDER": "Hi there !"
|
||||||
|
},
|
||||||
|
"CHANNEL_WELCOME_TAGLINE": {
|
||||||
|
"LABEL": "Welcome Tagline",
|
||||||
|
"PLACEHOLDER": "We make it simple to connect with us. Ask us anything, or share your feedback."
|
||||||
|
},
|
||||||
|
"CHANNEL_AGENT_AWAY_MESSAGE": {
|
||||||
|
"LABEL": "Agents Away Message",
|
||||||
|
"PLACEHOLDER": "Acme Inc typically replies in a few hours."
|
||||||
|
},
|
||||||
"WIDGET_COLOR": {
|
"WIDGET_COLOR": {
|
||||||
"LABEL": "Widget Color",
|
"LABEL": "Widget Color",
|
||||||
"PLACEHOLDER": "Update the widget color used in widget"
|
"PLACEHOLDER": "Update the widget color used in widget"
|
||||||
@@ -102,7 +114,7 @@
|
|||||||
"VIEW": "View",
|
"VIEW": "View",
|
||||||
"EDIT": {
|
"EDIT": {
|
||||||
"API": {
|
"API": {
|
||||||
"SUCCESS_MESSAGE": "Widget color updated successfully",
|
"SUCCESS_MESSAGE": "Inbox settings updated successfully",
|
||||||
"AUTO_ASSIGNMENT_SUCCESS_MESSAGE": "Auto assignment updated successfully",
|
"AUTO_ASSIGNMENT_SUCCESS_MESSAGE": "Auto assignment updated successfully",
|
||||||
"ERROR_MESSAGE": "Could not update widget color. Please try again later."
|
"ERROR_MESSAGE": "Could not update widget color. Please try again later."
|
||||||
},
|
},
|
||||||
@@ -132,7 +144,9 @@
|
|||||||
"INBOX_AGENTS_SUB_TEXT": "Add or remove agents from this inbox",
|
"INBOX_AGENTS_SUB_TEXT": "Add or remove agents from this inbox",
|
||||||
"UPDATE": "Update",
|
"UPDATE": "Update",
|
||||||
"AUTO_ASSIGNMENT": "Enable auto assignment",
|
"AUTO_ASSIGNMENT": "Enable auto assignment",
|
||||||
"AUTO_ASSIGNMENT_SUB_TEXT": "Enable or disable the automatic assignment of available agents on new conversations"
|
"INBOX_UPDATE_TITLE": "Inbox Settings",
|
||||||
|
"INBOX_UPDATE_SUB_TEXT": "Update your inbox settings",
|
||||||
|
"AUTO_ASSIGNMENT_SUB_TEXT": "Enable or disable the automatic assignment of new conversations to the agents added to this inbox."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"ACCOUNT_NAME": {
|
"ACCOUNT_NAME": {
|
||||||
"LABEL": "Account Name",
|
"LABEL": "Account Name",
|
||||||
"PLACEHOLDER": "Wayne Enterprises",
|
"PLACEHOLDER": "Wayne Enterprises",
|
||||||
"ERROR": "Account Name is too small"
|
"ERROR": "Account Name is too short"
|
||||||
},
|
},
|
||||||
"EMAIL": {
|
"EMAIL": {
|
||||||
"LABEL": "Email",
|
"LABEL": "Email",
|
||||||
|
|||||||
@@ -4,79 +4,177 @@
|
|||||||
:header-image="inbox.avatarUrl"
|
:header-image="inbox.avatarUrl"
|
||||||
:header-title="inboxName"
|
:header-title="inboxName"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<div 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')"
|
||||||
|
>
|
||||||
|
<div v-if="inbox.channel_type === 'Channel::WebWidget'">
|
||||||
|
<div class="medium-12 columns">
|
||||||
|
<label>
|
||||||
|
{{ $t('INBOX_MGMT.ADD.WEBSITE_CHANNEL.CHANNEL_NAME.LABEL') }}
|
||||||
|
<input
|
||||||
|
v-model.trim="inboxName"
|
||||||
|
type="text"
|
||||||
|
:placeholder="
|
||||||
|
$t('INBOX_MGMT.ADD.WEBSITE_CHANNEL.CHANNEL_NAME.PLACEHOLDER')
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="medium-12 columns">
|
||||||
|
<label>
|
||||||
|
{{ $t('INBOX_MGMT.ADD.WEBSITE_CHANNEL.CHANNEL_DOMAIN.LABEL') }}
|
||||||
|
<input
|
||||||
|
v-model.trim="channelWebsiteUrl"
|
||||||
|
type="text"
|
||||||
|
:placeholder="
|
||||||
|
$t(
|
||||||
|
'INBOX_MGMT.ADD.WEBSITE_CHANNEL.CHANNEL_DOMAIN.PLACEHOLDER'
|
||||||
|
)
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="medium-12 columns">
|
||||||
|
<label>
|
||||||
|
{{
|
||||||
|
$t('INBOX_MGMT.ADD.WEBSITE_CHANNEL.CHANNEL_WELCOME_TITLE.LABEL')
|
||||||
|
}}
|
||||||
|
<input
|
||||||
|
v-model.trim="channelWelcomeTitle"
|
||||||
|
type="text"
|
||||||
|
:placeholder="
|
||||||
|
$t(
|
||||||
|
'INBOX_MGMT.ADD.WEBSITE_CHANNEL.CHANNEL_WELCOME_TITLE.PLACEHOLDER'
|
||||||
|
)
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="medium-12 columns">
|
||||||
|
<label>
|
||||||
|
{{
|
||||||
|
$t(
|
||||||
|
'INBOX_MGMT.ADD.WEBSITE_CHANNEL.CHANNEL_WELCOME_TAGLINE.LABEL'
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
<input
|
||||||
|
v-model.trim="channelWelcomeTagline"
|
||||||
|
type="text"
|
||||||
|
:placeholder="
|
||||||
|
$t(
|
||||||
|
'INBOX_MGMT.ADD.WEBSITE_CHANNEL.CHANNEL_WELCOME_TAGLINE.PLACEHOLDER'
|
||||||
|
)
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="medium-12 columns">
|
||||||
|
<label>
|
||||||
|
{{
|
||||||
|
$t(
|
||||||
|
'INBOX_MGMT.ADD.WEBSITE_CHANNEL.CHANNEL_AGENT_AWAY_MESSAGE.LABEL'
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
<input
|
||||||
|
v-model.trim="channelAgentAwayMessage"
|
||||||
|
type="text"
|
||||||
|
:placeholder="
|
||||||
|
$t(
|
||||||
|
'INBOX_MGMT.ADD.WEBSITE_CHANNEL.CHANNEL_AGENT_AWAY_MESSAGE.PLACEHOLDER'
|
||||||
|
)
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="medium-12 columns">
|
||||||
|
<label>
|
||||||
|
{{ $t('INBOX_MGMT.ADD.WEBSITE_CHANNEL.WIDGET_COLOR.LABEL') }}
|
||||||
|
<compact
|
||||||
|
v-model="inbox.widget_color"
|
||||||
|
class="widget-color--selector"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label>
|
||||||
|
{{ $t('INBOX_MGMT.SETTINGS_POPUP.AUTO_ASSIGNMENT') }}
|
||||||
|
<select v-model="autoAssignment">
|
||||||
|
<option value="true">
|
||||||
|
{{ $t('INBOX_MGMT.EDIT.AUTO_ASSIGNMENT.ENABLED') }}
|
||||||
|
</option>
|
||||||
|
<option value="false">
|
||||||
|
{{ $t('INBOX_MGMT.EDIT.AUTO_ASSIGNMENT.DISABLED') }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
<p class="help-text">
|
||||||
|
{{ $t('INBOX_MGMT.SETTINGS_POPUP.AUTO_ASSIGNMENT_SUB_TEXT') }}
|
||||||
|
</p>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<woot-submit-button
|
||||||
|
:button-text="$t('INBOX_MGMT.SETTINGS_POPUP.UPDATE')"
|
||||||
|
:loading="uiFlags.isUpdatingInbox"
|
||||||
|
@click="updateInbox"
|
||||||
|
>
|
||||||
|
</woot-submit-button>
|
||||||
|
</settings-section>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- update agents in inbox -->
|
||||||
|
|
||||||
|
<div class="settings--content">
|
||||||
|
<settings-section
|
||||||
|
:title="$t('INBOX_MGMT.SETTINGS_POPUP.INBOX_AGENTS')"
|
||||||
|
:sub-title="$t('INBOX_MGMT.SETTINGS_POPUP.INBOX_AGENTS_SUB_TEXT')"
|
||||||
|
>
|
||||||
|
<multiselect
|
||||||
|
v-model="selectedAgents"
|
||||||
|
:options="agentList"
|
||||||
|
track-by="id"
|
||||||
|
label="name"
|
||||||
|
:multiple="true"
|
||||||
|
:close-on-select="false"
|
||||||
|
:clear-on-select="false"
|
||||||
|
:hide-selected="true"
|
||||||
|
placeholder="Pick some"
|
||||||
|
@select="$v.selectedAgents.$touch"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<woot-submit-button
|
||||||
|
:button-text="$t('INBOX_MGMT.SETTINGS_POPUP.UPDATE')"
|
||||||
|
:loading="isAgentListUpdating"
|
||||||
|
@click="updateAgents"
|
||||||
|
>
|
||||||
|
</woot-submit-button>
|
||||||
|
</settings-section>
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="inbox.channel_type === 'Channel::FacebookPage'"
|
v-if="inbox.channel_type === 'Channel::FacebookPage'"
|
||||||
class="settings--content"
|
class="settings--content"
|
||||||
>
|
>
|
||||||
<settings-form-header
|
<settings-section
|
||||||
:title="$t('INBOX_MGMT.SETTINGS_POPUP.MESSENGER_HEADING')"
|
:title="$t('INBOX_MGMT.SETTINGS_POPUP.MESSENGER_HEADING')"
|
||||||
:sub-title="$t('INBOX_MGMT.SETTINGS_POPUP.MESSENGER_SUB_HEAD')"
|
:sub-title="$t('INBOX_MGMT.SETTINGS_POPUP.MESSENGER_SUB_HEAD')"
|
||||||
>
|
>
|
||||||
</settings-form-header>
|
<woot-code :script="messengerScript"></woot-code>
|
||||||
<woot-code :script="messengerScript"></woot-code>
|
</settings-section>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="inbox.channel_type === 'Channel::WebWidget'">
|
<div v-else-if="inbox.channel_type === 'Channel::WebWidget'">
|
||||||
<div class="settings--content">
|
<div class="settings--content">
|
||||||
<settings-form-header
|
<settings-section
|
||||||
:title="$t('INBOX_MGMT.SETTINGS_POPUP.MESSENGER_HEADING')"
|
:title="$t('INBOX_MGMT.SETTINGS_POPUP.MESSENGER_HEADING')"
|
||||||
:sub-title="$t('INBOX_MGMT.SETTINGS_POPUP.MESSENGER_SUB_HEAD')"
|
:sub-title="$t('INBOX_MGMT.SETTINGS_POPUP.MESSENGER_SUB_HEAD')"
|
||||||
>
|
>
|
||||||
</settings-form-header>
|
<woot-code :script="inbox.web_widget_script"></woot-code>
|
||||||
<woot-code :script="inbox.web_widget_script"></woot-code>
|
</settings-section>
|
||||||
</div>
|
</div>
|
||||||
<div class="settings--content">
|
|
||||||
<settings-form-header
|
|
||||||
:title="$t('INBOX_MGMT.ADD.WEBSITE_CHANNEL.WIDGET_COLOR.LABEL')"
|
|
||||||
:sub-title="
|
|
||||||
$t('INBOX_MGMT.ADD.WEBSITE_CHANNEL.WIDGET_COLOR.PLACEHOLDER')
|
|
||||||
"
|
|
||||||
:button-text="$t('INBOX_MGMT.SETTINGS_POPUP.UPDATE')"
|
|
||||||
:is-updating="uiFlags.isUpdating"
|
|
||||||
@update="updateWidgetColor"
|
|
||||||
>
|
|
||||||
</settings-form-header>
|
|
||||||
<Compact v-model="inbox.widget_color" class="widget-color--selector" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="settings--content">
|
|
||||||
<settings-form-header
|
|
||||||
:title="$t('INBOX_MGMT.SETTINGS_POPUP.INBOX_AGENTS')"
|
|
||||||
:sub-title="$t('INBOX_MGMT.SETTINGS_POPUP.INBOX_AGENTS_SUB_TEXT')"
|
|
||||||
:button-text="$t('INBOX_MGMT.SETTINGS_POPUP.UPDATE')"
|
|
||||||
:is-updating="isAgentListUpdating"
|
|
||||||
@update="updateAgents"
|
|
||||||
>
|
|
||||||
</settings-form-header>
|
|
||||||
<multiselect
|
|
||||||
v-model="selectedAgents"
|
|
||||||
:options="agentList"
|
|
||||||
track-by="id"
|
|
||||||
label="name"
|
|
||||||
:multiple="true"
|
|
||||||
:close-on-select="false"
|
|
||||||
:clear-on-select="false"
|
|
||||||
:hide-selected="true"
|
|
||||||
placeholder="Pick some"
|
|
||||||
@select="$v.selectedAgents.$touch"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="settings--content">
|
|
||||||
<settings-form-header
|
|
||||||
:title="$t('INBOX_MGMT.SETTINGS_POPUP.AUTO_ASSIGNMENT')"
|
|
||||||
:sub-title="$t('INBOX_MGMT.SETTINGS_POPUP.AUTO_ASSIGNMENT_SUB_TEXT')"
|
|
||||||
:button-text="$t('INBOX_MGMT.SETTINGS_POPUP.UPDATE')"
|
|
||||||
:is-updating="uiFlags.isUpdatingAutoAssignment"
|
|
||||||
@update="updateAutoAssignment"
|
|
||||||
>
|
|
||||||
</settings-form-header>
|
|
||||||
<select v-model="autoAssignment">
|
|
||||||
<option value="true">
|
|
||||||
{{ $t('INBOX_MGMT.EDIT.AUTO_ASSIGNMENT.ENABLED') }}
|
|
||||||
</option>
|
|
||||||
<option value="false">
|
|
||||||
{{ $t('INBOX_MGMT.EDIT.AUTO_ASSIGNMENT.DISABLED') }}
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -88,12 +186,12 @@ import { mapGetters } from 'vuex';
|
|||||||
import { createMessengerScript } from 'dashboard/helper/scriptGenerator';
|
import { createMessengerScript } from 'dashboard/helper/scriptGenerator';
|
||||||
import { Compact } from 'vue-color';
|
import { Compact } from 'vue-color';
|
||||||
import configMixin from 'shared/mixins/configMixin';
|
import configMixin from 'shared/mixins/configMixin';
|
||||||
import SettingsFormHeader from '../../../../components/SettingsFormHeader.vue';
|
import SettingsSection from '../../../../components/SettingsSection';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
Compact,
|
Compact,
|
||||||
SettingsFormHeader,
|
SettingsSection,
|
||||||
},
|
},
|
||||||
mixins: [configMixin],
|
mixins: [configMixin],
|
||||||
data() {
|
data() {
|
||||||
@@ -102,6 +200,10 @@ export default {
|
|||||||
autoAssignment: false,
|
autoAssignment: false,
|
||||||
isUpdating: false,
|
isUpdating: false,
|
||||||
isAgentListUpdating: false,
|
isAgentListUpdating: false,
|
||||||
|
channelWebsiteUrl: '',
|
||||||
|
channelWelcomeTitle: '',
|
||||||
|
channelWelcomeTagline: '',
|
||||||
|
channelAgentAwayMessage: '',
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -145,6 +247,10 @@ export default {
|
|||||||
this.$store.dispatch('inboxes/get').then(() => {
|
this.$store.dispatch('inboxes/get').then(() => {
|
||||||
this.fetchAttachedAgents();
|
this.fetchAttachedAgents();
|
||||||
this.autoAssignment = this.inbox.enable_auto_assignment;
|
this.autoAssignment = this.inbox.enable_auto_assignment;
|
||||||
|
this.channelWebsiteUrl = this.inbox.website_url;
|
||||||
|
this.channelWelcomeTitle = this.inbox.welcome_title;
|
||||||
|
this.channelWelcomeTagline = this.inbox.welcome_tagline;
|
||||||
|
this.channelAgentAwayMessage = this.inbox.agent_away_message;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
async fetchAttachedAgents() {
|
async fetchAttachedAgents() {
|
||||||
@@ -181,34 +287,23 @@ export default {
|
|||||||
}
|
}
|
||||||
this.isAgentListUpdating = false;
|
this.isAgentListUpdating = false;
|
||||||
},
|
},
|
||||||
async updateWidgetColor() {
|
async updateInbox() {
|
||||||
try {
|
try {
|
||||||
await this.$store.dispatch('inboxes/updateWebsiteChannel', {
|
await this.$store.dispatch('inboxes/updateInbox', {
|
||||||
id: this.inbox.channel_id,
|
|
||||||
website: {
|
|
||||||
widget_color: this.getWidgetColor(this.inbox.widget_color),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
this.showAlert(this.$t('INBOX_MGMT.EDIT.API.SUCCESS_MESSAGE'));
|
|
||||||
} catch (error) {
|
|
||||||
this.showAlert(this.$t('INBOX_MGMT.EDIT.API.SUCCESS_MESSAGE'));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async updateAutoAssignment() {
|
|
||||||
try {
|
|
||||||
await this.$store.dispatch('inboxes/updateAutoAssignment', {
|
|
||||||
id: this.currentInboxId,
|
id: this.currentInboxId,
|
||||||
inbox: {
|
name: this.inboxName,
|
||||||
enable_auto_assignment: this.autoAssignment,
|
enable_auto_assignment: this.autoAssignment,
|
||||||
|
channel: {
|
||||||
|
widget_color: this.getWidgetColor(this.inbox.widget_color),
|
||||||
|
website_url: this.channelWebsiteUrl,
|
||||||
|
welcome_title: this.channelWelcomeTitle,
|
||||||
|
welcome_tagline: this.channelWelcomeTagline,
|
||||||
|
agent_away_message: this.channelAgentAwayMessage,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
this.showAlert(
|
this.showAlert(this.$t('INBOX_MGMT.EDIT.API.SUCCESS_MESSAGE'));
|
||||||
this.$t('INBOX_MGMT.EDIT.API.AUTO_ASSIGNMENT_SUCCESS_MESSAGE')
|
|
||||||
);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.showAlert(
|
this.showAlert(this.$t('INBOX_MGMT.EDIT.API.SUCCESS_MESSAGE'));
|
||||||
this.$t('INBOX_MGMT.EDIT.API.AUTO_ASSIGNMENT_SUCCESS_MESSAGE')
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getWidgetColor() {
|
getWidgetColor() {
|
||||||
@@ -226,3 +321,26 @@ export default {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import '~dashboard/assets/scss/variables';
|
||||||
|
@import '~dashboard/assets/scss/mixins';
|
||||||
|
|
||||||
|
.settings {
|
||||||
|
background: $color-white;
|
||||||
|
|
||||||
|
.settings--content {
|
||||||
|
&:last-child {
|
||||||
|
.settings--section {
|
||||||
|
border-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-top-bar {
|
||||||
|
@include background-light;
|
||||||
|
@include border-normal-bottom;
|
||||||
|
padding: $space-normal $space-larger;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -221,9 +221,8 @@ export default {
|
|||||||
return {
|
return {
|
||||||
user_access_token: this.user_access_token,
|
user_access_token: this.user_access_token,
|
||||||
page_access_token: this.selectedPage.access_token,
|
page_access_token: this.selectedPage.access_token,
|
||||||
page_name: this.selectedPage.name,
|
|
||||||
page_id: this.selectedPage.id,
|
page_id: this.selectedPage.id,
|
||||||
inbox_name: this.pageName,
|
inbox_name: this.selectedPage.name,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
<label>
|
<label>
|
||||||
{{ $t('INBOX_MGMT.ADD.WEBSITE_CHANNEL.CHANNEL_NAME.LABEL') }}
|
{{ $t('INBOX_MGMT.ADD.WEBSITE_CHANNEL.CHANNEL_NAME.LABEL') }}
|
||||||
<input
|
<input
|
||||||
v-model.trim="websiteName"
|
v-model.trim="inboxName"
|
||||||
type="text"
|
type="text"
|
||||||
:placeholder="
|
:placeholder="
|
||||||
$t('INBOX_MGMT.ADD.WEBSITE_CHANNEL.CHANNEL_NAME.PLACEHOLDER')
|
$t('INBOX_MGMT.ADD.WEBSITE_CHANNEL.CHANNEL_NAME.PLACEHOLDER')
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
<label>
|
<label>
|
||||||
{{ $t('INBOX_MGMT.ADD.WEBSITE_CHANNEL.CHANNEL_DOMAIN.LABEL') }}
|
{{ $t('INBOX_MGMT.ADD.WEBSITE_CHANNEL.CHANNEL_DOMAIN.LABEL') }}
|
||||||
<input
|
<input
|
||||||
v-model.trim="websiteUrl"
|
v-model.trim="channelWebsiteUrl"
|
||||||
type="text"
|
type="text"
|
||||||
:placeholder="
|
:placeholder="
|
||||||
$t('INBOX_MGMT.ADD.WEBSITE_CHANNEL.CHANNEL_DOMAIN.PLACEHOLDER')
|
$t('INBOX_MGMT.ADD.WEBSITE_CHANNEL.CHANNEL_DOMAIN.PLACEHOLDER')
|
||||||
@@ -38,11 +38,62 @@
|
|||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="medium-12 columns">
|
||||||
|
<label>
|
||||||
|
{{ $t('INBOX_MGMT.ADD.WEBSITE_CHANNEL.CHANNEL_WELCOME_TITLE.LABEL') }}
|
||||||
|
<input
|
||||||
|
v-model.trim="channelWelcomeTitle"
|
||||||
|
type="text"
|
||||||
|
:placeholder="
|
||||||
|
$t(
|
||||||
|
'INBOX_MGMT.ADD.WEBSITE_CHANNEL.CHANNEL_WELCOME_TITLE.PLACEHOLDER'
|
||||||
|
)
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="medium-12 columns">
|
||||||
|
<label>
|
||||||
|
{{
|
||||||
|
$t('INBOX_MGMT.ADD.WEBSITE_CHANNEL.CHANNEL_WELCOME_TAGLINE.LABEL')
|
||||||
|
}}
|
||||||
|
<input
|
||||||
|
v-model.trim="channelWelcomeTagline"
|
||||||
|
type="text"
|
||||||
|
:placeholder="
|
||||||
|
$t(
|
||||||
|
'INBOX_MGMT.ADD.WEBSITE_CHANNEL.CHANNEL_WELCOME_TAGLINE.PLACEHOLDER'
|
||||||
|
)
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="medium-12 columns">
|
||||||
|
<label>
|
||||||
|
{{
|
||||||
|
$t(
|
||||||
|
'INBOX_MGMT.ADD.WEBSITE_CHANNEL.CHANNEL_AGENT_AWAY_MESSAGE.LABEL'
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
<input
|
||||||
|
v-model.trim="channelAgentAwayMessage"
|
||||||
|
type="text"
|
||||||
|
:placeholder="
|
||||||
|
$t(
|
||||||
|
'INBOX_MGMT.ADD.WEBSITE_CHANNEL.CHANNEL_AGENT_AWAY_MESSAGE.PLACEHOLDER'
|
||||||
|
)
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="medium-12 columns">
|
<div class="medium-12 columns">
|
||||||
<label>
|
<label>
|
||||||
{{ $t('INBOX_MGMT.ADD.WEBSITE_CHANNEL.WIDGET_COLOR.LABEL') }}
|
{{ $t('INBOX_MGMT.ADD.WEBSITE_CHANNEL.WIDGET_COLOR.LABEL') }}
|
||||||
<compact v-model="widgetColor" class="widget-color--selector" />
|
<compact
|
||||||
|
v-model="channelWidgetColor"
|
||||||
|
class="widget-color--selector"
|
||||||
|
/>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -50,7 +101,7 @@
|
|||||||
<div class="medium-12 columns">
|
<div class="medium-12 columns">
|
||||||
<woot-submit-button
|
<woot-submit-button
|
||||||
:loading="uiFlags.isCreating"
|
:loading="uiFlags.isCreating"
|
||||||
:disabled="!websiteUrl || !websiteName"
|
:disabled="!channelWebsiteUrl || !inboxName"
|
||||||
:button-text="$t('INBOX_MGMT.ADD.WEBSITE_CHANNEL.SUBMIT_BUTTON')"
|
:button-text="$t('INBOX_MGMT.ADD.WEBSITE_CHANNEL.SUBMIT_BUTTON')"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -72,9 +123,12 @@ export default {
|
|||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
websiteName: '',
|
inboxName: '',
|
||||||
websiteUrl: '',
|
channelWebsiteUrl: '',
|
||||||
widgetColor: { hex: '#009CE0' },
|
channelWidgetColor: { hex: '#009CE0' },
|
||||||
|
channelWelcomeTitle: '',
|
||||||
|
channelWelcomeTagline: '',
|
||||||
|
channelAgentAwayMessage: '',
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -87,10 +141,14 @@ export default {
|
|||||||
const website = await this.$store.dispatch(
|
const website = await this.$store.dispatch(
|
||||||
'inboxes/createWebsiteChannel',
|
'inboxes/createWebsiteChannel',
|
||||||
{
|
{
|
||||||
website: {
|
name: this.inboxName,
|
||||||
website_name: this.websiteName,
|
channel: {
|
||||||
website_url: this.websiteUrl,
|
type: 'web_widget',
|
||||||
widget_color: this.widgetColor.hex,
|
website_url: this.channelWebsiteUrl,
|
||||||
|
widget_color: this.channelWidgetColor.hex,
|
||||||
|
welcome_title: this.channelWelcomeTitle,
|
||||||
|
welcome_tagline: this.channelWelcomeTagline,
|
||||||
|
agent_away_message: this.channelAgentAwayMessage,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -79,18 +79,7 @@ export const actions = {
|
|||||||
throw new Error(error);
|
throw new Error(error);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
updateWebsiteChannel: async ({ commit }, { id, ...inboxParams }) => {
|
updateInbox: async ({ commit }, { id, ...inboxParams }) => {
|
||||||
commit(types.default.SET_INBOXES_UI_FLAG, { isUpdating: true });
|
|
||||||
try {
|
|
||||||
const response = await WebChannel.update(id, inboxParams);
|
|
||||||
commit(types.default.EDIT_INBOXES, response.data);
|
|
||||||
commit(types.default.SET_INBOXES_UI_FLAG, { isUpdating: false });
|
|
||||||
} catch (error) {
|
|
||||||
commit(types.default.SET_INBOXES_UI_FLAG, { isUpdating: false });
|
|
||||||
throw new Error(error);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
updateAutoAssignment: async ({ commit }, { id, ...inboxParams }) => {
|
|
||||||
commit(types.default.SET_INBOXES_UI_FLAG, {
|
commit(types.default.SET_INBOXES_UI_FLAG, {
|
||||||
isUpdatingAutoAssignment: true,
|
isUpdatingAutoAssignment: true,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -70,35 +70,13 @@ describe('#actions', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#updateWebsiteChannel', () => {
|
describe('#updateInbox', () => {
|
||||||
it('sends correct actions if API is success', async () => {
|
|
||||||
axios.patch.mockResolvedValue({ data: inboxList[0] });
|
|
||||||
await actions.updateWebsiteChannel({ commit }, inboxList[0]);
|
|
||||||
expect(commit.mock.calls).toEqual([
|
|
||||||
[types.default.SET_INBOXES_UI_FLAG, { isUpdating: true }],
|
|
||||||
[types.default.EDIT_INBOXES, inboxList[0]],
|
|
||||||
[types.default.SET_INBOXES_UI_FLAG, { isUpdating: false }],
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
it('sends correct actions if API is error', async () => {
|
|
||||||
axios.patch.mockRejectedValue({ message: 'Incorrect header' });
|
|
||||||
await expect(
|
|
||||||
actions.updateWebsiteChannel({ commit }, inboxList[0])
|
|
||||||
).rejects.toThrow(Error);
|
|
||||||
expect(commit.mock.calls).toEqual([
|
|
||||||
[types.default.SET_INBOXES_UI_FLAG, { isUpdating: true }],
|
|
||||||
[types.default.SET_INBOXES_UI_FLAG, { isUpdating: false }],
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#updateAutoAssignment', () => {
|
|
||||||
it('sends correct actions if API is success', async () => {
|
it('sends correct actions if API is success', async () => {
|
||||||
const updatedInbox = inboxList[0];
|
const updatedInbox = inboxList[0];
|
||||||
updatedInbox.enable_auto_assignment = false;
|
updatedInbox.enable_auto_assignment = false;
|
||||||
|
|
||||||
axios.patch.mockResolvedValue({ data: updatedInbox });
|
axios.patch.mockResolvedValue({ data: updatedInbox });
|
||||||
await actions.updateAutoAssignment(
|
await actions.updateInbox(
|
||||||
{ commit },
|
{ commit },
|
||||||
{ id: updatedInbox.id, inbox: { enable_auto_assignment: false } }
|
{ id: updatedInbox.id, inbox: { enable_auto_assignment: false } }
|
||||||
);
|
);
|
||||||
@@ -114,7 +92,7 @@ describe('#actions', () => {
|
|||||||
it('sends correct actions if API is error', async () => {
|
it('sends correct actions if API is error', async () => {
|
||||||
axios.patch.mockRejectedValue({ message: 'Incorrect header' });
|
axios.patch.mockRejectedValue({ message: 'Incorrect header' });
|
||||||
await expect(
|
await expect(
|
||||||
actions.updateAutoAssignment(
|
actions.updateInbox(
|
||||||
{ commit },
|
{ commit },
|
||||||
{ id: inboxList[0].id, inbox: { enable_auto_assignment: false } }
|
{ id: inboxList[0].id, inbox: { enable_auto_assignment: false } }
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ export const IFrameHelper = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
onLoad: ({ widget_color: widgetColor }) => {
|
onLoad: ({ widgetColor }) => {
|
||||||
const iframe = IFrameHelper.getAppFrame();
|
const iframe = IFrameHelper.getAppFrame();
|
||||||
iframe.style.visibility = '';
|
iframe.style.visibility = '';
|
||||||
iframe.setAttribute('id', `chatwoot_live_chat_widget`);
|
iframe.setAttribute('id', `chatwoot_live_chat_widget`);
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export default {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
const { website_token: websiteToken = '' } = window.chatwootWebChannel;
|
const { websiteToken } = window.chatwootWebChannel;
|
||||||
if (IFrameHelper.isIFrame()) {
|
if (IFrameHelper.isIFrame()) {
|
||||||
IFrameHelper.sendMessage({
|
IFrameHelper.sendMessage({
|
||||||
event: 'loaded',
|
event: 'loaded',
|
||||||
|
|||||||
@@ -20,11 +20,6 @@ import { IFrameHelper } from 'widget/helpers/utils';
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ChatHeaderExpanded',
|
name: 'ChatHeaderExpanded',
|
||||||
computed: {
|
|
||||||
...mapGetters({
|
|
||||||
widgetColor: 'appConfig/getWidgetColor',
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
props: {
|
props: {
|
||||||
introHeading: {
|
introHeading: {
|
||||||
type: String,
|
type: String,
|
||||||
@@ -36,6 +31,11 @@ export default {
|
|||||||
'We make it simple to connect with us. Ask us anything, or share your feedback.',
|
'We make it simple to connect with us. Ask us anything, or share your feedback.',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters({
|
||||||
|
widgetColor: 'appConfig/getWidgetColor',
|
||||||
|
}),
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
closeWindow() {
|
closeWindow() {
|
||||||
if (IFrameHelper.isIFrame()) {
|
if (IFrameHelper.isIFrame()) {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ const actions = {
|
|||||||
|
|
||||||
const mutations = {
|
const mutations = {
|
||||||
[SET_WIDGET_COLOR]($state, data) {
|
[SET_WIDGET_COLOR]($state, data) {
|
||||||
$state.widgetColor = data.widget_color;
|
$state.widgetColor = data.widgetColor;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="home">
|
<div class="home">
|
||||||
<div class="header-wrap">
|
<div class="header-wrap">
|
||||||
<ChatHeaderExpanded v-if="isHeaderExpanded" />
|
<ChatHeaderExpanded
|
||||||
<ChatHeader v-else :title="getHeaderName" />
|
v-if="isHeaderExpanded"
|
||||||
|
:intro-heading="channelConfig.welcomeTitle"
|
||||||
|
:intro-body="channelConfig.welcomeTagline"
|
||||||
|
/>
|
||||||
|
<ChatHeader v-else :title="channelConfig.websiteName" />
|
||||||
</div>
|
</div>
|
||||||
<AvailableAgents v-if="showAvailableAgents" :agents="availableAgents" />
|
<AvailableAgents v-if="showAvailableAgents" :agents="availableAgents" />
|
||||||
<ConversationWrap :grouped-messages="groupedMessages" />
|
<ConversationWrap :grouped-messages="groupedMessages" />
|
||||||
@@ -45,8 +49,8 @@ export default {
|
|||||||
isHeaderExpanded() {
|
isHeaderExpanded() {
|
||||||
return this.conversationSize === 0;
|
return this.conversationSize === 0;
|
||||||
},
|
},
|
||||||
getHeaderName() {
|
channelConfig() {
|
||||||
return window.chatwootWebChannel.website_name;
|
return window.chatwootWebChannel;
|
||||||
},
|
},
|
||||||
showAvailableAgents() {
|
showAvailableAgents() {
|
||||||
return this.availableAgents.length > 0 && this.conversationSize < 1;
|
return this.availableAgents.length > 0 && this.conversationSize < 1;
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
# Table name: channel_facebook_pages
|
# Table name: channel_facebook_pages
|
||||||
#
|
#
|
||||||
# id :integer not null, primary key
|
# id :integer not null, primary key
|
||||||
# name :string not null
|
|
||||||
# page_access_token :string not null
|
# page_access_token :string not null
|
||||||
# user_access_token :string not null
|
# user_access_token :string not null
|
||||||
# created_at :datetime not null
|
# created_at :datetime not null
|
||||||
@@ -18,23 +17,19 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
class Channel::FacebookPage < ApplicationRecord
|
class Channel::FacebookPage < ApplicationRecord
|
||||||
|
# FIXME: this should be removed post 1.4 release. we moved avatars to inbox
|
||||||
include Avatarable
|
include Avatarable
|
||||||
|
|
||||||
self.table_name = 'channel_facebook_pages'
|
self.table_name = 'channel_facebook_pages'
|
||||||
|
|
||||||
validates :account_id, presence: true
|
validates :account_id, presence: true
|
||||||
validates :page_id, uniqueness: { scope: :account_id }
|
validates :page_id, uniqueness: { scope: :account_id }
|
||||||
has_one_attached :avatar
|
|
||||||
belongs_to :account
|
belongs_to :account
|
||||||
|
|
||||||
has_one :inbox, as: :channel, dependent: :destroy
|
has_one :inbox, as: :channel, dependent: :destroy
|
||||||
|
|
||||||
before_destroy :unsubscribe
|
before_destroy :unsubscribe
|
||||||
|
|
||||||
def name
|
|
||||||
'Facebook'
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def unsubscribe
|
def unsubscribe
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
# Table name: channel_twitter_profiles
|
# Table name: channel_twitter_profiles
|
||||||
#
|
#
|
||||||
# id :bigint not null, primary key
|
# id :bigint not null, primary key
|
||||||
# name :string
|
|
||||||
# twitter_access_token :string not null
|
# twitter_access_token :string not null
|
||||||
# twitter_access_token_secret :string not null
|
# twitter_access_token_secret :string not null
|
||||||
# created_at :datetime not null
|
# created_at :datetime not null
|
||||||
@@ -21,17 +20,12 @@ class Channel::TwitterProfile < ApplicationRecord
|
|||||||
|
|
||||||
validates :account_id, presence: true
|
validates :account_id, presence: true
|
||||||
validates :profile_id, uniqueness: { scope: :account_id }
|
validates :profile_id, uniqueness: { scope: :account_id }
|
||||||
has_one_attached :avatar
|
|
||||||
belongs_to :account
|
belongs_to :account
|
||||||
|
|
||||||
has_one :inbox, as: :channel, dependent: :destroy
|
has_one :inbox, as: :channel, dependent: :destroy
|
||||||
|
|
||||||
before_destroy :unsubscribe
|
before_destroy :unsubscribe
|
||||||
|
|
||||||
def name
|
|
||||||
'Twitter'
|
|
||||||
end
|
|
||||||
|
|
||||||
def create_contact_inbox(profile_id, name, additional_attributes)
|
def create_contact_inbox(profile_id, name, additional_attributes)
|
||||||
ActiveRecord::Base.transaction do
|
ActiveRecord::Base.transaction do
|
||||||
contact = inbox.account.contacts.create!(additional_attributes: additional_attributes, name: name)
|
contact = inbox.account.contacts.create!(additional_attributes: additional_attributes, name: name)
|
||||||
|
|||||||
@@ -2,14 +2,16 @@
|
|||||||
#
|
#
|
||||||
# Table name: channel_web_widgets
|
# Table name: channel_web_widgets
|
||||||
#
|
#
|
||||||
# id :integer not null, primary key
|
# id :integer not null, primary key
|
||||||
# website_name :string
|
# agent_away_message :string
|
||||||
# website_token :string
|
# website_token :string
|
||||||
# website_url :string
|
# website_url :string
|
||||||
# widget_color :string default("#1f93ff")
|
# welcome_tagline :string
|
||||||
# created_at :datetime not null
|
# welcome_title :string
|
||||||
# updated_at :datetime not null
|
# widget_color :string default("#1f93ff")
|
||||||
# account_id :integer
|
# created_at :datetime not null
|
||||||
|
# updated_at :datetime not null
|
||||||
|
# account_id :integer
|
||||||
#
|
#
|
||||||
# Indexes
|
# Indexes
|
||||||
#
|
#
|
||||||
@@ -19,7 +21,6 @@
|
|||||||
class Channel::WebWidget < ApplicationRecord
|
class Channel::WebWidget < ApplicationRecord
|
||||||
self.table_name = 'channel_web_widgets'
|
self.table_name = 'channel_web_widgets'
|
||||||
|
|
||||||
validates :website_name, presence: true
|
|
||||||
validates :website_url, presence: true
|
validates :website_url, presence: true
|
||||||
validates :widget_color, presence: true
|
validates :widget_color, presence: true
|
||||||
|
|
||||||
@@ -27,10 +28,6 @@ class Channel::WebWidget < ApplicationRecord
|
|||||||
has_one :inbox, as: :channel, dependent: :destroy
|
has_one :inbox, as: :channel, dependent: :destroy
|
||||||
has_secure_token :website_token
|
has_secure_token :website_token
|
||||||
|
|
||||||
def name
|
|
||||||
'Website'
|
|
||||||
end
|
|
||||||
|
|
||||||
def web_widget_script
|
def web_widget_script
|
||||||
"<script>
|
"<script>
|
||||||
(function(d,t) {
|
(function(d,t) {
|
||||||
|
|||||||
@@ -25,10 +25,10 @@ class NotificationSetting < ApplicationRecord
|
|||||||
flag_query_mode: :bit_operator
|
flag_query_mode: :bit_operator
|
||||||
}.freeze
|
}.freeze
|
||||||
|
|
||||||
EMAIL_NOTIFCATION_FLAGS = {
|
EMAIL_NOTIFICATION_FLAGS = {
|
||||||
1 => :conversation_creation,
|
1 => :conversation_creation,
|
||||||
2 => :conversation_assignment
|
2 => :conversation_assignment
|
||||||
}.freeze
|
}.freeze
|
||||||
|
|
||||||
has_flags EMAIL_NOTIFCATION_FLAGS.merge(column: 'email_flags').merge(DEFAULT_QUERY_SETTING)
|
has_flags EMAIL_NOTIFICATION_FLAGS.merge(column: 'email_flags').merge(DEFAULT_QUERY_SETTING)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
json.id @facebook_inbox.id
|
||||||
|
json.channel_id @facebook_inbox.channel_id
|
||||||
|
json.name @facebook_inbox.name
|
||||||
|
json.channel_type @facebook_inbox.channel_type
|
||||||
|
json.avatar_url @facebook_inbox.try(:avatar_url)
|
||||||
|
json.page_id @facebook_inbox.channel.try(:page_id)
|
||||||
|
json.enable_auto_assignment @facebook_inbox.enable_auto_assignment
|
||||||
@@ -4,4 +4,8 @@ json.name @inbox.name
|
|||||||
json.channel_type @inbox.channel_type
|
json.channel_type @inbox.channel_type
|
||||||
json.website_token @inbox.channel.try(:website_token)
|
json.website_token @inbox.channel.try(:website_token)
|
||||||
json.widget_color @inbox.channel.try(:widget_color)
|
json.widget_color @inbox.channel.try(:widget_color)
|
||||||
|
json.website_url @inbox.channel.try(:website_url)
|
||||||
|
json.welcome_title @inbox.channel.try(:welcome_title)
|
||||||
|
json.welcome_tagline @inbox.channel.try(:welcome_tagline)
|
||||||
|
json.agent_away_message @inbox.channel.try(:agent_away_message)
|
||||||
json.web_widget_script @inbox.channel.try(:web_widget_script)
|
json.web_widget_script @inbox.channel.try(:web_widget_script)
|
||||||
@@ -4,10 +4,13 @@ json.payload do
|
|||||||
json.channel_id inbox.channel_id
|
json.channel_id inbox.channel_id
|
||||||
json.name inbox.name
|
json.name inbox.name
|
||||||
json.channel_type inbox.channel_type
|
json.channel_type inbox.channel_type
|
||||||
json.avatar_url inbox.channel.try(:avatar_url)
|
json.avatar_url inbox.try(:avatar_url)
|
||||||
json.page_id inbox.channel.try(:page_id)
|
json.page_id inbox.channel.try(:page_id)
|
||||||
json.widget_color inbox.channel.try(:widget_color)
|
json.widget_color inbox.channel.try(:widget_color)
|
||||||
json.website_token inbox.channel.try(:website_token)
|
json.website_url inbox.channel.try(:website_url)
|
||||||
|
json.welcome_title inbox.channel.try(:welcome_title)
|
||||||
|
json.welcome_tagline inbox.channel.try(:welcome_tagline)
|
||||||
|
json.agent_away_message inbox.channel.try(:agent_away_message)
|
||||||
json.enable_auto_assignment inbox.enable_auto_assignment
|
json.enable_auto_assignment inbox.enable_auto_assignment
|
||||||
json.web_widget_script inbox.channel.try(:web_widget_script)
|
json.web_widget_script inbox.channel.try(:web_widget_script)
|
||||||
json.phone_number inbox.channel.try(:phone_number)
|
json.phone_number inbox.channel.try(:phone_number)
|
||||||
|
|||||||
11
app/views/api/v1/accounts/inboxes/update.json.jbuilder
Normal file
11
app/views/api/v1/accounts/inboxes/update.json.jbuilder
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
json.id @inbox.id
|
||||||
|
json.channel_id @inbox.channel_id
|
||||||
|
json.name @inbox.name
|
||||||
|
json.channel_type @inbox.channel_type
|
||||||
|
json.website_token @inbox.channel.try(:website_token)
|
||||||
|
json.widget_color @inbox.channel.try(:widget_color)
|
||||||
|
json.website_url @inbox.channel.try(:website_url)
|
||||||
|
json.welcome_title @inbox.channel.try(:welcome_title)
|
||||||
|
json.welcome_tagline @inbox.channel.try(:welcome_tagline)
|
||||||
|
json.agent_away_message @inbox.channel.try(:agent_away_message)
|
||||||
|
json.web_widget_script @inbox.channel.try(:web_widget_script)
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
json.id @inbox.id
|
|
||||||
json.channel_id @inbox.channel_id
|
|
||||||
json.name @inbox.name
|
|
||||||
json.channel_type @inbox.channel_type
|
|
||||||
json.website_token @inbox.channel.website_token
|
|
||||||
json.widget_color @inbox.channel.widget_color
|
|
||||||
json.web_widget_script @inbox.channel.try(:web_widget_script)
|
|
||||||
@@ -6,9 +6,11 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0" />
|
||||||
<script>
|
<script>
|
||||||
window.chatwootWebChannel = {
|
window.chatwootWebChannel = {
|
||||||
website_name: '<%= @web_widget.website_name %>',
|
websiteName: '<%= @web_widget.inbox.name %>',
|
||||||
widget_color: '<%= @web_widget.widget_color %>',
|
widgetColor: '<%= @web_widget.widget_color %>',
|
||||||
website_token: '<%= @web_widget.website_token %>'
|
websiteToken: '<%= @web_widget.welcome_title %>',
|
||||||
|
welcomeTitle: '<%= @web_widget.welcome_title %>',
|
||||||
|
welcomeTagline: '<%= @web_widget.welcome_tagline %>',
|
||||||
}
|
}
|
||||||
window.chatwootPubsubToken = '<%= @contact.pubsub_token %>'
|
window.chatwootPubsubToken = '<%= @contact.pubsub_token %>'
|
||||||
window.authToken = '<%= @token %>'
|
window.authToken = '<%= @token %>'
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ Rails.application.routes.draw do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
resources :inboxes, only: [:index, :destroy, :update] do
|
resources :inboxes, only: [:index, :create, :update, :destroy] do
|
||||||
post :set_agent_bot, on: :member
|
post :set_agent_bot, on: :member
|
||||||
end
|
end
|
||||||
resources :inbox_members, only: [:create, :show], param: :inbox_id
|
resources :inbox_members, only: [:create, :show], param: :inbox_id
|
||||||
@@ -97,9 +97,6 @@ Rails.application.routes.draw do
|
|||||||
end
|
end
|
||||||
|
|
||||||
resources :webhooks, except: [:show]
|
resources :webhooks, except: [:show]
|
||||||
namespace :widget do
|
|
||||||
resources :inboxes, only: [:create, :update]
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# end of account scoped api routes
|
# end of account scoped api routes
|
||||||
|
|||||||
35
db/migrate/20200417093432_remove_name_from_channels.rb
Normal file
35
db/migrate/20200417093432_remove_name_from_channels.rb
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
class RemoveNameFromChannels < ActiveRecord::Migration[6.0]
|
||||||
|
def change
|
||||||
|
remove_column :channel_facebook_pages, :name, :string
|
||||||
|
remove_column :channel_twitter_profiles, :name, :string
|
||||||
|
migrate_web_widget_name_to_inbox
|
||||||
|
remove_column :channel_web_widgets, :website_name, :string # rubocop:disable Rails/BulkChangeTable
|
||||||
|
|
||||||
|
add_column :channel_web_widgets, :welcome_title, :string
|
||||||
|
add_column :channel_web_widgets, :welcome_tagline, :string
|
||||||
|
add_column :channel_web_widgets, :agent_away_message, :string
|
||||||
|
purge_orphan_facebook_pages
|
||||||
|
remove_avatars_from_channel_to_inbox
|
||||||
|
end
|
||||||
|
|
||||||
|
def purge_orphan_facebook_pages
|
||||||
|
Channel::FacebookPage.all.each do |facebook_page|
|
||||||
|
facebook_page.destroy! if facebook_page.inbox.nil?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def migrate_web_widget_name_to_inbox
|
||||||
|
Channel::WebWidget.all.each do |widget|
|
||||||
|
widget.inbox.name = widget.website_name
|
||||||
|
widget.save!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def remove_avatars_from_channel_to_inbox
|
||||||
|
Channel::FacebookPage.all.each do |facebook_page|
|
||||||
|
next unless facebook_page.avatar
|
||||||
|
|
||||||
|
facebook_page.avatar.purge
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema.define(version: 2020_04_04_135009) do
|
ActiveRecord::Schema.define(version: 2020_04_17_093432) do
|
||||||
|
|
||||||
# These are extensions that must be enabled in order to support this database
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "plpgsql"
|
enable_extension "plpgsql"
|
||||||
@@ -104,7 +104,6 @@ ActiveRecord::Schema.define(version: 2020_04_04_135009) do
|
|||||||
end
|
end
|
||||||
|
|
||||||
create_table "channel_facebook_pages", id: :serial, force: :cascade do |t|
|
create_table "channel_facebook_pages", id: :serial, force: :cascade do |t|
|
||||||
t.string "name", null: false
|
|
||||||
t.string "page_id", null: false
|
t.string "page_id", null: false
|
||||||
t.string "user_access_token", null: false
|
t.string "user_access_token", null: false
|
||||||
t.string "page_access_token", null: false
|
t.string "page_access_token", null: false
|
||||||
@@ -126,7 +125,6 @@ ActiveRecord::Schema.define(version: 2020_04_04_135009) do
|
|||||||
end
|
end
|
||||||
|
|
||||||
create_table "channel_twitter_profiles", force: :cascade do |t|
|
create_table "channel_twitter_profiles", force: :cascade do |t|
|
||||||
t.string "name"
|
|
||||||
t.string "profile_id", null: false
|
t.string "profile_id", null: false
|
||||||
t.string "twitter_access_token", null: false
|
t.string "twitter_access_token", null: false
|
||||||
t.string "twitter_access_token_secret", null: false
|
t.string "twitter_access_token_secret", null: false
|
||||||
@@ -137,13 +135,15 @@ ActiveRecord::Schema.define(version: 2020_04_04_135009) do
|
|||||||
end
|
end
|
||||||
|
|
||||||
create_table "channel_web_widgets", id: :serial, force: :cascade do |t|
|
create_table "channel_web_widgets", id: :serial, force: :cascade do |t|
|
||||||
t.string "website_name"
|
|
||||||
t.string "website_url"
|
t.string "website_url"
|
||||||
t.integer "account_id"
|
t.integer "account_id"
|
||||||
t.datetime "created_at", null: false
|
t.datetime "created_at", null: false
|
||||||
t.datetime "updated_at", null: false
|
t.datetime "updated_at", null: false
|
||||||
t.string "website_token"
|
t.string "website_token"
|
||||||
t.string "widget_color", default: "#1f93ff"
|
t.string "widget_color", default: "#1f93ff"
|
||||||
|
t.string "welcome_title"
|
||||||
|
t.string "welcome_tagline"
|
||||||
|
t.string "agent_away_message"
|
||||||
t.index ["website_token"], name: "index_channel_web_widgets_on_website_token", unique: true
|
t.index ["website_token"], name: "index_channel_web_widgets_on_website_token", unique: true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ AccountUser.create!(
|
|||||||
role: :administrator
|
role: :administrator
|
||||||
)
|
)
|
||||||
|
|
||||||
web_widget = Channel::WebWidget.create!(account: account, website_name: 'Acme', website_url: 'https://acme.inc')
|
web_widget = Channel::WebWidget.create!(account: account, website_url: 'https://acme.inc')
|
||||||
|
|
||||||
inbox = Inbox.create!(channel: web_widget, account: account, name: 'Acme Support')
|
inbox = Inbox.create!(channel: web_widget, account: account, name: 'Acme Support')
|
||||||
InboxMember.create!(user: user, inbox: inbox)
|
InboxMember.create!(user: user, inbox: inbox)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ require 'rails_helper'
|
|||||||
|
|
||||||
RSpec.describe 'Callbacks API', type: :request do
|
RSpec.describe 'Callbacks API', type: :request do
|
||||||
let(:account) { create(:account) }
|
let(:account) { create(:account) }
|
||||||
let(:valid_params) { attributes_for(:channel_facebook_page).merge(page_name: 'Test', inbox_name: 'Test Inbox') }
|
let(:valid_params) { attributes_for(:channel_facebook_page).merge(inbox_name: 'Test Inbox') }
|
||||||
let(:inbox) { create(:inbox, account: account) }
|
let(:inbox) { create(:inbox, account: account) }
|
||||||
let!(:facebook_page) { create(:channel_facebook_page, inbox: inbox, account: account) }
|
let!(:facebook_page) { create(:channel_facebook_page, inbox: inbox, account: account) }
|
||||||
|
|
||||||
|
|||||||
@@ -88,6 +88,44 @@ RSpec.describe 'Inboxes API', type: :request do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'POST /api/v1/accounts/{account.id}/inboxes' do
|
||||||
|
let(:inbox) { create(:inbox, account: account) }
|
||||||
|
|
||||||
|
context 'when it is an unauthenticated user' do
|
||||||
|
it 'returns unauthorized' do
|
||||||
|
post "/api/v1/accounts/#{account.id}/inboxes"
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:unauthorized)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when it is an authenticated user' do
|
||||||
|
let(:admin) { create(:user, account: account, role: :administrator) }
|
||||||
|
let(:valid_params) { { name: 'test', channel: { type: 'web_widget', website_url: 'test.com' } } }
|
||||||
|
|
||||||
|
it 'creates inbox' do
|
||||||
|
post "/api/v1/accounts/#{account.id}/inboxes",
|
||||||
|
headers: admin.create_new_auth_token,
|
||||||
|
params: valid_params,
|
||||||
|
as: :json
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
expect(response.body).to include('test.com')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'will not create inbox for agent' do
|
||||||
|
agent = create(:user, account: account, role: :agent)
|
||||||
|
|
||||||
|
post "/api/v1/accounts/#{account.id}/inboxes",
|
||||||
|
headers: agent.create_new_auth_token,
|
||||||
|
params: valid_params,
|
||||||
|
as: :json
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:unauthorized)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe 'PATCH /api/v1/accounts/{account.id}/inboxes/:id' do
|
describe 'PATCH /api/v1/accounts/{account.id}/inboxes/:id' do
|
||||||
let(:inbox) { create(:inbox, account: account) }
|
let(:inbox) { create(:inbox, account: account) }
|
||||||
|
|
||||||
@@ -101,7 +139,7 @@ RSpec.describe 'Inboxes API', type: :request do
|
|||||||
|
|
||||||
context 'when it is an authenticated user' do
|
context 'when it is an authenticated user' do
|
||||||
let(:admin) { create(:user, account: account, role: :administrator) }
|
let(:admin) { create(:user, account: account, role: :administrator) }
|
||||||
let(:valid_params) { { inbox: { enable_auto_assignment: false } } }
|
let(:valid_params) { { enable_auto_assignment: false, channel: { website_url: 'test.com' } } }
|
||||||
|
|
||||||
it 'updates inbox' do
|
it 'updates inbox' do
|
||||||
patch "/api/v1/accounts/#{account.id}/inboxes/#{inbox.id}",
|
patch "/api/v1/accounts/#{account.id}/inboxes/#{inbox.id}",
|
||||||
@@ -118,10 +156,11 @@ RSpec.describe 'Inboxes API', type: :request do
|
|||||||
expect(inbox.avatar.attached?).to eq(false)
|
expect(inbox.avatar.attached?).to eq(false)
|
||||||
file = fixture_file_upload(Rails.root.join('spec/assets/avatar.png'), 'image/png')
|
file = fixture_file_upload(Rails.root.join('spec/assets/avatar.png'), 'image/png')
|
||||||
patch "/api/v1/accounts/#{account.id}/inboxes/#{inbox.id}",
|
patch "/api/v1/accounts/#{account.id}/inboxes/#{inbox.id}",
|
||||||
params: { inbox: { avatar: file } },
|
params: valid_params.merge(avatar: file),
|
||||||
headers: admin.create_new_auth_token
|
headers: admin.create_new_auth_token
|
||||||
|
|
||||||
expect(response).to have_http_status(:success)
|
expect(response).to have_http_status(:success)
|
||||||
|
expect(response.body).to include('test.com')
|
||||||
inbox.reload
|
inbox.reload
|
||||||
expect(inbox.avatar.attached?).to eq(true)
|
expect(inbox.avatar.attached?).to eq(true)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,79 +0,0 @@
|
|||||||
require 'rails_helper'
|
|
||||||
|
|
||||||
RSpec.describe '/api/v1/accounts/{account.id}/widget/inboxes', type: :request do
|
|
||||||
let(:account) { create(:account) }
|
|
||||||
let(:inbox) { create(:inbox, account: account) }
|
|
||||||
let(:admin) { create(:user, account: account, role: :administrator) }
|
|
||||||
let(:agent) { create(:user, account: account, role: :agent) }
|
|
||||||
|
|
||||||
describe 'POST /api/v1/accounts/{account.id}/widget/inboxes' do
|
|
||||||
let(:params) { { website: { website_name: 'test', website_url: 'test.com', widget_color: '#eaeaea' } } }
|
|
||||||
|
|
||||||
context 'when unauthenticated user' do
|
|
||||||
it 'returns unauthorized' do
|
|
||||||
post "/api/v1/accounts/#{account.id}/widget/inboxes", params: params
|
|
||||||
expect(response).to have_http_status(:unauthorized)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when user is logged in' do
|
|
||||||
context 'with user as administrator' do
|
|
||||||
it 'creates inbox and returns website_token' do
|
|
||||||
post "/api/v1/accounts/#{account.id}/widget/inboxes", params: params, headers: admin.create_new_auth_token
|
|
||||||
|
|
||||||
expect(response).to have_http_status(:success)
|
|
||||||
json_response = JSON.parse(response.body)
|
|
||||||
|
|
||||||
expect(json_response['name']).to eq('test')
|
|
||||||
expect(json_response['website_token']).not_to be_empty
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'with user as agent' do
|
|
||||||
it 'returns unauthorized' do
|
|
||||||
post "/api/v1/accounts/#{account.id}/widget/inboxes",
|
|
||||||
params: params,
|
|
||||||
headers: agent.create_new_auth_token
|
|
||||||
|
|
||||||
expect(response).to have_http_status(:unauthorized)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe 'PATCH /api/v1/accounts/{account.id}/widget/inboxes/:id' do
|
|
||||||
let(:update_params) { { website: { widget_color: '#eaeaea' } } }
|
|
||||||
|
|
||||||
context 'when unauthenticated user' do
|
|
||||||
it 'returns unauthorized' do
|
|
||||||
patch "/api/v1/accounts/#{account.id}/widget/inboxes/#{inbox.channel_id}", params: update_params
|
|
||||||
expect(response).to have_http_status(:unauthorized)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when user is logged in' do
|
|
||||||
context 'with user as administrator' do
|
|
||||||
it 'updates website channel' do
|
|
||||||
patch "/api/v1/accounts/#{account.id}/widget/inboxes/#{inbox.channel_id}",
|
|
||||||
params: update_params,
|
|
||||||
headers: admin.create_new_auth_token
|
|
||||||
|
|
||||||
expect(response).to have_http_status(:success)
|
|
||||||
json_response = JSON.parse(response.body)
|
|
||||||
|
|
||||||
expect(json_response['widget_color']).to eq('#eaeaea')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'with user as agent' do
|
|
||||||
it 'returns unauthorized' do
|
|
||||||
patch "/api/v1/accounts/#{account.id}/widget/inboxes/#{inbox.channel_id}",
|
|
||||||
params: update_params,
|
|
||||||
headers: agent.create_new_auth_token
|
|
||||||
|
|
||||||
expect(response).to have_http_status(:unauthorized)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
FactoryBot.define do
|
FactoryBot.define do
|
||||||
factory :channel_widget, class: 'Channel::WebWidget' do
|
factory :channel_widget, class: 'Channel::WebWidget' do
|
||||||
sequence(:website_name) { |n| "Example Website #{n}" }
|
|
||||||
sequence(:website_url) { |n| "https://example-#{n}.com" }
|
sequence(:website_url) { |n| "https://example-#{n}.com" }
|
||||||
sequence(:widget_color, &:to_s)
|
sequence(:widget_color, &:to_s)
|
||||||
account
|
account
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
FactoryBot.define do
|
FactoryBot.define do
|
||||||
factory :channel_facebook_page, class: 'Channel::FacebookPage' do
|
factory :channel_facebook_page, class: 'Channel::FacebookPage' do
|
||||||
name { Faker::Name.name }
|
|
||||||
page_access_token { SecureRandom.uuid }
|
page_access_token { SecureRandom.uuid }
|
||||||
user_access_token { SecureRandom.uuid }
|
user_access_token { SecureRandom.uuid }
|
||||||
page_id { SecureRandom.uuid }
|
page_id { SecureRandom.uuid }
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
FactoryBot.define do
|
FactoryBot.define do
|
||||||
factory :channel_twitter_profile, class: 'Channel::TwitterProfile' do
|
factory :channel_twitter_profile, class: 'Channel::TwitterProfile' do
|
||||||
name { Faker::Name.name }
|
|
||||||
twitter_access_token { SecureRandom.uuid }
|
twitter_access_token { SecureRandom.uuid }
|
||||||
twitter_access_token_secret { SecureRandom.uuid }
|
twitter_access_token_secret { SecureRandom.uuid }
|
||||||
profile_id { SecureRandom.uuid }
|
profile_id { SecureRandom.uuid }
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
FactoryBot.define do
|
|
||||||
factory :facebook_page, class: 'Channel::FacebookPage' do
|
|
||||||
sequence(:page_id) { |n| n }
|
|
||||||
sequence(:user_access_token) { |n| "random-token-#{n}" }
|
|
||||||
sequence(:name) { |n| "Facebook Page #{n}" }
|
|
||||||
sequence(:page_access_token) { |n| "page-access-token-#{n}" }
|
|
||||||
account
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -11,7 +11,7 @@ describe Facebook::SendReplyService do
|
|||||||
let!(:account) { create(:account) }
|
let!(:account) { create(:account) }
|
||||||
let(:bot) { class_double('Bot').as_stubbed_const }
|
let(:bot) { class_double('Bot').as_stubbed_const }
|
||||||
let!(:widget_inbox) { create(:inbox, account: account) }
|
let!(:widget_inbox) { create(:inbox, account: account) }
|
||||||
let!(:facebook_channel) { create(:facebook_page, account: account) }
|
let!(:facebook_channel) { create(:channel_facebook_page, account: account) }
|
||||||
let!(:facebook_inbox) { create(:inbox, channel: facebook_channel, account: account) }
|
let!(:facebook_inbox) { create(:inbox, channel: facebook_channel, account: account) }
|
||||||
let!(:contact) { create(:contact, account: account) }
|
let!(:contact) { create(:contact, account: account) }
|
||||||
let(:contact_inbox) { create(:contact_inbox, contact: contact, inbox: facebook_inbox) }
|
let(:contact_inbox) { create(:contact_inbox, contact: contact, inbox: facebook_inbox) }
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
patch:
|
|
||||||
tags:
|
|
||||||
- Widget
|
|
||||||
operationId: widgetInboxUpdate
|
|
||||||
summary: Update a website inbox
|
|
||||||
description: Update widget color of an inbox
|
|
||||||
parameters:
|
|
||||||
- name: data
|
|
||||||
in: body
|
|
||||||
required: true
|
|
||||||
schema:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
website:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
widget_color:
|
|
||||||
type: string
|
|
||||||
description: A Hex-color string used to customize the widget
|
|
||||||
responses:
|
|
||||||
200:
|
|
||||||
description: Success
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/inbox'
|
|
||||||
404:
|
|
||||||
description: Inbox not found
|
|
||||||
403:
|
|
||||||
description: Access denied
|
|
||||||
Reference in New Issue
Block a user