Merge branch 'release/2.15.0'
This commit is contained in:
commit
3e6d23e071
@ -102,8 +102,8 @@ jobs:
|
|||||||
echo "ERROR: The swagger.json file is not in sync with the yaml specification. Run 'rake swagger:build' and commit 'swagger/swagger.json'."
|
echo "ERROR: The swagger.json file is not in sync with the yaml specification. Run 'rake swagger:build' and commit 'swagger/swagger.json'."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
curl -L https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/5.3.0/openapi-generator-cli-5.3.0.jar > ~/tmp/openapi-generator-cli-5.3.0.jar
|
curl -L https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/6.3.0/openapi-generator-cli-6.3.0.jar > ~/tmp/openapi-generator-cli-6.3.0.jar
|
||||||
java -jar ~/tmp/openapi-generator-cli-5.3.0.jar validate -i swagger/swagger.json
|
java -jar ~/tmp/openapi-generator-cli-6.3.0.jar validate -i swagger/swagger.json
|
||||||
|
|
||||||
# Database setup
|
# Database setup
|
||||||
- run: yarn install --check-files
|
- run: yarn install --check-files
|
||||||
|
|||||||
@ -30,7 +30,7 @@ RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
|
|||||||
npm
|
npm
|
||||||
|
|
||||||
# Install rbenv and ruby
|
# Install rbenv and ruby
|
||||||
ARG RUBY_VERSION="3.0.4"
|
ARG RUBY_VERSION="3.1.3"
|
||||||
RUN git clone https://github.com/rbenv/rbenv.git ~/.rbenv \
|
RUN git clone https://github.com/rbenv/rbenv.git ~/.rbenv \
|
||||||
&& echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc \
|
&& echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc \
|
||||||
&& echo 'eval "$(rbenv init -)"' >> ~/.bashrc
|
&& echo 'eval "$(rbenv init -)"' >> ~/.bashrc
|
||||||
|
|||||||
@ -25,7 +25,7 @@
|
|||||||
// 1025,8025 mailhog
|
// 1025,8025 mailhog
|
||||||
"forwardPorts": [8025, 3000, 3035],
|
"forwardPorts": [8025, 3000, 3035],
|
||||||
|
|
||||||
"postCreateCommand": ".devcontainer/scripts/setup.sh && bundle exec rake db:chatwoot_prepare && yarn",
|
"postCreateCommand": ".devcontainer/scripts/setup.sh && POSTGRES_STATEMENT_TIMEOUT=600s bundle exec rake db:chatwoot_prepare && yarn",
|
||||||
"portsAttributes": {
|
"portsAttributes": {
|
||||||
"3000": {
|
"3000": {
|
||||||
"label": "Rails Server"
|
"label": "Rails Server"
|
||||||
|
|||||||
11
.env.example
11
.env.example
@ -52,6 +52,8 @@ POSTGRES_HOST=postgres
|
|||||||
POSTGRES_USERNAME=postgres
|
POSTGRES_USERNAME=postgres
|
||||||
POSTGRES_PASSWORD=
|
POSTGRES_PASSWORD=
|
||||||
RAILS_ENV=development
|
RAILS_ENV=development
|
||||||
|
# Changes the Postgres query timeout limit. The default is 14 seconds. Modify only when required.
|
||||||
|
# POSTGRES_STATEMENT_TIMEOUT=14s
|
||||||
RAILS_MAX_THREADS=5
|
RAILS_MAX_THREADS=5
|
||||||
|
|
||||||
# The email from which all outgoing emails are sent
|
# The email from which all outgoing emails are sent
|
||||||
@ -169,6 +171,9 @@ USE_INBOX_AVATAR_FOR_BOT=true
|
|||||||
## Sentry
|
## Sentry
|
||||||
# SENTRY_DSN=
|
# SENTRY_DSN=
|
||||||
|
|
||||||
|
## LogRocket
|
||||||
|
# LOG_ROCKET_PROJECT_ID=xxxxx/some-project
|
||||||
|
|
||||||
## Scout
|
## Scout
|
||||||
## https://scoutapm.com/docs/ruby/configuration
|
## https://scoutapm.com/docs/ruby/configuration
|
||||||
# SCOUT_KEY=YOURKEY
|
# SCOUT_KEY=YOURKEY
|
||||||
@ -186,11 +191,7 @@ USE_INBOX_AVATAR_FOR_BOT=true
|
|||||||
## https://github.com/DataDog/dd-trace-rb/blob/master/docs/GettingStarted.md#environment-variables
|
## https://github.com/DataDog/dd-trace-rb/blob/master/docs/GettingStarted.md#environment-variables
|
||||||
# DD_TRACE_AGENT_URL=
|
# DD_TRACE_AGENT_URL=
|
||||||
|
|
||||||
## IP look up configuration
|
# MaxMindDB API key to download GeoLite2 City database
|
||||||
## ref https://github.com/alexreisner/geocoder/blob/master/README_API_GUIDE.md
|
|
||||||
## works only on accounts with ip look up feature enabled
|
|
||||||
# IP_LOOKUP_SERVICE=geoip2
|
|
||||||
# maxmindb api key to use geoip2 service
|
|
||||||
# IP_LOOKUP_API_KEY=
|
# IP_LOOKUP_API_KEY=
|
||||||
|
|
||||||
## Rack Attack configuration
|
## Rack Attack configuration
|
||||||
|
|||||||
6
.github/workflows/nightly_installer.yml
vendored
6
.github/workflows/nightly_installer.yml
vendored
@ -44,3 +44,9 @@ jobs:
|
|||||||
# sudo systemctl restart chatwoot.target
|
# sudo systemctl restart chatwoot.target
|
||||||
# curl http://localhost:3000/api
|
# curl http://localhost:3000/api
|
||||||
|
|
||||||
|
- name: Upload chatwoot setup log file as an artifact
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
if: always()
|
||||||
|
with:
|
||||||
|
name: chatwoot-setup-log-file
|
||||||
|
path: /var/log/chatwoot-setup.log
|
||||||
|
|||||||
13
.github/workflows/run_foss_spec.yml
vendored
13
.github/workflows/run_foss_spec.yml
vendored
@ -15,7 +15,7 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-20.04
|
||||||
services:
|
services:
|
||||||
postgres:
|
postgres:
|
||||||
image: postgres:10.8
|
image: postgres:10.8
|
||||||
@ -49,6 +49,10 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
|
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
|
||||||
|
|
||||||
|
- uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: 16
|
||||||
|
|
||||||
- name: yarn
|
- name: yarn
|
||||||
run: yarn install
|
run: yarn install
|
||||||
|
|
||||||
@ -70,3 +74,10 @@ 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
|
||||||
|
|
||||||
|
- name: Upload rails log folder
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
if: always()
|
||||||
|
with:
|
||||||
|
name: rails-log-folder
|
||||||
|
path: log
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
npx --no-install lint-staged
|
npx --no-install lint-staged
|
||||||
|
|
||||||
# lint only staged ruby files
|
# lint only staged ruby files
|
||||||
git diff --name-only --cached | xargs ls -1 2>/dev/null | grep '\.rb$' | xargs bundle exec rubocop -a
|
git diff --name-only --cached | xargs ls -1 2>/dev/null | grep '\.rb$' | xargs bundle exec rubocop --force-exclusion -a
|
||||||
|
|
||||||
# stage rubocop changes to files
|
# stage rubocop changes to files
|
||||||
git diff --name-only --cached | xargs git add
|
git diff --name-only --cached | xargs git add
|
||||||
|
|||||||
7
.vscode/settings.json
vendored
7
.vscode/settings.json
vendored
@ -1 +1,6 @@
|
|||||||
{}
|
{
|
||||||
|
"cSpell.words": [
|
||||||
|
"chatwoot",
|
||||||
|
"dompurify"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|||||||
21
Gemfile
21
Gemfile
@ -4,7 +4,7 @@ ruby '3.1.3'
|
|||||||
|
|
||||||
##-- base gems for rails --##
|
##-- base gems for rails --##
|
||||||
gem 'rack-cors', require: 'rack/cors'
|
gem 'rack-cors', require: 'rack/cors'
|
||||||
gem 'rails', '~> 6.1', '>= 6.1.7.1'
|
gem 'rails', '~> 6.1', '>= 6.1.7.3'
|
||||||
# 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
|
||||||
|
|
||||||
@ -96,13 +96,16 @@ gem 'slack-ruby-client'
|
|||||||
gem 'google-cloud-dialogflow'
|
gem 'google-cloud-dialogflow'
|
||||||
|
|
||||||
##-- apm and error monitoring ---#
|
##-- apm and error monitoring ---#
|
||||||
gem 'ddtrace'
|
# loaded only when environment variables are set.
|
||||||
gem 'elastic-apm'
|
# ref application.rb
|
||||||
gem 'newrelic_rpm'
|
gem 'ddtrace', require: false
|
||||||
gem 'scout_apm'
|
gem 'elastic-apm', require: false
|
||||||
gem 'sentry-rails', '~> 5.3', '>= 5.3.1'
|
gem 'newrelic_rpm', require: false
|
||||||
gem 'sentry-ruby', '~> 5.3'
|
gem 'newrelic-sidekiq-metrics', require: false
|
||||||
gem 'sentry-sidekiq', '~> 5.3', '>= 5.3.1'
|
gem 'scout_apm', require: false
|
||||||
|
gem 'sentry-rails', require: false
|
||||||
|
gem 'sentry-ruby', require: false
|
||||||
|
gem 'sentry-sidekiq', require: false
|
||||||
|
|
||||||
##-- background job processing --##
|
##-- background job processing --##
|
||||||
gem 'sidekiq', '~> 6.4.2'
|
gem 'sidekiq', '~> 6.4.2'
|
||||||
@ -203,6 +206,8 @@ end
|
|||||||
# worked with microsoft refresh token
|
# worked with microsoft refresh token
|
||||||
gem 'omniauth-oauth2'
|
gem 'omniauth-oauth2'
|
||||||
|
|
||||||
|
gem 'audited', '~> 5.2'
|
||||||
|
|
||||||
# need for google auth
|
# need for google auth
|
||||||
gem 'omniauth'
|
gem 'omniauth'
|
||||||
gem 'omniauth-google-oauth2'
|
gem 'omniauth-google-oauth2'
|
||||||
|
|||||||
162
Gemfile.lock
162
Gemfile.lock
@ -9,63 +9,63 @@ GIT
|
|||||||
GEM
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
actioncable (6.1.7.1)
|
actioncable (6.1.7.3)
|
||||||
actionpack (= 6.1.7.1)
|
actionpack (= 6.1.7.3)
|
||||||
activesupport (= 6.1.7.1)
|
activesupport (= 6.1.7.3)
|
||||||
nio4r (~> 2.0)
|
nio4r (~> 2.0)
|
||||||
websocket-driver (>= 0.6.1)
|
websocket-driver (>= 0.6.1)
|
||||||
actionmailbox (6.1.7.1)
|
actionmailbox (6.1.7.3)
|
||||||
actionpack (= 6.1.7.1)
|
actionpack (= 6.1.7.3)
|
||||||
activejob (= 6.1.7.1)
|
activejob (= 6.1.7.3)
|
||||||
activerecord (= 6.1.7.1)
|
activerecord (= 6.1.7.3)
|
||||||
activestorage (= 6.1.7.1)
|
activestorage (= 6.1.7.3)
|
||||||
activesupport (= 6.1.7.1)
|
activesupport (= 6.1.7.3)
|
||||||
mail (>= 2.7.1)
|
mail (>= 2.7.1)
|
||||||
actionmailer (6.1.7.1)
|
actionmailer (6.1.7.3)
|
||||||
actionpack (= 6.1.7.1)
|
actionpack (= 6.1.7.3)
|
||||||
actionview (= 6.1.7.1)
|
actionview (= 6.1.7.3)
|
||||||
activejob (= 6.1.7.1)
|
activejob (= 6.1.7.3)
|
||||||
activesupport (= 6.1.7.1)
|
activesupport (= 6.1.7.3)
|
||||||
mail (~> 2.5, >= 2.5.4)
|
mail (~> 2.5, >= 2.5.4)
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.0)
|
||||||
actionpack (6.1.7.1)
|
actionpack (6.1.7.3)
|
||||||
actionview (= 6.1.7.1)
|
actionview (= 6.1.7.3)
|
||||||
activesupport (= 6.1.7.1)
|
activesupport (= 6.1.7.3)
|
||||||
rack (~> 2.0, >= 2.0.9)
|
rack (~> 2.0, >= 2.0.9)
|
||||||
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 (6.1.7.1)
|
actiontext (6.1.7.3)
|
||||||
actionpack (= 6.1.7.1)
|
actionpack (= 6.1.7.3)
|
||||||
activerecord (= 6.1.7.1)
|
activerecord (= 6.1.7.3)
|
||||||
activestorage (= 6.1.7.1)
|
activestorage (= 6.1.7.3)
|
||||||
activesupport (= 6.1.7.1)
|
activesupport (= 6.1.7.3)
|
||||||
nokogiri (>= 1.8.5)
|
nokogiri (>= 1.8.5)
|
||||||
actionview (6.1.7.1)
|
actionview (6.1.7.3)
|
||||||
activesupport (= 6.1.7.1)
|
activesupport (= 6.1.7.3)
|
||||||
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 (6.1.7.1)
|
activejob (6.1.7.3)
|
||||||
activesupport (= 6.1.7.1)
|
activesupport (= 6.1.7.3)
|
||||||
globalid (>= 0.3.6)
|
globalid (>= 0.3.6)
|
||||||
activemodel (6.1.7.1)
|
activemodel (6.1.7.3)
|
||||||
activesupport (= 6.1.7.1)
|
activesupport (= 6.1.7.3)
|
||||||
activerecord (6.1.7.1)
|
activerecord (6.1.7.3)
|
||||||
activemodel (= 6.1.7.1)
|
activemodel (= 6.1.7.3)
|
||||||
activesupport (= 6.1.7.1)
|
activesupport (= 6.1.7.3)
|
||||||
activerecord-import (1.4.0)
|
activerecord-import (1.4.0)
|
||||||
activerecord (>= 4.2)
|
activerecord (>= 4.2)
|
||||||
activestorage (6.1.7.1)
|
activestorage (6.1.7.3)
|
||||||
actionpack (= 6.1.7.1)
|
actionpack (= 6.1.7.3)
|
||||||
activejob (= 6.1.7.1)
|
activejob (= 6.1.7.3)
|
||||||
activerecord (= 6.1.7.1)
|
activerecord (= 6.1.7.3)
|
||||||
activesupport (= 6.1.7.1)
|
activesupport (= 6.1.7.3)
|
||||||
marcel (~> 1.0)
|
marcel (~> 1.0)
|
||||||
mini_mime (>= 1.1.0)
|
mini_mime (>= 1.1.0)
|
||||||
activesupport (6.1.7.1)
|
activesupport (6.1.7.3)
|
||||||
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)
|
||||||
@ -90,6 +90,8 @@ GEM
|
|||||||
rake (>= 10.4, < 14.0)
|
rake (>= 10.4, < 14.0)
|
||||||
ast (2.4.2)
|
ast (2.4.2)
|
||||||
attr_extras (6.2.5)
|
attr_extras (6.2.5)
|
||||||
|
audited (5.2.0)
|
||||||
|
activerecord (>= 5.0, < 7.1)
|
||||||
aws-eventstream (1.2.0)
|
aws-eventstream (1.2.0)
|
||||||
aws-partitions (1.605.0)
|
aws-partitions (1.605.0)
|
||||||
aws-sdk-core (3.131.2)
|
aws-sdk-core (3.131.2)
|
||||||
@ -136,7 +138,7 @@ GEM
|
|||||||
climate_control (1.1.1)
|
climate_control (1.1.1)
|
||||||
coderay (1.1.3)
|
coderay (1.1.3)
|
||||||
commonmarker (0.23.7)
|
commonmarker (0.23.7)
|
||||||
concurrent-ruby (1.1.10)
|
concurrent-ruby (1.2.2)
|
||||||
connection_pool (2.2.5)
|
connection_pool (2.2.5)
|
||||||
crack (0.4.5)
|
crack (0.4.5)
|
||||||
rexml
|
rexml
|
||||||
@ -187,7 +189,7 @@ GEM
|
|||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
http (>= 3.0)
|
http (>= 3.0)
|
||||||
email_reply_trimmer (0.1.13)
|
email_reply_trimmer (0.1.13)
|
||||||
erubi (1.10.0)
|
erubi (1.12.0)
|
||||||
et-orbi (1.2.7)
|
et-orbi (1.2.7)
|
||||||
tzinfo
|
tzinfo
|
||||||
execjs (2.8.1)
|
execjs (2.8.1)
|
||||||
@ -248,7 +250,7 @@ GEM
|
|||||||
grpc (~> 1.36)
|
grpc (~> 1.36)
|
||||||
geocoder (1.8.0)
|
geocoder (1.8.0)
|
||||||
gli (2.21.0)
|
gli (2.21.0)
|
||||||
globalid (1.0.1)
|
globalid (1.1.0)
|
||||||
activesupport (>= 5.0)
|
activesupport (>= 5.0)
|
||||||
gmail_xoauth (0.4.2)
|
gmail_xoauth (0.4.2)
|
||||||
oauth (>= 0.3.6)
|
oauth (>= 0.3.6)
|
||||||
@ -353,7 +355,7 @@ GEM
|
|||||||
mini_mime (>= 1.0.0)
|
mini_mime (>= 1.0.0)
|
||||||
multi_xml (>= 0.5.2)
|
multi_xml (>= 0.5.2)
|
||||||
httpclient (2.8.3)
|
httpclient (2.8.3)
|
||||||
i18n (1.11.0)
|
i18n (1.12.0)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
image_processing (1.12.2)
|
image_processing (1.12.2)
|
||||||
mini_magick (>= 4.9.5, < 5)
|
mini_magick (>= 4.9.5, < 5)
|
||||||
@ -417,8 +419,11 @@ GEM
|
|||||||
loofah (2.19.1)
|
loofah (2.19.1)
|
||||||
crass (~> 1.0.2)
|
crass (~> 1.0.2)
|
||||||
nokogiri (>= 1.5.9)
|
nokogiri (>= 1.5.9)
|
||||||
mail (2.7.1)
|
mail (2.8.1)
|
||||||
mini_mime (>= 0.1.1)
|
mini_mime (>= 0.1.1)
|
||||||
|
net-imap
|
||||||
|
net-pop
|
||||||
|
net-smtp
|
||||||
marcel (1.0.2)
|
marcel (1.0.2)
|
||||||
maxminddb (0.1.22)
|
maxminddb (0.1.22)
|
||||||
memoist (0.16.2)
|
memoist (0.16.2)
|
||||||
@ -428,8 +433,8 @@ GEM
|
|||||||
mime-types-data (3.2022.0105)
|
mime-types-data (3.2022.0105)
|
||||||
mini_magick (4.11.0)
|
mini_magick (4.11.0)
|
||||||
mini_mime (1.1.2)
|
mini_mime (1.1.2)
|
||||||
mini_portile2 (2.8.0)
|
mini_portile2 (2.8.1)
|
||||||
minitest (5.16.2)
|
minitest (5.18.0)
|
||||||
mock_redis (0.32.0)
|
mock_redis (0.32.0)
|
||||||
ruby2_keywords
|
ruby2_keywords
|
||||||
momentjs-rails (2.29.1.1)
|
momentjs-rails (2.29.1.1)
|
||||||
@ -449,16 +454,19 @@ GEM
|
|||||||
net-smtp (0.3.3)
|
net-smtp (0.3.3)
|
||||||
net-protocol
|
net-protocol
|
||||||
netrc (0.11.0)
|
netrc (0.11.0)
|
||||||
|
newrelic-sidekiq-metrics (1.6.1)
|
||||||
|
newrelic_rpm (~> 8)
|
||||||
|
sidekiq
|
||||||
newrelic_rpm (8.15.0)
|
newrelic_rpm (8.15.0)
|
||||||
nio4r (2.5.8)
|
nio4r (2.5.8)
|
||||||
nokogiri (1.13.10)
|
nokogiri (1.14.2)
|
||||||
mini_portile2 (~> 2.8.0)
|
mini_portile2 (~> 2.8.0)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
nokogiri (1.13.10-arm64-darwin)
|
nokogiri (1.14.2-arm64-darwin)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
nokogiri (1.13.10-x86_64-darwin)
|
nokogiri (1.14.2-x86_64-darwin)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
nokogiri (1.13.10-x86_64-linux)
|
nokogiri (1.14.2-x86_64-linux)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
oauth (0.5.10)
|
oauth (0.5.10)
|
||||||
oauth2 (2.0.9)
|
oauth2 (2.0.9)
|
||||||
@ -506,8 +514,8 @@ GEM
|
|||||||
pundit (2.2.0)
|
pundit (2.2.0)
|
||||||
activesupport (>= 3.0.0)
|
activesupport (>= 3.0.0)
|
||||||
raabro (1.4.0)
|
raabro (1.4.0)
|
||||||
racc (1.6.1)
|
racc (1.6.2)
|
||||||
rack (2.2.6.2)
|
rack (2.2.6.4)
|
||||||
rack-attack (6.6.1)
|
rack-attack (6.6.1)
|
||||||
rack (>= 1.0, < 3)
|
rack (>= 1.0, < 3)
|
||||||
rack-cors (1.1.1)
|
rack-cors (1.1.1)
|
||||||
@ -516,32 +524,32 @@ GEM
|
|||||||
rack
|
rack
|
||||||
rack-proxy (0.7.2)
|
rack-proxy (0.7.2)
|
||||||
rack
|
rack
|
||||||
rack-test (2.0.2)
|
rack-test (2.1.0)
|
||||||
rack (>= 1.3)
|
rack (>= 1.3)
|
||||||
rack-timeout (0.6.3)
|
rack-timeout (0.6.3)
|
||||||
rails (6.1.7.1)
|
rails (6.1.7.3)
|
||||||
actioncable (= 6.1.7.1)
|
actioncable (= 6.1.7.3)
|
||||||
actionmailbox (= 6.1.7.1)
|
actionmailbox (= 6.1.7.3)
|
||||||
actionmailer (= 6.1.7.1)
|
actionmailer (= 6.1.7.3)
|
||||||
actionpack (= 6.1.7.1)
|
actionpack (= 6.1.7.3)
|
||||||
actiontext (= 6.1.7.1)
|
actiontext (= 6.1.7.3)
|
||||||
actionview (= 6.1.7.1)
|
actionview (= 6.1.7.3)
|
||||||
activejob (= 6.1.7.1)
|
activejob (= 6.1.7.3)
|
||||||
activemodel (= 6.1.7.1)
|
activemodel (= 6.1.7.3)
|
||||||
activerecord (= 6.1.7.1)
|
activerecord (= 6.1.7.3)
|
||||||
activestorage (= 6.1.7.1)
|
activestorage (= 6.1.7.3)
|
||||||
activesupport (= 6.1.7.1)
|
activesupport (= 6.1.7.3)
|
||||||
bundler (>= 1.15.0)
|
bundler (>= 1.15.0)
|
||||||
railties (= 6.1.7.1)
|
railties (= 6.1.7.3)
|
||||||
sprockets-rails (>= 2.0.0)
|
sprockets-rails (>= 2.0.0)
|
||||||
rails-dom-testing (2.0.3)
|
rails-dom-testing (2.0.3)
|
||||||
activesupport (>= 4.2.0)
|
activesupport (>= 4.2.0)
|
||||||
nokogiri (>= 1.6)
|
nokogiri (>= 1.6)
|
||||||
rails-html-sanitizer (1.4.4)
|
rails-html-sanitizer (1.5.0)
|
||||||
loofah (~> 2.19, >= 2.19.1)
|
loofah (~> 2.19, >= 2.19.1)
|
||||||
railties (6.1.7.1)
|
railties (6.1.7.3)
|
||||||
actionpack (= 6.1.7.1)
|
actionpack (= 6.1.7.3)
|
||||||
activesupport (= 6.1.7.1)
|
activesupport (= 6.1.7.3)
|
||||||
method_source
|
method_source
|
||||||
rake (>= 12.2)
|
rake (>= 12.2)
|
||||||
thor (~> 1.0)
|
thor (~> 1.0)
|
||||||
@ -676,9 +684,9 @@ GEM
|
|||||||
spring-watcher-listen (2.0.1)
|
spring-watcher-listen (2.0.1)
|
||||||
listen (>= 2.7, < 4.0)
|
listen (>= 2.7, < 4.0)
|
||||||
spring (>= 1.2, < 3.0)
|
spring (>= 1.2, < 3.0)
|
||||||
sprockets (4.1.1)
|
sprockets (4.2.0)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
rack (> 1, < 3)
|
rack (>= 2.2.4, < 4)
|
||||||
sprockets-rails (3.4.2)
|
sprockets-rails (3.4.2)
|
||||||
actionpack (>= 5.2)
|
actionpack (>= 5.2)
|
||||||
activesupport (>= 5.2)
|
activesupport (>= 5.2)
|
||||||
@ -701,7 +709,7 @@ GEM
|
|||||||
nokogiri (>= 1.6, < 2.0)
|
nokogiri (>= 1.6, < 2.0)
|
||||||
twitty (0.1.4)
|
twitty (0.1.4)
|
||||||
oauth
|
oauth
|
||||||
tzinfo (2.0.4)
|
tzinfo (2.0.6)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
tzinfo-data (1.2022.1)
|
tzinfo-data (1.2022.1)
|
||||||
tzinfo (>= 1.0.0)
|
tzinfo (>= 1.0.0)
|
||||||
@ -746,7 +754,7 @@ GEM
|
|||||||
working_hours (1.4.1)
|
working_hours (1.4.1)
|
||||||
activesupport (>= 3.2)
|
activesupport (>= 3.2)
|
||||||
tzinfo
|
tzinfo
|
||||||
zeitwerk (2.6.0)
|
zeitwerk (2.6.7)
|
||||||
|
|
||||||
PLATFORMS
|
PLATFORMS
|
||||||
arm64-darwin-20
|
arm64-darwin-20
|
||||||
@ -763,6 +771,7 @@ DEPENDENCIES
|
|||||||
administrate
|
administrate
|
||||||
annotate
|
annotate
|
||||||
attr_extras
|
attr_extras
|
||||||
|
audited (~> 5.2)
|
||||||
aws-sdk-s3
|
aws-sdk-s3
|
||||||
azure-storage-blob
|
azure-storage-blob
|
||||||
barnes
|
barnes
|
||||||
@ -817,6 +826,7 @@ DEPENDENCIES
|
|||||||
net-imap
|
net-imap
|
||||||
net-pop
|
net-pop
|
||||||
net-smtp
|
net-smtp
|
||||||
|
newrelic-sidekiq-metrics
|
||||||
newrelic_rpm
|
newrelic_rpm
|
||||||
omniauth
|
omniauth
|
||||||
omniauth-google-oauth2
|
omniauth-google-oauth2
|
||||||
@ -831,7 +841,7 @@ DEPENDENCIES
|
|||||||
rack-attack
|
rack-attack
|
||||||
rack-cors
|
rack-cors
|
||||||
rack-timeout
|
rack-timeout
|
||||||
rails (~> 6.1, >= 6.1.7.1)
|
rails (~> 6.1, >= 6.1.7.3)
|
||||||
redis
|
redis
|
||||||
redis-namespace
|
redis-namespace
|
||||||
responders
|
responders
|
||||||
@ -844,9 +854,9 @@ DEPENDENCIES
|
|||||||
rubocop-rspec
|
rubocop-rspec
|
||||||
scout_apm
|
scout_apm
|
||||||
seed_dump
|
seed_dump
|
||||||
sentry-rails (~> 5.3, >= 5.3.1)
|
sentry-rails
|
||||||
sentry-ruby (~> 5.3)
|
sentry-ruby
|
||||||
sentry-sidekiq (~> 5.3, >= 5.3.1)
|
sentry-sidekiq
|
||||||
shoulda-matchers
|
shoulda-matchers
|
||||||
sidekiq (~> 6.4.2)
|
sidekiq (~> 6.4.2)
|
||||||
sidekiq-cron (~> 1.6, >= 1.6.0)
|
sidekiq-cron (~> 1.6, >= 1.6.0)
|
||||||
|
|||||||
6
Procfile
6
Procfile
@ -1,3 +1,3 @@
|
|||||||
release: bundle exec rails db:chatwoot_prepare
|
release: POSTGRES_STATEMENT_TIMEOUT=600s bundle exec rails db:chatwoot_prepare
|
||||||
web: bin/rails server -p $PORT -e $RAILS_ENV
|
web: bundle exec rails ip_lookup:setup && bin/rails server -p $PORT -e $RAILS_ENV
|
||||||
worker: bundle exec sidekiq -C config/sidekiq.yml
|
worker: bundle exec rails ip_lookup:setup && bundle exec sidekiq -C config/sidekiq.yml
|
||||||
|
|||||||
@ -2,3 +2,4 @@
|
|||||||
//= link administrate/application.css
|
//= link administrate/application.css
|
||||||
//= link administrate/application.js
|
//= link administrate/application.js
|
||||||
//= link dashboardChart.js
|
//= link dashboardChart.js
|
||||||
|
//= link secretField.js
|
||||||
|
|||||||
34
app/assets/javascripts/secretField.js
Normal file
34
app/assets/javascripts/secretField.js
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
// eslint-disable-next-line
|
||||||
|
function toggleSecretField(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
const toggler = e.currentTarget;
|
||||||
|
const secretField = toggler.parentElement;
|
||||||
|
const textElement = secretField.querySelector('[data-secret-masked]');
|
||||||
|
|
||||||
|
if (!textElement) return;
|
||||||
|
|
||||||
|
if (textElement.dataset.secretMasked === 'false') {
|
||||||
|
textElement.textContent = '•'.repeat(10);
|
||||||
|
textElement.dataset.secretMasked = 'true';
|
||||||
|
toggler.querySelector('svg use').setAttribute('xlink:href', '#eye-show');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
textElement.textContent = secretField.dataset.secretText;
|
||||||
|
textElement.dataset.secretMasked = 'false';
|
||||||
|
toggler.querySelector('svg use').setAttribute('xlink:href', '#eye-hide');
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line
|
||||||
|
function copySecretField(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
const toggler = e.currentTarget;
|
||||||
|
const secretField = toggler.parentElement;
|
||||||
|
|
||||||
|
navigator.clipboard.writeText(secretField.dataset.secretText);
|
||||||
|
}
|
||||||
@ -6,7 +6,6 @@
|
|||||||
@import 'utilities/text-color';
|
@import 'utilities/text-color';
|
||||||
|
|
||||||
@import 'selectize';
|
@import 'selectize';
|
||||||
@import 'datetime_picker';
|
|
||||||
|
|
||||||
@import 'library/clearfix';
|
@import 'library/clearfix';
|
||||||
@import 'library/data-label';
|
@import 'library/data-label';
|
||||||
|
|||||||
@ -43,3 +43,20 @@
|
|||||||
.cell-label--number {
|
.cell-label--number {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cell-data__secret-field {
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
span {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
margin-left: 5px;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
fill: currentColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -116,7 +116,11 @@ class Messages::Facebook::MessageBuilder < Messages::Messenger::MessageBuilder
|
|||||||
result = {}
|
result = {}
|
||||||
# OAuthException, code: 100, error_subcode: 2018218, message: (#100) No profile available for this user
|
# OAuthException, code: 100, error_subcode: 2018218, message: (#100) No profile available for this user
|
||||||
# We don't need to capture this error as we don't care about contact params in case of echo messages
|
# We don't need to capture this error as we don't care about contact params in case of echo messages
|
||||||
ChatwootExceptionTracker.new(e, account: @inbox.account).capture_exception unless @outgoing_echo
|
if e.message.include?('2018218')
|
||||||
|
Rails.logger.warn e
|
||||||
|
else
|
||||||
|
ChatwootExceptionTracker.new(e, account: @inbox.account).capture_exception unless @outgoing_echo
|
||||||
|
end
|
||||||
rescue StandardError => e
|
rescue StandardError => e
|
||||||
result = {}
|
result = {}
|
||||||
ChatwootExceptionTracker.new(e, account: @inbox.account).capture_exception
|
ChatwootExceptionTracker.new(e, account: @inbox.account).capture_exception
|
||||||
|
|||||||
@ -74,7 +74,7 @@ class V2::ReportBuilder
|
|||||||
:created_at,
|
:created_at,
|
||||||
default_value: 0,
|
default_value: 0,
|
||||||
range: range,
|
range: range,
|
||||||
permit: %w[day week month year],
|
permit: %w[day week month year hour],
|
||||||
time_zone: @timezone
|
time_zone: @timezone
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|||||||
@ -55,9 +55,9 @@ class Api::V1::Accounts::ArticlesController < Api::V1::Accounts::BaseController
|
|||||||
|
|
||||||
def article_params
|
def article_params
|
||||||
params.require(:article).permit(
|
params.require(:article).permit(
|
||||||
:title, :slug, :content, :description, :position, :category_id, :author_id, :associated_article_id, :status, meta: [:title,
|
:title, :slug, :position, :content, :description, :position, :category_id, :author_id, :associated_article_id, :status, meta: [:title,
|
||||||
:description,
|
:description,
|
||||||
{ tags: [] }]
|
{ tags: [] }]
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -142,6 +142,8 @@ class Api::V1::Accounts::ConversationsController < Api::V1::Accounts::BaseContro
|
|||||||
# and deprecate the support of passing only source_id as the param
|
# and deprecate the support of passing only source_id as the param
|
||||||
@contact_inbox ||= ::ContactInbox.find_by!(source_id: params[:source_id])
|
@contact_inbox ||= ::ContactInbox.find_by!(source_id: params[:source_id])
|
||||||
authorize @contact_inbox.inbox, :show?
|
authorize @contact_inbox.inbox, :show?
|
||||||
|
rescue ActiveRecord::RecordNotUnique
|
||||||
|
render json: { error: 'source_id should be unique' }, status: :unprocessable_entity
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_contact_inbox
|
def build_contact_inbox
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
class Api::V1::Accounts::Integrations::AppsController < Api::V1::Accounts::BaseController
|
class Api::V1::Accounts::Integrations::AppsController < Api::V1::Accounts::BaseController
|
||||||
before_action :check_admin_authorization?
|
before_action :check_admin_authorization?, except: [:index, :show]
|
||||||
before_action :fetch_apps, only: [:index]
|
before_action :fetch_apps, only: [:index]
|
||||||
before_action :fetch_app, only: [:show]
|
before_action :fetch_app, only: [:show]
|
||||||
|
|
||||||
|
|||||||
28
app/controllers/api/v1/accounts/search_controller.rb
Normal file
28
app/controllers/api/v1/accounts/search_controller.rb
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
class Api::V1::Accounts::SearchController < Api::V1::Accounts::BaseController
|
||||||
|
def index
|
||||||
|
@result = search('all')
|
||||||
|
end
|
||||||
|
|
||||||
|
def conversations
|
||||||
|
@result = search('Conversation')
|
||||||
|
end
|
||||||
|
|
||||||
|
def contacts
|
||||||
|
@result = search('Contact')
|
||||||
|
end
|
||||||
|
|
||||||
|
def messages
|
||||||
|
@result = search('Message')
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def search(search_type)
|
||||||
|
SearchService.new(
|
||||||
|
current_user: Current.user,
|
||||||
|
current_account: Current.account,
|
||||||
|
search_type: search_type,
|
||||||
|
params: params
|
||||||
|
).perform
|
||||||
|
end
|
||||||
|
end
|
||||||
@ -17,7 +17,7 @@ class Api::V1::Widget::MessagesController < Api::V1::Widget::BaseController
|
|||||||
@message.update!(submitted_email: contact_email)
|
@message.update!(submitted_email: contact_email)
|
||||||
ContactIdentifyAction.new(
|
ContactIdentifyAction.new(
|
||||||
contact: @contact,
|
contact: @contact,
|
||||||
params: { email: contact_email }
|
params: { email: contact_email, name: contact_name }
|
||||||
).perform
|
).perform
|
||||||
else
|
else
|
||||||
@message.update!(message_update_params[:message])
|
@message.update!(message_update_params[:message])
|
||||||
|
|||||||
@ -45,13 +45,18 @@ class Microsoft::CallbacksController < ApplicationController
|
|||||||
channel_email.inbox
|
channel_email.inbox
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Fallback name, for when name field is missing from users_data
|
||||||
|
def fallback_name
|
||||||
|
users_data['email'].split('@').first.parameterize.titleize
|
||||||
|
end
|
||||||
|
|
||||||
def create_microsoft_channel_with_inbox
|
def create_microsoft_channel_with_inbox
|
||||||
ActiveRecord::Base.transaction do
|
ActiveRecord::Base.transaction do
|
||||||
channel_email = Channel::Email.create!(email: users_data['email'], account: account)
|
channel_email = Channel::Email.create!(email: users_data['email'], account: account)
|
||||||
account.inboxes.create!(
|
account.inboxes.create!(
|
||||||
account: account,
|
account: account,
|
||||||
channel: channel_email,
|
channel: channel_email,
|
||||||
name: users_data['name']
|
name: users_data['name'] || fallback_name
|
||||||
)
|
)
|
||||||
channel_email
|
channel_email
|
||||||
end
|
end
|
||||||
|
|||||||
@ -12,6 +12,8 @@ class Public::Api::V1::Inboxes::MessagesController < Public::Api::V1::InboxesCon
|
|||||||
end
|
end
|
||||||
|
|
||||||
def update
|
def update
|
||||||
|
render json: { error: 'You cannot update the CSAT survey after 14 days' }, status: :unprocessable_entity and return if check_csat_locked
|
||||||
|
|
||||||
@message.update!(message_update_params)
|
@message.update!(message_update_params)
|
||||||
rescue StandardError => e
|
rescue StandardError => e
|
||||||
render json: { error: @contact.errors, message: e.message }.to_json, status: :internal_server_error
|
render json: { error: @contact.errors, message: e.message }.to_json, status: :internal_server_error
|
||||||
@ -43,7 +45,7 @@ class Public::Api::V1::Inboxes::MessagesController < Public::Api::V1::InboxesCon
|
|||||||
end
|
end
|
||||||
|
|
||||||
def message_update_params
|
def message_update_params
|
||||||
params.permit(submitted_values: [:name, :title, :value])
|
params.permit(submitted_values: [:name, :title, :value, { csat_survey_response: [:feedback_message, :rating] }])
|
||||||
end
|
end
|
||||||
|
|
||||||
def permitted_params
|
def permitted_params
|
||||||
@ -64,4 +66,8 @@ class Public::Api::V1::Inboxes::MessagesController < Public::Api::V1::InboxesCon
|
|||||||
message_type: :incoming
|
message_type: :incoming
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def check_csat_locked
|
||||||
|
(Time.zone.now.to_date - @message.created_at.to_date).to_i > 14 and @message.content_type == 'input_csat'
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
class Public::Api::V1::Portals::ArticlesController < PublicController
|
class Public::Api::V1::Portals::ArticlesController < Public::Api::V1::Portals::BaseController
|
||||||
before_action :ensure_custom_domain_request, only: [:show, :index]
|
before_action :ensure_custom_domain_request, only: [:show, :index]
|
||||||
before_action :portal
|
before_action :portal
|
||||||
before_action :set_category, except: [:index]
|
before_action :set_category, except: [:index]
|
||||||
@ -8,6 +8,7 @@ class Public::Api::V1::Portals::ArticlesController < PublicController
|
|||||||
def index
|
def index
|
||||||
@articles = @portal.articles
|
@articles = @portal.articles
|
||||||
@articles = @articles.search(list_params) if list_params.present?
|
@articles = @articles.search(list_params) if list_params.present?
|
||||||
|
@articles.order(position: :asc)
|
||||||
end
|
end
|
||||||
|
|
||||||
def show; end
|
def show; end
|
||||||
@ -15,21 +16,30 @@ class Public::Api::V1::Portals::ArticlesController < PublicController
|
|||||||
private
|
private
|
||||||
|
|
||||||
def set_article
|
def set_article
|
||||||
@article = @category.articles.find(params[:id])
|
@article = @category.articles.find(permitted_params[:id])
|
||||||
@article.increment_view_count
|
@article.increment_view_count
|
||||||
@parsed_content = render_article_content(@article.content)
|
@parsed_content = render_article_content(@article.content)
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_category
|
def set_category
|
||||||
@category = @portal.categories.find_by!(slug: params[:category_slug]) if params[:category_slug].present?
|
return if permitted_params[:category_slug].blank?
|
||||||
|
|
||||||
|
@category = @portal.categories.find_by!(
|
||||||
|
slug: permitted_params[:category_slug],
|
||||||
|
locale: permitted_params[:locale]
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def portal
|
def portal
|
||||||
@portal ||= Portal.find_by!(slug: params[:slug], archived: false)
|
@portal ||= Portal.find_by!(slug: permitted_params[:slug], archived: false)
|
||||||
end
|
end
|
||||||
|
|
||||||
def list_params
|
def list_params
|
||||||
params.permit(:query)
|
params.permit(:query, :locale)
|
||||||
|
end
|
||||||
|
|
||||||
|
def permitted_params
|
||||||
|
params.permit(:slug, :category_slug, :locale, :id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_article_content(content)
|
def render_article_content(content)
|
||||||
|
|||||||
22
app/controllers/public/api/v1/portals/base_controller.rb
Normal file
22
app/controllers/public/api/v1/portals/base_controller.rb
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
class Public::Api::V1::Portals::BaseController < PublicController
|
||||||
|
around_action :set_locale
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def set_locale(&)
|
||||||
|
switch_locale_with_portal(&) if params[:locale].present?
|
||||||
|
end
|
||||||
|
|
||||||
|
def switch_locale_with_portal(&)
|
||||||
|
locale_without_variant = params[:locale].split('_')[0]
|
||||||
|
is_locale_available = I18n.available_locales.map(&:to_s).include?(params[:locale])
|
||||||
|
is_locale_variant_available = I18n.available_locales.map(&:to_s).include?(locale_without_variant)
|
||||||
|
if is_locale_available
|
||||||
|
@locale = params[:locale]
|
||||||
|
elsif is_locale_variant_available
|
||||||
|
@locale = locale_without_variant
|
||||||
|
end
|
||||||
|
|
||||||
|
I18n.with_locale(@locale, &)
|
||||||
|
end
|
||||||
|
end
|
||||||
@ -1,11 +1,11 @@
|
|||||||
class Public::Api::V1::Portals::CategoriesController < PublicController
|
class Public::Api::V1::Portals::CategoriesController < Public::Api::V1::Portals::BaseController
|
||||||
before_action :ensure_custom_domain_request, only: [:show, :index]
|
before_action :ensure_custom_domain_request, only: [:show, :index]
|
||||||
before_action :portal
|
before_action :portal
|
||||||
before_action :set_category, only: [:show]
|
before_action :set_category, only: [:show]
|
||||||
layout 'portal'
|
layout 'portal'
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@categories = @portal.categories
|
@categories = @portal.categories.order(position: :asc)
|
||||||
end
|
end
|
||||||
|
|
||||||
def show; end
|
def show; end
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
class Public::Api::V1::PortalsController < PublicController
|
class Public::Api::V1::PortalsController < Public::Api::V1::Portals::BaseController
|
||||||
before_action :ensure_custom_domain_request, only: [:show]
|
before_action :ensure_custom_domain_request, only: [:show]
|
||||||
before_action :portal
|
before_action :portal
|
||||||
before_action :redirect_to_portal_with_locale, only: [:show]
|
before_action :redirect_to_portal_with_locale, only: [:show]
|
||||||
|
|||||||
@ -5,6 +5,7 @@ class WidgetsController < ActionController::Base
|
|||||||
before_action :set_global_config
|
before_action :set_global_config
|
||||||
before_action :set_web_widget
|
before_action :set_web_widget
|
||||||
before_action :ensure_account_is_active
|
before_action :ensure_account_is_active
|
||||||
|
before_action :ensure_location_is_supported
|
||||||
before_action :set_token
|
before_action :set_token
|
||||||
before_action :set_contact
|
before_action :set_contact
|
||||||
before_action :build_contact
|
before_action :build_contact
|
||||||
@ -18,6 +19,9 @@ class WidgetsController < ActionController::Base
|
|||||||
|
|
||||||
def set_web_widget
|
def set_web_widget
|
||||||
@web_widget = ::Channel::WebWidget.find_by!(website_token: permitted_params[:website_token])
|
@web_widget = ::Channel::WebWidget.find_by!(website_token: permitted_params[:website_token])
|
||||||
|
rescue ActiveRecord::RecordNotFound
|
||||||
|
Rails.logger.error('web widget does not exist')
|
||||||
|
render json: { error: 'web widget does not exist' }, status: :not_found
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_token
|
def set_token
|
||||||
@ -51,6 +55,8 @@ class WidgetsController < ActionController::Base
|
|||||||
render json: { error: 'Account is suspended' }, status: :unauthorized unless @web_widget.inbox.account.active?
|
render json: { error: 'Account is suspended' }, status: :unauthorized unless @web_widget.inbox.account.active?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def ensure_location_is_supported; end
|
||||||
|
|
||||||
def additional_attributes
|
def additional_attributes
|
||||||
if @web_widget.inbox.account.feature_enabled?('ip_lookup')
|
if @web_widget.inbox.account.feature_enabled?('ip_lookup')
|
||||||
{ created_at_ip: request.remote_ip }
|
{ created_at_ip: request.remote_ip }
|
||||||
@ -67,3 +73,5 @@ class WidgetsController < ActionController::Base
|
|||||||
response.headers.delete('X-Frame-Options')
|
response.headers.delete('X-Frame-Options')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
WidgetsController.prepend_mod_with('WidgetsController')
|
||||||
|
|||||||
@ -10,7 +10,7 @@ class AccessTokenDashboard < Administrate::BaseDashboard
|
|||||||
ATTRIBUTE_TYPES = {
|
ATTRIBUTE_TYPES = {
|
||||||
owner: Field::Polymorphic,
|
owner: Field::Polymorphic,
|
||||||
id: Field::Number,
|
id: Field::Number,
|
||||||
token: Field::String,
|
token: SecretField,
|
||||||
created_at: Field::DateTime,
|
created_at: Field::DateTime,
|
||||||
updated_at: Field::DateTime
|
updated_at: Field::DateTime
|
||||||
}.freeze
|
}.freeze
|
||||||
|
|||||||
4
app/fields/secret_field.rb
Normal file
4
app/fields/secret_field.rb
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
require 'administrate/field/base'
|
||||||
|
|
||||||
|
class SecretField < Administrate::Field::String
|
||||||
|
end
|
||||||
@ -1,4 +1,6 @@
|
|||||||
class EmailChannelFinder
|
class EmailChannelFinder
|
||||||
|
include EmailHelper
|
||||||
|
|
||||||
def initialize(email_object)
|
def initialize(email_object)
|
||||||
@email_object = email_object
|
@email_object = email_object
|
||||||
end
|
end
|
||||||
@ -7,7 +9,8 @@ class EmailChannelFinder
|
|||||||
channel = nil
|
channel = nil
|
||||||
recipient_mails = @email_object.to.to_a + @email_object.cc.to_a
|
recipient_mails = @email_object.to.to_a + @email_object.cc.to_a
|
||||||
recipient_mails.each do |email|
|
recipient_mails.each do |email|
|
||||||
channel = Channel::Email.find_by('lower(email) = ? OR lower(forward_to_email) = ?', email.downcase, email.downcase)
|
normalized_email = normalize_email_with_plus_addressing(email)
|
||||||
|
channel = Channel::Email.find_by('lower(email) = ? OR lower(forward_to_email) = ?', normalized_email, normalized_email)
|
||||||
break if channel.present?
|
break if channel.present?
|
||||||
end
|
end
|
||||||
channel
|
channel
|
||||||
|
|||||||
@ -3,4 +3,11 @@ module EmailHelper
|
|||||||
domain = email.split('@').last
|
domain = email.split('@').last
|
||||||
domain.split('.').first
|
domain.split('.').first
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# ref: https://www.rfc-editor.org/rfc/rfc5233.html
|
||||||
|
# This is not a mandatory requirement for email addresses, but it is a common practice.
|
||||||
|
# john+test@xyc.com is the same as john@xyc.com
|
||||||
|
def normalize_email_with_plus_addressing(email)
|
||||||
|
"#{email.split('@').first.split('+').first}@#{email.split('@').last}".downcase
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -17,6 +17,15 @@ module ReportingEventHelper
|
|||||||
from_in_inbox_timezone.working_time_until(to_in_inbox_timezone)
|
from_in_inbox_timezone.working_time_until(to_in_inbox_timezone)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def last_non_human_activity(conversation)
|
||||||
|
# check if a handoff event already exists
|
||||||
|
handoff_event = ReportingEvent.where(conversation_id: conversation.id, name: 'conversation_bot_handoff').last
|
||||||
|
|
||||||
|
# if a handoff exists, last non human activity is when the handoff ended,
|
||||||
|
# otherwise it's when the conversation was created
|
||||||
|
handoff_event&.event_end_time || conversation.created_at
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def configure_working_hours(working_hours)
|
def configure_working_hours(working_hours)
|
||||||
|
|||||||
@ -1,5 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-if="!authUIFlags.isFetching" id="app" class="app-wrapper app-root">
|
<div
|
||||||
|
v-if="!authUIFlags.isFetching"
|
||||||
|
id="app"
|
||||||
|
class="app-wrapper app-root"
|
||||||
|
:class="{ 'app-rtl--wrapper': isRTLView }"
|
||||||
|
>
|
||||||
<update-banner :latest-chatwoot-version="latestChatwootVersion" />
|
<update-banner :latest-chatwoot-version="latestChatwootVersion" />
|
||||||
<transition name="fade" mode="out-in">
|
<transition name="fade" mode="out-in">
|
||||||
<router-view />
|
<router-view />
|
||||||
@ -22,6 +27,7 @@ import NetworkNotification from './components/NetworkNotification';
|
|||||||
import UpdateBanner from './components/app/UpdateBanner.vue';
|
import UpdateBanner from './components/app/UpdateBanner.vue';
|
||||||
import vueActionCable from './helper/actionCable';
|
import vueActionCable from './helper/actionCable';
|
||||||
import WootSnackbarBox from './components/SnackbarContainer';
|
import WootSnackbarBox from './components/SnackbarContainer';
|
||||||
|
import rtlMixin from 'shared/mixins/rtlMixin';
|
||||||
import {
|
import {
|
||||||
registerSubscription,
|
registerSubscription,
|
||||||
verifyServiceWorkerExistence,
|
verifyServiceWorkerExistence,
|
||||||
@ -38,6 +44,8 @@ export default {
|
|||||||
WootSnackbarBox,
|
WootSnackbarBox,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
mixins: [rtlMixin],
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
showAddAccountModal: false,
|
showAddAccountModal: false,
|
||||||
@ -96,6 +104,7 @@ export default {
|
|||||||
} = this.getAccount(this.currentAccountId);
|
} = 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.latestChatwootVersion = latestChatwootVersion;
|
this.latestChatwootVersion = latestChatwootVersion;
|
||||||
vueActionCable.init(pubsubToken);
|
vueActionCable.init(pubsubToken);
|
||||||
},
|
},
|
||||||
|
|||||||
@ -8,20 +8,20 @@ class ReportsAPI extends ApiClient {
|
|||||||
super('reports', { accountScoped: true, apiVersion: 'v2' });
|
super('reports', { accountScoped: true, apiVersion: 'v2' });
|
||||||
}
|
}
|
||||||
|
|
||||||
getReports(
|
getReports({
|
||||||
metric,
|
metric,
|
||||||
since,
|
from,
|
||||||
until,
|
to,
|
||||||
type = 'account',
|
type = 'account',
|
||||||
id,
|
id,
|
||||||
group_by,
|
group_by,
|
||||||
business_hours
|
business_hours,
|
||||||
) {
|
}) {
|
||||||
return axios.get(`${this.url}`, {
|
return axios.get(`${this.url}`, {
|
||||||
params: {
|
params: {
|
||||||
metric,
|
metric,
|
||||||
since,
|
since: from,
|
||||||
until,
|
until: to,
|
||||||
type,
|
type,
|
||||||
id,
|
id,
|
||||||
group_by,
|
group_by,
|
||||||
|
|||||||
18
app/javascript/dashboard/api/search.js
Normal file
18
app/javascript/dashboard/api/search.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
/* global axios */
|
||||||
|
import ApiClient from './ApiClient';
|
||||||
|
|
||||||
|
class SearchAPI extends ApiClient {
|
||||||
|
constructor() {
|
||||||
|
super('search', { accountScoped: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
get({ q }) {
|
||||||
|
return axios.get(this.url, {
|
||||||
|
params: {
|
||||||
|
q,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new SearchAPI();
|
||||||
@ -20,7 +20,11 @@ describe('#Reports API', () => {
|
|||||||
});
|
});
|
||||||
describeWithAPIMock('API calls', context => {
|
describeWithAPIMock('API calls', context => {
|
||||||
it('#getAccountReports', () => {
|
it('#getAccountReports', () => {
|
||||||
reportsAPI.getReports('conversations_count', 1621103400, 1621621800);
|
reportsAPI.getReports({
|
||||||
|
metric: 'conversations_count',
|
||||||
|
from: 1621103400,
|
||||||
|
to: 1621621800,
|
||||||
|
});
|
||||||
expect(context.axiosMock.get).toHaveBeenCalledWith('/api/v2/reports', {
|
expect(context.axiosMock.get).toHaveBeenCalledWith('/api/v2/reports', {
|
||||||
params: {
|
params: {
|
||||||
metric: 'conversations_count',
|
metric: 'conversations_count',
|
||||||
|
|||||||
@ -14,11 +14,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.mx-input {
|
.mx-input {
|
||||||
border: 1px solid var(--color-border);
|
border: 1px solid var(--s-200);
|
||||||
border-radius: var(--border-radius-normal);
|
border-radius: var(--border-radius-normal);
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
display: flex;
|
display: flex;
|
||||||
height: 4.6rem;
|
height: 4.0rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx-input:disabled,
|
.mx-input:disabled,
|
||||||
|
|||||||
@ -7,6 +7,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
height: 4.0rem;
|
||||||
|
}
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
margin-bottom: var(--space-small);
|
margin-bottom: var(--space-small);
|
||||||
padding: var(--space-normal);
|
padding: var(--space-normal);
|
||||||
@ -64,7 +68,3 @@ code {
|
|||||||
.padding-right-small {
|
.padding-right-small {
|
||||||
padding-right: var(--space-one);
|
padding-right: var(--space-one);
|
||||||
}
|
}
|
||||||
|
|
||||||
.margin-right-small {
|
|
||||||
margin-right: var(--space-small);
|
|
||||||
}
|
|
||||||
|
|||||||
@ -16,12 +16,12 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.banner + .app-wrapper {
|
.banner + .app-wrapper {
|
||||||
.button--fixed-right-top {
|
.button--fixed-top {
|
||||||
top: 5.6 * $space-one;
|
top: 5.6 * $space-one;
|
||||||
}
|
}
|
||||||
|
|
||||||
.off-canvas-content {
|
.off-canvas-content {
|
||||||
.button--fixed-right-top {
|
.button--fixed-top {
|
||||||
top: $space-small;
|
top: $space-small;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
499
app/javascript/dashboard/assets/scss/_rtl.scss
Normal file
499
app/javascript/dashboard/assets/scss/_rtl.scss
Normal file
@ -0,0 +1,499 @@
|
|||||||
|
.app-rtl--wrapper {
|
||||||
|
direction: rtl;
|
||||||
|
|
||||||
|
// Primary sidebar
|
||||||
|
.primary--sidebar {
|
||||||
|
border-left: 1px solid var(--s-50);
|
||||||
|
border-right: 0;
|
||||||
|
|
||||||
|
.options-menu.dropdown-pane {
|
||||||
|
right: var(--space-smaller);
|
||||||
|
|
||||||
|
.auto-offline--toggle {
|
||||||
|
padding: var(--space-smaller) var(--space-one) var(--space-smaller)
|
||||||
|
var(--space-smaller);
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-items .button {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Secondary sidebar
|
||||||
|
.secondary-sidebar {
|
||||||
|
.secondary-menu {
|
||||||
|
border-left: 1px solid var(--s-50);
|
||||||
|
border-right: 0;
|
||||||
|
|
||||||
|
.nested.vertical.menu {
|
||||||
|
.badge--icon {
|
||||||
|
margin-left: var(--space-smaller);
|
||||||
|
margin-right: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-label {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.secondary-menu--icon {
|
||||||
|
margin-left: var(--space-smaller);
|
||||||
|
margin-right: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
.account-context--group .account-context--switch-group {
|
||||||
|
--overlay-shadow: linear-gradient(
|
||||||
|
to left,
|
||||||
|
rgba(255, 255, 255, 0) 0%,
|
||||||
|
rgba(255, 255, 255, 1) 50%
|
||||||
|
);
|
||||||
|
background-image: var(--overlay-shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Help center sidebar
|
||||||
|
.sidebar-header--wrap .header-title--wrap {
|
||||||
|
margin-left: unset;
|
||||||
|
margin-right: var(--space-small);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Woot button
|
||||||
|
.button {
|
||||||
|
.icon--emoji + .button__content {
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: var(--space-small);
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon--font + .button__content {
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: var(--space-small);
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon + .button__content {
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: var(--space-small);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Settings header
|
||||||
|
.settings-header {
|
||||||
|
.header--icon {
|
||||||
|
margin-left: var(--space-small);
|
||||||
|
margin-right: var(--space-smaller);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings.back-button {
|
||||||
|
direction: initial;
|
||||||
|
margin-left: var(--space-normal);
|
||||||
|
margin-right: var(--space-smaller);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Settings header action button
|
||||||
|
.button--fixed-top {
|
||||||
|
left: $space-small;
|
||||||
|
position: fixed;
|
||||||
|
right: unset;
|
||||||
|
top: $space-small;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Woot Tabs
|
||||||
|
.tabs-title {
|
||||||
|
&:first-child {
|
||||||
|
margin-left: var(--space-small);
|
||||||
|
margin-right: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-left: unset;
|
||||||
|
margin-right: var(--space-small);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// woot tables
|
||||||
|
table,
|
||||||
|
thead,
|
||||||
|
th {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Table footer
|
||||||
|
.footer {
|
||||||
|
.page-meta {
|
||||||
|
direction: initial;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wizard box
|
||||||
|
.wizard-box {
|
||||||
|
direction: initial;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Conversation details
|
||||||
|
.conversation-details-wrap {
|
||||||
|
.conv-header {
|
||||||
|
.user {
|
||||||
|
margin-left: var(--space-normal);
|
||||||
|
margin-right: unset;
|
||||||
|
|
||||||
|
.user--profile__meta {
|
||||||
|
margin-left: unset;
|
||||||
|
margin-right: var(--space-small);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions--container .resolve-actions {
|
||||||
|
margin-left: unset;
|
||||||
|
margin-right: var(--space-small);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.conversation-panel {
|
||||||
|
// Message text
|
||||||
|
.text-content {
|
||||||
|
p {
|
||||||
|
unicode-bidi: plaintext;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
padding-left: unset;
|
||||||
|
padding-right: var(--space-two);
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Message items and actions
|
||||||
|
li {
|
||||||
|
&.right {
|
||||||
|
.sender--info {
|
||||||
|
padding: var(--space-small) var(--space-smaller)
|
||||||
|
var(--space-smaller) 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.context-menu-wrap {
|
||||||
|
margin-left: 0;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Conversation footer
|
||||||
|
.conversation-footer {
|
||||||
|
.preview-item {
|
||||||
|
direction: initial;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Custom attributes section in conversation sidebar
|
||||||
|
.conversation-sidebar-wrap .checkbox-wrap {
|
||||||
|
.checkbox {
|
||||||
|
margin-left: var(--space-small);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Conversation sidebar toggle button
|
||||||
|
.sidebar-toggle--button {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Conversation sidebar close button
|
||||||
|
.close-button--rtl {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve actions button
|
||||||
|
.resolve-actions {
|
||||||
|
.button-group .button:first-child {
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
border-bottom-right-radius: var(--border-radius-normal);
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-top-right-radius: var(--border-radius-normal);
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-group .button:last-child {
|
||||||
|
border-bottom-left-radius: var(--border-radius-normal);
|
||||||
|
border-bottom-right-radius: 0;
|
||||||
|
border-top-left-radius: var(--border-radius-normal);
|
||||||
|
border-top-right-radius: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Conversation list
|
||||||
|
.conversations-list-wrap {
|
||||||
|
border-right: 0;
|
||||||
|
|
||||||
|
.conversation {
|
||||||
|
.conversation--meta {
|
||||||
|
left: $space-normal;
|
||||||
|
right: unset;
|
||||||
|
|
||||||
|
.unread {
|
||||||
|
margin-left: unset;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.assignee-label {
|
||||||
|
margin-left: 0;
|
||||||
|
margin-right: var(--space-one);
|
||||||
|
}
|
||||||
|
|
||||||
|
.show-more--button {
|
||||||
|
margin: unset;
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-header--wrap {
|
||||||
|
.search--input {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layout-switch__container {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Card label
|
||||||
|
.label-container {
|
||||||
|
.label {
|
||||||
|
margin-left: var(--space-smaller);
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Secondary sidebar toggle button
|
||||||
|
.toggle-sidebar {
|
||||||
|
margin-left: 0;
|
||||||
|
margin-right: var(--space-minus-small);
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bulk actions
|
||||||
|
.bulk-action__container {
|
||||||
|
.triangle {
|
||||||
|
left: var(--triangle-position);
|
||||||
|
right: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bulk-action__agents {
|
||||||
|
left: var(--space-small);
|
||||||
|
right: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
.labels-container {
|
||||||
|
left: var(--space-small);
|
||||||
|
right: unset;
|
||||||
|
|
||||||
|
.label-checkbox {
|
||||||
|
margin: 0 0 0 var(--space-one);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions-container {
|
||||||
|
left: var(--space-small);
|
||||||
|
right: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bulk-action__teams {
|
||||||
|
left: var(--space-small);
|
||||||
|
right: unset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Contact notes
|
||||||
|
.card.note-wrap {
|
||||||
|
.time-stamp {
|
||||||
|
unicode-bidi: plaintext;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notification panel
|
||||||
|
.notification-wrap {
|
||||||
|
left: 0;
|
||||||
|
right: var(--space-jumbo);
|
||||||
|
|
||||||
|
.action-button {
|
||||||
|
margin-left: var(--space-small);
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-content--wrap {
|
||||||
|
margin-left: 0;
|
||||||
|
margin-right: var(--space-small);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Help center
|
||||||
|
.article-container .row--article-block {
|
||||||
|
td:last-child {
|
||||||
|
direction: initial;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// scss-lint:disable SelectorDepth
|
||||||
|
.container .header-wrap .header-left-wrap .header-left-wrap > .page-title {
|
||||||
|
margin-right: var(--space-small);
|
||||||
|
}
|
||||||
|
|
||||||
|
.portal-container .container {
|
||||||
|
margin-left: unset !important;
|
||||||
|
margin-right: var(--space-small);
|
||||||
|
|
||||||
|
.configuration-items--wrap {
|
||||||
|
margin-left: var(--space-mega);
|
||||||
|
margin-right: unset !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
thead th {
|
||||||
|
padding-left: var(--space-small);
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody td {
|
||||||
|
padding-left: var(--space-small);
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.portal-popover__container .portal {
|
||||||
|
.actions-container {
|
||||||
|
margin-left: unset;
|
||||||
|
margin-right: var(--space-one);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-article--container {
|
||||||
|
.header-right--wrap {
|
||||||
|
.button-group .button:first-child {
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
border-bottom-right-radius: var(--border-radius-normal);
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-top-right-radius: var(--border-radius-normal);
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-group .button:last-child {
|
||||||
|
border-bottom-left-radius: var(--border-radius-normal);
|
||||||
|
border-bottom-right-radius: 0;
|
||||||
|
border-top-left-radius: var(--border-radius-normal);
|
||||||
|
border-top-right-radius: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-left--wrap {
|
||||||
|
.back-button {
|
||||||
|
direction: initial;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.article--buttons {
|
||||||
|
.dropdown-pane {
|
||||||
|
left: 0;
|
||||||
|
position: absolute;
|
||||||
|
right: unset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-button {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-settings--container {
|
||||||
|
border-left: 0;
|
||||||
|
border-right: 1px solid var(--color-border-light);
|
||||||
|
flex-direction: row-reverse;
|
||||||
|
margin-left: 0;
|
||||||
|
margin-right: var(--space-normal);
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: var(--space-normal);
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-list--container .header-left--wrap {
|
||||||
|
direction: initial;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Toggle switch
|
||||||
|
.toggle-button {
|
||||||
|
&.small {
|
||||||
|
span {
|
||||||
|
&.active {
|
||||||
|
transform: translate(var(--space-minus-small), var(--space-zero));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
--minus-space-one-point-five: -1.5rem;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
transform: translate(
|
||||||
|
var(--minus-space-one-point-five),
|
||||||
|
var(--space-zero)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Widget builder
|
||||||
|
.widget-builder-container .widget-preview {
|
||||||
|
direction: initial;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modal
|
||||||
|
.modal-container {
|
||||||
|
text-align: right;
|
||||||
|
|
||||||
|
.modal-footer {
|
||||||
|
button {
|
||||||
|
margin-left: 0;
|
||||||
|
margin-right: var(--space-small);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Other changes
|
||||||
|
|
||||||
|
.account-selector--wrap {
|
||||||
|
direction: initial;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inbox--name .inbox--icon {
|
||||||
|
margin-left: var(--space-micro);
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.colorpicker--chrome {
|
||||||
|
direction: initial;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mention--box {
|
||||||
|
direction: initial;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact--details .contact--bio {
|
||||||
|
direction: ltr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.merge-contacts .child-contact-wrap {
|
||||||
|
direction: ltr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact--form .input-group {
|
||||||
|
direction: initial;
|
||||||
|
}
|
||||||
|
|
||||||
|
// scss-lint:disable QualifyingElement
|
||||||
|
.dropdown-menu--header > span.title {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,7 +1,3 @@
|
|||||||
.margin-right-small {
|
|
||||||
margin-right: var(--space-small);
|
|
||||||
}
|
|
||||||
|
|
||||||
.margin-bottom-small {
|
.margin-bottom-small {
|
||||||
margin-bottom: var(--space-small);
|
margin-bottom: var(--space-small);
|
||||||
}
|
}
|
||||||
@ -14,6 +10,10 @@
|
|||||||
margin-left: var(--space-minus-slab);
|
margin-left: var(--space-minus-slab);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.margin-right-minus-slab {
|
||||||
|
margin-right: var(--space-minus-slab);
|
||||||
|
}
|
||||||
|
|
||||||
.fs-small {
|
.fs-small {
|
||||||
font-size: var(--font-size-small);
|
font-size: var(--font-size-small);
|
||||||
}
|
}
|
||||||
@ -75,3 +75,9 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.button--fixed-top {
|
||||||
|
position: fixed;
|
||||||
|
right: var(--space-small);
|
||||||
|
top: var(--space-small);
|
||||||
|
}
|
||||||
|
|||||||
@ -41,6 +41,7 @@
|
|||||||
@import 'layout';
|
@import 'layout';
|
||||||
@import 'animations';
|
@import 'animations';
|
||||||
@import 'foundation-custom';
|
@import 'foundation-custom';
|
||||||
|
@import 'rtl';
|
||||||
|
|
||||||
@import 'widgets/buttons';
|
@import 'widgets/buttons';
|
||||||
@import 'widgets/conv-header';
|
@import 'widgets/conv-header';
|
||||||
|
|||||||
@ -48,7 +48,7 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
width: 100%;
|
width: fit-content;
|
||||||
}
|
}
|
||||||
|
|
||||||
p {
|
p {
|
||||||
@ -169,7 +169,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.multiselect-wrap--small {
|
.multiselect-wrap--small {
|
||||||
$multiselect-height: 3.8rem;
|
$multiselect-height: 4.0rem;
|
||||||
|
|
||||||
.multiselect__tags,
|
.multiselect__tags,
|
||||||
.multiselect__input {
|
.multiselect__input {
|
||||||
|
|||||||
@ -9,7 +9,6 @@
|
|||||||
.integration--image {
|
.integration--image {
|
||||||
display: flex;
|
display: flex;
|
||||||
height: 10rem;
|
height: 10rem;
|
||||||
margin-right: $space-normal;
|
|
||||||
width: 10rem;
|
width: 10rem;
|
||||||
|
|
||||||
img {
|
img {
|
||||||
@ -18,12 +17,15 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.integration--title {
|
.integration--type {
|
||||||
font-size: $font-size-large;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
margin: 0 var(--space-normal);
|
||||||
}
|
}
|
||||||
|
|
||||||
.integration--description {
|
.integration--title {
|
||||||
padding-right: $space-medium;
|
font-size: var(--font-size-large);
|
||||||
}
|
}
|
||||||
|
|
||||||
.button-wrap {
|
.button-wrap {
|
||||||
|
|||||||
@ -50,22 +50,22 @@ $default-button-height: 4.0rem;
|
|||||||
|
|
||||||
&.secondary {
|
&.secondary {
|
||||||
border-color: var(--s-200);
|
border-color: var(--s-200);
|
||||||
color: var(--s-700)
|
color: var(--s-700);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.success {
|
&.success {
|
||||||
border-color: var(--s-200);
|
border-color: var(--s-200);
|
||||||
color: var(--g-700)
|
color: var(--g-700);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.alert {
|
&.alert {
|
||||||
border-color: var(--s-200);
|
border-color: var(--s-200);
|
||||||
color: var(--r-700)
|
color: var(--r-700);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.warning {
|
&.warning {
|
||||||
border-color: var(--s-200);
|
border-color: var(--s-200);
|
||||||
color: var(--y-700)
|
color: var(--y-700);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
@ -116,19 +116,19 @@ $default-button-height: 4.0rem;
|
|||||||
color: var(--w-700);
|
color: var(--w-700);
|
||||||
|
|
||||||
&.secondary {
|
&.secondary {
|
||||||
color: var(--s-700)
|
color: var(--s-700);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.success {
|
&.success {
|
||||||
color: var(--g-700)
|
color: var(--g-700);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.alert {
|
&.alert {
|
||||||
color: var(--r-700)
|
color: var(--r-700);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.warning {
|
&.warning {
|
||||||
color: var(--y-700)
|
color: var(--y-700);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
@ -205,11 +205,3 @@ $default-button-height: 4.0rem;
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// @TDOD move to utility file
|
|
||||||
.button--fixed-right-top {
|
|
||||||
position: fixed;
|
|
||||||
right: $space-small;
|
|
||||||
top: $space-small;
|
|
||||||
}
|
|
||||||
|
|||||||
@ -47,7 +47,7 @@ $resolve-button-width: 13.2rem;
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
margin-left: $space-slab;
|
margin-left: var(--space-small);
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,18 +13,18 @@
|
|||||||
@include flex;
|
@include flex;
|
||||||
@include flex-shrink;
|
@include flex-shrink;
|
||||||
border-bottom: 1px solid transparent;
|
border-bottom: 1px solid transparent;
|
||||||
border-left: $space-micro solid transparent;
|
border-left: var(--space-micro) solid transparent;
|
||||||
border-top: 1px solid transparent;
|
border-top: 1px solid transparent;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
padding: 0 0 0 $space-normal;
|
padding: 0 var(--space-normal);
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
animation: left-shift-animation 0.25s $swift-ease-out-function;
|
animation: left-shift-animation 0.25s $swift-ease-out-function;
|
||||||
background: $color-background;
|
background: var(--color-background);
|
||||||
border-bottom-color: $color-border-light;
|
border-bottom-color: var(--color-border-light);
|
||||||
border-left-color: $color-woot;
|
border-left-color: var(--color-woot);
|
||||||
border-top-color: $color-border-light;
|
border-top-color: var(--color-border-light);
|
||||||
|
|
||||||
.conversation--details {
|
.conversation--details {
|
||||||
border-top-color: transparent;
|
border-top-color: transparent;
|
||||||
@ -43,7 +43,7 @@
|
|||||||
|
|
||||||
&:last-child {
|
&:last-child {
|
||||||
.conversation--details {
|
.conversation--details {
|
||||||
border-bottom-color: $color-border-light;
|
border-bottom-color: var(--color-border-light);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,33 +51,32 @@
|
|||||||
@include border-light-bottom;
|
@include border-light-bottom;
|
||||||
@include border-light-top;
|
@include border-light-top;
|
||||||
border-bottom-color: transparent;
|
border-bottom-color: transparent;
|
||||||
margin: 0 0 0 $space-one;
|
padding: var(--space-slab) 0;
|
||||||
padding: $space-slab 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.conversation--user {
|
.conversation--user {
|
||||||
font-size: $font-size-small;
|
font-size: var(--font-size-small);
|
||||||
margin-bottom: 0;
|
margin: 0 var(--space-small);
|
||||||
text-transform: capitalize;
|
text-transform: capitalize;
|
||||||
|
|
||||||
.label {
|
.label {
|
||||||
left: $space-micro;
|
left: var(--space-one);
|
||||||
max-width: $space-jumbo;
|
max-width: var(--space-jumbo);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: relative;
|
position: relative;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
top: $space-micro;
|
top: var(--space-one);
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.conversation--message {
|
.conversation--message {
|
||||||
color: $color-body;
|
color: var(--color-body);
|
||||||
font-size: $font-size-small;
|
font-size: var(--font-size-small);
|
||||||
font-weight: $font-weight-normal;
|
font-weight: var(--font-weight-normal);
|
||||||
height: $space-medium;
|
height: var(--space-medium);
|
||||||
line-height: $space-medium;
|
line-height: var(--space-medium);
|
||||||
margin: 0;
|
margin: 0 var(--space-small);
|
||||||
max-width: 96%;
|
max-width: 96%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
@ -89,32 +88,31 @@
|
|||||||
@include flex;
|
@include flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: $space-normal;
|
right: var(--space-normal);
|
||||||
top: $space-normal;
|
top: var(--space-normal);
|
||||||
|
|
||||||
.unread {
|
.unread {
|
||||||
$unread-size: $space-normal;
|
|
||||||
@include round-corner;
|
@include round-corner;
|
||||||
@include light-shadow;
|
@include light-shadow;
|
||||||
background: darken($success-color, 3%);
|
background: darken($success-color, 3%);
|
||||||
color: $color-white;
|
color: var(--white);
|
||||||
display: none;
|
display: none;
|
||||||
font-size: $font-size-micro;
|
font-size: var(--font-size-micro);
|
||||||
font-weight: $font-weight-black;
|
font-weight: var(--font-weight-black);
|
||||||
height: $unread-size;
|
height: var(--space-normal);
|
||||||
line-height: $unread-size;
|
line-height: var(--space-normal);
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-top: $space-smaller;
|
margin-top: var(--space-smaller);
|
||||||
min-width: $unread-size;
|
min-width: var(--space-normal);
|
||||||
padding: 0 $space-smaller;
|
padding: 0 var(--space-smaller);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.timestamp {
|
.timestamp {
|
||||||
color: $dark-gray;
|
color: $dark-gray;
|
||||||
font-size: $font-size-micro;
|
font-size: var(--font-size-micro);
|
||||||
font-weight: $font-weight-normal;
|
font-weight: var(--font-weight-normal);
|
||||||
line-height: $space-normal;
|
line-height: var(--space-normal);
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -125,11 +123,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.conversation--message {
|
.conversation--message {
|
||||||
font-weight: $font-weight-bold;
|
font-weight: var(--font-weight-bold);
|
||||||
}
|
}
|
||||||
|
|
||||||
.conversation--user {
|
.conversation--user {
|
||||||
font-weight: $font-weight-bold;
|
font-weight: var(--font-weight-bold);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -67,6 +67,7 @@
|
|||||||
|
|
||||||
.conversations-list-wrap {
|
.conversations-list-wrap {
|
||||||
@include flex;
|
@include flex;
|
||||||
|
border-right: 1px solid var(--s-50);
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
.load-more-conversations {
|
.load-more-conversations {
|
||||||
@ -81,8 +82,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.conversations-list {
|
.conversations-list {
|
||||||
overflow-y: auto;
|
|
||||||
flex: 1 1;
|
flex: 1 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
@include breakpoint(large up) {
|
@include breakpoint(large up) {
|
||||||
@include scroll-on-hover;
|
@include scroll-on-hover;
|
||||||
@ -93,22 +94,10 @@
|
|||||||
@include flex;
|
@include flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding: $zero $zero $space-micro;
|
padding: 0 var(--space-normal);
|
||||||
|
|
||||||
.page-title {
|
.page-title {
|
||||||
margin-bottom: $zero;
|
margin-bottom: 0;
|
||||||
margin-left: $space-normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status--filter {
|
|
||||||
background-color: $color-background-light;
|
|
||||||
border: 1px solid $color-border;
|
|
||||||
float: right;
|
|
||||||
font-size: $font-size-mini;
|
|
||||||
height: $space-medium;
|
|
||||||
margin: 0;
|
|
||||||
padding: $zero $space-medium $zero $space-normal;
|
|
||||||
width: auto;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -391,14 +380,6 @@
|
|||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
blockquote {
|
|
||||||
border-left-color: var(--s-300);
|
|
||||||
|
|
||||||
p {
|
|
||||||
color: var(--s-300);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p:last-child {
|
p:last-child {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
@ -419,19 +400,8 @@
|
|||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
blockquote {
|
|
||||||
border-left-color: var(--w-100);
|
|
||||||
|
|
||||||
p {
|
|
||||||
color: var(--w-100);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pre code {
|
|
||||||
background: var(--color-background);
|
|
||||||
}
|
|
||||||
|
|
||||||
p:last-child {
|
p:last-child {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -3,23 +3,23 @@
|
|||||||
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: $space-normal $space-small $space-normal $space-two;
|
padding: var(--space-normal);
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
@include custom-border-top(3px, $color-woot);
|
@include custom-border-top(3px, var(--color-woot));
|
||||||
@include background-white;
|
@include background-white;
|
||||||
.heading,
|
.heading,
|
||||||
.metric {
|
.metric {
|
||||||
color: $color-woot;
|
color: var(--color-woot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.heading {
|
.heading {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
color: $color-heading;
|
color: var(--color-heading);
|
||||||
display: flex;
|
display: flex;
|
||||||
font-size: $font-size-small;
|
font-size: var(--font-size-small);
|
||||||
font-weight: $font-weight-bold;
|
font-weight: var(--font-weight-bold);
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,19 +29,19 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.metric-wrap {
|
.metric-wrap {
|
||||||
align-items: baseline;
|
align-items: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
.metric {
|
.metric {
|
||||||
font-size: $font-size-big;
|
font-size: var(--font-size-big);
|
||||||
font-weight: $font-weight-feather;
|
font-weight: var(--font-weight-feather);
|
||||||
margin-top: $space-smaller;
|
margin-top: var(--space-smaller);
|
||||||
}
|
}
|
||||||
|
|
||||||
.metric-trend {
|
.metric-trend {
|
||||||
font-size: $font-size-small;
|
font-size: var(--font-size-small);
|
||||||
margin-left: $space-small;
|
margin: 0 var(--space-small);
|
||||||
}
|
}
|
||||||
|
|
||||||
.metric-up {
|
.metric-up {
|
||||||
@ -53,7 +53,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.desc {
|
.desc {
|
||||||
font-size: $font-size-small;
|
font-size: var(--font-size-small);
|
||||||
margin: 0;
|
margin: 0;
|
||||||
text-transform: capitalize;
|
text-transform: capitalize;
|
||||||
}
|
}
|
||||||
@ -62,8 +62,8 @@
|
|||||||
.report-bar {
|
.report-bar {
|
||||||
@include background-white;
|
@include background-white;
|
||||||
@include border-light;
|
@include border-light;
|
||||||
margin: -1px $zero;
|
margin: var(--space-minus-micro) 0;
|
||||||
padding: $space-small $space-medium;
|
padding: var(--space-small) var(--space-medium);
|
||||||
|
|
||||||
.chart-container {
|
.chart-container {
|
||||||
@include flex;
|
@include flex;
|
||||||
@ -76,12 +76,12 @@
|
|||||||
|
|
||||||
.empty-state {
|
.empty-state {
|
||||||
color: $color-gray;
|
color: $color-gray;
|
||||||
font-size: $font-size-default;
|
font-size: var(--font-size-default);
|
||||||
margin: $space-jumbo;
|
margin: var(--space-jumbo);
|
||||||
}
|
}
|
||||||
|
|
||||||
.business-hours {
|
.business-hours {
|
||||||
margin: $space-normal;
|
margin: var(--space-normal);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,3 @@
|
|||||||
.date-picker {
|
|
||||||
margin-left: var(--space-smaller);
|
|
||||||
}
|
|
||||||
|
|
||||||
.margin-left-small {
|
|
||||||
margin-left: var(--space-smaller);
|
|
||||||
}
|
|
||||||
|
|
||||||
.reports-option__rounded--item {
|
.reports-option__rounded--item {
|
||||||
border-radius: 100%;
|
border-radius: 100%;
|
||||||
height: var(--space-two);
|
height: var(--space-two);
|
||||||
@ -26,17 +18,21 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.reports-option__title {
|
||||||
|
margin: 0 var(--space-small);
|
||||||
|
}
|
||||||
|
|
||||||
.business-hours {
|
.business-hours {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: end;
|
justify-content: flex-start;
|
||||||
margin-bottom: var(--space-normal);
|
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
padding-right: var(--space-normal);
|
padding-right: var(--space-normal);
|
||||||
}
|
}
|
||||||
|
|
||||||
.business-hours-text {
|
.business-hours-text {
|
||||||
font-size: var(--font-size-small);
|
font-size: var(--font-size-small);
|
||||||
|
margin: 0 var(--space-small);
|
||||||
}
|
}
|
||||||
|
|
||||||
.switch {
|
.switch {
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
.side-menu {
|
.side-menu {
|
||||||
i {
|
i {
|
||||||
margin-right: $space-smaller;
|
margin-right: var(--space-smaller);
|
||||||
min-width: $space-two;
|
min-width: var(--space-two);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -18,14 +18,14 @@
|
|||||||
|
|
||||||
.nested {
|
.nested {
|
||||||
a {
|
a {
|
||||||
font-size: $font-size-small;
|
font-size: var(--font-size-small);
|
||||||
margin-bottom: $space-micro;
|
margin-bottom: var(--space-micro);
|
||||||
margin-top: $space-micro;
|
margin-top: var(--space-micro);
|
||||||
|
|
||||||
.inbox-icon {
|
.inbox-icon {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-right: $space-micro;
|
margin-right: var(--space-micro);
|
||||||
min-width: $space-normal;
|
min-width: var(--space-normal);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -38,7 +38,8 @@
|
|||||||
@include space-between-column;
|
@include space-between-column;
|
||||||
@include border-normal-top;
|
@include border-normal-top;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding: $space-one $space-normal $space-one $space-one;
|
padding: var(--space-one) var(--space-normal) var(--space-one)
|
||||||
|
var(--space-one);
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
@ -60,14 +61,18 @@
|
|||||||
.hamburger--menu {
|
.hamburger--menu {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: block;
|
display: block;
|
||||||
margin-right: $space-normal;
|
margin-right: var(--space-normal);
|
||||||
}
|
}
|
||||||
|
|
||||||
.header--icon {
|
.header--icon {
|
||||||
display: block;
|
display: block;
|
||||||
margin-right: $space-normal;
|
margin: 0 var(--space-small) 0 var(--space-smaller);
|
||||||
|
|
||||||
@media screen and (max-width: 1200px) {
|
@media screen and (max-width: 1200px) {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.header-title {
|
||||||
|
margin: 0 var(--space-small);
|
||||||
|
}
|
||||||
|
|||||||
@ -12,7 +12,7 @@
|
|||||||
border-top-width: 0;
|
border-top-width: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
min-width: var(--space-mega);
|
min-width: var(--space-mega);
|
||||||
padding: $zero $space-normal;
|
padding: 0 var(--space-normal);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabs--with-scroll {
|
.tabs--with-scroll {
|
||||||
@ -37,26 +37,26 @@
|
|||||||
|
|
||||||
.tabs-title {
|
.tabs-title {
|
||||||
a {
|
a {
|
||||||
font-size: $font-size-default;
|
font-size: var(--font-size-default);
|
||||||
font-weight: $font-weight-medium;
|
font-weight: var(--font-weight-medium);
|
||||||
padding-bottom: $space-slab;
|
padding-bottom: var(--space-slab);
|
||||||
padding-top: $space-slab;
|
padding-top: var(--space-slab);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabs-title {
|
.tabs-title {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
margin: $zero $space-slab;
|
margin: 0 var(--space-small);
|
||||||
|
|
||||||
.badge {
|
.badge {
|
||||||
background: $color-background;
|
background: var(--color-background);
|
||||||
border-radius: $space-small;
|
border-radius: var(--space-small);
|
||||||
color: $color-gray;
|
color: var(--color-gray);
|
||||||
font-size: $font-size-micro;
|
font-size: var(--font-size-micro);
|
||||||
font-weight: $font-weight-black;
|
font-weight: var(--font-weight-black);
|
||||||
margin-left: $space-smaller;
|
margin: 0 var(--space-smaller);
|
||||||
padding: $space-smaller;
|
padding: var(--space-smaller);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:first-child {
|
&:first-child {
|
||||||
@ -80,7 +80,7 @@
|
|||||||
color: $medium-gray;
|
color: $medium-gray;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
font-size: $font-size-small;
|
font-size: var(--font-size-small);
|
||||||
position: relative;
|
position: relative;
|
||||||
top: 1px;
|
top: 1px;
|
||||||
transition: border-color 0.15s $swift-ease-out-function;
|
transition: border-color 0.15s $swift-ease-out-function;
|
||||||
@ -88,13 +88,13 @@
|
|||||||
|
|
||||||
&.is-active {
|
&.is-active {
|
||||||
a {
|
a {
|
||||||
border-bottom-color: $color-woot;
|
border-bottom-color: var(--color-woot);
|
||||||
color: $color-woot;
|
color: var(--color-woot);
|
||||||
}
|
}
|
||||||
|
|
||||||
.badge {
|
.badge {
|
||||||
background: $color-extra-light-blue;
|
background: $color-extra-light-blue;
|
||||||
color: $color-woot;
|
color: var(--color-woot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,21 +1,22 @@
|
|||||||
table {
|
table {
|
||||||
border-spacing: 0;
|
border-spacing: 0;
|
||||||
font-size: $font-size-small;
|
font-size: var(--font-size-small);
|
||||||
|
|
||||||
thead {
|
thead {
|
||||||
th {
|
th {
|
||||||
font-weight: $font-weight-bold;
|
font-weight: var(--font-weight-bold);
|
||||||
|
text-align: left;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tbody {
|
tbody {
|
||||||
tr {
|
tr {
|
||||||
border-bottom: 1px solid $color-border-light;
|
border-bottom: 1px solid var(--color-border-light);
|
||||||
}
|
}
|
||||||
|
|
||||||
td {
|
td {
|
||||||
padding: $space-one $space-small;
|
padding: var(--space-small);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -36,7 +37,7 @@ table {
|
|||||||
|
|
||||||
.agent-name {
|
.agent-name {
|
||||||
display: block;
|
display: block;
|
||||||
font-weight: $font-weight-medium;
|
font-weight: var(--font-weight-medium);
|
||||||
text-transform: capitalize;
|
text-transform: capitalize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -53,11 +53,10 @@
|
|||||||
<woot-button
|
<woot-button
|
||||||
v-else
|
v-else
|
||||||
v-tooltip.right="$t('FILTER.TOOLTIP_LABEL')"
|
v-tooltip.right="$t('FILTER.TOOLTIP_LABEL')"
|
||||||
variant="clear"
|
variant="smooth"
|
||||||
color-scheme="secondary"
|
color-scheme="secondary"
|
||||||
icon="filter"
|
icon="filter"
|
||||||
size="small"
|
size="tiny"
|
||||||
class="btn-filter"
|
|
||||||
@click="onToggleAdvanceFiltersModal"
|
@click="onToggleAdvanceFiltersModal"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -838,10 +837,6 @@ export default {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-filter {
|
|
||||||
margin: 0 var(--space-smaller);
|
|
||||||
}
|
|
||||||
|
|
||||||
.filter__applied {
|
.filter__applied {
|
||||||
padding: 0 0 var(--space-slab) 0 !important;
|
padding: 0 0 var(--space-slab) 0 !important;
|
||||||
border-bottom: 1px solid var(--color-border);
|
border-bottom: 1px solid var(--color-border);
|
||||||
@ -850,4 +845,14 @@ export default {
|
|||||||
.delete-custom-view__button {
|
.delete-custom-view__button {
|
||||||
margin-right: var(--space-normal);
|
margin-right: var(--space-normal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tab--chat-type {
|
||||||
|
padding: 0 var(--space-normal);
|
||||||
|
|
||||||
|
::v-deep {
|
||||||
|
.tabs {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
63
app/javascript/dashboard/components/MaskedText.vue
Normal file
63
app/javascript/dashboard/components/MaskedText.vue
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
<template>
|
||||||
|
<div class="text--container">
|
||||||
|
<woot-button size="small" class=" button--text" @click="onCopy">
|
||||||
|
{{ $t('COMPONENTS.CODE.BUTTON_TEXT') }}
|
||||||
|
</woot-button>
|
||||||
|
<woot-button
|
||||||
|
variant="clear"
|
||||||
|
size="small"
|
||||||
|
class="button--visibility"
|
||||||
|
color-scheme="secondary"
|
||||||
|
:icon="masked ? 'eye-show' : 'eye-hide'"
|
||||||
|
@click.prevent="toggleMasked"
|
||||||
|
/>
|
||||||
|
<highlightjs v-if="value" :code="masked ? '•'.repeat(10) : value" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import 'highlight.js/styles/default.css';
|
||||||
|
import { copyTextToClipboard } from 'shared/helpers/clipboard';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
value: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
masked: true,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async onCopy(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
await copyTextToClipboard(this.value);
|
||||||
|
bus.$emit('newToastMessage', this.$t('COMPONENTS.CODE.COPY_SUCCESSFUL'));
|
||||||
|
},
|
||||||
|
toggleMasked() {
|
||||||
|
this.masked = !this.masked;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.text--container {
|
||||||
|
position: relative;
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
.button--text,
|
||||||
|
.button--visibility {
|
||||||
|
margin-top: 0;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button--visibility {
|
||||||
|
right: 60px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -22,7 +22,6 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.toggle-sidebar {
|
.toggle-sidebar {
|
||||||
margin-right: var(--space-small);
|
|
||||||
margin-left: var(--space-minus-small);
|
margin-left: var(--space-minus-small);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -181,7 +181,7 @@ export default {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding: var(--space-smaller) 0 var(--space-smaller) var(--space-small);
|
padding: var(--space-smaller);
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|
||||||
.info-wrap {
|
.info-wrap {
|
||||||
|
|||||||
@ -26,6 +26,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return { showSwitchButton: false };
|
return { showSwitchButton: false };
|
||||||
@ -69,7 +70,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.switch-button {
|
.switch-button {
|
||||||
margin-right: var(--space-small);
|
margin: 0 var(--space-small);
|
||||||
}
|
}
|
||||||
|
|
||||||
.account-context--switch-group {
|
.account-context--switch-group {
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
<div
|
<div
|
||||||
v-if="show"
|
v-if="show"
|
||||||
v-on-clickaway="onClickAway"
|
v-on-clickaway="onClickAway"
|
||||||
class="dropdown-pane"
|
class="options-menu dropdown-pane"
|
||||||
:class="{ 'dropdown-pane--open': show }"
|
:class="{ 'dropdown-pane--open': show }"
|
||||||
>
|
>
|
||||||
<availability-status />
|
<availability-status />
|
||||||
@ -150,10 +150,10 @@ export default {
|
|||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.dropdown-pane {
|
.options-menu.dropdown-pane {
|
||||||
left: var(--space-slab);
|
left: var(--space-slab);
|
||||||
bottom: var(--space-larger);
|
bottom: var(--space-larger);
|
||||||
min-width: 22rem;
|
min-width: var(--space-giga);
|
||||||
top: unset;
|
top: unset;
|
||||||
z-index: var(--z-index-low);
|
z-index: var(--z-index-low);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -331,11 +331,11 @@ export default {
|
|||||||
.beta {
|
.beta {
|
||||||
padding-right: var(--space-smaller) !important;
|
padding-right: var(--space-smaller) !important;
|
||||||
padding-left: var(--space-smaller) !important;
|
padding-left: var(--space-smaller) !important;
|
||||||
margin-left: var(--space-smaller) !important;
|
margin: 0 var(--space-smaller) !important;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
font-size: var(--font-size-micro);
|
font-size: var(--font-size-micro);
|
||||||
font-weight: var(--font-weight-medium);
|
font-weight: var(--font-weight-medium);
|
||||||
line-height: 18px;
|
line-height: 14px;
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
border-radius: 2em;
|
border-radius: 2em;
|
||||||
color: var(--g-800);
|
color: var(--g-800);
|
||||||
@ -348,7 +348,7 @@ export default {
|
|||||||
color: var(--s-600);
|
color: var(--s-600);
|
||||||
font-size: var(--font-size-micro);
|
font-size: var(--font-size-micro);
|
||||||
font-weight: var(--font-weight-bold);
|
font-weight: var(--font-weight-bold);
|
||||||
margin-left: var(--space-smaller);
|
margin: 0 var(--space-smaller);
|
||||||
padding: var(--space-zero) var(--space-smaller);
|
padding: var(--space-zero) var(--space-smaller);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -12,7 +12,7 @@
|
|||||||
<a v-else :href="href" :style="anchorStyle">{{ title }}</a>
|
<a v-else :href="href" :style="anchorStyle">{{ title }}</a>
|
||||||
<button
|
<button
|
||||||
v-if="showClose"
|
v-if="showClose"
|
||||||
class="label-close--button "
|
class="label-close--button"
|
||||||
:style="{ color: textColor }"
|
:style="{ color: textColor }"
|
||||||
@click="onClick"
|
@click="onClick"
|
||||||
>
|
>
|
||||||
@ -108,6 +108,7 @@ export default {
|
|||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
font-weight: var(--font-weight-medium);
|
font-weight: var(--font-weight-medium);
|
||||||
|
gap: var(--space-smaller);
|
||||||
margin-right: var(--space-smaller);
|
margin-right: var(--space-smaller);
|
||||||
margin-bottom: var(--space-smaller);
|
margin-bottom: var(--space-smaller);
|
||||||
padding: var(--space-smaller);
|
padding: var(--space-smaller);
|
||||||
@ -126,9 +127,6 @@ export default {
|
|||||||
.label--icon {
|
.label--icon {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
.label-color-dot {
|
|
||||||
margin-right: var(--space-smaller);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.small .label--icon,
|
&.small .label--icon,
|
||||||
&.small .close--icon {
|
&.small .close--icon {
|
||||||
@ -209,7 +207,6 @@ export default {
|
|||||||
.label-close--button {
|
.label-close--button {
|
||||||
color: var(--s-800);
|
color: var(--s-800);
|
||||||
margin-bottom: var(--space-minus-micro);
|
margin-bottom: var(--space-minus-micro);
|
||||||
margin-left: var(--space-smaller);
|
|
||||||
border-radius: var(--border-radius-small);
|
border-radius: var(--border-radius-small);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
@ -232,7 +229,6 @@ export default {
|
|||||||
width: var(--space-slab);
|
width: var(--space-slab);
|
||||||
height: var(--space-slab);
|
height: var(--space-slab);
|
||||||
border-radius: var(--border-radius-small);
|
border-radius: var(--border-radius-small);
|
||||||
margin-right: var(--space-smaller);
|
|
||||||
box-shadow: var(--shadow-small);
|
box-shadow: var(--shadow-small);
|
||||||
}
|
}
|
||||||
.label.small .label-color-dot {
|
.label.small .label-color-dot {
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<button class="back-button" @click.capture="goBack">
|
<button class="settings back-button" @click.capture="goBack">
|
||||||
<fluent-icon icon="chevron-left" />
|
<fluent-icon icon="chevron-left" />
|
||||||
{{ buttonLabel || $t('GENERAL_SETTINGS.BACK') }}
|
{{ buttonLabel || $t('GENERAL_SETTINGS.BACK') }}
|
||||||
</button>
|
</button>
|
||||||
@ -29,4 +29,3 @@ export default {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<style scoped></style>
|
|
||||||
|
|||||||
@ -25,13 +25,15 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.inbox--name {
|
.inbox--name {
|
||||||
|
align-items: center;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
padding: var(--space-micro) 0;
|
padding: var(--space-micro) 0;
|
||||||
line-height: var(--space-slab);
|
line-height: var(--space-slab);
|
||||||
font-weight: var(--font-weight-medium);
|
font-weight: var(--font-weight-medium);
|
||||||
background: none;
|
background: none;
|
||||||
color: var(--s-500);
|
color: var(--s-600);
|
||||||
font-size: var(--font-size-mini);
|
font-size: var(--font-size-mini);
|
||||||
|
margin: 0 var(--space-one);
|
||||||
}
|
}
|
||||||
|
|
||||||
.inbox--icon {
|
.inbox--icon {
|
||||||
|
|||||||
@ -24,7 +24,7 @@
|
|||||||
<fluent-icon
|
<fluent-icon
|
||||||
icon="chevron-left"
|
icon="chevron-left"
|
||||||
size="18"
|
size="18"
|
||||||
class="margin-left-minus-slab"
|
:class="pageFooterIconClass"
|
||||||
/>
|
/>
|
||||||
</woot-button>
|
</woot-button>
|
||||||
<woot-button
|
<woot-button
|
||||||
@ -65,7 +65,7 @@
|
|||||||
<fluent-icon
|
<fluent-icon
|
||||||
icon="chevron-right"
|
icon="chevron-right"
|
||||||
size="18"
|
size="18"
|
||||||
class="margin-left-minus-slab"
|
:class="pageFooterIconClass"
|
||||||
/>
|
/>
|
||||||
</woot-button>
|
</woot-button>
|
||||||
</div>
|
</div>
|
||||||
@ -74,8 +74,11 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import rtlMixin from 'shared/mixins/rtlMixin';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {},
|
components: {},
|
||||||
|
mixins: [rtlMixin],
|
||||||
props: {
|
props: {
|
||||||
currentPage: {
|
currentPage: {
|
||||||
type: Number,
|
type: Number,
|
||||||
@ -91,6 +94,11 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
pageFooterIconClass() {
|
||||||
|
return this.isRTLView
|
||||||
|
? 'margin-right-minus-slab'
|
||||||
|
: 'margin-left-minus-slab';
|
||||||
|
},
|
||||||
isFooterVisible() {
|
isFooterVisible() {
|
||||||
return this.totalCount && !(this.firstIndex > this.totalCount);
|
return this.totalCount && !(this.firstIndex > this.totalCount);
|
||||||
},
|
},
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
:username="user.name"
|
:username="user.name"
|
||||||
:status="user.availability_status"
|
:status="user.availability_status"
|
||||||
/>
|
/>
|
||||||
<h6 class="text-block-title text-truncate text-capitalize">
|
<h6 class="text-block-title user-name text-truncate text-capitalize">
|
||||||
{{ user.name }}
|
{{ user.name }}
|
||||||
</h6>
|
</h6>
|
||||||
</div>
|
</div>
|
||||||
@ -38,11 +38,7 @@ export default {
|
|||||||
text-align: left;
|
text-align: left;
|
||||||
|
|
||||||
.user-name {
|
.user-name {
|
||||||
margin: 0;
|
margin: 0 var(--space-small);
|
||||||
text-transform: capitalize;
|
|
||||||
}
|
|
||||||
.user-thumbnail-box {
|
|
||||||
margin-right: var(--space-small);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -44,3 +44,14 @@ export default {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.status--filter {
|
||||||
|
background-color: var(--color-background-light);
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
font-size: var(--font-size-mini);
|
||||||
|
height: var(--space-medium);
|
||||||
|
margin: 0 var(--space-smaller);
|
||||||
|
padding: 0 var(--space-medium) 0 var(--space-small);
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="conversation-details-wrap"
|
class="conversation-details-wrap"
|
||||||
:class="{ 'with-border-left': !isOnExpandedLayout }"
|
:class="{ 'with-border-right': !isOnExpandedLayout }"
|
||||||
>
|
>
|
||||||
<conversation-header
|
<conversation-header
|
||||||
v-if="currentChat.id"
|
v-if="currentChat.id"
|
||||||
@ -144,8 +144,8 @@ export default {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
background: var(--color-background-light);
|
background: var(--color-background-light);
|
||||||
|
|
||||||
&.with-border-left {
|
&.with-border-right {
|
||||||
border-left: 1px solid var(--color-border);
|
border-right: 1px solid var(--color-border);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,6 +164,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.conversation-sidebar-wrap {
|
.conversation-sidebar-wrap {
|
||||||
|
border-right: 1px solid var(--color-border);
|
||||||
height: auto;
|
height: auto;
|
||||||
flex: 0 0;
|
flex: 0 0;
|
||||||
z-index: var(--z-index-low);
|
z-index: var(--z-index-low);
|
||||||
|
|||||||
@ -419,7 +419,6 @@ export default {
|
|||||||
.conversation--metadata {
|
.conversation--metadata {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding-right: var(--space-normal);
|
|
||||||
|
|
||||||
.label {
|
.label {
|
||||||
background: none;
|
background: none;
|
||||||
@ -432,6 +431,7 @@ export default {
|
|||||||
|
|
||||||
.assignee-label {
|
.assignee-label {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
|
margin-left: var(--space-small);
|
||||||
max-width: 50%;
|
max-width: 50%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,30 +10,30 @@
|
|||||||
:status="currentContact.availability_status"
|
:status="currentContact.availability_status"
|
||||||
/>
|
/>
|
||||||
<div class="user--profile__meta">
|
<div class="user--profile__meta">
|
||||||
<h3 class="user--name text-truncate">
|
<woot-button
|
||||||
<span class="margin-right-smaller">{{ currentContact.name }}</span>
|
variant="link"
|
||||||
<fluent-icon
|
color-scheme="secondary"
|
||||||
v-if="!isHMACVerified"
|
class="text-truncate"
|
||||||
v-tooltip="$t('CONVERSATION.UNVERIFIED_SESSION')"
|
@click.prevent="$emit('contact-panel-toggle')"
|
||||||
size="14"
|
>
|
||||||
class="hmac-warning__icon"
|
<h3 class="sub-block-title user--name text-truncate">
|
||||||
icon="warning"
|
<span>{{ currentContact.name }}</span>
|
||||||
/>
|
<fluent-icon
|
||||||
</h3>
|
v-if="!isHMACVerified"
|
||||||
|
v-tooltip="$t('CONVERSATION.UNVERIFIED_SESSION')"
|
||||||
|
size="14"
|
||||||
|
class="hmac-warning__icon"
|
||||||
|
icon="warning"
|
||||||
|
/>
|
||||||
|
</h3>
|
||||||
|
</woot-button>
|
||||||
<div class="conversation--header--actions text-truncate">
|
<div class="conversation--header--actions text-truncate">
|
||||||
<inbox-name
|
<inbox-name v-if="hasMultipleInboxes" :inbox="inbox" />
|
||||||
v-if="hasMultipleInboxes"
|
<span v-if="isSnoozed" class="snoozed--display-text">
|
||||||
:inbox="inbox"
|
|
||||||
class="margin-right-small"
|
|
||||||
/>
|
|
||||||
<span
|
|
||||||
v-if="isSnoozed"
|
|
||||||
class="snoozed--display-text margin-right-small"
|
|
||||||
>
|
|
||||||
{{ snoozedDisplayText }}
|
{{ snoozedDisplayText }}
|
||||||
</span>
|
</span>
|
||||||
<woot-button
|
<woot-button
|
||||||
class="user--profile__button margin-right-small"
|
class="user--profile__button"
|
||||||
size="small"
|
size="small"
|
||||||
variant="link"
|
variant="link"
|
||||||
@click="$emit('contact-panel-toggle')"
|
@click="$emit('contact-panel-toggle')"
|
||||||
@ -204,17 +204,21 @@ export default {
|
|||||||
|
|
||||||
.user--name {
|
.user--name {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
font-size: var(--font-size-medium);
|
line-height: 1.2;
|
||||||
line-height: 1.3;
|
|
||||||
margin: 0;
|
|
||||||
text-transform: capitalize;
|
text-transform: capitalize;
|
||||||
width: 100%;
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.conversation--header--actions {
|
.conversation--header--actions {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
font-size: var(--font-size-mini);
|
font-size: var(--font-size-mini);
|
||||||
|
gap: var(--space-small);
|
||||||
|
|
||||||
|
::v-deep .inbox--name {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.user--profile__button {
|
.user--profile__button {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
@ -228,5 +232,6 @@ export default {
|
|||||||
|
|
||||||
.hmac-warning__icon {
|
.hmac-warning__icon {
|
||||||
color: var(--y-600);
|
color: var(--y-600);
|
||||||
|
margin: 0 var(--space-micro);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -243,24 +243,20 @@ export default {
|
|||||||
return this.contentAttributes.translations || {};
|
return this.contentAttributes.translations || {};
|
||||||
},
|
},
|
||||||
displayQuotedButton() {
|
displayQuotedButton() {
|
||||||
if (!this.isIncoming) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.emailMessageContent.includes('<blockquote')) {
|
if (this.emailMessageContent.includes('<blockquote')) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!this.isIncoming) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
translationsAvailable() {
|
translationsAvailable() {
|
||||||
return !!Object.keys(this.translations).length;
|
return !!Object.keys(this.translations).length;
|
||||||
},
|
},
|
||||||
message() {
|
message() {
|
||||||
if (this.contentType === 'input_csat') {
|
|
||||||
return this.$t('CONVERSATION.CSAT_REPLY_MESSAGE');
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the message is an email, emailMessageContent would be present
|
// If the message is an email, emailMessageContent would be present
|
||||||
// In that case, we would use letter package to render the email
|
// In that case, we would use letter package to render the email
|
||||||
if (this.emailMessageContent && this.isIncoming) {
|
if (this.emailMessageContent && this.isIncoming) {
|
||||||
@ -278,6 +274,11 @@ export default {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (this.contentType === 'input_csat') {
|
||||||
|
return this.$t('CONVERSATION.CSAT_REPLY_MESSAGE') + botMessageContent;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
this.formatMessage(
|
this.formatMessage(
|
||||||
this.data.content,
|
this.data.content,
|
||||||
@ -659,4 +660,71 @@ li.right {
|
|||||||
.context-menu {
|
.context-menu {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Markdown styling */
|
||||||
|
|
||||||
|
.bubble .text-content {
|
||||||
|
p code {
|
||||||
|
background-color: var(--s-75);
|
||||||
|
display: inline-block;
|
||||||
|
line-height: 1;
|
||||||
|
|
||||||
|
border-radius: var(--border-radius-small);
|
||||||
|
padding: var(--space-smaller);
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
background-color: var(--s-75);
|
||||||
|
border-color: var(--s-75);
|
||||||
|
color: var(--s-800);
|
||||||
|
border-radius: var(--border-radius-normal);
|
||||||
|
padding: var(--space-small);
|
||||||
|
margin-top: var(--space-smaller);
|
||||||
|
margin-bottom: var(--space-small);
|
||||||
|
display: block;
|
||||||
|
line-height: 1.7;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
|
||||||
|
code {
|
||||||
|
background-color: transparent;
|
||||||
|
color: var(--s-800);
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote {
|
||||||
|
border-left: var(--space-micro) solid var(--s-75);
|
||||||
|
color: var(--s-800);
|
||||||
|
padding: var(--space-smaller) var(--space-small);
|
||||||
|
margin: var(--space-smaller) 0;
|
||||||
|
padding: var(--space-small) var(--space-small) 0 var(--space-normal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.right .bubble .text-content {
|
||||||
|
p code {
|
||||||
|
background-color: var(--w-600);
|
||||||
|
color: var(--white);
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
background-color: var(--w-800);
|
||||||
|
border-color: var(--w-700);
|
||||||
|
color: var(--white);
|
||||||
|
|
||||||
|
code {
|
||||||
|
background-color: transparent;
|
||||||
|
color: var(--white);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote {
|
||||||
|
border-left: var(--space-micro) solid var(--w-400);
|
||||||
|
color: var(--white);
|
||||||
|
|
||||||
|
p {
|
||||||
|
color: var(--w-75);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -27,7 +27,7 @@
|
|||||||
<emoji-input
|
<emoji-input
|
||||||
v-if="showEmojiPicker"
|
v-if="showEmojiPicker"
|
||||||
v-on-clickaway="hideEmojiPicker"
|
v-on-clickaway="hideEmojiPicker"
|
||||||
:class="emojiDialogClassOnExpanedLayout"
|
:class="emojiDialogClassOnExpandedLayoutAndRTLView"
|
||||||
:on-click="emojiOnClick"
|
:on-click="emojiOnClick"
|
||||||
/>
|
/>
|
||||||
<reply-email-head
|
<reply-email-head
|
||||||
@ -179,6 +179,7 @@ import { trimContent, debounce } from '@chatwoot/utils';
|
|||||||
import wootConstants from 'dashboard/constants';
|
import wootConstants from 'dashboard/constants';
|
||||||
import { isEditorHotKeyEnabled } from 'dashboard/mixins/uiSettings';
|
import { isEditorHotKeyEnabled } from 'dashboard/mixins/uiSettings';
|
||||||
import { CONVERSATION_EVENTS } from '../../../helper/AnalyticsHelper/events';
|
import { CONVERSATION_EVENTS } from '../../../helper/AnalyticsHelper/events';
|
||||||
|
import rtlMixin from 'shared/mixins/rtlMixin';
|
||||||
|
|
||||||
const EmojiInput = () => import('shared/components/emoji/EmojiInput');
|
const EmojiInput = () => import('shared/components/emoji/EmojiInput');
|
||||||
|
|
||||||
@ -202,6 +203,7 @@ export default {
|
|||||||
uiSettingsMixin,
|
uiSettingsMixin,
|
||||||
alertMixin,
|
alertMixin,
|
||||||
messageFormatterMixin,
|
messageFormatterMixin,
|
||||||
|
rtlMixin,
|
||||||
],
|
],
|
||||||
props: {
|
props: {
|
||||||
selectedTweet: {
|
selectedTweet: {
|
||||||
@ -422,10 +424,14 @@ export default {
|
|||||||
} = this.uiSettings;
|
} = this.uiSettings;
|
||||||
return conversationDisplayType !== CONDENSED;
|
return conversationDisplayType !== CONDENSED;
|
||||||
},
|
},
|
||||||
emojiDialogClassOnExpanedLayout() {
|
emojiDialogClassOnExpandedLayoutAndRTLView() {
|
||||||
return this.isOnExpandedLayout || this.popoutReplyBox
|
if (this.isOnExpandedLayout || this.popoutReplyBox) {
|
||||||
? 'emoji-dialog--expanded'
|
return 'emoji-dialog--expanded';
|
||||||
: '';
|
}
|
||||||
|
if (this.isRTLView) {
|
||||||
|
return 'emoji-dialog--rtl';
|
||||||
|
}
|
||||||
|
return '';
|
||||||
},
|
},
|
||||||
replyToUserLength() {
|
replyToUserLength() {
|
||||||
const selectedTweet = this.selectedTweet || {};
|
const selectedTweet = this.selectedTweet || {};
|
||||||
@ -881,6 +887,11 @@ export default {
|
|||||||
toggleTyping(status) {
|
toggleTyping(status) {
|
||||||
const conversationId = this.currentChat.id;
|
const conversationId = this.currentChat.id;
|
||||||
const isPrivate = this.isPrivate;
|
const isPrivate = this.isPrivate;
|
||||||
|
|
||||||
|
if (!conversationId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.$store.dispatch('conversationTypingStatus/toggleTyping', {
|
this.$store.dispatch('conversationTypingStatus/toggleTyping', {
|
||||||
status,
|
status,
|
||||||
conversationId,
|
conversationId,
|
||||||
@ -968,15 +979,9 @@ export default {
|
|||||||
},
|
},
|
||||||
getMessagePayloadForWhatsapp(message) {
|
getMessagePayloadForWhatsapp(message) {
|
||||||
const multipleMessagePayload = [];
|
const multipleMessagePayload = [];
|
||||||
const messagePayload = {
|
|
||||||
conversationId: this.currentChat.id,
|
|
||||||
message,
|
|
||||||
private: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
multipleMessagePayload.push(messagePayload);
|
|
||||||
|
|
||||||
if (this.attachedFiles && this.attachedFiles.length) {
|
if (this.attachedFiles && this.attachedFiles.length) {
|
||||||
|
let caption = message;
|
||||||
this.attachedFiles.forEach(attachment => {
|
this.attachedFiles.forEach(attachment => {
|
||||||
const attachedFile = this.globalConfig.directUploadsEnabled
|
const attachedFile = this.globalConfig.directUploadsEnabled
|
||||||
? attachment.blobSignedId
|
? attachment.blobSignedId
|
||||||
@ -985,10 +990,18 @@ export default {
|
|||||||
conversationId: this.currentChat.id,
|
conversationId: this.currentChat.id,
|
||||||
files: [attachedFile],
|
files: [attachedFile],
|
||||||
private: false,
|
private: false,
|
||||||
message: '',
|
message: caption,
|
||||||
};
|
};
|
||||||
multipleMessagePayload.push(attachmentPayload);
|
multipleMessagePayload.push(attachmentPayload);
|
||||||
|
caption = '';
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
const messagePayload = {
|
||||||
|
conversationId: this.currentChat.id,
|
||||||
|
message,
|
||||||
|
private: false,
|
||||||
|
};
|
||||||
|
multipleMessagePayload.push(messagePayload);
|
||||||
}
|
}
|
||||||
|
|
||||||
return multipleMessagePayload;
|
return multipleMessagePayload;
|
||||||
@ -1096,7 +1109,7 @@ export default {
|
|||||||
|
|
||||||
.emoji-dialog {
|
.emoji-dialog {
|
||||||
top: unset;
|
top: unset;
|
||||||
bottom: var(--space-normal);
|
bottom: -40px;
|
||||||
left: -320px;
|
left: -320px;
|
||||||
right: unset;
|
right: unset;
|
||||||
|
|
||||||
@ -1107,6 +1120,19 @@ export default {
|
|||||||
filter: drop-shadow(0px 4px 4px rgba(0, 0, 0, 0.08));
|
filter: drop-shadow(0px 4px 4px rgba(0, 0, 0, 0.08));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.emoji-dialog--rtl {
|
||||||
|
left: unset;
|
||||||
|
right: -320px;
|
||||||
|
&::before {
|
||||||
|
left: var(--space-minus-normal);
|
||||||
|
transform: rotate(90deg);
|
||||||
|
right: 0;
|
||||||
|
bottom: var(--space-small);
|
||||||
|
filter: drop-shadow(0px 4px 4px rgba(0, 0, 0, 0.08));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.emoji-dialog--expanded {
|
.emoji-dialog--expanded {
|
||||||
left: unset;
|
left: unset;
|
||||||
bottom: var(--space-jumbo);
|
bottom: var(--space-jumbo);
|
||||||
|
|||||||
@ -739,4 +739,9 @@ export const getLanguageName = (languageCode = '') => {
|
|||||||
return languageObj.name || '';
|
return languageObj.name || '';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getLanguageDirection = (languageCode = '') => {
|
||||||
|
const rtlLanguageIds = ['ar', 'as', 'fa', 'he', 'ku', 'ur'];
|
||||||
|
return rtlLanguageIds.includes(languageCode);
|
||||||
|
};
|
||||||
|
|
||||||
export default languages;
|
export default languages;
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { getLanguageName } from '../languages';
|
import { getLanguageName, getLanguageDirection } from '../languages';
|
||||||
|
|
||||||
describe('#getLanguageName', () => {
|
describe('#getLanguageName', () => {
|
||||||
it('Returns correct language name', () => {
|
it('Returns correct language name', () => {
|
||||||
@ -8,3 +8,10 @@ describe('#getLanguageName', () => {
|
|||||||
expect(getLanguageName('')).toEqual('');
|
expect(getLanguageName('')).toEqual('');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('#getLanguageDirection', () => {
|
||||||
|
it('Returns correct language direction', () => {
|
||||||
|
expect(getLanguageDirection('es')).toEqual(false);
|
||||||
|
expect(getLanguageDirection('ar')).toEqual(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@ -2,14 +2,14 @@
|
|||||||
<div
|
<div
|
||||||
class="message-text__wrap"
|
class="message-text__wrap"
|
||||||
:class="{
|
:class="{
|
||||||
'show--quoted': showQuotedContent,
|
'show--quoted': isQuotedContentPresent,
|
||||||
'hide--quoted': !showQuotedContent,
|
'hide--quoted': !isQuotedContentPresent,
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<div v-if="!isEmail" v-dompurify-html="message" class="text-content" />
|
<div v-if="!isEmail" v-dompurify-html="message" class="text-content" />
|
||||||
<letter v-else class="text-content" :html="message" />
|
<letter v-else class="text-content" :html="message" />
|
||||||
<button
|
<button
|
||||||
v-if="displayQuotedButton"
|
v-if="showQuoteToggle"
|
||||||
class="quoted-text--button"
|
class="quoted-text--button"
|
||||||
@click="toggleQuotedContent"
|
@click="toggleQuotedContent"
|
||||||
>
|
>
|
||||||
@ -49,6 +49,20 @@ export default {
|
|||||||
showQuotedContent: false,
|
showQuotedContent: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
isQuotedContentPresent() {
|
||||||
|
if (!this.isEmail) {
|
||||||
|
return this.message.includes('<blockquote');
|
||||||
|
}
|
||||||
|
return this.showQuotedContent;
|
||||||
|
},
|
||||||
|
showQuoteToggle() {
|
||||||
|
if (!this.isEmail) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return this.displayQuotedButton;
|
||||||
|
},
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
toggleQuotedContent() {
|
toggleQuotedContent() {
|
||||||
this.showQuotedContent = !this.showQuotedContent;
|
this.showQuotedContent = !this.showQuotedContent;
|
||||||
|
|||||||
@ -51,15 +51,11 @@ export default {
|
|||||||
border-radius: var(--border-radius-small);
|
border-radius: var(--border-radius-small);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
.menu-label {
|
.menu-label {
|
||||||
margin: 0;
|
margin: 0 var(--space-small);
|
||||||
font-size: var(--font-size-mini);
|
font-size: var(--font-size-mini);
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-icon {
|
|
||||||
margin-right: var(--space-small);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: var(--w-500);
|
background-color: var(--w-500);
|
||||||
color: var(--white);
|
color: var(--white);
|
||||||
@ -68,7 +64,6 @@ export default {
|
|||||||
|
|
||||||
.agent-thumbnail {
|
.agent-thumbnail {
|
||||||
margin-top: 0 !important;
|
margin-top: 0 !important;
|
||||||
margin-right: var(--space-small);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.label-pill {
|
.label-pill {
|
||||||
@ -77,6 +72,5 @@ export default {
|
|||||||
border-radius: var(--border-radius-rounded);
|
border-radius: var(--border-radius-rounded);
|
||||||
border: 1px solid var(--s-50);
|
border: 1px solid var(--s-50);
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
margin-right: var(--space-small);
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -43,13 +43,9 @@ export default {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
.menu-label {
|
.menu-label {
|
||||||
margin: 0;
|
margin: 0 var(--space-small);
|
||||||
font-size: var(--font-size-mini);
|
font-size: var(--font-size-mini);
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-icon {
|
|
||||||
margin-right: var(--space-small);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.submenu {
|
.submenu {
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="bulk-action__agents">
|
<div v-on-clickaway="onCloseAgentList" class="bulk-action__agents">
|
||||||
<div class="triangle" :style="cssVars">
|
<div class="triangle" :style="cssVars">
|
||||||
<svg height="12" viewBox="0 0 24 12" width="24">
|
<svg height="12" viewBox="0 0 24 12" width="24">
|
||||||
<path
|
<path
|
||||||
@ -50,7 +50,6 @@
|
|||||||
:status="agent.availability_status"
|
:status="agent.availability_status"
|
||||||
:username="agent.name"
|
:username="agent.name"
|
||||||
size="22px"
|
size="22px"
|
||||||
class="margin-right-small"
|
|
||||||
/>
|
/>
|
||||||
<span class="reports-option__title">{{ agent.name }}</span>
|
<span class="reports-option__title">{{ agent.name }}</span>
|
||||||
</div>
|
</div>
|
||||||
@ -127,6 +126,7 @@ export default {
|
|||||||
return {
|
return {
|
||||||
query: '',
|
query: '',
|
||||||
selectedAgent: null,
|
selectedAgent: null,
|
||||||
|
goBackToAgentList: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@ -170,6 +170,7 @@ export default {
|
|||||||
this.$emit('select', this.selectedAgent);
|
this.$emit('select', this.selectedAgent);
|
||||||
},
|
},
|
||||||
goBack() {
|
goBack() {
|
||||||
|
this.goBackToAgentList = true;
|
||||||
this.selectedAgent = null;
|
this.selectedAgent = null;
|
||||||
},
|
},
|
||||||
assignAgent(agent) {
|
assignAgent(agent) {
|
||||||
@ -178,6 +179,12 @@ export default {
|
|||||||
onClose() {
|
onClose() {
|
||||||
this.$emit('close');
|
this.$emit('close');
|
||||||
},
|
},
|
||||||
|
onCloseAgentList() {
|
||||||
|
if (this.selectedAgent === null && !this.goBackToAgentList) {
|
||||||
|
this.onClose();
|
||||||
|
}
|
||||||
|
this.goBackToAgentList = false;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -184,9 +184,18 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
// For RTL direction view
|
||||||
|
.app-rtl--wrapper {
|
||||||
|
.bulk-action__actions {
|
||||||
|
::v-deep .button--only-icon:last-child {
|
||||||
|
margin-right: var(--space-smaller);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.bulk-action__container {
|
.bulk-action__container {
|
||||||
border-bottom: 1px solid var(--s-100);
|
border-bottom: 1px solid var(--s-100);
|
||||||
padding: var(--space-normal) var(--space-one);
|
padding: var(--space-normal);
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -195,7 +204,7 @@ export default {
|
|||||||
|
|
||||||
span {
|
span {
|
||||||
font-size: var(--font-size-mini);
|
font-size: var(--font-size-mini);
|
||||||
margin-left: var(--space-smaller);
|
margin: 0 var(--space-smaller);
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type='checkbox'] {
|
input[type='checkbox'] {
|
||||||
|
|||||||
@ -97,7 +97,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.label-container {
|
.label-container {
|
||||||
margin-top: var(--space-micro);
|
margin: var(--space-micro) var(--space-small) 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.labels-wrap {
|
.labels-wrap {
|
||||||
|
|||||||
@ -5,7 +5,7 @@ export const CONVERSATION_EVENTS = Object.freeze({
|
|||||||
INSERTED_A_CANNED_RESPONSE: 'Inserted a canned response',
|
INSERTED_A_CANNED_RESPONSE: 'Inserted a canned response',
|
||||||
INSERTED_A_VARIABLE: 'Inserted a variable',
|
INSERTED_A_VARIABLE: 'Inserted a variable',
|
||||||
USED_MENTIONS: 'Used mentions',
|
USED_MENTIONS: 'Used mentions',
|
||||||
|
SEARCH_CONVERSATION: 'Searched conversations',
|
||||||
APPLY_FILTER: 'Applied filters in the conversation list',
|
APPLY_FILTER: 'Applied filters in the conversation list',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import AnalyticsHelper from './AnalyticsHelper';
|
import AnalyticsHelper from './AnalyticsHelper';
|
||||||
|
import LogRocket from 'logrocket';
|
||||||
import DashboardAudioNotificationHelper from './AudioAlerts/DashboardAudioNotificationHelper';
|
import DashboardAudioNotificationHelper from './AudioAlerts/DashboardAudioNotificationHelper';
|
||||||
|
|
||||||
export const CHATWOOT_SET_USER = 'CHATWOOT_SET_USER';
|
export const CHATWOOT_SET_USER = 'CHATWOOT_SET_USER';
|
||||||
@ -10,6 +11,12 @@ export const ANALYTICS_RESET = 'ANALYTICS_RESET';
|
|||||||
export const initializeAnalyticsEvents = () => {
|
export const initializeAnalyticsEvents = () => {
|
||||||
window.bus.$on(ANALYTICS_IDENTITY, ({ user }) => {
|
window.bus.$on(ANALYTICS_IDENTITY, ({ user }) => {
|
||||||
AnalyticsHelper.identify(user);
|
AnalyticsHelper.identify(user);
|
||||||
|
if (window.logRocketProjectId) {
|
||||||
|
LogRocket.identify(user.id, {
|
||||||
|
email: user.email,
|
||||||
|
name: user.name,
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
window.bus.$on(ANALYTICS_RESET, () => {});
|
window.bus.$on(ANALYTICS_RESET, () => {});
|
||||||
};
|
};
|
||||||
|
|||||||
@ -33,6 +33,7 @@ import uk from './locale/uk';
|
|||||||
import vi from './locale/vi';
|
import vi from './locale/vi';
|
||||||
import zh_CN from './locale/zh_CN';
|
import zh_CN from './locale/zh_CN';
|
||||||
import zh_TW from './locale/zh_TW';
|
import zh_TW from './locale/zh_TW';
|
||||||
|
import is from './locale/is';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
ar,
|
ar,
|
||||||
@ -70,4 +71,5 @@ export default {
|
|||||||
vi,
|
vi,
|
||||||
zh_CN,
|
zh_CN,
|
||||||
zh_TW,
|
zh_TW,
|
||||||
|
is,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -35,7 +35,7 @@
|
|||||||
"REMOVE_SELECTION": "إزالة التحديد",
|
"REMOVE_SELECTION": "إزالة التحديد",
|
||||||
"DOWNLOAD": "تنزيل",
|
"DOWNLOAD": "تنزيل",
|
||||||
"UNKNOWN_FILE_TYPE": "ملف غير معروف",
|
"UNKNOWN_FILE_TYPE": "ملف غير معروف",
|
||||||
"SAVE_CONTACT": "Save",
|
"SAVE_CONTACT": "حفظ",
|
||||||
"UPLOADING_ATTACHMENTS": "جاري تحميل المرفقات...",
|
"UPLOADING_ATTACHMENTS": "جاري تحميل المرفقات...",
|
||||||
"SUCCESS_DELETE_MESSAGE": "تم حذف الرسالة بنجاح",
|
"SUCCESS_DELETE_MESSAGE": "تم حذف الرسالة بنجاح",
|
||||||
"FAIL_DELETE_MESSSAGE": "تعذر حذف الرسالة! حاول مرة أخرى",
|
"FAIL_DELETE_MESSSAGE": "تعذر حذف الرسالة! حاول مرة أخرى",
|
||||||
@ -43,8 +43,8 @@
|
|||||||
"RATING_TITLE": "التقييم",
|
"RATING_TITLE": "التقييم",
|
||||||
"FEEDBACK_TITLE": "الملاحظات",
|
"FEEDBACK_TITLE": "الملاحظات",
|
||||||
"CARD": {
|
"CARD": {
|
||||||
"SHOW_LABELS": "Show labels",
|
"SHOW_LABELS": "إظهار السمات",
|
||||||
"HIDE_LABELS": "Hide labels"
|
"HIDE_LABELS": "إخفاء السمات"
|
||||||
},
|
},
|
||||||
"HEADER": {
|
"HEADER": {
|
||||||
"RESOLVE_ACTION": "إغلاق المحادثة",
|
"RESOLVE_ACTION": "إغلاق المحادثة",
|
||||||
@ -69,7 +69,7 @@
|
|||||||
"CARD_CONTEXT_MENU": {
|
"CARD_CONTEXT_MENU": {
|
||||||
"PENDING": "تحديد كمعلق",
|
"PENDING": "تحديد كمعلق",
|
||||||
"RESOLVED": "تحديد كمحلولة",
|
"RESOLVED": "تحديد كمحلولة",
|
||||||
"MARK_AS_UNREAD": "Mark as unread",
|
"MARK_AS_UNREAD": "وضع علامة كغير مقروء",
|
||||||
"REOPEN": "إعادة فتح المحادثة",
|
"REOPEN": "إعادة فتح المحادثة",
|
||||||
"SNOOZE": {
|
"SNOOZE": {
|
||||||
"TITLE": "غفوة",
|
"TITLE": "غفوة",
|
||||||
@ -135,8 +135,8 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"UNDEFINED_VARIABLES": {
|
"UNDEFINED_VARIABLES": {
|
||||||
"TITLE": "Undefined variables",
|
"TITLE": "متغيرات غير معرفة",
|
||||||
"MESSAGE": "You have {undefinedVariablesCount} undefined variables in your message: {undefinedVariables}. Would you like to send the message anyway?",
|
"MESSAGE": "لديك {undefinedVariablesCount} متغيرات غير محددة في رسالتك: {undefinedVariables}. هل ترغب في إرسال الرسالة على أي حال؟",
|
||||||
"CONFIRM": {
|
"CONFIRM": {
|
||||||
"YES": "إرسال",
|
"YES": "إرسال",
|
||||||
"CANCEL": "إلغاء"
|
"CANCEL": "إلغاء"
|
||||||
@ -165,7 +165,8 @@
|
|||||||
"CONTEXT_MENU": {
|
"CONTEXT_MENU": {
|
||||||
"COPY": "نسخ",
|
"COPY": "نسخ",
|
||||||
"DELETE": "حذف",
|
"DELETE": "حذف",
|
||||||
"CREATE_A_CANNED_RESPONSE": "إضافة إلى الردود السريعة"
|
"CREATE_A_CANNED_RESPONSE": "إضافة إلى الردود السريعة",
|
||||||
|
"TRANSLATE": "ترجم"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"EMAIL_TRANSCRIPT": {
|
"EMAIL_TRANSCRIPT": {
|
||||||
@ -253,5 +254,29 @@
|
|||||||
"BCC": "Bcc",
|
"BCC": "Bcc",
|
||||||
"CC": "Cc",
|
"CC": "Cc",
|
||||||
"SUBJECT": "الموضوع"
|
"SUBJECT": "الموضوع"
|
||||||
|
},
|
||||||
|
"CONVERSATION_PARTICIPANTS": {
|
||||||
|
"SIDEBAR_MENU_TITLE": "شارك",
|
||||||
|
"SIDEBAR_TITLE": "المشاركون في المحادثة",
|
||||||
|
"NO_RECORDS_FOUND": "لم يتم العثور على النتائج",
|
||||||
|
"ADD_PARTICIPANTS": "اختر المشاركين",
|
||||||
|
"REMANING_PARTICIPANTS_TEXT": "+%{count} أخرى",
|
||||||
|
"REMANING_PARTICIPANT_TEXT": "+%{count} أخرى",
|
||||||
|
"TOTAL_PARTICIPANTS_TEXT": "%{count} شخص مشارك.",
|
||||||
|
"TOTAL_PARTICIPANT_TEXT": "%{count} شخص مشارك.",
|
||||||
|
"NO_PARTICIPANTS_TEXT": "لا أحد يشارك !.",
|
||||||
|
"WATCH_CONVERSATION": "الانضمام إلى المحادثة",
|
||||||
|
"YOU_ARE_WATCHING": "أنت مشترك",
|
||||||
|
"API": {
|
||||||
|
"ERROR_MESSAGE": "تعذر التحديث، حاول مرة أخرى!",
|
||||||
|
"SUCCESS_MESSAGE": "تم تحديث المشاركين!"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"TRANSLATE_MODAL": {
|
||||||
|
"TITLE": "عرض المحتوى المترجم",
|
||||||
|
"DESC": "يمكنك عرض المحتوى المترجم في كل لغة.",
|
||||||
|
"ORIGINAL_CONTENT": "المحتوى الأصلي",
|
||||||
|
"TRANSLATED_CONTENT": "المحتوى المترجم",
|
||||||
|
"NO_TRANSLATIONS_AVAILABLE": "لا توجد ترجمات لهذا المحتوى"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -82,6 +82,7 @@
|
|||||||
"conversation_creation": "محادثة جديدة",
|
"conversation_creation": "محادثة جديدة",
|
||||||
"conversation_assignment": "تم تعيين المحادثة",
|
"conversation_assignment": "تم تعيين المحادثة",
|
||||||
"assigned_conversation_new_message": "رسالة جديدة",
|
"assigned_conversation_new_message": "رسالة جديدة",
|
||||||
|
"participating_conversation_new_message": "رسالة جديدة",
|
||||||
"conversation_mention": "إشارة"
|
"conversation_mention": "إشارة"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@ -28,6 +28,17 @@
|
|||||||
"SAVING": "جاري الحفظ...",
|
"SAVING": "جاري الحفظ...",
|
||||||
"SAVED": "تم الحفظ"
|
"SAVED": "تم الحفظ"
|
||||||
},
|
},
|
||||||
|
"ARTICLE_EDITOR": {
|
||||||
|
"IMAGE_UPLOAD": {
|
||||||
|
"TITLE": "رفع صورة",
|
||||||
|
"UPLOADING": "جاري الرفع...",
|
||||||
|
"SUCCESS": "Image uploaded successfully",
|
||||||
|
"ERROR": "Error while uploading image",
|
||||||
|
"ERROR_FILE_SIZE": "Image size should be less than {size}MB",
|
||||||
|
"ERROR_FILE_FORMAT": "Image format should be jpg, jpeg or png",
|
||||||
|
"ERROR_FILE_DIMENSIONS": "Image dimensions should be less than 2000 x 2000"
|
||||||
|
}
|
||||||
|
},
|
||||||
"ARTICLE_SETTINGS": {
|
"ARTICLE_SETTINGS": {
|
||||||
"TITLE": "إعدادات المقالة",
|
"TITLE": "إعدادات المقالة",
|
||||||
"FORM": {
|
"FORM": {
|
||||||
|
|||||||
@ -476,7 +476,8 @@
|
|||||||
"WHATSAPP_SECTION_TITLE": "مفتاح API",
|
"WHATSAPP_SECTION_TITLE": "مفتاح API",
|
||||||
"WHATSAPP_SECTION_UPDATE_TITLE": "Update API Key",
|
"WHATSAPP_SECTION_UPDATE_TITLE": "Update API Key",
|
||||||
"WHATSAPP_SECTION_UPDATE_PLACEHOLDER": "Enter the new API Key here",
|
"WHATSAPP_SECTION_UPDATE_PLACEHOLDER": "Enter the new API Key here",
|
||||||
"WHATSAPP_SECTION_UPDATE_BUTTON": "تحديث"
|
"WHATSAPP_SECTION_UPDATE_BUTTON": "تحديث",
|
||||||
|
"UPDATE_PRE_CHAT_FORM_SETTINGS": "Update Pre Chat Form Settings"
|
||||||
},
|
},
|
||||||
"AUTO_ASSIGNMENT": {
|
"AUTO_ASSIGNMENT": {
|
||||||
"MAX_ASSIGNMENT_LIMIT": "حد الإسناد التلقائي",
|
"MAX_ASSIGNMENT_LIMIT": "حد الإسناد التلقائي",
|
||||||
|
|||||||
@ -23,6 +23,7 @@ import { default as _settings } from './settings.json';
|
|||||||
import { default as _signup } from './signup.json';
|
import { default as _signup } from './signup.json';
|
||||||
import { default as _teamsSettings } from './teamsSettings.json';
|
import { default as _teamsSettings } from './teamsSettings.json';
|
||||||
import { default as _whatsappTemplates } from './whatsappTemplates.json';
|
import { default as _whatsappTemplates } from './whatsappTemplates.json';
|
||||||
|
import { default as _helpCenter } from './helpCenter.json';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
..._advancedFilters,
|
..._advancedFilters,
|
||||||
@ -50,4 +51,5 @@ export default {
|
|||||||
..._teamsSettings,
|
..._teamsSettings,
|
||||||
..._whatsappTemplates,
|
..._whatsappTemplates,
|
||||||
..._bulkActions,
|
..._bulkActions,
|
||||||
|
..._helpCenter,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -14,6 +14,11 @@
|
|||||||
"ERROR_MESSAGE": "تعذر الاتصال بالخادم، الرجاء المحاولة مرة أخرى لاحقاً",
|
"ERROR_MESSAGE": "تعذر الاتصال بالخادم، الرجاء المحاولة مرة أخرى لاحقاً",
|
||||||
"UNAUTH": "اسم المستخدم / كلمة المرور غير صحيحة. الرجاء المحاولة مرة أخرى"
|
"UNAUTH": "اسم المستخدم / كلمة المرور غير صحيحة. الرجاء المحاولة مرة أخرى"
|
||||||
},
|
},
|
||||||
|
"OAUTH": {
|
||||||
|
"GOOGLE_LOGIN": "Login with Google",
|
||||||
|
"BUSINESS_ACCOUNTS_ONLY": "Please use your company email address to login",
|
||||||
|
"NO_ACCOUNT_FOUND": "We couldn't find an account for your email address."
|
||||||
|
},
|
||||||
"FORGOT_PASSWORD": "نسيت كلمة المرور؟",
|
"FORGOT_PASSWORD": "نسيت كلمة المرور؟",
|
||||||
"CREATE_NEW_ACCOUNT": "إنشاء حساب جديد",
|
"CREATE_NEW_ACCOUNT": "إنشاء حساب جديد",
|
||||||
"SUBMIT": "تسجيل الدخول"
|
"SUBMIT": "تسجيل الدخول"
|
||||||
|
|||||||
@ -426,6 +426,12 @@
|
|||||||
"UNATTENDED": "بدون حضور",
|
"UNATTENDED": "بدون حضور",
|
||||||
"UNASSIGNED": "غير مسند"
|
"UNASSIGNED": "غير مسند"
|
||||||
},
|
},
|
||||||
|
"CONVERSATION_HEATMAP": {
|
||||||
|
"HEADER": "Conversation Traffic",
|
||||||
|
"NO_CONVERSATIONS": "No conversations",
|
||||||
|
"CONVERSATION": "%{count} conversation",
|
||||||
|
"CONVERSATIONS": "%{count} conversations"
|
||||||
|
},
|
||||||
"AGENT_CONVERSATIONS": {
|
"AGENT_CONVERSATIONS": {
|
||||||
"HEADER": "المحادثات من قبل الوكلاء",
|
"HEADER": "المحادثات من قبل الوكلاء",
|
||||||
"LOADING_MESSAGE": "جاري تحميل مقاييس الوكيل...",
|
"LOADING_MESSAGE": "جاري تحميل مقاييس الوكيل...",
|
||||||
@ -443,5 +449,14 @@
|
|||||||
"BUSY": "مشغول",
|
"BUSY": "مشغول",
|
||||||
"OFFLINE": "غير متصل"
|
"OFFLINE": "غير متصل"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"DAYS_OF_WEEK": {
|
||||||
|
"SUNDAY": "Sunday",
|
||||||
|
"MONDAY": "Monday",
|
||||||
|
"TUESDAY": "Tuesday",
|
||||||
|
"WEDNESDAY": "Wednesday",
|
||||||
|
"THURSDAY": "Thursday",
|
||||||
|
"FRIDAY": "Friday",
|
||||||
|
"SATURDAY": "Saturday"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
23
app/javascript/dashboard/i18n/locale/ar/search.json
Normal file
23
app/javascript/dashboard/i18n/locale/ar/search.json
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"SEARCH": {
|
||||||
|
"TABS": {
|
||||||
|
"ALL": "الكل",
|
||||||
|
"CONTACTS": "جهات الاتصال",
|
||||||
|
"CONVERSATIONS": "المحادثات",
|
||||||
|
"MESSAGES": "الرسائل"
|
||||||
|
},
|
||||||
|
"SECTION": {
|
||||||
|
"CONTACTS": "جهات الاتصال",
|
||||||
|
"CONVERSATIONS": "المحادثات",
|
||||||
|
"MESSAGES": "الرسائل"
|
||||||
|
},
|
||||||
|
"EMPTY_STATE": "No %{item} found for query '%{query}'",
|
||||||
|
"EMPTY_STATE_FULL": "No results found for query '%{query}'",
|
||||||
|
"PLACEHOLDER_KEYBINDING": "/ to focus",
|
||||||
|
"INPUT_PLACEHOLDER": "Search messages, contacts or conversations",
|
||||||
|
"EMPTY_STATE_DEFAULT": "Search by conversation id, email, phone number, messages for better search results.",
|
||||||
|
"BOT_LABEL": "رد آلي",
|
||||||
|
"READ_MORE": "Read more",
|
||||||
|
"WROTE": "wrote:"
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -79,7 +79,8 @@
|
|||||||
"CONVERSATION_ASSIGNMENT": "إرسال إشعارات البريد الإلكتروني عند إسناد محادثة لي",
|
"CONVERSATION_ASSIGNMENT": "إرسال إشعارات البريد الإلكتروني عند إسناد محادثة لي",
|
||||||
"CONVERSATION_CREATION": "إرسال إشعارات للبريد الإلكتروني عند ورود محادثة جديدة",
|
"CONVERSATION_CREATION": "إرسال إشعارات للبريد الإلكتروني عند ورود محادثة جديدة",
|
||||||
"CONVERSATION_MENTION": "إرسال إشعارات بالبريد الإلكتروني عندما يتم ذكرك في محادثة",
|
"CONVERSATION_MENTION": "إرسال إشعارات بالبريد الإلكتروني عندما يتم ذكرك في محادثة",
|
||||||
"ASSIGNED_CONVERSATION_NEW_MESSAGE": "إرسال إشعارات البريد الإلكتروني عند إنشاء رسالة جديدة في محادثة موكلة"
|
"ASSIGNED_CONVERSATION_NEW_MESSAGE": "إرسال إشعارات البريد الإلكتروني عند إنشاء رسالة جديدة في محادثة موكلة",
|
||||||
|
"PARTICIPATING_CONVERSATION_NEW_MESSAGE": "إرسال إشعارات التنية عند إنشاء رسالة جديدة في محادثة موكلة"
|
||||||
},
|
},
|
||||||
"API": {
|
"API": {
|
||||||
"UPDATE_SUCCESS": "يتم تحديث إعدادات الإشعارات بنجاح",
|
"UPDATE_SUCCESS": "يتم تحديث إعدادات الإشعارات بنجاح",
|
||||||
@ -92,6 +93,7 @@
|
|||||||
"CONVERSATION_CREATION": "إرسال إشعارات المتصفح عند ورود محادثة جديدة",
|
"CONVERSATION_CREATION": "إرسال إشعارات المتصفح عند ورود محادثة جديدة",
|
||||||
"CONVERSATION_MENTION": "إرسال إشعارات بالبريد الإلكتروني عندما يتم ذكرك او الاشارة اليك في محادثة",
|
"CONVERSATION_MENTION": "إرسال إشعارات بالبريد الإلكتروني عندما يتم ذكرك او الاشارة اليك في محادثة",
|
||||||
"ASSIGNED_CONVERSATION_NEW_MESSAGE": "إرسال إشعارات التنية عند إنشاء رسالة جديدة في محادثة موكلة",
|
"ASSIGNED_CONVERSATION_NEW_MESSAGE": "إرسال إشعارات التنية عند إنشاء رسالة جديدة في محادثة موكلة",
|
||||||
|
"PARTICIPATING_CONVERSATION_NEW_MESSAGE": "إرسال إشعارات التنية عند إنشاء رسالة جديدة في محادثة موكلة",
|
||||||
"HAS_ENABLED_PUSH": "لقد قمت بتمكين الإشعارات لهذا المتصفح.",
|
"HAS_ENABLED_PUSH": "لقد قمت بتمكين الإشعارات لهذا المتصفح.",
|
||||||
"REQUEST_PUSH": "تفعيل إشعارات المتصفح"
|
"REQUEST_PUSH": "تفعيل إشعارات المتصفح"
|
||||||
},
|
},
|
||||||
@ -192,6 +194,7 @@
|
|||||||
"CONVERSATIONS": "المحادثات",
|
"CONVERSATIONS": "المحادثات",
|
||||||
"ALL_CONVERSATIONS": "كل المحادثات",
|
"ALL_CONVERSATIONS": "كل المحادثات",
|
||||||
"MENTIONED_CONVERSATIONS": "الإشارات",
|
"MENTIONED_CONVERSATIONS": "الإشارات",
|
||||||
|
"PARTICIPATING_CONVERSATIONS": "شارك",
|
||||||
"UNATTENDED_CONVERSATIONS": "بدون حضور",
|
"UNATTENDED_CONVERSATIONS": "بدون حضور",
|
||||||
"REPORTS": "التقارير",
|
"REPORTS": "التقارير",
|
||||||
"SETTINGS": "الإعدادات",
|
"SETTINGS": "الإعدادات",
|
||||||
@ -239,6 +242,7 @@
|
|||||||
"DRAFT": "مسودة",
|
"DRAFT": "مسودة",
|
||||||
"ARCHIVED": "مؤرشفة",
|
"ARCHIVED": "مؤرشفة",
|
||||||
"CATEGORY": "الفئة",
|
"CATEGORY": "الفئة",
|
||||||
|
"SETTINGS": "الإعدادات",
|
||||||
"CATEGORY_EMPTY_MESSAGE": "لم يتم العثور على فئات"
|
"CATEGORY_EMPTY_MESSAGE": "لم يتم العثور على فئات"
|
||||||
},
|
},
|
||||||
"SET_AUTO_OFFLINE": {
|
"SET_AUTO_OFFLINE": {
|
||||||
|
|||||||
@ -5,6 +5,9 @@
|
|||||||
"TESTIMONIAL_HEADER": "إن كل ما يلزم هو خطوة واحدة للمضي قدما",
|
"TESTIMONIAL_HEADER": "إن كل ما يلزم هو خطوة واحدة للمضي قدما",
|
||||||
"TESTIMONIAL_CONTENT": "You're one step away from engaging your customers, retaining them and finding new ones.",
|
"TESTIMONIAL_CONTENT": "You're one step away from engaging your customers, retaining them and finding new ones.",
|
||||||
"TERMS_ACCEPT": "من خلال التسجيل، فإنك توافق على <a href=\"https://www.chatwoot.com/terms\">شروط الخدمة</a> و <a href=\"https://www.chatwoot.com/privacy-policy\">سياسة الخصوصية</a>",
|
"TERMS_ACCEPT": "من خلال التسجيل، فإنك توافق على <a href=\"https://www.chatwoot.com/terms\">شروط الخدمة</a> و <a href=\"https://www.chatwoot.com/privacy-policy\">سياسة الخصوصية</a>",
|
||||||
|
"OAUTH": {
|
||||||
|
"GOOGLE_SIGNUP": "Sign up with Google"
|
||||||
|
},
|
||||||
"COMPANY_NAME": {
|
"COMPANY_NAME": {
|
||||||
"LABEL": "Company name",
|
"LABEL": "Company name",
|
||||||
"PLACEHOLDER": "Enter your company name. eg: Wayne Enterprises",
|
"PLACEHOLDER": "Enter your company name. eg: Wayne Enterprises",
|
||||||
|
|||||||
@ -165,7 +165,8 @@
|
|||||||
"CONTEXT_MENU": {
|
"CONTEXT_MENU": {
|
||||||
"COPY": "Copy",
|
"COPY": "Copy",
|
||||||
"DELETE": "Изтрий",
|
"DELETE": "Изтрий",
|
||||||
"CREATE_A_CANNED_RESPONSE": "Add to canned responses"
|
"CREATE_A_CANNED_RESPONSE": "Add to canned responses",
|
||||||
|
"TRANSLATE": "Translate"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"EMAIL_TRANSCRIPT": {
|
"EMAIL_TRANSCRIPT": {
|
||||||
@ -253,5 +254,29 @@
|
|||||||
"BCC": "Bcc",
|
"BCC": "Bcc",
|
||||||
"CC": "Cc",
|
"CC": "Cc",
|
||||||
"SUBJECT": "Subject"
|
"SUBJECT": "Subject"
|
||||||
|
},
|
||||||
|
"CONVERSATION_PARTICIPANTS": {
|
||||||
|
"SIDEBAR_MENU_TITLE": "Participating",
|
||||||
|
"SIDEBAR_TITLE": "Conversation participants",
|
||||||
|
"NO_RECORDS_FOUND": "Няма намерени резултати",
|
||||||
|
"ADD_PARTICIPANTS": "Select participants",
|
||||||
|
"REMANING_PARTICIPANTS_TEXT": "+%{count} others",
|
||||||
|
"REMANING_PARTICIPANT_TEXT": "+%{count} other",
|
||||||
|
"TOTAL_PARTICIPANTS_TEXT": "%{count} people are participating.",
|
||||||
|
"TOTAL_PARTICIPANT_TEXT": "%{count} person is participating.",
|
||||||
|
"NO_PARTICIPANTS_TEXT": "No one is participating!.",
|
||||||
|
"WATCH_CONVERSATION": "Join conversation",
|
||||||
|
"YOU_ARE_WATCHING": "You are participating",
|
||||||
|
"API": {
|
||||||
|
"ERROR_MESSAGE": "Could not update, try again!",
|
||||||
|
"SUCCESS_MESSAGE": "Participants updated!"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"TRANSLATE_MODAL": {
|
||||||
|
"TITLE": "View translated content",
|
||||||
|
"DESC": "You can view the translated content in each langauge.",
|
||||||
|
"ORIGINAL_CONTENT": "Original Content",
|
||||||
|
"TRANSLATED_CONTENT": "Translated Content",
|
||||||
|
"NO_TRANSLATIONS_AVAILABLE": "No translations are available for this content"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -82,6 +82,7 @@
|
|||||||
"conversation_creation": "New conversation",
|
"conversation_creation": "New conversation",
|
||||||
"conversation_assignment": "Conversation Assigned",
|
"conversation_assignment": "Conversation Assigned",
|
||||||
"assigned_conversation_new_message": "New Message",
|
"assigned_conversation_new_message": "New Message",
|
||||||
|
"participating_conversation_new_message": "New Message",
|
||||||
"conversation_mention": "Mention"
|
"conversation_mention": "Mention"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@ -28,6 +28,17 @@
|
|||||||
"SAVING": "Saving...",
|
"SAVING": "Saving...",
|
||||||
"SAVED": "Saved"
|
"SAVED": "Saved"
|
||||||
},
|
},
|
||||||
|
"ARTICLE_EDITOR": {
|
||||||
|
"IMAGE_UPLOAD": {
|
||||||
|
"TITLE": "Upload image",
|
||||||
|
"UPLOADING": "Качване...",
|
||||||
|
"SUCCESS": "Image uploaded successfully",
|
||||||
|
"ERROR": "Error while uploading image",
|
||||||
|
"ERROR_FILE_SIZE": "Image size should be less than {size}MB",
|
||||||
|
"ERROR_FILE_FORMAT": "Image format should be jpg, jpeg or png",
|
||||||
|
"ERROR_FILE_DIMENSIONS": "Image dimensions should be less than 2000 x 2000"
|
||||||
|
}
|
||||||
|
},
|
||||||
"ARTICLE_SETTINGS": {
|
"ARTICLE_SETTINGS": {
|
||||||
"TITLE": "Article Settings",
|
"TITLE": "Article Settings",
|
||||||
"FORM": {
|
"FORM": {
|
||||||
|
|||||||
@ -476,7 +476,8 @@
|
|||||||
"WHATSAPP_SECTION_TITLE": "API Key",
|
"WHATSAPP_SECTION_TITLE": "API Key",
|
||||||
"WHATSAPP_SECTION_UPDATE_TITLE": "Update API Key",
|
"WHATSAPP_SECTION_UPDATE_TITLE": "Update API Key",
|
||||||
"WHATSAPP_SECTION_UPDATE_PLACEHOLDER": "Enter the new API Key here",
|
"WHATSAPP_SECTION_UPDATE_PLACEHOLDER": "Enter the new API Key here",
|
||||||
"WHATSAPP_SECTION_UPDATE_BUTTON": "Обновяване"
|
"WHATSAPP_SECTION_UPDATE_BUTTON": "Обновяване",
|
||||||
|
"UPDATE_PRE_CHAT_FORM_SETTINGS": "Update Pre Chat Form Settings"
|
||||||
},
|
},
|
||||||
"AUTO_ASSIGNMENT": {
|
"AUTO_ASSIGNMENT": {
|
||||||
"MAX_ASSIGNMENT_LIMIT": "Auto assignment limit",
|
"MAX_ASSIGNMENT_LIMIT": "Auto assignment limit",
|
||||||
|
|||||||
@ -23,6 +23,7 @@ import { default as _settings } from './settings.json';
|
|||||||
import { default as _signup } from './signup.json';
|
import { default as _signup } from './signup.json';
|
||||||
import { default as _teamsSettings } from './teamsSettings.json';
|
import { default as _teamsSettings } from './teamsSettings.json';
|
||||||
import { default as _whatsappTemplates } from './whatsappTemplates.json';
|
import { default as _whatsappTemplates } from './whatsappTemplates.json';
|
||||||
|
import { default as _helpCenter } from './helpCenter.json';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
..._advancedFilters,
|
..._advancedFilters,
|
||||||
@ -50,4 +51,5 @@ export default {
|
|||||||
..._teamsSettings,
|
..._teamsSettings,
|
||||||
..._whatsappTemplates,
|
..._whatsappTemplates,
|
||||||
..._bulkActions,
|
..._bulkActions,
|
||||||
|
..._helpCenter,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -14,6 +14,11 @@
|
|||||||
"ERROR_MESSAGE": "Не можа да се свърже с Woot сървър. Моля, опитайте отново по-късно",
|
"ERROR_MESSAGE": "Не можа да се свърже с Woot сървър. Моля, опитайте отново по-късно",
|
||||||
"UNAUTH": "Username / Password Incorrect. Please try again"
|
"UNAUTH": "Username / Password Incorrect. Please try again"
|
||||||
},
|
},
|
||||||
|
"OAUTH": {
|
||||||
|
"GOOGLE_LOGIN": "Login with Google",
|
||||||
|
"BUSINESS_ACCOUNTS_ONLY": "Please use your company email address to login",
|
||||||
|
"NO_ACCOUNT_FOUND": "We couldn't find an account for your email address."
|
||||||
|
},
|
||||||
"FORGOT_PASSWORD": "Forgot your password?",
|
"FORGOT_PASSWORD": "Forgot your password?",
|
||||||
"CREATE_NEW_ACCOUNT": "Create new account",
|
"CREATE_NEW_ACCOUNT": "Create new account",
|
||||||
"SUBMIT": "Login"
|
"SUBMIT": "Login"
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user