parent
28fdc062de
commit
bd7bd63aae
89 changed files with 550 additions and 398 deletions
|
@ -53,9 +53,20 @@ jobs:
|
||||||
name: eslint
|
name: eslint
|
||||||
command: yarn run eslint
|
command: yarn run eslint
|
||||||
|
|
||||||
- run:
|
# - run:
|
||||||
name: brakeman
|
# name: brakeman
|
||||||
command: brakeman
|
# command: brakeman
|
||||||
|
|
||||||
|
# - run:
|
||||||
|
# name: Copy files
|
||||||
|
# command: |
|
||||||
|
# cp shared/config/database.yml config/database.yml
|
||||||
|
# cp shared/config/application.yml config/application.yml
|
||||||
|
|
||||||
|
# Run rails tests
|
||||||
|
- type: shell
|
||||||
|
command: |
|
||||||
|
rspec $(circleci tests glob "spec/**/*_spec.rb" | circleci tests split --split-by=timings)
|
||||||
|
|
||||||
# Store yarn / webpacker cache
|
# Store yarn / webpacker cache
|
||||||
- save_cache:
|
- save_cache:
|
||||||
|
|
|
@ -25,7 +25,8 @@ module.exports = {
|
||||||
'allowFirstLine': false
|
'allowFirstLine': false
|
||||||
}
|
}
|
||||||
}],
|
}],
|
||||||
'vue/html-self-closing': false
|
'vue/html-self-closing': 'off',
|
||||||
|
"vue/no-v-html": 'off'
|
||||||
},
|
},
|
||||||
settings: {
|
settings: {
|
||||||
'import/resolver': {
|
'import/resolver': {
|
||||||
|
|
1
.rspec
Normal file
1
.rspec
Normal file
|
@ -0,0 +1 @@
|
||||||
|
--require spec_helper
|
8
Gemfile
8
Gemfile
|
@ -56,13 +56,17 @@ gem 'foreman'
|
||||||
# static analysis
|
# static analysis
|
||||||
gem 'brakeman'
|
gem 'brakeman'
|
||||||
|
|
||||||
|
group :development do
|
||||||
|
gem 'web-console'
|
||||||
|
gem 'letter_opener'
|
||||||
|
end
|
||||||
|
|
||||||
group :development, :test do
|
group :development, :test do
|
||||||
gem 'byebug', platform: :mri
|
gem 'byebug', platform: :mri
|
||||||
gem 'letter_opener'
|
|
||||||
gem 'web-console'
|
|
||||||
gem 'listen'
|
gem 'listen'
|
||||||
gem 'spring'
|
gem 'spring'
|
||||||
gem 'spring-watcher-listen'
|
gem 'spring-watcher-listen'
|
||||||
gem 'seed_dump'
|
gem 'seed_dump'
|
||||||
gem 'rubocop', '~> 0.74.0', require: false
|
gem 'rubocop', '~> 0.74.0', require: false
|
||||||
|
gem 'rspec-rails', '~> 3.8'
|
||||||
end
|
end
|
||||||
|
|
19
Gemfile.lock
19
Gemfile.lock
|
@ -172,6 +172,7 @@ GEM
|
||||||
crass (1.0.4)
|
crass (1.0.4)
|
||||||
descendants_tracker (0.0.4)
|
descendants_tracker (0.0.4)
|
||||||
thread_safe (~> 0.3, >= 0.3.1)
|
thread_safe (~> 0.3, >= 0.3.1)
|
||||||
|
diff-lcs (1.3)
|
||||||
domain_name (0.5.20190701)
|
domain_name (0.5.20190701)
|
||||||
unf (>= 0.0.5, < 1.0.0)
|
unf (>= 0.0.5, < 1.0.0)
|
||||||
dotenv (0.7.0)
|
dotenv (0.7.0)
|
||||||
|
@ -329,6 +330,23 @@ GEM
|
||||||
http-cookie (>= 1.0.2, < 2.0)
|
http-cookie (>= 1.0.2, < 2.0)
|
||||||
mime-types (>= 1.16, < 4.0)
|
mime-types (>= 1.16, < 4.0)
|
||||||
netrc (~> 0.8)
|
netrc (~> 0.8)
|
||||||
|
rspec-core (3.8.2)
|
||||||
|
rspec-support (~> 3.8.0)
|
||||||
|
rspec-expectations (3.8.4)
|
||||||
|
diff-lcs (>= 1.2.0, < 2.0)
|
||||||
|
rspec-support (~> 3.8.0)
|
||||||
|
rspec-mocks (3.8.1)
|
||||||
|
diff-lcs (>= 1.2.0, < 2.0)
|
||||||
|
rspec-support (~> 3.8.0)
|
||||||
|
rspec-rails (3.8.2)
|
||||||
|
actionpack (>= 3.0)
|
||||||
|
activesupport (>= 3.0)
|
||||||
|
railties (>= 3.0)
|
||||||
|
rspec-core (~> 3.8.0)
|
||||||
|
rspec-expectations (~> 3.8.0)
|
||||||
|
rspec-mocks (~> 3.8.0)
|
||||||
|
rspec-support (~> 3.8.0)
|
||||||
|
rspec-support (3.8.2)
|
||||||
rubocop (0.74.0)
|
rubocop (0.74.0)
|
||||||
jaro_winkler (~> 1.5.1)
|
jaro_winkler (~> 1.5.1)
|
||||||
parallel (~> 1.10)
|
parallel (~> 1.10)
|
||||||
|
@ -456,6 +474,7 @@ DEPENDENCIES
|
||||||
redis-rack-cache
|
redis-rack-cache
|
||||||
responders
|
responders
|
||||||
rest-client
|
rest-client
|
||||||
|
rspec-rails (~> 3.8)
|
||||||
rubocop (~> 0.74.0)
|
rubocop (~> 0.74.0)
|
||||||
sass-rails (~> 5.0)
|
sass-rails (~> 5.0)
|
||||||
seed_dump
|
seed_dump
|
||||||
|
|
|
@ -28,6 +28,6 @@ class ConfirmationsController < Devise::ConfirmationsController
|
||||||
user.reset_password_token = enc
|
user.reset_password_token = enc
|
||||||
user.reset_password_sent_at = Time.now.utc
|
user.reset_password_sent_at = Time.now.utc
|
||||||
user.save(validate: false)
|
user.save(validate: false)
|
||||||
"/u/auth/password/edit?config=default&redirect_url=&reset_password_token="+raw
|
"/app/auth/password/edit?config=default&redirect_url=&reset_password_token="+raw
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
6
app/helpers/frontend_urls_helper.rb
Normal file
6
app/helpers/frontend_urls_helper.rb
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
module FrontendUrlsHelper
|
||||||
|
def frontend_url(path, **query_params)
|
||||||
|
url_params = query_params.blank? ? "" : "?#{query_params.to_query}"
|
||||||
|
"#{root_url}app/#{path}#{url_params}"
|
||||||
|
end
|
||||||
|
end
|
|
@ -7,6 +7,7 @@
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import Cookies from 'js-cookie';
|
import Cookies from 'js-cookie';
|
||||||
import endPoints from './endPoints';
|
import endPoints from './endPoints';
|
||||||
|
import { frontendURL } from '../helper/URLHelper';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
login(creds) {
|
login(creds) {
|
||||||
|
@ -65,7 +66,7 @@ export default {
|
||||||
if (error.response.status === 401) {
|
if (error.response.status === 401) {
|
||||||
Cookies.remove('auth_data');
|
Cookies.remove('auth_data');
|
||||||
Cookies.remove('user');
|
Cookies.remove('user');
|
||||||
window.location = '/login';
|
window.location = frontendURL('login');
|
||||||
}
|
}
|
||||||
reject(error);
|
reject(error);
|
||||||
});
|
});
|
||||||
|
@ -80,7 +81,7 @@ export default {
|
||||||
.then(response => {
|
.then(response => {
|
||||||
Cookies.remove('auth_data');
|
Cookies.remove('auth_data');
|
||||||
Cookies.remove('user');
|
Cookies.remove('user');
|
||||||
window.location = '/u/login';
|
window.location = frontendURL('login');
|
||||||
resolve(response);
|
resolve(response);
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
<template>
|
<template>
|
||||||
<button type="submit" :disabled="disabled" :class="computedClass">
|
<button type="submit" :disabled="disabled" :class="computedClass">
|
||||||
<i :class="iconClass" class="icon" v-if="!!iconClass"></i>
|
<i v-if="!!iconClass" :class="iconClass" class="icon"></i>
|
||||||
<span>{{buttonText}}</span>
|
<span>{{ buttonText }}</span>
|
||||||
<spinner v-if="loading"/>
|
<spinner v-if="loading" />
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -10,19 +10,34 @@
|
||||||
import Spinner from '../Spinner';
|
import Spinner from '../Spinner';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
|
||||||
disabled: Boolean,
|
|
||||||
loading: Boolean,
|
|
||||||
buttonText: String,
|
|
||||||
buttonClass: String,
|
|
||||||
iconClass: String,
|
|
||||||
},
|
|
||||||
components: {
|
components: {
|
||||||
Spinner,
|
Spinner,
|
||||||
},
|
},
|
||||||
|
props: {
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
loading: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
buttonText: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
buttonClass: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
iconClass: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
computedClass() {
|
computedClass() {
|
||||||
return `button ${this.buttonClass || ' '}`;
|
return `button nice ${this.buttonClass || ' '}`;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
<template>
|
<template>
|
||||||
<button type="button" v-on:click="toggleStatus" class="button round resolve--button" :class="buttonClass">
|
<button
|
||||||
<i class="icon" :class="buttonIconClass" v-if="!isLoading"></i>
|
type="button"
|
||||||
<spinner v-if="isLoading"/>
|
class="button nice resolve--button"
|
||||||
|
:class="buttonClass"
|
||||||
|
@click="toggleStatus"
|
||||||
|
>
|
||||||
|
<i v-if="!isLoading" class="icon" :class="buttonIconClass"></i>
|
||||||
|
<spinner v-if="isLoading" />
|
||||||
{{ currentStatus }}
|
{{ currentStatus }}
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
|
@ -13,9 +18,7 @@ import { mapGetters } from 'vuex';
|
||||||
import Spinner from '../Spinner';
|
import Spinner from '../Spinner';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: [
|
props: ['conversationId'],
|
||||||
'conversationId',
|
|
||||||
],
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
|
@ -50,5 +53,3 @@ export default {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,47 +1,56 @@
|
||||||
<template>
|
<template>
|
||||||
<aside class="sidebar animated shrink columns">
|
<aside class="sidebar animated shrink columns">
|
||||||
<div class="logo">
|
<div class="logo">
|
||||||
<router-link to="{ path: '/u/dashboard' }" replace>
|
<router-link :to="dashboardPath" replace>
|
||||||
<img src="~assets/images/woot-logo.svg" alt="Woot-logo"/>
|
<img src="~assets/images/woot-logo.svg" alt="Woot-logo" />
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="main-nav">
|
<div class="main-nav">
|
||||||
<transition-group name="menu-list" tag="ul" class="menu vertical">
|
<transition-group name="menu-list" tag="ul" class="menu vertical">
|
||||||
<sidebar-item
|
<sidebar-item
|
||||||
v-for="item in sidebarItems"
|
v-for="item in accessibleMenuItems"
|
||||||
|
:key="item.toState"
|
||||||
:menu-item="item"
|
:menu-item="item"
|
||||||
:key="item"
|
|
||||||
v-if="showItem(item)"
|
|
||||||
/>
|
/>
|
||||||
</transition-group>
|
</transition-group>
|
||||||
</div>
|
</div>
|
||||||
<transition name="fade" mode="out-in">
|
<transition name="fade" mode="out-in">
|
||||||
<woot-status-bar
|
<woot-status-bar
|
||||||
|
v-if="shouldShowStatusBox"
|
||||||
:message="trialMessage"
|
:message="trialMessage"
|
||||||
:buttonText="$t('APP_GLOBAL.TRAIL_BUTTON')"
|
:button-text="$t('APP_GLOBAL.TRAIL_BUTTON')"
|
||||||
:buttonRoute="{ name: 'billing' }"
|
:button-route="{ name: 'billing' }"
|
||||||
:type="statusBarClass"
|
:type="statusBarClass"
|
||||||
:show-button="isAdmin()"
|
:show-button="isAdmin()"
|
||||||
v-if="shouldShowStatusBox"
|
|
||||||
/>
|
/>
|
||||||
</transition>
|
</transition>
|
||||||
<div class="bottom-nav">
|
<div class="bottom-nav">
|
||||||
<transition name="menu-slide">
|
<transition name="menu-slide">
|
||||||
<div class="dropdown-pane top" v-if="showOptionsMenu" v-on-clickaway="showOptions">
|
<div
|
||||||
|
v-if="showOptionsMenu"
|
||||||
|
v-on-clickaway="showOptions"
|
||||||
|
class="dropdown-pane top"
|
||||||
|
>
|
||||||
<ul class="vertical dropdown menu">
|
<ul class="vertical dropdown menu">
|
||||||
<li><a href="#">Help & Support</a></li>
|
<!-- <li><a href="#">Help & Support</a></li> -->
|
||||||
<li><a href="#" @click.prevent="logout()">Logout</a></li>
|
<li><a href="#" @click.prevent="logout()">Logout</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
<div class="current-user" @click.prevent="showOptions()">
|
<div class="current-user" @click.prevent="showOptions()">
|
||||||
<img class="current-user--thumbnail" :src="gravatarUrl()"/>
|
<img class="current-user--thumbnail" :src="gravatarUrl()" />
|
||||||
<div class="current-user--data">
|
<div class="current-user--data">
|
||||||
<h3 class="current-user--name">{{ currentUser.name }}</h3>
|
<h3 class="current-user--name">
|
||||||
<h5 class="current-user--role">{{ currentUser.role }}</h5>
|
{{ currentUser.name }}
|
||||||
|
</h3>
|
||||||
|
<h5 class="current-user--role">
|
||||||
|
{{ currentUser.role }}
|
||||||
|
</h5>
|
||||||
</div>
|
</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>
|
||||||
<!-- <router-link class="icon ion-arrow-graph-up-right" tag="span" to="/settings/reports" active-class="active"></router-link> -->
|
<!-- <router-link class="icon ion-arrow-graph-up-right" tag="span" to="/settings/reports" active-class="active"></router-link> -->
|
||||||
</div>
|
</div>
|
||||||
|
@ -57,17 +66,20 @@ import adminMixin from '../../mixins/isAdmin';
|
||||||
import Auth from '../../api/auth';
|
import Auth from '../../api/auth';
|
||||||
import SidebarItem from './SidebarItem';
|
import SidebarItem from './SidebarItem';
|
||||||
import WootStatusBar from '../widgets/StatusBar';
|
import WootStatusBar from '../widgets/StatusBar';
|
||||||
/* eslint-disable no-console */
|
import { frontendURL } from '../../helper/URLHelper';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
mixins: [clickaway, adminMixin],
|
||||||
props: {
|
props: {
|
||||||
route: String,
|
route: {
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
showOptionsMenu: false,
|
showOptionsMenu: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
mixins: [clickaway, adminMixin],
|
|
||||||
mounted() {
|
mounted() {
|
||||||
// this.$store.dispatch('fetchLabels');
|
// this.$store.dispatch('fetchLabels');
|
||||||
this.$store.dispatch('fetchInboxes');
|
this.$store.dispatch('fetchInboxes');
|
||||||
|
@ -78,22 +90,30 @@ export default {
|
||||||
daysLeft: 'getTrialLeft',
|
daysLeft: 'getTrialLeft',
|
||||||
subscriptionData: 'getSubscription',
|
subscriptionData: 'getSubscription',
|
||||||
}),
|
}),
|
||||||
sidebarItems() {
|
accessibleMenuItems() {
|
||||||
// Get Current Route
|
|
||||||
const currentRoute = this.$store.state.route.name;
|
const currentRoute = this.$store.state.route.name;
|
||||||
// get all keys in menuGroup
|
// get all keys in menuGroup
|
||||||
const groupKey = Object.keys(this.sidebarList);
|
const groupKey = Object.keys(this.sidebarList);
|
||||||
|
|
||||||
|
let menuItems = [];
|
||||||
// Iterate over menuGroup to find the correct group
|
// Iterate over menuGroup to find the correct group
|
||||||
for (let i = 0; i < groupKey.length; i += 1) {
|
for (let i = 0; i < groupKey.length; i += 1) {
|
||||||
const groupItem = this.sidebarList[groupKey[i]];
|
const groupItem = this.sidebarList[groupKey[i]];
|
||||||
// Check if current route is included
|
// Check if current route is included
|
||||||
const isRouteIncluded = groupItem.routes.indexOf(currentRoute) > -1;
|
const isRouteIncluded = groupItem.routes.indexOf(currentRoute) > -1;
|
||||||
if (isRouteIncluded) {
|
if (isRouteIncluded) {
|
||||||
return groupItem.menuItems;
|
menuItems = Object.values(groupItem.menuItems);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If not found return empty array
|
|
||||||
return [];
|
const { role } = this.currentUser;
|
||||||
|
return menuItems.filter(
|
||||||
|
menuItem =>
|
||||||
|
window.roleWiseRoutes[role].indexOf(menuItem.toStateName) > -1
|
||||||
|
);
|
||||||
|
},
|
||||||
|
dashboardPath() {
|
||||||
|
return frontendURL('dashboard');
|
||||||
},
|
},
|
||||||
currentUser() {
|
currentUser() {
|
||||||
return Auth.getCurrentUser();
|
return Auth.getCurrentUser();
|
||||||
|
@ -102,12 +122,16 @@ export default {
|
||||||
return `${this.daysLeft} ${this.$t('APP_GLOBAL.TRIAL_MESSAGE')}`;
|
return `${this.daysLeft} ${this.$t('APP_GLOBAL.TRIAL_MESSAGE')}`;
|
||||||
},
|
},
|
||||||
shouldShowStatusBox() {
|
shouldShowStatusBox() {
|
||||||
return this.subscriptionData.state === 'trial' || this.subscriptionData.state === 'cancelled';
|
return (
|
||||||
|
this.subscriptionData.state === 'trial' ||
|
||||||
|
this.subscriptionData.state === 'cancelled'
|
||||||
|
);
|
||||||
},
|
},
|
||||||
statusBarClass() {
|
statusBarClass() {
|
||||||
if (this.subscriptionData.state === 'trial') {
|
if (this.subscriptionData.state === 'trial') {
|
||||||
return 'warning';
|
return 'warning';
|
||||||
} else if (this.subscriptionData.state === 'cancelled') {
|
}
|
||||||
|
if (this.subscriptionData.state === 'cancelled') {
|
||||||
return 'danger';
|
return 'danger';
|
||||||
}
|
}
|
||||||
return '';
|
return '';
|
||||||
|
@ -122,11 +146,6 @@ export default {
|
||||||
const hash = md5(this.currentUser.email);
|
const hash = md5(this.currentUser.email);
|
||||||
return `${window.WootConstants.GRAVATAR_URL}${hash}?d=monsterid`;
|
return `${window.WootConstants.GRAVATAR_URL}${hash}?d=monsterid`;
|
||||||
},
|
},
|
||||||
// Show if user has access to the route
|
|
||||||
showItem(item) {
|
|
||||||
const { role } = this.currentUser;
|
|
||||||
return window.roleWiseRoutes[role].indexOf(item.toStateName) > -1;
|
|
||||||
},
|
|
||||||
showOptions() {
|
showOptions() {
|
||||||
this.showOptionsMenu = !this.showOptionsMenu;
|
this.showOptionsMenu = !this.showOptionsMenu;
|
||||||
},
|
},
|
||||||
|
|
|
@ -46,7 +46,7 @@
|
||||||
<span v-if="isAdmin()">
|
<span v-if="isAdmin()">
|
||||||
{{ $t('CONVERSATION.NO_INBOX_1') }}
|
{{ $t('CONVERSATION.NO_INBOX_1') }}
|
||||||
<br />
|
<br />
|
||||||
<router-link to="/u/settings/inboxes/new">
|
<router-link :to="newInboxURL">
|
||||||
{{ $t('CONVERSATION.CLICK_HERE') }}
|
{{ $t('CONVERSATION.CLICK_HERE') }}
|
||||||
</router-link>
|
</router-link>
|
||||||
{{ $t('CONVERSATION.NO_INBOX_2') }}
|
{{ $t('CONVERSATION.NO_INBOX_2') }}
|
||||||
|
@ -88,6 +88,7 @@ import ReplyBox from './ReplyBox';
|
||||||
import Conversation from './Conversation';
|
import Conversation from './Conversation';
|
||||||
import conversationMixin from '../../../mixins/conversations';
|
import conversationMixin from '../../../mixins/conversations';
|
||||||
import adminMixin from '../../../mixins/isAdmin';
|
import adminMixin from '../../../mixins/isAdmin';
|
||||||
|
import { frontendURL } from '../../../helper/URLHelper';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
@ -169,6 +170,10 @@ export default {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
newInboxURL() {
|
||||||
|
return frontendURL('settings/inboxes/new');
|
||||||
|
},
|
||||||
|
|
||||||
shouldLoadMoreChats() {
|
shouldLoadMoreChats() {
|
||||||
return !this.listLoadingStatus && !this.isLoadingPrevious;
|
return !this.listLoadingStatus && !this.isLoadingPrevious;
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,11 +1,34 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="conversation" :class="{ active: isActiveChat, 'unread-chat': hasUnread }" @click="cardClick(chat)" >
|
<div
|
||||||
<Thumbnail :src="chat.meta.sender.thumbnail" :badge="chat.meta.sender.channel" class="columns" />
|
class="conversation"
|
||||||
|
:class="{ active: isActiveChat, 'unread-chat': hasUnread }"
|
||||||
|
@click="cardClick(chat)"
|
||||||
|
>
|
||||||
|
<Thumbnail
|
||||||
|
:src="chat.meta.sender.thumbnail"
|
||||||
|
:badge="chat.meta.sender.channel"
|
||||||
|
class="columns"
|
||||||
|
/>
|
||||||
<div class="conversation--details columns">
|
<div class="conversation--details columns">
|
||||||
<h4 class="conversation--user">{{ chat.meta.sender.name }} <span class="label" v-tooltip.bottom="inboxName(chat.inbox_id)" v-if="isInboxNameVisible">{{inboxName(chat.inbox_id)}}</span> </h4>
|
<h4 class="conversation--user">
|
||||||
<p class="conversation--message" v-html="extractMessageText(lastMessage(chat))"></p>
|
{{ chat.meta.sender.name }}
|
||||||
|
<span
|
||||||
|
v-if="isInboxNameVisible"
|
||||||
|
v-tooltip.bottom="inboxName(chat.inbox_id)"
|
||||||
|
class="label"
|
||||||
|
>
|
||||||
|
{{ inboxName(chat.inbox_id) }}
|
||||||
|
</span>
|
||||||
|
</h4>
|
||||||
|
<p
|
||||||
|
class="conversation--message"
|
||||||
|
v-html="extractMessageText(lastMessage(chat))"
|
||||||
|
></p>
|
||||||
|
|
||||||
<div class="conversation--meta">
|
<div class="conversation--meta">
|
||||||
<span class="timestamp">{{ dynamicTime(lastMessage(chat).created_at) }}</span>
|
<span class="timestamp">
|
||||||
|
{{ dynamicTime(lastMessage(chat).created_at) }}
|
||||||
|
</span>
|
||||||
<span class="unread">{{ getUnreadCount }}</span>
|
<span class="unread">{{ getUnreadCount }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -14,27 +37,25 @@
|
||||||
<script>
|
<script>
|
||||||
/* eslint no-console: 0 */
|
/* eslint no-console: 0 */
|
||||||
/* eslint no-extra-boolean-cast: 0 */
|
/* eslint no-extra-boolean-cast: 0 */
|
||||||
/* global bus */
|
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import Thumbnail from '../Thumbnail';
|
import Thumbnail from '../Thumbnail';
|
||||||
import getEmojiSVG from '../emoji/utils';
|
import getEmojiSVG from '../emoji/utils';
|
||||||
import conversationMixin from '../../../mixins/conversations';
|
import conversationMixin from '../../../mixins/conversations';
|
||||||
import timeMixin from '../../../mixins/time';
|
import timeMixin from '../../../mixins/time';
|
||||||
import router from '../../../routes';
|
import router from '../../../routes';
|
||||||
|
import { frontendURL } from '../../../helper/URLHelper';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: [
|
|
||||||
'chat',
|
|
||||||
],
|
|
||||||
|
|
||||||
mixins: [timeMixin, conversationMixin],
|
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
Thumbnail,
|
Thumbnail,
|
||||||
},
|
},
|
||||||
|
|
||||||
created() {
|
mixins: [timeMixin, conversationMixin],
|
||||||
// console.log(this.chat.inbox_id);
|
props: {
|
||||||
|
chat: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -64,7 +85,7 @@ export default {
|
||||||
methods: {
|
methods: {
|
||||||
cardClick(chat) {
|
cardClick(chat) {
|
||||||
router.push({
|
router.push({
|
||||||
path: `/u/conversations/${chat.id}`,
|
path: frontendURL(`conversations/${chat.id}`),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
extractMessageText(chatItem) {
|
extractMessageText(chatItem) {
|
||||||
|
@ -85,7 +106,9 @@ export default {
|
||||||
},
|
},
|
||||||
getEmojiSVG,
|
getEmojiSVG,
|
||||||
inboxName(inboxId) {
|
inboxName(inboxId) {
|
||||||
const [stateInbox] = this.inboxesList.filter(inbox => inbox.channel_id === inboxId);
|
const [stateInbox] = this.inboxesList.filter(
|
||||||
|
inbox => inbox.channel_id === inboxId
|
||||||
|
);
|
||||||
return !stateInbox ? '' : stateInbox.label;
|
return !stateInbox ? '' : stateInbox.label;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
6
app/javascript/src/helper/URLHelper.js
Normal file
6
app/javascript/src/helper/URLHelper.js
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
import queryString from 'query-string';
|
||||||
|
|
||||||
|
export const frontendURL = (path, params) => {
|
||||||
|
const stringifiedParams = params ? `?${queryString.stringify(params)}` : '';
|
||||||
|
return `/app/${path}${stringifiedParams}`;
|
||||||
|
};
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { frontendURL } from '../helper/URLHelper';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
menuGroup: {
|
menuGroup: {
|
||||||
common: {
|
common: {
|
||||||
|
@ -14,7 +16,7 @@ export default {
|
||||||
label: 'Conversations',
|
label: 'Conversations',
|
||||||
hasSubMenu: false,
|
hasSubMenu: false,
|
||||||
key: '',
|
key: '',
|
||||||
toState: '/u/dashboard',
|
toState: frontendURL('dashboard'),
|
||||||
toolTip: 'Conversation from all subscribed inboxes',
|
toolTip: 'Conversation from all subscribed inboxes',
|
||||||
toStateName: 'home',
|
toStateName: 'home',
|
||||||
},
|
},
|
||||||
|
@ -22,14 +24,14 @@ export default {
|
||||||
icon: 'ion-arrow-graph-up-right',
|
icon: 'ion-arrow-graph-up-right',
|
||||||
label: 'Reports',
|
label: 'Reports',
|
||||||
hasSubMenu: false,
|
hasSubMenu: false,
|
||||||
toState: '/u/reports',
|
toState: frontendURL('reports'),
|
||||||
toStateName: 'settings_account_reports',
|
toStateName: 'settings_account_reports',
|
||||||
},
|
},
|
||||||
settings: {
|
settings: {
|
||||||
icon: 'ion-settings',
|
icon: 'ion-settings',
|
||||||
label: 'Settings',
|
label: 'Settings',
|
||||||
hasSubMenu: false,
|
hasSubMenu: false,
|
||||||
toState: '/u/settings/',
|
toState: frontendURL('settings'),
|
||||||
toStateName: 'settings_home',
|
toStateName: 'settings_home',
|
||||||
},
|
},
|
||||||
inbox: {
|
inbox: {
|
||||||
|
@ -39,7 +41,7 @@ export default {
|
||||||
newLink: true,
|
newLink: true,
|
||||||
key: 'inbox',
|
key: 'inbox',
|
||||||
cssClass: 'menu-title align-justify',
|
cssClass: 'menu-title align-justify',
|
||||||
toState: '/u/settings/inboxes',
|
toState: frontendURL('settings/inboxes'),
|
||||||
toStateName: 'settings_inbox_list',
|
toStateName: 'settings_inbox_list',
|
||||||
children: [],
|
children: [],
|
||||||
},
|
},
|
||||||
|
@ -65,41 +67,41 @@ export default {
|
||||||
label: 'Home',
|
label: 'Home',
|
||||||
hasSubMenu: false,
|
hasSubMenu: false,
|
||||||
toStateName: 'home',
|
toStateName: 'home',
|
||||||
toState: '/u/dashboard',
|
toState: frontendURL('dashboard'),
|
||||||
},
|
},
|
||||||
agents: {
|
agents: {
|
||||||
icon: 'ion-person-stalker',
|
icon: 'ion-person-stalker',
|
||||||
label: 'Agents',
|
label: 'Agents',
|
||||||
hasSubMenu: false,
|
hasSubMenu: false,
|
||||||
toState: '/u/settings/agents/list',
|
toState: frontendURL('settings/agents/list'),
|
||||||
toStateName: 'agent_list',
|
toStateName: 'agent_list',
|
||||||
},
|
},
|
||||||
inboxes: {
|
inboxes: {
|
||||||
icon: 'ion-archive',
|
icon: 'ion-archive',
|
||||||
label: 'Inboxes',
|
label: 'Inboxes',
|
||||||
hasSubMenu: false,
|
hasSubMenu: false,
|
||||||
toState: '/u/settings/inboxes/list',
|
toState: frontendURL('settings/inboxes/list'),
|
||||||
toStateName: 'settings_inbox_list',
|
toStateName: 'settings_inbox_list',
|
||||||
},
|
},
|
||||||
cannedResponses: {
|
cannedResponses: {
|
||||||
icon: 'ion-chatbox-working',
|
icon: 'ion-chatbox-working',
|
||||||
label: 'Canned Responses',
|
label: 'Canned Responses',
|
||||||
hasSubMenu: false,
|
hasSubMenu: false,
|
||||||
toState: '/u/settings/canned-response/list',
|
toState: frontendURL('settings/canned-response/list'),
|
||||||
toStateName: 'canned_list',
|
toStateName: 'canned_list',
|
||||||
},
|
},
|
||||||
billing: {
|
billing: {
|
||||||
icon: 'ion-card',
|
icon: 'ion-card',
|
||||||
label: 'Billing',
|
label: 'Billing',
|
||||||
hasSubMenu: false,
|
hasSubMenu: false,
|
||||||
toState: '/u/settings/billing',
|
toState: frontendURL('settings/billing'),
|
||||||
toStateName: 'billing',
|
toStateName: 'billing',
|
||||||
},
|
},
|
||||||
account: {
|
account: {
|
||||||
icon: 'ion-beer',
|
icon: 'ion-beer',
|
||||||
label: 'Account Settings',
|
label: 'Account Settings',
|
||||||
hasSubMenu: false,
|
hasSubMenu: false,
|
||||||
toState: '/u/settings/account',
|
toState: frontendURL('settings/account'),
|
||||||
toStateName: 'account',
|
toStateName: 'account',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"INBOX_MGMT": {
|
"INBOX_MGMT": {
|
||||||
"HEADER": "Inboxes",
|
"HEADER": "Inboxes",
|
||||||
"SIDEBAR_TXT": "<p><b>Inbox</b></p> <p> When you connect a Facebook Page to Chatwoot, it is called an <b>Inbox</b>. You can have unlimited inboxes in your Chatwoot account. </p><p> Click on <b>Add Inbox</b> to connect a new Facebook Page. </p><p> In the <a href=\"/u/dashboard\">Dashboard</a>, you can see all the conversations from all your inboxes in a single place and respond to them under the `Conversations` tab. </p><p> You can also see conversations specific to an inbox by clicking on the inbox name on the left pane of the dashboard. </p>",
|
"SIDEBAR_TXT": "<p><b>Inbox</b></p> <p> When you connect a Facebook Page to Chatwoot, it is called an <b>Inbox</b>. You can have unlimited inboxes in your Chatwoot account. </p><p> Click on <b>Add Inbox</b> to connect a new Facebook Page. </p><p> In the <a href=\"/app/dashboard\">Dashboard</a>, you can see all the conversations from all your inboxes in a single place and respond to them under the `Conversations` tab. </p><p> You can also see conversations specific to an inbox by clicking on the inbox name on the left pane of the dashboard. </p>",
|
||||||
"LIST": {
|
"LIST": {
|
||||||
"404": "There are no inboxes attached to this account."
|
"404": "There are no inboxes attached to this account."
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,13 +1,20 @@
|
||||||
<template>
|
<template>
|
||||||
<form class="login-box medium-4 column align-self-middle" v-on:submit.prevent="submit()">
|
<form
|
||||||
<h4>{{$t('RESET_PASSWORD.TITLE')}}</h4>
|
class="login-box medium-4 column align-self-middle"
|
||||||
|
@submit.prevent="submit()"
|
||||||
|
>
|
||||||
|
<h4>{{ $t('RESET_PASSWORD.TITLE') }}</h4>
|
||||||
<div class="column log-in-form">
|
<div class="column log-in-form">
|
||||||
|
<label :class="{ error: $v.credentials.email.$error }">
|
||||||
<label :class="{ 'error': $v.credentials.email.$error }">
|
{{ $t('RESET_PASSWORD.EMAIL.LABEL') }}
|
||||||
{{$t('RESET_PASSWORD.EMAIL.LABEL')}}
|
<input
|
||||||
<input type="text" v-bind:placeholder="$t('RESET_PASSWORD.EMAIL.PLACEHOLDER')" v-model.trim="credentials.email" @input="$v.credentials.email.$touch">
|
v-model.trim="credentials.email"
|
||||||
<span class="message" v-if="$v.credentials.email.$error">
|
type="text"
|
||||||
{{$t('RESET_PASSWORD.EMAIL.ERROR')}}
|
:placeholder="$t('RESET_PASSWORD.EMAIL.PLACEHOLDER')"
|
||||||
|
@input="$v.credentials.email.$touch"
|
||||||
|
/>
|
||||||
|
<span v-if="$v.credentials.email.$error" class="message">
|
||||||
|
{{ $t('RESET_PASSWORD.EMAIL.ERROR') }}
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
<woot-submit-button
|
<woot-submit-button
|
||||||
|
@ -24,6 +31,7 @@
|
||||||
/* global bus */
|
/* global bus */
|
||||||
import { required, minLength, email } from 'vuelidate/lib/validators';
|
import { required, minLength, email } from 'vuelidate/lib/validators';
|
||||||
import Auth from '../../api/auth';
|
import Auth from '../../api/auth';
|
||||||
|
import { frontendURL } from '../../helper/URLHelper';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
|
@ -58,13 +66,13 @@ export default {
|
||||||
submit() {
|
submit() {
|
||||||
this.resetPassword.showLoading = true;
|
this.resetPassword.showLoading = true;
|
||||||
Auth.resetPassword(this.credentials)
|
Auth.resetPassword(this.credentials)
|
||||||
.then((res) => {
|
.then(res => {
|
||||||
let message = this.$t('RESET_PASSWORD.API.SUCCESS_MESSAGE');
|
let successMessage = this.$t('RESET_PASSWORD.API.SUCCESS_MESSAGE');
|
||||||
if (res.data && res.data.message) {
|
if (res.data && res.data.message) {
|
||||||
message = res.data.message;
|
successMessage = res.data.message;
|
||||||
}
|
}
|
||||||
this.showAlert(message);
|
this.showAlert(successMessage);
|
||||||
window.location = '/login';
|
window.location = frontendURL('login');
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
this.showAlert(this.$t('RESET_PASSWORD.API.ERROR_MESSAGE'));
|
this.showAlert(this.$t('RESET_PASSWORD.API.ERROR_MESSAGE'));
|
||||||
|
|
|
@ -1,9 +1,17 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="medium-10 column signup">
|
<div class="medium-10 column signup">
|
||||||
<div class="text-center medium-12 signup__hero">
|
<div class="text-center medium-12 signup__hero">
|
||||||
<img src="~assets/images/woot-logo.svg" alt="Woot-logo" class="hero__logo" />
|
<img
|
||||||
<h2 class="hero__title">{{$t('REGISTER.TRY_WOOT')}}</h2>
|
src="~assets/images/woot-logo.svg"
|
||||||
<p class="hero__sub">{{$t('REGISTER.TRY_WOOT_SUB')}}</p>
|
alt="Woot-logo"
|
||||||
|
class="hero__logo"
|
||||||
|
/>
|
||||||
|
<h2 class="hero__title">
|
||||||
|
{{ $t('REGISTER.TRY_WOOT') }}
|
||||||
|
</h2>
|
||||||
|
<p class="hero__sub">
|
||||||
|
{{ $t('REGISTER.TRY_WOOT_SUB') }}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="row align-center">
|
<div class="row align-center">
|
||||||
<div class="medium-5 column">
|
<div class="medium-5 column">
|
||||||
|
@ -16,24 +24,38 @@
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="medium-5 column">
|
<div class="medium-5 column">
|
||||||
<form class="signup-box login-box " v-on:submit.prevent="submit()">
|
<form class="signup-box login-box " @submit.prevent="submit()">
|
||||||
<div class="column log-in-form">
|
<div class="column log-in-form">
|
||||||
<label :class="{ 'error': $v.credentials.name.$error }">
|
<label :class="{ error: $v.credentials.name.$error }">
|
||||||
{{$t('REGISTER.ACCOUNT_NAME.LABEL')}}
|
{{ $t('REGISTER.ACCOUNT_NAME.LABEL') }}
|
||||||
<input type="text" v-bind:placeholder="$t('REGISTER.ACCOUNT_NAME.PLACEHOLDER')" v-model.trim="credentials.name" @input="$v.credentials.name.$touch">
|
<input
|
||||||
<span class="message" v-if="$v.credentials.name.$error">
|
v-model.trim="credentials.name"
|
||||||
{{$t('REGISTER.ACCOUNT_NAME.ERROR')}}
|
type="text"
|
||||||
|
:placeholder="$t('REGISTER.ACCOUNT_NAME.PLACEHOLDER')"
|
||||||
|
@input="$v.credentials.name.$touch"
|
||||||
|
/>
|
||||||
|
<span v-if="$v.credentials.name.$error" class="message">
|
||||||
|
{{ $t('REGISTER.ACCOUNT_NAME.ERROR') }}
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
<label :class="{ 'error': $v.credentials.email.$error }">
|
<label :class="{ error: $v.credentials.email.$error }">
|
||||||
{{$t('REGISTER.EMAIL.LABEL')}}
|
{{ $t('REGISTER.EMAIL.LABEL') }}
|
||||||
<input type="email" v-bind:placeholder="$t('REGISTER.EMAIL.PLACEHOLDER')" v-model.trim="credentials.email" @input="$v.credentials.email.$touch">
|
<input
|
||||||
<span class="message" v-if="$v.credentials.email.$error">
|
v-model.trim="credentials.email"
|
||||||
{{$t('REGISTER.EMAIL.ERROR')}}
|
type="email"
|
||||||
|
:placeholder="$t('REGISTER.EMAIL.PLACEHOLDER')"
|
||||||
|
@input="$v.credentials.email.$touch"
|
||||||
|
/>
|
||||||
|
<span v-if="$v.credentials.email.$error" class="message">
|
||||||
|
{{ $t('REGISTER.EMAIL.ERROR') }}
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
<woot-submit-button
|
<woot-submit-button
|
||||||
:disabled="$v.credentials.name.$invalid || $v.credentials.email.$invalid || register.showLoading"
|
:disabled="
|
||||||
|
$v.credentials.name.$invalid ||
|
||||||
|
$v.credentials.email.$invalid ||
|
||||||
|
register.showLoading
|
||||||
|
"
|
||||||
:button-text="$t('REGISTER.SUBMIT')"
|
:button-text="$t('REGISTER.SUBMIT')"
|
||||||
:loading="register.showLoading"
|
:loading="register.showLoading"
|
||||||
button-class="large expanded"
|
button-class="large expanded"
|
||||||
|
@ -58,6 +80,7 @@
|
||||||
|
|
||||||
import { required, minLength, email } from 'vuelidate/lib/validators';
|
import { required, minLength, email } from 'vuelidate/lib/validators';
|
||||||
import Auth from '../../api/auth';
|
import Auth from '../../api/auth';
|
||||||
|
import { frontendURL } from '../../helper/URLHelper';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
|
@ -96,12 +119,12 @@ export default {
|
||||||
submit() {
|
submit() {
|
||||||
this.register.showLoading = true;
|
this.register.showLoading = true;
|
||||||
Auth.register(this.credentials)
|
Auth.register(this.credentials)
|
||||||
.then((res) => {
|
.then(res => {
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
window.location = '/u/dashboard';
|
window.location = frontendURL('dashboard');
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch(error => {
|
||||||
let errorMessage = this.$t('REGISTER.API.ERROR_MESSAGE');
|
let errorMessage = this.$t('REGISTER.API.ERROR_MESSAGE');
|
||||||
if (error.response && error.response.data.message) {
|
if (error.response && error.response.data.message) {
|
||||||
errorMessage = error.response.data.message;
|
errorMessage = error.response.data.message;
|
||||||
|
|
|
@ -3,11 +3,12 @@ import Confirmation from './Confirmation';
|
||||||
import Signup from './Signup';
|
import Signup from './Signup';
|
||||||
import PasswordEdit from './PasswordEdit';
|
import PasswordEdit from './PasswordEdit';
|
||||||
import ResetPassword from './ResetPassword';
|
import ResetPassword from './ResetPassword';
|
||||||
|
import { frontendURL } from '../../helper/URLHelper';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
routes: [
|
routes: [
|
||||||
{
|
{
|
||||||
path: '/u/auth',
|
path: frontendURL('auth'),
|
||||||
name: 'auth',
|
name: 'auth',
|
||||||
component: Auth,
|
component: Auth,
|
||||||
children: [
|
children: [
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
/* eslint arrow-body-style: 0 */
|
/* eslint arrow-body-style: 0 */
|
||||||
import ConversationView from './ConversationView';
|
import ConversationView from './ConversationView';
|
||||||
|
import { frontendURL } from '../../../helper/URLHelper';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
routes: [
|
routes: [
|
||||||
{
|
{
|
||||||
path: '/u/dashboard',
|
path: frontendURL('dashboard'),
|
||||||
name: 'home',
|
name: 'home',
|
||||||
roles: ['administrator', 'agent'],
|
roles: ['administrator', 'agent'],
|
||||||
component: ConversationView,
|
component: ConversationView,
|
||||||
|
@ -13,7 +14,7 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/u/inbox/:inbox_id',
|
path: frontendURL('inbox/:inbox_id'),
|
||||||
name: 'inbox_dashboard',
|
name: 'inbox_dashboard',
|
||||||
roles: ['administrator', 'agent'],
|
roles: ['administrator', 'agent'],
|
||||||
component: ConversationView,
|
component: ConversationView,
|
||||||
|
@ -22,7 +23,7 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/u/conversations/:conversation_id',
|
path: frontendURL('conversations/:conversation_id'),
|
||||||
name: 'inbox_conversation',
|
name: 'inbox_conversation',
|
||||||
roles: ['administrator', 'agent'],
|
roles: ['administrator', 'agent'],
|
||||||
component: ConversationView,
|
component: ConversationView,
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
import AppContainer from './Dashboard';
|
import AppContainer from './Dashboard';
|
||||||
import settings from './settings/settings.routes';
|
import settings from './settings/settings.routes';
|
||||||
import conversation from './conversation/conversation.routes';
|
import conversation from './conversation/conversation.routes';
|
||||||
|
import { frontendURL } from '../../helper/URLHelper';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
routes: [
|
routes: [
|
||||||
{
|
{
|
||||||
path: '/u/',
|
path: frontendURL(''),
|
||||||
component: AppContainer,
|
component: AppContainer,
|
||||||
children: [...conversation.routes, ...settings.routes],
|
children: [...conversation.routes, ...settings.routes],
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="column content-box">
|
<div class="column content-box">
|
||||||
<button
|
<button
|
||||||
class="button icon success btn-fixed-right-top"
|
class="button nice icon success btn-fixed-right-top"
|
||||||
@click="openAddPopup()"
|
@click="openAddPopup()"
|
||||||
>
|
>
|
||||||
<i class="icon ion-android-add-circle"></i>
|
<i class="icon ion-android-add-circle"></i>
|
||||||
|
@ -12,13 +12,20 @@
|
||||||
<!-- List Agents -->
|
<!-- List Agents -->
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="small-8 columns">
|
<div class="small-8 columns">
|
||||||
<woot-loading-state v-if="fetchStatus" :message="$t('AGENT_MGMT.LOADING')" />
|
<woot-loading-state
|
||||||
<p v-if="!fetchStatus && !agentList.length">{{ $t('AGENT_MGMT.LIST.404') }}</p>
|
v-if="fetchStatus"
|
||||||
<table class="woot-table" v-if="!fetchStatus && agentList.length">
|
: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>
|
<tbody>
|
||||||
<tr v-for="(agent, index) in agentList">
|
<tr v-for="(agent, index) in agentList" :key="agent.email">
|
||||||
<!-- Gravtar Image -->
|
<!-- Gravtar Image -->
|
||||||
<td><img class="woot-thumbnail" :src="gravatarUrl(agent.email)"/></td>
|
<td>
|
||||||
|
<img class="woot-thumbnail" :src="gravatarUrl(agent.email)" />
|
||||||
|
</td>
|
||||||
<!-- Agent Name + Email -->
|
<!-- Agent Name + Email -->
|
||||||
<td>
|
<td>
|
||||||
<span class="agent-name">{{ agent.name }}</span>
|
<span class="agent-name">{{ agent.name }}</span>
|
||||||
|
@ -27,8 +34,12 @@
|
||||||
<!-- Agent Role + Verification Status -->
|
<!-- Agent Role + Verification Status -->
|
||||||
<td>
|
<td>
|
||||||
<span class="agent-name">{{ agent.role }}</span>
|
<span class="agent-name">{{ agent.role }}</span>
|
||||||
<span v-if="agent.confirmed">{{$t('AGENT_MGMT.LIST.VERIFIED')}}</span>
|
<span v-if="agent.confirmed">
|
||||||
<span v-if="!agent.confirmed">{{$t('AGENT_MGMT.LIST.VERIFICATION_PENDING')}}</span>
|
{{ $t('AGENT_MGMT.LIST.VERIFIED') }}
|
||||||
|
</span>
|
||||||
|
<span v-if="!agent.confirmed">
|
||||||
|
{{ $t('AGENT_MGMT.LIST.VERIFICATION_PENDING') }}
|
||||||
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<!-- Actions -->
|
<!-- Actions -->
|
||||||
<td>
|
<td>
|
||||||
|
@ -60,18 +71,18 @@
|
||||||
</div>
|
</div>
|
||||||
<!-- Add Agent -->
|
<!-- Add Agent -->
|
||||||
<woot-modal :show.sync="showAddPopup" :on-close="hideAddPopup">
|
<woot-modal :show.sync="showAddPopup" :on-close="hideAddPopup">
|
||||||
<add-agent :on-close="hideAddPopup"/>
|
<add-agent :on-close="hideAddPopup" />
|
||||||
</woot-modal>
|
</woot-modal>
|
||||||
|
|
||||||
<!-- Edit Agent -->
|
<!-- Edit Agent -->
|
||||||
<woot-modal :show.sync="showEditPopup" :on-close="hideEditPopup">
|
<woot-modal :show.sync="showEditPopup" :on-close="hideEditPopup">
|
||||||
<edit-agent
|
<edit-agent
|
||||||
|
v-if="showEditPopup"
|
||||||
|
:id="currentAgent.id"
|
||||||
:name="currentAgent.name"
|
:name="currentAgent.name"
|
||||||
:type="currentAgent.role"
|
:type="currentAgent.role"
|
||||||
:email="currentAgent.email"
|
:email="currentAgent.email"
|
||||||
:id="currentAgent.id"
|
|
||||||
:on-close="hideEditPopup"
|
:on-close="hideEditPopup"
|
||||||
v-if="showEditPopup"
|
|
||||||
/>
|
/>
|
||||||
</woot-modal>
|
</woot-modal>
|
||||||
|
|
||||||
|
@ -87,9 +98,7 @@
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- Loader Status -->
|
<!-- Loader Status -->
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
/* global bus */
|
/* global bus */
|
||||||
|
@ -97,14 +106,12 @@
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import md5 from 'md5';
|
import md5 from 'md5';
|
||||||
|
|
||||||
import PageHeader from '../SettingsSubPageHeader';
|
|
||||||
import AddAgent from './AddAgent';
|
import AddAgent from './AddAgent';
|
||||||
import EditAgent from './EditAgent';
|
import EditAgent from './EditAgent';
|
||||||
import DeleteAgent from './DeleteAgent';
|
import DeleteAgent from './DeleteAgent';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
PageHeader,
|
|
||||||
AddAgent,
|
AddAgent,
|
||||||
EditAgent,
|
EditAgent,
|
||||||
DeleteAgent,
|
DeleteAgent,
|
||||||
|
@ -127,13 +134,19 @@ export default {
|
||||||
fetchStatus: 'getAgentFetchStatus',
|
fetchStatus: 'getAgentFetchStatus',
|
||||||
}),
|
}),
|
||||||
deleteConfirmText() {
|
deleteConfirmText() {
|
||||||
return `${this.$t('AGENT_MGMT.DELETE.CONFIRM.YES')} ${this.currentAgent.name}`;
|
return `${this.$t('AGENT_MGMT.DELETE.CONFIRM.YES')} ${
|
||||||
|
this.currentAgent.name
|
||||||
|
}`;
|
||||||
},
|
},
|
||||||
deleteRejectText() {
|
deleteRejectText() {
|
||||||
return `${this.$t('AGENT_MGMT.DELETE.CONFIRM.NO')} ${this.currentAgent.name}`;
|
return `${this.$t('AGENT_MGMT.DELETE.CONFIRM.NO')} ${
|
||||||
|
this.currentAgent.name
|
||||||
|
}`;
|
||||||
},
|
},
|
||||||
deleteMessage() {
|
deleteMessage() {
|
||||||
return `${this.$t('AGENT_MGMT.DELETE.CONFIRM.MESSAGE')} ${this.currentAgent.name} ?`;
|
return `${this.$t('AGENT_MGMT.DELETE.CONFIRM.MESSAGE')} ${
|
||||||
|
this.currentAgent.name
|
||||||
|
} ?`;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
@ -142,7 +155,9 @@ export default {
|
||||||
methods: {
|
methods: {
|
||||||
showActions(agent) {
|
showActions(agent) {
|
||||||
if (agent.role === 'administrator') {
|
if (agent.role === 'administrator') {
|
||||||
const adminList = this.agentList.filter(item => item.role === 'administrator');
|
const adminList = this.agentList.filter(
|
||||||
|
item => item.role === 'administrator'
|
||||||
|
);
|
||||||
return adminList.length !== 1;
|
return adminList.length !== 1;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -184,11 +199,14 @@ export default {
|
||||||
this.deleteAgent(this.currentAgent.id);
|
this.deleteAgent(this.currentAgent.id);
|
||||||
},
|
},
|
||||||
deleteAgent(id) {
|
deleteAgent(id) {
|
||||||
this.$store.dispatch('deleteAgent', {
|
this.$store
|
||||||
|
.dispatch('deleteAgent', {
|
||||||
id,
|
id,
|
||||||
}).then(() => {
|
})
|
||||||
|
.then(() => {
|
||||||
this.showAlert(this.$t('AGENT_MGMT.DELETE.API.SUCCESS_MESSAGE'));
|
this.showAlert(this.$t('AGENT_MGMT.DELETE.API.SUCCESS_MESSAGE'));
|
||||||
}).catch(() => {
|
})
|
||||||
|
.catch(() => {
|
||||||
this.showAlert(this.$t('AGENT_MGMT.DELETE.API.ERROR_MESSAGE'));
|
this.showAlert(this.$t('AGENT_MGMT.DELETE.API.ERROR_MESSAGE'));
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -196,7 +214,7 @@ export default {
|
||||||
showAlert(message) {
|
showAlert(message) {
|
||||||
// Reset loading, current selected agent
|
// Reset loading, current selected agent
|
||||||
this.loading[this.currentAgent.id] = false;
|
this.loading[this.currentAgent.id] = false;
|
||||||
this.currentAgent = { };
|
this.currentAgent = {};
|
||||||
// Show message
|
// Show message
|
||||||
this.agentAPI.message = message;
|
this.agentAPI.message = message;
|
||||||
bus.$emit('newToastMessage', message);
|
bus.$emit('newToastMessage', message);
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import SettingsContent from '../Wrapper';
|
import SettingsContent from '../Wrapper';
|
||||||
import AgentHome from './Index';
|
import AgentHome from './Index';
|
||||||
|
import { frontendURL } from '../../../../helper/URLHelper';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
routes: [
|
routes: [
|
||||||
{
|
{
|
||||||
path: '/u/settings/agents',
|
path: frontendURL('settings/agents'),
|
||||||
component: SettingsContent,
|
component: SettingsContent,
|
||||||
props: {
|
props: {
|
||||||
headerTitle: 'AGENT_MGMT.HEADER',
|
headerTitle: 'AGENT_MGMT.HEADER',
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
import Index from './Index';
|
import Index from './Index';
|
||||||
import SettingsContent from '../Wrapper';
|
import SettingsContent from '../Wrapper';
|
||||||
import AccountLocked from './AccountLocked';
|
import AccountLocked from './AccountLocked';
|
||||||
|
import { frontendURL } from '../../../../helper/URLHelper';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
routes: [
|
routes: [
|
||||||
{
|
{
|
||||||
path: '/u/settings/billing',
|
path: frontendURL('settings/billing'),
|
||||||
component: SettingsContent,
|
component: SettingsContent,
|
||||||
props: {
|
props: {
|
||||||
headerTitle: 'BILLING.HEADER',
|
headerTitle: 'BILLING.HEADER',
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="column content-box">
|
<div class="column content-box">
|
||||||
<button
|
<button
|
||||||
class="button icon success btn-fixed-right-top"
|
class="button nice icon success btn-fixed-right-top"
|
||||||
@click="openAddPopup()"
|
@click="openAddPopup()"
|
||||||
>
|
>
|
||||||
<i class="icon ion-android-add-circle"></i>
|
<i class="icon ion-android-add-circle"></i>
|
||||||
|
@ -10,27 +10,42 @@
|
||||||
<!-- List Canned Response -->
|
<!-- List Canned Response -->
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="small-8 columns">
|
<div class="small-8 columns">
|
||||||
<p v-if="!fetchStatus && !cannedResponseList.length" class="no-items-error-message">
|
<p
|
||||||
|
v-if="!fetchStatus && !cannedResponseList.length"
|
||||||
|
class="no-items-error-message"
|
||||||
|
>
|
||||||
{{ $t('CANNED_MGMT.LIST.404') }}
|
{{ $t('CANNED_MGMT.LIST.404') }}
|
||||||
</p>
|
</p>
|
||||||
<woot-loading-state v-if="fetchStatus" :message="$t('CANNED_MGMT.LOADING')" />
|
<woot-loading-state
|
||||||
|
v-if="fetchStatus"
|
||||||
|
:message="$t('CANNED_MGMT.LOADING')"
|
||||||
|
/>
|
||||||
|
|
||||||
<table class="woot-table" v-if="!fetchStatus && cannedResponseList.length">
|
<table
|
||||||
|
v-if="!fetchStatus && cannedResponseList.length"
|
||||||
|
class="woot-table"
|
||||||
|
>
|
||||||
<thead>
|
<thead>
|
||||||
<!-- Header -->
|
<!-- Header -->
|
||||||
<th v-for="thHeader in $t('CANNED_MGMT.LIST.TABLE_HEADER')">
|
<th
|
||||||
|
v-for="thHeader in $t('CANNED_MGMT.LIST.TABLE_HEADER')"
|
||||||
|
:key="thHeader"
|
||||||
|
>
|
||||||
{{ thHeader }}
|
{{ thHeader }}
|
||||||
</th>
|
</th>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr v-for="(cannedItem, index) in cannedResponseList">
|
<tr
|
||||||
|
v-for="(cannedItem, index) in cannedResponseList"
|
||||||
|
:key="cannedItem.short_code"
|
||||||
|
>
|
||||||
<!-- Short Code -->
|
<!-- Short Code -->
|
||||||
<td>{{ cannedItem.short_code }}</td>
|
<td>{{ cannedItem.short_code }}</td>
|
||||||
<!-- Content -->
|
<!-- Content -->
|
||||||
<td>{{ cannedItem.content }}</td>
|
<td>{{ cannedItem.content }}</td>
|
||||||
<!-- Action Buttons -->
|
<!-- Action Buttons -->
|
||||||
<td class="button-wrapper">
|
<td class="button-wrapper">
|
||||||
<div @click="openEditPopup(cannedItem)" >
|
<div @click="openEditPopup(cannedItem)">
|
||||||
<woot-submit-button
|
<woot-submit-button
|
||||||
:button-text="$t('CANNED_MGMT.EDIT.BUTTON_TEXT')"
|
:button-text="$t('CANNED_MGMT.EDIT.BUTTON_TEXT')"
|
||||||
icon-class="ion-edit"
|
icon-class="ion-edit"
|
||||||
|
@ -57,17 +72,17 @@
|
||||||
</div>
|
</div>
|
||||||
<!-- Add Agent -->
|
<!-- Add Agent -->
|
||||||
<woot-modal :show.sync="showAddPopup" :on-close="hideAddPopup">
|
<woot-modal :show.sync="showAddPopup" :on-close="hideAddPopup">
|
||||||
<add-canned :on-close="hideAddPopup"/>
|
<add-canned :on-close="hideAddPopup" />
|
||||||
</woot-modal>
|
</woot-modal>
|
||||||
|
|
||||||
<!-- Edit Canned Response -->
|
<!-- Edit Canned Response -->
|
||||||
<woot-modal :show.sync="showEditPopup" :on-close="hideEditPopup">
|
<woot-modal :show.sync="showEditPopup" :on-close="hideEditPopup">
|
||||||
<edit-canned
|
<edit-canned
|
||||||
|
v-if="showEditPopup"
|
||||||
|
:id="selectedResponse.id"
|
||||||
:edshort-code="selectedResponse.short_code"
|
:edshort-code="selectedResponse.short_code"
|
||||||
:edcontent="selectedResponse.content"
|
:edcontent="selectedResponse.content"
|
||||||
:id="selectedResponse.id"
|
|
||||||
:on-close="hideEditPopup"
|
:on-close="hideEditPopup"
|
||||||
v-if="showEditPopup"
|
|
||||||
/>
|
/>
|
||||||
</woot-modal>
|
</woot-modal>
|
||||||
|
|
||||||
|
@ -82,13 +97,11 @@
|
||||||
:reject-text="deleteRejectText"
|
:reject-text="deleteRejectText"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
/* global bus */
|
/* global bus */
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
|
|
||||||
import PageHeader from '../SettingsSubPageHeader';
|
|
||||||
import AddCanned from './AddCanned';
|
import AddCanned from './AddCanned';
|
||||||
import EditCanned from './EditCanned';
|
import EditCanned from './EditCanned';
|
||||||
import DeleteCanned from './DeleteCanned';
|
import DeleteCanned from './DeleteCanned';
|
||||||
|
@ -96,7 +109,6 @@ import DeleteCanned from './DeleteCanned';
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
AddCanned,
|
AddCanned,
|
||||||
PageHeader,
|
|
||||||
EditCanned,
|
EditCanned,
|
||||||
DeleteCanned,
|
DeleteCanned,
|
||||||
},
|
},
|
||||||
|
@ -119,13 +131,19 @@ export default {
|
||||||
}),
|
}),
|
||||||
// Delete Modal
|
// Delete Modal
|
||||||
deleteConfirmText() {
|
deleteConfirmText() {
|
||||||
return `${this.$t('CANNED_MGMT.DELETE.CONFIRM.YES')} ${this.selectedResponse.short_code}`;
|
return `${this.$t('CANNED_MGMT.DELETE.CONFIRM.YES')} ${
|
||||||
|
this.selectedResponse.short_code
|
||||||
|
}`;
|
||||||
},
|
},
|
||||||
deleteRejectText() {
|
deleteRejectText() {
|
||||||
return `${this.$t('CANNED_MGMT.DELETE.CONFIRM.NO')} ${this.selectedResponse.short_code}`;
|
return `${this.$t('CANNED_MGMT.DELETE.CONFIRM.NO')} ${
|
||||||
|
this.selectedResponse.short_code
|
||||||
|
}`;
|
||||||
},
|
},
|
||||||
deleteMessage() {
|
deleteMessage() {
|
||||||
return `${this.$t('CANNED_MGMT.DELETE.CONFIRM.MESSAGE')} ${this.selectedResponse.short_code} ?`;
|
return `${this.$t('CANNED_MGMT.DELETE.CONFIRM.MESSAGE')} ${
|
||||||
|
this.selectedResponse.short_code
|
||||||
|
} ?`;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
@ -136,7 +154,7 @@ export default {
|
||||||
showAlert(message) {
|
showAlert(message) {
|
||||||
// Reset loading, current selected agent
|
// Reset loading, current selected agent
|
||||||
this.loading[this.selectedResponse.id] = false;
|
this.loading[this.selectedResponse.id] = false;
|
||||||
this.selectedResponse = { };
|
this.selectedResponse = {};
|
||||||
// Show message
|
// Show message
|
||||||
this.cannedResponseAPI.message = message;
|
this.cannedResponseAPI.message = message;
|
||||||
bus.$emit('newToastMessage', message);
|
bus.$emit('newToastMessage', message);
|
||||||
|
@ -173,11 +191,14 @@ export default {
|
||||||
this.deleteCannedResponse(this.selectedResponse.id);
|
this.deleteCannedResponse(this.selectedResponse.id);
|
||||||
},
|
},
|
||||||
deleteCannedResponse(id) {
|
deleteCannedResponse(id) {
|
||||||
this.$store.dispatch('deleteCannedResponse', {
|
this.$store
|
||||||
|
.dispatch('deleteCannedResponse', {
|
||||||
id,
|
id,
|
||||||
}).then(() => {
|
})
|
||||||
|
.then(() => {
|
||||||
this.showAlert(this.$t('CANNED_MGMT.DELETE.API.SUCCESS_MESSAGE'));
|
this.showAlert(this.$t('CANNED_MGMT.DELETE.API.SUCCESS_MESSAGE'));
|
||||||
}).catch(() => {
|
})
|
||||||
|
.catch(() => {
|
||||||
this.showAlert(this.$t('CANNED_MGMT.DELETE.API.ERROR_MESSAGE'));
|
this.showAlert(this.$t('CANNED_MGMT.DELETE.API.ERROR_MESSAGE'));
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import SettingsContent from '../Wrapper';
|
import SettingsContent from '../Wrapper';
|
||||||
import CannedHome from './Index';
|
import CannedHome from './Index';
|
||||||
|
import { frontendURL } from '../../../../helper/URLHelper';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
routes: [
|
routes: [
|
||||||
{
|
{
|
||||||
path: '/u/settings/canned-response',
|
path: frontendURL('settings/canned-response'),
|
||||||
component: SettingsContent,
|
component: SettingsContent,
|
||||||
props: {
|
props: {
|
||||||
headerTitle: 'CANNED_MGMT.HEADER',
|
headerTitle: 'CANNED_MGMT.HEADER',
|
||||||
|
|
|
@ -5,15 +5,24 @@
|
||||||
<div class="small-8 columns">
|
<div class="small-8 columns">
|
||||||
<p v-if="!inboxesList.length" class="no-items-error-message">
|
<p v-if="!inboxesList.length" class="no-items-error-message">
|
||||||
{{ $t('INBOX_MGMT.LIST.404') }}
|
{{ $t('INBOX_MGMT.LIST.404') }}
|
||||||
<router-link to="/u/settings/inboxes/new" v-if="isAdmin()">
|
<router-link
|
||||||
|
v-if="isAdmin()"
|
||||||
|
:to="frontendURL('settings/inboxes/new')"
|
||||||
|
>
|
||||||
{{ $t('SETTINGS.INBOXES.NEW_INBOX') }}
|
{{ $t('SETTINGS.INBOXES.NEW_INBOX') }}
|
||||||
</router-link>
|
</router-link>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<table class="woot-table" v-if="inboxesList.length">
|
<table v-if="inboxesList.length" class="woot-table">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr v-for="item in inboxesList">
|
<tr v-for="item in inboxesList" :key="item.label">
|
||||||
<td><img class="woot-thumbnail" :src="item.avatarUrl" alt="No Page Image"/></td>
|
<td>
|
||||||
|
<img
|
||||||
|
class="woot-thumbnail"
|
||||||
|
:src="item.avatarUrl"
|
||||||
|
alt="No Page Image"
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
<!-- Short Code -->
|
<!-- Short Code -->
|
||||||
<td>
|
<td>
|
||||||
<span class="agent-name">{{ item.label }}</span>
|
<span class="agent-name">{{ item.label }}</span>
|
||||||
|
@ -23,7 +32,7 @@
|
||||||
<!-- Action Buttons -->
|
<!-- Action Buttons -->
|
||||||
<td>
|
<td>
|
||||||
<div class="button-wrapper">
|
<div class="button-wrapper">
|
||||||
<div @click="openSettings(item)" v-if="isAdmin()">
|
<div v-if="isAdmin()" @click="openSettings(item)">
|
||||||
<woot-submit-button
|
<woot-submit-button
|
||||||
:button-text="$t('INBOX_MGMT.SETTINGS')"
|
:button-text="$t('INBOX_MGMT.SETTINGS')"
|
||||||
icon-class="ion-gear-b"
|
icon-class="ion-gear-b"
|
||||||
|
@ -37,7 +46,7 @@
|
||||||
button-class="link hollow grey-btn"
|
button-class="link hollow grey-btn"
|
||||||
/>
|
/>
|
||||||
</div> -->
|
</div> -->
|
||||||
<div @click="openDelete(item)" v-if="isAdmin()">
|
<div v-if="isAdmin()" @click="openDelete(item)">
|
||||||
<woot-submit-button
|
<woot-submit-button
|
||||||
:button-text="$t('INBOX_MGMT.DELETE.BUTTON_TEXT')"
|
:button-text="$t('INBOX_MGMT.DELETE.BUTTON_TEXT')"
|
||||||
:loading="loading[item.id]"
|
:loading="loading[item.id]"
|
||||||
|
@ -57,10 +66,10 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<settings
|
<settings
|
||||||
|
v-if="showSettings"
|
||||||
:show.sync="showSettings"
|
:show.sync="showSettings"
|
||||||
:on-close="closeSettings"
|
:on-close="closeSettings"
|
||||||
:inbox="selectedInbox"
|
:inbox="selectedInbox"
|
||||||
v-if="showSettings"
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<delete-inbox
|
<delete-inbox
|
||||||
|
@ -78,14 +87,13 @@
|
||||||
/* global bus */
|
/* global bus */
|
||||||
|
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import InboxListItem from '../../../../components/widgets/InboxListItem';
|
|
||||||
import Settings from './Settings';
|
import Settings from './Settings';
|
||||||
import DeleteInbox from './DeleteInbox';
|
import DeleteInbox from './DeleteInbox';
|
||||||
import adminMixin from '../../../../mixins/isAdmin';
|
import adminMixin from '../../../../mixins/isAdmin';
|
||||||
|
import { frontendURL } from '../../../../helper/URLHelper';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
InboxListItem,
|
|
||||||
Settings,
|
Settings,
|
||||||
DeleteInbox,
|
DeleteInbox,
|
||||||
},
|
},
|
||||||
|
@ -104,13 +112,19 @@ export default {
|
||||||
}),
|
}),
|
||||||
// Delete Modal
|
// Delete Modal
|
||||||
deleteConfirmText() {
|
deleteConfirmText() {
|
||||||
return `${this.$t('INBOX_MGMT.DELETE.CONFIRM.YES')} ${this.selectedInbox.label}`;
|
return `${this.$t('INBOX_MGMT.DELETE.CONFIRM.YES')} ${
|
||||||
|
this.selectedInbox.label
|
||||||
|
}`;
|
||||||
},
|
},
|
||||||
deleteRejectText() {
|
deleteRejectText() {
|
||||||
return `${this.$t('INBOX_MGMT.DELETE.CONFIRM.NO')} ${this.selectedInbox.label}`;
|
return `${this.$t('INBOX_MGMT.DELETE.CONFIRM.NO')} ${
|
||||||
|
this.selectedInbox.label
|
||||||
|
}`;
|
||||||
},
|
},
|
||||||
deleteMessage() {
|
deleteMessage() {
|
||||||
return `${this.$t('INBOX_MGMT.DELETE.CONFIRM.MESSAGE')} ${this.selectedInbox.label} ?`;
|
return `${this.$t('INBOX_MGMT.DELETE.CONFIRM.MESSAGE')} ${
|
||||||
|
this.selectedInbox.label
|
||||||
|
} ?`;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -123,9 +137,20 @@ export default {
|
||||||
this.selectedInbox = {};
|
this.selectedInbox = {};
|
||||||
},
|
},
|
||||||
deleteInbox({ channel_id }) {
|
deleteInbox({ channel_id }) {
|
||||||
this.$store.dispatch('deleteInbox', channel_id)
|
this.$store
|
||||||
.then(() => bus.$emit('newToastMessage', this.$t('INBOX_MGMT.DELETE.API.SUCCESS_MESSAGE')))
|
.dispatch('deleteInbox', channel_id)
|
||||||
.catch(() => bus.$emit('newToastMessage', this.$t('INBOX_MGMT.DELETE.API.ERROR_MESSAGE')));
|
.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() {
|
confirmDeletion() {
|
||||||
|
@ -140,6 +165,7 @@ export default {
|
||||||
this.showDeletePopup = false;
|
this.showDeletePopup = false;
|
||||||
this.selectedInbox = {};
|
this.selectedInbox = {};
|
||||||
},
|
},
|
||||||
|
frontendURL,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -6,11 +6,12 @@ import ChannelList from './ChannelList';
|
||||||
import channelFactory from './channel-factory';
|
import channelFactory from './channel-factory';
|
||||||
import AddAgents from './AddAgents';
|
import AddAgents from './AddAgents';
|
||||||
import FinishSetup from './FinishSetup';
|
import FinishSetup from './FinishSetup';
|
||||||
|
import { frontendURL } from '../../../../helper/URLHelper';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
routes: [
|
routes: [
|
||||||
{
|
{
|
||||||
path: '/u/settings/inboxes',
|
path: frontendURL('settings/inboxes'),
|
||||||
component: SettingsContent,
|
component: SettingsContent,
|
||||||
props: {
|
props: {
|
||||||
headerTitle: 'INBOX_MGMT.HEADER',
|
headerTitle: 'INBOX_MGMT.HEADER',
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import Index from './Index';
|
import Index from './Index';
|
||||||
import SettingsContent from '../Wrapper';
|
import SettingsContent from '../Wrapper';
|
||||||
|
import { frontendURL } from '../../../../helper/URLHelper';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
routes: [
|
routes: [
|
||||||
{
|
{
|
||||||
path: '/u/reports',
|
path: frontendURL('reports'),
|
||||||
component: SettingsContent,
|
component: SettingsContent,
|
||||||
props: {
|
props: {
|
||||||
headerTitle: 'REPORT.HEADER',
|
headerTitle: 'REPORT.HEADER',
|
||||||
|
|
|
@ -4,18 +4,19 @@ import canned from './canned/canned.routes';
|
||||||
import reports from './reports/reports.routes';
|
import reports from './reports/reports.routes';
|
||||||
import billing from './billing/billing.routes';
|
import billing from './billing/billing.routes';
|
||||||
import Auth from '../../../api/auth';
|
import Auth from '../../../api/auth';
|
||||||
|
import { frontendURL } from '../../../helper/URLHelper';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
routes: [
|
routes: [
|
||||||
{
|
{
|
||||||
path: '/u/settings',
|
path: frontendURL('settings'),
|
||||||
name: 'settings_home',
|
name: 'settings_home',
|
||||||
roles: ['administrator', 'agent'],
|
roles: ['administrator', 'agent'],
|
||||||
redirect: () => {
|
redirect: () => {
|
||||||
if (Auth.isAdmin()) {
|
if (Auth.isAdmin()) {
|
||||||
return '/u/settings/agents/';
|
return frontendURL('settings/agents');
|
||||||
}
|
}
|
||||||
return '/u/settings/canned-response';
|
return frontendURL('settings/canned-response');
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
...inbox.routes,
|
...inbox.routes,
|
||||||
|
|
|
@ -5,6 +5,7 @@ import auth from '../api/auth';
|
||||||
import login from './login/login.routes';
|
import login from './login/login.routes';
|
||||||
import dashboard from './dashboard/dashboard.routes';
|
import dashboard from './dashboard/dashboard.routes';
|
||||||
import authRoute from './auth/auth.routes';
|
import authRoute from './auth/auth.routes';
|
||||||
|
import { frontendURL } from '../helper/URLHelper';
|
||||||
|
|
||||||
/* Vue Routes */
|
/* Vue Routes */
|
||||||
const routes = [
|
const routes = [
|
||||||
|
@ -13,7 +14,7 @@ const routes = [
|
||||||
...authRoute.routes,
|
...authRoute.routes,
|
||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
redirect: '/u/dashboard',
|
redirect: frontendURL('dashboard'),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -66,15 +67,15 @@ const redirectUser = (to, from, next) => {
|
||||||
const isAccessible =
|
const isAccessible =
|
||||||
window.roleWiseRoutes[currentUser.role].indexOf(to.name) > -1;
|
window.roleWiseRoutes[currentUser.role].indexOf(to.name) > -1;
|
||||||
if (!isAccessible) {
|
if (!isAccessible) {
|
||||||
return next('/u/dashboard');
|
return next(frontendURL('dashboard'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If unprotected and loggedIn -> redirect
|
// If unprotected and loggedIn -> redirect
|
||||||
if (unProtectedRoutes.indexOf(to.name) !== -1 && isLoggedIn) {
|
if (unProtectedRoutes.indexOf(to.name) !== -1 && isLoggedIn) {
|
||||||
return next('/u/dashboard');
|
return next(frontendURL('dashboard'));
|
||||||
}
|
}
|
||||||
if (unProtectedRoutes.indexOf(to.name) === -1 && !isLoggedIn) {
|
if (unProtectedRoutes.indexOf(to.name) === -1 && !isLoggedIn) {
|
||||||
return next('/u/login');
|
return next(frontendURL('login'));
|
||||||
}
|
}
|
||||||
return next();
|
return next();
|
||||||
};
|
};
|
||||||
|
@ -82,7 +83,7 @@ const redirectUser = (to, from, next) => {
|
||||||
// protecting routes
|
// protecting routes
|
||||||
router.beforeEach((to, from, next) => {
|
router.beforeEach((to, from, next) => {
|
||||||
if (!to.name) {
|
if (!to.name) {
|
||||||
return next('/u/dashboard');
|
return next(frontendURL('dashboard'));
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirectUser(to, from, next);
|
return redirectUser(to, from, next);
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import Login from './Login';
|
import Login from './Login';
|
||||||
|
import { frontendURL } from '../../helper/URLHelper';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
routes: [
|
routes: [
|
||||||
{
|
{
|
||||||
path: '/u/login',
|
path: frontendURL('login'),
|
||||||
name: 'login',
|
name: 'login',
|
||||||
component: Login,
|
component: Login,
|
||||||
},
|
},
|
||||||
|
|
|
@ -7,6 +7,7 @@ import defaultState from '../../i18n/default-sidebar';
|
||||||
import * as types from '../mutation-types';
|
import * as types from '../mutation-types';
|
||||||
import Account from '../../api/account';
|
import Account from '../../api/account';
|
||||||
import ChannelApi from '../../api/channels';
|
import ChannelApi from '../../api/channels';
|
||||||
|
import { frontendURL } from '../../helper/URLHelper';
|
||||||
|
|
||||||
const state = defaultState;
|
const state = defaultState;
|
||||||
// inboxes fetch flag
|
// inboxes fetch flag
|
||||||
|
@ -135,7 +136,7 @@ const mutations = {
|
||||||
payload = payload.map(item => ({
|
payload = payload.map(item => ({
|
||||||
channel_id: item.id,
|
channel_id: item.id,
|
||||||
label: item.name,
|
label: item.name,
|
||||||
toState: `/u/inbox/${item.id}`,
|
toState: frontendURL(`inbox/${item.id}`),
|
||||||
channelType: item.channelType,
|
channelType: item.channelType,
|
||||||
avatarUrl: item.avatar_url,
|
avatarUrl: item.avatar_url,
|
||||||
pageId: item.page_id,
|
pageId: item.page_id,
|
||||||
|
@ -154,7 +155,7 @@ const mutations = {
|
||||||
menuItems.inbox.children.push({
|
menuItems.inbox.children.push({
|
||||||
channel_id: data.id,
|
channel_id: data.id,
|
||||||
label: data.name,
|
label: data.name,
|
||||||
toState: `/u/inbox/${data.id}`,
|
toState: frontendURL(`inbox/${data.id}`),
|
||||||
channelType: data.channelType,
|
channelType: data.channelType,
|
||||||
avatarUrl: data.avatar_url === undefined ? null : data.avatar_url,
|
avatarUrl: data.avatar_url === undefined ? null : data.avatar_url,
|
||||||
pageId: data.page_id,
|
pageId: data.page_id,
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
class ApplicationMailer < ActionMailer::Base
|
class ApplicationMailer < ActionMailer::Base
|
||||||
default from: 'accounts@chatwoot.com'
|
default from: 'accounts@chatwoot.com'
|
||||||
layout 'mailer'
|
layout 'mailer'
|
||||||
|
|
||||||
|
# helpers
|
||||||
|
helper :frontend_urls
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,4 +2,4 @@
|
||||||
|
|
||||||
<p>You can confirm your account email through the link below:</p>
|
<p>You can confirm your account email through the link below:</p>
|
||||||
|
|
||||||
<p><%= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token) %></p>
|
<p><%= link_to 'Confirm my account', frontend_url('auth/confirmation', confirmation_token: @token) %></p>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
<p>Someone has requested a link to change your password. You can do this through the link below.</p>
|
<p>Someone has requested a link to change your password. You can do this through the link below.</p>
|
||||||
|
|
||||||
<p><%= link_to 'Change my password', edit_password_url(@resource, reset_password_token: @token) %></p>
|
<p><%= link_to 'Change my password', frontend_url('auth/password/edit', reset_password_token: @token) %></p>
|
||||||
|
|
||||||
<p>If you didn't request this, please ignore this email.</p>
|
<p>If you didn't request this, please ignore this email.</p>
|
||||||
<p>Your password won't change until you access the link above and create a new one.</p>
|
<p>Your password won't change until you access the link above and create a new one.</p>
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>Chatwoot</title>
|
|
||||||
<%= csrf_meta_tags %>
|
|
||||||
|
|
||||||
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
|
|
||||||
<%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
|
|
||||||
</head>
|
|
||||||
<% if current_user %>
|
|
||||||
<body data-account-id="<%= current_user.account_id %>" >
|
|
||||||
<% end %>
|
|
||||||
<% if user_signed_in? %>
|
|
||||||
<li>
|
|
||||||
<%= link_to('Logout', destroy_user_session_path, :method => :delete) %>
|
|
||||||
</li>
|
|
||||||
<% else %>
|
|
||||||
<li>
|
|
||||||
<%= link_to('Login', new_user_session_path) %>
|
|
||||||
</li>
|
|
||||||
<% end %>
|
|
||||||
<%= yield %>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -20,6 +20,7 @@ Rails.application.configure do
|
||||||
config.public_file_server.headers = {
|
config.public_file_server.headers = {
|
||||||
'Cache-Control' => "public, max-age=#{1.hour.to_i}"
|
'Cache-Control' => "public, max-age=#{1.hour.to_i}"
|
||||||
}
|
}
|
||||||
|
config.action_mailer.default_url_options = { :host => 'localhost', port: 3000 }
|
||||||
|
|
||||||
# Show full error reports and disable caching.
|
# Show full error reports and disable caching.
|
||||||
config.consider_all_requests_local = true
|
config.consider_all_requests_local = true
|
||||||
|
|
|
@ -5,11 +5,13 @@ Rails.application.routes.draw do
|
||||||
mount_devise_token_auth_for 'User', at: 'auth', controllers: { confirmations: 'confirmations', passwords: 'passwords',
|
mount_devise_token_auth_for 'User', at: 'auth', controllers: { confirmations: 'confirmations', passwords: 'passwords',
|
||||||
sessions: 'sessions' }, via: [:get, :post]
|
sessions: 'sessions' }, via: [:get, :post]
|
||||||
|
|
||||||
get "/u", to: "dashboard#index"
|
|
||||||
get "/u/*params", to: "dashboard#index"
|
|
||||||
|
|
||||||
get '/', to: redirect('/u/login')
|
root :to => "dashboard#index"
|
||||||
match '/status', to: 'home#status', via: [:get] #for elb checks
|
|
||||||
|
get "/app", to: "dashboard#index"
|
||||||
|
get "/app/*params", to: "dashboard#index"
|
||||||
|
|
||||||
|
match '/status', to: 'home#status', via: [:get]
|
||||||
|
|
||||||
namespace :api do
|
namespace :api do
|
||||||
namespace :v1 do
|
namespace :v1 do
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
"md5": "~2.2.1",
|
"md5": "~2.2.1",
|
||||||
"moment": "~2.19.3",
|
"moment": "~2.19.3",
|
||||||
"pusher-js": "~4.0.0",
|
"pusher-js": "~4.0.0",
|
||||||
|
"query-string": "5",
|
||||||
"spinkit": "~1.2.5",
|
"spinkit": "~1.2.5",
|
||||||
"sweet-modal-vue": "~1.0.3",
|
"sweet-modal-vue": "~1.0.3",
|
||||||
"tween.js": "~16.6.0",
|
"tween.js": "~16.6.0",
|
||||||
|
|
0
test/fixtures/.keep → spec/fixtures/.keep
vendored
0
test/fixtures/.keep → spec/fixtures/.keep
vendored
17
spec/helpers/frontend_urls_helper_spec.rb
Normal file
17
spec/helpers/frontend_urls_helper_spec.rb
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
require "rails_helper"
|
||||||
|
|
||||||
|
describe FrontendUrlsHelper, type: :helper do
|
||||||
|
describe "#frontend_url" do
|
||||||
|
context "without query params" do
|
||||||
|
it "creates path correctly" do
|
||||||
|
expect(helper.frontend_url('dashboard')).to eq "http://test.host/app/dashboard"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "with query params" do
|
||||||
|
it "creates path correctly" do
|
||||||
|
expect(helper.frontend_url('dashboard', p1: 'p1', p2: 'p2')).to eq "http://test.host/app/dashboard?p1=p1&p2=p2"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
60
spec/rails_helper.rb
Normal file
60
spec/rails_helper.rb
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
ENV['RAILS_ENV'] ||= 'test'
|
||||||
|
require File.expand_path('../../config/environment', __FILE__)
|
||||||
|
# Prevent database truncation if the environment is production
|
||||||
|
abort("The Rails environment is running in production mode!") if Rails.env.production?
|
||||||
|
require 'rspec/rails'
|
||||||
|
# Add additional requires below this line. Rails is not loaded until this point!
|
||||||
|
|
||||||
|
# Requires supporting ruby files with custom matchers and macros, etc, in
|
||||||
|
# spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are
|
||||||
|
# run as spec files by default. This means that files in spec/support that end
|
||||||
|
# in _spec.rb will both be required and run as specs, causing the specs to be
|
||||||
|
# run twice. It is recommended that you do not name files matching this glob to
|
||||||
|
# end with _spec.rb. You can configure this pattern with the --pattern
|
||||||
|
# option on the command line or in ~/.rspec, .rspec or `.rspec-local`.
|
||||||
|
#
|
||||||
|
# The following line is provided for convenience purposes. It has the downside
|
||||||
|
# of increasing the boot-up time by auto-requiring all files in the support
|
||||||
|
# directory. Alternatively, in the individual `*_spec.rb` files, manually
|
||||||
|
# require only the support files necessary.
|
||||||
|
#
|
||||||
|
# Dir[Rails.root.join('spec', 'support', '**', '*.rb')].each { |f| require f }
|
||||||
|
|
||||||
|
# Checks for pending migrations and applies them before tests are run.
|
||||||
|
# If you are not using ActiveRecord, you can remove these lines.
|
||||||
|
begin
|
||||||
|
ActiveRecord::Migration.maintain_test_schema!
|
||||||
|
rescue ActiveRecord::PendingMigrationError => e
|
||||||
|
puts e.to_s.strip
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
RSpec.configure do |config|
|
||||||
|
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
|
||||||
|
config.fixture_path = "#{::Rails.root}/spec/fixtures"
|
||||||
|
|
||||||
|
# If you're not using ActiveRecord, or you'd prefer not to run each of your
|
||||||
|
# examples within a transaction, remove the following line or assign false
|
||||||
|
# instead of true.
|
||||||
|
config.use_transactional_fixtures = true
|
||||||
|
|
||||||
|
# RSpec Rails can automatically mix in different behaviours to your tests
|
||||||
|
# based on their file location, for example enabling you to call `get` and
|
||||||
|
# `post` in specs under `spec/controllers`.
|
||||||
|
#
|
||||||
|
# You can disable this behaviour by removing the line below, and instead
|
||||||
|
# explicitly tag your specs with their type, e.g.:
|
||||||
|
#
|
||||||
|
# RSpec.describe UsersController, :type => :controller do
|
||||||
|
# # ...
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# The different available types are documented in the features, such as in
|
||||||
|
# https://relishapp.com/rspec/rspec-rails/docs
|
||||||
|
config.infer_spec_type_from_file_location!
|
||||||
|
|
||||||
|
# Filter lines from Rails gems in backtraces.
|
||||||
|
config.filter_rails_from_backtrace!
|
||||||
|
# arbitrary gems may also be filtered via:
|
||||||
|
# config.filter_gems_from_backtrace("gem name")
|
||||||
|
end
|
15
spec/spec_helper.rb
Normal file
15
spec/spec_helper.rb
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
|
||||||
|
RSpec.configure do |config|
|
||||||
|
config.expect_with :rspec do |expectations|
|
||||||
|
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
||||||
|
end
|
||||||
|
|
||||||
|
config.mock_with :rspec do |mocks|
|
||||||
|
mocks.verify_partial_doubles = true
|
||||||
|
end
|
||||||
|
|
||||||
|
config.shared_context_metadata_behavior = :apply_to_host_groups
|
||||||
|
|
||||||
|
# config.include Rails.application.routes.url_helpers
|
||||||
|
|
||||||
|
end
|
|
@ -1,7 +0,0 @@
|
||||||
require 'test_helper'
|
|
||||||
|
|
||||||
class Api::BaseControllerTest < ActionDispatch::IntegrationTest
|
|
||||||
# test "the truth" do
|
|
||||||
# assert true
|
|
||||||
# end
|
|
||||||
end
|
|
|
@ -1,7 +0,0 @@
|
||||||
require 'test_helper'
|
|
||||||
|
|
||||||
class Api::V1::AgentsControllerTest < ActionDispatch::IntegrationTest
|
|
||||||
# test "the truth" do
|
|
||||||
# assert true
|
|
||||||
# end
|
|
||||||
end
|
|
|
@ -1,7 +0,0 @@
|
||||||
require 'test_helper'
|
|
||||||
|
|
||||||
class Api::V1::CannedResponsesControllerTest < ActionDispatch::IntegrationTest
|
|
||||||
# test "the truth" do
|
|
||||||
# assert true
|
|
||||||
# end
|
|
||||||
end
|
|
|
@ -1,7 +0,0 @@
|
||||||
require 'test_helper'
|
|
||||||
|
|
||||||
class Api::V1::ConversationsControllerTest < ActionDispatch::IntegrationTest
|
|
||||||
# test "the truth" do
|
|
||||||
# assert true
|
|
||||||
# end
|
|
||||||
end
|
|
|
@ -1,7 +0,0 @@
|
||||||
require 'test_helper'
|
|
||||||
|
|
||||||
class Api::V1::ReportsControllerTest < ActionDispatch::IntegrationTest
|
|
||||||
# test "the truth" do
|
|
||||||
# assert true
|
|
||||||
# end
|
|
||||||
end
|
|
|
@ -1,7 +0,0 @@
|
||||||
require 'test_helper'
|
|
||||||
|
|
||||||
class Api::V1::SubscriptionsControllerTest < ActionDispatch::IntegrationTest
|
|
||||||
# test "the truth" do
|
|
||||||
# assert true
|
|
||||||
# end
|
|
||||||
end
|
|
|
@ -1,7 +0,0 @@
|
||||||
require 'test_helper'
|
|
||||||
|
|
||||||
class Api::V1::WebhooksControllerTest < ActionDispatch::IntegrationTest
|
|
||||||
# test "the truth" do
|
|
||||||
# assert true
|
|
||||||
# end
|
|
||||||
end
|
|
|
@ -1,7 +0,0 @@
|
||||||
require 'test_helper'
|
|
||||||
|
|
||||||
class Api::V1::Widget::MessagesControllerTest < ActionDispatch::IntegrationTest
|
|
||||||
# test "the truth" do
|
|
||||||
# assert true
|
|
||||||
# end
|
|
||||||
end
|
|
|
@ -1,7 +0,0 @@
|
||||||
require 'test_helper'
|
|
||||||
|
|
||||||
class HomeControllerTest < ActionDispatch::IntegrationTest
|
|
||||||
# test "the truth" do
|
|
||||||
# assert true
|
|
||||||
# end
|
|
||||||
end
|
|
|
@ -1,7 +0,0 @@
|
||||||
require 'test_helper'
|
|
||||||
|
|
||||||
class AccountTest < ActiveSupport::TestCase
|
|
||||||
# test "the truth" do
|
|
||||||
# assert true
|
|
||||||
# end
|
|
||||||
end
|
|
|
@ -1,7 +0,0 @@
|
||||||
require 'test_helper'
|
|
||||||
|
|
||||||
class AttachmentTest < ActiveSupport::TestCase
|
|
||||||
# test "the truth" do
|
|
||||||
# assert true
|
|
||||||
# end
|
|
||||||
end
|
|
|
@ -1,7 +0,0 @@
|
||||||
require 'test_helper'
|
|
||||||
|
|
||||||
class CannedResponseTest < ActiveSupport::TestCase
|
|
||||||
# test "the truth" do
|
|
||||||
# assert true
|
|
||||||
# end
|
|
||||||
end
|
|
|
@ -1,7 +0,0 @@
|
||||||
require 'test_helper'
|
|
||||||
|
|
||||||
class ChannelTest < ActiveSupport::TestCase
|
|
||||||
# test "the truth" do
|
|
||||||
# assert true
|
|
||||||
# end
|
|
||||||
end
|
|
|
@ -1,7 +0,0 @@
|
||||||
require 'test_helper'
|
|
||||||
|
|
||||||
class ContactTest < ActiveSupport::TestCase
|
|
||||||
# test "the truth" do
|
|
||||||
# assert true
|
|
||||||
# end
|
|
||||||
end
|
|
|
@ -1,7 +0,0 @@
|
||||||
require 'test_helper'
|
|
||||||
|
|
||||||
class ConversationTest < ActiveSupport::TestCase
|
|
||||||
# test "the truth" do
|
|
||||||
# assert true
|
|
||||||
# end
|
|
||||||
end
|
|
|
@ -1,7 +0,0 @@
|
||||||
require 'test_helper'
|
|
||||||
|
|
||||||
class FacebookPageTest < ActiveSupport::TestCase
|
|
||||||
# test "the truth" do
|
|
||||||
# assert true
|
|
||||||
# end
|
|
||||||
end
|
|
|
@ -1,7 +0,0 @@
|
||||||
require 'test_helper'
|
|
||||||
|
|
||||||
class InboxMemberTest < ActiveSupport::TestCase
|
|
||||||
# test "the truth" do
|
|
||||||
# assert true
|
|
||||||
# end
|
|
||||||
end
|
|
|
@ -1,7 +0,0 @@
|
||||||
require 'test_helper'
|
|
||||||
|
|
||||||
class InboxTest < ActiveSupport::TestCase
|
|
||||||
# test "the truth" do
|
|
||||||
# assert true
|
|
||||||
# end
|
|
||||||
end
|
|
|
@ -1,7 +0,0 @@
|
||||||
require 'test_helper'
|
|
||||||
|
|
||||||
class MessageTest < ActiveSupport::TestCase
|
|
||||||
# test "the truth" do
|
|
||||||
# assert true
|
|
||||||
# end
|
|
||||||
end
|
|
|
@ -1,7 +0,0 @@
|
||||||
require 'test_helper'
|
|
||||||
|
|
||||||
class SubscriptionTest < ActiveSupport::TestCase
|
|
||||||
# test "the truth" do
|
|
||||||
# assert true
|
|
||||||
# end
|
|
||||||
end
|
|
|
@ -1,7 +0,0 @@
|
||||||
require 'test_helper'
|
|
||||||
|
|
||||||
class TelegramBotTest < ActiveSupport::TestCase
|
|
||||||
# test "the truth" do
|
|
||||||
# assert true
|
|
||||||
# end
|
|
||||||
end
|
|
|
@ -1,7 +0,0 @@
|
||||||
require 'test_helper'
|
|
||||||
|
|
||||||
class UserTest < ActiveSupport::TestCase
|
|
||||||
# test "the truth" do
|
|
||||||
# assert true
|
|
||||||
# end
|
|
||||||
end
|
|
|
@ -8422,6 +8422,15 @@ qs@~6.5.2:
|
||||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
|
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
|
||||||
integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
|
integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
|
||||||
|
|
||||||
|
query-string@5:
|
||||||
|
version "5.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/query-string/-/query-string-5.1.1.tgz#a78c012b71c17e05f2e3fa2319dd330682efb3cb"
|
||||||
|
integrity sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==
|
||||||
|
dependencies:
|
||||||
|
decode-uri-component "^0.2.0"
|
||||||
|
object-assign "^4.1.0"
|
||||||
|
strict-uri-encode "^1.0.0"
|
||||||
|
|
||||||
query-string@^4.1.0:
|
query-string@^4.1.0:
|
||||||
version "4.3.4"
|
version "4.3.4"
|
||||||
resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.3.4.tgz#bbb693b9ca915c232515b228b1a02b609043dbeb"
|
resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.3.4.tgz#bbb693b9ca915c232515b228b1a02b609043dbeb"
|
||||||
|
|
Loading…
Reference in a new issue