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
|
||||
brakeman:
|
||||
enabled: true
|
||||
|
||||
checks:
|
||||
similar-code:
|
||||
enabled: false
|
||||
exclude_patterns:
|
||||
- "spec/"
|
||||
- "**/specs/"
|
||||
|
|
|
@ -1,17 +1,25 @@
|
|||
class Api::V1::Widget::InboxesController < Api::BaseController
|
||||
before_action :authorize_request
|
||||
before_action :set_web_widget_channel, only: [:update]
|
||||
before_action :set_inbox, only: [:update]
|
||||
|
||||
def create
|
||||
ActiveRecord::Base.transaction do
|
||||
channel = web_widgets.create!(
|
||||
website_name: permitted_params[:website_name],
|
||||
website_url: permitted_params[:website_url],
|
||||
widget_color: permitted_params[:widget_color]
|
||||
website_name: permitted_params[:website][:website_name],
|
||||
website_url: permitted_params[:website][:website_url],
|
||||
widget_color: permitted_params[:website][:widget_color]
|
||||
)
|
||||
@inbox = inboxes.create!(name: permitted_params[:website_name], channel: channel)
|
||||
@inbox = inboxes.create!(name: permitted_params[:website][:website_name], channel: channel)
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
@channel.update!(
|
||||
widget_color: permitted_params[:website][:widget_color]
|
||||
)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def authorize_request
|
||||
|
@ -26,7 +34,15 @@ class Api::V1::Widget::InboxesController < Api::BaseController
|
|||
current_account.web_widgets
|
||||
end
|
||||
|
||||
def set_web_widget_channel
|
||||
@channel = web_widgets.find_by(id: permitted_params[:id])
|
||||
end
|
||||
|
||||
def set_inbox
|
||||
@inbox = @channel.inbox
|
||||
end
|
||||
|
||||
def permitted_params
|
||||
params.fetch(:website).permit(:website_name, :website_url, :widget_color)
|
||||
params.permit(:id, website: [:website_name, :website_url, :widget_color])
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,7 +4,8 @@ const API_VERSION = `/api/v1`;
|
|||
|
||||
class ApiClient {
|
||||
constructor(url) {
|
||||
this.url = `${API_VERSION}/${url}`;
|
||||
this.apiVersion = API_VERSION;
|
||||
this.url = `${this.apiVersion}/${url}`;
|
||||
}
|
||||
|
||||
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,
|
||||
});
|
||||
}
|
||||
|
||||
create(params) {
|
||||
return axios.post(
|
||||
`${this.apiVersion}/callbacks/register_facebook_page`,
|
||||
params
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default new FBChannel();
|
||||
|
|
|
@ -5,19 +5,6 @@
|
|||
import endPoints from './endPoints';
|
||||
|
||||
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) {
|
||||
const urlData = endPoints('fetchFacebookPages');
|
||||
urlData.params.omniauth_token = token;
|
||||
|
|
|
@ -24,26 +24,6 @@ const endPoints = {
|
|||
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: {
|
||||
url: 'api/v1/callbacks/get_facebook_pages.json',
|
||||
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 => {
|
||||
|
|
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
|
||||
.settings-header {
|
||||
@include padding($space-small $space-normal);
|
||||
|
@ -196,51 +203,23 @@
|
|||
}
|
||||
}
|
||||
|
||||
.settings-modal {
|
||||
height: 80%;
|
||||
max-width: 1040px;
|
||||
width: 100%;
|
||||
.settings--content {
|
||||
@include margin($space-small $space-medium);
|
||||
|
||||
.delete-wrapper {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
@include flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
@include padding($space-normal $space-large);
|
||||
|
||||
a {
|
||||
margin-left: $space-normal;
|
||||
}
|
||||
.title {
|
||||
font-weight: $font-weight-medium;
|
||||
}
|
||||
|
||||
.settings--content {
|
||||
@include margin($space-medium);
|
||||
.code {
|
||||
max-height: $space-mega;
|
||||
overflow: scroll;
|
||||
white-space: nowrap;
|
||||
@include padding($space-one);
|
||||
background: $color-background;
|
||||
|
||||
.title {
|
||||
font-weight: $font-weight-medium;
|
||||
}
|
||||
|
||||
.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;
|
||||
code {
|
||||
background: transparent;
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,15 @@
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
.page-top-bar {
|
||||
@include padding($zero $space-two);
|
||||
|
||||
img {
|
||||
max-height: 6rem;
|
||||
}
|
||||
}
|
||||
|
||||
.modal-container {
|
||||
background-color: $color-white;
|
||||
border-radius: $space-small;
|
||||
|
@ -35,13 +44,6 @@
|
|||
position: relative;
|
||||
width: 60rem;
|
||||
|
||||
.page-top-bar {
|
||||
@include padding($zero $space-two);
|
||||
|
||||
img {
|
||||
max-height: 6rem;
|
||||
}
|
||||
}
|
||||
|
||||
.content-box {
|
||||
@include padding($zero);
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<div class="chat-list__top">
|
||||
<h1 class="page-title">
|
||||
<woot-sidemenu-icon />
|
||||
{{ getInboxName }}
|
||||
{{ inbox.name || pageTitle }}
|
||||
</h1>
|
||||
<chat-filter @statusFilterChange="getDataForStatusTab" />
|
||||
</div>
|
||||
|
@ -53,6 +53,11 @@ import conversationMixin from '../mixins/conversations';
|
|||
import wootConstants from '../constants';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ChatTypeTabs,
|
||||
ConversationCard,
|
||||
ChatFilter,
|
||||
},
|
||||
mixins: [timeMixin, conversationMixin],
|
||||
props: ['conversationInbox', 'pageTitle'],
|
||||
data() {
|
||||
|
@ -61,25 +66,12 @@ export default {
|
|||
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: {
|
||||
...mapGetters({
|
||||
chatLists: 'getAllConversations',
|
||||
mineChatsList: 'getMineChats',
|
||||
allChatList: 'getAllStatusChats',
|
||||
unAssignedChatsList: 'getUnAssignedChats',
|
||||
inboxesList: 'getInboxesList',
|
||||
chatListLoading: 'getChatListLoadingStatus',
|
||||
currentUserID: 'getCurrentUserID',
|
||||
activeInbox: 'getSelectedInbox',
|
||||
|
@ -92,12 +84,8 @@ export default {
|
|||
count: this.convStats[item.KEY] || 0,
|
||||
}));
|
||||
},
|
||||
getInboxName() {
|
||||
const inboxId = Number(this.activeInbox);
|
||||
const [stateInbox] = this.inboxesList.filter(
|
||||
inbox => inbox.channel_id === inboxId
|
||||
);
|
||||
return !stateInbox ? this.pageTitle : stateInbox.label;
|
||||
inbox() {
|
||||
return this.$store.getters['inboxes/getInbox'](this.activeInbox);
|
||||
},
|
||||
getToggleStatus() {
|
||||
if (this.toggleType) {
|
||||
|
@ -106,6 +94,18 @@ export default {
|
|||
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: {
|
||||
fetchData() {
|
||||
if (this.chatLists.length === 0) {
|
||||
|
@ -149,12 +149,6 @@ export default {
|
|||
return sorted;
|
||||
},
|
||||
},
|
||||
|
||||
components: {
|
||||
ChatTypeTabs,
|
||||
ConversationCard,
|
||||
ChatFilter,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
<template>
|
||||
<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">
|
||||
{{headerTitle}}
|
||||
{{ headerTitle }}
|
||||
</h2>
|
||||
<p class="small-12 column">
|
||||
{{headerContent}}
|
||||
<p v-if="headerContent" class="small-12 column">
|
||||
{{ headerContent }}
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
</div>
|
||||
<div v-if="buttonText" class="medium-4 text-right">
|
||||
<woot-submit-button
|
||||
class="small"
|
||||
class="default"
|
||||
:button-text="buttonText"
|
||||
:loading="isUpdating"
|
||||
@click="onClick()"
|
||||
|
@ -32,7 +32,7 @@ export default {
|
|||
},
|
||||
buttonText: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: '',
|
||||
},
|
||||
isUpdating: {
|
||||
type: Boolean,
|
||||
|
@ -51,8 +51,19 @@ export default {
|
|||
@import '~dashboard/assets/scss/variables';
|
||||
|
||||
.settings--form--header {
|
||||
align-items: center;
|
||||
border-bottom: 1px solid $color-border;
|
||||
display: flex;
|
||||
margin-bottom: $space-normal;
|
||||
padding: $space-normal 0;
|
||||
|
||||
.button {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin-bottom: 0;
|
||||
font-size: $font-size-default;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -13,6 +13,12 @@
|
|||
:key="item.toState"
|
||||
:menu-item="item"
|
||||
/>
|
||||
|
||||
<sidebar-item
|
||||
v-if="shouldShowInboxes"
|
||||
:key="inboxSection.toState"
|
||||
:menu-item="inboxSection"
|
||||
/>
|
||||
</transition-group>
|
||||
</div>
|
||||
|
||||
|
@ -41,7 +47,7 @@
|
|||
</div>
|
||||
</transition>
|
||||
<div class="current-user" @click.prevent="showOptions()">
|
||||
<thumbnail :src="gravatarUrl()" :username="currentUser.name"/>
|
||||
<thumbnail :src="gravatarUrl()" :username="currentUser.name" />
|
||||
<div class="current-user--data">
|
||||
<h3 class="current-user--name">
|
||||
{{ currentUser.name }}
|
||||
|
@ -50,9 +56,8 @@
|
|||
{{ currentUser.role }}
|
||||
</h5>
|
||||
</div>
|
||||
<span
|
||||
class="current-user--options icon ion-android-more-vertical"
|
||||
></span>
|
||||
<span class="current-user--options icon ion-android-more-vertical">
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
@ -69,12 +74,19 @@ import SidebarItem from './SidebarItem';
|
|||
import WootStatusBar from '../widgets/StatusBar';
|
||||
import { frontendURL } from '../../helper/URLHelper';
|
||||
import Thumbnail from '../widgets/Thumbnail';
|
||||
import sidemenuItems from '../../i18n/default-sidebar';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
SidebarItem,
|
||||
WootStatusBar,
|
||||
Thumbnail,
|
||||
},
|
||||
mixins: [clickaway, adminMixin],
|
||||
props: {
|
||||
route: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
data() {
|
||||
|
@ -82,27 +94,22 @@ export default {
|
|||
showOptionsMenu: false,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
// this.$store.dispatch('fetchLabels');
|
||||
this.$store.dispatch('fetchInboxes');
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
sidebarList: 'getMenuItems',
|
||||
daysLeft: 'getTrialLeft',
|
||||
subscriptionData: 'getSubscription',
|
||||
inboxes: 'inboxes/getInboxes',
|
||||
}),
|
||||
accessibleMenuItems() {
|
||||
const currentRoute = this.$store.state.route.name;
|
||||
// get all keys in menuGroup
|
||||
const groupKey = Object.keys(this.sidebarList);
|
||||
const groupKey = Object.keys(sidemenuItems);
|
||||
|
||||
let menuItems = [];
|
||||
// Iterate over menuGroup to find the correct group
|
||||
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
|
||||
const isRouteIncluded = groupItem.routes.includes(currentRoute);
|
||||
const isRouteIncluded = groupItem.routes.includes(this.currentRoute);
|
||||
if (isRouteIncluded) {
|
||||
menuItems = Object.values(groupItem.menuItems);
|
||||
}
|
||||
|
@ -114,6 +121,29 @@ export default {
|
|||
|
||||
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() {
|
||||
return Auth.getCurrentUser();
|
||||
},
|
||||
|
@ -140,7 +170,9 @@ export default {
|
|||
return `${this.daysLeft} ${this.$t('APP_GLOBAL.TRIAL_MESSAGE')}`;
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.$store.dispatch('inboxes/get');
|
||||
},
|
||||
methods: {
|
||||
gravatarUrl() {
|
||||
const hash = md5(this.currentUser.email);
|
||||
|
@ -165,11 +197,5 @@ export default {
|
|||
this.showOptionsMenu = !this.showOptionsMenu;
|
||||
},
|
||||
},
|
||||
|
||||
components: {
|
||||
SidebarItem,
|
||||
WootStatusBar,
|
||||
Thumbnail,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -63,7 +63,7 @@ export default {
|
|||
computed: {
|
||||
...mapGetters({
|
||||
currentChat: 'getSelectedChat',
|
||||
inboxesList: 'getInboxesList',
|
||||
inboxesList: 'inboxes/getInboxes',
|
||||
activeInbox: 'getSelectedInbox',
|
||||
}),
|
||||
|
||||
|
@ -107,11 +107,10 @@ export default {
|
|||
`;
|
||||
},
|
||||
getEmojiSVG,
|
||||
|
||||
inboxName(inboxId) {
|
||||
const [stateInbox] = this.inboxesList.filter(
|
||||
inbox => inbox.channel_id === inboxId
|
||||
);
|
||||
return !stateInbox ? '' : stateInbox.label;
|
||||
const stateInbox = this.$store.getters['inboxes/getInbox'](inboxId);
|
||||
return stateInbox.name || '';
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
<template>
|
||||
<div class="columns full-height conv-empty-state">
|
||||
<woot-loading-state
|
||||
v-if="fetchingInboxes || loadingChatList"
|
||||
v-if="uiFlags.isFetching || loadingChatList"
|
||||
:message="loadingIndicatorMessage"
|
||||
/>
|
||||
<!-- 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 -->
|
||||
<div v-if="!inboxesList.length">
|
||||
<img src="~dashboard/assets/images/inboxes.svg" alt="No Inboxes" />
|
||||
|
@ -49,12 +49,12 @@ export default {
|
|||
...mapGetters({
|
||||
currentChat: 'getSelectedChat',
|
||||
allConversations: 'getAllConversations',
|
||||
inboxesList: 'getInboxesList',
|
||||
fetchingInboxes: 'getInboxLoadingStatus',
|
||||
inboxesList: 'inboxes/getInboxes',
|
||||
uiFlags: 'inboxes/getUIFlags',
|
||||
loadingChatList: 'getChatListLoadingStatus',
|
||||
}),
|
||||
loadingIndicatorMessage() {
|
||||
if (this.fetchingInboxes) {
|
||||
if (this.uiFlags.isFetching) {
|
||||
return this.$t('CONVERSATION.LOADING_INBOXES');
|
||||
}
|
||||
return this.$t('CONVERSATION.LOADING_CONVERSATIONS');
|
||||
|
|
|
@ -75,10 +75,9 @@ export default {
|
|||
...mapGetters({
|
||||
currentChat: 'getSelectedChat',
|
||||
allConversations: 'getAllConversations',
|
||||
inboxesList: 'getInboxesList',
|
||||
inboxesList: 'inboxes/getInboxes',
|
||||
listLoadingStatus: 'getAllMessagesLoaded',
|
||||
getUnreadCount: 'getUnreadCount',
|
||||
fetchingInboxes: 'getInboxLoadingStatus',
|
||||
loadingChatList: 'getChatListLoadingStatus',
|
||||
}),
|
||||
|
||||
|
@ -99,7 +98,7 @@ export default {
|
|||
} else {
|
||||
[stateInbox] = this.inboxesList;
|
||||
}
|
||||
return !stateInbox ? 0 : stateInbox.pageId;
|
||||
return !stateInbox ? 0 : stateInbox.page_id;
|
||||
},
|
||||
// Get current FB Page ID link
|
||||
linkToMessage() {
|
||||
|
|
|
@ -1,109 +1,97 @@
|
|||
import { frontendURL } from '../helper/URLHelper';
|
||||
|
||||
export default {
|
||||
menuGroup: {
|
||||
common: {
|
||||
routes: [
|
||||
'home',
|
||||
'inbox_dashboard',
|
||||
'inbox_conversation',
|
||||
'settings_account_reports',
|
||||
'billing_deactivated',
|
||||
],
|
||||
menuItems: {
|
||||
assignedToMe: {
|
||||
icon: 'ion-chatbox-working',
|
||||
label: 'Conversations',
|
||||
hasSubMenu: false,
|
||||
key: '',
|
||||
toState: frontendURL('dashboard'),
|
||||
toolTip: 'Conversation from all subscribed inboxes',
|
||||
toStateName: 'home',
|
||||
},
|
||||
report: {
|
||||
icon: 'ion-arrow-graph-up-right',
|
||||
label: 'Reports',
|
||||
hasSubMenu: false,
|
||||
toState: frontendURL('reports'),
|
||||
toStateName: 'settings_account_reports',
|
||||
},
|
||||
settings: {
|
||||
icon: 'ion-settings',
|
||||
label: 'Settings',
|
||||
hasSubMenu: false,
|
||||
toState: frontendURL('settings'),
|
||||
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: [],
|
||||
},
|
||||
common: {
|
||||
routes: [
|
||||
'home',
|
||||
'inbox_dashboard',
|
||||
'inbox_conversation',
|
||||
'settings_account_reports',
|
||||
'billing_deactivated',
|
||||
],
|
||||
menuItems: {
|
||||
assignedToMe: {
|
||||
icon: 'ion-chatbox-working',
|
||||
label: 'Conversations',
|
||||
hasSubMenu: false,
|
||||
key: '',
|
||||
toState: frontendURL('dashboard'),
|
||||
toolTip: 'Conversation from all subscribed inboxes',
|
||||
toStateName: 'home',
|
||||
},
|
||||
report: {
|
||||
icon: 'ion-arrow-graph-up-right',
|
||||
label: 'Reports',
|
||||
hasSubMenu: false,
|
||||
toState: frontendURL('reports'),
|
||||
toStateName: 'settings_account_reports',
|
||||
},
|
||||
settings: {
|
||||
icon: 'ion-settings',
|
||||
label: 'Settings',
|
||||
hasSubMenu: false,
|
||||
toState: frontendURL('settings'),
|
||||
toStateName: 'settings_home',
|
||||
},
|
||||
},
|
||||
settings: {
|
||||
routes: [
|
||||
'agent_list',
|
||||
'agent_new',
|
||||
'canned_list',
|
||||
'canned_new',
|
||||
'settings_inbox',
|
||||
'settings_inbox_new',
|
||||
'settings_inbox_list',
|
||||
'settings_inboxes_page_channel',
|
||||
'settings_inboxes_add_agents',
|
||||
'settings_inbox_finish',
|
||||
'billing',
|
||||
],
|
||||
menuItems: {
|
||||
back: {
|
||||
icon: 'ion-ios-arrow-back',
|
||||
label: 'Home',
|
||||
hasSubMenu: false,
|
||||
toStateName: 'home',
|
||||
toState: frontendURL('dashboard'),
|
||||
},
|
||||
agents: {
|
||||
icon: 'ion-person-stalker',
|
||||
label: 'Agents',
|
||||
hasSubMenu: false,
|
||||
toState: frontendURL('settings/agents/list'),
|
||||
toStateName: 'agent_list',
|
||||
},
|
||||
inboxes: {
|
||||
icon: 'ion-archive',
|
||||
label: 'Inboxes',
|
||||
hasSubMenu: false,
|
||||
toState: frontendURL('settings/inboxes/list'),
|
||||
toStateName: 'settings_inbox_list',
|
||||
},
|
||||
cannedResponses: {
|
||||
icon: 'ion-chatbox-working',
|
||||
label: 'Canned Responses',
|
||||
hasSubMenu: false,
|
||||
toState: frontendURL('settings/canned-response/list'),
|
||||
toStateName: 'canned_list',
|
||||
},
|
||||
billing: {
|
||||
icon: 'ion-card',
|
||||
label: 'Billing',
|
||||
hasSubMenu: false,
|
||||
toState: frontendURL('settings/billing'),
|
||||
toStateName: 'billing',
|
||||
},
|
||||
account: {
|
||||
icon: 'ion-beer',
|
||||
label: 'Account Settings',
|
||||
hasSubMenu: false,
|
||||
toState: frontendURL('settings/account'),
|
||||
toStateName: 'account',
|
||||
},
|
||||
},
|
||||
settings: {
|
||||
routes: [
|
||||
'agent_list',
|
||||
'agent_new',
|
||||
'canned_list',
|
||||
'canned_new',
|
||||
'settings_inbox',
|
||||
'settings_inbox_new',
|
||||
'settings_inbox_list',
|
||||
'settings_inbox_show',
|
||||
'settings_inboxes_page_channel',
|
||||
'settings_inboxes_add_agents',
|
||||
'settings_inbox_finish',
|
||||
'billing',
|
||||
],
|
||||
menuItems: {
|
||||
back: {
|
||||
icon: 'ion-ios-arrow-back',
|
||||
label: 'Home',
|
||||
hasSubMenu: false,
|
||||
toStateName: 'home',
|
||||
toState: frontendURL('dashboard'),
|
||||
},
|
||||
agents: {
|
||||
icon: 'ion-person-stalker',
|
||||
label: 'Agents',
|
||||
hasSubMenu: false,
|
||||
toState: frontendURL('settings/agents/list'),
|
||||
toStateName: 'agent_list',
|
||||
},
|
||||
inboxes: {
|
||||
icon: 'ion-archive',
|
||||
label: 'Inboxes',
|
||||
hasSubMenu: false,
|
||||
toState: frontendURL('settings/inboxes/list'),
|
||||
toStateName: 'settings_inbox_list',
|
||||
},
|
||||
cannedResponses: {
|
||||
icon: 'ion-chatbox-working',
|
||||
label: 'Canned Responses',
|
||||
hasSubMenu: false,
|
||||
toState: frontendURL('settings/canned-response/list'),
|
||||
toStateName: 'canned_list',
|
||||
},
|
||||
billing: {
|
||||
icon: 'ion-card',
|
||||
label: 'Billing',
|
||||
hasSubMenu: false,
|
||||
toState: frontendURL('settings/billing'),
|
||||
toStateName: 'billing',
|
||||
},
|
||||
account: {
|
||||
icon: 'ion-beer',
|
||||
label: 'Account Settings',
|
||||
hasSubMenu: false,
|
||||
toState: frontendURL('settings/account'),
|
||||
toStateName: 'account',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -27,7 +27,8 @@
|
|||
"PLACEHOLDER": "Enter your website domain (eg: acme.com)"
|
||||
},
|
||||
"WIDGET_COLOR": {
|
||||
"LABEL": "Widget Color"
|
||||
"LABEL": "Widget Color",
|
||||
"PLACEHOLDER": "Update the widget color used in widget"
|
||||
},
|
||||
"SUBMIT_BUTTON":"Create inbox"
|
||||
},
|
||||
|
@ -52,10 +53,11 @@
|
|||
"LOADING_FB": "Authenticating you with Facebook...",
|
||||
"ERROR_FB_AUTH": "Something went wrong, Please refresh page...",
|
||||
"CREATING_CHANNEL": "Creating your Inbox...",
|
||||
"TITLE": "Configure Inbox Deatails",
|
||||
"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."
|
||||
"TITLE": "Configure Inbox Details",
|
||||
"DESC": ""
|
||||
},
|
||||
"AGENTS": {
|
||||
"BUTTON_TEXT": "Add agents",
|
||||
"ADD_AGENTS": "Adding Agents to your Inbox..."
|
||||
},
|
||||
"FINISH": {
|
||||
|
@ -66,6 +68,12 @@
|
|||
},
|
||||
"REAUTH": "Reauthorize",
|
||||
"VIEW": "View",
|
||||
"EDIT": {
|
||||
"API": {
|
||||
"SUCCESS_MESSAGE": "Widget color updated successfully",
|
||||
"ERROR_MESSAGE": "Could not update widget color. Please try again later."
|
||||
}
|
||||
},
|
||||
"DELETE": {
|
||||
"BUTTON_TEXT": "Delete",
|
||||
"CONFIRM": {
|
||||
|
|
|
@ -39,7 +39,6 @@ export default {
|
|||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
menuItems: 'getMenuItems',
|
||||
chatList: 'getAllConversations',
|
||||
}),
|
||||
},
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
<template>
|
||||
<div class="column">
|
||||
<h2 class="page-sub-title">
|
||||
{{headerTitle}}
|
||||
{{ headerTitle }}
|
||||
</h2>
|
||||
<p class="small-12 column" v-html="headerContent">
|
||||
</p>
|
||||
<p class="small-12 column" v-html="headerContent"></p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
<template>
|
||||
<div class="wizard-body columns content-box small-9">
|
||||
<loading-state v-if="showLoader" :message="emptyStateMessage">
|
||||
</loading-state>
|
||||
<form v-if="!showLoader" class="row" @submit.prevent="addAgents()">
|
||||
<form class="row" @submit.prevent="addAgents()">
|
||||
<div class="medium-12 columns">
|
||||
<page-header
|
||||
:header-title="$t('INBOX_MGMT.ADD.AGENTS.TITLE')"
|
||||
|
@ -31,8 +29,11 @@
|
|||
</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="medium-12 columns text-right">
|
||||
<input type="submit" value="Create Inbox" class="button" />
|
||||
<div class="medium-12 columns">
|
||||
<woot-submit-button
|
||||
:button-text="$t('INBOX_MGMT.AGENTS.BUTTON_TEXT')"
|
||||
:loading="isCreating"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -44,15 +45,13 @@
|
|||
/* global bus */
|
||||
import { mapGetters } from 'vuex';
|
||||
|
||||
import ChannelApi from '../../../../api/channels';
|
||||
import InboxMembersAPI from '../../../../api/inboxMembers';
|
||||
import router from '../../../index';
|
||||
import PageHeader from '../SettingsSubPageHeader';
|
||||
import LoadingState from '../../../../components/widgets/LoadingState';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
PageHeader,
|
||||
LoadingState,
|
||||
},
|
||||
|
||||
validations: {
|
||||
|
@ -65,9 +64,8 @@ export default {
|
|||
|
||||
data() {
|
||||
return {
|
||||
emptyStateMessage: this.$t('INBOX_MGMT.AGENTS.ADD_AGENTS'),
|
||||
showLoader: false,
|
||||
selectedAgents: [],
|
||||
isCreating: false,
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -82,25 +80,24 @@ export default {
|
|||
},
|
||||
|
||||
methods: {
|
||||
addAgents() {
|
||||
async addAgents() {
|
||||
this.isCreating = true;
|
||||
const inboxId = this.$route.params.inbox_id;
|
||||
ChannelApi.addAgentsToChannel(inboxId, this.selectedAgents.map(x => x.id))
|
||||
.then(() => {
|
||||
this.isCreating = false;
|
||||
router.replace({
|
||||
name: 'settings_inbox_finish',
|
||||
params: {
|
||||
page: 'new',
|
||||
inbox_id: this.$route.params.inbox_id,
|
||||
website_token: this.$route.params.website_token,
|
||||
},
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
bus.$emit('newToastMessage', error.message);
|
||||
this.isCreating = false;
|
||||
const selectedAgents = this.selectedAgents.map(x => x.id);
|
||||
|
||||
try {
|
||||
await InboxMembersAPI.create({ inboxId, agentList: selectedAgents });
|
||||
router.replace({
|
||||
name: 'settings_inbox_finish',
|
||||
params: {
|
||||
page: 'new',
|
||||
inbox_id: this.$route.params.inbox_id,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
bus.$emit('newToastMessage', error.message);
|
||||
}
|
||||
this.isCreating = false;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
>
|
||||
<div class="medium-12 columns text-center">
|
||||
<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>
|
||||
</div>
|
||||
<router-link
|
||||
|
@ -33,6 +33,11 @@ export default {
|
|||
EmptyState,
|
||||
},
|
||||
computed: {
|
||||
currentInbox() {
|
||||
return this.$store.getters['inboxes/getInbox'](
|
||||
this.$route.params.inbox_id
|
||||
);
|
||||
},
|
||||
message() {
|
||||
if (!this.$route.params.website_token) {
|
||||
return this.$t('INBOX_MGMT.FINISH.MESSAGE');
|
||||
|
@ -40,7 +45,7 @@ export default {
|
|||
return this.$t('INBOX_MGMT.FINISH.WEBSITE_SUCCESS');
|
||||
},
|
||||
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">
|
||||
<td>
|
||||
<img
|
||||
v-if="item.avatarUrl"
|
||||
v-if="item.avatar_url"
|
||||
class="woot-thumbnail"
|
||||
:src="item.avatarUrl"
|
||||
:src="item.avatar_url"
|
||||
alt="No Page Image"
|
||||
/>
|
||||
<img
|
||||
|
@ -32,11 +32,11 @@
|
|||
</td>
|
||||
<!-- Short Code -->
|
||||
<td>
|
||||
<span class="agent-name">{{ item.label }}</span>
|
||||
<span v-if="item.channelType === 'Channel::FacebookPage'">
|
||||
<span class="agent-name">{{ item.name }}</span>
|
||||
<span v-if="item.channel_type === 'Channel::FacebookPage'">
|
||||
Facebook
|
||||
</span>
|
||||
<span v-if="item.channelType === 'Channel::WebWidget'">
|
||||
<span v-if="item.channel_type === 'Channel::WebWidget'">
|
||||
Website
|
||||
</span>
|
||||
</td>
|
||||
|
@ -44,28 +44,23 @@
|
|||
<!-- Action Buttons -->
|
||||
<td>
|
||||
<div class="button-wrapper">
|
||||
<div v-if="isAdmin()" @click="openSettings(item)">
|
||||
<router-link :to="`/app/settings/inboxes/${item.id}`">
|
||||
<woot-submit-button
|
||||
v-if="isAdmin()"
|
||||
:button-text="$t('INBOX_MGMT.SETTINGS')"
|
||||
icon-class="ion-gear-b"
|
||||
button-class="link hollow grey-btn"
|
||||
/>
|
||||
</div>
|
||||
<!-- <div>
|
||||
<woot-submit-button
|
||||
:button-text="$t('INBOX_MGMT.REAUTH')"
|
||||
icon-class="ion-edit"
|
||||
</router-link>
|
||||
|
||||
<woot-submit-button
|
||||
v-if="isAdmin()"
|
||||
: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 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>
|
||||
@click="openDelete(item)"
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -120,22 +115,22 @@ export default {
|
|||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
inboxesList: 'getInboxesList',
|
||||
inboxesList: 'inboxes/getInboxes',
|
||||
}),
|
||||
// Delete Modal
|
||||
deleteConfirmText() {
|
||||
return `${this.$t('INBOX_MGMT.DELETE.CONFIRM.YES')} ${
|
||||
this.selectedInbox.label
|
||||
this.selectedInbox.name
|
||||
}`;
|
||||
},
|
||||
deleteRejectText() {
|
||||
return `${this.$t('INBOX_MGMT.DELETE.CONFIRM.NO')} ${
|
||||
this.selectedInbox.label
|
||||
this.selectedInbox.name
|
||||
}`;
|
||||
},
|
||||
deleteMessage() {
|
||||
return `${this.$t('INBOX_MGMT.DELETE.CONFIRM.MESSAGE')} ${
|
||||
this.selectedInbox.label
|
||||
this.selectedInbox.name
|
||||
} ?`;
|
||||
},
|
||||
},
|
||||
|
@ -148,21 +143,19 @@ export default {
|
|||
this.showSettings = false;
|
||||
this.selectedInbox = {};
|
||||
},
|
||||
deleteInbox({ channel_id }) {
|
||||
this.$store
|
||||
.dispatch('deleteInbox', channel_id)
|
||||
.then(() =>
|
||||
bus.$emit(
|
||||
'newToastMessage',
|
||||
this.$t('INBOX_MGMT.DELETE.API.SUCCESS_MESSAGE')
|
||||
)
|
||||
)
|
||||
.catch(() =>
|
||||
bus.$emit(
|
||||
'newToastMessage',
|
||||
this.$t('INBOX_MGMT.DELETE.API.ERROR_MESSAGE')
|
||||
)
|
||||
async deleteInbox({ id }) {
|
||||
try {
|
||||
await this.$store.dispatch('inboxes/delete', id);
|
||||
bus.$emit(
|
||||
'newToastMessage',
|
||||
this.$t('INBOX_MGMT.DELETE.API.SUCCESS_MESSAGE')
|
||||
);
|
||||
} catch (error) {
|
||||
bus.$emit(
|
||||
'newToastMessage',
|
||||
this.$t('INBOX_MGMT.DELETE.API.ERROR_MESSAGE')
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
confirmDeletion() {
|
||||
|
|
|
@ -1,148 +1,170 @@
|
|||
<template>
|
||||
<woot-modal class-name="settings-modal" :show.sync="show" :on-close="onClose">
|
||||
<div class="settings">
|
||||
<woot-modal-header
|
||||
:header-image="inbox.avatarUrl"
|
||||
:header-title="inbox.label"
|
||||
/>
|
||||
<div
|
||||
v-if="inbox.channelType === 'Channel::FacebookPage'"
|
||||
class="settings--content"
|
||||
<div class="settings columns container">
|
||||
<woot-modal-header
|
||||
:header-image="inbox.avatarUrl"
|
||||
:header-title="inbox.name"
|
||||
/>
|
||||
<div
|
||||
v-if="inbox.channel_type === 'Channel::FacebookPage'"
|
||||
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
|
||||
: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.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> -->
|
||||
<woot-code :script="webWidgetScript"></woot-code>
|
||||
</div>
|
||||
<div class="settings--content">
|
||||
<settings-form-header
|
||||
:title="$t('INBOX_MGMT.SETTINGS_POPUP.INBOX_AGENTS')"
|
||||
:sub-title="$t('INBOX_MGMT.SETTINGS_POPUP.INBOX_AGENTS_SUB_TEXT')"
|
||||
:title="$t('INBOX_MGMT.ADD.WEBSITE_CHANNEL.WIDGET_COLOR.LABEL')"
|
||||
:sub-title="
|
||||
$t('INBOX_MGMT.ADD.WEBSITE_CHANNEL.WIDGET_COLOR.PLACEHOLDER')
|
||||
"
|
||||
:button-text="$t('INBOX_MGMT.SETTINGS_POPUP.UPDATE')"
|
||||
:is-updating="isUpdating"
|
||||
@update="updateAgents"
|
||||
:is-updating="uiFlags.isUpdating"
|
||||
@update="updateWidgetColor"
|
||||
>
|
||||
</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"
|
||||
/>
|
||||
<Compact v-model="inbox.widget_color" class="widget-color--selector" />
|
||||
</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>
|
||||
|
||||
<script>
|
||||
/* eslint no-console: 0 */
|
||||
/* eslint-disable no-useless-escape */
|
||||
/* global bus */
|
||||
import { mapGetters } from 'vuex';
|
||||
import {
|
||||
createWebsiteWidgetScript,
|
||||
createMessengerScript,
|
||||
} from 'dashboard/helper/scriptGenerator';
|
||||
import { Compact } from 'vue-color';
|
||||
import SettingsFormHeader from '../../../../components/SettingsFormHeader.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Compact,
|
||||
SettingsFormHeader,
|
||||
},
|
||||
props: ['onClose', 'inbox', 'show'],
|
||||
data() {
|
||||
return {
|
||||
selectedAgents: [],
|
||||
isUpdating: false,
|
||||
widgetColor: { hex: this.inbox.widgetColor },
|
||||
isAgentListUpdating: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
agentList: 'agents/getAgents',
|
||||
uiFlags: 'inboxes/getUIFlags',
|
||||
}),
|
||||
currentInboxId() {
|
||||
return this.$route.params.inboxId;
|
||||
},
|
||||
inbox() {
|
||||
return this.$store.getters['inboxes/getInbox'](this.currentInboxId);
|
||||
},
|
||||
webWidgetScript() {
|
||||
return createWebsiteWidgetScript(this.inbox.websiteToken);
|
||||
return createWebsiteWidgetScript(this.inbox.website_token);
|
||||
},
|
||||
messengerScript() {
|
||||
return createMessengerScript(this.inbox.pageId);
|
||||
return createMessengerScript(this.inbox.page_id);
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.$store.dispatch('agents/get').then(() => {
|
||||
this.$store.dispatch('agents/get');
|
||||
this.$store.dispatch('inboxes/get').then(() => {
|
||||
this.fetchAttachedAgents();
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
fetchAttachedAgents() {
|
||||
this.$store
|
||||
.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);
|
||||
});
|
||||
showAlert(message) {
|
||||
bus.$emit('newToastMessage', message);
|
||||
},
|
||||
updateAgents() {
|
||||
const agentList = this.selectedAgents.map(el => el.id);
|
||||
this.isUpdating = true;
|
||||
this.$store
|
||||
.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')
|
||||
);
|
||||
async fetchAttachedAgents() {
|
||||
try {
|
||||
const response = await this.$store.dispatch('inboxMembers/get', {
|
||||
inboxId: this.currentInboxId,
|
||||
});
|
||||
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: {
|
||||
|
|
|
@ -219,14 +219,11 @@ export default {
|
|||
this.emptyStateMessage = this.$t('INBOX_MGMT.DETAILS.CREATING_CHANNEL');
|
||||
this.isCreating = true;
|
||||
this.$store
|
||||
.dispatch('addInboxItem', {
|
||||
channel: this.channel,
|
||||
params: this.channelParams(),
|
||||
})
|
||||
.then(response => {
|
||||
.dispatch('inboxes/createFBChannel', this.channelParams())
|
||||
.then(data => {
|
||||
router.replace({
|
||||
name: 'settings_inboxes_add_agents',
|
||||
params: { page: 'new', inbox_id: response.data.id },
|
||||
params: { page: 'new', inbox_id: data.id },
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
|
|
|
@ -55,8 +55,8 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
/* global bus */
|
||||
import { Compact } from 'vue-color';
|
||||
import { mapGetters } from 'vuex';
|
||||
import router from '../../../../index';
|
||||
import PageHeader from '../../SettingsSubPageHeader';
|
||||
|
||||
|
@ -73,26 +73,28 @@ export default {
|
|||
isCreating: false,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
bus.$on('new_website_channel', ({ inboxId, websiteToken }) => {
|
||||
computed: {
|
||||
...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({
|
||||
name: 'settings_inboxes_add_agents',
|
||||
params: {
|
||||
page: 'new',
|
||||
inbox_id: inboxId,
|
||||
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,
|
||||
inbox_id: website.id,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/* eslint arrow-body-style: 0 */
|
||||
import SettingsContent from '../Wrapper';
|
||||
import Settings from './Settings';
|
||||
import InboxHome from './Index';
|
||||
import InboxChannel from './InboxChannels';
|
||||
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 Channel from './modules/channels';
|
||||
import conversations from './modules/conversations';
|
||||
import inboxes from './modules/inboxes';
|
||||
import inboxMembers from './modules/inboxMembers';
|
||||
import reports from './modules/reports';
|
||||
import sideMenuItems from './modules/sidebar';
|
||||
|
||||
Vue.use(Vuex);
|
||||
export default new Vuex.Store({
|
||||
|
@ -19,7 +20,8 @@ export default new Vuex.Store({
|
|||
cannedResponse,
|
||||
Channel,
|
||||
conversations,
|
||||
inboxes,
|
||||
inboxMembers,
|
||||
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_ACTIVE_INBOX: 'SET_ACTIVE_INBOX',
|
||||
|
||||
// labels
|
||||
SET_LABELS: 'SET_LABELS',
|
||||
|
||||
// Set Inboxes
|
||||
INBOXES_LOADING: 'INBOXES_LOADING',
|
||||
// Inboxes
|
||||
SET_INBOXES_UI_FLAG: 'SET_INBOXES_UI_FLAG',
|
||||
SET_INBOXES: 'SET_INBOXES',
|
||||
SET_INBOX_ITEM: 'SET_INBOX_ITEM',
|
||||
DELETE_INBOX: 'DELETE_INBOX',
|
||||
ADD_INBOXES: 'ADD_INBOXES',
|
||||
EDIT_INBOXES: 'EDIT_INBOXES',
|
||||
DELETE_INBOXES: 'DELETE_INBOXES',
|
||||
|
||||
// Agent
|
||||
SET_AGENT_FETCHING_STATUS: 'SET_AGENT_FETCHING_STATUS',
|
||||
|
|
|
@ -6,6 +6,15 @@ export const create = (state, 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) => {
|
||||
state.records.forEach((element, index) => {
|
||||
if (element.id === data.id) {
|
||||
|
|
|
@ -39,6 +39,12 @@ $input-height: $space-two * 2;
|
|||
padding: $space-small $space-slab;
|
||||
}
|
||||
|
||||
&.default {
|
||||
font-size: $font-size-default;
|
||||
height: $space-medium;
|
||||
padding: $space-smaller $space-slab;
|
||||
}
|
||||
|
||||
&.large {
|
||||
font-size: $font-size-medium;
|
||||
height: $space-larger;
|
||||
|
|
|
@ -22,6 +22,7 @@ module Channel
|
|||
|
||||
validates :website_name, presence: true
|
||||
validates :website_url, presence: true
|
||||
validates :widget_color, presence: true
|
||||
|
||||
belongs_to :account
|
||||
has_one :inbox, as: :channel, dependent: :destroy
|
||||
|
|
|
@ -24,6 +24,10 @@ class InboxPolicy < ApplicationPolicy
|
|||
@user.administrator?
|
||||
end
|
||||
|
||||
def update?
|
||||
@user.administrator?
|
||||
end
|
||||
|
||||
def destroy?
|
||||
@user.administrator?
|
||||
end
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
json.data do
|
||||
json.payload do
|
||||
json.array! @agents do |agent|
|
||||
json.user_id agent.id
|
||||
json.name agent.name
|
||||
end
|
||||
json.payload do
|
||||
json.array! @agents do |agent|
|
||||
json.user_id agent.id
|
||||
json.name agent.name
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,17 +1,12 @@
|
|||
json.data do
|
||||
json.meta do
|
||||
end
|
||||
|
||||
json.payload do
|
||||
json.array! @inboxes do |inbox|
|
||||
json.id inbox.id
|
||||
json.channel_id inbox.channel_id
|
||||
json.name inbox.name
|
||||
json.channel_type inbox.channel_type
|
||||
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
|
||||
json.payload do
|
||||
json.array! @inboxes do |inbox|
|
||||
json.id inbox.id
|
||||
json.channel_id inbox.channel_id
|
||||
json.name inbox.name
|
||||
json.channel_type inbox.channel_type
|
||||
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
|
||||
|
|
|
@ -3,3 +3,4 @@ json.channel_id @inbox.channel_id
|
|||
json.name @inbox.name
|
||||
json.channel_type @inbox.channel_type
|
||||
json.website_token @inbox.channel.try(:website_token)
|
||||
json.widget_color @inbox.channel.try(:widget_color)
|
||||
|
|
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
|
||||
resources :messages, only: [:index, :create]
|
||||
resources :inboxes, only: [:create]
|
||||
resources :inboxes, only: [:create, :update]
|
||||
end
|
||||
|
||||
namespace :actions do
|
||||
|
@ -92,7 +92,7 @@ Rails.application.routes.draw do
|
|||
|
||||
# Sidekiq Web UI
|
||||
require 'sidekiq/web'
|
||||
authenticate :user, lambda { |u| u.administrator? } do
|
||||
authenticate :user, ->(u) { u.administrator? } do
|
||||
mount Sidekiq::Web => '/sidekiq'
|
||||
end
|
||||
|
||||
|
|
|
@ -2,11 +2,13 @@ require 'rails_helper'
|
|||
|
||||
RSpec.describe '/api/v1/widget/inboxes', type: :request do
|
||||
let(:account) { create(:account) }
|
||||
let(:inbox) { create(:inbox, account: account) }
|
||||
let(:admin) { create(:user, account: account, role: :administrator) }
|
||||
let(:agent) { create(:user, account: account, role: :agent) }
|
||||
let(:params) { { website: { website_name: 'test', website_url: 'test.com' } } }
|
||||
|
||||
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
|
||||
it 'returns unauthorized' do
|
||||
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 'with user as administrator' 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)
|
||||
json_response = JSON.parse(response.body)
|
||||
|
@ -31,8 +33,43 @@ RSpec.describe '/api/v1/widget/inboxes', type: :request do
|
|||
it 'returns unauthorized' do
|
||||
post '/api/v1/widget/inboxes',
|
||||
params: params,
|
||||
headers: agent.create_new_auth_token,
|
||||
as: :json
|
||||
headers: agent.create_new_auth_token
|
||||
|
||||
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)
|
||||
end
|
||||
|
|
|
@ -4,6 +4,7 @@ FactoryBot.define do
|
|||
factory :channel_widget, class: 'Channel::WebWidget' do
|
||||
sequence(:website_name) { |n| "Example Website #{n}" }
|
||||
sequence(:website_url) { |n| "https://example-#{n}.com" }
|
||||
sequence(:widget_color, &:to_s)
|
||||
account
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
FactoryBot.define do
|
||||
factory :inbox do
|
||||
account
|
||||
association :channel, factory: :channel_widget
|
||||
name { 'Inbox' }
|
||||
channel { FactoryBot.build(:channel_widget, account: account) }
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue