Move src to dashboard (#152)
This commit is contained in:
27
app/javascript/dashboard/routes/dashboard/Dashboard.vue
Normal file
27
app/javascript/dashboard/routes/dashboard/Dashboard.vue
Normal file
@@ -0,0 +1,27 @@
|
||||
<template>
|
||||
<div class="row app-wrapper">
|
||||
<sidebar :route="currentRoute"></sidebar>
|
||||
<router-view></router-view>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/* eslint no-console: 0 */
|
||||
import Sidebar from '../../components/layout/Sidebar';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
mainViewComponent: String,
|
||||
sidebarMenu: String,
|
||||
page: String,
|
||||
},
|
||||
components: {
|
||||
Sidebar,
|
||||
},
|
||||
computed: {
|
||||
currentRoute() {
|
||||
return ' ';
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,67 @@
|
||||
<template>
|
||||
<section class="app-content columns">
|
||||
<chat-list :conversationInbox="inboxId" :pageTitle="$t('CHAT_LIST.TAB_HEADING')" ></chat-list>
|
||||
<conversation-box :inbox-id="inboxId"></conversation-box>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/* eslint no-console: 0 */
|
||||
/* global bus */
|
||||
import { mapGetters } from 'vuex';
|
||||
|
||||
import ChatList from '../../../components/ChatList';
|
||||
import ConversationBox from '../../../components/widgets/conversation/ConversationBox';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ChatList,
|
||||
ConversationBox,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
pageTitle: this.$state,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
menuItems: 'getMenuItems',
|
||||
chatList: 'getAllConversations',
|
||||
}),
|
||||
},
|
||||
props: ['inboxId', 'conversationId'],
|
||||
|
||||
mounted() {
|
||||
this.$watch('$store.state.route', () => {
|
||||
switch (this.$store.state.route.name) {
|
||||
case 'inbox_conversation':
|
||||
this.setActiveChat();
|
||||
break;
|
||||
case 'inbox_dashboard':
|
||||
if (this.inboxId) {
|
||||
this.$store.dispatch('setActiveInbox', this.inboxId);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
this.$store.dispatch('setActiveInbox', null);
|
||||
break;
|
||||
}
|
||||
});
|
||||
this.$watch('chatList.length', () => {
|
||||
this.setActiveChat();
|
||||
});
|
||||
},
|
||||
|
||||
methods: {
|
||||
setActiveChat() {
|
||||
const conversationId = parseInt(this.conversationId, 10);
|
||||
const [chat] = this.chatList.filter(c => c.id === conversationId);
|
||||
if (!chat) return;
|
||||
this.$store.dispatch('setActiveChat', chat).then(() => {
|
||||
bus.$emit('scrollToMessage');
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,35 @@
|
||||
/* eslint arrow-body-style: 0 */
|
||||
import ConversationView from './ConversationView';
|
||||
import { frontendURL } from '../../../helper/URLHelper';
|
||||
|
||||
export default {
|
||||
routes: [
|
||||
{
|
||||
path: frontendURL('dashboard'),
|
||||
name: 'home',
|
||||
roles: ['administrator', 'agent'],
|
||||
component: ConversationView,
|
||||
props: () => {
|
||||
return { inboxId: 0 };
|
||||
},
|
||||
},
|
||||
{
|
||||
path: frontendURL('inbox/:inbox_id'),
|
||||
name: 'inbox_dashboard',
|
||||
roles: ['administrator', 'agent'],
|
||||
component: ConversationView,
|
||||
props: route => {
|
||||
return { inboxId: route.params.inbox_id };
|
||||
},
|
||||
},
|
||||
{
|
||||
path: frontendURL('conversations/:conversation_id'),
|
||||
name: 'inbox_conversation',
|
||||
roles: ['administrator', 'agent'],
|
||||
component: ConversationView,
|
||||
props: route => {
|
||||
return { conversationId: route.params.conversation_id };
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,14 @@
|
||||
import AppContainer from './Dashboard';
|
||||
import settings from './settings/settings.routes';
|
||||
import conversation from './conversation/conversation.routes';
|
||||
import { frontendURL } from '../../helper/URLHelper';
|
||||
|
||||
export default {
|
||||
routes: [
|
||||
{
|
||||
path: frontendURL(''),
|
||||
component: AppContainer,
|
||||
children: [...conversation.routes, ...settings.routes],
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<div class="settings-header">
|
||||
<h1 class="page-title">
|
||||
<back-button v-if="!showButton"></back-button>
|
||||
<i :class="icon"></i>
|
||||
<span>{{ headerTitle }}</span>
|
||||
</h1>
|
||||
<router-link
|
||||
:to="buttonRoute"
|
||||
class="button icon success"
|
||||
v-if="showNewButton && showButton && currentRole"
|
||||
>
|
||||
<i class="icon ion-android-add-circle"></i>
|
||||
{{buttonText}}
|
||||
</router-link>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import BackButton from '../../../components/widgets/BackButton';
|
||||
import Auth from '../../../api/auth';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
headerTitle: String,
|
||||
buttonRoute: String,
|
||||
buttonText: String,
|
||||
icon: String,
|
||||
showButton: Boolean,
|
||||
showNewButton: Boolean,
|
||||
hideButtonRoutes: {
|
||||
type: Array,
|
||||
default() {
|
||||
return ['agent_list', 'settings_inbox_list'];
|
||||
},
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
iconClass() {
|
||||
return `icon ${this.props.icon}`;
|
||||
},
|
||||
currentRole() {
|
||||
const { role } = Auth.getCurrentUser();
|
||||
return role === 'administrator';
|
||||
},
|
||||
},
|
||||
components: {
|
||||
BackButton,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,18 @@
|
||||
<template>
|
||||
<div class="column">
|
||||
<h2 class="page-sub-title">
|
||||
{{headerTitle}}
|
||||
</h2>
|
||||
<p class="small-12 column" v-html="headerContent">
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
headerTitle: String,
|
||||
headerContent: String,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,54 @@
|
||||
<template>
|
||||
<section class="app-content columns">
|
||||
<div class="view-box columns bg-light">
|
||||
<settings-header
|
||||
button-route="new"
|
||||
:icon="icon"
|
||||
:header-title="$t(headerTitle)"
|
||||
:button-text="$t(headerButtonText)"
|
||||
:show-button="showButton()"
|
||||
:show-new-button="showNewButton()"
|
||||
/>
|
||||
<!-- <transition name="slide-fade"> -->
|
||||
<keep-alive>
|
||||
<router-view></router-view>
|
||||
</keep-alive>
|
||||
<!-- </transition> -->
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/* eslint no-console: 0 */
|
||||
import SettingsHeader from './SettingsHeader';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
};
|
||||
},
|
||||
props: {
|
||||
headerTitle: String,
|
||||
headerButtonText: String,
|
||||
icon: String,
|
||||
newButtonRoutes: Array,
|
||||
},
|
||||
components: {
|
||||
SettingsHeader,
|
||||
},
|
||||
computed: {
|
||||
currentPage() {
|
||||
return this.$store.state.route.name;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
showButton() {
|
||||
/* eslint-disable no-unneeded-ternary */
|
||||
return this.newButtonRoutes ? this.newButtonRoutes.indexOf(this.currentPage) > -1 : true;
|
||||
},
|
||||
showNewButton() {
|
||||
return this.newButtonRoutes ? true : false;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,146 @@
|
||||
<template>
|
||||
<woot-modal :show.sync="show" :on-close="onClose">
|
||||
|
||||
<div class="column content-box">
|
||||
<woot-modal-header
|
||||
:header-image="headerImage"
|
||||
:header-title="$t('AGENT_MGMT.ADD.TITLE')"
|
||||
:header-content="$t('AGENT_MGMT.ADD.DESC')"
|
||||
/>
|
||||
|
||||
<form class="row" v-on:submit.prevent="addAgent()">
|
||||
<div class="medium-12 columns">
|
||||
<label :class="{ 'error': $v.agentName.$error }">
|
||||
{{ $t('AGENT_MGMT.ADD.FORM.NAME.LABEL') }}
|
||||
<input type="text" v-model.trim="agentName" @input="$v.agentName.$touch" :placeholder="$t('AGENT_MGMT.ADD.FORM.NAME.PLACEHOLDER')">
|
||||
</label>
|
||||
</div>
|
||||
<div class="medium-12 columns">
|
||||
<label :class="{ 'error': $v.agentType.$error }">
|
||||
{{ $t('AGENT_MGMT.ADD.FORM.AGENT_TYPE.LABEL') }}
|
||||
<multiselect
|
||||
v-model="agentType"
|
||||
:options="agentTypeList"
|
||||
:searchable="false"
|
||||
label="label"
|
||||
:placeholder="$t('AGENT_MGMT.ADD.FORM.AGENT_TYPE.PLACEHOLDER')"
|
||||
@select="setPageName"
|
||||
:allow-empty="true"
|
||||
:close-on-select="true"
|
||||
/>
|
||||
<span class="message" v-if="$v.agentType.$error">
|
||||
{{ $t('AGENT_MGMT.ADD.FORM.AGENT_TYPE.ERROR') }}
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="medium-12 columns">
|
||||
<label :class="{ 'error': $v.agentEmail.$error }">
|
||||
{{ $t('AGENT_MGMT.ADD.FORM.EMAIL.LABEL') }}
|
||||
<input type="text" v-model.trim="agentEmail" @input="$v.agentEmail.$touch" :placeholder="$t('AGENT_MGMT.ADD.FORM.EMAIL.PLACEHOLDER')">
|
||||
</label>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<div class="medium-12 columns">
|
||||
<woot-submit-button
|
||||
:disabled="$v.agentEmail.$invalid || $v.agentName.$invalid || addAgentsApi.showLoading"
|
||||
:button-text="$t('AGENT_MGMT.ADD.FORM.SUBMIT')"
|
||||
:loading="addAgentsApi.showLoading"
|
||||
/>
|
||||
<a @click="onClose">Cancel</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</woot-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/* global bus */
|
||||
/* eslint no-console: 0 */
|
||||
import { required, minLength, email } from 'vuelidate/lib/validators';
|
||||
|
||||
import PageHeader from '../SettingsSubPageHeader';
|
||||
|
||||
const agentImg = require('assets/images/agent.svg');
|
||||
|
||||
export default {
|
||||
props: [
|
||||
'onClose',
|
||||
],
|
||||
components: {
|
||||
PageHeader,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
agentName: '',
|
||||
agentEmail: '',
|
||||
agentType: this.$t('AGENT_MGMT.AGENT_TYPES')[1],
|
||||
vertical: 'bottom',
|
||||
horizontal: 'center',
|
||||
addAgentsApi: {
|
||||
showAlert: false,
|
||||
showLoading: false,
|
||||
message: '',
|
||||
},
|
||||
agentTypeList: this.$t('AGENT_MGMT.AGENT_TYPES'),
|
||||
show: true,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
headerImage() {
|
||||
return agentImg;
|
||||
},
|
||||
},
|
||||
validations: {
|
||||
agentName: {
|
||||
required,
|
||||
minLength: minLength(4),
|
||||
},
|
||||
agentEmail: {
|
||||
required,
|
||||
email,
|
||||
},
|
||||
agentType: {
|
||||
required,
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
setPageName({ name }) {
|
||||
this.$v.agentType.$touch();
|
||||
this.agentType = name;
|
||||
},
|
||||
showAlert() {
|
||||
bus.$emit('newToastMessage', this.addAgentsApi.message);
|
||||
},
|
||||
resetForm() {
|
||||
this.agentName = this.agentEmail = '';
|
||||
this.$v.agentName.$reset();
|
||||
this.$v.agentEmail.$reset();
|
||||
},
|
||||
addAgent() {
|
||||
// Show loading on button
|
||||
this.addAgentsApi.showLoading = true;
|
||||
// Make API Calls
|
||||
this.$store.dispatch('addAgent', {
|
||||
name: this.agentName,
|
||||
email: this.agentEmail,
|
||||
role: this.agentType.name.toLowerCase(),
|
||||
})
|
||||
.then(() => {
|
||||
// Reset Form, Show success message
|
||||
this.addAgentsApi.showLoading = false;
|
||||
this.addAgentsApi.message = this.$t('AGENT_MGMT.ADD.API.SUCCESS_MESSAGE');
|
||||
this.showAlert();
|
||||
this.resetForm();
|
||||
this.onClose();
|
||||
})
|
||||
.catch(() => {
|
||||
this.addAgentsApi.showLoading = false;
|
||||
this.addAgentsApi.message = this.$t('AGENT_MGMT.ADD.API.ERROR_MESSAGE');
|
||||
this.showAlert();
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<modal
|
||||
:show.sync="show"
|
||||
:on-close="onClose"
|
||||
>
|
||||
<woot-modal-header
|
||||
:header-title="title"
|
||||
:header-content="message"
|
||||
/>
|
||||
<div class="modal-footer delete-item">
|
||||
<button class="button" @click="onClose">
|
||||
{{ rejectText }}
|
||||
</button>
|
||||
<button class="alert button" @click="onConfirm">
|
||||
{{ confirmText }}
|
||||
</button>
|
||||
</div>
|
||||
</modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import PageHeader from '../SettingsSubPageHeader';
|
||||
import Modal from '../../../../components/Modal';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Modal,
|
||||
PageHeader,
|
||||
},
|
||||
props: {
|
||||
show: Boolean,
|
||||
onClose: Function,
|
||||
onConfirm: Function,
|
||||
title: String,
|
||||
message: String,
|
||||
confirmText: String,
|
||||
rejectText: String,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,137 @@
|
||||
<template>
|
||||
<modal :show.sync="show" :on-close="onClose">
|
||||
<div class="column content-box">
|
||||
<woot-modal-header
|
||||
:header-title="pageTitle"
|
||||
/>
|
||||
<form class="row medium-8" v-on:submit.prevent="editAgent()">
|
||||
<div class="medium-12 columns">
|
||||
<label :class="{ 'error': $v.agentName.$error }">
|
||||
{{ $t('AGENT_MGMT.EDIT.FORM.NAME.LABEL') }}
|
||||
<input type="text" v-model.trim="agentName" @input="$v.agentName.$touch" :placeholder="$t('AGENT_MGMT.EDIT.FORM.NAME.PLACEHOLDER')">
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="medium-12 columns">
|
||||
<label :class="{ 'error': $v.agentType.$error }">
|
||||
{{ $t('AGENT_MGMT.EDIT.FORM.AGENT_TYPE.LABEL') }}
|
||||
<multiselect
|
||||
v-model.trim="agentType"
|
||||
:options="agentTypeList"
|
||||
label="label"
|
||||
:placeholder="$t('AGENT_MGMT.EDIT.FORM.AGENT_TYPE.PLACEHOLDER')"
|
||||
:searchable="false"
|
||||
@select="setPageName"
|
||||
/>
|
||||
<span class="message" v-if="$v.agentType.$error">
|
||||
{{ $t('AGENT_MGMT.EDIT.FORM.AGENT_TYPE.ERROR') }}
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<div class="medium-12 columns">
|
||||
<woot-submit-button
|
||||
:disabled="$v.agentType.$invalid || $v.agentName.$invalid || editAgentsApi.showLoading"
|
||||
:button-text="$t('AGENT_MGMT.EDIT.FORM.SUBMIT')"
|
||||
:loading="editAgentsApi.showLoading"
|
||||
/>
|
||||
<a @click="onClose">Cancel</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/* global bus */
|
||||
/* eslint no-console: 0 */
|
||||
import { required, minLength } from 'vuelidate/lib/validators';
|
||||
|
||||
import PageHeader from '../SettingsSubPageHeader';
|
||||
import WootSubmitButton from '../../../../components/buttons/FormSubmitButton';
|
||||
import Modal from '../../../../components/Modal';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
PageHeader,
|
||||
WootSubmitButton,
|
||||
Modal,
|
||||
},
|
||||
props: {
|
||||
id: Number,
|
||||
name: String,
|
||||
type: String,
|
||||
onClose: Function,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
editAgentsApi: {
|
||||
showAlert: false,
|
||||
showLoading: false,
|
||||
message: '',
|
||||
},
|
||||
agentTypeList: this.$t('AGENT_MGMT.AGENT_TYPES'),
|
||||
agentName: this.name,
|
||||
agentType: {
|
||||
name: this.type,
|
||||
label: this.type,
|
||||
},
|
||||
show: true,
|
||||
};
|
||||
},
|
||||
validations: {
|
||||
agentName: {
|
||||
required,
|
||||
minLength: minLength(4),
|
||||
},
|
||||
agentType: {
|
||||
required,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
pageTitle() {
|
||||
return `${this.$t('AGENT_MGMT.EDIT.TITLE')} - ${this.name}`;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
setPageName({ name }) {
|
||||
this.$v.agentType.$touch();
|
||||
this.agentType = name;
|
||||
},
|
||||
showAlert() {
|
||||
bus.$emit('newToastMessage', this.editAgentsApi.message);
|
||||
},
|
||||
resetForm() {
|
||||
this.agentName = this.agentType = '';
|
||||
this.$v.agentName.$reset();
|
||||
this.$v.agentType.$reset();
|
||||
},
|
||||
editAgent() {
|
||||
// Show loading on button
|
||||
this.editAgentsApi.showLoading = true;
|
||||
// Make API Calls
|
||||
this.$store.dispatch('editAgent', {
|
||||
id: this.id,
|
||||
name: this.agentName,
|
||||
role: this.agentType.name.toLowerCase(),
|
||||
})
|
||||
.then(() => {
|
||||
// Reset Form, Show success message
|
||||
this.editAgentsApi.showLoading = false;
|
||||
this.editAgentsApi.message = this.$t('AGENT_MGMT.EDIT.API.SUCCESS_MESSAGE');
|
||||
this.showAlert();
|
||||
this.resetForm();
|
||||
setTimeout(() => {
|
||||
this.onClose();
|
||||
}, 10);
|
||||
})
|
||||
.catch(() => {
|
||||
this.editAgentsApi.showLoading = false;
|
||||
this.editAgentsApi.message = this.$t('AGENT_MGMT.EDIT.API.ERROR_MESSAGE');
|
||||
this.showAlert();
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,224 @@
|
||||
<template>
|
||||
<div class="column content-box">
|
||||
<button
|
||||
class="button nice icon success btn-fixed-right-top"
|
||||
@click="openAddPopup()"
|
||||
>
|
||||
<i class="icon ion-android-add-circle"></i>
|
||||
{{ $t('AGENT_MGMT.HEADER_BTN_TXT') }}
|
||||
</button>
|
||||
<!-- Canned Response API Status -->
|
||||
|
||||
<!-- List Agents -->
|
||||
<div class="row">
|
||||
<div class="small-8 columns">
|
||||
<woot-loading-state
|
||||
v-if="fetchStatus"
|
||||
:message="$t('AGENT_MGMT.LOADING')"
|
||||
/>
|
||||
<p v-if="!fetchStatus && !agentList.length">
|
||||
{{ $t('AGENT_MGMT.LIST.404') }}
|
||||
</p>
|
||||
<table v-if="!fetchStatus && agentList.length" class="woot-table">
|
||||
<tbody>
|
||||
<tr v-for="(agent, index) in agentList" :key="agent.email">
|
||||
<!-- Gravtar Image -->
|
||||
<td>
|
||||
<img class="woot-thumbnail" :src="gravatarUrl(agent.email)" />
|
||||
</td>
|
||||
<!-- Agent Name + Email -->
|
||||
<td>
|
||||
<span class="agent-name">{{ agent.name }}</span>
|
||||
<span>{{ agent.email }}</span>
|
||||
</td>
|
||||
<!-- Agent Role + Verification Status -->
|
||||
<td>
|
||||
<span class="agent-name">{{ agent.role }}</span>
|
||||
<span v-if="agent.confirmed">
|
||||
{{ $t('AGENT_MGMT.LIST.VERIFIED') }}
|
||||
</span>
|
||||
<span v-if="!agent.confirmed">
|
||||
{{ $t('AGENT_MGMT.LIST.VERIFICATION_PENDING') }}
|
||||
</span>
|
||||
</td>
|
||||
<!-- Actions -->
|
||||
<td>
|
||||
<div v-if="showActions(agent)" class="button-wrapper">
|
||||
<div @click="openEditPopup(agent)">
|
||||
<woot-submit-button
|
||||
:button-text="$t('AGENT_MGMT.EDIT.BUTTON_TEXT')"
|
||||
icon-class="ion-edit"
|
||||
button-class="link hollow grey-btn"
|
||||
/>
|
||||
</div>
|
||||
<div @click="openDeletePopup(agent, index)">
|
||||
<woot-submit-button
|
||||
:button-text="$t('AGENT_MGMT.DELETE.BUTTON_TEXT')"
|
||||
:loading="loading[agent.id]"
|
||||
icon-class="ion-close-circled"
|
||||
button-class="link hollow grey-btn"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="small-4 columns">
|
||||
<span v-html="$t('AGENT_MGMT.SIDEBAR_TXT')"></span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Add Agent -->
|
||||
<woot-modal :show.sync="showAddPopup" :on-close="hideAddPopup">
|
||||
<add-agent :on-close="hideAddPopup" />
|
||||
</woot-modal>
|
||||
|
||||
<!-- Edit Agent -->
|
||||
<woot-modal :show.sync="showEditPopup" :on-close="hideEditPopup">
|
||||
<edit-agent
|
||||
v-if="showEditPopup"
|
||||
:id="currentAgent.id"
|
||||
:name="currentAgent.name"
|
||||
:type="currentAgent.role"
|
||||
:email="currentAgent.email"
|
||||
:on-close="hideEditPopup"
|
||||
/>
|
||||
</woot-modal>
|
||||
|
||||
<!-- Delete Agent -->
|
||||
<delete-agent
|
||||
:show.sync="showDeletePopup"
|
||||
:on-close="closeDeletePopup"
|
||||
:on-confirm="confirmDeletion"
|
||||
:title="$t('AGENT_MGMT.DELETE.CONFIRM.TITLE')"
|
||||
:message="deleteMessage"
|
||||
:confirm-text="deleteConfirmText"
|
||||
:reject-text="deleteRejectText"
|
||||
/>
|
||||
|
||||
<!-- Loader Status -->
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
/* global bus */
|
||||
|
||||
import { mapGetters } from 'vuex';
|
||||
import md5 from 'md5';
|
||||
|
||||
import AddAgent from './AddAgent';
|
||||
import EditAgent from './EditAgent';
|
||||
import DeleteAgent from './DeleteAgent';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
AddAgent,
|
||||
EditAgent,
|
||||
DeleteAgent,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: {},
|
||||
showAddPopup: false,
|
||||
showDeletePopup: false,
|
||||
showEditPopup: false,
|
||||
agentAPI: {
|
||||
message: '',
|
||||
},
|
||||
currentAgent: {},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
agentList: 'getAgents',
|
||||
fetchStatus: 'getAgentFetchStatus',
|
||||
}),
|
||||
deleteConfirmText() {
|
||||
return `${this.$t('AGENT_MGMT.DELETE.CONFIRM.YES')} ${
|
||||
this.currentAgent.name
|
||||
}`;
|
||||
},
|
||||
deleteRejectText() {
|
||||
return `${this.$t('AGENT_MGMT.DELETE.CONFIRM.NO')} ${
|
||||
this.currentAgent.name
|
||||
}`;
|
||||
},
|
||||
deleteMessage() {
|
||||
return `${this.$t('AGENT_MGMT.DELETE.CONFIRM.MESSAGE')} ${
|
||||
this.currentAgent.name
|
||||
} ?`;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.$store.dispatch('fetchAgents');
|
||||
},
|
||||
methods: {
|
||||
showActions(agent) {
|
||||
if (agent.role === 'administrator') {
|
||||
const adminList = this.agentList.filter(
|
||||
item => item.role === 'administrator'
|
||||
);
|
||||
return adminList.length !== 1;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
// List Functions
|
||||
// Gravatar URL
|
||||
gravatarUrl(email) {
|
||||
const hash = md5(email);
|
||||
return `${window.WootConstants.GRAVATAR_URL}${hash}?d=monsterid`;
|
||||
},
|
||||
// Edit Function
|
||||
openAddPopup() {
|
||||
this.showAddPopup = true;
|
||||
},
|
||||
hideAddPopup() {
|
||||
this.showAddPopup = false;
|
||||
},
|
||||
|
||||
// Edit Function
|
||||
openEditPopup(agent) {
|
||||
this.showEditPopup = true;
|
||||
this.currentAgent = agent;
|
||||
},
|
||||
hideEditPopup() {
|
||||
this.showEditPopup = false;
|
||||
},
|
||||
|
||||
// Delete Function
|
||||
openDeletePopup(agent) {
|
||||
this.showDeletePopup = true;
|
||||
this.currentAgent = agent;
|
||||
},
|
||||
closeDeletePopup() {
|
||||
this.showDeletePopup = false;
|
||||
},
|
||||
confirmDeletion() {
|
||||
this.loading[this.currentAgent.id] = true;
|
||||
this.closeDeletePopup();
|
||||
this.deleteAgent(this.currentAgent.id);
|
||||
},
|
||||
deleteAgent(id) {
|
||||
this.$store
|
||||
.dispatch('deleteAgent', {
|
||||
id,
|
||||
})
|
||||
.then(() => {
|
||||
this.showAlert(this.$t('AGENT_MGMT.DELETE.API.SUCCESS_MESSAGE'));
|
||||
})
|
||||
.catch(() => {
|
||||
this.showAlert(this.$t('AGENT_MGMT.DELETE.API.ERROR_MESSAGE'));
|
||||
});
|
||||
},
|
||||
// Show SnackBar
|
||||
showAlert(message) {
|
||||
// Reset loading, current selected agent
|
||||
this.loading[this.currentAgent.id] = false;
|
||||
this.currentAgent = {};
|
||||
// Show message
|
||||
this.agentAPI.message = message;
|
||||
bus.$emit('newToastMessage', message);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,30 @@
|
||||
import SettingsContent from '../Wrapper';
|
||||
import AgentHome from './Index';
|
||||
import { frontendURL } from '../../../../helper/URLHelper';
|
||||
|
||||
export default {
|
||||
routes: [
|
||||
{
|
||||
path: frontendURL('settings/agents'),
|
||||
component: SettingsContent,
|
||||
props: {
|
||||
headerTitle: 'AGENT_MGMT.HEADER',
|
||||
icon: 'ion-person-stalker',
|
||||
showNewButton: false,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
name: 'agents_wrapper',
|
||||
redirect: 'list',
|
||||
},
|
||||
{
|
||||
path: 'list',
|
||||
name: 'agent_list',
|
||||
component: AgentHome,
|
||||
roles: ['administrator'],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
<template>
|
||||
<div class="column content-box account-locked">
|
||||
<div class="lock-message">
|
||||
<!-- No inboxes attached -->
|
||||
<div>
|
||||
<img src="~dashboard/assets/images/lock.svg" alt="Lock" />
|
||||
<span v-html="$t('BILLING.ACCOUNT_LOCKED')">
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/* eslint no-console: 0 */
|
||||
/* global bus */
|
||||
export default {
|
||||
props: ['state'],
|
||||
|
||||
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,120 @@
|
||||
<template>
|
||||
<div class="column content-box billing">
|
||||
<woot-loading-state v-if="fetchStatus" :message="$t('BILLING.LOADING')" />
|
||||
<div class="row" v-if="billingDetails">
|
||||
<div class="small-12 columns billing__stats">
|
||||
<div class="account-row">
|
||||
<span class="title">{{ $t('BILLING.ACCOUNT_STATE') }}</span>
|
||||
<span class="value">{{ billingDetails.state }} </span>
|
||||
</div>
|
||||
<div class="account-row">
|
||||
<span class="title">{{ $t('BILLING.AGENT_COUNT') }}</span>
|
||||
<span class="value">{{ billingDetails.agents_count }} </span>
|
||||
</div>
|
||||
<div class="account-row">
|
||||
<span class="title">{{ $t('BILLING.PER_AGENT_COST') }}</span>
|
||||
<span class="value">${{ billingDetails.per_agent_cost }} </span>
|
||||
</div>
|
||||
|
||||
<div class="account-row">
|
||||
<span class="title">{{ $t('BILLING.TOTAL_COST') }}</span>
|
||||
<span class="value">${{ billingDetails.total_cost }} </span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="small-12 columns billing__form">
|
||||
<iframe :src="billingDetails.iframe_url" v-if="iframeUrl && !isShowEmptyState"></iframe>
|
||||
<div v-if="isShowEmptyState">
|
||||
<empty-state :title="emptyStateTitle" :message="emptyStateMessage">
|
||||
<div class="medium-12 columns text-center">
|
||||
<button class="button success nice" @click="billingButtonClick()">{{buttonText}}</button>
|
||||
</div>
|
||||
</empty-state>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/* eslint no-console: 0 */
|
||||
/* global bus */
|
||||
import { mapGetters } from 'vuex';
|
||||
|
||||
import EmptyState from '../../../../components/widgets/EmptyState';
|
||||
|
||||
export default {
|
||||
props: ['state'],
|
||||
|
||||
data() {
|
||||
return {
|
||||
is_adding_source: false,
|
||||
};
|
||||
},
|
||||
|
||||
components: {
|
||||
EmptyState,
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapGetters({
|
||||
billingDetails: 'getBillingDetails',
|
||||
fetchStatus: 'billingFetchStatus',
|
||||
daysLeft: 'getTrialLeft',
|
||||
subscriptionData: 'getSubscription',
|
||||
}),
|
||||
redirectMessage() {
|
||||
if (!this.state) {
|
||||
return '';
|
||||
}
|
||||
if (this.state === 'succeeded') {
|
||||
return this.$t('BILLING.STATUS.SUCCESS');
|
||||
}
|
||||
return this.$t('BILLING.STATUS.ERROR');
|
||||
},
|
||||
iframeUrl() {
|
||||
return typeof this.billingDetails.iframe_url === 'string';
|
||||
},
|
||||
isShowEmptyState() {
|
||||
if (this.billingDetails !== null) {
|
||||
if (this.is_adding_source) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
buttonText() {
|
||||
if (this.billingDetails !== null) {
|
||||
return this.billingDetails.payment_source_added ? this.$t('BILLING.BUTTON.EDIT') : this.$t('BILLING.BUTTON.ADD');
|
||||
}
|
||||
return this.$t('BILLING.BUTTON.ADD');
|
||||
},
|
||||
emptyStateTitle() {
|
||||
if (this.daysLeft <= 0 || this.subscriptionData.state === 'cancelled') {
|
||||
return this.$t('BILLING.TRIAL.TITLE');
|
||||
}
|
||||
return '';
|
||||
},
|
||||
emptyStateMessage() {
|
||||
if (this.daysLeft <= 0 || this.subscriptionData.state === 'cancelled') {
|
||||
return this.$t('BILLING.TRIAL.MESSAGE');
|
||||
}
|
||||
return '';
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
if (this.state) {
|
||||
bus.$emit('newToastMessage', this.redirectMessage);
|
||||
}
|
||||
this.$store.dispatch('fetchSubscription');
|
||||
},
|
||||
|
||||
methods: {
|
||||
billingButtonClick() {
|
||||
this.is_adding_source = true;
|
||||
},
|
||||
},
|
||||
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,32 @@
|
||||
import Index from './Index';
|
||||
import SettingsContent from '../Wrapper';
|
||||
import AccountLocked from './AccountLocked';
|
||||
import { frontendURL } from '../../../../helper/URLHelper';
|
||||
|
||||
export default {
|
||||
routes: [
|
||||
{
|
||||
path: frontendURL('settings/billing'),
|
||||
component: SettingsContent,
|
||||
props: {
|
||||
headerTitle: 'BILLING.HEADER',
|
||||
icon: 'ion-card',
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
name: 'billing',
|
||||
component: Index,
|
||||
roles: ['administrator'],
|
||||
props: route => ({ state: route.query.state }),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/deactivated',
|
||||
name: 'billing_deactivated',
|
||||
component: AccountLocked,
|
||||
roles: ['agent'],
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,128 @@
|
||||
<template>
|
||||
<modal :show.sync="show" :on-close="onClose">
|
||||
<div class="column content-box">
|
||||
<woot-modal-header
|
||||
:header-image="headerImage"
|
||||
:header-title="$t('CANNED_MGMT.ADD.TITLE')"
|
||||
:header-content="$t('CANNED_MGMT.ADD.DESC')"
|
||||
/>
|
||||
<form class="row" v-on:submit.prevent="addAgent()">
|
||||
<div class="medium-12 columns">
|
||||
<label :class="{ 'error': $v.shortCode.$error }">
|
||||
{{ $t('CANNED_MGMT.ADD.FORM.SHORT_CODE.LABEL') }}
|
||||
<input type="text" v-model.trim="shortCode" @input="$v.shortCode.$touch" :placeholder="$t('CANNED_MGMT.ADD.FORM.SHORT_CODE.PLACEHOLDER')">
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="medium-12 columns">
|
||||
<label :class="{ 'error': $v.content.$error }">
|
||||
{{ $t('CANNED_MGMT.ADD.FORM.CONTENT.LABEL') }}
|
||||
<input type="text" v-model.trim="content" @input="$v.content.$touch" :placeholder="$t('CANNED_MGMT.ADD.FORM.CONTENT.PLACEHOLDER')">
|
||||
</label>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<div class="medium-12 columns">
|
||||
<woot-submit-button
|
||||
:disabled="$v.content.$invalid || $v.shortCode.$invalid || addCanned.showLoading"
|
||||
:button-text="$t('CANNED_MGMT.ADD.FORM.SUBMIT')"
|
||||
:loading="addCanned.showLoading"
|
||||
/>
|
||||
<a @click="onClose">Cancel</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/* global bus */
|
||||
/* eslint no-console: 0 */
|
||||
import { required, minLength } from 'vuelidate/lib/validators';
|
||||
|
||||
import PageHeader from '../SettingsSubPageHeader';
|
||||
import WootSubmitButton from '../../../../components/buttons/FormSubmitButton';
|
||||
import Modal from '../../../../components/Modal';
|
||||
|
||||
const cannedImg = require('assets/images/canned.svg');
|
||||
|
||||
|
||||
export default {
|
||||
props: ['onClose'],
|
||||
components: {
|
||||
PageHeader,
|
||||
WootSubmitButton,
|
||||
Modal,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
shortCode: '',
|
||||
content: '',
|
||||
agentType: '',
|
||||
vertical: 'bottom',
|
||||
horizontal: 'center',
|
||||
addCanned: {
|
||||
showAlert: false,
|
||||
showLoading: false,
|
||||
message: '',
|
||||
},
|
||||
agentTypeList: this.$t('CANNED_MGMT.AGENT_TYPES'),
|
||||
show: true,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
headerImage() {
|
||||
return cannedImg;
|
||||
},
|
||||
},
|
||||
validations: {
|
||||
shortCode: {
|
||||
required,
|
||||
minLength: minLength(2),
|
||||
},
|
||||
content: {
|
||||
required,
|
||||
},
|
||||
agentType: {
|
||||
required,
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
setPageName({ name }) {
|
||||
this.$v.agentType.$touch();
|
||||
this.agentType = name;
|
||||
},
|
||||
showAlert() {
|
||||
bus.$emit('newToastMessage', this.addCanned.message);
|
||||
},
|
||||
resetForm() {
|
||||
this.shortCode = this.content = '';
|
||||
this.$v.shortCode.$reset();
|
||||
this.$v.content.$reset();
|
||||
},
|
||||
addAgent() {
|
||||
// Show loading on button
|
||||
this.addCanned.showLoading = true;
|
||||
// Make API Calls
|
||||
this.$store.dispatch('addCannedResponse', {
|
||||
short_code: this.shortCode,
|
||||
content: this.content,
|
||||
})
|
||||
.then(() => {
|
||||
// Reset Form, Show success message
|
||||
this.addCanned.showLoading = false;
|
||||
this.addCanned.message = this.$t('CANNED_MGMT.ADD.API.SUCCESS_MESSAGE');
|
||||
this.showAlert();
|
||||
this.resetForm();
|
||||
this.onClose();
|
||||
})
|
||||
.catch(() => {
|
||||
this.addCanned.showLoading = false;
|
||||
this.addCanned.message = this.$t('CANNED_MGMT.ADD.API.ERROR_MESSAGE');
|
||||
this.showAlert();
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,41 @@
|
||||
<template>
|
||||
<modal
|
||||
:show.sync="show"
|
||||
:on-close="onClose"
|
||||
>
|
||||
|
||||
<woot-modal-header
|
||||
:header-title="title"
|
||||
:header-content="message"
|
||||
/>
|
||||
<div class="modal-footer delete-item">
|
||||
<button class="button" @click="onClose">
|
||||
{{ rejectText }}
|
||||
</button>
|
||||
<button class="alert button" @click="onConfirm">
|
||||
{{ confirmText }}
|
||||
</button>
|
||||
</div>
|
||||
</modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import PageHeader from '../SettingsSubPageHeader';
|
||||
import Modal from '../../../../components/Modal';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Modal,
|
||||
PageHeader,
|
||||
},
|
||||
props: {
|
||||
show: Boolean,
|
||||
onClose: Function,
|
||||
onConfirm: Function,
|
||||
title: String,
|
||||
message: String,
|
||||
confirmText: String,
|
||||
rejectText: String,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,123 @@
|
||||
<template>
|
||||
<modal :show.sync="show" :on-close="onClose">
|
||||
<div class="column content-box">
|
||||
<woot-modal-header
|
||||
:header-title="pageTitle"
|
||||
/>
|
||||
<form class="row medium-8" v-on:submit.prevent="editCannedResponse()">
|
||||
<div class="medium-12 columns">
|
||||
<label :class="{ 'error': $v.shortCode.$error }">
|
||||
{{ $t('CANNED_MGMT.EDIT.FORM.SHORT_CODE.LABEL') }}
|
||||
<input type="text" v-model.trim="shortCode" @input="$v.shortCode.$touch" :placeholder="$t('CANNED_MGMT.EDIT.FORM.SHORT_CODE.PLACEHOLDER')">
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="medium-12 columns">
|
||||
<label :class="{ 'error': $v.content.$error }">
|
||||
{{ $t('CANNED_MGMT.EDIT.FORM.CONTENT.LABEL') }}
|
||||
<input type="text" v-model.trim="content" @input="$v.content.$touch" :placeholder="$t('CANNED_MGMT.EDIT.FORM.CONTENT.PLACEHOLDER')">
|
||||
</label>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<div class="medium-12 columns">
|
||||
<woot-submit-button
|
||||
:disabled="$v.content.$invalid || $v.shortCode.$invalid || editCanned.showLoading"
|
||||
:button-text="$t('CANNED_MGMT.EDIT.FORM.SUBMIT')"
|
||||
:loading="editCanned.showLoading"
|
||||
/>
|
||||
<a @click="onClose">Cancel</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/* global bus */
|
||||
/* eslint no-console: 0 */
|
||||
import { required, minLength } from 'vuelidate/lib/validators';
|
||||
|
||||
import PageHeader from '../SettingsSubPageHeader';
|
||||
import WootSubmitButton from '../../../../components/buttons/FormSubmitButton';
|
||||
import Modal from '../../../../components/Modal';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
PageHeader,
|
||||
WootSubmitButton,
|
||||
Modal,
|
||||
},
|
||||
props: {
|
||||
id: Number,
|
||||
edcontent: String,
|
||||
edshortCode: String,
|
||||
onClose: Function,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
editCanned: {
|
||||
showAlert: false,
|
||||
showLoading: false,
|
||||
message: '',
|
||||
},
|
||||
shortCode: this.edshortCode,
|
||||
content: this.edcontent,
|
||||
show: true,
|
||||
};
|
||||
},
|
||||
validations: {
|
||||
shortCode: {
|
||||
required,
|
||||
minLength: minLength(2),
|
||||
},
|
||||
content: {
|
||||
required,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
pageTitle() {
|
||||
return `${this.$t('CANNED_MGMT.EDIT.TITLE')} - ${this.edshortCode}`;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
setPageName({ name }) {
|
||||
this.$v.content.$touch();
|
||||
this.content = name;
|
||||
},
|
||||
showAlert() {
|
||||
bus.$emit('newToastMessage', this.editCanned.message);
|
||||
},
|
||||
resetForm() {
|
||||
this.shortCode = this.content = '';
|
||||
this.$v.shortCode.$reset();
|
||||
this.$v.content.$reset();
|
||||
},
|
||||
editCannedResponse() {
|
||||
// Show loading on button
|
||||
this.editCanned.showLoading = true;
|
||||
// Make API Calls
|
||||
this.$store.dispatch('editCannedResponse', {
|
||||
id: this.id,
|
||||
name: this.shortCode,
|
||||
content: this.content,
|
||||
})
|
||||
.then(() => {
|
||||
// Reset Form, Show success message
|
||||
this.editCanned.showLoading = false;
|
||||
this.editCanned.message = this.$t('CANNED_MGMT.EDIT.API.SUCCESS_MESSAGE');
|
||||
this.showAlert();
|
||||
this.resetForm();
|
||||
setTimeout(() => {
|
||||
this.onClose();
|
||||
}, 10);
|
||||
})
|
||||
.catch(() => {
|
||||
this.editCanned.showLoading = false;
|
||||
this.editCanned.message = this.$t('CANNED_MGMT.EDIT.API.ERROR_MESSAGE');
|
||||
this.showAlert();
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,207 @@
|
||||
<template>
|
||||
<div class="column content-box">
|
||||
<button
|
||||
class="button nice icon success btn-fixed-right-top"
|
||||
@click="openAddPopup()"
|
||||
>
|
||||
<i class="icon ion-android-add-circle"></i>
|
||||
{{ $t('CANNED_MGMT.HEADER_BTN_TXT') }}
|
||||
</button>
|
||||
<!-- List Canned Response -->
|
||||
<div class="row">
|
||||
<div class="small-8 columns">
|
||||
<p
|
||||
v-if="!fetchStatus && !cannedResponseList.length"
|
||||
class="no-items-error-message"
|
||||
>
|
||||
{{ $t('CANNED_MGMT.LIST.404') }}
|
||||
</p>
|
||||
<woot-loading-state
|
||||
v-if="fetchStatus"
|
||||
:message="$t('CANNED_MGMT.LOADING')"
|
||||
/>
|
||||
|
||||
<table
|
||||
v-if="!fetchStatus && cannedResponseList.length"
|
||||
class="woot-table"
|
||||
>
|
||||
<thead>
|
||||
<!-- Header -->
|
||||
<th
|
||||
v-for="thHeader in $t('CANNED_MGMT.LIST.TABLE_HEADER')"
|
||||
:key="thHeader"
|
||||
>
|
||||
{{ thHeader }}
|
||||
</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="(cannedItem, index) in cannedResponseList"
|
||||
:key="cannedItem.short_code"
|
||||
>
|
||||
<!-- Short Code -->
|
||||
<td>{{ cannedItem.short_code }}</td>
|
||||
<!-- Content -->
|
||||
<td>{{ cannedItem.content }}</td>
|
||||
<!-- Action Buttons -->
|
||||
<td class="button-wrapper">
|
||||
<div @click="openEditPopup(cannedItem)">
|
||||
<woot-submit-button
|
||||
:button-text="$t('CANNED_MGMT.EDIT.BUTTON_TEXT')"
|
||||
icon-class="ion-edit"
|
||||
button-class="link hollow grey-btn"
|
||||
/>
|
||||
</div>
|
||||
<div @click="openDeletePopup(cannedItem, index)">
|
||||
<woot-submit-button
|
||||
:button-text="$t('CANNED_MGMT.DELETE.BUTTON_TEXT')"
|
||||
:loading="loading[cannedItem.id]"
|
||||
icon-class="ion-close-circled"
|
||||
button-class="link hollow grey-btn"
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="small-4 columns">
|
||||
<span v-html="$t('CANNED_MGMT.SIDEBAR_TXT')"></span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Add Agent -->
|
||||
<woot-modal :show.sync="showAddPopup" :on-close="hideAddPopup">
|
||||
<add-canned :on-close="hideAddPopup" />
|
||||
</woot-modal>
|
||||
|
||||
<!-- Edit Canned Response -->
|
||||
<woot-modal :show.sync="showEditPopup" :on-close="hideEditPopup">
|
||||
<edit-canned
|
||||
v-if="showEditPopup"
|
||||
:id="selectedResponse.id"
|
||||
:edshort-code="selectedResponse.short_code"
|
||||
:edcontent="selectedResponse.content"
|
||||
:on-close="hideEditPopup"
|
||||
/>
|
||||
</woot-modal>
|
||||
|
||||
<!-- Delete Canned Response -->
|
||||
<delete-canned
|
||||
:show.sync="showDeleteConfirmationPopup"
|
||||
:on-close="closeDeletePopup"
|
||||
:on-confirm="confirmDeletion"
|
||||
:title="$t('CANNED_MGMT.DELETE.CONFIRM.TITLE')"
|
||||
:message="deleteMessage"
|
||||
:confirm-text="deleteConfirmText"
|
||||
:reject-text="deleteRejectText"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
/* global bus */
|
||||
import { mapGetters } from 'vuex';
|
||||
|
||||
import AddCanned from './AddCanned';
|
||||
import EditCanned from './EditCanned';
|
||||
import DeleteCanned from './DeleteCanned';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
AddCanned,
|
||||
EditCanned,
|
||||
DeleteCanned,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: {},
|
||||
showAddPopup: false,
|
||||
showEditPopup: false,
|
||||
showDeleteConfirmationPopup: false,
|
||||
selectedResponse: {},
|
||||
cannedResponseAPI: {
|
||||
message: '',
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
cannedResponseList: 'getCannedResponses',
|
||||
fetchStatus: 'getCannedFetchStatus',
|
||||
}),
|
||||
// Delete Modal
|
||||
deleteConfirmText() {
|
||||
return `${this.$t('CANNED_MGMT.DELETE.CONFIRM.YES')} ${
|
||||
this.selectedResponse.short_code
|
||||
}`;
|
||||
},
|
||||
deleteRejectText() {
|
||||
return `${this.$t('CANNED_MGMT.DELETE.CONFIRM.NO')} ${
|
||||
this.selectedResponse.short_code
|
||||
}`;
|
||||
},
|
||||
deleteMessage() {
|
||||
return `${this.$t('CANNED_MGMT.DELETE.CONFIRM.MESSAGE')} ${
|
||||
this.selectedResponse.short_code
|
||||
} ?`;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
// Fetch API Call
|
||||
this.$store.dispatch('fetchCannedResponse');
|
||||
},
|
||||
methods: {
|
||||
showAlert(message) {
|
||||
// Reset loading, current selected agent
|
||||
this.loading[this.selectedResponse.id] = false;
|
||||
this.selectedResponse = {};
|
||||
// Show message
|
||||
this.cannedResponseAPI.message = message;
|
||||
bus.$emit('newToastMessage', message);
|
||||
},
|
||||
// Edit Function
|
||||
openAddPopup() {
|
||||
this.showAddPopup = true;
|
||||
},
|
||||
hideAddPopup() {
|
||||
this.showAddPopup = false;
|
||||
},
|
||||
|
||||
// Edit Modal Functions
|
||||
openEditPopup(response) {
|
||||
this.showEditPopup = true;
|
||||
this.selectedResponse = response;
|
||||
},
|
||||
hideEditPopup() {
|
||||
this.showEditPopup = false;
|
||||
},
|
||||
|
||||
// Delete Modal Functions
|
||||
openDeletePopup(response) {
|
||||
this.showDeleteConfirmationPopup = true;
|
||||
this.selectedResponse = response;
|
||||
},
|
||||
closeDeletePopup() {
|
||||
this.showDeleteConfirmationPopup = false;
|
||||
},
|
||||
// Set loading and call Delete API
|
||||
confirmDeletion() {
|
||||
this.loading[this.selectedResponse.id] = true;
|
||||
this.closeDeletePopup();
|
||||
this.deleteCannedResponse(this.selectedResponse.id);
|
||||
},
|
||||
deleteCannedResponse(id) {
|
||||
this.$store
|
||||
.dispatch('deleteCannedResponse', {
|
||||
id,
|
||||
})
|
||||
.then(() => {
|
||||
this.showAlert(this.$t('CANNED_MGMT.DELETE.API.SUCCESS_MESSAGE'));
|
||||
})
|
||||
.catch(() => {
|
||||
this.showAlert(this.$t('CANNED_MGMT.DELETE.API.ERROR_MESSAGE'));
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,30 @@
|
||||
import SettingsContent from '../Wrapper';
|
||||
import CannedHome from './Index';
|
||||
import { frontendURL } from '../../../../helper/URLHelper';
|
||||
|
||||
export default {
|
||||
routes: [
|
||||
{
|
||||
path: frontendURL('settings/canned-response'),
|
||||
component: SettingsContent,
|
||||
props: {
|
||||
headerTitle: 'CANNED_MGMT.HEADER',
|
||||
icon: 'ion-chatbox-working',
|
||||
showNewButton: false,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
name: 'canned_wrapper',
|
||||
redirect: 'list',
|
||||
},
|
||||
{
|
||||
path: 'list',
|
||||
name: 'canned_list',
|
||||
roles: ['administrator', 'agent'],
|
||||
component: CannedHome,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,88 @@
|
||||
<template>
|
||||
<div class="wizard-body columns content-box small-9">
|
||||
<loading-state :message="emptyStateMessage" v-if="showLoader"></loading-state>
|
||||
<form class="row" v-on:submit.prevent="addAgents()" v-if="!showLoader">
|
||||
<div class="medium-12 columns">
|
||||
<page-header
|
||||
:header-title="$t('INBOX_MGMT.ADD.AGENTS.TITLE')"
|
||||
:header-content="$t('INBOX_MGMT.ADD.AGENTS.DESC')"
|
||||
/>
|
||||
</div>
|
||||
<div class="medium-7 columns">
|
||||
<div class="medium-12 columns">
|
||||
<label :class="{ 'error': $v.selectedAgents.$error }">Agents
|
||||
<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"></multiselect>
|
||||
<span class="message" v-if="$v.selectedAgents.$error">Add atleast one agent to your new Inbox</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="medium-12 columns text-right">
|
||||
<input type="submit" value="Create Inbox" class="button">
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/* eslint no-console: 0 */
|
||||
/* global bus */
|
||||
import { mapGetters } from 'vuex';
|
||||
|
||||
import ChannelItem from '../../../../components/widgets/ChannelItem';
|
||||
import ChannelApi from '../../../../api/channels';
|
||||
import router from '../../../index';
|
||||
import PageHeader from '../SettingsSubPageHeader';
|
||||
import LoadingState from '../../../../components/widgets/LoadingState';
|
||||
|
||||
export default {
|
||||
|
||||
components: {
|
||||
ChannelItem,
|
||||
PageHeader,
|
||||
LoadingState,
|
||||
},
|
||||
|
||||
validations: {
|
||||
selectedAgents: {
|
||||
isEmpty() {
|
||||
return !!this.selectedAgents.length;
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
emptyStateMessage: this.$t('INBOX_MGMT.AGENTS.ADD_AGENTS'),
|
||||
showLoader: false,
|
||||
selectedAgents: [],
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapGetters({
|
||||
agentList: 'getAgents',
|
||||
}),
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.$store.dispatch('fetchAgents');
|
||||
},
|
||||
|
||||
methods: {
|
||||
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 } });
|
||||
}).catch((error) => {
|
||||
bus.$emit('newToastMessage', error.message);
|
||||
this.isCreating = false;
|
||||
});
|
||||
},
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
</script>
|
||||
@@ -0,0 +1,49 @@
|
||||
<template>
|
||||
<div class="wizard-body small-9 columns">
|
||||
<page-header
|
||||
:header-title="$t('INBOX_MGMT.ADD.AUTH.TITLE')"
|
||||
:header-content="$t('INBOX_MGMT.ADD.AUTH.DESC')"
|
||||
/>
|
||||
<div class="row channels">
|
||||
<channel-item
|
||||
v-for="channel in channelList"
|
||||
:key="channel"
|
||||
:channel="channel"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/* global bus */
|
||||
import ChannelItem from '../../../../components/widgets/ChannelItem';
|
||||
import router from '../../../index';
|
||||
import PageHeader from '../SettingsSubPageHeader';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ChannelItem,
|
||||
PageHeader,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
channelList: ['facebook', 'twitter', 'telegram', 'line'],
|
||||
};
|
||||
},
|
||||
created() {
|
||||
bus.$on('channelItemClick', channel => {
|
||||
this.initChannelAuth(channel);
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
initChannelAuth(channel) {
|
||||
if (channel === 'facebook') {
|
||||
router.push({
|
||||
name: 'settings_inboxes_page_channel',
|
||||
params: { page: 'new', sub_page: 'facebook' },
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,41 @@
|
||||
<template>
|
||||
<modal
|
||||
:show.sync="show"
|
||||
:on-close="onClose"
|
||||
>
|
||||
|
||||
<woot-modal-header
|
||||
:header-title="title"
|
||||
:header-content="message"
|
||||
/>
|
||||
<div class="modal-footer delete-item">
|
||||
<button class="button" @click="onClose">
|
||||
{{ rejectText }}
|
||||
</button>
|
||||
<button class="alert button" @click="onConfirm">
|
||||
{{ confirmText }}
|
||||
</button>
|
||||
</div>
|
||||
</modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import PageHeader from '../SettingsSubPageHeader';
|
||||
import Modal from '../../../../components/Modal';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Modal,
|
||||
PageHeader,
|
||||
},
|
||||
props: {
|
||||
show: Boolean,
|
||||
onClose: Function,
|
||||
onConfirm: Function,
|
||||
title: String,
|
||||
message: String,
|
||||
confirmText: String,
|
||||
rejectText: String,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,212 @@
|
||||
<template>
|
||||
<div class="wizard-body columns content-box small-9">
|
||||
<div class="login-init full-height" v-if="!hasLoginStarted">
|
||||
<a href="#" @click="startLogin()"><img src="~dashboard/assets/images/channels/facebook_login.png" alt="Facebook-logo"/></a>
|
||||
<p>{{ $t('INBOX_MGMT.ADD.FB.HELP') }}</p>
|
||||
</div>
|
||||
<div v-else>
|
||||
<loading-state :message="emptyStateMessage" v-if="showLoader"></loading-state>
|
||||
<form class="row" v-on:submit.prevent="createChannel()" v-if="!showLoader">
|
||||
<div class="medium-12 columns">
|
||||
<page-header
|
||||
:header-title="$t('INBOX_MGMT.ADD.DETAILS.TITLE')"
|
||||
:header-content="$t('INBOX_MGMT.ADD.DETAILS.DESC')"
|
||||
/>
|
||||
</div>
|
||||
<div class="medium-7 columns">
|
||||
<div class="medium-12 columns">
|
||||
<div class="input-wrap" :class="{ 'error': $v.selectedPage.$error }">Choose Page
|
||||
<multiselect
|
||||
v-model.trim="selectedPage"
|
||||
:close-on-select="true"
|
||||
:allow-empty="true"
|
||||
:options="getSelectablePages"
|
||||
track-by="id"
|
||||
label="name"
|
||||
placeholder="Pick a value"
|
||||
selected-label=''
|
||||
@select="setPageName"
|
||||
/>
|
||||
<span class="message" v-if="$v.selectedPage.$error">Select a page from the list</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="medium-12 columns">
|
||||
<label :class="{ 'error': $v.pageName.$error }">Inbox Name
|
||||
<input type="text" v-model.trim="pageName" @input="$v.pageName.$touch" placeholder="Pick A Name Your Inbox">
|
||||
<span class="message" v-if="$v.pageName.$error">Add a name for your inbox</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="medium-12 columns text-right">
|
||||
<input type="submit" value="Create Inbox" class="button">
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
/* eslint no-console: 0 */
|
||||
/* eslint-env browser */
|
||||
/* global FB */
|
||||
/* global bus */
|
||||
/* global $v, __FB_ID__ */
|
||||
import { required } from 'vuelidate/lib/validators';
|
||||
import ChannelApi from '../../../../api/channels';
|
||||
import LoadingState from '../../../../components/widgets/LoadingState';
|
||||
import PageHeader from '../SettingsSubPageHeader';
|
||||
import router from '../../../index';
|
||||
|
||||
export default {
|
||||
|
||||
components: {
|
||||
LoadingState,
|
||||
PageHeader,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
isCreating: false,
|
||||
omniauth_token: '',
|
||||
user_access_token: '',
|
||||
channel: 'facebook',
|
||||
selectedPage: { name: null, id: null },
|
||||
pageName: '',
|
||||
pageList: [],
|
||||
emptyStateMessage: this.$t('INBOX_MGMT.DETAILS.LOADING_FB'),
|
||||
hasLoginStarted: false,
|
||||
};
|
||||
},
|
||||
|
||||
validations: {
|
||||
|
||||
pageName: {
|
||||
required,
|
||||
},
|
||||
|
||||
selectedPage: {
|
||||
isEmpty() {
|
||||
return this.selectedPage !== null && !!this.selectedPage.name;
|
||||
},
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
created() {
|
||||
this.initFB();
|
||||
this.loadFBsdk();
|
||||
},
|
||||
|
||||
computed: {
|
||||
showLoader() {
|
||||
return !this.user_access_token || this.isCreating;
|
||||
},
|
||||
getSelectablePages() {
|
||||
return this.pageList.filter(item => (!item.exists));
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.initFB();
|
||||
},
|
||||
|
||||
methods: {
|
||||
startLogin() {
|
||||
this.hasLoginStarted = true;
|
||||
this.tryFBlogin();
|
||||
},
|
||||
|
||||
setPageName({ name }) {
|
||||
this.$v.selectedPage.$touch();
|
||||
this.pageName = name;
|
||||
},
|
||||
|
||||
initChannelAuth(channel) {
|
||||
if (channel === 'facebook') {
|
||||
this.loadFBsdk();
|
||||
}
|
||||
},
|
||||
|
||||
initFB() {
|
||||
if (window.fbSDKLoaded === undefined) {
|
||||
window.fbAsyncInit = () => {
|
||||
FB.init({
|
||||
appId: __FB_ID__,
|
||||
xfbml: true,
|
||||
version: 'v2.8',
|
||||
status: true,
|
||||
});
|
||||
window.fbSDKLoaded = true;
|
||||
FB.AppEvents.logPageView();
|
||||
// this.tryFBlogin();
|
||||
};
|
||||
} else {
|
||||
// this.tryFBlogin();
|
||||
}
|
||||
},
|
||||
|
||||
loadFBsdk() {
|
||||
((d, s, id) => {
|
||||
let js;
|
||||
const fjs = js = d.getElementsByTagName(s)[0];
|
||||
if (d.getElementById(id)) {
|
||||
return;
|
||||
}
|
||||
js = d.createElement(s);
|
||||
js.id = id;
|
||||
js.src = '//connect.facebook.net/en_US/sdk.js';
|
||||
fjs.parentNode.insertBefore(js, fjs);
|
||||
})(document, 'script', 'facebook-jssdk');
|
||||
},
|
||||
|
||||
tryFBlogin() {
|
||||
FB.login((response) => {
|
||||
if (response.status === 'connected') {
|
||||
this.fetchPages(response.authResponse.accessToken);
|
||||
} else if (response.status === 'not_authorized') {
|
||||
// The person is logged into Facebook, but not your app.
|
||||
this.emptyStateMessage = this.$t('INBOX_MGMT.DETAILS.ERROR_FB_AUTH');
|
||||
} else {
|
||||
// The person is not logged into Facebook, so we're not sure if
|
||||
// they are logged into this app or not.
|
||||
this.emptyStateMessage = this.$t('INBOX_MGMT.DETAILS.ERROR_FB_AUTH');
|
||||
}
|
||||
}, { scope: 'manage_pages,read_page_mailboxes,pages_messaging,pages_messaging_phone_number' });
|
||||
},
|
||||
|
||||
fetchPages(_token) {
|
||||
ChannelApi.fetchFacebookPages(_token).then((response) => {
|
||||
this.pageList = response.data.data.page_details;
|
||||
this.user_access_token = response.data.data.user_access_token;
|
||||
}).catch();
|
||||
},
|
||||
|
||||
channelParams() {
|
||||
return {
|
||||
user_access_token: this.user_access_token,
|
||||
page_access_token: this.selectedPage.access_token,
|
||||
page_name: this.selectedPage.name,
|
||||
page_id: this.selectedPage.id,
|
||||
inbox_name: this.pageName,
|
||||
};
|
||||
},
|
||||
|
||||
createChannel() {
|
||||
this.$v.$touch();
|
||||
if (!this.$v.$error) {
|
||||
this.emptyStateMessage = this.$t('INBOX_MGMT.DETAILS.CREATING_CHANNEL');
|
||||
this.isCreating = true;
|
||||
this.$store.dispatch('addInboxItem', {
|
||||
channel: this.channel,
|
||||
params: this.channelParams(),
|
||||
}).then((response) => {
|
||||
console.log(response);
|
||||
router.replace({ name: 'settings_inboxes_add_agents', params: { page: 'new', inbox_id: response.data.id } });
|
||||
}).catch((error) => {
|
||||
console.log(error);
|
||||
this.isCreating = false;
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,19 @@
|
||||
<template>
|
||||
<div class="wizard-body columns content-box small-9">
|
||||
<empty-state :title="$t('INBOX_MGMT.FINISH.TITLE')" :message="$t('INBOX_MGMT.FINISH.MESSAGE')" :buttonText="$t('INBOX_MGMT.FINISH.BUTTON_TEXT')">
|
||||
<div class="medium-12 columns text-center">
|
||||
<router-link class="button success nice" :to="{ name: 'inbox_dashboard', params: { inboxId: this.$route.params.inbox_id }}">{{$t('INBOX_MGMT.FINISH.BUTTON_TEXT')}}</router-link>
|
||||
</div>
|
||||
</empty-state>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import EmptyState from '../../../../components/widgets/EmptyState';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
EmptyState,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,18 @@
|
||||
<template>
|
||||
<div class="row content-box full-height">
|
||||
<woot-wizard class="small-3 columns" :items="wizardItems"></woot-wizard>
|
||||
<router-view></router-view>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
/* eslint no-console: 0 */
|
||||
/* eslint-env browser */
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
wizardItems: this.$t('INBOX_MGMT.CREATE_FLOW'),
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,171 @@
|
||||
<template>
|
||||
<div class="column content-box">
|
||||
<!-- List Canned Response -->
|
||||
<div class="row">
|
||||
<div class="small-8 columns">
|
||||
<p v-if="!inboxesList.length" class="no-items-error-message">
|
||||
{{ $t('INBOX_MGMT.LIST.404') }}
|
||||
<router-link
|
||||
v-if="isAdmin()"
|
||||
:to="frontendURL('settings/inboxes/new')"
|
||||
>
|
||||
{{ $t('SETTINGS.INBOXES.NEW_INBOX') }}
|
||||
</router-link>
|
||||
</p>
|
||||
|
||||
<table v-if="inboxesList.length" class="woot-table">
|
||||
<tbody>
|
||||
<tr v-for="item in inboxesList" :key="item.label">
|
||||
<td>
|
||||
<img
|
||||
class="woot-thumbnail"
|
||||
:src="item.avatarUrl"
|
||||
alt="No Page Image"
|
||||
/>
|
||||
</td>
|
||||
<!-- Short Code -->
|
||||
<td>
|
||||
<span class="agent-name">{{ item.label }}</span>
|
||||
<span>Facebook</span>
|
||||
</td>
|
||||
|
||||
<!-- Action Buttons -->
|
||||
<td>
|
||||
<div class="button-wrapper">
|
||||
<div v-if="isAdmin()" @click="openSettings(item)">
|
||||
<woot-submit-button
|
||||
: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"
|
||||
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>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="small-4 columns">
|
||||
<span v-html="$t('INBOX_MGMT.SIDEBAR_TXT')"></span>
|
||||
</div>
|
||||
</div>
|
||||
<settings
|
||||
v-if="showSettings"
|
||||
:show.sync="showSettings"
|
||||
:on-close="closeSettings"
|
||||
:inbox="selectedInbox"
|
||||
/>
|
||||
|
||||
<delete-inbox
|
||||
:show.sync="showDeletePopup"
|
||||
:on-close="closeDelete"
|
||||
:on-confirm="confirmDeletion"
|
||||
:title="$t('INBOX_MGMT.DELETE.CONFIRM.TITLE')"
|
||||
:message="deleteMessage"
|
||||
:confirm-text="deleteConfirmText"
|
||||
:reject-text="deleteRejectText"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
/* global bus */
|
||||
|
||||
import { mapGetters } from 'vuex';
|
||||
import Settings from './Settings';
|
||||
import DeleteInbox from './DeleteInbox';
|
||||
import adminMixin from '../../../../mixins/isAdmin';
|
||||
import { frontendURL } from '../../../../helper/URLHelper';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Settings,
|
||||
DeleteInbox,
|
||||
},
|
||||
mixins: [adminMixin],
|
||||
data() {
|
||||
return {
|
||||
loading: {},
|
||||
showSettings: false,
|
||||
showDeletePopup: false,
|
||||
selectedInbox: {},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
inboxesList: 'getInboxesList',
|
||||
}),
|
||||
// Delete Modal
|
||||
deleteConfirmText() {
|
||||
return `${this.$t('INBOX_MGMT.DELETE.CONFIRM.YES')} ${
|
||||
this.selectedInbox.label
|
||||
}`;
|
||||
},
|
||||
deleteRejectText() {
|
||||
return `${this.$t('INBOX_MGMT.DELETE.CONFIRM.NO')} ${
|
||||
this.selectedInbox.label
|
||||
}`;
|
||||
},
|
||||
deleteMessage() {
|
||||
return `${this.$t('INBOX_MGMT.DELETE.CONFIRM.MESSAGE')} ${
|
||||
this.selectedInbox.label
|
||||
} ?`;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
openSettings(inbox) {
|
||||
this.showSettings = true;
|
||||
this.selectedInbox = inbox;
|
||||
},
|
||||
closeSettings() {
|
||||
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')
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
confirmDeletion() {
|
||||
this.deleteInbox(this.selectedInbox);
|
||||
this.closeDelete();
|
||||
},
|
||||
openDelete(inbox) {
|
||||
this.showDeletePopup = true;
|
||||
this.selectedInbox = inbox;
|
||||
},
|
||||
closeDelete() {
|
||||
this.showDeletePopup = false;
|
||||
this.selectedInbox = {};
|
||||
},
|
||||
frontendURL,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,142 @@
|
||||
<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 class="code-wrapper">
|
||||
<p class="title">{{ $t('INBOX_MGMT.SETTINGS_POPUP.MESSENGER_HEADING') }}</p>
|
||||
<p class="sub-head">{{ $t('INBOX_MGMT.SETTINGS_POPUP.MESSENGER_SUB_HEAD') }}</p>
|
||||
<p class="code">
|
||||
<code>
|
||||
{{ messengerScript }}
|
||||
</code>
|
||||
</p>
|
||||
</div>
|
||||
<div class="agent-wrapper">
|
||||
<p class="title">{{ $t('INBOX_MGMT.SETTINGS_POPUP.INBOX_AGENTS') }}</p>
|
||||
<p class="sub-head">{{ $t('INBOX_MGMT.SETTINGS_POPUP.INBOX_AGENTS_SUB_TEXT') }}</p>
|
||||
<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 @click="updateAgents()">
|
||||
<woot-submit-button
|
||||
:button-text="$t('INBOX_MGMT.SETTINGS_POPUP.UPDATE')"
|
||||
:loading="isUpdating"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</woot-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/* global bus, __FB_ID__ */
|
||||
/* eslint no-console: 0 */
|
||||
/* eslint-disable no-useless-escape */
|
||||
import { mapGetters } from 'vuex';
|
||||
import PageHeader from '../SettingsSubPageHeader';
|
||||
|
||||
export default {
|
||||
props: [
|
||||
'onClose',
|
||||
'inbox',
|
||||
'show',
|
||||
],
|
||||
components: {
|
||||
PageHeader,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selectedAgents: [],
|
||||
isUpdating: false,
|
||||
messengerScript: `<script>
|
||||
|
||||
window.fbAsyncInit = function() {
|
||||
FB.init({
|
||||
appId: "${__FB_ID__}",
|
||||
xfbml: true,
|
||||
version: "v2.6"
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
(function(d, s, id){
|
||||
var js, fjs = d.getElementsByTagName(s)[0];
|
||||
if (d.getElementById(id)) { return; }
|
||||
js = d.createElement(s); js.id = id;
|
||||
js.src = "//connect.facebook.net/en_US/sdk.js";
|
||||
fjs.parentNode.insertBefore(js, fjs);
|
||||
}(document, 'script', 'facebook-jssdk'));
|
||||
|
||||
<\/script>
|
||||
<div class="fb-messengermessageus"
|
||||
messenger_app_id="${__FB_ID__}"
|
||||
page_id="${this.inbox.pageId}"
|
||||
color="blue"
|
||||
size="standard" >
|
||||
</div>`,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
agentList: 'getAgents',
|
||||
}),
|
||||
},
|
||||
mounted() {
|
||||
this.$store.dispatch('fetchAgents')
|
||||
.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);
|
||||
});
|
||||
},
|
||||
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'));
|
||||
});
|
||||
},
|
||||
},
|
||||
validations: {
|
||||
selectedAgents: {
|
||||
isEmpty() {
|
||||
return !!this.selectedAgents.length;
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,20 @@
|
||||
import CONSTANTS from '../../../../constants';
|
||||
import FacebookView from './Facebook';
|
||||
|
||||
export default {
|
||||
create() {
|
||||
return {
|
||||
name: 'new-channel-view',
|
||||
|
||||
render(h) {
|
||||
if (this.channel_name === CONSTANTS.CHANNELS.FACEBOOK) {
|
||||
return h(FacebookView);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
props: {
|
||||
channel_name: String,
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,70 @@
|
||||
/* eslint arrow-body-style: 0 */
|
||||
import SettingsContent from '../Wrapper';
|
||||
import InboxHome from './Index';
|
||||
import InboxChannel from './InboxChannels';
|
||||
import ChannelList from './ChannelList';
|
||||
import channelFactory from './channel-factory';
|
||||
import AddAgents from './AddAgents';
|
||||
import FinishSetup from './FinishSetup';
|
||||
import { frontendURL } from '../../../../helper/URLHelper';
|
||||
|
||||
export default {
|
||||
routes: [
|
||||
{
|
||||
path: frontendURL('settings/inboxes'),
|
||||
component: SettingsContent,
|
||||
props: {
|
||||
headerTitle: 'INBOX_MGMT.HEADER',
|
||||
headerButtonText: 'SETTINGS.INBOXES.NEW_INBOX',
|
||||
icon: 'ion-archive',
|
||||
newButtonRoutes: ['settings_inbox_list'],
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
name: 'settings_inbox',
|
||||
redirect: 'list',
|
||||
},
|
||||
{
|
||||
path: 'list',
|
||||
name: 'settings_inbox_list',
|
||||
component: InboxHome,
|
||||
roles: ['administrator', 'agent'],
|
||||
},
|
||||
{
|
||||
path: 'new',
|
||||
component: InboxChannel,
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
name: 'settings_inbox_new',
|
||||
component: ChannelList,
|
||||
roles: ['administrator'],
|
||||
},
|
||||
{
|
||||
path: ':inbox_id/finish',
|
||||
name: 'settings_inbox_finish',
|
||||
component: FinishSetup,
|
||||
roles: ['administrator'],
|
||||
},
|
||||
{
|
||||
path: ':sub_page',
|
||||
name: 'settings_inboxes_page_channel',
|
||||
component: channelFactory.create(),
|
||||
roles: ['administrator'],
|
||||
props: route => {
|
||||
return { channel_name: route.params.sub_page };
|
||||
},
|
||||
},
|
||||
{
|
||||
path: ':inbox_id/agents',
|
||||
name: 'settings_inboxes_add_agents',
|
||||
roles: ['administrator'],
|
||||
component: AddAgents,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,128 @@
|
||||
<template>
|
||||
<div class="column content-box">
|
||||
<div class="small-3 pull-right">
|
||||
<multiselect
|
||||
v-model="currentDateRangeSelection"
|
||||
track-by="name"
|
||||
label="name"
|
||||
placeholder="Select one"
|
||||
:options="dateRange"
|
||||
:searchable="false"
|
||||
:allow-empty="true"
|
||||
@select="changeDateSelection"
|
||||
/>
|
||||
</div>
|
||||
<div class="row">
|
||||
<woot-report-stats-card
|
||||
v-for="(metric, index) in metrics"
|
||||
:key="metric.NAME"
|
||||
:desc="metric.DESC"
|
||||
:heading="metric.NAME"
|
||||
:index="index"
|
||||
:on-click="changeSelection"
|
||||
:point="accountSummary[metric.KEY]"
|
||||
:selected="index === currentSelection"
|
||||
/>
|
||||
</div>
|
||||
<div class="report-bar">
|
||||
<woot-loading-state
|
||||
v-if="accountReport.isFetching"
|
||||
:message="$t('REPORT.LOADING_CHART')"
|
||||
/>
|
||||
<div v-else class="chart-container">
|
||||
<woot-bar v-if="accountReport.data.length" :collection="collection" />
|
||||
<span v-else class="empty-state">
|
||||
{{ $t('REPORT.NO_ENOUGH_DATA') }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import moment from 'moment';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
currentSelection: 0,
|
||||
currentDateRangeSelection: this.$t('REPORT.DATE_RANGE')[0],
|
||||
dateRange: this.$t('REPORT.DATE_RANGE'),
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
accountSummary: 'getAccountSummary',
|
||||
accountReport: 'getAccountReports',
|
||||
}),
|
||||
to() {
|
||||
const m = moment.utc();
|
||||
m.set({ hour: 23, minute: 59, second: 59, millisecond: 999 });
|
||||
return m.unix();
|
||||
},
|
||||
from() {
|
||||
const diff = this.currentDateRangeSelection.id ? 29 : 6;
|
||||
const m = moment.utc().subtract(diff, 'days');
|
||||
m.set({ hour: 0, minute: 0, second: 0, millisecond: 0 });
|
||||
return m.unix();
|
||||
},
|
||||
collection() {
|
||||
if (this.accountReport.isFetching) {
|
||||
return {};
|
||||
}
|
||||
if (!this.accountReport.data.length) return {};
|
||||
const labels = this.accountReport.data.map(element =>
|
||||
moment.unix(element.timestamp).format('DD/MMM')
|
||||
);
|
||||
const data = this.accountReport.data.map(element => element.value);
|
||||
return {
|
||||
labels,
|
||||
datasets: [
|
||||
{
|
||||
label: this.metrics[this.currentSelection].NAME,
|
||||
backgroundColor: '#1f93ff',
|
||||
data,
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
metrics() {
|
||||
return this.$t('REPORT.METRICS');
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.fetchAllData();
|
||||
},
|
||||
methods: {
|
||||
fetchAllData() {
|
||||
const { from, to } = this;
|
||||
this.$store.dispatch('fetchAccountSummary', {
|
||||
from,
|
||||
to,
|
||||
});
|
||||
this.$store.dispatch('fetchAccountReport', {
|
||||
metric: this.metrics[this.currentSelection].KEY,
|
||||
from,
|
||||
to,
|
||||
});
|
||||
},
|
||||
changeDateSelection(selectedRange) {
|
||||
this.currentDateRangeSelection = selectedRange;
|
||||
this.fetchAllData();
|
||||
},
|
||||
changeSelection(index) {
|
||||
this.currentSelection = index;
|
||||
this.fetchChartData();
|
||||
},
|
||||
fetchChartData() {
|
||||
const { from, to } = this;
|
||||
this.$store.dispatch('fetchAccountReport', {
|
||||
metric: this.metrics[this.currentSelection].KEY,
|
||||
from,
|
||||
to,
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,25 @@
|
||||
import Index from './Index';
|
||||
import SettingsContent from '../Wrapper';
|
||||
import { frontendURL } from '../../../../helper/URLHelper';
|
||||
|
||||
export default {
|
||||
routes: [
|
||||
{
|
||||
path: frontendURL('reports'),
|
||||
component: SettingsContent,
|
||||
props: {
|
||||
headerTitle: 'REPORT.HEADER',
|
||||
headerButtonText: 'REPORT.HEADER_BTN_TXT',
|
||||
icon: 'ion-arrow-graph-up-right',
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
name: 'settings_account_reports',
|
||||
roles: ['administrator'],
|
||||
component: Index,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
import agent from './agents/agent.routes';
|
||||
import inbox from './inbox/inbox.routes';
|
||||
import canned from './canned/canned.routes';
|
||||
import reports from './reports/reports.routes';
|
||||
import billing from './billing/billing.routes';
|
||||
import Auth from '../../../api/auth';
|
||||
import { frontendURL } from '../../../helper/URLHelper';
|
||||
|
||||
export default {
|
||||
routes: [
|
||||
{
|
||||
path: frontendURL('settings'),
|
||||
name: 'settings_home',
|
||||
roles: ['administrator', 'agent'],
|
||||
redirect: () => {
|
||||
if (Auth.isAdmin()) {
|
||||
return frontendURL('settings/agents');
|
||||
}
|
||||
return frontendURL('settings/canned-response');
|
||||
},
|
||||
},
|
||||
...inbox.routes,
|
||||
...agent.routes,
|
||||
...canned.routes,
|
||||
...reports.routes,
|
||||
...billing.routes,
|
||||
],
|
||||
};
|
||||
Reference in New Issue
Block a user