Merge branch 'release/3.11.0'
This commit is contained in:
commit
8ea412bc85
@ -7,7 +7,7 @@ defaults: &defaults
|
|||||||
working_directory: ~/build
|
working_directory: ~/build
|
||||||
docker:
|
docker:
|
||||||
# specify the version you desire here
|
# specify the version you desire here
|
||||||
- image: cimg/ruby:3.2.2-browsers
|
- image: cimg/ruby:3.3.3-browsers
|
||||||
|
|
||||||
# Specify service dependencies here if necessary
|
# Specify service dependencies here if necessary
|
||||||
# CircleCI maintains a library of pre-built images
|
# CircleCI maintains a library of pre-built images
|
||||||
@ -130,10 +130,7 @@ jobs:
|
|||||||
command: |
|
command: |
|
||||||
mkdir -p ~/tmp/test-results/frontend_specs
|
mkdir -p ~/tmp/test-results/frontend_specs
|
||||||
~/tmp/cc-test-reporter before-build
|
~/tmp/cc-test-reporter before-build
|
||||||
TESTFILES=$(circleci tests glob **/specs/*.spec.js | circleci tests split --split-by=timings)
|
yarn test:coverage
|
||||||
yarn test:coverage --profile 10 \
|
|
||||||
--out ~/tmp/test-results/yarn.xml \
|
|
||||||
-- ${TESTFILES}
|
|
||||||
- run:
|
- run:
|
||||||
name: Code Climate Test Coverage
|
name: Code Climate Test Coverage
|
||||||
command: |
|
command: |
|
||||||
|
|||||||
@ -12,7 +12,7 @@ services:
|
|||||||
args:
|
args:
|
||||||
VARIANT: "ubuntu-22.04"
|
VARIANT: "ubuntu-22.04"
|
||||||
NODE_VERSION: "20.9.0"
|
NODE_VERSION: "20.9.0"
|
||||||
RUBY_VERSION: "3.2.2"
|
RUBY_VERSION: "3.3.3"
|
||||||
# On Linux, you may need to update USER_UID and USER_GID below if not your local UID is not 1000.
|
# On Linux, you may need to update USER_UID and USER_GID below if not your local UID is not 1000.
|
||||||
USER_UID: "1000"
|
USER_UID: "1000"
|
||||||
USER_GID: "1000"
|
USER_GID: "1000"
|
||||||
@ -25,7 +25,7 @@ services:
|
|||||||
args:
|
args:
|
||||||
VARIANT: "ubuntu-22.04"
|
VARIANT: "ubuntu-22.04"
|
||||||
NODE_VERSION: "20.9.0"
|
NODE_VERSION: "20.9.0"
|
||||||
RUBY_VERSION: "3.2.2"
|
RUBY_VERSION: "3.3.3"
|
||||||
# On Linux, you may need to update USER_UID and USER_GID below if not your local UID is not 1000.
|
# On Linux, you may need to update USER_UID and USER_GID below if not your local UID is not 1000.
|
||||||
USER_UID: "1000"
|
USER_UID: "1000"
|
||||||
USER_GID: "1000"
|
USER_GID: "1000"
|
||||||
|
|||||||
@ -65,10 +65,10 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
env: {
|
env: {
|
||||||
browser: true,
|
browser: true,
|
||||||
jest: true,
|
|
||||||
node: true,
|
node: true,
|
||||||
},
|
},
|
||||||
globals: {
|
globals: {
|
||||||
bus: true,
|
bus: true,
|
||||||
|
vi: true,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
3.2.2
|
3.3.3
|
||||||
|
|||||||
8
Gemfile
8
Gemfile
@ -1,6 +1,6 @@
|
|||||||
source 'https://rubygems.org'
|
source 'https://rubygems.org'
|
||||||
|
|
||||||
ruby '3.2.2'
|
ruby '3.3.3'
|
||||||
|
|
||||||
##-- base gems for rails --##
|
##-- base gems for rails --##
|
||||||
gem 'rack-cors', '2.0.0', require: 'rack/cors'
|
gem 'rack-cors', '2.0.0', require: 'rack/cors'
|
||||||
@ -111,12 +111,12 @@ gem 'elastic-apm', require: false
|
|||||||
gem 'newrelic_rpm', require: false
|
gem 'newrelic_rpm', require: false
|
||||||
gem 'newrelic-sidekiq-metrics', '>= 1.6.2', require: false
|
gem 'newrelic-sidekiq-metrics', '>= 1.6.2', require: false
|
||||||
gem 'scout_apm', require: false
|
gem 'scout_apm', require: false
|
||||||
gem 'sentry-rails', '>= 5.18.0', require: false
|
gem 'sentry-rails', '>= 5.18.1', require: false
|
||||||
gem 'sentry-ruby', require: false
|
gem 'sentry-ruby', require: false
|
||||||
gem 'sentry-sidekiq', '>= 5.18.0', require: false
|
gem 'sentry-sidekiq', '>= 5.18.1', require: false
|
||||||
|
|
||||||
##-- background job processing --##
|
##-- background job processing --##
|
||||||
gem 'sidekiq', '>= 7.2.4'
|
gem 'sidekiq', '>= 7.3.0'
|
||||||
# We want cron jobs
|
# We want cron jobs
|
||||||
gem 'sidekiq-cron', '>= 1.12.0'
|
gem 'sidekiq-cron', '>= 1.12.0'
|
||||||
|
|
||||||
|
|||||||
59
Gemfile.lock
59
Gemfile.lock
@ -174,7 +174,9 @@ GEM
|
|||||||
crack (0.4.5)
|
crack (0.4.5)
|
||||||
rexml
|
rexml
|
||||||
crass (1.0.6)
|
crass (1.0.6)
|
||||||
csv-safe (3.2.1)
|
csv (3.3.0)
|
||||||
|
csv-safe (3.3.1)
|
||||||
|
csv (~> 3.0)
|
||||||
cypress-on-rails (1.16.0)
|
cypress-on-rails (1.16.0)
|
||||||
rack
|
rack
|
||||||
database_cleaner (2.0.2)
|
database_cleaner (2.0.2)
|
||||||
@ -183,13 +185,16 @@ GEM
|
|||||||
activerecord (>= 5.a)
|
activerecord (>= 5.a)
|
||||||
database_cleaner-core (~> 2.0.0)
|
database_cleaner-core (~> 2.0.0)
|
||||||
database_cleaner-core (2.0.1)
|
database_cleaner-core (2.0.1)
|
||||||
date (3.3.4)
|
datadog-ci (0.8.3)
|
||||||
ddtrace (1.11.1)
|
|
||||||
debase-ruby_core_source (>= 0.10.16, <= 3.2.0)
|
|
||||||
libdatadog (~> 2.0.0.1.0)
|
|
||||||
libddwaf (~> 1.8.2.0.0)
|
|
||||||
msgpack
|
msgpack
|
||||||
debase-ruby_core_source (3.2.0)
|
date (3.3.4)
|
||||||
|
ddtrace (1.23.2)
|
||||||
|
datadog-ci (~> 0.8.1)
|
||||||
|
debase-ruby_core_source (= 3.3.1)
|
||||||
|
libdatadog (~> 7.0.0.1.0)
|
||||||
|
libddwaf (~> 1.14.0.0.0)
|
||||||
|
msgpack
|
||||||
|
debase-ruby_core_source (3.3.1)
|
||||||
debug (1.8.0)
|
debug (1.8.0)
|
||||||
irb (>= 1.5.0)
|
irb (>= 1.5.0)
|
||||||
reline (>= 0.3.1)
|
reline (>= 0.3.1)
|
||||||
@ -416,15 +421,15 @@ GEM
|
|||||||
addressable (~> 2.8)
|
addressable (~> 2.8)
|
||||||
letter_opener (1.8.1)
|
letter_opener (1.8.1)
|
||||||
launchy (>= 2.2, < 3)
|
launchy (>= 2.2, < 3)
|
||||||
libdatadog (2.0.0.1.0)
|
libdatadog (7.0.0.1.0)
|
||||||
libdatadog (2.0.0.1.0-x86_64-linux)
|
libdatadog (7.0.0.1.0-x86_64-linux)
|
||||||
libddwaf (1.8.2.0.0)
|
libddwaf (1.14.0.0.0)
|
||||||
ffi (~> 1.0)
|
ffi (~> 1.0)
|
||||||
libddwaf (1.8.2.0.0-arm64-darwin)
|
libddwaf (1.14.0.0.0-arm64-darwin)
|
||||||
ffi (~> 1.0)
|
ffi (~> 1.0)
|
||||||
libddwaf (1.8.2.0.0-x86_64-darwin)
|
libddwaf (1.14.0.0.0-x86_64-darwin)
|
||||||
ffi (~> 1.0)
|
ffi (~> 1.0)
|
||||||
libddwaf (1.8.2.0.0-x86_64-linux)
|
libddwaf (1.14.0.0.0-x86_64-linux)
|
||||||
ffi (~> 1.0)
|
ffi (~> 1.0)
|
||||||
line-bot-api (1.28.0)
|
line-bot-api (1.28.0)
|
||||||
liquid (5.4.0)
|
liquid (5.4.0)
|
||||||
@ -434,6 +439,7 @@ GEM
|
|||||||
llhttp-ffi (0.4.0)
|
llhttp-ffi (0.4.0)
|
||||||
ffi-compiler (~> 1.0)
|
ffi-compiler (~> 1.0)
|
||||||
rake (~> 13.0)
|
rake (~> 13.0)
|
||||||
|
logger (1.6.0)
|
||||||
lograge (0.14.0)
|
lograge (0.14.0)
|
||||||
actionpack (>= 4)
|
actionpack (>= 4)
|
||||||
activesupport (>= 4)
|
activesupport (>= 4)
|
||||||
@ -460,7 +466,7 @@ GEM
|
|||||||
mini_magick (4.12.0)
|
mini_magick (4.12.0)
|
||||||
mini_mime (1.1.5)
|
mini_mime (1.1.5)
|
||||||
mini_portile2 (2.8.7)
|
mini_portile2 (2.8.7)
|
||||||
minitest (5.24.0)
|
minitest (5.24.1)
|
||||||
mock_redis (0.36.0)
|
mock_redis (0.36.0)
|
||||||
ruby2_keywords
|
ruby2_keywords
|
||||||
msgpack (1.7.0)
|
msgpack (1.7.0)
|
||||||
@ -703,23 +709,24 @@ GEM
|
|||||||
activesupport (>= 4)
|
activesupport (>= 4)
|
||||||
selectize-rails (0.12.6)
|
selectize-rails (0.12.6)
|
||||||
semantic_range (3.0.0)
|
semantic_range (3.0.0)
|
||||||
sentry-rails (5.18.0)
|
sentry-rails (5.18.1)
|
||||||
railties (>= 5.0)
|
railties (>= 5.0)
|
||||||
sentry-ruby (~> 5.18.0)
|
sentry-ruby (~> 5.18.1)
|
||||||
sentry-ruby (5.18.0)
|
sentry-ruby (5.18.1)
|
||||||
bigdecimal
|
bigdecimal
|
||||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||||
sentry-sidekiq (5.18.0)
|
sentry-sidekiq (5.18.1)
|
||||||
sentry-ruby (~> 5.18.0)
|
sentry-ruby (~> 5.18.1)
|
||||||
sidekiq (>= 3.0)
|
sidekiq (>= 3.0)
|
||||||
sexp_processor (4.17.0)
|
sexp_processor (4.17.0)
|
||||||
shoulda-matchers (5.3.0)
|
shoulda-matchers (5.3.0)
|
||||||
activesupport (>= 5.2.0)
|
activesupport (>= 5.2.0)
|
||||||
sidekiq (7.2.4)
|
sidekiq (7.3.0)
|
||||||
concurrent-ruby (< 2)
|
concurrent-ruby (< 2)
|
||||||
connection_pool (>= 2.3.0)
|
connection_pool (>= 2.3.0)
|
||||||
|
logger
|
||||||
rack (>= 2.2.4)
|
rack (>= 2.2.4)
|
||||||
redis-client (>= 0.19.0)
|
redis-client (>= 0.22.2)
|
||||||
sidekiq-cron (1.12.0)
|
sidekiq-cron (1.12.0)
|
||||||
fugit (~> 1.8)
|
fugit (~> 1.8)
|
||||||
globalid (>= 1.0.1)
|
globalid (>= 1.0.1)
|
||||||
@ -931,11 +938,11 @@ DEPENDENCIES
|
|||||||
scout_apm
|
scout_apm
|
||||||
scss_lint
|
scss_lint
|
||||||
seed_dump
|
seed_dump
|
||||||
sentry-rails (>= 5.18.0)
|
sentry-rails (>= 5.18.1)
|
||||||
sentry-ruby
|
sentry-ruby
|
||||||
sentry-sidekiq (>= 5.18.0)
|
sentry-sidekiq (>= 5.18.1)
|
||||||
shoulda-matchers
|
shoulda-matchers
|
||||||
sidekiq (>= 7.2.4)
|
sidekiq (>= 7.3.0)
|
||||||
sidekiq-cron (>= 1.12.0)
|
sidekiq-cron (>= 1.12.0)
|
||||||
simplecov (= 0.17.1)
|
simplecov (= 0.17.1)
|
||||||
slack-ruby-client (~> 2.2.0)
|
slack-ruby-client (~> 2.2.0)
|
||||||
@ -960,7 +967,7 @@ DEPENDENCIES
|
|||||||
working_hours
|
working_hours
|
||||||
|
|
||||||
RUBY VERSION
|
RUBY VERSION
|
||||||
ruby 3.2.2p185
|
ruby 3.3.3p89
|
||||||
|
|
||||||
BUNDLED WITH
|
BUNDLED WITH
|
||||||
2.4.6
|
2.5.14
|
||||||
|
|||||||
@ -37,7 +37,7 @@ class DashboardController < ActionController::Base
|
|||||||
end
|
end
|
||||||
|
|
||||||
def set_dashboard_scripts
|
def set_dashboard_scripts
|
||||||
@dashboard_scripts = GlobalConfig.get_value('DASHBOARD_SCRIPTS')
|
@dashboard_scripts = sensitive_path? ? nil : GlobalConfig.get_value('DASHBOARD_SCRIPTS')
|
||||||
end
|
end
|
||||||
|
|
||||||
def ensure_installation_onboarding
|
def ensure_installation_onboarding
|
||||||
@ -75,4 +75,14 @@ class DashboardController < ActionController::Base
|
|||||||
'application'
|
'application'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def sensitive_path?
|
||||||
|
# dont load dashboard scripts on sensitive paths like password reset
|
||||||
|
sensitive_paths = [edit_user_password_path].freeze
|
||||||
|
|
||||||
|
# remove app prefix
|
||||||
|
current_path = request.path.gsub(%r{^/app}, '')
|
||||||
|
|
||||||
|
sensitive_paths.include?(current_path)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,11 +0,0 @@
|
|||||||
import App from './App';
|
|
||||||
import '../../test-matchers';
|
|
||||||
|
|
||||||
describe(`App component`, () => {
|
|
||||||
it(`should be a component`, () => {
|
|
||||||
// Arrange
|
|
||||||
// Act
|
|
||||||
expect(App).toBeVueComponent('App');
|
|
||||||
// Assert
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -15,10 +15,10 @@ describe('#enterpriseAccountAPI', () => {
|
|||||||
describe('API calls', () => {
|
describe('API calls', () => {
|
||||||
const originalAxios = window.axios;
|
const originalAxios = window.axios;
|
||||||
const axiosMock = {
|
const axiosMock = {
|
||||||
post: jest.fn(() => Promise.resolve()),
|
post: vi.fn(() => Promise.resolve()),
|
||||||
get: jest.fn(() => Promise.resolve()),
|
get: vi.fn(() => Promise.resolve()),
|
||||||
patch: jest.fn(() => Promise.resolve()),
|
patch: vi.fn(() => Promise.resolve()),
|
||||||
delete: jest.fn(() => Promise.resolve()),
|
delete: vi.fn(() => Promise.resolve()),
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|||||||
@ -15,10 +15,10 @@ describe('#accountAPI', () => {
|
|||||||
describe('API calls', () => {
|
describe('API calls', () => {
|
||||||
const originalAxios = window.axios;
|
const originalAxios = window.axios;
|
||||||
const axiosMock = {
|
const axiosMock = {
|
||||||
post: jest.fn(() => Promise.resolve()),
|
post: vi.fn(() => Promise.resolve()),
|
||||||
get: jest.fn(() => Promise.resolve()),
|
get: vi.fn(() => Promise.resolve()),
|
||||||
patch: jest.fn(() => Promise.resolve()),
|
patch: vi.fn(() => Promise.resolve()),
|
||||||
delete: jest.fn(() => Promise.resolve()),
|
delete: vi.fn(() => Promise.resolve()),
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|||||||
@ -10,10 +10,10 @@ describe('#ContactsAPI', () => {
|
|||||||
describe('API calls', () => {
|
describe('API calls', () => {
|
||||||
const originalAxios = window.axios;
|
const originalAxios = window.axios;
|
||||||
const axiosMock = {
|
const axiosMock = {
|
||||||
post: jest.fn(() => Promise.resolve()),
|
post: vi.fn(() => Promise.resolve()),
|
||||||
get: jest.fn(() => Promise.resolve()),
|
get: vi.fn(() => Promise.resolve()),
|
||||||
patch: jest.fn(() => Promise.resolve()),
|
patch: vi.fn(() => Promise.resolve()),
|
||||||
delete: jest.fn(() => Promise.resolve()),
|
delete: vi.fn(() => Promise.resolve()),
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|||||||
@ -14,7 +14,7 @@ describe('#AgentAPI', () => {
|
|||||||
describe('API calls', () => {
|
describe('API calls', () => {
|
||||||
const originalAxios = window.axios;
|
const originalAxios = window.axios;
|
||||||
const axiosMock = {
|
const axiosMock = {
|
||||||
post: jest.fn(() => Promise.resolve()),
|
post: vi.fn(() => Promise.resolve()),
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|||||||
@ -14,10 +14,10 @@ describe('#PortalAPI', () => {
|
|||||||
describe('API calls', () => {
|
describe('API calls', () => {
|
||||||
const originalAxios = window.axios;
|
const originalAxios = window.axios;
|
||||||
const axiosMock = {
|
const axiosMock = {
|
||||||
post: jest.fn(() => Promise.resolve()),
|
post: vi.fn(() => Promise.resolve()),
|
||||||
get: jest.fn(() => Promise.resolve()),
|
get: vi.fn(() => Promise.resolve()),
|
||||||
patch: jest.fn(() => Promise.resolve()),
|
patch: vi.fn(() => Promise.resolve()),
|
||||||
delete: jest.fn(() => Promise.resolve()),
|
delete: vi.fn(() => Promise.resolve()),
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@ -44,10 +44,10 @@ describe('#PortalAPI', () => {
|
|||||||
describe('API calls', () => {
|
describe('API calls', () => {
|
||||||
const originalAxios = window.axios;
|
const originalAxios = window.axios;
|
||||||
const axiosMock = {
|
const axiosMock = {
|
||||||
post: jest.fn(() => Promise.resolve()),
|
post: vi.fn(() => Promise.resolve()),
|
||||||
get: jest.fn(() => Promise.resolve()),
|
get: vi.fn(() => Promise.resolve()),
|
||||||
patch: jest.fn(() => Promise.resolve()),
|
patch: vi.fn(() => Promise.resolve()),
|
||||||
delete: jest.fn(() => Promise.resolve()),
|
delete: vi.fn(() => Promise.resolve()),
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@ -71,10 +71,10 @@ describe('#PortalAPI', () => {
|
|||||||
describe('API calls', () => {
|
describe('API calls', () => {
|
||||||
const originalAxios = window.axios;
|
const originalAxios = window.axios;
|
||||||
const axiosMock = {
|
const axiosMock = {
|
||||||
post: jest.fn(() => Promise.resolve()),
|
post: vi.fn(() => Promise.resolve()),
|
||||||
get: jest.fn(() => Promise.resolve()),
|
get: vi.fn(() => Promise.resolve()),
|
||||||
patch: jest.fn(() => Promise.resolve()),
|
patch: vi.fn(() => Promise.resolve()),
|
||||||
delete: jest.fn(() => Promise.resolve()),
|
delete: vi.fn(() => Promise.resolve()),
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@ -98,10 +98,10 @@ describe('#PortalAPI', () => {
|
|||||||
describe('API calls', () => {
|
describe('API calls', () => {
|
||||||
const originalAxios = window.axios;
|
const originalAxios = window.axios;
|
||||||
const axiosMock = {
|
const axiosMock = {
|
||||||
post: jest.fn(() => Promise.resolve()),
|
post: vi.fn(() => Promise.resolve()),
|
||||||
get: jest.fn(() => Promise.resolve()),
|
get: vi.fn(() => Promise.resolve()),
|
||||||
patch: jest.fn(() => Promise.resolve()),
|
patch: vi.fn(() => Promise.resolve()),
|
||||||
delete: jest.fn(() => Promise.resolve()),
|
delete: vi.fn(() => Promise.resolve()),
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@ -129,10 +129,10 @@ describe('#PortalAPI', () => {
|
|||||||
describe('API calls', () => {
|
describe('API calls', () => {
|
||||||
const originalAxios = window.axios;
|
const originalAxios = window.axios;
|
||||||
const axiosMock = {
|
const axiosMock = {
|
||||||
post: jest.fn(() => Promise.resolve()),
|
post: vi.fn(() => Promise.resolve()),
|
||||||
get: jest.fn(() => Promise.resolve()),
|
get: vi.fn(() => Promise.resolve()),
|
||||||
patch: jest.fn(() => Promise.resolve()),
|
patch: vi.fn(() => Promise.resolve()),
|
||||||
delete: jest.fn(() => Promise.resolve()),
|
delete: vi.fn(() => Promise.resolve()),
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|||||||
@ -4,10 +4,10 @@ describe('#AssignableAgentsAPI', () => {
|
|||||||
describe('API calls', () => {
|
describe('API calls', () => {
|
||||||
const originalAxios = window.axios;
|
const originalAxios = window.axios;
|
||||||
const axiosMock = {
|
const axiosMock = {
|
||||||
post: jest.fn(() => Promise.resolve()),
|
post: vi.fn(() => Promise.resolve()),
|
||||||
get: jest.fn(() => Promise.resolve()),
|
get: vi.fn(() => Promise.resolve()),
|
||||||
patch: jest.fn(() => Promise.resolve()),
|
patch: vi.fn(() => Promise.resolve()),
|
||||||
delete: jest.fn(() => Promise.resolve()),
|
delete: vi.fn(() => Promise.resolve()),
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|||||||
@ -13,10 +13,10 @@ describe('#FBChannel', () => {
|
|||||||
describe('API calls', () => {
|
describe('API calls', () => {
|
||||||
const originalAxios = window.axios;
|
const originalAxios = window.axios;
|
||||||
const axiosMock = {
|
const axiosMock = {
|
||||||
post: jest.fn(() => Promise.resolve()),
|
post: vi.fn(() => Promise.resolve()),
|
||||||
get: jest.fn(() => Promise.resolve()),
|
get: vi.fn(() => Promise.resolve()),
|
||||||
patch: jest.fn(() => Promise.resolve()),
|
patch: vi.fn(() => Promise.resolve()),
|
||||||
delete: jest.fn(() => Promise.resolve()),
|
delete: vi.fn(() => Promise.resolve()),
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|||||||
@ -17,10 +17,10 @@ describe('#ContactsAPI', () => {
|
|||||||
describe('API calls', () => {
|
describe('API calls', () => {
|
||||||
const originalAxios = window.axios;
|
const originalAxios = window.axios;
|
||||||
const axiosMock = {
|
const axiosMock = {
|
||||||
post: jest.fn(() => Promise.resolve()),
|
post: vi.fn(() => Promise.resolve()),
|
||||||
get: jest.fn(() => Promise.resolve()),
|
get: vi.fn(() => Promise.resolve()),
|
||||||
patch: jest.fn(() => Promise.resolve()),
|
patch: vi.fn(() => Promise.resolve()),
|
||||||
delete: jest.fn(() => Promise.resolve()),
|
delete: vi.fn(() => Promise.resolve()),
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|||||||
@ -16,10 +16,10 @@ describe('#ConversationApi', () => {
|
|||||||
describe('API calls', () => {
|
describe('API calls', () => {
|
||||||
const originalAxios = window.axios;
|
const originalAxios = window.axios;
|
||||||
const axiosMock = {
|
const axiosMock = {
|
||||||
post: jest.fn(() => Promise.resolve()),
|
post: vi.fn(() => Promise.resolve()),
|
||||||
get: jest.fn(() => Promise.resolve()),
|
get: vi.fn(() => Promise.resolve()),
|
||||||
patch: jest.fn(() => Promise.resolve()),
|
patch: vi.fn(() => Promise.resolve()),
|
||||||
delete: jest.fn(() => Promise.resolve()),
|
delete: vi.fn(() => Promise.resolve()),
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|||||||
@ -11,10 +11,10 @@ describe('#Reports API', () => {
|
|||||||
describe('API calls', () => {
|
describe('API calls', () => {
|
||||||
const originalAxios = window.axios;
|
const originalAxios = window.axios;
|
||||||
const axiosMock = {
|
const axiosMock = {
|
||||||
post: jest.fn(() => Promise.resolve()),
|
post: vi.fn(() => Promise.resolve()),
|
||||||
get: jest.fn(() => Promise.resolve()),
|
get: vi.fn(() => Promise.resolve()),
|
||||||
patch: jest.fn(() => Promise.resolve()),
|
patch: vi.fn(() => Promise.resolve()),
|
||||||
delete: jest.fn(() => Promise.resolve()),
|
delete: vi.fn(() => Promise.resolve()),
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|||||||
@ -24,10 +24,10 @@ describe('#ConversationAPI', () => {
|
|||||||
describe('API calls', () => {
|
describe('API calls', () => {
|
||||||
const originalAxios = window.axios;
|
const originalAxios = window.axios;
|
||||||
const axiosMock = {
|
const axiosMock = {
|
||||||
post: jest.fn(() => Promise.resolve()),
|
post: vi.fn(() => Promise.resolve()),
|
||||||
get: jest.fn(() => Promise.resolve()),
|
get: vi.fn(() => Promise.resolve()),
|
||||||
patch: jest.fn(() => Promise.resolve()),
|
patch: vi.fn(() => Promise.resolve()),
|
||||||
delete: jest.fn(() => Promise.resolve()),
|
delete: vi.fn(() => Promise.resolve()),
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|||||||
@ -15,10 +15,10 @@ describe('#ConversationAPI', () => {
|
|||||||
describe('API calls', () => {
|
describe('API calls', () => {
|
||||||
const originalAxios = window.axios;
|
const originalAxios = window.axios;
|
||||||
const axiosMock = {
|
const axiosMock = {
|
||||||
post: jest.fn(() => Promise.resolve()),
|
post: vi.fn(() => Promise.resolve()),
|
||||||
get: jest.fn(() => Promise.resolve()),
|
get: vi.fn(() => Promise.resolve()),
|
||||||
patch: jest.fn(() => Promise.resolve()),
|
patch: vi.fn(() => Promise.resolve()),
|
||||||
delete: jest.fn(() => Promise.resolve()),
|
delete: vi.fn(() => Promise.resolve()),
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|||||||
@ -17,10 +17,10 @@ describe('#InboxesAPI', () => {
|
|||||||
describe('API calls', () => {
|
describe('API calls', () => {
|
||||||
const originalAxios = window.axios;
|
const originalAxios = window.axios;
|
||||||
const axiosMock = {
|
const axiosMock = {
|
||||||
post: jest.fn(() => Promise.resolve()),
|
post: vi.fn(() => Promise.resolve()),
|
||||||
get: jest.fn(() => Promise.resolve()),
|
get: vi.fn(() => Promise.resolve()),
|
||||||
patch: jest.fn(() => Promise.resolve()),
|
patch: vi.fn(() => Promise.resolve()),
|
||||||
delete: jest.fn(() => Promise.resolve()),
|
delete: vi.fn(() => Promise.resolve()),
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|||||||
@ -18,10 +18,10 @@ describe('#integrationAPI', () => {
|
|||||||
describe('API calls', () => {
|
describe('API calls', () => {
|
||||||
const originalAxios = window.axios;
|
const originalAxios = window.axios;
|
||||||
const axiosMock = {
|
const axiosMock = {
|
||||||
post: jest.fn(() => Promise.resolve()),
|
post: vi.fn(() => Promise.resolve()),
|
||||||
get: jest.fn(() => Promise.resolve()),
|
get: vi.fn(() => Promise.resolve()),
|
||||||
patch: jest.fn(() => Promise.resolve()),
|
patch: vi.fn(() => Promise.resolve()),
|
||||||
delete: jest.fn(() => Promise.resolve()),
|
delete: vi.fn(() => Promise.resolve()),
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|||||||
@ -11,10 +11,10 @@ describe('#accountAPI', () => {
|
|||||||
describe('createAMeeting', () => {
|
describe('createAMeeting', () => {
|
||||||
const originalAxios = window.axios;
|
const originalAxios = window.axios;
|
||||||
const axiosMock = {
|
const axiosMock = {
|
||||||
post: jest.fn(() => Promise.resolve()),
|
post: vi.fn(() => Promise.resolve()),
|
||||||
get: jest.fn(() => Promise.resolve()),
|
get: vi.fn(() => Promise.resolve()),
|
||||||
patch: jest.fn(() => Promise.resolve()),
|
patch: vi.fn(() => Promise.resolve()),
|
||||||
delete: jest.fn(() => Promise.resolve()),
|
delete: vi.fn(() => Promise.resolve()),
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@ -39,10 +39,10 @@ describe('#accountAPI', () => {
|
|||||||
describe('addParticipantToMeeting', () => {
|
describe('addParticipantToMeeting', () => {
|
||||||
const originalAxios = window.axios;
|
const originalAxios = window.axios;
|
||||||
const axiosMock = {
|
const axiosMock = {
|
||||||
post: jest.fn(() => Promise.resolve()),
|
post: vi.fn(() => Promise.resolve()),
|
||||||
get: jest.fn(() => Promise.resolve()),
|
get: vi.fn(() => Promise.resolve()),
|
||||||
patch: jest.fn(() => Promise.resolve()),
|
patch: vi.fn(() => Promise.resolve()),
|
||||||
delete: jest.fn(() => Promise.resolve()),
|
delete: vi.fn(() => Promise.resolve()),
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|||||||
@ -16,10 +16,10 @@ describe('#linearAPI', () => {
|
|||||||
describe('getTeams', () => {
|
describe('getTeams', () => {
|
||||||
const originalAxios = window.axios;
|
const originalAxios = window.axios;
|
||||||
const axiosMock = {
|
const axiosMock = {
|
||||||
post: jest.fn(() => Promise.resolve()),
|
post: vi.fn(() => Promise.resolve()),
|
||||||
get: jest.fn(() => Promise.resolve()),
|
get: vi.fn(() => Promise.resolve()),
|
||||||
patch: jest.fn(() => Promise.resolve()),
|
patch: vi.fn(() => Promise.resolve()),
|
||||||
delete: jest.fn(() => Promise.resolve()),
|
delete: vi.fn(() => Promise.resolve()),
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@ -41,10 +41,10 @@ describe('#linearAPI', () => {
|
|||||||
describe('getTeamEntities', () => {
|
describe('getTeamEntities', () => {
|
||||||
const originalAxios = window.axios;
|
const originalAxios = window.axios;
|
||||||
const axiosMock = {
|
const axiosMock = {
|
||||||
post: jest.fn(() => Promise.resolve()),
|
post: vi.fn(() => Promise.resolve()),
|
||||||
get: jest.fn(() => Promise.resolve()),
|
get: vi.fn(() => Promise.resolve()),
|
||||||
patch: jest.fn(() => Promise.resolve()),
|
patch: vi.fn(() => Promise.resolve()),
|
||||||
delete: jest.fn(() => Promise.resolve()),
|
delete: vi.fn(() => Promise.resolve()),
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@ -66,10 +66,10 @@ describe('#linearAPI', () => {
|
|||||||
describe('createIssue', () => {
|
describe('createIssue', () => {
|
||||||
const originalAxios = window.axios;
|
const originalAxios = window.axios;
|
||||||
const axiosMock = {
|
const axiosMock = {
|
||||||
post: jest.fn(() => Promise.resolve()),
|
post: vi.fn(() => Promise.resolve()),
|
||||||
get: jest.fn(() => Promise.resolve()),
|
get: vi.fn(() => Promise.resolve()),
|
||||||
patch: jest.fn(() => Promise.resolve()),
|
patch: vi.fn(() => Promise.resolve()),
|
||||||
delete: jest.fn(() => Promise.resolve()),
|
delete: vi.fn(() => Promise.resolve()),
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@ -96,10 +96,10 @@ describe('#linearAPI', () => {
|
|||||||
describe('link_issue', () => {
|
describe('link_issue', () => {
|
||||||
const originalAxios = window.axios;
|
const originalAxios = window.axios;
|
||||||
const axiosMock = {
|
const axiosMock = {
|
||||||
post: jest.fn(() => Promise.resolve()),
|
post: vi.fn(() => Promise.resolve()),
|
||||||
get: jest.fn(() => Promise.resolve()),
|
get: vi.fn(() => Promise.resolve()),
|
||||||
patch: jest.fn(() => Promise.resolve()),
|
patch: vi.fn(() => Promise.resolve()),
|
||||||
delete: jest.fn(() => Promise.resolve()),
|
delete: vi.fn(() => Promise.resolve()),
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@ -125,10 +125,10 @@ describe('#linearAPI', () => {
|
|||||||
describe('getLinkedIssue', () => {
|
describe('getLinkedIssue', () => {
|
||||||
const originalAxios = window.axios;
|
const originalAxios = window.axios;
|
||||||
const axiosMock = {
|
const axiosMock = {
|
||||||
post: jest.fn(() => Promise.resolve()),
|
post: vi.fn(() => Promise.resolve()),
|
||||||
get: jest.fn(() => Promise.resolve()),
|
get: vi.fn(() => Promise.resolve()),
|
||||||
patch: jest.fn(() => Promise.resolve()),
|
patch: vi.fn(() => Promise.resolve()),
|
||||||
delete: jest.fn(() => Promise.resolve()),
|
delete: vi.fn(() => Promise.resolve()),
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@ -150,10 +150,10 @@ describe('#linearAPI', () => {
|
|||||||
describe('unlinkIssue', () => {
|
describe('unlinkIssue', () => {
|
||||||
const originalAxios = window.axios;
|
const originalAxios = window.axios;
|
||||||
const axiosMock = {
|
const axiosMock = {
|
||||||
post: jest.fn(() => Promise.resolve()),
|
post: vi.fn(() => Promise.resolve()),
|
||||||
get: jest.fn(() => Promise.resolve()),
|
get: vi.fn(() => Promise.resolve()),
|
||||||
patch: jest.fn(() => Promise.resolve()),
|
patch: vi.fn(() => Promise.resolve()),
|
||||||
delete: jest.fn(() => Promise.resolve()),
|
delete: vi.fn(() => Promise.resolve()),
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@ -178,10 +178,10 @@ describe('#linearAPI', () => {
|
|||||||
describe('searchIssues', () => {
|
describe('searchIssues', () => {
|
||||||
const originalAxios = window.axios;
|
const originalAxios = window.axios;
|
||||||
const axiosMock = {
|
const axiosMock = {
|
||||||
post: jest.fn(() => Promise.resolve()),
|
post: vi.fn(() => Promise.resolve()),
|
||||||
get: jest.fn(() => Promise.resolve()),
|
get: vi.fn(() => Promise.resolve()),
|
||||||
patch: jest.fn(() => Promise.resolve()),
|
patch: vi.fn(() => Promise.resolve()),
|
||||||
delete: jest.fn(() => Promise.resolve()),
|
delete: vi.fn(() => Promise.resolve()),
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|||||||
@ -13,10 +13,10 @@ describe('#NotificationAPI', () => {
|
|||||||
describe('API calls', () => {
|
describe('API calls', () => {
|
||||||
const originalAxios = window.axios;
|
const originalAxios = window.axios;
|
||||||
const axiosMock = {
|
const axiosMock = {
|
||||||
post: jest.fn(() => Promise.resolve()),
|
post: vi.fn(() => Promise.resolve()),
|
||||||
get: jest.fn(() => Promise.resolve()),
|
get: vi.fn(() => Promise.resolve()),
|
||||||
patch: jest.fn(() => Promise.resolve()),
|
patch: vi.fn(() => Promise.resolve()),
|
||||||
delete: jest.fn(() => Promise.resolve()),
|
delete: vi.fn(() => Promise.resolve()),
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|||||||
@ -20,10 +20,10 @@ describe('#Reports API', () => {
|
|||||||
describe('API calls', () => {
|
describe('API calls', () => {
|
||||||
const originalAxios = window.axios;
|
const originalAxios = window.axios;
|
||||||
const axiosMock = {
|
const axiosMock = {
|
||||||
post: jest.fn(() => Promise.resolve()),
|
post: vi.fn(() => Promise.resolve()),
|
||||||
get: jest.fn(() => Promise.resolve()),
|
get: vi.fn(() => Promise.resolve()),
|
||||||
patch: jest.fn(() => Promise.resolve()),
|
patch: vi.fn(() => Promise.resolve()),
|
||||||
delete: jest.fn(() => Promise.resolve()),
|
delete: vi.fn(() => Promise.resolve()),
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|||||||
@ -12,10 +12,10 @@ describe('#SLAReports API', () => {
|
|||||||
describe('API calls', () => {
|
describe('API calls', () => {
|
||||||
const originalAxios = window.axios;
|
const originalAxios = window.axios;
|
||||||
const axiosMock = {
|
const axiosMock = {
|
||||||
post: jest.fn(() => Promise.resolve()),
|
post: vi.fn(() => Promise.resolve()),
|
||||||
get: jest.fn(() => Promise.resolve()),
|
get: vi.fn(() => Promise.resolve()),
|
||||||
patch: jest.fn(() => Promise.resolve()),
|
patch: vi.fn(() => Promise.resolve()),
|
||||||
delete: jest.fn(() => Promise.resolve()),
|
delete: vi.fn(() => Promise.resolve()),
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|||||||
@ -16,10 +16,10 @@ describe('#TeamsAPI', () => {
|
|||||||
describe('API calls', () => {
|
describe('API calls', () => {
|
||||||
const originalAxios = window.axios;
|
const originalAxios = window.axios;
|
||||||
const axiosMock = {
|
const axiosMock = {
|
||||||
post: jest.fn(() => Promise.resolve()),
|
post: vi.fn(() => Promise.resolve()),
|
||||||
get: jest.fn(() => Promise.resolve()),
|
get: vi.fn(() => Promise.resolve()),
|
||||||
patch: jest.fn(() => Promise.resolve()),
|
patch: vi.fn(() => Promise.resolve()),
|
||||||
delete: jest.fn(() => Promise.resolve()),
|
delete: vi.fn(() => Promise.resolve()),
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|||||||
@ -111,15 +111,6 @@
|
|||||||
@updateFolder="onUpdateSavedFilter"
|
@updateFolder="onUpdateSavedFilter"
|
||||||
/>
|
/>
|
||||||
</woot-modal>
|
</woot-modal>
|
||||||
<woot-modal
|
|
||||||
:show.sync="showCustomSnoozeModal"
|
|
||||||
:on-close="hideCustomSnoozeModal"
|
|
||||||
>
|
|
||||||
<custom-snooze-modal
|
|
||||||
@close="hideCustomSnoozeModal"
|
|
||||||
@choose-time="chooseSnoozeTime"
|
|
||||||
/>
|
|
||||||
</woot-modal>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -152,10 +143,6 @@ import {
|
|||||||
isOnUnattendedView,
|
isOnUnattendedView,
|
||||||
} from '../store/modules/conversations/helpers/actionHelpers';
|
} from '../store/modules/conversations/helpers/actionHelpers';
|
||||||
import { CONVERSATION_EVENTS } from '../helper/AnalyticsHelper/events';
|
import { CONVERSATION_EVENTS } from '../helper/AnalyticsHelper/events';
|
||||||
import { CMD_SNOOZE_CONVERSATION } from 'dashboard/routes/dashboard/commands/commandBarBusEvents';
|
|
||||||
import { findSnoozeTime } from 'dashboard/helper/snoozeHelpers';
|
|
||||||
import { getUnixTime } from 'date-fns';
|
|
||||||
import CustomSnoozeModal from 'dashboard/components/CustomSnoozeModal.vue';
|
|
||||||
import IntersectionObserver from './IntersectionObserver.vue';
|
import IntersectionObserver from './IntersectionObserver.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@ -170,7 +157,6 @@ export default {
|
|||||||
ConversationBulkActions,
|
ConversationBulkActions,
|
||||||
IntersectionObserver,
|
IntersectionObserver,
|
||||||
VirtualList,
|
VirtualList,
|
||||||
CustomSnoozeModal,
|
|
||||||
},
|
},
|
||||||
mixins: [
|
mixins: [
|
||||||
timeMixin,
|
timeMixin,
|
||||||
@ -247,7 +233,6 @@ export default {
|
|||||||
root: this.$refs.conversationList,
|
root: this.$refs.conversationList,
|
||||||
rootMargin: '100px 0px 100px 0px',
|
rootMargin: '100px 0px 100px 0px',
|
||||||
},
|
},
|
||||||
showCustomSnoozeModal: false,
|
|
||||||
|
|
||||||
itemComponent: ConversationItem,
|
itemComponent: ConversationItem,
|
||||||
// virtualListExtraProps is to pass the props to the conversationItem component.
|
// virtualListExtraProps is to pass the props to the conversationItem component.
|
||||||
@ -283,7 +268,6 @@ export default {
|
|||||||
campaigns: 'campaigns/getAllCampaigns',
|
campaigns: 'campaigns/getAllCampaigns',
|
||||||
labels: 'labels/getLabels',
|
labels: 'labels/getLabels',
|
||||||
selectedConversations: 'bulkActions/getSelectedConversationIds',
|
selectedConversations: 'bulkActions/getSelectedConversationIds',
|
||||||
contextMenuChatId: 'getContextMenuChatId',
|
|
||||||
}),
|
}),
|
||||||
hasAppliedFilters() {
|
hasAppliedFilters() {
|
||||||
return this.appliedFilters.length !== 0;
|
return this.appliedFilters.length !== 0;
|
||||||
@ -517,11 +501,6 @@ export default {
|
|||||||
this.$emitter.on('fetch_conversation_stats', () => {
|
this.$emitter.on('fetch_conversation_stats', () => {
|
||||||
this.$store.dispatch('conversationStats/get', this.conversationFilters);
|
this.$store.dispatch('conversationStats/get', this.conversationFilters);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.$emitter.on(CMD_SNOOZE_CONVERSATION, this.onCmdSnoozeConversation);
|
|
||||||
},
|
|
||||||
beforeDestroy() {
|
|
||||||
this.$emitter.off(CMD_SNOOZE_CONVERSATION, this.onCmdSnoozeConversation);
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
updateVirtualListProps(key, value) {
|
updateVirtualListProps(key, value) {
|
||||||
@ -999,43 +978,6 @@ export default {
|
|||||||
onContextMenuToggle(state) {
|
onContextMenuToggle(state) {
|
||||||
this.isContextMenuOpen = state;
|
this.isContextMenuOpen = state;
|
||||||
},
|
},
|
||||||
onCmdSnoozeConversation(snoozeType) {
|
|
||||||
if (snoozeType === wootConstants.SNOOZE_OPTIONS.UNTIL_CUSTOM_TIME) {
|
|
||||||
this.showCustomSnoozeModal = true;
|
|
||||||
} else {
|
|
||||||
this.toggleStatus(
|
|
||||||
wootConstants.STATUS_TYPE.SNOOZED,
|
|
||||||
findSnoozeTime(snoozeType) || null
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
chooseSnoozeTime(customSnoozeTime) {
|
|
||||||
this.showCustomSnoozeModal = false;
|
|
||||||
if (customSnoozeTime) {
|
|
||||||
this.toggleStatus(
|
|
||||||
wootConstants.STATUS_TYPE.SNOOZED,
|
|
||||||
getUnixTime(customSnoozeTime)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
toggleStatus(status, snoozedUntil) {
|
|
||||||
this.$store
|
|
||||||
.dispatch('toggleStatus', {
|
|
||||||
conversationId: this.currentChat?.id || this.contextMenuChatId,
|
|
||||||
status,
|
|
||||||
snoozedUntil,
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
this.$store.dispatch('setContextMenuChatId', null);
|
|
||||||
this.showAlert(this.$t('CONVERSATION.CHANGE_STATUS'));
|
|
||||||
});
|
|
||||||
},
|
|
||||||
hideCustomSnoozeModal() {
|
|
||||||
// if we select custom snooze and then the custom snooze modal is open
|
|
||||||
// Then if the custom snooze modal is closed and set the context menu chat id to null
|
|
||||||
this.$store.dispatch('setContextMenuChatId', null);
|
|
||||||
this.showCustomSnoozeModal = false;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -20,7 +20,7 @@
|
|||||||
:teams="teams"
|
:teams="teams"
|
||||||
:custom-views="customViews"
|
:custom-views="customViews"
|
||||||
:menu-config="activeSecondaryMenu"
|
:menu-config="activeSecondaryMenu"
|
||||||
:current-role="currentRole"
|
:current-user="currentUser"
|
||||||
:is-on-chatwoot-cloud="isOnChatwootCloud"
|
:is-on-chatwoot-cloud="isOnChatwootCloud"
|
||||||
@add-label="showAddLabelPopup"
|
@add-label="showAddLabelPopup"
|
||||||
@toggle-accounts="toggleAccountModal"
|
@toggle-accounts="toggleAccountModal"
|
||||||
@ -37,7 +37,8 @@ import alertMixin from 'shared/mixins/alertMixin';
|
|||||||
import PrimarySidebar from './sidebarComponents/Primary.vue';
|
import PrimarySidebar from './sidebarComponents/Primary.vue';
|
||||||
import SecondarySidebar from './sidebarComponents/Secondary.vue';
|
import SecondarySidebar from './sidebarComponents/Secondary.vue';
|
||||||
import keyboardEventListenerMixins from 'shared/mixins/keyboardEventListenerMixins';
|
import keyboardEventListenerMixins from 'shared/mixins/keyboardEventListenerMixins';
|
||||||
import router from '../../routes';
|
import router, { routesWithPermissions } from '../../routes';
|
||||||
|
import { hasPermissions } from '../../helper/permissionsHelper';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@ -98,9 +99,13 @@ export default {
|
|||||||
return getSidebarItems(this.accountId);
|
return getSidebarItems(this.accountId);
|
||||||
},
|
},
|
||||||
primaryMenuItems() {
|
primaryMenuItems() {
|
||||||
|
const userPermissions = this.currentUser.permissions;
|
||||||
const menuItems = this.sideMenuConfig.primaryMenu;
|
const menuItems = this.sideMenuConfig.primaryMenu;
|
||||||
return menuItems.filter(menuItem => {
|
return menuItems.filter(menuItem => {
|
||||||
const isAvailableForTheUser = menuItem.roles.includes(this.currentRole);
|
const isAvailableForTheUser = hasPermissions(
|
||||||
|
routesWithPermissions[menuItem.toStateName],
|
||||||
|
userPermissions
|
||||||
|
);
|
||||||
|
|
||||||
if (!isAvailableForTheUser) {
|
if (!isAvailableForTheUser) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@ -9,7 +9,6 @@ const primaryMenuItems = accountId => [
|
|||||||
featureFlag: FEATURE_FLAGS.INBOX_VIEW,
|
featureFlag: FEATURE_FLAGS.INBOX_VIEW,
|
||||||
toState: frontendURL(`accounts/${accountId}/inbox-view`),
|
toState: frontendURL(`accounts/${accountId}/inbox-view`),
|
||||||
toStateName: 'inbox_view',
|
toStateName: 'inbox_view',
|
||||||
roles: ['administrator', 'agent'],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: 'chat',
|
icon: 'chat',
|
||||||
@ -17,7 +16,6 @@ const primaryMenuItems = accountId => [
|
|||||||
label: 'CONVERSATIONS',
|
label: 'CONVERSATIONS',
|
||||||
toState: frontendURL(`accounts/${accountId}/dashboard`),
|
toState: frontendURL(`accounts/${accountId}/dashboard`),
|
||||||
toStateName: 'home',
|
toStateName: 'home',
|
||||||
roles: ['administrator', 'agent'],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: 'book-contacts',
|
icon: 'book-contacts',
|
||||||
@ -26,7 +24,6 @@ const primaryMenuItems = accountId => [
|
|||||||
featureFlag: FEATURE_FLAGS.CRM,
|
featureFlag: FEATURE_FLAGS.CRM,
|
||||||
toState: frontendURL(`accounts/${accountId}/contacts`),
|
toState: frontendURL(`accounts/${accountId}/contacts`),
|
||||||
toStateName: 'contacts_dashboard',
|
toStateName: 'contacts_dashboard',
|
||||||
roles: ['administrator', 'agent'],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: 'arrow-trending-lines',
|
icon: 'arrow-trending-lines',
|
||||||
@ -34,8 +31,7 @@ const primaryMenuItems = accountId => [
|
|||||||
label: 'REPORTS',
|
label: 'REPORTS',
|
||||||
featureFlag: FEATURE_FLAGS.REPORTS,
|
featureFlag: FEATURE_FLAGS.REPORTS,
|
||||||
toState: frontendURL(`accounts/${accountId}/reports`),
|
toState: frontendURL(`accounts/${accountId}/reports`),
|
||||||
toStateName: 'settings_account_reports',
|
toStateName: 'account_overview_reports',
|
||||||
roles: ['administrator'],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: 'megaphone',
|
icon: 'megaphone',
|
||||||
@ -44,7 +40,6 @@ const primaryMenuItems = accountId => [
|
|||||||
featureFlag: FEATURE_FLAGS.CAMPAIGNS,
|
featureFlag: FEATURE_FLAGS.CAMPAIGNS,
|
||||||
toState: frontendURL(`accounts/${accountId}/campaigns`),
|
toState: frontendURL(`accounts/${accountId}/campaigns`),
|
||||||
toStateName: 'ongoing_campaigns',
|
toStateName: 'ongoing_campaigns',
|
||||||
roles: ['administrator'],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: 'library',
|
icon: 'library',
|
||||||
@ -54,7 +49,6 @@ const primaryMenuItems = accountId => [
|
|||||||
alwaysVisibleOnChatwootInstances: true,
|
alwaysVisibleOnChatwootInstances: true,
|
||||||
toState: frontendURL(`accounts/${accountId}/portals`),
|
toState: frontendURL(`accounts/${accountId}/portals`),
|
||||||
toStateName: 'default_portal_articles',
|
toStateName: 'default_portal_articles',
|
||||||
roles: ['administrator'],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: 'settings',
|
icon: 'settings',
|
||||||
@ -62,7 +56,6 @@ const primaryMenuItems = accountId => [
|
|||||||
label: 'SETTINGS',
|
label: 'SETTINGS',
|
||||||
toState: frontendURL(`accounts/${accountId}/settings`),
|
toState: frontendURL(`accounts/${accountId}/settings`),
|
||||||
toStateName: 'settings_home',
|
toStateName: 'settings_home',
|
||||||
roles: ['administrator', 'agent'],
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@ -24,7 +24,6 @@ const settings = accountId => ({
|
|||||||
'settings_inbox_list',
|
'settings_inbox_list',
|
||||||
'settings_inbox_new',
|
'settings_inbox_new',
|
||||||
'settings_inbox_show',
|
'settings_inbox_show',
|
||||||
'settings_inbox',
|
|
||||||
'settings_inboxes_add_agents',
|
'settings_inboxes_add_agents',
|
||||||
'settings_inboxes_page_channel',
|
'settings_inboxes_page_channel',
|
||||||
'settings_integrations_dashboard_apps',
|
'settings_integrations_dashboard_apps',
|
||||||
@ -46,6 +45,9 @@ const settings = accountId => ({
|
|||||||
icon: 'briefcase',
|
icon: 'briefcase',
|
||||||
label: 'ACCOUNT_SETTINGS',
|
label: 'ACCOUNT_SETTINGS',
|
||||||
hasSubMenu: false,
|
hasSubMenu: false,
|
||||||
|
meta: {
|
||||||
|
permissions: ['administrator'],
|
||||||
|
},
|
||||||
toState: frontendURL(`accounts/${accountId}/settings/general`),
|
toState: frontendURL(`accounts/${accountId}/settings/general`),
|
||||||
toStateName: 'general_settings_index',
|
toStateName: 'general_settings_index',
|
||||||
},
|
},
|
||||||
@ -53,6 +55,9 @@ const settings = accountId => ({
|
|||||||
icon: 'people',
|
icon: 'people',
|
||||||
label: 'AGENTS',
|
label: 'AGENTS',
|
||||||
hasSubMenu: false,
|
hasSubMenu: false,
|
||||||
|
meta: {
|
||||||
|
permissions: ['administrator'],
|
||||||
|
},
|
||||||
toState: frontendURL(`accounts/${accountId}/settings/agents/list`),
|
toState: frontendURL(`accounts/${accountId}/settings/agents/list`),
|
||||||
toStateName: 'agent_list',
|
toStateName: 'agent_list',
|
||||||
featureFlag: FEATURE_FLAGS.AGENT_MANAGEMENT,
|
featureFlag: FEATURE_FLAGS.AGENT_MANAGEMENT,
|
||||||
@ -61,6 +66,9 @@ const settings = accountId => ({
|
|||||||
icon: 'people-team',
|
icon: 'people-team',
|
||||||
label: 'TEAMS',
|
label: 'TEAMS',
|
||||||
hasSubMenu: false,
|
hasSubMenu: false,
|
||||||
|
meta: {
|
||||||
|
permissions: ['administrator'],
|
||||||
|
},
|
||||||
toState: frontendURL(`accounts/${accountId}/settings/teams/list`),
|
toState: frontendURL(`accounts/${accountId}/settings/teams/list`),
|
||||||
toStateName: 'settings_teams_list',
|
toStateName: 'settings_teams_list',
|
||||||
featureFlag: FEATURE_FLAGS.TEAM_MANAGEMENT,
|
featureFlag: FEATURE_FLAGS.TEAM_MANAGEMENT,
|
||||||
@ -69,6 +77,9 @@ const settings = accountId => ({
|
|||||||
icon: 'mail-inbox-all',
|
icon: 'mail-inbox-all',
|
||||||
label: 'INBOXES',
|
label: 'INBOXES',
|
||||||
hasSubMenu: false,
|
hasSubMenu: false,
|
||||||
|
meta: {
|
||||||
|
permissions: ['administrator'],
|
||||||
|
},
|
||||||
toState: frontendURL(`accounts/${accountId}/settings/inboxes/list`),
|
toState: frontendURL(`accounts/${accountId}/settings/inboxes/list`),
|
||||||
toStateName: 'settings_inbox_list',
|
toStateName: 'settings_inbox_list',
|
||||||
featureFlag: FEATURE_FLAGS.INBOX_MANAGEMENT,
|
featureFlag: FEATURE_FLAGS.INBOX_MANAGEMENT,
|
||||||
@ -77,6 +88,9 @@ const settings = accountId => ({
|
|||||||
icon: 'tag',
|
icon: 'tag',
|
||||||
label: 'LABELS',
|
label: 'LABELS',
|
||||||
hasSubMenu: false,
|
hasSubMenu: false,
|
||||||
|
meta: {
|
||||||
|
permissions: ['administrator'],
|
||||||
|
},
|
||||||
toState: frontendURL(`accounts/${accountId}/settings/labels/list`),
|
toState: frontendURL(`accounts/${accountId}/settings/labels/list`),
|
||||||
toStateName: 'labels_list',
|
toStateName: 'labels_list',
|
||||||
featureFlag: FEATURE_FLAGS.LABELS,
|
featureFlag: FEATURE_FLAGS.LABELS,
|
||||||
@ -85,6 +99,9 @@ const settings = accountId => ({
|
|||||||
icon: 'code',
|
icon: 'code',
|
||||||
label: 'CUSTOM_ATTRIBUTES',
|
label: 'CUSTOM_ATTRIBUTES',
|
||||||
hasSubMenu: false,
|
hasSubMenu: false,
|
||||||
|
meta: {
|
||||||
|
permissions: ['administrator'],
|
||||||
|
},
|
||||||
toState: frontendURL(
|
toState: frontendURL(
|
||||||
`accounts/${accountId}/settings/custom-attributes/list`
|
`accounts/${accountId}/settings/custom-attributes/list`
|
||||||
),
|
),
|
||||||
@ -95,6 +112,9 @@ const settings = accountId => ({
|
|||||||
icon: 'automation',
|
icon: 'automation',
|
||||||
label: 'AUTOMATION',
|
label: 'AUTOMATION',
|
||||||
hasSubMenu: false,
|
hasSubMenu: false,
|
||||||
|
meta: {
|
||||||
|
permissions: ['administrator'],
|
||||||
|
},
|
||||||
toState: frontendURL(`accounts/${accountId}/settings/automation/list`),
|
toState: frontendURL(`accounts/${accountId}/settings/automation/list`),
|
||||||
toStateName: 'automation_list',
|
toStateName: 'automation_list',
|
||||||
featureFlag: FEATURE_FLAGS.AUTOMATIONS,
|
featureFlag: FEATURE_FLAGS.AUTOMATIONS,
|
||||||
@ -103,6 +123,9 @@ const settings = accountId => ({
|
|||||||
icon: 'bot',
|
icon: 'bot',
|
||||||
label: 'AGENT_BOTS',
|
label: 'AGENT_BOTS',
|
||||||
hasSubMenu: false,
|
hasSubMenu: false,
|
||||||
|
meta: {
|
||||||
|
permissions: ['administrator'],
|
||||||
|
},
|
||||||
globalConfigFlag: 'csmlEditorHost',
|
globalConfigFlag: 'csmlEditorHost',
|
||||||
toState: frontendURL(`accounts/${accountId}/settings/agent-bots`),
|
toState: frontendURL(`accounts/${accountId}/settings/agent-bots`),
|
||||||
toStateName: 'agent_bots',
|
toStateName: 'agent_bots',
|
||||||
@ -112,6 +135,9 @@ const settings = accountId => ({
|
|||||||
icon: 'flash-settings',
|
icon: 'flash-settings',
|
||||||
label: 'MACROS',
|
label: 'MACROS',
|
||||||
hasSubMenu: false,
|
hasSubMenu: false,
|
||||||
|
meta: {
|
||||||
|
permissions: ['administrator', 'agent'],
|
||||||
|
},
|
||||||
toState: frontendURL(`accounts/${accountId}/settings/macros`),
|
toState: frontendURL(`accounts/${accountId}/settings/macros`),
|
||||||
toStateName: 'macros_wrapper',
|
toStateName: 'macros_wrapper',
|
||||||
featureFlag: FEATURE_FLAGS.MACROS,
|
featureFlag: FEATURE_FLAGS.MACROS,
|
||||||
@ -120,6 +146,9 @@ const settings = accountId => ({
|
|||||||
icon: 'chat-multiple',
|
icon: 'chat-multiple',
|
||||||
label: 'CANNED_RESPONSES',
|
label: 'CANNED_RESPONSES',
|
||||||
hasSubMenu: false,
|
hasSubMenu: false,
|
||||||
|
meta: {
|
||||||
|
permissions: ['administrator', 'agent'],
|
||||||
|
},
|
||||||
toState: frontendURL(
|
toState: frontendURL(
|
||||||
`accounts/${accountId}/settings/canned-response/list`
|
`accounts/${accountId}/settings/canned-response/list`
|
||||||
),
|
),
|
||||||
@ -130,6 +159,9 @@ const settings = accountId => ({
|
|||||||
icon: 'flash-on',
|
icon: 'flash-on',
|
||||||
label: 'INTEGRATIONS',
|
label: 'INTEGRATIONS',
|
||||||
hasSubMenu: false,
|
hasSubMenu: false,
|
||||||
|
meta: {
|
||||||
|
permissions: ['administrator'],
|
||||||
|
},
|
||||||
toState: frontendURL(`accounts/${accountId}/settings/integrations`),
|
toState: frontendURL(`accounts/${accountId}/settings/integrations`),
|
||||||
toStateName: 'settings_integrations',
|
toStateName: 'settings_integrations',
|
||||||
featureFlag: FEATURE_FLAGS.INTEGRATIONS,
|
featureFlag: FEATURE_FLAGS.INTEGRATIONS,
|
||||||
@ -138,6 +170,9 @@ const settings = accountId => ({
|
|||||||
icon: 'star-emphasis',
|
icon: 'star-emphasis',
|
||||||
label: 'APPLICATIONS',
|
label: 'APPLICATIONS',
|
||||||
hasSubMenu: false,
|
hasSubMenu: false,
|
||||||
|
meta: {
|
||||||
|
permissions: ['administrator'],
|
||||||
|
},
|
||||||
toState: frontendURL(`accounts/${accountId}/settings/applications`),
|
toState: frontendURL(`accounts/${accountId}/settings/applications`),
|
||||||
toStateName: 'settings_applications',
|
toStateName: 'settings_applications',
|
||||||
featureFlag: FEATURE_FLAGS.INTEGRATIONS,
|
featureFlag: FEATURE_FLAGS.INTEGRATIONS,
|
||||||
@ -146,6 +181,9 @@ const settings = accountId => ({
|
|||||||
icon: 'key',
|
icon: 'key',
|
||||||
label: 'AUDIT_LOGS',
|
label: 'AUDIT_LOGS',
|
||||||
hasSubMenu: false,
|
hasSubMenu: false,
|
||||||
|
meta: {
|
||||||
|
permissions: ['administrator'],
|
||||||
|
},
|
||||||
toState: frontendURL(`accounts/${accountId}/settings/audit-log/list`),
|
toState: frontendURL(`accounts/${accountId}/settings/audit-log/list`),
|
||||||
toStateName: 'auditlogs_list',
|
toStateName: 'auditlogs_list',
|
||||||
isEnterpriseOnly: true,
|
isEnterpriseOnly: true,
|
||||||
@ -156,6 +194,9 @@ const settings = accountId => ({
|
|||||||
icon: 'document-list-clock',
|
icon: 'document-list-clock',
|
||||||
label: 'SLA',
|
label: 'SLA',
|
||||||
hasSubMenu: false,
|
hasSubMenu: false,
|
||||||
|
meta: {
|
||||||
|
permissions: ['administrator'],
|
||||||
|
},
|
||||||
toState: frontendURL(`accounts/${accountId}/settings/sla/list`),
|
toState: frontendURL(`accounts/${accountId}/settings/sla/list`),
|
||||||
toStateName: 'sla_list',
|
toStateName: 'sla_list',
|
||||||
isEnterpriseOnly: true,
|
isEnterpriseOnly: true,
|
||||||
@ -166,6 +207,9 @@ const settings = accountId => ({
|
|||||||
icon: 'credit-card-person',
|
icon: 'credit-card-person',
|
||||||
label: 'BILLING',
|
label: 'BILLING',
|
||||||
hasSubMenu: false,
|
hasSubMenu: false,
|
||||||
|
meta: {
|
||||||
|
permissions: ['administrator'],
|
||||||
|
},
|
||||||
toState: frontendURL(`accounts/${accountId}/settings/billing`),
|
toState: frontendURL(`accounts/${accountId}/settings/billing`),
|
||||||
toStateName: 'billing_settings_index',
|
toStateName: 'billing_settings_index',
|
||||||
showOnlyOnCloud: true,
|
showOnlyOnCloud: true,
|
||||||
|
|||||||
@ -29,6 +29,8 @@ import SecondaryNavItem from './SecondaryNavItem.vue';
|
|||||||
import AccountContext from './AccountContext.vue';
|
import AccountContext from './AccountContext.vue';
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import { FEATURE_FLAGS } from '../../../featureFlags';
|
import { FEATURE_FLAGS } from '../../../featureFlags';
|
||||||
|
import { hasPermissions } from '../../../helper/permissionsHelper';
|
||||||
|
import { routesWithPermissions } from '../../../routes';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@ -60,9 +62,9 @@ export default {
|
|||||||
type: Object,
|
type: Object,
|
||||||
default: () => {},
|
default: () => {},
|
||||||
},
|
},
|
||||||
currentRole: {
|
currentUser: {
|
||||||
type: String,
|
type: Object,
|
||||||
default: '',
|
default: () => {},
|
||||||
},
|
},
|
||||||
isOnChatwootCloud: {
|
isOnChatwootCloud: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
@ -80,16 +82,16 @@ export default {
|
|||||||
return this.customViews.filter(view => view.filter_type === 'contact');
|
return this.customViews.filter(view => view.filter_type === 'contact');
|
||||||
},
|
},
|
||||||
accessibleMenuItems() {
|
accessibleMenuItems() {
|
||||||
if (!this.currentRole) {
|
const menuItemsFilteredByPermissions = this.menuConfig.menuItems.filter(
|
||||||
return [];
|
menuItem => {
|
||||||
}
|
const { permissions: userPermissions = [] } = this.currentUser;
|
||||||
const menuItemsFilteredByRole = this.menuConfig.menuItems.filter(
|
return hasPermissions(
|
||||||
menuItem =>
|
routesWithPermissions[menuItem.toStateName],
|
||||||
window.roleWiseRoutes[this.currentRole].indexOf(
|
userPermissions
|
||||||
menuItem.toStateName
|
|
||||||
) > -1
|
|
||||||
);
|
);
|
||||||
return menuItemsFilteredByRole.filter(item => {
|
}
|
||||||
|
);
|
||||||
|
return menuItemsFilteredByPermissions.filter(item => {
|
||||||
if (item.showOnlyOnCloud) {
|
if (item.showOnlyOnCloud) {
|
||||||
return this.isOnChatwootCloud;
|
return this.isOnChatwootCloud;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -65,8 +65,9 @@
|
|||||||
:show-child-count="showChildCount(child.count)"
|
:show-child-count="showChildCount(child.count)"
|
||||||
:child-item-count="child.count"
|
:child-item-count="child.count"
|
||||||
/>
|
/>
|
||||||
|
<Policy :permissions="['administrator']">
|
||||||
<router-link
|
<router-link
|
||||||
v-if="showItem(menuItem)"
|
v-if="menuItem.newLink"
|
||||||
v-slot="{ href, navigate }"
|
v-slot="{ href, navigate }"
|
||||||
:to="menuItem.toState"
|
:to="menuItem.toState"
|
||||||
custom
|
custom
|
||||||
@ -86,6 +87,7 @@
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</router-link>
|
</router-link>
|
||||||
|
</Policy>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
</template>
|
</template>
|
||||||
@ -105,9 +107,10 @@ import {
|
|||||||
isOnMentionsView,
|
isOnMentionsView,
|
||||||
isOnUnattendedView,
|
isOnUnattendedView,
|
||||||
} from '../../../store/modules/conversations/helpers/actionHelpers';
|
} from '../../../store/modules/conversations/helpers/actionHelpers';
|
||||||
|
import Policy from '../../policy.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { SecondaryChildNavItem },
|
components: { SecondaryChildNavItem, Policy },
|
||||||
mixins: [adminMixin, configMixin],
|
mixins: [adminMixin, configMixin],
|
||||||
props: {
|
props: {
|
||||||
menuItem: {
|
menuItem: {
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
import AccountSelector from '../AccountSelector';
|
import AccountSelector from '../AccountSelector.vue';
|
||||||
import { createLocalVue, mount } from '@vue/test-utils';
|
import { createLocalVue, mount } from '@vue/test-utils';
|
||||||
import Vuex from 'vuex';
|
import Vuex from 'vuex';
|
||||||
import VueI18n from 'vue-i18n';
|
import VueI18n from 'vue-i18n';
|
||||||
|
|
||||||
import i18n from 'dashboard/i18n';
|
import i18n from 'dashboard/i18n';
|
||||||
import WootModal from 'dashboard/components/Modal';
|
import WootModal from 'dashboard/components/Modal.vue';
|
||||||
import WootModalHeader from 'dashboard/components/ModalHeader';
|
import WootModalHeader from 'dashboard/components/ModalHeader.vue';
|
||||||
import FluentIcon from 'shared/components/FluentIcon/DashboardIcon';
|
import FluentIcon from 'shared/components/FluentIcon/DashboardIcon.vue';
|
||||||
|
|
||||||
const localVue = createLocalVue();
|
const localVue = createLocalVue();
|
||||||
localVue.component('woot-modal', WootModal);
|
localVue.component('woot-modal', WootModal);
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
import AgentDetails from '../AgentDetails';
|
import AgentDetails from '../AgentDetails.vue';
|
||||||
import { createLocalVue, shallowMount } from '@vue/test-utils';
|
import { createLocalVue, shallowMount } from '@vue/test-utils';
|
||||||
import Vuex from 'vuex';
|
import Vuex from 'vuex';
|
||||||
import VueI18n from 'vue-i18n';
|
import VueI18n from 'vue-i18n';
|
||||||
import VTooltip from 'v-tooltip';
|
import VTooltip from 'v-tooltip';
|
||||||
|
|
||||||
import i18n from 'dashboard/i18n';
|
import i18n from 'dashboard/i18n';
|
||||||
import Thumbnail from 'dashboard/components/widgets/Thumbnail';
|
import Thumbnail from 'dashboard/components/widgets/Thumbnail.vue';
|
||||||
import WootButton from 'dashboard/components/ui/WootButton';
|
import WootButton from 'dashboard/components/ui/WootButton.vue';
|
||||||
const localVue = createLocalVue();
|
const localVue = createLocalVue();
|
||||||
localVue.use(Vuex);
|
localVue.use(Vuex);
|
||||||
localVue.use(VueI18n);
|
localVue.use(VueI18n);
|
||||||
|
|||||||
@ -1,13 +1,15 @@
|
|||||||
import NotificationBell from '../NotificationBell';
|
import NotificationBell from '../NotificationBell.vue';
|
||||||
import { createLocalVue, shallowMount } from '@vue/test-utils';
|
import { createLocalVue, shallowMount } from '@vue/test-utils';
|
||||||
import Vuex from 'vuex';
|
import Vuex from 'vuex';
|
||||||
import VueI18n from 'vue-i18n';
|
import VueI18n from 'vue-i18n';
|
||||||
|
import FluentIcon from 'shared/components/FluentIcon/DashboardIcon.vue';
|
||||||
|
|
||||||
import i18n from 'dashboard/i18n';
|
import i18n from 'dashboard/i18n';
|
||||||
|
|
||||||
const localVue = createLocalVue();
|
const localVue = createLocalVue();
|
||||||
localVue.use(Vuex);
|
localVue.use(Vuex);
|
||||||
localVue.use(VueI18n);
|
localVue.use(VueI18n);
|
||||||
|
localVue.component('fluent-icon', FluentIcon);
|
||||||
|
|
||||||
const i18nConfig = new VueI18n({
|
const i18nConfig = new VueI18n({
|
||||||
locale: 'en',
|
locale: 'en',
|
||||||
@ -27,7 +29,7 @@ describe('notificationBell', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
actions = {
|
actions = {
|
||||||
showNotification: jest.fn(),
|
showNotification: vi.fn(),
|
||||||
};
|
};
|
||||||
modules = {
|
modules = {
|
||||||
auth: {
|
auth: {
|
||||||
|
|||||||
@ -2,15 +2,21 @@ import AvailabilityStatus from '../AvailabilityStatus.vue';
|
|||||||
import { createLocalVue, mount } from '@vue/test-utils';
|
import { createLocalVue, mount } from '@vue/test-utils';
|
||||||
import Vuex from 'vuex';
|
import Vuex from 'vuex';
|
||||||
import VueI18n from 'vue-i18n';
|
import VueI18n from 'vue-i18n';
|
||||||
|
import VTooltip from 'v-tooltip';
|
||||||
|
|
||||||
|
import WootButton from 'dashboard/components/ui/WootButton.vue';
|
||||||
|
import WootDropdownItem from 'shared/components/ui/dropdown/DropdownItem.vue';
|
||||||
|
import WootDropdownMenu from 'shared/components/ui/dropdown/DropdownMenu.vue';
|
||||||
|
import WootDropdownHeader from 'shared/components/ui/dropdown/DropdownHeader.vue';
|
||||||
|
import WootDropdownDivider from 'shared/components/ui/dropdown/DropdownDivider.vue';
|
||||||
|
import FluentIcon from 'shared/components/FluentIcon/DashboardIcon.vue';
|
||||||
|
|
||||||
import WootButton from 'dashboard/components/ui/WootButton';
|
|
||||||
import WootDropdownItem from 'shared/components/ui/dropdown/DropdownItem';
|
|
||||||
import WootDropdownMenu from 'shared/components/ui/dropdown/DropdownMenu';
|
|
||||||
import WootDropdownHeader from 'shared/components/ui/dropdown/DropdownHeader';
|
|
||||||
import WootDropdownDivider from 'shared/components/ui/dropdown/DropdownDivider';
|
|
||||||
import i18n from 'dashboard/i18n';
|
import i18n from 'dashboard/i18n';
|
||||||
|
|
||||||
const localVue = createLocalVue();
|
const localVue = createLocalVue();
|
||||||
|
localVue.use(VTooltip, {
|
||||||
|
defaultHtml: false,
|
||||||
|
});
|
||||||
localVue.use(Vuex);
|
localVue.use(Vuex);
|
||||||
localVue.use(VueI18n);
|
localVue.use(VueI18n);
|
||||||
localVue.component('woot-button', WootButton);
|
localVue.component('woot-button', WootButton);
|
||||||
@ -18,12 +24,14 @@ localVue.component('woot-dropdown-header', WootDropdownHeader);
|
|||||||
localVue.component('woot-dropdown-menu', WootDropdownMenu);
|
localVue.component('woot-dropdown-menu', WootDropdownMenu);
|
||||||
localVue.component('woot-dropdown-divider', WootDropdownDivider);
|
localVue.component('woot-dropdown-divider', WootDropdownDivider);
|
||||||
localVue.component('woot-dropdown-item', WootDropdownItem);
|
localVue.component('woot-dropdown-item', WootDropdownItem);
|
||||||
|
localVue.component('fluent-icon', FluentIcon);
|
||||||
|
|
||||||
const i18nConfig = new VueI18n({ locale: 'en', messages: i18n });
|
const i18nConfig = new VueI18n({ locale: 'en', messages: i18n });
|
||||||
|
|
||||||
describe('AvailabilityStatus', () => {
|
describe('AvailabilityStatus', () => {
|
||||||
const currentAvailability = 'online';
|
const currentAvailability = 'online';
|
||||||
const currentAccountId = '1';
|
const currentAccountId = '1';
|
||||||
|
const currentUserAutoOffline = false;
|
||||||
let store = null;
|
let store = null;
|
||||||
let actions = null;
|
let actions = null;
|
||||||
let modules = null;
|
let modules = null;
|
||||||
@ -31,7 +39,7 @@ describe('AvailabilityStatus', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
actions = {
|
actions = {
|
||||||
updateAvailability: jest.fn(() => {
|
updateAvailability: vi.fn(() => {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
@ -41,6 +49,7 @@ describe('AvailabilityStatus', () => {
|
|||||||
getters: {
|
getters: {
|
||||||
getCurrentUserAvailability: () => currentAvailability,
|
getCurrentUserAvailability: () => currentAvailability,
|
||||||
getCurrentAccountId: () => currentAccountId,
|
getCurrentAccountId: () => currentAccountId,
|
||||||
|
getCurrentUserAutoOffline: () => currentUserAutoOffline,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
23
app/javascript/dashboard/components/policy.vue
Normal file
23
app/javascript/dashboard/components/policy.vue
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<script setup>
|
||||||
|
import { useStoreGetters } from 'dashboard/composables/store';
|
||||||
|
import { computed } from 'vue';
|
||||||
|
import { hasPermissions } from '../helper/permissionsHelper';
|
||||||
|
const props = defineProps({
|
||||||
|
permissions: {
|
||||||
|
type: Array,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const getters = useStoreGetters();
|
||||||
|
const user = getters.getCurrentUser.value;
|
||||||
|
const hasPermission = computed(() =>
|
||||||
|
hasPermissions(props.permissions, user.permissions)
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div v-if="hasPermission">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@ -1,5 +1,5 @@
|
|||||||
import { shallowMount } from '@vue/test-utils';
|
import { shallowMount } from '@vue/test-utils';
|
||||||
import SidemenuIcon from '../SidemenuIcon';
|
import SidemenuIcon from '../SidemenuIcon.vue';
|
||||||
|
|
||||||
describe('SidemenuIcon', () => {
|
describe('SidemenuIcon', () => {
|
||||||
test('matches snapshot', () => {
|
test('matches snapshot', () => {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||||
|
|
||||||
exports[`SidemenuIcon matches snapshot 1`] = `
|
exports[`SidemenuIcon > matches snapshot 1`] = `
|
||||||
<button
|
<button
|
||||||
class="-ml-3 text-black-900 dark:text-slate-300"
|
class="-ml-3 text-black-900 dark:text-slate-300"
|
||||||
color-scheme="secondary"
|
color-scheme="secondary"
|
||||||
|
|||||||
@ -18,11 +18,11 @@ describe('Date formatting functions', () => {
|
|||||||
const testDate = new Date(2020, 4, 15); // May 15, 2020
|
const testDate = new Date(2020, 4, 15); // May 15, 2020
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
jest.spyOn(navigator, 'language', 'get').mockReturnValue('en-US');
|
vi.spyOn(navigator, 'language', 'get').mockReturnValue('en-US');
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
jest.restoreAllMocks();
|
vi.restoreAllMocks();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns the correct month name from a date', () => {
|
it('returns the correct month name from a date', () => {
|
||||||
@ -39,7 +39,7 @@ describe('Date formatting functions', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('returns the correct date format for the current locale en-IN', () => {
|
it('returns the correct date format for the current locale en-IN', () => {
|
||||||
jest.spyOn(navigator, 'language', 'get').mockReturnValue('en-IN');
|
vi.spyOn(navigator, 'language', 'get').mockReturnValue('en-IN');
|
||||||
const expected = 'dd/MM/yyyy';
|
const expected = 'dd/MM/yyyy';
|
||||||
expect(getIntlDateFormatForLocale()).toBe(expected);
|
expect(getIntlDateFormatForLocale()).toBe(expected);
|
||||||
});
|
});
|
||||||
@ -231,13 +231,13 @@ describe('isHoveringNextDayInRange', () => {
|
|||||||
describe('getActiveDateRange', () => {
|
describe('getActiveDateRange', () => {
|
||||||
const currentDate = new Date(2020, 5, 15, 12, 0); // May 15, 2020, at noon
|
const currentDate = new Date(2020, 5, 15, 12, 0); // May 15, 2020, at noon
|
||||||
|
|
||||||
beforeAll(() => {
|
beforeEach(() => {
|
||||||
// Mocking the current date to ensure consistency in tests
|
// Mocking the current date to ensure consistency in tests
|
||||||
jest.useFakeTimers().setSystemTime(currentDate.getTime());
|
vi.useFakeTimers().setSystemTime(currentDate.getTime());
|
||||||
});
|
});
|
||||||
|
|
||||||
afterAll(() => {
|
afterEach(() => {
|
||||||
jest.useRealTimers();
|
vi.useRealTimers();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns the correct range for "last7days"', () => {
|
it('returns the correct range for "last7days"', () => {
|
||||||
|
|||||||
@ -127,7 +127,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="shouldShowContextMenu"
|
v-if="shouldShowContextMenu"
|
||||||
class="context-menu-wrap invisible group-hover:visible"
|
class="invisible context-menu-wrap group-hover:visible"
|
||||||
>
|
>
|
||||||
<context-menu
|
<context-menu
|
||||||
v-if="isBubble && !isMessageDeleted"
|
v-if="isBubble && !isMessageDeleted"
|
||||||
@ -166,6 +166,7 @@ import { ACCOUNT_EVENTS } from 'dashboard/helper/AnalyticsHelper/events';
|
|||||||
import { LOCAL_STORAGE_KEYS } from 'dashboard/constants/localStorage';
|
import { LOCAL_STORAGE_KEYS } from 'dashboard/constants/localStorage';
|
||||||
import { LocalStorage } from 'shared/helpers/localStorage';
|
import { LocalStorage } from 'shared/helpers/localStorage';
|
||||||
import { getDayDifferenceFromNow } from 'shared/helpers/DateHelper';
|
import { getDayDifferenceFromNow } from 'shared/helpers/DateHelper';
|
||||||
|
import * as Sentry from '@sentry/browser';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@ -502,15 +503,29 @@ export default {
|
|||||||
},
|
},
|
||||||
hasMediaAttachment(type) {
|
hasMediaAttachment(type) {
|
||||||
if (this.hasAttachments && this.data.attachments.length > 0) {
|
if (this.hasAttachments && this.data.attachments.length > 0) {
|
||||||
const { attachments = [{}] } = this.data;
|
return this.compareMessageFileType(this.data, type);
|
||||||
const { file_type: fileType } = attachments[0];
|
|
||||||
return fileType === type && !this.hasMediaLoadError;
|
|
||||||
}
|
}
|
||||||
if (this.storyReply) {
|
if (this.storyReply) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
|
compareMessageFileType(messageData, type) {
|
||||||
|
try {
|
||||||
|
const { attachments = [{}] } = messageData;
|
||||||
|
const { file_type: fileType } = attachments[0];
|
||||||
|
return fileType === type && !this.hasMediaLoadError;
|
||||||
|
} catch (err) {
|
||||||
|
Sentry.setContext('attachment-parsing-error', {
|
||||||
|
messageData,
|
||||||
|
type,
|
||||||
|
hasMediaLoadError: this.hasMediaLoadError,
|
||||||
|
});
|
||||||
|
|
||||||
|
Sentry.captureException(err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
handleContextMenuClick() {
|
handleContextMenuClick() {
|
||||||
this.showContextMenu = !this.showContextMenu;
|
this.showContextMenu = !this.showContextMenu;
|
||||||
},
|
},
|
||||||
|
|||||||
@ -197,7 +197,7 @@ import {
|
|||||||
import { LOCAL_STORAGE_KEYS } from 'dashboard/constants/localStorage';
|
import { LOCAL_STORAGE_KEYS } from 'dashboard/constants/localStorage';
|
||||||
import { LocalStorage } from 'shared/helpers/localStorage';
|
import { LocalStorage } from 'shared/helpers/localStorage';
|
||||||
|
|
||||||
const EmojiInput = () => import('shared/components/emoji/EmojiInput');
|
const EmojiInput = () => import('shared/components/emoji/EmojiInput.vue');
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
|||||||
@ -2,16 +2,16 @@ import { createLocalVue, mount } from '@vue/test-utils';
|
|||||||
import Vuex from 'vuex';
|
import Vuex from 'vuex';
|
||||||
import VueI18n from 'vue-i18n';
|
import VueI18n from 'vue-i18n';
|
||||||
import VTooltip from 'v-tooltip';
|
import VTooltip from 'v-tooltip';
|
||||||
import Button from 'dashboard/components/buttons/Button';
|
import Button from 'dashboard/components/buttons/Button.vue';
|
||||||
import i18n from 'dashboard/i18n';
|
import i18n from 'dashboard/i18n';
|
||||||
import FluentIcon from 'shared/components/FluentIcon/DashboardIcon';
|
import FluentIcon from 'shared/components/FluentIcon/DashboardIcon.vue';
|
||||||
import MoreActions from '../MoreActions';
|
import MoreActions from '../MoreActions.vue';
|
||||||
|
|
||||||
jest.mock('shared/helpers/mitt', () => ({
|
vi.mock('shared/helpers/mitt', () => ({
|
||||||
emitter: {
|
emitter: {
|
||||||
emit: jest.fn(),
|
emit: vi.fn(),
|
||||||
on: jest.fn(),
|
on: vi.fn(),
|
||||||
off: jest.fn(),
|
off: vi.fn(),
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -26,9 +26,9 @@ localVue.component('fluent-icon', FluentIcon);
|
|||||||
localVue.component('woot-button', Button);
|
localVue.component('woot-button', Button);
|
||||||
|
|
||||||
localVue.prototype.$emitter = {
|
localVue.prototype.$emitter = {
|
||||||
emit: jest.fn(),
|
emit: vi.fn(),
|
||||||
on: jest.fn(),
|
on: vi.fn(),
|
||||||
off: jest.fn(),
|
off: vi.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
const i18nConfig = new VueI18n({ locale: 'en', messages: i18n });
|
const i18nConfig = new VueI18n({ locale: 'en', messages: i18n });
|
||||||
@ -49,8 +49,8 @@ describe('MoveActions', () => {
|
|||||||
currentChat,
|
currentChat,
|
||||||
};
|
};
|
||||||
|
|
||||||
muteConversation = jest.fn(() => Promise.resolve());
|
muteConversation = vi.fn(() => Promise.resolve());
|
||||||
unmuteConversation = jest.fn(() => Promise.resolve());
|
unmuteConversation = vi.fn(() => Promise.resolve());
|
||||||
|
|
||||||
modules = {
|
modules = {
|
||||||
conversations: { actions: { muteConversation, unmuteConversation } },
|
conversations: { actions: { muteConversation, unmuteConversation } },
|
||||||
|
|||||||
@ -8,7 +8,7 @@ const buildComponent = ({ data = {}, methods = {} }) => ({
|
|||||||
data() {
|
data() {
|
||||||
return { ...data, selectedIndex: 0, items: [1, 2, 3] };
|
return { ...data, selectedIndex: 0, items: [1, 2, 3] };
|
||||||
},
|
},
|
||||||
methods: { ...methods, onSelect: jest.fn(), adjustScroll: jest.fn() },
|
methods: { ...methods, onSelect: vi.fn(), adjustScroll: vi.fn() },
|
||||||
mixins: [keyboardEventListenerMixins],
|
mixins: [keyboardEventListenerMixins],
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -21,7 +21,7 @@ describe('mentionSelectionKeyboardMixin', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('ArrowUp and Control+KeyP update selectedIndex correctly', () => {
|
it('ArrowUp and Control+KeyP update selectedIndex correctly', () => {
|
||||||
const preventDefault = jest.fn();
|
const preventDefault = vi.fn();
|
||||||
const keyboardEvents = wrapper.vm.getKeyboardEvents();
|
const keyboardEvents = wrapper.vm.getKeyboardEvents();
|
||||||
|
|
||||||
if (keyboardEvents && keyboardEvents.ArrowUp) {
|
if (keyboardEvents && keyboardEvents.ArrowUp) {
|
||||||
@ -39,7 +39,7 @@ describe('mentionSelectionKeyboardMixin', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('ArrowDown and Control+KeyN update selectedIndex correctly', () => {
|
it('ArrowDown and Control+KeyN update selectedIndex correctly', () => {
|
||||||
const preventDefault = jest.fn();
|
const preventDefault = vi.fn();
|
||||||
const keyboardEvents = wrapper.vm.getKeyboardEvents();
|
const keyboardEvents = wrapper.vm.getKeyboardEvents();
|
||||||
|
|
||||||
if (keyboardEvents && keyboardEvents.ArrowDown) {
|
if (keyboardEvents && keyboardEvents.ArrowDown) {
|
||||||
@ -57,7 +57,7 @@ describe('mentionSelectionKeyboardMixin', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('Enter key triggers onSelect method', () => {
|
it('Enter key triggers onSelect method', () => {
|
||||||
const preventDefault = jest.fn();
|
const preventDefault = vi.fn();
|
||||||
const keyboardEvents = wrapper.vm.getKeyboardEvents();
|
const keyboardEvents = wrapper.vm.getKeyboardEvents();
|
||||||
|
|
||||||
if (keyboardEvents && keyboardEvents.Enter) {
|
if (keyboardEvents && keyboardEvents.Enter) {
|
||||||
|
|||||||
@ -2,17 +2,17 @@ import { shallowMount } from '@vue/test-utils';
|
|||||||
import { emitter } from 'shared/helpers/mitt';
|
import { emitter } from 'shared/helpers/mitt';
|
||||||
import { useEmitter } from '../emitter';
|
import { useEmitter } from '../emitter';
|
||||||
|
|
||||||
jest.mock('shared/helpers/mitt', () => ({
|
vi.mock('shared/helpers/mitt', () => ({
|
||||||
emitter: {
|
emitter: {
|
||||||
on: jest.fn(),
|
on: vi.fn(),
|
||||||
off: jest.fn(),
|
off: vi.fn(),
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe('useEmitter', () => {
|
describe('useEmitter', () => {
|
||||||
let wrapper;
|
let wrapper;
|
||||||
const eventName = 'my-event';
|
const eventName = 'my-event';
|
||||||
const callback = jest.fn();
|
const callback = vi.fn();
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
wrapper = shallowMount({
|
wrapper = shallowMount({
|
||||||
@ -29,10 +29,6 @@ describe('useEmitter', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
jest.resetAllMocks();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should add an event listener on mount', () => {
|
it('should add an event listener on mount', () => {
|
||||||
expect(emitter.on).toHaveBeenCalledWith(eventName, callback);
|
expect(emitter.on).toHaveBeenCalledWith(eventName, callback);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -38,9 +38,10 @@ export class AnalyticsHelper {
|
|||||||
* @param {Object} user - User object
|
* @param {Object} user - User object
|
||||||
*/
|
*/
|
||||||
identify(user) {
|
identify(user) {
|
||||||
if (!this.analytics) {
|
if (!this.analytics || !user) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.user = user;
|
this.user = user;
|
||||||
this.analytics.identify(this.user.email, {
|
this.analytics.identify(this.user.email, {
|
||||||
userId: this.user.id,
|
userId: this.user.id,
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
import helperObject, { AnalyticsHelper } from '../';
|
import helperObject, { AnalyticsHelper } from '../';
|
||||||
|
|
||||||
jest.mock('@june-so/analytics-next', () => ({
|
vi.mock('@june-so/analytics-next', () => ({
|
||||||
AnalyticsBrowser: {
|
AnalyticsBrowser: {
|
||||||
load: () => [
|
load: () => [
|
||||||
{
|
{
|
||||||
identify: jest.fn(),
|
identify: vi.fn(),
|
||||||
track: jest.fn(),
|
track: vi.fn(),
|
||||||
page: jest.fn(),
|
page: vi.fn(),
|
||||||
group: jest.fn(),
|
group: vi.fn(),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -40,7 +40,7 @@ describe('AnalyticsHelper', () => {
|
|||||||
|
|
||||||
describe('identify', () => {
|
describe('identify', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
analyticsHelper.analytics = { identify: jest.fn(), group: jest.fn() };
|
analyticsHelper.analytics = { identify: vi.fn(), group: vi.fn() };
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call identify on analytics browser with correct arguments', () => {
|
it('should call identify on analytics browser with correct arguments', () => {
|
||||||
@ -87,7 +87,7 @@ describe('AnalyticsHelper', () => {
|
|||||||
|
|
||||||
describe('track', () => {
|
describe('track', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
analyticsHelper.analytics = { track: jest.fn() };
|
analyticsHelper.analytics = { track: vi.fn() };
|
||||||
analyticsHelper.user = { id: '123' };
|
analyticsHelper.user = { id: '123' };
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -118,7 +118,7 @@ describe('AnalyticsHelper', () => {
|
|||||||
|
|
||||||
describe('page', () => {
|
describe('page', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
analyticsHelper.analytics = { page: jest.fn() };
|
analyticsHelper.analytics = { page: vi.fn() };
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call the analytics.page method with the correct arguments', () => {
|
it('should call the analytics.page method with the correct arguments', () => {
|
||||||
|
|||||||
@ -2,34 +2,35 @@ import Vue from 'vue';
|
|||||||
import plugin from '../plugin';
|
import plugin from '../plugin';
|
||||||
import analyticsHelper from '../index';
|
import analyticsHelper from '../index';
|
||||||
|
|
||||||
|
vi.spyOn(analyticsHelper, 'init');
|
||||||
|
vi.spyOn(analyticsHelper, 'track');
|
||||||
|
|
||||||
describe('Vue Analytics Plugin', () => {
|
describe('Vue Analytics Plugin', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
jest.spyOn(analyticsHelper, 'init');
|
|
||||||
jest.spyOn(analyticsHelper, 'track');
|
|
||||||
Vue.use(plugin);
|
Vue.use(plugin);
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
it('should call the init method on analyticsHelper once during plugin installation', () => {
|
||||||
jest.resetModules();
|
expect(analyticsHelper.init).toHaveBeenCalledTimes(1);
|
||||||
jest.clearAllMocks();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call the init method on the analyticsHelper', () => {
|
it('should add the analyticsHelper to the Vue prototype as $analytics', () => {
|
||||||
expect(analyticsHelper.init).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should add the analyticsHelper to the Vue prototype', () => {
|
|
||||||
expect(Vue.prototype.$analytics).toBe(analyticsHelper);
|
expect(Vue.prototype.$analytics).toBe(analyticsHelper);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should add the track method to the Vue prototype', () => {
|
it('should add a track method to the Vue prototype as $track', () => {
|
||||||
expect(typeof Vue.prototype.$track).toBe('function');
|
expect(typeof Vue.prototype.$track).toBe('function');
|
||||||
Vue.prototype.$track('eventName');
|
Vue.prototype.$track('eventName');
|
||||||
expect(analyticsHelper.track).toHaveBeenCalledWith('eventName');
|
expect(analyticsHelper.track)
|
||||||
|
.toHaveBeenCalledTimes(1)
|
||||||
|
.toHaveBeenCalledWith('eventName');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call the track method on the analyticsHelper when $track is called', () => {
|
it('should call the track method on analyticsHelper with the correct event name when $track is called', () => {
|
||||||
Vue.prototype.$track('eventName');
|
const eventName = 'testEvent';
|
||||||
expect(analyticsHelper.track).toHaveBeenCalledWith('eventName');
|
Vue.prototype.$track(eventName);
|
||||||
|
expect(analyticsHelper.track)
|
||||||
|
.toHaveBeenCalledTimes(1)
|
||||||
|
.toHaveBeenCalledWith(eventName);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -52,6 +52,10 @@ class DashboardAudioNotificationHelper {
|
|||||||
};
|
};
|
||||||
|
|
||||||
executeRecurringNotification = () => {
|
executeRecurringNotification = () => {
|
||||||
|
if (!window.WOOT || !window.WOOT.$store) {
|
||||||
|
this.clearSetTimeout();
|
||||||
|
return;
|
||||||
|
}
|
||||||
const mineConversation = window.WOOT.$store.getters.getMineChats({
|
const mineConversation = window.WOOT.$store.getters.getMineChats({
|
||||||
assigneeType: 'me',
|
assigneeType: 'me',
|
||||||
status: 'open',
|
status: 'open',
|
||||||
|
|||||||
@ -9,6 +9,11 @@ import {
|
|||||||
|
|
||||||
const MAX_DISCONNECT_SECONDS = 10800;
|
const MAX_DISCONNECT_SECONDS = 10800;
|
||||||
|
|
||||||
|
// The disconnect delay threshold is added to account for delays in identifying
|
||||||
|
// disconnections (for example, the websocket disconnection takes up to 3 seconds)
|
||||||
|
// while fetching the latest updated conversations or messages.
|
||||||
|
const DISCONNECT_DELAY_THRESHOLD = 15;
|
||||||
|
|
||||||
class ReconnectService {
|
class ReconnectService {
|
||||||
constructor(store, router) {
|
constructor(store, router) {
|
||||||
this.store = store;
|
this.store = store;
|
||||||
@ -47,7 +52,8 @@ class ReconnectService {
|
|||||||
fetchConversations = async () => {
|
fetchConversations = async () => {
|
||||||
await this.store.dispatch('updateChatListFilters', {
|
await this.store.dispatch('updateChatListFilters', {
|
||||||
page: null,
|
page: null,
|
||||||
updatedWithin: this.getSecondsSinceDisconnect(),
|
updatedWithin:
|
||||||
|
this.getSecondsSinceDisconnect() + DISCONNECT_DELAY_THRESHOLD,
|
||||||
});
|
});
|
||||||
await this.store.dispatch('fetchAllConversations');
|
await this.store.dispatch('fetchAllConversations');
|
||||||
// Reset the updatedWithin in the store chat list filter after fetching conversations when the user is reconnected
|
// Reset the updatedWithin in the store chat list filter after fetching conversations when the user is reconnected
|
||||||
|
|||||||
34
app/javascript/dashboard/helper/permissionsHelper.js
Normal file
34
app/javascript/dashboard/helper/permissionsHelper.js
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
export const hasPermissions = (
|
||||||
|
requiredPermissions = [],
|
||||||
|
availablePermissions = []
|
||||||
|
) => {
|
||||||
|
return requiredPermissions.some(permission =>
|
||||||
|
availablePermissions.includes(permission)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const isPermissionsPresentInRoute = route =>
|
||||||
|
route.meta && route.meta.permissions;
|
||||||
|
|
||||||
|
export const buildPermissionsFromRouter = (routes = []) =>
|
||||||
|
routes.reduce((acc, route) => {
|
||||||
|
if (route.name) {
|
||||||
|
if (!isPermissionsPresentInRoute(route)) {
|
||||||
|
// eslint-disable-next-line
|
||||||
|
console.error(route);
|
||||||
|
throw new Error(
|
||||||
|
"The route doesn't have the required permissions defined"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
acc[route.name] = route.meta.permissions;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (route.children) {
|
||||||
|
acc = {
|
||||||
|
...acc,
|
||||||
|
...buildPermissionsFromRouter(route.children),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
@ -1,19 +1,16 @@
|
|||||||
|
import { hasPermissions } from './permissionsHelper';
|
||||||
|
|
||||||
// eslint-disable-next-line default-param-last
|
// eslint-disable-next-line default-param-last
|
||||||
export const getCurrentAccount = ({ accounts } = {}, accountId) => {
|
export const getCurrentAccount = ({ accounts } = {}, accountId) => {
|
||||||
return accounts.find(account => account.id === accountId);
|
return accounts.find(account => account.id === accountId);
|
||||||
};
|
};
|
||||||
|
|
||||||
// eslint-disable-next-line default-param-last
|
export const routeIsAccessibleFor = (route, userPermissions = []) => {
|
||||||
export const getUserRole = ({ accounts } = {}, accountId) => {
|
const { meta: { permissions: routePermissions = [] } = {} } = route;
|
||||||
const currentAccount = getCurrentAccount({ accounts }, accountId) || {};
|
return hasPermissions(routePermissions, userPermissions);
|
||||||
return currentAccount.role || null;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const routeIsAccessibleFor = (route, role, roleWiseRoutes) => {
|
const validateActiveAccountRoutes = (to, user) => {
|
||||||
return roleWiseRoutes[role].includes(route);
|
|
||||||
};
|
|
||||||
|
|
||||||
const validateActiveAccountRoutes = (to, user, roleWiseRoutes) => {
|
|
||||||
// If the current account is active, then check for the route permissions
|
// If the current account is active, then check for the route permissions
|
||||||
const accountDashboardURL = `accounts/${to.params.accountId}/dashboard`;
|
const accountDashboardURL = `accounts/${to.params.accountId}/dashboard`;
|
||||||
|
|
||||||
@ -22,15 +19,13 @@ const validateActiveAccountRoutes = (to, user, roleWiseRoutes) => {
|
|||||||
return accountDashboardURL;
|
return accountDashboardURL;
|
||||||
}
|
}
|
||||||
|
|
||||||
const userRole = getUserRole(user, Number(to.params.accountId));
|
const isAccessible = routeIsAccessibleFor(to, user.permissions);
|
||||||
const isAccessible = routeIsAccessibleFor(to.name, userRole, roleWiseRoutes);
|
|
||||||
// If the route is not accessible for the user, return to dashboard screen
|
// If the route is not accessible for the user, return to dashboard screen
|
||||||
return isAccessible ? null : accountDashboardURL;
|
return isAccessible ? null : accountDashboardURL;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const validateLoggedInRoutes = (to, user, roleWiseRoutes) => {
|
export const validateLoggedInRoutes = (to, user) => {
|
||||||
const currentAccount = getCurrentAccount(user, Number(to.params.accountId));
|
const currentAccount = getCurrentAccount(user, Number(to.params.accountId));
|
||||||
|
|
||||||
// If current account is missing, either user does not have
|
// If current account is missing, either user does not have
|
||||||
// access to the account or the account is deleted, return to login screen
|
// access to the account or the account is deleted, return to login screen
|
||||||
if (!currentAccount) {
|
if (!currentAccount) {
|
||||||
@ -40,7 +35,7 @@ export const validateLoggedInRoutes = (to, user, roleWiseRoutes) => {
|
|||||||
const isCurrentAccountActive = currentAccount.status === 'active';
|
const isCurrentAccountActive = currentAccount.status === 'active';
|
||||||
|
|
||||||
if (isCurrentAccountActive) {
|
if (isCurrentAccountActive) {
|
||||||
return validateActiveAccountRoutes(to, user, roleWiseRoutes);
|
return validateActiveAccountRoutes(to, user);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the current account is not active, then redirect the user to the suspended screen
|
// If the current account is not active, then redirect the user to the suspended screen
|
||||||
|
|||||||
@ -4,7 +4,7 @@ describe('DataManager', () => {
|
|||||||
const accountId = 'test-account';
|
const accountId = 'test-account';
|
||||||
let dataManager;
|
let dataManager;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeEach(async () => {
|
||||||
dataManager = new DataManager(accountId);
|
dataManager = new DataManager(accountId);
|
||||||
await dataManager.initDb();
|
await dataManager.initDb();
|
||||||
});
|
});
|
||||||
|
|||||||
@ -8,26 +8,26 @@ import {
|
|||||||
} from 'dashboard/helper/routeHelpers';
|
} from 'dashboard/helper/routeHelpers';
|
||||||
import ReconnectService from 'dashboard/helper/ReconnectService';
|
import ReconnectService from 'dashboard/helper/ReconnectService';
|
||||||
|
|
||||||
jest.mock('shared/helpers/mitt', () => ({
|
vi.mock('shared/helpers/mitt', () => ({
|
||||||
emitter: {
|
emitter: {
|
||||||
on: jest.fn(),
|
on: vi.fn(),
|
||||||
off: jest.fn(),
|
off: vi.fn(),
|
||||||
emit: jest.fn(),
|
emit: vi.fn(),
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock('date-fns', () => ({
|
vi.mock('date-fns', () => ({
|
||||||
differenceInSeconds: jest.fn(),
|
differenceInSeconds: vi.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock('dashboard/helper/routeHelpers', () => ({
|
vi.mock('dashboard/helper/routeHelpers', () => ({
|
||||||
isAConversationRoute: jest.fn(),
|
isAConversationRoute: vi.fn(),
|
||||||
isAInboxViewRoute: jest.fn(),
|
isAInboxViewRoute: vi.fn(),
|
||||||
isNotificationRoute: jest.fn(),
|
isNotificationRoute: vi.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const storeMock = {
|
const storeMock = {
|
||||||
dispatch: jest.fn(),
|
dispatch: vi.fn(),
|
||||||
getters: {
|
getters: {
|
||||||
getAppliedConversationFiltersQuery: [],
|
getAppliedConversationFiltersQuery: [],
|
||||||
'customViews/getActiveConversationFolder': { query: {} },
|
'customViews/getActiveConversationFolder': { query: {} },
|
||||||
@ -46,17 +46,17 @@ describe('ReconnectService', () => {
|
|||||||
let reconnectService;
|
let reconnectService;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
window.addEventListener = jest.fn();
|
window.addEventListener = vi.fn();
|
||||||
window.removeEventListener = jest.fn();
|
window.removeEventListener = vi.fn();
|
||||||
Object.defineProperty(window, 'location', {
|
Object.defineProperty(window, 'location', {
|
||||||
configurable: true,
|
configurable: true,
|
||||||
value: { reload: jest.fn() },
|
value: { reload: vi.fn() },
|
||||||
});
|
});
|
||||||
reconnectService = new ReconnectService(storeMock, routerMock);
|
reconnectService = new ReconnectService(storeMock, routerMock);
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
jest.clearAllMocks();
|
vi.clearAllMocks();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('constructor', () => {
|
describe('constructor', () => {
|
||||||
@ -102,7 +102,7 @@ describe('ReconnectService', () => {
|
|||||||
expect(reconnectService.getSecondsSinceDisconnect()).toBe(0);
|
expect(reconnectService.getSecondsSinceDisconnect()).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return the number of seconds since disconnect', () => {
|
it('should return the number of seconds + threshold since disconnect', () => {
|
||||||
reconnectService.disconnectTime = new Date();
|
reconnectService.disconnectTime = new Date();
|
||||||
differenceInSeconds.mockReturnValue(100);
|
differenceInSeconds.mockReturnValue(100);
|
||||||
expect(reconnectService.getSecondsSinceDisconnect()).toBe(100);
|
expect(reconnectService.getSecondsSinceDisconnect()).toBe(100);
|
||||||
@ -111,7 +111,7 @@ describe('ReconnectService', () => {
|
|||||||
|
|
||||||
describe('handleOnlineEvent', () => {
|
describe('handleOnlineEvent', () => {
|
||||||
it('should reload the page if disconnected for more than 3 hours', () => {
|
it('should reload the page if disconnected for more than 3 hours', () => {
|
||||||
reconnectService.getSecondsSinceDisconnect = jest
|
reconnectService.getSecondsSinceDisconnect = vi
|
||||||
.fn()
|
.fn()
|
||||||
.mockReturnValue(10801);
|
.mockReturnValue(10801);
|
||||||
reconnectService.handleOnlineEvent();
|
reconnectService.handleOnlineEvent();
|
||||||
@ -119,7 +119,7 @@ describe('ReconnectService', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should not reload the page if disconnected for less than 3 hours', () => {
|
it('should not reload the page if disconnected for less than 3 hours', () => {
|
||||||
reconnectService.getSecondsSinceDisconnect = jest
|
reconnectService.getSecondsSinceDisconnect = vi
|
||||||
.fn()
|
.fn()
|
||||||
.mockReturnValue(10799);
|
.mockReturnValue(10799);
|
||||||
reconnectService.handleOnlineEvent();
|
reconnectService.handleOnlineEvent();
|
||||||
@ -128,22 +128,27 @@ describe('ReconnectService', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('fetchConversations', () => {
|
describe('fetchConversations', () => {
|
||||||
it('should dispatch updateChatListFilters and fetchAllConversations', async () => {
|
it('should update the filters with disconnected time and the threshold', async () => {
|
||||||
reconnectService.getSecondsSinceDisconnect = jest
|
reconnectService.getSecondsSinceDisconnect = vi.fn().mockReturnValue(100);
|
||||||
.fn()
|
|
||||||
.mockReturnValue(100);
|
|
||||||
await reconnectService.fetchConversations();
|
await reconnectService.fetchConversations();
|
||||||
expect(storeMock.dispatch).toHaveBeenCalledWith('updateChatListFilters', {
|
expect(storeMock.dispatch).toHaveBeenCalledWith('updateChatListFilters', {
|
||||||
page: null,
|
page: null,
|
||||||
updatedWithin: 100,
|
updatedWithin: 115,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should dispatch updateChatListFilters and fetchAllConversations', async () => {
|
||||||
|
reconnectService.getSecondsSinceDisconnect = vi.fn().mockReturnValue(100);
|
||||||
|
await reconnectService.fetchConversations();
|
||||||
|
expect(storeMock.dispatch).toHaveBeenCalledWith('updateChatListFilters', {
|
||||||
|
page: null,
|
||||||
|
updatedWithin: 115,
|
||||||
});
|
});
|
||||||
expect(storeMock.dispatch).toHaveBeenCalledWith('fetchAllConversations');
|
expect(storeMock.dispatch).toHaveBeenCalledWith('fetchAllConversations');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should dispatch updateChatListFilters and reset updatedWithin', async () => {
|
it('should dispatch updateChatListFilters and reset updatedWithin', async () => {
|
||||||
reconnectService.getSecondsSinceDisconnect = jest
|
reconnectService.getSecondsSinceDisconnect = vi.fn().mockReturnValue(100);
|
||||||
.fn()
|
|
||||||
.mockReturnValue(100);
|
|
||||||
await reconnectService.fetchConversations();
|
await reconnectService.fetchConversations();
|
||||||
expect(storeMock.dispatch).toHaveBeenCalledWith('updateChatListFilters', {
|
expect(storeMock.dispatch).toHaveBeenCalledWith('updateChatListFilters', {
|
||||||
updatedWithin: null,
|
updatedWithin: null,
|
||||||
@ -173,7 +178,7 @@ describe('ReconnectService', () => {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
const spy = jest.spyOn(
|
const spy = vi.spyOn(
|
||||||
reconnectService,
|
reconnectService,
|
||||||
'fetchFilteredOrSavedConversations'
|
'fetchFilteredOrSavedConversations'
|
||||||
);
|
);
|
||||||
@ -191,7 +196,7 @@ describe('ReconnectService', () => {
|
|||||||
query: null,
|
query: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
const spy = jest.spyOn(reconnectService, 'fetchConversations');
|
const spy = vi.spyOn(reconnectService, 'fetchConversations');
|
||||||
|
|
||||||
await reconnectService.fetchConversationsOnReconnect();
|
await reconnectService.fetchConversationsOnReconnect();
|
||||||
|
|
||||||
@ -204,7 +209,7 @@ describe('ReconnectService', () => {
|
|||||||
query: { test: 'activeFolderQuery' },
|
query: { test: 'activeFolderQuery' },
|
||||||
};
|
};
|
||||||
|
|
||||||
const spy = jest.spyOn(
|
const spy = vi.spyOn(
|
||||||
reconnectService,
|
reconnectService,
|
||||||
'fetchFilteredOrSavedConversations'
|
'fetchFilteredOrSavedConversations'
|
||||||
);
|
);
|
||||||
@ -270,11 +275,11 @@ describe('ReconnectService', () => {
|
|||||||
describe('handleRouteSpecificFetch', () => {
|
describe('handleRouteSpecificFetch', () => {
|
||||||
it('should fetch conversations and messages if current route is a conversation route', async () => {
|
it('should fetch conversations and messages if current route is a conversation route', async () => {
|
||||||
isAConversationRoute.mockReturnValue(true);
|
isAConversationRoute.mockReturnValue(true);
|
||||||
const spyConversations = jest.spyOn(
|
const spyConversations = vi.spyOn(
|
||||||
reconnectService,
|
reconnectService,
|
||||||
'fetchConversationsOnReconnect'
|
'fetchConversationsOnReconnect'
|
||||||
);
|
);
|
||||||
const spyMessages = jest.spyOn(
|
const spyMessages = vi.spyOn(
|
||||||
reconnectService,
|
reconnectService,
|
||||||
'fetchConversationMessagesOnReconnect'
|
'fetchConversationMessagesOnReconnect'
|
||||||
);
|
);
|
||||||
@ -285,14 +290,14 @@ describe('ReconnectService', () => {
|
|||||||
|
|
||||||
it('should fetch notifications if current route is an inbox view route', async () => {
|
it('should fetch notifications if current route is an inbox view route', async () => {
|
||||||
isAInboxViewRoute.mockReturnValue(true);
|
isAInboxViewRoute.mockReturnValue(true);
|
||||||
const spy = jest.spyOn(reconnectService, 'fetchNotificationsOnReconnect');
|
const spy = vi.spyOn(reconnectService, 'fetchNotificationsOnReconnect');
|
||||||
await reconnectService.handleRouteSpecificFetch();
|
await reconnectService.handleRouteSpecificFetch();
|
||||||
expect(spy).toHaveBeenCalled();
|
expect(spy).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fetch notifications if current route is a notification route', async () => {
|
it('should fetch notifications if current route is a notification route', async () => {
|
||||||
isNotificationRoute.mockReturnValue(true);
|
isNotificationRoute.mockReturnValue(true);
|
||||||
const spy = jest.spyOn(reconnectService, 'fetchNotificationsOnReconnect');
|
const spy = vi.spyOn(reconnectService, 'fetchNotificationsOnReconnect');
|
||||||
await reconnectService.handleRouteSpecificFetch();
|
await reconnectService.handleRouteSpecificFetch();
|
||||||
expect(spy).toHaveBeenCalled();
|
expect(spy).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
@ -320,7 +325,7 @@ describe('ReconnectService', () => {
|
|||||||
|
|
||||||
describe('onDisconnect', () => {
|
describe('onDisconnect', () => {
|
||||||
it('should set disconnectTime and call setConversationLastMessageId', () => {
|
it('should set disconnectTime and call setConversationLastMessageId', () => {
|
||||||
reconnectService.setConversationLastMessageId = jest.fn();
|
reconnectService.setConversationLastMessageId = vi.fn();
|
||||||
reconnectService.onDisconnect();
|
reconnectService.onDisconnect();
|
||||||
expect(reconnectService.disconnectTime).toBeInstanceOf(Date);
|
expect(reconnectService.disconnectTime).toBeInstanceOf(Date);
|
||||||
expect(reconnectService.setConversationLastMessageId).toHaveBeenCalled();
|
expect(reconnectService.setConversationLastMessageId).toHaveBeenCalled();
|
||||||
@ -329,8 +334,8 @@ describe('ReconnectService', () => {
|
|||||||
|
|
||||||
describe('onReconnect', () => {
|
describe('onReconnect', () => {
|
||||||
it('should handle route-specific fetch, revalidate caches, and emit WEBSOCKET_RECONNECT_COMPLETED event', async () => {
|
it('should handle route-specific fetch, revalidate caches, and emit WEBSOCKET_RECONNECT_COMPLETED event', async () => {
|
||||||
reconnectService.handleRouteSpecificFetch = jest.fn();
|
reconnectService.handleRouteSpecificFetch = vi.fn();
|
||||||
reconnectService.revalidateCaches = jest.fn();
|
reconnectService.revalidateCaches = vi.fn();
|
||||||
await reconnectService.onReconnect();
|
await reconnectService.onReconnect();
|
||||||
expect(reconnectService.handleRouteSpecificFetch).toHaveBeenCalled();
|
expect(reconnectService.handleRouteSpecificFetch).toHaveBeenCalled();
|
||||||
expect(reconnectService.revalidateCaches).toHaveBeenCalled();
|
expect(reconnectService.revalidateCaches).toHaveBeenCalled();
|
||||||
|
|||||||
@ -36,16 +36,14 @@ describe('#createPendingMessage', () => {
|
|||||||
message: 'hi',
|
message: 'hi',
|
||||||
};
|
};
|
||||||
it('returns the pending message with expected new keys', () => {
|
it('returns the pending message with expected new keys', () => {
|
||||||
expect(createPendingMessage(message)).toHaveProperty(
|
expect(createPendingMessage(message)).toMatchObject({
|
||||||
'content',
|
content: expect.anything(),
|
||||||
'id',
|
id: expect.anything(),
|
||||||
'status',
|
status: expect.anything(),
|
||||||
'echo_id',
|
echo_id: expect.anything(),
|
||||||
'status',
|
created_at: expect.anything(),
|
||||||
'created_at',
|
message_type: expect.anything(),
|
||||||
'message_type',
|
});
|
||||||
'conversation_id'
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns the pending message with status progress', () => {
|
it('returns the pending message with status progress', () => {
|
||||||
@ -61,23 +59,20 @@ describe('#createPendingMessage', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns the pending message with attachmnet key if file is passed', () => {
|
it('returns the pending message with attachment key if file is passed', () => {
|
||||||
const messageWithFile = {
|
const messageWithFile = {
|
||||||
message: 'hi',
|
message: 'hi',
|
||||||
file: {},
|
file: {},
|
||||||
};
|
};
|
||||||
expect(createPendingMessage(messageWithFile)).toHaveProperty(
|
expect(createPendingMessage(messageWithFile)).toMatchObject({
|
||||||
'content',
|
content: expect.anything(),
|
||||||
'id',
|
id: expect.anything(),
|
||||||
'status',
|
status: expect.anything(),
|
||||||
'echo_id',
|
echo_id: expect.anything(),
|
||||||
'status',
|
created_at: expect.anything(),
|
||||||
'created_at',
|
message_type: expect.anything(),
|
||||||
'message_type',
|
attachments: [{ id: expect.anything() }],
|
||||||
'conversation_id',
|
});
|
||||||
'attachments',
|
|
||||||
'private'
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns the pending message to have one attachment', () => {
|
it('returns the pending message to have one attachment', () => {
|
||||||
|
|||||||
@ -19,19 +19,19 @@ describe('resize directive', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
el = document.createElement('div');
|
el = document.createElement('div');
|
||||||
binding = {
|
binding = {
|
||||||
value: jest.fn(),
|
value: vi.fn(),
|
||||||
};
|
};
|
||||||
observer = {
|
observer = {
|
||||||
observe: jest.fn(),
|
observe: vi.fn(),
|
||||||
unobserve: jest.fn(),
|
unobserve: vi.fn(),
|
||||||
disconnect: jest.fn(),
|
disconnect: vi.fn(),
|
||||||
};
|
};
|
||||||
window.ResizeObserver = ResizeObserverMock;
|
window.ResizeObserver = ResizeObserverMock;
|
||||||
jest.spyOn(window, 'ResizeObserver').mockImplementation(() => observer);
|
vi.spyOn(window, 'ResizeObserver').mockImplementation(() => observer);
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
jest.clearAllMocks();
|
vi.clearAllMocks();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create ResizeObserver on bind', () => {
|
it('should create ResizeObserver on bind', () => {
|
||||||
@ -44,7 +44,7 @@ describe('resize directive', () => {
|
|||||||
it('should call callback on observer callback', () => {
|
it('should call callback on observer callback', () => {
|
||||||
el = document.createElement('div');
|
el = document.createElement('div');
|
||||||
binding = {
|
binding = {
|
||||||
value: jest.fn(),
|
value: vi.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
resize.bind(el, binding);
|
resize.bind(el, binding);
|
||||||
|
|||||||
@ -283,17 +283,17 @@ describe('findNodeToInsertImage', () => {
|
|||||||
mockEditorState = {
|
mockEditorState = {
|
||||||
selection: {
|
selection: {
|
||||||
$from: {
|
$from: {
|
||||||
node: jest.fn(() => ({})),
|
node: vi.fn(() => ({})),
|
||||||
},
|
},
|
||||||
from: 0,
|
from: 0,
|
||||||
},
|
},
|
||||||
schema: {
|
schema: {
|
||||||
nodes: {
|
nodes: {
|
||||||
image: {
|
image: {
|
||||||
create: jest.fn(attrs => ({ type: { name: 'image' }, attrs })),
|
create: vi.fn(attrs => ({ type: { name: 'image' }, attrs })),
|
||||||
},
|
},
|
||||||
paragraph: {
|
paragraph: {
|
||||||
create: jest.fn((_, node) => ({
|
create: vi.fn((_, node) => ({
|
||||||
type: { name: 'paragraph' },
|
type: { name: 'paragraph' },
|
||||||
content: [node],
|
content: [node],
|
||||||
})),
|
})),
|
||||||
@ -381,11 +381,11 @@ describe('setURLWithQueryAndSize', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
selectedNode = {
|
selectedNode = {
|
||||||
setAttribute: jest.fn(),
|
setAttribute: vi.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
const tr = {
|
const tr = {
|
||||||
setNodeMarkup: jest.fn().mockReturnValue({
|
setNodeMarkup: vi.fn().mockReturnValue({
|
||||||
docChanged: true,
|
docChanged: true,
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
@ -397,7 +397,7 @@ describe('setURLWithQueryAndSize', () => {
|
|||||||
|
|
||||||
editorView = {
|
editorView = {
|
||||||
state,
|
state,
|
||||||
dispatch: jest.fn(),
|
dispatch: vi.fn(),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -420,7 +420,7 @@ describe('setURLWithQueryAndSize', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('does not update the editor view if the document has not changed', () => {
|
it('does not update the editor view if the document has not changed', () => {
|
||||||
editorView.state.tr.setNodeMarkup = jest.fn().mockReturnValue({
|
editorView.state.tr.setNodeMarkup = vi.fn().mockReturnValue({
|
||||||
docChanged: false,
|
docChanged: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,84 @@
|
|||||||
|
import {
|
||||||
|
buildPermissionsFromRouter,
|
||||||
|
hasPermissions,
|
||||||
|
} from '../permissionsHelper';
|
||||||
|
|
||||||
|
describe('hasPermissions', () => {
|
||||||
|
it('returns true if permission is present', () => {
|
||||||
|
expect(
|
||||||
|
hasPermissions(['contact_manage'], ['team_manage', 'contact_manage'])
|
||||||
|
).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns true if permission is not present', () => {
|
||||||
|
expect(
|
||||||
|
hasPermissions(['contact_manage'], ['team_manage', 'user_manage'])
|
||||||
|
).toBe(false);
|
||||||
|
expect(hasPermissions()).toBe(false);
|
||||||
|
expect(hasPermissions([])).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('buildPermissionsFromRouter', () => {
|
||||||
|
it('returns a valid object when routes have permissions defined', () => {
|
||||||
|
expect(
|
||||||
|
buildPermissionsFromRouter([
|
||||||
|
{
|
||||||
|
path: 'agent',
|
||||||
|
name: 'agent_list',
|
||||||
|
meta: { permissions: ['agent_admin'] },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'inbox',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
name: 'inbox_list',
|
||||||
|
meta: { permissions: ['inbox_admin'] },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'conversations',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'attachments',
|
||||||
|
name: 'attachments_list',
|
||||||
|
meta: { permissions: ['conversation_admin'] },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
).toEqual({
|
||||||
|
agent_list: ['agent_admin'],
|
||||||
|
inbox_list: ['inbox_admin'],
|
||||||
|
attachments_list: ['conversation_admin'],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws an error if a named routed does not have permissions defined', () => {
|
||||||
|
expect(() => {
|
||||||
|
buildPermissionsFromRouter([
|
||||||
|
{
|
||||||
|
path: 'agent',
|
||||||
|
name: 'agent_list',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}).toThrow("The route doesn't have the required permissions defined");
|
||||||
|
|
||||||
|
expect(() => {
|
||||||
|
buildPermissionsFromRouter([
|
||||||
|
{
|
||||||
|
path: 'agent',
|
||||||
|
name: 'agent_list',
|
||||||
|
meta: {},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}).toThrow("The route doesn't have the required permissions defined");
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -1,7 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
getConversationDashboardRoute,
|
getConversationDashboardRoute,
|
||||||
getCurrentAccount,
|
getCurrentAccount,
|
||||||
getUserRole,
|
|
||||||
isAConversationRoute,
|
isAConversationRoute,
|
||||||
routeIsAccessibleFor,
|
routeIsAccessibleFor,
|
||||||
validateLoggedInRoutes,
|
validateLoggedInRoutes,
|
||||||
@ -15,24 +14,11 @@ describe('#getCurrentAccount', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#getUserRole', () => {
|
|
||||||
it('should return the current role', () => {
|
|
||||||
expect(
|
|
||||||
getUserRole({ accounts: [{ id: 1, role: 'administrator' }] }, 1)
|
|
||||||
).toEqual('administrator');
|
|
||||||
expect(getUserRole({ accounts: [] }, 1)).toEqual(null);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#routeIsAccessibleFor', () => {
|
describe('#routeIsAccessibleFor', () => {
|
||||||
it('should return the correct access', () => {
|
it('should return the correct access', () => {
|
||||||
const roleWiseRoutes = { agent: ['conversations'], admin: ['billing'] };
|
let route = { meta: { permissions: ['administrator'] } };
|
||||||
expect(routeIsAccessibleFor('billing', 'agent', roleWiseRoutes)).toEqual(
|
expect(routeIsAccessibleFor(route, ['agent'])).toEqual(false);
|
||||||
false
|
expect(routeIsAccessibleFor(route, ['administrator'])).toEqual(true);
|
||||||
);
|
|
||||||
expect(routeIsAccessibleFor('billing', 'admin', roleWiseRoutes)).toEqual(
|
|
||||||
true
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -40,11 +26,7 @@ describe('#validateLoggedInRoutes', () => {
|
|||||||
describe('when account access is missing', () => {
|
describe('when account access is missing', () => {
|
||||||
it('should return the login route', () => {
|
it('should return the login route', () => {
|
||||||
expect(
|
expect(
|
||||||
validateLoggedInRoutes(
|
validateLoggedInRoutes({ params: { accountId: 1 } }, { accounts: [] })
|
||||||
{ params: { accountId: 1 } },
|
|
||||||
{ accounts: [] },
|
|
||||||
{}
|
|
||||||
)
|
|
||||||
).toEqual(`app/login`);
|
).toEqual(`app/login`);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -53,9 +35,12 @@ describe('#validateLoggedInRoutes', () => {
|
|||||||
it('return suspended route', () => {
|
it('return suspended route', () => {
|
||||||
expect(
|
expect(
|
||||||
validateLoggedInRoutes(
|
validateLoggedInRoutes(
|
||||||
{ name: 'conversations', params: { accountId: 1 } },
|
{
|
||||||
{ accounts: [{ id: 1, role: 'agent', status: 'suspended' }] },
|
name: 'conversations',
|
||||||
{ agent: ['conversations'] }
|
params: { accountId: 1 },
|
||||||
|
meta: { permissions: ['agent'] },
|
||||||
|
},
|
||||||
|
{ accounts: [{ id: 1, role: 'agent', status: 'suspended' }] }
|
||||||
)
|
)
|
||||||
).toEqual(`accounts/1/suspended`);
|
).toEqual(`accounts/1/suspended`);
|
||||||
});
|
});
|
||||||
@ -65,9 +50,22 @@ describe('#validateLoggedInRoutes', () => {
|
|||||||
it('returns null (no action required)', () => {
|
it('returns null (no action required)', () => {
|
||||||
expect(
|
expect(
|
||||||
validateLoggedInRoutes(
|
validateLoggedInRoutes(
|
||||||
{ name: 'conversations', params: { accountId: 1 } },
|
{
|
||||||
{ accounts: [{ id: 1, role: 'agent', status: 'active' }] },
|
name: 'conversations',
|
||||||
{ agent: ['conversations'] }
|
params: { accountId: 1 },
|
||||||
|
meta: { permissions: ['agent'] },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
permissions: ['agent'],
|
||||||
|
accounts: [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
role: 'agent',
|
||||||
|
permissions: ['agent'],
|
||||||
|
status: 'active',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
)
|
)
|
||||||
).toEqual(null);
|
).toEqual(null);
|
||||||
});
|
});
|
||||||
@ -76,9 +74,12 @@ describe('#validateLoggedInRoutes', () => {
|
|||||||
it('returns dashboard url', () => {
|
it('returns dashboard url', () => {
|
||||||
expect(
|
expect(
|
||||||
validateLoggedInRoutes(
|
validateLoggedInRoutes(
|
||||||
{ name: 'conversations', params: { accountId: 1 } },
|
{
|
||||||
{ accounts: [{ id: 1, role: 'agent', status: 'active' }] },
|
name: 'billing',
|
||||||
{ admin: ['conversations'], agent: [] }
|
params: { accountId: 1 },
|
||||||
|
meta: { permissions: ['administrator'] },
|
||||||
|
},
|
||||||
|
{ accounts: [{ id: 1, role: 'agent', status: 'active' }] }
|
||||||
)
|
)
|
||||||
).toEqual(`accounts/1/dashboard`);
|
).toEqual(`accounts/1/dashboard`);
|
||||||
});
|
});
|
||||||
@ -88,8 +89,7 @@ describe('#validateLoggedInRoutes', () => {
|
|||||||
expect(
|
expect(
|
||||||
validateLoggedInRoutes(
|
validateLoggedInRoutes(
|
||||||
{ name: 'account_suspended', params: { accountId: 1 } },
|
{ name: 'account_suspended', params: { accountId: 1 } },
|
||||||
{ accounts: [{ id: 1, role: 'agent', status: 'active' }] },
|
{ accounts: [{ id: 1, role: 'agent', status: 'active' }] }
|
||||||
{ agent: ['account_suspended'] }
|
|
||||||
)
|
)
|
||||||
).toEqual(`accounts/1/dashboard`);
|
).toEqual(`accounts/1/dashboard`);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { setColorTheme } from 'dashboard/helper/themeHelper.js';
|
import { setColorTheme } from 'dashboard/helper/themeHelper.js';
|
||||||
import { LocalStorage } from 'shared/helpers/localStorage';
|
import { LocalStorage } from 'shared/helpers/localStorage';
|
||||||
|
|
||||||
jest.mock('shared/helpers/localStorage');
|
vi.mock('shared/helpers/localStorage');
|
||||||
|
|
||||||
describe('setColorTheme', () => {
|
describe('setColorTheme', () => {
|
||||||
it('should set body class to dark if selectedColorScheme is dark', () => {
|
it('should set body class to dark if selectedColorScheme is dark', () => {
|
||||||
|
|||||||
@ -1,9 +1,8 @@
|
|||||||
import { uploadFile } from '../uploadHelper';
|
import { uploadFile } from '../uploadHelper';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
|
||||||
// Mocking axios using jest-mock-axios
|
|
||||||
global.axios = axios;
|
global.axios = axios;
|
||||||
jest.mock('axios');
|
vi.mock('axios');
|
||||||
|
|
||||||
describe('#Upload Helpers', () => {
|
describe('#Upload Helpers', () => {
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
|||||||
@ -4,8 +4,8 @@ import Vuex from 'vuex';
|
|||||||
import OpenAPI from '../../api/integrations/openapi';
|
import OpenAPI from '../../api/integrations/openapi';
|
||||||
import { LocalStorage } from '../../../shared/helpers/localStorage';
|
import { LocalStorage } from '../../../shared/helpers/localStorage';
|
||||||
|
|
||||||
jest.mock('../../api/integrations/openapi');
|
vi.mock('../../api/integrations/openapi');
|
||||||
jest.mock('../../../shared/helpers/localStorage');
|
vi.mock('../../../shared/helpers/localStorage');
|
||||||
|
|
||||||
const localVue = createLocalVue();
|
const localVue = createLocalVue();
|
||||||
localVue.use(Vuex);
|
localVue.use(Vuex);
|
||||||
@ -18,12 +18,12 @@ describe('aiMixin', () => {
|
|||||||
let actions;
|
let actions;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
OpenAPI.processEvent = jest.fn();
|
OpenAPI.processEvent = vi.fn();
|
||||||
LocalStorage.set = jest.fn();
|
LocalStorage.set = vi.fn();
|
||||||
LocalStorage.get = jest.fn();
|
LocalStorage.get = vi.fn();
|
||||||
|
|
||||||
actions = {
|
actions = {
|
||||||
['integrations/get']: jest.fn(),
|
['integrations/get']: vi.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
getters = {
|
getters = {
|
||||||
@ -63,20 +63,20 @@ describe('aiMixin', () => {
|
|||||||
localVue,
|
localVue,
|
||||||
});
|
});
|
||||||
|
|
||||||
const dispatchSpy = jest.spyOn(wrapper.vm.$store, 'dispatch');
|
const dispatchSpy = vi.spyOn(wrapper.vm.$store, 'dispatch');
|
||||||
await wrapper.vm.fetchIntegrationsIfRequired();
|
await wrapper.vm.fetchIntegrationsIfRequired();
|
||||||
expect(dispatchSpy).toHaveBeenCalledWith('integrations/get');
|
expect(dispatchSpy).toHaveBeenCalledWith('integrations/get');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not fetch integrations', async () => {
|
it('does not fetch integrations', async () => {
|
||||||
const dispatchSpy = jest.spyOn(wrapper.vm.$store, 'dispatch');
|
const dispatchSpy = vi.spyOn(wrapper.vm.$store, 'dispatch');
|
||||||
await wrapper.vm.fetchIntegrationsIfRequired();
|
await wrapper.vm.fetchIntegrationsIfRequired();
|
||||||
expect(dispatchSpy).not.toHaveBeenCalledWith('integrations/get');
|
expect(dispatchSpy).not.toHaveBeenCalledWith('integrations/get');
|
||||||
expect(wrapper.vm.isAIIntegrationEnabled).toBeTruthy();
|
expect(wrapper.vm.isAIIntegrationEnabled).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('fetches label suggestions', async () => {
|
it('fetches label suggestions', async () => {
|
||||||
const processEventSpy = jest.spyOn(OpenAPI, 'processEvent');
|
const processEventSpy = vi.spyOn(OpenAPI, 'processEvent');
|
||||||
await wrapper.vm.fetchLabelSuggestions({
|
await wrapper.vm.fetchLabelSuggestions({
|
||||||
conversationId: '123',
|
conversationId: '123',
|
||||||
});
|
});
|
||||||
|
|||||||
@ -11,7 +11,7 @@ describe('attributeMixin', () => {
|
|||||||
let store;
|
let store;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
actions = { updateUISettings: jest.fn(), toggleSidebarUIState: jest.fn() };
|
actions = { updateUISettings: vi.fn(), toggleSidebarUIState: vi.fn() };
|
||||||
getters = {
|
getters = {
|
||||||
getSelectedChat: () => ({
|
getSelectedChat: () => ({
|
||||||
id: 7165,
|
id: 7165,
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
import fileUploadMixin from 'dashboard/mixins/fileUploadMixin';
|
import fileUploadMixin from 'dashboard/mixins/fileUploadMixin';
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
|
|
||||||
jest.mock('shared/helpers/FileHelper', () => ({
|
vi.mock('shared/helpers/FileHelper', () => ({
|
||||||
checkFileSizeLimit: jest.fn(),
|
checkFileSizeLimit: vi.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock('activestorage', () => ({
|
vi.mock('activestorage', () => ({
|
||||||
DirectUpload: jest.fn().mockImplementation(() => ({
|
DirectUpload: vi.fn().mockImplementation(() => ({
|
||||||
create: jest.fn(),
|
create: vi.fn(),
|
||||||
})),
|
})),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -27,20 +27,20 @@ describe('FileUploadMixin', () => {
|
|||||||
vm.currentUser = {
|
vm.currentUser = {
|
||||||
access_token: 'token',
|
access_token: 'token',
|
||||||
};
|
};
|
||||||
vm.$t = jest.fn(message => message);
|
vm.$t = vi.fn(message => message);
|
||||||
vm.showAlert = jest.fn();
|
vm.showAlert = vi.fn();
|
||||||
vm.attachFile = jest.fn();
|
vm.attachFile = vi.fn();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call onDirectFileUpload when direct uploads are enabled', () => {
|
it('should call onDirectFileUpload when direct uploads are enabled', () => {
|
||||||
vm.onDirectFileUpload = jest.fn();
|
vm.onDirectFileUpload = vi.fn();
|
||||||
vm.onFileUpload({});
|
vm.onFileUpload({});
|
||||||
expect(vm.onDirectFileUpload).toHaveBeenCalledWith({});
|
expect(vm.onDirectFileUpload).toHaveBeenCalledWith({});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call onIndirectFileUpload when direct uploads are disabled', () => {
|
it('should call onIndirectFileUpload when direct uploads are disabled', () => {
|
||||||
vm.globalConfig.directUploadsEnabled = false;
|
vm.globalConfig.directUploadsEnabled = false;
|
||||||
vm.onIndirectFileUpload = jest.fn();
|
vm.onIndirectFileUpload = vi.fn();
|
||||||
vm.onFileUpload({});
|
vm.onFileUpload({});
|
||||||
expect(vm.onIndirectFileUpload).toHaveBeenCalledWith({});
|
expect(vm.onIndirectFileUpload).toHaveBeenCalledWith({});
|
||||||
});
|
});
|
||||||
@ -53,7 +53,7 @@ describe('FileUploadMixin', () => {
|
|||||||
|
|
||||||
it('shows an alert if the file size exceeds the maximum limit', () => {
|
it('shows an alert if the file size exceeds the maximum limit', () => {
|
||||||
const fakeFile = { size: 999999999 };
|
const fakeFile = { size: 999999999 };
|
||||||
vm.showAlert = jest.fn();
|
vm.showAlert = vi.fn();
|
||||||
vm.onDirectFileUpload(fakeFile);
|
vm.onDirectFileUpload(fakeFile);
|
||||||
expect(vm.showAlert).toHaveBeenCalledWith(expect.any(String));
|
expect(vm.showAlert).toHaveBeenCalledWith(expect.any(String));
|
||||||
});
|
});
|
||||||
@ -67,7 +67,7 @@ describe('FileUploadMixin', () => {
|
|||||||
|
|
||||||
it('shows an alert if the file size exceeds the maximum limit', () => {
|
it('shows an alert if the file size exceeds the maximum limit', () => {
|
||||||
const fakeFile = { size: 999999999 };
|
const fakeFile = { size: 999999999 };
|
||||||
vm.showAlert = jest.fn();
|
vm.showAlert = vi.fn();
|
||||||
vm.onIndirectFileUpload(fakeFile);
|
vm.onIndirectFileUpload(fakeFile);
|
||||||
expect(vm.showAlert).toHaveBeenCalledWith(expect.any(String));
|
expect(vm.showAlert).toHaveBeenCalledWith(expect.any(String));
|
||||||
});
|
});
|
||||||
|
|||||||
@ -11,14 +11,14 @@ describe('#messageStamp', () => {
|
|||||||
|
|
||||||
describe('#messageTimestamp', () => {
|
describe('#messageTimestamp', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
jest.useFakeTimers('modern');
|
vi.useFakeTimers('modern');
|
||||||
|
|
||||||
const mockDate = new Date(2023, 4, 5);
|
const mockDate = new Date(2023, 4, 5);
|
||||||
jest.setSystemTime(mockDate);
|
vi.setSystemTime(mockDate);
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
jest.useRealTimers();
|
vi.useRealTimers();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return the message date in the specified format if the message was sent in the current year', () => {
|
it('should return the message date in the specified format if the message was sent in the current year', () => {
|
||||||
@ -35,7 +35,7 @@ describe('#messageTimestamp', () => {
|
|||||||
|
|
||||||
describe('#dynamicTime', () => {
|
describe('#dynamicTime', () => {
|
||||||
it('returns correct value', () => {
|
it('returns correct value', () => {
|
||||||
Date.now = jest.fn(() => new Date(Date.UTC(2023, 1, 14)).valueOf());
|
Date.now = vi.fn(() => new Date(Date.UTC(2023, 1, 14)).valueOf());
|
||||||
expect(TimeMixin.methods.dynamicTime(1612971343)).toEqual(
|
expect(TimeMixin.methods.dynamicTime(1612971343)).toEqual(
|
||||||
'about 2 years ago'
|
'about 2 years ago'
|
||||||
);
|
);
|
||||||
|
|||||||
@ -14,7 +14,7 @@ describe('uiSettingsMixin', () => {
|
|||||||
let store;
|
let store;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
actions = { updateUISettings: jest.fn(), toggleSidebarUIState: jest.fn() };
|
actions = { updateUISettings: vi.fn(), toggleSidebarUIState: vi.fn() };
|
||||||
getters = {
|
getters = {
|
||||||
getUISettings: () => ({
|
getUISettings: () => ({
|
||||||
enter_to_send_enabled: false,
|
enter_to_send_enabled: false,
|
||||||
|
|||||||
@ -7,7 +7,9 @@ export const routes = [
|
|||||||
{
|
{
|
||||||
path: frontendURL('accounts/:accountId/search'),
|
path: frontendURL('accounts/:accountId/search'),
|
||||||
name: 'search',
|
name: 'search',
|
||||||
roles: ['administrator', 'agent'],
|
meta: {
|
||||||
|
permissions: ['administrator', 'agent'],
|
||||||
|
},
|
||||||
component: SearchView,
|
component: SearchView,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@ -41,7 +41,6 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Sidebar from '../../components/layout/Sidebar.vue';
|
import Sidebar from '../../components/layout/Sidebar.vue';
|
||||||
import CommandBar from './commands/commandbar.vue';
|
|
||||||
import { BUS_EVENTS } from 'shared/constants/busEvents';
|
import { BUS_EVENTS } from 'shared/constants/busEvents';
|
||||||
import WootKeyShortcutModal from 'dashboard/components/widgets/modal/WootKeyShortcutModal.vue';
|
import WootKeyShortcutModal from 'dashboard/components/widgets/modal/WootKeyShortcutModal.vue';
|
||||||
import AddAccountModal from 'dashboard/components/layout/sidebarComponents/AddAccountModal.vue';
|
import AddAccountModal from 'dashboard/components/layout/sidebarComponents/AddAccountModal.vue';
|
||||||
@ -50,6 +49,7 @@ import AddLabelModal from 'dashboard/routes/dashboard/settings/labels/AddLabel.v
|
|||||||
import NotificationPanel from 'dashboard/routes/dashboard/notifications/components/NotificationPanel.vue';
|
import NotificationPanel from 'dashboard/routes/dashboard/notifications/components/NotificationPanel.vue';
|
||||||
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
||||||
import wootConstants from 'dashboard/constants/globals';
|
import wootConstants from 'dashboard/constants/globals';
|
||||||
|
const CommandBar = () => import('./commands/commandbar.vue');
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
|||||||
@ -15,7 +15,7 @@ describe('AddReminder', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('tests resetValue', () => {
|
it('tests resetValue', () => {
|
||||||
const resetValue = jest.spyOn(wrapper.vm, 'resetValue');
|
const resetValue = vi.spyOn(wrapper.vm, 'resetValue');
|
||||||
wrapper.vm.content = 'test';
|
wrapper.vm.content = 'test';
|
||||||
wrapper.vm.date = '08/11/2022';
|
wrapper.vm.date = '08/11/2022';
|
||||||
wrapper.vm.resetValue();
|
wrapper.vm.resetValue();
|
||||||
@ -25,7 +25,7 @@ describe('AddReminder', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('tests optionSelected', () => {
|
it('tests optionSelected', () => {
|
||||||
const optionSelected = jest.spyOn(wrapper.vm, 'optionSelected');
|
const optionSelected = vi.spyOn(wrapper.vm, 'optionSelected');
|
||||||
wrapper.vm.label = '';
|
wrapper.vm.label = '';
|
||||||
wrapper.vm.optionSelected({ target: { value: 'test' } });
|
wrapper.vm.optionSelected({ target: { value: 'test' } });
|
||||||
expect(wrapper.vm.label).toEqual('test');
|
expect(wrapper.vm.label).toEqual('test');
|
||||||
@ -33,7 +33,7 @@ describe('AddReminder', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('tests onAdd', () => {
|
it('tests onAdd', () => {
|
||||||
const onAdd = jest.spyOn(wrapper.vm, 'onAdd');
|
const onAdd = vi.spyOn(wrapper.vm, 'onAdd');
|
||||||
wrapper.vm.label = 'label';
|
wrapper.vm.label = 'label';
|
||||||
wrapper.vm.content = 'content';
|
wrapper.vm.content = 'content';
|
||||||
wrapper.vm.date = '08/11/2022';
|
wrapper.vm.date = '08/11/2022';
|
||||||
|
|||||||
@ -7,13 +7,17 @@ export const routes = [
|
|||||||
{
|
{
|
||||||
path: frontendURL('accounts/:accountId/contacts'),
|
path: frontendURL('accounts/:accountId/contacts'),
|
||||||
name: 'contacts_dashboard',
|
name: 'contacts_dashboard',
|
||||||
roles: ['administrator', 'agent'],
|
meta: {
|
||||||
|
permissions: ['administrator', 'agent'],
|
||||||
|
},
|
||||||
component: ContactsView,
|
component: ContactsView,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: frontendURL('accounts/:accountId/contacts/custom_view/:id'),
|
path: frontendURL('accounts/:accountId/contacts/custom_view/:id'),
|
||||||
name: 'contacts_segments_dashboard',
|
name: 'contacts_segments_dashboard',
|
||||||
roles: ['administrator', 'agent'],
|
meta: {
|
||||||
|
permissions: ['administrator', 'agent'],
|
||||||
|
},
|
||||||
component: ContactsView,
|
component: ContactsView,
|
||||||
props: route => {
|
props: route => {
|
||||||
return { segmentsId: route.params.id };
|
return { segmentsId: route.params.id };
|
||||||
@ -22,7 +26,9 @@ export const routes = [
|
|||||||
{
|
{
|
||||||
path: frontendURL('accounts/:accountId/labels/:label/contacts'),
|
path: frontendURL('accounts/:accountId/labels/:label/contacts'),
|
||||||
name: 'contacts_labels_dashboard',
|
name: 'contacts_labels_dashboard',
|
||||||
roles: ['administrator', 'agent'],
|
meta: {
|
||||||
|
permissions: ['administrator', 'agent'],
|
||||||
|
},
|
||||||
component: ContactsView,
|
component: ContactsView,
|
||||||
props: route => {
|
props: route => {
|
||||||
return { label: route.params.label };
|
return { label: route.params.label };
|
||||||
@ -31,7 +37,9 @@ export const routes = [
|
|||||||
{
|
{
|
||||||
path: frontendURL('accounts/:accountId/contacts/:contactId'),
|
path: frontendURL('accounts/:accountId/contacts/:contactId'),
|
||||||
name: 'contact_profile_dashboard',
|
name: 'contact_profile_dashboard',
|
||||||
roles: ['administrator', 'agent'],
|
meta: {
|
||||||
|
permissions: ['administrator', 'agent'],
|
||||||
|
},
|
||||||
component: ContactManageView,
|
component: ContactManageView,
|
||||||
props: route => {
|
props: route => {
|
||||||
return { contactId: route.params.contactId };
|
return { contactId: route.params.contactId };
|
||||||
|
|||||||
@ -22,25 +22,40 @@
|
|||||||
:is-on-expanded-layout="isOnExpandedLayout"
|
:is-on-expanded-layout="isOnExpandedLayout"
|
||||||
@contact-panel-toggle="onToggleContactPanel"
|
@contact-panel-toggle="onToggleContactPanel"
|
||||||
/>
|
/>
|
||||||
|
<woot-modal
|
||||||
|
:show.sync="showCustomSnoozeModal"
|
||||||
|
:on-close="hideCustomSnoozeModal"
|
||||||
|
>
|
||||||
|
<custom-snooze-modal
|
||||||
|
@close="hideCustomSnoozeModal"
|
||||||
|
@choose-time="chooseSnoozeTime"
|
||||||
|
/>
|
||||||
|
</woot-modal>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
|
import { getUnixTime } from 'date-fns';
|
||||||
import ChatList from '../../../components/ChatList.vue';
|
import ChatList from '../../../components/ChatList.vue';
|
||||||
import ConversationBox from '../../../components/widgets/conversation/ConversationBox.vue';
|
import ConversationBox from '../../../components/widgets/conversation/ConversationBox.vue';
|
||||||
import PopOverSearch from './search/PopOverSearch.vue';
|
import PopOverSearch from './search/PopOverSearch.vue';
|
||||||
|
import CustomSnoozeModal from 'dashboard/components/CustomSnoozeModal.vue';
|
||||||
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
||||||
import { BUS_EVENTS } from 'shared/constants/busEvents';
|
import alertMixin from 'shared/mixins/alertMixin';
|
||||||
import wootConstants from 'dashboard/constants/globals';
|
import wootConstants from 'dashboard/constants/globals';
|
||||||
|
import { BUS_EVENTS } from 'shared/constants/busEvents';
|
||||||
|
import { CMD_SNOOZE_CONVERSATION } from 'dashboard/routes/dashboard/commands/commandBarBusEvents';
|
||||||
|
import { findSnoozeTime } from 'dashboard/helper/snoozeHelpers';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
ChatList,
|
ChatList,
|
||||||
ConversationBox,
|
ConversationBox,
|
||||||
PopOverSearch,
|
PopOverSearch,
|
||||||
|
CustomSnoozeModal,
|
||||||
},
|
},
|
||||||
mixins: [uiSettingsMixin],
|
mixins: [uiSettingsMixin, alertMixin],
|
||||||
props: {
|
props: {
|
||||||
inboxId: {
|
inboxId: {
|
||||||
type: [String, Number],
|
type: [String, Number],
|
||||||
@ -70,12 +85,14 @@ export default {
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
showSearchModal: false,
|
showSearchModal: false,
|
||||||
|
showCustomSnoozeModal: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
chatList: 'getAllConversations',
|
chatList: 'getAllConversations',
|
||||||
currentChat: 'getSelectedChat',
|
currentChat: 'getSelectedChat',
|
||||||
|
contextMenuChatId: 'getContextMenuChatId',
|
||||||
}),
|
}),
|
||||||
showConversationList() {
|
showConversationList() {
|
||||||
return this.isOnExpandedLayout ? !this.conversationId : true;
|
return this.isOnExpandedLayout ? !this.conversationId : true;
|
||||||
@ -112,6 +129,10 @@ export default {
|
|||||||
this.$watch('chatList.length', () => {
|
this.$watch('chatList.length', () => {
|
||||||
this.setActiveChat();
|
this.setActiveChat();
|
||||||
});
|
});
|
||||||
|
this.$emitter.on(CMD_SNOOZE_CONVERSATION, this.onCmdSnoozeConversation);
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
this.$emitter.off(CMD_SNOOZE_CONVERSATION, this.onCmdSnoozeConversation);
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
@ -186,6 +207,43 @@ export default {
|
|||||||
closeSearch() {
|
closeSearch() {
|
||||||
this.showSearchModal = false;
|
this.showSearchModal = false;
|
||||||
},
|
},
|
||||||
|
onCmdSnoozeConversation(snoozeType) {
|
||||||
|
if (snoozeType === wootConstants.SNOOZE_OPTIONS.UNTIL_CUSTOM_TIME) {
|
||||||
|
this.showCustomSnoozeModal = true;
|
||||||
|
} else {
|
||||||
|
this.toggleStatus(
|
||||||
|
wootConstants.STATUS_TYPE.SNOOZED,
|
||||||
|
findSnoozeTime(snoozeType) || null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
chooseSnoozeTime(customSnoozeTime) {
|
||||||
|
this.showCustomSnoozeModal = false;
|
||||||
|
if (customSnoozeTime) {
|
||||||
|
this.toggleStatus(
|
||||||
|
wootConstants.STATUS_TYPE.SNOOZED,
|
||||||
|
getUnixTime(customSnoozeTime)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
toggleStatus(status, snoozedUntil) {
|
||||||
|
this.$store
|
||||||
|
.dispatch('toggleStatus', {
|
||||||
|
conversationId: this.currentChat?.id || this.contextMenuChatId,
|
||||||
|
status,
|
||||||
|
snoozedUntil,
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
this.$store.dispatch('setContextMenuChatId', null);
|
||||||
|
this.showAlert(this.$t('CONVERSATION.CHANGE_STATUS'));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
hideCustomSnoozeModal() {
|
||||||
|
// if we select custom snooze and the custom snooze modal is open
|
||||||
|
// Then if the custom snooze modal is closed then set the context menu chat id to null
|
||||||
|
this.$store.dispatch('setContextMenuChatId', null);
|
||||||
|
this.showCustomSnoozeModal = false;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -117,7 +117,7 @@
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="w-full">
|
<div class="w-full">
|
||||||
<label> Social Profiles </label>
|
<label>{{ $t('CONTACTS_PAGE.LIST.TABLE_HEADER.SOCIAL_PROFILES') }}</label>
|
||||||
<div
|
<div
|
||||||
v-for="socialProfile in socialProfileKeys"
|
v-for="socialProfile in socialProfileKeys"
|
||||||
:key="socialProfile.key"
|
:key="socialProfile.key"
|
||||||
@ -130,7 +130,7 @@
|
|||||||
</span>
|
</span>
|
||||||
<input
|
<input
|
||||||
v-model="socialProfileUserNames[socialProfile.key]"
|
v-model="socialProfileUserNames[socialProfile.key]"
|
||||||
class="input-group-field ltr:rounded-l-none rtl:rounded-r-none !mb-0"
|
class="input-group-field ltr:!rounded-l-none rtl:rounded-r-none !mb-0"
|
||||||
type="text"
|
type="text"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,13 +1,15 @@
|
|||||||
/* eslint arrow-body-style: 0 */
|
/* eslint arrow-body-style: 0 */
|
||||||
import { frontendURL } from '../../../helper/URLHelper';
|
import { frontendURL } from '../../../helper/URLHelper';
|
||||||
const ConversationView = () => import('./ConversationView');
|
const ConversationView = () => import('./ConversationView.vue');
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
routes: [
|
routes: [
|
||||||
{
|
{
|
||||||
path: frontendURL('accounts/:accountId/dashboard'),
|
path: frontendURL('accounts/:accountId/dashboard'),
|
||||||
name: 'home',
|
name: 'home',
|
||||||
roles: ['administrator', 'agent'],
|
meta: {
|
||||||
|
permissions: ['administrator', 'agent'],
|
||||||
|
},
|
||||||
component: ConversationView,
|
component: ConversationView,
|
||||||
props: () => {
|
props: () => {
|
||||||
return { inboxId: 0 };
|
return { inboxId: 0 };
|
||||||
@ -16,7 +18,9 @@ export default {
|
|||||||
{
|
{
|
||||||
path: frontendURL('accounts/:accountId/conversations/:conversation_id'),
|
path: frontendURL('accounts/:accountId/conversations/:conversation_id'),
|
||||||
name: 'inbox_conversation',
|
name: 'inbox_conversation',
|
||||||
roles: ['administrator', 'agent'],
|
meta: {
|
||||||
|
permissions: ['administrator', 'agent'],
|
||||||
|
},
|
||||||
component: ConversationView,
|
component: ConversationView,
|
||||||
props: route => {
|
props: route => {
|
||||||
return { inboxId: 0, conversationId: route.params.conversation_id };
|
return { inboxId: 0, conversationId: route.params.conversation_id };
|
||||||
@ -25,7 +29,9 @@ export default {
|
|||||||
{
|
{
|
||||||
path: frontendURL('accounts/:accountId/inbox/:inbox_id'),
|
path: frontendURL('accounts/:accountId/inbox/:inbox_id'),
|
||||||
name: 'inbox_dashboard',
|
name: 'inbox_dashboard',
|
||||||
roles: ['administrator', 'agent'],
|
meta: {
|
||||||
|
permissions: ['administrator', 'agent'],
|
||||||
|
},
|
||||||
component: ConversationView,
|
component: ConversationView,
|
||||||
props: route => {
|
props: route => {
|
||||||
return { inboxId: route.params.inbox_id };
|
return { inboxId: route.params.inbox_id };
|
||||||
@ -36,7 +42,9 @@ export default {
|
|||||||
'accounts/:accountId/inbox/:inbox_id/conversations/:conversation_id'
|
'accounts/:accountId/inbox/:inbox_id/conversations/:conversation_id'
|
||||||
),
|
),
|
||||||
name: 'conversation_through_inbox',
|
name: 'conversation_through_inbox',
|
||||||
roles: ['administrator', 'agent'],
|
meta: {
|
||||||
|
permissions: ['administrator', 'agent'],
|
||||||
|
},
|
||||||
component: ConversationView,
|
component: ConversationView,
|
||||||
props: route => {
|
props: route => {
|
||||||
return {
|
return {
|
||||||
@ -48,7 +56,9 @@ export default {
|
|||||||
{
|
{
|
||||||
path: frontendURL('accounts/:accountId/label/:label'),
|
path: frontendURL('accounts/:accountId/label/:label'),
|
||||||
name: 'label_conversations',
|
name: 'label_conversations',
|
||||||
roles: ['administrator', 'agent'],
|
meta: {
|
||||||
|
permissions: ['administrator', 'agent'],
|
||||||
|
},
|
||||||
component: ConversationView,
|
component: ConversationView,
|
||||||
props: route => ({ label: route.params.label }),
|
props: route => ({ label: route.params.label }),
|
||||||
},
|
},
|
||||||
@ -57,7 +67,9 @@ export default {
|
|||||||
'accounts/:accountId/label/:label/conversations/:conversation_id'
|
'accounts/:accountId/label/:label/conversations/:conversation_id'
|
||||||
),
|
),
|
||||||
name: 'conversations_through_label',
|
name: 'conversations_through_label',
|
||||||
roles: ['administrator', 'agent'],
|
meta: {
|
||||||
|
permissions: ['administrator', 'agent'],
|
||||||
|
},
|
||||||
component: ConversationView,
|
component: ConversationView,
|
||||||
props: route => ({
|
props: route => ({
|
||||||
conversationId: route.params.conversation_id,
|
conversationId: route.params.conversation_id,
|
||||||
@ -67,7 +79,9 @@ export default {
|
|||||||
{
|
{
|
||||||
path: frontendURL('accounts/:accountId/team/:teamId'),
|
path: frontendURL('accounts/:accountId/team/:teamId'),
|
||||||
name: 'team_conversations',
|
name: 'team_conversations',
|
||||||
roles: ['administrator', 'agent'],
|
meta: {
|
||||||
|
permissions: ['administrator', 'agent'],
|
||||||
|
},
|
||||||
component: ConversationView,
|
component: ConversationView,
|
||||||
props: route => ({ teamId: route.params.teamId }),
|
props: route => ({ teamId: route.params.teamId }),
|
||||||
},
|
},
|
||||||
@ -76,7 +90,9 @@ export default {
|
|||||||
'accounts/:accountId/team/:teamId/conversations/:conversationId'
|
'accounts/:accountId/team/:teamId/conversations/:conversationId'
|
||||||
),
|
),
|
||||||
name: 'conversations_through_team',
|
name: 'conversations_through_team',
|
||||||
roles: ['administrator', 'agent'],
|
meta: {
|
||||||
|
permissions: ['administrator', 'agent'],
|
||||||
|
},
|
||||||
component: ConversationView,
|
component: ConversationView,
|
||||||
props: route => ({
|
props: route => ({
|
||||||
conversationId: route.params.conversationId,
|
conversationId: route.params.conversationId,
|
||||||
@ -86,7 +102,9 @@ export default {
|
|||||||
{
|
{
|
||||||
path: frontendURL('accounts/:accountId/custom_view/:id'),
|
path: frontendURL('accounts/:accountId/custom_view/:id'),
|
||||||
name: 'folder_conversations',
|
name: 'folder_conversations',
|
||||||
roles: ['administrator', 'agent'],
|
meta: {
|
||||||
|
permissions: ['administrator', 'agent'],
|
||||||
|
},
|
||||||
component: ConversationView,
|
component: ConversationView,
|
||||||
props: route => ({ foldersId: route.params.id }),
|
props: route => ({ foldersId: route.params.id }),
|
||||||
},
|
},
|
||||||
@ -95,7 +113,9 @@ export default {
|
|||||||
'accounts/:accountId/custom_view/:id/conversations/:conversation_id'
|
'accounts/:accountId/custom_view/:id/conversations/:conversation_id'
|
||||||
),
|
),
|
||||||
name: 'conversations_through_folders',
|
name: 'conversations_through_folders',
|
||||||
roles: ['administrator', 'agent'],
|
meta: {
|
||||||
|
permissions: ['administrator', 'agent'],
|
||||||
|
},
|
||||||
component: ConversationView,
|
component: ConversationView,
|
||||||
props: route => ({
|
props: route => ({
|
||||||
conversationId: route.params.conversation_id,
|
conversationId: route.params.conversation_id,
|
||||||
@ -105,7 +125,9 @@ export default {
|
|||||||
{
|
{
|
||||||
path: frontendURL('accounts/:accountId/mentions/conversations'),
|
path: frontendURL('accounts/:accountId/mentions/conversations'),
|
||||||
name: 'conversation_mentions',
|
name: 'conversation_mentions',
|
||||||
roles: ['administrator', 'agent'],
|
meta: {
|
||||||
|
permissions: ['administrator', 'agent'],
|
||||||
|
},
|
||||||
component: ConversationView,
|
component: ConversationView,
|
||||||
props: () => ({ conversationType: 'mention' }),
|
props: () => ({ conversationType: 'mention' }),
|
||||||
},
|
},
|
||||||
@ -114,7 +136,9 @@ export default {
|
|||||||
'accounts/:accountId/mentions/conversations/:conversationId'
|
'accounts/:accountId/mentions/conversations/:conversationId'
|
||||||
),
|
),
|
||||||
name: 'conversation_through_mentions',
|
name: 'conversation_through_mentions',
|
||||||
roles: ['administrator', 'agent'],
|
meta: {
|
||||||
|
permissions: ['administrator', 'agent'],
|
||||||
|
},
|
||||||
component: ConversationView,
|
component: ConversationView,
|
||||||
props: route => ({
|
props: route => ({
|
||||||
conversationId: route.params.conversationId,
|
conversationId: route.params.conversationId,
|
||||||
@ -124,7 +148,9 @@ export default {
|
|||||||
{
|
{
|
||||||
path: frontendURL('accounts/:accountId/unattended/conversations'),
|
path: frontendURL('accounts/:accountId/unattended/conversations'),
|
||||||
name: 'conversation_unattended',
|
name: 'conversation_unattended',
|
||||||
roles: ['administrator', 'agent'],
|
meta: {
|
||||||
|
permissions: ['administrator', 'agent'],
|
||||||
|
},
|
||||||
component: ConversationView,
|
component: ConversationView,
|
||||||
props: () => ({ conversationType: 'unattended' }),
|
props: () => ({ conversationType: 'unattended' }),
|
||||||
},
|
},
|
||||||
@ -133,7 +159,9 @@ export default {
|
|||||||
'accounts/:accountId/unattended/conversations/:conversationId'
|
'accounts/:accountId/unattended/conversations/:conversationId'
|
||||||
),
|
),
|
||||||
name: 'conversation_through_unattended',
|
name: 'conversation_through_unattended',
|
||||||
roles: ['administrator', 'agent'],
|
meta: {
|
||||||
|
permissions: ['administrator', 'agent'],
|
||||||
|
},
|
||||||
component: ConversationView,
|
component: ConversationView,
|
||||||
props: route => ({
|
props: route => ({
|
||||||
conversationId: route.params.conversationId,
|
conversationId: route.params.conversationId,
|
||||||
@ -143,7 +171,9 @@ export default {
|
|||||||
{
|
{
|
||||||
path: frontendURL('accounts/:accountId/participating/conversations'),
|
path: frontendURL('accounts/:accountId/participating/conversations'),
|
||||||
name: 'conversation_participating',
|
name: 'conversation_participating',
|
||||||
roles: ['administrator', 'agent'],
|
meta: {
|
||||||
|
permissions: ['administrator', 'agent'],
|
||||||
|
},
|
||||||
component: ConversationView,
|
component: ConversationView,
|
||||||
props: () => ({ conversationType: 'participating' }),
|
props: () => ({ conversationType: 'participating' }),
|
||||||
},
|
},
|
||||||
@ -152,7 +182,9 @@ export default {
|
|||||||
'accounts/:accountId/participating/conversations/:conversationId'
|
'accounts/:accountId/participating/conversations/:conversationId'
|
||||||
),
|
),
|
||||||
name: 'conversation_through_participating',
|
name: 'conversation_through_participating',
|
||||||
roles: ['administrator', 'agent'],
|
meta: {
|
||||||
|
permissions: ['administrator', 'agent'],
|
||||||
|
},
|
||||||
component: ConversationView,
|
component: ConversationView,
|
||||||
props: route => ({
|
props: route => ({
|
||||||
conversationId: route.params.conversationId,
|
conversationId: route.params.conversationId,
|
||||||
|
|||||||
@ -28,7 +28,9 @@ export default {
|
|||||||
{
|
{
|
||||||
path: frontendURL('accounts/:accountId/suspended'),
|
path: frontendURL('accounts/:accountId/suspended'),
|
||||||
name: 'account_suspended',
|
name: 'account_suspended',
|
||||||
roles: ['administrator', 'agent'],
|
meta: {
|
||||||
|
permissions: ['administrator', 'agent'],
|
||||||
|
},
|
||||||
component: Suspended,
|
component: Suspended,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@ -59,13 +59,12 @@
|
|||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import UpgradePage from './UpgradePage';
|
import UpgradePage from './UpgradePage.vue';
|
||||||
import { frontendURL } from '../../../../helper/URLHelper';
|
import { frontendURL } from '../../../../helper/URLHelper';
|
||||||
import Sidebar from 'dashboard/components/layout/Sidebar.vue';
|
import Sidebar from 'dashboard/components/layout/Sidebar.vue';
|
||||||
import { BUS_EVENTS } from 'shared/constants/busEvents';
|
import { BUS_EVENTS } from 'shared/constants/busEvents';
|
||||||
import PortalPopover from '../components/PortalPopover.vue';
|
import PortalPopover from '../components/PortalPopover.vue';
|
||||||
import HelpCenterSidebar from '../components/Sidebar/Sidebar.vue';
|
import HelpCenterSidebar from '../components/Sidebar/Sidebar.vue';
|
||||||
import CommandBar from 'dashboard/routes/dashboard/commands/commandbar.vue';
|
|
||||||
import WootKeyShortcutModal from 'dashboard/components/widgets/modal/WootKeyShortcutModal.vue';
|
import WootKeyShortcutModal from 'dashboard/components/widgets/modal/WootKeyShortcutModal.vue';
|
||||||
import AccountSelector from 'dashboard/components/layout/sidebarComponents/AccountSelector.vue';
|
import AccountSelector from 'dashboard/components/layout/sidebarComponents/AccountSelector.vue';
|
||||||
import NotificationPanel from 'dashboard/routes/dashboard/notifications/components/NotificationPanel.vue';
|
import NotificationPanel from 'dashboard/routes/dashboard/notifications/components/NotificationPanel.vue';
|
||||||
@ -73,6 +72,8 @@ import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
|||||||
import portalMixin from '../mixins/portalMixin';
|
import portalMixin from '../mixins/portalMixin';
|
||||||
import AddCategory from '../pages/categories/AddCategory.vue';
|
import AddCategory from '../pages/categories/AddCategory.vue';
|
||||||
import { FEATURE_FLAGS } from 'dashboard/featureFlags';
|
import { FEATURE_FLAGS } from 'dashboard/featureFlags';
|
||||||
|
const CommandBar = () =>
|
||||||
|
import('dashboard/routes/dashboard/commands/commandbar.vue');
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
|||||||
@ -1,42 +1,48 @@
|
|||||||
import HelpCenterLayout from './components/HelpCenterLayout';
|
import HelpCenterLayout from './components/HelpCenterLayout.vue';
|
||||||
import { getPortalRoute } from './helpers/routeHelper';
|
import { getPortalRoute } from './helpers/routeHelper';
|
||||||
|
|
||||||
const ListAllPortals = () => import('./pages/portals/ListAllPortals');
|
const ListAllPortals = () => import('./pages/portals/ListAllPortals.vue');
|
||||||
const NewPortal = () => import('./pages/portals/NewPortal');
|
const NewPortal = () => import('./pages/portals/NewPortal.vue');
|
||||||
|
|
||||||
const EditPortal = () => import('./pages/portals/EditPortal');
|
const EditPortal = () => import('./pages/portals/EditPortal.vue');
|
||||||
const EditPortalBasic = () => import('./pages/portals/EditPortalBasic');
|
const EditPortalBasic = () => import('./pages/portals/EditPortalBasic.vue');
|
||||||
const EditPortalCustomization = () =>
|
const EditPortalCustomization = () =>
|
||||||
import('./pages/portals/EditPortalCustomization');
|
import('./pages/portals/EditPortalCustomization.vue');
|
||||||
const EditPortalLocales = () => import('./pages/portals/EditPortalLocales.vue');
|
const EditPortalLocales = () => import('./pages/portals/EditPortalLocales.vue');
|
||||||
const ShowPortal = () => import('./pages/portals/ShowPortal');
|
const ShowPortal = () => import('./pages/portals/ShowPortal.vue');
|
||||||
const PortalDetails = () => import('./pages/portals/PortalDetails');
|
const PortalDetails = () => import('./pages/portals/PortalDetails.vue');
|
||||||
const PortalCustomization = () => import('./pages/portals/PortalCustomization');
|
const PortalCustomization = () =>
|
||||||
|
import('./pages/portals/PortalCustomization.vue');
|
||||||
const PortalSettingsFinish = () =>
|
const PortalSettingsFinish = () =>
|
||||||
import('./pages/portals/PortalSettingsFinish');
|
import('./pages/portals/PortalSettingsFinish.vue');
|
||||||
|
|
||||||
const ListAllCategories = () => import('./pages/categories/ListAllCategories');
|
const ListAllCategories = () =>
|
||||||
const NewCategory = () => import('./pages/categories/NewCategory');
|
import('./pages/categories/ListAllCategories.vue');
|
||||||
const EditCategory = () => import('./pages/categories/EditCategory');
|
const NewCategory = () => import('./pages/categories/NewCategory.vue');
|
||||||
|
const EditCategory = () => import('./pages/categories/EditCategory.vue');
|
||||||
const ListCategoryArticles = () =>
|
const ListCategoryArticles = () =>
|
||||||
import('./pages/articles/ListCategoryArticles');
|
import('./pages/articles/ListCategoryArticles.vue');
|
||||||
const ListAllArticles = () => import('./pages/articles/ListAllArticles');
|
const ListAllArticles = () => import('./pages/articles/ListAllArticles.vue');
|
||||||
const DefaultPortalArticles = () =>
|
const DefaultPortalArticles = () =>
|
||||||
import('./pages/articles/DefaultPortalArticles');
|
import('./pages/articles/DefaultPortalArticles.vue');
|
||||||
const NewArticle = () => import('./pages/articles/NewArticle');
|
const NewArticle = () => import('./pages/articles/NewArticle.vue');
|
||||||
const EditArticle = () => import('./pages/articles/EditArticle');
|
const EditArticle = () => import('./pages/articles/EditArticle.vue');
|
||||||
|
|
||||||
const portalRoutes = [
|
const portalRoutes = [
|
||||||
{
|
{
|
||||||
path: getPortalRoute(''),
|
path: getPortalRoute(''),
|
||||||
name: 'default_portal_articles',
|
name: 'default_portal_articles',
|
||||||
roles: ['administrator', 'agent'],
|
meta: {
|
||||||
|
permissions: ['administrator'],
|
||||||
|
},
|
||||||
component: DefaultPortalArticles,
|
component: DefaultPortalArticles,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: getPortalRoute('all'),
|
path: getPortalRoute('all'),
|
||||||
name: 'list_all_portals',
|
name: 'list_all_portals',
|
||||||
roles: ['administrator', 'agent'],
|
meta: {
|
||||||
|
permissions: ['administrator', 'agent'],
|
||||||
|
},
|
||||||
component: ListAllPortals,
|
component: ListAllPortals,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -47,55 +53,73 @@ const portalRoutes = [
|
|||||||
path: '',
|
path: '',
|
||||||
name: 'new_portal_information',
|
name: 'new_portal_information',
|
||||||
component: PortalDetails,
|
component: PortalDetails,
|
||||||
roles: ['administrator'],
|
meta: {
|
||||||
|
permissions: ['administrator'],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: ':portalSlug/customization',
|
path: ':portalSlug/customization',
|
||||||
name: 'portal_customization',
|
name: 'portal_customization',
|
||||||
component: PortalCustomization,
|
component: PortalCustomization,
|
||||||
roles: ['administrator'],
|
meta: {
|
||||||
|
permissions: ['administrator'],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: ':portalSlug/finish',
|
path: ':portalSlug/finish',
|
||||||
name: 'portal_finish',
|
name: 'portal_finish',
|
||||||
component: PortalSettingsFinish,
|
component: PortalSettingsFinish,
|
||||||
roles: ['administrator'],
|
meta: {
|
||||||
|
permissions: ['administrator'],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: getPortalRoute(':portalSlug'),
|
path: getPortalRoute(':portalSlug'),
|
||||||
name: 'portalSlug',
|
name: 'portalSlug',
|
||||||
roles: ['administrator', 'agent'],
|
meta: {
|
||||||
|
permissions: ['administrator', 'agent'],
|
||||||
|
},
|
||||||
component: ShowPortal,
|
component: ShowPortal,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: getPortalRoute(':portalSlug/edit'),
|
path: getPortalRoute(':portalSlug/edit'),
|
||||||
roles: ['administrator', 'agent'],
|
meta: {
|
||||||
|
permissions: ['administrator', 'agent'],
|
||||||
|
},
|
||||||
component: EditPortal,
|
component: EditPortal,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
name: 'edit_portal_information',
|
name: 'edit_portal_information',
|
||||||
component: EditPortalBasic,
|
component: EditPortalBasic,
|
||||||
roles: ['administrator'],
|
meta: {
|
||||||
|
permissions: ['administrator'],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'customizations',
|
path: 'customizations',
|
||||||
name: 'edit_portal_customization',
|
name: 'edit_portal_customization',
|
||||||
component: EditPortalCustomization,
|
component: EditPortalCustomization,
|
||||||
roles: ['administrator'],
|
meta: {
|
||||||
|
permissions: ['administrator'],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'locales',
|
path: 'locales',
|
||||||
name: 'edit_portal_locales',
|
name: 'edit_portal_locales',
|
||||||
component: EditPortalLocales,
|
component: EditPortalLocales,
|
||||||
roles: ['administrator'],
|
meta: {
|
||||||
|
permissions: ['administrator'],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'categories',
|
path: 'categories',
|
||||||
name: 'list_all_locale_categories',
|
name: 'list_all_locale_categories',
|
||||||
roles: ['administrator', 'agent'],
|
meta: {
|
||||||
|
permissions: ['administrator', 'agent'],
|
||||||
|
},
|
||||||
component: ListAllCategories,
|
component: ListAllCategories,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -106,39 +130,51 @@ const articleRoutes = [
|
|||||||
{
|
{
|
||||||
path: getPortalRoute(':portalSlug/:locale/articles'),
|
path: getPortalRoute(':portalSlug/:locale/articles'),
|
||||||
name: 'list_all_locale_articles',
|
name: 'list_all_locale_articles',
|
||||||
roles: ['administrator', 'agent'],
|
meta: {
|
||||||
|
permissions: ['administrator', 'agent'],
|
||||||
|
},
|
||||||
component: ListAllArticles,
|
component: ListAllArticles,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: getPortalRoute(':portalSlug/:locale/articles/new'),
|
path: getPortalRoute(':portalSlug/:locale/articles/new'),
|
||||||
name: 'new_article',
|
name: 'new_article',
|
||||||
roles: ['administrator', 'agent'],
|
meta: {
|
||||||
|
permissions: ['administrator', 'agent'],
|
||||||
|
},
|
||||||
component: NewArticle,
|
component: NewArticle,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: getPortalRoute(':portalSlug/:locale/articles/mine'),
|
path: getPortalRoute(':portalSlug/:locale/articles/mine'),
|
||||||
name: 'list_mine_articles',
|
name: 'list_mine_articles',
|
||||||
roles: ['administrator', 'agent'],
|
meta: {
|
||||||
|
permissions: ['administrator', 'agent'],
|
||||||
|
},
|
||||||
component: ListAllArticles,
|
component: ListAllArticles,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: getPortalRoute(':portalSlug/:locale/articles/archived'),
|
path: getPortalRoute(':portalSlug/:locale/articles/archived'),
|
||||||
name: 'list_archived_articles',
|
name: 'list_archived_articles',
|
||||||
roles: ['administrator', 'agent'],
|
meta: {
|
||||||
|
permissions: ['administrator', 'agent'],
|
||||||
|
},
|
||||||
component: ListAllArticles,
|
component: ListAllArticles,
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
path: getPortalRoute(':portalSlug/:locale/articles/draft'),
|
path: getPortalRoute(':portalSlug/:locale/articles/draft'),
|
||||||
name: 'list_draft_articles',
|
name: 'list_draft_articles',
|
||||||
roles: ['administrator', 'agent'],
|
meta: {
|
||||||
|
permissions: ['administrator', 'agent'],
|
||||||
|
},
|
||||||
component: ListAllArticles,
|
component: ListAllArticles,
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
path: getPortalRoute(':portalSlug/:locale/articles/:articleSlug'),
|
path: getPortalRoute(':portalSlug/:locale/articles/:articleSlug'),
|
||||||
name: 'edit_article',
|
name: 'edit_article',
|
||||||
roles: ['administrator', 'agent'],
|
meta: {
|
||||||
|
permissions: ['administrator', 'agent'],
|
||||||
|
},
|
||||||
component: EditArticle,
|
component: EditArticle,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
@ -147,19 +183,25 @@ const categoryRoutes = [
|
|||||||
{
|
{
|
||||||
path: getPortalRoute(':portalSlug/:locale/categories'),
|
path: getPortalRoute(':portalSlug/:locale/categories'),
|
||||||
name: 'all_locale_categories',
|
name: 'all_locale_categories',
|
||||||
roles: ['administrator', 'agent'],
|
meta: {
|
||||||
|
permissions: ['administrator', 'agent'],
|
||||||
|
},
|
||||||
component: ListAllCategories,
|
component: ListAllCategories,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: getPortalRoute(':portalSlug/:locale/categories/new'),
|
path: getPortalRoute(':portalSlug/:locale/categories/new'),
|
||||||
name: 'new_category_in_locale',
|
name: 'new_category_in_locale',
|
||||||
roles: ['administrator', 'agent'],
|
meta: {
|
||||||
|
permissions: ['administrator', 'agent'],
|
||||||
|
},
|
||||||
component: NewCategory,
|
component: NewCategory,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: getPortalRoute(':portalSlug/:locale/categories/:categorySlug'),
|
path: getPortalRoute(':portalSlug/:locale/categories/:categorySlug'),
|
||||||
name: 'show_category',
|
name: 'show_category',
|
||||||
roles: ['administrator', 'agent'],
|
meta: {
|
||||||
|
permissions: ['administrator', 'agent'],
|
||||||
|
},
|
||||||
component: ListAllArticles,
|
component: ListAllArticles,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -167,13 +209,17 @@ const categoryRoutes = [
|
|||||||
':portalSlug/:locale/categories/:categorySlug/articles'
|
':portalSlug/:locale/categories/:categorySlug/articles'
|
||||||
),
|
),
|
||||||
name: 'show_category_articles',
|
name: 'show_category_articles',
|
||||||
roles: ['administrator', 'agent'],
|
meta: {
|
||||||
|
permissions: ['administrator', 'agent'],
|
||||||
|
},
|
||||||
component: ListCategoryArticles,
|
component: ListCategoryArticles,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: getPortalRoute(':portalSlug/:locale/categories/:categorySlug'),
|
path: getPortalRoute(':portalSlug/:locale/categories/:categorySlug'),
|
||||||
name: 'edit_category',
|
name: 'edit_category',
|
||||||
roles: ['administrator', 'agent'],
|
meta: {
|
||||||
|
permissions: ['administrator', 'agent'],
|
||||||
|
},
|
||||||
component: EditCategory,
|
component: EditCategory,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@ -36,7 +36,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const EmojiInput = () => import('shared/components/emoji/EmojiInput');
|
const EmojiInput = () => import('shared/components/emoji/EmojiInput.vue');
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { EmojiInput },
|
components: { EmojiInput },
|
||||||
|
|||||||
@ -12,13 +12,17 @@ export const routes = [
|
|||||||
path: '',
|
path: '',
|
||||||
name: 'inbox_view',
|
name: 'inbox_view',
|
||||||
component: InboxEmptyStateView,
|
component: InboxEmptyStateView,
|
||||||
roles: ['administrator', 'agent'],
|
meta: {
|
||||||
|
permissions: ['administrator', 'agent'],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: ':notification_id',
|
path: ':notification_id',
|
||||||
name: 'inbox_view_conversation',
|
name: 'inbox_view_conversation',
|
||||||
component: InboxDetailView,
|
component: InboxDetailView,
|
||||||
roles: ['administrator', 'agent'],
|
meta: {
|
||||||
|
permissions: ['administrator', 'agent'],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
@ -18,7 +18,9 @@ export const routes = [
|
|||||||
path: '',
|
path: '',
|
||||||
name: 'notifications_index',
|
name: 'notifications_index',
|
||||||
component: NotificationsView,
|
component: NotificationsView,
|
||||||
roles: ['administrator', 'agent'],
|
meta: {
|
||||||
|
permissions: ['administrator', 'agent'],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
@ -6,7 +6,9 @@ export default {
|
|||||||
routes: [
|
routes: [
|
||||||
{
|
{
|
||||||
path: frontendURL('accounts/:accountId/settings/general'),
|
path: frontendURL('accounts/:accountId/settings/general'),
|
||||||
roles: ['administrator'],
|
meta: {
|
||||||
|
permissions: ['administrator'],
|
||||||
|
},
|
||||||
component: SettingsContent,
|
component: SettingsContent,
|
||||||
props: {
|
props: {
|
||||||
headerTitle: 'GENERAL_SETTINGS.TITLE',
|
headerTitle: 'GENERAL_SETTINGS.TITLE',
|
||||||
@ -18,7 +20,9 @@ export default {
|
|||||||
path: '',
|
path: '',
|
||||||
name: 'general_settings_index',
|
name: 'general_settings_index',
|
||||||
component: Index,
|
component: Index,
|
||||||
roles: ['administrator'],
|
meta: {
|
||||||
|
permissions: ['administrator'],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
@ -8,7 +8,9 @@ export default {
|
|||||||
routes: [
|
routes: [
|
||||||
{
|
{
|
||||||
path: frontendURL('accounts/:accountId/settings/agent-bots'),
|
path: frontendURL('accounts/:accountId/settings/agent-bots'),
|
||||||
roles: ['administrator'],
|
meta: {
|
||||||
|
permissions: ['administrator'],
|
||||||
|
},
|
||||||
component: SettingsContent,
|
component: SettingsContent,
|
||||||
props: {
|
props: {
|
||||||
headerTitle: 'AGENT_BOTS.HEADER',
|
headerTitle: 'AGENT_BOTS.HEADER',
|
||||||
@ -20,19 +22,25 @@ export default {
|
|||||||
path: '',
|
path: '',
|
||||||
name: 'agent_bots',
|
name: 'agent_bots',
|
||||||
component: Bot,
|
component: Bot,
|
||||||
roles: ['administrator'],
|
meta: {
|
||||||
|
permissions: ['administrator'],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'csml/new',
|
path: 'csml/new',
|
||||||
name: 'agent_bots_csml_new',
|
name: 'agent_bots_csml_new',
|
||||||
component: CsmlNewBot,
|
component: CsmlNewBot,
|
||||||
roles: ['administrator'],
|
meta: {
|
||||||
|
permissions: ['administrator'],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'csml/:botId',
|
path: 'csml/:botId',
|
||||||
name: 'agent_bots_csml_edit',
|
name: 'agent_bots_csml_edit',
|
||||||
component: CsmlEditBot,
|
component: CsmlEditBot,
|
||||||
roles: ['administrator'],
|
meta: {
|
||||||
|
permissions: ['administrator'],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
<woot-modal :show.sync="show" :on-close="onClose">
|
<woot-modal :show.sync="show" :on-close="onClose">
|
||||||
<div class="h-auto overflow-auto flex flex-col">
|
<div class="flex flex-col h-auto overflow-auto">
|
||||||
<woot-modal-header
|
<woot-modal-header
|
||||||
:header-title="$t('AGENT_MGMT.ADD.TITLE')"
|
:header-title="$t('AGENT_MGMT.ADD.TITLE')"
|
||||||
:header-content="$t('AGENT_MGMT.ADD.DESC')"
|
:header-content="$t('AGENT_MGMT.ADD.DESC')"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<form
|
<form
|
||||||
class="flex flex-col w-full items-start"
|
class="flex flex-col items-start w-full"
|
||||||
@submit.prevent="addAgent()"
|
@submit.prevent="addAgent()"
|
||||||
>
|
>
|
||||||
<div class="w-full">
|
<div class="w-full">
|
||||||
@ -45,7 +45,7 @@
|
|||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-row justify-end gap-2 py-2 px-0 w-full">
|
<div class="flex flex-row justify-end w-full gap-2 px-0 py-2">
|
||||||
<div class="w-full">
|
<div class="w-full">
|
||||||
<woot-submit-button
|
<woot-submit-button
|
||||||
:disabled="
|
:disabled="
|
||||||
@ -140,7 +140,7 @@ export default {
|
|||||||
} = error;
|
} = error;
|
||||||
|
|
||||||
let errorMessage = '';
|
let errorMessage = '';
|
||||||
if (error.response.status === 422 && !attributes.includes('base')) {
|
if (error?.response?.status === 422 && !attributes.includes('base')) {
|
||||||
errorMessage = this.$t('AGENT_MGMT.ADD.API.EXIST_MESSAGE');
|
errorMessage = this.$t('AGENT_MGMT.ADD.API.EXIST_MESSAGE');
|
||||||
} else {
|
} else {
|
||||||
errorMessage = this.$t('AGENT_MGMT.ADD.API.ERROR_MESSAGE');
|
errorMessage = this.$t('AGENT_MGMT.ADD.API.ERROR_MESSAGE');
|
||||||
|
|||||||
@ -15,14 +15,15 @@ export default {
|
|||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
name: 'agents_wrapper',
|
|
||||||
redirect: 'list',
|
redirect: 'list',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'list',
|
path: 'list',
|
||||||
name: 'agent_list',
|
name: 'agent_list',
|
||||||
component: AgentHome,
|
component: AgentHome,
|
||||||
roles: ['administrator'],
|
meta: {
|
||||||
|
permissions: ['administrator'],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
@ -15,14 +15,15 @@ export default {
|
|||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
name: 'attributes_wrapper',
|
|
||||||
redirect: 'list',
|
redirect: 'list',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'list',
|
path: 'list',
|
||||||
name: 'attributes_list',
|
name: 'attributes_list',
|
||||||
component: AttributesHome,
|
component: AttributesHome,
|
||||||
roles: ['administrator'],
|
meta: {
|
||||||
|
permissions: ['administrator'],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
@ -16,13 +16,14 @@ export default {
|
|||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
name: 'auditlogs_wrapper',
|
|
||||||
redirect: 'list',
|
redirect: 'list',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'list',
|
path: 'list',
|
||||||
name: 'auditlogs_list',
|
name: 'auditlogs_list',
|
||||||
roles: ['administrator'],
|
meta: {
|
||||||
|
permissions: ['administrator'],
|
||||||
|
},
|
||||||
component: AuditLogsHome,
|
component: AuditLogsHome,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@ -15,14 +15,15 @@ export default {
|
|||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
name: 'automation_wrapper',
|
|
||||||
redirect: 'list',
|
redirect: 'list',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'list',
|
path: 'list',
|
||||||
name: 'automation_list',
|
name: 'automation_list',
|
||||||
component: Automation,
|
component: Automation,
|
||||||
roles: ['administrator'],
|
meta: {
|
||||||
|
permissions: ['administrator'],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
@ -6,7 +6,9 @@ export default {
|
|||||||
routes: [
|
routes: [
|
||||||
{
|
{
|
||||||
path: frontendURL('accounts/:accountId/settings/billing'),
|
path: frontendURL('accounts/:accountId/settings/billing'),
|
||||||
roles: ['administrator'],
|
meta: {
|
||||||
|
permissions: ['administrator'],
|
||||||
|
},
|
||||||
component: SettingsContent,
|
component: SettingsContent,
|
||||||
props: {
|
props: {
|
||||||
headerTitle: 'BILLING_SETTINGS.TITLE',
|
headerTitle: 'BILLING_SETTINGS.TITLE',
|
||||||
@ -18,7 +20,9 @@ export default {
|
|||||||
path: '',
|
path: '',
|
||||||
name: 'billing_settings_index',
|
name: 'billing_settings_index',
|
||||||
component: Index,
|
component: Index,
|
||||||
roles: ['administrator'],
|
meta: {
|
||||||
|
permissions: ['administrator'],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
@ -19,7 +19,9 @@ export default {
|
|||||||
{
|
{
|
||||||
path: 'ongoing',
|
path: 'ongoing',
|
||||||
name: 'ongoing_campaigns',
|
name: 'ongoing_campaigns',
|
||||||
roles: ['administrator'],
|
meta: {
|
||||||
|
permissions: ['administrator'],
|
||||||
|
},
|
||||||
component: Index,
|
component: Index,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -35,7 +37,9 @@ export default {
|
|||||||
{
|
{
|
||||||
path: 'one_off',
|
path: 'one_off',
|
||||||
name: 'one_off',
|
name: 'one_off',
|
||||||
roles: ['administrator'],
|
meta: {
|
||||||
|
permissions: ['administrator'],
|
||||||
|
},
|
||||||
component: Index,
|
component: Index,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@ -16,13 +16,14 @@ export default {
|
|||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
name: 'canned_wrapper',
|
|
||||||
redirect: 'list',
|
redirect: 'list',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'list',
|
path: 'list',
|
||||||
name: 'canned_list',
|
name: 'canned_list',
|
||||||
roles: ['administrator', 'agent'],
|
meta: {
|
||||||
|
permissions: ['administrator', 'agent'],
|
||||||
|
},
|
||||||
component: CannedHome,
|
component: CannedHome,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
import Facebook from './channels/Facebook';
|
import Facebook from './channels/Facebook.vue';
|
||||||
import Website from './channels/Website';
|
import Website from './channels/Website.vue';
|
||||||
import Twitter from './channels/Twitter';
|
import Twitter from './channels/Twitter.vue';
|
||||||
import Api from './channels/Api';
|
import Api from './channels/Api.vue';
|
||||||
import Email from './channels/Email';
|
import Email from './channels/Email.vue';
|
||||||
import Sms from './channels/Sms';
|
import Sms from './channels/Sms.vue';
|
||||||
import Whatsapp from './channels/Whatsapp';
|
import Whatsapp from './channels/Whatsapp.vue';
|
||||||
import Line from './channels/Line';
|
import Line from './channels/Line.vue';
|
||||||
import Telegram from './channels/Telegram';
|
import Telegram from './channels/Telegram.vue';
|
||||||
|
|
||||||
const channelViewList = {
|
const channelViewList = {
|
||||||
facebook: Facebook,
|
facebook: Facebook,
|
||||||
|
|||||||
@ -28,14 +28,15 @@ export default {
|
|||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
name: 'settings_inbox',
|
|
||||||
redirect: 'list',
|
redirect: 'list',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'list',
|
path: 'list',
|
||||||
name: 'settings_inbox_list',
|
name: 'settings_inbox_list',
|
||||||
component: InboxHome,
|
component: InboxHome,
|
||||||
roles: ['administrator'],
|
meta: {
|
||||||
|
permissions: ['administrator'],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'new',
|
path: 'new',
|
||||||
@ -45,19 +46,25 @@ export default {
|
|||||||
path: '',
|
path: '',
|
||||||
name: 'settings_inbox_new',
|
name: 'settings_inbox_new',
|
||||||
component: ChannelList,
|
component: ChannelList,
|
||||||
roles: ['administrator'],
|
meta: {
|
||||||
|
permissions: ['administrator'],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: ':inbox_id/finish',
|
path: ':inbox_id/finish',
|
||||||
name: 'settings_inbox_finish',
|
name: 'settings_inbox_finish',
|
||||||
component: FinishSetup,
|
component: FinishSetup,
|
||||||
roles: ['administrator'],
|
meta: {
|
||||||
|
permissions: ['administrator'],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: ':sub_page',
|
path: ':sub_page',
|
||||||
name: 'settings_inboxes_page_channel',
|
name: 'settings_inboxes_page_channel',
|
||||||
component: channelFactory.create(),
|
component: channelFactory.create(),
|
||||||
roles: ['administrator'],
|
meta: {
|
||||||
|
permissions: ['administrator'],
|
||||||
|
},
|
||||||
props: route => {
|
props: route => {
|
||||||
return { channel_name: route.params.sub_page };
|
return { channel_name: route.params.sub_page };
|
||||||
},
|
},
|
||||||
@ -65,7 +72,9 @@ export default {
|
|||||||
{
|
{
|
||||||
path: ':inbox_id/agents',
|
path: ':inbox_id/agents',
|
||||||
name: 'settings_inboxes_add_agents',
|
name: 'settings_inboxes_add_agents',
|
||||||
roles: ['administrator'],
|
meta: {
|
||||||
|
permissions: ['administrator'],
|
||||||
|
},
|
||||||
component: AddAgents,
|
component: AddAgents,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -74,7 +83,9 @@ export default {
|
|||||||
path: ':inboxId',
|
path: ':inboxId',
|
||||||
name: 'settings_inbox_show',
|
name: 'settings_inbox_show',
|
||||||
component: Settings,
|
component: Settings,
|
||||||
roles: ['administrator'],
|
meta: {
|
||||||
|
permissions: ['administrator'],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
@ -26,13 +26,17 @@ export default {
|
|||||||
path: '',
|
path: '',
|
||||||
name: 'settings_applications',
|
name: 'settings_applications',
|
||||||
component: Index,
|
component: Index,
|
||||||
roles: ['administrator'],
|
meta: {
|
||||||
|
permissions: ['administrator'],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: ':integration_id',
|
path: ':integration_id',
|
||||||
name: 'settings_applications_integration',
|
name: 'settings_applications_integration',
|
||||||
component: IntegrationHooks,
|
component: IntegrationHooks,
|
||||||
roles: ['administrator'],
|
meta: {
|
||||||
|
permissions: ['administrator'],
|
||||||
|
},
|
||||||
props: route => ({
|
props: route => ({
|
||||||
integrationId: route.params.integration_id,
|
integrationId: route.params.integration_id,
|
||||||
}),
|
}),
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="h-auto overflow-auto flex flex-col">
|
<div class="flex flex-col h-auto overflow-auto">
|
||||||
<woot-modal-header
|
<woot-modal-header
|
||||||
:header-title="$t('INTEGRATION_SETTINGS.WEBHOOK.EDIT.TITLE')"
|
:header-title="$t('INTEGRATION_SETTINGS.WEBHOOK.EDIT.TITLE')"
|
||||||
/>
|
/>
|
||||||
@ -51,7 +51,7 @@ export default {
|
|||||||
this.onClose();
|
this.onClose();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const alertMessage =
|
const alertMessage =
|
||||||
error.response.data.message ||
|
error?.response?.data?.message ||
|
||||||
this.$t('INTEGRATION_SETTINGS.WEBHOOK.EDIT.API.ERROR_MESSAGE');
|
this.$t('INTEGRATION_SETTINGS.WEBHOOK.EDIT.API.ERROR_MESSAGE');
|
||||||
this.showAlert(alertMessage);
|
this.showAlert(alertMessage);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -30,32 +30,42 @@ export default {
|
|||||||
path: '',
|
path: '',
|
||||||
name: 'settings_integrations',
|
name: 'settings_integrations',
|
||||||
component: Index,
|
component: Index,
|
||||||
roles: ['administrator'],
|
meta: {
|
||||||
|
permissions: ['administrator'],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'webhook',
|
path: 'webhook',
|
||||||
component: Webhook,
|
component: Webhook,
|
||||||
name: 'settings_integrations_webhook',
|
name: 'settings_integrations_webhook',
|
||||||
roles: ['administrator'],
|
meta: {
|
||||||
|
permissions: ['administrator'],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'dashboard-apps',
|
path: 'dashboard-apps',
|
||||||
component: DashboardApps,
|
component: DashboardApps,
|
||||||
name: 'settings_integrations_dashboard_apps',
|
name: 'settings_integrations_dashboard_apps',
|
||||||
roles: ['administrator'],
|
meta: {
|
||||||
|
permissions: ['administrator'],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'slack',
|
path: 'slack',
|
||||||
name: 'settings_integrations_slack',
|
name: 'settings_integrations_slack',
|
||||||
component: Slack,
|
component: Slack,
|
||||||
roles: ['administrator'],
|
meta: {
|
||||||
|
permissions: ['administrator'],
|
||||||
|
},
|
||||||
props: route => ({ code: route.query.code }),
|
props: route => ({ code: route.query.code }),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: ':integration_id',
|
path: ':integration_id',
|
||||||
name: 'settings_integrations_integration',
|
name: 'settings_integrations_integration',
|
||||||
component: ShowIntegration,
|
component: ShowIntegration,
|
||||||
roles: ['administrator'],
|
meta: {
|
||||||
|
permissions: ['administrator'],
|
||||||
|
},
|
||||||
props: route => {
|
props: route => {
|
||||||
return {
|
return {
|
||||||
integrationId: route.params.integration_id,
|
integrationId: route.params.integration_id,
|
||||||
|
|||||||
@ -17,13 +17,17 @@ export default {
|
|||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
name: 'labels_wrapper',
|
name: 'labels_wrapper',
|
||||||
roles: ['administrator'],
|
meta: {
|
||||||
|
permissions: ['administrator'],
|
||||||
|
},
|
||||||
redirect: 'list',
|
redirect: 'list',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'list',
|
path: 'list',
|
||||||
name: 'labels_list',
|
name: 'labels_list',
|
||||||
roles: ['administrator'],
|
meta: {
|
||||||
|
permissions: ['administrator'],
|
||||||
|
},
|
||||||
component: Index,
|
component: Index,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@ -23,19 +23,25 @@ export default {
|
|||||||
path: '',
|
path: '',
|
||||||
name: 'macros_wrapper',
|
name: 'macros_wrapper',
|
||||||
component: Macros,
|
component: Macros,
|
||||||
roles: ['administrator', 'agent'],
|
meta: {
|
||||||
|
permissions: ['administrator', 'agent'],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'new',
|
path: 'new',
|
||||||
name: 'macros_new',
|
name: 'macros_new',
|
||||||
component: MacroEditor,
|
component: MacroEditor,
|
||||||
roles: ['administrator', 'agent'],
|
meta: {
|
||||||
|
permissions: ['administrator', 'agent'],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: ':macroId/edit',
|
path: ':macroId/edit',
|
||||||
name: 'macros_edit',
|
name: 'macros_edit',
|
||||||
component: MacroEditor,
|
component: MacroEditor,
|
||||||
roles: ['administrator', 'agent'],
|
meta: {
|
||||||
|
permissions: ['administrator', 'agent'],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
@ -8,14 +8,18 @@ export default {
|
|||||||
{
|
{
|
||||||
path: frontendURL('accounts/:accountId/profile'),
|
path: frontendURL('accounts/:accountId/profile'),
|
||||||
name: 'profile_settings',
|
name: 'profile_settings',
|
||||||
roles: ['administrator', 'agent'],
|
meta: {
|
||||||
|
permissions: ['administrator', 'agent'],
|
||||||
|
},
|
||||||
component: SettingsContent,
|
component: SettingsContent,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: 'settings',
|
path: 'settings',
|
||||||
name: 'profile_settings_index',
|
name: 'profile_settings_index',
|
||||||
component: Index,
|
component: Index,
|
||||||
roles: ['administrator', 'agent'],
|
meta: {
|
||||||
|
permissions: ['administrator', 'agent'],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { shallowMount, createLocalVue } from '@vue/test-utils';
|
import { shallowMount, createLocalVue } from '@vue/test-utils';
|
||||||
import Vuex from 'vuex';
|
import Vuex from 'vuex';
|
||||||
import ReportsFiltersAgents from '../../Filters/Agents';
|
import ReportsFiltersAgents from '../../Filters/Agents.vue';
|
||||||
|
|
||||||
const localVue = createLocalVue();
|
const localVue = createLocalVue();
|
||||||
localVue.use(Vuex);
|
localVue.use(Vuex);
|
||||||
@ -16,7 +16,7 @@ const mockStore = new Vuex.Store({
|
|||||||
getAgents: state => state.agents,
|
getAgents: state => state.agents,
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
get: jest.fn(),
|
get: vi.fn(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -50,7 +50,7 @@ describe('ReportsFiltersAgents.vue', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('dispatches the "agents/get" action when the component is mounted', () => {
|
it('dispatches the "agents/get" action when the component is mounted', () => {
|
||||||
const dispatchSpy = jest.spyOn(mockStore, 'dispatch');
|
const dispatchSpy = vi.spyOn(mockStore, 'dispatch');
|
||||||
|
|
||||||
shallowMount(ReportsFiltersAgents, mountParams);
|
shallowMount(ReportsFiltersAgents, mountParams);
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user