Compare commits
112
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6e3375fb84 | ||
|
|
58ce9bfc19 | ||
|
|
63a071f0d3 | ||
|
|
f0d3ae09d9 | ||
|
|
5c9ab21617 | ||
|
|
b71a580573 | ||
|
|
6a73953003 | ||
|
|
336af1ac9a | ||
|
|
e4de366b8d | ||
|
|
373c3dcd6c | ||
|
|
4b12dd89b0 | ||
|
|
c19cfeaa81 | ||
|
|
fa118ad18c | ||
|
|
249bfe44bb | ||
|
|
826d9ec5a7 | ||
|
|
d8b53f5d2f | ||
|
|
08ced452e1 | ||
|
|
24fe3805d8 | ||
|
|
845b0c0a68 | ||
|
|
8ce7539c5e | ||
|
|
5ee980465f | ||
|
|
fd633e1613 | ||
|
|
12a64f1b10 | ||
|
|
ffc2d98fb8 | ||
|
|
f7f04f788e | ||
|
|
a88d155dd7 | ||
|
|
e8b7e791a5 | ||
|
|
b18cac77fb | ||
|
|
647161121e | ||
|
|
cbbe939662 | ||
|
|
30cb141e5e | ||
|
|
b0d5c438ac | ||
|
|
3eefa4daee | ||
|
|
ddce999385 | ||
|
|
ff915dd2ea | ||
|
|
dd0930d75e | ||
|
|
3f0d96c24d | ||
|
|
c16b801562 | ||
|
|
018c468303 | ||
|
|
48bf8d08e5 | ||
|
|
fabb3c8da4 | ||
|
|
bdeb2f9812 | ||
|
|
53dc38e650 | ||
|
|
27fc24375d | ||
|
|
f999777a2d | ||
|
|
71d1e98765 | ||
|
|
2c93d563c1 | ||
|
|
f1b556d4a3 | ||
|
|
73c119cd3d | ||
|
|
0ad2f94151 | ||
|
|
f8cb806548 | ||
|
|
9ba5adfd60 | ||
|
|
0dd5104acb | ||
|
|
0968aead6c | ||
|
|
53d530b815 | ||
|
|
2429daa45c | ||
|
|
86894b27b5 | ||
|
|
05df4d4212 | ||
|
|
47fd0ea2b2 | ||
|
|
30b8a365dc | ||
|
|
29110ffd6b | ||
|
|
e9950afb1a | ||
|
|
f21298aeb9 | ||
|
|
7b6a56714f | ||
|
|
94a20af9db | ||
|
|
616371adbb | ||
|
|
2d4ef0c328 | ||
|
|
5e6e234afe | ||
|
|
18e9e771b5 | ||
|
|
0245e01763 | ||
|
|
fcd40e9636 | ||
|
|
4f5c5e9f85 | ||
|
|
cb07ac16d1 | ||
|
|
4fa299ff15 | ||
|
|
71e9566854 | ||
|
|
272f920811 | ||
|
|
e39d19b1e8 | ||
|
|
6c39aed882 | ||
|
|
8d43101892 | ||
|
|
f05535c5ad | ||
|
|
4503ba018d | ||
|
|
8bc2bc6d40 | ||
|
|
bfd192ebb2 | ||
|
|
6e13d22ff7 | ||
|
|
3e8b6e1aee | ||
|
|
3038aaef67 | ||
|
|
57a68666cb | ||
|
|
4acb824857 | ||
|
|
5c74674c2b | ||
|
|
52e49d5f9d | ||
|
|
e529e1206e | ||
|
|
a8009c7b39 | ||
|
|
99bb074656 | ||
|
|
9322112481 | ||
|
|
ce4cfee8bd | ||
|
|
f31fc2b375 | ||
|
|
e5f7807833 | ||
|
|
2483d99d3c | ||
|
|
336584c95a | ||
|
|
9ebabb9832 | ||
|
|
2acf09b3eb | ||
|
|
d18d3e9e2e | ||
|
|
deec1d213b | ||
|
|
25c3fb3c36 | ||
|
|
e2a6dc3e04 | ||
|
|
57feedbf25 | ||
|
|
71310c21ad | ||
|
|
623cd72897 | ||
|
|
04aa13e8aa | ||
|
|
fe8a67b76a | ||
|
|
e204fe1c1f | ||
|
|
08fa370261 |
@@ -15,9 +15,9 @@ defaults: &defaults
|
|||||||
- image: cimg/postgres:15.3
|
- image: cimg/postgres:15.3
|
||||||
- image: cimg/redis:6.2.6
|
- image: cimg/redis:6.2.6
|
||||||
environment:
|
environment:
|
||||||
- RAILS_LOG_TO_STDOUT: false
|
- RAILS_LOG_TO_STDOUT: false
|
||||||
- COVERAGE: true
|
- COVERAGE: true
|
||||||
- LOG_LEVEL: warn
|
- LOG_LEVEL: warn
|
||||||
parallelism: 4
|
parallelism: 4
|
||||||
resource_class: large
|
resource_class: large
|
||||||
|
|
||||||
@@ -46,7 +46,7 @@ jobs:
|
|||||||
export NVM_DIR="$HOME/.nvm"
|
export NVM_DIR="$HOME/.nvm"
|
||||||
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
|
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
|
||||||
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"
|
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"
|
||||||
nvm install v16
|
nvm install v20
|
||||||
echo 'export NVM_DIR="$HOME/.nvm"' >> $BASH_ENV
|
echo 'export NVM_DIR="$HOME/.nvm"' >> $BASH_ENV
|
||||||
echo '[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"' >> $BASH_ENV
|
echo '[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"' >> $BASH_ENV
|
||||||
|
|
||||||
@@ -64,7 +64,6 @@ jobs:
|
|||||||
- ~/.bundle
|
- ~/.bundle
|
||||||
key: chatwoot-bundle-{{ .Environment.CACHE_VERSION }}-v20220524-{{ checksum "Gemfile.lock" }}
|
key: chatwoot-bundle-{{ .Environment.CACHE_VERSION }}-v20220524-{{ checksum "Gemfile.lock" }}
|
||||||
|
|
||||||
|
|
||||||
# Only necessary if app uses webpacker or yarn in some other way
|
# Only necessary if app uses webpacker or yarn in some other way
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
@@ -82,7 +81,7 @@ jobs:
|
|||||||
- ~/.cache/yarn
|
- ~/.cache/yarn
|
||||||
|
|
||||||
- run:
|
- run:
|
||||||
name: Download cc-test-reporter
|
name: Download cc-test-reporter
|
||||||
command: |
|
command: |
|
||||||
mkdir -p ~/tmp
|
mkdir -p ~/tmp
|
||||||
curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ~/tmp/cc-test-reporter
|
curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ~/tmp/cc-test-reporter
|
||||||
@@ -182,7 +181,7 @@ jobs:
|
|||||||
- attach_workspace:
|
- attach_workspace:
|
||||||
at: ~/build
|
at: ~/build
|
||||||
- run:
|
- run:
|
||||||
name: Download cc-test-reporter
|
name: Download cc-test-reporter
|
||||||
command: |
|
command: |
|
||||||
mkdir -p ~/tmp
|
mkdir -p ~/tmp
|
||||||
curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ~/tmp/cc-test-reporter
|
curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ~/tmp/cc-test-reporter
|
||||||
@@ -204,4 +203,4 @@ workflows:
|
|||||||
- build
|
- build
|
||||||
- upload-coverage:
|
- upload-coverage:
|
||||||
requires:
|
requires:
|
||||||
- build
|
- build
|
||||||
|
|||||||
@@ -199,6 +199,8 @@ ANDROID_SHA256_CERT_FINGERPRINT=AC:73:8E:DE:EB:56:EA:CC:10:87:02:A7:65:37:7B:38:
|
|||||||
## Rack Attack configuration
|
## Rack Attack configuration
|
||||||
## To prevent and throttle abusive requests
|
## To prevent and throttle abusive requests
|
||||||
# ENABLE_RACK_ATTACK=true
|
# ENABLE_RACK_ATTACK=true
|
||||||
|
# RACK_ATTACK_LIMIT=300
|
||||||
|
# ENABLE_RACK_ATTACK_WIDGET_API=true
|
||||||
|
|
||||||
## Running chatwoot as an API only server
|
## Running chatwoot as an API only server
|
||||||
## setting this value to true will disable the frontend dashboard endpoints
|
## setting this value to true will disable the frontend dashboard endpoints
|
||||||
|
|||||||
+7
-3
@@ -6,7 +6,7 @@ module.exports = {
|
|||||||
'plugin:storybook/recommended',
|
'plugin:storybook/recommended',
|
||||||
],
|
],
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
parser: 'babel-eslint',
|
parser: '@babel/eslint-parser',
|
||||||
ecmaVersion: 2020,
|
ecmaVersion: 2020,
|
||||||
sourceType: 'module',
|
sourceType: 'module',
|
||||||
},
|
},
|
||||||
@@ -24,13 +24,16 @@ module.exports = {
|
|||||||
'jsx-a11y/label-has-for': 'off',
|
'jsx-a11y/label-has-for': 'off',
|
||||||
'jsx-a11y/anchor-is-valid': 'off',
|
'jsx-a11y/anchor-is-valid': 'off',
|
||||||
'import/no-unresolved': 'off',
|
'import/no-unresolved': 'off',
|
||||||
|
'vue/html-indent': 'off',
|
||||||
|
'vue/multi-word-component-names': 'off',
|
||||||
'vue/max-attributes-per-line': [
|
'vue/max-attributes-per-line': [
|
||||||
'error',
|
'error',
|
||||||
{
|
{
|
||||||
singleline: 20,
|
singleline: {
|
||||||
|
max: 20,
|
||||||
|
},
|
||||||
multiline: {
|
multiline: {
|
||||||
max: 1,
|
max: 1,
|
||||||
allowFirstLine: false,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -47,6 +50,7 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
'vue/no-v-html': 'off',
|
'vue/no-v-html': 'off',
|
||||||
|
'vue/component-definition-name-casing': 'off',
|
||||||
'vue/singleline-html-element-content-newline': 'off',
|
'vue/singleline-html-element-content-newline': 'off',
|
||||||
'import/extensions': ['off'],
|
'import/extensions': ['off'],
|
||||||
'no-console': 'error',
|
'no-console': 'error',
|
||||||
|
|||||||
@@ -52,7 +52,8 @@ jobs:
|
|||||||
|
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 16
|
node-version: 20
|
||||||
|
cache: yarn
|
||||||
|
|
||||||
- name: yarn
|
- name: yarn
|
||||||
run: yarn install
|
run: yarn install
|
||||||
@@ -75,6 +76,8 @@ jobs:
|
|||||||
- name: Run backend tests
|
- name: Run backend tests
|
||||||
run: |
|
run: |
|
||||||
bundle exec rspec --profile=10 --format documentation
|
bundle exec rspec --profile=10 --format documentation
|
||||||
|
env:
|
||||||
|
NODE_OPTIONS: --openssl-legacy-provider
|
||||||
|
|
||||||
- name: Upload rails log folder
|
- name: Upload rails log folder
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
|
|||||||
@@ -51,7 +51,8 @@ jobs:
|
|||||||
|
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 16
|
node-version: 20
|
||||||
|
cache: yarn
|
||||||
|
|
||||||
- name: yarn
|
- name: yarn
|
||||||
run: yarn install
|
run: yarn install
|
||||||
@@ -68,7 +69,12 @@ jobs:
|
|||||||
# Run Response Bot specs
|
# Run Response Bot specs
|
||||||
- name: Run backend tests
|
- name: Run backend tests
|
||||||
run: |
|
run: |
|
||||||
bundle exec rspec spec/enterprise/controllers/api/v1/accounts/response_sources_controller_spec.rb spec/enterprise/controllers/enterprise/api/v1/accounts/inboxes_controller_spec.rb:47 --profile=10 --format documentation
|
bundle exec rspec \
|
||||||
|
spec/enterprise/controllers/api/v1/accounts/response_sources_controller_spec.rb \
|
||||||
|
spec/enterprise/services/enterprise/message_templates/response_bot_service_spec.rb \
|
||||||
|
spec/enterprise/controllers/enterprise/api/v1/accounts/inboxes_controller_spec.rb:47 \
|
||||||
|
--profile=10 \
|
||||||
|
--format documentation
|
||||||
|
|
||||||
- name: Upload rails log folder
|
- name: Upload rails log folder
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
|
|||||||
@@ -21,7 +21,8 @@ jobs:
|
|||||||
|
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 16
|
node-version: 20
|
||||||
|
cache: 'yarn'
|
||||||
|
|
||||||
- name: yarn
|
- name: yarn
|
||||||
run: yarn install
|
run: yarn install
|
||||||
@@ -30,9 +31,11 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
rm -rf enterprise
|
rm -rf enterprise
|
||||||
rm -rf spec/enterprise
|
rm -rf spec/enterprise
|
||||||
|
|
||||||
- name: Run asset compile
|
- name: Run asset compile
|
||||||
run: bundle exec rake assets:precompile
|
run: bundle exec rake assets:precompile
|
||||||
|
env:
|
||||||
|
NODE_OPTIONS: --openssl-legacy-provider
|
||||||
|
|
||||||
- name: Size Check
|
- name: Size Check
|
||||||
run: yarn run size
|
run: yarn run size
|
||||||
|
|||||||
+4
-7
@@ -8,15 +8,11 @@ Layout/LineLength:
|
|||||||
Max: 150
|
Max: 150
|
||||||
|
|
||||||
Metrics/ClassLength:
|
Metrics/ClassLength:
|
||||||
Max: 125
|
Max: 175
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'app/models/conversation.rb'
|
|
||||||
- 'app/models/contact.rb'
|
|
||||||
- 'app/mailers/conversation_reply_mailer.rb'
|
|
||||||
- 'app/models/message.rb'
|
- 'app/models/message.rb'
|
||||||
- 'app/builders/messages/facebook/message_builder.rb'
|
- 'app/models/conversation.rb'
|
||||||
- 'app/controllers/api/v1/accounts/contacts_controller.rb'
|
|
||||||
- 'app/listeners/action_cable_listener.rb'
|
|
||||||
RSpec/ExampleLength:
|
RSpec/ExampleLength:
|
||||||
Max: 25
|
Max: 25
|
||||||
Style/Documentation:
|
Style/Documentation:
|
||||||
@@ -62,6 +58,7 @@ Metrics/BlockLength:
|
|||||||
Metrics/ModuleLength:
|
Metrics/ModuleLength:
|
||||||
Exclude:
|
Exclude:
|
||||||
- lib/seeders/message_seeder.rb
|
- lib/seeders/message_seeder.rb
|
||||||
|
- spec/support/slack_stubs.rb
|
||||||
Rails/ApplicationController:
|
Rails/ApplicationController:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'app/controllers/api/v1/widget/messages_controller.rb'
|
- 'app/controllers/api/v1/widget/messages_controller.rb'
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ ruby '3.2.2'
|
|||||||
|
|
||||||
##-- base gems for rails --##
|
##-- base gems for rails --##
|
||||||
gem 'rack-cors', require: 'rack/cors'
|
gem 'rack-cors', require: 'rack/cors'
|
||||||
gem 'rails', '~> 7.0.7.2'
|
gem 'rails', '~> 7.0.8.0'
|
||||||
# Reduces boot times through caching; required in config/boot.rb
|
# Reduces boot times through caching; required in config/boot.rb
|
||||||
gem 'bootsnap', require: false
|
gem 'bootsnap', require: false
|
||||||
|
|
||||||
@@ -76,6 +76,7 @@ gem 'pundit'
|
|||||||
# super admin
|
# super admin
|
||||||
gem 'administrate', '>= 0.19.0'
|
gem 'administrate', '>= 0.19.0'
|
||||||
gem 'administrate-field-active_storage'
|
gem 'administrate-field-active_storage'
|
||||||
|
gem 'administrate-field-belongs_to_search'
|
||||||
|
|
||||||
##--- gems for pubsub service ---##
|
##--- gems for pubsub service ---##
|
||||||
# https://karolgalanciak.com/blog/2019/11/30/from-activerecord-callbacks-to-publish-slash-subscribe-pattern-and-event-driven-design/
|
# https://karolgalanciak.com/blog/2019/11/30/from-activerecord-callbacks-to-publish-slash-subscribe-pattern-and-event-driven-design/
|
||||||
@@ -108,14 +109,14 @@ gem 'elastic-apm', require: false
|
|||||||
gem 'newrelic_rpm', require: false
|
gem 'newrelic_rpm', require: false
|
||||||
gem 'newrelic-sidekiq-metrics', require: false
|
gem 'newrelic-sidekiq-metrics', require: false
|
||||||
gem 'scout_apm', require: false
|
gem 'scout_apm', require: false
|
||||||
gem 'sentry-rails', '>= 5.10.0', require: false
|
gem 'sentry-rails', '>= 5.11.0', require: false
|
||||||
gem 'sentry-ruby', require: false
|
gem 'sentry-ruby', require: false
|
||||||
gem 'sentry-sidekiq', '>= 5.10.0', require: false
|
gem 'sentry-sidekiq', '>= 5.11.0', require: false
|
||||||
|
|
||||||
##-- background job processing --##
|
##-- background job processing --##
|
||||||
gem 'sidekiq'
|
gem 'sidekiq', '>= 7.1.3'
|
||||||
# We want cron jobs
|
# We want cron jobs
|
||||||
gem 'sidekiq-cron'
|
gem 'sidekiq-cron', '>= 1.10.1'
|
||||||
|
|
||||||
##-- Push notification service --##
|
##-- Push notification service --##
|
||||||
gem 'fcm'
|
gem 'fcm'
|
||||||
@@ -158,7 +159,7 @@ gem 'lograge', '~> 0.13.0', require: false
|
|||||||
# worked with microsoft refresh token
|
# worked with microsoft refresh token
|
||||||
gem 'omniauth-oauth2'
|
gem 'omniauth-oauth2'
|
||||||
|
|
||||||
gem 'audited', '~> 5.3'
|
gem 'audited', '~> 5.4', '>= 5.4.0'
|
||||||
|
|
||||||
# need for google auth
|
# need for google auth
|
||||||
gem 'omniauth'
|
gem 'omniauth'
|
||||||
@@ -188,7 +189,7 @@ group :development do
|
|||||||
gem 'bullet'
|
gem 'bullet'
|
||||||
gem 'letter_opener'
|
gem 'letter_opener'
|
||||||
gem 'scss_lint', require: false
|
gem 'scss_lint', require: false
|
||||||
gem 'web-console'
|
gem 'web-console', '>= 4.2.1'
|
||||||
|
|
||||||
# used in swagger build
|
# used in swagger build
|
||||||
gem 'json_refs'
|
gem 'json_refs'
|
||||||
|
|||||||
+89
-83
@@ -33,70 +33,70 @@ GIT
|
|||||||
GEM
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
actioncable (7.0.7.2)
|
actioncable (7.0.8)
|
||||||
actionpack (= 7.0.7.2)
|
actionpack (= 7.0.8)
|
||||||
activesupport (= 7.0.7.2)
|
activesupport (= 7.0.8)
|
||||||
nio4r (~> 2.0)
|
nio4r (~> 2.0)
|
||||||
websocket-driver (>= 0.6.1)
|
websocket-driver (>= 0.6.1)
|
||||||
actionmailbox (7.0.7.2)
|
actionmailbox (7.0.8)
|
||||||
actionpack (= 7.0.7.2)
|
actionpack (= 7.0.8)
|
||||||
activejob (= 7.0.7.2)
|
activejob (= 7.0.8)
|
||||||
activerecord (= 7.0.7.2)
|
activerecord (= 7.0.8)
|
||||||
activestorage (= 7.0.7.2)
|
activestorage (= 7.0.8)
|
||||||
activesupport (= 7.0.7.2)
|
activesupport (= 7.0.8)
|
||||||
mail (>= 2.7.1)
|
mail (>= 2.7.1)
|
||||||
net-imap
|
net-imap
|
||||||
net-pop
|
net-pop
|
||||||
net-smtp
|
net-smtp
|
||||||
actionmailer (7.0.7.2)
|
actionmailer (7.0.8)
|
||||||
actionpack (= 7.0.7.2)
|
actionpack (= 7.0.8)
|
||||||
actionview (= 7.0.7.2)
|
actionview (= 7.0.8)
|
||||||
activejob (= 7.0.7.2)
|
activejob (= 7.0.8)
|
||||||
activesupport (= 7.0.7.2)
|
activesupport (= 7.0.8)
|
||||||
mail (~> 2.5, >= 2.5.4)
|
mail (~> 2.5, >= 2.5.4)
|
||||||
net-imap
|
net-imap
|
||||||
net-pop
|
net-pop
|
||||||
net-smtp
|
net-smtp
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.0)
|
||||||
actionpack (7.0.7.2)
|
actionpack (7.0.8)
|
||||||
actionview (= 7.0.7.2)
|
actionview (= 7.0.8)
|
||||||
activesupport (= 7.0.7.2)
|
activesupport (= 7.0.8)
|
||||||
rack (~> 2.0, >= 2.2.4)
|
rack (~> 2.0, >= 2.2.4)
|
||||||
rack-test (>= 0.6.3)
|
rack-test (>= 0.6.3)
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.0)
|
||||||
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
||||||
actiontext (7.0.7.2)
|
actiontext (7.0.8)
|
||||||
actionpack (= 7.0.7.2)
|
actionpack (= 7.0.8)
|
||||||
activerecord (= 7.0.7.2)
|
activerecord (= 7.0.8)
|
||||||
activestorage (= 7.0.7.2)
|
activestorage (= 7.0.8)
|
||||||
activesupport (= 7.0.7.2)
|
activesupport (= 7.0.8)
|
||||||
globalid (>= 0.6.0)
|
globalid (>= 0.6.0)
|
||||||
nokogiri (>= 1.8.5)
|
nokogiri (>= 1.8.5)
|
||||||
actionview (7.0.7.2)
|
actionview (7.0.8)
|
||||||
activesupport (= 7.0.7.2)
|
activesupport (= 7.0.8)
|
||||||
builder (~> 3.1)
|
builder (~> 3.1)
|
||||||
erubi (~> 1.4)
|
erubi (~> 1.4)
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.0)
|
||||||
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
||||||
active_record_query_trace (1.8)
|
active_record_query_trace (1.8)
|
||||||
activejob (7.0.7.2)
|
activejob (7.0.8)
|
||||||
activesupport (= 7.0.7.2)
|
activesupport (= 7.0.8)
|
||||||
globalid (>= 0.3.6)
|
globalid (>= 0.3.6)
|
||||||
activemodel (7.0.7.2)
|
activemodel (7.0.8)
|
||||||
activesupport (= 7.0.7.2)
|
activesupport (= 7.0.8)
|
||||||
activerecord (7.0.7.2)
|
activerecord (7.0.8)
|
||||||
activemodel (= 7.0.7.2)
|
activemodel (= 7.0.8)
|
||||||
activesupport (= 7.0.7.2)
|
activesupport (= 7.0.8)
|
||||||
activerecord-import (1.4.1)
|
activerecord-import (1.4.1)
|
||||||
activerecord (>= 4.2)
|
activerecord (>= 4.2)
|
||||||
activestorage (7.0.7.2)
|
activestorage (7.0.8)
|
||||||
actionpack (= 7.0.7.2)
|
actionpack (= 7.0.8)
|
||||||
activejob (= 7.0.7.2)
|
activejob (= 7.0.8)
|
||||||
activerecord (= 7.0.7.2)
|
activerecord (= 7.0.8)
|
||||||
activesupport (= 7.0.7.2)
|
activesupport (= 7.0.8)
|
||||||
marcel (~> 1.0)
|
marcel (~> 1.0)
|
||||||
mini_mime (>= 1.1.0)
|
mini_mime (>= 1.1.0)
|
||||||
activesupport (7.0.7.2)
|
activesupport (7.0.8)
|
||||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||||
i18n (>= 1.6, < 2)
|
i18n (>= 1.6, < 2)
|
||||||
minitest (>= 5.1)
|
minitest (>= 5.1)
|
||||||
@@ -116,13 +116,18 @@ GEM
|
|||||||
administrate-field-active_storage (0.4.2)
|
administrate-field-active_storage (0.4.2)
|
||||||
administrate (>= 0.2.2)
|
administrate (>= 0.2.2)
|
||||||
rails (>= 7.0)
|
rails (>= 7.0)
|
||||||
|
administrate-field-belongs_to_search (0.8.0)
|
||||||
|
administrate (>= 0.3, < 1.0)
|
||||||
|
jbuilder (~> 2)
|
||||||
|
rails (>= 4.2, < 7.1)
|
||||||
|
selectize-rails (~> 0.6)
|
||||||
annotate (3.2.0)
|
annotate (3.2.0)
|
||||||
activerecord (>= 3.2, < 8.0)
|
activerecord (>= 3.2, < 8.0)
|
||||||
rake (>= 10.4, < 14.0)
|
rake (>= 10.4, < 14.0)
|
||||||
ast (2.4.2)
|
ast (2.4.2)
|
||||||
attr_extras (7.1.0)
|
attr_extras (7.1.0)
|
||||||
audited (5.3.3)
|
audited (5.4.0)
|
||||||
activerecord (>= 5.0, < 7.1)
|
activerecord (>= 5.0, < 7.2)
|
||||||
request_store (~> 1.2)
|
request_store (~> 1.2)
|
||||||
aws-eventstream (1.2.0)
|
aws-eventstream (1.2.0)
|
||||||
aws-partitions (1.760.0)
|
aws-partitions (1.760.0)
|
||||||
@@ -269,8 +274,8 @@ GEM
|
|||||||
grpc (~> 1.36)
|
grpc (~> 1.36)
|
||||||
geocoder (1.8.1)
|
geocoder (1.8.1)
|
||||||
gli (2.21.0)
|
gli (2.21.0)
|
||||||
globalid (1.1.0)
|
globalid (1.2.1)
|
||||||
activesupport (>= 5.0)
|
activesupport (>= 6.1)
|
||||||
gmail_xoauth (0.4.2)
|
gmail_xoauth (0.4.2)
|
||||||
oauth (>= 0.3.6)
|
oauth (>= 0.3.6)
|
||||||
google-apis-core (0.11.0)
|
google-apis-core (0.11.0)
|
||||||
@@ -450,9 +455,9 @@ GEM
|
|||||||
mime-types-data (~> 3.2015)
|
mime-types-data (~> 3.2015)
|
||||||
mime-types-data (3.2023.0218.1)
|
mime-types-data (3.2023.0218.1)
|
||||||
mini_magick (4.12.0)
|
mini_magick (4.12.0)
|
||||||
mini_mime (1.1.2)
|
mini_mime (1.1.5)
|
||||||
mini_portile2 (2.8.4)
|
mini_portile2 (2.8.4)
|
||||||
minitest (5.19.0)
|
minitest (5.20.0)
|
||||||
mock_redis (0.36.0)
|
mock_redis (0.36.0)
|
||||||
ruby2_keywords
|
ruby2_keywords
|
||||||
msgpack (1.7.0)
|
msgpack (1.7.0)
|
||||||
@@ -463,7 +468,7 @@ GEM
|
|||||||
activerecord (>= 5.2)
|
activerecord (>= 5.2)
|
||||||
net-http-persistent (4.0.2)
|
net-http-persistent (4.0.2)
|
||||||
connection_pool (~> 2.2)
|
connection_pool (~> 2.2)
|
||||||
net-imap (0.3.6)
|
net-imap (0.3.7)
|
||||||
date
|
date
|
||||||
net-protocol
|
net-protocol
|
||||||
net-pop (0.1.2)
|
net-pop (0.1.2)
|
||||||
@@ -478,14 +483,14 @@ GEM
|
|||||||
sidekiq
|
sidekiq
|
||||||
newrelic_rpm (8.16.0)
|
newrelic_rpm (8.16.0)
|
||||||
nio4r (2.5.9)
|
nio4r (2.5.9)
|
||||||
nokogiri (1.15.3)
|
nokogiri (1.15.4)
|
||||||
mini_portile2 (~> 2.8.2)
|
mini_portile2 (~> 2.8.2)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
nokogiri (1.15.3-arm64-darwin)
|
nokogiri (1.15.4-arm64-darwin)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
nokogiri (1.15.3-x86_64-darwin)
|
nokogiri (1.15.4-x86_64-darwin)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
nokogiri (1.15.3-x86_64-linux)
|
nokogiri (1.15.4-x86_64-linux)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
numo-narray (0.9.2.1)
|
numo-narray (0.9.2.1)
|
||||||
oauth (1.1.0)
|
oauth (1.1.0)
|
||||||
@@ -563,30 +568,30 @@ GEM
|
|||||||
rack-test (2.1.0)
|
rack-test (2.1.0)
|
||||||
rack (>= 1.3)
|
rack (>= 1.3)
|
||||||
rack-timeout (0.6.3)
|
rack-timeout (0.6.3)
|
||||||
rails (7.0.7.2)
|
rails (7.0.8)
|
||||||
actioncable (= 7.0.7.2)
|
actioncable (= 7.0.8)
|
||||||
actionmailbox (= 7.0.7.2)
|
actionmailbox (= 7.0.8)
|
||||||
actionmailer (= 7.0.7.2)
|
actionmailer (= 7.0.8)
|
||||||
actionpack (= 7.0.7.2)
|
actionpack (= 7.0.8)
|
||||||
actiontext (= 7.0.7.2)
|
actiontext (= 7.0.8)
|
||||||
actionview (= 7.0.7.2)
|
actionview (= 7.0.8)
|
||||||
activejob (= 7.0.7.2)
|
activejob (= 7.0.8)
|
||||||
activemodel (= 7.0.7.2)
|
activemodel (= 7.0.8)
|
||||||
activerecord (= 7.0.7.2)
|
activerecord (= 7.0.8)
|
||||||
activestorage (= 7.0.7.2)
|
activestorage (= 7.0.8)
|
||||||
activesupport (= 7.0.7.2)
|
activesupport (= 7.0.8)
|
||||||
bundler (>= 1.15.0)
|
bundler (>= 1.15.0)
|
||||||
railties (= 7.0.7.2)
|
railties (= 7.0.8)
|
||||||
rails-dom-testing (2.1.1)
|
rails-dom-testing (2.2.0)
|
||||||
activesupport (>= 5.0.0)
|
activesupport (>= 5.0.0)
|
||||||
minitest
|
minitest
|
||||||
nokogiri (>= 1.6)
|
nokogiri (>= 1.6)
|
||||||
rails-html-sanitizer (1.6.0)
|
rails-html-sanitizer (1.6.0)
|
||||||
loofah (~> 2.21)
|
loofah (~> 2.21)
|
||||||
nokogiri (~> 1.14)
|
nokogiri (~> 1.14)
|
||||||
railties (7.0.7.2)
|
railties (7.0.8)
|
||||||
actionpack (= 7.0.7.2)
|
actionpack (= 7.0.8)
|
||||||
activesupport (= 7.0.7.2)
|
activesupport (= 7.0.8)
|
||||||
method_source
|
method_source
|
||||||
rake (>= 12.2)
|
rake (>= 12.2)
|
||||||
thor (~> 1.0)
|
thor (~> 1.0)
|
||||||
@@ -598,7 +603,7 @@ GEM
|
|||||||
ffi (~> 1.0)
|
ffi (~> 1.0)
|
||||||
redis (5.0.6)
|
redis (5.0.6)
|
||||||
redis-client (>= 0.9.0)
|
redis-client (>= 0.9.0)
|
||||||
redis-client (0.14.1)
|
redis-client (0.17.0)
|
||||||
connection_pool
|
connection_pool
|
||||||
redis-namespace (1.10.0)
|
redis-namespace (1.10.0)
|
||||||
redis (>= 4)
|
redis (>= 4)
|
||||||
@@ -697,23 +702,23 @@ 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.10.0)
|
sentry-rails (5.11.0)
|
||||||
railties (>= 5.0)
|
railties (>= 5.0)
|
||||||
sentry-ruby (~> 5.10.0)
|
sentry-ruby (~> 5.11.0)
|
||||||
sentry-ruby (5.10.0)
|
sentry-ruby (5.11.0)
|
||||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||||
sentry-sidekiq (5.10.0)
|
sentry-sidekiq (5.11.0)
|
||||||
sentry-ruby (~> 5.10.0)
|
sentry-ruby (~> 5.11.0)
|
||||||
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.1.2)
|
sidekiq (7.1.3)
|
||||||
concurrent-ruby (< 2)
|
concurrent-ruby (< 2)
|
||||||
connection_pool (>= 2.3.0)
|
connection_pool (>= 2.3.0)
|
||||||
rack (>= 2.2.4)
|
rack (>= 2.2.4)
|
||||||
redis-client (>= 0.14.0)
|
redis-client (>= 0.14.0)
|
||||||
sidekiq-cron (1.10.0)
|
sidekiq-cron (1.10.1)
|
||||||
fugit (~> 1.8)
|
fugit (~> 1.8)
|
||||||
globalid (>= 1.0.1)
|
globalid (>= 1.0.1)
|
||||||
sidekiq (>= 6)
|
sidekiq (>= 6)
|
||||||
@@ -786,7 +791,7 @@ GEM
|
|||||||
version_gem (1.1.2)
|
version_gem (1.1.2)
|
||||||
warden (1.2.9)
|
warden (1.2.9)
|
||||||
rack (>= 2.0.9)
|
rack (>= 2.0.9)
|
||||||
web-console (4.2.0)
|
web-console (4.2.1)
|
||||||
actionview (>= 6.0.0)
|
actionview (>= 6.0.0)
|
||||||
activemodel (>= 6.0.0)
|
activemodel (>= 6.0.0)
|
||||||
bindex (>= 0.4.0)
|
bindex (>= 0.4.0)
|
||||||
@@ -805,14 +810,14 @@ GEM
|
|||||||
railties (>= 5.2)
|
railties (>= 5.2)
|
||||||
semantic_range (>= 2.3.0)
|
semantic_range (>= 2.3.0)
|
||||||
webrick (1.8.1)
|
webrick (1.8.1)
|
||||||
websocket-driver (0.7.5)
|
websocket-driver (0.7.6)
|
||||||
websocket-extensions (>= 0.1.0)
|
websocket-extensions (>= 0.1.0)
|
||||||
websocket-extensions (0.1.5)
|
websocket-extensions (0.1.5)
|
||||||
wisper (2.0.0)
|
wisper (2.0.0)
|
||||||
working_hours (1.4.1)
|
working_hours (1.4.1)
|
||||||
activesupport (>= 3.2)
|
activesupport (>= 3.2)
|
||||||
tzinfo
|
tzinfo
|
||||||
zeitwerk (2.6.9)
|
zeitwerk (2.6.11)
|
||||||
|
|
||||||
PLATFORMS
|
PLATFORMS
|
||||||
arm64-darwin-20
|
arm64-darwin-20
|
||||||
@@ -831,9 +836,10 @@ DEPENDENCIES
|
|||||||
acts-as-taggable-on
|
acts-as-taggable-on
|
||||||
administrate (>= 0.19.0)
|
administrate (>= 0.19.0)
|
||||||
administrate-field-active_storage
|
administrate-field-active_storage
|
||||||
|
administrate-field-belongs_to_search
|
||||||
annotate
|
annotate
|
||||||
attr_extras
|
attr_extras
|
||||||
audited (~> 5.3)
|
audited (~> 5.4, >= 5.4.0)
|
||||||
aws-sdk-s3
|
aws-sdk-s3
|
||||||
azure-storage-blob!
|
azure-storage-blob!
|
||||||
barnes
|
barnes
|
||||||
@@ -907,7 +913,7 @@ DEPENDENCIES
|
|||||||
rack-cors
|
rack-cors
|
||||||
rack-mini-profiler (>= 3.1.1)
|
rack-mini-profiler (>= 3.1.1)
|
||||||
rack-timeout
|
rack-timeout
|
||||||
rails (~> 7.0.7.2)
|
rails (~> 7.0.8.0)
|
||||||
redis
|
redis
|
||||||
redis-namespace
|
redis-namespace
|
||||||
responders
|
responders
|
||||||
@@ -922,12 +928,12 @@ DEPENDENCIES
|
|||||||
scout_apm
|
scout_apm
|
||||||
scss_lint
|
scss_lint
|
||||||
seed_dump
|
seed_dump
|
||||||
sentry-rails (>= 5.10.0)
|
sentry-rails (>= 5.11.0)
|
||||||
sentry-ruby
|
sentry-ruby
|
||||||
sentry-sidekiq (>= 5.10.0)
|
sentry-sidekiq (>= 5.11.0)
|
||||||
shoulda-matchers
|
shoulda-matchers
|
||||||
sidekiq
|
sidekiq (>= 7.1.3)
|
||||||
sidekiq-cron
|
sidekiq-cron (>= 1.10.1)
|
||||||
simplecov (= 0.17.1)
|
simplecov (= 0.17.1)
|
||||||
slack-ruby-client (~> 2.0.0)
|
slack-ruby-client (~> 2.0.0)
|
||||||
spring
|
spring
|
||||||
@@ -943,7 +949,7 @@ DEPENDENCIES
|
|||||||
tzinfo-data
|
tzinfo-data
|
||||||
uglifier
|
uglifier
|
||||||
valid_email2
|
valid_email2
|
||||||
web-console
|
web-console (>= 4.2.1)
|
||||||
web-push
|
web-push
|
||||||
webmock
|
webmock
|
||||||
webpacker
|
webpacker
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
backend: bin/rails s -p 3000
|
backend: bin/rails s -p 3000
|
||||||
frontend: bin/webpack-dev-server
|
frontend: export NODE_OPTIONS=--openssl-legacy-provider && bin/webpack-dev-server
|
||||||
# https://github.com/mperham/sidekiq/issues/3090#issuecomment-389748695
|
# https://github.com/mperham/sidekiq/issues/3090#issuecomment-389748695
|
||||||
worker: dotenv bundle exec sidekiq -C config/sidekiq.yml
|
worker: dotenv bundle exec sidekiq -C config/sidekiq.yml
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
2.17.0
|
3.1.0
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
2.3.0
|
2.4.0
|
||||||
|
|||||||
@@ -60,6 +60,9 @@
|
|||||||
],
|
],
|
||||||
"stack": "heroku-20",
|
"stack": "heroku-20",
|
||||||
"buildpacks": [
|
"buildpacks": [
|
||||||
|
{
|
||||||
|
"url": "heroku/nodejs"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"url": "heroku/ruby"
|
"url": "heroku/ruby"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,3 +29,5 @@
|
|||||||
@import 'components/pagination';
|
@import 'components/pagination';
|
||||||
@import 'components/search';
|
@import 'components/search';
|
||||||
@import 'components/reports';
|
@import 'components/reports';
|
||||||
|
|
||||||
|
@import 'custom_styles';
|
||||||
|
|||||||
@@ -24,14 +24,6 @@ select {
|
|||||||
font-size: $base-font-size;
|
font-size: $base-font-size;
|
||||||
}
|
}
|
||||||
|
|
||||||
input,
|
|
||||||
select,
|
|
||||||
textarea {
|
|
||||||
display: block;
|
|
||||||
font-family: $base-font-family;
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
[type="color"],
|
[type="color"],
|
||||||
[type="date"],
|
[type="date"],
|
||||||
[type="datetime-local"],
|
[type="datetime-local"],
|
||||||
@@ -51,6 +43,7 @@ textarea {
|
|||||||
background-color: $white;
|
background-color: $white;
|
||||||
border: $base-border;
|
border: $base-border;
|
||||||
border-radius: $base-border-radius;
|
border-radius: $base-border-radius;
|
||||||
|
font-family: $base-font-family;
|
||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
transition: border-color $base-duration $base-timing;
|
transition: border-color $base-duration $base-timing;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
float: left;
|
float: left;
|
||||||
margin-bottom: $base-spacing;
|
margin-bottom: $base-spacing;
|
||||||
margin-top: 0.25em;
|
margin-top: 0.25em;
|
||||||
text-align: right;
|
text-align: left;
|
||||||
width: calc(15% - 1rem);
|
width: calc(16% - 1rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
.preserve-whitespace {
|
.preserve-whitespace {
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
float: left;
|
float: left;
|
||||||
margin-bottom: $base-spacing;
|
margin-bottom: $base-spacing;
|
||||||
margin-left: 2rem;
|
margin-left: 2rem;
|
||||||
width: calc(85% - 1rem);
|
width: calc(84% - 1rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
.attribute--nested {
|
.attribute--nested {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ input[type="submit"],
|
|||||||
color: $white;
|
color: $white;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
font-size: $font-size-default;
|
font-size: $font-size-small;
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
font-weight: $font-weight-medium;
|
font-weight: $font-weight-medium;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
|
|||||||
@@ -1,13 +1,18 @@
|
|||||||
.main-content {
|
.main-content {
|
||||||
font-size: $font-size-default;
|
font-size: $font-size-default;
|
||||||
left: 23rem;
|
left: 21rem;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 0;
|
right: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-content__body {
|
.main-content__body {
|
||||||
|
font-size: $font-size-small;
|
||||||
padding: $space-two;
|
padding: $space-two;
|
||||||
|
|
||||||
|
table {
|
||||||
|
font-size: $font-size-small;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-content__header {
|
.main-content__header {
|
||||||
@@ -20,7 +25,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.main-content__page-title {
|
.main-content__page-title {
|
||||||
font-size: $font-size-large;
|
font-size: $font-size-medium;
|
||||||
font-weight: $font-weight-medium;
|
font-weight: $font-weight-medium;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
.logo-brand {
|
.logo-brand {
|
||||||
margin-bottom: $space-normal;
|
margin-bottom: $space-normal;
|
||||||
padding: $space-normal $space-smaller $space-small;
|
padding: $space-normal $space-smaller $space-small;
|
||||||
text-align: center;
|
text-align: left;
|
||||||
|
|
||||||
|
img {
|
||||||
|
margin-bottom: $space-smaller;
|
||||||
|
max-height: 3rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.navigation {
|
.navigation {
|
||||||
@@ -19,12 +24,13 @@
|
|||||||
padding: $space-normal;
|
padding: $space-normal;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
width: 23rem;
|
width: 21rem;
|
||||||
z-index: 1023;
|
z-index: 1023;
|
||||||
|
|
||||||
li {
|
li {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
font-size: $font-size-small;
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: $color-gray;
|
color: $color-gray;
|
||||||
@@ -35,6 +41,10 @@
|
|||||||
min-width: $space-medium;
|
min-width: $space-medium;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
margin: $space-slab;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.navigation__link {
|
.navigation__link {
|
||||||
@@ -43,7 +53,7 @@
|
|||||||
display: block;
|
display: block;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
margin-bottom: $space-smaller;
|
margin-bottom: $space-smaller;
|
||||||
padding: $space-one;
|
padding: $space-small;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: $blue;
|
color: $blue;
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
// custom styles for the dashboard
|
||||||
|
|
||||||
|
.feature-cell {
|
||||||
|
background: $color-extra-light-blue;
|
||||||
|
border-radius: 10px;
|
||||||
|
float: left;
|
||||||
|
margin-left: 8px;
|
||||||
|
margin-top: 6px;
|
||||||
|
padding: 4px 12px;
|
||||||
|
|
||||||
|
.icon-container {
|
||||||
|
margin-right: 4px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.value-container {
|
||||||
|
margin-left: 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.feature-container {
|
||||||
|
max-width: 100rem;
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@ $base-font-family: PlusJakarta, Inter, -apple-system, BlinkMacSystemFont, "Segoe
|
|||||||
sans-serif !default;
|
sans-serif !default;
|
||||||
$heading-font-family: $base-font-family !default;
|
$heading-font-family: $base-font-family !default;
|
||||||
|
|
||||||
$base-font-size: 16px !default;
|
$base-font-size: 14px !default;
|
||||||
|
|
||||||
$base-line-height: 1.5 !default;
|
$base-line-height: 1.5 !default;
|
||||||
$heading-line-height: 1.2 !default;
|
$heading-line-height: 1.2 !default;
|
||||||
|
|||||||
@@ -78,11 +78,6 @@ $conv-header-height: 4rem;
|
|||||||
$inbox-thumb-size: 4.8rem;
|
$inbox-thumb-size: 4.8rem;
|
||||||
|
|
||||||
|
|
||||||
// Spinner
|
|
||||||
$spinkit-spinner-color: $color-white !default;
|
|
||||||
$spinkit-spinner-margin: 0 0 0 1.6rem !default;
|
|
||||||
$spinkit-size: 1.6rem !default;
|
|
||||||
|
|
||||||
// Snackbar default
|
// Snackbar default
|
||||||
$woot-snackbar-bg: #323232;
|
$woot-snackbar-bg: #323232;
|
||||||
$woot-snackbar-button: #ffeb3b;
|
$woot-snackbar-button: #ffeb3b;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
class Api::V1::Accounts::ArticlesController < Api::V1::Accounts::BaseController
|
class Api::V1::Accounts::ArticlesController < Api::V1::Accounts::BaseController
|
||||||
before_action :portal
|
before_action :portal
|
||||||
before_action :check_authorization
|
before_action :check_authorization
|
||||||
before_action :fetch_article, except: [:index, :create, :attach_file, :reorder]
|
before_action :fetch_article, except: [:index, :create, :reorder]
|
||||||
before_action :set_current_page, only: [:index]
|
before_action :set_current_page, only: [:index]
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@@ -36,17 +36,6 @@ class Api::V1::Accounts::ArticlesController < Api::V1::Accounts::BaseController
|
|||||||
head :ok
|
head :ok
|
||||||
end
|
end
|
||||||
|
|
||||||
def attach_file
|
|
||||||
file_blob = ActiveStorage::Blob.create_and_upload!(
|
|
||||||
key: nil,
|
|
||||||
io: params[:background_image].tempfile,
|
|
||||||
filename: params[:background_image].original_filename,
|
|
||||||
content_type: params[:background_image].content_type
|
|
||||||
)
|
|
||||||
file_blob.save!
|
|
||||||
render json: { file_url: url_for(file_blob) }
|
|
||||||
end
|
|
||||||
|
|
||||||
def reorder
|
def reorder
|
||||||
Article.update_positions(params[:positions_hash])
|
Article.update_positions(params[:positions_hash])
|
||||||
head :ok
|
head :ok
|
||||||
|
|||||||
@@ -20,16 +20,6 @@ class Api::V1::Accounts::AutomationRulesController < Api::V1::Accounts::BaseCont
|
|||||||
@automation_rule
|
@automation_rule
|
||||||
end
|
end
|
||||||
|
|
||||||
def attach_file
|
|
||||||
file_blob = ActiveStorage::Blob.create_and_upload!(
|
|
||||||
key: nil,
|
|
||||||
io: params[:attachment].tempfile,
|
|
||||||
filename: params[:attachment].original_filename,
|
|
||||||
content_type: params[:attachment].content_type
|
|
||||||
)
|
|
||||||
render json: { blob_key: file_blob.key, blob_id: file_blob.id }
|
|
||||||
end
|
|
||||||
|
|
||||||
def update
|
def update
|
||||||
ActiveRecord::Base.transaction do
|
ActiveRecord::Base.transaction do
|
||||||
automation_rule_update
|
automation_rule_update
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
class Api::V1::Accounts::ContactInboxesController < Api::V1::Accounts::BaseController
|
||||||
|
before_action :ensure_inbox
|
||||||
|
|
||||||
|
def filter
|
||||||
|
contact_inbox = @inbox.contact_inboxes.where(inbox_id: permitted_params[:inbox_id], source_id: permitted_params[:source_id])
|
||||||
|
return head :not_found if contact_inbox.empty?
|
||||||
|
|
||||||
|
@contact = contact_inbox.first.contact
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def ensure_inbox
|
||||||
|
@inbox = Current.account.inboxes.find(permitted_params[:inbox_id])
|
||||||
|
authorize @inbox, :show?
|
||||||
|
end
|
||||||
|
|
||||||
|
def permitted_params
|
||||||
|
params.permit(:inbox_id, :source_id)
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -18,18 +18,19 @@ class Api::V1::Accounts::ContactsController < Api::V1::Accounts::BaseController
|
|||||||
|
|
||||||
def index
|
def index
|
||||||
@contacts_count = resolved_contacts.count
|
@contacts_count = resolved_contacts.count
|
||||||
@contacts = fetch_contacts_with_conversation_count(resolved_contacts)
|
@contacts = fetch_contacts(resolved_contacts)
|
||||||
end
|
end
|
||||||
|
|
||||||
def search
|
def search
|
||||||
render json: { error: 'Specify search string with parameter q' }, status: :unprocessable_entity if params[:q].blank? && return
|
render json: { error: 'Specify search string with parameter q' }, status: :unprocessable_entity if params[:q].blank? && return
|
||||||
|
|
||||||
contacts = resolved_contacts.where(
|
contacts = resolved_contacts.where(
|
||||||
'name ILIKE :search OR email ILIKE :search OR phone_number ILIKE :search OR contacts.identifier LIKE :search',
|
'name ILIKE :search OR email ILIKE :search OR phone_number ILIKE :search OR contacts.identifier LIKE :search
|
||||||
|
OR contacts.additional_attributes->>\'company_name\' ILIKE :search',
|
||||||
search: "%#{params[:q].strip}%"
|
search: "%#{params[:q].strip}%"
|
||||||
)
|
)
|
||||||
@contacts_count = contacts.count
|
@contacts_count = contacts.count
|
||||||
@contacts = fetch_contacts_with_conversation_count(contacts)
|
@contacts = fetch_contacts(contacts)
|
||||||
end
|
end
|
||||||
|
|
||||||
def import
|
def import
|
||||||
@@ -63,7 +64,7 @@ class Api::V1::Accounts::ContactsController < Api::V1::Accounts::BaseController
|
|||||||
result = ::Contacts::FilterService.new(params.permit!, current_user).perform
|
result = ::Contacts::FilterService.new(params.permit!, current_user).perform
|
||||||
contacts = result[:contacts]
|
contacts = result[:contacts]
|
||||||
@contacts_count = result[:count]
|
@contacts_count = result[:count]
|
||||||
@contacts = fetch_contacts_with_conversation_count(contacts)
|
@contacts = fetch_contacts(contacts)
|
||||||
end
|
end
|
||||||
|
|
||||||
def contactable_inboxes
|
def contactable_inboxes
|
||||||
@@ -125,17 +126,14 @@ class Api::V1::Accounts::ContactsController < Api::V1::Accounts::BaseController
|
|||||||
@current_page = params[:page] || 1
|
@current_page = params[:page] || 1
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_contacts_with_conversation_count(contacts)
|
def fetch_contacts(contacts)
|
||||||
conversation_count_sub_query = 'SELECT COUNT(*) FROM "conversations" WHERE "conversations"."contact_id" = "contacts"."id"'
|
contacts_with_avatar = filtrate(contacts)
|
||||||
contacts_with_conversation_count = filtrate(contacts)
|
.includes([{ avatar_attachment: [:blob] }])
|
||||||
.select("contacts.*, (#{conversation_count_sub_query}) as conversations_count")
|
.page(@current_page).per(RESULTS_PER_PAGE)
|
||||||
.group('contacts.id')
|
|
||||||
.includes([{ avatar_attachment: [:blob] }])
|
|
||||||
.page(@current_page).per(RESULTS_PER_PAGE)
|
|
||||||
|
|
||||||
return contacts_with_conversation_count.includes([{ contact_inboxes: [:inbox] }]) if @include_contact_inboxes
|
return contacts_with_avatar.includes([{ contact_inboxes: [:inbox] }]) if @include_contact_inboxes
|
||||||
|
|
||||||
contacts_with_conversation_count
|
contacts_with_avatar
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_contact_inbox
|
def build_contact_inbox
|
||||||
|
|||||||
@@ -39,16 +39,6 @@ class Api::V1::Accounts::MacrosController < Api::V1::Accounts::BaseController
|
|||||||
head :ok
|
head :ok
|
||||||
end
|
end
|
||||||
|
|
||||||
def attach_file
|
|
||||||
file_blob = ActiveStorage::Blob.create_and_upload!(
|
|
||||||
key: nil,
|
|
||||||
io: params[:attachment].tempfile,
|
|
||||||
filename: params[:attachment].original_filename,
|
|
||||||
content_type: params[:attachment].content_type
|
|
||||||
)
|
|
||||||
render json: { blob_key: file_blob.key, blob_id: file_blob.id }
|
|
||||||
end
|
|
||||||
|
|
||||||
def execute
|
def execute
|
||||||
::MacrosExecutionJob.perform_later(@macro, conversation_ids: params[:conversation_ids], user: Current.user)
|
::MacrosExecutionJob.perform_later(@macro, conversation_ids: params[:conversation_ids], user: Current.user)
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
class Api::V1::Accounts::PortalsController < Api::V1::Accounts::BaseController
|
class Api::V1::Accounts::PortalsController < Api::V1::Accounts::BaseController
|
||||||
include ::FileTypeHelper
|
include ::FileTypeHelper
|
||||||
|
|
||||||
before_action :fetch_portal, except: [:index, :create, :attach_file]
|
before_action :fetch_portal, except: [:index, :create]
|
||||||
before_action :check_authorization
|
before_action :check_authorization
|
||||||
before_action :set_current_page, only: [:index]
|
before_action :set_current_page, only: [:index]
|
||||||
|
|
||||||
@@ -53,16 +53,6 @@ class Api::V1::Accounts::PortalsController < Api::V1::Accounts::BaseController
|
|||||||
@portal.logo.attach(blob)
|
@portal.logo.attach(blob)
|
||||||
end
|
end
|
||||||
|
|
||||||
def attach_file
|
|
||||||
file_blob = ActiveStorage::Blob.create_and_upload!(
|
|
||||||
key: nil,
|
|
||||||
io: params[:logo].tempfile,
|
|
||||||
filename: params[:logo].original_filename,
|
|
||||||
content_type: params[:logo].content_type
|
|
||||||
)
|
|
||||||
render json: { blob_key: file_blob.key, blob_id: file_blob.id }
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def fetch_portal
|
def fetch_portal
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
class Api::V1::Accounts::UploadController < Api::V1::Accounts::BaseController
|
||||||
|
def create
|
||||||
|
file_blob = ActiveStorage::Blob.create_and_upload!(
|
||||||
|
key: nil,
|
||||||
|
io: params[:attachment].tempfile,
|
||||||
|
filename: params[:attachment].original_filename,
|
||||||
|
content_type: params[:attachment].content_type
|
||||||
|
)
|
||||||
|
file_blob.save!
|
||||||
|
|
||||||
|
render json: { file_url: url_for(file_blob), blob_key: file_blob.key, blob_id: file_blob.id }
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -1,7 +1,15 @@
|
|||||||
class Api::V1::Integrations::WebhooksController < ApplicationController
|
class Api::V1::Integrations::WebhooksController < ApplicationController
|
||||||
def create
|
def create
|
||||||
builder = Integrations::Slack::IncomingMessageBuilder.new(params)
|
builder = Integrations::Slack::IncomingMessageBuilder.new(permitted_params)
|
||||||
response = builder.perform
|
response = builder.perform
|
||||||
render json: response
|
render json: response
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
# TODO: This is a temporary solution to permit all params for slack unfurling job.
|
||||||
|
# We should only permit the params that we use. Handle all the params based on events and send it to the respective services.
|
||||||
|
def permitted_params
|
||||||
|
params.permit!
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ module EnsureCurrentAccountHelper
|
|||||||
|
|
||||||
def ensure_current_account
|
def ensure_current_account
|
||||||
account = Account.find(params[:account_id])
|
account = Account.find(params[:account_id])
|
||||||
ensure_account_is_active?(account)
|
render_unauthorized('Account is suspended') and return unless account.active?
|
||||||
|
|
||||||
if current_user
|
if current_user
|
||||||
account_accessible_for_user?(account)
|
account_accessible_for_user?(account)
|
||||||
@@ -27,8 +27,4 @@ module EnsureCurrentAccountHelper
|
|||||||
def account_accessible_for_bot?(account)
|
def account_accessible_for_bot?(account)
|
||||||
render_unauthorized('You are not authorized to access this account') unless @resource.agent_bot_inboxes.find_by(account_id: account.id)
|
render_unauthorized('You are not authorized to access this account') unless @resource.agent_bot_inboxes.find_by(account_id: account.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def ensure_account_is_active?(account)
|
|
||||||
render_unauthorized('Account is suspended') unless account.active?
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -9,11 +9,14 @@ module RequestExceptionHandler
|
|||||||
|
|
||||||
def handle_with_exception
|
def handle_with_exception
|
||||||
yield
|
yield
|
||||||
rescue ActiveRecord::RecordNotFound
|
rescue ActiveRecord::RecordNotFound => e
|
||||||
|
log_handled_error(e)
|
||||||
render_not_found_error('Resource could not be found')
|
render_not_found_error('Resource could not be found')
|
||||||
rescue Pundit::NotAuthorizedError
|
rescue Pundit::NotAuthorizedError => e
|
||||||
|
log_handled_error(e)
|
||||||
render_unauthorized('You are not authorized to do this action')
|
render_unauthorized('You are not authorized to do this action')
|
||||||
rescue ActionController::ParameterMissing => e
|
rescue ActionController::ParameterMissing => e
|
||||||
|
log_handled_error(e)
|
||||||
render_could_not_create_error(e.message)
|
render_could_not_create_error(e.message)
|
||||||
ensure
|
ensure
|
||||||
# to address the thread variable leak issues in Puma/Thin webserver
|
# to address the thread variable leak issues in Puma/Thin webserver
|
||||||
@@ -41,6 +44,7 @@ module RequestExceptionHandler
|
|||||||
end
|
end
|
||||||
|
|
||||||
def render_record_invalid(exception)
|
def render_record_invalid(exception)
|
||||||
|
log_handled_error(exception)
|
||||||
render json: {
|
render json: {
|
||||||
message: exception.record.errors.full_messages.join(', '),
|
message: exception.record.errors.full_messages.join(', '),
|
||||||
attributes: exception.record.errors.attribute_names
|
attributes: exception.record.errors.attribute_names
|
||||||
@@ -48,6 +52,11 @@ module RequestExceptionHandler
|
|||||||
end
|
end
|
||||||
|
|
||||||
def render_error_response(exception)
|
def render_error_response(exception)
|
||||||
|
log_handled_error(exception)
|
||||||
render json: exception.to_hash, status: exception.http_status
|
render json: exception.to_hash, status: exception.http_status
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def log_handled_error(exception)
|
||||||
|
logger.info("Handled error: #{exception.inspect}")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ class Public::Api::V1::Inboxes::ConversationsController < Public::Api::V1::Inbox
|
|||||||
end
|
end
|
||||||
|
|
||||||
def create_conversation
|
def create_conversation
|
||||||
::Conversation.create!(conversation_params)
|
ConversationBuilder.new(params: conversation_params, contact_inbox: @contact_inbox).perform
|
||||||
end
|
end
|
||||||
|
|
||||||
def trigger_typing_event(event)
|
def trigger_typing_event(event)
|
||||||
@@ -41,11 +41,6 @@ class Public::Api::V1::Inboxes::ConversationsController < Public::Api::V1::Inbox
|
|||||||
end
|
end
|
||||||
|
|
||||||
def conversation_params
|
def conversation_params
|
||||||
{
|
params.permit(custom_attributes: {})
|
||||||
account_id: @contact_inbox.contact.account_id,
|
|
||||||
inbox_id: @contact_inbox.inbox_id,
|
|
||||||
contact_id: @contact_inbox.contact_id,
|
|
||||||
contact_inbox_id: @contact_inbox.id
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ class Public::Api::V1::Portals::ArticlesController < Public::Api::V1::Portals::B
|
|||||||
end
|
end
|
||||||
|
|
||||||
def list_params
|
def list_params
|
||||||
params.permit(:query, :locale, :sort)
|
params.permit(:query, :locale, :sort, :status)
|
||||||
end
|
end
|
||||||
|
|
||||||
def permitted_params
|
def permitted_params
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
class Public::Api::V1::Portals::BaseController < PublicController
|
class Public::Api::V1::Portals::BaseController < PublicController
|
||||||
before_action :show_plain_layout
|
before_action :show_plain_layout
|
||||||
|
before_action :set_color_scheme
|
||||||
around_action :set_locale
|
around_action :set_locale
|
||||||
|
after_action :allow_iframe_requests
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
@@ -8,6 +10,14 @@ class Public::Api::V1::Portals::BaseController < PublicController
|
|||||||
@is_plain_layout_enabled = params[:show_plain_layout] == 'true'
|
@is_plain_layout_enabled = params[:show_plain_layout] == 'true'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def set_color_scheme
|
||||||
|
@theme = if %w[dark light].include?(params[:theme])
|
||||||
|
params[:theme]
|
||||||
|
else
|
||||||
|
''
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def set_locale(&)
|
def set_locale(&)
|
||||||
switch_locale_with_portal(&) if params[:locale].present?
|
switch_locale_with_portal(&) if params[:locale].present?
|
||||||
switch_locale_with_article(&) if params[:article_slug].present?
|
switch_locale_with_article(&) if params[:article_slug].present?
|
||||||
@@ -39,4 +49,8 @@ class Public::Api::V1::Portals::BaseController < PublicController
|
|||||||
|
|
||||||
I18n.with_locale(@locale, &)
|
I18n.with_locale(@locale, &)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def allow_iframe_requests
|
||||||
|
response.headers.delete('X-Frame-Options') if @is_plain_layout_enabled
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
class SlackUploadsController < ApplicationController
|
||||||
|
include Rails.application.routes.url_helpers
|
||||||
|
before_action :set_blob, only: [:show]
|
||||||
|
|
||||||
|
def show
|
||||||
|
if @blob
|
||||||
|
redirect_to blob_url
|
||||||
|
else
|
||||||
|
redirect_to avatar_url
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def set_blob
|
||||||
|
@blob = ActiveStorage::Blob.find_by(key: params[:blob_key])
|
||||||
|
end
|
||||||
|
|
||||||
|
def blob_url
|
||||||
|
url_for(@blob.representation(resize_to_fill: [250, nil]))
|
||||||
|
end
|
||||||
|
|
||||||
|
def avatar_url
|
||||||
|
base_url = ENV.fetch('FRONTEND_URL', nil)
|
||||||
|
"#{base_url}/integrations/slack/#{params[:sender_type]}.png"
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -6,6 +6,7 @@ class SuperAdmin::InstanceStatusesController < SuperAdmin::ApplicationController
|
|||||||
postgres_status
|
postgres_status
|
||||||
redis_metrics
|
redis_metrics
|
||||||
chatwoot_edition
|
chatwoot_edition
|
||||||
|
instance_meta
|
||||||
end
|
end
|
||||||
|
|
||||||
def chatwoot_edition
|
def chatwoot_edition
|
||||||
@@ -18,6 +19,10 @@ class SuperAdmin::InstanceStatusesController < SuperAdmin::ApplicationController
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def instance_meta
|
||||||
|
@metrics['Database Migrations'] = ActiveRecord::Base.connection.migration_context.needs_migration? ? 'pending' : 'completed'
|
||||||
|
end
|
||||||
|
|
||||||
def chatwoot_version
|
def chatwoot_version
|
||||||
@metrics['Chatwoot version'] = Chatwoot.config[:version]
|
@metrics['Chatwoot version'] = Chatwoot.config[:version]
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ class AccountDashboard < Administrate::BaseDashboard
|
|||||||
end
|
end
|
||||||
|
|
||||||
ATTRIBUTE_TYPES = {
|
ATTRIBUTE_TYPES = {
|
||||||
id: Field::Number,
|
id: Field::Number.with_options(searchable: true),
|
||||||
name: Field::String,
|
name: Field::String.with_options(searchable: true),
|
||||||
created_at: Field::DateTime,
|
created_at: Field::DateTime,
|
||||||
updated_at: Field::DateTime,
|
updated_at: Field::DateTime,
|
||||||
users: CountField,
|
users: CountField,
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ class AccountUserDashboard < Administrate::BaseDashboard
|
|||||||
# which determines how the attribute is displayed
|
# which determines how the attribute is displayed
|
||||||
# on pages throughout the dashboard.
|
# on pages throughout the dashboard.
|
||||||
ATTRIBUTE_TYPES = {
|
ATTRIBUTE_TYPES = {
|
||||||
account: Field::BelongsTo.with_options(searchable: true, searchable_field: 'name', order: 'id DESC'),
|
account: Field::BelongsToSearch.with_options(class_name: 'Account', searchable: true, searchable_field: [:name, :id], order: 'id DESC'),
|
||||||
user: Field::BelongsTo.with_options(searchable: true, searchable_field: 'name', order: 'id DESC'),
|
user: Field::BelongsToSearch.with_options(class_name: 'User', searchable: true, searchable_field: [:name, :email, :id], order: 'id DESC'),
|
||||||
inviter: Field::BelongsTo.with_options(class_name: 'User', searchable: true, searchable_field: 'name'),
|
inviter: Field::BelongsToSearch.with_options(class_name: 'User', searchable: true, searchable_field: [:name, :email, :id], order: 'id DESC'),
|
||||||
id: Field::Number,
|
id: Field::Number,
|
||||||
role: Field::Select.with_options(collection: AccountUser.roles.keys),
|
role: Field::Select.with_options(collection: AccountUser.roles.keys),
|
||||||
created_at: Field::DateTime,
|
created_at: Field::DateTime,
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ class UserDashboard < Administrate::BaseDashboard
|
|||||||
# on pages throughout the dashboard.
|
# on pages throughout the dashboard.
|
||||||
ATTRIBUTE_TYPES = {
|
ATTRIBUTE_TYPES = {
|
||||||
account_users: Field::HasMany,
|
account_users: Field::HasMany,
|
||||||
id: Field::Number,
|
id: Field::Number.with_options(searchable: true),
|
||||||
avatar_url: AvatarField,
|
avatar_url: AvatarField,
|
||||||
avatar: Field::ActiveStorage.with_options(
|
avatar: Field::ActiveStorage.with_options(
|
||||||
destroy_url: proc do |_namespace, _resource, attachment|
|
destroy_url: proc do |_namespace, _resource, attachment|
|
||||||
@@ -28,9 +28,9 @@ class UserDashboard < Administrate::BaseDashboard
|
|||||||
confirmed_at: Field::DateTime,
|
confirmed_at: Field::DateTime,
|
||||||
confirmation_sent_at: Field::DateTime,
|
confirmation_sent_at: Field::DateTime,
|
||||||
unconfirmed_email: Field::String,
|
unconfirmed_email: Field::String,
|
||||||
name: Field::String,
|
name: Field::String.with_options(searchable: true),
|
||||||
display_name: Field::String,
|
display_name: Field::String,
|
||||||
email: Field::String,
|
email: Field::String.with_options(searchable: true),
|
||||||
tokens: Field::String.with_options(searchable: false),
|
tokens: Field::String.with_options(searchable: false),
|
||||||
created_at: Field::DateTime,
|
created_at: Field::DateTime,
|
||||||
updated_at: Field::DateTime,
|
updated_at: Field::DateTime,
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
module PortalHelper
|
||||||
|
def generate_portal_bg_color(portal_color, theme)
|
||||||
|
base_color = theme == 'dark' ? 'black' : 'white'
|
||||||
|
"color-mix(in srgb, #{portal_color} 10%, #{base_color})"
|
||||||
|
end
|
||||||
|
|
||||||
|
def generate_portal_bg(portal_color, theme)
|
||||||
|
bg_image = theme == 'dark' ? 'grid_dark.svg' : 'grid.svg'
|
||||||
|
"background: url(/assets/images/hc/#{bg_image}) #{generate_portal_bg_color(portal_color, theme)}"
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -26,14 +26,14 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import AddAccountModal from '../dashboard/components/layout/sidebarComponents/AddAccountModal';
|
import AddAccountModal from '../dashboard/components/layout/sidebarComponents/AddAccountModal.vue';
|
||||||
import LoadingState from './components/widgets/LoadingState.vue';
|
import LoadingState from './components/widgets/LoadingState.vue';
|
||||||
import NetworkNotification from './components/NetworkNotification';
|
import NetworkNotification from './components/NetworkNotification.vue';
|
||||||
import UpdateBanner from './components/app/UpdateBanner.vue';
|
import UpdateBanner from './components/app/UpdateBanner.vue';
|
||||||
import UpgradeBanner from './components/app/UpgradeBanner.vue';
|
import UpgradeBanner from './components/app/UpgradeBanner.vue';
|
||||||
import PaymentPendingBanner from './components/app/PaymentPendingBanner.vue';
|
import PaymentPendingBanner from './components/app/PaymentPendingBanner.vue';
|
||||||
import vueActionCable from './helper/actionCable';
|
import vueActionCable from './helper/actionCable';
|
||||||
import WootSnackbarBox from './components/SnackbarContainer';
|
import WootSnackbarBox from './components/SnackbarContainer.vue';
|
||||||
import rtlMixin from 'shared/mixins/rtlMixin';
|
import rtlMixin from 'shared/mixins/rtlMixin';
|
||||||
import { setColorTheme } from './helper/themeHelper';
|
import { setColorTheme } from './helper/themeHelper';
|
||||||
import {
|
import {
|
||||||
@@ -111,10 +111,8 @@ export default {
|
|||||||
this.$store.dispatch('setActiveAccount', {
|
this.$store.dispatch('setActiveAccount', {
|
||||||
accountId: this.currentAccountId,
|
accountId: this.currentAccountId,
|
||||||
});
|
});
|
||||||
const {
|
const { locale, latest_chatwoot_version: latestChatwootVersion } =
|
||||||
locale,
|
this.getAccount(this.currentAccountId);
|
||||||
latest_chatwoot_version: latestChatwootVersion,
|
|
||||||
} = this.getAccount(this.currentAccountId);
|
|
||||||
const { pubsub_token: pubsubToken } = this.currentUser || {};
|
const { pubsub_token: pubsubToken } = this.currentUser || {};
|
||||||
this.setLocale(locale);
|
this.setLocale(locale);
|
||||||
this.updateRTLDirectionView(locale);
|
this.updateRTLDirectionView(locale);
|
||||||
|
|||||||
@@ -15,9 +15,8 @@ class ApiClient {
|
|||||||
|
|
||||||
// eslint-disable-next-line class-methods-use-this
|
// eslint-disable-next-line class-methods-use-this
|
||||||
get accountIdFromRoute() {
|
get accountIdFromRoute() {
|
||||||
const isInsideAccountScopedURLs = window.location.pathname.includes(
|
const isInsideAccountScopedURLs =
|
||||||
'/app/accounts'
|
window.location.pathname.includes('/app/accounts');
|
||||||
);
|
|
||||||
|
|
||||||
if (isInsideAccountScopedURLs) {
|
if (isInsideAccountScopedURLs) {
|
||||||
return window.location.pathname.split('/')[3];
|
return window.location.pathname.split('/')[3];
|
||||||
|
|||||||
@@ -9,14 +9,6 @@ class AutomationsAPI extends ApiClient {
|
|||||||
clone(automationId) {
|
clone(automationId) {
|
||||||
return axios.post(`${this.url}/${automationId}/clone`);
|
return axios.post(`${this.url}/${automationId}/clone`);
|
||||||
}
|
}
|
||||||
|
|
||||||
attachment(file) {
|
|
||||||
return axios.post(`${this.url}/attach_file`, file, {
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'multipart/form-data',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default new AutomationsAPI();
|
export default new AutomationsAPI();
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ class ContactAPI extends ApiClient {
|
|||||||
return axios.get(requestURL);
|
return axios.get(requestURL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line default-param-last
|
||||||
filter(page = 1, sortAttr = 'name', queryPayload) {
|
filter(page = 1, sortAttr = 'name', queryPayload) {
|
||||||
let requestURL = `${this.url}/filter?${buildContactParams(page, sortAttr)}`;
|
let requestURL = `${this.url}/filter?${buildContactParams(page, sortAttr)}`;
|
||||||
return axios.post(requestURL, queryPayload);
|
return axios.post(requestURL, queryPayload);
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import accountAPI from '../account';
|
import accountAPI from '../account';
|
||||||
import ApiClient from '../../ApiClient';
|
import ApiClient from '../../ApiClient';
|
||||||
import describeWithAPIMock from '../../specs/apiSpecHelper';
|
|
||||||
|
|
||||||
describe('#enterpriseAccountAPI', () => {
|
describe('#enterpriseAccountAPI', () => {
|
||||||
it('creates correct instance', () => {
|
it('creates correct instance', () => {
|
||||||
@@ -13,17 +12,33 @@ describe('#enterpriseAccountAPI', () => {
|
|||||||
expect(accountAPI).toHaveProperty('checkout');
|
expect(accountAPI).toHaveProperty('checkout');
|
||||||
});
|
});
|
||||||
|
|
||||||
describeWithAPIMock('API calls', context => {
|
describe('API calls', () => {
|
||||||
|
const originalAxios = window.axios;
|
||||||
|
const axiosMock = {
|
||||||
|
post: jest.fn(() => Promise.resolve()),
|
||||||
|
get: jest.fn(() => Promise.resolve()),
|
||||||
|
patch: jest.fn(() => Promise.resolve()),
|
||||||
|
delete: jest.fn(() => Promise.resolve()),
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
window.axios = axiosMock;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
window.axios = originalAxios;
|
||||||
|
});
|
||||||
|
|
||||||
it('#checkout', () => {
|
it('#checkout', () => {
|
||||||
accountAPI.checkout();
|
accountAPI.checkout();
|
||||||
expect(context.axiosMock.post).toHaveBeenCalledWith(
|
expect(axiosMock.post).toHaveBeenCalledWith(
|
||||||
'/enterprise/api/v1/checkout'
|
'/enterprise/api/v1/checkout'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('#subscription', () => {
|
it('#subscription', () => {
|
||||||
accountAPI.subscription();
|
accountAPI.subscription();
|
||||||
expect(context.axiosMock.post).toHaveBeenCalledWith(
|
expect(axiosMock.post).toHaveBeenCalledWith(
|
||||||
'/enterprise/api/v1/subscription'
|
'/enterprise/api/v1/subscription'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -47,20 +47,6 @@ class ArticlesAPI extends PortalsAPI {
|
|||||||
return axios.delete(`${this.url}/${portalSlug}/articles/${articleId}`);
|
return axios.delete(`${this.url}/${portalSlug}/articles/${articleId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
uploadImage({ portalSlug, file }) {
|
|
||||||
let formData = new FormData();
|
|
||||||
formData.append('background_image', file);
|
|
||||||
return axios.post(
|
|
||||||
`${this.url}/${portalSlug}/articles/attach_file`,
|
|
||||||
formData,
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'multipart/form-data',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
reorderArticles({ portalSlug, reorderedGroup, categorySlug }) {
|
reorderArticles({ portalSlug, reorderedGroup, categorySlug }) {
|
||||||
return axios.post(`${this.url}/${portalSlug}/articles/reorder`, {
|
return axios.post(`${this.url}/${portalSlug}/articles/reorder`, {
|
||||||
positions_hash: reorderedGroup,
|
positions_hash: reorderedGroup,
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ class ReportsAPI extends ApiClient {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line default-param-last
|
||||||
getSummary(since, until, type = 'account', id, groupBy, businessHours) {
|
getSummary(since, until, type = 'account', id, groupBy, businessHours) {
|
||||||
return axios.get(`${this.url}/summary`, {
|
return axios.get(`${this.url}/summary`, {
|
||||||
params: {
|
params: {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import accountAPI from '../account';
|
import accountAPI from '../account';
|
||||||
import ApiClient from '../ApiClient';
|
import ApiClient from '../ApiClient';
|
||||||
import describeWithAPIMock from './apiSpecHelper';
|
|
||||||
|
|
||||||
describe('#accountAPI', () => {
|
describe('#accountAPI', () => {
|
||||||
it('creates correct instance', () => {
|
it('creates correct instance', () => {
|
||||||
@@ -13,12 +12,28 @@ describe('#accountAPI', () => {
|
|||||||
expect(accountAPI).toHaveProperty('createAccount');
|
expect(accountAPI).toHaveProperty('createAccount');
|
||||||
});
|
});
|
||||||
|
|
||||||
describeWithAPIMock('API calls', context => {
|
describe('API calls', () => {
|
||||||
|
const originalAxios = window.axios;
|
||||||
|
const axiosMock = {
|
||||||
|
post: jest.fn(() => Promise.resolve()),
|
||||||
|
get: jest.fn(() => Promise.resolve()),
|
||||||
|
patch: jest.fn(() => Promise.resolve()),
|
||||||
|
delete: jest.fn(() => Promise.resolve()),
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
window.axios = axiosMock;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
window.axios = originalAxios;
|
||||||
|
});
|
||||||
|
|
||||||
it('#createAccount', () => {
|
it('#createAccount', () => {
|
||||||
accountAPI.createAccount({
|
accountAPI.createAccount({
|
||||||
name: 'Chatwoot',
|
name: 'Chatwoot',
|
||||||
});
|
});
|
||||||
expect(context.axiosMock.post).toHaveBeenCalledWith('/api/v1/accounts', {
|
expect(axiosMock.post).toHaveBeenCalledWith('/api/v1/accounts', {
|
||||||
name: 'Chatwoot',
|
name: 'Chatwoot',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import accountActionsAPI from '../accountActions';
|
import accountActionsAPI from '../accountActions';
|
||||||
import ApiClient from '../ApiClient';
|
import ApiClient from '../ApiClient';
|
||||||
import describeWithAPIMock from './apiSpecHelper';
|
|
||||||
|
|
||||||
describe('#ContactsAPI', () => {
|
describe('#ContactsAPI', () => {
|
||||||
it('creates correct instance', () => {
|
it('creates correct instance', () => {
|
||||||
@@ -8,10 +7,26 @@ describe('#ContactsAPI', () => {
|
|||||||
expect(accountActionsAPI).toHaveProperty('merge');
|
expect(accountActionsAPI).toHaveProperty('merge');
|
||||||
});
|
});
|
||||||
|
|
||||||
describeWithAPIMock('API calls', context => {
|
describe('API calls', () => {
|
||||||
|
const originalAxios = window.axios;
|
||||||
|
const axiosMock = {
|
||||||
|
post: jest.fn(() => Promise.resolve()),
|
||||||
|
get: jest.fn(() => Promise.resolve()),
|
||||||
|
patch: jest.fn(() => Promise.resolve()),
|
||||||
|
delete: jest.fn(() => Promise.resolve()),
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
window.axios = axiosMock;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
window.axios = originalAxios;
|
||||||
|
});
|
||||||
|
|
||||||
it('#merge', () => {
|
it('#merge', () => {
|
||||||
accountActionsAPI.merge(1, 2);
|
accountActionsAPI.merge(1, 2);
|
||||||
expect(context.axiosMock.post).toHaveBeenCalledWith(
|
expect(axiosMock.post).toHaveBeenCalledWith(
|
||||||
'/api/v1/actions/contact_merge',
|
'/api/v1/actions/contact_merge',
|
||||||
{
|
{
|
||||||
base_contact_id: 1,
|
base_contact_id: 1,
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
function apiSpecHelper() {
|
|
||||||
beforeEach(() => {
|
|
||||||
this.originalAxios = window.axios;
|
|
||||||
this.axiosMock = {
|
|
||||||
post: jest.fn(() => Promise.resolve()),
|
|
||||||
get: jest.fn(() => Promise.resolve()),
|
|
||||||
patch: jest.fn(() => Promise.resolve()),
|
|
||||||
delete: jest.fn(() => Promise.resolve()),
|
|
||||||
};
|
|
||||||
window.axios = this.axiosMock;
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
window.axios = this.originalAxios;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// https://stackoverflow.com/a/59344023/3901856
|
|
||||||
const sharedWrapper = describe('sharedWrapper', () => {});
|
|
||||||
export default function describeWithAPIMock(skillName, testFn) {
|
|
||||||
return describe(skillName, function configureContext() {
|
|
||||||
function Context() {}
|
|
||||||
Context.prototype = sharedWrapper.ctx;
|
|
||||||
this.ctx = new Context();
|
|
||||||
apiSpecHelper.call(this);
|
|
||||||
testFn.call(this, this);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
import articlesAPI from '../helpCenter/articles';
|
import articlesAPI from '../helpCenter/articles';
|
||||||
import ApiClient from 'dashboard/api/helpCenter/portals';
|
import ApiClient from 'dashboard/api/helpCenter/portals';
|
||||||
import describeWithAPIMock from './apiSpecHelper';
|
|
||||||
|
|
||||||
describe('#PortalAPI', () => {
|
describe('#PortalAPI', () => {
|
||||||
it('creates correct instance', () => {
|
it('creates correct instance', () => {
|
||||||
@@ -12,7 +11,23 @@ describe('#PortalAPI', () => {
|
|||||||
expect(articlesAPI).toHaveProperty('delete');
|
expect(articlesAPI).toHaveProperty('delete');
|
||||||
expect(articlesAPI).toHaveProperty('getArticles');
|
expect(articlesAPI).toHaveProperty('getArticles');
|
||||||
});
|
});
|
||||||
describeWithAPIMock('API calls', context => {
|
describe('API calls', () => {
|
||||||
|
const originalAxios = window.axios;
|
||||||
|
const axiosMock = {
|
||||||
|
post: jest.fn(() => Promise.resolve()),
|
||||||
|
get: jest.fn(() => Promise.resolve()),
|
||||||
|
patch: jest.fn(() => Promise.resolve()),
|
||||||
|
delete: jest.fn(() => Promise.resolve()),
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
window.axios = axiosMock;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
window.axios = originalAxios;
|
||||||
|
});
|
||||||
|
|
||||||
it('#getArticles', () => {
|
it('#getArticles', () => {
|
||||||
articlesAPI.getArticles({
|
articlesAPI.getArticles({
|
||||||
pageNumber: 1,
|
pageNumber: 1,
|
||||||
@@ -21,30 +36,62 @@ describe('#PortalAPI', () => {
|
|||||||
status: 'published',
|
status: 'published',
|
||||||
author_id: '1',
|
author_id: '1',
|
||||||
});
|
});
|
||||||
expect(context.axiosMock.get).toHaveBeenCalledWith(
|
expect(axiosMock.get).toHaveBeenCalledWith(
|
||||||
'/api/v1/portals/room-rental/articles?page=1&locale=en-US&status=published&author_id=1'
|
'/api/v1/portals/room-rental/articles?page=1&locale=en-US&status=published&author_id=1'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
describeWithAPIMock('API calls', context => {
|
describe('API calls', () => {
|
||||||
|
const originalAxios = window.axios;
|
||||||
|
const axiosMock = {
|
||||||
|
post: jest.fn(() => Promise.resolve()),
|
||||||
|
get: jest.fn(() => Promise.resolve()),
|
||||||
|
patch: jest.fn(() => Promise.resolve()),
|
||||||
|
delete: jest.fn(() => Promise.resolve()),
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
window.axios = axiosMock;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
window.axios = originalAxios;
|
||||||
|
});
|
||||||
|
|
||||||
it('#getArticle', () => {
|
it('#getArticle', () => {
|
||||||
articlesAPI.getArticle({
|
articlesAPI.getArticle({
|
||||||
id: 1,
|
id: 1,
|
||||||
portalSlug: 'room-rental',
|
portalSlug: 'room-rental',
|
||||||
});
|
});
|
||||||
expect(context.axiosMock.get).toHaveBeenCalledWith(
|
expect(axiosMock.get).toHaveBeenCalledWith(
|
||||||
'/api/v1/portals/room-rental/articles/1'
|
'/api/v1/portals/room-rental/articles/1'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
describeWithAPIMock('API calls', context => {
|
describe('API calls', () => {
|
||||||
|
const originalAxios = window.axios;
|
||||||
|
const axiosMock = {
|
||||||
|
post: jest.fn(() => Promise.resolve()),
|
||||||
|
get: jest.fn(() => Promise.resolve()),
|
||||||
|
patch: jest.fn(() => Promise.resolve()),
|
||||||
|
delete: jest.fn(() => Promise.resolve()),
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
window.axios = axiosMock;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
window.axios = originalAxios;
|
||||||
|
});
|
||||||
|
|
||||||
it('#updateArticle', () => {
|
it('#updateArticle', () => {
|
||||||
articlesAPI.updateArticle({
|
articlesAPI.updateArticle({
|
||||||
articleId: 1,
|
articleId: 1,
|
||||||
portalSlug: 'room-rental',
|
portalSlug: 'room-rental',
|
||||||
articleObj: { title: 'Update shipping address' },
|
articleObj: { title: 'Update shipping address' },
|
||||||
});
|
});
|
||||||
expect(context.axiosMock.patch).toHaveBeenCalledWith(
|
expect(axiosMock.patch).toHaveBeenCalledWith(
|
||||||
'/api/v1/portals/room-rental/articles/1',
|
'/api/v1/portals/room-rental/articles/1',
|
||||||
{
|
{
|
||||||
title: 'Update shipping address',
|
title: 'Update shipping address',
|
||||||
@@ -52,13 +99,29 @@ describe('#PortalAPI', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
describeWithAPIMock('API calls', context => {
|
describe('API calls', () => {
|
||||||
|
const originalAxios = window.axios;
|
||||||
|
const axiosMock = {
|
||||||
|
post: jest.fn(() => Promise.resolve()),
|
||||||
|
get: jest.fn(() => Promise.resolve()),
|
||||||
|
patch: jest.fn(() => Promise.resolve()),
|
||||||
|
delete: jest.fn(() => Promise.resolve()),
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
window.axios = axiosMock;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
window.axios = originalAxios;
|
||||||
|
});
|
||||||
|
|
||||||
it('#deleteArticle', () => {
|
it('#deleteArticle', () => {
|
||||||
articlesAPI.deleteArticle({
|
articlesAPI.deleteArticle({
|
||||||
articleId: 1,
|
articleId: 1,
|
||||||
portalSlug: 'room-rental',
|
portalSlug: 'room-rental',
|
||||||
});
|
});
|
||||||
expect(context.axiosMock.delete).toHaveBeenCalledWith(
|
expect(axiosMock.delete).toHaveBeenCalledWith(
|
||||||
'/api/v1/portals/room-rental/articles/1'
|
'/api/v1/portals/room-rental/articles/1'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,18 +1,30 @@
|
|||||||
import assignableAgentsAPI from '../assignableAgents';
|
import assignableAgentsAPI from '../assignableAgents';
|
||||||
import describeWithAPIMock from './apiSpecHelper';
|
|
||||||
|
|
||||||
describe('#AssignableAgentsAPI', () => {
|
describe('#AssignableAgentsAPI', () => {
|
||||||
describeWithAPIMock('API calls', context => {
|
describe('API calls', () => {
|
||||||
|
const originalAxios = window.axios;
|
||||||
|
const axiosMock = {
|
||||||
|
post: jest.fn(() => Promise.resolve()),
|
||||||
|
get: jest.fn(() => Promise.resolve()),
|
||||||
|
patch: jest.fn(() => Promise.resolve()),
|
||||||
|
delete: jest.fn(() => Promise.resolve()),
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
window.axios = axiosMock;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
window.axios = originalAxios;
|
||||||
|
});
|
||||||
|
|
||||||
it('#getAssignableAgents', () => {
|
it('#getAssignableAgents', () => {
|
||||||
assignableAgentsAPI.get([1]);
|
assignableAgentsAPI.get([1]);
|
||||||
expect(context.axiosMock.get).toHaveBeenCalledWith(
|
expect(axiosMock.get).toHaveBeenCalledWith('/api/v1/assignable_agents', {
|
||||||
'/api/v1/assignable_agents',
|
params: {
|
||||||
{
|
inbox_ids: [1],
|
||||||
params: {
|
},
|
||||||
inbox_ids: [1],
|
});
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import fbChannel from '../../channel/fbChannel';
|
import fbChannel from '../../channel/fbChannel';
|
||||||
import ApiClient from '../../ApiClient';
|
import ApiClient from '../../ApiClient';
|
||||||
import describeWithAPIMock from '../apiSpecHelper';
|
|
||||||
|
|
||||||
describe('#FBChannel', () => {
|
describe('#FBChannel', () => {
|
||||||
it('creates correct instance', () => {
|
it('creates correct instance', () => {
|
||||||
@@ -11,10 +10,26 @@ describe('#FBChannel', () => {
|
|||||||
expect(fbChannel).toHaveProperty('update');
|
expect(fbChannel).toHaveProperty('update');
|
||||||
expect(fbChannel).toHaveProperty('delete');
|
expect(fbChannel).toHaveProperty('delete');
|
||||||
});
|
});
|
||||||
describeWithAPIMock('API calls', context => {
|
describe('API calls', () => {
|
||||||
|
const originalAxios = window.axios;
|
||||||
|
const axiosMock = {
|
||||||
|
post: jest.fn(() => Promise.resolve()),
|
||||||
|
get: jest.fn(() => Promise.resolve()),
|
||||||
|
patch: jest.fn(() => Promise.resolve()),
|
||||||
|
delete: jest.fn(() => Promise.resolve()),
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
window.axios = axiosMock;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
window.axios = originalAxios;
|
||||||
|
});
|
||||||
|
|
||||||
it('#create', () => {
|
it('#create', () => {
|
||||||
fbChannel.create({ omniauthToken: 'ASFM131CSF@#@$', appId: 'chatwoot' });
|
fbChannel.create({ omniauthToken: 'ASFM131CSF@#@$', appId: 'chatwoot' });
|
||||||
expect(context.axiosMock.post).toHaveBeenCalledWith(
|
expect(axiosMock.post).toHaveBeenCalledWith(
|
||||||
'/api/v1/callbacks/register_facebook_page',
|
'/api/v1/callbacks/register_facebook_page',
|
||||||
{
|
{
|
||||||
omniauthToken: 'ASFM131CSF@#@$',
|
omniauthToken: 'ASFM131CSF@#@$',
|
||||||
@@ -27,7 +42,7 @@ describe('#FBChannel', () => {
|
|||||||
omniauthToken: 'ASFM131CSF@#@$',
|
omniauthToken: 'ASFM131CSF@#@$',
|
||||||
inboxId: 1,
|
inboxId: 1,
|
||||||
});
|
});
|
||||||
expect(context.axiosMock.post).toHaveBeenCalledWith(
|
expect(axiosMock.post).toHaveBeenCalledWith(
|
||||||
'/api/v1/callbacks/reauthorize_page',
|
'/api/v1/callbacks/reauthorize_page',
|
||||||
{
|
{
|
||||||
omniauth_token: 'ASFM131CSF@#@$',
|
omniauth_token: 'ASFM131CSF@#@$',
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import contactAPI, { buildContactParams } from '../contacts';
|
import contactAPI, { buildContactParams } from '../contacts';
|
||||||
import ApiClient from '../ApiClient';
|
import ApiClient from '../ApiClient';
|
||||||
import describeWithAPIMock from './apiSpecHelper';
|
|
||||||
|
|
||||||
describe('#ContactsAPI', () => {
|
describe('#ContactsAPI', () => {
|
||||||
it('creates correct instance', () => {
|
it('creates correct instance', () => {
|
||||||
@@ -15,56 +14,67 @@ describe('#ContactsAPI', () => {
|
|||||||
expect(contactAPI).toHaveProperty('destroyAvatar');
|
expect(contactAPI).toHaveProperty('destroyAvatar');
|
||||||
});
|
});
|
||||||
|
|
||||||
describeWithAPIMock('API calls', context => {
|
describe('API calls', () => {
|
||||||
|
const originalAxios = window.axios;
|
||||||
|
const axiosMock = {
|
||||||
|
post: jest.fn(() => Promise.resolve()),
|
||||||
|
get: jest.fn(() => Promise.resolve()),
|
||||||
|
patch: jest.fn(() => Promise.resolve()),
|
||||||
|
delete: jest.fn(() => Promise.resolve()),
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
window.axios = axiosMock;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
window.axios = originalAxios;
|
||||||
|
});
|
||||||
|
|
||||||
it('#get', () => {
|
it('#get', () => {
|
||||||
contactAPI.get(1, 'name', 'customer-support');
|
contactAPI.get(1, 'name', 'customer-support');
|
||||||
expect(context.axiosMock.get).toHaveBeenCalledWith(
|
expect(axiosMock.get).toHaveBeenCalledWith(
|
||||||
'/api/v1/contacts?include_contact_inboxes=false&page=1&sort=name&labels[]=customer-support'
|
'/api/v1/contacts?include_contact_inboxes=false&page=1&sort=name&labels[]=customer-support'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('#getConversations', () => {
|
it('#getConversations', () => {
|
||||||
contactAPI.getConversations(1);
|
contactAPI.getConversations(1);
|
||||||
expect(context.axiosMock.get).toHaveBeenCalledWith(
|
expect(axiosMock.get).toHaveBeenCalledWith(
|
||||||
'/api/v1/contacts/1/conversations'
|
'/api/v1/contacts/1/conversations'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('#getContactableInboxes', () => {
|
it('#getContactableInboxes', () => {
|
||||||
contactAPI.getContactableInboxes(1);
|
contactAPI.getContactableInboxes(1);
|
||||||
expect(context.axiosMock.get).toHaveBeenCalledWith(
|
expect(axiosMock.get).toHaveBeenCalledWith(
|
||||||
'/api/v1/contacts/1/contactable_inboxes'
|
'/api/v1/contacts/1/contactable_inboxes'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('#getContactLabels', () => {
|
it('#getContactLabels', () => {
|
||||||
contactAPI.getContactLabels(1);
|
contactAPI.getContactLabels(1);
|
||||||
expect(context.axiosMock.get).toHaveBeenCalledWith(
|
expect(axiosMock.get).toHaveBeenCalledWith('/api/v1/contacts/1/labels');
|
||||||
'/api/v1/contacts/1/labels'
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('#updateContactLabels', () => {
|
it('#updateContactLabels', () => {
|
||||||
const labels = ['support-query'];
|
const labels = ['support-query'];
|
||||||
contactAPI.updateContactLabels(1, labels);
|
contactAPI.updateContactLabels(1, labels);
|
||||||
expect(context.axiosMock.post).toHaveBeenCalledWith(
|
expect(axiosMock.post).toHaveBeenCalledWith('/api/v1/contacts/1/labels', {
|
||||||
'/api/v1/contacts/1/labels',
|
labels,
|
||||||
{
|
});
|
||||||
labels,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('#search', () => {
|
it('#search', () => {
|
||||||
contactAPI.search('leads', 1, 'date', 'customer-support');
|
contactAPI.search('leads', 1, 'date', 'customer-support');
|
||||||
expect(context.axiosMock.get).toHaveBeenCalledWith(
|
expect(axiosMock.get).toHaveBeenCalledWith(
|
||||||
'/api/v1/contacts/search?include_contact_inboxes=false&page=1&sort=date&q=leads&labels[]=customer-support'
|
'/api/v1/contacts/search?include_contact_inboxes=false&page=1&sort=date&q=leads&labels[]=customer-support'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('#destroyCustomAttributes', () => {
|
it('#destroyCustomAttributes', () => {
|
||||||
contactAPI.destroyCustomAttributes(1, ['cloudCustomer']);
|
contactAPI.destroyCustomAttributes(1, ['cloudCustomer']);
|
||||||
expect(context.axiosMock.post).toHaveBeenCalledWith(
|
expect(axiosMock.post).toHaveBeenCalledWith(
|
||||||
'/api/v1/contacts/1/destroy_custom_attributes',
|
'/api/v1/contacts/1/destroy_custom_attributes',
|
||||||
{
|
{
|
||||||
custom_attributes: ['cloudCustomer'],
|
custom_attributes: ['cloudCustomer'],
|
||||||
@@ -75,7 +85,7 @@ describe('#ContactsAPI', () => {
|
|||||||
it('#importContacts', () => {
|
it('#importContacts', () => {
|
||||||
const file = 'file';
|
const file = 'file';
|
||||||
contactAPI.importContacts(file);
|
contactAPI.importContacts(file);
|
||||||
expect(context.axiosMock.post).toHaveBeenCalledWith(
|
expect(axiosMock.post).toHaveBeenCalledWith(
|
||||||
'/api/v1/contacts/import',
|
'/api/v1/contacts/import',
|
||||||
expect.any(FormData),
|
expect.any(FormData),
|
||||||
{
|
{
|
||||||
@@ -96,7 +106,7 @@ describe('#ContactsAPI', () => {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
contactAPI.filter(1, 'name', queryPayload);
|
contactAPI.filter(1, 'name', queryPayload);
|
||||||
expect(context.axiosMock.post).toHaveBeenCalledWith(
|
expect(axiosMock.post).toHaveBeenCalledWith(
|
||||||
'/api/v1/contacts/filter?include_contact_inboxes=false&page=1&sort=name',
|
'/api/v1/contacts/filter?include_contact_inboxes=false&page=1&sort=name',
|
||||||
queryPayload
|
queryPayload
|
||||||
);
|
);
|
||||||
@@ -104,7 +114,7 @@ describe('#ContactsAPI', () => {
|
|||||||
|
|
||||||
it('#destroyAvatar', () => {
|
it('#destroyAvatar', () => {
|
||||||
contactAPI.destroyAvatar(1);
|
contactAPI.destroyAvatar(1);
|
||||||
expect(context.axiosMock.delete).toHaveBeenCalledWith(
|
expect(axiosMock.delete).toHaveBeenCalledWith(
|
||||||
'/api/v1/contacts/1/avatar'
|
'/api/v1/contacts/1/avatar'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import conversationsAPI from '../conversations';
|
import conversationsAPI from '../conversations';
|
||||||
import ApiClient from '../ApiClient';
|
import ApiClient from '../ApiClient';
|
||||||
import describeWithAPIMock from './apiSpecHelper';
|
|
||||||
|
|
||||||
describe('#ConversationApi', () => {
|
describe('#ConversationApi', () => {
|
||||||
it('creates correct instance', () => {
|
it('creates correct instance', () => {
|
||||||
@@ -14,10 +13,26 @@ describe('#ConversationApi', () => {
|
|||||||
expect(conversationsAPI).toHaveProperty('updateLabels');
|
expect(conversationsAPI).toHaveProperty('updateLabels');
|
||||||
});
|
});
|
||||||
|
|
||||||
describeWithAPIMock('API calls', context => {
|
describe('API calls', () => {
|
||||||
|
const originalAxios = window.axios;
|
||||||
|
const axiosMock = {
|
||||||
|
post: jest.fn(() => Promise.resolve()),
|
||||||
|
get: jest.fn(() => Promise.resolve()),
|
||||||
|
patch: jest.fn(() => Promise.resolve()),
|
||||||
|
delete: jest.fn(() => Promise.resolve()),
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
window.axios = axiosMock;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
window.axios = originalAxios;
|
||||||
|
});
|
||||||
|
|
||||||
it('#getLabels', () => {
|
it('#getLabels', () => {
|
||||||
conversationsAPI.getLabels(1);
|
conversationsAPI.getLabels(1);
|
||||||
expect(context.axiosMock.get).toHaveBeenCalledWith(
|
expect(axiosMock.get).toHaveBeenCalledWith(
|
||||||
'/api/v1/conversations/1/labels'
|
'/api/v1/conversations/1/labels'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -25,7 +40,7 @@ describe('#ConversationApi', () => {
|
|||||||
it('#updateLabels', () => {
|
it('#updateLabels', () => {
|
||||||
const labels = ['support-query'];
|
const labels = ['support-query'];
|
||||||
conversationsAPI.updateLabels(1, labels);
|
conversationsAPI.updateLabels(1, labels);
|
||||||
expect(context.axiosMock.post).toHaveBeenCalledWith(
|
expect(axiosMock.post).toHaveBeenCalledWith(
|
||||||
'/api/v1/conversations/1/labels',
|
'/api/v1/conversations/1/labels',
|
||||||
{
|
{
|
||||||
labels,
|
labels,
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import csatReportsAPI from '../csatReports';
|
import csatReportsAPI from '../csatReports';
|
||||||
import ApiClient from '../ApiClient';
|
import ApiClient from '../ApiClient';
|
||||||
import describeWithAPIMock from './apiSpecHelper';
|
|
||||||
|
|
||||||
describe('#Reports API', () => {
|
describe('#Reports API', () => {
|
||||||
it('creates correct instance', () => {
|
it('creates correct instance', () => {
|
||||||
@@ -9,10 +8,26 @@ describe('#Reports API', () => {
|
|||||||
expect(csatReportsAPI).toHaveProperty('get');
|
expect(csatReportsAPI).toHaveProperty('get');
|
||||||
expect(csatReportsAPI).toHaveProperty('getMetrics');
|
expect(csatReportsAPI).toHaveProperty('getMetrics');
|
||||||
});
|
});
|
||||||
describeWithAPIMock('API calls', context => {
|
describe('API calls', () => {
|
||||||
|
const originalAxios = window.axios;
|
||||||
|
const axiosMock = {
|
||||||
|
post: jest.fn(() => Promise.resolve()),
|
||||||
|
get: jest.fn(() => Promise.resolve()),
|
||||||
|
patch: jest.fn(() => Promise.resolve()),
|
||||||
|
delete: jest.fn(() => Promise.resolve()),
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
window.axios = axiosMock;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
window.axios = originalAxios;
|
||||||
|
});
|
||||||
|
|
||||||
it('#get', () => {
|
it('#get', () => {
|
||||||
csatReportsAPI.get({ page: 1, from: 1622485800, to: 1623695400 });
|
csatReportsAPI.get({ page: 1, from: 1622485800, to: 1623695400 });
|
||||||
expect(context.axiosMock.get).toHaveBeenCalledWith(
|
expect(axiosMock.get).toHaveBeenCalledWith(
|
||||||
'/api/v1/csat_survey_responses',
|
'/api/v1/csat_survey_responses',
|
||||||
{
|
{
|
||||||
params: {
|
params: {
|
||||||
@@ -26,7 +41,7 @@ describe('#Reports API', () => {
|
|||||||
});
|
});
|
||||||
it('#getMetrics', () => {
|
it('#getMetrics', () => {
|
||||||
csatReportsAPI.getMetrics({ from: 1622485800, to: 1623695400 });
|
csatReportsAPI.getMetrics({ from: 1622485800, to: 1623695400 });
|
||||||
expect(context.axiosMock.get).toHaveBeenCalledWith(
|
expect(axiosMock.get).toHaveBeenCalledWith(
|
||||||
'/api/v1/csat_survey_responses/metrics',
|
'/api/v1/csat_survey_responses/metrics',
|
||||||
{
|
{
|
||||||
params: { since: 1622485800, until: 1623695400 },
|
params: { since: 1622485800, until: 1623695400 },
|
||||||
@@ -39,7 +54,7 @@ describe('#Reports API', () => {
|
|||||||
to: 1623695400,
|
to: 1623695400,
|
||||||
user_ids: 1,
|
user_ids: 1,
|
||||||
});
|
});
|
||||||
expect(context.axiosMock.get).toHaveBeenCalledWith(
|
expect(axiosMock.get).toHaveBeenCalledWith(
|
||||||
'/api/v1/csat_survey_responses/download',
|
'/api/v1/csat_survey_responses/download',
|
||||||
{
|
{
|
||||||
params: {
|
params: {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import conversationAPI from '../../inbox/conversation';
|
import conversationAPI from '../../inbox/conversation';
|
||||||
import ApiClient from '../../ApiClient';
|
import ApiClient from '../../ApiClient';
|
||||||
import describeWithAPIMock from '../apiSpecHelper';
|
|
||||||
|
|
||||||
describe('#ConversationAPI', () => {
|
describe('#ConversationAPI', () => {
|
||||||
it('creates correct instance', () => {
|
it('creates correct instance', () => {
|
||||||
@@ -22,7 +21,23 @@ describe('#ConversationAPI', () => {
|
|||||||
expect(conversationAPI).toHaveProperty('filter');
|
expect(conversationAPI).toHaveProperty('filter');
|
||||||
});
|
});
|
||||||
|
|
||||||
describeWithAPIMock('API calls', context => {
|
describe('API calls', () => {
|
||||||
|
const originalAxios = window.axios;
|
||||||
|
const axiosMock = {
|
||||||
|
post: jest.fn(() => Promise.resolve()),
|
||||||
|
get: jest.fn(() => Promise.resolve()),
|
||||||
|
patch: jest.fn(() => Promise.resolve()),
|
||||||
|
delete: jest.fn(() => Promise.resolve()),
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
window.axios = axiosMock;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
window.axios = originalAxios;
|
||||||
|
});
|
||||||
|
|
||||||
it('#get conversations', () => {
|
it('#get conversations', () => {
|
||||||
conversationAPI.get({
|
conversationAPI.get({
|
||||||
inboxId: 1,
|
inboxId: 1,
|
||||||
@@ -32,19 +47,16 @@ describe('#ConversationAPI', () => {
|
|||||||
labels: [],
|
labels: [],
|
||||||
teamId: 1,
|
teamId: 1,
|
||||||
});
|
});
|
||||||
expect(context.axiosMock.get).toHaveBeenCalledWith(
|
expect(axiosMock.get).toHaveBeenCalledWith('/api/v1/conversations', {
|
||||||
'/api/v1/conversations',
|
params: {
|
||||||
{
|
inbox_id: 1,
|
||||||
params: {
|
team_id: 1,
|
||||||
inbox_id: 1,
|
status: 'open',
|
||||||
team_id: 1,
|
assignee_type: 'me',
|
||||||
status: 'open',
|
page: 1,
|
||||||
assignee_type: 'me',
|
labels: [],
|
||||||
page: 1,
|
},
|
||||||
labels: [],
|
});
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('#search', () => {
|
it('#search', () => {
|
||||||
@@ -53,7 +65,7 @@ describe('#ConversationAPI', () => {
|
|||||||
page: 1,
|
page: 1,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(context.axiosMock.get).toHaveBeenCalledWith(
|
expect(axiosMock.get).toHaveBeenCalledWith(
|
||||||
'/api/v1/conversations/search',
|
'/api/v1/conversations/search',
|
||||||
{
|
{
|
||||||
params: {
|
params: {
|
||||||
@@ -66,7 +78,7 @@ describe('#ConversationAPI', () => {
|
|||||||
|
|
||||||
it('#toggleStatus', () => {
|
it('#toggleStatus', () => {
|
||||||
conversationAPI.toggleStatus({ conversationId: 12, status: 'online' });
|
conversationAPI.toggleStatus({ conversationId: 12, status: 'online' });
|
||||||
expect(context.axiosMock.post).toHaveBeenCalledWith(
|
expect(axiosMock.post).toHaveBeenCalledWith(
|
||||||
`/api/v1/conversations/12/toggle_status`,
|
`/api/v1/conversations/12/toggle_status`,
|
||||||
{
|
{
|
||||||
status: 'online',
|
status: 'online',
|
||||||
@@ -77,7 +89,7 @@ describe('#ConversationAPI', () => {
|
|||||||
|
|
||||||
it('#assignAgent', () => {
|
it('#assignAgent', () => {
|
||||||
conversationAPI.assignAgent({ conversationId: 12, agentId: 34 });
|
conversationAPI.assignAgent({ conversationId: 12, agentId: 34 });
|
||||||
expect(context.axiosMock.post).toHaveBeenCalledWith(
|
expect(axiosMock.post).toHaveBeenCalledWith(
|
||||||
`/api/v1/conversations/12/assignments?assignee_id=34`,
|
`/api/v1/conversations/12/assignments?assignee_id=34`,
|
||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
@@ -85,7 +97,7 @@ describe('#ConversationAPI', () => {
|
|||||||
|
|
||||||
it('#assignTeam', () => {
|
it('#assignTeam', () => {
|
||||||
conversationAPI.assignTeam({ conversationId: 12, teamId: 1 });
|
conversationAPI.assignTeam({ conversationId: 12, teamId: 1 });
|
||||||
expect(context.axiosMock.post).toHaveBeenCalledWith(
|
expect(axiosMock.post).toHaveBeenCalledWith(
|
||||||
`/api/v1/conversations/12/assignments`,
|
`/api/v1/conversations/12/assignments`,
|
||||||
{
|
{
|
||||||
team_id: 1,
|
team_id: 1,
|
||||||
@@ -95,7 +107,7 @@ describe('#ConversationAPI', () => {
|
|||||||
|
|
||||||
it('#markMessageRead', () => {
|
it('#markMessageRead', () => {
|
||||||
conversationAPI.markMessageRead({ id: 12 });
|
conversationAPI.markMessageRead({ id: 12 });
|
||||||
expect(context.axiosMock.post).toHaveBeenCalledWith(
|
expect(axiosMock.post).toHaveBeenCalledWith(
|
||||||
`/api/v1/conversations/12/update_last_seen`
|
`/api/v1/conversations/12/update_last_seen`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -105,7 +117,7 @@ describe('#ConversationAPI', () => {
|
|||||||
conversationId: 12,
|
conversationId: 12,
|
||||||
status: 'typing_on',
|
status: 'typing_on',
|
||||||
});
|
});
|
||||||
expect(context.axiosMock.post).toHaveBeenCalledWith(
|
expect(axiosMock.post).toHaveBeenCalledWith(
|
||||||
`/api/v1/conversations/12/toggle_typing_status`,
|
`/api/v1/conversations/12/toggle_typing_status`,
|
||||||
{
|
{
|
||||||
typing_status: 'typing_on',
|
typing_status: 'typing_on',
|
||||||
@@ -115,14 +127,14 @@ describe('#ConversationAPI', () => {
|
|||||||
|
|
||||||
it('#mute', () => {
|
it('#mute', () => {
|
||||||
conversationAPI.mute(45);
|
conversationAPI.mute(45);
|
||||||
expect(context.axiosMock.post).toHaveBeenCalledWith(
|
expect(axiosMock.post).toHaveBeenCalledWith(
|
||||||
'/api/v1/conversations/45/mute'
|
'/api/v1/conversations/45/mute'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('#unmute', () => {
|
it('#unmute', () => {
|
||||||
conversationAPI.unmute(45);
|
conversationAPI.unmute(45);
|
||||||
expect(context.axiosMock.post).toHaveBeenCalledWith(
|
expect(axiosMock.post).toHaveBeenCalledWith(
|
||||||
'/api/v1/conversations/45/unmute'
|
'/api/v1/conversations/45/unmute'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -135,18 +147,15 @@ describe('#ConversationAPI', () => {
|
|||||||
labels: [],
|
labels: [],
|
||||||
teamId: 1,
|
teamId: 1,
|
||||||
});
|
});
|
||||||
expect(context.axiosMock.get).toHaveBeenCalledWith(
|
expect(axiosMock.get).toHaveBeenCalledWith('/api/v1/conversations/meta', {
|
||||||
'/api/v1/conversations/meta',
|
params: {
|
||||||
{
|
inbox_id: 1,
|
||||||
params: {
|
team_id: 1,
|
||||||
inbox_id: 1,
|
status: 'open',
|
||||||
team_id: 1,
|
assignee_type: 'me',
|
||||||
status: 'open',
|
labels: [],
|
||||||
assignee_type: 'me',
|
},
|
||||||
labels: [],
|
});
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('#sendEmailTranscript', () => {
|
it('#sendEmailTranscript', () => {
|
||||||
@@ -154,7 +163,7 @@ describe('#ConversationAPI', () => {
|
|||||||
conversationId: 45,
|
conversationId: 45,
|
||||||
email: 'john@acme.inc',
|
email: 'john@acme.inc',
|
||||||
});
|
});
|
||||||
expect(context.axiosMock.post).toHaveBeenCalledWith(
|
expect(axiosMock.post).toHaveBeenCalledWith(
|
||||||
'/api/v1/conversations/45/transcript',
|
'/api/v1/conversations/45/transcript',
|
||||||
{
|
{
|
||||||
email: 'john@acme.inc',
|
email: 'john@acme.inc',
|
||||||
@@ -167,7 +176,7 @@ describe('#ConversationAPI', () => {
|
|||||||
conversationId: 45,
|
conversationId: 45,
|
||||||
customAttributes: { order_d: '1001' },
|
customAttributes: { order_d: '1001' },
|
||||||
});
|
});
|
||||||
expect(context.axiosMock.post).toHaveBeenCalledWith(
|
expect(axiosMock.post).toHaveBeenCalledWith(
|
||||||
'/api/v1/conversations/45/custom_attributes',
|
'/api/v1/conversations/45/custom_attributes',
|
||||||
{
|
{
|
||||||
custom_attributes: { order_d: '1001' },
|
custom_attributes: { order_d: '1001' },
|
||||||
@@ -202,9 +211,7 @@ describe('#ConversationAPI', () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
conversationAPI.filter(payload);
|
conversationAPI.filter(payload);
|
||||||
expect(
|
expect(axiosMock.post).toHaveBeenCalledWith(
|
||||||
context.axiosMock.post
|
|
||||||
).toHaveBeenCalledWith(
|
|
||||||
'/api/v1/conversations/filter',
|
'/api/v1/conversations/filter',
|
||||||
payload.queryData,
|
payload.queryData,
|
||||||
{ params: { page: payload.page } }
|
{ params: { page: payload.page } }
|
||||||
@@ -213,7 +220,7 @@ describe('#ConversationAPI', () => {
|
|||||||
|
|
||||||
it('#getAllAttachments', () => {
|
it('#getAllAttachments', () => {
|
||||||
conversationAPI.getAllAttachments(1);
|
conversationAPI.getAllAttachments(1);
|
||||||
expect(context.axiosMock.get).toHaveBeenCalledWith(
|
expect(axiosMock.get).toHaveBeenCalledWith(
|
||||||
'/api/v1/conversations/1/attachments'
|
'/api/v1/conversations/1/attachments'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import messageAPI, { buildCreatePayload } from '../../inbox/message';
|
import messageAPI, { buildCreatePayload } from '../../inbox/message';
|
||||||
import ApiClient from '../../ApiClient';
|
import ApiClient from '../../ApiClient';
|
||||||
import describeWithAPIMock from '../apiSpecHelper';
|
|
||||||
|
|
||||||
describe('#ConversationAPI', () => {
|
describe('#ConversationAPI', () => {
|
||||||
it('creates correct instance', () => {
|
it('creates correct instance', () => {
|
||||||
@@ -13,13 +12,29 @@ describe('#ConversationAPI', () => {
|
|||||||
expect(messageAPI).toHaveProperty('getPreviousMessages');
|
expect(messageAPI).toHaveProperty('getPreviousMessages');
|
||||||
});
|
});
|
||||||
|
|
||||||
describeWithAPIMock('API calls', context => {
|
describe('API calls', () => {
|
||||||
|
const originalAxios = window.axios;
|
||||||
|
const axiosMock = {
|
||||||
|
post: jest.fn(() => Promise.resolve()),
|
||||||
|
get: jest.fn(() => Promise.resolve()),
|
||||||
|
patch: jest.fn(() => Promise.resolve()),
|
||||||
|
delete: jest.fn(() => Promise.resolve()),
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
window.axios = axiosMock;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
window.axios = originalAxios;
|
||||||
|
});
|
||||||
|
|
||||||
it('#getPreviousMessages', () => {
|
it('#getPreviousMessages', () => {
|
||||||
messageAPI.getPreviousMessages({
|
messageAPI.getPreviousMessages({
|
||||||
conversationId: 12,
|
conversationId: 12,
|
||||||
before: 4573,
|
before: 4573,
|
||||||
});
|
});
|
||||||
expect(context.axiosMock.get).toHaveBeenCalledWith(
|
expect(axiosMock.get).toHaveBeenCalledWith(
|
||||||
`/api/v1/conversations/12/messages`,
|
`/api/v1/conversations/12/messages`,
|
||||||
{
|
{
|
||||||
params: {
|
params: {
|
||||||
@@ -35,7 +50,6 @@ describe('#ConversationAPI', () => {
|
|||||||
message: 'test content',
|
message: 'test content',
|
||||||
echoId: 12,
|
echoId: 12,
|
||||||
isPrivate: true,
|
isPrivate: true,
|
||||||
|
|
||||||
files: [new Blob(['test-content'], { type: 'application/pdf' })],
|
files: [new Blob(['test-content'], { type: 'application/pdf' })],
|
||||||
});
|
});
|
||||||
expect(formPayload).toBeInstanceOf(FormData);
|
expect(formPayload).toBeInstanceOf(FormData);
|
||||||
@@ -58,8 +72,10 @@ describe('#ConversationAPI', () => {
|
|||||||
private: false,
|
private: false,
|
||||||
echo_id: 12,
|
echo_id: 12,
|
||||||
content_attributes: { in_reply_to: 12 },
|
content_attributes: { in_reply_to: 12 },
|
||||||
bcc_emails: '',
|
|
||||||
cc_emails: '',
|
cc_emails: '',
|
||||||
|
bcc_emails: '',
|
||||||
|
to_emails: '',
|
||||||
|
template_params: undefined,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import inboxesAPI from '../inboxes';
|
import inboxesAPI from '../inboxes';
|
||||||
import ApiClient from '../ApiClient';
|
import ApiClient from '../ApiClient';
|
||||||
import describeWithAPIMock from './apiSpecHelper';
|
|
||||||
|
|
||||||
describe('#InboxesAPI', () => {
|
describe('#InboxesAPI', () => {
|
||||||
it('creates correct instance', () => {
|
it('creates correct instance', () => {
|
||||||
@@ -14,19 +13,32 @@ describe('#InboxesAPI', () => {
|
|||||||
expect(inboxesAPI).toHaveProperty('getAgentBot');
|
expect(inboxesAPI).toHaveProperty('getAgentBot');
|
||||||
expect(inboxesAPI).toHaveProperty('setAgentBot');
|
expect(inboxesAPI).toHaveProperty('setAgentBot');
|
||||||
});
|
});
|
||||||
describeWithAPIMock('API calls', context => {
|
|
||||||
|
describe('API calls', () => {
|
||||||
|
const originalAxios = window.axios;
|
||||||
|
const axiosMock = {
|
||||||
|
post: jest.fn(() => Promise.resolve()),
|
||||||
|
get: jest.fn(() => Promise.resolve()),
|
||||||
|
patch: jest.fn(() => Promise.resolve()),
|
||||||
|
delete: jest.fn(() => Promise.resolve()),
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
window.axios = axiosMock;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
window.axios = originalAxios;
|
||||||
|
});
|
||||||
|
|
||||||
it('#getCampaigns', () => {
|
it('#getCampaigns', () => {
|
||||||
inboxesAPI.getCampaigns(2);
|
inboxesAPI.getCampaigns(2);
|
||||||
expect(context.axiosMock.get).toHaveBeenCalledWith(
|
expect(axiosMock.get).toHaveBeenCalledWith('/api/v1/inboxes/2/campaigns');
|
||||||
'/api/v1/inboxes/2/campaigns'
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('#deleteInboxAvatar', () => {
|
it('#deleteInboxAvatar', () => {
|
||||||
inboxesAPI.deleteInboxAvatar(2);
|
inboxesAPI.deleteInboxAvatar(2);
|
||||||
expect(context.axiosMock.delete).toHaveBeenCalledWith(
|
expect(axiosMock.delete).toHaveBeenCalledWith('/api/v1/inboxes/2/avatar');
|
||||||
'/api/v1/inboxes/2/avatar'
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import integrationAPI from '../integrations';
|
import integrationAPI from '../integrations';
|
||||||
import ApiClient from '../ApiClient';
|
import ApiClient from '../ApiClient';
|
||||||
import describeWithAPIMock from './apiSpecHelper';
|
|
||||||
|
|
||||||
describe('#integrationAPI', () => {
|
describe('#integrationAPI', () => {
|
||||||
it('creates correct instance', () => {
|
it('creates correct instance', () => {
|
||||||
@@ -16,11 +15,27 @@ describe('#integrationAPI', () => {
|
|||||||
expect(integrationAPI).toHaveProperty('listAllSlackChannels');
|
expect(integrationAPI).toHaveProperty('listAllSlackChannels');
|
||||||
expect(integrationAPI).toHaveProperty('deleteHook');
|
expect(integrationAPI).toHaveProperty('deleteHook');
|
||||||
});
|
});
|
||||||
describeWithAPIMock('API calls', context => {
|
describe('API calls', () => {
|
||||||
|
const originalAxios = window.axios;
|
||||||
|
const axiosMock = {
|
||||||
|
post: jest.fn(() => Promise.resolve()),
|
||||||
|
get: jest.fn(() => Promise.resolve()),
|
||||||
|
patch: jest.fn(() => Promise.resolve()),
|
||||||
|
delete: jest.fn(() => Promise.resolve()),
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
window.axios = axiosMock;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
window.axios = originalAxios;
|
||||||
|
});
|
||||||
|
|
||||||
it('#connectSlack', () => {
|
it('#connectSlack', () => {
|
||||||
const code = 'SDNFJNSDFNDSJN';
|
const code = 'SDNFJNSDFNDSJN';
|
||||||
integrationAPI.connectSlack(code);
|
integrationAPI.connectSlack(code);
|
||||||
expect(context.axiosMock.post).toHaveBeenCalledWith(
|
expect(axiosMock.post).toHaveBeenCalledWith(
|
||||||
'/api/v1/integrations/slack',
|
'/api/v1/integrations/slack',
|
||||||
{
|
{
|
||||||
code,
|
code,
|
||||||
@@ -31,7 +46,7 @@ describe('#integrationAPI', () => {
|
|||||||
it('#updateSlack', () => {
|
it('#updateSlack', () => {
|
||||||
const updateObj = { referenceId: 'SDFSDGSVE' };
|
const updateObj = { referenceId: 'SDFSDGSVE' };
|
||||||
integrationAPI.updateSlack(updateObj);
|
integrationAPI.updateSlack(updateObj);
|
||||||
expect(context.axiosMock.patch).toHaveBeenCalledWith(
|
expect(axiosMock.patch).toHaveBeenCalledWith(
|
||||||
'/api/v1/integrations/slack',
|
'/api/v1/integrations/slack',
|
||||||
{
|
{
|
||||||
reference_id: updateObj.referenceId,
|
reference_id: updateObj.referenceId,
|
||||||
@@ -41,16 +56,14 @@ describe('#integrationAPI', () => {
|
|||||||
|
|
||||||
it('#listAllSlackChannels', () => {
|
it('#listAllSlackChannels', () => {
|
||||||
integrationAPI.listAllSlackChannels();
|
integrationAPI.listAllSlackChannels();
|
||||||
expect(context.axiosMock.get).toHaveBeenCalledWith(
|
expect(axiosMock.get).toHaveBeenCalledWith(
|
||||||
'/api/v1/integrations/slack/list_all_channels'
|
'/api/v1/integrations/slack/list_all_channels'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('#delete', () => {
|
it('#delete', () => {
|
||||||
integrationAPI.delete(2);
|
integrationAPI.delete(2);
|
||||||
expect(context.axiosMock.delete).toHaveBeenCalledWith(
|
expect(axiosMock.delete).toHaveBeenCalledWith('/api/v1/integrations/2');
|
||||||
'/api/v1/integrations/2'
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('#createHook', () => {
|
it('#createHook', () => {
|
||||||
@@ -59,7 +72,7 @@ describe('#integrationAPI', () => {
|
|||||||
settings: { api_key: 'SDFSDGSVE' },
|
settings: { api_key: 'SDFSDGSVE' },
|
||||||
};
|
};
|
||||||
integrationAPI.createHook(hookData);
|
integrationAPI.createHook(hookData);
|
||||||
expect(context.axiosMock.post).toHaveBeenCalledWith(
|
expect(axiosMock.post).toHaveBeenCalledWith(
|
||||||
'/api/v1/integrations/hooks',
|
'/api/v1/integrations/hooks',
|
||||||
hookData
|
hookData
|
||||||
);
|
);
|
||||||
@@ -67,7 +80,7 @@ describe('#integrationAPI', () => {
|
|||||||
|
|
||||||
it('#deleteHook', () => {
|
it('#deleteHook', () => {
|
||||||
integrationAPI.deleteHook(2);
|
integrationAPI.deleteHook(2);
|
||||||
expect(context.axiosMock.delete).toHaveBeenCalledWith(
|
expect(axiosMock.delete).toHaveBeenCalledWith(
|
||||||
'/api/v1/integrations/hooks/2'
|
'/api/v1/integrations/hooks/2'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import DyteAPIClient from '../../integrations/dyte';
|
import DyteAPIClient from '../../integrations/dyte';
|
||||||
import ApiClient from '../../ApiClient';
|
import ApiClient from '../../ApiClient';
|
||||||
import describeWithAPIMock from '../apiSpecHelper';
|
|
||||||
|
|
||||||
describe('#accountAPI', () => {
|
describe('#accountAPI', () => {
|
||||||
it('creates correct instance', () => {
|
it('creates correct instance', () => {
|
||||||
@@ -9,10 +8,26 @@ describe('#accountAPI', () => {
|
|||||||
expect(DyteAPIClient).toHaveProperty('addParticipantToMeeting');
|
expect(DyteAPIClient).toHaveProperty('addParticipantToMeeting');
|
||||||
});
|
});
|
||||||
|
|
||||||
describeWithAPIMock('createAMeeting', context => {
|
describe('createAMeeting', () => {
|
||||||
|
const originalAxios = window.axios;
|
||||||
|
const axiosMock = {
|
||||||
|
post: jest.fn(() => Promise.resolve()),
|
||||||
|
get: jest.fn(() => Promise.resolve()),
|
||||||
|
patch: jest.fn(() => Promise.resolve()),
|
||||||
|
delete: jest.fn(() => Promise.resolve()),
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
window.axios = axiosMock;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
window.axios = originalAxios;
|
||||||
|
});
|
||||||
|
|
||||||
it('creates a valid request', () => {
|
it('creates a valid request', () => {
|
||||||
DyteAPIClient.createAMeeting(1);
|
DyteAPIClient.createAMeeting(1);
|
||||||
expect(context.axiosMock.post).toHaveBeenCalledWith(
|
expect(axiosMock.post).toHaveBeenCalledWith(
|
||||||
'/api/v1/integrations/dyte/create_a_meeting',
|
'/api/v1/integrations/dyte/create_a_meeting',
|
||||||
{
|
{
|
||||||
conversation_id: 1,
|
conversation_id: 1,
|
||||||
@@ -21,10 +36,26 @@ describe('#accountAPI', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describeWithAPIMock('addParticipantToMeeting', context => {
|
describe('addParticipantToMeeting', () => {
|
||||||
|
const originalAxios = window.axios;
|
||||||
|
const axiosMock = {
|
||||||
|
post: jest.fn(() => Promise.resolve()),
|
||||||
|
get: jest.fn(() => Promise.resolve()),
|
||||||
|
patch: jest.fn(() => Promise.resolve()),
|
||||||
|
delete: jest.fn(() => Promise.resolve()),
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
window.axios = axiosMock;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
window.axios = originalAxios;
|
||||||
|
});
|
||||||
|
|
||||||
it('creates a valid request', () => {
|
it('creates a valid request', () => {
|
||||||
DyteAPIClient.addParticipantToMeeting(1);
|
DyteAPIClient.addParticipantToMeeting(1);
|
||||||
expect(context.axiosMock.post).toHaveBeenCalledWith(
|
expect(axiosMock.post).toHaveBeenCalledWith(
|
||||||
'/api/v1/integrations/dyte/add_participant_to_meeting',
|
'/api/v1/integrations/dyte/add_participant_to_meeting',
|
||||||
{
|
{
|
||||||
message_id: 1,
|
message_id: 1,
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import notificationsAPI from '../notifications';
|
import notificationsAPI from '../notifications';
|
||||||
import ApiClient from '../ApiClient';
|
import ApiClient from '../ApiClient';
|
||||||
import describeWithAPIMock from './apiSpecHelper';
|
|
||||||
|
|
||||||
describe('#NotificationAPI', () => {
|
describe('#NotificationAPI', () => {
|
||||||
it('creates correct instance', () => {
|
it('creates correct instance', () => {
|
||||||
@@ -11,31 +10,47 @@ describe('#NotificationAPI', () => {
|
|||||||
expect(notificationsAPI).toHaveProperty('read');
|
expect(notificationsAPI).toHaveProperty('read');
|
||||||
expect(notificationsAPI).toHaveProperty('readAll');
|
expect(notificationsAPI).toHaveProperty('readAll');
|
||||||
});
|
});
|
||||||
describeWithAPIMock('API calls', context => {
|
describe('API calls', () => {
|
||||||
|
const originalAxios = window.axios;
|
||||||
|
const axiosMock = {
|
||||||
|
post: jest.fn(() => Promise.resolve()),
|
||||||
|
get: jest.fn(() => Promise.resolve()),
|
||||||
|
patch: jest.fn(() => Promise.resolve()),
|
||||||
|
delete: jest.fn(() => Promise.resolve()),
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
window.axios = axiosMock;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
window.axios = originalAxios;
|
||||||
|
});
|
||||||
|
|
||||||
it('#get', () => {
|
it('#get', () => {
|
||||||
notificationsAPI.get(1);
|
notificationsAPI.get(1);
|
||||||
expect(context.axiosMock.get).toHaveBeenCalledWith(
|
expect(axiosMock.get).toHaveBeenCalledWith(
|
||||||
'/api/v1/notifications?page=1'
|
'/api/v1/notifications?page=1'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('#getNotifications', () => {
|
it('#getNotifications', () => {
|
||||||
notificationsAPI.getNotifications(1);
|
notificationsAPI.getNotifications(1);
|
||||||
expect(context.axiosMock.get).toHaveBeenCalledWith(
|
expect(axiosMock.get).toHaveBeenCalledWith(
|
||||||
'/api/v1/notifications/1/notifications'
|
'/api/v1/notifications/1/notifications'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('#getUnreadCount', () => {
|
it('#getUnreadCount', () => {
|
||||||
notificationsAPI.getUnreadCount();
|
notificationsAPI.getUnreadCount();
|
||||||
expect(context.axiosMock.get).toHaveBeenCalledWith(
|
expect(axiosMock.get).toHaveBeenCalledWith(
|
||||||
'/api/v1/notifications/unread_count'
|
'/api/v1/notifications/unread_count'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('#read', () => {
|
it('#read', () => {
|
||||||
notificationsAPI.read(48670, 'Conversation');
|
notificationsAPI.read(48670, 'Conversation');
|
||||||
expect(context.axiosMock.post).toHaveBeenCalledWith(
|
expect(axiosMock.post).toHaveBeenCalledWith(
|
||||||
'/api/v1/notifications/read_all',
|
'/api/v1/notifications/read_all',
|
||||||
{
|
{
|
||||||
primary_actor_id: 'Conversation',
|
primary_actor_id: 'Conversation',
|
||||||
@@ -46,7 +61,7 @@ describe('#NotificationAPI', () => {
|
|||||||
|
|
||||||
it('#readAll', () => {
|
it('#readAll', () => {
|
||||||
notificationsAPI.readAll();
|
notificationsAPI.readAll();
|
||||||
expect(context.axiosMock.post).toHaveBeenCalledWith(
|
expect(axiosMock.post).toHaveBeenCalledWith(
|
||||||
'/api/v1/notifications/read_all'
|
'/api/v1/notifications/read_all'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import reportsAPI from '../reports';
|
import reportsAPI from '../reports';
|
||||||
import ApiClient from '../ApiClient';
|
import ApiClient from '../ApiClient';
|
||||||
import describeWithAPIMock from './apiSpecHelper';
|
|
||||||
|
|
||||||
describe('#Reports API', () => {
|
describe('#Reports API', () => {
|
||||||
it('creates correct instance', () => {
|
it('creates correct instance', () => {
|
||||||
@@ -18,14 +17,30 @@ describe('#Reports API', () => {
|
|||||||
expect(reportsAPI).toHaveProperty('getInboxReports');
|
expect(reportsAPI).toHaveProperty('getInboxReports');
|
||||||
expect(reportsAPI).toHaveProperty('getTeamReports');
|
expect(reportsAPI).toHaveProperty('getTeamReports');
|
||||||
});
|
});
|
||||||
describeWithAPIMock('API calls', context => {
|
describe('API calls', () => {
|
||||||
|
const originalAxios = window.axios;
|
||||||
|
const axiosMock = {
|
||||||
|
post: jest.fn(() => Promise.resolve()),
|
||||||
|
get: jest.fn(() => Promise.resolve()),
|
||||||
|
patch: jest.fn(() => Promise.resolve()),
|
||||||
|
delete: jest.fn(() => Promise.resolve()),
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
window.axios = axiosMock;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
window.axios = originalAxios;
|
||||||
|
});
|
||||||
|
|
||||||
it('#getAccountReports', () => {
|
it('#getAccountReports', () => {
|
||||||
reportsAPI.getReports({
|
reportsAPI.getReports({
|
||||||
metric: 'conversations_count',
|
metric: 'conversations_count',
|
||||||
from: 1621103400,
|
from: 1621103400,
|
||||||
to: 1621621800,
|
to: 1621621800,
|
||||||
});
|
});
|
||||||
expect(context.axiosMock.get).toHaveBeenCalledWith('/api/v2/reports', {
|
expect(axiosMock.get).toHaveBeenCalledWith('/api/v2/reports', {
|
||||||
params: {
|
params: {
|
||||||
metric: 'conversations_count',
|
metric: 'conversations_count',
|
||||||
since: 1621103400,
|
since: 1621103400,
|
||||||
@@ -38,20 +53,17 @@ describe('#Reports API', () => {
|
|||||||
|
|
||||||
it('#getAccountSummary', () => {
|
it('#getAccountSummary', () => {
|
||||||
reportsAPI.getSummary(1621103400, 1621621800);
|
reportsAPI.getSummary(1621103400, 1621621800);
|
||||||
expect(context.axiosMock.get).toHaveBeenCalledWith(
|
expect(axiosMock.get).toHaveBeenCalledWith('/api/v2/reports/summary', {
|
||||||
'/api/v2/reports/summary',
|
params: {
|
||||||
{
|
business_hours: undefined,
|
||||||
params: {
|
group_by: undefined,
|
||||||
business_hours: undefined,
|
id: undefined,
|
||||||
group_by: undefined,
|
since: 1621103400,
|
||||||
id: undefined,
|
timezone_offset: -0,
|
||||||
since: 1621103400,
|
type: 'account',
|
||||||
timezone_offset: -0,
|
until: 1621621800,
|
||||||
type: 'account',
|
},
|
||||||
until: 1621621800,
|
});
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('#getAgentReports', () => {
|
it('#getAgentReports', () => {
|
||||||
@@ -60,60 +72,48 @@ describe('#Reports API', () => {
|
|||||||
to: 1621621800,
|
to: 1621621800,
|
||||||
businessHours: true,
|
businessHours: true,
|
||||||
});
|
});
|
||||||
expect(context.axiosMock.get).toHaveBeenCalledWith(
|
expect(axiosMock.get).toHaveBeenCalledWith('/api/v2/reports/agents', {
|
||||||
'/api/v2/reports/agents',
|
params: {
|
||||||
{
|
since: 1621103400,
|
||||||
params: {
|
until: 1621621800,
|
||||||
since: 1621103400,
|
business_hours: true,
|
||||||
until: 1621621800,
|
},
|
||||||
business_hours: true,
|
});
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('#getLabelReports', () => {
|
it('#getLabelReports', () => {
|
||||||
reportsAPI.getLabelReports({ from: 1621103400, to: 1621621800 });
|
reportsAPI.getLabelReports({ from: 1621103400, to: 1621621800 });
|
||||||
expect(context.axiosMock.get).toHaveBeenCalledWith(
|
expect(axiosMock.get).toHaveBeenCalledWith('/api/v2/reports/labels', {
|
||||||
'/api/v2/reports/labels',
|
params: {
|
||||||
{
|
since: 1621103400,
|
||||||
params: {
|
until: 1621621800,
|
||||||
since: 1621103400,
|
},
|
||||||
until: 1621621800,
|
});
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('#getInboxReports', () => {
|
it('#getInboxReports', () => {
|
||||||
reportsAPI.getInboxReports({ from: 1621103400, to: 1621621800 });
|
reportsAPI.getInboxReports({ from: 1621103400, to: 1621621800 });
|
||||||
expect(context.axiosMock.get).toHaveBeenCalledWith(
|
expect(axiosMock.get).toHaveBeenCalledWith('/api/v2/reports/inboxes', {
|
||||||
'/api/v2/reports/inboxes',
|
params: {
|
||||||
{
|
since: 1621103400,
|
||||||
params: {
|
until: 1621621800,
|
||||||
since: 1621103400,
|
},
|
||||||
until: 1621621800,
|
});
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('#getTeamReports', () => {
|
it('#getTeamReports', () => {
|
||||||
reportsAPI.getTeamReports({ from: 1621103400, to: 1621621800 });
|
reportsAPI.getTeamReports({ from: 1621103400, to: 1621621800 });
|
||||||
expect(context.axiosMock.get).toHaveBeenCalledWith(
|
expect(axiosMock.get).toHaveBeenCalledWith('/api/v2/reports/teams', {
|
||||||
'/api/v2/reports/teams',
|
params: {
|
||||||
{
|
since: 1621103400,
|
||||||
params: {
|
until: 1621621800,
|
||||||
since: 1621103400,
|
},
|
||||||
until: 1621621800,
|
});
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('#getConversationMetric', () => {
|
it('#getConversationMetric', () => {
|
||||||
reportsAPI.getConversationMetric('account');
|
reportsAPI.getConversationMetric('account');
|
||||||
expect(context.axiosMock.get).toHaveBeenCalledWith(
|
expect(axiosMock.get).toHaveBeenCalledWith(
|
||||||
'/api/v2/reports/conversations',
|
'/api/v2/reports/conversations',
|
||||||
{
|
{
|
||||||
params: {
|
params: {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import teamsAPI from '../teams';
|
import teamsAPI from '../teams';
|
||||||
import ApiClient from '../ApiClient';
|
import ApiClient from '../ApiClient';
|
||||||
import describeWithAPIMock from './apiSpecHelper';
|
|
||||||
|
|
||||||
describe('#TeamsAPI', () => {
|
describe('#TeamsAPI', () => {
|
||||||
it('creates correct instance', () => {
|
it('creates correct instance', () => {
|
||||||
@@ -14,17 +13,33 @@ describe('#TeamsAPI', () => {
|
|||||||
expect(teamsAPI).toHaveProperty('addAgents');
|
expect(teamsAPI).toHaveProperty('addAgents');
|
||||||
expect(teamsAPI).toHaveProperty('updateAgents');
|
expect(teamsAPI).toHaveProperty('updateAgents');
|
||||||
});
|
});
|
||||||
describeWithAPIMock('API calls', context => {
|
describe('API calls', () => {
|
||||||
|
const originalAxios = window.axios;
|
||||||
|
const axiosMock = {
|
||||||
|
post: jest.fn(() => Promise.resolve()),
|
||||||
|
get: jest.fn(() => Promise.resolve()),
|
||||||
|
patch: jest.fn(() => Promise.resolve()),
|
||||||
|
delete: jest.fn(() => Promise.resolve()),
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
window.axios = axiosMock;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
window.axios = originalAxios;
|
||||||
|
});
|
||||||
|
|
||||||
it('#getAgents', () => {
|
it('#getAgents', () => {
|
||||||
teamsAPI.getAgents({ teamId: 1 });
|
teamsAPI.getAgents({ teamId: 1 });
|
||||||
expect(context.axiosMock.get).toHaveBeenCalledWith(
|
expect(axiosMock.get).toHaveBeenCalledWith(
|
||||||
'/api/v1/teams/1/team_members'
|
'/api/v1/teams/1/team_members'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('#addAgents', () => {
|
it('#addAgents', () => {
|
||||||
teamsAPI.addAgents({ teamId: 1, agentsList: { user_ids: [1, 10, 21] } });
|
teamsAPI.addAgents({ teamId: 1, agentsList: { user_ids: [1, 10, 21] } });
|
||||||
expect(context.axiosMock.post).toHaveBeenCalledWith(
|
expect(axiosMock.post).toHaveBeenCalledWith(
|
||||||
'/api/v1/teams/1/team_members',
|
'/api/v1/teams/1/team_members',
|
||||||
{
|
{
|
||||||
user_ids: { user_ids: [1, 10, 21] },
|
user_ids: { user_ids: [1, 10, 21] },
|
||||||
@@ -38,7 +53,7 @@ describe('#TeamsAPI', () => {
|
|||||||
teamId: 1,
|
teamId: 1,
|
||||||
agentsList,
|
agentsList,
|
||||||
});
|
});
|
||||||
expect(context.axiosMock.patch).toHaveBeenCalledWith(
|
expect(axiosMock.patch).toHaveBeenCalledWith(
|
||||||
'/api/v1/teams/1/team_members',
|
'/api/v1/teams/1/team_members',
|
||||||
{
|
{
|
||||||
user_ids: agentsList,
|
user_ids: agentsList,
|
||||||
|
|||||||
@@ -81,11 +81,6 @@ $conv-header-height: 2.5rem;
|
|||||||
$inbox-thumb-size: 3rem;
|
$inbox-thumb-size: 3rem;
|
||||||
|
|
||||||
|
|
||||||
// Spinner
|
|
||||||
$spinkit-spinner-color: $color-white !default;
|
|
||||||
$spinkit-spinner-margin: 0 0 0 1rem !default;
|
|
||||||
$spinkit-size: 1rem !default;
|
|
||||||
|
|
||||||
// Snackbar default
|
// Snackbar default
|
||||||
$woot-snackbar-bg: #323232;
|
$woot-snackbar-bg: #323232;
|
||||||
$woot-snackbar-button: #ffeb3b;
|
$woot-snackbar-button: #ffeb3b;
|
||||||
|
|||||||
@@ -149,15 +149,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.multiselect-wrap--small {
|
.multiselect-wrap--small {
|
||||||
.multiselect__tags,
|
|
||||||
.multiselect__input {
|
|
||||||
@apply items-center flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.multiselect__tags,
|
.multiselect__tags,
|
||||||
.multiselect__input,
|
.multiselect__input,
|
||||||
.multiselect {
|
.multiselect {
|
||||||
@apply bg-white dark:bg-slate-900 text-slate-800 dark:text-slate-100 rounded-[5px] text-sm h-10 min-h-[2.5rem];
|
@apply bg-white dark:bg-slate-900 text-slate-800 dark:text-slate-100 rounded-[5px] text-sm min-h-[2.5rem];
|
||||||
}
|
}
|
||||||
|
|
||||||
.multiselect__input {
|
.multiselect__input {
|
||||||
@@ -165,11 +161,15 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.multiselect__single {
|
.multiselect__single {
|
||||||
@apply items-center flex m-0 text-sm max-h-[2.375rem] text-slate-800 dark:text-slate-100 bg-white dark:bg-slate-900 py-1 px-0.5;
|
@apply items-center flex m-0 text-sm max-h-[2.375rem] text-slate-800 dark:text-slate-100 bg-white dark:bg-slate-900 py-3 px-0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.multiselect__placeholder {
|
.multiselect__placeholder {
|
||||||
@apply m-0 py-1 px-0.5;
|
@apply m-0 py-2 px-0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.multiselect__tag {
|
||||||
|
@apply py-[6px] my-[1px];
|
||||||
}
|
}
|
||||||
|
|
||||||
.multiselect__select {
|
.multiselect__select {
|
||||||
@@ -181,9 +181,6 @@
|
|||||||
@apply bg-transparent;
|
@apply bg-transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.multiselect__tags-wrap {
|
|
||||||
@apply flex-shrink-0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.multiselect-wrap--medium {
|
.multiselect-wrap--medium {
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
@import 'shared/assets/stylesheets/border-radius';
|
@import 'shared/assets/stylesheets/border-radius';
|
||||||
@import 'variables';
|
@import 'variables';
|
||||||
|
|
||||||
@import '~spinkit/scss/spinners/7-three-bounce';
|
|
||||||
@import 'vue-multiselect/dist/vue-multiselect.min.css';
|
@import 'vue-multiselect/dist/vue-multiselect.min.css';
|
||||||
@import '~shared/assets/stylesheets/ionicons';
|
@import '~shared/assets/stylesheets/ionicons';
|
||||||
|
|
||||||
|
|||||||
@@ -2,10 +2,6 @@
|
|||||||
transition: box-shadow 0.35s $swift-ease-out-function,
|
transition: box-shadow 0.35s $swift-ease-out-function,
|
||||||
height 2s $swift-ease-out-function;
|
height 2s $swift-ease-out-function;
|
||||||
|
|
||||||
&.is-focused {
|
|
||||||
box-shadow: var(--shadow);
|
|
||||||
}
|
|
||||||
|
|
||||||
.reply-box__top {
|
.reply-box__top {
|
||||||
.icon {
|
.icon {
|
||||||
color: $medium-gray;
|
color: $medium-gray;
|
||||||
@@ -47,8 +43,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&.is-private {
|
&.is-private {
|
||||||
@apply bg-yellow-100 dark:bg-yellow-800;
|
|
||||||
|
|
||||||
.reply-box__top {
|
.reply-box__top {
|
||||||
@apply bg-yellow-100 dark:bg-yellow-800;
|
@apply bg-yellow-100 dark:bg-yellow-800;
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import EmojiOrIcon from 'shared/components/EmojiOrIcon';
|
import EmojiOrIcon from 'shared/components/EmojiOrIcon.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
|||||||
@@ -10,7 +10,8 @@
|
|||||||
<div
|
<div
|
||||||
class="flex items-center justify-between py-0 px-4"
|
class="flex items-center justify-between py-0 px-4"
|
||||||
:class="{
|
:class="{
|
||||||
'pb-3 border-b border-slate-75 dark:border-slate-700': hasAppliedFiltersOrActiveFolders,
|
'pb-3 border-b border-slate-75 dark:border-slate-700':
|
||||||
|
hasAppliedFiltersOrActiveFolders,
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<div class="flex max-w-[85%] justify-center items-center">
|
<div class="flex max-w-[85%] justify-center items-center">
|
||||||
@@ -24,9 +25,7 @@
|
|||||||
v-if="!hasAppliedFiltersOrActiveFolders"
|
v-if="!hasAppliedFiltersOrActiveFolders"
|
||||||
class="p-1 my-0.5 mx-1 rounded-md capitalize bg-slate-50 dark:bg-slate-800 text-xxs text-slate-600 dark:text-slate-300"
|
class="p-1 my-0.5 mx-1 rounded-md capitalize bg-slate-50 dark:bg-slate-800 text-xxs text-slate-600 dark:text-slate-300"
|
||||||
>
|
>
|
||||||
{{
|
{{ $t(`CHAT_LIST.CHAT_STATUS_FILTER_ITEMS.${activeStatus}.TEXT`) }}
|
||||||
this.$t(`CHAT_LIST.CHAT_STATUS_FILTER_ITEMS.${activeStatus}.TEXT`)
|
|
||||||
}}
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-1">
|
<div class="flex items-center gap-1">
|
||||||
@@ -193,17 +192,17 @@
|
|||||||
<script>
|
<script>
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
|
|
||||||
import ConversationAdvancedFilter from './widgets/conversation/ConversationAdvancedFilter';
|
import ConversationAdvancedFilter from './widgets/conversation/ConversationAdvancedFilter.vue';
|
||||||
import ConversationBasicFilter from './widgets/conversation/ConversationBasicFilter';
|
import ConversationBasicFilter from './widgets/conversation/ConversationBasicFilter.vue';
|
||||||
import ChatTypeTabs from './widgets/ChatTypeTabs';
|
import ChatTypeTabs from './widgets/ChatTypeTabs.vue';
|
||||||
import ConversationCard from './widgets/conversation/ConversationCard';
|
import ConversationCard from './widgets/conversation/ConversationCard.vue';
|
||||||
import timeMixin from '../mixins/time';
|
import timeMixin from '../mixins/time';
|
||||||
import eventListenerMixins from 'shared/mixins/eventListenerMixins';
|
import eventListenerMixins from 'shared/mixins/eventListenerMixins';
|
||||||
import conversationMixin from '../mixins/conversations';
|
import conversationMixin from '../mixins/conversations';
|
||||||
import wootConstants from 'dashboard/constants/globals';
|
import wootConstants from 'dashboard/constants/globals';
|
||||||
import advancedFilterTypes from './widgets/conversation/advancedFilterItems';
|
import advancedFilterTypes from './widgets/conversation/advancedFilterItems';
|
||||||
import filterQueryGenerator from '../helper/filterQueryGenerator.js';
|
import filterQueryGenerator from '../helper/filterQueryGenerator.js';
|
||||||
import AddCustomViews from 'dashboard/routes/dashboard/customviews/AddCustomViews';
|
import AddCustomViews from 'dashboard/routes/dashboard/customviews/AddCustomViews.vue';
|
||||||
import DeleteCustomViews from 'dashboard/routes/dashboard/customviews/DeleteCustomViews.vue';
|
import DeleteCustomViews from 'dashboard/routes/dashboard/customviews/DeleteCustomViews.vue';
|
||||||
import ConversationBulkActions from './widgets/conversation/conversationBulkActions/Index.vue';
|
import ConversationBulkActions from './widgets/conversation/conversationBulkActions/Index.vue';
|
||||||
import alertMixin from 'shared/mixins/alertMixin';
|
import alertMixin from 'shared/mixins/alertMixin';
|
||||||
@@ -642,10 +641,8 @@ export default {
|
|||||||
},
|
},
|
||||||
handleKeyEvents(e) {
|
handleKeyEvents(e) {
|
||||||
if (hasPressedAltAndJKey(e)) {
|
if (hasPressedAltAndJKey(e)) {
|
||||||
const {
|
const { allConversations, activeConversationIndex } =
|
||||||
allConversations,
|
this.getKeyboardListenerParams();
|
||||||
activeConversationIndex,
|
|
||||||
} = this.getKeyboardListenerParams();
|
|
||||||
if (activeConversationIndex === -1) {
|
if (activeConversationIndex === -1) {
|
||||||
allConversations[0].click();
|
allConversations[0].click();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,7 +61,7 @@
|
|||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
v-if="isAttributeTypeLink"
|
v-if="isAttributeTypeLink"
|
||||||
:href="value"
|
:href="hrefURL"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
class="value inline-block rounded-sm mb-0 break-all py-0.5 px-1"
|
class="value inline-block rounded-sm mb-0 break-all py-0.5 px-1"
|
||||||
@@ -188,6 +188,9 @@ export default {
|
|||||||
urlValue() {
|
urlValue() {
|
||||||
return isValidURL(this.value) ? this.value : '---';
|
return isValidURL(this.value) ? this.value : '---';
|
||||||
},
|
},
|
||||||
|
hrefURL() {
|
||||||
|
return isValidURL(this.value) ? this.value : '';
|
||||||
|
},
|
||||||
notAttributeTypeCheckboxAndList() {
|
notAttributeTypeCheckboxAndList() {
|
||||||
return !this.isAttributeTypeCheckbox && !this.isAttributeTypeList;
|
return !this.isAttributeTypeCheckbox && !this.isAttributeTypeList;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -13,10 +13,10 @@
|
|||||||
/>
|
/>
|
||||||
<div class="flex flex-row justify-end gap-2 py-2 px-0 w-full">
|
<div class="flex flex-row justify-end gap-2 py-2 px-0 w-full">
|
||||||
<woot-button variant="clear" @click.prevent="onClose">
|
<woot-button variant="clear" @click.prevent="onClose">
|
||||||
{{ this.$t('CONVERSATION.CUSTOM_SNOOZE.CANCEL') }}
|
{{ $t('CONVERSATION.CUSTOM_SNOOZE.CANCEL') }}
|
||||||
</woot-button>
|
</woot-button>
|
||||||
<woot-button>
|
<woot-button>
|
||||||
{{ this.$t('CONVERSATION.CUSTOM_SNOOZE.APPLY') }}
|
{{ $t('CONVERSATION.CUSTOM_SNOOZE.APPLY') }}
|
||||||
</woot-button>
|
</woot-button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="text--container">
|
<div class="text--container">
|
||||||
<woot-button size="small" class=" button--text" @click="onCopy">
|
<woot-button size="small" class="button--text" @click="onCopy">
|
||||||
{{ $t('COMPONENTS.CODE.BUTTON_TEXT') }}
|
{{ $t('COMPONENTS.CODE.BUTTON_TEXT') }}
|
||||||
</woot-button>
|
</woot-button>
|
||||||
<woot-button
|
<woot-button
|
||||||
|
|||||||
@@ -66,9 +66,9 @@ export default {
|
|||||||
'right-aligned': 'right-aligned',
|
'right-aligned': 'right-aligned',
|
||||||
};
|
};
|
||||||
|
|
||||||
return `modal-mask skip-context-menu ${modalClassNameMap[
|
return `modal-mask skip-context-menu ${
|
||||||
this.modalType
|
modalClassNameMap[this.modalType] || ''
|
||||||
] || ''}`;
|
}`;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
<div
|
<div
|
||||||
class="ml-0 mr-0 flex pt-0 pr-4 pb-4 pl-0"
|
class="ml-0 mr-0 flex pt-0 pr-4 pb-4 pl-0"
|
||||||
:class="{
|
:class="{
|
||||||
'pt-4 border-b border-solid border-slate-50 dark:border-slate-700/30': showBorder,
|
'pt-4 border-b border-solid border-slate-50 dark:border-slate-700/30':
|
||||||
|
showBorder,
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<div class="w-[30%] min-w-0 max-w-[30%] pr-12">
|
<div class="w-[30%] min-w-0 max-w-[30%] pr-12">
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import WootSnackbar from './Snackbar';
|
import WootSnackbar from './Snackbar.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
|||||||
@@ -72,10 +72,8 @@ export default {
|
|||||||
const { custom_attributes: subscription } = account;
|
const { custom_attributes: subscription } = account;
|
||||||
if (!subscription) return EMPTY_SUBSCRIPTION_INFO;
|
if (!subscription) return EMPTY_SUBSCRIPTION_INFO;
|
||||||
|
|
||||||
const {
|
const { subscription_status: status, subscription_ends_on: endsOn } =
|
||||||
subscription_status: status,
|
subscription;
|
||||||
subscription_ends_on: endsOn,
|
|
||||||
} = subscription;
|
|
||||||
|
|
||||||
return { status, endsOn: new Date(endsOn) };
|
return { status, endsOn: new Date(endsOn) };
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
const semver = require('semver');
|
import semver from 'semver';
|
||||||
|
|
||||||
export const hasAnUpdateAvailable = (latestVersion, currentVersion) => {
|
export const hasAnUpdateAvailable = (latestVersion, currentVersion) => {
|
||||||
if (!semver.valid(latestVersion)) {
|
if (!semver.valid(latestVersion)) {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Spinner from 'shared/components/Spinner';
|
import Spinner from 'shared/components/Spinner.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
:is-loading="isLoading"
|
:is-loading="isLoading"
|
||||||
@click="onCmdResolveConversation"
|
@click="onCmdResolveConversation"
|
||||||
>
|
>
|
||||||
{{ this.$t('CONVERSATION.HEADER.RESOLVE_ACTION') }}
|
{{ $t('CONVERSATION.HEADER.RESOLVE_ACTION') }}
|
||||||
</woot-button>
|
</woot-button>
|
||||||
<woot-button
|
<woot-button
|
||||||
v-else-if="isResolved"
|
v-else-if="isResolved"
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
:is-loading="isLoading"
|
:is-loading="isLoading"
|
||||||
@click="onCmdOpenConversation"
|
@click="onCmdOpenConversation"
|
||||||
>
|
>
|
||||||
{{ this.$t('CONVERSATION.HEADER.REOPEN_ACTION') }}
|
{{ $t('CONVERSATION.HEADER.REOPEN_ACTION') }}
|
||||||
</woot-button>
|
</woot-button>
|
||||||
<woot-button
|
<woot-button
|
||||||
v-else-if="showOpenButton"
|
v-else-if="showOpenButton"
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
:is-loading="isLoading"
|
:is-loading="isLoading"
|
||||||
@click="onCmdOpenConversation"
|
@click="onCmdOpenConversation"
|
||||||
>
|
>
|
||||||
{{ this.$t('CONVERSATION.HEADER.OPEN_ACTION') }}
|
{{ $t('CONVERSATION.HEADER.OPEN_ACTION') }}
|
||||||
</woot-button>
|
</woot-button>
|
||||||
<woot-button
|
<woot-button
|
||||||
v-if="showAdditionalActions"
|
v-if="showAdditionalActions"
|
||||||
@@ -57,7 +57,7 @@
|
|||||||
icon="snooze"
|
icon="snooze"
|
||||||
@click="() => openSnoozeModal()"
|
@click="() => openSnoozeModal()"
|
||||||
>
|
>
|
||||||
{{ this.$t('CONVERSATION.RESOLVE_DROPDOWN.SNOOZE_UNTIL') }}
|
{{ $t('CONVERSATION.RESOLVE_DROPDOWN.SNOOZE_UNTIL') }}
|
||||||
</woot-button>
|
</woot-button>
|
||||||
</woot-dropdown-item>
|
</woot-dropdown-item>
|
||||||
<woot-dropdown-item v-if="!isPending">
|
<woot-dropdown-item v-if="!isPending">
|
||||||
@@ -68,7 +68,7 @@
|
|||||||
icon="book-clock"
|
icon="book-clock"
|
||||||
@click="() => toggleStatus(STATUS_TYPE.PENDING)"
|
@click="() => toggleStatus(STATUS_TYPE.PENDING)"
|
||||||
>
|
>
|
||||||
{{ this.$t('CONVERSATION.RESOLVE_DROPDOWN.MARK_PENDING') }}
|
{{ $t('CONVERSATION.RESOLVE_DROPDOWN.MARK_PENDING') }}
|
||||||
</woot-button>
|
</woot-button>
|
||||||
</woot-dropdown-item>
|
</woot-dropdown-item>
|
||||||
</woot-dropdown-menu>
|
</woot-dropdown-menu>
|
||||||
@@ -90,7 +90,7 @@ import { getUnixTime } from 'date-fns';
|
|||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import { mixin as clickaway } from 'vue-clickaway';
|
import { mixin as clickaway } from 'vue-clickaway';
|
||||||
import alertMixin from 'shared/mixins/alertMixin';
|
import alertMixin from 'shared/mixins/alertMixin';
|
||||||
import CustomSnoozeModal from 'dashboard/components/CustomSnoozeModal';
|
import CustomSnoozeModal from 'dashboard/components/CustomSnoozeModal.vue';
|
||||||
import eventListenerMixins from 'shared/mixins/eventListenerMixins';
|
import eventListenerMixins from 'shared/mixins/eventListenerMixins';
|
||||||
import {
|
import {
|
||||||
hasPressedAltAndEKey,
|
hasPressedAltAndEKey,
|
||||||
|
|||||||
@@ -49,11 +49,11 @@
|
|||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import { mixin as clickaway } from 'vue-clickaway';
|
import { mixin as clickaway } from 'vue-clickaway';
|
||||||
import alertMixin from 'shared/mixins/alertMixin';
|
import alertMixin from 'shared/mixins/alertMixin';
|
||||||
import WootDropdownItem from 'shared/components/ui/dropdown/DropdownItem';
|
import WootDropdownItem from 'shared/components/ui/dropdown/DropdownItem.vue';
|
||||||
import WootDropdownMenu from 'shared/components/ui/dropdown/DropdownMenu';
|
import WootDropdownMenu from 'shared/components/ui/dropdown/DropdownMenu.vue';
|
||||||
import WootDropdownHeader from 'shared/components/ui/dropdown/DropdownHeader';
|
import WootDropdownHeader from 'shared/components/ui/dropdown/DropdownHeader.vue';
|
||||||
import WootDropdownDivider from 'shared/components/ui/dropdown/DropdownDivider';
|
import WootDropdownDivider from 'shared/components/ui/dropdown/DropdownDivider.vue';
|
||||||
import AvailabilityStatusBadge from '../widgets/conversation/AvailabilityStatusBadge';
|
import AvailabilityStatusBadge from '../widgets/conversation/AvailabilityStatusBadge.vue';
|
||||||
import wootConstants from 'dashboard/constants/globals';
|
import wootConstants from 'dashboard/constants/globals';
|
||||||
|
|
||||||
const { AVAILABILITY_STATUS_KEYS } = wootConstants;
|
const { AVAILABILITY_STATUS_KEYS } = wootConstants;
|
||||||
|
|||||||
@@ -35,8 +35,8 @@ import adminMixin from '../../mixins/isAdmin';
|
|||||||
import { getSidebarItems } from './config/default-sidebar';
|
import { getSidebarItems } from './config/default-sidebar';
|
||||||
import alertMixin from 'shared/mixins/alertMixin';
|
import alertMixin from 'shared/mixins/alertMixin';
|
||||||
|
|
||||||
import PrimarySidebar from './sidebarComponents/Primary';
|
import PrimarySidebar from './sidebarComponents/Primary.vue';
|
||||||
import SecondarySidebar from './sidebarComponents/Secondary';
|
import SecondarySidebar from './sidebarComponents/Secondary.vue';
|
||||||
import {
|
import {
|
||||||
hasPressedAltAndCKey,
|
hasPressedAltAndCKey,
|
||||||
hasPressedAltAndRKey,
|
hasPressedAltAndRKey,
|
||||||
|
|||||||
@@ -34,8 +34,8 @@
|
|||||||
<woot-submit-button
|
<woot-submit-button
|
||||||
:disabled="
|
:disabled="
|
||||||
$v.accountName.$invalid ||
|
$v.accountName.$invalid ||
|
||||||
$v.accountName.$invalid ||
|
$v.accountName.$invalid ||
|
||||||
uiFlags.isCreating
|
uiFlags.isCreating
|
||||||
"
|
"
|
||||||
:button-text="$t('CREATE_ACCOUNT.FORM.SUBMIT')"
|
:button-text="$t('CREATE_ACCOUNT.FORM.SUBMIT')"
|
||||||
:loading="uiFlags.isCreating"
|
:loading="uiFlags.isCreating"
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import Thumbnail from '../../widgets/Thumbnail';
|
import Thumbnail from '../../widgets/Thumbnail.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
|||||||
@@ -3,7 +3,8 @@
|
|||||||
<button
|
<button
|
||||||
class="text-slate-600 dark:text-slate-100 w-10 h-10 my-2 flex items-center justify-center rounded-lg hover:bg-slate-25 dark:hover:bg-slate-700 dark:hover:text-slate-100 hover:text-slate-600 relative"
|
class="text-slate-600 dark:text-slate-100 w-10 h-10 my-2 flex items-center justify-center rounded-lg hover:bg-slate-25 dark:hover:bg-slate-700 dark:hover:text-slate-100 hover:text-slate-600 relative"
|
||||||
:class="{
|
:class="{
|
||||||
'bg-woot-50 dark:bg-slate-800 text-woot-500 hover:bg-woot-50': isNotificationPanelActive,
|
'bg-woot-50 dark:bg-slate-800 text-woot-500 hover:bg-woot-50':
|
||||||
|
isNotificationPanelActive,
|
||||||
}"
|
}"
|
||||||
@click="openNotificationPanel"
|
@click="openNotificationPanel"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -109,9 +109,9 @@
|
|||||||
import { mixin as clickaway } from 'vue-clickaway';
|
import { mixin as clickaway } from 'vue-clickaway';
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import Auth from '../../../api/auth';
|
import Auth from '../../../api/auth';
|
||||||
import WootDropdownItem from 'shared/components/ui/dropdown/DropdownItem';
|
import WootDropdownItem from 'shared/components/ui/dropdown/DropdownItem.vue';
|
||||||
import WootDropdownMenu from 'shared/components/ui/dropdown/DropdownMenu';
|
import WootDropdownMenu from 'shared/components/ui/dropdown/DropdownMenu.vue';
|
||||||
import AvailabilityStatus from 'dashboard/components/layout/AvailabilityStatus';
|
import AvailabilityStatus from 'dashboard/components/layout/AvailabilityStatus.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
|||||||
@@ -39,11 +39,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import Logo from './Logo';
|
import Logo from './Logo.vue';
|
||||||
import PrimaryNavItem from './PrimaryNavItem';
|
import PrimaryNavItem from './PrimaryNavItem.vue';
|
||||||
import OptionsMenu from './OptionsMenu';
|
import OptionsMenu from './OptionsMenu.vue';
|
||||||
import AgentDetails from './AgentDetails';
|
import AgentDetails from './AgentDetails.vue';
|
||||||
import NotificationBell from './NotificationBell';
|
import NotificationBell from './NotificationBell.vue';
|
||||||
import wootConstants from 'dashboard/constants/globals';
|
import wootConstants from 'dashboard/constants/globals';
|
||||||
import { frontendURL } from 'dashboard/helper/URLHelper';
|
import { frontendURL } from 'dashboard/helper/URLHelper';
|
||||||
import { ACCOUNT_EVENTS } from '../../../helper/AnalyticsHelper/events';
|
import { ACCOUNT_EVENTS } from '../../../helper/AnalyticsHelper/events';
|
||||||
|
|||||||
+4
-2
@@ -9,7 +9,8 @@
|
|||||||
class="font-medium h-7 my-1 hover:bg-slate-25 hover:text-bg-50 flex items-center px-2 rounded-md dark:hover:bg-slate-800"
|
class="font-medium h-7 my-1 hover:bg-slate-25 hover:text-bg-50 flex items-center px-2 rounded-md dark:hover:bg-slate-800"
|
||||||
:class="{
|
:class="{
|
||||||
'bg-woot-25 dark:bg-slate-800': isActive,
|
'bg-woot-25 dark:bg-slate-800': isActive,
|
||||||
'text-ellipsis overflow-hidden whitespace-nowrap max-w-full': shouldTruncate,
|
'text-ellipsis overflow-hidden whitespace-nowrap max-w-full':
|
||||||
|
shouldTruncate,
|
||||||
}"
|
}"
|
||||||
@click="navigate"
|
@click="navigate"
|
||||||
>
|
>
|
||||||
@@ -44,7 +45,8 @@
|
|||||||
class="text-sm text-slate-700 dark:text-slate-100"
|
class="text-sm text-slate-700 dark:text-slate-100"
|
||||||
:class="{
|
:class="{
|
||||||
'text-woot-500 dark:text-woot-500': isActive,
|
'text-woot-500 dark:text-woot-500': isActive,
|
||||||
'text-ellipsis overflow-hidden whitespace-nowrap max-w-full': shouldTruncate,
|
'text-ellipsis overflow-hidden whitespace-nowrap max-w-full':
|
||||||
|
shouldTruncate,
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
{{ label }}
|
{{ label }}
|
||||||
|
|||||||
@@ -35,7 +35,8 @@
|
|||||||
:class="{
|
:class="{
|
||||||
'text-slate-300 dark:text-slate-600': isCountZero && !isActiveView,
|
'text-slate-300 dark:text-slate-600': isCountZero && !isActiveView,
|
||||||
'text-slate-600 dark:text-slate-50': !isCountZero && !isActiveView,
|
'text-slate-600 dark:text-slate-50': !isCountZero && !isActiveView,
|
||||||
'bg-woot-75 dark:bg-woot-200 text-woot-600 dark:text-woot-600': isActiveView,
|
'bg-woot-75 dark:bg-woot-200 text-woot-600 dark:text-woot-600':
|
||||||
|
isActiveView,
|
||||||
'bg-slate-50 dark:bg-slate-700': !isActiveView,
|
'bg-slate-50 dark:bg-slate-700': !isActiveView,
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
@@ -97,7 +98,7 @@ import {
|
|||||||
getInboxWarningIconClass,
|
getInboxWarningIconClass,
|
||||||
} from 'dashboard/helper/inbox';
|
} from 'dashboard/helper/inbox';
|
||||||
|
|
||||||
import SecondaryChildNavItem from './SecondaryChildNavItem';
|
import SecondaryChildNavItem from './SecondaryChildNavItem.vue';
|
||||||
import {
|
import {
|
||||||
isOnMentionsView,
|
isOnMentionsView,
|
||||||
isOnUnattendedView,
|
isOnUnattendedView,
|
||||||
|
|||||||
@@ -53,16 +53,24 @@ export default {
|
|||||||
const createdTimeDiff = Date.now() - this.createdAtTimestamp * 1000;
|
const createdTimeDiff = Date.now() - this.createdAtTimestamp * 1000;
|
||||||
const isBeforeAMonth = createdTimeDiff > DAY_IN_MILLI_SECONDS * 30;
|
const isBeforeAMonth = createdTimeDiff > DAY_IN_MILLI_SECONDS * 30;
|
||||||
return !isBeforeAMonth
|
return !isBeforeAMonth
|
||||||
? `Created ${this.createdAtTimeAgo}`
|
? `${this.$t('CHAT_LIST.CHAT_TIME_STAMP.CREATED.LATEST')} ${
|
||||||
: `Created at: ${this.dateFormat(this.createdAtTimestamp)}`;
|
this.createdAtTimeAgo
|
||||||
|
}`
|
||||||
|
: `${this.$t(
|
||||||
|
'CHAT_LIST.CHAT_TIME_STAMP.CREATED.OLDEST'
|
||||||
|
)} ${this.dateFormat(this.createdAtTimestamp)}`;
|
||||||
},
|
},
|
||||||
lastActivity() {
|
lastActivity() {
|
||||||
const lastActivityTimeDiff =
|
const lastActivityTimeDiff =
|
||||||
Date.now() - this.lastActivityTimestamp * 1000;
|
Date.now() - this.lastActivityTimestamp * 1000;
|
||||||
const isNotActive = lastActivityTimeDiff > DAY_IN_MILLI_SECONDS * 30;
|
const isNotActive = lastActivityTimeDiff > DAY_IN_MILLI_SECONDS * 30;
|
||||||
return !isNotActive
|
return !isNotActive
|
||||||
? `Last activity ${this.lastActivityAtTimeAgo}`
|
? `${this.$t('CHAT_LIST.CHAT_TIME_STAMP.LAST_ACTIVITY.ACTIVE')} ${
|
||||||
: `Last activity: ${this.dateFormat(this.lastActivityTimestamp)}`;
|
this.lastActivityAtTimeAgo
|
||||||
|
}`
|
||||||
|
: `${this.$t(
|
||||||
|
'CHAT_LIST.CHAT_TIME_STAMP.LAST_ACTIVITY.NOT_ACTIVE'
|
||||||
|
)} ${this.dateFormat(this.lastActivityTimestamp)}`;
|
||||||
},
|
},
|
||||||
tooltipText() {
|
tooltipText() {
|
||||||
return `${this.createdAt}
|
return `${this.createdAt}
|
||||||
|
|||||||
@@ -28,8 +28,8 @@
|
|||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import Spinner from 'shared/components/Spinner';
|
import Spinner from 'shared/components/Spinner.vue';
|
||||||
import EmojiOrIcon from 'shared/components/EmojiOrIcon';
|
import EmojiOrIcon from 'shared/components/EmojiOrIcon.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'WootButton',
|
name: 'WootButton',
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="animation-container margin-top-1">
|
<div class="animation-container margin-top-1">
|
||||||
<div class="ai-typing--wrap ">
|
<div class="ai-typing--wrap">
|
||||||
<fluent-icon icon="wand" size="14" class="ai-typing--icon" />
|
<fluent-icon icon="wand" size="14" class="ai-typing--icon" />
|
||||||
<label>
|
<label>
|
||||||
{{ $t('INTEGRATION_SETTINGS.OPEN_AI.ASSISTANCE_MODAL.AI_WRITING') }}
|
{{ $t('INTEGRATION_SETTINGS.OPEN_AI.ASSISTANCE_MODAL.AI_WRITING') }}
|
||||||
|
|||||||
@@ -108,7 +108,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import AutomationActionTeamMessageInput from './AutomationActionTeamMessageInput.vue';
|
import AutomationActionTeamMessageInput from './AutomationActionTeamMessageInput.vue';
|
||||||
import AutomationActionFileInput from './AutomationFileInput.vue';
|
import AutomationActionFileInput from './AutomationFileInput.vue';
|
||||||
import WootMessageEditor from 'dashboard/components/widgets/WootWriter/Editor';
|
import WootMessageEditor from 'dashboard/components/widgets/WootWriter/Editor.vue';
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
AutomationActionTeamMessageInput,
|
AutomationActionTeamMessageInput,
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Spinner from 'shared/components/Spinner';
|
import Spinner from 'shared/components/Spinner.vue';
|
||||||
import alertMixin from 'shared/mixins/alertMixin';
|
import alertMixin from 'shared/mixins/alertMixin';
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@@ -61,11 +61,9 @@ export default {
|
|||||||
this.label = this.$t('AUTOMATION.ATTACHMENT.LABEL_UPLOADING');
|
this.label = this.$t('AUTOMATION.ATTACHMENT.LABEL_UPLOADING');
|
||||||
try {
|
try {
|
||||||
const file = event.target.files[0];
|
const file = event.target.files[0];
|
||||||
const formData = new FormData();
|
|
||||||
formData.append('attachment', file, file.name);
|
|
||||||
const id = await this.$store.dispatch(
|
const id = await this.$store.dispatch(
|
||||||
'automations/uploadAttachment',
|
'automations/uploadAttachment',
|
||||||
formData
|
file
|
||||||
);
|
);
|
||||||
this.$emit('input', [id]);
|
this.$emit('input', [id]);
|
||||||
this.uploadState = 'uploaded';
|
this.uploadState = 'uploaded';
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import ChannelSelector from '../ChannelSelector';
|
import ChannelSelector from '../ChannelSelector.vue';
|
||||||
export default {
|
export default {
|
||||||
components: { ChannelSelector },
|
components: { ChannelSelector },
|
||||||
props: {
|
props: {
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import LoadingState from 'dashboard/components/widgets/LoadingState';
|
import LoadingState from 'dashboard/components/widgets/LoadingState.vue';
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
LoadingState,
|
LoadingState,
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
<!-- eslint-disable vue/no-mutating-props -->
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
@@ -255,7 +256,9 @@ export default {
|
|||||||
value === 'conversation_attribute' ||
|
value === 'conversation_attribute' ||
|
||||||
value === 'contact_attribute'
|
value === 'contact_attribute'
|
||||||
) {
|
) {
|
||||||
|
// eslint-disable-next-line vue/no-mutating-props
|
||||||
this.value.custom_attribute_type = this.customAttributeType;
|
this.value.custom_attribute_type = this.customAttributeType;
|
||||||
|
// eslint-disable-next-line vue/no-mutating-props
|
||||||
} else this.value.custom_attribute_type = '';
|
} else this.value.custom_attribute_type = '';
|
||||||
},
|
},
|
||||||
immediate: true,
|
immediate: true,
|
||||||
|
|||||||
@@ -30,9 +30,9 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import AddLabel from 'shared/components/ui/dropdown/AddLabel';
|
import AddLabel from 'shared/components/ui/dropdown/AddLabel.vue';
|
||||||
import eventListenerMixins from 'shared/mixins/eventListenerMixins';
|
import eventListenerMixins from 'shared/mixins/eventListenerMixins';
|
||||||
import LabelDropdown from 'shared/components/ui/label/LabelDropdown';
|
import LabelDropdown from 'shared/components/ui/label/LabelDropdown.vue';
|
||||||
import { mixin as clickaway } from 'vue-clickaway';
|
import { mixin as clickaway } from 'vue-clickaway';
|
||||||
import adminMixin from 'dashboard/mixins/isAdmin';
|
import adminMixin from 'dashboard/mixins/isAdmin';
|
||||||
import {
|
import {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user