Refactor: Inbox store, remove inboxes from sidebar (#387)
* Refactor: Inbox store, remove inboxes from sidebar * Add a new page for inbox settings * Show inboxes on sidebar * Add inbox_members API * Disable similar-code check * Fix codeclimate scss issues * Add widget_color update API and actions * Add specs for inbox store * Fix Facebook auth flow * Fix agent loading, inbox name
This commit is contained in:
parent
96f8070e79
commit
5ddc46c474
51 changed files with 1028 additions and 726 deletions
|
@ -11,7 +11,9 @@ plugins:
|
||||||
enabled: true
|
enabled: true
|
||||||
brakeman:
|
brakeman:
|
||||||
enabled: true
|
enabled: true
|
||||||
|
checks:
|
||||||
|
similar-code:
|
||||||
|
enabled: false
|
||||||
exclude_patterns:
|
exclude_patterns:
|
||||||
- "spec/"
|
- "spec/"
|
||||||
- "**/specs/"
|
- "**/specs/"
|
||||||
|
|
|
@ -1,17 +1,25 @@
|
||||||
class Api::V1::Widget::InboxesController < Api::BaseController
|
class Api::V1::Widget::InboxesController < Api::BaseController
|
||||||
before_action :authorize_request
|
before_action :authorize_request
|
||||||
|
before_action :set_web_widget_channel, only: [:update]
|
||||||
|
before_action :set_inbox, only: [:update]
|
||||||
|
|
||||||
def create
|
def create
|
||||||
ActiveRecord::Base.transaction do
|
ActiveRecord::Base.transaction do
|
||||||
channel = web_widgets.create!(
|
channel = web_widgets.create!(
|
||||||
website_name: permitted_params[:website_name],
|
website_name: permitted_params[:website][:website_name],
|
||||||
website_url: permitted_params[:website_url],
|
website_url: permitted_params[:website][:website_url],
|
||||||
widget_color: permitted_params[:widget_color]
|
widget_color: permitted_params[:website][:widget_color]
|
||||||
)
|
)
|
||||||
@inbox = inboxes.create!(name: permitted_params[:website_name], channel: channel)
|
@inbox = inboxes.create!(name: permitted_params[:website][:website_name], channel: channel)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
@channel.update!(
|
||||||
|
widget_color: permitted_params[:website][:widget_color]
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def authorize_request
|
def authorize_request
|
||||||
|
@ -26,7 +34,15 @@ class Api::V1::Widget::InboxesController < Api::BaseController
|
||||||
current_account.web_widgets
|
current_account.web_widgets
|
||||||
end
|
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
|
def permitted_params
|
||||||
params.fetch(:website).permit(:website_name, :website_url, :widget_color)
|
params.permit(:id, website: [:website_name, :website_url, :widget_color])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,7 +4,8 @@ const API_VERSION = `/api/v1`;
|
||||||
|
|
||||||
class ApiClient {
|
class ApiClient {
|
||||||
constructor(url) {
|
constructor(url) {
|
||||||
this.url = `${API_VERSION}/${url}`;
|
this.apiVersion = API_VERSION;
|
||||||
|
this.url = `${this.apiVersion}/${url}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
get() {
|
get() {
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
/* global axios */
|
|
||||||
import endPoints from './endPoints';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
getLabels() {
|
|
||||||
const urlData = endPoints('fetchLabels');
|
|
||||||
return axios.get(urlData.url);
|
|
||||||
},
|
|
||||||
|
|
||||||
getInboxes() {
|
|
||||||
const urlData = endPoints('fetchInboxes');
|
|
||||||
return axios.get(urlData.url);
|
|
||||||
},
|
|
||||||
|
|
||||||
deleteInbox(id) {
|
|
||||||
const urlData = endPoints('inbox').delete(id);
|
|
||||||
return axios.delete(urlData.url);
|
|
||||||
},
|
|
||||||
|
|
||||||
listInboxAgents(id) {
|
|
||||||
const urlData = endPoints('inbox').agents.get(id);
|
|
||||||
return axios.get(urlData.url);
|
|
||||||
},
|
|
||||||
|
|
||||||
updateInboxAgents(inboxId, agentList) {
|
|
||||||
const urlData = endPoints('inbox').agents.post();
|
|
||||||
return axios.post(urlData.url, {
|
|
||||||
user_ids: agentList,
|
|
||||||
inbox_id: inboxId,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
|
|
@ -19,6 +19,13 @@ class FBChannel extends ApiClient {
|
||||||
contact_id: contactId,
|
contact_id: contactId,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
create(params) {
|
||||||
|
return axios.post(
|
||||||
|
`${this.apiVersion}/callbacks/register_facebook_page`,
|
||||||
|
params
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default new FBChannel();
|
export default new FBChannel();
|
||||||
|
|
|
@ -5,19 +5,6 @@
|
||||||
import endPoints from './endPoints';
|
import endPoints from './endPoints';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
// Get Inbox related to the account
|
|
||||||
createChannel(channel, channelParams) {
|
|
||||||
const urlData = endPoints('createChannel')(channel, channelParams);
|
|
||||||
return axios.post(urlData.url, urlData.params);
|
|
||||||
},
|
|
||||||
|
|
||||||
addAgentsToChannel(inboxId, agentsId) {
|
|
||||||
const urlData = endPoints('addAgentsToChannel');
|
|
||||||
urlData.params.inbox_id = inboxId;
|
|
||||||
urlData.params.user_ids = agentsId;
|
|
||||||
return axios.post(urlData.url, urlData.params);
|
|
||||||
},
|
|
||||||
|
|
||||||
fetchFacebookPages(token) {
|
fetchFacebookPages(token) {
|
||||||
const urlData = endPoints('fetchFacebookPages');
|
const urlData = endPoints('fetchFacebookPages');
|
||||||
urlData.params.omniauth_token = token;
|
urlData.params.omniauth_token = token;
|
||||||
|
|
|
@ -24,26 +24,6 @@ const endPoints = {
|
||||||
params: { inbox_id: null },
|
params: { inbox_id: null },
|
||||||
},
|
},
|
||||||
|
|
||||||
fetchLabels: {
|
|
||||||
url: 'api/v1/labels.json',
|
|
||||||
},
|
|
||||||
|
|
||||||
fetchInboxes: {
|
|
||||||
url: 'api/v1/inboxes.json',
|
|
||||||
},
|
|
||||||
|
|
||||||
createChannel(channel, channelParams) {
|
|
||||||
return {
|
|
||||||
url: `api/v1/callbacks/register_${channel}_page.json`,
|
|
||||||
params: channelParams,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
addAgentsToChannel: {
|
|
||||||
url: 'api/v1/inbox_members.json',
|
|
||||||
params: { user_ids: [], inbox_id: null },
|
|
||||||
},
|
|
||||||
|
|
||||||
fetchFacebookPages: {
|
fetchFacebookPages: {
|
||||||
url: 'api/v1/callbacks/get_facebook_pages.json',
|
url: 'api/v1/callbacks/get_facebook_pages.json',
|
||||||
params: { omniauth_token: '' },
|
params: { omniauth_token: '' },
|
||||||
|
@ -69,26 +49,6 @@ const endPoints = {
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
inbox: {
|
|
||||||
delete(id) {
|
|
||||||
return {
|
|
||||||
url: `/api/v1/inboxes/${id}`,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
agents: {
|
|
||||||
get(id) {
|
|
||||||
return {
|
|
||||||
url: `/api/v1/inbox_members/${id}.json`,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
post() {
|
|
||||||
return {
|
|
||||||
url: '/api/v1/inbox_members.json',
|
|
||||||
};
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default page => {
|
export default page => {
|
||||||
|
|
17
app/javascript/dashboard/api/inboxMembers.js
Normal file
17
app/javascript/dashboard/api/inboxMembers.js
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
/* global axios */
|
||||||
|
import ApiClient from './ApiClient';
|
||||||
|
|
||||||
|
class InboxMembers extends ApiClient {
|
||||||
|
constructor() {
|
||||||
|
super('inbox_members');
|
||||||
|
}
|
||||||
|
|
||||||
|
create({ inboxId, agentList }) {
|
||||||
|
return axios.post(this.url, {
|
||||||
|
inbox_id: inboxId,
|
||||||
|
user_ids: agentList,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new InboxMembers();
|
9
app/javascript/dashboard/api/inboxes.js
Normal file
9
app/javascript/dashboard/api/inboxes.js
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import ApiClient from './ApiClient';
|
||||||
|
|
||||||
|
class Inboxes extends ApiClient {
|
||||||
|
constructor() {
|
||||||
|
super('inboxes');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new Inboxes();
|
13
app/javascript/dashboard/api/specs/inboxes.spec.js
Normal file
13
app/javascript/dashboard/api/specs/inboxes.spec.js
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import inboxes from '../inboxes';
|
||||||
|
import ApiClient from '../ApiClient';
|
||||||
|
|
||||||
|
describe('#AgentAPI', () => {
|
||||||
|
it('creates correct instance', () => {
|
||||||
|
expect(inboxes).toBeInstanceOf(ApiClient);
|
||||||
|
expect(inboxes).toHaveProperty('get');
|
||||||
|
expect(inboxes).toHaveProperty('show');
|
||||||
|
expect(inboxes).toHaveProperty('create');
|
||||||
|
expect(inboxes).toHaveProperty('update');
|
||||||
|
expect(inboxes).toHaveProperty('delete');
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,3 +1,10 @@
|
||||||
|
.settings {
|
||||||
|
overflow: auto;
|
||||||
|
|
||||||
|
.page-top-bar {
|
||||||
|
@include padding($space-normal $space-two $zero);
|
||||||
|
}
|
||||||
|
}
|
||||||
// Conversation header - Light BG
|
// Conversation header - Light BG
|
||||||
.settings-header {
|
.settings-header {
|
||||||
@include padding($space-small $space-normal);
|
@include padding($space-small $space-normal);
|
||||||
|
@ -196,51 +203,23 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-modal {
|
.settings--content {
|
||||||
height: 80%;
|
@include margin($space-small $space-medium);
|
||||||
max-width: 1040px;
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
.delete-wrapper {
|
.title {
|
||||||
position: absolute;
|
font-weight: $font-weight-medium;
|
||||||
bottom: 0;
|
|
||||||
width: 100%;
|
|
||||||
@include flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: space-between;
|
|
||||||
@include padding($space-normal $space-large);
|
|
||||||
|
|
||||||
a {
|
|
||||||
margin-left: $space-normal;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings--content {
|
.code {
|
||||||
@include margin($space-medium);
|
max-height: $space-mega;
|
||||||
|
overflow: scroll;
|
||||||
|
white-space: nowrap;
|
||||||
|
@include padding($space-one);
|
||||||
|
background: $color-background;
|
||||||
|
|
||||||
.title {
|
code {
|
||||||
font-weight: $font-weight-medium;
|
background: transparent;
|
||||||
}
|
border: 0;
|
||||||
|
|
||||||
.code {
|
|
||||||
max-height: $space-mega;
|
|
||||||
overflow: scroll;
|
|
||||||
white-space: nowrap;
|
|
||||||
@include padding($space-one);
|
|
||||||
background: $color-background;
|
|
||||||
|
|
||||||
code {
|
|
||||||
background: transparent;
|
|
||||||
border: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.agent-wrapper {
|
|
||||||
@include margin($space-medium);
|
|
||||||
|
|
||||||
.title {
|
|
||||||
font-weight: $font-weight-medium;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,15 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.page-top-bar {
|
||||||
|
@include padding($zero $space-two);
|
||||||
|
|
||||||
|
img {
|
||||||
|
max-height: 6rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.modal-container {
|
.modal-container {
|
||||||
background-color: $color-white;
|
background-color: $color-white;
|
||||||
border-radius: $space-small;
|
border-radius: $space-small;
|
||||||
|
@ -35,13 +44,6 @@
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 60rem;
|
width: 60rem;
|
||||||
|
|
||||||
.page-top-bar {
|
|
||||||
@include padding($zero $space-two);
|
|
||||||
|
|
||||||
img {
|
|
||||||
max-height: 6rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-box {
|
.content-box {
|
||||||
@include padding($zero);
|
@include padding($zero);
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<div class="chat-list__top">
|
<div class="chat-list__top">
|
||||||
<h1 class="page-title">
|
<h1 class="page-title">
|
||||||
<woot-sidemenu-icon />
|
<woot-sidemenu-icon />
|
||||||
{{ getInboxName }}
|
{{ inbox.name || pageTitle }}
|
||||||
</h1>
|
</h1>
|
||||||
<chat-filter @statusFilterChange="getDataForStatusTab" />
|
<chat-filter @statusFilterChange="getDataForStatusTab" />
|
||||||
</div>
|
</div>
|
||||||
|
@ -53,6 +53,11 @@ import conversationMixin from '../mixins/conversations';
|
||||||
import wootConstants from '../constants';
|
import wootConstants from '../constants';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
components: {
|
||||||
|
ChatTypeTabs,
|
||||||
|
ConversationCard,
|
||||||
|
ChatFilter,
|
||||||
|
},
|
||||||
mixins: [timeMixin, conversationMixin],
|
mixins: [timeMixin, conversationMixin],
|
||||||
props: ['conversationInbox', 'pageTitle'],
|
props: ['conversationInbox', 'pageTitle'],
|
||||||
data() {
|
data() {
|
||||||
|
@ -61,25 +66,12 @@ export default {
|
||||||
activeStatus: 0,
|
activeStatus: 0,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
mounted() {
|
|
||||||
this.$watch('$store.state.route', () => {
|
|
||||||
if (this.$store.state.route.name !== 'inbox_conversation') {
|
|
||||||
this.$store.dispatch('emptyAllConversations');
|
|
||||||
this.fetchData();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.$store.dispatch('emptyAllConversations');
|
|
||||||
this.fetchData();
|
|
||||||
this.$store.dispatch('agents/get');
|
|
||||||
},
|
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
chatLists: 'getAllConversations',
|
chatLists: 'getAllConversations',
|
||||||
mineChatsList: 'getMineChats',
|
mineChatsList: 'getMineChats',
|
||||||
allChatList: 'getAllStatusChats',
|
allChatList: 'getAllStatusChats',
|
||||||
unAssignedChatsList: 'getUnAssignedChats',
|
unAssignedChatsList: 'getUnAssignedChats',
|
||||||
inboxesList: 'getInboxesList',
|
|
||||||
chatListLoading: 'getChatListLoadingStatus',
|
chatListLoading: 'getChatListLoadingStatus',
|
||||||
currentUserID: 'getCurrentUserID',
|
currentUserID: 'getCurrentUserID',
|
||||||
activeInbox: 'getSelectedInbox',
|
activeInbox: 'getSelectedInbox',
|
||||||
|
@ -92,12 +84,8 @@ export default {
|
||||||
count: this.convStats[item.KEY] || 0,
|
count: this.convStats[item.KEY] || 0,
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
getInboxName() {
|
inbox() {
|
||||||
const inboxId = Number(this.activeInbox);
|
return this.$store.getters['inboxes/getInbox'](this.activeInbox);
|
||||||
const [stateInbox] = this.inboxesList.filter(
|
|
||||||
inbox => inbox.channel_id === inboxId
|
|
||||||
);
|
|
||||||
return !stateInbox ? this.pageTitle : stateInbox.label;
|
|
||||||
},
|
},
|
||||||
getToggleStatus() {
|
getToggleStatus() {
|
||||||
if (this.toggleType) {
|
if (this.toggleType) {
|
||||||
|
@ -106,6 +94,18 @@ export default {
|
||||||
return 'Resolved';
|
return 'Resolved';
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
mounted() {
|
||||||
|
this.$watch('$store.state.route', () => {
|
||||||
|
if (this.$store.state.route.name !== 'inbox_conversation') {
|
||||||
|
this.$store.dispatch('emptyAllConversations');
|
||||||
|
this.fetchData();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.$store.dispatch('emptyAllConversations');
|
||||||
|
this.fetchData();
|
||||||
|
this.$store.dispatch('agents/get');
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
fetchData() {
|
fetchData() {
|
||||||
if (this.chatLists.length === 0) {
|
if (this.chatLists.length === 0) {
|
||||||
|
@ -149,12 +149,6 @@ export default {
|
||||||
return sorted;
|
return sorted;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
components: {
|
|
||||||
ChatTypeTabs,
|
|
||||||
ConversationCard,
|
|
||||||
ChatFilter,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="column page-top-bar">
|
<div class="column page-top-bar">
|
||||||
<img :src="headerImage" alt="No image" v-if="headerImage"/>
|
<img v-if="headerImage" :src="headerImage" alt="No image" />
|
||||||
<h2 class="page-sub-title">
|
<h2 class="page-sub-title">
|
||||||
{{headerTitle}}
|
{{ headerTitle }}
|
||||||
</h2>
|
</h2>
|
||||||
<p class="small-12 column">
|
<p v-if="headerContent" class="small-12 column">
|
||||||
{{headerContent}}
|
{{ headerContent }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div v-if="buttonText" class="medium-4 text-right">
|
<div v-if="buttonText" class="medium-4 text-right">
|
||||||
<woot-submit-button
|
<woot-submit-button
|
||||||
class="small"
|
class="default"
|
||||||
:button-text="buttonText"
|
:button-text="buttonText"
|
||||||
:loading="isUpdating"
|
:loading="isUpdating"
|
||||||
@click="onClick()"
|
@click="onClick()"
|
||||||
|
@ -32,7 +32,7 @@ export default {
|
||||||
},
|
},
|
||||||
buttonText: {
|
buttonText: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
default: '',
|
||||||
},
|
},
|
||||||
isUpdating: {
|
isUpdating: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
|
@ -51,8 +51,19 @@ export default {
|
||||||
@import '~dashboard/assets/scss/variables';
|
@import '~dashboard/assets/scss/variables';
|
||||||
|
|
||||||
.settings--form--header {
|
.settings--form--header {
|
||||||
|
align-items: center;
|
||||||
border-bottom: 1px solid $color-border;
|
border-bottom: 1px solid $color-border;
|
||||||
|
display: flex;
|
||||||
margin-bottom: $space-normal;
|
margin-bottom: $space-normal;
|
||||||
padding: $space-normal 0;
|
padding: $space-normal 0;
|
||||||
|
|
||||||
|
.button {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
margin-bottom: 0;
|
||||||
|
font-size: $font-size-default;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -13,6 +13,12 @@
|
||||||
:key="item.toState"
|
:key="item.toState"
|
||||||
:menu-item="item"
|
:menu-item="item"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<sidebar-item
|
||||||
|
v-if="shouldShowInboxes"
|
||||||
|
:key="inboxSection.toState"
|
||||||
|
:menu-item="inboxSection"
|
||||||
|
/>
|
||||||
</transition-group>
|
</transition-group>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -41,7 +47,7 @@
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
<div class="current-user" @click.prevent="showOptions()">
|
<div class="current-user" @click.prevent="showOptions()">
|
||||||
<thumbnail :src="gravatarUrl()" :username="currentUser.name"/>
|
<thumbnail :src="gravatarUrl()" :username="currentUser.name" />
|
||||||
<div class="current-user--data">
|
<div class="current-user--data">
|
||||||
<h3 class="current-user--name">
|
<h3 class="current-user--name">
|
||||||
{{ currentUser.name }}
|
{{ currentUser.name }}
|
||||||
|
@ -50,9 +56,8 @@
|
||||||
{{ currentUser.role }}
|
{{ currentUser.role }}
|
||||||
</h5>
|
</h5>
|
||||||
</div>
|
</div>
|
||||||
<span
|
<span class="current-user--options icon ion-android-more-vertical">
|
||||||
class="current-user--options icon ion-android-more-vertical"
|
</span>
|
||||||
></span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
|
@ -69,12 +74,19 @@ import SidebarItem from './SidebarItem';
|
||||||
import WootStatusBar from '../widgets/StatusBar';
|
import WootStatusBar from '../widgets/StatusBar';
|
||||||
import { frontendURL } from '../../helper/URLHelper';
|
import { frontendURL } from '../../helper/URLHelper';
|
||||||
import Thumbnail from '../widgets/Thumbnail';
|
import Thumbnail from '../widgets/Thumbnail';
|
||||||
|
import sidemenuItems from '../../i18n/default-sidebar';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
components: {
|
||||||
|
SidebarItem,
|
||||||
|
WootStatusBar,
|
||||||
|
Thumbnail,
|
||||||
|
},
|
||||||
mixins: [clickaway, adminMixin],
|
mixins: [clickaway, adminMixin],
|
||||||
props: {
|
props: {
|
||||||
route: {
|
route: {
|
||||||
type: String,
|
type: String,
|
||||||
|
default: '',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
|
@ -82,27 +94,22 @@ export default {
|
||||||
showOptionsMenu: false,
|
showOptionsMenu: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
mounted() {
|
|
||||||
// this.$store.dispatch('fetchLabels');
|
|
||||||
this.$store.dispatch('fetchInboxes');
|
|
||||||
},
|
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
sidebarList: 'getMenuItems',
|
|
||||||
daysLeft: 'getTrialLeft',
|
daysLeft: 'getTrialLeft',
|
||||||
subscriptionData: 'getSubscription',
|
subscriptionData: 'getSubscription',
|
||||||
|
inboxes: 'inboxes/getInboxes',
|
||||||
}),
|
}),
|
||||||
accessibleMenuItems() {
|
accessibleMenuItems() {
|
||||||
const currentRoute = this.$store.state.route.name;
|
|
||||||
// get all keys in menuGroup
|
// get all keys in menuGroup
|
||||||
const groupKey = Object.keys(this.sidebarList);
|
const groupKey = Object.keys(sidemenuItems);
|
||||||
|
|
||||||
let menuItems = [];
|
let menuItems = [];
|
||||||
// Iterate over menuGroup to find the correct group
|
// Iterate over menuGroup to find the correct group
|
||||||
for (let i = 0; i < groupKey.length; i += 1) {
|
for (let i = 0; i < groupKey.length; i += 1) {
|
||||||
const groupItem = this.sidebarList[groupKey[i]];
|
const groupItem = sidemenuItems[groupKey[i]];
|
||||||
// Check if current route is included
|
// Check if current route is included
|
||||||
const isRouteIncluded = groupItem.routes.includes(currentRoute);
|
const isRouteIncluded = groupItem.routes.includes(this.currentRoute);
|
||||||
if (isRouteIncluded) {
|
if (isRouteIncluded) {
|
||||||
menuItems = Object.values(groupItem.menuItems);
|
menuItems = Object.values(groupItem.menuItems);
|
||||||
}
|
}
|
||||||
|
@ -114,6 +121,29 @@ export default {
|
||||||
|
|
||||||
return this.filterMenuItemsByRole(menuItems);
|
return this.filterMenuItemsByRole(menuItems);
|
||||||
},
|
},
|
||||||
|
currentRoute() {
|
||||||
|
return this.$store.state.route.name;
|
||||||
|
},
|
||||||
|
shouldShowInboxes() {
|
||||||
|
return sidemenuItems.common.routes.includes(this.currentRoute);
|
||||||
|
},
|
||||||
|
inboxSection() {
|
||||||
|
return {
|
||||||
|
icon: 'ion-folder',
|
||||||
|
label: 'Inboxes',
|
||||||
|
hasSubMenu: true,
|
||||||
|
newLink: true,
|
||||||
|
key: 'inbox',
|
||||||
|
cssClass: 'menu-title align-justify',
|
||||||
|
toState: frontendURL('settings/inboxes'),
|
||||||
|
toStateName: 'settings_inbox_list',
|
||||||
|
children: this.inboxes.map(inbox => ({
|
||||||
|
id: inbox.id,
|
||||||
|
label: inbox.name,
|
||||||
|
toState: frontendURL(`inbox/${inbox.id}`),
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
},
|
||||||
currentUser() {
|
currentUser() {
|
||||||
return Auth.getCurrentUser();
|
return Auth.getCurrentUser();
|
||||||
},
|
},
|
||||||
|
@ -140,7 +170,9 @@ export default {
|
||||||
return `${this.daysLeft} ${this.$t('APP_GLOBAL.TRIAL_MESSAGE')}`;
|
return `${this.daysLeft} ${this.$t('APP_GLOBAL.TRIAL_MESSAGE')}`;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
mounted() {
|
||||||
|
this.$store.dispatch('inboxes/get');
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
gravatarUrl() {
|
gravatarUrl() {
|
||||||
const hash = md5(this.currentUser.email);
|
const hash = md5(this.currentUser.email);
|
||||||
|
@ -165,11 +197,5 @@ export default {
|
||||||
this.showOptionsMenu = !this.showOptionsMenu;
|
this.showOptionsMenu = !this.showOptionsMenu;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
components: {
|
|
||||||
SidebarItem,
|
|
||||||
WootStatusBar,
|
|
||||||
Thumbnail,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -63,7 +63,7 @@ export default {
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
currentChat: 'getSelectedChat',
|
currentChat: 'getSelectedChat',
|
||||||
inboxesList: 'getInboxesList',
|
inboxesList: 'inboxes/getInboxes',
|
||||||
activeInbox: 'getSelectedInbox',
|
activeInbox: 'getSelectedInbox',
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
@ -107,11 +107,10 @@ export default {
|
||||||
`;
|
`;
|
||||||
},
|
},
|
||||||
getEmojiSVG,
|
getEmojiSVG,
|
||||||
|
|
||||||
inboxName(inboxId) {
|
inboxName(inboxId) {
|
||||||
const [stateInbox] = this.inboxesList.filter(
|
const stateInbox = this.$store.getters['inboxes/getInbox'](inboxId);
|
||||||
inbox => inbox.channel_id === inboxId
|
return stateInbox.name || '';
|
||||||
);
|
|
||||||
return !stateInbox ? '' : stateInbox.label;
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="columns full-height conv-empty-state">
|
<div class="columns full-height conv-empty-state">
|
||||||
<woot-loading-state
|
<woot-loading-state
|
||||||
v-if="fetchingInboxes || loadingChatList"
|
v-if="uiFlags.isFetching || loadingChatList"
|
||||||
:message="loadingIndicatorMessage"
|
:message="loadingIndicatorMessage"
|
||||||
/>
|
/>
|
||||||
<!-- Show empty state images if not loading -->
|
<!-- Show empty state images if not loading -->
|
||||||
<div v-if="!fetchingInboxes && !loadingChatList" class="current-chat">
|
<div v-if="!uiFlags.isFetching && !loadingChatList" class="current-chat">
|
||||||
<!-- No inboxes attached -->
|
<!-- No inboxes attached -->
|
||||||
<div v-if="!inboxesList.length">
|
<div v-if="!inboxesList.length">
|
||||||
<img src="~dashboard/assets/images/inboxes.svg" alt="No Inboxes" />
|
<img src="~dashboard/assets/images/inboxes.svg" alt="No Inboxes" />
|
||||||
|
@ -49,12 +49,12 @@ export default {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
currentChat: 'getSelectedChat',
|
currentChat: 'getSelectedChat',
|
||||||
allConversations: 'getAllConversations',
|
allConversations: 'getAllConversations',
|
||||||
inboxesList: 'getInboxesList',
|
inboxesList: 'inboxes/getInboxes',
|
||||||
fetchingInboxes: 'getInboxLoadingStatus',
|
uiFlags: 'inboxes/getUIFlags',
|
||||||
loadingChatList: 'getChatListLoadingStatus',
|
loadingChatList: 'getChatListLoadingStatus',
|
||||||
}),
|
}),
|
||||||
loadingIndicatorMessage() {
|
loadingIndicatorMessage() {
|
||||||
if (this.fetchingInboxes) {
|
if (this.uiFlags.isFetching) {
|
||||||
return this.$t('CONVERSATION.LOADING_INBOXES');
|
return this.$t('CONVERSATION.LOADING_INBOXES');
|
||||||
}
|
}
|
||||||
return this.$t('CONVERSATION.LOADING_CONVERSATIONS');
|
return this.$t('CONVERSATION.LOADING_CONVERSATIONS');
|
||||||
|
|
|
@ -75,10 +75,9 @@ export default {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
currentChat: 'getSelectedChat',
|
currentChat: 'getSelectedChat',
|
||||||
allConversations: 'getAllConversations',
|
allConversations: 'getAllConversations',
|
||||||
inboxesList: 'getInboxesList',
|
inboxesList: 'inboxes/getInboxes',
|
||||||
listLoadingStatus: 'getAllMessagesLoaded',
|
listLoadingStatus: 'getAllMessagesLoaded',
|
||||||
getUnreadCount: 'getUnreadCount',
|
getUnreadCount: 'getUnreadCount',
|
||||||
fetchingInboxes: 'getInboxLoadingStatus',
|
|
||||||
loadingChatList: 'getChatListLoadingStatus',
|
loadingChatList: 'getChatListLoadingStatus',
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
@ -99,7 +98,7 @@ export default {
|
||||||
} else {
|
} else {
|
||||||
[stateInbox] = this.inboxesList;
|
[stateInbox] = this.inboxesList;
|
||||||
}
|
}
|
||||||
return !stateInbox ? 0 : stateInbox.pageId;
|
return !stateInbox ? 0 : stateInbox.page_id;
|
||||||
},
|
},
|
||||||
// Get current FB Page ID link
|
// Get current FB Page ID link
|
||||||
linkToMessage() {
|
linkToMessage() {
|
||||||
|
|
|
@ -1,109 +1,97 @@
|
||||||
import { frontendURL } from '../helper/URLHelper';
|
import { frontendURL } from '../helper/URLHelper';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
menuGroup: {
|
common: {
|
||||||
common: {
|
routes: [
|
||||||
routes: [
|
'home',
|
||||||
'home',
|
'inbox_dashboard',
|
||||||
'inbox_dashboard',
|
'inbox_conversation',
|
||||||
'inbox_conversation',
|
'settings_account_reports',
|
||||||
'settings_account_reports',
|
'billing_deactivated',
|
||||||
'billing_deactivated',
|
],
|
||||||
],
|
menuItems: {
|
||||||
menuItems: {
|
assignedToMe: {
|
||||||
assignedToMe: {
|
icon: 'ion-chatbox-working',
|
||||||
icon: 'ion-chatbox-working',
|
label: 'Conversations',
|
||||||
label: 'Conversations',
|
hasSubMenu: false,
|
||||||
hasSubMenu: false,
|
key: '',
|
||||||
key: '',
|
toState: frontendURL('dashboard'),
|
||||||
toState: frontendURL('dashboard'),
|
toolTip: 'Conversation from all subscribed inboxes',
|
||||||
toolTip: 'Conversation from all subscribed inboxes',
|
toStateName: 'home',
|
||||||
toStateName: 'home',
|
},
|
||||||
},
|
report: {
|
||||||
report: {
|
icon: 'ion-arrow-graph-up-right',
|
||||||
icon: 'ion-arrow-graph-up-right',
|
label: 'Reports',
|
||||||
label: 'Reports',
|
hasSubMenu: false,
|
||||||
hasSubMenu: false,
|
toState: frontendURL('reports'),
|
||||||
toState: frontendURL('reports'),
|
toStateName: 'settings_account_reports',
|
||||||
toStateName: 'settings_account_reports',
|
},
|
||||||
},
|
settings: {
|
||||||
settings: {
|
icon: 'ion-settings',
|
||||||
icon: 'ion-settings',
|
label: 'Settings',
|
||||||
label: 'Settings',
|
hasSubMenu: false,
|
||||||
hasSubMenu: false,
|
toState: frontendURL('settings'),
|
||||||
toState: frontendURL('settings'),
|
toStateName: 'settings_home',
|
||||||
toStateName: 'settings_home',
|
|
||||||
},
|
|
||||||
inbox: {
|
|
||||||
icon: 'ion-folder',
|
|
||||||
label: 'Inboxes',
|
|
||||||
hasSubMenu: true,
|
|
||||||
newLink: true,
|
|
||||||
key: 'inbox',
|
|
||||||
cssClass: 'menu-title align-justify',
|
|
||||||
toState: frontendURL('settings/inboxes'),
|
|
||||||
toStateName: 'settings_inbox_list',
|
|
||||||
children: [],
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
settings: {
|
},
|
||||||
routes: [
|
settings: {
|
||||||
'agent_list',
|
routes: [
|
||||||
'agent_new',
|
'agent_list',
|
||||||
'canned_list',
|
'agent_new',
|
||||||
'canned_new',
|
'canned_list',
|
||||||
'settings_inbox',
|
'canned_new',
|
||||||
'settings_inbox_new',
|
'settings_inbox',
|
||||||
'settings_inbox_list',
|
'settings_inbox_new',
|
||||||
'settings_inboxes_page_channel',
|
'settings_inbox_list',
|
||||||
'settings_inboxes_add_agents',
|
'settings_inbox_show',
|
||||||
'settings_inbox_finish',
|
'settings_inboxes_page_channel',
|
||||||
'billing',
|
'settings_inboxes_add_agents',
|
||||||
],
|
'settings_inbox_finish',
|
||||||
menuItems: {
|
'billing',
|
||||||
back: {
|
],
|
||||||
icon: 'ion-ios-arrow-back',
|
menuItems: {
|
||||||
label: 'Home',
|
back: {
|
||||||
hasSubMenu: false,
|
icon: 'ion-ios-arrow-back',
|
||||||
toStateName: 'home',
|
label: 'Home',
|
||||||
toState: frontendURL('dashboard'),
|
hasSubMenu: false,
|
||||||
},
|
toStateName: 'home',
|
||||||
agents: {
|
toState: frontendURL('dashboard'),
|
||||||
icon: 'ion-person-stalker',
|
},
|
||||||
label: 'Agents',
|
agents: {
|
||||||
hasSubMenu: false,
|
icon: 'ion-person-stalker',
|
||||||
toState: frontendURL('settings/agents/list'),
|
label: 'Agents',
|
||||||
toStateName: 'agent_list',
|
hasSubMenu: false,
|
||||||
},
|
toState: frontendURL('settings/agents/list'),
|
||||||
inboxes: {
|
toStateName: 'agent_list',
|
||||||
icon: 'ion-archive',
|
},
|
||||||
label: 'Inboxes',
|
inboxes: {
|
||||||
hasSubMenu: false,
|
icon: 'ion-archive',
|
||||||
toState: frontendURL('settings/inboxes/list'),
|
label: 'Inboxes',
|
||||||
toStateName: 'settings_inbox_list',
|
hasSubMenu: false,
|
||||||
},
|
toState: frontendURL('settings/inboxes/list'),
|
||||||
cannedResponses: {
|
toStateName: 'settings_inbox_list',
|
||||||
icon: 'ion-chatbox-working',
|
},
|
||||||
label: 'Canned Responses',
|
cannedResponses: {
|
||||||
hasSubMenu: false,
|
icon: 'ion-chatbox-working',
|
||||||
toState: frontendURL('settings/canned-response/list'),
|
label: 'Canned Responses',
|
||||||
toStateName: 'canned_list',
|
hasSubMenu: false,
|
||||||
},
|
toState: frontendURL('settings/canned-response/list'),
|
||||||
billing: {
|
toStateName: 'canned_list',
|
||||||
icon: 'ion-card',
|
},
|
||||||
label: 'Billing',
|
billing: {
|
||||||
hasSubMenu: false,
|
icon: 'ion-card',
|
||||||
toState: frontendURL('settings/billing'),
|
label: 'Billing',
|
||||||
toStateName: 'billing',
|
hasSubMenu: false,
|
||||||
},
|
toState: frontendURL('settings/billing'),
|
||||||
account: {
|
toStateName: 'billing',
|
||||||
icon: 'ion-beer',
|
},
|
||||||
label: 'Account Settings',
|
account: {
|
||||||
hasSubMenu: false,
|
icon: 'ion-beer',
|
||||||
toState: frontendURL('settings/account'),
|
label: 'Account Settings',
|
||||||
toStateName: 'account',
|
hasSubMenu: false,
|
||||||
},
|
toState: frontendURL('settings/account'),
|
||||||
|
toStateName: 'account',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -27,7 +27,8 @@
|
||||||
"PLACEHOLDER": "Enter your website domain (eg: acme.com)"
|
"PLACEHOLDER": "Enter your website domain (eg: acme.com)"
|
||||||
},
|
},
|
||||||
"WIDGET_COLOR": {
|
"WIDGET_COLOR": {
|
||||||
"LABEL": "Widget Color"
|
"LABEL": "Widget Color",
|
||||||
|
"PLACEHOLDER": "Update the widget color used in widget"
|
||||||
},
|
},
|
||||||
"SUBMIT_BUTTON":"Create inbox"
|
"SUBMIT_BUTTON":"Create inbox"
|
||||||
},
|
},
|
||||||
|
@ -52,10 +53,11 @@
|
||||||
"LOADING_FB": "Authenticating you with Facebook...",
|
"LOADING_FB": "Authenticating you with Facebook...",
|
||||||
"ERROR_FB_AUTH": "Something went wrong, Please refresh page...",
|
"ERROR_FB_AUTH": "Something went wrong, Please refresh page...",
|
||||||
"CREATING_CHANNEL": "Creating your Inbox...",
|
"CREATING_CHANNEL": "Creating your Inbox...",
|
||||||
"TITLE": "Configure Inbox Deatails",
|
"TITLE": "Configure Inbox Details",
|
||||||
"DESC": "an addendum to this post, you can absolutely support what I’m doing by working with me at Reach by Creatomic. Get in touch: jon@creatomic.co for content, podcasts, marketing campaigns — we do a lot and we do it well. If you can help me hit that monthly rev. target by letting me help you find more customers and make more money, that’s a win win."
|
"DESC": ""
|
||||||
},
|
},
|
||||||
"AGENTS": {
|
"AGENTS": {
|
||||||
|
"BUTTON_TEXT": "Add agents",
|
||||||
"ADD_AGENTS": "Adding Agents to your Inbox..."
|
"ADD_AGENTS": "Adding Agents to your Inbox..."
|
||||||
},
|
},
|
||||||
"FINISH": {
|
"FINISH": {
|
||||||
|
@ -66,6 +68,12 @@
|
||||||
},
|
},
|
||||||
"REAUTH": "Reauthorize",
|
"REAUTH": "Reauthorize",
|
||||||
"VIEW": "View",
|
"VIEW": "View",
|
||||||
|
"EDIT": {
|
||||||
|
"API": {
|
||||||
|
"SUCCESS_MESSAGE": "Widget color updated successfully",
|
||||||
|
"ERROR_MESSAGE": "Could not update widget color. Please try again later."
|
||||||
|
}
|
||||||
|
},
|
||||||
"DELETE": {
|
"DELETE": {
|
||||||
"BUTTON_TEXT": "Delete",
|
"BUTTON_TEXT": "Delete",
|
||||||
"CONFIRM": {
|
"CONFIRM": {
|
||||||
|
|
|
@ -39,7 +39,6 @@ export default {
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
menuItems: 'getMenuItems',
|
|
||||||
chatList: 'getAllConversations',
|
chatList: 'getAllConversations',
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<h2 class="page-sub-title">
|
<h2 class="page-sub-title">
|
||||||
{{headerTitle}}
|
{{ headerTitle }}
|
||||||
</h2>
|
</h2>
|
||||||
<p class="small-12 column" v-html="headerContent">
|
<p class="small-12 column" v-html="headerContent"></p>
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="wizard-body columns content-box small-9">
|
<div class="wizard-body columns content-box small-9">
|
||||||
<loading-state v-if="showLoader" :message="emptyStateMessage">
|
<form class="row" @submit.prevent="addAgents()">
|
||||||
</loading-state>
|
|
||||||
<form v-if="!showLoader" class="row" @submit.prevent="addAgents()">
|
|
||||||
<div class="medium-12 columns">
|
<div class="medium-12 columns">
|
||||||
<page-header
|
<page-header
|
||||||
:header-title="$t('INBOX_MGMT.ADD.AGENTS.TITLE')"
|
:header-title="$t('INBOX_MGMT.ADD.AGENTS.TITLE')"
|
||||||
|
@ -31,8 +29,11 @@
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="medium-12 columns text-right">
|
<div class="medium-12 columns">
|
||||||
<input type="submit" value="Create Inbox" class="button" />
|
<woot-submit-button
|
||||||
|
:button-text="$t('INBOX_MGMT.AGENTS.BUTTON_TEXT')"
|
||||||
|
:loading="isCreating"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
@ -44,15 +45,13 @@
|
||||||
/* global bus */
|
/* global bus */
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
|
|
||||||
import ChannelApi from '../../../../api/channels';
|
import InboxMembersAPI from '../../../../api/inboxMembers';
|
||||||
import router from '../../../index';
|
import router from '../../../index';
|
||||||
import PageHeader from '../SettingsSubPageHeader';
|
import PageHeader from '../SettingsSubPageHeader';
|
||||||
import LoadingState from '../../../../components/widgets/LoadingState';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
PageHeader,
|
PageHeader,
|
||||||
LoadingState,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
validations: {
|
validations: {
|
||||||
|
@ -65,9 +64,8 @@ export default {
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
emptyStateMessage: this.$t('INBOX_MGMT.AGENTS.ADD_AGENTS'),
|
|
||||||
showLoader: false,
|
|
||||||
selectedAgents: [],
|
selectedAgents: [],
|
||||||
|
isCreating: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -82,25 +80,24 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
addAgents() {
|
async addAgents() {
|
||||||
this.isCreating = true;
|
this.isCreating = true;
|
||||||
const inboxId = this.$route.params.inbox_id;
|
const inboxId = this.$route.params.inbox_id;
|
||||||
ChannelApi.addAgentsToChannel(inboxId, this.selectedAgents.map(x => x.id))
|
const selectedAgents = this.selectedAgents.map(x => x.id);
|
||||||
.then(() => {
|
|
||||||
this.isCreating = false;
|
try {
|
||||||
router.replace({
|
await InboxMembersAPI.create({ inboxId, agentList: selectedAgents });
|
||||||
name: 'settings_inbox_finish',
|
router.replace({
|
||||||
params: {
|
name: 'settings_inbox_finish',
|
||||||
page: 'new',
|
params: {
|
||||||
inbox_id: this.$route.params.inbox_id,
|
page: 'new',
|
||||||
website_token: this.$route.params.website_token,
|
inbox_id: this.$route.params.inbox_id,
|
||||||
},
|
},
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
bus.$emit('newToastMessage', error.message);
|
|
||||||
this.isCreating = false;
|
|
||||||
});
|
});
|
||||||
|
} catch (error) {
|
||||||
|
bus.$emit('newToastMessage', error.message);
|
||||||
|
}
|
||||||
|
this.isCreating = false;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
>
|
>
|
||||||
<div class="medium-12 columns text-center">
|
<div class="medium-12 columns text-center">
|
||||||
<div class="website--code">
|
<div class="website--code">
|
||||||
<woot-code v-if="$route.params.website_token" :script="websiteScript">
|
<woot-code v-if="currentInbox.website_token" :script="websiteScript">
|
||||||
</woot-code>
|
</woot-code>
|
||||||
</div>
|
</div>
|
||||||
<router-link
|
<router-link
|
||||||
|
@ -33,6 +33,11 @@ export default {
|
||||||
EmptyState,
|
EmptyState,
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
currentInbox() {
|
||||||
|
return this.$store.getters['inboxes/getInbox'](
|
||||||
|
this.$route.params.inbox_id
|
||||||
|
);
|
||||||
|
},
|
||||||
message() {
|
message() {
|
||||||
if (!this.$route.params.website_token) {
|
if (!this.$route.params.website_token) {
|
||||||
return this.$t('INBOX_MGMT.FINISH.MESSAGE');
|
return this.$t('INBOX_MGMT.FINISH.MESSAGE');
|
||||||
|
@ -40,7 +45,7 @@ export default {
|
||||||
return this.$t('INBOX_MGMT.FINISH.WEBSITE_SUCCESS');
|
return this.$t('INBOX_MGMT.FINISH.WEBSITE_SUCCESS');
|
||||||
},
|
},
|
||||||
websiteScript() {
|
websiteScript() {
|
||||||
return createWebsiteWidgetScript(this.$route.params.website_token);
|
return createWebsiteWidgetScript(this.currentInbox.website_token);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -18,9 +18,9 @@
|
||||||
<tr v-for="item in inboxesList" :key="item.id">
|
<tr v-for="item in inboxesList" :key="item.id">
|
||||||
<td>
|
<td>
|
||||||
<img
|
<img
|
||||||
v-if="item.avatarUrl"
|
v-if="item.avatar_url"
|
||||||
class="woot-thumbnail"
|
class="woot-thumbnail"
|
||||||
:src="item.avatarUrl"
|
:src="item.avatar_url"
|
||||||
alt="No Page Image"
|
alt="No Page Image"
|
||||||
/>
|
/>
|
||||||
<img
|
<img
|
||||||
|
@ -32,11 +32,11 @@
|
||||||
</td>
|
</td>
|
||||||
<!-- Short Code -->
|
<!-- Short Code -->
|
||||||
<td>
|
<td>
|
||||||
<span class="agent-name">{{ item.label }}</span>
|
<span class="agent-name">{{ item.name }}</span>
|
||||||
<span v-if="item.channelType === 'Channel::FacebookPage'">
|
<span v-if="item.channel_type === 'Channel::FacebookPage'">
|
||||||
Facebook
|
Facebook
|
||||||
</span>
|
</span>
|
||||||
<span v-if="item.channelType === 'Channel::WebWidget'">
|
<span v-if="item.channel_type === 'Channel::WebWidget'">
|
||||||
Website
|
Website
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
|
@ -44,28 +44,23 @@
|
||||||
<!-- Action Buttons -->
|
<!-- Action Buttons -->
|
||||||
<td>
|
<td>
|
||||||
<div class="button-wrapper">
|
<div class="button-wrapper">
|
||||||
<div v-if="isAdmin()" @click="openSettings(item)">
|
<router-link :to="`/app/settings/inboxes/${item.id}`">
|
||||||
<woot-submit-button
|
<woot-submit-button
|
||||||
|
v-if="isAdmin()"
|
||||||
:button-text="$t('INBOX_MGMT.SETTINGS')"
|
:button-text="$t('INBOX_MGMT.SETTINGS')"
|
||||||
icon-class="ion-gear-b"
|
icon-class="ion-gear-b"
|
||||||
button-class="link hollow grey-btn"
|
button-class="link hollow grey-btn"
|
||||||
/>
|
/>
|
||||||
</div>
|
</router-link>
|
||||||
<!-- <div>
|
|
||||||
<woot-submit-button
|
<woot-submit-button
|
||||||
:button-text="$t('INBOX_MGMT.REAUTH')"
|
v-if="isAdmin()"
|
||||||
icon-class="ion-edit"
|
:button-text="$t('INBOX_MGMT.DELETE.BUTTON_TEXT')"
|
||||||
|
:loading="loading[item.id]"
|
||||||
|
icon-class="ion-close-circled"
|
||||||
button-class="link hollow grey-btn"
|
button-class="link hollow grey-btn"
|
||||||
/>
|
@click="openDelete(item)"
|
||||||
</div> -->
|
/>
|
||||||
<div v-if="isAdmin()" @click="openDelete(item)">
|
|
||||||
<woot-submit-button
|
|
||||||
:button-text="$t('INBOX_MGMT.DELETE.BUTTON_TEXT')"
|
|
||||||
:loading="loading[item.id]"
|
|
||||||
icon-class="ion-close-circled"
|
|
||||||
button-class="link hollow grey-btn"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -120,22 +115,22 @@ export default {
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
inboxesList: 'getInboxesList',
|
inboxesList: 'inboxes/getInboxes',
|
||||||
}),
|
}),
|
||||||
// Delete Modal
|
// Delete Modal
|
||||||
deleteConfirmText() {
|
deleteConfirmText() {
|
||||||
return `${this.$t('INBOX_MGMT.DELETE.CONFIRM.YES')} ${
|
return `${this.$t('INBOX_MGMT.DELETE.CONFIRM.YES')} ${
|
||||||
this.selectedInbox.label
|
this.selectedInbox.name
|
||||||
}`;
|
}`;
|
||||||
},
|
},
|
||||||
deleteRejectText() {
|
deleteRejectText() {
|
||||||
return `${this.$t('INBOX_MGMT.DELETE.CONFIRM.NO')} ${
|
return `${this.$t('INBOX_MGMT.DELETE.CONFIRM.NO')} ${
|
||||||
this.selectedInbox.label
|
this.selectedInbox.name
|
||||||
}`;
|
}`;
|
||||||
},
|
},
|
||||||
deleteMessage() {
|
deleteMessage() {
|
||||||
return `${this.$t('INBOX_MGMT.DELETE.CONFIRM.MESSAGE')} ${
|
return `${this.$t('INBOX_MGMT.DELETE.CONFIRM.MESSAGE')} ${
|
||||||
this.selectedInbox.label
|
this.selectedInbox.name
|
||||||
} ?`;
|
} ?`;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -148,21 +143,19 @@ export default {
|
||||||
this.showSettings = false;
|
this.showSettings = false;
|
||||||
this.selectedInbox = {};
|
this.selectedInbox = {};
|
||||||
},
|
},
|
||||||
deleteInbox({ channel_id }) {
|
async deleteInbox({ id }) {
|
||||||
this.$store
|
try {
|
||||||
.dispatch('deleteInbox', channel_id)
|
await this.$store.dispatch('inboxes/delete', id);
|
||||||
.then(() =>
|
bus.$emit(
|
||||||
bus.$emit(
|
'newToastMessage',
|
||||||
'newToastMessage',
|
this.$t('INBOX_MGMT.DELETE.API.SUCCESS_MESSAGE')
|
||||||
this.$t('INBOX_MGMT.DELETE.API.SUCCESS_MESSAGE')
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.catch(() =>
|
|
||||||
bus.$emit(
|
|
||||||
'newToastMessage',
|
|
||||||
this.$t('INBOX_MGMT.DELETE.API.ERROR_MESSAGE')
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
} catch (error) {
|
||||||
|
bus.$emit(
|
||||||
|
'newToastMessage',
|
||||||
|
this.$t('INBOX_MGMT.DELETE.API.ERROR_MESSAGE')
|
||||||
|
);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
confirmDeletion() {
|
confirmDeletion() {
|
||||||
|
|
|
@ -1,148 +1,170 @@
|
||||||
<template>
|
<template>
|
||||||
<woot-modal class-name="settings-modal" :show.sync="show" :on-close="onClose">
|
<div class="settings columns container">
|
||||||
<div class="settings">
|
<woot-modal-header
|
||||||
<woot-modal-header
|
:header-image="inbox.avatarUrl"
|
||||||
:header-image="inbox.avatarUrl"
|
:header-title="inbox.name"
|
||||||
:header-title="inbox.label"
|
/>
|
||||||
/>
|
<div
|
||||||
<div
|
v-if="inbox.channel_type === 'Channel::FacebookPage'"
|
||||||
v-if="inbox.channelType === 'Channel::FacebookPage'"
|
class="settings--content"
|
||||||
class="settings--content"
|
>
|
||||||
|
<settings-form-header
|
||||||
|
:title="$t('INBOX_MGMT.SETTINGS_POPUP.MESSENGER_HEADING')"
|
||||||
|
:sub-title="$t('INBOX_MGMT.SETTINGS_POPUP.MESSENGER_SUB_HEAD')"
|
||||||
>
|
>
|
||||||
|
</settings-form-header>
|
||||||
|
<woot-code :script="messengerScript"></woot-code>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="inbox.channel_type === 'Channel::WebWidget'">
|
||||||
|
<div class="settings--content">
|
||||||
<settings-form-header
|
<settings-form-header
|
||||||
: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>
|
</settings-form-header>
|
||||||
<woot-code :script="messengerScript"></woot-code>
|
<woot-code :script="webWidgetScript"></woot-code>
|
||||||
</div>
|
|
||||||
<div v-else-if="inbox.channelType === 'Channel::WebWidget'">
|
|
||||||
<div class="settings--content">
|
|
||||||
<settings-form-header
|
|
||||||
:title="$t('INBOX_MGMT.SETTINGS_POPUP.MESSENGER_HEADING')"
|
|
||||||
:sub-title="$t('INBOX_MGMT.SETTINGS_POPUP.MESSENGER_SUB_HEAD')"
|
|
||||||
>
|
|
||||||
</settings-form-header>
|
|
||||||
<woot-code :script="webWidgetScript"></woot-code>
|
|
||||||
</div>
|
|
||||||
<!-- <div class="settings--content">
|
|
||||||
<settings-form-header
|
|
||||||
:title="$t('INBOX_MGMT.ADD.WEBSITE_CHANNEL.WIDGET_COLOR.LABEL')"
|
|
||||||
:sub-title="$t('INBOX_MGMT.SETTINGS_POPUP.INBOX_AGENTS_SUB_TEXT')"
|
|
||||||
:button-text="$t('INBOX_MGMT.SETTINGS_POPUP.UPDATE')"
|
|
||||||
:is-updating="isUpdating"
|
|
||||||
v-on:update="updateAgents"
|
|
||||||
>
|
|
||||||
</settings-form-header>
|
|
||||||
<Compact v-model="widgetColor" />
|
|
||||||
</div> -->
|
|
||||||
</div>
|
</div>
|
||||||
<div class="settings--content">
|
<div class="settings--content">
|
||||||
<settings-form-header
|
<settings-form-header
|
||||||
:title="$t('INBOX_MGMT.SETTINGS_POPUP.INBOX_AGENTS')"
|
:title="$t('INBOX_MGMT.ADD.WEBSITE_CHANNEL.WIDGET_COLOR.LABEL')"
|
||||||
:sub-title="$t('INBOX_MGMT.SETTINGS_POPUP.INBOX_AGENTS_SUB_TEXT')"
|
:sub-title="
|
||||||
|
$t('INBOX_MGMT.ADD.WEBSITE_CHANNEL.WIDGET_COLOR.PLACEHOLDER')
|
||||||
|
"
|
||||||
:button-text="$t('INBOX_MGMT.SETTINGS_POPUP.UPDATE')"
|
:button-text="$t('INBOX_MGMT.SETTINGS_POPUP.UPDATE')"
|
||||||
:is-updating="isUpdating"
|
:is-updating="uiFlags.isUpdating"
|
||||||
@update="updateAgents"
|
@update="updateWidgetColor"
|
||||||
>
|
>
|
||||||
</settings-form-header>
|
</settings-form-header>
|
||||||
<multiselect
|
<Compact v-model="inbox.widget_color" class="widget-color--selector" />
|
||||||
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>
|
||||||
</div>
|
</div>
|
||||||
</woot-modal>
|
<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>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
/* eslint no-console: 0 */
|
/* eslint no-console: 0 */
|
||||||
/* eslint-disable no-useless-escape */
|
|
||||||
/* global bus */
|
/* global bus */
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import {
|
import {
|
||||||
createWebsiteWidgetScript,
|
createWebsiteWidgetScript,
|
||||||
createMessengerScript,
|
createMessengerScript,
|
||||||
} from 'dashboard/helper/scriptGenerator';
|
} from 'dashboard/helper/scriptGenerator';
|
||||||
|
import { Compact } from 'vue-color';
|
||||||
import SettingsFormHeader from '../../../../components/SettingsFormHeader.vue';
|
import SettingsFormHeader from '../../../../components/SettingsFormHeader.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
Compact,
|
||||||
SettingsFormHeader,
|
SettingsFormHeader,
|
||||||
},
|
},
|
||||||
props: ['onClose', 'inbox', 'show'],
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
selectedAgents: [],
|
selectedAgents: [],
|
||||||
isUpdating: false,
|
isUpdating: false,
|
||||||
widgetColor: { hex: this.inbox.widgetColor },
|
isAgentListUpdating: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
agentList: 'agents/getAgents',
|
agentList: 'agents/getAgents',
|
||||||
|
uiFlags: 'inboxes/getUIFlags',
|
||||||
}),
|
}),
|
||||||
|
currentInboxId() {
|
||||||
|
return this.$route.params.inboxId;
|
||||||
|
},
|
||||||
|
inbox() {
|
||||||
|
return this.$store.getters['inboxes/getInbox'](this.currentInboxId);
|
||||||
|
},
|
||||||
webWidgetScript() {
|
webWidgetScript() {
|
||||||
return createWebsiteWidgetScript(this.inbox.websiteToken);
|
return createWebsiteWidgetScript(this.inbox.website_token);
|
||||||
},
|
},
|
||||||
messengerScript() {
|
messengerScript() {
|
||||||
return createMessengerScript(this.inbox.pageId);
|
return createMessengerScript(this.inbox.page_id);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.$store.dispatch('agents/get').then(() => {
|
this.$store.dispatch('agents/get');
|
||||||
|
this.$store.dispatch('inboxes/get').then(() => {
|
||||||
this.fetchAttachedAgents();
|
this.fetchAttachedAgents();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
fetchAttachedAgents() {
|
showAlert(message) {
|
||||||
this.$store
|
bus.$emit('newToastMessage', message);
|
||||||
.dispatch('listInboxAgents', {
|
|
||||||
inboxId: this.inbox.channel_id,
|
|
||||||
})
|
|
||||||
.then(response => {
|
|
||||||
const { payload } = response.data;
|
|
||||||
payload.forEach(el => {
|
|
||||||
const [item] = this.agentList.filter(
|
|
||||||
agent => agent.id === el.user_id
|
|
||||||
);
|
|
||||||
if (item) this.selectedAgents.push(item);
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.log(error);
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
updateAgents() {
|
async fetchAttachedAgents() {
|
||||||
const agentList = this.selectedAgents.map(el => el.id);
|
try {
|
||||||
this.isUpdating = true;
|
const response = await this.$store.dispatch('inboxMembers/get', {
|
||||||
this.$store
|
inboxId: this.currentInboxId,
|
||||||
.dispatch('updateInboxAgents', {
|
|
||||||
inboxId: this.inbox.channel_id,
|
|
||||||
agentList,
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
this.isUpdating = false;
|
|
||||||
bus.$emit(
|
|
||||||
'newToastMessage',
|
|
||||||
this.$t('AGENT_MGMT.EDIT.API.SUCCESS_MESSAGE')
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
this.isUpdating = false;
|
|
||||||
bus.$emit(
|
|
||||||
'newToastMessage',
|
|
||||||
this.$t('AGENT_MGMT.EDIT.API.ERROR_MESSAGE')
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
const {
|
||||||
|
data: { payload },
|
||||||
|
} = response;
|
||||||
|
payload.forEach(el => {
|
||||||
|
const [item] = this.agentList.filter(
|
||||||
|
agent => agent.id === el.user_id
|
||||||
|
);
|
||||||
|
if (item) {
|
||||||
|
this.selectedAgents.push(item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async updateAgents() {
|
||||||
|
const agentList = this.selectedAgents.map(el => el.id);
|
||||||
|
this.isAgentListUpdating = true;
|
||||||
|
try {
|
||||||
|
await this.$store.dispatch('inboxMembers/create', {
|
||||||
|
inboxId: this.currentInboxId,
|
||||||
|
agentList,
|
||||||
|
});
|
||||||
|
this.showAlert(this.$t('AGENT_MGMT.EDIT.API.SUCCESS_MESSAGE'));
|
||||||
|
} catch (error) {
|
||||||
|
this.showAlert(this.$t('AGENT_MGMT.EDIT.API.ERROR_MESSAGE'));
|
||||||
|
}
|
||||||
|
this.isAgentListUpdating = false;
|
||||||
|
},
|
||||||
|
async updateWidgetColor() {
|
||||||
|
try {
|
||||||
|
await this.$store.dispatch('inboxes/updateWebsiteChannel', {
|
||||||
|
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'));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getWidgetColor() {
|
||||||
|
return typeof this.inbox.widget_color !== 'object'
|
||||||
|
? this.inbox.widget_color
|
||||||
|
: this.inbox.widget_color.hex;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
validations: {
|
validations: {
|
||||||
|
|
|
@ -219,14 +219,11 @@ export default {
|
||||||
this.emptyStateMessage = this.$t('INBOX_MGMT.DETAILS.CREATING_CHANNEL');
|
this.emptyStateMessage = this.$t('INBOX_MGMT.DETAILS.CREATING_CHANNEL');
|
||||||
this.isCreating = true;
|
this.isCreating = true;
|
||||||
this.$store
|
this.$store
|
||||||
.dispatch('addInboxItem', {
|
.dispatch('inboxes/createFBChannel', this.channelParams())
|
||||||
channel: this.channel,
|
.then(data => {
|
||||||
params: this.channelParams(),
|
|
||||||
})
|
|
||||||
.then(response => {
|
|
||||||
router.replace({
|
router.replace({
|
||||||
name: 'settings_inboxes_add_agents',
|
name: 'settings_inboxes_add_agents',
|
||||||
params: { page: 'new', inbox_id: response.data.id },
|
params: { page: 'new', inbox_id: data.id },
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
|
|
|
@ -55,8 +55,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
/* global bus */
|
|
||||||
import { Compact } from 'vue-color';
|
import { Compact } from 'vue-color';
|
||||||
|
import { mapGetters } from 'vuex';
|
||||||
import router from '../../../../index';
|
import router from '../../../../index';
|
||||||
import PageHeader from '../../SettingsSubPageHeader';
|
import PageHeader from '../../SettingsSubPageHeader';
|
||||||
|
|
||||||
|
@ -73,26 +73,28 @@ export default {
|
||||||
isCreating: false,
|
isCreating: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
mounted() {
|
computed: {
|
||||||
bus.$on('new_website_channel', ({ inboxId, websiteToken }) => {
|
...mapGetters({
|
||||||
|
uiFlags: 'inboxes/getUIFlags',
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async createChannel() {
|
||||||
|
const website = await this.$store.dispatch(
|
||||||
|
'inboxes/createWebsiteChannel',
|
||||||
|
{
|
||||||
|
website: {
|
||||||
|
website_name: this.websiteName,
|
||||||
|
website_url: this.websiteUrl,
|
||||||
|
widget_color: this.widgetColor.hex,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
router.replace({
|
router.replace({
|
||||||
name: 'settings_inboxes_add_agents',
|
name: 'settings_inboxes_add_agents',
|
||||||
params: {
|
params: {
|
||||||
page: 'new',
|
page: 'new',
|
||||||
inbox_id: inboxId,
|
inbox_id: website.id,
|
||||||
website_token: websiteToken,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
createChannel() {
|
|
||||||
this.isCreating = true;
|
|
||||||
this.$store.dispatch('addWebsiteChannel', {
|
|
||||||
website: {
|
|
||||||
website_name: this.websiteName,
|
|
||||||
website_url: this.websiteUrl,
|
|
||||||
widget_color: this.widgetColor.hex,
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/* eslint arrow-body-style: 0 */
|
/* eslint arrow-body-style: 0 */
|
||||||
import SettingsContent from '../Wrapper';
|
import SettingsContent from '../Wrapper';
|
||||||
|
import Settings from './Settings';
|
||||||
import InboxHome from './Index';
|
import InboxHome from './Index';
|
||||||
import InboxChannel from './InboxChannels';
|
import InboxChannel from './InboxChannels';
|
||||||
import ChannelList from './ChannelList';
|
import ChannelList from './ChannelList';
|
||||||
|
@ -64,6 +65,12 @@ export default {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: ':inboxId',
|
||||||
|
name: 'settings_inbox_show',
|
||||||
|
component: Settings,
|
||||||
|
roles: ['administrator'],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
|
@ -7,8 +7,9 @@ import billing from './modules/billing';
|
||||||
import cannedResponse from './modules/cannedResponse';
|
import cannedResponse from './modules/cannedResponse';
|
||||||
import Channel from './modules/channels';
|
import Channel from './modules/channels';
|
||||||
import conversations from './modules/conversations';
|
import conversations from './modules/conversations';
|
||||||
|
import inboxes from './modules/inboxes';
|
||||||
|
import inboxMembers from './modules/inboxMembers';
|
||||||
import reports from './modules/reports';
|
import reports from './modules/reports';
|
||||||
import sideMenuItems from './modules/sidebar';
|
|
||||||
|
|
||||||
Vue.use(Vuex);
|
Vue.use(Vuex);
|
||||||
export default new Vuex.Store({
|
export default new Vuex.Store({
|
||||||
|
@ -19,7 +20,8 @@ export default new Vuex.Store({
|
||||||
cannedResponse,
|
cannedResponse,
|
||||||
Channel,
|
Channel,
|
||||||
conversations,
|
conversations,
|
||||||
|
inboxes,
|
||||||
|
inboxMembers,
|
||||||
reports,
|
reports,
|
||||||
sideMenuItems,
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
24
app/javascript/dashboard/store/modules/inboxMembers.js
Normal file
24
app/javascript/dashboard/store/modules/inboxMembers.js
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import InboxMembersAPI from '../../api/inboxMembers';
|
||||||
|
|
||||||
|
const state = {};
|
||||||
|
|
||||||
|
const getters = {};
|
||||||
|
|
||||||
|
const actions = {
|
||||||
|
get(_, { inboxId }) {
|
||||||
|
return InboxMembersAPI.show(inboxId);
|
||||||
|
},
|
||||||
|
create(_, { inboxId, agentList }) {
|
||||||
|
return InboxMembersAPI.create({ inboxId, agentList });
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const mutations = {};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
namespaced: true,
|
||||||
|
state,
|
||||||
|
getters,
|
||||||
|
actions,
|
||||||
|
mutations,
|
||||||
|
};
|
109
app/javascript/dashboard/store/modules/inboxes.js
Normal file
109
app/javascript/dashboard/store/modules/inboxes.js
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
import * as MutationHelpers from 'shared/helpers/vuex/mutationHelpers';
|
||||||
|
import * as types from '../mutation-types';
|
||||||
|
import InboxesAPI from '../../api/inboxes';
|
||||||
|
import WebChannel from '../../api/channel/webChannel';
|
||||||
|
import FBChannel from '../../api/channel/fbChannel';
|
||||||
|
|
||||||
|
export const state = {
|
||||||
|
records: [],
|
||||||
|
uiFlags: {
|
||||||
|
isFetching: false,
|
||||||
|
isFetchingItem: false,
|
||||||
|
isCreating: false,
|
||||||
|
isUpdating: false,
|
||||||
|
isDeleting: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getters = {
|
||||||
|
getInboxes($state) {
|
||||||
|
return $state.records;
|
||||||
|
},
|
||||||
|
getInbox: $state => inboxId => {
|
||||||
|
const [inbox] = $state.records.filter(
|
||||||
|
record => record.id === Number(inboxId)
|
||||||
|
);
|
||||||
|
return inbox || {};
|
||||||
|
},
|
||||||
|
getUIFlags($state) {
|
||||||
|
return $state.uiFlags;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const actions = {
|
||||||
|
get: async ({ commit }) => {
|
||||||
|
commit(types.default.SET_INBOXES_UI_FLAG, { isFetching: true });
|
||||||
|
try {
|
||||||
|
const response = await InboxesAPI.get();
|
||||||
|
commit(types.default.SET_INBOXES_UI_FLAG, { isFetching: false });
|
||||||
|
commit(types.default.SET_INBOXES, response.data.payload);
|
||||||
|
} catch (error) {
|
||||||
|
commit(types.default.SET_INBOXES_UI_FLAG, { isFetching: false });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
createWebsiteChannel: async ({ commit }, params) => {
|
||||||
|
try {
|
||||||
|
commit(types.default.SET_INBOXES_UI_FLAG, { isCreating: true });
|
||||||
|
const response = await WebChannel.create(params);
|
||||||
|
commit(types.default.ADD_INBOXES, response.data);
|
||||||
|
commit(types.default.SET_INBOXES_UI_FLAG, { isCreating: false });
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
commit(types.default.SET_INBOXES_UI_FLAG, { isCreating: false });
|
||||||
|
throw new Error(error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
createFBChannel: async ({ commit }, params) => {
|
||||||
|
try {
|
||||||
|
commit(types.default.SET_INBOXES_UI_FLAG, { isCreating: true });
|
||||||
|
const response = await FBChannel.create(params);
|
||||||
|
commit(types.default.ADD_INBOXES, response.data);
|
||||||
|
commit(types.default.SET_INBOXES_UI_FLAG, { isCreating: false });
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
commit(types.default.SET_INBOXES_UI_FLAG, { isCreating: false });
|
||||||
|
throw new Error(error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
updateWebsiteChannel: 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);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
delete: async ({ commit }, inboxId) => {
|
||||||
|
commit(types.default.SET_INBOXES_UI_FLAG, { isDeleting: true });
|
||||||
|
try {
|
||||||
|
await InboxesAPI.delete(inboxId);
|
||||||
|
commit(types.default.DELETE_INBOXES, inboxId);
|
||||||
|
commit(types.default.SET_INBOXES_UI_FLAG, { isDeleting: false });
|
||||||
|
} catch (error) {
|
||||||
|
commit(types.default.SET_INBOXES_UI_FLAG, { isDeleting: false });
|
||||||
|
throw new Error(error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const mutations = {
|
||||||
|
[types.default.SET_INBOXES_UI_FLAG]($state, uiFlag) {
|
||||||
|
$state.uiFlags = { ...$state.uiFlags, ...uiFlag };
|
||||||
|
},
|
||||||
|
[types.default.SET_INBOXES]: MutationHelpers.set,
|
||||||
|
[types.default.SET_INBOXES_ITEM]: MutationHelpers.setSingleRecord,
|
||||||
|
[types.default.ADD_INBOXES]: MutationHelpers.create,
|
||||||
|
[types.default.EDIT_INBOXES]: MutationHelpers.update,
|
||||||
|
[types.default.DELETE_INBOXES]: MutationHelpers.destroy,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
namespaced: true,
|
||||||
|
state,
|
||||||
|
getters,
|
||||||
|
actions,
|
||||||
|
mutations,
|
||||||
|
};
|
|
@ -1,195 +0,0 @@
|
||||||
/* eslint no-console: 0 */
|
|
||||||
/* eslint-env browser */
|
|
||||||
/* eslint no-param-reassign: 0 */
|
|
||||||
/* global bus */
|
|
||||||
// import * as types from '../mutation-types';
|
|
||||||
import defaultState from '../../i18n/default-sidebar';
|
|
||||||
import * as types from '../mutation-types';
|
|
||||||
import Account from '../../api/account';
|
|
||||||
import ChannelApi from '../../api/channels';
|
|
||||||
import { frontendURL } from '../../helper/URLHelper';
|
|
||||||
import WebChannel from '../../api/channel/webChannel';
|
|
||||||
|
|
||||||
const state = defaultState;
|
|
||||||
// inboxes fetch flag
|
|
||||||
state.inboxesLoading = false;
|
|
||||||
|
|
||||||
const getters = {
|
|
||||||
getMenuItems(_state) {
|
|
||||||
return _state.menuGroup;
|
|
||||||
},
|
|
||||||
getInboxesList(_state) {
|
|
||||||
return _state.menuGroup.common.menuItems.inbox.children;
|
|
||||||
},
|
|
||||||
getInboxLoadingStatus(_state) {
|
|
||||||
return _state.inboxesLoading;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const actions = {
|
|
||||||
// Fetch Labels
|
|
||||||
fetchLabels({ commit }) {
|
|
||||||
Account.getLabels()
|
|
||||||
.then(response => {
|
|
||||||
commit(types.default.SET_LABELS, response.data);
|
|
||||||
})
|
|
||||||
.catch();
|
|
||||||
},
|
|
||||||
// Fetch Inboxes
|
|
||||||
fetchInboxes({ commit }) {
|
|
||||||
commit(types.default.INBOXES_LOADING, true);
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
Account.getInboxes()
|
|
||||||
.then(response => {
|
|
||||||
commit(types.default.INBOXES_LOADING, false);
|
|
||||||
commit(types.default.SET_INBOXES, response.data);
|
|
||||||
resolve();
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
commit(types.default.INBOXES_LOADING, false);
|
|
||||||
reject(error);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
deleteInbox({ commit }, id) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
Account.deleteInbox(id)
|
|
||||||
.then(response => {
|
|
||||||
if (response.status === 200) {
|
|
||||||
commit(types.default.DELETE_INBOX, id);
|
|
||||||
resolve();
|
|
||||||
} else {
|
|
||||||
reject();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
reject(error);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
addWebsiteChannel: async ({ commit }, params) => {
|
|
||||||
try {
|
|
||||||
const response = await WebChannel.create(params);
|
|
||||||
commit(types.default.SET_INBOX_ITEM, response);
|
|
||||||
bus.$emit('new_website_channel', {
|
|
||||||
inboxId: response.data.id,
|
|
||||||
websiteToken: response.data.website_token,
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
// Handle error
|
|
||||||
}
|
|
||||||
},
|
|
||||||
addInboxItem({ commit }, { channel, params }) {
|
|
||||||
const donePromise = new Promise(resolve => {
|
|
||||||
ChannelApi.createChannel(channel, params)
|
|
||||||
.then(response => {
|
|
||||||
commit(types.default.SET_INBOX_ITEM, response);
|
|
||||||
resolve(response);
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.log(error);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return donePromise;
|
|
||||||
},
|
|
||||||
listInboxAgents(_, { inboxId }) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
Account.listInboxAgents(inboxId)
|
|
||||||
.then(response => {
|
|
||||||
if (response.status === 200) {
|
|
||||||
resolve(response.data);
|
|
||||||
} else {
|
|
||||||
reject();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
reject(error);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
updateInboxAgents(_, { inboxId, agentList }) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
Account.updateInboxAgents(inboxId, agentList)
|
|
||||||
.then(response => {
|
|
||||||
if (response.status === 200) {
|
|
||||||
resolve(response.data);
|
|
||||||
} else {
|
|
||||||
reject();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
reject(error);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const mutations = {
|
|
||||||
// Set Labels
|
|
||||||
[types.default.SET_LABELS](_state, data) {
|
|
||||||
let payload = data.data.payload.labels;
|
|
||||||
payload = payload.map(item => ({
|
|
||||||
label: item,
|
|
||||||
toState: `/#/${item}`,
|
|
||||||
}));
|
|
||||||
// Identify menuItem to update
|
|
||||||
// May have more than one object to update
|
|
||||||
// Iterate it accordingly. Updating commmon sidebar now.
|
|
||||||
const { menuItems } = _state.menuGroup.common;
|
|
||||||
// Update children for key `label`
|
|
||||||
menuItems.labels.children = payload;
|
|
||||||
},
|
|
||||||
|
|
||||||
[types.default.INBOXES_LOADING](_state, flag) {
|
|
||||||
_state.inboxesLoading = flag;
|
|
||||||
},
|
|
||||||
// Set Inboxes
|
|
||||||
[types.default.SET_INBOXES](_state, data) {
|
|
||||||
let { payload } = data.data;
|
|
||||||
payload = payload.map(item => ({
|
|
||||||
channel_id: item.id,
|
|
||||||
label: item.name,
|
|
||||||
toState: frontendURL(`inbox/${item.id}`),
|
|
||||||
channelType: item.channel_type,
|
|
||||||
avatarUrl: item.avatar_url,
|
|
||||||
pageId: item.page_id,
|
|
||||||
websiteToken: item.website_token,
|
|
||||||
widgetColor: item.widget_color,
|
|
||||||
}));
|
|
||||||
// Identify menuItem to update
|
|
||||||
// May have more than one object to update
|
|
||||||
// Iterate it accordingly. Updating commmon sidebar now.
|
|
||||||
const { menuItems } = _state.menuGroup.common;
|
|
||||||
// Update children for key `inbox`
|
|
||||||
menuItems.inbox.children = payload;
|
|
||||||
},
|
|
||||||
|
|
||||||
[types.default.SET_INBOX_ITEM](_state, { data }) {
|
|
||||||
const { menuItems } = _state.menuGroup.common;
|
|
||||||
// Update children for key `inbox`
|
|
||||||
menuItems.inbox.children.push({
|
|
||||||
channel_id: data.id,
|
|
||||||
label: data.name,
|
|
||||||
toState: frontendURL(`inbox/${data.id}`),
|
|
||||||
channelType: data.channel_type,
|
|
||||||
avatarUrl: data.avatar_url === undefined ? null : data.avatar_url,
|
|
||||||
pageId: data.page_id,
|
|
||||||
websiteToken: data.website_token,
|
|
||||||
widgetColor: data.widget_color,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
[types.default.DELETE_INBOX](_state, id) {
|
|
||||||
const { menuItems } = _state.menuGroup.common;
|
|
||||||
let inboxList = menuItems.inbox.children;
|
|
||||||
inboxList = inboxList.filter(inbox => inbox.channel_id !== id);
|
|
||||||
menuItems.inbox.children = inboxList;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export default {
|
|
||||||
state,
|
|
||||||
getters,
|
|
||||||
actions,
|
|
||||||
mutations,
|
|
||||||
};
|
|
|
@ -0,0 +1,116 @@
|
||||||
|
import axios from 'axios';
|
||||||
|
import { actions } from '../../inboxes';
|
||||||
|
import * as types from '../../../mutation-types';
|
||||||
|
import inboxList from './fixtures';
|
||||||
|
|
||||||
|
const commit = jest.fn();
|
||||||
|
global.axios = axios;
|
||||||
|
jest.mock('axios');
|
||||||
|
|
||||||
|
describe('#actions', () => {
|
||||||
|
describe('#get', () => {
|
||||||
|
it('sends correct actions if API is success', async () => {
|
||||||
|
axios.get.mockResolvedValue({ data: { payload: inboxList } });
|
||||||
|
await actions.get({ commit });
|
||||||
|
expect(commit.mock.calls).toEqual([
|
||||||
|
[types.default.SET_INBOXES_UI_FLAG, { isFetching: true }],
|
||||||
|
[types.default.SET_INBOXES_UI_FLAG, { isFetching: false }],
|
||||||
|
[types.default.SET_INBOXES, inboxList],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
it('sends correct actions if API is error', async () => {
|
||||||
|
axios.get.mockRejectedValue({ message: 'Incorrect header' });
|
||||||
|
await actions.get({ commit });
|
||||||
|
expect(commit.mock.calls).toEqual([
|
||||||
|
[types.default.SET_INBOXES_UI_FLAG, { isFetching: true }],
|
||||||
|
[types.default.SET_INBOXES_UI_FLAG, { isFetching: false }],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#createWebsiteChannel', () => {
|
||||||
|
it('sends correct actions if API is success', async () => {
|
||||||
|
axios.post.mockResolvedValue({ data: inboxList[0] });
|
||||||
|
await actions.createWebsiteChannel({ commit }, inboxList[0]);
|
||||||
|
expect(commit.mock.calls).toEqual([
|
||||||
|
[types.default.SET_INBOXES_UI_FLAG, { isCreating: true }],
|
||||||
|
[types.default.ADD_INBOXES, inboxList[0]],
|
||||||
|
[types.default.SET_INBOXES_UI_FLAG, { isCreating: false }],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
it('sends correct actions if API is error', async () => {
|
||||||
|
axios.post.mockRejectedValue({ message: 'Incorrect header' });
|
||||||
|
await expect(actions.createWebsiteChannel({ commit })).rejects.toThrow(
|
||||||
|
Error
|
||||||
|
);
|
||||||
|
expect(commit.mock.calls).toEqual([
|
||||||
|
[types.default.SET_INBOXES_UI_FLAG, { isCreating: true }],
|
||||||
|
[types.default.SET_INBOXES_UI_FLAG, { isCreating: false }],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#createFBChannel', () => {
|
||||||
|
it('sends correct actions if API is success', async () => {
|
||||||
|
axios.post.mockResolvedValue({ data: inboxList[0] });
|
||||||
|
await actions.createFBChannel({ commit }, inboxList[0]);
|
||||||
|
expect(commit.mock.calls).toEqual([
|
||||||
|
[types.default.SET_INBOXES_UI_FLAG, { isCreating: true }],
|
||||||
|
[types.default.ADD_INBOXES, inboxList[0]],
|
||||||
|
[types.default.SET_INBOXES_UI_FLAG, { isCreating: false }],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
it('sends correct actions if API is error', async () => {
|
||||||
|
axios.post.mockRejectedValue({ message: 'Incorrect header' });
|
||||||
|
await expect(actions.createFBChannel({ commit })).rejects.toThrow(Error);
|
||||||
|
expect(commit.mock.calls).toEqual([
|
||||||
|
[types.default.SET_INBOXES_UI_FLAG, { isCreating: true }],
|
||||||
|
[types.default.SET_INBOXES_UI_FLAG, { isCreating: false }],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#updateWebsiteChannel', () => {
|
||||||
|
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('#delete', () => {
|
||||||
|
it('sends correct actions if API is success', async () => {
|
||||||
|
axios.delete.mockResolvedValue({ data: inboxList[0] });
|
||||||
|
await actions.delete({ commit }, inboxList[0].id);
|
||||||
|
expect(commit.mock.calls).toEqual([
|
||||||
|
[types.default.SET_INBOXES_UI_FLAG, { isDeleting: true }],
|
||||||
|
[types.default.DELETE_INBOXES, inboxList[0].id],
|
||||||
|
[types.default.SET_INBOXES_UI_FLAG, { isDeleting: false }],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
it('sends correct actions if API is error', async () => {
|
||||||
|
axios.delete.mockRejectedValue({ message: 'Incorrect header' });
|
||||||
|
await expect(actions.delete({ commit }, inboxList[0].id)).rejects.toThrow(
|
||||||
|
Error
|
||||||
|
);
|
||||||
|
expect(commit.mock.calls).toEqual([
|
||||||
|
[types.default.SET_INBOXES_UI_FLAG, { isDeleting: true }],
|
||||||
|
[types.default.SET_INBOXES_UI_FLAG, { isDeleting: false }],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,42 @@
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
channel_id: 1,
|
||||||
|
name: 'Test FacebookPage 1',
|
||||||
|
channel_type: 'Channel::FacebookPage',
|
||||||
|
avatar_url: 'random_image.png',
|
||||||
|
page_id: '12345',
|
||||||
|
widget_color: null,
|
||||||
|
website_token: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
channel_id: 2,
|
||||||
|
name: 'Test Widget 1',
|
||||||
|
channel_type: 'Channel::WebWidget',
|
||||||
|
avatar_url: null,
|
||||||
|
page_id: null,
|
||||||
|
widget_color: '#7B64FF',
|
||||||
|
website_token: 'randomid123',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
channel_id: 3,
|
||||||
|
name: 'Test Widget 2',
|
||||||
|
channel_type: 'Channel::WebWidget',
|
||||||
|
avatar_url: null,
|
||||||
|
page_id: null,
|
||||||
|
widget_color: '#68BC00',
|
||||||
|
website_token: 'randomid124',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
channel_id: 4,
|
||||||
|
name: 'Test Widget 3',
|
||||||
|
channel_type: 'Channel::WebWidget',
|
||||||
|
avatar_url: null,
|
||||||
|
page_id: null,
|
||||||
|
widget_color: '#68BC00',
|
||||||
|
website_token: 'randomid125',
|
||||||
|
},
|
||||||
|
];
|
|
@ -0,0 +1,46 @@
|
||||||
|
import { getters } from '../../inboxes';
|
||||||
|
import inboxList from './fixtures';
|
||||||
|
|
||||||
|
describe('#getters', () => {
|
||||||
|
it('getInboxes', () => {
|
||||||
|
const state = {
|
||||||
|
records: inboxList,
|
||||||
|
};
|
||||||
|
expect(getters.getInboxes(state)).toEqual(inboxList);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getInbox', () => {
|
||||||
|
const state = {
|
||||||
|
records: inboxList,
|
||||||
|
};
|
||||||
|
expect(getters.getInbox(state)(1)).toEqual({
|
||||||
|
id: 1,
|
||||||
|
channel_id: 1,
|
||||||
|
name: 'Test FacebookPage 1',
|
||||||
|
channel_type: 'Channel::FacebookPage',
|
||||||
|
avatar_url: 'random_image.png',
|
||||||
|
page_id: '12345',
|
||||||
|
widget_color: null,
|
||||||
|
website_token: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getUIFlags', () => {
|
||||||
|
const state = {
|
||||||
|
uiFlags: {
|
||||||
|
isFetching: true,
|
||||||
|
isFetchingItem: false,
|
||||||
|
isCreating: false,
|
||||||
|
isUpdating: false,
|
||||||
|
isDeleting: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
expect(getters.getUIFlags(state)).toEqual({
|
||||||
|
isFetching: true,
|
||||||
|
isFetchingItem: false,
|
||||||
|
isCreating: false,
|
||||||
|
isUpdating: false,
|
||||||
|
isDeleting: false,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,94 @@
|
||||||
|
import * as types from '../../../mutation-types';
|
||||||
|
import { mutations } from '../../inboxes';
|
||||||
|
import inboxList from './fixtures';
|
||||||
|
|
||||||
|
describe('#mutations', () => {
|
||||||
|
describe('#SET_INBOXES', () => {
|
||||||
|
it('set inbox records', () => {
|
||||||
|
const state = { records: [] };
|
||||||
|
mutations[types.default.SET_INBOXES](state, inboxList);
|
||||||
|
expect(state.records).toEqual(inboxList);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#SET_INBOXES_ITEM', () => {
|
||||||
|
it('push inbox if inbox doesnot exist to the store', () => {
|
||||||
|
const state = {
|
||||||
|
records: [],
|
||||||
|
};
|
||||||
|
mutations[types.default.SET_INBOXES_ITEM](state, inboxList[0]);
|
||||||
|
expect(state.records).toEqual([inboxList[0]]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('update inbox if it exists to the store', () => {
|
||||||
|
const state = {
|
||||||
|
records: [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
channel_id: 1,
|
||||||
|
name: 'Test FacebookPage',
|
||||||
|
channel_type: 'Channel::FacebookPage',
|
||||||
|
avatar_url: 'random_image1.png',
|
||||||
|
page_id: '1235',
|
||||||
|
widget_color: null,
|
||||||
|
website_token: null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
mutations[types.default.SET_INBOXES_ITEM](state, inboxList[0]);
|
||||||
|
expect(state.records).toEqual([inboxList[0]]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#ADD_INBOXES', () => {
|
||||||
|
it('push new record in the inbox store', () => {
|
||||||
|
const state = {
|
||||||
|
records: [],
|
||||||
|
};
|
||||||
|
mutations[types.default.ADD_INBOXES](state, inboxList[0]);
|
||||||
|
expect(state.records).toEqual([inboxList[0]]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('#EDIT_INBOXES', () => {
|
||||||
|
it('update inbox in the store', () => {
|
||||||
|
const state = {
|
||||||
|
records: [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
channel_id: 1,
|
||||||
|
name: 'Test FacebookPage',
|
||||||
|
channel_type: 'Channel::FacebookPage',
|
||||||
|
avatar_url: 'random_image1.png',
|
||||||
|
page_id: '1235',
|
||||||
|
widget_color: null,
|
||||||
|
website_token: null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
mutations[types.default.EDIT_INBOXES](state, inboxList[0]);
|
||||||
|
expect(state.records).toEqual([inboxList[0]]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#DELETE_INBOXES', () => {
|
||||||
|
it('delete inbox from store', () => {
|
||||||
|
const state = {
|
||||||
|
records: [inboxList[0]],
|
||||||
|
};
|
||||||
|
mutations[types.default.DELETE_INBOXES](state, 1);
|
||||||
|
expect(state.records).toEqual([]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#DELETE_INBOXES', () => {
|
||||||
|
it('delete inbox from store', () => {
|
||||||
|
const state = {
|
||||||
|
uiFlags: { isFetchingItem: false },
|
||||||
|
};
|
||||||
|
mutations[types.default.SET_INBOXES_UI_FLAG](state, {
|
||||||
|
isFetchingItem: true,
|
||||||
|
});
|
||||||
|
expect(state.uiFlags).toEqual({ isFetchingItem: true });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -31,14 +31,13 @@ export default {
|
||||||
SET_PREVIOUS_CONVERSATIONS: 'SET_PREVIOUS_CONVERSATIONS',
|
SET_PREVIOUS_CONVERSATIONS: 'SET_PREVIOUS_CONVERSATIONS',
|
||||||
SET_ACTIVE_INBOX: 'SET_ACTIVE_INBOX',
|
SET_ACTIVE_INBOX: 'SET_ACTIVE_INBOX',
|
||||||
|
|
||||||
// labels
|
// Inboxes
|
||||||
SET_LABELS: 'SET_LABELS',
|
SET_INBOXES_UI_FLAG: 'SET_INBOXES_UI_FLAG',
|
||||||
|
|
||||||
// Set Inboxes
|
|
||||||
INBOXES_LOADING: 'INBOXES_LOADING',
|
|
||||||
SET_INBOXES: 'SET_INBOXES',
|
SET_INBOXES: 'SET_INBOXES',
|
||||||
SET_INBOX_ITEM: 'SET_INBOX_ITEM',
|
SET_INBOX_ITEM: 'SET_INBOX_ITEM',
|
||||||
DELETE_INBOX: 'DELETE_INBOX',
|
ADD_INBOXES: 'ADD_INBOXES',
|
||||||
|
EDIT_INBOXES: 'EDIT_INBOXES',
|
||||||
|
DELETE_INBOXES: 'DELETE_INBOXES',
|
||||||
|
|
||||||
// Agent
|
// Agent
|
||||||
SET_AGENT_FETCHING_STATUS: 'SET_AGENT_FETCHING_STATUS',
|
SET_AGENT_FETCHING_STATUS: 'SET_AGENT_FETCHING_STATUS',
|
||||||
|
|
|
@ -6,6 +6,15 @@ export const create = (state, data) => {
|
||||||
state.records.push(data);
|
state.records.push(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const setSingleRecord = (state, data) => {
|
||||||
|
const recordIndex = state.records.findIndex(record => record.id === data.id);
|
||||||
|
if (recordIndex > -1) {
|
||||||
|
state.records[recordIndex] = data;
|
||||||
|
} else {
|
||||||
|
create(state, data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const update = (state, data) => {
|
export const update = (state, data) => {
|
||||||
state.records.forEach((element, index) => {
|
state.records.forEach((element, index) => {
|
||||||
if (element.id === data.id) {
|
if (element.id === data.id) {
|
||||||
|
|
|
@ -39,6 +39,12 @@ $input-height: $space-two * 2;
|
||||||
padding: $space-small $space-slab;
|
padding: $space-small $space-slab;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.default {
|
||||||
|
font-size: $font-size-default;
|
||||||
|
height: $space-medium;
|
||||||
|
padding: $space-smaller $space-slab;
|
||||||
|
}
|
||||||
|
|
||||||
&.large {
|
&.large {
|
||||||
font-size: $font-size-medium;
|
font-size: $font-size-medium;
|
||||||
height: $space-larger;
|
height: $space-larger;
|
||||||
|
|
|
@ -22,6 +22,7 @@ module Channel
|
||||||
|
|
||||||
validates :website_name, presence: true
|
validates :website_name, presence: true
|
||||||
validates :website_url, presence: true
|
validates :website_url, presence: true
|
||||||
|
validates :widget_color, presence: true
|
||||||
|
|
||||||
belongs_to :account
|
belongs_to :account
|
||||||
has_one :inbox, as: :channel, dependent: :destroy
|
has_one :inbox, as: :channel, dependent: :destroy
|
||||||
|
|
|
@ -24,6 +24,10 @@ class InboxPolicy < ApplicationPolicy
|
||||||
@user.administrator?
|
@user.administrator?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def update?
|
||||||
|
@user.administrator?
|
||||||
|
end
|
||||||
|
|
||||||
def destroy?
|
def destroy?
|
||||||
@user.administrator?
|
@user.administrator?
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
json.data do
|
json.payload do
|
||||||
json.payload do
|
json.array! @agents do |agent|
|
||||||
json.array! @agents do |agent|
|
json.user_id agent.id
|
||||||
json.user_id agent.id
|
json.name agent.name
|
||||||
json.name agent.name
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,17 +1,12 @@
|
||||||
json.data do
|
json.payload do
|
||||||
json.meta do
|
json.array! @inboxes do |inbox|
|
||||||
end
|
json.id inbox.id
|
||||||
|
json.channel_id inbox.channel_id
|
||||||
json.payload do
|
json.name inbox.name
|
||||||
json.array! @inboxes do |inbox|
|
json.channel_type inbox.channel_type
|
||||||
json.id inbox.id
|
json.avatar_url inbox.channel.try(:avatar).try(:url)
|
||||||
json.channel_id inbox.channel_id
|
json.page_id inbox.channel.try(:page_id)
|
||||||
json.name inbox.name
|
json.widget_color inbox.channel.try(:widget_color)
|
||||||
json.channel_type inbox.channel_type
|
json.website_token inbox.channel.try(:website_token)
|
||||||
json.avatar_url inbox.channel.try(:avatar).try(:url)
|
|
||||||
json.page_id inbox.channel.try(:page_id)
|
|
||||||
json.widget_color inbox.channel.try(:widget_color)
|
|
||||||
json.website_token inbox.channel.try(:website_token)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,3 +3,4 @@ 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.website_token @inbox.channel.try(:website_token)
|
json.website_token @inbox.channel.try(:website_token)
|
||||||
|
json.widget_color @inbox.channel.try(:widget_color)
|
||||||
|
|
6
app/views/api/v1/widget/inboxes/update.json.jbuilder
Normal file
6
app/views/api/v1/widget/inboxes/update.json.jbuilder
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
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
|
|
@ -26,7 +26,7 @@ Rails.application.routes.draw do
|
||||||
|
|
||||||
namespace :widget do
|
namespace :widget do
|
||||||
resources :messages, only: [:index, :create]
|
resources :messages, only: [:index, :create]
|
||||||
resources :inboxes, only: [:create]
|
resources :inboxes, only: [:create, :update]
|
||||||
end
|
end
|
||||||
|
|
||||||
namespace :actions do
|
namespace :actions do
|
||||||
|
@ -92,7 +92,7 @@ Rails.application.routes.draw do
|
||||||
|
|
||||||
# Sidekiq Web UI
|
# Sidekiq Web UI
|
||||||
require 'sidekiq/web'
|
require 'sidekiq/web'
|
||||||
authenticate :user, lambda { |u| u.administrator? } do
|
authenticate :user, ->(u) { u.administrator? } do
|
||||||
mount Sidekiq::Web => '/sidekiq'
|
mount Sidekiq::Web => '/sidekiq'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -2,11 +2,13 @@ require 'rails_helper'
|
||||||
|
|
||||||
RSpec.describe '/api/v1/widget/inboxes', type: :request do
|
RSpec.describe '/api/v1/widget/inboxes', type: :request do
|
||||||
let(:account) { create(:account) }
|
let(:account) { create(:account) }
|
||||||
|
let(:inbox) { create(:inbox, account: account) }
|
||||||
let(:admin) { create(:user, account: account, role: :administrator) }
|
let(:admin) { create(:user, account: account, role: :administrator) }
|
||||||
let(:agent) { create(:user, account: account, role: :agent) }
|
let(:agent) { create(:user, account: account, role: :agent) }
|
||||||
let(:params) { { website: { website_name: 'test', website_url: 'test.com' } } }
|
|
||||||
|
|
||||||
describe 'POST /api/v1/widget/inboxes' do
|
describe 'POST /api/v1/widget/inboxes' do
|
||||||
|
let(:params) { { website: { website_name: 'test', website_url: 'test.com', widget_color: '#eaeaea' } } }
|
||||||
|
|
||||||
context 'when unauthenticated user' do
|
context 'when unauthenticated user' do
|
||||||
it 'returns unauthorized' do
|
it 'returns unauthorized' do
|
||||||
post '/api/v1/widget/inboxes', params: params
|
post '/api/v1/widget/inboxes', params: params
|
||||||
|
@ -17,7 +19,7 @@ RSpec.describe '/api/v1/widget/inboxes', type: :request do
|
||||||
context 'when user is logged in' do
|
context 'when user is logged in' do
|
||||||
context 'with user as administrator' do
|
context 'with user as administrator' do
|
||||||
it 'creates inbox and returns website_token' do
|
it 'creates inbox and returns website_token' do
|
||||||
post '/api/v1/widget/inboxes', params: params, headers: admin.create_new_auth_token, as: :json
|
post '/api/v1/widget/inboxes', params: params, headers: admin.create_new_auth_token
|
||||||
|
|
||||||
expect(response).to have_http_status(:success)
|
expect(response).to have_http_status(:success)
|
||||||
json_response = JSON.parse(response.body)
|
json_response = JSON.parse(response.body)
|
||||||
|
@ -31,8 +33,43 @@ RSpec.describe '/api/v1/widget/inboxes', type: :request do
|
||||||
it 'returns unauthorized' do
|
it 'returns unauthorized' do
|
||||||
post '/api/v1/widget/inboxes',
|
post '/api/v1/widget/inboxes',
|
||||||
params: params,
|
params: params,
|
||||||
headers: agent.create_new_auth_token,
|
headers: agent.create_new_auth_token
|
||||||
as: :json
|
|
||||||
|
expect(response).to have_http_status(:unauthorized)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'PATCH /api/v1/widget/inboxes/:id' do
|
||||||
|
let(:update_params) { { website: { widget_color: '#eaeaea' } } }
|
||||||
|
|
||||||
|
context 'when unauthenticated user' do
|
||||||
|
it 'returns unauthorized' do
|
||||||
|
patch "/api/v1/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/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/widget/inboxes/#{inbox.channel_id}",
|
||||||
|
params: update_params,
|
||||||
|
headers: agent.create_new_auth_token
|
||||||
|
|
||||||
expect(response).to have_http_status(:unauthorized)
|
expect(response).to have_http_status(:unauthorized)
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,6 +4,7 @@ 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_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)
|
||||||
account
|
account
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
FactoryBot.define do
|
FactoryBot.define do
|
||||||
factory :inbox do
|
factory :inbox do
|
||||||
account
|
account
|
||||||
association :channel, factory: :channel_widget
|
|
||||||
name { 'Inbox' }
|
name { 'Inbox' }
|
||||||
|
channel { FactoryBot.build(:channel_widget, account: account) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue